From 516503c3c5eb7a12786c6adb5fda681a372e2e5a Mon Sep 17 00:00:00 2001 From: Marko Mikulicic Date: Tue, 30 Jul 2019 18:51:42 +0200 Subject: [PATCH] Allow access to sealed secret services/proxy to any authenticated user This allows kubeseal to fetch the certificate public key (and perform other actions such as /verify and /rotate endpoints) even if the caller doesn't have otherwise the rights to access the kube-system namespace (or any other namespace where the sealed-secrets controller might have been deployed), as it often happens that users are not granted such broad permissions on production clusters. We historically suggested users to just distribute the certificate out of bound and use the `--cert` flag. However, with the advent of master key rotation, this is becoming increasingly more cumbersome, especially since it's critical that users end up using the right certificate (i.e. the certificate has to be authenticated). Master key rotation also requires users to periodically rotate the secrets, which requires access to the /rotate endpoint. This change includes a fine-grained RBAC rule that allows access to the sealed-secrets controller HTTP API to any authenticated user in the cluster. Users are still free to disable this feature by applying an override during deployment, but our default RBAC config should include it. The controller currently exposes the following endpoints: - `/healthz' - `/v1/verify` - `/v1/rotate` - `/v1/cert.pem` The controller already must not expose any secrets via the HTTP endpoint, since while RBAC would prevent end-users to access the service via the proxy, nothing prevents any unprivileged workload in the cluster unless admins have explicitly configured a strict network policy rule set. Closes #166 --- cmd/controller/server.go | 3 +++ controller.jsonnet | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/cmd/controller/server.go b/cmd/controller/server.go index 763517ee1..7ded17825 100644 --- a/cmd/controller/server.go +++ b/cmd/controller/server.go @@ -26,6 +26,9 @@ type certProvider func() []*x509.Certificate type secretChecker func([]byte) (bool, error) type secretRotator func([]byte) ([]byte, error) +// httpserver starts an HTTP that exposes core functionality like serving the public key +// or secret rotation and validation. This endpoint is designed to be accessible by +// all users of a given cluster. It must not leak any secret material. func httpserver(cp certProvider, sc secretChecker, sr secretRotator) { httpRateLimiter := rateLimter() diff --git a/controller.jsonnet b/controller.jsonnet index bc14c89b3..88424cb7e 100644 --- a/controller.jsonnet +++ b/controller.jsonnet @@ -38,6 +38,26 @@ controller { ], }, + serviceProxierRole: kube.Role('sealed-secrets-service-proxier') + $.namespace { + rules: [ + { + apiGroups: [ + '', + ], + resources: [ + 'services/proxy', + ], + resourceNames: [ + 'http:sealed-secrets-controller:', // kubeseal uses net.JoinSchemeNamePort when crafting proxy subresource URLs + 'sealed-secrets-controller', // but often services are referred by name only, let's not make it unnecessarily cryptic + ], + verbs: [ + 'get', + ], + }, + ], + }, + unsealerBinding: kube.ClusterRoleBinding('sealed-secrets-controller') { roleRef_: $.unsealerRole, subjects_+: [$.account], @@ -48,6 +68,13 @@ controller { subjects_+: [$.account], }, + serviceProxierBinding: kube.RoleBinding('sealed-secrets-service-proxier') + $.namespace { + roleRef_: $.serviceProxierRole, + // kube.libsonnet assumes object here have a namespace, but system groups don't + // thus are not supposed to use the magic "_" here. + subjects+: [kube.Group('system:authenticated')], + }, + controller+: { spec+: { template+: {