newServer

This commit is contained in:
Zakaria 2024-08-15 16:09:26 +00:00
parent 1ac68b12a0
commit a274dd9fd2
49 changed files with 2236 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

46
deployment/.github/workflows/test.yml vendored Normal file
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
deployment/.gitignore vendored Normal file
View File

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

13
deployment/LICENSE Normal file
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.

16
deployment/README.md Normal file
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,98 @@
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 # [PROXY]
# - "80:80" # [PROXY]
# # https # [PROXY]
# - "443: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
- "8850: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
# - "8080: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]
# SSL setup guide: https://defguard.gitbook.io/defguard/features/setting-up-your-instance/docker-compose#ssl-setup
# - ${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

BIN
deployment/docs/header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

10
deployment/gateway/.env Normal file
View File

@ -0,0 +1,10 @@
# 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=http://192.168.1.197:50055/
# Token from Defguard app to secure gRPC connection, available on network page.
DEFGUARD_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJEZWZHdWFyZCIsInN1YiI6IkRFRkdVQVJELU5FVFdPUkstMiIsImNsaWVudF9pZCI6IjIiLCJleHAiOjYwMTg2NjMyMjYsIm5iZiI6MTcyMzY5NTkzMX0.cmQTo1ZIflFAW-STanDi3rgb9lqE55Hf50yetX1BSUE
# Defines how often (in seconds) should interface statistics be sent to Defguard server
DEFGUARD_STATS_PERIOD=30

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,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCbzBOckyI+yISZ
8+K+wkacfAR/NJsqJ1pyq7mKa+fbWMeINharP3G0qBmL56sromxl2zgC/9VU5lnR
UwSjvA5UtwOtfAxwhtRf9gmWYA6qUC8csSLev/nwTi/BiHzeJvEJFB5y4JCMPfs/
grRVWIW79to3zIUY2PKLcY9xvQwGwn9w9nYasnESdv2931ZTWR0ibYkrhMfqt1eW
njB63ZISWWoIVirO/WfcoAMtihuGt1IK/tlARk1nT1SFAhNncG59tzMa7uz2jJIq
c3JEoAhMeClH08Yv0+rRIB3dXExEKg5/6wr8eE7hGBpBEfpaeMPQocQYwLBEH82f
vR9hioTNAgMBAAECggEABsjeWcU2ipFk5OdwCmqfdJUaBQgfcbiBAdlknRiR4kV9
evsQBakfpIRliGgwVhgerFjdmZPWkHnvk2QQIalA3oORz25FhGj6uqsWQ8wvF0+i
nZdBM34cvlBJWB4/gdUkTgboRIxu/wx78ooYnFbcjBD189QeP7TC6zuoyYU4Dts8
FaJ4FcGa+tKFWhCh2wqtGxisHOxUmgaMmkOljLcpLmEA7DpUfRew7dIHptkGylox
8ahyVVTPq8H5R1lsshvDw0MFLsS12hHtTn1yicTFQNOg8JGX9lV67fCyC3pFxLiR
ZtGcUe0MiiCVltHOqwPQEBsIcweN0SRk2JZA0FnAAQKBgQDPKveaqNuU9rY7R0QF
8mjVnbPbobrmi5k9XqvuoPOuDA/86e0BYlv7BDST8Y8hOyK9aBScg+4vCKvEptoR
AGDIjdd2GueGW/HjwZa2kjunXQk0Rn1C9OsQpwNLTR3XLwmPyWvMZLVMEx/bGuLg
lNgxj+qetmSq2jwpYLkpZqiYAQKBgQDAhUXsd5uXuDtzp8NToxaoO+juKFYIRNKF
EP6m/7Pmwb2PGAT+a7erI9wktKdpN/AL8uQjder0hOHmN/ik+nwEB04HHDQxyoXt
VzayzBuhjfEKg+QwWuRu79eTlFd2Ed4bYbsPod1XMfj9XSI64wFZZCNF2XqfRiuU
uuFkE2jMzQKBgQCVjdAnj0TNWfkd/AmIPYIey/T+VdfF/PsICaMW5oxjlgOosfrN
qAL8yAFo19ZayAUBNPTENJ2qyJivo0ADTAGSZosnkK6ZGSEbKTKy5Ag6fvhZC5X7
0zEq2VaQcsBbCnLdoSu35u/WVmwF0Xf9ZpZX2SwnnUY47MjHmjKxR5HoAQKBgQCD
FjwWVxrKo9dXWNPXDyVOR/zCrRRnbPUrRfcfHt0QMrsvw9sioZXeIfyzwY58Rmpc
uHY+7vucox5t846KR0RKOe8XSE0B2jR29vt3oyLtTgjicAvgIQOJxiWzhz5GVsQ1
QMZuTni39n7jhZbZIdi5VUXvObYU3WKvUtBFpDGnwQKBgA3GtNzxQ8vzUkciUfue
uHaStFZEASM8m+Fz6J5JDTVzeHzJA6vFQCm2YPFqWlYHZLTZsuXWknm0iCntPCMN
Bbn0J5Ap8IUg3mzm4IHVGUNkGKrLm3Go77E4ZZrYOq7O2dGdtJPQppMaJ10FXdUr
pHiQmASJ88fBasr1sQ12/2vQ
-----END PRIVATE KEY-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIUFAaYomt0LdhGk7d3r7/cojUSpYowDQYJKoZIhvcNAQEL
BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMQwwCgYDVQQHDANOWUMxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA4MTUwNzA1MDRa
Fw0zNDA4MTMwNzA1MDRaMEsxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTEMMAoG
A1UEBwwDTllDMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbzBOckyI+yISZ8+K+wkacfAR/
NJsqJ1pyq7mKa+fbWMeINharP3G0qBmL56sromxl2zgC/9VU5lnRUwSjvA5UtwOt
fAxwhtRf9gmWYA6qUC8csSLev/nwTi/BiHzeJvEJFB5y4JCMPfs/grRVWIW79to3
zIUY2PKLcY9xvQwGwn9w9nYasnESdv2931ZTWR0ibYkrhMfqt1eWnjB63ZISWWoI
VirO/WfcoAMtihuGt1IK/tlARk1nT1SFAhNncG59tzMa7uz2jJIqc3JEoAhMeClH
08Yv0+rRIB3dXExEKg5/6wr8eE7hGBpBEfpaeMPQocQYwLBEH82fvR9hioTNAgMB
AAGjUzBRMB0GA1UdDgQWBBSuVx6w1tDFOr/nxkhH5zUCoKQjnjAfBgNVHSMEGDAW
gBSuVx6w1tDFOr/nxkhH5zUCoKQjnjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQATnuDVzsiLdg1D7KIWvavEv4l0aj1TzMAnev7ASoCeDumVKxrv
1F8B8gKiYtA0r1GXInq0x9o4/u7nZK00hqyPBvHmTDCwimV0KMD+XEcg28rwTfn8
0RbVcUIEMNVRs1eEV3wSUHoJMxhEf7kSGJ7X8C+zu0CEQ9loBqAAbO7unhJZuoT6
TixM0nqV5ss3hkk8RFhe3902mRT8Fit5wotNHRYquvi8yku4EZyArudjkQoalCBV
CNnhWxTug+Sc1XY6jMWdfFnU4bVK+hjuSnYwIM6VRDXmYRGuxCFf8lEfQNIRUOdF
N9Xp6iJcj0Lg3lW4i9lLS2z/jW+ICgtPYTcK
-----END CERTIFICATE-----

View File

@ -0,0 +1 @@
21E9338B4AD12CCAF2F29C30B8DA6E8C7D993090

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDHTCCAgUCFCHpM4tK0SzK8vKcMLjabox9mTCQMA0GCSqGSIb3DQEBCwUAMEsx
CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTEMMAoGA1UEBwwDTllDMSEwHwYDVQQK
DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjQwODE1MDcwNTQ5WhcNMjUw
ODE1MDcwNTQ5WjBLMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkxDDAKBgNVBAcM
A05ZQzEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu9RdFYdklQ94lioWaFDEOb/1KeQ8BQ1X
b0YjYvgJ+rWobMNRls7A12g+gJ8EAPVDP7p9YaC06ovievSfaueC9byo9lfWsUrW
mwNA4/lH82j+/qKl8Yi8doBnQ6UAlSmt/g0PYE+/PQvRahld1K0LvqOpZIVa5imv
NY0h9Bt9J9gYKSZPvTh4MapsWGdVurkLUvQglfU4DpHKGvRoGT1nMYKX7BlFgtkM
hzD4jRoyOFyXwyyL9UFqHFYUozscPmm5deX0nNcfOs0NUOBHp11u+MG8R9mThct2
JzEmu9lXxshBVk1Yrf7kHpZuW0YrvqNCSRYu9MiBiPSTZGdt1TrsoQIDAQABMA0G
CSqGSIb3DQEBCwUAA4IBAQAW17f+yj2IJis7SmrKFho1+bQapQac0err61g7jGYS
5Jo7f0Qvk7K5FUwwAbqF6zr8C31+PZrZmou1n8ht6y4rf/mmZV3ujuSf8cRuX9Jr
RpBpwDhBQU51/iZaQm+eDwuw8KJDGnHRQE7EzP6CNO5G9M7YblI3nJdcbXIog1Y7
JgOOf5UZmerVhQm9wUNvVJ19p4qWW5u1AGXSaXTu2tcj6NSMITT2lLL321AdL4Vs
Zl7vpUV6ZbyDd7rYwqXnWEFLIWWTi9CKk8nSe130oag8PPhNCOFUY39M/1u1ZfeC
LypC2FNYJseA+xZnZXxAXkDOBLt8rIxTykVxAR8idElN
-----END CERTIFICATE-----

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICrzCCAZcCAQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMQwwCgYDVQQH
DANOWUMxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALvUXRWHZJUPeJYqFmhQxDm/9SnkPAUN
V29GI2L4Cfq1qGzDUZbOwNdoPoCfBAD1Qz+6fWGgtOqL4nr0n2rngvW8qPZX1rFK
1psDQOP5R/No/v6ipfGIvHaAZ0OlAJUprf4ND2BPvz0L0WoZXdStC76jqWSFWuYp
rzWNIfQbfSfYGCkmT704eDGqbFhnVbq5C1L0IJX1OA6Ryhr0aBk9ZzGCl+wZRYLZ
DIcw+I0aMjhcl8Msi/VBahxWFKM7HD5puXXl9JzXHzrNDVDgR6ddbvjBvEfZk4XL
dicxJrvZV8bIQVZNWK3+5B6WbltGK76jQkkWLvTIgYj0k2RnbdU67KECAwEAAaAf
MB0GCSqGSIb3DQEJBzEQDA5aYWthcmlhMTk4NiEhITANBgkqhkiG9w0BAQsFAAOC
AQEAUae4G2/G01LzT4MyTRhXja8uU1AKbxx7KztIN9WxWsRgQJjLte9gcANcsku+
e+yrMlfm+LjTRH6bG3yNyyxFWncS8IyG3a+N2rvXoEO4lMIwQfyanYPyOB0ONoR3
fzB+Ssgw8txiXwOSIG7KsQkebJ30aSqUe6FuUd9kY02bx1NYGvctf9mtBv+d9Q5Y
xYv1S0S6TtjB9PnUMWdfLm0Xmj9X0XzPjKrtxwamJ/AF3uTsQseuqcKQpbLCd32r
w0mjeZHtVrDgrxVgqxt1c86eAHwnhmD4jy9Vl2tJLcPNIXvTQNxoi5h/tYi1TlD3
Mj6K46g29ee1HiLaL7ZbJb7Geg==
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC71F0Vh2SVD3iW
KhZoUMQ5v/Up5DwFDVdvRiNi+An6tahsw1GWzsDXaD6AnwQA9UM/un1hoLTqi+J6
9J9q54L1vKj2V9axStabA0Dj+UfzaP7+oqXxiLx2gGdDpQCVKa3+DQ9gT789C9Fq
GV3UrQu+o6lkhVrmKa81jSH0G30n2BgpJk+9OHgxqmxYZ1W6uQtS9CCV9TgOkcoa
9GgZPWcxgpfsGUWC2QyHMPiNGjI4XJfDLIv1QWocVhSjOxw+abl15fSc1x86zQ1Q
4EenXW74wbxH2ZOFy3YnMSa72VfGyEFWTVit/uQelm5bRiu+o0JJFi70yIGI9JNk
Z23VOuyhAgMBAAECggEAKwwkuUZqeu8sx92lfQrlrgacfZldWBsSquH8QjZusxLn
IuYw0MtZzwSJLplDJaUQYI6xJarbS9X7dgqSbsHYddFjN/IxtjhcrvIz8Qu0vciW
iG1mctDPwKj1Ab/TPlxrEAqWN5CPV8JZoGNn6dIvGuYPcIZPquhqy28pFPUn3fV3
cSS2dR3D6OU6yxEImZGffDwrZdXTTM+N1QMLHl4ivPzIjzrbwlVMVVODKracNvUE
79wYcieiEgbAM3DzlHnaW4BINw2ugoWFTAKMtHiVV8U3wQhDKvwljgFMTjB9O6PQ
nXRzoVwW5KX9gwpo9CBNVWn8/D+S04KHObC4cIKeQQKBgQD3ugq2sU1zXnBh/n7P
4Ac0Fv8Xwd/Ai3gRraJ3fFHKdwWKIL0m5DWDvALhoM+I/joLgUy0SV05R+iQzSu3
3hLrp9GqWn/drNOvUhnPeO1MwvHATpDRCEkza5I1uRn6wZf95eclvDgocqnutWGv
t43sccm38iivIzs2YYmPdgQ75QKBgQDCGjnxXwU/wHV+uOFNSs5R/LIpCpqGzoW7
5PQguJQOQY2Gq3aoPwnSGycgkOfkg2cibv04lDuoCnooYAc6SZy0N0wy9FDAsuzB
+6RNHGrr3jsqKJkRPwDal4Hb9AIMkhcIt9RIzEjO4nMKb7GxkhKD5glFVxWlNmMT
CWmXuIo6DQKBgQDM1epx8d1m3dnzTWoyHL4YFkPLsyV+olQf2gES55sB3LSZ6EQ/
Wkfdq6J+SmgQkJYSWVHBaUBKUuk8gkn5+QiQDu3Q/I/qDjPjLfHlmcotxKv9JXmd
Pkq41+PHxEx1CYrSCD2++Ak/eMCGfzhNAWu67MOs8/EsD+ewKaqDE0Sg+QKBgAVx
ktpwHceR8Dmjmb3/MRYfjieUgozxUdLZMveP9acIs51pRaSmT/IyjMBfEAHapZPT
pQpnLd0inhZvywQZeGmde2eaboFZA0bVdeArwdvnmaUvCkvvhmibAytWBpCvsDGw
ZiW8hPY4Z52NUGB4hkhotS3aqWK+ybyI8QsuQ8IpAoGBAIsY6ARakykXZueMGLQN
B7n5cyoWlQNr3iD0UCUMDHqjUe8IUIfw1ZMaHun+aI3UaY/chHBkaxfy+0IQatNd
1F9wmCNmHrjjoBLPEqPvh7TeAEfzexi4fnadLukXWb9KyZLA9YEdos4CK9EE0+Nx
6r076e6gFIRQr09qR7pGz9TQ
-----END PRIVATE KEY-----

View File

@ -0,0 +1,22 @@
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
#- "51820: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