Skip to content

Commit

Permalink
Removed keynames from user facing functionality
Browse files Browse the repository at this point in the history
This commit addresses the concern that introducing keynames to
kubeseal and the sealedsecrets api introduces breaking changes and
exposes too many internal details to users.

In particular, removed all references and usages of keynames from
kubeseal and sealedsecret type defintions. Includes relevant
changes to controller to make it work with the changes.
  • Loading branch information
anzboi committed Apr 10, 2019
1 parent fac3802 commit 43bf1c1
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 98 deletions.
46 changes: 25 additions & 21 deletions cmd/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ func unseal(sclient v1.SecretsGetter, codecs runtimeserializer.CodecFactory, key
objName := fmt.Sprintf("%s/%s", ssecret.GetObjectMeta().GetNamespace(), ssecret.GetObjectMeta().GetName())
log.Printf("Updating %s", objName)

privKey, err := keyRegistry.getPrivateKey(ssecret.Spec.EncryptionKeyName)

secret, err := ssecret.Unseal(codecs, privKey)
secret, _, err := attemptUnseal(ssecret, keyRegistry)
if err != nil {
// TODO: Add error event
return err
Expand Down Expand Up @@ -187,11 +185,7 @@ func (c *Controller) unseal(key string) error {
ssecret := obj.(*ssv1alpha1.SealedSecret)
log.Printf("Updating %s", key)

privKey, err := c.keyRegistry.getPrivateKey(ssecret.Spec.EncryptionKeyName)
if err != nil {
return err
}
secret, err := ssecret.Unseal(scheme.Codecs, privKey)
secret, _, err := c.attemptUnseal(ssecret)
if err != nil {
return err
}
Expand Down Expand Up @@ -254,11 +248,7 @@ func (c *Controller) AttemptUnseal(content []byte) (bool, error) {

switch s := object.(type) {
case *ssv1alpha1.SealedSecret:
privKey, err := c.keyRegistry.getPrivateKey(s.Spec.EncryptionKeyName)
if err != nil {
return false, fmt.Errorf("Could not retrieve private key from registry. %s", err)
}
if _, err := s.Unseal(scheme.Codecs, privKey); err != nil {
if _, _, err := c.attemptUnseal(s); err != nil {
return false, nil
}
return true, nil
Expand All @@ -268,6 +258,9 @@ func (c *Controller) AttemptUnseal(content []byte) (bool, error) {
}
}

// Rotate takes a sealed secret and returns a sealed secret that has been encrypted
// with the latest private key. If the secret is already encryptes with the latest,
// returns the input.
func (c *Controller) Rotate(content []byte) ([]byte, error) {
object, err := runtime.Decode(scheme.Codecs.UniversalDecoder(ssv1alpha1.SchemeGroupVersion), content)
if err != nil {
Expand All @@ -276,21 +269,19 @@ func (c *Controller) Rotate(content []byte) ([]byte, error) {

switch s := object.(type) {
case *ssv1alpha1.SealedSecret:
privKey, err := c.keyRegistry.getPrivateKey(s.Spec.EncryptionKeyName)
if err != nil {
return nil, fmt.Errorf("Could not retrieve private key from registry. %v", err)
}
secret, err := s.Unseal(scheme.Codecs, privKey)
secret, keyName, err := c.attemptUnseal(s)
if err != nil {
return nil, fmt.Errorf("Error decrypting sealed secret. %v", err)
return nil, fmt.Errorf("Error decrypting secret. %v", err)
}

latestKeyName := c.keyRegistry.latestKeyName()
if keyName == latestKeyName {
return content, nil // Already encrypted with latest secret, return input
}
latestPrivKey, err := c.keyRegistry.getPrivateKey(latestKeyName)
if err != nil {
return nil, fmt.Errorf("Error getting latest private key. %v", err)
}
resealedSecret, err := ssv1alpha1.NewSealedSecret(scheme.Codecs, latestKeyName, &latestPrivKey.PublicKey, secret)
resealedSecret, err := ssv1alpha1.NewSealedSecret(scheme.Codecs, &latestPrivKey.PublicKey, secret)
if err != nil {
return nil, fmt.Errorf("Error creating new sealed secret. %v", err)
}
Expand All @@ -303,3 +294,16 @@ func (c *Controller) Rotate(content []byte) ([]byte, error) {
return nil, fmt.Errorf("Unexpected resoure type: %s", s.GetObjectKind().GroupVersionKind().String())
}
}

func (c *Controller) attemptUnseal(ss *ssv1alpha1.SealedSecret) (*apiv1.Secret, string, error) {
return attemptUnseal(ss, c.keyRegistry)
}

func attemptUnseal(ss *ssv1alpha1.SealedSecret, keyRegistry *KeyRegistry) (*apiv1.Secret, string, error) {
for _, privKey := range keyRegistry.keys {
if secret, err := ss.Unseal(scheme.Codecs, privKey); err == nil {
return secret, "", nil
}
}
return nil, "", fmt.Errorf("No key could decrypt secret")
}
78 changes: 14 additions & 64 deletions cmd/kubeseal/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@ var (
controllerNs = flag.String("controller-namespace", metav1.NamespaceSystem, "Namespace of sealed-secrets controller.")
controllerName = flag.String("controller-name", "sealed-secrets-controller", "Name of sealed-secrets controller.")
outputFormat = flag.String("format", "json", "Output format for sealed secret. Either json or yaml")
keyName = flag.String("keyname", "", "Name of a key to use.")
rotate = flag.Bool("rotate", false, "Rotate the given key to use the latest private key in the cluster.")
dumpCert = flag.Bool("fetch-cert", false, "Write certificate to stdout. Useful for later use with --cert")
dumpKeyName = flag.Bool("fetch-keyname", false, "Write keyname so stdout. Useful for later use with --keyname")
printVersion = flag.Bool("version", false, "Print version information and exit")
validateSecret = flag.Bool("validate", false, "Validate that the sealed secret can be decrypted")

Expand Down Expand Up @@ -118,82 +116,43 @@ func prettyEncoder(codecs runtimeserializer.CodecFactory, mediaType string, gv r
return enc, nil
}

func openCertFile(certFile string) (io.ReadCloser, string, error) {
func openCertFile(certFile string) (io.ReadCloser, error) {
f, err := os.Open(certFile)
if err != nil {
return nil, "", fmt.Errorf("Error reading %s: %v", certFile, err)
return nil, fmt.Errorf("Error reading %s: %v", certFile, err)
}
return f, *keyName, nil
return f, nil
}

func openCertHTTP(c corev1.CoreV1Interface, namespace, name string) (io.ReadCloser, string, error) {
func openCertHTTP(c corev1.CoreV1Interface, namespace, name string) (io.ReadCloser, error) {
f, err := c.
Services(namespace).
ProxyGet("http", name, "", "/v1/cert.pem", map[string]string{"keyname": *keyName}).
ProxyGet("http", name, "", "/v1/cert.pem", nil).
Stream()
if err != nil {
return nil, "", fmt.Errorf("Error fetching certificate: %v", err)
return nil, fmt.Errorf("Error fetching certificate: %v", err)
}
return f, *keyName, nil
return f, nil
}

func openCert() (io.ReadCloser, string, error) {
func openCert() (io.ReadCloser, error) {
if *certFile != "" {
return openCertFile(*certFile)
}

if *keyName == "" {
name, err := getKeyName()
if err != nil {
return nil, "", err
}
*keyName = name
}

conf, err := clientConfig.ClientConfig()
if err != nil {
return nil, "", err
return nil, err
}
conf.AcceptContentTypes = "application/x-pem-file, */*"
restClient, err := corev1.NewForConfig(conf)
if err != nil {
return nil, "", err
return nil, err
}
return openCertHTTP(restClient, *controllerNs, *controllerName)
}

func getKeyName() (string, error) {
if *keyName != "" {
return *keyName, nil
}

// Setup client
conf, err := clientConfig.ClientConfig()
if err != nil {
return "", err
}
conf.AcceptContentTypes = "plain/text"
restClient, err := corev1.NewForConfig(conf)
if err != nil {
return "", err
}
// Get the latest keyname from controller
f, err := restClient.
Services(*controllerNs).
ProxyGet("http", *controllerName, "", "/v1/keyname", nil).
Stream()
if err != nil {
return "", fmt.Errorf("Error fetching keyname: %v", err)
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
return "", fmt.Errorf("Error reading fetched keyname: %v", err)
}
return string(data), nil
}

func seal(in io.Reader, out io.Writer, codecs runtimeserializer.CodecFactory, pubKey *rsa.PublicKey, keyname string) error {
func seal(in io.Reader, out io.Writer, codecs runtimeserializer.CodecFactory, pubKey *rsa.PublicKey) error {
secret, err := readSecret(codecs.UniversalDecoder(), in)
if err != nil {
return err
Expand Down Expand Up @@ -229,7 +188,7 @@ func seal(in io.Reader, out io.Writer, codecs runtimeserializer.CodecFactory, pu
secret.SetDeletionTimestamp(nil)
secret.DeletionGracePeriodSeconds = nil

ssecret, err := ssv1alpha1.NewSealedSecret(codecs, keyname, pubKey, secret)
ssecret, err := ssv1alpha1.NewSealedSecret(codecs, pubKey, secret)
if err != nil {
return err
}
Expand Down Expand Up @@ -382,23 +341,14 @@ func main() {
return
}

if *dumpKeyName {
keyName, err := getKeyName()
if err != nil {
panic(err.Error())
}
os.Stdout.WriteString(keyName)
return
}

if *rotate {
if err := rotateSealedSecret(os.Stdin, os.Stdout, scheme.Codecs, *controllerNs, *controllerName); err != nil {
panic(err.Error())
}
return
}

f, name, err := openCert()
f, err := openCert()
if err != nil {
panic(err.Error())
}
Expand All @@ -416,7 +366,7 @@ func main() {
panic(err.Error())
}

if err := seal(os.Stdin, os.Stdout, scheme.Codecs, pubKey, name); err != nil {
if err := seal(os.Stdin, os.Stdout, scheme.Codecs, pubKey); err != nil {
panic(err.Error())
}
}
7 changes: 3 additions & 4 deletions pkg/apis/sealed-secrets/v1alpha1/sealedsecret_expansion.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func labelFor(o metav1.Object) ([]byte, bool, bool) {
}
namespaceWide := o.GetAnnotations()[SealedSecretNamespaceWideAnnotation]
if namespaceWide == "true" {
return []byte(o.GetNamespace()), false, true
return []byte(o.GetNamespace()), false, true
}
return []byte(fmt.Sprintf("%s/%s", o.GetNamespace(), o.GetName())), false, false
}
Expand Down Expand Up @@ -77,7 +77,7 @@ func NewSealedSecretV1(codecs runtimeserializer.CodecFactory, pubKey *rsa.Public
// NewSealedSecret creates a new SealedSecret object wrapping the
// provided secret. This encrypts only the values of each secrets
// individually, so secrets can be updated one by one.
func NewSealedSecret(codecs runtimeserializer.CodecFactory, keyName string, pubKey *rsa.PublicKey, secret *v1.Secret) (*SealedSecret, error) {
func NewSealedSecret(codecs runtimeserializer.CodecFactory, pubKey *rsa.PublicKey, secret *v1.Secret) (*SealedSecret, error) {
if secret.GetNamespace() == "" {
return nil, fmt.Errorf("Secret must declare a namespace")
}
Expand All @@ -88,8 +88,7 @@ func NewSealedSecret(codecs runtimeserializer.CodecFactory, keyName string, pubK
Namespace: secret.GetNamespace(),
},
Spec: SealedSecretSpec{
EncryptedData: map[string][]byte{},
EncryptionKeyName: keyName,
EncryptedData: map[string][]byte{},
},
Type: secret.Type,
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/apis/sealed-secrets/v1alpha1/sealedsecret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func TestSealRoundTrip(t *testing.T) {
},
}

ssecret, err := NewSealedSecret(codecs, "keyname", &key.PublicKey, &secret)
ssecret, err := NewSealedSecret(codecs, &key.PublicKey, &secret)
if err != nil {
t.Fatalf("NewSealedSecret returned error: %v", err)
}
Expand Down Expand Up @@ -229,7 +229,7 @@ func TestSealRoundTripWithClusterWide(t *testing.T) {
},
}

ssecret, err := NewSealedSecret(codecs, "keyname", &key.PublicKey, &secret)
ssecret, err := NewSealedSecret(codecs, &key.PublicKey, &secret)
if err != nil {
t.Fatalf("NewSealedSecret returned error: %v", err)
}
Expand Down Expand Up @@ -270,7 +270,7 @@ func TestSealRoundTripWithMisMatchClusterWide(t *testing.T) {
},
}

ssecret, err := NewSealedSecret(codecs, "keyname", &key.PublicKey, &secret)
ssecret, err := NewSealedSecret(codecs, &key.PublicKey, &secret)
if err != nil {
t.Fatalf("NewSealedSecret returned error: %v", err)
}
Expand Down Expand Up @@ -309,7 +309,7 @@ func TestSealRoundTripWithNamespaceWide(t *testing.T) {
},
}

ssecret, err := NewSealedSecret(codecs, "keyname", &key.PublicKey, &secret)
ssecret, err := NewSealedSecret(codecs, &key.PublicKey, &secret)
if err != nil {
t.Fatalf("NewSealedSecret returned error: %v", err)
}
Expand Down Expand Up @@ -350,7 +350,7 @@ func TestSealRoundTripWithMisMatchNamespaceWide(t *testing.T) {
},
}

ssecret, err := NewSealedSecret(codecs, "keyname", &key.PublicKey, &secret)
ssecret, err := NewSealedSecret(codecs, &key.PublicKey, &secret)
if err != nil {
t.Fatalf("NewSealedSecret returned error: %v", err)
}
Expand Down
7 changes: 3 additions & 4 deletions pkg/apis/sealed-secrets/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

const (
// SealedSecretName is the name used in SealedSecret CRD
// SealedSecretName is the name used in SealedSecret TPR
SealedSecretName = "sealed-secret." + GroupName
// SealedSecretPlural is the collection plural used with SealedSecret API
SealedSecretPlural = "sealedsecrets"
Expand All @@ -26,9 +26,8 @@ const (
// SealedSecretSpec is the specification of a SealedSecret
type SealedSecretSpec struct {
// Data is deprecated and will be removed eventually. Use per-value EncryptedData instead.
Data []byte `json:"data,omitempty"`
EncryptedData map[string][]byte `json:"encryptedData"`
EncryptionKeyName string `json:"keyname"`
Data []byte `json:"data,omitempty"`
EncryptedData map[string][]byte `json:"encryptedData"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down

0 comments on commit 43bf1c1

Please sign in to comment.