8.1 Caddy Admin API概述
8.1.1 API简介
Caddy Admin API是一个强大的RESTful API,允许您在运行时动态管理Caddy配置。
主要特性: - 实时配置更新 - 无需重启服务 - 完整的配置管理 - 监控和诊断 - 安全的访问控制
8.1.2 API端点
# 默认API地址
http://localhost:2019
# 主要端点
GET /config/ # 获取完整配置
POST /config/ # 更新完整配置
PATCH /config/ # 部分更新配置
DELETE /config/ # 删除配置
GET /config/apps/ # 获取应用配置
POST /load # 加载配置
POST /stop # 停止服务
8.1.3 启用Admin API
# Caddyfile配置
{
admin localhost:2019
# 或禁用API
# admin off
}
example.com {
respond "Hello World"
}
{
"admin": {
"listen": "localhost:2019"
},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [":80"],
"routes": [
{
"match": [{"host": ["example.com"]}],
"handle": [{
"handler": "static_response",
"body": "Hello World"
}]
}
]
}
}
}
}
}
8.2 JSON配置格式
8.2.1 配置结构
{
"admin": {
"listen": "localhost:2019",
"enforce_origin": false,
"origins": ["localhost:2019", "127.0.0.1:2019"]
},
"logging": {
"logs": {
"default": {
"level": "INFO",
"writer": {
"output": "stdout"
}
}
}
},
"storage": {
"module": "file_system",
"root": "/var/lib/caddy"
},
"apps": {
"http": {},
"tls": {},
"pki": {}
}
}
8.2.2 HTTP应用配置
{
"apps": {
"http": {
"http_port": 80,
"https_port": 443,
"grace_period": "5s",
"servers": {
"srv0": {
"listen": [":80", ":443"],
"routes": [
{
"match": [
{
"host": ["example.com"],
"path": ["/api/*"]
}
],
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{"dial": "localhost:8080"}
]
}
]
}
],
"automatic_https": {
"disable": false,
"disable_redirects": false
}
}
}
}
}
}
8.2.3 TLS配置
{
"apps": {
"tls": {
"automation": {
"policies": [
{
"subjects": ["example.com", "*.example.com"],
"issuers": [
{
"module": "acme",
"ca": "https://acme-v02.api.letsencrypt.org/directory",
"email": "admin@example.com",
"agreed": true,
"challenges": {
"dns": {
"provider": {
"name": "cloudflare",
"api_token": "{env.CLOUDFLARE_API_TOKEN}"
}
}
}
}
]
}
]
},
"certificates": {
"load_files": [
{
"certificate": "/path/to/cert.pem",
"key": "/path/to/key.pem"
}
]
}
}
}
}
8.3 API操作实例
8.3.1 获取配置
# 获取完整配置
curl http://localhost:2019/config/ | jq
# 获取特定路径配置
curl http://localhost:2019/config/apps/http/servers
# 获取路由配置
curl http://localhost:2019/config/apps/http/servers/srv0/routes
8.3.2 更新配置
# 添加新路由
curl -X POST http://localhost:2019/config/apps/http/servers/srv0/routes \
-H "Content-Type: application/json" \
-d '{
"match": [{"host": ["api.example.com"]}],
"handle": [{
"handler": "reverse_proxy",
"upstreams": [{"dial": "localhost:3000"}]
}]
}'
# 更新现有路由
curl -X PUT http://localhost:2019/config/apps/http/servers/srv0/routes/0 \
-H "Content-Type: application/json" \
-d '{
"match": [{"host": ["updated.example.com"]}],
"handle": [{
"handler": "static_response",
"body": "Updated response"
}]
}'
8.3.3 删除配置
# 删除特定路由
curl -X DELETE http://localhost:2019/config/apps/http/servers/srv0/routes/0
# 删除整个服务器配置
curl -X DELETE http://localhost:2019/config/apps/http/servers/srv0
8.4 配置管理脚本
8.4.1 Python管理脚本
#!/usr/bin/env python3
import requests
import json
import sys
class CaddyAPI:
def __init__(self, base_url="http://localhost:2019"):
self.base_url = base_url
self.session = requests.Session()
def get_config(self, path=""):
"""获取配置"""
url = f"{self.base_url}/config/{path}"
response = self.session.get(url)
response.raise_for_status()
return response.json()
def update_config(self, path, config):
"""更新配置"""
url = f"{self.base_url}/config/{path}"
response = self.session.post(url, json=config)
response.raise_for_status()
return response.json() if response.content else None
def delete_config(self, path):
"""删除配置"""
url = f"{self.base_url}/config/{path}"
response = self.session.delete(url)
response.raise_for_status()
def add_route(self, server, route_config):
"""添加路由"""
path = f"apps/http/servers/{server}/routes"
return self.update_config(path, route_config)
def add_upstream(self, server, route_index, upstream):
"""添加上游服务器"""
path = f"apps/http/servers/{server}/routes/{route_index}/handle/0/upstreams"
return self.update_config(path, upstream)
def reload_config(self, config_file):
"""重新加载配置文件"""
with open(config_file, 'r') as f:
config = json.load(f)
url = f"{self.base_url}/load"
response = self.session.post(url, json=config)
response.raise_for_status()
# 使用示例
if __name__ == "__main__":
api = CaddyAPI()
# 获取当前配置
config = api.get_config()
print(json.dumps(config, indent=2))
# 添加新路由
new_route = {
"match": [{"host": ["test.example.com"]}],
"handle": [{
"handler": "static_response",
"body": "Test response"
}]
}
api.add_route("srv0", new_route)
print("Route added successfully")
8.4.2 Bash管理脚本
#!/bin/bash
# Caddy API管理脚本
CADDY_API="http://localhost:2019"
# 获取配置
get_config() {
local path="${1:-}"
curl -s "$CADDY_API/config/$path" | jq .
}
# 添加路由
add_route() {
local server="$1"
local host="$2"
local upstream="$3"
local route_config=$(cat <<EOF
{
"match": [{"host": ["$host"]}],
"handle": [{
"handler": "reverse_proxy",
"upstreams": [{"dial": "$upstream"}]
}]
}
EOF
)
curl -X POST "$CADDY_API/config/apps/http/servers/$server/routes" \
-H "Content-Type: application/json" \
-d "$route_config"
}
# 删除路由
delete_route() {
local server="$1"
local route_index="$2"
curl -X DELETE "$CADDY_API/config/apps/http/servers/$server/routes/$route_index"
}
# 重新加载配置
reload_config() {
local config_file="$1"
curl -X POST "$CADDY_API/load" \
-H "Content-Type: application/json" \
-d @"$config_file"
}
# 健康检查
health_check() {
if curl -s "$CADDY_API/config/" > /dev/null; then
echo "Caddy API is healthy"
return 0
else
echo "Caddy API is not responding"
return 1
fi
}
# 主函数
main() {
case "$1" in
"get")
get_config "$2"
;;
"add-route")
add_route "$2" "$3" "$4"
;;
"delete-route")
delete_route "$2" "$3"
;;
"reload")
reload_config "$2"
;;
"health")
health_check
;;
*)
echo "Usage: $0 {get|add-route|delete-route|reload|health}"
echo " get [path] - Get configuration"
echo " add-route server host upstream - Add route"
echo " delete-route server index - Delete route"
echo " reload config-file - Reload configuration"
echo " health - Health check"
exit 1
;;
esac
}
main "$@"
8.5 动态配置管理
8.5.1 配置模板系统
#!/usr/bin/env python3
import json
import os
from string import Template
class CaddyConfigTemplate:
def __init__(self, template_dir="templates"):
self.template_dir = template_dir
def load_template(self, template_name):
"""加载配置模板"""
template_path = os.path.join(self.template_dir, f"{template_name}.json")
with open(template_path, 'r') as f:
return Template(f.read())
def generate_config(self, template_name, variables):
"""生成配置"""
template = self.load_template(template_name)
config_str = template.substitute(variables)
return json.loads(config_str)
def create_site_config(self, domain, upstream, ssl_email):
"""创建站点配置"""
variables = {
'domain': domain,
'upstream': upstream,
'ssl_email': ssl_email
}
return self.generate_config('site', variables)
# 模板文件: templates/site.json
site_template = '''
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [":80", ":443"],
"routes": [
{
"match": [{"host": ["$domain"]}],
"handle": [{
"handler": "reverse_proxy",
"upstreams": [{"dial": "$upstream"}]
}]
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": ["$domain"],
"issuers": [
{
"module": "acme",
"email": "$ssl_email"
}
]
}
]
}
}
}
}
'''
8.5.2 配置验证
import jsonschema
# Caddy配置JSON Schema
caddy_schema = {
"type": "object",
"properties": {
"admin": {
"type": "object",
"properties": {
"listen": {"type": "string"},
"enforce_origin": {"type": "boolean"}
}
},
"apps": {
"type": "object",
"properties": {
"http": {
"type": "object",
"properties": {
"servers": {
"type": "object",
"patternProperties": {
".*": {
"type": "object",
"properties": {
"listen": {
"type": "array",
"items": {"type": "string"}
},
"routes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"match": {"type": "array"},
"handle": {"type": "array"}
},
"required": ["handle"]
}
}
},
"required": ["listen"]
}
}
}
}
}
}
}
}
}
def validate_config(config):
"""验证Caddy配置"""
try:
jsonschema.validate(config, caddy_schema)
return True, None
except jsonschema.ValidationError as e:
return False, str(e)
# 使用示例
config = {
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [":80"],
"routes": [
{
"handle": [{
"handler": "static_response",
"body": "Hello"
}]
}
]
}
}
}
}
}
valid, error = validate_config(config)
if valid:
print("Configuration is valid")
else:
print(f"Configuration error: {error}")
8.6 监控和诊断
8.6.1 API监控
# 获取服务器状态
curl http://localhost:2019/config/apps/http/servers/srv0
# 获取路由信息
curl http://localhost:2019/config/apps/http/servers/srv0/routes
# 获取TLS证书信息
curl http://localhost:2019/config/apps/tls/certificates
8.6.2 配置备份和恢复
#!/usr/bin/env python3
import json
import datetime
import os
class CaddyBackup:
def __init__(self, api_url="http://localhost:2019", backup_dir="backups"):
self.api_url = api_url
self.backup_dir = backup_dir
os.makedirs(backup_dir, exist_ok=True)
def backup_config(self):
"""备份当前配置"""
import requests
response = requests.get(f"{self.api_url}/config/")
response.raise_for_status()
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = os.path.join(self.backup_dir, f"caddy_config_{timestamp}.json")
with open(backup_file, 'w') as f:
json.dump(response.json(), f, indent=2)
print(f"Configuration backed up to {backup_file}")
return backup_file
def restore_config(self, backup_file):
"""恢复配置"""
import requests
with open(backup_file, 'r') as f:
config = json.load(f)
response = requests.post(f"{self.api_url}/load", json=config)
response.raise_for_status()
print(f"Configuration restored from {backup_file}")
def list_backups(self):
"""列出所有备份"""
backups = [f for f in os.listdir(self.backup_dir) if f.endswith('.json')]
backups.sort(reverse=True)
return backups
# 使用示例
if __name__ == "__main__":
backup = CaddyBackup()
# 创建备份
backup.backup_config()
# 列出备份
backups = backup.list_backups()
print("Available backups:")
for b in backups:
print(f" {b}")
8.7 实战案例
8.7.1 动态负载均衡
#!/usr/bin/env python3
import requests
import time
import threading
class DynamicLoadBalancer:
def __init__(self, caddy_api="http://localhost:2019"):
self.caddy_api = caddy_api
self.upstreams = []
self.health_check_interval = 30
def add_upstream(self, upstream):
"""添加上游服务器"""
self.upstreams.append(upstream)
self._update_caddy_config()
def remove_upstream(self, upstream):
"""移除上游服务器"""
if upstream in self.upstreams:
self.upstreams.remove(upstream)
self._update_caddy_config()
def _update_caddy_config(self):
"""更新Caddy配置"""
config = {
"match": [{"host": ["api.example.com"]}],
"handle": [{
"handler": "reverse_proxy",
"upstreams": [{"dial": upstream} for upstream in self.upstreams],
"health_checks": {
"active": {
"path": "/health",
"interval": "30s",
"timeout": "5s"
}
}
}]
}
# 更新Caddy配置
url = f"{self.caddy_api}/config/apps/http/servers/srv0/routes/0"
response = requests.put(url, json=config)
response.raise_for_status()
print(f"Updated load balancer with {len(self.upstreams)} upstreams")
def health_check(self):
"""健康检查"""
healthy_upstreams = []
for upstream in self.upstreams:
try:
response = requests.get(f"http://{upstream}/health", timeout=5)
if response.status_code == 200:
healthy_upstreams.append(upstream)
except requests.RequestException:
print(f"Upstream {upstream} is unhealthy")
if set(healthy_upstreams) != set(self.upstreams):
self.upstreams = healthy_upstreams
self._update_caddy_config()
def start_health_monitoring(self):
"""启动健康监控"""
def monitor():
while True:
self.health_check()
time.sleep(self.health_check_interval)
thread = threading.Thread(target=monitor, daemon=True)
thread.start()
print("Health monitoring started")
# 使用示例
if __name__ == "__main__":
lb = DynamicLoadBalancer()
# 添加上游服务器
lb.add_upstream("localhost:8001")
lb.add_upstream("localhost:8002")
lb.add_upstream("localhost:8003")
# 启动健康监控
lb.start_health_monitoring()
# 保持运行
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Stopping load balancer")
8.7.2 自动SSL证书管理
#!/usr/bin/env python3
import requests
import json
from datetime import datetime, timedelta
class SSLManager:
def __init__(self, caddy_api="http://localhost:2019"):
self.caddy_api = caddy_api
def add_domain(self, domain, email):
"""添加域名并自动获取SSL证书"""
# 获取当前TLS配置
tls_config = self._get_tls_config()
# 添加新的自动化策略
new_policy = {
"subjects": [domain],
"issuers": [
{
"module": "acme",
"ca": "https://acme-v02.api.letsencrypt.org/directory",
"email": email,
"agreed": True
}
]
}
if "automation" not in tls_config:
tls_config["automation"] = {"policies": []}
tls_config["automation"]["policies"].append(new_policy)
# 更新TLS配置
self._update_tls_config(tls_config)
print(f"Added SSL automation for domain: {domain}")
def _get_tls_config(self):
"""获取当前TLS配置"""
try:
response = requests.get(f"{self.caddy_api}/config/apps/tls")
response.raise_for_status()
return response.json()
except requests.RequestException:
return {}
def _update_tls_config(self, config):
"""更新TLS配置"""
response = requests.put(f"{self.caddy_api}/config/apps/tls", json=config)
response.raise_for_status()
def get_certificates(self):
"""获取所有证书信息"""
response = requests.get(f"{self.caddy_api}/config/apps/tls/certificates")
response.raise_for_status()
return response.json()
def check_certificate_expiry(self):
"""检查证书过期时间"""
certificates = self.get_certificates()
expiring_soon = []
for cert_id, cert_info in certificates.items():
# 这里需要解析证书信息来获取过期时间
# 实际实现中需要使用cryptography库
print(f"Certificate {cert_id}: {cert_info}")
return expiring_soon
# 使用示例
if __name__ == "__main__":
ssl_manager = SSLManager()
# 为新域名添加SSL
ssl_manager.add_domain("new.example.com", "admin@example.com")
# 检查证书状态
certificates = ssl_manager.get_certificates()
print(json.dumps(certificates, indent=2))
8.8 本章总结
本章详细介绍了Caddy的Admin API和JSON配置格式:
- Admin API基础:了解API端点和基本操作
- JSON配置:掌握完整的JSON配置结构
- 动态管理:实现运行时配置更新
- 脚本自动化:使用Python和Bash进行配置管理
- 监控诊断:配置验证和健康检查
- 实战应用:动态负载均衡和SSL管理
8.9 练习题
- 使用Admin API创建一个新的虚拟主机
- 编写脚本自动备份和恢复Caddy配置
- 实现基于健康检查的动态上游管理
- 创建配置模板系统支持多环境部署
- 开发证书过期监控和自动续期系统
下一章预告:第九章将介绍Caddy的高级特性,包括中间件开发、自定义插件和企业级功能。