package linuxmodules

import (
	"fmt"
	"rubric/utils"
)

var SecurityModule = Module{
	Key:         "security",
	Aliases:     []string{"sec", "selinux", "firewall", "безопасность"},
	Title:       "Безопасность: SELinux и Firewall",
	Description: "Темы 45–46, 48: SELinux, firewalld, восстановление доступа",
	Run:         showSecurity,
}

func showSecurity() {
	utils.Header("Linux — Безопасность: SELinux и Firewall")

	// ─── 45. SELinux ──────────────────────────────────────────────
	utils.Section("45. Принудительный контроль доступа — SELinux")

	utils.Println("SELinux (Security-Enhanced Linux) — система принудительного контроля доступа.")
	utils.Println("Работает поверх обычных прав Unix: даже root ограничен политикой SELinux.")
	fmt.Println()
	utils.Println(utils.ColorBold + "Режимы SELinux:" + utils.ColorReset)
	utils.Code(`Enforcing  — SELinux активен, запрещённые действия блокируются
             и логируются (производственный режим)
Permissive — SELinux активен, нарушения только логируются
             (не блокируются). Для отладки.
Disabled   — SELinux полностью отключён (требует перезагрузки)

# Текущий режим:
getenforce
sestatus               # подробная информация

# Временно изменить режим (до перезагрузки):
setenforce 0           # Enforcing → Permissive
setenforce 1           # Permissive → Enforcing
setenforce Permissive  # то же самое

# Постоянно — /etc/selinux/config:
SELINUX=enforcing      # enforcing / permissive / disabled
SELINUXTYPE=targeted   # политика

# После изменения с/на disabled — нужна перезагрузка.
# При включении SELinux после disabled — нужна переметка файлов:
touch /.autorelabel && reboot`)

	utils.Println(utils.ColorBold + "Контексты SELinux:" + utils.ColorReset)
	utils.Code(`Каждый файл, процесс и порт имеет SELinux-контекст:
user:role:type:level
 ↓     ↓    ↓     ↓
system_u:object_r:httpd_sys_content_t:s0

Самое важное — type (тип): httpd_sys_content_t, etc_t...

# Просмотр контекстов файлов:
ls -Z /var/www/html/
ls -lZ /etc/nginx/

# Просмотр контекста процессов:
ps auxZ | grep nginx

# Просмотр контекста портов:
semanage port -l | grep http`)

	utils.Println(utils.ColorBold + "Управление контекстами:" + utils.ColorReset)
	utils.Code(`# Изменить контекст файла:
chcon -t httpd_sys_content_t /var/www/mysite/
chcon -R -t httpd_sys_content_t /var/www/mysite/   # рекурсивно

# Проблема: chcon не постоянен, restorecon сбросит его.
# Правильно: использовать semanage fcontext:

# Добавить постоянное правило для контекста:
semanage fcontext -a -t httpd_sys_content_t '/srv/web(/.*)?'
# Применить правила к файлам:
restorecon -Rv /srv/web/

# Восстановить контекст по умолчанию:
restorecon -Rv /var/www/html/

# Проверить ожидаемый контекст:
matchpathcon /var/www/html/index.html`)

	utils.Println(utils.ColorBold + "Булевые переменные SELinux (Booleans):" + utils.ColorReset)
	utils.Code(`# Booleans — включают/выключают определённые возможности:

# Просмотр всех:
getsebool -a
getsebool -a | grep httpd

# Включить/выключить (временно):
setsebool httpd_can_network_connect on
setsebool httpd_can_network_connect off

# Постоянно (после перезагрузки):
setsebool -P httpd_can_network_connect on
setsebool -P httpd_enable_homedirs on

# Полезные booleans:
httpd_can_network_connect      # nginx/apache → внешняя сеть
httpd_can_network_connect_db   # nginx/apache → БД
httpd_enable_homedirs          # доступ к /home
samba_enable_home_dirs         # Samba к /home
ssh_sysadm_login               # SSH-вход для sysadmin`)

	utils.Println(utils.ColorBold + "Порты и SELinux:" + utils.ColorReset)
	utils.Code(`# Посмотреть разрешённые порты:
semanage port -l
semanage port -l | grep http

# Разрешить nginx слушать нестандартный порт 8888:
semanage port -a -t http_port_t -p tcp 8888

# Удалить правило порта:
semanage port -d -t http_port_t -p tcp 8888

# Изменить существующее правило:
semanage port -m -t http_port_t -p tcp 8888`)

	utils.Println(utils.ColorBold + "Диагностика проблем SELinux:" + utils.ColorReset)
	utils.Code(`# Логи SELinux:
/var/log/audit/audit.log       # основной лог

# Поиск запретов:
ausearch -m avc -ts recent     # последние запреты AVC
grep 'denied' /var/log/audit/audit.log | tail -20

# audit2why — объяснить запрет:
ausearch -m avc -ts recent | audit2why

# audit2allow — создать модуль для разрешения:
ausearch -m avc -ts recent | audit2allow -m mymodule > mymodule.te
checkmodule -M -m -o mymodule.mod mymodule.te
semodule_package -o mymodule.pp -m mymodule.mod
semodule -i mymodule.pp

# sealert — графическая диагностика (если установлен setroubleshoot):
sealert -a /var/log/audit/audit.log`)

	// ─── 46. firewalld ────────────────────────────────────────────
	utils.Section("46. Межсетевой экран — firewalld")

	utils.Println("firewalld — динамический менеджер firewall в RHEL/Fedora/CentOS.")
	utils.Println("Работает поверх nftables (или iptables).")
	fmt.Println()
	utils.Println(utils.ColorBold + "Зоны (Zones) — основное понятие firewalld:" + utils.ColorReset)
	utils.Code(`Зона определяет уровень доверия к трафику интерфейса.

drop      — всё входящее молча отбрасывается
block     — входящее отклоняется с сообщением об ошибке
public    — публичные сети, только явно разрешённое (по умолчанию)
external  — внешние сети с маскарадингом
internal  — внутренние сети, больше доверие
home      — домашние сети
work      — рабочие сети
dmz       — демилитаризованная зона
trusted   — весь трафик разрешён`)

	utils.Println(utils.ColorBold + "Основные команды firewall-cmd:" + utils.ColorReset)
	utils.Command("firewall-cmd --state", "работает ли firewalld")
	utils.Command("systemctl status firewalld", "статус сервиса")
	utils.Command("firewall-cmd --get-active-zones", "активные зоны и интерфейсы")
	utils.Command("firewall-cmd --get-default-zone", "зона по умолчанию")
	utils.Command("firewall-cmd --list-all", "все правила активной зоны")
	utils.Command("firewall-cmd --list-all --zone=public", "правила зоны public")
	fmt.Println()
	utils.Println(utils.ColorBold + "Управление сервисами и портами:" + utils.ColorReset)
	utils.Code(`# Разрешить сервис (временно, до перезагрузки):
firewall-cmd --add-service=http
firewall-cmd --add-service=https
firewall-cmd --add-service=ssh

# Постоянно (--permanent):
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload         # применить permanent-правила

# Удалить сервис:
firewall-cmd --permanent --remove-service=http

# Список доступных сервисов:
firewall-cmd --get-services

# Разрешить порт напрямую:
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --permanent --add-port=5000-5100/tcp
firewall-cmd --permanent --remove-port=8080/tcp`)

	utils.Println(utils.ColorBold + "Rich rules — сложные правила:" + utils.ColorReset)
	utils.Code(`# Разрешить SSH только с конкретного IP:
firewall-cmd --permanent --add-rich-rule='
    rule family="ipv4"
    source address="192.168.1.0/24"
    service name="ssh" accept'

# Заблокировать IP:
firewall-cmd --permanent --add-rich-rule='
    rule family="ipv4"
    source address="10.10.10.5"
    drop'

# Ограничить количество соединений (rate limiting):
firewall-cmd --permanent --add-rich-rule='
    rule service name="ssh"
    limit value="5/m" accept'`)

	utils.Println(utils.ColorBold + "Маскарадинг (NAT):" + utils.ColorReset)
	utils.Code(`# Включить маскарадинг (нужно для роутера):
firewall-cmd --permanent --zone=external --add-masquerade

# Пробросить порт:
firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080
# Внешний 80 → внутренний 8080`)

	// ─── 48. Восстановление доступа ───────────────────────────────
	utils.Section("48. Восстановление доступа")

	utils.Println("Что делать если потеряли пароль root или доступ к системе.")
	fmt.Println()
	utils.Println(utils.ColorBold + "Сброс пароля root (RHEL/CentOS):" + utils.ColorReset)
	utils.Code(`1. Перезагрузить систему.
2. В меню GRUB нажать 'e' на нужной записи.
3. Найти строку начинающуюся с linux или linuxefi.
4. В конце строки добавить: rd.break
5. Нажать Ctrl+X для загрузки.

# В initramfs shell:
mount -o remount,rw /sysroot    # перемонтировать с правами записи
chroot /sysroot                 # перейти в систему
passwd root                     # задать новый пароль root
# Если SELinux включён:
touch /.autorelabel             # переметить файлы SELinux
exit
reboot`)

	utils.Println(utils.ColorBold + "Сброс пароля root (Ubuntu/Debian):" + utils.ColorReset)
	utils.Code(`1. В GRUB выбрать "Advanced options".
2. Выбрать строку "(recovery mode)".
3. В меню recovery выбрать "root".

# Или: в GRUB нажать 'e', найти строку linux,
# заменить ro на rw, добавить в конец: init=/bin/bash

# В shell:
passwd root
# Если файловая система только для чтения:
mount -o remount,rw /
passwd root
sync
reboot -f`)

	utils.Println(utils.ColorBold + "Восстановление из LiveCD:" + utils.ColorReset)
	utils.Code(`# Загрузиться с LiveCD/LiveUSB

# Найти системный раздел:
lsblk

# Смонтировать:
mount /dev/sda2 /mnt
mount /dev/sda1 /mnt/boot       # boot раздел

# Для UEFI:
mount /dev/sda1 /mnt/boot/efi

# Chroot:
mount --bind /dev /mnt/dev
mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
chroot /mnt

# Теперь можно: passwd root, grub-install, etc.`)

	utils.Footer()
}
