detached db23f3a

docs(wiki): cleanup broken links and add secure-vps-setup guide

- Remove broken wiki links (nextjs-deploy, multi-service-app, cloudflare-setup, development.md)
- Fix getting-started confusion in wiki tutorials (point to docs)
- Add status command documentation
- Add secure-vps-setup guide (Tailscale SSH, firewalld, CrowdSec)
- Update CLI index with status doc link
commit
db23f3adad6bbeeb68f536730a36bcac7a1f7df2
author
bnema · · 2026-01-21 21:47 UTC
parent
63c01ba
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
7 files · +415 -26
M docs/cli/index.md +1 -1
@@ -25,7 +25,7 @@ Commands are organized by where they run:
2525 | `gordon deploy` | Manually deploy or redeploy a route | [serve](./serve.md#gordon-deploy) |
2626 | `gordon reload` | Reload configuration and sync containers | [serve](./serve.md#gordon-reload) |
2727 | `gordon logs` | Display Gordon process or container logs | [serve](./serve.md#gordon-logs) |
28-| `gordon status` | Show Gordon server status | - |
28+| `gordon status` | Show Gordon server status | [status](./status.md) |
2929
3030 ## Client Commands
3131
@@ -25,7 +25,7 @@ Commands are organized by where they run:
25 | `gordon deploy` | Manually deploy or redeploy a route | [serve](./serve.md#gordon-deploy) |25 | `gordon deploy` | Manually deploy or redeploy a route | [serve](./serve.md#gordon-deploy) |
26 | `gordon reload` | Reload configuration and sync containers | [serve](./serve.md#gordon-reload) |26 | `gordon reload` | Reload configuration and sync containers | [serve](./serve.md#gordon-reload) |
27 | `gordon logs` | Display Gordon process or container logs | [serve](./serve.md#gordon-logs) |27 | `gordon logs` | Display Gordon process or container logs | [serve](./serve.md#gordon-logs) |
28-| `gordon status` | Show Gordon server status | - |28+| `gordon status` | Show Gordon server status | [status](./status.md) |
29 29
30 ## Client Commands30 ## Client Commands
31 31
A docs/cli/status.md +105 -0
@@ -0,0 +1,105 @@
1+# Status Command
2+
3+Show Gordon server status and container health.
4+
5+## gordon status
6+
7+Display server configuration and container status for all routes.
8+
9+```bash
10+gordon status --remote https://gordon.mydomain.com --token $TOKEN
11+```
12+
13+> **Note:** The status command requires remote mode. Use `--remote` flag or set `GORDON_REMOTE` environment variable.
14+
15+### Output
16+
17+```
18+Gordon Status
19+
20+Domain: registry.example.com
21+Registry Port: 5000
22+Server Port: 8080
23+Routes: 3
24+Auto-Route: true
25+Network Isolation: false
26+
27+Container Status:
28+ app.example.com: running
29+ api.example.com: running
30+ worker.example.com: stopped
31+```
32+
33+### Information Displayed
34+
35+| Field | Description |
36+|-------|-------------|
37+| Domain | Registry domain from configuration |
38+| Registry Port | Docker registry port |
39+| Server Port | Gordon admin API port |
40+| Routes | Total configured routes |
41+| Auto-Route | Whether auto-routing is enabled |
42+| Network Isolation | Whether network isolation is enabled |
43+| Container Status | Status of each route's container |
44+
45+### Container States
46+
47+| State | Description |
48+|-------|-------------|
49+| running | Container is running and healthy |
50+| stopped | Container was stopped |
51+| exited | Container exited (check logs for errors) |
52+| paused | Container is paused |
53+| unknown | Unable to determine container state |
54+
55+## Flags
56+
57+The status command uses global flags for remote access:
58+
59+| Flag | Description |
60+|------|-------------|
61+| `--remote` | Remote Gordon URL |
62+| `--token` | Authentication token |
63+
64+## Environment Variables
65+
66+| Variable | Description |
67+|----------|-------------|
68+| `GORDON_REMOTE` | Remote Gordon URL |
69+| `GORDON_TOKEN` | Authentication token |
70+
71+## Examples
72+
73+### Check Remote Server Status
74+
75+```bash
76+# Using flags
77+gordon status --remote https://gordon.mydomain.com --token $TOKEN
78+
79+# Using environment variables
80+export GORDON_REMOTE=https://gordon.mydomain.com
81+export GORDON_TOKEN=your-token
82+gordon status
83+```
84+
85+### Quick Health Check
86+
87+```bash
88+# Check if all containers are running
89+gordon status --remote https://gordon.mydomain.com --token $TOKEN | grep -E "(running|stopped|exited)"
90+```
91+
92+## Required Permissions
93+
94+The status endpoint requires `admin:status:read` scope in the authentication token.
95+
96+```bash
97+# Generate token with required scope
98+gordon auth token generate --subject admin --scopes admin:status:read
99+```
100+
101+## Related
102+
103+- [Serve Command](./serve.md)
104+- [Routes Command](./routes.md)
105+- [Remote CLI Management](/wiki/guides/remote-cli.md)
@@ -0,0 +1,105 @@
1+# Status Command
2+
3+Show Gordon server status and container health.
4+
5+## gordon status
6+
7+Display server configuration and container status for all routes.
8+
9+```bash
10+gordon status --remote https://gordon.mydomain.com --token $TOKEN
11+```
12+
13+> **Note:** The status command requires remote mode. Use `--remote` flag or set `GORDON_REMOTE` environment variable.
14+
15+### Output
16+
17+```
18+Gordon Status
19+
20+Domain: registry.example.com
21+Registry Port: 5000
22+Server Port: 8080
23+Routes: 3
24+Auto-Route: true
25+Network Isolation: false
26+
27+Container Status:
28+ app.example.com: running
29+ api.example.com: running
30+ worker.example.com: stopped
31+```
32+
33+### Information Displayed
34+
35+| Field | Description |
36+|-------|-------------|
37+| Domain | Registry domain from configuration |
38+| Registry Port | Docker registry port |
39+| Server Port | Gordon admin API port |
40+| Routes | Total configured routes |
41+| Auto-Route | Whether auto-routing is enabled |
42+| Network Isolation | Whether network isolation is enabled |
43+| Container Status | Status of each route's container |
44+
45+### Container States
46+
47+| State | Description |
48+|-------|-------------|
49+| running | Container is running and healthy |
50+| stopped | Container was stopped |
51+| exited | Container exited (check logs for errors) |
52+| paused | Container is paused |
53+| unknown | Unable to determine container state |
54+
55+## Flags
56+
57+The status command uses global flags for remote access:
58+
59+| Flag | Description |
60+|------|-------------|
61+| `--remote` | Remote Gordon URL |
62+| `--token` | Authentication token |
63+
64+## Environment Variables
65+
66+| Variable | Description |
67+|----------|-------------|
68+| `GORDON_REMOTE` | Remote Gordon URL |
69+| `GORDON_TOKEN` | Authentication token |
70+
71+## Examples
72+
73+### Check Remote Server Status
74+
75+```bash
76+# Using flags
77+gordon status --remote https://gordon.mydomain.com --token $TOKEN
78+
79+# Using environment variables
80+export GORDON_REMOTE=https://gordon.mydomain.com
81+export GORDON_TOKEN=your-token
82+gordon status
83+```
84+
85+### Quick Health Check
86+
87+```bash
88+# Check if all containers are running
89+gordon status --remote https://gordon.mydomain.com --token $TOKEN | grep -E "(running|stopped|exited)"
90+```
91+
92+## Required Permissions
93+
94+The status endpoint requires `admin:status:read` scope in the authentication token.
95+
96+```bash
97+# Generate token with required scope
98+gordon auth token generate --subject admin --scopes admin:status:read
99+```
100+
101+## Related
102+
103+- [Serve Command](./serve.md)
104+- [Routes Command](./routes.md)
105+- [Remote CLI Management](/wiki/guides/remote-cli.md)
M wiki/examples/index.md +0 -18
@@ -6,7 +6,6 @@ Annotated Gordon configuration examples for common scenarios.
66
77 - [Minimal Configuration](./minimal.md) - Simplest working setup
88 - [Production Configuration](./production.md) - Full production setup
9-- [Development Configuration](./development.md) - Local development setup
109
1110 ## Quick Reference
1211
@@ -20,23 +19,6 @@ gordon_domain = "gordon.local"
2019 "app.local" = "myapp:latest"
2120 ```
2221
23-### Development
24-
25-```toml
26-[server]
27-port = 8080
28-gordon_domain = "gordon.local"
29-
30-[auth]
31-enabled = false
32-
33-[auto_route]
34-enabled = true
35-
36-[routes]
37-"app.local" = "myapp:latest"
38-```
39-
4022 ### Production
4123
4224 ```toml
@@ -6,7 +6,6 @@ Annotated Gordon configuration examples for common scenarios.
6 6
7 - [Minimal Configuration](./minimal.md) - Simplest working setup7 - [Minimal Configuration](./minimal.md) - Simplest working setup
8 - [Production Configuration](./production.md) - Full production setup8 - [Production Configuration](./production.md) - Full production setup
9-- [Development Configuration](./development.md) - Local development setup
10 9
11 ## Quick Reference10 ## Quick Reference
12 11
@@ -20,23 +19,6 @@ gordon_domain = "gordon.local"
20 "app.local" = "myapp:latest"19 "app.local" = "myapp:latest"
21 ```20 ```
22 21
23-### Development
24-
25-```toml
26-[server]
27-port = 8080
28-gordon_domain = "gordon.local"
29-
30-[auth]
31-enabled = false
32-
33-[auto_route]
34-enabled = true
35-
36-[routes]
37-"app.local" = "myapp:latest"
38-```
39-
40 ### Production22 ### Production
41 23
42 ```toml24 ```toml
M wiki/guides/index.md +1 -1
@@ -4,12 +4,12 @@ In-depth guides for specific Gordon setups and integrations.
44
55 ## Server Setup
66
7+- [Secure VPS Setup](./secure-vps-setup.md) - Tailscale SSH, firewalld, and CrowdSec
78 - [Running Gordon in a Container](./running-in-container.md) - Deploy Gordon itself in Docker or Podman
89 - [Podman Rootless Setup](./podman-rootless.md) - Enhanced security with rootless containers
910
1011 ## TLS & Load Balancing
1112
12-- [Cloudflare DNS & Proxy Setup](./cloudflare-setup.md) - HTTPS termination with Cloudflare (recommended)
1313 - [AWS Application Load Balancer](./aws-alb.md) - Deploy behind AWS ALB
1414 - [Kubernetes Ingress](./kubernetes-ingress.md) - Deploy behind Kubernetes ingress controller
1515
@@ -4,12 +4,12 @@ In-depth guides for specific Gordon setups and integrations.
4 4
5 ## Server Setup5 ## Server Setup
6 6
7+- [Secure VPS Setup](./secure-vps-setup.md) - Tailscale SSH, firewalld, and CrowdSec
7 - [Running Gordon in a Container](./running-in-container.md) - Deploy Gordon itself in Docker or Podman8 - [Running Gordon in a Container](./running-in-container.md) - Deploy Gordon itself in Docker or Podman
8 - [Podman Rootless Setup](./podman-rootless.md) - Enhanced security with rootless containers9 - [Podman Rootless Setup](./podman-rootless.md) - Enhanced security with rootless containers
9 10
10 ## TLS & Load Balancing11 ## TLS & Load Balancing
11 12
12-- [Cloudflare DNS & Proxy Setup](./cloudflare-setup.md) - HTTPS termination with Cloudflare (recommended)
13 - [AWS Application Load Balancer](./aws-alb.md) - Deploy behind AWS ALB13 - [AWS Application Load Balancer](./aws-alb.md) - Deploy behind AWS ALB
14 - [Kubernetes Ingress](./kubernetes-ingress.md) - Deploy behind Kubernetes ingress controller14 - [Kubernetes Ingress](./kubernetes-ingress.md) - Deploy behind Kubernetes ingress controller
15 15
A wiki/guides/secure-vps-setup.md +301 -0
@@ -0,0 +1,301 @@
1+# Secure VPS Setup
2+
3+A production-ready VPS security configuration with Tailscale-only SSH access and CrowdSec intrusion prevention.
4+
5+## Overview
6+
7+This guide configures three security layers:
8+
9+| Layer | Protection |
10+|-------|------------|
11+| SSH | Tailscale only (invisible to internet) |
12+| Firewall | firewalld with DROP policy |
13+| IPS | CrowdSec with community blocklists |
14+
15+## Prerequisites
16+
17+- Fresh Ubuntu 24.04 VPS
18+- Tailscale account
19+- Initial SSH access via public IP (temporary)
20+- Cloudflare account for HTTPS termination
21+
22+---
23+
24+## Step 1: Initial System Hardening
25+
26+Before anything else, enable automatic security updates and time sync:
27+
28+```bash
29+# Automatic security updates
30+apt update && apt install -y unattended-upgrades
31+dpkg-reconfigure -plow unattended-upgrades
32+
33+# Verify time sync (enabled by default on Ubuntu 24.04)
34+timedatectl set-ntp true
35+timedatectl status
36+
37+# Basic kernel hardening
38+cat > /etc/sysctl.d/99-security.conf << 'EOF'
39+# SYN flood protection
40+net.ipv4.tcp_syncookies = 1
41+
42+# Reverse path filtering (anti-spoofing)
43+net.ipv4.conf.all.rp_filter = 1
44+net.ipv4.conf.default.rp_filter = 1
45+
46+# Disable ICMP redirects
47+net.ipv4.conf.all.accept_redirects = 0
48+net.ipv4.conf.all.send_redirects = 0
49+net.ipv6.conf.all.accept_redirects = 0
50+
51+# Ignore ICMP broadcasts
52+net.ipv4.icmp_echo_ignore_broadcasts = 1
53+EOF
54+
55+sysctl --system
56+```
57+
58+## Step 2: Install Tailscale
59+
60+> **Important**: Use the official repository, NOT the Snap package (has AppArmor issues with Tailscale SSH on Ubuntu 24.04).
61+
62+```bash
63+# Add official Tailscale repository
64+curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | \
65+ tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
66+curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | \
67+ tee /etc/apt/sources.list.d/tailscale.list
68+
69+# Install
70+apt update
71+apt install -y tailscale
72+```
73+
74+## Step 3: Connect Tailscale with SSH
75+
76+```bash
77+tailscale up --ssh
78+```
79+
80+This outputs an authentication URL. Open it and authenticate with your Tailscale account.
81+
82+**Important**: After connecting, go to https://login.tailscale.com/admin/machines and **disable key expiry** for this server to prevent lockouts.
83+
84+## Step 4: Configure firewalld
85+
86+```bash
87+# Install firewalld
88+apt install -y firewalld
89+
90+# Enable and start
91+systemctl enable --now firewalld
92+
93+# Trust Tailscale interface (allows all traffic from your tailnet)
94+firewall-cmd --permanent --zone=trusted --add-interface=tailscale0
95+
96+# Allow Tailscale UDP port (WireGuard)
97+firewall-cmd --permanent --add-port=41641/udp
98+
99+# Allow web traffic (for Gordon reverse proxy)
100+firewall-cmd --permanent --add-service=http
101+firewall-cmd --permanent --add-service=https
102+
103+# Apply changes
104+firewall-cmd --reload
105+```
106+
107+> **Note**: Gordon's registry is proxied through port 80/443 via `gordon_domain`. Port 5000 does NOT need to be exposed publicly.
108+
109+## Step 5: Verify Tailscale SSH Works
110+
111+From another machine on your tailnet:
112+
113+```bash
114+# Get the server's Tailscale hostname
115+tailscale status
116+
117+# Connect from your local machine
118+ssh root@<tailscale-hostname>
119+# or
120+tailscale ssh root@<tailscale-hostname>
121+```
122+
123+## Step 6: Block Public SSH
124+
125+**Only proceed once Tailscale SSH is confirmed working.**
126+
127+```bash
128+# Remove SSH from public zone
129+firewall-cmd --permanent --remove-service=ssh
130+
131+# Set default policy to DROP all unmatched traffic
132+firewall-cmd --permanent --zone=public --set-target=DROP
133+
134+# Apply changes
135+firewall-cmd --reload
136+```
137+
138+Verify public SSH is blocked:
139+
140+```bash
141+# From another machine (not on tailnet)
142+ssh -o ConnectTimeout=5 root@<public-ip> # Should timeout
143+```
144+
145+---
146+
147+## Step 7: Install CrowdSec
148+
149+```bash
150+# Add CrowdSec repo (NOT Ubuntu's outdated version)
151+curl -s https://install.crowdsec.net | bash
152+
153+# Install CrowdSec
154+apt update
155+apt install -y crowdsec
156+```
157+
158+## Step 8: Install nftables Bouncer
159+
160+Ubuntu 24.04 uses nftables by default:
161+
162+```bash
163+apt install -y crowdsec-firewall-bouncer-nftables
164+systemctl enable --now crowdsec-firewall-bouncer
165+```
166+
167+## Step 9: Install Security Collections
168+
169+```bash
170+# HTTP protection
171+cscli collections install crowdsecurity/http-cve
172+cscli collections install crowdsecurity/base-http-scenarios
173+
174+# Reload to apply
175+systemctl reload crowdsec
176+```
177+
178+## Step 10: Whitelist Tailscale IPs
179+
180+Prevent CrowdSec from blocking Tailscale traffic:
181+
182+```bash
183+cat > /etc/crowdsec/parsers/s02-enrich/tailscale-whitelist.yaml << 'EOF'
184+name: tailscale-whitelist
185+description: "Whitelist Tailscale CGNAT range"
186+whitelist:
187+ reason: "Tailscale internal traffic"
188+ cidr:
189+ - "100.64.0.0/10"
190+EOF
191+
192+systemctl reload crowdsec
193+```
194+
195+## Step 11: Enroll in CrowdSec Console
196+
197+This enables community blocklists - IPs blocked across all CrowdSec users globally (15,000+ malicious IPs).
198+
199+1. Create account at https://app.crowdsec.net
200+2. Go to **Security Engines** → **Add Security Engine**
201+3. Copy enrollment key and run:
202+
203+```bash
204+cscli console enroll <your-enrollment-key>
205+```
206+
207+4. Accept the enrollment in the CrowdSec console
208+5. Enable console management:
209+
210+```bash
211+systemctl restart crowdsec
212+cscli console enable console_management
213+systemctl reload crowdsec
214+```
215+
216+---
217+
218+## Verification
219+
220+```bash
221+# Firewall status
222+firewall-cmd --state
223+firewall-cmd --list-all
224+firewall-cmd --list-all --zone=trusted
225+
226+# CrowdSec status
227+cscli metrics
228+cscli bouncers list
229+cscli console status
230+
231+# Verify nftables has CrowdSec rules
232+nft list ruleset | grep -A5 'crowdsec'
233+
234+# View blocked IPs count
235+cscli decisions list | wc -l
236+```
237+
238+---
239+
240+## Port Summary
241+
242+| Port | Protocol | Status | Purpose |
243+|------|----------|--------|---------|
244+| 22 | TCP | Closed | SSH (use Tailscale instead) |
245+| 80 | TCP | Open | HTTP (Gordon proxy) |
246+| 443 | TCP | Open | HTTPS (Gordon proxy via Cloudflare) |
247+| 5000 | TCP | Closed | Registry (proxied through 80/443) |
248+| 41641 | UDP | Open | Tailscale WireGuard |
249+
250+---
251+
252+## Troubleshooting
253+
254+### Can't SSH via Tailscale
255+
256+Check for ACL issues:
257+
258+```bash
259+journalctl -u tailscaled --since "10 minutes ago" | grep -i "rejected\|acl"
260+```
261+
262+If you see `rejected due to acl`, check your Tailscale ACLs at https://login.tailscale.com/admin/acls
263+
264+### Locked out completely
265+
266+Use your VPS provider's console access (e.g., Hetzner Cloud Console):
267+
268+```bash
269+firewall-cmd --add-service=ssh # Temporarily re-enable public SSH
270+```
271+
272+### CrowdSec not blocking
273+
274+Verify bouncer is running:
275+
276+```bash
277+systemctl status crowdsec-firewall-bouncer
278+cscli bouncers list # Should show "Valid"
279+```
280+
281+### Check what's being blocked
282+
283+```bash
284+cscli decisions list
285+cscli alerts list
286+```
287+
288+---
289+
290+## Next Steps
291+
292+- [Install Gordon](/docs/getting-started.md)
293+- [Podman Rootless Setup](./podman-rootless.md) - Enhanced container isolation
294+- [Cloudflare DNS & Proxy Setup](#) - HTTPS termination (coming soon)
295+
296+## References
297+
298+- [Tailscale SSH Documentation](https://tailscale.com/kb/1193/tailscale-ssh)
299+- [firewalld Documentation](https://firewalld.org/documentation/)
300+- [CrowdSec Documentation](https://docs.crowdsec.net/)
301+- [CrowdSec Console](https://app.crowdsec.net)
@@ -0,0 +1,301 @@
1+# Secure VPS Setup
2+
3+A production-ready VPS security configuration with Tailscale-only SSH access and CrowdSec intrusion prevention.
4+
5+## Overview
6+
7+This guide configures three security layers:
8+
9+| Layer | Protection |
10+|-------|------------|
11+| SSH | Tailscale only (invisible to internet) |
12+| Firewall | firewalld with DROP policy |
13+| IPS | CrowdSec with community blocklists |
14+
15+## Prerequisites
16+
17+- Fresh Ubuntu 24.04 VPS
18+- Tailscale account
19+- Initial SSH access via public IP (temporary)
20+- Cloudflare account for HTTPS termination
21+
22+---
23+
24+## Step 1: Initial System Hardening
25+
26+Before anything else, enable automatic security updates and time sync:
27+
28+```bash
29+# Automatic security updates
30+apt update && apt install -y unattended-upgrades
31+dpkg-reconfigure -plow unattended-upgrades
32+
33+# Verify time sync (enabled by default on Ubuntu 24.04)
34+timedatectl set-ntp true
35+timedatectl status
36+
37+# Basic kernel hardening
38+cat > /etc/sysctl.d/99-security.conf << 'EOF'
39+# SYN flood protection
40+net.ipv4.tcp_syncookies = 1
41+
42+# Reverse path filtering (anti-spoofing)
43+net.ipv4.conf.all.rp_filter = 1
44+net.ipv4.conf.default.rp_filter = 1
45+
46+# Disable ICMP redirects
47+net.ipv4.conf.all.accept_redirects = 0
48+net.ipv4.conf.all.send_redirects = 0
49+net.ipv6.conf.all.accept_redirects = 0
50+
51+# Ignore ICMP broadcasts
52+net.ipv4.icmp_echo_ignore_broadcasts = 1
53+EOF
54+
55+sysctl --system
56+```
57+
58+## Step 2: Install Tailscale
59+
60+> **Important**: Use the official repository, NOT the Snap package (has AppArmor issues with Tailscale SSH on Ubuntu 24.04).
61+
62+```bash
63+# Add official Tailscale repository
64+curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | \
65+ tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
66+curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | \
67+ tee /etc/apt/sources.list.d/tailscale.list
68+
69+# Install
70+apt update
71+apt install -y tailscale
72+```
73+
74+## Step 3: Connect Tailscale with SSH
75+
76+```bash
77+tailscale up --ssh
78+```
79+
80+This outputs an authentication URL. Open it and authenticate with your Tailscale account.
81+
82+**Important**: After connecting, go to https://login.tailscale.com/admin/machines and **disable key expiry** for this server to prevent lockouts.
83+
84+## Step 4: Configure firewalld
85+
86+```bash
87+# Install firewalld
88+apt install -y firewalld
89+
90+# Enable and start
91+systemctl enable --now firewalld
92+
93+# Trust Tailscale interface (allows all traffic from your tailnet)
94+firewall-cmd --permanent --zone=trusted --add-interface=tailscale0
95+
96+# Allow Tailscale UDP port (WireGuard)
97+firewall-cmd --permanent --add-port=41641/udp
98+
99+# Allow web traffic (for Gordon reverse proxy)
100+firewall-cmd --permanent --add-service=http
101+firewall-cmd --permanent --add-service=https
102+
103+# Apply changes
104+firewall-cmd --reload
105+```
106+
107+> **Note**: Gordon's registry is proxied through port 80/443 via `gordon_domain`. Port 5000 does NOT need to be exposed publicly.
108+
109+## Step 5: Verify Tailscale SSH Works
110+
111+From another machine on your tailnet:
112+
113+```bash
114+# Get the server's Tailscale hostname
115+tailscale status
116+
117+# Connect from your local machine
118+ssh root@<tailscale-hostname>
119+# or
120+tailscale ssh root@<tailscale-hostname>
121+```
122+
123+## Step 6: Block Public SSH
124+
125+**Only proceed once Tailscale SSH is confirmed working.**
126+
127+```bash
128+# Remove SSH from public zone
129+firewall-cmd --permanent --remove-service=ssh
130+
131+# Set default policy to DROP all unmatched traffic
132+firewall-cmd --permanent --zone=public --set-target=DROP
133+
134+# Apply changes
135+firewall-cmd --reload
136+```
137+
138+Verify public SSH is blocked:
139+
140+```bash
141+# From another machine (not on tailnet)
142+ssh -o ConnectTimeout=5 root@<public-ip> # Should timeout
143+```
144+
145+---
146+
147+## Step 7: Install CrowdSec
148+
149+```bash
150+# Add CrowdSec repo (NOT Ubuntu's outdated version)
151+curl -s https://install.crowdsec.net | bash
152+
153+# Install CrowdSec
154+apt update
155+apt install -y crowdsec
156+```
157+
158+## Step 8: Install nftables Bouncer
159+
160+Ubuntu 24.04 uses nftables by default:
161+
162+```bash
163+apt install -y crowdsec-firewall-bouncer-nftables
164+systemctl enable --now crowdsec-firewall-bouncer
165+```
166+
167+## Step 9: Install Security Collections
168+
169+```bash
170+# HTTP protection
171+cscli collections install crowdsecurity/http-cve
172+cscli collections install crowdsecurity/base-http-scenarios
173+
174+# Reload to apply
175+systemctl reload crowdsec
176+```
177+
178+## Step 10: Whitelist Tailscale IPs
179+
180+Prevent CrowdSec from blocking Tailscale traffic:
181+
182+```bash
183+cat > /etc/crowdsec/parsers/s02-enrich/tailscale-whitelist.yaml << 'EOF'
184+name: tailscale-whitelist
185+description: "Whitelist Tailscale CGNAT range"
186+whitelist:
187+ reason: "Tailscale internal traffic"
188+ cidr:
189+ - "100.64.0.0/10"
190+EOF
191+
192+systemctl reload crowdsec
193+```
194+
195+## Step 11: Enroll in CrowdSec Console
196+
197+This enables community blocklists - IPs blocked across all CrowdSec users globally (15,000+ malicious IPs).
198+
199+1. Create account at https://app.crowdsec.net
200+2. Go to **Security Engines** → **Add Security Engine**
201+3. Copy enrollment key and run:
202+
203+```bash
204+cscli console enroll <your-enrollment-key>
205+```
206+
207+4. Accept the enrollment in the CrowdSec console
208+5. Enable console management:
209+
210+```bash
211+systemctl restart crowdsec
212+cscli console enable console_management
213+systemctl reload crowdsec
214+```
215+
216+---
217+
218+## Verification
219+
220+```bash
221+# Firewall status
222+firewall-cmd --state
223+firewall-cmd --list-all
224+firewall-cmd --list-all --zone=trusted
225+
226+# CrowdSec status
227+cscli metrics
228+cscli bouncers list
229+cscli console status
230+
231+# Verify nftables has CrowdSec rules
232+nft list ruleset | grep -A5 'crowdsec'
233+
234+# View blocked IPs count
235+cscli decisions list | wc -l
236+```
237+
238+---
239+
240+## Port Summary
241+
242+| Port | Protocol | Status | Purpose |
243+|------|----------|--------|---------|
244+| 22 | TCP | Closed | SSH (use Tailscale instead) |
245+| 80 | TCP | Open | HTTP (Gordon proxy) |
246+| 443 | TCP | Open | HTTPS (Gordon proxy via Cloudflare) |
247+| 5000 | TCP | Closed | Registry (proxied through 80/443) |
248+| 41641 | UDP | Open | Tailscale WireGuard |
249+
250+---
251+
252+## Troubleshooting
253+
254+### Can't SSH via Tailscale
255+
256+Check for ACL issues:
257+
258+```bash
259+journalctl -u tailscaled --since "10 minutes ago" | grep -i "rejected\|acl"
260+```
261+
262+If you see `rejected due to acl`, check your Tailscale ACLs at https://login.tailscale.com/admin/acls
263+
264+### Locked out completely
265+
266+Use your VPS provider's console access (e.g., Hetzner Cloud Console):
267+
268+```bash
269+firewall-cmd --add-service=ssh # Temporarily re-enable public SSH
270+```
271+
272+### CrowdSec not blocking
273+
274+Verify bouncer is running:
275+
276+```bash
277+systemctl status crowdsec-firewall-bouncer
278+cscli bouncers list # Should show "Valid"
279+```
280+
281+### Check what's being blocked
282+
283+```bash
284+cscli decisions list
285+cscli alerts list
286+```
287+
288+---
289+
290+## Next Steps
291+
292+- [Install Gordon](/docs/getting-started.md)
293+- [Podman Rootless Setup](./podman-rootless.md) - Enhanced container isolation
294+- [Cloudflare DNS & Proxy Setup](#) - HTTPS termination (coming soon)
295+
296+## References
297+
298+- [Tailscale SSH Documentation](https://tailscale.com/kb/1193/tailscale-ssh)
299+- [firewalld Documentation](https://firewalld.org/documentation/)
300+- [CrowdSec Documentation](https://docs.crowdsec.net/)
301+- [CrowdSec Console](https://app.crowdsec.net)
M wiki/index.md +5 -4
@@ -6,18 +6,19 @@ Community tutorials, guides, and examples for Gordon.
66
77 Step-by-step guides for common tasks:
88
9-- [Deploy Your First App](./tutorials/first-deploy.md) - Get started with Gordon
10-- [Deploy a Next.js App](./tutorials/nextjs-deploy.md) - Full-stack JavaScript deployment
9+- [Deploy Your First App](./tutorials/first-deploy.md) - Deploy a simple web app in minutes
1110 - [Add PostgreSQL to Your App](./tutorials/postgres-service.md) - Database attachments
12-- [Deploy Multi-Service Applications](./tutorials/multi-service-app.md) - Microservices setup
11+
12+> **New to Gordon?** Start with the [Getting Started guide](/docs/getting-started.md) in the official docs.
1313
1414 ## Guides
1515
1616 In-depth guides for specific setups:
1717
18+- [Secure VPS Setup](./guides/secure-vps-setup.md) - Tailscale SSH, firewalld, and CrowdSec
1819 - [Running Gordon in a Container](./guides/running-in-container.md) - Deploy Gordon itself in Docker or Podman
1920 - [Podman Rootless Setup](./guides/podman-rootless.md) - Enhanced security with rootless containers
20-- [Cloudflare DNS & Proxy Setup](./guides/cloudflare-setup.md) - HTTPS termination with Cloudflare
21+- [Remote CLI Management](./guides/remote-cli.md) - Manage Gordon instances remotely
2122 - [Using Pass for Secrets](./guides/secrets-pass.md) - Unix password manager integration
2223 - [Using SOPS for Secrets](./guides/secrets-sops.md) - Encrypted secrets with SOPS
2324
@@ -6,18 +6,19 @@ Community tutorials, guides, and examples for Gordon.
6 6
7 Step-by-step guides for common tasks:7 Step-by-step guides for common tasks:
8 8
9-- [Deploy Your First App](./tutorials/first-deploy.md) - Get started with Gordon9+- [Deploy Your First App](./tutorials/first-deploy.md) - Deploy a simple web app in minutes
10-- [Deploy a Next.js App](./tutorials/nextjs-deploy.md) - Full-stack JavaScript deployment
11 - [Add PostgreSQL to Your App](./tutorials/postgres-service.md) - Database attachments10 - [Add PostgreSQL to Your App](./tutorials/postgres-service.md) - Database attachments
12-- [Deploy Multi-Service Applications](./tutorials/multi-service-app.md) - Microservices setup11+
12+> **New to Gordon?** Start with the [Getting Started guide](/docs/getting-started.md) in the official docs.
13 13
14 ## Guides14 ## Guides
15 15
16 In-depth guides for specific setups:16 In-depth guides for specific setups:
17 17
18+- [Secure VPS Setup](./guides/secure-vps-setup.md) - Tailscale SSH, firewalld, and CrowdSec
18 - [Running Gordon in a Container](./guides/running-in-container.md) - Deploy Gordon itself in Docker or Podman19 - [Running Gordon in a Container](./guides/running-in-container.md) - Deploy Gordon itself in Docker or Podman
19 - [Podman Rootless Setup](./guides/podman-rootless.md) - Enhanced security with rootless containers20 - [Podman Rootless Setup](./guides/podman-rootless.md) - Enhanced security with rootless containers
20-- [Cloudflare DNS & Proxy Setup](./guides/cloudflare-setup.md) - HTTPS termination with Cloudflare21+- [Remote CLI Management](./guides/remote-cli.md) - Manage Gordon instances remotely
21 - [Using Pass for Secrets](./guides/secrets-pass.md) - Unix password manager integration22 - [Using Pass for Secrets](./guides/secrets-pass.md) - Unix password manager integration
22 - [Using SOPS for Secrets](./guides/secrets-sops.md) - Encrypted secrets with SOPS23 - [Using SOPS for Secrets](./guides/secrets-sops.md) - Encrypted secrets with SOPS
23 24
M wiki/tutorials/index.md +2 -2
@@ -1,8 +1,8 @@
11 # Tutorials
22
3-Step-by-step guides to help you get started with Gordon.
3+Step-by-step guides for common Gordon tasks.
44
5-## Getting Started
5+> **New to Gordon?** Start with the [Getting Started guide](/docs/getting-started.md) in the official docs.
66
77 - [Deploy Your First App](./first-deploy.md) - Deploy a simple web app in minutes
88
@@ -1,8 +1,8 @@
1 # Tutorials1 # Tutorials
2 2
3-Step-by-step guides to help you get started with Gordon.3+Step-by-step guides for common Gordon tasks.
4 4
5-## Getting Started5+> **New to Gordon?** Start with the [Getting Started guide](/docs/getting-started.md) in the official docs.
6 6
7 - [Deploy Your First App](./first-deploy.md) - Deploy a simple web app in minutes7 - [Deploy Your First App](./first-deploy.md) - Deploy a simple web app in minutes
8 8

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close