-
-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration Reference
Exhaustive documentation of every configurable constant, enum value, regex pattern, per-backend option, and CLI flag in the Apotropaios framework.
- Runtime Configuration
- Security Constants
- Performance Constants
- Backup Constants
- TTL Limits
- Error Codes
- Log Levels
- Rule Actions
- Rule Directions
- Rule States
- Duration Types
- Connection Tracking States
- Syslog Levels
- Validation Patterns
- Shell Metacharacters
- Backend: iptables
- Backend: nftables
- Backend: firewalld
- Backend: UFW
- Backend: ipset
- Supported Operating Systems
- Directory Paths
- File Names
- CLI Commands
- CLI Global Options
- Cancel Keywords
Apotropaios reads built-in defaults from code constants in core/constants.py at import time, and additionally loads an optional INI-format configuration file at startup. Because a framework running as root that modifies kernel firewall rules must not act on a file that could be tampered with, every candidate file passes a trust gate before use: it must be owned by root or the current effective user and must not be group- or world-writable. Files failing the gate are skipped entirely with a warning -- never partially applied.
The search order is: /etc/apotropaios/apotropaios.conf (system-wide), then conf/apotropaios.conf under the framework base directory, then the copy shipped inside the package. The first trusted match wins. Setting precedence is: command line > configuration file > built-in defaults. The configuration file currently drives the console log level ([logging] log_level, applied only when --log-level is not passed) and the default firewall backend ([firewall] default_backend, applied only when --backend is not passed); remaining sections are parsed and reserved for framework use. The APOTROPAIOS_BASE_DIR environment variable overrides the framework base directory when it names an existing directory, relocating the data/ tree (logs, rules, backups) and the project-local configuration lookup without touching the installation; an invalid override emits a warning and falls back to the default.
Override at runtime via CLI flags:
| Flag | Overrides | Default | Example |
|---|---|---|---|
--log-level LEVEL |
Console log verbosity | warning |
--log-level trace |
--backend NAME |
Active firewall backend | auto-detect | --backend firewalld |
--interactive |
Operation mode | CLI | --interactive |
--non-interactive |
Prompt behavior | interactive | --non-interactive |
Defined in Security class (core/constants.py):
| Constant | Value | Type | Implication |
|---|---|---|---|
UMASK |
0o077 |
int | New files/dirs have no group/world access. Set via os.umask() during init_security(). |
FILE_PERMS |
0o600 |
int | All created files: owner read/write only. Applied via os.chmod(). |
DIR_PERMS |
0o700 |
int | All created directories: owner-only access. |
EXEC_PERMS |
0o700 |
int | Executable files: owner-only. |
MAX_INPUT_LENGTH |
4096 |
int |
sanitize_input() truncates beyond this. Prevents DoS via regex on long strings. |
MAX_PATH_LENGTH |
4096 |
int | File path validation rejects paths exceeding this. |
MAX_RULE_DESCRIPTION_LENGTH |
256 |
int |
validate_description() rejects longer descriptions. |
MAX_LOG_FILE_SIZE_BYTES |
104,857,600 |
int | 100MB. _SecureRotatingHandler triggers rotation at this threshold. |
MAX_LOG_FILES_RETAINED |
10 |
int | Number of rotated log files kept. Oldest deleted on rotation. |
LOCK_TIMEOUT_SECONDS |
30 |
int |
FileLock.acquire() gives up after this many seconds. Raises LockTimeoutError. |
LOCK_RETRY_INTERVAL |
1 |
int | Seconds between lock acquisition retry attempts. |
Defined in Performance class:
| Constant | Value | Type | Implication |
|---|---|---|---|
SUBPROCESS_TIMEOUT |
30 |
int | Every subprocess.run() call times out after 30 seconds. Prevents hanging on stuck firewall commands (e.g., firewalld D-Bus hang). |
EXPIRY_CHECK_INTERVAL |
30 |
int | ExpiryMonitor daemon thread wakes this often (seconds). TTL precision is ±30 seconds. |
MAX_CONCURRENT_OPERATIONS |
4 |
int | ThreadPoolExecutor worker count for parallel_exec(). Used in backup export. |
OPERATION_TIMEOUT_SECONDS |
300 |
int | 5 minutes. Overall operation timeout for long-running tasks. |
BATCH_SIZE |
50 |
int | Rules per batch in bulk operations (import). |
Defined in Backup class:
| Constant | Value | Implication |
|---|---|---|
PREFIX |
apotropaios_backup |
Archive filename prefix. Full name: apotropaios_backup_<label>_<timestamp>.tar.gz
|
EXTENSION |
.tar.gz |
Archive format (gzip-compressed tar) |
MANIFEST_FILE |
manifest.json |
Metadata file inside each archive. Contains: timestamp, version, label, backends, file checksums. |
MAX_RETAINED |
20 |
Maximum backup archives before pruning. _enforce_retention() deletes oldest. |
INTEGRITY_ALGORITHM |
sha256 |
Algorithm for .sha256 sidecar files and .integrity files in immutable snapshots. |
Defined in TTLLimits class:
| Constant | Value | Human-Readable |
|---|---|---|
MIN_SECONDS |
60 |
1 minute -- minimum temporary rule duration |
MAX_SECONDS |
2,592,000 |
30 days -- maximum temporary rule duration |
validate_ttl() rejects values outside this range.
Defined as ErrorCode(IntEnum). Usable directly as sys.exit() arguments.
| Code | Name | Category | When Raised |
|---|---|---|---|
| 0 | SUCCESS |
General | Operation completed successfully |
| 1 | GENERAL |
General | Unspecified error (catch-all) |
| 2 | USAGE |
General | Invalid CLI arguments, mutually exclusive flags |
| 3 | PERMISSION |
General | Not running as root (euid != 0) |
| 10 | OS_UNSUPPORTED |
Detection | OS not in SUPPORTED_OS list |
| 11 | FW_NOT_FOUND |
Detection | No firewall binary found on PATH |
| 12 | FW_NOT_RUNNING |
Detection | Firewall installed but systemd service not active |
| 13 | FW_INSTALL_FAIL |
Detection | apt/dnf/pacman package installation failed |
| 20 | RULE_INVALID |
Rules | Parameter validation failed (any of 27 validators) |
| 21 | RULE_EXISTS |
Rules | Duplicate UUID already in index |
| 22 | RULE_NOT_FOUND |
Rules | UUID not in index |
| 23 | RULE_APPLY_FAIL |
Rules | Backend subprocess returned non-zero |
| 24 | RULE_REMOVE_FAIL |
Rules | Backend failed to delete rule |
| 25 | RULE_IMPORT_FAIL |
Rules | Import file parse error or validation failure |
| 30 | BACKUP_FAIL |
Backup | Archive creation failed (disk space, permissions) |
| 31 | RESTORE_FAIL |
Backup | Restore failed (checksum mismatch, path traversal) |
| 32 | BACKUP_NOT_FOUND |
Backup | Specified archive file does not exist |
| 40 | VALIDATION_FAIL |
Security | Input validation rejected parameter |
| 41 | INPUT_SANITIZE_FAIL |
Security |
sanitize_input() detected dangerous content |
| 50 | LOG_FAIL |
Logging | Logger initialization failed (disk, permissions) |
| 51 | LOG_HANDLE_LOST |
Logging | File handler lost during operation |
| 60 | LOCK_FAIL |
Locking | Lock acquisition failed (non-timeout) |
| 61 | LOCK_TIMEOUT |
Locking | Lock not acquired within LOCK_TIMEOUT_SECONDS |
| 70 | INTEGRITY_FAIL |
Integrity | SHA-256 checksum mismatch |
| 71 | MEMORY_FAIL |
Integrity | Memory allocation or scrubbing failure |
| 80 | SIGNAL_RECEIVED |
Signals | SIGTERM/SIGINT/SIGHUP received |
| 81 | CLEANUP_FAIL |
Signals | CleanupStack handler raised exception |
Defined as LogLevel(IntEnum):
| Level | Numeric | stdlib Mapping | Console Default? | Description |
|---|---|---|---|---|
TRACE |
0 | 5 (custom) | No | Ultra-verbose: cleanup registration, security init, validation steps |
DEBUG |
1 | 10 | No | Development diagnostics: function entry/exit, variable values |
INFO |
2 | 20 | No | Normal operations: init complete, rule created, backend selected |
WARNING |
3 | 30 | Yes (default) | Non-fatal issues: backend removal partially failed, stale lock detected |
ERROR |
4 | 40 | Yes | Operation failures: backend rejected rule, index save failed |
CRITICAL |
5 | 50 | Yes | Fatal: framework cannot continue, security violation |
NONE |
99 | 100 | -- | Suppress all console output (log file still captures everything) |
Key behavior: The log FILE always captures all levels (TRACE and above). The --log-level flag only controls the CONSOLE handler threshold. This means full diagnostic information is always available in the log file for post-mortem analysis.
Defined as RuleAction(StrEnum):
| Action | Type | Backend Translation |
|---|---|---|
accept |
Terminal | iptables: -j ACCEPT. nft: accept. firewalld: accept. ufw: allow. |
drop |
Terminal | iptables: -j DROP. nft: drop. firewalld: drop. ufw: deny. |
reject |
Terminal | iptables: -j REJECT. nft: reject. firewalld: reject. ufw: reject. |
log |
Non-terminal | iptables: -j LOG --log-prefix. nft: log prefix. firewalld: log prefix=. ufw: ufw logging on. |
masquerade |
Terminal | iptables: -j MASQUERADE. nft: masquerade. firewalld: masquerade. |
snat |
Terminal | iptables: -j SNAT. nft: snat. |
dnat |
Terminal | iptables: -j DNAT. nft: dnat. |
return |
Terminal | iptables: -j RETURN. nft: return. |
Compound actions: log,drop combines non-terminal + terminal. Maximum one terminal per compound. drop,accept is rejected (double-terminal).
Frozensets for classification:
-
TERMINAL_ACTIONS:{accept, drop, reject, masquerade, snat, dnat, return} -
NON_TERMINAL_ACTIONS:{log} -
ALL_ACTIONS: union of both
Defined as RuleDirection(StrEnum):
| Direction | iptables Chain | nftables Chain | firewalld | ufw |
|---|---|---|---|---|
inbound |
INPUT | input | Rich rule (inbound) | ufw allow in |
outbound |
OUTPUT | output | Rich rule (outbound) | ufw allow out |
forward |
FORWARD | forward | Rich rule (forward) | ufw route allow |
Defined as RuleState(StrEnum):
| State | Meaning | Transitions |
|---|---|---|
active |
Rule applied in backend and tracked in index | → inactive (deactivate), → expired (TTL), → removed (delete) |
inactive |
Removed from backend, retained in index | → active (re-activate) |
pending |
Being created (transient state) | → active |
expired |
TTL expired, auto-deactivated | -- (terminal state, can be removed) |
Defined as DurationType(StrEnum):
| Type | Behavior |
|---|---|
permanent |
Rule persists until explicitly removed or deactivated. No TTL. |
temporary |
Rule has a TTL (60-2,592,000 seconds). ExpiryMonitor auto-deactivates when expired. |
Defined as ConnState(StrEnum):
| State | Kernel Module | Description | Common Use |
|---|---|---|---|
new |
conntrack | First packet of a new connection | Rate limiting new connections |
established |
conntrack | Packet in an existing tracked connection | Stateful allow rules |
related |
conntrack | New connection related to existing (e.g., FTP data channel) | Stateful allow rules |
invalid |
conntrack | Does not match any known connection | Drop suspicious traffic |
untracked |
conntrack | Explicitly excluded from tracking | High-performance bypass |
Multiple states comma-separated: --conn-state new,established,related
Defined as SyslogLevel(StrEnum). Used with --log-level on firewall LOG actions (distinct from framework LogLevel):
| Level | Severity | When to Use |
|---|---|---|
emerg |
0 -- Emergency | System unusable |
alert |
1 -- Alert | Immediate action required |
crit |
2 -- Critical | Critical conditions |
err |
3 -- Error | Error conditions |
warning |
4 -- Warning | Warning conditions (default for LOG actions) |
notice |
5 -- Notice | Normal but significant |
info |
6 -- Informational | Informational messages |
debug |
7 -- Debug | Debug-level messages |
Precompiled regex patterns in Pattern class. All use re.fullmatch() (whitelist -- reject anything that doesn't match completely):
| Pattern | Regex | Validates |
|---|---|---|
IPV4 |
^([0-9]{1,3}\.){3}[0-9]{1,3}$ |
IPv4 format (octet range checked separately) |
IPV6 |
^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$ |
IPv6 format (supports :: compression) |
CIDR_V4 |
^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ |
IPv4 CIDR (prefix range checked separately) |
CIDR_V6 |
^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}/[0-9]{1,3}$ |
IPv6 CIDR |
PORT |
^[0-9]{1,5}$ |
Port number format (range checked separately: 1-65535) |
PORT_RANGE |
^[0-9]{1,5}[-:][0-9]{1,5}$ |
Port range: 8080-8090 or 8080:8090 |
PROTOCOL |
^(tcp|udp|icmp|icmpv6|sctp|all)$ |
Allowed protocols |
HOSTNAME |
RFC 1123 pattern | Labels ≤63 chars, alphanumeric + hyphens |
INTERFACE |
^[a-zA-Z][a-zA-Z0-9._-]{0,14}$ |
Network interface: starts with letter, ≤15 chars |
ZONE |
^[a-zA-Z][a-zA-Z0-9_-]{0,31}$ |
Firewalld zone: starts with letter, ≤32 chars |
CHAIN |
^[a-zA-Z][a-zA-Z0-9_-]{0,63}$ |
Chain name: starts with letter, ≤64 chars |
TABLE |
^[a-zA-Z][a-zA-Z0-9_]{0,31}$ |
Table name: starts with letter, ≤32 chars |
IPSET_NAME |
^[a-zA-Z][a-zA-Z0-9_-]{0,30}$ |
ipset name: starts with letter, ≤31 chars |
RULE_ID |
^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$ |
UUID v4 format |
SAFE_PATH |
^[a-zA-Z0-9/_. ~:+\-]+$ |
Safe file path characters |
NUMERIC |
^[0-9]+$ |
Unsigned integer |
ALNUM_SAFE |
^[a-zA-Z0-9_-]+$ |
Safe identifier |
SHELL_METACHARACTERS frozenset: ; | & \ $ ( ) { } \ < > ! #`
These 15 characters are rejected by _contains_shell_meta() using frozenset intersection (O(1) per character). Any input containing these characters is rejected by validators that call this function (ports, chains, tables, zones, interfaces, ipset names, descriptions, hostnames).
IPTABLES_TABLES frozenset: {filter, nat, mangle, raw, security}
| Table | Purpose | Default Chains |
|---|---|---|
filter |
Packet filtering (default) | INPUT, OUTPUT, FORWARD |
nat |
Network address translation | PREROUTING, POSTROUTING, OUTPUT |
mangle |
Packet alteration | PREROUTING, INPUT, OUTPUT, FORWARD, POSTROUTING |
raw |
Pre-conntrack processing | PREROUTING, OUTPUT |
security |
SELinux/AppArmor integration | INPUT, OUTPUT, FORWARD |
IPTABLES_BUILTIN_CHAINS frozenset: {INPUT, OUTPUT, FORWARD, PREROUTING, POSTROUTING}
| Direction | Chain | Table |
|---|---|---|
inbound |
INPUT |
filter |
outbound |
OUTPUT |
filter |
forward |
FORWARD |
filter |
iptables LOG is a non-terminating target. log,drop creates two separate kernel rules:
iptables -A INPUT ... -j LOG --log-prefix "apotropaios:UUID: " --log-level infoiptables -A INPUT ... -j DROP
Both rules share identical match criteria. Removal deletes both rules.
| Rule Parameter | iptables Arguments |
|---|---|
| protocol | -p tcp |
| src_ip |
--source 10.0.0.1 or --source 10.0.0.0/24
|
| dst_ip | --destination 192.168.1.0/24 |
| src_port |
--sport 8080 (requires -p tcp or -p udp) |
| dst_port |
--dport 443 (requires -p tcp or -p udp) |
| interface (inbound) | -i eth0 |
| interface (outbound) | -o eth0 |
| conn_state | -m conntrack --ctstate NEW,ESTABLISHED |
| limit | -m limit --limit 5/minute |
| limit_burst | --limit-burst 10 |
| comment | -m comment --comment "apotropaios:UUID" |
| log_prefix | --log-prefix "PREFIX: " |
| log_level | --log-level warning |
| Operation | iptables Commands |
|---|---|
block_all() |
iptables -P INPUT DROP, iptables -P OUTPUT DROP, iptables -P FORWARD DROP
|
allow_all() |
iptables -F (flush all rules), then iptables -P INPUT/OUTPUT/FORWARD ACCEPT
|
reset() |
For each table in {filter, nat, mangle, raw, security}: iptables -t TABLE -F, iptables -t TABLE -X. Then set all policies to ACCEPT. |
NFTABLES_TABLE_FAMILIES frozenset: {inet, ip, ip6, arp, bridge, netdev}
| Family | Description | Default for Framework |
|---|---|---|
inet |
IPv4 + IPv6 combined | Yes (used by default) |
ip |
IPv4 only | No |
ip6 |
IPv6 only | No |
arp |
ARP protocol | No |
bridge |
Ethernet bridging | No |
netdev |
Ingress/egress hook | No |
Apotropaios auto-creates the nftables table and chain if they don't exist:
- Table:
nft add table inet apotropaios - Chain:
nft add chain inet apotropaios input { type filter hook input priority 0 \; policy accept \; }
nftables combines compound actions in a single expression:
nft add rule inet apotropaios input tcp dport 443 log prefix "apotropaios:UUID: " drop
| Direction | Chain | Hook |
|---|---|---|
inbound |
input |
hook input priority 0 |
outbound |
output |
hook output priority 0 |
forward |
forward |
hook forward priority 0 |
firewalld uses zones to group network interfaces with trust levels. All rule operations include the --zone parameter (default: public).
| Common Zone | Trust Level | Description |
|---|---|---|
drop |
Lowest | All incoming dropped, no reply |
block |
Low | Incoming rejected with icmp-host-prohibited |
public |
Low | Default zone. Selected incoming allowed. |
external |
Low | For external-facing interfaces with masquerading |
dmz |
Low | For DMZ servers with limited access |
work |
Medium | Trusted work network |
home |
Medium | Trusted home network |
internal |
Medium | Internal network |
trusted |
Highest | All traffic accepted |
_build_rich_rule() constructs rich rules:
rule family="ipv4" source address="10.0.0.1" port port="443" protocol="tcp" accept
rule family="ipv4" source address="10.0.0.0/24" port port="22" protocol="tcp" log prefix="SSH: " level="info" accept
All firewalld operations use --permanent followed by --reload:
firewall-cmd --zone=public --add-rich-rule='...' --permanent
firewall-cmd --reload
reset() iterates ALL zones (not just the default). For each zone, removes all rich rules and reloads:
firewall-cmd --zone=<z> --list-rich-rules # Get current rules
firewall-cmd --zone=<z> --remove-rich-rule='...' --permanent # Remove each
firewall-cmd --reload
| Framework Action | UFW Verb | Notes |
|---|---|---|
accept |
allow |
ufw allow <port>/tcp |
drop |
deny |
ufw deny <port>/tcp |
reject |
reject |
ufw reject <port>/tcp |
log,drop |
deny + logging |
Terminal action becomes ufw verb; ufw logging on enables logging |
log,accept |
allow + logging |
Same approach |
All UFW commands include --force to suppress interactive confirmation prompts. Without this, ufw enable asks "Proceed with operation (y|n)?" which hangs in non-interactive mode.
| Direction | UFW Syntax |
|---|---|
inbound |
ufw allow in <port>/tcp or ufw allow in from <ip> to any port <port>
|
outbound |
ufw allow out <port>/tcp |
IPSET_TYPES frozenset:
| Set Type | Entry Format | Use Case |
|---|---|---|
hash:ip |
Single IP address | Blocklists, allowlists |
hash:net |
CIDR network | Network-based filtering |
hash:ip,port |
IP + protocol + port | Service-specific filtering |
hash:net,port |
Network + protocol + port | Network + service filtering |
hash:net,iface |
Network + interface | Interface-specific network rules |
list:set |
Set of sets | Hierarchical set organization |
ipset creates the set, then creates an iptables rule referencing it:
ipset create blocklist hash:ip
ipset add blocklist 10.0.0.50
iptables -A INPUT -m set --match-set blocklist src -j DROP
Removal reverses: remove iptables rule first (_remove_iptables_refs()), then destroy set.
_validate_entry() validates entries against the set type:
-
hash:ip→validate_ip() -
hash:net→validate_cidr() -
hash:ip,port→ IP + protocol:port format validation
SUPPORTED_OS tuple of OSInfo NamedTuples:
| os_id | Display Name | Package Manager | Family |
|---|---|---|---|
ubuntu |
Ubuntu | apt |
debian |
kali |
Kali Linux | apt |
debian |
debian |
Debian 12 | apt |
debian |
rocky |
Rocky Linux 9 | dnf |
rhel |
almalinux |
AlmaLinux 9 | dnf |
rhel |
arch |
Arch Linux | pacman |
arch |
OS_FAMILY_MAP maps os_id and id_like values to families:
| Key | Family |
|---|---|
| ubuntu, debian, kali | debian |
| rhel, centos, fedora, rocky, almalinux | rhel |
| opensuse | suse |
| arch | arch |
DirPath class (relative to framework base directory):
| Constant | Value | Purpose |
|---|---|---|
CONF |
conf |
Configuration files |
DATA |
data |
Runtime data root |
LOGS |
data/logs |
Per-execution log files |
RULES |
data/rules |
Rule index and state files |
BACKUPS |
data/backups |
Backup archives |
TEMP |
data/.tmp |
Temporary staging |
FileName class:
| Constant | Value | Purpose |
|---|---|---|
CONF |
apotropaios.conf |
Framework configuration |
RULE_INDEX |
rule_index.dat |
Pipe-delimited rule index (27 fields) |
RULE_STATE |
rule_state.dat |
TTL and lifecycle state tracking |
LOCK |
apotropaios.lock |
Advisory lock file |
PID |
apotropaios.pid |
Process ID file |
CLI_COMMANDS tuple (18 commands):
| Command | Help Bypass Init? | Requires Root? | Description |
|---|---|---|---|
menu |
No | Yes | Launch interactive menu |
help |
Yes | No | Show global help |
detect |
No | Yes | OS and firewall detection |
status |
No | Yes | Service state display |
add-rule |
No | Yes | Create and apply rule |
remove-rule |
No | Yes | Remove rule by UUID |
activate-rule |
No | Yes | Re-activate deactivated rule |
deactivate-rule |
No | Yes | Deactivate (keep in index) |
list-rules |
No | Yes | Show Apotropaios-tracked rules |
system-rules |
No | Yes | Dump native backend rules |
block-all |
No | Yes | Drop all traffic |
allow-all |
No | Yes | Allow all traffic |
import |
No | Yes | Import rules from file |
export |
No | Yes | Export rules to file |
backup |
No | Yes | Create backup archive |
restore |
No | Yes | Restore from archive |
install |
No | Yes | Install firewall package |
update |
No | Yes | Update firewall package |
Per-command help (COMMAND --help) also bypasses framework initialization.
| Flag | Position | Requires Init? | Description |
|---|---|---|---|
--help, -h
|
Any | No | Global or per-command help |
--version, -v
|
Any | No | Version and exit |
--interactive |
Before command | Yes | Launch menu (mutually exclusive with commands) |
--non-interactive |
Any | Yes | Disable prompts (mutually exclusive with --interactive) |
--log-level LEVEL |
Any | Yes | Console verbosity: trace/debug/info/warning/error/critical/none |
--backend NAME |
Any | Yes | Select backend: iptables/nftables/firewalld/ufw/ipset |
All flags are position-independent (work before or after the command name).
CANCEL_KEYWORDS frozenset: {q, quit, cancel, back, b}
Type any of these at any interactive menu prompt to cancel the current operation and return to the previous menu. Case-insensitive, whitespace-trimmed.
Apotropaios Firewall Manager -- Python Variant · Home · Quick Start · Architecture · Security · Bash Variant
Apotropaios -- Python Variant
Getting Started
Architecture
Operations
Project
35 files · 14,545 lines · 322 tests