Skip to content

Commit a2dfa76

Browse files
authored
Merge pull request #5 from Whispergate/dev
V0.3.1 - IAC, Opsec, and Phishing Updates
2 parents 7f31ce1 + e4248e9 commit a2dfa76

103 files changed

Lines changed: 10814 additions & 250 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,22 @@ INFRAGUARD_DB_PATH=/app/data/infraguard.db
7272
# Examples: "status", "api/ping", ".well-known/health", "favicon.ico"
7373
INFRAGUARD_HEALTH_PATH=health
7474

75+
# ── Decoy Pages ───────────────────────────────────────────────────────
76+
# Directory containing SPA folders for the "decoy" drop action.
77+
# Each subfolder is a complete site (index.html + assets).
78+
# Docker: /app/pages | Local: pages
79+
IG_DECOY_PAGES_DIR=/app/pages
80+
# Which site folder to serve (e.g., "InfraGuardBlog", "TestBlog")
81+
IG_DECOY_SITE=
82+
7583
# ── Rules / Blocklists ────────────────────────────────────────────────
76-
# Path to ingested IP blocklist file. Generate with:
77-
# infraguard ingest rules/.htaccess --format blocklist -o rules/banned_ips.txt
84+
# Directory containing .htaccess and robots.txt rule files.
85+
# Any files found here are automatically ingested on proxy startup.
86+
# Docker: /app/rules | Local: rules
87+
INFRAGUARD_RULES_DIR=/app/rules
88+
89+
# Path to ingested IP blocklist file (legacy - rules_dir is preferred).
90+
# Generate manually with: infraguard ingest rules/.htaccess --format blocklist -o rules/banned_ips.txt
7891
# Docker: /app/rules/banned_ips.txt | Local: rules/banned_ips.txt
7992
INFRAGUARD_BANNED_IP_FILE=/app/rules/banned_ips.txt
8093

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,6 @@ __marimo__/
223223
# Vendor source (cloned at build time, not tracked)
224224
vendor/pwndrop/pwndrop/
225225
images/.$InfraGuard Infrastructure Diagram.drawio.bkp
226+
.planning/*
227+
.claude
228+
.infraguard-deploy

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ WORKDIR /app
1010
COPY pyproject.toml README.md ./
1111
COPY infraguard/ infraguard/
1212
COPY examples/ examples/
13+
COPY pages/ pages/
1314

1415
RUN pip install --no-cache-dir ".[all]"
1516

config/config.yaml

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@ domains:
2020
# Fronts a CS teamserver behind a jQuery CDN lookalike.
2121
# Requests that don't match the malleable profile are redirected
2222
# to the real jQuery site so the redirector looks legitimate.
23-
cdn.example.com:
24-
upstream: "${INFRAGUARD_CS_UPSTREAM}"
25-
profile_path: "examples/jquery-c2.3.14.profile"
26-
profile_type: "cobalt_strike"
27-
whitelist_cidrs:
28-
- "192.168.1.0/24" # operator subnet
29-
- "10.0.0.0/8" # internal range
30-
decoy_dir: null # or path to static site served to blocked visitors
31-
drop_action:
32-
type: "redirect"
33-
target: "https://jquery.com"
23+
# cdn.example.com:
24+
# upstream: "${INFRAGUARD_CS_UPSTREAM}"
25+
# profile_path: "examples/jquery-c2.3.14.profile"
26+
# profile_type: "cobalt_strike"
27+
# whitelist_cidrs:
28+
# - "192.168.1.0/24" # operator subnet
29+
# - "10.0.0.0/8" # internal range
30+
# decoy_dir: null # or path to static site served to blocked visitors
31+
# drop_action:
32+
# type: "redirect"
33+
# target: "https://jquery.com"
3434

3535
# ── Content delivery routes (optional) ──────────────────────────
3636
# Evaluated BEFORE the C2 profile filter. Serve payloads, decoys,
@@ -56,14 +56,14 @@ domains:
5656
# Fronts a Mythic teamserver behind a generic CDN static-assets page.
5757
# Uses the Mythic HTTPX JSON profile format. Non-matching requests
5858
# get proxied to a real site so the redirector returns valid HTML.
59-
static.example.net:
59+
${INFRAGUARD_DOMAIN}:
6060
upstream: "${INFRAGUARD_MYTHIC_UPSTREAM}"
61-
profile_path: "examples/mythic-httpx.json"
61+
profile_path: "examples/jquery-c2.3.14.profile.json"
6262
profile_type: "mythic"
63-
whitelist_cidrs: [] # empty = all IPs are allowed (no whitelist)
63+
whitelist_cidrs: [] # empty = all IPs are allowed (no whitelist)
6464
drop_action:
65-
type: "proxy" # serve real content from the target site
66-
target: "https://example.net"
65+
type: "decoy"
66+
target: "${IG_DECOY_SITE}"
6767
content_routes:
6868
- path: "/downloads/*"
6969
backend:
@@ -88,6 +88,7 @@ intel:
8888
geoip_asn_db: "${INFRAGUARD_GEOIP_ASN_DB}"
8989
geoip_country_db: "${INFRAGUARD_GEOIP_COUNTRY_DB}"
9090
banned_ip_file: "${INFRAGUARD_BANNED_IP_FILE}"
91+
rules_dir: "${INFRAGUARD_RULES_DIR}" # auto-ingest .htaccess / robots.txt on startup
9192
feeds:
9293
enabled: true
9394

@@ -104,6 +105,9 @@ api:
104105
auth_token: "${INFRAGUARD_API_TOKEN}"
105106
health_path: "/${INFRAGUARD_HEALTH_PATH}"
106107

108+
decoy_pages_dir: "${IG_DECOY_PAGES_DIR}" # base directory for decoy SPA sites
109+
107110
logging:
108111
level: "${INFRAGUARD_LOG_LEVEL}"
109112
format: "json"
113+

config/test-config-full.yaml

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,19 @@ listeners:
7575
# teamserver, IP whitelist, drop action, and content delivery routes.
7676
#
7777
# Supported profile types:
78-
# cobalt_strike - .profile files (Malleable C2 DSL)
79-
# mythic - .json files (Mythic HTTPX profile format)
80-
# brute_ratel - .json files (BRC4 server config with listeners)
81-
# sliver - .json files (Sliver HTTP C2 profile with URI generation)
82-
# havoc - .toml files (Havoc/Kaine profile with URI alternation patterns)
78+
#
79+
# C2 frameworks (require profile_path):
80+
# cobalt_strike - .profile files (Malleable C2 DSL)
81+
# mythic - .json files (Mythic HTTPX profile format)
82+
# brute_ratel - .json files (BRC4 server config with listeners)
83+
# sliver - .json files (Sliver HTTP C2 profile with URI generation)
84+
# havoc - .toml files (Havoc/Kaine profile with URI alternation patterns)
85+
#
86+
# Phishing frameworks (no profile_path needed):
87+
# gophish - GoPhish landing pages, tracking, and report endpoints
88+
# evilginx - Evilginx reverse proxy phishlets (proxies all paths)
89+
# cuddlephish - CuddlePhish OAuth/device-code phishing flows
90+
# passthrough - Generic proxy mode - all paths forwarded, no profile matching
8391

8492
domains:
8593

@@ -172,6 +180,79 @@ domains:
172180
# type: "proxy"
173181
# target: "https://portal.azure.com"
174182

183+
# ── GoPhish domain ─────────────────────────────────────────────────
184+
# Fronts a GoPhish instance. No C2 profile needed - path-based
185+
# filtering allows tracking pixels (/track/*), report endpoint
186+
# (/report), static assets (/static/*), and landing pages.
187+
# IP/bot/geo filters still run to block scanners.
188+
#
189+
# Use allowed_paths to override the built-in GoPhish patterns:
190+
# phish.example.com:
191+
# upstream: "https://10.0.0.10:3333"
192+
# profile_type: "gophish"
193+
# # allowed_paths: # optional - overrides built-in patterns
194+
# # - "/custom-landing" # exact path
195+
# # - "/campaigns/*" # prefix glob
196+
# # - "~^/t/[a-f0-9]+" # regex (prefix with ~)
197+
# drop_action:
198+
# type: "redirect"
199+
# target: "https://example.com"
200+
201+
# ── Evilginx domain ────────────────────────────────────────────────
202+
# Fronts an Evilginx reverse proxy. Two modes:
203+
#
204+
# 1. With phishlet file (recommended): InfraGuard parses the phishlet
205+
# to extract login paths and auth_urls for filtering.
206+
# Set profile_path to the phishlet YAML file.
207+
#
208+
# 2. Without phishlet: all paths are forwarded (Evilginx default).
209+
# Use allowed_paths to restrict if needed.
210+
#
211+
# IP/bot/geo filters protect against scanners discovering the phishlet.
212+
# login.example.com:
213+
# upstream: "https://10.0.0.11:443"
214+
# profile_type: "evilginx"
215+
# profile_path: "examples/wordpress-phishlet.yaml" # optional phishlet file
216+
# # allowed_paths: # optional - replaces defaults
217+
# # - "/wp-login.php"
218+
# # - "/wp-admin/*"
219+
# drop_action:
220+
# type: "proxy"
221+
# target: "https://login.microsoftonline.com"
222+
223+
# ── CuddlePhish domain ─────────────────────────────────────────────
224+
# Fronts a CuddlePhish OAuth/device-code phishing server. All paths
225+
# forwarded - CuddlePhish handles OAuth flow routing internally.
226+
# Use allowed_paths to restrict to specific OAuth endpoints.
227+
# auth.example.com:
228+
# upstream: "https://10.0.0.12:8443"
229+
# profile_type: "cuddlephish"
230+
# # allowed_paths:
231+
# # - "/devicelogin"
232+
# # - "/common/oauth2/*"
233+
# # - "/common/login"
234+
# drop_action:
235+
# type: "redirect"
236+
# target: "https://login.microsoftonline.com"
237+
238+
# ── Generic passthrough domain ──────────────────────────────────────
239+
# Pure reverse proxy with IP/bot/geo filtering. All requests that
240+
# pass the filter pipeline are forwarded to upstream. No profile
241+
# matching at all. Use for custom phishing frameworks, payload
242+
# delivery servers, or any HTTP service that needs scanner protection.
243+
#
244+
# If allowed_paths is set, only those paths are proxied (converts
245+
# passthrough into path-filtered mode).
246+
# proxy.example.com:
247+
# upstream: "https://10.0.0.13:8080"
248+
# profile_type: "passthrough"
249+
# # allowed_paths: # optional - restricts passthrough
250+
# # - "/api/*"
251+
# # - "/webhook"
252+
# drop_action:
253+
# type: "reset"
254+
# target: ""
255+
175256
# ── IP Intelligence ───────────────────────────────────────────────────
176257

177258
intel:
@@ -181,16 +262,20 @@ intel:
181262
geoip_country_db: "${INFRAGUARD_GEOIP_COUNTRY_DB}" # GeoLite2-Country.mmdb
182263

183264
blocked_countries: [] # e.g. ["CN", "RU", "KP", "IR"]
184-
allowed_countries: [] # e.g. ["US", "GB", "DE"] if set, ONLY these pass
265+
allowed_countries: [] # e.g. ["US", "GB", "DE"] - if set, ONLY these pass
185266
blocked_asns: [] # e.g. [14061, 16276] for DigitalOcean, OVH
186-
allowed_asns: [] # e.g. [15169, 13335] for Google, Cloudflare if set, ONLY these pass
267+
allowed_asns: [] # e.g. [15169, 13335] for Google, Cloudflare - if set, ONLY these pass
187268
auto_block_scanners: true # block Shodan, Censys, Rapid7, etc.
188269
dynamic_whitelist_threshold: 3 # auto-whitelist after N valid C2 requests
189270

190271
# Ingested blocklist from rules/ directory
191272
# Generate with: infraguard ingest rules/.htaccess --format blocklist -o rules/banned_ips.txt
192273
banned_ip_file: "${INFRAGUARD_BANNED_IP_FILE}"
193274

275+
# Auto-ingest .htaccess and robots.txt from this directory on startup.
276+
# Drop rule files into the rules/ directory and they'll be loaded automatically.
277+
rules_dir: "${INFRAGUARD_RULES_DIR}"
278+
194279
# Threat intel feed auto-update
195280
feeds:
196281
enabled: true

docker-compose.yml

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ services:
1212
- ./config:/app/config:ro
1313
- ./examples:/app/examples:ro
1414
- ./rules:/app/rules:ro
15+
- ./pages:/app/pages:ro
1516
- ./data:/app/data
1617
- certs:/app/certs:ro
1718
- geoip:/app/geoip:ro
@@ -60,6 +61,7 @@ services:
6061
# /etc/letsencrypt/live/${INFRAGUARD_DOMAIN}/privkey.pem
6162
#
6263
# After first run, restart the proxy to pick up the new certs.
64+
# docker compose --profile letsencrypt up certbot
6365
certbot:
6466
image: certbot/certbot:latest
6567
container_name: infraguard-certbot
@@ -69,19 +71,19 @@ services:
6971
- certs:/etc/letsencrypt
7072
- certbot-www:/var/www/certbot
7173
entrypoint: /bin/sh
72-
command: >
73-
-c '
74-
if [ "${INFRAGUARD_LETSENCRYPT}" = "true" ]; then
75-
certbot certonly --standalone
76-
--non-interactive --agree-tos
77-
--email ${INFRAGUARD_DOMAIN_EMAIL}
78-
-d ${INFRAGUARD_DOMAIN}
79-
--preferred-challenges http
80-
&& echo "Certificate obtained for ${INFRAGUARD_DOMAIN}"
74+
command:
75+
- -c
76+
- |
77+
if [ "$${INFRAGUARD_LETSENCRYPT}" = "true" ]; then
78+
certbot certonly --standalone \
79+
--non-interactive --agree-tos \
80+
--email $${INFRAGUARD_DOMAIN_EMAIL} \
81+
-d $${INFRAGUARD_DOMAIN} \
82+
--preferred-challenges http \
83+
&& echo "Certificate obtained for $${INFRAGUARD_DOMAIN}"
8184
else
82-
echo "Let'\''s Encrypt disabled (INFRAGUARD_LETSENCRYPT != true)"
85+
echo "Let's Encrypt disabled (INFRAGUARD_LETSENCRYPT != true)"
8386
fi
84-
'
8587
ports:
8688
- "80:80"
8789
networks:

infraguard/config/defaults.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
"""Default configuration values for InfraGuard."""
22

3+
import os
4+
from pathlib import Path
5+
6+
_CONFIG_DIR = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "infraguard"
7+
_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
8+
39
DEFAULT_BLOCK_SCORE_THRESHOLD = 0.7
4-
DEFAULT_DB_PATH = "infraguard.db"
10+
DEFAULT_DB_PATH = str(_CONFIG_DIR / "infraguard.db")
511
DEFAULT_RETENTION_DAYS = 30
612
DEFAULT_API_BIND = "127.0.0.1"
713
DEFAULT_API_PORT = 8080

0 commit comments

Comments
 (0)