feat: forward tcp traffic easily
This commit is contained in:
parent
bd2e806aa1
commit
ebeb6d5c6b
@ -206,11 +206,44 @@ The role implements proper data isolation for both database backends:
|
|||||||
|
|
||||||
The compose file is deployed to `{{ podman_projects_dir }}/immich/docker-compose.yml` and managed via a systemd service.
|
The compose file is deployed to `{{ podman_projects_dir }}/immich/docker-compose.yml` and managed via a systemd service.
|
||||||
|
|
||||||
|
## Nginx Reverse Proxy with ACME/Let's Encrypt
|
||||||
|
|
||||||
|
The role includes an Nginx vhost template with native ACME support for automatic HTTPS certificate management.
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
1. Nginx role deployed with `acme_email` configured
|
||||||
|
2. Port 80/443 accessible from internet (for ACME HTTP-01 challenge)
|
||||||
|
3. DNS pointing to your server
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```yaml
|
||||||
|
# Enable Nginx reverse proxy
|
||||||
|
immich_nginx_enabled: true
|
||||||
|
immich_nginx_hostname: "blog.hello.com"
|
||||||
|
|
||||||
|
# In nginx role configuration (host_vars or group_vars)
|
||||||
|
acme_email: "admin@carabosse.cloud"
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Deploys HTTPS vhost with automatic Let's Encrypt certificate
|
||||||
|
- HTTP → HTTPS redirect
|
||||||
|
- Proxies to Immich container on localhost
|
||||||
|
- Handles WebSocket upgrades for live photos
|
||||||
|
- Large file upload support (50GB max)
|
||||||
|
|
||||||
|
**ACME automatic features:**
|
||||||
|
- Certificate issuance on first deployment
|
||||||
|
- Automatic renewal
|
||||||
|
- HTTP-01 challenge handling
|
||||||
|
|
||||||
## Post-Installation
|
## Post-Installation
|
||||||
|
|
||||||
After deployment:
|
After deployment:
|
||||||
|
|
||||||
1. Access Immich at `http://<host-ip>:2283`
|
1. Access Immich at:
|
||||||
|
- **With Nginx enabled**: `https://{{ immich_nginx_hostname }}`
|
||||||
|
- **Without Nginx**: `http://<host-ip>:{{ immich_port }}`
|
||||||
2. Create an admin account on first login
|
2. Create an admin account on first login
|
||||||
3. Configure mobile/desktop apps to point to your server
|
3. Configure mobile/desktop apps to point to your server
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,41 @@
|
|||||||
|
# Immich vhost with Let's Encrypt (Certbot)
|
||||||
|
# Managed by Ansible - DO NOT EDIT MANUALLY
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name {{ immich_nginx_hostname }};
|
server_name {{ immich_nginx_hostname }};
|
||||||
|
|
||||||
|
# Certbot webroot for ACME challenges
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect to HTTPS
|
||||||
|
location / {
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name {{ immich_nginx_hostname }};
|
||||||
|
|
||||||
|
# Let's Encrypt certificates (managed by Certbot)
|
||||||
|
ssl_certificate /etc/letsencrypt/live/{{ immich_nginx_hostname }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/{{ immich_nginx_hostname }}/privkey.pem;
|
||||||
|
|
||||||
|
# SSL configuration
|
||||||
|
ssl_protocols {{ nginx_ssl_protocols }};
|
||||||
|
ssl_prefer_server_ciphers {{ 'on' if nginx_ssl_prefer_server_ciphers else 'off' }};
|
||||||
|
|
||||||
|
{% if nginx_log_backend == 'journald' %}
|
||||||
|
access_log syslog:server=unix:/dev/log,nohostname,tag=nginx_immich;
|
||||||
|
error_log syslog:server=unix:/dev/log,nohostname,tag=nginx_immich;
|
||||||
|
{% else %}
|
||||||
|
access_log /var/log/nginx/{{ immich_nginx_hostname }}_access.log main;
|
||||||
|
error_log /var/log/nginx/{{ immich_nginx_hostname }}_error.log;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
client_max_body_size 50000M;
|
client_max_body_size 50000M;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
|||||||
@ -9,6 +9,8 @@ Installs and configures Nginx as a reverse proxy for web applications with modul
|
|||||||
- Configurable logging backend (journald or traditional files)
|
- Configurable logging backend (journald or traditional files)
|
||||||
- Automatic logrotate for file-based logging
|
- Automatic logrotate for file-based logging
|
||||||
- SSL/TLS configuration
|
- SSL/TLS configuration
|
||||||
|
- **Native ACME/Let's Encrypt support** (Nginx 1.25.0+)
|
||||||
|
- **Transparent proxy forwarding** (HTTP/HTTPS to other hosts)
|
||||||
|
|
||||||
## Service Integration Pattern
|
## Service Integration Pattern
|
||||||
|
|
||||||
@ -32,6 +34,33 @@ Each service role should deploy its own vhost config:
|
|||||||
notify: Reload nginx
|
notify: Reload nginx
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Transparent Proxy Forwarding
|
||||||
|
|
||||||
|
Forward TCP traffic from this Nginx instance to services on other hosts using the `stream` module (layer 4 proxy).
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```yaml
|
||||||
|
nginx_forwarder:
|
||||||
|
"blog.hello.com":
|
||||||
|
forward_to: "my.host.lan"
|
||||||
|
http: true # Forward port 80 (default: true)
|
||||||
|
https: true # Forward port 443 (default: true)
|
||||||
|
```
|
||||||
|
|
||||||
|
**How it works:**
|
||||||
|
- **Stream-based TCP proxy** (layer 4, not HTTP layer 7)
|
||||||
|
- No protocol inspection - just forwards raw TCP packets
|
||||||
|
- **HTTPS passes through encrypted** - backend host handles TLS termination
|
||||||
|
- HTTP also uses stream (simpler, but no HTTP features like headers/logging)
|
||||||
|
|
||||||
|
**Use case:** Omega (gateway) forwards all traffic to Andromeda (internal server) that handles its own TLS certificates.
|
||||||
|
|
||||||
|
**Important notes:**
|
||||||
|
- Stream configs deployed to `/etc/nginx/streams.d/`
|
||||||
|
- No HTTP logging (stream doesn't understand HTTP protocol)
|
||||||
|
- No X-Forwarded-For headers (transparent TCP forwarding)
|
||||||
|
- Only ONE domain can use port 443 forwarding (TCP port limitation)
|
||||||
|
|
||||||
## Logging Backends
|
## Logging Backends
|
||||||
|
|
||||||
**journald (default):**
|
**journald (default):**
|
||||||
@ -64,10 +93,19 @@ tail -f /var/log/nginx/error.log
|
|||||||
|
|
||||||
# List loaded vhosts
|
# List loaded vhosts
|
||||||
ls -la /etc/nginx/conf.d/
|
ls -la /etc/nginx/conf.d/
|
||||||
|
|
||||||
|
# List stream forwarders
|
||||||
|
ls -la /etc/nginx/streams.d/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration Variables
|
||||||
|
|
||||||
|
See [defaults/main.yml](defaults/main.yml) for all available variables.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- [Nginx Documentation](https://nginx.org/en/docs/)
|
- [Nginx Documentation](https://nginx.org/en/docs/)
|
||||||
|
- [Nginx ACME Support](https://blog.nginx.org/blog/native-support-for-acme-protocol)
|
||||||
|
- [Nginx Stream Module](https://nginx.org/en/docs/stream/ngx_stream_core_module.html)
|
||||||
- [Nginx Logging](https://nginx.org/en/docs/syslog.html)
|
- [Nginx Logging](https://nginx.org/en/docs/syslog.html)
|
||||||
- [Nginx SSL/TLS](https://nginx.org/en/docs/http/configuring_https_servers.html)
|
- [Nginx SSL/TLS](https://nginx.org/en/docs/http/configuring_https_servers.html)
|
||||||
@ -2,6 +2,9 @@
|
|||||||
# Nginx configuration directory for service vhosts
|
# Nginx configuration directory for service vhosts
|
||||||
nginx_conf_dir: /etc/nginx/conf.d
|
nginx_conf_dir: /etc/nginx/conf.d
|
||||||
|
|
||||||
|
# Nginx stream configuration directory (TCP/UDP proxies)
|
||||||
|
nginx_streams_dir: /etc/nginx/streams.d
|
||||||
|
|
||||||
# Worker processes (auto = number of CPU cores)
|
# Worker processes (auto = number of CPU cores)
|
||||||
nginx_worker_processes: auto
|
nginx_worker_processes: auto
|
||||||
|
|
||||||
@ -23,3 +26,16 @@ nginx_log_backend: journald
|
|||||||
nginx_logrotate_rotate: 14 # Keep 14 days of logs
|
nginx_logrotate_rotate: 14 # Keep 14 days of logs
|
||||||
nginx_logrotate_frequency: daily # daily|weekly|monthly
|
nginx_logrotate_frequency: daily # daily|weekly|monthly
|
||||||
nginx_logrotate_compress: true # Compress rotated logs
|
nginx_logrotate_compress: true # Compress rotated logs
|
||||||
|
|
||||||
|
# Forwarder configuration (transparent proxy to other hosts)
|
||||||
|
# Example:
|
||||||
|
# nginx_forwarder:
|
||||||
|
# "blog.hello.com":
|
||||||
|
# forward_to: "you.domain.org"
|
||||||
|
# http: true # Forward port 80 (default: true)
|
||||||
|
# https: true # Forward port 443 (default: true)
|
||||||
|
nginx_forwarder: {}
|
||||||
|
|
||||||
|
# Let's Encrypt / Certbot configuration
|
||||||
|
# acme_email: "" # Required for Let's Encrypt - intentionally undefined
|
||||||
|
# Set this variable to enable Certbot installation and certificate management
|
||||||
|
|||||||
@ -9,11 +9,54 @@
|
|||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
nginx_user: "{{ nginx_user | default('www-data') }}"
|
nginx_user: "{{ nginx_user | default('www-data') }}"
|
||||||
|
|
||||||
|
- name: Add Nginx official APT signing key (Debian/Ubuntu)
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: https://nginx.org/keys/nginx_signing.key
|
||||||
|
dest: /etc/apt/keyrings/nginx-archive-keyring.asc
|
||||||
|
mode: "0644"
|
||||||
|
when:
|
||||||
|
- ansible_facts['os_family'] == 'Debian'
|
||||||
|
|
||||||
|
- name: Add Nginx official repository (Debian/Ubuntu)
|
||||||
|
ansible.builtin.deb822_repository:
|
||||||
|
name: nginx-official
|
||||||
|
types: deb
|
||||||
|
uris: http://nginx.org/packages/mainline/debian/
|
||||||
|
suites: "{{ ansible_facts['distribution_release'] }}"
|
||||||
|
components: nginx
|
||||||
|
signed_by: /etc/apt/keyrings/nginx-archive-keyring.asc
|
||||||
|
state: present
|
||||||
|
when:
|
||||||
|
- ansible_facts['os_family'] == 'Debian'
|
||||||
|
|
||||||
- name: Install nginx
|
- name: Install nginx
|
||||||
ansible.builtin.package:
|
ansible.builtin.package:
|
||||||
name: nginx
|
name: nginx
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
|
- name: Install nginx stream module (Debian)
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: libnginx-mod-stream
|
||||||
|
state: present
|
||||||
|
when:
|
||||||
|
- ansible_facts['os_family'] == 'Debian'
|
||||||
|
- nginx_forwarder is defined
|
||||||
|
- nginx_forwarder | length > 0
|
||||||
|
|
||||||
|
- name: Install Certbot
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: certbot
|
||||||
|
state: present
|
||||||
|
when: acme_email is defined
|
||||||
|
|
||||||
|
- name: Enable Certbot renewal timer
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: certbot-renew.timer
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
when: acme_email is defined
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
- name: Ensure nginx conf.d directory exists
|
- name: Ensure nginx conf.d directory exists
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ nginx_conf_dir }}"
|
path: "{{ nginx_conf_dir }}"
|
||||||
@ -22,6 +65,23 @@
|
|||||||
group: root
|
group: root
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Ensure nginx streams.d directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ nginx_streams_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Ensure Certbot webroot directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /var/www/certbot
|
||||||
|
state: directory
|
||||||
|
owner: "{{ nginx_user }}"
|
||||||
|
group: "{{ nginx_user }}"
|
||||||
|
mode: "0755"
|
||||||
|
when: acme_email is defined
|
||||||
|
|
||||||
- name: Deploy nginx main configuration
|
- name: Deploy nginx main configuration
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: nginx.conf.j2
|
src: nginx.conf.j2
|
||||||
@ -32,6 +92,31 @@
|
|||||||
validate: nginx -t -c %s
|
validate: nginx -t -c %s
|
||||||
notify: Reload nginx
|
notify: Reload nginx
|
||||||
|
|
||||||
|
- name: Deploy stream forwarder configurations
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: forwarder.conf.j2
|
||||||
|
dest: "{{ nginx_streams_dir }}/forwarder-{{ domain | replace('.', '_') }}.conf"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
loop: "{{ nginx_forwarder | dict2items }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: item
|
||||||
|
vars:
|
||||||
|
domain: "{{ item.key }}"
|
||||||
|
config: "{{ item.value }}"
|
||||||
|
when:
|
||||||
|
- nginx_forwarder is defined
|
||||||
|
- nginx_forwarder | length > 0
|
||||||
|
notify: Reload nginx
|
||||||
|
|
||||||
|
- name: Validate nginx configuration after stream forwarder deployment
|
||||||
|
ansible.builtin.command: nginx -t
|
||||||
|
changed_when: false
|
||||||
|
when:
|
||||||
|
- nginx_forwarder is defined
|
||||||
|
- nginx_forwarder | length > 0
|
||||||
|
|
||||||
- name: Deploy logrotate configuration for nginx
|
- name: Deploy logrotate configuration for nginx
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: logrotate-nginx.j2
|
src: logrotate-nginx.j2
|
||||||
|
|||||||
25
roles/nginx/templates/forwarder.conf.j2
Normal file
25
roles/nginx/templates/forwarder.conf.j2
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# TCP stream forwarder for {{ domain }}
|
||||||
|
# Managed by Ansible - DO NOT EDIT MANUALLY
|
||||||
|
# Transparent TCP proxy (no protocol inspection)
|
||||||
|
|
||||||
|
{% if config.http | default(true) %}
|
||||||
|
upstream {{ domain | replace('.', '_') | replace('-', '_') }}_http {
|
||||||
|
server {{ config.forward_to }}:80;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
proxy_pass {{ domain | replace('.', '_') | replace('-', '_') }}_http;
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config.https | default(true) %}
|
||||||
|
upstream {{ domain | replace('.', '_') | replace('-', '_') }}_https {
|
||||||
|
server {{ config.forward_to }}:443;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443;
|
||||||
|
proxy_pass {{ domain | replace('.', '_') | replace('-', '_') }}_https;
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
@ -9,6 +9,11 @@ pid /run/nginx.pid;
|
|||||||
|
|
||||||
include /usr/share/nginx/modules/*.conf;
|
include /usr/share/nginx/modules/*.conf;
|
||||||
|
|
||||||
|
{% if nginx_forwarder and nginx_forwarder | length > 0 %}
|
||||||
|
# Load stream module for TCP/UDP proxying
|
||||||
|
load_module modules/ngx_stream_module.so;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
events {
|
events {
|
||||||
worker_connections {{ nginx_worker_connections }};
|
worker_connections {{ nginx_worker_connections }};
|
||||||
}
|
}
|
||||||
@ -48,3 +53,11 @@ http {
|
|||||||
# Load modular configuration files from the conf.d directory
|
# Load modular configuration files from the conf.d directory
|
||||||
include {{ nginx_conf_dir }}/*.conf;
|
include {{ nginx_conf_dir }}/*.conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{% if nginx_forwarder and nginx_forwarder | length > 0 %}
|
||||||
|
# Stream block for TCP/UDP proxying
|
||||||
|
stream {
|
||||||
|
# Load stream configurations
|
||||||
|
include {{ nginx_streams_dir }}/*.conf;
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
|||||||
41
roles/nginx/templates/vhost-acme.conf.j2
Normal file
41
roles/nginx/templates/vhost-acme.conf.j2
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# HTTPS vhost with Let's Encrypt (Certbot) for {{ server_name }}
|
||||||
|
# Managed by Ansible - DO NOT EDIT MANUALLY
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name {{ server_name }};
|
||||||
|
|
||||||
|
# Certbot webroot for ACME challenges
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect to HTTPS
|
||||||
|
location / {
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name {{ server_name }};
|
||||||
|
|
||||||
|
# Let's Encrypt certificates (managed by Certbot)
|
||||||
|
ssl_certificate /etc/letsencrypt/live/{{ server_name }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/{{ server_name }}/privkey.pem;
|
||||||
|
|
||||||
|
# SSL configuration
|
||||||
|
ssl_protocols {{ nginx_ssl_protocols }};
|
||||||
|
ssl_prefer_server_ciphers {{ 'on' if nginx_ssl_prefer_server_ciphers else 'off' }};
|
||||||
|
|
||||||
|
{% if nginx_log_backend == 'journald' %}
|
||||||
|
access_log syslog:server=unix:/dev/log,nohostname,tag=nginx_{{ server_name | replace('.', '_') }};
|
||||||
|
error_log syslog:server=unix:/dev/log,nohostname,tag=nginx_{{ server_name | replace('.', '_') }};
|
||||||
|
{% else %}
|
||||||
|
access_log /var/log/nginx/{{ server_name }}_access.log main;
|
||||||
|
error_log /var/log/nginx/{{ server_name }}_error.log;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Service-specific configuration included below
|
||||||
|
{{ vhost_config | default('') }}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user