rvs-prxy/Design_Doc/Tech_Desc.md

14 KiB

Technical Architecture and Implementation Details

Script Architecture

The deploy-rvs-prxy.sh script is an automated deployment tool supporting both FRP and Traefik modes.

Key Architecture: The script runs directly on the same server where the application is hosted (local deployment), eliminating the need for SSH-based remote operations in Traefik mode.

Deployment Modes:

  1. Setup Reverse Proxy: Deploys new Traefik or FRP Tunnel with Traefik
  2. Setup New Domain: Adds new domain configuration to existing reverse proxy
  3. Setup Port Exposure: Exposes a host port to a container port
  4. Setup External Service Domain: Adds a domain routed to an external web service URL

Configuration File Structure:

  • .env.rvs-prxy - Reverse proxy instance configuration (FRP mode, Traefik version, etc.)
  • .env-<domain> - Domain-specific configuration (DOMAIN_NAME, LOCAL_SERVICE_NAME, LOCAL_SERVICE_PORT)
    • Domain format: Any domain format supported (.com, .net, .org, .org.hk, .com.hk, subdomains, etc.)
    • Filename pattern: Dots replaced with dashes (e.g., app.example.com → .env-app-example-com)
  • .env-port-<port> - Port-based routing configuration (HOST_PORT, CONTAINER_NAME, CONTAINER_PORT)
  • .env-ext-<domain> - External service routing configuration (DOMAIN_NAME, EXTERNAL_URL)
    • Filename pattern: Dots replaced with dashes (e.g., box.example.com/api → .env-ext-box-example-com-api)
  • Multiple domain, port and external service files can coexist

Automatic Mode Detection:

  • No config files → Interactive wizard for proxy setup
  • .env.rvs-prxy exists, proxy not running → Setup Reverse Proxy
  • .env.rvs-prxy exists, proxy running, no domain/port/ext file → Interactive wizard for domain/port/external service setup
  • .env.rvs-prxy + .env-<domain> exists → Setup New Domain
  • .env.rvs-prxy + .env-port-<port> exists → Setup Port Exposure
  • .env.rvs-prxy + .env-ext-<domain> exists → Setup External Service Domain
  • Batch precedence when several types coexist: port files first, then external service files, then domain files

Validation:

  • Proxy Setup Mode: Validates proxy-specific settings (TRAEFIK_VER, LETSENCRYPT_EMAIL for Traefik; FRP_SERVER_ADDR for FRP)
  • Domain Setup Mode: Validates domain-specific settings (DOMAIN_NAME, LOCAL_SERVICE_NAME, LOCAL_SERVICE_PORT)
  • External Service Mode: Validates DOMAIN_NAME and EXTERNAL_URL; requires Traefik mode (FRP=false), since FRP tunnels TCP ports rather than performing HTTP routing

Workflow:

First Run (No .env.rvs-prxy):

  1. Interactive wizard asks for proxy instance settings
  2. Creates .env.rvs-prxy with Traefik/FRP settings
  3. Validation checks proxy settings (TRAEFIK_VER, LETSENCRYPT_EMAIL)
  4. Deploys reverse proxy
  5. Shows message: "Next: Run the script again to setup your first domain or port"

Second Run (.env.rvs-prxy exists, proxy running):

  1. Detects running proxy
  2. Interactive wizard asks for domain, port or external service settings
  3. Creates .env-, .env-port- or .env-ext- with settings
  4. Validation checks domain/port/external service settings
  5. Checks if application service/container is running (skipped for external service routing)
  6. Configures domain/port/external service in existing reverse proxy
  7. Shows: "Service will be accessible at: https://domain.name or http://host-ip:port"

Four Deployment Modes:

  1. Setup Reverse Proxy - Deploys Traefik or FRP Tunnel
  2. Setup New Domain - Adds domain-based routing to a local container
  3. Setup Port Exposure - Adds port-based routing
  4. Setup External Service Domain - Adds domain-based routing to an external URL (Traefik only)

Core Components

1. Environment Loading (load_env)

  • Loads configuration from .env.rvs-prxy.development, .env.rvs-prxy.production, or .env.rvs-prxy
  • Supports files in current directory or script directory
  • Interactive Setup Wizard: If no .env.rvs-prxy file is found, automatically launches a guided setup wizard
  • Normalizes boolean values (FRP variable)
  • Validates required parameters for each mode
  • Supports four modes: proxy setup, domain setup, port exposure, and external service routing
  • Classifies config files into DOMAIN_ENV_FILES, PORT_ENV_FILES, and EXT_ENV_FILES; the first file of each type determines the detected mode (port > external > domain)

Interactive Setup Wizard Features:

  • Step-by-step guided configuration
  • Mode selection (Traefik or FRP) for proxy setup
  • Choice between domain setup, port exposure, or external service routing when proxy exists
  • Mode-specific configuration prompts
  • Sensible defaults for all settings
  • Automatic saving to .env.rvs-prxy, .env-<domain>, .env-port-<port>, or .env-ext-<domain> files

2. Instance Management (prompt_instance_name)

  • Auto-detects existing instances from Docker volumes (FRP mode)
  • Supports instance name override via INSTANCE_NAME environment variable
  • Generates timestamped default names
  • Saves selections to .env for future runs

3. Docker Network Selection (select_docker_network)

  • Lists available Docker networks
  • Supports custom network names
  • Remembers previous selections via .para file in volume (FRP mode)

4. Configuration Generation

  • FRP Mode: Generates frpc.toml with dynamic proxy configurations
  • Traefik Mode: Generates YAML configuration files for routing rules (local deployment)

5. Container Management

  • Creates and manages Docker containers with persistent volumes
  • Supports container restart and lifecycle operations
  • Displays operation commands for user reference

Traefik Mode Implementation (Local Deployment)

Setup Flow (setup_traefik_only)

  1. Pre-request Checks

    • Verify Docker command permissions
  2. Application Service Verification (check_app_service_local)

    • Confirms target service is running locally
    • Verifies connection to traefik-public network
  3. Traefik Container Creation (create_traefik_container_local)

    • Creates traefik_data volume for persistence
    • Generates dynamic configuration with:
      • Dashboard routing
      • HTTP to HTTPS redirect
      • Security headers
    • Creates traefik-public Docker network
    • Uses Docker Compose for container orchestration
    • Detects Docker version for command compatibility (docker compose vs docker-compose)
  4. Service Configuration (configure_traefik_local)

    • Generates service-specific routing rules
    • Supports sub-path routing (e.g., example.com/api)
    • Creates unique configuration filenames to avoid conflicts
    • Supports dynamic configuration loading (no restart required by default)
  5. External Service Configuration (configure_traefik_external, called by setup_external_domain)

    • Generates routing rules for a public domain whose upstream is an external URL
    • Requires Traefik mode (FRP=false) - rejects .env-ext-* files when FRP is enabled
    • No local container lookup and no Docker network requirement for the upstream
    • Persists settings as .env-ext-<domain> via save_ext_env() for later re-runs

Local Docker Operations

  • local_docker_exec(): Executes Docker commands directly on the local server
  • local_volume_copy(): Copies files to Docker volumes using temporary containers

Configuration File Structure

Traefik Main Config (traefik.yml):

http:
  routers:
    api:
      rule: "PathPrefix(`/api`)"
      service: api@internal
    dashboard:
      rule: "PathPrefix(`/dashboard`)"
      service: api@internal
    http-catchall:
      rule: "PathRegexp(`{any:.*}`)"
      entryPoints:
        - "web"
      service: "noop@internal"
      middlewares:
        - "redirect-to-https"
  middlewares:
    api-headers:
      headers:
        customFrameOptionsValue: SAMEORIGIN
        contentTypeNosniff: true
        browserXssFilter: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 15552000
    redirect-to-https:
      redirectScheme:
        scheme: "https"
        permanent: true

Service Config (dynamic, per-service):

http:
  routers:
    <domain>-https:
      rule: 'Host(`<domain>`)'
      service: <domain>-service
      tls:
        certResolver: letsencrypt
      entryPoints:
        - websecure
    <domain>-http:
      rule: 'Host(`<domain>`)'
      service: <domain>-service
      entryPoints:
        - web
      middlewares:
        - redirect-to-https
  services:
    <domain>-service:
      loadBalancer:
        servers:
          - url: 'http://<container-name>:<port>'
        passHostHeader: true

Router, service, and middleware names use the domain (or domain-without-sub-path) as the prefix instead of the container name. This ensures unique resource names when multiple domains share the same backend container. The backend URL still references the container hostname for Docker networking.

External Service Config (dynamic, per-domain, written by configure_traefik_external):

http:
  routers:
    <domain>-https:
      rule: 'Host(`<domain>`)'
      service: <domain>-service
      tls:
        certResolver: letsencrypt
      entryPoints:
        - websecure
    <domain>-http:
      rule: 'Host(`<domain>`)'
      service: <domain>-service
      entryPoints:
        - web
      middlewares:
        - redirect-to-https
  middlewares:
    <domain>-strip:
      stripPrefix:
        paths:
          - '<public-sub-path>'
    <domain>-add:
      addPrefix:
        path: '<origin-base-path>'
  services:
    <domain>-service:
      loadBalancer:
        servers:
          - url: '<scheme>://<external-host>[:<port>]'
        passHostHeader: false

Implementation notes:

  • Config file name: app-ext-<domain-without-sub-path>.yml inside the traefik_data volume, written via a temporary alpine container (local_volume_copy)
  • parse_external_url() splits EXTERNAL_URL into scheme, host, port (default 443 for https, 80 for http) and base path; a non-numeric or out-of-range port is rejected
  • The origin path is never part of the server URL; it is added back with addPrefix so a public sub-path and an origin base path compose correctly
  • passHostHeader: false makes the origin receive its own hostname instead of the public domain, so upstream vhost routing and origin certificates keep working
  • Origin TLS uses the container trust store (no insecureSkipVerify); only the public domain gets a Let's Encrypt certificate
  • DNS for the external host is resolved inside the Traefik container, so check_external_service_reachable() only performs a local nslookup/host check and warns without aborting

Entrypoints Configuration: Default entrypoints (web on :80, websecure on :443) are defined in the docker-compose.yml command args. Additional entrypoints can be configured via the EXTRA_ENTRYPOINTS environment variable in .env.rvs-prxy:

  • Format: name=:port/protocol,name2=:port (comma-separated)
  • Example: quic=:443/udp enables HTTP/3 QUIC on UDP port 443
  • These are injected into both the docker-compose.yml command: section and the docker run command
  • Port mappings (TCP/UDP) are automatically added to the ports: section
  • The parse_extra_entrypoints() function parses the variable and builds:
    • EXTRA_ENTRYPOINT_ARGS: array of --entrypoints.<name>.address=<address> args
    • EXTRA_PORT_MAPPINGS: array of port mappings in host:container/protocol format
    • EXTRA_COMPOSE_ENTRYPOINTS: YAML lines for docker-compose.yml command section
    • EXTRA_COMPOSE_PORTS: YAML lines for docker-compose.yml ports section

FRP Mode Implementation

Setup Flow

  1. Instance Setup: Prompt for instance name
  2. Port Configuration: Prompt for internal service port
  3. FRP Config Generation (generate_frpc_config)
    • Extracts existing configuration from volume
    • Supports dynamic proxy configurations via PROXY_*_PORT variables
    • Generates frpc.toml with TCP proxy rules
  4. Container Start (start_docker_container)
    • Creates volume if not exists
    • Starts frpc container with configuration mounted
  5. Proxy Server Update (update_proxy_server_config)
    • Uploads dynamic configuration script to proxy server
    • Updates docker-compose.app.yml with port mappings and labels
    • Supports domain-based routing configuration

Dynamic Proxy Configuration

The script supports dynamic proxy configurations through environment variables:

  • PROXY_<NAME>_PORT: Defines remote port for proxy
  • PROXY_<NAME>_LOCAL_PORT: Defines local port (defaults to INTERNAL_SERVICE_PORT)

Example:

PROXY_WEB_PORT=12077
PROXY_WEB_LOCAL_PORT=3000
PROXY_SSH_PORT=2222
PROXY_SSH_LOCAL_PORT=22

Proxy Server Configuration Update

The script generates and executes a remote script on the proxy server that:

  • Checks existing configuration status (not_found, up_to_date, outdated)
  • Updates port mappings section in docker-compose.app.yml
  • Updates labels section for Traefik routing
  • Optionally restarts Docker Compose service

File Deployment (Traefik Mode)

The deploy command syncs files from local folder to target server:

  • Source: LOCAL_FOLDER (default: ../out/)
  • Destination: DEST_PATH on target server
  • Uses rsync for efficient synchronization with --delete option

Logging and Debugging

Logging Functions:

  • log_info(): Green [INFO] messages
  • log_error(): Red [ERROR] messages
  • log_warn(): Yellow [WARN] messages
  • log_debug(): Blue [DEBUG] messages (only when DEBUG=true)

Status Commands:

  • check_status_on_target(): Shows Traefik and Application service status
  • view_traefik_logs(): Displays last 50 lines of Traefik logs

Error Handling

  • Uses set -e for immediate exit on errors
  • Validates required parameters before execution
  • Provides detailed error messages with suggested fixes
  • Graceful handling of missing configurations

Version Compatibility

Docker Version Detection:

  • Detects Docker version to determine command syntax
  • Uses docker compose for Docker 20.10.0+
  • Falls back to docker-compose for older versions
  • Handles Compose file version field (deprecated in v2.0+)

Traefik Version:

  • Default: v3.6.9
  • Configurable via TRAEFIK_VER environment variable