#!/bin/sh # Copyright 2022 Felix Freeman # # This file is part of "LeanWeb" and licensed under the terms of the GNU Affero # General Public License version 3 with permissions for compatibility with the # Hacktivista General Public License. You should have received a copy of this # license along with the software. If not, see . # An automated setup for LeanWeb services on a Debian 12 (or 11) machine. # Test basic requirements if [ "$(id -u)" -ne 0 ]; then echo 'Only can be run as root.' exit 1 elif ! expr "$(cat /etc/issue.net 2>/dev/null)" : "Debian GNU/Linux 1[12]" \ >/dev/null 2>&1 then echo 'This script cowardly refuses to try this outside of a modern Debian.' echo "\"I'm sorry\", it says, watery-eyed." exit 2 fi # User-set variables echo 'System username:' read -r PROJECT_USER id "$PROJECT_USER" >/dev/null 2>&1 || \ (echo "$PROJECT_USER is not a user" && exit 3) echo "Project identifier (default \"$PROJECT_USER\")" read -r IDENTIFIER IDENTIFIER="${IDENTIFIER:-$PROJECT_USER}" echo "Path to project (default \"/opt/$IDENTIFIER\"):" read -r PROJECT_PATH PROJECT_PATH="${PROJECT_PATH:-/opt/$IDENTIFIER}" test -d "$PROJECT_PATH" || \ (echo "$PROJECT_PATH does not exist" && exit 4) chmod o+x "$PROJECT_PATH" # so Nginx can read public/ echo 'Environment (default "development"):' read -r RACK_ENV export RACK_ENV="${RACK_ENV:-development}" echo 'Rack port (default "9292"):' read -r RACK_PORT RACK_PORT="${RACK_PORT:-9292}" echo 'Domain:' read -r DOMAIN # Setup secondary domain (www. or without www.) in production environments if [ "$RACK_ENV" != 'development' ]; then case $DOMAIN in www.*) SECONDARY_DOMAIN="${DOMAIN#www.}" ;; *.*.*) ;; # subdomain, no secondary domain *) SECONDARY_DOMAIN="www.$DOMAIN" ;; esac fi echo 'IP address to which the domain is bound:' read -r IP echo 'Space-separated notification emails:' read -r NOTIFICATION_EMAILS echo 'Setup Hawese? (y/n*)' read -r CHOICE if [ "$CHOICE" = 'y' ]; then echo 'Hawese endpoint (default "https://api.hackware.cl"):' read -r HAWESE_ENDPOINT HAWESE_ENDPOINT="${HAWESE_ENDPOINT:-https://api.hackware.cl}" echo 'Hawese origin:' read -r HAWESE_ORIGIN echo 'Hawese auth token (optional, for /payments/):' read -r HAWESE_AUTH_TOKEN fi echo 'Setup SMTP? (y/n*)' read -r CHOICE if [ "$CHOICE" = 'y' ]; then echo 'From (in format "Name " or "user@mail"):' read -r SMTP_FROM echo 'Host:' read -r SMTP_HOST echo 'Port (default "25"):' read -r SMTP_PORT echo 'User (optional):' read -r SMTP_USER echo 'Password (optional):' read -r SMTP_PASSWORD echo 'Security (tls/starttls/none, default "none"):' read -r SMTP_SECURITY fi if [ ! -f "$PROJECT_PATH/Gemfile" ]; then echo 'Git repository URL:' read -r GIT_REPO fi # Install required packages apt update && apt install -y nginx ruby-full ruby-bundler gcc libc6-dev make # Execute on $PROJECT_PATH directory alias sudou="sudo -D "$PROJECT_PATH" -u \$PROJECT_USER" cd "$PROJECT_PATH" # Clone repo if needed if [ -n "$GIT_REPO" ]; then apt install git sudou git clone "$GIT_REPO" "$PROJECT_PATH" fi # Env vars file cat << EOF | sudou tee "$PROJECT_PATH/.env" >/dev/null RACK_ENV=$RACK_ENV LEANWEB_ENDPOINT=https://$DOMAIN NOTIFICATION_EMAILS='$NOTIFICATION_EMAILS' EOF if [ -n "$HAWESE_ENDPOINT" ] && [ -n "$HAWESE_ORIGIN" ]; then echo "HAWESE_ENDPOINT=$HAWESE_ENDPOINT" >> "$PROJECT_PATH/.env" echo "HAWESE_ORIGIN=$HAWESE_ORIGIN" >> "$PROJECT_PATH/.env" if [ -n "$HAWESE_AUTH_TOKEN" ]; then echo "HAWESE_AUTH_TOKEN=$HAWESE_AUTH_TOKEN" >> "$PROJECT_PATH/.env" fi fi echo "SMTP_FROM='$SMTP_FROM'" >> "$PROJECT_PATH/.env" echo "SMTP_HOST=$SMTP_HOST" >> "$PROJECT_PATH/.env" if [ -n "$SMTP_PORT" ]; then echo "SMTP_PORT=$SMTP_PORT" >> "$PROJECT_PATH/.env" fi if [ -n "$SMTP_USER" ]; then echo "SMTP_USER=$SMTP_USER" >> "$PROJECT_PATH/.env" fi if [ -n "$SMTP_PASSWORD" ]; then echo "SMTP_PASSWORD='$SMTP_PASSWORD'" >> "$PROJECT_PATH/.env" fi if [ -n "$SMTP_SECURITY" ]; then echo "SMTP_SECURITY=$SMTP_SECURITY" >> "$PROJECT_PATH/.env" fi # Load .env files when profile is loaded, system-wide if [ ! -f /etc/profile.d/env.sh ]; then cat <<- SH > /etc/profile.d/env.sh if [ -f "\$HOME/.env" ]; then set -a . "\$HOME/.env" set +a fi SH fi # Bundle sudou bundle config set --local path vendor/bundle if [ "$RACK_ENV" != 'development' ]; then sudou bundle config set --local deployment true sudou bundle config set without development else sudou bundle config unset without fi sudou bundle install # SystemD service cat << EOF > "/etc/systemd/system/$IDENTIFIER.service" [Unit] Description=$IDENTIFIER After=syslog.target network.target [Service] Type=simple User=$PROJECT_USER Group=$PROJECT_USER WorkingDirectory=$PROJECT_PATH EnvironmentFile=$PROJECT_PATH/.env ExecStart=/usr/bin/bundle exec rackup -o localhost -p $RACK_PORT SyslogIdentifier=$IDENTIFIER [Install] WantedBy=default.target EOF systemctl enable --now "$IDENTIFIER" # SSL certificate if [ "$RACK_ENV" = 'development' ]; then openssl req -x509 -nodes -newkey rsa:4096 \ -keyout "/etc/ssl/private/$DOMAIN.key" \ -out "/etc/ssl/certs/$DOMAIN.crt" \ -sha256 -days 3650 \ -subj "/CN=$DOMAIN" else apt install -y python3-certbot-nginx if [ -n "$SECONDARY_DOMAIN" ]; then certbot certonly --nginx --agree-tos \ --email "${NOTIFICATION_EMAILS% *}" \ --no-eff-email -d "$DOMAIN" -d "$SECONDARY_DOMAIN" else certbot certonly --nginx --agree-tos \ --email "${NOTIFICATION_EMAILS% *}" \ --no-eff-email -d "$DOMAIN" fi fi # Nginx HTTP server if [ -n "$SECONDARY_DOMAIN" ]; then cat <<- EOF > "/etc/nginx/conf.d/$DOMAIN.conf" server { listen $IP:80; server_name $DOMAIN $SECONDARY_DOMAIN; location / { return 301 https://$DOMAIN\$request_uri; } } server { listen $IP:443 ssl; server_name $SECONDARY_DOMAIN; # Certbot certificates ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; location / { return 301 https://$DOMAIN\$request_uri; } } EOF else cat <<- EOF > "/etc/nginx/conf.d/$DOMAIN.conf" server { listen $IP:80; server_name $DOMAIN; location / { return 301 https://\$host\$request_uri; } } EOF fi cat << EOF >> "/etc/nginx/conf.d/$DOMAIN.conf" server { listen $IP:443 ssl http2; server_name $DOMAIN; access_log /var/log/nginx/$DOMAIN.access.log; error_log /var/log/nginx/$DOMAIN.error.log; EOF if [ "$RACK_ENV" = 'development' ]; then cat <<- EOF >> "/etc/nginx/conf.d/$DOMAIN.conf" ssl_certificate /etc/ssl/certs/$DOMAIN.crt; ssl_certificate_key /etc/ssl/private/$DOMAIN.key; EOF else cat <<- EOF >> "/etc/nginx/conf.d/$DOMAIN.conf" # Certbot certificates ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; EOF fi cat << EOF >> "/etc/nginx/conf.d/$DOMAIN.conf" root $PROJECT_PATH/public; location ~* ^.+\.(jpg|png|svg|webp|woff2|LICENSE)\$ { expires max; try_files \$uri =404; } location / { proxy_set_header Host \$http_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_intercept_errors on; recursive_error_pages on; proxy_pass http://127.0.0.1:9292/; error_page 404 = @static; } location @static { expires -1; try_files \$uri \$uri.html \$uri/ =404; } } EOF systemctl reload nginx # Finishing cat << EOF All done! consider running sudo -D $PROJECT_PATH -u $PROJECT_USER -- bundle exec rake build_static chown -R $PROJECT_USER:www-data $PROJECT_PATH/public EOF