File size: 3,471 Bytes
de83fbe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf3ef55
de83fbe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# deploy/Caddyfile.example — reference Caddy config for this box.
#
# The deployment runs TWO bots behind one Caddy:
#
#   arb.elghaly.dev  ->  127.0.0.1:8080   the Rust bot   (basicauth, private)
#   bot.elghaly.dev  ->  127.0.0.1:8081   this Python bot (public, read-only)
#
# Copy to /etc/caddy/Caddyfile, adjust, then:
#
#     sudo caddy validate --config /etc/caddy/Caddyfile
#     sudo systemctl reload caddy
#
# ─────────────────────────────────────────────────────────────────────────
# THE PATH ALLOWLIST IS THE POINT OF THIS FILE
# ─────────────────────────────────────────────────────────────────────────
# A bare `reverse_proxy 127.0.0.1:8081` publishes EVERY route the Python
# bot serves, and two of them are not public routes:
#
#   POST /command   fund-moving. Runs /hunt, toggles ghost and mint mode.
#   GET  /logs      operational detail — wallet addresses, tx signatures,
#                   route sizes, internal errors.
#
# Both are Bearer-token gated in the application (modules/bot_api_server.py)
# and both are rate-limited and lockout-protected (modules/http_guard.py),
# so a bare proxy is not an immediate compromise. It is still the wrong
# shape. Anything that can move funds should be unreachable for two
# independent reasons, so that no single mistake — a token in a shell
# history, a bad `git add`, a regression in the auth check — is sufficient
# on its own.
#
# So this config lists what MAY be reached and 404s everything else. Note
# the direction: an allowlist fails closed, so a new endpoint added to the
# Python server tomorrow is private until someone deliberately publishes
# it. A blocklist would fail open and publish it by default.

arb.elghaly.dev {
	basicauth {
		# Generate with:  caddy hash-password
		# The hash is not a password, but it is still an offline-crackable
		# artifact — keep it out of screenshots and out of git.
		admin REPLACE_WITH_BCRYPT_HASH
	}
	reverse_proxy 127.0.0.1:8080
}

bot.elghaly.dev {
	encode gzip

	# The public read-only surface. Nothing here can act, and nothing here
	# returns a secret — see modules/bot_api_server.py's own audit note for
	# the field-by-field reasoning.
	@public {
		path /
		path /status
		path /api/pnl
		path /api/receipts
		path /api/routes
		path /api/nearmiss
		path /api/engine
		path /receipts.csv
	}
	handle @public {
		reverse_proxy 127.0.0.1:8081
	}

	# Everything else, including /command and /logs, does not exist as far
	# as the internet is concerned. 404 rather than 403: a 403 confirms the
	# path is real and worth attacking, which is free reconnaissance.
	handle {
		respond "not found" 404
	}

	header {
		# This page loads no external anything — no CDN, no fonts, no
		# analytics — so the policy can be this tight honestly. 'unsafe-inline'
		# covers the one inline <style> and <script> the page is built from.
		Content-Security-Policy "default-src 'none'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"
		X-Content-Type-Options "nosniff"
		Referrer-Policy "no-referrer"
		X-Frame-Options "DENY"
		-Server
	}
}