Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: etcd peer sans list #5806

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/app/machined/pkg/controllers/secrets/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (ctrl *APIController) Run(ctx context.Context, r controller.Runtime, logger
}
}

//nolint:gocyclo,cyclop
//nolint:gocyclo,cyclop,dupl
func (ctrl *APIController) reconcile(ctx context.Context, r controller.Runtime, logger *zap.Logger, isControlplane bool) error {
inputs := []controller.Input{
{
Expand Down
70 changes: 66 additions & 4 deletions internal/app/machined/pkg/controllers/secrets/etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ package secrets
import (
"context"
"fmt"
stdlibnet "net"

"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
"go.uber.org/zap"

"github.com/talos-systems/net"
"github.com/talos-systems/talos/internal/pkg/etcd"
"github.com/talos-systems/talos/pkg/machinery/resources/config"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
"github.com/talos-systems/talos/pkg/machinery/resources/secrets"
"github.com/talos-systems/talos/pkg/machinery/resources/time"
Expand All @@ -30,6 +33,8 @@ func (ctrl *EtcdController) Name() string {
}

// Inputs implements controller.Controller interface.
//
//nolint:dupl
func (ctrl *EtcdController) Inputs() []controller.Input {
return []controller.Input{
{
Expand All @@ -50,6 +55,12 @@ func (ctrl *EtcdController) Inputs() []controller.Input {
ID: pointer.To(time.StatusID),
Kind: controller.InputWeak,
},
{
Namespace: config.NamespaceName,
Type: config.MachineConfigType,
ID: pointer.To(config.V1Alpha1ID),
Kind: controller.InputWeak,
},
}
}

Expand Down Expand Up @@ -96,7 +107,7 @@ func (ctrl *EtcdController) Run(ctx context.Context, r controller.Runtime, logge
continue
}

return err
return fmt.Errorf("error getting network status: %w", err)
}

networkStatus := networkResource.(*network.Status).TypedSpec()
Expand All @@ -119,23 +130,74 @@ func (ctrl *EtcdController) Run(ctx context.Context, r controller.Runtime, logge
continue
}

cfg, err := r.Get(ctx, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
if err != nil {
if state.IsNotFoundError(err) {
continue
}

return fmt.Errorf("error getting machine config: %w", err)
}

cfgProvider := cfg.(*config.MachineConfig).Config()
subnet := cfgProvider.Cluster().Etcd().Subnet()

sanIPs, err := ctrl.findSanIPs(subnet)
if err != nil {
return err
}

if err = r.Modify(ctx, secrets.NewEtcd(), func(r resource.Resource) error {
return ctrl.updateSecrets(etcdRoot, r.(*secrets.Etcd).TypedSpec())
return ctrl.updateSecrets(etcdRoot, r.(*secrets.Etcd).TypedSpec(), sanIPs)
}); err != nil {
return err
}
}
}

func (ctrl *EtcdController) updateSecrets(etcdRoot *secrets.EtcdRootSpec, etcdCerts *secrets.EtcdCertsSpec) error {
func (ctrl *EtcdController) findSanIPs(subnet string) ([]stdlibnet.IP, error) {
if subnet != "" {
peerListenAddr := ""

ips, err := net.IPAddrs()
if err != nil {
return nil, fmt.Errorf("error listing IPs: %w", err)
}

ips = net.IPFilter(ips, network.NotSideroLinkStdIP)

network, err := net.ParseCIDR(subnet)
if err != nil {
return nil, fmt.Errorf("failed to parse subnet: %w", err)
}

for _, ip := range ips {
if network.Contains(ip) {
peerListenAddr = ip.String()

break
}
}

if peerListenAddr == "" {
return nil, fmt.Errorf("no address matched the provided subnet")
}

return []stdlibnet.IP{stdlibnet.ParseIP(peerListenAddr)}, nil
}

return nil, nil
}

func (ctrl *EtcdController) updateSecrets(etcdRoot *secrets.EtcdRootSpec, etcdCerts *secrets.EtcdCertsSpec, sanIPs []stdlibnet.IP) error {
var err error

etcdCerts.Etcd, err = etcd.GenerateCert(etcdRoot.EtcdCA)
if err != nil {
return fmt.Errorf("error generating etcd client certs: %w", err)
}

etcdCerts.EtcdPeer, err = etcd.GeneratePeerCert(etcdRoot.EtcdCA)
etcdCerts.EtcdPeer, err = etcd.GeneratePeerCert(etcdRoot.EtcdCA, sanIPs)
if err != nil {
return fmt.Errorf("error generating etcd peer certs: %w", err)
}
Expand Down
10 changes: 2 additions & 8 deletions internal/app/machined/pkg/system/services/etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,6 @@ func (e *Etcd) HealthSettings(runtime.Runtime) *health.Settings {

//nolint:gocyclo
func generatePKI(ctx context.Context, r runtime.Runtime) (err error) {
// remove legacy etcd PKI directory to handle upgrades with `--preserve` to Talos 0.12
// TODO: remove me in Talos 0.13
if err = os.RemoveAll("/etc/kubernetes/pki/etcd"); err != nil {
return err
}

if err = os.MkdirAll(constants.EtcdPKIPath, 0o700); err != nil {
return err
}
Expand Down Expand Up @@ -574,8 +568,8 @@ func (e *Etcd) argsForControlPlane(ctx context.Context, r runtime.Runtime) error
"listen-peer-urls": "https://" + net.FormatAddress(listenAddress) + ":2380",
"listen-client-urls": "https://" + net.FormatAddress(listenAddress) + ":2379",
"client-cert-auth": "true",
"cert-file": constants.KubernetesEtcdPeerCert,
"key-file": constants.KubernetesEtcdPeerKey,
"cert-file": constants.KubernetesEtcdCert,
"key-file": constants.KubernetesEtcdKey,
"trusted-ca-file": constants.KubernetesEtcdCACert,
"peer-client-cert-auth": "true",
"peer-cert-file": constants.KubernetesEtcdPeerCert,
Expand Down
39 changes: 25 additions & 14 deletions internal/pkg/etcd/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import (
"github.com/talos-systems/talos/pkg/machinery/resources/network"
)

// NewCommonOptions set common certificate options.
func NewCommonOptions() ([]x509.Option, error) {
// newCommonOptions set common certificate options.
func newCommonOptions() ([]x509.Option, error) {
ips, err := net.IPAddrs()
if err != nil {
return nil, fmt.Errorf("failed to discover IP addresses: %w", err)
Expand All @@ -36,16 +36,8 @@ func NewCommonOptions() ([]x509.Option, error) {
return nil, fmt.Errorf("failed to get hostname: %w", err)
}

dnsNames, err := net.DNSNames()
if err != nil {
return nil, fmt.Errorf("failed to get host DNS names: %w", err)
}

dnsNames = append(dnsNames, "localhost")

return []x509.Option{
x509.CommonName(hostname),
x509.DNSNames(dnsNames),
x509.IPAddresses(ips),
x509.NotAfter(time.Now().Add(87600 * time.Hour)),
x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature | stdlibx509.KeyUsageKeyEncipherment),
Expand All @@ -55,8 +47,8 @@ func NewCommonOptions() ([]x509.Option, error) {
// GeneratePeerCert generates etcd peer certificate and key from etcd CA.
//
//nolint:dupl
func GeneratePeerCert(etcdCA *x509.PEMEncodedCertificateAndKey) (*x509.PEMEncodedCertificateAndKey, error) {
opts, err := NewCommonOptions()
func GeneratePeerCert(etcdCA *x509.PEMEncodedCertificateAndKey, sansIP []stdlibnet.IP) (*x509.PEMEncodedCertificateAndKey, error) {
opts, err := newCommonOptions()
if err != nil {
return nil, err
}
Expand All @@ -68,6 +60,17 @@ func GeneratePeerCert(etcdCA *x509.PEMEncodedCertificateAndKey) (*x509.PEMEncode
}),
)

if len(sansIP) > 0 {
ips := []stdlibnet.IP{
stdlibnet.ParseIP("127.0.0.1"),
stdlibnet.ParseIP("::1"),
}

opts = append(opts,
x509.IPAddresses(append(ips, sansIP...)),
)
}

ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(etcdCA)
if err != nil {
return nil, fmt.Errorf("failed loading CA from config: %w", err)
Expand All @@ -85,12 +88,20 @@ func GeneratePeerCert(etcdCA *x509.PEMEncodedCertificateAndKey) (*x509.PEMEncode
//
//nolint:dupl
func GenerateCert(etcdCA *x509.PEMEncodedCertificateAndKey) (*x509.PEMEncodedCertificateAndKey, error) {
opts, err := NewCommonOptions()
opts, err := newCommonOptions()
if err != nil {
return nil, err
}

dnsNames, err := net.DNSNames()
if err != nil {
return nil, fmt.Errorf("failed to get host DNS names: %w", err)
}

dnsNames = append(dnsNames, "localhost")

opts = append(opts,
x509.DNSNames(dnsNames),
x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{
stdlibx509.ExtKeyUsageServerAuth,
stdlibx509.ExtKeyUsageClientAuth,
Expand All @@ -112,7 +123,7 @@ func GenerateCert(etcdCA *x509.PEMEncodedCertificateAndKey) (*x509.PEMEncodedCer

// GenerateClientCert generates client certificate and key from etcd CA.
func GenerateClientCert(etcdCA *x509.PEMEncodedCertificateAndKey, commonName string) (*x509.PEMEncodedCertificateAndKey, error) {
opts, err := NewCommonOptions()
opts, err := newCommonOptions()
if err != nil {
return nil, err
}
Expand Down