Sécuriser son serveur Linux avec Ansible

Introduction

Dans cet article nous allons voir comment utiliser Ansible pour bootstraper et sécuriser rapidement des serveurs sous Ubuntu Server 14.04. Le but de cet article n’est pas de faire un bastion imprenable mais de mettre en place quelques règles pour se protéger contre les attaques de brute force ssh.

Nous commencerons par dresser la liste des tâches de base que nous pensons nécessaires pour sécuriser un serveur puis nous vous présenterons Ansible et pourquoi notre choix s’est tourné vers cet outil.

Le b.a.-ba de la sécurisation d’un serveur

Les astuces présentées ici ne sont pas exhaustives et ne sont qu’un premier pas vers la sécurisation de votre serveur.

Première étape : sécurisation du serveur ssh

La sécurisation du serveur ssh passe par 4 étapes :

  • la désactivation de la connexion via l’utilisateur root
  • l’ajout de bob comme utilisateur pouvant se connecter via ssh
  • la désactivation de la connexion via mot de passe (utilisation des clés ssh)
  • changement du port par défaut du serveur ssh

Deuxième étape : protection contre les attaques de brute force avec fail2ban

fail2ban est un outil qui analyse les logs de différents service (ssh, nginx, apache…) pour détecter des intrusions et bannir les ip des attaquants.

http://doc.ubuntu-fr.org/fail2ban

Troisième étape : configurer simplement son firewall avec ufw

ufw est un outil en ligne de commande qui permet très simplement de configurer son firewall.

http://doc.ubuntu-fr.org/ufw

Nous allons vous montrer comment utiliser Ansible pour appliquer ces règles simples.

Pourquoi Ansible ?

Ansible est un outil de déploiement automatisé dans la même veine que Chef, Puppet ou Salt.
Ce qui nous a particulièrement plu avec Ansible c’est sa simplicité. Une demi-journée suffit pour être déjà productif. Attention toute fois, nous vous montrons comment bootstrapper un serveur simple, dans le cas d’une architecture plus conséquente, il est possible d’utiliser d’autres mécanismes d’Ansible (ex : mode pull qui utilise un agent récupérant sa configuration depuis un serveur central).

Ansible peut fonctionner sans agent, ce qui permet de simplier la configuration rapide d’un serveur. Il suffit d’une connexion ssh et de python installé sur la machine distante. Par ailleurs, Ansible est fourni avec un grand nombre de modules ce qui permet de ne pas avoir à réinventer la roue.

Implémentation

Pré-requis

Avoir un compte utilisateur avec droit de sudo sur le serveur différent de root. Avoir généré une clé ssh pour l’utilisateur Bob. Dans la suite nous supposons que cet utilisateur est “bob” et que le serveur à bootstraper s’appelle « bob_server ».

L’installation d’Ansible sur votre poste est déjà bien documentée ici, nous ne reviendrons donc pas sur ce point.
Nous allons reprendre chacun des points précédents et voir comment les implémenter. Avant cela, voici une petite explication de l’architecture d’un projet Ansible:


my_ansible
- roles
  - role1
  - role2
    - tasks
    - handlers
     - templates
     - files
     - vars
production
site.yml

Les rôles

Les différentes actions à effectuer sur les serveur sont définies dans des rôles qui sont placés dans le dossier roles. Dans chacun des roles on retrouvre differents dossiers :

tasks : regroupe les actions à effectuer sur les serveur, par exemple, installer un paquet.
handlers : regroupe les action ra effectuer suite à une notification, par exemple redémarrer un service
templates : contient des templates de fichier qui seront remplis et copier sur le serveur lors de l’exécution d’Ansible
files : contient les fichiers plats qui seront copiés sur le serveur.
vars : regroupe les variables relatives au rôle

Définition des serveurs

La définition des serveurs se fait dans le fichier production. On peut regrouper les serveurs dans des groupes

1
2
3
# ansible_path/production
[NOM_DU_GROUPE]
bob_server ansible_ssh_port=SSH_PORT ansible_ssh_host=SERVEUR_IP ansible_connection=ssh ansible_ssh_user=bob

Affectation des rôles

Le fichier site.yml décrit les rôles à appliquer pour une machine (ou un groupe de machines) donnée. Par exemple :

1
2
3
4
5
6
# ansible_path/site.yml
- hosts: NOM_DU_GROUPE
  remote_user: bob
  roles:
    - role: ROLE1
    - role: ROLE2

Première étape : sécurisation du serveur ssh

Préambule

Nous allons commencer par créer un role ssh. Pour cela on crée le dossier ssh dans le dossier roles :
ansible_path/roles/ssh/

Nous allons avoir besoin de créer un handler pour pouvoir redémarrer le service ssh une fois les modifications effectuées.

1
2
3
# ansible_path/roles/ssh/handlers/main.yml
- name: restart ssh
  service: name=ssh state=restarted

Les trois changements de configuration du serveur ssh se font en modifiant des lignes du fichier /etc/ssh/sshd_config

Désactivation de la connexion via l’utilisateur root

1
2
3
4
5
# ansible_path/roles/ssh/tasks/main.yml
- name: Disallow root SSH access
  lineinfile: dest=/etc/ssh/sshd_config regexp="^PermitRootLogin" line="PermitRootLogin no" state=present
  notify:
    - restart ssh

lineinfile permet de modifier un fichier en lui passant une regex en entrée et la ligne à écrire à la place.
notify restart ssh fait référence au handler créé précédement.

Ajout de bob dans les utilisateurs pouvant se connecter en ssh

Le but est de n’autoriser que bob à se connecter au serveur via ssh.

1
2
3
4
5
# ansible_path/roles/ssh/tasks/main.yml
- name: Allow only bob user to login
  lineinfile: dest=/etc/ssh/sshd_config regexp="^AllowUsers" line="AllowUsers bob" state=present
  notify:
    - restart ssh

Ajout de la clé ssh de bob

Ajouter la clef ssh de l’utilisateur bob dans le fichier ~/.ssh/authorized_keys permet à l’utilisateur de se connecter sans mot de passe.

1
2
3
# ansible_path/roles/users/tasks/main.yml
- name: Add bob authorized_keys file
  template: src=public_keys/ssh_keys dest=/home/bob/.ssh/authorized_keys owner=bob group=bob

Suppression de l’authentification par mot de passe

1
2
3
4
5
# ansible_path/roles/ssh/tasks/main.yml
- name: Disallow password authentication
  lineinfile: dest=/etc/ssh/sshd_config regexp="^PasswordAuthentication" line="PasswordAuthentication no" state=present
  notify:
    - restart ssh

Changement du port par défaut du serveur ssh

Pour que la variable soit disponible dans les différents roles (ssh et ufw pour le firewall), nous allons rajouter la variable ssh_port dans le fichier de variable du serveur. Le nom du fichier a le même nom que le serveur décrit dans ansible_path/production

1
2
# ansible_path/host_vars/bob_server
ssh_port: MON_PORT
1
2
3
4
5
# ansible_path/roles/ssh/tasks/main.yml
- name: change SSHD port
  lineinfile: dest=/etc/ssh/sshd_config regexp="^Port 22" line="Port{{ ssh_port }}" state=present
  notify:
    - restart ssh

Deuxième étape : protection contre les attaques de brute force avec fail2ban

Pour mettre en place fail2ban, il suffit simplement de l’installer :

1
2
- name: Install fail2ban
  apt: name=fail2ban state=installed

La configuration de base permet de protéger le serveur ssh, à savoir bannir un utilisateur 10 min lors de 6 tentatives infructueuses. Vous pouvez évidement durcir cette règle en modifiant la configuration de fail2ban.

Troisième étape : configurer simplement son firewall avec ufw

Ansible a déjà un module ufw intégré. Voici un exemple de configuration :

1
2
- name : Enable UFW and close all ports
  ufw: state=enabled policy=deny
1
2
3
- name : Allow all access to tcp port SSH_PORT, 80 and 443
  ufw: rule=allow port={{item}} proto=tcp
  with_items: [ "{{ ssh_port }}" ,80,443]

Conclusion

Sécuriser son serveur contre les attaques les plus classiques et systémiques est à la porté de tous. Avec Ansbile il est simple d’automatiser ce processus.

Ansible nous permet d’être très rapidement opérationnel. Trois points nous ont particulièrement séduits :

  • la possibilité d’utiliser un grand nombre de modules « build-in » (ufw, users, ssh-keys …)
  • ne nécessite pas d’agent sur le serveur
  • la gestion à la fois des rôles et de l’inventaire dans le même outil

Le fait d’utiliser un outil comme Ansible permet de savoir à tout instant ce qui a été configuré sur le serveur. Ça permet de réinstaller le serveur sans douleur en une commande, et de savoir à tout moment ce qui a été installé et comment ont été configuré les logiciels. Ça demande un peu de rigueur pour ne rien faire « à la main » sur le serveur, mais le jeu en vaut la chandelle.
Ça peut être aussi bien utiliser pour gérer un seul serveur qu’une ferme de serveur.

Il est possible d’utiliser des outils comme serverspec pour tester les changements sur le serveur. Ça permet de tester la gestion de configuration.

Le code source de cette article est disponible ici :
https://github.com/TeaBough/bootstrap-server-ansible.git