1.15.4.5 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:
CONCOURSE_KUBERNETES_IN_CLUSTER=true
or set the path to a kubeconfig
file:
CONCOURSE_KUBERNETES_CONFIG_PATH=~/.kube/config
Credential lookup rules
When resolving a parameter such as ((foo))
, Concourse will lookup for it in the following order in the namespace configured for that team:
Name: PIPELINE_NAME.foo Namespace: concourse-TEAM_NAME Type: Opaque Data ==== value: 32 bytes
Name: foo Namespace: concourse-TEAM_NAME Type: Opaque Data ==== value: 32 bytes
You can also have nested fields, which can be accessed using .
syntax, e.g. ((foo.bar))
:
Name: PIPELINE_NAME.foo Namespace: concourse-TEAM_NAME Type: Opaque Data ==== bar: 32 bytes
Name: foo Namespace: concourse-TEAM_NAME Type: Opaque Data ==== bar: 32 bytes
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:
CONCOURSE_KUBERNETES_NAMESPACE_PREFIX=some-other-prefix-
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 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:
CONCOURSE_KUBERNETES_NAMESPACE_PREFIX=myprefix-
web
must be able to get
secrets from the following namespaces:
myprefix-team-a
myprefix-team-b
To allow web
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:
apiVersion: v1
kind: ServiceAccount
metadata:
name: web
labels:
app: web
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
ServiceAccount
that we had created before.
This is accomplished through the RoleBinding object, which is per-namespace (thus, per-team).
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 (seemetadata.namespace
), making the effective permissions restricted to the namespace applied in theRoleBinding
.
---
# 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:5.3.0"
args: [ web ]
env:
- name: CONCOURSE_KUBERNETES_NAMESPACE_PREFIX
value: "myprefix-"
# ...