Skip to content

Commit fc4d9f5

Browse files
committed
First commit
0 parents  commit fc4d9f5

File tree

4 files changed

+256
-0
lines changed

4 files changed

+256
-0
lines changed

README.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Caddy Domain Checker - Lightweight HTTP Service for Caddy TLS Automation
2+
3+
This is a lightweight HTTP service designed to integrate with [Caddy](https://caddyserver.com/) for TLS automation. It provides a simple endpoint to check if a given domain is allowed for certificate issuance.
4+
5+
No external dependencies are required for this basic setup. The service reads a list of allowed domains from a file and dynamically updates the list without restarting the server.
6+
7+
## Features
8+
9+
- Query endpoint for domain validation.
10+
- Dynamic domain management through a file (`domains.txt`).
11+
- Logs requests and system activity.
12+
- Lightweight and easy to configure.
13+
14+
## How It Works
15+
16+
1. **Domain Validation**: The service checks whether a domain is allowed by consulting a list of domains stored in `domains.txt`.
17+
2. **Endpoint**: The service exposes an HTTP GET endpoint `/check_domain` for domain validation.
18+
3. **Dynamic Updates**: The service monitors the `domains.txt` file for changes every minute.
19+
20+
## Installation
21+
22+
### Prerequisites
23+
- Python 3.7 or higher
24+
25+
### Steps
26+
1. Clone this repository.
27+
2. Navigate to the project directory.
28+
3. Install any required dependencies (none required for this basic setup).
29+
4. Create an empty `domains.txt` file in the project directory:
30+
```bash
31+
touch domains.txt
32+
```
33+
34+
## Usage
35+
36+
### Running the Server
37+
1. Start the server:
38+
```bash
39+
python3 server.py
40+
```
41+
2. The server runs on `http://127.0.0.1:8008` by default.
42+
43+
### Querying the Service
44+
- Use the `/check_domain` endpoint to validate a domain.
45+
- Example request:
46+
```bash
47+
curl "http://127.0.0.1:8008/check_domain?domain=example.com"
48+
```
49+
- Response:
50+
- Allowed:
51+
```json
52+
{"domain": "example.com", "allowed": true}
53+
```
54+
- Not Allowed:
55+
```json
56+
{"domain": "example.com", "allowed": false}
57+
```
58+
59+
### Managing Domains
60+
- Add or remove domains in the `domains.txt` file.
61+
- Changes will be reflected within a minute without restarting the server.
62+
63+
## Configuration
64+
65+
| Parameter | Default Value | Description |
66+
|-------------------|------------------|---------------------------------|
67+
| `DOMAINS_FILE` | `domains.txt` | Path to the domains file. |
68+
| `LOG_FILE` | `domain_checker.log` | Path to the log file (optional). |
69+
| Server Address | `127.0.0.1:8008` | IP and port for the HTTP server.|
70+
71+
## Logging
72+
73+
Logs system activity and domain queries to the console. You can configure it to log to a file by uncommenting the `FileHandler` line in the `logging` setup.
74+
75+
## Systemd Service Setup
76+
77+
To set up the service to run automatically using `systemd`, follow these steps:
78+
79+
1. Create a new service file:
80+
```bash
81+
sudo nano /etc/systemd/system/caddy-domain-checker.service
82+
```
83+
84+
2. Add the following content to the file:
85+
```ini
86+
[Unit]
87+
Description=Caddy Domain Checker Service
88+
After=network.target
89+
90+
[Service]
91+
ExecStart=/usr/bin/python3 /mnt/data/www/domain_checker/app.py
92+
WorkingDirectory=/mnt/data/www/domain_checker
93+
Restart=always
94+
RestartSec=5
95+
User=www-data
96+
Group=www-data
97+
StandardOutput=journal
98+
StandardError=journal
99+
100+
[Install]
101+
WantedBy=multi-user.target
102+
```
103+
104+
3. Save and close the file.
105+
106+
4. Reload the `systemd` daemon to recognize the new service:
107+
```bash
108+
sudo systemctl daemon-reload
109+
```
110+
111+
5. Enable the service to start on boot:
112+
```bash
113+
sudo systemctl enable caddy-domain-checker
114+
```
115+
116+
6. Start the service:
117+
```bash
118+
sudo systemctl start caddy-domain-checker
119+
```
120+
121+
7. Check the service status to ensure it is running:
122+
```bash
123+
sudo systemctl status caddy-domain-checker
124+
```
125+
126+
## Development
127+
128+
### Testing
129+
- Use `curl` or any HTTP client to interact with the service.
130+
- Ensure the `domains.txt` file is updated correctly for testing.
131+
132+
### Stopping the Server
133+
- Stop the server gracefully with `Ctrl+C`.
134+
135+
## License
136+
This project is licensed under the MIT License. See the LICENSE file for details.
137+
138+
---
139+
140+
**Note:** This service is intended to work seamlessly with Caddy's TLS automation and is not designed for general-purpose use without modifications.

app.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import os
2+
import time
3+
import logging
4+
from http.server import BaseHTTPRequestHandler, HTTPServer
5+
from urllib.parse import urlparse, parse_qs
6+
from threading import Thread
7+
8+
# File path for allowed domains
9+
DOMAINS_FILE = "domains.txt"
10+
LOG_FILE = "domain_checker.log"
11+
allowed_domains = set()
12+
13+
# Configure logging
14+
logging.basicConfig(
15+
level=logging.INFO,
16+
format="%(asctime)s [%(levelname)s] %(message)s",
17+
handlers=[
18+
#logging.FileHandler(LOG_FILE), # Log to a file
19+
logging.StreamHandler() # Log to stdout
20+
]
21+
)
22+
23+
24+
def load_domains():
25+
"""Load domains from the file into memory."""
26+
global allowed_domains
27+
if os.path.exists(DOMAINS_FILE):
28+
with open(DOMAINS_FILE, "r") as f:
29+
allowed_domains.clear()
30+
allowed_domains.update(line.strip() for line in f if line.strip())
31+
logging.info("Domains loaded successfully.")
32+
else:
33+
with open(DOMAINS_FILE, "w") as f:
34+
pass # Create the file if it doesn't exist
35+
logging.warning(f"{DOMAINS_FILE} not found. Created an empty file.")
36+
37+
38+
def update_domains_file():
39+
"""Continuously update the domains from the file."""
40+
while True:
41+
load_domains()
42+
time.sleep(60) # Update every minute
43+
44+
45+
class RequestHandler(BaseHTTPRequestHandler):
46+
"""HTTP Request Handler for domain queries."""
47+
48+
def do_GET(self):
49+
"""Handle GET requests."""
50+
parsed_path = urlparse(self.path)
51+
if parsed_path.path != "/check_domain":
52+
self.send_response(404)
53+
self.end_headers()
54+
self.wfile.write(b"Not Found")
55+
logging.warning(f"404 Not Found: {self.path}")
56+
return
57+
58+
query_params = parse_qs(parsed_path.query)
59+
domain = query_params.get("domain", [None])[0]
60+
61+
if not domain:
62+
self.send_response(400)
63+
self.end_headers()
64+
self.wfile.write(b'{"error": "Domain parameter is required"}')
65+
logging.warning("400 Bad Request: Missing domain parameter")
66+
return
67+
68+
is_allowed = domain in allowed_domains
69+
response = f'{{"domain": "{domain}", "allowed": {str(is_allowed).lower()}}}'
70+
self.send_response(200 if is_allowed else 404)
71+
self.send_header("Content-Type", "application/json")
72+
self.end_headers()
73+
self.wfile.write(response.encode())
74+
75+
logging.info(f"Domain query: {domain}, Allowed: {is_allowed}")
76+
77+
78+
def run_server():
79+
"""Run the HTTP server."""
80+
server_address = ("127.0.0.1", 8008)
81+
httpd = HTTPServer(server_address, RequestHandler)
82+
logging.info("Server running on http://127.0.0.1:8008")
83+
httpd.serve_forever()
84+
85+
86+
if __name__ == "__main__":
87+
# Load domains initially
88+
load_domains()
89+
90+
# Start the background thread to update domains
91+
updater_thread = Thread(target=update_domains_file, daemon=True)
92+
updater_thread.start()
93+
94+
# Run the HTTP server
95+
try:
96+
run_server()
97+
except KeyboardInterrupt:
98+
logging.info("Shutting down the server...")
99+
exit(0)

domain-checker.service

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[Unit]
2+
Description=Caddy Domain Checker Service
3+
After=network.target
4+
5+
[Service]
6+
ExecStart=/usr/bin/python3 /mnt/data/www/domain_checker/app.py
7+
WorkingDirectory=/mnt/data/www/domain_checker
8+
Restart=always
9+
RestartSec=5
10+
User=www-data
11+
Group=www-data
12+
StandardOutput=journal
13+
StandardError=journal
14+
15+
[Install]
16+
WantedBy=multi-user.target

service-log-tail.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
journalctl -u domain-checker.service -f

0 commit comments

Comments
 (0)