##Initial server setup
After receiving email with password ssh to remote server:
```bash
ssh root@ip
```
Then update the system
```bash
sudo apt update
sudo apt upgrade
```
To check the hostnames on machine type
```bash
hostname
```
or access the file that contains these records
```bash
cat /etc/hosts/
```
To change hostname of the machine:
```bash
hostname set-hostname name
```
Also change it /etc/hosts file. Open it with text editor for example nano
```bash
nano /etc/hosts
```
It is good practice to add user with a limited right on the system instead of running all commands as root. To add a new user type
```bash
adduser username
```
To list all local users
```bash
cut -d: -f1 /etc/passwd
```
We need to add the user we have just created to sudo group
```bash
adduser user sudo
```
Now we are able to change the user
```bash
su user
```
Now we have to enable ssh key-bases authentication. Instead of passwords which can be bruteforced we will use key to access remote server. At first on the remote machine we need to create a folder with the name .ssh This folder must be located in the home directory of the user in which we logging in.
```bash
cd ~
mkdir .ssh
```
Now on local machine we need to create pair of keys with the help of __ssh-keygen__
Parameters:
- [ b ] parameter stands for lenght of the key in bits
- [ f ] parameter stands for filename
```bash
ssh-keygen -b 4096 -f filename
```
After running this command 2 files will be created in the folder where you ran ssh-keygen.
Files:
- Keyname - Private Key
- Keyname.pub - Public Key
Now we need to upload Public Key from local machine to remote machine. We will use __scp__ command
```bash
scp keys_folder_on_local_machine/keyname.pub user@ip:~/.ssh
```
On the remote machine copy contents of the public key file to the `~/.ssh/authorized_keys` file
```bash
cat ~/.ssh/keyname.pub >> authorized_keys
```
Remove original keyfile file from server
```bash
rm ~/.ssh/keyname.pub
```
On remote machine change .ssh folder permissions:
```bash
sudo chmod 700 ~/.ssh/
```
Then change permissions of all files in .ssh folder
```bash
sudo chmod 600 ~/.ssh/*
```
Now we can login using ssh key. To test it at first we need to drop existing connection with the server
```bash
exit
```
To log into server with the help of key type
```bash
ssh -i location_to_privatekey user@ip
```
We loged in with the help of ssh key, but we still have access to this server using password. We need to disable password authentication. Also there is still enabled root login which has to be turned off because we have limited user. Open __sshd_config file__ with the text editor
```bash
nano /etc/ssh/sshd_config
```
Find, uncomment and change several strings in this config file
- PermitRootLogin no
- PasswordAuthentication no
Now restart ssh service
```bash
sudo systemctl restart sshd
```
## Firewall
To make changes in the server's firewall settings we will use __ufw__ (uncomplicated firewall). To install it
```bash
sudo apt install ufw
```
Allow outgoing traffic
```bash
sudo ufw default allow outgoing
```
Dissallow incoming traffic:
```bash
sudo ufw default deny incoming
```
Now the default rule for incoming traffic is deny. It means that all the incoming traffic will be denied by the server including ssh. At this point if we update ufw rules we will lose access to the server so we have to add ssh traffic to exceptions.
```bash
sudo ufw allow ssh
```
Also for testing purposes we will allow port 5000. Default port which flask runs
```bash
sudo ufw allow 5000
```
Changes have not been made yes. We need to run
```bash
sudo ufw enable
```
We will get warning that we can lose connection. Press y and continue
## Uploading application files
There are several posible options to move application files to the server:
- GIT
- FTP
- SCP
At this time we will use scp. Flask app may have dependencies (3rd-party libraries or python libraries that are not in the default package). We need to make full list of them. At first activate python virtual environment which runs flask application.
```bash
source activate path_to_venv_activate_script
```
Now write requirements to requirements.txt file
```bash
pip freeze > requirements.txt
```
Move file to project folder
```bash
mv requirements.txt project_folder_path
```
Upload app folder to server using __scp__
Parameters:
- [ r ] - Recursive
- [ i ] - Path to ssh Private Key
```bash
scp -i path_to_privatekey -r app_folder_path user@ip:location
```
After all project files were moved to server we have to create python virtual environment for the project.
```bash
sudo apt install python3-pip
sudo apt install python3-venv
```
Now we can create virtual environment but at first we will create folder which will contain all virtual environments. We will create it in the home directory of the user
```bash
cd ~
mkdir virtual_envs
```
Now create virtual environment for the project
```bash
python3 -m venv ~/virtual_envs/virtual_environment_name
```
Activate this environment
```bash
source ~/virtual_envs/virtual_environments_folder/bin/activate
```
Move to project folder and install all dependencies
```bash
cd project_folder
pip install -r requirements.txt
```
To check installed packages type
```bash
pip list
```
## Database setup
For this project as the database will be used __MYSQL__
Install database
```bash
sudo apt install mysql-server
```
After it has been downloaded
```bash
sudo mysql_secure_installation
```
To login as root to mysql
```bash
sudo mysql -u root -p
```
Enter the password
To show all databases
```bash
show databases;
```
To show all users in db
```bash
select user from mysql.user;
```
To create a new user
```
create user username@localhost identified by password
```
To give access privileges to new user
```
grant all privileges on *.* to username@localhost;
```
In this case to a limited user were given all rights. To give a specific right on a specific database
```
grant right on database.table to username@localhost
```
To remove rights use revoke instead of grant
To update privileges
```
flush privileges;
```
Before letting app accessing database we need to create it
## Nginx + Gunicorn
To install Nginx
```bash
sudo apt install nginx
```
Before installing gunicorn make sure you are in the virtual environment of your project
```bash
pip install gunicorn
```
Now we will configure nginx. At first remove default nginx configuration file
```bash
sudo rm /etc/nginx/sites-enabled/default
```
Create new configuration file
```bash
sudo nano /etc/nginx/sites-enabled/app_name
```
Configuration file
```nginx
server {
listen 80;
server_name ip;
location /static {
alias app_folder/static
}
location / {
proxy_pass http://localhost:8000;
include /etc/nginx/proxy_params;
proxy_redirect off;
}
}
```
Now configure firewall
```bash
sudo ufw allow http/tcp
sudo ufw delete allow 5000
sudo ufw enable
```
Restart nginx
```bash
sudo systemctl restart nginx
```
To run gunicorn
```bash
gunicorn -w amount_of_workers run:app
```
Numbers of workers amount of cpu cores * 2 + 1
To check amount of cpu cores
```bash
nproc --all
```
Go to project folder
```bash
cd project_folder
```
To automate server restart use supervizor
```bash
sudo apt install supervisor
```
Configure supervisor
```bash
sudo nano /etc/supervisor/conf.d/app_name.conf
```
Type there
```
[program:appname]
directory=project_folder_path
command=virtual_environment_folder/bin/gunicorn -w 3 run:app
user=user
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stderr_logfile=/var/log/app_name/app_name.err.log
stdout_logfile=/var/log/app_name/app_name.out.log
```
Now create log folder. And log files
```
sudo mkdir -p /var/log/app_name
sudo touch /var/log/app_name/app_name.err.log
sudo touch /var/log/app_name/app_name.out.log
```
Restart supervisor
```bash
sudo supervisorctl reload
```
## Environments variables
To set environment variable
```bash
export name=value
```
To show all variables
```bash
printenv
```
or
```bash
set
```
If you want to check specific environment variable combine command with grep. For example you want to check environmental variable __MAIL_USERNAME__
```bash
printenv | grep MAIL_USERNAME
```
Also we have to let supervisor see environmental variables. Open file /etc/supervisor/supervisord.conf
```bash
sudo nano /etc/supervisor/supervisord.conf
```
Under section [supervisord] add your environment variables
```bash
environment=env_var_1:"value_1",env_var_2:"value_2"
```
## Domain Name
Few steps
- Obtain domain name
- Enable Whois guard
- Add AAAA Record for IPv6
- Add A Record for IPv4
- Change on domain register nameservers
- Set Reverse DNS
## SSL Encryption
To secure a web server we will use free certificate from let's encrypt. We have shell access to server so we can use certbot. We have ubuntu and nginx so we need to run next commands
```bash
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
```
To install certbot run
```bash
sudo apt-get install python-certbot-nginx
```
Also we need to change nginx configuration file. We need to replace ip address for a domain name
```bash
sudo nano /etc/nginx/sites-enabled/app_name
```
```nginx
server {
listen 80;
server_name domain_name;
location /static {
alias app_folder/static
}
location / {
proxy_pass http://localhost:8000;
include /etc/nginx/proxy_params;
proxy_redirect off;
}
}
```
Now we can run certbot command
```bash
sudo certbot --nginx
```
It will ask several questions
- Email address
- Ask us to agree with terms and conditions
- Share info with us sending it on your email
Now it will ask for which domain names you want to create the certificate. You need to type a number.
Also it will ask if we want to redirect http traffic to https.
Now it will create the certificate and change our nginx configuration file for our app. Now configuration file will look like this
```nginx
server {
server_name domain_name;
location /static {
alias /project_folder/static;
}
location / {
proxy_pass http://localhost:8000;
include /etc/nginx/proxy_params;
proxy_redirect off;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/domain_name/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/domain_name/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = domain_name) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name domain_name;
return 404; # managed by Certbot
}
```
Now we need to allow https traffic
```bash
sudo ufw allow https
```
Also we can disallow http traffic but our nginx configured in a way to redirect http traffic to https. Last step is to disallow test port and confirm changes.
```bash
sudo ufw delete allow 5000
sudo ufw enable
```
Restart nginx
```bash
sudo systemctl restart nginx
```
We can set auto renew for our certificates otherwise they will last around __90__ days and after it they will not be considered valid. To imitate certificate renewal we can run dry run
```bash
sudo certbot renew --dry-run
```
To auto renew certificate we can run cron job
```bash
sudo crontab -e
```
Choose text editor and type in the end
```
30 4 1 * * sudo certbot renew --quiet
```
Save file