6.1 Аутентифікація та авторизація користувачів
На цьому етапі ми розглянемо, як забезпечити безпеку нашого багатоконтейнерного застосунку та управління доступом. Це включає аутентифікацію користувачів, шифрування даних, захист API та налаштування безпечних з'єднань між сервісами.
Ціль: забезпечити, щоб тільки зареєстровані та аутентифіковані користувачі могли взаємодіяти із застосунком та виконувати операції.
Реалізація JWT (JSON Web Token) для аутентифікації
Крок 1. Встановлення необхідних бібліотек:
pip install Flask-JWT-Extended
Крок 2. Налаштування JWT у Flask-застосунку:
Додайте наступні зміни у файл backend/app/__init__.py:
from flask_jwt_extended import JWTManager
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://taskuser:taskpassword@database:5432/taskdb'
app.config['JWT_SECRET_KEY'] = 'your_jwt_secret_key' # Замініть на ваш секретний ключ
db = SQLAlchemy(app)
jwt = JWTManager(app)
from app import routes
Крок 3. Створення маршрутів для реєстрації та авторизації:
Додайте наступні маршрути у файл backend/app/routes.py:
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from werkzeug.security import generate_password_hash, check_password_hash
from app.models import User, Task
@app.route('/register', methods=['POST'])
def register():
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user = User(username=data['username'], password=hashed_password)
db.session.add(new_user)
db.session.commit()
return jsonify({'message': 'User registered successfully'}), 201
@app.route('/login', methods=['POST'])
def login():
data = request.get_json()
user = User.query.filter_by(username=data['username']).first()
if not user or not check_password_hash(user.password, data['password']):
return jsonify({'message': 'Invalid credentials'}), 401
access_token = create_access_token(identity=user.id)
return jsonify({'access_token': access_token}), 200
@app.route('/tasks', methods=['GET'])
@jwt_required()
def get_tasks():
current_user_id = get_jwt_identity()
tasks = Task.query.filter_by(owner_id=current_user_id).all()
return jsonify([task.to_dict() for task in tasks])
Крок 4. Оновлення моделі User для зберігання паролів:
Оновіть файл backend/app/models.py:
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
tasks = db.relationship('Task', backref='owner', lazy=True)
6.2 Шифрування даних
Мета: забезпечити захист даних при передачі між клієнтом і сервером.
Використання HTTPS
Крок 1. Налаштування Nginx як реверс-проксі з підтримкою HTTPS:
Створіть файл nginx.conf в кореневій директорії проекту:
server {
listen 80;
server_name your_domain.com;
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api {
proxy_pass http://backend:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Крок 2. Створення Dockerfile для Nginx:
Створіть файл Dockerfile у директорії nginx:
FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
Крок 3. Додавання Nginx у compose.yaml:
Оновіть файл compose.yaml, додавши сервіс для Nginx:
version: '3'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
networks:
- task-network
backend:
build: ./backend
ports:
- "5000:5000"
depends_on:
- database
networks:
- task-network
environment:
- DATABASE_URL=postgresql://taskuser:taskpassword@database:5432/taskdb
database:
image: postgres:13
environment:
- POSTGRES_DB=taskdb
- POSTGRES_USER=taskuser
- POSTGRES_PASSWORD=taskpassword
networks:
- task-network
volumes:
- db-data:/var/lib/postgresql/data
nginx:
build: ./nginx
ports:
- "80:80"
depends_on:
- frontend
- backend
networks:
- task-network
networks:
task-network:
driver: bridge
volumes:
db-data:
6.3 Отримання SSL сертифіката за допомогою Let's Encrypt
Крок 1. Встановлення Certbot:
Дотримуйтесь інструкцій на офіційному сайті Certbot для встановлення Certbot.
Крок 2. Отримання сертифіката:
sudo certbot certonly --standalone -d your_domain.com
Крок 3. Налаштування Nginx для використання SSL:
Оновіть nginx.conf для використання SSL:
server {
listen 80;
server_name your_domain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name your_domain.com;
ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
location / {
proxy_pass http://frontend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api {
proxy_pass http://backend:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Крок 4. Оновлення Dockerfile для Nginx для копіювання сертифікатів:
FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
COPY /etc/letsencrypt /etc/letsencrypt
6.4 Захист API
Мета: обмежити доступ до API та запобігти несанкціонованим запитам.
Використання JWT для захисту маршрутів
Ми вже додали захист для маршрутів задач, використовуючи декоратор @jwt_required(). Переконайтеся, що всі чутливі маршрути захищені цим декоратором:
from flask_jwt_extended import jwt_required, get_jwt_identity
@app.route('/tasks', methods=['GET'])
@jwt_required()
def get_tasks():
current_user_id = get_jwt_identity()
tasks = Task.query.filter_by(owner_id=current_user_id).all()
return jsonify([task.to_dict() for task in tasks])
Обмеження доступу до бази даних
Мета: запобігти несанкціонованому доступу до бази даних.
Налаштування ролей та привілеїв
Крок 1. Створення користувача із обмеженими привілеями:
CREATE USER limited_user WITH PASSWORD 'limited_password';
GRANT CONNECT ON DATABASE taskdb TO limited_user;
GRANT USAGE ON SCHEMA public TO limited_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO limited_user;
Крок 2. Оновлення змінної середовища DATABASE_URL:
Оновіть змінну середовища DATABASE_URL у файлі compose.yaml:
environment:
- DATABASE_URL=postgresql://limited_user:limited_password@database:5432/taskdb
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ