2 votes

Vérifier si la modification du pare-feu de l'UFW m'empêcherait de me reconnecter.

Je souhaite gérer les règles de pare-feu UFW sur un certain nombre de machines Ubuntu 18.04 distantes en utilisant Ansible. Si une modification des règles du pare-feu m'empêche de me reconnecter aux machines via SSH, ce serait très difficile à réparer (aller au centre de données à la hâte, taper des mots de passe root compliqués un par un, modifier la configuration du pare-feu manuellement). Existe-t-il un moyen de vérifier que la modification d'une règle de pare-feu ne m'empêchera pas de me reconnecter avant que la modification ne soit appliquée ?

Sinon, existe-t-il un moyen de restaurer automatiquement les règles du pare-feu si elles sont appliquées et que je suis bloqué ? (Je pourrais faire ma propre sauvegarde et configurer une tâche cron pour la restaurer, puis me connecter à nouveau et supprimer la tâche cron, mais peut-être quelque chose de ce genre existe-t-il déjà).

0 votes

Vous les testez sur une autre machine, comme une VM locale.

2voto

John Mahowald Points 28597

Non intégré au module ufw. Et les changements que vous effectuez seront appliqués au prochain redémarrage ou au prochain rechargement du pare-feu.

Ce que vous pouvez faire est de recharger le pare-feu, puis de tester une nouvelle connexion au port SSH. Si cela échoue, réinitialisez ufw via la connexion persistante encore ouverte.

J'ai une implémentation de ce qui est ennuyeusement appelé ansible-role-ufw . Notez en particulier l'utilisation de wait_for comme wait_for_connection utilisera la connexion persistante et ne détectera pas l'échec.

Attention, il n'y a qu'un seul essai pour que cela fonctionne. Vous avez toujours besoin d'un accès à la console à distance pour les cas où SSH ne fonctionne pas.

0 votes

Brillant, merci beaucoup ! Cela a fonctionné pour moi, bien que j'aie dû l'étendre un peu pour obtenir le vrai nom d'hôte SSH à partir de .ssh/config. Je vais le poster comme réponse.

1voto

Gerard H. Pille Points 2424

Appliquez les règles manuellement, sans les sauvegarder, mais avant de le faire, programmez un redémarrage ou une réinitialisation des règles actuelles après quelques minutes. Ainsi, si la nouvelle règle devait causer un quelconque dommage, ce ne serait que pendant ces quelques minutes.

0 votes

Idée intéressante, mais les modules Ansible ufw ne semblent pas fournir un moyen de le faire. De plus, si les règles sont correctes, je devrais quand même les sauvegarder et m'assurer que ce que je sauvegarde est exactement ce que j'ai testé. Cela semble donc un peu plus compliqué et un peu moins sûr que de les sauvegarder et de programmer une restauration.

0voto

Ajay Gautam Points 371

Voilà ce que j'ai obtenu, en prolongeant John Mahowald Le code de l'entreprise :

roles/set_firewall_rules/tasks/main.yml

# Apply all the requested firewall rules, then try to establish a new SSH connection to the host.
# If that SSH connection fails then reset the firewall, so the user is not locked out of the machine!

# Make sure the SSH connection details figured out by target_ssh_info can actually be used to connect before the change.
# If they're not we'd end up resetting the firewall after ANY change.
- name: Try to SSH before updating firewall
  become: no
  wait_for:
    host: "{{ target_ssh_host }}"
    port: "{{ target_ssh_port }}"
    search_regex: SSH
    timeout: 5
    msg: "Failed to connect to {{ target_ssh_host }}:{{ target_ssh_port }} before firewall rule change"
  connection: local

- name: Set firewall rules
  ufw:
    src: "{{ item.src }}"
    port: "{{ item.port }}"
    proto: "{{ item.proto }}"
    rule: "{{ item.rule }}"
    comment: "{{ item.comment }}"
  register: firewall_rules
  loop: "{{ rules }}"

# Enable/reload the firewall as a separate task, after all rules have been added, so that the order of rules doesn't matter, i.e. we're not locked out
# if a deny rule comes before an allow rule (as it should).
- name: Enable and reload firewall
  ufw:
    state: enabled
  register: firewall_enabled

- name: Try to SSH after updating firewall
  become: no
  # wait_for is key here: it establishes a new connection, while wait_for_connection would re-use the existing one
  wait_for:
    host: "{{ target_ssh_host }}"
    port: "{{ target_ssh_port }}"
    search_regex: SSH
    timeout: 5
    msg: "Failed to connect to {{ target_ssh_host }}:{{ target_ssh_port }} after firewall rule change, trying to reset ufw"
  when: firewall_rules.changed or firewall_enabled.changed
  connection: local
  ignore_errors: yes
  register: ssh_after_ufw_change

# Reset the firewall if the new connection failed above. This works (mostly!), because it uses the existing connection
- name: Reset firewall if unable to SSH
  ufw:
    state: reset
  when:
    - firewall_rules.changed or firewall_enabled.changed
    - ssh_after_ufw_change.failed

# Stop the playbook - the host is now open to the world (firewall is off), which the user really needs to fix ASAP.
# It's probably better than being locked out of it, though!
- name: Fail if unable to SSH after firewall change
  fail:
    msg: "Locked out of SSH after firewall rule changes - firewall was reset"
  when:
    - firewall_rules.changed or firewall_enabled.changed
    - ssh_after_ufw_change.failed

roles/set_firewall_rules/meta/main.yml

---
dependencies:
- { role: target_ssh_info }

rôles/target_ssh_info/tasks/main.yml

# Set target_ssh_host and target_ssh_port facts to the real hostname and port SSH uses to connect.

# ansible_host and ansible_port can be set at the host level to define what Ansible passes to ssh, but ssh then looks up ansible_host in ~/.ssh/config.
# This role figure out the real hostname it then connects to - useful for establishing a non-SSH connection to the same host.
# ansible_port is similar, but a little different: if set it overrides the value in ~/.ssh/config.

- name: Get hostname from local SSH config
  shell: "ssh -G '{{ ansible_host | default(inventory_hostname) }}' | awk '/^hostname / { print $2 }'"
  connection: local
  become: no
  register: ssh_host
  changed_when: false

- name: Get port from local SSH config
  shell: "ssh -G '{{ ansible_host | default(inventory_hostname) }}' | awk '/^port / { print $2 }'"
  connection: local
  become: no
  register: ssh_port
  changed_when: false
  when: ansible_port is not defined

# ansible_port overrides whatever is set in .ssh/config
- name: Set target SSH host and port
  set_fact:
    target_ssh_host: "{{ ssh_host.stdout }}"
    target_ssh_port: "{{ ansible_port | default (ssh_port.stdout) }}"

Notez que ssh -G renvoie le nom d'hôte et le port même s'ils ne sont pas surchargés dans .ssh/config, c'est-à-dire que ssh -G arbitrarystring renvoie juste "arbitrarystring" comme nom d'hôte et 22 comme port.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X