Add reference documentation

This commit is contained in:
Owen
2026-09-10 17:41:36 -04:00
parent c7f8851806
commit 695507ce9d
17 changed files with 410 additions and 1 deletions
-1
View File
@@ -46,7 +46,6 @@ public/branding
server/db/index.ts
server/build.ts
postgres/
dynamic/
*.mmdb
scratch/
tsconfig.json
+54
View File
@@ -0,0 +1,54 @@
you need 3 instances at a minimum: node1 running pangolin, node 2 running pangolin, and a database server running postgres and redis. the third instance does not need to be a instance - you could deploy pg and redis however you want as long as its accessable to the nodes. the redis that is deployed needs to support pub sub.
the two pangolin nodes need to have public STATIC ips accessible on the internet <NODE1_EXTERNAL_IP> AND <NODE2_EXTERNAL_IP>
the two nodes need tp be able to address each other <NODE1_INTERNAL_IP> and <NODE2_INTERNAL_IP>
open ports should look like this
**Outbound Rules**
| Name | IP version | Type | Protocol | Port range | Destination | Description |
| --- | --- | --- | --- | --- | --- | --- |
| | IPv4 | All traffic | All | All | 0.0.0.0/0 | Allow all outbound |
**Inbound Rules**
| Name | IP version | Type | Protocol | Port range | Source | Description |
| --- | --- | --- | --- | --- | --- | --- |
| | IPv4 | Custom UDP | UDP | 21820 | 0.0.0.0/0 | WireGuard Relay Port |
| | IPv4 | DNS (UDP) | UDP | 53 | 0.0.0.0/0 | DNS |
| | IPv4 | HTTP | TCP | 80 | 0.0.0.0/0 | Ping and redirects |
| | IPv4 | Custom UDP | UDP | 51820 | 0.0.0.0/0 | WireGuard Port |
| | IPv4 | Custom TCP | TCP | 3004 | <self - all other nodes> | Pangolin API |
| | IPv4 | HTTPS | TCP | 443 | 0.0.0.0/0 | Resources inbound |
add your email address for acme into <CONTACT_EMAIL>
only one of the nodes - in this case node1 - should be configured to run the acme client. the other node should have acme disabled. this is because only one node should be responsible for generating and renewing certificates. the other node will use the same certificates from the database. this is controlled with `acme.enable_acme_client`
--
when you start for the first time pick one node to start first. This node will init the database and print out the init token to the logs. Use this token to visit the UI and login to create the first user. Then bring up the other nodes
--
whats required:
a load balancer in front of the nodes. this can be a cloud load balancer or a self hosted one like traefik. the load balancer should be configured to route traffic to both nodes pangolin UI and . the load balancer should also have a health check configured to check the /ping endpoint on both nodes. if a node is unhealthy, the load balancer should stop routing traffic to that node.
todo: we should put the dynamic config back on both nodes so that all the upstream LB has to do is route to one entrypoint and we deal with the pangolin routing downstream like the websocket and api and stuff
troubleshooting:
if you run into loopback issues with the local pangolin instance not being able to address the local gerbil at the IP of the host programmed in reachble at in the docker compose file then you can set the following in the private config file. this will force it to address the docker container instead.
```
gerbil:
local_exit_node_reachable_at: "http://gerbil:3004"
```
@@ -0,0 +1,23 @@
services:
postgres:
image: postgres:17
container_name: postgres
environment:
POSTGRES_DB: postgres # Default database name
POSTGRES_USER: postgres # Default user
POSTGRES_PASSWORD: password # Default password (change for production!)
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
restart: always
redis:
image: redis:latest
container_name: redis
ports:
- "6379:6379"
restart: always
volumes:
postgres_data:
@@ -0,0 +1,38 @@
# To see all available options, please visit the docs:
# https://docs.pangolin.net/
gerbil:
start_port: 51820
base_endpoint: "<THIS_NODE_EXTERNAL_IP>"
exit_node_name: "node1"
app:
dashboard_url: "https://pangolin.example.com"
log_level: "info"
postgres:
connection_string: postgresql://<POSTGRES_USERNAME>:<POSTGRES_PASSWORD>@<POSTGRES_INTERNAL_HOST>:5432/postgres
traefik:
site_types: ["newt"] # Wireguard and local sites are not support in clustering
file_mode: true # Pangolin will generate and save yaml files in a shared volume
server:
secret: "<SECRET>"
cors:
origins: ["https://pangolin.example.com"]
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
allowed_headers: ["X-CSRF-Token", "Content-Type"]
credentials: false
maxmind_db_path: "./config/GeoLite2-Country.mmdb" # Make sure to download and place into the config dir
maxmind_asn_path: "./config/GeoLite2-ASN.mmdb"
flags:
require_email_verification: false
disable_signup_without_invite: true
disable_user_create_org: false
allow_raw_resources: false
enable_acme_cert_sync: false
disable_local_sites: true
disable_basic_wireguard_sites: true
disable_config_managed_domains: true
@@ -0,0 +1,18 @@
app:
region: "region1"
identity_provider_mode: "org"
redis:
host: "<REDIS_INTERNAL_HOST>"
port: 6379
flags:
enable_redis: true
use_pangolin_dns: true
acme:
cert_mode: "pangolin"
contact_email: "<CONTACT_EMAIL>"
enable_acme_client: true
dns:
enabled: true
nameserver_name: "ns.example.com"
cname_extension: "cname.example.com"
site_extension: "site.example.com" # Optional
@@ -0,0 +1,41 @@
providers:
file:
directory: "/var/dynamic"
watch: true
experimental:
plugins:
badger:
moduleName: "github.com/fosrl/badger"
version: "v1.7.0"
log:
level: "INFO"
format: "common"
maxSize: 100
maxBackups: 3
maxAge: 3
compress: true
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
proxyProtocol: # Just accept everything for now!
trustedIPs:
- 0.0.0.0/0
- ::1/128
transport:
respondingTimeouts:
readTimeout: "30m"
http:
encodedCharacters:
allowEncodedSlash: true
allowEncodedQuestionMark: true
serversTransport:
insecureSkipVerify: true
ping:
entryPoint: "web"
@@ -0,0 +1,69 @@
name: pangolin
services:
pangolin:
image: docker.io/fosrl/pangolin:ee-latest
container_name: pangolin
restart: unless-stopped
deploy:
resources:
limits:
memory: 2g
reservations:
memory: 512m
ports:
- 53:53/udp # DNS
- 3000:3000 # Pangolin UI
volumes:
- ./config:/app/config
- ./config/certificates:/var/certificates
- ./config/dynamic:/var/dynamic
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
gerbil:
image: docker.io/fosrl/gerbil:latest
container_name: gerbil
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://<NODE1_INTERNAL_IP>:3004
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/
- --trusted-upstreams=<NODE1_EXTERNAL_IP>,<NODE2_EXTERNAL_IP>
volumes:
- ./config/:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 51820:51820/udp # wireguard
- 21820:21820/udp # relay
- 443:8443 # resources
- 80:80 # web
- 3004:3004 # gerbil api
traefik:
image: docker.io/traefik:v3.7.11
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
volumes:
- ./config/traefik:/etc/traefik:ro
- ./config/traefik/logs:/var/log/traefik
- ./config/certificates:/var/certificates:ro
- ./config/dynamic:/var/dynamic:ro
networks:
default:
driver: bridge
name: pangolin
@@ -0,0 +1,38 @@
# To see all available options, please visit the docs:
# https://docs.pangolin.net/
gerbil:
start_port: 51820
base_endpoint: "<THIS_NODE_EXTERNAL_IP>"
exit_node_name: "node2"
app:
dashboard_url: "https://pangolin.example.com"
log_level: "info"
postgres:
connection_string: postgresql://<POSTGRES_USERNAME>:<POSTGRES_PASSWORD>@<POSTGRES_INTERNAL_HOST>:5432/postgres
traefik:
site_types: ["newt"] # Wireguard and local sites are not support in clustering
file_mode: true # Pangolin will generate and save yaml files in a shared volume
server:
secret: "<SECRET>"
cors:
origins: ["https://pangolin.example.com"]
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
allowed_headers: ["X-CSRF-Token", "Content-Type"]
credentials: false
maxmind_db_path: "./config/GeoLite2-Country.mmdb" # Make sure to download and place into the config dir
maxmind_asn_path: "./config/GeoLite2-ASN.mmdb"
flags:
require_email_verification: false
disable_signup_without_invite: true
disable_user_create_org: false
allow_raw_resources: false
enable_acme_cert_sync: false
disable_local_sites: true
disable_basic_wireguard_sites: true
disable_config_managed_domains: true
@@ -0,0 +1,16 @@
app:
region: "region1"
identity_provider_mode: "org"
redis:
host: "<REDIS_INTERNAL_HOST>"
port: 6379
flags:
enable_redis: true
use_pangolin_dns: true
acme:
cert_mode: "pangolin"
dns:
enabled: true
nameserver_name: "ns.example.com"
cname_extension: "cname.example.com"
site_extension: "site.example.com" # Optional
@@ -0,0 +1,41 @@
providers:
file:
directory: "/var/dynamic"
watch: true
experimental:
plugins:
badger:
moduleName: "github.com/fosrl/badger"
version: "v1.7.0"
log:
level: "INFO"
format: "common"
maxSize: 100
maxBackups: 3
maxAge: 3
compress: true
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
proxyProtocol: # Just accept everything for now!
trustedIPs:
- 0.0.0.0/0
- ::1/128
transport:
respondingTimeouts:
readTimeout: "30m"
http:
encodedCharacters:
allowEncodedSlash: true
allowEncodedQuestionMark: true
serversTransport:
insecureSkipVerify: true
ping:
entryPoint: "web"
@@ -0,0 +1,69 @@
name: pangolin
services:
pangolin:
image: docker.io/fosrl/pangolin:ee-latest
container_name: pangolin
restart: unless-stopped
deploy:
resources:
limits:
memory: 2g
reservations:
memory: 512m
ports:
- 53:53/udp # DNS
- 3000:3000 # Pangolin UI
volumes:
- ./config:/app/config
- ./config/certificates:/var/certificates
- ./config/dynamic:/var/dynamic
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
gerbil:
image: docker.io/fosrl/gerbil:latest
container_name: gerbil
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://<NODE1_INTERNAL_IP>:3004
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/
- --trusted-upstreams=<NODE1_EXTERNAL_IP>,<NODE2_EXTERNAL_IP>
volumes:
- ./config/:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 51820:51820/udp # wireguard
- 21820:21820/udp # relay
- 443:8443 # resources
- 80:80 # web
- 3004:3004 # gerbil api
traefik:
image: docker.io/traefik:v3.7.11
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
volumes:
- ./config/traefik:/etc/traefik:ro
- ./config/traefik/logs:/var/log/traefik
- ./config/certificates:/var/certificates:ro
- ./config/dynamic:/var/dynamic:ro
networks:
default:
driver: bridge
name: pangolin
+2
View File
@@ -0,0 +1,2 @@
tls:
certificates: []
+1
View File
@@ -0,0 +1 @@
{}