initial files

This commit is contained in:
xoffio
2025-11-03 23:45:48 -05:00
commit 39ab6d08b5
17 changed files with 518 additions and 0 deletions

23
.helmignore Normal file
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/

24
Chart.yaml Normal file
View File

@@ -0,0 +1,24 @@
apiVersion: v2
name: xo-pangolin
description: Unofficial helm chart to deploy Pangolin on Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.11.0"

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 XpaceOff
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

18
README.md Normal file
View File

@@ -0,0 +1,18 @@
Unofficial Pangolin Helm chart
```bash
git clone https://github.com/XpaceOff/xo-pangolin.git
cd xo-pangolin
# Install example:
helm install pangolin xo-pangolin \
--set-string settings.pangolin.serverSecretKey=SECRET_KEY_HERE \
--set-string ingress.hostDomain="pangolin.test" \
--set-string volumes[0].name=vol-pangolin \
--set-string volumes[0].nfs.path=/volume/cluster/pangolin \
--set-string volumes[0].nfs.server=nas.lan
# Uninstall example:
helm uninstall pangolin
```

20
files/config.yml Normal file
View File

@@ -0,0 +1,20 @@
app:
dashboard_url: "https://{{ .Values.ingress.hostDomain }}"
log_level: info
save_logs: true
domains:
domain1:
base_domain: "{{ .Values.ingress.hostDomain }}"
server:
secret: "{{ .Values.settings.pangolin.serverSecretKey }}"
internal_hostname: "{{ include "xo-pangolin.fullname" . }}-pangolin-cip.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain | default "cluster.local" }}"
gerbil:
base_endpoint: "{{ .Values.ingress.hostDomain }}"
flags:
require_email_verification: false
disable_signup_without_invite: true
disable_user_create_org: true

21
files/dynamic_config.yml Normal file
View File

@@ -0,0 +1,21 @@
http:
routers:
next-router:
rule: Host(`{{ .Values.ingress.hostDomain }}`) && !PathPrefix(`/api/v1`)
service: next-service
entryPoints: [web]
api-router:
rule: "Host(`{{ .Values.ingress.hostDomain }}`) && PathPrefix(`/api/v1`)"
service: api-service
entryPoints: [web]
ws-router:
rule: "Host(`{{ .Values.ingress.hostDomain }}`)"
service: api-service
entryPoints: [web]
services:
next-service:
loadBalancer:
servers: [{ url: "http://{{ include "xo-pangolin.fullname" . }}-pangolin-cip.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain | default "cluster.local" }}:3002" }]
api-service:
loadBalancer:
servers: [{ url: "http://{{ include "xo-pangolin.fullname" . }}-pangolin-cip.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain | default "cluster.local" }}:3000" }]

42
files/traefik_config.yml Normal file
View File

@@ -0,0 +1,42 @@
providers:
http:
endpoint: "http://{{ include "xo-pangolin.fullname" . }}-pangolin-cip.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain | default "cluster.local" }}:3001/api/v1/traefik-config"
pollInterval: "5s"
file:
filename: "/etc/traefik/dynamic_config.yml"
experimental:
plugins:
badger:
moduleName: "github.com/fosrl/badger"
version: {{ .Values.settings.traefik.badgerVersion }}
log:
level: "DEBUG"
format: "common"
accessLog:
# JSON format
format: json
# Filter on status codes, retry attempts and minimal duration
filters:
statusCodes:
- "200-599"
fields:
headers:
defaultMode: drop
names:
User-Agent: keep
X-Forwarded-For: keep
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
serversTransport:
insecureSkipVerify: true
ping:
entryPoint: "web"

6
templates/NOTES.txt Normal file
View File

@@ -0,0 +1,6 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.ingress.hostDomain }}
{{- end }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "xo-pangolin.fullname" . }}-gerbil-lb'

62
templates/_helpers.tpl Normal file
View File

@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "xo-pangolin.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 "xo-pangolin.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 "xo-pangolin.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "xo-pangolin.labels" -}}
helm.sh/chart: {{ include "xo-pangolin.chart" . }}
{{ include "xo-pangolin.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "xo-pangolin.selectorLabels" -}}
app.kubernetes.io/name: {{ include "xo-pangolin.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "xo-pangolin.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "xo-pangolin.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,72 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "xo-pangolin.fullname" . }}-gerbil
spec:
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: gerbil
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: gerbil
app: {{ include "xo-pangolin.fullname" . }}-gerbil
spec:
containers:
- name: gerbil
env:
- name: LOG_LEVEL
value: DEBUG
image: "{{ .Values.deployments.gerbil.image.repository }}:{{ .Values.deployments.gerbil.image.tag }}"
imagePullPolicy: "{{ .Values.deployments.gerbil.image.pullPolicy }}"
args:
- '--remoteConfig=http://{{ include "xo-pangolin.fullname" . }}-pangolin-cip.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain | default "cluster.local" }}:3001/api/v1/'
- '--reachableAt=http://{{ include "xo-pangolin.fullname" . }}-gerbil-cip.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain | default "cluster.local" }}:3004'
- '--generateAndSaveKeyTo=/var/config/key'
securityContext:
capabilities:
add:
- NET_ADMIN
{{- $vols := required "values.volumes must be a non-empty list" .Values.volumes }}
{{- $first := required "values.volumes[0] is missing" (index $vols 0) }}
{{- $volName := required "values.volumes[0].name is required" $first.name }}
volumeMounts:
- mountPath: /var/config
name: {{ $volName }}
subPath: config
- name: traefik
image: "{{ .Values.deployments.traefik.image.repository }}:{{ .Values.deployments.traefik.image.tag }}"
imagePullPolicy: "{{ .Values.deployments.traefik.image.pullPolicy }}"
args:
- '--configFile=/etc/traefik/traefik_config.yml'
volumeMounts:
- mountPath: /var/certificates
name: {{ $volName }}
readOnly: true
subPath: pangolin-data
- mountPath: /var/dynamic
name: {{ $volName }}
readOnly: true
subPath: pangolin-data
- mountPath: /letsencrypt
name: {{ $volName }}
subPath: config/letsencrypt
- mountPath: /etc/traefik/traefik_config.yml
name: config-file
subPath: traefik_config.yml
readOnly: true
- mountPath: /etc/traefik/dynamic_config.yml
name: config-file
subPath: dynamic_config.yml
readOnly: true
volumes:
{{- toYaml $vols | nindent 8 }}
- name: config-file
secret:
secretName: {{ include "xo-pangolin.fullname" . }}-config

View File

@@ -0,0 +1,21 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "xo-pangolin.fullname" . }}-gerbil-cip
spec:
ports:
- name: api
port: 3004
protocol: TCP
targetPort: 3004
- name: http
port: 80
protocol: TCP
targetPort: 80
- name: https
port: 443
protocol: TCP
targetPort: 443
selector:
app: {{ include "xo-pangolin.fullname" . }}-gerbil
type: ClusterIP

View File

@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "xo-pangolin.fullname" . }}-gerbil-lb
spec:
externalTrafficPolicy: Local
ports:
- name: wg0
port: 51820
protocol: UDP
targetPort: 51820
- name: wg1
port: 21820
protocol: UDP
targetPort: 21820
selector:
app: {{ include "xo-pangolin.fullname" . }}-gerbil
type: LoadBalancer

View File

@@ -0,0 +1,49 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "xo-pangolin.fullname" . }}-pangolin
spec:
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: pangolin
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: pangolin
app: {{ include "xo-pangolin.fullname" . }}-pangolin
spec:
containers:
- image: "{{ .Values.deployments.pangolin.image.repository }}:{{ .Values.deployments.pangolin.image.tag }}"
imagePullPolicy: "{{ .Values.deployments.pangolin.image.pullPolicy }}"
name: pangolin
readinessProbe:
failureThreshold: 15
httpGet:
path: /api/v1/
port: 3001
scheme: HTTP
{{- $vols := required "values.volumes must be a non-empty list" .Values.volumes }}
{{- $first := required "values.volumes[0] is missing" (index $vols 0) }}
{{- $volName := required "values.volumes[0].name is required" $first.name }}
volumeMounts:
- mountPath: /var/certificates
name: {{ $volName }}
subPath: pangolin-data
- mountPath: /var/dynamic
name: {{ $volName }}
subPath: pangolin-data
- mountPath: /app/config
name: {{ $volName }}
subPath: config
- mountPath: /app/config/config.yml
name: config-file
subPath: config.yml
volumes:
{{- toYaml $vols | nindent 8 }}
- name: config-file
secret:
secretName: {{ include "xo-pangolin.fullname" . }}-config

View File

@@ -0,0 +1,35 @@
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "xo-pangolin.fullname" . }}-pangolin-in
spec:
rules:
- host: {{ .Values.ingress.hostDomain }}
http:
paths:
- backend:
service:
name: {{ include "xo-pangolin.fullname" . }}-gerbil-cip
port:
number: 80
path: /
pathType: Prefix
- backend:
service:
name: {{ include "xo-pangolin.fullname" . }}-gerbil-cip
port:
number: 80
path: /api/v1
pathType: Prefix
- host: '*.{{ .Values.ingress.hostDomain }}'
http:
paths:
- backend:
service:
name: {{ include "xo-pangolin.fullname" . }}-gerbil-cip
port:
number: 80
path: /
pathType: Prefix
{{- end }}

View File

@@ -0,0 +1,21 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "xo-pangolin.fullname" . }}-pangolin-cip
spec:
ports:
- name: http-ext-api
port: 3000
protocol: TCP
targetPort: 3000
- name: http-int-api
port: 3001
protocol: TCP
targetPort: 3001
- name: http-ui
port: 3002
protocol: TCP
targetPort: 3002
selector:
app: {{ include "xo-pangolin.fullname" . }}-pangolin
type: ClusterIP

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: {{ include "xo-pangolin.fullname" . }}-config
stringData:
config.yml: |
{{- $file := required "files/config.yml missing" (.Files.Get "files/config.yml") -}}
{{- tpl $file . | nindent 4 }}
traefik_config.yml: |
{{- $file := required "files/traefik_config.yml missing" (.Files.Get "files/traefik_config.yml") -}}
{{- tpl $file . | nindent 4 }}
dynamic_config.yml: |
{{- $file := required "files/dynamic_config.yml missing" (.Files.Get "files/dynamic_config.yml") -}}
{{- tpl $file . | nindent 4 }}

50
values.yaml Normal file
View File

@@ -0,0 +1,50 @@
# Default values for xo-pangolin.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
settings:
pangolin:
serverSecretKey: 'SECRET_KEY_HERE'
traefik:
badgerVersion: "v1.2.0"
deployments:
pangolin:
replicaCount: 1
image:
repository: "fosrl/pangolin"
pullPolicy: IfNotPresent
tag: "1.11.0"
gerbil:
replicaCount: 1
image:
repository: "fosrl/gerbil"
pullPolicy: IfNotPresent
tag: "1.2.2"
traefik:
replicaCount: 1
image:
repository: "traefik"
pullPolicy: IfNotPresent
tag: "3.4.0"
# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/
ingress:
hostDomain: pangolin.test
enabled: true
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
# Additional volumes on the output Deployment definition.
volumes: []