antony647 commited on
Commit
d247d74
·
verified ·
1 Parent(s): 76915da

Upload 5 files

Browse files
Files changed (5) hide show
  1. Dockerfile +6 -0
  2. inference.py +162 -0
  3. models.py +54 -0
  4. openenv.yaml +30 -0
  5. pyproject.toml +20 -0
Dockerfile ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+ WORKDIR /app
3
+ COPY . .
4
+ RUN pip install --no-cache-dir .
5
+ EXPOSE 7860
6
+ CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "7860"]
inference.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ from openai import OpenAI
4
+
5
+ API_BASE_URL = os.getenv("API_BASE_URL", "https://api-inference.huggingface.co/v1/")
6
+ MODEL_NAME = os.getenv("MODEL_NAME", "meta-llama/Meta-Llama-3-8B-Instruct")
7
+ HF_TOKEN = os.getenv("HF_TOKEN", "dummy_token")
8
+
9
+ ENV_URL = "http://127.0.0.1:7860"
10
+
11
+ client = OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN)
12
+
13
+
14
+ def log_start(task, env, model):
15
+ print(f"[START] task={task} env={env} model={model}", flush=True)
16
+
17
+
18
+ def log_step(step, action, reward, done, error):
19
+ done_str = "true" if done else "false"
20
+ err_str = "null" if error is None else f'"{error}"'
21
+ print(
22
+ f"[STEP] step={step} action={action} reward={float(reward):.2f} "
23
+ f"done={done_str} error={err_str}",
24
+ flush=True,
25
+ )
26
+
27
+
28
+ def log_end(success, steps, score, rewards):
29
+ succ_str = "true" if success else "false"
30
+ rews_str = ",".join([f"{float(r):.2f}" for r in rewards])
31
+ print(
32
+ f"[END] success={succ_str} steps={steps} score={score:.2f} rewards={rews_str}",
33
+ flush=True,
34
+ )
35
+
36
+
37
+ def _classify_trend(history, key):
38
+ if len(history) < 2:
39
+ return "UNKNOWN"
40
+ delta = history[-1][key] - history[0][key]
41
+ if key == "pps":
42
+ if delta > 1000:
43
+ return "SURGING"
44
+ if delta > 200:
45
+ return "RISING"
46
+ if delta < -200:
47
+ return "FALLING"
48
+ else:
49
+ if delta > 10:
50
+ return "RISING"
51
+ if delta < -5:
52
+ return "FALLING"
53
+ return "STABLE"
54
+
55
+
56
+ def get_action(history):
57
+ entries = []
58
+ for h in history:
59
+ entries.append(
60
+ f"(CPU {h['cpu']:.1f}%, PPS {h['pps']:.0f}, "
61
+ f"BW {h['bw']:.1f}Mbps, Health {h['health']:.0f}%)"
62
+ )
63
+ telemetry = " -> ".join(entries)
64
+
65
+ pps_trend = _classify_trend(history, "pps")
66
+ cpu_trend = _classify_trend(history, "cpu")
67
+
68
+ prompt = (
69
+ f"Network telemetry (last {len(history)} snapshots):\n"
70
+ f" {telemetry}\n"
71
+ f"PPS trend: {pps_trend} | CPU trend: {cpu_trend}\n\n"
72
+ f"Respond with exactly one word: monitor, rate_limit, or block."
73
+ )
74
+
75
+ system_msg = (
76
+ "You are a strict IoT network defense AI protecting critical infrastructure. "
77
+ "Analyse the telemetry trend and choose the optimal mitigation action. "
78
+ "Output ONLY one word — no explanation."
79
+ )
80
+
81
+ try:
82
+ response = client.chat.completions.create(
83
+ model=MODEL_NAME,
84
+ messages=[
85
+ {"role": "system", "content": system_msg},
86
+ {"role": "user", "content": prompt},
87
+ ],
88
+ max_tokens=10,
89
+ temperature=0.1,
90
+ )
91
+ text = response.choices[0].message.content.strip().lower()
92
+
93
+ if "block" in text:
94
+ return "block"
95
+ if "limit" in text or "rate" in text:
96
+ return "rate_limit"
97
+ return "monitor"
98
+ except Exception:
99
+ return "monitor"
100
+
101
+
102
+ def run_episode(task_id):
103
+ log_start(task=task_id, env="clairs-network-defense", model=MODEL_NAME)
104
+
105
+ try:
106
+ res = requests.post(f"{ENV_URL}/reset", json={"task_id": task_id}).json()
107
+ obs = res if "cpu_usage_percent" in res else res.get("observation", {})
108
+ except Exception:
109
+ obs = {
110
+ "cpu_usage_percent": 0.0,
111
+ "packet_rate_pps": 0.0,
112
+ "active_connections": 0,
113
+ "bandwidth_mbps": 0.0,
114
+ "memory_usage_percent": 30.0,
115
+ "system_health": 100.0,
116
+ }
117
+
118
+ done = False
119
+ step_count = 0
120
+ rewards = []
121
+ history = []
122
+
123
+ while not done and step_count < 10:
124
+ step_count += 1
125
+
126
+ cpu = obs.get("cpu_usage_percent", 0.0)
127
+ pps = obs.get("packet_rate_pps", 0.0)
128
+ bw = obs.get("bandwidth_mbps", 0.0)
129
+ health = obs.get("system_health", 100.0)
130
+ history.append({"cpu": cpu, "pps": pps, "bw": bw, "health": health})
131
+
132
+ if len(history) > 3:
133
+ history.pop(0)
134
+
135
+ action = get_action(history)
136
+
137
+ try:
138
+ step_res = requests.post(
139
+ f"{ENV_URL}/step", json={"decision": action}
140
+ ).json()
141
+ obs = step_res.get("observation", obs)
142
+ reward = step_res.get("reward", 0.01)
143
+ done = step_res.get("done", True)
144
+ error = None
145
+ except Exception as e:
146
+ reward = 0.01
147
+ done = True
148
+ error = str(e)
149
+
150
+ rewards.append(reward)
151
+ log_step(step=step_count, action=action, reward=reward, done=done, error=error)
152
+
153
+ raw_score = sum(rewards) / len(rewards) if rewards else 0.01
154
+ score = max(0.01, min(0.99, raw_score))
155
+ success = score >= 0.5
156
+ log_end(success=success, steps=step_count, score=score, rewards=rewards)
157
+
158
+
159
+ if __name__ == "__main__":
160
+ tasks = ["task_1_easy", "task_2_medium", "task_3_hard", "task_4_expert"]
161
+ for t in tasks:
162
+ run_episode(t)
models.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field
2
+ from typing import Dict, Any
3
+
4
+
5
+ class Observation(BaseModel):
6
+ cpu_usage_percent: float = Field(
7
+ ...,
8
+ description="Current CPU load of the IoT gateway (0.0 to 100.0).",
9
+ )
10
+ packet_rate_pps: float = Field(
11
+ ...,
12
+ description="Incoming packets per second observed at the network interface.",
13
+ )
14
+ active_connections: int = Field(
15
+ ...,
16
+ description="Number of concurrent TCP connections to the device.",
17
+ )
18
+ bandwidth_mbps: float = Field(
19
+ ...,
20
+ description="Current bandwidth consumption in megabits per second.",
21
+ )
22
+ memory_usage_percent: float = Field(
23
+ ...,
24
+ description="Device memory utilization (0.0 to 100.0).",
25
+ )
26
+ system_health: float = Field(
27
+ ...,
28
+ description="Cumulative system integrity score (0.0 to 100.0). "
29
+ "Degrades under sustained unmitigated attacks.",
30
+ )
31
+
32
+
33
+ class Action(BaseModel):
34
+ decision: str = Field(
35
+ ...,
36
+ description="The mitigation action to apply. "
37
+ "Must be one of: 'monitor', 'rate_limit', or 'block'.",
38
+ )
39
+
40
+
41
+ class StepResponse(BaseModel):
42
+ observation: Observation
43
+ reward: float = Field(
44
+ ...,
45
+ description="Reward score for the agent's action (strictly 0.0 to 1.0).",
46
+ )
47
+ done: bool = Field(
48
+ ...,
49
+ description="Whether the episode has terminated.",
50
+ )
51
+ info: Dict[str, Any] = Field(
52
+ default_factory=dict,
53
+ description="Diagnostic metadata: attack phase, severity, health, etc.",
54
+ )
openenv.yaml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: "1.0"
2
+ name: clairs-network-defense
3
+ description: "CLAIRS: Autonomous Defense Environment for IoT DDoS Mitigation — dynamic traffic simulation with multi-criteria reward scoring."
4
+
5
+ tasks:
6
+ - id: task_1_easy
7
+ description: >
8
+ Normal Traffic Monitoring. Benign IoT traffic with small natural fluctuations.
9
+ The agent must maintain monitoring without triggering false positives.
10
+
11
+ - id: task_2_medium
12
+ description: >
13
+ Volumetric DDoS Flood. A massive packet flood ramps from 5 000 to 50 000 PPS.
14
+ The agent must detect the surge and escalate to full blocking.
15
+
16
+ - id: task_3_hard
17
+ description: >
18
+ Stealth Low-and-Slow DDoS. A gradual attack that stays below obvious CPU thresholds.
19
+ The agent must detect the subtle PPS trend and apply rate limiting.
20
+
21
+ - id: task_4_expert
22
+ description: >
23
+ Multi-Wave APT Campaign. The attacker probes (PPS 4 000–12 000), retreats to
24
+ normal levels, then surges (PPS 15 000–45 000). The agent must not relax
25
+ defenses during the deceptive retreat phase.
26
+
27
+ endpoints:
28
+ reset: /reset
29
+ step: /step
30
+ state: /state
pyproject.toml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "clairs-network-defense"
7
+ version = "2.0.0"
8
+ description = "CLAIRS: Autonomous Defense Environment for IoT DDoS Mitigation"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "fastapi",
13
+ "uvicorn",
14
+ "pydantic",
15
+ "requests",
16
+ "openai",
17
+ ]
18
+
19
+ [project.scripts]
20
+ server = "server.app:main"