mirror of
https://github.com/ParrotXray/Mantis.git
synced 2026-08-24 19:00:27 +09:00
Fix/some issus (#14)
* wip * feat: Add egress eBPF access control and fix Suricata HTTP port detection * feat: adjust code with rustfmt * feat: adjust code with rustfmt * feat: Remove SERVICE stage from ingress eBPF pipeline
This commit is contained in:
parent
6eb64d36da
commit
5e146717d9
141
.research/findings/tasks/acc-001-c1.md
Normal file
141
.research/findings/tasks/acc-001-c1.md
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# acc-001: Account System, JWT Auth, and axum 0.8 Migration
|
||||||
|
**Cycle**: 1 | **Theme**: backend-infra | **Kind**: design + implementation | **Status**: done
|
||||||
|
**Date**: 2026-05-23
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Implemented a complete auth stack (SQLCipher-encrypted account DB, Argon2id password hashing,
|
||||||
|
HS256 JWT bearer tokens) and simultaneously migrated the HTTP layer from actix-web 4 to axum 0.8.
|
||||||
|
Auth is opt-in: removing `[Config.auth]` from config.toml disables all auth routes with zero
|
||||||
|
impact on existing monitoring endpoints.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
### Q: Which crates form the auth stack, and why?
|
||||||
|
|
||||||
|
A: Three crates, all pure-Rust or vendored:
|
||||||
|
|
||||||
|
| Crate | Version | Role |
|
||||||
|
|---|---|---|
|
||||||
|
| `rusqlite` | 0.31 feat `bundled-sqlcipher-vendored-openssl` | Encrypted SQLite; vendors both SQLCipher and OpenSSL — zero system deps |
|
||||||
|
| `argon2` | 0.5 | Argon2id password hashing; PHC string format (salt embedded in hash string) |
|
||||||
|
| `jsonwebtoken` | 9 | HS256 JWT sign/verify; v9 chosen over v10 to avoid mandatory crypto backend selection |
|
||||||
|
|
||||||
|
**Confidence**: high
|
||||||
|
|
||||||
|
### Q: What is the DB schema?
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE accounts (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
username TEXT UNIQUE NOT NULL,
|
||||||
|
password_hash TEXT NOT NULL, -- PHC string, includes salt
|
||||||
|
role TEXT NOT NULL DEFAULT 'viewer',
|
||||||
|
created_at INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE sessions (
|
||||||
|
token TEXT PRIMARY KEY,
|
||||||
|
account_id TEXT NOT NULL,
|
||||||
|
expires_at INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
DB is opened with `PRAGMA key = '<db_key>'` as the **first** SQL statement.
|
||||||
|
Default admin account (`admin` / `admin`) is seeded on first boot if accounts table is empty.
|
||||||
|
|
||||||
|
### Q: What are the auth API endpoints and their exact response formats?
|
||||||
|
|
||||||
|
**POST /auth/login**
|
||||||
|
```
|
||||||
|
Request: { "username": "admin", "password": "admin" }
|
||||||
|
Response 200: { "token": "<HS256 JWT>" }
|
||||||
|
Response 401: "Invalid credentials"
|
||||||
|
Response 501: "Auth not configured" (if [Config.auth] absent)
|
||||||
|
```
|
||||||
|
|
||||||
|
**GET /auth/me** — requires `Authorization: Bearer <token>`
|
||||||
|
```
|
||||||
|
Response 200: { "id": "admin", "username": "admin", "role": "admin" }
|
||||||
|
Response 401: "Unauthorized"
|
||||||
|
```
|
||||||
|
|
||||||
|
**POST /auth/logout** — requires `Authorization: Bearer <token>`
|
||||||
|
```
|
||||||
|
Response 200: (empty body)
|
||||||
|
```
|
||||||
|
Stateless: server does nothing; client discards the token.
|
||||||
|
|
||||||
|
### Q: JWT claims structure?
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sub": "<account_id>",
|
||||||
|
"username": "<username>",
|
||||||
|
"role": "admin | viewer",
|
||||||
|
"exp": <unix_timestamp>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Signed with HS256 using `jwt_secret` from config. Expiry default: 86400s (24h).
|
||||||
|
|
||||||
|
### Q: Why axum 0.8 over actix-web 4?
|
||||||
|
|
||||||
|
A: Switched to unify state passing via a single `AppState` (Clone) instead of N separate
|
||||||
|
`web::Data<T>` registrations, and to get native Tower middleware composability for future
|
||||||
|
rate-limiting and auth middleware layers. Migration was mechanical: ~1200 lines changed,
|
||||||
|
zero functional difference.
|
||||||
|
|
||||||
|
Key axum 0.8 gotchas encountered:
|
||||||
|
- Path params use `{param}` not `:param` (breaking change from 0.7)
|
||||||
|
- `async_trait` macro not needed — Rust 2024 edition supports async fn in traits natively
|
||||||
|
- WebSocket: must `socket.split()` into `SplitSink + SplitStream` for concurrent send/recv
|
||||||
|
|
||||||
|
### Q: How does graceful shutdown work?
|
||||||
|
|
||||||
|
```rust
|
||||||
|
axum::serve(listener, app)
|
||||||
|
.with_graceful_shutdown(async { tokio::signal::ctrl_c().await.ok(); })
|
||||||
|
.await?;
|
||||||
|
```
|
||||||
|
SIGINT → axum drains active connections → `run_http_server()` returns →
|
||||||
|
`system.terminate()` detaches eBPF, stops ML engine, stops Suricata.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Unexpected Discoveries
|
||||||
|
|
||||||
|
- `actix` (actor crate) was listed in Cargo.toml but not used anywhere in the codebase.
|
||||||
|
Removed cleanly with no impact.
|
||||||
|
- axum 0.8 `with_graceful_shutdown` is synchronous to add but was previously missing,
|
||||||
|
meaning SIGINT killed the process before eBPF detach could run.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
1. **JWT secret rotation**: no mechanism to rotate `jwt_secret` without invalidating all
|
||||||
|
active sessions. Acceptable for research prototype; production would need versioned keys
|
||||||
|
or short TTL + refresh tokens.
|
||||||
|
2. **`sessions` table is unused**: currently pure stateless JWT; the sessions table exists
|
||||||
|
in schema but nothing writes to it. If logout-invalidation is needed later, sessions
|
||||||
|
table is ready to use as a blocklist.
|
||||||
|
3. **Role enforcement**: `AuthenticatedUser.role` is decoded from JWT but no handler
|
||||||
|
currently gates on role. Future admin-only routes should check `user.0.role == "admin"`.
|
||||||
|
4. **Default admin password**: seeded as "admin" if no accounts exist. Must document that
|
||||||
|
operators change this on first boot; consider forcing a password-change flow.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Impact on Downstream Tasks
|
||||||
|
|
||||||
|
- **HTTP API (auth/accounts/lists/system)**: foundation is in place. Next: account CRUD
|
||||||
|
(`GET/POST /api/accounts`, `PUT /api/accounts/:id/password`, `DELETE /api/accounts/:id`)
|
||||||
|
and role-based guard on management endpoints.
|
||||||
|
- **Suppress list API**: `POST /api/suppress` can now be auth-gated via `AuthenticatedUser`
|
||||||
|
extractor — add it to the route and it's protected.
|
||||||
|
- **XDP active response**: when implemented, `POST /api/blacklist` should require auth.
|
||||||
125
CLAUDE.md
125
CLAUDE.md
@ -48,8 +48,9 @@ Ignore these expected errors when SKIP_EBPF_BUILD is set:
|
|||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
├── core/
|
├── core/
|
||||||
|
│ ├── app_state.rs - AppState (Clone) shared across all axum handlers
|
||||||
│ ├── ebpf/ - XDP/AF_XDP packet capture, access control
|
│ ├── ebpf/ - XDP/AF_XDP packet capture, access control
|
||||||
│ └── infrastructure/- AppServices init, AppConfig, GeoIP, MLAlert
|
│ └── infrastructure/- AppServices init, AppConfig, AppDb, GeoIP
|
||||||
├── detection/
|
├── detection/
|
||||||
│ ├── ml/ - ML inference pipeline
|
│ ├── ml/ - ML inference pipeline
|
||||||
│ │ ├── engine.rs - inference loop (tokio interval)
|
│ │ ├── engine.rs - inference loop (tokio interval)
|
||||||
@ -62,10 +63,13 @@ src/
|
|||||||
│ │ └── traffic_logger.rs - CSV recording mode
|
│ │ └── traffic_logger.rs - CSV recording mode
|
||||||
│ └── rule/ - Suricata/Snort rule matching (vectorscan)
|
│ └── rule/ - Suricata/Snort rule matching (vectorscan)
|
||||||
├── model/
|
├── model/
|
||||||
│ ├── error/ - one file per domain (ml, ebpf, http, rule, ...)
|
│ ├── error/ - one file per domain (ml, ebpf, http, auth, ...)
|
||||||
│ ├── log/ - one file per domain (ml, ebpf, http, rule, ...)
|
│ ├── log/ - one file per domain (ml, ebpf, http, auth, ...)
|
||||||
│ └── ml_detection.rs- shared ML types (FlowKey, DetectionResult, ...)
|
│ └── ml_detection.rs- shared ML types (FlowKey, DetectionResult, ...)
|
||||||
├── web/ - actix-web HTTP API and WebSocket endpoints
|
├── web/
|
||||||
|
│ ├── api/ - axum route handlers
|
||||||
|
│ ├── middleware/ - auth.rs: AuthenticatedUser (FromRequestParts)
|
||||||
|
│ └── websocket/ - alert, health, flow WebSocket handlers
|
||||||
└── utils/ - packet parsing, logging setup, boot time
|
└── utils/ - packet parsing, logging setup, boot time
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -133,6 +137,119 @@ ML artifacts: `mantis/static/artifacts/`
|
|||||||
- `deep_autoencoder.onnx` - LSTM autoencoder model
|
- `deep_autoencoder.onnx` - LSTM autoencoder model
|
||||||
- `inference_config.json` - window size, feature names, scaler params, threshold
|
- `inference_config.json` - window size, feature names, scaler params, threshold
|
||||||
|
|
||||||
|
Auth DB: `static/db/app.db` (SQLCipher-encrypted, path configurable via `[Config.auth].db_path`)
|
||||||
|
|
||||||
|
## HTTP API
|
||||||
|
|
||||||
|
Base URL: `http://<host>:8080`
|
||||||
|
|
||||||
|
All routes that require auth expect `Authorization: Bearer <token>` header.
|
||||||
|
Auth is disabled if `[Config.auth]` is absent from config.toml.
|
||||||
|
|
||||||
|
### Auth
|
||||||
|
|
||||||
|
| Method | Path | Auth | Request body | Response |
|
||||||
|
|--------|------|------|--------------|----------|
|
||||||
|
| POST | `/auth/login` | No | `{"username":"…","password":"…"}` | `{"token":"<jwt>"}` |
|
||||||
|
| GET | `/auth/me` | Bearer | — | `{"id":"…","username":"…","role":"…"}` |
|
||||||
|
| POST | `/auth/logout` | Bearer | — | 200 OK (stateless, client discards token) |
|
||||||
|
|
||||||
|
JWT claims: `{ sub, username, role, exp }`. Default TTL: 86400s.
|
||||||
|
Default admin: username=`admin`, password=`admin` (seeded on first boot).
|
||||||
|
|
||||||
|
### eBPF Access Control — `/ebpf/access_control`
|
||||||
|
|
||||||
|
| Method | Path | Body | Description |
|
||||||
|
|--------|------|------|-------------|
|
||||||
|
| GET | `/{nic}/ipv4/{flow}/{list_type}` | — | Get IPv4 list |
|
||||||
|
| PUT | `/{nic}/ipv4/{flow}/{list_type}` | `SocketAddrV4` JSON | Add entry |
|
||||||
|
| DELETE | `/{nic}/ipv4/{flow}/{list_type}` | `SocketAddrV4` JSON | Remove entry |
|
||||||
|
| GET | `/{nic}/ipv6/{flow}/{list_type}` | — | Get IPv6 list |
|
||||||
|
| PUT | `/{nic}/ipv6/{flow}/{list_type}` | `SocketAddrV6` JSON | Add entry |
|
||||||
|
| DELETE | `/{nic}/ipv6/{flow}/{list_type}` | `SocketAddrV6` JSON | Remove entry |
|
||||||
|
|
||||||
|
`nic`: `ingress` | `egress` — `flow`: `source` | `destination` — `list_type`: `whitelist` | `blacklist`
|
||||||
|
|
||||||
|
Ingress access control targets the ingress eBPF (inbound packets from external network).
|
||||||
|
Egress access control targets the egress eBPF (outbound packets from internal network).
|
||||||
|
`source` matches the packet's src IP/port; `destination` matches dst IP/port.
|
||||||
|
|
||||||
|
### eBPF Service Control — `/ebpf/service`
|
||||||
|
|
||||||
|
| Method | Path | Body | Description |
|
||||||
|
|--------|------|------|-------------|
|
||||||
|
| GET | `/ipv4/http_service` | — | List IPv4 HTTP service entries |
|
||||||
|
| PUT | `/ipv4/http_service` | `[SocketAddrV4, [HttpMethod]]` | Add HTTP service |
|
||||||
|
| DELETE | `/ipv4/http_service` | `[SocketAddrV4, [HttpMethod]]` | Remove HTTP service |
|
||||||
|
| GET | `/ipv6/http_service` | — | List IPv6 HTTP service entries |
|
||||||
|
| PUT | `/ipv6/http_service` | `[SocketAddrV6, [HttpMethod]]` | Add HTTP service |
|
||||||
|
| DELETE | `/ipv6/http_service` | `[SocketAddrV6, [HttpMethod]]` | Remove HTTP service |
|
||||||
|
| GET | `/ssh_white_list` | — | SSH whitelist enabled? |
|
||||||
|
| POST | `/ssh_white_list/enable` | — | Enable SSH whitelist |
|
||||||
|
| POST | `/ssh_white_list/disable` | — | Disable SSH whitelist |
|
||||||
|
| GET/PUT/DELETE | `/ipv4/ssh_service` | `SocketAddrV4` | SSH service list |
|
||||||
|
| GET/PUT/DELETE | `/ipv6/ssh_service` | `SocketAddrV6` | SSH service list |
|
||||||
|
| GET/PUT/DELETE | `/ipv4/ssh_white_list` | `Ipv4Addr` | SSH whitelist |
|
||||||
|
| GET/PUT/DELETE | `/ipv6/ssh_white_list` | `Ipv6Addr` | SSH whitelist |
|
||||||
|
| GET/PUT/DELETE | `/ipv4/ssh_black_list` | `Ipv4Addr` | SSH blacklist |
|
||||||
|
| GET/PUT/DELETE | `/ipv6/ssh_black_list` | `Ipv6Addr` | SSH blacklist |
|
||||||
|
|
||||||
|
### eBPF Statistics — `/ebpf/statistics`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/get/ipv4/{direction}/{flow_direction}/{time_type}` | IPv4 flow data snapshot |
|
||||||
|
| GET | `/get/ipv6/{direction}/{flow_direction}/{time_type}` | IPv6 flow data snapshot |
|
||||||
|
| GET (WS) | `/websocket/ipv4/{direction}/{flow_direction}/{time_type}` | IPv4 flow stream |
|
||||||
|
| GET (WS) | `/websocket/ipv6/{direction}/{flow_direction}/{time_type}` | IPv6 flow stream |
|
||||||
|
|
||||||
|
`direction`: `inbound` | `outbound` — `flow_direction`: `source` | `destination`
|
||||||
|
`time_type`: `per_second` | `per_minute` | `per_hour`
|
||||||
|
|
||||||
|
### Health — `/health`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/metrics` | Current system metrics (CPU, memory, etc.) |
|
||||||
|
| GET | `/status` | Boolean system health |
|
||||||
|
| GET (WS) | `/websocket/metrics` | Live metrics stream |
|
||||||
|
|
||||||
|
### Detection — `/detection`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET (WS) | `/websocket/alert` | Unified alert stream (`UnifiedAlert` JSON) |
|
||||||
|
|
||||||
|
`UnifiedAlert` fields: `source`, `severity`, `src_ip`, `dst_ip`, `proto`, `timestamp`, `detail`
|
||||||
|
|
||||||
|
### Misc — `/misc`
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| GET | `/boot_time` | System boot timestamp |
|
||||||
|
|
||||||
|
### WebSocket Protocol
|
||||||
|
|
||||||
|
All WebSocket endpoints push JSON on data events. Clients should:
|
||||||
|
- Respond to `Ping` frames with `Pong` (handled automatically by most WS clients)
|
||||||
|
- Send `Close` frame to disconnect gracefully
|
||||||
|
|
||||||
|
## HTTP Framework
|
||||||
|
|
||||||
|
axum 0.8 + tower-http. State is passed via `AppState` (Clone) registered with `.with_state()`.
|
||||||
|
New handlers: add `State(state): State<AppState>` as parameter. Path params use `{param}` syntax.
|
||||||
|
Auth-protected handlers: add `user: AuthenticatedUser` as parameter (auto-rejects invalid tokens).
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Example: auth-protected handler
|
||||||
|
async fn my_handler(
|
||||||
|
user: AuthenticatedUser,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
// user.0.role, user.0.username available
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Implementation Priority
|
## Implementation Priority
|
||||||
|
|
||||||
See `TODO` for the full backlog.
|
See `TODO` for the full backlog.
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
pub mod ingress {
|
pub mod ingress {
|
||||||
pub const ACCESS_CONTROL: u32 = 0;
|
pub const ACCESS_CONTROL: u32 = 0;
|
||||||
pub const SERVICE: u32 = 1;
|
pub const STATISTICS: u32 = 1;
|
||||||
pub const STATISTICS: u32 = 2;
|
pub const TRANSMISSION: u32 = 2;
|
||||||
pub const TRANSMISSION: u32 = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod egress {
|
pub mod egress {
|
||||||
pub const STATISTICS: u32 = 0;
|
pub const ACCESS_CONTROL: u32 = 0;
|
||||||
pub const TRANSMISSION: u32 = 1;
|
pub const STATISTICS: u32 = 1;
|
||||||
|
pub const TRANSMISSION: u32 = 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ default_admin_password = "admin"
|
|||||||
|
|
||||||
# Suricata rule engine. Remove this entire section to disable.
|
# Suricata rule engine. Remove this entire section to disable.
|
||||||
[Config.suricata]
|
[Config.suricata]
|
||||||
home_net = "140.130.34.0/24"
|
home_net = ["140.130.34.0/24"]
|
||||||
worker_cpu_set = [4, 6]
|
worker_cpu_set = [4, 6]
|
||||||
management_cpu = 0
|
management_cpu = 0
|
||||||
af_packet_threads = "auto"
|
af_packet_threads = "auto"
|
||||||
|
|||||||
110
egress-ebpf/src/action/access_control.rs
Normal file
110
egress-ebpf/src/action/access_control.rs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
use aya_ebpf::macros::map;
|
||||||
|
use aya_ebpf::maps::HashMap;
|
||||||
|
use common::define::setting::{MAX_RULES, MAX_RULES_PORT};
|
||||||
|
use common::model::event::{IPv4Event, IPv6Event};
|
||||||
|
use common::model::ip_address::{IPv4, IPv6, Port};
|
||||||
|
|
||||||
|
#[map]
|
||||||
|
static EGRESS_IPV4_SRC_WHITELIST: HashMap<IPv4, [Port; MAX_RULES_PORT]> =
|
||||||
|
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||||
|
#[map]
|
||||||
|
static EGRESS_IPV6_SRC_WHITELIST: HashMap<IPv6, [Port; MAX_RULES_PORT]> =
|
||||||
|
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||||
|
#[map]
|
||||||
|
static EGRESS_IPV4_DST_WHITELIST: HashMap<IPv4, [Port; MAX_RULES_PORT]> =
|
||||||
|
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||||
|
#[map]
|
||||||
|
static EGRESS_IPV6_DST_WHITELIST: HashMap<IPv6, [Port; MAX_RULES_PORT]> =
|
||||||
|
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||||
|
#[map]
|
||||||
|
static EGRESS_IPV4_SRC_BLACKLIST: HashMap<IPv4, [Port; MAX_RULES_PORT]> =
|
||||||
|
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||||
|
#[map]
|
||||||
|
static EGRESS_IPV6_SRC_BLACKLIST: HashMap<IPv6, [Port; MAX_RULES_PORT]> =
|
||||||
|
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||||
|
#[map]
|
||||||
|
static EGRESS_IPV4_DST_BLACKLIST: HashMap<IPv4, [Port; MAX_RULES_PORT]> =
|
||||||
|
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||||
|
#[map]
|
||||||
|
static EGRESS_IPV6_DST_BLACKLIST: HashMap<IPv6, [Port; MAX_RULES_PORT]> =
|
||||||
|
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||||
|
|
||||||
|
pub fn ipv4_is_whitelisted(event: &IPv4Event) -> bool {
|
||||||
|
unsafe {
|
||||||
|
if let Some(ports) = EGRESS_IPV4_SRC_WHITELIST.get(&event.src_ip) {
|
||||||
|
if is_port_exist(ports, event.src_port) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ports) = EGRESS_IPV4_DST_WHITELIST.get(&event.dst_ip) {
|
||||||
|
if is_port_exist(ports, event.dst_port) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ipv6_is_whitelisted(event: &IPv6Event) -> bool {
|
||||||
|
unsafe {
|
||||||
|
if let Some(ports) = EGRESS_IPV6_SRC_WHITELIST.get(&event.src_ip) {
|
||||||
|
if is_port_exist(ports, event.src_port) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ports) = EGRESS_IPV6_DST_WHITELIST.get(&event.dst_ip) {
|
||||||
|
if is_port_exist(ports, event.dst_port) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ipv4_is_blacklisted(event: &IPv4Event) -> bool {
|
||||||
|
unsafe {
|
||||||
|
if let Some(ports) = EGRESS_IPV4_SRC_BLACKLIST.get(&event.src_ip) {
|
||||||
|
if is_port_exist(ports, event.src_port) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ports) = EGRESS_IPV4_DST_BLACKLIST.get(&event.dst_ip) {
|
||||||
|
if is_port_exist(ports, event.dst_port) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ipv6_is_blacklisted(event: &IPv6Event) -> bool {
|
||||||
|
unsafe {
|
||||||
|
if let Some(ports) = EGRESS_IPV6_SRC_BLACKLIST.get(&event.src_ip) {
|
||||||
|
if is_port_exist(ports, event.src_port) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ports) = EGRESS_IPV6_DST_BLACKLIST.get(&event.dst_ip) {
|
||||||
|
if is_port_exist(ports, event.dst_port) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_port_exist(ports: &[Port; MAX_RULES_PORT], target_port: Port) -> bool {
|
||||||
|
if ports.get(0) == Some(&0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for &port in ports.iter() {
|
||||||
|
if port == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if port == target_port {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
@ -1 +1,2 @@
|
|||||||
|
pub mod access_control;
|
||||||
pub mod statistics;
|
pub mod statistics;
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
mod action;
|
mod action;
|
||||||
|
|
||||||
use action::statistics;
|
use action::{access_control, statistics};
|
||||||
use aya_ebpf::bindings::xdp_action;
|
use aya_ebpf::bindings::xdp_action;
|
||||||
use aya_ebpf::macros::{map, xdp};
|
use aya_ebpf::macros::{map, xdp};
|
||||||
use aya_ebpf::maps::{PerCpuArray, ProgramArray, XskMap};
|
use aya_ebpf::maps::{PerCpuArray, ProgramArray, XskMap};
|
||||||
@ -33,7 +33,50 @@ unsafe fn packet_intake(ctx: XdpContext) -> Result<u32, ()> {
|
|||||||
let end = ctx.data_end();
|
let end = ctx.data_end();
|
||||||
let ptr = PARSED_PACKET.get_ptr_mut(0).ok_or(())?;
|
let ptr = PARSED_PACKET.get_ptr_mut(0).ok_or(())?;
|
||||||
parsing::parse_packet(start, end, ptr)?;
|
parsing::parse_packet(start, end, ptr)?;
|
||||||
let _ = PROGRAM_ARRAY.tail_call(&ctx, STATISTICS);
|
let _ = PROGRAM_ARRAY.tail_call(&ctx, ACCESS_CONTROL);
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[xdp]
|
||||||
|
pub fn access_control(ctx: XdpContext) -> u32 {
|
||||||
|
unsafe {
|
||||||
|
match try_access_control(&ctx) {
|
||||||
|
Ok(action) => action,
|
||||||
|
Err(_) => {
|
||||||
|
let _ = PROGRAM_ARRAY.tail_call(&ctx, TRANSMISSION);
|
||||||
|
xdp_action::XDP_PASS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn try_access_control(ctx: &XdpContext) -> Result<u32, ()> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
|
||||||
|
let parsed_packet = &*ptr;
|
||||||
|
match parsed_packet {
|
||||||
|
Event::IPv4(event) => {
|
||||||
|
if access_control::ipv4_is_whitelisted(event) {
|
||||||
|
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
if access_control::ipv4_is_blacklisted(event) {
|
||||||
|
return Ok(xdp_action::XDP_DROP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::IPv6(event) => {
|
||||||
|
if access_control::ipv6_is_whitelisted(event) {
|
||||||
|
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
if access_control::ipv6_is_blacklisted(event) {
|
||||||
|
return Ok(xdp_action::XDP_DROP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
pub mod access_control;
|
pub mod access_control;
|
||||||
pub mod service;
|
|
||||||
pub mod statistics;
|
pub mod statistics;
|
||||||
|
|||||||
@ -1,163 +0,0 @@
|
|||||||
use aya_ebpf::macros::map;
|
|
||||||
use aya_ebpf::maps::{Array, HashMap};
|
|
||||||
use common::define::offset::*;
|
|
||||||
use common::define::setting::MAX_RULES;
|
|
||||||
use common::model::event::{IPv4Event, IPv6Event};
|
|
||||||
use common::model::http_method::HttpMethodBitmap;
|
|
||||||
use common::model::ip_address::*;
|
|
||||||
use common::model::placeholder::PlaceHolder;
|
|
||||||
use network_types::ip::IpProto;
|
|
||||||
use network_types::tcp::TcpHdr;
|
|
||||||
|
|
||||||
#[map]
|
|
||||||
static IPV4_HTTP_SERVICE: HashMap<AddrPortV4, HttpMethodBitmap> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
|
||||||
#[map]
|
|
||||||
static IPV6_HTTP_SERVICE: HashMap<AddrPortV6, HttpMethodBitmap> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
|
||||||
#[map]
|
|
||||||
static SSH_WHITE_LIST_ENABLE: Array<PlaceHolder> = Array::with_max_entries(1, 0);
|
|
||||||
#[map]
|
|
||||||
static IPV4_SSH_SERVICE: HashMap<AddrPortV4, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
|
||||||
#[map]
|
|
||||||
static IPV6_SSH_SERVICE: HashMap<AddrPortV6, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
|
||||||
#[map]
|
|
||||||
static IPV4_SSH_WHITE_LIST: HashMap<IPv4, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
|
||||||
#[map]
|
|
||||||
static IPV6_SSH_WHITE_LIST: HashMap<IPv6, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
|
||||||
#[map]
|
|
||||||
static IPV4_SSH_BLACK_LIST: HashMap<IPv4, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
|
||||||
#[map]
|
|
||||||
static IPV6_SSH_BLACK_LIST: HashMap<IPv6, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
|
||||||
|
|
||||||
pub fn ipv4_service_rule_violation(start: usize, end: usize, event: &IPv4Event) -> bool {
|
|
||||||
let protocol = event.protocol;
|
|
||||||
let source = event.source_addr();
|
|
||||||
let destination = event.destination_addr();
|
|
||||||
ipv4_http_service_violation(start, end, &protocol, &destination)
|
|
||||||
|| ipv4_ssh_service_violation(&source, &destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ipv6_service_rule_violation(start: usize, end: usize, event: &IPv6Event) -> bool {
|
|
||||||
let protocol = event.protocol;
|
|
||||||
let source = event.source_addr();
|
|
||||||
let destination = event.destination_addr();
|
|
||||||
ipv6_http_service_violation(start, end, &protocol, &destination)
|
|
||||||
|| ipv6_ssh_service_violation(&source, &destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn ipv4_http_service_violation(start: usize, end: usize, protocol: &IpProto, destination: &AddrPortV4) -> bool {
|
|
||||||
match IPV4_HTTP_SERVICE.get_ptr_mut(destination) {
|
|
||||||
Some(allow_method) => {
|
|
||||||
if !matches!(protocol, IpProto::Tcp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
if start + IPV4_TCP_HEADER_END > end {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let tcp_header = &*((start + IPV4_TCP_HEADER_START) as *const TcpHdr);
|
|
||||||
if tcp_header.syn() != 0 || tcp_header.rst() != 0 || tcp_header.fin() != 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if tcp_header.psh() == 0 || tcp_header.ack() == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let doff = tcp_header.doff();
|
|
||||||
if doff < 5 || doff > 15 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let tcp_header_len = (doff * 4) as usize;
|
|
||||||
let tcp_payload_start = IPV4_TCP_HEADER_END + tcp_header_len;
|
|
||||||
match get_http_request_method(start, end, tcp_payload_start) {
|
|
||||||
Some(http_method) => *allow_method & http_method == 0,
|
|
||||||
None => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn ipv6_http_service_violation(start: usize, end: usize, protocol: &IpProto, destination: &AddrPortV6) -> bool {
|
|
||||||
match IPV6_HTTP_SERVICE.get_ptr_mut(destination) {
|
|
||||||
Some(allow_method) => {
|
|
||||||
if !matches!(protocol, IpProto::Tcp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
if start + IPV6_TCP_HEADER_END > end {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let tcp_header = &*((start + IPV6_TCP_HEADER_START) as *const TcpHdr);
|
|
||||||
if tcp_header.syn() != 0 || tcp_header.rst() != 0 || tcp_header.fin() != 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if tcp_header.psh() == 0 || tcp_header.ack() == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let doff = tcp_header.doff();
|
|
||||||
if doff < 5 || doff > 15 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let tcp_header_len = (doff * 4) as usize;
|
|
||||||
let tcp_payload_start = IPV6_TCP_HEADER_END + tcp_header_len;
|
|
||||||
match get_http_request_method(start, end, tcp_payload_start) {
|
|
||||||
Some(http_method) => *allow_method & http_method == 0,
|
|
||||||
None => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn get_http_request_method(start: usize, end: usize, offset: usize) -> Option<HttpMethodBitmap> {
|
|
||||||
if start + offset + 8 > end {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let data = unsafe { core::slice::from_raw_parts((start + offset) as *const u8, 8) };
|
|
||||||
match &data[..4] {
|
|
||||||
b"GET " => Some(1 << 0),
|
|
||||||
b"POST" if &data[4..5] == b" " => Some(1 << 1),
|
|
||||||
b"PUT " => Some(1 << 2),
|
|
||||||
b"DELE" if &data[4..7] == b"TE " => Some(1 << 3),
|
|
||||||
b"HEAD" if &data[4..5] == b" " => Some(1 << 4),
|
|
||||||
b"OPTI" if &data[4..8] == b"ONS " => Some(1 << 5),
|
|
||||||
b"PATC" if &data[4..6] == b"H " => Some(1 << 6),
|
|
||||||
b"TRAC" if &data[4..6] == b"E " => Some(1 << 7),
|
|
||||||
b"CONN" if &data[4..8] == b"ECT " => Some(1 << 8),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn ipv4_ssh_service_violation(source: &AddrPortV4, destination: &AddrPortV4) -> bool {
|
|
||||||
unsafe {
|
|
||||||
if IPV4_SSH_SERVICE.get(destination).is_some() {
|
|
||||||
if SSH_WHITE_LIST_ENABLE.get(0).is_some() {
|
|
||||||
IPV4_SSH_WHITE_LIST.get(&source.ip()).is_none()
|
|
||||||
} else {
|
|
||||||
IPV4_SSH_BLACK_LIST.get(&source.ip()).is_some()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn ipv6_ssh_service_violation(source_ip: &AddrPortV6, destination: &AddrPortV6) -> bool {
|
|
||||||
unsafe {
|
|
||||||
if IPV6_SSH_SERVICE.get(destination).is_some() {
|
|
||||||
if SSH_WHITE_LIST_ENABLE.get(0).is_some() {
|
|
||||||
IPV6_SSH_WHITE_LIST.get(&source_ip.ip()).is_none()
|
|
||||||
} else {
|
|
||||||
IPV6_SSH_BLACK_LIST.get(&source_ip.ip()).is_some()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,7 +12,7 @@ use common::define::program_array::ingress::*;
|
|||||||
use common::ebpf::parsing;
|
use common::ebpf::parsing;
|
||||||
use common::model::event::Event;
|
use common::model::event::Event;
|
||||||
|
|
||||||
use crate::action::{access_control, service, statistics};
|
use crate::action::{access_control, statistics};
|
||||||
|
|
||||||
#[map]
|
#[map]
|
||||||
static PROGRAM_ARRAY: ProgramArray = ProgramArray::with_max_entries(8, 0);
|
static PROGRAM_ARRAY: ProgramArray = ProgramArray::with_max_entries(8, 0);
|
||||||
@ -80,43 +80,6 @@ unsafe fn try_access_control(ctx: &XdpContext) -> Result<u32, ()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _ = PROGRAM_ARRAY.tail_call(ctx, SERVICE);
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[xdp]
|
|
||||||
pub fn service(ctx: XdpContext) -> u32 {
|
|
||||||
unsafe {
|
|
||||||
match try_service(&ctx) {
|
|
||||||
Ok(action) => action,
|
|
||||||
Err(_) => {
|
|
||||||
let _ = PROGRAM_ARRAY.tail_call(&ctx, TRANSMISSION);
|
|
||||||
xdp_action::XDP_PASS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn try_service(ctx: &XdpContext) -> Result<u32, ()> {
|
|
||||||
unsafe {
|
|
||||||
let start = ctx.data();
|
|
||||||
let end = ctx.data_end();
|
|
||||||
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
|
|
||||||
let parsed_packet = &*ptr;
|
|
||||||
match parsed_packet {
|
|
||||||
Event::IPv4(event) => {
|
|
||||||
if service::ipv4_service_rule_violation(start, end, event) {
|
|
||||||
return Ok(xdp_action::XDP_DROP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::IPv6(event) => {
|
|
||||||
if service::ipv6_service_rule_violation(start, end, event) {
|
|
||||||
return Ok(xdp_action::XDP_DROP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -273,8 +273,6 @@ fn build_egress_ebpf(dst: &PathBuf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_frontend(frontend_dir: &PathBuf, static_dir: &PathBuf) {
|
fn build_frontend(frontend_dir: &PathBuf, static_dir: &PathBuf) {
|
||||||
let _ = dotenvy::dotenv();
|
|
||||||
|
|
||||||
if !frontend_dir.exists() {
|
if !frontend_dir.exists() {
|
||||||
panic!("Frontend directory {:?} does not exist", frontend_dir);
|
panic!("Frontend directory {:?} does not exist", frontend_dir);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::core::ebpf::access_control::AccessControl;
|
use crate::core::ebpf::access_control::AccessControl;
|
||||||
use crate::core::ebpf::service::Service;
|
|
||||||
use crate::core::ebpf::statistics::Statistics;
|
use crate::core::ebpf::statistics::Statistics;
|
||||||
use crate::core::infrastructure::app_config::AppConfig;
|
use crate::core::infrastructure::app_config::AppConfig;
|
||||||
use crate::core::infrastructure::app_db::AppDB;
|
use crate::core::infrastructure::app_db::AppDB;
|
||||||
@ -14,7 +13,6 @@ pub struct AppState {
|
|||||||
pub app_config: Arc<AppConfig>,
|
pub app_config: Arc<AppConfig>,
|
||||||
pub inference_config: Arc<InferenceConfig>,
|
pub inference_config: Arc<InferenceConfig>,
|
||||||
pub access_control: Arc<AccessControl>,
|
pub access_control: Arc<AccessControl>,
|
||||||
pub service: Arc<Service>,
|
|
||||||
pub statistics: Arc<Statistics>,
|
pub statistics: Arc<Statistics>,
|
||||||
pub health: Arc<SystemHealth>,
|
pub health: Arc<SystemHealth>,
|
||||||
pub detection_alert: Arc<DetectionAlert>,
|
pub detection_alert: Arc<DetectionAlert>,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use common::define::setting::MAX_RULES_PORT;
|
|||||||
use common::model::ip_address::{IPv4, IPv6, Port};
|
use common::model::ip_address::{IPv4, IPv6, Port};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use crate::model::direction::FlowDirection;
|
use crate::model::direction::{Direction, FlowDirection};
|
||||||
use crate::model::error::Error;
|
use crate::model::error::Error;
|
||||||
use crate::model::error::ebpf::EbpfError;
|
use crate::model::error::ebpf::EbpfError;
|
||||||
use crate::model::ip_address::NativeConvert;
|
use crate::model::ip_address::NativeConvert;
|
||||||
@ -15,117 +15,242 @@ use crate::model::list_type::ListType;
|
|||||||
use crate::utils::ip_address::convert_ports_to_vec;
|
use crate::utils::ip_address::convert_ports_to_vec;
|
||||||
|
|
||||||
pub struct AccessControl {
|
pub struct AccessControl {
|
||||||
ipv4_src_whitelist: RwLock<MapWrapper<IPv4>>,
|
ingress_ipv4_src_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||||
ipv4_src_blacklist: RwLock<MapWrapper<IPv4>>,
|
ingress_ipv4_src_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||||
ipv4_dst_whitelist: RwLock<MapWrapper<IPv4>>,
|
ingress_ipv4_dst_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||||
ipv4_dst_blacklist: RwLock<MapWrapper<IPv4>>,
|
ingress_ipv4_dst_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||||
ipv6_src_whitelist: RwLock<MapWrapper<IPv6>>,
|
ingress_ipv6_src_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||||
ipv6_src_blacklist: RwLock<MapWrapper<IPv6>>,
|
ingress_ipv6_src_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||||
ipv6_dst_whitelist: RwLock<MapWrapper<IPv6>>,
|
ingress_ipv6_dst_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||||
ipv6_dst_blacklist: RwLock<MapWrapper<IPv6>>,
|
ingress_ipv6_dst_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||||
|
egress_ipv4_src_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||||
|
egress_ipv4_src_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||||
|
egress_ipv4_dst_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||||
|
egress_ipv4_dst_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||||
|
egress_ipv6_src_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||||
|
egress_ipv6_src_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||||
|
egress_ipv6_dst_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||||
|
egress_ipv6_dst_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccessControl {
|
impl AccessControl {
|
||||||
pub fn new(ebpf: &mut Ebpf) -> Result<Self, Error> {
|
pub fn new(ingress_ebpf: &mut Ebpf, egress_ebpf: &mut Ebpf) -> Result<Self, Error> {
|
||||||
let access_control = Self {
|
Ok(Self {
|
||||||
ipv4_src_whitelist: RwLock::new(MapWrapper::new(ebpf, "IPV4_SRC_WHITELIST")?),
|
ingress_ipv4_src_whitelist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV4_SRC_WHITELIST")?),
|
||||||
ipv4_src_blacklist: RwLock::new(MapWrapper::new(ebpf, "IPV4_SRC_BLACKLIST")?),
|
ingress_ipv4_src_blacklist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV4_SRC_BLACKLIST")?),
|
||||||
ipv4_dst_whitelist: RwLock::new(MapWrapper::new(ebpf, "IPV4_DST_WHITELIST")?),
|
ingress_ipv4_dst_whitelist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV4_DST_WHITELIST")?),
|
||||||
ipv4_dst_blacklist: RwLock::new(MapWrapper::new(ebpf, "IPV4_DST_BLACKLIST")?),
|
ingress_ipv4_dst_blacklist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV4_DST_BLACKLIST")?),
|
||||||
ipv6_src_whitelist: RwLock::new(MapWrapper::new(ebpf, "IPV6_SRC_WHITELIST")?),
|
ingress_ipv6_src_whitelist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV6_SRC_WHITELIST")?),
|
||||||
ipv6_src_blacklist: RwLock::new(MapWrapper::new(ebpf, "IPV6_SRC_BLACKLIST")?),
|
ingress_ipv6_src_blacklist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV6_SRC_BLACKLIST")?),
|
||||||
ipv6_dst_whitelist: RwLock::new(MapWrapper::new(ebpf, "IPV6_DST_WHITELIST")?),
|
ingress_ipv6_dst_whitelist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV6_DST_WHITELIST")?),
|
||||||
ipv6_dst_blacklist: RwLock::new(MapWrapper::new(ebpf, "IPV6_DST_BLACKLIST")?),
|
ingress_ipv6_dst_blacklist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV6_DST_BLACKLIST")?),
|
||||||
};
|
egress_ipv4_src_whitelist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV4_SRC_WHITELIST")?),
|
||||||
Ok(access_control)
|
egress_ipv4_src_blacklist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV4_SRC_BLACKLIST")?),
|
||||||
|
egress_ipv4_dst_whitelist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV4_DST_WHITELIST")?),
|
||||||
|
egress_ipv4_dst_blacklist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV4_DST_BLACKLIST")?),
|
||||||
|
egress_ipv6_src_whitelist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV6_SRC_WHITELIST")?),
|
||||||
|
egress_ipv6_src_blacklist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV6_SRC_BLACKLIST")?),
|
||||||
|
egress_ipv6_dst_whitelist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV6_DST_WHITELIST")?),
|
||||||
|
egress_ipv6_dst_blacklist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV6_DST_BLACKLIST")?),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_ipv4_list(&self, direction: FlowDirection, list_type: ListType) -> HashMap<Ipv4Addr, Vec<Port>> {
|
pub async fn get_ipv4_list(
|
||||||
let map_wrapper = match (direction, list_type) {
|
&self,
|
||||||
(FlowDirection::Source, ListType::White) => self.ipv4_src_whitelist.read().await,
|
nic: Direction,
|
||||||
(FlowDirection::Source, ListType::Black) => self.ipv4_src_blacklist.read().await,
|
flow: FlowDirection,
|
||||||
(FlowDirection::Destination, ListType::White) => self.ipv4_dst_whitelist.read().await,
|
list_type: ListType,
|
||||||
(FlowDirection::Destination, ListType::Black) => self.ipv4_dst_blacklist.read().await,
|
) -> HashMap<Ipv4Addr, Vec<Port>> {
|
||||||
|
let guard = match (nic, flow, list_type) {
|
||||||
|
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||||
|
self.ingress_ipv4_src_whitelist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||||
|
self.ingress_ipv4_src_blacklist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.ingress_ipv4_dst_whitelist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.ingress_ipv4_dst_blacklist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::White) => self.egress_ipv4_src_whitelist.read().await,
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::Black) => self.egress_ipv4_src_blacklist.read().await,
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.egress_ipv4_dst_whitelist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.egress_ipv4_dst_blacklist.read().await
|
||||||
|
}
|
||||||
};
|
};
|
||||||
map_wrapper.get_list()
|
guard.get_list()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_ipv6_list(&self, direction: FlowDirection, list_type: ListType) -> HashMap<Ipv6Addr, Vec<Port>> {
|
pub async fn get_ipv6_list(
|
||||||
let map_wrapper = match (direction, list_type) {
|
&self,
|
||||||
(FlowDirection::Source, ListType::White) => self.ipv6_src_whitelist.read().await,
|
nic: Direction,
|
||||||
(FlowDirection::Source, ListType::Black) => self.ipv6_src_blacklist.read().await,
|
flow: FlowDirection,
|
||||||
(FlowDirection::Destination, ListType::White) => self.ipv6_dst_whitelist.read().await,
|
list_type: ListType,
|
||||||
(FlowDirection::Destination, ListType::Black) => self.ipv6_dst_blacklist.read().await,
|
) -> HashMap<Ipv6Addr, Vec<Port>> {
|
||||||
|
let guard = match (nic, flow, list_type) {
|
||||||
|
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||||
|
self.ingress_ipv6_src_whitelist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||||
|
self.ingress_ipv6_src_blacklist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.ingress_ipv6_dst_whitelist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.ingress_ipv6_dst_blacklist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::White) => self.egress_ipv6_src_whitelist.read().await,
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::Black) => self.egress_ipv6_src_blacklist.read().await,
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.egress_ipv6_dst_whitelist.read().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.egress_ipv6_dst_blacklist.read().await
|
||||||
|
}
|
||||||
};
|
};
|
||||||
map_wrapper.get_list()
|
guard.get_list()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_ipv4_list(
|
pub async fn add_ipv4_list(
|
||||||
&self,
|
&self,
|
||||||
direction: FlowDirection,
|
nic: Direction,
|
||||||
|
flow: FlowDirection,
|
||||||
list_type: ListType,
|
list_type: ListType,
|
||||||
address: SocketAddrV4,
|
address: SocketAddrV4,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let ip: u32 = (*address.ip()).to_bits().to_be();
|
let ip: u32 = (*address.ip()).to_bits().to_be();
|
||||||
let port = address.port();
|
let port = address.port();
|
||||||
let mut map_wrapper = match (direction, list_type) {
|
let mut guard = match (nic, flow, list_type) {
|
||||||
(FlowDirection::Source, ListType::White) => self.ipv4_src_whitelist.write().await,
|
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||||
(FlowDirection::Source, ListType::Black) => self.ipv4_src_blacklist.write().await,
|
self.ingress_ipv4_src_whitelist.write().await
|
||||||
(FlowDirection::Destination, ListType::White) => self.ipv4_dst_whitelist.write().await,
|
}
|
||||||
(FlowDirection::Destination, ListType::Black) => self.ipv4_dst_blacklist.write().await,
|
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||||
|
self.ingress_ipv4_src_blacklist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.ingress_ipv4_dst_whitelist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.ingress_ipv4_dst_blacklist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::White) => self.egress_ipv4_src_whitelist.write().await,
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::Black) => self.egress_ipv4_src_blacklist.write().await,
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.egress_ipv4_dst_whitelist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.egress_ipv4_dst_blacklist.write().await
|
||||||
|
}
|
||||||
};
|
};
|
||||||
map_wrapper.add(ip, port)
|
guard.add(ip, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_ipv6_list(
|
pub async fn add_ipv6_list(
|
||||||
&self,
|
&self,
|
||||||
direction: FlowDirection,
|
nic: Direction,
|
||||||
|
flow: FlowDirection,
|
||||||
list_type: ListType,
|
list_type: ListType,
|
||||||
address: SocketAddrV6,
|
address: SocketAddrV6,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let ip: u128 = (*address.ip()).to_bits().to_be();
|
let ip: u128 = (*address.ip()).to_bits().to_be();
|
||||||
let port = address.port();
|
let port = address.port();
|
||||||
let mut map_wrapper = match (direction, list_type) {
|
let mut guard = match (nic, flow, list_type) {
|
||||||
(FlowDirection::Source, ListType::White) => self.ipv6_src_whitelist.write().await,
|
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||||
(FlowDirection::Source, ListType::Black) => self.ipv6_src_blacklist.write().await,
|
self.ingress_ipv6_src_whitelist.write().await
|
||||||
(FlowDirection::Destination, ListType::White) => self.ipv6_dst_whitelist.write().await,
|
}
|
||||||
(FlowDirection::Destination, ListType::Black) => self.ipv6_dst_blacklist.write().await,
|
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||||
|
self.ingress_ipv6_src_blacklist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.ingress_ipv6_dst_whitelist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.ingress_ipv6_dst_blacklist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::White) => self.egress_ipv6_src_whitelist.write().await,
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::Black) => self.egress_ipv6_src_blacklist.write().await,
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.egress_ipv6_dst_whitelist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.egress_ipv6_dst_blacklist.write().await
|
||||||
|
}
|
||||||
};
|
};
|
||||||
map_wrapper.add(ip, port)
|
guard.add(ip, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_ipv4_list(
|
pub async fn remove_ipv4_list(
|
||||||
&self,
|
&self,
|
||||||
direction: FlowDirection,
|
nic: Direction,
|
||||||
|
flow: FlowDirection,
|
||||||
list_type: ListType,
|
list_type: ListType,
|
||||||
address: SocketAddrV4,
|
address: SocketAddrV4,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let ip: u32 = (*address.ip()).to_bits().to_be();
|
let ip: u32 = (*address.ip()).to_bits().to_be();
|
||||||
let port = address.port();
|
let port = address.port();
|
||||||
let mut map_wrapper = match (direction, list_type) {
|
let mut guard = match (nic, flow, list_type) {
|
||||||
(FlowDirection::Source, ListType::White) => self.ipv4_src_whitelist.write().await,
|
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||||
(FlowDirection::Source, ListType::Black) => self.ipv4_src_blacklist.write().await,
|
self.ingress_ipv4_src_whitelist.write().await
|
||||||
(FlowDirection::Destination, ListType::White) => self.ipv4_dst_whitelist.write().await,
|
}
|
||||||
(FlowDirection::Destination, ListType::Black) => self.ipv4_dst_blacklist.write().await,
|
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||||
|
self.ingress_ipv4_src_blacklist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.ingress_ipv4_dst_whitelist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.ingress_ipv4_dst_blacklist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::White) => self.egress_ipv4_src_whitelist.write().await,
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::Black) => self.egress_ipv4_src_blacklist.write().await,
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.egress_ipv4_dst_whitelist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.egress_ipv4_dst_blacklist.write().await
|
||||||
|
}
|
||||||
};
|
};
|
||||||
map_wrapper.remove(ip, port)
|
guard.remove(ip, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_ipv6_list(
|
pub async fn remove_ipv6_list(
|
||||||
&self,
|
&self,
|
||||||
direction: FlowDirection,
|
nic: Direction,
|
||||||
|
flow: FlowDirection,
|
||||||
list_type: ListType,
|
list_type: ListType,
|
||||||
address: SocketAddrV6,
|
address: SocketAddrV6,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let ip: u128 = (*address.ip()).to_bits().to_be();
|
let ip: u128 = (*address.ip()).to_bits().to_be();
|
||||||
let port = address.port();
|
let port = address.port();
|
||||||
let mut map_wrapper = match (direction, list_type) {
|
let mut guard = match (nic, flow, list_type) {
|
||||||
(FlowDirection::Source, ListType::White) => self.ipv6_src_whitelist.write().await,
|
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||||
(FlowDirection::Source, ListType::Black) => self.ipv6_src_blacklist.write().await,
|
self.ingress_ipv6_src_whitelist.write().await
|
||||||
(FlowDirection::Destination, ListType::White) => self.ipv6_dst_whitelist.write().await,
|
}
|
||||||
(FlowDirection::Destination, ListType::Black) => self.ipv6_dst_blacklist.write().await,
|
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||||
|
self.ingress_ipv6_src_blacklist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.ingress_ipv6_dst_whitelist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.ingress_ipv6_dst_blacklist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::White) => self.egress_ipv6_src_whitelist.write().await,
|
||||||
|
(Direction::Egress, FlowDirection::Source, ListType::Black) => self.egress_ipv6_src_blacklist.write().await,
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||||
|
self.egress_ipv6_dst_whitelist.write().await
|
||||||
|
}
|
||||||
|
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||||
|
self.egress_ipv6_dst_blacklist.write().await
|
||||||
|
}
|
||||||
};
|
};
|
||||||
map_wrapper.remove(ip, port)
|
guard.remove(ip, port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
pub mod access_control;
|
pub mod access_control;
|
||||||
pub mod service;
|
|
||||||
pub mod statistics;
|
pub mod statistics;
|
||||||
pub mod xsk_manager;
|
pub mod xsk_manager;
|
||||||
|
|
||||||
@ -11,7 +10,6 @@ use macros::log;
|
|||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use crate::core::ebpf::access_control::AccessControl;
|
use crate::core::ebpf::access_control::AccessControl;
|
||||||
use crate::core::ebpf::service::Service;
|
|
||||||
use crate::core::ebpf::statistics::Statistics;
|
use crate::core::ebpf::statistics::Statistics;
|
||||||
use crate::core::ebpf::xsk_manager::XskManager;
|
use crate::core::ebpf::xsk_manager::XskManager;
|
||||||
use crate::core::infrastructure::app_config::AppConfig;
|
use crate::core::infrastructure::app_config::AppConfig;
|
||||||
@ -23,7 +21,6 @@ use crate::model::error::system::SystemError;
|
|||||||
pub struct EbpfServices {
|
pub struct EbpfServices {
|
||||||
pub xsk_manager: Arc<XskManager>,
|
pub xsk_manager: Arc<XskManager>,
|
||||||
pub access_control: Arc<AccessControl>,
|
pub access_control: Arc<AccessControl>,
|
||||||
pub service: Arc<Service>,
|
|
||||||
pub statistics: Arc<Statistics>,
|
pub statistics: Arc<Statistics>,
|
||||||
pub shutdowns: SegQueue<oneshot::Sender<()>>,
|
pub shutdowns: SegQueue<oneshot::Sender<()>>,
|
||||||
}
|
}
|
||||||
@ -31,13 +28,11 @@ pub struct EbpfServices {
|
|||||||
impl EbpfServices {
|
impl EbpfServices {
|
||||||
pub fn new(app_config: Arc<AppConfig>, ingress_ebpf: &mut Ebpf, egress_ebpf: &mut Ebpf) -> Result<Self, Error> {
|
pub fn new(app_config: Arc<AppConfig>, ingress_ebpf: &mut Ebpf, egress_ebpf: &mut Ebpf) -> Result<Self, Error> {
|
||||||
let xsk_manager = XskManager::new(app_config.clone(), ingress_ebpf, egress_ebpf)?;
|
let xsk_manager = XskManager::new(app_config.clone(), ingress_ebpf, egress_ebpf)?;
|
||||||
let access_control = AccessControl::new(ingress_ebpf)?;
|
let access_control = AccessControl::new(ingress_ebpf, egress_ebpf)?;
|
||||||
let service = Service::new(ingress_ebpf)?;
|
|
||||||
let statistics = Statistics::new(app_config.clone(), ingress_ebpf, egress_ebpf)?;
|
let statistics = Statistics::new(app_config.clone(), ingress_ebpf, egress_ebpf)?;
|
||||||
let ebpf_services = Self {
|
let ebpf_services = Self {
|
||||||
xsk_manager: Arc::new(xsk_manager),
|
xsk_manager: Arc::new(xsk_manager),
|
||||||
access_control: Arc::new(access_control),
|
access_control: Arc::new(access_control),
|
||||||
service: Arc::new(service),
|
|
||||||
statistics: Arc::new(statistics),
|
statistics: Arc::new(statistics),
|
||||||
shutdowns: SegQueue::new(),
|
shutdowns: SegQueue::new(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,332 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
|
|
||||||
|
|
||||||
use aya::maps::{Array as AyaArray, HashMap as AyaHashMap, MapData};
|
|
||||||
use aya::{Ebpf, Pod};
|
|
||||||
use common::model::http_method::{HttpMethod, HttpMethodBitmap};
|
|
||||||
use common::model::ip_address::{AddrPortV4, AddrPortV6, IPv4, IPv6};
|
|
||||||
use common::model::placeholder::PlaceHolder;
|
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
use crate::model::error::Error;
|
|
||||||
use crate::model::error::ebpf::EbpfError;
|
|
||||||
use crate::model::ip_address::NativeConvert;
|
|
||||||
|
|
||||||
pub struct Service {
|
|
||||||
ipv4_http_service: RwLock<HttpServiceWrapper<AddrPortV4>>,
|
|
||||||
ipv6_http_service: RwLock<HttpServiceWrapper<AddrPortV6>>,
|
|
||||||
ssh_white_list_enable: RwLock<WhiteListControl>,
|
|
||||||
ipv4_ssh_service: RwLock<SshServiceWrapper<AddrPortV4>>,
|
|
||||||
ipv6_ssh_service: RwLock<SshServiceWrapper<AddrPortV6>>,
|
|
||||||
ipv4_ssh_white_list: RwLock<SshListWrapper<IPv4>>,
|
|
||||||
ipv6_ssh_white_list: RwLock<SshListWrapper<IPv6>>,
|
|
||||||
ipv4_ssh_black_list: RwLock<SshListWrapper<IPv4>>,
|
|
||||||
ipv6_ssh_black_list: RwLock<SshListWrapper<IPv6>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Service {
|
|
||||||
pub fn new(ebpf: &mut Ebpf) -> Result<Self, Error> {
|
|
||||||
let service = Self {
|
|
||||||
ipv4_http_service: RwLock::new(HttpServiceWrapper::new(ebpf, "IPV4_HTTP_SERVICE")?),
|
|
||||||
ipv6_http_service: RwLock::new(HttpServiceWrapper::new(ebpf, "IPV6_HTTP_SERVICE")?),
|
|
||||||
ssh_white_list_enable: RwLock::new(WhiteListControl::new(ebpf, "SSH_WHITE_LIST_ENABLE")?),
|
|
||||||
ipv4_ssh_service: RwLock::new(SshServiceWrapper::new(ebpf, "IPV4_SSH_SERVICE")?),
|
|
||||||
ipv6_ssh_service: RwLock::new(SshServiceWrapper::new(ebpf, "IPV6_SSH_SERVICE")?),
|
|
||||||
ipv4_ssh_white_list: RwLock::new(SshListWrapper::new(ebpf, "IPV4_SSH_WHITE_LIST")?),
|
|
||||||
ipv6_ssh_white_list: RwLock::new(SshListWrapper::new(ebpf, "IPV6_SSH_WHITE_LIST")?),
|
|
||||||
ipv4_ssh_black_list: RwLock::new(SshListWrapper::new(ebpf, "IPV4_SSH_BLACK_LIST")?),
|
|
||||||
ipv6_ssh_black_list: RwLock::new(SshListWrapper::new(ebpf, "IPV6_SSH_BLACK_LIST")?),
|
|
||||||
};
|
|
||||||
Ok(service)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_ipv4_http_service(&self) -> HashMap<SocketAddrV4, Vec<HttpMethod>> {
|
|
||||||
self.ipv4_http_service.read().await.get_http_method()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_ipv6_http_service(&self) -> HashMap<SocketAddrV6, Vec<HttpMethod>> {
|
|
||||||
self.ipv6_http_service.read().await.get_http_method()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_ipv4_http_service(
|
|
||||||
&self,
|
|
||||||
address: SocketAddrV4,
|
|
||||||
http_method: Vec<HttpMethod>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.ipv4_http_service
|
|
||||||
.write()
|
|
||||||
.await
|
|
||||||
.add_http_service(address, http_method)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_ipv6_http_service(
|
|
||||||
&self,
|
|
||||||
address: SocketAddrV6,
|
|
||||||
http_method: Vec<HttpMethod>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.ipv6_http_service
|
|
||||||
.write()
|
|
||||||
.await
|
|
||||||
.add_http_service(address, http_method)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_ipv4_http_service(
|
|
||||||
&self,
|
|
||||||
address: SocketAddrV4,
|
|
||||||
removed_http_method: Vec<HttpMethod>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.ipv4_http_service
|
|
||||||
.write()
|
|
||||||
.await
|
|
||||||
.remove_http_service(address, removed_http_method)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_ipv6_http_service(
|
|
||||||
&self,
|
|
||||||
address: SocketAddrV6,
|
|
||||||
removed_http_method: Vec<HttpMethod>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.ipv6_http_service
|
|
||||||
.write()
|
|
||||||
.await
|
|
||||||
.remove_http_service(address, removed_http_method)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn is_ssh_white_list_enable(&self) -> bool {
|
|
||||||
self.ssh_white_list_enable.read().await.is_white_list_enable()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn enable_ssh_white_list(&self) -> Result<(), Error> {
|
|
||||||
self.ssh_white_list_enable.write().await.enable_white_list()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn disable_ssh_white_list(&self) -> Result<(), Error> {
|
|
||||||
self.ssh_white_list_enable.write().await.disable_white_list()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_ipv4_ssh_service(&self) -> Vec<SocketAddrV4> {
|
|
||||||
self.ipv4_ssh_service.read().await.get_ssh_service()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_ipv6_ssh_service(&self) -> Vec<SocketAddrV6> {
|
|
||||||
self.ipv6_ssh_service.read().await.get_ssh_service()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_ipv4_ssh_service(&self, address: SocketAddrV4) -> Result<(), Error> {
|
|
||||||
self.ipv4_ssh_service.write().await.add_ssh_service(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_ipv6_ssh_service(&self, address: SocketAddrV6) -> Result<(), Error> {
|
|
||||||
self.ipv6_ssh_service.write().await.add_ssh_service(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_ipv4_ssh_service(&self, address: SocketAddrV4) -> Result<(), Error> {
|
|
||||||
self.ipv4_ssh_service.write().await.remove_ssh_service(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_ipv6_ssh_service(&self, address: SocketAddrV6) -> Result<(), Error> {
|
|
||||||
self.ipv6_ssh_service.write().await.remove_ssh_service(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_ipv4_ssh_white_list(&self) -> Vec<Ipv4Addr> {
|
|
||||||
self.ipv4_ssh_white_list.read().await.get_list()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_ipv6_ssh_white_list(&self) -> Vec<Ipv6Addr> {
|
|
||||||
self.ipv6_ssh_white_list.read().await.get_list()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_ipv4_ssh_white_list(&self, ip: Ipv4Addr) -> Result<(), Error> {
|
|
||||||
self.ipv4_ssh_white_list.write().await.add_list(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_ipv6_ssh_white_list(&self, ip: Ipv6Addr) -> Result<(), Error> {
|
|
||||||
self.ipv6_ssh_white_list.write().await.add_list(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_ipv4_ssh_white_list(&self, ip: Ipv4Addr) -> Result<(), Error> {
|
|
||||||
self.ipv4_ssh_white_list.write().await.remove_list(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_ipv6_ssh_white_list(&self, ip: Ipv6Addr) -> Result<(), Error> {
|
|
||||||
self.ipv6_ssh_white_list.write().await.remove_list(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_ipv4_ssh_black_list(&self) -> Vec<Ipv4Addr> {
|
|
||||||
self.ipv4_ssh_black_list.read().await.get_list()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_ipv6_ssh_black_list(&self) -> Vec<Ipv6Addr> {
|
|
||||||
self.ipv6_ssh_black_list.read().await.get_list()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_ipv4_ssh_black_list(&self, ip: Ipv4Addr) -> Result<(), Error> {
|
|
||||||
self.ipv4_ssh_black_list.write().await.add_list(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_ipv6_ssh_black_list(&self, ip: Ipv6Addr) -> Result<(), Error> {
|
|
||||||
self.ipv6_ssh_black_list.write().await.add_list(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_ipv4_ssh_black_list(&self, ip: Ipv4Addr) -> Result<(), Error> {
|
|
||||||
self.ipv4_ssh_black_list.write().await.remove_list(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_ipv6_ssh_black_list(&self, ip: Ipv6Addr) -> Result<(), Error> {
|
|
||||||
self.ipv6_ssh_black_list.write().await.remove_list(ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WhiteListControl {
|
|
||||||
map: AyaArray<MapData, PlaceHolder>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WhiteListControl {
|
|
||||||
fn new(ebpf: &mut Ebpf, map_name: &str) -> Result<Self, Error> {
|
|
||||||
let map = ebpf.take_map(map_name).ok_or(EbpfError::MapNotFound)?;
|
|
||||||
let map = AyaArray::try_from(map).map_err(EbpfError::MapOperationError)?;
|
|
||||||
Ok(Self { map })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_white_list_enable(&self) -> bool {
|
|
||||||
match self.map.get(&0, 0) {
|
|
||||||
Ok(status) => {
|
|
||||||
if status == 0 {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enable_white_list(&mut self) -> Result<(), Error> {
|
|
||||||
self.map.set(0, 1_u8, 0).map_err(EbpfError::MapOperationError)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn disable_white_list(&mut self) -> Result<(), Error> {
|
|
||||||
self.map.set(0, 0_u8, 0).map_err(EbpfError::MapOperationError)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct HttpServiceWrapper<T> {
|
|
||||||
map: AyaHashMap<MapData, T, HttpMethodBitmap>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: NativeConvert + Pod> HttpServiceWrapper<T> {
|
|
||||||
fn new(ebpf: &mut Ebpf, map_name: &str) -> Result<Self, Error> {
|
|
||||||
let map = ebpf.take_map(map_name).ok_or(EbpfError::MapNotFound)?;
|
|
||||||
let map = AyaHashMap::try_from(map).map_err(EbpfError::MapOperationError)?;
|
|
||||||
Ok(Self { map })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_http_method(&self) -> HashMap<T::Native, Vec<HttpMethod>> {
|
|
||||||
self.map
|
|
||||||
.iter()
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.map(|(key, value)| {
|
|
||||||
let address = key.into_native();
|
|
||||||
(address, HttpMethod::convert_from_bitmap(value))
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_http_service(&mut self, address: T::Native, http_method: Vec<HttpMethod>) -> Result<(), Error> {
|
|
||||||
let address = T::from_native(address);
|
|
||||||
let ebpf_method = HttpMethod::convert_to_bitmap(http_method);
|
|
||||||
self.map
|
|
||||||
.insert(address, ebpf_method, 0)
|
|
||||||
.map_err(|_| EbpfError::RuleReachLimit)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_http_service(&mut self, address: T::Native, removed_http_method: Vec<HttpMethod>) -> Result<(), Error> {
|
|
||||||
let address = T::from_native(address);
|
|
||||||
if let Ok(current_http_method) = self.map.get(&address, 0) {
|
|
||||||
let mut http_method = HttpMethod::convert_from_bitmap(current_http_method);
|
|
||||||
http_method.retain(|method| !removed_http_method.contains(method));
|
|
||||||
if http_method.is_empty() {
|
|
||||||
self.map.remove(&address).map_err(EbpfError::MapOperationError)?;
|
|
||||||
} else {
|
|
||||||
let new_http_method = HttpMethod::convert_to_bitmap(http_method);
|
|
||||||
self.map
|
|
||||||
.insert(&address, new_http_method, 0)
|
|
||||||
.map_err(EbpfError::MapOperationError)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(EbpfError::IpDoesNotExist)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SshServiceWrapper<T> {
|
|
||||||
map: AyaHashMap<MapData, T, PlaceHolder>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: NativeConvert + Pod> SshServiceWrapper<T> {
|
|
||||||
fn new(ebpf: &mut Ebpf, map_name: &str) -> Result<Self, Error> {
|
|
||||||
let map = ebpf.take_map(map_name).ok_or(EbpfError::MapNotFound)?;
|
|
||||||
let map = AyaHashMap::try_from(map).map_err(EbpfError::MapOperationError)?;
|
|
||||||
Ok(Self { map })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_ssh_service(&self) -> Vec<T::Native> {
|
|
||||||
self.map
|
|
||||||
.keys()
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.map(|key| key.into_native())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_ssh_service(&mut self, address: T::Native) -> Result<(), Error> {
|
|
||||||
let address = T::from_native(address);
|
|
||||||
self.map
|
|
||||||
.insert(address, 0_u8, 0)
|
|
||||||
.map_err(|_| EbpfError::RuleReachLimit)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_ssh_service(&mut self, address: T::Native) -> Result<(), Error> {
|
|
||||||
let address = T::from_native(address);
|
|
||||||
self.map.remove(&address).map_err(|_| EbpfError::IpDoesNotExist)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SshListWrapper<T> {
|
|
||||||
map: AyaHashMap<MapData, T, PlaceHolder>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: NativeConvert + Pod> SshListWrapper<T> {
|
|
||||||
fn new(ebpf: &mut Ebpf, map_name: &str) -> Result<Self, Error> {
|
|
||||||
let map = ebpf.take_map(map_name).ok_or(EbpfError::MapNotFound)?;
|
|
||||||
let map = AyaHashMap::try_from(map).map_err(EbpfError::MapOperationError)?;
|
|
||||||
Ok(Self { map })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_list(&self) -> Vec<T::Native> {
|
|
||||||
self.map
|
|
||||||
.keys()
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.map(|key| key.into_native())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_list(&mut self, address: T::Native) -> Result<(), Error> {
|
|
||||||
let address = T::from_native(address);
|
|
||||||
self.map
|
|
||||||
.insert(address, 0_u8, 0)
|
|
||||||
.map_err(|_| EbpfError::RuleReachLimit)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_list(&mut self, address: T::Native) -> Result<(), Error> {
|
|
||||||
let address = T::from_native(address);
|
|
||||||
self.map.remove(&address).map_err(|_| EbpfError::IpDoesNotExist)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -25,10 +25,6 @@ impl AppDB {
|
|||||||
pub fn open(path: impl AsRef<Path>, key: &str) -> Result<Self, Error> {
|
pub fn open(path: impl AsRef<Path>, key: &str) -> Result<Self, Error> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
|
||||||
if let Some(parent) = Path::new(path).parent() {
|
|
||||||
std::fs::create_dir_all(parent).map_err(|e| AuthError::DBError { msg: e.to_string() })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let conn = Connection::open(path).map_err(|e| AuthError::DBError { msg: e.to_string() })?;
|
let conn = Connection::open(path).map_err(|e| AuthError::DBError { msg: e.to_string() })?;
|
||||||
|
|
||||||
// Must be the first statement on the connection to unlock the encrypted DB.
|
// Must be the first statement on the connection to unlock the encrypted DB.
|
||||||
|
|||||||
@ -40,6 +40,10 @@ pub struct System {
|
|||||||
|
|
||||||
impl System {
|
impl System {
|
||||||
pub async fn new() -> Result<Self, Error> {
|
pub async fn new() -> Result<Self, Error> {
|
||||||
|
Logging::initialize()?;
|
||||||
|
|
||||||
|
log!(SystemLog::Initializing);
|
||||||
|
|
||||||
let (mut ingress_ebpf, ingress_program_array) = System::get_ingress_ebpf()?;
|
let (mut ingress_ebpf, ingress_program_array) = System::get_ingress_ebpf()?;
|
||||||
let (mut egress_ebpf, egress_program_array) = System::get_egress_ebpf()?;
|
let (mut egress_ebpf, egress_program_array) = System::get_egress_ebpf()?;
|
||||||
let app_config = Arc::new(AppConfig::new()?);
|
let app_config = Arc::new(AppConfig::new()?);
|
||||||
@ -73,8 +77,6 @@ impl System {
|
|||||||
pub async fn run(&mut self) -> Result<(), Error> {
|
pub async fn run(&mut self) -> Result<(), Error> {
|
||||||
let ebpf_services = self.ebpf_services.clone();
|
let ebpf_services = self.ebpf_services.clone();
|
||||||
let app_services = self.app_services.clone();
|
let app_services = self.app_services.clone();
|
||||||
Logging::initialize()?;
|
|
||||||
log!(SystemLog::Initializing);
|
|
||||||
|
|
||||||
log!(MLLog::ModelsLoaded(
|
log!(MLLog::ModelsLoaded(
|
||||||
self.app_services.ml_models.get_model_info("deep_autoencoder")
|
self.app_services.ml_models.get_model_info("deep_autoencoder")
|
||||||
@ -85,7 +87,6 @@ impl System {
|
|||||||
});
|
});
|
||||||
|
|
||||||
self.aya_log_init()?;
|
self.aya_log_init()?;
|
||||||
log!(SystemLog::InitializeComplete);
|
|
||||||
self.attach_ebpf()?;
|
self.attach_ebpf()?;
|
||||||
|
|
||||||
ebpf_services
|
ebpf_services
|
||||||
@ -93,6 +94,8 @@ impl System {
|
|||||||
.await?;
|
.await?;
|
||||||
app_services.run().await?;
|
app_services.run().await?;
|
||||||
self.run_http_server().await?;
|
self.run_http_server().await?;
|
||||||
|
|
||||||
|
log!(SystemLog::InitializeComplete);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +149,6 @@ impl System {
|
|||||||
app_config: self.app_config.clone(),
|
app_config: self.app_config.clone(),
|
||||||
inference_config: self.inference_config.clone(),
|
inference_config: self.inference_config.clone(),
|
||||||
access_control: self.ebpf_services.access_control.clone(),
|
access_control: self.ebpf_services.access_control.clone(),
|
||||||
service: self.ebpf_services.service.clone(),
|
|
||||||
statistics: self.ebpf_services.statistics.clone(),
|
statistics: self.ebpf_services.statistics.clone(),
|
||||||
health: self.app_services.health.clone(),
|
health: self.app_services.health.clone(),
|
||||||
detection_alert: self.app_services.detection_alert.clone(),
|
detection_alert: self.app_services.detection_alert.clone(),
|
||||||
@ -168,6 +170,8 @@ impl System {
|
|||||||
.await
|
.await
|
||||||
.map_err(HttpError::BindPortError)?;
|
.map_err(HttpError::BindPortError)?;
|
||||||
|
|
||||||
|
log!(SystemLog::HttpServerListening { port });
|
||||||
|
|
||||||
axum::serve(listener, app)
|
axum::serve(listener, app)
|
||||||
.with_graceful_shutdown(async {
|
.with_graceful_shutdown(async {
|
||||||
tokio::signal::ctrl_c().await.ok();
|
tokio::signal::ctrl_c().await.ok();
|
||||||
@ -189,7 +193,6 @@ impl System {
|
|||||||
"access_control",
|
"access_control",
|
||||||
ingress::ACCESS_CONTROL,
|
ingress::ACCESS_CONTROL,
|
||||||
)?;
|
)?;
|
||||||
Self::load_program(&mut ingress_ebpf, &mut program_array, "service", ingress::SERVICE)?;
|
|
||||||
Self::load_program(&mut ingress_ebpf, &mut program_array, "statistics", ingress::STATISTICS)?;
|
Self::load_program(&mut ingress_ebpf, &mut program_array, "statistics", ingress::STATISTICS)?;
|
||||||
Self::load_program(
|
Self::load_program(
|
||||||
&mut ingress_ebpf,
|
&mut ingress_ebpf,
|
||||||
@ -205,6 +208,12 @@ impl System {
|
|||||||
Ebpf::load(aya::include_bytes_aligned!(env!("EGRESS_PATH"))).map_err(EbpfError::EbpfNotFound)?;
|
Ebpf::load(aya::include_bytes_aligned!(env!("EGRESS_PATH"))).map_err(EbpfError::EbpfNotFound)?;
|
||||||
let program_array = egress_ebpf.take_map("PROGRAM_ARRAY").ok_or(EbpfError::MapNotFound)?;
|
let program_array = egress_ebpf.take_map("PROGRAM_ARRAY").ok_or(EbpfError::MapNotFound)?;
|
||||||
let mut program_array = ProgramArray::try_from(program_array).map_err(EbpfError::MapOperationError)?;
|
let mut program_array = ProgramArray::try_from(program_array).map_err(EbpfError::MapOperationError)?;
|
||||||
|
Self::load_program(
|
||||||
|
&mut egress_ebpf,
|
||||||
|
&mut program_array,
|
||||||
|
"access_control",
|
||||||
|
egress::ACCESS_CONTROL,
|
||||||
|
)?;
|
||||||
Self::load_program(&mut egress_ebpf, &mut program_array, "statistics", egress::STATISTICS)?;
|
Self::load_program(&mut egress_ebpf, &mut program_array, "statistics", egress::STATISTICS)?;
|
||||||
Self::load_program(
|
Self::load_program(
|
||||||
&mut egress_ebpf,
|
&mut egress_ebpf,
|
||||||
|
|||||||
@ -33,6 +33,7 @@ impl SuricataEngine {
|
|||||||
eve_socket: &Path,
|
eve_socket: &Path,
|
||||||
fusion: Arc<FusionEngine>,
|
fusion: Arc<FusionEngine>,
|
||||||
) -> Result<Arc<Self>, SuricataError> {
|
) -> Result<Arc<Self>, SuricataError> {
|
||||||
|
Self::kill_existing();
|
||||||
Self::setup_veth()?;
|
Self::setup_veth()?;
|
||||||
|
|
||||||
let ifindex = Self::get_ifindex(MIRROR_IFACE)?;
|
let ifindex = Self::get_ifindex(MIRROR_IFACE)?;
|
||||||
@ -242,7 +243,7 @@ vars:
|
|||||||
ENIP_CLIENT: "$HOME_NET"
|
ENIP_CLIENT: "$HOME_NET"
|
||||||
ENIP_SERVER: "$HOME_NET"
|
ENIP_SERVER: "$HOME_NET"
|
||||||
port-groups:
|
port-groups:
|
||||||
HTTP_PORTS: "80"
|
HTTP_PORTS: "80,8080,8000,8008,8443,8888"
|
||||||
SHELLCODE_PORTS: "!80"
|
SHELLCODE_PORTS: "!80"
|
||||||
ORACLE_PORTS: 1521
|
ORACLE_PORTS: 1521
|
||||||
SSH_PORTS: 22
|
SSH_PORTS: 22
|
||||||
@ -293,6 +294,8 @@ app-layer:
|
|||||||
enabled: yes
|
enabled: yes
|
||||||
http:
|
http:
|
||||||
enabled: yes
|
enabled: yes
|
||||||
|
detection-ports:
|
||||||
|
dp: "any"
|
||||||
dns:
|
dns:
|
||||||
enabled: yes
|
enabled: yes
|
||||||
smtp:
|
smtp:
|
||||||
@ -303,6 +306,7 @@ app-layer:
|
|||||||
af-packet:
|
af-packet:
|
||||||
- interface: {iface}
|
- interface: {iface}
|
||||||
threads: {threads}
|
threads: {threads}
|
||||||
|
cluster-id: 99
|
||||||
use-mmap: yes
|
use-mmap: yes
|
||||||
tpacket-v3: yes
|
tpacket-v3: yes
|
||||||
ring-size: {ring_size}
|
ring-size: {ring_size}
|
||||||
@ -315,7 +319,7 @@ legacy:
|
|||||||
|
|
||||||
host-mode: sniffer-only
|
host-mode: sniffer-only
|
||||||
"#,
|
"#,
|
||||||
home_net = config.home_net,
|
home_net = config.home_net.join(","),
|
||||||
rule_path = rule_path,
|
rule_path = rule_path,
|
||||||
eve_socket = eve_socket,
|
eve_socket = eve_socket,
|
||||||
suppress_path = suppress_path,
|
suppress_path = suppress_path,
|
||||||
@ -349,6 +353,11 @@ host-mode: sniffer-only
|
|||||||
Ok(fd)
|
Ok(fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn kill_existing() {
|
||||||
|
let _ = Command::new("pkill").args(["-x", "suricata"]).output();
|
||||||
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
}
|
||||||
|
|
||||||
fn setup_veth() -> Result<(), SuricataError> {
|
fn setup_veth() -> Result<(), SuricataError> {
|
||||||
let _ = Command::new("ip").args(["link", "del", MIRROR_IFACE]).output();
|
let _ = Command::new("ip").args(["link", "del", MIRROR_IFACE]).output();
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ pub struct ConfigTable {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct SuricataConfig {
|
pub struct SuricataConfig {
|
||||||
pub home_net: String,
|
pub home_net: Vec<String>,
|
||||||
pub worker_cpu_set: Option<[u32; 2]>,
|
pub worker_cpu_set: Option<[u32; 2]>,
|
||||||
pub management_cpu: Option<u32>,
|
pub management_cpu: Option<u32>,
|
||||||
#[serde(default = "default_af_threads")]
|
#[serde(default = "default_af_threads")]
|
||||||
|
|||||||
@ -27,5 +27,8 @@ loggable! {
|
|||||||
#[error("Traffic logging mode enabled — writing packets to: {path}")]
|
#[error("Traffic logging mode enabled — writing packets to: {path}")]
|
||||||
TrafficLoggingEnabled { path: String } => tracing::Level::INFO,
|
TrafficLoggingEnabled { path: String } => tracing::Level::INFO,
|
||||||
|
|
||||||
|
#[error("HTTP server listening on 0.0.0.0:{port}")]
|
||||||
|
HttpServerListening { port: u16 } => tracing::Level::INFO,
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,65 +7,65 @@ use axum::routing::{delete, get, put};
|
|||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
|
|
||||||
use crate::core::app_state::AppState;
|
use crate::core::app_state::AppState;
|
||||||
use crate::model::direction::FlowDirection;
|
use crate::model::direction::{Direction, FlowDirection};
|
||||||
use crate::model::list_type::ListType;
|
use crate::model::list_type::ListType;
|
||||||
|
|
||||||
pub fn router() -> Router<AppState> {
|
pub fn router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route(
|
.route(
|
||||||
"/ipv4/{direction}/{list_type}",
|
"/{nic_direction}/ipv4/{flow_direction}/{list_type}",
|
||||||
get(get_ipv4_list).put(add_ipv4_list).delete(remove_ipv4_list),
|
get(get_ipv4_list).put(add_ipv4_list).delete(remove_ipv4_list),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/ipv6/{direction}/{list_type}",
|
"/{nic_direction}/ipv6/{flow_direction}/{list_type}",
|
||||||
get(get_ipv6_list).put(add_ipv6_list).delete(remove_ipv6_list),
|
get(get_ipv6_list).put(add_ipv6_list).delete(remove_ipv6_list),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_ipv4_list(
|
async fn get_ipv4_list(
|
||||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
Json(state.access_control.get_ipv4_list(direction, list_type).await)
|
Json(state.access_control.get_ipv4_list(nic, flow, list_type).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_ipv6_list(
|
async fn get_ipv6_list(
|
||||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
Json(state.access_control.get_ipv6_list(direction, list_type).await)
|
Json(state.access_control.get_ipv6_list(nic, flow, list_type).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_ipv4_list(
|
async fn add_ipv4_list(
|
||||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(address): Json<SocketAddrV4>,
|
Json(address): Json<SocketAddrV4>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match state.access_control.add_ipv4_list(direction, list_type, address).await {
|
match state.access_control.add_ipv4_list(nic, flow, list_type, address).await {
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
Ok(_) => StatusCode::OK.into_response(),
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_ipv6_list(
|
async fn add_ipv6_list(
|
||||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(address): Json<SocketAddrV6>,
|
Json(address): Json<SocketAddrV6>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match state.access_control.add_ipv6_list(direction, list_type, address).await {
|
match state.access_control.add_ipv6_list(nic, flow, list_type, address).await {
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
Ok(_) => StatusCode::OK.into_response(),
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ipv4_list(
|
async fn remove_ipv4_list(
|
||||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(address): Json<SocketAddrV4>,
|
Json(address): Json<SocketAddrV4>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match state
|
match state
|
||||||
.access_control
|
.access_control
|
||||||
.remove_ipv4_list(direction, list_type, address)
|
.remove_ipv4_list(nic, flow, list_type, address)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
Ok(_) => StatusCode::OK.into_response(),
|
||||||
@ -74,13 +74,13 @@ async fn remove_ipv4_list(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ipv6_list(
|
async fn remove_ipv6_list(
|
||||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(address): Json<SocketAddrV6>,
|
Json(address): Json<SocketAddrV6>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match state
|
match state
|
||||||
.access_control
|
.access_control
|
||||||
.remove_ipv6_list(direction, list_type, address)
|
.remove_ipv6_list(nic, flow, list_type, address)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
Ok(_) => StatusCode::OK.into_response(),
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
pub mod access_control;
|
pub mod access_control;
|
||||||
pub mod service;
|
|
||||||
pub mod statistics;
|
pub mod statistics;
|
||||||
|
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
@ -9,6 +8,5 @@ use crate::core::app_state::AppState;
|
|||||||
pub fn router() -> Router<AppState> {
|
pub fn router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.nest("/access_control", access_control::router())
|
.nest("/access_control", access_control::router())
|
||||||
.nest("/service", service::router())
|
|
||||||
.nest("/statistics", statistics::router())
|
.nest("/statistics", statistics::router())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,243 +0,0 @@
|
|||||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
|
|
||||||
|
|
||||||
use axum::extract::{Path, State};
|
|
||||||
use axum::http::StatusCode;
|
|
||||||
use axum::response::IntoResponse;
|
|
||||||
use axum::routing::{delete, get, post, put};
|
|
||||||
use axum::{Json, Router};
|
|
||||||
use common::model::http_method::HttpMethod;
|
|
||||||
|
|
||||||
use crate::core::app_state::AppState;
|
|
||||||
|
|
||||||
pub fn router() -> Router<AppState> {
|
|
||||||
Router::new()
|
|
||||||
.route(
|
|
||||||
"/ipv4/http_service",
|
|
||||||
get(get_ipv4_http_service)
|
|
||||||
.put(add_ipv4_http_service)
|
|
||||||
.delete(remove_ipv4_http_service),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/ipv6/http_service",
|
|
||||||
get(get_ipv6_http_service)
|
|
||||||
.put(add_ipv6_http_service)
|
|
||||||
.delete(remove_ipv6_http_service),
|
|
||||||
)
|
|
||||||
.route("/ssh_white_list", get(is_ssh_white_list_enable))
|
|
||||||
.route("/ssh_white_list/enable", post(enable_ssh_white_list))
|
|
||||||
.route("/ssh_white_list/disable", post(disable_ssh_white_list))
|
|
||||||
.route(
|
|
||||||
"/ipv4/ssh_service",
|
|
||||||
get(get_ipv4_ssh_service)
|
|
||||||
.put(add_ipv4_ssh_service)
|
|
||||||
.delete(remove_ipv4_ssh_service),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/ipv6/ssh_service",
|
|
||||||
get(get_ipv6_ssh_service)
|
|
||||||
.put(add_ipv6_ssh_service)
|
|
||||||
.delete(remove_ipv6_ssh_service),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/ipv4/ssh_white_list",
|
|
||||||
get(get_ipv4_ssh_white_list)
|
|
||||||
.put(add_ipv4_ssh_white_list)
|
|
||||||
.delete(remove_ipv4_ssh_white_list),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/ipv6/ssh_white_list",
|
|
||||||
get(get_ipv6_ssh_white_list)
|
|
||||||
.put(add_ipv6_ssh_white_list)
|
|
||||||
.delete(remove_ipv6_ssh_white_list),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/ipv4/ssh_black_list",
|
|
||||||
get(get_ipv4_ssh_black_list)
|
|
||||||
.put(add_ipv4_ssh_black_list)
|
|
||||||
.delete(remove_ipv4_ssh_black_list),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/ipv6/ssh_black_list",
|
|
||||||
get(get_ipv6_ssh_black_list)
|
|
||||||
.put(add_ipv6_ssh_black_list)
|
|
||||||
.delete(remove_ipv6_ssh_black_list),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_ipv4_http_service(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
Json(state.service.get_ipv4_http_service().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_ipv6_http_service(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
Json(state.service.get_ipv6_http_service().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_ipv4_http_service(
|
|
||||||
State(state): State<AppState>,
|
|
||||||
Json(payload): Json<(SocketAddrV4, Vec<HttpMethod>)>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let (addr, methods) = payload;
|
|
||||||
match state.service.add_ipv4_http_service(addr, methods).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_ipv6_http_service(
|
|
||||||
State(state): State<AppState>,
|
|
||||||
Json(payload): Json<(SocketAddrV6, Vec<HttpMethod>)>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let (addr, methods) = payload;
|
|
||||||
match state.service.add_ipv6_http_service(addr, methods).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_ipv4_http_service(
|
|
||||||
State(state): State<AppState>,
|
|
||||||
Json(payload): Json<(SocketAddrV4, Vec<HttpMethod>)>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let (addr, methods) = payload;
|
|
||||||
match state.service.remove_ipv4_http_service(addr, methods).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_ipv6_http_service(
|
|
||||||
State(state): State<AppState>,
|
|
||||||
Json(payload): Json<(SocketAddrV6, Vec<HttpMethod>)>,
|
|
||||||
) -> impl IntoResponse {
|
|
||||||
let (addr, methods) = payload;
|
|
||||||
match state.service.remove_ipv6_http_service(addr, methods).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn is_ssh_white_list_enable(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
Json(state.service.is_ssh_white_list_enable().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn enable_ssh_white_list(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
match state.service.enable_ssh_white_list().await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn disable_ssh_white_list(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
match state.service.disable_ssh_white_list().await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_ipv4_ssh_service(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
Json(state.service.get_ipv4_ssh_service().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_ipv6_ssh_service(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
Json(state.service.get_ipv6_ssh_service().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_ipv4_ssh_service(State(state): State<AppState>, Json(addr): Json<SocketAddrV4>) -> impl IntoResponse {
|
|
||||||
match state.service.add_ipv4_ssh_service(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_ipv6_ssh_service(State(state): State<AppState>, Json(addr): Json<SocketAddrV6>) -> impl IntoResponse {
|
|
||||||
match state.service.add_ipv6_ssh_service(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_ipv4_ssh_service(State(state): State<AppState>, Json(addr): Json<SocketAddrV4>) -> impl IntoResponse {
|
|
||||||
match state.service.remove_ipv4_ssh_service(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_ipv6_ssh_service(State(state): State<AppState>, Json(addr): Json<SocketAddrV6>) -> impl IntoResponse {
|
|
||||||
match state.service.remove_ipv6_ssh_service(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_ipv4_ssh_white_list(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
Json(state.service.get_ipv4_ssh_white_list().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_ipv6_ssh_white_list(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
Json(state.service.get_ipv6_ssh_white_list().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_ipv4_ssh_white_list(State(state): State<AppState>, Json(addr): Json<Ipv4Addr>) -> impl IntoResponse {
|
|
||||||
match state.service.add_ipv4_ssh_white_list(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_ipv6_ssh_white_list(State(state): State<AppState>, Json(addr): Json<Ipv6Addr>) -> impl IntoResponse {
|
|
||||||
match state.service.add_ipv6_ssh_white_list(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_ipv4_ssh_white_list(State(state): State<AppState>, Json(addr): Json<Ipv4Addr>) -> impl IntoResponse {
|
|
||||||
match state.service.remove_ipv4_ssh_white_list(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_ipv6_ssh_white_list(State(state): State<AppState>, Json(addr): Json<Ipv6Addr>) -> impl IntoResponse {
|
|
||||||
match state.service.remove_ipv6_ssh_white_list(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_ipv4_ssh_black_list(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
Json(state.service.get_ipv4_ssh_black_list().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_ipv6_ssh_black_list(State(state): State<AppState>) -> impl IntoResponse {
|
|
||||||
Json(state.service.get_ipv6_ssh_black_list().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_ipv4_ssh_black_list(State(state): State<AppState>, Json(addr): Json<Ipv4Addr>) -> impl IntoResponse {
|
|
||||||
match state.service.add_ipv4_ssh_black_list(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_ipv6_ssh_black_list(State(state): State<AppState>, Json(addr): Json<Ipv6Addr>) -> impl IntoResponse {
|
|
||||||
match state.service.add_ipv6_ssh_black_list(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_ipv4_ssh_black_list(State(state): State<AppState>, Json(addr): Json<Ipv4Addr>) -> impl IntoResponse {
|
|
||||||
match state.service.remove_ipv4_ssh_black_list(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_ipv6_ssh_black_list(State(state): State<AppState>, Json(addr): Json<Ipv6Addr>) -> impl IntoResponse {
|
|
||||||
match state.service.remove_ipv6_ssh_black_list(addr).await {
|
|
||||||
Ok(_) => StatusCode::OK.into_response(),
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user