Skip to content

VLety/fastApiProject

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

REST API server solution based on FastAPI framework with RBAC model

Recommended for PoC and Prototype approach or as a base template
The delivery result of this project can be seen here https://fastapiproject.key-info.com.ua/api/v1/docs

Note

  • Purpose: Intended for educational and promotional needs.
  • Audience: Python Junior+/Middle level with linux DevOps skills.

Tip

This solution is presented in the most easy-to-learn form without using Docker delivery technology or an additional full-fledged WSGI HTTP server such as Gunicorn. We will simply use the Uvicorn ASGI web server that is already built into FastAPI framework with NGINX as a proxy server. And this deployment option will be sufficient for PoC, Prototype, or even MVP purposes.
Read more about FastAPI recommended deployment.

Important

However, to fully utilize the solution in a production environment, it is recommended to add Docker delivery technology, use PostgreSQL database instead of SQLite3 (AWS RDS will be enough), optionally add Redis for caching support and Gunicorn WSGI server with automatic management of multiple worker processes in front of Uvicorn ASGI server, if it is really necessary according to the project requirements.

Project IDEA

Note

User story

Need to create a Prototype/MVP backend solution for implementing a simple Ticket system.

  • Participants: User of the outsourcing support department, Employee and Ticket on the basis of which the employees are supported. The creation and processing of the Ticket is the responsibility of the support department.
  • Security: Access to the system based on user's roles. It is also necessary to have separate explicit user attributes to control enabling/disabling access to the system and allowing/denying login.

Technical requirements

  • REST API server with Swagger UI.
  • OAuth2 authorization protocol with Role-based access control (RBAC) model.
  • Ability for easily integration to other backend solutions and frontend UI/UX part.

Project Tech stack

  • Python 3.10+
  • FastAPI as a base project framework read more
  • SQLAlchemy V2 as SQL toolkit and Object Relational Mapper read more
  • Pydantic V2 as schemas builder read more
  • Annotated(typing) for metadata management read more
  • PyJWT for encode and decode JSON Web Tokens (JWT) read more
  • Passlib[Argon2] for password hashing read more
  • Pytest as a Python testing framework read more
  • Uvicorn as ASGI web server read more
  • NGINX as Reverse Proxy & TLS Termination service read more
  • Certbot as Let’s Encrypt SSL certificate manager read more

Note

What is the difference between WSGI and ASGI server interface specification? In simple words: WSGI is synchronous, handling one request at a time, and blocking execution until processing is complete. ASGI is asynchronous, handling multiple requests concurrently without blocking other requests. We know that FastAPI is an asynchronous framework, so Uvicorn ASGI web server is an obvious choice.

Note

Using Nginx as a Proxy in front of your WSGI or ASGI server may not be necessary for PoC or Prototype approach, but is recommended for additional resilience and full-fledged production environment. Nginx can deal with serving your static media and buffering slow requests, leaving your application servers free from load as much as possible, add more security etc.

Note

For password hashing we will not apply the commonly used [bcrypt] algorithm, we will use the Argon2. Argon2 is a modern Password Hashing Algorithm and is intended to replace pbkdf2_sha256, bcrypt, and scrypt read more

Project specification and standards

  • OpenAPI Specification v3.1 read more
  • OAuth 2.0 authorization protocol read more
  • Role-Based Access Control (RBAC) permissions model read more

Project Tech features

  • Full-fledged RESTful API server with Swagger UI
  • OAuth 2.0 authorization protocol
  • JWT token authentication with expiration period and optional authorization scopes
  • Flexible project customization and tuning using configuration files
  • CRUD operations for 3 objects: User, Employee and Ticket
  • PATCH operations on User object (name or password changing etc)
  • User RBAC permissions model for each API endpoint with additional security attributes: Disabled and LoginDenied

Tip

Learn more about the project security model

The role-based access control (RBAC) model is used to grant permissions to any API endpoint, except for the Authentication section, where we have:

  • Login event based on username and password (get a valid token) /api/v1/token
  • Get information about me (based on a valid user token) /api/v1/me
  • Get my current status considering on Disabled and LoginDenied User state (based on a valid user token) /api/v1/status

RBAC roles:

  • admin - can do CRUD (PATCH) requests with User, Employee and Ticket (top level of security).
  • manager - can do CRUD requests with Employee and Ticket, also read User and UPDATE: Contacts, Disabled and LoginDenied attribute. Can't change User role(s).
  • support - can do CRUD requests with Ticket, also read Employee.

Additional security attributes

  • Disabled users with valid token cannot access any endpoints regardless of their role(s), except for the Authentication section - but can Login (get valid token).
  • LoginDenied users cannot Login (cannot get valid token).
  • Default token expiration period equal "ACCESS_TOKEN_EXPIRE_MINUTES": 60 (we can changer this setting in ./config/config.json file).

Setup and deploy project to the "clear" AWS EC2 Ubuntu instance

Tip

For the EC2 instance, it is recommended to choose the Ubuntu 24.04 LTS OS type as it comes with Python 3.12 pre-installed but you can try other linux OS. We will not consider the installation of EC2 instance via AWS console in this article as this is a separate topic, assuming that the necessary knowledge already exists. For PoC or Prototype project deployments, the AWS Free Tier will be sufficient read more.

Important

In our project setup we will use everywhere the name "fastApiProject", linux user "ubuntu" and url "fastapiproject.key-info.com.ua", what is done for simplicity. But for your needs you can/can/must use your own name/user/url.

To proceed further, log in to the EC2 instance linux server console...

Install base software

Update system

sudo apt update && sudo apt upgrade -y

Install GIT

sudo apt -y install git

Install common Python Dependencies

sudo apt -y install build-essential libssl-dev libffi-dev python3-dev
sudo apt -y install python3-pip
sudo apt -y install python3-venv

Setup project configuration

Clone a project from a GitHub repository

git clone https://github.com/VLety/fastApiProject.git

Let's go to our project catalog

cd /home/ubuntu/fastApiProject/

Create lightweight Python “virtual environments” (VENV) read more:

python3 -m venv venv

Activate VENV

source venv/bin/activate

Install Python dependencies

VENV must be in Active mode

pip3 install "fastapi[standard]"
pip3 install SQLAlchemy
pip3 install pyjwt
pip3 install "passlib[argon2]"
pip3 install pytest
pip3 install pytest-assert-utils

Or use requirements.txt installation file - the choice is yours

pip3 install -r ./requirements.txt

Deactivate VENV

deactivate

Setup configuration files

Caution

Never ever add configuration files to the repository! This is due to potential security issues and problems with updates delivery.

Important

There are many approaches to avoid "config problem" - we will use the initial creation of configuration files from the project config templates.

Copy all 5 config template files from ./setup/config to the base project's ./config folder and rename them by removing the "template" extension.

cp -f /home/ubuntu/fastApiProject/setup/config/*.template /home/ubuntu/fastApiProject/config/
cd /home/ubuntu/fastApiProject/config/
mv -f config.json.template config.json
mv -f permissions.json.template permissions.json
mv -f schemas.json.template schemas.json
mv -f test_main.json.template test_main.json
mv -f log.ini.template log.ini

As a result, we should get the following list of files:
image

Generate a new SECRET_KEY that will be used to encrypt/decrypt JWT tokens

openssl rand -hex 32

Copy new SECRET_KEY to the project /config/config.json file

nano /home/ubuntu/fastApiProject/config/config.json

Change SECRET_KEY value

"auth": {
    "SECRET_KEY": "paste new secret key here",

Save: Ctrl+o, Exit: Ctrl+x

Warning

Do not use the project default SECRET_KEY for production environment!

Tip

If you wish, you can change the necessary project parameters using the initial setup, please going through:

  • config.json: The file is intended to store the main project configuration settings.
  • schemas.json: The file is used to configure Pydantic schemas validation.
  • permissions.json: The file is used to configure RBAC permissions for API endpoints.
  • test_main.json: The file is intended to store the main project test settings.
  • log.ini: The file is used to configure server logging.

Change password for default users

Initially we have 3 default users: admin, manager and support. So please open the ./setup/setup.json file, change the passwords for all 3 users and save the file with the new passwords.

nano /home/ubuntu/fastApiProject/setup/setup.json

Example:
image
Save: Ctrl+o, Exit: Ctrl+x

Tip

Default password requirements (can be changed in the schemas.json file): minimum password length is 8 characters - maximum password length is 16 characters. At least one uppercase and one lowercase letter, one number and one special character.

Update new passwords in database

Activate project's VENV

cd /home/ubuntu/fastApiProject/
source venv/bin/activate

Run password update

python /home/ubuntu/fastApiProject/setup/change_users_password.py

We should see something like this: image

Run project TEST to check if everthing setup properly

Set relevant admin user name & password in the ./config/test_main.json file

nano /home/ubuntu/fastApiProject/config/test_main.json

Save: Ctrl+o, Exit: Ctrl+x

Run test in compact output mode

VENV must be in Active mode

python -m pytest --disable-pytest-warnings /home/ubuntu/fastApiProject/test_main.py

Run test in verbose output mode [optional]

python -m pytest -rP /home/ubuntu/fastApiProject/test_main.py

Also we can run API server in a port mode [optional]

VENV must be in Active mode

uvicorn main:app --host 127.0.0.1 --port 8000

We should see something like this:
image

Tip

Setup project configuration completed successfully!

Add Systemd service

Note

A tool that is starting to be common on linux systems is Systemd. It is a system services manager that allows for strict process management, resources and permissions control. The Linux/Unix socket approach is used to create a communication endpoint and return a file descriptor referencing that endpoint. We will use the Systemd service to manage the state of our API server: starting, restarting, stopping and check current status. Read more about Uvicorn RUN instance settings

Create a Systemd service file

sudo nano /etc/systemd/system/fastApiProject.service

Type:

[Unit]
Description=Uvicorn instance to serve fastApiProject
After=network.target

[Service]
# The specific user that our service will run as
# Need R/W/X access to the project folder
User=ubuntu
Group=ubuntu

# Set project & venv PATH
WorkingDirectory=/home/ubuntu/fastApiProject
Environment="PATH=/home/ubuntu/fastApiProject/venv/bin"

# RUN instance
ExecStart=/home/ubuntu/fastApiProject/venv/bin/uvicorn main:app --workers 3 --log-config /home/ubuntu/fastApiProject/config/log.ini --forwarded-allow-ips='*' --uds /tmp/fastApiProject.sock

# Support parameters
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5

# Socket .sock file access type (access from NGINX side requires false)
PrivateTmp=false

# This user can be transiently created by systemd
# DynamicUser=true

# If your app does not need administrative capabilities, let systemd know
# ProtectSystem=strict

[Install]
WantedBy=multi-user.target

Save: Ctrl+o and Exit: Ctrl+y

Cause Systemd to reload units update from disk

sudo systemctl daemon-reload

Set service to autoload when server starts

sudo systemctl enable fastApiProject.service

Start project service

sudo systemctl start fastApiProject.service

Check project service status

sudo systemctl status fastApiProject.service

We should see something like this:
image

Tip

Systemd service added successfully!

NGINX setup

Note

FastAPI latency is lower when communicating with NGINX via a socket than when communicating via a port, but both solutions will work. We will go the way of NGINX communicating with Uvicorn via a socket connection.

Add the public IP address of EC2 instance to your domain's DNS A record.

My variant: image

Install NGINX

sudo apt update && sudo apt upgrade -y
sudo apt -y install nginx

Delete default NGINX configuration file symlink

sudo rm /etc/nginx/sites-enabled/default

Create new NGINX configuration file

sudo nano /etc/nginx/sites-available/fastApiProject

Type:

Important

Don't forget to specify your domain url for the server_name fastApiProject.key-info.com.ua; configuration string

server {
    listen 80;
    
    # Project server URL
    server_name fastApiProject.key-info.com.ua;

    location / {

		# Unix Socket mode
		proxy_pass http://fastApiProject;
		proxy_redirect off;
		proxy_buffering off;
		proxy_ignore_client_abort on;

		# NGINX timeout should be about 5 seconds longer than the same Uvicorn setting
		proxy_connect_timeout 65s;
		proxy_read_timeout 65s;
		proxy_send_timeout 65s;
		
		# Proxy header section
		proxy_set_header Host $http_host;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection $connection_upgrade;
		proxy_set_header X-Real-IP $remote_addr;
		
		# NGINX to pass through the 'server' header of upstream server (Uvicorn)
		proxy_pass_header Server;
		
		# Sets the HTTP protocol version for proxying. By default, version 1.0 is used.
		# Version 1.1 is recommended for use with keepalive connections and NTLM authentication. 
		proxy_http_version 1.1;
	}

	# Path for project static files
	location /static {
		root /home/ubuntu/fastApiProject/static;
        }

    # Optional project settings which you need to select at your own discretion
    server_tokens off;
    client_max_body_size 8M;
    keepalive_requests 5000;  
    keepalive_timeout 120;
    set_real_ip_from 10.0.0.0/8;
    set_real_ip_from 172.16.0.0/12;
    set_real_ip_from 192.168.0.0/16;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    gzip on; # gzip settings
    gzip_proxied any;
    gzip_disable "msie6";
    gzip_comp_level 6;
    gzip_min_length 200; # check your average response size and configure accordingly
}

map $http_upgrade $connection_upgrade {
	default upgrade;
    '' close;
}

# Uvicorn service file descriptor referencing
upstream fastApiProject{
    server unix:/tmp/fastApiProject.sock;
}

Save: Ctrl+o, Exit: Ctrl+x

Copy NGINX configuration file as symlink to the site-enabled folder

sudo ln -sf /etc/nginx/sites-available/fastApiProject /etc/nginx/sites-enabled/fastApiProject

Restart NGINX

sudo systemctl restart nginx.service

Check NGINX status

sudo systemctl status nginx.service

We should see something like this: image

Check the project's HTTP URL

My variant: http://fastapiproject.key-info.com.ua/api/v1/docs image

Tip

NGINX setup completed successfully!

Certbot setup

Install Certbot

sudo apt update && sudo apt upgrade -y
sudo apt install snapd
sudo snap install --classic certbot

Run Certbot to create project's SSL certificate

In our project, the role of the TLS terminator will be performed by the NGINX server.

sudo certbot --nginx

We should see something like this (also enter your email address for important Certbot messages):
image
If we now look at the NGINX configuration file, we will see the changes thanks to which we got the HTTPS connection.
image
The Certbot packages on your system come with a cron job that will renew your certificates automatically before they expire (certificate are valid for 90 days). You will not need to run Certbot again, unless you change your configuration. You can test automatic renewal for your certificates by running this command:

sudo certbot renew --dry-run

Restart NGINX for HTTPS changes update

sudo systemctl restart nginx

Check the project's HTTPS URL
My variant: https://fastapiproject.key-info.com.ua/api/v1/docs

Tip

Project setup and deployment completed successfully!

Releases

No releases published

Packages

No packages published