Ansible ne parvient pas à authentifier Sudo même lorsque le laissez-passer Sudo est donné

9

Problème

En utilisant la dernière version stable d'Ansible, j'ai un problème étrange où mon playbook se bloque sur un serveur pendant "Gathering_Facts" mais fonctionne correctement sur d'autres serveurs similaires lors de l'utilisation de Sudo. Sur le serveur Ansible, j'exécute en tant qu'utilisateur (utilisateur NIS) et j'utilise sudo (en tant que root) sur le serveur distant pour apporter des modifications. Si je supprime Sudo de cette configuration, tout fonctionne bien.

Installer

Versions logicielles

  • OS : RHEL 6.4
  • Version Ansible : ansible 1.8.2
  • Version Sudo :
    Sudo version 1.8.6p3
    Plugin de politique Sudoers version 1.8.6p3
    Sudoers file grammar version 42
    Sudoers I / O plugin version 1.8.6p3
    
  • Version SSH : OpenSSH_5.3p1, OpenSSL 1.0.0-fips 29 mars 2010

Carte du serveur

                   -------- User1 @ Server1: sudo -H -S -p (se bloque sur Gathering_Facts)
                  /
Utilisateur1 @ Ansible ----
                  \
                   -------- User1 @ Server2: sudo -H -S -p (fonctionne très bien)

Utilisateurs

  • Utilisateur1: utilisateur accessible NIS sur Server1 et Server2.
  • root: utilisateur root local pour chaque serveur.

Configuration ansible

Parties pertinentes de mon ansible.cfg .

ansible.cfg

sudo           = true
sudo_user      = root
ask_sudo_pass  = True
ask_pass       = True
...
gathering = smart
....
# change this for alternative sudo implementations
sudo_exe = sudo

# what flags to pass to sudo
#sudo_flags = -H
...
# remote_user = ansible

Voici un playbook de test simple pour toucher un fichier vide puis le supprimer. Vraiment, je veux juste tester si je peux obtenir qu'Ansible utilise correctement sudo sur le serveur distant. Si le playbook fonctionne, je suis en bonne forme.

TEST.yml

---
- hosts: Server1:Server2
  vars:
  - test_file: '/tmp/ansible_test_file.txt'
  sudo: yes
  tasks:
  - name: create empty file to test connectivity and sudo access
    file: dest={{ test_file }}
          state=touch
          owner=root group=root mode=0600
    notify:
    - clean
  handlers:
  - name: clean
    file: dest={{ test_file }}
          state=absent

Configuration de Sudo

/ etc / sudoers

Host_Alias SRV     = Server1, Server2
User_Alias SUPPORT = User1, User2, User3
SUPPORT SRV=(root) ALL

Cette configuration sudo fonctionne très bien sur les DEUX serveurs. Aucun problème avec sudo lui-même.

Comment je gère tout cela

Très simple:

$ ansible-playbook test.yml
Mot de passe SSH: 
mot de passe sudo [par défaut le mot de passe SSH]:

JOUER [Serveur1: Serveur2] ******************************************** ** 

RASSEMBLEMENT FAITS ************************************************ *************** 
ok: [Server2]
a échoué: [Server1] => {"failed": true, "parsed": false}

Désolé réessayez.
[sudo via ansible, key = mxxiqyvztlfnbctwixzmgvhwfdarumtq] mot de passe: 
sudo: 1 tentative de mot de passe incorrect


TÂCHE: [créer un fichier vide pour tester la connectivité et l'accès sudo] **************** 
modifié: [Server2]

AVIS: [nettoyer] ********************************************* **************** 
modifié: [Server2]

PLAY RECAP ************************************************ ******************** 
           pour réessayer, utilisez: --limit @ / home / User1 / test.retry

Server1: ok = 0 changé = 0 inaccessible = 0 a échoué = 1   
Server2: ok = 3 changé = 2 inaccessible = 0 a échoué = 0

Échoue indépendamment du fait que j'entre explicitement les mots de passe SSH / Sudo ainsi qu'implicitement (en laissant sudo passer par défaut à SSH).

Journaux du serveur distant

Server1 (échoue)

/ var / log / secure

31 décembre 15:21:10 Server1 sshd [27093]: mot de passe accepté pour l'utilisateur1 à partir du port xxxx 51446 ssh2
31 décembre 15:21:10 Server1 sshd [27093]: pam_unix (sshd: session): session ouverte pour l'utilisateur User1 par (uid = 0)
31 décembre 15:21:11 Server1 sshd [27095]: demande de sous-système pour sftp
31 décembre 15:21:11 Server1 sudo: pam_unix (sudo: auth): échec d'authentification; logname = User1 uid = 187 euid = 0 tty = / dev / pts / 1 ruser = User1 rhost = user = User1
31 décembre 15:26:13 Server1 sudo: pam_unix (sudo: auth): la conversation a échoué
31 décembre 15:26:13 Sudo Server1: pam_unix (sudo: auth): l'auth n'a pas pu identifier le mot de passe pour [User1]
31 décembre 15:26:13 Sudo Server1: Utilisateur1: 1 tentative de mot de passe incorrect; ATS = pts / 1; PWD = / home / User1; USER = root; COMMAND = / bin / sh -c echo SUDO-SUCCESS-mxxiqyvztlfnbctwixzmgvhwfdarumtq; LANG = C LC_CTYPE = C / usr / bin / python /tmp/.ansible/tmp/ansible-tmp-1420039272.66-164754043073536/setup; rm -rf /tmp/.ansible/tmp/ansible-tmp-1420039272.66-164754043073536/> / dev / null 2> & 1
31 décembre 15:26:13 Server1 sshd [27093]: pam_unix (sshd: session): session fermée pour l'utilisateur User1 

Server2 (fonctionne très bien)

/ var / log / secure

31 décembre 15:21:12 Server2 sshd [31447]: mot de passe accepté pour l'utilisateur 1 du port xxxx 60346 ssh2
31 décembre 15:21:12 Server2 sshd [31447]: pam_unix (sshd: session): session ouverte pour l'utilisateur User1 par (uid = 0)
31 décembre 15:21:12 Server2 sshd [31449]: demande de sous-système pour sftp
31 décembre 15:21:12 Sudo Server2: User1: TTY = pts / 2; PWD = / home / User1; USER = root; COMMAND = / bin / sh -c echo SUDO-SUCCESS-vjaypzeocvrdlqalxflgcrcoezhnbibs; LANG = C LC_CTYPE = C / usr / bin / python /tmp/.ansible/tmp/ansible-tmp-1420039272.68-243930711246149/setup; rm -rf /tmp/.ansible/tmp/ansible-tmp-1420039272.68-243930711246149/> / dev / null 2> & 1
31 décembre 15:21:14 Server2 sshd [31447]: pam_unix (sshd: session): session fermée pour l'utilisateur User1 

Sortie STrace

Voici la sortie de strace lors du ciblage de la commande ansible de l'utilisateur root. Commander:

while [[ -z $(ps -fu root|grep [a]nsible|awk '{print $2}') ]]; do
    continue
done
strace -vfp $(ps -fu root|grep [a]nsible|awk '{print $2}') -o /root/strace.out`

Server1

23650 select (0, NULL, NULL, NULL, {1, 508055}) = 0 (Timeout)
Prise 23650 (PF_NETLINK, SOCK_RAW, 9) = 10
23650 fcntl (10, F_SETFD, FD_CLOEXEC) = 0
23650 readlink ("/ proc / self / exe", "/ usr / bin / sudo", 4096) = 13
23650 sendto (10, "| \ 0 \ 0 \ 0L \ 4 \ 5 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0op = PAM: authentique" ..., 124, 0, {sa_family = AF_NETLINK, pid = 0, groupes = 00000000}, 12) = 124
23650 sondage ([{fd = 10, events = POLLIN}], 1, 500) = 1 ([{fd = 10, revents = POLLIN}])
23650 recvfrom (10, "$ \ 0 \ 0 \ 0 \ 2 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0b \\\ 0 \ 0 \ 0 \ 0 \ 0 \ 0 | \ 0 \ 0 \ 0L \ 4 \ 5 \ 0 \ 1 \ 0 \ 0 \ 0 "..., 8988, MSG_PEEK | MSG_DONTWAIT, {sa_family = AF_NETLINK, pid = 0, groups = 00000000}, [12]) = 36
23650 recvfrom (10, "$ \ 0 \ 0 \ 0 \ 2 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0b \\\ 0 \ 0 \ 0 \ 0 \ 0 \ 0 | \ 0 \ 0 \ 0L \ 4 \ 5 \ 0 \ 1 \ 0 \ 0 \ 0 "..., 8988, MSG_DONTWAIT, {sa_family = AF_NETLINK, pid = 0, groups = 00000000}, [12]) = 36
23650 fermer (10) = 0
23650 écriture (2, "Désolé, réessayez. \ N", 18) = 18
23650 gettimeofday ({1420050850, 238344}, NULL) = 0
Prise 23650 (PF_FILE, SOCK_STREAM, 0) = 10
23650 connect (10, {sa_family = AF_FILE, path = "/ var / run / dbus / system_bus_socket"}, 33) = 0

Server2

6625 select (8, [5 7], [], NULL, NULL) =? ERESTARTNOHAND (À redémarrer)
6625 --- SIGCHLD (sortie de l'enfant) @ 0 (0) ---
6625 écriture (8, "\ 21", 1) = 1
6625 rt_sigreturn (0x8) = -1 EINTR (appel système interrompu)
6625 select (8, [5 7], [], NULL, NULL) = 1 (dans [7])
6625 lu (7, "\ 21", 1) = 1
6625 wait4 (6636, [{WIFEXITED (s) && WEXITSTATUS (=) 0}], WNOHANG | WSTOPPED, NULL) = 6636
6625 rt_sigprocmask (SIG_BLOCK, NULL, [], 8) = 0
Prise 6625 (PF_NETLINK, SOCK_RAW, 9) = 6
6625 fcntl (6, F_SETFD, FD_CLOEXEC) = 0
6625 readlink ("/ proc / self / exe", "/ usr / bin / sudo", 4096) = 13
6625 sendto (6, "x \ 0 \ 0 \ 0R \ 4 \ 5 \ 0 \ 6 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0op = PAM: session_c" ..., 120, 0, {sa_family = AF_NETLINK, pid = 0, groupes = 00000000}, 12) = 120
6625 sondage ([{fd = 6, events = POLLIN}], 1, 500) = 1 ([{fd = 6, revents = POLLIN}])
6625 recvfrom (6, "$ \ 0 \ 0 \ 0 \ 2 \ 0 \ 0 \ 0 \ 6 \ 0 \ 0 \ 0 \ 330 \ 355 \ 377 \ 377 \ 0 \ 0 \ 0 \ 0x \ 0 \ 0 \ 0R \ 4 \ 5 \ 0 \ 6 \ 0 \ 0 \ 0 "..., 8988, MSG_PEEK | MSG_DONTWAIT, {sa_family = AF_NETLINK, pid = 0, groups = 00000000}, [12]) = 36
6625 recvfrom (6, "$ \ 0 \ 0 \ 0 \ 2 \ 0 \ 0 \ 0 \ 6 \ 0 \ 0 \ 0 \ 330 \ 355 \ 377 \ 377 \ 0 \ 0 \ 0 \ 0x \ 0 \ 0 \ 0R \ 4 \ 5 \ 0 \ 6 \ 0 \ 0 \ 0 "..., 8988, MSG_DONTWAIT, {sa_family = AF_NETLINK, pid = 0, groups = 00000000}, [12]) = 36
6625 fermer (6) = 0
6625 ouvert ("/ etc / security / pam_env.conf", O_RDONLY) = 6
6625 fstat (6, {st_dev = makedev (253, 1), st_ino = 521434, st_mode = S_IFREG | 0644, st_nlink = 1, st_uid = 0, st_gid = 0, st_blksize = 4096, st_blocks = 8, st_size = 2980, st_atime = 2014/12 / 31-16: 10: 01, st_mtime = 2012/10 / 15-08: 23: 52, st_ctime = 2014/06 / 16-15: 45: 35}) = 0
6625 mmap (NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) = 0x7fbc3a59a000
6625 read (6, "# \ n # This is the configuration fi" ..., 4096) = 2980
6625 lu (6, "", 4096) = 0
6625 fermer (6) = 0
6625 munmap (0x7fbc3a59a000, 4096) = 0
6625 ouvert ("/ etc / environment", O_RDONLY) = 6

My Guess

Server1 n'obtient pas le mot de passe correctement ou demande / attend de manière incorrecte un mot de passe. Cela ne ressemble pas à un problème Sudo ou Ansible (seuls, ils fonctionnent tous les deux très bien), mais Server1 ne semble pas recevoir les informations d'identification (ou y adhérer) d'une manière similaire à Server2. Les serveurs 1 et 2 ont des objectifs différents, il est donc possible qu'ils présentent des différences d'authentification ou de version de package, mais ils ont tous deux été construits à partir du même référentiel; par conséquent, ils ne devraient pas être SI différents.

PAM Auth

Je pensais que les systèmes avaient peut-être différentes configurations PAM, ce qui entraînait un traitement différent des mots de passe. J'ai comparé les fichiers /etc/pam.d/ (en utilisant md5sum [file]), et ils sont les mêmes entre les deux systèmes.

Les tests

Sudo STDIN

Test d' un autre problème où sudo ne lirait pas un mot de passe de STDIN, mais cela fonctionnait bien sur les deux serveurs.

Testez Sudo Ad-Hoc

-bash-4.1 $ ansible Server1 -m fichier -a "dest = / tmp / ansible_test.txt state = touch" -sK
Mot de passe SSH: 
mot de passe sudo [par défaut le mot de passe SSH]: 
Server1 | succès >> {
    "changé": vrai, 
    "dest": "/tmp/ansible_test.txt", 
    "gid": 0, 
    "groupe": "racine", 
    "mode": "0644", 
    "propriétaire": "root", 
    "taille": 0, 
    "état": "fichier", 
    "uid": 0
}

Succès! Mais pourquoi?!

TL; DR

  1. Server1 semble attendre l'invite de mot de passe sudo tandis que Server2 fonctionne très bien.
  2. L'exécution ansible"ad-hoc" sur Server1 fonctionne très bien. L'exécuter en tant que playbook échoue.

Des questions)

  • Qu'est-ce qui pourrait faire que ma configuration Ansible Sudo fonctionne correctement sur un serveur et être rejetée sur un autre?
  • Ansible exécute-t-il le «mot de passe» du mot de passe de la machine locale à la machine distante différemment lors de l'exécution ad-hoc par rapport à Playbook? J'ai supposé qu'ils seraient les mêmes.

Je pense que cela se rapproche de la simple soumission d'un rapport de bogue à la page GitHub uniquement sur le fait que l'accès sudo a des résultats différents selon que j'exécute ad-hoc ou non.

BrM13
la source

Réponses:

4

Ce que je ferais c'est d'utiliser

strace -vfp `pidof sshd`

et voyez où ça échoue.

Vérifiez également le compte, peut-être qu'il est restreint ou quelque chose, mais je parie que quelque chose ne va pas avec votre fichier / etc / hosts ou qu'il est changé dans le processus.

Iulian
la source
Merci, Lulian. J'ai appliqué quelques modifications à la question, une section étant la sortie STrace. Il est clair qu'il existe une différence entre les deux serveurs dans la façon dont ils procèdent après le démarrage du processus ansible sur le serveur distant. Les analyses ultérieures et les captures de traces étaient cohérentes.
BrM13
Je pense que vous avez besoin de plus de cette strace -vfp, faites-le manuellement sur le processus sshd supérieur et suivez la sortie. Je ne pense pas qu'après avoir lu le mot de passe, il suffit de fermer le canal comme ça avant de passer par PAM, etc.
Iulian
J'avais déjà essayé votre suggestion, mais j'ai dû manquer l'élément crucial (d'où la raison pour laquelle j'ai choisi de regarder le processus ansible dans la STrace initiale). Après un autre essai, j'ai trouvé une variable {{password}} vide en cours de transmission au lieu du vrai mot de passe. J'ai choisi de soumettre une autre "réponse" séparément, car votre réponse m'a finalement mis sur la bonne voie.
BrM13
4

En utilisant @lulian comme point d'ancrage dans cette réponse, le problème est venu à un voyou ansible_sudo_pass:défini dans les group_vars qui remplaçait le mot de passe entré pour --ask-sudo-pass.

En utilisant ce qui suit:

while [[ -z $(ps -eaf|grep 'sshd: [U]ser1@pts/1') ]]; do
    continue
done
strace -ff -vfp $(ps -eaf|grep 'sshd: [U]ser1@pts/1'|awk '{print $2}') -o /root/strace_sshd1_2.out

J'ai été en mesure de trouver ce qui write(4, "{{ password }}\n", 15)était passé au lieu du mot de passe entré. Après quelques recherches rapides, j'ai effectivement trouvé ansible_sudo_passdéfini dans mes group_vars qui remplaçait mon mot de passe entré.

En tant qu'INF pour tout le monde, la ansible_sudo_pass:définition semble avoir la priorité sur --ask-sudo-passce qui, au premier abord, semblait contre-intuitif. En fin de compte, c'est une erreur de l'utilisateur, mais la méthodologie de @ lulian dans le débogage de l'interaction SSH ainsi que la découverte de relations entre ansible_sudo_passet --ask-sudo-passdevraient être très utiles pour les autres. (Avec un peu de chance!)

BrM13
la source
1
Je dirais qu'Ansible donnant la priorité aux variables définies par fichier sur les options de ligne de commande est contre-intuitif et mauvais comportement. Curieusement, il reconnaît que cela est rompu lorsque vous passez des options avec -e, et vous pouvez peut-être contourner cela en passant une option appropriée avec -e.
Christopher Cashell