176 lines
7.6 KiB
YAML
176 lines
7.6 KiB
YAML
---
|
||
- name: Умное сканирование сети и классификация устройств
|
||
hosts: localhost
|
||
connection: local
|
||
gather_facts: no
|
||
become: yes # !!! ВАЖНО: Для определения OS (nmap -O) нужны права root
|
||
vars:
|
||
# --- НАСТРОЙКИ SEMAPHORE ---
|
||
semaphore_url: "http://192.168.0.198:9999"
|
||
semaphore_project_id: 1
|
||
semaphore_key_id: 7
|
||
semaphore_api_token: "9ojexqiwt1xkemig7j1bd1pe-frh7hkre4reryk2occ="
|
||
inventory_name: "Smart Network Scan"
|
||
|
||
# --- СЕТИ ---
|
||
subnets:
|
||
- "192.168.0.0/24"
|
||
# добавьте другие подсети
|
||
|
||
# Порты для проверки (добавил 8291 для MikroTik, 80/443 для веб-морд, 631 для принтеров)
|
||
scan_ports: [22, 445, 5985, 80, 443, 8291, 631]
|
||
|
||
tasks:
|
||
# ----------------------------------------------------------------
|
||
# ШАГ 1: Быстрый поиск живых хостов (Ping Sweep)
|
||
# Это нужно, чтобы не сканировать пустоту тяжелым сканом
|
||
# ----------------------------------------------------------------
|
||
- name: Быстрый поиск активных IP
|
||
command: >
|
||
nmap -sn -n --min-rate 1000 -T4 -oG -
|
||
{{ subnets | join(' ') }}
|
||
register: ping_scan
|
||
changed_when: false
|
||
|
||
- name: Формирование списка живых IP
|
||
set_fact:
|
||
active_ips: "{{ ping_scan.stdout | regex_findall('Host: ([0-9.]+)') | unique | list }}"
|
||
|
||
- name: Проверка
|
||
fail:
|
||
msg: "Живых хостов не найдено."
|
||
when: active_ips | length == 0
|
||
|
||
# ----------------------------------------------------------------
|
||
# ШАГ 2: Глубокий анализ каждого IP (ОС, Версии, Имена)
|
||
# Используем -O (OS), -sV (Versions) и скрипты
|
||
# ----------------------------------------------------------------
|
||
- name: Глубокое сканирование найденных хостов
|
||
shell: |
|
||
# Запускаем Nmap с определением ОС и скриптами SMB/Banner
|
||
# -O: Определение ОС (требует sudo)
|
||
# --osscan-guess: Пытаться угадать, если не уверен
|
||
# --script: Скрипты для точного имени Windows и SSH баннеров
|
||
nmap -O -sV -p {{ scan_ports | join(',') }} --script=smb-os-discovery,banner -Pn -n -T4 {{ item }}
|
||
loop: "{{ active_ips }}"
|
||
register: deep_scan_results
|
||
changed_when: false
|
||
|
||
# ----------------------------------------------------------------
|
||
# ШАГ 3: Анализ и классификация (Магия Ansible + Jinja2)
|
||
# ----------------------------------------------------------------
|
||
- name: Классификация хостов
|
||
set_fact:
|
||
classified_hosts: []
|
||
|
||
- name: Разбор результатов сканирования
|
||
set_fact:
|
||
classified_hosts: "{{ classified_hosts + [ host_data ] }}"
|
||
vars:
|
||
output: "{{ item.stdout }}"
|
||
ip: "{{ item.item }}"
|
||
|
||
# --- ЛОГИКА ОПРЕДЕЛЕНИЯ ИМЕНИ ---
|
||
# Пытаемся найти имя через SMB, если нет - DNS, если нет - IP
|
||
smb_name: "{{ output | regex_search('Computer name: ([\\w-]+)', '\\1') | first | default('') }}"
|
||
dns_name_cmd: "nslookup {{ ip }} 192.168.1.250 | grep 'name =' | awk '{print $NF}' | sed 's/\\.$//'"
|
||
# (В реальном плейбуке nslookup внутри loop медленно, тут упрощено)
|
||
final_name: >-
|
||
{% if smb_name != '' %}{{ smb_name | lower }}
|
||
{% else %}host_{{ ip | replace('.', '_') }}{% endif %}
|
||
|
||
# --- ЛОГИКА ОПРЕДЕЛЕНИЯ ТИПА ---
|
||
host_type: >-
|
||
{% if 'Windows' in output or 'Microsoft Windows' in output or '445/tcp open' in output %}windows
|
||
{% elif 'MikroTik' in output or 'RouterOS' in output or '8291/tcp open' in output %}mikrotik
|
||
{% elif 'Linux' in output or 'Ubuntu' in output or 'Debian' in output %}linux
|
||
{% elif 'HP' in output or 'Printer' in output or '631/tcp open' in output %}printer
|
||
{% else %}other{% endif %}
|
||
|
||
# --- СОБИРАЕМ ОБЪЕКТ ---
|
||
host_data:
|
||
ip: "{{ ip }}"
|
||
name: "{{ final_name }}"
|
||
type: "{{ host_type }}"
|
||
raw_os: "{{ output | regex_search('OS details: ([^\\n]+)', '\\1') | first | default('Unknown') }}"
|
||
loop: "{{ deep_scan_results.results }}"
|
||
no_log: true
|
||
|
||
# ----------------------------------------------------------------
|
||
# ШАГ 4: Генерация инвентаря по группам
|
||
# ----------------------------------------------------------------
|
||
- name: Генерация текста инвентаря
|
||
set_fact:
|
||
inventory_content: |
|
||
# === Windows Systems ===
|
||
[windows]
|
||
{% for host in classified_hosts if host.type == 'windows' %}
|
||
{{ host.name }} ansible_host={{ host.ip }} # Detected: {{ host.raw_os }}
|
||
{% endfor %}
|
||
|
||
[windows:vars]
|
||
ansible_connection=ssh
|
||
ansible_shell_type=powershell
|
||
ansible_user=o.grechko
|
||
ansible_port=22
|
||
|
||
# === Linux Systems ===
|
||
[linux]
|
||
{% for host in classified_hosts if host.type == 'linux' %}
|
||
{{ host.name }} ansible_host={{ host.ip }} # Detected: {{ host.raw_os }}
|
||
{% endfor %}
|
||
|
||
[linux:vars]
|
||
ansible_connection=ssh
|
||
ansible_user=root
|
||
|
||
# === MikroTik Routers ===
|
||
[routers_mikrotik]
|
||
{% for host in classified_hosts if host.type == 'mikrotik' %}
|
||
{{ host.name }} ansible_host={{ host.ip }}
|
||
{% endfor %}
|
||
|
||
[routers_mikrotik:vars]
|
||
ansible_connection=network_cli
|
||
ansible_network_os=routeros
|
||
ansible_user=admin
|
||
|
||
# === Printers & Others ===
|
||
[others]
|
||
{% for host in classified_hosts if host.type == 'printer' or host.type == 'other' %}
|
||
{{ host.name }} ansible_host={{ host.ip }} # Type: {{ host.type }}, OS: {{ host.raw_os }}
|
||
{% endfor %}
|
||
|
||
# ----------------------------------------------------------------
|
||
# ШАГ 5: Отправка в Semaphore (Ваш рабочий метод через файл)
|
||
# ----------------------------------------------------------------
|
||
- name: Сохранение JSON
|
||
copy:
|
||
content: |
|
||
{
|
||
"name": "{{ inventory_name }} {{ 1000 | random }}",
|
||
"project_id": {{ semaphore_project_id | int }},
|
||
"type": "static",
|
||
"ssh_key_id": {{ semaphore_key_id | int }},
|
||
"become_key_id": null,
|
||
"repository_id": null,
|
||
"inventory": {{ inventory_content | to_json }}
|
||
}
|
||
dest: /tmp/semaphore_smart_payload.json
|
||
|
||
- name: Отправка через CURL
|
||
command: >
|
||
curl -v -X POST "{{ semaphore_url }}/api/project/{{ semaphore_project_id }}/inventory"
|
||
-H "Authorization: Bearer {{ semaphore_api_token }}"
|
||
-H "Content-Type: application/json"
|
||
-H "Accept: application/json"
|
||
-d @/tmp/semaphore_smart_payload.json
|
||
register: curl_result
|
||
ignore_errors: yes
|
||
|
||
- name: Очистка
|
||
file: path=/tmp/semaphore_smart_payload.json state=absent
|
||
|
||
- name: Результат
|
||
debug:
|
||
msg: "Проверьте инвентарь! Создан ID, если статус 200/201" |