diff --git a/.travis.yml b/.travis.yml index 104988df..517f17af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ # .travis.yml language: go go: -- "1.12" - +- "1.14" + branches: - master @@ -15,7 +15,7 @@ env: # avoid issues in travis with ssh / https acccess before_script: - - git config --global url."git@github.ibm.com:".insteadOf "https://github.ibm.com/" + - git config --global url."git@github.com:".insteadOf "https://github.com/" after_success: # docker login is required if you want to push docker images. diff --git a/build b/build index 09721d36..722ceb6b 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.2 . +docker build --no-cache -t trustedseriviceidentity/ti-webhook:v1.3 . -docker push trustedseriviceidentity/ti-webhook:v1.2 +docker push trustedseriviceidentity/ti-webhook:v1.3 diff --git a/charts/ti-key-release-1/Chart.yaml b/charts/ti-key-release-1/Chart.yaml index 93d23b09..77f22d14 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.2.0 +version: 1.3.0 home: https://github.com/IBM/trusted-service-identity maintainers: - name: Brandon Lum diff --git a/charts/ti-key-release-1/templates/configmap/mutatingwebhook.yaml b/charts/ti-key-release-1/templates/configmap/mutatingwebhook.yaml index 87c18926..0e5b60e6 100644 --- a/charts/ti-key-release-1/templates/configmap/mutatingwebhook.yaml +++ b/charts/ti-key-release-1/templates/configmap/mutatingwebhook.yaml @@ -6,7 +6,7 @@ metadata: labels: app: tsi-mutate webhooks: - - name: tsi-mutate.morven.me + - name: tsi-mutate.trusted-service-identity.ibm clientConfig: service: name: tsi-mutate-webhook-svc diff --git a/charts/ti-key-release-1/templates/configmap/tsi-mutate-configmap.yaml b/charts/ti-key-release-1/templates/configmap/tsi-mutate-configmap.yaml index 166082c8..6bbd12ae 100644 --- a/charts/ti-key-release-1/templates/configmap/tsi-mutate-configmap.yaml +++ b/charts/ti-key-release-1/templates/configmap/tsi-mutate-configmap.yaml @@ -30,8 +30,8 @@ data: - name: pod-metadata mountPath: /pod-metadata readOnly: true - - name: host-etc - mountPath: /host/etc + - name: host-machineid + mountPath: /host/machineid readOnly: true volumes: - name: tsi-secrets @@ -42,11 +42,11 @@ data: path: /tsi-secure/sockets # directory must exist type: Directory - - name: host-etc + - name: host-machineid hostPath: # directory location on host - path: /etc - type: Directory + path: /etc/machine-id + type: File - name: pod-metadata downwardAPI: items: diff --git a/charts/ti-key-release-1/values.yaml b/charts/ti-key-release-1/values.yaml index 1f457e5b..ce5d0eb1 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.2 +tiVersion: v1.3 # target docker repo tiRepo: trustedseriviceidentity diff --git a/charts/ti-key-release-2-1.3.0.tgz b/charts/ti-key-release-2-1.3.0.tgz new file mode 100644 index 00000000..27fa2698 Binary files /dev/null and b/charts/ti-key-release-2-1.3.0.tgz differ diff --git a/charts/ti-key-release-2/Chart.yaml b/charts/ti-key-release-2/Chart.yaml index f9ca39ac..1658e856 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.2.0 +version: 1.3.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 07fe01f9..aa38a830 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.2.0 + version: 1.3.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 4f18972d..8715c18f 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.2 +tiVersion: v1.3 # target docker repo tiRepo: trustedseriviceidentity diff --git a/charts/ti-setup/Chart.yaml b/charts/ti-setup/Chart.yaml index 3f326f36..baa69226 100644 --- a/charts/ti-setup/Chart.yaml +++ b/charts/ti-setup/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 description: A Helm chart for deployment of TI-KeyRelease name: ti-setup -version: 1.2.0 +version: 1.3.0 home: https://github.com/IBM/trusted-service-identity maintainers: - name: Brandon Lum diff --git a/charts/tsi-node-setup-1.3.0.tgz b/charts/tsi-node-setup-1.3.0.tgz new file mode 100644 index 00000000..51fb66a6 Binary files /dev/null and b/charts/tsi-node-setup-1.3.0.tgz differ diff --git a/charts/tsi-node-setup/Chart.yaml b/charts/tsi-node-setup/Chart.yaml index fbe63c52..3cb9579f 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.2.0 +version: 1.3.0 home: https://github.com/IBM/trusted-service-identity maintainers: - name: Brandon Lum diff --git a/charts/tsi-node-setup/values.yaml b/charts/tsi-node-setup/values.yaml index 3fe451bc..b430b89e 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.2 +tsiVersion: v1.3 # target docker repo tsiRepo: trustedseriviceidentity diff --git a/components/jss/Makefile b/components/jss/Makefile index 366d3ac0..9a53b8d9 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.2 +MUTABLE_IMAGE_PUB := $(REPO)/$(BINARY_NAME_PUB):v1.3 IMAGE_PRIV := $(REPO)/$(BINARY_NAME_PRIV):$(GIT_COMMIT_SHA) -MUTABLE_IMAGE_PRIV := $(REPO)/$(BINARY_NAME_PRIV):v1.2 +MUTABLE_IMAGE_PRIV := $(REPO)/$(BINARY_NAME_PRIV):v1.3 all: docker docker-push timestamp diff --git a/components/jwt-sidecar/Makefile b/components/jwt-sidecar/Makefile index 1ffe39dd..43a146b6 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.2 +MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.3 all: docker docker-push timestamp diff --git a/components/jwt-sidecar/execute-get-token.sh b/components/jwt-sidecar/execute-get-token.sh index 571f6eb2..eef8a6fa 100755 --- a/components/jwt-sidecar/execute-get-token.sh +++ b/components/jwt-sidecar/execute-get-token.sh @@ -15,7 +15,7 @@ while true echo -n "images=$(cat /pod-metadata/ti-images | sha256sum | awk '{print $1}')&" >> /tmp/claims echo -n "cluster-name=$(cat /pod-metadata/ti-cluster-name)&" >> /tmp/claims echo -n "cluster-region=$(cat /pod-metadata/ti-cluster-region)&" >> /tmp/claims - echo -n "machineid=$(cat /host/etc/machine-id)" >> /tmp/claims + echo -n "machineid=$(cat /host/machineid)" >> /tmp/claims curl --unix-socket ${SOCKETFILE} http://localhost/getJWT?"$(cat /tmp/claims)" > /jwt/token # make the wait 5 seconds shorter than JWT TTL diff --git a/components/node-setup/Makefile b/components/node-setup/Makefile index d20eccef..4078d6ce 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.2 +MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.3 all: docker docker-push timestamp diff --git a/components/vtpm2-server/Makefile b/components/vtpm2-server/Makefile index 1e8f47d1..e04c4c5e 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.2 +MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.3 all: docker docker-push timestamp diff --git a/components/vtpm2-server/vtpm2-server.yaml b/components/vtpm2-server/vtpm2-server.yaml index f81c0f93..a7a814df 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.2 + image: trustedseriviceidentity/vtpm2-server:v1.3 imagePullPolicy: Always env: - name: ISS diff --git a/deployment/configmap.yaml b/deployment/configmap.yaml index 32cc6d6a..787caeee 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.2 + image: trustedseriviceidentity/ti-gen-vault-cert:v1.3 imagePullSecrets: - name: regcred imagePullPolicy: Always diff --git a/deployment/deployment.yaml b/deployment/deployment.yaml index e22782ec..0f24b81b 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.2 + image: trustedseriviceidentity/ti-webhook:v1.3 imagePullPolicy: Always args: - -tsiMutateConfigFile=/etc/webhook/config/tsiMutateConfig.yaml diff --git a/deployment/mutatingwebhook.yaml b/deployment/mutatingwebhook.yaml index 8dd2b6cc..cf89d39d 100644 --- a/deployment/mutatingwebhook.yaml +++ b/deployment/mutatingwebhook.yaml @@ -5,7 +5,7 @@ metadata: labels: app: tsi-mutate webhooks: - - name: tsi-mutate.morven.me + - name: tsi-mutate.trusted-service-identity.ibm clientConfig: service: name: tsi-mutate-webhook-svc diff --git a/examples/myubuntuErr1.yaml b/examples/myubuntuErr1.yaml new file mode 100644 index 00000000..1a5d731d --- /dev/null +++ b/examples/myubuntuErr1.yaml @@ -0,0 +1,48 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: myubuntu + name: myubuntu +spec: + replicas: 1 + selector: + matchLabels: + app: myubuntu + template: + metadata: + annotations: + admission.trusted.identity/inject: "false" + tsi.secrets: | + - tsi.secret/name: "mysecret1" + tsi.secret/role: "demo" + tsi.secret/vault-path: "secret/ti-demo-all" + tsi.secret/local-name: "mysecrets/myubuntu-mysecret1" + labels: + app: myubuntu + name: myubuntu + spec: + containers: + - name: myubuntu + image: ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4 + imagePullPolicy: Always + command: [ "/bin/bash", "-c", "--" ] + args: [ "while true; do cat /mydata; sleep 5; done;" ] + volumeMounts: + - mountPath: /host/etc + name: host-etc + - mountPath: /host/sockets + name: tsi-sockets + - mountPath: /host/secrets + name: tsi-secrets + volumes: + - emptyDir: {} + name: tsi-secrets + - hostPath: + path: /tsi-secure/sockets + type: Directory + name: tsi-sockets + - hostPath: + path: /etc + type: Directory + name: host-etc diff --git a/makefile b/makefile index 60daae7e..73d9c0c1 100644 --- a/makefile +++ b/makefile @@ -7,13 +7,15 @@ 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.2 +MUTABLE_IMAGE := $(REPO)/$(BINARY_NAME):v1.3 GOARCH=$(shell go env GOARCH) -.PHONY: all test-deps build-deps fmt vet lint get-deps test build docker docker-push dep +.PHONY: all fast test-deps build-deps fmt vet lint get-deps test build docker docker-push dep all: dep get-deps fmt test build docker timestamp +fast: test build docker docker-push timestamp + dep: go mod tidy @@ -46,7 +48,7 @@ build-deps: dep go mod vendor fmt: - @if [ -n "$$(gofmt -l ${GOFILES})" ]; then echo 'Please run gofmt -l -w $GOFILES on your code.' && exit 1; fi + @if [ -n "$$(gofmt -l ${GOFILES})" ]; then echo 'Please run gofmt -l -w ${GOFILES} on your code.' && exit 1; fi vet: @set -e; for LINE in ${GOPACKAGES}; do go vet $${LINE} ; done diff --git a/tests/ConfigFile.yaml b/tests/ConfigFile.yaml index 858d2305..91f3447e 100644 --- a/tests/ConfigFile.yaml +++ b/tests/ConfigFile.yaml @@ -1,6 +1,6 @@ sidecarContainers: - name: jwt-sidecar - image: trustedseriviceidentity/ti-jwt-sidecar:v1.2 + image: trustedseriviceidentity/ti-jwt-sidecar:v1.3 imagePullPolicy: Always env: - name: HOST_IP diff --git a/tests/ExpectAddContainer.json b/tests/ExpectAddContainer.json new file mode 100644 index 00000000..be6c560b --- /dev/null +++ b/tests/ExpectAddContainer.json @@ -0,0 +1,57 @@ +[ + { + "op": "add", + "path": "/spec/containers/-", + "value": { + "name": "jwt-sidecar", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.3", + "env": [ + { + "name": "HOST_IP", + "valueFrom": { + "fieldRef": { + "fieldPath": "status.hostIP" + } + } + }, + { + "name": "JWT_TTL_SEC", + "value": "30" + }, + { + "name": "VAULT_ADDR", + "value": "http://ti-test1.eu-de.containers.appdomain.cloud" + }, + { + "name": "SECRET_REFRESH_SEC", + "value": "45" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "tsi-secrets", + "mountPath": "/usr/share/secrets" + }, + { + "name": "tsi-sockets", + "mountPath": "/host/sockets" + }, + { + "name": "pod-metadata", + "readOnly": true, + "mountPath": "/pod-metadata" + }, + { + "name": "host-etc", + "readOnly": true, + "mountPath": "/host/etc" + } + ], + "imagePullPolicy": "Always", + "securityContext": { + "runAsUser": 0 + } + } + } +] diff --git a/tests/ExpectAddVolume.json b/tests/ExpectAddVolume.json new file mode 100644 index 00000000..54c0ade5 --- /dev/null +++ b/tests/ExpectAddVolume.json @@ -0,0 +1,93 @@ +[ + { + "op": "add", + "path": "/spec/volumes/-", + "value": { + "name": "tsi-secrets", + "emptyDir": {} + } + }, + { + "op": "add", + "path": "/spec/volumes/-", + "value": { + "name": "tsi-sockets", + "hostPath": { + "path": "/tsi-secure/sockets", + "type": "Directory" + } + } + }, + { + "op": "add", + "path": "/spec/volumes/-", + "value": { + "name": "host-etc", + "hostPath": { + "path": "/etc", + "type": "Directory" + } + } + }, + { + "op": "add", + "path": "/spec/volumes/-", + "value": { + "name": "pod-metadata", + "downwardAPI": { + "items": [ + { + "path": "tsi-secrets", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['tsi.secrets']" + } + }, + { + "path": "ti-identity", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-identity']" + } + }, + { + "path": "ti-pod-name", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + }, + { + "path": "ti-pod-namespace", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + }, + { + "path": "ti-images", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-images']" + } + }, + { + "path": "ti-cluster-name", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-cluster-name']" + } + }, + { + "path": "ti-cluster-region", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-cluster-region']" + } + } + ], + "defaultMode": 420 + } + } + } +] diff --git a/tests/ExpectAddVolumeMount.json b/tests/ExpectAddVolumeMount.json new file mode 100644 index 00000000..23fadd56 --- /dev/null +++ b/tests/ExpectAddVolumeMount.json @@ -0,0 +1,11 @@ +[ + { + "op": "add", + "path": "/spec/containers/0/volumeMounts/-", + "value": { + "name": "tsi-secrets", + "readOnly": true, + "mountPath": "/tsi-secrets" + } + } +] diff --git a/tests/ExpectMutateInit.json b/tests/ExpectMutateInit.json index 6eea7be0..57ed5d1b 100644 --- a/tests/ExpectMutateInit.json +++ b/tests/ExpectMutateInit.json @@ -3,7 +3,7 @@ "SidecarContainers": [ { "name": "jwt-sidecar", - "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.2", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.3", "env": [ { "name": "HOST_IP", diff --git a/tests/ExpectTsiMutateConfig.json b/tests/ExpectTsiMutateConfig.json index 66ef7219..228e055d 100644 --- a/tests/ExpectTsiMutateConfig.json +++ b/tests/ExpectTsiMutateConfig.json @@ -3,7 +3,7 @@ "SidecarContainers": [ { "name": "jwt-sidecar", - "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.2", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.3", "env": [ { "name": "HOST_IP", diff --git a/tests/ExpectUpdateAnnotation.json b/tests/ExpectUpdateAnnotation.json new file mode 100644 index 00000000..81465732 --- /dev/null +++ b/tests/ExpectUpdateAnnotation.json @@ -0,0 +1,15 @@ +[ + { + "op": "replace", + "path": "/metadata/annotations", + "value": { + "admission.trusted.identity/inject": "true", + "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", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" + } + } +] diff --git a/tests/ExpectUpdateAnnotation2.json b/tests/ExpectUpdateAnnotation2.json new file mode 100644 index 00000000..d5fcd20c --- /dev/null +++ b/tests/ExpectUpdateAnnotation2.json @@ -0,0 +1,15 @@ +[ + { + "op": "replace", + "path": "/metadata/annotations", + "value": { + "admission.trusted.identity/inject": "true", + "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.3", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" + } + } +] diff --git a/tests/ExpectUpdateAnnotations.json b/tests/ExpectUpdateAnnotations.json new file mode 100644 index 00000000..a0be4859 --- /dev/null +++ b/tests/ExpectUpdateAnnotations.json @@ -0,0 +1,15 @@ +[ + { + "op": "replace", + "path": "/metadata/annotations", + "value": { + "admission.trusted.identity/inject": "true", + "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", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" + } + } +] diff --git a/tests/FakeAddContainer.json b/tests/FakeAddContainer.json new file mode 100644 index 00000000..31eed109 --- /dev/null +++ b/tests/FakeAddContainer.json @@ -0,0 +1,53 @@ +[ + { + "name": "jwt-sidecar", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.3", + "env": [ + { + "name": "HOST_IP", + "valueFrom": { + "fieldRef": { + "fieldPath": "status.hostIP" + } + } + }, + { + "name": "JWT_TTL_SEC", + "value": "30" + }, + { + "name": "VAULT_ADDR", + "value": "http://ti-test1.eu-de.containers.appdomain.cloud" + }, + { + "name": "SECRET_REFRESH_SEC", + "value": "45" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "tsi-secrets", + "mountPath": "/usr/share/secrets" + }, + { + "name": "tsi-sockets", + "mountPath": "/host/sockets" + }, + { + "name": "pod-metadata", + "readOnly": true, + "mountPath": "/pod-metadata" + }, + { + "name": "host-etc", + "readOnly": true, + "mountPath": "/host/etc" + } + ], + "imagePullPolicy": "Always", + "securityContext": { + "runAsUser": 0 + } + } +] diff --git a/tests/FakeAddContainerTarget.json b/tests/FakeAddContainerTarget.json new file mode 100644 index 00000000..aa71667d --- /dev/null +++ b/tests/FakeAddContainerTarget.json @@ -0,0 +1,25 @@ +[ + { + "name": "myubuntu", + "image": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4", + "command": [ + "/bin/bash", + "-c", + "--" + ], + "args": [ + "while true; do cat /mydata; sleep 5; done;" + ], + "resources": {}, + "volumeMounts": [ + { + "name": "default-token-sggvc", + "readOnly": true, + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" + } + ], + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } +] diff --git a/tests/FakeAddVolume.json b/tests/FakeAddVolume.json new file mode 100644 index 00000000..a1e608ac --- /dev/null +++ b/tests/FakeAddVolume.json @@ -0,0 +1,77 @@ +[ + { + "name": "tsi-secrets", + "emptyDir": {} + }, + { + "name": "tsi-sockets", + "hostPath": { + "path": "/tsi-secure/sockets", + "type": "Directory" + } + }, + { + "name": "host-etc", + "hostPath": { + "path": "/etc", + "type": "Directory" + } + }, + { + "name": "pod-metadata", + "downwardAPI": { + "items": [ + { + "path": "tsi-secrets", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['tsi.secrets']" + } + }, + { + "path": "ti-identity", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-identity']" + } + }, + { + "path": "ti-pod-name", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + }, + { + "path": "ti-pod-namespace", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + }, + { + "path": "ti-images", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-images']" + } + }, + { + "path": "ti-cluster-name", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-cluster-name']" + } + }, + { + "path": "ti-cluster-region", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-cluster-region']" + } + } + ], + "defaultMode": 420 + } + } +] diff --git a/tests/FakeAddVolumeMount.json b/tests/FakeAddVolumeMount.json new file mode 100644 index 00000000..23fadd56 --- /dev/null +++ b/tests/FakeAddVolumeMount.json @@ -0,0 +1,11 @@ +[ + { + "op": "add", + "path": "/spec/containers/0/volumeMounts/-", + "value": { + "name": "tsi-secrets", + "readOnly": true, + "mountPath": "/tsi-secrets" + } + } +] diff --git a/tests/FakeAddVolumeMountTarget.json b/tests/FakeAddVolumeMountTarget.json new file mode 100644 index 00000000..08ef82af --- /dev/null +++ b/tests/FakeAddVolumeMountTarget.json @@ -0,0 +1,7 @@ +[ + { + "name": "default-token-sggvc", + "readOnly": true, + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" + } +] diff --git a/tests/FakeAddVolumeTarget.json b/tests/FakeAddVolumeTarget.json new file mode 100644 index 00000000..72ecdd9d --- /dev/null +++ b/tests/FakeAddVolumeTarget.json @@ -0,0 +1,8 @@ +[ + { + "name": "default-token-sggvc", + "secret": { + "secretName": "default-token-sggvc" + } + } +] diff --git a/tests/FakeAdmissionResponse.json b/tests/FakeAdmissionResponse.json index 0ad36dd5..698eca79 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.2", + "image":"trustedseriviceidentity/ti-gen-vault-cert:v1.3", "resources":{ }, @@ -101,7 +101,7 @@ "path":"/spec/containers/-", "value":{ "name":"jwt-sidecar", - "image":"trustedseriviceidentity/ti-jwt-sidecar:v1.2", + "image":"trustedseriviceidentity/ti-jwt-sidecar:v1.3", "command":[ "/bin/sh", "-c", diff --git a/tests/FakeAnnotationsAdded.yaml b/tests/FakeAnnotationsAdded.yaml new file mode 100644 index 00000000..4bddc7f3 --- /dev/null +++ b/tests/FakeAnnotationsAdded.yaml @@ -0,0 +1,4 @@ +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" diff --git a/tests/FakeIsSafeCreateError.json b/tests/FakeIsSafeCreateError.json new file mode 100644 index 00000000..b97c2964 --- /dev/null +++ b/tests/FakeIsSafeCreateError.json @@ -0,0 +1,115 @@ +{ + "kind": "Pod", + "apiVersion": "v1", + "metadata": { + "generateName": "myubuntu-6fb798d94-", + "creationTimestamp": null, + "labels": { + "app": "myubuntu", + "pod-template-hash": "6fb798d94" + }, + "annotations": { + "admission.trusted.identity/inject": "false", + "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-name: \"mysecrets/myubuntu-mysecret1\"\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "kind": "ReplicaSet", + "name": "myubuntu-6fb798d94", + "uid": "7efb3182-6f81-11ea-be8b-6a908431fac0", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "volumes": [ + { + "name": "tsi-secrets", + "emptyDir": {} + }, + { + "name": "tsi-sockets", + "hostPath": { + "path": "/tsi-secure/sockets", + "type": "Directory" + } + }, + { + "name": "host-etc", + "hostPath": { + "path": "/etc", + "type": "Directory" + } + }, + { + "name": "default-token-sggvc", + "secret": { + "secretName": "default-token-sggvc" + } + } + ], + "containers": [ + { + "name": "myubuntu", + "image": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4", + "command": [ + "/bin/bash", + "-c", + "--" + ], + "args": [ + "while true; do cat /mydata; sleep 5; done;" + ], + "resources": {}, + "volumeMounts": [ + { + "name": "host-etc", + "mountPath": "/host/etc" + }, + { + "name": "tsi-sockets", + "mountPath": "/host/sockets" + }, + { + "name": "tsi-secrets", + "mountPath": "/host/secrets" + }, + { + "name": "default-token-sggvc", + "readOnly": true, + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" + } + ], + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "default", + "serviceAccount": "default", + "securityContext": {}, + "schedulerName": "default-scheduler", + "tolerations": [ + { + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 600 + }, + { + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 600 + } + ], + "priority": 0 + }, + "status": {} +} diff --git a/tests/FakeIsSafeCreateOK.json b/tests/FakeIsSafeCreateOK.json new file mode 100644 index 00000000..1f8f30f3 --- /dev/null +++ b/tests/FakeIsSafeCreateOK.json @@ -0,0 +1,85 @@ +{ + "kind": "Pod", + "apiVersion": "v1", + "metadata": { + "generateName": "myubuntu-79bb88676b-", + "creationTimestamp": null, + "labels": { + "app": "myubuntu", + "pod-template-hash": "79bb88676b" + }, + "annotations": { + "admission.trusted.identity/inject": "true", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "kind": "ReplicaSet", + "name": "myubuntu-79bb88676b", + "uid": "b0272e4e-6f7f-11ea-be8b-6a908431fac0", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "volumes": [ + { + "name": "default-token-sggvc", + "secret": { + "secretName": "default-token-sggvc" + } + } + ], + "containers": [ + { + "name": "myubuntu", + "image": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4", + "command": [ + "/bin/bash", + "-c", + "--" + ], + "args": [ + "while true; do cat /mydata; sleep 5; done;" + ], + "resources": {}, + "volumeMounts": [ + { + "name": "default-token-sggvc", + "readOnly": true, + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" + } + ], + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "default", + "serviceAccount": "default", + "securityContext": {}, + "schedulerName": "default-scheduler", + "tolerations": [ + { + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 600 + }, + { + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 600 + } + ], + "priority": 0 + }, + "status": {} +} diff --git a/tests/FakeIsSafeUpdateError.json b/tests/FakeIsSafeUpdateError.json new file mode 100644 index 00000000..5759af14 --- /dev/null +++ b/tests/FakeIsSafeUpdateError.json @@ -0,0 +1,291 @@ +{ + "kind": "Pod", + "apiVersion": "v1", + "metadata": { + "name": "myubuntu-79bb88676b-qt222", + "generateName": "myubuntu-79bb88676b-", + "namespace": "trusted-identity", + "uid": "b039281d-6f7f-11ea-be8b-6a908431fac0", + "resourceVersion": "45012419", + "creationTimestamp": "2020-03-26T16:34:39Z", + "labels": { + "app": "myubuntu", + "pod-template-hash": "79bb88676b" + }, + "annotations": { + "admission.trusted.identity/inject": "true", + "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.3", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "kind": "ReplicaSet", + "name": "myubuntu-79bb88676b", + "uid": "b0272e4e-6f7f-11ea-be8b-6a908431fac0", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "volumes": [ + { + "name": "default-token-sggvc", + "secret": { + "secretName": "default-token-sggvc", + "defaultMode": 420 + } + }, + { + "name": "tsi-secrets", + "emptyDir": {} + }, + { + "name": "tsi-sockets", + "hostPath": { + "path": "/tsi-secure/sockets", + "type": "Directory" + } + }, + { + "name": "host-etc", + "hostPath": { + "path": "/etc", + "type": "Directory" + } + }, + { + "name": "pod-metadata", + "downwardAPI": { + "items": [ + { + "path": "tsi-secrets", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['tsi.secrets']" + } + }, + { + "path": "ti-identity", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-identity']" + } + }, + { + "path": "ti-pod-name", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + }, + { + "path": "ti-pod-namespace", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + }, + { + "path": "ti-images", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-images']" + } + }, + { + "path": "ti-cluster-name", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-cluster-name']" + } + }, + { + "path": "ti-cluster-region", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-cluster-region']" + } + } + ], + "defaultMode": 420 + } + } + ], + "containers": [ + { + "name": "myubuntu", + "image": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4", + "command": [ + "/bin/bash", + "-c", + "--" + ], + "args": [ + "while true; do cat /mydata; sleep 5; done;" + ], + "resources": {}, + "volumeMounts": [ + { + "name": "default-token-sggvc", + "readOnly": true, + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" + }, + { + "name": "tsi-secrets", + "readOnly": true, + "mountPath": "/tsi-secrets" + } + ], + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + }, + { + "name": "jwt-sidecar", + "image": "ubuntu:latest", + "env": [ + { + "name": "HOST_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.hostIP" + } + } + }, + { + "name": "JWT_TTL_SEC", + "value": "30" + }, + { + "name": "VAULT_ADDR", + "value": "http://ti-test1.eu-de.containers.appdomain.cloud" + }, + { + "name": "SECRET_REFRESH_SEC", + "value": "45" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "tsi-secrets", + "mountPath": "/usr/share/secrets" + }, + { + "name": "tsi-sockets", + "mountPath": "/host/sockets" + }, + { + "name": "pod-metadata", + "readOnly": true, + "mountPath": "/pod-metadata" + }, + { + "name": "host-etc", + "readOnly": true, + "mountPath": "/host/etc" + } + ], + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always", + "securityContext": { + "runAsUser": 0 + } + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "default", + "serviceAccount": "default", + "nodeName": "10.135.67.145", + "securityContext": {}, + "schedulerName": "default-scheduler", + "tolerations": [ + { + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 600 + }, + { + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 600 + } + ], + "priority": 0 + }, + "status": { + "phase": "Running", + "conditions": [ + { + "type": "Initialized", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2020-03-26T16:34:39Z" + }, + { + "type": "Ready", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2020-03-26T16:34:43Z" + }, + { + "type": "ContainersReady", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2020-03-26T16:34:43Z" + }, + { + "type": "PodScheduled", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2020-03-26T16:34:39Z" + } + ], + "hostIP": "10.135.67.145", + "podIP": "172.30.90.91", + "startTime": "2020-03-26T16:34:39Z", + "containerStatuses": [ + { + "name": "jwt-sidecar", + "state": { + "running": { + "startedAt": "2020-03-26T16:34:43Z" + } + }, + "lastState": {}, + "ready": true, + "restartCount": 0, + "image": "docker.io/trustedseriviceidentity/ti-jwt-sidecar:v1.3", + "imageID": "docker.io/trustedseriviceidentity/ti-jwt-sidecar@sha256:2991b88e7e378112c72ddc4f2ac8e98c0b438b4d4b8d8556be2b5904428434db", + "containerID": "containerd://a73da0404c484e906109732a5f0b4e0f38d2fc41c0d09112b23a56d4fd793e26" + }, + { + "name": "myubuntu", + "state": { + "running": { + "startedAt": "2020-03-26T16:34:42Z" + } + }, + "lastState": {}, + "ready": true, + "restartCount": 0, + "image": "sha256:549b9b86cb8d75a2b668c21c50ee092716d070f129fd1493f95ab7e43767eab8", + "imageID": "docker.io/library/ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4", + "containerID": "containerd://944c5b65590813ad7edfca393bdc49e00abed99bb0a22b2ccbc0e01337bbdec0" + } + ], + "qosClass": "BestEffort" + } +} diff --git a/tests/FakeIsSafeUpdateOK.json b/tests/FakeIsSafeUpdateOK.json new file mode 100644 index 00000000..cc05a125 --- /dev/null +++ b/tests/FakeIsSafeUpdateOK.json @@ -0,0 +1,291 @@ +{ + "kind": "Pod", + "apiVersion": "v1", + "metadata": { + "name": "myubuntu-79bb88676b-qt222", + "generateName": "myubuntu-79bb88676b-", + "namespace": "trusted-identity", + "uid": "b039281d-6f7f-11ea-be8b-6a908431fac0", + "resourceVersion": "45012171", + "creationTimestamp": "2020-03-26T16:34:39Z", + "labels": { + "app": "myubuntu", + "pod-template-hash": "79bb88676b" + }, + "annotations": { + "admission.trusted.identity/inject": "true", + "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", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "kind": "ReplicaSet", + "name": "myubuntu-79bb88676b", + "uid": "b0272e4e-6f7f-11ea-be8b-6a908431fac0", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "volumes": [ + { + "name": "default-token-sggvc", + "secret": { + "secretName": "default-token-sggvc", + "defaultMode": 420 + } + }, + { + "name": "tsi-secrets", + "emptyDir": {} + }, + { + "name": "tsi-sockets", + "hostPath": { + "path": "/tsi-secure/sockets", + "type": "Directory" + } + }, + { + "name": "host-etc", + "hostPath": { + "path": "/etc", + "type": "Directory" + } + }, + { + "name": "pod-metadata", + "downwardAPI": { + "items": [ + { + "path": "tsi-secrets", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['tsi.secrets']" + } + }, + { + "path": "ti-identity", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-identity']" + } + }, + { + "path": "ti-pod-name", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + }, + { + "path": "ti-pod-namespace", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + }, + { + "path": "ti-images", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-images']" + } + }, + { + "path": "ti-cluster-name", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-cluster-name']" + } + }, + { + "path": "ti-cluster-region", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.annotations['admission.trusted.identity/ti-cluster-region']" + } + } + ], + "defaultMode": 420 + } + } + ], + "containers": [ + { + "name": "myubuntu", + "image": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4", + "command": [ + "/bin/bash", + "-c", + "--" + ], + "args": [ + "while true; do cat /mydata; sleep 5; done;" + ], + "resources": {}, + "volumeMounts": [ + { + "name": "default-token-sggvc", + "readOnly": true, + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" + }, + { + "name": "tsi-secrets", + "readOnly": true, + "mountPath": "/tsi-secrets" + } + ], + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + }, + { + "name": "jwt-sidecar", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.3", + "env": [ + { + "name": "HOST_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.hostIP" + } + } + }, + { + "name": "JWT_TTL_SEC", + "value": "30" + }, + { + "name": "VAULT_ADDR", + "value": "http://ti-test1.eu-de.containers.appdomain.cloud" + }, + { + "name": "SECRET_REFRESH_SEC", + "value": "45" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "tsi-secrets", + "mountPath": "/usr/share/secrets" + }, + { + "name": "tsi-sockets", + "mountPath": "/host/sockets" + }, + { + "name": "pod-metadata", + "readOnly": true, + "mountPath": "/pod-metadata" + }, + { + "name": "host-etc", + "readOnly": true, + "mountPath": "/host/etc" + } + ], + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always", + "securityContext": { + "runAsUser": 0 + } + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "default", + "serviceAccount": "default", + "nodeName": "10.135.67.145", + "securityContext": {}, + "schedulerName": "default-scheduler", + "tolerations": [ + { + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 600 + }, + { + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "effect": "NoExecute", + "tolerationSeconds": 600 + } + ], + "priority": 0 + }, + "status": { + "phase": "Running", + "conditions": [ + { + "type": "Initialized", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2020-03-26T16:34:39Z" + }, + { + "type": "Ready", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2020-03-26T16:34:43Z" + }, + { + "type": "ContainersReady", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2020-03-26T16:34:43Z" + }, + { + "type": "PodScheduled", + "status": "True", + "lastProbeTime": null, + "lastTransitionTime": "2020-03-26T16:34:39Z" + } + ], + "hostIP": "10.135.67.145", + "podIP": "172.30.90.91", + "startTime": "2020-03-26T16:34:39Z", + "containerStatuses": [ + { + "name": "jwt-sidecar", + "state": { + "running": { + "startedAt": "2020-03-26T16:34:43Z" + } + }, + "lastState": {}, + "ready": true, + "restartCount": 0, + "image": "docker.io/trustedseriviceidentity/ti-jwt-sidecar:v1.3", + "imageID": "docker.io/trustedseriviceidentity/ti-jwt-sidecar@sha256:2991b88e7e378112c72ddc4f2ac8e98c0b438b4d4b8d8556be2b5904428434db", + "containerID": "containerd://a73da0404c484e906109732a5f0b4e0f38d2fc41c0d09112b23a56d4fd793e26" + }, + { + "name": "myubuntu", + "state": { + "running": { + "startedAt": "2020-03-26T16:34:42Z" + } + }, + "lastState": {}, + "ready": true, + "restartCount": 0, + "image": "sha256:549b9b86cb8d75a2b668c21c50ee092716d070f129fd1493f95ab7e43767eab8", + "imageID": "docker.io/library/ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4", + "containerID": "containerd://944c5b65590813ad7edfca393bdc49e00abed99bb0a22b2ccbc0e01337bbdec0" + } + ], + "qosClass": "BestEffort" + } +} diff --git a/tests/FakeMutationNotRequired1.json b/tests/FakeMutationNotRequired1.json new file mode 100644 index 00000000..98498368 --- /dev/null +++ b/tests/FakeMutationNotRequired1.json @@ -0,0 +1,22 @@ +{ + "generateName": "myubuntu-79bb88676b-", + "creationTimestamp": null, + "labels": { + "app": "myubuntu", + "pod-template-hash": "79bb88676b" + }, + "annotations": { + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "kind": "ReplicaSet", + "name": "myubuntu-79bb88676b", + "uid": "d583bc29-6f81-11ea-be8b-6a908431fac0", + "controller": true, + "blockOwnerDeletion": true + } + ] +} diff --git a/tests/FakeMutationNotRequired2.json b/tests/FakeMutationNotRequired2.json new file mode 100644 index 00000000..f4f11547 --- /dev/null +++ b/tests/FakeMutationNotRequired2.json @@ -0,0 +1,23 @@ +{ + "generateName": "myubuntu-79bb88676b-", + "creationTimestamp": null, + "labels": { + "app": "myubuntu", + "pod-template-hash": "79bb88676b" + }, + "annotations": { + "admission.trusted.identity/inject": "false", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "kind": "ReplicaSet", + "name": "myubuntu-79bb88676b", + "uid": "d583bc29-6f81-11ea-be8b-6a908431fac0", + "controller": true, + "blockOwnerDeletion": true + } + ] +} diff --git a/tests/FakeMutationRequired.json b/tests/FakeMutationRequired.json new file mode 100644 index 00000000..dd91ade5 --- /dev/null +++ b/tests/FakeMutationRequired.json @@ -0,0 +1,23 @@ +{ + "generateName": "myubuntu-79bb88676b-", + "creationTimestamp": null, + "labels": { + "app": "myubuntu", + "pod-template-hash": "79bb88676b" + }, + "annotations": { + "admission.trusted.identity/inject": "true", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "kind": "ReplicaSet", + "name": "myubuntu-79bb88676b", + "uid": "d583bc29-6f81-11ea-be8b-6a908431fac0", + "controller": true, + "blockOwnerDeletion": true + } + ] +} diff --git a/tests/FakeTsiMutateConfig.json b/tests/FakeTsiMutateConfig.json index 1bd515bc..b73cfff6 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.2", + "image": "trustedseriviceidentity/ti-gen-vault-cert:v1.3", "resources": {}, "volumeMounts": [ { @@ -43,7 +43,7 @@ "SidecarContainers": [ { "name": "jwt-sidecar", - "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.2", + "image": "trustedseriviceidentity/ti-jwt-sidecar:v1.3", "command": [ "/bin/sh", "-c", diff --git a/tests/FakeUpdateAnnotation.json b/tests/FakeUpdateAnnotation.json new file mode 100644 index 00000000..df53b1fe --- /dev/null +++ b/tests/FakeUpdateAnnotation.json @@ -0,0 +1,6 @@ +{ + "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" +} diff --git a/tests/FakeUpdateAnnotation2.json b/tests/FakeUpdateAnnotation2.json new file mode 100644 index 00000000..2beeca7d --- /dev/null +++ b/tests/FakeUpdateAnnotation2.json @@ -0,0 +1,6 @@ +{ + "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.3" +} diff --git a/tests/FakeUpdateAnnotationError.json b/tests/FakeUpdateAnnotationError.json new file mode 100644 index 00000000..a91f606d --- /dev/null +++ b/tests/FakeUpdateAnnotationError.json @@ -0,0 +1,9 @@ +{ + "admission.trusted.identity/inject": "true", + "admission.trusted.identity/status": "mutated", + "admission.trusted.identity/ti-cluster-name": "ti-test1xxx", + "admission.trusted.identity/ti-cluster-region": "eu-de", + "admission.trusted.identity/ti-images": "ubuntu@sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" +} diff --git a/tests/FakeUpdateAnnotationTarget.json b/tests/FakeUpdateAnnotationTarget.json new file mode 100644 index 00000000..1d61d100 --- /dev/null +++ b/tests/FakeUpdateAnnotationTarget.json @@ -0,0 +1,5 @@ +{ + "admission.trusted.identity/inject": "true", + "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-name: \"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-name: \"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-name: \"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-name: \"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-name: \"mysecrets/non-existing\"\n" +} diff --git a/webhook.go b/webhook.go index 1a3455b7..e3eb6ca1 100644 --- a/webhook.go +++ b/webhook.go @@ -3,6 +3,7 @@ package main import ( "crypto/sha256" "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -46,6 +47,11 @@ const ( admissionWebhookAnnotationClusterRegion = "admission.trusted.identity/ti-cluster-region" ) +var ( + ErrHostpathSocket = errors.New("Using hostPath Volume with '/tsi-secure' is not allowed") + ErrSidecarImg = errors.New("Attempt to modify the sidecar image") +) + type WebhookServer struct { tsiMutateConfig *tsiMutateConfig server *http.Server @@ -61,7 +67,7 @@ type cigKube struct { } func NewCigKube() (*cigKube, error) { - glog.Infof("Getting cluster Config") + glog.Info("Getting cluster Config") kubeConf, err := rest.InClusterConfig() if err != nil { glog.Infof("Err: %v", err) @@ -174,8 +180,56 @@ func loadtsiMutateConfig(configFile string) (*tsiMutateConfig, error) { return &cfg, nil } +// isSafe validates the format of the request to check if the requested +// operation is permitted. For CREATE operations, it checks if the request +// contains references to proteced socket and /etc Volumes. +// And since UPDATE, does not allowed volume modifications, it only makes sure +// that sidecar image has not been modified +// it returns an error if operation is not permitted +func isSafe(pod *corev1.Pod, operationType string) error { + + // this output can be used for creating tests/FakeIsSafeXXX.json files + glog.Infof("isSafe log. Operation %v", operationType) + // logJSON("FakeIsSafeX.json", pod) + + vols := pod.Spec.Volumes + + switch operationType { + case "CREATE": + for _, v := range vols { + // glog.Infof("***** VNAME: %v VHOSTPATH: %v", v.Name, v.HostPath) + if v.HostPath != nil && strings.Contains(v.HostPath.Path, "/tsi-secure") { + return ErrHostpathSocket + } + } + case "UPDATE": + // volumes cannot be added nor modified on the update, but + // we need to prevent image change for the sidecar + conts := pod.Spec.Containers + for _, c := range conts { + // glog.Infof("****** CNAME: %v, CIMG: %v, CVOLUMEMOUNTS: %v", c.Name, c.Image, c.VolumeMounts) + if c.Name == "jwt-sidecar" { + // extract the image name, skip the version label + // trustedseriviceidentity/ti-jwt-sidecar:v1.3 + img := strings.Split(c.Image, ":") + if img[0] == "trustedseriviceidentity/ti-jwt-sidecar" { + glog.Infof("Sidecar image matches! %v", c.Image) + } else { + return ErrSidecarImg + } + } + } + } + return nil +} + // Check whether the target resoured need to be mutated func mutationRequired(ignoredList []string, metadata *metav1.ObjectMeta) bool { + + // this output can be used for creating tests/FakeMutationRequiredXXX.json files + glog.Infof("mutationRequired log. ignoredList %#v", ignoredList) + // logJSON("FakeMutationRequired.json", metadata) + // skip special kubernete system namespaces for _, namespace := range ignoredList { if metadata.Namespace == namespace { @@ -197,12 +251,14 @@ func mutationRequired(ignoredList []string, metadata *metav1.ObjectMeta) bool { case "y", "yes", "true", "on": required = true } - - glog.Infof("Mutation policy for %v/%v: required:%v", metadata.Namespace, metadata.Name, required) return required } func addContainer(target, added []corev1.Container, basePath string) (patch []patchOperation) { + // this output can be used for creating tests/FakeAddContainer.json files + // logJSON("FakeAddContainerTarget.json", target) + // logJSON("FakeAddContainer.json", added) + first := len(target) == 0 var value interface{} for _, add := range added { @@ -220,10 +276,18 @@ func addContainer(target, added []corev1.Container, basePath string) (patch []pa Value: value, }) } + + // this output can be used for creating tests/ExpectedAddContainer.json file + // logJSON("ExpectAddContainer.json", patch) return patch } func addVolume(target, added []corev1.Volume, basePath string) (patch []patchOperation) { + + // this output can be used for creating tests/FakeAddVolume.json files + // logJSON("FakeAddVolumeTarget.json", target) + // logJSON("FakeAddVolume.json", added) + first := len(target) == 0 var value interface{} for _, add := range added { @@ -241,10 +305,16 @@ func addVolume(target, added []corev1.Volume, basePath string) (patch []patchOpe Value: value, }) } + // this output can be used for creating tests/ExpectedVolumeM.json file + // logJSON("ExpectAddVolume.json", patch) return patch } func addVolumeMount(target, added []corev1.VolumeMount, basePath string) (patch []patchOperation) { + // this output can be used for creating tests/FakeVolumeMount.json files + // logJSON("FakeAddVolumeMountTarget.json", target) + // logJSON("FakeAddVolumeMount.json", added) + first := len(target) == 0 var value interface{} for _, add := range added { @@ -262,11 +332,17 @@ func addVolumeMount(target, added []corev1.VolumeMount, basePath string) (patch Value: value, }) } + // this output can be used for creating tests/ExpectedVolumeMount.json file + // logJSON("ExpectAddVolumeMount.json", patch) return patch } func updateAnnotation(target map[string]string, added map[string]string) (patch []patchOperation) { + // this output can be used for creating tests/FakeUpdateAnnotation.json files + // logJSON("FakeUpdateAnnotationTarget.json", target) + // logJSON("FakeUpdateAnnotation.json", added) + // cannot add individual path values. Must add the entire Annotation object // so add/replace new values then patch it all at once if target == nil { @@ -281,6 +357,9 @@ func updateAnnotation(target map[string]string, added map[string]string) (patch Path: "/metadata/annotations", Value: target, }) + + // this output can be used for creating tests/ExpectedUpdateAnnotation.json files + // logJSON("ExpectedUpdateAnnotation.json", patch) return patch } @@ -401,7 +480,6 @@ func createPatch(pod *corev1.Pod, tsiMutateConfig *tsiMutateConfig) ([]byte, err patch = append(patch, addVolume(pod.Spec.Volumes, tsiMutateConfig.Volumes, "/spec/volumes")...) for i, c := range pod.Spec.Containers { - glog.Infof("add vol mounts : %#v", addVolumeMount(c.VolumeMounts, tsiMutateConfig.AddVolumeMounts, fmt.Sprintf("/spec/containers/%d/volumeMounts", i))) patch = append(patch, addVolumeMount(c.VolumeMounts, tsiMutateConfig.AddVolumeMounts, fmt.Sprintf("/spec/containers/%d/volumeMounts", i))...) } } @@ -429,9 +507,25 @@ func (whsvr *WebhookServer) mutate(ar *v1beta1.AdmissionReview) *v1beta1.Admissi glog.Infof("AdmissionReview for Kind=%v, Namespace=%v Name=%v (%v) UID=%v patchOperation=%v UserInfo=%v", req.Kind, req.Namespace, req.Name, pod.Name, req.UID, req.Operation, req.UserInfo) + err := isSafe(&pod, string(req.Operation)) + + if err != nil { + glog.Error(err.Error()) + glog.Infof("Not safe to continue with %v for pod: %v/%v. Disallowing", req.Operation, pod.GenerateName, req.Namespace) + reason := metav1.StatusReason("TSI Mutation Webhook disallowed this pod creation for safety reasons") + return &v1beta1.AdmissionResponse{ + Allowed: false, + Result: &metav1.Status{ + Message: err.Error(), + Reason: reason, + }, + } + } + glog.Infof("Safe to continue with %v/%#v", pod.GenerateName, req.Namespace) + // determine whether to perform mutation if !mutationRequired(ignoredNamespaces, &pod.ObjectMeta) { - glog.Infof("Skipping mutation for %s/%s due to policy check", pod.Namespace, pod.Name) + glog.Infof("Skipping mutation for %s/%s due to policy check", pod.GenerateName, req.Namespace) return &v1beta1.AdmissionResponse{ Allowed: true, } @@ -521,13 +615,13 @@ func (whsvr *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { resp, err := json.Marshal(admissionReview) if err != nil { - glog.Errorf("Can't encode response: %v", err) - http.Error(w, fmt.Sprintf("could not encode response: %v", err), http.StatusInternalServerError) + glog.Errorf("Can't encode the response: %v", err) + http.Error(w, fmt.Sprintf("could not encode the response: %v", err), http.StatusInternalServerError) } - glog.Infof("Ready to write reponse ...") + glog.Infof("Ready to write the reponse ...") if _, err := w.Write(resp); err != nil { glog.Errorf("Can't write response: %v", err) - http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("could not write the response: %v", err), http.StatusInternalServerError) } } diff --git a/webhook_test.go b/webhook_test.go index 0dd64fc0..2248d571 100644 --- a/webhook_test.go +++ b/webhook_test.go @@ -2,7 +2,7 @@ package main import ( "encoding/json" - "flag" + "errors" "fmt" "io/ioutil" "net/http" @@ -13,6 +13,7 @@ import ( ctiv1 "github.com/IBM/trusted-service-identity/pkg/apis/cti/v1" "github.com/nsf/jsondiff" "k8s.io/api/admission/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) /* @@ -26,18 +27,24 @@ var admissionRequest v1beta1.AdmissionRequest var admissionResponse v1beta1.AdmissionResponse var admissionReview v1beta1.AdmissionReview +const ( + SUCCESS = "Testing %s successful" + ERROR = "Testing %s failed" + ERRORWITH = "Testing %s failed with %s" +) + func init() { // Uncomment out the code below to display messages from "glog" - flag.Set("alsologtostderr", fmt.Sprintf("%t", true)) - var logLevel string - flag.StringVar(&logLevel, "logLevel", "5", "test") - flag.Parse() - flag.Lookup("v").Value.Set(logLevel) - fmt.Println("Argument '-logLevel' is ", logLevel) + //flag.Set("alsologtostderr", fmt.Sprintf("%t", true)) + //var logLevel string + //flag.StringVar(&logLevel, "logLevel", "5", "test") + //flag.Parse() + // //flag.Lookup("v").Value.Set(logLevel) + // fmt.Println("Argument '-logLevel' is ", logLevel) // load K8s objects and unmarshal them to test the API format - pod = getFakePod() + pod = getFakePod("tests/FakePod.json") admissionRequest = getFakeAdmissionRequest() admissionResponse = getFakeAdmissionResponse() admissionReview = getFakeAdmissionReview() @@ -58,20 +65,20 @@ func (ck *cigKubeTest) GetClusterTI(namespace, name string) (ctiv1.ClusterTI, er } // TestLoadInitFile - tests loadtsiMutateConfig method from webhook.go. Validates the output -func TestLoadInitFile(t *testing.T) { +func TestLoadConfigFile(t *testing.T) { + testName := "load config file" icc, err := loadtsiMutateConfig("tests/ConfigFile.yaml") if err != nil { - t.Errorf("Error loading tsiMutateConfig %v", err) + t.Errorf(ERRORWITH, testName, err) return } err = validateResult(icc, "tests/ExpectTsiMutateConfig.json") if err != nil { - t.Errorf("Result failed: %v", err) + t.Errorf(ERRORWITH, testName, err) return } - t.Log("Results match expections") - + t.Logf(SUCCESS, testName) } // TestPseudoUUID - testing function to generate UUIDs @@ -84,9 +91,160 @@ func TestLoadInitFile(t *testing.T) { // t.Logf("UUID: %v", uuid) //} +func TestIsSafe(t *testing.T) { + // test 1 Create OK + testName := "CREATE with safe pod" + tpod := getFakePod("tests/FakeIsSafeCreateOK.json") + err := isSafe(&tpod, "CREATE") + if err == nil { + t.Logf(SUCCESS, testName) + } else { + t.Errorf(ERROR, testName) + } + + // test 2 Create Error + testName = "CREATE with non-safe pod" + tpod = getFakePod("tests/FakeIsSafeCreateError.json") + err = isSafe(&tpod, "CREATE") + if err != nil && errors.Is(err, ErrHostpathSocket) { + t.Logf(SUCCESS, testName) + } else { + t.Fatalf(ERROR, testName) + } + + // test 3 Update OK + testName = "UPDATE with safe pod" + tpod = getFakePod("tests/FakeIsSafeUpdateOK.json") + err = isSafe(&tpod, "UPDATE") + if err == nil { + t.Logf(SUCCESS, testName) + } else { + t.Errorf(ERROR, testName) + } + + // test 4 Update Error + testName = "UPDATE with non-safe pod" + tpod = getFakePod("tests/FakeIsSafeUpdateError.json") + err = isSafe(&tpod, "UPDATE") + if err != nil && errors.Is(err, ErrSidecarImg) { + t.Logf(SUCCESS, testName) + } else { + t.Fatalf(ERROR, testName) + } + +} + +func TestMutationRequired(t *testing.T) { + + // testing if pad that should be mutated + testName := "mutation required" + meta := getFakeMetadata("tests/FakeMutationRequired.json") + ignoredNamespaces := []string{"kube-system", "kube-public"} + if mutationRequired(ignoredNamespaces, &meta) { + t.Logf(SUCCESS, testName) + t.Logf("Testing mutating metadata successful") + } else { + t.Errorf(ERROR, testName) + t.Error("Testing mutating metadata failed") + } + + // testing pod with "admission.trusted.identity/inject": "false" + testName = "mutation not required [1]" + meta = getFakeMetadata("tests/FakeMutationNotRequired1.json") + ignoredNamespaces = []string{"kube-system", "kube-public"} + if !mutationRequired(ignoredNamespaces, &meta) { + t.Logf(SUCCESS, testName) + } else { + t.Errorf(ERROR, testName) + } + + // testing pod with missing "admission.trusted.identity/inject" + testName = "mutation not required [2]" + meta = getFakeMetadata("tests/FakeMutationNotRequired2.json") + ignoredNamespaces = []string{"kube-system", "kube-public"} + if !mutationRequired(ignoredNamespaces, &meta) { + t.Logf(SUCCESS, testName) + } else { + t.Errorf(ERROR, testName) + } +} + +func TestUpdateAnnotation(t *testing.T) { + + // test 1, standard create + testName := "update standard annotations" + target := getFakeAnnotation("tests/FakeUpdateAnnotationTarget.json") + added := getFakeAnnotation("tests/FakeUpdateAnnotation.json") + result := updateAnnotation(target, added) + + err := validateResult(result, "tests/ExpectUpdateAnnotation.json") + if err != nil { + t.Fatalf(ERROR, testName) + return + } + t.Logf(SUCCESS, testName) + + // test 2, UPDATE to force incorrect annotations + testName = "update error annotations" + target = getFakeAnnotation("tests/FakeUpdateAnnotationError.json") + added = getFakeAnnotation("tests/FakeUpdateAnnotation2.json") + result = updateAnnotation(target, added) + + err = validateResult(result, "tests/ExpectUpdateAnnotation2.json") + if err != nil { + t.Errorf(ERROR, testName) + return + } + t.Logf(SUCCESS, testName) +} + +func TestAddContainer(t *testing.T) { + testName := "add container" + target := getFakeContainers("tests/FakeAddContainerTarget.json") + add := getFakeContainers("tests/FakeAddContainer.json") + result := addContainer(target, add, "/spec/containers") + + err := validateResult(result, "tests/ExpectAddContainer.json") + if err != nil { + t.Errorf(ERRORWITH, testName, err) + return + } + t.Logf(SUCCESS, testName) +} + +func TestAddVolume(t *testing.T) { + testName := "add volume" + target := getFakeVolume("tests/FakeAddVolumeTarget.json") + add := getFakeVolume("tests/FakeAddVolume.json") + result := addVolume(target, add, "/spec/volumes") + + err := validateResult(result, "tests/ExpectAddVolume.json") + if err != nil { + t.Errorf(ERRORWITH, testName, err) + return + } + t.Logf(SUCCESS, testName) +} + // TestMutateInitialization - tests the results of calling `mutateInitialization` in webhook func TestMutateInitialization(t *testing.T) { + testName := "load config file" + icc, err := loadtsiMutateConfig("tests/ConfigFile.yaml") + if err != nil { + t.Errorf(ERRORWITH, testName, err) + return + } + + err = validateResult(icc, "tests/ExpectTsiMutateConfig.json") + if err != nil { + t.Errorf(ERRORWITH, testName, err) + return + } else { + t.Logf(SUCCESS, testName) + } + + testName = "mutate initialization" ret := ctiv1.ClusterTI{ Info: ctiv1.ClusterTISpec{ ClusterName: "testCluster", @@ -95,12 +253,6 @@ func TestMutateInitialization(t *testing.T) { } clInfo := newCigKubeTest(ret) - icc, err := loadtsiMutateConfig("tests/ConfigFile.yaml") - if err != nil { - t.Errorf("Error loading tsiMutateConfig %v", err) - return - } - whsvr := &WebhookServer{ tsiMutateConfig: icc, server: &http.Server{}, @@ -110,16 +262,16 @@ func TestMutateInitialization(t *testing.T) { // get test result of running mutateInitialization method: result, err := whsvr.mutateInitialization(pod, &admissionRequest) if err != nil { - t.Errorf("Error executing mutateInitialization %v", err) + t.Errorf(ERRORWITH, testName, err) return } err = validateResult(result, "tests/ExpectMutateInit.json") if err != nil { - t.Errorf("Result failed: %v", err) + t.Errorf(ERRORWITH, testName, err) return } - t.Logf("Results match expections") + t.Logf(SUCCESS, testName) } func getContentOfTheFile(file string) string { @@ -130,17 +282,73 @@ func getContentOfTheFile(file string) string { return string(dat) } -func getFakePod() corev1.Pod { - s := getContentOfTheFile("tests/FakePod.json") +func getFakePod(file string) corev1.Pod { + s := getContentOfTheFile(file) pod := corev1.Pod{} - json.Unmarshal([]byte(s), &pod) + err := json.Unmarshal([]byte(s), &pod) + if err != nil { + panic(err) + } return pod } +func getFakeContainers(file string) []corev1.Container { + s := getContentOfTheFile(file) + c := []corev1.Container{} + err := json.Unmarshal([]byte(s), &c) + if err != nil { + panic(err) + } + return c +} + +func getFakeVolume(file string) []corev1.Volume { + s := getContentOfTheFile(file) + vol := []corev1.Volume{} + err := json.Unmarshal([]byte(s), &vol) + if err != nil { + panic(err) + } + return vol +} + +func getFakeVolumeMount(file string) []corev1.VolumeMount { + s := getContentOfTheFile(file) + vol := []corev1.VolumeMount{} + err := json.Unmarshal([]byte(s), &vol) + if err != nil { + panic(err) + } + return vol +} + +func getFakeMetadata(file string) metav1.ObjectMeta { + s := getContentOfTheFile(file) + meta := metav1.ObjectMeta{} + err := json.Unmarshal([]byte(s), &meta) + if err != nil { + panic(err) + } + return meta +} + +func getFakeAnnotation(file string) map[string]string { + s := getContentOfTheFile(file) + ann := make(map[string]string) + err := json.Unmarshal([]byte(s), &ann) + if err != nil { + panic(err) + } + return ann +} + func getFakeAdmissionRequest() v1beta1.AdmissionRequest { s := getContentOfTheFile("tests/FakeAdmissionRequest.json") ar := v1beta1.AdmissionRequest{} - json.Unmarshal([]byte(s), &ar) + err := json.Unmarshal([]byte(s), &ar) + if err != nil { + panic(err) + } return ar } @@ -154,14 +362,20 @@ func getFakeAdmissionResponse() v1beta1.AdmissionResponse { func getFakeAdmissionReview() v1beta1.AdmissionReview { s := getContentOfTheFile("tests/FakeAdmissionReview.json") ar := v1beta1.AdmissionReview{} - json.Unmarshal([]byte(s), &ar) + err := json.Unmarshal([]byte(s), &ar) + if err != nil { + panic(err) + } return ar } func getTsiMutateConfig() tsiMutateConfig { s := getContentOfTheFile("tests/FakeTsiMutateConfig.json") obj := tsiMutateConfig{} - json.Unmarshal([]byte(s), &obj) + err := json.Unmarshal([]byte(s), &obj) + if err != nil { + panic(err) + } return obj } @@ -195,7 +409,7 @@ func validateResult(r interface{}, expectedFile string) error { opts := jsondiff.DefaultHTMLOptions() diff, text := jsondiff.Compare(result, exp, &opts) if diff == jsondiff.FullMatch { - fmt.Printf("Results match expections: %v", diff) + // fmt.Printf("Results match expections: %v", diff) return nil } return fmt.Errorf("Results do not match expections. diff: %v text: %v", diff, text)