Debian / Proxmox / PFSense
Avant propos
Nous allons mettre en place un serveur web basé sur l'infrastructure suivante :

Nous aurons donc un hyperviseur (géré par proxmox) qui recevra le trafic venant d'internet (http/https), le dispatchera sur une machine virtuelle PFSense qui elle même reroutera le trafic sur un conteneur nginx qui fera office de proxy.
L'avantage de cette infrastructure étant que l'hyperviseur n'est pas attaquable directement en http/https puisqu'il délègue directement le travail à la machine virtuelle PFSense.
Installation de debian
En premier lieu, après l'installation d'une DEBIAN 10 chez contabo, il faut nécessairement revoir le partitionnement. Pour cela il est plus simple de passer en mode rescue et d'utiliser GPARTED via VNC.
Niveau partitionnement nous allons faire cela comme suit : 60go pour le système en ext4 1,5To pour les conteneurs en ext4 (que l'on va changer en LVM après coup)
On installe lvm2 :
apt install lvm2
Installation de proxmox
On passe à l'installation de la partie hyperviseur qui sera gérée par la solution de virtualisation PROXMOX.
On s'assure que le fichier /etc/hosts (notamment le nom de domaine externe) contient bien l'ip externe du serveur et que la commande "hostname --ip-address" renvoie bien cette adresse.
127.0.0.1 localhost localhost.localdomain
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
194.163.139.21 ic1 ic1.icareapp.fr vmi599254.contaboserver.net
On ajout les sources d'installation de proxmox :
echo "deb http://download.proxmox.com/debian/pve buster pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
wget http://download.proxmox.com/debian/proxmox-ve-release-6.x.gpg -O /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
chmod +r /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
apt update && apt full-upgrade
On lance l'installation de proxmox :
apt install proxmox-ve postfix open-iscsi
Au prompt, quand il est demandé "Modify smb.conf to use WINS settings from DHCP?", répondre "non".
Concernant postfix, laissons à "local only".
On redémarre le serveur :
reboot
On retire le package "os-prober" :
apt remove os-prober
On installe le paquet ifupdown2 qui nous sera utile pour appliquer des changements de configuration réseau "à chaud" par la suite :
apt install ifupdown2
Il doit être dorénavant possible d'accéder au web panel en tapant, dans le cas du serveur ic1, https://ic1.icareapp.fr:8006.
En SSH, on va créer un groupe ayant les droits admin depuis le webpanel.
Toujours en SSH, on va créer un utilisateur (autre que root) ayant un accès admin complet au web panel :
pveum groupadd admin -comment "System Administrators
pveum aclmod / -group admin -role Administrator
Toujours en SSH, on crée un utilisateur qu'on affectera au group admin depuis le web panel :
adduser aurelien
On se rend sur le web panel dans la rubrique Datacenter > Permissions > Users. On clique sur "Add" et on renseigne les informations de notre utilisateur créé précédemment.
Tant qu'on est avec l'utilisateur root on en profite pour générer le certificat SSL du web panel. Cela se déroule dans "ic1 > System > Certificates". Ici on crée un compte ACME puis on ajoute le nom de domaine d'accès au web panel (ic1.icareapp.fr) et enfin on clique sur "order certificate now". Cela aura pour effet de générer / rattacher un certificat Let's encrypt.
Par précaution, on va désactiver l'utilisateur root pour que celui-ci ne puisse plus accéder au web panel. Pour cela on se rend dans la même rubrique que précédemment, on double clique sur "root" et on décoche "enabled".
On procède à la création d'un volume physique (PV - Physical Volume) LVM :
pvcreate /dev/sda3
On crée un premier groupe de volume (VG - Volume Group) de partition lvm sur cette même partition :
vgcreate vg01 /dev/sda3
On crée un premier volume logique dans ce groupe (LV - Logical Volume) pour y stocker nos données (conteneurs notamment) :
lvcreate -L 100G -n data vg01
Maintenant que l'on a notre premier volume logique (qui fait donc 100go mais qui pourra être étendu dans la limite de ce que possède le groupe dont il fait parti), nous allons sur le web panel de proxmox pour créer un stockage dédié aux conteneurs et vm.
Pour cela on se rend sur Datacenter > Storage et on ajoute un storage "LVM-THIN" :

On supprime le contenu "Disk image" et "Container" du storage "local" (pas obligatoire mais cela permet de bien dissocier les espaces de stockage des conteneurs/vm du reste).
Installation de PFSense
PFSense est un OS complet qui fait office de pare-feu, de routeur et/ou de VPN. Cela nous permet de ne pas utiliser iptables/ufw directement sur l'hyperviseur ou décrire des règles de routage dans l'interface de ce dernier.
L'hyperviseur restant en frontal, on va router tout le trafic de celui-ci sur la VM PFSense.
Pour cela on va éditer l'interface configurée par le provider :
cp /etc/network/interfaces /etc/network/interfaces.bak
Et on édite comme suit (ou on passe par le web panel dans "ic1 > System > Network" :
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet manual
iface eth0 inet6 static
address 2a02:c206:2059:9254:0000:0000:0000:0001
netmask 64
gateway fe80::1
accept_ra 0
autoconf 0
privext 0
auto vmbr0
iface vmbr0 inet static
address 194.163.139.21/18
gateway 194.163.128.1
dns-search invalid
dns-nameservers 161.97.189.52 161.97.189.51
up ip route replace 194.163.128.0/18 via 194.163.128.1 dev vmbr0
bridge-ports eth0
bridge-stp off
bridge-fd 0
#internet
auto vmbr1
iface vmbr1 inet static
address 10.0.0.1/30
bridge-ports none
bridge-stp off
bridge-fd 0
#WAN
auto vmbr2
iface vmbr2 inet static
address 192.168.9.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
#LAN
On recharge l'interface réseau pour appliquer les modifications (cela est rendu possible grâce au paquet ifupdown2 qu'on a installé plus haut) :
ifreload -a
On va télécharger la dernière version sur le site : PFSense. Idéalement l'iso en architecture AMD64.
cd /var/lib/vz/template/iso
wget https://nyifiles.netgate.com/mirror/downloads/pfSense-CE-2.5.1-RELEASE-amd64.iso.gz
gunzip pfSense-CE-2.5.1-RELEASE-amd64.iso.gz
On peut voir l'iso sur proxmox dans notre stockage "local" rubrique "ISO Images" :

On va maintenant pouvoir créer une VM (et non un conteneur, attention). Cela se passe sur l'interface de proxmox.







On ne démarre pas la VM !
Une fois la VM créée, on va la rattacher à deux interfaces réseau. D'une part le WAN et d'autre part le LAN car c'est elle qui fera le lien entre les deux.
Pour faire cela on se rend sur la VM via le web panel de proxmox et on se rend sur "Hardware". D'ici on clique sur "Add / Network Device" pour ajouter VMBR2 :

On peut maintenant démarrer la VM et on va pouvoir lancer l'installation de PFSense en se rendant, depuis le web panel, sur la console. Si la VM ne démarre pas à cause de KVM, il est possible de désactiver KVM dans les options de la VM (déconseillé mais sur certains VPS comme ceux de contabo cela est malheureusement obligatoire).
L'installation en console va lancer l'interface graphique de PFSense.
Premier écran on choisi "install" :

Deuxième étape il est demandé la disposition clavier (Christian ?) mais cela n'a pas d'importance puisque nous serons de nouveau basculé en qwerty par la suite (merci !).
Troisième étape partitionnement, on choisit Auto: Guided Disk Setup.
A la fin de l'installation on répond "no" et on peut redémarrer la VM :

Une fois la VM redémarrée, on arrive sur un menu de choix. On va assigner correctement les interfaces donc on prend le choix 1.
Pour le VLAN on répond non. Et ensuite pour le WAN on choisi en0 qui représente vmbr0 et pour le LAN eno1 qui représente vmbr1 :

Ensuite on prend le choix 2 pour configurer correctement les adresses ip.
Pour le WAN :
- DHCP : no
- IPv4 : 10.0.0.2
- Subnet bit count: 30
- Upstream gateway address : 10.0.0.1
- DHCP6 : no
- IPv6 : Rien
- Do you want to revert HTTP as the webConfigurator protocol? : no
Pour le LAN:
- DHCP : no
- IPv4 : 192.168.9.254
- Subnet bit count: 24
- Upstream gateway address : Rien
- DHCP6 : no
- IPv6 : Rien
- Do you want to revert HTTP as the webConfigurator protocol? : no
Une fois cette configuration effectuée, on nous indique que l'interface est accessible depuis l'adresse https://192.168.9.254 sauf que bien évidemment cela ne fonctionne pas :) car il faut être dans le LAN pour y accéder.
On peut lancer un petit test de ping. En choisissant le menu 7, on test de pinguer l'adresse locale de l'hyperviseur 10.0.0.1. Si ça ne répond pas je vous invite à revenir au tout début de ce tuto et bien relire ;).
On va maintenant se connecter à l'interface de PFSense mais de manière temporaire, histoire de pouvoir le configurer via son interface.
Pour cela on va se connecter en ssh à l'hyperviseur comme suit :
ssh [email protected] -L 8443:192.168.9.254:443
Et maintenant magie, on accède au PFSense via l'url https://localhost:8443.
La connexion initiale se fait via les informations de connexion suivantes :
- Username = admin
- Password = pfsense
Au démarrage on suit le wizard, tout bêtement (on changera le compte utilisateur plus tard).


A l'étape 4 tout doit être bon mais il faut désactiver la case Block RFC1918 Private Networks. En temps normal, le WAN correspond à Internet. Dans notre cas spécifique, ce n’est pas le cas, et on doit donc décocher cette case.
Puis il est demandé de changer le mot de passe admin. On peut recharger l'interface admin en cliquant sur "reload".
Nous allons maintenant envoyer tout le trafic sur la PFSense, configurer PFSense et déployer une application.
Ce qu'il faut savoir avant tout et bien garder à l'esprit c'est que l'hyperviseur, même si maintenant nous avons un pare-feu applicatif qui va également s'occuper de communiquer avec les VM et conteneurs, reste en première ligne. De ce fait, actuellement, l'hyperviseur peut communiquer avec les VM et conteneurs par le biais du réseau local. Si l'on tente par exemple de faire un ping sur
Sur l’hyperviseur, on va ajouter une route qui dit : Tous les paquets vers le LAN doivent passer par l’interface WAN de la PFSense. C’est cette règle qui nous permet de forcer tout le trafic à passer par la PFSense, comme si la PFSense était en frontal, avant d’arriver dans le LAN.
On crée un fichier "/root/scripts/pfsense-route.sh" avec la commande suivante :
cat > /root/scripts/pfsense-route.sh << EOF
#!/bin/sh
## IP forwarding activation
echo 1 > /proc/sys/net/ipv4/ip_forward
## Rediriger les paquets destinés au LAN pour l'interface WAN de la PFSense
ip route change 192.168.9.0/24 via 10.0.0.2 dev vmbr1
EOF
On autorise l'exécution de script :
chmod +x /root/scripts/pfsense-route.sh
On ajoute l'appel de ce script dans le fichier "/etc/network/interface" dans l'interface VMBR2 :
[...]
auto vmbr2
iface vmbr2 inet static
address 192.168.9.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
post-up /root/scripts/pfsense-route.sh
#LAN
On peut exécuter le script mais attention, nous n'aurons plus accès à notre PFSense via le tunnel SSH et donc plus d'accès à l'interface de ce dernier pour le configurer comme il faut.
On ping l'ip de la VM PFSense depuis l'hyperviseur :
ping 192.168.9.254
On devrait avoir une réponse.
Ensuite on exécute le script :
./root/scripts/pfsense-route.sh
Normalement, si on ping la VM PFSense, celle-ci ne répond plus. Si c'est le cas c'est que ça marche bien :).
On va ajouter une première règle de parefeu pour réautoriser le ping mais qui passera donc cette fois-ci par PFSense. Pour cela on se rend sur l'interface de PFSense dans le menu "Firewall > Rules". On clique sur le bouton "Add to the top" et on rempli comme suit :
- Action : Pass
- Interface : WAN
- Address Family : IPv4
- Protocol : ICMP
- Source : Any
- Destination : Any
On sauvegarde et on applique.
Configuration du port forwarding et règles iptables
On va maintenant mettre en place le port forwading (étape assez longue).
On va commencer par mettre en place des variables qui seront utilisés dans un script que l'on va écrire pour se simplifier la vie.
export PUBIP=194.163.139.21
export SSHPORT=22153
Puis on crée la première partie du script :
cat > /root/scripts/iptables.sh << EOF
#!/bin/sh
# ---------
# VARIABLES
# ---------
## Proxmox bridge holding Public IP
PrxPubVBR="vmbr0"
## Proxmox bridge on VmWanNET (PFSense WAN side)
PrxVmWanVBR="vmbr1"
## Proxmox bridge on PrivNET (PFSense LAN side)
PrxVmPrivVBR="vmbr2"
## Network/Mask of VmWanNET
VmWanNET="10.0.0.0/30"
## Network/Mmask of PrivNET
PrivNET="192.168.9.0/24"
## Network/Mmask of VpnNET
VpnNET="10.2.2.0/24"
## Public IP => Your own public IP address
PublicIP="${PUBIP}"
## Proxmox IP on the same network than PFSense WAN (VmWanNET)
ProxVmWanIP="10.0.0.1"
## Proxmox IP on the same network than VMs
ProxVmPrivIP="192.168.9.1"
## PFSense IP used by the firewall (inside VM)
PfsVmWanIP="10.0.0.2"
EOF
Inoffensif jusque là puisque nous ne créons que des variables.
A partir d'ici attention car nous allons écrire les lignes du script qui auront pour effet de bloquer tout le traffic, tous les paquets seront ainsi droppés.
Dans ce script, au delà de tout bloquer, on crée des chaînes qui vont capturer toutes les nouvelles connexions TCP et UDP, respectivement.
Et enfin, on ajoute des règles un peu de base :
- On accepte les connexions localhost
- On ne stoppe pas les connexions existantes. Comme notre connexion SSH par exemple
- On autorise les pings. Parce que c’est pratique pour débugger
cat >> /root/scripts/iptables.sh << EOF
# ---------------------
# CLEAN ALL & DROP IPV6
# ---------------------
### Delete all existing rules.
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X
### This policy does not handle IPv6 traffic except to drop it.
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
ip6tables -P FORWARD DROP
# --------------
# DEFAULT POLICY
# --------------
### Block ALL !
iptables -P OUTPUT DROP
iptables -P INPUT DROP
iptables -P FORWARD DROP
# ------
# CHAINS
# ------
### Creating chains
iptables -N TCP
iptables -N UDP
# UDP = ACCEPT / SEND TO THIS CHAIN
iptables -A INPUT -p udp -m conntrack --ctstate **NEW** -j UDP
# TCP = ACCEPT / SEND TO THIS CHAIN
iptables -A INPUT -p tcp --syn -m conntrack --ctstate **NEW** -j TCP
# ------------
# GLOBAL RULES
# ------------
# Allow localhost
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# Don't break the current/active connections
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Allow Ping - Comment this to return timeout to ping request
iptables -A INPUT -p icmp --icmp-type 8 -m conntrack --ctstate **NEW** -j ACCEPT
EOF
On va ajouter deux règles pour l'interface VMBR0 (qui relie le serveur à internet) sur les paquets entrants.
- On autorise les nouvelles connexions sur le port SSH qui passent par vmbr0 et dont la destination est notre IP publique
- On autorise les nouvelles connexions sur le port 8006 qui passent par vmbr0 et dont la destination est notre IP publique
cat >> /root/scripts/iptables.sh << EOF
# --------------------
# RULES FOR PrxPubVBR
# --------------------
### INPUT RULES
# ---------------
# Allow SSH server
iptables -A TCP -i \$PrxPubVBR -d \$PublicIP -p tcp --dport ${SSHPORT} -j ACCEPT
# Allow Proxmox WebUI
iptables -A TCP -i \$PrxPubVBR -d \$PublicIP -p tcp --dport 8006 -j ACCEPT
EOF
Plus tard, on pourra même supprimer ces règles. À la place, on se connectera au VPN, et à partir de là on aura accès au SSH ou au Proxmox.
Ensuite, on ajoute des règles pour les paquets sortants.
On autorise les pings, les paquets http et https, les ports pour les DNS, SSH et web panel de proxmox.
cat >> /root/scripts/iptables.sh << EOF
### OUTPUT RULES
# ---------------
# Allow ping out
iptables -A OUTPUT -p icmp -j ACCEPT
### Proxmox Host as CLIENT
# Allow HTTP/HTTPS
iptables -A OUTPUT -o \$PrxPubVBR -s \$PublicIP -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -o \$PrxPubVBR -s \$PublicIP -p tcp --dport 443 -j ACCEPT
# Allow DNS
iptables -A OUTPUT -o \$PrxPubVBR -s \$PublicIP -p udp --dport 53 -j ACCEPT
### Proxmox Host as SERVER
# Allow SSH
iptables -A OUTPUT -o \$PrxPubVBR -s \$PublicIP -p tcp --sport ${SSHPORT} -j ACCEPT
# Allow PROXMOX WebUI
iptables -A OUTPUT -o \$PrxPubVBR -s \$PublicIP -p tcp --sport 8006 -j ACCEPT
EOF
Il ne nous reste plus qu'à router tout le traffic vers PFSense.
cat >> /root/scripts/iptables.sh << EOF
### FORWARD RULES
# ----------------
### Redirect (NAT) traffic from internet
# All tcp to PFSense WAN except ${SSHPORT}, 8006
iptables -A PREROUTING -t nat -i \$PrxPubVBR -p tcp --match multiport ! --dports ${SSHPORT},8006 -j DNAT --to \$PfsVmWanIP
# All udp to PFSense WAN
iptables -A PREROUTING -t nat -i \$PrxPubVBR -p udp -j DNAT --to \$PfsVmWanIP
# Allow request forwarding to PFSense WAN interface
iptables -A FORWARD -i \$PrxPubVBR -d \$PfsVmWanIP -o \$PrxVmWanVBR -p tcp -j ACCEPT
iptables -A FORWARD -i \$PrxPubVBR -d \$PfsVmWanIP -o \$PrxVmWanVBR -p udp -j ACCEPT
# Allow request forwarding from LAN
iptables -A FORWARD -i \$PrxVmWanVBR -s \$VmWanNET -j ACCEPT
EOF
Et autoriser le port forwarding :
cat >> /root/scripts/iptables.sh << EOF
### MASQUERADE MANDATORY
# Allow WAN network (PFSense) to use vmbr0 public adress to go out
iptables -t nat -A POSTROUTING -s \$VmWanNET -o \$PrxPubVBR -j MASQUERADE
EOF
Maintenant que notre script est prêt, on peut le lancer et nous aurons un serveur prêt à l'emploi :).