asd
This commit is contained in:
@@ -49,6 +49,9 @@ services:
|
|||||||
DATABASE_URL: postgresql://${POSTGRES_USER:-adctrl}:${POSTGRES_PASSWORD:-adctrl_secure_password}@postgres:5432/${POSTGRES_DB:-adctrl}
|
DATABASE_URL: postgresql://${POSTGRES_USER:-adctrl}:${POSTGRES_PASSWORD:-adctrl_secure_password}@postgres:5432/${POSTGRES_DB:-adctrl}
|
||||||
SECRET_TOKEN: ${SECRET_TOKEN}
|
SECRET_TOKEN: ${SECRET_TOKEN}
|
||||||
SCOREBOARD_WS_URL: ${SCOREBOARD_WS_URL:-ws://10.60.0.1:8080/api/events}
|
SCOREBOARD_WS_URL: ${SCOREBOARD_WS_URL:-ws://10.60.0.1:8080/api/events}
|
||||||
|
SCOREBOARD_API_URL: ${SCOREBOARD_API_URL:-http://10.60.0.1:8080/api}
|
||||||
|
USE_HTTP_POLLING: ${USE_HTTP_POLLING:-false}
|
||||||
|
POLLING_INTERVAL: ${POLLING_INTERVAL:-10}
|
||||||
OUR_TEAM_ID: ${OUR_TEAM_ID:-1}
|
OUR_TEAM_ID: ${OUR_TEAM_ID:-1}
|
||||||
ALERT_THRESHOLD_POINTS: ${ALERT_THRESHOLD_POINTS:-100}
|
ALERT_THRESHOLD_POINTS: ${ALERT_THRESHOLD_POINTS:-100}
|
||||||
ALERT_THRESHOLD_TIME: ${ALERT_THRESHOLD_TIME:-300}
|
ALERT_THRESHOLD_TIME: ${ALERT_THRESHOLD_TIME:-300}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ from contextlib import asynccontextmanager
|
|||||||
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://adctrl:adctrl@postgres:5432/adctrl")
|
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://adctrl:adctrl@postgres:5432/adctrl")
|
||||||
SECRET_TOKEN = os.getenv("SECRET_TOKEN", "change-me-in-production")
|
SECRET_TOKEN = os.getenv("SECRET_TOKEN", "change-me-in-production")
|
||||||
SCOREBOARD_WS_URL = os.getenv("SCOREBOARD_WS_URL", "ws://scoreboard:8080/api/events")
|
SCOREBOARD_WS_URL = os.getenv("SCOREBOARD_WS_URL", "ws://scoreboard:8080/api/events")
|
||||||
|
SCOREBOARD_API_URL = os.getenv("SCOREBOARD_API_URL", "http://10.60.0.1:8080/api")
|
||||||
|
USE_HTTP_POLLING = os.getenv("USE_HTTP_POLLING", "false").lower() == "true"
|
||||||
|
POLLING_INTERVAL = int(os.getenv("POLLING_INTERVAL", "10")) # seconds
|
||||||
OUR_TEAM_ID = int(os.getenv("OUR_TEAM_ID", "1"))
|
OUR_TEAM_ID = int(os.getenv("OUR_TEAM_ID", "1"))
|
||||||
ALERT_THRESHOLD_POINTS = float(os.getenv("ALERT_THRESHOLD_POINTS", "100"))
|
ALERT_THRESHOLD_POINTS = float(os.getenv("ALERT_THRESHOLD_POINTS", "100"))
|
||||||
ALERT_THRESHOLD_TIME = int(os.getenv("ALERT_THRESHOLD_TIME", "300")) # seconds
|
ALERT_THRESHOLD_TIME = int(os.getenv("ALERT_THRESHOLD_TIME", "300")) # seconds
|
||||||
@@ -184,6 +187,58 @@ async def send_telegram_alert(message: str):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error sending telegram alert: {e}")
|
print(f"Error sending telegram alert: {e}")
|
||||||
|
|
||||||
|
async def http_polling_listener():
|
||||||
|
"""Poll scoreboard HTTP API for attacks as alternative to WebSocket"""
|
||||||
|
last_round = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
# Try to fetch scoreboard data
|
||||||
|
# Common ForcAD endpoints: /api/scoreboard, /api/teams, /api/attacks
|
||||||
|
endpoints_to_try = [
|
||||||
|
f"{SCOREBOARD_API_URL}/attacks",
|
||||||
|
f"{SCOREBOARD_API_URL}/scoreboard",
|
||||||
|
f"{SCOREBOARD_API_URL}/flag_stats"
|
||||||
|
]
|
||||||
|
|
||||||
|
for endpoint in endpoints_to_try:
|
||||||
|
try:
|
||||||
|
async with session.get(endpoint, timeout=aiohttp.ClientTimeout(total=5)) as resp:
|
||||||
|
if resp.status == 200:
|
||||||
|
data = await resp.json()
|
||||||
|
print(f"✓ Fetched data from {endpoint}")
|
||||||
|
|
||||||
|
# Process based on response structure
|
||||||
|
if isinstance(data, list):
|
||||||
|
# List of attacks/events
|
||||||
|
for item in data:
|
||||||
|
await process_attack_event(item)
|
||||||
|
elif isinstance(data, dict):
|
||||||
|
# Could be scoreboard with nested data
|
||||||
|
# Try common keys
|
||||||
|
for key in ['attacks', 'flags', 'events', 'data']:
|
||||||
|
if key in data and isinstance(data[key], list):
|
||||||
|
for item in data[key]:
|
||||||
|
await process_attack_event(item)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Might be a single event
|
||||||
|
await process_attack_event(data)
|
||||||
|
|
||||||
|
break # Success, no need to try other endpoints
|
||||||
|
except aiohttp.ClientError as e:
|
||||||
|
print(f"Failed to fetch from {endpoint}: {e}")
|
||||||
|
continue
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f"Invalid JSON from {endpoint}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"HTTP polling error: {e}")
|
||||||
|
|
||||||
|
await asyncio.sleep(POLLING_INTERVAL)
|
||||||
|
|
||||||
async def websocket_listener():
|
async def websocket_listener():
|
||||||
"""Listen to scoreboard WebSocket for events"""
|
"""Listen to scoreboard WebSocket for events"""
|
||||||
reconnect_delay = 5
|
reconnect_delay = 5
|
||||||
@@ -248,9 +303,18 @@ async def lifespan(app: FastAPI):
|
|||||||
global db_pool, ws_task
|
global db_pool, ws_task
|
||||||
db_pool = await asyncpg.create_pool(DATABASE_URL, min_size=2, max_size=10)
|
db_pool = await asyncpg.create_pool(DATABASE_URL, min_size=2, max_size=10)
|
||||||
|
|
||||||
# Start WebSocket listener
|
# Start listener based on configuration
|
||||||
|
if USE_HTTP_POLLING:
|
||||||
|
print(f"Starting HTTP polling mode (interval: {POLLING_INTERVAL}s)")
|
||||||
|
print(f"Scoreboard API: {SCOREBOARD_API_URL}")
|
||||||
|
ws_task = asyncio.create_task(http_polling_listener())
|
||||||
|
else:
|
||||||
|
print(f"Starting WebSocket listener mode")
|
||||||
|
print(f"Scoreboard WS: {SCOREBOARD_WS_URL}")
|
||||||
ws_task = asyncio.create_task(websocket_listener())
|
ws_task = asyncio.create_task(websocket_listener())
|
||||||
|
|
||||||
|
print(f"Our team ID: {OUR_TEAM_ID}")
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
@@ -268,7 +332,13 @@ app = FastAPI(title="Scoreboard Injector", lifespan=lifespan)
|
|||||||
# API Endpoints
|
# API Endpoints
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
async def health_check():
|
async def health_check():
|
||||||
return {"status": "ok", "timestamp": datetime.utcnow().isoformat()}
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"timestamp": datetime.utcnow().isoformat(),
|
||||||
|
"team_id": OUR_TEAM_ID,
|
||||||
|
"mode": "http_polling" if USE_HTTP_POLLING else "websocket",
|
||||||
|
"scoreboard_url": SCOREBOARD_API_URL if USE_HTTP_POLLING else SCOREBOARD_WS_URL
|
||||||
|
}
|
||||||
|
|
||||||
@app.get("/stats", dependencies=[Depends(verify_token)])
|
@app.get("/stats", dependencies=[Depends(verify_token)])
|
||||||
async def get_stats():
|
async def get_stats():
|
||||||
@@ -409,6 +479,56 @@ async def inject_test_attack(attacker_id: int, victim_id: int, service: str = "t
|
|||||||
await process_attack_event(test_event)
|
await process_attack_event(test_event)
|
||||||
return {"status": "injected", "event": test_event}
|
return {"status": "injected", "event": test_event}
|
||||||
|
|
||||||
|
@app.get("/debug/scoreboard", dependencies=[Depends(verify_token)])
|
||||||
|
async def debug_scoreboard():
|
||||||
|
"""Check if scoreboard is reachable and show raw data"""
|
||||||
|
results = {
|
||||||
|
"mode": "http_polling" if USE_HTTP_POLLING else "websocket",
|
||||||
|
"endpoints_tested": []
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
# Test WebSocket
|
||||||
|
results["websocket_url"] = SCOREBOARD_WS_URL
|
||||||
|
try:
|
||||||
|
async with session.ws_connect(SCOREBOARD_WS_URL, timeout=aiohttp.ClientTimeout(total=5)) as ws:
|
||||||
|
results["websocket_status"] = "reachable"
|
||||||
|
except Exception as e:
|
||||||
|
results["websocket_status"] = f"unreachable: {str(e)}"
|
||||||
|
|
||||||
|
# Test HTTP endpoints
|
||||||
|
results["http_api_url"] = SCOREBOARD_API_URL
|
||||||
|
endpoints = [
|
||||||
|
"/attacks",
|
||||||
|
"/scoreboard",
|
||||||
|
"/flag_stats",
|
||||||
|
"/teams",
|
||||||
|
"/events"
|
||||||
|
]
|
||||||
|
|
||||||
|
for endpoint in endpoints:
|
||||||
|
url = f"{SCOREBOARD_API_URL}{endpoint}"
|
||||||
|
try:
|
||||||
|
async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as resp:
|
||||||
|
data = await resp.text()
|
||||||
|
results["endpoints_tested"].append({
|
||||||
|
"url": url,
|
||||||
|
"status": resp.status,
|
||||||
|
"reachable": resp.status == 200,
|
||||||
|
"data_preview": data[:500] if resp.status == 200 else None
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
results["endpoints_tested"].append({
|
||||||
|
"url": url,
|
||||||
|
"reachable": False,
|
||||||
|
"error": str(e)
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
results["error"] = str(e)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
uvicorn.run(app, host="0.0.0.0", port=8002)
|
uvicorn.run(app, host="0.0.0.0", port=8002)
|
||||||
|
|||||||
Reference in New Issue
Block a user