Implement mirroring for API CAStore

Makes the mirror code essentially identical.
This commit is contained in:
Justin Santa Barbara 2017-11-12 21:52:35 -05:00
parent 5cc5a936f9
commit ca6268b25b
2 changed files with 155 additions and 91 deletions

View File

@ -30,11 +30,9 @@ import (
"golang.org/x/crypto/ssh"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kops/pkg/acls"
"k8s.io/kops/pkg/apis/kops"
kopsinternalversion "k8s.io/kops/pkg/client/clientset_generated/clientset/typed/kops/internalversion"
"k8s.io/kops/pkg/pki"
"k8s.io/kops/pkg/sshcredentials"
"k8s.io/kops/util/pkg/vfs"
)
@ -628,81 +626,26 @@ func (c *ClientsetCAStore) DeleteSSHCredential(item *kops.SSHCredential) error {
}
func (c *ClientsetCAStore) MirrorTo(basedir vfs.Path) error {
list, err := c.clientset.Keysets(c.namespace).List(v1.ListOptions{})
keysets, err := c.ListKeysets()
if err != nil {
return fmt.Errorf("error listing keysets: %v", err)
return err
}
for i := range list.Items {
keyset := &list.Items[i]
if keyset.Spec.Type == kops.SecretTypeSecret {
continue
}
primary := FindPrimary(keyset)
if primary == nil {
return fmt.Errorf("found keyset with no primary data: %s", keyset.Name)
}
switch keyset.Spec.Type {
case kops.SecretTypeKeypair:
for i := range keyset.Spec.Keys {
item := &keyset.Spec.Keys[i]
{
p := basedir.Join("issued", keyset.Name, item.Id+".crt")
acl, err := acls.GetACL(p, c.cluster)
if err != nil {
return err
}
err = p.WriteFile(item.PublicMaterial, acl)
if err != nil {
return fmt.Errorf("error writing %q: %v", p, err)
}
}
{
p := basedir.Join("private", keyset.Name, item.Id+".key")
acl, err := acls.GetACL(p, c.cluster)
if err != nil {
return err
}
err = p.WriteFile(item.PrivateMaterial, acl)
if err != nil {
return fmt.Errorf("error writing %q: %v", p, err)
}
}
}
default:
return fmt.Errorf("Ignoring unknown secret type: %q", keyset.Spec.Type)
for _, keyset := range keysets {
if err := mirrorKeyset(c.cluster, basedir, keyset); err != nil {
return err
}
}
sshCredentials, err := c.clientset.SSHCredentials(c.namespace).List(v1.ListOptions{})
sshCredentials, err := c.ListSSHCredentials()
if err != nil {
return fmt.Errorf("error listing SSHCredentials: %v", err)
}
for i := range sshCredentials.Items {
sshCredential := &sshCredentials.Items[i]
id, err := sshcredentials.Fingerprint(sshCredential.Spec.PublicKey)
if err != nil {
return fmt.Errorf("error fingerprinting SSH public key %q: %v", sshCredential.Name, err)
}
p := basedir.Join("ssh", "public", sshCredential.Name, id)
acl, err := acls.GetACL(p, c.cluster)
if err != nil {
for _, sshCredential := range sshCredentials {
if err := mirrorSSHCredential(c.cluster, basedir, sshCredential); err != nil {
return err
}
err = p.WriteFile([]byte(sshCredential.Spec.PublicKey), acl)
if err != nil {
return fmt.Errorf("error writing %q: %v", p, err)
}
}
return nil

View File

@ -256,31 +256,28 @@ func (c *VFSCAStore) loadKeysetBundle(p vfs.Path) (*keyset, error) {
return keyset, nil
}
// writeKeysetBundle writes a keyset bundle to VFS
func (c *VFSCAStore) writeKeysetBundle(p vfs.Path, name string, keyset *keyset, includePrivateMaterial bool) error {
p = p.Join("keyset.yaml")
o := &v1alpha2.Keyset{}
func (k *keyset) ToAPIObject(name string) (*kops.Keyset, error) {
o := &kops.Keyset{}
o.Name = name
o.Spec.Type = v1alpha2.SecretTypeKeypair
o.Spec.Type = kops.SecretTypeKeypair
for _, ki := range keyset.items {
oki := v1alpha2.KeysetItem{
for _, ki := range k.items {
oki := kops.KeysetItem{
Id: ki.id,
}
if ki.certificate != nil {
var publicMaterial bytes.Buffer
if _, err := ki.certificate.WriteTo(&publicMaterial); err != nil {
return err
return nil, err
}
oki.PublicMaterial = publicMaterial.Bytes()
}
if includePrivateMaterial && ki.privateKey != nil {
if ki.privateKey != nil {
var privateMaterial bytes.Buffer
if _, err := ki.privateKey.WriteTo(&privateMaterial); err != nil {
return err
return nil, err
}
oki.PrivateMaterial = privateMaterial.Bytes()
@ -289,25 +286,59 @@ func (c *VFSCAStore) writeKeysetBundle(p vfs.Path, name string, keyset *keyset,
o.Spec.Keys = append(o.Spec.Keys, oki)
}
var objectData bytes.Buffer
{
codecs := kopscodecs.Codecs
yaml, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), "application/yaml")
if !ok {
glog.Fatalf("no YAML serializer registered")
}
encoder := codecs.EncoderForVersion(yaml.Serializer, v1alpha2.SchemeGroupVersion)
return o, nil
}
if err := encoder.Encode(o, &objectData); err != nil {
return fmt.Errorf("error serializing keyset: %v", err)
}
// writeKeysetBundle writes a keyset bundle to VFS
func (c *VFSCAStore) writeKeysetBundle(p vfs.Path, name string, keyset *keyset, includePrivateMaterial bool) error {
p = p.Join("keyset.yaml")
o, err := keyset.ToAPIObject(name)
if err != nil {
return err
}
if !includePrivateMaterial {
o = removePrivateKeyMaterial(o)
}
objectData, err := serializeKeysetBundle(o)
if err != nil {
return err
}
acl, err := acls.GetACL(p, c.cluster)
if err != nil {
return err
}
return p.WriteFile(objectData.Bytes(), acl)
return p.WriteFile(objectData, acl)
}
// serializeKeysetBundle converts a keyset bundle to yaml, for writing to VFS
func serializeKeysetBundle(o *kops.Keyset) ([]byte, error) {
var objectData bytes.Buffer
codecs := kopscodecs.Codecs
yaml, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), "application/yaml")
if !ok {
glog.Fatalf("no YAML serializer registered")
}
encoder := codecs.EncoderForVersion(yaml.Serializer, v1alpha2.SchemeGroupVersion)
if err := encoder.Encode(o, &objectData); err != nil {
return nil, fmt.Errorf("error serializing keyset: %v", err)
}
return objectData.Bytes(), nil
}
// removePrivateKeyMaterial returns a copy of the Keyset with the private key data removed
func removePrivateKeyMaterial(o *kops.Keyset) *kops.Keyset {
copy := o.DeepCopy()
for i := range copy.Spec.Keys {
copy.Spec.Keys[i].PrivateMaterial = nil
}
return copy
}
func (c *VFSCAStore) loadCertificates(p vfs.Path, useBundle bool) (*keyset, error) {
@ -552,10 +583,100 @@ func (c *VFSCAStore) MirrorTo(basedir vfs.Path) error {
}
glog.V(2).Infof("Mirroring key store from %q to %q", c.basedir, basedir)
aclOracle := func(p vfs.Path) (vfs.ACL, error) {
return acls.GetACL(p, c.cluster)
keysets, err := c.ListKeysets()
if err != nil {
return err
}
return vfs.CopyTree(c.basedir, basedir, aclOracle)
for _, keyset := range keysets {
if err := mirrorKeyset(c.cluster, basedir, keyset); err != nil {
return err
}
}
sshCredentials, err := c.ListSSHCredentials()
if err != nil {
return fmt.Errorf("error listing SSHCredentials: %v", err)
}
for _, sshCredential := range sshCredentials {
if err := mirrorSSHCredential(c.cluster, basedir, sshCredential); err != nil {
return err
}
}
return nil
}
// mirrorKeyset writes keyset bundles for the certificates & privatekeys
func mirrorKeyset(cluster *kops.Cluster, basedir vfs.Path, keyset *kops.Keyset) error {
primary := FindPrimary(keyset)
if primary == nil {
return fmt.Errorf("found keyset with no primary data: %s", keyset.Name)
}
switch keyset.Spec.Type {
case kops.SecretTypeKeypair:
{
data, err := serializeKeysetBundle(removePrivateKeyMaterial(keyset))
if err != nil {
return err
}
p := basedir.Join("issued", keyset.Name, "keyset.yaml")
acl, err := acls.GetACL(p, cluster)
if err != nil {
return err
}
err = p.WriteFile(data, acl)
if err != nil {
return fmt.Errorf("error writing %q: %v", p, err)
}
}
{
data, err := serializeKeysetBundle(keyset)
if err != nil {
return err
}
p := basedir.Join("private", keyset.Name, "keyset.yaml")
acl, err := acls.GetACL(p, cluster)
if err != nil {
return err
}
err = p.WriteFile(data, acl)
if err != nil {
return fmt.Errorf("error writing %q: %v", p, err)
}
}
default:
return fmt.Errorf("unknown secret type: %q", keyset.Spec.Type)
}
return nil
}
// mirrorSSHCredential writes the SSH credential file to the mirror location
func mirrorSSHCredential(cluster *kops.Cluster, basedir vfs.Path, sshCredential *kops.SSHCredential) error {
id, err := sshcredentials.Fingerprint(sshCredential.Spec.PublicKey)
if err != nil {
return fmt.Errorf("error fingerprinting SSH public key %q: %v", sshCredential.Name, err)
}
p := basedir.Join("ssh", "public", sshCredential.Name, id)
acl, err := acls.GetACL(p, cluster)
if err != nil {
return err
}
err = p.WriteFile([]byte(sshCredential.Spec.PublicKey), acl)
if err != nil {
return fmt.Errorf("error writing %q: %v", p, err)
}
return nil
}
func (c *VFSCAStore) IssueCert(signer string, id string, serial *big.Int, privateKey *pki.PrivateKey, template *x509.Certificate) (*pki.Certificate, error) {