mirror of
https://github.com/fosrl/docs-v2.git
synced 2026-07-20 14:51:28 +02:00
Merge branch 'main' into dev
This commit is contained in:
@@ -182,6 +182,7 @@
|
||||
"manage/remote-node/config-file"
|
||||
]
|
||||
},
|
||||
"manage/endpoints-and-pops",
|
||||
"manage/integration-api",
|
||||
"manage/branding"
|
||||
]
|
||||
@@ -232,7 +233,8 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"self-host/manual/podman-quadlets"
|
||||
]
|
||||
},
|
||||
"self-host/dns-and-networking",
|
||||
|
||||
62
manage/endpoints-and-pops.mdx
Normal file
62
manage/endpoints-and-pops.mdx
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "Endpoints & Relays"
|
||||
icon: "network-wired"
|
||||
description: "Pangolin Cloud hostnames for firewall allowlists and network whitelisting"
|
||||
---
|
||||
|
||||
import PangolinCloudTocCta from "/snippets/pangolin-cloud-toc-cta.mdx";
|
||||
|
||||
<PangolinCloudTocCta />
|
||||
|
||||
<Note>
|
||||
This page applies to [Pangolin Cloud](https://app.pangolin.net/auth/signup) only. If you self-host Pangolin, see [DNS & Networking](/self-host/dns-and-networking) for the hostnames and ports on your own deployment.
|
||||
</Note>
|
||||
|
||||
Use this page when you need to allowlist Pangolin in a corporate firewall, proxy, or egress policy. Sites, clients, and browsers connect to two classes of infrastructure on Pangolin Cloud: the **control plane** and **points of presence** (also called relays).
|
||||
|
||||
## Control Plane
|
||||
|
||||
The control plane is the central Pangolin service that runs the dashboard, REST API, authentication, and configuration orchestration.
|
||||
|
||||
| Hostname | Purpose |
|
||||
| --- | --- |
|
||||
| `app.pangolin.net` | Dashboard, API, authentication, and WebSocket configuration for sites and clients |
|
||||
|
||||
Sites and clients use this hostname as their **endpoint**. For example, Sites connect here to register, receive policy, and maintain their control channel.
|
||||
|
||||
## Points of Presence
|
||||
|
||||
Points of presence (PoPs), also called relays, are the networking edge where sites and clients connect for data traffic.
|
||||
|
||||
| Hostname |
|
||||
| --- |
|
||||
| `uranus.pop.pangolin.net` |
|
||||
| `mars.pop.pangolin.net` |
|
||||
| `earth.pop.pangolin.net` |
|
||||
| `jupiter.pop.pangolin.net` |
|
||||
| `saturn.pop.pangolin.net` |
|
||||
| `neptune.pop.pangolin.net` |
|
||||
| `pluto.pop.pangolin.net` |
|
||||
| `mercury.pop.pangolin.net` |
|
||||
|
||||
### What PoPs Do
|
||||
|
||||
- **Public resources** send all traffic through a PoP.
|
||||
- **Private resources** use PoPs to coordinate [NAT traversal](/manage/clients/nat-traversal) between a client and a site. If hole punching fails, traffic relays through the PoP instead.
|
||||
|
||||
Pangolin sites and clients automatically select the most optimal PoP to connect to. You do not choose a PoP manually during normal operation.
|
||||
|
||||
## Remote Nodes
|
||||
|
||||
If you use [remote nodes](/manage/remote-node/understanding-nodes), the point of presence (relay) for your traffic is your self-hosted node, not the cloud PoPs listed above.
|
||||
|
||||
The control plane endpoint for dashboard access, API calls, and authentication remains `app.pangolin.net`.
|
||||
|
||||
## Whitelisting Checklist
|
||||
|
||||
To allow Pangolin Cloud on an outbound firewall or proxy, permit traffic to:
|
||||
|
||||
1. `app.pangolin.net` for the control plane
|
||||
2. All `*.pop.pangolin.net` hostnames listed above for site and client data traffic
|
||||
|
||||
If you use remote nodes, also allow traffic to your remote node's public hostname or IP address.
|
||||
@@ -35,6 +35,10 @@ Wildcards allow you to define aliases that match multiple hostnames using specia
|
||||
|
||||
If you use a wildcard such as `*.proxy.internal`, it will match any hostname that ends with `.proxy.internal` and has something before the dot—such as `host.proxy.internal`, `longerhost.proxy.internal`, or even `sub.host.proxy.internal`. However, the wildcard will not match the base domain itself (`autoco.internal` without anything before the dot).
|
||||
|
||||
### .local TLD
|
||||
|
||||
The `.local` TLD is reserved for local networking and multicast DNS (mDNS). mDNS is commonly used by Apple Bonjour, Linux zeroconf, and limited Windows features. Because of this, aliases that use `.local` may not resolve reliably across many devices. We recommend using a subdomain you control (for example, `alias.mywebsite.com`) or a private/internal domain such as `alias.internal` or `alias.corp`.
|
||||
|
||||
## Custom Upstream DNS
|
||||
|
||||
Aliases work by overriding the DNS of your computer running the client so that all DNS requests are sent to the Pangolin client for resolution. That behavior is controlled by the Enable Aliases (Override DNS) preference; see [Configure Clients](/manage/clients/configure-client#enable-aliases-override-dns). The DNS server on your computer is typically `100.96.128.1` (the first address inside of your utility subnet on the org) when connected to the tunnel, which forwards requests to an upstream server. By default, we use `1.1.1.1`, but this upstream address can be configured in the CLI or in the client settings.
|
||||
|
||||
214
self-host/community-guides/traefik-hardening.mdx
Normal file
214
self-host/community-guides/traefik-hardening.mdx
Normal file
@@ -0,0 +1,214 @@
|
||||
---
|
||||
title: "Securing Traefik Post-Install"
|
||||
description: "Hardening steps after Traefik v3 install—split file provider rules, TLS options, security headers, HTTPS redirects, and EC384 ACME certificates."
|
||||
---
|
||||
|
||||
The steps I would take to secure Traefik and such post install.
|
||||
|
||||
Treat this as a template—adapt entrypoint names, paths, resolvers, and middleware references to your stack. Don't copy-paste the blocks wholesale without checking they match your setup.
|
||||
|
||||
A few I would recommend but this assumes you won't ever be doing http traffic.. (externally)
|
||||
|
||||
```yaml
|
||||
redirections: # Only caring for https based traffic.
|
||||
entryPoint:
|
||||
to: websecure # match your websecure entrypoint name.
|
||||
scheme: https
|
||||
permanent: true
|
||||
```
|
||||
|
||||
Into your traefik_dynamic though I would recommend actually splitting the files and using like,
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
file:
|
||||
directory: "/rules" # Path inside the container; must match the mount target (e.g. ./rules:/rules:ro below).
|
||||
watch: true
|
||||
```
|
||||
|
||||
By default it's using a file path which makes adding more file like stuff annoying,
|
||||
|
||||
```yaml
|
||||
traefik:
|
||||
image: traefik:v3
|
||||
container_name: traefik
|
||||
network_mode: service:gerbil
|
||||
restart: always
|
||||
secrets:
|
||||
- cf_dns_api_token
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./rules:/rules:ro # This
|
||||
```
|
||||
|
||||
Then we go in the folder relative of the compose,
|
||||
|
||||
You will need to move your traefik dynamic into this folder. Depending how you have set it up I would also recommend to be using wildcard certs with dns validation if you aren't already.
|
||||
|
||||
Anyways, `/rules/tls.yml`
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/traefik-v3-file-provider.json
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
minVersion: VersionTLS12
|
||||
maxVersion: VersionTLS13
|
||||
sniStrict: true # Clients without SNI or wrong hostname will fail; expected for strict TLS.
|
||||
# clientAuth:
|
||||
# # in PEM format. each file can contain multiple CAs.
|
||||
# caFiles:
|
||||
# - /certs/global/origin-pull-ca.pem
|
||||
# clientAuthType: RequireAndVerifyClientCert
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
||||
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
curvePreferences:
|
||||
- secp521r1
|
||||
- secp384r1
|
||||
- x25519
|
||||
```
|
||||
|
||||
`/rules/middlewares.yml`
|
||||
|
||||
Though would recommend to setup chains if you plan to have other middleware's to be aligned.. The above will score you an A+
|
||||
|
||||
```yaml
|
||||
http:
|
||||
middlewares:
|
||||
middlewares-secure-headers:
|
||||
headers:
|
||||
accessControlAllowMethods:
|
||||
- GET
|
||||
- OPTIONS
|
||||
- PUT
|
||||
hostsProxyHeaders:
|
||||
- "X-Forwarded-Host"
|
||||
#sslRedirect: true # Not used in Version 2.5
|
||||
stsSeconds: 63072000
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
forceSTSHeader: true
|
||||
frameDeny: true #overwritten by customFrameOptionsValue
|
||||
customFrameOptionsValue: "SAMEORIGIN"
|
||||
contentTypeNosniff: true
|
||||
browserXssFilter: false
|
||||
customBrowserXSSValue: "0"
|
||||
referrerPolicy: "same-origin"
|
||||
contentSecurityPolicy: "upgrade-insecure-requests"
|
||||
customResponseHeaders:
|
||||
X-Robots-Tag: "none, noarchive, nosnippet, notranslate, noimageindex"
|
||||
X-powered-by: ""
|
||||
server: ""
|
||||
Permissions-Policy: "camera=(), microphone=(), geolocation=()"
|
||||
|
||||
chain-secure:
|
||||
chain:
|
||||
middlewares:
|
||||
- middlewares-secure-headers
|
||||
```
|
||||
|
||||
In your `traefik_config.yml`
|
||||
|
||||
```yaml
|
||||
ocsp: {} # Let Traefik check for ocsp over clients,
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
http:
|
||||
redirections: # Only caring for https based traffic.
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
permanent: true
|
||||
# forwardedHeaders: # Don't need this unless you're using cloudflare infront or so
|
||||
# trustedIPs: *trustedIPs
|
||||
websecure:
|
||||
address: ":443"
|
||||
asDefault: true # Routers without an entryPoints list use websecure. Mostly useful for file entries an example shown at the very bottom.
|
||||
http3: # If you want to have QUIC you can have these just make sure to enable in the compose - 443:443/udp but if not you can skip this.
|
||||
advertisedPort: 443
|
||||
# transport: # Can lead to dos attacks if you're not careful.
|
||||
# respondingTimeouts:
|
||||
# readTimeout: "30m"
|
||||
http:
|
||||
middlewares:
|
||||
- middlewares-secure-headers@file # We don't include this on the web part as some stuff like HSTS and other headers aren't meant to be served on http at all and would go against spec like Fiesty duck's.
|
||||
tls:
|
||||
options: default # Can be named anything to align with the rules/tls.yml
|
||||
certResolver: dns # Match to your certResolver line in /config/traefik/traefik_config.yml
|
||||
|
||||
# forwardedHeaders: # Don't set this unless you know what you're doing.
|
||||
# trustedIPs: *trustedIPs # *trustedIPs must be a YAML anchor defined earlier in this file (e.g. Cloudflare IP ranges).
|
||||
|
||||
# proxyProtocol: # Don't set this unless you know what you're doing.
|
||||
# trustedIPs: *InternalIPs # *InternalIPs anchor—only enable if something in front actually sends PROXY protocol.
|
||||
```
|
||||
|
||||
Would also recommend to ask for EC384 certs which are more secure then RSA but also might limit older clients from connecting, You can do so via,
|
||||
|
||||
```yaml
|
||||
certificatesResolvers:
|
||||
dns:
|
||||
acme:
|
||||
storage: acme.json # Use a persistent volume path; file must be writable by Traefik and chmod'd to 600.
|
||||
keyType: 'EC384' # This asks LE for ECC certs over RSA, For reference a ECC384 is like asking for a RSA 7680-bit over like the normal RSA4096 or 2048's.
|
||||
dnsChallenge:
|
||||
provider: cloudflare # Change if you use another DNS host; provider must match your env/secrets setup.
|
||||
resolvers:
|
||||
- "1.1.1.1:53"
|
||||
- "1.0.0.1:53"
|
||||
```
|
||||
|
||||
## Pangolin dashboard (`/rules/pangolin.yml`)
|
||||
|
||||
Routers below use `chain-secure@file` from `/rules/middlewares.yml`.
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
next-router:
|
||||
rule: >
|
||||
(Host(`pangolin.{{ env "DOMAIN1" }}`))
|
||||
service: next-service
|
||||
middlewares:
|
||||
- chain-secure@file
|
||||
priority: 10
|
||||
|
||||
api-router:
|
||||
rule: >
|
||||
(Host(`pangolin.{{ env "DOMAIN1" }}`) && PathPrefix(`/api/v1`))
|
||||
service: api-service
|
||||
middlewares:
|
||||
- chain-secure@file
|
||||
priority: 100
|
||||
|
||||
services:
|
||||
next-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3002" # Next.js server
|
||||
|
||||
api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3000" # API/WebSocket server
|
||||
|
||||
tcp:
|
||||
serversTransports:
|
||||
pp-transport-v1:
|
||||
proxyProtocol:
|
||||
version: 1
|
||||
pp-transport-v2:
|
||||
proxyProtocol:
|
||||
version: 2
|
||||
```
|
||||
|
||||
What's not shown is like DANE-443 (TLSA), RoFS which I will write later along with the apparmor/seccomp profiles.
|
||||
494
self-host/manual/podman-quadlets.mdx
Normal file
494
self-host/manual/podman-quadlets.mdx
Normal file
@@ -0,0 +1,494 @@
|
||||
---
|
||||
title: "Podman Quadlets (Rootless)"
|
||||
description: "Deploy Pangolin manually using Podman Quadlets (rootless) without the automated installer"
|
||||
---
|
||||
|
||||
import PangolinCloudTocCta from "/snippets/pangolin-cloud-toc-cta.mdx";
|
||||
|
||||
<PangolinCloudTocCta />
|
||||
|
||||
This guide walks through a manual deployment using the same file layout the installer generates from `install/config/*` in the Pangolin source tree. Use it if you want the installer's defaults, but you want to create and maintain the files yourself.
|
||||
|
||||
This guide assumes you already have a Linux server with Podman installed and has been tested on Debian 13.5 with Podman version 5.4.2.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Review the [quick install guide](/self-host/quick-install) and [DNS & networking](/self-host/dns-and-networking) first. At minimum you need:
|
||||
|
||||
- A public Linux server
|
||||
- A base domain such as `example.com`
|
||||
- A dashboard hostname such as `pangolin.example.com`
|
||||
- An email address for Let's Encrypt
|
||||
- TCP ports `80` and `443` open
|
||||
- UDP ports `51820` and `21820` open if you are using tunneling
|
||||
|
||||
<Tip>
|
||||
If you do not want tunneling, see [Without Tunneling](/self-host/advanced/without-tunneling). In that mode you will skip the `gerbil` service and expose Traefik directly.
|
||||
</Tip>
|
||||
|
||||
<Note>
|
||||
`base domain` is the parent domain you will attach resources to, such as `example.com`. `dashboard hostname` is the specific hostname for the Pangolin UI and API, such as `pangolin.example.com`.
|
||||
</Note>
|
||||
|
||||
<Warning>
|
||||
## Note about ports 80 and 443
|
||||
By default, unprivileged users cannot bind to privileged ports (< 1024). Some workarounds for this include:
|
||||
1. Changing the unprivileged start port to 80
|
||||
2. Using `iptables` / `nftables` to redirect 80 and 443 to ports above 1023 (such as 8080 and 8443, respectively)
|
||||
|
||||
Configuring this is out of the scope of this guide, but many guides exist online for this exact situation.
|
||||
|
||||
**This guide assumes you use option 1.**
|
||||
</Warning>
|
||||
|
||||
|
||||
## File Layout
|
||||
|
||||
Create the following project structure:
|
||||
|
||||
```text
|
||||
~/.config/
|
||||
└── containers/
|
||||
└── systemd
|
||||
├── gerbil.container
|
||||
├── pangolin.container
|
||||
├── traefik.container
|
||||
├── services.pod
|
||||
└── config/
|
||||
├── config.yml
|
||||
├── db/
|
||||
├── letsencrypt/
|
||||
└── traefik/
|
||||
├── dynamic_config.yml
|
||||
├── logs/
|
||||
└── traefik_config.yml
|
||||
```
|
||||
|
||||
The following files are created later by the running services or added only when you enable optional features:
|
||||
|
||||
- `config/db/db.sqlite` is created by Pangolin on first startup.
|
||||
- `config/key` is created by Gerbil when tunneling is enabled.
|
||||
- `config/GeoLite2-Country.mmdb` is optional and only needed for [geo-blocking](/self-host/advanced/enable-geoblocking). It is not downloaded by the running services in a manual install; download it manually before enabling geo-blocking.
|
||||
|
||||
## Create the Directories
|
||||
|
||||
Create the project folders:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/containers/systemd # only needed if this is the first time you're running rootless Podman containers
|
||||
cd ~/.config/containers/systemd
|
||||
mkdir -p config/db config/letsencrypt config/traefik/logs
|
||||
```
|
||||
|
||||
## Create the Configuration Files
|
||||
|
||||
<Steps>
|
||||
<Step title="Create container and pod files">
|
||||
This section defines the Pangolin, Gerbil, and Traefik containers, the pod, their shared volumes, and the ports exposed on the host.
|
||||
|
||||
```ini title="pangolin.container"
|
||||
[Container]
|
||||
ContainerName=pangolin
|
||||
Image=docker.io/fosrl/pangolin:ee-latest
|
||||
|
||||
Pod=services.pod
|
||||
|
||||
HealthCmd=["curl","-f","http://localhost:3001/api/v1/"]
|
||||
HealthInterval=10s
|
||||
HealthRetries=15
|
||||
HealthTimeout=10s
|
||||
Notify=healthy
|
||||
|
||||
Volume=./config:/app/config
|
||||
Volume=./config/letsencrypt:/app/config/letsencrypt:ro
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
```ini title="gerbil.container"
|
||||
[Unit]
|
||||
After=pangolin.service
|
||||
Requires=pangolin.service
|
||||
|
||||
[Container]
|
||||
ContainerName=gerbil
|
||||
Image=docker.io/fosrl/gerbil:latest
|
||||
|
||||
Pod=services.pod
|
||||
|
||||
AddCapability=NET_ADMIN SYS_MODULE
|
||||
Exec='--reachableAt=http://localhost:3004' '--generateAndSaveKeyTo=/var/config/key' '--remoteConfig=http://localhost:3001/api/v1/'
|
||||
|
||||
Volume=./config/:/var/config
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
```ini title="traefik.container"
|
||||
[Unit]
|
||||
After=pangolin.service
|
||||
Requires=pangolin.service
|
||||
|
||||
[Container]
|
||||
ContainerName=traefik
|
||||
Image=docker.io/traefik:latest
|
||||
|
||||
Pod=services.pod
|
||||
|
||||
Exec='--configFile=/etc/traefik/traefik_config.yml'
|
||||
|
||||
Volume=./config/traefik:/etc/traefik:ro
|
||||
Volume=./config/letsencrypt:/letsencrypt
|
||||
Volume=./config/traefik/logs:/var/log/traefik
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
```ini title="services.pod"
|
||||
[Unit]
|
||||
Description=Pangolin Pod
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Pod]
|
||||
PodName=services
|
||||
|
||||
Network=pasta
|
||||
|
||||
PublishPort=51820:51820/udp
|
||||
PublishPort=21820:21820/udp
|
||||
PublishPort=443:443
|
||||
# Uncomment the line below if you enable HTTP/3 in Traefik.
|
||||
# PublishPort=443:443/udp
|
||||
PublishPort=80:80
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
<Note>
|
||||
This is the installer's default community layout with Gerbil enabled. If you want to pin releases instead of using `latest`, replace the image tags with the versions you intend to run.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Create config/traefik/traefik_config.yml">
|
||||
This file configures Traefik's providers, Badger plugin, Let's Encrypt resolver, entry points, logs, and health check endpoint.
|
||||
|
||||
```yaml title="config/traefik/traefik_config.yml"
|
||||
api:
|
||||
insecure: true
|
||||
dashboard: true
|
||||
|
||||
providers:
|
||||
http:
|
||||
endpoint: "http://localhost:3001/api/v1/traefik-config"
|
||||
pollInterval: "5s"
|
||||
file:
|
||||
filename: "/etc/traefik/dynamic_config.yml"
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "v1.4.0" # Check github.com/fosrl/badger for the latest release.
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
maxSize: 100
|
||||
maxBackups: 3
|
||||
maxAge: 3
|
||||
compress: true
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
storage: "/letsencrypt/acme.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
# Uncomment to enable HTTP/3. You must also expose 443/udp in services.pod.
|
||||
# http3:
|
||||
# advertisedPort: 443
|
||||
http:
|
||||
tls:
|
||||
certResolver: "letsencrypt"
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedQuestionMark: true
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
ping:
|
||||
entryPoint: "web"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Traefik stores Let's Encrypt certificates at `/letsencrypt/acme.json` inside the container. The container file mounts that path from `./config/letsencrypt`, so Traefik will create `config/letsencrypt/acme.json` when it needs certificate storage.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Create config/traefik/dynamic_config.yml">
|
||||
This file defines the routers, middleware, and services that send dashboard, API, and WebSocket traffic to Pangolin.
|
||||
|
||||
```yaml title="config/traefik/dynamic_config.yml"
|
||||
http:
|
||||
middlewares:
|
||||
badger:
|
||||
plugin:
|
||||
badger:
|
||||
disableForwardAuth: true
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
|
||||
routers:
|
||||
main-app-router-redirect:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- redirect-to-https
|
||||
- badger
|
||||
|
||||
next-router:
|
||||
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
middlewares:
|
||||
- badger
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
api-router:
|
||||
rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
middlewares:
|
||||
- badger
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
ws-router:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
middlewares:
|
||||
- badger
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
services:
|
||||
next-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:3002"
|
||||
|
||||
api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:3000"
|
||||
|
||||
tcp:
|
||||
serversTransports:
|
||||
pp-transport-v1:
|
||||
proxyProtocol:
|
||||
version: 1
|
||||
pp-transport-v2:
|
||||
proxyProtocol:
|
||||
version: 2
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create config/config.yml">
|
||||
This file contains Pangolin's application settings, dashboard domain, base domain, CORS origin, and server secret.
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
# To see all available options, please visit the docs:
|
||||
# https://docs.pangolin.net/
|
||||
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "pangolin.example.com" # REPLACE WITH YOUR DASHBOARD DOMAIN
|
||||
|
||||
app:
|
||||
dashboard_url: "https://pangolin.example.com" # REPLACE WITH YOUR DASHBOARD DOMAIN
|
||||
log_level: "info"
|
||||
telemetry:
|
||||
anonymous_usage: true
|
||||
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: "example.com" # REPLACE WITH YOUR BASE DOMAIN
|
||||
|
||||
server:
|
||||
secret: "replace-with-a-long-random-secret" # REPLACE WITH SECURE SECRET
|
||||
cors:
|
||||
origins: ["https://pangolin.example.com"] # REPLACE WITH YOUR DASHBOARD DOMAIN
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
||||
allowed_headers: ["X-CSRF-Token", "Content-Type"]
|
||||
credentials: false
|
||||
|
||||
flags:
|
||||
require_email_verification: false
|
||||
disable_signup_without_invite: true
|
||||
disable_user_create_org: false
|
||||
allow_raw_resources: true
|
||||
```
|
||||
|
||||
Replace these values before starting the stack:
|
||||
|
||||
- `pangolin.example.com` with your dashboard hostname
|
||||
- `example.com` with your base domain
|
||||
- `replace-with-a-long-random-secret` with a strong random secret
|
||||
- `admin@example.com` in `traefik_config.yml` with your Let's Encrypt email
|
||||
|
||||
Generate a secret with:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not reuse a weak or short `server.secret`. If you need to rotate it later, use `pangctl rotate-server-secret`. See the [container CLI tool guide](/self-host/advanced/container-cli-tool#rotate-server-secret).
|
||||
Please note you will need to run `podman exec ...` instead of `docker exec ...`.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Optional Email Configuration
|
||||
|
||||
If you want Pangolin to send email, add this block to `config/config.yml` and set `flags.require_email_verification` to `true`:
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
email:
|
||||
smtp_host: "smtp.example.com"
|
||||
smtp_port: 587
|
||||
smtp_user: "smtp-user"
|
||||
smtp_pass: "smtp-password"
|
||||
no_reply: "noreply@example.com"
|
||||
```
|
||||
|
||||
### Optional Geo-blocking Configuration
|
||||
|
||||
If you want geo-blocking, download the MaxMind database and add this line under `server`:
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
server:
|
||||
maxmind_db_path: "./config/GeoLite2-Country.mmdb"
|
||||
```
|
||||
|
||||
See [Enable Geo-blocking](/self-host/advanced/enable-geoblocking) for the full process.
|
||||
|
||||
## Start the Stack
|
||||
|
||||
<Steps>
|
||||
<Step title="Reload and start the services">
|
||||
```bash
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user start services-pod
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Enable lingering so that services stay up after you log out">
|
||||
```bash
|
||||
loginctl enable-linger $USER
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Watch the logs">
|
||||
```bash
|
||||
podman logs -f pangolin traefik gerbil
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Verify the containers are healthy">
|
||||
```bash
|
||||
podman ps -a
|
||||
```
|
||||
|
||||
`pangolin`, `traefik`, and `gerbil` should all report as running after the first startup finishes.
|
||||
</Step>
|
||||
|
||||
<Step title="Get the setup token from the Pangolin logs">
|
||||
Check the Pangolin container logs:
|
||||
|
||||
```bash
|
||||
podman logs pangolin
|
||||
```
|
||||
|
||||
Pangolin prints a setup token to stdout on first boot. Copy that token before continuing.
|
||||
</Step>
|
||||
|
||||
<Step title="Open the initial setup page">
|
||||
Visit:
|
||||
|
||||
```text
|
||||
https://pangolin.example.com/auth/initial-setup
|
||||
```
|
||||
|
||||
Replace the hostname with your real dashboard domain, then use the setup token from the Pangolin logs to register the first admin account.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Verify the Setup
|
||||
|
||||
You should expect the following on a healthy first install:
|
||||
|
||||
- `podman ps -a` shows `pangolin`, `traefik`, and `gerbil` as running.
|
||||
- `podman logs pangolin` includes the one-time setup token for the first admin account.
|
||||
- Visiting `https://<your-dashboard-domain>/auth/initial-setup` loads the setup page.
|
||||
- `config/db/db.sqlite` exists after Pangolin starts.
|
||||
- `config/key` exists after Gerbil starts.
|
||||
|
||||
<Tip>
|
||||
The first Let's Encrypt certificate request can take a short while. If the page initially shows a certificate warning, wait a minute and refresh.
|
||||
</Tip>
|
||||
|
||||
## If Something Fails
|
||||
|
||||
- If the setup page does not load, confirm your DNS record points to the server and ports `80` and `443` are reachable.
|
||||
- If you cannot complete first-time signup, check `podman logs pangolin` and copy the setup token printed by Pangolin.
|
||||
- If certificates are not issued, confirm `admin@example.com` was replaced and that nothing else is already bound to ports `80` or `443` (or whatever alternate ports you selected on the host).
|
||||
- If `pangolin` never becomes healthy, inspect `podman logs -f pangolin`.
|
||||
- If tunneling does not work, inspect `podman logs -f gerbil` and confirm UDP ports `51820` and `21820` are open.
|
||||
- If Traefik serves the wrong host, re-check every `pangolin.example.com` replacement in both Traefik files and `config/config.yml`.
|
||||
|
||||
## Without Tunneling
|
||||
|
||||
If you do not want Gerbil:
|
||||
|
||||
- Remove the gerbil service.
|
||||
- Remove the `gerbil` block from `config/config.yml`.
|
||||
|
||||
That mode is covered in more detail in [Without Tunneling](/self-host/advanced/without-tunneling).
|
||||
Reference in New Issue
Block a user