setup_hosting.sh
· 8.6 KiB · Bash
Bruto
#!/bin/bash
# =============================================================================
# Hosting setup with Cloudflare Tunnel
# Author Fedya Serafiev
# Site itpraktika.com
# =============================================================================
set -euo pipefail
# -- Colors -------------------------------------------------------------------
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
info() { echo -e "${CYAN}[INFO] $*${RESET}"; }
success() { echo -e "${GREEN}[OK] $*${RESET}"; }
warn() { echo -e "${YELLOW}[WARN] $*${RESET}"; }
error() { echo -e "${RED}[ERROR] $*${RESET}" >&2; exit 1; }
# -- Password generator -------------------------------------------------------
gen_pass() {
openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 24
}
# -- Input validation ---------------------------------------------------------
validate_yn() {
[[ "$1" =~ ^[yYnN]$ ]] || error "Vuvedete 'y' ili 'n'."
}
validate_count() {
[[ "$1" =~ ^[0-2]$ ]] || error "Vuvedete 0, 1 ili 2."
}
# =============================================================================
echo -e "\n${BOLD}================================================${RESET}"
echo -e "${BOLD} Nastrojka na Hosting s Cloudflare Tunnel ${RESET}"
echo -e "${BOLD}================================================${RESET}\n"
# -- [1/4] Cloudflare Tunnel method -------------------------------------------
echo -e "${BOLD}[1/4] Cloudflare Tunnel - metod na svarzavane${RESET}"
echo " 1) Token - vuvedash gotov token ot Cloudflare Dashboard"
echo " 2) Login - cloudflared se logva sam (URL / brauzar)"
echo ""
read -rp "Izberi metod [1/2]: " cf_method
case "$cf_method" in
1)
read -rp " Vuvedete Tunnel Token: " cf_token
[[ -n "$cf_token" ]] || error "Tokenat ne mozhe da e prazen."
CLOUDFLARED_CMD='tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}'
CF_ENV_LINE=" - TUNNEL_TOKEN=\${TUNNEL_TOKEN}"
CF_TOKEN_IN_ENV="TUNNEL_TOKEN=$cf_token"
;;
2)
info "Pri startиrane cloudflared shte izvede URL za avtentikaciya."
CLOUDFLARED_CMD='tunnel --no-autoupdate login'
CF_ENV_LINE=""
CF_TOKEN_IN_ENV="# TUNNEL_TOKEN= (login method)"
cf_token=""
;;
*)
error "Nevaliден izbor. Vuvedete 1 ili 2."
;;
esac
# -- [2/4] Uptime Kuma --------------------------------------------------------
echo -e "\n${BOLD}[2/4] Uptime Kuma${RESET}"
read -rp "Instaliraj Uptime Kuma? (y/n): " install_kuma
validate_yn "$install_kuma"
install_kuma="${install_kuma,,}"
# -- [3/4] WordPress sites ----------------------------------------------------
echo -e "\n${BOLD}[3/4] WordPress sajtove${RESET}"
read -rp "Broy WordPress sajtove? (0/1/2): " wp_count
validate_count "$wp_count"
# -- [4/4] Generate passwords -------------------------------------------------
echo -e "\n${BOLD}[4/4] Generirane na paroli...${RESET}"
DB_ROOT_PASS=$(gen_pass)
WP1_DB_PASS=$(gen_pass)
WP2_DB_PASS=$(gen_pass)
# =============================================================================
# .env file
# =============================================================================
cat > .env <<ENVEOF
# -- Cloudflare ---------------------------------------------------------------
$CF_TOKEN_IN_ENV
# -- Database -----------------------------------------------------------------
DB_ROOT_PASSWORD=$DB_ROOT_PASS
WP1_DB_PASSWORD=$WP1_DB_PASS
WP2_DB_PASSWORD=$WP2_DB_PASS
ENVEOF
chmod 600 .env
success ".env created (chmod 600)"
# =============================================================================
# DB init SQL
# =============================================================================
mkdir -p db-init
SQL_CONTENT=""
if [[ "$wp_count" -ge 1 ]]; then
SQL_CONTENT+="CREATE DATABASE IF NOT EXISTS wp1_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n"
SQL_CONTENT+="CREATE USER IF NOT EXISTS 'wp1_user'@'%' IDENTIFIED BY '${WP1_DB_PASS}';\n"
SQL_CONTENT+="GRANT ALL PRIVILEGES ON wp1_db.* TO 'wp1_user'@'%';\n"
fi
if [[ "$wp_count" -ge 2 ]]; then
SQL_CONTENT+="CREATE DATABASE IF NOT EXISTS wp2_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n"
SQL_CONTENT+="CREATE USER IF NOT EXISTS 'wp2_user'@'%' IDENTIFIED BY '${WP2_DB_PASS}';\n"
SQL_CONTENT+="GRANT ALL PRIVILEGES ON wp2_db.* TO 'wp2_user'@'%';\n"
fi
SQL_CONTENT+="FLUSH PRIVILEGES;\n"
printf "%b" "$SQL_CONTENT" > db-init/01-init.sql
success "db-init/01-init.sql created"
# =============================================================================
# docker-compose.yml
# =============================================================================
cat > docker-compose.yml <<DCEOF
services:
# -- Cloudflare Tunnel -------------------------------------------------------
cloudflared:
image: cloudflare/cloudflared:latest
restart: always
command: $CLOUDFLARED_CMD
networks:
- internal
DCEOF
# Add environment block only for token method
if [[ -n "$CF_ENV_LINE" ]]; then
cat >> docker-compose.yml <<DCEOF
environment:
$CF_ENV_LINE
DCEOF
fi
# -- MariaDB ------------------------------------------------------------------
cat >> docker-compose.yml <<DCEOF
# -- Database ----------------------------------------------------------------
db:
image: mariadb:10.6
restart: always
environment:
MYSQL_ROOT_PASSWORD: \${DB_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
- ./db-init:/docker-entrypoint-initdb.d:ro
networks:
- internal
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
DCEOF
# -- Uptime Kuma --------------------------------------------------------------
if [[ "$install_kuma" == "y" ]]; then
cat >> docker-compose.yml <<DCEOF
# -- Uptime Kuma -------------------------------------------------------------
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
restart: always
volumes:
- kuma_data:/app/data
networks:
- internal
DCEOF
fi
# -- WordPress 1 --------------------------------------------------------------
if [[ "$wp_count" -ge 1 ]]; then
cat >> docker-compose.yml <<DCEOF
# -- WordPress 1 -------------------------------------------------------------
wp1:
image: wordpress:latest
restart: always
depends_on:
db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wp1_user
WORDPRESS_DB_PASSWORD: \${WP1_DB_PASSWORD}
WORDPRESS_DB_NAME: wp1_db
volumes:
- wp1_data:/var/www/html
networks:
- internal
DCEOF
fi
# -- WordPress 2 --------------------------------------------------------------
if [[ "$wp_count" -ge 2 ]]; then
cat >> docker-compose.yml <<DCEOF
# -- WordPress 2 -------------------------------------------------------------
wp2:
image: wordpress:latest
restart: always
depends_on:
db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wp2_user
WORDPRESS_DB_PASSWORD: \${WP2_DB_PASSWORD}
WORDPRESS_DB_NAME: wp2_db
volumes:
- wp2_data:/var/www/html
networks:
- internal
DCEOF
fi
# -- Networks & Volumes -------------------------------------------------------
cat >> docker-compose.yml <<DCEOF
networks:
internal:
driver: bridge
volumes:
db_data:
DCEOF
[[ "$install_kuma" == "y" ]] && echo " kuma_data:" >> docker-compose.yml
[[ "$wp_count" -ge 1 ]] && echo " wp1_data:" >> docker-compose.yml
[[ "$wp_count" -ge 2 ]] && echo " wp2_data:" >> docker-compose.yml
success "docker-compose.yml created"
# =============================================================================
# Summary
# =============================================================================
echo ""
echo -e "${BOLD}================================================${RESET}"
echo -e "${BOLD} Summary / Rezultat ${RESET}"
echo -e "${BOLD}================================================${RESET}"
echo -e " CF method : ${CYAN}$([ "$cf_method" == "1" ] && echo "Token" || echo "Login")${RESET}"
echo -e " Uptime Kuma : ${CYAN}$([ "$install_kuma" == "y" ] && echo "Yes" || echo "No")${RESET}"
echo -e " WordPress : ${CYAN}$wp_count site(s)${RESET}"
echo ""
echo -e "${BOLD} Files created:${RESET}"
echo " .env (chmod 600)"
echo " docker-compose.yml"
echo " db-init/01-init.sql"
echo ""
echo -e "${BOLD} Next step:${RESET}"
echo -e " ${GREEN}docker compose up -d${RESET}"
echo ""
warn "Keep .env in a safe place - it contains your passwords!"
echo -e "${BOLD}================================================${RESET}\n"
| 1 | #!/bin/bash |
| 2 | # ============================================================================= |
| 3 | # Hosting setup with Cloudflare Tunnel |
| 4 | # Author Fedya Serafiev |
| 5 | # Site itpraktika.com |
| 6 | # ============================================================================= |
| 7 | |
| 8 | set -euo pipefail |
| 9 | |
| 10 | # -- Colors ------------------------------------------------------------------- |
| 11 | RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' |
| 12 | CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' |
| 13 | |
| 14 | info() { echo -e "${CYAN}[INFO] $*${RESET}"; } |
| 15 | success() { echo -e "${GREEN}[OK] $*${RESET}"; } |
| 16 | warn() { echo -e "${YELLOW}[WARN] $*${RESET}"; } |
| 17 | error() { echo -e "${RED}[ERROR] $*${RESET}" >&2; exit 1; } |
| 18 | |
| 19 | # -- Password generator ------------------------------------------------------- |
| 20 | gen_pass() { |
| 21 | openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 24 |
| 22 | } |
| 23 | |
| 24 | # -- Input validation --------------------------------------------------------- |
| 25 | validate_yn() { |
| 26 | [[ "$1" =~ ^[yYnN]$ ]] || error "Vuvedete 'y' ili 'n'." |
| 27 | } |
| 28 | |
| 29 | validate_count() { |
| 30 | [[ "$1" =~ ^[0-2]$ ]] || error "Vuvedete 0, 1 ili 2." |
| 31 | } |
| 32 | |
| 33 | # ============================================================================= |
| 34 | echo -e "\n${BOLD}================================================${RESET}" |
| 35 | echo -e "${BOLD} Nastrojka na Hosting s Cloudflare Tunnel ${RESET}" |
| 36 | echo -e "${BOLD}================================================${RESET}\n" |
| 37 | |
| 38 | # -- [1/4] Cloudflare Tunnel method ------------------------------------------- |
| 39 | echo -e "${BOLD}[1/4] Cloudflare Tunnel - metod na svarzavane${RESET}" |
| 40 | echo " 1) Token - vuvedash gotov token ot Cloudflare Dashboard" |
| 41 | echo " 2) Login - cloudflared se logva sam (URL / brauzar)" |
| 42 | echo "" |
| 43 | read -rp "Izberi metod [1/2]: " cf_method |
| 44 | |
| 45 | case "$cf_method" in |
| 46 | 1) |
| 47 | read -rp " Vuvedete Tunnel Token: " cf_token |
| 48 | [[ -n "$cf_token" ]] || error "Tokenat ne mozhe da e prazen." |
| 49 | CLOUDFLARED_CMD='tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}' |
| 50 | CF_ENV_LINE=" - TUNNEL_TOKEN=\${TUNNEL_TOKEN}" |
| 51 | CF_TOKEN_IN_ENV="TUNNEL_TOKEN=$cf_token" |
| 52 | ;; |
| 53 | 2) |
| 54 | info "Pri startиrane cloudflared shte izvede URL za avtentikaciya." |
| 55 | CLOUDFLARED_CMD='tunnel --no-autoupdate login' |
| 56 | CF_ENV_LINE="" |
| 57 | CF_TOKEN_IN_ENV="# TUNNEL_TOKEN= (login method)" |
| 58 | cf_token="" |
| 59 | ;; |
| 60 | *) |
| 61 | error "Nevaliден izbor. Vuvedete 1 ili 2." |
| 62 | ;; |
| 63 | esac |
| 64 | |
| 65 | # -- [2/4] Uptime Kuma -------------------------------------------------------- |
| 66 | echo -e "\n${BOLD}[2/4] Uptime Kuma${RESET}" |
| 67 | read -rp "Instaliraj Uptime Kuma? (y/n): " install_kuma |
| 68 | validate_yn "$install_kuma" |
| 69 | install_kuma="${install_kuma,,}" |
| 70 | |
| 71 | # -- [3/4] WordPress sites ---------------------------------------------------- |
| 72 | echo -e "\n${BOLD}[3/4] WordPress sajtove${RESET}" |
| 73 | read -rp "Broy WordPress sajtove? (0/1/2): " wp_count |
| 74 | validate_count "$wp_count" |
| 75 | |
| 76 | # -- [4/4] Generate passwords ------------------------------------------------- |
| 77 | echo -e "\n${BOLD}[4/4] Generirane na paroli...${RESET}" |
| 78 | DB_ROOT_PASS=$(gen_pass) |
| 79 | WP1_DB_PASS=$(gen_pass) |
| 80 | WP2_DB_PASS=$(gen_pass) |
| 81 | |
| 82 | # ============================================================================= |
| 83 | # .env file |
| 84 | # ============================================================================= |
| 85 | cat > .env <<ENVEOF |
| 86 | # -- Cloudflare --------------------------------------------------------------- |
| 87 | $CF_TOKEN_IN_ENV |
| 88 | |
| 89 | # -- Database ----------------------------------------------------------------- |
| 90 | DB_ROOT_PASSWORD=$DB_ROOT_PASS |
| 91 | WP1_DB_PASSWORD=$WP1_DB_PASS |
| 92 | WP2_DB_PASSWORD=$WP2_DB_PASS |
| 93 | ENVEOF |
| 94 | |
| 95 | chmod 600 .env |
| 96 | success ".env created (chmod 600)" |
| 97 | |
| 98 | # ============================================================================= |
| 99 | # DB init SQL |
| 100 | # ============================================================================= |
| 101 | mkdir -p db-init |
| 102 | |
| 103 | SQL_CONTENT="" |
| 104 | if [[ "$wp_count" -ge 1 ]]; then |
| 105 | SQL_CONTENT+="CREATE DATABASE IF NOT EXISTS wp1_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n" |
| 106 | SQL_CONTENT+="CREATE USER IF NOT EXISTS 'wp1_user'@'%' IDENTIFIED BY '${WP1_DB_PASS}';\n" |
| 107 | SQL_CONTENT+="GRANT ALL PRIVILEGES ON wp1_db.* TO 'wp1_user'@'%';\n" |
| 108 | fi |
| 109 | if [[ "$wp_count" -ge 2 ]]; then |
| 110 | SQL_CONTENT+="CREATE DATABASE IF NOT EXISTS wp2_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n" |
| 111 | SQL_CONTENT+="CREATE USER IF NOT EXISTS 'wp2_user'@'%' IDENTIFIED BY '${WP2_DB_PASS}';\n" |
| 112 | SQL_CONTENT+="GRANT ALL PRIVILEGES ON wp2_db.* TO 'wp2_user'@'%';\n" |
| 113 | fi |
| 114 | SQL_CONTENT+="FLUSH PRIVILEGES;\n" |
| 115 | |
| 116 | printf "%b" "$SQL_CONTENT" > db-init/01-init.sql |
| 117 | success "db-init/01-init.sql created" |
| 118 | |
| 119 | # ============================================================================= |
| 120 | # docker-compose.yml |
| 121 | # ============================================================================= |
| 122 | cat > docker-compose.yml <<DCEOF |
| 123 | services: |
| 124 | |
| 125 | # -- Cloudflare Tunnel ------------------------------------------------------- |
| 126 | cloudflared: |
| 127 | image: cloudflare/cloudflared:latest |
| 128 | restart: always |
| 129 | command: $CLOUDFLARED_CMD |
| 130 | networks: |
| 131 | - internal |
| 132 | DCEOF |
| 133 | |
| 134 | # Add environment block only for token method |
| 135 | if [[ -n "$CF_ENV_LINE" ]]; then |
| 136 | cat >> docker-compose.yml <<DCEOF |
| 137 | environment: |
| 138 | $CF_ENV_LINE |
| 139 | DCEOF |
| 140 | fi |
| 141 | |
| 142 | # -- MariaDB ------------------------------------------------------------------ |
| 143 | cat >> docker-compose.yml <<DCEOF |
| 144 | |
| 145 | # -- Database ---------------------------------------------------------------- |
| 146 | db: |
| 147 | image: mariadb:10.6 |
| 148 | restart: always |
| 149 | environment: |
| 150 | MYSQL_ROOT_PASSWORD: \${DB_ROOT_PASSWORD} |
| 151 | volumes: |
| 152 | - db_data:/var/lib/mysql |
| 153 | - ./db-init:/docker-entrypoint-initdb.d:ro |
| 154 | networks: |
| 155 | - internal |
| 156 | healthcheck: |
| 157 | test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] |
| 158 | interval: 10s |
| 159 | timeout: 5s |
| 160 | retries: 5 |
| 161 | DCEOF |
| 162 | |
| 163 | # -- Uptime Kuma -------------------------------------------------------------- |
| 164 | if [[ "$install_kuma" == "y" ]]; then |
| 165 | cat >> docker-compose.yml <<DCEOF |
| 166 | |
| 167 | # -- Uptime Kuma ------------------------------------------------------------- |
| 168 | uptime-kuma: |
| 169 | image: louislam/uptime-kuma:latest |
| 170 | container_name: uptime-kuma |
| 171 | restart: always |
| 172 | volumes: |
| 173 | - kuma_data:/app/data |
| 174 | networks: |
| 175 | - internal |
| 176 | DCEOF |
| 177 | fi |
| 178 | |
| 179 | # -- WordPress 1 -------------------------------------------------------------- |
| 180 | if [[ "$wp_count" -ge 1 ]]; then |
| 181 | cat >> docker-compose.yml <<DCEOF |
| 182 | |
| 183 | # -- WordPress 1 ------------------------------------------------------------- |
| 184 | wp1: |
| 185 | image: wordpress:latest |
| 186 | restart: always |
| 187 | depends_on: |
| 188 | db: |
| 189 | condition: service_healthy |
| 190 | environment: |
| 191 | WORDPRESS_DB_HOST: db |
| 192 | WORDPRESS_DB_USER: wp1_user |
| 193 | WORDPRESS_DB_PASSWORD: \${WP1_DB_PASSWORD} |
| 194 | WORDPRESS_DB_NAME: wp1_db |
| 195 | volumes: |
| 196 | - wp1_data:/var/www/html |
| 197 | networks: |
| 198 | - internal |
| 199 | DCEOF |
| 200 | fi |
| 201 | |
| 202 | # -- WordPress 2 -------------------------------------------------------------- |
| 203 | if [[ "$wp_count" -ge 2 ]]; then |
| 204 | cat >> docker-compose.yml <<DCEOF |
| 205 | |
| 206 | # -- WordPress 2 ------------------------------------------------------------- |
| 207 | wp2: |
| 208 | image: wordpress:latest |
| 209 | restart: always |
| 210 | depends_on: |
| 211 | db: |
| 212 | condition: service_healthy |
| 213 | environment: |
| 214 | WORDPRESS_DB_HOST: db |
| 215 | WORDPRESS_DB_USER: wp2_user |
| 216 | WORDPRESS_DB_PASSWORD: \${WP2_DB_PASSWORD} |
| 217 | WORDPRESS_DB_NAME: wp2_db |
| 218 | volumes: |
| 219 | - wp2_data:/var/www/html |
| 220 | networks: |
| 221 | - internal |
| 222 | DCEOF |
| 223 | fi |
| 224 | |
| 225 | # -- Networks & Volumes ------------------------------------------------------- |
| 226 | cat >> docker-compose.yml <<DCEOF |
| 227 | |
| 228 | networks: |
| 229 | internal: |
| 230 | driver: bridge |
| 231 | |
| 232 | volumes: |
| 233 | db_data: |
| 234 | DCEOF |
| 235 | |
| 236 | [[ "$install_kuma" == "y" ]] && echo " kuma_data:" >> docker-compose.yml |
| 237 | [[ "$wp_count" -ge 1 ]] && echo " wp1_data:" >> docker-compose.yml |
| 238 | [[ "$wp_count" -ge 2 ]] && echo " wp2_data:" >> docker-compose.yml |
| 239 | |
| 240 | success "docker-compose.yml created" |
| 241 | |
| 242 | # ============================================================================= |
| 243 | # Summary |
| 244 | # ============================================================================= |
| 245 | echo "" |
| 246 | echo -e "${BOLD}================================================${RESET}" |
| 247 | echo -e "${BOLD} Summary / Rezultat ${RESET}" |
| 248 | echo -e "${BOLD}================================================${RESET}" |
| 249 | echo -e " CF method : ${CYAN}$([ "$cf_method" == "1" ] && echo "Token" || echo "Login")${RESET}" |
| 250 | echo -e " Uptime Kuma : ${CYAN}$([ "$install_kuma" == "y" ] && echo "Yes" || echo "No")${RESET}" |
| 251 | echo -e " WordPress : ${CYAN}$wp_count site(s)${RESET}" |
| 252 | echo "" |
| 253 | echo -e "${BOLD} Files created:${RESET}" |
| 254 | echo " .env (chmod 600)" |
| 255 | echo " docker-compose.yml" |
| 256 | echo " db-init/01-init.sql" |
| 257 | echo "" |
| 258 | echo -e "${BOLD} Next step:${RESET}" |
| 259 | echo -e " ${GREEN}docker compose up -d${RESET}" |
| 260 | echo "" |
| 261 | warn "Keep .env in a safe place - it contains your passwords!" |
| 262 | echo -e "${BOLD}================================================${RESET}\n" |