This commit is contained in:
Zakaria 2024-08-23 23:39:15 +00:00
commit 44e1fca7c4
42 changed files with 2124 additions and 0 deletions

View File

@ -0,0 +1,41 @@
name: Release Charts
on:
push:
branches:
- main
jobs:
release:
runs-on: [self-hosted, Linux, X64]
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: v3.14.0
# https://github.com/helm/chart-releaser-action/issues/74
- name: Add repositories
run: |
for dir in $(ls -d charts/*/); do
helm dependency list $dir 2> /dev/null | tail +2 | head -n -1 | awk '{ print "helm repo add " $1 " " $3 }' | while read cmd; do $cmd; done
done
- name: Run chart-releaser
uses: helm/chart-releaser-action@v1.6.0
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
with:
charts_dir: charts
skip_existing: true

View File

@ -0,0 +1,46 @@
name: Test setup script
on:
push:
branches:
- main
paths:
- 'docker-compose/**'
- '.github/workflows/test.yml'
jobs:
test:
name: Test setup script
runs-on: [self-hosted, Linux]
steps:
- name: Login to GitHub container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create working directory
run: mkdir temp
- name: Run setup script
env:
DEFGUARD_DOMAIN: "id.localhost"
DEFGUARD_ENROLLMENT_DOMAIN: "enrollment.localhost"
DEFGUARD_VPN_NAME: "test_location"
DEFGUARD_VPN_IP: "10.0.60.1/24"
DEFGUARD_VPN_GATEWAY_IP: "10.20.20.40"
DEFGUARD_VPN_GATEWAY_PORT: "50050"
CORE_IMAGE_TAG: latest
PROXY_IMAGE_TAG: latest
GATEWAY_IMAGE_TAG: latest
working-directory: temp
run: curl --proto '=https' --tlsv1.2 -sSf -L https://raw.githubusercontent.com/DefGuard/deployment/main/docker-compose/setup.sh | bash -s - --non-interactive
- name: Sleep for 10 seconds
working-directory: temp
run: sleep 10s
- name: Test defguard is available
working-directory: temp
run: curl -f http://id.localhost/api/v1/health
- name: Stop compose stack
if: always()
working-directory: temp
run: docker-compose down

3
defguardDocker/deployment/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
docker-compose/.env
docker-compose/.volumes
.idea

View File

@ -0,0 +1,13 @@
Copyright 2023 teonite ventures sp. z o.o. (teonite)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,16 @@
<p align="center">
<img src="docs/header.png" alt="defguard">
</p>
# Defguard deployment
Check our [documentation](https://defguard.gitbook.io/defguard/features/setting-up-your-instance) for deployment
instructions.
## Community and Support
Find us on Matrix: [#defguard:teonite.com](https://matrix.to/#/#defguard:teonite.com)
## Contribution
Please review the [Contributing guide](https://defguard.gitbook.io/defguard/for-developers/contributing) for information on how to get started contributing to the project. You might also find our [environment setup guide](https://defguard.gitbook.io/defguard/for-developers/dev-env-setup) handy.

View File

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@ -0,0 +1,7 @@
apiVersion: v2
name: defguard-proxy
description: Defguard proxy is a public-facing proxy for core defguard service
type: application
version: 0.3.5
appVersion: 0.5.0

View File

@ -0,0 +1,20 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}/
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "defguard-proxy.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "defguard-proxy.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "defguard-proxy.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "defguard-proxy.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "defguard-proxy.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "defguard-proxy.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "defguard-proxy.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "defguard-proxy.labels" -}}
helm.sh/chart: {{ include "defguard-proxy.chart" . }}
{{ include "defguard-proxy.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "defguard-proxy.selectorLabels" -}}
app.kubernetes.io/name: {{ include "defguard-proxy.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "defguard-proxy.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "defguard-proxy.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "defguard-proxy.fullname" . }}-config
labels:
{{- include "defguard-proxy.labels" . | nindent 4 }}
data:
DEFGUARD_PROXY_HTTP_PORT: {{ .Values.service.ports.http | quote }}
DEFGUARD_PROXY_GRPC_PORT: {{ .Values.service.ports.grpc | quote }}

View File

@ -0,0 +1,67 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "defguard-proxy.fullname" . }}
labels:
{{- include "defguard-proxy.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "defguard-proxy.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "defguard-proxy.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "defguard-proxy.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
envFrom:
- configMapRef:
name: {{ include "defguard-proxy.fullname" . }}-config
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.ports.http }}
protocol: TCP
- name: grpc
containerPort: {{ .Values.service.ports.grpc }}
protocol: TCP
livenessProbe:
httpGet:
path: /api/v1/health
port: http
readinessProbe:
httpGet:
path: /api/v1/health
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
annotations:
traefik.ingress.kubernetes.io/service.serversscheme: h2c
name: {{ include "defguard-proxy.fullname" . }}-grpc
labels:
{{- include "defguard-proxy.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.ports.grpc }}
targetPort: grpc
protocol: TCP
name: grpc
selector:
{{- include "defguard-proxy.selectorLabels" . | nindent 4 }}

View File

@ -0,0 +1,52 @@
{{- if .Values.ingress.grpc.enabled -}}
{{- $fullName := include "defguard-proxy.fullname" . -}}
{{- if and .Values.ingress.grpc.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.grpc.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.grpc.annotations "kubernetes.io/ingress.class" .Values.ingress.grpc.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}-grpc
labels:
{{- include "defguard-proxy.labels" . | nindent 4 }}
{{- with .Values.ingress.grpc.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.grpc.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.grpc.className }}
{{- end }}
{{- if .Values.ingress.grpc.tls }}
tls:
- hosts:
- {{ .Values.ingress.grpc.host | quote }}
secretName: {{ printf "%s-grpc-tls" .Values.ingress.grpc.host }}
{{- end }}
rules:
- host: {{ .Values.ingress.grpc.host | quote }}
http:
paths:
- path: /
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }}
pathType: ImplementationSpecific
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}-grpc
port:
number: {{ .Values.service.ports.grpc }}
{{- else }}
serviceName: {{ $fullName }}-grpc
servicePort: {{ .Values.service.ports.grpc }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,52 @@
{{- if .Values.ingress.web.enabled -}}
{{- $fullName := include "defguard-proxy.fullname" . -}}
{{- if and .Values.ingress.web.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.web.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.web.annotations "kubernetes.io/ingress.class" .Values.ingress.web.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}-web
labels:
{{- include "defguard-proxy.labels" . | nindent 4 }}
{{- with .Values.ingress.web.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.web.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.web.className }}
{{- end }}
{{- if .Values.ingress.web.tls }}
tls:
- hosts:
- {{ .Values.ingress.web.host | quote }}
secretName: {{ printf "%s-web-tls" .Values.ingress.web.host }}
{{- end }}
rules:
- host: {{ .Values.ingress.web.host | quote }}
http:
paths:
- path: /
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }}
pathType: ImplementationSpecific
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}-web
port:
number: {{ .Values.service.ports.http }}
{{- else }}
serviceName: {{ $fullName }}-web
servicePort: {{ .Values.service.ports.http }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "defguard-proxy.fullname" . }}-web
labels:
{{- include "defguard-proxy.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.ports.http }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "defguard-proxy.selectorLabels" . | nindent 4 }}

View File

@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "defguard-proxy.serviceAccountName" . }}
labels:
{{- include "defguard-proxy.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,42 @@
affinity: {}
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
fullnameOverride: ""
image:
pullPolicy: IfNotPresent
repository: ghcr.io/defguard/defguard-proxy
tag: ""
imagePullSecrets: []
ingress:
grpc:
annotations: {}
className: ""
enabled: true
host: enrollment-grpc.local
tls: false
web:
annotations: {}
className: ""
enabled: true
host: enrollment.local
tls: false
nameOverride: ""
nodeSelector: {}
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
publicUrl: "http://enrollment.local"
replicaCount: 1
resources: {}
securityContext: {}
service:
ports:
http: 8080
grpc: 50051
type: ClusterIP
serviceAccount:
annotations: {}
create: true
tolerations: []

View File

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@ -0,0 +1,9 @@
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 12.12.10
- name: defguard-proxy
repository: https://defguard.github.io/deployment
version: 0.3.5
digest: sha256:de930b480616cfa369caf7b1447c5b3e729fce3e17994717ab0f64aa02c027e7
generated: "2024-07-26T09:00:54.309522115+02:00"

View File

@ -0,0 +1,17 @@
apiVersion: v2
name: defguard
description: Defguard is an open-source enterprise wireGuard VPN with MFA and SSO
type: application
version: 0.7.6
appVersion: 0.11.0
dependencies:
- name: postgresql
condition: postgresql.enabled
version: 12.12.10
repository: https://charts.bitnami.com/bitnami
- name: defguard-proxy
condition: defguard-proxy.enabled
version: 0.3.5
repository: https://defguard.github.io/deployment

View File

@ -0,0 +1,20 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}/
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "defguard.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "defguard.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "defguard.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "defguard.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@ -0,0 +1,78 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "defguard.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "defguard.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "defguard.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "defguard.labels" -}}
helm.sh/chart: {{ include "defguard.chart" . }}
{{ include "defguard.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "defguard.selectorLabels" -}}
app.kubernetes.io/name: {{ include "defguard.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "defguard.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "defguard.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Define OpenID secret name
*/}}
{{- define "defguard.openidSecretName" -}}
{{- $name := "openid-key" }}
{{- $name }}
{{- end }}
{{/*
Define JWT secret name
*/}}
{{- define "defguard.jwtSecretName" -}}
{{- $name := "jwt-secrets" }}
{{- $name }}
{{- end }}

View File

@ -0,0 +1,30 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "defguard.fullname" . }}-config
labels:
{{- include "defguard.labels" . | nindent 4 }}
data:
{{- if .Values.cookie.domain }}
DEFGUARD_COOKIE_DOMAIN: {{ .Values.cookie.domain }}
{{- end }}
DEFGUARD_COOKIE_INSECURE: {{ .Values.cookie.insecure | quote }}
DEFGUARD_DB_HOST: {{ .Values.postgresql.host | default (printf "%s-postgresql" (include "defguard.fullname" .)) }}
DEFGUARD_DB_PORT: {{ .Values.postgresql.port | quote}}
DEFGUARD_DB_NAME: {{ .Values.postgresql.auth.database }}
DEFGUARD_DB_USER: {{ .Values.postgresql.auth.username }}
DEFGUARD_GRPC_PORT: {{ .Values.service.ports.grpc | quote }}
DEFGUARD_ENROLLMENT_URL: {{ index .Values "defguard-proxy" "publicUrl" }}
{{- if .Values.proxyUrl }}
DEFGUARD_PROXY_URL: {{ .Values.proxyUrl }}
{{- end }}
DEFGUARD_URL: {{ .Values.publicUrl }}
DEFGUARD_WEBAUTHN_RP_ID: {{ .Values.ingress.web.host }}
{{- if .Values.ldap.enabled }}
DEFGUARD_LDAP_ADMIN_GROUP: {{ .Values.ldap.admin_group | quote }}
DEFGUARD_LDAP_BIND_PASSWORD: {{ .Values.ldap.bind_password | quote }}
DEFGUARD_LDAP_BIND_USERNAME: {{ .Values.ldap.bind_username | quote }}
DEFGUARD_LDAP_GROUP_SEARCH_BASE: {{ .Values.ldap.group_search_base | quote }}
DEFGUARD_LDAP_USER_SEARCH_BASE: {{ .Values.ldap.user_search_base | quote }}
DEFGUARD_LDAP_URL: {{ .Values.ldap.url | quote }}
{{- end }}

View File

@ -0,0 +1,105 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "defguard.fullname" . }}
labels:
{{- include "defguard.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "defguard.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "defguard.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "defguard.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
env:
- name: DEFGUARD_DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.postgresql.auth.existingSecret }}
key: {{ .Values.postgresql.auth.existingSecretPasswordKey | default "password" }}
- name: DEFGUARD_AUTH_SECRET
valueFrom:
secretKeyRef:
name: {{ .Values.existingJwtSecret | default (include "defguard.jwtSecretName" .) }}
key: auth
- name: DEFGUARD_GATEWAY_SECRET
valueFrom:
secretKeyRef:
name: {{ .Values.existingJwtSecret | default (include "defguard.jwtSecretName" .) }}
key: gateway
- name: DEFGUARD_YUBIBRIDGE_SECRET
valueFrom:
secretKeyRef:
name: {{ .Values.existingJwtSecret | default (include "defguard.jwtSecretName" .) }}
key: yubi-bridge
- name: DEFGUARD_SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.existingJwtSecret | default (include "defguard.jwtSecretName" .) }}
key: secret-key
- name: DEFGUARD_OPENID_KEY
value: "/etc/defguard-openid-key.pem"
envFrom:
- configMapRef:
name: {{ include "defguard.fullname" . }}-config
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8000
protocol: TCP
- name: grpc
containerPort: 50055
protocol: TCP
livenessProbe:
httpGet:
path: /api/v1/health
port: http
readinessProbe:
httpGet:
path: /api/v1/health
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
- name: openid-key
mountPath: "/etc/defguard-openid-key.pem"
readOnly: true
subPath: openid-key
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
- name: openid-key
secret:
secretName: {{ .Values.existingOpenIdSecret | default (include "defguard.openidSecretName" .) }}
optional: false

View File

@ -0,0 +1,25 @@
{{ if not .Values.existingJwtSecret }}
{{- $auth := (randAlpha 16) | b64enc | quote }}
{{- $gateway := (randAlpha 16) | b64enc | quote }}
{{- $yubiBridge := (randAlpha 16) | b64enc | quote }}
{{- $secretKey := (randAlpha 64) | b64enc | quote }}
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (include "defguard.jwtSecretName" .)) }}
{{- if $secret }}
{{- $auth = index $secret.data "auth" }}
{{- $gateway = index $secret.data "gateway" }}
{{- $yubiBridge = index $secret.data "yubi-bridge" }}
{{- $secretKey = index $secret.data "secret-key" }}
{{- end }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "defguard.jwtSecretName" . }}
labels:
{{- include "defguard.labels" . | nindent 4 }}
type: Opaque
data:
auth: {{ $auth }}
gateway: {{ $gateway }}
yubi-bridge: {{ $yubiBridge }}
secret-key: {{ $secretKey }}
{{- end }}

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "defguard.fullname" . }}-web
labels:
{{- include "defguard.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.ports.http }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "defguard.selectorLabels" . | nindent 4 }}

View File

@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
annotations:
traefik.ingress.kubernetes.io/service.serversscheme: h2c
name: {{ include "defguard.fullname" . }}-grpc
labels:
{{- include "defguard.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.ports.grpc }}
targetPort: grpc
protocol: TCP
name: grpc
selector:
{{- include "defguard.selectorLabels" . | nindent 4 }}

View File

@ -0,0 +1,52 @@
{{- if .Values.ingress.grpc.enabled -}}
{{- $fullName := include "defguard.fullname" . -}}
{{- if and .Values.ingress.grpc.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.grpc.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.grpc.annotations "kubernetes.io/ingress.class" .Values.ingress.grpc.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}-grpc
labels:
{{- include "defguard.labels" . | nindent 4 }}
{{- with .Values.ingress.grpc.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.grpc.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.grpc.className }}
{{- end }}
{{- if .Values.ingress.grpc.tls }}
tls:
- hosts:
- {{ .Values.ingress.grpc.host | quote }}
secretName: {{ printf "%s-grpc-tls" .Values.ingress.grpc.host }}
{{- end }}
rules:
- host: {{ .Values.ingress.grpc.host | quote }}
http:
paths:
- path: /
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }}
pathType: ImplementationSpecific
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}-grpc
port:
number: {{ .Values.service.ports.grpc }}
{{- else }}
serviceName: {{ $fullName }}-grpc
servicePort: {{ .Values.service.ports.grpc }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,52 @@
{{- if .Values.ingress.web.enabled -}}
{{- $fullName := include "defguard.fullname" . -}}
{{- if and .Values.ingress.web.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.web.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.web.annotations "kubernetes.io/ingress.class" .Values.ingress.web.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}-web
labels:
{{- include "defguard.labels" . | nindent 4 }}
{{- with .Values.ingress.web.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.web.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.web.className }}
{{- end }}
{{- if .Values.ingress.web.tls }}
tls:
- hosts:
- {{ .Values.ingress.web.host | quote }}
secretName: {{ printf "%s-web-tls" .Values.ingress.web.host }}
{{- end }}
rules:
- host: {{ .Values.ingress.web.host | quote }}
http:
paths:
- path: /
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }}
pathType: ImplementationSpecific
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}-web
port:
number: {{ .Values.service.ports.http }}
{{- else }}
serviceName: {{ $fullName }}-web
servicePort: {{ .Values.service.ports.http }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,16 @@
{{ if not .Values.existingOpenIdSecret }}
{{- $openIdKey := (genPrivateKey "rsa") | b64enc | quote }}
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (include "defguard.openidSecretName" .)) }}
{{- if $secret }}
{{- $openIdKey = index $secret.data "openid-key" }}
{{- end }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "defguard.openidSecretName" . }}
labels:
{{- include "defguard.labels" . | nindent 4 }}
type: Opaque
data:
openid-key: {{ $openIdKey }}
{{- end }}

View File

@ -0,0 +1,19 @@
{{ if .Values.postgresql.enabled }}
{{- $password := (randAlpha 16) | b64enc | quote }}
{{- $postgresPassword := (randAlpha 16) | b64enc | quote }}
{{- $secret := (lookup "v1" "Secret" .Release.Namespace .Values.postgresql.auth.existingSecret) }}
{{- if $secret }}
{{- $password = index $secret.data "password" }}
{{- $postgresPassword = index $secret.data "postgres-password" }}
{{- end }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.postgresql.auth.existingSecret }}
labels:
{{- include "defguard.labels" . | nindent 4 }}
type: Opaque
data:
password: {{ $password }}
postgres-password: {{ $postgresPassword }}
{{- end }}

View File

@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "defguard.serviceAccountName" . }}
labels:
{{- include "defguard.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,75 @@
affinity: {}
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
cookie:
domain: ""
insecure: false
fullnameOverride: ""
image:
pullPolicy: IfNotPresent
repository: ghcr.io/defguard/defguard
tag: ""
imagePullSecrets: []
ingress:
grpc:
annotations: {}
className: ""
enabled: true
host: defguard-grpc.local
tls: false
web:
annotations: {}
className: ""
enabled: true
host: defguard.local
tls: false
existingJwtSecret: ""
ldap:
admin_group: ""
bind_password: ""
bind_username: ""
enabled: false
group_search_base: ""
url: ""
user_search_base: ""
nameOverride: ""
nodeSelector: {}
existingOpenIdSecret: ""
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
# sub-chart bitnami/postgresql
postgresql:
enabled: true
host: "" # set if using external postgresql ~ enabled: false
port: 5432
auth:
database: defguard
existingSecret: postgres-password
existingSecretPasswordKey: "" # set if using external postgresql ~ enabled: false
username: defguard
proxyUrl: ""
publicUrl: "http://defguard.local"
replicaCount: 1
resources: {}
securityContext: {}
service:
ports:
grpc: 50055
http: 80
type: ClusterIP
serviceAccount:
annotations: {}
create: true
tolerations: []
# sub-chart defguard-proxy
defguard-proxy:
enabled: false
publicUrl: "http://enrollment.local"
ingress:
grpc:
host: defguard-proxy-grpc.local
web:
host: enrollment.local

View File

@ -0,0 +1,31 @@
# The best way to define each secret is to generate random strings with e.g.:
#
# openssl rand -base64 48 #this will generate a 48chars random string
#
# Please provide secret strings (do not share them) for:
#
# Secret used for JWT cryptography
DEFGUARD_AUTH_SECRET=<YOUR_AUTH_SECRET>
# Secret used for JWT cryptography in YubiBridge GRPC communication
DEFGUARD_YUBIBRIDGE_SECRET=<DEFGUARD_YUBIBRIDGE_SECRET>
# Secret used for JWT cryptography in gateway GRPC communication
DEFGUARD_GATEWAY_SECRET=<DEFGUARD_GATEWAY_SECRET>
# Secret used for private cookies cryptography; must be at least 64 characters long
DEFGUARD_SECRET_KEY=<DEFGUARD_SECRET_KEY>
# Database password
DEFGUARD_DB_PASSWORD=<YOUR_DB_PASSWORD>
# Public URL of your Defguard instance
# E.g.: https://defguard.mycompany.com
DEFGUARD_URL=<YOUR_DEFGUARD_URL>
# Webauthn RP ID (https://w3c.github.io/webauthn/#rp-id)
# E.g.: defguard.mycompany.com (without http/https)
DEFGUARD_WEBAUTHN_RP_ID=<YOUR_DEFGUARD_WEBAUTHN_RP_ID>
# Public URL of your defguard proxy gRPC server
# DEFGUARD_PROXY_URL=<YOUR_PROXY_GRPC_URL>
# Public URL of your enrollment service
# E.g.: https://enrollment.mycompany.com
# DEFGUARD_ENROLLMENT_URL=<YOUR_PROXY_URL> # [ENROLLMENT]
# Token used for VPN gateway authorization
# DEFGUARD_TOKEN=<GATEWAY_TOKEN> # [VPN]
# Enable insecure cookies when not using HTTPS
# DEFGUARD_COOKIE_INSECURE=true # [HTTP]

View File

@ -0,0 +1,110 @@
services:
db:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_DB: defguard
POSTGRES_USER: defguard
POSTGRES_PASSWORD: ${DEFGUARD_DB_PASSWORD}
volumes:
- ${VOLUME_DIR:-./.volumes}/db:/var/lib/postgresql/data
# ports:
# - "5432:5432"
caddy: # [PROXY]
image: caddy:2.7-alpine # [PROXY]
restart: unless-stopped # [PROXY]
volumes: # [PROXY]
- ${VOLUME_DIR:-./.volumes}/caddy/data:/data # [PROXY]
- ${VOLUME_DIR:-./.volumes}/caddy/config:/config # [PROXY]
- ${VOLUME_DIR:-./.volumes}/caddy/Caddyfile:/etc/caddy/Caddyfile # [PROXY]
ports: # [PROXY]
#http
- "8002:80" # [PROXY]
#https
- "6443:443" # [PROXY]
core:
image: ghcr.io/defguard/defguard:${CORE_IMAGE_TAG:-latest}
restart: unless-stopped
environment:
DEFGUARD_AUTH_SECRET: ${DEFGUARD_AUTH_SECRET}
DEFGUARD_GATEWAY_SECRET: ${DEFGUARD_GATEWAY_SECRET}
DEFGUARD_YUBIBRIDGE_SECRET: ${DEFGUARD_YUBIBRIDGE_SECRET}
DEFGUARD_SECRET_KEY: ${DEFGUARD_SECRET_KEY}
DEFGUARD_DEFAULT_ADMIN_PASSWORD: ${DEFGUARD_DEFAULT_ADMIN_PASSWORD}
DEFGUARD_DB_HOST: db
DEFGUARD_DB_PORT: 5432
DEFGUARD_DB_USER: defguard
DEFGUARD_DB_PASSWORD: ${DEFGUARD_DB_PASSWORD}
DEFGUARD_DB_NAME: defguard
DEFGUARD_URL: ${DEFGUARD_URL}
DEFGUARD_LOG_LEVEL: info
DEFGUARD_WEBAUTHN_RP_ID: ${DEFGUARD_WEBAUTHN_RP_ID}
DEFGUARD_COOKIE_INSECURE: ${DEFGUARD_COOKIE_INSECURE:-false}
DEFGUARD_ENROLLMENT_URL: ${DEFGUARD_ENROLLMENT_URL} # [ENROLLMENT]
DEFGUARD_PROXY_URL: https://proxy:50052 # [ENROLLMENT]
DEFGUARD_PROXY_GRPC_CA: /ssl/defguard-ca.pem # [ENROLLMENT]
DEFGUARD_GRPC_CERT: /ssl/defguard-grpc.crt
DEFGUARD_GRPC_KEY: /ssl/defguard-grpc.key
## RSA setup guide: https://defguard.gitbook.io/defguard/community-features/setting-up-your-instance/docker-compose#openid-rsa-setup
DEFGUARD_OPENID_KEY: /keys/rsakey.pem
## LDAP setup guide: https://defguard.gitbook.io/defguard/features/ldap-synchronization-setup
# DEFGUARD_LDAP_URL: ldap://localhost:389 # [LDAP]
# DEFGUARD_LDAP_BIND_USERNAME: cn=admin,dc=example,dc=org # [LDAP]
# DEFGUARD_LDAP_BIND_PASSWORD: password # [LDAP]
ports:
# web
- "9876:8000"
# grpc
- "50055:50055"
depends_on:
- db
volumes:
# SSL setup guide: https://defguard.gitbook.io/defguard/features/setting-up-your-instance/docker-compose#ssl-setup
- ${VOLUME_DIR:-./.volumes}/ssl:/ssl
## RSA setup guide: https://defguard.gitbook.io/defguard/community-features/setting-up-your-instance/docker-compose#openid-rsa-setup
- ${VOLUME_DIR:-./.volumes}/core/rsakey.pem:/keys/rsakey.pem
proxy: # [ENROLLMENT]
image: ghcr.io/defguard/defguard-proxy:${PROXY_IMAGE_TAG:-latest} # [ENROLLMENT]
restart: unless-stopped # [ENROLLMENT]
environment: # [ENROLLMENT]
DEFGUARD_PROXY_GRPC_PORT: 50052 # [ENROLLMENT]
DEFGUARD_PROXY_GRPC_CERT: /ssl/defguard-proxy-grpc.crt # [ENROLLMENT]
DEFGUARD_PROXY_GRPC_KEY: /ssl/defguard-proxy-grpc.key # [ENROLLMENT]
volumes: # [ENROLLMENT]
#SSL setup guide: https://defguard.gitbook.io/defguard/features/setting-up-your-instance/docker-compose#ssl-setup
- ${VOLUME_DIR:-./.volumes}/ssl:/ssl # [ENROLLMENT]
ports:
# web
- "8588:8080"
depends_on: # [ENROLLMENT]
- core # [ENROLLMENT]
gateway: # [VPN]
image: ghcr.io/defguard/gateway:${GATEWAY_IMAGE_TAG:-latest} # [VPN]
restart: unless-stopped # [VPN]
network_mode: "host" # [VPN]
environment: # [VPN]
DEFGUARD_GRPC_URL: https://localhost:50055 # [VPN]
DEFGUARD_GRPC_CA: /ssl/defguard-ca.pem # [VPN]
DEFGUARD_STATS_PERIOD: 30 # [VPN]
DEFGUARD_TOKEN: ${DEFGUARD_TOKEN} # [VPN]
volumes: # [VPN]
- ${VOLUME_DIR:-./.volumes}/ssl:/ssl # [VPN]
cap_add: # [VPN]
- NET_ADMIN # [VPN]
gateway-OpenVPN: # [VPN]
image: ghcr.io/defguard/gateway:${GATEWAY_IMAGE_TAG:-latest} # [VPN]
restart: unless-stopped # [VPN]
network_mode: "host" # [VPN]
environment: # [VPN]
DEFGUARD_GRPC_URL: https://localhost:50055 # [VPN]
DEFGUARD_GRPC_CA: /ssl/defguard-ca.pem # [VPN]
DEFGUARD_STATS_PERIOD: 30 # [VPN]
DEFGUARD_TOKEN: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJEZWZHdWFyZCIsInN1YiI6IkRFRkdVQVJELU5FVFdPUkstNSIsImNsaWVudF9pZCI6IjUiLCJleHAiOjYwMTkyNjE4NDMsIm5iZiI6MTcyNDI5NDU0OH0.cV9dgj0B7hjT7LaLqbZ0sp8u-Fl71X13mnDppFXaD4E
volumes: # [VPN]
- ${VOLUME_DIR:-./.volumes}/ssl:/ssl # [VPN]
cap_add: # [VPN]
- NET_ADMIN # [VPN]

View File

@ -0,0 +1,890 @@
#!/usr/bin/env bash
# shellcheck shell=bash
# This is a script that sets up an entire defguard instance (including core,
# gateway, enrollment proxy and reverse proxy). It's goal is to prepare
# a working instance by running a single command.
set -o errexit # abort on nonzero exitstatus
set -o pipefail # don't hide errors within pipes
# Global variables
VERSION="1.0.2"
SECRET_LENGTH=64
PASSWORD_LENGTH=16
VOLUME_DIR=".volumes"
SSL_DIR="${VOLUME_DIR}/ssl"
RSA_DIR="${VOLUME_DIR}/core"
COMPOSE_FILE="docker-compose.yaml"
ENV_FILE=".env"
LOG_FILE=$(mktemp setup.log.XXXXXX)
BASE_COMPOSE_FILE_URL="https://raw.githubusercontent.com/DefGuard/deployment/main/docker-compose/docker-compose.yaml"
BASE_ENV_FILE_URL="https://raw.githubusercontent.com/DefGuard/deployment/main/docker-compose/.env.template"
CORE_IMAGE_TAG="${CORE_IMAGE_TAG:-latest}"
GATEWAY_IMAGE_TAG="${GATEWAY_IMAGE_TAG:-latest}"
PROXY_IMAGE_TAG="${PROXY_IMAGE_TAG:-latest}"
#####################
### MAIN FUNCTION ###
#####################
main() {
is_utf_term
is_term_color
tput reset
print_header
# display help `--help` argument is found
for i in $*; do
test "$i" == "--help" && print_usage && exit 0
# run non interactive
if [[ "$i" == "--non-interactive" ]]; then
CFG_NON_INTERACTIVE=1
# we need to remove this element from $* or getopt will return an error
set -- $(remove_element "$i" $*)
fi
# configure https
if [[ "$i" == "--use-https" ]]; then
CFG_USE_HTTPS=1
# we need to remove this element from $* or getopt will return an error
set -- $(remove_element "$i" $*)
fi
done
#
# First let's gather the ENV/command line variables
#
# load configuration from env variables
load_configuration_from_env
# load configuration from CLI options
load_configuration_from_cli "$@"
# load configuration from user inputs
if [ X$CFG_VOLUME_DIR != X ]; then
VOLUME_DIR=${CFG_VOLUME_DIR}
SSL_DIR="${VOLUME_DIR}/ssl"
RSA_DIR="${VOLUME_DIR}/core"
fi
export VOLUME_DIR
# We have enough to check the enviromnent
# so check if necessary tools are available
check_environment
# load configuration from user inputs
if ! [ $CFG_NON_INTERACTIVE ]; then
load_configuration_from_input
fi
# check that all required configuration options are set
validate_required_variables
# generate external service URLs based on config
generate_external_urls
# print out config
print_config
# set current working directory
WORK_DIR_PATH=$(pwd)
# setup RSA & SSL keys
setup_keys
# generate caddyfile
create_caddyfile
# generate `.env` file
generate_env_file
# enable insecure cookies if not using HTTPS
if ! [ "$CFG_USE_HTTPS" ]; then
uncomment_feature "HTTP" "${PROD_ENV_FILE}"
fi
# generate base docker-compose file
PROD_COMPOSE_FILE="${WORK_DIR_PATH}/${COMPOSE_FILE}"
if [ -f "$PROD_COMPOSE_FILE" ]; then
echo -n " ${TXT_BEGIN} Using existing docker-compose file at ${PROD_COMPOSE_FILE}... "
print_confirmation
else
fetch_base_compose_file
fi
# enable reverse proxy in compose file
uncomment_feature "PROXY" "${PROD_COMPOSE_FILE}"
# enable enrollment service in compose file
if [ "$CFG_ENABLE_ENROLLMENT" ]; then
enable_enrollment
fi
# fetch latest images
echo " ${TXT_BEGIN} Fetching latest Docker images: "
$COMPOSE_CMD -f "${PROD_COMPOSE_FILE}" --env-file "${PROD_ENV_FILE}" pull
# enable and setup VPN gateway
if [ "$CFG_ENABLE_VPN" ]; then
enable_vpn_gateway
fi
# start docker-compose stack
echo " ${TXT_BEGIN} Starting docker-compose stack"
$COMPOSE_CMD -f "${PROD_COMPOSE_FILE}" --env-file "${PROD_ENV_FILE}" up -d
if [ $? -ne 0 ]; then
echo >&2 "ERROR: failed to start docker-compose stack"
exit 1
fi
print_instance_summary
}
########################
### HELPER FUNCTIONS ###
########################
check_character_support() {
local char="$1"
echo -e "$char" | grep -q "$char"
}
is_utf_term() {
if check_character_support "√"; then
TXT_CHECK="✓"
TXT_BEGIN="▶"
TXT_SUB="▷"
TXT_STAR="★"
TXT_X="✗"
TXT_INPUT="✍"
else
TXT_CHECK="+"
TXT_BEGIN=">>"
TXT_SUB=">"
TXT_STAR="*"
TXT_X="x"
TXT_INPUT=" ::"
fi
}
is_term_color() {
if [[ $TERM == *"256"* ]]; then
C_RED="\033[31m"
C_GREEN="\033[32m"
C_YELLOW="\033[33m"
C_BLUE="\033[34m"
C_WHITE="\033[37m"
C_GREY="\033[90m"
C_LRED="\033[91m"
C_LGREEN="\033[92m"
C_LYELLOW="\033[93m"
C_LBLUE="\033[94m"
C_BOLD="\033[1m"
C_ITALICS="\033[3m"
C_BG_GREY="\033[100m"
C_END="\033[0m"
else
C_RED=""
C_GREEN=""
C_YELLOW=""
C_BLUE=""
C_WHITE=""
C_GREY=""
C_LRED=""
C_LGREEN=""
C_LYELLOW=""
C_LBLUE=""
C_BOLD=""
C_ITALICS=""
C_BG_GREY=""
C_END=""
fi
}
# remove array element
remove_element() {
local remove=$1
local result=()
for element in "$@"; do
if [[ "$element" != "$remove" ]]; then
result+=("$element")
fi
done
echo "${result[@]}"
}
# Function to convert relative path to absolute path
to_absolute_path() {
local path="$1"
if [[ "${path:0:1}" != "/" ]]; then
path="$(cd "$(dirname "$path")" && pwd)/$(basename "$path")"
fi
echo ${path}
}
print_header() {
echo -e "${C_LBLUE}"
cat << _EOF_
#
## #
## ## # # ## #
## ## # # # #
# ## # #### # #### ##### #### # # #### ### #### #
# ## ## # ## # ## # # # # # # # # # ##
## ## # # ######## # # # # # # # # #
# ## ## # # # ## # ##### # # ###### # # #
# ## # # ## # # # # # # # # # # ##
## ## #### # ##### # ####### #### # #### # # #### #
## ## # # #
## # #######
#
_EOF_
echo -e "${C_END}"
echo
echo "defguard docker-compose deployment setup script v${VERSION}"
echo -e "Copyright (C) 2023-2024 ${C_BOLD}teonite${C_END} <${C_BG_GREY}${C_YELLOW}https://teonite.com${C_END}>"
echo
}
print_confirmation() {
echo -e " ${C_LGREEN}${TXT_CHECK}${C_END} "
}
print_usage() {
echo "Usage: ${BASENAME} [options]"
echo
echo 'Available options:'
echo
echo -e "\t--help this help message"
echo -e "\t--non-interactive run in non-interactive mode - !REQUIRES SETTING all options/env vars"
echo -e "\t--domain <domain> domain where defguard web UI will be available"
echo -e "\t--enrollment-domain <domain> domain where enrollment service will be available"
echo -e "\t--use-https configure reverse proxy to use HTTPS"
echo -e "\t--volume <directory> Docker volumes directory - default: ${VOLUME_DIR}"
echo -e "\t--vpn-name <name> VPN location name"
echo -e "\t--vpn-ip <address> VPN server address & netmask (e.g. 10.0.50.1/24)"
echo -e "\t--vpn-gateway-ip <ip> VPN gateway external IP (! NOT DOMAIN - IP)"
echo -e "\t--vpn-gateway-port <port> VPN gateway external port (your clients connect here)"
echo
}
command_exists() {
local command="$1"
command -v "$command" >/dev/null 2>&1
}
command_exists_check() {
local command="$1"
if ! command_exists "$command"; then
echo >&2 "ERROR: $command command not found"
echo >&2 "ERROR: dependency failed, exiting..."
exit 2
fi
}
check_environment() {
echo -n " ${TXT_BEGIN} Checking if all required tools are available..."
# compose can be provided by newer docker versions or a separate docker-compose
docker compose version >/dev/null 2>&1
if [ $? = 0 ]; then
COMPOSE_CMD="docker compose"
else
if command_exists docker-compose; then
COMPOSE_CMD="docker-compose"
else
echo
echo >&2 "ERROR: docker-compose or docker compose command not found"
echo >&2 "ERROR: dependency failed, exiting..."
exit 3
fi
fi
command_exists_check openssl
command_exists_check curl
command_exists_check grep
# Check if the volume dir is an absolute path since docker requires it
VOLUME_DIR=$(to_absolute_path "${VOLUME_DIR}")
if [ -d ${VOLUME_DIR} ]; then
echo
echo >&2 "ERROR: volume directory: ${VOLUME_DIR} exists."
echo >&2 "ERROR: this means, I would overwrite the configuration, database and certificates."
echo >&2 "ERROR: please backup or remove the volume directory."
exit 3
fi
# create all necessary directories
for dir in ${VOLUME_DIR} ${SSL_DIR} ${RSA_DIR}; do
mkdir ${dir}
if [ $? -ne 0 ]; then
echo >&2 "ERROR: cloud not create volume directory: ${dir}"
exit 3
fi
done
print_confirmation
}
load_configuration_from_env() {
echo -n " ${TXT_BEGIN} Loading configuration from environment variables... "
# required variables
CFG_DOMAIN="$DEFGUARD_DOMAIN"
# optional variables
CFG_VOLUME_DIR="$DEFGUARD_VOLUME_DIR"
CFG_VPN_NAME="$DEFGUARD_VPN_NAME"
CFG_VPN_IP="$DEFGUARD_VPN_IP"
CFG_VPN_GATEWAY_IP="$DEFGUARD_VPN_GATEWAY_IP"
CFG_VPN_GATEWAY_PORT="$DEFGUARD_VPN_GATEWAY_PORT"
CFG_ENROLLMENT_DOMAIN="$DEFGUARD_ENROLLMENT_DOMAIN"
if ! [ $CFG_USE_HTTPS ]; then
CFG_USE_HTTPS="$DEFGUARD_USE_HTTPS"
fi
print_confirmation
}
load_configuration_from_cli() {
echo -n " ${TXT_BEGIN} Loading configuration from CLI arguments... "
ARGUMENT_LIST=(
"domain"
"enrollment-domain"
"volume"
"vpn-name"
"vpn-ip"
"vpn-gateway-ip"
"vpn-gateway-port"
)
# read arguments
opts=$(
getopt \
--longoptions "$(printf "%s:," "${ARGUMENT_LIST[@]}")" \
--name "$(basename "$0")" \
--options "" \
-- "$@"
)
eval set --$opts
while [[ $# -gt 0 ]]; do
case "$1" in
--domain)
CFG_DOMAIN=$2
shift 2
;;
--enrollment-domain)
CFG_ENROLLMENT_DOMAIN=$2
shift 2
;;
--volume)
CFG_VOLUME_DIR=$2
shift 2
;;
--vpn-name)
CFG_VPN_NAME=$2
shift 2
;;
--vpn-ip)
CFG_VPN_IP=$2
shift 2
;;
--vpn-gateway-ip)
CFG_VPN_GATEWAY_IP=$2
shift 2
;;
--vpn-gateway-port)
CFG_VPN_GATEWAY_PORT=$2
shift 2
;;
*)
break
;;
esac
done
print_confirmation
}
load_configuration_from_input() {
echo -ne "${C_ITALICS}${C_LBLUE}"
cat << _EOF_
Please provide the values to configure your defguard instance. If you've
already configured some options by setting environment variables or through
CLI options, those will be used as defaults.
If you prefer to disable this user input section, please restart the script
with --non-interactive CLI flag.
_EOF_
echo -ne "${C_GREY}"
cat << _EOF_
Choose domains that will be used to expose your instance through Caddy
reverse proxy. defguard uses a separate domain for the Web UI, and for
the optional enrollment/desktop client configuration/password reset
service.
If you don't provide any domain for the enrollment service, the service
itself will not be deployed.
You can also enable HTTPS here (highly recommended), which will configure
Caddy to automatically provision SSL certificates.
_EOF_
echo -ne "${C_BOLD}"
cat << _EOF_
Please note that this requires your server to have a public IP address
and public DNS records for your chosen domains to be configured
correctly (pointing to your server's IP address).
_EOF_
echo -ne "${C_END}"
echo -e " ${C_BOLD}${C_GREEN}${TXT_STAR} General config ${TXT_STAR}${C_END}\n"
while [ X${domain} = "X" ]; do
echo -ne "${C_YELLOW}${TXT_INPUT}${C_END} "
read -p "Enter defguard domain [default: ${CFG_DOMAIN}]: " domain
if [ "$domain" ]; then
CFG_DOMAIN="$domain"
fi
done
echo -ne "${C_YELLOW}${TXT_INPUT}${C_END} "
read -p "Enter enrollment domain [default: ${CFG_ENROLLMENT_DOMAIN}]: " enroll
if [ "$enroll" ]; then
CFG_ENROLLMENT_DOMAIN="$enroll"
fi
use_https_bool_value="false"
if [ $CFG_USE_HTTPS ]; then use_https_bool_value="true"; fi
echo -ne "${C_YELLOW}${TXT_INPUT}${C_END} "
read -p "Use HTTPS [default: ${use_https_bool_value}]: " https
if [ "$https" ]; then
CFG_USE_HTTPS=1
fi
echo
echo -e " ${C_BOLD}${C_GREEN}${TXT_STAR} WireGuard VPN${TXT_STAR}${C_END}\n"
echo -ne "${C_ITALICS}${C_GREY}"
cat << _EOF_
If you wish to configure and deploy WireGuard VPN gateway, please
provide your VPN location name. To skip, just press enter and VPN will
not be configured.
_EOF_
echo -ne "${C_END}\n"
echo -ne "${C_YELLOW}${TXT_INPUT}${C_END} "
read -p "Enter VPN location name [default: ${CFG_VPN_NAME}]: " vpn_name
if [ "$vpn_name" ]; then
CFG_VPN_NAME="$vpn_name"
fi
if [ "$CFG_VPN_NAME" ]; then
while [ X${vpn_ip} = "X" ]; do
echo -ne "${C_YELLOW}${TXT_INPUT}${C_END} "
read -p "Enter VPN server address and subnet (e.g. 10.0.60.1/24) [default: ${CFG_VPN_IP}]: " vpn_ip
if [ "$vpn_ip" ]; then
CFG_VPN_IP="$vpn_ip"
fi
done
echo -ne "${C_ITALICS}${C_GREY}"
cat << _EOF_
Now we'll configure a public endpoint (IP + port) that your WireGuard
client devices will use to safely connect to your gateway from the
public internet.
Since we'll be starting the gateway on this server the IP address should
be the same as your server's public IP address.
_EOF_
echo -ne "${C_BOLD}"
cat << _EOF_
Please also remember that your firewall should be configured
to allow incoming UDP traffic on the chosen WireGuard port.
_EOF_
echo -ne "${C_END}"
while [ X${public_ip} = "X" ]; do
echo -ne "${C_YELLOW}${TXT_INPUT}${C_END} "
read -p "Enter VPN gateway public IP (no domains!) [default: ${CFG_VPN_GATEWAY_IP}]: " public_ip
if [ "$public_ip" ]; then
CFG_VPN_GATEWAY_IP="$public_ip"
fi
done
while [ X${public_port} = "X" ]; do
echo -ne "${C_YELLOW}${TXT_INPUT}${C_END} "
read -p "Enter VPN gateway public port [default: ${CFG_VPN_GATEWAY_PORT}]: " public_port
if [ "$public_port" ]; then
CFG_VPN_GATEWAY_PORT="$public_port"
fi
done
else
echo -e " ${C_BOLD}${C_RED}${TXT_X} ${C_GREY} WireGuard VPN skipped${C_END}\n"
fi
echo
echo -e "${C_BOLD}${C_GREEN}Thank you. We'll now proceed with the deployment using provided values.${C_END}"
}
check_required_variable() {
local var_name="$1"
if [ -z "${!var_name}" ]; then
echo >&2 "ERROR: ${var_name} configuration option not set"
exit 4
fi
}
validate_required_variables() {
echo -n " ${TXT_BEGIN} Validating configuration options..."
check_required_variable "CFG_DOMAIN"
# if VPN name is given validate other VPN configurations are present
if [ "$CFG_VPN_NAME" ]; then
CFG_ENABLE_VPN=1
check_required_variable "CFG_VPN_IP"
check_required_variable "CFG_VPN_GATEWAY_IP"
check_required_variable "CFG_VPN_GATEWAY_PORT"
fi
print_confirmation
}
generate_external_urls() {
# prepare full defguard URL
if [ $CFG_USE_HTTPS ]; then
CFG_DEFGUARD_URL="https://${CFG_DOMAIN}"
else
CFG_DEFGUARD_URL="http://${CFG_DOMAIN}"
fi
# prepare full enrollment URL
if [ "$CFG_ENROLLMENT_DOMAIN" ]; then
CFG_ENABLE_ENROLLMENT=1
if [ "$CFG_USE_HTTPS" ]; then
CFG_ENROLLMENT_URL="https://${CFG_ENROLLMENT_DOMAIN}"
else
CFG_ENROLLMENT_URL="http://${CFG_ENROLLMENT_DOMAIN}"
fi
fi
}
print_config() {
echo
echo " ${TXT_BEGIN} Setting up your defguard instance with following config:"
echo
echo -e " ${TXT_SUB} data volume: ${C_BOLD}${VOLUME_DIR}${C_END}"
echo
echo -e " ${TXT_SUB} domain: ${C_BOLD}${CFG_DOMAIN}${C_END}"
echo -e " ${TXT_SUB} web UI URL: ${C_BOLD}${CFG_DEFGUARD_URL}${C_END}"
if [ "$CFG_VPN_NAME" ]; then
echo -e " ${TXT_SUB} VPN location name: ${C_BOLD}${CFG_VPN_NAME}${C_END}"
echo -e " ${TXT_SUB} VPN address: ${C_BOLD}${CFG_VPN_IP}${C_END}"
echo -e " ${TXT_SUB} VPN gateway IP: ${C_BOLD}${CFG_VPN_GATEWAY_IP}${C_END}"
echo -e " ${TXT_SUB} VPN gateway port: ${C_BOLD}${CFG_VPN_GATEWAY_PORT}${C_END}"
fi
if [ "$CFG_ENROLLMENT_DOMAIN" ]; then
echo -e " ${TXT_SUB} Enrollment service domain: ${C_BOLD}${CFG_ENROLLMENT_DOMAIN}${C_END}"
echo -e " ${TXT_SUB} Enrollment service URL: ${C_BOLD}${CFG_ENROLLMENT_URL}${C_END}"
fi
echo
echo -e " ${TXT_BEGIN} All executed command's results are in log file: ${C_BOLD}${LOG_FILE}${C_END}"
echo
}
setup_keys() {
echo " ${TXT_BEGIN} Setting up SSL certificates and RSA keys..."
if [ -d ${SSL_DIR} -a "$(ls -A ${SSL_DIR})" ]; then
echo " ${TXT_SUB} Using existing SSL certificates from ${SSL_DIR}"
else
generate_certs
fi
if [ -d ${RSA_DIR} -a "$(ls -A ${RSA_DIR})" ]; then
echo " ${TXT_SUB} Using existing RSA keys from ${RSA_DIR}."
else
generate_rsa
fi
}
generate_certs() {
echo " ${TXT_BEGIN} Creating new SSL certificates in ${SSL_DIR}..."
mkdir -p ${SSL_DIR}
PASSPHRASE=$(generate_secret)
echo "PEM passphrase for SSL certificates set to '${PASSPHRASE}'."
# generate private key for CA
openssl genrsa -des3 -out ${SSL_DIR}/defguard-ca.key -passout pass:"${PASSPHRASE}" 2048 2>&1 >> ${LOG_FILE}
# generate Root Certificate
# TODO: allow configuring CA parameters
openssl req -x509 -new -nodes -key ${SSL_DIR}/defguard-ca.key -sha256 -days 1825 -out ${SSL_DIR}/defguard-ca.pem -passin pass:"${PASSPHRASE}" -subj "/C=PL/ST=Zachodniopomorskie/L=Szczecin/O=Example/OU=IT Department/CN=${CFG_DOMAIN}" 2>&1 >> ${LOG_FILE}
# generate CA-signed certificate for defguard gRPC
openssl genrsa -out ${SSL_DIR}/defguard-grpc.key 2048 2>&1 >> ${LOG_FILE}
openssl req -new -key ${SSL_DIR}/defguard-grpc.key -out ${SSL_DIR}/defguard-grpc.csr -subj "/C=PL/ST=Zachodniopomorskie/L=Szczecin/O=Example/OU=IT Department/CN=${CFG_DOMAIN}" 2>&1 >> ${LOG_FILE}
cat >${SSL_DIR}/defguard-grpc.ext <<EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${CFG_DOMAIN}
DNS.2 = core
DNS.3 = localhost
EOF
openssl x509 -req -in ${SSL_DIR}/defguard-grpc.csr -CA ${SSL_DIR}/defguard-ca.pem -CAkey ${SSL_DIR}/defguard-ca.key -passin pass:"${PASSPHRASE}" -CAcreateserial \
-out ${SSL_DIR}/defguard-grpc.crt -days 1000 -sha256 -extfile ${SSL_DIR}/defguard-grpc.ext 2>&1 >> ${LOG_FILE}
# generate CA-signed certificate for defguard proxy gRPC
openssl genrsa -out ${SSL_DIR}/defguard-proxy-grpc.key 2048 2>&1 >> ${LOG_FILE}
openssl req -new -key ${SSL_DIR}/defguard-proxy-grpc.key -out ${SSL_DIR}/defguard-proxy-grpc.csr -subj "/C=PL/ST=Zachodniopomorskie/L=Szczecin/O=Example/OU=IT Department/CN=${CFG_DOMAIN}" 2>&1 >> ${LOG_FILE}
cat >${SSL_DIR}/defguard-proxy-grpc.ext <<EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = proxy
DNS.2 = localhost
EOF
openssl x509 -req -in ${SSL_DIR}/defguard-proxy-grpc.csr -CA ${SSL_DIR}/defguard-ca.pem -CAkey ${SSL_DIR}/defguard-ca.key -passin pass:"${PASSPHRASE}" -CAcreateserial \
-out ${SSL_DIR}/defguard-proxy-grpc.crt -days 1000 -sha256 -extfile ${SSL_DIR}/defguard-proxy-grpc.ext 2>&1 >> ${LOG_FILE}
}
generate_rsa() {
echo "Generating RSA keys in ${RSA_DIR}..."
mkdir -p ${RSA_DIR}
openssl genpkey -out ${RSA_DIR}/rsakey.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 2>&1 >> ${LOG_FILE}
}
generate_secret() {
generate_secret_inner "${SECRET_LENGTH}"
}
generate_password() {
generate_secret_inner "${PASSWORD_LENGTH}"
}
generate_secret_inner() {
local length="$1"
openssl rand -base64 ${length} | tr -d "=+/" | tr -d '\n' | cut -c1-${length-1}
}
create_caddyfile() {
caddy_volume_path="${VOLUME_DIR}/caddy"
caddyfile_path="${caddy_volume_path}/Caddyfile"
mkdir -p ${caddy_volume_path}
cat >${caddyfile_path} <<EOF
${CFG_DEFGUARD_URL} {
reverse_proxy core:8000
}
EOF
if [ "$CFG_ENABLE_ENROLLMENT" ]; then
cat >>${caddyfile_path} <<EOF
${CFG_ENROLLMENT_URL} {
reverse_proxy proxy:8080
}
EOF
fi
cat >>${caddyfile_path} <<EOF
:80 {
respond 404
}
:443 {
respond 404
}
EOF
}
fetch_base_compose_file() {
echo -n " ${TXT_BEGIN} Fetching base compose file to ${PROD_COMPOSE_FILE}... "
curl --proto '=https' --tlsv1.2 -sSf "${BASE_COMPOSE_FILE_URL}" -o "${PROD_COMPOSE_FILE}" 2>&1 >> ${LOG_FILE}
print_confirmation
}
generate_env_file() {
PROD_ENV_FILE="${WORK_DIR_PATH}/${ENV_FILE}"
fetch_base_env_file
update_env_file
print_confirmation
}
fetch_base_env_file() {
echo -e " ${TXT_BEGIN} Fetching base ${ENV_FILE} file for compose stack..."
curl --proto '=https' --tlsv1.2 -sSf "${BASE_ENV_FILE_URL}" -o "${PROD_ENV_FILE}" 2>&1 >> ${LOG_FILE}
print_confirmation
}
update_env_file() {
echo -n " ${TXT_BEGIN} Setting environment variables in ${ENV_FILE} file for compose stack..."
# set image versions
set_env_file_value "CORE_IMAGE_TAG" "${CORE_IMAGE_TAG}"
set_env_file_value "PROXY_IMAGE_TAG" "${PROXY_IMAGE_TAG}"
set_env_file_value "GATEWAY_IMAGE_TAG" "${GATEWAY_IMAGE_TAG}"
# fill in values
set_env_file_secret "DEFGUARD_AUTH_SECRET"
set_env_file_secret "DEFGUARD_YUBIBRIDGE_SECRET"
set_env_file_secret "DEFGUARD_GATEWAY_SECRET"
set_env_file_secret "DEFGUARD_SECRET_KEY"
# use existing password if set in env variable
if [ "$DEFGUARD_DB_PASSWORD" ]; then
set_env_file_value "DEFGUARD_DB_PASSWORD" "${DEFGUARD_DB_PASSWORD}"
else
set_env_file_password "DEFGUARD_DB_PASSWORD"
fi
DEFGUARD_DEFAULT_ADMIN_PASSWORD="$(generate_password)"
set_env_file_value "DEFGUARD_DEFAULT_ADMIN_PASSWORD" "${DEFGUARD_DEFAULT_ADMIN_PASSWORD}"
set_env_file_value "DEFGUARD_URL" "${CFG_DEFGUARD_URL}"
set_env_file_value "DEFGUARD_WEBAUTHN_RP_ID" "${CFG_DOMAIN}"
print_confirmation
}
set_env_file_value() {
# make sure variable exists in file
grep -qF "${1}=" "${PROD_ENV_FILE}" || echo "${1}=" >>"${PROD_ENV_FILE}"
sed -i "s@\(${1}\)=.*@\1=${2}@" "${PROD_ENV_FILE}"
}
set_env_file_secret() {
set_env_file_value "${1}" "$(generate_secret)" "${PROD_ENV_FILE}"
}
set_env_file_password() {
set_env_file_value "${1}" "$(generate_password)" "${PROD_ENV_FILE}"
}
uncomment_feature() {
sed -i "s@# \(.*\) # \[${1}\]@\1@" "${2}"
}
enable_enrollment() {
echo -n " ${TXT_BEGIN} Enabling enrollment proxy service in compose file..."
# update .env file
uncomment_feature "ENROLLMENT" "${PROD_ENV_FILE}"
set_env_file_value "DEFGUARD_ENROLLMENT_URL" "${CFG_ENROLLMENT_URL}"
# update compose file
uncomment_feature "ENROLLMENT" "${PROD_COMPOSE_FILE}"
print_confirmation
}
enable_vpn_gateway() {
echo " ${TXT_BEGIN} Enabling VPN gateway service..."
uncomment_feature "VPN" "${PROD_COMPOSE_FILE}"
uncomment_feature "VPN" "${PROD_ENV_FILE}"
# fetch latest image
echo " ${TXT_SUB} Fetching latest gateway image..."
$COMPOSE_CMD -f "${PROD_COMPOSE_FILE}" --env-file "${PROD_ENV_FILE}" pull gateway
# create VPN location
echo " ${TXT_BEGIN} Adding VPN to core & generating gateway token..."
VPN_NETWORK=`echo ${CFG_VPN_IP} | awk -F'[./]' '{print $1"."$2"."$3".0/"$5}'`
token=$($COMPOSE_CMD -f "${PROD_COMPOSE_FILE}" --env-file "${PROD_ENV_FILE}" run core init-vpn-location --name "${CFG_VPN_NAME}" --address "${CFG_VPN_IP}" --endpoint "${CFG_VPN_GATEWAY_IP}" --port "${CFG_VPN_GATEWAY_PORT}" --allowed-ips "${VPN_NETWORK}" | tail -n 1)
if [ $? -ne 0 ]; then
echo >&2 "ERROR: failed to create VPN network"
exit 1
fi
# add gateway token to .env file
set_env_file_value "DEFGUARD_TOKEN" "${token}"
}
print_instance_summary() {
echo
echo -e "${C_LGREEN} ${TXT_CHECK} defguard setup finished successfully${C_END}"
echo
echo "If your DNS configuration is correct your defguard instance should be available at:"
echo
echo -e "\t${TXT_SUB} Web UI: ${C_BOLD}${CFG_DEFGUARD_URL}${C_END}"
if [ "$CFG_ENABLE_ENROLLMENT" ]; then
echo -e "\t${TXT_SUB} Enrollment service: ${C_BOLD}${CFG_ENROLLMENT_URL}${C_END}"
fi
echo
echo -e " ${TXT_BEGIN} You can log into the UI using the default admin user:"
echo
echo -e "\t${TXT_SUB} username: ${C_BOLD}admin${C_END}"
echo -e "\t${TXT_SUB} password: ${C_BOLD}${DEFGUARD_DEFAULT_ADMIN_PASSWORD}${C_END}"
echo
if [ "$CFG_ENABLE_VPN" ]; then
echo -e "\t\tVPN server public endpoint is ${C_BOLD}${CFG_VPN_GATEWAY_IP}:${CFG_VPN_GATEWAY_PORT}${C_END}"
echo -e "\t\tVPN network is ${C_BOLD}${VPN_NETWORK}${C_END}"
echo -e "\t\t! Make sure your firewall allows external UDP traffic to port ${C_BOLD}${CFG_VPN_GATEWAY_PORT}${C_END} !"
echo
echo -e "\t\tTo test if the VPN is working: ping ${CFG_VPN_IP} (after connecting to VPN)"
fi
echo
echo -e "Files used to deploy your instance are stored in:"
echo -e "\t docker compose file: ${C_BOLD}${PROD_COMPOSE_FILE}${C_END}"
echo -e "\t docker compose environment: ${C_BOLD}${PROD_ENV_FILE}${C_END}"
echo
echo -e "Persistent data (docker volumes) is stored in ${C_BOLD}${VOLUME_DIR}${C_END}"
echo
echo -e " ${C_YELLOW}${TXT_STAR} To support our work, please star us on GitHub! ${TXT_STAR}${C_END}"
echo -e " ${C_YELLOW}${TXT_STAR} https://github.com/defguard/defguard ${TXT_STAR}${C_END}"
echo
}
# run main function
main "$@" || exit 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,9 @@
# Use userspace wireguard implementation, useful on systems without native wireguard support
# Set to 0/1
DEFGUARD_USERSPACE=0
# Defguard GRPC URL, e.g.: defguard-grpc.mycompany.com
DEFGUARD_GRPC_URL=<DEFGUARD_GRPC_URL>
# Token from Defguard app to secure gRPC connection, available on network page.
DEFGUARD_TOKEN=<DEFGUARD_TOKEN>
# Defines how often (in seconds) should interface statistics be sent to Defguard server
DEFGUARD_STATS_PERIOD=30

View File

@ -0,0 +1,22 @@
version: "3"
services:
gateway:
image: ghcr.io/defguard/gateway:latest
restart: unless-stopped
network_mode: "host"
environment:
# load variables from .env file
- DEFGUARD_GRPC_URL
- DEFGUARD_TOKEN
- DEFGUARD_STATS_PERIOD
- RUST_LOG=debug
# SSL setup guide: https://defguard.gitbook.io/defguard/features/setting-up-your-instance/docker-compose#ssl-setup
# - DEFGUARD_GRPC_CA: /ssl/defguard-ca.pem
ports:
# wireguard endpoint
- "50051:50051/udp"
#volumes:
# SSL setup guide: https://defguard.gitbook.io/defguard/features/setting-up-your-instance/docker-compose#ssl-setup
#- ./.volumes/ssl:/ssl
cap_add:
- NET_ADMIN