update 2025-04-06
Changes: - fix README.md old json format usage - rename "redirect" option to "proxy_pass" - move docker-compose.yml and nginx.conf.j2 to examples and add to .gitignore - fix situation when one domain from domains.txt and servers.yaml appeared twice in nginx.conf
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,4 +1,7 @@
|
|||||||
/nginx/domains.txt
|
/nginx/domains.txt
|
||||||
/nginx/servers.json
|
|
||||||
/nginx/servers.yaml
|
/nginx/servers.yaml
|
||||||
|
/nginx/nginx.conf.j2
|
||||||
|
/nginx.conf
|
||||||
*.env
|
*.env
|
||||||
|
/.venv
|
||||||
|
/docker-compose.y*ml
|
||||||
24
README.md
24
README.md
@@ -2,19 +2,17 @@
|
|||||||
|
|
||||||
## Preparation
|
## Preparation
|
||||||
|
|
||||||
Add your domains for _certbot_ to [domains.txt file at nginx directory](nginx/domains.txt) and servers configuration
|
1. Copy and edit (if needed) [nginx/nginx.conf.j2](nginx/nginx.conf.j2.example) file.
|
||||||
json file in format
|
2. Add your certified domains to nginx/domains.txt file ([example](nginx/domains.txt.example)).
|
||||||
```json
|
|
||||||
{
|
These domains will be used by _certbot_ to monitor and update (if possible) certificates. _Nginx_ will also setup
|
||||||
"domain": {
|
http server for the given entries.
|
||||||
"redirect": "redirection_path",
|
|
||||||
"options": [
|
However if the domain is set both in domains.txt and servers.yaml (next step), _nginx_ will
|
||||||
"some_nginx_option_on_server_level here;"
|
use https with certificates at the given path. That will fail nginx startup if the certificate
|
||||||
]
|
to at least one domain is missing (--http-only option will skip domains.txt check).
|
||||||
}
|
|
||||||
}
|
3. Add your servers section configuration to nginx/servers.yaml ([example](nginx/servers.yaml.example)).
|
||||||
```
|
|
||||||
to [servers.json file at nginx directory](nginx/servers.json).
|
|
||||||
|
|
||||||
User email used for certbot can be set as environment variable at build process or in .env file.
|
User email used for certbot can be set as environment variable at build process or in .env file.
|
||||||
|
|
||||||
|
|||||||
@@ -23,16 +23,10 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
- 443:443
|
- 443:443
|
||||||
networks:
|
|
||||||
- hosting_net
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
ssl:
|
ssl:
|
||||||
name: ssl_nginx_ssl
|
name: ssl_nginx_ssl
|
||||||
letsencrypt:
|
letsencrypt:
|
||||||
name: ssl_nginx_letsencrypt
|
name: ssl_nginx_letsencrypt
|
||||||
|
|
||||||
networks:
|
|
||||||
hosting_net:
|
|
||||||
external: true
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.11-alpine as builder
|
FROM python:3.11-alpine AS builder
|
||||||
|
|
||||||
RUN pip3 install --no-cache-dir pyyaml jinja2
|
RUN pip3 install --no-cache-dir pyyaml jinja2
|
||||||
|
|
||||||
@@ -16,12 +16,11 @@ FROM nginx:alpine
|
|||||||
COPY --from=builder /nginx.conf /etc/nginx/nginx.conf
|
COPY --from=builder /nginx.conf /etc/nginx/nginx.conf
|
||||||
COPY domains.txt /domains.txt
|
COPY domains.txt /domains.txt
|
||||||
|
|
||||||
RUN echo "16 2 */7 * * nginx -s reload" > /etc/crontabs/root && \
|
RUN echo "20 2 */7 * * nginx -s reload" > /etc/crontabs/root && \
|
||||||
\
|
\
|
||||||
echo "cp /domains.txt /ssl/domains.txt" > /entrypoint && \
|
echo "cp /domains.txt /ssl/domains.txt" > /entrypoint && \
|
||||||
echo "crond" >> /entrypoint && \
|
echo "crond" >> /entrypoint && \
|
||||||
echo "nginx -g 'daemon off;'" >> /entrypoint && \
|
echo "nginx -g 'daemon off;'" >> /entrypoint
|
||||||
echo "nginx" >> /entrypoint
|
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/sh"]
|
ENTRYPOINT ["/bin/sh"]
|
||||||
CMD ["/entrypoint"]
|
CMD ["/entrypoint"]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
"""Add domain servers to nginx.conf executable script."""
|
"""Add domain servers to nginx.conf executable script."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -16,7 +17,7 @@ class Server:
|
|||||||
|
|
||||||
name: str
|
name: str
|
||||||
all_names: str | None = None
|
all_names: str | None = None
|
||||||
redirect: str | None = None
|
proxy_pass: str | None = None
|
||||||
certificate_dir: str | None = None
|
certificate_dir: str | None = None
|
||||||
port: int = None
|
port: int = None
|
||||||
ssl_port: int = None
|
ssl_port: int = None
|
||||||
@@ -42,7 +43,7 @@ class Server:
|
|||||||
return {
|
return {
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"all_names": self.all_names,
|
"all_names": self.all_names,
|
||||||
"redirect": self.redirect,
|
"proxy_pass": self.proxy_pass,
|
||||||
"server_options": self.server_options,
|
"server_options": self.server_options,
|
||||||
"location_options": self.location_options,
|
"location_options": self.location_options,
|
||||||
"certificate_dir": self.certificate_dir,
|
"certificate_dir": self.certificate_dir,
|
||||||
@@ -105,19 +106,25 @@ def main() -> None:
|
|||||||
domains_with_certs = []
|
domains_with_certs = []
|
||||||
|
|
||||||
nginx_servers: list[Server] = []
|
nginx_servers: list[Server] = []
|
||||||
|
resolver: str = "127.0.0.1"
|
||||||
|
acme_challenge_location: str | None = None
|
||||||
|
|
||||||
if args.servers_config is not None:
|
if args.servers_config is not None:
|
||||||
with open(args.servers_config, "r", encoding="utf-8") as file:
|
with open(args.servers_config, "r", encoding="utf-8") as file:
|
||||||
data: dict = yaml.safe_load(file)
|
data: dict = yaml.safe_load(file)
|
||||||
resolver: str = data.get("resolver", "127.0.0.1")
|
resolver: str = data.get("resolver", resolver)
|
||||||
acme_challenge_location: str | None = data.get("acme_challenge_location")
|
acme_challenge_location = data.get("acme_challenge_location")
|
||||||
servers: dict[str, dict[str, Any]] = data["servers"]
|
servers: dict[str, dict[str, Any]] = data["servers"]
|
||||||
|
|
||||||
for server_name, params in servers.items():
|
for server_name, params in servers.items():
|
||||||
nginx_servers.append(
|
nginx_servers.append(
|
||||||
Server(
|
Server(
|
||||||
name=(server_name if "*" not in server_name else f"{server_name} {server_name.replace('*.', '')}"),
|
name=server_name,
|
||||||
all_names=params.get("all_names"),
|
all_names=params.get(
|
||||||
redirect=params.get("redirect"),
|
"all_names",
|
||||||
|
None if "*" not in server_name else f"{server_name} {server_name.replace('*.', '', 1)}",
|
||||||
|
),
|
||||||
|
proxy_pass=params.get("proxy_pass"),
|
||||||
certificate_dir=_get_certificate_path(
|
certificate_dir=_get_certificate_path(
|
||||||
args.http_only, domains_with_certs, args.certificates_path, server_name
|
args.http_only, domains_with_certs, args.certificates_path, server_name
|
||||||
),
|
),
|
||||||
@@ -130,8 +137,12 @@ def main() -> None:
|
|||||||
for domain in domains_with_certs:
|
for domain in domains_with_certs:
|
||||||
if not any(
|
if not any(
|
||||||
(
|
(
|
||||||
domain == server.name
|
(server.all_names is None and domain == server.name)
|
||||||
or (domain == f"*.{server.name[server.name.find('.') + 1:]}" if "." in server.name else False)
|
or (
|
||||||
|
server.all_names is not None
|
||||||
|
and f" {domain}" in server.all_names
|
||||||
|
or server.all_names.startswith(domain)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
for server in nginx_servers
|
for server in nginx_servers
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
your.domain.to_redirect
|
your.domain.to_listen
|
||||||
your_other.doma.in
|
your_other.doma.in
|
||||||
*.doma.in
|
*.doma.in
|
||||||
#commented.domain
|
#commented.domain
|
||||||
domain.without.redirect
|
domain.without.proxy
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.10-alpine as builder
|
FROM python:3.10-alpine AS builder
|
||||||
|
|
||||||
COPY add_servers.py /add_servers.py
|
COPY add_servers.py /add_servers.py
|
||||||
COPY domains.txt /domains.txt
|
COPY domains.txt /domains.txt
|
||||||
@@ -18,8 +18,7 @@ COPY domains.txt /domains.txt
|
|||||||
|
|
||||||
RUN echo "(sleep 120 && killall nginx) &" > /entrypoint && \
|
RUN echo "(sleep 120 && killall nginx) &" > /entrypoint && \
|
||||||
echo "cp /domains.txt /ssl/domains.txt" >> /entrypoint && \
|
echo "cp /domains.txt /ssl/domains.txt" >> /entrypoint && \
|
||||||
echo "nginx -g 'daemon off;'" >> /entrypoint && \
|
echo "nginx -g 'daemon off;'" >> /entrypoint
|
||||||
echo "nginx" >> /entrypoint
|
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/sh"]
|
ENTRYPOINT ["/bin/sh"]
|
||||||
CMD ["/entrypoint"]
|
CMD ["/entrypoint"]
|
||||||
|
|||||||
@@ -1,82 +1,83 @@
|
|||||||
{#- variables ~ examples: #}
|
{#- variables ~ examples: #}
|
||||||
{#- acme_challenge_location ~ /ssl/: #}
|
{#- acme_challenge_location ~ /ssl/: #}
|
||||||
{#- resolver ~ 127.0.0.11: #}
|
{#- resolver ~ 127.0.0.11: #}
|
||||||
{#- servers ~ ["name": ..., ("redirect": ..., "server_options": ..., "location_options": ..., "all_names": ..., "port": ..., "ssl_port": ...)]: #}
|
{#- servers ~ ["name": ..., ("proxy_pass": ..., "server_options": ..., "location_options": ..., "all_names": ..., "port": ..., "ssl_port": ...)]: #}
|
||||||
{#- #}user nginx;
|
{#- #}user nginx;
|
||||||
worker_processes auto;
|
worker_processes auto;
|
||||||
|
|
||||||
error_log /var/log/nginx/error.log notice;
|
error_log /var/log/nginx/error.log notice;
|
||||||
pid /var/run/nginx.pid;
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
events {
|
events {
|
||||||
worker_connections 1024;
|
worker_connections 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
http {
|
http {
|
||||||
ssl_session_cache shared:SSL:10m;
|
ssl_session_cache shared:SSL:10m;
|
||||||
ssl_session_timeout 10m;
|
ssl_session_timeout 10m;
|
||||||
server_tokens off;
|
server_tokens off;
|
||||||
gzip on;
|
gzip on;
|
||||||
|
|
||||||
proxy_connect_timeout 300;
|
proxy_connect_timeout 300;
|
||||||
proxy_send_timeout 600;
|
proxy_send_timeout 600;
|
||||||
proxy_read_timeout 600;
|
proxy_read_timeout 600;
|
||||||
send_timeout 600;
|
send_timeout 600;
|
||||||
client_max_body_size 500M;
|
client_max_body_size 500M;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
return 404;
|
return 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
{%- for server in servers %}
|
{%- for server in servers %}
|
||||||
server {
|
{# #}
|
||||||
listen {{ server["port"] or 80 }};
|
server {
|
||||||
{%- if server["certificate_dir"] is not none %}
|
listen {{ server["port"] or 80 }};
|
||||||
listen {{ server["ssl_port"] or 443 }} ssl;
|
{%- if server["certificate_dir"] is not none %}
|
||||||
{%- endif %}
|
listen {{ server["ssl_port"] or 443 }} ssl;
|
||||||
keepalive_timeout 70;
|
ssl_certificate {{ server["certificate_dir"] }}/fullchain.pem;
|
||||||
|
ssl_certificate_key {{ server["certificate_dir"] }}/privkey.pem;
|
||||||
server_name {{ server["all_names"] or server["name"] }};
|
|
||||||
|
if ($scheme = 'http') {
|
||||||
{%- if acme_challenge_location is defined %}
|
return 302 https://$host$request_uri;
|
||||||
{# #}
|
}
|
||||||
location /.well-known/acme-challenge {
|
{%- endif %}
|
||||||
root {{ acme_challenge_location }};
|
keepalive_timeout 70;
|
||||||
}
|
|
||||||
{%- endif %}
|
server_name {{ server["all_names"] or server["name"] }};
|
||||||
|
|
||||||
{%- if server["server_options"]|length > 0 %}
|
{%- if acme_challenge_location is not none %}
|
||||||
{# #}
|
{# #}
|
||||||
{%- for server_option in server["server_options"] %}
|
location /.well-known/acme-challenge {
|
||||||
{{ server_option }}
|
root {{ acme_challenge_location }};
|
||||||
{%- endfor %}
|
}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{%- if server["certificate_dir"] is not none %}
|
{%- if server["server_options"]|length > 0 %}
|
||||||
{# #}
|
{# #}
|
||||||
ssl_certificate {{ server["certificate_dir"] }}/fullchain.pem;
|
{%- for server_option in server["server_options"] %}
|
||||||
ssl_certificate_key {{ server["certificate_dir"] }}/privkey.pem;
|
{{ server_option }}
|
||||||
{%- endif %}
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
{%- if server["redirect"] is not none %}
|
|
||||||
{# #}
|
{%- if server["proxy_pass"] is not none %}
|
||||||
location / {
|
{# #}
|
||||||
resolver {{ resolver }};
|
location / {
|
||||||
set $host_{{ loop.index }} {{ server["redirect"] }};
|
resolver {{ resolver }};
|
||||||
proxy_pass $host_{{ loop.index }};
|
set $host_{{ loop.index }} {{ server["proxy_pass"] }};
|
||||||
|
proxy_pass $host_{{ loop.index }};
|
||||||
proxy_set_header Host $host:$server_port;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header Host $host:$server_port;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
{%- if server["location_options"]|length > 0 %}
|
|
||||||
{# #}
|
{%- if server["location_options"]|length > 0 %}
|
||||||
{%- for location_option in server["location_options"] %}
|
{# #}
|
||||||
{{ location_option }}
|
{%- for location_option in server["location_options"] %}
|
||||||
{%- endfor %}
|
{{ location_option }}
|
||||||
{%- endif %}
|
{%- endfor %}
|
||||||
}
|
{%- endif %}
|
||||||
{%- endif %}
|
}
|
||||||
}
|
{%- endif %}
|
||||||
{%- endfor %}
|
}
|
||||||
}
|
{%- endfor %}
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
resolver: 127.0.0.1
|
resolver: 127.0.0.1
|
||||||
acme_challenge_location: /etc/nginx/acme/
|
acme_challenge_location: /etc/nginx/acme/
|
||||||
|
|
||||||
|
|
||||||
servers:
|
servers:
|
||||||
your.domain.to_redirect:
|
your.domain.to_redirect:
|
||||||
redirect: "http://redirection.address"
|
proxy_pass: "http://redirection.address"
|
||||||
your_other.doma.in:
|
your_other.doma.in:
|
||||||
redirect: "http://redirection-other.address"
|
proxy_pass: "http://redirection-other.address"
|
||||||
server_options:
|
server_options:
|
||||||
- "proxy_buffering off;"
|
- "proxy_buffering off;"
|
||||||
- "proxy_request_buffering off;"
|
- "proxy_request_buffering off;"
|
||||||
@@ -16,7 +17,7 @@ servers:
|
|||||||
- "proxy_read_timeout 86400;"
|
- "proxy_read_timeout 86400;"
|
||||||
"*.doma.in":
|
"*.doma.in":
|
||||||
all_names: "*.doma.in doma.in"
|
all_names: "*.doma.in doma.in"
|
||||||
redirect: "http://full.subdomain.redirect"
|
proxy_pass: "http://full.subdomain.proxy"
|
||||||
server_options:
|
server_options:
|
||||||
- "proxy_buffering off;"
|
- "proxy_buffering off;"
|
||||||
- "proxy_request_buffering off;"
|
- "proxy_request_buffering off;"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
# dummy pyproject file for linting
|
||||||
[tool.pylint]
|
[tool.pylint]
|
||||||
max-line-length = 120
|
max-line-length = 120
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 120
|
line-length = 120
|
||||||
|
|||||||
Reference in New Issue
Block a user