diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..2bc0ca0
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,33 @@
+[run]
+branch = True
+source =
+ nginx_proxy
+ nginx
+ cloudflare
+omit =
+ tests/*
+ */test_*
+ */__pycache__/*
+ */venv/*
+ */site-packages/*
+ setup.py
+ */conftest.py
+
+[report]
+exclude_lines =
+ pragma: no cover
+ def __repr__
+ raise AssertionError
+ raise NotImplementedError
+ if __name__ == .__main__.:
+ if TYPE_CHECKING:
+ @abstractmethod
+ @abstract
+precision = 2
+show_missing = True
+
+[html]
+directory = htmlcov
+
+[xml]
+output = coverage.xml
diff --git a/README.md b/README.md
index f78239a..318cdf7 100644
--- a/README.md
+++ b/README.md
@@ -4,193 +4,227 @@ Nginx-Proxy
[](https://codecov.io/github/mesudip/nginx-proxy)
[](https://hub.docker.com/layers/mesudip/nginx-proxy/latest)
-Docker container for automatically creating nginx configuration based on active containers in docker host.
+Fully automated Nginx reverse proxy for Docker and Swarm.
-- Easy server configuration with environment variables
-- Map multiple containers to different locations on same server
-- Automatic Let's Encrypt ssl certificate registration
-- Basic Authorization
+No more writing configuration files. `nginx-proxy` watches for container changes and automatically generates reverse proxy configurations. It supports SSL via Let's Encrypt, Basic Auth, and custom Nginx directives—all configured through environment variables.
-## Quick Setup
-### Setup nginx-proxy
-```
-docker pull mesudip/nginx-proxy
-docker network create frontend; # create a network for nginx proxy
-docker run --network frontend \
- --name nginx-proxy \
- -v /var/run/docker.sock:/var/run/docker.sock:ro \
- -v nginx_data:/etc/nginx \
- -v nginx_logs:/var/log/nginx \
- -p 80:80 \
- -p 443:443 \
- -d --restart always mesudip/nginx-proxy
-```
-### Setup your container
-The only thing that matters is that the container shares at least one common network to the nginx container and `VIRTUAL_HOST`
-environment variable is set.
-Examples:
-- **WordPress**
+### Key Features
+- **Zero-config Nginx:** Automatically discovers and routes traffic to containers and services
+- **SSL Automation:** Automatic SSL certificates via Let's Encrypt (ACME).
+- **Flexible Routing:** Host-based, path-based, and multiple domains per container.
+- **Advanced Features:** Sticky sessions, basic auth, redirects, and custom Nginx directives.
+- **Swarm Ready:** Compatible with Docker Swarm services.
+
+## Philosophy
+`nginx-proxy` is built on the belief that infrastructure should be invisible. Each service keeps its configuration.
+
+## Quick Start
+### 1. Start nginx-proxy
+
+**Note** When migrating to v3, old volumes should be removed.
+```bash
+docker network create frontend
+
+docker run -d --restart always \
+ --name nginx-proxy \
+ --network frontend \
+ -p 80:80 \
+ -p 443:443 \
+ -v /var/run/docker.sock:/var/run/docker.sock:ro \
+ -v nginx_data:/etc/nginx \
+ -v nginx_logs:/var/log/nginx \
+ mesudip/nginx-proxy:v3
```
-docker run --network frontend \
- --name wordpress-server \
- -e VIRTUAL_HOST="wordpress.example.com" \
- wordpress
+
+### 2. Run a Service
+Start a container on the same network and set the `VIRTUAL_HOST` environment variable.
+
+#### Example: WordPress
+```bash
+docker run -d \
+ --name wordpress \
+ --network frontend \
+ -e VIRTUAL_HOST="blog.example.com" \
+ wordpress
```
- - **Docker Registry**
- ```
-docker run --network frontend \
- --name docker-registry \
- -e VIRTUAL_HOST='https://registry.example.com/v2 -> /v2; client_max_body_size 2g' \
- -e PROXY_BASIC_AUTH="registry.example.com -> user1:password,user2:password2,user3:password3" \
- registry:2
+
+#### Example: Private Registry (Advanced)
+```bash
+docker run -d \
+ --name registry \
+ --network frontend \
+ -e VIRTUAL_HOST="htttps://registry.example.com/v2" \
+ -e PROXY_BASIC_AUTH="registry.example.com -> user1:pass1,user2:pass2" \
+ -e "client_max_body_size=2g" \
+ registry:2
```
-Details of Using nginx-proxy
-======================
- - [Configure `nginx-proxy`](#configure-nginx-proxy)
- - [Configure enrironment VIRTUAL_HOST in your containers](#configure-environment-virtual_host-in-your-containers)
- - [WebSockets](#support-for-websocket)
- - [Multiple hosts in same container](#multiple-virtual-hosts-on-same-container)
- - [Redirection](#redirection)
- - [Https and SSL](#ssl-support)
- - [Basic Authorization](#basic-authorization)
- - [Default Server](#default-server)
-
-## Configure `nginx-proxy`
-Following directries can be mounted as volumes to persist configurations
-- `/etc/nginx/conf.d` nginx configuration directory. You can add your own custom configurations here
-- `/etc/nginx/ssl` directory for storing ssl certificates, ssl private key and Let's Encrypt account key.
-- `/var/log/nginx` directory nginx logs
-- `/etc/nginx/challenges` directory for storing challenge content when registering Let's Encrypt certificate
-
-Some of the default behaviour of `nginx-proxy` can be changed with environment variables.
-- `DHPARAM_SIZE` Default - `2048` : Set size of dhparam usd for ssl certificates
-- `CLIENT_MAX_BODY_SIZE` Default - `1m` : Set default max body size for all the servers.
-- `NGINX_WORKER_PROCESSES` Default - `auto` : Set number of nginx workers.
-- `NGINX_WORKER_CONNECTIONS` Default - `65535` : Set number of connections per worker.
-
-## Configure environment `VIRTUAL_HOST` in your containers
-When you want a container's to be hosted on a domain set `VIRTUAL_HOST` environment variable to desired `server_name` entry.
-For virtual host to work it requires
-- nginx-proxy container to be on the same network as that of the container.
-- port to be exposed in Docker file or while creating the container. When missing or if it has multiple exposed ports, port 80 is used by default.
-- when hosting on port other than 80, make sure that your nginx-proxy container's port is bind-mounted to host port.
-
-Some configurations possible through `VIRTUAL_HOST`
-
- `VIRTUAL_HOST` | release address | container path | container port
-:--- | :--- |:---------------| :---
-example.com | http://example.com | / | exposed port
-example.com:8080 | http://example.com:8080 | / | exposed port
-example.com -> :8080 | http://example.com | / | `8080`
-https://example.com | https://example.com | / | exposed port
-example.com/api | http://example.com/api | /api | exposed port
-example.com/api/ -> / | http://example.com/api | / | exposed port
-example.com/api -> :8080/api | http://example.com/api | /api | 8080
-https://example.com/api/v1:5001 -> :8080/api | https://example.com/api/v1:5001 | /api | 8080
-wss://example.com/websocket | wss://example.com/websocket | / | exposed port
-
-With `VIRTUAL_HOST` you can inject nginx directives into location each configuration must be separed with a `;`
-. You can see the possible directives in nginx documentation.
-
-**Example :** `VIRTUAL_HOST=somesite.example.com -> :8080 ;proxy_read_timeout 900;client_max_body_size 2g;` will generate configuration as follows
-```nginx.conf
-server{
- server_name somesite.example.com;
- listen 80;
- location /{
- proxy_read_timeout 900;
- client_max_body_size 2g;
- proxy_pass http://127.2.3.4; // your container ip here
- }
-}
+## Documentation
+- [Configuration](#configuration)
+- [Virtual Hosts](#virtual-hosts)
+- [SSL Support](#ssl-support)
+- [Docker Swarm](#docker-swarm-support)
+- [Advanced Features](#advanced-features)
+
+### Configuration
+
+#### Volume layouts
+- `/etc/nginx/conf.d` – rendered configs (`nginx-proxy.conf`) plus any custom files you mount.
+- `/etc/nginx/ssl` – certificate material, split into `/certs` and `/private` (override with `SSL_DIR`, `SSL_CERTS_DIR`, `SSL_KEY_DIR`).
+- `/etc/nginx/challenges` – ACME challenge payloads, configurable via `CHALLENGE_DIR` and `WELLKNOWN_PATH`.
+- `/var/log/nginx` – access/error logs.
+
+#### NginxProxy Configuration variables
+Control the default behavior of `nginx-proxy`:
+
+| Variable | Default | Description |
+| :--- | :--- | :--- |
+| `CLIENT_MAX_BODY_SIZE` | `1m` | Default max body size for uploads. |
+| `NGINX_WORKER_PROCESSES` | `auto` | Number of Nginx worker processes. |
+| `NGINX_WORKER_CONNECTIONS` | `65535` | Max connections per worker. |
+| `CERT_RENEW_THRESHOLD_DAYS` | `30` | By default certificates are renewed when they have <=30 days remaining. |
+| `ENABLE_IPV6` | `false` | Enable IPv6 support on nginx. |
+| `DOCKER_SWARM` | `ignore` | Treats every container like local by defeault. Set `enable` for Swarm support, `strict` for Swarm-only or`exclude` to not include swarm containers |
+| `SWARM_DOCKER_HOST` | - | URL of the Swarm manager socket (e.g., `tcp://manager:2375`). |
+| `CERTAPI_URL` | - | External Certificate API URL. |
+| `CHALLENGE_DIR` | `/etc/nginx/challenges/` | Base directory for acme challenge store, when requesting certificates with acme. `.well-known/acme-challenge` folder lives inside this.|
+| `CLOUDFLARE_API_KEY_KEY*` | - | Cloudflare api keys to issue DNS certificates.|
+
+
+## Virtual Hosts
+To expose a container, set the `VIRTUAL_HOST*` environment variable. The container must be on the same Docker network as `nginx-proxy`.
+
+### Configuration Format
+`VIRTUAL_HOST=[://] [] [-> [:] ] [; ]`
+
+### Examples
+| VIRTUAL_HOST | Description |
+| :--- | :--- |
+| `example.com` | Proxies to exposed container port. |
+| `example.com -> :8080` | Proxies to port 8080. |
+| `https://example.com` | HTTPS proxy to container exposed http port |
+| `https://example.com/api` | Only /api path is passed on to container. /api prefix is not removed |
+| `https://example.com/api -> /v1` | Re-maps path `/api` to `/v1` on container exposed port|
+| `https://example.com/api -> :8080/v1` | Re-maps path `/api` to `/v1` on container port 8080|
+| `https://example.com/api -> https://_:8080/v1` | Re-maps path `/api` to `/v1` on port 8080. Https is used to connect to container|
+
+**Note:** Directives separated by `;` are injected into the Nginx `location` block.
+Example: `VIRTUAL_HOST=site.com; proxy_read_timeout 900;`
+
+### WebSockets
+WebSocket support requires explicit configuration if the protocol is not detected automatically.
+
+- **Explicit:** `VIRTUAL_HOST=wss://ws.example.com -> :8080/websocket`
+- **Auto-Upgrade:** `VIRTUAL_HOST=https+wss://example.com` (Supports both HTTP and WSS on te host).
+
+### Multiple Hosts
+A single container can serve multiple domains path mappings. All that matters is that env variable starts with `VIRTUAL_HOST` e.g. `VIRTUAL_HOST1`, `VIRTUAL_HOST2`, etc.
+
+**Example:**
+```bash
+docker run -d --network frontend \
+ -e "VIRTUAL_HOST_API=https://api.example.com -> :3000" \
+ -e "VIRTUAL_HOST_ADMIN=https://admin.example.com -> :4000" \
+ my-app
```
-### Support for websocket
-Exposing websocket requires the websocket endpoint to be explicitly configured via virtual host. The websocket endpoint can be `ws://` or `wss://`.
-If you want to use both websocket and non-websocket endpoints you will have to use multiple hosts
-`-e "VIRTUAL_HOST=wss://ws.example.com -> :8080/websocket"`
+### Static Virtual Hosts
+Proxy to external hosts, (not in Docker) using `STATIC_VIRTUAL_HOST`. The container must be running for the site to be live.
-If you are not sure what paths should be exposed on websocket, you can opt for auto upgrade to websocket.
+Format: `STATIC_VIRTUAL_HOST=domain.com->http://192.168.0.1:8080`.
-`-e "VIRTUAL_HOST=https+wss://example.com"`
+**Note** Be aware that if domain as target, nginx will crash if DNS resolution fails.
-### Multiple Virtual Hosts on same container
-To have multiple virtual hosts out of single container, you can use `VIRTUAL_HOST1`, `VIRTUAL_HOST2`, `VIRTUAL_HOST3` and so on. In fact the only thing it matters is that the environment variable starts with `VIRTUAL_HOST`.
+## Docker Swarm Support [Preview]
+Enable swarm mode by setting `DOCKER_SWARM` to `enable` (local & swarm) or `strict` (swarm only).
+If current node is not manager, set `SWARM_DOCKER_HOST=tcp://manager:2375`.
-**Example:** setting up a go-ethereum node.
+**Warning** : Automatic exposed port detection will not work when swarm support is enabled. You must explicitly set port on the `VIRTUAL_HOST` or set `VIRTUAL_PORT` on the container.
+
+## Advanced Features
+### Redirection
+Redirect traffic from one domain to another.
```bash
- docker run -d --network frontend \
- -e "VIRTUAL_HOST1=https://ethereum.example.com -> :8545" \
- -e "VIRTUAL_HOST2=wss://ethereum.example.com/ws -> :8546/" \
- ethereum/client-go \
- --rpc --rpcaddr "0.0.0.0" --ws --wsaddr 0.0.0.0
+-e 'VIRTUAL_HOST=https://example.uk' \
+-e 'PROXY_FULL_REDIRECT=example.com,www.example.uk -> example.uk'
```
-## Redirection
- Let's say you want to serve a website on `example.uk`. You might want users visiting `www.example.uk`,`example.com`,`www.example.com`
- to redirect to `example.uk`. You can simply use `PROXY_FULL_REDIRECT` environment variable.
- ```
- -e 'VIRTUAL_HOST=https://example.uk -> :7000' \
- -e 'PROXY_FULL_REDIRECT=example.com,www.example.com,www.example.uk->example.uk'
- ```
+
+### Sticky Sessions
+Enable session affinity (sticky sessions) for load balancing.
+Set `NGINX_STICKY_SESSION` on the **backend container**.
+- `true` or `ip_hash` – enable `ip_hash` balancing.
+- `false` – disable stickiness (round-robin).
+- Any other string – injected verbatim (e.g., `hash $cookie_sessionid consistent`).
## SSL Support
-Issuing of SSL certificate is done using acme-nginx library for Let's Encrypt. If a precheck determines that
-the domain we are trying to issue certificate is not owned by current machine, a self-signed certificate is
-generated instead.
-
-### Using SSL for exposing endpoint
-Certificate is automatically requested by the nginx-proxy container.
-It requests for a challenge and verifies the challenge to obtain the certificate.
-It is saved under directory `/etc/nginx/ssl/certs` and the private key is located inside
-directoy `/etc/nginx/ssl/private`
-
-### Using your Own SSL certificate
-If you already have a ssl certificate that you want to use, copy it under the `/etc/nginx/ssl/certs` directory and it's key under the directory `/etc/nginx/ssl/private` file should be named `domain.crt` and `domain.key`.
-
-Wildcard certificates can be used. For example to use `*.example.com` wildcard, you should create files
-`/etc/nginx/ssl/certs/*.example.com.crt` and `/etc/nginx/ssl/private/*.example.com.key` in the container's filesystem.
-
-**Note that `*.com` or `*` is not a valid wildcard.** Wild card must have at least 2 dots.
-
-`/etc/nginx/ssl/certs/*.example.com.crt` certificate will :
-- be used for `host1.example.com`
-- be used for `www.example.com`
-- not be used for `xyz.host1.example.com`
-- not be used for `example.com`
-
- ***DHPARAM_SIZE :***
- Default size of DH key used for https connection is `2048`bits. The key size can be changed by changing `DHPARAM_SIZE` environment variable
-
-### Manually obtaining certificate.
-You can manually obtain Let's encrypt certificate using the nginx-proxy container.
-Note that you must set ip in DNS entry to point the correct server.
-
-To issue a certificate for a domain you can simply use this command.
-- `docker exec nginx-proxy getssl www.example.com`
-
- Obtained certificate is saved on `/etc/nginx/ssl/certs/www.example.com` and private is saved on `/etc/nginx/ssl/private/www.example.com`
-
-To issue certificates for multiple domain you can simply add more parameters to the above command
-
- - `docker exec nginx-proxy getssl www.example.com example.com ww.example.com`
-
- All the domains are registered on the same certificate and the filename is set from the first parameter
- passed to the command. so `/etc/nginx/ssl/certs/www.example.com` and `/etc/nginx/ssl/private/www.example.com` are generated
-
-Use `docker exec nginx-proxy getssl --help` for getting help with the command
+`nginx-proxy` automatically requests and renews Let's Encrypt certificates.
+
+### Automatic SSL
+1. Expose ports 80 and 443 on `nginx-proxy`.
+2. Map `/etc/nginx/ssl` and `/etc/nginx/challenges`.
+3. Set `VIRTUAL_HOST` to start with `https://` in your containers.
+
+### Wildcard Certificates With Cloudflare DNS
+To issue wildcard certificates (e.g., `*.example.com`) or avoid exposing port 80, you can use DNS challenges via Cloudflare.
+
+1. Set the `CLOUDFLARE_API_KEY*` environment variables on `nginx-proxy`. `nginx-proxy` supports multiple api-keys and will discover available domains.
+2. Set `VIRTUAL_HOST` to a wildcard domain (e.g., `*.example.com`) or just use it for regular domains to use DNS validation.
+
+**Example:**
+```bash
+docker run -d \
+ -e CLOUDFLARE_API_KEY_KEY1="your-cloudflare-api-token" \
+ ...
+ mesudip/nginx-proxy
+```
+
+### Custom Certificates
+Mount your own certificates:
+- Directory: `/etc/nginx/ssl/certs`
+- Naming: `example.com.crt` and `example.com.key`
+
+Wildcard support: `*.example.com.crt` will match `sub.example.com`.
+
+### Manual Certificate Management
+Use the `getssl` command inside the container:
+```bash
+docker exec nginx-proxy getssl example.com
+```
## Basic Authorization
-Basic Auth can be enabled on the container with environment variable `PROXY_BASIC_AUTH`.
-- `PROXY_BASIC_AUTH=user1:password1,user2:password2,user3:password3` adds basic auth feature to your configured `VIRTUAL_HOST` server root.
-- `PROXY_BASIC_AUTH=example.com/api/v1/admin -> admin1:password1,admin2:password2` adds basic auth only to the location starting from `api/v1/admin`
+Protect your services with HTTP Basic Auth.
+Set `PROXY_BASIC_AUTH` environment variable.
-**Note:** Basic authorization will be ignored if the container's host doesn't use `https`
+- **Per-Host:** `example.com -> user:password`
+- **Per-Path:** `example.com/admin -> user:password`
+
+**Note:** Passwords should not be base64 encoded; the proxy handles hashing.
## Default Server
-When request comes for a server name that is not registered in `nginx-proxy`, It responds with 503 by default.
-If you want the requested to be passed to a container instead, when setting up the container you can add `PROXY_DEFAULT_SERVER=true` environment along with `VIRTUAL_HOST`.
+Requests with an unknown `Host` header return **503 Service Unavailable**.
+Control this with the `DEFAULT_HOST` variable on the `nginx-proxy` container:
+- `true` (default): Returns 503.
+- `false`: Passes default requests to the first configured server.
+
+To route unknown traffic to a specific container:
+```bash
+-e "VIRTUAL_HOST=fallback.example.com" \
+-e "PROXY_DEFAULT_SERVER=true"
+```
+
+## Manual Certificate Commands
+```
+docker exec nginx-proxy verify www.example.com ## check if request routes back to this server.
+
+docker exec nginx-proxy getssl www.example.com example.com www2.example.com # issue certificate
+
+```
+
+## 🚀 Roadmap
+We are constantly improving `nginx-proxy` to make it the most robust and versatile zero-config reverse proxy solution. Here’s what’s coming next:
-This much is sufficient for http connections, but for https connections, you might want to setup
-[wildcard certificates](#using-your-own-ssl-certificate) so that your users dont get invalid ssl certificate errors.
+- **High Availability & Multi-Node Swarm Deployment:** Comprehensive guides and templates for deploying `nginx-proxy` in a multi-node Swarm environment for maximum uptime.
+- **100% Test Coverage:** Ensuring rock-solid stability and reliability for every release.
+- **Remote Swarm Cluster Support:** Monitor and route traffic for Swarm clusters running on remote nodes seamlessly.
+- **Pangolin Support:** Integration with Pangolin as an alternative reverse-proxy engine.
\ No newline at end of file
diff --git a/getssl b/getssl
index 21e0e48..501da1e 100755
--- a/getssl
+++ b/getssl
@@ -1,41 +1,92 @@
#!/usr/bin/env python3
import sys
-
+import os
from nginx import Nginx
from nginx_proxy import SSL
-import os
+from urllib.parse import urlparse
def print_usage():
- print("Usage: Obtain Let'sEncrypt ssl certificate for a domain or multiple domains ")
+ print("Usage: Obtain Let'sEncrypt ssl certificate for a domain or multiple domains")
print()
- print(" getssl [--options] [ hostname2 hostname3 ...]")
+ print(" getssl [hostname2 ...]")
print()
- print("Available options")
- print(" --skip-dns-check Do not perform check if dns points to this machine")
- print(" --new Override if certificate already exists")
- print(" --force Do no perform any check and call acme directly")
+ print("Note: This script respects environment variables: CERTAPI_URL, SSL_DIR, CHALLENGE_DIR, etc.")
exit(1)
if __name__ == "__main__":
if len(sys.argv) < 2:
print_usage()
+
arg_set = set(sys.argv[1:])
- kwargs = {}
- if 'h' in arg_set or "-h" in arg_set or "--help" in arg_set or "-help" in arg_set or "help" in arg_set:
+ if any(x in arg_set for x in ['-h', '--help', 'help']):
+ print_usage()
+
+ # Load configuration from environment variables (consistent with NginxProxyApp)
+ def _strip_end(s: str, char="/") -> str:
+ return s[:-1] if s.endswith(char) else s
+
+ ssl_dir = _strip_end(os.getenv("SSL_DIR", "/etc/ssl").strip())
+ conf_dir = _strip_end(os.getenv("NGINX_CONF_DIR", "/etc/nginx").strip())
+ challenge_dir = _strip_end(os.getenv("CHALLENGE_DIR", "/etc/nginx/challenges").strip()) + "/"
+ cert_renew_threshold_days = int(os.getenv("CERT_RENEW_THRESHOLD_DAYS", "30").strip())
+
+ # CertAPI Configuration
+ certapi_url = os.getenv("CERTAPI_URL", "").strip()
+ mock_server_config = {}
+
+ if certapi_url:
+ parsed = urlparse(certapi_url)
+ port = parsed.port
+ if port is None:
+ port = 443 if parsed.scheme == "https" else 80
+
+ mock_server_config["certapi"] = {
+ "url": certapi_url,
+ "host": parsed.hostname,
+ "scheme": parsed.scheme,
+ "port": port
+ }
+
+ class MockServer:
+ def __init__(self, config):
+ self.config = config
+
+ config_path = os.path.join(conf_dir, "conf.d/gen-ssl-direct.conf")
+ # Make sure challenge dir exists for local mode
+ if not os.path.exists(challenge_dir):
+ try:
+ os.makedirs(challenge_dir, exist_ok=True)
+ except OSError:
+ pass # Might fail if readonly, but then Nginx might scream
+
+ nginx = Nginx.Nginx(config_path, challenge_dir=challenge_dir)
+
+ # Initialize SSL
+ ssl_manager = SSL.SSL(
+ ssl_dir,
+ nginx=nginx,
+ update_threshold_seconds=cert_renew_threshold_days * 24 * 3600,
+ server=MockServer(mock_server_config)
+ )
+
+ domains = [x for x in sys.argv[1:] if not x.startswith("-")]
+ if not domains:
+ print("No domains provided.")
print_usage()
- else:
- if "--force" in arg_set:
- kwargs["no_self_check"] = True
- kwargs["ignore_existing"] = True
- if "--new" in arg_set:
- kwargs["ignore_existing"] = True
- if "--skip-dns-check" in arg_set:
- kwargs["no_self_check"] = True
- config = "/etc/nginx/conf.d/gen-ssl-direct.conf"
- nginx = Nginx.Nginx(config)
- ssl = SSL.SSL("/etc/ssl", nginx=nginx)
- ssl.register_certificate([x for x in sys.argv[1:] if not x.startswith("-")], **kwargs)
- if os.path.exists(config):
- os.remove(config)
+
+ try:
+ ssl_manager.register_certificate(domains)
+ # Attempt to reload nginx to pick up changes (if running)
+ print("Reloading nginx...")
+ if not nginx.reload():
+ print("Warning: Failed to reload nginx. Changes may not apply immediately.")
+ except Exception as e:
+ print(f"Error obtaining certificate: {e}")
+ import traceback
+ traceback.print_exc()
+ sys.exit(1)
+ finally:
+ if os.path.exists(config_path):
+ os.remove(config_path)
diff --git a/verify b/verify
index 7571641..cb21264 100755
--- a/verify
+++ b/verify
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
-
import sys
-
+import os
+import requests
from nginx import Nginx
@@ -11,18 +11,66 @@ def print_usage():
print()
print(" verify [ hostname2 hostname3 ...]")
print()
+ print("Note: If CERTAPI_URL is set, this tool checks connectivity to the CertAPI server.")
+ print(" Only when CertAPI is NOT used, local challenge directory verification is performed.")
exit(1)
+def check_certapi(url):
+ print(f"Checking CertAPI connectivity: {url}")
+ try:
+ # Check /docs endpoint as used in SSL.py
+ target = url.rstrip("/") + "/docs"
+ response = requests.get(target, timeout=5)
+ if response.status_code == 200:
+ print("CertAPI is reachable and operational.")
+ return True
+ else:
+ print(f"Warning: CertAPI responded with status {response.status_code} at {target}")
+ return False
+ except Exception as e:
+ print(f"Error connecting to CertAPI: {e}")
+ return False
+
+
if __name__ == "__main__":
if len(sys.argv) < 2:
print_usage()
+
arg_set = set(sys.argv[1:])
- kwargs = {}
- if 'h' in arg_set or "-h" in arg_set or "--help" in arg_set or "-help" in arg_set or "help" in arg_set:
+ if any(x in arg_set for x in ['-h', '--help', 'help']):
print_usage()
+
+ # Environment configuration
+ def _strip_end(s: str, char="/") -> str:
+ return s[:-1] if s.endswith(char) else s
+
+ certapi_url = os.getenv("CERTAPI_URL", "").strip()
+
+ if certapi_url:
+ # CertAPI Mode
+ if not check_certapi(certapi_url):
+ sys.exit(1)
+ print("CertAPI is configured. Local domain verification via challenge directory is skipped.")
else:
- nginx = Nginx.Nginx("/etc/nginx/conf.d/dummy.conf")
+ # Local Mode
+ conf_dir = _strip_end(os.getenv("NGINX_CONF_DIR", "/etc/nginx").strip())
+ challenge_dir = _strip_end(os.getenv("CHALLENGE_DIR", "/etc/nginx/challenges").strip()) + "/"
+
+ # Ensure challenge_dir exists or Nginx.verify_domain might fail creating files
+ if not os.path.exists(challenge_dir):
+ try:
+ os.makedirs(challenge_dir, exist_ok=True)
+ except OSError:
+ pass
+
+ config_path = os.path.join(conf_dir, "conf.d/dummy.conf")
+ nginx = Nginx.Nginx(config_path, challenge_dir=challenge_dir)
+
domains = [x for x in sys.argv[1:] if not x.startswith("-")]
+ if not domains:
+ print("No domains provided.")
+ print_usage()
+
result = nginx.verify_domain(domains)
print("Owned Domains:" + str(result))