Ansible – Instalar SSH usando processo de automação

Olá Pessoal,

   Já faz um bom tempo que não escrevo aqui no BLOG, mas tudo isso é devido outras tarefas que estão tomando também um certo tempo ( recertificação CCIE, estudos DevOps, projetos particulares, aulas, treinamentos, etc ). Enfim, hoje venho aqui trazer uma situação real que acredito que diversos leitores de nosso canal podem vivenciar em sua infra-estrutura e de alguma forma já fizeram esse questionamento quando começei aqui explicar sobre Ansible 💡 . Você pode conferir nossos posts sobre Ansible aqui.

   De fato, vocês observaram que a todo momento o que implementamos usando essa ferramenta foi utilizando o processo de acesso remoto a nossos equipamentos, é claro, executando as operações necessarias via CLI. Mas qual é esse acesso remoto? SSH.  :mrgreen: 

   Pois bem, se observarmos todos os módulos para Networking os acessos aos equipamentos é feito pela biblioteca ” Paramiko “, e antes da versão 2.4 do Ansible não tinhamos nenhum módulo para utilizarmos os acessos via Telnet. Devido a isso, foi criado na versão 2.4.0  um módulo para que pudessemos avaliar os equipamentos ou executar algum comando ( CLI ) pelo acesso via Telnet. Esse módulo você pode conferir através desse link ( Telnet no Ansible ).

   Baseado nesses fatos, a ideia é acessar os equipamento via telnet, implementar script para habilitar o SSH, e após essa configuração toda a nossa infra-estrutura já estaria apta para utilizar-se de uma metodologia de maior segurança para acesso remoto aos equipamentos e pronto para trabalharmos com qualquer outro módulo disponível dentro da estrutura do Ansible. O que posso dizer é, mais ou menos!!! 

   Como estamos pensando em automação vamos pontuar os ítens para que isso seja efetivo:

  • Acessar o equipamento via telnet.
  • Verificar se o equipamento tem o ssh habilitado.
  • Qual a versão implementada do SSH.
  • Caso negativo, devemos instalar o script para acesso via SSH e importar a chave SSH para o host remoto da gerência para evitarmos as perguntas se queremos aceitar a chave em nosso primeiro acesso.
  • Caso positivo, devemos importar a chave SSH para o host remoto da gerência para evitarmos as perguntas se queremos aceitar a chave em nosso primeiro acesso.

   As dificuldades encontradas com o módulo telnet do Ansible é de fato capturar alguns logs ( utilizar pipe ) e fazer as tratativas dos outputs para obter somente o necessário, então o desafio foi encontrar biblioteca para usarmos nesta demanda. Depois de avaliar diferentes caminhos/testes e pedir ajuda à grupo discussão no SLACK Networktocode ( onde indico para todos com intuito adquirir experiências com essas pessoas, e claro, de uma comunidade que de fato ajuda muito nas dúvidas e idéias para desenvolvermos nossas soluções ), verifiquei poderíamos trabalhar com a biblioteca ” ntc-ansible “, pois ela já oferece o acesso via telnet e temos diversos outputs ” show commands ” formatados JSONJavaScript Object Notation “, onde a comunidade executou via TextFSM, trazendo assim esse ” array ” para ficar de fato mais pratico a consulta de nossos outputs. Mas, nossa vida não é fácil, pois esse comando que é necessario em minha playbook não tinhamos pronto, porém não foi impactante devido a conseguir executar os filtros e capturar o output essencial do comando.

   Para nossa estrutura de arquivos e pastas eu desenvolvi desta forma.

   Vocês podem observar que temos uma estrutura de ” roles “, ” templates ” e ” output ” onde a principal pasta para corresponder a nossa playbook estaria dentro de roles, desta forma podemos fazer nossas validações ” conditionals ” na playbook e trabalhar baseado nos outputs/status para as execuções das tarefas posteriores. Nosso arquivo raiz para iniciar implementação estaria em ” ssh_config.yaml “.

---
- name: INSTALL SSH IN CISCO DEVICES
  hosts: routers-client
  gather_facts: no
  connection: local
  vars:
    template_dir: /home/rodrigo/Documents/ansible/ntc-ansible/ntc-templates/templates/

  tasks:
  - name: OBTAIN LOGIN CREDENTIALS
    include_vars: secret.yaml

  - name: INCLUDE SSH ENABLE ROLE
    include_role:
      name: ensure_ssh_enabled

  - name: INCLUDE SSH KEY ON THE PATH .ssh/known_hosts
    include_role:
      name: get_ssh_key

   Como podem observar a primeira validação é garantir se temos implementado o SSH, e caso negativo, iremos fazer a implementação de nosso script. Dentro da estrutura de roles podemos ter diversos arquivos denominados com o mesmo nome, porém ele sempre irá buscar o arquivo main.yaml que é a base de nossa role.

---
- name: ACCESS BY TELNET
  ntc_show_command:
      connection: telnet
      platform: cisco_ios
      template_dir: "{{ template_dir }}"
      command: "{{ item }}"
      host: "{{ inventory_hostname }}"
      username: "{{ creds['username'] }}"
      password: "{{ creds['password'] }}"
      secret: "{{ creds['auth_pass'] }}"
  with_items:
      - show ip ssh | include SSH Enabled - version 2
  register: ssh_telnet

- name: ENABLE SSH IF REQUIRED
  include_tasks: enable_ssh.yaml
  when: "ssh_telnet.results.0.response.0 != 'SSH Enabled - version 2.0'"

   Para ficar mais claro vocês podem observar no debug ( via ah-hoc command ” -vvv ” ) output sendo demostrado com valor essencial para validar via condição ( when ) de qualquer coisa que seja diferente de versão 2 , então ele irá chamar nossa próxima task para implementar nosso script.

PS.: Nessa execução o valor é o mesmo comparado portando ele esta executando o ” skipping

   Em nossa próxima task temos novamente o acesso sendo executado via telnet e a implementação dos comandos necessário para subirmos o protocolo SSH, liberando assim esse equipamento para seus devidos fins.

---
- name: INSTALL SSH TEMPLATE
  ntc_config_command:
      connection: telnet
      platform: cisco_ios
      host: "{{ inventory_hostname }}"
      username: "{{ creds['username'] }}"
      password: "{{ creds['password'] }}"
      secret: "{{ creds['auth_pass'] }}"
      commands_file: "/home/rodrigo/Documents/ansible/IWAN Project/ssh_installation_by_telnet/template/ssh_configuration.txt"

   Após toda essa validação e configuração ( se necessario ) nós precisamos fazer com que nosso host tenha essa chave inserida em seu arquivo de controle ( ~/.ssh/known_hosts ) para que ele não faça essa pergunta chata quando for acessar pela primeira vez seu equipamento.

   Para isso, iremos ter nossa próxima role para trazer essas validações e inserções dentro do arquivo necessário. Após chamar role ” get_ssh_key ” iremos novamente ter nosso arquivo main.yaml sendo executado e nele iremos tratar as informações.

---
- name: ACCESS BY TELNET
  ntc_show_command:
      connection: telnet
      platform: cisco_ios
      template_dir: "{{ template_dir }}"
      command: "{{ item }}"
      host: "{{ inventory_hostname }}"
      username: "{{ creds['username'] }}"
      password: "{{ creds['password'] }}"
      secret: "{{ creds['auth_pass'] }}"
  with_items:
      - show ip ssh | begin ^ssh-rsa
  register: ssh_key

- name: Save output in variable related inline command
  set_fact: myvar="{{ ssh_key.results.0.response.0 }}"
  when: ssh_key is defined

- name: Replace space inside myvar
  set_fact:
    clean: "{{ myvar.replace('\n','') }}"
  when: ssh_key is defined

- name: ADD KEY INSIDE LOCALHOST OF SSH CONNECTION
  lineinfile:
    path: ~/.ssh/fake_hosts
    state: present
    line: "{{ inventory_hostname }} {{ clean }}"
    insertafter: EOF
  when: ssh_key is defined

   Um dos problemas enfrentados nesse tópico foi justamente a comparação da chave existente com o fato de possívelmente eu já ter essa chave inserida em seu arquivo de hosts, portanto dependendo de sua infra-estrutura essas informações poderiam ficar duplicadas e também a cada execução ele salvar a mesma chave em seu arquivo. Para validação no arquivo temos um módulo muito interessante chamado ” lineinfile ” que de fato ele pesquisa no arquivo se temos essa informação, caso negativo, ele insere o que foi solicitado. Como dito o problema sobre a inserção toda vez que executava a playbook ficava atrelado algum espaço ( \n ) que ele tinha do output ficando interpretável para o modulo lineinfile, com isso precisou ser feito um ” replace ” desses espaços para que pudesse comparar minha variável com o arquivo.

PS.: Vocês vão observar que criei um arquivo ” fake_hosts ” justamente para que você não fique testando dentro de seu arquivo de produção e de alguma forma possa impactar aquilo que esta funcionado, portanto criei o arquivo para você validar os seus passos e analisar se esta sendo executado da maneira correta, pois no momento de rodar talvez em cima de 100, 300 devices o rollback pode ficar complicado. ( rsrs )

   Vamos partir do principio que não temos nada instalado em nosso equipamento e a configuração que será implementado é conforme abaixo. 

!
username ssh privilege 15 password 0 sshansible
!
ip domain-name cisco.com
!
crypto key generate rsa modulus 2048
!
ip ssh version 2
ip ssh source-interface g3
ip ssh time-out 5
!
line vty 0 4
 login local
 transport input all
!

   Abaixo você pode conferir que removi toda a configuração de ssh e deletei no meu arquivo de teste para garantir que não temos nada populado.

   Com isso, em nosso próximo output você poderá observar que de fato não consigo fazer nenhum acesso SSH, e após execução de nossa playbook ( capturando duas changes ) temos de fato implementado nosso script e chave inserida. O erro mencionado abaixo em meu detalhamento refere-se ao fato de eu não ter salvo nossa chave SSH no arquivo correto, devido ao que foi comentando logo acima.

   Para garantir podemos acessar nosso equipamento e consolidar as informações relacionados ao script implementado, bem como, adicionei um novo usuario.

   Validando nosso arquivo ” fake_hosts ” para garantir que temos a mesma chave inserida no arquivo e comparado ao nosso equipamento.
   Vale lembrar que, o foi adicionado em nosso arquivo de ” known_hosts ” é o hostname, portanto se você acessa seus equipamentos pelo nome ele terá essa menção adicionado no arquivo, porém se o DNS não resolver esse nome você terá que acessar pelo endereço IP e que nesse caso não é a mesma informação que populamos em nosso arquivo de “ known_hosts “, sendo assim ele irá questionar para adicionar sua chave.

   Logo abaixo, coloco alguns exemplo para garantir o correto funcionamento, colocando hipóteses de alguns itens que podemos obter durante a implementação que diversos hosts. 

   Nesse caso foi removido apenas ” no ip ssh version 2 ” para que possamos implementar o script, mas não inserir chaves duplicadas em nosso arquivo.

   Declaro que esse post ficou bem grande, porém quis demonstrar todas as possibilidades colocando os outputs e anuências que foram vivenciadas até o desenvolvimento dessa automação.

   Espero que vocês tenham gostado e de fato ajude de alguma forma a solucionar problemas vivenciados em seu dia a dia. Fique a vontade de compartilhar esse artigo e de colocar mais sugestões ou melhorias para essa estrutura.

   Esses códigos estão disponivéis também no meu Github.  😛 

Abs,
Rodrigo

0
0

Link permanente para este artigo: https://ciscoredes.com.br/2017/12/21/ansible-instalar-ssh-usando-processo-de-automacao/

Deixe um comentário

Seu e-mail não será publicado.

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Translate