mirror of
https://github.com/linuxserver/docker-swag.git
synced 2025-11-02 05:57:45 +09:00
initial env var ingestion for rev proxy configs
This commit is contained in:
@@ -79,6 +79,8 @@ RUN \
|
||||
php84-tokenizer \
|
||||
php84-xmlreader \
|
||||
php84-xsl \
|
||||
python3 \
|
||||
py3-jinja2 \
|
||||
whois && \
|
||||
echo "**** install certbot plugins ****" && \
|
||||
if [ -z ${CERTBOT_VERSION+x} ]; then \
|
||||
|
||||
@@ -79,6 +79,8 @@ RUN \
|
||||
php84-tokenizer \
|
||||
php84-xmlreader \
|
||||
php84-xsl \
|
||||
python3 \
|
||||
py3-jinja2 \
|
||||
whois && \
|
||||
echo "**** install certbot plugins ****" && \
|
||||
if [ -z ${CERTBOT_VERSION+x} ]; then \
|
||||
|
||||
83
README.md
83
README.md
@@ -85,6 +85,88 @@ INSTALL_PIP_PACKAGES=certbot-dns-<plugin>
|
||||
Set the required credentials (usually found in the plugin documentation) in `/config/dns-conf/<plugin>.ini`.
|
||||
It is recommended to attempt obtaining a certificate with `STAGING=true` first to make sure the plugin is working as expected.
|
||||
|
||||
|
||||
### Dynamic Reverse Proxy Configuration via Environment Variables
|
||||
|
||||
SWAG can dynamically generate reverse proxy configuration files directly from environment variables, bypassing the need to manage individual `.conf` files. When any `PROXY_CONFIG_*` variable is detected, this mode is activated, and any existing `.conf` files in `/config/nginx/proxy-confs/` will be removed at startup.
|
||||
|
||||
**Service Definition**
|
||||
|
||||
Each reverse proxy service is defined by an environment variable following the format `PROXY_CONFIG_<SERVICE_NAME>`. The service name will be used as the subdomain (e.g., `SERVICE_NAME.yourdomain.com`), with the special exception of `DEFAULT` (see below). The value of the variable must be a valid JSON object.
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
# Configure the default site (root domain) to proxy to a dashboard service
|
||||
- 'PROXY_CONFIG_DEFAULT={"name": "dashboard", "port": 80, "auth": "authelia", "quic": true}'
|
||||
|
||||
# Simple subdomain service
|
||||
- 'PROXY_CONFIG_HOMARR={"port": 7575, "auth": "authelia"}'
|
||||
|
||||
# Service with a boolean flag for HTTPS backend and QUIC enabled
|
||||
- 'PROXY_CONFIG_HEIMDALL={"port": 443, "https": true, "quic": true}'
|
||||
|
||||
# Complex service with nested objects and lists (incomplete example for syntax)
|
||||
- 'PROXY_CONFIG_PLEX={
|
||||
"port": 32400,
|
||||
"proxy_redirect_off": true,
|
||||
"buffering_off": true,
|
||||
"proxy_set_headers": [
|
||||
{"key": "X-Plex-Client-Identifier", "value": "$$http_x_plex_client_identifier"},
|
||||
{"key": "X-Plex-Device", "value": "$$http_x_plex_device"}
|
||||
],
|
||||
"extra_locations": [
|
||||
{"path": "/library/streams/", "custom_directives": ["proxy_pass_request_headers off"]}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
The available keys in the JSON object correspond to the options in the underlying Nginx template. Common keys include `port`, `https`, `quic`, `auth`, `buffering_off`, `proxy_set_headers`, and `extra_locations`.
|
||||
|
||||
**Configuring the Default Site (Root Domain)**
|
||||
|
||||
To configure the service that responds on your root domain (e.g., `https://yourdomain.com`), use the special service name `DEFAULT`.
|
||||
|
||||
* The environment variable is `PROXY_CONFIG_DEFAULT`.
|
||||
* Unlike subdomain services, the `DEFAULT` configuration **must** include a `"name"` key in its JSON value. This key specifies the name of the container that SWAG should proxy traffic to.
|
||||
* If `PROXY_CONFIG_DEFAULT` is not set, the container will serve the standard SWAG welcome page on the root domain.
|
||||
|
||||
Example:
|
||||
```yaml
|
||||
environment:
|
||||
# This will proxy https://yourdomain.com to the 'dashboard' container on port 80
|
||||
- 'PROXY_CONFIG_DEFAULT={"name": "dashboard", "port": 80, "auth": "none"}'
|
||||
```
|
||||
|
||||
**Authentication Management**
|
||||
|
||||
Authentication can be managed globally or per-service with a clear order of precedence.
|
||||
|
||||
1. **Per-Service Override (Highest Priority):** Add an `auth` key directly inside the service's JSON configuration.
|
||||
* `"auth": "authelia"`: Enables Authelia for this service.
|
||||
* `"auth": "basic"`: Enables Basic Authentication for this service (see below).
|
||||
* `"auth": "none"`: Explicitly disables authentication for this service.
|
||||
|
||||
2. **Global Exclusions:** A comma-separated list of service names to exclude from the global authenticator.
|
||||
* `PROXY_AUTH_EXCLUDE=ntfy,public-dashboard`
|
||||
|
||||
3. **Global Default (Lowest Priority):** A single variable sets the default authentication provider for all services that don't have a per-service override and are not in the exclusion list.
|
||||
* `PROXY_AUTH_PROVIDER=authelia` (can be `ldap`, `authentik`, etc.)
|
||||
|
||||
**Basic Authentication**
|
||||
|
||||
If you set `"auth": "basic"` for any service, you must also provide the credentials using these two environment variables. The container will automatically create the necessary `.htpasswd` file.
|
||||
|
||||
* `PROXY_AUTH_BASIC_USER`: The username for basic authentication.
|
||||
* `PROXY_AUTH_BASIC_PASS`: The password for basic authentication.
|
||||
|
||||
Example:
|
||||
```yaml
|
||||
environment:
|
||||
- 'PROXY_CONFIG_PORTAINER={"port": 9000, "auth": "basic"}'
|
||||
- PROXY_AUTH_BASIC_USER=myadmin
|
||||
- PROXY_AUTH_BASIC_PASS=supersecretpassword
|
||||
```
|
||||
|
||||
### Security and password protection
|
||||
|
||||
* The container detects changes to url and subdomains, revokes existing certs and generates new ones during start.
|
||||
@@ -433,6 +515,7 @@ Once registered you can define the dockerfile to use with `-f Dockerfile.aarch64
|
||||
|
||||
## Versions
|
||||
|
||||
* **02.09.25:** - Add ability to define proxy configurations via environment variables.
|
||||
* **18.07.25:** - Rebase to Alpine 3.22 with PHP 8.4. Add QUIC support. Drop PHP bindings for mcrypt as it is no longer maintained.
|
||||
* **05.05.25:** - Disable Certbot's built in log rotation.
|
||||
* **19.01.25:** - Add [Auto Reload](https://github.com/linuxserver/docker-mods/tree/swag-auto-reload) functionality to SWAG.
|
||||
|
||||
@@ -82,6 +82,88 @@ app_setup_block: |
|
||||
Set the required credentials (usually found in the plugin documentation) in `/config/dns-conf/<plugin>.ini`.
|
||||
It is recommended to attempt obtaining a certificate with `STAGING=true` first to make sure the plugin is working as expected.
|
||||
|
||||
|
||||
### Dynamic Reverse Proxy Configuration via Environment Variables
|
||||
|
||||
SWAG can dynamically generate reverse proxy configuration files directly from environment variables, bypassing the need to manage individual `.conf` files. When any `PROXY_CONFIG_*` variable is detected, this mode is activated, and any existing `.conf` files in `/config/nginx/proxy-confs/` will be removed at startup.
|
||||
|
||||
**Service Definition**
|
||||
|
||||
Each reverse proxy service is defined by an environment variable following the format `PROXY_CONFIG_<SERVICE_NAME>`. The service name will be used as the subdomain (e.g., `SERVICE_NAME.yourdomain.com`), with the special exception of `DEFAULT` (see below). The value of the variable must be a valid JSON object.
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
# Configure the default site (root domain) to proxy to a dashboard service
|
||||
- 'PROXY_CONFIG_DEFAULT={"name": "dashboard", "port": 80, "auth": "authelia", "quic": true}'
|
||||
|
||||
# Simple subdomain service
|
||||
- 'PROXY_CONFIG_HOMARR={"port": 7575, "auth": "authelia"}'
|
||||
|
||||
# Service with a boolean flag for HTTPS backend and QUIC enabled
|
||||
- 'PROXY_CONFIG_HEIMDALL={"port": 443, "https": true, "quic": true}'
|
||||
|
||||
# Complex service with nested objects and lists (incomplete example for syntax)
|
||||
- 'PROXY_CONFIG_PLEX={
|
||||
"port": 32400,
|
||||
"proxy_redirect_off": true,
|
||||
"buffering_off": true,
|
||||
"proxy_set_headers": [
|
||||
{"key": "X-Plex-Client-Identifier", "value": "$$http_x_plex_client_identifier"},
|
||||
{"key": "X-Plex-Device", "value": "$$http_x_plex_device"}
|
||||
],
|
||||
"extra_locations": [
|
||||
{"path": "/library/streams/", "custom_directives": ["proxy_pass_request_headers off"]}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
The available keys in the JSON object correspond to the options in the underlying Nginx template. Common keys include `port`, `https`, `quic`, `auth`, `buffering_off`, `proxy_set_headers`, and `extra_locations`.
|
||||
|
||||
**Configuring the Default Site (Root Domain)**
|
||||
|
||||
To configure the service that responds on your root domain (e.g., `https://yourdomain.com`), use the special service name `DEFAULT`.
|
||||
|
||||
* The environment variable is `PROXY_CONFIG_DEFAULT`.
|
||||
* Unlike subdomain services, the `DEFAULT` configuration **must** include a `"name"` key in its JSON value. This key specifies the name of the container that SWAG should proxy traffic to.
|
||||
* If `PROXY_CONFIG_DEFAULT` is not set, the container will serve the standard SWAG welcome page on the root domain.
|
||||
|
||||
Example:
|
||||
```yaml
|
||||
environment:
|
||||
# This will proxy https://yourdomain.com to the 'dashboard' container on port 80
|
||||
- 'PROXY_CONFIG_DEFAULT={"name": "dashboard", "port": 80, "auth": "none"}'
|
||||
```
|
||||
|
||||
**Authentication Management**
|
||||
|
||||
Authentication can be managed globally or per-service with a clear order of precedence.
|
||||
|
||||
1. **Per-Service Override (Highest Priority):** Add an `auth` key directly inside the service's JSON configuration.
|
||||
* `"auth": "authelia"`: Enables Authelia for this service.
|
||||
* `"auth": "basic"`: Enables Basic Authentication for this service (see below).
|
||||
* `"auth": "none"`: Explicitly disables authentication for this service.
|
||||
|
||||
2. **Global Exclusions:** A comma-separated list of service names to exclude from the global authenticator.
|
||||
* `PROXY_AUTH_EXCLUDE=ntfy,public-dashboard`
|
||||
|
||||
3. **Global Default (Lowest Priority):** A single variable sets the default authentication provider for all services that don't have a per-service override and are not in the exclusion list.
|
||||
* `PROXY_AUTH_PROVIDER=authelia` (can be `ldap`, `authentik`, etc.)
|
||||
|
||||
**Basic Authentication**
|
||||
|
||||
If you set `"auth": "basic"` for any service, you must also provide the credentials using these two environment variables. The container will automatically create the necessary `.htpasswd` file.
|
||||
|
||||
* `PROXY_AUTH_BASIC_USER`: The username for basic authentication.
|
||||
* `PROXY_AUTH_BASIC_PASS`: The password for basic authentication.
|
||||
|
||||
Example:
|
||||
```yaml
|
||||
environment:
|
||||
- 'PROXY_CONFIG_PORTAINER={"port": 9000, "auth": "basic"}'
|
||||
- PROXY_AUTH_BASIC_USER=myadmin
|
||||
- PROXY_AUTH_BASIC_PASS=supersecretpassword
|
||||
```
|
||||
|
||||
### Security and password protection
|
||||
|
||||
* The container detects changes to url and subdomains, revokes existing certs and generates new ones during start.
|
||||
@@ -218,6 +300,7 @@ init_diagram: |
|
||||
"swag:latest" <- Base Images
|
||||
# changelog
|
||||
changelogs:
|
||||
- {date: "02.09.25:", desc: "Add ability to define proxy configurations via environment variables."}
|
||||
- {date: "18.07.25:", desc: "Rebase to Alpine 3.22 with PHP 8.4. Add QUIC support. Drop PHP bindings for mcrypt as it is no longer maintained."}
|
||||
- {date: "05.05.25:", desc: "Disable Certbot's built in log rotation."}
|
||||
- {date: "19.01.25:", desc: "Add [Auto Reload](https://github.com/linuxserver/docker-mods/tree/swag-auto-reload) functionality to SWAG."}
|
||||
|
||||
153
root/app/config-generator/generate_configs.py
Normal file
153
root/app/config-generator/generate_configs.py
Normal file
@@ -0,0 +1,153 @@
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
# --- Configuration ---
|
||||
TEMPLATE_DIR = '/app/config-generator/templates'
|
||||
PROXY_OUTPUT_DIR = '/config/nginx/proxy-confs'
|
||||
DEFAULT_CONF_OUTPUT = '/config/nginx/site-confs/default.conf'
|
||||
HTPASSWD_FILE = '/config/nginx/.htpasswd'
|
||||
# ---------------------
|
||||
|
||||
def process_service_config(service_name, service_config_json, global_auth_provider, auth_exclude_list):
|
||||
"""Processes a single service configuration, including auth logic."""
|
||||
service_config = json.loads(service_config_json)
|
||||
|
||||
# The default service doesn't have a subdomain name in the traditional sense
|
||||
if service_name.lower() == 'default':
|
||||
# We still need a target container name, let the user define it or raise an error
|
||||
if 'name' not in service_config:
|
||||
raise ValueError("PROXY_CONFIG_DEFAULT must contain a 'name' key specifying the target container name.")
|
||||
else:
|
||||
service_config['name'] = service_name
|
||||
|
||||
# --- Authentication Logic ---
|
||||
auth_provider = 'none' # Default
|
||||
# 1. Per-service override
|
||||
if 'auth' in service_config:
|
||||
auth_provider = service_config['auth']
|
||||
print(f" - Found per-service auth override: '{auth_provider}'")
|
||||
# 2. Global provider check
|
||||
elif global_auth_provider and service_name not in auth_exclude_list:
|
||||
auth_provider = global_auth_provider
|
||||
print(f" - Applying global auth provider: '{auth_provider}'")
|
||||
# 3. Otherwise, no auth
|
||||
else:
|
||||
if service_name in auth_exclude_list:
|
||||
print(f" - Service is in global exclude list. No auth.")
|
||||
else:
|
||||
print(f" - No auth provider specified.")
|
||||
|
||||
service_config['auth_provider'] = auth_provider
|
||||
return service_config
|
||||
|
||||
def generate_configs():
|
||||
"""
|
||||
Generates Nginx config files from PROXY_CONFIG environment variables and a Jinja2 template.
|
||||
"""
|
||||
print("--- Starting Nginx Config Generation from Environment Variables ---")
|
||||
|
||||
# Ensure output directories exist
|
||||
os.makedirs(PROXY_OUTPUT_DIR, exist_ok=True)
|
||||
os.makedirs(os.path.dirname(DEFAULT_CONF_OUTPUT), exist_ok=True)
|
||||
print(f"Output directories are ready.")
|
||||
|
||||
# Get global auth settings from environment variables
|
||||
global_auth_provider = os.environ.get('PROXY_AUTH_PROVIDER')
|
||||
auth_exclude_list = os.environ.get('PROXY_AUTH_EXCLUDE', '').split(',')
|
||||
auth_exclude_list = [name.strip() for name in auth_exclude_list if name.strip()]
|
||||
|
||||
# Get basic auth credentials
|
||||
basic_auth_user = os.environ.get('PROXY_AUTH_BASIC_USER')
|
||||
basic_auth_pass = os.environ.get('PROXY_AUTH_BASIC_PASS')
|
||||
basic_auth_configured = False
|
||||
|
||||
print(f"Global Auth Provider: {global_auth_provider}")
|
||||
print(f"Auth Exclude List: {auth_exclude_list}")
|
||||
|
||||
# Collect and process service configurations
|
||||
subdomain_services = []
|
||||
default_service = None
|
||||
|
||||
for key, value in os.environ.items():
|
||||
if key.startswith('PROXY_CONFIG_'):
|
||||
service_name = key.replace('PROXY_CONFIG_', '').lower()
|
||||
print(f" Processing service: {service_name}")
|
||||
print(value)
|
||||
try:
|
||||
service_config = process_service_config(service_name, value, global_auth_provider, auth_exclude_list)
|
||||
|
||||
# Handle Basic Auth File Creation
|
||||
if service_config['auth_provider'] == 'basic' and not basic_auth_configured:
|
||||
if basic_auth_user and basic_auth_pass:
|
||||
print(f" - Configuring Basic Auth with user '{basic_auth_user}'.")
|
||||
try:
|
||||
os.makedirs(os.path.dirname(HTPASSWD_FILE), exist_ok=True)
|
||||
command = ['htpasswd', '-bc', HTPASSWD_FILE, basic_auth_user, basic_auth_pass]
|
||||
subprocess.run(command, check=True, capture_output=True, text=True)
|
||||
print(f" - Successfully created '{HTPASSWD_FILE}'.")
|
||||
basic_auth_configured = True
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f" [!!] ERROR: 'htpasswd' command failed: {e.stderr}. Basic auth will not be enabled.")
|
||||
service_config['auth_provider'] = 'none'
|
||||
except FileNotFoundError:
|
||||
print(f" [!!] ERROR: 'htpasswd' command not found. Basic auth will not be enabled.")
|
||||
service_config['auth_provider'] = 'none'
|
||||
else:
|
||||
print(f" [!!] WARNING: 'auth: basic' is set, but PROXY_AUTH_BASIC_USER or PROXY_AUTH_BASIC_PASS is missing. Skipping auth.")
|
||||
service_config['auth_provider'] = 'none'
|
||||
|
||||
if service_name == 'default':
|
||||
default_service = service_config
|
||||
else:
|
||||
subdomain_services.append(service_config)
|
||||
|
||||
except (json.JSONDecodeError, ValueError) as e:
|
||||
print(f" [!!] ERROR: Could not parse or validate config for {service_name}: {e}. Skipping.")
|
||||
except Exception as e:
|
||||
print(f" [!!] ERROR: An unexpected error occurred processing {service_name}: {e}. Skipping.")
|
||||
|
||||
# Set up Jinja2 environment
|
||||
try:
|
||||
env = Environment(loader=FileSystemLoader(TEMPLATE_DIR), trim_blocks=True, lstrip_blocks=True)
|
||||
proxy_template = env.get_template('proxy.conf.j2')
|
||||
default_template = env.get_template('default.conf.j2')
|
||||
print("\nJinja2 templates loaded successfully.")
|
||||
except Exception as e:
|
||||
print(f"ERROR: Failed to load Jinja2 templates from '{TEMPLATE_DIR}': {e}. Exiting.")
|
||||
return
|
||||
|
||||
# Generate default site config if specified
|
||||
if default_service:
|
||||
print("\n--- Generating Default Site Config ---")
|
||||
try:
|
||||
rendered_content = default_template.render(item=default_service)
|
||||
with open(DEFAULT_CONF_OUTPUT, 'w') as f:
|
||||
f.write(rendered_content)
|
||||
print(f" [OK] Generated {os.path.basename(DEFAULT_CONF_OUTPUT)}")
|
||||
except Exception as e:
|
||||
print(f" [!!] ERROR: Failed to render or write default config: {e}")
|
||||
else:
|
||||
print("\n--- PROXY_CONFIG_DEFAULT not set, default site config will not be generated. ---")
|
||||
|
||||
|
||||
# Generate subdomain proxy configs
|
||||
print("\n--- Generating Subdomain Proxy Configs ---")
|
||||
if not subdomain_services:
|
||||
print("No subdomain services found to configure.")
|
||||
for service in subdomain_services:
|
||||
filename = f"{service['name']}.subdomain.conf"
|
||||
output_path = os.path.join(PROXY_OUTPUT_DIR, filename)
|
||||
try:
|
||||
rendered_content = proxy_template.render(item=service)
|
||||
with open(output_path, 'w') as f:
|
||||
f.write(rendered_content)
|
||||
print(f" [OK] Generated {filename}")
|
||||
except Exception as e:
|
||||
print(f" [!!] ERROR: Failed to render or write config for {service['name']}: {e}")
|
||||
|
||||
print("\n--- Generation Complete ---")
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_configs()
|
||||
103
root/app/config-generator/templates/default.conf.j2
Normal file
103
root/app/config-generator/templates/default.conf.j2
Normal file
@@ -0,0 +1,103 @@
|
||||
## Version 2025/08/28
|
||||
# THIS FILE IS AUTO-GENERATED BY THE CONTAINER. DO NOT EDIT.
|
||||
#
|
||||
# This is the default server block, handling requests to the root domain.
|
||||
|
||||
# redirect all traffic to https
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# main server block
|
||||
server {
|
||||
listen 443 ssl default_server;
|
||||
{% if item.quic %}
|
||||
listen 443 quic reuseport default_server;
|
||||
{% else %}
|
||||
# listen 443 quic reuseport default_server;
|
||||
{% endif %}
|
||||
listen [::]:443 ssl default_server;
|
||||
{% if item.quic %}
|
||||
listen [::]:443 quic reuseport default_server;
|
||||
{% else %}
|
||||
# listen [::]:443 quic reuseport default_server;
|
||||
{% endif %}
|
||||
|
||||
server_name _;
|
||||
|
||||
include /config/nginx/ssl.conf;
|
||||
|
||||
client_max_body_size {{ item.client_max_body_size | default('0') }};
|
||||
{% if item.proxy_redirect_off %}
|
||||
proxy_redirect off;
|
||||
{% endif %}
|
||||
{% if item.buffering_off %}
|
||||
proxy_buffering off;
|
||||
{% endif %}
|
||||
|
||||
{% if item.auth_provider and item.auth_provider not in ['none', 'basic'] %}
|
||||
# enable for {{ item.auth_provider }}
|
||||
include /config/nginx/{{ item.auth_provider }}-server.conf;
|
||||
{% endif %}
|
||||
|
||||
location / {
|
||||
{% if item.auth_provider == 'basic' %}
|
||||
# enable for basic auth
|
||||
auth_basic "Restricted";
|
||||
auth_basic_user_file /config/nginx/.htpasswd;
|
||||
{% elif item.auth_provider and item.auth_provider != 'none' %}
|
||||
# enable for {{ item.auth_provider }}
|
||||
include /config/nginx/{{ item.auth_provider }}-location.conf;
|
||||
{% else %}
|
||||
# No authentication enabled for this service.
|
||||
{% endif %}
|
||||
|
||||
include /config/nginx/proxy.conf;
|
||||
include /config/nginx/resolver.conf;
|
||||
set $upstream_app {{ item.name }};
|
||||
set $upstream_port {{ item.port }};
|
||||
set $upstream_proto {% if item.https %}https{% else %}http{% endif %};
|
||||
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
|
||||
{% if item.proxy_set_headers %}
|
||||
{% for header in item.proxy_set_headers %}
|
||||
proxy_set_header {{ header.key }} {{ header.value }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if item.custom_directives %}
|
||||
{% for directive in item.custom_directives %}
|
||||
{{ directive }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
{% if item.extra_locations %}
|
||||
{% for loc in item.extra_locations %}
|
||||
location {{ loc.path }} {
|
||||
include /config/nginx/proxy.conf;
|
||||
include /config/nginx/resolver.conf;
|
||||
set $upstream_app {{ loc.app | default(item.name) }};
|
||||
set $upstream_port {{ loc.port | default(item.port) }};
|
||||
set $upstream_proto {% if loc.https %}https{% elif item.https and loc.https is not defined %}https{% else %}http{% endif %};
|
||||
proxy_pass $upstream_proto://$upstream_app:$upstream_port{% if loc.proxy_pass_path %}{{ loc.proxy_pass_path }}{% endif %};
|
||||
{% if loc.proxy_set_headers %}
|
||||
{% for header in loc.proxy_set_headers %}
|
||||
proxy_set_header {{ header.key }} {{ header.value }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if loc.custom_directives %}
|
||||
{% for directive in loc.custom_directives %}
|
||||
{{ directive }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
# enable subdomain method reverse proxy confs
|
||||
include /config/nginx/proxy-confs/*.conf;
|
||||
141
root/app/config-generator/templates/proxy.conf.j2
Normal file
141
root/app/config-generator/templates/proxy.conf.j2
Normal file
@@ -0,0 +1,141 @@
|
||||
## Version 2025/08/28
|
||||
# THIS FILE IS AUTO-GENERATED BY THE CONTAINER. DO NOT EDIT.
|
||||
#
|
||||
# make sure that your {{ item.name }} container is named {{ item.name }}
|
||||
# make sure that your dns has a cname set for {{ item.name }}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
{% if item.quic %}
|
||||
listen 443 quic reuseport;
|
||||
{% else %}
|
||||
# listen 443 quic reuseport;
|
||||
{% endif %}
|
||||
listen [::]:443 ssl;
|
||||
{% if item.quic %}
|
||||
listen [::]:443 quic reuseport;
|
||||
{% else %}
|
||||
# listen [::]:443 quic reuseport;
|
||||
{% endif %}
|
||||
|
||||
server_name {{ item.name }}.*;
|
||||
|
||||
include /config/nginx/ssl.conf;
|
||||
|
||||
client_max_body_size {{ item.client_max_body_size | default('0') }};
|
||||
{% if item.proxy_redirect_off %}
|
||||
proxy_redirect off;
|
||||
{% endif %}
|
||||
{% if item.buffering_off %}
|
||||
proxy_buffering off;
|
||||
{% endif %}
|
||||
|
||||
{% if item.auth_provider and item.auth_provider not in ['none', 'basic'] %}
|
||||
# enable for {{ item.auth_provider }}
|
||||
include /config/nginx/{{ item.auth_provider }}-server.conf;
|
||||
{% endif %}
|
||||
|
||||
location / {
|
||||
{% if item.auth_provider == 'basic' %}
|
||||
# enable for basic auth
|
||||
auth_basic "Restricted";
|
||||
auth_basic_user_file /config/nginx/.htpasswd;
|
||||
{% elif item.auth_provider and item.auth_provider != 'none' %}
|
||||
# enable for {{ item.auth_provider }}
|
||||
include /config/nginx/{{ item.auth_provider }}-location.conf;
|
||||
{% else %}
|
||||
# No authentication enabled for this service.
|
||||
{% endif %}
|
||||
|
||||
include /config/nginx/proxy.conf;
|
||||
include /config/nginx/resolver.conf;
|
||||
set $upstream_app {{ item.name }};
|
||||
set $upstream_port {{ item.port }};
|
||||
set $upstream_proto {% if item.https %}https{% else %}http{% endif %};
|
||||
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
|
||||
{% if item.hide_xframe %}
|
||||
proxy_hide_header X-Frame-Options;
|
||||
{% endif %}
|
||||
{% if item.iframe_friendly %}
|
||||
# Uncomment to allow loading in an iframe (i.e. Organizr)
|
||||
#proxy_hide_header X-Frame-Options;
|
||||
{% endif %}
|
||||
{% if item.hide_x_forwarded_port %}
|
||||
# Hide proxy port to prevent CSRF errors
|
||||
proxy_hide_header X-Forwarded-Port;
|
||||
{% endif %}
|
||||
{% if item.set_x_scheme %}
|
||||
proxy_set_header X-Scheme https;
|
||||
{% endif %}
|
||||
{% if item.websockets %}
|
||||
proxy_buffering off;
|
||||
proxy_socket_keepalive on;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
|
||||
proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
|
||||
proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
|
||||
{% endif %}
|
||||
{% if item.proxy_pass_headers %}
|
||||
{% for header in item.proxy_pass_headers %}
|
||||
proxy_pass_header {{ header }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if item.proxy_set_headers %}
|
||||
{% for header in item.proxy_set_headers %}
|
||||
proxy_set_header {{ header.key }} {{ header.value }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if item.proxy_hide_headers %}
|
||||
{% for header in item.proxy_hide_headers %}
|
||||
proxy_hide_header {{ header }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if item.add_headers %}
|
||||
{% for header in item.add_headers %}
|
||||
add_header {{ header.key }} "{{ header.value }}";
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if item.custom_directives %}
|
||||
{% for directive in item.custom_directives %}
|
||||
{{ directive }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
}
|
||||
{% if item.api %}
|
||||
location ~ (?:/{{ item.name }})?/api {
|
||||
include /config/nginx/proxy.conf;
|
||||
include /config/nginx/resolver.conf;
|
||||
set $upstream_app {{ item.name }};
|
||||
set $upstream_port {{ item.port }};
|
||||
set $upstream_proto {% if item.https %}https{% else %}http{% endif %};
|
||||
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
|
||||
}
|
||||
{% endif %}
|
||||
{% if item.extra_locations %}
|
||||
{% for loc in item.extra_locations %}
|
||||
location ~ (?:/{{ item.name }})?{{ loc.path }} {
|
||||
include /config/nginx/proxy.conf;
|
||||
include /config/nginx/resolver.conf;
|
||||
set $upstream_app {{ loc.app | default(item.name) }};
|
||||
set $upstream_port {{ loc.port | default(item.port) }};
|
||||
set $upstream_proto {% if loc.https %}https{% elif item.https and loc.https is not defined %}https{% else %}http{% endif %};
|
||||
proxy_pass $upstream_proto://$upstream_app:$upstream_port{% if loc.proxy_pass_path %}{{ loc.proxy_pass_path }}{% endif %};
|
||||
{% if loc.proxy_set_headers %}
|
||||
{% for header in loc.proxy_set_headers %}
|
||||
proxy_set_header {{ header.key }} {{ header.value }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if loc.proxy_hide_headers %}
|
||||
{% for header in loc.proxy_hide_headers %}
|
||||
proxy_hide_header {{ header }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if loc.custom_directives %}
|
||||
{% for directive in loc.custom_directives %}
|
||||
{{ directive }};
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
}
|
||||
@@ -42,3 +42,20 @@ fi
|
||||
if [[ ! -f /config/nginx/ldap-server.conf ]]; then
|
||||
cp /defaults/nginx/ldap-server.conf.sample /config/nginx/ldap-server.conf
|
||||
fi
|
||||
|
||||
# check if any PROXY_CONFIG environment variables are set
|
||||
if env | grep -q "^PROXY_CONFIG_"; then
|
||||
echo "INFO: Found PROXY_CONFIG environment variables. Generating Nginx configs from environment..."
|
||||
# clean the target directory to ensure a fresh start
|
||||
echo "INFO: Cleaning /config/nginx/proxy-confs/ of existing files..."
|
||||
rm -f /config/nginx/proxy-confs/*
|
||||
# run the Python generator script
|
||||
echo "INFO: Running python config generator..."
|
||||
if ! python3 /app/config-generator/generate_configs.py; then
|
||||
echo "ERROR: The python config generator script failed. Please check the logs above. Container will not start."
|
||||
exit 1
|
||||
fi
|
||||
echo "INFO: Config generation complete."
|
||||
else
|
||||
echo "INFO: No PROXY_CONFIG variables found. User is expected to manage /config/nginx/proxy-confs/ manually."
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user