Skip to content

Instantly share code, notes, and snippets.

@panchr
Last active November 15, 2015 21:10
Show Gist options
  • Save panchr/035fb3c81b5cd4cd579e to your computer and use it in GitHub Desktop.
Save panchr/035fb3c81b5cd4cd579e to your computer and use it in GitHub Desktop.
Server initialization script, oriented towards Python servers.
# Rushy Panchal
# configure.py
# Initializes the server
import subprocess
def main():
'''Main process'''
with open("nginx.conf", "w") as nginx_conf:
nginx_conf.write('''{{ nginx_conf }}''')
with open("init.sh", "w") as init_file:
init_file.write('''{{ init_data }}''')
with open("upstart-app.conf", "w") as upstart_file:
upstart_file.write('''{{ upstart_script }}''')
{% if https %}
with open("bundle.crt", "w") as bundle_file:
bundle_file.write('''{{ ssl_bundle }}''')
with open("private.key", "w") as key_file:
key_file.write('''{{ private_key }}'')
{% endif %}
subprocess.call("chmod a+x init.sh && ./init.sh", shell = True)
if __name__ == '__main__':
main()
# Rushy Panchal
# packageInit.py
# Packages the initialization scripts into one Python file
import sys
import os
sys.path.append(os.path.join(os.path.realpath(".."), "app"))
import config
import jinja2
CONFIG = {
"https": config.PROTOCOL == "https://",
}
CONFIG.update(config.__dict__)
jinja_loader = jinja2.FileSystemLoader(searchpath = ".")
jinja_env = jinja2.Environment(trim_blocks = True, lstrip_blocks = True, loader = jinja_loader)
def main():
'''Main process'''
nginx_conf = formatFile("nginx.conf")
init_data = formatFile("init")
upstart_script = formatFile("upstart-app.conf")
ssl_bundle = formatFile("ssl/bundle.crt")
ssl_key = formatFile("ssl/private.key")
configure_py_template = jinja_env.get_template("package.py")
# with open("package.py", "r") as configure_data:
# configure_py_template = jinja_env.get_template(configure_data)
packageData = {
"nginx_conf": nginx_conf,
"init_data": init_data,
"upstart_script": upstart_script,
"ssl_bundle": ssl_bundle,
"ssl_key": ssl_key,
}
packageData.update(CONFIG)
packagedCode = configure_py_template.render(**packageData)
with open("configure.py", "w") as python_init:
python_init.write(packagedCode)
def formatFile(filePath, dictConfig = None):
'''Formats the file data with the configuration data'''
if not dictConfig:
dictConfig = CONFIG
# with open(filePath, 'r') as fileObject:
# fileTemplate = jinja2.Template(fileObject.read())
fileTemplate = jinja_env.get_template(filePath)
return fileTemplate.render(**dictConfig)
if __name__ == '__main__':
main()
#!/bin/bash
# Rushy Panchal
# Install all dependencies and setup the environment
# Options
CWD="$PWD"
# Add the new user
sudo adduser "{{ SERVER.user }}"
sudo gpasswd -a "{{ SERVER.user }}" sudo
# Update repositories
sudo apt-get update && sudo apt-get upgrade -y
# Necessary packages
sudo apt-get install -y git curl ufw fail2ban vnstat
# Python packages
sudo apt-get install -y build-essential # this is for compiling most C programs and C-based Python packages
sudo apt-get install -y python python-pip python-dev libffi-dev
sudo pip install pip --upgrade
sudo pip install virtualenv Fabric # Fabric is a Python task runner
sudo apt-get -y install fabric
# Install Nginx
sudo apt-get install -y nginx
# Install MongoDB
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
sudo apt-get update
sudo apt-get install -y mongodb-org build-essential openssl libssl-dev pkg-config
sudo mkdir -p /data/db # Database directory for MongoDB
# Create the /var/www/ directory for the application
sudo chown -R "{{ SERVER.user }}":"{{ SERVER.user }}" /var/www
sudo mkdir -p "/var/www/{{ SERVER.service }}"
sudo chown -R "{{ SERVER.user }}":www-data "/var/www/{{ SERVER.service }}"
sudo chmod 755 /var/www
sudo usermod -a -G www-data "{{ SERVER.user }}"
# Create NginX directories and add the configuration
sudo mkdir -p /var/cache/nginx
sudo rm /etc/nginx/sites-available/default
sudo rm /etc/nginx/sites-enabled/default
sudo mv nginx.conf "/etc/nginx/sites-available/{{ DOMAIN }}"
sudo ln -s "/etc/nginx/sites-available/{{ DOMAIN }}" "/etc/nginx/sites-enabled/{{ DOMAIN }}"
{% if https %}
# NginX SSL configuration
sudo mkdir -p "/etc/nginx/ssl/{{ DOMAIN }}"
cd ~/
sudo mv bundle.crt "/etc/nginx/ssl/{{ DOMAIN }}.bundle.crt"
sudo mv private.key "/etc/nginx/ssl/{{ DOMAIN }}.private.key"
{% endif %}
# Create the Git repository and add the post-receive hook
sudo mkdir -p "/var/repo/{{ SERVER.service }}.git"
cd "/var/repo/{{ SERVER.service }}.git"
sudo git init --bare
sudo cat <<EOT >> "hooks/post-receive"
#!/bin/sh
git --work-tree=/var/www/{{ SERVER.service }} --git-dir=/var/repo/{{ SERVER.service }}.git checkout -f
EOT
sudo chmod +x hooks/post-receive
sudo rm hooks/*.sample
cd /var/repo
sudo chown -R {{ SERVER.user }}:{{ SERVER.user }} "/var/repo/{{ SERVER.service }}.git"
cd ~/
# Firewall configuration
sudo ufw allow ssh # make sure to enable SSH access BEFORE enabling the firewall
sudo ufw allow http
{% if https %}
sudo ufw allow https
{% endif %}
sudo ufw allow from 127.0.0.1 to any port "{{ PORT }}" # Application
sudo ufw allow from 127.0.0.1 to any port "{{ DB_PORT }}" # MongoDB
sudo ufw --force enable # force start UFW - no prompt
# Disable logging into root via SSH
sudo sed -i '/^#/!{s/PermitRootLogin .*/PermitRootLogin no/}' /etc/ssh/sshd_config
sudo service ssh restart
# Start services
cd "$CWD"
sudo service nginx start
sudo service mongod start
# Installing Upstart script
cd ~/
sudo mv upstart-app.conf "/etc/init/{{ SERVER.service }}.conf"
sudo mkdir -p "/var/log/{{ SERVER.service }}" # Cryptosana log directory
sudo chown -R "{{ SERVER.user }}":"{{ SERVER.user }}" "/var/log/{{ SERVER.service }}"
sudo touch "/var/log/upstart/{{ SERVER.service }}.log" # create the initial log item for upstart
sudo ln -s "/var/log/upstart/{{ SERVER.service }}.log" "/var/log/{{ SERVER.service }}/service.log" # make link to the Upstart log
sudo ln -s "/var/log/{{ SERVER.service}}" "/var/www/{{ SERVER.service }}/log"
# Installing Memory Swap
sudo fallocate -l "{{ SERVER.swapSize }}" /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo "/swapfile none swap sq 0 0" >> /etc/fstab
# Create environment variable, {{ SERVER.service }} for the app directory
# for both the root and the {{ SERVER.user }}
echo "export APP=/var/www/{{ SERVER.service }}" >> ~/.profile
sudo -H -u {{ SERVER.user }} bash -c 'echo "export APP=/var/www/{{ SERVER.service }}" >> ~/.profile'
# Cleanup
sudo apt-get autoremove -y
sudo apt-get clean
cd ~/
rm init.sh configure.py # delete the configuration programs
# Final messages
echo "You might also want to add a pre-receive hook, /var/repo/{{ SERVER.service }}.git/hooks/pre-receive, to run unit tests before accepting a push."
# Rushy Panchal
# /etc/nginx/sites-available/{{ DOMAIN }}
# Configuration for Nginx
{% if https %}
server {
# redirect to SSL
listen 80;
server_name {{ DOMAIN}};
return 301 https://$server_name$request_uri;
}
{% endif %}
server {
{% if SERVER.cloudflare %}
# CloudFlare forwarded IP
set_real_ip_from 199.27.128.0/21;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
real_ip_header CF-Connecting-IP;
{% endif %}
{% if https %}
listen 443 ssl;
{% else %}
listen 80;
{% endif %}
root /var/www/{{ SERVER.service }};
{% if https %}
# SSL configuration
ssl_certificate /etc/nginx/ssl/{{ DOMAIN }}.bundle.crt;
ssl_certificate_key /etc/nginx/ssl/{{ DOMAIN }}.private.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS +RC4 RC4";
{% endif %}
server_name {{ DOMAIN }} www.{{ DOMAIN }};
autoindex off;
# Gzip configuration, to compress responses
gzip on;
gzip_comp_level 5;
gzip_vary on;
gzip_min_length 1000;
gzip_buffers 16 8k;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; # text/html is already added by default
# Error pages
error_page 403 /static/errors/403.html;
error_page 404 /static/errors/404.html;
error_page 500 /static/errors/500.html;
error_page 502 /static/errors/502.html;
error_page 503 /static/errors/503.html;
error_page 504 /static/errors/504.html;
location / {
# Forward to the application server via uWSGI
include uwsgi_params;
uwsgi_param HTTP_X_Real_IP $remote_addr;
uwsgi_param HTTP_X_Forwarded_For $proxy_add_x_forwarded_for;
uwsgi_param HTTP_X_Forwarded_Proto $scheme;
uwsgi_param HTTP_Host $http_host;
uwsgi_param HTTP_X_NginX_Proxy true;
uwsgi_pass unix:/app/app.sock;
}
location ^~ /static {
# Static files
alias /var/www/{{ SERVER.service }}/static;
access_log off;
etag on;
expires max;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
add_header Access-Control-Allow-Origin *;
}
}
#!/bin/bash
while read oldrev newrev refname
do
# only run this script for the master branch
if [ "$refname" == "refs/heads/master" ]; then
echo "Preparing to run unit tests for $newrev"
# store the revision as a physical file
mkdir -p "git-tmp/$newrev"
git archive $newrev | tar -x -C "git-tmp/$newrev"
echo "Running unit tests for $newrev"
cd "git-tmp/$newrev"
nosetests --where=app --with-coverage --cover-package=app --cover-min-percentage=25 # Python unit tests
TEST_RESULTS=$?
if [ $TEST_RESULTS -ne 0 ]; then
# A non-zero return code means an error occurred, so tell the user and exit
echo "Unit tests failed on rev $newrev - push denied. Run tests locally and confirm they pass before pushing again." && exit 1
fi
fi
done
echo "Unit tests passed! Proceeding with push..." && exit 0 # all unit tests passed, so proceed with the push
#!/bin/bash
# Rushy Panchal
virtualenv venv
source venv/bin/activate
pip install pip --upgrade
pip install -r app/requirements.txt
deactivate
# Rushy Panchal
# Application upstart configuration
description "uWSGI server instance configured for {{ SERVER.service }}"
start on runlevel [2345]
stop on runlevel [!2345]
# Respawn the process if it exits
# However, if it crashes more than 10 times in 5 seconds, do not restart
respawn limit 10 5
setuid {{ SERVER.user }}
setgid www-data
env PATH=/var/www/{{ SERVER.service }}/venv/bin
chdir /var/www/{{ SERVER.service }}/app
exec uwsgi --ini uwsgi.ini
@panchr
Copy link
Author

panchr commented Nov 15, 2015

With a proper configuration, these files can be used to bootstrap a Flask/MongoDB-based web server.

Running createConfigure.py (with proper dependencies and config setup) will output configure.py. This can then be uploaded to your server and run, and it will handle the rest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment