From d8bfb292057b05a880b4c72b5463f39ebc5c6f65 Mon Sep 17 00:00:00 2001 From: NW Date: Wed, 24 Jun 2026 11:30:38 +0100 Subject: [PATCH] feat: add tor-proxy service for SSH and admin panel access via Tor - Add tor-proxy/Dockerfile: Alpine + Tor with entrypoint - Add tor-proxy/entrypoint.sh: dynamic torrc generation with env var validation - Update docker-compose.yml: add tor-proxy service with shared tor_proxy_net network - Two Tor hidden services: SSH (port 22) and admin panel (port 80 -> 3001) - Update .env.example: add SSH_HOST_IP, SHOP_CONTAINER, ADMIN_PORT vars --- .env.example | 8 ++++++ docker-compose.yml | 37 ++++++++++++++++++++++++++ tor-proxy/Dockerfile | 12 +++++++++ tor-proxy/entrypoint.sh | 57 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 tor-proxy/Dockerfile create mode 100644 tor-proxy/entrypoint.sh diff --git a/.env.example b/.env.example index 46ed06a..f8a2ccc 100644 --- a/.env.example +++ b/.env.example @@ -39,6 +39,14 @@ WG_ADDRESS= WG_DNS= WG_ALLOWED_IPS=0.0.0.0/0,::/0 +# --- Tor Proxy --- +# SSH backend: куда Tor перенаправляет SSH (по умолчанию хост-машина) +SSH_HOST_IP=host.docker.internal +# Имя контейнера магазина (для проброса админки через Tor) +SHOP_CONTAINER=telegram_shop_prod +# Порт админ-панели внутри контейнера магазина +ADMIN_PORT=3001 + # --- Gitea API (для CI/CD и пайплайна) --- GITEA_API_URL=https://git.softuniq.eu/api/v1 GITEA_TOKEN= \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 402f863..6874b49 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,3 +27,40 @@ services: timeout: 10s retries: 3 start_period: 60s + networks: + - default + - tor_proxy_net + + tor-proxy: + build: + context: ./tor-proxy + dockerfile: Dockerfile + container_name: tor-proxy + environment: + SSH_HOST_IP: ${SSH_HOST_IP:-host.docker.internal} + SHOP_CONTAINER: ${SHOP_CONTAINER:-telegram_shop_prod} + ADMIN_PORT: ${ADMIN_PORT:-3001} + volumes: + - tor_data:/var/lib/tor + extra_hosts: + - "host.docker.internal:host-gateway" + networks: + - default + - tor_proxy_net + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "test -s /var/lib/tor/ssh/hostname && test -s /var/lib/tor/admin/hostname"] + interval: 60s + timeout: 10s + retries: 2 + start_period: 120s + +networks: + tor_proxy_net: + name: tor_proxy_net + driver: bridge + attachable: true + +volumes: + tor_data: + name: tor_proxy_data \ No newline at end of file diff --git a/tor-proxy/Dockerfile b/tor-proxy/Dockerfile new file mode 100644 index 0000000..7aaffaf --- /dev/null +++ b/tor-proxy/Dockerfile @@ -0,0 +1,12 @@ +FROM alpine:3.18 + +RUN apk add --no-cache \ + tor \ + bash + +COPY entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh + +EXPOSE 22 80 + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] \ No newline at end of file diff --git a/tor-proxy/entrypoint.sh b/tor-proxy/entrypoint.sh new file mode 100644 index 0000000..33bdb43 --- /dev/null +++ b/tor-proxy/entrypoint.sh @@ -0,0 +1,57 @@ +#!/bin/bash +set -e + +validate_alnum() { + local val="$1" + local name="$2" + if ! echo "$val" | grep -qE '^[a-zA-Z0-9._-]+$'; then + echo "ERROR: $name contains invalid characters: $val" + exit 1 + fi + if echo "$val" | grep -q $'\n'; then + echo "ERROR: $name contains newlines: $val" + exit 1 + fi +} + +validate_alnum "$SSH_HOST_IP" "SSH_HOST_IP" +validate_alnum "$SHOP_CONTAINER" "SHOP_CONTAINER" +if ! echo "$ADMIN_PORT" | grep -qE '^[0-9]+$'; then + echo "ERROR: ADMIN_PORT must be a number: $ADMIN_PORT" + exit 1 +fi + +if [ "$SSH_HOST_IP" = "host.docker.internal" ]; then + if ! getent hosts host.docker.internal >/dev/null 2>&1; then + GATEWAY=$(ip route | grep default | awk '{print $3}') + if [ -n "$GATEWAY" ]; then + SSH_HOST_IP="$GATEWAY" + echo "host.docker.internal not resolvable, using gateway: $SSH_HOST_IP" + fi + fi +fi + +mkdir -p /var/lib/tor/ssh /var/lib/tor/admin +chmod 700 /var/lib/tor/ssh /var/lib/tor/admin + +cat > /etc/tor/torrc <