Restructuration du dépot
This commit is contained in:
2
applicatif/zone_ctf/README.md
Normal file
2
applicatif/zone_ctf/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# Présentation de la zone CTF
|
||||
Vous trouverez ici toute la documentation relative au fonctionnement et à la configuration de la zone CTF. Cela comprend le reverse proxy CTF, l'environnement Web, l'environnement Système et CTFd.
|
||||
250
applicatif/zone_ctf/environnement_systeme.md
Normal file
250
applicatif/zone_ctf/environnement_systeme.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Environnement Système CTF
|
||||
|
||||
Il faut impérativement une VM pour que Docker soit fluide
|
||||
|
||||
## Installation de Docker
|
||||
```
|
||||
apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
|
||||
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
|
||||
apt-get update
|
||||
apt-get install docker-ce
|
||||
```
|
||||
|
||||
## Mise en place de l'environement système
|
||||
|
||||
La restauration des challenges actuels est expliquée à la fin.
|
||||
|
||||
### Procédure pour la création d'un nouveau challenge
|
||||
|
||||
Placer les Dockerfile est la source du challenge dans /home/systeme/SystemeChall/<challenge>
|
||||
Le Dockerfile doit idéalement être fait à partir de debmod, créer un utilisateur systemeXX et contenir
|
||||
```
|
||||
USER systemeXX
|
||||
WORKDIR /home/systemeXX
|
||||
```
|
||||
|
||||
### Installation et création de l'utilisateur administrateur non root
|
||||
```
|
||||
apt-get install python3-pip
|
||||
pip3 install --upgrade docker
|
||||
pip3 install --upgrade argcomplete
|
||||
adduser systeme
|
||||
adduser systeme sudo
|
||||
adduser systeme docker
|
||||
su systeme
|
||||
```
|
||||
#### /usr/local/bin/dockersh
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# PYTHON_ARGCOMPLETE_OK
|
||||
import os
|
||||
os.environ['TERM'] = 'xterm'
|
||||
import argparse
|
||||
from configparser import ConfigParser, ExtendedInterpolation
|
||||
import docker
|
||||
import random
|
||||
import string
|
||||
import sys
|
||||
from pwd import getpwnam
|
||||
|
||||
prog = 'dockersh'
|
||||
config_file = "/etc/dockertempsh.ini"
|
||||
|
||||
import os
|
||||
user = os.getlogin()
|
||||
|
||||
host = os.uname()[1]
|
||||
|
||||
cli = docker.APIClient()
|
||||
|
||||
def containers(image_filter='', container_filter='', sort_by='Created', all=True):
|
||||
cs = cli.containers(all=all, filters={'label': "user="+user})
|
||||
cs.sort(key=lambda c: c[sort_by])
|
||||
cs = [c for c in cs if str(c['Image']+':latest').startswith(image_filter)]
|
||||
cs = [c for c in cs if c['Names'][0][1:].startswith(container_filter)]
|
||||
return cs
|
||||
|
||||
def random_string(length):
|
||||
def random_char():
|
||||
return random.choice(string.ascii_uppercase + string.digits)
|
||||
return ''.join(random_char() for _ in range(length))
|
||||
|
||||
def strip(s, suffix=''):
|
||||
for c in ['/', ':', '.', ' ']:
|
||||
s = s.replace(c, '')
|
||||
if s.endswith(suffix):
|
||||
s = s[:len(s)-len(suffix)]
|
||||
return s
|
||||
|
||||
def image_split(s):
|
||||
sp = s.split(':')
|
||||
if len(sp) == 1:
|
||||
return sp[0], 'latest'
|
||||
else:
|
||||
return sp[0], sp[1]
|
||||
|
||||
#Chargement du fichier de configuration
|
||||
config_envir = {
|
||||
"USER": user,
|
||||
"HOME": os.environ['HOME']
|
||||
}
|
||||
cfg = ConfigParser(config_envir, interpolation=ExtendedInterpolation())
|
||||
cfg.read(config_file)
|
||||
|
||||
ini = cfg[user] if cfg.has_section(user) else cfg['DEFAULT']
|
||||
|
||||
#Spécification particulière
|
||||
post_cmd = ""
|
||||
name = ""
|
||||
image = ""
|
||||
home = ini['homedir']
|
||||
suffix = ini['suffix']
|
||||
|
||||
#Vérification des spécifications
|
||||
name_passed = (name != "")
|
||||
image_passed = (image != "")
|
||||
|
||||
#Génération du container temporaire
|
||||
|
||||
#Création du nom random
|
||||
if not image_passed:
|
||||
image = ini['image']
|
||||
image_base, image_tag = image_split(image)
|
||||
image = image_base + ':' + image_tag
|
||||
name = strip(image) + '_tmp' + random_string(4)
|
||||
|
||||
full_name = name + suffix
|
||||
|
||||
#Création
|
||||
if len(containers(container_filter=name)) == 0:
|
||||
volumes = []
|
||||
if "volumes" in ini:
|
||||
volumes = volumes + ini["volumes"].split(",")
|
||||
volumes = [v.split(":") for v in volumes]
|
||||
binds = {v[0].strip():{"bind":v[1].strip(),"mode":v[2].strip()} for v in volumes}
|
||||
volumes = [v[1] for v in volumes]
|
||||
|
||||
host_config = cli.create_host_config(
|
||||
binds=binds,
|
||||
restart_policy={'Name' : 'unless-stopped'},
|
||||
cap_add='SYS_PTRACE', #GDB
|
||||
security_opt=['seccomp:unconfined']) #GDB
|
||||
|
||||
userpwd = getpwnam(user)
|
||||
cli.create_container(image,
|
||||
stdin_open=True,
|
||||
tty=True,
|
||||
name=full_name,
|
||||
hostname='systemekrkn',
|
||||
labels={'group': prog, 'user': user},
|
||||
volumes=volumes,
|
||||
working_dir=home,
|
||||
environment={
|
||||
"HOST_USER_ID": userpwd.pw_uid,
|
||||
"HOST_USER_GID": userpwd.pw_gid,
|
||||
"HOST_USER_NAME": user
|
||||
},
|
||||
host_config=host_config
|
||||
)
|
||||
|
||||
#Lancement et attach
|
||||
cli.start(full_name)
|
||||
os.popen('docker exec '+full_name + ' echo Initialization finished.').read().split(":")[-1]
|
||||
|
||||
user_bash = "/bin/bash" #Path par défaut
|
||||
cmd = post_cmd if post_cmd else user_bash #Donne la posibilité de spécifié le path dans le .ini
|
||||
os.system('docker exec -u '+user+" " + "-it" +' '+ full_name + ' ' + cmd)
|
||||
|
||||
#Arrêt à la fin
|
||||
cli.remove_container(full_name, v=True, force=True)
|
||||
cli.close()
|
||||
```
|
||||
|
||||
```
|
||||
chmod +x /usr/local/bin/dockersh
|
||||
```
|
||||
### Création d'une image docker de base
|
||||
|
||||
#### Dockerfile
|
||||
```
|
||||
FROM debian:latest
|
||||
RUN dpkg --add-architecture i386
|
||||
RUN apt update && apt install -y gdb elfutils binutils python-minimal perl zip pwgen nano gcc
|
||||
```
|
||||
Création de l'image de base debmod
|
||||
```
|
||||
docker built -t debmod .
|
||||
```
|
||||
### Script pour la création des images Docker à partir des Dockerfile
|
||||
|
||||
Usage : ./createImg <systemeXX> <dockerfile>
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
if [ "$#" -lt "2" ]
|
||||
then
|
||||
echo "Usage : ./createImg <systemeXX> <dockerfile>"
|
||||
else
|
||||
if [ -f "$2" ];then
|
||||
docker build -t $1 $2
|
||||
else
|
||||
echo "Usage : ./createImg <systemeXX> <dockerfile>"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
### Script pour la création d'un utilisateur et son ajout à DockerTemp
|
||||
|
||||
Usage ./deployEnv <systemeXX>
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
if [ "$#" -eq "0" ]
|
||||
then
|
||||
echo "Usage : ./deployEnv <systemeXX>"
|
||||
else
|
||||
if grep -q "$1" /etc/dockertempsh.ini
|
||||
then
|
||||
echo "Utilisateur déjà crée dans /etc/dockertempsh.ini ECHEC"
|
||||
exit 1
|
||||
else
|
||||
useradd -m -p $1 -s /usr/local/bin/dockersh $1
|
||||
echo "$1:$1" | chpasswd
|
||||
adduser $1 docker
|
||||
echo -e "[$1]
|
||||
image = $1
|
||||
suffix = _$1
|
||||
homedir = /home/$1
|
||||
volumes = /globalinfo:/globalinfo:ro" >> /etc/dockertempsh.ini
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
Une fois le programme mis au bon endroit et les deux scripts exécutés avec succès tout est prêt. Pour personnaliser le message d'accueil, il faut modifier le /etc/motd de la VM et non celui des containers Docker.
|
||||
|
||||
## Restauration des challenges déjà existants
|
||||
Voilà la correspondance utilisateur / challenge
|
||||
```
|
||||
systeme1 -> easyshell
|
||||
systeme2 -> pwn_my_home
|
||||
systeme3 -> overflow
|
||||
systeme4 -> shellcode1
|
||||
systeme5 -> shellcode3
|
||||
systeme6 -> shellcode3
|
||||
systeme7 -> history
|
||||
```
|
||||
|
||||
Extraire l'archive des challenge dans /home/systeme/SystemeChall/
|
||||
|
||||
### Script qui utilisera les deux autres pour tout déployer
|
||||
```
|
||||
#!/bin/bash
|
||||
declare -a path=(easyshell pwn_my_home overflow shellcode1 shellcode2 shellcode3 history)
|
||||
for i in `seq 0 6`;
|
||||
do
|
||||
./createImg.sh systeme$(($i+1)) "/home/systeme/SystemeChall/${path[${i}]}"
|
||||
./deployEnv.sh systeme$(($i+1))
|
||||
done
|
||||
```
|
||||
63
applicatif/zone_ctf/environnement_web.md
Normal file
63
applicatif/zone_ctf/environnement_web.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Environnement Web CTF
|
||||
|
||||
Il faut impérativement une VM pour que Docker soit fluide
|
||||
|
||||
## Installation de Docker et Docker-Compose
|
||||
```
|
||||
apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
|
||||
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
|
||||
apt-get update
|
||||
apt-get install docker-ce
|
||||
curl -L "https://github.com/docker/compose/releases/download/1.25.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
chmod +x /usr/local/bin/docker-compose
|
||||
```
|
||||
|
||||
## Mise en place de l'environnement Web
|
||||
|
||||
L'archive contient les sources docker des 11 environnements ainsi qu'un fichier docker-compose qui mettra en place tous les environnements.
|
||||
|
||||
```
|
||||
Easy HTML : Port 8081
|
||||
Fais moi un cookie : Port 8082
|
||||
Header manquant : Port 8083
|
||||
Easy admin : Port 8084
|
||||
Easy LFI & Harder LFI : Port 8085
|
||||
SQL Injection : Port 8086
|
||||
strcmp : Port 8087
|
||||
Easy NoSQL & Standard & Harder NoSQL : Port 8088
|
||||
Blind SQL : Port 8089
|
||||
XML : Port 8090
|
||||
Vole mon cookie si tu peux : Port 8091
|
||||
```
|
||||
|
||||
Un reverse proxy en local redirigera vers le bon port en fonction du numéro du challenge.
|
||||
|
||||
### Création d'un utilisateur non-root
|
||||
```
|
||||
adduser WebChalls --force-badname
|
||||
adduser WebChalls sudo
|
||||
su WebChalls
|
||||
```
|
||||
|
||||
Pour la mise en place il suffit de placer le contenu de l'archive dans /home/WebChalls/WebChalls
|
||||
|
||||
### Création d'un service pour le démarrage automatique
|
||||
#### /etc/systemd/system/webchall.service
|
||||
```
|
||||
[Unit]
|
||||
Description=WebChall
|
||||
Requires=webchall.service
|
||||
After=webchall.service
|
||||
[Service]
|
||||
Restart=always
|
||||
ExecStart=/usr/local/bin/docker-compose -f /home/WebChalls/WebChalls/docker-compose.yml up
|
||||
ExecStop=/usr/local/bin/docker-compose -f /home/WebChalls/WebChalls/docker-compose.yml down
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
Il suffit maintenant de l'activer
|
||||
```
|
||||
systemctl enable webchall
|
||||
systemctl start webchall
|
||||
```
|
||||
41
applicatif/zone_ctf/nginx_ctf.md
Normal file
41
applicatif/zone_ctf/nginx_ctf.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Reverse proxy NGINX sur le réseau CTF
|
||||
|
||||
## Spécification du container
|
||||
Ce service n'est pas redondé car non vital, son IP est 10.0.2.5 sur le réseau CTF.
|
||||
|
||||
## Objectif
|
||||
Il doit rediriger les requêtes arrivant de HAProxy vers le bon container en fonction de l'hostname. Pour cela nous allons utiliser des serveurs web HTTP Nginx.
|
||||
|
||||
## Installation de nginx et persistance,
|
||||
```
|
||||
apt-get update
|
||||
apt-get install -y nginx
|
||||
systemctl enable nginx.service
|
||||
```
|
||||
|
||||
## Mise en place d'un serveur faisant office de reverse proxy
|
||||
|
||||
On ajoute un serveur web dans /etc/nginx/sites-available avec un nom décrivant bien le service derrière ce serveur.
|
||||
|
||||
Voilà la template du serveur web,
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
server_name address.fr;
|
||||
location / {
|
||||
proxy_pass http://ip_reseau_ctf/;
|
||||
proxy_set_header Host $http_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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
On active ce serveur web,
|
||||
```
|
||||
ln -s /etc/nginx/sites-available/<nom_serveur> /etc/nginx/sites-enabled
|
||||
systemctl restart nginx
|
||||
```
|
||||
|
||||
La procédure est tout le temps la méthode générale pour ajouter un serveur à Nginx. Elle est décrite ici. Cependant, dans certians cas, il peut être nécessaire d'enlever un ou plusieurs proxy\_set\_header dans la configuration du serveur Nginx.
|
||||
2
applicatif/zone_krkn/README.md
Normal file
2
applicatif/zone_krkn/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# Présentation de la zone KRKN
|
||||
Vous trouverez ici toute la documentation relative au fonctionnement et à la configuration de la zone KRKN. Cela comprend tous les containers des services publics (Nextcloud, Gitea...) et les services internes nécessaires au fonctionnement des services publics comme l'annuaire LDAP.
|
||||
570
applicatif/zone_krkn/ldap.md
Normal file
570
applicatif/zone_krkn/ldap.md
Normal file
@@ -0,0 +1,570 @@
|
||||
# LDAP
|
||||
Nous allons ici mettre en place le serveur LDAP qui sera répliqué sur les deux nodes. Tout les services utiliseront LDAP pour l'authentification des utilisateurs.
|
||||
A noté que pour des questions pratique nous n'allons pas utilisé Fusion Directory, il faudra donc créer un schéma pour chaque service et modifier les utilisateur avec ldapadd et ldapmodify.
|
||||
Pour la sécurisation de LDAP nous allons utiliser LDAP avec STARTTLS.
|
||||
## Installation slapd
|
||||
On commence par installer le serveur ldap.
|
||||
```
|
||||
apt-get update
|
||||
apt-get install slapd ldap-utils
|
||||
```
|
||||
|
||||
## Configuration de sladp
|
||||
On commence par reconfigurer le packet
|
||||
```
|
||||
dpkg-reconfigure slapd
|
||||
```
|
||||
Il faut répondre de la manière suivante
|
||||
```
|
||||
Omit OpenLDAP server configuration? No
|
||||
DNS domain name: krhacken.org
|
||||
Organization name? Kr[HACK]en
|
||||
Administrator password: PASSWORD
|
||||
Confirm password: PASSWORD
|
||||
Database backend to use: MDB
|
||||
Do you want the database to be removed when slapd is purged? YES
|
||||
Allow LDAPv2 protocol? No
|
||||
```
|
||||
### /etc/ldap/ldap.conf
|
||||
```
|
||||
BASE dc=krhacken,dc=org
|
||||
URI ldap://IP.LDAP/
|
||||
```
|
||||
|
||||
## Centralisation des fichiers de configuration
|
||||
Nous allons créer un répertoire /root/ldap/conf qui va centraliser tous nos fichiers de configuration
|
||||
```
|
||||
mkdir -p /root/ldap/conf/
|
||||
```
|
||||
|
||||
## Mise en place SSL
|
||||
```
|
||||
apt-get install gnutls-bin ssl-cert
|
||||
mkdir /etc/ssl/templates
|
||||
```
|
||||
### /etc/ssl/templates/ca_server.conf
|
||||
```
|
||||
cn = LDAP Server CA
|
||||
ca
|
||||
cert_signing_key
|
||||
```
|
||||
### /etc/ssl/templates/ldap_server.conf
|
||||
```
|
||||
organization = "krhacken"
|
||||
cn = ldap.krhacken.org
|
||||
tls_www_server
|
||||
encryption_key
|
||||
signing_key
|
||||
expiration_days = 3652
|
||||
```
|
||||
### CA clé et certificat
|
||||
```
|
||||
certtool -p --outfile /etc/ssl/private/ca_server.key
|
||||
certtool -s --load-privkey /etc/ssl/private/ca_server.key --template /etc/ssl/templates/ca_server.conf --outfile /etc/ssl/certs/ca_server.pem
|
||||
```
|
||||
### LDAP clé et certificat
|
||||
```
|
||||
certtool -p --sec-param high --outfile /etc/ssl/private/ldap_server.key
|
||||
certtool -c --load-privkey /etc/ssl/private/ldap_server.key --load-ca-certificate /etc/ssl/certs/ca_server.pem --load-ca-privkey /etc/ssl/private/ca_server.key --template /etc/ssl/templates/ldap_server.conf --outfile /etc/ssl/certs/ldap_server.pem
|
||||
```
|
||||
Nous avons maintenant créer tout les certificats nécessaire pour pouvoir chiffrer LDAP avec STARTTLS.
|
||||
|
||||
## Chiffrement par STARTTLS
|
||||
Nous avons choisi STARTTLS au lieu de LDAPS car il est plus sûr pour notre usage.
|
||||
### Gestion des permissions
|
||||
```
|
||||
usermod -aG ssl-cert openldap
|
||||
chown :ssl-cert /etc/ssl/private/ldap_server.key
|
||||
chmod 640 /etc/ssl/private/ldap_server.key
|
||||
```
|
||||
|
||||
## Ajout des certificat à OpenLDAP
|
||||
### /root/ldap/conf/addcerts.ldif
|
||||
```
|
||||
dn: cn=config
|
||||
changetype: modify
|
||||
replace: olcTLSCACertificateFile
|
||||
olcTLSCACertificateFile: /etc/ssl/certs/ca_server.pem
|
||||
|
||||
dn: cn=config
|
||||
changetype: modify
|
||||
replace: olcTLSCertificateFile
|
||||
olcTLSCertificateFile: /etc/ssl/certs/ldap_server.pem
|
||||
|
||||
dn: cn=config
|
||||
changetype: modify
|
||||
replace: olcTLSCertificateKeyFile
|
||||
olcTLSCertificateKeyFile: /etc/ssl/private/ldap_server.key
|
||||
```
|
||||
Application des modification et redémarrage de slapd
|
||||
```
|
||||
ldapmodify -H ldapi:// -Y EXTERNAL -f addcerts.ldif
|
||||
service slapd force-reload
|
||||
```
|
||||
## Sur le serveur
|
||||
```
|
||||
cp /etc/ssl/certs/ca_server.pem /etc/ldap/ca_certs.pem
|
||||
```
|
||||
Il faut ensuite ajuster la configuration en modifiant la ligne suivante
|
||||
### /etc/ldap/ldap.conf
|
||||
```
|
||||
...
|
||||
TLS_CACERT /etc/ldap/ca_certs.pem
|
||||
...
|
||||
```
|
||||
### Vérification
|
||||
La commande
|
||||
```
|
||||
ldapsearch -xLLL -H ldap://localhost -D cn=viewer,ou=system,dc=krhacken,dc=org -w passview -b "dc=krhacken,dc=org"
|
||||
```
|
||||
doit retourner une erreur, si on ajout -ZZ à la fin ça doit fonctionner
|
||||
|
||||
|
||||
## Configuration des futurs client LDAP
|
||||
Sur tout les futurs client LDAP il faudra activer la connexion SSL.
|
||||
Il faut commencer par copier le certificat de la CA (ca_server.pem)
|
||||
```
|
||||
cat ca_server.pem | tee -a /etc/ldap/ca_certs.pem
|
||||
```
|
||||
Il faut ensuite modifier la configuration en modifiant la ligne suivante
|
||||
### /etc/ldap/ldap.conf
|
||||
```
|
||||
...
|
||||
TLS_CACERT /etc/ldap/ca_certs.pem
|
||||
...
|
||||
```
|
||||
|
||||
## Droits d'accès pour la configuration
|
||||
### /root/ldap/conf/acces-conf-admin.ldif
|
||||
```
|
||||
dn: olcDatabase={0}config,cn=config
|
||||
changeType: modify
|
||||
add: olcAccess
|
||||
olcAccess: to * by dn.exact=cn=admin,dc=krhacken,dc=org manage by * break
|
||||
```
|
||||
Puis on applique le .ldif
|
||||
```
|
||||
ldapmodify -Y external -H ldapi:/// -f acces-conf-admin.ldif
|
||||
```
|
||||
|
||||
# Les overlays
|
||||
Les overlays sont des fonctionnalités supplémentaires. Si dessous l'ensemble des overlays que nous allons utiliser ainsi que leur utilité.
|
||||
|
||||
## MemberOf
|
||||
L’overlay memberof permet de savoir dans quels groupes se trouve un utilisateur en une seule requête au lieu de deux.
|
||||
### /root/ldap/conf/memberof_act.ldif
|
||||
```
|
||||
dn: cn=module,cn=config
|
||||
cn:module
|
||||
objectclass: olcModuleList
|
||||
objectclass: top
|
||||
olcmoduleload: memberof.la
|
||||
olcmodulepath: /usr/lib/ldap
|
||||
```
|
||||
### /root/ldap/conf/memberof_conf.ldif
|
||||
```
|
||||
dn: olcOverlay=memberof,olcDatabase={1}mdb,cn=config
|
||||
changetype: add
|
||||
objectClass: olcMemberOf
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcConfig
|
||||
objectClass: top
|
||||
olcOverlay: memberof
|
||||
olcMemberOfDangling: ignore
|
||||
olcMemberOfRefInt: TRUE
|
||||
olcMemberOfGroupOC: groupOfNames
|
||||
olcMemberOfMemberAD: member
|
||||
olcMemberOfMemberOfAD: memberOf
|
||||
```
|
||||
On applique les modifications
|
||||
```
|
||||
ldapadd -Y EXTERNAL -H ldapi:/// -f memberof_act.ldif
|
||||
ldapadd -Y EXTERNAL -H ldapi:/// -f memberof_conf.ldif
|
||||
```
|
||||
Vérification
|
||||
```
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcModuleList"
|
||||
```
|
||||
|
||||
## refint
|
||||
L'overlay permet de s’assurer de la cohérence de l’annuaire lors de suppression d’entrées.
|
||||
### /root/ldap/conf/refint_act.ldif
|
||||
```
|
||||
dn: cn=module,cn=config
|
||||
cn: module
|
||||
objectclass: olcModuleList
|
||||
objectclass: top
|
||||
olcmoduleload: refint.la
|
||||
olcmodulepath: /usr/lib/ldap
|
||||
```
|
||||
### /root/ldap/conf/refint_conf.ldif
|
||||
```
|
||||
dn: olcOverlay=refint,olcDatabase={1}mdb,cn=config
|
||||
objectClass: olcConfig
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcRefintConfig
|
||||
objectClass: top
|
||||
olcOverlay: refint
|
||||
olcRefintAttribute: memberof member manager owner
|
||||
olcRefintNothing: cn=admin,dc=krhacken,dc=org
|
||||
```
|
||||
On applique les modifications
|
||||
```
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f refint_act.ldif
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f refint_conf.ldif
|
||||
```
|
||||
Vérifications
|
||||
```
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcModuleList"
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcRefintConfig"
|
||||
```
|
||||
|
||||
## Audit Log
|
||||
Cet overlay sert à auditer chaque modification au sein de l’annuaire. Dans notre cas, cela sera inscrit dans le fichier : /var/log/openldap/audit.ldif
|
||||
|
||||
### /root/ldap/conf/auditlog_act.ldif
|
||||
```
|
||||
dn: cn=module,cn=config
|
||||
cn: module
|
||||
objectclass: olcModuleList
|
||||
objectclass: top
|
||||
olcModuleLoad: auditlog.la
|
||||
olcmodulepath: /usr/lib/ldap
|
||||
```
|
||||
### /root/ldap/conf/auditlog_conf.ldif
|
||||
```
|
||||
dn: olcOverlay=auditlog,olcDatabase={1}mdb,cn=config
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcAuditLogConfig
|
||||
olcOverlay: auditlog
|
||||
olcAuditlogFile: /var/log/openldap/auditlog.ldif
|
||||
```
|
||||
On applique les modifications
|
||||
```
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f auditlog_act.ldif
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f auditlog_conf.ldif
|
||||
```
|
||||
On créer le fichier
|
||||
```
|
||||
mkdir /var/log/openldap
|
||||
chmod 755 /var/log/openldap
|
||||
chown openldap:openldap /var/log/openldap
|
||||
touch /var/log/openldap/auditlog.ldif
|
||||
chmod 755 /var/log/openldap/auditlog.ldif
|
||||
chown openldap:openldap /var/log/openldap/auditlog.ldif
|
||||
```
|
||||
Vérifications
|
||||
```
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcModuleList"
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcAuditLogConfig"
|
||||
```
|
||||
|
||||
## Unique
|
||||
Cet overlay permet de nous assurer l’unicité des attributs que l’on spécifie.
|
||||
### /root/ldap/conf/unique_act.ldif
|
||||
```
|
||||
dn: cn=module,cn=config
|
||||
cn: module
|
||||
objectclass: olcModuleList
|
||||
objectclass: top
|
||||
olcModuleLoad: unique.la
|
||||
olcmodulepath: /usr/lib/ldap
|
||||
```
|
||||
### /root/ldap/conf/unique_conf.ldif
|
||||
```
|
||||
dn: olcOverlay=unique,olcDatabase={1}mdb,cn=config
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcUniqueConfig
|
||||
olcOverlay: unique
|
||||
olcUniqueUri: ldap:///ou=people,dc=krhacken,dc=org?uid?sub
|
||||
olcUniqueUri: ldap:///ou=people,dc=krhacken,dc=org?mail?sub
|
||||
olcUniqueUri: ldap:///ou=people,dc=krhacken,dc=org?uidNumber?sub
|
||||
olcUniqueUri: ldap:///ou=groups,dc=krhacken,dc=org?cn?sub
|
||||
```
|
||||
Nous demandons ici à ce que les attributs UI, mail et uidNumber dans l’ou people soient uniques. Et que l’attribut cn dans l’ou groups soit lui aussi unique.
|
||||
On applique les modifications,
|
||||
```
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f unique_act.ldif
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f unique_conf.ldif
|
||||
```
|
||||
Vérifications,
|
||||
```
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcModuleList"
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcUniqueConfig"
|
||||
```
|
||||
|
||||
## Ppolicy
|
||||
Cet overlay va nous permettre de spécifier une politique de mot de passe.
|
||||
|
||||
On va ajouter son schéma dans l’annuaire
|
||||
```
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/ppolicy.ldif
|
||||
```
|
||||
Dans la branche cn=schema, on doit voir le schéma ppolicy,
|
||||
```
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=schema,cn=config" cn
|
||||
```
|
||||
### /root/ldap/conf/ppolicy_act.ldif
|
||||
```
|
||||
dn: cn=module,cn=config
|
||||
cn: module
|
||||
objectclass: olcModuleList
|
||||
objectclass: top
|
||||
olcModuleLoad: ppolicy.la
|
||||
olcmodulepath: /usr/lib/ldap
|
||||
```
|
||||
### /root/ldap/conf/ppolicy_conf.ldif
|
||||
```
|
||||
dn: olcOverlay=ppolicy,olcDatabase={1}mdb,cn=config
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcPpolicyConfig
|
||||
olcOverlay: ppolicy
|
||||
olcPPolicyDefault: cn=ppolicy,dc=krhacken,dc=org
|
||||
olcPPolicyHashCleartext: TRUE
|
||||
olcPPolicyUseLockout: FALSE
|
||||
```
|
||||
Explication,
|
||||
- olcPPolicyDefault : Indique le DN de configuration utilisé
|
||||
- olcPPolicyHashCleartext : Indique si les mots de passe doivent être cryptés.
|
||||
On applique les modifications,
|
||||
```
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f ppolicy_act.ldif
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f ppolicy_conf.ldif
|
||||
```
|
||||
On va maintenant créer la politique par défaut.
|
||||
|
||||
### /root/ldap/conf/ppolicy_def.ldif
|
||||
```
|
||||
dn: cn=ppolicy,dc=krhacken,dc=org
|
||||
objectClass: top
|
||||
objectClass: device
|
||||
objectClass: pwdPolicy
|
||||
cn: ppolicy
|
||||
pwdAllowUserChange: TRUE
|
||||
pwdAttribute: userPassword
|
||||
pwdCheckQuality: 1
|
||||
pwdExpireWarning: 0
|
||||
pwdFailureCountInterval: 30
|
||||
pwdGraceAuthNLimit: 5
|
||||
pwdInHistory: 5
|
||||
pwdLockout: TRUE
|
||||
pwdLockoutDuration: 60
|
||||
pwdMaxAge: 0
|
||||
pwdMaxFailure: 5
|
||||
pwdMinAge: 0
|
||||
pwdMinLength: 5
|
||||
pwdMustChange: FALSE
|
||||
pwdSafeModify: FALSE
|
||||
```
|
||||
La signification des attributs est :
|
||||
- pwdAllowUserChange : indique si l’utilisateur peut changer son mot de passe.
|
||||
- pwdCheckQuality : indique si OpenLDAP renvoie une erreur si le mot de passe n’est pas conforme
|
||||
- pwdExpireWarning : avertissement d’expiration.
|
||||
- pwdFailureCountInterval : Intervalle de temps entre deux tentatives infructueuses pour qu’elles soient considérées comme « à la suite ».
|
||||
- pwdGraceAuthNLimit : période de grâce suite à l’expiration du mot de passe.
|
||||
- pwdInHistory : nombre de mots de passe dans l’historique.
|
||||
- pwdLockout : indique si on bloque le compte au bout de X échecs.
|
||||
- pwdLockoutDuration : durée du blocage du compte (en secondes).
|
||||
- pwdMaxAge : age maximal du mot de passe (en secondes).
|
||||
- pwdMaxFailure : nombre d’échecs de saisie du mot de passe maximal (avant blocage).
|
||||
- pwdMinAge : age minimal du mot de passe (en secondes).
|
||||
- pwdMinLength : longueur minimale du mot de passe.
|
||||
- pwdMustChange : indique si l’utilisateur doit changer son mot de passe.
|
||||
- pwdSafeModify : indique si il faut envoyer l’ancien mot de passe avec le nouveau pour modification.
|
||||
|
||||
On applique les modifications,
|
||||
```
|
||||
ldapadd -H ldap://localhost -D cn=admin,dc=krhacken,dc=org -y /root/pwdldap -f ppolicy_def.ldif
|
||||
```
|
||||
Vérifications
|
||||
```
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=schema,cn=config" cn
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "cn=config" "Objectclass=olcPpolicyConfig" -LLL
|
||||
ldapsearch -QLLLY EXTERNAL -H ldapi:/// -b "dc=krhacken,dc=org" "Objectclass=pwdPolicy"
|
||||
```
|
||||
|
||||
## Mise en place des OU
|
||||
Les OUs sont des conteneurs qui permettent de ranger les données dans l’annuaire, de les hiérarchiser.
|
||||
|
||||
### /root/ldap/conf/OU.ldif
|
||||
```
|
||||
dn: ou=people,dc=krhacken,dc=org
|
||||
ou: people
|
||||
objectClass: organizationalUnit
|
||||
|
||||
dn: ou=group,dc=krhacken,dc=org
|
||||
ou: group
|
||||
objectClass: organizationalUnit
|
||||
|
||||
dn: ou=system,dc=krhacken,dc=org
|
||||
ou: system
|
||||
objectClass: organizationalUnit
|
||||
|
||||
dn: ou=krhacken,ou=people,dc=krhacken,dc=org
|
||||
ou: krhacken
|
||||
objectClass: organizationalUnit
|
||||
|
||||
dn: ou=client,ou=people,dc=krhacken,dc=org
|
||||
ou: client
|
||||
objectClass: organizationalUnit
|
||||
|
||||
dn: ou=sysgroup,ou=group,dc=krhacken,dc=org
|
||||
ou: sysgroup
|
||||
objectClass: organizationalUnit
|
||||
|
||||
dn: ou=workgroup,ou=group,dc=krhacken,dc=org
|
||||
ou: workgroup
|
||||
objectClass: organizationalUnit
|
||||
```
|
||||
On rajoute les OU au ldap
|
||||
```
|
||||
ldapadd -cxWD cn=admin,dc=krhacken,dc=org -y /root/pwdldap -f OU.ldif
|
||||
```
|
||||
|
||||
Explication rapide
|
||||
- krhacken contenant tout les utilisateurs krhacken
|
||||
- people contient tout les utilisateurs (krhacken ou non)
|
||||
|
||||
|
||||
## Utilisateurs
|
||||
### /root/ldap/conf/User_PSEUDO.ldif
|
||||
```
|
||||
dn: uid=PSEUDO,ou=krhacken,ou=people,dc=krhacken,dc=org
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
uid: niko
|
||||
sn: niko
|
||||
givenName: Nicolas
|
||||
cn: Nicolas
|
||||
displayName: Nicolas
|
||||
userPassword: password
|
||||
mail: mail@spam.com
|
||||
title: Admin
|
||||
initials: N
|
||||
```
|
||||
On ajoute l'utilisateur
|
||||
```
|
||||
ldapadd -cxWD cn=admin,dc=krhacken,dc=org -y /root/pwdldap -f User_PSEUDO.ldif
|
||||
```
|
||||
Commande pour la connexion à un utilisateur
|
||||
```
|
||||
ldapsearch -xLLLH ldap://localhost -D uid=PSEUDO,ou=krhacken,ou=people,dc=krhacken,dc=org -W -b "dc=krhacken,dc=org" "uid=PSEUDO"
|
||||
```
|
||||
|
||||
## Groupes
|
||||
|
||||
Il existe deux types de groupes : les posixgroup et les groupofnames.
|
||||
Les posixgroup sont similaires au groupes Unix, et les groupofnames ressemblent plus à des groupes AD.
|
||||
Pour faire simple, l’avantage des groupofnames est qu’avec un filtre sur un utilisateur, on peut connaitre ses groupes (avec l’overlay memberof). Chose impossible avec les posixgroups.
|
||||
|
||||
|
||||
### /root/ldap/conf/Group.ldif
|
||||
```
|
||||
dn: cn=cloud,ou=sysgroup,ou=group,dc=krhacken,dc=org
|
||||
cn: cloud
|
||||
description: Cloud
|
||||
objectClass: groupOfNames
|
||||
member: cn=admin,dc=krhacken,dc=org
|
||||
|
||||
dn: cn=krhacken,ou=workgroup,ou=group,dc=krhacken,dc=org
|
||||
cn: krhacken
|
||||
description: krhacken
|
||||
objectClass: groupOfNames
|
||||
member: cn=admin,dc=krhacken,dc=org
|
||||
```
|
||||
On ajoute les
|
||||
```
|
||||
ldapadd -cxWD cn=admin,dc=krhacken,dc=org -y /root/pwdldap -f Group.ldif
|
||||
```
|
||||
On peu tester memberof pour voir si admin est bien dans les bon groupes
|
||||
```
|
||||
ldapsearch -xLLLH ldap://localhost -D cn=admin,dc=krhacken,dc=org -y /root/pwdldap -b "dc=krhacken,dc=org" "cn=admin" memberof
|
||||
```
|
||||
|
||||
Pour rajouter un utilisateur dans un groupe avec un fichier ldif (addusertogroup.ldif)
|
||||
```
|
||||
dn: cn=cloud,ou=sysgroup,ou=group,dc=krhacken,dc=org
|
||||
changetype: modify
|
||||
add: member
|
||||
member: uid=niko,ou=krhacken,ou=people,dc=krhacken,dc=org
|
||||
```
|
||||
On ajoute l'utilisateur avec
|
||||
```
|
||||
ldapmodify -cxWD cn=admin,dc=krhacken,dc=org -y /root/pwdldap -f addusertogroup.ldif
|
||||
```
|
||||
|
||||
# Sécurisation de l'annuaire
|
||||
|
||||
## Comptes avec permissions réduite
|
||||
Nous allons créer deux compte systèmes.
|
||||
- Un viewer qui aura uniquement les droits en lecture de l'arbre
|
||||
- Un Writer qui lui aura les droits en écriture
|
||||
|
||||
### /root/ldap/conf/viewer.ldif
|
||||
```
|
||||
dn: cn=viewer,ou=system,dc=krhacken,dc=org
|
||||
objectClass: simpleSecurityObject
|
||||
objectClass: organizationalRole
|
||||
cn: viewer
|
||||
description: LDAP viewer
|
||||
userPassword: passview
|
||||
```
|
||||
|
||||
### /root/ldap/conf/writer.ldif
|
||||
```
|
||||
dn: cn=writer,ou=system,dc=krhacken,dc=org
|
||||
objectClass: simpleSecurityObject
|
||||
objectClass: organizationalRole
|
||||
cn: writer
|
||||
description: LDAP Writer
|
||||
userPassword: passwrite
|
||||
```
|
||||
Ajout des utilisateurs,
|
||||
```
|
||||
ldapadd -cxWD cn=admin,dc=krhacken,dc=org -y /root/pwdldap -f viewer.ldif
|
||||
ldapadd -cxWD cn=admin,dc=krhacken,dc=org -y /root/pwdldap -f writer.ldif
|
||||
```
|
||||
|
||||
On autorise la lecture de l'arbre uniquement au utilisateur authentifié en modifiant une ACL
|
||||
### /root/ldap/conf/acl.ldif
|
||||
```
|
||||
dn: olcDatabase={1}mdb,cn=config
|
||||
changetype: modify
|
||||
replace: olcAccess
|
||||
olcAccess: to attrs=userPassword by self write by anonymous auth by dn="cn=writer,ou=system,dc=krhacken,dc=org" write by dn="cn=viewer,ou=system,dc=krhacken,dc=org" read by dn="cn=admin,dc=krhacken,dc=org" write by * none
|
||||
olcAccess: to dn.base="dc=krhacken,dc=org" by users read
|
||||
olcAccess: to * by self write by dn="cn=admin,dc=krhacken,dc=org" write by * read by anonymous none
|
||||
```
|
||||
On modife l'ACL
|
||||
```
|
||||
ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f acl.ldif
|
||||
```
|
||||
|
||||
## Forcer SSL
|
||||
Si c'est le cas on peut maintenant forcer la connexion SSL
|
||||
### /root/ldap/conf/forcetls.ldif
|
||||
```
|
||||
dn: olcDatabase={1}mdb,cn=config
|
||||
changetype: modify
|
||||
add: olcSecurity
|
||||
olcSecurity: tls=1
|
||||
```
|
||||
Ajout des modifications et application
|
||||
```
|
||||
ldapmodify -H ldapi:// -Y EXTERNAL -f forcetls.ldif
|
||||
systemctl restart slapd
|
||||
```
|
||||
Vérifions si TLS est obligatoire,
|
||||
Cette commande doit retourner une erreur
|
||||
```
|
||||
ldapsearch -H ldap:// -x -b "dc=sessionkrkn,dc=fr" -LLL dn
|
||||
```
|
||||
et celle la doit aboutir
|
||||
```
|
||||
ldapsearch -H ldap:// -x -b "dc=example,dc=com" -LLL -Z dn
|
||||
```
|
||||
|
||||
Voilà pour la mise en place de base du LDAP cependant il faut configuré chaque client pour se connecter au serveur avec STARTTLS.
|
||||
|
||||
NB : Il manque la réplication que nous mettrons en place plus tard.
|
||||
1081
applicatif/zone_krkn/mail.md
Normal file
1081
applicatif/zone_krkn/mail.md
Normal file
File diff suppressed because it is too large
Load Diff
2
applicatif/zone_route/README.md
Normal file
2
applicatif/zone_route/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# Présentation de la zone ROUTE
|
||||
Vous trouverez ici toute la documentation relative au fonctionnement et à la configuration de la zone ROUTE. Cela comprend les deux HAProxy et les deux reverses proxy public nginx.
|
||||
323
applicatif/zone_route/haproxy.md
Normal file
323
applicatif/zone_route/haproxy.md
Normal file
@@ -0,0 +1,323 @@
|
||||
# Gestion du flux post-firewall
|
||||
|
||||
## Présentation des containers
|
||||
|
||||
Deux containers Debian 10 identiques, un sur Alpha l'autre sur Bêta avec deux interfaces
|
||||
- Sur Alpha le container HAProxy a comme IP 10.0.0.3/24 sur ROUTE (eth0) 10.0.1.3/24 sur CTF (eth1)
|
||||
- Sur Beta le container HAProxy a comme IP 10.0.0.4/24 sur ROUTE 10.0.1.4/24 sur CTF
|
||||
L'option Firewall PVE des interfaces est désactivée
|
||||
|
||||
## Objectifs et choix techniques
|
||||
|
||||
Trois objectifs pour la gestion du flux post-firewall
|
||||
- Redondance du proxy/load balancer entre les deux nodes.
|
||||
- Séparation des flux (reverse public et reverse ctf).
|
||||
- Load balancing entre deux reverse proxy Nginx public (un sur chaque nodes).
|
||||
- Connexion SSL entre l'utilisateur et le proxy.
|
||||
- Vérification du certificat du client et de son CN si tentative de connexion au panel Proxmox (uniquement).
|
||||
|
||||
|
||||
Voici les choix techniques faits afin de répondre à ces objectifs
|
||||
- Pour le load balancing entre les deux reverse proxy Nginx, on utilisera HAProxy.Le lien sécurisé sera entre l'utilisateur de HAProxy qui soumettra ensuite des requêtes HTTP à un des reverse proxy Nginx.
|
||||
- Pour la redondance du proxy entre les deux nodes, on utilisera keepalived et la technologie VRRP pour garantir la disponibilité du proxy via une ip virtuelle.
|
||||
- Pour la vérification du certificat client - uniquement si le client veut se connecter au panel Proxmox- on fera une première frontend à la couche 4 (TCP) pour les connexions https qui rediregera les connexions vers le panel sur une frontend dédié et le reste vers une frontend par défaut.
|
||||
- Pour les certificats SSL, nous allons utiliser Let's Encrypt avec un serveur Nginx local dédié à l'obtention des certificats et des scripts pour le dépoiement sur les deux containers HAProxy.
|
||||
- La séparation des flux entre les deux reverse public et le reverse ctf ce fera au niveau de la seconde frontend par défaut tout comme le filtrage des requêtes par nom de domaine.
|
||||
|
||||
### Voilà un schéma résumé
|
||||
|
||||

|
||||
|
||||
## Création d'un canal d'échange par clé entre les deux containers
|
||||
Afin de pouvoir faire des scp de manière automatique entre les deux containers, il faut mettre en place une connexion ssh par clé en root entre les deux containers.
|
||||
|
||||
Le procédé est le même, en voici les variantes,
|
||||
- Sur Alpha le container HAProxy aura comme IP 10.0.0.3
|
||||
- Sur Beta le container HAProxy aura comme IP 10.0.0.4
|
||||
|
||||
### /etc/ssh/sshd_config
|
||||
Remplacer la ligne concernée par
|
||||
```
|
||||
PermitRootLogin yes
|
||||
```
|
||||
|
||||
### Génération et échange de la clé
|
||||
```
|
||||
adduser hasync
|
||||
ssh-keygen -o -a 100 -t ed25519 -f /root/.ssh/id_ed25519
|
||||
|
||||
Alpha : ssh-copy-id -i /root/.ssh/id_ed25519 root@10.0.0.4
|
||||
Beta : ssh-copy-id -i /root/.ssh/id_ed25519 root@10.0.0.3
|
||||
```
|
||||
|
||||
### /etc/ssh/sshd_config
|
||||
Remplacer les lignes concernées par
|
||||
```
|
||||
PermitRootLogin without-password
|
||||
PubkeyAuthentication yes
|
||||
```
|
||||
Il est maintenant possible de se connecter par clé entre les containers
|
||||
|
||||
|
||||
## Installation et configuration de HAProxy sur chaque node
|
||||
|
||||
Le procédé est le même, en voici les variantes,
|
||||
- Sur Alpha le container HAProxy aura comme IP 10.0.0.3
|
||||
- Sur Beta le container HAProxy aura comme IP 10.0.0.4
|
||||
|
||||
### Installation
|
||||
```
|
||||
apt-get update
|
||||
apt-get install -y haproxy hatop certbot nginx
|
||||
systemctl enable haproxy
|
||||
systemctl enable nginx
|
||||
```
|
||||
Pour la vérification du certificat du client, la méthode est dans la git. Il faut placer le ca.crt dans /home/hasync/pve.crt et ajouter le CN autorisé dans /home/hasync/allowed_cn.txt
|
||||
|
||||
### Configuration
|
||||
Voilà une rapide explication de la configuration faite
|
||||
- Une première frontend (all-web-in) de type TCP en écoute sur le port 443 qui vérifie si les requêtes tcp sont bien SSL et redirige tout vers la frontend principale via un proxy sauf les requêtes vers la panel Proxmox qui sont redirigées vers la frontend admin via un proxy.
|
||||
|
||||
- La frontend principale (user-web-in) de type http écoute sur le ports 80 et le proxy évoqué plus haut. Filtrage des requêtes ne respectant pas le nom de domaine et forçage du https sans www sinon rejet du packet. Redirection des requêtes Let's Encrypt vers un serveur Nginx local dédié aux certifications sinon séparation du flux avec d'un côté une backend s'occupant du load balancing entre les deux reverse proxy Nginx public et de l'autre une backend redirigeant vers le reverse proxy ctf à l'aide du nom de domaine.
|
||||
|
||||
#### /etc/haproxy/haproxy.cfg
|
||||
```
|
||||
global
|
||||
log /dev/log local0
|
||||
log /dev/log local1 notice
|
||||
chroot /var/lib/haproxy
|
||||
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
|
||||
stats timeout 30s
|
||||
user haproxy
|
||||
group haproxy
|
||||
daemon
|
||||
ca-base /etc/ssl/certs
|
||||
crt-base /etc/ssl/private
|
||||
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
|
||||
ssl-default-bind-options no-sslv3
|
||||
tune.ssl.default-dh-param 2048
|
||||
|
||||
defaults
|
||||
log global
|
||||
option dontlognull
|
||||
timeout connect 5000
|
||||
timeout client 50000
|
||||
timeout server 50000
|
||||
errorfile 400 /etc/haproxy/errors/400.http
|
||||
errorfile 403 /etc/haproxy/errors/403.http
|
||||
errorfile 408 /etc/haproxy/errors/408.http
|
||||
errorfile 500 /etc/haproxy/errors/500.http
|
||||
errorfile 502 /etc/haproxy/errors/502.http
|
||||
errorfile 503 /etc/haproxy/errors/503.http
|
||||
errorfile 504 /etc/haproxy/errors/504.http
|
||||
|
||||
frontend all-web-in
|
||||
mode tcp
|
||||
bind *:443 interface eth0
|
||||
tcp-request inspect-delay 5s
|
||||
tcp-request content accept if { req_ssl_hello_type 1 }
|
||||
use_backend is-admin if { req_ssl_sni -i pve.sessionkrkn.fr }
|
||||
use_backend is-admin if { req_ssl_sni -i rspamd.sessionkrkn.fr }
|
||||
default_backend is-user
|
||||
|
||||
frontend user-web-in
|
||||
mode http
|
||||
bind *:80 interface eth0
|
||||
bind abns@haproxy-user accept-proxy ssl accept-proxy no-sslv3 crt /etc/ssl/letsencrypt interface eth0
|
||||
acl host_letsencrypt path_beg /.well-known/acme-challenge
|
||||
acl authorized_host hdr_end(host) sessionkrkn.fr
|
||||
acl mail hdr_end(host) mail.sessionkrkn.fr
|
||||
acl rspamd path_beg /rspamd/
|
||||
acl ctf_host hdr_end(host) ctf.sessionkrkn.fr
|
||||
acl ctf_host hdr_end(host) web.sessionkrkn.fr
|
||||
acl host_www hdr_beg(host) -i www.
|
||||
|
||||
reqirep ^Host:\ www.(.*)$ Host:\ \1 if host_www !host_letsencrypt !mail
|
||||
reqadd X-Forwarded-Proto:\ http
|
||||
reqadd X-Forwarded-Proto:\ https
|
||||
|
||||
redirect scheme https code 301 if !{ ssl_fc } authorized_host !host_letsencrypt !mail
|
||||
use_backend nginx-ctf if ctf_host !host_letsencrypt !mail
|
||||
use_backend letsencrypt if host_letsencrypt !mail
|
||||
use_backend reverse-nginx if authorized_host !ctf_host OR mail
|
||||
default_backend drop-http
|
||||
|
||||
frontend admin-in
|
||||
mode http
|
||||
bind abns@haproxy-admin accept-proxy ssl no-sslv3 crt /etc/ssl/letsencrypt ca-file /home/hasync/pve.crt verify required interface eth0
|
||||
acl is_auth ssl_c_s_dn(cn) -i -f /etc/haproxy/allowed_cn.txt
|
||||
acl pve hdr_end(host) pve.sessionkrkn.fr
|
||||
acl rspamd hdr_end(host) rspamd.sessionkrkn.fr
|
||||
use_backend reverse-nginx if { ssl_fc_has_crt } is_auth rspamd
|
||||
use_backend pve-interface if { ssl_fc_has_crt } is_auth pve
|
||||
default_backend drop-http
|
||||
|
||||
backend is-admin
|
||||
mode tcp
|
||||
server admin-in abns@haproxy-admin send-proxy-v2
|
||||
|
||||
backend is-user
|
||||
mode tcp
|
||||
server admin-in abns@haproxy-user send-proxy-v2
|
||||
|
||||
backend letsencrypt
|
||||
mode http
|
||||
http-request set-header Host letsencrypt.requests
|
||||
server letsencrypt 127.0.0.1:8164
|
||||
|
||||
backend pve-interface
|
||||
mode http
|
||||
balance roundrobin
|
||||
server pve-alpha 10.0.0.1:8006 check ssl verify none
|
||||
server pve-beta 10.0.0.2:8006 check ssl verify none
|
||||
|
||||
backend reverse-nginx
|
||||
mode http
|
||||
balance roundrobin
|
||||
server reverse1 10.0.0.6:80 check
|
||||
server reverse2 10.0.0.7:80 check
|
||||
|
||||
backend nginx-ctf
|
||||
mode http
|
||||
server nginx-ctf1 10.0.2.5:80 check
|
||||
|
||||
backend drop-http
|
||||
mode http
|
||||
http-request deny
|
||||
```
|
||||
|
||||
Une fois HAProxy configuré, il faut configurer le serveur Nginx permettant l'obtention des certificats Let's Encrypt.
|
||||
|
||||
```
|
||||
rm /etc/nginx/sites-enabled/default
|
||||
rm /etc/nginx/sites-available/default
|
||||
rm /etc/letsencrypt/live/README
|
||||
mkdir -p /home/hasync/letsencrypt-requests
|
||||
```
|
||||
|
||||
#### /etc/nginx/sites-availables/letsencrypt.conf
|
||||
```
|
||||
server {
|
||||
listen 8164;
|
||||
server_name letsencrypt.requests;
|
||||
root /home/hasync/letsencrypt-requests;
|
||||
}
|
||||
```
|
||||
|
||||
### Démarrage des services
|
||||
```
|
||||
systemctl restart nginx.service
|
||||
systemctl restart haproxy.service
|
||||
```
|
||||
### Obtention des premiers certificats et déploiement
|
||||
```
|
||||
certbot certonly --webroot -w /home/hasync/letsencrypt-requests/ -d sub.sessionkrkn.fr
|
||||
```
|
||||
|
||||
Voici un script pour mettre en place les certificats Let's Encrypt au bon endroit qui s'occupe de propager les certificats sur l'autre container.
|
||||
```
|
||||
#!/bin/bash
|
||||
rm -f /etc/letsencrypt/live/README
|
||||
rm -rf /etc/ssl/letsencrypt/*
|
||||
for domain in $(ls /etc/letsencrypt/live); do
|
||||
cat /etc/letsencrypt/live/$domain/privkey.pem /etc/letsencrypt/live/$domain/fullchain.pem > /etc/ssl/letsencrypt/$domain.pem
|
||||
done
|
||||
scp -r /etc/ssl/letsencrypt/* root@10.0.0.3:/etc/ssl/letsencrypt
|
||||
ssh root@10.0.0.3 'systemctl restart haproxy.service'
|
||||
service haproxy restart
|
||||
```
|
||||
|
||||
## Mise en place de la haute disponibilité du load balancer
|
||||
Voilà la configuration que nous allons mettre en place,
|
||||
- Sur Alpha le container HAProxy aura comme IP 10.0.0.3
|
||||
- Sur Beta le container HAProxy aura comme IP 10.0.0.4
|
||||
- L'IP virtuelle 10.0.0.5 sera attribuée en fonction de la disponibilité des load balancer
|
||||
- La node Alpha sera le master et la node Beta sera le Slave
|
||||
|
||||
### Installation (commune au deux nodes)
|
||||
```
|
||||
apt-get install -y keepalived
|
||||
systemctl enable keepalived.service
|
||||
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
|
||||
echo "net.ipv4.ip_nonlocal_bind=1" >> /etc/sysctl.conf
|
||||
sysctl -p /etc/sysctl.conf
|
||||
```
|
||||
|
||||
### Configuration sur Alpha
|
||||
Comme décrite plus haut
|
||||
#### /etc/keepalived/keepalived.conf
|
||||
```
|
||||
vrrp_script chk_haproxy {
|
||||
script "killall -0 haproxy"
|
||||
interval 2
|
||||
weight 2
|
||||
}
|
||||
|
||||
vrrp_instance VI_1 {
|
||||
interface eth0
|
||||
state MASTER
|
||||
virtual_router_id 51
|
||||
priority 101 # 101 on master, 100 on backup
|
||||
virtual_ipaddress {
|
||||
10.0.0.5 # the virtual IP
|
||||
}
|
||||
track_script {
|
||||
chk_haproxy
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration sur Beta
|
||||
Comme décrite plus haut
|
||||
#### /etc/keepalived/keepalived.conf
|
||||
```
|
||||
vrrp_script chk_haproxy {
|
||||
script "killall -0 haproxy"
|
||||
interval 2
|
||||
weight 2
|
||||
}
|
||||
|
||||
vrrp_instance VI_1 {
|
||||
interface eth0
|
||||
state MASTER
|
||||
virtual_router_id 51
|
||||
priority 100 # 101 on master, 100 on backup
|
||||
virtual_ipaddress {
|
||||
10.0.0.5 # the virtual IP
|
||||
}
|
||||
track_script {
|
||||
chk_haproxy
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Vérification
|
||||
Le retour de cette commande doit montrer l'adresse IP 10.0.0.5 sur Alpha
|
||||
```
|
||||
ip a | grep -e inet.*eth0
|
||||
```
|
||||
|
||||
## Renouvellement automatique des certificats
|
||||
|
||||
Pour une question de simplicité d'administration, les certificats Let's Encrypt se renouvelleront automatiquement grâce à une crontab. Le problème est qu'un seul des deux containers sera accessible depuis l'extérieur. Il faut donc copier les certificats via scp entre les deux containers.
|
||||
|
||||
### /home/hasync/renew.sh
|
||||
Voilà un script d'automatisation à mettre sur les deux containers
|
||||
```
|
||||
#!/bin/bash
|
||||
if [ "$(ip a | grep -c "10.0.0.5")" -ge 1 ]; then
|
||||
certbot renew
|
||||
rm -rf /etc/ssl/letsencrypt/*
|
||||
for domain in $(ls /etc/letsencrypt/live); do
|
||||
cat /etc/letsencrypt/live/$domain/privkey.pem /etc/letsencrypt/live/$domain/fullchain.pem > /etc/ssl/letsencrypt/$domain.pem
|
||||
done
|
||||
scp -r /etc/ssl/letsencrypt/* hasync@<ip_autre_ct>:/etc/ssl/letsencrypt
|
||||
else
|
||||
fi
|
||||
```
|
||||
Le script est stocké dans /home/hasync/renew.sh, voici la crontab à ajouter (crontab -e) sur les deux containers.
|
||||
```
|
||||
0 4 */15 * * /bin/sh /home/hasync/renew.sh >/dev/null 2>&1
|
||||
```
|
||||
|
||||
C'est tout pour la configuration de HAProxy, la prochaine étape est la mise en place des trois reverses proxy.
|
||||
83
applicatif/zone_route/nginx_public.md
Normal file
83
applicatif/zone_route/nginx_public.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Reverse proxy NGINX sur le réseau public
|
||||
|
||||
## Spécification des containers
|
||||
Ce service est redondé car vital, son IP est 10.0.0.6 sur Alpha et 10.0.0.7 sur Beta.
|
||||
|
||||
## Objectif
|
||||
Il doit rediriger les requêtes arrivant de HAProxy vers le bon container en fonction de l'hostname. Pour cela nous allons utiliser des serveurs web HTTP avec des proxy sur Nginx sans s'occuper de l'autre serveur web.
|
||||
|
||||
## Création d'un canal d'échange par clé entre les deux containers
|
||||
Afin de pouvoir faire des scp de manière automatique entre les deux containers, il faut mettre en place une connexion ssh par clé en root entre les deux containers.
|
||||
|
||||
Le procédé est le même, en voici les variantes,
|
||||
- Sur Alpha le container Nginx aura comme IP 10.0.0.6
|
||||
- Sur Beta le container HAProxy aura comme IP 10.0.0.7
|
||||
|
||||
### /etc/ssh/sshd_config
|
||||
Remplacer la ligne concernée par
|
||||
```
|
||||
PermitRootLogin yes
|
||||
```
|
||||
|
||||
### Génération et échange de la clé
|
||||
```
|
||||
ssh-keygen -o -a 100 -t ed25519 -f /root/.ssh/id_ed25519
|
||||
|
||||
Alpha : ssh-copy-id -i /root/.ssh/id_ed25519 root@10.0.0.7
|
||||
Beta : ssh-copy-id -i /root/.ssh/id_ed25519 root@10.0.0.6
|
||||
```
|
||||
|
||||
### /etc/ssh/sshd_config
|
||||
Remplacer les lignes concernées par
|
||||
```
|
||||
PermitRootLogin without-password
|
||||
PubkeyAuthentication yes
|
||||
```
|
||||
Il est maintenant possible de se connecter par clé entre les containers
|
||||
|
||||
## Installation de Nginx sur les deux containers
|
||||
```
|
||||
apt-get update
|
||||
apt-get install -y nginx
|
||||
systemctl enable nginx.service
|
||||
```
|
||||
|
||||
## Mise en place d'un serveur faisant office de reverse proxy
|
||||
|
||||
On ajoute un serveur web dans /etc/nginx/sites-available avec un nom décrivant bien le service derrière ce serveur.
|
||||
|
||||
Voilà la template du serveur web,
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
server_name address.fr;
|
||||
location / {
|
||||
proxy_pass http://ip_reseau_public/;
|
||||
proxy_set_header Host $http_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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Voilà un script permetant l'installation d'un serveur web présent dans /etc/nginx/sites-available. Il prend en entrée le nom du fichier du serveur à activer
|
||||
```
|
||||
if [ "$#" -eq "0" ]
|
||||
then
|
||||
echo "Bad Usage !"
|
||||
else
|
||||
if [ -f "/etc/nginx/sites-available/$1" ]
|
||||
then
|
||||
ln -s /etc/nginx/sites-available/$1 /etc/nginx/sites-enabled
|
||||
systemctl restart nginx.service
|
||||
scp /etc/nginx/sites-available/$1 root@<ip_autre_ct>:/etc/nginx/sites-available/
|
||||
ssh root@<ip_autre_ct> "ln -s /etc/nginx/sites-available/$1 /etc/nginx/sites-enabled"
|
||||
ssh root@<ip_autre_ct> 'systemctl restart nginx.service'
|
||||
else
|
||||
echo "Not exist !"
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
La procédure est tout le temps la méthode générale pour ajouter un serveur à Nginx. Elle est décrite ici. Cependant, dans certains cas, il peut être nécessaire d'enlever un ou plusieurs proxy\_set\_header dans la configuration du serveur Nginx.
|
||||
BIN
applicatif/zone_route/schema_route.png
Normal file
BIN
applicatif/zone_route/schema_route.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 143 KiB |
41
applicatif/zone_wan/opnsense.md
Normal file
41
applicatif/zone_wan/opnsense.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Firewall
|
||||
|
||||
Au niveau du pare-feu nous allons utiliser OPNSense (fork de pfSense). Les deux nodes auront une VM avec OPNSense pour la Haute Disponibilité, l'IP publique (WAN) se déplacera entre les deux VM grâce à une IP virtuelle à CARP et à pfSense. Ca sera la même chose pour la gateway sur chaque interface.
|
||||
|
||||
## Les interfaces
|
||||
- Interface d'entrée WAN (vmbr0)
|
||||
- Interfaces de sortie ROUTE, KRKN, CTF, EXT (vmbr1 et vmbr2)
|
||||
|
||||
Toutes les autres interfaçe seront pour des liens purement locaux (Corosync, pfSync et VXLAN) donc géré avec un parefeu local type UFW et non par OPNSense.
|
||||
|
||||
## Configuration de la VM
|
||||
- Guest OS : Other
|
||||
- Bus Device : VirtIO Block
|
||||
- Network device model : VirtIO (paravirtualized)
|
||||
|
||||
Il faudra ensuite ajouter au moins deux interfaces réseau.
|
||||
|
||||
## Installation du système
|
||||
Il faut récupérer une image iso "DVD" sur le site d'OpnSense
|
||||
|
||||
Lors du démarrage il faut utiliser,
|
||||
- login : installer
|
||||
- pass : opnsense
|
||||
|
||||
Ne pas configurer de VLANs.
|
||||
|
||||
Une fois installation terminée, quitter le programme d'installation et redémarrer la VM.
|
||||
|
||||
## Premiers paramétrages d'OPNSense
|
||||
### Interfaces
|
||||
Une fois la VM rédémarrée pour la première fois, il faut faut assigner les interfaces, choix 1 dans le menu (assign interfaces).
|
||||
Il faut configurer 2 interfaces pour commencer, une le WAN (vmbr0) et une pour le LAN (vmbr1). On ajoutera le reste plus tard.
|
||||
|
||||
|
||||
# En attente du choix WAN...
|
||||
|
||||
### Règles (uniquement DNAT)
|
||||
- DNAT 80,443 route:10.0.0.5
|
||||
- DNAT 25,465,587,143,993,4190 krkn:10.0.1.10
|
||||
- DNAT 2222 ctf:10.0.2.12
|
||||
- DNAT 8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091 ctf:10.0.2.13
|
||||
22
applicatif/zone_wan/option_possible.md
Normal file
22
applicatif/zone_wan/option_possible.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Différentes options pour la zone WAN
|
||||
|
||||
Pour l'accès au pare-feu du serveur, plusieurs options sont envisageables :
|
||||
|
||||
## En ipv4
|
||||
|
||||
NB: Il y aura un firewall de type OPNSense dans un VM sur chaque node. Il faut donc que l'ipv4 puisse être alloué à une VM.
|
||||
|
||||
### Une seule ipv4
|
||||
Un firewall sur chaque serveur et une ipv4 publique (virtuelle) qui se déplacerait entre les deux firewall en fonction de leur disponibilité.
|
||||
|
||||
### Deux ipv4
|
||||
Deux combinaisons possibles
|
||||
- Une par serveur avec un firewall sur chaque serveur, le choix du serveur se ferait au niveau des entrées DNS.
|
||||
- Une pour l'accès au firewall sur le même modèle que décrit pour une seule ipv4 et une autre pour un accès direct au serveur sans passer par le firewall principal.
|
||||
|
||||
## En ipv6
|
||||
Un réseau uniquement ipv6 serait plus compliqué à mettre en place au niveau des accès depuis des clients ipv4. Cependant un bloc d'ipv6 en plus d'une ou deux ipv4 serait un plus.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Le choix qui serait le plus fiable au niveau de la gestion de la disponibilité du serveur serait une ipv4 pour l'accès au firewall et une seconde ipv4 pour l'accès direct au serveur car plus sûr en cas de crash des deux VM. Avec un bloc d'ipv6 pour pouvoir rajouter progressivement le support de l'ipv6.
|
||||
Reference in New Issue
Block a user