Pinniped blog
Pinniped v0.33.0: Externally-managed CA bundles for Pinniped's custom resources
Aug 7, 2024
Photo from Unsplash
Pinniped’s v0.33.0 release enables Pinniped administrators to use externally-provided CA bundles for all custom resources for which Pinniped acts as a client. This includes OIDC identity providers, LDAP and Active Directory servers, GitHub Enterprise Servers, and any JWT or webhook authenticators running on or off the cluster.
This should reduce manual steps to install or configure Pinniped, since administrators no longer need to provide the CA bundle
inline within a Pinniped custom resource, and can instead use a ConfigMap
or Secret
object in the same namespace as
Pinniped Supervisor or Concierge. Often, these ConfigMap
and Secret
objects will be managed by tooling such as
cert-manager
, trust-manager
, or Vault
, all of which can help manage certificate distribution.
Concierge and Supervisor will monitor these ConfigMap
or Secret
objects, and automatically read in any changes.
This means that manual updates to custom resources is no longer required, to ensure almost no downtime if the
certificate needs to be rotated.
Example
Here is an example of how you would previously configure a CA bundle for an ActiveDirectoryIdentityProvider
.
apiVersion: idp.supervisor.pinniped.dev/v1alpha1
kind: ActiveDirectoryIdentityProvider
metadata:
name: my-active-directory-idp
namespace: pinniped-supervisor
spec:
host: "activedirectory.example.com:636"
bind:
secretName: "active-directory-bind-account"
tls:
# This would contain your base64 encoded CA bundle.
certificateAuthorityData: LS0tLS1CRUdJTiBDR...
And here is an example of how you can alternatively configure the CA bundle using this new feature,
with the new certificateAuthorityDataSource
configuration option.
apiVersion: idp.supervisor.pinniped.dev/v1alpha1
kind: ActiveDirectoryIdentityProvider
metadata:
name: my-active-directory-idp
namespace: pinniped-supervisor
spec:
host: "activedirectory.example.com:636"
bind:
secretName: "active-directory-bind-account"
tls:
# This is the new feature! Instead of embedding your CA
# bundle here, you can refer to a Secret or ConfigMap.
certificateAuthorityDataSource:
kind: ConfigMap
name: my-ca-bundle-config-map
key: ca.crt
In the above example, the ConfigMap must be in the pinniped-supervisor
namespace.
Its content will be dynamically loaded, and will be automatically watched for any updates,
which will also be dynamically reloaded.
This gives you the advantage of being able to more easily update your CA bundles using any automation tools that can update ConfigMaps or Secrets.
The same feature has been added to all Concierge authenticator resource types (WebhookAuthenticator
and JWTAuthenticator
)
and all Supervisor identity provider resource types
(OIDCIdentityProvider
, GitHubIdentityProvider
, ActiveDirectoryIdentityProvider
, and LDAPIdentityProvider
).
Demo using local-user-authenticator
as a service and trust-manager
to manage the CA bundles
This demo will show using the new certificateAuthorityDataSource
feature on a WebhookAuthenticator
.
In order to demo that, we need a webhook provider and a CA bundle.
We can install Pinniped’s local-user-authenticator
to act as webhook provider and source of identities,
and then configure the Concierge WebhookAuthenticator
to use it.
During installation, local-user-authenticator
will generate its own self-signed CA bundle that we
must provide to Concierge in the WebhookAuthenticator
custom resource.
NOTE: The local-user-authenticator
is not production-ready. It’s used here only for demonstration purposes
because it is easy to install and configure as an identity provider for a WebhookAuthenticator
.
We will use trust-manager
to distribute the CA bundle from the local-user-authenticator
namespace
to the pinniped-concierge
namespace. Usually, trust-manager
is used to distribute certificates generated
by cert-manager
, but in this demo we don’t need the added complexity that would come from also using cert-manager
.
Setup and creating a cluster
In order to perform the following steps, you should have recent versions of the following tools:
kind
kubectl
pinniped
(https://pinniped.dev/docs/howto/install-cli/)docker
helm
Create a local kind
cluster:
$ kind create cluster -n pinniped-testing
Install local-user-authenticator
:
$ kubectl apply --filename https://get.pinniped.dev/v0.33.0/install-local-user-authenticator.yaml
# Create a user "pinny" with password "password123" and group "some-group".
# Each secret in this namespace acts like a user definition.
$ kubectl create secret generic pinny \
--namespace local-user-authenticator \
--from-literal=groups=some-group \
--from-literal=passwordHash=$(htpasswd -nbBC 10 x password123 | sed -e "s/^x://")
# Wait for the CA bundle to be generated by local-user-authenticator
$ kubectl wait \
--for=jsonpath={.data.caCertificate} \
secret/local-user-authenticator-tls-serving-certificate \
--namespace local-user-authenticator \
--timeout 60s
# Label this secret, so that trust-manager can find it later
$ kubectl label secret local-user-authenticator-tls-serving-certificate \
--namespace local-user-authenticator \
readable-by-trust-manager=yes-please
Install trust-manager
, with steps taken from cert-manager installation
and trust-manager installation:
$ helm repo add jetstack https://charts.jetstack.io --force-update
# Install trust-manager into the local-user-authenticator namespace,
# just so that it can read secrets in that namespace.
# This is not meant for production use.
$ helm upgrade trust-manager jetstack/trust-manager \
--install \
--namespace local-user-authenticator \
--wait \
--set app.webhook.tls.helmCert.enabled=true \
--set app.trust.namespace=local-user-authenticator
# Various warnings will print out related to this installation
# being unsuitable for production use
Configure trust-manager
to replicate the local-user-authenticator
CA bundle into the desired namespaces.
This is merely one way of many to use trust-manager
and not meant to be a specific recommendation.
First, install Concierge’s resources, which will create a namespace called pinniped-concierge
.
$ kubectl apply --filename https://get.pinniped.dev/v0.33.0/install-pinniped-concierge-crds.yaml
$ kubectl apply --filename https://get.pinniped.dev/v0.33.0/install-pinniped-concierge-resources.yaml
# Apply a label for trust-manager's namespaceSelector
$ kubectl label namespaces pinniped-concierge \
allow-trust-manager-bundles=yes-please \
--overwrite=true
# Create a Bundle so that trust-manager will propagate the CA bundle from
# local-user-authenticator to concierge
$ cat << EOF > my-trust-manager-bundle.yaml
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: my-trust-manager-bundle
spec:
sources:
- secret:
selector:
matchLabels:
readable-by-trust-manager: yes-please
key: "caCertificate"
target:
# Sync the bundle to a ConfigMap called 'my-trust-manager-bundle'.
configMap:
key: "local-user-authenticator-ca.pem"
namespaceSelector:
matchLabels:
allow-trust-manager-bundles: "yes-please"
EOF
$ kubectl apply --filename my-trust-manager-bundle.yaml
# Confirm that the ConfigMap was created
$ kubectl get configmap my-trust-manager-bundle \
--namespace pinniped-concierge \
--output yaml
Create a WebhookAuthenticator
to tell Concierge to validate static tokens using the installed local-user-authenticator
.
$ cat << EOF > concierge.webhookauthenticator.yaml
apiVersion: authentication.concierge.pinniped.dev/v1alpha1
kind: WebhookAuthenticator
metadata:
name: local-user-authenticator
spec:
endpoint: https://local-user-authenticator.local-user-authenticator.svc/authenticate
tls:
# This API element is introduced in v0.33.0 as part of this new feature.
certificateAuthorityDataSource:
kind: ConfigMap
name: my-trust-manager-bundle
key: local-user-authenticator-ca.pem
EOF
# Create the webhook authenticator
$ kubectl apply --filename concierge.webhookauthenticator.yaml
Attempt a login!
# As an admin user, let's grant the user's group some permissions
$ kubectl create clusterrolebinding members-of-some-group-are-admins \
--clusterrole=view \
--group=some-group
# Now we get a user kubeconfig...
$ pinniped get kubeconfig \
--static-token "pinny:password123" \
--concierge-authenticator-type webhook \
--concierge-authenticator-name local-user-authenticator \
> pinniped-kubeconfig.yaml
# ... and perform an action as that user
$ kubectl get pods -A --kubeconfig pinniped-kubeconfig.yaml
# Success!
Where to read more
- See the original proposal for this body of work here
Join the Pinniped community!
Pinniped is better because of our contributors and maintainers. It's because of you that we can bring great software to the community.
Connect with the community on GitHub and Slack.
Join our Google Group to receive updates and meeting invitations.
Related content
Pinniped v0.31.0: GitHub as an identity provider
With the release of v0.31.0, Pinniped brings GitHub identities to Kubernetes clusters everywhere
Pinniped v0.25.0: With External Certificate Management for the Impersonation Proxy and more!
With v0.25.0 you get external certificate management for the impersonation proxy, easier scheduling of the kube-cert-agent, and more
Pinniped v0.26.0: Multiple identity providers and identity transformations
With the release of v0.26.0, Pinniped now supports multiple identity providers and identity transformations