web-dev-qa-db-fra.com

Limiter en toute sécurité les livres de lecture Ansible à une seule machine?

J'utilise Ansible pour certaines tâches simples de gestion des utilisateurs avec un petit groupe d'ordinateurs. Actuellement, mes livres de lecture sont définis sur hosts: all et mon fichier hosts est constitué d'un seul groupe avec toutes les machines répertoriées:

# file: hosts
[office]
iMac-1.local
iMac-2.local
iMac-3.local

Je me suis souvent trouvé dans l'obligation de cibler une seule machine. La commande ansible-playbook peut limiter les lectures comme ceci:

ansible-playbook --limit iMac-2.local user.yml

Mais cela semble un peu fragile, en particulier pour un livre de jeu potentiellement destructeur. Si vous laissez de côté le drapeau limit, le livret de jeu serait exécuté partout. Étant donné que ces outils ne sont utilisés que de temps en temps, il semble utile de prendre des mesures pour une lecture à toute épreuve afin d'éviter toute attaque accidentelle dans les prochains mois.

Existe-t-il une bonne pratique pour limiter les exécutions de playbook à une seule machine? Idéalement, les cahiers devraient être inoffensifs si certains détails importants étaient omis.

182
joemaller

Il s'avère qu'il est possible d'entrer un nom d'hôte directement dans le livre de lecture. L'exécution du livre de lecture avec hosts: iMac-2.local fonctionnera sans problème. Mais c'est un peu maladroit.

Une meilleure solution pourrait être de définir les hôtes du livre de lecture en utilisant une variable, puis de passer une adresse d'hôte spécifique via --extra-vars:

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

Lancer le playbook:

ansible-playbook user.yml --extra-vars "target=iMac-2.local"

Si {{ target }} n'est pas défini, le livre de lecture ne fait rien. Un groupe du fichier hosts peut également être passé si nécessaire. Globalement, cela semble être un moyen beaucoup plus sûr de construire un livre de jeu potentiellement destructeur.

Playbook ciblant un seul hôte:

$ ansible-playbook user.yml --extra-vars "target=iMac-2.local" --list-hosts

playbook: user.yml

  play #1 (iMac-2.local): Host count=1
    iMac-2.local

Playbook avec un groupe d'hôtes:

$ ansible-playbook user.yml --extra-vars "target=office" --list-hosts

playbook: user.yml

  play #1 (office): Host count=3
    iMac-1.local
    iMac-2.local
    iMac-3.local

Oublier de définir des hôtes est sûr!

$ ansible-playbook user.yml --list-hosts

playbook: user.yml

  play #1 ({{target}}): Host count=0
175
joemaller

Il existe également une petite astuce qui vous permet de spécifier un hôte unique sur la ligne de commande (ou plusieurs hôtes, je suppose), sans inventaire intermédiaire:

ansible-playbook -i "iMac1-local," user.yml

Notez la virgule (, ) à la fin; cela signifie que c'est une liste, pas un fichier.

À présent, cela ne vous protégera pas si vous transmettez accidentellement un fichier d'inventaire réel. Ce n'est donc peut-être pas une bonne solution à ce problème spécifique. Mais c'est un truc pratique à savoir!

145
Tybstar

Cette approche se terminera si plusieurs hôtes sont fournis en vérifiant la variable play_hosts . Le module fail est utilisé pour sortir si la condition d'hôte unique n'est pas remplie. Les exemples ci-dessous utilisent un fichier hosts avec deux hôtes alice et bob.

user.yml (playbook)

---
- hosts: all
  tasks:
    - name: Check for single Host
      fail: msg="Single Host check failed."
      when: "{{ play_hosts|length }} != 1"
    - debug: msg='I got executed!'

Exécuter le livre de lecture sans filtres d'hôte

$ ansible-playbook user.yml
PLAY [all] ****************************************************************
TASK: [Check for single Host] *********************************************
failed: [alice] => {"failed": true}
msg: Single Host check failed.
failed: [bob] => {"failed": true}
msg: Single Host check failed.
FATAL: all hosts have already failed -- aborting

Exécuter le livre de lecture sur un hôte unique

$ ansible-playbook user.yml --limit=alice

PLAY [all] ****************************************************************

TASK: [Check for single Host] *********************************************
skipping: [alice]

TASK: [debug msg='I got executed!'] ***************************************
ok: [alice] => {
    "msg": "I got executed!"
}
69
Marwan Alsabbagh

Il y a IMHO un moyen plus pratique. Vous pouvez en effet demander de manière interactive à l'utilisateur la ou les machines auxquelles il souhaite appliquer le livre de lecture grâce à vars_Prompt:

---

- hosts: "{{ hosts }}"
  vars_Prompt:
    - name: "hosts"
      Prompt: "Which hosts would you like to setup?"
      private: no
  tasks:
    […]
21
Buzut

Pour développer la réponse de joemailer, si vous souhaitez que la capacité de correspondance de modèle corresponde à tout sous-ensemble de machines distantes (comme le fait la commande ansible), tout en rendant très difficile l’exécution accidentelle du playbook sur toutes les est ce que je suis venu avec:

Même livre de jeu que dans l'autre réponse:

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

Ayons les hôtes suivants:

iMac-10.local
iMac-11.local
iMac-22.local

Maintenant, pour exécuter la commande sur tous les périphériques, vous devez expliquer la valeur "all" de la variable cible.

ansible-playbook user.yml --extra-vars "target=all"

Et pour le limiter à un modèle spécifique, vous pouvez définir target=pattern_here

ou, alternativement, vous pouvez laisser target=all et ajouter l'argument --limit, par exemple:

--limit iMac-1*

c'est à dire. ansible-playbook user.yml --extra-vars "target=all" --limit iMac-1* --list-hosts

qui se traduit par:

playbook: user.yml

  play #1 (office): Host count=2
    iMac-10.local
    iMac-11.local
16
deadbeef404

Les utilisateurs AWS utilisant le script d'inventaire externe EC2 peuvent simplement filtrer par ID d'instance:

ansible-playbook sample-playbook.yml --limit i-c98d5a71 --list-hosts

Cela fonctionne car le script d'inventaire crée des groupes par défaut .

7
Frank

Depuis la version 1.7, ansible a l'option run_once . La section contient également des informations sur diverses autres techniques.

5
Berend de Boer

Nous avons des cahiers génériques utilisables par un grand nombre d’équipes. Nous avons également des fichiers d’inventaire spécifiques à l’environnement, contenant plusieurs déclarations de groupe.

Pour forcer une personne appelant un livre de jeu à spécifier un groupe auquel faire face, nous semons une entrée factice en haut du livre:

[ansible-dummy-group]
dummy-server

Nous incluons ensuite le contrôle suivant comme première étape dans le livre de jeu partagé:

- hosts: all
  gather_facts: False
  run_once: true
  tasks:
  - fail:
      msg: "Please specify a group to run this playbook against"
    when: '"dummy-server" in ansible_play_batch'

Si le serveur factice apparaît dans la liste des hôtes sur lesquels le playbook doit être exécuté (ansible_play_batch), alors l'appelant n'a pas spécifié de groupe et l'exécution du playbook échouera.

2
mcdowellstl

Je ne comprends vraiment pas comment toutes les réponses sont si compliquées.

ansible-playbook user.yml -i hosts/hosts --limit iMac-2.local --check

Le mode de vérification vous permet de fonctionner en mode sèche, sans apporter de modification.

1
knocte

J'ai un script d'emballage appelé provision qui vous oblige à choisir la cible. Je n'ai donc pas à le gérer ailleurs.

Pour ceux qui sont curieux, j'utilise ENV vars pour les options utilisées par mon vagrantfile (ajout de l'argument ansible correspondant pour les systèmes en nuage) et je laisse passer le reste des arguments anibles. Lorsque je crée et alimente plus de 10 serveurs à la fois, j'inclue une nouvelle tentative automatique sur les serveurs défaillants (tant que des progrès sont accomplis - lors de la création d'une centaine de serveurs à la fois, plusieurs échouaient la première fois ). 

echo 'Usage: [VAR=value] bin/provision [options] dev|all|TARGET|vagrant'
echo '  bootstrap - Bootstrap servers ssh port and initial security provisioning'
echo '  dev - Provision localhost for development and control'
echo '  TARGET - specify specific Host or group of hosts'
echo '  all - provision all servers'
echo '  vagrant - Provision local vagrant machine (environment vars only)'
echo
echo 'Environment VARS'
echo '  BOOTSTRAP - use cloud providers default user settings if set'
echo '  TAGS - if TAGS env variable is set, then only tasks with these tags are run'
echo '  SKIP_TAGS - only run plays and tasks whose tags do not match these values'
echo '  START_AT_TASK - start the playbook at the task matching this name'
echo
ansible-playbook --help | sed -e '1d
    s#=/etc/ansible/hosts# set by bin/provision argument#
    /-k/s/$/ (use for fresh systems)/
    /--tags/s/$/ (use TAGS var instead)/
    /--skip-tags/s/$/ (use SKIP_TAGS var instead)/
    /--start-at-task/s/$/ (use START_AT_TASK var instead)/
'
0
iheggie

C'est un peu plus compliqué si vous voulez utiliser une connexion locale. Mais cela devrait être correct si vous utilisez une variable pour le paramètre hosts et créez dans le fichier hosts une entrée spéciale pour localhost.

Dans (tous) les playbooks, les hôtes: line sont réglés sur:

- hosts: "{{ target | default('no_hosts')}}"

Dans le fichier hôtes d’inventaire, ajoutez une entrée pour l’hôte local qui définit la connexion comme étant locale:

[localhost]
127.0.0.1  ansible_connection=local

Ensuite, sur la ligne de commande, exécutez les commandes définissant explicitement la cible - par exemple:

$ ansible-playbook --extra-vars "target=localhost" test.yml

Cela fonctionnera également avec ansible-pull:

$ ansible-pull -U <git-repo-here> -d ~/ansible --extra-vars "target=localhost" test.yml

Si vous oubliez de définir la variable sur la ligne de commande, la commande commettra une erreur en toute sécurité (tant que vous n'avez pas créé de groupe d'hôtes appelé 'no_hosts'!) Avec l'avertissement suivant:

skipping: no hosts matched

Et comme mentionné ci-dessus, vous pouvez cibler une seule machine (tant qu'elle se trouve dans votre fichier hosts) avec:

$ ansible-playbook --extra-vars "target=server.domain" test.yml

ou un groupe avec quelque chose comme:

$ ansible-playbook --extra-vars "target=web-servers" test.yml
0
bailey86