- Athenz
- Proof of concept: k8s identity integration with Athenz
-
-
Save gotwarlost/f46bd4abd59c1bbebcd0b8c3a9851d5b to your computer and use it in GitHub Desktop.
Athenz provides 2 mechanisms for services to get an identity, one by proving key ownership and the other when launched using a provider (openstack, AWS, mesos, kubernetes etc.)
A service that possesses its own private key can get a TLS cert from Athenz corresponding to its private key.
POST "/instance/my.domain/the-service/refresh"
Input:
{
"csr": "PEM-encoded CSR",
"keyId": "v1"
}
The CSR should be signed by the private key for the supplied key-version where the public key has been uploaded to Athenz.
Output:
{
"name": "my.domain.the-service",
"certificate": "PEM encoded cert",
"caCertBundle": "PEM encoded root CA bundle"
}
A service being launched by another can get an identity using an identity document that the launcher validates on Athenz's behalf. The CSR is signed using an ephemeral private key.
POST "/instance"
Input:
{
"provider": "provider.domain.launcher-service",
"domain": "my.domain",
"service": "the-service",
"attestationData": "JWT or some other format",
"csr": "PEM-encoded CSR",
"token": true
}
Output:
{
"provider": "provider.domain.launcher-service",
"name": "my.domain.the-service",
"instanceId": "athenz-generated-id",
"x509Certificate": "PEM encoded cert",
"x509CertificateSigner": "PEM encoded root CA bundle",
"serviceToken": "signed principal token",
"attributes": {}
}
After the initial identity is gotten, it may be used as proof for refreshes. The refresh request must be posted using a client with certificates for the previous identity.
POST /instance/provider.domain.launcher-service/my.domain/the-service/instance-id
Input:
{
"csr": "PEM-encoded CSR",
"token": true
}
Output:
{
"provider": "provider.domain.launcher-service",
"name": "my.domain.the-service",
"instanceId": "athenz-generated-id",
"x509Certificate": "PEM encoded cert",
"x509CertificateSigner": "PEM encoded root CA bundle",
"serviceToken": "signed principal token",
"attributes": {}
}
Athenz calls back to the launcher service to verify the requested identity. The launcher service is discovered using the endpoint registered for it in Athenz. Athenz will only trust the callback endpoint if its TLS cert has been minted using the Athenz root CA and has the expected common name.
POST "/instance"
Input:
{
"provider": "provider.domain.launcher-service",
"domain": "my.domain",
"service": "the-service",
"attestationData": "JWT",
"attributes": {
"sanDNS": "name1,name2",
"sanIP": "ip1,ip2",
"clientIP": "ip"
}
}
Output: Returns the same object with a 200 response, with possibly additional attributes, if the identity should be granted.
-
Replace app secrets with trusted bootstrap
-
Implement bootstrap in line with k8s direction
-
Pull identity on startup (as opposed to push) so that bootstrap is race-free, refresh with pulls
-
Running pods should be able to tolerate identity agent restarts for crashes/ upgrades
-
Abstract application owners from details of implementation
- Interface is named volumes they can rely on
/var/athenz/volumes
+ volume-root <- SHA256 hash of target path provided by kubelet
- data.json <- contains pod attributes, not visible inside pod
+ mount <- the directory that is mounted to path
+ connect <- bind mount of agent directory containing host socket
- id <- file containing the mount path hash to be used by pod client as identifier
Listens under /var/athenz/agent
. Supports the following API.
POST /init/OPAQUE-ID
No inputs.
Output
{
"ntoken": "signed principal token",
"keyPEM": "PEM-encoded key",
"certPEM": "PEM-encoded cert",
"caCertPEM": "PEM encoded rootCA bundle"
}
POST /refresh/OPAQUE-ID
Input:
{
"keyPEM": "PEM-encoded key",
"certPEM": "PEM-encoded cert"
}
Output:
{
"ntoken": "signed principal token",
"keyPEM": "PEM-encoded key",
"certPEM": "PEM-encoded cert",
"caCertPEM": "PEM encoded rootCA bundle"
}
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: athenz-test-app
labels:
app: athenz-test-app
spec:
replicas: 1
template:
metadata:
name: athenz-test-app
annotations:
initializer.kubernetes.io/athenz: "true"
labels:
app: athenz-test-app
spec:
serviceAccountName: athenz-test-app
containers:
- name: main
image: local/k8s-athenz-test-app:latest
imagePullPolicy: Never
volumeMounts:
- { name: ntoken, mountPath: /tokens, readOnly: true }
- { name: tlscerts, mountPath: /var/athenz/tls, readOnly: true }
volumes:
- { name: ntoken, emptyDir: {} }
- { name: tlscerts, emptyDir: {} }
apiVersion: v1
kind: Pod
metadata:
annotations:
initializer.kubernetes.io/athenz: "true"
labels:
app: athenz-test-app
name: athenz-test-app-674cf87c7c-nwbmp
spec:
containers:
- args:
- --mode=refresh
- --endpoint=unix:///identity/connect/agent.sock
command:
- /usr/bin/athenz-sia
image: local/k8s-athenz-sia:latest
name: sia-refresh
volumeMounts:
- mountPath: /tokens
name: ntoken
- mountPath: /tls
name: tlscerts
- mountPath: /identity
name: sia-identity
- image: local/k8s-athenz-test-app:latest
imagePullPolicy: Never
name: main
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /tokens
name: ntoken
readOnly: true
- mountPath: /var/athenz/tls
name: tlscerts
readOnly: true
initContainers:
- args:
- --mode=init
- --endpoint=unix:///identity/connect/agent.sock
command:
- /usr/bin/athenz-sia
image: local/k8s-athenz-sia:latest
name: sia-init
volumeMounts:
- mountPath: /tokens
name: ntoken
- mountPath: /tls
name: tlscerts
- mountPath: /identity
name: sia-identity
volumes:
- flexVolume:
driver: athenz.kubernetes.io/athenz-volume-driver
name: sia-identity
- emptyDir: {}
name: ntoken
- emptyDir: {}
name: tlscerts