Merge pull request #3288 from justinsb/refactor_pki

Automatic merge from submit-queue

Refactor PKI classes into their own package
This commit is contained in:
Kubernetes Submit Queue 2017-08-28 07:01:27 -07:00 committed by GitHub
commit 327235a22c
13 changed files with 506 additions and 421 deletions

View File

@ -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
}

View File

@ -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) {

View File

@ -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

152
pkg/pki/certificate.go Normal file
View File

@ -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)
}

108
pkg/pki/csr.go Normal file
View File

@ -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
}

171
pkg/pki/privatekey.go Normal file
View File

@ -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
}
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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)
}

View File

@ -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,

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}