Zbuduj własne laboratorium agenta SI ds. bezpieczeństwa

Jak stworzyć autonomiczny system łatania podatności przy użyciu LM Studio 0.4.1, Devstral i Claude CLI.
Enterprise'owe narzędzia SI ds. bezpieczeństwa, takie jak Cogent Security, właśnie zebrały 42 miliony dolarów na automatyzację zarządzania podatnościami. Imponujące. Ale co, gdybyś mógł zbudować coś podobnego dla własnego domowego laboratorium — używając narzędzi open-source i lokalnej SI?
W tym artykule pokażę ci, jak stworzyć własnego agenta SI ds. bezpieczeństwa, który:
- Skanuje kontenery Docker, maszyny wirtualne Proxmox i kontenery LXC w poszukiwaniu podatności
- Używa LM Studio 0.4.1 z Anthropic-kompatybilnym API — działa natywnie z Claude CLI
- Uruchamia lokalnie model Devstral Small 2 firmy Mistral (68% na SWE-bench Verified)
- Kosztuje 0 zł w opłatach za API — wszystko działa na twoim sprzęcie
To jest poradnik, który chciałbym mieć, gdy zaczynałem automatyzować bezpieczeństwo mojego homelaba.
Co się zmieniło w styczniu 2026
LM Studio 0.4.1 (wydany 29 stycznia 2026) wprowadził przełomową funkcję: natywną kompatybilność z Anthropic API.
Oznacza to:
- ✅ Claude CLI działa bezpośrednio z lokalnymi modelami
- ✅ Użycie endpointu
/v1/messages(identycznego jak w Anthropic) - ✅ Drop-in replacement dla Anthropic SDK
- ✅ Wsparcie dla streamingu (
message_start,content_block_delta,message_stop)
Koniec z obejściami. Koniec z warstwą kompatybilności OpenAI. Czysta, natywna integracja Claude z twoimi lokalnymi modelami.
Stos technologiczny
| Komponent | Przeznaczenie |
|---|---|
| LM Studio 0.4.1 | Lokalny serwer modeli z Anthropic API |
| Devstral Small 2 | Model specjalizowany w kodzie, 24B parametrów |
| Claude CLI | Interfejs agenta |
| Trivy | Skaner podatności kontenerów |
| Grype | Alternatywny skaner podatności |
| proxmoxer | Proxmox Python API |
Część 1: Konfiguracja LM Studio 0.4.1+ z Anthropic API
Dlaczego LM Studio 0.4.1?
Wydanie z 29 stycznia 2026 dodało natywną kompatybilność z Anthropic API:
- Endpoint:
http://localhost:1234/v1/messages - Środowisko:
ANTHROPIC_BASE_URL=http://localhost:1234 - Auth:
ANTHROPIC_AUTH_TOKEN=lmstudio(lub dowolny ciąg znaków)
Sprawia to, że LM Studio jest drop-in replacement dla chmurowego API Anthropic.
Instalacja
Linux (AppImage):
# Download LM Studio 0.4.1+
wget https://releases.lmstudio.ai/linux/0.4.1/LM-Studio-0.4.1-x86_64.AppImage
chmod +x LM-Studio-0.4.1-x86_64.AppImage
./LM-Studio-0.4.1-x86_64.AppImage
macOS:
brew install --cask lm-studio
Windows: Pobierz instalator z https://lmstudio.ai/
Instalacja CLI LM Studio
LM Studio 0.4.1+ zawiera narzędzie wiersza poleceń lms:
# Verify CLI installation (after running LM Studio GUI at least once)
lms --help
# Check if server is running
lms ps
# List loaded models
lms ls
Pobieranie modelu Devstral Small 2
# Install Hugging Face Hub
pip install -U huggingface-hub
# Download Devstral Small 2 (Q4_K_M quantization)
huggingface-cli download \
unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF \
Devstral-Small-2-24B-Instruct-2512-Q4_K_M.gguf \
--local-dir ~/.lmstudio/models
# Alternative: Codestral-22B (smaller, faster)
huggingface-cli download \
lmstudio-community/Codestral-22B-v0.1-GGUF \
Codestral-22B-v0.1-Q4_K_M.gguf \
--local-dir ~/.lmstudio/models
Włączanie serwera kompatybilnego z Anthropic
# Load model and start server
lms load unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF \
--gpu 1.0 \
--context-length 32768
# Start server
lms server start --port 1234
Weryfikacja statusu serwera
lms ps
# Expected output:
# ✓ Server running on port 1234
# ✓ Model loaded: Devstral-Small-2-24B-Instruct-2512
# ✓ API format: Anthropic Messages
Testowanie Anthropic-kompatybilnego API
Test Python:
#!/usr/bin/env python3
from anthropic import Anthropic
def test_lm_studio():
client = Anthropic(
base_url="http://localhost:1234",
api_key="lmstudio"
)
try:
message = client.messages.create(
model="devstral-small-24b",
max_tokens=1024,
messages=[{"role": "user", "content": "What is CVE-2024-3094?"}]
)
print("✅ Connection successful!")
print(f"Response: {message.content[0].text}")
return True
except Exception as e:
print(f"❌ Connection failed: {e}")
return False
if __name__ == "__main__":
test_lm_studio()
Test cURL:
curl http://localhost:1234/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: lmstudio" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "devstral-small-24b",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Explain CVE scanning in one paragraph"}]
}'
Część 2: Instalacja Claude CLI
Instalacja
# macOS, Linux, WSL
curl -fsSL https://claude.ai/install.sh | bash
# Verify
claude --version
Konfiguracja
# Add to ~/.bashrc or ~/.zshrc
export ANTHROPIC_BASE_URL="http://localhost:1234"
export ANTHROPIC_AUTH_TOKEN="lmstudio"
source ~/.bashrc
Test Claude CLI
# Interactive session
claude
# Single command
claude "List all Python files and check for SQL injection vulnerabilities"
# Print mode (query and exit)
claude -p "Review this Dockerfile for security issues"
Weryfikacja połączenia
DEBUG=1 claude -p "What is 2+2?"
# Expected output includes:
# → Connecting to http://localhost:1234/v1/messages
# → Model: devstral-small-24b
Część 3: Konfiguracja skanowania podatności
Trivy (zalecany dla Dockera)
# Install latest Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | \
sh -s -- -b /usr/local/bin
# Scan a Docker image
trivy image nginx:latest --format json --output trivy-report.json
# Scan all running containers
mkdir -p reports
docker ps --format '{{.Image}}' | while read img; do
safe_name=$(echo "$img" | tr '/:' '_')
trivy image "$img" --format json --output "reports/${safe_name}.json"
done
Grype (alternatywa)
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | \
sh -s -- -b /usr/local/bin
grype nginx:latest -o json > grype-report.json
Skaner Proxmox
#!/usr/bin/env python3
import json, os
from typing import List, Dict, Any, Optional
from proxmoxer import ProxmoxAPI
class ProxmoxScanner:
def __init__(self, host: str, user: str,
password: Optional[str] = None, verify_ssl: bool = False):
password = password or os.getenv('PROXMOX_PASSWORD')
if not password:
raise ValueError("Password required (set PROXMOX_PASSWORD env var)")
self.proxmox = ProxmoxAPI(host, user=user, password=password, verify_ssl=verify_ssl)
def scan_lxc_containers(self) -> List[Dict[str, Any]]:
results = []
for node in self.proxmox.nodes.get():
node_name = node['node']
for container in self.proxmox.nodes(node_name).lxc.get():
if container['status'] != 'running':
continue
vmid = container['vmid']
try:
result = self.proxmox.nodes(node_name).lxc(vmid).exec.post(
command='apt list --upgradable 2>/dev/null | grep -i security'
)
results.append({
'type': 'lxc', 'node': node_name, 'vmid': vmid,
'name': container.get('name', f'CT-{vmid}'),
'security_updates': result
})
except Exception as e:
print(f"Error scanning LXC {vmid}: {e}")
return results
if __name__ == "__main__":
scanner = ProxmoxScanner(
host=os.getenv('PROXMOX_HOST', '192.168.1.100'),
user=os.getenv('PROXMOX_USER', 'root@pam'),
password=os.getenv('PROXMOX_PASSWORD')
)
results = scanner.scan_lxc_containers()
with open('proxmox-scan-results.json', 'w') as f:
json.dump(results, f, indent=2)
Część 4: Budowanie agenta SI ds. bezpieczeństwa
#!/usr/bin/env python3
import json, subprocess, os
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Any
from anthropic import Anthropic
class SecurityAgent:
def __init__(self, lm_studio_url: str = "http://localhost:1234"):
if not lm_studio_url.startswith(('http://', 'https://')):
raise ValueError("Invalid LM Studio URL format")
self.client = Anthropic(base_url=lm_studio_url, api_key="lmstudio")
self.model = "devstral-small-24b"
self.scans_dir = Path("scans")
self.patches_dir = Path("patches")
self.scans_dir.mkdir(parents=True, exist_ok=True)
self.patches_dir.mkdir(parents=True, exist_ok=True)
def call_devstral(self, system_prompt: str, user_message: str) -> str:
try:
message = self.client.messages.create(
model=self.model, max_tokens=4096,
system=system_prompt,
messages=[{"role": "user", "content": user_message}]
)
return message.content[0].text
except Exception as e:
print(f"Error calling Devstral: {e}")
return ""
def scan_docker(self) -> List[Dict[str, Any]]:
print("🔍 Scanning Docker containers...")
result = subprocess.run(
["docker", "ps", "--format", "{{.Names}}\t{{.Image}}"],
capture_output=True, text=True, check=False
)
containers = [
{'name': parts[0], 'image': parts[1]}
for line in result.stdout.strip().split('\n')
if line and len(parts := line.split('\t')) == 2
]
scan_results = []
for container in containers:
print(f" Scanning {container['name']} ({container['image']})...")
scan_file = self.scans_dir / f"docker_{container['name']}.json"
subprocess.run(
["trivy", "image", container['image'],
"--format", "json", "--output", str(scan_file), "--quiet"],
capture_output=True, check=False
)
if scan_file.exists():
with open(scan_file) as f:
scan_data = json.load(f)
vulnerabilities = [
{
'id': v.get('VulnerabilityID'),
'severity': v.get('Severity'),
'package': v.get('PkgName'),
'installed': v.get('InstalledVersion'),
'fixed': v.get('FixedVersion'),
}
for r in scan_data.get('Results', [])
for v in r.get('Vulnerabilities', [])
]
scan_results.append({
'container': container['name'],
'image': container['image'],
'vulnerabilities': vulnerabilities
})
return scan_results
def analyze_vulnerabilities(self, scan_results: List[Dict[str, Any]]) -> Dict[str, Any]:
system_prompt = """You are a security expert AI. Analyze vulnerability scan results and
output a JSON remediation plan: {"summary":"...","critical_vulnerabilities":[...],"remediation_steps":[...],"automation_candidates":[...]}"""
response = self.call_devstral(
system_prompt,
f"Analyze these scan results:\n\n{json.dumps(scan_results, indent=2)}"
)
try:
start, end = response.find('{'), response.rfind('}') + 1
if start >= 0 and end > start:
return json.loads(response[start:end])
except json.JSONDecodeError:
pass
return {"raw_analysis": response}
def run_full_scan_and_patch_cycle(self) -> Dict[str, Any]:
print("=" * 60)
print("🤖 Homelab AI Security Agent - Starting Scan")
print("=" * 60)
scan_results = self.scan_docker()
print("\n🧠 Analyzing vulnerabilities with Devstral...")
analysis = self.analyze_vulnerabilities(scan_results)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
analysis_file = self.scans_dir / f"analysis_{timestamp}.json"
with open(analysis_file, 'w') as f:
json.dump(analysis, f, indent=2)
print(f"\n✅ Analysis saved to {analysis_file}")
return {'scan_results': scan_results, 'analysis': analysis}
if __name__ == "__main__":
agent = SecurityAgent()
agent.run_full_scan_and_patch_cycle()
Część 5: Używanie Claude CLI do autonomicznego łatania
# Scan nginx and apply patches for CRITICAL/HIGH CVEs
claude -p "Scan nginx container for vulnerabilities using trivy, then apply security patches if any HIGH or CRITICAL CVEs are found"
# Create Proxmox patch playbook
claude -p "Create an Ansible playbook that updates all security packages on Proxmox LXC containers. Include error handling and rollback capabilities."
Wrapper Python:
#!/usr/bin/env python3
import subprocess, os
def run_claude_agent(prompt: str, workdir: str = ".") -> str:
env = os.environ.copy()
env["ANTHROPIC_BASE_URL"] = "http://localhost:1234"
env["ANTHROPIC_AUTH_TOKEN"] = "lmstudio"
result = subprocess.run(
["claude", "-p", prompt],
cwd=workdir, env=env, capture_output=True, text=True, check=False
)
return result.stdout
print(run_claude_agent("Scan docker-compose.yml for security issues"))
Część 6: Automatyczne planowanie zadań
Timer Systemd
# /etc/systemd/system/security-agent.service
[Unit]
Description=Homelab AI Security Agent
After=network.target docker.service
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /opt/security-agent/main.py
WorkingDirectory=/opt/security-agent
Environment="ANTHROPIC_BASE_URL=http://localhost:1234"
Environment="ANTHROPIC_AUTH_TOKEN=lmstudio"
# /etc/systemd/system/security-agent.timer
[Unit]
Description=Run security agent daily at 6 AM
[Timer]
OnCalendar=*-*-* 06:00:00
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now security-agent.timer
sudo systemctl list-timers security-agent.timer
Powiadomienia Telegram
import requests, os
def send_telegram_alert(message: str) -> bool:
bot_token = os.getenv("TELEGRAM_BOT_TOKEN")
chat_id = os.getenv("TELEGRAM_CHAT_ID")
if not bot_token or not chat_id:
return False
try:
r = requests.post(
f"https://api.telegram.org/bot{bot_token}/sendMessage",
json={"chat_id": chat_id, "text": message, "parse_mode": "HTML"},
timeout=10
)
return r.status_code == 200
except Exception:
return False
send_telegram_alert("🚨 <b>5 critical vulnerabilities found in nginx</b>")
Wymagania sprzętowe
- Minimum: 16 GB VRAM dla kwantyzacji Q4_K_M
- Zalecane: kontekst 32K (≈ 18–20 GB VRAM łącznie)
- Claude CLI działa najlepiej przy kontekście 25K+
Rozwiązywanie problemów
Problemy z serwerem LM Studio
lms ps
lms server stop && lms server start --port 1234
Problemy z połączeniem Claude CLI
echo $ANTHROPIC_BASE_URL # should be http://localhost:1234
echo $ANTHROPIC_AUTH_TOKEN # should be lmstudio
DEBUG=1 claude -p "test"
sudo lsof -i :1234 # check port
Problemy z OOM / ładowaniem modelu
nvidia-smi # check VRAM
# Reduce context or quantization
lms load model-name --gpu 0.7 --context-length 16384
Kluczowe wnioski
- LM Studio 0.4.1 ma natywne wsparcie Anthropic API — bez żadnych obejść
- Claude CLI działa bezpośrednio z lokalnymi modelami poprzez
ANTHROPIC_BASE_URL - Devstral Small 2 osiąga 68% na SWE-bench Verified
- Minimum 16 GB VRAM dla kwantyzacji Q4_K_M
- Kontekst 25K+ wymagany dla najlepszej wydajności Claude CLI
- Całkowity koszt API: 0 zł — wszystko działa na twoim sprzęcie
Jestem architektem systemów specjalizującym się w AI/ML, systemach Linux i centrach danych. Pomagam organizacjom budować bezpieczną, zautomatyzowaną infrastrukturę przy użyciu narzędzi open source.
#SI #AgenciSI #Cyberbezpieczeństwo #Homelab #Proxmox #Docker #LMStudio #Devstral #ClaudeCLI #Anthropic #OpenSource #DevSecOps #Automatyzacja