Skip to content

Instantly share code, notes, and snippets.

@philsmy
Last active October 14, 2024 14:49
Show Gist options
  • Save philsmy/1feeef1974fc58d5d547c6d0bcab3c1f to your computer and use it in GitHub Desktop.
Save philsmy/1feeef1974fc58d5d547c6d0bcab3c1f to your computer and use it in GitHub Desktop.
Scripts for configuring an Ubuntu 22 machine (these don't work for 24). Use the first file to create the `deploy` user. Use the other files AS the deploy user to install all you need.
#!/bin/bash
set -euo pipefail
########################
### SCRIPT VARIABLES ###
########################
# Name of the user to create and grant sudo privileges
USERNAME=deploy
# Whether to copy over the root user's `authorized_keys` file to the new sudo
# user.
COPY_AUTHORIZED_KEYS_FROM_ROOT=true
# Additional public keys to add to the new sudo user
# OTHER_PUBLIC_KEYS_TO_ADD=(
# "ssh-rsa AAAAB..."
# "ssh-rsa AAAAB..."
# )
OTHER_PUBLIC_KEYS_TO_ADD=(
)
####################
### SCRIPT LOGIC ###
####################
# Add sudo user and grant privileges
useradd --create-home --shell "/bin/bash" --groups sudo "${USERNAME}"
# Check whether the root account has a real password set
encrypted_root_pw="$(grep root /etc/shadow | cut --delimiter=: --fields=2)"
if [ "${encrypted_root_pw}" != "*" ]; then
# Transfer auto-generated root password to user if present
# and lock the root account to password-based access
echo "${USERNAME}:${encrypted_root_pw}" | chpasswd --encrypted
passwd --lock root
else
# Delete invalid password for user if using keys so that a new password
# can be set without providing a previous value
passwd --delete "${USERNAME}"
fi
# Expire the sudo user's password immediately to force a change
# chage --lastday 0 "${USERNAME}"
# Create SSH directory for sudo user
home_directory="$(eval echo ~${USERNAME})"
mkdir --parents "${home_directory}/.ssh"
# Copy `authorized_keys` file from root if requested
if [ "${COPY_AUTHORIZED_KEYS_FROM_ROOT}" = true ]; then
cp /root/.ssh/authorized_keys "${home_directory}/.ssh"
fi
# Add additional provided public keys
for pub_key in "${OTHER_PUBLIC_KEYS_TO_ADD[@]}"; do
echo "${pub_key}" >> "${home_directory}/.ssh/authorized_keys"
done
# Adjust SSH configuration ownership and permissions
chmod 0700 "${home_directory}/.ssh"
chmod 0600 "${home_directory}/.ssh/authorized_keys"
chown --recursive "${USERNAME}":"${USERNAME}" "${home_directory}/.ssh"
# Disable root SSH login with password
# sed --in-place 's/^PermitRootLogin.*/PermitRootLogin no/g' /etc/ssh/sshd_config
sed --in-place 's/^PasswordAuthentication.*/PasswordAuthentication no/g' /etc/ssh/sshd_config
if sshd -t -q; then
systemctl restart sshd
fi
# Add exception for SSH and then enable UFW firewall
ufw allow OpenSSH
ufw --force enable
# get up to date
apt update && apt -y upgrade && apt-get autoclean && apt-get -y autoremove
#!/bin/bash
set -euo pipefail
# rbenv
sudo apt update
sudo apt install -y git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev dirmngr gnupg apt-transport-https ca-certificates redis-server redis-tools nodejs yarn
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
export PATH="$HOME/.rbenv/bin:$PATH"
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
eval "$(rbenv init -)"
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"
git clone https://github.com/rbenv/rbenv-vars.git ~/.rbenv/plugins/rbenv-vars
# install the ruby version
rbenv install 3.3.1
rbenv global 3.3.1
ruby -v
gem install bundler
#!/bin/bash
set -euo pipefail
# This is optional (obviously) but if you want to install MySQL and ElasticSearch run this script
sudo apt install -y mysql-server
sudo systemctl start mysql.service
# see https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-22-04
# for more instructions
# and elastic search
curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic.gpg] https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
sudo apt update
sudo apt install elasticsearch
# see https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-elasticsearch-on-ubuntu-22-04
# for more instructions
# if we just need localhost (like for staging) all is good
sudo systemctl start elasticsearch
sudo systemctl enable elasticsearch
#!/bin/bash
set -euo pipefail
# Update package list and install prerequisites
sudo apt update
sudo apt install -y nginx dirmngr gnupg apt-transport-https ca-certificates curl
# Add Phusion Passenger APT repository and key
curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/phusion.gpg >/dev/null
echo "deb https://oss-binaries.phusionpassenger.com/apt/passenger jammy main" | sudo tee /etc/apt/sources.list.d/passenger.list
# Update package list again and install Passenger + Nginx module
sudo apt update
sudo apt install -y libnginx-mod-http-passenger
# Enable Passenger Nginx module if not already enabled
if [ ! -f /etc/nginx/modules-enabled/50-mod-http-passenger.conf ]; then
sudo ln -s /usr/share/nginx/modules-available/mod-http-passenger.load /etc/nginx/modules-enabled/50-mod-http-passenger.conf
fi
# Create Passenger configuration file for Nginx
sudo tee /etc/nginx/conf.d/mod-http-passenger.conf > /dev/null <<EOF
passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
passenger_ruby /home/deploy/.rbenv/shims/ruby;
passenger_user_switching on;
passenger_user deploy;
passenger_group deploy;
EOF
# Restart Nginx to apply changes
sudo systemctl restart nginx
# Install Certbot for SSL certificate management
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# Configure UFW to allow 'Nginx Full' and remove 'Nginx HTTP'
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'
# Ensure proper permissions for Passenger
chmod o+x $HOME
# Verify installations
if ! systemctl is-active --quiet nginx; then
echo "Error: Nginx service is not running."
exit 1
else
echo "Nginx installed and running successfully."
fi
if ! command -v certbot >/dev/null 2>&1; then
echo "Error: Certbot installation failed."
exit 1
else
echo "Certbot installed successfully."
fi
echo "Nginx with Passenger installation and configuration completed successfully."
#!/bin/bash
set -euo pipefail
# mysql client. May be unnecessary if you are not using mysql
sudo apt install -y mysql-client libmysqlclient-dev
# we are install node 18 here. You might want higher or lower version
sudo apt --fix-broken install
sudo apt update
sudo apt remove -y nodejs nodejs-doc
sudo apt purge nodejs
sudo apt autoremove -y
cd ~
curl -sL https://deb.nodesource.com/setup_18.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo apt install -y nodejs
node -v
# yarn
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get -y install yarn
# chromium for the pdf generation
sudo apt install -y chromium-browser
sudo apt-get update
sudo apt-get install -y libgbm1
sudo apt install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
# imagemagick for image processing
sudo apt install -y imagemagick
#!/bin/bash
set -euo pipefail
# Set APP_NAME from the command-line argument or the environment variable, with a default value if neither is provided
APP_NAME=${1:-${APP_NAME:-my-app}}
# Ensure APP_NAME is set
if [ -z "$APP_NAME" ]; then
echo "Error: No application name provided. Please provide it as a command-line argument or set the APP_NAME environment variable."
exit 1
fi
# Create necessary directories and files
mkdir -p /home/deploy/$APP_NAME
touch /home/deploy/$APP_NAME/.rbenv-vars
# Update irbrc configuration
echo 'IRB.conf[:USE_AUTOCOMPLETE] = false' >> /home/deploy/.irbrc
# Update bashrc with environment settings and aliases
cat << EOF >> /home/deploy/.bashrc
export RAILS_ENV=production
alias all_logs='sudo tail -f /var/log/nginx/*.log $APP_NAME/shared/log/*.log'
alias nginx_logs='sudo tail -f /var/log/nginx/*.log'
alias app_logs='sudo tail -f /home/deploy/$APP_NAME/shared/log/production.log'
alias "cdcur=cd /home/deploy/$APP_NAME/current"
alias "console=cdcur && bundle exec rails console -- --nomultiline"
EOF
# Example of copying the env file (commented out as in the original script)
# scp -i ~/.ssh/${APP_NAME}_rsa .env.production [email protected]:$APP_NAME/.rbenv-vars
echo "Setup completed for application: $APP_NAME"
#!/bin/bash
set -euo pipefail
# Install prerequisites
sudo apt-get update
sudo apt-get install -y lsb-release curl gpg ufw
# Install Redis
sudo curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list > /dev/null
sudo apt-get update
sudo apt-get install -y redis
# Configure Redis
sudo bash -c 'echo "supervised systemd" >> /etc/redis/redis.conf'
sudo systemctl restart redis.service
# Enable Redis to start on boot
sudo systemctl enable redis.service
# Allow Redis through the firewall
sudo ufw allow 6379
# Verify Redis installation
if ! systemctl is-active --quiet redis; then
echo "Error: Redis service is not running."
exit 1
else
echo "Redis installed and running successfully."
fi
# Install Memcached
sudo apt-get install -y memcached libmemcached-tools
# Enable Memcached to start on boot
sudo systemctl enable memcached.service
# Start Memcached service
sudo systemctl start memcached.service
# Verify Memcached installation
if ! systemctl is-active --quiet memcached; then
echo "Error: Memcached service is not running."
exit 1
else
echo "Memcached installed and running successfully."
fi
echo "Redis and Memcached installation and configuration completed successfully."
# goes in /etc/nginx/sites-available/my-app
# but really it's better to use ssl
server {
server_name _;
listen 80;
root /home/deploy/my-app/current/public;
passenger_enabled on;
passenger_app_env production;
passenger_preload_bundler on;
access_log /var/log/nginx/app.access.log;
error_log /var/log/nginx/app.error.log;
location /cable {
passenger_app_group_name my-app_websocket;
passenger_force_max_concurrent_requests_per_process 0;
}
# Allow uploads up to 100MB in size
client_body_buffer_size 10K;
client_header_buffer_size 1k;
client_max_body_size 100m;
large_client_header_buffers 4 4k;
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
location ~ ^/(assets|packs) {
expires max;
gzip_static on;
}
location ~ /apple-icon.png {
return 204;
log_not_found off;
}
if (-f $document_root/system/maintenance.html) {
return 503;
}
error_page 503 @maintenance;
location @maintenance {
rewrite ^(.*)$ /system/maintenance.html break;
}
location ~* ^/(?:favicon|apple-touch-icon|android-chrome-|mstile-|safari-pinned-tab.svg|browserconfig.xml|mainfest.json) {
root /home/deploy/my-app/current/public;
}
location = /browserconfig.xml {
root /home/deploy/my-app/current/public;
}
location = /manifest.json {
root /home/deploy/my-app/current/public;
}
location = /ads.txt {
root /home/deploy/my-app/current/public;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment