Work version
This commit is contained in:
commit
dac9a1e3f4
197 changed files with 14953 additions and 0 deletions
0
.ansible/.lock
Normal file
0
.ansible/.lock
Normal file
26
.gitea/workflows/yamllint_and_pass_check.yml
Normal file
26
.gitea/workflows/yamllint_and_pass_check.yml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
name: YAML Lint
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
yamllint:
|
||||
runs-on: ubuntu-yamllint
|
||||
steps:
|
||||
- name: Checkout linting
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run yamllint with auto-detected config
|
||||
run: |
|
||||
yamllint -c .yamllint .
|
||||
gitleaks:
|
||||
runs-on: ubuntu-yamllint
|
||||
steps:
|
||||
- name: Checkout passwords leaks
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Run gitleaks
|
||||
run: gitleaks detect --source . --verbose
|
||||
...
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
.vscode/
|
||||
7
.gitleaks.toml
Normal file
7
.gitleaks.toml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[extend]
|
||||
useDefault = true
|
||||
|
||||
[allowlist]
|
||||
paths = [
|
||||
# '''roles/logs_settings_on_ptaf/templates/filebeat.yml.j2''',
|
||||
]
|
||||
11
.yamllint
Normal file
11
.yamllint
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
extends: default
|
||||
|
||||
ignore: |
|
||||
roles/filebeat_install/files/fields.yml
|
||||
.gitea/workflows/yamllint.yml
|
||||
|
||||
rules:
|
||||
line-length:
|
||||
max: 180
|
||||
...
|
||||
40
README.md
Executable file
40
README.md
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
# waf_playbooks
|
||||
|
||||
Репозиторий для всех задач решаемых на WAF с помощью ansible.
|
||||
|
||||
Все изменения должны делаться в новых ветках и потом мерджиться в master.
|
||||
|
||||
При пуше коммитов в гит происходит проверка кода на соответствие правилам оформления кода с помощью `yamllint`, а также проверка на утечки с помощью `gitleaks`. Если проверка не будет пройдена, то не будет возможности смёрджить изменения в основную ветку. Так что если ваш коммит н еможет пройти какую-либо проверку, то можно запустить проверку этими же инструментами локально и найти в чём проблема.
|
||||
|
||||
## Dockerfile для yamllint и gitleaks
|
||||
```bash
|
||||
FROM docker.gitea.com/runner-images:ubuntu-latest
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y yamllint && \
|
||||
apt-get install -y wget && \
|
||||
wget https://github.com/gitleaks/gitleaks/releases/download/v8.18.4/gitleaks_8.18.4_linux_x64.tar.gz && \
|
||||
tar -xzf gitleaks_8.18.4_linux_x64.tar.gz && \
|
||||
mv gitleaks /usr/local/bin/ && \
|
||||
rm gitleaks_8.18.4_linux_x64.tar.gz && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
## Docker-compose.yml для yamllint и gitleaks
|
||||
```bash
|
||||
version: '3'
|
||||
services:
|
||||
act_runner:
|
||||
image: gitea/act_runner:latest
|
||||
privileged: true
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./runner_data:/data
|
||||
environment:
|
||||
- GITEA_INSTANCE_URL=https://svc-git.cirex.ru
|
||||
- GITEA_RUNNER_REGISTRATION_TOKEN=EQcod
|
||||
- GITEA_RUNNER_NAME=my-docker-runner
|
||||
- GITEA_RUNNER_LABELS=ubuntu-yamllint:docker://ubuntu-yamllint:latest,ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest,ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04
|
||||
restart: unless-stopped
|
||||
```
|
||||
30
ansible.cfg
Executable file
30
ansible.cfg
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
[defaults]
|
||||
inventory = ./inventory/hosts.yml
|
||||
host_key_checking = False
|
||||
pipelining = True
|
||||
forks = 10
|
||||
strategy = free
|
||||
stdout_callback = default
|
||||
callback_whitelist = default
|
||||
callback_default_result_format = yaml
|
||||
remote_user = ansible
|
||||
python_interpreter = /usr/bin/python3
|
||||
gathering = smart
|
||||
fact_caching_timeoout = 86400
|
||||
fact_caching = jsonfile
|
||||
fact_caching_connection = /tmp/ansible_fact_cache
|
||||
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
become_method = sudo
|
||||
become_user = root
|
||||
|
||||
[persistent_connections]
|
||||
pipelining = True
|
||||
|
||||
[ssh_connection]
|
||||
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
|
||||
retries = 3
|
||||
|
||||
[logging]
|
||||
log_path = /var/log/ansible.log
|
||||
4
inventory/group_vars/AZ1.yml
Executable file
4
inventory/group_vars/AZ1.yml
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
gray_gateway:
|
||||
white_gateway:
|
||||
...
|
||||
4
inventory/group_vars/AZ2.yml
Executable file
4
inventory/group_vars/AZ2.yml
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
gray_gateway:
|
||||
white_gateway:
|
||||
...
|
||||
4
inventory/group_vars/PD101.yml
Normal file
4
inventory/group_vars/PD101.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
gray_gateway:
|
||||
white_gateway:
|
||||
...
|
||||
4
inventory/group_vars/PD201.yml
Normal file
4
inventory/group_vars/PD201.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
gray_gateway:
|
||||
white_gateway:
|
||||
...
|
||||
159
inventory/group_vars/prod.yml
Executable file
159
inventory/group_vars/prod.yml
Executable file
|
|
@ -0,0 +1,159 @@
|
|||
---
|
||||
api_sp_token:
|
||||
db_user:
|
||||
db_password:
|
||||
telegram_bot_token:
|
||||
git_token:
|
||||
telegram_chat_id:
|
||||
grafana_api_token:
|
||||
db_name: test_info
|
||||
username_on_new_hosts: user
|
||||
install_ssh_pass:
|
||||
timezone: "Europe/Moscow"
|
||||
ntp_servers:
|
||||
-
|
||||
-
|
||||
dns_servers:
|
||||
-
|
||||
-
|
||||
need_support_soft:
|
||||
- xrdp
|
||||
- tree
|
||||
- mc
|
||||
- wget
|
||||
- gpg
|
||||
- python3
|
||||
- ansible
|
||||
- git
|
||||
- apt-transport-https
|
||||
- ca-certificates
|
||||
- curl
|
||||
- apt-utils
|
||||
- gnupg2
|
||||
xrdp_pass:
|
||||
primary_db_host: '10.10.10.8'
|
||||
secondary_db_host: '10.10.10.5'
|
||||
arch_version: amd64
|
||||
ubuntu_codename: noble
|
||||
zabbix_version: "7.0"
|
||||
zabbix_server: "10.10.20.5"
|
||||
zabbix_port: "10050"
|
||||
zabbix_hostname: system.hostname
|
||||
ubuntu_version: "24.04"
|
||||
compose_version: 2.39.3
|
||||
iptables_version: 1.8.10-3ubuntu2
|
||||
docker_agent_version: '4.1.6.409465'
|
||||
docker_image: ptaf-core-nginx-agent:release-{{ docker_agent_version }}
|
||||
local_directory_docker_image: /home/install/ptaf-core-nginx-agent_release-{{ docker_agent_version }}.tar
|
||||
magos_bin: "/usr/local/bin/magos"
|
||||
number_of_worker_processes: 4
|
||||
number_of_worker_connections: 64000
|
||||
sysctl_params:
|
||||
net.core.netdev_max_backlog: 10000
|
||||
net.core.somaxconn: 262144
|
||||
net.ipv4.tcp_syncookies: 1
|
||||
net.ipv4.tcp_max_syn_backlog: 262144
|
||||
net.ipv4.tcp_max_tw_buckets: 720000
|
||||
net.ipv4.tcp_timestamps: 1
|
||||
net.ipv4.tcp_tw_reuse: 1
|
||||
net.ipv4.tcp_fin_timeout: 30
|
||||
net.ipv4.tcp_keepalive_time: 1800
|
||||
net.ipv4.tcp_keepalive_probes: 7
|
||||
net.ipv4.tcp_keepalive_intvl: 30
|
||||
net.core.wmem_max: 33554432
|
||||
net.core.rmem_max: 33554432
|
||||
net.core.rmem_default: 8388608
|
||||
net.core.wmem_default: 4194394
|
||||
net.ipv4.tcp_rmem: "4096 8388608 16777216"
|
||||
net.ipv4.tcp_wmem: "4096 4194394 16777216"
|
||||
net.ipv4.ip_local_port_range: "1024 65535"
|
||||
net.netfilter.nf_conntrack_max: 1048576
|
||||
antibot_networks:
|
||||
-
|
||||
-
|
||||
waf_networks:
|
||||
-
|
||||
-
|
||||
need_soft:
|
||||
- tree
|
||||
- mc
|
||||
- ncdu
|
||||
- chrony
|
||||
- sqlite3
|
||||
- python3-pip
|
||||
- postgresql-client-common
|
||||
- postgresql-client-16
|
||||
- mtr
|
||||
- traceroute
|
||||
- angie-module-njs
|
||||
user_list:
|
||||
- username:
|
||||
home_dir:
|
||||
key_file:
|
||||
- username:
|
||||
home_dir:
|
||||
key_file:
|
||||
need_install_firewall: true
|
||||
app_config:
|
||||
Test_service:
|
||||
# По умолчанию пусто. Если надо добавить, то добалять адрес с : на конце
|
||||
LISTEN_IP:
|
||||
TENANT_NAME: "Test_service"
|
||||
CONTAINER_NUMBERS: 1
|
||||
# Есть ли настройки без кастомов
|
||||
USUAL_SETTINGS: true
|
||||
# Здесь и далее указывается только то у чего нет кастомных настроек.
|
||||
# Ни апстирмов, ни портов, ни server.
|
||||
SID_NUMBERS:
|
||||
- 11111
|
||||
- 11111
|
||||
- 11111
|
||||
# Приложения в тенанте. Подставляются в шаблоны в апстримы и имена логов
|
||||
APP_IN_TENANT_NAMES:
|
||||
- "test_1_ru"
|
||||
- "test_2_ru"
|
||||
- "test_3_ru"
|
||||
# Приложения в тенанте. Подставляются в server_name.
|
||||
# Если их несколько для одного приложения, то пишутся в одну строку
|
||||
SERVER_NAMES_FOR_APPS:
|
||||
- "test.1.ru"
|
||||
- "test.2.ru"
|
||||
- "test.3.ru"
|
||||
# Адреса ориджинов. Подставляются в апстримы
|
||||
UPSTREAM_IPS:
|
||||
- 91.206.126.155
|
||||
- 91.206.126.36
|
||||
- 91.206.126.162
|
||||
# Группировка ориджинов на случай, если в апстриме больше одного адреса
|
||||
UPSTREAM_GROUPS:
|
||||
- ips: "{{ app_config[app].UPSTREAM_IPS[:1] }}"
|
||||
# Приложения в тенанте. Подставляются в шаблоны в апстримы и имена логов
|
||||
suffix: "test_1_ru"
|
||||
- ips: "{{ app_config[app].UPSTREAM_IPS[1:2] }}"
|
||||
suffix: "test_2_ru"
|
||||
- ips: "{{ app_config[app].UPSTREAM_IPS[2:3] }}"
|
||||
suffix: "test_3_ru"
|
||||
# Есть ли кастомные правила для PT AF Nginx или кастомные locations
|
||||
CUSTOM_CONFIG_PTAF_NGINX_NEED: false
|
||||
# Есть ли кастомные изменения в upstream PT AF Nginx
|
||||
CUSTOM_CONFIG_PTAF_NGINX_UPSTREAM_NEED: false
|
||||
# Есть ли кастомные правила для Angie или кастомные locations
|
||||
CUSTOM_CONFIG_ANGIE_NEED: false
|
||||
# Есть ли кастомные изменения в upstream Angie
|
||||
CUSTOM_CONFIG_ANGIE_UPSTREAM_NEED: false
|
||||
# Есть ли кастомные порты
|
||||
CUSTOM_PORTS_NEED: false
|
||||
# Есть ли кастомные настройки апстримов Angie для кастомных портов
|
||||
CUSTOM_CONFIG_ANGIE_UPSTREAM_NEED_WITH_CUSTOM_PORTS: false
|
||||
# Есть ли кастомные настройки апстримов PT AF Nginx для кастомных портов
|
||||
CUSTOM_CONFIG_PTAF_NGINX_UPSTREAM_NEED_WITH_CUSTOM_PORTS: false
|
||||
# Есть ли кастомные правила Angie
|
||||
CUSTOM_CONFIG_ANGIE_NEED_WITH_CUSTOM_PORTS: false
|
||||
# Есть ли кастомные правила PT AF Nginx
|
||||
CUSTOM_CONFIG_PTAF_NGINX_NEED_WITH_CUSTOM_PORTS: false
|
||||
# Есть ли кастом в location. Не может быть без CUSTOM_CONFIG_ANGIE_NEED:true
|
||||
CUSTOM_LOCATIONS_NEED: false
|
||||
# Нужна ли отгрузка логов заказчику
|
||||
FLUENT_BIT_NEED: false
|
||||
CONNECTION_STRING:
|
||||
...
|
||||
20
inventory/hosts.yml
Executable file
20
inventory/hosts.yml
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
all:
|
||||
children:
|
||||
prod:
|
||||
children:
|
||||
PTAF:
|
||||
children:
|
||||
AZ1:
|
||||
hosts:
|
||||
WAF--AZ1-test01:
|
||||
ansible_host: 10.10.10.4
|
||||
WAF--AZ1-test02:
|
||||
ansible_host: 10.10.10.5
|
||||
AZ2:
|
||||
hosts:
|
||||
WAF-AZ2-test01:
|
||||
ansible_host: 10.10.11.4
|
||||
WAF-AZ2-test02:
|
||||
ansible_host: 10.10.11.5
|
||||
...
|
||||
3
roles/auspex/defaults/main.yml
Normal file
3
roles/auspex/defaults/main.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
auspex_bin: "/usr/local/bin/auspex"
|
||||
...
|
||||
82
roles/auspex/tasks/main.yml
Normal file
82
roles/auspex/tasks/main.yml
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
- name: Copy auspex from cloud
|
||||
ansible.builtin.get_url:
|
||||
url: ""
|
||||
dest: "{{ auspex_bin }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0744'
|
||||
timeout: 60
|
||||
force: true
|
||||
retries: 3
|
||||
delay: 10
|
||||
|
||||
- name: Create cron task for auspex cert checker
|
||||
ansible.builtin.cron:
|
||||
name: "Run auspex cert checker"
|
||||
minute: "0"
|
||||
hour: "9"
|
||||
day: "*"
|
||||
month: "*"
|
||||
weekday: "*"
|
||||
job: "{{ auspex_bin }} -c"
|
||||
user: root
|
||||
|
||||
- name: Create cron task for auspex port checker
|
||||
ansible.builtin.cron:
|
||||
name: "Run auspex port checker"
|
||||
minute: "*/16"
|
||||
hour: "*"
|
||||
day: "*"
|
||||
month: "*"
|
||||
weekday: "*"
|
||||
job: "{{ auspex_bin }} -p"
|
||||
user: root
|
||||
|
||||
- name: Create cron task for auspex match checker
|
||||
ansible.builtin.cron:
|
||||
name: "Run auspex match checker"
|
||||
minute: "*/11"
|
||||
hour: "*"
|
||||
day: "*"
|
||||
month: "*"
|
||||
weekday: "*"
|
||||
job: "{{ auspex_bin }} -m"
|
||||
user: root
|
||||
|
||||
- name: Create cron task for auspex logs checker
|
||||
ansible.builtin.cron:
|
||||
name: "Run auspex logs checker"
|
||||
minute: "*/30"
|
||||
hour: "*"
|
||||
day: "*"
|
||||
month: "*"
|
||||
weekday: "*"
|
||||
job: "{{ auspex_bin }} -l"
|
||||
user: root
|
||||
|
||||
- name: Copy auspex on and off scripts
|
||||
ansible.builtin.template:
|
||||
src: "{{ item }}"
|
||||
dest: "/usr/local/bin/{{ (item | basename).removesuffix('.j2') }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0744'
|
||||
loop: "{{ query('fileglob', 'templates/*.sh.j2') }}"
|
||||
|
||||
- name: Create directory /etc/auspex
|
||||
ansible.builtin.file:
|
||||
path: /etc/auspex
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
|
||||
- name: Create .env
|
||||
ansible.builtin.template:
|
||||
src: auspex.env.j2
|
||||
dest: /etc/auspex/auspex.env
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0600'
|
||||
...
|
||||
22
roles/auspex/templates/auspex.env.j2
Normal file
22
roles/auspex/templates/auspex.env.j2
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
TELEGRAM_BOT_TOKEN={{ telegram_bot_token }}
|
||||
TELEGRAM_CHAT_ID={{ telegram_chat_id }}
|
||||
DB_USER={{ db_user }}
|
||||
DB_PASSWORD={{ db_password }}
|
||||
DB_NAME=waf_info
|
||||
DB_PORT=5432
|
||||
PRIMARY_DB_HOST=10.100.10.8
|
||||
SECONDARY_DB_HOST=10.100.13.5
|
||||
PTAF_BASE_DIR=/home/install/ptaf
|
||||
ANGIE_CONF_DIR=/etc/angie/http.d
|
||||
SSL_DIR=/etc/ssl
|
||||
NGINX_CONFIG_GLOB=/home/install/conf/ptaf-nginx/*/*/*/*.conf
|
||||
STATE_FILE_PATH=/tmp/port_checker_state.json
|
||||
LOG_DIR=/var/log/ptaf_nginx
|
||||
CERT_CRITICAL_DAYS=3
|
||||
CERT_WARNING_DAYS=15
|
||||
CERT_INFO_DAYS=30
|
||||
LOG_STALE_MINUTES=61
|
||||
CHECK_TIMEOUT_SEC=10
|
||||
MAX_RETRIES=3
|
||||
RETRY_DELAY_SEC=2
|
||||
MAX_WORKERS=10
|
||||
34
roles/auspex/templates/auspex_l_off.sh.j2
Normal file
34
roles/auspex/templates/auspex_l_off.sh.j2
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для комментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/30 * * * * {{ auspex_bin }} -l"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в незакомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка найдена и не закомментирована - комментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^\(${ESCAPED_LINE}\)|#\1|" | crontab -
|
||||
echo "Строка закомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка уже закомментирована
|
||||
echo "Строка уже закомментирована, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
34
roles/auspex/templates/auspex_l_on.sh.j2
Normal file
34
roles/auspex/templates/auspex_l_on.sh.j2
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для раскомментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/30 * * * * {{ auspex_bin }} -l"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в закомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка найдена и закомментирована - раскомментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^#\(${ESCAPED_LINE}\)|\1|" | crontab -
|
||||
echo "Строка раскомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка уже раскомментирована
|
||||
echo "Строка уже активна, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
34
roles/auspex/templates/auspex_m_off.sh.j2
Normal file
34
roles/auspex/templates/auspex_m_off.sh.j2
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для комментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/11 * * * * {{ auspex_bin }} -m"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в незакомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка найдена и не закомментирована - комментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^\(${ESCAPED_LINE}\)|#\1|" | crontab -
|
||||
echo "Строка закомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка уже закомментирована
|
||||
echo "Строка уже закомментирована, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
34
roles/auspex/templates/auspex_m_on.sh.j2
Normal file
34
roles/auspex/templates/auspex_m_on.sh.j2
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для раскомментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/11 * * * * {{ auspex_bin }} -m"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в закомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка найдена и закомментирована - раскомментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^#\(${ESCAPED_LINE}\)|\1|" | crontab -
|
||||
echo "Строка раскомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка уже раскомментирована
|
||||
echo "Строка уже активна, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
34
roles/auspex/templates/auspex_p_off.sh.j2
Normal file
34
roles/auspex/templates/auspex_p_off.sh.j2
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для комментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/11 * * * * {{ auspex_bin }} -p"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в незакомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка найдена и не закомментирована - комментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^\(${ESCAPED_LINE}\)|#\1|" | crontab -
|
||||
echo "Строка закомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка уже закомментирована
|
||||
echo "Строка уже закомментирована, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
34
roles/auspex/templates/auspex_p_on.sh.j2
Normal file
34
roles/auspex/templates/auspex_p_on.sh.j2
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для раскомментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/11 * * * * {{ auspex_bin }} -p"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в закомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка найдена и закомментирована - раскомментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^#\(${ESCAPED_LINE}\)|\1|" | crontab -
|
||||
echo "Строка раскомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка уже раскомментирована
|
||||
echo "Строка уже активна, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
3
roles/auspex_l_off/README.md
Normal file
3
roles/auspex_l_off/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Auspex logs off
|
||||
|
||||
Отключение проверки свежести логов
|
||||
16
roles/auspex_l_off/tasks/main.yml
Normal file
16
roles/auspex_l_off/tasks/main.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
- name: Check utils
|
||||
ansible.builtin.stat:
|
||||
path: /usr/local/bin/auspex
|
||||
register: utils_stat
|
||||
|
||||
- name: Auspex logs check off
|
||||
ansible.builtin.command: /usr/local/bin/auspex_l_off.sh
|
||||
register: result
|
||||
when: utils_stat.stat.exists and utils_stat.stat.executable
|
||||
|
||||
- name: Show result
|
||||
ansible.builtin.debug:
|
||||
var: result.stdout
|
||||
when: utils_stat.stat.exists
|
||||
...
|
||||
3
roles/auspex_l_on/tasks/README.md
Normal file
3
roles/auspex_l_on/tasks/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Auspex logs on
|
||||
|
||||
Включение проверки свежести логов
|
||||
16
roles/auspex_l_on/tasks/main.yml
Normal file
16
roles/auspex_l_on/tasks/main.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
- name: Check utils
|
||||
ansible.builtin.stat:
|
||||
path: /usr/local/bin/auspex
|
||||
register: utils_stat
|
||||
|
||||
- name: Auspex logs check on
|
||||
ansible.builtin.command: /usr/local/bin/auspex_l_on.sh
|
||||
register: result
|
||||
when: utils_stat.stat.exists and utils_stat.stat.executable
|
||||
|
||||
- name: Show result
|
||||
ansible.builtin.debug:
|
||||
var: result.stdout
|
||||
when: utils_stat.stat.exists
|
||||
...
|
||||
3
roles/auspex_m_off/README.md
Normal file
3
roles/auspex_m_off/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Auspex match off
|
||||
|
||||
Отключение проверки связности Angie и Docker
|
||||
16
roles/auspex_m_off/tasks/main.yml
Normal file
16
roles/auspex_m_off/tasks/main.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
- name: Check utils
|
||||
ansible.builtin.stat:
|
||||
path: /usr/local/bin/auspex
|
||||
register: utils_stat
|
||||
|
||||
- name: Auspex match off
|
||||
ansible.builtin.command: /usr/local/bin/auspex_m_off.sh
|
||||
register: result
|
||||
when: utils_stat.stat.exists and utils_stat.stat.executable
|
||||
|
||||
- name: Show result
|
||||
ansible.builtin.debug:
|
||||
var: result.stdout
|
||||
when: utils_stat.stat.exists
|
||||
...
|
||||
3
roles/auspex_m_on/README.md
Normal file
3
roles/auspex_m_on/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Auspex logs on
|
||||
|
||||
Включение проверки связности Angie и Docker
|
||||
16
roles/auspex_m_on/tasks/main.yml
Normal file
16
roles/auspex_m_on/tasks/main.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
- name: Check utils
|
||||
ansible.builtin.stat:
|
||||
path: /usr/local/bin/auspex
|
||||
register: utils_stat
|
||||
|
||||
- name: Auspex match on
|
||||
ansible.builtin.command: /usr/local/bin/auspex_m_on.sh
|
||||
register: result
|
||||
when: utils_stat.stat.exists and utils_stat.stat.executable
|
||||
|
||||
- name: Show result
|
||||
ansible.builtin.debug:
|
||||
var: result.stdout
|
||||
when: utils_stat.stat.exists
|
||||
...
|
||||
3
roles/auspex_p_off/README.md
Normal file
3
roles/auspex_p_off/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Auspex ports off
|
||||
|
||||
Отключение проверки доступности портов
|
||||
16
roles/auspex_p_off/tasks/main.yml
Normal file
16
roles/auspex_p_off/tasks/main.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
- name: Check utils
|
||||
ansible.builtin.stat:
|
||||
path: /usr/local/bin/auspex
|
||||
register: utils_stat
|
||||
|
||||
- name: Port checker off
|
||||
ansible.builtin.command: /usr/local/bin/auspex_p_off.sh
|
||||
register: result
|
||||
when: utils_stat.stat.exists and utils_stat.stat.executable
|
||||
|
||||
- name: Show result
|
||||
ansible.builtin.debug:
|
||||
var: result.stdout
|
||||
when: utils_stat.stat.exists
|
||||
...
|
||||
3
roles/auspex_p_on/README.md
Normal file
3
roles/auspex_p_on/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Auspex ports on
|
||||
|
||||
Включение проверки доступности портов
|
||||
16
roles/auspex_p_on/tasks/main.yml
Normal file
16
roles/auspex_p_on/tasks/main.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
- name: Check utils
|
||||
ansible.builtin.stat:
|
||||
path: /usr/local/bin/auspex
|
||||
register: utils_stat
|
||||
|
||||
- name: Port checker on
|
||||
ansible.builtin.command: /usr/local/bin/auspex_p_on.sh
|
||||
register: result
|
||||
when: utils_stat.stat.exists and utils_stat.stat.executable
|
||||
|
||||
- name: Show result
|
||||
ansible.builtin.debug:
|
||||
var: result.stdout
|
||||
when: utils_stat.stat.exists
|
||||
...
|
||||
9
roles/certs_settings/README.md
Normal file
9
roles/certs_settings/README.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Certs_settings
|
||||
|
||||
Роль для раскатки SSL-сертификатов.
|
||||
|
||||
Если нужно добавить сертификаты для стороннего АнтиДДОС, то можно просто положить их в папку files рядом с сертами SP.
|
||||
|
||||
Если нужно добавить сертификаты для mtls, то в директории *files* есть директория mtls, туда и нужно положить сертификаты.
|
||||
|
||||
НЕ ЗАБЫВАЙТЕ называть сертификаты так, чтобы было понятно к чему они принадлежат, а также шифровать их через ansible-vault.
|
||||
6
roles/certs_settings/handlers/main.yml
Normal file
6
roles/certs_settings/handlers/main.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: Reload angie
|
||||
service:
|
||||
name: "angie"
|
||||
state: reloaded
|
||||
...
|
||||
41
roles/certs_settings/tasks/main.yml
Normal file
41
roles/certs_settings/tasks/main.yml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
- name: Copy antiddos cert
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: /etc/ssl/certs/
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
loop: "{{ lookup('fileglob', 'files/*.crt', 'files/*.cer', 'files/*.pem', wantlist=True) }}"
|
||||
notify: Reload angie
|
||||
|
||||
- name: Copy antiddos key
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: /etc/ssl/private/
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
loop: "{{ lookup('fileglob', 'files/*.key', wantlist=True) }}"
|
||||
notify: Reload angie
|
||||
|
||||
- name: Copy mtls certs
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: /etc/ssl/mtls/
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
loop: "{{ lookup('fileglob', 'files/mtls/*.crt', 'files/mtls/*.cer', 'files/mtls/*.pem', wantlist=True) }}"
|
||||
notify: Reload angie
|
||||
|
||||
- name: Copy mtls keys
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: /etc/ssl/mtls/
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
loop: "{{ lookup('fileglob', 'files/mtls/*.key', wantlist=True) }}"
|
||||
notify: Reload angie
|
||||
...
|
||||
4
roles/chrony_install/README.md
Executable file
4
roles/chrony_install/README.md
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
# Роль для установки и настройки Chrony для синхрониации времени
|
||||
|
||||
В `defaults/main` нужно указать нужную временную зону и адреса NTP-серверов.
|
||||
Далее роль проверит и при необходимости установит Chrony и внесёт настройки в файл `/etc/chrony/chrony.conf`
|
||||
25
roles/chrony_install/tasks/main.yml
Executable file
25
roles/chrony_install/tasks/main.yml
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
- name: Chrony install
|
||||
ansible.builtin.apt:
|
||||
name: chrony
|
||||
state: present
|
||||
|
||||
- name: Timezone settings
|
||||
ansible.builtin.timezone:
|
||||
name: "{{ timezone }}"
|
||||
|
||||
- name: Chrony settings
|
||||
ansible.builtin.template:
|
||||
src: chrony.conf.j2
|
||||
dest: /etc/chrony/chrony.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Chrony enable
|
||||
ansible.builtin.service:
|
||||
name: chronyd
|
||||
state: restarted
|
||||
enabled: true
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
...
|
||||
3
roles/chrony_install/templates/chrony.conf.j2
Executable file
3
roles/chrony_install/templates/chrony.conf.j2
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
{% for server in ntp_servers %}
|
||||
server {{ server }} iburst
|
||||
{% endfor %}
|
||||
1
roles/different_need_soft_install/README.md
Executable file
1
roles/different_need_soft_install/README.md
Executable file
|
|
@ -0,0 +1 @@
|
|||
# Роль для установки различного необходимого софта
|
||||
73
roles/different_need_soft_install/files/angie.conf
Executable file
73
roles/different_need_soft_install/files/angie.conf
Executable file
|
|
@ -0,0 +1,73 @@
|
|||
user angie;
|
||||
worker_processes auto;
|
||||
worker_rlimit_nofile 65536;
|
||||
|
||||
error_log /var/log/angie/error.log notice;
|
||||
pid /run/angie.pid;
|
||||
|
||||
load_module modules/ngx_http_js_module.so;
|
||||
|
||||
events {
|
||||
worker_connections 65536;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
server_names_hash_bucket_size 128;
|
||||
include /etc/angie/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
js_path /etc/angie/njs/;
|
||||
js_import main from host_transform.js;
|
||||
|
||||
log_format waf escape=json
|
||||
'{'
|
||||
'"SID":$http_x_sid,'
|
||||
'"server_name":"$server_name",'
|
||||
'"app_name":"$host",'
|
||||
'"proxyed_to":"$upstream_addr",'
|
||||
'"upstream_status":"$upstream_status",'
|
||||
'"upstream_response_length":"$upstream_response_length",'
|
||||
'"upstream_response_time":"$upstream_response_time",'
|
||||
'"upstream_bytes_sent":"$upstream_bytes_sent",'
|
||||
'"server_port":$server_port,'
|
||||
'"server_addr":"$server_addr",'
|
||||
'"server_protocol":"$server_protocol",'
|
||||
'"remote_addr":"$remote_addr",'
|
||||
'"response_status_code":$status,'
|
||||
'"response_body_bytes_sent":$body_bytes_sent,'
|
||||
'"http_x_forwarded_for":"$http_x_forwarded_for",'
|
||||
'"uri":"$uri",'
|
||||
'"http_user_agent":"$http_user_agent",'
|
||||
'"query_string":"$query_string",'
|
||||
'"request_method":"$request_method",'
|
||||
'"request_time":$request_time,'
|
||||
'"@timestamp":"$time_iso8601"'
|
||||
'}';
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
log_format extended '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" rt="$request_time" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||
'h="$host" sn="$server_name" ru="$request_uri" u="$uri" '
|
||||
'ucs="$upstream_cache_status" ua="$upstream_addr" us="$upstream_status" '
|
||||
'uct="$upstream_connect_time" urt="$upstream_response_time"';
|
||||
|
||||
access_log /var/log/angie/access.log waf;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
include /etc/angie/http.d/*.conf;
|
||||
}
|
||||
|
||||
#stream {
|
||||
# include /etc/angie/stream.d/*.conf;
|
||||
#}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# Default server для порта 80 - отбрасывает неизвестные запросы
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
|
||||
# Возвращаем 444 (закрываем соединение без ответа)
|
||||
return 444;
|
||||
|
||||
# Или можно вернуть 403/404:
|
||||
# return 403 "Access denied";
|
||||
}
|
||||
|
||||
# Default server для порта 443
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/sp.crt;
|
||||
ssl_certificate_key /etc/ssl/private/sp.key;
|
||||
|
||||
return 444;
|
||||
}
|
||||
13
roles/different_need_soft_install/files/docker_network.json
Executable file
13
roles/different_need_soft_install/files/docker_network.json
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"bip": "172.17.0.1/24",
|
||||
"default-address-pools": [
|
||||
{
|
||||
"base": "172.18.0.0/16",
|
||||
"size": 24
|
||||
},
|
||||
{
|
||||
"base": "172.19.0.0/16",
|
||||
"size": 24
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
function sanitizeHost(r) {
|
||||
var host = r.headersIn.Host || '';
|
||||
return host.replace(/_/g, '-');
|
||||
}
|
||||
|
||||
export default { sanitizeHost };
|
||||
20
roles/different_need_soft_install/files/portainer_docker_compose.yml
Executable file
20
roles/different_need_soft_install/files/portainer_docker_compose.yml
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
services:
|
||||
portainer:
|
||||
container_name: portainer
|
||||
image: portainer/portainer-ce:lts
|
||||
restart: always
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /home/install/portainer/portainer_data:/data
|
||||
ports:
|
||||
- 29443:9443
|
||||
|
||||
volumes:
|
||||
portainer_data:
|
||||
name: portainer_data
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: portainer_network
|
||||
...
|
||||
63
roles/different_need_soft_install/tasks/angie_install.yml
Executable file
63
roles/different_need_soft_install/tasks/angie_install.yml
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
- name: Download GPG Angie key
|
||||
ansible.builtin.get_url:
|
||||
url: https://angie.software/keys/angie-signing.gpg
|
||||
dest: /etc/apt/trusted.gpg.d/angie-signing.gpg
|
||||
mode: '0644'
|
||||
|
||||
- name: Create sources.list for Angie
|
||||
ansible.builtin.template:
|
||||
src: "templates/angie.list.j2"
|
||||
dest: "/etc/apt/sources.list.d/angie.list"
|
||||
register: angie_gpg_result
|
||||
|
||||
- name: Update cache
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
when: angie_gpg_result.changed
|
||||
|
||||
- name: Install Angie
|
||||
ansible.builtin.apt:
|
||||
name: angie
|
||||
state: present
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Change angie.conf
|
||||
ansible.builtin.copy:
|
||||
src: files/angie.conf
|
||||
dest: /etc/angie/angie.conf
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Create njs dir
|
||||
ansible.builtin.file:
|
||||
path: /etc/angie/njs/
|
||||
state: directory
|
||||
mode: '0755'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Angie njs copy
|
||||
ansible.builtin.copy:
|
||||
src: files/host_transform.js
|
||||
dest: /etc/angie/njs/host_transform.js
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Angie default route copy
|
||||
ansible.builtin.copy:
|
||||
src: files/angie_00-default.conf
|
||||
dest: /etc/angie/http.d/00-default.conf
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Start and enable Angie service
|
||||
ansible.builtin.systemd:
|
||||
name: angie
|
||||
state: started
|
||||
enabled: true
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
...
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
- name: Copy lazydocker from cloud
|
||||
ansible.builtin.get_url:
|
||||
url: ""
|
||||
dest: "/usr/local/bin/lazydocker"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0744'
|
||||
timeout: 60
|
||||
retries: 3
|
||||
delay: 10
|
||||
when: inventory_hostname is match("^PTAF")
|
||||
...
|
||||
25
roles/different_need_soft_install/tasks/main.yml
Executable file
25
roles/different_need_soft_install/tasks/main.yml
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
- name: Soft from Ubuntu repo
|
||||
ansible.builtin.include_tasks: soft_from_ubuntu_repo.yml
|
||||
|
||||
- name: Angie install
|
||||
ansible.builtin.include_tasks: angie_install.yml
|
||||
when: inventory_hostname is match("^PTAF")
|
||||
|
||||
- name: Need soft install
|
||||
ansible.builtin.include_tasks: need_soft_install.yml
|
||||
|
||||
- name: Portainer run
|
||||
ansible.builtin.include_tasks: portainer_install.yml
|
||||
when: inventory_hostname is match("^PTAF")
|
||||
|
||||
- name: Zabbix install
|
||||
ansible.builtin.include_tasks: zabbix_install.yml
|
||||
|
||||
- name: Lazydocker install
|
||||
ansible.builtin.include_tasks: lazydocker_install.yml
|
||||
|
||||
- name: Logrotate settings
|
||||
ansible.builtin.include_role:
|
||||
name: logrotate_settings
|
||||
...
|
||||
96
roles/different_need_soft_install/tasks/need_soft_install.yml
Executable file
96
roles/different_need_soft_install/tasks/need_soft_install.yml
Executable file
|
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
- name: Install prerequisites
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- ca-certificates
|
||||
- curl
|
||||
- software-properties-common
|
||||
- gnupg
|
||||
state: present
|
||||
|
||||
- name: Install iptables
|
||||
ansible.builtin.apt:
|
||||
name: iptables={{ iptables_version }}
|
||||
state: present
|
||||
when: inventory_hostname is match("^PTAF")
|
||||
|
||||
- name: Docker install
|
||||
block:
|
||||
- name: Download GPG Docker key
|
||||
ansible.builtin.get_url:
|
||||
url: https://download.docker.com/linux/ubuntu/gpg
|
||||
dest: /etc/apt/keyrings/docker.asc
|
||||
mode: '0644'
|
||||
|
||||
- name: Create sources.list for Docker
|
||||
ansible.builtin.template:
|
||||
src: "templates/docker.list.j2"
|
||||
dest: "/etc/apt/sources.list.d/docker.list"
|
||||
register: docker_gpg_result
|
||||
|
||||
- name: Update cache
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
when: docker_gpg_result.changed
|
||||
|
||||
- name: Install Docker
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- docker-ce
|
||||
- docker-ce-cli
|
||||
- containerd.io
|
||||
- docker-buildx-plugin
|
||||
- docker-compose-plugin
|
||||
state: present
|
||||
register: docker_install_result
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Install Docker Compose
|
||||
ansible.builtin.get_url:
|
||||
# yamllint disable-line rule:line-length
|
||||
url: https://github.com/docker/compose/releases/download/v{{ compose_version }}/docker-compose-Linux-x86_64
|
||||
dest: /usr/local/bin/docker-compose
|
||||
mode: '0755'
|
||||
register: compose_install_result
|
||||
|
||||
- name: Copy docker network settings
|
||||
ansible.builtin.copy:
|
||||
src: files/docker_network.json
|
||||
dest: /etc/docker/daemon.json
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
register: docker_install_result
|
||||
|
||||
- name: Add system users to docker group
|
||||
ansible.builtin.user:
|
||||
name: "{{ item }}"
|
||||
groups: docker
|
||||
append: true
|
||||
with_items:
|
||||
- zabbix
|
||||
- install
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Add TB users to docker group
|
||||
ansible.builtin.user:
|
||||
name: "{{ item.username }}"
|
||||
groups: docker
|
||||
append: true
|
||||
with_items: "{{ user_list }}"
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
when: inventory_hostname is match("^PTAF")
|
||||
|
||||
- name: Check need restart docker
|
||||
ansible.builtin.set_fact:
|
||||
# yamllint disable-line rule:line-length
|
||||
need_restart: "{{ docker_install_result is changed or compose_install_result is changed }}"
|
||||
when: inventory_hostname is match("^PTAF")
|
||||
|
||||
- name: Restart Docker service
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
state: restarted
|
||||
when: need_restart
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
...
|
||||
25
roles/different_need_soft_install/tasks/portainer_install.yml
Executable file
25
roles/different_need_soft_install/tasks/portainer_install.yml
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
- name: Create need dir
|
||||
ansible.builtin.file:
|
||||
path: /home/install/portainer
|
||||
state: directory
|
||||
mode: '0755'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Download portainer docker compose file
|
||||
ansible.builtin.copy:
|
||||
src: files/portainer_docker_compose.yml
|
||||
dest: /home/install/portainer/docker-compose.yml
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Run Docker compose
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /home/install/portainer/
|
||||
files:
|
||||
- docker-compose.yml
|
||||
ignore_errors: true
|
||||
...
|
||||
8
roles/different_need_soft_install/tasks/soft_from_ubuntu_repo.yml
Executable file
8
roles/different_need_soft_install/tasks/soft_from_ubuntu_repo.yml
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
- name: Install different soft
|
||||
ansible.builtin.apt:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items: "{{ need_soft }}"
|
||||
ignore_errors: true
|
||||
...
|
||||
15
roles/different_need_soft_install/tasks/zabbix_install.yml
Executable file
15
roles/different_need_soft_install/tasks/zabbix_install.yml
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
- name: Pull and run Zabbix Agent container
|
||||
community.docker.docker_container:
|
||||
name: zabbix-agent-{{ zabbix_version }}
|
||||
image: zabbix/zabbix-agent:{{ zabbix_version }}-ubuntu-latest
|
||||
state: started
|
||||
restart_policy: always
|
||||
env:
|
||||
ZBX_SERVER_HOST: "{{ zabbix_server }}"
|
||||
ZBX_LISTENPORT: "{{ zabbix_port }}"
|
||||
ZBX_HOSTNAMEITEM: "{{ zabbix_hostname }}"
|
||||
ports:
|
||||
- "10050:10050"
|
||||
ignore_errors: true
|
||||
...
|
||||
1
roles/different_need_soft_install/templates/angie.list.j2
Executable file
1
roles/different_need_soft_install/templates/angie.list.j2
Executable file
|
|
@ -0,0 +1 @@
|
|||
deb https://download.angie.software/angie/ubuntu/{{ ubuntu_version }} {{ ubuntu_codename }} main
|
||||
1
roles/different_need_soft_install/templates/docker.list.j2
Executable file
1
roles/different_need_soft_install/templates/docker.list.j2
Executable file
|
|
@ -0,0 +1 @@
|
|||
deb [arch={{ arch_version }} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu {{ ubuntu_codename }} stable
|
||||
4
roles/dns_settings/README.md
Executable file
4
roles/dns_settings/README.md
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
# Роль для настройки DNS-серверов
|
||||
|
||||
В `defaults/main` указываются dns-серверы на которые будет обращаться хост.
|
||||
Далее роль подкидывает на хост заполненный шаблон по пути `/etc/resolv.conf` и перезагружает службу.
|
||||
14
roles/dns_settings/tasks/main.yml
Executable file
14
roles/dns_settings/tasks/main.yml
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
- name: New resolv conf upload
|
||||
ansible.builtin.template:
|
||||
src: resolv.conf.j2
|
||||
dest: /etc/resolv.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Restart systemd-resolved
|
||||
ansible.builtin.systemd:
|
||||
name: systemd-resolved
|
||||
state: restarted
|
||||
...
|
||||
5
roles/dns_settings/templates/resolv.conf.j2
Executable file
5
roles/dns_settings/templates/resolv.conf.j2
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
{% for server in dns_servers %}
|
||||
nameserver {{ server }}
|
||||
{% endfor %}
|
||||
options edns0 trust-ad
|
||||
search .
|
||||
8
roles/filebeat_install/README.md
Executable file
8
roles/filebeat_install/README.md
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
# Роль для настройки access логов nginx и angie по одному шаблону
|
||||
|
||||
Во время работы роли происходит следующее:
|
||||
|
||||
1. Проверяется какой веб-сервер используется;
|
||||
2. На основе полученных данных в нужные файлы подкидывается форма `access`-логов;
|
||||
3. В конфигурационные файлы добавляется указание использовать нужную форму;
|
||||
4. Устанавливается и запускается `filebeat`.
|
||||
3
roles/filebeat_install/defaults/main.yml
Executable file
3
roles/filebeat_install/defaults/main.yml
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
filebeat_install_filebeat_password:
|
||||
...
|
||||
9581
roles/filebeat_install/files/fields.yml
Executable file
9581
roles/filebeat_install/files/fields.yml
Executable file
File diff suppressed because it is too large
Load diff
46
roles/filebeat_install/tasks/main.yml
Executable file
46
roles/filebeat_install/tasks/main.yml
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
- name: Check if filebeat is installed
|
||||
ansible.builtin.command: dpkg -s filebeat
|
||||
register: filebeat_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Install filebeat
|
||||
ansible.builtin.apt:
|
||||
# yamllint disable-line rule:line-length
|
||||
deb: ""
|
||||
when: filebeat_check.rc != 0
|
||||
|
||||
- name: Make backups
|
||||
ansible.builtin.copy:
|
||||
src: "/etc/filebeat/{{ item }}"
|
||||
dest: "/etc/filebeat/{{ item | basename }}.bak"
|
||||
remote_src: true
|
||||
with_items:
|
||||
- filebeat.yml
|
||||
- fields.yml
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Add new filebeat conf
|
||||
ansible.builtin.template:
|
||||
src: templates/filebeat.yml.j2
|
||||
dest: /etc/filebeat/filebeat.yml
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Add new fields conf
|
||||
ansible.builtin.template:
|
||||
src: files/fields.yml
|
||||
dest: /etc/filebeat/fields.yml
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Enable filebeat
|
||||
ansible.builtin.service:
|
||||
name: filebeat
|
||||
state: restarted
|
||||
enabled: true
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
...
|
||||
472
roles/filebeat_install/templates/filebeat.yml.j2
Executable file
472
roles/filebeat_install/templates/filebeat.yml.j2
Executable file
|
|
@ -0,0 +1,472 @@
|
|||
filebeat.inputs:
|
||||
|
||||
- type: log
|
||||
enabled: true
|
||||
id: nginx-access
|
||||
paths:
|
||||
- /var/log/ptaf_nginx/*/*/*access.log
|
||||
- /var/log/ptaf_nginx/*/*/*access.log.1
|
||||
json:
|
||||
keys_under_root: true
|
||||
overwrite_keys: true
|
||||
add_error_key: true
|
||||
fields:
|
||||
log_type: access
|
||||
fields_under_root: true
|
||||
|
||||
- type: filestream
|
||||
id: nginx-error
|
||||
enabled: true
|
||||
paths:
|
||||
- /var/log/ptaf_nginx/*/*/*error.log
|
||||
- /var/log/ptaf_nginx/*/*/*error.log.1
|
||||
fields:
|
||||
log_type: "error"
|
||||
fields_under_root: true
|
||||
|
||||
- type: filestream
|
||||
id: angie-error
|
||||
enabled: true
|
||||
paths:
|
||||
- /var/log/angie/*error.log
|
||||
- /var/log/angie/*error.log.1
|
||||
|
||||
fields:
|
||||
log_type: "angie-error"
|
||||
fields_under_root: true
|
||||
|
||||
setup.ilm.enabled: false
|
||||
|
||||
setup.template.enabled: false
|
||||
setup.template.name: "ptaf"
|
||||
setup.template.pattern: "ptaf-*"
|
||||
|
||||
# Отключаем автоматическое создание индексов
|
||||
setup.dashboards.enabled: false
|
||||
|
||||
output.elasticsearch:
|
||||
enabled: true
|
||||
# Array of hosts to connect to.
|
||||
hosts: ["https://10.10.10.23:9200", "https://10.10.10.24:9200"]
|
||||
username: "vector"
|
||||
password: {{ filebeat_install_filebeat_password }}
|
||||
#Вывод в консоль
|
||||
#output.console:
|
||||
# codec.json:
|
||||
# pretty: true
|
||||
|
||||
#SSL configuration for HTTPS
|
||||
ssl:
|
||||
verification_mode: none # Отключаем проверку сертификата
|
||||
|
||||
# Индексы для разных типов логов
|
||||
indices:
|
||||
- index: "ptaf-access-%{+yyyy.MM.dd}"
|
||||
when:
|
||||
equals:
|
||||
log_type: "access"
|
||||
|
||||
- index: "ptaf-error-%{+yyyy.MM.dd}"
|
||||
when:
|
||||
equals:
|
||||
log_type: "error"
|
||||
|
||||
- index: "ptaf-angie-error-%{+yyyy.MM.dd}"
|
||||
when:
|
||||
equals:
|
||||
log_type: "angie-error"
|
||||
|
||||
seccomp:
|
||||
enabled: false
|
||||
|
||||
processors:
|
||||
- script:
|
||||
lang: javascript
|
||||
source: |
|
||||
function process(event) {
|
||||
// Обработка upstream_response_time
|
||||
var responseTime = event.Get("upstream_response_time");
|
||||
if (responseTime && typeof responseTime === 'string') {
|
||||
var values = responseTime.split(",");
|
||||
if (values.length > 0) {
|
||||
var lastValue = values[values.length - 1].trim();
|
||||
var numValue = parseFloat(lastValue);
|
||||
if (!isNaN(numValue)) {
|
||||
event.Put("upstream_response_time_digital", numValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обработка upstream_status
|
||||
var upstreamStatus = event.Get("upstream_status");
|
||||
if (upstreamStatus && typeof upstreamStatus === 'string') {
|
||||
var statusValues = upstreamStatus.split(",");
|
||||
if (statusValues.length > 0) {
|
||||
var lastStatus = statusValues[statusValues.length - 1].trim();
|
||||
var intStatus = parseInt(lastStatus, 10);
|
||||
if (!isNaN(intStatus)) {
|
||||
event.Put("upstream_status_digital", intStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обработка upstream_response_length
|
||||
var responseLength = event.Get("upstream_response_length");
|
||||
if (responseLength && typeof responseLength === 'string') {
|
||||
var lengthValues = responseLength.split(",");
|
||||
if (lengthValues.length > 0) {
|
||||
var lastLength = lengthValues[lengthValues.length - 1].trim();
|
||||
var intLength = parseInt(lastLength, 10);
|
||||
if (!isNaN(intLength)) {
|
||||
event.Put("upstream_response_length_digital", intLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обработка upstream_bytes_sent (ДОБАВЛЕНО)
|
||||
var bytesSent = event.Get("upstream_bytes_sent");
|
||||
if (bytesSent && typeof bytesSent === 'string') {
|
||||
var bytesValues = bytesSent.split(",");
|
||||
if (bytesValues.length > 0) {
|
||||
var lastBytes = bytesValues[bytesValues.length - 1].trim();
|
||||
var intBytes = parseInt(lastBytes, 10);
|
||||
if (!isNaN(intBytes)) {
|
||||
event.Put("upstream_bytes_sent_digital", intBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
- drop_fields:
|
||||
fields: ["agent","ecs.version","log.offset","input.type","type"]
|
||||
ignore_missing: true
|
||||
|
||||
|
||||
- dissect:
|
||||
tokenizer: "/var/log/ptaf_nginx/%{TENANT}/%{}"
|
||||
field: "log.file.path"
|
||||
target_prefix: ""
|
||||
|
||||
- rename:
|
||||
fields:
|
||||
- from: "host.name"
|
||||
to: "node_name"
|
||||
ignore_missing: true
|
||||
fail_on_error: false
|
||||
|
||||
# Процессор для составной переменной agent_name
|
||||
|
||||
- script:
|
||||
lang: javascript
|
||||
source: |
|
||||
function process(event) {
|
||||
var tenant = event.Get("TENANT");
|
||||
var nodeName = event.Get("node_name");
|
||||
var logPath = event.Get("log.file.path");
|
||||
|
||||
if (!tenant || !nodeName || !logPath) return;
|
||||
|
||||
// Извлекаем AZ часть
|
||||
var azMatch = nodeName.match(/(AZ\d+)/);
|
||||
var azPart = azMatch ? azMatch[1] : "AZ0";
|
||||
|
||||
// Извлекаем имя хоста (последняя часть после дефиса)
|
||||
var parts = nodeName.split('-');
|
||||
var nodePart = parts[parts.length - 1];
|
||||
|
||||
// Извлекаем agent (ищем "ptaf-agent001")
|
||||
var agentName = "agent000";
|
||||
var agentMatch = logPath.match(/ptaf-(agent\d+)/);
|
||||
if (agentMatch) {
|
||||
agentName = agentMatch[1];
|
||||
}
|
||||
|
||||
// Преобразуем nodePart: test01 → t01
|
||||
var shortNodePart = "t00";
|
||||
if (nodePart) {
|
||||
// Ищем первую букву и цифры
|
||||
var nodeMatch = nodePart.match(/^([a-zA-Z])[a-zA-Z]*(\d+)$/);
|
||||
if (nodeMatch) {
|
||||
var letter = nodeMatch[1]; // первая буква
|
||||
var digits = nodeMatch[2]; // цифры
|
||||
shortNodePart = letter.toLowerCase() + digits;
|
||||
} else {
|
||||
// Если не нашли буквы, берем только цифры
|
||||
var digitsOnly = nodePart.match(/(\d+)/);
|
||||
if (digitsOnly) {
|
||||
shortNodePart = "t" + digitsOnly[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразуем agentName: agent001 → a001
|
||||
var shortAgentName = "a000";
|
||||
if (agentName) {
|
||||
// Ищем первую букву и цифры
|
||||
var agentMatch = agentName.match(/^([a-zA-Z])[a-zA-Z]*(\d+)$/);
|
||||
if (agentMatch) {
|
||||
var letter = agentMatch[1]; // первая буква
|
||||
var digits = agentMatch[2]; // цифры
|
||||
shortAgentName = letter.toLowerCase() + digits;
|
||||
} else {
|
||||
// Если не нашли буквы, берем только цифры
|
||||
var digitsOnly = agentName.match(/(\d+)/);
|
||||
if (digitsOnly) {
|
||||
shortAgentName = "a" + digitsOnly[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Создаем composite_id в новом формате
|
||||
event.Put("agent_name", tenant + "_" + azPart + "_" + shortNodePart + "_" + shortAgentName);
|
||||
}
|
||||
ignore_failure: true
|
||||
|
||||
# Процессор для nginx-error: парсинг timestamp из сообщения
|
||||
- dissect:
|
||||
tokenizer: "%{error_timestamp} [%{log_level}] %{rest}"
|
||||
field: "message"
|
||||
target_prefix: ""
|
||||
ignore_failure: true
|
||||
when:
|
||||
equals:
|
||||
log_type: "error"
|
||||
|
||||
# Преобразование timestamp для nginx-error
|
||||
- timestamp:
|
||||
field: "error_timestamp"
|
||||
layouts:
|
||||
- "2006-01-02 15:04:05.000"
|
||||
timezone: "Europe/Moscow"
|
||||
target_field: "@timestamp"
|
||||
ignore_failure: true
|
||||
when:
|
||||
equals:
|
||||
log_type: "error"
|
||||
# Удаление временных полей для nginx-error
|
||||
- drop_fields:
|
||||
fields: ["error_timestamp", "log_level", "rest"]
|
||||
ignore_missing: true
|
||||
when:
|
||||
equals:
|
||||
log_type: "error"
|
||||
|
||||
- drop_fields:
|
||||
fields: ["agent","ecs.version","log.offset","input.type","type"]
|
||||
ignore_missing: true
|
||||
|
||||
|
||||
- dissect:
|
||||
field: "log.file.path"
|
||||
tokenizer: '/var/log/angie/%{extracted_sid}_%{rest}'
|
||||
target_prefix: ""
|
||||
overwrite_keys: false # Не перезаписывать существующие поля
|
||||
ignore_failure: true
|
||||
|
||||
# 2. Копируем extracted_sid в SID, если SID не существует
|
||||
- script:
|
||||
lang: javascript
|
||||
source: |
|
||||
function process(event) {
|
||||
var extractedSid = event.Get("extracted_sid");
|
||||
var existingSid = event.Get("SID");
|
||||
|
||||
// Если SID уже существует, оставляем его
|
||||
if (existingSid !== null && existingSid !== undefined && existingSid !== "") {
|
||||
// Удаляем временное поле extracted_sid
|
||||
event.Delete("extracted_sid");
|
||||
return;
|
||||
}
|
||||
|
||||
// Если извлекли SID из пути и SID еще не существует
|
||||
if (extractedSid && !existingSid) {
|
||||
event.Put("SID", extractedSid);
|
||||
// Удаляем временное поле
|
||||
event.Delete("extracted_sid");
|
||||
}
|
||||
}
|
||||
ignore_failure: true
|
||||
|
||||
# 3. Удаляем временное поле rest
|
||||
- drop_fields:
|
||||
fields: ["rest"]
|
||||
ignore_missing: true
|
||||
# Процессор для angie-error: парсинг timestamp из сообщения
|
||||
|
||||
# 1. Первичный разбор error-лога с помощью dissect
|
||||
- dissect:
|
||||
when:
|
||||
and:
|
||||
- equals:
|
||||
log_type: "angie-error"
|
||||
- contains:
|
||||
message: "client:"
|
||||
field: "message"
|
||||
tokenizer: '%{error_date} %{error_time} [%{error_level}] %{pid_worker}: *%{connection_id} %{rest}'
|
||||
target_prefix: "error"
|
||||
overwrite_keys: true
|
||||
ignore_failure: true
|
||||
# 2. Расширенная обработка оставшейся части в script
|
||||
- script:
|
||||
when:
|
||||
and:
|
||||
- equals:
|
||||
log_type: "angie-error"
|
||||
- has_fields: ['error.rest']
|
||||
lang: javascript
|
||||
source: |
|
||||
function process(event) {
|
||||
var rest = event.Get("error.rest");
|
||||
if (!rest) return;
|
||||
|
||||
// Разделяем текст ошибки и дополнительные поля
|
||||
// Ищем первое вхождение ", client:" чтобы отделить текст ошибки
|
||||
var clientIndex = rest.indexOf(", client:");
|
||||
if (clientIndex === -1) {
|
||||
// Если нет client:, значит это простое сообщение
|
||||
event.Put("error.message_text", rest);
|
||||
return;
|
||||
}
|
||||
|
||||
// Извлекаем текст ошибки (все до ", client:")
|
||||
var errorMessage = rest.substring(0, clientIndex);
|
||||
event.Put("error.message_text", errorMessage.trim());
|
||||
|
||||
// Извлекаем ключ-значение пары
|
||||
var kvString = rest.substring(clientIndex + 2); // +2 чтобы пропустить ", "
|
||||
|
||||
// Разбираем пары ключ:значение
|
||||
parseKeyValuePairs(kvString, event);
|
||||
|
||||
// Дополнительно разбираем PID#worker
|
||||
var pidWorker = event.Get("error.pid_worker");
|
||||
if (pidWorker && pidWorker.includes("#")) {
|
||||
var parts = pidWorker.split("#");
|
||||
event.Put("error.pid", parts[0]);
|
||||
event.Put("error.worker", parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для разбора строки с key:value парами
|
||||
function parseKeyValuePairs(kvString, event) {
|
||||
var regex = /(\w+):\s*(?:"([^"]*)"|([^,]*))/g;
|
||||
var match;
|
||||
var fields = {};
|
||||
|
||||
while ((match = regex.exec(kvString)) !== null) {
|
||||
var key = match[1];
|
||||
var value = match[2] || match[3];
|
||||
|
||||
// Очищаем значение от лишних пробелов
|
||||
if (value) {
|
||||
value = value.trim();
|
||||
}
|
||||
|
||||
// Сохраняем поле с соответствующим префиксом
|
||||
var fieldName = "error." + key;
|
||||
event.Put(fieldName, value);
|
||||
}
|
||||
}
|
||||
ignore_failure: true
|
||||
|
||||
# 3. Для notice логов (без полей client, server и т.д.)
|
||||
- dissect:
|
||||
when:
|
||||
and:
|
||||
- equals:
|
||||
log_type: "angie-error"
|
||||
- not:
|
||||
contains:
|
||||
message: "client:"
|
||||
field: "message"
|
||||
tokenizer: '%{error_date} %{error_time} [%{error_level}] %{pid_worker}: %{error_message}'
|
||||
target_prefix: "error"
|
||||
overwrite_keys: true
|
||||
ignore_failure: true
|
||||
|
||||
# 4. Создание единого поля datetime
|
||||
- script:
|
||||
when:
|
||||
equals:
|
||||
log_type: "angie-error"
|
||||
lang: javascript
|
||||
source: |
|
||||
function process(event) {
|
||||
var date = event.Get("error.error_date");
|
||||
var time = event.Get("error.error_time");
|
||||
|
||||
if (date && time) {
|
||||
// Преобразуем дату из 2026/01/29 в 2026-01-29
|
||||
var isoDate = date.replace(/\//g, "-");
|
||||
// Создаем строку в формате ISO 8601
|
||||
var dateTime = isoDate + "T" + time;
|
||||
event.Put("error.datetime", dateTime);
|
||||
}
|
||||
}
|
||||
ignore_failure: true
|
||||
|
||||
# 5. Установка timestamp из лога
|
||||
- timestamp:
|
||||
when:
|
||||
equals:
|
||||
log_type: "angie-error"
|
||||
field: "error.datetime"
|
||||
layouts:
|
||||
- "2006-01-02T15:04:05"
|
||||
timezone: "Europe/Moscow"
|
||||
ignore_failure: true
|
||||
|
||||
# 6. Дополнительная обработка специальных полей
|
||||
- script:
|
||||
when:
|
||||
and:
|
||||
- equals:
|
||||
log_type: "angie-error"
|
||||
- has_fields: ['error.request']
|
||||
lang: javascript
|
||||
source: |
|
||||
function process(event) {
|
||||
// Разбираем поле request на метод, путь и версию
|
||||
var request = event.Get("error.request");
|
||||
if (request) {
|
||||
var parts = request.split(" ");
|
||||
if (parts.length >= 3) {
|
||||
event.Put("error.http_method", parts[0]);
|
||||
event.Put("error.http_path", parts[1]);
|
||||
event.Put("error.http_version", parts[2].replace("HTTP/", ""));
|
||||
}
|
||||
}
|
||||
|
||||
// Разбираем upstream URL
|
||||
var upstream = event.Get("error.upstream");
|
||||
if (upstream && upstream.startsWith("http")) {
|
||||
try {
|
||||
var url = upstream.match(/^(\w+):\/\/([^:/]+)(?::(\d+))?(\/.*)?$/);
|
||||
if (url) {
|
||||
event.Put("error.upstream_protocol", url[1]);
|
||||
event.Put("error.upstream_host", url[2]);
|
||||
event.Put("error.upstream_port", url[3] || "80");
|
||||
event.Put("error.upstream_path", url[4] || "/");
|
||||
}
|
||||
} catch(e) {
|
||||
// Игнорируем ошибки парсинга
|
||||
}
|
||||
}
|
||||
}
|
||||
ignore_failure: true
|
||||
|
||||
# 7. Удаление промежуточных полей
|
||||
- drop_fields:
|
||||
when:
|
||||
equals:
|
||||
log_type: "angie-error"
|
||||
fields: [
|
||||
"error_date", "error_time", "error.error_date", "error.error_time",
|
||||
"error.datetime", "error.rest", "error.pid_worker",
|
||||
"error.request", "error.message", "tags"
|
||||
]
|
||||
ignore_missing: true
|
||||
# Общий drop_fields для всех логов
|
||||
- drop_fields:
|
||||
fields: ["agent","ecs.version","log.offset","input.type","type","log.flags"]
|
||||
ignore_missing: true
|
||||
...
|
||||
1
roles/fluent_bit_install/README.md
Normal file
1
roles/fluent_bit_install/README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Роль для установки и настройке Fluent-bit для отгрузки логов PT AF заказчикам
|
||||
6
roles/fluent_bit_install/files/fluent-bit.conf
Executable file
6
roles/fluent_bit_install/files/fluent-bit.conf
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
[SERVICE]
|
||||
flush 1
|
||||
storage.path /var/log/flb-storage/
|
||||
parsers_file parsers.conf
|
||||
plugins_file plugins.conf
|
||||
@INCLUDE ptaf-*.conf
|
||||
31
roles/fluent_bit_install/files/test.conf
Normal file
31
roles/fluent_bit_install/files/test.conf
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
[INPUT]
|
||||
Name syslog
|
||||
Tag test
|
||||
Parser syslog-rfc5424
|
||||
Mode tcp
|
||||
Listen 172.17.0.1
|
||||
Port 5141
|
||||
Buffer_Max_Size 128KB
|
||||
Buffer_Chunk_Size 15MB
|
||||
storage.type filesystem
|
||||
|
||||
[OUTPUT]
|
||||
Name syslog
|
||||
syslog_format rfc5424
|
||||
Match test
|
||||
host 10.8.0.24
|
||||
port 5684
|
||||
mode tcp
|
||||
tls on
|
||||
tls.verify off
|
||||
tls.debug 1
|
||||
syslog_severity_preset 6
|
||||
syslog_facility_preset 16
|
||||
syslog_hostname_key host
|
||||
syslog_appname_key ident
|
||||
syslog_procid_key pid
|
||||
syslog_msgid_key msgid
|
||||
syslog_message_key message
|
||||
storage.total_limit_size 3000MB
|
||||
Retry_Limit False
|
||||
syslog_maxsize 51200
|
||||
34
roles/fluent_bit_install/tasks/main.yml
Executable file
34
roles/fluent_bit_install/tasks/main.yml
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
- name: Check if fluent-bit is installed
|
||||
ansible.builtin.command: dpkg -l fluent-bit
|
||||
register: fluent_bit_check
|
||||
ignore_errors: true
|
||||
changed_when: false
|
||||
|
||||
- name: Fluent-bit install
|
||||
ansible.builtin.shell: "curl https://raw.githubusercontent.com/fluent/fluent-bit/master/install.sh | sh"
|
||||
when: fluent_bit_check.rc != 0
|
||||
|
||||
- name: Copy fluent-bit conf
|
||||
ansible.builtin.copy:
|
||||
src: files/fluent-bit.conf
|
||||
dest: /etc/fluent-bit/fluent-bit.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Copy input output conf
|
||||
ansible.builtin.template:
|
||||
src: "{{ item }}"
|
||||
dest: /etc/fluent-bit/
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
loop: "{{ lookup('fileglob', 'files/ptaf-*', wantlist=True) }}"
|
||||
|
||||
- name: Ensure fluent-bit service is running and enabled
|
||||
ansible.builtin.service:
|
||||
name: fluent-bit
|
||||
state: restarted
|
||||
enabled: true
|
||||
...
|
||||
3
roles/grafana_gen/defaults/main.yml
Normal file
3
roles/grafana_gen/defaults/main.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
grafana_gen_bin: "/usr/local/bin/grafana_gen"
|
||||
...
|
||||
34
roles/grafana_gen/files/grafana_gen_off.sh
Normal file
34
roles/grafana_gen/files/grafana_gen_off.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для комментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/10 * * * * /usr/local/bin/grafana_gen"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в незакомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка найдена и не закомментирована - комментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^\(${ESCAPED_LINE}\)|#\1|" | crontab -
|
||||
echo "Строка закомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка уже закомментирована
|
||||
echo "Строка уже закомментирована, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
34
roles/grafana_gen/files/grafana_gen_on.sh
Normal file
34
roles/grafana_gen/files/grafana_gen_on.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для раскомментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/10 * * * * /usr/local/bin/grafana_gen"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в закомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка найдена и закомментирована - раскомментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^#\(${ESCAPED_LINE}\)|\1|" | crontab -
|
||||
echo "Строка раскомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка уже раскомментирована
|
||||
echo "Строка уже активна, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
51
roles/grafana_gen/tasks/main.yml
Normal file
51
roles/grafana_gen/tasks/main.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
- name: Copy grafana gen from cloud
|
||||
ansible.builtin.get_url:
|
||||
url: ""
|
||||
dest: "{{ grafana_gen_bin }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0744'
|
||||
timeout: 60
|
||||
force: true
|
||||
retries: 3
|
||||
delay: 10
|
||||
register: download_result
|
||||
|
||||
- name: Copy grafana gen on and off scripts
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: /usr/local/bin/
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0744'
|
||||
loop: "{{ lookup('fileglob', 'files/*.sh', wantlist=True) }}"
|
||||
|
||||
- name: Create cron task for update
|
||||
ansible.builtin.cron:
|
||||
name: "Run grafana_gen every 10 minutes"
|
||||
minute: "*/10"
|
||||
hour: "*"
|
||||
day: "*"
|
||||
month: "*"
|
||||
weekday: "*"
|
||||
job: "{{ grafana_gen_bin }}"
|
||||
user: root
|
||||
when: download_result.changed
|
||||
|
||||
- name: Create directory /etc/grafana_gen
|
||||
ansible.builtin.file:
|
||||
path: /etc/grafana_gen
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
|
||||
- name: Create .env
|
||||
ansible.builtin.template:
|
||||
src: grafana_gen.env.j2
|
||||
dest: /etc/grafana_gen/grafana_gen.env
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0600'
|
||||
...
|
||||
7
roles/grafana_gen/templates/grafana_gen.env.j2
Normal file
7
roles/grafana_gen/templates/grafana_gen.env.j2
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
DB_USER={{ db_user }}
|
||||
DB_PASSWORD={{ db_password }}
|
||||
GIT_TOKEN={{ git_token }}
|
||||
GRAFANA_API_KEY={{ grafana_api_token }}
|
||||
GRAFANA_URL=https://metrics.cloudstack.ru
|
||||
GRAFANA_FOLDER=WAF - PTAF
|
||||
DASHBOARD_TITLE=PT AF Requests
|
||||
4
roles/logrotate_run_now/tasks/main.yml
Normal file
4
roles/logrotate_run_now/tasks/main.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
- name: Logrotate run
|
||||
ansible.builtin.command: "logrotate -f /etc/logrotate.conf"
|
||||
...
|
||||
11
roles/logrotate_settings/files/logrotate.timer
Executable file
11
roles/logrotate_settings/files/logrotate.timer
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
[Unit]
|
||||
Description=Daily rotation of log files
|
||||
Documentation=man:logrotate(8) man:logrotate.conf(5)
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*:0/10
|
||||
AccuracySec=1m
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
15
roles/logrotate_settings/files/logrotate_angie
Executable file
15
roles/logrotate_settings/files/logrotate_angie
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
/var/log/angie/*.log {
|
||||
hourly
|
||||
missingok
|
||||
rotate 2
|
||||
compress
|
||||
maxage 15
|
||||
notifempty
|
||||
create 644 angie angie
|
||||
sharedscripts
|
||||
postrotate
|
||||
if [ -f /run/angie.pid ]; then
|
||||
kill -HUP $(cat /run/angie.pid)
|
||||
fi
|
||||
endscript
|
||||
}
|
||||
20
roles/logrotate_settings/files/logrotate_ptaf
Normal file
20
roles/logrotate_settings/files/logrotate_ptaf
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/var/log/ptaf_nginx/*/*/*.log {
|
||||
daily
|
||||
maxsize 200M
|
||||
missingok
|
||||
rotate 3
|
||||
maxage 15
|
||||
compress
|
||||
notifempty
|
||||
create 777 root root
|
||||
su root root
|
||||
sharedscripts
|
||||
postrotate
|
||||
CONTAINER_IDS=$(docker ps --filter "status=running" --filter "name=ptaf*" --format='{{.ID}}')
|
||||
for CONTAINER_ID in $CONTAINER_IDS; do
|
||||
docker exec "$CONTAINER_ID" /opt/ptaf/bin/ptaf-nginx -s reopen
|
||||
done
|
||||
|
||||
find /var/log/ptaf_nginx -type f -name "*.log" -size 0 -mmin +60 -delete 2>/dev/null || true
|
||||
endscript
|
||||
}
|
||||
15
roles/logrotate_settings/files/logrotate_solidwall
Normal file
15
roles/logrotate_settings/files/logrotate_solidwall
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/var/log/solidwall/*.log {
|
||||
daily
|
||||
maxsize 200M
|
||||
missingok
|
||||
rotate 3
|
||||
maxage 15
|
||||
compress
|
||||
notifempty
|
||||
create 777 root adm
|
||||
su root adm
|
||||
sharedscripts
|
||||
postrotate
|
||||
systemctl reload solidwall-nginx > /dev/null 2>&1 || true
|
||||
endscript
|
||||
}
|
||||
26
roles/logrotate_settings/files/logrotate_syslog
Executable file
26
roles/logrotate_settings/files/logrotate_syslog
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
/var/log/syslog
|
||||
/var/log/mail.info
|
||||
/var/log/mail.warn
|
||||
/var/log/mail.err
|
||||
/var/log/mail.log
|
||||
/var/log/daemon.log
|
||||
/var/log/kern.log
|
||||
/var/log/auth.log
|
||||
/var/log/user.log
|
||||
/var/log/lpr.log
|
||||
/var/log/cron.log
|
||||
/var/log/debug
|
||||
/var/log/messages
|
||||
{
|
||||
rotate 2
|
||||
daily
|
||||
maxsize 1G
|
||||
missingok
|
||||
notifempty
|
||||
compress
|
||||
su root root
|
||||
sharedscripts
|
||||
postrotate
|
||||
/usr/lib/rsyslog/rsyslog-rotate
|
||||
endscript
|
||||
}
|
||||
64
roles/logrotate_settings/tasks/main.yml
Executable file
64
roles/logrotate_settings/tasks/main.yml
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
- name: Check logrotate dir
|
||||
ansible.builtin.file:
|
||||
path: /etc/logrotate.d/
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Logrotate angie settings
|
||||
ansible.builtin.copy:
|
||||
src: logrotate_angie
|
||||
dest: /etc/logrotate.d/angie
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
when: inventory_hostname is match("^PTAF")
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Logrotate syslog settings
|
||||
ansible.builtin.copy:
|
||||
src: files/logrotate_syslog
|
||||
dest: /etc/logrotate.d/rsyslog
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Logrotate PT AF settings
|
||||
ansible.builtin.copy:
|
||||
src: files/logrotate_ptaf
|
||||
dest: /etc/logrotate.d/ptaf
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
when: inventory_hostname is match("^PTAF")
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Logrotate SolidWall settings
|
||||
ansible.builtin.copy:
|
||||
src: files/logrotate_solidwall
|
||||
dest: /etc/logrotate.d/solidwall
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
when: inventory_hostname is match("^ce-swall")
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Logrotate timer
|
||||
ansible.builtin.copy:
|
||||
src: files/logrotate.timer
|
||||
dest: /lib/systemd/system/logrotate.timer
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
- name: Start and enable logrotate timer
|
||||
ansible.builtin.systemd:
|
||||
name: logrotate.timer
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
...
|
||||
4
roles/magos/README.md
Normal file
4
roles/magos/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Роль magos
|
||||
|
||||
Роль раскатывает на хосты с docker PT AF утилиту magos, которая запускается каждые пять минут по крону и ходит в БД и API ServicePipe и актуализирует данные по upstream, weight и server_name.
|
||||
Логи выполненных операций пишутся в файл и каждые день отправляются в git. Конфиги Angie и PT AF Nginx также отправляются в git.
|
||||
7
roles/magos/defaults/main.yml
Normal file
7
roles/magos/defaults/main.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
magos_upload_logs_script_path: "/usr/local/bin/upload_logs.sh"
|
||||
magos_upload_configs_script_path: "/usr/local/bin/upload_configs.sh"
|
||||
magos_metrics_script_path: "/usr/local/bin/magos_metrics.sh"
|
||||
magos_log_dir: "/var/log/update_apps_configs_logs"
|
||||
magos_log_file: "/var/log/update_apps_configs_logs/update_apps_configs.log"
|
||||
...
|
||||
34
roles/magos/files/magos_off.sh
Normal file
34
roles/magos/files/magos_off.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для комментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/5 * * * * /usr/local/bin/magos_metrics.sh"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в незакомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка найдена и не закомментирована - комментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^\(${ESCAPED_LINE}\)|#\1|" | crontab -
|
||||
echo "Строка закомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка уже закомментирована
|
||||
echo "Строка уже закомментирована, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
34
roles/magos/files/magos_on.sh
Normal file
34
roles/magos/files/magos_on.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Скрипт для раскомментирования строки в root crontab
|
||||
|
||||
# Строка, которую нужно найти (без комментария)
|
||||
TARGET_LINE="*/5 * * * * /usr/local/bin/magos_metrics.sh"
|
||||
|
||||
# Проверяем, запущен ли скрипт от root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Ошибка: скрипт должен быть запущен от имени root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Получаем текущий crontab
|
||||
CURRENT_CRON=$(crontab -l 2>/dev/null)
|
||||
|
||||
# Экранируем специальные символы для использования в regex
|
||||
ESCAPED_LINE=$(echo "$TARGET_LINE" | sed 's/[.*[\^$]/\\&/g')
|
||||
|
||||
# Проверяем, существует ли строка в закомментированном виде
|
||||
if echo "$CURRENT_CRON" | grep -qE "^#${ESCAPED_LINE}"; then
|
||||
# Строка найдена и закомментирована - раскомментируем её
|
||||
echo "$CURRENT_CRON" | sed "s|^#\(${ESCAPED_LINE}\)|\1|" | crontab -
|
||||
echo "Строка раскомментирована успешно"
|
||||
exit 0
|
||||
elif echo "$CURRENT_CRON" | grep -qE "^${ESCAPED_LINE}"; then
|
||||
# Строка уже раскомментирована
|
||||
echo "Строка уже активна, ничего не делаем"
|
||||
exit 0
|
||||
else
|
||||
# Строка не найдена вообще
|
||||
echo "Предупреждение: строка не найдена в crontab"
|
||||
exit 2
|
||||
fi
|
||||
90
roles/magos/tasks/main.yml
Normal file
90
roles/magos/tasks/main.yml
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
---
|
||||
- name: Tuning core
|
||||
ansible.builtin.include_tasks: tuning_core.yml
|
||||
|
||||
- name: Create directoies
|
||||
ansible.builtin.file:
|
||||
path: /var/log/{{ item }}
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
loop:
|
||||
- update_apps_configs_logs
|
||||
- ptaf_configs
|
||||
|
||||
- name: DNS conf
|
||||
ansible.builtin.include_role:
|
||||
name: dns_settings
|
||||
|
||||
- name: Copy certs
|
||||
ansible.builtin.include_role:
|
||||
name: certs_settings
|
||||
|
||||
- name: Logrotate settings
|
||||
ansible.builtin.include_role:
|
||||
name: logrotate_settings
|
||||
|
||||
- name: Node_exporter install
|
||||
ansible.builtin.include_role:
|
||||
name: node_exporter_install
|
||||
|
||||
- name: Copy magos from cloud
|
||||
ansible.builtin.include_role:
|
||||
name: update_magos
|
||||
register: magos_download_result
|
||||
|
||||
- name: Copy upload log script to host
|
||||
ansible.builtin.template:
|
||||
src: templates/upload_logs.sh.j2
|
||||
dest: "{{ magos_upload_logs_script_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0744'
|
||||
|
||||
- name: Copy upload configs script to host
|
||||
ansible.builtin.template:
|
||||
src: templates/upload_configs.sh.j2
|
||||
dest: "{{ magos_upload_configs_script_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0744'
|
||||
|
||||
- name: Copy magos metrics script to host
|
||||
ansible.builtin.template:
|
||||
src: templates/magos_metrics.sh.j2
|
||||
dest: "{{ magos_metrics_script_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0744'
|
||||
|
||||
- name: Start soft
|
||||
ansible.builtin.shell: "{{ magos_metrics_script_path }}"
|
||||
when: magos_download_result.changed
|
||||
ignore_errors: true
|
||||
|
||||
- name: Create cron task for update
|
||||
ansible.builtin.cron:
|
||||
name: "Run update_apps_configs every 5 minutes"
|
||||
minute: "*/5"
|
||||
hour: "*"
|
||||
day: "*"
|
||||
month: "*"
|
||||
weekday: "*"
|
||||
job: "{{ magos_metrics_script_path }}"
|
||||
user: root
|
||||
|
||||
- name: Copy magos on and off scripts
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: /usr/local/bin/
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0744'
|
||||
loop: "{{ lookup('fileglob', 'files/*.sh', wantlist=True) }}"
|
||||
|
||||
- name: Auspex install
|
||||
ansible.builtin.include_role:
|
||||
name: auspex
|
||||
ignore_errors: true
|
||||
...
|
||||
19
roles/magos/tasks/tuning_core.yml
Executable file
19
roles/magos/tasks/tuning_core.yml
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
- name: Backup sysctl
|
||||
ansible.builtin.copy:
|
||||
src: /etc/sysctl.conf
|
||||
dest: /etc/sysctl.conf.backup
|
||||
remote_src: true
|
||||
|
||||
- name: Load conntrack module
|
||||
ansible.builtin.modprobe:
|
||||
name: nf_conntrack
|
||||
state: present
|
||||
|
||||
- name: Tuning core
|
||||
ansible.builtin.sysctl:
|
||||
name: "{{ item.key }}"
|
||||
value: "{{ item.value }}"
|
||||
loop: "{{ sysctl_params | dict2items }}"
|
||||
notify: Reload sysctl
|
||||
...
|
||||
60
roles/magos/templates/magos_metrics.sh.j2
Normal file
60
roles/magos/templates/magos_metrics.sh.j2
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Конфигурация
|
||||
METRIC_FILE="/var/lib/node_exporter/textfile_collector/cron_magos.prom"
|
||||
TIMEOUT_SECONDS=120 # 2 минуты максимум на выполнение
|
||||
mkdir -p "$(dirname ${METRIC_FILE})"
|
||||
|
||||
# Закрываем stdin чтобы скрипты не ждали ввода
|
||||
exec </dev/null
|
||||
|
||||
START=$(date +%s)
|
||||
STATUS=0
|
||||
|
||||
# Функция для безопасного выполнения команды с timeout
|
||||
safe_run() {
|
||||
local cmd="$1"
|
||||
local description="$2"
|
||||
|
||||
# Запускаем с timeout и закрытым stdin
|
||||
timeout ${TIMEOUT_SECONDS} bash -c "${cmd}" </dev/null
|
||||
local exit_code=$?
|
||||
|
||||
if [ ${exit_code} -eq 124 ]; then
|
||||
echo "[$(date)] ERROR: ${description} timed out after ${TIMEOUT_SECONDS}s" >> /var/log/cron_monitor_errors.log
|
||||
return 1
|
||||
elif [ ${exit_code} -ne 0 ]; then
|
||||
echo "[$(date)] ERROR: ${description} failed with exit code ${exit_code}" >> /var/log/cron_monitor_errors.log
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Выполняем команды последовательно
|
||||
if safe_run "{{ magos_bin }} > {{ magos_log_file }} 2>&1" "magos"; then
|
||||
if safe_run "{{ magos_upload_logs_script_path }}" "upload_logs.sh"; then
|
||||
if safe_run "{{ magos_upload_configs_script_path }}" "upload_configs.sh"; then
|
||||
STATUS=1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
DURATION=$(($(date +%s) - START))
|
||||
|
||||
# Записываем метрики
|
||||
cat > "${METRIC_FILE}" << EOF
|
||||
# HELP cron_magos_status Cron job execution status (1=success, 0=failure)
|
||||
# TYPE cron_magos_status gauge
|
||||
cron_magos_status ${STATUS}
|
||||
|
||||
# HELP cron_magos_last_run Last execution timestamp
|
||||
# TYPE cron_magos_last_run gauge
|
||||
cron_magos_last_run $(date +%s)
|
||||
|
||||
# HELP cron_magos_duration_seconds Execution duration
|
||||
# TYPE cron_magos_duration_seconds gauge
|
||||
cron_magos_duration_seconds ${DURATION}
|
||||
EOF
|
||||
|
||||
exit $((1 - STATUS))
|
||||
163
roles/magos/templates/upload_configs.sh.j2
Normal file
163
roles/magos/templates/upload_configs.sh.j2
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
BRANCH=$(hostname)
|
||||
ANGIE_CONF_DIR="/etc/angie/http.d/"
|
||||
PTAF_CONF_DIR="/home/install/conf/ptaf-nginx/"
|
||||
REPO_URL="https://{{ git_token }}@svc-git.cirex.ru/cloudstack/ptaf_config_logs.git"
|
||||
|
||||
# Функция для обработки одной директории
|
||||
process_directory() {
|
||||
local dir="$1"
|
||||
local branch_suffix="$2"
|
||||
local full_branch="${BRANCH}-${branch_suffix}"
|
||||
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') ======================================"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Processing directory: $dir"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Branch: $full_branch"
|
||||
|
||||
cd "$dir"
|
||||
|
||||
# Если это не git-репозиторий — инициализируем новый
|
||||
if [ ! -d ".git" ]; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Инициализируем новый локальный git-репозиторий"
|
||||
git init
|
||||
git config user.name "ptaf-sync-script"
|
||||
git config user.email "ptaf-sync-script@$(hostname)"
|
||||
fi
|
||||
|
||||
# Настраиваем пользователя
|
||||
git config user.name "ptaf-sync-script" 2>/dev/null || true
|
||||
git config user.email "ptaf-sync-script@$(hostname)" 2>/dev/null || true
|
||||
|
||||
# Настраиваем стратегию разрешения конфликтов: всегда предпочитать локальную версию
|
||||
git config pull.rebase true
|
||||
git config rebase.autoStash true
|
||||
|
||||
# Добавляем/проверяем remote origin
|
||||
if ! git remote | grep -q '^origin$'; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Добавляем remote origin"
|
||||
git remote add origin "$REPO_URL"
|
||||
else
|
||||
git remote set-url origin "$REPO_URL"
|
||||
fi
|
||||
|
||||
# Очищаем возможные битые ссылки
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Очищаем старые ссылки"
|
||||
git remote prune origin 2>/dev/null || true
|
||||
|
||||
# КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: Получаем только нашу ветку
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Проверяем существование ветки '$full_branch' на сервере"
|
||||
if git ls-remote --heads origin "$full_branch" | grep -q "$full_branch"; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Ветка существует на сервере, получаем её"
|
||||
git fetch origin "$full_branch:refs/remotes/origin/$full_branch" --force 2>/dev/null || {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Ошибка при fetch, очищаем и повторяем"
|
||||
rm -f .git/refs/remotes/origin/"$full_branch"
|
||||
git fetch origin "$full_branch:refs/remotes/origin/$full_branch" --force 2>/dev/null || true
|
||||
}
|
||||
BRANCH_EXISTS_REMOTE=true
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Ветка '$full_branch' ещё не существует на сервере"
|
||||
BRANCH_EXISTS_REMOTE=false
|
||||
fi
|
||||
|
||||
# Проверяем, существует ли ветка локально
|
||||
if ! git show-ref --verify --quiet refs/heads/"$full_branch"; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Создаём локальную ветку '$full_branch'"
|
||||
|
||||
if [ "$BRANCH_EXISTS_REMOTE" = true ]; then
|
||||
# Создаём ветку на основе удалённой
|
||||
git checkout -b "$full_branch" "origin/$full_branch"
|
||||
else
|
||||
# Создаём новую orphan ветку
|
||||
git checkout --orphan "$full_branch" 2>/dev/null || git checkout -b "$full_branch"
|
||||
git rm -rf . 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
git checkout "$full_branch" 2>/dev/null || {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Ошибка переключения, принудительно переключаемся"
|
||||
git checkout -f "$full_branch"
|
||||
}
|
||||
fi
|
||||
|
||||
# Добавляем все файлы
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Добавляем все файлы"
|
||||
git add -A .
|
||||
|
||||
# Проверяем наличие изменений и коммитим ИХ СНАЧАЛА
|
||||
if ! git diff-index --quiet HEAD -- 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
|
||||
COMMIT_MSG="Config update $(date '+%Y-%m-%d %H:%M:%S') [host: $(hostname -s)]"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Обнаружены локальные изменения — коммитим их ПЕРЕД pull"
|
||||
git commit -m "$COMMIT_MSG"
|
||||
fi
|
||||
|
||||
# Теперь делаем pull/rebase если ветка существует на сервере
|
||||
if [ "$BRANCH_EXISTS_REMOTE" = true ]; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Синхронизация с удалённой веткой origin/$full_branch"
|
||||
|
||||
# Вариант 1: Пробуем обычный rebase
|
||||
if git pull --rebase origin "$full_branch" 2>/dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Rebase успешен"
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Конфликт при rebase, разрешаем автоматически"
|
||||
|
||||
# Прерываем rebase
|
||||
git rebase --abort 2>/dev/null || true
|
||||
|
||||
# Вариант 2: Используем стратегию merge с предпочтением локальных изменений
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Используем merge стратегию с предпочтением локальных изменений"
|
||||
if git pull --no-rebase --strategy=recursive --strategy-option=ours origin "$full_branch" 2>/dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Merge успешен с предпочтением локальных изменений"
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Merge также не удался, используем force push"
|
||||
# Вариант 3: Если и merge не сработал - будем пушить с force
|
||||
# (это безопасно, так как мы уже закоммитили все локальные изменения)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Создаём keepalive коммит, если изменений больше нет
|
||||
if git diff-index --quiet HEAD -- 2>/dev/null && git diff --cached --quiet 2>/dev/null; then
|
||||
COMMIT_MSG="No changes – keepalive commit $(date '+%Y-%m-%d %H:%M:%S') [host: $(hostname -s)]"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Нет новых изменений — создаём пустой коммит"
|
||||
git commit --allow-empty -m "$COMMIT_MSG"
|
||||
fi
|
||||
|
||||
# Пушим с fallback вариантами
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Пушим ветку $full_branch"
|
||||
if git push origin "$full_branch" --set-upstream 2>/dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Push успешен"
|
||||
elif git push origin "$full_branch" --force-with-lease 2>/dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Push успешен с --force-with-lease"
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Используем force push (локальные изменения приоритетнее)"
|
||||
git push origin "$full_branch" --force || {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') ОШИБКА: Не удалось запушить ветку $full_branch"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Успешно завершено: $dir"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Обрабатываем первую директорию
|
||||
if [ -d "$ANGIE_CONF_DIR" ]; then
|
||||
process_directory "$ANGIE_CONF_DIR" "angie-conf" || {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') ОШИБКА при обработке $ANGIE_CONF_DIR"
|
||||
}
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') ПРЕДУПРЕЖДЕНИЕ: Директория $ANGIE_CONF_DIR не найдена"
|
||||
fi
|
||||
|
||||
# Обрабатываем вторую директорию
|
||||
if [ -d "$PTAF_CONF_DIR" ]; then
|
||||
process_directory "$PTAF_CONF_DIR" "ptaf-conf" || {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') ОШИБКА при обработке $PTAF_CONF_DIR"
|
||||
}
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') ПРЕДУПРЕЖДЕНИЕ: Директория $PTAF_CONF_DIR не найдена"
|
||||
fi
|
||||
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') ======================================"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') All backups completed."
|
||||
132
roles/magos/templates/upload_logs.sh.j2
Normal file
132
roles/magos/templates/upload_logs.sh.j2
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
BRANCH=$(hostname)-logs
|
||||
LOGS_DIR="{{ magos_log_dir }}"
|
||||
REPO_URL="https://{{ git_token }}@svc-git.cirex.ru/cloudstack/ptaf_config_logs.git"
|
||||
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Начинаем синхронизацию логов в ветку '$BRANCH'"
|
||||
cd "$LOGS_DIR"
|
||||
|
||||
# Если это не git-репозиторий — инициализируем новый
|
||||
if [ ! -d ".git" ]; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Инициализируем новый локальный git-репозиторий"
|
||||
git init
|
||||
git config user.name "ptaf-sync-script"
|
||||
git config user.email "ptaf-sync-script@$(hostname)"
|
||||
fi
|
||||
|
||||
# Настраиваем пользователя
|
||||
git config user.name "ptaf-sync-script" 2>/dev/null || true
|
||||
git config user.email "ptaf-sync-script@$(hostname)" 2>/dev/null || true
|
||||
|
||||
# Настраиваем стратегию разрешения конфликтов
|
||||
git config pull.rebase true
|
||||
git config rebase.autoStash true
|
||||
|
||||
# Добавляем/проверяем remote origin
|
||||
if ! git remote | grep -q '^origin$'; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Добавляем remote origin: $REPO_URL"
|
||||
git remote add origin "$REPO_URL"
|
||||
else
|
||||
# Обновляем URL на случай изменения токена
|
||||
git remote set-url origin "$REPO_URL"
|
||||
fi
|
||||
|
||||
# Очищаем возможные битые ссылки перед fetch
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Очищаем старые ссылки"
|
||||
git remote prune origin 2>/dev/null || true
|
||||
|
||||
# КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: Получаем только нашу ветку, а не все ветки
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Получаем информацию только о ветке '$BRANCH'"
|
||||
if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then
|
||||
# Ветка существует на сервере - получаем только её
|
||||
git fetch origin "$BRANCH:refs/remotes/origin/$BRANCH" --force 2>/dev/null || {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Ошибка при fetch, пытаемся очистить и повторить"
|
||||
rm -f .git/refs/remotes/origin/"$BRANCH"
|
||||
git fetch origin "$BRANCH:refs/remotes/origin/$BRANCH" --force 2>/dev/null || true
|
||||
}
|
||||
BRANCH_EXISTS_REMOTE=true
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Ветка '$BRANCH' ещё не существует на сервере"
|
||||
BRANCH_EXISTS_REMOTE=false
|
||||
fi
|
||||
|
||||
# Проверяем, существует ли ветка локально
|
||||
if ! git show-ref --verify --quiet refs/heads/"$BRANCH"; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Создаём локальную ветку '$BRANCH'"
|
||||
|
||||
if [ "$BRANCH_EXISTS_REMOTE" = true ]; then
|
||||
# Создаём ветку на основе удалённой
|
||||
git checkout -b "$BRANCH" "origin/$BRANCH"
|
||||
else
|
||||
# Создаём новую ветку (может быть пустой или на основе текущего HEAD)
|
||||
git checkout --orphan "$BRANCH" 2>/dev/null || git checkout -b "$BRANCH"
|
||||
# Очищаем индекс для orphan ветки
|
||||
git rm -rf . 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
git checkout "$BRANCH" 2>/dev/null || {
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Ошибка переключения на ветку, принудительно переключаемся"
|
||||
git checkout -f "$BRANCH"
|
||||
}
|
||||
fi
|
||||
|
||||
# Добавляем все изменения
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Добавляем все изменения"
|
||||
git add -A
|
||||
|
||||
# Проверяем, есть ли что коммитить и коммитим СНАЧАЛА
|
||||
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
|
||||
COMMIT_MSG="Auto commit $(date '+%Y-%m-%d %H:%M:%S') [host: $(hostname)]"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Коммитим локальные изменения ПЕРЕД pull: $COMMIT_MSG"
|
||||
git commit -m "$COMMIT_MSG"
|
||||
fi
|
||||
|
||||
# Теперь делаем pull/rebase если ветка существует на сервере
|
||||
if [ "$BRANCH_EXISTS_REMOTE" = true ]; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Синхронизация с удалённой веткой origin/$BRANCH"
|
||||
|
||||
# Вариант 1: Пробуем обычный rebase
|
||||
if git pull --rebase origin "$BRANCH" 2>/dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Rebase успешен"
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Конфликт при rebase, разрешаем автоматически"
|
||||
|
||||
# Прерываем rebase
|
||||
git rebase --abort 2>/dev/null || true
|
||||
|
||||
# Вариант 2: Используем стратегию merge с предпочтением локальных изменений
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Используем merge стратегию с предпочтением локальных изменений"
|
||||
if git pull --no-rebase --strategy=recursive --strategy-option=ours origin "$BRANCH" 2>/dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Merge успешен с предпочтением локальных изменений"
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Merge также не удался, используем force push"
|
||||
# Вариант 3: Если и merge не сработал - будем пушить с force
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Проверяем снова, есть ли что коммитить после синхронизации
|
||||
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Есть изменения после синхронизации, коммитим"
|
||||
git commit -m "Post-sync changes $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
fi
|
||||
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Пушим в origin/$BRANCH"
|
||||
# Пушим с fallback вариантами
|
||||
if git push origin "$BRANCH" --set-upstream 2>/dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Push успешен"
|
||||
elif git push origin "$BRANCH" --force-with-lease 2>/dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Push успешен с --force-with-lease"
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Используем force push (локальные изменения приоритетнее)"
|
||||
git push origin "$BRANCH" --force
|
||||
fi
|
||||
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Успешно закоммичено и запушено"
|
||||
|
||||
# Запускаем следующий скрипт
|
||||
{{ magos_upload_configs_script_path }} &
|
||||
|
||||
exit 0
|
||||
3
roles/magos_off/README.md
Normal file
3
roles/magos_off/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Magos off
|
||||
|
||||
Отключение автоматизации настройки WAF
|
||||
16
roles/magos_off/tasks/main.yml
Normal file
16
roles/magos_off/tasks/main.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
- name: Check script
|
||||
ansible.builtin.stat:
|
||||
path: /usr/local/bin/magos_off.sh
|
||||
register: script_stat
|
||||
|
||||
- name: Waf automata off
|
||||
ansible.builtin.command: /usr/local/bin/magos_off.sh
|
||||
register: result
|
||||
when: script_stat.stat.exists and script_stat.stat.executable
|
||||
|
||||
- name: Show result
|
||||
ansible.builtin.debug:
|
||||
var: result.stdout
|
||||
when: script_stat.stat.exists
|
||||
...
|
||||
3
roles/magos_on/README.md
Normal file
3
roles/magos_on/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Magos on
|
||||
|
||||
Включение автоматизации настройки WAF
|
||||
16
roles/magos_on/tasks/main.yml
Normal file
16
roles/magos_on/tasks/main.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
- name: Check script
|
||||
ansible.builtin.stat:
|
||||
path: /usr/local/bin/magos_on.sh
|
||||
register: script_stat
|
||||
|
||||
- name: Waf automata on
|
||||
ansible.builtin.command: /usr/local/bin/magos_on.sh
|
||||
register: result
|
||||
when: script_stat.stat.exists and script_stat.stat.executable
|
||||
|
||||
- name: Show result
|
||||
ansible.builtin.debug:
|
||||
var: result.stdout
|
||||
when: script_stat.stat.exists
|
||||
...
|
||||
3
roles/netplan_update/README.md
Executable file
3
roles/netplan_update/README.md
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
# Роль для обновления Netplan
|
||||
|
||||
Автоматически смотрит какие адреса назначены на интерфейсы и согласно этому заполняет конфиг Netplan. Сети до которых нужен доступ прописываются в `defaults/main.yml`.
|
||||
5
roles/netplan_update/defaults/main.yml
Normal file
5
roles/netplan_update/defaults/main.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
netplan_update_gray_additional_routes:
|
||||
- 10.11.2.5/32 # Zabbix
|
||||
- 10.10.3.0/24 # Серверы AD DNS NTP
|
||||
...
|
||||
36
roles/netplan_update/tasks/main.yml
Executable file
36
roles/netplan_update/tasks/main.yml
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
- name: Set gray interface (10.100.x.x)
|
||||
ansible.builtin.set_fact:
|
||||
interface_1: "{{ item.key }}"
|
||||
interface_ip_1_with_mask: "{{ item.value.ipv4.address }}/{{ item.value.ipv4.prefix }}"
|
||||
loop: "{{ ansible_facts | dict2items }}"
|
||||
when:
|
||||
- item.value.ipv4 is defined
|
||||
- item.value.ipv4.address is defined
|
||||
- item.value.ipv4.address.startswith('10.100')
|
||||
|
||||
- name: Set white interface
|
||||
ansible.builtin.set_fact:
|
||||
interface_2: "{{ item.key }}"
|
||||
interface_ip_2_with_mask: "{{ item.value.ipv4.address }}/{{ item.value.ipv4.prefix }}"
|
||||
loop: "{{ ansible_facts | dict2items }}"
|
||||
when:
|
||||
- item.value.ipv4 is defined
|
||||
- item.value.ipv4.address is defined
|
||||
- not item.value.ipv4.address.startswith('10.100')
|
||||
- not item.value.ipv4.address.startswith('127.')
|
||||
|
||||
- name: Create backup
|
||||
ansible.builtin.command: cp /etc/netplan/99-netcfg-vmware.yaml /etc/netplan/99-netcfg-vmware.yaml.bak
|
||||
|
||||
- name: Copy new netplan config
|
||||
ansible.builtin.template:
|
||||
src: templates/99-netcfg-vmware.yaml.j2
|
||||
dest: /etc/netplan/99-netcfg-vmware.yaml
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0600'
|
||||
|
||||
- name: Apply new netplan
|
||||
ansible.builtin.command: sudo netplan apply
|
||||
...
|
||||
24
roles/netplan_update/templates/99-netcfg-vmware.yaml.j2
Executable file
24
roles/netplan_update/templates/99-netcfg-vmware.yaml.j2
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
# Changed from Ansible.
|
||||
network:
|
||||
version: 2
|
||||
renderer: networkd
|
||||
ethernets:
|
||||
{{ interface_1 }}:
|
||||
dhcp4: no
|
||||
dhcp6: no
|
||||
addresses:
|
||||
- {{ interface_ip_1_with_mask }}
|
||||
routes:
|
||||
{% for route in netplan_update_gray_additional_routes %}
|
||||
- to: {{ route }}
|
||||
via: {{ gray_gateway }}
|
||||
{% endfor %}
|
||||
{{ interface_2 }}:
|
||||
dhcp4: no
|
||||
dhcp6: no
|
||||
addresses:
|
||||
- {{ interface_ip_2_with_mask }}
|
||||
routes:
|
||||
- to: default
|
||||
via: {{ white_gateway }}
|
||||
|
||||
9
roles/new_node_settings/README.md
Executable file
9
roles/new_node_settings/README.md
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
# Роль для первичной настройки хоста после разворачивания
|
||||
|
||||
Данная роль выпоняет следующие действия:
|
||||
1. Устанавливает `Angie` и логротейт для него;
|
||||
2. Устанавливает `Zabbix-agent` через `Docker`;
|
||||
3. Ставит различный дополнительный софт из репозиториев, который может понадобиться;
|
||||
4. Конфигурирует `Chrony` и `DNS`;
|
||||
5. Настривает `SSH` и подкидывает ключи;
|
||||
6. Меняет пароль для пользователя `install`.
|
||||
0
roles/new_node_settings/files/authorized_keys
Executable file
0
roles/new_node_settings/files/authorized_keys
Executable file
8
roles/new_node_settings/tasks/change_password_for_user.yml
Executable file
8
roles/new_node_settings/tasks/change_password_for_user.yml
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
- name: Change password for install user
|
||||
ansible.builtin.user:
|
||||
name: "{{ username_on_new_hosts }}"
|
||||
password: "{{ install_ssh_pass | password_hash('sha512') }}"
|
||||
update_password: always
|
||||
state: present
|
||||
...
|
||||
49
roles/new_node_settings/tasks/main.yml
Executable file
49
roles/new_node_settings/tasks/main.yml
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
- name: DNS conf
|
||||
ansible.builtin.include_role:
|
||||
name: dns_settings
|
||||
|
||||
- name: Netplan update
|
||||
ansible.builtin.include_role:
|
||||
name: netplan_update
|
||||
|
||||
- name: Restart connection
|
||||
ansible.builtin.meta: reset_connection
|
||||
|
||||
- name: APT cache update
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
ignore_errors: true
|
||||
|
||||
- name: Different need soft install
|
||||
ansible.builtin.include_role:
|
||||
name: different_need_soft_install
|
||||
|
||||
- name: SolidWall repo install
|
||||
ansible.builtin.include_role:
|
||||
name: solidwall_repo_add
|
||||
when: inventory_hostname is match("^ce-swall")
|
||||
|
||||
- name: Node exporter install
|
||||
ansible.builtin.include_role:
|
||||
name: node_exporter_install
|
||||
|
||||
- name: SSH conf
|
||||
ansible.builtin.include_role:
|
||||
name: ssh_settings
|
||||
|
||||
- name: Logrotate settings
|
||||
ansible.builtin.include_role:
|
||||
name: logrotate_settings
|
||||
|
||||
- name: Chrony conf
|
||||
ansible.builtin.include_role:
|
||||
name: chrony_install
|
||||
|
||||
- name: Update firewall
|
||||
ansible.builtin.include_role:
|
||||
name: update_firewall
|
||||
|
||||
- name: Change password for user
|
||||
ansible.builtin.include_tasks: change_password_for_user.yml
|
||||
...
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue