Skip to content

Commit

Permalink
feat: TLS support for web (#301)
Browse files Browse the repository at this point in the history
* Added TLS support for web

- Added optional configurations `cert_file` and `key_file` to run web server with https

Signed-off-by: Dmytro Bondar <[email protected]>

* Helm chart update

- Refactored Ingress to use one host only (`config.web.external_url` is required)
- Added Certificate resource template (secret is mounted to container into `/app/certs/`)
- Added support for service with mixed protocols (exposes UI and Wireguard ports on same IP)
- Added helm-docs target to makefile
- Changed pod labels to use selectorLabels
- Removed default probes (app runs without healthy web)
- Removed sections from README

Signed-off-by: Dmytro Bondar <[email protected]>

* Fix chart workflow path filter

* Fix chart lint issue

* Skip clean-up tested chart

* Try k3d cluster

---------

Signed-off-by: Dmytro Bondar <[email protected]>
  • Loading branch information
bonddim authored Sep 22, 2024
1 parent e3d05a4 commit 6ffe1a9
Show file tree
Hide file tree
Showing 17 changed files with 286 additions and 271 deletions.
24 changes: 13 additions & 11 deletions .github/workflows/chart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ name: Chart
on:
pull_request:
branches: [master]
paths: [deploy/helm]
paths: ['deploy/helm/**']
push:
branches: [master]
paths: [deploy/helm]
paths: ['deploy/helm/**']

jobs:
lint-test:
Expand All @@ -24,6 +24,14 @@ jobs:
with:
fetch-depth: 0

- name: Check docs
run: |
make 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
# ct lint requires Python 3.x to run following packages:
# - yamale (https://github.com/23andMe/Yamale)
# - yamllint (https://github.com/adrienverge/yamllint)
Expand All @@ -36,15 +44,9 @@ jobs:
- 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
- uses: nolar/setup-k3d-k3s@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Run chart-testing (install)
run: ct install --config ct.yaml
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,9 @@ build-docker:
docker build --progress=plain \
--build-arg BUILD_IDENTIFIER=${ENV_BUILD_IDENTIFIER} --build-arg BUILD_VERSION=${ENV_BUILD_VERSION} \
--build-arg TARGETPLATFORM=unknown . \
-t h44z/wg-portal:local
-t h44z/wg-portal:local

#< helm-docs: Generate the helm chart documentation
.PHONY: helm-docs
helm-docs:
docker run --rm --volume "${PWD}/deploy:/helm-docs" -u "$$(id -u)" jnorwood/helm-docs -s file
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ The following configuration options are available:
| csrf_secret | web | extremely_secret | The CSRF secret. |
| site_title | web | WireGuard Portal | The title that is shown in the web frontend. |
| site_company_name | web | WireGuard Portal | The company name that is shown at the bottom of the web frontend. |

| cert_file | web | | (Optional) Path to the TLS certificate file |
| key_file | web | | (Optional) Path to the TLS certificate key file |

## Upgrading from V1

Expand Down
4 changes: 2 additions & 2 deletions deploy/helm/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ annotations:
# 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
version: 0.2.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
appVersion: latest
126 changes: 58 additions & 68 deletions deploy/helm/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 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)
![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: latest](https://img.shields.io/badge/AppVersion-latest-informational?style=flat-square)

WireGuard Configuration Portal with LDAP, OAuth, OIDC authentication

Expand All @@ -27,90 +27,80 @@ The [Values](#values) section lists the parameters that can be configured during

## 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 |
| nameOverride | string | `""` | Partially override resource names (adds suffix) |
| fullnameOverride | string | `""` | Fully override resource names |
| extraDeploy | list | `[]` | Array of extra objects to deploy with the release |
| config.advanced | tpl/object | `{}` | Advanced configuration options. |
| config.auth | tpl/object | `{}` | Auth configuration options. |
| config.core | tpl/object | `{}` | Core configuration options.<br> 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.<br> `listening_address` will be set automatically from `service.web.port`. `external_url` is required to enable ingress and certificate resources. |
| revisionHistoryLimit | string | `10` | The number of old ReplicaSets to retain to allow rollback. |
| workloadType | string | `"Deployment"` | Workload type - `Deployment` or `StatefulSet` |
| strategy | object | `{"type":"RollingUpdate"}` | Update strategy for the workload Valid values are: `RollingUpdate` or `Recreate` for Deployment, `RollingUpdate` or `OnDelete` for StatefulSet |
| image.repository | string | `"ghcr.io/h44z/wg-portal"` | Image repository |
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
| 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 |
| initContainers | tpl/list | `[]` | Pod init containers |
| 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 |
| dnsPolicy | string | `"ClusterFirst"` | Set DNS policy for the pod. Valid values are `ClusterFirstWithHostNet`, `ClusterFirst`, `Default` or `None`. |
| restartPolicy | string | `"Always"` | Restart policy for all containers within the pod. Valid values are `Always`, `OnFailure` or `Never`. |
| hostNetwork | string | `false`. | Use the host's network namespace. |
| resources | object | `{}` | Resources requests and limits |
| command | list | `[]` | Overwrite pod command |
| args | list | `[]` | Additional pod arguments |
| env | tpl/list | `[]` | Additional environment variables |
| envFrom | tpl/list | `[]` | Additional environment variables from a secret or configMap |
| livenessProbe | object | `{}` | Liveness probe configuration |
| readinessProbe | object | `{}` | Readiness probe configuration |
| startupProbe | object | `{}` | Startup probe configuration |
| 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.<br> 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.<br> 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 |
| volumeMounts | tpl/list | `[]` | Additional volumeMounts |
| nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node Selector configuration |
| tolerations | list | `[]` | Tolerations configuration |
| affinity | object | `{}` | Affinity configuration |
| service.mixed.enabled | bool | `false` | Whether to create a single service for the web and wireguard interfaces |
| service.mixed.type | string | `"LoadBalancer"` | Service type |
| 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.web.port | int | `8888` | Web service port Used for the web interface listener |
| 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 |
| 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. |
| ingress.enabled | bool | `false` | Specifies whether an ingress resource should be created |
| ingress.className | string | `""` | Ingress class name |
| ingress.annotations | object | `{}` | Ingress annotations |
| ingress.tls | bool | `false` | Ingress TLS configuration. Enable certificate resource or add ingress annotation to create required secret |
| certificate.enabled | bool | `false` | Specifies whether a certificate resource should be created |
| certificate.issuer.name | string | `""` | Certificate issuer name |
| certificate.issuer.kind | string | `""` | Certificate issuer kind (ClusterIssuer or Issuer) |
| certificate.issuer.group | string | `"cert-manager.io"` | Certificate issuer group |
| certificate.duration | string | `""` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.renewBefore | string | `""` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.commonName | string | `""` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.emailAddresses | list | `[]` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.ipAddresses | list | `[]` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.keystores | object | `{}` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.privateKey | object | `{}` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.secretTemplate | object | `{}` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.subject | object | `{}` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.uris | list | `[]` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| certificate.usages | list | `[]` | Optional. [Documentation](https://cert-manager.io/docs/usage/certificate/#creating-certificate-resources) |
| persistence.enabled | bool | `false` | Specifies whether an persistent volume should be created |
| persistence.size | string | `"1Gi"` | Persistent Volume size |
| persistence.annotations | object | `{}` | Persistent Volume Claim annotations |
| persistence.storageClass | string | `""` | Persistent Volume storage class. If undefined (the default) cluster's default provisioner will be used. |

### RBAC

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| persistence.accessMode | string | `"ReadWriteOnce"` | Persistent Volume Access Mode |
| persistence.size | string | `"1Gi"` | Persistent Volume size |
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
| 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 |
9 changes: 9 additions & 0 deletions deploy/helm/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,12 @@ resources:
requests:
storage: {{ .Values.persistence.size | quote }}
{{- end -}}

{{/*
Define hostname
*/}}
{{- define "wg-portal.hostname" -}}
{{- if .Values.config.web.external_url -}}
{{- (urlParse (tpl .Values.config.web.external_url .)).hostname -}}
{{- end -}}
{{- end -}}
13 changes: 11 additions & 2 deletions deploy/helm/templates/_pod.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ metadata:
{{- tpl (toYaml .) $ | nindent 4 }}
{{- end }}
labels:
{{- include "wg-portal.labels" . | nindent 4 }}
{{- include "wg-portal.selectorLabels" . | nindent 4 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 4 }}
{{- end }}
Expand Down Expand Up @@ -36,7 +36,7 @@ spec:
envFrom: {{- tpl (toYaml .) $ | nindent 8 }}
{{- end }}
ports:
- name: http
- name: web
containerPort: {{ .Values.service.web.port }}
protocol: TCP
{{- range $index, $port := .Values.service.wireguard.ports }}
Expand Down Expand Up @@ -65,6 +65,10 @@ spec:
readOnly: true
- name: data
mountPath: /app/data
{{- if and .Values.certificate.enabled (include "wg-portal.hostname" .) }}
- name: certs
mountPath: /app/certs
{{- end }}
{{- with .Values.volumeMounts }}
{{- tpl (toYaml .) $ | nindent 8 }}
{{- end }}
Expand Down Expand Up @@ -97,6 +101,11 @@ spec:
- name: config
secret:
secretName: {{ include "wg-portal.fullname" . }}
{{- if and .Values.certificate.enabled (include "wg-portal.hostname" .) }}
- name: certs
secret:
secretName: {{ include "wg-portal.fullname" . }}-tls
{{- end }}
{{- if not .Values.persistence.enabled }}
- name: data
emptyDir: {}
Expand Down
53 changes: 53 additions & 0 deletions deploy/helm/templates/_service.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{{/*
Define the service template
{{- include "wg-portal.service" (dict "context" $ "scope" .Values.service.<name> "ports" list "name" "<name>") -}}
*/}}
{{- define "wg-portal.service.tpl" -}}
apiVersion: v1
kind: Service
metadata:
{{- with .scope.annotations }}
annotations: {{- toYaml . | nindent 4 }}
{{- end }}
labels: {{- include "wg-portal.labels" .context | nindent 4 }}
name: {{ include "wg-portal.fullname" .context }}{{ ternary "" (printf "-%s" .name) (empty .name) }}
spec:
{{- with .scope.clusterIP }}
clusterIP: {{ . }}
{{- end }}
{{- with .scope.externalIPs }}
externalIPs: {{ toYaml . | nindent 4 }}
{{- end }}
{{- with .scope.externalName }}
externalName: {{ . }}
{{- end }}
{{- with .scope.externalTrafficPolicy }}
externalTrafficPolicy: {{ . }}
{{- end }}
{{- with .scope.healthCheckNodePort }}
healthCheckNodePort: {{ . }}
{{- end }}
{{- with .scope.loadBalancerIP }}
loadBalancerIP: {{ . }}
{{- end }}
{{- with .scope.loadBalancerSourceRanges }}
loadBalancerSourceRanges: {{ toYaml . | nindent 4 }}
{{- end }}
ports: {{- toYaml .ports | nindent 4 }}
{{- with .scope.publishNotReadyAddresses }}
publishNotReadyAddresses: {{ . }}
{{- end }}
{{- with .scope.sessionAffinity }}
sessionAffinity: {{ . }}
{{- end }}
{{- with .scope.sessionAffinityConfig }}
sessionAffinityConfig: {{ toYaml . | nindent 4 }}
{{- end }}
{{- with .scope.topologyKeys }}
topologyKeys: {{ toYaml . | nindent 4 }}
{{- end }}
{{- with .scope.type }}
type: {{ . }}
{{- end }}
selector: {{- include "wg-portal.selectorLabels" .context | nindent 4 }}
{{- end -}}
Loading

0 comments on commit 6ffe1a9

Please sign in to comment.