From 2d2083eb4035760f1e943c8ee3e715979d6fe121 Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Mon, 4 May 2020 13:44:56 -0400 Subject: [PATCH 01/16] Vault auth plugin addition to check trust chain claims Signed-off-by: Brandon Lum --- examples/vault-plugin/path_login.go | 59 ++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/examples/vault-plugin/path_login.go b/examples/vault-plugin/path_login.go index f1d4c16c..4bd1276f 100644 --- a/examples/vault-plugin/path_login.go +++ b/examples/vault-plugin/path_login.go @@ -5,6 +5,7 @@ import ( "crypto/x509" "errors" "fmt" + "strings" "time" oidc "github.com/coreos/go-oidc" @@ -40,13 +41,40 @@ func pathLogin(b *jwtAuthBackend) *framework.Path { } } +// getCertChainClaimsMap obtains the TSI claims which are part of the CA +// chain used to verify a JWT. This implies that the claims from the claim +// must meet the requirements put forth by the chain of trust. +func getCertChainClaimsMap(certs []*x509.Certificate) (map[string]string, error) { + // It is important to go through every cert and overwrite the map claims + // to prevent an attack where an intermediate CA can be used to sign another + // set of CAs which do not have these extended names. So each attribute + // must be propagated down which can be done by traversing and populating + // the map in reverse order. + claimsMap := map[string]string{} + for _, cert := range certs { + for _, v := range cert.URIs { + if strings.ToUpper(v.Scheme) == "TSI" { + ss := strings.Split(v.Opaque, ":") + if len(ss) < 2 { + return nil, errors.New("TSI alt name is not in correct format") + } + k := ss[0] + v := strings.Join(ss[1:], ":") + claimsMap[k] = v + } + } + } + + return claimsMap, nil +} + // validateCertChain validates the jwt cert chain and returns the public key // that can validate the JWT if verifiable -func validateCertChain(rootCAPEM []byte, jwtToken *jwt.JSONWebToken) (interface{}, error) { +func validateCertChain(rootCAPEM []byte, jwtToken *jwt.JSONWebToken) (interface{}, map[string]string, error) { roots := x509.NewCertPool() ok := roots.AppendCertsFromPEM(rootCAPEM) if !ok { - return nil, fmt.Errorf("Error appending root cert in x509 CertPool") + return nil, nil, fmt.Errorf("Error appending root cert in x509 CertPool") } opts := x509.VerifyOptions{ @@ -57,12 +85,17 @@ func validateCertChain(rootCAPEM []byte, jwtToken *jwt.JSONWebToken) (interface{ for _, h := range jwtToken.Headers { certs, err := h.Certificates(opts) if err == nil && len(certs) > 0 && len(certs[0]) > 0 { + claims, err := getCertChainClaimsMap(certs[0]) + if err != nil { + return nil, nil, err + } + fmt.Printf("Verification Success! %v\n", certs) - return certs[0][0].PublicKey, nil + return certs[0][0].PublicKey, claims, nil } } - return nil, fmt.Errorf("Unable to verify cert chain") + return nil, nil, fmt.Errorf("Unable to verify cert chain") } func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { @@ -110,12 +143,13 @@ func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d claims := jwt.Claims{} var valid bool + errMsg := "" for i, key := range config.JWTValidationPubKeys { var validateKey interface{} // If there is a valid x5c chain, do chain validation and use the // provided CA (first cert of chain as the public key) which acts // as the intermediary. - validateKey, err := validateCertChain([]byte(key), parsedJWT) + validateKey, certClaims, err := validateCertChain([]byte(key), parsedJWT) if err != nil { // If can't validate cert chain, use the rootCA public key fmt.Printf("Couldn't validate cert chain\n") @@ -123,12 +157,25 @@ func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d } if err := parsedJWT.Claims(validateKey, &claims, &allClaims); err == nil { + for k, v := range certClaims { + if vv, ok := allClaims[k]; ok { + if vvString, _ := vv.(string); vvString != v { + errMsg = fmt.Sprintf("Trust chain assertion of field %v failed, expected %v, got %v", + k, v, vvString) + continue + } + } + } + valid = true break } } if !valid { - return logical.ErrorResponse("no known key successfully validated the token signature"), nil + if errMsg != "" { + errMsg = "no known key successfully validated the token signature" + } + return logical.ErrorResponse(errMsg), nil } // We require notbefore or expiry; if only one is provided, we allow 5 minutes of leeway. From 3593dbc048243c8b85bfd948a7acd39271756a5c Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Tue, 5 May 2020 15:50:48 -0400 Subject: [PATCH 02/16] Add checking of alt names in x5c chain to validate payload Signed-off-by: Brandon Lum --- components/jss/gen-jwt.py | 51 +++++++++++++++++++++++++++++++-- components/jss/requirements.txt | 1 + 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/components/jss/gen-jwt.py b/components/jss/gen-jwt.py index 56d896dc..5cb6b378 100755 --- a/components/jss/gen-jwt.py +++ b/components/jss/gen-jwt.py @@ -27,6 +27,9 @@ 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: @@ -34,6 +37,40 @@ 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: + 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.""" @@ -78,18 +115,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__': diff --git a/components/jss/requirements.txt b/components/jss/requirements.txt index f2e1e506..36d26b87 100644 --- a/components/jss/requirements.txt +++ b/components/jss/requirements.txt @@ -1 +1,2 @@ Flask==1.0.2 +cryptography==2.9.2 From c6fbef138f710d4cdd81045ccbc3d66fd152b363 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Wed, 6 May 2020 00:57:55 +0200 Subject: [PATCH 03/16] Updated version --- build | 4 ++-- charts/ti-key-release-1/Chart.yaml | 2 +- charts/ti-key-release-1/values.yaml | 2 +- charts/ti-key-release-2/Chart.yaml | 2 +- charts/ti-key-release-2/requirements.yaml | 2 +- charts/ti-key-release-2/values.yaml | 2 +- charts/tsi-node-setup/Chart.yaml | 2 +- components/jss/Makefile | 4 ++-- components/jwt-sidecar/Makefile | 2 +- components/node-setup/Makefile | 2 +- components/vtpm2-server/Makefile | 2 +- components/vtpm2-server/vtpm2-server.yaml | 2 +- deployment/configmap.yaml | 2 +- deployment/deployment.yaml | 2 +- makefile | 2 +- tests/ConfigFile.yaml | 2 +- tests/ExpectAddContainer.json | 2 +- tests/ExpectMutateInit.json | 2 +- tests/ExpectTsiMutateConfig.json | 2 +- tests/ExpectUpdateAnnotation2.json | 2 +- tests/FakeAddContainer.json | 2 +- tests/FakeAdmissionResponse.json | 4 ++-- tests/FakeIsSafeUpdateError.json | 4 ++-- tests/FakeIsSafeUpdateOK.json | 4 ++-- tests/FakeTsiMutateConfig.json | 4 ++-- tests/FakeUpdateAnnotation2.json | 2 +- 26 files changed, 32 insertions(+), 32 deletions(-) diff --git a/build b/build index 7cb651ab..30fdab8f 100755 --- a/build +++ b/build @@ -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 diff --git a/charts/ti-key-release-1/Chart.yaml b/charts/ti-key-release-1/Chart.yaml index bd847b57..9a821692 100644 --- a/charts/ti-key-release-1/Chart.yaml +++ b/charts/ti-key-release-1/Chart.yaml @@ -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 diff --git a/charts/ti-key-release-1/values.yaml b/charts/ti-key-release-1/values.yaml index 22f5edaf..024a4167 100644 --- a/charts/ti-key-release-1/values.yaml +++ b/charts/ti-key-release-1/values.yaml @@ -6,7 +6,7 @@ namespace: trusted-identity # version of ti-components -tiVersion: v1.4 +tiVersion: v1.5 # target docker repo tiRepo: trustedseriviceidentity diff --git a/charts/ti-key-release-2/Chart.yaml b/charts/ti-key-release-2/Chart.yaml index 40a13f38..d4b04abe 100644 --- a/charts/ti-key-release-2/Chart.yaml +++ b/charts/ti-key-release-2/Chart.yaml @@ -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 diff --git a/charts/ti-key-release-2/requirements.yaml b/charts/ti-key-release-2/requirements.yaml index b3653412..b83dbf48 100644 --- a/charts/ti-key-release-2/requirements.yaml +++ b/charts/ti-key-release-2/requirements.yaml @@ -1,4 +1,4 @@ dependencies: - name: ti-key-release-1 - version: 1.4.0 + version: 1.5.0 repository: "file://../ti-key-release-1" diff --git a/charts/ti-key-release-2/values.yaml b/charts/ti-key-release-2/values.yaml index fae179ef..dda8199b 100644 --- a/charts/ti-key-release-2/values.yaml +++ b/charts/ti-key-release-2/values.yaml @@ -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 diff --git a/charts/tsi-node-setup/Chart.yaml b/charts/tsi-node-setup/Chart.yaml index 70089dd0..3c122922 100644 --- a/charts/tsi-node-setup/Chart.yaml +++ b/charts/tsi-node-setup/Chart.yaml @@ -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 diff --git a/components/jss/Makefile b/components/jss/Makefile index fb628a51..d7e53ff3 100644 --- a/components/jss/Makefile +++ b/components/jss/Makefile @@ -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 diff --git a/components/jwt-sidecar/Makefile b/components/jwt-sidecar/Makefile index 05a74a40..f90d54fe 100644 --- a/components/jwt-sidecar/Makefile +++ b/components/jwt-sidecar/Makefile @@ -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 diff --git a/components/node-setup/Makefile b/components/node-setup/Makefile index acaf0108..bb632cca 100644 --- a/components/node-setup/Makefile +++ b/components/node-setup/Makefile @@ -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 diff --git a/components/vtpm2-server/Makefile b/components/vtpm2-server/Makefile index c7df8b51..1a39267c 100755 --- a/components/vtpm2-server/Makefile +++ b/components/vtpm2-server/Makefile @@ -4,7 +4,7 @@ BUILD_DATE="$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")" BINARY_NAME="vtpm2-server" 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 diff --git a/components/vtpm2-server/vtpm2-server.yaml b/components/vtpm2-server/vtpm2-server.yaml index 8481d057..7fa59730 100644 --- a/components/vtpm2-server/vtpm2-server.yaml +++ b/components/vtpm2-server/vtpm2-server.yaml @@ -15,7 +15,7 @@ spec: containers: - name: vtpm2-server-pub #image: {{ .Values.tiRepo }}/{{ index .Values "ti-key-release-1" "jssService" "type" }}:{{ .Values.tiVersion }} - image: trustedseriviceidentity/vtpm2-server:v1.4 + image: trustedseriviceidentity/vtpm2-server:v1.5 imagePullPolicy: Always env: - name: ISS diff --git a/deployment/configmap.yaml b/deployment/configmap.yaml index dd4ea4c3..b42df766 100644 --- a/deployment/configmap.yaml +++ b/deployment/configmap.yaml @@ -6,7 +6,7 @@ data: tsiMutateConfig.yaml: | initContainers: - name: gen-vault-cert - image: trustedseriviceidentity/ti-gen-vault-cert:v1.4 + image: trustedseriviceidentity/ti-gen-vault-cert:v1.5 imagePullSecrets: - name: regcred imagePullPolicy: Always diff --git a/deployment/deployment.yaml b/deployment/deployment.yaml index 4ebcdf27..10398cd9 100644 --- a/deployment/deployment.yaml +++ b/deployment/deployment.yaml @@ -17,7 +17,7 @@ spec: serviceAccountName: ti-sa containers: - name: tsi-mutate - image: trustedseriviceidentity/ti-webhook:v1.4 + image: trustedseriviceidentity/ti-webhook:v1.5 imagePullPolicy: Always args: - -tsiMutateConfigFile=/etc/webhook/config/tsiMutateConfig.yaml diff --git a/makefile b/makefile index dd8d3d1d..895b300a 100644 --- a/makefile +++ b/makefile @@ -7,7 +7,7 @@ BUILD_DATE=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ") BINARY_NAME=ti-webhook REPO ?= trustedseriviceidentity IMAGE := $(REPO)/$(BINARY_NAME):$(GIT_COMMIT_SHA) -MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.4 +MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.5 GOARCH=$(shell go env GOARCH) .PHONY: all fast test-deps build-deps fmt vet lint get-deps test build docker docker-push dep diff --git a/tests/ConfigFile.yaml b/tests/ConfigFile.yaml index e26831d8..5f8e49ab 100644 --- a/tests/ConfigFile.yaml +++ b/tests/ConfigFile.yaml @@ -1,6 +1,6 @@ sidecarContainers: - name: jwt-sidecar - image: trustedseriviceidentity/ti-jwt-sidecar:v1.4 + image: trustedseriviceidentity/ti-jwt-sidecar:v1.5 imagePullPolicy: Always env: - name: HOST_IP diff --git a/tests/ExpectAddContainer.json b/tests/ExpectAddContainer.json index 97ae2cf2..0a231d78 100644 --- a/tests/ExpectAddContainer.json +++ b/tests/ExpectAddContainer.json @@ -4,7 +4,7 @@ "path": "/spec/containers/-", "value": { "name": "jwt-sidecar", - "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.5", "env": [ { "name": "HOST_IP", diff --git a/tests/ExpectMutateInit.json b/tests/ExpectMutateInit.json index fe5e9055..ff3021e6 100644 --- a/tests/ExpectMutateInit.json +++ b/tests/ExpectMutateInit.json @@ -3,7 +3,7 @@ "SidecarContainers": [ { "name": "jwt-sidecar", - "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.5", "env": [ { "name": "HOST_IP", diff --git a/tests/ExpectTsiMutateConfig.json b/tests/ExpectTsiMutateConfig.json index d883e121..9d44d3e1 100644 --- a/tests/ExpectTsiMutateConfig.json +++ b/tests/ExpectTsiMutateConfig.json @@ -3,7 +3,7 @@ "SidecarContainers": [ { "name": "jwt-sidecar", - "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.5", "env": [ { "name": "HOST_IP", diff --git a/tests/ExpectUpdateAnnotation2.json b/tests/ExpectUpdateAnnotation2.json index 5cb34f92..e38ee19e 100644 --- a/tests/ExpectUpdateAnnotation2.json +++ b/tests/ExpectUpdateAnnotation2.json @@ -7,7 +7,7 @@ "admission.trusted.identity/status": "mutated", "admission.trusted.identity/ti-cluster-name": "ti-test1", "admission.trusted.identity/ti-cluster-region": "eu-de", - "admission.trusted.identity/ti-images": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4,trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "admission.trusted.identity/ti-images": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4,trustedseriviceidentity/ti-jwt-sidecar:v1.5", "kubernetes.io/psp": "ibm-privileged-psp", "tsi.secrets": "- tsi.secret/name: \"mysecret1\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/ti-demo-all\"\n tsi.secret/local-path: \"mysecrets/myubuntu-mysecret1\"\n- tsi.secret/name: \"mysecret2.json\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/ti-demo-r\"\n tsi.secret/local-path: \"mysecrets/myubuntu-mysecret2\"\n- tsi.secret/name: \"password\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/ti-demo-r\"\n tsi.secret/local-path: \"mysecrets/myubuntu-passwords\"\n- tsi.secret/name: \"invalid\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/ti-demo-all\"\n tsi.secret/local-path: \"mysecrets/myubuntu-invalid\"\n- tsi.secret/name: \"non-existing\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/nothing\"\n tsi.secret/local-path: \"mysecrets/non-existing\"\n" } diff --git a/tests/FakeAddContainer.json b/tests/FakeAddContainer.json index 848728d5..18eea7bf 100644 --- a/tests/FakeAddContainer.json +++ b/tests/FakeAddContainer.json @@ -1,7 +1,7 @@ [ { "name": "jwt-sidecar", - "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.5", "env": [ { "name": "HOST_IP", diff --git a/tests/FakeAdmissionResponse.json b/tests/FakeAdmissionResponse.json index 03154967..b34fed98 100644 --- a/tests/FakeAdmissionResponse.json +++ b/tests/FakeAdmissionResponse.json @@ -55,7 +55,7 @@ "value":[ { "name":"gen-vault-cert", - "image":"trustedseriviceidentity/ti-gen-vault-cert:v1.4", + "image":"trustedseriviceidentity/ti-gen-vault-cert:v1.5", "resources":{ }, @@ -101,7 +101,7 @@ "path":"/spec/containers/-", "value":{ "name":"jwt-sidecar", - "image":"trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "image":"trustedseriviceidentity/ti-jwt-sidecar:v1.5", "command":[ "/bin/sh", "-c", diff --git a/tests/FakeIsSafeUpdateError.json b/tests/FakeIsSafeUpdateError.json index cb69e9ba..58ab006a 100644 --- a/tests/FakeIsSafeUpdateError.json +++ b/tests/FakeIsSafeUpdateError.json @@ -17,7 +17,7 @@ "admission.trusted.identity/status": "mutated", "admission.trusted.identity/ti-cluster-name": "ti-test1", "admission.trusted.identity/ti-cluster-region": "eu-de", - "admission.trusted.identity/ti-images": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4,trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "admission.trusted.identity/ti-images": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4,trustedseriviceidentity/ti-jwt-sidecar:v1.5", "kubernetes.io/psp": "ibm-privileged-psp", "tsi.secrets": "- tsi.secret/name: \"mysecretxx1\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/ti-demo-all\"\n tsi.secret/local-path: \"mysecrets/myubuntu-mysecret1\"\n- tsi.secret/name: \"mysecret2.json\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/ti-demo-r\"\n tsi.secret/local-path: \"mysecrets/myubuntu-mysecret2\"\n- tsi.secret/name: \"password\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/ti-demo-r\"\n tsi.secret/local-path: \"mysecrets/myubuntu-passwords\"\n- tsi.secret/name: \"invalid\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/ti-demo-all\"\n tsi.secret/local-path: \"mysecrets/myubuntu-invalid\"\n- tsi.secret/name: \"non-existing\"\n tsi.secret/role: \"demo\"\n tsi.secret/vault-path: \"secret/nothing\"\n tsi.secret/local-path: \"mysecrets/non-existing\"\n" }, @@ -267,7 +267,7 @@ "lastState": {}, "ready": true, "restartCount": 0, - "image": "docker.io/trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "image": "docker.io/trustedseriviceidentity/ti-jwt-sidecar:v1.5", "imageID": "docker.io/trustedseriviceidentity/ti-jwt-sidecar@sha256:2991b88e7e378112c72ddc4f2ac8e98c0b438b4d4b8d8556be2b5904428434db", "containerID": "containerd://a73da0404c484e906109732a5f0b4e0f38d2fc41c0d09112b23a56d4fd793e26" }, diff --git a/tests/FakeIsSafeUpdateOK.json b/tests/FakeIsSafeUpdateOK.json index 4cde311d..2e106b00 100644 --- a/tests/FakeIsSafeUpdateOK.json +++ b/tests/FakeIsSafeUpdateOK.json @@ -148,7 +148,7 @@ }, { "name": "jwt-sidecar", - "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.5", "env": [ { "name": "HOST_IP", @@ -267,7 +267,7 @@ "lastState": {}, "ready": true, "restartCount": 0, - "image": "docker.io/trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "image": "docker.io/trustedseriviceidentity/ti-jwt-sidecar:v1.5", "imageID": "docker.io/trustedseriviceidentity/ti-jwt-sidecar@sha256:2991b88e7e378112c72ddc4f2ac8e98c0b438b4d4b8d8556be2b5904428434db", "containerID": "containerd://a73da0404c484e906109732a5f0b4e0f38d2fc41c0d09112b23a56d4fd793e26" }, diff --git a/tests/FakeTsiMutateConfig.json b/tests/FakeTsiMutateConfig.json index eb6db997..f8f25c4d 100644 --- a/tests/FakeTsiMutateConfig.json +++ b/tests/FakeTsiMutateConfig.json @@ -2,7 +2,7 @@ "InitContainers": [ { "name": "gen-vault-cert", - "image": "trustedseriviceidentity/ti-gen-vault-cert:v1.4", + "image": "trustedseriviceidentity/ti-gen-vault-cert:v1.5", "resources": {}, "volumeMounts": [ { @@ -43,7 +43,7 @@ "SidecarContainers": [ { "name": "jwt-sidecar", - "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.4", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.5", "command": [ "/bin/sh", "-c", diff --git a/tests/FakeUpdateAnnotation2.json b/tests/FakeUpdateAnnotation2.json index 74dfa942..28d11157 100644 --- a/tests/FakeUpdateAnnotation2.json +++ b/tests/FakeUpdateAnnotation2.json @@ -2,5 +2,5 @@ "admission.trusted.identity/status": "mutated", "admission.trusted.identity/ti-cluster-name": "ti-test1", "admission.trusted.identity/ti-cluster-region": "eu-de", - "admission.trusted.identity/ti-images": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4,trustedseriviceidentity/ti-jwt-sidecar:v1.4" + "admission.trusted.identity/ti-images": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4,trustedseriviceidentity/ti-jwt-sidecar:v1.5" } From da4f63c311b43c9231715b5767f017cd53714854 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Wed, 6 May 2020 00:59:31 +0200 Subject: [PATCH 04/16] Extended SCR with cluster-name and cluster-region --- charts/tsi-node-setup/templates/NOTES.txt | 3 +- .../tsi-node-setup/templates/daemonset.yaml | 4 +++ charts/tsi-node-setup/values.yaml | 7 ++++- components/node-setup/Dockerfile | 10 ++----- components/node-setup/init-node.sh | 29 +++++++++++++++++-- examples/vault-plugin/demo.registerJSS.sh | 22 ++++++++++++-- 6 files changed, 60 insertions(+), 15 deletions(-) diff --git a/charts/tsi-node-setup/templates/NOTES.txt b/charts/tsi-node-setup/templates/NOTES.txt index 746d7e51..16b04e44 100644 --- a/charts/tsi-node-setup/templates/NOTES.txt +++ b/charts/tsi-node-setup/templates/NOTES.txt @@ -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 }} diff --git a/charts/tsi-node-setup/templates/daemonset.yaml b/charts/tsi-node-setup/templates/daemonset.yaml index 07f787bb..b404a44e 100644 --- a/charts/tsi-node-setup/templates/daemonset.yaml +++ b/charts/tsi-node-setup/templates/daemonset.yaml @@ -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: diff --git a/charts/tsi-node-setup/values.yaml b/charts/tsi-node-setup/values.yaml index 6c37f043..d361ef0a 100644 --- a/charts/tsi-node-setup/values.yaml +++ b/charts/tsi-node-setup/values.yaml @@ -6,7 +6,7 @@ namespace: trusted-identity # version of TSI components -tsiVersion: v1.4 +tsiVersion: v1.5 # target docker repo tsiRepo: trustedseriviceidentity @@ -24,3 +24,8 @@ tsiRepo: trustedseriviceidentity reset: all: "false" x5c: "true" + +# Cluster Information +cluster: + name: cluster-name + region: dal01 diff --git a/components/node-setup/Dockerfile b/components/node-setup/Dockerfile index 32d4d845..1e17c5bc 100644 --- a/components/node-setup/Dockerfile +++ b/components/node-setup/Dockerfile @@ -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/ diff --git a/components/node-setup/init-node.sh b/components/node-setup/init-node.sh index 2aa7789a..11b13627 100755 --- a/components/node-setup/init-node.sh +++ b/components/node-setup/init-node.sh @@ -49,16 +49,39 @@ else logme "${PRIVATEDIR}/sockets/app.sock removed" fi + # create a private key if [ "$RESETALL" == "true" ]; then if ! [ -f "${PRIV_KEY}" ]; then - openssl genrsa -out "${PRIV_KEY}" 2048 - openssl req -new -sha256 -key "${PRIV_KEY}" -out "${SERV_CSR}" -subj "/CN=jss-jwt-server" - logme "private key ${PRIV_KEY} and ${SERV_CSR} created" + + if [ "$CLUSTER_REGION" == "" ] || [ "$CLUSTER_NAME" == "" ]; then + logme "Env. variables CLUSTER_REGION and CLUSTER_NAME must be set. Terminating..." + else + cat > "openssl.cnf" << EOF +[req] +req_extensions = v3_req + +[v3_req] +subjectAltName= @alt_names + +[alt_names] +URI.1 = TSI:cluster-name:$CLUSTER_NAME +URI.2 = TSI:cluster-region:$CLUSTER_REGION +# URI.3 = TSI:datacenter:fra02 +EOF + + openssl genrsa -out "${PRIV_KEY}" 2048 + openssl req -new -sha256 -key "${PRIV_KEY}" -out "${SERV_CSR}" -subj "/CN=jss-jwt-server" -reqexts v3_req -config <(cat /etc/ssl/openssl.cnf openssl.cnf) + logme "private key ${PRIV_KEY} and ${SERV_CSR} created" + logme "for cluster-name: $CLUSTER_NAME and cluster-region:$CLUSTER_REGION" + fi + else logme "private key ${PRIV_KEY} and ${SERV_CSR} already exist. Do nothing" fi fi + + # end of the audit log logme "end of audit record" diff --git a/examples/vault-plugin/demo.registerJSS.sh b/examples/vault-plugin/demo.registerJSS.sh index ec6ee88a..acebcdee 100755 --- a/examples/vault-plugin/demo.registerJSS.sh +++ b/examples/vault-plugin/demo.registerJSS.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -x ## create help menu: helpme() @@ -35,6 +35,21 @@ register() exit 1 fi + # extract the X509v3 TSI fields: + TSIEXT=$1.csr.tsi + openssl req -in "${CSR}" -noout -text |grep "URI:TSI" > $TSIEXT + RT=$? + if [ $RT -ne 0 ] ; then + echo "Missing x509v3 URI:TSI extensions for cluter-name and cluster-region" + rm "${TSIEXT}" + exit 1 + fi + + # format: + # URI:TSI:cluster-name:my-cluster-name, URI:TSI:cluster-region:eu-de + # remove the "URI:" prefix and leading spaces + TSI_URI=$(cat $TSIEXT | sed 's/URI://g' | sed 's/ //g') + # echo "Root Token: ${ROOT_TOKEN}" vault login -no-print ${ROOT_TOKEN} RT=$? @@ -48,13 +63,14 @@ register() X5C="$1.x5c" # create an intermedate certificate for 50 years - vault write pki/root/sign-intermediate csr=@$CSR format=pem_bundle ttl=438000h -format=json > out + vault write pki/root/sign-intermediate csr=@$CSR format=pem_bundle ttl=438000h uri_sans="$TSI_URI" -format=json > out CERT=$(cat out | jq -r '.["data"].certificate' | grep -v '\-\-\-') CHAIN=$(cat out | jq -r '.["data"].issuing_ca' | grep -v '\-\-\-') echo "[\"${CERT}\",\"${CHAIN}\"]" > "$X5C" # cleanup CSR rm "${CSR}" + rm "${TSIEXT}" # cat "$X5C" # copy the x5c file to the setup pod: @@ -79,7 +95,7 @@ register() # ["MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVM # ... # H0aBsXBTWVU+4=","MIIE+zCC....wCW/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd"] - } +} if [ ! "$1" == "" ] ; then export ROOT_TOKEN=$1 From 27b72009bd6c36fd7dec948d130a6538557bf0d7 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Wed, 6 May 2020 13:23:39 +0200 Subject: [PATCH 05/16] Update init-node.sh --- components/node-setup/init-node.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/node-setup/init-node.sh b/components/node-setup/init-node.sh index 11b13627..e3e67a95 100755 --- a/components/node-setup/init-node.sh +++ b/components/node-setup/init-node.sh @@ -67,6 +67,11 @@ subjectAltName= @alt_names [alt_names] URI.1 = TSI:cluster-name:$CLUSTER_NAME URI.2 = TSI:cluster-region:$CLUSTER_REGION +# To assert additional claims about this intermediate CA +# add new lines in the following format: +# URI.x = TSI: +# where x is a next sequencial number and claim is +# a `key:value` pair. For example: # URI.3 = TSI:datacenter:fra02 EOF From 74f717fa1acf0046be65414dd56a98001fbe9052 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Wed, 6 May 2020 14:24:54 +0200 Subject: [PATCH 06/16] Always create openssl.cnf on worker host Both JSS and VTPM need openssl.cnf file to setup node identity in CA --- .../templates/jss-server.yaml | 6 +- components/node-setup/init-node.sh | 74 ++++++++++--------- components/vtpm2-server/Dockerfile | 2 +- 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/charts/ti-key-release-2/templates/jss-server.yaml b/charts/ti-key-release-2/templates/jss-server.yaml index fe60dcba..20cfc861 100644 --- a/charts/ti-key-release-2/templates/jss-server.yaml +++ b/charts/ti-key-release-2/templates/jss-server.yaml @@ -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" }} @@ -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 }} diff --git a/components/node-setup/init-node.sh b/components/node-setup/init-node.sh index e3e67a95..9bb4a9e7 100755 --- a/components/node-setup/init-node.sh +++ b/components/node-setup/init-node.sh @@ -9,6 +9,7 @@ AUDIT_LOG=/host/logs/tsi-audit.log PRIV_KEY=${PRIVATEDIR}/private.key SERV_CSR=${PRIVATEDIR}/server.csr X5C=${PRIVATEDIR}/x5c +SSLCONF=${PRIVATEDIR}/openssl.cnf # a handy function to format the audit log logme() { @@ -17,6 +18,16 @@ now=$(date +"%Y-%m-%d.%H:%M:%S") echo "$now,$1" >> ${AUDIT_LOG} } +# this function ends the operation and waits forever, so the container is not +# recreated +end() { + # end of the audit log + logme "end of audit record. Waiting forever now..." + + # keep the pod running + tail -f /dev/null +} + # output some info about the pod processing these operations logme "pod $HOSTNAME connected to host $(cat /host/etc/hostname) machineid: $(cat /host/etc/machine-id)" @@ -29,6 +40,31 @@ if [ "$RESETALL" == "true" ]; then logme "full reset performed" fi +# openssl.cnf contains cluster idenity information and must be always created +# even if private keys are not created (e.g. to be used by VTPM2) +if [ "$CLUSTER_REGION" == "" ] || [ "$CLUSTER_NAME" == "" ]; then + logme "Env. variables CLUSTER_REGION and CLUSTER_NAME must be set. Terminating..." + end +else + cat > ${SSLCONF} << EOF +[req] +req_extensions = v3_req + +[v3_req] +subjectAltName= @alt_names + +[alt_names] +URI.1 = TSI:cluster-name:$CLUSTER_NAME +URI.2 = TSI:cluster-region:$CLUSTER_REGION +# To assert additional claims about this intermediate CA +# add new lines in the following format: +# URI.x = TSI: +# where x is a next sequencial number and claim is +# a `key:value` pair. For example: +# URI.3 = TSI:datacenter:fra02 +EOF +fi + # reset the private key and all the other secure stuff if [ "$RESETX5C" == "true" ]; then rm "${X5C}" @@ -53,42 +89,12 @@ fi # create a private key if [ "$RESETALL" == "true" ]; then if ! [ -f "${PRIV_KEY}" ]; then - - if [ "$CLUSTER_REGION" == "" ] || [ "$CLUSTER_NAME" == "" ]; then - logme "Env. variables CLUSTER_REGION and CLUSTER_NAME must be set. Terminating..." - else - cat > "openssl.cnf" << EOF -[req] -req_extensions = v3_req - -[v3_req] -subjectAltName= @alt_names - -[alt_names] -URI.1 = TSI:cluster-name:$CLUSTER_NAME -URI.2 = TSI:cluster-region:$CLUSTER_REGION -# To assert additional claims about this intermediate CA -# add new lines in the following format: -# URI.x = TSI: -# where x is a next sequencial number and claim is -# a `key:value` pair. For example: -# URI.3 = TSI:datacenter:fra02 -EOF - openssl genrsa -out "${PRIV_KEY}" 2048 openssl req -new -sha256 -key "${PRIV_KEY}" -out "${SERV_CSR}" -subj "/CN=jss-jwt-server" -reqexts v3_req -config <(cat /etc/ssl/openssl.cnf openssl.cnf) logme "private key ${PRIV_KEY} and ${SERV_CSR} created" logme "for cluster-name: $CLUSTER_NAME and cluster-region:$CLUSTER_REGION" - fi - - else - logme "private key ${PRIV_KEY} and ${SERV_CSR} already exist. Do nothing" - fi + else + logme "private key ${PRIV_KEY} and ${SERV_CSR} already exist. Do nothing" + fi fi - - -# end of the audit log -logme "end of audit record" - -# keep the pod running -tail -f /dev/null +end diff --git a/components/vtpm2-server/Dockerfile b/components/vtpm2-server/Dockerfile index 83d1a7bd..711717ce 100644 --- a/components/vtpm2-server/Dockerfile +++ b/components/vtpm2-server/Dockerfile @@ -5,7 +5,7 @@ RUN dnf -y install python swtpm procps-ng uwsgi uwsgi-plugin-python2 RUN dnf -y install git openssl-devel automake autoconf libtool make file \ && git clone https://git.code.sf.net/p/ibmtpm20tss/tss ibmtpm20tss-tss \ && cd ibmtpm20tss-tss \ - && git checkout tags/v1.4.0 \ + && git checkout tags/v1.3.0 \ && autoreconf -i \ && ./configure --prefix=/usr --disable-tpm-1.2 \ && make -j$(nproc) \ From df088d6076734fe595fe80aa105b57f84c966f24 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Wed, 6 May 2020 19:23:18 +0200 Subject: [PATCH 07/16] Added extended CA values to VTPM2 --- components/node-setup/init-node.sh | 2 +- components/vtpm2-server/Dockerfile | 4 +++- components/vtpm2-server/startup_tpm.sh | 8 +++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/components/node-setup/init-node.sh b/components/node-setup/init-node.sh index 9bb4a9e7..b3acefcb 100755 --- a/components/node-setup/init-node.sh +++ b/components/node-setup/init-node.sh @@ -90,7 +90,7 @@ fi if [ "$RESETALL" == "true" ]; then if ! [ -f "${PRIV_KEY}" ]; then openssl genrsa -out "${PRIV_KEY}" 2048 - openssl req -new -sha256 -key "${PRIV_KEY}" -out "${SERV_CSR}" -subj "/CN=jss-jwt-server" -reqexts v3_req -config <(cat /etc/ssl/openssl.cnf openssl.cnf) + openssl req -new -sha256 -key "${PRIV_KEY}" -out "${SERV_CSR}" -subj "/CN=jss-jwt-server" -reqexts v3_req -config <(cat /etc/ssl/openssl.cnf ${SSLCONF}) logme "private key ${PRIV_KEY} and ${SERV_CSR} created" logme "for cluster-name: $CLUSTER_NAME and cluster-region:$CLUSTER_REGION" else diff --git a/components/vtpm2-server/Dockerfile b/components/vtpm2-server/Dockerfile index 711717ce..5e36856b 100644 --- a/components/vtpm2-server/Dockerfile +++ b/components/vtpm2-server/Dockerfile @@ -36,7 +36,9 @@ RUN dnf -y install python2-cryptography \ COPY gen-jwt.sh EngineJWK.py gen-jwt.py run-tpm-server.sh startup_tpm.sh validate-jwt.py /usr/local/bin/ -RUN jwt=$(gen-jwt.sh \ +RUN jwt=$(mkdir -p /host/tsi-secure/ \ + && touch /host/tsi-secure/openssl.cnf \ + && gen-jwt.sh \ --iss example-issuer \ --aud foo,bar \ --claims=email:foo@google.com,dead:beef) \ diff --git a/components/vtpm2-server/startup_tpm.sh b/components/vtpm2-server/startup_tpm.sh index bb7146a3..5fe7af06 100644 --- a/components/vtpm2-server/startup_tpm.sh +++ b/components/vtpm2-server/startup_tpm.sh @@ -29,6 +29,12 @@ else fi fi +OPENSSLCNF="/host/tsi-secure/openssl.cnf" +if ! [ -f "${OPENSSLCNF}" ]; then + echo "Missing ${OPENSSLCNF} file with node identity" + exit 1 +fi + TPMKEYFILE="${STATEDIR}/tpm.key" if ! [ -f "${TPMKEYFILE}" ]; then # Check whether a key is already there at 'our' index @@ -49,5 +55,5 @@ if ! [ -f "${TPMKEYFILE}" ]; then echo -n "${TPMKEYURI}" > "${STATEDIR}/tpmkeyurl" openssl rsa -inform engine -engine tpm2 -pubout -in "${TPMKEYFILE}" -out "${STATEDIR}/tpmpubkey.pem" - openssl req -engine tpm2 -new -key "${TPMKEYFILE}" -keyform engine -subj "/CN=vtpm2-jwt-server" -out "${STATEDIR}/server.csr" + openssl req -engine tpm2 -new -key "${TPMKEYFILE}" -keyform engine -subj "/CN=vtpm2-jwt-server" -out "${STATEDIR}/server.csr -reqexts v3_req -config <(cat /etc/ssl/openssl.cnf ${OPENSSLCNF})" fi From ab8108dd0b13db50f257f059d9771a77db97a1ca Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Wed, 6 May 2020 14:12:10 -0400 Subject: [PATCH 08/16] add check only to iterate on alt sub name ext Signed-off-by: Brandon Lum --- components/jss/gen-jwt.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/jss/gen-jwt.py b/components/jss/gen-jwt.py index 5cb6b378..783a39aa 100755 --- a/components/jss/gen-jwt.py +++ b/components/jss/gen-jwt.py @@ -49,6 +49,8 @@ def get_cert_claims(x5c): 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:"): From 34ce10fbad850cb8aeb52f2e8d9828c76858a84a Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Wed, 6 May 2020 23:08:37 +0200 Subject: [PATCH 09/16] Updated Helm charts --- charts/ti-key-release-2-1.5.0.tgz | Bin 0 -> 7024 bytes charts/tsi-node-setup-1.5.0.tgz | Bin 0 -> 2312 bytes examples/vault-plugin/demo.registerJSS.sh | 2 +- go.mod | 2 +- go.sum | 4 ++-- 5 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 charts/ti-key-release-2-1.5.0.tgz create mode 100644 charts/tsi-node-setup-1.5.0.tgz diff --git a/charts/ti-key-release-2-1.5.0.tgz b/charts/ti-key-release-2-1.5.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..9f13007f1cbed9a9bac7ebe7191cdd5b91b5bd83 GIT binary patch literal 7024 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 O0RR7~4yeTdyZ`_~T>{Vm literal 0 HcmV?d00001 diff --git a/charts/tsi-node-setup-1.5.0.tgz b/charts/tsi-node-setup-1.5.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..b2518c8a97d61fd7beff24c7d42c13507afb19a3 GIT binary patch literal 2312 zcmV+j3HSCNiwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PI?CQ{y<&pV#^n{Up!U?Q#<*0RmO7+Nx!NyQPi+ig0sxucj2c z9cz&#uOtUDGu&stlH@qHNeDZ`F}GFGizJp>-9NR|f7G$jtS4lQJ&mTw`^PC&CYaNV zzj?Vf7z_r7qmla_3%JEeQuJV*n&6Ic4hl0l zL)VMX{`TOF%ida;=&> zMitnpAxW1`dL1azgLU?KU|H?Z17ne7u)>q6oE43ci@`y|{It&ArJAZEQ@9k)y%bIvMX^eFyYpFZ_>V8*i0MQ@wU znR$*s+M`CJ6hqkQKRggiMNaQ?8~&|Va7SJyKH9pH0V(Nv5M#o`I>*pk61k)^@C65V zI_t)KIYXsbjPTiJWg>!uJ@=Dk<047eJ#>0YW+-C!+rvEev7J^jl7jO&{8muT5{9w$ zVQv8d`3HLLj^0=qb}rrQ>LiFT8QEooEBEUA>b z7_1b#pjk`sEX{Mh2nkZ<1SGGeY|31Dsz5Z}<^zL*stk%8pX_nWge?7arcs|qWdf92s>B1C^ zugOVm;OxrfBRSQsxQJpFz>KPuSbn)2-+VZ`S_R@SN@ihe0DOvkjmhJYa_owlGz4v^ zDRf}*G`P=dQP6RjAe}|*i|Npy#{n7US|5s_P|NnjJH*VxJEAqdU$;~qoy>GqRZELrd{Rj4* z&89*sd^s7L?0>ZT=>4DF!|na=RazIWs4=L7`@`PvJlv%yU{Ww1GckpnMz?f|x_S>l z7kMs~(V$c09Hv}O{AG=a>0409sbMp8akJJ>MNGONaOwl?_qoD^-D3=Q%%t#tKLlX& zDv-j>w2&Z21)K>CNN_U#Fg8*l=|Wk8dVf5&0bG+{%1qyVdiW%md{lk+x#&ozefzO^ z)HBgvGMdonw#Z%WTa%qY-{oW{n9y6Y6PPR~JO3tKcuy6Rg@&_}Q%! Date: Wed, 6 May 2020 23:23:32 -0400 Subject: [PATCH 10/16] implement sub alt checking for vtpm2server Signed-off-by: Brandon Lum --- components/vtpm2-server/gen-jwt.py | 51 +++++++++++++++++++++++- components/vtpm2-server/requirements.txt | 1 + 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/components/vtpm2-server/gen-jwt.py b/components/vtpm2-server/gen-jwt.py index c2b27186..7c99ee75 100755 --- a/components/vtpm2-server/gen-jwt.py +++ b/components/vtpm2-server/gen-jwt.py @@ -29,9 +29,47 @@ from jwcrypto import jwt, jwk from EngineJWK import EngineJWK +from cryptography import x509 +from cryptography.hazmat.backends import default_backend + expire = int(os.getenv('TTL_SEC', 30)) iss = os.getenv('ISS', 'wsched@us.ibm.com') +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.""" @@ -98,17 +136,26 @@ def main(args): statedir = os.getenv('STATEDIR') or '/tmp' # 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" + except Exception as e: + # using without x5c chain of trust should be disabled + print e + raise e + token = jwt.JWT(header={"alg": "RS256", "typ": "JWT", "kid": key.key_id},claims=payload) token.make_signed_token(key) return token.serialize() diff --git a/components/vtpm2-server/requirements.txt b/components/vtpm2-server/requirements.txt index f2e1e506..36d26b87 100755 --- a/components/vtpm2-server/requirements.txt +++ b/components/vtpm2-server/requirements.txt @@ -1 +1,2 @@ Flask==1.0.2 +cryptography==2.9.2 From 9cab2824a0fda90f67d7e343465a7c468cb324a6 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Thu, 7 May 2020 12:47:20 +0200 Subject: [PATCH 11/16] CA extention support for VTPM2 --- README.md | 14 ++++++++++---- charts/ti-key-release-2-1.5.0.tgz | Bin 7024 -> 7022 bytes charts/tsi-node-setup-1.5.0.tgz | Bin 2312 -> 2314 bytes components/node-setup/init-node.sh | 14 ++++++++++++++ components/vtpm2-server/startup_tpm.sh | 8 ++++---- examples/vault-plugin/demo.vault-setup.sh | 12 ++++++++++-- go.mod | 2 +- go.sum | 4 ++-- 8 files changed, 41 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index eb46da57..1dea841b 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,9 @@ $ curl http:/// 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) @@ -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. @@ -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 @@ -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): diff --git a/charts/ti-key-release-2-1.5.0.tgz b/charts/ti-key-release-2-1.5.0.tgz index 9f13007f1cbed9a9bac7ebe7191cdd5b91b5bd83..a48fe793cea2d7a85eafcee069000f6a23b2b274 100644 GIT binary patch delta 6906 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 AF8}}l delta 6908 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 CG}Tf7 diff --git a/charts/tsi-node-setup-1.5.0.tgz b/charts/tsi-node-setup-1.5.0.tgz index b2518c8a97d61fd7beff24c7d42c13507afb19a3..b58a43995174223de083ac0a292932fed0efd32e 100644 GIT binary patch delta 2260 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 delta 2258 zcmV;@2rc)D5{MFzPJaSbuG*?)fxD%S0g7;Qcdw=tyB%whC9fn0GBey~zmnuQwn+#( z!!fs2(TgOOTHQaj)PK~m(X1zAj6IE}$ot1BRVJ9zjK6ugHW&;Bhoh1E9t;NU@4^1w z!JFaU!DxT9zdNvW!~MbG;0+935X0J5XhYST!B@*_FYXW0Xn)S$qtZ-@5N1O{^SpZO z41+<3#Hb_1a^t#=;1}c>MD~*#%l>DS z7pPy@0c^1U;plMGw*S4oy{-MfM(e@}CbZxN{MI|GZXSw&bm1mt8khzO|2lei-beQuJV*n&6Ic4hl0lL)VMJt-|Cqq9!o$uw{oqTJ4O}QsUb<1PI?_E(uG{H znU#Hu^S5w3zIKT2N0Iw3aS6Pu)48GJ@pVADbu4xg(@i*Uowzm!8mlQMC81nn2kvCSV}Hmk5LGgb3Z+m~Cz>U;ckY)?x>n1z z)j9PpNMb4eWQbc-NWel07lH^_uwl76dnS&#JuV8)gf|B!1&MrLSOuN3qi3H8fWoOe zjxptf;r~K-Y5D)kI{O!wH>cyk+?%hLu_^yMJQ%e6|7di$yY>IC(VSziC3Q?N!zgDT zt$znkahcTxga81|^AJ9Ls=PRKTCu{O^9}^?@LB&)I z-G?rs2(?ZMzL@U4xOJe-y~zq~Y)p-jqkqBJIvMX^eFyYpFZ_>V8*i0MQ@wUnR$*s+M`CJ6hqkQ zKRggiMNaQ?8~&|Va7SJyKH9pH0V(Nv5M#o`I>*pk61k)^@C65VI_t)KIYXsbjDPUi zW@RFRgFW|?W#b}A*gbT5OJ*oy_uIof_OYE-GLnMxIs8^o&Ju>P^}d?vCDA z8Fn!mCK%B|BWRf+{H>Uv5@>Tix9o%=k2Tn2m$Qu7B-luAtKU!NP9-N7V@vCDzZ|0< z0&3%+#ja7JP|zlZGv6Q_C!=l)4zK6uY2VOYkhs zbG_vB|85oGkI&!S8qP`~o=ekjRahInzZ+n#m@o-+zSF($bf?A5%l$paZQbw2F|6&c zHJ-&~g=)XY<$>{3oKBE$-sdu6u3J@I>Hc?l34+h|zQb*l?EJX{oe!Nav41X^Wm0&k zdLvX%=6TUP4{j@43YhFy5tWIE7x9zHgpG57PW#J@V%?;ALh)E86WUM_tw}HzM&tE% zR29Qk^QwwPmer#vpW(Mcnu@Je<4P_W6?BSmZ!&LU8v6nnw6@`e{r}qdA5+Yvu&K~j z7lSs$|Gm+$UH>^847TzARexH&{!8;*_h&EFI zznl^_36|cg(`I&6aDF8@i{>F5@jE)#H40a-VrG6Ug~59h)`q~9gBZst#;#(9wLuiK zChcNf*Qevto1^pd#vu3tO>#x;}eGqRZELrd{Rj4*&89*s zd^s7L?0>ZT=>4DF!|na=RazIWs4=L7`@`PvJlv%yU{Ww1GckpnMz?f|x_S>l7kMs~ z(V$c09Hv}O{AG=a>0409sbMp8akJJ>MNGONaOwl?_kX#le?J6Z^D2defbW0>-?EiuM+{*WVhA;a9&<6V-?jIbs?SFr`zqS9@XxsOHw(tLJ-~V~?{U86r gPxJMkZ*8{Qwr$(CZC~B~6954J|0AXz`~Wfl07RmD?EnA( diff --git a/components/node-setup/init-node.sh b/components/node-setup/init-node.sh index b3acefcb..2d58e26c 100755 --- a/components/node-setup/init-node.sh +++ b/components/node-setup/init-node.sh @@ -49,6 +49,20 @@ else cat > ${SSLCONF} << EOF [req] req_extensions = v3_req +distinguished_name = req_distinguished_name + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_min = 2 +countryName_max = 2 +stateOrProvinceName = State or Province Name (full name) +localityName = Locality Name (eg, city) +0.organizationName = Organization Name (eg, company) +organizationalUnitName = Organizational Unit Name (eg, section) +commonName = Common Name (eg, fully qualified host name) +commonName_max = 64 +emailAddress = Email Address +emailAddress_max = 64 [v3_req] subjectAltName= @alt_names diff --git a/components/vtpm2-server/startup_tpm.sh b/components/vtpm2-server/startup_tpm.sh index 5fe7af06..28258752 100644 --- a/components/vtpm2-server/startup_tpm.sh +++ b/components/vtpm2-server/startup_tpm.sh @@ -29,9 +29,9 @@ else fi fi -OPENSSLCNF="/host/tsi-secure/openssl.cnf" -if ! [ -f "${OPENSSLCNF}" ]; then - echo "Missing ${OPENSSLCNF} file with node identity" +TSIOPENSSLCNF="/host/tsi-secure/openssl.cnf" +if ! [ -f "${TSIOPENSSLCNF}" ]; then + echo "Missing ${TSIOPENSSLCNF} file with node identity" exit 1 fi @@ -55,5 +55,5 @@ if ! [ -f "${TPMKEYFILE}" ]; then echo -n "${TPMKEYURI}" > "${STATEDIR}/tpmkeyurl" openssl rsa -inform engine -engine tpm2 -pubout -in "${TPMKEYFILE}" -out "${STATEDIR}/tpmpubkey.pem" - openssl req -engine tpm2 -new -key "${TPMKEYFILE}" -keyform engine -subj "/CN=vtpm2-jwt-server" -out "${STATEDIR}/server.csr -reqexts v3_req -config <(cat /etc/ssl/openssl.cnf ${OPENSSLCNF})" + openssl req -engine tpm2 -new -key "${TPMKEYFILE}" -keyform engine -subj "/CN=vtpm2-jwt-server" -out "${STATEDIR}/server.csr" -reqexts v3_req -config <(cat ${TSIOPENSSLCNF} ) fi diff --git a/examples/vault-plugin/demo.vault-setup.sh b/examples/vault-plugin/demo.vault-setup.sh index 097f4d2b..bc0af9b2 100755 --- a/examples/vault-plugin/demo.vault-setup.sh +++ b/examples/vault-plugin/demo.vault-setup.sh @@ -37,7 +37,8 @@ setupVault() if [ $RT -ne 0 ] ; then echo " 'vault secrets enable pki' command failed" echo "maybe already set?" - exit 1 + read -n 1 -s -r -p 'Press any key to continue' + #exit 1 fi # Increase the TTL by tuning the secrets engine. The default value of 30 days may # be too short, so increase it to 1 year: @@ -58,8 +59,15 @@ setupVault() # obtain the SHA256 for the plugin # if the deployed image has the same binary as the one on your system, use the # following method: + VAULTPOD=$($kk get po | grep tsi-vault- | grep Running | awk '{print $1}') + if [ "$VAULTPOD" == "" ]; then + echo "No running Vault container in this namespace. Perhaps Vault is running in a different location" + echo "Please validate by running the following command: " + echo " $kk get po | grep tsi-vault- | grep Running" + exit 1 + fi export SHA256 - SHA256=$($kk exec $($kk get po | grep tsi-vault-| awk '{print $1}') /usr/bin/sha256sum /plugins/vault-plugin-auth-ti-jwt | cut -d' ' -f1) + SHA256=$($kk exec "$VAULTPOD" /usr/bin/sha256sum /plugins/vault-plugin-auth-ti-jwt | cut -d' ' -f1) # another way to obtain this SHA, use a local plugin created by the build process # assuming it is identical to the one one deployed in Vault container. # SHA256=$(shasum -a 256 "${PWD}/pkg/linux_amd64/${PLUGIN}" | cut -d' ' -f1) diff --git a/go.mod b/go.mod index 91f1a64e..65a81d1d 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8 // indirect + golang.org/x/tools v0.0.0-20200507050207-a1532b81a28f // indirect google.golang.org/appengine v1.5.0 // indirect google.golang.org/genproto v0.0.0-20190321212433-e79c0c59cdb5 // indirect google.golang.org/grpc v1.19.1 // indirect diff --git a/go.sum b/go.sum index b0f777e1..b9767474 100644 --- a/go.sum +++ b/go.sum @@ -243,8 +243,8 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 h1:QjA/9ArTfVTLfEhClDCG7SG golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8 h1:BMFHd4OFnFtWX46Xj4DN6vvT1btiBxyq+s0orYBqcQY= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200507050207-a1532b81a28f h1:8vKMSP2LYgBjfB4WCpdfkW5sK5C9eDWguFX9xhqSN2U= +golang.org/x/tools v0.0.0-20200507050207-a1532b81a28f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From b1d984b37b361f9a6b5563d85dd9dfce5b4779d8 Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Thu, 7 May 2020 09:12:55 -0400 Subject: [PATCH 12/16] vtpm-server2 set cryptography library to 2.3 for compatibility with JWKTPM backend code Signed-off-by: Brandon Lum --- components/vtpm2-server/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/vtpm2-server/requirements.txt b/components/vtpm2-server/requirements.txt index 36d26b87..b6ee6515 100755 --- a/components/vtpm2-server/requirements.txt +++ b/components/vtpm2-server/requirements.txt @@ -1,2 +1,2 @@ Flask==1.0.2 -cryptography==2.9.2 +cryptography==2.3 From d2424510f5cc409025f8553878681724eaf1b5fe Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Thu, 7 May 2020 11:13:11 -0400 Subject: [PATCH 13/16] Add vault path_login unit test for cert chain claims validation Signed-off-by: Brandon Lum --- examples/vault-plugin/path_login_test.go | 65 ++++++++++++++++++++++++ go.mod | 2 +- go.sum | 2 + 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/examples/vault-plugin/path_login_test.go b/examples/vault-plugin/path_login_test.go index 0928a3a7..c191acb4 100644 --- a/examples/vault-plugin/path_login_test.go +++ b/examples/vault-plugin/path_login_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/hashicorp/vault/logical" + "github.com/pkg/errors" jose "gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2/jwt" ) @@ -765,6 +766,46 @@ func TestLogin_NestedGroups(t *testing.T) { } } +func validateTokenX5cChainClaims(rootCA string, token string) error { + allClaims := map[string]interface{}{} + claims := jwt.Claims{} + + parsedJWT, err := jwt.ParseSigned(token) + if err != nil { + return errors.Wrapf(err, "Unable to parse JWT token") + } + + var validateKey interface{} + var certClaims map[string]string + validateKey, certClaims, err = validateCertChain([]byte(rootCA), parsedJWT) + if err != nil { + // If can't validate cert chain, use the rootCA public key + validateKey = rootCA + } + + if err := parsedJWT.Claims(validateKey, &claims, &allClaims); err == nil { + for k, v := range certClaims { + if vv, ok := allClaims[k]; ok { + if vv.(string) != v { + return errors.New("Err: Claims don't match cert") + } + } + } + return nil + } else { + return err + } +} + +func TestTokenX5cChainClaims(t *testing.T) { + if err := validateTokenX5cChainClaims(testTokenCert, testGoodToken); err != nil { + t.Fatal("good token should pass chain check claim but failed") + } + if err := validateTokenX5cChainClaims(testTokenCert, testBadToken); err == nil { + t.Fatal("bad token should not pass chain check claim but passed") + } +} + const ( pubCA string = `-----BEGIN CERTIFICATE----- MIIDLDCCAhQCCQCMsgqQcWbl6DANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJV @@ -841,4 +882,28 @@ KipoGJME+9Yeb9YoZXa4OHl+vNeNR4gHqhuQ9eMqNb7UbzMo51psAcbcJRBa9Erb jWtd4Snl/u9ziZ0dlAZyFzaytCABbMwMKIjlCzSLMCNZGDOjGyLtkaWzB+0HRZ3h ZXGLmyl7eSc8ml5o8AZ6aJ2niou0NCJl4VgxJQstBzkFZR+eSL0Z -----END RSA PRIVATE KEY-----` + + testTokenCert string = `-----BEGIN CERTIFICATE----- +MIIDCDCCAfCgAwIBAgIJAI8ECOEbEYGjMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV +BAMMDHZhdWx0LXNlcnZlcjAeFw0yMDA1MDQxNDUzMzNaFw0yMDA2MDMxNDUzMzNa +MBkxFzAVBgNVBAMMDmpzcy1qd3Qtc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA6Ei0+AcignaBWnRzAlj9dkVlbDkZ+Z0DeCvA35yN+Oz/h3HC +tKIUr13uJXBzHt4cfKuPQzB5hTvHAPz7KDafkcVFOrvAmB145bypJ0u4Lyi3GNDx +9DKCtfE8G+JGfJeyiS8kt2TRI2JHBiZRiOH6DZWUcjX9XAgCA2+C+7fXDK6yb3LS +JaqavCv0RDTlDtWe0qbS+50akUkoqYeKpwOi5WMuLMjZBn1nlyY5wBY7i4SHLrnr +xHujf5egEaP31lgDM+L3iXYXUEJYvWzwVOQrMrvdM/6zv55fHEq5hDKuWlln6aAA +/R0bsmA7aPQhFrJ3mbgq/OPZLU6zRyxT1ajY6QIDAQABo1UwUzBRBgNVHREESjBI +hhpUU0k6Y2x1c3Rlci1uYW1lOm15Y2x1c3RlcoYUVFNJOnJlZ2lvbk5hbWU6ZXUt +ZGWGFFRTSTpkYXRhY2VudGVyOmZyYTAyMA0GCSqGSIb3DQEBBQUAA4IBAQBfyKhB +1XGzA99jYpPxV+m7y5IvAuLjVIFyM0zyW1ZRMBj/NfW2NJWaO+4qthqN6ByjmhIl +Q1drYDI7pvkflvL+5ZCWMLUWrftz670i5zNuHdGavk+LbLiKTzfBdqkr6agB5oug +VVwWQ11TvJms4YEVz8t5HKkIqvn9gKq1BaWcOnG+VSn5zLrl9CuzH3eKEJDOrd50 +Q0C+xfB7Ikkhh/AiQt/PmYx1pJZUqxg90+7ndHVExh8y3VHWKFqaiN30ZfRdjIjc +2kzKQ5QaslWMP1m/TepCkrvG6KonJ6T4xAVSmMWSlNNCSOOCp2EfYdFjytWmanWc +Y1sgysDkYs5ztN2W +-----END CERTIFICATE-----` + + testGoodToken string = `eyJhbGciOiJSUzI1NiIsImtpZCI6Im9FczYwZmFPbkNFWGtEa0hLamt1TTV2aXRnZG5yZTVlVnN6VzcyOUw0MmciLCJ0eXAiOiJKV1QiLCJ4NWMiOlsiTUlJRENEQ0NBZkNnQXdJQkFnSUpBSThFQ09FYkVZR2pNQTBHQ1NxR1NJYjNEUUVCQlFVQU1CY3hGVEFUQmdOVkJBTU1ESFpoZFd4MExYTmxjblpsY2pBZUZ3MHlNREExTURReE5EVXpNek5hRncweU1EQTJNRE14TkRVek16TmFNQmt4RnpBVkJnTlZCQU1NRG1wemN5MXFkM1F0YzJWeWRtVnlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTZFaTArQWNpZ25hQlduUnpBbGo5ZGtWbGJEa1orWjBEZUN2QTM1eU4rT3ovaDNIQ3RLSVVyMTN1SlhCekh0NGNmS3VQUXpCNWhUdkhBUHo3S0RhZmtjVkZPcnZBbUIxNDVieXBKMHU0THlpM0dORHg5REtDdGZFOEcrSkdmSmV5aVM4a3QyVFJJMkpIQmlaUmlPSDZEWldVY2pYOVhBZ0NBMitDKzdmWERLNnliM0xTSmFxYXZDdjBSRFRsRHRXZTBxYlMrNTBha1Vrb3FZZUtwd09pNVdNdUxNalpCbjFubHlZNXdCWTdpNFNITHJucnhIdWpmNWVnRWFQMzFsZ0RNK0wzaVhZWFVFSll2V3p3Vk9Rck1ydmRNLzZ6djU1ZkhFcTVoREt1V2xsbjZhQUEvUjBic21BN2FQUWhGckozbWJncS9PUFpMVTZ6Unl4VDFhalk2UUlEQVFBQm8xVXdVekJSQmdOVkhSRUVTakJJaGhwVVUwazZZMngxYzNSbGNpMXVZVzFsT20xNVkyeDFjM1JsY29ZVVZGTkpPbkpsWjJsdmJrNWhiV1U2WlhVdFpHV0dGRlJUU1Rwa1lYUmhZMlZ1ZEdWeU9tWnlZVEF5TUEwR0NTcUdTSWIzRFFFQkJRVUFBNElCQVFCZnlLaEIxWEd6QTk5allwUHhWK203eTVJdkF1TGpWSUZ5TTB6eVcxWlJNQmovTmZXMk5KV2FPKzRxdGhxTjZCeWptaElsUTFkcllESTdwdmtmbHZMKzVaQ1dNTFVXcmZ0ejY3MGk1ek51SGRHYXZrK0xiTGlLVHpmQmRxa3I2YWdCNW91Z1ZWd1dRMTFUdkptczRZRVZ6OHQ1SEtrSXF2bjlnS3ExQmFXY09uRytWU241ekxybDlDdXpIM2VLRUpET3JkNTBRMEMreGZCN0lra2hoL0FpUXQvUG1ZeDFwSlpVcXhnOTArN25kSFZFeGg4eTNWSFdLRnFhaU4zMFpmUmRqSWpjMmt6S1E1UWFzbFdNUDFtL1RlcENrcnZHNktvbko2VDR4QVZTbU1XU2xOTkNTT09DcDJFZllkRmp5dFdtYW5XY1kxc2d5c0RrWXM1enROMlciLCJNSUlDcWpDQ0FaSUNDUUNoUEovcDZwTUNpVEFOQmdrcWhraUc5dzBCQVFzRkFEQVhNUlV3RXdZRFZRUUREQXgyWVhWc2RDMXpaWEoyWlhJd0hoY05NakF3TlRBME1UUTFNak0xV2hjTk1qQXdOakF6TVRRMU1qTTFXakFYTVJVd0V3WURWUVFEREF4MllYVnNkQzF6WlhKMlpYSXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDN3hVeHB3UzgwdFNxdENzQ0RTZ1NjNmJTNGdDbEhMWTZJQUdkNGROdVZBdlMydzlnUm1SUzUrZWJrOEppQmZMbGtKWDZDOUZpcldoUm5rTmo2K1RtUmcrdzhpdERwV2k3UFpYY0VoK3U4Z2dqNHBnM0g5VFQ5dFdOempKOCtuQUlUVDdIczR1ck9kbHBrSlFQZjBlZURjQStHd3QySjE1MGRIQWNDQmc5SnQ2T0QrSVJTUUFhODRoN3lKdVpFWjl2Zm0xMGpqZXpjeitqUlVVaVorTmhNL0xnVWFLcjQybm96bE9ybGkvditzYlFkZDh4RnZVUUJsVDFRRUhaTUlydFl1NUFKOG1TQ0poSGQ1ZHZ2VmowVzh1Vmw3M0RYdWZTM3dKS2tzRlhYemVpa1NFTUY0bUY0YjUyOWlFNVIyb0Z1MlRxSFc5Um5TakhreW5WOVN1VGJBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLTmVjbFhvWTZxazRtbkFRZzdWZWtaZHViRU1YbnZGL01OdVd3NXBrMlcrZWZrRGl3ZmJHOVBqN3lIa2dsZGlZWFovQWFBZkhTR1hHQUlWeUtRcGtaOVc0TGRCeStSUFRRbWxOUHlzM1JJQWtJMFVSRDc4Sjk0dlBObjNnVE5xa2tpMEpVSCtnMUFBcmFCcWMzNVprVmJpQmsxNWNLdHA1SVpXeDV4SkxUSU1tcE93RUx3NHVuR3ZHU0JBWFRkV1JralRpVjVuVVNMSXVHMzBTMWxaOVdhTHNKRFdpTDFGRWxpNFpaM2dBcW9FeVNnS2RrTU5jOWhtRnZFNTd6N21yNmFvZitoVTN5QkRpQkt2ajNiNGl2RGVLNjNqMThKWHB4VDZnKzFBR2VyUnRnT1RPLzFneWRRR2dqeEJZeFBRWWJ5OGR4V1VwN3dyQjFlVW45OS9jc2c9Il19. eyJjbHVzdGVyLW5hbWUiOiJteWNsdXN0ZXIiLCJkYXRhY2VudGVyIjoiZnJhMDIiLCJyZWdpb25OYW1lIjoiZXUtZGUiLCJleHAiOjE1ODgyODExNzcsImlhdCI6MTU4ODI4MTExNywiaW1hZ2VzIjoiMzBiZWVkMDY2NWQ5Y2I0ZGY2MTZjY2E4NGVmMmMwNmQyMzIzZTAyODY5ZmNjYThiYmZiZjBkOGM1YTM5ODdjYyIsImltYWdlcy1uYW1lcyI6InVidW50dUBzaGEyNTY6MjUwY2M2ZjNmM2ZmYzVjZGFhOWQ4ZjQ5NDZhYzc5ODIxYWFmYjRkM2FmYzkzOTI4ZjBkZTkzMzZlYmEyMWFhNCIsImlzcyI6IndzY2hlZEB1cy5pYm0uY29tIiwibWFjaGluZWlkIjoiYjQ2ZTE2NWMzMmQzNDJkOTg5NmQ5ZWViNDNjNGQ1ZGQiLCJuYW1lc3BhY2UiOiJ0ZXN0IiwicG9kIjoibXl1YnVudHUtNjc1NmQ2NjViYy1tMmp6NiIsInN1YiI6IndzY2hlZEB1cy5pYm0uY29tIn0.Yy_nTzLWmxDMCCq1TOfiHLMU9SFAIPv1ahxRHcbnn6tCOVVp1E13zKSZtoUajBFaj0CefgokpBnUipIpzcvZX4XeBy6--t6DR7Epd5RWDryj7tcIh_ShsK00V7PKeF4JvGhq15iDhKHNIYBbSzwLNBDhbyARzlFE1tszjKcUeBiZf7zvQ8C0SjISjtDE4f- dvnquTI62P8nhYqQXiiTpjIhKbgdHECxbdgVgLgfwHdxbkiBiqOodijikS0TjgkC5iUBGDWZvhbQLq8OCVThZoM09aT4gIE5PnBMRJXhAUkzS39lKvQNCONiVGkNvwVCqK3LLffVfe3vQy3aNiKXTUw` + + testBadToken string = `eyJhbGciOiJSUzI1NiIsImtpZCI6Im9FczYwZmFPbkNFWGtEa0hLamt1TTV2aXRnZG5yZTVlVnN6VzcyOUw0MmciLCJ0eXAiOiJKV1QiLCJ4NWMiOlsiTUlJRENEQ0NBZkNnQXdJQkFnSUpBSThFQ09FYkVZR2pNQTBHQ1NxR1NJYjNEUUVCQlFVQU1CY3hGVEFUQmdOVkJBTU1ESFpoZFd4MExYTmxjblpsY2pBZUZ3MHlNREExTURReE5EVXpNek5hRncweU1EQTJNRE14TkRVek16TmFNQmt4RnpBVkJnTlZCQU1NRG1wemN5MXFkM1F0YzJWeWRtVnlNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTZFaTArQWNpZ25hQlduUnpBbGo5ZGtWbGJEa1orWjBEZUN2QTM1eU4rT3ovaDNIQ3RLSVVyMTN1SlhCekh0NGNmS3VQUXpCNWhUdkhBUHo3S0RhZmtjVkZPcnZBbUIxNDVieXBKMHU0THlpM0dORHg5REtDdGZFOEcrSkdmSmV5aVM4a3QyVFJJMkpIQmlaUmlPSDZEWldVY2pYOVhBZ0NBMitDKzdmWERLNnliM0xTSmFxYXZDdjBSRFRsRHRXZTBxYlMrNTBha1Vrb3FZZUtwd09pNVdNdUxNalpCbjFubHlZNXdCWTdpNFNITHJucnhIdWpmNWVnRWFQMzFsZ0RNK0wzaVhZWFVFSll2V3p3Vk9Rck1ydmRNLzZ6djU1ZkhFcTVoREt1V2xsbjZhQUEvUjBic21BN2FQUWhGckozbWJncS9PUFpMVTZ6Unl4VDFhalk2UUlEQVFBQm8xVXdVekJSQmdOVkhSRUVTakJJaGhwVVUwazZZMngxYzNSbGNpMXVZVzFsT20xNVkyeDFjM1JsY29ZVVZGTkpPbkpsWjJsdmJrNWhiV1U2WlhVdFpHV0dGRlJUU1Rwa1lYUmhZMlZ1ZEdWeU9tWnlZVEF5TUEwR0NTcUdTSWIzRFFFQkJRVUFBNElCQVFCZnlLaEIxWEd6QTk5allwUHhWK203eTVJdkF1TGpWSUZ5TTB6eVcxWlJNQmovTmZXMk5KV2FPKzRxdGhxTjZCeWptaElsUTFkcllESTdwdmtmbHZMKzVaQ1dNTFVXcmZ0ejY3MGk1ek51SGRHYXZrK0xiTGlLVHpmQmRxa3I2YWdCNW91Z1ZWd1dRMTFUdkptczRZRVZ6OHQ1SEtrSXF2bjlnS3ExQmFXY09uRytWU241ekxybDlDdXpIM2VLRUpET3JkNTBRMEMreGZCN0lra2hoL0FpUXQvUG1ZeDFwSlpVcXhnOTArN25kSFZFeGg4eTNWSFdLRnFhaU4zMFpmUmRqSWpjMmt6S1E1UWFzbFdNUDFtL1RlcENrcnZHNktvbko2VDR4QVZTbU1XU2xOTkNTT09DcDJFZllkRmp5dFdtYW5XY1kxc2d5c0RrWXM1enROMlciLCJNSUlDcWpDQ0FaSUNDUUNoUEovcDZwTUNpVEFOQmdrcWhraUc5dzBCQVFzRkFEQVhNUlV3RXdZRFZRUUREQXgyWVhWc2RDMXpaWEoyWlhJd0hoY05NakF3TlRBME1UUTFNak0xV2hjTk1qQXdOakF6TVRRMU1qTTFXakFYTVJVd0V3WURWUVFEREF4MllYVnNkQzF6WlhKMlpYSXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDN3hVeHB3UzgwdFNxdENzQ0RTZ1NjNmJTNGdDbEhMWTZJQUdkNGROdVZBdlMydzlnUm1SUzUrZWJrOEppQmZMbGtKWDZDOUZpcldoUm5rTmo2K1RtUmcrdzhpdERwV2k3UFpYY0VoK3U4Z2dqNHBnM0g5VFQ5dFdOempKOCtuQUlUVDdIczR1ck9kbHBrSlFQZjBlZURjQStHd3QySjE1MGRIQWNDQmc5SnQ2T0QrSVJTUUFhODRoN3lKdVpFWjl2Zm0xMGpqZXpjeitqUlVVaVorTmhNL0xnVWFLcjQybm96bE9ybGkvditzYlFkZDh4RnZVUUJsVDFRRUhaTUlydFl1NUFKOG1TQ0poSGQ1ZHZ2VmowVzh1Vmw3M0RYdWZTM3dKS2tzRlhYemVpa1NFTUY0bUY0YjUyOWlFNVIyb0Z1MlRxSFc5Um5TakhreW5WOVN1VGJBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLTmVjbFhvWTZxazRtbkFRZzdWZWtaZHViRU1YbnZGL01OdVd3NXBrMlcrZWZrRGl3ZmJHOVBqN3lIa2dsZGlZWFovQWFBZkhTR1hHQUlWeUtRcGtaOVc0TGRCeStSUFRRbWxOUHlzM1JJQWtJMFVSRDc4Sjk0dlBObjNnVE5xa2tpMEpVSCtnMUFBcmFCcWMzNVprVmJpQmsxNWNLdHA1SVpXeDV4SkxUSU1tcE93RUx3NHVuR3ZHU0JBWFRkV1JralRpVjVuVVNMSXVHMzBTMWxaOVdhTHNKRFdpTDFGRWxpNFpaM2dBcW9FeVNnS2RrTU5jOWhtRnZFNTd6N21yNmFvZitoVTN5QkRpQkt2ajNiNGl2RGVLNjNqMThKWHB4VDZnKzFBR2VyUnRnT1RPLzFneWRRR2dqeEJZeFBRWWJ5OGR4V1VwN3dyQjFlVW45OS9jc2c9Il19. eyJjbHVzdGVyLW5hbWUiOiJteWNsdXN0ZXIiLCJkYXRhY2VudGVyIjoiZGFsMDUiLCJyZWdpb25OYW1lIjoidXMtZWFzdCIsImV4cCI6MTU4ODI4MTE3NywiaWF0IjoxNTg4MjgxMTE3LCJpbWFnZXMiOiIzMGJlZWQwNjY1ZDljYjRkZjYxNmNjYTg0ZWYyYzA2ZDIzMjNlMDI4NjlmY2NhOGJiZmJmMGQ4YzVhMzk4N2NjIiwiaW1hZ2VzLW5hbWVzIjoidWJ1bnR1QHNoYTI1NjoyNTBjYzZmM2YzZmZjNWNkYWE5ZDhmNDk0NmFjNzk4MjFhYWZiNGQzYWZjOTM5MjhmMGRlOTMzNmViYTIxYWE0IiwiaXNzIjoid3NjaGVkQHVzLmlibS5jb20iLCJtYWNoaW5laWQiOiJiNDZlMTY1YzMyZDM0MmQ5ODk2ZDllZWI0M2M0ZDVkZCIsIm5hbWVzcGFjZSI6InRlc3QiLCJwb2QiOiJteXVidW50dS02NzU2ZDY2NWJjLW0yano2Iiwic3ViIjoid3NjaGVkQHVzLmlibS5jb20ifQ.X5ECXRbjMKapelN-O7zmqvfdVTMtm2G7RdIjWpWnAcOpz9WrsCFCZdJ4KoeT_AF7DFTJVdh6-VhEqu2ezsZz6nI6CPnZFzm68O4Yr2LzeFFoZF4jhws0abvht- WIz44n1KZ5ERQDQ8AykGLgTHOTQsc9zhzb8zK4pdIgK0oJ0bYuUgLnhry9g4PGIMs70vvskX4vdGgYvt5cZCHKvXBQTGvarCHeJGFrfRj89FJAikQ5dXfHJg8sedBOBpJEYZ4X9PZD1URqhg0zYQs0YpPnmZbJ4v5OnOpPJGvFE31CBTHjkq6-NP3k6cOi7qCCAVbukq0cGnJTBOW32N8VBtGrig` ) diff --git a/go.mod b/go.mod index 65a81d1d..a4715b62 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf // indirect github.com/pierrre/gotestcover v0.0.0-20160517101806-924dca7d15f0 // indirect + github.com/pkg/errors v0.9.1 github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -53,7 +54,6 @@ require ( golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914 - golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect golang.org/x/tools v0.0.0-20200507050207-a1532b81a28f // indirect diff --git a/go.sum b/go.sum index b9767474..f650afc9 100644 --- a/go.sum +++ b/go.sum @@ -158,6 +158,8 @@ github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf h1:0d7SseXGaeqFXfRTLbi github.com/pierrec/lz4 v0.0.0-20190222153722-062282ea0dcf/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrre/gotestcover v0.0.0-20160517101806-924dca7d15f0 h1:i5VIxp6QB8oWZ8IkK8zrDgeT6ORGIUeiN+61iETwJbI= github.com/pierrre/gotestcover v0.0.0-20160517101806-924dca7d15f0/go.mod h1:4xpMLz7RBWyB+ElzHu8Llua96TRCB3YwX+l5EP1wmHk= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From a78a4f0eb1601946c3c9b2ef5a888e70735bddeb Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Thu, 7 May 2020 11:46:51 -0400 Subject: [PATCH 14/16] fix check claim on vault auth plugin Signed-off-by: Brandon Lum --- examples/vault-plugin/path_login.go | 27 +++++++++++++++--------- examples/vault-plugin/path_login_test.go | 8 ++----- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/examples/vault-plugin/path_login.go b/examples/vault-plugin/path_login.go index 4bd1276f..1957eeec 100644 --- a/examples/vault-plugin/path_login.go +++ b/examples/vault-plugin/path_login.go @@ -3,8 +3,8 @@ package jwtauth import ( "context" "crypto/x509" - "errors" "fmt" + "github.com/pkg/errors" "strings" "time" @@ -98,6 +98,19 @@ func validateCertChain(rootCAPEM []byte, jwtToken *jwt.JSONWebToken) (interface{ return nil, nil, fmt.Errorf("Unable to verify cert chain") } +func checkClaims(certClaims map[string]string, payload map[string]interface{}) error { + for k, v := range certClaims { + if vv, ok := payload[k]; ok { + if vvString, _ := vv.(string); vvString != v { + return errors.Errorf("Trust chain assertion of field %v failed, expected %v, got %v", + k, v, vvString) + + } + } + } + return nil +} + func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { token := d.Get("jwt").(string) if len(token) == 0 { @@ -157,16 +170,10 @@ func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d } if err := parsedJWT.Claims(validateKey, &claims, &allClaims); err == nil { - for k, v := range certClaims { - if vv, ok := allClaims[k]; ok { - if vvString, _ := vv.(string); vvString != v { - errMsg = fmt.Sprintf("Trust chain assertion of field %v failed, expected %v, got %v", - k, v, vvString) - continue - } - } + if err = checkClaims(certClaims, allClaims); err != nil { + errMsg = err.Error() + continue } - valid = true break } diff --git a/examples/vault-plugin/path_login_test.go b/examples/vault-plugin/path_login_test.go index c191acb4..cef72ba7 100644 --- a/examples/vault-plugin/path_login_test.go +++ b/examples/vault-plugin/path_login_test.go @@ -784,12 +784,8 @@ func validateTokenX5cChainClaims(rootCA string, token string) error { } if err := parsedJWT.Claims(validateKey, &claims, &allClaims); err == nil { - for k, v := range certClaims { - if vv, ok := allClaims[k]; ok { - if vv.(string) != v { - return errors.New("Err: Claims don't match cert") - } - } + if err := checkClaims(certClaims, allClaims); err != nil { + return errors.New("Err: Claims don't match cert") } return nil } else { From 3f147e077307afe444812f027f1ed1d1302860c0 Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Thu, 7 May 2020 13:00:29 -0400 Subject: [PATCH 15/16] vault auth correct errMsg handling Signed-off-by: Brandon Lum --- examples/vault-plugin/path_login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/vault-plugin/path_login.go b/examples/vault-plugin/path_login.go index 1957eeec..83323256 100644 --- a/examples/vault-plugin/path_login.go +++ b/examples/vault-plugin/path_login.go @@ -179,7 +179,7 @@ func (b *jwtAuthBackend) pathLogin(ctx context.Context, req *logical.Request, d } } if !valid { - if errMsg != "" { + if errMsg == "" { errMsg = "no known key successfully validated the token signature" } return logical.ErrorResponse(errMsg), nil From 0ce1887ea789bb9f494c5c317ad93a88badcbc97 Mon Sep 17 00:00:00 2001 From: Mariusz Sabath Date: Fri, 8 May 2020 20:30:01 +0200 Subject: [PATCH 16/16] Minor formatting fixes and comments --- components/jss/gen-jwt.py | 7 +++++-- components/jss/gen-jwt.sh | 12 +++++++++--- .../jwt-sidecar/execute-get-vault-secrets.sh | 11 +++++++++++ examples/vault-plugin/Makefile | 2 +- examples/vault-plugin/demo.vault-setup.sh | 15 +++++++++------ examples/vault-plugin/path_login.go | 5 +++-- examples/vault-plugin/vault.yaml | 2 +- 7 files changed, 39 insertions(+), 15 deletions(-) diff --git a/components/jss/gen-jwt.py b/components/jss/gen-jwt.py index 783a39aa..9e545c6f 100755 --- a/components/jss/gen-jwt.py +++ b/components/jss/gen-jwt.py @@ -19,8 +19,11 @@ - 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 diff --git a/components/jss/gen-jwt.sh b/components/jss/gen-jwt.sh index d75e9c9f..206be98b 100755 --- a/components/jss/gen-jwt.sh +++ b/components/jss/gen-jwt.sh @@ -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} diff --git a/components/jwt-sidecar/execute-get-vault-secrets.sh b/components/jwt-sidecar/execute-get-vault-secrets.sh index dd29114e..61ad7f1d 100755 --- a/components/jwt-sidecar/execute-get-vault-secrets.sh +++ b/components/jwt-sidecar/execute-get-vault-secrets.sh @@ -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" diff --git a/examples/vault-plugin/Makefile b/examples/vault-plugin/Makefile index 0fdfe28e..83608843 100644 --- a/examples/vault-plugin/Makefile +++ b/examples/vault-plugin/Makefile @@ -12,7 +12,7 @@ BUILD_DATE="$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")" IMG_NAME="ti-vault" REPO ?= trustedseriviceidentity IMAGE := $(REPO)/$(IMG_NAME):$(GIT_COMMIT_SHA) -MUTABLE_IMAGE := $(REPO)/$(IMG_NAME):v0.3 +MUTABLE_IMAGE := $(REPO)/$(IMG_NAME):v0.4 ARCH=$(shell if test `uname -m` = "x86_64"; then echo amd64 ; else echo `uname -m`; fi;) diff --git a/examples/vault-plugin/demo.vault-setup.sh b/examples/vault-plugin/demo.vault-setup.sh index bc0af9b2..352ce8cd 100755 --- a/examples/vault-plugin/demo.vault-setup.sh +++ b/examples/vault-plugin/demo.vault-setup.sh @@ -31,7 +31,7 @@ setupVault() # remove any previously set VAULT_TOKEN, that overrides ROOT_TOKEN in Vault client export VAULT_TOKEN= - vault status + # vault status vault secrets enable pki RT=$? if [ $RT -ne 0 ] ; then @@ -50,7 +50,7 @@ setupVault() export OUT OUT=$(vault write pki/root/generate/internal common_name=${COMMON_NAME} \ ttl=876000h -format=json) - echo "$OUT" + # echo "$OUT" # capture the public key as plugin-config.json CERT=$(echo "$OUT" | jq -r '.["data"].issuing_ca'| awk '{printf "%s\\n", $0}') @@ -83,7 +83,9 @@ setupVault() echo " 'vault write sys/plugins/catalog/auth/vault-plugin-auth-ti-jwt ...' command failed" exit 1 fi - vault read sys/plugins/catalog/auth/vault-plugin-auth-ti-jwt -format=json + # useful for debugging: + # vault read sys/plugins/catalog/auth/vault-plugin-auth-ti-jwt -format=json + # then enable this plugin vault auth enable -path="trusted-identity" -plugin-name="vault-plugin-auth-ti-jwt" plugin RT=$? @@ -93,17 +95,18 @@ setupVault() fi export MOUNT_ACCESSOR - MOUNT_ACCESSOR=$(curl --header "X-Vault-Token: ${ROOT_TOKEN}" --request GET "${VAULT_ADDR}/v1/sys/auth" | jq -r '.["trusted-identity/"].accessor') + MOUNT_ACCESSOR=$(curl -sS --header "X-Vault-Token: ${ROOT_TOKEN}" --request GET "${VAULT_ADDR}/v1/sys/auth" | jq -r '.["trusted-identity/"].accessor') # configure plugin using the Issuing CA created internally above - curl --header "X-Vault-Token: ${ROOT_TOKEN}" --request POST --data @${CONFIG} "${VAULT_ADDR}/v1/auth/trusted-identity/config" + curl -sS --header "X-Vault-Token: ${ROOT_TOKEN}" --request POST --data @${CONFIG} "${VAULT_ADDR}/v1/auth/trusted-identity/config" RT=$? if [ $RT -ne 0 ] ; then echo "failed to configure trusted-identity plugin" exit 1 fi - curl --header "X-Vault-Token: ${ROOT_TOKEN}" --request GET "${VAULT_ADDR}/v1/auth/trusted-identity/config" + CONFIG=$(curl -sS --header "X-Vault-Token: ${ROOT_TOKEN}" --request GET "${VAULT_ADDR}/v1/auth/trusted-identity/config" | jq) + # echo "*** $CONFIG" } if [ ! "$1" == "" ] ; then diff --git a/examples/vault-plugin/path_login.go b/examples/vault-plugin/path_login.go index 83323256..e028adae 100644 --- a/examples/vault-plugin/path_login.go +++ b/examples/vault-plugin/path_login.go @@ -4,10 +4,11 @@ import ( "context" "crypto/x509" "fmt" - "github.com/pkg/errors" "strings" "time" + "github.com/pkg/errors" + oidc "github.com/coreos/go-oidc" "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/helper/cidrutil" @@ -102,7 +103,7 @@ func checkClaims(certClaims map[string]string, payload map[string]interface{}) e for k, v := range certClaims { if vv, ok := payload[k]; ok { if vvString, _ := vv.(string); vvString != v { - return errors.Errorf("Trust chain assertion of field %v failed, expected %v, got %v", + return errors.Errorf("Trust chain assertion of field %v failed, expected %v, got %v", k, v, vvString) } diff --git a/examples/vault-plugin/vault.yaml b/examples/vault-plugin/vault.yaml index 22b7b94b..0633d7c2 100644 --- a/examples/vault-plugin/vault.yaml +++ b/examples/vault-plugin/vault.yaml @@ -31,5 +31,5 @@ spec: spec: containers: - name: tsi-vault - image: trustedseriviceidentity/ti-vault:v0.3 + image: trustedseriviceidentity/ti-vault:v0.4 imagePullPolicy: Always