chore: update

This commit is contained in:
2026-03-03 23:42:23 +01:00
parent 52b059fafa
commit 0073f80a48
95 changed files with 2720 additions and 40619 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ backend_dir: "/home/ubuntu/backend"
backend_binary: "/home/ubuntu/backend/main"
uploads_dir: "/home/ubuntu/backend/uploads"
frontend_port: 5173
domain_name: "uber-stup.club"
domain_name: "mln-uber.club"
# Utilisateurs et permissions
user_web: "www-data" # Utilisateur pour les applications web
user_deploy: "ubuntu" # Utilisateur pour le déploiement
+4
View File
@@ -1,5 +1,9 @@
[all]
uber-stup ansible_host=5.252.20.129 ansible_user=root ansible_ssh_pass=rL9lY6YkcDQmfRuZ3Z
uber-stup-web ansible_host=185.234.9.102 ansible_user=root ansible_ssh_pass=hTASFYOydY46haeO3J
[uber-stup]
uber-stup ansible_host=5.252.20.129 ansible_user=root ansible_ssh_pass=rL9lY6YkcDQmfRuZ3Z
[uber-stup-web]
uber-stup-web ansible_host=185.234.9.102 ansible_user=root ansible_ssh_pass=hTASFYOydY46haeO3J
+15 -2
View File
@@ -1,6 +1,6 @@
---
- name: Installation et configuration du frontend et backend
hosts: all
hosts: uber-stup
become: true
gather_facts: true
@@ -94,7 +94,6 @@
owner: "{{ user_deploy }}"
group: "{{ user_deploy }}"
mode: "0755"
tags: backend
- name: Synchroniser le backend Go
ansible.builtin.synchronize:
@@ -111,6 +110,20 @@
recursive: yes
tags: backend
- name: Fix backend ownership after rsync (rsync tourne en root)
ansible.builtin.file:
path: "{{ backend_dir }}"
owner: "{{ user_deploy }}"
group: "{{ user_deploy }}"
recurse: yes
tags: backend
- name: Remove old binary if owned by another user
ansible.builtin.file:
path: "{{ backend_dir }}/main"
state: absent
tags: backend
- name: Compile backend as deploy user
become_user: "{{ user_deploy }}"
ansible.builtin.shell: |
+1 -1
View File
@@ -3,7 +3,7 @@
# Fail2Ban Installation et configuration
# ============================================
- name: Installation et configuration de Fail2Ban
hosts: uber-stup
hosts: uber-stup-web
become: true
gather_facts: true
+260
View File
@@ -0,0 +1,260 @@
- name: Déploiement complet Frontend
hosts: uber-stup-web
gather_facts: true
become: true
vars:
domain_name: mln-uber.club
frontend_dir: /home/ubuntu/frontend
frontend_port: 5173
tasks:
- name: Update and upgrade
ansible.builtin.apt:
update_cache: yes
cache_valid_time: 3600
upgrade: dist
- name: Install basic dependencies
ansible.builtin.apt:
name:
- curl
- nginx
- tar
- build-essential
- certbot
- python3-certbot-nginx
state: present
- name: Créer l'utilisateur de déploiement s'il n'existe pas
ansible.builtin.user:
name: "{{ user_deploy }}"
shell: /bin/bash
create_home: yes
state: present
- name: Ensure /home/{{ user_deploy }} exists with correct permissions
ansible.builtin.file:
path: "/home/{{ user_deploy }}"
state: directory
owner: "{{ user_deploy }}"
group: "{{ user_deploy }}"
mode: "0755"
- name: Setup Node.js 20 repository
ansible.builtin.shell: |
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
args:
executable: /bin/bash
creates: /etc/apt/sources.list.d/nodesource.list
- name: Install Node.js 20
ansible.builtin.apt:
name: nodejs
state: present
update_cache: yes
dpkg_options: "force-overwrite"
- name: Verify Node.js and npm versions
ansible.builtin.shell: |
node -v
npm -v
register: versions_check
changed_when: false
- name: Show versions
ansible.builtin.debug:
msg: "{{ versions_check.stdout_lines }}"
- name: Create folder for frontend
ansible.builtin.file:
path: "{{ frontend_dir }}"
state: directory
owner: "{{ user_deploy }}"
group: "{{ user_deploy }}"
mode: "0755"
tags: frontend
- name: Synchroniser le frontend (excluant node_modules et .git)
become_user: "{{ user_deploy }}"
ansible.builtin.synchronize:
src: ../frontend-prep/
dest: "{{ frontend_dir }}/"
rsync_opts:
- "--exclude=node_modules"
- "--exclude=.git"
- "--exclude=.gitignore"
- "--exclude=build"
delete: no
recursive: yes
perms: yes
owner: yes
rsync_path: rsync
tags: frontend
- name: Install npm dependencies
become_user: "{{ user_deploy }}"
ansible.builtin.command: npm install
args:
chdir: "{{ frontend_dir }}"
tags: frontend
- name: Build frontend
become_user: "{{ user_deploy }}"
ansible.builtin.command: npm run build
args:
chdir: "{{ frontend_dir }}"
tags: frontend
- name: Install serve globally
ansible.builtin.command: npm install -g serve
tags: frontend
- name: Création du fichier systemd pour le frontend
ansible.builtin.copy:
dest: /etc/systemd/system/frontend.service
owner: root
group: root
mode: "0644"
content: |
[Unit]
Description=Frontend React (serve)
After=network.target
[Service]
User={{ user_deploy }}
WorkingDirectory={{ frontend_dir }}
ExecStart=/usr/bin/npx serve -s build -l {{ frontend_port }}
Restart=always
RestartSec=5
Environment="NODE_ENV=production"
Environment="VITE_TOMTOM_API_KEY=MERY8I7LMeYVSLKO5WuV73W9rKJpBLoB"
[Install]
WantedBy=multi-user.target
notify: Reload systemd
tags: frontend
- name: Enable and start frontend service
ansible.builtin.systemd:
name: frontend
state: started
enabled: yes
daemon_reload: yes
tags: frontend
- name: Configuration UFW
ansible.builtin.ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "22"
- "80"
- "443"
- name: Activation du firewall
ansible.builtin.ufw:
state: enabled
- name: Vérifier si un certificat existe déjà
ansible.builtin.stat:
path: "/etc/letsencrypt/live/{{ domain_name }}/fullchain.pem"
register: cert_file
tags: [certbot]
- name: Déployer la configuration Nginx HTTP
ansible.builtin.template:
src: templates/nginx-frontend.conf.j2
dest: /etc/nginx/sites-available/frontend
vars:
ssl_enabled: false
tags: [certbot]
- name: Activation du site Nginx
ansible.builtin.file:
src: /etc/nginx/sites-available/frontend
dest: /etc/nginx/sites-enabled/frontend
state: link
force: yes
tags: [certbot]
- name: Suppression du site par défaut
ansible.builtin.file:
path: /etc/nginx/sites-enabled/default
state: absent
tags: [certbot]
- name: Test de la configuration Nginx
ansible.builtin.command: nginx -t
changed_when: false
tags: [certbot]
- name: Redémarrage de Nginx
ansible.builtin.systemd:
name: nginx
state: restarted
enabled: yes
tags: [certbot]
# ============================================================
# Certificat SSL Let's Encrypt
# ============================================================
- name: Générer le certificat SSL avec Certbot
ansible.builtin.command: >
certbot certonly --nginx
-d {{ domain_name }}
--non-interactive
--agree-tos
--email admin@{{ domain_name }}
when: not cert_file.stat.exists
tags: [certbot]
# ============================================================
# Nginx - reconfiguration HTTPS après certificat
# ============================================================
- name: Vérifier la présence du certificat
ansible.builtin.stat:
path: "/etc/letsencrypt/live/{{ domain_name }}/fullchain.pem"
register: cert_file_after
- name: Déployer la configuration Nginx HTTPS
ansible.builtin.template:
src: templates/nginx-frontend.conf.j2
dest: /etc/nginx/sites-available/frontend
vars:
ssl_enabled: true
when: cert_file_after.stat.exists
notify: Restart nginx
tags: [nginx]
- name: Test de la configuration Nginx finale
ansible.builtin.command: nginx -t
changed_when: false
tags: [nginx]
- name: Redémarrage de Nginx avec SSL
ansible.builtin.systemd:
name: nginx
state: restarted
when: cert_file_after.stat.exists
tags: [nginx]
- name: Vérifier le renouvellement automatique
ansible.builtin.command: certbot renew --dry-run
register: certbot_renew
changed_when: false
failed_when: false
tags: [certbot]
- name: Afficher le statut du renouvellement
ansible.builtin.debug:
msg: "{{ certbot_renew.stdout_lines }}"
tags: [certbot]
handlers:
- name: Reload systemd
ansible.builtin.systemd:
daemon_reload: yes
- name: Restart nginx
ansible.builtin.systemd:
name: nginx
state: restarted
-74
View File
@@ -1,74 +0,0 @@
SecRuleRemoveById 932235
SecRuleRemoveById 911100
SecRule REQUEST_URI "@streq /api/v2/admin/protected/products" \
"id:399002,phase:2,nolog,pass,\
ctl:ruleRemoveById=920120,\
ctl:ruleRemoveById=920121"
SecRule REQUEST_URI "@beginsWith /uploads/" \
"id:1000,\
phase:1,\
pass,\
nolog,\
ctl:ruleEngine=Off"
SecRule IP:BANNED "@eq 1" \
"id:100000,phase:1,deny,status:403,log,\
msg:'IP is banned'"
SecRule IP:REPUTATION_SCORE "@ge 100" \
"id:100099,phase:1,deny,status:403,log,\
msg:'Critical reputation score',\
setvar:'ip.blocked=1',expirevar:'ip.blocked=86400'"
SecRule TX:SQL_INJECTION_SCORE "@ge 5" \
"id:100001,phase:2,deny,status:403,log,\
msg:'SQL Injection detected',\
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
SecRule TX:XSS_SCORE "@ge 5" \
"id:100010,phase:2,deny,status:403,log,\
msg:'XSS detected',\
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
SecRule TX:RCE_SCORE "@ge 5" \
"id:100020,phase:2,deny,status:403,log,\
msg:'RCE detected',\
setvar:'ip.banned=1',expirevar:'ip.banned=259200'"
SecRule TX:LFI_SCORE "@ge 5" \
"id:100030,phase:2,deny,status:403,log,\
msg:'LFI detected',\
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
SecRule TX:INBOUND_ANOMALY_SCORE "@ge 20" \
"id:100060,phase:2,deny,status:403,log,\
msg:'Critical anomaly score',\
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
SecRule REQUEST_URI "@streq /api/v1/panier/remove" \
"id:399010,phase:1,nolog,pass,\
ctl:ruleRemoveById=911100,ctl:ruleRemoveById=920350"
SecRule REQUEST_URI "@streq /api/v1/panier/clear" \
"id:399011,phase:1,nolog,pass,\
ctl:ruleRemoveById=911100,ctl:ruleRemoveById=920350"
SecRule REQUEST_URI "@streq /api/v2/admin/protected/products" \
"id:399001,phase:2,nolog,pass,\
ctl:ruleRemoveById=932235"
SecAction \
"id:400161,phase:1,nolog,pass,\
setvar:'ip.request_window_1sec=+1',\
expirevar:'ip.request_window_1sec=1'"
SecRule IP:REQUEST_WINDOW_1SEC "@gt 20" \
"id:400160,phase:1,deny,status:429,log,\
msg:'Too many requests'"
SecRule IP:REPUTATION_SCORE "@ge 100" \
"id:409999,phase:1,deny,status:403,log,\
msg:'Critical reputation score',\
setvar:'ip.blocked=1',expirevar:'ip.blocked=86400'"
+191
View File
@@ -0,0 +1,191 @@
# =========================================================
# Request ID for correlation
# =========================================================
map $http_x_request_id $req_id {
default $http_x_request_id;
"" $request_id;
}
# =========================================================
# Backend distant
# =========================================================
upstream backend {
server uber-stup.club:443;
keepalive 32;
}
{% if ssl_enabled %}
# =========================================================
# HTTP → HTTPS redirect + ACME challenge
# =========================================================
server {
listen 80;
listen [::]:80;
server_name {{ domain_name }};
# Certbot renouvellement automatique
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# =========================================================
# HTTPS Server
# =========================================================
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name {{ domain_name }};
# =========================================================
# SSL/TLS - Let's Encrypt
# =========================================================
ssl_certificate /etc/letsencrypt/live/{{ domain_name }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ domain_name }}/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/{{ domain_name }}/chain.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
{% else %}
# =========================================================
# HTTP Server (phase pré-certificat)
# =========================================================
server {
listen 80;
listen [::]:80;
server_name {{ domain_name }};
# ACME challenge accessible avant l'émission du certificat
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
{% endif %}
root {{ frontend_dir }}/dist;
index index.html;
client_max_body_size {{ nginx_max_body_size }};
location /api/ {
limit_except GET POST PUT PATCH DELETE OPTIONS { deny all; }
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Request-ID' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400 always;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
proxy_pass https://backend;
proxy_http_version 1.1;
proxy_ssl_server_name on;
proxy_ssl_name uber-stup.club;
proxy_set_header Host uber-stup.club;
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;
proxy_set_header X-Request-ID $req_id;
proxy_set_header Connection "";
proxy_connect_timeout {{ nginx_proxy_timeout }}s;
proxy_send_timeout {{ nginx_proxy_timeout }}s;
proxy_read_timeout {{ nginx_proxy_timeout }}s;
}
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
}
location /uploads/ {
alias {{ uploads_dir }}/;
expires 30d;
add_header Cache-Control "public, immutable";
}
location ^~ /uploads/images/ {
alias {{ uploads_dir }}/images/;
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
expires 30d;
add_header Cache-Control "public, immutable" always;
types {
image/jpeg jpg jpeg;
image/png png;
image/gif gif;
image/webp webp;
image/svg+xml svg;
}
default_type image/jpeg;
}
location ^~ /uploads/videos/ {
alias {{ uploads_dir }}/videos/;
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
expires 30d;
add_header Cache-Control "public, immutable" always;
types {
video/mp4 mp4;
video/webm webm;
video/ogg ogv;
}
default_type video/mp4;
}
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
{% if ssl_enabled %}
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
{% endif %}
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' data:;
connect-src 'self' https://api.tomtom.com https://uber-stup.club;
" always;
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~* (\.env|\.git|package\.json|package-lock\.json|yarn\.lock|Dockerfile|docker-compose\.yml)$ {
deny all;
access_log off;
log_not_found off;
}
}