Skip to content

Instantly share code, notes, and snippets.

@dnorhoj
Last active December 14, 2022 22:54
Show Gist options
  • Save dnorhoj/ade8a34cff0c0ef29e22ea9206f833f7 to your computer and use it in GitHub Desktop.
Save dnorhoj/ade8a34cff0c0ef29e22ea9206f833f7 to your computer and use it in GitHub Desktop.
Script for managing mappings to local server ports in nginx. Also adds/removes ssl and dns CNAME
#!/bin/bash
# 2022 - Daniel Norhøj <daniel@dnorhoj.me> and Super02
# This script is used to manage the sites on the server.
# It is used to add, remove, and list sites.
# The sites are stored in the /etc/nginx/sites-available directory.
# The sites are enabled by creating a symlink in the /etc/nginx/sites-enabled directory.
# This script requires
# - certbot (for ssl)
# - nginx (for nginx management)
# - curl (for sending cloudflare api requests)
# - jq (for parsing the api requests)
# -- Colors --
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
# -- Helper functions --
has_ssl() {
return $(grep -q "ssl_certificate" "/etc/nginx/sites-available/$1")
}
get_port() {
# Get the port from the site config
grep "proxy_pass" "/etc/nginx/sites-available/$1" | grep -oE "http://.+:[0-9]+" | cut -d: -f3
}
check_root() {
if [ "$(id -u)" != "0" ]; then
echo -e "${RED}Error: ${NC}Please run as root"
exit 1
fi
}
load_config() {
if [ ! -f "/etc/site-manager.conf" ]; then
echo -e "${RED}Error: ${NC}Config file not found: /etc/site-manager.conf"
echo "Please create the config file and add the following variables:"
echo ""
echo "CLOUDFLARE_API_TOKEN=\"<token>\""
echo "CLOUDFLARE_DNS_ZONE_ID=\"<zone_id>\""
echo "CLOUDFLARE_DNS_TARGET=\"<target>\""
exit 1
fi
# Load the config file
source "/etc/site-manager.conf"
}
# -- Subcommands --
list_sites() {
# List all sites in the /etc/nginx/sites-available directory
echo -e "Available sites: (${RED}red${NC} = disabled, ${YELLOW}yellow${NC} = enabled (no ssl), ${GREEN}green${NC} = enabled (ssl))"
for site in /etc/nginx/sites-available/*; do
if [ "$(basename $site)" == "default" ]; then
continue
fi
if [ -L "/etc/nginx/sites-enabled/$(basename $site)" ]; then
# Check if site has SSL
if has_ssl "$(basename $site)"; then
echo -en "- ${GREEN}$(basename $site)${NC}"
else
echo -en "- ${YELLOW}$(basename $site)${NC}"
fi
else
# If the site is disabled, print it in red
echo -en "- ${RED}$(basename $site)${NC}"
fi
# Print the port
echo -e " -> $(get_port "$(basename $site)")"
done
}
add_site() {
check_root
# Add a new site to the server
# The site name is the first argument
site_name=$2
port=$3
# Check if site name or port is empty
if [ -z "$port" ]; then
echo "Usage: $0 $1 [site_name] [port]"
exit 1
fi
# Check if the site already exists
if [ -f "/etc/nginx/sites-available/$site_name" ]; then
echo -e "${RED}Error: ${NC}Site already exists: $site_name"
exit 1
fi
# Check if the site is wrong format
if [[ ! "$site_name" =~ ^[a-zA-Z0-9.-]+$ ]]; then
echo -e "${RED}Error: ${NC}Site name is not valid: $site_name"
echo -e "${YELLOW}Example: ${NC}test.example.com"
exit 1
fi
# Check if port is wrong format
if [[ ! "$port" =~ ^[0-9]+$ ]]; then
echo -e "${RED}Error: ${NC}Port is not valid: $port"
echo -e "${YELLOW}Example: ${NC}3000"
exit 1
fi
# Create the site file
echo -e "${YELLOW}Creating site${NC} (path: /etc/nginx/sites-available/$site_name)"
cat > "/etc/nginx/sites-available/$site_name" << EOF
server {
listen 80;
listen [::]:80;
server_name $site_name;
location / {
include proxy_params;
proxy_pass http://127.0.0.1:$port;
}
}
EOF
ln -s "/etc/nginx/sites-available/$site_name" "/etc/nginx/sites-enabled/$site_name"
echo -e "${GREEN}Site created and enabled:${NC} $site_name -> $port"
# Reload nginx
echo -e "${YELLOW}Reloading nginx...${NC}"
systemctl reload nginx
}
remove_site() {
check_root
# Remove a site from the server
# The site name is the first argument
site_name=$2
# Check if site name is empty
if [ -z "$site_name" ]; then
echo "Usage: $0 $1 [site_name]"
exit 1
fi
# Check if the site exists
if [ ! -f "/etc/nginx/sites-available/$site_name" ]; then
echo -e "${RED}Error: ${NC}Site does not exist: $site_name"
exit 1
fi
# Are you sure?
read -p "Are you sure you want to remove the site $site_name? [y/N] " -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
# Remove the site file
echo -e "${YELLOW}Removing site${NC} (path: /etc/nginx/sites-available/$site_name)"
rm "/etc/nginx/sites-available/$site_name"
rm "/etc/nginx/sites-enabled/$site_name" 2> /dev/null
echo -e "${GREEN}Site removed:${NC} $site_name"
# Reload nginx
echo -e "${YELLOW}Reloading nginx...${NC}"
systemctl reload nginx
}
enable_site() {
check_root
# Enable a site
# The site name is the first argument
site_name=$2
# Check if site name is empty
if [ -z "$site_name" ]; then
echo "Usage: $0 $1 [site_name]"
exit 1
fi
# Check if the site exists
if [ ! -f "/etc/nginx/sites-available/$site_name" ]; then
echo -e "${RED}Error: ${NC}Site does not exist: $site_name"
exit 1
fi
# Check if the site is already enabled
if [ -L "/etc/nginx/sites-enabled/$site_name" ]; then
echo -e "${RED}Error: ${NC}Site is already enabled: $site_name"
exit 1
fi
# Enable the site
ln -s "/etc/nginx/sites-available/$site_name" "/etc/nginx/sites-enabled/$site_name"
echo -e "${GREEN}Site enabled:${NC} $site_name"
# Reload nginx
echo -e "${YELLOW}Reloading nginx...${NC}"
systemctl reload nginx
}
disable_site() {
check_root
# Disable a site
# The site name is the first argument
site_name=$2
# Check if site name is empty
if [ -z "$site_name" ]; then
echo "Usage: $0 $1 [site_name]"
exit 1
fi
# Check if the site exists
if [ ! -f "/etc/nginx/sites-available/$site_name" ]; then
echo -e "${RED}Error: ${NC}Site does not exist: $site_name"
exit 1
fi
# Check if the site is already disabled
if [ ! -L "/etc/nginx/sites-enabled/$site_name" ]; then
echo -e "${RED}Error: ${NC}Site is already disabled: $site_name"
exit 1
fi
# Disable the site
rm "/etc/nginx/sites-enabled/$site_name"
echo -e "${GREEN}Site disabled:${NC} $site_name"
# Reload nginx
echo -e "${YELLOW}Reloading nginx...${NC}"
systemctl reload nginx
}
enable_ssl() {
check_root
# Enable SSL for a site
# The site name is the first argument
site_name=$2
# Check if site name is empty
if [ -z "$site_name" ]; then
echo "Usage: $0 $1 [site_name]"
exit 1
fi
# Check if the site exists
if [ ! -f "/etc/nginx/sites-available/$site_name" ]; then
echo -e "${RED}Error: ${NC}Site does not exist: $site_name"
exit 1
fi
# Check if the site is already enabled
if [ ! -L "/etc/nginx/sites-enabled/$site_name" ]; then
echo -e "${RED}Error: ${NC}You have to enable the site before enabling SSL!"
exit 1
fi
echo -e "${YELLOW}Enabling SSL...${NC}"
# Use certbot to enable SSL
certbot --nginx -d $site_name --non-interactive
if [ $? -eq 0 ]; then
echo -e "${GREEN}SSL enabled${NC}"
else
echo -e "${RED}Error: ${NC}Failed to enable SSL for $site_name"
exit 1
fi
}
add_dns() {
# Load config
load_config
# Add DNS records for a site via Cloudflare API
# The site name is the first argument
site_name=$2
# Check if site name is empty
if [ -z "$site_name" ]; then
echo "Usage: $0 $1 [site_name]"
exit 1
fi
# Check if the site exists
if [ ! -f "/etc/nginx/sites-available/$site_name" ]; then
echo -e "${YELLOW}Warning: ${NC}The site '$site_name' does not exist."
read -p "Do you want to add a DNS record for '$site_name' anyway? [y/N] " -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
# Add DNS record
response=$(curl -X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_DNS_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data "{\"type\":\"CNAME\",\"name\":\"$site_name\",\"content\":\"$CLOUDFLARE_DNS_TARGET\",\"ttl\":1,\"proxied\":true}" \
--write-out '%{http_code}' --silent --output /dev/null
)
if [ "$response" != "200" ]; then
echo -e "${RED}Error: ${NC}Failed to add DNS record for domain: $site_name -> $CLOUDFLARE_DNS_TARGET"
echo "Response code: $response"
exit 1
fi
echo -e "${GREEN}DNS record added for domain:${NC} $site_name -> $CLOUDFLARE_DNS_TARGET"
}
remove_dns() {
# Load config
load_config
# Add DNS records for a site via Cloudflare API
# The site name is the first argument
site_name=$2
# Check if site name is empty
if [ -z "$site_name" ]; then
echo "Usage: $0 $1 [site_name]"
exit 1
fi
# Check if the site exists
if [ ! -f "/etc/nginx/sites-available/$site_name" ]; then
echo -e "${YELLOW}Warning: ${NC}The site '$site_name' does not exist."
read -p "Do you want to remove the DNS record anyway? [y/N] " -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
# Get identifier of DNS record
dns_record_id=$(
curl -X GET "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_DNS_ZONE_ID/dns_records?name=$site_name" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--silent | jq -r .result[0].id
)
# Remove DNS record
if [ "$dns_record_id" == null ]; then
echo -e "${RED}Error: ${NC}Site DNS does not exist: $site_name"
exit 1
fi
response=$(
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_DNS_ZONE_ID/dns_records/$dns_record_id" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--write-out '%{http_code}' --silent --output /dev/null
)
if [ "$response" != "200" ]; then
echo -e "${RED}Error: ${NC}Failed to remove DNS record for domain: $site_name -> $CLOUDFLARE_DNS_TARGET"
echo "Response code: $response"
exit 1
fi
echo -e "${GREEN}DNS record removed for domain:${NC} $site_name -> $CLOUDFLARE_DNS_TARGET"
}
full_add() {
# Check if the script is run as root
check_root
# Check if address/port is empty
if [ -z "$3" ]; then
echo "Usage: $0 $1 [site_name] [port]"
exit 1
fi
# Add site
add_site $@
# Add DNS records
add_dns $@
# Enable SSL
enable_ssl $@
}
full_remove() {
# Check if the script is run as root
check_root
# Check if address/port is empty
if [ -z "$2" ]; then
echo "Usage: $0 $1 [site_name]"
exit 1
fi
# Remove DNS records
remove_dns $@
# Remove site
remove_site $@
}
usage() {
echo "Usage: $1 [subcommand] [options]"
echo "Run '$1 --help' for more information."
exit 1
}
# -- Main script --
subcommand=$1
if [ -z "$subcommand" ]; then
usage $0
fi
case "$subcommand" in
--help|-h)
echo "Usage: $0 [subcommand] [options]"
echo "Subcommands:"
echo " list,l,ls: List all sites"
echo " add,a: Add a site"
echo " enable,e: Enable a site"
echo " add_dns,dns: Add DNS records for a site via Cloudflare API"
echo " add_ssl,ssl: Enable SSL for a site"
echo " full_add,aa: Add a site, add DNS records and enable SSL"
echo " remove,r,rm: Remove a site"
echo " disable,d: Disable a site"
echo " remove_dns,rd: Remove DNS records for a site via Cloudflare API"
echo " full_remove,rr: Remove a site and remove DNS records"
echo " reload,rl: Reload nginx"
echo " --help,-h: Show this help"
exit 0
;;
list|l|ls)
list_sites $@
;;
add|a)
add_site $@
;;
enable|e)
enable_site $@
;;
add_dns|dns)
add_dns $@
;;
add_ssl|ssl)
enable_ssl $@
;;
full_add|aa)
full_add $@
;;
remove|r)
remove_site $@
;;
disable|d)
disable_site $@
;;
remove_dns|rd)
remove_dns $@
;;
full_remove|rr)
full_remove $@
;;
reload|rl)
echo -e "${YELLOW}Reloading nginx...${NC}"
systemctl reload nginx
;;
*)
echo -e "${RED}Unknown subcommand:${NC} $subcommand"
usage $0
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment