diff --git a/federation/apply_federation.go b/federation/apply_federation.go index 9959b44c97..3740c61408 100644 --- a/federation/apply_federation.go +++ b/federation/apply_federation.go @@ -36,6 +36,7 @@ import ( "k8s.io/kops/pkg/apis/kops/registry" "k8s.io/kops/pkg/client/simple" "k8s.io/kops/pkg/kubeconfig" + "k8s.io/kops/pkg/pki" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/fitasks" "k8s.io/kops/upup/pkg/fi/k8sapi" @@ -398,7 +399,7 @@ func (o *ApplyFederationOperation) ensureFederationNamespace(k8s federation_clie }) } -func EnsureCASecret(keystore fi.Keystore) (*fi.Certificate, *fi.PrivateKey, error) { +func EnsureCASecret(keystore fi.Keystore) (*pki.Certificate, *pki.PrivateKey, error) { id := fi.CertificateId_CA caCert, caPrivateKey, err := keystore.FindKeypair(id) if err != nil { @@ -411,9 +412,9 @@ func EnsureCASecret(keystore fi.Keystore) (*fi.Certificate, *fi.PrivateKey, erro return nil, nil, fmt.Errorf("error generating RSA private key: %v", err) } - caPrivateKey = &fi.PrivateKey{Key: caRsaKey} + caPrivateKey = &pki.PrivateKey{Key: caRsaKey} - caCert, err = fi.SignNewCertificate(caPrivateKey, template, nil, nil) + caCert, err = pki.SignNewCertificate(caPrivateKey, template, nil, nil) if err != nil { return nil, nil, err } diff --git a/federation/federation_configuration.go b/federation/federation_configuration.go index 39fcb0abc2..806dd5f381 100644 --- a/federation/federation_configuration.go +++ b/federation/federation_configuration.go @@ -26,6 +26,7 @@ import ( "k8s.io/kops/federation/targets/kubernetestarget" kopsapi "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/kubeconfig" + "k8s.io/kops/pkg/pki" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/fitasks" ) @@ -275,7 +276,7 @@ func (o *FederationConfiguration) EnsureConfiguration(c *fi.Context) error { return nil } -func (o *FederationConfiguration) ensureSecretKubeconfig(c *fi.Context, caCert *fi.Certificate, user kubeconfig.KubectlUser) error { +func (o *FederationConfiguration) ensureSecretKubeconfig(c *fi.Context, caCert *pki.Certificate, user kubeconfig.KubectlUser) error { k8s := c.Target.(*kubernetestarget.KubernetesTarget).KubernetesClient _, err := mutateSecret(k8s, o.Namespace, o.KubeconfigSecretName, func(s *v1.Secret) (*v1.Secret, error) { diff --git a/hack/.packages b/hack/.packages index 9f05e98008..067499f5a7 100644 --- a/hack/.packages +++ b/hack/.packages @@ -72,6 +72,7 @@ k8s.io/kops/pkg/model/iam k8s.io/kops/pkg/model/resources k8s.io/kops/pkg/model/vspheremodel k8s.io/kops/pkg/openapi +k8s.io/kops/pkg/pki k8s.io/kops/pkg/pretty k8s.io/kops/pkg/resources k8s.io/kops/pkg/resources/digitalocean diff --git a/pkg/pki/certificate.go b/pkg/pki/certificate.go new file mode 100644 index 0000000000..45980b3940 --- /dev/null +++ b/pkg/pki/certificate.go @@ -0,0 +1,152 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pki + +import ( + "bytes" + "crypto" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/json" + "encoding/pem" + "fmt" + "github.com/golang/glog" + "io" +) + +type Certificate struct { + Subject pkix.Name + IsCA bool + + Certificate *x509.Certificate + PublicKey crypto.PublicKey +} + +func (c *Certificate) UnmarshalJSON(b []byte) error { + s := "" + if err := json.Unmarshal(b, &s); err == nil { + r, err := LoadPEMCertificate([]byte(s)) + if err != nil { + // Alternative form: Check if base64 encoded + // TODO: Do we need this? I think we need this only on nodeup, but maybe we could just not base64-it? + d, err2 := base64.StdEncoding.DecodeString(s) + if err2 == nil { + r2, err2 := LoadPEMCertificate(d) + if err2 == nil { + glog.Warningf("used base64 decode of certificate") + r = r2 + err = nil + } + } + + if err != nil { + glog.Infof("Invalid certificate data: %q", string(b)) + return fmt.Errorf("error parsing certificate: %v", err) + } + } + *c = *r + return nil + } + return fmt.Errorf("unknown format for Certificate: %q", string(b)) +} + +func (c *Certificate) MarshalJSON() ([]byte, error) { + var data bytes.Buffer + _, err := c.WriteTo(&data) + if err != nil { + return nil, fmt.Errorf("error writing SSL certificate: %v", err) + } + return json.Marshal(data.String()) +} + +func LoadPEMCertificate(pemData []byte) (*Certificate, error) { + cert, err := parsePEMCertificate(pemData) + if err != nil { + return nil, err + } + + c := &Certificate{ + Subject: cert.Subject, + Certificate: cert, + PublicKey: cert.PublicKey, + IsCA: cert.IsCA, + } + return c, nil +} + +var _ io.WriterTo = &Certificate{} + +func parsePEMCertificate(pemData []byte) (*x509.Certificate, error) { + for { + block, rest := pem.Decode(pemData) + if block == nil { + return nil, fmt.Errorf("could not parse certificate") + } + + if block.Type == "CERTIFICATE" { + glog.V(8).Infof("Parsing pem block: %q", block.Type) + return x509.ParseCertificate(block.Bytes) + } else { + glog.Infof("Ignoring unexpected PEM block: %q", block.Type) + } + + pemData = rest + } +} + +func (c *Certificate) AsString() (string, error) { + // Nicer behaviour because this is called from templates + if c == nil { + return "", fmt.Errorf("AsString called on nil Certificate") + } + + var data bytes.Buffer + _, err := c.WriteTo(&data) + if err != nil { + return "", fmt.Errorf("error writing SSL certificate: %v", err) + } + return data.String(), nil +} + +func (c *Certificate) AsBytes() ([]byte, error) { + // Nicer behaviour because this is called from templates + if c == nil { + return nil, fmt.Errorf("AsBytes called on nil Certificate") + } + + var data bytes.Buffer + _, err := c.WriteTo(&data) + if err != nil { + return nil, fmt.Errorf("error writing SSL certificate: %v", err) + } + return data.Bytes(), nil +} + +func (c *Certificate) WriteTo(w io.Writer) (int64, error) { + // For the dry-run case + if c.Certificate == nil { + return 0, nil + } + + var b bytes.Buffer + err := pem.Encode(&b, &pem.Block{Type: "CERTIFICATE", Bytes: c.Certificate.Raw}) + if err != nil { + return 0, err + } + return b.WriteTo(w) +} diff --git a/pkg/pki/csr.go b/pkg/pki/csr.go new file mode 100644 index 0000000000..e82187ccd6 --- /dev/null +++ b/pkg/pki/csr.go @@ -0,0 +1,108 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pki + +import ( + crypto_rand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "fmt" + "github.com/golang/glog" + "math/big" + "time" +) + +// BuildPKISerial produces a serial number for certs that is vanishingly unlikely to collide +// The timestamp should be provided as an input (time.Now().UnixNano()), and then we combine +// that with a 32 bit random crypto-rand integer. +// We also know that a bigger value was created later (modulo clock skew) +func BuildPKISerial(timestamp int64) *big.Int { + randomLimit := new(big.Int).Lsh(big.NewInt(1), 32) + randomComponent, err := crypto_rand.Int(crypto_rand.Reader, randomLimit) + if err != nil { + glog.Fatalf("error generating random number: %v", err) + } + + serial := big.NewInt(timestamp) + serial.Lsh(serial, 32) + serial.Or(serial, randomComponent) + + return serial +} + +func SignNewCertificate(privateKey *PrivateKey, template *x509.Certificate, signer *x509.Certificate, signerPrivateKey *PrivateKey) (*Certificate, error) { + if template.PublicKey == nil { + rsaPrivateKey, ok := privateKey.Key.(*rsa.PrivateKey) + if ok { + template.PublicKey = rsaPrivateKey.Public() + } + } + + if template.PublicKey == nil { + return nil, fmt.Errorf("PublicKey not set, and cannot be determined from %T", privateKey) + } + + now := time.Now() + if template.NotBefore.IsZero() { + template.NotBefore = now.Add(time.Hour * -48) + } + + if template.NotAfter.IsZero() { + template.NotAfter = now.Add(time.Hour * 10 * 365 * 24) + } + + if template.SerialNumber == nil { + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := crypto_rand.Int(crypto_rand.Reader, serialNumberLimit) + if err != nil { + return nil, fmt.Errorf("error generating certificate serial number: %s", err) + } + template.SerialNumber = serialNumber + } + var parent *x509.Certificate + if signer != nil { + parent = signer + } else { + parent = template + signerPrivateKey = privateKey + } + + if template.KeyUsage == 0 { + template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment + } + + if template.ExtKeyUsage == nil { + template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} + } + //c.SignatureAlgorithm = do we want to overrride? + + certificateData, err := x509.CreateCertificate(crypto_rand.Reader, template, parent, template.PublicKey, signerPrivateKey.Key) + if err != nil { + return nil, fmt.Errorf("error creating certificate: %v", err) + } + + c := &Certificate{} + c.PublicKey = template.PublicKey + + cert, err := x509.ParseCertificate(certificateData) + if err != nil { + return nil, fmt.Errorf("error parsing certificate: %v", err) + } + c.Certificate = cert + + return c, nil +} diff --git a/pkg/pki/privatekey.go b/pkg/pki/privatekey.go new file mode 100644 index 0000000000..e50ad9c271 --- /dev/null +++ b/pkg/pki/privatekey.go @@ -0,0 +1,171 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pki + +import ( + "bytes" + "crypto" + crypto_rand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "fmt" + "github.com/golang/glog" + "io" +) + +func ParsePEMPrivateKey(data []byte) (*PrivateKey, error) { + k, err := parsePEMPrivateKey(data) + if err != nil { + return nil, err + } + if k == nil { + return nil, nil + } + return &PrivateKey{Key: k}, nil +} + +func GeneratePrivateKey() (*PrivateKey, error) { + rsaKey, err := rsa.GenerateKey(crypto_rand.Reader, 2048) + if err != nil { + return nil, fmt.Errorf("error generating RSA private key: %v", err) + } + + privateKey := &PrivateKey{Key: rsaKey} + return privateKey, nil +} + +type PrivateKey struct { + Key crypto.PrivateKey +} + +func (k *PrivateKey) AsString() (string, error) { + // Nicer behaviour because this is called from templates + if k == nil { + return "", fmt.Errorf("AsString called on nil private key") + } + + var data bytes.Buffer + _, err := k.WriteTo(&data) + if err != nil { + return "", fmt.Errorf("error writing SSL private key: %v", err) + } + return data.String(), nil +} + +func (k *PrivateKey) AsBytes() ([]byte, error) { + // Nicer behaviour because this is called from templates + if k == nil { + return nil, fmt.Errorf("AsBytes called on nil private key") + } + + var data bytes.Buffer + _, err := k.WriteTo(&data) + if err != nil { + return nil, fmt.Errorf("error writing SSL PrivateKey: %v", err) + } + return data.Bytes(), nil +} + +func (k *PrivateKey) UnmarshalJSON(b []byte) (err error) { + s := "" + if err := json.Unmarshal(b, &s); err == nil { + r, err := parsePEMPrivateKey([]byte(s)) + if err != nil { + // Alternative form: Check if base64 encoded + // TODO: Do we need this? I think we need this only on nodeup, but maybe we could just not base64-it? + d, err2 := base64.StdEncoding.DecodeString(s) + if err2 == nil { + r2, err2 := parsePEMPrivateKey(d) + if err2 == nil { + glog.Warningf("used base64 decode of PrivateKey") + r = r2 + err = nil + } + } + + if err != nil { + return fmt.Errorf("error parsing private key: %v", err) + } + } + k.Key = r + return nil + } + + return fmt.Errorf("unknown format for private key: %q", string(b)) +} + +func (k *PrivateKey) MarshalJSON() ([]byte, error) { + var data bytes.Buffer + _, err := k.WriteTo(&data) + if err != nil { + return nil, fmt.Errorf("error writing SSL private key: %v", err) + } + return json.Marshal(data.String()) +} + +var _ io.WriterTo = &PrivateKey{} + +func (k *PrivateKey) WriteTo(w io.Writer) (int64, error) { + if k.Key == nil { + // For the dry-run case + return 0, nil + } + + var data bytes.Buffer + var err error + + switch pk := k.Key.(type) { + case *rsa.PrivateKey: + err = pem.Encode(w, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)}) + default: + return 0, fmt.Errorf("unknown private key type: %T", k.Key) + } + + if err != nil { + return 0, fmt.Errorf("error writing SSL private key: %v", err) + } + + return data.WriteTo(w) +} + +func parsePEMPrivateKey(pemData []byte) (crypto.PrivateKey, error) { + for { + block, rest := pem.Decode(pemData) + if block == nil { + return nil, fmt.Errorf("could not parse private key") + } + + if block.Type == "RSA PRIVATE KEY" { + glog.V(8).Infof("Parsing pem block: %q", block.Type) + return x509.ParsePKCS1PrivateKey(block.Bytes) + } else if block.Type == "PRIVATE KEY" { + glog.V(8).Infof("Parsing pem block: %q", block.Type) + k, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + return k.(crypto.PrivateKey), nil + } else { + glog.Infof("Ignoring unexpected PEM block: %q", block.Type) + } + + pemData = rest + } +} diff --git a/upup/pkg/fi/ca.go b/upup/pkg/fi/ca.go index 992283a436..bacb0c35c4 100644 --- a/upup/pkg/fi/ca.go +++ b/upup/pkg/fi/ca.go @@ -18,34 +18,15 @@ package fi import ( "bytes" - "crypto" - crypto_rand "crypto/rand" - "crypto/rsa" "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "encoding/json" - "encoding/pem" "fmt" - "io" - "math/big" - "time" + "k8s.io/kops/pkg/pki" "k8s.io/kops/util/pkg/vfs" - - "github.com/golang/glog" ) const CertificateId_CA = "ca" -type Certificate struct { - Subject pkix.Name - IsCA bool - - Certificate *x509.Certificate - PublicKey crypto.PublicKey -} - const ( SecretTypeSSHPublicKey = "SSHPublicKey" SecretTypeKeypair = "Keypair" @@ -62,76 +43,29 @@ type KeystoreItem struct { Data []byte } -func (c *Certificate) UnmarshalJSON(b []byte) error { - s := "" - if err := json.Unmarshal(b, &s); err == nil { - r, err := LoadPEMCertificate([]byte(s)) - if err != nil { - // Alternative form: Check if base64 encoded - // TODO: Do we need this? I think we need this only on nodeup, but maybe we could just not base64-it? - d, err2 := base64.StdEncoding.DecodeString(s) - if err2 == nil { - r2, err2 := LoadPEMCertificate(d) - if err2 == nil { - glog.Warningf("used base64 decode of certificate") - r = r2 - err = nil - } - } - - if err != nil { - glog.Infof("Invalid certificate data: %q", string(b)) - return fmt.Errorf("error parsing certificate: %v", err) - } - } - *c = *r - return nil - } - return fmt.Errorf("unknown format for Certificate: %q", string(b)) -} - -func (c *Certificate) MarshalJSON() ([]byte, error) { - var data bytes.Buffer - _, err := c.WriteTo(&data) - if err != nil { - return nil, fmt.Errorf("error writing SSL certificate: %v", err) - } - return json.Marshal(data.String()) -} - // Keystore contains just the functions we need to issue keypairs, not to list / manage them type Keystore interface { // FindKeypair finds a cert & private key, returning nil where either is not found // (if the certificate is found but not keypair, that is not an error: only the cert will be returned) - FindKeypair(name string) (*Certificate, *PrivateKey, error) + FindKeypair(name string) (*pki.Certificate, *pki.PrivateKey, error) - CreateKeypair(name string, template *x509.Certificate, privateKey *PrivateKey) (*Certificate, error) + CreateKeypair(name string, template *x509.Certificate, privateKey *pki.PrivateKey) (*pki.Certificate, error) // Store the keypair - StoreKeypair(id string, cert *Certificate, privateKey *PrivateKey) error -} - -func GeneratePrivateKey() (*PrivateKey, error) { - rsaKey, err := rsa.GenerateKey(crypto_rand.Reader, 2048) - if err != nil { - return nil, fmt.Errorf("error generating RSA private key: %v", err) - } - - privateKey := &PrivateKey{Key: rsaKey} - return privateKey, nil + StoreKeypair(id string, cert *pki.Certificate, privateKey *pki.PrivateKey) error } type CAStore interface { Keystore // Cert returns the primary specified certificate - Cert(name string) (*Certificate, error) + Cert(name string) (*pki.Certificate, error) // CertificatePool returns all active certificates with the specified id CertificatePool(name string) (*CertificatePool, error) - PrivateKey(name string) (*PrivateKey, error) + PrivateKey(name string) (*pki.PrivateKey, error) - FindCert(name string) (*Certificate, error) - FindPrivateKey(name string) (*PrivateKey, error) + FindCert(name string) (*pki.Certificate, error) + FindPrivateKey(name string) (*pki.PrivateKey, error) // List will list all the items, but will not fetch the data List() ([]*KeystoreItem, error) @@ -140,7 +74,7 @@ type CAStore interface { VFSPath() vfs.Path // AddCert adds an alternative certificate to the pool (primarily useful for CAs) - AddCert(name string, cert *Certificate) error + AddCert(name string, cert *pki.Certificate) error // AddSSHPublicKey adds an SSH public key AddSSHPublicKey(name string, data []byte) error @@ -152,268 +86,9 @@ type CAStore interface { DeleteSecret(item *KeystoreItem) error } -func (c *Certificate) AsString() (string, error) { - // Nicer behaviour because this is called from templates - if c == nil { - return "", fmt.Errorf("AsString called on nil Certificate") - } - - var data bytes.Buffer - _, err := c.WriteTo(&data) - if err != nil { - return "", fmt.Errorf("error writing SSL certificate: %v", err) - } - return data.String(), nil -} - -func (c *Certificate) AsBytes() ([]byte, error) { - // Nicer behaviour because this is called from templates - if c == nil { - return nil, fmt.Errorf("AsBytes called on nil Certificate") - } - - var data bytes.Buffer - _, err := c.WriteTo(&data) - if err != nil { - return nil, fmt.Errorf("error writing SSL certificate: %v", err) - } - return data.Bytes(), nil -} - -type PrivateKey struct { - Key crypto.PrivateKey -} - -func (k *PrivateKey) AsString() (string, error) { - // Nicer behaviour because this is called from templates - if k == nil { - return "", fmt.Errorf("AsString called on nil private key") - } - - var data bytes.Buffer - _, err := k.WriteTo(&data) - if err != nil { - return "", fmt.Errorf("error writing SSL private key: %v", err) - } - return data.String(), nil -} - -func (k *PrivateKey) AsBytes() ([]byte, error) { - // Nicer behaviour because this is called from templates - if k == nil { - return nil, fmt.Errorf("AsBytes called on nil private key") - } - - var data bytes.Buffer - _, err := k.WriteTo(&data) - if err != nil { - return nil, fmt.Errorf("error writing SSL PrivateKey: %v", err) - } - return data.Bytes(), nil -} - -func (k *PrivateKey) UnmarshalJSON(b []byte) (err error) { - s := "" - if err := json.Unmarshal(b, &s); err == nil { - r, err := parsePEMPrivateKey([]byte(s)) - if err != nil { - // Alternative form: Check if base64 encoded - // TODO: Do we need this? I think we need this only on nodeup, but maybe we could just not base64-it? - d, err2 := base64.StdEncoding.DecodeString(s) - if err2 == nil { - r2, err2 := parsePEMPrivateKey(d) - if err2 == nil { - glog.Warningf("used base64 decode of PrivateKey") - r = r2 - err = nil - } - } - - if err != nil { - return fmt.Errorf("error parsing private key: %v", err) - } - } - k.Key = r - return nil - } - - return fmt.Errorf("unknown format for private key: %q", string(b)) -} - -func (k *PrivateKey) MarshalJSON() ([]byte, error) { - var data bytes.Buffer - _, err := k.WriteTo(&data) - if err != nil { - return nil, fmt.Errorf("error writing SSL private key: %v", err) - } - return json.Marshal(data.String()) -} - -var _ io.WriterTo = &PrivateKey{} - -func (k *PrivateKey) WriteTo(w io.Writer) (int64, error) { - if k.Key == nil { - // For the dry-run case - return 0, nil - } - - var data bytes.Buffer - var err error - - switch pk := k.Key.(type) { - case *rsa.PrivateKey: - err = pem.Encode(w, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)}) - default: - return 0, fmt.Errorf("unknown private key type: %T", k.Key) - } - - if err != nil { - return 0, fmt.Errorf("error writing SSL private key: %v", err) - } - - return data.WriteTo(w) -} - -func LoadPEMCertificate(pemData []byte) (*Certificate, error) { - cert, err := parsePEMCertificate(pemData) - if err != nil { - return nil, err - } - - c := &Certificate{ - Subject: cert.Subject, - Certificate: cert, - PublicKey: cert.PublicKey, - IsCA: cert.IsCA, - } - return c, nil -} - -func SignNewCertificate(privateKey *PrivateKey, template *x509.Certificate, signer *x509.Certificate, signerPrivateKey *PrivateKey) (*Certificate, error) { - if template.PublicKey == nil { - rsaPrivateKey, ok := privateKey.Key.(*rsa.PrivateKey) - if ok { - template.PublicKey = rsaPrivateKey.Public() - } - } - - if template.PublicKey == nil { - return nil, fmt.Errorf("PublicKey not set, and cannot be determined from %T", privateKey) - } - - now := time.Now() - if template.NotBefore.IsZero() { - template.NotBefore = now.Add(time.Hour * -48) - } - - if template.NotAfter.IsZero() { - template.NotAfter = now.Add(time.Hour * 10 * 365 * 24) - } - - if template.SerialNumber == nil { - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := crypto_rand.Int(crypto_rand.Reader, serialNumberLimit) - if err != nil { - return nil, fmt.Errorf("error generating certificate serial number: %s", err) - } - template.SerialNumber = serialNumber - } - var parent *x509.Certificate - if signer != nil { - parent = signer - } else { - parent = template - signerPrivateKey = privateKey - } - - if template.KeyUsage == 0 { - template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment - } - - if template.ExtKeyUsage == nil { - template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} - } - //c.SignatureAlgorithm = do we want to overrride? - - certificateData, err := x509.CreateCertificate(crypto_rand.Reader, template, parent, template.PublicKey, signerPrivateKey.Key) - if err != nil { - return nil, fmt.Errorf("error creating certificate: %v", err) - } - - c := &Certificate{} - c.PublicKey = template.PublicKey - - cert, err := x509.ParseCertificate(certificateData) - if err != nil { - return nil, fmt.Errorf("error parsing certificate: %v", err) - } - c.Certificate = cert - - return c, nil -} - -var _ io.WriterTo = &Certificate{} - -func (c *Certificate) WriteTo(w io.Writer) (int64, error) { - // For the dry-run case - if c.Certificate == nil { - return 0, nil - } - - var b bytes.Buffer - err := pem.Encode(&b, &pem.Block{Type: "CERTIFICATE", Bytes: c.Certificate.Raw}) - if err != nil { - return 0, err - } - return b.WriteTo(w) -} - -func parsePEMCertificate(pemData []byte) (*x509.Certificate, error) { - for { - block, rest := pem.Decode(pemData) - if block == nil { - return nil, fmt.Errorf("could not parse certificate") - } - - if block.Type == "CERTIFICATE" { - glog.V(8).Infof("Parsing pem block: %q", block.Type) - return x509.ParseCertificate(block.Bytes) - } else { - glog.Infof("Ignoring unexpected PEM block: %q", block.Type) - } - - pemData = rest - } -} - -func parsePEMPrivateKey(pemData []byte) (crypto.PrivateKey, error) { - for { - block, rest := pem.Decode(pemData) - if block == nil { - return nil, fmt.Errorf("could not parse private key") - } - - if block.Type == "RSA PRIVATE KEY" { - glog.V(8).Infof("Parsing pem block: %q", block.Type) - return x509.ParsePKCS1PrivateKey(block.Bytes) - } else if block.Type == "PRIVATE KEY" { - glog.V(8).Infof("Parsing pem block: %q", block.Type) - k, err := x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - return nil, err - } - return k.(crypto.PrivateKey), nil - } else { - glog.Infof("Ignoring unexpected PEM block: %q", block.Type) - } - - pemData = rest - } -} - type CertificatePool struct { - Secondary []*Certificate - Primary *Certificate + Secondary []*pki.Certificate + Primary *pki.Certificate } func (c *CertificatePool) AsString() (string, error) { diff --git a/upup/pkg/fi/fitasks/keypair.go b/upup/pkg/fi/fitasks/keypair.go index 3cf01400fb..8054a3f13d 100644 --- a/upup/pkg/fi/fitasks/keypair.go +++ b/upup/pkg/fi/fitasks/keypair.go @@ -24,6 +24,7 @@ import ( "strings" "github.com/golang/glog" + "k8s.io/kops/pkg/pki" "k8s.io/kops/upup/pkg/fi" ) @@ -177,7 +178,7 @@ func (_ *Keypair) Render(c *fi.Context, a, e, changes *Keypair) error { // if we change keys we often have to regenerate e.g. the service accounts // TODO: Eventually rotate keys / don't always reuse? if privateKey == nil { - privateKey, err = fi.GeneratePrivateKey() + privateKey, err = pki.GeneratePrivateKey() if err != nil { return err } diff --git a/upup/pkg/fi/k8sapi/cert_secret.go b/upup/pkg/fi/k8sapi/cert_secret.go index 17b9f40cd1..009e5aa494 100644 --- a/upup/pkg/fi/k8sapi/cert_secret.go +++ b/upup/pkg/fi/k8sapi/cert_secret.go @@ -19,7 +19,7 @@ package k8sapi import ( "fmt" "k8s.io/client-go/pkg/api/v1" - "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/pkg/pki" ) // KeypairSecret is a wrapper around a k8s Secret object that holds a TLS keypair @@ -27,8 +27,8 @@ type KeypairSecret struct { Namespace string Name string - Certificate *fi.Certificate - PrivateKey *fi.PrivateKey + Certificate *pki.Certificate + PrivateKey *pki.PrivateKey } // ParseKeypairSecret parses the secret object, decoding the certificate & private-key, if present @@ -39,7 +39,7 @@ func ParseKeypairSecret(secret *v1.Secret) (*KeypairSecret, error) { certData := secret.Data[v1.TLSCertKey] if certData != nil { - cert, err := fi.LoadPEMCertificate(certData) + cert, err := pki.LoadPEMCertificate(certData) if err != nil { return nil, fmt.Errorf("error parsing certificate in %s/%s: %q", k.Namespace, k.Name, err) } @@ -47,7 +47,7 @@ func ParseKeypairSecret(secret *v1.Secret) (*KeypairSecret, error) { } keyData := secret.Data[v1.TLSPrivateKeyKey] if keyData != nil { - key, err := fi.ParsePEMPrivateKey(keyData) + key, err := pki.ParsePEMPrivateKey(keyData) if err != nil { return nil, fmt.Errorf("error parsing key in %s/%s: %q", k.Namespace, k.Name, err) } diff --git a/upup/pkg/fi/k8sapi/k8s_keystore.go b/upup/pkg/fi/k8sapi/k8s_keystore.go index 026f58e6c3..13091deea1 100644 --- a/upup/pkg/fi/k8sapi/k8s_keystore.go +++ b/upup/pkg/fi/k8sapi/k8s_keystore.go @@ -24,6 +24,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/pkg/api/v1" + "k8s.io/kops/pkg/pki" "k8s.io/kops/upup/pkg/fi" "math/big" "time" @@ -49,7 +50,7 @@ func NewKubernetesKeystore(client kubernetes.Interface, namespace string) fi.Key return c } -func (c *KubernetesKeystore) issueCert(id string, serial *big.Int, privateKey *fi.PrivateKey, template *x509.Certificate) (*fi.Certificate, error) { +func (c *KubernetesKeystore) issueCert(id string, serial *big.Int, privateKey *pki.PrivateKey, template *x509.Certificate) (*pki.Certificate, error) { glog.Infof("Issuing new certificate: %q", id) template.SerialNumber = serial @@ -63,7 +64,7 @@ func (c *KubernetesKeystore) issueCert(id string, serial *big.Int, privateKey *f return nil, fmt.Errorf("CA keypair was not found; cannot issue certificates") } - cert, err := fi.SignNewCertificate(privateKey, template, caCert.Certificate, caKey) + cert, err := pki.SignNewCertificate(privateKey, template, caCert.Certificate, caKey) if err != nil { return nil, err } @@ -87,7 +88,7 @@ func (c *KubernetesKeystore) findSecret(id string) (*v1.Secret, error) { return secret, nil } -func (c *KubernetesKeystore) FindKeypair(id string) (*fi.Certificate, *fi.PrivateKey, error) { +func (c *KubernetesKeystore) FindKeypair(id string) (*pki.Certificate, *pki.PrivateKey, error) { secret, err := c.findSecret(id) if err != nil { return nil, nil, err @@ -105,9 +106,9 @@ func (c *KubernetesKeystore) FindKeypair(id string) (*fi.Certificate, *fi.Privat return keypair.Certificate, keypair.PrivateKey, nil } -func (c *KubernetesKeystore) CreateKeypair(id string, template *x509.Certificate, privateKey *fi.PrivateKey) (*fi.Certificate, error) { +func (c *KubernetesKeystore) CreateKeypair(id string, template *x509.Certificate, privateKey *pki.PrivateKey) (*pki.Certificate, error) { t := time.Now().UnixNano() - serial := fi.BuildPKISerial(t) + serial := pki.BuildPKISerial(t) cert, err := c.issueCert(id, serial, privateKey, template) if err != nil { @@ -117,7 +118,7 @@ func (c *KubernetesKeystore) CreateKeypair(id string, template *x509.Certificate return cert, nil } -func (c *KubernetesKeystore) StoreKeypair(id string, cert *fi.Certificate, privateKey *fi.PrivateKey) error { +func (c *KubernetesKeystore) StoreKeypair(id string, cert *pki.Certificate, privateKey *pki.PrivateKey) error { keypair := &KeypairSecret{ Namespace: c.namespace, Name: id, diff --git a/upup/pkg/fi/nodeup/template_functions.go b/upup/pkg/fi/nodeup/template_functions.go index a5e5f78ccb..425d53c821 100644 --- a/upup/pkg/fi/nodeup/template_functions.go +++ b/upup/pkg/fi/nodeup/template_functions.go @@ -27,6 +27,7 @@ import ( api "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/nodeup" "k8s.io/kops/pkg/flagbuilder" + "k8s.io/kops/pkg/pki" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/secrets" "k8s.io/kops/util/pkg/vfs" @@ -122,17 +123,17 @@ func (t *templateFunctions) populate(dest template.FuncMap) { } // CACertificate returns the primary CA certificate for the cluster -func (t *templateFunctions) CACertificate() (*fi.Certificate, error) { +func (t *templateFunctions) CACertificate() (*pki.Certificate, error) { return t.keyStore.Cert(fi.CertificateId_CA) } // PrivateKey returns the specified private key -func (t *templateFunctions) PrivateKey(id string) (*fi.PrivateKey, error) { +func (t *templateFunctions) PrivateKey(id string) (*pki.PrivateKey, error) { return t.keyStore.PrivateKey(id) } // Certificate returns the specified private key -func (t *templateFunctions) Certificate(id string) (*fi.Certificate, error) { +func (t *templateFunctions) Certificate(id string) (*pki.Certificate, error) { return t.keyStore.Cert(id) } diff --git a/upup/pkg/fi/vfs_castore.go b/upup/pkg/fi/vfs_castore.go index 1ea3ebc3fe..026d7c3ad9 100644 --- a/upup/pkg/fi/vfs_castore.go +++ b/upup/pkg/fi/vfs_castore.go @@ -33,6 +33,7 @@ import ( "github.com/golang/glog" "golang.org/x/crypto/ssh" + "k8s.io/kops/pkg/pki" "k8s.io/kops/util/pkg/vfs" ) @@ -125,15 +126,15 @@ func (c *VFSCAStore) generateCACertificate() (*certificates, *privateKeys, error return nil, nil, fmt.Errorf("error generating RSA private key: %v", err) } - caPrivateKey := &PrivateKey{Key: caRsaKey} + caPrivateKey := &pki.PrivateKey{Key: caRsaKey} - caCertificate, err := SignNewCertificate(caPrivateKey, template, nil, nil) + caCertificate, err := pki.SignNewCertificate(caPrivateKey, template, nil, nil) if err != nil { return nil, nil, err } t := time.Now().UnixNano() - serial := BuildPKISerial(t) + serial := pki.BuildPKISerial(t) keyPath := c.buildPrivateKeyPath(CertificateId_CA, serial) err = c.storePrivateKey(caPrivateKey, keyPath) @@ -186,11 +187,11 @@ func (c *VFSCAStore) buildPrivateKeyPath(id string, serial *big.Int) vfs.Path { } type certificates struct { - certificates map[string]*Certificate + certificates map[string]*pki.Certificate primary string } -func (p *certificates) Primary() *Certificate { +func (p *certificates) Primary() *pki.Certificate { if p.primary == "" { return nil } @@ -207,7 +208,7 @@ func (c *VFSCAStore) loadCertificates(p vfs.Path) (*certificates, error) { } certs := &certificates{ - certificates: make(map[string]*Certificate), + certificates: make(map[string]*pki.Certificate), } for _, f := range files { @@ -241,7 +242,7 @@ func (c *VFSCAStore) loadCertificates(p vfs.Path) (*certificates, error) { return certs, nil } -func (c *VFSCAStore) loadOneCertificate(p vfs.Path) (*Certificate, error) { +func (c *VFSCAStore) loadOneCertificate(p vfs.Path) (*pki.Certificate, error) { data, err := p.ReadFile() if err != nil { if os.IsNotExist(err) { @@ -249,7 +250,7 @@ func (c *VFSCAStore) loadOneCertificate(p vfs.Path) (*Certificate, error) { } return nil, err } - cert, err := LoadPEMCertificate(data) + cert, err := pki.LoadPEMCertificate(data) if err != nil { return nil, err } @@ -259,12 +260,12 @@ func (c *VFSCAStore) loadOneCertificate(p vfs.Path) (*Certificate, error) { return cert, nil } -func (c *VFSCAStore) Cert(id string) (*Certificate, error) { +func (c *VFSCAStore) Cert(id string) (*pki.Certificate, error) { cert, err := c.FindCert(id) if err == nil && cert == nil { if c.DryRun { glog.Warningf("using empty certificate, because running with DryRun") - return &Certificate{}, err + return &pki.Certificate{}, err } return nil, fmt.Errorf("cannot find certificate %q", id) } @@ -285,7 +286,7 @@ func (c *VFSCAStore) CertificatePool(id string) (*CertificatePool, error) { } -func (c *VFSCAStore) FindKeypair(id string) (*Certificate, *PrivateKey, error) { +func (c *VFSCAStore) FindKeypair(id string) (*pki.Certificate, *pki.PrivateKey, error) { cert, err := c.FindCert(id) if err != nil { return nil, nil, err @@ -299,7 +300,7 @@ func (c *VFSCAStore) FindKeypair(id string) (*Certificate, *PrivateKey, error) { return cert, key, nil } -func (c *VFSCAStore) FindCert(id string) (*Certificate, error) { +func (c *VFSCAStore) FindCert(id string) (*pki.Certificate, error) { var certs *certificates var err error @@ -309,7 +310,7 @@ func (c *VFSCAStore) FindCert(id string) (*Certificate, error) { return nil, fmt.Errorf("error in 'FindCert' attempting to load cert %q: %v", id, err) } - var cert *Certificate + var cert *pki.Certificate if certs != nil && certs.primary != "" { cert = certs.certificates[certs.primary] } @@ -404,7 +405,7 @@ func (c *VFSCAStore) List() ([]*KeystoreItem, error) { return items, nil } -func (c *VFSCAStore) IssueCert(id string, serial *big.Int, privateKey *PrivateKey, template *x509.Certificate) (*Certificate, error) { +func (c *VFSCAStore) IssueCert(id string, serial *big.Int, privateKey *pki.PrivateKey, template *x509.Certificate) (*pki.Certificate, error) { glog.Infof("Issuing new certificate: %q", id) template.SerialNumber = serial @@ -417,7 +418,7 @@ func (c *VFSCAStore) IssueCert(id string, serial *big.Int, privateKey *PrivateKe if caPrivateKeys == nil || caPrivateKeys.Primary() == nil { return nil, fmt.Errorf("ca.key was not found; cannot issue certificates") } - cert, err := SignNewCertificate(privateKey, template, caCertificates.Primary().Certificate, caPrivateKeys.Primary()) + cert, err := pki.SignNewCertificate(privateKey, template, caCertificates.Primary().Certificate, caPrivateKeys.Primary()) if err != nil { return nil, err } @@ -432,7 +433,7 @@ func (c *VFSCAStore) IssueCert(id string, serial *big.Int, privateKey *PrivateKe return c.loadOneCertificate(p) } -func (c *VFSCAStore) StoreKeypair(id string, cert *Certificate, privateKey *PrivateKey) error { +func (c *VFSCAStore) StoreKeypair(id string, cert *pki.Certificate, privateKey *pki.PrivateKey) error { serial := cert.Certificate.SerialNumber { @@ -455,11 +456,11 @@ func (c *VFSCAStore) StoreKeypair(id string, cert *Certificate, privateKey *Priv return nil } -func (c *VFSCAStore) AddCert(id string, cert *Certificate) error { +func (c *VFSCAStore) AddCert(id string, cert *pki.Certificate) error { glog.Infof("Adding TLS certificate: %q", id) // We add with a timestamp of zero so this will never be the newest cert - serial := BuildPKISerial(0) + serial := pki.BuildPKISerial(0) p := c.buildCertificatePath(id, serial) @@ -474,11 +475,11 @@ func (c *VFSCAStore) AddCert(id string, cert *Certificate) error { } type privateKeys struct { - keys map[string]*PrivateKey + keys map[string]*pki.PrivateKey primary string } -func (p *privateKeys) Primary() *PrivateKey { +func (p *privateKeys) Primary() *pki.PrivateKey { if p.primary == "" { return nil } @@ -495,7 +496,7 @@ func (c *VFSCAStore) loadPrivateKeys(p vfs.Path) (*privateKeys, error) { } keys := &privateKeys{ - keys: make(map[string]*PrivateKey), + keys: make(map[string]*pki.PrivateKey), } for _, f := range files { @@ -529,7 +530,7 @@ func (c *VFSCAStore) loadPrivateKeys(p vfs.Path) (*privateKeys, error) { return keys, nil } -func (c *VFSCAStore) loadOnePrivateKey(p vfs.Path) (*PrivateKey, error) { +func (c *VFSCAStore) loadOnePrivateKey(p vfs.Path) (*pki.PrivateKey, error) { data, err := p.ReadFile() if err != nil { if os.IsNotExist(err) { @@ -537,25 +538,14 @@ func (c *VFSCAStore) loadOnePrivateKey(p vfs.Path) (*PrivateKey, error) { } return nil, err } - k, err := ParsePEMPrivateKey(data) + k, err := pki.ParsePEMPrivateKey(data) if err != nil { return nil, fmt.Errorf("error parsing private key from %q: %v", p, err) } return k, err } -func ParsePEMPrivateKey(data []byte) (*PrivateKey, error) { - k, err := parsePEMPrivateKey(data) - if err != nil { - return nil, err - } - if k == nil { - return nil, nil - } - return &PrivateKey{Key: k}, nil -} - -func (c *VFSCAStore) FindPrivateKey(id string) (*PrivateKey, error) { +func (c *VFSCAStore) FindPrivateKey(id string) (*pki.PrivateKey, error) { var keys *privateKeys if id == CertificateId_CA { _, caPrivateKeys, err := c.readCAKeypairs() @@ -573,19 +563,19 @@ func (c *VFSCAStore) FindPrivateKey(id string) (*PrivateKey, error) { } - var key *PrivateKey + var key *pki.PrivateKey if keys != nil && keys.primary != "" { key = keys.keys[keys.primary] } return key, nil } -func (c *VFSCAStore) PrivateKey(id string) (*PrivateKey, error) { +func (c *VFSCAStore) PrivateKey(id string) (*pki.PrivateKey, error) { key, err := c.FindPrivateKey(id) if err == nil && key == nil { if c.DryRun { glog.Warningf("using empty certificate, because running with DryRun") - return &PrivateKey{}, err + return &pki.PrivateKey{}, err } return nil, fmt.Errorf("cannot find SSL key %q", id) } @@ -593,7 +583,7 @@ func (c *VFSCAStore) PrivateKey(id string) (*PrivateKey, error) { } -func (c *VFSCAStore) CreateKeypair(id string, template *x509.Certificate, privateKey *PrivateKey) (*Certificate, error) { +func (c *VFSCAStore) CreateKeypair(id string, template *x509.Certificate, privateKey *pki.PrivateKey) (*pki.Certificate, error) { serial := c.buildSerial() cert, err := c.IssueCert(id, serial, privateKey, template) @@ -604,7 +594,7 @@ func (c *VFSCAStore) CreateKeypair(id string, template *x509.Certificate, privat return cert, nil } -func (c *VFSCAStore) storePrivateKey(privateKey *PrivateKey, p vfs.Path) error { +func (c *VFSCAStore) storePrivateKey(privateKey *pki.PrivateKey, p vfs.Path) error { var data bytes.Buffer _, err := privateKey.WriteTo(&data) if err != nil { @@ -614,7 +604,7 @@ func (c *VFSCAStore) storePrivateKey(privateKey *PrivateKey, p vfs.Path) error { return p.WriteFile(data.Bytes()) } -func (c *VFSCAStore) storeCertificate(cert *Certificate, p vfs.Path) error { +func (c *VFSCAStore) storeCertificate(cert *pki.Certificate, p vfs.Path) error { // TODO: replace storePrivateKey & storeCertificate with writeFile(io.WriterTo)? var data bytes.Buffer _, err := cert.WriteTo(&data) @@ -627,25 +617,7 @@ func (c *VFSCAStore) storeCertificate(cert *Certificate, p vfs.Path) error { func (c *VFSCAStore) buildSerial() *big.Int { t := time.Now().UnixNano() - return BuildPKISerial(t) -} - -// BuildPKISerial produces a serial number for certs that is vanishingly unlikely to collide -// The timestamp should be provided as an input (time.Now().UnixNano()), and then we combine -// that with a 32 bit random crypto-rand integer. -// We also know that a bigger value was created later (modulo clock skew) -func BuildPKISerial(timestamp int64) *big.Int { - randomLimit := new(big.Int).Lsh(big.NewInt(1), 32) - randomComponent, err := crypto_rand.Int(crypto_rand.Reader, randomLimit) - if err != nil { - glog.Fatalf("error generating random number: %v", err) - } - - serial := big.NewInt(timestamp) - serial.Lsh(serial, 32) - serial.Or(serial, randomComponent) - - return serial + return pki.BuildPKISerial(t) } func formatFingerprint(data []byte) string { @@ -761,7 +733,7 @@ func (c *VFSCAStore) loadPath(p vfs.Path) ([]*KeystoreItem, error) { return keystoreItems, nil } -func (c *VFSCAStore) loadData(p vfs.Path) (*PrivateKey, error) { +func (c *VFSCAStore) loadData(p vfs.Path) (*pki.PrivateKey, error) { data, err := p.ReadFile() if err != nil { if os.IsNotExist(err) { @@ -769,7 +741,7 @@ func (c *VFSCAStore) loadData(p vfs.Path) (*PrivateKey, error) { } return nil, err } - k, err := ParsePEMPrivateKey(data) + k, err := pki.ParsePEMPrivateKey(data) if err != nil { return nil, fmt.Errorf("error parsing private key from %q: %v", p, err) } diff --git a/upup/pkg/kutil/import_cluster.go b/upup/pkg/kutil/import_cluster.go index 45ab300a78..47926280f6 100644 --- a/upup/pkg/kutil/import_cluster.go +++ b/upup/pkg/kutil/import_cluster.go @@ -31,6 +31,7 @@ import ( "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops/registry" "k8s.io/kops/pkg/client/simple" + "k8s.io/kops/pkg/pki" "k8s.io/kops/pkg/resources" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup" @@ -714,7 +715,7 @@ func (u *UserDataConfiguration) ParseBool(key string) *bool { return fi.Bool(false) } -func (u *UserDataConfiguration) ParseCert(key string) (*fi.Certificate, error) { +func (u *UserDataConfiguration) ParseCert(key string) (*pki.Certificate, error) { s := u.Settings[key] if s == "" { return nil, nil @@ -724,7 +725,7 @@ func (u *UserDataConfiguration) ParseCert(key string) (*fi.Certificate, error) { if err != nil { return nil, fmt.Errorf("error decoding base64 certificate %q: %v", key, err) } - cert, err := fi.LoadPEMCertificate(data) + cert, err := pki.LoadPEMCertificate(data) if err != nil { return nil, fmt.Errorf("error parsing certificate %q: %v", key, err) } @@ -732,7 +733,7 @@ func (u *UserDataConfiguration) ParseCert(key string) (*fi.Certificate, error) { return cert, nil } -func (u *UserDataConfiguration) ParseKey(key string) (*fi.PrivateKey, error) { +func (u *UserDataConfiguration) ParseKey(key string) (*pki.PrivateKey, error) { s := u.Settings[key] if s == "" { return nil, nil @@ -742,7 +743,7 @@ func (u *UserDataConfiguration) ParseKey(key string) (*fi.PrivateKey, error) { if err != nil { return nil, fmt.Errorf("error decoding base64 private key %q: %v", key, err) } - k, err := fi.ParsePEMPrivateKey(data) + k, err := pki.ParsePEMPrivateKey(data) if err != nil { return nil, fmt.Errorf("error parsing private key %q: %v", key, err) }