README
¶
Thycotic DevOps Secrets Vault Kubernetes Secret Injector
A Kubernetes Mutating Webhook
that injects Secret data from Thycotic DevOps Secrets Vault (DSV) into
Kubernetes (k8s) cluster Secrets. The webhook is made available to the
Kubernetes cluster as the dsv-injector which can be hosted in k8s or as a
stand-alone service.
The webhook intercepts CREATE and UPDATE Secret admissions and supplements
or overwrites the Secret data with Secret data from DSV. The webhook
configuration is a set of DSV Role to Client Credential and Tenant mappings.
The webhook updates k8s Secrets based on a set of annotations (see below).
The webhook uses the DSV Go SDK to communicate with DSV.
It was built and tested with Minikube.
Configure
The webhook requires a JSON formatted list of DSV Role to Client Credential and
Tenant mappings, stored in configs/roles.json:
{
"my-role": {
"credentials": {
"clientId": "93d866d4-635f-4d4e-9ce3-0ef7f879f319",
"clientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxx-xxxxx"
},
"tenant": "mytenant"
},
"default": {
"credentials": {
"clientId": "64241412-3934-4aed-af26-95b1eaba0e6a",
"clientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxx-xxxxx"
},
"tenant": "mytenant"
}
}
The default Role is used when the k8s secret being modified does not specify a particular Role.
Run
The thycotic/dsv-injector image contains the dsv-injector-svc executable, but
it requires a certificate, the associated key and a roles.json file (see above).
$ /usr/bin/dsv-injector-svc -?
flag provided but not defined: -?
Usage of ./dsv-injector-svc:
-cert string
the path of the certificate file in PEM format (default "injector.pem")
-hostport string
the host:port e.g. localhost:8080 (default ":18543")
-key string
the path of the certificate key file in PEM format (default "injector.key")
-roles string
the path of JSON formatted roles file (default "roles.json")
To get a certificate and key, ensure that openssl
and
Use scripts/get_cert.sh, to get a certificate from your Kubernetes cluster:
$ sh scripts/get_cert.sh
Usage: get_cert.sh -n NAME [OPTIONS]...
-n, -name, --name NAME
Maps to the host portion of the FQDN that is the subject of the
certificate; also the basename of the certificate and key files.
-d, -directory, --directory=DIRECTORY
The location of the resulting certificate and private-key. The
default is '.'
-N, -namespace, --namespace=NAMESPACE
Represents the Kubernetes cluster Namespace and maps to the
domain of the FQDN that is the subject of the certificate.
the default is 'default'
-b, -bits, --bits=BITS
the RSA key size in bits; default is 2048
Build
Building the dsv-injector image requires Docker or
Podman.
Building and deploying the test_image requires a Kubernetes cluster.
The Makefile defaults are based on Minikube.
To build the image run:
make image
Test
To configure the Kubernetes cluster to to call the webhook as stand-alone
service running on the host at $(SERVICE_IP):
make deploy_host
Note that $(SERVICE_IP) defaults to the IP address of the host executing the
build. Also note that localhost will not work as an alternative.
To deploy the dsv-injector service as a POD and configure the webhook to call
it, run:
make deploy
Minikube
By default, the build uses Minikube and it must be up to build the test image.
minikube tunnel must also be running so that the build can communicate with
the registry unless the build uses an external $(REGISTRY).
Execute eval $(minikube dockerenv) in the build shell to use the Minikube
Docker daemon.
The Registry
The test image is based on the release image so the build needs to pull the latter to build the former.
By default, the build will look for a 'registry' service on the cluster and if
none is found it will run minikube addons enable registry to start one.
This behavior can be overridden by specifying the registry host:port in
$(REGISTRY). Note that the Makefile does not invoke docker login.
The CA Certificate
The WebHook uses the cluster CA certificate. The default location is
${HOME}/.minikube/ca.crt but that can be overridden by setting $(CA_CRT).
The location of the CA certificate can be gotten from the cluster configuration:
kubectl config view -o jsonpath='{.clusters[*].cluster.certificate-authority}'
If that returns null then the certificate is embedded in the cluster configuration.
In that case, set $(CA_BUNDLE) to the output of:
kubectl config view -o jsonpath='{.clusters[*].cluster.certificate-authority-data}' | tr -d '"'
Use
Once the dsv-injector is up and available to the Kubernetes cluster, and the
MutatingAdmissionWebhook is configured to call it, any
appropriately annotated k8s Secrets will be modified by it whenever they are
created or updated.
The four annotations that affect the behavior of the webhook are:
const(
roleAnnotation = "dsv.thycotic.com/role"
setAnnotation = "dsv.thycotic.com/set-secret"
addNotation = "dsv.thycotic.com/add-to-secret"
updateAnnotation = "dsv.thycotic.com/update-secret"
)
roleAnnotation sets the DSV Role that k8s should use to access the DSV Secret
that will be used to modify the k8s Secret. If it is present then the Role
must exist in the above mentioned Role mappings that the webhook is configured
to use. If it is absent then the default mapping is used.
The setAnnotation, addAnnotation and updateAnnotation contain the path to
the DSV Secret that will be used to modified the k8s Secret being admitted.
addAnnotationadds missing fields without overwriting or removing existing fields.updateAnnotationadds and overwrites existing fields but does not remove fields.setAnnotationoverwrites fields and removes fields that do not exist in the DSV Secret.
Only one of these should be specified on any given k8s Secret, however, if more
than one are defined then the order of precedence is setAnnotation then
addAnnotation then updateAnnotation.
Examples
---
apiVersion: v1
kind: Secret
metadata:
name: example-secret
annotations:
dsv.thycotic.com/role: my-role
dsv.thycotic.com/set-secret: /test/secret
type: Opaque
data:
username: dW5tb2RpZmllZC11c2VybmFtZQ==
domain: dW5tb2RpZmllZC1kb21haW4=
password: dW5tb2RpZmllZC1wYXNzd29yZA==
The above example specifies a Role so a mapping for that role must exist in the
current webhook configuration. It uses the setAnnotation so the data in the
secret will be overwritten; if /test/secret contains a username and
password but no domain then the secret would contain the username and
password from the DSV Secret Data and the domain field will be removed.
There are more examples in the examples directory. Each one will show
how each annotation works when run against an example with only a username and
password in it.
$ thy secret read /test/secret -f .data
{
"password": "alongpassword",
"username": "someuser"
}