Skip to content

Instantly share code, notes, and snippets.

@thilak-rao
Last active December 19, 2015 22:32
Show Gist options
  • Select an option

  • Save thilak-rao/a06ca8da444fbc7b4859 to your computer and use it in GitHub Desktop.

Select an option

Save thilak-rao/a06ca8da444fbc7b4859 to your computer and use it in GitHub Desktop.

Basic Server Setup

Install zsh and oh-my-zsh

# Obligatory
sudo apt-get update

sudo apt-get install -y zsh git-core
sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

Change the shell to zsh

sudo chsh -s $(which zsh) username

Configure SSH Daemon

sudo nano /etc/ssh/sshd_config

# Disable Password based auth
PasswordAuthentication no

# Change port number 
Port 50683

Restart SSH Daemon

sudo service ssh restart

Secure MySQL installation

mysql_secure_installation

Configuring a Basic Firewall

# Allow SSH to run on port 50683
sudo ufw allow 50683/tcp 

# Allow HTTP traffic
sudo ufw allow 80/tcp

# Allow SSL Traffic 
sudo ufw allow 443/tcp

After you've finished adding the exceptions, you can review your selections by typing:

sudo ufw show added

If everything looks good, you can enable the firewall by typing:

sudo ufw enable

Set our server's timezone

sudo dpkg-reconfigure tzdata

Configure NTP. This will allow your computer to stay in sync with other servers, leading to more predictability in operations that rely on having the correct time.

sudo apt-get install ntp

Tweaking Nginx

Open Nginx configuration

sudo nano /etc/nginx/nginx.conf

Tweak these settings:

# Optimized for DigitalOcean 512MB, 1CPU instance. 
# Do your own Math for other larger setups. 
worker_processes 1;
worker_connections 1024;

keepalive_timeout 10;

# Refer nginx.conf attached to this gist. 

Create a custom configuration file

sudo nano /etc/nginx/conf.d/settings.conf

# Copy paste these tweaks from settings.conf attached to this gist. 

Restart Nginx

sudo service nginx restart

Setting up SSL with Lets Encrypt

# Temporarily disable firewall to allow letsencrypt to verify domain name. 
sudo ufw disable

# Clone letsencrypt to your hone directory
cd ~/ && git clone https://github.com/letsencrypt/letsencrypt 

# Stop Nginx
sudo service nginx stop

# Obtain certificate from letsencrypt. Remember that your email mentioned on your whois record should match the email address and domain your provide. 
cd ~/letsencrypt && ./letsencrypt-auto --agree-dev-preview --server https://acme-v01.api.letsencrypt.org/directory auth

It should now tell you where the certs are stored. For me, it was /etc/letsencrypt/live/example.com

# Enable firewall back on. 
sudo ufw enable

# certs should be readable by www-data
sudo chown :www-data /etc/letsencrypt/live/example.com/privkey.pem
sudo chown :www-data /etc/letsencrypt/live/example.com/fullchain.pem

# Finally, remove letsencrypt folder
cd ~/ && sudo rm -r letsencrypt/

Configure Nginx Server Blocks

Server blocks are nothing but vhosts in the Apache world.

# Won't use the default. It's easier to work in /var/www directory
sudo mkdir -p /var/www/example.com/html

# Transfer ownership to regular user.
sudo chown -R $USER:$USER /var/www/example.com/html

# The permissions should be correct, but just to be safe. 
sudo chmod -R 755 /var/www

Create an index.html to test server blocks

nano /var/www/example.com/html/index.html

Copy paste a sample html, this is just to test our server block.

<html>
    <head>
        <title>Welcome to Example.com!</title>
    </head>
    <body>
        <h1>Success!  The example.com server block is working!</h1>
    </body>
</html>

Replace the contents of the default server block, with the contents of default attached to this gist.

sudo rm /etc/nginx/sites-available/default
sudo nano /etc/nginx/sites-available/default
# Now copy the contents of default attached to this gist 

Let's make a custom log folder.

sudo mkdir -p /etc/nginx/logs/static
sudo touch /etc/nginx/logs/static.log

Create a brand new server block

sudo nano /etc/nginx/sites-available/example.com

# Just copy serverblock attached to this gist. 
# Don't forget to replace example.com to your own domain. 

Enable the server block

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

Nginx Monitoring

# Install pip
sudo apt-get install python-pip python-dev build-essential
sudo pip install --upgrade pip
sudo pip install --upgrade virtualenv

# Install ngxtop
sudo pip install ngxtop

# For access logs
# sudo ngxtop -l /var/log/nginx/access.log

# For error logs
# sudo ngxtop -l /var/log/nginx/error.log

# For more details: https://github.com/lebinh/ngxtop
# Drop requests for unknown hosts
#
# If no default server is defined, nginx will use the first found server.
# To prevent host header attacks, or other potential problems when an unknown
# servername is used in a request, it's recommended to drop the request
# returning 444 "no response".
server {
listen 80 default_server;
return 444;
}
user www-data;
worker_processes 1;
pid /run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 10;
types_hash_max_size 2048;
server_tokens off;
server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
# Choose between www and non-www, listen on the *wrong* one and redirect to
# the right one -- http://wiki.nginx.org/Pitfalls#Server_Name
#
server {
listen [::]:80;
listen 80;
# listen on both hosts
server_name example.com www.example.com;
# and redirect to the https host (declared below)
# avoiding http://www -> https://www -> https:// chain.
return 301 https://example.com$request_uri;
}
server {
listen [::]:443;
listen 443;
# listen on the wrong host
server_name www.example.com;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1 SSLv3;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions
ssl_session_timeout 24h;
# Use a higher keepalive timeout to reduce the need for repeated handshakes
keepalive_timeout 300; # up from 75 secs default
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 216.146.35.35 216.146.36.36 valid=60s;
resolver_timeout 2s;
# and redirect to the non-www host (declared below)
return 301 https://example.com$request_uri;
}
server {
listen [::]:443 ssl spdy;
listen 443 ssl spdy;
# The host name to respond to
server_name example.com;
index index.php index.html index.htm;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1 SSLv3;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions
ssl_session_timeout 24h;
# Use a higher keepalive timeout to reduce the need for repeated handshakes
keepalive_timeout 300; # up from 75 secs default
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 216.146.35.35 216.146.36.36 valid=60s;
resolver_timeout 2s;
location / {
try_files $uri $uri/ /index.html;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Path for static files
root /var/www/example.com/html;
#Specify a charset
charset utf-8;
# Custom 404 page
error_page 404 /404.html;
# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
# Force the latest IE version
add_header "X-UA-Compatible" "IE=Edge";
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
# cache.appcache, your document html and data
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
expires -1;
access_log /etc/nginx/logs/static.log;
}
# Feed
location ~* \.(?:rss|atom)$ {
expires 1h;
add_header Cache-Control "public";
}
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# CSS and Javascript
location ~* \.(?:css|js)$ {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# Prevent clients from accessing hidden files (starting with a dot)
# This is particularly important if you store .htpasswd files in the site hierarchy
# Access to `/.well-known/` is allowed.
# https://www.mnot.net/blog/2010/04/07/well-known
# https://tools.ietf.org/html/rfc5785
location ~* /\.(?!well-known\/) {
deny all;
}
# Prevent clients from accessing to backup/config/source files
location ~* (?:\.(?:bak|conf|uploads|files|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$ {
deny all;
}
}
###
# (Optimized for DigitalOcean 512MB, 1CPU instance)
###
# This handles the client buffer size, meaning any POST actions sent to Nginx. POST actions are typically form submissions.
client_body_buffer_size 10K;
# Similar to the previous directive, only instead it handles the client header size. For all intents and purposes, 1K is usually a decent size for this directive.
client_header_buffer_size 1k;
# The maximum allowed size for a client request. If the maximum size is exceeded, then Nginx will spit out a 413 error or Request Entity Too Large.
client_max_body_size 8m;
#The maximum number and size of buffers for large client headers.
large_client_header_buffers 2 1k;
# Timeouts can also drastically improve performance.
client_body_timeout 12;
client_header_timeout 12;
send_timeout 10;
# Gzip Settings
gzip_vary on;
gzip_proxied any;
gzip_comp_level 9;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json
application/x-javascript text/xml application/xml
application/xml+rss text/javascript;
upstream php5-fpm-sock {
server unix:/var/run/php5-fpm.sock;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment