Compare commits

..
1 Commits
Author SHA1 Message Date
Ruslan Piatrovich 4cf4def1f6 Initial commit 2026-06-04 08:26:19 +00:00
32 changed files with 1 additions and 3239 deletions
-13
View File
@@ -1,13 +0,0 @@
# Python / virtualenv
.venv/
venv/
__pycache__/
*.pyc
# Salt runtime artifacts
.salt/
# IDE / OS files
.idea/
.vscode/
.DS_Store
-16
View File
@@ -1,16 +0,0 @@
extends: default
ignore: |
.git/
.venv/
venv/
rules:
comments-indentation: disable
document-start:
present: false
line-length:
max: 120
level: warning
truthy:
check-keys: false
-43
View File
@@ -1,43 +0,0 @@
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 -142
View File
@@ -1,143 +1,2 @@
# Salt baseline для PMG # mailgw-salt
Репозиторий содержит автономный набор 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
View File
@@ -1,129 +0,0 @@
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/
-3
View File
@@ -1,3 +0,0 @@
base:
'*':
- pmg
-56
View File
@@ -1,56 +0,0 @@
{% 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
-28
View File
@@ -1,28 +0,0 @@
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
-30
View File
@@ -1,30 +0,0 @@
{% 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 %}
-5
View File
@@ -1,5 +0,0 @@
{% set base_packages = salt['pillar.get']('pmg:base_packages', []) %}
pmg-base-packages:
pkg.installed:
- pkgs: {{ base_packages }}
-151
View File
@@ -1,151 +0,0 @@
{% 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 %}
-4
View File
@@ -1,4 +0,0 @@
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
-14
View File
@@ -1,14 +0,0 @@
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";
-10
View File
@@ -1,10 +0,0 @@
# 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 %}
-14
View File
@@ -1,14 +0,0 @@
## 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
-12
View File
@@ -1,12 +0,0 @@
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 %}
-3
View File
@@ -1,3 +0,0 @@
[Journal]
SystemMaxUse={{ journald_system_max_use }}
MaxFileSec={{ journald_max_file_sec }}
-49
View File
@@ -1,49 +0,0 @@
#!/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
-8
View File
@@ -1,8 +0,0 @@
[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
-219
View File
@@ -1,219 +0,0 @@
#!/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}"
-11
View File
@@ -1,11 +0,0 @@
[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
-11
View File
@@ -1,11 +0,0 @@
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
-39
View File
@@ -1,39 +0,0 @@
{% 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 %}
-50
View File
@@ -1,50 +0,0 @@
{% 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 %}
-208
View File
@@ -1,208 +0,0 @@
{% 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 %}
-94
View File
@@ -1,94 +0,0 @@
{% 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 %}
-35
View File
@@ -1,35 +0,0 @@
{% 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
-50
View File
@@ -1,50 +0,0 @@
{% 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 %}
-31
View File
@@ -1,31 +0,0 @@
{% 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) }'"
-19
View File
@@ -1,19 +0,0 @@
#!/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'"
-3
View File
@@ -1,3 +0,0 @@
base:
'p-mailgw-x*-0*':
- pmg.init