No description, website, or topics provided.
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

Linux Server Configuration Project

This is my submission for Udacity's Full Stack Web Developer Nanodegree's Linux Server Configuration Project. The task was to take a baseline installation of a Linux server and prepare it to host a web application. I secured the server from a number of attack vectors, installed and configured a database server, installed and configured a web server, and deployed an existing web application onto it.

Server Details

This server is no longer online because I stopped paying for the instance after I completed the course.

IP Address:

SSH Port: 2200


NIP.IO is a wildcard DNS that allows you to map [anything].[IP Address] to the corresponding [IP Address]

Linux Distribution: Ubuntu

Server Host: Amazon Lightsail (Amazon Web Services)

Secure the Server

Update all currently installed packages

Update the package source list

sudo apt-get update

Update the software

sudo apt-get upgrade

Change SSH Port

Open configuration file of the SSH daemon

sudo nano /etc/ssh/sshd_config

Locate the line

# Port 22

Replace that line

Port 2200

In order for the changes to take affect, you need to restart the SSH daemon

sudo service sshd restart

You will temporarily be locked out of your server until you take the following step

Configure Lightsail (AWS) Firewall

Go the the dashboard for your Lightsail linux instance

Click on the Networking tab

Click the Add another button under the firewall table and enable 2 more ports:

Open the new SSH port

Custom | TCP | 2200

Open the default port for NTP

Custom | TCP | 123

Click Edit rules and delete the rule for the default SSH port 22

Now, you can connect to the Linux server via SSH from your own terminal at the new port (2200)

Download the private key from the Lightsail (AWS) dashboard

Place the file into the /users/[user]/.ssh directory

Make sure to tighten the permissions on this file

chmod 400 [private key]

Login to the server

ssh ubuntu@[public ip] -p 2200 -i ~/.ssh/[private key]

Configure Uncomplicated Firewall

By default, block all incoming requests to all ports

sudo ufw default deny incoming

By default, allow all outgoing requests from all ports

sudo ufw default allow outgoing

Open port 2200 for SSH

sudo ufw allow 2200/tcp

Open port 80 for HTTP

sudo ufw allow www

Open port 123 for NTP

sudo ufw allow ntp

Enable the firewall

sudo ufw enable

Disable Root Login

Open configuration file of the SSH daemon

sudo nano /etc/ssh/sshd_config

Locate the line

PermitRootLogin without-password

Replace that line

PermitRootLogin no

In order for the changes to take affect, you need to restart the SSH daemon

sudo service sshd restart

Creating New User

Create and Give Sudo Access

Create the user

sudo adduser grader

Give sudo access

sudo usermod -aG sudo grader

Set Up SSH for New User

Return to local machine and generate the key-pair


This command will prompt you for a path for the keypair


Read out the contents of the public key

cat .ssh/[public key]

Copy the result of this command to your clipboard

Log back into the server as the grader user

Create a directory called .ssh within that user's home directory

mkdir .ssh

Create a new file within that directory that will contain all the public keys that this user is allowed to user for authentication

touch .ssh/authorized_keys

Open this file and paste in the public key

sudo nano .ssh/authorized_keys

Now, we need to tighten the permissions on the authorized_keys file and the .ssh directory

chmod 700 .ssh

chmod 644 .ssh/authorized_keys

Forcing Key-Based Authentication

Now that we have set up the key-pair for the grader user, we need to force key-based authentication for security purposes

Open configuration file of the SSH daemon

sudo nano /etc/ssh/sshd_config

Locate the line

# PasswordAuthentication no

Uncomment the file so it reads

PasswordAuthentication no

In order for the changes to take affect, you need to restart the SSH daemon

sudo service sshd restart

Install and Configure PostgreSQL

Use the apt-get package installer to download PostgreSQL

sudo apt-get install postgresql postgresql-contrib

Now, we need to login into the database server

sudo -u postgres psql

Let's create a database user named catalog

CREATE USER catalog WITH PASSWORD 'password';

Let's give the user permission to create databases


Create a database named catalog


Connect to the catalog database

\connect catalog

Revoke all rights


Grant rights to catalog user

GRANT ALL ON SCHEMA public TO catalog;

Exit the database


Install and Configure Apache to Run Python mod_wsgi App

Install the Apache2 web server

sudo apt-get install apache2

Install the application handler - mod_wsgi

sudo apt-get install libapache2-mod-wsgi-py3

We will create a directory in our home directory to work in and link to it from the site-root defined by Apache's configuration

mkdir ~/flaskapp

sudo ln -sT ~/flaskapp /var/www/html/flaskapp

Now, we bring in all the code from my github repo into this directory

Now, rename the file as


Fill PostgreSQL Database

Open the python3 shell


Import the database model from

from flaskapp import db

Create the database model in the PostgreSQL database


Open the file in the flaskapp directory

sudo nano

Replace from app import Category, Item, User with from flaskapp import Category, Item, User

Replace engine = create_engine('[db_name]') with engine = create_engine('postgresql://catalog:password@localhost/catalog')

Then, we run this file to fill in the database


Configure Apache and mod_wsgi

Create a .wsgi file to load the app

sudo nano flaskapp.wsgi

Paste in the following code

  activate_this = '/home/ubuntu/environment/bin/'
  with open(activate_this) as file_:
    exec(, dict(__file__=activate_this))
  import sys
  sys.path.insert(0, '/var/www/html/flaskapp')
  from flaskapp import app as application

Now, we need to adjust the Apache2 server configuration

sudo nano /etc/apache2/sites-enabled/000-default.conf

Paste in the following code just after the DocumentRoot /var/www/html line

WSGIDaemonProcess flaskapp threads=5
WSGIScriptAlias / /var/www/html/flaskapp/flaskapp.wsgi
<Directory flaskapp>
    WSGIProcessGroup flaskapp
    WSGIApplicationGroup %{GLOBAL}
    Order deny,allow
    Allow from all

Using a Virtual Environment to Install Packages

Create a virtual environment in pure python3

python3 -m venv environment

Now, we activate the virtual environment

source environment/bin/activate

Finally, we install the necessary packages

pip3 install flask
pip3 install sqlalchemy
pip3 install oauth2client
pip3 install flask_sqlalchemy
pip3 install flask_recaptcha

We have a slight problem because in order for mod_wsgi to activate the virtual environment and recognize these packages is for it run a python file called However, when we create a virtual environment with native python, this file is not created. All we need to do is create a file called and paste in the necessary code

sudo nano environment/bin/

Paste in this code

"""By using execfile(this_file, dict(__file__=this_file)) you will
activate this virtualenv environment.

This can be used when you must use an existing Python interpreter, not the virtualenv bin/python """

try: file except NameError: raise AssertionError( "You must run this like execfile('path/to/', dict(file='path/to/'))") import sys import os

old_os_path = os.environ.get('PATH', '') os.environ['PATH'] = os.path.dirname(os.path.abspath(file)) + os.pathsep + old_os_path base = os.path.dirname(os.path.dirname(os.path.abspath(file))) if sys.platform == 'win32': site_packages = os.path.join(base, 'Lib', 'site-packages') else: site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages') prev_sys_path = list(sys.path) import site site.addsitedir(site_packages) sys.real_prefix = sys.prefix sys.prefix = base

new_sys_path = [] for item in list(sys.path): if item not in prev_sys_path: new_sys_path.append(item) sys.path.remove(item) sys.path[:0] = new_sys_path

Fix Google Oauth2 Login

Go to the, select your project, and navigate to APIs & Services > Credentials

Now, edit your OAuth 2.0 client ID

Add http://www.[public ip of your server] to Authorized JavaScript origins and Authorized redirect URIs

Download the new OAuth 2.0 client ID JSON file and copy its contents to your clipboard

Back on your server, open the client_secrets.json file and paste in the new JSON

Now, open the file

sudo nano

Find the two lines with client_secrets.json and replace that filename with the absolute path to the file /var/www/html/flaskapp/client_secrets.json

Finally, everything is ready, and all we have to do is restart the server

sudo service apache2 restart