diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 000000000..a30b7eae4 --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +description: An open source, git-ops, zero-trust secrets encryption and decryption solution for Kubernetes applications +name: kamus +version: 0.1.0 +keywords: + - gitops + - secrets +sources: + - https://github.com/Soluto/Kamus +maintainers: + - name: Omer Levi Hevroni + - name: Shai Katz diff --git a/chart/templates/NOTES.txt b/chart/templates/NOTES.txt new file mode 100644 index 000000000..fd13fe4ea --- /dev/null +++ b/chart/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http://{{ . }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "kamus.name" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "kamus.name" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "kamus.name" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.externalPort }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "kamus.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:{{ .Values.service.internalPort }} +{{- end }} diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl new file mode 100644 index 000000000..5f0dfb210 --- /dev/null +++ b/chart/templates/_helpers.tpl @@ -0,0 +1,15 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "kamus.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "appsettings.secret.json" }} +{{ printf "{\n\t\"ActiveDirectory\": { " }} +{{ if .Values.activeDirectory.clientSecret}} +{{ printf "\t\t\"ClientSecret\": \"%s\" " .Values.activeDirectory.clientSecret }} +{{- end -}} +{{ printf "} \n}"}} +{{- end }} \ No newline at end of file diff --git a/chart/templates/autoscaling-decryptor.yaml b/chart/templates/autoscaling-decryptor.yaml new file mode 100644 index 000000000..5efe0421f --- /dev/null +++ b/chart/templates/autoscaling-decryptor.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ template "kamus.name" . }}-decryptor + namespace: {{ .Values.team }} + labels: + app: {{ template "kamus.name" . }} + component: decryptor + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + scaleTargetRef: + apiVersion: apps/v1beta1 + kind: Deployment + name: {{ template "kamus.name" . }}-decryptor + minReplicas: {{ .Values.autoscale.minReplicas }} + maxReplicas: {{ .Values.autoscale.maxReplicas }} + targetCPUUtilizationPercentage: {{ .Values.autoscale.targetCPU }} \ No newline at end of file diff --git a/chart/templates/autoscaling-encryptor.yaml b/chart/templates/autoscaling-encryptor.yaml new file mode 100644 index 000000000..48187fdb7 --- /dev/null +++ b/chart/templates/autoscaling-encryptor.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ template "kamus.name" . }}-encryptor + namespace: {{ .Values.team }} + labels: + app: {{ template "kamus.name" . }} + component: encryptor + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + scaleTargetRef: + apiVersion: apps/v1beta1 + kind: Deployment + name: {{ template "kamus.name" . }}-encryptor + minReplicas: {{ .Values.autoscale.minReplicas }} + maxReplicas: {{ .Values.autoscale.maxReplicas }} + targetCPUUtilizationPercentage: {{ .Values.autoscale.targetCPU }} \ No newline at end of file diff --git a/chart/templates/configmap-decryptor.yaml b/chart/templates/configmap-decryptor.yaml new file mode 100644 index 000000000..97127906c --- /dev/null +++ b/chart/templates/configmap-decryptor.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "kamus.name" . }}-decryptor + namespace: {{ .Values.team }} +data: +{{- if .Values.config }} +{{ toYaml .Values.config | trimSuffix "\n" | indent 2 }} +{{- end }} \ No newline at end of file diff --git a/chart/templates/configmap-encryptor.yaml b/chart/templates/configmap-encryptor.yaml new file mode 100644 index 000000000..0be968b67 --- /dev/null +++ b/chart/templates/configmap-encryptor.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "kamus.name" . }}-encryptor + namespace: {{ .Values.team }} +data: +{{- if .Values.config }} +{{ toYaml .Values.config | trimSuffix "\n" | indent 2 }} +{{- end }} \ No newline at end of file diff --git a/chart/templates/deployment-decryptor.yaml b/chart/templates/deployment-decryptor.yaml new file mode 100644 index 000000000..79a841fcf --- /dev/null +++ b/chart/templates/deployment-decryptor.yaml @@ -0,0 +1,65 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ template "kamus.name" . }}-decryptor + namespace: {{ .Values.team }} + labels: + app: {{ template "kamus.name" . }} + component: decryptor + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + strategy: + rollingUpdate: + maxUnavailable: {{ .Values.maxUnavailable }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "kamus.name" . }} + release: {{ .Release.Name }} + component: decryptor + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + template: + metadata: + labels: + app: {{ template "kamus.name" . }} + release: {{ .Release.Name }} + component: decryptor + spec: + serviceAccountName: {{ template "kamus.name" . }} + automountServiceAccountToken: true + containers: + - name: decryptor-api + image: {{ .Values.image.repository }}/kamus:decryptor-{{ .Values.image.version }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: secret-volume + mountPath: /app/secrets + ports: + - containerPort: 9999 + livenessProbe: + httpGet: + path: /api/v1/isAlive + port: 9999 + readinessProbe: + httpGet: + path: /api/v1/isAlive + port: 9999 + resources: +{{ toYaml .Values.resources | indent 12 }} + envFrom: + - configMapRef: + name: {{ template "kamus.name" . }}-decryptor + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + - name: {{ toYaml .Values.imagePullSecrets }} + {{- end }} + volumes: + - name: secret-volume + secret: + secretName: {{ template "kamus.name" . }} + {{- if .Values.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} + {{- end }} diff --git a/chart/templates/deployment-encryptor.yaml b/chart/templates/deployment-encryptor.yaml new file mode 100644 index 000000000..ab7c42ff9 --- /dev/null +++ b/chart/templates/deployment-encryptor.yaml @@ -0,0 +1,100 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ template "kamus.name" . }}-encryptor + namespace: {{ .Values.team }} + labels: + app: {{ template "kamus.name" . }} + component: encryptor + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + strategy: + rollingUpdate: + maxUnavailable: {{ .Values.maxUnavailable }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "kamus.name" . }} + component: encryptor + release: {{ .Release.Name }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + template: + metadata: + labels: + app: {{ template "kamus.name" . }} + component: encryptor + release: {{ .Release.Name }} + spec: + serviceAccountName: {{ template "kamus.name" . }} + automountServiceAccountToken: false + containers: + - name: encryptor-api + image: {{ .Values.image.repository }}/kamus:decryptor-{{ .Values.image.version }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: secret-volume + mountPath: /app/secrets + ports: + - containerPort: 9999 + livenessProbe: + httpGet: + path: /api/v1/isAlive + port: 9999 + readinessProbe: + httpGet: + path: /api/v1/isAlive + port: 9999 + resources: +{{ toYaml .Values.resources | indent 12 }} + envFrom: + - configMapRef: + name: {{ template "kamus.name" . }}-encryptor + {{- if .Values.useAirbag }} + - name: "airbag" + image: "soluto/airbag:0.0.8" + ports: + - containerPort: {{ .Values.airbag.airbagPort }} + env: + - name: BACKEND_HOST_NAME + value: {{ .Values.airbag.backendHostName | quote }} + - name: BACKEND_SERVICE_PORT + value: "9999" + - name: UNAUTHENTICATED_ROUTES + value: '/api/v1/isAlive,/metrics' + - name: COLLECT_METRICS + value: {{ .Values.airbag.collectMetrics | quote }} + - name: ASPNETCORE_URLS + value: {{ print "http://+:" .Values.airbag.airbagPort }} + {{- if .Values.airbag.authority }} + - name: AUTHORITY + value: {{ .Values.airbag.authority | quote }} + - name: AUDIENCE + value: {{ .Values.airbag.audience | quote }} + - name: ISSUER + value: {{ .Values.airbag.issuer | quote }} + {{- end }} + livenessProbe: + httpGet: + path: {{ .Values.service.isAlivePath }} + port: {{ .Values.service.internalPort }} + readinessProbe: + httpGet: + path: {{ .Values.service.isAlivePath }} + port: {{ .Values.service.internalPort }} + resources: +{{ toYaml .Values.airbag.resources | indent 12 }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: + - name: {{ toYaml .Values.imagePullSecrets }} + {{- end }} + volumes: + - name: secret-volume + secret: + secretName: {{ template "kamus.name" . }} + {{- if .Values.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} + {{- end }} diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml new file mode 100644 index 000000000..3ebc6777e --- /dev/null +++ b/chart/templates/ingress.yaml @@ -0,0 +1,40 @@ +{{- if .Values.ingress.enabled -}} +{{- $serviceName := include "kamus.name" . -}} +{{- $servicePort := .Values.service.externalPort -}} +{{- $servicePath := .Values.ingress.path | default "/" -}} +{{- $tlsSecretName := .Values.ingress.tls.secretName -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "kamus.name" . }} + namespace: {{ .Values.team }} + labels: + app: {{ template "kamus.name" . }} + component: encryptor + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- if .Values.ingress.annotations }} + annotations: +{{ toYaml .Values.ingress.annotations | indent 4 }} +{{- end }} +spec: + rules: + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host }} + http: + paths: + - path: {{ $servicePath }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end }} + + tls: + - hosts: + {{- range $host := .Values.ingress.hosts }} + - {{ $host }} + {{- end }} + + secretName: {{ $tlsSecretName }} +{{- end -}} \ No newline at end of file diff --git a/chart/templates/role.yaml b/chart/templates/role.yaml new file mode 100644 index 000000000..cdf8e741f --- /dev/null +++ b/chart/templates/role.yaml @@ -0,0 +1,9 @@ +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + namespace: {{ .Values.team }} + name: {{ template "kamus.name" . }} +rules: +- apiGroups: ["authentication.k8s.io"] + resources: ["tokenreviews"] + verbs: ["create"] \ No newline at end of file diff --git a/chart/templates/rolebinding.yaml b/chart/templates/rolebinding.yaml new file mode 100644 index 000000000..85f47e3c3 --- /dev/null +++ b/chart/templates/rolebinding.yaml @@ -0,0 +1,13 @@ +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "kamus.name" . }} + namespace: {{ .Values.team }} +subjects: +- kind: ServiceAccount + name: {{ template "kamus.name" . }} + namespace: {{ .Values.team }} +roleRef: + kind: Role + name: {{ template "kamus.name" . }} + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/chart/templates/secret.yaml b/chart/templates/secret.yaml new file mode 100644 index 000000000..2615d677c --- /dev/null +++ b/chart/templates/secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + namespace: {{ .Values.team }} + name: {{ include "kamus.name" . }} +type: Opaque +data: + appsettings.secret.json: {{ include "appsettings.secret.json" . | b64enc}} \ No newline at end of file diff --git a/chart/templates/service-decryptor.yaml b/chart/templates/service-decryptor.yaml new file mode 100644 index 000000000..e92f503e1 --- /dev/null +++ b/chart/templates/service-decryptor.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "kamus.name" . }}-decryptor + namespace: {{ .Values.team }} +{{- if .Values.service.annotations }} + annotations: +{{ toYaml .Values.service.annotations | indent 4 }} +{{- end }} + labels: + app: {{ template "kamus.name" . }} + component: decryptor + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: 80 + targetPort: 9999 + protocol: TCP + name: {{ .Values.service.name }} + selector: + app: {{ template "kamus.name" . }} + component: decryptor + release: {{ .Release.Name }} diff --git a/chart/templates/service-encryptor.yaml b/chart/templates/service-encryptor.yaml new file mode 100644 index 000000000..bb6e15c0e --- /dev/null +++ b/chart/templates/service-encryptor.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "kamus.name" . }} + namespace: {{ .Values.team }} +{{- if .Values.service.annotations }} + annotations: +{{ toYaml .Values.service.annotations | indent 4 }} +{{- end }} + labels: + app: {{ template "kamus.name" . }}-decryptor + component: encryptor + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: 80 + {{- if .Values.useAirbag }} + targetPort: {{ .Values.airbag.airbagPort }} + {{- else }} + targetPort: 9999 + {{- end }} + protocol: TCP + name: {{ .Values.service.name }} + selector: + app: {{ template "kamus.name" . }} + component: encryptor + release: {{ .Release.Name }} diff --git a/chart/templates/serviceaccount.yaml b/chart/templates/serviceaccount.yaml new file mode 100644 index 000000000..635f1d808 --- /dev/null +++ b/chart/templates/serviceaccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "kamus.name" . }} + namespace: {{ .Values.team }} \ No newline at end of file diff --git a/chart/test.sh b/chart/test.sh new file mode 100755 index 000000000..ebed4986c --- /dev/null +++ b/chart/test.sh @@ -0,0 +1,54 @@ +set -e + +echo "Without airbag" +helm template . -f tests/values-without-airbag.yaml > template.yaml +cat template.yaml | kubetest -t tests/common --verbose +cat template.yaml | kubetest -t tests/without-airbag --verbose + +echo "With airbag" +helm template . -f tests/values-with-airbag.yaml > template.yaml +cat template.yaml | kubetest -t tests/common --verbose +cat template.yaml | kubetest -t tests/with-airbag --verbose + +echo "With annotations" +helm template . -f tests/values-with-annotations.yaml > template.yaml +cat template.yaml | kubetest -t tests/common --verbose +cat template.yaml | kubetest -t tests/with-annotations --verbose + +echo "With command and args" +helm template . -f tests/values-with-command-and-args.yaml > template.yaml +cat template.yaml | kubetest -t tests/common --verbose +cat template.yaml | kubetest -t tests/with-command-and-args --verbose + +echo "With custom probes" +helm template . -f tests/values-with-custom-probes.yaml > template.yaml +cat template.yaml | kubetest -t tests/common --verbose +cat template.yaml | kubetest -t tests/with-custom-probes --verbose + +echo "With role" +helm template . -f tests/values-with-role.yaml > template.yaml +cat template.yaml | kubetest -t tests/common --verbose +cat template.yaml | kubetest -t tests/with-role --verbose + +echo "with ingress path" +helm template . -f tests/values-with-ingress-path.yaml > template.yaml +cat template.yaml | kubetest -t tests/common --verbose +cat template.yaml | kubetest -t tests/with-ingress-path --verbose + +echo "with custom tls cert" +helm template . -f tests/values-with-custom-tls-cert.yaml > template.yaml +cat template.yaml | kubetest -t tests/common --verbose +cat template.yaml | kubetest -t tests/with-custom-tls-cert --verbose + +echo "with encrypted secrets" +helm template . -f tests/values-with-encrypted-secrets.yaml > template.yaml +cat template.yaml | kubetest -t tests/common --verbose +cat template.yaml | kubetest -t tests/with-encrypted-secrets --verbose + +echo "with long nameOverride" +helm template . -f tests/values-with-long-nameOverride.yaml > template.yaml +cat template.yaml | kubetest -t tests/with-long-nameOverride --verbose + +echo "with virtual service" +helm template . -f tests/values-with-virtual-service.yaml > template.yaml +cat template.yaml | kubetest -t tests/with-virtual-service --verbose diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 000000000..5cf316197 --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,59 @@ +# Default values for kamus. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +replicaCount: 1 +maxUnavailable: 0 +revisionHistoryLimit: 5 +useAirbag: true +airbag: + backendHostName: "localhost" + collectMetrics: "true" + airbagPort: "7000" + resources: + limits: + cpu: 500m + memory: 600Mi + requests: + cpu: 100m + memory: 128Mi +image: + version: 1.0 + repository: acreastus.azurecr.io + pullPolicy: IfNotPresent +service: + name: nginx + type: ClusterIP + externalPort: 80 + internalPort: 7000 + isAlivePath: /api/v1/isAlive + annotations: + prometheus.io/scrape: "true" +ingress: + enabled: false + path: / + # Used to create an Ingress record. + hosts: + - chart-example.local + annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + tls: + # Secrets must be manually created in the namespace. + secretName: <> + # hosts: + # - chart-example.local +resources: + limits: + cpu: 500m + memory: 600Mi + requests: + cpu: 100m + memory: 128Mi +autoscale: + minReplicas: 2 + maxReplicas: 10 + targetCPU: 50 +envFrom: {} +container: {} +activeDirectory: + clientSecret: test