diff --git a/4server b/4server new file mode 100644 index 0000000..c11d89c Binary files /dev/null and b/4server differ diff --git a/alpine/.bashrc b/alpine/.bashrc index fba8c0e..65156ee 100644 --- a/alpine/.bashrc +++ b/alpine/.bashrc @@ -12,3 +12,4 @@ set_prod() { export HOSTS_FILE="/app/hosts.all" echo "HOSTS_FILE set to: $HOSTS_FILE" } + diff --git a/alpine/Dockerfile b/alpine/Dockerfile index 6968989..048e455 100644 --- a/alpine/Dockerfile +++ b/alpine/Dockerfile @@ -28,7 +28,7 @@ RUN ssh-keygen -t rsa -b 4096 -f /root/.ssh/id_rsa -N "" && \ chmod 600 /root/.ssh/id_rsa && \ chmod 600 /root/.ssh/config -COPY ass /usr/bin/ +COPY rex /usr/bin/ COPY template /usr/bin/ COPY dpush /usr/bin/ COPY create_volume /usr/bin/ diff --git a/alpine/config b/alpine/config index fb36c60..1b40fe8 100644 --- a/alpine/config +++ b/alpine/config @@ -5,21 +5,21 @@ Host dev Host saopaulo - Hostname 192.168.9.11 + Hostname saopaulo User ansible IdentityFile /mnt/encrypted_volume/.ssh/saopaulo Host mumbai - Hostname 192.168.9.17 + Hostname mumbai User ansible IdentityFile /mnt/encrypted_volume/.ssh/mumbai Host london - Hostname 192.168.9.15 + Hostname london User ansible IdentityFile /mnt/encrypted_volume/.ssh/london Host boston - Hostname 192.168.9.16 + Hostname boston User ansible IdentityFile /mnt/encrypted_volume/.ssh/boston diff --git a/alpine/create_volume b/alpine/create_volume index 5dafe54..853b931 100755 --- a/alpine/create_volume +++ b/alpine/create_volume @@ -10,7 +10,8 @@ fi FILE="/data/$1" MAPPER_NAME="encrypted_volume" MOUNT_POINT="/mnt/${MAPPER_NAME}" -dd if=/dev/zero of="$FILE" bs=1M count=100 +dd if=/dev/zero of="$FILE" bs=1M count=10 + echo "Setting up LUKS on $FILE..." cryptsetup luksFormat "$FILE" diff --git a/alpine/hosts b/alpine/hosts new file mode 100644 index 0000000..1e5b66a --- /dev/null +++ b/alpine/hosts @@ -0,0 +1,9 @@ +127.0.0.1 dev +::1 dev + +192.168.111.209 dev + +192.168.9.11 saopaulo +192.168.9.17 mumbai +192.168.9.15 london +192.168.9.16 boston diff --git a/alpine/ass b/alpine/rex similarity index 100% rename from alpine/ass rename to alpine/rex diff --git a/app/etc/.bashrc b/app/etc/.bashrc new file mode 100644 index 0000000..65156ee --- /dev/null +++ b/app/etc/.bashrc @@ -0,0 +1,15 @@ +# ~/.bashrc + +echo "command: mount_volume " +echo "alias: set_prod" + +export hosts_file="/app/hosts.dev" + +export PS1="\[\e[32m\]\h:\w\$\[\e[0m\] " +df -h . + +set_prod() { + export HOSTS_FILE="/app/hosts.all" + echo "HOSTS_FILE set to: $HOSTS_FILE" +} + diff --git a/app/templates/od8n-api b/app/etc/init.d/4server-api.init.j2 similarity index 62% rename from app/templates/od8n-api rename to app/etc/init.d/4server-api.init.j2 index eaa1d0d..50ad3fb 100644 --- a/app/templates/od8n-api +++ b/app/etc/init.d/4server-api.init.j2 @@ -1,9 +1,9 @@ #!/sbin/openrc-run -name="od8n-api" -description="OD8N API Service" +name="4server-api" +description="4server API Service" -command="/OD8N/sbin/api" +command="/4server/sbin/api" command_args="" pidfile="/run/${RC_SVCNAME}.pid" command_background="yes" @@ -13,8 +13,8 @@ if [ -f /etc/od8n ]; then export $(cut -d= -f1 /etc/od8n) fi -output_log="/OD8N/data/api.log" -error_log="/OD8N/data/api.log" +output_log="/4server/data/api.log" +error_log="/4server/data/api.log" depend() { need net diff --git a/app/nebula/config.yml b/app/nebula/config.yml new file mode 100644 index 0000000..828f944 --- /dev/null +++ b/app/nebula/config.yml @@ -0,0 +1,87 @@ +pki: + ca: /etc/nebula/ca.crt + cert: /etc/nebula/{{ hostname }}.crt + key: /etc/nebula/{{ hostname }}.key +static_host_map: + "192.168.9.1": ["167.71.79.60:4242"] +lighthouse: + # am_lighthouse is used to enable lighthouse functionality for a node. This should ONLY be true on nodes + # you have configured to be lighthouses in your network + am_lighthouse: false + interval: 60 + hosts: +listen: + host: 0.0.0.0 + port: 4242 +punchy: + punch: true + +relay: + am_relay: false + use_relays: true + +tun: + disabled: false + dev: nebula1 + drop_local_broadcast: false + drop_multicast: false + tx_queue: 500 + mtu: 1300 + routes: + #- mtu: 8800 + # route: 10.0.0.0/16 + + unsafe_routes: +logging: + level: info + format: text +firewall: + outbound_action: drop + inbound_action: drop + + conntrack: + tcp_timeout: 12m + udp_timeout: 3m + default_timeout: 10m + + outbound: + - port: any + proto: any + host: any + + inbound: + - port: any + proto: icmp + host: any + + - port: 22 #GIT + proto: tcp + groups: + - admin + - ansible + + - port: 8080 + proto: tcp + groups: + -admin + + + - port: 3001 + proto: tcp + groups: + -admin + + - port: 8080 + proto: tcp + groups: + -admin + + + + + + + + + + diff --git a/app/nebula/nebula b/app/nebula/nebula new file mode 100755 index 0000000..615b717 Binary files /dev/null and b/app/nebula/nebula differ diff --git a/app/nebula/nebula-cert b/app/nebula/nebula-cert new file mode 100755 index 0000000..78f7ad9 Binary files /dev/null and b/app/nebula/nebula-cert differ diff --git a/app/sbin/api b/app/sbin/api new file mode 100755 index 0000000..29a6cd8 --- /dev/null +++ b/app/sbin/api @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 + +from fastapi import FastAPI, HTTPException, Depends, Response +from fastapi.security.api_key import APIKeyHeader +from fastapi.responses import RedirectResponse +from pydantic import BaseModel +import psutil +import sqlite3 +import subprocess +import os +import sys +import uvicorn +from typing import Optional + +# Constants +DB_PATH = "/4server/data/contracts/contracts.db" +BIN_PATH = "/4server/sbin" +API_KEY = os.getenv("API_KEY", "your-secret-api-key") + +# FastAPI app +app = FastAPI() + +# Security +api_key_header = APIKeyHeader(name="X-API-Key") + + +def verify_api_key(key: str = Depends(api_key_header)): + if key != API_KEY: + raise HTTPException(status_code=403, detail="Unauthorized") + + +# ---------------------- Database ---------------------- +def init_db(): + """Initialize the database with containers table.""" + os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute(''' + CREATE TABLE IF NOT EXISTS containers ( + ID INTEGER PRIMARY KEY AUTOINCREMENT, + UUID CHAR(36), + location CHAR(100), + email CHAR(100), + expires DATE, + tags TEXT, + env TEXT + ) + ''') + conn.commit() + conn.close() + + +# ---------------------- Models ---------------------- +class ContainerModel(BaseModel): + UUID: str + location: str + email: str + expires: str + tags: Optional[str] = None + env: Optional[str] = None + + +class StartContainerRequest(BaseModel): + uuid: str + email: str + + +# ---------------------- Routes ---------------------- +@app.get("/", include_in_schema=False) +def redirect_to_odoo(): + return RedirectResponse(url="https://ODOO4PROJECTS.com") + + +@app.post("/startContainer", dependencies=[Depends(verify_api_key)]) +def start_container(request: StartContainerRequest): + try: + result = subprocess.run( + [os.path.join(BIN_PATH, "startContainer"), request.uuid, request.email], + capture_output=True, + text=True, + check=True + ) + return {"status": "success", "output": result.stdout} + except subprocess.CalledProcessError as e: + print(f"Error in /startContainer: {e.stderr}", file=sys.stderr) + raise HTTPException(status_code=500, detail=f"Command failed: {e.stderr}") + + +@app.get("/system", dependencies=[Depends(verify_api_key)]) +def get_system_info(): + try: + with open("/etc/alpine-release") as f: + version = f.read().strip() + return {"alpine_version": version} + except FileNotFoundError: + raise HTTPException(status_code=404, detail="File not found. Press play on tape") + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@app.get("/resources", dependencies=[Depends(verify_api_key)]) +def get_resources(): + mem = psutil.virtual_memory() + disk = psutil.disk_usage("/") + return { + "memory": {"total": mem.total, "available": mem.available, "used": mem.used}, + "disk": {"total": disk.total, "used": disk.used, "free": disk.free}, + "cpu_count": psutil.cpu_count(logical=True), + } + + +@app.get("/containers", dependencies=[Depends(verify_api_key)]) +def get_containers(): + result = subprocess.run([BIN_PATH+'/getContainers'], capture_output=True, text=True) + if result.returncode != 0: + return Response(content='{"error": "Script failed"}', media_type="application/json", status_code=500) + return Response(content=result.stdout, media_type="application/json") + + +@app.post("/container", dependencies=[Depends(verify_api_key)]) +def upsert_container(container: ContainerModel): + try: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + cursor.execute("SELECT 1 FROM containers WHERE UUID = ?", (container.UUID,)) + exists = cursor.fetchone() + + if exists: + cursor.execute(""" + UPDATE containers SET + location = ?, email = ?, expires = ?, tags = ?, env = ? + WHERE UUID = ? + """, ( + container.location, container.email, container.expires, + container.tags, container.env, container.UUID + )) + operation = "update" + else: + cursor.execute(""" + INSERT INTO containers (UUID, location, email, expires, tags, env) + VALUES (?, ?, ?, ?, ?, ?) + """, ( + container.UUID, container.location, container.email, + container.expires, container.tags, container.env + )) + operation = "insert" + + conn.commit() + return {"status": "success", "operation": operation} + except Exception as e: + print(f"Error in /container: {e}", file=sys.stderr) + raise HTTPException(status_code=500, detail=str(e)) + finally: + conn.close() + + +# ---------------------- Entry Point ---------------------- +if __name__ == "__main__": + print("Version 0.1") + init_db() + uvicorn.run(app, host="10.5.0.1", port=8888) + diff --git a/app/sbin/getContainers b/app/sbin/getContainers new file mode 100755 index 0000000..544032a --- /dev/null +++ b/app/sbin/getContainers @@ -0,0 +1,61 @@ +#!/bin/bash + +# Collect running container IDs +container_ids=$(docker ps -q) + +# Start JSON array +echo "[" + +first=true + +for cid in $container_ids; do + # Get basic info + name=$(docker inspect --format '{{.Name}}' "$cid" | sed 's/^\/\(.*\)/\1/') + image=$(docker inspect --format '{{.Config.Image}}' "$cid") + container_id=$(docker inspect --format '{{.Id}}' "$cid") + + # RAM usage in bytes (from docker stats) + mem_usage=$(docker stats --no-stream --format "{{.MemUsage}}" "$cid") + # mem_usage looks like "12.34MiB / 1.944GiB" — extract the used part + mem_used=$(echo "$mem_usage" | awk '{print $1}') + + # Convert mem_used to MiB as a number + # Support KiB, MiB, GiB units + ram_usage=$(echo "$mem_used" | awk ' + { + val = substr($0, 1, length($0)-3) + unit = substr($0, length($0)-2, 3) + if (unit == "KiB") print val / 1024 + else if (unit == "MiB") print val + else if (unit == "GiB") print val * 1024 + else print 0 + }') + + # Traefik labels (extract labels JSON) + labels=$(docker inspect --format '{{json .Config.Labels}}' "$cid" | jq -r 'to_entries | map(select(.key | test("^traefik.*"))) | map({(.key): .value}) | add') + + # Comma between JSON objects + if [ "$first" = false ]; then + echo "," + fi + first=false + + # Output JSON object + jq -n \ + --arg name "$name" \ + --arg id "$container_id" \ + --arg image "$image" \ + --argjson ram "$ram_usage" \ + --argjson tags "$labels" \ + '{ + name: $name, + id: $id, + image: $image, + ram_mb: $ram, + traefik_tags: $tags + }' +done + +# End JSON array +echo "]" + diff --git a/app/sbin/startContainer b/app/sbin/startContainer new file mode 100755 index 0000000..b15fe13 --- /dev/null +++ b/app/sbin/startContainer @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +exec > /4server/data/startContainer.log 2>&1 + +echo "$(date '+%Y-%m-%d %H:%M') - startContainer $1" + + +CONTAINER_NAME="$1" + +# Get the hostname of the machine +HOSTNAME=$(hostname) + +mkdir -p /4server/data/${CONTAINER_NAME}/n8n +mkdir -p /4server/data/${CONTAINER_NAME}/data + +sudo chmod 777 /4server/data/${CONTAINER_NAME}/n8n +sudo chmod 777 /4server/data/${CONTAINER_NAME}/data + + +# Stop the container if it exists +if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + echo "$(date '+%Y-%m-%d %H:%M') - stopping existing container $CONTAINER_NAME" + docker stop "$CONTAINER_NAME" + docker rm "$CONTAINER_NAME" +fi + + + +docker run -d \ + --name "$CONTAINER_NAME" \ + -p 5678 \ + --cap-add=SYS_ADMIN \ + --security-opt seccomp=unconfined \ + --restart=always \ + -e N8N_HOST="${CONTAINER_NAME}.od8n.com" \ + -e N8N_PORT=5678 \ + -e N8N_PROTOCOL=https \ + -e NODE_ENV=production \ + -e WEBHOOK_URL="https://${CONTAINER_NAME}.od8n.com/" \ + -e GENERIC_TIMEZONE="UTC-3" \ + -v "/4server/data/${CONTAINER_NAME}/n8n:/home/node/.n8n" \ + -v "/4server/data/${CONTAINER_NAME}/data:/data" \ + --label "traefik.enable=true" \ + --label "traefik.http.routers.${CONTAINER_NAME}.rule=Host(\`${CONTAINER_NAME}.od8n.com\`)" \ + --label "traefik.http.routers.${CONTAINER_NAME}.entrypoints=web,websecure" \ + --label "traefik.http.routers.${CONTAINER_NAME}.tls=true" \ + --label "traefik.http.routers.${CONTAINER_NAME}.tls.certresolver=production" \ + --label "traefik.http.services.${CONTAINER_NAME}.loadbalancer.server.port=5678" \ + --network docker-compose_4projects \ + n8nio/n8n:latest + + +echo "Done $1" diff --git a/app/templates/od8n b/app/templates/4server similarity index 100% rename from app/templates/od8n rename to app/templates/4server diff --git a/app/templates/4server-api b/app/templates/4server-api new file mode 100644 index 0000000..50ad3fb --- /dev/null +++ b/app/templates/4server-api @@ -0,0 +1,25 @@ +#!/sbin/openrc-run + +name="4server-api" +description="4server API Service" + +command="/4server/sbin/api" +command_args="" +pidfile="/run/${RC_SVCNAME}.pid" +command_background="yes" + +if [ -f /etc/od8n ]; then + . /etc/od8n + export $(cut -d= -f1 /etc/od8n) +fi + +output_log="/4server/data/api.log" +error_log="/4server/data/api.log" + +depend() { + need net + use logger dns + after firewall +} + + diff --git a/app/templates/docker-compose.yml b/app/templates/docker-compose.yml index 59503a8..15d9ad5 100644 --- a/app/templates/docker-compose.yml +++ b/app/templates/docker-compose.yml @@ -8,8 +8,8 @@ services: - POSTGRES_PASSWORD=deradmin - POSTGRES_USER=deradmin1 volumes: - - /OD8N/data/postgres:/var/lib/postgresql/data/ - - /OD8N/data/pg_backup/:/BACKUP/ + - /4server/data/postgres:/var/lib/postgresql/data/ + - /4server/data/pg_backup/:/BACKUP/ networks: 4projects: ipv4_address: 10.5.0.200 @@ -23,9 +23,9 @@ services: #- 8080:8080 volumes: - /run/docker.sock:/run/docker.sock:ro - - /OD8N/config/traefik/etc:/etc/traefik - - /OD8N/data/traefik/certs:/certs - - /OD8N/data/traefik/traefik-logs:/var/log/traefik + - /4server/config/traefik/etc:/etc/traefik + - /4server/data/traefik/certs:/certs + - /4server/data/traefik/traefik-logs:/var/log/traefik networks: - 4projects restart: unless-stopped diff --git a/app/templates/traefik.yaml b/app/templates/traefik.yaml index 06e9858..c8d0591 100644 --- a/app/templates/traefik.yaml +++ b/app/templates/traefik.yaml @@ -2,27 +2,14 @@ global: checkNewVersion: false sendAnonymousUsage: false -# -- (Optional) Change Log Level and Format here... -# - loglevels [DEBUG, INFO, WARNING, ERROR, CRITICAL] -# - format [common, json, logfmt] -# log: -# level: ERROR -# format: common -# filePath: /var/log/traefik/traefik.log - -# -- (Optional) Enable Accesslog and change Format here... -# - format [common, json, logfmt] accesslog: -# format: common filePath: /var/log/traefik/access.log -# -- (Optional) Enable API and Dashboard here, don't do in production api: dashboard: true disableDashboardAd: true insecure: true -# -- Change EntryPoints here... entryPoints: web: address: :80 @@ -97,5 +84,19 @@ http: trustForwardHeader: true + routers: + saopaulo-router: + rule: "Host(`dev.local`)" + service: saopaulo-service + entryPoints: + - websecure + tls: + certResolver: production + + services: + saopaulo-service: + loadBalancer: + servers: + - url: "http://10.5.0.1:8888" diff --git a/app/todo.txt b/app/todo.txt new file mode 100644 index 0000000..25bed3f --- /dev/null +++ b/app/todo.txt @@ -0,0 +1,3 @@ +create user +set bash as default user +