Ansible и telnet: когда нельзя, но очень хочется


Telnet is a obviously a very insecure protocol, completely insecure in fact, and we strongly want to discourage usage. We would likely refuse the pull request, to be honest — it is just asking for a world of hurt should MITM possibilities arise.
— Michael DeHaan, создатель Ansible

Абсолютно согласен. Эм, а как мне быть, когда у меня 20 тысяч legacy свитчей, которые перезагружаются/зависают из-за подключения по SSH, а часть оборудования его вообще не поддерживает?
Прошу под кат.
UPD: гайд устарел, потому что в версии 2.5 выпустили отдельный модуль telnet
docs.ansible.com/ansible/latest/modules/telnet_module.html



Особенности Ansible


SSH


В данный момент (релиз 2.3.0) в Ansible используется 3 вида соединений:

  • SSH
  • Local
  • Docker

На самом деле SSH состоит из 2 типов и отличается использованием транспорта – ssh и paramiko. Последний использовался по умолчанию до версии Ansible 1.3, современные дистрибутивы используют параметр smart (т.е. OpenSSH), значительно ускоряя работу SSH с помощью мультиплексирования (функция Control Master). Для совместимости с legacy оборудованием лучше использовать paramiko, устанавливая ключ –c paramiko при работе с модулями/плейбуками.

Важно: при аутентификации по паролю, а не по ключам вместо ssh используется sshpass.

Если необходимо, установите пакет.

sudo apt-get install sshpass

Для RHEL-based систем нужно включить EPEL

yum --enablerepo=epel install sshpass

Для SSH есть прекрасный модуль raw, отправляющий одну команду требуемому количеству хостов и отображающий вывод со всех хостов. Для Telnet нам придется воспользоваться playbook, имеющим схожий функционал.

Telnet


Для работы с Telnet мы вынуждены использовать тип соединения local. Это означает, что команды playbook будут исполняться непосредственно на хосте Ansible (на удаленных свитчах вряд ли установлен Python, хех).

Для этого устанавливаем:

sudo apt-get install telnet

или

yum install telnet

Еще понадобится pexpect, который доступен через менеджер дополнений pip. Pip устанавливается вместе с Ansible, поэтому достаточно выполнить:

pip install pexpect

Подготовка окружения завершена.

Настройка inventory


Воспользуемся стандартным файлом /etc/ansible/hosts


[test_cluster]
192.168.0.[10:25]

Итак, наши коммутаторы входят в сущность test_cluster, имеют IP-адреса со 192.168.0.10 по 192.168.0.25. Предполагается, что на них настроен единый аккаунт с правами администратора, разрешен доступ по telnet.

Создаем наш playbook в формате .yml


---

- hosts: test_cluster
  gather_facts: false
  connection: local
  tasks:
  - name: telnet,login and execute command
    ignore_errors: true
    expect:
      command: telnet "{{ inventory_hostname }}"
      responses:
        (?i)username: "admin"
        (?i)password: "12345"
        (?i)#: "{{COMMAND}}\r\nlogout\r\nexit\r\nquit"
      echo: yes
    register: telnet_output


  - name: Debug output
    debug: var=telnet_output.stdout_lines

Идем по порядку:


hosts: test_cluster

Хосты, к которым выполняется подключение

gather_facts: false

Нормально не работает с сетевым оборудованием, нужно отключить

connection: local

Pexpect есть только на хосте Ansible.

tasks:

Начинаем работу с модулями

ignore_errors: true

Неизвестно, что будет в выводе – из-за ограниченного функционала модуля expect может получиться результат FAILED. Рекомендуется отключить.

command: telnet "{{ inventory_hostname }}"

Исполняется на хосте Ansible, происходит подключение ко всем удаленным хостам.

responses:

(?i) означает, что игнорируется регистр.
До : мы указываем, что ожидаем, после – чем отвечаем.
# — проверяем, что аутентификация успешна, мы находимся в привилегированном режиме; отвечаем комбинацией переменной команды с выходом из терминала (logout/exit/quit)

Работа с expect предполагает доскональное знание CLI удаленного хоста, наличие навыков обращения с регулярными выражениями python. Например, добавление строки #: "save" будет бессмысленно, т.к. сопоставление будет происходить только по первому условию #: "{{COMMAND}}\r\nlogout\r\nexit\r\nquit"

register: telnet_output

Собираем вывод, помещаем в переменную telnet_output. Debug возвращает нам вывод в удобном виде.

Запускаем playbook c нужной командой:

ansible-playbook raw_telnet.yml  -e '{"COMMAND":"show stp"}'

Результат выполнения:

        "Command: show stp",
        "",
        "",
        "",
        "STP Bridge Global Settings",
        "",
        "---------------------------",
        "",
        "STP Status        : Enabled",
        "",
        "STP Version       : RSTP",
        "",
        "Max Age           : 20     ",
        "",
        "Hello Time        : 2      ",
        "",
        "Forward Delay     : 15     ",
        "",
        "Max Hops          : 20     ",
        "",
        "TX Hold Count     : 3      ",
        "",
        "Forwarding BPDU   : Enabled",

При желании практически все можно заменить переменными и не править playbook вообще. Конечно, хранить пароли в открытом виде тоже небезопасно, для этого в Ansible существуют Vaults.

Cсылки:


Оригинальная документация
Основы Ansible
Ansible+сети

UPD: гайд устарел, потому что в версии 2.5 выпустили отдельный модуль telnet
docs.ansible.com/ansible/latest/modules/telnet_module.html

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 11

    0
    Супер, спасибо!
      +2
      Мне вот интересно, те кто до сих пор используют apt-get вместо apt, просто привыкли набирать apt-get, не знают про это сокращение или же сохраняют в своих мануалах для совместимости со старыми версиями Debian/Ubuntu?
        +1

        apt-get есть всегда и пример точно сработает, а apt — появился в убунте16.04, то бишь год назад. 7х дебианов еще полно, потому, что "не надо трогать" :) А статья про админство, где, зачастую, лучше постарше, но постабильнее

          0

          В 14.04 тоже есть, но умеет значительно меньше, чем в 16.04. Но CLI глазу приятный и на четыре клавиши нажимать меньше в самых типовых сценариях — это решает, использую всегда, когда вспоминаю :-)

          +1
          На текущем Debian для apt не работает bash-completion из коробки, так что «сокращиние» выходит весьма сомнительным.
            +1

            Заинтересовался и потыкал палочкой этот apt. --no-install-recommends не умеет, --print-uris не умеет, showpkg не умеет. apt-get + apt-cache умеют гораздо больше, не вижу смысла использовать ещё и apt. Ну и как сказал Gaernebjorn, систем на precise, trusty и старых релизах дебиан ещё много.

              0

              Если не изменяет память, то apt предназначен прежде всего для работы ручками из терминала, а для скриптов рекомендуется apt-get, apt-cache и т. д.

              +1

              Читал все эти apt-get install sshpass и pip install pexpect и думал, что можно же роль написать...


              Картинка великолепная, очень подходит к статье. Если кто-то ещё не видел источник, то вот:


              telnet towel.blinkenlights.nl
                0
                Я вот пытаюсь понять, при динамическом inventory файле как люди не раздражаются ограничиваться json выводом?
                Я костыле-писатель, формирую файл баш-скриптом и всегда указываю
                ansible-playbook example.yml -i `inventory.sh`
                

                Как обходить дупликацию хостов находящихся в разных сущностях, и нужно ли это делать?

                И что-то до меня никак не доходит, как засунуть в сущность В все хосты, кроме тех что в сущности А, пробовал так:
                host4
                host5
                host6
                [groupA]
                host1
                host2
                host3
                [groupB]
                all:!groupA
                [groupC]
                host7
                host8
                host9
                
                  0
                  Приветствую, уважаемый коллега костыле-писатель! Мы-то с Вами знаем, что лучше дописывать костыли, чем изобретать велосипеды!

                  При исполнении достаточно сделать так:
                  ansible-playbook --limit 'all:!groupA' playbook.yml

                  или в плейбуке:

                  — hosts: 'all:!groupA'

                  А что с JSON-выводом? Знаю, что можно сконвертить вывод в YAML или включить regex-фильтры. Взглянул бы на Ваши примеры.
                    0
                    При исполнении то понятно, как это перенести в инвентарный файл?
                    Вот пример скрипта который готовит инвентарный файл из днс записей для ансибла, для безопасности на днс сервере все запросы TXT записей запрещены.
                    Вот пример данных.
                    Соответственно, зачем усложнять такой примитив?

                    Проблема в том что в чистом sh нету возможности обработки и вывода json формата.

                Only users with full accounts can post comments. Log in, please.