Skip to content

Instantly share code, notes, and snippets.

@gotwarlost
Last active March 6, 2018 11:09
Show Gist options
  • Save gotwarlost/f46bd4abd59c1bbebcd0b8c3a9851d5b to your computer and use it in GitHub Desktop.
Save gotwarlost/f46bd4abd59c1bbebcd0b8c3a9851d5b to your computer and use it in GitHub Desktop.

Athenz summary

data model

  • Domain maps to k8s namespace (with name mangling)
  • Service maps to k8s service account

Athenz APIs

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.)

Exchange CSR for TLS cert

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"
}

Get tokens and TLS certs using launcher service

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": {}
}

Refresh an identity returned in a previous call

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": {}
}

Provider callback

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

Sequence diagrams

Pod launch

pod launch

Pod startup

pod startup

Identity agent and volume driver setup

Volume driver filesystem for single flex volume

   /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

Identity agent

Listens under /var/athenz/agent. Supports the following API.

Get initial identity

POST /init/OPAQUE-ID

No inputs.

Output

{
	"ntoken": "signed principal token",
	"keyPEM": "PEM-encoded key",
	"certPEM": "PEM-encoded cert",
	"caCertPEM": "PEM encoded rootCA bundle"
}

Refresh identity using prior credentials

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"
}

Input YAML

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: {} }

Initialized YAML

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment