Skip to content

Commit

Permalink
Merge pull request #36 from IBM/vault_auth_check
Browse files Browse the repository at this point in the history
Vault auth plugin and gen-jwt.py addition to check trust chain claims
  • Loading branch information
mrsabath authored May 11, 2020
2 parents 0e71ed0 + 0ce1887 commit 4e879ad
Show file tree
Hide file tree
Showing 51 changed files with 424 additions and 95 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ $ curl http://<Ingress Subdomain or ICP master IP>/
At this point, this is an expected result.

### Setup Cluster Nodes
The following information is required to deploy TSI node-setup helm chart:
* cluster name - name of the cluster. This must correspond to the actual name of the cluster
* cluster region - label associated with the actual region for the data center (e.g. eu-de, dal09, wdc01)
TSI currently supports 2 methods for signing JWT Tokens:
* using TPM2 - private keys are obtained directly from TPM using TPM wrapper (VTPM2)
* using custom signing service JSS (JWT Signing Service)
Expand All @@ -216,7 +219,8 @@ To use vTPM, deploy TSI Node Setup helm charts with all the functions disabled.

Replace X.X.X with a proper version numbers (typically the highest, the most recent).
```console
helm install charts/tsi-node-setup-X.X.X --debug --name tsi-setup --set reset.all=false --set reset.x5c=false
helm install charts/tsi-node-setup-X.X.X --debug --name tsi-setup --set reset.all=false \
--set reset.x5c=false --set cluster.name=CLUSTER_NAME --set cluster.region=CLUSTER_REGION
```

In order to run JSS server, all worker nodes have to be setup with private keys. This operation needs to be executed only once.
Expand All @@ -225,12 +229,14 @@ If you are running this setup for the first time or like to override previous se


```console
helm install charts/tsi-node-setup-X.X.X --debug --name tsi-setup --set reset.all=true
helm install charts/tsi-node-setup-X.X.X --debug --name tsi-setup --set reset.all=true \
--set cluster.name=CLUSTER_NAME --set cluster.region=CLUSTER_REGION
```

To keep the existing private key, but just reset the intermediate CA (`x5c`)
```console
helm install charts/tsi-node-setup-X.X.X --debug --name tsi-setup --set reset.x5c=true
helm install charts/tsi-node-setup-X.X.X --debug --name tsi-setup --set reset.x5c=true \
--set cluster.name=CLUSTER_NAME --set cluster.region=CLUSTER_REGION
```

Once the worker nodes are setup, deploy the TSI environment
Expand All @@ -244,7 +250,7 @@ TI helm charts are included with this repo under [charts/](./charts/) directory.
You can use them directly or use the charts that you built yourself (see instructions below).

The following information is required to deploy TSI helm charts:
* cluster name - name of the cluster. This should correspond to actual name of the cluster
* cluster name - name of the cluster. This must correspond to the actual name of the cluster
* cluster region - label associated with the actual region for the data center (e.g. eu-de, dal09, wdc01)
* vault address - the remote address of the Vault service that contains the TSI secrets to be retrieved by the sidecar. Use the env. variable VAULT_ADDR set [above](./README.md#setup-vault)
* jss service - TSI currently support 2 mechanism for running the JSS (JWT Signing Service):
Expand Down
4 changes: 2 additions & 2 deletions build
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
dep ensure
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o ti-webhook .
if [ $? -ne 0 ]; then exit 1 ; fi
docker build --no-cache -t trustedseriviceidentity/ti-webhook:v1.4 .
docker build --no-cache -t trustedseriviceidentity/ti-webhook:v1.5 .

docker push trustedseriviceidentity/ti-webhook:v1.4
docker push trustedseriviceidentity/ti-webhook:v1.5
2 changes: 1 addition & 1 deletion charts/ti-key-release-1/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v1
description: A Helm chart for deployment of TI-KeyRelease
name: ti-key-release-1
version: 1.4.0
version: 1.5.0
home: https://github.com/IBM/trusted-service-identity
maintainers:
- name: Brandon Lum
Expand Down
2 changes: 1 addition & 1 deletion charts/ti-key-release-1/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace: trusted-identity

# version of ti-components
tiVersion: v1.4
tiVersion: v1.5

# target docker repo
tiRepo: trustedseriviceidentity
Expand Down
Binary file added charts/ti-key-release-2-1.5.0.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion charts/ti-key-release-2/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v1
description: A Helm chart for deployment of TI-KeyRelease
name: ti-key-release-2
version: 1.4.0
version: 1.5.0
home: https://github.com/IBM/trusted-service-identity
maintainers:
- name: Brandon Lum
Expand Down
2 changes: 1 addition & 1 deletion charts/ti-key-release-2/requirements.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
dependencies:
- name: ti-key-release-1
version: 1.4.0
version: 1.5.0
repository: "file://../ti-key-release-1"
6 changes: 3 additions & 3 deletions charts/ti-key-release-2/templates/jss-server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ spec:
volumeMounts:
- mountPath: /host/sockets
name: tsi-sockets
- mountPath: /host/tsi-secure
name: tsi-secure
readOnly: true
{{- end }}

{{- if eq .Values.jssService.type "jss-server" }}
Expand Down Expand Up @@ -78,12 +81,9 @@ spec:
path: /tsi-secure/sockets
# directory might be created
type: DirectoryOrCreate

{{- if eq .Values.jssService.type "jss-server" }}
- name: tsi-secure
hostPath:
# directory location on host
path: /tsi-secure
# directory must exist
type: Directory
{{- end }}
2 changes: 1 addition & 1 deletion charts/ti-key-release-2/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# cannot use the value from `ti-key-release-1`
# parsing problem:
# `error converting YAML to JSON`
tiVersion: v1.4
tiVersion: v1.5

# target docker repo
tiRepo: trustedseriviceidentity
Expand Down
Binary file added charts/tsi-node-setup-1.5.0.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion charts/tsi-node-setup/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v1
appVersion: "1.0"
description: A Helm chart for setting up TSI nodes
name: tsi-node-setup
version: 1.4.0
version: 1.5.0
home: https://github.com/IBM/trusted-service-identity
maintainers:
- name: Brandon Lum
Expand Down
3 changes: 2 additions & 1 deletion charts/tsi-node-setup/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ This Helm chart initializes the TSI nodes.
namespace: {{ .Values.namespace }}
reset.all: {{ .Values.reset.all }}
reset.x5c: {{ .Values.reset.x5c }}

cluster.name: {{ .Values.cluster.name }}
cluster.region: {{ .Values.cluster.region }}

After successful cluster setup, this Helm chart must be deleted:
helm delete --purge --debug {{ .Release.Name }}
4 changes: 4 additions & 0 deletions charts/tsi-node-setup/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ spec:
value: "{{ .Values.reset.x5c }}"
- name: PRIVATEDIR
value: "/host/tsi-secure"
- name: CLUSTER_NAME
value: "{{ .Values.cluster.name }}"
- name: CLUSTER_REGION
value: "{{ .Values.cluster.region }}"
# get the host IP and pass as env. var
- name: HOST_IP
valueFrom:
Expand Down
7 changes: 6 additions & 1 deletion charts/tsi-node-setup/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace: trusted-identity

# version of TSI components
tsiVersion: v1.4
tsiVersion: v1.5

# target docker repo
tsiRepo: trustedseriviceidentity
Expand All @@ -24,3 +24,8 @@ tsiRepo: trustedseriviceidentity
reset:
all: "false"
x5c: "true"

# Cluster Information
cluster:
name: cluster-name
region: dal01
4 changes: 2 additions & 2 deletions components/jss/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ BINARY_NAME_PUB="jss-server-pub"
BINARY_NAME_PRIV="jss-server-priv"
REPO ?= trustedseriviceidentity
IMAGE_PUB := $(REPO)/$(BINARY_NAME_PUB):$(GIT_COMMIT_SHA)
MUTABLE_IMAGE_PUB := $(REPO)/$(BINARY_NAME_PUB):v1.4
MUTABLE_IMAGE_PUB := $(REPO)/$(BINARY_NAME_PUB):v1.5
IMAGE_PRIV := $(REPO)/$(BINARY_NAME_PRIV):$(GIT_COMMIT_SHA)
MUTABLE_IMAGE_PRIV := $(REPO)/$(BINARY_NAME_PRIV):v1.4
MUTABLE_IMAGE_PRIV := $(REPO)/$(BINARY_NAME_PRIV):v1.5

all: docker docker-push timestamp

Expand Down
60 changes: 55 additions & 5 deletions components/jss/gen-jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,63 @@
- issuer(iss) is passed as env. var. (ISS)
- token expiration is passed as env. var (TTL_SEC)
Example:
./gen-jwt.py --aud foo,bar --claimms name:tt|cluster-name:EUcluster|cluster-region:eu-de|images:trustedseriviceidentity/myubuntu@sha256:5b224e11f0,ubuntu:latest private-key.pem
Example to generate TSI JWT:
./gen-jwt.py --aud foo,bar --claims "pod:myubuntu-6756d665bc-gc25f|namespace:test|images-names:ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4|images:30beed0665d9cb4df616cca84ef2c06d2323e02869fcca8bbfbf0d8c5a3987cc|cluster-name:my-cluster-name|cluster-region:eu-de|machineid=fbafad4e9df3498f85a555914e241539" private-key.pem
If claims don't match the x5c, you might need to comment out:
line 130 # payload = check_payload(payload, cc)
"""
import argparse
import time
import os
from os.path import join, exists

from cryptography import x509
from cryptography.hazmat.backends import default_backend

from jwcrypto import jwt, jwk

# obtaind evn. variables:
expire = int(os.getenv('TTL_SEC', 30))
iss = os.getenv('ISS', 'wsched@us.ibm.com')
statedir = os.getenv('STATEDIR', '/host/tsi-secure')

def format_pem_cert(c):
body = ""
for i in xrange(0, len(c), 64):
body += c[i:i+64] + '\n'

return "-----BEGIN CERTIFICATE-----\n{}-----END CERTIFICATE-----".format(body)

def get_cert_claims(x5c):
certClaims = {}
for certData in x5c:
cert = x509.load_pem_x509_certificate(format_pem_cert(certData), default_backend())
for ex in cert.extensions:
if type(ex.value) is not x509.extensions.SubjectAlternativeName:
continue
for uri in ex.value:
f = str(uri.value)
if f.startswith("TSI:") or f.startswith("tsi:"):
try:
sps = f[len("TSI:"):].split(":")
certClaims[sps[0]] = ':'.join(sps[1:])
except:
raise Exception("Invalid TSI URI in x509 alt names")
return certClaims

def check_payload (payload, certClaims):
for k, v in certClaims.items():
if k in payload and payload[k] != v:
return None
else:
payload[k]=v

return payload




def main(args):
"""Generates a signed JSON Web Token from local private key."""

Expand Down Expand Up @@ -78,18 +120,26 @@ def main(args):

# add chain of trust
x5cfile = join(statedir, "x5c")
errMsg = "Error opening/processing x5c file"
if exists(x5cfile):
try:
with open(x5cfile) as x:
# serialize the given x5c as json Sring[]
x5c = x.read().strip()[1:-1].replace('"', '').split(',')
cc = get_cert_claims(x5c)
payload = check_payload(payload, cc)
if payload is None:
errMsg = "Payload claims do not match chain of trust"
raise Exception(errMsg)
token = jwt.JWT(header={"alg": "RS256", "x5c":x5c, "typ": "JWT", "kid": key.key_id},
claims=payload)
token.make_signed_token(key)
return token.serialize()
except:
print "Error opening/processing x5c file"
# using without x5c chain of trust should be disabled
except Exception as e:
# using without x5c chain of trust should be disabled
print e
raise e

raise Exception("System not initialized. Missing x5c file. Abort!")

if __name__ == '__main__':
Expand Down
12 changes: 9 additions & 3 deletions components/jss/gen-jwt.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
#!/usr/bin/env bash

# this script is use for debugging. It's not called by any of the components directly
# assuming 'private.key' and 'x5c' files exist, here is the format to test it:
# This script is used for debugging and manually generating JWT tokens.
# It's not called by any of the components directly
# assuming 'private.key' and 'x5c' files exist, here is the format to test it:
#
# /usr/local/bin/gen-jwt.sh -sub test@test.com -claims "name:tt|cluster-name:EUcluster|cluster-region:eu-de|images:trustedseriviceidentity/myubuntu@sha256:5b224e11f0,ubuntu:latest"
# /usr/local/bin/gen-jwt.sh -sub test@test.com --claims="pod:myubuntu-6756d665bc-gc25f|namespace:test|images-names:ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4|images:30beed0665d9cb4df616cca84ef2c06d2323e02869fcca8bbfbf0d8c5a3987cc|cluster-name:my-cluster-name|cluster-region:eu-de|machineid=fbafad4e9df3498f85a555914e241539"
#
# If your claims don't match the x5c, you might need to comment out in gen-jwt.py:
# line 130 # payload = check_payload(payload, cc)

# TJW expires Jan 2052
export TTL_SEC=999999999

DIR="$(dirname "$0")"
STATEDIR=${STATEDIR:-/host/tsi-secure}
Expand Down
1 change: 1 addition & 0 deletions components/jss/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Flask==1.0.2
cryptography==2.9.2
2 changes: 1 addition & 1 deletion components/jwt-sidecar/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ BUILD_DATE="$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")"
BINARY_NAME="ti-jwt-sidecar"
REPO ?= trustedseriviceidentity
IMAGE := $(REPO)/$(BINARY_NAME):$(GIT_COMMIT_SHA)
MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.4
MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.5

all: docker docker-push timestamp

Expand Down
11 changes: 11 additions & 0 deletions components/jwt-sidecar/execute-get-vault-secrets.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
#!/bin/bash

# For debbugging and manually retrieving the vault secrets:
# VAULT_ADDR is already defined on the sidecar. You can modify the token and
# the role
# curl --request POST --data '{"jwt": "'"$(cat /jwt/token)"'", "role": "'demo-r'"}' "${VAULT_ADDR}"/v1/auth/trusted-identity/login | jq
# using the response above extract the auth.client_token and set as VAULT_TOKEN
# export VAULT_TOKEN=$(echo $RESP | jq -r '.auth.client_token')
# Or, if results are successful, use all-in-one:
# export VAULT_TOKEN=$(curl --request POST --data '{"jwt": "'"$(cat /jwt/token)"'", "role": "'demo-r'"}' "${VAULT_ADDR}"/v1/auth/trusted-identity/login | jq -r '.auth.client_token')
# now ready to retrieve the secret:
# vault kv get -format=json secret/ti-demo-r/eu-de/mysecret4

JWTFILE="/jwt/token"
SECREQFILE="/pod-metadata/tsi-secrets"

Expand Down
10 changes: 3 additions & 7 deletions components/node-setup/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ FROM ubuntu:18.04
RUN apt update && \
apt install -y curl jq vim && \
apt install -y openssl
# apt install -y openssl && \
# apt install -y python python-pip


# we absolutely need python2-cryptography otherwise we may pick up the one from pip
# and this one may cause issues
#RUN apt install -y python-cryptography \
# && pip install jwcrypto
RUN cd /usr/local/bin && \
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \
chmod +x kubectl

COPY init-node.sh /usr/local/bin/

Expand Down
2 changes: 1 addition & 1 deletion components/node-setup/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ BUILD_DATE="$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")"
BINARY_NAME="node-setup"
REPO ?= trustedseriviceidentity
IMAGE := $(REPO)/$(BINARY_NAME):$(GIT_COMMIT_SHA)
MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.4
MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.5

all: docker docker-push timestamp

Expand Down
Loading

0 comments on commit 4e879ad

Please sign in to comment.