Added x509 and jwt rapps
Change-Id: Ic384fcad11dcb63fe4265d3dbcff5ea17f933cfc
diff --git a/rapps/Dockerfile_rra b/rapps/Dockerfile_rra
new file mode 100644
index 0000000..6958e79
--- /dev/null
+++ b/rapps/Dockerfile_rra
@@ -0,0 +1,6 @@
+FROM golang:latest
+RUN mkdir /app
+COPY ./rapps-rapp-auth-provider /app
+RUN chmod +x /app/rapps-rapp-auth-provider
+WORKDIR /app
+ENTRYPOINT ["/app/rapps-rapp-auth-provider"]
diff --git a/rapps/Dockerfile_rrji b/rapps/Dockerfile_rrji
new file mode 100644
index 0000000..238d90b
--- /dev/null
+++ b/rapps/Dockerfile_rrji
@@ -0,0 +1,6 @@
+FROM golang:latest
+RUN mkdir /app
+COPY ./rapps-rapp-jwt-invoker /app
+RUN chmod +x /app/rapps-rapp-jwt-invoker
+WORKDIR /app
+ENTRYPOINT ["/app/rapps-rapp-jwt-invoker"]
diff --git a/rapps/Dockerfile_rrjp b/rapps/Dockerfile_rrjp
new file mode 100644
index 0000000..e109a8f
--- /dev/null
+++ b/rapps/Dockerfile_rrjp
@@ -0,0 +1,6 @@
+FROM golang:latest
+RUN mkdir /app
+COPY ./rapps-rapp-jwt-provider /app
+RUN chmod +x /app/rapps-rapp-jwt-provider
+WORKDIR /app
+ENTRYPOINT ["/app/rapps-rapp-jwt-provider"]
diff --git a/rapps/Dockerfile_rrxi b/rapps/Dockerfile_rrxi
new file mode 100644
index 0000000..ca5675c
--- /dev/null
+++ b/rapps/Dockerfile_rrxi
@@ -0,0 +1,6 @@
+FROM golang:latest
+RUN mkdir /app
+RUN mkdir /certs
+COPY ./rapps-rapp-x509-invoker /app
+RUN chmod +x /app/rapps-rapp-x509-invoker
+WORKDIR /app
diff --git a/rapps/Dockerfile_rrxp b/rapps/Dockerfile_rrxp
new file mode 100644
index 0000000..dfc4b30
--- /dev/null
+++ b/rapps/Dockerfile_rrxp
@@ -0,0 +1,6 @@
+FROM golang:latest
+RUN mkdir /app
+COPY ./rapps-rapp-x509-provider /app
+RUN chmod +x /app/rapps-rapp-x509-provider
+WORKDIR /app
+ENTRYPOINT ["/app/rapps-rapp-x509-provider"]
diff --git a/rapps/IstioOperator.yaml b/rapps/IstioOperator.yaml
new file mode 100644
index 0000000..83604e7
--- /dev/null
+++ b/rapps/IstioOperator.yaml
@@ -0,0 +1,45 @@
+apiVersion: install.istio.io/v1alpha1
+kind: IstioOperator
+spec:
+ profile: demo
+ meshConfig:
+ accessLogEncoding: TEXT
+ accessLogFile: "/dev/stdout"
+ accessLogFormat: ""
+ outboundTrafficPolicy:
+ mode: REGISTRY_ONLY
+ values:
+ pilot:
+ jwksResolverExtraRootCA: |
+ -----BEGIN CERTIFICATE-----
+ MIIFdTCCA12gAwIBAgIUb2mMsNxZ3fpdLt0memNEwSs+yCUwDQYJKoZIhvcNAQEL
+ BQAwSjELMAkGA1UEBhMCSUUxDDAKBgNVBAsMA0VTVDERMA8GA1UEAwwIZXN0LnRl
+ Y2gxGjAYBgkqhkiG9w0BCQEWC2NhQG1haWwuY29tMB4XDTIyMDMyOTEyMjMxOVoX
+ DTMyMDMyNjEyMjMxOVowSjELMAkGA1UEBhMCSUUxDDAKBgNVBAsMA0VTVDERMA8G
+ A1UEAwwIZXN0LnRlY2gxGjAYBgkqhkiG9w0BCQEWC2NhQG1haWwuY29tMIICIjAN
+ BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAy2E9PlJyZBh64gxnqrhB7ELS59TK
+ mKRZEgyWYCyS54sOsVLt7HwrzeHxMqhccyEB3+S0jqgVFe7RQ2dEIqc9H1upG2TH
+ Cznz7+epYXFj7wRfQAXM53mEJYIVPcjJ31iFBHKURC6l/ZBLENNG+mXBN9cO7nMe
+ b99w8Sc5jVMy9VmKDZMzJildtWhyEGEDq4C69TAJq8zfvPExkOZW9iSg25FaCoip
+ IO19EYVxl6BYnjgKr48s1XyREBUnOkw6IeVLzD/2co5UpJd40yolXAG8eDxxSGzT
+ EjyVMR3tph86FQ8H053lYB5Y3u6iwCdALf9TvUpEv+ZL4BcB+I4U0RdtLQGL2iuv
+ 9NLeqVAfmtXC3st+DgukxvJA3+iGDGyssvY3EF3eCB9QnjjbDwvZ4raG4DIcBNQ3
+ FfpfpoSswXI4KU2JXgS/V28Az46NIFwwT3WvwhFT5aCUcInNPAF2vDSUfDvlHl39
+ BSSKAqsPnvJIDTnlmJoSo28uca2SkSkXL2N43vGOPV4/UYRIz+bqSFNfu48nfe1I
+ E83PKTCTDum+iOscteF1xMU3KrWLpdkBzPW1PfVK6OcAgbKZvfBGNdNOmygfMj5t
+ Slw0bc2Gpd1ISJyQK0L2DVOSMeB6+PyDdJEYUVe+Xh2uqnaGJnAS90//X/FiOJrJ
+ Y5GrgeVLAkDyOjcCAwEAAaNTMFEwHQYDVR0OBBYEFHoCuHWgHsN1cS7TRuJgk1Yv
+ deY7MB8GA1UdIwQYMBaAFHoCuHWgHsN1cS7TRuJgk1YvdeY7MA8GA1UdEwEB/wQF
+ MAMBAf8wDQYJKoZIhvcNAQELBQADggIBALp0D+Sw09OxZhq8CGw/fQn+AScY9JSE
+ E/4C+jVwSVygi7BKcJfqy8aq7cGe+O9sAEnmxDrle1oECVIXX+mhhS7cD5kRdOsb
+ WAjJBqi+B6YgNuawLfQldnHJV/opjb0FBytaGpEMWYsAj0xcoVe4Nj/x7myQ4qoD
+ Y8r8wEFriOwTk+0dICg40I2EUeq5qoJ7Q5bbdYPfe8EhJAkN4u7xJ6P6GDY6Zvoo
+ JpYSSAaKLZb9yd4SxAoDvyuEZL6YNX8vgfPEZqVi2lm5uDkeE+xqWhL2j0ECKXPN
+ PLQMFBCaVPO9RueiwV/P/l0DuChY7dSAHn9kqdS6PlSGe411OGTpxz5laD9Ho4a9
+ UOAurbtu76wAPnsxszAxMAGqEXvZgcX+zUBm4uGPpLUu5vIiWgE/DpwmIpT5jwDu
+ EV0e7C43q3kT5ieqzxDb3gvUWdQZ4Qg6qa8js7KfKH7L0ToCtZACnpdVXjxE1Mp6
+ aCKAPPo8AJm2YdS0Zyj1w8ZN6tDStZ6sfFyEkcRiLOF0pL0qJKw/aqgZd0cHCZed
+ z9p+zpuSbJgnEqax0G7fF5hGofUuCIz4F8CNiehjpZDrCHqPrbCsUveu4iP+cw2N
+ /DZsEJUr0qL+QsAll2L6Zm8z1bAGxomxfFqUAHPep+msFyKT6W2SXz3MzTClq1JK
+ CruKkw029sEv
+ -----END CERTIFICATE-----
diff --git a/rapps/auth.yaml b/rapps/auth.yaml
new file mode 100644
index 0000000..9b091ad
--- /dev/null
+++ b/rapps/auth.yaml
@@ -0,0 +1,46 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: auth-code-deployment
+ namespace: default
+ labels:
+ app: auth-code
+spec:
+ selector:
+ matchLabels:
+ app: auth-code
+ template:
+ metadata:
+ labels:
+ app: auth-code
+ version: v1
+ spec:
+ containers:
+ - name: auth-code
+ image: ktimoney/rapps-rapp-auth-provider
+ imagePullPolicy: IfNotPresent
+ ports:
+ - containerPort: 9000
+ resources:
+ limits:
+ memory: 256Mi
+ cpu: "250m"
+ requests:
+ memory: 128Mi
+ cpu: "80m"
+ replicas: 1
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: auth-code
+ namespace: default
+spec:
+ selector:
+ app: auth-code
+ ports:
+ - protocol: TCP
+ port: 80
+ targetPort: 9000
+ nodePort: 31233
+ type: NodePort
diff --git a/rapps/charts/rapp-jwt-invoker/.helmignore b/rapps/charts/rapp-jwt-invoker/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/rapps/charts/rapp-jwt-invoker/Chart.yaml b/rapps/charts/rapp-jwt-invoker/Chart.yaml
new file mode 100644
index 0000000..843ff42
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: rapp-jwt-invoker
+description: A Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.16.0"
diff --git a/rapps/charts/rapp-jwt-invoker/templates/NOTES.txt b/rapps/charts/rapp-jwt-invoker/templates/NOTES.txt
new file mode 100644
index 0000000..7955f62
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/NOTES.txt
@@ -0,0 +1,22 @@
+1. Get the application URL by running these commands:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+ {{- range .paths }}
+ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
+ {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rapp-jwt-invoker.fullname" . }})
+ 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 --namespace {{ .Release.Namespace }} svc -w {{ include "rapp-jwt-invoker.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rapp-jwt-invoker.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+ echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rapp-jwt-invoker.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
diff --git a/rapps/charts/rapp-jwt-invoker/templates/_helpers.tpl b/rapps/charts/rapp-jwt-invoker/templates/_helpers.tpl
new file mode 100644
index 0000000..f74ce3a
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "rapp-jwt-invoker.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "rapp-jwt-invoker.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "rapp-jwt-invoker.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "rapp-jwt-invoker.labels" -}}
+helm.sh/chart: {{ include "rapp-jwt-invoker.chart" . }}
+{{ include "rapp-jwt-invoker.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "rapp-jwt-invoker.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "rapp-jwt-invoker.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "rapp-jwt-invoker.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "rapp-jwt-invoker.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-jwt-invoker/templates/clusterrole.yaml b/rapps/charts/rapp-jwt-invoker/templates/clusterrole.yaml
new file mode 100644
index 0000000..23d1586
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/clusterrole.yaml
@@ -0,0 +1,20 @@
+{{- if .Values.rbac.create -}}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app: {{ template "rapp-jwt-invoker.name" .}}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "rapp-jwt-invoker.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+subjects:
+- kind: ServiceAccount
+ name: {{ template "rapp-jwt-invoker.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+{{- end -}}
diff --git a/rapps/charts/rapp-jwt-invoker/templates/deployment.yaml b/rapps/charts/rapp-jwt-invoker/templates/deployment.yaml
new file mode 100644
index 0000000..3bb60bd
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/deployment.yaml
@@ -0,0 +1,78 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "rapp-jwt-invoker.fullname" . }}
+ labels:
+ {{- include "rapp-jwt-invoker.labels" . | nindent 4 }}
+spec:
+ {{- if not .Values.autoscaling.enabled }}
+ replicas: {{ .Values.replicaCount }}
+ {{- end }}
+ selector:
+ matchLabels:
+ {{- include "rapp-jwt-invoker.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ {{- with .Values.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "rapp-jwt-invoker.selectorLabels" . | nindent 8 }}
+ spec:
+ {{- with .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "rapp-jwt-invoker.serviceAccountName" . }}
+ securityContext:
+ {{- toYaml .Values.podSecurityContext | nindent 8 }}
+ containers:
+ - name: {{ .Chart.Name }}
+ securityContext:
+ {{- toYaml .Values.securityContext | nindent 12 }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ command: ["/app/rapps-rapp-jwt-invoker"]
+ args: [
+ "-securityEnabled", "{{ .Values.rapp.securityEnabled }}",
+ "-realm", "{{ .Values.rapp.realm }}",
+ "-client", "{{ .Values.rapp.client }}",
+ "-role", "{{ with index .Values.rapp.roles 0 }}{{ .role }}{{ end }}",
+ "-rapp", "{{ with index .Values.rapp.apps 0 }}{{ .prefix }}{{ end }}",
+ "-methods", "{{- range .Values.rapp.apps }}{{ join "," .methods }}{{- end }}"
+ ]
+ ports:
+ - name: http
+ containerPort: 9000
+ protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /health
+ port: 9000
+ initialDelaySeconds: 5
+ periodSeconds: 60
+ readinessProbe:
+ exec:
+ command: ["stat", "init.txt"]
+ resources:
+ {{- toYaml .Values.resources | nindent 12 }}
+ volumeMounts:
+ - name: jwt-invoker-certs-persistent-storage
+ mountPath: /certs
+ volumes:
+ - name: jwt-invoker-certs-persistent-storage
+ persistentVolumeClaim:
+ claimName: jwt-invoker-certs-storage-pv-claim
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
diff --git a/rapps/charts/rapp-jwt-invoker/templates/hpa.yaml b/rapps/charts/rapp-jwt-invoker/templates/hpa.yaml
new file mode 100644
index 0000000..71e704c
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/hpa.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2beta1
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "rapp-jwt-invoker.fullname" . }}
+ labels:
+ {{- include "rapp-jwt-invoker.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ include "rapp-jwt-invoker.fullname" . }}
+ minReplicas: {{ .Values.autoscaling.minReplicas }}
+ maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+ metrics:
+ {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: cpu
+ targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+ {{- end }}
+ {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: memory
+ targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ {{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-jwt-invoker/templates/ingress.yaml b/rapps/charts/rapp-jwt-invoker/templates/ingress.yaml
new file mode 100644
index 0000000..e8be03d
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/ingress.yaml
@@ -0,0 +1,41 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "rapp-jwt-invoker.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+ name: {{ $fullName }}
+ labels:
+ {{- include "rapp-jwt-invoker.labels" . | nindent 4 }}
+ {{- with .Values.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+ {{- end }}
+ rules:
+ {{- range .Values.ingress.hosts }}
+ - host: {{ .host | quote }}
+ http:
+ paths:
+ {{- range .paths }}
+ - path: {{ .path }}
+ backend:
+ serviceName: {{ $fullName }}
+ servicePort: {{ $svcPort }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
diff --git a/rapps/charts/rapp-jwt-invoker/templates/persistentvolume.yaml b/rapps/charts/rapp-jwt-invoker/templates/persistentvolume.yaml
new file mode 100644
index 0000000..5370e21
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/persistentvolume.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: jwt-invoker-certs-storage-pv-volume
+ namespace: istio-nonrtric
+ labels:
+ type: local
+ app: rapp-jwt-invoker
+spec:
+ storageClassName: manual
+ capacity:
+ storage: 10Mi
+ accessModes:
+ - ReadOnlyMany
+ hostPath:
+ path: "/var/rapps/certs"
diff --git a/rapps/charts/rapp-jwt-invoker/templates/persistentvolumeclaim.yaml b/rapps/charts/rapp-jwt-invoker/templates/persistentvolumeclaim.yaml
new file mode 100644
index 0000000..022d3f9
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/persistentvolumeclaim.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: jwt-invoker-certs-storage-pv-claim
+ namespace: istio-nonrtric
+ labels:
+ app: rapp-jwt-invoker
+spec:
+ storageClassName: manual
+ accessModes:
+ - ReadOnlyMany
+ resources:
+ requests:
+ storage: 10Mi
diff --git a/rapps/charts/rapp-jwt-invoker/templates/service.yaml b/rapps/charts/rapp-jwt-invoker/templates/service.yaml
new file mode 100644
index 0000000..2d1b89b
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "rapp-jwt-invoker.fullname" . }}
+ labels:
+ {{- include "rapp-jwt-invoker.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "rapp-jwt-invoker.selectorLabels" . | nindent 4 }}
diff --git a/rapps/charts/rapp-jwt-invoker/templates/serviceaccount.yaml b/rapps/charts/rapp-jwt-invoker/templates/serviceaccount.yaml
new file mode 100644
index 0000000..09dd89a
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "rapp-jwt-invoker.serviceAccountName" . }}
+ labels:
+ {{- include "rapp-jwt-invoker.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-jwt-invoker/templates/tests/test-connection.yaml b/rapps/charts/rapp-jwt-invoker/templates/tests/test-connection.yaml
new file mode 100644
index 0000000..357c0f7
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ include "rapp-jwt-invoker.fullname" . }}-test-connection"
+ labels:
+ {{- include "rapp-jwt-invoker.labels" . | nindent 4 }}
+ annotations:
+ "helm.sh/hook": test
+spec:
+ containers:
+ - name: wget
+ image: busybox
+ command: ['wget']
+ args: ['{{ include "rapp-jwt-invoker.fullname" . }}:{{ .Values.service.port }}']
+ restartPolicy: Never
diff --git a/rapps/charts/rapp-jwt-invoker/values.yaml b/rapps/charts/rapp-jwt-invoker/values.yaml
new file mode 100644
index 0000000..199d1db
--- /dev/null
+++ b/rapps/charts/rapp-jwt-invoker/values.yaml
@@ -0,0 +1,102 @@
+# Default values for rapp-jwt-invoker.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+
+image:
+ repository: ktimoney/rapps-rapp-jwt-invoker
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose default is the chart appVersion.
+ tag: "latest"
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+serviceAccount:
+ # Specifies whether a service account should be created
+ create: true
+ # Annotations to add to the service account
+ annotations: {}
+ # The name of the service account to use.
+ # If not set and create is true, a name is generated using the fullname template
+ name: "rapp-jwt-invoker"
+
+rbac:
+ # Specifies whether rbac is enabled
+ create: true
+
+podAnnotations: {}
+
+podSecurityContext: {}
+ # fsGroup: 2000
+
+securityContext: {}
+ # capabilities:
+ # drop:
+ # - ALL
+ # readOnlyRootFilesystem: true
+ # runAsNonRoot: true
+ # runAsUser: 1000
+
+service:
+ type: ClusterIP
+ port: 80
+
+ingress:
+ enabled: false
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ hosts:
+ - host: rapp-jwt-invoker
+ paths:
+ - path: /
+ backend:
+ serviceName: rapp-jwt-invoker
+ servicePort: 80
+ tls: []
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+
+resources: {}
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+
+autoscaling:
+ enabled: false
+ minReplicas: 1
+ maxReplicas: 100
+ targetCPUUtilizationPercentage: 80
+ # targetMemoryUtilizationPercentage: 80
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+
+rapp:
+ securityEnabled: true
+ type: invoker
+ realm: jwt
+ client: jwtprovider-cli
+ roles:
+ - role : provider-viewer
+ grants:
+ - GET
+ apps:
+ - prefix: rapp-jwt-provider
+ methods:
+ - GET
diff --git a/rapps/charts/rapp-jwt-provider/.helmignore b/rapps/charts/rapp-jwt-provider/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/rapps/charts/rapp-jwt-provider/Chart.yaml b/rapps/charts/rapp-jwt-provider/Chart.yaml
new file mode 100644
index 0000000..2d75dd7
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: rapp-jwt-provider
+description: A Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.16.0"
diff --git a/rapps/charts/rapp-jwt-provider/templates/NOTES.txt b/rapps/charts/rapp-jwt-provider/templates/NOTES.txt
new file mode 100644
index 0000000..a244434
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/NOTES.txt
@@ -0,0 +1,22 @@
+1. Get the application URL by running these commands:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+ {{- range .paths }}
+ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
+ {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rapp-jwt-provider.fullname" . }})
+ 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 --namespace {{ .Release.Namespace }} svc -w {{ include "rapp-jwt-provider.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rapp-jwt-provider.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+ echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rapp-jwt-provider.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
diff --git a/rapps/charts/rapp-jwt-provider/templates/_helpers.tpl b/rapps/charts/rapp-jwt-provider/templates/_helpers.tpl
new file mode 100644
index 0000000..46ee122
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "rapp-jwt-provider.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "rapp-jwt-provider.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "rapp-jwt-provider.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "rapp-jwt-provider.labels" -}}
+helm.sh/chart: {{ include "rapp-jwt-provider.chart" . }}
+{{ include "rapp-jwt-provider.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "rapp-jwt-provider.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "rapp-jwt-provider.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "rapp-jwt-provider.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "rapp-jwt-provider.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-jwt-provider/templates/clusterrole.yaml b/rapps/charts/rapp-jwt-provider/templates/clusterrole.yaml
new file mode 100644
index 0000000..2fab92b
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/clusterrole.yaml
@@ -0,0 +1,20 @@
+{{- if .Values.rbac.create -}}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app: {{ template "rapp-jwt-provider.name" .}}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "rapp-jwt-provider.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+subjects:
+- kind: ServiceAccount
+ name: {{ template "rapp-jwt-provider.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+{{- end -}}
diff --git a/rapps/charts/rapp-jwt-provider/templates/deployment.yaml b/rapps/charts/rapp-jwt-provider/templates/deployment.yaml
new file mode 100644
index 0000000..67137a3
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/deployment.yaml
@@ -0,0 +1,61 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "rapp-jwt-provider.fullname" . }}
+ labels:
+ {{- include "rapp-jwt-provider.labels" . | nindent 4 }}
+spec:
+ {{- if not .Values.autoscaling.enabled }}
+ replicas: {{ .Values.replicaCount }}
+ {{- end }}
+ selector:
+ matchLabels:
+ {{- include "rapp-jwt-provider.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ {{- with .Values.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "rapp-jwt-provider.selectorLabels" . | nindent 8 }}
+ spec:
+ {{- with .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "rapp-jwt-provider.serviceAccountName" . }}
+ securityContext:
+ {{- toYaml .Values.podSecurityContext | nindent 8 }}
+ containers:
+ - name: {{ .Chart.Name }}
+ securityContext:
+ {{- toYaml .Values.securityContext | nindent 12 }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - name: http
+ containerPort: 9000
+ protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ resources:
+ {{- toYaml .Values.resources | nindent 12 }}
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
diff --git a/rapps/charts/rapp-jwt-provider/templates/hpa.yaml b/rapps/charts/rapp-jwt-provider/templates/hpa.yaml
new file mode 100644
index 0000000..7f4af59
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/hpa.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2beta1
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "rapp-jwt-provider.fullname" . }}
+ labels:
+ {{- include "rapp-jwt-provider.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ include "rapp-jwt-provider.fullname" . }}
+ minReplicas: {{ .Values.autoscaling.minReplicas }}
+ maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+ metrics:
+ {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: cpu
+ targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+ {{- end }}
+ {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: memory
+ targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ {{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-jwt-provider/templates/ingress.yaml b/rapps/charts/rapp-jwt-provider/templates/ingress.yaml
new file mode 100644
index 0000000..b986148
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/ingress.yaml
@@ -0,0 +1,41 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "rapp-jwt-provider.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+ name: {{ $fullName }}
+ labels:
+ {{- include "rapp-jwt-provider.labels" . | nindent 4 }}
+ {{- with .Values.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+ {{- end }}
+ rules:
+ {{- range .Values.ingress.hosts }}
+ - host: {{ .host | quote }}
+ http:
+ paths:
+ {{- range .paths }}
+ - path: {{ .path }}
+ backend:
+ serviceName: {{ $fullName }}
+ servicePort: {{ $svcPort }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
diff --git a/rapps/charts/rapp-jwt-provider/templates/persistentvolume.yaml b/rapps/charts/rapp-jwt-provider/templates/persistentvolume.yaml
new file mode 100644
index 0000000..c3d23a9
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/persistentvolume.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: jwt-provider-certs-storage-pv-volume
+ namespace: istio-nonrtric
+ labels:
+ type: local
+ app: rapp-jwt-provider
+spec:
+ storageClassName: manual
+ capacity:
+ storage: 10Mi
+ accessModes:
+ - ReadOnlyMany
+ hostPath:
+ path: "/var/rapps/certs"
diff --git a/rapps/charts/rapp-jwt-provider/templates/service.yaml b/rapps/charts/rapp-jwt-provider/templates/service.yaml
new file mode 100644
index 0000000..953ec4f
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "rapp-jwt-provider.fullname" . }}
+ labels:
+ {{- include "rapp-jwt-provider.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "rapp-jwt-provider.selectorLabels" . | nindent 4 }}
diff --git a/rapps/charts/rapp-jwt-provider/templates/serviceaccount.yaml b/rapps/charts/rapp-jwt-provider/templates/serviceaccount.yaml
new file mode 100644
index 0000000..259943a
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "rapp-jwt-provider.serviceAccountName" . }}
+ labels:
+ {{- include "rapp-jwt-provider.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-jwt-provider/templates/tests/test-connection.yaml b/rapps/charts/rapp-jwt-provider/templates/tests/test-connection.yaml
new file mode 100644
index 0000000..6c71552
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ include "rapp-jwt-provider.fullname" . }}-test-connection"
+ labels:
+ {{- include "rapp-jwt-provider.labels" . | nindent 4 }}
+ annotations:
+ "helm.sh/hook": test
+spec:
+ containers:
+ - name: wget
+ image: busybox
+ command: ['wget']
+ args: ['{{ include "rapp-jwt-provider.fullname" . }}:{{ .Values.service.port }}']
+ restartPolicy: Never
diff --git a/rapps/charts/rapp-jwt-provider/values.yaml b/rapps/charts/rapp-jwt-provider/values.yaml
new file mode 100644
index 0000000..ade36e6
--- /dev/null
+++ b/rapps/charts/rapp-jwt-provider/values.yaml
@@ -0,0 +1,104 @@
+# Default values for rapp-jwt-provider.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+
+image:
+ repository: ktimoney/rapps-rapp-jwt-provider
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose default is the chart appVersion.
+ tag: "latest"
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+serviceAccount:
+ # Specifies whether a service account should be created
+ create: true
+ # Annotations to add to the service account
+ annotations: {}
+ # The name of the service account to use.
+ # If not set and create is true, a name is generated using the fullname template
+ name: "rapp-jwt-provider"
+
+rbac:
+ # Specifies whether rbac should be enabled
+ create: true
+
+podAnnotations: {}
+
+podSecurityContext: {}
+ # fsGroup: 2000
+
+securityContext: {}
+ # capabilities:
+ # drop:
+ # - ALL
+ # readOnlyRootFilesystem: true
+ # runAsNonRoot: true
+ # runAsUser: 1000
+
+service:
+ type: ClusterIP
+ port: 80
+
+ingress:
+ enabled: false
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ hosts:
+ - host: rapp-jwt-provider
+ paths:
+ - path: /
+ backend:
+ serviceName: rapp-jwt-provider
+ servicePort: 80
+ tls: []
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+
+resources: {}
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+
+autoscaling:
+ enabled: false
+ minReplicas: 1
+ maxReplicas: 100
+ targetCPUUtilizationPercentage: 80
+ # targetMemoryUtilizationPercentage: 80
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+rapp:
+ securityEnabled: true
+ type: provider
+ realm: jwt
+ client: jwtprovider-cli
+ roles:
+ - role : provider-viewer
+ grants:
+ - GET
+ - role : provider-admin
+ grants:
+ - GET
+ - PUT
+ - PUT
+ - DELETE
+
diff --git a/rapps/charts/rapp-x509-invoker/.helmignore b/rapps/charts/rapp-x509-invoker/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/rapps/charts/rapp-x509-invoker/Chart.yaml b/rapps/charts/rapp-x509-invoker/Chart.yaml
new file mode 100644
index 0000000..0018754
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: rapp-x509-invoker
+description: A Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.16.0"
diff --git a/rapps/charts/rapp-x509-invoker/templates/NOTES.txt b/rapps/charts/rapp-x509-invoker/templates/NOTES.txt
new file mode 100644
index 0000000..200f372
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/NOTES.txt
@@ -0,0 +1,22 @@
+1. Get the application URL by running these commands:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+ {{- range .paths }}
+ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
+ {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rapp-x509-invoker.fullname" . }})
+ 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 --namespace {{ .Release.Namespace }} svc -w {{ include "rapp-x509-invoker.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rapp-x509-invoker.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+ echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rapp-x509-invoker.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
diff --git a/rapps/charts/rapp-x509-invoker/templates/_helpers.tpl b/rapps/charts/rapp-x509-invoker/templates/_helpers.tpl
new file mode 100644
index 0000000..b8a93da
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "rapp-x509-invoker.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "rapp-x509-invoker.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "rapp-x509-invoker.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "rapp-x509-invoker.labels" -}}
+helm.sh/chart: {{ include "rapp-x509-invoker.chart" . }}
+{{ include "rapp-x509-invoker.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "rapp-x509-invoker.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "rapp-x509-invoker.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "rapp-x509-invoker.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "rapp-x509-invoker.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-x509-invoker/templates/clusterrole.yaml b/rapps/charts/rapp-x509-invoker/templates/clusterrole.yaml
new file mode 100644
index 0000000..71eddc2
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/clusterrole.yaml
@@ -0,0 +1,20 @@
+{{- if .Values.rbac.create -}}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app: {{ template "rapp-x509-invoker.name" .}}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "rapp-x509-invoker.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+subjects:
+- kind: ServiceAccount
+ name: {{ template "rapp-x509-invoker.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+{{- end -}}
diff --git a/rapps/charts/rapp-x509-invoker/templates/deployment.yaml b/rapps/charts/rapp-x509-invoker/templates/deployment.yaml
new file mode 100644
index 0000000..d1fca53
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/deployment.yaml
@@ -0,0 +1,78 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "rapp-x509-invoker.fullname" . }}
+ labels:
+ {{- include "rapp-x509-invoker.labels" . | nindent 4 }}
+spec:
+ {{- if not .Values.autoscaling.enabled }}
+ replicas: {{ .Values.replicaCount }}
+ {{- end }}
+ selector:
+ matchLabels:
+ {{- include "rapp-x509-invoker.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ {{- with .Values.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "rapp-x509-invoker.selectorLabels" . | nindent 8 }}
+ spec:
+ {{- with .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "rapp-x509-invoker.serviceAccountName" . }}
+ securityContext:
+ {{- toYaml .Values.podSecurityContext | nindent 8 }}
+ containers:
+ - name: {{ .Chart.Name }}
+ securityContext:
+ {{- toYaml .Values.securityContext | nindent 12 }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ command: ["/app/rapps-rapp-x509-invoker"]
+ args: [
+ "-securityEnabled", "{{ .Values.rapp.securityEnabled }}",
+ "-realm", "{{ .Values.rapp.realm }}",
+ "-client", "{{ .Values.rapp.client }}",
+ "-role", "{{ with index .Values.rapp.roles 0 }}{{ .role }}{{ end }}",
+ "-rapp", "{{ with index .Values.rapp.apps 0 }}{{ .prefix }}{{ end }}",
+ "-methods", "{{- range .Values.rapp.apps }}{{ join "," .methods }}{{- end }}"
+ ]
+ ports:
+ - name: http
+ containerPort: 9000
+ protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /health
+ port: 9000
+ initialDelaySeconds: 5
+ periodSeconds: 60
+ readinessProbe:
+ exec:
+ command: ["stat", "init.txt"]
+ resources:
+ {{- toYaml .Values.resources | nindent 12 }}
+ volumeMounts:
+ - name: x509-rapps-certs-persistent-storage
+ mountPath: /certs
+ volumes:
+ - name: x509-rapps-certs-persistent-storage
+ persistentVolumeClaim:
+ claimName: x509-rapps-certs-storage-pv-claim
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
diff --git a/rapps/charts/rapp-x509-invoker/templates/hpa.yaml b/rapps/charts/rapp-x509-invoker/templates/hpa.yaml
new file mode 100644
index 0000000..b86fa99
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/hpa.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2beta1
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "rapp-x509-invoker.fullname" . }}
+ labels:
+ {{- include "rapp-x509-invoker.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ include "rapp-x509-invoker.fullname" . }}
+ minReplicas: {{ .Values.autoscaling.minReplicas }}
+ maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+ metrics:
+ {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: cpu
+ targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+ {{- end }}
+ {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: memory
+ targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ {{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-x509-invoker/templates/ingress.yaml b/rapps/charts/rapp-x509-invoker/templates/ingress.yaml
new file mode 100644
index 0000000..a49f3a3
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/ingress.yaml
@@ -0,0 +1,41 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "rapp-x509-invoker.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+ name: {{ $fullName }}
+ labels:
+ {{- include "rapp-x509-invoker.labels" . | nindent 4 }}
+ {{- with .Values.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+ {{- end }}
+ rules:
+ {{- range .Values.ingress.hosts }}
+ - host: {{ .host | quote }}
+ http:
+ paths:
+ {{- range .paths }}
+ - path: {{ .path }}
+ backend:
+ serviceName: {{ $fullName }}
+ servicePort: {{ $svcPort }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
diff --git a/rapps/charts/rapp-x509-invoker/templates/persistentvolume.yaml b/rapps/charts/rapp-x509-invoker/templates/persistentvolume.yaml
new file mode 100644
index 0000000..b64a1a8
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/persistentvolume.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: x509-rapps-certs-storage-pv-volume
+ namespace: istio-nonrtric
+ labels:
+ type: local
+ app: rapp-x509-invoker
+spec:
+ storageClassName: manual
+ capacity:
+ storage: 10Mi
+ accessModes:
+ - ReadOnlyMany
+ hostPath:
+ path: "/var/rapps/certs"
diff --git a/rapps/charts/rapp-x509-invoker/templates/persistentvolumeclaim.yaml b/rapps/charts/rapp-x509-invoker/templates/persistentvolumeclaim.yaml
new file mode 100644
index 0000000..1622ed5
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/persistentvolumeclaim.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: x509-rapps-certs-storage-pv-claim
+ namespace: istio-nonrtric
+ labels:
+ app: rapp-x509-invoker
+spec:
+ storageClassName: manual
+ accessModes:
+ - ReadOnlyMany
+ resources:
+ requests:
+ storage: 10Mi
diff --git a/rapps/charts/rapp-x509-invoker/templates/service.yaml b/rapps/charts/rapp-x509-invoker/templates/service.yaml
new file mode 100644
index 0000000..e363a2f
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "rapp-x509-invoker.fullname" . }}
+ labels:
+ {{- include "rapp-x509-invoker.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "rapp-x509-invoker.selectorLabels" . | nindent 4 }}
diff --git a/rapps/charts/rapp-x509-invoker/templates/serviceaccount.yaml b/rapps/charts/rapp-x509-invoker/templates/serviceaccount.yaml
new file mode 100644
index 0000000..4c3405c
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "rapp-x509-invoker.serviceAccountName" . }}
+ labels:
+ {{- include "rapp-x509-invoker.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-x509-invoker/templates/tests/test-connection.yaml b/rapps/charts/rapp-x509-invoker/templates/tests/test-connection.yaml
new file mode 100644
index 0000000..ad4855a
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ include "rapp-x509-invoker.fullname" . }}-test-connection"
+ labels:
+ {{- include "rapp-x509-invoker.labels" . | nindent 4 }}
+ annotations:
+ "helm.sh/hook": test
+spec:
+ containers:
+ - name: wget
+ image: busybox
+ command: ['wget']
+ args: ['{{ include "rapp-x509-invoker.fullname" . }}:{{ .Values.service.port }}']
+ restartPolicy: Never
diff --git a/rapps/charts/rapp-x509-invoker/values.yaml b/rapps/charts/rapp-x509-invoker/values.yaml
new file mode 100644
index 0000000..bcdfaed
--- /dev/null
+++ b/rapps/charts/rapp-x509-invoker/values.yaml
@@ -0,0 +1,102 @@
+# Default values for rapp-x509-invoker.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+
+image:
+ repository: ktimoney/rapps-rapp-x509-invoker
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose default is the chart appVersion.
+ tag: "latest"
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+serviceAccount:
+ # Specifies whether a service account should be created
+ create: true
+ # Annotations to add to the service account
+ annotations: {}
+ # The name of the service account to use.
+ # If not set and create is true, a name is generated using the fullname template
+ name: "rapp-x509-invoker"
+
+rbac:
+ # Specifies whether rbac is enabled
+ create: true
+
+podAnnotations: {}
+
+podSecurityContext: {}
+ # fsGroup: 2000
+
+securityContext: {}
+ # capabilities:
+ # drop:
+ # - ALL
+ # readOnlyRootFilesystem: true
+ # runAsNonRoot: true
+ # runAsUser: 1000
+
+service:
+ type: ClusterIP
+ port: 80
+
+ingress:
+ enabled: false
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ hosts:
+ - host: rapp-x509-invoker
+ paths:
+ - path: /
+ backend:
+ serviceName: rapp-x509-invoker
+ servicePort: 80
+ tls: []
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+
+resources: {}
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+
+autoscaling:
+ enabled: false
+ minReplicas: 1
+ maxReplicas: 100
+ targetCPUUtilizationPercentage: 80
+ # targetMemoryUtilizationPercentage: 80
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+
+rapp:
+ securityEnabled: true
+ type: invoker
+ realm: x509
+ client: x509provider-cli
+ roles:
+ - role : provider-viewer
+ grants:
+ - GET
+ apps:
+ - prefix: rapp-x509-provider
+ methods:
+ - GET
diff --git a/rapps/charts/rapp-x509-provider/.helmignore b/rapps/charts/rapp-x509-provider/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/rapps/charts/rapp-x509-provider/Chart.yaml b/rapps/charts/rapp-x509-provider/Chart.yaml
new file mode 100644
index 0000000..0148f85
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: rapp-x509-provider
+description: A Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.16.0"
diff --git a/rapps/charts/rapp-x509-provider/templates/NOTES.txt b/rapps/charts/rapp-x509-provider/templates/NOTES.txt
new file mode 100644
index 0000000..3f038ee
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/templates/NOTES.txt
@@ -0,0 +1,22 @@
+1. Get the application URL by running these commands:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+ {{- range .paths }}
+ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
+ {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rapp-x509-provider.fullname" . }})
+ 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 --namespace {{ .Release.Namespace }} svc -w {{ include "rapp-x509-provider.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rapp-x509-provider.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+ echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rapp-x509-provider.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
diff --git a/rapps/charts/rapp-x509-provider/templates/_helpers.tpl b/rapps/charts/rapp-x509-provider/templates/_helpers.tpl
new file mode 100644
index 0000000..a2e7b7e
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "rapp-x509-provider.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "rapp-x509-provider.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "rapp-x509-provider.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "rapp-x509-provider.labels" -}}
+helm.sh/chart: {{ include "rapp-x509-provider.chart" . }}
+{{ include "rapp-x509-provider.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "rapp-x509-provider.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "rapp-x509-provider.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "rapp-x509-provider.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "rapp-x509-provider.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-x509-provider/templates/clusterrole.yaml b/rapps/charts/rapp-x509-provider/templates/clusterrole.yaml
new file mode 100644
index 0000000..2747446
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/templates/clusterrole.yaml
@@ -0,0 +1,20 @@
+{{- if .Values.rbac.create -}}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app: {{ template "rapp-x509-provider.name" .}}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "rapp-x509-provider.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+subjects:
+- kind: ServiceAccount
+ name: {{ template "rapp-x509-provider.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+{{- end -}}
diff --git a/rapps/charts/rapp-x509-provider/templates/deployment.yaml b/rapps/charts/rapp-x509-provider/templates/deployment.yaml
new file mode 100644
index 0000000..48b8235
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/templates/deployment.yaml
@@ -0,0 +1,61 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "rapp-x509-provider.fullname" . }}
+ labels:
+ {{- include "rapp-x509-provider.labels" . | nindent 4 }}
+spec:
+ {{- if not .Values.autoscaling.enabled }}
+ replicas: {{ .Values.replicaCount }}
+ {{- end }}
+ selector:
+ matchLabels:
+ {{- include "rapp-x509-provider.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ {{- with .Values.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "rapp-x509-provider.selectorLabels" . | nindent 8 }}
+ spec:
+ {{- with .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "rapp-x509-provider.serviceAccountName" . }}
+ securityContext:
+ {{- toYaml .Values.podSecurityContext | nindent 8 }}
+ containers:
+ - name: {{ .Chart.Name }}
+ securityContext:
+ {{- toYaml .Values.securityContext | nindent 12 }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - name: http
+ containerPort: 9000
+ protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ resources:
+ {{- toYaml .Values.resources | nindent 12 }}
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
diff --git a/rapps/charts/rapp-x509-provider/templates/hpa.yaml b/rapps/charts/rapp-x509-provider/templates/hpa.yaml
new file mode 100644
index 0000000..a716621
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/templates/hpa.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2beta1
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "rapp-x509-provider.fullname" . }}
+ labels:
+ {{- include "rapp-x509-provider.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ include "rapp-x509-provider.fullname" . }}
+ minReplicas: {{ .Values.autoscaling.minReplicas }}
+ maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+ metrics:
+ {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: cpu
+ targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+ {{- end }}
+ {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: memory
+ targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ {{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-x509-provider/templates/ingress.yaml b/rapps/charts/rapp-x509-provider/templates/ingress.yaml
new file mode 100644
index 0000000..da8a2e9
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/templates/ingress.yaml
@@ -0,0 +1,41 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "rapp-x509-provider.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+ name: {{ $fullName }}
+ labels:
+ {{- include "rapp-x509-provider.labels" . | nindent 4 }}
+ {{- with .Values.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+ {{- end }}
+ rules:
+ {{- range .Values.ingress.hosts }}
+ - host: {{ .host | quote }}
+ http:
+ paths:
+ {{- range .paths }}
+ - path: {{ .path }}
+ backend:
+ serviceName: {{ $fullName }}
+ servicePort: {{ $svcPort }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
diff --git a/rapps/charts/rapp-x509-provider/templates/service.yaml b/rapps/charts/rapp-x509-provider/templates/service.yaml
new file mode 100644
index 0000000..8e21778
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "rapp-x509-provider.fullname" . }}
+ labels:
+ {{- include "rapp-x509-provider.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "rapp-x509-provider.selectorLabels" . | nindent 4 }}
diff --git a/rapps/charts/rapp-x509-provider/templates/serviceaccount.yaml b/rapps/charts/rapp-x509-provider/templates/serviceaccount.yaml
new file mode 100644
index 0000000..eb675ae
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "rapp-x509-provider.serviceAccountName" . }}
+ labels:
+ {{- include "rapp-x509-provider.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/rapps/charts/rapp-x509-provider/templates/tests/test-connection.yaml b/rapps/charts/rapp-x509-provider/templates/tests/test-connection.yaml
new file mode 100644
index 0000000..6549eb1
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ include "rapp-x509-provider.fullname" . }}-test-connection"
+ labels:
+ {{- include "rapp-x509-provider.labels" . | nindent 4 }}
+ annotations:
+ "helm.sh/hook": test
+spec:
+ containers:
+ - name: wget
+ image: busybox
+ command: ['wget']
+ args: ['{{ include "rapp-x509-provider.fullname" . }}:{{ .Values.service.port }}']
+ restartPolicy: Never
diff --git a/rapps/charts/rapp-x509-provider/values.yaml b/rapps/charts/rapp-x509-provider/values.yaml
new file mode 100644
index 0000000..ba6d7be
--- /dev/null
+++ b/rapps/charts/rapp-x509-provider/values.yaml
@@ -0,0 +1,104 @@
+# Default values for rapp-x509-provider.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+
+image:
+ repository: ktimoney/rapps-rapp-x509-provider
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose default is the chart appVersion.
+ tag: "latest"
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+serviceAccount:
+ # Specifies whether a service account should be created
+ create: true
+ # Annotations to add to the service account
+ annotations: {}
+ # The name of the service account to use.
+ # If not set and create is true, a name is generated using the fullname template
+ name: "rapp-x509-provider"
+
+rbac:
+ # Specifies whether rbac should be enabled
+ create: true
+
+podAnnotations: {}
+
+podSecurityContext: {}
+ # fsGroup: 2000
+
+securityContext: {}
+ # capabilities:
+ # drop:
+ # - ALL
+ # readOnlyRootFilesystem: true
+ # runAsNonRoot: true
+ # runAsUser: 1000
+
+service:
+ type: ClusterIP
+ port: 80
+
+ingress:
+ enabled: false
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ hosts:
+ - host: rapp-x509-provider
+ paths:
+ - path: /
+ backend:
+ serviceName: rapp-x509-provider
+ servicePort: 80
+ tls: []
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+
+resources: {}
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+
+autoscaling:
+ enabled: false
+ minReplicas: 1
+ maxReplicas: 100
+ targetCPUUtilizationPercentage: 80
+ # targetMemoryUtilizationPercentage: 80
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+rapp:
+ securityEnabled: true
+ type: provider
+ realm: x509
+ client: x509provider-cli
+ roles:
+ - role : provider-viewer
+ grants:
+ - GET
+ - role : provider-admin
+ grants:
+ - GET
+ - PUT
+ - PUT
+ - DELETE
+
diff --git a/rapps/go.mod b/rapps/go.mod
index d9b2596..5d99959 100644
--- a/rapps/go.mod
+++ b/rapps/go.mod
@@ -4,10 +4,13 @@
require (
github.com/Nerzal/gocloak/v10 v10.0.1
+ github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/pkg/errors v0.9.1
+ golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
gopkg.in/yaml.v2 v2.4.0
helm.sh/helm/v3 v3.8.0
istio.io/client-go v1.13.0
+ k8s.io/api v0.23.4
k8s.io/apimachinery v0.23.4
k8s.io/cli-runtime v0.23.4
k8s.io/client-go v0.23.4
@@ -108,7 +111,6 @@
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
- golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
@@ -125,7 +127,6 @@
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
istio.io/api v0.0.0-20220208030606-6253688c0c91 // indirect
istio.io/gogo-genproto v0.0.0-20211208193508-5ab4acc9eb1e // indirect
- k8s.io/api v0.23.4 // indirect
k8s.io/apiextensions-apiserver v0.23.1 // indirect
k8s.io/apiserver v0.23.1 // indirect
k8s.io/component-base v0.23.1 // indirect
diff --git a/rapps/go.sum b/rapps/go.sum
index 1539f8e..2caa5ae 100644
--- a/rapps/go.sum
+++ b/rapps/go.sum
@@ -339,6 +339,7 @@
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684 h1:DBZ2sN7CK6dgvHVpQsQj4sRMCbWTmd17l+5SUCjnQSY=
diff --git a/rapps/keycloak.yaml b/rapps/keycloak.yaml
index 44aa3a7..8aa56cd 100644
--- a/rapps/keycloak.yaml
+++ b/rapps/keycloak.yaml
@@ -131,3 +131,66 @@
persistentVolumeClaim:
claimName: keycloak-certs-pv-claim
---
+apiVersion: networking.istio.io/v1alpha3
+kind: Gateway
+metadata:
+ name: kcgateway
+spec:
+ selector:
+ istio: ingressgateway # use istio default ingress gateway
+ servers:
+ - port:
+ number: 443
+ name: https
+ protocol: HTTPS
+ tls:
+ mode: PASSTHROUGH
+ hosts:
+ - keycloak.est.tech
+ - port:
+ number: 80
+ name: http
+ protocol: HTTP
+ hosts:
+ - "*"
+---
+apiVersion: networking.istio.io/v1alpha3
+kind: VirtualService
+metadata:
+ name: keycloak-tls-vs
+spec:
+ hosts:
+ - keycloak.est.tech
+ gateways:
+ - kcgateway
+ tls:
+ - match:
+ - port: 443
+ sniHosts:
+ - keycloak.est.tech
+ route:
+ - destination:
+ host: keycloak.default.svc.cluster.local
+ port:
+ number: 8443
+---
+apiVersion: networking.istio.io/v1beta1
+kind: VirtualService
+metadata:
+ name: keycloak-vs
+spec:
+ hosts:
+ - "*"
+ gateways:
+ - kcgateway
+ http:
+ - name: "keycloak-routes"
+ match:
+ - uri:
+ prefix: "/auth"
+ route:
+ - destination:
+ port:
+ number: 8080
+ host: keycloak.default.svc.cluster.local
+---
diff --git a/rapps/rapps-helm-installer.go b/rapps/rapps-helm-installer.go
index dc49289..6baeca0 100644
--- a/rapps/rapps-helm-installer.go
+++ b/rapps/rapps-helm-installer.go
@@ -2,6 +2,7 @@
import (
"context"
+ "database/sql"
"encoding/json"
"flag"
"fmt"
@@ -22,7 +23,6 @@
"net/http"
"os"
"path/filepath"
- "database/sql"
)
var settings *cli.EnvSettings
@@ -35,13 +35,14 @@
var namespace string
type ChartInfo struct {
- Name string
- Namespace string
- Revision int
- Updated string
- Status string
- Chart string
- AppVersion string
+ Name string `json:",omitempty"`
+ Namespace string `json:",omitempty"`
+ Revision int `json:",omitempty"`
+ Updated string `json:",omitempty"`
+ Status string `json:",omitempty"`
+ Chart string `json:",omitempty"`
+ AppVersion string `json:",omitempty"`
+ Values map[string]interface{} `json:"-"`
}
type Rapp struct {
@@ -49,7 +50,6 @@
SecurityEnabled bool
Realm string
Client string
- Keycloak string
Roles []struct {
Role string
Grants []string
@@ -63,11 +63,11 @@
var rapp Rapp
const (
- host = "postgres.default"
- port = 5432
- user = "capif"
- password = "capif"
- dbname = "capif"
+ host = "postgres.default"
+ port = 5432
+ user = "capif"
+ password = "capif"
+ dbname = "capif"
)
func runInstall(res http.ResponseWriter, req *http.Request) {
@@ -82,52 +82,56 @@
chartMuseumService, chartMuseumPort := findService("chartmuseum", "default")
fmt.Printf("Chart Museum service:%s, Port:%d\n", chartMuseumService, chartMuseumPort)
url := "http://" + chartMuseumService + ":" + fmt.Sprint(chartMuseumPort)
- // Add repo
- fmt.Printf("Adding %s to Helm Repo\n", url)
- _, err := addToRepo(url)
- if err != nil {
- msg = err.Error()
- } else {
- install, err = dryRun()
+ if !chartInstalled(chartName) {
+ // Add repo
+ fmt.Printf("Adding %s to Helm Repo\n", url)
+ _, err := addToRepo(url)
if err != nil {
msg = err.Error()
} else {
- if rapp.SecurityEnabled && rapp.Type == "provider" {
- // keycloak client setup
- fmt.Println("Setting up keycloak")
- _, err = http.Get("http://rapps-keycloak-mgr.default/create?realm=" + rapp.Realm + "&name=" + rapp.Client + "&role=" + rapp.Roles[0].Role)
- if err != nil {
- msg = err.Error()
- } else {
- fmt.Println("Setting up istio")
- _, err := http.Get("http://rapps-istio-mgr.default/create?name=" + chartName + "&realm=" + rapp.Realm + "&role=" + rapp.Roles[0].Role + "&method=" + rapp.Roles[0].Grants[0])
+ install, err = dryRun()
+ if err != nil {
+ msg = err.Error()
+ } else {
+ if rapp.SecurityEnabled && rapp.Type == "provider" {
+ // keycloak client setup
+ fmt.Println("Setting up keycloak")
+ _, err = http.Get("http://rapps-keycloak-mgr.default/create?realm=" + rapp.Realm + "&name=" + rapp.Client + "&role=" + rapp.Roles[0].Role)
if err != nil {
msg = err.Error()
} else {
- // Install chart
- fmt.Printf("Installing chart %s to %s namespace\n", chartName, namespace)
- chart, err = installHelmChart(install)
+ fmt.Println("Setting up istio")
+ _, err := http.Get("http://rapps-istio-mgr.default/create?name=" + chartName + "&realm=" + rapp.Realm + "&role=" + rapp.Roles[0].Role + "&method=" + rapp.Roles[0].Grants[0])
if err != nil {
- msg = "Error occurred during installation " + err.Error()
+ msg = err.Error()
} else {
- msg = "Successfully installed release: " + chart
+ // Install chart
+ fmt.Printf("Installing chart %s to %s namespace\n", chartName, namespace)
+ chart, err = installHelmChart(install)
+ if err != nil {
+ msg = "Error occurred during installation " + err.Error()
+ } else {
+ msg = "Successfully installed release: " + chart
+ }
}
}
- }
- } else {
- // Install chart
- fmt.Printf("Installing chart %s to %s namespace\n", chartName, namespace)
- chart, err = installHelmChart(install)
- if err != nil {
- msg = "Error occurred during installation " + err.Error()
} else {
- msg = "Successfully installed release: " + chart
+ // Install chart
+ fmt.Printf("Installing chart %s to %s namespace\n", chartName, namespace)
+ chart, err = installHelmChart(install)
+ if err != nil {
+ msg = "Error occurred during installation " + err.Error()
+ } else {
+ msg = "Successfully installed release: " + chart
+ }
}
- }
+ }
}
+ registrerRapp(chartName, rapp.Type)
+ } else {
+ msg = chartName + " has already been installed"
}
- registrerRapp(chartName, rapp.Type)
// create response binary data
data := []byte(msg) // slice of bytes
@@ -143,33 +147,36 @@
var msg string
var chart string
- //var install *action.Install
- _, err := dryRun()
- if err != nil {
- msg = err.Error()
- } else {
- chart, err = uninstallHelmChart(chartName)
+ if chartInstalled(chartName) {
+ err := getChartValues(chartName)
if err != nil {
- msg = "Error occurred during uninstall " + err.Error()
+ msg = err.Error()
} else {
- msg = "Successfully uninstalled release: " + chart
- }
- if rapp.SecurityEnabled && rapp.Type == "provider" {
- // Remove istio objects for rapp
- fmt.Println("Removing istio services")
- _, err := http.Get("http://rapps-istio-mgr.default/remove?name=" + chartName)
+ chart, err = uninstallHelmChart(chartName)
if err != nil {
- msg = err.Error()
+ msg = "Error occurred during uninstall " + err.Error()
+ } else {
+ msg = "Successfully uninstalled release: " + chart
}
- // remove keycloak client
- fmt.Println("Removing keycloak client")
- _, err = http.Get("http://rapps-keycloak-mgr.default/remove?realm=" + rapp.Realm + "&name=" + rapp.Client + "&role=" + rapp.Roles[0].Role)
- if err != nil {
- msg = err.Error()
+ if rapp.SecurityEnabled && rapp.Type == "provider" {
+ // Remove istio objects for rapp
+ fmt.Println("Removing istio services")
+ _, err := http.Get("http://rapps-istio-mgr.default/remove?name=" + chartName)
+ if err != nil {
+ msg = err.Error()
+ }
+ // remove keycloak client
+ fmt.Println("Removing keycloak client")
+ _, err = http.Get("http://rapps-keycloak-mgr.default/remove?realm=" + rapp.Realm + "&name=" + rapp.Client + "&role=" + rapp.Roles[0].Role)
+ if err != nil {
+ msg = err.Error()
+ }
}
}
+ unregistrerRapp(chartName, rapp.Type)
+ } else {
+ msg = chartName + " is not installed"
}
- unregistrerRapp(chartName, rapp.Type)
// create response binary data
data := []byte(msg) // slice of bytes
@@ -388,7 +395,7 @@
fmt.Println(err)
}
for _, release := range releases {
- fmt.Println("Release: " + release.Name + " Status: " + release.Info.Status.String())
+ //fmt.Println("Release: " + release.Name + " Status: " + release.Info.Status.String())
chart.Name = release.Name
chart.Namespace = release.Namespace
chart.Revision = release.Version
@@ -396,66 +403,100 @@
chart.Status = release.Info.Status.String()
chart.Chart = release.Chart.Metadata.Name + "-" + release.Chart.Metadata.Version
chart.AppVersion = release.Chart.Metadata.AppVersion
+ chart.Values = release.Chart.Values
charts = append(charts, chart)
}
return charts
}
+func chartInstalled(chartName string) bool {
+ charts := list()
+ for _, chart := range charts {
+ if chart.Name == chartName {
+ return true
+ }
+ }
+ return false
+}
+
+func getChartValues(chartName string) error {
+ charts := list()
+ for _, chart := range charts {
+ if chart.Name == chartName {
+ rappMap := chart.Values["rapp"]
+ fmt.Println("rappMap:", rappMap)
+ // Convert map to json string
+ jsonStr, err := json.Marshal(rappMap)
+ if err != nil {
+ fmt.Println("Error:", err)
+ return err
+ }
+
+ if err := json.Unmarshal(jsonStr, &rapp); err != nil {
+ fmt.Println("Error:", err)
+ return err
+ }
+ return nil
+ }
+ }
+ return errors.New("Chart: cannot retrieve values")
+}
+
func registrerRapp(chartName, chartType string) {
- psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
+ psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
- db, err := sql.Open("postgres", psqlconn)
- if err != nil{
- fmt.Println(err)
- } else {
- fmt.Println("Connected!")
- }
+ db, err := sql.Open("postgres", psqlconn)
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Connected!")
+ }
- defer db.Close()
+ defer db.Close()
- // create
- // hardcoded
- createStmt := `CREATE TABLE IF NOT EXISTS services (
+ // create
+ // hardcoded
+ createStmt := `CREATE TABLE IF NOT EXISTS services (
id serial PRIMARY KEY,
name VARCHAR ( 50 ) UNIQUE NOT NULL,
type VARCHAR ( 50 ) NOT NULL,
created_on TIMESTAMP DEFAULT NOW()
);`
- resp, err := db.Exec(createStmt)
- if err != nil{
- fmt.Println(err)
- } else {
- fmt.Println(resp)
- }
+ _, err = db.Exec(createStmt)
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Created table for service registry")
+ }
- // dynamic
- insertDynStmt := `insert into "services"("name", "type") values($1, $2)`
- _, err = db.Exec(insertDynStmt, chartName, chartType)
- if err != nil{
- fmt.Println(err)
- } else {
- fmt.Println(resp)
- }
+ // dynamic
+ insertDynStmt := `insert into "services"("name", "type") values($1, $2)`
+ _, err = db.Exec(insertDynStmt, chartName, chartType)
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Inserted " + chartName + " into service registry")
+ }
}
func unregistrerRapp(chartName, chartType string) {
- psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
+ psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
- db, err := sql.Open("postgres", psqlconn)
- if err != nil{
- fmt.Println(err)
- } else {
- fmt.Println("Connected!")
- }
+ db, err := sql.Open("postgres", psqlconn)
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Connected!")
+ }
- defer db.Close()
+ defer db.Close()
- // dynamic
- deleteDynStmt := `delete from services where name=$1 and type=$2`
- resp, err := db.Exec(deleteDynStmt, chartName, chartType)
- if err != nil{
- fmt.Println(err)
- } else {
- fmt.Println(resp)
- }
+ // dynamic
+ deleteDynStmt := `delete from services where name=$1 and type=$2`
+ _, err = db.Exec(deleteDynStmt, chartName, chartType)
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Deleted " + chartName + " from service registry")
+ }
}
diff --git a/rapps/rapps-istio-mgr.go b/rapps/rapps-istio-mgr.go
index 04189ce..93ed8bb 100644
--- a/rapps/rapps-istio-mgr.go
+++ b/rapps/rapps-istio-mgr.go
@@ -78,6 +78,14 @@
jwksUri: "http://192.168.49.2:31560/auth/realms/REALM-NAME/protocol/openid-connect/certs"
- issuer: "http://keycloak.default:8080/auth/realms/REALM-NAME"
jwksUri: "http://keycloak.default:8080/auth/realms/REALM-NAME/protocol/openid-connect/certs"
+ - issuer: "https://192.168.49.2:31561/auth/realms/REALM-NAME"
+ jwksUri: "https://192.168.49.2:31561/auth/realms/REALM-NAME/protocol/openid-connect/certs"
+ - issuer: "https://keycloak.default:8443/auth/realms/REALM-NAME"
+ jwksUri: "https://keycloak.default:8443/auth/realms/REALM-NAME/protocol/openid-connect/certs"
+ - issuer: "https://keycloak.est.tech:443/auth/realms/REALM-NAME"
+ jwksUri: "https://keycloak.default:8443/auth/realms/REALM-NAME/protocol/openid-connect/certs"
+ - issuer: "http://istio-ingressgateway.istio-system:80/auth/realms/REALM-NAME"
+ jwksUri: "http://keycloak.default:8080/auth/realms/REALM-NAME/protocol/openid-connect/certs"
`
var authorizationPolicyManifest = `
@@ -94,11 +102,11 @@
rules:
- from:
- source:
- requestPrincipals: ["http://192.168.49.2:31560/auth/realms/REALM-NAME/", "http://keycloak.default:8080/auth/realms/REALM-NAME/"]
+ requestPrincipals: ["http://192.168.49.2:31560/auth/realms/REALM-NAME/", "http://keycloak.default:8080/auth/realms/REALM-NAME/", "https://192.168.49.2:31561/auth/realms/REALM-NAME/", "https://keycloak.default:8443/auth/realms/REALM-NAME/", "https://keycloak.est.tech:443/auth/realms/REALM-NAME/", "http://istio-ingressgateway.istio-system:80/auth/realms/REALM-NAME/"]
- to:
- operation:
methods: ["METHOD-NAME"]
- paths: ["/RAPP-NAME*"]
+ paths: ["/RAPP-NAME"]
when:
- key: request.auth.claims[clientRole]
values: ["ROLE-NAME"]
@@ -133,11 +141,11 @@
func createGateway(clientset *versioned.Clientset, appName string) (string, error) {
gtClient := clientset.NetworkingV1beta1().Gateways(NAMESPACE)
- gatewayManifest = strings.Replace(gatewayManifest, "RAPP-NAME", appName, -1)
- gatewayManifest = strings.Replace(gatewayManifest, "RAPP-NS", NAMESPACE, -1)
+ manifest := strings.Replace(gatewayManifest, "RAPP-NAME", appName, -1)
+ manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1)
gt := &netv1beta1.Gateway{}
- dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(gatewayManifest)), 1000)
+ dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1000)
if err := dec.Decode(>); err != nil {
return "", err
@@ -155,11 +163,11 @@
func createVirtualService(clientset *versioned.Clientset, appName string) (string, error) {
vsClient := clientset.NetworkingV1beta1().VirtualServices(NAMESPACE)
- virtualServiceManifest = strings.Replace(virtualServiceManifest, "RAPP-NAME", appName, -1)
- virtualServiceManifest = strings.Replace(virtualServiceManifest, "RAPP-NS", NAMESPACE, -1)
+ manifest := strings.Replace(virtualServiceManifest, "RAPP-NAME", appName, -1)
+ manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1)
vs := &netv1beta1.VirtualService{}
- dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(virtualServiceManifest)), 1000)
+ dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1000)
if err := dec.Decode(&vs); err != nil {
return "", err
@@ -177,12 +185,12 @@
func createRequestAuthentication(clientset *versioned.Clientset, appName, realmName string) (string, error) {
raClient := clientset.SecurityV1beta1().RequestAuthentications(NAMESPACE)
- requestAuthenticationManifest = strings.Replace(requestAuthenticationManifest, "RAPP-NAME", appName, -1)
- requestAuthenticationManifest = strings.Replace(requestAuthenticationManifest, "REALM-NAME", realmName, -1)
- requestAuthenticationManifest = strings.Replace(requestAuthenticationManifest, "RAPP-NS", NAMESPACE, -1)
+ manifest := strings.Replace(requestAuthenticationManifest, "RAPP-NAME", appName, -1)
+ manifest = strings.Replace(manifest, "REALM-NAME", realmName, -1)
+ manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1)
ra := &secv1beta1.RequestAuthentication{}
- dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(requestAuthenticationManifest)), 1000)
+ dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1000)
if err := dec.Decode(&ra); err != nil {
return "", err
@@ -200,14 +208,14 @@
func createAuthorizationPolicy(clientset *versioned.Clientset, appName, realmName, roleName, methodName string) (string, error) {
apClient := clientset.SecurityV1beta1().AuthorizationPolicies(NAMESPACE)
- authorizationPolicyManifest = strings.Replace(authorizationPolicyManifest, "RAPP-NAME", appName, -1)
- authorizationPolicyManifest = strings.Replace(authorizationPolicyManifest, "REALM-NAME", realmName, -1)
- authorizationPolicyManifest = strings.Replace(authorizationPolicyManifest, "ROLE-NAME", roleName, -1)
- authorizationPolicyManifest = strings.Replace(authorizationPolicyManifest, "METHOD-NAME", methodName, -1)
- authorizationPolicyManifest = strings.Replace(authorizationPolicyManifest, "RAPP-NS", NAMESPACE, -1)
+ manifest := strings.Replace(authorizationPolicyManifest, "RAPP-NAME", appName, -1)
+ manifest = strings.Replace(manifest, "REALM-NAME", realmName, -1)
+ manifest = strings.Replace(manifest, "ROLE-NAME", roleName, -1)
+ manifest = strings.Replace(manifest, "METHOD-NAME", methodName, -1)
+ manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1)
ap := &secv1beta1.AuthorizationPolicy{}
- dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(authorizationPolicyManifest)), 1000)
+ dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1000)
if err := dec.Decode(&ap); err != nil {
return "", err
diff --git a/rapps/rapps-keycloak-mgr.go b/rapps/rapps-keycloak-mgr.go
index 0d0a29c..82097ff 100644
--- a/rapps/rapps-keycloak-mgr.go
+++ b/rapps/rapps-keycloak-mgr.go
@@ -9,10 +9,12 @@
kubernetes "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"net/http"
+ "strings"
+ "rapps/utils/pemtojwks"
)
const (
- namespace = "istio-nonrtric"
+ namespace = "istio-nonrtric"
)
func createClient(res http.ResponseWriter, req *http.Request) {
@@ -25,7 +27,9 @@
if err != nil {
msg = err.Error()
}
- createSecret(msg, clientName, realmName, role, namespace)
+ if realmName != "x509" && realmName != "jwt" {
+ createSecret(msg, clientName, realmName, role, namespace)
+ }
// create response binary data
data := []byte(msg) // slice of bytes
// write `data` to response
@@ -40,7 +44,9 @@
var msg string = "Removed keycloak " + clientName + " from " + realmName + " realm"
remove(realmName, clientName)
- removeSecret(namespace, role)
+ if realmName != "x509" && realmName != "jwt" {
+ removeSecret(namespace, role)
+ }
// create response binary data
data := []byte(msg) // slice of bytes
// write `data` to response
@@ -82,7 +88,21 @@
fmt.Println("Realm already exists", realmName)
}
- newClient := gocloak.Client{
+ flowAlias := "x509 direct grant"
+ flowId := ""
+ flows, err := client.GetAuthenticationFlows(ctx, token.AccessToken, realmName)
+ if err != nil {
+ fmt.Println("Oh no!, failed to get flows :(")
+ } else {
+ for _, flow := range flows {
+ if flow.Alias != nil && *flow.Alias == flowAlias {
+ flowId = *flow.ID
+ }
+ }
+ fmt.Println("Retrieved AuthenticationFlow id", flowId)
+ }
+
+ newClient1 := gocloak.Client{
ClientID: gocloak.StringP(clientName),
Enabled: gocloak.BoolP(true),
DirectAccessGrantsEnabled: gocloak.BoolP(true),
@@ -91,11 +111,54 @@
ServiceAccountsEnabled: gocloak.BoolP(true),
ClientAuthenticatorType: gocloak.StringP("client-secret"),
DefaultClientScopes: &[]string{"email"},
- Attributes: &map[string]string{"use.refresh.tokens": "true",
- "client_credentials.use_refresh_token": "true"},
+ Attributes: &map[string]string{"use.refresh.tokens": "true",
+ "client_credentials.use_refresh_token": "true"},
}
+
+ newClient2 := gocloak.Client{
+ ClientID: gocloak.StringP(clientName),
+ Enabled: gocloak.BoolP(true),
+ DirectAccessGrantsEnabled: gocloak.BoolP(true),
+ BearerOnly: gocloak.BoolP(false),
+ PublicClient: gocloak.BoolP(false),
+ ServiceAccountsEnabled: gocloak.BoolP(true),
+ ClientAuthenticatorType: gocloak.StringP("client-x509"),
+ DefaultClientScopes: &[]string{"openid", "profile", "email"},
+ Attributes: &map[string]string{"use.refresh.tokens": "true",
+ "client_credentials.use_refresh_token": "true",
+ "x509.subjectdn": ".*client@mail.com.*",
+ "x509.allow.regex.pattern.comparison": "true"},
+ AuthenticationFlowBindingOverrides: &map[string]string{"direct_grant": flowId},
+ }
+
+ jwksString := pemtojwks.CreateJWKS("/certs/client_pub.key", "public", "/certs/client.crt")
+ newClient3 := gocloak.Client{
+ ClientID: gocloak.StringP(clientName),
+ Enabled: gocloak.BoolP(true),
+ DirectAccessGrantsEnabled: gocloak.BoolP(true),
+ BearerOnly: gocloak.BoolP(false),
+ PublicClient: gocloak.BoolP(false),
+ ServiceAccountsEnabled: gocloak.BoolP(true),
+ ClientAuthenticatorType: gocloak.StringP("client-jwt"),
+ DefaultClientScopes: &[]string{"email"},
+ Attributes: &map[string]string{"token.endpoint.auth.signing.alg": "RS256",
+ "use.jwks.string": "true",
+ "jwks.string": jwksString,
+ },
+ }
+
+ var newClient gocloak.Client
+ if strings.HasPrefix(clientName, "x509") {
+ newClient = newClient2
+ } else if strings.HasPrefix(clientName, "jwt") {
+ newClient = newClient3
+ } else {
+ newClient = newClient1
+ }
+
clientId, err := client.CreateClient(ctx, token.AccessToken, realmName, newClient)
if err != nil {
+ fmt.Println("Failed to create client", err)
return "", err
} else {
fmt.Println("Created realm client", clientId)
@@ -119,6 +182,23 @@
fmt.Println("Service Account user", *user.Username)
}
+ if strings.HasPrefix(clientName, "x509") {
+ newUser := gocloak.User{
+ ID: gocloak.StringP(realmName + "user"),
+ Username: gocloak.StringP(realmName + "user"),
+ Email: gocloak.StringP("client@mail.com"),
+ Enabled: gocloak.BoolP(true),
+ }
+
+ realmUser, err := client.CreateUser(ctx, token.AccessToken, realmName, newUser)
+ if err != nil {
+ fmt.Println(err)
+ panic("Oh no!, failed to create user :(")
+ } else {
+ fmt.Println("Created new user", realmUser)
+ }
+ }
+
clientRole, err := client.GetClientRole(ctx, token.AccessToken, realmName, clientId, clientRoleName)
if err != nil {
fmt.Println(err)
@@ -137,8 +217,8 @@
}
clientroleMapper := gocloak.ProtocolMapperRepresentation{
- ID: gocloak.StringP("clientroleapper"),
- Name: gocloak.StringP("clientroleapper"),
+ ID: gocloak.StringP("Client Role " + clientName + " Mapper"),
+ Name: gocloak.StringP("Client Role " + clientName + " Mapper"),
Protocol: gocloak.StringP("openid-connect"),
ProtocolMapper: gocloak.StringP("oidc-usermodel-client-role-mapper"),
Config: &map[string]string{
@@ -160,6 +240,26 @@
fmt.Println("Client rolemapper added to client")
}
+ if strings.HasPrefix(clientName, "x509") {
+ clientRole := *newClient.ClientID + "." + clientRoleName
+
+ clientroleMapper := gocloak.ProtocolMapperRepresentation{
+ ID: gocloak.StringP("Hardcoded " + clientName + " Mapper"),
+ Name: gocloak.StringP("Hardcoded " + clientName + " Mapper"),
+ Protocol: gocloak.StringP("openid-connect"),
+ ProtocolMapper: gocloak.StringP("oidc-hardcoded-role-mapper"),
+ Config: &map[string]string{
+ "role": clientRole,
+ },
+ }
+ _, err = client.CreateClientProtocolMapper(ctx, token.AccessToken, realmName, clientId, clientroleMapper)
+ if err != nil {
+ return "", err
+ } else {
+ fmt.Println("Created hardcoded-role-mapper for ", clientRole)
+ }
+ }
+
_, err = client.RegenerateClientSecret(ctx, token.AccessToken, realmName, clientId)
if err != nil {
return "", err
@@ -190,7 +290,7 @@
}
func createSecret(clientSecret, clientName, realmName, role, namespace string) {
- secretName := role +"-secret"
+ secretName := role + "-secret"
clientset := connectToK8s()
secrets := clientset.CoreV1().Secrets(namespace)
secret := &corev1.Secret{
@@ -198,8 +298,8 @@
Name: secretName,
Namespace: namespace,
Labels: map[string]string{
- "app" : secretName,
- },
+ "app": secretName,
+ },
},
Type: "Opaque",
StringData: map[string]string{"client_secret": clientSecret, "client_id": clientName, "realm": realmName},
@@ -237,11 +337,29 @@
fmt.Println("Deleted client ", clientName)
}
}
+
+ userName := realmName + "user"
+ users, err := adminClient.GetUsers(ctx, token.AccessToken, realmName,
+ gocloak.GetUsersParams{
+ Username: gocloak.StringP(userName),
+ })
+ if err != nil {
+ panic("List users failed:" + err.Error())
+ }
+ for _, user := range users {
+ err = adminClient.DeleteUser(ctx, token.AccessToken, realmName, *user.ID)
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Deleted user ", userName)
+ }
+ }
+
}
func removeSecret(namespace, role string) {
clientset := connectToK8s()
- secretName := role +"-secret"
+ secretName := role + "-secret"
secrets := clientset.CoreV1().Secrets(namespace)
err := secrets.Delete(context.TODO(), secretName, metav1.DeleteOptions{})
if err != nil {
diff --git a/rapps/rapps-keycloak-mgr.yaml b/rapps/rapps-keycloak-mgr.yaml
index b7113b7..fd00a52 100644
--- a/rapps/rapps-keycloak-mgr.yaml
+++ b/rapps/rapps-keycloak-mgr.yaml
@@ -1,3 +1,34 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: keycloak-mgr-pv-volume
+ namespace: default
+ labels:
+ app: rapps-keycloak-mgr
+spec:
+ storageClassName: manual
+ capacity:
+ storage: 10Mi
+ accessModes:
+ - ReadOnlyMany
+ hostPath:
+ path: "/var/rapps/certs"
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: keycloak-mgr-pv-claim
+ namespace: default
+ labels:
+ app: rapps-keycloak-mgr
+spec:
+ storageClassName: manual
+ accessModes:
+ - ReadOnlyMany
+ resources:
+ requests:
+ storage: 10Mi
+---
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -28,6 +59,13 @@
requests:
memory: 128Mi
cpu: "80m"
+ volumeMounts:
+ - name: keycloak-mgr-cert-storage
+ mountPath: /certs
+ volumes:
+ - name: keycloak-mgr-cert-storage
+ persistentVolumeClaim:
+ claimName: keycloak-mgr-pv-claim
serviceAccountName: helm-app
replicas: 1
---
diff --git a/rapps/rapps-rapp-auth-provider.go b/rapps/rapps-rapp-auth-provider.go
new file mode 100644
index 0000000..e84c11c
--- /dev/null
+++ b/rapps/rapps-rapp-auth-provider.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+)
+
+type Jwttoken struct {
+ Access_token string
+ Expires_in int
+ Refresh_expires_in int
+ Refresh_token string
+ Token_type string
+ Not_before_policy int
+ Session_state string
+ Scope string
+}
+
+var jwt Jwttoken
+
+func getToken(auth_code string) string {
+ clientSecret := "Ctz6aBahmjQvAt7Lwgg8qDNsniuPkNCC"
+ clientId := "jwtsecret"
+ realmName := "jwtrealm"
+ keycloakHost := "keycloak"
+ keycloakPort := "8080"
+ keycloakUrl := "http://" + keycloakHost + ":" + keycloakPort + "/auth/realms/" + realmName + "/protocol/openid-connect/token"
+ resp, err := http.PostForm(keycloakUrl,
+ url.Values{"code": {auth_code}, "grant_type": {"authorization_code"},
+ "client_id": {clientId}, "client_secret": {clientSecret}})
+ if err != nil {
+ fmt.Println(err)
+ panic("Something wrong with the credentials or url ")
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ fmt.Println(string(body))
+ json.Unmarshal([]byte(body), &jwt)
+ return jwt.Access_token
+}
+
+func noprefix(res http.ResponseWriter, req *http.Request) {
+ // create response binary data
+ data := []byte("Authorization code default") // slice of bytes
+ // write `data` to response
+ res.Write(data)
+}
+
+func callback(res http.ResponseWriter, req *http.Request) {
+ query := req.URL.Query()
+ code := query.Get("code")
+ token := getToken(code)
+ res.WriteHeader(http.StatusOK)
+ res.Write([]byte(token))
+}
+
+func main() {
+ // create a new handler
+ callbackHandler := http.HandlerFunc(callback)
+ http.Handle("/callback", callbackHandler)
+ noPrefixHandler := http.HandlerFunc(noprefix)
+ http.Handle("/", noPrefixHandler)
+ http.ListenAndServe(":9000", nil)
+}
diff --git a/rapps/rapps-rapp-invoker.go b/rapps/rapps-rapp-invoker.go
index d00c589..1a73006 100644
--- a/rapps/rapps-rapp-invoker.go
+++ b/rapps/rapps-rapp-invoker.go
@@ -79,7 +79,7 @@
var jsonValue []byte = []byte{}
var restUrl string = ""
- if service == "rapp-provider" && securityEnabled == "true" {
+ if securityEnabled == "true" {
secretName := role + "-secret"
token = getToken(secretName)
} else {
@@ -143,8 +143,8 @@
time.Sleep(1 * time.Second)
flag.StringVar(&gatewayHost, "gatewayHost", "istio-ingressgateway.istio-system", "Gateway Host")
flag.StringVar(&gatewayPort, "gatewayPort", "80", "Gateway Port")
- flag.StringVar(&keycloakHost, "keycloakHost", "keycloak.default", "Keycloak Host")
- flag.StringVar(&keycloakPort, "keycloakPort", "8080", "Keycloak Port")
+ flag.StringVar(&keycloakHost, "keycloakHost", "istio-ingressgateway.istio-system", "Keycloak Host")
+ flag.StringVar(&keycloakPort, "keycloakPort", "80", "Keycloak Port")
flag.StringVar(&useGateway, "useGateway", "Y", "Connect to services through API gateway")
flag.StringVar(&securityEnabled, "securityEnabled", "true", "Security is required to use this application")
flag.StringVar(&role, "role", "provider-viewer", "Role granted to application")
diff --git a/rapps/rapps-rapp-jwt-invoker.go b/rapps/rapps-rapp-jwt-invoker.go
new file mode 100644
index 0000000..de806ca
--- /dev/null
+++ b/rapps/rapps-rapp-jwt-invoker.go
@@ -0,0 +1,174 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+ "rapps/utils/generatejwt"
+)
+
+type Jwttoken struct {
+ Access_token string
+ Expires_in int
+ Refresh_expires_in int
+ Refresh_token string
+ Token_type string
+ Not_before_policy int
+ Session_state string
+ Scope string
+}
+
+var gatewayHost string
+var gatewayPort string
+var keycloakHost string
+var keycloakPort string
+var keycloakAlias string
+var securityEnabled string
+var useGateway string
+var role string
+var rapp string
+var methods string
+var realmName string
+var clientId string
+var healthy bool = true
+var ttime time.Time
+var jwt Jwttoken
+
+const (
+ namespace = "istio-nonrtric"
+ scope = "email"
+ client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
+)
+
+func getToken() string {
+ if ttime.Before(time.Now()) {
+ client_assertion := getClientAssertion()
+ keycloakUrl := "http://" + keycloakHost + ":" + keycloakPort + "/auth/realms/" + realmName + "/protocol/openid-connect/token"
+ resp, err := http.PostForm(keycloakUrl, url.Values{"client_assertion_type": {client_assertion_type},
+ "client_assertion": {client_assertion}, "grant_type": {"client_credentials"}, "client_id": {clientId},
+ "scope": {scope}})
+ if err != nil {
+ fmt.Println(err)
+ panic("Something wrong with the credentials or url ")
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ json.Unmarshal([]byte(body), &jwt)
+ ttime = time.Now()
+ ttime = ttime.Add(time.Second * time.Duration(jwt.Expires_in))
+ }
+ return jwt.Access_token
+}
+
+func getClientAssertion() string {
+ realm := "http://" + keycloakHost + ":" + keycloakPort + "/auth/realms/" + realmName
+ clientAssertion := generatejwt.CreateJWT("/certs/client.key", "/certs/client_pub.key", "", clientId, realm)
+ return clientAssertion
+}
+
+func MakeRequest(client *http.Client, prefix string, method string, ch chan string) {
+ var service = strings.Split(prefix, "/")[1]
+ var gatewayUrl = "http://" + gatewayHost + ":" + gatewayPort
+ var token = ""
+ var jsonValue []byte = []byte{}
+ var restUrl string = ""
+
+ if securityEnabled == "true" {
+ token = getToken()
+ } else {
+ useGateway = "N"
+ }
+
+ if strings.ToUpper(useGateway) != "Y" {
+ gatewayUrl = "http://" + service + "." + namespace + ":80"
+ prefix = ""
+ }
+
+ restUrl = gatewayUrl + prefix
+
+ req, err := http.NewRequest(method, restUrl, bytes.NewBuffer(jsonValue))
+ if err != nil {
+ fmt.Printf("Got error %s", err.Error())
+ }
+ req.Header.Set("Content-type", "application/json")
+ req.Header.Set("Authorization", "Bearer "+token)
+
+ resp, err := client.Do(req)
+ if err != nil {
+ fmt.Printf("Got error %s", err.Error())
+ }
+ defer resp.Body.Close()
+ body, _ := ioutil.ReadAll(resp.Body)
+
+ respString := string(body[:])
+ if respString == "RBAC: access denied" {
+ respString += " for " + service + " " + strings.ToLower(method) + " request"
+ }
+ fmt.Printf("Received response for %s %s request - %s\n", service, strings.ToLower(method), respString)
+ ch <- prefix + "," + method
+}
+
+func health(res http.ResponseWriter, req *http.Request) {
+ if healthy {
+ res.WriteHeader(http.StatusOK)
+ res.Write([]byte("healthy"))
+ } else {
+ res.WriteHeader(http.StatusInternalServerError)
+ res.Write([]byte("unhealthy"))
+ }
+}
+
+func main() {
+ ttime = time.Now()
+ time.Sleep(1 * time.Second)
+ flag.StringVar(&gatewayHost, "gatewayHost", "istio-ingressgateway.istio-system", "Gateway Host")
+ flag.StringVar(&gatewayPort, "gatewayPort", "80", "Gateway Port")
+ flag.StringVar(&keycloakHost, "keycloakHost", "istio-ingressgateway.istio-system", "Keycloak Host")
+ flag.StringVar(&keycloakPort, "keycloakPort", "80", "Keycloak Port")
+ flag.StringVar(&useGateway, "useGateway", "Y", "Connect to services through API gateway")
+ flag.StringVar(&securityEnabled, "securityEnabled", "true", "Security is required to use this application")
+ flag.StringVar(&realmName, "realm", "jwt", "Keycloak realm")
+ flag.StringVar(&clientId, "client", "jwtprovider-cli", "Keycloak client")
+ flag.StringVar(&role, "role", "provider-viewer", "Role granted to application")
+ flag.StringVar(&rapp, "rapp", "rapp-jwt-provider", "Name of rapp to invoke")
+ flag.StringVar(&methods, "methods", "GET", "Methods to access application")
+ flag.Parse()
+
+ healthHandler := http.HandlerFunc(health)
+ http.Handle("/health", healthHandler)
+ go func() {
+ http.ListenAndServe(":9000", nil)
+ }()
+
+ client := &http.Client{
+ Timeout: time.Second * 10,
+ }
+
+ ch := make(chan string)
+ var prefixArray []string = []string{"/" + rapp}
+ var methodArray []string = []string{methods}
+ for _, prefix := range prefixArray {
+ for _, method := range methodArray {
+ go MakeRequest(client, prefix, method, ch)
+ }
+ }
+
+ ioutil.WriteFile("init.txt", []byte("Initialization done."), 0644)
+
+ for r := range ch {
+ go func(resp string) {
+ time.Sleep(10 * time.Second)
+ elements := strings.Split(resp, ",")
+ prefix := elements[0]
+ method := elements[1]
+ MakeRequest(client, prefix, method, ch)
+ }(r)
+ }
+
+}
diff --git a/rapps/rapps-rapp-jwt-provider.go b/rapps/rapps-rapp-jwt-provider.go
new file mode 100644
index 0000000..b0abbb4
--- /dev/null
+++ b/rapps/rapps-rapp-jwt-provider.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "net/http"
+)
+
+// create a handler struct
+type HttpHandler struct{}
+
+// implement `ServeHTTP` method on `HttpHandler` struct
+func (h HttpHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
+ // create response binary data
+ data := []byte("Hello World JWT!") // slice of bytes
+ // write `data` to response
+ res.Write(data)
+}
+
+func main() {
+ // create a new handler
+ handler := HttpHandler{}
+ // listen and serve
+ http.ListenAndServe(":9000", handler)
+}
diff --git a/rapps/rapps-rapp-x509-invoker.go b/rapps/rapps-rapp-x509-invoker.go
new file mode 100644
index 0000000..bdebc7a
--- /dev/null
+++ b/rapps/rapps-rapp-x509-invoker.go
@@ -0,0 +1,201 @@
+package main
+
+import (
+ "bytes"
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+)
+
+type Jwttoken struct {
+ Access_token string
+ Expires_in int
+ Refresh_expires_in int
+ Refresh_token string
+ Token_type string
+ Not_before_policy int
+ Session_state string
+ Scope string
+}
+
+var gatewayHost string
+var gatewayPort string
+var keycloakHost string
+var keycloakPort string
+var keycloakAlias string
+var securityEnabled string
+var useGateway string
+var role string
+var rapp string
+var methods string
+var realmName string
+var clientId string
+var healthy bool = true
+var ttime time.Time
+var jwt Jwttoken
+
+const (
+ namespace = "istio-nonrtric"
+ scope = "openid profile"
+)
+
+func getToken() string {
+ if ttime.Before(time.Now()) {
+ client := getClient()
+ keycloakUrl := "https://" + keycloakAlias + ":" + keycloakPort + "/auth/realms/" + realmName + "/protocol/openid-connect/token"
+ resp, err := client.PostForm(keycloakUrl, url.Values{"username": {""}, "password": {""}, "grant_type": {"password"}, "client_id": {clientId}, "scope": {scope}})
+ if err != nil {
+ fmt.Println(err)
+ panic("Something wrong with the credentials or url ")
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ json.Unmarshal([]byte(body), &jwt)
+ ttime = time.Now()
+ ttime = ttime.Add(time.Second * time.Duration(jwt.Expires_in))
+ }
+ return jwt.Access_token
+}
+
+func getClient() *http.Client {
+ caCert, _ := ioutil.ReadFile("/certs/rootCA.crt")
+ caCertPool := x509.NewCertPool()
+ caCertPool.AppendCertsFromPEM(caCert)
+
+ cert, _ := tls.LoadX509KeyPair("/certs/client.crt", "/certs/client.key")
+
+ dialer := &net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ DualStack: true,
+ }
+
+ client := &http.Client{
+ Transport: &http.Transport{
+ DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+ fmt.Println("address original =", addr)
+ if addr == keycloakAlias+":"+keycloakPort {
+ addr = keycloakHost + ":" + keycloakPort
+ fmt.Println("address modified =", addr)
+ }
+ return dialer.DialContext(ctx, network, addr)
+ },
+ TLSClientConfig: &tls.Config{
+ RootCAs: caCertPool,
+ Certificates: []tls.Certificate{cert},
+ },
+ },
+ }
+ return client
+}
+
+func MakeRequest(client *http.Client, prefix string, method string, ch chan string) {
+ var service = strings.Split(prefix, "/")[1]
+ var gatewayUrl = "http://" + gatewayHost + ":" + gatewayPort
+ var token = ""
+ var jsonValue []byte = []byte{}
+ var restUrl string = ""
+
+ if securityEnabled == "true" {
+ token = getToken()
+ } else {
+ useGateway = "N"
+ }
+
+ if strings.ToUpper(useGateway) != "Y" {
+ gatewayUrl = "http://" + service + "." + namespace + ":80"
+ prefix = ""
+ }
+
+ restUrl = gatewayUrl + prefix
+
+ req, err := http.NewRequest(method, restUrl, bytes.NewBuffer(jsonValue))
+ if err != nil {
+ fmt.Printf("Got error %s", err.Error())
+ }
+ req.Header.Set("Content-type", "application/json")
+ req.Header.Set("Authorization", "Bearer "+token)
+
+ resp, err := client.Do(req)
+ if err != nil {
+ fmt.Printf("Got error %s", err.Error())
+ }
+ defer resp.Body.Close()
+ body, _ := ioutil.ReadAll(resp.Body)
+
+ respString := string(body[:])
+ if respString == "RBAC: access denied" {
+ respString += " for " + service + " " + strings.ToLower(method) + " request"
+ }
+ fmt.Printf("Received response for %s %s request - %s\n", service, strings.ToLower(method), respString)
+ ch <- prefix + "," + method
+}
+
+func health(res http.ResponseWriter, req *http.Request) {
+ if healthy {
+ res.WriteHeader(http.StatusOK)
+ res.Write([]byte("healthy"))
+ } else {
+ res.WriteHeader(http.StatusInternalServerError)
+ res.Write([]byte("unhealthy"))
+ }
+}
+
+func main() {
+ ttime = time.Now()
+ time.Sleep(1 * time.Second)
+ flag.StringVar(&gatewayHost, "gatewayHost", "istio-ingressgateway.istio-system", "Gateway Host")
+ flag.StringVar(&gatewayPort, "gatewayPort", "80", "Gateway Port")
+ flag.StringVar(&keycloakHost, "keycloakHost", "istio-ingressgateway.istio-system", "Keycloak Host")
+ flag.StringVar(&keycloakPort, "keycloakPort", "443", "Keycloak Port")
+ flag.StringVar(&keycloakAlias, "keycloakAlias", "keycloak.est.tech", "Keycloak URL Alias")
+ flag.StringVar(&useGateway, "useGateway", "Y", "Connect to services through API gateway")
+ flag.StringVar(&securityEnabled, "securityEnabled", "true", "Security is required to use this application")
+ flag.StringVar(&realmName, "realm", "x509", "Keycloak realm")
+ flag.StringVar(&clientId, "client", "x509provider-cli", "Keycloak client")
+ flag.StringVar(&role, "role", "provider-viewer", "Role granted to application")
+ flag.StringVar(&rapp, "rapp", "rapp-x509-provider", "Name of rapp to invoke")
+ flag.StringVar(&methods, "methods", "GET", "Methods to access application")
+ flag.Parse()
+
+ healthHandler := http.HandlerFunc(health)
+ http.Handle("/health", healthHandler)
+ go func() {
+ http.ListenAndServe(":9000", nil)
+ }()
+
+ client := &http.Client{
+ Timeout: time.Second * 10,
+ }
+
+ ch := make(chan string)
+ var prefixArray []string = []string{"/" + rapp}
+ var methodArray []string = []string{methods}
+ for _, prefix := range prefixArray {
+ for _, method := range methodArray {
+ go MakeRequest(client, prefix, method, ch)
+ }
+ }
+
+ ioutil.WriteFile("init.txt", []byte("Initialization done."), 0644)
+
+ for r := range ch {
+ go func(resp string) {
+ time.Sleep(10 * time.Second)
+ elements := strings.Split(resp, ",")
+ prefix := elements[0]
+ method := elements[1]
+ MakeRequest(client, prefix, method, ch)
+ }(r)
+ }
+
+}
diff --git a/rapps/rapps-rapp-x509-provider.go b/rapps/rapps-rapp-x509-provider.go
new file mode 100644
index 0000000..f4bf4a8
--- /dev/null
+++ b/rapps/rapps-rapp-x509-provider.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "net/http"
+)
+
+// create a handler struct
+type HttpHandler struct{}
+
+// implement `ServeHTTP` method on `HttpHandler` struct
+func (h HttpHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
+ // create response binary data
+ data := []byte("Hello World X509!") // slice of bytes
+ // write `data` to response
+ res.Write(data)
+}
+
+func main() {
+ // create a new handler
+ handler := HttpHandler{}
+ // listen and serve
+ http.ListenAndServe(":9000", handler)
+}
diff --git a/rapps/utils/generatejwt/generatejwt.go b/rapps/utils/generatejwt/generatejwt.go
new file mode 100644
index 0000000..666be24
--- /dev/null
+++ b/rapps/utils/generatejwt/generatejwt.go
@@ -0,0 +1,134 @@
+package generatejwt
+
+import (
+ "fmt"
+ "github.com/dgrijalva/jwt-go"
+ "io/ioutil"
+ "log"
+ "time"
+)
+
+type JWT struct {
+ privateKey []byte
+ publicKey []byte
+}
+
+func NewJWT(privateKey []byte, publicKey []byte) JWT {
+ return JWT{
+ privateKey: privateKey,
+ publicKey: publicKey,
+ }
+}
+
+func readFile(file string) []byte {
+ key, err := ioutil.ReadFile(file)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ return key
+}
+
+func (j JWT) createWithKey(ttl time.Duration, content interface{}, client , realm string) (string, error) {
+ key, err := jwt.ParseRSAPrivateKeyFromPEM(j.privateKey)
+ if err != nil {
+ return "", fmt.Errorf("create: parse key: %w", err)
+ }
+
+ now := time.Now().UTC()
+
+ claims := make(jwt.MapClaims)
+ claims["dat"] = content // Our custom data.
+ claims["exp"] = now.Add(ttl).Unix() // The expiration time after which the token must be disregarded.
+ claims["iat"] = now.Unix() // The time at which the token was issued.
+ claims["nbf"] = now.Unix() // The time before which the token must be disregarded.
+ claims["jti"] = "myJWTId" + fmt.Sprint(now.UnixNano())
+ claims["sub"] = client //"jwtkey"
+ claims["iss"] = client //"jwtkey"
+ claims["aud"] = realm //"https://192.168.49.2:31561/auth/realms/jwtrealm"
+
+ token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
+ //token.Header["kid"] = "AKAwbsKtqu9OmIwIsPOUf5zTJkIC73hzY9Myv4srjTs"
+ tokenString, err := token.SignedString(key)
+ if err != nil {
+ return "", fmt.Errorf("create: sign token: %w", err)
+ }
+
+ return tokenString, nil
+}
+
+func createWithSecret(ttl time.Duration, content interface{}, secret string) (string, error) {
+ now := time.Now().UTC()
+
+ claims := make(jwt.MapClaims)
+ claims["dat"] = content // Our custom data.
+ claims["exp"] = now.Add(ttl).Unix() // The expiration time after which the token must be disregarded.
+ claims["iat"] = now.Unix() // The time at which the token was issued.
+ claims["nbf"] = now.Unix() // The time before which the token must be disregarded.
+ claims["jti"] = "myJWTId" + fmt.Sprint(now.UnixNano())
+ claims["sub"] = "jwtsecret"
+ claims["iss"] = "jwtsecret"
+ claims["aud"] = "https://192.168.49.2:31561/auth/realms/jwtrealm"
+
+ token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret))
+ if err != nil {
+ return "", fmt.Errorf("create: sign token: %w", err)
+ }
+
+ return token, nil
+}
+
+func (j JWT) Validate(token string) (interface{}, error) {
+ key, err := jwt.ParseRSAPublicKeyFromPEM(j.publicKey)
+ if err != nil {
+ return "", fmt.Errorf("validate: parse key: %w", err)
+ }
+
+ tok, err := jwt.Parse(token, func(jwtToken *jwt.Token) (interface{}, error) {
+ if _, ok := jwtToken.Method.(*jwt.SigningMethodRSA); !ok {
+ return nil, fmt.Errorf("unexpected method: %s", jwtToken.Header["alg"])
+ }
+
+ return key, nil
+ })
+ if err != nil {
+ return nil, fmt.Errorf("validate: %w", err)
+ }
+
+ claims, ok := tok.Claims.(jwt.MapClaims)
+ if !ok || !tok.Valid {
+ return nil, fmt.Errorf("validate: invalid")
+ }
+
+ return claims["dat"], nil
+}
+
+
+func CreateJWT(privateKeyFile, publicKeyFile, secret, client, realm string) string {
+ if secret == "" {
+ prvKey := readFile(privateKeyFile)
+ pubKey := readFile(publicKeyFile)
+
+ jwtToken := NewJWT(prvKey, pubKey)
+
+ // 1. Create a new JWT token.
+ tok, err := jwtToken.createWithKey(time.Hour, "Can be anything", client, realm)
+ if err != nil {
+ log.Fatalln(err)
+ }
+
+ // 2. Validate an existing JWT token.
+ _, err = jwtToken.Validate(tok)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ return tok
+ } else {
+ // 1. Create a new JWT token.
+ tok, err := createWithSecret(time.Hour, "Can be anything", secret)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ return tok
+ }
+
+}
diff --git a/rapps/utils/pemtojwks/pemtojwks.go b/rapps/utils/pemtojwks/pemtojwks.go
new file mode 100644
index 0000000..6592916
--- /dev/null
+++ b/rapps/utils/pemtojwks/pemtojwks.go
@@ -0,0 +1,115 @@
+package pemtojwks
+
+import (
+ "crypto/rsa"
+ "crypto/sha1"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/json"
+ "encoding/pem"
+ "fmt"
+ "golang.org/x/crypto/ssh"
+ "io/ioutil"
+ "math/big"
+)
+
+type Jwks struct {
+ Keys []Key `json:"keys"`
+}
+type Key struct {
+ Kid string `json:"kid,omitempty"`
+ Kty string `json:"kty"`
+ Use string `json:"use"`
+ N string `json:"n"`
+ E string `json:"e"`
+ X5c []string `json:"x5c"`
+ X5t string `json:"x5t"`
+}
+
+func getKeyFromPrivate(key []byte) *rsa.PublicKey {
+ parsed, err := ssh.ParseRawPrivateKey(key)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ // Convert back to an *rsa.PrivateKey
+ privateKey := parsed.(*rsa.PrivateKey)
+
+ publicKey := &privateKey.PublicKey
+ return publicKey
+}
+
+func getKeyFromPublic(key []byte) *rsa.PublicKey {
+ pubPem, _ := pem.Decode(key)
+
+ parsed, err := x509.ParsePKIXPublicKey(pubPem.Bytes)
+ if err != nil {
+ fmt.Println("Unable to parse RSA public key", err)
+ }
+
+ // Convert back to an *rsa.PublicKey
+ publicKey := parsed.(*rsa.PublicKey)
+
+ return publicKey
+}
+
+func getCert(cert []byte) *x509.Certificate {
+ certPem, _ := pem.Decode(cert)
+ if certPem == nil {
+ panic("Failed to parse pem file")
+ }
+
+ // pass cert bytes
+ certificate, err := x509.ParseCertificate(certPem.Bytes)
+ if err != nil {
+ fmt.Println("Unable to parse Certificate", err)
+ }
+
+ return certificate
+}
+
+func CreateJWKS(keyFile, keyType, certFile string) string {
+ key, err := ioutil.ReadFile(keyFile)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ var publicKey *rsa.PublicKey
+
+ if keyType == "public" {
+ publicKey = getKeyFromPublic(key)
+ } else {
+ publicKey = getKeyFromPrivate(key)
+ }
+
+ cert, err := ioutil.ReadFile(certFile)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ certificate := getCert(cert)
+ // generate fingerprint with sha1
+ // you can also use md5, sha256, etc.
+ fingerprint := sha1.Sum(certificate.Raw)
+
+
+ jwksKey := Key{
+ Kid: "SIGNING_KEY",
+ Kty: "RSA",
+ Use: "sig",
+ N: base64.RawStdEncoding.EncodeToString(publicKey.N.Bytes()),
+ E: base64.RawStdEncoding.EncodeToString(big.NewInt(int64(publicKey.E)).Bytes()),
+ X5c: []string{base64.RawStdEncoding.EncodeToString(certificate.Raw)},
+ X5t: base64.RawStdEncoding.EncodeToString(fingerprint[:]),
+ }
+ jwksKeys := []Key{jwksKey}
+ jwks := Jwks{jwksKeys}
+
+ jwksJson, err := json.Marshal(jwks)
+ if err != nil {
+ fmt.Println(err)
+ return err.Error()
+ }
+ return string(jwksJson)
+
+}