From f8b535d5c118599c28e4ef6bdafc5a1f0e95dc86 Mon Sep 17 00:00:00 2001
From: Ivan Josipovic <9521987+IvanJosipovic@users.noreply.github.com>
Date: Thu, 29 Sep 2022 19:33:20 -0700
Subject: [PATCH] feat: initial version (#3)
---
.github/renovate.json | 16 ++
.github/workflows/cicd.yml | 91 +++++++++
.releaserc.json | 23 +++
README.md | 69 ++++++-
charts/ingress-nginx-validate-jwt/.helmignore | 23 +++
charts/ingress-nginx-validate-jwt/Chart.yaml | 24 +++
.../templates/NOTES.txt | 22 +++
.../templates/_helpers.tpl | 62 ++++++
.../templates/deployment.yaml | 66 +++++++
.../templates/hpa.yaml | 28 +++
.../templates/ingress.yaml | 61 ++++++
.../templates/service.yaml | 15 ++
.../templates/serviceaccount.yaml | 12 ++
.../templates/tests/test-connection.yaml | 15 ++
charts/ingress-nginx-validate-jwt/values.yaml | 91 +++++++++
docs/validate-jwt.drawio | 1 +
docs/validate-jwt.png | Bin 0 -> 45019 bytes
global.json | 7 +
ingress-nginx-validate-jwt-tests/UnitTest1.cs | 183 ++++++++++++++++++
ingress-nginx-validate-jwt-tests/Usings.cs | 1 +
.../ingress-nginx-validate-jwt-tests.csproj | 31 +++
ingress-nginx-validate-jwt.sln | 38 ++++
.../Controllers/AuthController.cs | 88 +++++++++
.../Controllers/HealthController.cs | 26 +++
ingress-nginx-validate-jwt/HostedService.cs | 28 +++
.../ISettingsService.cs | 9 +
ingress-nginx-validate-jwt/Program.cs | 36 ++++
.../Properties/launchSettings.json | 14 ++
ingress-nginx-validate-jwt/SettingsService.cs | 41 ++++
.../appsettings.Development.json | 11 ++
ingress-nginx-validate-jwt/appsettings.json | 10 +
.../ingress-nginx-validate-jwt.csproj | 28 +++
32 files changed, 1169 insertions(+), 1 deletion(-)
create mode 100644 .github/renovate.json
create mode 100644 .github/workflows/cicd.yml
create mode 100644 .releaserc.json
create mode 100644 charts/ingress-nginx-validate-jwt/.helmignore
create mode 100644 charts/ingress-nginx-validate-jwt/Chart.yaml
create mode 100644 charts/ingress-nginx-validate-jwt/templates/NOTES.txt
create mode 100644 charts/ingress-nginx-validate-jwt/templates/_helpers.tpl
create mode 100644 charts/ingress-nginx-validate-jwt/templates/deployment.yaml
create mode 100644 charts/ingress-nginx-validate-jwt/templates/hpa.yaml
create mode 100644 charts/ingress-nginx-validate-jwt/templates/ingress.yaml
create mode 100644 charts/ingress-nginx-validate-jwt/templates/service.yaml
create mode 100644 charts/ingress-nginx-validate-jwt/templates/serviceaccount.yaml
create mode 100644 charts/ingress-nginx-validate-jwt/templates/tests/test-connection.yaml
create mode 100644 charts/ingress-nginx-validate-jwt/values.yaml
create mode 100644 docs/validate-jwt.drawio
create mode 100644 docs/validate-jwt.png
create mode 100644 global.json
create mode 100644 ingress-nginx-validate-jwt-tests/UnitTest1.cs
create mode 100644 ingress-nginx-validate-jwt-tests/Usings.cs
create mode 100644 ingress-nginx-validate-jwt-tests/ingress-nginx-validate-jwt-tests.csproj
create mode 100644 ingress-nginx-validate-jwt.sln
create mode 100644 ingress-nginx-validate-jwt/Controllers/AuthController.cs
create mode 100644 ingress-nginx-validate-jwt/Controllers/HealthController.cs
create mode 100644 ingress-nginx-validate-jwt/HostedService.cs
create mode 100644 ingress-nginx-validate-jwt/ISettingsService.cs
create mode 100644 ingress-nginx-validate-jwt/Program.cs
create mode 100644 ingress-nginx-validate-jwt/Properties/launchSettings.json
create mode 100644 ingress-nginx-validate-jwt/SettingsService.cs
create mode 100644 ingress-nginx-validate-jwt/appsettings.Development.json
create mode 100644 ingress-nginx-validate-jwt/appsettings.json
create mode 100644 ingress-nginx-validate-jwt/ingress-nginx-validate-jwt.csproj
diff --git a/.github/renovate.json b/.github/renovate.json
new file mode 100644
index 0000000..cb78dad
--- /dev/null
+++ b/.github/renovate.json
@@ -0,0 +1,16 @@
+{
+ "enabled": true,
+ "timezone": "America/Vancouver",
+ "dependencyDashboard": true,
+ "dependencyDashboardTitle": "Renovate Dashboard",
+ "commitMessageSuffix": "",
+ "commitBody": "",
+ "semanticCommits": "enabled",
+ "suppressNotifications": ["prIgnoreNotification"],
+ "rebaseWhen": "conflicted",
+ "assignees": ["@ivanjosipovic"],
+ "extends": [
+ "config:base"
+ ]
+}
+
diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml
new file mode 100644
index 0000000..d5d7a8a
--- /dev/null
+++ b/.github/workflows/cicd.yml
@@ -0,0 +1,91 @@
+name: CICD
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - 'main'
+ - 'alpha'
+ - 'beta'
+ - 'dev'
+ pull_request:
+ types: [opened, reopened, synchronize]
+
+env:
+ semantic_version: 19
+
+jobs:
+ create-release:
+ name: Create Release
+ runs-on: ubuntu-latest
+ outputs:
+ new_release_published: ${{ steps.semantic.outputs.new_release_published }}
+ new_release_version: ${{ (steps.semantic.outputs.new_release_published && steps.semantic.outputs.new_release_version) || '0.0.1' }}
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Configure Git
+ run: |
+ git config user.name "$GITHUB_ACTOR"
+ git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
+
+ - name: Semantic Release
+ uses: cycjimmy/semantic-release-action@v2
+ id: semantic
+ with:
+ semantic_version: ${{ env.semantic_version }}
+ dry_run: true
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v2
+ with:
+ global-json-file: global.json
+
+ - name: .NET Build
+ run: dotnet build -c Release
+
+ - name: .NET Test
+ run: dotnet test -c Release --collect:"XPlat Code Coverage"
+
+ - name: Coverage
+ uses: codecov/codecov-action@v3
+ with:
+ file: coverage.cobertura.xml
+
+ - name: Build Image
+ run: dotnet publish -c Release --os linux --arch x64 -p:PublishProfile=DefaultContainer -p:Version=${{ (steps.semantic.outputs.new_release_published && steps.semantic.outputs.new_release_version) || '0.0.1' }}
+
+ - name: Docker Push
+ if: steps.semantic.outputs.new_release_published == 'true'
+ run: |
+ echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${GITHUB_ACTOR} --password-stdin
+ docker tag ingress-nginx-validate-jwt:${{ (steps.semantic.outputs.new_release_published && steps.semantic.outputs.new_release_version) }} ghcr.io/${GITHUB_REPOSITORY,,}/ingress-nginx-validate-jwt:${{ (steps.semantic.outputs.new_release_published && steps.semantic.outputs.new_release_version) }}
+ docker push ghcr.io/${GITHUB_REPOSITORY,,}/ingress-nginx-validate-jwt:${{ (steps.semantic.outputs.new_release_published && steps.semantic.outputs.new_release_version) }}
+
+ - name: Semantic Release
+ if: steps.semantic.outputs.new_release_published == 'true'
+ uses: cycjimmy/semantic-release-action@v2
+ with:
+ semantic_version: ${{ env.semantic_version }}
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Install Helm
+ if: steps.semantic.outputs.new_release_published == 'true'
+ uses: azure/setup-helm@v3
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Update Helm Versionin
+ if: steps.semantic.outputs.new_release_published == 'true'
+ shell: bash
+ run: |
+ sed -i 's/0.0.1/${{ (steps.semantic.outputs.new_release_published && steps.semantic.outputs.new_release_version) || '0.0.1' }}/' ./charts/ingress-nginx-validate-jwt/Chart.yaml
+
+ - name: Run chart-releaser
+ if: steps.semantic.outputs.new_release_published == 'true'
+ uses: helm/chart-releaser-action@v1
+ env:
+ CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
\ No newline at end of file
diff --git a/.releaserc.json b/.releaserc.json
new file mode 100644
index 0000000..6cab66f
--- /dev/null
+++ b/.releaserc.json
@@ -0,0 +1,23 @@
+{
+ "branches": [
+ "main",
+ {
+ "name": "beta",
+ "prerelease": true
+ },
+ {
+ "name": "alpha",
+ "prerelease": true
+ }
+ ],
+ "plugins": [
+ "@semantic-release/commit-analyzer",
+ "@semantic-release/release-notes-generator",
+ [
+ "@semantic-release/github",
+ {
+ "assets": []
+ }
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 30e209b..01f988d 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,69 @@
# ingress-nginx-validate-jwt
-ingress-nginx-validate-jwt
+
+[![codecov](https://codecov.io/gh/IvanJosipovic/ingress-nginx-validate-jwt/branch/main/graph/badge.svg?token=hh1FWYrH5r)](https://codecov.io/gh/IvanJosipovic/ingress-nginx-validate-jwt)
+
+## What is this?
+
+This project is an API server which is used along with the [nginx.ingress.kubernetes.io/auth-url](https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#external-authentication) annotation for ingress-nginx and enables per Ingress customizable JWT validation.
+
+## Install
+
+```bash
+helm repo add ingress-nginx-validate-jwt https://ivanjosipovic.github.io/ingress-nginx-validate-jwt
+
+helm repo update
+
+helm install ingress-nginx-validate-jwt \
+ingress-nginx-validate-jwt/ingress-nginx-validate-jwt \
+--create-namespace \
+--namespace ingress-nginx-validate-jwt \
+--set openIdProviderConfigurationUrl="https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"
+```
+
+### Options
+
+- openIdProviderConfigurationUrl
+ - OpenID Provider Configuration Url for your Identity Provider
+- logLevel
+ - Logging Level (Trace, Debug, Information, Warning, Error, Critical, and None)
+- [Helm Values](charts/ingress-nginx-validate-jwt/values.yaml)
+
+## Configure Ingress
+
+```yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: ingress
+ namespace: default
+ annotations:
+ nginx.ingress.kubernetes.io/auth-url: http://ingress-nginx-validate-jwt.ingress-nginx-validate-jwt.svc.cluster.local/auth?tid=11111111-1111-1111-1111-111111111111&aud=22222222-2222-2222-2222-222222222222&aud=33333333-3333-3333-3333-333333333333
+spec:
+```
+
+## Parameters
+
+The /auth endpoint supports configurable parameters in the format of {claim}={value}. In the case the same claim is called more than once, the traffic will have to match only one.
+
+For example, using the following query string
+/auth?
+tid=11111111-1111-1111-1111-111111111111
+&aud=22222222-2222-2222-2222-222222222222
+&aud=33333333-3333-3333-3333-333333333333
+
+Along with validating the JWT token, the token must have a claim tid=11111111-1111-1111-1111-111111111111 and one of aud=22222222-2222-2222-2222-222222222222
+ or aud=33333333-3333-3333-3333-333333333333
+
+## Design
+
+![alt text](/docs/validate-jwt.png)
+
+## Metrics
+
+Metrics are exposed on :80/metrics
+
+| Metric Name | Description |
+|---|---|
+| ingress_nginx_validate_jwt_authorized | Number of Authorized operations ongoing |
+| ingress_nginx_validate_jwt_unauthorized | Number of Unauthorized operations ongoing |
+| ingress_nginx_validate_jwt_duration_seconds | Histogram of JWT validation durations |
diff --git a/charts/ingress-nginx-validate-jwt/.helmignore b/charts/ingress-nginx-validate-jwt/.helmignore
new file mode 100644
index 0000000..0e8a0eb
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/.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/charts/ingress-nginx-validate-jwt/Chart.yaml b/charts/ingress-nginx-validate-jwt/Chart.yaml
new file mode 100644
index 0000000..a31cd35
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: ingress-nginx-validate-jwt
+description: Enables ingress-nginx to validate JWT tokens
+
+# 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.0.1
+
+# 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: "0.0.1"
diff --git a/charts/ingress-nginx-validate-jwt/templates/NOTES.txt b/charts/ingress-nginx-validate-jwt/templates/NOTES.txt
new file mode 100644
index 0000000..e9403f9
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/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 "ingress-nginx-validate-jwt.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 "ingress-nginx-validate-jwt.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "ingress-nginx-validate-jwt.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 "ingress-nginx-validate-jwt.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/charts/ingress-nginx-validate-jwt/templates/_helpers.tpl b/charts/ingress-nginx-validate-jwt/templates/_helpers.tpl
new file mode 100644
index 0000000..4c9aee1
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "ingress-nginx-validate-jwt.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 "ingress-nginx-validate-jwt.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 "ingress-nginx-validate-jwt.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "ingress-nginx-validate-jwt.labels" -}}
+helm.sh/chart: {{ include "ingress-nginx-validate-jwt.chart" . }}
+{{ include "ingress-nginx-validate-jwt.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "ingress-nginx-validate-jwt.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "ingress-nginx-validate-jwt.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "ingress-nginx-validate-jwt.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "ingress-nginx-validate-jwt.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/charts/ingress-nginx-validate-jwt/templates/deployment.yaml b/charts/ingress-nginx-validate-jwt/templates/deployment.yaml
new file mode 100644
index 0000000..e894570
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/templates/deployment.yaml
@@ -0,0 +1,66 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "ingress-nginx-validate-jwt.fullname" . }}
+ labels:
+ {{- include "ingress-nginx-validate-jwt.labels" . | nindent 4 }}
+spec:
+ {{- if not .Values.autoscaling.enabled }}
+ replicas: {{ .Values.replicaCount }}
+ {{- end }}
+ selector:
+ matchLabels:
+ {{- include "ingress-nginx-validate-jwt.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ {{- with .Values.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "ingress-nginx-validate-jwt.selectorLabels" . | nindent 8 }}
+ spec:
+ {{- with .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "ingress-nginx-validate-jwt.serviceAccountName" . }}
+ securityContext:
+ {{- toYaml .Values.podSecurityContext | nindent 8 }}
+ containers:
+ - name: {{ .Chart.Name }}
+ env:
+ - name: "OpenIdProviderConfigurationUrl"
+ value: "{{ .Values.openIdProviderConfigurationUrl }}"
+ - name: "Logging__LogLevel__Default"
+ value: "{{ .Values.logLevel }}"
+ securityContext:
+ {{- toYaml .Values.securityContext | nindent 12 }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - name: http
+ containerPort: 80
+ protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /health
+ port: http
+ readinessProbe:
+ httpGet:
+ path: /health
+ 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/charts/ingress-nginx-validate-jwt/templates/hpa.yaml b/charts/ingress-nginx-validate-jwt/templates/hpa.yaml
new file mode 100644
index 0000000..c048f2c
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/templates/hpa.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2beta1
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "ingress-nginx-validate-jwt.fullname" . }}
+ labels:
+ {{- include "ingress-nginx-validate-jwt.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ include "ingress-nginx-validate-jwt.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/charts/ingress-nginx-validate-jwt/templates/ingress.yaml b/charts/ingress-nginx-validate-jwt/templates/ingress.yaml
new file mode 100644
index 0000000..4df034b
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/templates/ingress.yaml
@@ -0,0 +1,61 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "ingress-nginx-validate-jwt.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
+ {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
+ {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
+ {{- end }}
+{{- end }}
+{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1
+{{- else 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 "ingress-nginx-validate-jwt.labels" . | nindent 4 }}
+ {{- with .Values.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
+ ingressClassName: {{ .Values.ingress.className }}
+ {{- end }}
+ {{- 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 }}
+ {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
+ pathType: {{ .pathType }}
+ {{- end }}
+ backend:
+ {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
+ service:
+ name: {{ $fullName }}
+ port:
+ number: {{ $svcPort }}
+ {{- else }}
+ serviceName: {{ $fullName }}
+ servicePort: {{ $svcPort }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+{{- end }}
diff --git a/charts/ingress-nginx-validate-jwt/templates/service.yaml b/charts/ingress-nginx-validate-jwt/templates/service.yaml
new file mode 100644
index 0000000..71c824c
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "ingress-nginx-validate-jwt.fullname" . }}
+ labels:
+ {{- include "ingress-nginx-validate-jwt.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "ingress-nginx-validate-jwt.selectorLabels" . | nindent 4 }}
diff --git a/charts/ingress-nginx-validate-jwt/templates/serviceaccount.yaml b/charts/ingress-nginx-validate-jwt/templates/serviceaccount.yaml
new file mode 100644
index 0000000..43b19f8
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "ingress-nginx-validate-jwt.serviceAccountName" . }}
+ labels:
+ {{- include "ingress-nginx-validate-jwt.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/charts/ingress-nginx-validate-jwt/templates/tests/test-connection.yaml b/charts/ingress-nginx-validate-jwt/templates/tests/test-connection.yaml
new file mode 100644
index 0000000..54510ab
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ include "ingress-nginx-validate-jwt.fullname" . }}-test-connection"
+ labels:
+ {{- include "ingress-nginx-validate-jwt.labels" . | nindent 4 }}
+ annotations:
+ "helm.sh/hook": test
+spec:
+ containers:
+ - name: wget
+ image: busybox
+ command: ['wget']
+ args: ['{{ include "ingress-nginx-validate-jwt.fullname" . }}:{{ .Values.service.port }}']
+ restartPolicy: Never
diff --git a/charts/ingress-nginx-validate-jwt/values.yaml b/charts/ingress-nginx-validate-jwt/values.yaml
new file mode 100644
index 0000000..1a0d610
--- /dev/null
+++ b/charts/ingress-nginx-validate-jwt/values.yaml
@@ -0,0 +1,91 @@
+# Default values for ingress-nginx-validate-jwt.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+
+image:
+ repository: ghcr.io/ivanjosipovic/ingress-nginx-validate-jwt/ingress-nginx-validate-jwt
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose default is the chart appVersion.
+ tag: ""
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+# OpenID Provider Configuration Url for your Identity Provider
+openIdProviderConfigurationUrl: "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"
+
+# Log Level
+logLevel: Information
+
+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: ""
+
+podAnnotations:
+ prometheus.io/scrape: 'true'
+ prometheus.io/port: '80'
+ prometheus.io/path: '/metrics'
+
+podSecurityContext: {}
+ # fsGroup: 2000
+
+securityContext: {}
+ # capabilities:
+ # drop:
+ # - ALL
+ # readOnlyRootFilesystem: true
+ # runAsNonRoot: true
+ # runAsUser: 1000
+
+service:
+ type: ClusterIP
+ port: 80
+
+ingress:
+ enabled: false
+ className: ""
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ hosts:
+ - host: chart-example.local
+ paths:
+ - path: /
+ pathType: ImplementationSpecific
+ 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: {}
diff --git a/docs/validate-jwt.drawio b/docs/validate-jwt.drawio
new file mode 100644
index 0000000..6ccb091
--- /dev/null
+++ b/docs/validate-jwt.drawio
@@ -0,0 +1 @@
+7Vpdd5s2GP41vrQPCOPgy9hJ1qztmjU563q1I4OM1ciIChE7+fV7JYT5EM681g45PfFFgh4JSTzvt2Dgzdfb3wROVx95RNgAOdF24F0MEJoGLvxVwGMB+FMDxIJGBVQDbukTMaBj0JxGJGsMlJwzSdMmGPIkIaFsYFgIvmkOW3LWXDXFMbGA2xAzG/1CI7kq0MB3KvwdofGqXNl1TM8al4MNkK1wxDc1yLsceHPBuSyu1ts5YYq7kpfivqs9vbuNCZLIQ27wrmLx9D74+lcgFnfe54/ZBv85NLM8YJabBzablY8lA4LnSUTUJM7Am21WVJLbFIeqdwMiB2wl1wxaLlya6YiQZLt3n+7u6UFrCF8TKR5hiLlhbPgyCoM8095U9HsTg61q1KMSxEbk8W7qihW4MMT8D5KQRdL7fEFEQiRoZpsunKWFHi7pVrFWZyflNJF6d/5s4F8AghmNEwBCIIcIAOhaK+RsyRNprMFFFX5B1zE8A6ML+IufckHUs4HyS0wTImA7V9Xe/rkl4oGGJBtlD/FxhOO3hDO2hTP2R55ni2d8MulMLBmQCGzYNLmQKx7zBLPLCp01lboa84Hz1AjrG5Hy0YgA55I3RankM+eMC72i5+gf4JnEQp4rx6OkynCW0bCErygrb8+k4PekNkGAFt5koiaGQTU88kkQjXfCU0/2vOiACJ6LkDzDmJEObCkm8plxk25VEIRhSR+a+zi+WM9+BbFenk9mWqw9iM8N+pSfZznNJKbJdkiTWJDM9pvZPZHhykiuJpEIZ6tm9Mma0adlMCg48y9RlyyW+me54Zkzchx/gObOaOKpf8ozo7nucDWMWui0E9VTtEdO90x8pu+G/o5J3BaGgtZYHTkgnUgVC+ttrBKv0f3O7Y8oxAP1mOJaX1ykXMUh5fcpJDbnJuRIZRIzhheE3fCMSsobgagc/qE1YMGl5Ouu0KWnmuHwPtZmWDIfkSXOmTxO/LGyA2QHIL8jORgHp3JT/utyU/v8z4EO7Ihuanygm/L69FJjy0udhxLIs7yTsbZ8zYoBBxhIt8HVpMhzySBrm+9KF+cQKyoGcBERUXYmPCFHsq9WgteVfHeY18lS74klH7ikEZZk+G0j34LILxdEThAjvGnPMWJqKTFMM9rAncP7hG8SaPGUJDQaAtVLGucCa556CCxAsXj8W92v1c4AXzUA+mfaF1uzQtF6rLduiKBAmxKmBo8YUYKfq1uMegzBIv0zr6EiwzKNODjqmOlvlBeoDeHLZUakpTG7Xfy4EgWWEl1HwCCVsJ2JDgwLCFuTWF3dCP5AIT7YGsQYTTNSs/yQ8Tx6kXMd3/WbZhnYZumiDrsMThVcXPtgx32GstNTtIuthiLXtynqYgidjCG7irMPCPtkyCtPf3pjyM4gvX4ZCl4bQ3YON+6VoZ2TKWvIoG+GbN/++5c7AMCPS6gMIKAj5/zm2mLtLd19S3dBnc9GHppWv6Bp/31nv8iOIR1nJi9p/2evzP6RHUM63mn0GEN6z0OQ7SGdXhny3P+uMV+WIbvIBOd/DsgnKCSdW3XOl6fKPSKHcawiyieoOeHfNYx1WoWnsxTgr6APSgxdYThVSVEVGzXyJ99z9R5bH0wOM11dqrVdP91qwsv+skJxi35z2Oas8b16leq8u7u7gX+fyfecZFLTCWwipwiHdxCbkiNtABX9f6h3EupBy7cSzpKLDRZR1l5VdRT8quMnzdJRNuIV/c1jrV1TbUOaDeBESS1kmK6zIy0+3seCfqMcSr38iuy4sBKSo+zCL/r3LAE1eZbyBAz7OKtNms+8ex8F1p9lmvBqQeC+0tKWuwHnIZt+pZk9mUPZeqploDILYGQp9yYZXT6sebzzEyfF+94f1L868I/kKVsnyl1fDHR9zuH+wAcD0Ky+pykOXqqPkrzLfwE=
\ No newline at end of file
diff --git a/docs/validate-jwt.png b/docs/validate-jwt.png
new file mode 100644
index 0000000000000000000000000000000000000000..52f3ac06f7cbb79db24541b2dc25254c6b54b313
GIT binary patch
literal 45019
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
XV43({;ou#&1MsJWRhKWmiVOJ claims)
+ {
+ return "Bearer " + s_tokenHandler.WriteToken(new JwtSecurityToken(Issuer, null, claims, null, DateTime.UtcNow.AddMinutes(20), SigningCredentials));
+ }
+ }
+
+ public static IEnumerable