diff --git a/README.md b/README.md index f143561..d5fbca8 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,10 @@ It is adviseable to review the contents of `quickstart.sh` before running it, as ```bash curl -sSL https://raw.githubusercontent.com/joby-lol/webserver-config/refs/heads/main/quickstart.sh | bash ``` +## Adding a site + +To set up a site, run `add-site.sh` and it will prompt you for all the data necessary. What you will need before running it is: + +* A domain name using Cloudflare's DNS, with both @ and * subdomains pointing at your server + * If you want to use their proxy, you need to turn on the SSL/TLS encryption mode of Full, Full(Strict), or Strict(SSL-Only Origin Pull). Flexible or Off will not work. +* A Cloudflare API key capable of editing the DNS for that domain diff --git a/add-site.sh b/add-site.sh index 5113ef9..9955e02 100755 --- a/add-site.sh +++ b/add-site.sh @@ -1,13 +1,33 @@ #!/bin/bash +# Script to add a site to the server +if [ "$EUID" -ne 0 ]; then + echo "Please run as root or with sudo" + exit +fi + # Prompt for user input read -p "Enter the desired username: " username -read -sp "Enter the password for $username: " password -echo read -p "Enter the domain name (e.g., example.com): " domain +read -p "Enter the email address to be used for this account: " email read -sp "Enter your Cloudflare API key: " cf_api_key echo +# Generated passwords +password=$(openssl rand -base64 12) +mysql_password=$(openssl rand -base64 12) + +# Get hostname or IP +hostname=$(hostname -f) +if [ $? -ne 0 ] || [ -z "$hostname" ] || [[ "$hostname" != *"."* ]]; then + echo "Failed to get a valid hostname. Falling back to IP address." + hostname=$(curl -s -4 icanhazip.com || curl -s -4 ifconfig.me || ip addr show | grep 'inet ' | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 | head -n 1) + if [ -z "$hostname" ]; then + echo "Failed to determine IP address. Using 'localhost' as fallback." + hostname="localhost" + fi +fi + # Create the user and add to www-data group sudo useradd -m -s /bin/bash -G www-data $username echo "$username:$password" | sudo chpasswd @@ -31,19 +51,53 @@ sudo bash -c "echo ' umask 022 ' >> /etc/nginx/nginx.conf" +# Create MySQL user and grant permissions +sudo mysql < $info_file << EOL +Domain: $domain +Email: $email + +SFTP Host: $hostname +Username: $username +Password: $password + +MySQL Username: $username +MySQL Password: $mysql_password +MySQL Host: $hostname +EOL" +sudo chown $username:www-data $info_file +sudo chmod 600 $info_file + # Create Cloudflare credentials file -cf_credentials="/root/.cloudflare/$domain.ini" -sudo mkdir -p /root/.cloudflare +cf_credentials="$main_web_root/cloudflare.ini" sudo bash -c "cat > $cf_credentials << EOL dns_cloudflare_api_token = $cf_api_key EOL" +# note that cloudflare credentials are only even readable by root, to make it +# harder for attackers to get them, since they would allow lateral movement +sudo chown root:root $cf_credentials sudo chmod 600 $cf_credentials # Request wildcard certificate using Cloudflare DNS challenge sudo certbot certonly --dns-cloudflare \ --dns-cloudflare-credentials $cf_credentials \ -d $domain -d *.$domain \ - --non-interactive + --non-interactive \ + --agree-tos \ + --email $email # Create Nginx configuration nginx_config="/etc/nginx/sites-available/$domain" @@ -132,5 +186,6 @@ echo "Setup complete for $domain" echo "Main website files should be placed in: $main_web_root/_main/www" echo "Subdomain files should be placed in: $main_web_root/subdomains/[subdomain]/www" echo "Logs will be stored in: $main_web_root/logs" +echo "Site information (including MySQL credentials) is stored in: $info_file" echo "Cloudflare credentials for this domain are stored in: $cf_credentials" echo "Remember to log out and log back in for group changes to take effect." diff --git a/fail2ban.conf b/fail2ban.conf new file mode 100644 index 0000000..53c8d26 --- /dev/null +++ b/fail2ban.conf @@ -0,0 +1,31 @@ +[sshd] +enabled = true +port = ssh +filter = sshd +logpath = /var/log/auth.log +maxretry = 5 +bantime = 3600 + +[nginx-http-auth] +enabled = true +filter = nginx-http-auth +port = http,https +logpath = /var/log/nginx/error.log +maxretry = 5 +bantime = 3600 + +[mysqld-auth] +enabled = true +filter = mysqld-auth +port = 3306 +logpath = /var/log/mysql/error.log +maxretry = 5 +bantime = 3600 + +[phpmyadmin] +enabled = true +port = http,https +filter = phpmyadmin +logpath = /var/log/nginx/access.log +maxretry = 5 +bantime = 3600 diff --git a/install-server.sh b/install-server.sh index 4e0f93e..a8fe1ea 100755 --- a/install-server.sh +++ b/install-server.sh @@ -1,24 +1,20 @@ #!/bin/bash # Core Setup Script for Web Server +if [ "$EUID" -ne 0 ]; then + echo "Please run as root or with sudo" + exit +fi # Update and upgrade packages sudo apt update sudo apt upgrade -y -# Install Nginx and UFW -sudo apt install nginx ufw -y +# Install Nginx, UFW, MySQL, and fail2ban +sudo apt install nginx ufw mysql-server fail2ban -y # Install PHP and necessary modules -sudo apt install php-fpm php-curl php-gd php-mbstring php-xml php-zip php-pdo php-sqlite3 -y - -# Start and enable PHP-FPM service -sudo systemctl start php-fpm -sudo systemctl enable php-fpm - -# Ensure Nginx is started and enabled -sudo systemctl start nginx -sudo systemctl enable nginx +sudo apt install php-fpm php-curl php-gd php-mbstring php-xml php-zip php-pdo php-mysql php-sqlite3 -y # UFW Setup sudo ufw default deny incoming @@ -43,12 +39,6 @@ sudo sysctl -p # Install Certbot and Cloudflare plugin sudo apt install certbot python3-certbot-dns-cloudflare -y -# Prompt user for email address -read -p "Enter your email address for Certbot registration: " email_address - -# Register with Certbot -sudo certbot register --email "$email_address" --agree-tos --no-eff-email - # Create post-renewal hook for Nginx reload sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy sudo bash -c "cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << EOL @@ -57,13 +47,90 @@ systemctl reload nginx EOL" sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh -# Verify auto-renewal configuration -echo "Verifying Certbot auto-renewal configuration..." -sudo certbot renew --dry-run +# Configure MySQL for Unix socket authentication +sudo mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH auth_socket;" +sudo mysql -e "DELETE FROM mysql.user WHERE User='';" +sudo mysql -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');" +sudo mysql -e "DROP DATABASE IF EXISTS test;" +sudo mysql -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';" +sudo mysql -e "FLUSH PRIVILEGES;" +# Enable MySQL error logging +sudo sed -i '/^\[mysqld\]/a log_error = /var/log/mysql/error.log' /etc/mysql/mysql.conf.d/mysqld.cnf +sudo systemctl restart mysql + +# Configure fail2ban +sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local + +# Copy custom fail2ban configuration +sudo cp fail2ban.conf /etc/fail2ban/jail.d/custom.conf + +# Ensure fail2ban can read the MySQL log +sudo usermod -a -G adm fail2ban + +# Create MySQL auth filter for fail2ban +sudo bash -c "cat > /etc/fail2ban/filter.d/mysqld-auth.conf << EOL +[Definition] +failregex = ^%(__prefix_line)s[0-9]+ \[Warning\] Access denied for user '\w+'@'' +ignoreregex = +EOL" + +# Create phpMyAdmin filter for fail2ban +sudo bash -c "cat > /etc/fail2ban/filter.d/phpmyadmin.conf << EOL +[Definition] +failregex = ^ .* \"(GET|POST) /phpmyadmin/index\.php HTTP/.*\" 403 + ^ .* \"(GET|POST) /phpmyadmin/.* HTTP/.*\" 404 +ignoreregex = +EOL" + +# Install phpMyAdmin +sudo apt install phpmyadmin -y + +# Configure phpMyAdmin with Nginx +sudo bash -c "cat > /etc/nginx/conf.d/phpmyadmin.conf << EOL +location /phpmyadmin { + root /usr/share/; + index index.php index.html index.htm; + location ~ ^/phpmyadmin/(.+\.php)$ { + try_files \$uri =404; + root /usr/share/; + fastcgi_pass unix:/var/run/php/php-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; + include /etc/nginx/fastcgi_params; + access_log /var/log/nginx/phpmyadmin_access.log; + } + location ~* ^/phpmyadmin/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ { + root /usr/share/; + } +} +EOL" + +# Start and enable MySQL +sudo systemctl start mysql +sudo systemctl enable mysql + +# Start and enable PHP-FPM service +sudo systemctl start php-fpm +sudo systemctl enable php-fpm + +# Start and enable Nginx +sudo systemctl start nginx +sudo systemctl enable nginx + +# Start and enable fail2ban +sudo systemctl start fail2ban +sudo systemctl enable fail2ban + +# Output completion messages echo "Core setup completed successfully!" -echo "Certbot has been registered with the provided email address" +echo "Nginx, PHP, MySQL, fail2ban, and phpMyAdmin have been installed and configured" +echo "Certbot and its Cloudflare plugin have been installed" echo "A post-renewal hook has been added to reload Nginx after certificate renewal" -echo "Auto-renewal has been verified. If you saw no errors, it's correctly set up." echo "UFW has been configured and enabled" echo "A 2GB swap file has been set up and configured" +echo "MySQL has been configured for Unix socket authentication (no password, command-line only for root)" +echo "fail2ban has been configured to protect SSH, Nginx HTTP authentication, and MySQL" +echo "phpMyAdmin has been installed and configured with Nginx" +echo "You can access phpMyAdmin at http://your_server_ip/phpmyadmin" +echo "You can now use 'sudo bash add_site.sh' to add new sites and configure SSL for each" diff --git a/quickstart.sh b/quickstart.sh index 8113340..1a286db 100755 --- a/quickstart.sh +++ b/quickstart.sh @@ -1,5 +1,13 @@ #!/bin/bash +# Script to quickly set up a new server +# Download and install by running: +# curl -sSL https://raw.githubusercontent.com/joby-lol/webserver-config/refs/heads/main/quickstart.sh | bash +if [ "$EUID" -ne 0 ]; then + echo "Please run as root or with sudo" + exit +fi + # Update package list sudo apt update