This commit is contained in:
Oliver
2025-09-02 08:54:21 +02:00
parent 7d0f1967b3
commit 0ea18322be
11 changed files with 69 additions and 61 deletions

7
app/hardening Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
# DISABLE ROOT and PASSWORD LOGIN
rex "doas sh -c 'grep -q \"permit nopass 4server as root\" /etc/doas.d/4server.conf 2>/dev/null || echo \"permit nopass 4server as root\" | tee -a /etc/doas.d/4server.conf > /dev/null'"
rex "doas sh -c 'sed -i \"s/^#\?PasswordAuthentication.*/PasswordAuthentication no/\" /etc/ssh/sshd_config'"
rex "doas sh -c 'sed -i \"s/^#\?PasswordAuthentication.*/PasswordAuthentication no/\" /etc/ssh/sshd_config.d/50-cloud-init.conf'"
rex doas rc-service sshd restart

View File

@@ -1,2 +1,2 @@
API_KEY=4h6lDzAOVksuCqmhEB3
API_KEY=your-secret-api-key
HOSTNAME="dev"

1
app/host_vars/hosts Normal file
View File

@@ -0,0 +1 @@
dev

View File

@@ -3,7 +3,7 @@ template templates/hostname /etc/hostname
rex doas apk update
rex doas apk add bash doas openssh linux-lts
rex doas apk add bash doas rsync openssh linux-lts
### activate lts kerner
template templates/extlinux.conf /boot/extlinux.conf
@@ -12,7 +12,6 @@ rex doas chmod 644 /boot/extlinux.conf
# ass swap file ????
# ------ disable root user and login
rex doas mkdir -p /4server
rex doas chmod 777 /4server
@@ -63,16 +62,13 @@ rex doas chown 4server:4server /home/4server/.ssh/authorized_keys
rex doas usermod -p Ne82Vrx8QfUdNHvLgct 4server
rex doas passwd -u 4server
template templates/.profile /home/4server/.profile
template templates/etc/doas.d/4server.conf /etc/doas.d/4server.conf
rex doas mkdir -p /etc/doas.d
rex "doas sh -c 'grep -q \"permit nopass 4server as root\" /etc/doas.d/4server.conf 2>/dev/null || echo \"permit nopass 4server as root\" | tee -a /etc/doas.d/4server.conf > /dev/null'"
rex "doas sh -c 'sed -i \"s/^#\?PasswordAuthentication.*/PasswordAuthentication no/\" /etc/ssh/sshd_config'"
rex "doas sh -c 'sed -i \"s/^#\?PasswordAuthentication.*/PasswordAuthentication no/\" /etc/ssh/sshd_config.d/50-cloud-init.conf'"
rex doas rc-service sshd restart
rex doas rc-service nebula restart
rex doas reboot

View File

@@ -8,16 +8,19 @@ import sqlite3
import subprocess
import os
import uvicorn
from typing import Optional
from typing import Dict, Any, Optional
from datetime import datetime
import json
# Constants
# ---------------------- Constants ----------------------
DB_PATH = "/4server/data/contracts.db"
BIN_PATH = "/4server/sbin"
API_KEY = os.getenv("API_KEY", "your-secret-api-key")
VERSION = "API: 0.0.7"
# FastAPI app
# ---------------------- FastAPI App ----------------------
app = FastAPI()
api_key_header = APIKeyHeader(name="X-API-Key")
@@ -44,7 +47,7 @@ def init_db():
cursor.execute('''
CREATE TABLE IF NOT EXISTS containers (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
UUID CHAR(50),
UUID CHAR(50) UNIQUE,
email CHAR(100),
expires DATE,
tags TEXT,
@@ -65,27 +68,24 @@ def init_db():
def execute_db(query: str, params: tuple = (), fetch: bool = False):
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row # <-- Add this line
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute(query, params)
conn.commit()
data = cursor.fetchall() if fetch else None
conn.close()
if data and fetch:
return [dict(row) for row in data] # Convert each row to dict
return data
return [dict(row) for row in data]
return None
# ---------------------- Models ----------------------
class ContainerModel(BaseModel):
UUID: Optional[str] = None
UUID: str
email: Optional[str] = None
expires: Optional[str] = None
tags: Optional[str] = None
env: Optional[str] = None
env: Optional[Dict[str, Any]] = None
affiliate: Optional[str] = None
image: Optional[str] = None
history: Optional[str] = None
@@ -96,16 +96,8 @@ class ContainerModel(BaseModel):
bump: Optional[str] = None
class ContainerIDRequest(BaseModel):
container_id: Optional[str] = None
class UpdateContainerRequest(ContainerModel):
pass
class InfoContainerRequest(BaseModel):
container_id: Optional[str] = None
class UUIDRequest(BaseModel):
UUID: str
# ---------------------- Routes ----------------------
@@ -115,7 +107,9 @@ def redirect_to_odoo():
@app.post("/container/update", dependencies=[Depends(verify_api_key)])
def update_container(request: UpdateContainerRequest):
def update_container(request: ContainerModel):
env_str = json.dumps(request.env) if isinstance(request.env, dict) else request.env
existing = execute_db("SELECT * FROM containers WHERE UUID = ?", (request.UUID,), fetch=True)
if existing:
execute_db("""
@@ -123,7 +117,7 @@ def update_container(request: UpdateContainerRequest):
history=?, comment=?, domains=?, status=?, created=?, bump=?
WHERE UUID=?
""", (
request.email, request.expires, request.tags, request.env, request.affiliate,
request.email, request.expires, request.tags, env_str, request.affiliate,
request.image, request.history, request.comment, request.domains, request.status,
request.created, request.bump, request.UUID
))
@@ -133,7 +127,7 @@ def update_container(request: UpdateContainerRequest):
comment, domains, status, created, bump)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
request.UUID, request.email, request.expires, request.tags, request.env,
request.UUID, request.email, request.expires, request.tags, env_str,
request.affiliate, request.image, request.history, request.comment,
request.domains, request.status, request.created, request.bump
))
@@ -141,44 +135,44 @@ def update_container(request: UpdateContainerRequest):
@app.post("/container/start", dependencies=[Depends(verify_api_key)])
def start_container(request: ContainerIDRequest):
return {"message": run_command([f"{BIN_PATH}/startContainer", request.container_id])}
def start_container(request: UUIDRequest):
return {"message": run_command([f"{BIN_PATH}/startContainer", request.UUID])}
@app.post("/container/stop", dependencies=[Depends(verify_api_key)])
def stop_container(request: ContainerIDRequest):
return {"message": run_command([f"{BIN_PATH}/stopContainer", request.container_id])}
def stop_container(request: UUIDRequest):
return {"message": run_command([f"{BIN_PATH}/stopContainer", request.UUID])}
@app.post("/container/nuke", dependencies=[Depends(verify_api_key)])
def nuke_container(request: ContainerIDRequest):
status = execute_db("SELECT status FROM containers WHERE UUID=?", (request.container_id,), fetch=True)
if not status or status[0][0] != "nuke":
def nuke_container(request: UUIDRequest):
status = execute_db("SELECT status FROM containers WHERE UUID=?", (request.UUID,), fetch=True)
if not status or status[0]["status"] != "nuke":
raise HTTPException(400, "Container status is not 'nuke'")
return {"message": run_command([f"{BIN_PATH}/nukeContainer", request.container_id])}
return {"message": run_command([f"{BIN_PATH}/nukeContainer", request.UUID])}
@app.post("/container/info", dependencies=[Depends(verify_api_key)])
def info_container(request: InfoContainerRequest):
if request.container_id:
rows = execute_db("SELECT * FROM containers WHERE UUID=?", (request.container_id,), fetch=True)
def info_container(request: Optional[UUIDRequest] = None):
if request:
rows = execute_db("SELECT * FROM containers WHERE UUID=?", (request.UUID,), fetch=True)
else:
rows = execute_db("SELECT * FROM containers", fetch=True)
return {"containers": rows}
@app.post("/container/bump", dependencies=[Depends(verify_api_key)])
def bump_container(request: ContainerIDRequest):
def bump_container(request: UUIDRequest):
today = datetime.utcnow().strftime("%Y-%m-%d")
execute_db("UPDATE containers SET bump=? WHERE UUID=?", (today, request.container_id))
msg = run_command([f"{BIN_PATH}/bumpContainer", request.container_id])
execute_db("UPDATE containers SET bump=? WHERE UUID=?", (today, request.UUID))
msg = run_command([f"{BIN_PATH}/bumpContainer", request.UUID])
return {"message": msg, "bump_date": today}
@app.post("/container/quota", dependencies=[Depends(verify_api_key)])
def container_quota(request: ContainerIDRequest):
def container_quota(request: UUIDRequest):
output = run_command([
"docker", "stats", request.container_id, "--no-stream",
"docker", "stats", request.UUID, "--no-stream",
"--format", "{{.MemUsage}},{{.BlockIO}}"
])
mem_usage, disk_usage = output.split(",")
@@ -202,7 +196,7 @@ def get_system_info():
try:
alpine_version = None
last_update = None
bump_dates = execute_db("SELECT MAX(bump) FROM containers", fetch=True)[0][0]
bump_dates = execute_db("SELECT MAX(bump) AS latest_bump FROM containers", fetch=True)[0]["latest_bump"]
if os.path.exists("/4server/data/update"):
with open("/4server/data/update") as f:
last_update = f.read().strip()
@@ -238,3 +232,4 @@ if __name__ == "__main__":
init_db()
uvicorn.run(app, host="10.5.0.1", port=8888)

View File

@@ -28,16 +28,16 @@ docker run -d \
--cap-add=SYS_ADMIN \
--security-opt seccomp=unconfined \
--restart=always \
-e N8N_HOST="${UUID}.od8n.com" \
-e N8N_HOST="${UUID}.odoo4projects.com" \
-e N8N_PORT=5678 \
-e N8N_PROTOCOL=https \
-e NODE_ENV=production \
-e WEBHOOK_URL="https://${UUID}.od8n.com/" \
-e WEBHOOK_URL="https://${UUID}.odoo4projects.com/" \
-e GENERIC_TIMEZONE="UTC-3" \
-v "/4server/data/${UUID}/n8n:/home/node/.n8n" \
-v "/4server/data/${UUID}/data:/data" \
--label "traefik.enable=true" \
--label "traefik.http.routers.${UUID}.rule=Host(\`${UUID}.od8n.com\`)" \
--label "traefik.http.routers.${UUID}.rule=Host(\`${UUID}.odoo4projects.com\`)" \
--label "traefik.http.routers.${UUID}.entrypoints=web,websecure" \
--label "traefik.http.routers.${UUID}.tls=true" \
--label "traefik.http.routers.${UUID}.tls.certresolver=production" \

View File

@@ -16,7 +16,9 @@ if [[ -z "$UUID" ]]; then
fi
while IFS="=" read -r key value; do
export "$key=$value"
if [ -n "$key" ]; then
export "$key=$value"
fi
done < <(sqlite3 "$DB_PATH" "
SELECT 'UUID=' || UUID FROM containers WHERE UUID='$UUID'
UNION ALL SELECT 'EMAIL=' || email FROM containers WHERE UUID='$UUID'
@@ -37,8 +39,7 @@ done < <(sqlite3 "$DB_PATH" "
# Debug: print loaded environment variables
env | grep -E 'UUID|EMAIL|EXPIRES|TAGS|ENV|AFFILIATE|IMAGE|HISTORY|COMMENT|DOMAINS|STATUS|CREATED|BUMP'
echo "UUID ${UUID}"
eval $(echo "$ENV" | jq -r 'to_entries | .[] | "export \(.key)=\(.value)"')
# Extract the second part of UUID (split by "-")
SECOND_PART=$(echo "$UUID" | cut -d'-' -f2)
@@ -59,5 +60,3 @@ case "$SECOND_PART" in
exit 2
;;
esac

View File

@@ -0,0 +1 @@
permit nopass 4server as root

View File

@@ -5,8 +5,8 @@
# ========= CONFIG =========
API_KEY="your-secret-api-key"
BASE_URL="https://dev.local"
CONTAINER_ID="001-001-123e4567-e89b-12d3-a456-426614174000" # sample UUID
CONTAINER_ID="001-002-123e4567-e89b-12d3-a456-426614174000" # sample UUID
BASE_URL="https://dev.odoo4projects.com"
# ==========================
# --- Functions for each endpoint ---
@@ -28,7 +28,11 @@ update_container() {
"tags": "N8N, SQLITE",
"affiliate": "OLIVER",
"domains": "N8N.local",
"status":"hot"
"status":"hot",
"env": {
"p": "123",
"BRANCH": "release"
}
}'
}

Binary file not shown.