From 6ec5cb02cb40bb16bb5556ab67807cb66a9535a3 Mon Sep 17 00:00:00 2001 From: Andrew Rynhard Date: Tue, 24 Sep 2019 08:32:59 -0700 Subject: [PATCH] refactor: decouple grpc client and userdata code This detangles the gRPC client code from the userdata code. The motivation behind this is to make creating clients more simple and not dependent on our configuration format. Signed-off-by: Andrew Rynhard --- cmd/osctl/cmd/gen.go | 2 +- cmd/osctl/cmd/inject.go | 32 --- cmd/osctl/cmd/root.go | 7 +- cmd/osctl/pkg/client/client.go | 75 ++++--- .../machined/internal/phase/userdata/pki.go | 61 ------ .../internal/sequencer/v1alpha1/types.go | 1 - .../pkg/system/services/kubeadm/kubeadm.go | 22 +-- internal/app/osd/main.go | 54 +++-- internal/app/trustd/main.go | 45 ++++- internal/pkg/platform/iso/iso.go | 3 +- pkg/constants/constants.go | 7 +- pkg/crypto/x509/x509.go | 39 +++- pkg/grpc/gen/local.go | 35 ++++ pkg/grpc/gen/{gen.go => remote.go} | 65 +++--- pkg/grpc/middleware/auth/basic/basic.go | 18 -- pkg/grpc/tls/cert.go | 185 ------------------ pkg/grpc/tls/local.go | 90 +++++++++ pkg/grpc/tls/provider.go | 129 ++++++++++++ pkg/grpc/tls/remote.go | 85 ++++++++ pkg/grpc/tls/tls.go | 26 +-- pkg/userdata/os_security.go | 3 +- pkg/userdata/userdata.go | 47 ----- 22 files changed, 546 insertions(+), 485 deletions(-) delete mode 100644 internal/app/machined/internal/phase/userdata/pki.go create mode 100644 pkg/grpc/gen/local.go rename pkg/grpc/gen/{gen.go => remote.go} (51%) delete mode 100644 pkg/grpc/tls/cert.go create mode 100644 pkg/grpc/tls/local.go create mode 100644 pkg/grpc/tls/provider.go create mode 100644 pkg/grpc/tls/remote.go diff --git a/cmd/osctl/cmd/gen.go b/cmd/osctl/cmd/gen.go index ef80d3b2b..ce04b5715 100644 --- a/cmd/osctl/cmd/gen.go +++ b/cmd/osctl/cmd/gen.go @@ -191,7 +191,7 @@ func init() { // Certificate Authorities caCmd.Flags().StringVar(&organization, "organization", "", "X.509 distinguished name for the Organization") helpers.Should(cobra.MarkFlagRequired(caCmd.Flags(), "organization")) - caCmd.Flags().IntVar(&hours, "hours", 24, "the hours from now on which the certificate validity period ends") + caCmd.Flags().IntVar(&hours, "hours", 87600, "the hours from now on which the certificate validity period ends") caCmd.Flags().BoolVar(&rsa, "rsa", false, "generate in RSA format") // Keys keyCmd.Flags().StringVar(&name, "name", "", "the basename of the generated file") diff --git a/cmd/osctl/cmd/inject.go b/cmd/osctl/cmd/inject.go index f96bb2472..28044a036 100644 --- a/cmd/osctl/cmd/inject.go +++ b/cmd/osctl/cmd/inject.go @@ -36,19 +36,6 @@ var injectOSCmd = &cobra.Command{ }, } -// injectIdentityCmd represents the inject command -// nolint: dupl -var injectIdentityCmd = &cobra.Command{ - Use: "identity", - Short: "inject identity data.", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - if err := inject(args, crt, key, injectIdentityData); err != nil { - helpers.Fatalf("%s", err) - } - }, -} - // injectKubernetesCmd represents the inject command // nolint: dupl var injectKubernetesCmd = &cobra.Command{ @@ -76,20 +63,6 @@ func injectOSData(u *userdata.UserData, crt, key string) (err error) { return nil } -// nolint: dupl -func injectIdentityData(u *userdata.UserData, crt, key string) (err error) { - if u.Security == nil { - u.Security = newSecurity() - } - crtAndKey, err := x509.NewCertificateAndKeyFromFiles(crt, key) - if err != nil { - return - } - u.Security.OS.Identity = crtAndKey - - return nil -} - // nolint: dupl func injectKubernetesData(u *userdata.UserData, crt, key string) (err error) { if u.Security == nil { @@ -144,19 +117,14 @@ func newSecurity() *userdata.Security { func init() { injectOSCmd.Flags().StringVar(&crt, "crt", "", "the path to the PKI certificate") - injectIdentityCmd.Flags().StringVar(&crt, "crt", "", "the path to the PKI certificate") injectKubernetesCmd.Flags().StringVar(&crt, "crt", "", "the path to the PKI certificate") helpers.Should(injectOSCmd.MarkFlagRequired("crt")) - helpers.Should(injectIdentityCmd.MarkFlagRequired("crt")) helpers.Should(injectKubernetesCmd.MarkFlagRequired("crt")) injectOSCmd.Flags().StringVar(&key, "key", "", "the path to the PKI key") - injectIdentityCmd.Flags().StringVar(&key, "key", "", "the path to the PKI key") injectKubernetesCmd.Flags().StringVar(&key, "key", "", "the path to the PKI key") helpers.Should(injectOSCmd.MarkFlagRequired("key")) - helpers.Should(injectIdentityCmd.MarkFlagRequired("key")) helpers.Should(injectKubernetesCmd.MarkFlagRequired("key")) - injectCmd.AddCommand(injectOSCmd, injectIdentityCmd, injectKubernetesCmd) rootCmd.AddCommand(injectCmd) } diff --git a/cmd/osctl/cmd/root.go b/cmd/osctl/cmd/root.go index 84ac8e345..30964b5bb 100644 --- a/cmd/osctl/cmd/root.go +++ b/cmd/osctl/cmd/root.go @@ -101,14 +101,11 @@ func Execute() { // setupClient wraps common code to initialize osd client func setupClient(action func(*client.Client)) { - creds, err := client.NewDefaultClientCredentials(talosconfig) + t, creds, err := client.NewClientTargetAndCredentialsFromConfig(talosconfig) if err != nil { helpers.Fatalf("error getting client credentials: %s", err) } - if target != "" { - creds.Target = target - } - c, err := client.NewClient(constants.OsdPort, creds) + c, err := client.NewClient(creds, t, constants.OsdPort) if err != nil { helpers.Fatalf("error constructing client: %s", err) } diff --git a/cmd/osctl/pkg/client/client.go b/cmd/osctl/pkg/client/client.go index 33bcc85b6..67a5a3ae2 100644 --- a/cmd/osctl/pkg/client/client.go +++ b/cmd/osctl/pkg/client/client.go @@ -30,10 +30,9 @@ import ( // Credentials represents the set of values required to initialize a vaild // Client. type Credentials struct { - Target string - ca []byte - crt []byte - key []byte + ca []byte + crt []byte + key []byte } // Client implements the proto.OSClient interface. It serves as the @@ -46,70 +45,84 @@ type Client struct { NetworkClient networkapi.NetworkClient } -// NewDefaultClientCredentials initializes ClientCredentials using default paths +// NewClientTargetAndCredentialsFromConfig initializes ClientCredentials using default paths // to the required CA, certificate, and key. -func NewDefaultClientCredentials(p string) (creds *Credentials, err error) { +func NewClientTargetAndCredentialsFromConfig(p string) (target string, creds *Credentials, err error) { c, err := config.Open(p) if err != nil { return } if c.Context == "" { - return nil, fmt.Errorf("'context' key is not set in the config") + return "", nil, fmt.Errorf("'context' key is not set in the config") } context := c.Contexts[c.Context] if context == nil { - return nil, fmt.Errorf("context %q is not defined in 'contexts' key in config", c.Context) + return "", nil, fmt.Errorf("context %q is not defined in 'contexts' key in config", c.Context) } caBytes, err := base64.StdEncoding.DecodeString(context.CA) if err != nil { - return - } - crtBytes, err := base64.StdEncoding.DecodeString(context.Crt) - if err != nil { - return - } - keyBytes, err := base64.StdEncoding.DecodeString(context.Key) - if err != nil { - return - } - creds = &Credentials{ - Target: context.Target, - ca: caBytes, - crt: crtBytes, - key: keyBytes, + return "", nil, fmt.Errorf("error decoding CA: %v", err) } - return creds, nil + crtBytes, err := base64.StdEncoding.DecodeString(context.Crt) + if err != nil { + return "", nil, fmt.Errorf("error decoding certificate: %v", err) + } + + keyBytes, err := base64.StdEncoding.DecodeString(context.Key) + if err != nil { + return "", nil, fmt.Errorf("error decoding key: %v", err) + } + + creds = &Credentials{ + ca: caBytes, + crt: crtBytes, + key: keyBytes, + } + + return context.Target, creds, nil +} + +// NewClientCredentials initializes ClientCredentials using default paths +// to the required CA, certificate, and key. +func NewClientCredentials(ca, crt, key []byte) (creds *Credentials) { + creds = &Credentials{ + ca: ca, + crt: crt, + key: key, + } + + return creds } // NewClient initializes a Client. -func NewClient(port int, clientcreds *Credentials) (c *Client, err error) { +func NewClient(creds *Credentials, target string, port int) (c *Client, err error) { grpcOpts := []grpc.DialOption{} c = &Client{} - crt, err := tls.X509KeyPair(clientcreds.crt, clientcreds.key) + crt, err := tls.X509KeyPair(creds.crt, creds.key) if err != nil { return nil, fmt.Errorf("could not load client key pair: %s", err) } certPool := x509.NewCertPool() - if ok := certPool.AppendCertsFromPEM(clientcreds.ca); !ok { + if ok := certPool.AppendCertsFromPEM(creds.ca); !ok { return nil, fmt.Errorf("failed to append client certs") } // TODO(andrewrynhard): Do not parse the address. Pass the IP and port in as separate // parameters. - creds := credentials.NewTLS(&tls.Config{ - ServerName: clientcreds.Target, + transportCreds := credentials.NewTLS(&tls.Config{ + ServerName: target, Certificates: []tls.Certificate{crt}, // Set the root certificate authorities to use the self-signed // certificate. RootCAs: certPool, }) - grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(creds)) - c.conn, err = grpc.Dial(fmt.Sprintf("%s:%d", net.FormatAddress(clientcreds.Target), port), grpcOpts...) + grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(transportCreds)) + c.conn, err = grpc.Dial(fmt.Sprintf("%s:%d", net.FormatAddress(target), port), grpcOpts...) if err != nil { return } diff --git a/internal/app/machined/internal/phase/userdata/pki.go b/internal/app/machined/internal/phase/userdata/pki.go deleted file mode 100644 index 8ceca1968..000000000 --- a/internal/app/machined/internal/phase/userdata/pki.go +++ /dev/null @@ -1,61 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package userdata - -import ( - "log" - - "github.com/pkg/errors" - - "github.com/talos-systems/talos/internal/app/machined/internal/phase" - "github.com/talos-systems/talos/internal/pkg/platform" - "github.com/talos-systems/talos/internal/pkg/runtime" - "github.com/talos-systems/talos/pkg/constants" - "github.com/talos-systems/talos/pkg/crypto/x509" - "github.com/talos-systems/talos/pkg/grpc/gen" - "github.com/talos-systems/talos/pkg/userdata" -) - -// PKI represents the PKI task. -type PKI struct{} - -// NewPKITask initializes and returns an UserData task. -func NewPKITask() phase.Task { - return &PKI{} -} - -// RuntimeFunc returns the runtime function. -func (task *PKI) RuntimeFunc(mode runtime.Mode) phase.RuntimeFunc { - return task.runtime -} - -func (task *PKI) runtime(platform platform.Platform, data *userdata.UserData) (err error) { - if data.Services.Kubeadm.IsControlPlane() { - log.Println("generating PKI locally") - var csr *x509.CertificateSigningRequest - if csr, err = data.NewIdentityCSR(); err != nil { - return err - } - var crt *x509.Certificate - crt, err = x509.NewCertificateFromCSRBytes(data.Security.OS.CA.Crt, data.Security.OS.CA.Key, csr.X509CertificateRequestPEM) - if err != nil { - return err - } - data.Security.OS.Identity.Crt = crt.X509CertificatePEM - return nil - } - - log.Println("generating PKI from trustd") - var generator *gen.Generator - generator, err = gen.NewGenerator(data, constants.TrustdPort) - if err != nil { - return errors.Wrap(err, "failed to create trustd client") - } - if err = generator.Identity(data); err != nil { - return errors.Wrap(err, "failed to generate identity") - } - - return nil -} diff --git a/internal/app/machined/internal/sequencer/v1alpha1/types.go b/internal/app/machined/internal/sequencer/v1alpha1/types.go index 08d978ede..abda061ac 100644 --- a/internal/app/machined/internal/sequencer/v1alpha1/types.go +++ b/internal/app/machined/internal/sequencer/v1alpha1/types.go @@ -64,7 +64,6 @@ func (d *Sequencer) Boot() error { ), phase.NewPhase( "user requests", - userdatatask.NewPKITask(), userdatatask.NewExtraEnvVarsTask(), userdatatask.NewExtraFilesTask(), ), diff --git a/internal/app/machined/pkg/system/services/kubeadm/kubeadm.go b/internal/app/machined/pkg/system/services/kubeadm/kubeadm.go index 1e4389654..b47f5f197 100644 --- a/internal/app/machined/pkg/system/services/kubeadm/kubeadm.go +++ b/internal/app/machined/pkg/system/services/kubeadm/kubeadm.go @@ -221,18 +221,12 @@ func FileSet(files []string) []*securityapi.ReadFileRequest { return fileRequests } -// CreateSecurityClients handles instantiating a trustd client connection +// CreateSecurityClients handles instantiating a security API client connection // to each trustd endpoint defined in userdata -func CreateSecurityClients(data *userdata.UserData) ([]securityapi.SecurityClient, error) { - var creds basic.Credentials - var err error +func CreateSecurityClients(data *userdata.UserData) (clients []securityapi.SecurityClient, err error) { + clients = []securityapi.SecurityClient{} - trustds := []securityapi.SecurityClient{} - - creds, err = basic.NewCredentials(data.Services.Trustd) - if err != nil { - return trustds, err - } + creds := basic.NewTokenCredentials(data.Services.Trustd.Token) // Create a trustd client for each endpoint to set up // a fan out approach to gathering the files @@ -240,14 +234,14 @@ func CreateSecurityClients(data *userdata.UserData) ([]securityapi.SecurityClien for _, endpoint := range data.Services.Trustd.Endpoints { conn, err = basic.NewConnection(endpoint, constants.TrustdPort, creds) if err != nil { - return trustds, err + return clients, err } - trustds = append(trustds, securityapi.NewSecurityClient(conn)) + clients = append(clients, securityapi.NewSecurityClient(conn)) } - return trustds, nil + return clients, nil } -// Download handles the retrieval of files from a trustd endpoint +// Download handles the retrieval of files from a security API endpoint. func Download(ctx context.Context, client securityapi.SecurityClient, file *securityapi.ReadFileRequest, content chan<- []byte) { select { case <-ctx.Done(): diff --git a/internal/app/osd/main.go b/internal/app/osd/main.go index acae59581..75dfd5192 100644 --- a/internal/app/osd/main.go +++ b/internal/app/osd/main.go @@ -5,9 +5,10 @@ package main import ( - "context" "flag" "log" + stdlibnet "net" + "os" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -16,6 +17,7 @@ import ( "github.com/talos-systems/talos/pkg/constants" "github.com/talos-systems/talos/pkg/grpc/factory" "github.com/talos-systems/talos/pkg/grpc/tls" + "github.com/talos-systems/talos/pkg/net" "github.com/talos-systems/talos/pkg/startup" "github.com/talos-systems/talos/pkg/userdata" ) @@ -28,9 +30,10 @@ func init() { flag.Parse() } +// nolint: gocyclo func main() { if err := startup.RandSeed(); err != nil { - log.Fatalf("startup: %s", err) + log.Fatalf("failed to seed RNG: %s", err) } data, err := userdata.Open(*dataPath) @@ -38,40 +41,63 @@ func main() { log.Fatalf("open user data: %v", err) } - tlsCertProvider, err := tls.NewRenewingFileCertificateProvider(context.TODO(), data) + ips, err := net.IPAddrs() if err != nil { - log.Fatalln("failed to create new dynamic certificate provider:", err) + log.Fatalf("failed to discover IP addresses: %+v", err) } - config, err := tls.NewConfigWithOpts( + // TODO(andrewrynhard): Allow for DNS names. + for _, san := range data.Services.Trustd.CertSANs { + if ip := stdlibnet.ParseIP(san); ip != nil { + ips = append(ips, ip) + } + } + + hostname, err := os.Hostname() + if err != nil { + log.Fatalf("failed to discover hostname: %+v", err) + } + + var provider tls.CertificateProvider + provider, err = tls.NewRemoteRenewingFileCertificateProvider(data.Services.Trustd.Token, data.Services.Trustd.Endpoints, constants.TrustdPort, hostname, ips) + if err != nil { + log.Fatalf("failed to create remote certificate provider: %+v", err) + } + + ca, err := provider.GetCA() + if err != nil { + log.Fatalf("failed to get root CA: %+v", err) + } + + config, err := tls.New( tls.WithClientAuthType(tls.Mutual), - tls.WithCACertPEM(data.Security.OS.CA.Crt), - tls.WithCertificateProvider(tlsCertProvider)) + tls.WithCACertPEM(ca), + tls.WithCertificateProvider(provider), + ) if err != nil { log.Fatalf("failed to create OS-level TLS configuration: %v", err) } - MachineClient, err := reg.NewMachineClient() + machineClient, err := reg.NewMachineClient() if err != nil { log.Fatalf("init client: %v", err) } - TimeClient, err := reg.NewTimeClient() + timeClient, err := reg.NewTimeClient() if err != nil { log.Fatalf("ntp client: %v", err) } - NetworkClient, err := reg.NewNetworkClient() + networkClient, err := reg.NewNetworkClient() if err != nil { log.Fatalf("networkd client: %v", err) } - log.Println("Starting osd") err = factory.ListenAndServe( ®.Registrator{ Data: data, - MachineClient: MachineClient, - TimeClient: TimeClient, - NetworkClient: NetworkClient, + MachineClient: machineClient, + TimeClient: timeClient, + NetworkClient: networkClient, }, factory.Port(constants.OsdPort), factory.ServerOptions( diff --git a/internal/app/trustd/main.go b/internal/app/trustd/main.go index 6ea521c74..1912e2cbf 100644 --- a/internal/app/trustd/main.go +++ b/internal/app/trustd/main.go @@ -7,6 +7,8 @@ package main import ( "flag" "log" + stdlibnet "net" + "os" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -16,6 +18,7 @@ import ( "github.com/talos-systems/talos/pkg/grpc/factory" "github.com/talos-systems/talos/pkg/grpc/middleware/auth/basic" "github.com/talos-systems/talos/pkg/grpc/tls" + "github.com/talos-systems/talos/pkg/net" "github.com/talos-systems/talos/pkg/startup" "github.com/talos-systems/talos/pkg/userdata" ) @@ -28,6 +31,7 @@ func init() { flag.Parse() } +// nolint: gocyclo func main() { var err error @@ -37,18 +41,45 @@ func main() { data, err := userdata.Open(*dataPath) if err != nil { - log.Fatalf("credentials: %v", err) + log.Fatalf("failed to open machine config: %v", err) } - config, err := tls.NewConfig(tls.ServerOnly, data.Security.OS) - if err != nil { - log.Fatalf("credentials: %v", err) - } - - creds, err := basic.NewCredentials(data.Services.Trustd) + ips, err := net.IPAddrs() if err != nil { log.Fatal(err) } + for _, san := range data.Services.Trustd.CertSANs { + if ip := stdlibnet.ParseIP(san); ip != nil { + ips = append(ips, ip) + } + } + + hostname, err := os.Hostname() + if err != nil { + log.Fatal(err) + } + + var provider tls.CertificateProvider + provider, err = tls.NewLocalRenewingFileCertificateProvider(data.Security.OS.CA.Key, data.Security.OS.CA.Crt, hostname, ips) + if err != nil { + log.Fatalln("failed to create local certificate provider:", err) + } + + ca, err := provider.GetCA() + if err != nil { + log.Fatal(err) + } + + config, err := tls.New( + tls.WithClientAuthType(tls.ServerOnly), + tls.WithCACertPEM(ca), + tls.WithCertificateProvider(provider), + ) + if err != nil { + log.Fatalf("failed to create TLS config: %v", err) + } + + creds := basic.NewTokenCredentials(data.Services.Trustd.Token) err = factory.ListenAndServe( ®.Registrator{Data: data.Security.OS}, diff --git a/internal/pkg/platform/iso/iso.go b/internal/pkg/platform/iso/iso.go index c75427c50..323207d27 100644 --- a/internal/pkg/platform/iso/iso.go +++ b/internal/pkg/platform/iso/iso.go @@ -23,8 +23,7 @@ func (i *ISO) UserData() (data *userdata.UserData, err error) { data = &userdata.UserData{ Security: &userdata.Security{ OS: &userdata.OSSecurity{ - CA: &x509.PEMEncodedCertificateAndKey{}, - Identity: &x509.PEMEncodedCertificateAndKey{}, + CA: &x509.PEMEncodedCertificateAndKey{}, }, Kubernetes: &userdata.KubernetesSecurity{ CA: &x509.PEMEncodedCertificateAndKey{}, diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 15d10cfe1..fc715dd10 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -175,11 +175,8 @@ const ( // RootfsAsset defines a well known name for our rootfs filename RootfsAsset = "rootfs.sqsh" - // NodeCertFile is the filename where the current Talos Node Certificate may be found - NodeCertFile = SystemRunPath + "/talos-node.crt" - - // NodeCertRenewalInterval is the default interval at which Talos Node Certifications should be renewed - NodeCertRenewalInterval = 24 * time.Hour + // DefaultCertificateValidityDuration is the default duration for a certificate. + DefaultCertificateValidityDuration = 24 * time.Hour // SystemVarPath is the path to write runtime system related files and // directories. diff --git a/pkg/crypto/x509/x509.go b/pkg/crypto/x509/x509.go index 39f6b263b..1195bb3f6 100644 --- a/pkg/crypto/x509/x509.go +++ b/pkg/crypto/x509/x509.go @@ -22,6 +22,8 @@ import ( "net" "strings" "time" + + "github.com/talos-systems/talos/pkg/constants" ) // CertificateAuthority represents a CA. @@ -136,7 +138,7 @@ func NewDefaultOptions(setters ...Option) *Options { DNSNames: []string{}, Bits: 4096, RSA: false, - NotAfter: time.Now().Add(24 * time.Hour), + NotAfter: time.Now().Add(constants.DefaultCertificateValidityDuration), } for _, setter := range setters { @@ -385,6 +387,41 @@ func NewCertificateAndKeyFromFiles(crt, key string) (p *PEMEncodedCertificateAnd return p, nil } +// NewCSRAndIdentity generates and PEM encoded certificate and key, along with a +// CSR for the generated key. +func NewCSRAndIdentity(hostname string, ips []net.IP) (csr *CertificateSigningRequest, identity *PEMEncodedCertificateAndKey, err error) { + var key *Key + key, err = NewKey() + if err != nil { + return nil, nil, err + } + + identity = &PEMEncodedCertificateAndKey{ + Key: key.KeyPEM, + } + + pemBlock, _ := pem.Decode(key.KeyPEM) + if pemBlock == nil { + return nil, nil, fmt.Errorf("failed to decode key") + } + + keyEC, err := x509.ParseECPrivateKey(pemBlock.Bytes) + if err != nil { + return nil, nil, err + } + + opts := []Option{} + opts = append(opts, DNSNames([]string{hostname})) + opts = append(opts, IPAddresses(ips)) + + csr, err = NewCertificateSigningRequest(keyEC, opts...) + if err != nil { + return nil, nil, err + } + + return csr, identity, nil +} + // UnmarshalYAML implements the yaml.Unmarshaler interface for // PEMEncodedCertificateAndKey. It is expected that the Crt and Key are a base64 // encoded string in the YAML file. This function decodes the strings into byte diff --git a/pkg/grpc/gen/local.go b/pkg/grpc/gen/local.go new file mode 100644 index 000000000..cf4c35428 --- /dev/null +++ b/pkg/grpc/gen/local.go @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package gen + +import ( + "github.com/talos-systems/talos/pkg/crypto/x509" +) + +// LocalGenerator represents the OS identity generator. +type LocalGenerator struct { + caKey []byte + caCrt []byte +} + +// NewLocalGenerator initializes a LocalGenerator. +func NewLocalGenerator(caKey, caCrt []byte) (g *LocalGenerator, err error) { + g = &LocalGenerator{caKey, caCrt} + + return g, nil +} + +// Identity creates an identity certificate using a local root CA. +func (g *LocalGenerator) Identity(csr *x509.CertificateSigningRequest) (ca, crt []byte, err error) { + var c *x509.Certificate + c, err = x509.NewCertificateFromCSRBytes(g.caCrt, g.caKey, csr.X509CertificateRequestPEM) + if err != nil { + return ca, crt, err + } + + crt = c.X509CertificatePEM + + return g.caCrt, crt, nil +} diff --git a/pkg/grpc/gen/gen.go b/pkg/grpc/gen/remote.go similarity index 51% rename from pkg/grpc/gen/gen.go rename to pkg/grpc/gen/remote.go index beb9887fe..8b0f843f9 100644 --- a/pkg/grpc/gen/gen.go +++ b/pkg/grpc/gen/remote.go @@ -16,37 +16,33 @@ import ( securityapi "github.com/talos-systems/talos/api/security" "github.com/talos-systems/talos/pkg/crypto/x509" "github.com/talos-systems/talos/pkg/grpc/middleware/auth/basic" - "github.com/talos-systems/talos/pkg/userdata" ) -// Generator represents the OS identity generator. -type Generator struct { +// RemoteGenerator represents the OS identity generator. +type RemoteGenerator struct { client securityapi.SecurityClient } -// NewGenerator initializes a Generator with a preconfigured grpc.ClientConn. -func NewGenerator(data *userdata.UserData, port int) (g *Generator, err error) { - if len(data.Services.Trustd.Endpoints) == 0 { +// NewRemoteGenerator initializes a RemoteGenerator with a preconfigured grpc.ClientConn. +func NewRemoteGenerator(token string, endpoints []string, port int) (g *RemoteGenerator, err error) { + if len(endpoints) == 0 { return nil, fmt.Errorf("at least one root of trust endpoint is required") } - creds, err := basic.NewCredentials(data.Services.Trustd) - if err != nil { - return nil, err - } + creds := basic.NewTokenCredentials(token) // Loop through trustd endpoints and attempt to download PKI var conn *grpc.ClientConn var multiError *multierror.Error - for i := 0; i < len(data.Services.Trustd.Endpoints); i++ { - conn, err = basic.NewConnection(data.Services.Trustd.Endpoints[i], port, creds) + for i := 0; i < len(endpoints); i++ { + conn, err = basic.NewConnection(endpoints[i], port, creds) if err != nil { multiError = multierror.Append(multiError, err) // Unable to connect, bail and attempt to contact next endpoint continue } client := securityapi.NewSecurityClient(conn) - return &Generator{client: client}, nil + return &RemoteGenerator{client: client}, nil } // We were unable to connect to any trustd endpoint @@ -55,35 +51,31 @@ func NewGenerator(data *userdata.UserData, port int) (g *Generator, err error) { } // Certificate implements the securityapi.SecurityClient interface. -func (g *Generator) Certificate(in *securityapi.CertificateRequest) (resp *securityapi.CertificateResponse, err error) { +func (g *RemoteGenerator) Certificate(in *securityapi.CertificateRequest) (resp *securityapi.CertificateResponse, err error) { ctx := context.Background() resp, err = g.client.Certificate(ctx, in) if err != nil { - return + return nil, err } return resp, err } -// Identity creates a CSR and sends it to trustd for signing. -// A signed certificate is returned. -func (g *Generator) Identity(data *userdata.UserData) (err error) { - if data.Security == nil { - data.Security = &userdata.Security{} - } - data.Security.OS = &userdata.OSSecurity{CA: &x509.PEMEncodedCertificateAndKey{}} - var csr *x509.CertificateSigningRequest - if csr, err = data.NewIdentityCSR(); err != nil { - return err - } +// Identity creates an identity certificate via the security API. +func (g *RemoteGenerator) Identity(csr *x509.CertificateSigningRequest) (ca, crt []byte, err error) { req := &securityapi.CertificateRequest{ Csr: csr.X509CertificateRequestPEM, } - return poll(g, req, data.Security.OS) + ca, crt, err = g.poll(req) + if err != nil { + return nil, nil, err + } + + return ca, crt, nil } -func poll(g *Generator, in *securityapi.CertificateRequest, data *userdata.OSSecurity) (err error) { +func (g *RemoteGenerator) poll(in *securityapi.CertificateRequest) (ca []byte, crt []byte, err error) { timeout := time.NewTimer(time.Minute * 5) defer timeout.Stop() tick := time.NewTicker(time.Second * 5) @@ -92,18 +84,19 @@ func poll(g *Generator, in *securityapi.CertificateRequest, data *userdata.OSSec for { select { case <-timeout.C: - return fmt.Errorf("timeout waiting for certificate") + return nil, nil, fmt.Errorf("timeout waiting for certificate") case <-tick.C: - resp, _err := g.Certificate(in) - if _err != nil { - log.Println(_err) + var resp *securityapi.CertificateResponse + resp, err = g.Certificate(in) + if err != nil { + log.Println(err) continue } - data.CA = &x509.PEMEncodedCertificateAndKey{} - data.CA.Crt = resp.Ca - data.Identity.Crt = resp.Crt - return nil + ca = resp.Ca + crt = resp.Crt + + return ca, crt, nil } } } diff --git a/pkg/grpc/middleware/auth/basic/basic.go b/pkg/grpc/middleware/auth/basic/basic.go index f4648d2e4..b65c79568 100644 --- a/pkg/grpc/middleware/auth/basic/basic.go +++ b/pkg/grpc/middleware/auth/basic/basic.go @@ -6,14 +6,12 @@ package basic import ( "crypto/tls" - "errors" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "github.com/talos-systems/talos/pkg/net" - "github.com/talos-systems/talos/pkg/userdata" ) // Credentials describes an authorization method. @@ -43,19 +41,3 @@ func NewConnection(address string, port int, creds credentials.PerRPCCredentials return conn, nil } - -// NewCredentials returns credentials.PerRPCCredentials based on username and -// password, or a token. The token method takes precedence over the username -// and password. -func NewCredentials(data *userdata.Trustd) (creds Credentials, err error) { - switch { - case data.Username != "" && data.Password != "": - creds = NewUsernameAndPasswordCredentials(data.Username, data.Password) - case data.Token != "": - creds = NewTokenCredentials(data.Token) - default: - return nil, errors.New("failed to find valid credentials") - } - - return creds, nil -} diff --git a/pkg/grpc/tls/cert.go b/pkg/grpc/tls/cert.go deleted file mode 100644 index 7a624664d..000000000 --- a/pkg/grpc/tls/cert.go +++ /dev/null @@ -1,185 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package tls - -import ( - "context" - "crypto/tls" - "crypto/x509" - "io/ioutil" - "log" - "sync" - "time" - - "github.com/pkg/errors" - - "github.com/talos-systems/talos/pkg/constants" - "github.com/talos-systems/talos/pkg/grpc/gen" - "github.com/talos-systems/talos/pkg/userdata" -) - -// CertificateProvider describes an interface by which TLS certificates may be managed -type CertificateProvider interface { - - // GetCertificate returns the current certificate matching the given client request - GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) - - // UpdateCertificate updates the stored certificate for the given client request - UpdateCertificate(h *tls.ClientHelloInfo, cert *tls.Certificate) error -} - -type singleCertificateProvider struct { - sync.RWMutex - cert *tls.Certificate - - updateHooks []func(newCert *tls.Certificate) -} - -func (p *singleCertificateProvider) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { - if p == nil { - return nil, errors.New("no provider") - } - - p.RLock() - defer p.RUnlock() - return p.cert, nil -} - -func (p *singleCertificateProvider) UpdateCertificate(h *tls.ClientHelloInfo, cert *tls.Certificate) error { - p.Lock() - p.cert = cert - p.Unlock() - - for _, f := range p.updateHooks { - f(cert) - } - return nil -} - -type userDataCertificateProvider struct { - data *userdata.OSSecurity -} - -func (p *userDataCertificateProvider) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { - cert, err := tls.X509KeyPair(p.data.Identity.Crt, p.data.Identity.Key) - return &cert, err -} - -func (p *userDataCertificateProvider) UpdateCertificate(h *tls.ClientHelloInfo, cert *tls.Certificate) error { - // No-op - return nil -} - -type renewingFileCertificateProvider struct { - singleCertificateProvider - - certFile string - - // For now, this is using the complete userdata object. It should probably - // be pared down to just what is necessary, later. However, the current TLS - // generator code requires the whole thing... so we do, too. - data *userdata.UserData - - g *gen.Generator -} - -// NewRenewingFileCertificateProvider returns a new CertificateProvider which -// manages and updates its certificates from trustd, storing a cache file copy. -// -// TODO: the flow here is a bit wonky, but it should be fixable after we change -// to have the default be ephemeral node certificates. Until then, however, we -// are doing a dance between the userdata-stored cert, the filesystem-cached -// cert, and the memory-cached cert. -func NewRenewingFileCertificateProvider(ctx context.Context, data *userdata.UserData) (CertificateProvider, error) { - g, err := gen.NewGenerator(data, constants.TrustdPort) - if err != nil { - return nil, errors.Wrap(err, "failed to create TLS generator") - } - - p := &renewingFileCertificateProvider{ - g: g, - data: data, - certFile: constants.NodeCertFile, - } - - if err = p.loadInitialCert(); err != nil { - return nil, errors.Wrap(err, "failed to load initial certificate") - } - - go p.manageUpdates(ctx) - - return p, nil -} - -func (p *renewingFileCertificateProvider) loadInitialCert() error { - // TODO: eventually, we will reverse this priority, and have the override - // come from the userdata. For now, however, we use the local file to - // override the userdata, because we _always_ have userdata certs, and the - // userdata is intended to be immutable. - - data, err := ioutil.ReadFile(p.certFile) - if err != nil || len(data) == 0 { - // If we cannot read the cert from the file, then we use the userdata-supplied one - data = p.data.Security.OS.Identity.Crt - } - - cert, err := tls.X509KeyPair(data, p.data.Security.OS.Identity.Key) - if err != nil { - return errors.Wrap(err, "failed to parse cert and key into a TLS Certificate") - } - - return p.UpdateCertificate(nil, &cert) -} - -func (p *renewingFileCertificateProvider) manageUpdates(ctx context.Context) { - nextRenewal := constants.NodeCertRenewalInterval - - for ctx.Err() == nil { - if c, _ := p.GetCertificate(nil); c != nil { // nolint: errcheck - if len(c.Certificate) > 0 { - cert, err := x509.ParseCertificate(c.Certificate[0]) - if err == nil { - nextRenewal = time.Until(cert.NotAfter) / 2 - } else { - log.Println("failed to parse current leaf certificate") - } - } else { - log.Println("current leaf certificate not found") - } - } else { - log.Println("certificate not found") - } - - if nextRenewal > constants.NodeCertRenewalInterval { - nextRenewal = constants.NodeCertRenewalInterval - } - - select { - case <-time.After(nextRenewal): - case <-ctx.Done(): - return - } - if err := p.renewCert(); err != nil { - log.Println("failed to renew certificate:", err) - continue - } - } -} - -func (p *renewingFileCertificateProvider) renewCert() error { - if err := p.g.Identity(p.data); err != nil { - return errors.Wrap(err, "failed to renew certificate") - } - - // TODO: updating the cert using the generator automatically stores the new - // cert to userdata. Therefore, we need to pull that cert out in order to - // update the CertificateProvider's cache of it - cert, err := tls.X509KeyPair(p.data.Security.OS.Identity.Crt, p.data.Security.OS.Identity.Key) - if err != nil { - return errors.Wrap(err, "failed to parse cert and key into a TLS Certificate") - } - - return p.UpdateCertificate(nil, &cert) -} diff --git a/pkg/grpc/tls/local.go b/pkg/grpc/tls/local.go new file mode 100644 index 000000000..f69dd0ee0 --- /dev/null +++ b/pkg/grpc/tls/local.go @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package tls + +import ( + "context" + "crypto/tls" + "net" + + "github.com/pkg/errors" + + "github.com/talos-systems/talos/pkg/crypto/x509" + "github.com/talos-systems/talos/pkg/grpc/gen" +) + +type renewingLocalCertificateProvider struct { + embeddableCertificateProvider + + caKey []byte + caCrt []byte + + generator *gen.LocalGenerator +} + +// NewLocalRenewingFileCertificateProvider returns a new CertificateProvider +// which manages and updates its certificates using a local key. +func NewLocalRenewingFileCertificateProvider(caKey, caCrt []byte, hostname string, ips []net.IP) (CertificateProvider, error) { + g, err := gen.NewLocalGenerator(caKey, caCrt) + if err != nil { + return nil, errors.Wrap(err, "failed to create TLS generator") + } + + provider := &renewingLocalCertificateProvider{ + caKey: caKey, + caCrt: caCrt, + generator: g, + } + + provider.embeddableCertificateProvider = embeddableCertificateProvider{ + hostname: hostname, + ips: ips, + updateFunc: provider.update, + } + + var ( + ca []byte + cert tls.Certificate + ) + if ca, cert, err = provider.updateFunc(); err != nil { + return nil, errors.Wrap(err, "failed to create initial certificate") + } + + if err = provider.UpdateCertificates(ca, &cert); err != nil { + return nil, err + } + + // nolint: errcheck + go provider.manageUpdates(context.Background()) + + return provider, nil +} + +// nolint: dupl +func (p *renewingLocalCertificateProvider) update() (ca []byte, cert tls.Certificate, err error) { + var ( + crt []byte + csr *x509.CertificateSigningRequest + identity *x509.PEMEncodedCertificateAndKey + ) + + csr, identity, err = x509.NewCSRAndIdentity(p.hostname, p.ips) + if err != nil { + return nil, cert, err + } + + if ca, crt, err = p.generator.Identity(csr); err != nil { + return nil, cert, errors.Wrap(err, "failed to generate identity") + } + + identity.Crt = crt + + cert, err = tls.X509KeyPair(identity.Crt, identity.Key) + if err != nil { + return nil, cert, errors.Wrap(err, "failed to parse cert and key into a TLS Certificate") + } + + return ca, cert, nil +} diff --git a/pkg/grpc/tls/provider.go b/pkg/grpc/tls/provider.go new file mode 100644 index 000000000..14026dfbf --- /dev/null +++ b/pkg/grpc/tls/provider.go @@ -0,0 +1,129 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package tls + +import ( + "context" + "crypto/tls" + "crypto/x509" + "log" + "net" + "sync" + "time" + + "github.com/pkg/errors" + + "github.com/talos-systems/talos/pkg/constants" +) + +// CertificateProvider describes an interface by which TLS certificates may be managed. +type CertificateProvider interface { + // GetCA returns the active root CA. + GetCA() ([]byte, error) + + // GetCertificate returns the current certificate matching the given client request. + GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) + + // UpdateCertificate updates the stored certificate for the given client request. + UpdateCertificates([]byte, *tls.Certificate) error +} + +type embeddableCertificateProvider struct { + sync.RWMutex + + ca []byte + crt *tls.Certificate + + hostname string + ips []net.IP + + updateFunc func() ([]byte, tls.Certificate, error) + updateHooks []func(newCert *tls.Certificate) +} + +func (p *embeddableCertificateProvider) GetCA() ([]byte, error) { + if p == nil { + return nil, errors.New("no provider") + } + + p.RLock() + defer p.RUnlock() + + return p.ca, nil +} + +func (p *embeddableCertificateProvider) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { + if p == nil { + return nil, errors.New("no provider") + } + + p.RLock() + defer p.RUnlock() + + return p.crt, nil +} + +func (p *embeddableCertificateProvider) UpdateCertificates(ca []byte, cert *tls.Certificate) error { + p.Lock() + p.ca = ca + p.crt = cert + p.Unlock() + + for _, f := range p.updateHooks { + f(cert) + } + + return nil +} + +func (p *embeddableCertificateProvider) manageUpdates(ctx context.Context) (err error) { + nextRenewal := constants.DefaultCertificateValidityDuration + + for ctx.Err() == nil { + // nolint: errcheck + if c, _ := p.GetCertificate(nil); c != nil { + if len(c.Certificate) > 0 { + var crt *x509.Certificate + crt, err = x509.ParseCertificate(c.Certificate[0]) + if err == nil { + nextRenewal = time.Until(crt.NotAfter) / 2 + } else { + log.Println("failed to parse current leaf certificate") + } + } else { + log.Println("current leaf certificate not found") + } + } else { + log.Println("certificate not found") + } + + log.Println("next renewal in", nextRenewal) + if nextRenewal > constants.DefaultCertificateValidityDuration { + nextRenewal = constants.DefaultCertificateValidityDuration + } + + select { + case <-time.After(nextRenewal): + case <-ctx.Done(): + return nil + } + + var ( + ca []byte + cert tls.Certificate + ) + if ca, cert, err = p.updateFunc(); err != nil { + log.Println("failed to renew certificate:", err) + continue + } + + if err = p.UpdateCertificates(ca, &cert); err != nil { + log.Println("failed to renew certificate:", err) + continue + } + } + + return errors.New("certificate update manager exited unexpectedly") +} diff --git a/pkg/grpc/tls/remote.go b/pkg/grpc/tls/remote.go new file mode 100644 index 000000000..2c9c77cc8 --- /dev/null +++ b/pkg/grpc/tls/remote.go @@ -0,0 +1,85 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package tls + +import ( + "context" + "crypto/tls" + "net" + + "github.com/pkg/errors" + + "github.com/talos-systems/talos/pkg/crypto/x509" + "github.com/talos-systems/talos/pkg/grpc/gen" +) + +type renewingRemoteCertificateProvider struct { + embeddableCertificateProvider + + generator *gen.RemoteGenerator +} + +// NewRemoteRenewingFileCertificateProvider returns a new CertificateProvider +// which manages and updates its certificates from the security API. +func NewRemoteRenewingFileCertificateProvider(token string, endpoints []string, port int, hostname string, ips []net.IP) (CertificateProvider, error) { + g, err := gen.NewRemoteGenerator(token, endpoints, port) + if err != nil { + return nil, errors.Wrap(err, "failed to create TLS generator") + } + + provider := &renewingRemoteCertificateProvider{ + generator: g, + } + + provider.embeddableCertificateProvider = embeddableCertificateProvider{ + hostname: hostname, + ips: ips, + updateFunc: provider.update, + } + + var ( + ca []byte + cert tls.Certificate + ) + if ca, cert, err = provider.updateFunc(); err != nil { + return nil, errors.Wrap(err, "failed to create initial certificate") + } + + if err = provider.UpdateCertificates(ca, &cert); err != nil { + return nil, err + } + + // nolint: errcheck + go provider.manageUpdates(context.Background()) + + return provider, nil +} + +// nolint: dupl +func (p *renewingRemoteCertificateProvider) update() (ca []byte, cert tls.Certificate, err error) { + var ( + crt []byte + csr *x509.CertificateSigningRequest + identity *x509.PEMEncodedCertificateAndKey + ) + + csr, identity, err = x509.NewCSRAndIdentity(p.hostname, p.ips) + if err != nil { + return nil, cert, err + } + + if ca, crt, err = p.generator.Identity(csr); err != nil { + return nil, cert, errors.Wrap(err, "failed to generate identity") + } + + identity.Crt = crt + + cert, err = tls.X509KeyPair(identity.Crt, identity.Key) + if err != nil { + return nil, cert, errors.Wrap(err, "failed to parse cert and key into a TLS Certificate") + } + + return ca, cert, nil +} diff --git a/pkg/grpc/tls/tls.go b/pkg/grpc/tls/tls.go index 840042c9c..64a277d37 100644 --- a/pkg/grpc/tls/tls.go +++ b/pkg/grpc/tls/tls.go @@ -9,8 +9,6 @@ import ( "crypto/x509" "github.com/pkg/errors" - - "github.com/talos-systems/talos/pkg/userdata" ) // Type represents the TLS authentication type. @@ -47,8 +45,7 @@ func WithClientAuthType(t Type) func(*tls.Config) error { // certificate. // // NOTE: specifying this option will CLEAR any configured Certificates, since -// they would otherwise override this option -// +// they would otherwise override this option. func WithCertificateProvider(p CertificateProvider) func(*tls.Config) error { return func(cfg *tls.Config) error { if p == nil { @@ -101,8 +98,8 @@ func defaultConfig() *tls.Config { } } -// NewConfigWithOpts returns a new TLS Configuration modified by any provided configuration options -func NewConfigWithOpts(opts ...ConfigOptionFunc) (cfg *tls.Config, err error) { +// New returns a new TLS Configuration modified by any provided configuration options +func New(opts ...ConfigOptionFunc) (cfg *tls.Config, err error) { cfg = defaultConfig() for _, f := range opts { @@ -112,20 +109,3 @@ func NewConfigWithOpts(opts ...ConfigOptionFunc) (cfg *tls.Config, err error) { } return } - -// NewConfig initializes a TLS config for the specified type. -func NewConfig(t Type, data *userdata.OSSecurity) (config *tls.Config, err error) { - config = defaultConfig() - - if err = WithClientAuthType(t)(config); err != nil { - return nil, errors.Wrap(err, "failed to apply ClientAuthType preference") - } - if err = WithCACertPEM(data.CA.Crt)(config); err != nil { - return nil, errors.Wrap(err, "failed to apply CA Certificate from UserData") - } - if err = WithCertificateProvider(&userDataCertificateProvider{data: data})(config); err != nil { - return nil, errors.Wrap(err, "failed to apply userdata-sourced CertificateProvider") - } - - return -} diff --git a/pkg/userdata/os_security.go b/pkg/userdata/os_security.go index c8dcf9c6b..3cb66043a 100644 --- a/pkg/userdata/os_security.go +++ b/pkg/userdata/os_security.go @@ -12,8 +12,7 @@ import ( // OSSecurity represents the set of security options specific to the OS. type OSSecurity struct { - CA *x509.PEMEncodedCertificateAndKey `yaml:"ca"` - Identity *x509.PEMEncodedCertificateAndKey `yaml:"identity"` + CA *x509.PEMEncodedCertificateAndKey `yaml:"ca"` } // OSSecurityCheck defines the function type for checks diff --git a/pkg/userdata/userdata.go b/pkg/userdata/userdata.go index 2bba32765..d3acaac28 100644 --- a/pkg/userdata/userdata.go +++ b/pkg/userdata/userdata.go @@ -5,11 +5,9 @@ package userdata import ( - stdlibx509 "crypto/x509" "encoding/pem" "fmt" "io/ioutil" - stdlibnet "net" "os" "strings" @@ -17,7 +15,6 @@ import ( "golang.org/x/xerrors" "github.com/talos-systems/talos/pkg/crypto/x509" - "github.com/talos-systems/talos/pkg/net" yaml "gopkg.in/yaml.v2" ) @@ -90,50 +87,6 @@ type File struct { Path string `yaml:"path"` } -// NewIdentityCSR creates a new CSR for the node's identity certificate. -func (data *UserData) NewIdentityCSR() (csr *x509.CertificateSigningRequest, err error) { - var key *x509.Key - key, err = x509.NewKey() - if err != nil { - return nil, err - } - - data.Security.OS.Identity = &x509.PEMEncodedCertificateAndKey{} - data.Security.OS.Identity.Key = key.KeyPEM - - pemBlock, _ := pem.Decode(key.KeyPEM) - if pemBlock == nil { - return nil, fmt.Errorf("failed to decode key") - } - keyEC, err := stdlibx509.ParseECPrivateKey(pemBlock.Bytes) - if err != nil { - return nil, err - } - ips, err := net.IPAddrs() - if err != nil { - return nil, err - } - for _, san := range data.Services.Trustd.CertSANs { - if ip := stdlibnet.ParseIP(san); ip != nil { - ips = append(ips, ip) - } - } - hostname, err := os.Hostname() - if err != nil { - return - } - opts := []x509.Option{} - names := []string{hostname} - opts = append(opts, x509.DNSNames(names)) - opts = append(opts, x509.IPAddresses(ips)) - csr, err = x509.NewCertificateSigningRequest(keyEC, opts...) - if err != nil { - return nil, err - } - - return csr, nil -} - // Open is a convenience function that reads the user data from disk, and // unmarshals it. func Open(p string) (data *UserData, err error) {