##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