Compare commits
8
Commits
4cf4def1f6
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ec315d96c | ||
|
|
664a544aa9 | ||
|
|
7373d5520f | ||
|
|
7230484b44 | ||
|
|
86dd59e462 | ||
|
|
c492399300 | ||
|
|
85cecf78f6 | ||
|
|
35d81332fa |
+13
@@ -0,0 +1,13 @@
|
||||
# Python / virtualenv
|
||||
.venv/
|
||||
venv/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Salt runtime artifacts
|
||||
.salt/
|
||||
|
||||
# IDE / OS files
|
||||
.idea/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
@@ -0,0 +1,16 @@
|
||||
extends: default
|
||||
|
||||
ignore: |
|
||||
.git/
|
||||
.venv/
|
||||
venv/
|
||||
|
||||
rules:
|
||||
comments-indentation: disable
|
||||
document-start:
|
||||
present: false
|
||||
line-length:
|
||||
max: 120
|
||||
level: warning
|
||||
truthy:
|
||||
check-keys: false
|
||||
@@ -0,0 +1,43 @@
|
||||
SHELL := /usr/bin/env bash
|
||||
.RECIPEPREFIX := >
|
||||
|
||||
SALT_CALL ?= salt-call
|
||||
SALT_LINT ?= salt-lint
|
||||
YAMLLINT ?= yamllint
|
||||
STATE_ROOT ?= .
|
||||
PILLAR_ROOT ?= ./pillar
|
||||
MINION_ID ?= p-mailgw-x2-01.lmru.tech
|
||||
MASTER_IP ?= 10.80.38.152
|
||||
MASTER_HOST ?= p-mailgw-x1-01.lmru.tech
|
||||
|
||||
.PHONY: lint check apply verify update-check update cluster-bootstrap
|
||||
|
||||
lint:
|
||||
>@if command -v $(SALT_LINT) >/dev/null 2>&1; then \
|
||||
> $(SALT_LINT) pmg/*.sls pillar/*.sls top.sls; \
|
||||
>else \
|
||||
> echo "salt-lint не найден, пропускаю"; \
|
||||
>fi
|
||||
>@if command -v $(YAMLLINT) >/dev/null 2>&1; then \
|
||||
> $(YAMLLINT) .; \
|
||||
>else \
|
||||
> echo "yamllint не найден, пропускаю"; \
|
||||
>fi
|
||||
|
||||
check:
|
||||
>sudo $(SALT_CALL) --local --id $(MINION_ID) --file-root=$(STATE_ROOT) --pillar-root=$(PILLAR_ROOT) state.apply pmg.init test=True
|
||||
|
||||
apply:
|
||||
>sudo $(SALT_CALL) --local --id $(MINION_ID) --file-root=$(STATE_ROOT) --pillar-root=$(PILLAR_ROOT) state.apply pmg.init
|
||||
|
||||
verify:
|
||||
>sudo $(SALT_CALL) --local --id $(MINION_ID) --file-root=$(STATE_ROOT) --pillar-root=$(PILLAR_ROOT) state.apply pmg.verify
|
||||
|
||||
update-check:
|
||||
>sudo $(SALT_CALL) --local --id $(MINION_ID) --file-root=$(STATE_ROOT) --pillar-root=$(PILLAR_ROOT) state.apply pmg.update test=True
|
||||
|
||||
update:
|
||||
>sudo $(SALT_CALL) --local --id $(MINION_ID) --file-root=$(STATE_ROOT) --pillar-root=$(PILLAR_ROOT) state.apply pmg.update
|
||||
|
||||
cluster-bootstrap:
|
||||
>bash "./scripts/cluster-bootstrap.sh" "$(MINION_ID)" "$(MASTER_HOST)" "$(MASTER_IP)"
|
||||
@@ -1,2 +1,143 @@
|
||||
# mailgw-salt
|
||||
# Salt baseline для PMG
|
||||
|
||||
Репозиторий содержит автономный набор Salt states для базовой эксплуатации и hardening Proxmox Mail Gateway.
|
||||
|
||||
## Что внутри
|
||||
|
||||
- `top.sls` — привязка состояний к minion ID.
|
||||
- `pillar/pmg.sls` — переменные и параметры PMG.
|
||||
- `pmg/*.sls` — состояния:
|
||||
- `common` (базовые пакеты),
|
||||
- `node_exporter` (установка и включение Prometheus node exporter),
|
||||
- `smtp_monitoring` (SMTP-метрики: blackbox probe для 25/26 и postfix queue/connection gauges),
|
||||
- `ssh_hardening`,
|
||||
- `custom_check` (кастомная AV-проверка PMG через KESL),
|
||||
- `security_agents` (Wazuh/Qualys),
|
||||
- `update` (пакетные обновления + pre/post проверки PMG),
|
||||
- `auto_updates` (security-only, без auto reboot),
|
||||
- `audit_logging`,
|
||||
- `pmg_baseline` (проверка/поддержание сервисов PMG),
|
||||
- `verify` (проверки состояния).
|
||||
- `pmg/files/*` — шаблоны конфигов.
|
||||
|
||||
## Требования
|
||||
|
||||
- Salt (рекомендуется актуальная LTS-ветка).
|
||||
- Debian/PMG узлы с systemd.
|
||||
- root-доступ на узел.
|
||||
|
||||
## Быстрый запуск
|
||||
|
||||
```bash
|
||||
make lint
|
||||
make check
|
||||
make apply
|
||||
make verify
|
||||
make update-check
|
||||
make update
|
||||
```
|
||||
|
||||
По умолчанию `MINION_ID=p-mailgw-x2-01.lmru.tech`. Можно переопределить:
|
||||
|
||||
```bash
|
||||
make check MINION_ID=p-mailgw-x1-01.lmru.tech
|
||||
```
|
||||
|
||||
## Основные переменные
|
||||
|
||||
Все параметры задаются в `pillar/pmg.sls`:
|
||||
|
||||
- `pmg.base_packages`
|
||||
- `pmg.ssh_allow_users`
|
||||
- `pmg.ssh_password_authentication`
|
||||
- `pmg.journald_system_max_use`
|
||||
- `pmg.journald_max_file_sec`
|
||||
- `pmg.node_exporter.*`
|
||||
- `pmg.smtp_monitoring.*`
|
||||
- `pmg.pmg_services`
|
||||
- `pmg.pmg_cluster_only_services`
|
||||
- `pmg.cluster_join.*`
|
||||
- `pmg.update_dist_upgrade`
|
||||
- `pmg.update_auto_reboot`
|
||||
- `pmg.update_reboot_delay_minutes`
|
||||
- `pmg.custom_check.*`
|
||||
- `pmg.custom_check.log_dir_user|log_dir_group|log_dir_mode|log_file_mode`
|
||||
- `pmg.manage_hosts_file`
|
||||
- `pmg.hosts_file_entries`
|
||||
- `pmg.wazuh.*`
|
||||
- `pmg.kesl.*`
|
||||
- `pmg.klnagent.*`
|
||||
- `pmg.qualys.*`
|
||||
|
||||
`pmg.node_exporter.args` по умолчанию включает textfile collector:
|
||||
`--collector.textfile.directory=/var/lib/prometheus/node-exporter`.
|
||||
|
||||
`pmg.smtp_monitoring` публикует расширенные SMTP метрики в node_exporter:
|
||||
- queue total/deferred + age oldest message;
|
||||
- SMTP listen/established для портов 25/26;
|
||||
- recent window counters (`pmg.smtp_monitoring.log_window_seconds`) по логам postfix:
|
||||
sent, received, deferred, bounced, rejected, tls failures, smtp connections.
|
||||
|
||||
`pmg.cluster_join` делает:
|
||||
- precheck passwordless SSH с новой ноды на `pmg.cluster_join.master_ip`;
|
||||
- non-interactive `pmgcm join` при заданном `pmg.cluster_join.password`.
|
||||
|
||||
Важно: `pmgcm join` использует пароль PMG (`root@pam` на master) для получения ticket
|
||||
(это не SSH-пароль). Без `pmg.cluster_join.password` state делает skip-уведомление.
|
||||
|
||||
Рекомендуется передавать пароль через переменную окружения (без сохранения в `pillar/pmg.sls`):
|
||||
|
||||
```bash
|
||||
export PMG_CLUSTER_JOIN_PASSWORD='***'
|
||||
PILLAR_B64="$(python3 -c 'import json, os, base64; print(base64.b64encode(json.dumps({\"pmg\": {\"cluster_join\": {\"password\": os.environ[\"PMG_CLUSTER_JOIN_PASSWORD\"]}}}).encode()).decode())')"
|
||||
ssh root@p-mailgw-x1-02.lmru.tech "cd /root/mailgw-salt && salt-call --local --id p-mailgw-x1-02.lmru.tech --file-root=. --pillar-root=./pillar state.apply pmg.cluster_join pillar=\"\$(python3 -c \"import base64; print(base64.b64decode(\\\"${PILLAR_B64}\\\").decode())\")\""
|
||||
```
|
||||
|
||||
Для полностью автоматического bootstrap SSH-доверия (без паролей) с управляющей машины:
|
||||
|
||||
```bash
|
||||
make cluster-bootstrap MINION_ID=p-mailgw-x1-02.lmru.tech MASTER_HOST=p-mailgw-x1-01.lmru.tech MASTER_IP=10.80.38.152
|
||||
```
|
||||
|
||||
## Отдельное обновление PMG
|
||||
|
||||
Перед обновлением:
|
||||
|
||||
```bash
|
||||
make update-check MINION_ID=p-mailgw-x1-01.lmru.tech
|
||||
```
|
||||
|
||||
Применение:
|
||||
|
||||
```bash
|
||||
make update MINION_ID=p-mailgw-x1-01.lmru.tech
|
||||
make verify MINION_ID=p-mailgw-x1-01.lmru.tech
|
||||
```
|
||||
|
||||
State `pmg.update` выполняет:
|
||||
- precheck кластера (`pmgcm status`);
|
||||
- precheck критичных PMG сервисов;
|
||||
- пакетное обновление (`pkg.uptodate`, с `dist_upgrade` по `pmg.update_dist_upgrade=true`);
|
||||
- postcheck кластера и сервисов;
|
||||
- уведомление, если требуется reboot (`/var/run/reboot-required`).
|
||||
- опциональный автоперезапуск по `pmg.update_auto_reboot=true` (через `shutdown -r +N`).
|
||||
|
||||
## PMG custom check (Kaspersky)
|
||||
|
||||
Коротко про `pmg.custom_check`:
|
||||
- готовит окружение (`pwgen`, `/var/log/mail_av`, `/usr/local/bin/pmg-custom-check`);
|
||||
- готовит Kaspersky-учетку (`pmg.custom_check.ksc_superuser.*`; сейчас `superuser` + `NOPASSWD`);
|
||||
- настраивает исключения KESL из сканирования через `pmg.custom_check.kesl_excluded_paths`
|
||||
(по умолчанию: `/var/spool/pmg`, `/var/lib/clamav`, `/tmp/.proxdump_*`) и делает
|
||||
`systemctl restart kesl` только при изменении этих настроек;
|
||||
- если `kesl/klnagent64` установлены — включает сервисы и `custom_check` в PMG, если нет — выводит уведомление "ставится через Kaspersky Administration Center"; сервисы включатся вторым прогоном после установки пакетов.
|
||||
|
||||
`pmg.security_agents` дополнительно ставит/настраивает `kesl` и `klnagent64`:
|
||||
- для `kesl`:
|
||||
- скачивает `.deb` из `pmg.kesl.package_url`;
|
||||
- формирует `autoinstall.ini` и запускает `/opt/kaspersky/kesl/bin/kesl-setup.pl --autoinstall=...`;
|
||||
- поднимает сервис `kesl`;
|
||||
- скачивает `.deb` из `pmg.klnagent.package_url`;
|
||||
- формирует файл ответов `KLAUTOANSWERS` (минимум: `KLNAGENT_SERVER`,
|
||||
`KLNAGENT_AUTOINSTALL`, `EULA_ACCEPTED`);
|
||||
- устанавливает/переустанавливает пакет в тихом режиме и поднимает сервис `klnagent64`.
|
||||
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
pmg:
|
||||
base_packages:
|
||||
- curl
|
||||
- vim-tiny
|
||||
- rsync
|
||||
- ca-certificates
|
||||
- jq
|
||||
|
||||
ssh_allow_users: []
|
||||
ssh_password_authentication: true
|
||||
|
||||
journald_system_max_use: 1G
|
||||
journald_max_file_sec: 1month
|
||||
|
||||
node_exporter:
|
||||
enabled: true
|
||||
package: prometheus-node-exporter
|
||||
service: prometheus-node-exporter
|
||||
textfile_directory: /var/lib/prometheus/node-exporter
|
||||
args: --collector.textfile.directory=/var/lib/prometheus/node-exporter
|
||||
|
||||
smtp_monitoring:
|
||||
enabled: true
|
||||
log_window_seconds: 300
|
||||
ports:
|
||||
- 25
|
||||
- 26
|
||||
blackbox:
|
||||
package: prometheus-blackbox-exporter
|
||||
service: prometheus-blackbox-exporter
|
||||
|
||||
pmg_services:
|
||||
- postfix
|
||||
- pmg-smtp-filter
|
||||
- pmgpolicy
|
||||
- pmgdaemon
|
||||
- pmgmirror
|
||||
- pmgtunnel
|
||||
pmg_cluster_only_services:
|
||||
- pmgmirror
|
||||
- pmgtunnel
|
||||
cluster_join:
|
||||
enabled: true
|
||||
master_ip: 10.80.38.152
|
||||
fingerprint: "4C:63:ED:BB:01:F0:63:5B:92:AC:37:D8:52:D9:0A:CB:5B:78:EE:3A:36:83:86:29:C8:19:D2:EC:CE:AC:88:EC"
|
||||
password: ""
|
||||
update_dist_upgrade: true
|
||||
update_auto_reboot: false
|
||||
update_reboot_delay_minutes: 1
|
||||
|
||||
custom_check:
|
||||
enabled: true
|
||||
script_path: /usr/local/bin/pmg-custom-check
|
||||
log_dir: /var/log/mail_av
|
||||
log_dir_user: pmg-smtp-filter
|
||||
log_dir_group: pmg
|
||||
log_dir_mode: "0770"
|
||||
log_file_mode: "0660"
|
||||
managed_packages:
|
||||
- pwgen
|
||||
ksc_superuser:
|
||||
enabled: true
|
||||
name: superuser
|
||||
manage_sudoers: true
|
||||
required_services:
|
||||
- kesl
|
||||
- klnagent64
|
||||
kesl_excluded_paths:
|
||||
- /var/spool/pmg
|
||||
- /var/lib/clamav
|
||||
- /tmp/.proxdump_*
|
||||
|
||||
manage_hosts_file: false
|
||||
hosts_file_entries:
|
||||
- ip: 10.80.38.152
|
||||
hostname: p-mailgw-x1-01
|
||||
aliases:
|
||||
- p-mailgw-x1-01.lmru.tech
|
||||
- ip: 10.80.38.153
|
||||
hostname: p-mailgw-x1-02
|
||||
aliases:
|
||||
- p-mailgw-x1-02.lmru.tech
|
||||
- ip: 10.220.41.152
|
||||
hostname: p-mailgw-x2-01
|
||||
aliases:
|
||||
- p-mailgw-x2-01.lmru.tech
|
||||
- ip: 10.220.41.153
|
||||
hostname: p-mailgw-x2-02
|
||||
aliases:
|
||||
- p-mailgw-x2-02.lmru.tech
|
||||
|
||||
wazuh:
|
||||
enabled: true
|
||||
version: 4.14.0-1
|
||||
manager: 10.80.120.205
|
||||
group: test_auditd_unix
|
||||
|
||||
kesl:
|
||||
enabled: true
|
||||
package_url: https://products.s.kaspersky-labs.com/endpoints/keslinux10/12.4.0.1225/multilanguage-INT-12.4.0.1225/167a2f9c5bdb4e869f4603fbc3a829c1/kesl_12.4.0-1225_amd64.deb
|
||||
package_path: /opt/kesl_12.4.0-1225_amd64.deb
|
||||
autoinstall_path: /etc/opt/kaspersky/kesl/autoinstall.ini
|
||||
ksvla_mode: "No"
|
||||
endpoint_agent_mode: "No"
|
||||
eula_agreed: "Yes"
|
||||
privacy_policy_agreed: "Yes"
|
||||
use_ksn: "No"
|
||||
group_clean: "No"
|
||||
locale: ""
|
||||
install_license: ""
|
||||
updater_source: SCServer
|
||||
update_execute: "No"
|
||||
|
||||
klnagent:
|
||||
enabled: true
|
||||
package_url: https://products.s.kaspersky-labs.com/administrationkit/ksc10/16.2.0.1023/russian-INT-29664057-ru/2129c97dba254c9fb8a4161f29a2241c/klnagent64_16.2.0-1023_amd64.deb
|
||||
package_path: /opt/klnagent64_16.2.0-1023_amd64.deb
|
||||
administration_server: 10.80.38.76
|
||||
autoanswers_path: /etc/opt/kaspersky/klnagent64/autoanswers.conf
|
||||
autoinstall: true
|
||||
eula_accepted: true
|
||||
|
||||
qualys:
|
||||
enabled: true
|
||||
package_url: https://storage.yandexcloud.net/qualys-agent/QualysCloudAgent.deb
|
||||
package_path: /opt/QualysCloudAgent.deb
|
||||
activation_id: 5eb9ad8e-02cc-431a-9afb-b8caad175ec1
|
||||
customer_id: 4d868109-dad3-5536-82b4-9f3a6f3010bc
|
||||
server_uri: https://qagpublic.qg1.apps.qualys.eu/CloudAgent/
|
||||
@@ -0,0 +1,3 @@
|
||||
base:
|
||||
'*':
|
||||
- pmg
|
||||
@@ -0,0 +1,56 @@
|
||||
{% set journald_system_max_use = salt['pillar.get']('pmg:journald_system_max_use', '1G') %}
|
||||
{% set journald_max_file_sec = salt['pillar.get']('pmg:journald_max_file_sec', '1month') %}
|
||||
|
||||
pmg-journald-dropin-dir:
|
||||
file.directory:
|
||||
- name: /etc/systemd/journald.conf.d
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0755'
|
||||
|
||||
pmg-journald-retention:
|
||||
file.managed:
|
||||
- name: /etc/systemd/journald.conf.d/99-retention.conf
|
||||
- source: salt://pmg/files/journald-retention.conf.jinja
|
||||
- template: jinja
|
||||
- context:
|
||||
journald_system_max_use: {{ journald_system_max_use }}
|
||||
journald_max_file_sec: {{ journald_max_file_sec }}
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0644'
|
||||
|
||||
pmg-restart-journald:
|
||||
cmd.run:
|
||||
- name: systemctl restart systemd-journald
|
||||
- onchanges:
|
||||
- file: pmg-journald-retention
|
||||
|
||||
pmg-auditd-package:
|
||||
pkg.installed:
|
||||
- name: auditd
|
||||
|
||||
pmg-audit-rules:
|
||||
file.managed:
|
||||
- name: /etc/audit/rules.d/99-pmg-baseline.rules
|
||||
- source: salt://pmg/files/99-pmg-baseline.rules.jinja
|
||||
- template: jinja
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0640'
|
||||
|
||||
pmg-reload-audit-rules:
|
||||
cmd.run:
|
||||
- name: augenrules --load
|
||||
- onchanges:
|
||||
- file: pmg-audit-rules
|
||||
|
||||
pmg-auditd-service:
|
||||
service.running:
|
||||
- name: auditd
|
||||
- enable: true
|
||||
|
||||
pmg-rsyslog-service:
|
||||
service.running:
|
||||
- name: rsyslog
|
||||
- enable: true
|
||||
@@ -0,0 +1,28 @@
|
||||
pmg-updates-packages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- unattended-upgrades
|
||||
- apt-listchanges
|
||||
|
||||
pmg-apt-auto-upgrades:
|
||||
file.managed:
|
||||
- name: /etc/apt/apt.conf.d/20auto-upgrades
|
||||
- source: salt://pmg/files/20auto-upgrades.jinja
|
||||
- template: jinja
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0644'
|
||||
|
||||
pmg-unattended-policy:
|
||||
file.managed:
|
||||
- name: /etc/apt/apt.conf.d/50unattended-upgrades
|
||||
- source: salt://pmg/files/50unattended-upgrades.jinja
|
||||
- template: jinja
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0644'
|
||||
|
||||
pmg-unattended-service:
|
||||
service.running:
|
||||
- name: unattended-upgrades
|
||||
- enable: true
|
||||
@@ -0,0 +1,30 @@
|
||||
{% set cluster_join_enabled = salt['pillar.get']('pmg:cluster_join:enabled', False) %}
|
||||
{% set cluster_join_master_ip = salt['pillar.get']('pmg:cluster_join:master_ip', '') %}
|
||||
{% set cluster_join_fingerprint = salt['pillar.get']('pmg:cluster_join:fingerprint', '') %}
|
||||
{% set cluster_join_password = salt['pillar.get']('pmg:cluster_join:password', '') %}
|
||||
|
||||
{% if cluster_join_enabled and cluster_join_master_ip and cluster_join_fingerprint %}
|
||||
pmg-cluster-join-passwordless-ssh-check:
|
||||
cmd.run:
|
||||
- name: ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new root@{{ cluster_join_master_ip }} "exit 0"
|
||||
- timeout: 20
|
||||
|
||||
{% if cluster_join_password %}
|
||||
pmg-cluster-join:
|
||||
cmd.run:
|
||||
- name: pmgcm join {{ cluster_join_master_ip }} --fingerprint {{ cluster_join_fingerprint }}
|
||||
- stdin: "{{ cluster_join_password }}"
|
||||
- timeout: 60
|
||||
- require:
|
||||
- cmd: pmg-cluster-join-passwordless-ssh-check
|
||||
- unless: "pmgcm status 2>/dev/null | awk 'BEGIN { ok=1 } /no cluster defined/ { ok=0 } END { exit(ok ? 0 : 1) }'"
|
||||
{% else %}
|
||||
pmg-cluster-join-skipped-password:
|
||||
test.show_notification:
|
||||
- text: "Cluster join skipped: set pmg.cluster_join.password (PMG root@pam password on master) for non-interactive join."
|
||||
{% endif %}
|
||||
{% elif cluster_join_enabled %}
|
||||
pmg-cluster-join-skipped:
|
||||
test.show_notification:
|
||||
- text: "Cluster join skipped: set pmg.cluster_join.master_ip and pmg.cluster_join.fingerprint in pillar."
|
||||
{% endif %}
|
||||
@@ -0,0 +1,5 @@
|
||||
{% set base_packages = salt['pillar.get']('pmg:base_packages', []) %}
|
||||
|
||||
pmg-base-packages:
|
||||
pkg.installed:
|
||||
- pkgs: {{ base_packages }}
|
||||
@@ -0,0 +1,151 @@
|
||||
{% set custom_check_enabled = salt['pillar.get']('pmg:custom_check:enabled', True) %}
|
||||
{% set custom_check_script_path = salt['pillar.get']('pmg:custom_check:script_path', '/usr/local/bin/pmg-custom-check') %}
|
||||
{% set custom_check_log_dir = salt['pillar.get']('pmg:custom_check:log_dir', '/var/log/mail_av') %}
|
||||
{% set custom_check_log_dir_user = salt['pillar.get']('pmg:custom_check:log_dir_user', 'pmg-smtp-filter') %}
|
||||
{% set custom_check_log_dir_group = salt['pillar.get']('pmg:custom_check:log_dir_group', 'pmg') %}
|
||||
{% set custom_check_log_dir_mode = salt['pillar.get']('pmg:custom_check:log_dir_mode', '0770') %}
|
||||
{% set custom_check_log_file_mode = salt['pillar.get']('pmg:custom_check:log_file_mode', '0660') %}
|
||||
{% set custom_check_managed_packages = salt['pillar.get']('pmg:custom_check:managed_packages', ['pwgen']) %}
|
||||
{% set custom_check_required_services = salt['pillar.get']('pmg:custom_check:required_services', ['kesl', 'klnagent64']) %}
|
||||
{% set custom_check_kesl_excluded_paths = salt['pillar.get']('pmg:custom_check:kesl_excluded_paths', ['/var/spool/pmg', '/var/lib/clamav', '/tmp/.proxdump_*']) %}
|
||||
{% set ksc_superuser_enabled = salt['pillar.get']('pmg:custom_check:ksc_superuser:enabled', False) %}
|
||||
{% set ksc_superuser_name = salt['pillar.get']('pmg:custom_check:ksc_superuser:name', '') %}
|
||||
{% set ksc_superuser_manage_sudoers = salt['pillar.get']('pmg:custom_check:ksc_superuser:manage_sudoers', True) %}
|
||||
{% set kesl_installed = salt['pkg.version']('kesl') != '' %}
|
||||
{% set klnagent64_installed = salt['pkg.version']('klnagent64') != '' %}
|
||||
{% set custom_check_kaspersky_ready = kesl_installed and klnagent64_installed %}
|
||||
|
||||
{% if custom_check_enabled %}
|
||||
{% if ksc_superuser_enabled and ksc_superuser_name %}
|
||||
pmg-custom-check-ksc-superuser:
|
||||
user.present:
|
||||
- name: {{ ksc_superuser_name }}
|
||||
- shell: /bin/bash
|
||||
- createhome: true
|
||||
|
||||
{% if ksc_superuser_manage_sudoers %}
|
||||
pmg-custom-check-ksc-superuser-sudo-package:
|
||||
pkg.installed:
|
||||
- name: sudo
|
||||
|
||||
pmg-custom-check-ksc-superuser-sudoers:
|
||||
file.managed:
|
||||
- name: /etc/sudoers.d/90-kaspersky-superuser
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0440'
|
||||
- contents:
|
||||
- '{{ ksc_superuser_name }} ALL = (ALL) NOPASSWD: ALL'
|
||||
- check_cmd: visudo -cf
|
||||
- require:
|
||||
- pkg: pmg-custom-check-ksc-superuser-sudo-package
|
||||
- user: pmg-custom-check-ksc-superuser
|
||||
{% endif %}
|
||||
{% elif ksc_superuser_enabled %}
|
||||
pmg-custom-check-ksc-superuser-missing-name:
|
||||
test.fail_without_changes:
|
||||
- name: "pmg.custom_check.ksc_superuser.enabled=true but pmg.custom_check.ksc_superuser.name is empty."
|
||||
{% endif %}
|
||||
|
||||
pmg-custom-check-managed-packages:
|
||||
pkg.installed:
|
||||
- pkgs: {{ custom_check_managed_packages }}
|
||||
|
||||
pmg-custom-check-log-dir:
|
||||
file.directory:
|
||||
- name: {{ custom_check_log_dir }}
|
||||
- user: {{ custom_check_log_dir_user }}
|
||||
- group: {{ custom_check_log_dir_group }}
|
||||
- mode: '{{ custom_check_log_dir_mode }}'
|
||||
- recurse:
|
||||
- user
|
||||
- group
|
||||
|
||||
pmg-custom-check-log-file:
|
||||
file.managed:
|
||||
- name: {{ custom_check_log_dir }}/checked
|
||||
- user: {{ custom_check_log_dir_user }}
|
||||
- group: {{ custom_check_log_dir_group }}
|
||||
- mode: '{{ custom_check_log_file_mode }}'
|
||||
- replace: false
|
||||
- require:
|
||||
- file: pmg-custom-check-log-dir
|
||||
|
||||
pmg-custom-check-script:
|
||||
file.managed:
|
||||
- name: {{ custom_check_script_path }}
|
||||
- source: salt://pmg/files/pmg-custom-check.jinja
|
||||
- template: jinja
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0755'
|
||||
- context:
|
||||
custom_check_log_dir: {{ custom_check_log_dir }}
|
||||
- require:
|
||||
- pkg: pmg-custom-check-managed-packages
|
||||
- file: pmg-custom-check-log-dir
|
||||
|
||||
{% if custom_check_kaspersky_ready %}
|
||||
pmg-custom-check-kaspersky-ready:
|
||||
test.nop:
|
||||
- name: "KESL and klnagent64 are installed."
|
||||
{% else %}
|
||||
pmg-custom-check-kaspersky-missing-note:
|
||||
test.show_notification:
|
||||
- text: "KESL/klnagent64 не найдены. Установите через Kaspersky Administration Center (сервисный superuser/NOPASSWD), затем повторите state.apply pmg.custom_check."
|
||||
{% endif %}
|
||||
|
||||
{% if custom_check_kaspersky_ready %}
|
||||
{% for service_name in custom_check_required_services %}
|
||||
pmg-custom-check-service-{{ service_name }}:
|
||||
service.running:
|
||||
- name: {{ service_name }}
|
||||
- enable: true
|
||||
- require:
|
||||
- test: pmg-custom-check-kaspersky-ready
|
||||
{% endfor %}
|
||||
|
||||
{% for excluded_path in custom_check_kesl_excluded_paths %}
|
||||
pmg-custom-check-kesl-excluded-scope-{{ loop.index0 }}:
|
||||
cmd.run:
|
||||
- name: >-
|
||||
kesl-control --set-settings File_Threat_Protection
|
||||
ExcludedFromScanScope.item_{{ "%04d" | format(loop.index0) }}.Path='{{ excluded_path }}'
|
||||
ExcludedFromScanScope.item_{{ "%04d" | format(loop.index0) }}.UseScanArea=Yes
|
||||
ExcludedFromScanScope.item_{{ "%04d" | format(loop.index0) }}.AreaMask.item_0000='*'
|
||||
- unless: >-
|
||||
kesl-control --get-settings File_Threat_Protection 2>/dev/null
|
||||
| grep -Fq "Path={{ excluded_path }}"
|
||||
- require:
|
||||
- test: pmg-custom-check-kaspersky-ready
|
||||
{% for service_name in custom_check_required_services %}
|
||||
- service: pmg-custom-check-service-{{ service_name }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
pmg-custom-check-kesl-restart:
|
||||
cmd.wait:
|
||||
- name: systemctl restart kesl
|
||||
- watch:
|
||||
{% for excluded_path in custom_check_kesl_excluded_paths %}
|
||||
- cmd: pmg-custom-check-kesl-excluded-scope-{{ loop.index0 }}
|
||||
{% endfor %}
|
||||
- require:
|
||||
- test: pmg-custom-check-kaspersky-ready
|
||||
|
||||
pmg-custom-check-enable-in-pmg:
|
||||
cmd.run:
|
||||
- name: pmgsh set /config/admin --custom_check 1
|
||||
- unless: "pmgsh get /config/admin | grep -q '\"custom_check\" : 1'"
|
||||
- require:
|
||||
- file: pmg-custom-check-script
|
||||
- pkg: pmg-custom-check-managed-packages
|
||||
{% for service_name in custom_check_required_services %}
|
||||
- service: pmg-custom-check-service-{{ service_name }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
pmg-custom-check-disabled-note:
|
||||
test.show_notification:
|
||||
- text: "PMG custom_check disabled by pillar (pmg.custom_check.enabled=false)."
|
||||
{% endif %}
|
||||
@@ -0,0 +1,4 @@
|
||||
APT::Periodic::Update-Package-Lists "1";
|
||||
APT::Periodic::Download-Upgradeable-Packages "1";
|
||||
APT::Periodic::Unattended-Upgrade "1";
|
||||
APT::Periodic::AutocleanInterval "7";
|
||||
@@ -0,0 +1,14 @@
|
||||
Unattended-Upgrade::Origins-Pattern {
|
||||
"origin=Debian,codename=${distro_codename},label=Debian-Security";
|
||||
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
|
||||
};
|
||||
|
||||
Unattended-Upgrade::Package-Blacklist {
|
||||
};
|
||||
|
||||
Unattended-Upgrade::MinimalSteps "true";
|
||||
Unattended-Upgrade::InstallOnShutdown "false";
|
||||
Unattended-Upgrade::MailOnlyOnError "true";
|
||||
Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
Unattended-Upgrade::Automatic-Reboot "false";
|
||||
Unattended-Upgrade::SyslogEnable "true";
|
||||
@@ -0,0 +1,10 @@
|
||||
# Managed by Salt.
|
||||
PasswordAuthentication {{ "yes" if ssh_password_authentication else "no" }}
|
||||
PermitRootLogin prohibit-password
|
||||
PubkeyAuthentication yes
|
||||
X11Forwarding no
|
||||
MaxAuthTries 3
|
||||
LoginGraceTime 30
|
||||
{% if ssh_allow_users %}
|
||||
AllowUsers {{ " ".join(ssh_allow_users) }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,14 @@
|
||||
## Managed by Salt.
|
||||
-D
|
||||
-b 8192
|
||||
|
||||
-w /etc/passwd -p wa -k identity
|
||||
-w /etc/group -p wa -k identity
|
||||
-w /etc/shadow -p wa -k identity
|
||||
-w /etc/gshadow -p wa -k identity
|
||||
|
||||
-w /etc/sudoers -p wa -k privilege-escalation
|
||||
-w /etc/sudoers.d/ -p wa -k privilege-escalation
|
||||
|
||||
-w /etc/ssh/sshd_config -p wa -k ssh
|
||||
-w /etc/ssh/sshd_config.d/ -p wa -k ssh
|
||||
@@ -0,0 +1,12 @@
|
||||
modules:
|
||||
{% for port in smtp_ports %}
|
||||
smtp_{{ port }}:
|
||||
prober: tcp
|
||||
timeout: 5s
|
||||
tcp:
|
||||
preferred_ip_protocol: ip4
|
||||
query_response:
|
||||
- expect: "^220[ -]"
|
||||
- send: "EHLO blackbox.local"
|
||||
- expect: "^250[ -]"
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,3 @@
|
||||
[Journal]
|
||||
SystemMaxUse={{ journald_system_max_use }}
|
||||
MaxFileSec={{ journald_max_file_sec }}
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
# Managed by Salt: PMG custom check via Kaspersky KESL.
|
||||
|
||||
set -u
|
||||
|
||||
LOG_DIR="{{ custom_check_log_dir }}"
|
||||
KESL_BIN="/opt/kaspersky/kesl/bin/kesl-control"
|
||||
SCAN_TARGET="${2:-}"
|
||||
|
||||
if [ -z "${SCAN_TARGET}" ]; then
|
||||
echo "v1"
|
||||
echo "ERROR: empty scan target"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "${LOG_DIR}"
|
||||
|
||||
POLICY_NAME="$(pwgen 10 -1 -s -0)"
|
||||
POLICY_FILE="${LOG_DIR}/${POLICY_NAME}.pol"
|
||||
|
||||
cat > "${POLICY_FILE}" <<EOF
|
||||
ActionOnThreat=Inform
|
||||
ReportCleanObjects=No
|
||||
ScanFiles=Yes
|
||||
ScanMailBases=Yes
|
||||
ScanPlainMail=Yes
|
||||
UseAnalyzer=Yes
|
||||
HeuristicLevel=Medium
|
||||
[ScanScope.item_0000]
|
||||
Path=${SCAN_TARGET}
|
||||
EOF
|
||||
|
||||
"${KESL_BIN}" --create-task "${POLICY_NAME}" --type ODS --file "${POLICY_FILE}" >/dev/null
|
||||
VIRUS_NAME="$("${KESL_BIN}" --start-task "${POLICY_NAME}" -W | grep 'DetectName' | awk -F'=' '{print $2}' | head -1)"
|
||||
"${KESL_BIN}" --delete-task "${POLICY_NAME}"
|
||||
|
||||
if [ -z "${VIRUS_NAME}" ]; then
|
||||
rm -f "${POLICY_FILE}"
|
||||
echo "Mail ${SCAN_TARGET} was checked successfully" >> "${LOG_DIR}/checked"
|
||||
echo "v1"
|
||||
echo "OK"
|
||||
else
|
||||
echo "In mail ${SCAN_TARGET} find ${VIRUS_NAME}" >> "${POLICY_FILE}"
|
||||
echo "Mail ${SCAN_TARGET} was blocked. More info in ${POLICY_FILE}" >> "${LOG_DIR}/checked"
|
||||
echo "v1"
|
||||
echo "VIRUS: ${VIRUS_NAME}"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=Generate PMG SMTP metrics for node_exporter textfile collector
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/bin/pmg-smtp-metrics.sh
|
||||
@@ -0,0 +1,219 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
outfile="/var/lib/prometheus/node-exporter/pmg_smtp.prom"
|
||||
tmpfile="$(mktemp)"
|
||||
statefile="/var/lib/prometheus/node-exporter/pmg_smtp.state"
|
||||
statetmp="$(mktemp)"
|
||||
now_epoch="$(date +%s)"
|
||||
window_seconds={{ smtp_log_window_seconds | int }}
|
||||
|
||||
if [ "${window_seconds}" -lt 60 ]; then
|
||||
window_seconds=60
|
||||
fi
|
||||
|
||||
to_int() {
|
||||
local value="${1:-0}"
|
||||
if [[ "${value}" =~ ^[0-9]+$ ]]; then
|
||||
echo "${value}"
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
get_established_count() {
|
||||
local port="$1"
|
||||
ss -Htn state established "( sport = :${port} )" 2>/dev/null | awk 'END { print NR + 0 }'
|
||||
}
|
||||
|
||||
get_listen_up() {
|
||||
local port="$1"
|
||||
if ss -Hln "( sport = :${port} )" 2>/dev/null | awk 'END { exit(NR > 0 ? 0 : 1) }'; then
|
||||
echo "1"
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
service_up() {
|
||||
local service="$1"
|
||||
if systemctl is-active --quiet "${service}" 2>/dev/null; then
|
||||
echo "1"
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
last_cursor=""
|
||||
total_smtp_connections=0
|
||||
total_smtp_received=0
|
||||
total_smtp_sent=0
|
||||
total_smtp_deferred=0
|
||||
total_smtp_bounced=0
|
||||
total_smtp_rejected=0
|
||||
total_smtp_tls_failures=0
|
||||
prev_queue_total=0
|
||||
prev_queue_ts=0
|
||||
|
||||
if [ -f "${statefile}" ]; then
|
||||
while IFS='=' read -r key value; do
|
||||
case "${key}" in
|
||||
last_cursor) last_cursor="${value}" ;;
|
||||
total_smtp_connections) total_smtp_connections="$(to_int "${value}")" ;;
|
||||
total_smtp_received) total_smtp_received="$(to_int "${value}")" ;;
|
||||
total_smtp_sent) total_smtp_sent="$(to_int "${value}")" ;;
|
||||
total_smtp_deferred) total_smtp_deferred="$(to_int "${value}")" ;;
|
||||
total_smtp_bounced) total_smtp_bounced="$(to_int "${value}")" ;;
|
||||
total_smtp_rejected) total_smtp_rejected="$(to_int "${value}")" ;;
|
||||
total_smtp_tls_failures) total_smtp_tls_failures="$(to_int "${value}")" ;;
|
||||
prev_queue_total) prev_queue_total="$(to_int "${value}")" ;;
|
||||
prev_queue_ts) prev_queue_ts="$(to_int "${value}")" ;;
|
||||
esac
|
||||
done < "${statefile}"
|
||||
fi
|
||||
|
||||
queue_total="$(postqueue -j 2>/dev/null | awk 'END { print NR + 0 }')"
|
||||
queue_deferred="$(postqueue -j 2>/dev/null | awk -F'"' '/"queue_name":/ { if ($4 == "deferred") c++ } END { print c + 0 }')"
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
oldest_queue_epoch="$(postqueue -j 2>/dev/null | jq -sr 'map(.arrival_time // empty) | if length == 0 then 0 else min end' 2>/dev/null || echo "0")"
|
||||
else
|
||||
oldest_queue_epoch="0"
|
||||
fi
|
||||
|
||||
if [ "${oldest_queue_epoch}" -gt 0 ] 2>/dev/null; then
|
||||
queue_oldest_age_seconds="$(( now_epoch - oldest_queue_epoch ))"
|
||||
if [ "${queue_oldest_age_seconds}" -lt 0 ]; then
|
||||
queue_oldest_age_seconds=0
|
||||
fi
|
||||
else
|
||||
queue_oldest_age_seconds=0
|
||||
fi
|
||||
|
||||
if [ "${prev_queue_ts}" -gt 0 ] && [ "${now_epoch}" -gt "${prev_queue_ts}" ]; then
|
||||
queue_delta_seconds="$(( now_epoch - prev_queue_ts ))"
|
||||
queue_drain_per_minute="$(awk -v prev="${prev_queue_total}" -v curr="${queue_total}" -v dt="${queue_delta_seconds}" 'BEGIN { d = prev - curr; if (dt <= 0 || d <= 0) print 0; else printf "%.3f", (d * 60.0) / dt }')"
|
||||
else
|
||||
queue_drain_per_minute="0"
|
||||
fi
|
||||
|
||||
log_slice="$(journalctl --no-pager -o cat --since "-${window_seconds} sec" 2>/dev/null || true)"
|
||||
smtp_connections_recent="$(printf '%s\n' "${log_slice}" | grep -Ec 'postfix/smtpd.*connect from' || true)"
|
||||
smtp_received_recent="$(printf '%s\n' "${log_slice}" | grep -Ec 'postfix/qmgr.*: .*: from=<' || true)"
|
||||
smtp_sent_recent="$(printf '%s\n' "${log_slice}" | grep -Ec 'status=sent' || true)"
|
||||
smtp_deferred_recent="$(printf '%s\n' "${log_slice}" | grep -Ec 'status=deferred' || true)"
|
||||
smtp_bounced_recent="$(printf '%s\n' "${log_slice}" | grep -Ec 'status=bounced' || true)"
|
||||
smtp_rejected_recent="$(printf '%s\n' "${log_slice}" | grep -Ec 'NOQUEUE: reject:| reject:' || true)"
|
||||
smtp_tls_fail_recent="$(printf '%s\n' "${log_slice}" | grep -Eci '(tls|starttls).*(fail|error|warning|lost)' || true)"
|
||||
|
||||
journal_cursor_args=()
|
||||
if [ -n "${last_cursor}" ]; then
|
||||
journal_cursor_args+=(--after-cursor "${last_cursor}")
|
||||
fi
|
||||
journal_delta="$(journalctl --no-pager -o cat "${journal_cursor_args[@]}" 2>/dev/null || true)"
|
||||
delta_smtp_connections="$(printf '%s\n' "${journal_delta}" | grep -Ec 'postfix/smtpd.*connect from' || true)"
|
||||
delta_smtp_received="$(printf '%s\n' "${journal_delta}" | grep -Ec 'postfix/qmgr.*: .*: from=<' || true)"
|
||||
delta_smtp_sent="$(printf '%s\n' "${journal_delta}" | grep -Ec 'status=sent' || true)"
|
||||
delta_smtp_deferred="$(printf '%s\n' "${journal_delta}" | grep -Ec 'status=deferred' || true)"
|
||||
delta_smtp_bounced="$(printf '%s\n' "${journal_delta}" | grep -Ec 'status=bounced' || true)"
|
||||
delta_smtp_rejected="$(printf '%s\n' "${journal_delta}" | grep -Ec 'NOQUEUE: reject:| reject:' || true)"
|
||||
delta_smtp_tls_failures="$(printf '%s\n' "${journal_delta}" | grep -Eci '(tls|starttls).*(fail|error|warning|lost)' || true)"
|
||||
|
||||
total_smtp_connections="$(( total_smtp_connections + delta_smtp_connections ))"
|
||||
total_smtp_received="$(( total_smtp_received + delta_smtp_received ))"
|
||||
total_smtp_sent="$(( total_smtp_sent + delta_smtp_sent ))"
|
||||
total_smtp_deferred="$(( total_smtp_deferred + delta_smtp_deferred ))"
|
||||
total_smtp_bounced="$(( total_smtp_bounced + delta_smtp_bounced ))"
|
||||
total_smtp_rejected="$(( total_smtp_rejected + delta_smtp_rejected ))"
|
||||
total_smtp_tls_failures="$(( total_smtp_tls_failures + delta_smtp_tls_failures ))"
|
||||
|
||||
current_cursor="$(journalctl --no-pager -n 1 -o cat --show-cursor 2>/dev/null | awk -F': ' '/^-- cursor: / { c = $2 } END { print c }')"
|
||||
if [ -z "${current_cursor}" ]; then
|
||||
current_cursor="${last_cursor}"
|
||||
fi
|
||||
|
||||
{
|
||||
echo "# HELP pmg_postfix_queue_messages_total Number of messages in Postfix queue."
|
||||
echo "# TYPE pmg_postfix_queue_messages_total gauge"
|
||||
echo "pmg_postfix_queue_messages_total ${queue_total}"
|
||||
echo "# HELP pmg_postfix_queue_deferred_messages Number of deferred messages in Postfix queue."
|
||||
echo "# TYPE pmg_postfix_queue_deferred_messages gauge"
|
||||
echo "pmg_postfix_queue_deferred_messages ${queue_deferred}"
|
||||
echo "# HELP pmg_postfix_queue_oldest_age_seconds Age of oldest queued message in seconds."
|
||||
echo "# TYPE pmg_postfix_queue_oldest_age_seconds gauge"
|
||||
echo "pmg_postfix_queue_oldest_age_seconds ${queue_oldest_age_seconds}"
|
||||
echo "# HELP pmg_postfix_queue_drain_per_minute Queue drain rate in messages per minute (only positive drain)."
|
||||
echo "# TYPE pmg_postfix_queue_drain_per_minute gauge"
|
||||
echo "pmg_postfix_queue_drain_per_minute ${queue_drain_per_minute}"
|
||||
echo "# HELP pmg_smtp_metrics_window_seconds Time window in seconds used for recent SMTP counters."
|
||||
echo "# TYPE pmg_smtp_metrics_window_seconds gauge"
|
||||
echo "pmg_smtp_metrics_window_seconds ${window_seconds}"
|
||||
echo "# HELP pmg_smtp_connections_recent Number of SMTP connections observed in recent window."
|
||||
echo "# TYPE pmg_smtp_connections_recent gauge"
|
||||
echo "pmg_smtp_connections_recent ${smtp_connections_recent}"
|
||||
echo "# HELP pmg_smtp_received_recent Number of received messages observed in recent window."
|
||||
echo "# TYPE pmg_smtp_received_recent gauge"
|
||||
echo "pmg_smtp_received_recent ${smtp_received_recent}"
|
||||
echo "# HELP pmg_smtp_sent_recent Number of sent messages observed in recent window."
|
||||
echo "# TYPE pmg_smtp_sent_recent gauge"
|
||||
echo "pmg_smtp_sent_recent ${smtp_sent_recent}"
|
||||
echo "# HELP pmg_smtp_deferred_recent Number of deferred deliveries observed in recent window."
|
||||
echo "# TYPE pmg_smtp_deferred_recent gauge"
|
||||
echo "pmg_smtp_deferred_recent ${smtp_deferred_recent}"
|
||||
echo "# HELP pmg_smtp_bounced_recent Number of bounced deliveries observed in recent window."
|
||||
echo "# TYPE pmg_smtp_bounced_recent gauge"
|
||||
echo "pmg_smtp_bounced_recent ${smtp_bounced_recent}"
|
||||
echo "# HELP pmg_smtp_rejected_recent Number of rejected SMTP attempts observed in recent window."
|
||||
echo "# TYPE pmg_smtp_rejected_recent gauge"
|
||||
echo "pmg_smtp_rejected_recent ${smtp_rejected_recent}"
|
||||
echo "# HELP pmg_smtp_tls_failures_recent Number of TLS/STARTTLS failures observed in recent window."
|
||||
echo "# TYPE pmg_smtp_tls_failures_recent gauge"
|
||||
echo "pmg_smtp_tls_failures_recent ${smtp_tls_fail_recent}"
|
||||
echo "# HELP pmg_smtp_connections_total Total SMTP connections seen in logs since metrics script state start."
|
||||
echo "# TYPE pmg_smtp_connections_total counter"
|
||||
echo "pmg_smtp_connections_total ${total_smtp_connections}"
|
||||
echo "# HELP pmg_smtp_messages_total Total SMTP message events by direction/status since metrics script state start."
|
||||
echo "# TYPE pmg_smtp_messages_total counter"
|
||||
echo "pmg_smtp_messages_total{direction=\"received\",status=\"accepted\"} ${total_smtp_received}"
|
||||
echo "pmg_smtp_messages_total{direction=\"sent\",status=\"sent\"} ${total_smtp_sent}"
|
||||
echo "pmg_smtp_messages_total{direction=\"delivery\",status=\"deferred\"} ${total_smtp_deferred}"
|
||||
echo "pmg_smtp_messages_total{direction=\"delivery\",status=\"bounced\"} ${total_smtp_bounced}"
|
||||
echo "pmg_smtp_messages_total{direction=\"inbound\",status=\"rejected\"} ${total_smtp_rejected}"
|
||||
echo "# HELP pmg_smtp_tls_failures_total Total TLS/STARTTLS failures seen in logs since metrics script state start."
|
||||
echo "# TYPE pmg_smtp_tls_failures_total counter"
|
||||
echo "pmg_smtp_tls_failures_total ${total_smtp_tls_failures}"
|
||||
echo "# HELP pmg_smtp_port_listen_up SMTP listen status on port (1 up, 0 down)."
|
||||
echo "# TYPE pmg_smtp_port_listen_up gauge"
|
||||
echo "# HELP pmg_smtp_port_established_connections Established SMTP connections on port."
|
||||
echo "# TYPE pmg_smtp_port_established_connections gauge"
|
||||
echo "# HELP pmg_service_up PMG-related service status (1 up, 0 down)."
|
||||
echo "# TYPE pmg_service_up gauge"
|
||||
{% for port in smtp_ports %}
|
||||
echo "pmg_smtp_port_listen_up{port=\"{{ port }}\"} $(get_listen_up {{ port }})"
|
||||
echo "pmg_smtp_port_established_connections{port=\"{{ port }}\"} $(get_established_count {{ port }})"
|
||||
{% endfor %}
|
||||
echo "pmg_service_up{service=\"postfix\"} $(service_up postfix)"
|
||||
echo "pmg_service_up{service=\"pmg-smtp-filter\"} $(service_up pmg-smtp-filter)"
|
||||
echo "pmg_service_up{service=\"pmgpolicy\"} $(service_up pmgpolicy)"
|
||||
echo "pmg_service_up{service=\"pmgdaemon\"} $(service_up pmgdaemon)"
|
||||
echo "pmg_service_up{service=\"pmgmirror\"} $(service_up pmgmirror)"
|
||||
echo "pmg_service_up{service=\"pmgtunnel\"} $(service_up pmgtunnel)"
|
||||
} > "${tmpfile}"
|
||||
|
||||
mv "${tmpfile}" "${outfile}"
|
||||
chmod 0644 "${outfile}"
|
||||
|
||||
{
|
||||
echo "last_cursor=${current_cursor}"
|
||||
echo "total_smtp_connections=${total_smtp_connections}"
|
||||
echo "total_smtp_received=${total_smtp_received}"
|
||||
echo "total_smtp_sent=${total_smtp_sent}"
|
||||
echo "total_smtp_deferred=${total_smtp_deferred}"
|
||||
echo "total_smtp_bounced=${total_smtp_bounced}"
|
||||
echo "total_smtp_rejected=${total_smtp_rejected}"
|
||||
echo "total_smtp_tls_failures=${total_smtp_tls_failures}"
|
||||
echo "prev_queue_total=${queue_total}"
|
||||
echo "prev_queue_ts=${now_epoch}"
|
||||
} > "${statetmp}"
|
||||
|
||||
mv "${statetmp}" "${statefile}"
|
||||
chmod 0644 "${statefile}"
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Run PMG SMTP metrics exporter script every minute
|
||||
|
||||
[Timer]
|
||||
OnBootSec=30s
|
||||
OnUnitActiveSec=1min
|
||||
AccuracySec=15s
|
||||
Unit=pmg-smtp-metrics.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -0,0 +1,11 @@
|
||||
include:
|
||||
- pmg.common
|
||||
- pmg.node_exporter
|
||||
- pmg.smtp_monitoring
|
||||
- pmg.ssh_hardening
|
||||
- pmg.custom_check
|
||||
- pmg.security_agents
|
||||
- pmg.cluster_join
|
||||
- pmg.auto_updates
|
||||
- pmg.audit_logging
|
||||
- pmg.pmg_baseline
|
||||
@@ -0,0 +1,39 @@
|
||||
{% set node_exporter_enabled = salt['pillar.get']('pmg:node_exporter:enabled', True) %}
|
||||
{% set node_exporter_package = salt['pillar.get']('pmg:node_exporter:package', 'prometheus-node-exporter') %}
|
||||
{% set node_exporter_service = salt['pillar.get']('pmg:node_exporter:service', 'prometheus-node-exporter') %}
|
||||
{% set node_exporter_textfile_dir = salt['pillar.get']('pmg:node_exporter:textfile_directory', '/var/lib/prometheus/node-exporter') %}
|
||||
{% set node_exporter_args = salt['pillar.get']('pmg:node_exporter:args', '--collector.textfile.directory=' ~ node_exporter_textfile_dir) %}
|
||||
|
||||
{% if node_exporter_enabled %}
|
||||
pmg-node-exporter-package:
|
||||
pkg.installed:
|
||||
- name: {{ node_exporter_package }}
|
||||
|
||||
pmg-node-exporter-textfile-dir:
|
||||
file.directory:
|
||||
- name: {{ node_exporter_textfile_dir }}
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: "0755"
|
||||
|
||||
pmg-node-exporter-defaults:
|
||||
file.managed:
|
||||
- name: /etc/default/prometheus-node-exporter
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: "0644"
|
||||
- contents:
|
||||
- "# Managed by Salt."
|
||||
- "ARGS=\"{{ node_exporter_args }}\""
|
||||
|
||||
pmg-node-exporter-service:
|
||||
service.running:
|
||||
- name: {{ node_exporter_service }}
|
||||
- enable: true
|
||||
- require:
|
||||
- pkg: pmg-node-exporter-package
|
||||
- file: pmg-node-exporter-textfile-dir
|
||||
- file: pmg-node-exporter-defaults
|
||||
- watch:
|
||||
- file: pmg-node-exporter-defaults
|
||||
{% endif %}
|
||||
@@ -0,0 +1,50 @@
|
||||
{% set pmg_services = salt['pillar.get']('pmg:pmg_services', []) %}
|
||||
{% set pmg_cluster_only_services = salt['pillar.get']('pmg:pmg_cluster_only_services', ['pmgmirror', 'pmgtunnel']) %}
|
||||
{% set manage_hosts_file = salt['pillar.get']('pmg:manage_hosts_file', False) %}
|
||||
{% set hosts_file_entries = salt['pillar.get']('pmg:hosts_file_entries', []) %}
|
||||
|
||||
{% for svc in pmg_services %}
|
||||
pmg-service-{{ svc }}:
|
||||
service.running:
|
||||
- name: {{ svc }}
|
||||
- enable: true
|
||||
{% if svc in pmg_cluster_only_services %}
|
||||
- onlyif: "pmgcm status 2>/dev/null | awk 'BEGIN { ok=1 } /no cluster defined/ { ok=0 } END { exit(ok ? 0 : 1) }'"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
pmg-cluster-status:
|
||||
cmd.run:
|
||||
- name: pmgcm status
|
||||
|
||||
pmg-postfix-identity-from-system-hostname:
|
||||
cmd.run:
|
||||
- name: >-
|
||||
HN="$(hostname -f)" &&
|
||||
DM="${HN#*.}" &&
|
||||
postconf -e "myhostname=${HN}" "mydomain=${DM}" &&
|
||||
systemctl reload postfix
|
||||
- unless: >-
|
||||
HN="$(hostname -f)" &&
|
||||
DM="${HN#*.}" &&
|
||||
[ "$(postconf -h myhostname)" = "${HN}" ] &&
|
||||
[ "$(postconf -h mydomain)" = "${DM}" ]
|
||||
- require:
|
||||
- service: pmg-service-postfix
|
||||
|
||||
{% if manage_hosts_file %}
|
||||
pmg-managed-hosts:
|
||||
file.blockreplace:
|
||||
- name: /etc/hosts
|
||||
- marker_start: "#-- SALT PMG HOSTS START --"
|
||||
- marker_end: "#-- SALT PMG HOSTS END --"
|
||||
- append_if_not_found: true
|
||||
- content: |
|
||||
{% for host in hosts_file_entries %}
|
||||
{% if host.get('aliases', []) %}
|
||||
{{ host['ip'] }} {{ host['hostname'] }} {{ ' '.join(host['aliases']) }}
|
||||
{% else %}
|
||||
{{ host['ip'] }} {{ host['hostname'] }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,208 @@
|
||||
{% set wazuh_enabled = salt['pillar.get']('pmg:wazuh:enabled', True) %}
|
||||
{% set wazuh_version = salt['pillar.get']('pmg:wazuh:version', '4.14.0-1') %}
|
||||
{% set wazuh_manager = salt['pillar.get']('pmg:wazuh:manager', '10.80.120.205') %}
|
||||
{% set wazuh_group = salt['pillar.get']('pmg:wazuh:group', 'test_auditd_unix') %}
|
||||
{% set wazuh_pkg_name = 'wazuh-agent_' ~ wazuh_version ~ '_amd64.deb' %}
|
||||
{% set wazuh_pkg_url = 'https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/' ~ wazuh_pkg_name %}
|
||||
{% set wazuh_pkg_path = '/var/tmp/' ~ wazuh_pkg_name %}
|
||||
|
||||
{% set kesl_enabled = salt['pillar.get']('pmg:kesl:enabled', True) %}
|
||||
{% set kesl_package_url = salt['pillar.get']('pmg:kesl:package_url', 'https://products.s.kaspersky-labs.com/endpoints/keslinux10/12.4.0.1225/multilanguage-INT-12.4.0.1225/167a2f9c5bdb4e869f4603fbc3a829c1/kesl_12.4.0-1225_amd64.deb') %}
|
||||
{% set kesl_package_path = salt['pillar.get']('pmg:kesl:package_path', '/opt/kesl_12.4.0-1225_amd64.deb') %}
|
||||
{% set kesl_autoinstall_path = salt['pillar.get']('pmg:kesl:autoinstall_path', '/etc/opt/kaspersky/kesl/autoinstall.ini') %}
|
||||
{% set kesl_eula_agreed = salt['pillar.get']('pmg:kesl:eula_agreed', 'Yes') %}
|
||||
{% set kesl_privacy_policy_agreed = salt['pillar.get']('pmg:kesl:privacy_policy_agreed', 'Yes') %}
|
||||
{% set kesl_use_ksn = salt['pillar.get']('pmg:kesl:use_ksn', 'No') %}
|
||||
{% set kesl_group_clean = salt['pillar.get']('pmg:kesl:group_clean', 'No') %}
|
||||
{% set kesl_locale = salt['pillar.get']('pmg:kesl:locale', '') %}
|
||||
{% set kesl_updater_source = salt['pillar.get']('pmg:kesl:updater_source', 'SCServer') %}
|
||||
{% set kesl_install_license = salt['pillar.get']('pmg:kesl:install_license', '') %}
|
||||
{% set kesl_update_execute = salt['pillar.get']('pmg:kesl:update_execute', 'No') %}
|
||||
{% set kesl_ksvla_mode = salt['pillar.get']('pmg:kesl:ksvla_mode', 'No') %}
|
||||
{% set kesl_endpoint_agent_mode = salt['pillar.get']('pmg:kesl:endpoint_agent_mode', 'No') %}
|
||||
{% set kesl_setup_bin = '/opt/kaspersky/kesl/bin/kesl-setup.pl' %}
|
||||
{% set kesl_setup_marker = '/var/lib/salt/pmg-kesl-autoinstall.done' %}
|
||||
|
||||
{% set klnagent_enabled = salt['pillar.get']('pmg:klnagent:enabled', True) %}
|
||||
{% set klnagent_package_url = salt['pillar.get']('pmg:klnagent:package_url', 'https://products.s.kaspersky-labs.com/administrationkit/ksc10/16.2.0.1023/russian-INT-29664057-ru/2129c97dba254c9fb8a4161f29a2241c/klnagent64_16.2.0-1023_amd64.deb') %}
|
||||
{% set klnagent_package_path = salt['pillar.get']('pmg:klnagent:package_path', '/opt/klnagent64_16.2.0-1023_amd64.deb') %}
|
||||
{% set klnagent_server = salt['pillar.get']('pmg:klnagent:administration_server', '10.80.38.76') %}
|
||||
{% set klnagent_autoanswers_path = salt['pillar.get']('pmg:klnagent:autoanswers_path', '/etc/opt/kaspersky/klnagent64/autoanswers.conf') %}
|
||||
{% set klnagent_autoinstall = salt['pillar.get']('pmg:klnagent:autoinstall', True) %}
|
||||
{% set klnagent_eula_accepted = salt['pillar.get']('pmg:klnagent:eula_accepted', True) %}
|
||||
{% set klnagent_config_marker = '/var/lib/salt/pmg-klnagent-autoinstall.done' %}
|
||||
|
||||
{% set qualys_enabled = salt['pillar.get']('pmg:qualys:enabled', True) %}
|
||||
{% set qualys_package_url = salt['pillar.get']('pmg:qualys:package_url', 'https://storage.yandexcloud.net/qualys-agent/QualysCloudAgent.deb') %}
|
||||
{% set qualys_package_path = salt['pillar.get']('pmg:qualys:package_path', '/opt/QualysCloudAgent.deb') %}
|
||||
{% set qualys_activation_id = salt['pillar.get']('pmg:qualys:activation_id', '') %}
|
||||
{% set qualys_customer_id = salt['pillar.get']('pmg:qualys:customer_id', '') %}
|
||||
{% set qualys_server_uri = salt['pillar.get']('pmg:qualys:server_uri', 'https://qagpublic.qg1.apps.qualys.eu/CloudAgent/') %}
|
||||
|
||||
{% if kesl_enabled and kesl_package_path %}
|
||||
pmg-kesl-download:
|
||||
cmd.run:
|
||||
- name: wget -qO {{ kesl_package_path }} {{ kesl_package_url }}
|
||||
- unless: test -f {{ kesl_package_path }}
|
||||
|
||||
pmg-kesl-autoinstall-config:
|
||||
file.managed:
|
||||
- name: {{ kesl_autoinstall_path }}
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0600'
|
||||
- makedirs: true
|
||||
- contents:
|
||||
- "KSVLA_MODE={{ kesl_ksvla_mode }}"
|
||||
- "ENDPOINT_AGENT_MODE={{ kesl_endpoint_agent_mode }}"
|
||||
- "EULA_AGREED={{ kesl_eula_agreed }}"
|
||||
- "PRIVACY_POLICY_AGREED={{ kesl_privacy_policy_agreed }}"
|
||||
- "USE_KSN={{ kesl_use_ksn }}"
|
||||
- "GROUP_CLEAN={{ kesl_group_clean }}"
|
||||
{% if kesl_locale %}
|
||||
- "LOCALE={{ kesl_locale }}"
|
||||
{% endif %}
|
||||
{% if kesl_install_license %}
|
||||
- "INSTALL_LICENSE={{ kesl_install_license }}"
|
||||
{% endif %}
|
||||
- "UPDATER_SOURCE={{ kesl_updater_source }}"
|
||||
- "UPDATE_EXECUTE={{ kesl_update_execute }}"
|
||||
- require:
|
||||
- cmd: pmg-kesl-install
|
||||
|
||||
pmg-kesl-install:
|
||||
cmd.run:
|
||||
- name: apt-get install -y {{ kesl_package_path }}
|
||||
- onlyif: test -f {{ kesl_package_path }}
|
||||
- unless: dpkg-query -W -f='${Status}' kesl 2>/dev/null | grep -q 'install ok installed'
|
||||
- require:
|
||||
- cmd: pmg-kesl-download
|
||||
|
||||
pmg-kesl-setup:
|
||||
cmd.run:
|
||||
- name: mkdir -p /var/lib/salt && {{ kesl_setup_bin }} --autoinstall={{ kesl_autoinstall_path }} && touch {{ kesl_setup_marker }}
|
||||
- onlyif: test -x {{ kesl_setup_bin }}
|
||||
- unless: test -f {{ kesl_setup_marker }}
|
||||
- require:
|
||||
- cmd: pmg-kesl-install
|
||||
- file: pmg-kesl-autoinstall-config
|
||||
|
||||
pmg-kesl-service:
|
||||
service.running:
|
||||
- name: kesl
|
||||
- enable: true
|
||||
- require:
|
||||
- cmd: pmg-kesl-install
|
||||
- cmd: pmg-kesl-setup
|
||||
{% elif kesl_enabled %}
|
||||
pmg-kesl-skipped:
|
||||
test.show_notification:
|
||||
- text: "Kaspersky Endpoint Security skipped: set package_url + package_path in pillar."
|
||||
{% endif %}
|
||||
|
||||
{% if klnagent_enabled and klnagent_package_path and klnagent_server %}
|
||||
pmg-klnagent-download:
|
||||
cmd.run:
|
||||
- name: wget -qO {{ klnagent_package_path }} {{ klnagent_package_url }}
|
||||
- unless: test -f {{ klnagent_package_path }}
|
||||
|
||||
pmg-klnagent-autoanswers:
|
||||
file.managed:
|
||||
- name: {{ klnagent_autoanswers_path }}
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0600'
|
||||
- makedirs: true
|
||||
- contents:
|
||||
- "KLNAGENT_SERVER={{ klnagent_server }}"
|
||||
- "KLNAGENT_AUTOINSTALL={{ '1' if klnagent_autoinstall else '0' }}"
|
||||
- "EULA_ACCEPTED={{ '1' if klnagent_eula_accepted else '0' }}"
|
||||
|
||||
pmg-klnagent-install:
|
||||
cmd.run:
|
||||
- name: DEBIAN_FRONTEND=noninteractive KLAUTOANSWERS={{ klnagent_autoanswers_path }} apt-get install -y {{ klnagent_package_path }}
|
||||
- onlyif: test -f {{ klnagent_package_path }}
|
||||
- unless: dpkg-query -W -f='${Status}' klnagent64 2>/dev/null | grep -q 'install ok installed'
|
||||
- require:
|
||||
- cmd: pmg-klnagent-download
|
||||
- file: pmg-klnagent-autoanswers
|
||||
|
||||
pmg-klnagent-autoinstall-configure:
|
||||
cmd.run:
|
||||
- name: >-
|
||||
mkdir -p /var/lib/salt &&
|
||||
DEBIAN_FRONTEND=noninteractive KLAUTOANSWERS={{ klnagent_autoanswers_path }} apt-get install -y --reinstall {{ klnagent_package_path }} &&
|
||||
touch {{ klnagent_config_marker }}
|
||||
- onlyif: dpkg-query -W -f='${Status}' klnagent64 2>/dev/null | grep -q 'install ok installed'
|
||||
- unless: test -f {{ klnagent_config_marker }}
|
||||
- require:
|
||||
- cmd: pmg-klnagent-install
|
||||
- file: pmg-klnagent-autoanswers
|
||||
|
||||
pmg-klnagent-service:
|
||||
service.running:
|
||||
- name: klnagent64
|
||||
- enable: true
|
||||
- require:
|
||||
- cmd: pmg-klnagent-install
|
||||
- cmd: pmg-klnagent-autoinstall-configure
|
||||
{% elif klnagent_enabled %}
|
||||
pmg-klnagent-skipped:
|
||||
test.show_notification:
|
||||
- text: "Kaspersky Administration Agent skipped: set package_url + package_path + administration_server in pillar."
|
||||
{% endif %}
|
||||
|
||||
{% if wazuh_enabled %}
|
||||
pmg-wazuh-download:
|
||||
cmd.run:
|
||||
- name: wget -qO {{ wazuh_pkg_path }} {{ wazuh_pkg_url }}
|
||||
- unless: test -f {{ wazuh_pkg_path }}
|
||||
|
||||
pmg-wazuh-install:
|
||||
cmd.run:
|
||||
- name: WAZUH_MANAGER='{{ wazuh_manager }}' WAZUH_AGENT_GROUP='{{ wazuh_group }}' WAZUH_AGENT_NAME="$(hostname -f)" dpkg -i {{ wazuh_pkg_path }}
|
||||
- unless: dpkg-query -W -f='${Status}' wazuh-agent 2>/dev/null | grep -q 'install ok installed'
|
||||
- require:
|
||||
- cmd: pmg-wazuh-download
|
||||
|
||||
pmg-wazuh-service:
|
||||
service.running:
|
||||
- name: wazuh-agent
|
||||
- enable: true
|
||||
- require:
|
||||
- cmd: pmg-wazuh-install
|
||||
{% endif %}
|
||||
|
||||
{% if qualys_enabled and qualys_package_path and qualys_activation_id and qualys_customer_id %}
|
||||
pmg-qualys-download:
|
||||
cmd.run:
|
||||
- name: wget -qO {{ qualys_package_path }} {{ qualys_package_url }}
|
||||
- unless: test -f {{ qualys_package_path }}
|
||||
|
||||
pmg-qualys-install:
|
||||
cmd.run:
|
||||
- name: dpkg --install {{ qualys_package_path }}
|
||||
- onlyif: test -f {{ qualys_package_path }}
|
||||
- unless: dpkg-query -W -f='${Status}' qualys-cloud-agent 2>/dev/null | grep -q 'install ok installed'
|
||||
- require:
|
||||
- cmd: pmg-qualys-download
|
||||
|
||||
pmg-qualys-activate:
|
||||
cmd.run:
|
||||
- name: /usr/local/qualys/cloud-agent/bin/qualys-cloud-agent.sh ActivationId={{ qualys_activation_id }} CustomerId={{ qualys_customer_id }} ServerUri={{ qualys_server_uri }}
|
||||
- onchanges:
|
||||
- cmd: pmg-qualys-install
|
||||
- onlyif: test -x /usr/local/qualys/cloud-agent/bin/qualys-cloud-agent.sh
|
||||
|
||||
pmg-qualys-service:
|
||||
service.running:
|
||||
- name: qualys-cloud-agent
|
||||
- enable: true
|
||||
- onlyif: test -f {{ qualys_package_path }}
|
||||
- require:
|
||||
- cmd: pmg-qualys-install
|
||||
{% elif qualys_enabled %}
|
||||
pmg-qualys-skipped:
|
||||
test.show_notification:
|
||||
- text: "Qualys agent skipped: set package_url + package_path + activation_id + customer_id in pillar."
|
||||
{% endif %}
|
||||
@@ -0,0 +1,94 @@
|
||||
{% set smtp_monitoring_enabled = salt['pillar.get']('pmg:smtp_monitoring:enabled', True) %}
|
||||
{% set blackbox_package = salt['pillar.get']('pmg:smtp_monitoring:blackbox:package', 'prometheus-blackbox-exporter') %}
|
||||
{% set blackbox_service = salt['pillar.get']('pmg:smtp_monitoring:blackbox:service', 'prometheus-blackbox-exporter') %}
|
||||
{% set smtp_ports = salt['pillar.get']('pmg:smtp_monitoring:ports', [25, 26]) %}
|
||||
{% set smtp_log_window_seconds = salt['pillar.get']('pmg:smtp_monitoring:log_window_seconds', 300) %}
|
||||
|
||||
{% if smtp_monitoring_enabled %}
|
||||
pmg-smtp-monitoring-packages:
|
||||
pkg.installed:
|
||||
- pkgs:
|
||||
- {{ blackbox_package }}
|
||||
- iproute2
|
||||
- jq
|
||||
|
||||
pmg-smtp-monitoring-metrics-dir:
|
||||
file.directory:
|
||||
- name: /var/lib/prometheus/node-exporter
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: "0755"
|
||||
|
||||
pmg-smtp-monitoring-script:
|
||||
file.managed:
|
||||
- name: /usr/local/bin/pmg-smtp-metrics.sh
|
||||
- source: salt://pmg/files/pmg-smtp-metrics.sh.jinja
|
||||
- template: jinja
|
||||
- context:
|
||||
smtp_ports: {{ smtp_ports }}
|
||||
smtp_log_window_seconds: {{ smtp_log_window_seconds }}
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: "0755"
|
||||
- require:
|
||||
- file: pmg-smtp-monitoring-metrics-dir
|
||||
|
||||
pmg-smtp-monitoring-service:
|
||||
file.managed:
|
||||
- name: /etc/systemd/system/pmg-smtp-metrics.service
|
||||
- source: salt://pmg/files/pmg-smtp-metrics.service.jinja
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: "0644"
|
||||
|
||||
pmg-smtp-monitoring-timer:
|
||||
file.managed:
|
||||
- name: /etc/systemd/system/pmg-smtp-metrics.timer
|
||||
- source: salt://pmg/files/pmg-smtp-metrics.timer.jinja
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: "0644"
|
||||
|
||||
pmg-smtp-monitoring-blackbox-config:
|
||||
file.managed:
|
||||
- name: /etc/prometheus/blackbox.yml
|
||||
- source: salt://pmg/files/blackbox.yml.jinja
|
||||
- template: jinja
|
||||
- context:
|
||||
smtp_ports: {{ smtp_ports }}
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: "0644"
|
||||
- require:
|
||||
- pkg: pmg-smtp-monitoring-packages
|
||||
|
||||
pmg-smtp-monitoring-systemd-reload:
|
||||
cmd.run:
|
||||
- name: systemctl daemon-reload
|
||||
- onchanges:
|
||||
- file: pmg-smtp-monitoring-service
|
||||
- file: pmg-smtp-monitoring-timer
|
||||
|
||||
pmg-smtp-monitoring-blackbox-service:
|
||||
service.running:
|
||||
- name: {{ blackbox_service }}
|
||||
- enable: true
|
||||
- require:
|
||||
- pkg: pmg-smtp-monitoring-packages
|
||||
- watch:
|
||||
- file: pmg-smtp-monitoring-blackbox-config
|
||||
|
||||
pmg-smtp-monitoring-timer-running:
|
||||
service.running:
|
||||
- name: pmg-smtp-metrics.timer
|
||||
- enable: true
|
||||
- require:
|
||||
- cmd: pmg-smtp-monitoring-systemd-reload
|
||||
- file: pmg-smtp-monitoring-script
|
||||
|
||||
pmg-smtp-monitoring-run-once:
|
||||
cmd.run:
|
||||
- name: /usr/local/bin/pmg-smtp-metrics.sh
|
||||
- require:
|
||||
- file: pmg-smtp-monitoring-script
|
||||
{% endif %}
|
||||
@@ -0,0 +1,35 @@
|
||||
{% set ssh_allow_users = salt['pillar.get']('pmg:ssh_allow_users', []) %}
|
||||
{% set ssh_password_authentication = salt['pillar.get']('pmg:ssh_password_authentication', True) %}
|
||||
|
||||
pmg-sshd-dropin-dir:
|
||||
file.directory:
|
||||
- name: /etc/ssh/sshd_config.d
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0755'
|
||||
|
||||
pmg-sshd-hardening-config:
|
||||
file.managed:
|
||||
- name: /etc/ssh/sshd_config.d/99-hardening.conf
|
||||
- source: salt://pmg/files/99-hardening.conf.jinja
|
||||
- template: jinja
|
||||
- context:
|
||||
ssh_allow_users: {{ ssh_allow_users }}
|
||||
ssh_password_authentication: {{ ssh_password_authentication }}
|
||||
- user: root
|
||||
- group: root
|
||||
- mode: '0644'
|
||||
|
||||
pmg-validate-sshd-config:
|
||||
cmd.run:
|
||||
- name: sshd -t
|
||||
- onchanges:
|
||||
- file: pmg-sshd-hardening-config
|
||||
|
||||
pmg-sshd-service:
|
||||
service.running:
|
||||
- name: ssh
|
||||
- enable: true
|
||||
- reload: true
|
||||
- watch:
|
||||
- cmd: pmg-validate-sshd-config
|
||||
@@ -0,0 +1,50 @@
|
||||
{% set pmg_services = salt['pillar.get']('pmg:pmg_services', []) %}
|
||||
{% set pmg_update_dist_upgrade = salt['pillar.get']('pmg:update_dist_upgrade', True) %}
|
||||
{% set pmg_update_auto_reboot = salt['pillar.get']('pmg:update_auto_reboot', False) %}
|
||||
{% set pmg_update_reboot_delay_minutes = salt['pillar.get']('pmg:update_reboot_delay_minutes', 1) %}
|
||||
|
||||
pmg-update-precheck-cluster:
|
||||
cmd.run:
|
||||
- name: pmgcm status
|
||||
|
||||
{% for svc in pmg_services %}
|
||||
pmg-update-precheck-service-{{ svc }}:
|
||||
cmd.run:
|
||||
- name: systemctl is-active --quiet {{ svc }}
|
||||
{% endfor %}
|
||||
|
||||
pmg-update-packages:
|
||||
pkg.uptodate:
|
||||
- refresh: true
|
||||
- dist_upgrade: {{ pmg_update_dist_upgrade }}
|
||||
|
||||
pmg-update-postcheck-cluster:
|
||||
cmd.run:
|
||||
- name: pmgcm status
|
||||
- require:
|
||||
- pkg: pmg-update-packages
|
||||
|
||||
{% for svc in pmg_services %}
|
||||
pmg-update-postcheck-service-{{ svc }}:
|
||||
cmd.run:
|
||||
- name: systemctl is-active --quiet {{ svc }}
|
||||
- require:
|
||||
- pkg: pmg-update-packages
|
||||
{% endfor %}
|
||||
|
||||
pmg-update-reboot-required-note:
|
||||
test.show_notification:
|
||||
- text: "После обновления требуется reboot: /var/run/reboot-required"
|
||||
- onlyif: test -f /var/run/reboot-required
|
||||
|
||||
{% if pmg_update_auto_reboot %}
|
||||
pmg-update-schedule-reboot:
|
||||
cmd.run:
|
||||
- name: "shutdown -r +{{ pmg_update_reboot_delay_minutes }} \"Salt pmg.update: reboot required\""
|
||||
- onlyif: test -f /var/run/reboot-required
|
||||
- require:
|
||||
- cmd: pmg-update-postcheck-cluster
|
||||
{% for svc in pmg_services %}
|
||||
- cmd: pmg-update-postcheck-service-{{ svc }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,31 @@
|
||||
{% set pmg_services = salt['pillar.get']('pmg:pmg_services', []) %}
|
||||
{% set pmg_cluster_only_services = salt['pillar.get']('pmg:pmg_cluster_only_services', ['pmgmirror', 'pmgtunnel']) %}
|
||||
|
||||
{% for svc in pmg_services %}
|
||||
pmg-verify-service-{{ svc }}:
|
||||
cmd.run:
|
||||
- name: systemctl is-active --quiet {{ svc }}
|
||||
{% if svc in pmg_cluster_only_services %}
|
||||
- onlyif: "pmgcm status 2>/dev/null | awk 'BEGIN { ok=1 } /no cluster defined/ { ok=0 } END { exit(ok ? 0 : 1) }'"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
pmg-verify-cluster:
|
||||
cmd.run:
|
||||
- name: pmgcm status
|
||||
|
||||
pmg-verify-sshd-config:
|
||||
cmd.run:
|
||||
- name: sshd -t
|
||||
|
||||
pmg-verify-blackbox-service:
|
||||
cmd.run:
|
||||
- name: systemctl is-active --quiet prometheus-blackbox-exporter
|
||||
|
||||
pmg-verify-smtp-metrics-timer:
|
||||
cmd.run:
|
||||
- name: systemctl is-active --quiet pmg-smtp-metrics.timer
|
||||
|
||||
pmg-verify-smtp-metrics-exposed:
|
||||
cmd.run:
|
||||
- name: "curl -fsS http://127.0.0.1:9100/metrics | awk 'BEGIN { ok=0 } /^pmg_smtp_sent_recent / { ok=1 } END { exit(ok ? 0 : 1) }'"
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 3 ]]; then
|
||||
echo "usage: $0 <target_host> <master_host> <master_ip>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
target_host="$1"
|
||||
master_host="$2"
|
||||
master_ip="$3"
|
||||
|
||||
ssh "root@${target_host}" "mkdir -p /root/.ssh && chmod 700 /root/.ssh && [ -f /root/.ssh/id_ed25519 ] || ssh-keygen -t ed25519 -N '' -f /root/.ssh/id_ed25519 >/dev/null"
|
||||
|
||||
pubkey_b64="$(ssh "root@${target_host}" "python3 -c 'import base64, pathlib; print(base64.b64encode(pathlib.Path(\"/root/.ssh/id_ed25519.pub\").read_bytes()).decode())'")"
|
||||
|
||||
ssh "root@${master_host}" "python3 -c 'import base64, pathlib; pub = base64.b64decode(\"${pubkey_b64}\").decode().strip(); ssh_dir = pathlib.Path(\"/root/.ssh\"); ssh_dir.mkdir(mode=0o700, parents=True, exist_ok=True); auth = ssh_dir / \"authorized_keys\"; lines = auth.read_text().splitlines() if auth.exists() else []; auth.write_text((\"\\n\".join(lines + [pub]) + \"\\n\") if pub not in lines else ((\"\\n\".join(lines) + \"\\n\") if lines else \"\")); auth.chmod(0o600)'"
|
||||
|
||||
ssh "root@${target_host}" "ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new root@${master_ip} 'exit 0'"
|
||||
Reference in New Issue
Block a user