Initial commit: abyssVpn project

- Tauri desktop app (apps/desktop)
- Python Django API service (services/api)
- Deployment scripts and docs
This commit is contained in:
arthur_chen
2026-05-15 09:06:10 +08:00
commit f69efb67ad
146 changed files with 18039 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
@echo off
setlocal
call "%~dp0config.bat"
echo [AbyssInfo] Checking server %SERVER_USER%@%SERVER_HOST% ...
%SSH_BASE% "whoami && hostname && cat /etc/os-release && echo --- tools --- && (command -v docker || true) && (docker --version || true) && (docker compose version || true) && (command -v git || true) && (command -v nginx || true)"
if errorlevel 1 (
echo [AbyssInfo] Server check failed.
exit /b 1
)
echo [AbyssInfo] Server check completed.
+20
View File
@@ -0,0 +1,20 @@
@echo off
setlocal
call "%~dp0config.bat"
echo [AbyssInfo] Uploading environment installer ...
%SCP_BASE% "%~dp0remote\install_env.sh" %SERVER_USER%@%SERVER_HOST%:/tmp/abyssinfo_install_env.sh
if errorlevel 1 (
echo [AbyssInfo] Upload failed.
exit /b 1
)
echo [AbyssInfo] Installing server environment ...
%SSH_BASE% "chmod +x /tmp/abyssinfo_install_env.sh && /tmp/abyssinfo_install_env.sh"
if errorlevel 1 (
echo [AbyssInfo] Environment installation failed.
exit /b 1
)
echo [AbyssInfo] Environment installation completed.
+23
View File
@@ -0,0 +1,23 @@
@echo off
setlocal
call "%~dp0config.bat"
echo [AbyssInfo] Uploading Netmaker installer and cached assets ...
%SCP_BASE% "%~dp0vendor\nm-quick.sh" %SERVER_USER%@%SERVER_HOST%:/tmp/nm-quick.sh
if errorlevel 1 exit /b 1
%SCP_BASE% -r "%~dp0vendor\netmaker-assets" %SERVER_USER%@%SERVER_HOST%:/tmp/netmaker-assets
if errorlevel 1 exit /b 1
%SCP_BASE% "%~dp0remote\deploy_netmaker.sh" %SERVER_USER%@%SERVER_HOST%:/tmp/abyssinfo_deploy_netmaker.sh
if errorlevel 1 exit /b 1
echo [AbyssInfo] Deploying Netmaker ...
%SSH_BASE% "chmod +x /tmp/abyssinfo_deploy_netmaker.sh && /tmp/abyssinfo_deploy_netmaker.sh"
if errorlevel 1 (
echo [AbyssInfo] Netmaker deployment failed.
exit /b 1
)
echo [AbyssInfo] Netmaker deployment completed.
+14
View File
@@ -0,0 +1,14 @@
@echo off
setlocal
call "%~dp0config.bat"
echo [AbyssInfo] Checking Netmaker containers and endpoints ...
%SSH_BASE% "cd /root/netmaker 2>/dev/null || cd /root; docker compose ps || true; echo --- containers ---; docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'; echo --- health ---; curl -kIs --connect-timeout 10 https://api.nm.101-96-214-81.nip.io | head || true; curl -kIs --connect-timeout 10 https://dashboard.nm.101-96-214-81.nip.io | head || true"
if errorlevel 1 (
echo [AbyssInfo] Netmaker check failed.
exit /b 1
)
echo [AbyssInfo] Netmaker check completed.
+16
View File
@@ -0,0 +1,16 @@
@echo off
setlocal
call "%~dp0config.bat"
echo [AbyssInfo] Uploading Netmaker API probe ...
%SCP_BASE% "%~dp0remote\netmaker_api_probe.sh" %SERVER_USER%@%SERVER_HOST%:/tmp/abyssinfo_netmaker_api_probe.sh
if errorlevel 1 exit /b 1
echo [AbyssInfo] Probing Netmaker API ...
%SSH_BASE% "chmod +x /tmp/abyssinfo_netmaker_api_probe.sh && /tmp/abyssinfo_netmaker_api_probe.sh"
if errorlevel 1 (
echo [AbyssInfo] Netmaker API probe failed.
exit /b 1
)
echo [AbyssInfo] Netmaker API probe completed.
+16
View File
@@ -0,0 +1,16 @@
@echo off
setlocal
call "%~dp0config.bat"
echo [AbyssInfo] Uploading Netmaker network initializer ...
%SCP_BASE% "%~dp0remote\init_netmaker_network.sh" %SERVER_USER%@%SERVER_HOST%:/tmp/abyssinfo_init_netmaker_network.sh
if errorlevel 1 exit /b 1
echo [AbyssInfo] Initializing Netmaker network ...
%SSH_BASE% "chmod +x /tmp/abyssinfo_init_netmaker_network.sh && /tmp/abyssinfo_init_netmaker_network.sh"
if errorlevel 1 (
echo [AbyssInfo] Netmaker network initialization failed.
exit /b 1
)
echo [AbyssInfo] Netmaker network initialization completed.
+17
View File
@@ -0,0 +1,17 @@
@echo off
setlocal
call "%~dp0config.bat"
echo [AbyssInfo] Configuring Django API .env ...
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\configure_api_env.ps1" ^
-ServerHost "%SERVER_HOST%" ^
-ServerUser "%SERVER_USER%" ^
-SshKey "%SSH_KEY%" ^
-ProjectRoot "%~dp0.."
if errorlevel 1 (
echo [AbyssInfo] Django API .env configuration failed.
exit /b 1
)
echo [AbyssInfo] Django API .env configured.
+27
View File
@@ -0,0 +1,27 @@
@echo off
setlocal
call "%~dp0config.bat"
set "API_ZIP=%TEMP%\abyssinfo-api.zip"
echo [AbyssInfo] Packaging Django API ...
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\package_api.ps1" ^
-ProjectRoot "%~dp0.." ^
-OutputZip "%API_ZIP%"
if errorlevel 1 exit /b 1
echo [AbyssInfo] Uploading Django API package and deploy script ...
%SCP_BASE% "%API_ZIP%" %SERVER_USER%@%SERVER_HOST%:/tmp/abyssinfo-api.zip
if errorlevel 1 exit /b 1
%SCP_BASE% "%~dp0remote\deploy_api.sh" %SERVER_USER%@%SERVER_HOST%:/tmp/abyssinfo_deploy_api.sh
if errorlevel 1 exit /b 1
echo [AbyssInfo] Deploying Django API ...
%SSH_BASE% "chmod +x /tmp/abyssinfo_deploy_api.sh && REMOTE_APP_DIR=%REMOTE_APP_DIR% API_DOMAIN=%API_DOMAIN% API_LEGACY_DOMAIN=%API_LEGACY_DOMAIN% NETMAKER_BASE_DOMAIN=%NETMAKER_BASE_DOMAIN% /tmp/abyssinfo_deploy_api.sh"
if errorlevel 1 (
echo [AbyssInfo] Django API deployment failed.
exit /b 1
)
echo [AbyssInfo] Django API deployment completed.
+13
View File
@@ -0,0 +1,13 @@
@echo off
setlocal
call "%~dp0config.bat"
echo [AbyssInfo] Checking Django API service and endpoint ...
%SSH_BASE% "systemctl is-active --quiet abyssinfo-api && curl -fsS http://127.0.0.1:8000/health/ && echo && curl -kfsS https://%API_DOMAIN%/health/"
if errorlevel 1 (
echo [AbyssInfo] Django API check failed.
exit /b 1
)
echo.
echo [AbyssInfo] Django API check completed.
+20
View File
@@ -0,0 +1,20 @@
@echo off
setlocal
chcp 65001 >nul
call "%~dp0config.bat"
if not exist "%SSH_KEY%" (
echo [AbyssInfo] SSH key not found: %SSH_KEY%
exit /b 1
)
echo [AbyssInfo] Uploading VPN-only Netmaker admin setup script...
%SCP_BASE% "%~dp0remote\setup_vpn_admin_access.sh" "%SERVER_USER%@%SERVER_HOST%:/tmp/abyssinfo_setup_vpn_admin_access.sh"
if errorlevel 1 exit /b 1
echo [AbyssInfo] Configuring VPN-only Netmaker admin entry...
%SSH_BASE% "chmod +x /tmp/abyssinfo_setup_vpn_admin_access.sh && NETMAKER_BASE_DOMAIN=%NETMAKER_BASE_DOMAIN% NETMAKER_NETWORK_ID=homevpn VPN_ADMIN_PORT=9090 /tmp/abyssinfo_setup_vpn_admin_access.sh"
if errorlevel 1 exit /b 1
echo [AbyssInfo] Done.
+19
View File
@@ -0,0 +1,19 @@
@echo off
setlocal
call "%~dp0config.bat"
echo [AbyssInfo] Uploading Gitea public access setup script ...
%SCP_BASE% "%~dp0remote\setup_gitea_public_access.sh" %SERVER_USER%@%SERVER_HOST%:/tmp/abyssinfo_setup_gitea_public_access.sh
if errorlevel 1 (
echo [AbyssInfo] Upload failed.
exit /b 1
)
echo [AbyssInfo] Configuring Gitea public access ...
%SSH_BASE% "chmod +x /tmp/abyssinfo_setup_gitea_public_access.sh && GITEA_DOMAIN=%GITEA_DOMAIN% GITEA_WEB_UPSTREAM=%GITEA_WEB_UPSTREAM% GITEA_OIDC_UPSTREAM=%GITEA_OIDC_UPSTREAM% /tmp/abyssinfo_setup_gitea_public_access.sh"
if errorlevel 1 (
echo [AbyssInfo] Gitea public access setup failed.
exit /b 1
)
echo [AbyssInfo] Gitea public access setup completed.
+13
View File
@@ -0,0 +1,13 @@
@echo off
set "SERVER_HOST=101.96.214.81"
set "SERVER_USER=root"
set "SSH_KEY=%~dp0..\codex.pem"
set "REMOTE_APP_DIR=/opt/abyssinfo-home-vpn"
set "API_DOMAIN=vpn.abyssinfo.com"
set "API_LEGACY_DOMAIN=vpn-api.nm.101-96-214-81.nip.io"
set "NETMAKER_BASE_DOMAIN=vpn.abyssinfo.com"
set "GITEA_DOMAIN=gitea.abyssinfo.com"
set "GITEA_WEB_UPSTREAM=100.88.0.1:3001"
set "GITEA_OIDC_UPSTREAM=100.88.0.1:3080"
set "SSH_BASE=ssh -o StrictHostKeyChecking=accept-new -i %SSH_KEY% %SERVER_USER%@%SERVER_HOST%"
set "SCP_BASE=scp -o StrictHostKeyChecking=accept-new -i %SSH_KEY%"
+157
View File
@@ -0,0 +1,157 @@
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="${REMOTE_APP_DIR:-/opt/abyssinfo-home-vpn}"
API_DIR="${APP_DIR}/api"
VENV_DIR="${APP_DIR}/.venv-api"
ZIP_FILE="${API_ZIP_FILE:-/tmp/abyssinfo-api.zip}"
API_DOMAIN="${API_DOMAIN:-vpn-api.nm.101-96-214-81.sslip.io}"
API_LEGACY_DOMAIN="${API_LEGACY_DOMAIN:-vpn-api.nm.101-96-214-81.nip.io}"
API_PORT="${API_PORT:-8000}"
NETMAKER_DOMAIN="${NETMAKER_BASE_DOMAIN:-nm.101-96-214-81.nip.io}"
echo "[AbyssInfo] Deploying Django API"
echo "[AbyssInfo] API domain: ${API_DOMAIN}"
if [[ "$(id -u)" != "0" ]]; then
echo "[AbyssInfo] Please run as root." >&2
exit 1
fi
if [[ ! -f "${ZIP_FILE}" ]]; then
echo "[AbyssInfo] Missing ${ZIP_FILE}" >&2
exit 1
fi
echo "[AbyssInfo] Installing Python runtime ..."
dnf install -y python3.12 python3.12-pip unzip
mkdir -p "${API_DIR}"
PREVIOUS_ENV="/tmp/abyssinfo-api.previous.env"
if [[ -f "${API_DIR}/.env" ]]; then
cp "${API_DIR}/.env" "${PREVIOUS_ENV}"
else
rm -f "${PREVIOUS_ENV}"
fi
find "${API_DIR}" -mindepth 1 \
! -name db.sqlite3 \
! -path "${API_DIR}/db.sqlite3" \
-exec rm -rf {} +
echo "[AbyssInfo] Extracting API package ..."
set +e
unzip -q "${ZIP_FILE}" -d "${API_DIR}"
UNZIP_EXIT=$?
set -e
if [[ "${UNZIP_EXIT}" -gt 1 ]]; then
echo "[AbyssInfo] Failed to unzip API package. Exit code ${UNZIP_EXIT}" >&2
exit "${UNZIP_EXIT}"
fi
if [[ ! -f "${API_DIR}/.env" ]]; then
echo "[AbyssInfo] Missing ${API_DIR}/.env" >&2
exit 1
fi
echo "[AbyssInfo] Normalizing production env ..."
python3.12 - <<PY
from pathlib import Path
import secrets
env_path = Path("${API_DIR}/.env")
previous_env_path = Path("${PREVIOUS_ENV}")
values = {}
for candidate in [previous_env_path, env_path]:
if not candidate.exists():
continue
for line in candidate.read_text(encoding="utf-8").splitlines():
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.lstrip("\\ufeff")
if value or key not in values:
values[key] = value
if not values.get("DJANGO_SECRET_KEY"):
values["DJANGO_SECRET_KEY"] = secrets.token_urlsafe(48)
if not values.get("JWT_SECRET"):
values["JWT_SECRET"] = secrets.token_urlsafe(48)
values["DJANGO_DEBUG"] = "false"
values["DJANGO_ALLOWED_HOSTS"] = "127.0.0.1,localhost,${API_DOMAIN},${API_LEGACY_DOMAIN}"
values["CORS_ALLOWED_ORIGINS"] = "http://localhost:5173,http://127.0.0.1:5173,http://tauri.localhost,https://tauri.localhost,tauri://localhost,https://${API_DOMAIN}"
values["FEISHU_REDIRECT_URI"] = "https://${API_DOMAIN}/auth/feishu/callback"
ordered = [
"DJANGO_SECRET_KEY", "DJANGO_DEBUG", "DJANGO_ALLOWED_HOSTS", "CORS_ALLOWED_ORIGINS",
"FEISHU_APP_ID", "FEISHU_APP_SECRET", "FEISHU_REDIRECT_URI", "FEISHU_EVENT_VERIFICATION_TOKEN", "TAURI_DEEP_LINK_SCHEME",
"JWT_SECRET", "JWT_EXPIRES_SECONDS", "OAUTH_STATE_EXPIRES_SECONDS", "LOGIN_CODE_EXPIRES_SECONDS",
"NETMAKER_BASE_URL", "NETMAKER_API_TOKEN", "NETMAKER_NETWORK_ID",
"NETMAKER_ENROLLMENT_TAGS", "NETMAKER_ENROLLMENT_TTL_SECONDS",
]
env_path.write_text("\\n".join(f"{key}={values.get(key, '')}" for key in ordered) + "\\n", encoding="utf-8")
PY
echo "[AbyssInfo] Creating virtual environment ..."
if [[ ! -x "${VENV_DIR}/bin/python" ]]; then
python3.12 -m venv "${VENV_DIR}"
fi
"${VENV_DIR}/bin/python" -m pip install --upgrade pip
"${VENV_DIR}/bin/pip" install -r "${API_DIR}/requirements.txt"
echo "[AbyssInfo] Running Django checks and migrations ..."
cd "${API_DIR}"
"${VENV_DIR}/bin/python" manage.py check
"${VENV_DIR}/bin/python" manage.py migrate --noinput
"${VENV_DIR}/bin/python" manage.py collectstatic --noinput
echo "[AbyssInfo] Installing systemd service ..."
cat >/etc/systemd/system/abyssinfo-api.service <<SERVICE
[Unit]
Description=AbyssInfo Home VPN Django API
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=${API_DIR}
Environment=PYTHONUNBUFFERED=1
ExecStart=${VENV_DIR}/bin/gunicorn config.wsgi:application --bind 0.0.0.0:${API_PORT} --workers 2 --timeout 60
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
SERVICE
systemctl daemon-reload
systemctl enable --now abyssinfo-api
systemctl restart abyssinfo-api
echo "[AbyssInfo] Updating Netmaker Caddy reverse proxy ..."
CADDYFILE="/root/netmaker/Caddyfile"
if [[ ! -f "${CADDYFILE}" ]]; then
echo "[AbyssInfo] Missing ${CADDYFILE}" >&2
exit 1
fi
if ! grep -q "https://${API_DOMAIN}" "${CADDYFILE}"; then
cat >>"${CADDYFILE}" <<CADDY
# AbyssInfo Django API
https://${API_DOMAIN} {
reverse_proxy http://host.docker.internal:${API_PORT}
}
CADDY
fi
(cd /root/netmaker && docker compose exec -T caddy caddy reload --config /etc/caddy/Caddyfile)
echo "[AbyssInfo] Verifying API health ..."
curl -fsS "http://127.0.0.1:${API_PORT}/health/" >/dev/null
curl -kfsS "https://${API_DOMAIN}/health/" >/dev/null
echo "[AbyssInfo] Django API deployed:"
echo " https://${API_DOMAIN}/health/"
+168
View File
@@ -0,0 +1,168 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_DOMAIN="${NETMAKER_BASE_DOMAIN:-nm.101-96-214-81.nip.io}"
ADMIN_EMAIL="${NETMAKER_ADMIN_EMAIL:-admin@abyssinfo.local}"
ASSET_PORT="${NETMAKER_ASSET_PORT:-18080}"
ASSET_DIR="/tmp/netmaker-assets"
QUICK_SCRIPT="/tmp/nm-quick.sh"
echo "[AbyssInfo] Deploying Netmaker CE"
echo "[AbyssInfo] Base domain: ${BASE_DOMAIN}"
if [[ "$(id -u)" != "0" ]]; then
echo "[AbyssInfo] Please run as root." >&2
exit 1
fi
if [[ ! -f "${QUICK_SCRIPT}" ]]; then
echo "[AbyssInfo] Missing ${QUICK_SCRIPT}" >&2
exit 1
fi
if [[ ! -d "${ASSET_DIR}" ]]; then
echo "[AbyssInfo] Missing ${ASSET_DIR}" >&2
exit 1
fi
echo "[AbyssInfo] Installing Netmaker quick-install dependencies ..."
dnf install -y wget jq bind-utils gawk wireguard-tools python3
echo "[AbyssInfo] Ensuring Docker is running ..."
mkdir -p /etc/docker
cat >/etc/docker/daemon.json <<'JSON'
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.m.daocloud.io",
"https://hub-mirror.c.163.com"
]
}
JSON
systemctl daemon-reload
systemctl restart docker
systemctl enable --now docker
echo "[AbyssInfo] Stopping Nginx to free ports 80/443 for Netmaker Caddy ..."
systemctl disable --now nginx 2>/dev/null || true
health_check() {
local dashboard_url="https://dashboard.${BASE_DOMAIN}"
if [[ ! -f /root/netmaker/docker-compose.yml ]]; then
echo "[AbyssInfo] Missing /root/netmaker/docker-compose.yml" >&2
return 1
fi
echo "[AbyssInfo] Netmaker containers:"
(cd /root/netmaker && docker compose ps)
echo "[AbyssInfo] Checking dashboard endpoint: ${dashboard_url}"
curl -kfsSI --connect-timeout 15 "${dashboard_url}" >/dev/null
}
apply_internal_only_defaults() {
local env_file="/root/netmaker/netmaker.env"
if [[ ! -f "${env_file}" ]]; then
return 0
fi
echo "[AbyssInfo] Applying internal-only Netmaker client defaults ..."
python3 - <<'PY'
from pathlib import Path
env_path = Path("/root/netmaker/netmaker.env")
values = {}
order = []
for line in env_path.read_text(encoding="utf-8").splitlines():
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
if key not in values:
order.append(key)
values[key] = value
for key, value in {
"DNS_MODE": "off",
"MANAGE_DNS": "false",
}.items():
if key not in values:
order.append(key)
values[key] = value
env_path.write_text("\n".join(f"{key}={values[key]}" for key in order) + "\n", encoding="utf-8")
PY
}
if [[ "${NETMAKER_FORCE_REINSTALL:-0}" == "1" ]]; then
echo "[AbyssInfo] Force reinstall requested. Removing previous Netmaker deployment ..."
if [[ -f /root/netmaker/docker-compose.yml ]]; then
(cd /root/netmaker && docker compose down --volumes || true)
fi
rm -rf /root/netmaker /etc/netmaker /etc/netclient
elif [[ -f /root/netmaker/docker-compose.yml ]]; then
echo "[AbyssInfo] Existing Netmaker deployment found. Starting services and running health check ..."
apply_internal_only_defaults
(cd /root/netmaker && docker compose up -d)
if health_check; then
echo "[AbyssInfo] Existing Netmaker deployment is healthy."
echo "[AbyssInfo] URLs:"
echo " Dashboard: https://dashboard.${BASE_DOMAIN}"
echo " API: https://api.${BASE_DOMAIN}"
echo " Broker: wss://broker.${BASE_DOMAIN}"
exit 0
fi
echo "[AbyssInfo] Existing deployment is not healthy. Set NETMAKER_FORCE_REINSTALL=1 to rebuild it." >&2
exit 1
fi
echo "[AbyssInfo] Preparing quick installer ..."
mkdir -p /root
cp "${QUICK_SCRIPT}" /root/nm-quick.sh
chmod +x /root/nm-quick.sh
# Use uploaded cached assets instead of pulling compose files from GitHub on the server.
sed -i "s|local BASE_URL=\"https://raw.githubusercontent.com/gravitl/netmaker/\$BRANCH\"|local BASE_URL=\"http://127.0.0.1:${ASSET_PORT}\"|g" /root/nm-quick.sh
echo "[AbyssInfo] Starting temporary local asset server ..."
pkill -f "python3 -m http.server ${ASSET_PORT}" 2>/dev/null || true
cd "${ASSET_DIR}"
python3 -m http.server "${ASSET_PORT}" --bind 127.0.0.1 >/tmp/abyssinfo-netmaker-assets.log 2>&1 &
ASSET_PID=$!
trap 'kill ${ASSET_PID} 2>/dev/null || true' EXIT
sleep 2
curl -fsS "http://127.0.0.1:${ASSET_PORT}/compose/docker-compose.yml" >/dev/null
echo "[AbyssInfo] Running Netmaker quick installer ..."
cd /root
export NM_DOMAIN="${BASE_DOMAIN}"
export NM_EMAIL="${ADMIN_EMAIL}"
export NM_SKIP_DEPS=1
# Input sequence for nm-quick CE:
# 1 = use auto/generated domain, email, y = confirm settings.
set +e
printf "1\n%s\ny\n" "${ADMIN_EMAIL}" | /root/nm-quick.sh -c
QUICK_EXIT=$?
set -e
echo "[AbyssInfo] Netmaker files:"
ls -la /root/netmaker
if ! health_check; then
echo "[AbyssInfo] Netmaker health check failed after quick installer exit code ${QUICK_EXIT}." >&2
exit 1
fi
if [[ "${QUICK_EXIT}" != "0" ]]; then
echo "[AbyssInfo] Quick installer exited with ${QUICK_EXIT}, but Netmaker services are healthy. Continuing."
fi
apply_internal_only_defaults
(cd /root/netmaker && docker compose up -d)
echo "[AbyssInfo] URLs:"
echo " Dashboard: https://dashboard.${BASE_DOMAIN}"
echo " API: https://api.${BASE_DOMAIN}"
echo " Broker: wss://broker.${BASE_DOMAIN}"
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_DOMAIN="${NETMAKER_BASE_DOMAIN:-nm.101-96-214-81.nip.io}"
ENV_FILE="${NETMAKER_ENV_FILE:-/root/netmaker/netmaker.env}"
NETWORK_ID="${NETMAKER_NETWORK_ID:-homevpn}"
NETWORK_RANGE="${NETMAKER_NETWORK_RANGE:-100.88.0.0/24}"
if [[ ! -f "${ENV_FILE}" ]]; then
echo "[AbyssInfo] Missing ${ENV_FILE}" >&2
exit 1
fi
set -a
# shellcheck source=/dev/null
. "${ENV_FILE}"
set +a
if [[ -z "${MASTER_KEY:-}" ]]; then
echo "[AbyssInfo] MASTER_KEY is missing in ${ENV_FILE}" >&2
exit 1
fi
api_url="https://api.${BASE_DOMAIN}"
auth_header="Authorization: Bearer ${MASTER_KEY}"
echo "[AbyssInfo] Checking Netmaker network: ${NETWORK_ID}"
networks_json="$(curl -ksS -H "${auth_header}" "${api_url}/api/networks")"
if echo "${networks_json}" | jq -e --arg id "${NETWORK_ID}" '.[]? | select(.netid == $id)' >/dev/null; then
echo "[AbyssInfo] Network already exists: ${NETWORK_ID}"
else
echo "[AbyssInfo] Creating Netmaker network: ${NETWORK_ID} (${NETWORK_RANGE})"
payload="$(jq -n \
--arg netid "${NETWORK_ID}" \
--arg addressrange "${NETWORK_RANGE}" \
'{
netid: $netid,
addressrange: $addressrange,
defaultacl: "yes",
defaultmtu: 1420,
defaultlistenport: 51821,
defaultkeepalive: 20,
auto_join: true
}')"
response_file="$(mktemp)"
code="$(curl -ksS -o "${response_file}" -w "%{http_code}" \
-X POST "${api_url}/api/networks" \
-H "${auth_header}" \
-H "Content-Type: application/json" \
--data "${payload}" || true)"
if [[ "${code}" -lt 200 || "${code}" -ge 300 ]]; then
echo "[AbyssInfo] Failed to create network. HTTP ${code}" >&2
head -c 500 "${response_file}" >&2 || true
echo >&2
rm -f "${response_file}"
exit 1
fi
rm -f "${response_file}"
fi
echo "[AbyssInfo] Current networks:"
curl -ksS -H "${auth_header}" "${api_url}/api/networks" \
| jq -r '.[] | "- \(.netid) \(.addressrange // "")"'
echo "[AbyssInfo] Backend config:"
echo "NETMAKER_BASE_URL=${api_url}"
echo "NETMAKER_NETWORK_ID=${NETWORK_ID}"
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail
echo "[AbyssInfo] Server environment installer"
echo "[AbyssInfo] User: $(whoami)"
echo "[AbyssInfo] OS:"
cat /etc/os-release
if [[ "$(id -u)" != "0" ]]; then
echo "[AbyssInfo] Please run as root." >&2
exit 1
fi
echo "[AbyssInfo] Installing base packages ..."
dnf install -y dnf-plugins-core ca-certificates curl git tar gzip unzip policycoreutils-python-utils
if ! command -v docker >/dev/null 2>&1; then
echo "[AbyssInfo] Adding Docker CE repository ..."
rm -f /etc/yum.repos.d/docker-ce.repo
dnf clean all
if ! dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo; then
echo "[AbyssInfo] Aliyun Docker repository failed, using official repository ..."
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
fi
dnf clean all
dnf makecache --disablerepo='*' --enablerepo='docker-ce-stable' || true
echo "[AbyssInfo] Installing Docker Engine and Compose plugin ..."
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
else
echo "[AbyssInfo] Docker already installed."
fi
systemctl enable --now docker
if ! command -v nginx >/dev/null 2>&1; then
echo "[AbyssInfo] Installing Nginx ..."
dnf install -y nginx
else
echo "[AbyssInfo] Nginx already installed."
fi
mkdir -p /opt/abyssinfo-home-vpn
if systemctl is-active --quiet firewalld; then
echo "[AbyssInfo] Configuring running firewalld ..."
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
else
echo "[AbyssInfo] firewalld is not running; skipping OS firewall changes."
fi
echo "[AbyssInfo] Versions:"
docker --version
docker compose version
git --version
nginx -v
echo "[AbyssInfo] Environment ready."
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_DOMAIN="${NETMAKER_BASE_DOMAIN:-nm.101-96-214-81.nip.io}"
ENV_FILE="${NETMAKER_ENV_FILE:-/root/netmaker/netmaker.env}"
if [[ ! -f "${ENV_FILE}" ]]; then
echo "[AbyssInfo] Missing ${ENV_FILE}" >&2
exit 1
fi
set -a
# shellcheck source=/dev/null
. "${ENV_FILE}"
set +a
if [[ -z "${MASTER_KEY:-}" ]]; then
echo "[AbyssInfo] MASTER_KEY is missing in ${ENV_FILE}" >&2
exit 1
fi
echo "[AbyssInfo] Probing Netmaker API routes"
for path in \
/api/networks \
/api/v1/networks \
/api/v1/enrollment-keys \
/api/server/getserverinfo \
/api/users/adm/hasadmin \
/api/v1/server/getserverinfo
do
response_file="$(mktemp)"
code="$(curl -ksS -o "${response_file}" -w "%{http_code}" \
-H "Authorization: Bearer ${MASTER_KEY}" \
"https://api.${BASE_DOMAIN}${path}" || true)"
printf "%s %s " "${code}" "${path}"
head -c 180 "${response_file}" | tr '\n' ' '
echo
rm -f "${response_file}"
done
+107
View File
@@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -euo pipefail
GITEA_DOMAIN="${GITEA_DOMAIN:-gitea.abyssinfo.com}"
GITEA_WEB_UPSTREAM="${GITEA_WEB_UPSTREAM:-100.88.0.1:3001}"
GITEA_OIDC_UPSTREAM="${GITEA_OIDC_UPSTREAM:-100.88.0.1:3080}"
NETMAKER_DIR="${NETMAKER_DIR:-/root/netmaker}"
CADDYFILE="${CADDYFILE:-${NETMAKER_DIR}/Caddyfile}"
echo "[AbyssInfo] Configuring public Gitea reverse proxy"
echo "[AbyssInfo] Domain: ${GITEA_DOMAIN}"
echo "[AbyssInfo] Web upstream: http://${GITEA_WEB_UPSTREAM}"
echo "[AbyssInfo] OIDC upstream: http://${GITEA_OIDC_UPSTREAM}"
if [[ "$(id -u)" != "0" ]]; then
echo "[AbyssInfo] Please run as root." >&2
exit 1
fi
if [[ ! -f "${CADDYFILE}" ]]; then
echo "[AbyssInfo] Missing ${CADDYFILE}" >&2
exit 1
fi
echo "[AbyssInfo] Checking upstreams from server ..."
curl -fsS --connect-timeout 3 --max-time 8 "http://${GITEA_OIDC_UPSTREAM}/oidc/.well-known/openid-configuration" >/dev/null \
&& echo "[AbyssInfo] OIDC upstream OK" \
|| echo "[AbyssInfo] WARN: OIDC upstream is not reachable from server"
curl -fsSI --connect-timeout 3 --max-time 8 "http://${GITEA_WEB_UPSTREAM}/user/login" >/dev/null \
&& echo "[AbyssInfo] Gitea web upstream OK" \
|| echo "[AbyssInfo] WARN: Gitea web upstream is not reachable from server"
python3 - <<PY
from pathlib import Path
caddyfile = Path("${CADDYFILE}")
domain = "${GITEA_DOMAIN}"
web = "${GITEA_WEB_UPSTREAM}"
oidc = "${GITEA_OIDC_UPSTREAM}"
start = "# AbyssInfo Gitea public access begin"
end = "# AbyssInfo Gitea public access end"
block = f"""
{start}
https://{domain} {{
\tencode gzip zstd
\thandle /oidc/* {{
\t\treverse_proxy http://{oidc} {{
\t\t\theader_up Host {{host}}
\t\t\theader_up X-Forwarded-Host {{host}}
\t\t\theader_up X-Forwarded-Proto https
\t\t\theader_up X-Forwarded-Ssl on
\t\t}}
\t}}
\thandle /auth/* {{
\t\treverse_proxy http://{oidc} {{
\t\t\theader_up Host {{host}}
\t\t\theader_up X-Forwarded-Host {{host}}
\t\t\theader_up X-Forwarded-Proto https
\t\t\theader_up X-Forwarded-Ssl on
\t\t}}
\t}}
\thandle /interaction/* {{
\t\treverse_proxy http://{oidc} {{
\t\t\theader_up Host {{host}}
\t\t\theader_up X-Forwarded-Host {{host}}
\t\t\theader_up X-Forwarded-Proto https
\t\t\theader_up X-Forwarded-Ssl on
\t\t}}
\t}}
\thandle {{
\t\treverse_proxy http://{web} {{
\t\t\theader_up Host {{host}}
\t\t\theader_up X-Forwarded-Host {{host}}
\t\t\theader_up X-Forwarded-Proto https
\t\t\theader_up X-Forwarded-Ssl on
\t\t}}
\t}}
}}
{end}
""".strip()
text = caddyfile.read_text(encoding="utf-8")
if start in text and end in text:
before, rest = text.split(start, 1)
_old, after = rest.split(end, 1)
text = before.rstrip() + "\n\n" + block + "\n" + after.lstrip()
else:
text = text.rstrip() + "\n\n" + block + "\n"
caddyfile.write_text(text, encoding="utf-8")
PY
echo "[AbyssInfo] Reloading Caddy ..."
(cd "${NETMAKER_DIR}" && docker compose exec -T caddy caddy reload --config /etc/caddy/Caddyfile)
echo "[AbyssInfo] Checking public endpoints ..."
curl -kfsS --connect-timeout 5 --max-time 12 "https://${GITEA_DOMAIN}/oidc/.well-known/openid-configuration" >/dev/null \
&& echo "[AbyssInfo] Public OIDC endpoint OK" \
|| echo "[AbyssInfo] WARN: Public OIDC endpoint is not ready. Check DNS and upstream reachability."
curl -kfsSI --connect-timeout 5 --max-time 12 "https://${GITEA_DOMAIN}/user/login" >/dev/null \
&& echo "[AbyssInfo] Public Gitea login endpoint OK" \
|| echo "[AbyssInfo] WARN: Public Gitea login endpoint is not ready. Check DNS and upstream reachability."
echo "[AbyssInfo] Gitea public access configured:"
echo " https://${GITEA_DOMAIN}/oidc/.well-known/openid-configuration -> http://${GITEA_OIDC_UPSTREAM}"
echo " https://${GITEA_DOMAIN}/user/login -> http://${GITEA_WEB_UPSTREAM}"
+227
View File
@@ -0,0 +1,227 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_DOMAIN="${NETMAKER_BASE_DOMAIN:-vpn.abyssinfo.com}"
NETWORK_ID="${NETMAKER_NETWORK_ID:-homevpn}"
ENV_FILE="${NETMAKER_ENV_FILE:-/root/netmaker/netmaker.env}"
INSTALL_DIR="${NETCLIENT_INSTALL_DIR:-/usr/local/bin}"
NETCLIENT_VERSION="${NETCLIENT_VERSION:-v1.5.1}"
ADMIN_PORT="${VPN_ADMIN_PORT:-9090}"
NETMAKER_DIR="${NETMAKER_DIR:-/root/netmaker}"
CADDY_FILE="${NETMAKER_DIR}/Caddyfile"
COMPOSE_FILE="${NETMAKER_DIR}/docker-compose.yml"
if [[ "$(id -u)" != "0" ]]; then
echo "[AbyssInfo] Please run as root." >&2
exit 1
fi
if [[ ! -f "${ENV_FILE}" ]]; then
echo "[AbyssInfo] Missing ${ENV_FILE}" >&2
exit 1
fi
if [[ ! -f "${CADDY_FILE}" || ! -f "${COMPOSE_FILE}" ]]; then
echo "[AbyssInfo] Missing Netmaker compose files in ${NETMAKER_DIR}" >&2
exit 1
fi
set -a
# shellcheck source=/dev/null
. "${ENV_FILE}"
set +a
if [[ -z "${MASTER_KEY:-}" ]]; then
echo "[AbyssInfo] MASTER_KEY is missing in ${ENV_FILE}" >&2
exit 1
fi
api_url="https://api.${BASE_DOMAIN}"
auth_header="Authorization: Bearer ${MASTER_KEY}"
install_netclient() {
if command -v netclient >/dev/null 2>&1; then
return 0
fi
echo "[AbyssInfo] Installing netclient ${NETCLIENT_VERSION} ..."
dnf install -y wget jq wireguard-tools >/dev/null
local arch
arch="$(uname -m)"
case "${arch}" in
x86_64|amd64) arch="amd64" ;;
aarch64|arm64) arch="arm64" ;;
*)
echo "[AbyssInfo] Unsupported architecture: ${arch}" >&2
exit 1
;;
esac
mkdir -p "${INSTALL_DIR}"
wget -qO "${INSTALL_DIR}/netclient" "https://github.com/gravitl/netclient/releases/download/${NETCLIENT_VERSION}/netclient-linux-${arch}"
chmod +x "${INSTALL_DIR}/netclient"
"${INSTALL_DIR}/netclient" install
}
current_vpn_ip() {
if [[ -f /etc/netclient/nodes.json ]]; then
jq -r --arg network "${NETWORK_ID}" '.[$network].address.IP // empty' /etc/netclient/nodes.json 2>/dev/null |
awk 'NF && $1 !~ /^169\.254\./ { print; exit }'
fi
}
create_server_join_token() {
local key_name response_file code token
key_name="abyssinfo-server-admin-$(date +%Y%m%d%H%M%S)"
response_file="$(mktemp)"
local payload
payload="$(jq -n \
--arg name "${key_name}" \
--arg network "${NETWORK_ID}" \
'{
name: $name,
tags: ["abyssinfo-server-admin"],
type: 2,
uses_remaining: 1,
unlimited: false,
expiration: 0,
networks: [$network],
groups: ["homevpn", "server", "admin"],
auto_egress: false,
auto_assign_gw: false
}')"
code="$(curl -ksS -o "${response_file}" -w "%{http_code}" \
-X POST "${api_url}/api/v1/enrollment-keys" \
-H "${auth_header}" \
-H "Content-Type: application/json" \
--data "${payload}" || true)"
if [[ "${code}" -lt 200 || "${code}" -ge 300 ]]; then
echo "[AbyssInfo] Failed to create server enrollment token. HTTP ${code}" >&2
head -c 500 "${response_file}" >&2 || true
echo >&2
rm -f "${response_file}"
exit 1
fi
token="$(jq -r '.token // .data.token // empty' "${response_file}")"
rm -f "${response_file}"
if [[ -z "${token}" || "${token}" == "null" ]]; then
echo "[AbyssInfo] Netmaker did not return an enrollment token." >&2
exit 1
fi
printf "%s" "${token}"
}
join_server_to_vpn() {
local ip token
ip="$(current_vpn_ip || true)"
if [[ -n "${ip}" ]]; then
echo "[AbyssInfo] Server is already in ${NETWORK_ID}: ${ip}"
systemctl enable --now netclient >/dev/null 2>&1 || true
return 0
fi
echo "[AbyssInfo] Joining server to Netmaker network ${NETWORK_ID} ..."
token="$(create_server_join_token)"
netclient join -t "${token}" >/tmp/abyssinfo-server-netclient-join.log 2>&1 || {
echo "[AbyssInfo] netclient join failed. Last log lines:" >&2
tail -n 80 /tmp/abyssinfo-server-netclient-join.log >&2 || true
exit 1
}
systemctl enable --now netclient >/dev/null 2>&1 || true
for _ in $(seq 1 90); do
ip="$(current_vpn_ip || true)"
if [[ -n "${ip}" ]]; then
echo "[AbyssInfo] Server VPN IP: ${ip}"
return 0
fi
sleep 2
done
echo "[AbyssInfo] Server joined, but VPN IP was not detected." >&2
exit 1
}
configure_caddy_vpn_admin() {
local vpn_ip="$1"
echo "[AbyssInfo] Configuring Caddy VPN-only admin entry on ${vpn_ip}:${ADMIN_PORT} ..."
cp -a "${CADDY_FILE}" "${CADDY_FILE}.bak.$(date +%Y%m%d%H%M%S)"
cp -a "${COMPOSE_FILE}" "${COMPOSE_FILE}.bak.$(date +%Y%m%d%H%M%S)"
VPN_IP="${vpn_ip}" VPN_ADMIN_PORT="${ADMIN_PORT}" python3 - <<'PY'
from pathlib import Path
import os
import re
caddy_path = Path("/root/netmaker/Caddyfile")
compose_path = Path("/root/netmaker/docker-compose.yml")
vpn_ip = os.environ["VPN_IP"]
port = os.environ["VPN_ADMIN_PORT"]
block = f"""
# AbyssInfo VPN-only Netmaker Dashboard
:{port} {{
\theader {{
\t\tX-Robots-Tag "none"
\t\t-Server
\t}}
\treverse_proxy http://netmaker-ui
}}
"""
caddy = caddy_path.read_text(encoding="utf-8")
caddy = re.sub(
r"\n# AbyssInfo VPN-only Netmaker Dashboard\n:\d+ \{.*?\n\}\n?",
"\n",
caddy,
flags=re.S,
).rstrip() + block
caddy_path.write_text(caddy, encoding="utf-8")
compose = compose_path.read_text(encoding="utf-8")
mapping = f' - "{vpn_ip}:{port}:{port}/tcp"'
lines = []
inserted = False
for line in compose.splitlines():
if f":{port}:{port}/tcp" in line:
if not inserted:
lines.append(mapping)
inserted = True
continue
lines.append(line)
if '"50051:50051"' in line and not inserted:
lines.append(mapping)
inserted = True
if not inserted:
raise SystemExit("failed to insert caddy port mapping after 50051")
compose_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
PY
(cd "${NETMAKER_DIR}" && docker compose up -d caddy)
sleep 2
curl -fsSI --connect-timeout 10 "http://${vpn_ip}:${ADMIN_PORT}" >/dev/null
}
main() {
install_netclient
join_server_to_vpn
local vpn_ip
vpn_ip="$(current_vpn_ip)"
configure_caddy_vpn_admin "${vpn_ip}"
echo "[AbyssInfo] VPN-only Netmaker dashboard:"
echo " http://${vpn_ip}:${ADMIN_PORT}"
echo "[AbyssInfo] Only VPN clients with route to ${NETWORK_ID} should be able to reach this address."
}
main "$@"
+62
View File
@@ -0,0 +1,62 @@
param(
[Parameter(Mandatory = $true)][string]$ServerHost,
[Parameter(Mandatory = $true)][string]$ServerUser,
[Parameter(Mandatory = $true)][string]$SshKey,
[Parameter(Mandatory = $true)][string]$ProjectRoot
)
$ErrorActionPreference = "Stop"
function New-Secret {
-join (1..64 | ForEach-Object { "{0:x}" -f (Get-Random -Minimum 0 -Maximum 16) })
}
$apiDir = Join-Path $ProjectRoot "services\api"
$envPath = Join-Path $apiDir ".env"
$remote = "${ServerUser}@${ServerHost}"
$existing = @{}
if (Test-Path -LiteralPath $envPath) {
Get-Content -LiteralPath $envPath | ForEach-Object {
if ($_ -and -not $_.StartsWith("#") -and $_.Contains("=")) {
$parts = $_.Split("=", 2)
$existing[$parts[0].TrimStart([char]0xFEFF)] = $parts[1]
}
}
}
$masterKey = (& ssh.exe -i $SshKey $remote "grep '^MASTER_KEY=' /root/netmaker/netmaker.env | cut -d= -f2-").Trim()
if (-not $masterKey) {
throw "MASTER_KEY was not found on the Netmaker server."
}
$djangoSecret = New-Secret
$jwtSecret = New-Secret
$feishuAppId = $existing["FEISHU_APP_ID"]
$feishuAppSecret = $existing["FEISHU_APP_SECRET"]
$content = @"
DJANGO_SECRET_KEY=$djangoSecret
DJANGO_DEBUG=true
DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost,$ServerHost
CORS_ALLOWED_ORIGINS=http://localhost:5173,tauri://localhost
FEISHU_APP_ID=$feishuAppId
FEISHU_APP_SECRET=$feishuAppSecret
FEISHU_REDIRECT_URI=http://127.0.0.1:8000/auth/feishu/callback
TAURI_DEEP_LINK_SCHEME=myvpn
JWT_SECRET=$jwtSecret
JWT_EXPIRES_SECONDS=600
OAUTH_STATE_EXPIRES_SECONDS=300
LOGIN_CODE_EXPIRES_SECONDS=120
NETMAKER_BASE_URL=https://api.vpn.abyssinfo.com
NETMAKER_API_TOKEN=$masterKey
NETMAKER_NETWORK_ID=homevpn
NETMAKER_ENROLLMENT_TAGS=homevpn,desktop,vpn
NETMAKER_ENROLLMENT_TTL_SECONDS=600
"@
[System.IO.File]::WriteAllText($envPath, $content, [System.Text.UTF8Encoding]::new($false))
Write-Host "[AbyssInfo] Wrote services\api\.env"
+34
View File
@@ -0,0 +1,34 @@
param(
[Parameter(Mandatory = $true)][string]$ProjectRoot,
[Parameter(Mandatory = $true)][string]$OutputZip
)
$ErrorActionPreference = "Stop"
$apiDir = Join-Path $ProjectRoot "services\api"
$stageRoot = Join-Path $env:TEMP ("abyssinfo-api-stage-" + [guid]::NewGuid().ToString("N"))
$stageApi = Join-Path $stageRoot "api"
New-Item -ItemType Directory -Force -Path $stageApi | Out-Null
$excludeDirs = @(".venv", "__pycache__", "staticfiles")
$excludeFiles = @("db.sqlite3")
Get-ChildItem -LiteralPath $apiDir -Force | ForEach-Object {
if ($_.PSIsContainer) {
if ($excludeDirs -notcontains $_.Name) {
Copy-Item -LiteralPath $_.FullName -Destination $stageApi -Recurse -Force
}
} elseif ($excludeFiles -notcontains $_.Name) {
Copy-Item -LiteralPath $_.FullName -Destination $stageApi -Force
}
}
if (Test-Path -LiteralPath $OutputZip) {
Remove-Item -LiteralPath $OutputZip -Force
}
Compress-Archive -Path (Join-Path $stageApi "*") -DestinationPath $OutputZip -Force
Remove-Item -LiteralPath $stageRoot -Recurse -Force
Write-Host "[AbyssInfo] Packaged API to $OutputZip"
@@ -0,0 +1,76 @@
services:
netmaker:
container_name: netmaker
image: gravitl/netmaker:$SERVER_IMAGE_TAG
env_file: ./netmaker.env
restart: always
volumes:
- dnsconfig:/root/config/dnsconfig
- sqldata:/root/data
environment:
# config-dependant vars
- STUN_SERVERS=stun1.l.google.com:19302,stun2.l.google.com:19302,stun3.l.google.com:19302,stun4.l.google.com:19302
# The domain/host IP indicating the mq broker address
- BROKER_ENDPOINT=wss://broker.${NM_DOMAIN} # For EMQX broker use `BROKER_ENDPOINT=wss://broker.${NM_DOMAIN}/mqtt`
# For EMQX broker (uncomment the two lines below)
#- BROKER_TYPE=emqx
#- EMQX_REST_ENDPOINT=http://mq:18083
# The base domain of netmaker
- SERVER_NAME=${NM_DOMAIN}
- SERVER_API_CONN_STRING=api.${NM_DOMAIN}:443
# Address of the CoreDNS server. Defaults to SERVER_HOST
- COREDNS_ADDR=${SERVER_HOST}
# Overrides SERVER_HOST if set. Useful for making HTTP available via different interfaces/networks.
- SERVER_HTTP_HOST=api.${NM_DOMAIN}
netmaker-ui:
container_name: netmaker-ui
image: gravitl/netmaker-ui:$UI_IMAGE_TAG
env_file: ./netmaker.env
environment:
# config-dependant vars
# URL where UI will send API requests. Change based on SERVER_HOST, SERVER_HTTP_HOST, and API_PORT
BACKEND_URL: "https://api.${NM_DOMAIN}"
depends_on:
- netmaker
links:
- "netmaker:api"
restart: always
caddy:
image: caddy:2.8.4
container_name: caddy
env_file: ./netmaker.env
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_conf:/config
ports:
- "80:80/tcp"
- "443:443/tcp"
- "50051:50051"
mq:
container_name: mq
image: eclipse-mosquitto:2.0.15-openssl
env_file: ./netmaker.env
depends_on:
- netmaker
restart: unless-stopped
command: [ "/mosquitto/config/wait.sh" ]
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf
- ./wait.sh:/mosquitto/config/wait.sh
- mosquitto_logs:/mosquitto/log
- mosquitto_data:/mosquitto/data
volumes:
caddy_data: { } # runtime data for caddy
caddy_conf: { } # configuration file for Caddy
sqldata: { }
dnsconfig: { } # storage for coredns
mosquitto_logs: { } # storage for mqtt logs
mosquitto_data: { } # storage for mqtt data
+34
View File
@@ -0,0 +1,34 @@
# Dashboard
https://dashboard.{$NM_DOMAIN} {
# Apply basic security headers
header {
# Enable cross origin access to *.{$NM_DOMAIN}
Access-Control-Allow-Origin *.{$NM_DOMAIN}
# Enable HTTP Strict Transport Security (HSTS)
Strict-Transport-Security "max-age=31536000;"
# Enable cross-site filter (XSS) and tell browser to block detected attacks
X-XSS-Protection "1; mode=block"
# Disallow the site to be rendered within a frame on a foreign domain (clickjacking protection)
X-Frame-Options "SAMEORIGIN"
# Prevent search engines from indexing
X-Robots-Tag "none"
# Remove the server name
-Server
}
reverse_proxy http://netmaker-ui
}
# API
https://api.{$NM_DOMAIN} {
reverse_proxy http://netmaker:8081
}
# MQ
broker.{$NM_DOMAIN} {
@ws {
header Connection *Upgrade*
header Upgrade websocket
}
reverse_proxy @ws mq:8883 # For EMQX websockets use `reverse_proxy @ws mq:8083`
}
+10
View File
@@ -0,0 +1,10 @@
per_listener_settings false
listener 8883
protocol websockets
allow_anonymous false
listener 1883
protocol websockets
allow_anonymous false
password_file /mosquitto/password.txt
+18
View File
@@ -0,0 +1,18 @@
#!/bin/ash
encrypt_password() {
echo "${MQ_USERNAME}:${MQ_PASSWORD}" > /mosquitto/password.txt
mosquitto_passwd -U /mosquitto/password.txt
}
main(){
encrypt_password
echo "Starting MQ..."
# Run the main container command.
/docker-entrypoint.sh
/usr/sbin/mosquitto -c /mosquitto/config/mosquitto.conf
}
main "${@}"
@@ -0,0 +1,111 @@
# Email used for SSL certificates
NM_EMAIL=
# The base domain of netmaker
NM_DOMAIN=
# Public IPv4 endpoint of machine
SERVER_HOST=
# Public IPv6 endpoint of machine
SERVER_HOST6=
# The admin master key for accessing the API. Change this in any production installation.
MASTER_KEY=
# The username to set for MQ access
MQ_USERNAME=
# The password to set for MQ access
MQ_PASSWORD=
INSTALL_TYPE=
NETMAKER_TENANT_ID=
LICENSE_KEY=
SERVER_IMAGE_TAG=
UI_IMAGE_TAG=
# used for HA - identifies this server vs other servers if unset uses host_name
HOST_NAME=
METRICS_EXPORTER=off
#metrics exporter secret
METRICS_SECRET=
#metrics exporter user
METRICS_USERNAME=netmaker
# Enables DNS Mode, meaning all nodes will set hosts file for private dns settings
DNS_MODE=on
# Enable auto update of netclient ? ENUM:- enabled,disabled | default=enabled
NETCLIENT_AUTO_UPDATE=enabled
# The HTTP API port for Netmaker. Used for API calls / communication from front end.
# If changed, need to change port of BACKEND_URL for netmaker-ui.
API_PORT=8081
EXPORTER_API_PORT=8085
# The "allowed origin" for API requests. Change to restrict where API requests can come from with comma-separated
# URLs. ex:- https://dashboard.netmaker.domain1.com,https://dashboard.netmaker.domain2.com
CORS_ALLOWED_ORIGIN=*
# Show keys permanently in UI (until deleted) as opposed to 1-time display.
DISPLAY_KEYS=on
# Database to use - sqlite, postgres
DATABASE=sqlite
# The address of the mq server. If running from docker compose it will be "mq". Otherwise, need to input address.
# If using "host networking", it will find and detect the IP of the mq container.
# For EMQX websockets use `SERVER_BROKER_ENDPOINT=ws://mq:8083/mqtt`
SERVER_BROKER_ENDPOINT=ws://mq:1883
# Logging verbosity level - 1, 2, or 3
VERBOSITY=1
DEBUG_MODE=off
# Enables the REST backend (API running on API_PORT at SERVER_HTTP_HOST).
REST_BACKEND=on
# If turned "on", Server will not set Host based on remote IP check.
# This is already overridden if SERVER_HOST is set. Turned "off" by default.
DISABLE_REMOTE_IP_CHECK=off
# Whether or not to send telemetry data to help improve Netmaker. Switch to "off" to opt out of sending telemetry.
TELEMETRY=on
###
#
# OAuth section
#
###
# only mentioned domains will be allowded to signup using oauth, by default all domains are allowed
ALLOWED_EMAIL_DOMAINS=*
# "<azure-ad|github|google|oidc>"
AUTH_PROVIDER=
# "<client id of your oauth provider>"
CLIENT_ID=
# "<client secret of your oauth provider>"
CLIENT_SECRET=
# "https://dashboard.<netmaker base domain>"
FRONTEND_URL=
# "<only for azure, you may optionally specify the tenant for the OAuth>"
AZURE_TENANT=
# https://oidc.yourprovider.com - URL of oidc provider
OIDC_ISSUER=
# Duration of JWT token validity in seconds
JWT_VALIDITY_DURATION=43200
# Allow a user to connect to multiple networks simultaneously
RAC_RESTRICT_TO_SINGLE_NETWORK=false
# if turned on data will be cached on to improve performance significantly (IMPORTANT: If HA set to `false` )
CACHING_ENABLED=true
# if turned on netclient checks if peers are reachable over private/LAN address, and choose that as peer endpoint
ENDPOINT_DETECTION=true
# config for sending emails
# mail server host
SMTP_HOST=smtp.gmail.com
# mail server port
SMTP_PORT=587
# sender email
EMAIL_SENDER_ADDR=
# sender smtp user, if unset sender email will be used
EMAIL_SENDER_USER=
# sender smtp password
EMAIL_SENDER_PASSWORD=
# default domain for internal DNS lookup
DEFAULT_DOMAIN=nm.internal
# managed dns setting, set to true to resolve dns entries on netmaker network
MANAGE_DNS=true
# set to true, old acl is supported, otherwise, old acl is disabled
OLD_ACL_SUPPORT=true
# if STUN is set to true, hole punch is called
STUN=true
# Metrics Collection Port
METRICS_PORT=51821
# Metrics Collection interval in minutes
PUBLISH_METRIC_INTERVAL=15
PROMETHEUS_HOST=http://prometheus:9090 #https://prometheus.${NM_DOMAIN}
NETMAKER_METRICS_TARGET=http://netmaker-exporter:8085 #https://netmaker-exporter.${NM_DOMAIN}
METRICS_SECRET=
+1222
View File
File diff suppressed because it is too large Load Diff