335 lines
10 KiB
Bash
335 lines
10 KiB
Bash
#!/bin/sh
|
|
# /usr/libexec/rpcd/vhost-manager
|
|
# RPCD backend for VHost Manager module
|
|
# Provides ubus interface: luci.vhost-manager
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
# Configuration
|
|
UCI_CONFIG="vhost_manager"
|
|
NGINX_VHOSTS_DIR="/etc/nginx/conf.d"
|
|
ACME_DIR="/etc/acme"
|
|
|
|
# Helper: Check if nginx is running
|
|
nginx_running() {
|
|
pgrep -x nginx > /dev/null 2>&1
|
|
}
|
|
|
|
# Helper: Get nginx status
|
|
get_nginx_status() {
|
|
if nginx_running; then
|
|
echo "running"
|
|
else
|
|
echo "stopped"
|
|
fi
|
|
}
|
|
|
|
# Helper: Count vhosts
|
|
count_vhosts() {
|
|
if [ -d "$NGINX_VHOSTS_DIR" ]; then
|
|
ls -1 "$NGINX_VHOSTS_DIR"/*.conf 2>/dev/null | wc -l
|
|
else
|
|
echo "0"
|
|
fi
|
|
}
|
|
|
|
# Helper: List vhosts
|
|
list_vhosts() {
|
|
json_add_array "vhosts"
|
|
|
|
if [ -d "$NGINX_VHOSTS_DIR" ]; then
|
|
for conf in "$NGINX_VHOSTS_DIR"/*.conf; do
|
|
[ -f "$conf" ] || continue
|
|
|
|
# Extract server_name from config
|
|
SERVER_NAME=$(grep -m1 "server_name" "$conf" | awk '{print $2}' | tr -d ';')
|
|
PROXY_PASS=$(grep -m1 "proxy_pass" "$conf" | awk '{print $2}' | tr -d ';')
|
|
SSL_ENABLED="false"
|
|
|
|
if grep -q "ssl_certificate" "$conf"; then
|
|
SSL_ENABLED="true"
|
|
fi
|
|
|
|
json_add_object ""
|
|
json_add_string "name" "$(basename "$conf" .conf)"
|
|
json_add_string "domain" "$SERVER_NAME"
|
|
json_add_string "backend" "$PROXY_PASS"
|
|
json_add_boolean "ssl" "$SSL_ENABLED"
|
|
json_add_boolean "enabled" 1
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
}
|
|
|
|
# Helper: Get SSL certificates
|
|
list_certificates() {
|
|
json_add_array "certificates"
|
|
|
|
if [ -d "$ACME_DIR" ]; then
|
|
for cert_dir in "$ACME_DIR"/*/; do
|
|
[ -d "$cert_dir" ] || continue
|
|
|
|
DOMAIN=$(basename "$cert_dir")
|
|
CERT_FILE="$cert_dir/fullchain.cer"
|
|
|
|
if [ -f "$CERT_FILE" ]; then
|
|
# Get expiry date
|
|
EXPIRY=$(openssl x509 -enddate -noout -in "$CERT_FILE" 2>/dev/null | cut -d= -f2)
|
|
|
|
json_add_object ""
|
|
json_add_string "domain" "$DOMAIN"
|
|
json_add_string "expiry" "$EXPIRY"
|
|
json_add_boolean "valid" 1
|
|
json_close_object
|
|
fi
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
}
|
|
|
|
# Initialize JSON
|
|
json_init
|
|
|
|
case "$1" in
|
|
list)
|
|
# List available methods
|
|
json_add_object "status"
|
|
json_close_object
|
|
|
|
json_add_object "get_vhosts"
|
|
json_close_object
|
|
|
|
json_add_object "get_vhost"
|
|
json_add_string "name" "string"
|
|
json_close_object
|
|
|
|
json_add_object "add_vhost"
|
|
json_add_string "domain" "string"
|
|
json_add_string "backend" "string"
|
|
json_add_boolean "ssl" false
|
|
json_close_object
|
|
|
|
json_add_object "delete_vhost"
|
|
json_add_string "name" "string"
|
|
json_close_object
|
|
|
|
json_add_object "get_certificates"
|
|
json_close_object
|
|
|
|
json_add_object "request_certificate"
|
|
json_add_string "domain" "string"
|
|
json_close_object
|
|
|
|
json_add_object "reload_nginx"
|
|
json_close_object
|
|
|
|
json_add_object "test_config"
|
|
json_close_object
|
|
|
|
json_dump
|
|
;;
|
|
|
|
call)
|
|
case "$2" in
|
|
status)
|
|
# Return module status
|
|
json_add_string "module" "vhost-manager"
|
|
json_add_string "version" "2.0.0"
|
|
json_add_string "nginx_status" "$(get_nginx_status)"
|
|
json_add_boolean "nginx_running" $(nginx_running && echo 1 || echo 0)
|
|
json_add_int "vhost_count" "$(count_vhosts)"
|
|
json_add_string "config_dir" "$NGINX_VHOSTS_DIR"
|
|
json_add_string "acme_dir" "$ACME_DIR"
|
|
|
|
# Check nginx version
|
|
if command -v nginx > /dev/null 2>&1; then
|
|
NGINX_VERSION=$(nginx -v 2>&1 | cut -d/ -f2)
|
|
json_add_string "nginx_version" "$NGINX_VERSION"
|
|
fi
|
|
|
|
json_dump
|
|
;;
|
|
|
|
get_vhosts)
|
|
# Return list of vhosts
|
|
list_vhosts
|
|
json_dump
|
|
;;
|
|
|
|
get_vhost)
|
|
# Get single vhost details
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var vhost_name name
|
|
|
|
CONF_FILE="$NGINX_VHOSTS_DIR/${vhost_name}.conf"
|
|
|
|
if [ -f "$CONF_FILE" ]; then
|
|
json_add_boolean "found" 1
|
|
json_add_string "name" "$vhost_name"
|
|
json_add_string "config" "$(cat "$CONF_FILE")"
|
|
else
|
|
json_add_boolean "found" 0
|
|
json_add_string "error" "VHost not found"
|
|
fi
|
|
|
|
json_dump
|
|
;;
|
|
|
|
add_vhost)
|
|
# Add new vhost
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var domain domain
|
|
json_get_var backend backend
|
|
json_get_var ssl ssl
|
|
|
|
# Validate
|
|
if [ -z "$domain" ] || [ -z "$backend" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Domain and backend are required"
|
|
json_dump
|
|
exit 0
|
|
fi
|
|
|
|
# Create config
|
|
VHOST_NAME=$(echo "$domain" | tr '.' '-')
|
|
CONF_FILE="$NGINX_VHOSTS_DIR/${VHOST_NAME}.conf"
|
|
|
|
mkdir -p "$NGINX_VHOSTS_DIR"
|
|
|
|
cat > "$CONF_FILE" << NGINX_EOF
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name $domain;
|
|
|
|
location / {
|
|
proxy_pass $backend;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
}
|
|
}
|
|
NGINX_EOF
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "VHost created"
|
|
json_add_string "config_file" "$CONF_FILE"
|
|
json_dump
|
|
;;
|
|
|
|
delete_vhost)
|
|
# Delete vhost
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var vhost_name name
|
|
|
|
CONF_FILE="$NGINX_VHOSTS_DIR/${vhost_name}.conf"
|
|
|
|
if [ -f "$CONF_FILE" ]; then
|
|
rm -f "$CONF_FILE"
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "VHost deleted"
|
|
else
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "VHost not found"
|
|
fi
|
|
|
|
json_dump
|
|
;;
|
|
|
|
get_certificates)
|
|
# List SSL certificates
|
|
list_certificates
|
|
json_dump
|
|
;;
|
|
|
|
request_certificate)
|
|
# Request Let's Encrypt certificate
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var domain domain
|
|
|
|
if [ -z "$domain" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Domain is required"
|
|
json_dump
|
|
exit 0
|
|
fi
|
|
|
|
# Check if acme.sh is available
|
|
if command -v acme.sh > /dev/null 2>&1; then
|
|
# Request certificate (async - just start the process)
|
|
acme.sh --issue -d "$domain" --webroot /www --keylength ec-256 &
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Certificate request started"
|
|
json_add_string "domain" "$domain"
|
|
else
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "acme.sh not installed"
|
|
fi
|
|
|
|
json_dump
|
|
;;
|
|
|
|
reload_nginx)
|
|
# Reload nginx configuration
|
|
if nginx -t > /dev/null 2>&1; then
|
|
/etc/init.d/nginx reload
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Nginx reloaded"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Configuration test failed"
|
|
json_add_string "details" "$(nginx -t 2>&1)"
|
|
fi
|
|
json_dump
|
|
;;
|
|
|
|
test_config)
|
|
# Test nginx configuration
|
|
TEST_OUTPUT=$(nginx -t 2>&1)
|
|
TEST_RESULT=$?
|
|
|
|
if [ $TEST_RESULT -eq 0 ]; then
|
|
json_add_boolean "valid" 1
|
|
json_add_string "message" "Configuration OK"
|
|
else
|
|
json_add_boolean "valid" 0
|
|
json_add_string "error" "$TEST_OUTPUT"
|
|
fi
|
|
json_dump
|
|
;;
|
|
|
|
*)
|
|
# Unknown method
|
|
json_add_int "error" -32601
|
|
json_add_string "message" "Method not found: $2"
|
|
json_dump
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
*)
|
|
echo "Usage: $0 {list|call}"
|
|
exit 1
|
|
;;
|
|
esac
|