Skip to content

Commit 5cfd69c

Browse files
authored
Create CLI_manage_domains.sh
1 parent 2baef50 commit 5cfd69c

File tree

1 file changed

+264
-0
lines changed

1 file changed

+264
-0
lines changed

CLI_manage_domains.sh

+264
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
#!/usr/bin/env bash
2+
3+
# Manage domains on both Pi-hole and Nginx Proxy Manager
4+
#
5+
# Usage:
6+
# To add a domain:
7+
# ./manage_domains.sh add http(s)://<ip>:<port> domain.ext
8+
#
9+
# To delete a domain:
10+
# ./manage_domains.sh delete domain.ext
11+
#
12+
# To check certificates:
13+
# ./manage_domains.sh check_certs
14+
#
15+
16+
17+
# Configuration (adjust as needed):
18+
NGINX_EMAIL="[email protected]"
19+
NGINX_PASSWORD="your_nginx_password"
20+
NGINX_HOST="your_nginx_instance_FQDN_or_ip" # (for token retrieval; no scheme here)
21+
NGINX_IP="192.168.0.254" # The IP used in the Pi-hole DNS record
22+
23+
PIHOLE_PASSWORD="your_nginx_password"
24+
PIHOLE_HOST="your_pihole_instance_FQDN_or_ip"
25+
26+
27+
###############################################
28+
# Check for required commands
29+
###############################################
30+
check_dependencies() {
31+
for cmd in curl jq; do
32+
if ! command -v "$cmd" >/dev/null 2>&1; then
33+
echo "Error: Required command '$cmd' is not installed. Please install it." >&2
34+
exit 1
35+
fi
36+
done
37+
}
38+
39+
check_dependencies
40+
41+
42+
###############################################
43+
# Usage function
44+
###############################################
45+
usage() {
46+
echo "Usage:"
47+
echo " To add a domain: $0 add http(s)://<ip>:<port> domain.ext"
48+
echo " To delete a domain: $0 delete domain.ext"
49+
echo " To check certificates: $0 check_certs"
50+
exit 1
51+
}
52+
53+
54+
###############################################
55+
# Argument Parsing
56+
###############################################
57+
if [ "$#" -eq 0 ]; then
58+
usage
59+
fi
60+
61+
if [ "$1" = "check_certs" ]; then
62+
ACTION="check_certs"
63+
elif [ "$#" -eq 1 ]; then
64+
usage
65+
elif [ "$#" -eq 2 ]; then
66+
if [ "$1" = "delete" ]; then
67+
ACTION="delete"
68+
DOMAIN="$2"
69+
else
70+
usage
71+
fi
72+
elif [ "$#" -eq 3 ]; then
73+
#elif [ "$#" -eq 3 ]; then # Uncomment if you have multiple certificates to choose from.
74+
if [ "$1" = "add" ]; then
75+
ACTION="add"
76+
URL="$2"
77+
DOMAIN="$3"
78+
CERT_ID="1" # Certificate ID to automatically use it for SSL termination. Check yours by running the script with check_certs
79+
# CERT_ID="$4" # Uncomment if you have multiple certificates to choose from.
80+
else
81+
usage
82+
fi
83+
else
84+
usage
85+
fi
86+
87+
88+
###############################################
89+
# Nginx Proxy Manager Functions
90+
###############################################
91+
92+
get_nginx_token() {
93+
local resp token
94+
resp=$(curl -s -X POST "https://${NGINX_HOST}/api/tokens" \
95+
-H "Content-Type: application/json" \
96+
-d "{\"identity\": \"${NGINX_EMAIL}\", \"secret\": \"${NGINX_PASSWORD}\"}")
97+
token=$(echo "$resp" | jq -r '.token')
98+
if [ -z "$token" ] || [ "$token" = "null" ]; then
99+
echo "Failed to retrieve Nginx token." >&2
100+
exit 1
101+
fi
102+
echo "$token"
103+
}
104+
105+
106+
add_nginx_domain() {
107+
local token scheme rest forward_host forward_port payload response
108+
token=$(get_nginx_token)
109+
110+
# Parse URL: expecting format http(s)://<ip>:<port>
111+
scheme="${URL%%://*}"
112+
rest="${URL#*://}"
113+
forward_host="${rest%%:*}"
114+
forward_port="${rest#*:}"
115+
116+
# Validate that PORT is numeric and within 1-65535
117+
if ! [[ $forward_port =~ ^[0-9]+$ ]]; then
118+
echo "Error: Port '$forward_port' is not a valid number." >&2
119+
exit 1
120+
fi
121+
if [ "$forward_port" -lt 1 ] || [ "$forward_port" -gt 65535 ]; then
122+
echo "Error: Port must be between 1 and 65535, got '$forward_port'." >&2
123+
exit 1
124+
fi
125+
126+
payload=$(cat <<EOF
127+
{
128+
"domain_names": ["${DOMAIN}"],
129+
"forward_scheme": "${scheme}",
130+
"forward_host": "${forward_host}",
131+
"forward_port": ${forward_port},
132+
"certificate_id": ${CERT_ID},
133+
"ssl_forced": false,
134+
"hsts_enabled": false,
135+
"hsts_subdomains": false,
136+
"http2_support": false,
137+
"caching_enabled": false,
138+
"block_exploits": false,
139+
"advanced_config": "",
140+
"enabled": true,
141+
"meta": {}
142+
}
143+
EOF
144+
)
145+
response=$(curl -s -X POST "http://${NGINX_HOST}/api/nginx/proxy-hosts" \
146+
-H "Authorization: Bearer ${token}" \
147+
-H "Content-Type: application/json" \
148+
-d "${payload}")
149+
echo "Nginx add response:"
150+
echo "${response}"
151+
}
152+
153+
154+
delete_nginx_domain() {
155+
local token matches num_matches host_id response search_term
156+
token=$(get_nginx_token)
157+
search_term="${DOMAIN}"
158+
response=$(curl -s -X GET "http://${NGINX_HOST}/api/nginx/proxy-hosts" \
159+
-H "Authorization: Bearer ${token}" \
160+
-H "Content-Type: application/json")
161+
matches=$(echo "$response" | jq --arg search "$search_term" '[.[] | select(.domain_names[] | contains($search))]')
162+
num_matches=$(echo "$matches" | jq 'length')
163+
if [ "$num_matches" -eq 0 ]; then
164+
echo "No proxy hosts found containing '$search_term'." >&2
165+
exit 0
166+
elif [ "$num_matches" -gt 1 ]; then
167+
echo "Multiple matching hosts found. Please refine your search term." >&2
168+
echo "$matches" | jq '.[] | {id, domain_names}'
169+
exit 1
170+
else
171+
host_id=$(echo "$matches" | jq '.[0].id')
172+
echo "Deleting Nginx proxy host with ID ${host_id}..."
173+
response=$(curl -s -X DELETE "http://${NGINX_HOST}/api/nginx/proxy-hosts/${host_id}" \
174+
-H "Authorization: Bearer ${token}" \
175+
-H "Content-Type: application/json")
176+
echo "Nginx delete response:"
177+
echo "${response}"
178+
fi
179+
}
180+
181+
182+
check_nginx_certs() {
183+
local token resp
184+
token=$(get_nginx_token)
185+
resp=$(curl -s -X GET "http://${NGINX_HOST}/api/nginx/certificates?expand=owner" \
186+
-H "Authorization: Bearer ${token}" \
187+
-H "Content-Type: application/json")
188+
echo "Certificates:"
189+
echo "$resp" | jq .
190+
}
191+
192+
193+
###############################################
194+
# Pi-hole Functions
195+
###############################################
196+
197+
get_pihole_tokens() {
198+
local auth_json sid csrf
199+
auth_json=$(curl -s -X POST "https://${PIHOLE_HOST}/api/auth" \
200+
-H 'accept: application/json' \
201+
-H 'content-type: application/json' \
202+
-d "{\"password\":\"${PIHOLE_PASSWORD}\"}")
203+
sid=$(echo "$auth_json" | jq -r '.session.sid')
204+
csrf=$(echo "$auth_json" | jq -r '.session.csrf')
205+
if [ -z "$sid" ] || [ "$sid" = "null" ]; then
206+
echo "Pi-hole authentication failed." >&2
207+
exit 1
208+
fi
209+
echo "$sid;$csrf"
210+
}
211+
212+
213+
add_pihole_domain() {
214+
local tokens sid csrf url result
215+
tokens=$(get_pihole_tokens)
216+
sid="${tokens%%;*}"
217+
csrf="${tokens#*;}"
218+
url="https://${PIHOLE_HOST}/api/config/dns/hosts/${NGINX_IP}%20${DOMAIN}"
219+
result=$(curl -s -X PUT "$url" \
220+
-H 'content-type: application/json' \
221+
-d "{\"sid\":\"${sid}\"}" \
222+
-d "{\"csrf\":\"${csrf}\"}")
223+
echo "Pi-hole add response:"
224+
echo "${result}"
225+
}
226+
227+
228+
delete_pihole_domain() {
229+
local tokens sid csrf url result
230+
tokens=$(get_pihole_tokens)
231+
sid="${tokens%%;*}"
232+
csrf="${tokens#*;}"
233+
url="https://${PIHOLE_HOST}/api/config/dns/hosts/${NGINX_IP}%20${DOMAIN}"
234+
result=$(curl -s -X DELETE "$url" \
235+
-H 'content-type: application/json' \
236+
-d "{\"sid\":\"${sid}\"}" \
237+
-d "{\"csrf\":\"${csrf}\"}")
238+
echo "Pi-hole delete response:"
239+
echo "${result}"
240+
}
241+
242+
243+
###############################################
244+
# Main Execution
245+
###############################################
246+
case "$ACTION" in
247+
add)
248+
echo "Adding domain '${DOMAIN}'..."
249+
add_nginx_domain
250+
add_pihole_domain
251+
;;
252+
delete)
253+
echo "Deleting domain '${DOMAIN}'..."
254+
delete_nginx_domain
255+
delete_pihole_domain
256+
;;
257+
check_certs)
258+
echo "Checking certificates..."
259+
check_nginx_certs
260+
;;
261+
*)
262+
usage
263+
;;
264+
esac

0 commit comments

Comments
 (0)