Durcissez vos rôles Ansible avec OpenScap
Publié le :

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 vagrantEnsuite dans le fichier molecule.yml de ce scenario il faut utiliser cette
configuration de plateforme :
---dependency:  name: galaxydriver:  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: 2222provisioner:  name: ansibleverifier:  name: ansiblePour 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.xmlDocument type: Source Data StreamImported: 2022-09-30T15:23:16
Stream: scap_org.open-scap_datastream_from_xccdf_ssg-ubuntu2204-xccdf-1.2.xmlGenerated: (null)Version: 1.3Checklists:        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/2Checks:        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.xmlDictionaries:        Ref-Id: scap_org.open-scap_cref_ssg-ubuntu2204-cpe-dictionary.xmlPour 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 listINFO     Running default > listINFO     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 vagrantmolecule login --scenario-name vagrantUtilisation 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.2J’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: trueA l’installation d’OpenScap et des contents (sa configuration), je demande à lancer un scan.
Allez, on lance notre converge :
molecule converge --scenario-name vagrantAu 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].

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.

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].

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.ymlOn 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 %sOn relance le converge et on check le rapport. Et ????

YOUPPPPPIIIIII !!!
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.