Aller au contenu principal

Durcissez vos rôles Ansible avec OpenScap

· 9 minutes de lecture
Stéphane ROBERT
Consultant DevOps

logo devops

Cet article est la suite de celui consacrer à la maintenance des collections et des rôles Ansible avec Renovate. Aujourd'hui, nous allons voir comment durcir vos rôles avec l'aide d'OpenScap.

Si on analyse les rôles présents sur Ansible Galaxy, c'est assez rare de trouver des rôles permettant de configurer des services, des middlewares qui intègrent les quelques bonnes pratiques de sécurité. Et c'est bien dommage. La sécurisation de nos assets devraient la priorité de Tous !

Pour vous aider à réaliser cette tâche, j'ai écrit un simple rôle Ansible chargé d'installer et de lancer un scan de sécurité avec OpenScap. OpenScap est un produit RedHat Open-Source qui propose des outils d'audits permettant de sécuriser vos assets.

Ecriture du role d'installation d'OpenScap

OpenScap est normalement fourni sous forme de packages, mais pour les dernières versions de certaines Distributions Linux (comme les debian) ce n'est pas le cas. Et plutôt que d'installer le package, nous allons tout simplement builder le produit à partir de son code source. Comme cela nous profiterons de la dernière version stable.

Le code de ce rôle est disponible sur mon compte GitHub.

Comme pour tous mes rôles, j'utilise Molecule pour la partie tests. Là ce role n'en intègre pas pour le moment de tests, mais il utilise cette fois non pas le driver docker, mais celui de vagrant. En effet, j'ai constaté que dans certains cas les images Docker officiels n'intégraient pas forcément les mêmes packages et les mêmes configurations que ceux que l'on rencontre dans une installation classique avec une image ISO.

Installation et utilisation du driver molecule-vagrant

Pour installer le driver vagrant c'est assez simple via la commande pip :

pip install molecule[vagrant]

Pour créer le scénario utiliser ce driver, il faut ensuite utiliser cette commande :

molecule init scenario -d vagrant --provisioner-name ansible --verifier-name testinfra vagrant

Ensuite dans le fichier molecule.yml de ce scenario il faut utiliser cette configuration de plateforme :

---
dependency:
name: galaxy
driver:
name: vagrant
provider:
name: libvirt
provision: true
cachier: machine
parallel: false

platforms:
- name: openscap-debian
box: debian/bullseye64
# box: generic/ubuntu2204
memory: 1024
cpus: 2
interfaces:
# `network_name` is the required identifier, all other keys map to
# arguments.
- auto_config: true
network_name: private_network
type: dhcp
- network_name: forwarded_port
guest: 22
host: 2222
provisioner:
name: ansible
verifier:
name: ansible

Pour le moment, je ne travaille que des distributions Debian, mais dans les prochaines versions de ce role, j'ajouterai le support de plusieurs distributions via un système de variables. Si vous voulez l'utiliser pour d'autres, il faut simplement changer le paramètre box.

Quelques explications sur le role

Les tâches principales sont les suivantes :

- name: Include task
ansible.builtin.include_tasks:
file: "{{ ansible_distribution | lower }}{{ ansible_distribution_major_version }}.yml"
- name: Clone oscap project
ansible.builtin.git:
repo: https://github.com/OpenSCAP/openscap.git
dest: /tmp/openscap
version: "{{ oscap_version }}"
recursive: true
force: true
- name: Cmake
ansible.builtin.shell:
cmd: cmake .. -DCMAKE_INSTALL_PREFIX=/usr
chdir: /tmp/openscap/build
register: my_output
changed_when: my_output.rc != 0
tags:
- skip_ansible_lint
- name: Build OpenScap
ansible.builtin.shell:
cmd: make install
chdir: /tmp/openscap/build
become: true
register: my_output
changed_when: my_output.rc != 0
tags:
- skip_ansible_lint
- name: Install
ansible.builtin.shell:
cmd: make install
chdir: /tmp/openscap/build
become: true
register: my_output
changed_when: my_output.rc != 0
tags:
- skip_ansible_lint
- name: Install Content Block
when: install_content
block:
- name: Create folder to put ComplianceAsCode project
ansible.builtin.file:
mode: 0755
owner: root
path: "{{ item }}"
state: directory
become: true
with_items:
- /opt/openscap-content
- /tmp/openscap-reports
- name: Unzip ComplianceAsCode project
ansible.builtin.unarchive:
src: "https://github.com/ComplianceAsCode/content/releases/download/v{{ content_version }}/scap-security-guide-{{ content_version }}.zip"
dest: /opt/openscap-content
remote_src: true
extra_opts: '-j'
become: true
- name: Install Content Block
when: scan
block:
- name: Scan
ansible.builtin.shell:
cmd: "oscap xccdf eval --profile {{ openscap_profile }} --results-arf /tmp/openscap-reports/arf-{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version }}.xml --report /tmp/openscap-reports/report-{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version }}.html /opt/openscap-content/{{ openscap_security_policy }}"
become: true
register: result
failed_when: result.rc == 1
changed_when: false
tags:
- skip_ansible_lint
- name: Get reports
ansible.builtin.fetch:
src: "{{ item }}"
dest: "./"
flat: true
become: true
with_items:
- "/tmp/openscap-reports/report-{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version }}.html"
- "/tmp/openscap-reports/arf-{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version }}.xml"

Vous voyez ! Rien de bien compliqué. Pour ajouter une distribution, il faut dupliquer une existante, ubuntu22.yml par exemple et éditer son contenu pour qu'il installe les dépendances de compilation. La liste des packages se trouve dans le dépôt officiel d'OpenScap.

- name: Update apt cache
ansible.builtin.apt:
update_cache: true
when: ansible_os_family == 'Debian'
become: true

- name: Install package to build oscap
ansible.builtin.package:
name:
- cmake
- libdbus-1-dev
- libdbus-glib-1-dev
- libcurl4-openssl-dev
- libgcrypt20-dev
- libselinux1-dev
- libxslt1-dev
- libgconf2-dev
- libacl1-dev
- libblkid-dev
- libcap-dev
- libxml2-dev
- libldap2-dev
- libpcre3-dev
- python3-dev
- swig
- libxml-parser-perl
- libxml-xpath-perl
- libperl-dev
- libbz2-dev
- librpm-dev
- g++
- libapt-pkg-dev
- libyaml-dev
- libxmlsec1-dev
- libxmlsec1-openssl
- unzip
- git
state: present
become: true
- name: Set profile variables
ansible.builtin.set_fact:
openscap_profile: "xccdf_org.ssgproject.content_profile_cis_level2_server"
openscap_security_policy: "ssg-ubuntu2204-ds.xml"

Ah oui le plus important, pour le moment, j'ai codé en dure les paramètres du choix du profil et la security policy pour chaque distribs.

Pour retrouver le profile qui est fonction du profil de la distribution :

oscap info /opt/openscap-content/ssg-ubuntu2204-ds.xml
Document type: Source Data Stream
Imported: 2022-09-30T15:23:16

Stream: scap_org.open-scap_datastream_from_xccdf_ssg-ubuntu2204-xccdf-1.2.xml
Generated: (null)
Version: 1.3
Checklists:
Ref-Id: scap_org.open-scap_cref_ssg-ubuntu2204-xccdf-1.2.xml
Status: draft
Generated: 2022-09-30
Resolved: true
Profiles:
Title: CIS Ubuntu 22.04 Level 1 Server Benchmark
Id: xccdf_org.ssgproject.content_profile_cis_level1_server
Title: CIS Ubuntu 22.04 Level 1 Workstation Benchmark
Id: xccdf_org.ssgproject.content_profile_cis_level1_workstation
Title: CIS Ubuntu 22.04 Level 2 Server Benchmark
Id: xccdf_org.ssgproject.content_profile_cis_level2_server
Title: CIS Ubuntu 22.04 Level 2 Workstation Benchmark
Id: xccdf_org.ssgproject.content_profile_cis_level2_workstation
Title: Standard System Security Profile for Ubuntu 22.04
Id: xccdf_org.ssgproject.content_profile_standard
Referenced check files:
ssg-ubuntu2204-oval.xml
system: http://oval.mitre.org/XMLSchema/oval-definitions-5
ssg-ubuntu2204-ocil.xml
system: http://scap.nist.gov/schema/ocil/2
Checks:
Ref-Id: scap_org.open-scap_cref_ssg-ubuntu2204-oval.xml
Ref-Id: scap_org.open-scap_cref_ssg-ubuntu2204-ocil.xml
Ref-Id: scap_org.open-scap_cref_ssg-ubuntu2204-cpe-oval.xml
Dictionaries:
Ref-Id: scap_org.open-scap_cref_ssg-ubuntu2204-cpe-dictionary.xml

Pour lancer les commandes, comme le converge d'un autre scenario, il faut ajouter son nom derrière l'option --scenario-name. Ce qui donne :

molecule list
INFO Running default > list
INFO Running vagrant > list
╷ ╷ ╷ ╷ ╷
Instance Name │ Driver Name │ Provisioner Name │ Scenario Name │ Created │ Converged
╶───────────────┼─────────────┼──────────────────┼───────────────┼─────────┼───────────╴
debian11_base │ docker │ ansible │ default │ false │ false
base-debian │ vagrant │ ansible │ vagrant │ true │ true

molecule converge --scenario-name vagrant
molecule login --scenario-name vagrant

Utilisation du role OpenScap dans notre collection de base

Comme dit plus haut, je reprends la suite du précédent article sur la construction d'une collection de base.

Je modifie simplement le fichier requirements.yml pour intégrer mon rôle OpenScap :

---
roles:
- name: stephrobert.bootstrap
version: 1.0.3
- name: stephrobert.openscap
version: 1.0.2

J'ajoute un playbook permettant l'installation d'OpenScap :

---
- name: Install Openscap on Client
hosts: all
roles:
- role: stephrobert.openscap
vars:
- oscap_version: "1.3.6"
- install_content: true
- install_oscap: true
- scan: true

A l'installation d'OpenScap et des contents (sa configuration), je demande à lancer un scan.

Allez, on lance notre converge :

molecule converge --scenario-name vagrant

Au de quelques instants, vous devriez voir apparaître un fichier nommé report-debian-11.html (j'utilise cette fois une box Debian) dans le dossier playbooks.

Pour afficher ce rapport dans Vscode j'utilise l'extension Live Preview.

Il suffit de faire un clic droit dans l'explorateur de Vscode sur le nom de ce fichier et de sélectionner le menu [Afficher l'aperçu].

openscap report vscode ansible.

Sécurisation du service SSHD

Pour mon exemple, je vais sécuriser le service sshd. Si vous descendez dans ce fichier, vous devriez voir une zone de recherche dans lequel vous pouvez taper ssh.

openscap report vscode ansible ssh

On voit que nous avons cinq règles qui ne sont pas respectées. Comment les sécuriser ? Très simple. Cliquez sur [Show all results Details] puis sur le titre de la règle en échec que vous voulez résoudre. Cliquez sur [Remediation Ansible snippet].

openscap report vscode ansible ssh

Oh la solution toute prête. Plus qu'à l'intégrer dans votre rôle. Pour tester la remédiation sur la collection, il suffit d'ajouter une tache dans le fichier converge.yml avant le scan. Ce qui donne :

---
- name: Import bootstrap playbook
ansible.builtin.import_playbook: ../../playbooks/bootstrap.yml
- name: Securing Instance
ansible.builtin.import_playbook: ../../playbooks/securing.yml
- name: Import install OpenScap
ansible.builtin.import_playbook: ../../playbooks/install-openscap.yml

On copie le contenu de la remédiation dans le fichier playbooks/securing.yml :

---
- name: Securing VM
hosts: all
become: true
tasks:
- name: Set SSH Client Alive Count Max to zero
block:
- name: Check for duplicate values
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*ClientAliveCountMax\s+
state: absent
check_mode: true
changed_when: false
register: dupes

- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*ClientAliveCountMax\s+
state: absent
when: dupes.found is defined and dupes.found > 1

- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
regexp: (?i)^\s*ClientAliveCountMax\s+
line: ClientAliveCountMax 0
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s

On relance le converge et on check le rapport. Et ????

openscap report vscode ansible ssh

YOUPPPPPIIIIII !!!

attention

Vous ne pourrez plus dire que vous ne savez pas sécuriser vos assets !!!!

Plus loin

Bon ce n'est pas encore parfait, mais les grands principes sont là. Par exemple le role openscap pourrait être optimisé pour éviter de réinstaller les contents d'OpenScap. Il pourrait aussi compiler les packages RPM et DEB. Je n'ai pas pris le temps, car j'étais pressé de vous présenter ce tutoriel. Ça sera fait dans les prochains temps. Donc il y a encore du taf.

Openscap possède une commande oscap-ssh qui permet de lancer le scan sur des machines distantes. Les seules contraintes openscap doit être installé sur les machines et le content sur le manager ansible. On peut produire des fichiers xml (peut être json) qui sont exploitables pour faire des audits de contrôle.

Je regarde comment lancer les rapports oval pour lister les cve et je mets le billet à jour. Je recherche d'autres outils d'audits open-source à intégrer. Pour ceux que ça intéresse la documentation complète se trouve ici.

Je vais aussi changer le driver utilisé dans la CI pour poper des VM sur AWS. C'est plus simple à mettre en œuvre et on a à disposition la plupart des distributions via les AMI.

Le code source du rôle est ici. N'hésitez à forker voir à déposer des PR.