Kubernetes Credential Manager
Concourse can be configured to pull credentials
from Kubernetes secret objects.
To configure it, either enable the in-cluster client by setting the following environment variable on
the web node:
or set the path to a kubeconfig file:
Credential lookup rules
When resolving a parameter such as ((foo)), Concourse will look for it in the following order in the namespace
configured for that team:
You can also have nested fields if the contents of the secret is JSON, which can
be accessed using . syntax (e.g. ((foo.bar))).
The prefix prepended to the namespace used by Concourse to search for secrets
(in the examples above, concourse-) can be changed by configuring the following
in the web node:
If an action is being run in a one-off build, Concourse will not include the pipeline name in the secret that it looks for.
Configuring Kubernetes RBAC
As the Web nodes need to retrieve secrets from namespaces that are not their own, they needs extra permissions to do so.
If you have k8's RBAC enabled, that means creating the necessary Kubernetes objects to identify the Web nodes and give them access to a predefined list of namespaces where the secrets live.
Regardless of how the Kubernetes RBAC-related objects are created, the basic
requirement is that web must be able to read secrets in the namespaces where
each teams' secrets live.
For instance, if you have the following teams which you want to read secrets from:
- team-a
- team-b
Assuming the following web node configuration:
The web node must be able to get secrets from the following namespaces:
myprefix-team-amyprefix-team-b
To allow the web node to interpolate credentials for "team-a" and "team-b", we'd then need to create a few Kubernetes RBAC objects.
Starting with identifying the web service as an actor, we can use a
ServiceAccount
for that:
To allow actors to do something, in this case, retrieve secrets from a given namespace, a ClusterRole is then needed.
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: read-secrets
labels:
app: web
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
As that role is useless if not bound to an actor, the next step is creating the
the object that represents binding the role to the web's ServiceAccount that
we created before.
This is accomplished through the RoleBinding object, which is per-namespace (thus, per-team).
Note
Even though in this example we're binding to a ClusterRole (which is not
tied to any namespace), the use of such cluster role is (see
metadata.namespace), making the effective permissions restricted to the
namespace applied in the RoleBinding.
---
# Role binding for the first team (`team-b`), allowing `web`
# to consume secrets from it.
#
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: web-team-a
namespace: myprefix-team-a
labels:
app: web
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: read-secrets
subjects:
- kind: ServiceAccount
name: web
namespace: concourse
---
# Role binding for the second team (`team-b`), allowing `web`
# to consume secrets from it.
#
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: web-team-b
namespace: myprefix-team-b
labels:
app: web
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: read-secrets
subjects:
- kind: ServiceAccount
name: web
namespace: concourse
To finish the example, we need to associate the web
Pod with the service,
granting the pod access to those namespaces through the roles that have been
bound to it.
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 1
template:
metadata:
labels:
app: web
spec:
serviceAccountName: web
containers:
- name: web
image: "concourse/concourse:latest"
args: [ web ]
env:
- name: CONCOURSE_KUBERNETES_NAMESPACE_PREFIX
value: "myprefix-"
# ...