Skip to content

Commit a7a737b

Browse files
authored
Merge pull request #8 from 01rabbit/feature/dynamic-wan-selection
display: refresh EPD on WAN interface change and improve WAN detection
2 parents a2c4ee2 + eee6e81 commit a7a737b

Some content is hidden

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

43 files changed

+1732
-268
lines changed

.github/workflows/ci.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main, feature/** ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: [3.11]
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v4
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
25+
- name: Create virtualenv (.venv)
26+
run: |
27+
python -m venv .venv
28+
.venv/bin/python -m pip install --upgrade pip
29+
30+
- name: Install project and test dependencies
31+
run: |
32+
# Install package in editable mode and test extras from pyproject
33+
.venv/bin/pip install -e '.[test]'
34+
# Pillow is optional in pyproject; ensure it is present for renderer tests
35+
.venv/bin/pip install pillow
36+
37+
- name: Run tests
38+
run: .venv/bin/pytest -v
39+
40+
- name: Upload pytest JUnit result (optional)
41+
if: always()
42+
uses: actions/upload-artifact@v4
43+
with:
44+
name: pytest-log
45+
path: .pytest_cache

CHANGELOG.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [3.0.0] - 2025-11-09
6+
### Added
7+
- Dynamic WAN selection and runtime orchestration via `azctl wan-manager`:
8+
- Evaluates candidate uplink interfaces and selects the healthiest WAN at boot and runtime.
9+
- Writes health snapshots to `runtime/wan_state.json` (production path `/var/run/azazel/wan_state.json`); path can be overridden with `AZAZEL_WAN_STATE_PATH`.
10+
- Candidate precedence: explicit CLI `--candidate``AZAZEL_WAN_CANDIDATES` env var (comma-separated) → `configs/network/azazel.yaml` (`interfaces.external`/`interfaces.wan`) → safe fallbacks.
11+
- On WAN change, the manager reapplies traffic control (`bin/azazel-traffic-init.sh`), refreshes NAT, and restarts dependent services (Suricata, `azctl-unified`).
12+
13+
- Universal runtime interface resolution for consumers:
14+
- CLI/TUI, scripts, and services now prefer explicit CLI args → environment variables (`AZAZEL_WAN_IF` / `AZAZEL_LAN_IF`) → WAN manager state → configuration values → final fallback.
15+
- Added `AZAZEL_WAN_CANDIDATES` and `AZAZEL_WAN_STATE_PATH` environment variables for operational control and testing.
16+
17+
### Changed
18+
- Scripts and documentation updated to use parameterized interface references (`${AZAZEL_WAN_IF:-<fallback>}` and `${AZAZEL_LAN_IF:-<fallback>}`) in help text and examples. Where safe, runtime resolution now uses the WAN manager helper instead of hard-coded interface names.
19+
20+
### Notes
21+
- Backwards-compatible: explicit CLI flags and environment variables still override runtime selection. Existing deployments should continue to work; review scripts that assume literal interface names before automating deployment.
22+
- Tests and shell syntax checks were run after edits; no regressions detected in the unit test suite.
23+
- QoS features are opt-in via systemd service enablement.
24+
- All changes maintain backward compatibility with existing configurations.
25+
526
## [2.2.0] - 2025-11-07
627
### Added
728
- **Internal Network QoS Control**: Comprehensive privilege-based traffic shaping and security enforcement for LAN devices.
@@ -20,6 +41,7 @@ All notable changes to this project will be documented in this file.
2041
### Changed
2142
- QoS scripts support DRY_RUN mode (print commands without execution, no root required).
2243
- All QoS scripts are idempotent (safe to re-run).
44+
- Dynamic WAN selection: `wan-manager` now determines the active WAN interface at runtime and writes runtime/wan_state.json. Consumers (CLI, TUI, scripts) will use that selection by default when `--wan-if` is omitted. Environment variables `AZAZEL_WAN_IF` and `AZAZEL_LAN_IF` may be used to override defaults where needed.
2345

2446
### Security
2547
- MAC address verification prevents ARP spoofing for privileged devices.
@@ -34,8 +56,6 @@ All notable changes to this project will be documented in this file.
3456

3557
### Notes
3658
- Minor version bump (2.1.0 → 2.2.0) adds significant new QoS feature without breaking existing functionality.
37-
- QoS features are opt-in via systemd service enablement.
38-
- All changes maintain backward compatibility with existing configurations.
3959

4060
## [2.1.0] - 2025-11-07
4161
### Added
@@ -84,6 +104,18 @@ Semantic versioning: MAJOR.MINOR.PATCH. Deprecations queued for removal after at
84104
## [1.0.0] - 2025-10-05
85105
### Initial release
86106
- Initial public baseline of Azazel-Pi with core features:
107+
108+
## [3.1.0] - 2025-11-09
109+
### Added
110+
- Display: clear and force a full E-Paper refresh when the active WAN interface changes (e.g. eth0 -> wlan1) to avoid ghosting and show the updated interface/IP immediately. (commit 478b8ee)
111+
- Status collection: prefer kernel default route when runtime WAN state is missing and provide a `wan_state_path` injection point for testing/overrides.
112+
- Renderer: improve network line formatting by removing the redundant "WAN" prefix and suppressing non-actionable "[WAN] unknown" messages; reserve footer area to prevent text overlap.
113+
114+
### Changed
115+
- Backwards-compatible `StatusCollector` initialization handling in `epd_daemon` — older installs without the new `wan_state_path` parameter are tolerated.
116+
117+
### Notes
118+
- These are backward-compatible improvements (minor release). See commit 478b8ee for details and files changed: `azazel_pi/core/display/status_collector.py`, `epd_daemon.py`, `renderer.py`.
87119
- Suricata integration for network threat detection
88120
- AI-based threat evaluation pipeline and scoring
89121
- Basic TUI and CLI utilities for status and control

README.md

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,18 @@ Lightweight configuration optimized for Raspberry Pi, enabling rapid deployment
145145
After cloning the repository or downloading a release, run the complete automated installer:
146146

147147
```bash
148-
cd Azazel-Pi
149-
# Complete installation with all dependencies and configurations
148+
# Launch TUI menu. If you omit --wan-if the CLI will dynamically resolve the WAN
149+
# interface using the WAN manager (recommended). You can also force an interface
150+
# via the AZAZEL_WAN_IF / AZAZEL_LAN_IF environment variables.
151+
# Example: prefer runtime selection — WAN will be resolved automatically when omitted.
152+
# You can override the detected interfaces with environment variables:
153+
# export AZAZEL_LAN_IF=${AZAZEL_LAN_IF:-wlan0}
154+
# export AZAZEL_WAN_IF=${AZAZEL_WAN_IF:-wlan1}
155+
# then run the CLI without the --wan-if flag if you want the runtime helper to pick the WAN.
156+
python3 -m azctl.cli menu --lan-if ${AZAZEL_LAN_IF:-wlan0}
157+
# or: omit --wan-if to let the system choose the active WAN interface
158+
python3 -m azctl.cli menu --lan-if ${AZAZEL_LAN_IF:-wlan0}
159+
```
150160
sudo scripts/install_azazel_complete.sh --start
151161

152162
# Or step-by-step installation:
@@ -203,6 +213,35 @@ sudo systemctl enable --now azazel-epd.service
203213

204214
See [`docs/en/EPD_SETUP.md`](docs/en/EPD_SETUP.md) for complete E-Paper configuration instructions.
205215

216+
## Running tests (developer)
217+
218+
This project uses a local virtual environment at `.venv` for development tests. To run the unit tests that exercise E-Paper rendering in emulation mode, do the following:
219+
220+
1. Activate or create the virtual environment (example):
221+
222+
```bash
223+
python3 -m venv .venv
224+
source .venv/bin/activate
225+
pip install -U pip
226+
pip install -r requirements-dev.txt
227+
```
228+
229+
2. Install optional dependencies used by E-Paper rendering (Pillow) if not included in `requirements-dev.txt`:
230+
231+
```bash
232+
pip install pillow
233+
```
234+
235+
3. Run tests (example):
236+
237+
```bash
238+
.venv/bin/pytest tests/core/test_epd_daemon.py -q
239+
```
240+
241+
Notes:
242+
- The E-Paper renderer supports `--emulate` which avoids hardware access and writes a PNG file when run in `--mode test`.
243+
- Use `--wan-state-path` to point the renderer/collector at a custom WAN state file for integration testing.
244+
206245
### Optional: Front Mattermost with Nginx
207246

208247
To serve Mattermost via Nginx reverse proxy (recommended), use the provided template and setup script:
@@ -229,7 +268,7 @@ The interactive Terminal User Interface (TUI) menu provides comprehensive system
229268
python3 -m azctl.cli menu
230269

231270
# With specific interface configuration
232-
python3 -m azctl.cli menu --lan-if wlan0 --wan-if wlan1
271+
python3 -m azctl.cli menu --lan-if ${AZAZEL_LAN_IF:-wlan0} --wan-if ${AZAZEL_WAN_IF:-wlan1}
233272
```
234273

235274
**Modular Architecture:**
@@ -293,11 +332,16 @@ echo '{"mode": "lockdown"}' | azctl events --config -
293332
The modular TUI menu provides comprehensive system management:
294333

295334
```bash
296-
# Launch modular TUI menu
335+
# Launch modular TUI menu. If --wan-if is omitted, azctl will consult the
336+
# WAN manager to select the active WAN interface. To override selection use
337+
# the CLI flags or environment variables described below.
297338
python3 -m azctl.cli menu
298339

299-
# Specify custom interfaces
300-
python3 -m azctl.cli menu --lan-if wlan0 --wan-if wlan1
340+
# Specify custom interfaces (explicit override)
341+
python3 -m azctl.cli menu --lan-if ${AZAZEL_LAN_IF:-wlan0} --wan-if ${AZAZEL_WAN_IF:-wlan1}
342+
343+
# Or let the system choose WAN automatically:
344+
python3 -m azctl.cli menu --lan-if ${AZAZEL_LAN_IF:-wlan0}
301345
```
302346

303347
**Menu Features:**
@@ -386,7 +430,12 @@ python3 -m azctl.cli menu --lan-if wlan0 --wan-if wlan1
386430
### Configuration Workflow
387431

388432
1. **Edit Core Configuration**: Modify `/etc/azazel/azazel.yaml` to adjust delay values, bandwidth controls, and lockdown allowlists (template at `configs/network/azazel.yaml`).
389-
- By default, `wlan0` is treated as the internal LAN (AP), and both `wlan1` and `eth0` are considered external (WAN/uplink) interfaces. See `interfaces.external: ["eth0", "wlan1"]` in `configs/network/azazel.yaml` and adjust as needed.
433+
- Interface defaults: `${AZAZEL_LAN_IF:-wlan0}` is typically treated as the internal LAN (AP); `${AZAZEL_WAN_IF:-wlan1}` and `${AZAZEL_WAN_IF:-eth0}` are common external (WAN/uplink) candidates and are listed under `interfaces.external` in `configs/network/azazel.yaml`.
434+
Note: Azazel now prefers a runtime WAN selection produced by the WAN manager when `--wan-if` is not provided. To explicitly override the chosen interfaces, set the environment variables `AZAZEL_WAN_IF` and/or `AZAZEL_LAN_IF` before running commands or scripts.
435+
- Override options:
436+
- CLI: pass `--lan-if` and/or `--wan-if` to `azctl` commands to explicitly set interfaces.
437+
- Environment: set `AZAZEL_LAN_IF` or `AZAZEL_WAN_IF` to change defaults for scripts and services.
438+
- Dynamic: if `--wan-if` is omitted, `azctl` will query the WAN manager (recommended) to pick the active WAN interface based on runtime health checks.
390439

391440
2. **Generate Suricata Rules**: Use `scripts/suricata_generate.py` to render environment-specific IDS configurations
392441

@@ -396,6 +445,32 @@ python3 -m azctl.cli menu --lan-if wlan0 --wan-if wlan1
396445

397446
5. **Monitor Operations**: Analyze scoring results in `decisions.log` and use `azctl` for manual mode switching during incidents
398447

448+
### Dynamic WAN Selection (NEW)
449+
450+
- The `azctl wan-manager` service evaluates all candidate WAN interfaces (from `interfaces.external`) after boot and continuously during runtime.
451+
- Health snapshots (link status, IP presence, estimated speed) are written to `runtime/wan_state.json` (or `/var/run/azazel/wan_state.json` on deployed systems) and surfaced on the E-Paper display. You can override the default path with the `AZAZEL_WAN_STATE_PATH` environment variable when testing or for non-standard deployments.
452+
- The WAN manager reads candidate lists in order of precedence: explicit CLI `--candidate` arguments, the `AZAZEL_WAN_CANDIDATES` environment variable (comma-separated), values declared in `configs/network/azazel.yaml` (`interfaces.external` or `interfaces.wan`), then safe fallbacks. Use `AZAZEL_WAN_CANDIDATES` to force a specific candidate ordering without changing config files.
453+
- When the active interface changes, the manager reapplies `bin/azazel-traffic-init.sh`, refreshes NAT (`iptables -t nat`), and restarts dependent services (Suricata and `azctl-unified`) so they immediately consume the new interface.
454+
- Suricata now launches through `azazel_pi.core.network.suricata_wrapper`, which reads the same WAN state file, so restarting the service is sufficient to follow the latest selection.
455+
456+
Developer note — non-root testing and fallback behavior
457+
458+
- The WAN manager will attempt to write the runtime state file to a system runtime path (for example `/var/run/azazel/wan_state.json`) when running as a system service. On systems where the process does not have permission to create `/var/run/azazel`, the manager now falls back automatically to a repository-local path `runtime/wan_state.json` so developers can run and test `azctl wan-manager` without root.
459+
- For explicit control in tests or non-standard deployments, set `AZAZEL_WAN_STATE_PATH` to a writable path before running the manager. Example (development):
460+
461+
```bash
462+
# write state into the repository runtime directory (no root required)
463+
AZAZEL_WAN_STATE_PATH=runtime/wan_state.json python3 -m azctl.cli wan-manager --once
464+
```
465+
466+
- For production systems, run the WAN manager via systemd (root) so that traffic-init, iptables/nft, and service restarts run with the required privileges. Example (recommended for deployed systems):
467+
468+
```bash
469+
sudo systemctl enable --now azazel-wan-manager.service
470+
```
471+
472+
These options allow safe developer testing while preserving the intended privileged behavior in production.
473+
399474
### Defensive Mode Operations
400475

401476
- **Portal Mode**: Baseline monitoring with minimal network impact

README_ja.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,11 @@ sudo systemctl enable --now azazel-epd.service
202202
python3 -m azctl.cli menu
203203

204204
# 特定のインターフェース設定で起動
205-
python3 -m azctl.cli menu --lan-if wlan0 --wan-if wlan1
205+
# 例: 実行時のWAN選択を優先します。`--wan-if` を省略した場合、WANマネージャが既定値を選択します。
206+
# 必要に応じて環境変数で上書きできます:
207+
# export AZAZEL_LAN_IF=${AZAZEL_LAN_IF:-wlan0}
208+
# export AZAZEL_WAN_IF=${AZAZEL_WAN_IF:-wlan1}
209+
python3 -m azctl.cli menu --lan-if ${AZAZEL_LAN_IF:-wlan0}
206210
```
207211

208212
**モジュラーアーキテクチャ:**
@@ -283,13 +287,14 @@ echo '{"mode": "lockdown"}' | azctl events --config -
283287
python3 -m azctl.cli menu
284288

285289
# カスタムインターフェースを指定
286-
python3 -m azctl.cli menu --lan-if wlan0 --wan-if wlan1
290+
python3 -m azctl.cli menu --lan-if ${AZAZEL_LAN_IF:-wlan0} --wan-if ${AZAZEL_WAN_IF:-wlan1}
287291
```
288292

289293
**メニュー機能:**
290294

291295
1. **コア設定の編集**: `/etc/azazel/azazel.yaml` を修正して遅延値、帯域制御、ロックダウン許可リストを調整(テンプレートは `configs/network/azazel.yaml`)。
292-
- 既定では `wlan0` を内部LAN(AP)、`wlan1``eth0` を外部(WAN/アップリンク)として扱います。`configs/network/azazel.yaml``interfaces.external``["eth0", "wlan1"]` を定義済みです(必要に応じて変更可能)。
296+
- 既定では `wlan0` を内部LAN(AP)、`wlan1``eth0` を外部(WAN/アップリンク)として扱います。`configs/network/azazel.yaml``interfaces.external``["eth0", "wlan1"]` を定義済みです(必要に応じて変更可能)。
297+
注: `--wan-if` を指定しない場合、WAN 管理コンポーネントがランタイムで最適な WAN インターフェイスを選択します。明示的に指定したい場合は `AZAZEL_WAN_IF` / `AZAZEL_LAN_IF` を環境変数で設定してください。
293298

294299
2. **Suricataルール生成**: `scripts/suricata_generate.py` を使用して環境固有のIDS設定をレンダリング
295300

@@ -412,6 +417,33 @@ azctl/menu/
412417
- [`docs/ja/API_REFERENCE.md`](docs/ja/API_REFERENCE.md) — Pythonモジュールとスクリプトリファレンス
413418
- [`docs/ja/SURICATA_INSTALLER.md`](docs/ja/SURICATA_INSTALLER.md) — Suricataインストールと設定詳細
414419

420+
#### 動的WAN切り替え(新機能)
421+
422+
- `azctl wan-manager` サービスが `interfaces.external` に列挙されたインターフェースを順番にヘルスチェックし、起動直後と運用中の両方で最も安定した WAN を自動選択します。
423+
- 選定結果と各インターフェースの状態は `runtime/wan_state.json`(本番では `/var/run/azazel/wan_state.json`)に記録され、E-Paper 画面にも「再設定中」「WAN切替完了」といったメッセージで表示されます。テストやカスタム配置では `AZAZEL_WAN_STATE_PATH` 環境変数で状態ファイルの場所を上書きできます。
424+
- WAN マネージャは候補の読み取り順序(優先順位)を持ちます: 明示的な CLI の `--candidate``AZAZEL_WAN_CANDIDATES` 環境変数(カンマ区切り)→ `configs/network/azazel.yaml``interfaces.external` / `interfaces.wan` → フォールバック。設定ファイルを直接編集せずに候補順序を制御したい環境では `AZAZEL_WAN_CANDIDATES` を利用してください。
425+
- 切り替え時には `bin/azazel-traffic-init.sh`、NAT (`iptables -t nat`) を再適用し、Suricata と `azctl-unified` を順次再起動して即座に新しいインターフェースを利用させます。
426+
- Suricata は `azazel_pi.core.network.suricata_wrapper` を経由して起動するため、サービス再起動だけで常に最新の WAN 状態を参照できます。
427+
428+
開発者向けメモ — 非 root 環境でのテストとフォールバック動作
429+
430+
- WAN マネージャは通常システムのランタイムパス(例: `/var/run/azazel/wan_state.json`)へ状態を書き込みます。systemd 等で root 権限で実行される本番環境ではこれが期待どおりに動作します。
431+
- 一方で開発や CI 環境などでプロセスに `/var/run/azazel` を作成する権限が無い場合、現在の実装は自動的にリポジトリ内の `runtime/wan_state.json` にフォールバックするため、非 root ユーザーでも `azctl wan-manager` を実行して動作確認ができます。
432+
- 明示的に書き込み先を指定したい場合は `AZAZEL_WAN_STATE_PATH` 環境変数を設定してください(開発例):
433+
434+
```bash
435+
# リポジトリ内の runtime ディレクトリに状態を書き込む(root 不要)
436+
AZAZEL_WAN_STATE_PATH=runtime/wan_state.json python3 -m azctl.cli wan-manager --once
437+
```
438+
439+
- 本番運用では systemd(root)経由で WAN マネージャを動かすことを推奨します。これにより `tc`/`iptables`(または `nft`)やサービス再起動など、特権を要する処理が正しく行われます。例(推奨):
440+
441+
```bash
442+
sudo systemctl enable --now azazel-wan-manager.service
443+
```
444+
445+
これにより、開発者は権限に縛られずにローカルで動作確認ができ、本番では特権を持った実行で期待どおりの自動適用が行われます。
446+
415447
## 開発の背景
416448

417449
現代のサイバー攻撃はますます高速化・自動化されており、従来のハニーポットでは不十分です。このシステムは **観察やブロックではなく戦術的遅延** を目的として設計されており、時間を防御資産として活用します。

0 commit comments

Comments
 (0)