From 6f52cb2ada6fe7b6a8900662fd05f0f13d5aa61d Mon Sep 17 00:00:00 2001 From: Dmytro Bondar Date: Thu, 4 Jul 2024 22:37:30 +0200 Subject: [PATCH] Init Helm chart (#255) * Initial chart version * Add CI/CD for chart * Fix admin creds template * Add command, args, env, envFrom * Render volumes and volumeMounts with tpl * Change persistance accessMode type * Add update strategy config * Use custom types in docs * Add startup probe config * Fix web.external_url config --- .github/workflows/chart.yml | 84 ++++++ ct.yaml | 5 + deploy/helm/.helmignore | 23 ++ deploy/helm/Chart.yaml | 25 ++ deploy/helm/README.md | 116 ++++++++ deploy/helm/README.md.gotmpl | 27 ++ deploy/helm/templates/NOTES.txt | 24 ++ deploy/helm/templates/_helpers.tpl | 100 +++++++ deploy/helm/templates/_pod.tpl | 111 ++++++++ deploy/helm/templates/deployment.yaml | 17 ++ deploy/helm/templates/extras.yaml | 4 + deploy/helm/templates/ingress.yaml | 47 ++++ deploy/helm/templates/pvc.yaml | 11 + deploy/helm/templates/secret.yaml | 41 +++ deploy/helm/templates/service-web.yaml | 16 ++ deploy/helm/templates/service-wireguard.yaml | 27 ++ deploy/helm/templates/serviceaccount.yaml | 10 + deploy/helm/templates/statefulset.yaml | 24 ++ deploy/helm/values.yaml | 269 +++++++++++++++++++ 19 files changed, 981 insertions(+) create mode 100644 .github/workflows/chart.yml create mode 100644 ct.yaml create mode 100644 deploy/helm/.helmignore create mode 100644 deploy/helm/Chart.yaml create mode 100644 deploy/helm/README.md create mode 100644 deploy/helm/README.md.gotmpl create mode 100644 deploy/helm/templates/NOTES.txt create mode 100644 deploy/helm/templates/_helpers.tpl create mode 100644 deploy/helm/templates/_pod.tpl create mode 100644 deploy/helm/templates/deployment.yaml create mode 100644 deploy/helm/templates/extras.yaml create mode 100644 deploy/helm/templates/ingress.yaml create mode 100644 deploy/helm/templates/pvc.yaml create mode 100644 deploy/helm/templates/secret.yaml create mode 100644 deploy/helm/templates/service-web.yaml create mode 100644 deploy/helm/templates/service-wireguard.yaml create mode 100644 deploy/helm/templates/serviceaccount.yaml create mode 100644 deploy/helm/templates/statefulset.yaml create mode 100644 deploy/helm/values.yaml diff --git a/.github/workflows/chart.yml b/.github/workflows/chart.yml new file mode 100644 index 0000000..9086387 --- /dev/null +++ b/.github/workflows/chart.yml @@ -0,0 +1,84 @@ +# Publish chart to the GitHub Container Registry (GHCR) on push to master +# Run the following tests on PRs: +# - Check if chart's documentation is up to date +# - Check chart linting +# - Check chart installation in a Kind cluster +# - Check chart packaging + +name: Chart + +on: + pull_request: + branches: [master] + paths: [deploy/helm] + push: + branches: [master] + paths: [deploy/helm] + +jobs: + lint-test: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # ct lint requires Python 3.x to run following packages: + # - yamale (https://github.com/23andMe/Yamale) + # - yamllint (https://github.com/adrienverge/yamllint) + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - uses: helm/chart-testing-action@v2 + + - name: Run chart-testing (lint) + run: ct lint --config ct.yaml + + - name: Check docs + run: | + docker run --rm --volume "${PWD}/deploy:/helm-docs" -u "$(id -u)" jnorwood/helm-docs + if ! git diff --exit-code; then + echo "error::Documentation is not up to date. Please run helm-docs and commit changes." + exit 1 + fi + + - uses: helm/kind-action@v1 + + - name: Run chart-testing (install) + run: ct install --config ct.yaml + + - name: Run helm package charts + run: | + for chart in $(ct list-changed --config ct.yaml); do + helm package $chart -d out + done + + publish: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' }} + permissions: + packages: write + steps: + - uses: actions/checkout@v4 + + - uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: helm/chart-testing-action@v2 + - name: Run helm package charts + run: | + for chart in $(ct list-changed --config ct.yaml); do + helm package $chart -d out + done + + - name: Push chart to GHCR + working-directory: out + run: | + for pkg in $(ls *.tgz); do + helm push $pkg oci://ghcr.io/${{ github.repository_owner }}/charts + done diff --git a/ct.yaml b/ct.yaml new file mode 100644 index 0000000..106bad7 --- /dev/null +++ b/ct.yaml @@ -0,0 +1,5 @@ +# See https://github.com/helm/chart-testing#configuration +remote: origin +chart-dirs: deploy +target-branch: master +validate-maintainers: false diff --git a/deploy/helm/.helmignore b/deploy/helm/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/deploy/helm/.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/deploy/helm/Chart.yaml b/deploy/helm/Chart.yaml new file mode 100644 index 0000000..e17febb --- /dev/null +++ b/deploy/helm/Chart.yaml @@ -0,0 +1,25 @@ +apiVersion: v2 +name: wg-portal +description: WireGuard Configuration Portal with LDAP, OAuth, OIDC authentication +# Version is set to ensure compatibility with the chart's Ingress resource. +kubeVersion: '>=1.19.0' +type: application +home: https://wgportal.org +icon: https://wgportal.org/assets/images/logo.svg +sources: + - https://github.com/h44z/wg-portal + +annotations: + artifacthub.io/category: networking + artifacthub.io/changes: "" + +# 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: v2.0.0-alpha.2 diff --git a/deploy/helm/README.md b/deploy/helm/README.md new file mode 100644 index 0000000..59869ef --- /dev/null +++ b/deploy/helm/README.md @@ -0,0 +1,116 @@ +# wg-portal + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v2.0.0-alpha.2](https://img.shields.io/badge/AppVersion-v2.0.0--alpha.2-informational?style=flat-square) + +WireGuard Configuration Portal with LDAP, OAuth, OIDC authentication + +**Homepage:** + +## Source Code + +* + +## Requirements + +Kubernetes: `>=1.19.0` + +## Installing the Chart + +To install the chart with the release name `wg-portal`: + +```console +helm install wg-portal oci://ghcr.io/h44z/charts/wg-portal +``` + +This command deploy wg-portal on the Kubernetes cluster in the default configuration. +The [Values](#values) section lists the parameters that can be configured during installation. + +## Values + +### Parameters + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Affinity configuration | +| args | list | `[]` | Additional pod arguments | +| command | list | `[]` | Overwrite pod command | +| dnsPolicy | string | `"ClusterFirst"` | Set DNS policy for the pod. Valid values are `ClusterFirstWithHostNet`, `ClusterFirst`, `Default` or `None`. | +| env | tpl/list | `[]` | Additional environment variables | +| envFrom | tpl/list | `[]` | Additional environment variables from a secret or configMap | +| hostNetwork | string | `false`. | Use the host's network namespace. | +| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy | +| image.repository | string | `"ghcr.io/h44z/wg-portal"` | Image repository | +| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | +| imagePullSecrets | list | `[]` | Image pull secrets | +| initContainers | tpl/list | `[]` | Pod init containers | +| nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node Selector configuration | +| podAnnotations | tpl/object | `{}` | Extra annotations to add to the pod | +| podLabels | object | `{}` | Extra labels to add to the pod | +| podSecurityContext | object | `{}` | Pod Security Context | +| resources | object | `{}` | Resources requests and limits | +| restartPolicy | string | `"Always"` | Restart policy for all containers within the pod. Valid values are `Always`, `OnFailure` or `Never`. | +| revisionHistoryLimit | string | `10` | The number of old ReplicaSets to retain to allow rollback. | +| securityContext.capabilities.add | list | `["NET_ADMIN"]` | Add capabilities to the container | +| sidecarContainers | tpl/list | `[]` | Pod sidecar containers | +| strategy | object | `{"type":"RollingUpdate"}` | Update strategy for the workload Valid values are: `RollingUpdate` or `Recreate` for Deployment, `RollingUpdate` or `OnDelete` for StatefulSet | +| tolerations | list | `[]` | Tolerations configuration | +| volumeMounts | tpl/list | `[]` | Additional volumeMounts | +| volumes | tpl/list | `[]` | Additional volumes | +| workloadType | string | `"Deployment"` | Workload type - `Deployment` or `StatefulSet` | + +### Configuration + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| config.advanced | tpl/object | `{}` | Advanced configuration options. | +| config.auth | tpl/object | `{}` | Auth configuration options. | +| config.core | tpl/object | `{}` | Core configuration options.
If external admins in `auth` are not defined and there are no `admin_user` and `admin_password` defined here, the default credentials will be generated. | +| config.database | tpl/object | `{}` | Database configuration options | +| config.mail | tpl/object | `{}` | Mail configuration options | +| config.statistics | tpl/object | `{}` | Statistics configuration options | +| config.web | tpl/object | `{}` | Web configuration options.
The chart will set `listening_address` automatically from `service.web.port`, and `external_url` from `ingress.host` if enabled. | + +### Common + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| extraDeploy | list | `[]` | Array of extra objects to deploy with the release | +| fullnameOverride | string | `""` | Fully override resource names | +| nameOverride | string | `""` | Partially override resource names (adds suffix) | + +### Traffic exposure + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| ingress.annotations | object | `{}` | Ingress annotations | +| ingress.className | string | `""` | Ingress class name | +| ingress.enabled | bool | `false` | Specifies whether an ingress resource should be created | +| ingress.host | string | `""` | Ingress host FQDN | +| ingress.path | string | `"/"` | Ingress path | +| ingress.pathType | string | `"ImplementationSpecific"` | Ingress path type | +| ingress.tls | list | `[]` | Ingress TLS configuration | +| service.web.annotations | object | `{}` | Annotations for the web service | +| service.web.port | int | `8888` | Web service port Used for the web interface listener | +| service.web.type | string | `"ClusterIP"` | Web service type | +| service.wireguard.annotations | object | `{}` | Annotations for the WireGuard service | +| service.wireguard.ports | list | `[51820]` | Wireguard service ports. Exposes the WireGuard ports for created interfaces. Lowerest port is selected as start port for the first interface. Increment next port by 1 for each additional interface. | +| service.wireguard.type | string | `"LoadBalancer"` | Wireguard service type | + +### Persistence + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| persistence.accessMode | string | `"ReadWriteOnce"` | Persistent Volume Access Mode | +| persistence.annotations | object | `{}` | Persistent Volume Claim annotations | +| persistence.enabled | bool | `false` | Specifies whether an persistent volume should be created | +| persistence.size | string | `"1Gi"` | Persistent Volume size | +| persistence.storageClass | string | `""` | Persistent Volume storage class. If undefined (the default) cluster's default provisioner will be used. | + +### RBAC + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| serviceAccount.annotations | object | `{}` | Service account annotations | +| serviceAccount.automount | bool | `false` | Automatically mount a ServiceAccount's API credentials | +| serviceAccount.create | bool | `true` | Specifies whether a service account should be created | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | diff --git a/deploy/helm/README.md.gotmpl b/deploy/helm/README.md.gotmpl new file mode 100644 index 0000000..5e2cd9e --- /dev/null +++ b/deploy/helm/README.md.gotmpl @@ -0,0 +1,27 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +## Installing the Chart + +To install the chart with the release name `wg-portal`: + +```console +helm install wg-portal oci://ghcr.io/h44z/charts/wg-portal +``` + +This command deploy wg-portal on the Kubernetes cluster in the default configuration. +The [Values](#values) section lists the parameters that can be configured during installation. + +{{ template "chart.valuesSection" . }} diff --git a/deploy/helm/templates/NOTES.txt b/deploy/helm/templates/NOTES.txt new file mode 100644 index 0000000..03bdd1f --- /dev/null +++ b/deploy/helm/templates/NOTES.txt @@ -0,0 +1,24 @@ +{{- $serviceName := printf "%s-web" (include "wg-portal.fullname" .) -}} +{{- $servicePort := .Values.service.web.port }} + +{{- if not .Values.ingress.enabled }} +Get the application URL by running these commands: +{{- if eq "ClusterIP" .Values.service.web.type }} + kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ $serviceName }} {{ $servicePort }}:{{ $servicePort }} + +Visit http://127.0.0.1:{{ $servicePort }} to use your application + +{{- else if eq "LoadBalancer" .Values.service.web.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 {{ $serviceName }}' + export SERVICE_IP=$(kubectl get --namespace {{ .Release.Namespace }} svc {{ $serviceName }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ $servicePort }} + +{{- else if eq "NodePort" .Values.service.web.type }} + export NODE_IP=$(kubectl get --namespace {{ .Release.Namespace }} nodes -o jsonpath="{.items[0].status.addresses[0].address}") + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} svc {{ $serviceName }} -o jsonpath="{.spec.ports[0].nodePort}" ) + echo http://$NODE_IP:$NODE_PORT +{{- end }} +{{- else }} +Visit http{{ if .Values.ingress.tls }}s{{ end }}://{{ .Values.ingress.host }}{{ .Values.ingress.path }} to use your application +{{- end }} diff --git a/deploy/helm/templates/_helpers.tpl b/deploy/helm/templates/_helpers.tpl new file mode 100644 index 0000000..cfeb4cf --- /dev/null +++ b/deploy/helm/templates/_helpers.tpl @@ -0,0 +1,100 @@ +{{/* +Expand the name of the chart +*/}} +{{- define "wg-portal.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 "wg-portal.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 "wg-portal.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "wg-portal.labels" -}} +helm.sh/chart: {{ include "wg-portal.chart" . }} +{{ include "wg-portal.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "wg-portal.selectorLabels" -}} +app.kubernetes.io/name: {{ include "wg-portal.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "wg-portal.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "wg-portal.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Define default admin credentials +If external auth is enabled and has admin group mappings, +the admin_user and admin_password values are not used. +*/}} +{{- define "wg-portal.admin" -}} +{{- $externalAdmin := false -}} +{{- with .Values.config.auth -}} + {{- range (default list .ldap) -}} + {{- if hasKey . "admin_group" -}} + {{- $externalAdmin = true -}} + {{- end -}} + {{- end }} + {{- range (concat (default list .oidc) (default list .oauth)) -}} + {{- if hasKey .field_map "is_admin" -}} + {{- $externalAdmin = true -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- if not $externalAdmin -}} +admin_user: admin@wgportal.local +admin_password: {{ printf "%s/%s" .Release.Name .Release.Namespace | b64enc }} +{{- end -}} +{{- end -}} + +{{/* +Define PersistentVolumeClaim spec +*/}} +{{- define "wg-portal.pvc" -}} +accessModes: [{{ .Values.persistence.accessMode }}] +{{- with .Values.persistence.storageClass }} +storageClassName: {{ . }} +{{- end }} +resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- end -}} diff --git a/deploy/helm/templates/_pod.tpl b/deploy/helm/templates/_pod.tpl new file mode 100644 index 0000000..da52f3d --- /dev/null +++ b/deploy/helm/templates/_pod.tpl @@ -0,0 +1,111 @@ +{{- define "wg-portal.podTemplate" -}} +metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + kubectl.kubernetes.io/default-container: {{ .Chart.Name }} + {{- with .Values.podAnnotations }} + {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} + labels: + {{- include "wg-portal.labels" . | nindent 4 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.affinity }} + affinity: {{- toYaml . | nindent 4 }} + {{- end }} + automountServiceAccountToken: {{ .Values.serviceAccount.automount }} + containers: + {{- with .Values.sidecarContainers }} + {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag}}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.command }} + command: {{ . }} + {{- end }} + {{- with .Values.args }} + args: {{ . }} + {{- end }} + {{- with .Values.env }} + env: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + {{- with .Values.envFrom }} + envFrom: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.service.web.port }} + protocol: TCP + {{- range $index, $port := .Values.service.wireguard.ports }} + - name: wg{{ $index }} + containerPort: {{ $port }} + protocol: UDP + {{- end }} + {{- with .Values.livenessProbe }} + livenessProbe: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.startupProbe }} + startupProbe: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.securityContext }} + securityContext: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.resources}} + resources: {{- toYaml . | nindent 8 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /app/config + readOnly: true + - name: data + mountPath: /app/data + {{- with .Values.volumeMounts }} + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + {{- with .Values.dnsPolicy }} + dnsPolicy: {{ . }} + {{- end }} + {{- with .Values.hostNetwork }} + hostNetwork: {{ . }} + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.initContainers }} + initContainers: {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.restartPolicy }} + restartPolicy: {{ . }} + {{- end }} + serviceAccountName: {{ include "wg-portal.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: {{- toYaml . | nindent 4 }} + {{- end }} + volumes: + - name: config + secret: + secretName: {{ include "wg-portal.fullname" . }} + {{- if not .Values.persistence.enabled }} + - name: data + emptyDir: {} + {{- else if eq .Values.workloadType "Deployment" }} + - name: data + persistentVolumeClaim: + claimName: {{ include "wg-portal.fullname" . }} + {{- end }} + {{- with .Values.volumes }} + {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} +{{- end -}} diff --git a/deploy/helm/templates/deployment.yaml b/deploy/helm/templates/deployment.yaml new file mode 100644 index 0000000..8d2db04 --- /dev/null +++ b/deploy/helm/templates/deployment.yaml @@ -0,0 +1,17 @@ +{{- if eq .Values.workloadType "Deployment" -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "wg-portal.fullname" . }} + labels: {{- include "wg-portal.labels" . | nindent 4 }} +spec: + {{- with .Values.revisionHistoryLimit }} + revisionHistoryLimit: {{ . }} + {{- end }} + {{- with .Values.strategy }} + strategy: {{- toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: {{- include "wg-portal.selectorLabels" . | nindent 6 }} + template: {{- include "wg-portal.podTemplate" . | nindent 4 }} +{{- end -}} diff --git a/deploy/helm/templates/extras.yaml b/deploy/helm/templates/extras.yaml new file mode 100644 index 0000000..e8220c4 --- /dev/null +++ b/deploy/helm/templates/extras.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy -}} +{{- tpl (toYaml .) $ }} +--- +{{- end -}} diff --git a/deploy/helm/templates/ingress.yaml b/deploy/helm/templates/ingress.yaml new file mode 100644 index 0000000..7413c78 --- /dev/null +++ b/deploy/helm/templates/ingress.yaml @@ -0,0 +1,47 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + {{- with .Values.ingress.annotations }} + annotations: {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "wg-portal.fullname" . }} + labels: {{- include "wg-portal.labels" . | nindent 4 }} +spec: + ingressClassName: {{ .Values.ingress.className }} + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: / + pathType: {{ default "ImplementationSpecific" .pathType }} + backend: + service: + name: {{ include "wg-portal.fullname" . }}-web + port: + name: http + {{- range .Values.ingress.extraHosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ default "ImplementationSpecific" .pathType }} + backend: + service: + name: {{ include "wg-portal.fullname" . }}-web + port: + name: http + {{- end }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} +{{- end }} diff --git a/deploy/helm/templates/pvc.yaml b/deploy/helm/templates/pvc.yaml new file mode 100644 index 0000000..aa9d955 --- /dev/null +++ b/deploy/helm/templates/pvc.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.persistence.enabled (eq .Values.workloadType "Deployment") -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + {{- with .Values.persistence.annotations }} + annotations: {{- toYaml . | nindent 4}} + {{- end }} + name: {{ include "wg-portal.fullname" . }} + labels: {{- include "wg-portal.labels" . | nindent 4 }} +spec: {{- include "wg-portal.pvc" . | nindent 2 }} +{{- end -}} diff --git a/deploy/helm/templates/secret.yaml b/deploy/helm/templates/secret.yaml new file mode 100644 index 0000000..d8936f5 --- /dev/null +++ b/deploy/helm/templates/secret.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "wg-portal.fullname" . }} + labels: {{- include "wg-portal.labels" . | nindent 4 }} +stringData: + config.yml: | + advanced: + start_listen_port: {{ .Values.service.wireguard.ports | sortAlpha | first }} + {{- with .Values.config.advanced }} + {{- tpl (toYaml (omit . "start_listen_port")) $ | nindent 6 }} + {{- end }} + + {{- with .Values.config.auth }} + auth: {{- tpl (toYaml .) $ | nindent 6 }} + {{- end }} + + {{- with mustMerge .Values.config.core (include "wg-portal.admin" . | fromYaml) }} + core: {{- tpl (toYaml .) $ | nindent 6 }} + {{- end }} + + {{- with .Values.config.database }} + database: {{- tpl (toYaml .) $ | nindent 6 }} + {{- end }} + + {{- with .Values.config.mail }} + mail: {{- tpl (toYaml .) $ | nindent 6 }} + {{- end }} + + {{- with .Values.config.statistics }} + statistics: {{- tpl (toYaml .) $ | nindent 6 }} + {{- end }} + web: + listening_address: :{{ .Values.service.web.port }} + {{- if and .Values.ingress.enabled (not (hasKey .Values.config.web "external_url")) }} + {{- $proto := ternary "http" "https" (empty .Values.ingress.tls) }} + external_url: {{ trimSuffix "/" (printf "%s://%s%s" $proto .Values.ingress.host .Values.ingress.path) }} + {{- end }} + {{- with .Values.config.web }} + {{- tpl (toYaml (omit . "listening_address")) $ | nindent 6 }} + {{- end }} diff --git a/deploy/helm/templates/service-web.yaml b/deploy/helm/templates/service-web.yaml new file mode 100644 index 0000000..531ba70 --- /dev/null +++ b/deploy/helm/templates/service-web.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + {{- with .Values.service.web.annotations }} + annotations: {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "wg-portal.fullname" . }}-web + labels: {{- include "wg-portal.labels" . | nindent 4 }} +spec: + ports: + - port: {{ .Values.service.web.port }} + targetPort: http + protocol: TCP + name: http + selector: {{- include "wg-portal.selectorLabels" . | nindent 4 }} + type: {{ .Values.service.web.type }} diff --git a/deploy/helm/templates/service-wireguard.yaml b/deploy/helm/templates/service-wireguard.yaml new file mode 100644 index 0000000..3a01cf9 --- /dev/null +++ b/deploy/helm/templates/service-wireguard.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: Service +metadata: + {{- with .Values.service.wireguard.annotations }} + annotations: {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "wg-portal.fullname" . }}-wireguard + labels: {{- include "wg-portal.labels" . | nindent 4 }} +spec: + {{- with .Values.service.wireguard.externalTrafficPolicy }} + externalTrafficPolicy: {{ . }} + {{- end }} + {{- with .Values.service.wireguard.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- toYaml . | nindent 4 }} + {{- end }} + ports: + {{- range $index, $port := .Values.service.wireguard.ports }} + - port: {{ $port }} + targetPort: wg{{ $index }} + protocol: UDP + name: wg{{ $index }} + {{- end }} + selector: {{- include "wg-portal.selectorLabels" . | nindent 4 }} + {{- with .Values.service.wireguard.sessionAffinity }} + sessionAffinity: {{ . }} + {{- end }} + type: {{ .Values.service.wireguard.type }} diff --git a/deploy/helm/templates/serviceaccount.yaml b/deploy/helm/templates/serviceaccount.yaml new file mode 100644 index 0000000..f9576b5 --- /dev/null +++ b/deploy/helm/templates/serviceaccount.yaml @@ -0,0 +1,10 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "wg-portal.serviceAccountName" . }} + labels: {{- include "wg-portal.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/templates/statefulset.yaml b/deploy/helm/templates/statefulset.yaml new file mode 100644 index 0000000..e25870e --- /dev/null +++ b/deploy/helm/templates/statefulset.yaml @@ -0,0 +1,24 @@ +{{- if eq .Values.workloadType "StatefulSet" -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "wg-portal.fullname" . }} + labels: {{- include "wg-portal.labels" . | nindent 4 }} +spec: + {{- with .Values.revisionHistoryLimit }} + revisionHistoryLimit: {{ . }} + {{- end }} + {{- with .Values.strategy }} + updateStrategy: {{- toYaml . | nindent 4 }} + {{- end }} + serviceName: {{ template "wg-portal.fullname" . }}-web + selector: + matchLabels: {{- include "wg-portal.selectorLabels" . | nindent 6 }} + template: {{- include "wg-portal.podTemplate" . | nindent 4 }} + {{- if .Values.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + spec: {{- include "wg-portal.pvc" . | nindent 8 }} + {{- end -}} +{{- end -}} diff --git a/deploy/helm/values.yaml b/deploy/helm/values.yaml new file mode 100644 index 0000000..142c990 --- /dev/null +++ b/deploy/helm/values.yaml @@ -0,0 +1,269 @@ +# Default values for wg-portal. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# -- Partially override resource names (adds suffix) +# @section -- Common +nameOverride: '' +# -- Fully override resource names +# @section -- Common +fullnameOverride: '' +# -- Array of extra objects to deploy with the release +# @section -- Common +extraDeploy: [] + +# https://github.com/h44z/wg-portal/blob/master/README.md#configuration-options +config: + # -- (tpl/object) Advanced configuration options. + # @section -- Configuration + advanced: {} + # -- (tpl/object) Auth configuration options. + # @section -- Configuration + auth: {} + # -- (tpl/object) Core configuration options.
+ # @section -- Configuration + # If external admins in `auth` are not defined and + # there are no `admin_user` and `admin_password` defined here, + # the default credentials will be generated. + core: {} + # -- (tpl/object) Database configuration options + # @section -- Configuration + database: {} + # -- (tpl/object) Mail configuration options + # @section -- Configuration + mail: {} + # -- (tpl/object) Statistics configuration options + # @section -- Configuration + statistics: {} + # -- (tpl/object) Web configuration options.
+ # @section -- Configuration + # The chart will set `listening_address` automatically from `service.web.port`, + # and `external_url` from `ingress.host` if enabled. + web: {} + +# -- The number of old ReplicaSets to retain to allow rollback. +# @section -- Parameters +# @default -- `10` +revisionHistoryLimit: '' +# -- Workload type - `Deployment` or `StatefulSet` +# @section -- Parameters +workloadType: Deployment +# -- Update strategy for the workload +# Valid values are: +# `RollingUpdate` or `Recreate` for Deployment, +# `RollingUpdate` or `OnDelete` for StatefulSet +# @section -- Parameters +strategy: + type: RollingUpdate + +image: + # -- Image repository + # @section -- Parameters + repository: ghcr.io/h44z/wg-portal + # -- Image pull policy + # @section -- Parameters + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + # @section -- Parameters + tag: '' + +# -- Image pull secrets +# @section -- Parameters +imagePullSecrets: [] + +# -- (tpl/object) Extra annotations to add to the pod +# @section -- Parameters +podAnnotations: {} + +# -- Extra labels to add to the pod +# @section -- Parameters +podLabels: {} + +# -- Pod Security Context +# @section -- Parameters +podSecurityContext: {} + +# Container Security Context +securityContext: + capabilities: + # -- Add capabilities to the container + # @section -- Parameters + add: + - NET_ADMIN + +# -- (tpl/list) Pod init containers +# @section -- Parameters +initContainers: [] +# -- (tpl/list) Pod sidecar containers +# @section -- Parameters +sidecarContainers: [] + +# -- Set DNS policy for the pod. +# Valid values are `ClusterFirstWithHostNet`, `ClusterFirst`, `Default` or `None`. +# @default -- `"ClusterFirst"` +# @section -- Parameters +dnsPolicy: '' + +# -- Restart policy for all containers within the pod. +# Valid values are `Always`, `OnFailure` or `Never`. +# @default -- `"Always"` +# @section -- Parameters +restartPolicy: '' + +# -- Use the host's network namespace. +# @default -- `false`. +# @section -- Parameters +hostNetwork: '' + +# -- Resources requests and limits +# @section -- Parameters +resources: {} + +# -- Overwrite pod command +# @section -- Parameters +command: [] + +# -- Additional pod arguments +# @section -- Parameters +args: [] + +# -- (tpl/list) Additional environment variables +# @section -- Parameters +env: [] + +# -- (tpl/list) Additional environment variables from a secret or configMap +# @section -- Parameters +envFrom: [] + +# -- Liveness probe configuration +# @ignore +livenessProbe: + failureThreshold: 10 + httpGet: + path: / + port: http + +# -- Readiness probe configuration +# @ignore +readinessProbe: + httpGet: + path: / + port: http + +# -- Startup probe configuration +# @ignore +startupProbe: + initialDelaySeconds: 5 + failureThreshold: 10 + httpGet: + path: / + port: http + scheme: HTTP + +# -- (tpl/list) Additional volumes +# @section -- Parameters +volumes: [] + +# -- (tpl/list) Additional volumeMounts +# @section -- Parameters +volumeMounts: [] + +# -- Node Selector configuration +# @section -- Parameters +nodeSelector: + kubernetes.io/os: linux + +# -- Tolerations configuration +# @section -- Parameters +tolerations: [] + +# -- Affinity configuration +# @section -- Parameters +affinity: {} + +service: + web: + # -- Annotations for the web service + # @section -- Traffic exposure + annotations: {} + # -- Web service type + # @section -- Traffic exposure + type: ClusterIP + # -- Web service port + # Used for the web interface listener + # @section -- Traffic exposure + port: 8888 + wireguard: + # -- Annotations for the WireGuard service + # @section -- Traffic exposure + annotations: {} + # -- Wireguard service type + # @section -- Traffic exposure + type: LoadBalancer + # -- Wireguard service ports. + # Exposes the WireGuard ports for created interfaces. + # Lowerest port is selected as start port for the first interface. + # Increment next port by 1 for each additional interface. + # @section -- Traffic exposure + ports: + - 51820 + +ingress: + # -- Specifies whether an ingress resource should be created + # @section -- Traffic exposure + enabled: false + # -- Ingress class name + # @section -- Traffic exposure + className: '' + # -- Ingress annotations + # @section -- Traffic exposure + # @section -- Traffic exposure + annotations: {} + # -- Ingress host FQDN + # @section -- Traffic exposure + host: '' + # -- Ingress path type + # @section -- Traffic exposure + pathType: ImplementationSpecific + # -- Ingress path + # @section -- Traffic exposure + path: / + # -- Ingress TLS configuration + # @section -- Traffic exposure + tls: [] + # - secretName: wg-portal-example-tls + # hosts: + # - wg-portal.example.local + +persistence: + # -- Specifies whether an persistent volume should be created + # @section -- Persistence + enabled: false + # -- Persistent Volume Claim annotations + # @section -- Persistence + annotations: {} + # -- Persistent Volume storage class. + # If undefined (the default) cluster's default provisioner will be used. + # @section -- Persistence + storageClass: '' + # -- Persistent Volume Access Mode + # @section -- Persistence + accessMode: ReadWriteOnce + # -- Persistent Volume size + # @section -- Persistence + size: 1Gi + +serviceAccount: + # -- Specifies whether a service account should be created + # @section -- RBAC + create: true + # -- Service account annotations + # @section -- RBAC + annotations: {} + # -- Automatically mount a ServiceAccount's API credentials + # @section -- RBAC + automount: false + # -- The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + # @section -- RBAC + name: ''