Secretless Broker Sidecar

This topic describes step-by-step how to enable applications in a Kubernetes environment to retrieve credentials from Conjur and use the Secretless Broker sidecar to connect to target services.

Prerequisites

  • You have installed and configured a Conjur cluster, and you have deployed a Follower to your OpenShift / Kubernetes cluster configured to use the Kubernetes Authenticator

  • You have an application that requires a supported database

  • Your database is running and accessible to apps in your OpenShift / Kubernetes environment, it supports SSL, and the credentials for the database have been loaded into Conjur.

  • Make sure you run Namespace Prep.

Required Information

To deploy Secretless, you need the following information about your Conjur configuration:

Required info

Description

How we refer to it

Policy branch with database credentials

The fully qualified ID of the policy branch in Conjur that contains your database secrets. You must have `read` access to the secrets in this branch so that you can see the IDs of the secrets you need.

DB_POLICY_BRANCH

Layer/group with access to secrets

The fully qualified Conjur ID of a layer or group whose members have access to the database secrets. You add the application host identity to this layer/group and the Secretless sidecar authenticates to Conjur using this host identity to retrieve secrets. Note that the examples refer to a layer; if you are provided with a group, replace all references to !layer DAP_SECRET_LAYER with !group DAP_SECRET_LAYER instead.

DAP_SECRET_LAYER

OpenShift vs Kubernetes

In this topic, the code samples assume you're using Kubernetes. If you're using OpenShift :

  • Replace kubectl with oc in all code snippets

  • Replace references to kubernetes: "true" in host annotations with the OpenShift analogue, openshift: "true"

  • References to namespaces may be equivalently considered references to OpenShift projects

Before getting started, you may want to read up on how Secretless works or how to configure Secretless.

Add your application to Conjur policy

Submit a request to your Conjur admin to create a host identity for your application. Your host can be defined using a variety of Kubernetes resources. For more information about the available options, see Workload identity for Kubernetes (cert-based authentication).

The following example uses the service account-based host identity. The host looks like this:

 
  - !host
      id: service-account-based-app
      annotations:
        authn-k8s/namespace: APP_NAMESPACE
        authn-k8s/service-account: APP_SERVICE_ACCOUNT
        authn-k8s/authentication-container-name: secretless

where APP_NAMESPACE is your app's Kubernetes namespace and APP_SERVICE_ACCOUNT is a service account that must be made available in the application pod.

Add the host definition to root or to any other branch where you want the policy to reside, for example conjur/authn-k8s/AUTHENTICATOR_NAME/apps. The host belongs to a layer with the same name as the branch. This allows the host to authenticate to Conjur with the Kubernetes Authenticator.

Finally, to give the host access to the database credentials, add it to the DAP_SECRET_LAYER layer.

Enable the Kubernetes Authenticator for your application

Save the following policy snippet (with variables updated to the values for your configuration) to a file named host-policy.yml:

 
- !host
  id: service-account-based-app
  annotations:
    authn-k8s/namespace: APP_NAMESPACE
    authn-k8s/service-account: APP_SERVICE_ACCOUNT
    authn-k8s/authentication-container-name: secretless
    kubernetes: "true"
# grant membership to the conjur/authn-k8s/AUTHENTICATOR_NAME/apps layer
- !grant
  role: !layer
  member: !host service-account-based-app

To create the host identity and ensure it's a member of a layer with access to the authenticator webservice, load the policy into the conjur/authn-k8s/AUTHENTICATOR_NAME/apps branch:

 
conjur policy load delete -b root -f <path to your policy>
 

Since we're using a service account-based host identity for our application, we'll need to remember to create the APP_SERVICE_ACCOUNT service account in our application manifest and add the service account to our pod definition.

 

In the example, the authentication container name is set to secretless in the host definition. When we add the Secretless container to our application manifest, we must define the container with name secretless as well.

Grant access to the database credentials

Save the following policy snippet (with variables updated to the values for your configuration) to a file named host-entitlement.yml:

 

- !grant
  role: !layer DAP_SECRET_LAYER
  member: !host conjur/authn-k8s/AUTHENTICATOR_NAME/apps/service-account-based-app

Add your application host to DAP_SECRET_LAYER so it has access to the database credentials:

 
conjur policy load -b root -f host-entitlement.yml

Prepare the application namespace

Grant the Follower access to pods in the namespace

To prepare your application's Kubernetes namespace (which we will continue to refer to as APP_NAMESPACE, as above) give the Follower permission to deliver Conjur access tokens to applications deployed in the namespace. To do so, create a RoleBinding:

  conjur policy load root host-entitlement.yml

Prepare the Secretless configuration

Write the Secretless configuration file

To begin we'll use the information on where the database credentials are stored in Conjur policy to help us write the Secretless Broker configuration. Secretless uses its configuration to determine where to listen for new connection requests, where to route those connections, and where to get the credentials for each connection.

Let's assume that we're using a PostgreSQL database and we've stored its address, username, and password in the ConjurDB_POLICY_BRANCH policy branch. Secretless recognizes Conjur by the secret provider name conjur and refers to the PostgreSQL service authenticator using the protocol identifier pg.

The Secretless configuration for this single PostgreSQL service authenticator that retrieves its credentials from Conjur looks like this:

 

version: "2"
services:
  postgres-sample:
    protocol: pg
    listenOn: tcp://0.0.0.0:5432
    credentials:
      address:
        from: conjur
        get: DB_POLICY_BRANCH/address
      password:
        from: conjur
        get: DB_POLICY_BRANCH/password
      username:
        from: conjur
        get: DB_POLICY_BRANCH/username
              

The configuration above instructs Secretless to listen on port 5432 for an incoming PostgreSQL connection. The credentials will come from the referenced variables stored in the pg-sample Conjur policy branch.

 

For your own application you will want to replace DB_POLICY_BRANCH with the actual fully qualified ID of the policy branch that your database secrets are in, and you will want to update the individual secret references to reference the actual secret names used in your Conjur policy.

 

The example above is for the PostgreSQL service authenticator, but other database authenticators will have different protocol identifiers and may require different credentials. Refer to the individual documentation pages for more information.

Save the configuration above to a file named secretless.yml. You may find it convenient to check this file in with your application code, since this configuration is specific to your application.

Store the Secretless configuration in the application namespace

To store the configuration in Kubernetes / OpenShift and make it accessible to the Secretless sidecar container, create a new ConfigMap in Kubernetes using the secretless.yml you saved in the previous step.

 

kubectl create configmap secretless-config --from-file=secretless.yml
 

Remember the name of the ConfigMap where you store your application's Secretless configuration; you will need to reference this in your application manifest later.

Deploy your application with the Secretless sidecar

You will deploy Secretless as a sidecar in the same pod as your application. To do this, you will update your application manifest to include the Secretless container definition, mounting its configuration ConfigMap as a volume.

Update your application

You can delete any code in your application that fetches credentials from a vault. Instead, your database client can simply connect to the address Secretless is listening on. It doesn't need credentials at all. Secretless automatically injects the credentials into the connection request and authenticates on your behalf.

In our example, remove any existing database credentials and configure the database client to connect instead to localhost:5432 - the port that Secretless will be listening on.

 

If you are connecting to PostgreSQL, your database name needs to be in the connection string you provide to the client. For example: postgresql://localhost:5432/my-database

Add Secretless to your app deployment manifest

Secretless Broker is deployed as a sidecar container in the same pod as your application. You can deploy Secretless with your app by modifying your application manifest to include the Secretless Broker container definition, with its configuration file mounted as a volume via the Secretless configuration ConfigMap. Secretless Broker is a public image available on DockerHub or Red Hat.

The final step is to add the Secretless Broker container definition to your application manifest. We've provided a sample below, but since Kubernetes application manifests can vary widely you may need to make some small changes. The key elements that we've added are:

  • A unique service account definition for the application, which we have also added to the pod spec
  • The Secretless container and its Conjur configuration, including the X.509 CA certificate chain and its secretless.yml configuration file provided by ConfigMap

 
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: APP_SERVICE_ACCOUNT
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-app
  name: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      serviceAccountName: APP_SERVICE_ACCOUNT
      containers:
      # Insert your container definition here
      # Ensure it's configured to point to Secretless for the DB connection
      #
      # - name: ${my-app}
      #   image: ${my-app:latest}
      - name: secretless
        image: cyberark/secretless-broker:latest
        imagePullPolicy: Always
        args: ["-f", "/etc/secretless/secretless.yml"]
        ports:
        - containerPort: 5432
        env:
          - name: CONJUR_AUTHN_LOGIN
            value: "host/conjur/authn-k8s/AUTHENTICATOR_NAME/apps/service-account-based-app
          - name: MY_POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: MY_POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: MY_POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        envFrom:
          - configMapRef:
            name: conjur-connect
        volumeMounts:
          - mountPath: /etc/secretless
            name: config
            readOnly: true
      volumes:
        - name: config
          configMap:
            name: secretless-config
            defaultMode: 420

                  

You can pull and load the environment variables from the ConfigMap generated during the Namespace preparation so that you can eliminate loading environment variables individually.

 

Environment variables not listed above are still required.

Finally, save your updated application manifest and apply it to deploy your pod.

Your application will now be connecting to your database via Secretless without ever knowing your database credentials.

 
kubectl create -f my-app-manifest.yml