| 1 |
1 |
|
# Makenotwork Caddy Configuration
|
| 2 |
2 |
|
# Place in /etc/caddy/Caddyfile on the server
|
|
3 |
+ |
#
|
|
4 |
+ |
# TLS: Cloudflare Origin CA cert (wildcard *.makenot.work + makenot.work)
|
|
5 |
+ |
# All HTTPS traffic routed through Cloudflare proxy (origin IP hidden).
|
|
6 |
+ |
# Authenticated Origin Pulls: only Cloudflare can reach the origin.
|
|
7 |
+ |
# git.makenot.work is SSH-only (no Caddy block needed, port 22 via sshd).
|
|
8 |
+ |
|
|
9 |
+ |
# Shared TLS config: Origin CA cert + Authenticated Origin Pulls (mTLS)
|
|
10 |
+ |
(cloudflare_tls) {
|
|
11 |
+ |
tls /etc/caddy/cloudflare-origin.pem /etc/caddy/cloudflare-origin-key.pem {
|
|
12 |
+ |
client_auth {
|
|
13 |
+ |
mode require_and_verify
|
|
14 |
+ |
trusted_ca_cert_file /etc/caddy/cloudflare-authenticated-origin-pull-ca.pem
|
|
15 |
+ |
}
|
|
16 |
+ |
}
|
|
17 |
+ |
}
|
| 3 |
18 |
|
|
| 4 |
19 |
|
makenot.work {
|
| 5 |
|
- |
# Serve documentation as static files
|
| 6 |
|
- |
handle /docs/* {
|
| 7 |
|
- |
root * /opt/makenotwork/docs
|
| 8 |
|
- |
uri strip_prefix /docs
|
| 9 |
|
- |
file_server
|
| 10 |
|
- |
}
|
|
20 |
+ |
import cloudflare_tls
|
|
21 |
+ |
|
|
22 |
+ |
# Serve documentation as static files
|
|
23 |
+ |
handle /docs/* {
|
|
24 |
+ |
root * /opt/makenotwork/docs
|
|
25 |
+ |
uri strip_prefix /docs
|
|
26 |
+ |
file_server
|
|
27 |
+ |
}
|
|
28 |
+ |
|
|
29 |
+ |
# Reverse proxy to application
|
|
30 |
+ |
handle {
|
|
31 |
+ |
reverse_proxy localhost:3000
|
|
32 |
+ |
}
|
|
33 |
+ |
|
|
34 |
+ |
# Security headers
|
|
35 |
+ |
header {
|
|
36 |
+ |
X-Frame-Options "SAMEORIGIN"
|
|
37 |
+ |
X-Content-Type-Options "nosniff"
|
|
38 |
+ |
X-XSS-Protection "1; mode=block"
|
|
39 |
+ |
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
40 |
+ |
Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(self)"
|
|
41 |
+ |
Referrer-Policy "strict-origin-when-cross-origin"
|
|
42 |
+ |
Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline' https://unpkg.com https://js.stripe.com; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data: https://fsn1.your-objectstorage.com; connect-src 'self' https://api.stripe.com https://fsn1.your-objectstorage.com; media-src 'self' https://fsn1.your-objectstorage.com; frame-src https://js.stripe.com; base-uri 'self'; form-action 'self'"
|
|
43 |
+ |
}
|
|
44 |
+ |
|
|
45 |
+ |
# Static error pages when app is down
|
|
46 |
+ |
handle_errors {
|
|
47 |
+ |
@404 expression {err.status_code} == 404
|
|
48 |
+ |
handle @404 {
|
|
49 |
+ |
root * /opt/makenotwork/error-pages
|
|
50 |
+ |
rewrite * /404.html
|
|
51 |
+ |
file_server
|
|
52 |
+ |
}
|
|
53 |
+ |
@500 expression {err.status_code} == 500
|
|
54 |
+ |
handle @500 {
|
|
55 |
+ |
root * /opt/makenotwork/error-pages
|
|
56 |
+ |
rewrite * /500.html
|
|
57 |
+ |
file_server
|
|
58 |
+ |
}
|
|
59 |
+ |
handle {
|
|
60 |
+ |
root * /opt/makenotwork/error-pages
|
|
61 |
+ |
rewrite * /502.html
|
|
62 |
+ |
file_server
|
|
63 |
+ |
}
|
|
64 |
+ |
}
|
|
65 |
+ |
|
|
66 |
+ |
encode gzip zstd
|
|
67 |
+ |
|
|
68 |
+ |
log {
|
|
69 |
+ |
output file /var/log/caddy/makenotwork.log
|
|
70 |
+ |
format json
|
|
71 |
+ |
}
|
|
72 |
+ |
}
|
| 11 |
73 |
|
|
| 12 |
|
- |
# Reverse proxy to application
|
| 13 |
|
- |
handle {
|
| 14 |
|
- |
reverse_proxy localhost:3000
|
| 15 |
|
- |
}
|
|
74 |
+ |
# Multithreaded forum
|
|
75 |
+ |
forums.makenot.work {
|
|
76 |
+ |
import cloudflare_tls
|
| 16 |
77 |
|
|
| 17 |
|
- |
# Security headers
|
| 18 |
|
- |
header {
|
| 19 |
|
- |
# Prevent clickjacking
|
| 20 |
|
- |
X-Frame-Options "SAMEORIGIN"
|
| 21 |
|
- |
# Prevent MIME type sniffing
|
| 22 |
|
- |
X-Content-Type-Options "nosniff"
|
| 23 |
|
- |
# Enable XSS filter
|
| 24 |
|
- |
X-XSS-Protection "1; mode=block"
|
| 25 |
|
- |
# HSTS — browsers never attempt HTTP
|
| 26 |
|
- |
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
| 27 |
|
- |
# Lock down unused browser APIs
|
| 28 |
|
- |
Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(self)"
|
| 29 |
|
- |
# Referrer policy
|
| 30 |
|
- |
Referrer-Policy "strict-origin-when-cross-origin"
|
| 31 |
|
- |
# Content Security Policy
|
| 32 |
|
- |
Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline' https://unpkg.com https://js.stripe.com; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data: https://fsn1.your-objectstorage.com; connect-src 'self' https://api.stripe.com https://fsn1.your-objectstorage.com; media-src 'self' https://fsn1.your-objectstorage.com; frame-src https://js.stripe.com; base-uri 'self'; form-action 'self'"
|
| 33 |
|
- |
}
|
|
78 |
+ |
reverse_proxy localhost:3400
|
| 34 |
79 |
|
|
| 35 |
|
- |
# Static error pages when app is down
|
| 36 |
|
- |
handle_errors {
|
| 37 |
|
- |
@404 expression {err.status_code} == 404
|
| 38 |
|
- |
handle @404 {
|
| 39 |
|
- |
root * /opt/makenotwork/error-pages
|
| 40 |
|
- |
rewrite * /404.html
|
| 41 |
|
- |
file_server
|
| 42 |
|
- |
}
|
| 43 |
|
- |
@500 expression {err.status_code} == 500
|
| 44 |
|
- |
handle @500 {
|
| 45 |
|
- |
root * /opt/makenotwork/error-pages
|
| 46 |
|
- |
rewrite * /500.html
|
| 47 |
|
- |
file_server
|
| 48 |
|
- |
}
|
| 49 |
|
- |
handle {
|
| 50 |
|
- |
root * /opt/makenotwork/error-pages
|
| 51 |
|
- |
rewrite * /502.html
|
| 52 |
|
- |
file_server
|
| 53 |
|
- |
}
|
| 54 |
|
- |
}
|
|
80 |
+ |
header {
|
|
81 |
+ |
X-Frame-Options "SAMEORIGIN"
|
|
82 |
+ |
X-Content-Type-Options "nosniff"
|
|
83 |
+ |
X-XSS-Protection "1; mode=block"
|
|
84 |
+ |
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
85 |
+ |
Permissions-Policy "camera=(), microphone=(), geolocation=()"
|
|
86 |
+ |
Referrer-Policy "strict-origin-when-cross-origin"
|
|
87 |
+ |
Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data:; connect-src 'self'; base-uri 'self'; form-action 'self' https://makenot.work"
|
|
88 |
+ |
}
|
| 55 |
89 |
|
|
| 56 |
|
- |
# Compression
|
| 57 |
|
- |
encode gzip zstd
|
|
90 |
+ |
encode gzip zstd
|
| 58 |
91 |
|
|
| 59 |
|
- |
# Logging
|
| 60 |
|
- |
log {
|
| 61 |
|
- |
output file /var/log/caddy/makenotwork.log
|
| 62 |
|
- |
format json
|
| 63 |
|
- |
}
|
|
92 |
+ |
log {
|
|
93 |
+ |
output file /var/log/caddy/forums.log
|
|
94 |
+ |
format json
|
|
95 |
+ |
}
|
| 64 |
96 |
|
}
|
| 65 |
97 |
|
|
| 66 |
|
- |
# Redirect www and .com variants to canonical domain
|
| 67 |
|
- |
www.makenot.work, makenotwork.com, www.makenotwork.com {
|
| 68 |
|
- |
redir https://makenot.work{uri} permanent
|
|
98 |
+ |
# Redirect www to canonical domain
|
|
99 |
+ |
# Note: makenotwork.com and www.makenotwork.com redirects are handled by
|
|
100 |
+ |
# Cloudflare Redirect Rules (edge-level, no origin hit needed).
|
|
101 |
+ |
# Those domains are not covered by the *.makenot.work Origin CA cert.
|
|
102 |
+ |
www.makenot.work {
|
|
103 |
+ |
import cloudflare_tls
|
|
104 |
+ |
redir https://makenot.work{uri} permanent
|
| 69 |
105 |
|
}
|