Aller au contenu principal

Durcissez vos rôles Ansible avec OpenScap

· 9 minutes de lecture
Stéphane ROBERT
Consultant 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].

.

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

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.