Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kubernetes Secrets Provider #2

Merged
merged 1 commit into from
Jul 31, 2024

Conversation

lxfontes
Copy link
Member

@lxfontes lxfontes commented Jul 24, 2024

Kubernetes Secrets Backend Implementation for wasmCloud Secrets

Basic Usage

Get secret app-secrets in the default namespace, and expose secret key some-password as some_password to component.

spec:
  policies:
    - name: rust-hello-world-secrets-default
      type: policy.secret.wasmcloud.dev/v1alpha1
      properties:
        backend: kube
  components:
    - name: http-component
      type: component
      properties:
        image: .......
        secrets:
          - name: some_password
            properties:
              policy: rust-hello-world-secrets-default
              key: app-secrets/some-password

Advanced Usage ( Impersonation )

Get secret cluster-secrets in the kube-system namespace, and expose secret key tls.crt as cluster_certificate to component.
The backend will impersonate the wasmcloud-secrets-privileged ClusterRole, defined in impersonate.

spec:
  policies:
    - name: rust-hello-world-secrets-impersonation
      type: policy.secret.wasmcloud.dev/v1alpha1
      properties:
        backend: kube
        # Cluster Role to Impersonate
        impersonate: wasmcloud-secrets-privileged
        # Namespace to retrieve secrets from
        namespace: kube-system
  components:
    - name: http-component
      type: component
      properties:
        image: ...
        secrets:
          - name: cluster_certificate
            properties:
              policy: rust-hello-world-secrets-impersonation
              key: cluster-secrets/tls.crt

Machinery

  • wasmCloud Secrets Protocol ( server_xkey and get operations )
  • wasCap jwt validation using Ed25519
  • wasCap Host & Entity Capabilities unwrapping

Other backend providers can be implemented by using secrets.NewServer() and its secrets.Handler companion.

type secretProvider struct{}
func (s *secretProvider) Get(ctx context.Context, r *secrets.Request) (*secrets.Response, error) {
return &secrets.Response{
  Secret: &secrets.SecretValue{
    Name:         r.Name,
    StringSecret: "p@$$w0rd",
    Version:      "latest",
  },
}, nil
}

...
provider := &secretProvider{}
// create a secrets server with ephemeral curve key
// can also pass stable key with `secrets.WithKeyPair(nkeys.KeyPair)`
secretsServer, _ := secrets.NewServer("provider-name", natsConnection, provider, secrets.WithEphemeralKey())

// Start secrets server
secretsServer.Run()

// Shutdown secrets server
// Receives a boolean to drain in-flight messages or bail quickly
secretsServer.Shutdown(true)

secrets/secrets-kubernetes/go.mod Outdated Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Outdated Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Outdated Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Show resolved Hide resolved
Copy link
Member

@brooksmtownsend brooksmtownsend left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is seriously awesome to see. I'll leave approval to the other reviewers who can validate the K8s manifests more closely, and/or the Go code more closely, but from my perspective I just have questions & nits

secrets/secrets-kubernetes/deploy/impersonation/README.md Outdated Show resolved Hide resolved
return nil, fmt.Errorf("%w: couldn't impersonate (%s)", secrets.ErrInvalidRequest, err.Error())
}

kubeSecret, err := kubeClient.Secrets(policy.Namespace).Get(ctx, kubeSecretName, metav1.GetOptions{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<3

if *secretsBackendSeed != "" {
secretsBackendKey, err = nkeys.FromSeed([]byte(*secretsBackendSeed))
} else {
slog.Info("Creating ephemeral curve keys. DO NOT USE THIS IN PRODUCTION.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually like the idea of using a completely ephemeral curve key, why wouldn't we want to do that given that the generated key shouldn't need to be used elsewhere?

Is the concern that, if you use an ephemeral key, that the host that's querying the backend may be unknowingly talking to a secrets backend that's malicious?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One downside to a just-in-time generated key as opposed to pre-generated key would be that you couldn't run multiple instances of the secret backend, because they would have different keys.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the concern that, if you use an ephemeral key, that the host that's querying the backend may be unknowingly talking to a secrets backend that's malicious?

precisely. as of now wasmcloudhost caches the backend server_xkey indefinitely, so we need to restart wasmcloudhost on secret backend key rotation.
the ⚖️ is that if we re-request server_xkey upon failure on the host, we might be talking to a rogue server. so maybe we need to look into verifying if a payload was signed with a known key ( signing keys style )

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be fine as long as you don't want to run more than one instance of the secrets backend. If you did want to run more than one pod for the backend then you'd have an issue if each one has an ephemeral key, since each instance would have a different key, which means that hosts could have a chance of not being able to decrypt the response.

We talked about ways to mitigate this by having some sort of stateful API endpoint a host could to get a key and that backends would query to get their private key, but I think that may need to come in a later iteration of the secrets api.

secrets/secrets-kubernetes/pkg/secrets/server.go Outdated Show resolved Hide resolved
secrets/secrets-kubernetes/pkg/secrets/types.go Outdated Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Outdated Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Show resolved Hide resolved

func init() {
SigningMethodEd25519 = &SigningMethodNats{}
jwt.RegisterSigningMethod(SigningMethodEd25519.Alg(), func() jwt.SigningMethod {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad you can do this with Go packages, because I still have yet to find a Rust crate that will let you define arbitrary key signing methods

@lxfontes
Copy link
Member Author

tested error serialization with host and it works now.
tl;dr: our error enum in rust is serialized with https://serde.rs/enum-representations.html#externally-tagged
So error payloads can be either

{ "error": "This is an Error" }

and also

{ "error": { "CustomError": "Message"}

Screenshot 2024-07-31 at 10 29 33 AM
Screenshot 2024-07-31 at 10 29 22 AM

@lxfontes lxfontes marked this pull request as ready for review July 31, 2024 14:40
@lxfontes lxfontes requested review from a team as code owners July 31, 2024 14:40
@lxfontes lxfontes changed the title proof of concept: Kubernetes Secrets Provider Kubernetes Secrets Provider Jul 31, 2024
secrets/secrets-kubernetes/main.go Outdated Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Outdated Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Outdated Show resolved Hide resolved
secrets/secrets-kubernetes/main.go Outdated Show resolved Hide resolved
Signed-off-by: Lucas Fontes <lucas@cosmonic.com>
@joonas joonas merged commit b9bd0b5 into wasmCloud:main Jul 31, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants