├── .devcontainer └── devcontainer.json ├── .github └── workflows │ ├── helm-publish.yml │ └── helm-qa.yml ├── .gitignore ├── Makefile ├── README.md ├── ci ├── default.yaml ├── test-with-1-replica.yaml ├── test-with-byoc.yaml ├── test-with-byos.yaml ├── test-with-clusterip.yaml ├── test-with-custom-keygen-script.yaml ├── test-with-disabled-config-mgmt.yaml ├── test-with-disabled-secret-mgmt.yaml ├── test-with-dns-config.yaml ├── test-with-extra-secret-env.yaml ├── test-with-extra-sidecar.yaml ├── test-with-healthsidecar-daemonset-hostport.yaml ├── test-with-healthsidecar-nodeport.yaml ├── test-with-nodeport.yaml ├── test-with-runtimeclass.yaml ├── test-with-seccomp.yaml ├── test-with-volumes.yaml └── test-with-wgmgr-keygen.yaml ├── dashboard-screenshot.png ├── helm └── wireguard │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── ci │ ├── README.md │ ├── default-values.yaml │ └── wg-keymgr-values.yaml │ ├── files │ └── dashboard.json │ ├── scripts │ └── gen-key.sh │ ├── templates │ ├── _helpers.tpl │ ├── config.yaml │ ├── dashboard.yaml │ ├── deployment.yaml │ ├── extra-config-maps.yaml │ ├── extra-storage.yaml │ ├── hpa.yaml │ ├── pdb.yaml │ ├── privatekey-gen-job.yaml │ ├── prometheusrule.yaml │ ├── sa.yaml │ ├── service-exporter.yaml │ ├── service-health-endpoint.yaml │ ├── service.yaml │ ├── servicemonitor.yaml │ └── tests │ │ └── client-connect.yaml │ └── values.yaml └── renovate.json /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WG-Chart Dev Container", 3 | "image": "mcr.microsoft.com/vscode/devcontainers/go:latest", 4 | "features": { 5 | "ghcr.io/devcontainers/features/github-cli:1": {}, 6 | "ghcr.io/devcontainers/features/common-utils:2": {}, 7 | "ghcr.io/devcontainers/features/docker-in-docker:2": {}, 8 | "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {}, 9 | "ghcr.io/mpriscella/features/kind:1": {}, 10 | "ghcr.io/devcontainers-contrib/features/apt-packages:1": { 11 | "packages": "wireguard" 12 | } 13 | }, 14 | "customizations": { 15 | "vscode": { 16 | "extensions": [ 17 | "ms-kubernetes-tools.vscode-kubernetes-tools", 18 | "redhat.vscode-yaml", 19 | "github.vscode-pull-request-github", 20 | "github.vscode-github-actions", 21 | "ms-azuretools.vscode-docker", 22 | "johnpapa.vscode-peacock", 23 | "ms-vscode.makefile-tools" 24 | ], 25 | "settings": { 26 | "editor.tabSize": 2, 27 | "terminal.integrated.defaultProfile.linux": "zsh", 28 | "terminal.integrated.profiles.linux": { 29 | "bash": { 30 | "path": "bash", 31 | "icon": "terminal-bash" 32 | }, 33 | "zsh": { 34 | "path": "zsh" 35 | } 36 | } 37 | } 38 | }, 39 | "postStartCommand": "git config --global --add safe.directory ${containerWorkspaceFolder} && curl -sS https://webinstall.dev/k9s | bash" 40 | } 41 | } -------------------------------------------------------------------------------- /.github/workflows/helm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Release Chart 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - "helm/**" 8 | - ".github/workflows/helm-publish.yml" 9 | jobs: 10 | Release: 11 | name: 'Helm' 12 | uses: curium-rocks/flows/.github/workflows/helm-release.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/helm-qa.yml: -------------------------------------------------------------------------------- 1 | name: Lint and Test Chart 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: '5 8 * * *' 6 | pull_request: 7 | paths: 8 | - 'helm/**' 9 | - '.github/workflows/helm-qa.yml' 10 | jobs: 11 | test: 12 | strategy: 13 | matrix: 14 | suite: 15 | - name: default 16 | ct_extra_args: >- 17 | --upgrade 18 | name: 'Helm (${{ matrix.suite.name }})' 19 | uses: curium-rocks/flows/.github/workflows/helm-qa.yml@main 20 | with: 21 | charts_dir: 'helm' 22 | ct_extra_args: ${{ matrix.suite.ct_extra_args }} 23 | run-kubeval: false 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CHART_PATH = helm/wireguard 2 | OVERRIDE_PATH ?= ci/default.yaml 3 | TEMPLATE_ARGS ?= '--disable-openapi-validation' 4 | HELM_NAMESPACE ?= wireguard-test 5 | HELM_RELEASE_NAME ?= wireguard-test 6 | 7 | lint: 8 | helm lint $(CHART_PATH) -f $(OVERRIDE_PATH) 9 | 10 | template: 11 | helm template --debug $(TEMPLATE_ARGS) $(CHART_PATH) -f $(OVERRIDE_PATH) 12 | 13 | deploy: 14 | helm --namespace $(HELM_NAMESPACE) upgrade --install $(HELM_RELEASE_NAME) ./helm/wireguard/ -f $(OVERRIDE_PATH) $(HELM_EXTRA_ARGS) 15 | 16 | test: 17 | helm --namespace $(HELM_NAMESPACE) test $(HELM_RELEASE_NAME) 18 | 19 | cluster: 20 | kind create cluster 21 | 22 | cluster-go-away: 23 | kind delete cluster 24 | 25 | clean-secret: 26 | kubectl --namespace $(HELM_NAMESPACE) delete secret $(HELM_RELEASE_NAME)-wg-generated 27 | 28 | clean: 29 | helm --namespace $(HELM_NAMESPACE) del $(HELM_RELEASE_NAME) 30 | 31 | docs-update: 32 | docker run --rm --volume "$$PWD:/helm-docs" --network host jnorwood/helm-docs:latest 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wireguard Chart 2 | A simple chart that can be used to run wireguard inside of a Kubernetes cluster. 3 | 4 | ## Add the helm repo 5 | To add this helm repo, run `helm repo add wireguard https://bryopsida.github.io/wireguard-chart` followed by a `helm repo update` to fetch the contents. 6 | 7 | ## Deploy 8 | To deploy with defaults and use the automatically generated private key you can use `helm upgrade --install wg-vpn-1 wireguard/wireguard --namespace `. 9 | This will create a load balancer service exposing UDP port `51820`, to run multiple wireguard releases you will need to change the service port to avoid collisions, 10 | you can find the helm values documentation [here](helm/wireguard/README.md). By default no client configurations are added. The default CIDR for the VPN network is `10.34.0.1/24` 11 | 12 | ## Maintain client configurations 13 | Follow the wireguard [documentation](https://www.wireguard.com/quickstart/) for generating keys and determining client IPs. Clients can be set by providing the following yaml override values. 14 | 15 | ``` yaml 16 | wireguard: 17 | clients: 18 | - PublicKey: 19 | AllowedIPs: 10.34.0.2/32 20 | ``` 21 | 22 | And feeding it into helm `helm upgrade --install wg-vpn-1 wireguard/wireguard --namespace -f ` 23 | 24 | 25 | ## Example Tunnel Configuration(s) 26 | ### Route all traffic and use kube-dns 27 | 28 | If you want to route all traffic through WireGuard and use the cluster dns to allow service discovery, you can use the following tunnel configuration. 29 | 30 | Release values 31 | 32 | ``` yaml 33 | service: 34 | port: 51225 35 | wireguard: 36 | allowWan: false 37 | clients: 38 | - AllowedIPs: 172.32.32.3/32 39 | PresharedKey: 40 | PublicKey: 41 | - AllowedIPs: 172.32.32.2/32 42 | PresharedKey: 43 | PublicKey: 44 | serverAddress: 172.32.32.1/24 45 | serverCidr: 172.32.32.0/24 46 | ``` 47 | 48 | Where kube-dns service ip address is `10.43.0.10`. 49 | 50 | 51 | ``` ini 52 | [Interface] 53 | PrivateKey = 54 | Address = 172.32.32.2/32 55 | DNS = 10.43.0.10 56 | 57 | [Peer] 58 | PublicKey = 59 | PresharedKey = 60 | AllowedIPs = 0.0.0.0/0 61 | Endpoint = : of wireguard endpoint 62 | ``` 63 | 64 | How you can test it works: 65 | 66 | 1) Pick a internal K8S service. I'm going to use a couchdb service as an example. 67 | 2) Dig it as shown below 68 | 3) You should see that the kube-dns server answers it and you should be able to resolve A records 69 | 70 | ``` shell 71 | dig couchdb-couchdb.couchdb.svc.cluster.local 72 | 73 | ; <<>> DiG 9.10.6 <<>> couchdb-couchdb.couchdb.svc.cluster.local 74 | ;; global options: +cmd 75 | ;; Got answer: 76 | ;; WARNING: .local is reserved for Multicast DNS 77 | ;; You are currently testing what happens when an mDNS query is leaked to DNS 78 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65023 79 | ;; flags: qr aa rd; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1 80 | ;; WARNING: recursion requested but not available 81 | 82 | ;; OPT PSEUDOSECTION: 83 | ; EDNS: version: 0, flags:; udp: 4096 84 | ;; QUESTION SECTION: 85 | ;couchdb-couchdb.couchdb.svc.cluster.local. IN A 86 | 87 | ;; ANSWER SECTION: 88 | couchdb-couchdb.couchdb.svc.cluster.local. 5 IN A 89 | couchdb-couchdb.couchdb.svc.cluster.local. 5 IN A 90 | couchdb-couchdb.couchdb.svc.cluster.local. 5 IN A 91 | couchdb-couchdb.couchdb.svc.cluster.local. 5 IN A 92 | couchdb-couchdb.couchdb.svc.cluster.local. 5 IN A 93 | couchdb-couchdb.couchdb.svc.cluster.local. 5 IN A 94 | 95 | ;; Query time: 51 msec 96 | ;; SERVER: 10.43.0.10#53(10.43.0.10) 97 | ;; WHEN: Sun Apr 02 15:04:51 CDT 2023 98 | ;; MSG SIZE rcvd: 41 99 | ``` 100 | 101 | If you are using something like cilium and have access to hubble you can verify the network flows there as well. 102 | 103 | 104 | ## Prometheus Metrics 105 | 106 | ```yaml 107 | metrics: 108 | enabled: true 109 | ``` 110 | 111 | - Adds another container with [prometheus_wireguard_exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter) 112 | - Installs a Prometheus ServiceMonitor to scrape metrics from the wireguard server 113 | - Deploys a Grafana dashboard: 114 | ![Grafana Dashboard](dashboard-screenshot.png) 115 | -------------------------------------------------------------------------------- /ci/default.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bryopsida/wireguard-chart/9fecf0da8aace415be9a95de93a35708d406de75/ci/default.yaml -------------------------------------------------------------------------------- /ci/test-with-1-replica.yaml: -------------------------------------------------------------------------------- 1 | service: 2 | type: ClusterIP 3 | replicaCount: 1 4 | autoscaling: 5 | enabled: false -------------------------------------------------------------------------------- /ci/test-with-byoc.yaml: -------------------------------------------------------------------------------- 1 | configSecretName: my-super-duper-config-secret 2 | configSecretProperty: not-wg0.conf 3 | -------------------------------------------------------------------------------- /ci/test-with-byos.yaml: -------------------------------------------------------------------------------- 1 | secretName: my-super-duper-secret 2 | -------------------------------------------------------------------------------- /ci/test-with-clusterip.yaml: -------------------------------------------------------------------------------- 1 | service: 2 | type: ClusterIP 3 | -------------------------------------------------------------------------------- /ci/test-with-custom-keygen-script.yaml: -------------------------------------------------------------------------------- 1 | keygenJob: 2 | extraScripts: 3 | test.sh: | 4 | #!/usr/bin/env sh 5 | echo "Hello from custom script, EXTRA_NAMESPACE=$EXTRA_NAMESPACE" 6 | /job/entry-point.sh 7 | extraEnv: 8 | EXTRA_NAMESPACE: "{{ .Release.Namespace }}" 9 | command: 10 | - "/job/test.sh" -------------------------------------------------------------------------------- /ci/test-with-disabled-config-mgmt.yaml: -------------------------------------------------------------------------------- 1 | disableConfigManagement: true 2 | -------------------------------------------------------------------------------- /ci/test-with-disabled-secret-mgmt.yaml: -------------------------------------------------------------------------------- 1 | disablePrivateKeyManagement: true 2 | -------------------------------------------------------------------------------- /ci/test-with-dns-config.yaml: -------------------------------------------------------------------------------- 1 | dnsPolicy: None 2 | dnsConfig: 3 | nameservers: 4 | - 192.0.2.1 # this is an example 5 | searches: 6 | - ns1.svc.cluster-domain.example 7 | - my.dns.search.suffix 8 | options: 9 | - name: ndots 10 | value: "2" 11 | - name: edns0 12 | -------------------------------------------------------------------------------- /ci/test-with-extra-secret-env.yaml: -------------------------------------------------------------------------------- 1 | keygenJob: 2 | extraEnvSecrets: 3 | TEST_1: 4 | secretName: SUPER_DUPER_SECRET 5 | secretPropertyName: password 6 | extraEnvSecrets: 7 | TEST_2: 8 | secretName: SUPER_DUPER_SECRET 9 | secretPropertyName: password -------------------------------------------------------------------------------- /ci/test-with-extra-sidecar.yaml: -------------------------------------------------------------------------------- 1 | # with host network used, a service object is not needed 2 | service: 3 | enabled: false 4 | replicaCount: 1 5 | autoscaling: 6 | enabled: false 7 | deploymentStrategy: 8 | type: "Recreate" 9 | # create storage volumes for wg-portal sql lite database 10 | extraStorage: 11 | - name: wg-portal-data 12 | storage: 8Gi 13 | # omitting storage class and using default, if you need 14 | # to target a particular storage class specify this 15 | # storageClassName: some-storage-class 16 | accessModes: 17 | # ReadWriteOnce is used for easy testing, 18 | # if you are going to run multiple pods this needs to be 19 | # ReadWriteMany and you'll need a storage system that supports this 20 | - ReadWriteOnce 21 | volumeMode: Filesystem 22 | # wg-portal docs use host network and NET_ADMIN 23 | runPodOnHostNetwork: true 24 | extraConfigMaps: 25 | - name: wg-portal-config 26 | data: 27 | # this is from the sample config file in the wg-portal repo 28 | # as a placeholder in testing 29 | # be sure to adjust the external_url to something appropriate for your 30 | # network 31 | config.yml: | 32 | advanced: 33 | log_level: trace 34 | 35 | core: 36 | admin_user: admin 37 | admin_password: wgportal 38 | create_default_peer: true 39 | create_default_peer_on_creation: false 40 | 41 | web: 42 | external_url: http://localhost:8888 43 | request_logging: true 44 | # we need to mount/attach the storage to the pod 45 | volumes: 46 | - name: wg-portal-data 47 | persistentVolumeClaim: 48 | claimName: wg-portal-data 49 | - name: wg-portal-config 50 | configMap: 51 | name: wg-portal-config 52 | # add wg-portal side car 53 | extraSideCars: 54 | - name: wg-portal 55 | image: wgportal/wg-portal 56 | imagePullPolicy: Always 57 | securityContext: 58 | # wg-portal runs as root and enumerates devices 59 | # as far as I can tell it requires high level access to the node 60 | # to function 61 | runAsNonRoot: false 62 | capabilities: 63 | add: 64 | - NET_ADMIN 65 | ports: 66 | - containerPort: 8888 67 | protocol: TCP 68 | name: http 69 | # we need to mount the pod volume into the side car 70 | volumeMounts: 71 | - mountPath: "/app/config" 72 | name: wg-portal-config 73 | - mountPath: "/app/data" 74 | name: wg-portal-data 75 | # assuming the generated wg key secret is used 76 | # and the default wg0.conf management is used 77 | # need to project the following into /etc/wireguard for wg-portal 78 | - name: config 79 | mountPath: /etc/wireguard/wg0.conf 80 | subPath: wg0.conf 81 | - name: privatekey 82 | mountPath: /etc/wireguard/privatekey 83 | subPath: privatekey 84 | -------------------------------------------------------------------------------- /ci/test-with-healthsidecar-daemonset-hostport.yaml: -------------------------------------------------------------------------------- 1 | healthSideCar: 2 | enabled: true 3 | service: 4 | enabled: false 5 | useHostPort: true 6 | hostPort: 10000 7 | autoscaling: 8 | enabled: false 9 | daemonSet: true 10 | -------------------------------------------------------------------------------- /ci/test-with-healthsidecar-nodeport.yaml: -------------------------------------------------------------------------------- 1 | healthSideCar: 2 | enabled: true 3 | autoscaling: 4 | enabled: false 5 | replicaCount: 1 6 | deploymentStrategy: 7 | type: Recreate 8 | -------------------------------------------------------------------------------- /ci/test-with-nodeport.yaml: -------------------------------------------------------------------------------- 1 | service: 2 | type: NodePort -------------------------------------------------------------------------------- /ci/test-with-runtimeclass.yaml: -------------------------------------------------------------------------------- 1 | runtimeClassName: 'runc' -------------------------------------------------------------------------------- /ci/test-with-seccomp.yaml: -------------------------------------------------------------------------------- 1 | securityContext: 2 | seccompProfile: 3 | type: RuntimeDefault 4 | -------------------------------------------------------------------------------- /ci/test-with-volumes.yaml: -------------------------------------------------------------------------------- 1 | volumes: 2 | - name: vol 3 | hostPath: 4 | path: /any/path/it/will/be/replaced 5 | volumeMounts: 6 | - name: vol 7 | mountPath: /mount-in-container 8 | -------------------------------------------------------------------------------- /ci/test-with-wgmgr-keygen.yaml: -------------------------------------------------------------------------------- 1 | keygenJob: 2 | useWireguardManager: true 3 | -------------------------------------------------------------------------------- /dashboard-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bryopsida/wireguard-chart/9fecf0da8aace415be9a95de93a35708d406de75/dashboard-screenshot.png -------------------------------------------------------------------------------- /helm/wireguard/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | ci 25 | -------------------------------------------------------------------------------- /helm/wireguard/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: wireguard 3 | description: A Helm chart for managing a wireguard vpn in kubernetes 4 | type: application 5 | version: 0.30.0 6 | appVersion: "0.0.0" 7 | maintainers: 8 | - name: bryopsida 9 | -------------------------------------------------------------------------------- /helm/wireguard/README.md: -------------------------------------------------------------------------------- 1 | # wireguard 2 | 3 | ![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.0](https://img.shields.io/badge/AppVersion-0.0.0-informational?style=flat-square) 4 | 5 | A Helm chart for managing a wireguard vpn in kubernetes 6 | 7 | ## Maintainers 8 | 9 | | Name | Email | Url | 10 | | ---- | ------ | --- | 11 | | bryopsida | | | 12 | 13 | ## Values 14 | 15 | | Key | Type | Default | Description | 16 | |-----|------|---------|-------------| 17 | | affinity | object | `{"podAntiAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":[{"labelSelector":{"matchLabels":{"app":"{{ .Release.Name }}-wireguard","role":"vpn"}},"topologyKey":"kubernetes.io/hostname"}]}}` | Set pod affinity or antiAffinity | 18 | | autoscaling.enabled | bool | `true` | | 19 | | autoscaling.maxReplicas | int | `10` | | 20 | | autoscaling.minReplicas | int | `3` | | 21 | | autoscaling.targetCPUUtilizationPercentage | int | `75` | | 22 | | configSecretName | string | `nil` | If provided, this secret will be used instead of the config created from the helm value scope | 23 | | configSecretProperty | string | `"wg0.conf"` | The property/key on the secret holding the wireguard configuration file | 24 | | daemonSet | bool | `false` | Run as a DaemonSet instead of Deployment | 25 | | deploymentStrategy.rollingUpdate.maxSurge | int | `1` | | 26 | | deploymentStrategy.rollingUpdate.maxUnavailable | int | `0` | | 27 | | deploymentStrategy.type | string | `"RollingUpdate"` | | 28 | | disableConfigManagement | bool | `false` | Disable creation and any mount of the wireguard confifugration file, this assumes another mechanism is provided/used to manage a configuration file | 29 | | disablePrivateKeyManagement | bool | `false` | Disable creation and any mounting of a private key, this assumes another mechanism is provided/used at the container level to fetch the private key | 30 | | disruptionBudget.enabled | bool | `true` | | 31 | | disruptionBudget.minAvailable | int | `2` | | 32 | | extraConfigMaps | list | `[]` | Create additional configmaps that may be used in sidecars | 33 | | extraEnv | object | `{}` | Provide additional environment variables to the wireguard container | 34 | | extraSideCars | list | `[]` | Provide additional sidecars to the wireguard pod, these are directly attached to the pod and must be well formed ContainerSpec | 35 | | extraStorage | list | `[]` | Create storage claims that can be used by side cars | 36 | | healthSideCar.enabled | bool | `false` | Opt in side car to expose a http health end point for external load balancers that are not kubernetes aware, in most cases this is not needed | 37 | | healthSideCar.hostPort | int | `13000` | When useHostPort is true this is the host port defined | 38 | | healthSideCar.image.pullPolicy | string | `"Always"` | Pull Policy always to avoid cached rolling tags, if you change this you should use a non rolling tag | 39 | | healthSideCar.image.repository | string | `"ghcr.io/bryopsida/http-healthcheck-sidecar"` | Override repo if you prefer to use your own image | 40 | | healthSideCar.image.tag | string | `"main"` | Rolling tag used by default to take patches automatically | 41 | | healthSideCar.resources | object | `{"limits":{"cpu":"100m","ephemeral-storage":"256Mi","memory":"256Mi"},"requests":{"cpu":"100m","ephemeral-storage":"8Mi","memory":"256Mi"}}` | set resource constraints, set to nil to remove | 42 | | healthSideCar.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsGroup":10001,"runAsNonRoot":true,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | Secure settings by default, can be overriden to reduce security posture if needed | 43 | | healthSideCar.service.enabled | bool | `true` | Toggle to enable the service, if the pod is a daemonset healthSideCar.useHostPort can be used instead | 44 | | healthSideCar.service.nodePort | int | `31313` | The port for the service exposed on each node | 45 | | healthSideCar.service.port | int | `3000` | Override service port if needed | 46 | | healthSideCar.service.type | string | `"NodePort"` | Service type, given the use case, in most cases this should be NodePort | 47 | | healthSideCar.useHostPort | bool | `false` | When enabled the container will define a host port, in most cases this should only be used when deploying with daemonSet: true | 48 | | hostPort | int | `51820` | Host port to expose the VPN service on | 49 | | image.pullPolicy | string | `"Always"` | | 50 | | image.repository | string | `"ghcr.io/bryopsida/wireguard"` | | 51 | | image.tag | string | `"main"` | | 52 | | initContainer.image.repository | string | `"busybox"` | | 53 | | initContainer.image.tag | string | `"latest"` | | 54 | | initContainer.resources.limits.cpu | string | `"100m"` | | 55 | | initContainer.resources.limits.ephemeral-storage | string | `"64Mi"` | | 56 | | initContainer.resources.limits.memory | string | `"64Mi"` | | 57 | | initContainer.resources.requests.cpu | string | `"100m"` | | 58 | | initContainer.resources.requests.ephemeral-storage | string | `"8Mi"` | | 59 | | initContainer.resources.requests.memory | string | `"64Mi"` | | 60 | | keygenJob.command | list | `["/job/entry-point.sh"]` | Specify the script to run to generate the private key | 61 | | keygenJob.containerSecurityContext.allowPrivilegeEscalation | bool | `false` | | 62 | | keygenJob.containerSecurityContext.privileged | bool | `false` | | 63 | | keygenJob.containerSecurityContext.readOnlyRootFilesystem | bool | `true` | | 64 | | keygenJob.containerSecurityContext.runAsGroup | int | `1000` | | 65 | | keygenJob.containerSecurityContext.runAsNonRoot | bool | `true` | | 66 | | keygenJob.containerSecurityContext.runAsUser | int | `1000` | | 67 | | keygenJob.extraEnv | object | `{}` | Add additional environment variables to the key generation job, supports helm templating | 68 | | keygenJob.extraScripts | object | `{}` | Inject additional scripts into the key generation job | 69 | | keygenJob.image.pullPolicy | string | `"Always"` | | 70 | | keygenJob.image.repository | string | `"ghcr.io/curium-rocks/wg-kubectl"` | | 71 | | keygenJob.image.tag | string | `"latest"` | | 72 | | keygenJob.podAnnotations | object | `{}` | | 73 | | keygenJob.podSecurityContext.fsGroup | int | `1000` | | 74 | | keygenJob.podSecurityContext.fsGroupChangePolicy | string | `"Always"` | | 75 | | keygenJob.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | | 76 | | keygenJob.resources.limits.cpu | string | `"100m"` | | 77 | | keygenJob.resources.limits.ephemeral-storage | string | `"128Mi"` | | 78 | | keygenJob.resources.limits.memory | string | `"256Mi"` | | 79 | | keygenJob.resources.requests.cpu | string | `"100m"` | | 80 | | keygenJob.resources.requests.ephemeral-storage | string | `"8Mi"` | | 81 | | keygenJob.resources.requests.memory | string | `"256Mi"` | | 82 | | keygenJob.useWireguardManager | bool | `true` | when enabled, uses a image with go bindings for k8s and wg to create the secret if it does not exist, on re-runs it leaves the existing secret in place and exits succesfully | 83 | | keygenJob.wireguardMgrImage | object | `{"pullPolicy":"Always","repository":"ghcr.io/bryopsida/k8s-wireguard-mgr","tag":"main"}` | When useWireguardManager is enabled this image is used instead of the kubectl image | 84 | | labels | object | `{}` | | 85 | | metrics.dashboard.annotations | object | `{}` | Grafana dashboard annotations | 86 | | metrics.dashboard.enabled | bool | `true` | Create a ConfigMap with a Grafana dashboard | 87 | | metrics.dashboard.labels | object | `{"grafana_dashboard":"1"}` | Grafana dashboard labels | 88 | | metrics.enabled | bool | `false` | Enable exposing Wireguard metrics | 89 | | metrics.extraEnv.EXPORT_LATEST_HANDSHAKE_DELAY | string | `"true"` | Adds the wireguard_latest_handshake_delay_seconds metric that automatically calculates the seconds passed since the last handshake | 90 | | metrics.extraEnv.PROMETHEUS_WIREGUARD_EXPORTER_ADDRESS | string | `"0.0.0.0"` | Specify the service address. This is the address your Prometheus instance should point to | 91 | | metrics.extraEnv.PROMETHEUS_WIREGUARD_EXPORTER_CONFIG_FILE_NAMES | string | `"/etc/wireguard/{{ .Values.configSecretProperty }}"` | This flag adds the friendly_name attribute or the friendly_json attributes to the exported entries. See [Friendly tags](https://mindflavor.github.io/prometheus_wireguard_exporter/#friendly-tags) for more details. Multiple files are allowed (they will be merged as a single file in memory so avoid duplicates) | 92 | | metrics.extraEnv.PROMETHEUS_WIREGUARD_EXPORTER_EXPORT_REMOTE_IP_AND_PORT_ENABLED | string | `"true"` | Exports peer’s remote ip and port as labels (if available) | 93 | | metrics.extraEnv.PROMETHEUS_WIREGUARD_EXPORTER_INTERFACES | string | `"all"` | Specifies the interface(s) passed to the wg show dump parameter. Multiple parameters are allowed | 94 | | metrics.extraEnv.PROMETHEUS_WIREGUARD_EXPORTER_PREPEND_SUDO_ENABLED | string | `"false"` | Prepends sudo to wg commands | 95 | | metrics.extraEnv.PROMETHEUS_WIREGUARD_EXPORTER_SEPARATE_ALLOWED_IPS_ENABLED | string | `"true"` | Enable the allowed ip + subnet split mode for the labels | 96 | | metrics.extraEnv.PROMETHEUS_WIREGUARD_EXPORTER_VERBOSE_ENABLED | string | `"false"` | Enable verbose mode | 97 | | metrics.image | object | `{"pullPolicy":"IfNotPresent","repository":"docker.io/mindflavor/prometheus-wireguard-exporter","tag":"3.6.6"}` | Wireguard Exporter image | 98 | | metrics.prometheusRule.annotations | object | `{}` | Annotations | 99 | | metrics.prometheusRule.enabled | bool | `false` | Create PrometheusRule Resource for scraping metrics using PrometheusOperator | 100 | | metrics.prometheusRule.groups | list | `[]` | Groups, containing the alert rules. Example: groups: - name: Wireguard rules: - alert: WireguardInstanceNotAvailable annotations: message: "Wireguard instance in namespace {{ `{{` }} $labels.namespace {{ `}}` }} has not been available for the last 5 minutes." expr: | absent(kube_pod_status_ready{namespace="{{ include "common.names.namespace" . }}", condition="true"} * on (pod) kube_pod_labels{pod=~"{{ include "common.names.fullname" . }}-\\d+", namespace="{{ include "common.names.namespace" . }}"}) != 0 for: 5m labels: severity: critical | 101 | | metrics.prometheusRule.labels | object | `{}` | Additional labels that can be used so PrometheusRule will be discovered by Prometheus | 102 | | metrics.prometheusRule.namespace | string | `""` | Namespace of the ServiceMonitor. If empty, current namespace is used | 103 | | metrics.resources.limits.cpu | string | `"100m"` | | 104 | | metrics.resources.limits.ephemeral-storage | string | `"128Mi"` | | 105 | | metrics.resources.limits.memory | string | `"256Mi"` | | 106 | | metrics.resources.requests.cpu | string | `"100m"` | | 107 | | metrics.resources.requests.ephemeral-storage | string | `"8Mi"` | | 108 | | metrics.resources.requests.memory | string | `"256Mi"` | | 109 | | metrics.service.annotations | object | `{}` | Annotations for enabling prometheus to access the metrics endpoints | 110 | | metrics.service.labels | object | `{}` | Additional service labels | 111 | | metrics.service.port | int | `9586` | Metrics service HTTP port | 112 | | metrics.serviceMonitor.annotations | object | `{}` | Annotations | 113 | | metrics.serviceMonitor.enabled | bool | `true` | Create ServiceMonitor Resource for scraping metrics using PrometheusOperator | 114 | | metrics.serviceMonitor.honorLabels | bool | `false` | honorLabels chooses the metric's labels on collisions with target labels | 115 | | metrics.serviceMonitor.interval | string | `"30s"` | Interval at which metrics should be scraped | 116 | | metrics.serviceMonitor.jobLabel | string | `""` | The name of the label on the target service to use as the job name in prometheus. | 117 | | metrics.serviceMonitor.labels | object | `{}` | Additional labels that can be used so ServiceMonitor will be discovered by Prometheus | 118 | | metrics.serviceMonitor.metricRelabelings | list | `[]` | MetricRelabelConfigs to apply to samples before ingestion | 119 | | metrics.serviceMonitor.namespace | string | `""` | Namespace of the ServiceMonitor. If empty, current namespace is used | 120 | | metrics.serviceMonitor.path | string | `"/metrics"` | The endpoint configuration of the ServiceMonitor. Path is mandatory. Interval, timeout and relabelings can be overwritten. | 121 | | metrics.serviceMonitor.port | string | `"exporter"` | Metrics service HTTP port | 122 | | metrics.serviceMonitor.relabelings | list | `[]` | RelabelConfigs to apply to samples before scraping | 123 | | metrics.serviceMonitor.scrapeTimeout | string | `""` | Specify the timeout after which the scrape is ended e.g: scrapeTimeout: 30s | 124 | | metrics.serviceMonitor.selector | object | `{}` | Prometheus instance selector labels ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration | 125 | | nodeSelector | object | `{}` | Set pod nodeSelector, a simplified version of affinity | 126 | | podAnnotations | object | `{}` | | 127 | | replicaCount | int | `3` | | 128 | | resources.limits.cpu | string | `"100m"` | | 129 | | resources.limits.ephemeral-storage | string | `"128Mi"` | | 130 | | resources.limits.memory | string | `"256Mi"` | | 131 | | resources.requests.cpu | string | `"100m"` | | 132 | | resources.requests.ephemeral-storage | string | `"8Mi"` | | 133 | | resources.requests.memory | string | `"256Mi"` | | 134 | | runPodOnHostNetwork | bool | `false` | Run pod on host network | 135 | | runtimeClassName | string | `nil` | Override the default runtime class of the container, if not provided `runc` will most likely be used | 136 | | secretName | string | `nil` | Name of a secret with a wireguard private key on key privatekey, if not provided on first install a hook generates one. | 137 | | securityContext.allowPrivilegeEscalation | bool | `true` | | 138 | | securityContext.privileged | bool | `false` | | 139 | | securityContext.readOnlyRootFilesystem | bool | `true` | | 140 | | securityContext.runAsNonRoot | bool | `true` | | 141 | | securityContext.runAsUser | int | `1000` | | 142 | | service.annotations | object | `{}` | Annotations | 143 | | service.enabled | bool | `true` | Whether the service will be created or not | 144 | | service.externalTrafficPolicy | string | `""` | External Traffic Policy for the service | 145 | | service.extraPorts | list | `[]` | Extra ports that can be attached to the service object, these are passed directly to the port array on the service and must be well formed to the specification | 146 | | service.loadBalancerClass | string | `""` | loadBalancerClass for Service Controllers that support it | 147 | | service.loadBalancerIP | string | `""` | IP to assign to the LoadBalancer service | 148 | | service.nodePort | int | `31820` | Node port, only valid with service type: NodePort | 149 | | service.port | int | `51820` | Service port, default is 51820 UDP | 150 | | service.type | string | `"LoadBalancer"` | Service type, to keep internal to cluster use ClusterIP or NodePort | 151 | | tolerations | list | `[]` | Set pod tolerations | 152 | | useHostPort | bool | `false` | Expose VPN service on hostPort | 153 | | volumeMounts | object | `{}` | Passthrough pod volume mounts | 154 | | volumes | object | `{}` | Passthrough pod volumes | 155 | | wireguard.clients | list | `[]` | A collection of clients that will be added to wg0.conf, accepts objects with keys PublicKey and AllowedIPs (mandatory) and optional FriendlyName or FriendlyJson (https://github.com/MindFlavor/prometheus_wireguard_exporter#friendly-tags) and PersistentKeepalive (https://www.wireguard.com/quickstart/#nat-and-firewall-traversal-persistence), stored in secret | 156 | | wireguard.interfaceOpts | object | `{}` | A collection of extraopts for wireguard interface | 157 | | wireguard.natAddSourceNet | bool | `true` | Add the serverCidr to the nat source net option | 158 | | wireguard.serverAddress | string | `"10.34.0.1/24"` | Address of the VPN server | 159 | | wireguard.serverCidr | string | `"10.34.0.0/24"` | Subnet for your VPN, take care not to clash with cluster POD cidr | 160 | 161 | ---------------------------------------------- 162 | Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) 163 | -------------------------------------------------------------------------------- /helm/wireguard/ci/README.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | 3 | This is a set of value files that will be tested in the CI pipeline. 4 | 5 | You can read more about the behavior [here](https://github.com/helm/charts/blob/master/test/README.md#providing-custom-test-values) 6 | -------------------------------------------------------------------------------- /helm/wireguard/ci/default-values.yaml: -------------------------------------------------------------------------------- 1 | # kics-scan ignore 2 | service: 3 | type: ClusterIP 4 | wireguard: 5 | clients: 6 | - AllowedIPs: 172.32.32.2/32 7 | # used for testing only 8 | PublicKey: NzYmaNXHi8+3NBpg7uoRFw7wO+fLG65gZToKqtecLAo= 9 | serverAddress: 172.32.32.1/24 10 | serverCidr: 172.32.32.0/24 11 | replicaCount: 1 12 | autoscaling: 13 | enabled: false 14 | deploymentStrategy: 15 | type: Recreate 16 | -------------------------------------------------------------------------------- /helm/wireguard/ci/wg-keymgr-values.yaml: -------------------------------------------------------------------------------- 1 | # kics-scan ignore 2 | service: 3 | type: ClusterIP 4 | wireguard: 5 | clients: 6 | - AllowedIPs: 172.32.32.2/32 7 | # used for testing only 8 | PublicKey: NzYmaNXHi8+3NBpg7uoRFw7wO+fLG65gZToKqtecLAo= 9 | serverAddress: 172.32.32.1/24 10 | serverCidr: 172.32.32.0/24 11 | replicaCount: 1 12 | autoscaling: 13 | enabled: false 14 | deploymentStrategy: 15 | type: Recreate 16 | keygenJob: 17 | useWireguardManager: true 18 | -------------------------------------------------------------------------------- /helm/wireguard/files/dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": { 7 | "type": "grafana", 8 | "uid": "-- Grafana --" 9 | }, 10 | "enable": true, 11 | "hide": true, 12 | "iconColor": "rgba(0, 211, 255, 1)", 13 | "name": "Annotations & Alerts", 14 | "target": { 15 | "limit": 100, 16 | "matchAny": false, 17 | "tags": [], 18 | "type": "dashboard" 19 | }, 20 | "type": "dashboard" 21 | } 22 | ] 23 | }, 24 | "description": "Metrics for Wireguard VPN", 25 | "editable": true, 26 | "fiscalYearStartMonth": 0, 27 | "gnetId": 17251, 28 | "graphTooltip": 0, 29 | "id": 91, 30 | "links": [], 31 | "liveNow": true, 32 | "panels": [ 33 | { 34 | "datasource": { 35 | "type": "prometheus", 36 | "uid": "prometheus" 37 | }, 38 | "description": "", 39 | "fieldConfig": { 40 | "defaults": { 41 | "color": { 42 | "mode": "thresholds" 43 | }, 44 | "mappings": [], 45 | "thresholds": { 46 | "mode": "percentage", 47 | "steps": [ 48 | { 49 | "color": "green", 50 | "value": null 51 | }, 52 | { 53 | "color": "orange", 54 | "value": 70 55 | }, 56 | { 57 | "color": "red", 58 | "value": 85 59 | } 60 | ] 61 | } 62 | }, 63 | "overrides": [ 64 | { 65 | "matcher": { 66 | "id": "byType", 67 | "options": "number" 68 | }, 69 | "properties": [ 70 | { 71 | "id": "unit", 72 | "value": "decbytes" 73 | } 74 | ] 75 | } 76 | ] 77 | }, 78 | "gridPos": { 79 | "h": 9, 80 | "w": 24, 81 | "x": 0, 82 | "y": 0 83 | }, 84 | "id": 30, 85 | "links": [], 86 | "options": { 87 | "colorMode": "value", 88 | "graphMode": "area", 89 | "justifyMode": "auto", 90 | "orientation": "auto", 91 | "reduceOptions": { 92 | "calcs": [ 93 | "lastNotNull" 94 | ], 95 | "fields": "", 96 | "values": false 97 | }, 98 | "textMode": "auto" 99 | }, 100 | "pluginVersion": "9.5.3", 101 | "targets": [ 102 | { 103 | "datasource": { 104 | "type": "prometheus", 105 | "uid": "prometheus" 106 | }, 107 | "editorMode": "code", 108 | "exemplar": false, 109 | "expr": "sum by(allowed_ip_0,friendly_name) (rate(wireguard_received_bytes_total{allowed_ip_0=~\"$ips\", public_key=~\".*$keys\", friendly_name=~\".*$names\"}[$__rate_interval])) > 0", 110 | "format": "time_series", 111 | "hide": false, 112 | "instant": false, 113 | "interval": "", 114 | "legendFormat": "{{allowed_ip_0}}: {{friendly_name}}", 115 | "range": true, 116 | "refId": "B" 117 | } 118 | ], 119 | "title": "Connected Clients (SI)", 120 | "type": "stat" 121 | }, 122 | { 123 | "datasource": { 124 | "type": "prometheus", 125 | "uid": "prometheus" 126 | }, 127 | "fieldConfig": { 128 | "defaults": { 129 | "color": { 130 | "mode": "palette-classic" 131 | }, 132 | "custom": { 133 | "axisCenteredZero": false, 134 | "axisColorMode": "text", 135 | "axisLabel": "", 136 | "axisPlacement": "auto", 137 | "barAlignment": 0, 138 | "drawStyle": "line", 139 | "fillOpacity": 6, 140 | "gradientMode": "none", 141 | "hideFrom": { 142 | "legend": false, 143 | "tooltip": false, 144 | "viz": false 145 | }, 146 | "lineInterpolation": "linear", 147 | "lineWidth": 1, 148 | "pointSize": 5, 149 | "scaleDistribution": { 150 | "log": 2, 151 | "type": "log" 152 | }, 153 | "showPoints": "auto", 154 | "spanNulls": false, 155 | "stacking": { 156 | "group": "A", 157 | "mode": "none" 158 | }, 159 | "thresholdsStyle": { 160 | "mode": "off" 161 | } 162 | }, 163 | "mappings": [], 164 | "thresholds": { 165 | "mode": "absolute", 166 | "steps": [ 167 | { 168 | "color": "green", 169 | "value": null 170 | }, 171 | { 172 | "color": "red", 173 | "value": 80 174 | } 175 | ] 176 | } 177 | }, 178 | "overrides": [ 179 | { 180 | "matcher": { 181 | "id": "byType", 182 | "options": "number" 183 | }, 184 | "properties": [ 185 | { 186 | "id": "unit", 187 | "value": "decbytes" 188 | } 189 | ] 190 | } 191 | ] 192 | }, 193 | "gridPos": { 194 | "h": 8, 195 | "w": 12, 196 | "x": 0, 197 | "y": 9 198 | }, 199 | "id": 18, 200 | "options": { 201 | "legend": { 202 | "calcs": [], 203 | "displayMode": "list", 204 | "placement": "bottom", 205 | "showLegend": true 206 | }, 207 | "tooltip": { 208 | "mode": "single", 209 | "sort": "none" 210 | } 211 | }, 212 | "targets": [ 213 | { 214 | "datasource": { 215 | "type": "prometheus", 216 | "uid": "prometheus" 217 | }, 218 | "editorMode": "code", 219 | "expr": "rate( wireguard_received_bytes_total{allowed_ip_0=~\"$ips\", public_key=~\".*$keys\", friendly_name=~\".*$names\"}[$__rate_interval]) >0", 220 | "legendFormat": "{{allowed_ip_0}}: {{friendly_name}}", 221 | "range": true, 222 | "refId": "A" 223 | } 224 | ], 225 | "title": "Received Bytes", 226 | "type": "timeseries" 227 | }, 228 | { 229 | "datasource": { 230 | "type": "prometheus", 231 | "uid": "prometheus" 232 | }, 233 | "fieldConfig": { 234 | "defaults": { 235 | "color": { 236 | "mode": "palette-classic" 237 | }, 238 | "custom": { 239 | "axisCenteredZero": false, 240 | "axisColorMode": "text", 241 | "axisLabel": "", 242 | "axisPlacement": "auto", 243 | "barAlignment": 0, 244 | "drawStyle": "line", 245 | "fillOpacity": 12, 246 | "gradientMode": "none", 247 | "hideFrom": { 248 | "legend": false, 249 | "tooltip": false, 250 | "viz": false 251 | }, 252 | "lineInterpolation": "linear", 253 | "lineStyle": { 254 | "fill": "solid" 255 | }, 256 | "lineWidth": 1, 257 | "pointSize": 5, 258 | "scaleDistribution": { 259 | "log": 2, 260 | "type": "log" 261 | }, 262 | "showPoints": "auto", 263 | "spanNulls": false, 264 | "stacking": { 265 | "group": "A", 266 | "mode": "none" 267 | }, 268 | "thresholdsStyle": { 269 | "mode": "off" 270 | } 271 | }, 272 | "mappings": [], 273 | "thresholds": { 274 | "mode": "absolute", 275 | "steps": [ 276 | { 277 | "color": "green", 278 | "value": null 279 | }, 280 | { 281 | "color": "red", 282 | "value": 80 283 | } 284 | ] 285 | } 286 | }, 287 | "overrides": [ 288 | { 289 | "matcher": { 290 | "id": "byType", 291 | "options": "number" 292 | }, 293 | "properties": [ 294 | { 295 | "id": "unit", 296 | "value": "decbytes" 297 | } 298 | ] 299 | } 300 | ] 301 | }, 302 | "gridPos": { 303 | "h": 8, 304 | "w": 12, 305 | "x": 12, 306 | "y": 9 307 | }, 308 | "id": 28, 309 | "options": { 310 | "legend": { 311 | "calcs": [], 312 | "displayMode": "list", 313 | "placement": "bottom", 314 | "showLegend": true 315 | }, 316 | "tooltip": { 317 | "mode": "single", 318 | "sort": "none" 319 | } 320 | }, 321 | "targets": [ 322 | { 323 | "datasource": { 324 | "type": "prometheus", 325 | "uid": "prometheus" 326 | }, 327 | "editorMode": "code", 328 | "expr": "rate(wireguard_sent_bytes_total{allowed_ip_0=~\"$ips\", public_key=~\".*$keys\", friendly_name=~\".*$names\"}[$__rate_interval]) > 0", 329 | "legendFormat": "{{allowed_ip_0}}: {{friendly_name}}", 330 | "range": true, 331 | "refId": "A" 332 | } 333 | ], 334 | "title": "Send Bytes", 335 | "type": "timeseries" 336 | }, 337 | { 338 | "datasource": { 339 | "type": "prometheus", 340 | "uid": "prometheus" 341 | }, 342 | "fieldConfig": { 343 | "defaults": { 344 | "color": { 345 | "mode": "continuous-GrYlRd" 346 | }, 347 | "custom": { 348 | "fillOpacity": 70, 349 | "lineWidth": 0, 350 | "spanNulls": false 351 | }, 352 | "mappings": [], 353 | "thresholds": { 354 | "mode": "absolute", 355 | "steps": [ 356 | { 357 | "color": "green", 358 | "value": null 359 | }, 360 | { 361 | "color": "red", 362 | "value": 80 363 | } 364 | ] 365 | } 366 | }, 367 | "overrides": [] 368 | }, 369 | "gridPos": { 370 | "h": 12, 371 | "w": 12, 372 | "x": 0, 373 | "y": 17 374 | }, 375 | "id": 8, 376 | "options": { 377 | "alignValue": "left", 378 | "legend": { 379 | "displayMode": "list", 380 | "placement": "bottom", 381 | "showLegend": true 382 | }, 383 | "mergeValues": true, 384 | "rowHeight": 0.9, 385 | "showValue": "auto", 386 | "tooltip": { 387 | "mode": "single", 388 | "sort": "none" 389 | } 390 | }, 391 | "pluginVersion": "9.2.1", 392 | "targets": [ 393 | { 394 | "datasource": { 395 | "type": "prometheus", 396 | "uid": "prometheus" 397 | }, 398 | "editorMode": "code", 399 | "exemplar": false, 400 | "expr": "rate(wireguard_latest_handshake_seconds{allowed_ip_0=~\"$ips\", public_key=~\".*$keys\", friendly_name=~\".*$names\"}[$__rate_interval]) > 0", 401 | "instant": false, 402 | "legendFormat": "{{allowed_ip_0}}: {{friendly_name}}", 403 | "range": true, 404 | "refId": "A" 405 | } 406 | ], 407 | "title": "Latest Handshakes (Seconds)", 408 | "type": "state-timeline" 409 | }, 410 | { 411 | "datasource": { 412 | "type": "prometheus", 413 | "uid": "prometheus" 414 | }, 415 | "fieldConfig": { 416 | "defaults": { 417 | "color": { 418 | "mode": "continuous-GrYlRd" 419 | }, 420 | "custom": { 421 | "fillOpacity": 70, 422 | "lineWidth": 0, 423 | "spanNulls": false 424 | }, 425 | "mappings": [], 426 | "thresholds": { 427 | "mode": "absolute", 428 | "steps": [ 429 | { 430 | "color": "green", 431 | "value": null 432 | }, 433 | { 434 | "color": "red", 435 | "value": 80 436 | } 437 | ] 438 | } 439 | }, 440 | "overrides": [] 441 | }, 442 | "gridPos": { 443 | "h": 12, 444 | "w": 12, 445 | "x": 12, 446 | "y": 17 447 | }, 448 | "id": 31, 449 | "options": { 450 | "alignValue": "left", 451 | "legend": { 452 | "displayMode": "list", 453 | "placement": "bottom", 454 | "showLegend": true 455 | }, 456 | "mergeValues": true, 457 | "rowHeight": 0.9, 458 | "showValue": "auto", 459 | "tooltip": { 460 | "mode": "single", 461 | "sort": "none" 462 | } 463 | }, 464 | "pluginVersion": "9.2.1", 465 | "targets": [ 466 | { 467 | "datasource": { 468 | "type": "prometheus", 469 | "uid": "prometheus" 470 | }, 471 | "editorMode": "code", 472 | "exemplar": false, 473 | "expr": "rate(wireguard_latest_handshake_delay_seconds{allowed_ip_0=~\"$ips\", public_key=~\".*$keys\", friendly_name=~\".*$names\"}[$__rate_interval]) > 0", 474 | "format": "time_series", 475 | "instant": false, 476 | "legendFormat": "{{allowed_ip_0}}: {{friendly_name}} {{pod}}", 477 | "range": true, 478 | "refId": "A" 479 | } 480 | ], 481 | "title": "Latest Handshake Delay (Seconds)", 482 | "type": "state-timeline" 483 | } 484 | ], 485 | "refresh": "5s", 486 | "schemaVersion": 38, 487 | "style": "dark", 488 | "tags": [], 489 | "templating": { 490 | "list": [ 491 | { 492 | "current": { 493 | "selected": true, 494 | "text": [ 495 | "All" 496 | ], 497 | "value": [ 498 | "$__all" 499 | ] 500 | }, 501 | "datasource": { 502 | "type": "prometheus", 503 | "uid": "prometheus" 504 | }, 505 | "definition": "label_values(wireguard_latest_handshake_seconds,allowed_ip_0)", 506 | "hide": 0, 507 | "includeAll": true, 508 | "label": "List of IPs", 509 | "multi": true, 510 | "name": "ips", 511 | "options": [], 512 | "query": { 513 | "query": "label_values(wireguard_latest_handshake_seconds,allowed_ip_0)", 514 | "refId": "PrometheusVariableQueryEditor-VariableQuery" 515 | }, 516 | "refresh": 1, 517 | "regex": "", 518 | "skipUrlSync": false, 519 | "sort": 0, 520 | "type": "query" 521 | }, 522 | { 523 | "current": { 524 | "selected": true, 525 | "text": [ 526 | "All" 527 | ], 528 | "value": [ 529 | "$__all" 530 | ] 531 | }, 532 | "datasource": { 533 | "type": "prometheus", 534 | "uid": "prometheus" 535 | }, 536 | "definition": "label_values(wireguard_latest_handshake_seconds{allowed_ip_0=~\"$ips\"},public_key)", 537 | "hide": 0, 538 | "includeAll": true, 539 | "label": "Public Keys", 540 | "multi": true, 541 | "name": "keys", 542 | "options": [], 543 | "query": { 544 | "query": "label_values(wireguard_latest_handshake_seconds{allowed_ip_0=~\"$ips\"},public_key)", 545 | "refId": "PrometheusVariableQueryEditor-VariableQuery" 546 | }, 547 | "refresh": 1, 548 | "regex": "", 549 | "skipUrlSync": false, 550 | "sort": 0, 551 | "type": "query" 552 | }, 553 | { 554 | "current": { 555 | "selected": true, 556 | "text": [ 557 | "All" 558 | ], 559 | "value": [ 560 | "$__all" 561 | ] 562 | }, 563 | "datasource": { 564 | "type": "prometheus", 565 | "uid": "prometheus" 566 | }, 567 | "definition": "label_values(wireguard_latest_handshake_seconds{allowed_ip_0=~\"$ips\"},friendly_name)", 568 | "hide": 0, 569 | "includeAll": true, 570 | "label": "Names", 571 | "multi": true, 572 | "name": "names", 573 | "options": [], 574 | "query": { 575 | "query": "label_values(wireguard_latest_handshake_seconds{allowed_ip_0=~\"$ips\"},friendly_name)", 576 | "refId": "PrometheusVariableQueryEditor-VariableQuery" 577 | }, 578 | "refresh": 1, 579 | "regex": "", 580 | "skipUrlSync": false, 581 | "sort": 0, 582 | "type": "query" 583 | } 584 | ] 585 | }, 586 | "time": { 587 | "from": "now-5m", 588 | "to": "now" 589 | }, 590 | "timepicker": {}, 591 | "timezone": "", 592 | "title": "Wireguard", 593 | "uid": "CJRCJWNVk", 594 | "version": 2, 595 | "weekStart": "" 596 | } 597 | -------------------------------------------------------------------------------- /helm/wireguard/scripts/gen-key.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | kubectl --namespace $RELEASE_NAMESPACE create secret generic $SECRET_NAME --from-literal=privatekey=$(wg genkey) 3 | -------------------------------------------------------------------------------- /helm/wireguard/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "wireguard.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "wireguard.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "wireguard.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Selector labels 35 | */}} 36 | {{- define "wireguard.selectorLabels" -}} 37 | app.kubernetes.io/name: {{ include "wireguard.name" . }} 38 | app.kubernetes.io/instance: {{ .Release.Name }} 39 | {{- end }} 40 | 41 | {{/* 42 | Common labels 43 | */}} 44 | {{- define "wireguard.labels" -}} 45 | helm.sh/chart: {{ include "wireguard.chart" . }} 46 | {{ include "wireguard.selectorLabels" . }} 47 | {{- if .Chart.AppVersion }} 48 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 49 | {{- end }} 50 | app.kubernetes.io/managed-by: {{ .Release.Service }} 51 | {{- end }} 52 | 53 | 54 | {{/* Seccomp profile partial */}} 55 | {{- define "wireguard.seccompProfile" -}} 56 | {{- if .Values.securityContext.seccompProfile }} 57 | seccompProfile: {{ .Values.securityContext.seccompProfile | toYaml | nindent 2}} 58 | {{- end }} 59 | {{- end -}} 60 | 61 | {{/* Runtime Class partial */}} 62 | {{- define "wireguard.runtimeClass" }} 63 | {{- if .Values.runtimeClassName }} 64 | runtimeClassName: "{{ .Values.runtimeClassName }}" 65 | {{- end }} 66 | {{- end }} -------------------------------------------------------------------------------- /helm/wireguard/templates/config.yaml: -------------------------------------------------------------------------------- 1 | {{- define "wg-config-template" -}} 2 | {{- $natSourceNetOption := .Values.wireguard.natAddSourceNet | ternary (printf "%s %s" "-s" .Values.wireguard.serverCidr) ("") -}} 3 | [Interface] 4 | Address = {{ .Values.wireguard.serverAddress }} 5 | ListenPort = 51820 6 | PostUp = wg set wg0 private-key /etc/wireguard/privatekey && iptables -t nat -A POSTROUTING {{ $natSourceNetOption }} -o eth0 -j MASQUERADE 7 | PostDown = iptables -t nat -D POSTROUTING -s {{ $natSourceNetOption }} -o eth0 -j MASQUERADE 8 | {{- range $key, $value := .Values.wireguard.interfaceOpts }} 9 | {{ $key }} = {{ $value }} 10 | {{- end }} 11 | # Clients 12 | {{- range .Values.wireguard.clients }} 13 | [Peer] 14 | {{- if .FriendlyName }} 15 | # friendly_name = {{ .FriendlyName }} 16 | {{- end }} 17 | {{- if .FriendlyJson }} 18 | # friendly_json = {{ .FriendlyJson | toJson }} 19 | {{- end }} 20 | PublicKey = {{ .PublicKey }} 21 | AllowedIPs = {{ .AllowedIPs }} 22 | {{- if .PresharedKey }} 23 | PresharedKey = {{ .PresharedKey }} 24 | {{- end }} 25 | {{- if .PersistentKeepalive }} 26 | PersistentKeepalive = {{ .PersistentKeepalive }} 27 | {{- end }} 28 | 29 | {{- end }} 30 | # End Clients 31 | {{- end -}} 32 | {{- if and (not .Values.configSecretName) (not .Values.disableConfigManagement) }} 33 | kind: Secret 34 | apiVersion: v1 35 | metadata: 36 | name: "{{ .Release.Name }}-wg-config" 37 | data: 38 | wg0.conf: {{ include "wg-config-template" . | b64enc }} 39 | {{- end }} 40 | -------------------------------------------------------------------------------- /helm/wireguard/templates/dashboard.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.metrics.enabled .Values.metrics.dashboard.enabled }} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: "{{ .Release.Name }}-dashboard" 6 | {{- if .Values.metrics.dashboard.labels }} 7 | labels: 8 | {{ toYaml .Values.metrics.dashboard.labels | indent 4 }} 9 | {{- end }} 10 | {{- if .Values.metrics.dashboard.annotations }} 11 | annotations: 12 | {{ toYaml .Values.metrics.dashboard.annotations | indent 4 }} 13 | {{- end }} 14 | data: 15 | wireguard.json: |- 16 | {{- .Files.Get "files/dashboard.json" | nindent 4 }} 17 | {{- end }} 18 | -------------------------------------------------------------------------------- /helm/wireguard/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- define "execprobe" -}} 2 | exec: 3 | command: 4 | - /bin/sh 5 | - -c 6 | - 'ip link show dev wg0 | grep -s up' 7 | {{- end -}} 8 | 9 | {{- define "core.securitycontext" -}} 10 | {{ include "wireguard.seccompProfile" . }} 11 | capabilities: 12 | drop: 13 | - ALL 14 | add: 15 | - NET_ADMIN 16 | - NET_RAW 17 | - SETUID 18 | - SETGID 19 | {{- end -}} 20 | 21 | {{- define "wg.securitycontext" -}} 22 | {{ include "core.securitycontext" . }} 23 | runAsNonRoot: {{ .Values.securityContext.runAsNonRoot | default true }} 24 | runAsUser: {{ .Values.securityContext.runAsUser | default 1000 }} 25 | readOnlyRootFilesystem: {{ .Values.securityContext.readOnlyRootFilesystem | default true }} 26 | allowPrivilegeEscalation: {{ .Values.securityContext.allowPrivilegeEscalation | default true }} 27 | privileged: {{ .Values.securityContext.privileged | default false }} 28 | {{- end -}} 29 | 30 | {{- define "init.securitycontext" -}} 31 | {{ include "core.securitycontext" . }} 32 | runAsNonRoot: false 33 | privileged: true 34 | {{- end -}} 35 | 36 | --- 37 | apiVersion: apps/v1 38 | {{- if .Values.daemonSet }} 39 | kind: DaemonSet 40 | {{- else }} 41 | kind: Deployment 42 | {{- end }} 43 | metadata: 44 | name: "{{ .Release.Name }}-wireguard" 45 | labels: 46 | role: vpn 47 | {{- if .Values.labels }} 48 | {{- range $key, $value := .Values.labels }} 49 | {{ $key }}: {{ $value | quote }} 50 | {{- end }} 51 | {{- end }} 52 | 53 | spec: 54 | {{- if and (not .Values.autoscaling.enabled) (not .Values.daemonSet) }} 55 | replicas: {{ .Values.replicaCount }} 56 | {{- end }} 57 | selector: 58 | matchLabels: 59 | app: "{{ .Release.Name }}-wireguard" 60 | {{- if not .Values.daemonSet }} 61 | {{- if eq .Values.deploymentStrategy.type "RollingUpdate" }} 62 | strategy: {{ .Values.deploymentStrategy | toYaml | nindent 4 }} 63 | {{- else }} 64 | strategy: 65 | type: {{ .Values.deploymentStrategy.type }} 66 | {{- end }} 67 | {{- end }} 68 | template: 69 | metadata: 70 | annotations: 71 | {{- if not .Values.configSecretName }} 72 | checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} 73 | {{- end }} 74 | {{- if .Values.podAnnotations }} 75 | {{- range $key, $value := .Values.podAnnotations }} 76 | {{ $key }}: {{ $value | quote }} 77 | {{- end }} 78 | {{- end }} 79 | labels: 80 | app: "{{ .Release.Name }}-wireguard" 81 | role: vpn 82 | {{- if .Values.labels }} 83 | {{- range $key, $value := .Values.labels }} 84 | {{ $key }}: {{ $value | quote }} 85 | {{- end }} 86 | {{- end }} 87 | spec: 88 | {{- include "wireguard.runtimeClass" . | indent 6 }} 89 | {{- if .Values.dnsPolicy }} 90 | dnsPolicy: {{ .Values.dnsPolicy }} 91 | {{- end }} 92 | {{- if .Values.dnsConfig }} 93 | dnsConfig: {{ .Values.dnsConfig | toYaml | nindent 8 }} 94 | {{- end }} 95 | serviceAccountName: {{ .Release.Name }}-sa 96 | topologySpreadConstraints: 97 | - maxSkew: 1 98 | topologyKey: kubernetes.io/hostname 99 | whenUnsatisfiable: ScheduleAnyway 100 | labelSelector: 101 | matchLabels: 102 | app: "{{ .Release.Name }}-wireguard" 103 | {{- if semverCompare ">=1.27-0" .Capabilities.KubeVersion.GitVersion }} 104 | matchLabelKeys: 105 | - pod-template-hash 106 | {{- end }} 107 | automountServiceAccountToken: {{ .Values.healthSideCar.enabled }} 108 | {{- if .Values.runPodOnHostNetwork }} 109 | hostNetwork: true 110 | {{- end }} 111 | securityContext: 112 | fsGroup: {{ .Values.securityContext.runAsUser | default 1000 }} 113 | fsGroupChangePolicy: "OnRootMismatch" 114 | runAsNonRoot: {{ .Values.securityContext.runAsNonRoot | default true }} 115 | {{- include "wireguard.seccompProfile" . | indent 8 }} 116 | {{- if .Values.image.pullSecret }} 117 | imagePullSecrets: 118 | - name: "{{ .Values.image.pullSecret }}" 119 | {{- end }} 120 | {{- if .Values.affinity }} 121 | affinity: 122 | {{- $affinity := .Values.affinity | toYaml }} 123 | {{ tpl $affinity . | nindent 8 | trim }} 124 | {{- end }} 125 | {{- if .Values.nodeSelector }} 126 | nodeSelector: {{ .Values.nodeSelector | toYaml | nindent 8 }} 127 | {{- end }} 128 | {{- if .Values.tolerations }} 129 | tolerations: {{ .Values.tolerations | toYaml | nindent 8 }} 130 | {{- end }} 131 | initContainers: 132 | - name: sysctls 133 | image: "{{ .Values.initContainer.image.repository }}:{{ .Values.initContainer.image.tag }}" 134 | command: 135 | - sh 136 | - -c 137 | - sysctl -w net.ipv4.ip_forward=1 && sysctl -w net.ipv4.conf.all.forwarding=1 138 | securityContext: {{ include "init.securitycontext" . | nindent 12 }} 139 | resources: {{ .Values.initContainer.resources | toYaml | nindent 12 }} 140 | containers: 141 | - name: wireguard 142 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 143 | imagePullPolicy: "{{ .Values.image.pullPolicy }}" 144 | ports: 145 | - containerPort: 51820 146 | protocol: UDP 147 | name: wireguard 148 | {{- if .Values.useHostPort }} 149 | hostPort: {{ .Values.hostPort }} 150 | {{- end }} 151 | env: 152 | - name: LOG_LEVEL 153 | value: {{ default "info" .Values.logLevel }} 154 | {{- range $key, $value := .Values.extraEnv }} 155 | - name: {{ $key }} 156 | value: {{ (tpl $value $) | quote }} 157 | {{- end }} 158 | {{- range $key, $value := .Values.extraEnvSecrets }} 159 | - name: {{ $key }} 160 | valueFrom: 161 | secretKeyRef: 162 | name: {{ tpl $value.secretName $ }} 163 | key: {{ tpl $value.secretPropertyName $ }} 164 | {{- end }} 165 | securityContext: {{ include "wg.securitycontext" . | nindent 12 }} 166 | resources: {{ .Values.resources | toYaml | nindent 12 }} 167 | startupProbe: {{ include "execprobe" . | nindent 12 }} 168 | periodSeconds: 2 169 | failureThreshold: 15 170 | readinessProbe: {{ include "execprobe" . | nindent 12 }} 171 | initialDelaySeconds: 5 172 | periodSeconds: 10 173 | livenessProbe: {{ include "execprobe" . | nindent 12 }} 174 | initialDelaySeconds: 20 175 | periodSeconds: 10 176 | volumeMounts: 177 | - name: run 178 | mountPath: /run 179 | {{- if not .Values.disableConfigManagement }} 180 | - name: config 181 | mountPath: /etc/wireguard/wg0.conf 182 | subPath: {{ .Values.configSecretProperty | quote }} 183 | {{- end }} 184 | {{- if not .Values.disablePrivateKeyManagement }} 185 | - name: privatekey 186 | mountPath: /etc/wireguard/privatekey 187 | subPath: privatekey 188 | {{- end }} 189 | {{- if .Values.volumeMounts }} 190 | {{- .Values.volumeMounts | toYaml | nindent 10 }} 191 | {{- end }} 192 | {{- if .Values.metrics.enabled }} 193 | - name: wireguard-exporter 194 | image: "{{ .Values.metrics.image.repository }}:{{ .Values.metrics.image.tag }}" 195 | imagePullPolicy: "{{ .Values.metrics.image.pullPolicy }}" 196 | args: 197 | - -a 198 | {{- if .Values.metrics.extraEnv.PROMETHEUS_WIREGUARD_EXPORTER_PREPEND_SUDO_ENABLED }} 199 | - "true" 200 | {{- else }} 201 | - "false" 202 | {{- end }} 203 | ports: 204 | - containerPort: {{ .Values.metrics.service.port }} 205 | protocol: TCP 206 | name: exporter 207 | env: 208 | {{- range $key, $value := .Values.metrics.extraEnv }} 209 | - name: {{ $key }} 210 | value: {{ (tpl $value $) | quote }} 211 | {{- end }} 212 | - name: PROMETHEUS_WIREGUARD_EXPORTER_PORT 213 | value: "{{ .Values.metrics.service.port }}" 214 | securityContext: {{ include "wg.securitycontext" . | nindent 12 }} 215 | resources: {{ .Values.metrics.resources | toYaml | nindent 12 }} 216 | startupProbe: 217 | httpGet: 218 | path: /metrics 219 | port: {{ .Values.metrics.service.port }} 220 | periodSeconds: 2 221 | failureThreshold: 15 222 | readinessProbe: 223 | httpGet: 224 | path: /metrics 225 | port: {{ .Values.metrics.service.port }} 226 | initialDelaySeconds: 5 227 | periodSeconds: 10 228 | livenessProbe: 229 | httpGet: 230 | path: /metrics 231 | port: {{ .Values.metrics.service.port }} 232 | initialDelaySeconds: 20 233 | periodSeconds: 10 234 | volumeMounts: 235 | - name: run 236 | mountPath: /run 237 | {{- if not .Values.disableConfigManagement }} 238 | - name: config 239 | mountPath: /etc/wireguard/wg0.conf 240 | subPath: {{ .Values.configSecretProperty | quote }} 241 | {{- end }} 242 | {{- if not .Values.disablePrivateKeyManagement }} 243 | - name: privatekey 244 | mountPath: /etc/wireguard/privatekey 245 | subPath: privatekey 246 | {{- end }} 247 | {{- if .Values.volumeMounts }} 248 | {{- .Values.volumeMounts | toYaml | nindent 10 }} 249 | {{- end }} 250 | {{- end }} 251 | {{- if .Values.healthSideCar.enabled }} 252 | - name: health 253 | image: "{{ .Values.healthSideCar.image.repository }}:{{ .Values.healthSideCar.image.tag }}" 254 | imagePullPolicy: "{{ .Values.healthSideCar.image.pullPolicy }}" 255 | resources: {{ .Values.healthSideCar.resources | toYaml | nindent 12 }} 256 | securityContext: {{ .Values.healthSideCar.securityContext | toYaml | nindent 12 }} 257 | ports: 258 | - containerPort: 3000 259 | protocol: TCP 260 | name: wg-health-ep 261 | {{- if .Values.healthSideCar.useHostPort }} 262 | hostPort: {{ .Values.healthSideCar.hostPort }} 263 | {{- end }} 264 | {{- end }} 265 | {{- if .Values.extraSideCars }} 266 | {{- .Values.extraSideCars | toYaml | nindent 8 }} 267 | {{- end }} 268 | volumes: 269 | - name: run 270 | emptyDir: {} 271 | {{- if not .Values.disableConfigManagement }} 272 | - name: config 273 | secret: 274 | secretName: "{{ coalesce .Values.configSecretName (printf "%s-wg-config" .Release.Name) }}" 275 | {{- end }} 276 | {{- if not .Values.disablePrivateKeyManagement }} 277 | - name: privatekey 278 | secret: 279 | secretName: "{{ coalesce .Values.secretName (printf "%s-wg-generated" .Release.Name) }}" 280 | {{- end }} 281 | {{- if .Values.volumes }} 282 | {{- .Values.volumes | toYaml | nindent 6 }} 283 | {{- end }} 284 | -------------------------------------------------------------------------------- /helm/wireguard/templates/extra-config-maps.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.extraConfigMaps }} 2 | {{- range .Values.extraConfigMaps }} 3 | --- 4 | apiVersion: v1 5 | kind: ConfigMap 6 | metadata: 7 | name: {{ .name }} 8 | namespace: {{ $.Release.Namespace }} 9 | data: {{ .data | toYaml | nindent 2 }} 10 | {{- end }} 11 | {{- end }} 12 | -------------------------------------------------------------------------------- /helm/wireguard/templates/extra-storage.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.extraStorage }} 2 | {{- range .Values.extraStorage }} 3 | --- 4 | apiVersion: v1 5 | kind: PersistentVolumeClaim 6 | metadata: 7 | name: {{ .name }} 8 | namespace: {{ $.Release.Namespace }} 9 | annotations: 10 | "helm.sh/resource-policy": keep 11 | spec: 12 | {{- if .storageClassName }} 13 | storageClassName: {{ .storageClassName }} 14 | {{- end }} 15 | {{- if .volumeName }} 16 | volumeName: {{ .volumeName }} 17 | {{- end }} 18 | accessModes: {{ .accessModes | toYaml | nindent 4 }} 19 | volumeMode: {{ .volumeMode }} 20 | resources: 21 | requests: 22 | storage: {{ .storage }} 23 | {{- end }} 24 | {{- end }} 25 | -------------------------------------------------------------------------------- /helm/wireguard/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ .Release.Name }}-wireguard 6 | spec: 7 | scaleTargetRef: 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | name: {{ .Release.Name }}-wireguard 11 | minReplicas: {{ .Values.autoscaling.minReplicas }} 12 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 13 | targetCPUUtilizationPercentage: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 14 | {{- end }} 15 | -------------------------------------------------------------------------------- /helm/wireguard/templates/pdb.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.disruptionBudget.enabled }} 2 | apiVersion: policy/v1 3 | kind: PodDisruptionBudget 4 | metadata: 5 | name: {{ .Release.Name }}-pdb 6 | spec: 7 | minAvailable: {{ .Values.disruptionBudget.minAvailable }} 8 | selector: 9 | matchLabels: 10 | app: "{{ .Release.Name }}-wireguard" 11 | {{- end }} 12 | -------------------------------------------------------------------------------- /helm/wireguard/templates/privatekey-gen-job.yaml: -------------------------------------------------------------------------------- 1 | {{- if and (not .Values.secretName) (not .Values.disablePrivateKeyManagement) }} 2 | {{- /* Only needed if a secret isn't provided */}} 3 | 4 | {{- /* Create role for the hook job so it can create a secret */}} 5 | {{- /* In plain terms this role will be able to create a secret with a certain name in the namespace*/}} 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: Role 9 | metadata: 10 | namespace: {{ .Release.Namespace }} 11 | name: {{ .Release.Name }}-secret-creator 12 | labels: 13 | {{- include "wireguard.labels" . | nindent 4 }} 14 | annotations: 15 | "helm.sh/hook": pre-install 16 | "helm.sh/hook-weight": "0" 17 | "helm.sh/resource-policy": delete 18 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" 19 | rules: 20 | - apiGroups: [""] 21 | resources: ["secrets"] 22 | verbs: ["create"] 23 | {{- /* Need a service account for the job/hook */}} 24 | --- 25 | apiVersion: v1 26 | kind: ServiceAccount 27 | metadata: 28 | namespace: {{ .Release.Namespace }} 29 | name: {{ .Release.Name }}-pre-install-job-sa 30 | labels: 31 | {{- include "wireguard.labels" . | nindent 4 }} 32 | annotations: 33 | "helm.sh/hook": pre-install 34 | "helm.sh/hook-weight": "0" 35 | "helm.sh/resource-policy": keep 36 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" 37 | {{- /* Need to bind the service account to the role */}} 38 | --- 39 | apiVersion: rbac.authorization.k8s.io/v1 40 | kind: RoleBinding 41 | metadata: 42 | name: {{ .Release.Name }}-hook-create-secret 43 | namespace: {{ .Release.Namespace }} 44 | labels: 45 | {{- include "wireguard.labels" . | nindent 4 }} 46 | annotations: 47 | "helm.sh/hook": pre-install 48 | "helm.sh/hook-weight": "1" 49 | "helm.sh/resource-policy": delete 50 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" 51 | subjects: 52 | - kind: ServiceAccount 53 | name: {{ .Release.Name }}-pre-install-job-sa 54 | namespace: {{ .Release.Namespace }} 55 | roleRef: 56 | kind: Role 57 | name: {{ .Release.Name }}-secret-creator 58 | apiGroup: rbac.authorization.k8s.io 59 | {{- /* A script to generate the private key and create the secret is required */}} 60 | {{- /* A config map with the script is used to mount into a generic alpine container with kubectl already installed */}} 61 | --- 62 | apiVersion: v1 63 | kind: ConfigMap 64 | metadata: 65 | name: {{ .Release.Name }}-wg-gen-scripts 66 | namespace: {{ .Release.Namespace }} 67 | labels: 68 | {{- include "wireguard.labels" . | nindent 4 }} 69 | annotations: 70 | "helm.sh/hook": pre-install 71 | "helm.sh/hook-weight": "1" 72 | "helm.sh/resource-policy": delete 73 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" 74 | data: 75 | {{ (.Files.Glob "scripts/*").AsConfig | indent 2 }} 76 | {{- if .Values.keygenJob.extraScripts }} 77 | {{- range $key, $value := .Values.keygenJob.extraScripts }} 78 | {{- $key | nindent 2 }}: | 79 | {{- $value | nindent 4 }} 80 | {{- end }} 81 | {{- end }} 82 | {{- /* Create a job that cleans up after 5 minutes that creates the secret since the user didn't provide one*/}} 83 | --- 84 | apiVersion: batch/v1 85 | kind: Job 86 | metadata: 87 | name: "{{ .Release.Name }}-wg-gen" 88 | namespace: "{{ .Release.Namespace }}" 89 | labels: 90 | {{- include "wireguard.labels" . | nindent 4 }} 91 | annotations: 92 | "helm.sh/hook": pre-install 93 | "helm.sh/hook-weight": "2" 94 | "helm.sh/resource-policy": delete 95 | "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded" 96 | spec: 97 | ttlSecondsAfterFinished: 60 98 | template: 99 | metadata: 100 | {{- if .Values.keygenJob.podAnnotations }} 101 | annotations: 102 | {{- range $key, $value := .Values.keygenJob.podAnnotations }} 103 | {{ $key }}: {{ $value | quote }} 104 | {{- end }} 105 | {{- end }} 106 | spec: 107 | {{- include "wireguard.runtimeClass" . | indent 6 }} 108 | serviceAccountName: {{ .Release.Name }}-pre-install-job-sa 109 | restartPolicy: Never 110 | securityContext: {{ .Values.keygenJob.podSecurityContext | toYaml | nindent 8 }} 111 | {{- with .Values.imagePullSecrets }} 112 | imagePullSecrets: 113 | {{- toYaml . | nindent 8 }} 114 | {{- end }} 115 | volumes: 116 | - name: script 117 | configMap: 118 | name: {{ .Release.Name }}-wg-gen-scripts 119 | items: 120 | - key: gen-key.sh 121 | path: entry-point.sh 122 | mode: 0755 123 | {{- range $key, $value := .Values.keygenJob.extraScripts }} 124 | - key: {{ $key }} 125 | path: {{ $key }} 126 | mode: 0755 127 | {{- end }} 128 | containers: 129 | - name: keygen-job 130 | {{- if not .Values.keygenJob.useWireguardManager }} 131 | volumeMounts: 132 | - name: script 133 | mountPath: /job/ 134 | image: "{{ .Values.keygenJob.image.repository }}:{{ .Values.keygenJob.image.tag }}" 135 | imagePullPolicy: "{{ .Values.keygenJob.image.pullPolicy }}" 136 | {{- else }} 137 | image: "{{ .Values.keygenJob.wireguardMgrImage.repository }}:{{ .Values.keygenJob.wireguardMgrImage.tag }}" 138 | imagePullPolicy: "{{ .Values.keygenJob.wireguardMgrImage.pullPolicy }}" 139 | {{- end }} 140 | securityContext: {{ .Values.keygenJob.containerSecurityContext | toYaml | nindent 10 }} 141 | resources: {{ .Values.keygenJob.resources | toYaml | nindent 10 }} 142 | env: 143 | {{- if .Values.keygenJob.useWireguardManager }} 144 | - name: K8S_WG_MGR_SERVER_SECRET_NAME 145 | {{- else }} 146 | - name: SECRET_NAME 147 | {{- end }} 148 | value: "{{ .Release.Name }}-wg-generated" 149 | - name: RELEASE_NAMESPACE 150 | value: "{{ .Release.Namespace }}" 151 | - name: RELEASE_NAME 152 | value: "{{ .Release.Name }}" 153 | {{- range $key, $value := .Values.keygenJob.extraEnv }} 154 | - name: {{ $key }} 155 | value: {{ tpl $value $ | quote }} 156 | {{- end }} 157 | {{- range $key, $value := .Values.keygenJob.extraEnvSecrets }} 158 | - name: {{ $key }} 159 | valueFrom: 160 | secretKeyRef: 161 | name: {{ tpl $value.secretName $ }} 162 | key: {{ tpl $value.secretPropertyName $ }} 163 | {{- end }} 164 | {{- if not .Values.keygenJob.useWireguardManager }} 165 | command: {{ .Values.keygenJob.command | toYaml | nindent 10}} 166 | {{- end }} 167 | {{- end }} 168 | -------------------------------------------------------------------------------- /helm/wireguard/templates/prometheusrule.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled .Values.metrics.prometheusRule.groups }} 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: PrometheusRule 4 | metadata: 5 | name: {{ .Release.Name }}-wireguard 6 | {{- if .Values.metrics.serviceMonitor.namespace }} 7 | namespace: {{ .Values.metrics.serviceMonitor.namespace }} 8 | {{- end }} 9 | labels: 10 | app: "{{ .Release.Name }}-wireguard" 11 | {{- if .Values.metrics.serviceMonitor.labels }} 12 | {{- toYaml .Values.metrics.serviceMonitor.labels | nindent 4 }} 13 | {{- end }} 14 | {{- if .Values.metrics.serviceMonitor.annotations }} 15 | annotations: 16 | {{- toYaml .Values.metrics.serviceMonitor.annotations | nindent 4 }} 17 | {{- end }} 18 | spec: 19 | groups: {{- toYaml .Values.metrics.prometheusRule.groups | nindent 4 }} 20 | {{- end }} 21 | -------------------------------------------------------------------------------- /helm/wireguard/templates/sa.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ .Release.Name }}-sa 6 | automountServiceAccountToken: false 7 | {{- if .Values.healthSideCar.enabled }} 8 | --- 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: Role 11 | metadata: 12 | name: {{ .Release.Name }}-pod-reader 13 | rules: 14 | - apiGroups: [""] 15 | resources: ["pods"] 16 | verbs: ["get"] 17 | --- 18 | apiVersion: rbac.authorization.k8s.io/v1 19 | kind: RoleBinding 20 | metadata: 21 | name: {{ .Release.Name }}-read-pods 22 | subjects: 23 | - kind: ServiceAccount 24 | name: {{ .Release.Name }}-sa 25 | roleRef: 26 | kind: Role 27 | name: {{ .Release.Name }}-pod-reader 28 | apiGroup: rbac.authorization.k8s.io 29 | {{- end }} 30 | -------------------------------------------------------------------------------- /helm/wireguard/templates/service-exporter.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.metrics.enabled }} 2 | kind: Service 3 | apiVersion: v1 4 | metadata: 5 | name: "{{ .Release.Name }}-exporter-wireguard" 6 | labels: 7 | app: "{{ .Release.Name }}-exporter-wireguard" 8 | app.kubernetes.io/component: metrics 9 | {{- if .Values.metrics.service.labels }} 10 | {{- toYaml .Values.metrics.service.labels | nindent 4 }} 11 | {{- end }} 12 | {{- if .Values.metrics.service.annotations }} 13 | annotations: 14 | {{- toYaml .Values.metrics.service.annotations | nindent 4 }} 15 | {{- end }} 16 | spec: 17 | type: ClusterIP 18 | clusterIP: None 19 | ports: 20 | - name: exporter 21 | protocol: TCP 22 | port: {{ .Values.metrics.service.port }} 23 | targetPort: {{ .Values.metrics.service.port }} 24 | selector: 25 | app: "{{ .Release.Name }}-wireguard" 26 | {{- end }} 27 | -------------------------------------------------------------------------------- /helm/wireguard/templates/service-health-endpoint.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.healthSideCar.enabled .Values.healthSideCar.service.enabled }} 2 | kind: Service 3 | apiVersion: v1 4 | metadata: 5 | name: "{{ .Release.Name }}-healthcheck" 6 | labels: 7 | app: "{{ .Release.Name }}-healthcheck" 8 | app.kubernetes.io/component: health 9 | {{- if .Values.healthSideCar.service.labels }} 10 | {{- toYaml .Values.healthSideCar.service.labels | nindent 4 }} 11 | {{- end }} 12 | {{- if .Values.healthSideCar.service.annotations }} 13 | annotations: 14 | {{- toYaml .Values.healthSideCar.service.annotations | nindent 4 }} 15 | {{- end }} 16 | spec: 17 | type: {{ .Values.healthSideCar.service.type }} 18 | ports: 19 | - name: 20 | protocol: TCP 21 | port: {{ .Values.healthSideCar.service.port }} 22 | targetPort: 3000 23 | {{- if eq .Values.healthSideCar.service.type "NodePort" }} 24 | nodePort: {{ .Values.healthSideCar.service.nodePort }} 25 | {{- end }} 26 | selector: 27 | app: "{{ .Release.Name }}-wireguard" 28 | {{- if and .Values.healthSideCar.service.loadBalancerClass (semverCompare ">=1.24-0" .Capabilities.KubeVersion.Version) }} 29 | loadBalancerClass: {{ .Values.healthSideCar.service.loadBalancerClass }} 30 | {{- end }} 31 | {{- end }} 32 | -------------------------------------------------------------------------------- /helm/wireguard/templates/service.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.service.enabled }} 2 | kind: Service 3 | apiVersion: v1 4 | metadata: 5 | name: "{{ .Release.Name }}-wireguard" 6 | labels: 7 | app: "{{ .Release.Name }}-wireguard" 8 | {{- if .Values.service.annotations }} 9 | annotations: 10 | {{- toYaml .Values.service.annotations | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | type: {{ .Values.service.type }} 14 | ports: 15 | - name: wg 16 | protocol: UDP 17 | port: {{ .Values.service.port }} 18 | targetPort: 51820 19 | {{- if .Values.service.type | eq "NodePort" }} 20 | nodePort: {{ .Values.service.nodePort }} 21 | {{- end }} 22 | {{- if .Values.service.extraPorts }} 23 | {{- .Values.service.extraPorts | toYaml | nindent 4 }} 24 | {{- end }} 25 | selector: 26 | app: "{{ .Release.Name }}-wireguard" 27 | {{- if .Values.service.externalTrafficPolicy }} 28 | externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} 29 | {{- end }} 30 | {{- if .Values.service.loadBalancerIP }} 31 | loadBalancerIP: {{ .Values.service.loadBalancerIP }} 32 | {{- end }} 33 | {{- if and .Values.service.loadBalancerClass (semverCompare ">=1.24-0" .Capabilities.KubeVersion.Version) }} 34 | loadBalancerClass: {{ .Values.service.loadBalancerClass }} 35 | {{- end }} 36 | {{- end }} -------------------------------------------------------------------------------- /helm/wireguard/templates/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | name: "{{ .Release.Name }}-wireguard" 6 | {{- if .Values.metrics.serviceMonitor.namespace }} 7 | namespace: {{ .Values.metrics.serviceMonitor.namespace }} 8 | {{- end }} 9 | labels: 10 | app: "{{ .Release.Name }}-wireguard" 11 | {{- if .Values.metrics.serviceMonitor.labels }} 12 | {{- toYaml .Values.metrics.serviceMonitor.labels | nindent 4 }} 13 | {{- end }} 14 | {{- if .Values.metrics.serviceMonitor.annotations }} 15 | annotations: 16 | {{- toYaml .Values.metrics.serviceMonitor.annotations | nindent 4 }} 17 | {{- end }} 18 | spec: 19 | {{- if .Values.metrics.serviceMonitor.jobLabel }} 20 | jobLabel: {{ .Values.metrics.serviceMonitor.jobLabel }} 21 | {{- end }} 22 | endpoints: 23 | - port: exporter 24 | path: {{ .Values.metrics.serviceMonitor.path }} 25 | {{- if .Values.metrics.serviceMonitor.interval }} 26 | interval: {{ .Values.metrics.serviceMonitor.interval }} 27 | {{- end }} 28 | {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} 29 | scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} 30 | {{- end }} 31 | {{- if .Values.metrics.serviceMonitor.relabelings }} 32 | relabelings: {{ toYaml .Values.metrics.serviceMonitor.relabelings | nindent 8 }} 33 | {{- end }} 34 | {{- if .Values.metrics.serviceMonitor.metricRelabelings }} 35 | metricRelabelings: {{ toYaml .Values.metrics.serviceMonitor.metricRelabelings | nindent 8 }} 36 | {{- end }} 37 | {{- if .Values.metrics.serviceMonitor.honorLabels }} 38 | honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} 39 | {{- end }} 40 | namespaceSelector: 41 | matchNames: 42 | - {{ .Release.Namespace | quote }} 43 | selector: 44 | matchLabels: 45 | app: "{{ .Release.Name }}-exporter-wireguard" 46 | app.kubernetes.io/component: metrics 47 | {{- end }} 48 | -------------------------------------------------------------------------------- /helm/wireguard/templates/tests/client-connect.yaml: -------------------------------------------------------------------------------- 1 | # kics-scan ignore 2 | --- 3 | apiVersion: batch/v1 4 | kind: Job 5 | metadata: 6 | name: wireguard-client-test 7 | namespace: {{ .Release.Namespace }} 8 | annotations: 9 | helm.sh/hook: test 10 | spec: 11 | template: 12 | spec: 13 | automountServiceAccountToken: false 14 | initContainers: 15 | - name: sysctls 16 | image: busybox:stable 17 | command: 18 | - sh 19 | - -c 20 | - sysctl -w net.ipv4.ip_forward=1 && sysctl -w net.ipv4.conf.all.forwarding=1 21 | securityContext: 22 | runAsNonRoot: false 23 | privileged: true 24 | resources: 25 | requests: 26 | memory: 64Mi 27 | cpu: "100m" 28 | limits: 29 | memory: 64Mi 30 | cpu: "100m" 31 | containers: 32 | - name: wireguard-client 33 | image: ghcr.io/bryopsida/wireguard:main 34 | securityContext: 35 | runAsNonRoot: {{ .Values.securityContext.runAsNonRoot | default true }} 36 | runAsUser: {{ .Values.securityContext.runAsUser | default 1000 }} 37 | readOnlyRootFilesystem: {{ .Values.securityContext.readOnlyRootFilesystem | default true }} 38 | allowPrivilegeEscalation: {{ .Values.securityContext.allowPrivilegeEscalation | default true }} 39 | privileged: {{ .Values.securityContext.privileged | default false }} 40 | capabilities: 41 | drop: 42 | - ALL 43 | add: 44 | - NET_ADMIN 45 | - NET_RAW 46 | - SETUID 47 | - SETGID 48 | env: 49 | - name: WG_PRIVATE_KEY 50 | value: "gILClOGWPR+w9q4f0A7hlTKdQHC0vHwXPrGLHPKJi2o=" 51 | - name: WG_SERVER 52 | value: "{{ .Release.Name }}-wireguard.{{ .Release.Namespace }}.svc" 53 | - name: WG_SERVER_PORT 54 | value: "{{ .Values.service.port }}" 55 | - name: WG_SERVER_CIDR 56 | value: "{{ .Values.wireguard.serverCidr }}" 57 | - name: WG_CLIENT_IP 58 | value: "172.32.32.2/32" 59 | resources: 60 | requests: 61 | cpu: "100m" 62 | memory: "128Mi" 63 | limits: 64 | cpu: "500m" 65 | memory: "256Mi" 66 | command: 67 | - /bin/sh 68 | args: 69 | - -c 70 | - | 71 | set -e 72 | 73 | cat < /tmp/wg0.conf 74 | [Interface] 75 | PrivateKey = ${WG_PRIVATE_KEY} 76 | Address = ${WG_CLIENT_IP} 77 | 78 | [Peer] 79 | PublicKey = $(cat /etc/wireguard-server/privatekey | wg pubkey) 80 | Endpoint = ${WG_SERVER}:${WG_SERVER_PORT} 81 | AllowedIPs = ${WG_SERVER_CIDR} 82 | EOF 83 | 84 | wg-quick up /tmp/wg0.conf 85 | sleep 5 86 | sudo ping -c 1 -W 1 172.32.32.1 87 | volumeMounts: 88 | - name: wireguard-server-secret 89 | mountPath: /etc/wireguard-server/ 90 | readOnly: true 91 | - name: tmp-volume 92 | mountPath: /tmp 93 | volumes: 94 | - name: wireguard-server-secret 95 | secret: 96 | secretName: {{ .Release.Name }}-wg-generated 97 | - name: tmp-volume 98 | emptyDir: {} 99 | restartPolicy: Never 100 | backoffLimit: 4 101 | -------------------------------------------------------------------------------- /helm/wireguard/values.yaml: -------------------------------------------------------------------------------- 1 | # -- Run as a DaemonSet instead of Deployment 2 | daemonSet: false 3 | image: 4 | repository: ghcr.io/bryopsida/wireguard 5 | tag: main 6 | pullPolicy: Always 7 | 8 | initContainer: 9 | image: 10 | repository: busybox 11 | tag: latest 12 | resources: 13 | requests: 14 | memory: 64Mi 15 | cpu: "100m" 16 | ephemeral-storage: 8Mi 17 | limits: 18 | memory: 64Mi 19 | cpu: "100m" 20 | ephemeral-storage: 64Mi 21 | keygenJob: 22 | # -- when enabled, uses a image with go bindings for k8s and wg to create the secret if it does not exist, on re-runs it leaves the existing secret in place and exits succesfully 23 | useWireguardManager: true 24 | # -- When useWireguardManager is enabled this image is used instead of the kubectl image 25 | wireguardMgrImage: 26 | repository: ghcr.io/bryopsida/k8s-wireguard-mgr 27 | tag: main 28 | pullPolicy: Always 29 | image: 30 | repository: ghcr.io/curium-rocks/wg-kubectl 31 | tag: latest 32 | pullPolicy: Always 33 | podAnnotations: {} 34 | podSecurityContext: 35 | seccompProfile: 36 | type: RuntimeDefault 37 | fsGroup: 1000 38 | fsGroupChangePolicy: Always 39 | containerSecurityContext: 40 | runAsUser: 1000 41 | runAsGroup: 1000 42 | runAsNonRoot: true 43 | privileged: false 44 | allowPrivilegeEscalation: false 45 | readOnlyRootFilesystem: true 46 | resources: 47 | requests: 48 | memory: 256Mi 49 | cpu: "100m" 50 | ephemeral-storage: 8Mi 51 | limits: 52 | memory: 256Mi 53 | cpu: "100m" 54 | ephemeral-storage: 128Mi 55 | # -- Specify the script to run to generate the private key 56 | command: ["/job/entry-point.sh"] 57 | # -- Inject additional scripts into the key generation job 58 | extraScripts: {} 59 | # -- Add additional environment variables to the key generation job, supports helm templating 60 | extraEnv: {} 61 | podAnnotations: {} 62 | labels: {} 63 | # -- Run pod on host network 64 | runPodOnHostNetwork: false 65 | # -- Expose VPN service on hostPort 66 | useHostPort: false 67 | # -- Host port to expose the VPN service on 68 | hostPort: 51820 69 | wireguard: 70 | # -- Address of the VPN server 71 | serverAddress: 10.34.0.1/24 72 | # -- Subnet for your VPN, take care not to clash with cluster POD cidr 73 | serverCidr: 10.34.0.0/24 74 | # -- Add the serverCidr to the nat source net option 75 | natAddSourceNet: true 76 | # -- A collection of extraopts for wireguard interface 77 | interfaceOpts: {} 78 | # -- A collection of clients that will be added to wg0.conf, accepts objects with keys PublicKey and AllowedIPs (mandatory) and optional FriendlyName or FriendlyJson (https://github.com/MindFlavor/prometheus_wireguard_exporter#friendly-tags) and PersistentKeepalive (https://www.wireguard.com/quickstart/#nat-and-firewall-traversal-persistence), stored in secret 79 | clients: [] 80 | # - FriendlyName: username1 81 | # ## FriendlyJson will override FriendlyName 82 | # # FriendlyJson: 83 | # # username: "username1" 84 | # AllowedIPs: 10.34.0.101/32 85 | # PublicKey: QTxoajwVHWZ7qqVwY2F9T1L04M0j5GSNC15++LZw1iA= 86 | # # Normally PersistentKeepalive is not required 87 | # #PersistentKeepalive: 25 88 | securityContext: 89 | runAsNonRoot: true 90 | runAsUser: 1000 91 | readOnlyRootFilesystem: true 92 | allowPrivilegeEscalation: true 93 | privileged: false 94 | service: 95 | # -- Whether the service will be created or not 96 | enabled: true 97 | # -- Service type, to keep internal to cluster use ClusterIP or NodePort 98 | type: LoadBalancer 99 | # -- Service port, default is 51820 UDP 100 | port: 51820 101 | # -- Node port, only valid with service type: NodePort 102 | nodePort: 31820 103 | # -- External Traffic Policy for the service 104 | externalTrafficPolicy: "" 105 | # -- IP to assign to the LoadBalancer service 106 | loadBalancerIP: "" 107 | # -- Annotations 108 | annotations: {} 109 | # -- Extra ports that can be attached to the service object, these are passed directly to the port array on the service and must be well formed to the specification 110 | extraPorts: [] 111 | # -- loadBalancerClass for Service Controllers that support it 112 | loadBalancerClass: "" 113 | # -- Name of a secret with a wireguard private key on key privatekey, if not provided on first install a hook generates one. 114 | secretName: ~ 115 | replicaCount: 3 116 | resources: 117 | requests: 118 | memory: 256Mi 119 | cpu: "100m" 120 | ephemeral-storage: 8Mi 121 | limits: 122 | memory: 256Mi 123 | cpu: "100m" 124 | ephemeral-storage: 128Mi 125 | # -- Override the default runtime class of the container, if not provided `runc` will most likely be used 126 | runtimeClassName: ~ 127 | deploymentStrategy: 128 | type: "RollingUpdate" 129 | rollingUpdate: 130 | maxUnavailable: 0 131 | maxSurge: 1 132 | disruptionBudget: 133 | enabled: true 134 | minAvailable: 2 135 | autoscaling: 136 | enabled: true 137 | minReplicas: 3 138 | maxReplicas: 10 139 | targetCPUUtilizationPercentage: 75 140 | # -- Provide additional environment variables to the wireguard container 141 | extraEnv: {} 142 | # TEST_ENV_VAR: test-value 143 | # -- Provide additional sidecars to the wireguard pod, these are directly attached to the pod and must be well formed ContainerSpec 144 | extraSideCars: [] 145 | # -- Create storage claims that can be used by side cars 146 | extraStorage: [] 147 | # - name: conf 148 | # storageClassName: default 149 | # storage: 8Gi 150 | # accessModes: 151 | # - ReadWriteMany 152 | # volumeMode: Filesystem 153 | # -- Create additional configmaps that may be used in sidecars 154 | extraConfigMaps: [] 155 | # - name: some-config 156 | # data: 157 | # key1: | 158 | # some config file data 159 | # -- If provided, this secret will be used instead of the config created from the helm value scope 160 | configSecretName: ~ 161 | # -- The property/key on the secret holding the wireguard configuration file 162 | configSecretProperty: wg0.conf 163 | # -- Disable creation and any mounting of a private key, this assumes another mechanism is provided/used at the container level to fetch the private key 164 | disablePrivateKeyManagement: false 165 | # -- Disable creation and any mount of the wireguard confifugration file, this assumes another mechanism is provided/used to manage a configuration file 166 | disableConfigManagement: false 167 | # -- Passthrough pod volumes 168 | volumes: {} 169 | # -- Passthrough pod volume mounts 170 | volumeMounts: {} 171 | # -- Set pod affinity or antiAffinity 172 | affinity: 173 | # nodeAffinity: 174 | # requiredDuringSchedulingIgnoredDuringExecution: 175 | # nodeSelectorTerms: 176 | # - matchExpressions: 177 | # - key: "example.com/vpn" 178 | # operator: Exists 179 | podAntiAffinity: 180 | requiredDuringSchedulingIgnoredDuringExecution: 181 | - labelSelector: 182 | matchLabels: 183 | app: "{{ .Release.Name }}-wireguard" 184 | role: vpn 185 | topologyKey: kubernetes.io/hostname 186 | # -- Set pod nodeSelector, a simplified version of affinity 187 | nodeSelector: {} 188 | # example.com/vpn: "" 189 | # -- Set pod tolerations 190 | tolerations: [] 191 | # - effect: NoSchedule 192 | # operator: Exists 193 | 194 | ## Metrics configuration 195 | metrics: 196 | # -- Enable exposing Wireguard metrics 197 | enabled: false 198 | # -- Wireguard Exporter image 199 | image: 200 | repository: docker.io/mindflavor/prometheus-wireguard-exporter 201 | tag: 3.6.6 202 | pullPolicy: IfNotPresent 203 | resources: 204 | requests: 205 | memory: 256Mi 206 | cpu: "100m" 207 | ephemeral-storage: 8Mi 208 | limits: 209 | memory: 256Mi 210 | cpu: "100m" 211 | ephemeral-storage: 128Mi 212 | # @params -- Wireguard Exporter environment variables. See https://mindflavor.github.io/prometheus_wireguard_exporter 213 | extraEnv: 214 | # -- Enable verbose mode 215 | PROMETHEUS_WIREGUARD_EXPORTER_VERBOSE_ENABLED: "false" 216 | # -- Prepends sudo to wg commands 217 | PROMETHEUS_WIREGUARD_EXPORTER_PREPEND_SUDO_ENABLED: "false" 218 | # -- Specify the service address. This is the address your Prometheus instance should point to 219 | PROMETHEUS_WIREGUARD_EXPORTER_ADDRESS: "0.0.0.0" 220 | # -- This flag adds the friendly_name attribute or the friendly_json attributes to the exported entries. See [Friendly tags](https://mindflavor.github.io/prometheus_wireguard_exporter/#friendly-tags) for more details. Multiple files are allowed (they will be merged as a single file in memory so avoid duplicates) 221 | PROMETHEUS_WIREGUARD_EXPORTER_CONFIG_FILE_NAMES: "/etc/wireguard/{{ .Values.configSecretProperty }}" 222 | # -- Enable the allowed ip + subnet split mode for the labels 223 | PROMETHEUS_WIREGUARD_EXPORTER_SEPARATE_ALLOWED_IPS_ENABLED: "true" 224 | # -- Exports peer’s remote ip and port as labels (if available) 225 | PROMETHEUS_WIREGUARD_EXPORTER_EXPORT_REMOTE_IP_AND_PORT_ENABLED: "true" 226 | # -- Specifies the interface(s) passed to the wg show dump parameter. Multiple parameters are allowed 227 | PROMETHEUS_WIREGUARD_EXPORTER_INTERFACES: "all" 228 | # -- Adds the wireguard_latest_handshake_delay_seconds metric that automatically calculates the seconds passed since the last handshake 229 | EXPORT_LATEST_HANDSHAKE_DELAY: "true" 230 | ## Wireguard metrics service parameters 231 | service: 232 | # -- Metrics service HTTP port 233 | port: 9586 234 | # -- Additional service labels 235 | labels: {} 236 | # -- Annotations for enabling prometheus to access the metrics endpoints 237 | annotations: {} 238 | ## Prometheus Operator ServiceMonitor configuration 239 | serviceMonitor: 240 | # -- Create ServiceMonitor Resource for scraping metrics using PrometheusOperator 241 | enabled: true 242 | # -- Metrics service HTTP port 243 | port: exporter 244 | # -- The endpoint configuration of the ServiceMonitor. Path is mandatory. Interval, timeout and relabelings can be overwritten. 245 | path: "/metrics" 246 | # -- Namespace of the ServiceMonitor. If empty, current namespace is used 247 | namespace: "" 248 | # -- Interval at which metrics should be scraped 249 | interval: 30s 250 | # -- Specify the timeout after which the scrape is ended 251 | # e.g: 252 | # scrapeTimeout: 30s 253 | scrapeTimeout: "" 254 | # -- Additional labels that can be used so ServiceMonitor will be discovered by Prometheus 255 | labels: {} 256 | # -- Annotations 257 | annotations: {} 258 | # -- Prometheus instance selector labels 259 | # ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration 260 | selector: {} 261 | # -- RelabelConfigs to apply to samples before scraping 262 | relabelings: [] 263 | # -- MetricRelabelConfigs to apply to samples before ingestion 264 | metricRelabelings: [] 265 | # -- honorLabels chooses the metric's labels on collisions with target labels 266 | honorLabels: false 267 | # -- The name of the label on the target service to use as the job name in prometheus. 268 | jobLabel: "" 269 | ## Prometheus Operator alert rules configuration 270 | prometheusRule: 271 | # -- Create PrometheusRule Resource for scraping metrics using PrometheusOperator 272 | enabled: false 273 | # -- Namespace of the ServiceMonitor. If empty, current namespace is used 274 | namespace: "" 275 | # -- Additional labels that can be used so PrometheusRule will be discovered by Prometheus 276 | labels: {} 277 | # -- Annotations 278 | annotations: {} 279 | # -- Groups, containing the alert rules. 280 | # Example: 281 | # groups: 282 | # - name: Wireguard 283 | # rules: 284 | # - alert: WireguardInstanceNotAvailable 285 | # annotations: 286 | # message: "Wireguard instance in namespace {{ `{{` }} $labels.namespace {{ `}}` }} has not been available for the last 5 minutes." 287 | # expr: | 288 | # absent(kube_pod_status_ready{namespace="{{ include "common.names.namespace" . }}", condition="true"} * on (pod) kube_pod_labels{pod=~"{{ include "common.names.fullname" . }}-\\d+", namespace="{{ include "common.names.namespace" . }}"}) != 0 289 | # for: 5m 290 | # labels: 291 | # severity: critical 292 | groups: [] 293 | dashboard: 294 | # -- Create a ConfigMap with a Grafana dashboard 295 | enabled: true 296 | # -- Grafana dashboard labels 297 | labels: 298 | grafana_dashboard: "1" 299 | # -- Grafana dashboard annotations 300 | annotations: {} 301 | # k8s-sidecar-target-directory: /tmp/dashboards/Other 302 | ## Health sidecar configuration 303 | healthSideCar: 304 | # -- Opt in side car to expose a http health end point for external load balancers that are not kubernetes aware, in most cases this is not needed 305 | enabled: false 306 | # -- Secure settings by default, can be overriden to reduce security posture if needed 307 | securityContext: 308 | capabilities: 309 | drop: 310 | - ALL 311 | runAsGroup: 10001 312 | runAsUser: 10001 313 | seccompProfile: 314 | type: RuntimeDefault 315 | runAsNonRoot: true 316 | readOnlyRootFilesystem: true 317 | allowPrivilegeEscalation: false 318 | # -- set resource constraints, set to nil to remove 319 | resources: 320 | requests: 321 | memory: 256Mi 322 | cpu: "100m" 323 | ephemeral-storage: 8Mi 324 | limits: 325 | memory: 256Mi 326 | cpu: "100m" 327 | ephemeral-storage: 256Mi 328 | image: 329 | # -- Override repo if you prefer to use your own image 330 | repository: ghcr.io/bryopsida/http-healthcheck-sidecar 331 | # -- Rolling tag used by default to take patches automatically 332 | tag: main 333 | # -- Pull Policy always to avoid cached rolling tags, if you change this you should use a non rolling tag 334 | pullPolicy: Always 335 | service: 336 | # -- Override service port if needed 337 | port: 3000 338 | # -- Toggle to enable the service, if the pod is a daemonset healthSideCar.useHostPort can be used instead 339 | enabled: true 340 | # -- Service type, given the use case, in most cases this should be NodePort 341 | type: NodePort 342 | # -- The port for the service exposed on each node 343 | nodePort: 31313 344 | # -- When enabled the container will define a host port, in most cases this should only be used when deploying with daemonSet: true 345 | useHostPort: false 346 | # -- When useHostPort is true this is the host port defined 347 | hostPort: 13000 348 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | --------------------------------------------------------------------------------