Skip to content

Commit

Permalink
Simplify extraction of service-account public keys
Browse files Browse the repository at this point in the history
  • Loading branch information
johngmyers committed Jun 5, 2021
1 parent bceb901 commit 12465ac
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 49 deletions.
1 change: 0 additions & 1 deletion nodeup/pkg/model/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 50 additions & 24 deletions nodeup/pkg/model/fakes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package model

import (
"crypto/x509"
"fmt"
"testing"

"k8s.io/kops/pkg/apis/kops"
Expand All @@ -26,43 +27,68 @@ import (
"k8s.io/kops/util/pkg/vfs"
)

// fakeKeyStore mocks out some of fi.KeyStore, for our tests.
type fakeKeyStore struct {
T *testing.T
// fakeCAStore mocks out some of fi.CAStore, for our tests.
type fakeCAStore struct {
T *testing.T
privateKeysets map[string]*kops.Keyset
certs map[string]*pki.Certificate
}

var _ fi.Keystore = &fakeKeyStore{}
var _ fi.CAStore = &fakeCAStore{}

func (k fakeKeyStore) FindPrimaryKeypair(name string) (*pki.Certificate, *pki.PrivateKey, error) {
panic("fakeKeyStore does not implement FindPrimaryKeypair")
func (k fakeCAStore) FindPrimaryKeypair(name string) (*pki.Certificate, *pki.PrivateKey, error) {
panic("fakeCAStore does not implement FindPrimaryKeypair")
}

func (k fakeKeyStore) FindKeyset(name string) (*fi.Keyset, error) {
panic("fakeKeyStore does not implement FindKeyset")
}
func (k fakeCAStore) FindKeyset(name string) (*fi.Keyset, error) {
kopsKeyset := k.privateKeysets[name]
if kopsKeyset == nil {
return nil, nil
}

func (k fakeKeyStore) CreateKeypair(signer string, name string, template *x509.Certificate, privateKey *pki.PrivateKey) (*pki.Certificate, error) {
panic("fakeKeyStore does not implement CreateKeypair")
}
keyset := &fi.Keyset{
Items: make(map[string]*fi.KeysetItem),
}

func (k fakeKeyStore) StoreKeyset(name string, keyset *fi.Keyset) error {
panic("fakeKeyStore does not implement StoreKeyset")
}
for _, key := range kopsKeyset.Spec.Keys {
ki := &fi.KeysetItem{
Id: key.Id,
}
if len(key.PublicMaterial) != 0 {
cert, err := pki.ParsePEMCertificate(key.PublicMaterial)
if err != nil {
return nil, fmt.Errorf("error loading certificate %s/%s: %v", name, key.Id, err)
}
ki.Certificate = cert
}

func (k fakeKeyStore) MirrorTo(basedir vfs.Path) error {
panic("fakeKeyStore does not implement MirrorTo")
if len(key.PrivateMaterial) != 0 {
privateKey, err := pki.ParsePEMPrivateKey(key.PrivateMaterial)
if err != nil {
return nil, fmt.Errorf("error loading private key %s/%s: %v", name, key.Id, err)
}
ki.PrivateKey = privateKey
}

keyset.Items[key.Id] = ki
}

keyset.Primary = keyset.Items[fi.FindPrimary(kopsKeyset).Id]

return keyset, nil
}

// fakeCAStore mocks out some of fi.CAStore, for our tests.
// Although CAStore currently embeds KeyStore, we maintain the split here in the hope we can clean this up in future.
type fakeCAStore struct {
fakeKeyStore
func (k fakeCAStore) CreateKeypair(signer string, name string, template *x509.Certificate, privateKey *pki.PrivateKey) (*pki.Certificate, error) {
panic("fakeCAStore does not implement CreateKeypair")
}

privateKeysets map[string]*kops.Keyset
certs map[string]*pki.Certificate
func (k fakeCAStore) StoreKeyset(name string, keyset *fi.Keyset) error {
panic("fakeCAStore does not implement StoreKeyset")
}

var _ fi.CAStore = &fakeCAStore{}
func (k fakeCAStore) MirrorTo(basedir vfs.Path) error {
panic("fakeCAStore does not implement MirrorTo")
}

func (k fakeCAStore) FindCertificatePool(name string) (*fi.CertificatePool, error) {
panic("fakeCAStore does not implement FindCertificatePool")
Expand Down
26 changes: 5 additions & 21 deletions nodeup/pkg/model/kube_apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ limitations under the License.
package model

import (
"bytes"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"path/filepath"
"strings"
Expand All @@ -30,7 +26,6 @@ import (
"k8s.io/kops/pkg/k8scodecs"
"k8s.io/kops/pkg/kubeconfig"
"k8s.io/kops/pkg/kubemanifest"
"k8s.io/kops/pkg/pki"
"k8s.io/kops/pkg/wellknownports"
"k8s.io/kops/pkg/wellknownusers"
"k8s.io/kops/upup/pkg/fi"
Expand Down Expand Up @@ -87,7 +82,7 @@ func (b *KubeAPIServerBuilder) Build(c *fi.ModelBuilderContext) error {
}
}
{
keyset, err := b.KeyStore.FindPrivateKeyset("service-account")
keyset, err := b.KeyStore.FindKeyset("service-account")
if err != nil {
return err
}
Expand All @@ -96,25 +91,14 @@ func (b *KubeAPIServerBuilder) Build(c *fi.ModelBuilderContext) error {
return fmt.Errorf("service-account keyset not found")
}

buf := new(bytes.Buffer)
for _, keyItem := range keyset.Spec.Keys {
privateKey, err := pki.ParsePEMPrivateKey(keyItem.PrivateMaterial)
if err != nil {
return fmt.Errorf("error loading service-account private key %s: %v", keyItem.Id, err)
}
pkData, err := x509.MarshalPKIXPublicKey(privateKey.Key.(*rsa.PrivateKey).Public())
if err != nil {
return fmt.Errorf("marshalling public key: %v", err)
}
err = pem.Encode(buf, &pem.Block{Type: "RSA PUBLIC KEY", Bytes: pkData})
if err != nil {
return fmt.Errorf("encoding public key: %v", err)
}
buf, err := keyset.ToPublicKeyBytes()
if err != nil {
return err
}

c.AddTask(&nodetasks.File{
Path: filepath.Join(b.PathSrvKubernetes(), "service-account.pub"),
Contents: fi.NewBytesResource(buf.Bytes()),
Contents: fi.NewBytesResource(buf),
Type: nodetasks.FileType_File,
Mode: s("0600"),
})
Expand Down
11 changes: 8 additions & 3 deletions nodeup/pkg/model/kubelet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,9 @@ func mockedPopulateClusterSpec(c *kops.Cluster, cloud fi.Cloud) (*kops.Cluster,

const dummyCertificate = "-----BEGIN CERTIFICATE-----\nMIIC2DCCAcCgAwIBAgIRALJXAkVj964tq67wMSI8oJQwDQYJKoZIhvcNAQELBQAw\nFTETMBEGA1UEAxMKa3ViZXJuZXRlczAeFw0xNzEyMjcyMzUyNDBaFw0yNzEyMjcy\nMzUyNDBaMBUxEzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUA\nA4IBDwAwggEKAoIBAQDgnCkSmtnmfxEgS3qNPaUCH5QOBGDH/inHbWCODLBCK9gd\nXEcBl7FVv8T2kFr1DYb0HVDtMI7tixRVFDLgkwNlW34xwWdZXB7GeoFgU1xWOQSY\nOACC8JgYTQ/139HBEvgq4sej67p+/s/SNcw34Kk7HIuFhlk1rRk5kMexKIlJBKP1\nYYUYetsJ/QpUOkqJ5HW4GoetE76YtHnORfYvnybviSMrh2wGGaN6r/s4ChOaIbZC\nAn8/YiPKGIDaZGpj6GXnmXARRX/TIdgSQkLwt0aTDBnPZ4XvtpI8aaL8DYJIqAzA\nNPH2b4/uNylat5jDo0b0G54agMi97+2AUrC9UUXpAgMBAAGjIzAhMA4GA1UdDwEB\n/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBVGR2r\nhzXzRMU5wriPQAJScszNORvoBpXfZoZ09FIupudFxBVU3d4hV9StKnQgPSGA5XQO\nHE97+BxJDuA/rB5oBUsMBjc7y1cde/T6hmi3rLoEYBSnSudCOXJE4G9/0f8byAJe\nrN8+No1r2VgZvZh6p74TEkXv/l3HBPWM7IdUV0HO9JDhSgOVF1fyQKJxRuLJR8jt\nO6mPH2UX0vMwVa4jvwtkddqk2OAdYQvH9rbDjjbzaiW0KnmdueRo92KHAN7BsDZy\nVpXHpqo1Kzg7D3fpaXCf5si7lqqrdJVXH4JC72zxsPehqgi8eIuqOBkiDWmRxAxh\n8yGeRx9AbknHh4Ia\n-----END CERTIFICATE-----\n"
const dummyKey = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA4JwpEprZ5n8RIEt6jT2lAh+UDgRgx/4px21gjgywQivYHVxH\nAZexVb/E9pBa9Q2G9B1Q7TCO7YsUVRQy4JMDZVt+McFnWVwexnqBYFNcVjkEmDgA\ngvCYGE0P9d/RwRL4KuLHo+u6fv7P0jXMN+CpOxyLhYZZNa0ZOZDHsSiJSQSj9WGF\nGHrbCf0KVDpKieR1uBqHrRO+mLR5zkX2L58m74kjK4dsBhmjeq/7OAoTmiG2QgJ/\nP2IjyhiA2mRqY+hl55lwEUV/0yHYEkJC8LdGkwwZz2eF77aSPGmi/A2CSKgMwDTx\n9m+P7jcpWreYw6NG9BueGoDIve/tgFKwvVFF6QIDAQABAoIBAA0ktjaTfyrAxsTI\nBezb7Zr5NBW55dvuII299cd6MJo+rI/TRYhvUv48kY8IFXp/hyUjzgeDLunxmIf9\n/Zgsoic9Ol44/g45mMduhcGYPzAAeCdcJ5OB9rR9VfDCXyjYLlN8H8iU0734tTqM\n0V13tQ9zdSqkGPZOIcq/kR/pylbOZaQMe97BTlsAnOMSMKDgnftY4122Lq3GYy+t\nvpr+bKVaQZwvkLoSU3rECCaKaghgwCyX7jft9aEkhdJv+KlwbsGY6WErvxOaLWHd\ncuMQjGapY1Fa/4UD00mvrA260NyKfzrp6+P46RrVMwEYRJMIQ8YBAk6N6Hh7dc0G\n8Z6i1m0CgYEA9HeCJR0TSwbIQ1bDXUrzpftHuidG5BnSBtax/ND9qIPhR/FBW5nj\n22nwLc48KkyirlfIULd0ae4qVXJn7wfYcuX/cJMLDmSVtlM5Dzmi/91xRiFgIzx1\nAsbBzaFjISP2HpSgL+e9FtSXaaqeZVrflitVhYKUpI/AKV31qGHf04sCgYEA6zTV\n99Sb49Wdlns5IgsfnXl6ToRttB18lfEKcVfjAM4frnkk06JpFAZeR+9GGKUXZHqs\nz2qcplw4d/moCC6p3rYPBMLXsrGNEUFZqBlgz72QA6BBq3X0Cg1Bc2ZbK5VIzwkg\nST2SSux6ccROfgULmN5ZiLOtdUKNEZpFF3i3qtsCgYADT/s7dYFlatobz3kmMnXK\nsfTu2MllHdRys0YGHu7Q8biDuQkhrJwhxPW0KS83g4JQym+0aEfzh36bWcl+u6R7\nKhKj+9oSf9pndgk345gJz35RbPJYh+EuAHNvzdgCAvK6x1jETWeKf6btj5pF1U1i\nQ4QNIw/QiwIXjWZeubTGsQKBgQCbduLu2rLnlyyAaJZM8DlHZyH2gAXbBZpxqU8T\nt9mtkJDUS/KRiEoYGFV9CqS0aXrayVMsDfXY6B/S/UuZjO5u7LtklDzqOf1aKG3Q\ndGXPKibknqqJYH+bnUNjuYYNerETV57lijMGHuSYCf8vwLn3oxBfERRX61M/DU8Z\nworz/QKBgQDCTJI2+jdXg26XuYUmM4XXfnocfzAXhXBULt1nENcogNf1fcptAVtu\nBAiz4/HipQKqoWVUYmxfgbbLRKKLK0s0lOWKbYdVjhEm/m2ZU8wtXTagNwkIGoyq\nY/C1Lox4f1ROJnCjc/hfcOjcxX5M8A8peecHWlVtUPKTJgxQ7oMKcw==\n-----END RSA PRIVATE KEY-----\n"
const previousCertificate = "-----BEGIN CERTIFICATE-----\nMIIBZzCCARGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9zZXJ2\naWNlLWFjY291bnQwHhcNMjEwNTAyMjAzMDA2WhcNMzEwNTAyMjAzMDA2WjAaMRgw\nFgYDVQQDEw9zZXJ2aWNlLWFjY291bnQwXDANBgkqhkiG9w0BAQEFAANLADBIAkEA\n2JbeF8dNwqfEKKD65aGlVs58fWkA0qZdVLKw8qATzRBJTi1nqbj2kAR4gyy/C8Mx\nouxva/om9d7Sq8Ka55T7+wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T\nAQH/BAUwAwEB/zAdBgNVHQ4EFgQUI5beFHueAGyT1pQ6UTOdbMfj3gQwDQYJKoZI\nhvcNAQELBQADQQBwPLO+Np8o6k3aNBGKE4JTCOs06X72OXNivkWWWP/9XGz6x4DI\nHPU65kbUn/pWXBUVVlpsKsdmWA2Bu8pd/vD+\n-----END CERTIFICATE-----\n"
const previousKey = "-----BEGIN RSA PRIVATE KEY-----\nMIIBPQIBAAJBANiW3hfHTcKnxCig+uWhpVbOfH1pANKmXVSysPKgE80QSU4tZ6m4\n9pAEeIMsvwvDMaLsb2v6JvXe0qvCmueU+/sCAwEAAQJBAKt/gmpHqP3qA3u8RA5R\n2W6L360Z2Mnza1FmkI/9StCCkJGjuE5yDhxU4JcVnFyX/nMxm2ockEEQDqRSu7Oo\nxTECIQD2QsUsgFL4FnXWzTclySJ6ajE4Cte3gSDOIvyMNMireQIhAOEnsV8UaSI+\nZyL7NMLzMPLCgtsrPnlamr8gdrEHf9ITAiEAxCCLbpTI/4LL2QZZrINTLVGT34Fr\nKl/yI5pjrrp/M2kCIQDfOktQyRuzJ8t5kzWsUxCkntS+FxHJn1rtQ3Jp8dV4oQIh\nAOyiVWDyLZJvg7Y24Ycmp86BZjM9Wk/BfWpBXKnl9iDY\n-----END RSA PRIVATE KEY-----"
const nextCertificate = "-----BEGIN CERTIFICATE-----\nMIIBZzCCARGgAwIBAgIBBDANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9zZXJ2\naWNlLWFjY291bnQwHhcNMjEwNTAyMjAzMjE3WhcNMzEwNTAyMjAzMjE3WjAaMRgw\nFgYDVQQDEw9zZXJ2aWNlLWFjY291bnQwXDANBgkqhkiG9w0BAQEFAANLADBIAkEA\no4Tridlsf4Yz3UAiup/scSTiG/OqxkUW3Fz7zGKvVcLeYj9GEIKuzoB1VFk1nboD\nq4cCuGLfdzaQdCQKPIsDuwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T\nAQH/BAUwAwEB/zAdBgNVHQ4EFgQUhPbxEmUbwVOCa+fZgxreFhf67UEwDQYJKoZI\nhvcNAQELBQADQQALMsyK2Q7C/bk27eCvXyZKUfrLvor10hEjwGhv14zsKWDeTj/J\nA1LPYp7U9VtFfgFOkVbkLE9Rstc0ltNrPqxA\n-----END CERTIFICATE-----\n"
const nextKey = "-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAKOE64nZbH+GM91AIrqf7HEk4hvzqsZFFtxc+8xir1XC3mI/RhCC\nrs6AdVRZNZ26A6uHArhi33c2kHQkCjyLA7sCAwEAAQJAejInjmEzqmzQr0NxcIN4\nPukwK3FBKl+RAOZfqNIKcww14mfOn7Gc6lF2zEC4GnLiB3tthbSXoBGi54nkW4ki\nyQIhANZNne9UhQlwyjsd3WxDWWrl6OOZ3J8ppMOIQni9WRLlAiEAw1XEdxPOSOSO\nB6rucpTT1QivVvyEFIb/ukvPm769Mh8CIQDNQwKnHdlfNX0+KljPPaMD1LrAZbr/\naC+8aWLhqtsKUQIgF7gUcTkwdV17eabh6Xv09Qtm7zMefred2etWvFy+8JUCIECv\nFYOKQVWHX+Q7CHX2K1oTECVnZuW1UItdDYVlFYxQ\n-----END RSA PRIVATE KEY-----"

func simplePrivateKeyset(s string) *kops.Keyset {
Expand All @@ -285,22 +287,25 @@ func simplePrivateKeyset(s string) *kops.Keyset {
}
}

func rotatingPrivateKeyset(s string) *kops.Keyset {
func rotatingPrivateKeyset() *kops.Keyset {
return &kops.Keyset{
Spec: kops.KeysetSpec{
PrimaryId: "3",
Keys: []kops.KeysetItem{
{
Id: "2",
PrivateMaterial: []byte(previousKey),
PublicMaterial: []byte(previousCertificate),
},
{
Id: "3",
PrivateMaterial: []byte(s),
PrivateMaterial: []byte(dummyKey),
PublicMaterial: []byte(dummyCertificate),
},
{
Id: "4",
PrivateMaterial: []byte(nextKey),
PublicMaterial: []byte(nextCertificate),
},
},
},
Expand Down Expand Up @@ -338,7 +343,7 @@ func RunGoldenTest(t *testing.T, basedir string, key string, builder func(*Nodeu
"kube-controller-manager": simplePrivateKeyset(dummyKey),
"kube-proxy": simplePrivateKeyset(dummyKey),
"kube-scheduler": simplePrivateKeyset(dummyKey),
"service-account": rotatingPrivateKeyset(dummyKey),
"service-account": rotatingPrivateKeyset(),
}
keystore.certs = map[string]*pki.Certificate{
"ca": mustParseCertificate(dummyCertificate),
Expand Down
26 changes: 26 additions & 0 deletions upup/pkg/fi/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ package fi

import (
"bytes"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"sort"
"strconv"

"k8s.io/kops/pkg/apis/kops"
Expand Down Expand Up @@ -200,3 +203,26 @@ func KeysetItemIdOlder(a, b string) bool {
return a < b
}
}

func (k *Keyset) ToPublicKeyBytes() ([]byte, error) {
keys := make([]string, 0, len(k.Items))
for k := range k.Items {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return KeysetItemIdOlder(k.Items[keys[i]].Id, k.Items[keys[j]].Id)
})

buf := new(bytes.Buffer)
for _, key := range keys {
item := k.Items[key]
publicKeyData, err := x509.MarshalPKIXPublicKey(item.Certificate.PublicKey)
if err != nil {
return nil, fmt.Errorf("marshalling public key %s: %v", item.Id, err)
}
if err = pem.Encode(buf, &pem.Block{Type: "RSA PUBLIC KEY", Bytes: publicKeyData}); err != nil {
return nil, fmt.Errorf("encoding public key %s: %v", item.Id, err)
}
}
return buf.Bytes(), nil
}

0 comments on commit 12465ac

Please sign in to comment.