| Variable | Default | Description |
|----------|---------|-------------|
| `FRONTEND_DIR` | /app/frontend | Frontend directory |
-| `DB_FILE` | /data/database.db | SQLite file |
-| `DB_RESET` | false | Reset DB on every startup |
-| `LOG_LEVEL` | info | Log level |
-| `LOG_TO_FILE` | false | Enable file logging |
-| `LOG_FILE` | /data/app.log | Application log file |
-| `LOG_ACCESS_FILE` | /data/access.log | HTTP access log |
-| `DOMAIN` | example.com | Public domain |
-| `PUBLIC_IP` | 127.0.0.1 | Public IP |
-| `HTTP_PORT` | 8000 | Internal HTTP port |
-| `LOGIN_MAX_ATTEMPTS` | 5 | Login attempts |
-| `LOGIN_WINDOW_SECONDS` | 600 | Attempt window |
+| `DB_FILE` | /data/database.db | SQLite file |
+| `DB_RESET` | false | Reset DB on every startup |
+| `LOG_LEVEL` | info | Log level |
+| `LOG_TO_FILE` | false | Enable file logging |
+| `LOG_FILE` | /data/app.log | Application log file |
+| `LOG_ACCESS_FILE` | /data/access.log | HTTP access log |
+| `DOMAIN` | example.com | Public domain |
+| `PUBLIC_IP` | 127.0.0.1 | Public IP |
+| `HTTP_PORT` | 8000 | Internal HTTP port |
+| `LOGIN_MAX_ATTEMPTS` | 5 | Login attempts |
+| `LOGIN_WINDOW_SECONDS` | 600 | Attempt window |
| `ADMIN_USER` | admin | Admin username |
-| `ADMIN_PASSWORD` | admin | Admin password (development) |
+| `ADMIN_PASSWORD` | admin | Admin password (development) |
| `ADMIN_PASSWORD_HASH_FILE` | /run/secrets/admin_password_hash | Admin password hash |
| `SESSION_SECRET` | (auto-generated) | Session secret |
+| `DNS_CFG_PATH` | /dns/etc | Bind9 Configuration folder |
+| `DNS_HOST_FILE` | {DOMAIN}/hosts.inc | BIND9 Hosts file |
+| `DNS_ALIAS_FILE` | {DOMAIN}/alias.inc | BIND9 Alias file |
+| `DNS_REVERSE_FILE` | reverse/hosts.inc | BIND9 Reverse Hosts file |
---
# -----------------------------
def get_hosts():
conn = get_db()
- cur = conn.execute("SELECT * FROM hosts ORDER BY name")
- rows = cur.fetchall()
- return [dict(r) for r in rows]
+ cur = conn.execute("SELECT * FROM hosts")
+ rows = [dict(r) for r in cur.fetchall()]
+ # Sort by IPv4
+ rows.sort(key=lambda h: ipaddress.IPv4Address(h['ipv4']))
+ return rows
# -----------------------------
# SELECT SINGLE HOST
"Users: admin=%s | password=%s | hash=%s | hash_file=%s",
settings.ADMIN_USER, safe_admin_pwd, safe_admin_hash, settings.ADMIN_PASSWORD_HASH_FILE
)
+ logger.info(
+ "DNS: path=%s | host file=%s | alias file=%s | reverse file=%s",
+ settings.DNS_CFG_PATH, settings.DNS_HOST_FILE, settings.DNS_ALIAS_FILE, settings.DNS_REVERSE_FILE
+ )
# ------------------------------------------------------------------------------
# Shutdown log
# import standard modules
from fastapi import APIRouter, Request, Response
-from fastapi.responses import FileResponse, RedirectResponse
+from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
import asyncio
+import json
import os
import ipaddress
import time
-
+# Import local modules
+from backend.db.hosts import get_hosts
# Import Settings
from settings.settings import settings
async def apt_dns_reload(request: Request):
start_ns = time.monotonic_ns()
- await asyncio.sleep(0.2)
+ # Inizializzazioni
+ error = False
+ message = None
+ code = None
+ status = None
+
+ try:
+ # Get Hosts List
+ hosts = get_hosts()
+
+ # Save DNS Configuration
+ path = settings.DNS_HOST_FILE
+ with open(path, "w", encoding="utf-8") as f:
+ for h in hosts:
+ line = f"{h.get('name')}\t\t IN\tA\t{h.get('ipv4')}\n"
+ f.write(line)
+
+ path = settings.DNS_HOST_FILE + ".json"
+ with open(path, "w", encoding="utf-8") as f:
+ #json.dump(hosts, f, indent=4, ensure_ascii=False)
+ for h in hosts:
+ f.write(json.dumps(h, ensure_ascii=False) + "\n")
+
+ except Exception as err:
+ error = True
+ message = str(err).strip()
+
+ if error:
+ code = "DNS_RELOAD_ERROR"
+ # default del messaggio se vuoto o None
+ if not message:
+ message = "DNS reload error"
+ status = "failure"
+ #http_status = 500
+ else:
+ code = "DNS_RELOAD_OK"
+ message = "DNS configuration reload successfully"
+ status = "success"
+ #http_status = 200
- end_ns = time.monotonic_ns()
- took_ms = (end_ns - start_ns)
+ took_ms = (time.monotonic_ns() - start_ns) / 1_000_000
- return {
- "code": "DNS_RELOAD_OK",
- "status": "success",
- "message": "DNS configuration reload successfully",
+ payload = {
+ "code": code,
+ "status": status,
+ "message": message,
"details": {
"took_ms": took_ms
}
}
+ return JSONResponse(content=payload)
- TZ=${DOCKER_TZ}
volumes:
- ${DOCKER_CFG_DIR}/network:/data
+ - ${DOCKER_CFG_DIR}/bind9:/dns
networks:
- proxy
const data = await res.json();
if(data.code !== "DNS_RELOAD_OK"){
- const err = new Error(`Error reloading DNS: ${data.code} ${data.message}`);
+ const err = new Error(`Error reloading DNS: ${data.message}`);
err.status = data.code;
throw err;
}
const data = await res.json();
if(data.code !== "DHCP_RELOAD_OK"){
- const err = new Error(`Error reloading DHCP: ${data.code} ${data.message}`);
+ const err = new Error(`Error reloading DHCP: ${data.message}`);
err.status = data.code;
throw err;
}
ADMIN_USER = "admin"
ADMIN_PASSWORD = "admin"
ADMIN_PASSWORD_HASH_FILE = "/run/secrets/admin_password_hash"
+
+# ---------------------------------------------------------
+# DNS
+# ---------------------------------------------------------
+DNS_CFG_PATH="/dns/etc"
+DNS_HOST_FILE=f"{DOMAIN}/hosts.inc"
+DNS_ALIAS_FILE=f"{DOMAIN}/alias.inc"
+DNS_REVERSE_FILE="reverse/hosts.inc"
(os.getenv("ADMIN_PASSWORD_HASH") or _read_text_if_exists(os.getenv("ADMIN_PASSWORD_HASH_FILE", default.ADMIN_PASSWORD_HASH_FILE)) or None)
))
+ # DNS
+ DNS_CFG_PATH: str = Field(default_factory=lambda: os.getenv("DNS_CFG_PATH", default.DNS_CFG_PATH))
+ DNS_HOST_FILE: str = Field(default_factory=lambda: os.getenv("DNS_HOST_FILE", default.DNS_HOST_FILE))
+ DNS_ALIAS_FILE: str = Field(default_factory=lambda: os.getenv("DNS_ALIAS_FILE", default.DNS_ALIAS_FILE))
+ DNS_REVERSE_FILE: str = Field(default_factory=lambda: os.getenv("DNS_REVERSE_FILE", default.DNS_REVERSE_FILE))
+
def model_post_init(self, __context) -> None:
if self.DEVEL:
ts = datetime.datetime.now().strftime("%Y%m%d-%H%M")
else:
object.__setattr__(self, "APP_VERSION", self.APP_VERSION)
+ # Update Files
+ if self.DOMAIN.lower() != default.DOMAIN.lower():
+ self.DNS_HOST_FILE = self.DNS_HOST_FILE.replace(default.DOMAIN, self.DOMAIN)
+ self.DNS_ALIAS_FILE = self.DNS_ALIAS_FILE.replace(default.DOMAIN, self.DOMAIN)
+ self.DNS_REVERSE_FILE = self.DNS_REVERSE_FILE.replace(default.DOMAIN, self.DOMAIN)
+ self.DNS_HOST_FILE = self.DNS_CFG_PATH + "/" + self.DNS_HOST_FILE
+ self.DNS_ALIAS_FILE = self.DNS_CFG_PATH + "/" + self.DNS_ALIAS_FILE
+ self.DNS_REVERSE_FILE = self.DNS_CFG_PATH + "/" + self.DNS_REVERSE_FILE
# ---------------------------------------------------------
# Singleton
# ---------------------------------------------------------