Ajouter de la SWAP sur un VPS


Les VPS sont livrés avec un disque virtuel contenant une partition unique occupant tout l’espace. Il n’est donc pas possible de modifier le schéma de partitionnement.
Heureusement pour nous, Linux accepte deux types de swap:

  • une partition dédiée
  • un fichier d’échange

Et c’est avec ce deuxième type que l’on va pouvoir ajouter de la swap.

    1. Création d’un fichier d’échange de 2Go
      dd if=/dev/zero of=/var/tmp/swapfile bs=1G count=2
    2. On force la synchronisation des disques (c’est à dire: écrire sur le disque toutes les données présentes dans les tampons en mémoire)
      sync
    3. On modifie les droits de ce fichier d’échange
      chmod 0600 /var/tmp/swapfile
    4. Initialisation du fichier d’échange, afin d’être utilisable comme extension de mémoire. Jusqu’ici le fichier avait l’étiquette SELinux user_tmp_t. La commande suivante va aussi changer l’étiquette pour swapfile_t.
      mkswap -c /var/tmp/swapfile
    5. On active notre nouvelle swap
      swapon /var/tmp/swapfile

Pour rendre l’utilisation de notre fichier swap permanent, il faut rajouter la ligne suivante dans le fichier /etc/fstab. C’est la même ligne que pour une partition d’échange, mis à part le chemin absolu du fichier, en lieu et place du chemin absolu du périphérique.

/var/tmp/swapfile swap swap defaults 0 0

Configuration, initialisation et utilisation de tripwire

Avec la sortie de la nouvelle version de tripwire, j’en profite pour rédiger un tutoriel sur la configuration, l’initialisation et l’utilisation de tripwire.
Tripwire ne peut que constater la modification des fichiers et doit donc être installé/configuré juste après l’installation de la distribution.

Configuration initiale

L’initialisation consiste à créer deux paires de clés, et à chiffrer le fichier de configuration, ainsi que celui de définition de la politique. La paire nommée site servant à chiffrer/déchiffrer les fichiers de configuration et de définition de la politique, et la paire nommée local servant à chiffrer/déchiffrer la base de données et les rapports.

Méthode manuelle pas à pas

Générer la paire de clés nommée site:

sudo twadmin -m G -v -S /etc/tripwire/site.key

Générer la paire de clés nommée local:

sudo twadmin -m G -v -L /etc/tripwire/$(hostname --fqdn)-local.key

Chiffrer le fichier de configuration avec la paire de clés nommée site:

sudo twadmin -m F -c /etc/tripwire/tw.cfg -S /etc/tripwire/site.key /etc/tripwire/twcfg.txt

Chiffrer le fichier de politique avec la paire de clés nommée site:

sudo twadmin -m P -p /etc/tripwire/tw.pol -S /etc/tripwire/site.key /etc/tripwire/twpol.txt

Avec le script qui va bien

Ce script interactif est présent dans le paquet RPM. Dans le cas d’un déploiement automatique (via ansible ou autre), il faudra utiliser la méthode manuelle.

sudo tripwire-setup-keyfiles


----------------------------------------------
The Tripwire site and local passphrases are used to sign a  variety  of
files, such as the configuration, policy, and database files.

Passphrases should be at least 8 characters in length and contain  both
letters and numbers.

See the Tripwire manual for more information.

----------------------------------------------
Creating key files...

(When selecting a passphrase, keep in mind that good passphrases typically
have upper and lower case letters, digits and punctuation marks, and are
at least 8 characters in length.)

Enter the site keyfile passphrase:
Verify the site keyfile passphrase:
Generating key (this may take several minutes)...Key generation complete.

(When selecting a passphrase, keep in mind that good passphrases typically
have upper and lower case letters, digits and punctuation marks, and are
at least 8 characters in length.)

Enter the local keyfile passphrase:
Verify the local keyfile passphrase:
Generating key (this may take several minutes)...Key generation complete.

----------------------------------------------
Signing configuration file...
Please enter your site passphrase: 
Wrote configuration file: /etc/tripwire/tw.cfg

A clear-text version of the Tripwire configuration file:
/etc/tripwire/twcfg.txt
has been preserved for your inspection.  It  is  recommended  that  you
move this file to a secure location and/or encrypt it in place (using a
tool such as GPG, for example) after you have examined it.


----------------------------------------------
Signing policy file...
Please enter your site passphrase: 
Wrote policy file: /etc/tripwire/tw.pol

A clear-text version of the Tripwire policy file:
/etc/tripwire/twpol.txt
has been preserved for  your  inspection.  This  implements  a  minimal
policy, intended only to test  essential  Tripwire  functionality.  You
should edit the policy file to  describe  your  system,  and  then  use
twadmin to generate a new signed copy of the Tripwire policy.

Once you have a satisfactory Tripwire policy file, you should move  the
clear-text version to a secure location  and/or  encrypt  it  in  place
(using a tool such as GPG, for example).

Now run "tripwire --init" to enter Database Initialization  Mode.  This
reads the policy file, generates a database based on its contents,  and
then cryptographically signs the resulting  database.  Options  can  be
entered on the command line to specify which policy, configuration, and
key files are used  to  create  the  database.  The  filename  for  the
database can be specified as well. If no  options  are  specified,  the
default values from the current configuration file are used.

Initialisation

On va créer un petit script afin de modifier notre fichier de politique. Celui-ci commentera en direct, dans le fichier de politique, les fichiers absents de notre système.

Fichier /tmp/configure-tripwire.sh
#/bin/bash

while read line
do
        if [ "${line:0:13}" == "### Filename:" ]
        then
                key=$(echo ${line:14} | sed "s;/;\\\\/;g")
                sudo sed -i -e "/${key}/ s/^/#/" /etc/tripwire/twpol.txt
        fi
done

On lance une première fois l’initialisation en envoyant la sortie sur notre script.

sudo tripwire --init | /tmp/configure-tripwire.sh

Une fois notre fichier de politique modifier, on va chiffrer ce fichier, encore une fois

sudo twadmin -m P -p /etc/tripwire/tw.pol -S /etc/tripwire/site.key /etc/tripwire/twpol.txt

Et on relance l’initialisation

sudo tripwire --init

Utilisation

Tripwire n’utilise que la version chiffrée des fichiers de configuration et de définition de la politique. On peut tout à fait supprimer/vider les fichiers en clair. On peut toutefois les récupérer à partir de leur version chiffrée.

  • Récupération du fichier de configuration
    sudo twadmin -m f > /etc/tripwire/twcfg.txt
  • Récupération du fichier de définition de la politique
    sudo twadmin -m p > /etc/tripwire/twpol.txt

Tâches usuelles:

  • Vérification du système (En plus de la tâche planifiée). C’est utile après un mise à jour, pour valider ensuite ces modifications.
    sudo tripwire --check
  • Application des modifications du système trouvées lors de la vérification dans la base de données et rapportées dans le rapport
    sudo tripwire --update --accept-all --twrfile /var/lib/tripwire/report/<nom d'hôte pleinement qualifié>-<date du rapport>-<heure du rapport>.twr
  • Mise à jour de la politique, une fois le fichier /etc/tripwire/twpol.txt modifié
    sudo tripwire --update-policy -p /etc/tripwire/tw.pol --secure-mode low /etc/tripwire/twpol.txt
  • Visualiser un rapport précis
    sudo twprint -m r -r /var/lib/tripwire/report/web.tartarefr.eu-20180520-033515.twr
  • Dumper de la base de données
    sudo twprint -m d

Ansible par la pratique – Première partie: les bases

Ici, on a seulement notre hôte physique (localhost) faisant fonctionner une machine virtuelle (ipv4:192.168.122.243). Bien entendu, notre hôte physique Fedora a le paquet RPM ansible installé.

dnf install ansible

Notre machine virtuelle est une CentOS fraichement installée (minimal), sans utilisateur (pour l’instant).

Lancer des commandes manuellement

On va, dans un premier temps essayé de contacter notre machine virtuelle avec un simple ping.

ansible 192.168.122.243 -m ping
[WARNING]: Could not match supplied host pattern, ignoring: all
[WARNING]: provided hosts list is empty, only localhost is available
[WARNING]: Could not match supplied host pattern, ignoring: 192.168.122.243
[WARNING]: No hosts matched, nothing to do

Cela ne fonctionne pas.

En effet, Ansible ne connait pas notre cible et refuse de communiquer avec elle. De plus notre utilisateur n’existe pas sur la cible

Il nous faudrait déclarer l’hôte distant dans le fichier /etc/ansible/hosts pour que ça fonctionne. Mais comme on n’est pas le super-utilisateur, on va faire autrement, et en suivant le guide ansible des bonnes pratiques petit à petit.

On va créer deux répertoires, le nom peut-être ce que vous voulez, il faudra juste adapter les commandes pour utiliser ces répertoires:

  • production
  • staging (recettage ou pré-production)

et dans chaque répertoire on va créer un fichier nommé hosts, où l’on définira des groupes dans lesquels on déclarera nos hôtes. Ce fichier est au format INI.

Dans l’exemple suivant, on déclare notre machine virtuelle dans le groupe server et notre hôte local dans le groupe ansible.

Fichier staging/hosts
[server]
192.168.122.243

[ansible]
localhost

Et au lancement de la commande, on va spécifier le répertoire de notre environnement avec l’option -i, et pour utiliser le super-utilisateur sur notre cible, on le spéficie (avec demande de mot de passe)

ansible -i staging 192.168.122.243 --user root --ask-pass -m ping
192.168.122.243 | SUCCESS => {
"changed": false,
"failed": false,
"ping": "pong"
}

Cette fois ça fonctionne.
Essayons maintenant une commande

ansible -i staging 192.168.122.243 --user root --ask-pass -a whoami
192.168.122.243 | SUCCESS | rc=0 >>
root

Par défaut Ansible utilise le même utilisateur sur l’hôte distant. On s’assure donc que celui existe sur notre machine virtuelle ou que l’on spécifie l’utilisateur à utiliser dans la ligne de commande.

Lancer une série de commandes

Le fichier permettant de lister les commande à lancer s’appelle un playbook. Il peut être monolithique ou divisé en plusieurs parties. Ansible recommande la séparation par rôle, ce qui permet de réutiliser les commandes d’un playbook et de les partager facilement avec la communauté.

Les rôles

Il est important de bien réfléchir à la séparation des différents rôles que devra comporter notre serveur, afin d’avoir la meilleur modularité possible.

On va prendre l’exemple d’un serveur personnel (vps), qui comporterait un blog (wordpress), un outil d’analyse du trafic web (piwik alias matomo) et tout le monitoring nécessaire à tous cela. On arrive à ce stade à 3 rôles. Mais sachant que le blog et piwik ont tous les deux besoin d’un serveur web et d’une base de données, que le système à besoin d’être mis à jour, que certaines actions seront nécessaires à plusieurs rôles (ex: redémarrer le service httpd après un changement), on arrive à

Rôles de notre serveur:

  • system: concernera la post-installation du système
    • mettre le système à jour
    • Définition des pools ntp (fr)
    • Définition du nom d’hôte
    • Définition basique du firewall: SSH (22)
    • Redirection des mails locaux vers une adresse externe
    • etc…
  • pki: Concernera l’obtention et la mise à jour des certificats SSL
    • Copie de nos clés privées
    • Copies de nos fichiers et scripts gérant la pki (systemd)
  • webserver: Concernera l’installation et la configuration d’apache
    • Installation et configuration d’apache
    • Mise à jour des extensions (ex modsecurity), hors RPM (les paquets RPMs seront mis à jour avec le rôle system)
    • Ajout des ports HTTP (80) et HTTPS (443) au firewall
  • database: Concernera l’installation et la configuration de mariadb
    • Installation et configuration de mariadb
  • blog: Concernera l’installation et la configuration de wordpress
    • Installation et configuration du blog
    • définition de l’alias ou du vhost pour le webserver
    • import de la sauvegarde si elle existe
    • Mise à jour des extensions, hors RPM (les paquets RPMs seront mis à jour avec le rôle system)
  • webstat: Concernera l’installation et la configuration de piwik
    • Installation et configuration de piwik
    • définition de l’alias ou du vhost pour le webserver
    • import de la sauvegarde si elle existe
  • monitoring: Concernera l’installation et la configuration du monitoring
    • Installation et configuration des outils
  • ids: Concernera l’installation et la configuration des outils de détection et de vérification d’intrusion
    • Installation et configuration des outils

On remarque que le changement d’un élément ne perturbe pas (ou peu) les autres rôles. Ainsi le changement de produit n’est pas une tâche trop difficile. Le changement de piwik par awstats sera transparent pour les autres rôles, mais le changement du serveur web ou de la base de données impactera les rôles du blog et de l’outil d’analyse web.

Les playbooks

Un playbook est un fichier texte au format YAML.

Il s’agit dune succession de tâches à accomplir. L’erreur sur l’une d’entre elle entraine l’arrêt du processus (par défaut). Il existe un très grand nombre de modules et la documentation officielle des modules Ansible les explique tous.

L’exemple suivant se sert de deux modules:

  • yum: pour manager les paquets RPMs de la distribution Linux
  • shell: pour lancer des commandes

et effectue les tâches suivantes:

  • Installation du dépôt EPEL
  • Mise à jour de la distribution
  • Installation d’une liste de paquets RPMs
  • Initialisation de etckeeper
  • Premier commit de etckeeper

pour chaque hôte membre du groupe server (fichier hosts du répertoire production ou staging).

Fichier site.yml
--- 
- hosts: server
  tasks:
    - name: Ensure epel repository is set
      yum:
        name: epel-release
        state: latest
      become: true
      
    # System update
    - name: Ensure all pkgs are up-to-date
      yum:
        name: '*'
        state: latest
      become: true
      tags: update

    # Install packages
    - name: Ensure system RPMs are installed and up-to-date
      yum: 
        pkg: "{{ item }}"
        state: latest
      become: true
      with_items:
        - git
        - postfix
        - chrony
        - mlocate
        - screen
        - vim-enhanced
        - yum-utils
        - bzip2
        - unzip
        - bind-utils
        - man-pages
        - net-tools
        - etckeeper
      
    # Manage etckeeper
    - name: Ensure etc is versionned
      shell: "etckeeper init"
      args:
        executable: /bin/bash
        creates: /etc/.git
        chdir: /etc        
      become: true
      
    - name: Ensure first commit is done for etc
      shell: "etckeeper commit 'First commit'"
      args:
        executable: /bin/bash
        creates: /etc/.git/refs/heads/master
        chdir: /etc        
      become: true

Quelques remarques sur les tâches:

  • Toutes les tâches doivent être exécutées par le super-utilisateur, c’est le rôle de l’instruction become: true (root par défaut, mais on peut le spécifier avec become_user)
  • Les tâches d’initialisation d’etckeeper ne doivent pas être exécuter plusieurs fois. On spécifie donc un nom de fichier avec l’instruction creates, qui, s’il existe, ne lancera pas la commande. On se place également dans un répertoire précis avant de lancer la commande avec l’instruction chdir

Le playbook se lance ainsi:

ansible-playbook -i staging --ask-become-pass site.yml
SUDO password:

PLAY [server] ******************************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.122.243]

TASK [Ensure epel repository is set] *******************************************
changed: [192.168.122.243]

TASK [Ensure all pkgs are up-to-date] ******************************************
changed: [192.168.122.243]

TASK [Ensure system RPMs are installed and up-to-date] *************************
changed: [192.168.122.243] => (item=[u'git', u'postfix', u'chrony', u'mlocate', u'screen', u'vim-enhanced', u'yum-utils', u'bzip2', u'unzip', u'bind-utils', u'man-pages', u'net-tools', u'etckeeper'])

TASK [Ensure etc is versionned] ************************************************
changed: [192.168.122.243]

TASK [Ensure first commit is done for etc] *************************************
changed: [192.168.122.243]

PLAY RECAP *********************************************************************
192.168.122.243            : ok=6    changed=5    unreachable=0    failed=0

Si on relance le même playbook, les tâches déjà effectuées ne seront pas relancées.

Et c’est une règle d’or des playbooks, ils peuvent être relancé n’importe quand et le résultat sera toujours prévisible (idempotent), c’est à dire qu’il n’y aura pas d’effet de bord, en cas de doublon par exemple.

En effet, dans notre exemple, on ne peut pas initialiser le dépôt git d’etckeeper plusieurs fois et c’est au playbook de gérer cette situation (en posant un fichier et en testant sa présence).

ansible-playbook -i staging --ask-become-pass site.yml
SUDO password:

PLAY [server] ******************************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.122.243]

TASK [Ensure epel repository is set] *******************************************
ok: [192.168.122.243]

TASK [Ensure all pkgs are up-to-date] ******************************************
ok: [192.168.122.243]

TASK [Ensure system RPMs are installed and up-to-date] *************************
ok: [192.168.122.243] => (item=[u'git', u'postfix', u'chrony', u'mlocate', u'screen', u'vim-enhanced', u'yum-utils', u'bzip2', u'unzip', u'bind-utils', u'man-pages', u'net-tools', u'etckeeper'])

TASK [Ensure etc is versionned] ************************************************
ok: [192.168.122.243]

TASK [Ensure first commit is done for etc] *************************************
ok: [192.168.122.243]

PLAY RECAP *********************************************************************
192.168.122.243            : ok=6    changed=0    unreachable=0    failed=0

Si l’utilisateur courant n’a pas besoin de mot de passe pour les commandes sudo, on ne sera pas obligé de spécifier l’option –ask-become-pass.

Protéger ses variables en les mettant dans un coffre

Si Ansible s’avère bien pratique pour gérer les hôtes distants, il apparait que certaines pratiques sont dangereuses au niveau de la sécurité. En effet comment affecter un mot de passe à l’utilisateur MySQL root et en même temps commiter les fichiers Ansibles sur un dépôt public distant, sans compromettre la sécurité. Ansible a résolu le problème avec vault (coffre).

Ansible-vault encode les données avec l’algorithme AES256

Il y a deux manière de l’utiliser:

  • Chiffrer la totalité du fichier de variables et utiliser les commandes ad-hoc pour éditer le fichier
  • Chiffrer seulement la valeur de la variable dans un fichier texte en clair

Prenons comme exemple un fichier de variable host_vars/server qui contiendrait l’entrée suivante:

mysql_root_password = "monsupermotdepasse"

Méthode 1: Encrypter le fichier

On créé notre fichier chiffré et on inscrit notre variable une fois le fichier ouvert par l’éditeur par défaut (variable EDITOR) avec la commande

ansible-vault create host_vars/server
New Vault password: 
Confirm New Vault password:

On pourra éditer notre fichier chiffré, avec l’éditeur par défaut

ansible-vault edit host_vars/server

Alternativement, on peut créer un fichier en clair et le chiffrer par la suite

echo 'mysql_root_password = "monsupermotdepasse"' > host_vars/server
ansible-vault encrypt host_vars/server

Ce qui donnera le fichier suivant

Fichier host_vars/server
$ANSIBLE_VAULT;1.1;AES256
61346330393736306566356131633264323234353664653034646239326439633261643630393162
6633646264326134373230343935636361353033313262630a356566336261623261343233666135
33333635353331663434366137653934633238633561346463626130633063663636373330663031
3738363239663635380a326532386133343732306535393466393433306434323265366336323037
39356531316631366161376161633166336562366330313038343961626261333237356237333062
3830373337656561653364313064353866306132653864623834

Méthode 2: Chiffrer la valeur de la variable

On chiffre la valeur avec la commande idoine

ansible-vault encrypt_string --vault-id @prompt 'monsupermotdepasse' --name 'mysql_root_password' >> host_vars/server
New vault password (default): 
Confirm vew vault password (default): 
Encryption successful

Ce qui donnera le fichier suivant

Fichier host_vars/server
mysql_root_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          65343663373236316433393833616164353236303666663437613438306630336135353238326137
          3566333939353435613539613066373463323231656635320a666633366637343738653634396137
          33316530396338316638303765636165363132363934376234316430633432613632663439326661
          3530613339653038640a396436373763363332313336623061313834353238613766393662396533
          30653531653265623165333037396539396632393535636166646538646638373261

Et maintenant ?

Les bases sont posées et un exemple concret sera bientôt disponible dans la deuxième partie: Premier playbook avec les rôles.

Déménagement

Peut-être l’avez vous remarqué, TartareFR a déménagé cet été. Ce n’était pas vraiment prévu et j’étais satisfait de mon hébergeur, obambu, mais celui-ci m’a annoncé qu’il arrête au 1 septembre les VPS KVM. Par le passé, n’ayant pas réussi à installer un VPN ppp sur de l’openVZ, je ne suis pas prêt à migrer sur une plateforme cloud. Les offres de firstheberg m’ont semblaient plus que raisonnables, moyennant quelques ajustements, ce qui m’a permis de blinder mon playbook d’installation ansible.

  • la distribution CentOS n’a pas SELinux
    Rien de grave en soit à partir du moment où on le sait. On installe les paquets RPM manquants (selinux-policy et selinux-policy-targeted), l’activation est ensuite triviale (édition du fichier /etc/sysconfig/selinux), mais un ré-étiquetage complet du système (touch /.autorelabel) est nécéssaire.
  • Le port 25 est bloqué en sortie
    Là, j’avoue, j’ai flippé. Mais en fait, une simple demande au support permet l’ouverture du port et ce, rapidement (moins d’1 heure, malgré une demande faite à l’heure de fermeture du support)
  • Le serveur mail est ssmtp. Rien de bien compliqué à modifier.

Connexion automatique d’un utilisateur avec une session verrouillée (sddm)

Connexion automatique d’un utilisateur

Il faut éditer le fichier de configuration de sddm afin d’ajouter les entrées de connexion automatique d’un utilisateur, dans la section Autologin:

  • Session: spécifier la session utilisateur (plasma, gnome, etc…). La liste des sessions disponible (installée sur votre poste) se trouve dans le répertoire /usr/share/xsessions/
    ls /usr/share/xsessions/
  • Relogin: Spécifier si sddm doit reconnecter l’utilisateur lorsque celui-ci se déconnecte
  • User: Le login de l’utilisateur à connecter automatiquement
Fichier /etc/sddm.conf
...
[Autologin]
Relogin=false
Session=plasma.desktop
User=tartarefr
...

Verrouillage de la session au démarrage

Pour verrouiller la session d’un utilisateur au démarrage de celle-ci, il faut ajouter au fichier (ou le créer s’il n’existe pas) ~/.profile

Fichier ~/.profile
export DESKTOP_LOCKED=yes

La ligne de commande correspondante (pour un copier/coller)

echo "export DESKTOP_LOCKED=yes" >> ~/.profile

Remplacer les mots de passe par l’insertion d’une clé USB

Ou comment remplacer ce que l’on sait (le mot de passe) par ce que l’on possède (une clé USB particulière). Cela permet de donner une seconde vie à des clés USB totalement obsolètes de par leur taille vraiment faible (inférieure à 1Go).

Pamusb permet de se connecter sans avoir à taper un mot de passe. Pour cela un module PAM est ajouté et la sécurité repose sur la possession de cette fameuse clé. Bien que cela ne soit pas impossible, il est très difficile de remplacer cette clé, qui sera reconnue par son ID (iSerial) et qui possédera le même secret partagé que l’ordinateur. C’est cette dernière partie qui est très difficile à reproduire car le secret partagé est changé à chaque utilisation.

Pour obtenir ce numéro de série, on fait la différence de la commande lsusb avant et après l’insertion de la clé pour en déduire le numéro du bus et de device. Pour moi c’est ça

Bus 002 Device 007: ID 04e8:1623 Samsung Electronics Co., Ltd

J’interroge donc lsusb (en tant que superutilisateur root) sur ce périphérique et je vais comparer ce chiffre à celui que me proposera pam-usb lors de la configuration de ma clé USB.

sudo lsusb -s 002:007 -v | grep iSerial
iSerial 3 0123456789AB

L’installation de pam-usb est triviale

sudo dnf install pam_usb

On branche ensuite notre clé USB, si ce n’est pas déjà fait, (sans montage de partition) et on la déclare comme clé utilisable par pam-usb en lui donnant un nom

sudo pamusb-conf --add-device masterkey
Please select the device you wish to add.
* Using "Samsung Mighty Drive (0123456789AB)" (only option)

Which volume would you like to use for storing data ?
* Using "/dev/sdh1 (UUID: 99899601-c7de-4086-bc81-f2ff36a0b509)" (only option)

Name            : masterkey
Vendor          : Samsung
Model           : Mighty Drive
Serial          : 0123456789AB
UUID            : 99899601-c7de-4086-bc81-f2ff36a0b509

Save to /etc/pamusb.conf ?
[Y/n]

Impossible d’écrire dans le fichier /etc/pamusb.conf

Le fichier /etc/pamusb.conf par défaut comporte des commentaires multi-lignes.

Bien que cela soit valide d’un point de vue XML, pam-usb n’aime vraiment pas ça.

De plus la bonne indentation du fichier est le cadet de ses soucis.

Donc on écrit un fichier valide pour pam-usb et on s’occupera de l’indentation manuellement à la fin.

sudo mv/etc/pamusb.conf /etc/pamusb.conf.orig
sudo echo '<configuration><defaults></defaults><devices></devices><users></users><services></services></configuration>' > /etc/pamusb.conf

On ajoute notre utilisateur

sudo pamusb-conf --add-user tartarefr
Which device would you like to use for authentication ?
* Using "masterkey" (only option)

User            : tartarefr
Device          : masterkey

Save to /etc/pamusb.conf ?
[Y/n]

On modifie les fichiers /etc/pam.d/system-auth et /etc/pam.d/system-auth-ac pour ajouter la ligne autorisant l’authentification par le module pam-usb avant celle de l’authentification unix (ligne existante qui ne doit pas être modifiée)

Fichiers /etc/pam.d/system-auth et /etc/pam.d/system-auth-ac
auth        sufficient    pam_usb.so
auth        sufficient    pam_unix.so ....

On vérifie

pamusb-check tartarefr
* Authentication request for user "tartarefr" (pamusb-check)
* Device "masterkey" is connected (good).
* Performing one time pad verification...
* Access granted
sudo cat /etc/fedora-release
* pam_usb v0.5.0
* Authentication request for user "didier" (sudo)
* Device "masterkey" is connected (good).
* Performing one time pad verification...
* Access granted.
Fedora release 28 (Twenty Eight)

Au secours, ça ne fonctionne plus
Il suffit de supprimer le répertoire local comportant le secret partagé (en se connectant avec le mot de passe unix). Le secret partagé sera réinitialisé avec la commande pamusb-check.

rm -rf ~/.pamusb
pamusb-check $USER

Relayer ses courriels via gmail

Afin de s’affranchir du relais SMTP de son F.A.I. on peut utiliser son compte google (gmail) pour envoyer les courriels avec son serveur de courrier postfix. C’est bon pour la planète car cela supprime les intermédiaires entre son courrier et les services de renseignements peu scrupuleux de notre vie privée. En parlant de ça, quelqu’un a déjà réussi à faire une restauration à partir de la solution NSACloudBackup ?

Il suffit de modifier un tout petit peu la configuration de postfix (qui a été le service mail par défaut de fedora et qui est toujours celui de centos).

On édite le fichier, afin de rajouter ou de modifier les entrées suivantes:

Fichier /etc/postfix/main.cf
relayhost = [smtp.gmail.com]:587

# use tls
smtp_use_tls=yes

# use sasl when authenticating to foreign SMTP servers
smtp_sasl_auth_enable = yes

# path to password map file
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd

# list of CAs to trust when verifying server certificate
smtp_tls_CAfile = /etc/ssl/certs/ca-bundle.trust.crt

# eliminates default security options which are imcompatible with gmail
smtp_sasl_security_options = noanonymous
smtp_sasl_mechanism_filter = plain

On génère le fichier fichier contenant les identifiants.

Fichier /etc/postfix/sasl_passwd
[smtp.gmail.com]:587  <email gmail>:<motdepasse>

On transforme ce fichier dans un langage compréhensible par postfix (fichiers indexés) et on supprime la version en clair

sudo postmap /etc/postfix/sasl_passwd
sudo rm /etc/postfix/sasl_passwd

On redémarre le service postfix et le tour est joué.

sudo systemctl restart postfix

Google considère que la seule méthode d’autentification sécurisée est OAuth2, et que SASL ne l’est pas. Il faudra donc autoriser les connexions “moins sécurisée” à son compte Google sur la page https://www.google.com/settings/security/lesssecureapps.

On teste la bonne réception d’un courriel envoyé avec la commande suivante:

echo 'Test from CLI' | mail -s 'gmail relay' <mon adresse email>

Dockerisation du service cobbler

Étude d’un système cobbler existant

Un système cobbler comporte plusieurs services:

  • le service cobbler à proprement parlé
  • un service web (apache) pour distribuer les RPMs
  • un service de transfert de fichier basique (tftp) pour distribuer le noyau bootable permettant d’afficher le menu, et tout ce qui va avec.

L’analyse du système existant nous montre

  1. Nous n’avons pas besoin de gérer le DHCP et le DNS, ceux-ci sont déjà gérés sur notre réseau et seul l’adresse du next_server sera mis à jour dans la configuration du DHCP.
  2. On utilise plusieurs snippets additionnels pour configurer la cible dans la phase post-installation (notamment une mise à jour de la distribution, l’installation du dépôt epel, etc…).
  3. Seulement quelques répertoires contiennent des données personnalisées:
    • /var/lib/cobbler: Ce répertoire contient deux types de données:
      • Les Données dynamiques propres à chaque installation et qui doivent être persistantes. Elles seront donc intégrées dans un ou plusieurs volumes de données: /var/lib/cobbler/config (la configuration dynamique) et /var/lib/cobbler/backup (le répertoire des sauvegardes)
      • Les Données semi-statiques. Elles seront intégrées directement dans l’image docker: les modèles de kickstart (définition de l’installation), les snippets (bout de code ré-utilisable), etc…
    • /var/www/cobbler: qui contient le dépôt de paquets RPM des distributions importées. Ces fichiers sont servis par le serveur web et ce répertoire sera intégré dans un volume de données.
    • /var/lib/tftp: qui contient le noyau bootable (pxelinux.0) , le menu d’installation, etc… Ces fichiers sont servis par le serveur tftpd et ce répertoire sera intégré dans un volume de données
    • On aura besoin d’un cinquième point de montage (/mnt sur le conteneur) où les isos des distributions seront montées par le serveur hôte.
      mkdir /mnt/centos
      mount -o ro /path/to/iso/CentOS-7-x86_64-DVD-1804.iso /mnt/centos
    • Et même d’un sixième point de montage, si l’hôte du conteneur a un noyau inférieur à 4.7, à cause du bug concernant les sockets unix sur overlayfs (lien): /var/run/supervisor

Bien entendu, pour ajouter un modèle de kickstart ou un snippet, l’image docker devra être reconstruite. Mais à l’usage, on se rend compte qu’on ajoute/modifie moins de fichiers snippets ou kickstart, que la disponibilité des mises à jour du paquet cobbler.

Démarrage

L’image peut être trouvée sur docker.io

docker pull tartarefr/docker-cobbler

Les variables d’environnement

Le service docker peut être personnalisé via les variables d’environnement suivantes:

  • HOST_IP_ADDR: Cette variable est la seule variable obligatoire car c’est elle qui permet la connexion à l’API cobbler et ne peut pas avoir de valeur par défaut. Elle doit prendre la valeur de l’adresse IP de l’hôte hostname --ip-address | awk '{print $1}'
  • HOST_HTTP_PORT: Port de l’hôte branché sur le port 80 du conteneur. Par défaut c’est 80 (-p 80:80)
  • DEFAULT_ROOT_PASSWD: Mot de passe en clair du compte superutilisateur root qui sera configuré sur les cibles (défaut: cobbler)
  • COBBLER_WEB_USER: Login de cobbler web (défaut: cobbler)
  • COBBLER_WEB_PASSWD: Mot de passe de cobbler web (défaut: cobbler)
  • COBBLER_WEB_REALM: Royaume de cobbler web car c’est de l’authentification digest (défaut: cobbler)
  • COBBLER_LANG: La langue à configurer lors de l’installation des cibles (défaut: fr_FR)
  • COBBLER_KEYBOARD: Le clavier à configurer lors de l’installation des cibles (défaut: fr-latin9)
  • COBBLER_TZ: Le fuseau horaire à configurer lors de l’installation des cibles (défaut: Europe/Paris)

Mise en place avant le premier démarrage

  1. On commence par télécharger l’iso DVD de centos 7
  2. Il va nous falloir construire 5 volumes docker pour un conteneur (même ratio que le pastis)
    docker volume create cobbler_www
    docker volume create cobbler_tftp
    docker volume create cobbler_config
    docker volume create cobbler_backup
    docker volume create cobbler_run
    
  3. On créé le point de montage de notre iso
    sudo mkdir /mnt/centos
  4. On monte notre iso dans /mnt/centos
    sudo mount -o ro /path/to/iso/CentOS-7-x86_64-DVD-1804.iso /mnt/centos
    

Démarrage

Je conseille fortement de modifier la valeur de DEFAULT_ROOT_PASSWD. En effet c’est un des tout premiers mot de passe root essayé par les pirates.

Contrairement à la valeur par défaut, notre serveur web (api et cobbler_web) sera accessible depuis l’hôte sur le port 60080.

docker run -d --privileged \
           -v cobbler_www:/var/www/cobbler:z \
           -v cobbler_tftp:/var/lib/tftp:z \
           -v cobbler_config:/var/lib/cobbler/config:z \
           -v cobbler_backup:/var/lib/cobbler/backup:z \
           -v cobbler_run:/var/run/supervisor:z \
           -v /mnt/centos:/mnt:z \
           -e DEFAULT_ROOT_PASSWD=cobbler \
           -e HOST_IP_ADDR=$(hostname --ip-address | awk '{print $1}') \
           -e HOST_HTTP_PORT=60080 \
           -e COBBLER_WEB_USER=cobbler \
           -e COBBLER_WEB_PASSWD=cobbler \
           -e COBBLER_WEB_REALM=Cobbler \
           -e COBBLER_LANG=fr_FR \
           -e COBBLER_KEYBOARD=fr-latin9 \
           -e COBBLER_TZ=Europe/Paris \
           -p 69:69/udp \
           -p 60080:80 \
           -p 60443:443 \
           -p 25151:25151 \
           --name cobbler \
           tartarefr/docker-cobbler:latest

Initialisation

Une fois à l’intérieur du conteneur, on va ajouter la cible memtest à notre menu d’installation, puis on va importer notre distribution centos 7, ajouter un profile supplémentaire qui permettra d’installer centos 7 avec un environnement graphique et synchroniser le tout.

  1. Immersion dans notre conteneur
    docker exec -ti cobbler /bin/bash
  2. Ajout de la cible memtest. On vérifie au préalable que le fichier /boot/memtest86+-5.01 existe bien (la version peut être modifiée et la ligne suivante doit être adaptée en conséquence)
    cobbler image add --name=memtest86+ --file=/boot/memtest86+-5.01 --image-type=direct
  3. Import de la distribution centos 7
    cobbler import --path=/mnt --name=CentOS-7-x86_64
  4. Ajout du profile pour une installation d’une centos desktop
    cobbler profile add --name=CentOS-7-x86_64-Desktop \
        --distro=CentOS-7-x86_64 \
        --kickstart=/var/lib/cobbler/kickstarts/sample_end.ks \
        --virt-file-size=12 \
        --virt-ram=2048
    
    cobbler profile edit --name CentOS-7-x86_64-Desktop --ksmeta="type=desktop"
    
  5. Synchronisation
    cobbler sync


Malheureusement, le changement de fichier iso (démontage et le montage d’un autre iso sur le même point de montage) sur l’hôte ne met pas à jour le contenu du répertoire /mnt dans le conteneur.
Pour ajouter une autre distribution, il faudra:

  1. stopper le conteneur
  2. détruire le conteneur
  3. démonter l’iso sur l’hôte
  4. monter le nouvel iso
  5. démarrer un nouveau conteneur

Modification de l’image

Pour ajouter un modèle de kickstart ou un snippet, il suffit de placer le fichier dans le répertoire idoine et de modifier le fichier Dockerfile:

  1. Ajouter une instruction docker de copie du fichier dans l’image du conteneur
  2. Si c’est un snippet s’éxecutant dans la phase post-installation, ajouter le nom à la liste des snippets post-installation à l’instruction d’activation (Activate personnal snippets)
  3. Reconstruire l’image
  4. Déployer la nouvelle image

Quelques mots sur l’image docker

J’ai préféré construire une image en partant de zéro, car celles disponibles sont soit plus mise à jour, soit elles utilisent systemd. Je préfère garder cette option pour les cas particuliers, inutile de sortir le tank pour écraser un moustique.

Supervisord est un bien meilleur candidat pour le poste d’orchestrateur dans un conteneur (le bon outil pour la bonne tâche) et permet de redémarrer un ou plusieurs services.

supervisorctl restart cobblerd

L’ensemble du projet cobbler est versionné sur gitlab, et en copie sur github (pour la construction automatique des images sur docker.io).

Fichier Dockerfile
FROM centos:7

MAINTAINER Didier FABERT (tartare) <didier@tartarefr.eu>

# RPM REPOs
RUN yum install -y \
    epel-release \
    && yum clean all \
    && rm -rf /var/cache/yum

RUN yum update -y \
    && yum clean all \
    && rm -rf /var/cache/yum

RUN yum install -y \
  cobbler \
  cobbler-web \
  pykickstart \
  debmirror \
  curl wget \
  rsync \
  supervisor \
  net-tools \
  memtest86+ \
  && yum clean all \
  &&  rm -rf /var/cache/yum

# Copy supervisor conf
COPY supervisord/supervisord.conf /etc/supervisord.conf
COPY supervisord/cobblerd.ini /etc/supervisord.d/cobblerd.ini
COPY supervisord/tftpd.ini /etc/supervisord.d/tftpd.ini
COPY supervisord/httpd.ini /etc/supervisord.d/httpd.ini

# Copy personnal snippets
COPY snippets/partition_config /var/lib/cobbler/snippets/partition_config
COPY snippets/configure_X /var/lib/cobbler/snippets/configure_X
COPY snippets/add_repos /var/lib/cobbler/snippets/add_repos
COPY snippets/disable_prelink /var/lib/cobbler/snippets/disable_prelink
COPY snippets/systemd_persistant_journal /var/lib/cobbler/snippets/systemd_persistant_journal
COPY snippets/rkhunter /var/lib/cobbler/snippets/rkhunter
COPY snippets/enable_X /var/lib/cobbler/snippets/enable_X
COPY snippets/yum_update /var/lib/cobbler/snippets/yum_update

# Copy personnal kickstart

# Activate personnal snippets
RUN for kickstart in sample sample_end legacy ; \
    do \
        additional_post_snippets="" ; \
        for snippet in \
                        add_repos \
                        disable_prelink \
                        systemd_persistant_journal \
                        rkhunter \
                        enable_X \
                        yum_update ; \
        do \
          additional_post_snippets="${additional_post_snippets}\n\$SNIPPET('${snippet}')" ; \
        done ; \
        sed -i \
           -e "/post_anamon/ s/$/${additional_post_snippets}/" \
           -e "/^autopart/ s/^.*$/\$SNIPPET('partition_config')/" \
           -e "/^skipx/ s/^.*$/\$SNIPPET('configure_X')/" \
       /var/lib/cobbler/kickstarts/${kickstart}.ks ; \
    done

# Install vim-enhanced by default and desktop packages if profile have el_type set to desktop (ksmeta)
RUN echo -e "@core\n\nvim-enhanced\n#set \$el_type = \$getVar('type', 'minimal')\n#if \$el_type == 'desktop'\n@base\n@network-tools\n@x11\n@graphical-admin-tools\n#set \$el_version = \$getVar('os_version', None)\n#if \$el_version == 'rhel6'\n@desktop-platform\n@basic-desktop\n#else if \$el_version == 'rhel7'\n@gnome-desktop\n#end if\n#end if\nkernel" >> /var/lib/cobbler/snippets/func_install_if_enabled

COPY first-sync.sh /usr/local/bin/first-sync.sh
COPY entrypoint.sh /entrypoint.sh
RUN chmod 755 /entrypoint.sh /usr/local/bin/first-sync.sh

EXPOSE 69 80 443 25151

VOLUME [ "/var/www/cobbler", "/var/lib/tftp", "/var/lib/cobbler/config", "/var/lib/cobbler/backup", "/var/run/supervisor", "/mnt" ]

ENTRYPOINT /entrypoint.sh

Lancement de la construction

docker build -t local/cobbler .

En cas de problème, ne pas hésiter à regarder le fichier /var/log/cobbler/cobbler.log

Protéger apache avec fail2ban

Cet article fait suite à l’article sur la sécuristion de son VPS, où l’on avait installé le service fail2ban et sécurisé le service SSH. On s’attaque maintenant à la protection d’Apache.

Pour éloigner les relous qui essaie d’accéder à w00tw00t ou à phpMyAdmin, Il faut ajouter les deux filtres suivant dans le répertoire /etc/fail2ban/filter.d/.

  • Fichier /etc/fail2ban/filter.d/apache-pma.conf
    # Fail2Ban configuration file
    # Bans bots scanning for non-existing phpMyAdmin installations on your webhost.
    #
    [Definition]
    # Option: failregex
    # Notes.: Regexp to match often probed and not available phpmyadmin paths.
    # Values: TEXT
    #
    failregex = [[]client []] File does not exist: .*(PMA|phpmyadmin|myadmin|mysql|mysqladmin|sqladmin|mypma|admin|xampp|mysqldb|mydb|db|pmadb|phpmyadmin1|myadmin2)
    # Option: ignoreregex
    # Notes.: regex to ignore. If this regex matches, the line is ignored.
    # Values: TEXT
    #
    ignoreregex =
    
  • Fichier /etc/fail2ban/filter.d/apache-w00tw00t.conf
    [Definition]
    # Option:  failregex
    # Notes.:  regex to match the w00tw00t scan messages in the logfile. The
    #          host must be matched by a group named "host". The tag "" can
    #          be used for standard IP/hostname matching.
    # Values:  TEXT
    failregex = ^ -.*"GET \/w00tw00t\.at\.ISC\.SANS\.DFind\:\).*".*
    # Option:  ignoreregex
    # Notes.:  regex to ignore. If this regex matches, the line is ignored.
    # Values:  TEXT
    ignoreregex =
    

Maintenant pour activer les règles concernant apache (incluses dans fail2ban) et nos 2 règles personnelles, on ajoute au fichier jail.local, les lignes suivantes.

Fichier /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 X.X.X.X
...
[apache-auth]
enabled      = true
maxretry     = 2
journalmatch =
backend      = polling

[apache-badbots]
enabled      = true
maxretry     = 1
journalmatch =
backend      = polling

[apache-noscript]
enabled      = true
maxretry     = 1
journalmatch =
backend      = polling

[apache-overflows]
enabled      = true
maxretry     = 1
journalmatch =
backend      = polling

[apache-nohome]
enabled      = true
maxretry     = 1
journalmatch =
backend      = polling

[apache-botsearch]
enabled      = true
maxretry     = 1
journalmatch =
backend      = polling

[apache-modsecurity]
enabled      = true
maxretry     = 2
journalmatch =
backend      = polling

[apache-shellshock]
enabled      = true
maxretry     = 1
journalmatch =
backend      = polling

[apache-pma]
enabled      = true
maxretry     = 1
journalmatch =
backend      = polling
logpath      = %(apache_error_log)s
bantime      = 172800
findtime     = 172800

[apache-w00tw00t]
enabled      = true
maxretry     = 1
journalmatch =
backend      = polling
logpath      = %(apache_access_log)s
bantime      = 172800
findtime     = 172800

Les règles qui nécessitent d’avoir accès à des fichiers de log, devront impérativement avoir les définitions backend = polling et journalmatch = car le backend par défaut est réglé sur auto (c’est à dire systemd). D’ailleurs le fichier jail.conf contient les lignes suivantes

# Note: if systemd backend is chosen as the default but you enable a jail
#       for which logs are present only in its own log files, specify some other
#       backend for that jail (e.g. polling) and provide empty value for
#       journalmatch. See https://github.com/fail2ban/fail2ban/issues/959#issuecomment-74901200

C.Q.F.D…

Passage à Fedora 28

Le passage de Fedora 27 à Fedora 28 s’est passé sans douleur et quasiment sans incident. Bien entendu, il faut sauvegarder ses données au préalable et il peut être judicieux d’avoir à disposition un live-CD, dans le cas très improbable où la loi de Murphy fait des siennes …

Montée de version

  1. Mettre à jour la version actuelle
    sudo dnf upgrade --refresh
  2. Installer le plugin dnf de montée de version
    sudo dnf install dnf-plugin-system-upgrade
  3. Démarrer la montée de version
    • ajouter les dépôts correspondants à la nouvelle version de Fedora
    • télécharger les paquets RPM (sans les installer)
    • tester la transaction d’installation
    • etc…
    sudo dnf system-upgrade download --releasever=28

    On peut ajouter l’option ‐‐allowerasing pour régler le problème de paquet non disponible pour la nouvelle version, de dépendances cassées ou de paquets retirés.

  4. Redémarrage et application de la montée de version
    sudo dnf system-upgrade reboot
  5. Faire une longue pause. Le système va installer énormément de paquets et redémarrer (encore) une fois l’opération terminée. C’est l’étape la plus stressante pour ceux qui n’ont pas encore l’habitude de faire des montées de version.

Problèmes rencontrés

  • Le paquet vim-enhanced est en conflit avec vim-common, résolu en autorisant la suppression du paquet vim-enhanced: sudo dnf upgrade --best --allowerasing
  • Le relais SMTP vers GMail ne fonctionne plus, résolu en recréant le fichier contenant mes identifiants GMail et utilisable par postfix (sasl_passwd.db), et en redémarrant postfix.
    sudo postmap /etc/postfix/sasl_passwd && sudo systemctl restart postfix