fixes
This commit is contained in:
@@ -14,3 +14,8 @@ Host manchester
|
|||||||
User 4server
|
User 4server
|
||||||
IdentityFile /app/host_vars/manchester/manchester
|
IdentityFile /app/host_vars/manchester/manchester
|
||||||
|
|
||||||
|
Host boston
|
||||||
|
Hostname 192.168.9.16
|
||||||
|
User 4server
|
||||||
|
IdentityFile /app/host_vars/boston/boston
|
||||||
|
|
||||||
|
|||||||
7
app/hardening
Executable file
7
app/hardening
Executable 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
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
API_KEY=4h6lDzAOVksuCqmhEB3
|
API_KEY=your-secret-api-key
|
||||||
HOSTNAME="dev"
|
HOSTNAME="dev"
|
||||||
|
|||||||
1
app/host_vars/hosts
Normal file
1
app/host_vars/hosts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
dev
|
||||||
@@ -3,7 +3,7 @@ template templates/hostname /etc/hostname
|
|||||||
|
|
||||||
|
|
||||||
rex doas apk update
|
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
|
### activate lts kerner
|
||||||
template templates/extlinux.conf /boot/extlinux.conf
|
template templates/extlinux.conf /boot/extlinux.conf
|
||||||
@@ -12,7 +12,6 @@ rex doas chmod 644 /boot/extlinux.conf
|
|||||||
|
|
||||||
|
|
||||||
# ass swap file ????
|
# ass swap file ????
|
||||||
# ------ disable root user and login
|
|
||||||
|
|
||||||
rex doas mkdir -p /4server
|
rex doas mkdir -p /4server
|
||||||
rex doas chmod 777 /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 usermod -p Ne82Vrx8QfUdNHvLgct 4server
|
||||||
rex doas passwd -u 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 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 sshd restart
|
||||||
rex doas rc-service nebula restart
|
rex doas rc-service nebula restart
|
||||||
rex doas reboot
|
rex doas reboot
|
||||||
|
|||||||
79
app/sbin/api
79
app/sbin/api
@@ -8,16 +8,19 @@ import sqlite3
|
|||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from typing import Optional
|
from typing import Dict, Any, Optional
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
# Constants
|
|
||||||
|
|
||||||
|
# ---------------------- Constants ----------------------
|
||||||
DB_PATH = "/4server/data/contracts.db"
|
DB_PATH = "/4server/data/contracts.db"
|
||||||
BIN_PATH = "/4server/sbin"
|
BIN_PATH = "/4server/sbin"
|
||||||
API_KEY = os.getenv("API_KEY", "your-secret-api-key")
|
API_KEY = os.getenv("API_KEY", "your-secret-api-key")
|
||||||
VERSION = "API: 0.0.7"
|
VERSION = "API: 0.0.7"
|
||||||
|
|
||||||
# FastAPI app
|
# ---------------------- FastAPI App ----------------------
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
api_key_header = APIKeyHeader(name="X-API-Key")
|
api_key_header = APIKeyHeader(name="X-API-Key")
|
||||||
|
|
||||||
@@ -44,7 +47,7 @@ def init_db():
|
|||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS containers (
|
CREATE TABLE IF NOT EXISTS containers (
|
||||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
UUID CHAR(50),
|
UUID CHAR(50) UNIQUE,
|
||||||
email CHAR(100),
|
email CHAR(100),
|
||||||
expires DATE,
|
expires DATE,
|
||||||
tags TEXT,
|
tags TEXT,
|
||||||
@@ -65,27 +68,24 @@ def init_db():
|
|||||||
|
|
||||||
def execute_db(query: str, params: tuple = (), fetch: bool = False):
|
def execute_db(query: str, params: tuple = (), fetch: bool = False):
|
||||||
conn = sqlite3.connect(DB_PATH)
|
conn = sqlite3.connect(DB_PATH)
|
||||||
conn.row_factory = sqlite3.Row # <-- Add this line
|
conn.row_factory = sqlite3.Row
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute(query, params)
|
cursor.execute(query, params)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
data = cursor.fetchall() if fetch else None
|
data = cursor.fetchall() if fetch else None
|
||||||
conn.close()
|
conn.close()
|
||||||
if data and fetch:
|
if data and fetch:
|
||||||
return [dict(row) for row in data] # Convert each row to dict
|
return [dict(row) for row in data]
|
||||||
return data
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------- Models ----------------------
|
# ---------------------- Models ----------------------
|
||||||
class ContainerModel(BaseModel):
|
class ContainerModel(BaseModel):
|
||||||
UUID: Optional[str] = None
|
UUID: str
|
||||||
email: Optional[str] = None
|
email: Optional[str] = None
|
||||||
expires: Optional[str] = None
|
expires: Optional[str] = None
|
||||||
tags: Optional[str] = None
|
tags: Optional[str] = None
|
||||||
env: Optional[str] = None
|
env: Optional[Dict[str, Any]] = None
|
||||||
affiliate: Optional[str] = None
|
affiliate: Optional[str] = None
|
||||||
image: Optional[str] = None
|
image: Optional[str] = None
|
||||||
history: Optional[str] = None
|
history: Optional[str] = None
|
||||||
@@ -96,16 +96,8 @@ class ContainerModel(BaseModel):
|
|||||||
bump: Optional[str] = None
|
bump: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class ContainerIDRequest(BaseModel):
|
class UUIDRequest(BaseModel):
|
||||||
container_id: Optional[str] = None
|
UUID: str
|
||||||
|
|
||||||
|
|
||||||
class UpdateContainerRequest(ContainerModel):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class InfoContainerRequest(BaseModel):
|
|
||||||
container_id: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------- Routes ----------------------
|
# ---------------------- Routes ----------------------
|
||||||
@@ -115,7 +107,9 @@ def redirect_to_odoo():
|
|||||||
|
|
||||||
|
|
||||||
@app.post("/container/update", dependencies=[Depends(verify_api_key)])
|
@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)
|
existing = execute_db("SELECT * FROM containers WHERE UUID = ?", (request.UUID,), fetch=True)
|
||||||
if existing:
|
if existing:
|
||||||
execute_db("""
|
execute_db("""
|
||||||
@@ -123,7 +117,7 @@ def update_container(request: UpdateContainerRequest):
|
|||||||
history=?, comment=?, domains=?, status=?, created=?, bump=?
|
history=?, comment=?, domains=?, status=?, created=?, bump=?
|
||||||
WHERE UUID=?
|
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.image, request.history, request.comment, request.domains, request.status,
|
||||||
request.created, request.bump, request.UUID
|
request.created, request.bump, request.UUID
|
||||||
))
|
))
|
||||||
@@ -133,7 +127,7 @@ def update_container(request: UpdateContainerRequest):
|
|||||||
comment, domains, status, created, bump)
|
comment, domains, status, created, bump)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
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.affiliate, request.image, request.history, request.comment,
|
||||||
request.domains, request.status, request.created, request.bump
|
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)])
|
@app.post("/container/start", dependencies=[Depends(verify_api_key)])
|
||||||
def start_container(request: ContainerIDRequest):
|
def start_container(request: UUIDRequest):
|
||||||
return {"message": run_command([f"{BIN_PATH}/startContainer", request.container_id])}
|
return {"message": run_command([f"{BIN_PATH}/startContainer", request.UUID])}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/container/stop", dependencies=[Depends(verify_api_key)])
|
@app.post("/container/stop", dependencies=[Depends(verify_api_key)])
|
||||||
def stop_container(request: ContainerIDRequest):
|
def stop_container(request: UUIDRequest):
|
||||||
return {"message": run_command([f"{BIN_PATH}/stopContainer", request.container_id])}
|
return {"message": run_command([f"{BIN_PATH}/stopContainer", request.UUID])}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/container/nuke", dependencies=[Depends(verify_api_key)])
|
@app.post("/container/nuke", dependencies=[Depends(verify_api_key)])
|
||||||
def nuke_container(request: ContainerIDRequest):
|
def nuke_container(request: UUIDRequest):
|
||||||
status = execute_db("SELECT status FROM containers WHERE UUID=?", (request.container_id,), fetch=True)
|
status = execute_db("SELECT status FROM containers WHERE UUID=?", (request.UUID,), fetch=True)
|
||||||
if not status or status[0][0] != "nuke":
|
if not status or status[0]["status"] != "nuke":
|
||||||
raise HTTPException(400, "Container status is not '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)])
|
@app.post("/container/info", dependencies=[Depends(verify_api_key)])
|
||||||
def info_container(request: InfoContainerRequest):
|
def info_container(request: Optional[UUIDRequest] = None):
|
||||||
if request.container_id:
|
if request:
|
||||||
rows = execute_db("SELECT * FROM containers WHERE UUID=?", (request.container_id,), fetch=True)
|
rows = execute_db("SELECT * FROM containers WHERE UUID=?", (request.UUID,), fetch=True)
|
||||||
else:
|
else:
|
||||||
rows = execute_db("SELECT * FROM containers", fetch=True)
|
rows = execute_db("SELECT * FROM containers", fetch=True)
|
||||||
return {"containers": rows}
|
return {"containers": rows}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/container/bump", dependencies=[Depends(verify_api_key)])
|
@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")
|
today = datetime.utcnow().strftime("%Y-%m-%d")
|
||||||
execute_db("UPDATE containers SET bump=? WHERE UUID=?", (today, request.container_id))
|
execute_db("UPDATE containers SET bump=? WHERE UUID=?", (today, request.UUID))
|
||||||
msg = run_command([f"{BIN_PATH}/bumpContainer", request.container_id])
|
msg = run_command([f"{BIN_PATH}/bumpContainer", request.UUID])
|
||||||
return {"message": msg, "bump_date": today}
|
return {"message": msg, "bump_date": today}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/container/quota", dependencies=[Depends(verify_api_key)])
|
@app.post("/container/quota", dependencies=[Depends(verify_api_key)])
|
||||||
def container_quota(request: ContainerIDRequest):
|
def container_quota(request: UUIDRequest):
|
||||||
output = run_command([
|
output = run_command([
|
||||||
"docker", "stats", request.container_id, "--no-stream",
|
"docker", "stats", request.UUID, "--no-stream",
|
||||||
"--format", "{{.MemUsage}},{{.BlockIO}}"
|
"--format", "{{.MemUsage}},{{.BlockIO}}"
|
||||||
])
|
])
|
||||||
mem_usage, disk_usage = output.split(",")
|
mem_usage, disk_usage = output.split(",")
|
||||||
@@ -202,7 +196,7 @@ def get_system_info():
|
|||||||
try:
|
try:
|
||||||
alpine_version = None
|
alpine_version = None
|
||||||
last_update = 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"):
|
if os.path.exists("/4server/data/update"):
|
||||||
with open("/4server/data/update") as f:
|
with open("/4server/data/update") as f:
|
||||||
last_update = f.read().strip()
|
last_update = f.read().strip()
|
||||||
@@ -238,3 +232,4 @@ if __name__ == "__main__":
|
|||||||
init_db()
|
init_db()
|
||||||
uvicorn.run(app, host="10.5.0.1", port=8888)
|
uvicorn.run(app, host="10.5.0.1", port=8888)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,16 +28,16 @@ docker run -d \
|
|||||||
--cap-add=SYS_ADMIN \
|
--cap-add=SYS_ADMIN \
|
||||||
--security-opt seccomp=unconfined \
|
--security-opt seccomp=unconfined \
|
||||||
--restart=always \
|
--restart=always \
|
||||||
-e N8N_HOST="${UUID}.od8n.com" \
|
-e N8N_HOST="${UUID}.odoo4projects.com" \
|
||||||
-e N8N_PORT=5678 \
|
-e N8N_PORT=5678 \
|
||||||
-e N8N_PROTOCOL=https \
|
-e N8N_PROTOCOL=https \
|
||||||
-e NODE_ENV=production \
|
-e NODE_ENV=production \
|
||||||
-e WEBHOOK_URL="https://${UUID}.od8n.com/" \
|
-e WEBHOOK_URL="https://${UUID}.odoo4projects.com/" \
|
||||||
-e GENERIC_TIMEZONE="UTC-3" \
|
-e GENERIC_TIMEZONE="UTC-3" \
|
||||||
-v "/4server/data/${UUID}/n8n:/home/node/.n8n" \
|
-v "/4server/data/${UUID}/n8n:/home/node/.n8n" \
|
||||||
-v "/4server/data/${UUID}/data:/data" \
|
-v "/4server/data/${UUID}/data:/data" \
|
||||||
--label "traefik.enable=true" \
|
--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}.entrypoints=web,websecure" \
|
||||||
--label "traefik.http.routers.${UUID}.tls=true" \
|
--label "traefik.http.routers.${UUID}.tls=true" \
|
||||||
--label "traefik.http.routers.${UUID}.tls.certresolver=production" \
|
--label "traefik.http.routers.${UUID}.tls.certresolver=production" \
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ if [[ -z "$UUID" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
while IFS="=" read -r key value; do
|
while IFS="=" read -r key value; do
|
||||||
|
if [ -n "$key" ]; then
|
||||||
export "$key=$value"
|
export "$key=$value"
|
||||||
|
fi
|
||||||
done < <(sqlite3 "$DB_PATH" "
|
done < <(sqlite3 "$DB_PATH" "
|
||||||
SELECT 'UUID=' || UUID FROM containers WHERE UUID='$UUID'
|
SELECT 'UUID=' || UUID FROM containers WHERE UUID='$UUID'
|
||||||
UNION ALL SELECT 'EMAIL=' || email 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
|
# Debug: print loaded environment variables
|
||||||
env | grep -E 'UUID|EMAIL|EXPIRES|TAGS|ENV|AFFILIATE|IMAGE|HISTORY|COMMENT|DOMAINS|STATUS|CREATED|BUMP'
|
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 "-")
|
# Extract the second part of UUID (split by "-")
|
||||||
SECOND_PART=$(echo "$UUID" | cut -d'-' -f2)
|
SECOND_PART=$(echo "$UUID" | cut -d'-' -f2)
|
||||||
@@ -59,5 +60,3 @@ case "$SECOND_PART" in
|
|||||||
exit 2
|
exit 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
app/templates/etc/doas.d/4server.conf
Normal file
1
app/templates/etc/doas.d/4server.conf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
permit nopass 4server as root
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
# ========= CONFIG =========
|
# ========= CONFIG =========
|
||||||
API_KEY="your-secret-api-key"
|
API_KEY="your-secret-api-key"
|
||||||
BASE_URL="https://dev.local"
|
CONTAINER_ID="001-002-123e4567-e89b-12d3-a456-426614174000" # sample UUID
|
||||||
CONTAINER_ID="001-001-123e4567-e89b-12d3-a456-426614174000" # sample UUID
|
BASE_URL="https://dev.odoo4projects.com"
|
||||||
# ==========================
|
# ==========================
|
||||||
|
|
||||||
# --- Functions for each endpoint ---
|
# --- Functions for each endpoint ---
|
||||||
@@ -28,7 +28,11 @@ update_container() {
|
|||||||
"tags": "N8N, SQLITE",
|
"tags": "N8N, SQLITE",
|
||||||
"affiliate": "OLIVER",
|
"affiliate": "OLIVER",
|
||||||
"domains": "N8N.local",
|
"domains": "N8N.local",
|
||||||
"status":"hot"
|
"status":"hot",
|
||||||
|
"env": {
|
||||||
|
"p": "123",
|
||||||
|
"BRANCH": "release"
|
||||||
|
}
|
||||||
}'
|
}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user