Pull to refresh

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

Reading time3 min
Views20K

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
Tags:
Hubs:
Total votes 15: ↑15 and ↓0+15
Comments11

Articles