Durcissez vos rôles Ansible avec OpenScap
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: 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: 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.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.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 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 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 !!!
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.