broke install into many separate scripts

This commit is contained in:
Joby 2024-10-20 19:57:00 -04:00
parent 9ded453635
commit a5a4b85c49
15 changed files with 600 additions and 317 deletions

View file

@ -2,16 +2,16 @@
This repository contains scripts for setting up a very basic web server and adding new sites. It is not perfect, but it will get you off the ground quickly.
This script will install PHP and nginx, and create a
## Installation
## One-liner
The following command will install these tools.
It is adviseable to review the contents of `quickstart.sh` before running it, as it's generally a good security practice to understand what a script does before executing it, especially with elevated privileges. You may need to install `curl` first.
These commands will clone the tool onto your server and do the basic installation and configuration of core server software. At the moment that is Nginx, PHP, MySQL, and Certbot. A variety of fail2ban and ufw security measures are also added to improve your security posture.
```bash
curl -sSL https://raw.githubusercontent.com/joby-lol/webserver-config/refs/heads/main/quickstart.sh | bash
# clone the repository and cd into it
git clone https://github.com/joby-lol/webserver-config
cd webserver-config
# run installation script
./install.sh
```
## Adding a site

View file

@ -3,13 +3,27 @@
# Script to add a site to the server
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit
exit 1
fi
# Set up logging
LOG_FILE="/var/log/site_setup.log"
exec > >(tee -a "$LOG_FILE") 2>&1
echo "Installation started at $(date)"
echo "Logging to $LOG_FILE"
# Prompt for user input
read -p "Enter the desired username: " username
read -p "Enter the domain name (e.g., example.com): " domain
if [[ ! "$domain" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "Invalid domain name format"
exit 1
fi
read -p "Enter the email address to be used for this account: " email
if [[ ! "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "Invalid email format"
exit 1
fi
read -sp "Enter your Cloudflare API key: " cf_api_key
echo
@ -19,42 +33,28 @@ 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
# Set up directory structure
main_web_root="/var/www/$domain"
sudo mkdir -p $main_web_root/{_main/www,subdomains,logs}
sudo mkdir -p "$main_web_root"/{_main/www,subdomains,logs}
# Create the user with the web root as home directory and add to www-data and websftpusers groups
sudo useradd -m -d /var/www/$domain -s /usr/sbin/nologin -U -G www-data,websftpusers $username
echo "$username:$password" | sudo chpasswd
# Set ownership and permissions for the main site directory
sudo chown -R $username:www-data $main_web_root
sudo find $main_web_root -type d -exec chmod 2750 {} +
sudo find $main_web_root -type f -exec chmod 640 {} +
sudo chown -R "$username:www-data" "$main_web_root"
sudo find "$main_web_root" -type d -exec chmod 2750 {} +
sudo find "$main_web_root" -type f -exec chmod 640 {} +
# Set ownership and permissions for the logs directory
sudo chown root:www-data $main_web_root/logs
sudo chmod 755 $main_web_root/logs
# Ensure new log files get correct permissions
sudo bash -c "echo '
# Set proper permissions for new log files
umask 022
' >> /etc/nginx/nginx.conf"
sudo chown root:www-data "$main_web_root/logs"
sudo chmod 755 "$main_web_root/logs"
# Create MySQL user and grant permissions
sudo mysql <<EOF
CREATE USER '${username}'@'localhost' IDENTIFIED BY '${mysql_password}';
CREATE USER '${username}'@'%' IDENTIFIED BY '${mysql_password}';
CREATE USER IF NOT EXISTS '${username}'@'localhost' IDENTIFIED BY '${mysql_password}';
CREATE USER IF NOT EXISTS '${username}'@'%' IDENTIFIED BY '${mysql_password}';
GRANT ALL PRIVILEGES ON \`${username}_%\`.* TO '${username}'@'localhost';
GRANT ALL PRIVILEGES ON \`${username}_%\`.* TO '${username}'@'%';
GRANT CREATE USER ON *.* TO '${username}'@'localhost' WITH GRANT OPTION;
@ -66,7 +66,7 @@ EOF
# Create site info file
info_file="$main_web_root/site_info.txt"
sudo bash -c "cat > $info_file << EOL
sudo bash -c "cat > \"$info_file\" << EOL
Domain: $domain
Email: $email
@ -78,114 +78,65 @@ MySQL Username: $username
MySQL Password: $mysql_password
MySQL Host: $hostname
EOL"
sudo chown $username:www-data $info_file
sudo chmod 600 $info_file
sudo chown "$username:$username" "$info_file"
sudo chmod 600 "$info_file"
# Create Cloudflare credentials file
cf_credentials="$main_web_root/cloudflare.ini"
sudo bash -c "cat > $cf_credentials << EOL
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
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 \
--dns-cloudflare-credentials "$cf_credentials" \
-d "$domain" -d "*.$domain" \
--non-interactive \
--agree-tos \
--email $email
--email "$email"
# Create Nginx configuration
# Copy Nginx configuration from adjacent file
nginx_config="/etc/nginx/sites-available/$domain"
sudo bash -c "cat > $nginx_config << EOL
server {
listen 80;
listen [::]:80;
server_name .$domain;
return 301 https://\$host\$request_uri;
}
sudo cp "$(realpath "site-config.conf")" "$nginx_config"
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name .$domain;
# replace $DOMAIN placeholder in the nginx config file
sudo sed -i "s/\$DOMAIN/$domain/g" "$nginx_config"
ssl_certificate /etc/letsencrypt/live/$domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$domain/privkey.pem;
# Determine the subdomain and set the root accordingly
set \$subdomain '';
if (\$host ~* ^([^.]+)\.$domain$) {
set \$subdomain \$1;
}
# Default root for subdomains
root $main_web_root/subdomains/\$subdomain/www;
# For the main domain, use the _main/www directory
if (\$host = $domain) {
root $main_web_root/_main/www;
access_log $main_web_root/logs/main_access.log;
error_log $main_web_root/logs/main_error.log;
}
# For subdomains, use separate log files
if (\$subdomain != '') {
access_log $main_web_root/logs/\${subdomain}_access.log;
error_log $main_web_root/logs/\${subdomain}_error.log;
}
index index.html index.htm index.php;
location / {
try_files \$uri \$uri/ @router;
}
location @router {
if (!-f \$document_root/router.php) {
return 404;
}
fastcgi_pass unix:/var/run/php/php-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME \$document_root/router.php;
}
location ~ \.php$ {
try_files \$uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
EOL"
# replace $MAIN_WEB_ROOT placeholder in the nginx config file
sudo sed -i "s#\$MAIN_WEB_ROOT#$main_web_root#g" "$nginx_config"
# Enable the site
sudo ln -s $nginx_config /etc/nginx/sites-enabled/
sudo ln -sf "$nginx_config" /etc/nginx/sites-enabled/
# Test Nginx configuration
sudo nginx -t
# Enable log rotation
sudo tee /etc/logrotate.d/"$domain" > /dev/null <<EOL
$main_web_root/logs/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
dateext
dateformat -%Y-%m-%d
size 100M
postrotate
/usr/sbin/invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}
EOL
# If the test is successful, reload Nginx
if [ $? -eq 0 ]; then
sudo systemctl reload nginx
echo "Nginx configuration has been updated and reloaded."
else
echo "Nginx configuration test failed. Please check the configuration."
fi
# Reload nginx
sudo systemctl reload nginx
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 "Access via SFTP at $hostname with the username $username and the password $password"
echo "Main website files should be placed in: _main/www"
echo "Subdomain files should be placed in: subdomains/[subdomain]/www"
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."
echo "Logs are stored in: logs"

View file

@ -1,31 +0,0 @@
[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

View file

@ -1,136 +0,0 @@
#!/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, 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-mysql php-sqlite3 -y
# UFW Setup
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw --force enable
# Swap Setup
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# Make swap permanent
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# Adjust swappiness
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# Install Certbot and Cloudflare plugin
sudo apt install certbot python3-certbot-dns-cloudflare -y
# 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
#!/bin/bash
systemctl reload nginx
EOL"
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
# 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+'@'<HOST>'
ignoreregex =
EOL"
# Create phpMyAdmin filter for fail2ban
sudo bash -c "cat > /etc/fail2ban/filter.d/phpmyadmin.conf << EOL
[Definition]
failregex = ^<HOST> .* \"(GET|POST) /phpmyadmin/index\.php HTTP/.*\" 403
^<HOST> .* \"(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 "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 "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"

57
install.sh Normal file
View file

@ -0,0 +1,57 @@
#!/bin/bash
# Core Setup Script for Web Server
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit 1
fi
# Set up logging
LOG_FILE="/var/log/server_setup.log"
exec > >(tee -a "$LOG_FILE") 2>&1
echo "Installation started at $(date)"
# run additional scripts from install/
INSTALL_DIR="./install"
# Check if install directory exists
if [ ! -d "$INSTALL_DIR" ]; then
echo "Error: $INSTALL_DIR directory not found"
exit 1
fi
# Make all .sh files in the install directory executable
find "$INSTALL_DIR" -name "*.sh" -type f -exec chmod +x {} \;
# Function to run scripts in a directory
run_scripts() {
for script in "$1"/*.sh; do
if [ -f "$script" ]; then
echo "Running $script..."
if [ -z "$SKIP_SCRIPT" ] || [[ "$SKIP_SCRIPT" != *"$(basename "$script")"* ]]; then
bash "$script"
if [ $? -ne 0 ]; then
echo "Error executing $script"
exit 1
fi
else
echo "Skipping $script"
fi
fi
done
}
# Run all scripts in the install directory
run_scripts "$INSTALL_DIR"
# Start and enable key services
for service in mysql nginx fail2ban; do
echo "Restarting $service..."
sudo systemctl restart $service
sudo systemctl enable $service
done
# Output completion messages
echo "Core setup completed successfully at $(date)"
echo "You can now use add_site.sh to add new users/sites"
echo "For more details, check the log file at $LOG_FILE"

View file

@ -0,0 +1,61 @@
#!/bin/bash
# Check if script is run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit 1
fi
# Update and upgrade packages
apt update
apt upgrade -y
# Install Nginx, UFW, NTP, and fail2ban
apt install nginx ufw ntp fail2ban -y
# Install PHP and necessary modules
apt install php-fpm php-curl php-gd php-mbstring php-xml php-zip php-pdo php-mysql php-sqlite3 -y
# Set timezone
timedatectl set-timezone UTC
# UFW Setup
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 'Nginx Full'
ufw --force enable
# Create custom fail2ban configuration
cat > /etc/fail2ban/jail.d/server.conf << EOL
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 10
bantime = 86400
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600
EOL
# SSH hardening
sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
# Ensure fail2ban jail.local exists
if [ ! -f /etc/fail2ban/jail.local ]; then
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
fi
# Restart fail2ban to apply changes
systemctl restart fail2ban
echo "Server setup and fail2ban configuration completed."

View file

@ -0,0 +1,54 @@
#!/bin/bash
# Check if script is run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit 1
fi
# Set up a default nginx site in /var/www/default
echo "Setting up default Nginx site in /var/www/default..."
# Create the directory for the default site
mkdir -p /var/www/default
# Create a simple HTML file for the default site
cat > /var/www/default/index.html << EOL
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Default Site</title>
</head>
<body>
<h1>Nothing here</h1>
<p>There is nothing here</p>
</body>
</html>
EOL
# Create the Nginx server block configuration for the default site
cat > /etc/nginx/sites-available/default << EOL
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/default;
index index.html;
server_name _;
location / {
try_files \$uri \$uri/ =404;
}
}
EOL
# Enable the default site
ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/
# Reload Nginx to apply changes
systemctl reload nginx
echo "Default Nginx site has been set up and enabled."

18
install/certbot.sh Normal file
View file

@ -0,0 +1,18 @@
#!/bin/bash
# Check if script is run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit 1
fi
# Install Certbot and Cloudflare plugin
sudo apt install certbot python3-certbot-dns-cloudflare -y
# 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
#!/bin/bash
systemctl reload nginx
EOL"
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

60
install/fail2ban-nginx.sh Normal file
View file

@ -0,0 +1,60 @@
#!/bin/bash
# Check if script is run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit 1
fi
echo "Setting up fail2ban for Nginx errors with strict, moderate, and lenient jails..."
# Create the filter files
cat > /etc/fail2ban/filter.d/nginx-4xx-strict.conf << EOL
[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH).*" (401|403) .*$
ignoreregex =
EOL
cat > /etc/fail2ban/filter.d/nginx-4xx-moderate.conf << EOL
[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH).*" (400|405|406|408|413|444) .*$
ignoreregex =
EOL
cat > /etc/fail2ban/filter.d/nginx-4xx-lenient.conf << EOL
[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH).*" (404|429) .*$
ignoreregex =
EOL
# Create a new jail configuration file in jail.d
cat > /etc/fail2ban/jail.d/nginx-4xx-jails.conf << EOL
[nginx-4xx-strict]
enabled = true
port = http,https
filter = nginx-4xx-strict
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 600
bantime = 3600
[nginx-4xx-moderate]
enabled = true
port = http,https
filter = nginx-4xx-moderate
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 600
bantime = 1800
[nginx-4xx-lenient]
enabled = true
port = http,https
filter = nginx-4xx-lenient
logpath = /var/log/nginx/access.log
maxretry = 20
findtime = 600
bantime = 900
EOL
echo "fail2ban setup for Nginx errors completed with strict, moderate, and lenient jails."

69
install/mysql.sh Normal file
View file

@ -0,0 +1,69 @@
#!/bin/bash
# Check if script is run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit 1
fi
# Auto-detect hostname
HOSTNAME=$(hostname -f)
# Install MySQL and Certbot
apt install mysql-server certbot -y
# Generate SSL certificate
certbot certonly --webroot -w /var/www/default -d $HOSTNAME --agree-tos --register-unsafely-without-email --non-interactive
# Configure MySQL for Unix socket authentication
mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH auth_socket;"
mysql -e "DELETE FROM mysql.user WHERE User='';"
mysql -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');"
mysql -e "DROP DATABASE IF EXISTS test;"
mysql -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';"
mysql -e "FLUSH PRIVILEGES;"
# Allow remote MySQL access (users who can access it this way are added later)
sed -i 's/^bind-address\s*=\s*127\.0\.0\.1/bind-address = 0.0.0.0/' /etc/mysql/mysql.conf.d/mysqld.cnf
# Enable MySQL error logging
sed -i '/^\[mysqld\]/a log_error = /var/log/mysql/error.log' /etc/mysql/mysql.conf.d/mysqld.cnf
# Create a separate file for SSL configuration
cat > /etc/mysql/mysql.conf.d/ssl.cnf << EOL
[mysqld]
# SSL Configuration
ssl-ca=/etc/letsencrypt/live/$HOSTNAME/chain.pem
ssl-cert=/etc/letsencrypt/live/$HOSTNAME/cert.pem
ssl-key=/etc/letsencrypt/live/$HOSTNAME/privkey.pem
require_secure_transport=ON
EOL
# Restart MySQL to apply changes
systemctl restart mysql
# Create MySQL fail2ban configuration
cat > /etc/fail2ban/jail.d/mysql.conf << EOL
[mysqld-auth]
enabled = true
filter = mysqld-auth
port = 3306
logpath = /var/log/mysql/error.log
maxretry = 5
bantime = 3600
EOL
# Ensure fail2ban can read the MySQL log
usermod -a -G adm fail2ban
# Create MySQL auth filter for fail2ban
cat > /etc/fail2ban/filter.d/mysqld-auth.conf << EOL
[Definition]
failregex = ^%(__prefix_line)s[0-9]+ \[Warning\] Access denied for user '\w+'@'<HOST>'
ignoreregex =
EOL
# UFW setup
ufw allow 3306/tcp
echo "MySQL installation, SSL configuration, and fail2ban setup completed."

View file

@ -0,0 +1,57 @@
#!/bin/bash
# Exit immediately if a command exits with a non-zero status
set -e
# Check if script is run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit 1
fi
# Name of the new group
NEW_GROUP="websftpusers"
# Create the new group
if ! getent group $NEW_GROUP > /dev/null 2>&1; then
groupadd $NEW_GROUP
echo "Group $NEW_GROUP created successfully."
else
echo "Group $NEW_GROUP already exists."
fi
# Check if sshd_config.d directory exists, create if it doesn't
SSHD_CONFIG_DIR="/etc/ssh/sshd_config.d"
if [ ! -d "$SSHD_CONFIG_DIR" ]; then
mkdir -p "$SSHD_CONFIG_DIR"
echo "Created $SSHD_CONFIG_DIR directory."
# Ensure the main sshd_config includes the .d directory
if ! grep -q "Include /etc/ssh/sshd_config.d/\*.conf" /etc/ssh/sshd_config; then
echo "Include /etc/ssh/sshd_config.d/*.conf" >> /etc/ssh/sshd_config
echo "Added include directive to main sshd_config."
fi
fi
# Create a new configuration file for websftpusers
CONFIG_FILE="$SSHD_CONFIG_DIR/websftpusers.conf"
cat << EOF > "$CONFIG_FILE"
# Configuration for $NEW_GROUP
Match Group $NEW_GROUP
ChrootDirectory %h
ForceCommand internal-sftp
PasswordAuthentication yes
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
EOF
echo "Created $CONFIG_FILE with $NEW_GROUP configuration."
# Restart SSH service to apply changes
systemctl restart sshd
echo "SSH service restarted to apply changes."
echo "Setup complete. New group $NEW_GROUP has been created and SSHD configured for SFTP access."

26
install/swap.sh Normal file
View file

@ -0,0 +1,26 @@
#!/bin/bash
# Check if script is run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit 1
fi
# Exit if swapfile exists
if [ -f /swapfile ]; then
echo "Swapfile already exists. Exiting..."
exit 1
fi
# Swap Setup
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# Make swap permanent
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# Adjust swappiness
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

View file

@ -0,0 +1,38 @@
#!/bin/bash
# Check if script is run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or with sudo"
exit 1
fi
# Exit if unattended-upgrades is already installed
if dpkg -l unattended-upgrades > /dev/null; then
echo "unattended-upgrades is already installed"
exit 0
fi
# Install unattended-upgrades
sudo apt install unattended-upgrades apt-listchanges -y
# Configure unattended-upgrades silently
sudo bash -c "cat > /etc/apt/apt.conf.d/20auto-upgrades << EOL
APT::Periodic::Update-Package-Lists \"1\";
APT::Periodic::Unattended-Upgrade \"1\";
APT::Periodic::AutocleanInterval \"7\";
Unattended-Upgrade::Remove-Unused-Dependencies \"true\";
Unattended-Upgrade::Automatic-Reboot \"true\";
EOL"
# Configure which updates to automatically install
sudo bash -c "cat > /etc/apt/apt.conf.d/50unattended-upgrades << EOL
Unattended-Upgrade::Allowed-Origins {
\"\${distro_id}:\${distro_codename}\";
\"\${distro_id}:\${distro_codename}-security\";
\"\${distro_id}ESMApps:\${distro_codename}-apps-security\";
\"\${distro_id}ESM:\${distro_codename}-infra-security\";
};
Unattended-Upgrade::Package-Blacklist {
// Add packages here that you don't want to be automatically upgraded
};
EOL"

View file

@ -1,26 +0,0 @@
#!/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
# Install curl and git
sudo apt install -y git
# Clone the repository
git clone https://github.com/joby-lol/webserver-config.git
# Change to the repository directory
cd webserver-config
# Execute install-server.sh
sudo ./install-server.sh
echo "Server setup complete. Use 'sudo ./add_site.sh' to add new sites."

85
site-config.conf Normal file
View file

@ -0,0 +1,85 @@
server {
listen 80;
listen [::]:80;
server_name .$DOMAIN;
return 301 https://\$host\$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name .$DOMAIN;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# Determine the subdomain and set the root accordingly
set \$subdomain '';
if (\$host ~* ^([^.]+)\.$DOMAIN$) {
set \$subdomain \$1;
}
# Default root for subdomains
# subdomains are the default, so that we get 404s for nonexistant subdomains
root $MAIN_WEB_ROOT/subdomains/\$subdomain/www;
# For the main domain, use the _main/www directory
if (\$host = $DOMAIN) {
root $MAIN_WEB_ROOT/_main/www;
}
# Index file names
index index.html index.htm index.php;
# Try files first, then use the router.php file if it exists
location / {
try_files \$uri \$uri/ @router;
}
# Use the router.php file for all nonexistant file requests if it exists
location @router {
if (!-f \$document_root/router.php) {
return 404;
}
fastcgi_pass unix:/var/run/php/php-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME \$document_root/router.php;
}
# PHP Configuration
location ~ \.php$ {
try_files \$uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
# Deny access to .ht* files
location ~ /\.ht {
deny all;
}
# Additional config options
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
client_max_body_size 20M;
# Log to both default location and custom site directory, named by subdomain
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
if (\$subdomain != '') {
access_log $MAIN_WEB_ROOT/logs/\$subdomain.access.log;
error_log $MAIN_WEB_ROOT/logs/\$subdomain.error.log;
}else {
access_log $MAIN_WEB_ROOT/logs/_main.access.log;
error_log $MAIN_WEB_ROOT/logs/_main.error.log;
}
}