docu-vps/README.md

19 KiB

Documentation du VPS Krhacken

Première ébauche, restructuration nécessaire

Système d'exploitation

Sélectionner une template depuis le panneau de contrôle Proxgroup (Additional tools/Reinstall).

Distribution Actuelle : Ubuntu 20.04 LTS

Choix techniques

Les différents services sont compartimentés dans des conteneurs LXC ou Docker.

Liste des conteneurs

Nom d'hôte Service
bilmak nginx
syze nextcloud
lozova gitea
syhnal matrix
butivka ...

Amélioration motd

rm /etc/update-motd.d/50-motd-news # Pour Ubuntu
echo -n "
echo \"

 ██ ▄█▀ ██▀███   ██░ ██  ▄▄▄       ▄████▄   ██ ▄█▀▓█████  ███▄    █ 
 ██▄█▒ ▓██ ▒ ██▒▓██░ ██▒▒████▄    ▒██▀ ▀█   ██▄█▒ ▓█   ▀  ██ ▀█   █ 
▓███▄░ ▓██ ░▄█ ▒▒██▀▀██░▒██  ▀█▄  ▒▓█    ▄ ▓███▄░ ▒███   ▓██  ▀█ ██▒
▓██ █▄ ▒██▀▀█▄  ░▓█ ░██ ░██▄▄▄▄██ ▒▓▓▄ ▄██▒▓██ █▄ ▒▓█  ▄ ▓██▒  ▐▌██▒
▒██▒ █▄░██▓ ▒██▒░▓█▒░██▓ ▓█   ▓██▒▒ ▓███▀ ░▒██▒ █▄░▒████▒▒██░   ▓██░
▒ ▒▒ ▓▒░ ▒▓ ░▒▓░ ▒ ░░▒░▒ ▒▒   ▓▒█░░ ░▒ ▒  ░▒ ▒▒ ▓▒░░ ▒░ ░░ ▒░   ▒ ▒ 
░ ░▒ ▒░  ░▒ ░ ▒░ ▒ ░▒░ ░  ▒   ▒▒ ░  ░  ▒   ░ ░▒ ▒░ ░ ░  ░░ ░░   ░ ▒░
░ ░░ ░   ░░   ░  ░  ░░ ░  ░   ▒   ░        ░ ░░ ░    ░      ░   ░ ░ 
░  ░      ░      ░  ░  ░      ░  ░░ ░      ░  ░      ░  ░         ░ 
                                  ░                                 
\"
" >> /etc/update-motd.d/00-header

Installation des paquets

apt install ferm fail2ban vim lxd git

Sécurisation SSH

Copier les clefs publiques des sysadmin dans le fichier ~/.ssh/authorized_keys.

Puis interdire l'authentification par mot de passe (pas de risque puisque qu'il y a un accès NoVNC):

echo "PasswordAuthentification no" >> /etc/ssh/sshd_config
systemctl restart ssh

Configuration ZSH (optionnel)

apt install zsh zsh-common zsh-syntax-highlighting zsh-doc zsh-autosuggestions
# Garder les variables d'environnement
echo "emulate sh -c 'source /etc/profile'" >> /etc/zsh/zprofile
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" # oui cette commande pique les yeux ^^ 

On choisit un beau de thème de shell (steeef par exemple !):

vim ~/.zshrc
ZSH_THEME="steeef"

Et on ajoute quelques plugins sympathiques :

plugins=(
	git
	python
	pip
	ssh-agent
	debian
	ansible
	themes
)

IP forward

Dé-commenter la ligne suivante dans le fichier /etc/sysctl.conf :

net.ipv4.ip_forward=1

Appliquer les changements :

sysctl -p /etc/sysctl.conf

Configuration pare-feu Ferm

@def $DEV_PRIVATE = br0;
@def $DEV_WORLD = ens18;

@def $NET_PRIVATE = 10.0.0.0/24;

table filter {
    chain INPUT {
        policy DROP;

        # connection tracking
        mod state state INVALID DROP;
        mod state state (ESTABLISHED RELATED) ACCEPT;

        # allow local connections
        interface lo ACCEPT;

        # respond to ping
        proto icmp icmp-type echo-request ACCEPT;

        # for IPsec
        interface $DEV_WORLD {
            proto udp dport 500 ACCEPT;
            proto (esp ah) ACCEPT;
        }

        # allow SSH connections
        proto tcp dport ssh ACCEPT;

        # for LXD
        proto (udp tcp) dport domain ACCEPT;
        proto udp dport bootps ACCEPT;
        
        # WEB
        proto tcp dport http ACCEPT;
        proto tcp dport https ACCEPT;
    }

    chain OUTPUT {
        policy ACCEPT;

        # connection tracking
        mod state state INVALID DROP;
        mod state state (ESTABLISHED RELATED) ACCEPT;
    }

    chain FORWARD {
        policy DROP;

        # connection tracking
        mod state state INVALID DROP;
        mod state state (ESTABLISHED RELATED) ACCEPT;

        # connections from the internal net to the internet or to other
        # internal nets are allowed
        interface $DEV_PRIVATE ACCEPT;
    }
}

table nat {
    chain POSTROUTING {
        # masquerade private IP addresses
        saddr $NET_PRIVATE outerface $DEV_WORLD MASQUERADE;
    }
}

Configuration LXD

Initialisation

lxd init
Would you like to use LXD clustering? (yes/no) [default=no]: no
Do you want to configure a new storage pool? (yes/no) [default=yes]: yes
Name of the new storage pool [default=default]: lxd-pool
Name of the storage backend to use (zfs, ceph, btrfs, dir, lvm) [default=btrfs]: btrfs
Create a new LVM pool? (yes/no) [default=yes]: yes
Would you like to use an existing empty disk or partition? (yes/no) [default=no]: yes
Path to the existing block device: /dev/sda3
Would you like to connect to a MAAS server? (yes/no) [default=no]: no
Would you like to create a new local network bridge? (yes/no) [default=yes]: yes
What should the new bridge be called? [default=lxdbr0]: br0
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 10.0.0.1/24
Would you like LXD to NAT IPv4 traffic on your bridge? [default=yes]: yes
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: none # Pour l'instant car l'infra proxgroup ne le permet pas
Would you like LXD to be available over the network? (yes/no) [default=no]: no # pas pour l'instant
Would you like stale cached images to be updated automatically? (yes/no) [default=yes] yes
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: yes

Désactivation du pare-feu intégré

lxc network set br0 ipv4.firewall false
lxc network set br0 ipv6.firewall false

Système de fichiers

Il est important de mentionner l'utilisation du système de fichiers BTRFS, qui apporte une grande flexibilité pour le stockage des conteneurs et des images de base. Chacun entité se verra attribuer un sous-volume. Pour avoir un aperçu des sous-volumes créés :

mount /dev/sda3
btrfs subvolume list /mnt
umount /mnt # Quand on a fini

Gestion des conteneurs

Profil

C'est certainement un des éléments clefs pour la création d'un conteneur, puisqu'il définira tous les caractéristiques de celui-ci. Un profil "krhacken" été créé avec les paramètres suivants :

config:
  limits.cpu: "2"
  limits.memory: 256MB
description: Krhacken LXD profile
devices:
  eth0:
    name: eth0
    network: br0
    type: nic
  root:
    path: /
    pool: lxd-pool
    type: disk
name: krhacken

Et voici quelques commandes pour créer un nouveau profil et le configurer :

lxc profile create krkn1
lxc profile edit krkn1 # Edition directe du fichier de configuration /!\ difficile
lxc profile set krkn1 limits.cpu 4 # Limiter l'utilisation à 4 CPU
lxc profile set krkn1 limits.memory 256MB # Limiter l'utilisation de la RAM à 256MB

Création d'un conteneur

Exemple pour créer le conteneur "bilmak" avec le profile krhacken à partir d'une image Debian Buster:

lxc launch -p krhacken images:debian/buster bilmak

Stop & Start

lxc stop <CT>

lxc start <CT>

Snapshots de conteneurs

Créer un snap

lxc snapshot <CT>

Restaurer un snap

lxc restore <CT> snap

Redirection de ports

La vieille méthode du NAT avec ferm pour rediriger les ports, applique d'avoir de connaître à l'avance l'ip de chaque, or pour plus de flexibilité les addresses sont attribuées via DHCP.

Nous utiliserons donc la nouvelle solution proposée par LXD à savoir le "device proxy". Le fait de passer par un couche supérieure permet de faire abstraction de l'IP du conteneur.

Voici comment rediriger les ports 80 et 443 pour sur conteneur exécutant un serveur web (type NginX) :

lxc config device add bilmak http proxy listen=tcp:IPv4_publique:80 connect=tcp:127.0.0.1:80
lxc config device add bilmak https proxy listen=tcp:IPv4_publique:443 connect=tcp:127.0.0.1:443

Mise en place des services

Proxy Nginx (Bilmak)

Pré-requis :

# Installation de paquets de base
apt install nginx certbot python3-certbot-dns-ovh vim net-tools dnsutils unattended-upgrades apt-listchanges wget curl
# Activation des màj auto de sécurité
echo unattended-upgrades unattended-upgrades/enable_auto_updates boolean true | debconf-set-selections
dpkg-reconfigure -f noninteractive unattended-upgrades
ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime

Certbot

Générer une clef d'API ovh : voir article de blog /root/.ovh.ini

dns_ovh_endpoint = ovh-eu
dns_ovh_application_key = xxx
dns_ovh_application_secret = xxx
dns_ovh_consumer_key = xxx

Puis on fait le petit chmod 600 /root/.ovh.ini kivabien.

Génération à blanc dry-run du certificat.

certbot certonly --dns-ovh --dns-ovh-credentials ~/.ovh.ini -d *.krhacken.org --dry-run

Si tout s'est bien passé :

certbot certonly --dns-ovh --dns-ovh-credentials ~/.ovh.ini -d *.krhacken.org

Fichiers de configuration Nginx

Adapter /etc/nginx/nginx.conf

ssl_protocols TLSv1.2 TLSv1.3; 
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; # openssl dhparam -out /etc/nginx/dhparam.pem 4096
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver 80.67.169.12 80.67.169.40 valid=300s;
resolver_timeout 5s;

vhost pour nextcloud : /etc/nginx/sites-available/nextcloud

server {
	listen 80;
	server_name cloud.krhacken.org;
	return 301 https://$server_name$request_uri;
}
server {
	listen 443 ssl;
	server_name cloud.krhacken.org;

	location / {
		proxy_pass http://IP_nextcloud_server;
		include /etc/nginx/proxy_params;
	}

	# Upload settings
	client_max_body_size 1G;
	fastcgi_buffers 64 4K;

	# C{ard,al}Dav tweaks
	location = /.well-known/carddav {
		rewrite ^(.*) https://$server_name/remote.php/dav permanent;
	}
	location = /.well-known/caldav {
		rewrite ^(.*) https://$server_name/remote.php/dav permanent;
	}
	location = /.well-known/webfinger {
		rewrite ^(.*) https://$server_name/public.php?service=webfinger permanent;
	}
	
	# TLS
	ssl_certificate /etc/letsencrypt/live/krhacken.org/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/krhacken.org/privkey.pem;
	
	# Logs
	access_log /var/log/nginx/nextcloud.access.log;
	error_log /var/log/nginx/nextcloud.error.log;
	
	# STS
	add_header Strict-Transport-Security "max-age=31536000;includeSubDomains" always;
}

Il possible qu'un bug survienne lors du redémarrage de Nginx, la solution est ici.

Nextcloud (Syze)

Pré-requis :

# Installation de paquets de base
apt install vim wget sudo unzip unattended-upgrades nginx postgresql php-fpm php-common php-curl php-gd php-json php-xml php-mbstring php-zip php-pgsql php-imap php-apcu php-imagick php-intl php-bz2 php-gmp
# Activation des màj auto de sécurité
echo unattended-upgrades unattended-upgrades/enable_auto_updates boolean true | debconf-set-selections
dpkg-reconfigure -f noninteractive unattended-upgrades
ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime

Migration Mariadb -> PostgreSQL

Suivre cet article de blog.

Fichiers de configuration nginx

vhost pour nextcloud : /etc/nginx/sites-available/nextcloud

upstream php-handler {
    server 127.0.0.1:9000;
}

server {
    listen 80;
    listen [::]:80;

    server_name cloud.krhacken.org;

    # Set real client IP
    set_real_ip_from 10.0.0.0/8;
    real_ip_header X-Real-IP;
    real_ip_recursive on;

    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Download-Options "noopen" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    add_header X-Robots-Tag "none" always;
    add_header X-XSS-Protection "1; mode=block" always;
    
    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;

    # Path to the root of your installation
    root /var/www/nextcloud;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # The following 2 rules are only needed for the user_webfinger app.
    # Uncomment it if you're planning to use this app.
    #rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
    #rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;

    # The following rule is only needed for the Social app.
    # Uncomment it if you're planning to use this app.
    #rewrite ^/.well-known/webfinger /public.php?service=webfinger last;

    location = /.well-known/carddav {
      return 301 $scheme://$host:$server_port/remote.php/dav;
    }
    location = /.well-known/caldav {
      return 301 $scheme://$host:$server_port/remote.php/dav;
    }

    # set max upload size
    client_max_body_size 1G;

    # Enable gzip but do not remove ETag headers
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    # Uncomment if your server is build with the ngx_pagespeed module
    # This module is currently not supported.
    #pagespeed off;

    location / {
        rewrite ^ /index.php;
    }

    location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
        deny all;
    }
    location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
        deny all;
    }

    #location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
    location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy)\.php(?:$|\/) {
        fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
        try_files $fastcgi_script_name =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param HTTPS on;
        # Avoid sending the security headers twice
        fastcgi_param modHeadersAvailable true;
        # Enable pretty urls
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
        try_files $uri/ =404;
        index index.php;
    }

    # Adding the cache control header for js, css and map files
    # Make sure it is BELOW the PHP block
    location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463";
        # Add headers to serve security related headers (It is intended to
        # have those duplicated to the ones above)
        # Before enabling Strict-Transport-Security headers please read into
        # this topic first.
        add_header Strict-Transport-Security "max-age=15552000; includeSubDomains; preload;";
        #
        # WARNING: Only add the preload option once you read about
        # the consequences in https://hstspreload.org/. This option
        # will add the domain to a hardcoded list that is shipped
        # in all major browsers and getting removed from this list
        # could take several months.
        add_header Referrer-Policy "no-referrer" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-Download-Options "noopen" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Permitted-Cross-Domain-Policies "none" always;
        add_header X-Robots-Tag "none" always;
        add_header X-XSS-Protection "1; mode=block" always;
        # Optional: Don't log access to assets
        access_log off;
    }

    location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ {
        try_files $uri /index.php$request_uri;
        # Optional: Don't log access to other assets
        access_log off;
    }

}

Optimisations PHP : /etc/nginx/conf.d/php-optimizations.conf

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_cache_valid 404 1m;
fastcgi_cache_valid any 1h;
fastcgi_cache_methods GET HEAD;

Pour plus d'informations, il suffit de suivre la documentation officielle.

Configuration LXC (pas utilisé finalement --> passage à LXD)

Globale

/etc/lxc/default.conf

# Defaults
lxc.apparmor.profile = generated
lxc.apparmor.allow_nesting = 1

# Network bridge configuration
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.hwaddr = ad:ba:be:xx:xx:xx

lxc-net

/etc/defaults/lxc-net (activer la création du bridge)

USE_LXC_BRIDGE="true"

Unprivileged

~/.config/lxc/default.conf

# Defaults
lxc.apparmor.profile = generated
lxc.apparmor.allow_nesting = 1

# Network bridge configuration
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.hwaddr = ad:ba:be:xx:xx:xx

# Allow nesting
lxc.mount.auto = cgroup

# ID map for umpriviledged containers
lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536

# "Secure" mounting
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed

# Disable AppArmor confinement for containers started by non-root
# See https://discuss.linuxcontainers.org/t/unprivileged-container-wont-start-cgroups-sysvinit/6766 and
# https://discuss.linuxcontainers.org/t/cannot-use-generated-profile-apparmor-parser-not-available/4449
lxc.apparmor.profile = unconfined