Compare commits
69 Commits
c722255161
...
tmp
| Author | SHA1 | Date | |
|---|---|---|---|
| 00cc795a8e | |||
| d46c56d386 | |||
| d180f06ab0 | |||
| c729ca850b | |||
| 4de49b9d0e | |||
| d2a796e67f | |||
| 96176d2f0c | |||
| 4836eabc2c | |||
| 63d729aec4 | |||
| 04737ff93c | |||
| 9d7a9207f6 | |||
| c22af5a786 | |||
| cf8a68b72a | |||
| 89a278d0bc | |||
| 25be7bab30 | |||
| cb5a34ac95 | |||
| 2bd26f9d4d | |||
| 31b893a85a | |||
| 86506f744a | |||
| 5877dd00ba | |||
| 78cda0c83d | |||
| d490eb9131 | |||
| d770dd669c | |||
| 2362332d15 | |||
| 607719e7d9 | |||
| bf18fa9a3c | |||
| 00eff22bf5 | |||
| dd447678ad | |||
| b066e85b29 | |||
| fb4781f3ae | |||
| eada80734a | |||
| 265d6d5ce7 | |||
| ec2b6e13b7 | |||
| 07f4c78355 | |||
| fbea0bc085 | |||
| fc76f14fca | |||
| f7031b96ea | |||
| 19ec4e82f3 | |||
| a9a446d8b7 | |||
| 7db0de6eda | |||
| db68076181 | |||
| c0b936f792 | |||
| dd903276f7 | |||
| 08a4112420 | |||
| a420980165 | |||
| c4ce5155fe | |||
| e34afe876c | |||
| b24d95ac83 | |||
| 0891a4dcc1 | |||
| c8d7f83fcc | |||
| abd5f2c97a | |||
| 868dec5ab3 | |||
| 4ef1bedad7 | |||
| 0f7e022a5f | |||
| 25cc09b930 | |||
| 7f0d996c00 | |||
| 69653d9883 | |||
| 49fc40b1f7 | |||
| 534f950565 | |||
| 7f7bdfa749 | |||
| 5174e06ce5 | |||
| 74ebd01b5f | |||
| 3d954256a8 | |||
| 46d036d2c1 | |||
| 607ee6297c | |||
| fa1d8dad61 | |||
| cdad11f2d1 | |||
| 631c25885c | |||
| be7c730423 |
@@ -8,10 +8,18 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-and-deploy:
|
build-and-deploy:
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
|
container:
|
||||||
|
image: python:3.12-slim
|
||||||
|
options: --volume /var/www/knowledge-base:/var/www/knowledge-base
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Install git
|
||||||
run: |
|
run: |
|
||||||
git clone --branch main https://git.tishenko.dev/tish/knowledge-base.git
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends git
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 --branch main https://git.tishenko.dev/tish/knowledge-base.git
|
||||||
|
|
||||||
- name: Build site
|
- name: Build site
|
||||||
run: |
|
run: |
|
||||||
@@ -21,8 +29,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Deploy site
|
- name: Deploy site
|
||||||
run: |
|
run: |
|
||||||
# Удаляем только содержимое /var/www/knowledge-base,
|
|
||||||
# чтобы сохранить права и владельца самой папки
|
|
||||||
cd knowledge-base
|
cd knowledge-base
|
||||||
rm -rf /var/www/knowledge-base/*
|
# Удаляем старую версию сайта
|
||||||
|
rm -rf /var/www/knowledge-base/site
|
||||||
|
# Копируем новую
|
||||||
cp -r site/ /var/www/knowledge-base/site/
|
cp -r site/ /var/www/knowledge-base/site/
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
Просто склад моих заметок доступный всем.
|
Просто склад моих заметок, доступный всем.
|
||||||
|
|
||||||
Можно предлагать правки и дополнения на [GitHub](https://github.com/Arity-T/knowledge-base).
|
Можно предлагать правки и дополнения на [GitHub](https://github.com/Arity-T/knowledge-base).
|
||||||
143
docs/VS-Code/attachments/latex.json
Normal file
143
docs/VS-Code/attachments/latex.json
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
{
|
||||||
|
// Place your snippets for latex here. Each snippet is defined under a snippet name and has a prefix, body and
|
||||||
|
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||||
|
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
|
||||||
|
// same ids are connected.
|
||||||
|
// Example:
|
||||||
|
// "Print to console": {
|
||||||
|
// "prefix": "log",
|
||||||
|
// "body": [
|
||||||
|
// "console.log('$1');",
|
||||||
|
// "$2"
|
||||||
|
// ],
|
||||||
|
// "description": "Log output to console"
|
||||||
|
// }
|
||||||
|
"Inclue image": {
|
||||||
|
"prefix": [
|
||||||
|
"\\fig",
|
||||||
|
"\\img"
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
"\\begin{figure}[h!]",
|
||||||
|
" \\centering",
|
||||||
|
" \\includegraphics[width=0.${1:5}\\linewidth]{img/$2.png}",
|
||||||
|
" \\caption{$3.}",
|
||||||
|
" \\label{fig:$2}",
|
||||||
|
"\\end{figure}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Include listing": {
|
||||||
|
"prefix": [
|
||||||
|
"\\lst",
|
||||||
|
"\\listing"
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
"\\begin{lstlisting}[caption={$1.}, label={lst:$2}]",
|
||||||
|
"$3",
|
||||||
|
"\\end{lstlisting}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Texttt": {
|
||||||
|
"prefix": "\\tt",
|
||||||
|
"body": [
|
||||||
|
"\\texttt{$1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Textit": {
|
||||||
|
"prefix": "\\it",
|
||||||
|
"body": [
|
||||||
|
"\\textit{$1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Textbf": {
|
||||||
|
"prefix": "\\bf",
|
||||||
|
"body": [
|
||||||
|
"\\textbf{$1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Picture reference": {
|
||||||
|
"prefix": [
|
||||||
|
"рис",
|
||||||
|
"Рис"
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
"Рис.~\\ref{fig:$1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Special multiline cell for tables": {
|
||||||
|
"prefix": "\\specialcell",
|
||||||
|
"body": "\\newcommand{\\specialcell}[2][l]{\\begin{tabular}[#1]{@{}l@{}}#2\\end{tabular}}"
|
||||||
|
},
|
||||||
|
"TableX": {
|
||||||
|
// c - это просто колонка с тектом по центру, X - максимальная по ширине
|
||||||
|
"prefix": "\\tablex",
|
||||||
|
"body": [
|
||||||
|
"${1:\\usepackage{tabularx\\}}",
|
||||||
|
"\\begin{table}[h!]",
|
||||||
|
" \\centering",
|
||||||
|
" \\caption{$2.}",
|
||||||
|
" \\footnotesize",
|
||||||
|
" \\begin{tabularx}{\\textwidth}{${3:|X|X|X|}}",
|
||||||
|
" \\hline",
|
||||||
|
" \\textbf{${4:Заголовок 1}} & \\textbf{${5:Заголовок 2}} & \\textbf{${6:Заголовок 3}} \\\\\\",
|
||||||
|
" \\hline",
|
||||||
|
" Один & Два & Три \\\\\\",
|
||||||
|
" \\hline",
|
||||||
|
" \\end{tabularx}",
|
||||||
|
"\\end{table}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Table": {
|
||||||
|
"prefix": "\\table",
|
||||||
|
"body": [
|
||||||
|
"\\begin{table}[h!]",
|
||||||
|
" \\centering",
|
||||||
|
" \\caption{$1.}",
|
||||||
|
" \\footnotesize",
|
||||||
|
" \\begin{tabular}{${2:|c|c|c|}}",
|
||||||
|
" \\hline",
|
||||||
|
" \\textbf{${3:Заголовок 1}} & \\textbf{${4:Заголовок 2}} & \\textbf{${5:Заголовок 3}} \\\\\\",
|
||||||
|
" \\hline",
|
||||||
|
" Один & Два & Три \\\\\\",
|
||||||
|
" \\hline",
|
||||||
|
" \\end{tabular}",
|
||||||
|
"\\end{table}"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"Literature list": {
|
||||||
|
"prefix": ["\\literature", "\\bibliography"],
|
||||||
|
"body": [
|
||||||
|
"\\newpage",
|
||||||
|
"\\section*{Список литературы}",
|
||||||
|
"\\addcontentsline{toc}{section}{Список литературы}",
|
||||||
|
"",
|
||||||
|
"\\vspace{-1.5cm}",
|
||||||
|
"\\begin{thebibliography}{0}",
|
||||||
|
" \\bibitem{novikov}",
|
||||||
|
" Новиков, Ф. А. <<Дискретная математика для программистов>>. — 3-е изд. — Санкт-Петербург: Питер, 2009. — 383 с.",
|
||||||
|
" \\bibitem{vostrov}",
|
||||||
|
" Востров А. В, <<Лекции по теории графов>> URL: \\url{https://tema.spbstu.ru/tgraph_lect/}, Дата обращения: 17.09.2024",
|
||||||
|
"\\end{thebibliography}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"change number": {
|
||||||
|
"prefix": ["\\counter", "\\addtocounter"],
|
||||||
|
"body": [
|
||||||
|
"\\addtocounter{${1:table}}{-1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include pdf": {
|
||||||
|
"prefix": ["\\includepdf", "\\pdf"],
|
||||||
|
"body": [
|
||||||
|
"${1:\\usepackage{pdfpages\\}}",
|
||||||
|
"${2:\\usepackage{tikz\\}}",
|
||||||
|
"\\addtocounter{figure}{1}",
|
||||||
|
"\\includepdf[pages={1}, fitpaper, pagecommand={",
|
||||||
|
"\\thispagestyle{empty}",
|
||||||
|
" \\begin{tikzpicture}[remember picture, overlay]",
|
||||||
|
" \\node at (current page.south) [anchor=north, yshift=35pt] {\\large{${3:Рис 3. }}};",
|
||||||
|
" \\end{tikzpicture}",
|
||||||
|
"}]{${4:pdf/$5.pdf}}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 365 KiB After Width: | Height: | Size: 376 KiB |
@@ -11,11 +11,72 @@
|
|||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
|
|
||||||
|
`ctrl + shift + p` - главное сочетание клавиш, которое стоит запомнить.
|
||||||
|
|
||||||
|
`alt + 1/2/3/...` - переключение между открытыми вкладками в порядке их расположения.
|
||||||
|
|
||||||
Удалить множественные курсоры можно с помощью `Esc`.
|
Удалить множественные курсоры можно с помощью `Esc`.
|
||||||
|
|
||||||
Если с помощью `ctrl + d` выделили лишнее, то сочетанием `ctrl + u` можно убрать последнее выделение.
|
Если с помощью `ctrl + d` выделили лишнее, то сочетанием `ctrl + u` можно убрать последнее выделение.
|
||||||
|
|
||||||
`ctrl + alt + ←/→` - перенести файл в левую/правую группу вкладок.
|
`ctrl + alt + ←/→` - перенести файл в левую/правую группу вкладок.
|
||||||
|
|
||||||
|
`alt + z` - крайне полезно при работе с Latex, Markdown и другими "широкими" файлами.
|
||||||
|
|
||||||
|
`ctrl + j` - скрыть/показать нижнюю панель (терминал, логи и т.д.).
|
||||||
|
|
||||||
|
`alt + shift + r` - показать файл, выбранный в левой панели с файлами, в проводнике.
|
||||||
|
|
||||||
|
`ctrl + shift + .` - breadcrumbs
|
||||||
|
|
||||||
|
## Кастомные сочетания клавиш
|
||||||
|
|
||||||
|
Сочетания клавиш для быстрого создания папок и файлов через проводник внутри VS Code.
|
||||||
|
|
||||||
|
- `a` - создать файл.
|
||||||
|
- `shift + a` - создать папку.
|
||||||
|
|
||||||
|
Быстрый переход к определению и типу функции, метода, переменной, etc. В дополнение к `F12`. Удобно использовать вместе с `alt + ←/→`.
|
||||||
|
|
||||||
|
- `alt + d` - перейти к определению.
|
||||||
|
- `alt + t` - перейти к определению типа.
|
||||||
|
- `alt + i` - перейти к реализации (актуально для Java).
|
||||||
|
|
||||||
|
Обычно я отключаю `explorer.autoReveal` в настройках, чтобы боковая панель с проводником не изменялась, когда я переключаюсь по файлам. Если мне всё-таки требуется выделить открытый файл в `Explorer View`, то я использую дополнительное сочетание клавиш.
|
||||||
|
|
||||||
|
- `alt + shift + r` - показать открытый файл в `Explorer View`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"key": "a",
|
||||||
|
"command": "explorer.newFile",
|
||||||
|
"when": "filesExplorerFocus && !inputFocus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "shift+a",
|
||||||
|
"command": "explorer.newFolder",
|
||||||
|
"when": "filesExplorerFocus && !inputFocus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "alt+d",
|
||||||
|
"command": "editor.action.revealDefinition",
|
||||||
|
"when": "editorHasDefinitionProvider && editorTextFocus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "alt+t",
|
||||||
|
"command": "editor.action.goToTypeDefinition",
|
||||||
|
"when": "editorHasTypeDefinitionProvider && editorTextFocus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "alt+i",
|
||||||
|
"command": "editor.action.goToImplementation",
|
||||||
|
"when": "editorHasImplementationProvider && editorTextFocus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "shift+alt+r",
|
||||||
|
"command": "workbench.files.action.showActiveFileInExplorer",
|
||||||
|
"when": "editorTextFocus"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
39
docs/VS-Code/latex.md
Normal file
39
docs/VS-Code/latex.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# LaTeX в VS Code
|
||||||
|
|
||||||
|
## Подготовка
|
||||||
|
|
||||||
|
Вся работа с LaTeX в VS Code завязана на расширении [LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop). Вот [документация](https://github.com/James-Yu/LaTeX-Workshop/wiki/Install) по установке. Вкратце:
|
||||||
|
|
||||||
|
1. Устанавливаем [Perl](https://strawberryperl.com/)
|
||||||
|
2. Устанавливаем [MikTeX](https://miktex.org/)
|
||||||
|
3. Устанавливаем [LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop)
|
||||||
|
|
||||||
|
После установки можно открыть любой `.tex` файл и попробовать скомпилировать его (`ctrl + alt + b`), а затем открыть предпросмотр (`ctrl + alt + v`). При сохранении файл будет автоматически компилироваться, а предпросмотр обновляться.
|
||||||
|
|
||||||
|
## Сниппеты
|
||||||
|
|
||||||
|
### Создание
|
||||||
|
В LaTeX очень много повторяющихся конструкций, грех не использовать сниппеты. Нажимаем `ctrl + shift + p` (или `f1`), ищем пункт `Snippets: Configure Snippets`, затем ищем `latex`. Автоматически будет создан файл `latex.json`, в который можно добавлять сниппеты.
|
||||||
|
|
||||||
|
### Использование
|
||||||
|
|
||||||
|
Примеры [сниппетов](attachments/latex.json), которые я использую.
|
||||||
|
|
||||||
|
- `\img` - для вставки картинок.
|
||||||
|
- `\lst` или `\listing` - для вставки листингов.
|
||||||
|
- `\tablex` и `\table` - для вставки таблиц.
|
||||||
|
- `\pdf` - для вставки PDF файлов.
|
||||||
|
|
||||||
|
## Примечание
|
||||||
|
|
||||||
|
- Работать с текстом в VS Code намного удобнее, в первую очередь за счёт [горячих клавиш](hotkeys.md). В особенности полезно сочетание `alt + z`, с помощью которого включается автоматический перенос строк.
|
||||||
|
- При написании отчётов, которые обычно обрастают множеством правок, полезно использовать `Git`. `MikTex` создаёт много временных файлов, которые в `Git` добавлять не нужно, поэтому ниже представлена заготовка для `.gitignore`.
|
||||||
|
```sh title=".gitignore"
|
||||||
|
# Игнорировать всё,
|
||||||
|
**/*
|
||||||
|
# кроме
|
||||||
|
!.gitignore
|
||||||
|
!report.tex
|
||||||
|
!img
|
||||||
|
!img/*
|
||||||
|
```
|
||||||
28
docs/assets/logo.svg
Normal file
28
docs/assets/logo.svg
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 175.000000 198.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
|
||||||
|
<g transform="translate(0.000000,198.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#ffffff" stroke="none">
|
||||||
|
<path d="M202 1689 l-92 -231 250 -620 c137 -342 262 -639 277 -661 31 -45 98
|
||||||
|
-92 153 -107 47 -13 155 -13 205 1 58 16 123 65 155 116 32 49 100 214 100
|
||||||
|
240 0 28 -153 434 -160 426 -4 -4 -42 -102 -85 -218 -42 -115 -86 -219 -96
|
||||||
|
-230 -19 -19 -19 -19 -38 0 -10 11 -40 78 -66 150 -26 72 -89 243 -140 380
|
||||||
|
-51 138 -152 412 -225 610 -73 198 -135 363 -139 367 -3 4 -48 -97 -99 -223z"/>
|
||||||
|
<path d="M783 1906 c-60 -19 -107 -51 -139 -93 -27 -36 -114 -232 -114 -259 0
|
||||||
|
-12 59 -178 130 -366 12 -32 25 -58 29 -58 4 0 43 96 86 213 43 116 85 220 95
|
||||||
|
230 16 17 17 17 37 0 14 -12 111 -262 289 -743 147 -399 271 -736 276 -750 9
|
||||||
|
-22 22 5 104 209 l93 234 -229 573 c-126 315 -241 598 -255 629 -35 79 -75
|
||||||
|
126 -137 160 -45 24 -66 29 -138 32 -52 2 -101 -2 -127 -11z"/>
|
||||||
|
<path d="M1412 1737 c-34 -94 -62 -178 -62 -185 0 -20 173 -443 179 -437 2 3
|
||||||
|
35 80 73 171 l67 165 -91 230 c-51 126 -94 229 -97 229 -3 0 -34 -78 -69 -173z"/>
|
||||||
|
<path d="M834 1116 c-35 -16 -72 -58 -80 -93 -17 -67 30 -145 99 -164 83 -22
|
||||||
|
159 29 172 116 15 101 -97 184 -191 141z"/>
|
||||||
|
<path d="M242 850 c-5 -14 -36 -90 -68 -169 -31 -79 -57 -150 -57 -156 0 -17
|
||||||
|
176 -455 183 -455 7 0 130 333 130 353 0 15 -151 402 -170 437 -7 12 -10 10
|
||||||
|
-18 -10z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1,8 +1,5 @@
|
|||||||
# Алиасы команд в Git
|
# Алиасы команд в Git
|
||||||
|
|
||||||
## Полезные ссылки
|
|
||||||
- [Раздел](https://githowto.com/ru/aliases) в GitHowTo
|
|
||||||
|
|
||||||
## Мои алиасы
|
## Мои алиасы
|
||||||
Просто выполнить в терминале:
|
Просто выполнить в терминале:
|
||||||
```sh
|
```sh
|
||||||
@@ -11,6 +8,7 @@ git config --global alias.ci commit
|
|||||||
git config --global alias.cim "commit -m"
|
git config --global alias.cim "commit -m"
|
||||||
git config --global alias.st status
|
git config --global alias.st status
|
||||||
git config --global alias.br branch
|
git config --global alias.br branch
|
||||||
|
git config --global alias.cp cherry-pick
|
||||||
git config --global alias.hist "log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short"
|
git config --global alias.hist "log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short"
|
||||||
git config --global alias.histt "log --pretty=format:'%h %cd | %s%d [%an]' --graph --date=iso"
|
git config --global alias.histt "log --pretty=format:'%h %cd | %s%d [%an]' --graph --date=iso"
|
||||||
git config --global alias.ad "add -A"
|
git config --global alias.ad "add -A"
|
||||||
@@ -37,3 +35,6 @@ git config --global --get-regexp alias
|
|||||||
```sh
|
```sh
|
||||||
git config --global --unset-all alias.your-alias
|
git config --global --unset-all alias.your-alias
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
- [Раздел в GitHowTo](https://githowto.com/ru/aliases)
|
||||||
|
|||||||
84
docs/git/git-filter-repo.md
Normal file
84
docs/git/git-filter-repo.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# git-filter-repo
|
||||||
|
|
||||||
|
С помощью [git-filter-repo](https://github.com/newren/git-filter-repo/) можно переписать историю репозитория.
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
Глобально или в `virtualenv`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip install git-filter-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Замена метаданных коммитов
|
||||||
|
|
||||||
|
`git-filter-repo` позволяет создать скрипт на `Python`, который будет вызван для всех коммитов репозитория. Доступ к данным коммита, с возможностю их редактирования, предоставляется через объект `commit`. Таким образом можно описать любую логику изменения метаданных коммитов на обычном `Python`.
|
||||||
|
|
||||||
|
Ниже представлен пример изменения метаданных коммита по его хэшу. Узнать хэш коммита можно с помощью, например, `git log`.
|
||||||
|
|
||||||
|
=== "Command"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git filter-repo --force --commit-callback "callback.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "callback.py"
|
||||||
|
|
||||||
|
```python
|
||||||
|
"""Файл создан для использования с git-filter-repo.
|
||||||
|
|
||||||
|
Пример команды:
|
||||||
|
git filter-repo --force --commit-callback "callback.py"
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
|
|
||||||
|
def git_timestamp(date_str: str, tz_offset: int = 3) -> bytes:
|
||||||
|
"""
|
||||||
|
Преобразует строку с датой в формат временной метки для git-filter-repo.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
date_str (str): Дата в формате "DD.MM.YYYY HH:MM:SS".
|
||||||
|
tz_offset (int, optional): Сдвиг временной зоны относительно UTC.
|
||||||
|
По умолчанию 3 (московское время).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bytes: Байтовая строка с временной меткой и сдвигом временной зоны
|
||||||
|
в формате `b"{timestamp} {timezone_offset}"`.
|
||||||
|
"""
|
||||||
|
tz = timezone(timedelta(hours=tz_offset))
|
||||||
|
dt = datetime.strptime(date_str, "%d.%m.%Y %H:%M:%S").replace(tzinfo=tz)
|
||||||
|
|
||||||
|
timestamp = int(dt.timestamp())
|
||||||
|
timezone_offset = f"{tz_offset:+03d}00"
|
||||||
|
|
||||||
|
return f"{timestamp} {timezone_offset}".encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
# Обязательно нужно указывать полный хэш коммита
|
||||||
|
if commit.original_id == b"72f6afa37aac4cdbf8d37f22470b77c221a8fce0":
|
||||||
|
# Сообщение коммита
|
||||||
|
commit.message = "Новое сообщение коммита!".encode("utf-8")
|
||||||
|
|
||||||
|
new_name = "Иван Иванов".encode("utf-8")
|
||||||
|
new_email = "ivan-ivanov@mail.com".encode("utf-8")
|
||||||
|
new_date = git_timestamp("01.01.2000 10:00:00")
|
||||||
|
|
||||||
|
# Автор изменений
|
||||||
|
commit.author_name = new_name
|
||||||
|
commit.author_email = new_email
|
||||||
|
|
||||||
|
# Автор коммита
|
||||||
|
commit.committer_name = new_name
|
||||||
|
commit.committer_email = new_email
|
||||||
|
|
||||||
|
# Дата изменений и дата коммита
|
||||||
|
commit.author_date = new_date
|
||||||
|
commit.committer_date = new_date
|
||||||
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
|
||||||
|
- [git-filter-repo](https://github.com/newren/git-filter-repo/?tab=readme-ov-file#simple-example-with-comparisons) on GitHub
|
||||||
|
- [usage examples](https://www.mankier.com/1/git-filter-repo)
|
||||||
134
docs/git/undo.md
Normal file
134
docs/git/undo.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# Отмена действий в Git
|
||||||
|
|
||||||
|
## Отмена изменений до `git add`
|
||||||
|
|
||||||
|
Вернуть состояние файла к последнему коммиту.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git restore <path-to-dir-or-file>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Отмена изменений после `git add` до `git commit`
|
||||||
|
|
||||||
|
Если нежелательные изменения уже проиндексированы, т. е. выполнена команда `git add`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Отменяет git add
|
||||||
|
git restore --staged <path-to-dir-or-file>
|
||||||
|
# Возвращает к исходному состоянию
|
||||||
|
git restore <path-to-dir-or-file>
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно и одной командой.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git reset --hard <path-to-dir-or-file>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Отмена коммитов до `git push`
|
||||||
|
|
||||||
|
### Изменение последнего коммита
|
||||||
|
|
||||||
|
Часто нужно что-то добавить/исправить/удалить в последнем коммите, в том числе сообщение коммита.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Добавляем/исправляем/удаляем
|
||||||
|
# Индексируем изменения (git add)
|
||||||
|
git commit -m "New message" --amend
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно без `-m` и сообщения, если не хотим его менять, тогда надо будет просто закрыть открывшийся редактор, либо добавить флаг `--no-edit`, чтобы `Git` даже и не предлагал изменять сообщение.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Добавляем/исправляем/удаляем
|
||||||
|
# Индексируем изменения (git add)
|
||||||
|
git commit --amend --no-edit
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning "Коммит не должен быть запушен!"
|
||||||
|
|
||||||
|
Команда `git commit --amend` изменяет историю репозитория, не нужно использовать её, если последний коммит уже оказался на сервере (была выполнена команда `git push`).
|
||||||
|
|
||||||
|
### Удаление последних коммитов
|
||||||
|
|
||||||
|
Можно полностью удалить коммит из истории репозитория с помощью `git reset`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Можно добавить --hard, чтобы откатить изменения и вернуть рабочий каталог
|
||||||
|
# к состоянию, в котором он был на предыдущем коммите
|
||||||
|
git reset HEAD~1
|
||||||
|
```
|
||||||
|
|
||||||
|
После такого удаления можно делать `git push`, не опасаясь, что кто-нибудь ещё сможет получить доступ к данным коммита, его следы могут остаться лишь в локальной копии репозитория.
|
||||||
|
|
||||||
|
!!! warning "Коммит не должен быть запушен!"
|
||||||
|
|
||||||
|
Команда `git reset` изменяет историю репозитория, не нужно использовать её, если последний коммит уже оказался на сервере (была выполнена команда `git push`).
|
||||||
|
|
||||||
|
??? info "Подробнее про `git reset`"
|
||||||
|
|
||||||
|
Команда `git reset <commit>` просто перемещает текущую ветку и `HEAD` на указанный коммит. Соответственно, вместо `HEAD~1` (указатель на предыдущий коммит) можно указать любой другой коммит в текущей ветке, например, `HEAD~3` - позволит отменить последние 3 коммита. Разумеется можно указать хэш коммита, к которому нужно откатиться.
|
||||||
|
|
||||||
|
Параметр `--hard` говорит о том, что нужно сбросить и индекс и рабочий каталог до состояния указанного коммита. Есть и другие варианты: `--soft` - оставить изменения в индексе и рабочем каталоге, `--mixed` (значение по умолчанию) - сбросить индекс, но не трогать рабочий каталог.
|
||||||
|
|
||||||
|
??? info "Как восстановить коммиты, удалённые с помощью `git reset`"
|
||||||
|
|
||||||
|
`Git` ведёт локальный журнал всех операций, так что хэш коммита ещё можно восстановить.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git reflog
|
||||||
|
```
|
||||||
|
|
||||||
|
Зная хэш коммита, можно вернуть состояние ветки к нему с помощью того же `git reset`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git reset <commit-hash>
|
||||||
|
```
|
||||||
|
|
||||||
|
Или, например, вынести его в отдельную ветку.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git checkout <commit-hash>
|
||||||
|
git checkout -b branch-with-restored-commit
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Отмена коммитов после `git push`
|
||||||
|
|
||||||
|
### Отмена изменений последнего коммита
|
||||||
|
|
||||||
|
Если коммит уже оказался в удалённом репозитории, то самый простой вариант - просто сделать новый коммит, который будет отменять изменения предыдущего.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git revert HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно указать диапазон коммитов, которые нужно отменить, или перечислить их хэши.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Создаст коммит с отменой изменений последних трёх коммитов
|
||||||
|
git revert HEAD~3..HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
После `git revert` можно спокойно делать `git push`, не опасаясь, что возникнут какие-либо конфликты с удалённым репозиторием.
|
||||||
|
|
||||||
|
!!! warning "Изменения сохраняются в репозитории"
|
||||||
|
|
||||||
|
Никакие данные ни из локального, ни из удалённого репозитория не удаляются, все коммиты остаются в истории той же самой ветки. `git revert` не подходит, если нужно полностью удалить нежелательные изменения (например, конфиденциальные данные) из локального и удалённого репозитория.
|
||||||
|
|
||||||
|
### Удаление последних коммитов из удалённого репозитория
|
||||||
|
|
||||||
|
Переписывать историю удалённого репозитория можно если над репозиторием работает один человек, либо, если на сервер была отправлена конфиденциальная информация, которую непременно нужно удалить, в других случаях лучше использовать `git revert`.
|
||||||
|
|
||||||
|
Для отмены коммитов можно использовать методы, описанные в предыдущем разделе, а затем перезаписать историю удалённого репозитория.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git push --force
|
||||||
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
- GitHowTo [12](https://githowto.com/ru/undoing_local_changes), [13](https://githowto.com/ru/undoing_staged_changes), [14](https://githowto.com/ru/undoing_committed_changes), [15](https://githowto.com/ru/removing_commits_from_a_branch).
|
||||||
|
- [Отмена нескольких коммитов](https://stackoverflow.com/questions/1463340/how-can-i-revert-multiple-git-commits#answer-1470452).
|
||||||
|
- [Удаление запушенного коммита](https://stackoverflow.com/questions/3293531/how-to-permanently-remove-few-commits-from-remote-branch#answer-41726152).
|
||||||
5
docs/misc/devtools.md
Normal file
5
docs/misc/devtools.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Заметка о Chrome DevTools
|
||||||
|
|
||||||
|
## Дебаг элементов, исчезающих при смене фокуса
|
||||||
|
|
||||||
|
В DevTools нажимаем сочетание клавиш `ctrl + shift + p` и ищем раздел `Show Rendering`. Устанавливаем флажок `Emulate a focused page`. Теперь элементы, исчезающие при смене фокуса (датапикеры, модальные окна, etc.), не будут исчезать при переключении на DevTools, а значит их код можно спокойно инспектировать.
|
||||||
451
docs/misc/gitea.md
Normal file
451
docs/misc/gitea.md
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
# Gitea
|
||||||
|
|
||||||
|
Шпаргалки актуальны для Gitea 1.24.
|
||||||
|
|
||||||
|
## Установка с Docker
|
||||||
|
|
||||||
|
Эта заметка лишь дополнение к [документации](https://docs.gitea.com/installation/install-with-docker#basics).
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Создаём служебного пользователя
|
||||||
|
sudo useradd --create-home --shell /bin/bash --system gitea
|
||||||
|
|
||||||
|
# Даём пользователю права использовать Docker
|
||||||
|
# Строго говоря это необязательно для разворачивания Gitea,
|
||||||
|
# но скорее всего и ранеры будут запускаться от этого пользователя,
|
||||||
|
# так что ему в любом случае потребуются права на Docker
|
||||||
|
sudo usermod -aG docker gitea
|
||||||
|
|
||||||
|
# Переключаемся на пользователя gitea
|
||||||
|
sudo su - gitea
|
||||||
|
|
||||||
|
# Узнаём его uid и gid
|
||||||
|
id
|
||||||
|
|
||||||
|
# Создаём docker-compose.yml по примеру
|
||||||
|
# Указываем переменные USER, USER_UID и USER_GID и порты
|
||||||
|
nano docker-compose.yml
|
||||||
|
|
||||||
|
# Создаём директорию для данных Gitea из-под пользователя gitea,
|
||||||
|
# иначе Docker сам создаст её из-под root
|
||||||
|
# см. секцию "volumes" в docker-compose.yml
|
||||||
|
mkdir data
|
||||||
|
|
||||||
|
# Запускаем Gitea
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "docker-compose.yml"
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
gitea:
|
||||||
|
external: false
|
||||||
|
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: docker.gitea.com/gitea:1.24.6
|
||||||
|
container_name: gitea
|
||||||
|
environment:
|
||||||
|
- USER=<user>
|
||||||
|
- USER_UID=<uid>
|
||||||
|
- USER_GID=<gid>
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- gitea
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- "28500:3000"
|
||||||
|
- "28522:22"
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь можно перейти по адресу `http://<IPv4>:28500` и завершить установку Gitea.
|
||||||
|
|
||||||
|
Подключиться к контейнеру можно командой.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker exec --user gitea -it gitea bash
|
||||||
|
|
||||||
|
# Уже внутри контейнера можно запускать бинарник gitea
|
||||||
|
/usr/local/bin/gitea help
|
||||||
|
```
|
||||||
|
|
||||||
|
## Создание бэкапа
|
||||||
|
|
||||||
|
Ссылка на [документацию](https://docs.gitea.com/administration/backup-and-restore).
|
||||||
|
|
||||||
|
### Gitea установлена из бинарника
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# gitea - это пользователь под которым запущен Gitea, часто это просто git
|
||||||
|
sudo su - gitea
|
||||||
|
|
||||||
|
mkdir gitea-backup
|
||||||
|
cd gitea-backup
|
||||||
|
|
||||||
|
# Нужно указать актуальный путь к конфигу
|
||||||
|
/usr/local/bin/gitea dump -c /etc/gitea/app.ini
|
||||||
|
|
||||||
|
# Если планируется восстановление с другой базой данных,
|
||||||
|
# то нужно указать параметр --database <sqlite3|mysql|postgres>
|
||||||
|
/usr/local/bin/gitea dump --database sqlite3 -c /etc/gitea/app.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gitea установлена с Docker
|
||||||
|
|
||||||
|
Предполагается, что Gitea развёрнута с помощью Docker Compose как описано в инструкции [выше](#установка-с-docker).
|
||||||
|
|
||||||
|
Подключаемся к контейнеру.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker exec --user gitea -it gitea bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Внутри контейнера выполняем.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Бэкап нужно создать в директории /data, потому что она прокинута на хост
|
||||||
|
cd /data
|
||||||
|
|
||||||
|
# (Опционально) Создаём директорию для бэкапов
|
||||||
|
mkdir backups
|
||||||
|
cd backups
|
||||||
|
|
||||||
|
# Нужно указать актуальный путь к конфигу
|
||||||
|
/usr/local/bin/gitea dump -c /data/gitea/conf/app.ini
|
||||||
|
|
||||||
|
# Если планируется восстановление с другой базой данных,
|
||||||
|
# то нужно указать параметр --database <sqlite3|mysql|postgres>
|
||||||
|
/usr/local/bin/gitea dump --database sqlite3 -c /data/gitea/conf/app.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
То же самое в одну команду.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker exec -u gitea -w /data gitea /usr/local/bin/gitea dump -c /data/gitea/conf/app.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
### Перенос бэкапа на другую машину
|
||||||
|
|
||||||
|
Перенести бэкап на другую машину можно, например, так:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Запускаем HTTP сервер (с scp загрузка больших файлов займёт много времени)
|
||||||
|
python3 -m http.server 8080
|
||||||
|
|
||||||
|
# На другой машине скачиваем бэкап
|
||||||
|
curl -O http://<IP>:8080/gitea-dump-1760203345.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Восстановление из бэкапа в Docker
|
||||||
|
|
||||||
|
Предполагается, что Gitea разворачивается из [бэкапа](#создание-бэкапа) с помощью Docker Compose как описано в инструкции [выше](#установка-с-docker). В [документации](https://docs.gitea.com/1.24/administration/backup-and-restore#using-docker-restore) есть соответствующая инструкция, однако она не полная и содержит ошибки.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Если создавали пользователя по инструкции выше,
|
||||||
|
# то команды выполняем от него
|
||||||
|
sudo su - gitea
|
||||||
|
|
||||||
|
# Распаковываем бэкап
|
||||||
|
unzip -q gitea-dump-*.zip -d dump
|
||||||
|
|
||||||
|
# Создаём директорию для данных Gitea
|
||||||
|
mkdir data
|
||||||
|
|
||||||
|
# Копируем данные
|
||||||
|
cp -r dump/data/ data/gitea/
|
||||||
|
|
||||||
|
# Копируем репозитории
|
||||||
|
mkdir data/git/
|
||||||
|
cp -r dump/repos/ data/git/repositories/
|
||||||
|
|
||||||
|
# Копируем кастомные стили и шаблоны
|
||||||
|
cp -r dump/custom/. data/gitea/
|
||||||
|
|
||||||
|
# Если Gitea в Docker будет работать с SQLite,
|
||||||
|
# то восстановить базу данных можно так.
|
||||||
|
# Команды для других баз данныех есть в документации
|
||||||
|
sqlite3 data/gitea/gitea.db < dump/gitea-db.sql
|
||||||
|
|
||||||
|
# Копируем конфиг
|
||||||
|
mkdir data/gitea/conf
|
||||||
|
cp dump/app.ini data/gitea/conf/app.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
Если до этого Gitea была запущена не через Docker, то нужно отредактировать конфиг.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
vim data/gitea/conf/app.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "`app.ini` для Docker"
|
||||||
|
|
||||||
|
Это не полноценный конфиг, а лишь часть настроек для запуска Gitea Docker.
|
||||||
|
Подразумевается, что конфиг был перенесён из бэкапа.
|
||||||
|
|
||||||
|
`SSH_PORT` нужно указать тот же, что и в `docker-compose.yml`.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
RUN_USER = gitea
|
||||||
|
WORK_PATH = /data/gitea
|
||||||
|
|
||||||
|
[server]
|
||||||
|
LOCAL_ROOT_URL = http://localhost:3000
|
||||||
|
APP_DATA_PATH = /data/gitea
|
||||||
|
DOMAIN = localhost
|
||||||
|
SSH_DOMAIN = localhost
|
||||||
|
HTTP_PORT = 3000
|
||||||
|
SSH_PORT = 28522
|
||||||
|
|
||||||
|
[repository]
|
||||||
|
ROOT = /data/git/repositories
|
||||||
|
|
||||||
|
[database]
|
||||||
|
PATH = /data/gitea/gitea.db
|
||||||
|
DB_TYPE = sqlite3
|
||||||
|
HOST = localhost:3306
|
||||||
|
NAME = gitea
|
||||||
|
USER = root
|
||||||
|
PASSWD =
|
||||||
|
LOG_SQL = false
|
||||||
|
|
||||||
|
[lfs]
|
||||||
|
PATH = /data/git/lfs
|
||||||
|
|
||||||
|
[log]
|
||||||
|
ROOT_PATH = /data/gitea/log
|
||||||
|
```
|
||||||
|
|
||||||
|
Запускаем Gitea, скорее всего она начнёт падать с ошибкой `permission denied`, а Docker будет пытаться её перезапустить. При первом запуске Gitea создаёт директории для ssh ключей, но по какой-то причине они создаются из-под `root`, а не из-под пользователя `gitea`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Специально без -d, ждём когда в логах повалятся ошибки и нажимаем Ctrl+C
|
||||||
|
docker compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь нужно из-под `root` или с помощью `sudo` указать нужные права.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Выходим из пользователя gitea (Ctrl + D или exit)
|
||||||
|
# и выполняем команду с root правами
|
||||||
|
sudo chown -R gitea:gitea ~gitea/data
|
||||||
|
```
|
||||||
|
|
||||||
|
Снова запускаем Gitea.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo su - gitea
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Если всё работает корректно, то файлы бэкапа можно удалить.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rm -rf dump gitea-dump-*.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Кастомизация Gitea
|
||||||
|
|
||||||
|
Во всех командах подразумевается, что Gitea [установлена из бинарника](https://docs.gitea.com/installation/install-from-binary) и [запускается как `systemd` сервис](https://docs.gitea.com/installation/linux-service).
|
||||||
|
|
||||||
|
В документации есть страница, посвящённая [кастомизации Gitea](https://docs.gitea.com/administration/customizing-gitea).
|
||||||
|
|
||||||
|
|
||||||
|
### Свой `css`
|
||||||
|
|
||||||
|
Добавляем ссылку на свой файл со стилями.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Путь по умолчанию
|
||||||
|
export GITEA_CUSTOM=/var/lib/gitea/custom
|
||||||
|
|
||||||
|
sudo -u git mkdir -p $GITEA_CUSTOM/templates/custom
|
||||||
|
sudo -u git nano $GITEA_CUSTOM/templates/custom/header.tmpl
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "header.tmpl"
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link rel="stylesheet" href="/assets/css/custom.css">
|
||||||
|
```
|
||||||
|
|
||||||
|
Создаём файл со стилями.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo -u git mkdir -p $GITEA_CUSTOM/public/assets/css
|
||||||
|
sudo -u git nano $GITEA_CUSTOM/public/assets/css/custom.css
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Пример custom.css"
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Стили для git.tishenko.dev */
|
||||||
|
* {
|
||||||
|
transition: all 0.125s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Список переменных и их значения по умолчанию
|
||||||
|
*
|
||||||
|
* Для светлой темы
|
||||||
|
* https://github.com/go-gitea/gitea/blob/main/web_src/css/themes/theme-gitea-light.css
|
||||||
|
*
|
||||||
|
* Для тёмной темы
|
||||||
|
* https://github.com/go-gitea/gitea/blob/main/web_src/css/themes/theme-gitea-dark.css
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Переопределения переменных для светлой и тёмной темы одновременно */
|
||||||
|
:root {
|
||||||
|
/* Основной цвет */
|
||||||
|
--color-primary: #6674c4;
|
||||||
|
--color-primary-contrast: #ffffff;
|
||||||
|
|
||||||
|
/* https://maketintsandshades.com/#4051B5 */
|
||||||
|
--color-primary-dark-1: #7985cb;
|
||||||
|
--color-primary-dark-2: #8c97d3;
|
||||||
|
--color-primary-dark-3: #a0a8da;
|
||||||
|
--color-primary-dark-4: #b3b9e1;
|
||||||
|
--color-primary-dark-5: #c6cbe9;
|
||||||
|
--color-primary-dark-6: #d9dcf0;
|
||||||
|
--color-primary-dark-7: #eceef8;
|
||||||
|
|
||||||
|
--color-primary-light-1: #5362bc;
|
||||||
|
--color-primary-light-2: #4051b5;
|
||||||
|
--color-primary-light-3: #3a49a3;
|
||||||
|
--color-primary-light-4: #334191;
|
||||||
|
--color-primary-light-5: #2d397f;
|
||||||
|
--color-primary-light-6: #26316d;
|
||||||
|
--color-primary-light-7: #20295b;
|
||||||
|
|
||||||
|
--color-primary-alpha-10: #6674c419;
|
||||||
|
--color-primary-alpha-20: #6674c433;
|
||||||
|
--color-primary-alpha-30: #6674c44b;
|
||||||
|
--color-primary-alpha-40: #6674c466;
|
||||||
|
--color-primary-alpha-50: #6674c480;
|
||||||
|
--color-primary-alpha-60: #6674c499;
|
||||||
|
--color-primary-alpha-70: #6674c4b3;
|
||||||
|
--color-primary-alpha-80: #6674c4cc;
|
||||||
|
--color-primary-alpha-90: #6674c4e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Переопределения переменных для светлой темы */
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {}
|
||||||
|
|
||||||
|
#navbar-logo {
|
||||||
|
padding: 5px !important;
|
||||||
|
background: #14151A;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar-logo img {
|
||||||
|
width: 27px !important;
|
||||||
|
height: 27px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar-logo:hover {
|
||||||
|
background: #14151A !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Переопределения переменных для тёмной темы */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
/* Шапка */
|
||||||
|
--color-nav-bg: #14151A;
|
||||||
|
--color-secondary-nav-bg: #14151A;
|
||||||
|
--color-nav-text: #BEC1C6;
|
||||||
|
--color-nav-hover-bg: #272A35;
|
||||||
|
|
||||||
|
/* Тёмно-серый фон основной */
|
||||||
|
--color-body: #1E2129;
|
||||||
|
|
||||||
|
--color-input-background: #14151A;
|
||||||
|
--color-menu: #14151A;
|
||||||
|
--color-card: #14151A;
|
||||||
|
--color-button: #14151A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Перезапускаем Gitea.
|
||||||
|
```sh
|
||||||
|
sudo systemctl restart gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
После изменения стилей, страницу в браузере нужно обновить с помощью `ctrl + f5`.
|
||||||
|
|
||||||
|
### Настройка `app.ini`
|
||||||
|
|
||||||
|
Перечень всех возможных настроек представлен в [документации](https://docs.gitea.com/administration/config-cheat-sheet).
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/gitea/app.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Пример параметров app.ini"
|
||||||
|
```ini
|
||||||
|
[server]
|
||||||
|
LANDING_PAGE = explore
|
||||||
|
|
||||||
|
[other]
|
||||||
|
SHOW_FOOTER_VERSION = false
|
||||||
|
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
|
||||||
|
SHOW_FOOTER_POWERED_BY = false
|
||||||
|
ENABLE_FEED = false
|
||||||
|
|
||||||
|
[i18n]
|
||||||
|
LANGS = en-US,ru-RU
|
||||||
|
NAMES = English,Русский
|
||||||
|
|
||||||
|
[repository]
|
||||||
|
DISABLE_STARS = true
|
||||||
|
|
||||||
|
[cron]
|
||||||
|
ENABLED = true
|
||||||
|
|
||||||
|
[ui.meta]
|
||||||
|
AUTHOR = Artem Tishenko: Personal Git Repository Hub
|
||||||
|
DESCRIPTION = A personal hub for managing Git repositories by Artem Tishenko.
|
||||||
|
KEYWORDS = Artem Tishenko, Artyom Tishchenko, Git, self-hosted, personal projects, repositories, Gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
Перезапускаем Gitea.
|
||||||
|
```sh
|
||||||
|
sudo systemctl restart gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
### Изменение шаблонов страниц
|
||||||
|
|
||||||
|
Ищем шаблон для нужной версии в [репозитории Gitea](https://github.com/go-gitea/gitea/tree/main/templates), загружаем с помощью `wget` по такому же пути в `$GITEA_CUSTOM/templates` и редактируем.
|
||||||
|
|
||||||
|
Так, например, можно убрать пункт "Помощь" с ссылкой на `https://docs.gitea.com` из основного меню.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Путь по умолчанию
|
||||||
|
export GITEA_CUSTOM=/var/lib/gitea/custom
|
||||||
|
|
||||||
|
# gitea --version
|
||||||
|
export GITEA_VERSION=v1.22.3
|
||||||
|
|
||||||
|
sudo -u git mkdir -p $GITEA_CUSTOM/templates/base
|
||||||
|
wget -P $GITEA_CUSTOM/templates/base https://raw.githubusercontent.com/go-gitea/gitea/refs/tags/$GITEA_VERSION/templates/base/head_navbar.tmpl
|
||||||
|
sudo -u git nano $GITEA_CUSTOM/templates/base/head_navbar.tmpl
|
||||||
|
```
|
||||||
|
|
||||||
|
Перезапускаем Gitea.
|
||||||
|
```sh
|
||||||
|
sudo systemctl restart gitea
|
||||||
|
```
|
||||||
282
docs/misc/minecraft-server.md
Normal file
282
docs/misc/minecraft-server.md
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
# Minecraft Server
|
||||||
|
|
||||||
|
[docker-minecraft-server](https://github.com/itzg/docker-minecraft-server) - самый простой и удобный способ запуска своего Minecraft сервера. У проекта есть хорошая [документация](https://docker-minecraft-server.readthedocs.io/en/latest/), но всё же есть некоторые нюансы, которые стоит записать.
|
||||||
|
|
||||||
|
## Запуск сервера
|
||||||
|
|
||||||
|
1. Устанавливаем [`docker`](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository). Не забываем про [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user).
|
||||||
|
2. Создаём папку для сервера.
|
||||||
|
```sh
|
||||||
|
mkdir minecraft-server
|
||||||
|
cd minecraft-server
|
||||||
|
```
|
||||||
|
3. Создаём файл `docker-compose.yml`.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nano docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "docker-compose.yml"
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# https://docker-minecraft-server.readthedocs.io/en/latest/
|
||||||
|
services:
|
||||||
|
mc:
|
||||||
|
image: itzg/minecraft-server
|
||||||
|
tty: true
|
||||||
|
stdin_open: true
|
||||||
|
ports:
|
||||||
|
# Порт 25565 является стандартным, все клиенты по умолчанию
|
||||||
|
# подключаются к нему, если порт не задан явно
|
||||||
|
- "25565:25565"
|
||||||
|
environment:
|
||||||
|
# Полный список переменных можно найти в документации
|
||||||
|
# https://docker-minecraft-server.readthedocs.io/en/latest/variables/
|
||||||
|
|
||||||
|
EULA: "TRUE"
|
||||||
|
|
||||||
|
# Не забудьте указать нужную версию Minecraft
|
||||||
|
VERSION: 1.19.4
|
||||||
|
|
||||||
|
# Разрешаем подключаться без лицензии Minecraft
|
||||||
|
ONLINE_MODE: false
|
||||||
|
|
||||||
|
# Указываем название игрового мира (будет храниться в ./data/<LEVEL>)
|
||||||
|
# Чтобы изменить мир, достаточно изменить значение этой переменной
|
||||||
|
# и перезапустить контейнер
|
||||||
|
LEVEL: MyWorld
|
||||||
|
|
||||||
|
# Описание сервера
|
||||||
|
MOTD: |
|
||||||
|
Tish's Minecraft Server on %VERSION%
|
||||||
|
|
||||||
|
# Иконка сервера
|
||||||
|
# Надо разместить файл icon.png в ./data/
|
||||||
|
# ICON: /data/icon.png
|
||||||
|
# OVERRIDE_ICON: true
|
||||||
|
|
||||||
|
# Автоотключение пустого сервера
|
||||||
|
# ENABLE_AUTOSTOP: TRUE
|
||||||
|
# AUTOSTOP_TIMEOUT_EST: 300
|
||||||
|
# AUTOSTOP_TIMEOUT_INIT: 600
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Запускаем сервер.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Остановка сервера
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker compose stop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Консоль сервера
|
||||||
|
|
||||||
|
`ctrl + p ctrl + q` - отключиться от консоли.
|
||||||
|
Если просто нажать `ctrl + c`, то сервер остановится.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker compose attach mc
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шпаргалка по командам
|
||||||
|
|
||||||
|
1. `/list` — список игроков.
|
||||||
|
2. `/op <nickname>` — сделать игрока оператором.
|
||||||
|
3. `/deop <nickname>` — удалить из списка операторов.
|
||||||
|
4. Отдельной команды для просмотра списка операторов нет, но он хранится в файле `data/ops.json`.
|
||||||
|
5. `/whitelist <on|off>` — включить или выключить белый список игроков.
|
||||||
|
6. `/whitelist list` — вывести белый список игроков.
|
||||||
|
7. `/whitelist add <nickname>` — добавить игрока в белый список.
|
||||||
|
8. `/whitelist remove <nickname>` — удалить игрока из белого списка.
|
||||||
|
9. `/gamemode <0|1|2|3>` — изменить режим игры (0 — выживание, 1 — креатив, 2 — приключение, 3 — наблюдатель).
|
||||||
|
10. `/time set <day|night|<число>>` — установить время суток.
|
||||||
|
11. `/weather <clear|rain|thunder>` — изменить погоду.
|
||||||
|
12. `/tp <nickname1> <nickname2>` — телепортировать первого игрока ко второму.
|
||||||
|
13. `/difficulty <peaceful|easy|normal|hard>` — изменить уровень сложности сервера.
|
||||||
|
|
||||||
|
## Домен для сервера
|
||||||
|
|
||||||
|
Если порт стандартный (`25565`), то при подключении можно просто указать домен в A-записи которого указан IP-адрес сервера. Однако если порт нестандартный или хочется сделать несколько доменных имён для одного сервера, то можно использовать SRV-записи.
|
||||||
|
|
||||||
|
Например, если я создам домен `minecraft.tishenko.dev` и запущу сервер на порте `12345`, то чтобы подключаться к серверу без указания порта, мне нужно будет добавить следующую SRV-запись для `minecraft.tishenko.dev`:
|
||||||
|
|
||||||
|
```dns
|
||||||
|
_minecraft._tcp.minecraft.tishenko.dev. 3600 IN SRV 0 5 12345 minecraft.tishenko.dev.
|
||||||
|
```
|
||||||
|
|
||||||
|
Обычно ДНС провайдеры предоставляют UI для создания записей, в нём скорее всего будут следующие поля:
|
||||||
|
|
||||||
|
- Поддомен: `minecraft`
|
||||||
|
- Сервис: `minecraft` (SpaceWeb, например, сам подставляет `_`)
|
||||||
|
- Протокол: `tcp`
|
||||||
|
- TTL: `3600`
|
||||||
|
- Приоритет: `0`
|
||||||
|
- Вес: `5`
|
||||||
|
- Порт: `12345`
|
||||||
|
- Целевой домен: `minecraft.tishenko.dev.` (точка в конце обязательна)
|
||||||
|
|
||||||
|
Целевой домен не обязательно должен совпадать с поддоменом. Можно сделать подключение к серверу по домену `tishenko.dev` указан следующую SRV-запись для домена `tishenko.dev`:
|
||||||
|
|
||||||
|
```dns
|
||||||
|
_minecraft._tcp.tishenko.dev. 3600 IN SRV 0 5 12345 minecraft.tishenko.dev.
|
||||||
|
```
|
||||||
|
|
||||||
|
Домен `minecraft.tishenko.dev` в своей A-записи должен указывать на IP-адрес сервера, но при этом совершенно неважно какая A-запись будет у `tishenko.dev`.
|
||||||
|
|
||||||
|
DNS-записи распространяются не мгновенно, но обычно это занимает 15-30 минут. Отслеживать распространение записей можно с помощью сайта [DNS Checker](https://dnschecker.org/#SRV/_minecraft._tcp.tishenko.dev).
|
||||||
|
|
||||||
|
!!! note "Клиенты Minecraft читают SRV-запись"
|
||||||
|
|
||||||
|
С помощью SRV-записи можно подменять как порт, так и домен сервера. Стандартный клиент Minecraft Java Edition при подключении по домену автоматически ищет SRV-запись _minecraft._tcp.<домен> в DNS. Если такая запись есть, клиент использует указанные в ней порт и хост. С Bedrock Edition могут возникнуть сложности, там не все клиенты читают SRV-запись.
|
||||||
|
|
||||||
|
|
||||||
|
## Автоматическое создание бэкапов
|
||||||
|
|
||||||
|
Скрипт ниже автоматически создаёт бэкапы при завершении работы сервера. Особенно удобно сочетать его с настройкой `ENABLE_AUTOSTOP`, тогда при завершении игровой сессии бэкап будет создан автоматически.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
```sh
|
||||||
|
# Создаём скрипт в директории рядом с docker-compose.yml
|
||||||
|
nano run-and-backup.sh
|
||||||
|
|
||||||
|
# После создания скрипта надо дать ему права на выполнение
|
||||||
|
chmod u+x run-and-backup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "run-and-backup.sh"
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# === настройки ===
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
DATA_DIR="${PROJECT_DIR}/data"
|
||||||
|
BACKUP_DIR="${PROJECT_DIR}/backups"
|
||||||
|
KEEP_BACKUPS=3
|
||||||
|
TAR_COMPRESS_FLAGS="-czf"
|
||||||
|
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
ts() { date +"%Y-%m-%d_%H-%M-%S"; }
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
cat <<'EOF'
|
||||||
|
run-and-backup.sh — запускает Minecraft-сервер через Docker Compose
|
||||||
|
и создаёт резервную копию каталога ./data после завершения работы.
|
||||||
|
|
||||||
|
ИСПОЛЬЗОВАНИЕ:
|
||||||
|
./run-and-backup.sh — запустить сервер в foreground
|
||||||
|
./run-and-backup.sh -d — запустить в background (логи в run-and-backup.log)
|
||||||
|
./run-and-backup.sh -b — только сделать бэкап и выйти
|
||||||
|
./run-and-backup.sh -h — показать эту справку
|
||||||
|
|
||||||
|
ЧТО ДЕЛАЕТ:
|
||||||
|
1) docker compose up (следит за завершением контейнера)
|
||||||
|
2) по Ctrl+C или штатному выходу — docker compose down
|
||||||
|
3) создаёт архив ./backups/mc-data-YYYY-MM-DD_HH-MM-SS.tar.gz
|
||||||
|
4) хранит последние KEEP_BACKUPS бэкапов
|
||||||
|
|
||||||
|
КАК ВОССТАНОВИТЬ СЕРВЕР ИЗ БЭКАПА:
|
||||||
|
1) Остановите сервер:
|
||||||
|
docker compose down
|
||||||
|
2) Очистите или перенесите текущие данные:
|
||||||
|
mv ./data ./data.old
|
||||||
|
mkdir ./data
|
||||||
|
ИЛИ
|
||||||
|
rm -rf ./data/*
|
||||||
|
3) Распакуйте нужный архив в ./data:
|
||||||
|
tar -xzf ./backups/mc-data-YYYY-MM-DD_HH-MM-SS.tar.gz -C ./data
|
||||||
|
4) Запустите сервер
|
||||||
|
|
||||||
|
ПРИМЕЧАНИЯ:
|
||||||
|
• Архив содержит всё из ./data (мир, плагины, конфиги, whitelist, ops и т.д.)
|
||||||
|
• Для фонового режима логи скрипта пишутся в ./run-and-backup.log
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
backup() {
|
||||||
|
local stamp archive
|
||||||
|
stamp="$(ts)"
|
||||||
|
archive="${BACKUP_DIR}/mc-data-${stamp}.tar.gz"
|
||||||
|
|
||||||
|
if [[ ! -d "$DATA_DIR" ]]; then
|
||||||
|
echo "[!] Нет каталога DATA_DIR: $DATA_DIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[*] Бэкап ${DATA_DIR} -> ${archive}"
|
||||||
|
tar $TAR_COMPRESS_FLAGS "$archive" -C "$DATA_DIR" .
|
||||||
|
echo "[+] Готово: $archive"
|
||||||
|
|
||||||
|
echo "[*] Ротация: оставляю последние ${KEEP_BACKUPS}"
|
||||||
|
ls -1t "${BACKUP_DIR}"/mc-data-*.tar.* 2>/dev/null | tail -n +$((KEEP_BACKUPS+1)) | xargs -r rm -f
|
||||||
|
}
|
||||||
|
|
||||||
|
graceful_down_and_backup() {
|
||||||
|
echo "[*] Останавливаю docker compose (graceful)..."
|
||||||
|
docker compose down || true
|
||||||
|
backup
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- разбор флагов ----
|
||||||
|
bg=false
|
||||||
|
do_backup_only=false
|
||||||
|
|
||||||
|
while getopts ":dbh" opt; do
|
||||||
|
case "$opt" in
|
||||||
|
d) bg=true ;;
|
||||||
|
b) do_backup_only=true ;;
|
||||||
|
h) show_help; exit 0 ;;
|
||||||
|
\?) echo "Неизвестный флаг: -$OPTARG" >&2; show_help; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# -b имеет приоритет: просто делаем бэкап и выходим
|
||||||
|
if $do_backup_only; then
|
||||||
|
backup
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# запуск в фоне
|
||||||
|
if $bg; then
|
||||||
|
echo "[*] Запускаю в background (логи: ${PROJECT_DIR}/run-and-backup.log)"
|
||||||
|
nohup "$0" >"${PROJECT_DIR}/run-and-backup.log" 2>&1 &
|
||||||
|
echo "[✓] PID: $!"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---- основной режим ----
|
||||||
|
trap graceful_down_and_backup INT TERM
|
||||||
|
|
||||||
|
echo "[*] Запускаю docker compose в foreground (Ctrl+C для остановки)..."
|
||||||
|
if docker compose up; then
|
||||||
|
echo "[*] docker compose завершился сам — делаю бэкап..."
|
||||||
|
backup
|
||||||
|
else
|
||||||
|
echo "[!] docker compose завершился с ошибкой; если это был Ctrl+C, бэкап уже выполнен ловушкой."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[✓] Готово."
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь можно запускать сервер командой `./run-and-backup.sh`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./run-and-backup.sh -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Вывести справку можно командой.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./run-and-backup.sh -h
|
||||||
|
```
|
||||||
@@ -1,8 +1,38 @@
|
|||||||
# Заметка о Powershell
|
# Заметка о Powershell
|
||||||
|
|
||||||
## Мой Powershell profile
|
## Установка Powershell
|
||||||
|
|
||||||
Открыть файл настроек Powershell:
|
По умолчанию в Windows установлен устаревший Windows PowerShell с кучей багов и отсутствующей поддержкой базовых операторов (`&&`, `|`, `||`). Установить новый Powershell можно по [инструкции в репозитории](https://github.com/PowerShell/PowerShell?tab=readme-ov-file#get-powershell).
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
winget search Microsoft.PowerShell
|
||||||
|
winget install --id Microsoft.PowerShell --source winget
|
||||||
|
```
|
||||||
|
|
||||||
|
Затем стоит [установить PowerShell как профиль по умолчанию](https://stackoverflow.com/a/75891592/17341937).
|
||||||
|
|
||||||
|
Чтобы VSCode использовал PowerShell в качестве профиля по умолчанию, нужно добавить в `settings.json` следующий параметр:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"terminal.integrated.defaultProfile.windows": "PowerShell"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Обновление PSReadLine
|
||||||
|
|
||||||
|
На старых версиях PSReadLine, который используется под капотом PowerShell, возникают различные баги: [иногда не печатаются заглавные буквы](https://github.com/PowerShell/PowerShell/issues/10794#issuecomment-542319327), [не работает `Ctrl + C` при запуске с русской раскладкой](https://github.com/PowerShell/PSReadLine/issues/1393#issuecomment-2065423282). Так что стоит сразу его обновить.
|
||||||
|
|
||||||
|
Закрыть открытые PowerShell, в том числе внутри VS Code или других IDE. Запустить `cmd` от имени администратора и выполнить:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
"C:\Program Files\PowerShell\7\pwsh.exe" -noprofile -command "Install-Module PSReadLine -Force -SkipPublisherCheck -AllowPrerelease"
|
||||||
|
```
|
||||||
|
|
||||||
|
Баг с `Ctrl + C` исправлен частично. Если открыть PowerShell с русской раскладкой, то вместо `Ctrl + C` всё равно будет появляться буква `с`, но теперь достаточно переключить раскладку на английскую и всё заработает.
|
||||||
|
|
||||||
|
## Мой PowerShell profile
|
||||||
|
|
||||||
|
Открыть файл настроек PowerShell:
|
||||||
```powershell
|
```powershell
|
||||||
code $profile # или notepad $profile
|
code $profile # или notepad $profile
|
||||||
```
|
```
|
||||||
@@ -12,12 +42,14 @@ code $profile # или notepad $profile
|
|||||||
# Лучше заменить настоящим wget
|
# Лучше заменить настоящим wget
|
||||||
# https://eternallybored.org/misc/wget/
|
# https://eternallybored.org/misc/wget/
|
||||||
# Скачать EXE для 64-bit и добавить в папку в PATH
|
# Скачать EXE для 64-bit и добавить в папку в PATH
|
||||||
remove-item alias:wget
|
# Удалять алиас нужно только в Windows PowerShell
|
||||||
|
# remove-item alias:wget
|
||||||
|
|
||||||
# Заменяем Invoke-WebRequest нормальным curl
|
# Заменяем Invoke-WebRequest нормальным curl
|
||||||
# Скачиваем curl for 64-bit тут https://curl.se/windows/
|
# Скачиваем curl for 64-bit тут https://curl.se/windows/
|
||||||
# Из папки bin архива переносим curl.exe в папку в PATH
|
# Из папки bin архива переносим curl.exe в папку в PATH
|
||||||
remove-item alias:curl
|
# Удалять алиас нужно только в Windows PowerShell
|
||||||
|
# remove-item alias:curl
|
||||||
|
|
||||||
# Алиасы
|
# Алиасы
|
||||||
new-alias actvenv venv/Scripts/activate
|
new-alias actvenv venv/Scripts/activate
|
||||||
@@ -28,8 +60,11 @@ function crtvenv {
|
|||||||
actvenv
|
actvenv
|
||||||
}
|
}
|
||||||
|
|
||||||
function python-path {
|
# Аналог команды which в Linux
|
||||||
python -c "import sys; print(sys.executable)"
|
# https://stackoverflow.com/a/16949127/17341937
|
||||||
|
function which($name)
|
||||||
|
{
|
||||||
|
Get-Command $name | Select-Object -ExpandProperty Definition
|
||||||
}
|
}
|
||||||
|
|
||||||
# Модуль для автокомплита GIT https://github.com/dahlbyk/posh-git
|
# Модуль для автокомплита GIT https://github.com/dahlbyk/posh-git
|
||||||
@@ -45,7 +80,7 @@ Import-Module DockerCompletion
|
|||||||
# Более удобное автодополнение
|
# Более удобное автодополнение
|
||||||
Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
|
Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
|
||||||
|
|
||||||
# Перемещаться по истории использования команды с помощью стрелочек
|
# Удобный поиск по истории команд с помощью стрелочек
|
||||||
Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward
|
Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward
|
||||||
Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward
|
Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward
|
||||||
```
|
```
|
||||||
@@ -56,21 +91,6 @@ Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward
|
|||||||
Set-ExecutionPolicy RemoteSigned
|
Set-ExecutionPolicy RemoteSigned
|
||||||
```
|
```
|
||||||
|
|
||||||
## Баг с uppercase
|
|
||||||
|
|
||||||
В какой-то момент заглавные буквы просто перестают печататься в Powershell, это означает, что нужно обновить `PSReadLine`.
|
|
||||||
|
|
||||||
Запустить Powershell от имени администратора и выполнить:
|
|
||||||
```powershell
|
|
||||||
Install-Module -Name PowerShellGet -Force
|
|
||||||
```
|
|
||||||
|
|
||||||
Перезапустить от имени администратора и выполнить:
|
|
||||||
```powershell
|
|
||||||
Install-Module PSReadLine -AllowPrerelease -Force
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Крутой аналог grep - ripgrep
|
## Крутой аналог grep - ripgrep
|
||||||
|
|
||||||
[Скачать](https://github.com/BurntSushi/ripgrep/releases/) релиз для винды и добавить в PATH
|
[Скачать](https://github.com/BurntSushi/ripgrep/releases/) релиз для винды и добавить в PATH
|
||||||
|
|||||||
248
docs/python/django-deployment.md
Normal file
248
docs/python/django-deployment.md
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
Предполагается, что на сервере установлена `Ubuntu`.
|
||||||
|
|
||||||
|
## Подготовка
|
||||||
|
|
||||||
|
В этом разделе общая последовательность действий для запуска `Django` приложения на сервере. Многое в нём зависит от конкретного проекта, поэтому команды и их последовательность может быть совсем другой. Главное, что в результате папка с приложением должна оказаться на сервере, а также должно быть создано виртуальное окружение `Python` (не обязательно через `venv`) со всеми зависимостями проекта.
|
||||||
|
|
||||||
|
### Установка Python
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Для начала обновим установленные пакеты
|
||||||
|
sudo apt update
|
||||||
|
sudo apt upgrade
|
||||||
|
|
||||||
|
# Не забудьте указать нужную версию python
|
||||||
|
sudo apt install python3.10 python3.10-venv -y
|
||||||
|
```
|
||||||
|
|
||||||
|
Python может сходу не установиться и из-за следующей ошибки.
|
||||||
|
```
|
||||||
|
E: Unable to locate package python3.10
|
||||||
|
E: Couldn't find any package by glob 'python3.10'
|
||||||
|
E: Unable to locate package python3.10-venv
|
||||||
|
E: Couldn't find any package by glob 'python3.10-venv'
|
||||||
|
```
|
||||||
|
|
||||||
|
Нужно просто добавить репозиторий со списками пакетов Python.
|
||||||
|
```sh
|
||||||
|
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||||
|
sudo apt update
|
||||||
|
# и снова пробуем установить python
|
||||||
|
```
|
||||||
|
|
||||||
|
### Клонируем проект
|
||||||
|
|
||||||
|
На сервере создаём SSH ключ и копируем его в раздел `Deploy keys` (в случае `GitHub`) в настройках репозитория. Если проект публичный, то ключ создавать не обязательно.
|
||||||
|
```sh
|
||||||
|
ssh-keygen
|
||||||
|
cat ~/.ssh/id_rsa.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
А затем клонируем репозиторий.
|
||||||
|
```sh
|
||||||
|
git clone <repo-url.git>
|
||||||
|
```
|
||||||
|
|
||||||
|
Если вдруг `Git` не установлен.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt install git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Готовим Django к запуску
|
||||||
|
|
||||||
|
Предварительно нужно добавить домен или IP-адрес сервера в `ALLOWED_HOSTS` в `settings.py`. Также стоит посмотреть на [deployment checklist](https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/), который предоставляет документация `Django`.
|
||||||
|
|
||||||
|
Настраиваем виртуальное окружение.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3.10 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Создаём миграции и суперпользователя. Команды отличаются в зависимости от проекта.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python manage.py makemigrations <app_name>
|
||||||
|
python manage.py migrate
|
||||||
|
python manage.py createsuperuser
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Gunicorn
|
||||||
|
|
||||||
|
### Установка
|
||||||
|
```sh { .annotate }
|
||||||
|
# В виртуальном окружении проекта выполнить
|
||||||
|
pip install gunicorn
|
||||||
|
|
||||||
|
# Можно перейти по адресу сервера в браузере на порт 8000 и убедиться,
|
||||||
|
# что всё работает (ну почти, статики тут не будет)
|
||||||
|
# Не на всех VDS может быть открыт 8000 порт, в таком случае просто
|
||||||
|
# смотрим на отсутствие ошибок
|
||||||
|
gunicorn --bind 0.0.0.0:8000 django-app-name.wsgi # (1)!
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Не забудьте заменить `django-app-name.wsgi` на реальный путь к `wsgi.py` файлу проекта.
|
||||||
|
|
||||||
|
### UNIX-сокет
|
||||||
|
Создаём UNIX-сокет в `systemd` для локального обмена данными между `Gunicorn`, в котором развёрнут `Django`, и `nginx`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/systemd/system/gunicorn.socket
|
||||||
|
```
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=gunicorn socket
|
||||||
|
|
||||||
|
[Socket]
|
||||||
|
ListenStream=/run/gunicorn.sock
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sockets.target
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip "UNIX-сокет можно назвать как угодно"
|
||||||
|
|
||||||
|
Это особенно полезно, когда на одной машине с помощью `Gunicorn` нужно развёрнуть сразу несколько приложений. Кстати, имя сокета (`gunicorn.sock`) не обязательно должно совпадать с названием файла конфигурации сокета для `systemd` (`gunicorn.socket`). При этом в командах по типу `systemctl status` нужно будет использовать имя конфигурационного файла.
|
||||||
|
|
||||||
|
### Сервис в systemd
|
||||||
|
|
||||||
|
Создаём сервис в `systemd`, чтобы `Gunicorn` мог работать как фоновый процесс и запускался вместе с системой.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/systemd/system/gunicorn.service
|
||||||
|
```
|
||||||
|
|
||||||
|
В секции `Service` нужно указать актуального пользователя и путь до проекта, а также путь до `wsgi.py` внутри проекта.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=gunicorn daemon
|
||||||
|
Requires=gunicorn.socket
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=user
|
||||||
|
Group=user
|
||||||
|
WorkingDirectory=/home/user/project-folder
|
||||||
|
ExecStart=/home/user/project-folder/venv/bin/gunicorn \
|
||||||
|
--access-logfile - \
|
||||||
|
--workers 1 \
|
||||||
|
--bind unix:/run/gunicorn.sock \
|
||||||
|
django-app-name.wsgi:application
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь можно запустить сокет и добавить его в автозапуск. `systemd` автоматически запустит сервис `Gunicorn`, когда на сокет придёт первый запрос.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Предварительно перезагружаем systemd
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
|
||||||
|
sudo systemctl start gunicorn.socket
|
||||||
|
sudo systemctl enable gunicorn.socket
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Nginx
|
||||||
|
|
||||||
|
### Установка
|
||||||
|
|
||||||
|
После установки `nginx` должен запуститься автоматически, чтобы проверить можно перейти по адресу сервера в браузере.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt install nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Собираем статику
|
||||||
|
|
||||||
|
Собираем статику (стили, скрипты, картинки) и переносим в `/var/www/` - именно этот каталог обычно используется для её хранения. Если поместить статику в другое место, то могут возникнуть проблемы с доступами. `nginx` просто не сможет работать с нашими файлами, если у него не будет прав на чтение файлов статики и прав на исполнение всех директорий в путях к этим файлам. В конфиге `/etc/nginx/nginx.conf` можно узнать от имени какого пользователя `nginx` обрабатывает запросы.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Команда собирает всю статику Django в папку, которая
|
||||||
|
# указана в settings.py (см. STATIC_ROOT)
|
||||||
|
python manage.py collectstatic
|
||||||
|
|
||||||
|
# Переносим статику из staticfiles (см. STATIC_ROOT) в /var/www/
|
||||||
|
# Вместо static может потребоваться указать другую папку (см. STATIC_URL)
|
||||||
|
sudo mkdir /var/www/django-project
|
||||||
|
sudo cp -r staticfiles /var/www/django-project/static
|
||||||
|
```
|
||||||
|
|
||||||
|
### Конфиг nginx
|
||||||
|
|
||||||
|
Теперь можно настроить `nginx`.
|
||||||
|
```bash
|
||||||
|
# Вместо my-site можно указать название проекта
|
||||||
|
sudo nano /etc/nginx/sites-available/my-site
|
||||||
|
```
|
||||||
|
|
||||||
|
Минимальный конфиг.
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name your_domain_or_ip;
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location /static/ {
|
||||||
|
root /var/www/django-project;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
include proxy_params;
|
||||||
|
proxy_pass http://unix:/run/gunicorn.sock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Добавляем конфиг в активные конфиги, с которыми сейчас работает nginx.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ln -s /etc/nginx/sites-available/my-site /etc/nginx/sites-enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверка корректности конфигов.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nginx -t
|
||||||
|
```
|
||||||
|
|
||||||
|
Если всё в порядке, перезапускаем `nginx`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Если что-то идёт не так...
|
||||||
|
|
||||||
|
### Логи NGINX
|
||||||
|
```bash
|
||||||
|
# Информация обо всех запросах
|
||||||
|
sudo tail -f /var/log/nginx/access.log
|
||||||
|
|
||||||
|
# Информация обо всех ошибках и предупреждениях
|
||||||
|
sudo tail -f /var/log/nginx/error.log
|
||||||
|
|
||||||
|
# Проверка корректности конфигов
|
||||||
|
sudo nginx -t
|
||||||
|
|
||||||
|
# Очистка логов без необходимости перезапуска NGINX
|
||||||
|
sudo truncate -s 0 /var/log/nginx/access.log
|
||||||
|
sudo truncate -s 0 /var/log/nginx/error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Логи Gunicorn
|
||||||
|
```bash
|
||||||
|
sudo journalctl -u gunicorn.service -f
|
||||||
|
sudo journalctl -u gunicorn.socket -f
|
||||||
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
- [How to deploy Django](https://docs.djangoproject.com/en/5.1/howto/deployment/)
|
||||||
|
- [Deployment checklist](https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/)
|
||||||
14
docs/python/django.md
Normal file
14
docs/python/django.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
## Отслеживание SQL запросов
|
||||||
|
|
||||||
|
```python
|
||||||
|
from django.db import connection, reset_queries
|
||||||
|
|
||||||
|
# Сбрасываем счетчик запросов
|
||||||
|
reset_queries()
|
||||||
|
|
||||||
|
# Код, который работает с БД
|
||||||
|
|
||||||
|
# Теперь выводим все запросы, которые были зафиксированы
|
||||||
|
for query in connection.queries:
|
||||||
|
print(query)
|
||||||
|
```
|
||||||
51
docs/servers/bash.md
Normal file
51
docs/servers/bash.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Bash
|
||||||
|
|
||||||
|
## Поиск по истории команд в bash (`↑/↓`)
|
||||||
|
|
||||||
|
Включаем поиск по истории команд `bash` по префиксу.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nano ~/.inputrc
|
||||||
|
```
|
||||||
|
|
||||||
|
=== ".inputrc"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
"\e[A": history-search-backward
|
||||||
|
"\e[B": history-search-forward
|
||||||
|
```
|
||||||
|
|
||||||
|
После обновления `.inputrc` нужно либо начать сеанс заново, либо выполнить команду.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bind -f ~/.inputrc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Поиск по истории команд (`ctrl + r`)
|
||||||
|
|
||||||
|
`ctrl + r` — начать поиск по истории команд. Дальше можно вводить любую часть команды, поиск будет инкрементально обновляться с каждым введённым символом.
|
||||||
|
|
||||||
|
- При повторном нажатии `ctrl + r` будет выведена следующая подходящая команда.
|
||||||
|
- `Esc` или `ctrl + g` — прервать поиск и вернуться в обычный режим.
|
||||||
|
- `→` или `ctrl + j` — вставить найденную команду в командную строку, но не выполнять. Пригождается, если нужно предварительно отредактировать команду.
|
||||||
|
- `Enter` — выполнить найденную команду.
|
||||||
|
|
||||||
|
|
||||||
|
## Сочетания клавиш
|
||||||
|
|
||||||
|
В Bash есть множество сочетаний клавиш для быстрой навигации и редактирования команд.
|
||||||
|
Это функции [GNU Readline](https://en.wikipedia.org/wiki/GNU_Readline) (по этой же ссылке можно найти полный список сочетаний).
|
||||||
|
|
||||||
|
- `ctrl + l` — очистить экран (аналог `clear`).
|
||||||
|
- `alt + .` — вставить последний аргумент предыдущей команды.
|
||||||
|
- `ctrl + a` — в начало строки.
|
||||||
|
- `ctrl + e` — в конец строки.
|
||||||
|
- `alt + b` — назад на одно слово.
|
||||||
|
- `alt + f` — вперед на одно слово.
|
||||||
|
- `ctrl + b` и `ctrl + f` — назад/вперед на один символ (аналог `← / →`, но не нужно тянуться к стрелочкам).
|
||||||
|
- `ctrl + p` и `ctrl + n` — переход по истории команд (аналог `↑ / ↓`, но не нужно тянуться к стрелочкам).
|
||||||
|
- `alt + r` — отменить изменения в команде и вернуть к виду, в котором она хранится в истории команд.
|
||||||
|
- `alt + d` — удалить слово справа от курсора.
|
||||||
|
- `ctrl + w` — удалить слово слева от курсора.
|
||||||
75
docs/servers/cleanup.md
Normal file
75
docs/servers/cleanup.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Очистка места на сервере
|
||||||
|
|
||||||
|
## Полезные команды
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Проверить место во всех разделах
|
||||||
|
df -h
|
||||||
|
|
||||||
|
# Размер файла/папки
|
||||||
|
du -sh <path>
|
||||||
|
|
||||||
|
# Показать самые большие директории в корне
|
||||||
|
sudo du -h -d1 / | sort -hr
|
||||||
|
|
||||||
|
# В домашнем каталоге
|
||||||
|
du -h -d1 ~ | sort -hr
|
||||||
|
|
||||||
|
# Показать файлы больше 100MB
|
||||||
|
find . -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9}' | sort -hr
|
||||||
|
```
|
||||||
|
|
||||||
|
## Логи `journalctl`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Посмотреть сколько занимают логи журнала
|
||||||
|
sudo journalctl --disk-usage
|
||||||
|
|
||||||
|
# Оставить только 100MB самых актуальных логов
|
||||||
|
sudo journalctl --rotate
|
||||||
|
sudo journalctl --vacuum-size=100M
|
||||||
|
|
||||||
|
# Можно задать параметры SystemMaxUse и RuntimeMaxUse
|
||||||
|
# Вместо MB надо использовать M
|
||||||
|
sudo nano /etc/systemd/journald.conf
|
||||||
|
|
||||||
|
# Применить изменения в конфиге
|
||||||
|
sudo systemctl restart systemd-journald
|
||||||
|
```
|
||||||
|
|
||||||
|
## Другие логи
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Посмотреть сколько места занимают логи
|
||||||
|
sudo du -h -d1 /var/log | sort -hr
|
||||||
|
|
||||||
|
# Очищать файлы с логами лучше командой, чтобы процессы могли
|
||||||
|
# дальше писать логи в этим файлы
|
||||||
|
sudo truncate -s 0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Кэш пакетов
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt-get clean
|
||||||
|
sudo apt-get autoremove --purge
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Посмотреть сколько место занимает докер
|
||||||
|
docker system df
|
||||||
|
# Показать детализацию по образам, контейнерам, волюмам
|
||||||
|
docker system df -v
|
||||||
|
|
||||||
|
# Удалить все нетегированные образы, остановленные контейнеры,
|
||||||
|
# неиспользуемые сети, кэш сборки
|
||||||
|
docker system prune
|
||||||
|
|
||||||
|
# Очистит также все образы, с которыми не связан ни один контейнер
|
||||||
|
docker system prune -a
|
||||||
|
|
||||||
|
# Удалить волюмы, с которыми не связан ни один контейнер
|
||||||
|
docker volume prune
|
||||||
|
```
|
||||||
83
docs/servers/grafana.md
Normal file
83
docs/servers/grafana.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
|
||||||
|
## Запуск Prometheus с помощью systemd
|
||||||
|
|
||||||
|
Ссылку на последнюю версию `Prometheus` можно найти на [странице загрузок](https://prometheus.io/download/).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Скачиваем и распаковываем релиз
|
||||||
|
wget <link>
|
||||||
|
tar xvf prometheus-*.*-amd64.tar.gz
|
||||||
|
cd prometheus-*.*
|
||||||
|
|
||||||
|
# Создаём отдельного пользователя и группу для запуска prometheus
|
||||||
|
sudo adduser --system --no-create-home --group prometheus
|
||||||
|
|
||||||
|
# Конфиг
|
||||||
|
sudo mkdir /etc/prometheus
|
||||||
|
sudo cp prometheus.yml /etc/prometheus/
|
||||||
|
sudo chown -R prometheus:prometheus /etc/prometheus
|
||||||
|
|
||||||
|
# Папка для данных
|
||||||
|
sudo mkdir /var/lib/prometheus
|
||||||
|
sudo chown -R prometheus:prometheus /var/lib/prometheus
|
||||||
|
|
||||||
|
# Бинарники prometheus и promtool
|
||||||
|
sudo cp prometheus /usr/local/bin/
|
||||||
|
sudo chown prometheus:prometheus /usr/local/bin/prometheus
|
||||||
|
sudo cp promtool /usr/local/bin/
|
||||||
|
sudo chown prometheus:prometheus /usr/local/bin/promtool
|
||||||
|
```
|
||||||
|
|
||||||
|
Создаём `systemd` сервис. Список возможных параметров запуска `Prometheus` представлен в [документации](https://prometheus.io/docs/prometheus/latest/command-line/prometheus/).
|
||||||
|
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/systemd/system/prometheus.service
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "prometheus.service"
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Prometheus Server
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=prometheus
|
||||||
|
Group=prometheus
|
||||||
|
Restart=on-failure
|
||||||
|
ExecStart=/usr/local/bin/prometheus \
|
||||||
|
--config.file=/etc/prometheus/prometheus.yml \
|
||||||
|
--storage.tsdb.path=/var/lib/prometheus \
|
||||||
|
--storage.tsdb.retention.size=500MB
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Добавляем `Prometheus` в автозагрузку и запускаем.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable prometheus
|
||||||
|
sudo systemctl start prometheus
|
||||||
|
sudo systemctl status prometheus
|
||||||
|
```
|
||||||
|
|
||||||
|
При обновлении конфига, нужно будет перезапустить сервис.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo systemctl restart prometheus
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Node Exporter
|
||||||
|
|
||||||
|
Устанавливаем `Node Exporter` по инструкции из [документации](https://prometheus.io/docs/guides/node-exporter/). Сервис в `systemd` для `Node Exporter` будет создан автоматически.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo systemctl status node_exporter.service
|
||||||
|
```
|
||||||
|
|
||||||
|
[Пример](https://grafana.com/grafana/dashboards/1860-node-exporter-full/) дашборда `Grafana` для `Node Exporter`.
|
||||||
454
docs/servers/mail.md
Normal file
454
docs/servers/mail.md
Normal file
@@ -0,0 +1,454 @@
|
|||||||
|
# Почта on-premise
|
||||||
|
|
||||||
|
Настройка почты на своём сервере со своим доменным именем. В качестве почтового сервера используется [Docker Mailserver](https://github.com/docker-mailserver/docker-mailserver) версии 15.1.0. Вебклиент — [Roundcube Webmail](https://github.com/roundcube/roundcubemail) версии 1.6.11.
|
||||||
|
|
||||||
|
## Открытие портов для почты
|
||||||
|
|
||||||
|
Многие хостинг провайдеры блокируют исходящие соединения на портах 25, 465 и 587, чтобы предотвратить спам или вредоносные рассылки с их серверов. Можно проверить доступность портов с помощью команды `nc`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Выведет "Connection to smtp.gmail.com 25 port [tcp/*] succeeded!",
|
||||||
|
# если порт открыт
|
||||||
|
nc -vz smtp.gmail.com 25
|
||||||
|
nc -vz smtp.gmail.com 465
|
||||||
|
nc -vz smtp.gmail.com 587
|
||||||
|
```
|
||||||
|
|
||||||
|
Если порты закрыты, то можно обратиться в поддержку хостинг провайдера с запросом на открытие портов.
|
||||||
|
|
||||||
|
??? abstract "Пример обращения в поддержку"
|
||||||
|
```
|
||||||
|
Добрый день!
|
||||||
|
|
||||||
|
Прошу разблокировать исходящие соединения на порты 25/tcp, 465/tcp и 587/tcp
|
||||||
|
для моего сервера (<IP-адрес сервера>).
|
||||||
|
Сервер используется для личного почтового домена <домен>, не для массовых рассылок.
|
||||||
|
|
||||||
|
Спасибо!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Настройка DNS
|
||||||
|
|
||||||
|
Настройка DNS на примере домена `tishenko.dev` (почта `@tishenko.dev`). Более подробное описание всех настроек можно прочитать в документации Docker Mailserver: [[1]](https://docker-mailserver.github.io/docker-mailserver/latest/usage/#minimal-dns-setup) и [[2]](https://docker-mailserver.github.io/docker-mailserver/latest/config/best-practices/dkim_dmarc_spf/).
|
||||||
|
|
||||||
|
Со стороны DNS провайдера:
|
||||||
|
|
||||||
|
1. Добавляем поддомен для почты, например, `mail.tishenko.dev` и `www.mail.tishenko.dev`. В A-записи поддомена указываем IP-адрес сервера.
|
||||||
|
|
||||||
|
2. Добавляем MX-запись для основного домена `tishenko.dev`, именно этот домен будет использоваться для отправки и получения почты `@tishenko.dev`.
|
||||||
|
```dns
|
||||||
|
10 mail.tishenko.dev.
|
||||||
|
```
|
||||||
|
Обязательно с точкой в конце. Число 10 это приоритет MX-записи, чем меньше число, тем выше приоритет. Приоритет не играет роли, если запись только одна. MX-записи, созданные DNS провайдером, нужно удалить.
|
||||||
|
|
||||||
|
3. Добавляем TXT-запись для DMARC. Запись надо создать для домена `_dmarc.tishenko.dev.`.
|
||||||
|
```dns
|
||||||
|
v=DMARC1; p=quarantine; sp=quarantine; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@tishenko.dev; ruf=mailto:dmarc.report@tishenko.dev
|
||||||
|
```
|
||||||
|
DMARC-записи, созданные DNS провайдером, нужно удалить.
|
||||||
|
|
||||||
|
4. Добавляем TXT-запись для SPF. Запись надо создать для домена `tishenko.dev.`.
|
||||||
|
```dns
|
||||||
|
v=spf1 mx -all
|
||||||
|
```
|
||||||
|
SPF-записи, созданные DNS провайдером, нужно удалить.
|
||||||
|
|
||||||
|
5. Для окончательной настройки нужно также добавить TXT-запись для DKIM. Однако это можно сделать только после настройки почтового сервера и создания пары ключей. Подробнее про настройку DKIM написано [ниже](#настройка-dkim).
|
||||||
|
|
||||||
|
Со стороны владельца IP-адреса, как правило это VDS провайдер, создаём или редактируем PTR запись для IP-адреса почтового сервера. Указываем в ней почтовый адрес: `mail.tishenko.dev.`.
|
||||||
|
|
||||||
|
??? question "Как проверить настройки DNS?"
|
||||||
|
|
||||||
|
Можно использовать [DNS Checker](https://dnschecker.org/) для проверки [A](https://dnschecker.org/#A/mail.tishenko.dev), [MX](https://dnschecker.org/#MX/tishenko.dev), [PTR](https://dnschecker.org/#PTR/146.103.98.219), [DMARC](https://dnschecker.org/#TXT/_dmarc.tishenko.dev) и [SPF](https://dnschecker.org/#TXT/tishenko.dev) записей.
|
||||||
|
|
||||||
|
Либо использовать команду `dig`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Команда должна вывести IP-адрес сервера
|
||||||
|
dig @1.1.1.1 +short A mail.tishenko.dev
|
||||||
|
|
||||||
|
# 10 mail.tishenko.dev.
|
||||||
|
dig @1.1.1.1 +short MX tishenko.dev
|
||||||
|
|
||||||
|
# mail.tishenko.dev.
|
||||||
|
dig @1.1.1.1 +short -x 146.103.98.219
|
||||||
|
|
||||||
|
# Проверить DMARC
|
||||||
|
dig @1.1.1.1 +short TXT _dmarc.tishenko.dev
|
||||||
|
|
||||||
|
# Проверить SPF
|
||||||
|
dig @1.1.1.1 +short TXT tishenko.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Обновление DNS происходит не мгновенно, обычно это занимает около 20 минут.
|
||||||
|
|
||||||
|
??? abstract "Пример файла зоны"
|
||||||
|
|
||||||
|
В панели управления DNS провайдера можно выгрузить файл зоны и убедиться, что все записи добавились корректно.
|
||||||
|
|
||||||
|
```dns
|
||||||
|
IN MX 10 mail.tishenko.dev.
|
||||||
|
mail IN A 146.103.98.219
|
||||||
|
www.mail IN A 146.103.98.219
|
||||||
|
@ 600 IN TXT "v=spf1 mx -all"
|
||||||
|
_dmarc 600 IN TXT "v=DMARC1; p=quarantine; sp=quarantine; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@tishenko.dev; ruf=mailto:dmarc.report@tishenko.dev"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Mailserver
|
||||||
|
|
||||||
|
Docker Mailserver (DMS) имеет отличную [документацию](https://docker-mailserver.github.io/docker-mailserver/latest/usage/). И DNS и почтовый сервер можно настроить просто пройдясь по ней. Эта заметка лишь дополняет документацию.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Открываем порты для почты (если используется ufw)
|
||||||
|
sudo ufw allow 25,143,465,587,993/tcp
|
||||||
|
|
||||||
|
# Создаём отдельного пользователя для управления почтой
|
||||||
|
# Пользователь vmail в контейнере DMS по умолчанию имеет uid 5000,
|
||||||
|
# поэтому желательно, чтобы и на хосте он имел такой же uid
|
||||||
|
sudo useradd -u 5000 --create-home --shell /bin/bash vmail
|
||||||
|
sudo usermod -aG docker vmail
|
||||||
|
|
||||||
|
# Переключаемся на пользователя vmail
|
||||||
|
sudo su - vmail
|
||||||
|
|
||||||
|
# Скачиваем compose.yaml и mailserver.env из репозитория DMS
|
||||||
|
# Версия 15.1.0
|
||||||
|
DMS_GITHUB_URL="https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/refs/tags/v15.1.0"
|
||||||
|
wget "${DMS_GITHUB_URL}/compose.yaml"
|
||||||
|
wget "${DMS_GITHUB_URL}/mailserver.env"
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь нужно отредактировать `compose.yaml` и `mailserver.env`.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nano compose.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "compose.yaml"
|
||||||
|
|
||||||
|
В `image` указываем конкретную версию вместо `:latest`. В `hostname` указываем почтовый домен (e. g. mail.tishenko.dev).
|
||||||
|
|
||||||
|
Также прокидываем в контейнер волюм `/etc/letsencrypt` для подключения SSL сертификатов, актуально если на хосте используется letsencrypt.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
mailserver:
|
||||||
|
image: ghcr.io/docker-mailserver/docker-mailserver:15.1.0
|
||||||
|
container_name: mailserver
|
||||||
|
# Provide the FQDN of your mail server here (Your DNS MX record should point to this value)
|
||||||
|
hostname: mail.tishenko.dev
|
||||||
|
env_file: mailserver.env
|
||||||
|
# More information about the mail-server ports:
|
||||||
|
# https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/
|
||||||
|
ports:
|
||||||
|
- "25:25" # SMTP (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
|
||||||
|
- "143:143" # IMAP4 (explicit TLS => STARTTLS)
|
||||||
|
- "465:465" # ESMTP (implicit TLS)
|
||||||
|
- "587:587" # ESMTP (explicit TLS => STARTTLS)
|
||||||
|
- "993:993" # IMAP4 (implicit TLS)
|
||||||
|
volumes:
|
||||||
|
- ./docker-data/dms/mail-data/:/var/mail/
|
||||||
|
- ./docker-data/dms/mail-state/:/var/mail-state/
|
||||||
|
- ./docker-data/dms/mail-logs/:/var/log/mail/
|
||||||
|
- ./docker-data/dms/config/:/tmp/docker-mailserver/
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- /etc/letsencrypt:/etc/letsencrypt:ro
|
||||||
|
restart: always
|
||||||
|
stop_grace_period: 1m
|
||||||
|
# Uncomment if using `ENABLE_FAIL2BAN=1`:
|
||||||
|
# cap_add:
|
||||||
|
# - NET_ADMIN
|
||||||
|
healthcheck:
|
||||||
|
test: "ss --listening --ipv4 --tcp | grep --silent ':smtp' || exit 1"
|
||||||
|
timeout: 3s
|
||||||
|
retries: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "mailserver.env"
|
||||||
|
|
||||||
|
В этой заметке DMS настраивается с антиспамом Rspamd. Как отмечено в [документации](https://docker-mailserver.github.io/docker-mailserver/v15.1/config/security/rspamd/), его планируется использовать по умолчанию в будущих версиях DMS. На той же странице документации перечислены legacy проверки, которые нужно отключить. Тут они также продублированы.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Указываем тип SSL сертификатов
|
||||||
|
SSL_TYPE=letsencrypt
|
||||||
|
|
||||||
|
# Включаем Rspamd
|
||||||
|
ENABLE_RSPAMD=1
|
||||||
|
|
||||||
|
# Отключаем legacy проверки, т. к. они уже включены в Rspamd
|
||||||
|
ENABLE_OPENDKIM=0
|
||||||
|
ENABLE_OPENDMARC=0
|
||||||
|
ENABLE_POLICYD_SPF=0
|
||||||
|
ENABLE_AMAVIS=0
|
||||||
|
RSPAMD_GREYLISTING=1
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Создаём директории для волюмов DMS заранее,
|
||||||
|
# чтобы у них был правильный владелец (vmail)
|
||||||
|
mkdir -p ./docker-data/dms/{mail-data,mail-state,mail-logs,config}
|
||||||
|
|
||||||
|
# Запускаем DMS
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# В течение двух минут после первого запуска DMS нужно создать хотя бы один
|
||||||
|
# почтовый адрес, иначе контейнер завершится с ошибкой
|
||||||
|
# Команда предложит задать пароль для нового почтового аккаунта
|
||||||
|
docker exec -it mailserver setup email add artem@tishenko.dev
|
||||||
|
|
||||||
|
# Обязательно добавляем alias для адреса postmaster
|
||||||
|
docker exec -it mailserver setup alias add postmaster@tishenko.dev artem@tishenko.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Настройка SSL
|
||||||
|
|
||||||
|
В документации DMS есть отдельная [страница](https://docker-mailserver.github.io/docker-mailserver/latest/config/security/ssl/) про настройку SSL сертификатов.
|
||||||
|
|
||||||
|
Далее подразумевается, что волюм `/etc/letsencrypt` уже прокинут в контейнер, а также в `mailserver.env` указана переменная `SSL_TYPE=letsencrypt`.
|
||||||
|
|
||||||
|
Получаем сертификаты. Команды нужно выполнять от пользователя с правом использовать `sudo`. `certbot` можно установить с помощью [pip](https://certbot.eff.org/instructions?ws=other&os=pip).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Порт 80 нужен для получения и обновления сертификатов
|
||||||
|
sudo ufw allow 80/tcp
|
||||||
|
|
||||||
|
# Если на сервере есть nginx или другой веб-сервер, используем соответствующий флаг
|
||||||
|
sudo certbot certonly --nginx -d mail.tishenko.dev -d www.mail.tishenko.dev
|
||||||
|
|
||||||
|
# Если на сервере нет nginx (порт 80 не должен быть занят)
|
||||||
|
sudo certbot certonly --standalone -d mail.tishenko.dev -d www.mail.tishenko.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Настройка DKIM
|
||||||
|
|
||||||
|
В документации DMS есть отдельная [страница](https://docker-mailserver.github.io/docker-mailserver/latest/config/best-practices/dkim_dmarc_spf/) про настройку DKIM, DMARC и SPF. Про настройку DMARC и SPF написано [выше](#настройка-dns), а вот для настройки DKIM нужно сначала сгенерировать пару ключей. Публичный ключ как раз и указывается в DKIM.
|
||||||
|
|
||||||
|
Команда для генерации ключей. DMS должен быть запущен.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Выведет в консоль значение для TXT-записи
|
||||||
|
# Также её можно узнать в файле
|
||||||
|
# cat ./docker-data/dms/config/rspamd/dkim/rsa-2048-mail-tishenko.dev.public.dns.txt
|
||||||
|
docker exec -it mailserver setup config dkim domain tishenko.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь добавляем TXT запись для DKIM в DNS. Имя записи должно быть `mail._domainkey.tishenko.dev.`. Проверить запись можно с помощью сайта [DNS Checker](https://dnschecker.org/#TXT/mail._domainkey.tishenko.dev) или команды `dig`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Должна вывести "v=DKIM1; k=rsa; p=<публичный ключ>"
|
||||||
|
dig @1.1.1.1 +short TXT mail._domainkey.tishenko.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка
|
||||||
|
|
||||||
|
Работоспособность и настройки DMS можно проверить с помощью сайта [Mail-Tester](https://www.mail-tester.com/).
|
||||||
|
|
||||||
|
Пример команды для отправки тестового письма.
|
||||||
|
```sh
|
||||||
|
docker exec -it mailserver swaks \
|
||||||
|
--to <адрес с mail-tester> \
|
||||||
|
--from artem@tishenko.dev \
|
||||||
|
--server mail.tishenko.dev \
|
||||||
|
--port 587 \
|
||||||
|
--tls \
|
||||||
|
--auth LOGIN \
|
||||||
|
--auth-user artem@tishenko.dev \
|
||||||
|
--auth-password 'password'
|
||||||
|
```
|
||||||
|
|
||||||
|
Если письмо дойдёт до тестового адреса, то сайт выведет результаты проверки DNS-записей и общую оценку настройки почтового сервера. Если всё сделано правильно, то оценка будет 10/10.
|
||||||
|
|
||||||
|
Дополнительно можно проверить настройки почтового сервера с помощью сайта [MX Toolbox](https://mxtoolbox.com/emailhealth).
|
||||||
|
|
||||||
|
### Администрирование
|
||||||
|
|
||||||
|
Администрировать DMS можно через скрипт `setup` внутри контейнера. Для этого можно подключиться к контейнеру с помощью команды.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker exec -it mailserver bash
|
||||||
|
|
||||||
|
# Уже внутри контейнера
|
||||||
|
setup help
|
||||||
|
```
|
||||||
|
|
||||||
|
Если нужно выполнить всего одну команду, то можно не запускать bash.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker exec -it mailserver setup help
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Roundcube
|
||||||
|
|
||||||
|
Roundcube проще всего развернуть с помощью Docker Compose. Актуальную версию образа можно выбрать на [Docker Hub](https://hub.docker.com/r/roundcube/roundcubemail). Там же можно посмотреть список переменных окружения и их значения. Далее подразумевается, что выбран образ `apache-nonroot`, а также что для управления почтой создан отдельный пользователь `vmail` с uid/gid 5000, как показано в инструкции [выше](#настройка-docker-mailserver).
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Переключаемся на пользователя vmail
|
||||||
|
sudo su - vmail
|
||||||
|
|
||||||
|
mkdir ./roundcube
|
||||||
|
cd ./roundcube
|
||||||
|
|
||||||
|
# Создаём директории для волюмов Roundcube заранее,
|
||||||
|
# чтобы у них был правильный владелец (vmail)
|
||||||
|
mkdir -p ./roundcube/{app,config,db,tmp}
|
||||||
|
|
||||||
|
nano docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Пример docker-compose.yml"
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
roundcube:
|
||||||
|
image: roundcube/roundcubemail:1.6.11-apache-nonroot
|
||||||
|
container_name: roundcube
|
||||||
|
restart: always
|
||||||
|
user: "5000:5000"
|
||||||
|
ports:
|
||||||
|
- "25025:8000"
|
||||||
|
environment:
|
||||||
|
# IMAP
|
||||||
|
- ROUNDCUBEMAIL_DEFAULT_HOST=ssl://mail.tishenko.dev
|
||||||
|
- ROUNDCUBEMAIL_DEFAULT_PORT=993
|
||||||
|
|
||||||
|
# SMTP
|
||||||
|
- ROUNDCUBEMAIL_SMTP_SERVER=tls://mail.tishenko.dev
|
||||||
|
- ROUNDCUBEMAIL_SMTP_PORT=587
|
||||||
|
- ROUNDCUBEMAIL_SMTP_USER=%u
|
||||||
|
- ROUNDCUBEMAIL_SMTP_PASS=%p
|
||||||
|
|
||||||
|
# DB
|
||||||
|
- ROUNDCUBEMAIL_DB_TYPE=sqlite
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
- ROUNDCUBEMAIL_USERNAME_DOMAIN=tishenko.dev
|
||||||
|
volumes:
|
||||||
|
- ./roundcube/app:/var/www/html
|
||||||
|
- ./roundcube/config:/var/roundcube/config
|
||||||
|
- ./roundcube/db:/var/roundcube/db
|
||||||
|
- ./roundcube/tmp:/tmp/roundcube-temp
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь Roundcube доступен на `http://localhost:25025`, чтобы его можно было использовать извне, нужно настроить nginx или аналогичный веб-сервер.
|
||||||
|
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/nginx/sites-available/mail.conf
|
||||||
|
sudo ln -s /etc/nginx/sites-available/mail.conf /etc/nginx/sites-enabled/
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Пример nginx конфига"
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name mail.tishenko.dev www.mail.tishenko.dev;
|
||||||
|
|
||||||
|
client_max_body_size 25m;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:25025;
|
||||||
|
include proxy_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Установить SSL сертификат можно с помощью certbot. Причём если сертификат уже был получен на этапе [настройки DMS](#настройка-ssl), то certbot предложит использовать его.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo certbot --nginx -d mail.tishenko.dev -d www.mail.tishenko.dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Конфиг
|
||||||
|
|
||||||
|
Некоторые настройки Roundcube нельзя задать через переменные окружения, они задаются в файле `config.inc.php`. Например, "название продукта", оно отображается в заголовке страницы, на странице входа и в других местах. По умолчанию это `Roundcube Webmail`. Его можно изменить в `config/config.inc.php`.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nano roundcube/config/config.inc.php
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "config.inc.php"
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
$config['product_name'] = 'Tish\'s Mail';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Настройки аккаунта
|
||||||
|
|
||||||
|
Настройки, относящиеся к конкретному аккаунту, можно задать через UI. Как минимум стоит установить отображаемое имя, иначе у получателей будет отображаться только адрес электронной почты: `Настройки` -> `Профили` -> `Отображаемое имя`. В разделе с профилями также можно указать подпись для писем.
|
||||||
|
|
||||||
|
Roundcube позволяет задать несколько профилей и соответствующих адресов для одного аккаунта и легко переключаться между ними через UI. Для этого в DMS нужно создать алиас для основного адреса, а затем добавить профиль через UI Roundcube в разделе `Настройки` -> `Профили`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker exec -it mailserver setup email add <алиас> <основной адрес>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Внешний вид
|
||||||
|
|
||||||
|
Подразумевается, что прокинут волюм `./roundcube/app:/var/www/html`. Чтобы внешние изменения не терялись при перезапуске контейнера, нужно создать свою тему для Roundcube на основе темы по умолчанию и изменять её.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Желательно выполнять команды от пользователя vmail,
|
||||||
|
# чтобы не было проблем с правами
|
||||||
|
# Либо подключиться к контейнеру через docker exec -it roundcube bash
|
||||||
|
# и редактировать темы из контейнера
|
||||||
|
sudo su - vmail
|
||||||
|
|
||||||
|
# Переходим в директорию с docker-compose.yml для Roundcube
|
||||||
|
cd ./roundcube
|
||||||
|
|
||||||
|
# В разделе environment добавляем переменную окружения
|
||||||
|
# ROUNDCUBEMAIL_SKIN=custom
|
||||||
|
nano docker-compose.yml
|
||||||
|
|
||||||
|
# Копируем тему по умолчанию
|
||||||
|
# Roundcube должен был быть запущен хотя бы один раз,
|
||||||
|
# чтобы тема по умолчанию появилась в волюме
|
||||||
|
cp -r ./roundcube/app/skins/elastic/ ./roundcube/app/skins/custom/
|
||||||
|
|
||||||
|
# Перезапускаем Roundcube
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь любые изменения в теме `custom` будут сохраняться при перезапуске контейнера.
|
||||||
|
|
||||||
|
Подробнее про темы Roundcube можно посмотреть в [документации](https://github.com/roundcube/roundcubemail/wiki/Skins).
|
||||||
|
|
||||||
|
#### Favicon
|
||||||
|
|
||||||
|
Favicon находится в `skins/custom/images/favicon.ico`. Достаточно просто заменить его на свой.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp your-favicon.ico ./roundcube/app/skins/custom/images/favicon.ico
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Логотип
|
||||||
|
|
||||||
|
Логотип находится в `skins/custom/images/logo.svg`. Достаточно просто заменить его на свой.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp your-logo.svg ./roundcube/app/skins/custom/images/logo.svg
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Логотип в письмах
|
||||||
|
|
||||||
|
Для того чтобы у получателей вместо плейсхолдера рядом с именем отправителя отображался логотип, нужно добавить BIMI TXT-запись в DNS для домена `default._bimi.tishenko.dev.`. Нужно будет настроить Nginx или другой веб-сервер, чтобы логотип был доступен по указанному в BIMI адресу.
|
||||||
|
|
||||||
|
```dns
|
||||||
|
v=BIMI1; l=https://tishenko.dev/logo.svg;
|
||||||
|
```
|
||||||
|
|
||||||
|
Однако в gmail и некоторых других почтовых клиентах он всё равно не будет отображаться, так как они требуют для этого платные VMC сертификаты.
|
||||||
174
docs/servers/nginx.md
Normal file
174
docs/servers/nginx.md
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
## Добавление сайта
|
||||||
|
|
||||||
|
Создаём конфиг.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/nginx/sites-available/new-site.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Статический сайт"
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
server_name example.com www.example.com;
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
root /var/www/new-site;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Веб-приложение"
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
server_name giga-chill.ru www.giga-chill.ru;
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
# Все запросы к /api/* перенаправляются на бэкенд
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:8081/;
|
||||||
|
include proxy_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Спецификация API в формате OpenAPI
|
||||||
|
location = /api/openapi.yml {
|
||||||
|
alias /var/www/giga-chill/openapi.yml;
|
||||||
|
types { text/yaml yml yaml; }
|
||||||
|
charset utf-8;
|
||||||
|
charset_types text/yaml application/yaml text/x-yaml application/x-yaml;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Документация API в Swagger UI
|
||||||
|
location = /api/swagger { return 301 /api/swagger/; }
|
||||||
|
location /api/swagger/ {
|
||||||
|
proxy_pass http://127.0.0.1:1240/;
|
||||||
|
include proxy_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Документация API в Redocly
|
||||||
|
location = /api/redoc { return 301 /api/redoc/; }
|
||||||
|
location /api/redoc/ {
|
||||||
|
alias /var/www/giga-chill/redoc/;
|
||||||
|
index index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Все остальные запросы направляются на фронтенд
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
include proxy_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Активируем конфиг.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ln -s /etc/nginx/sites-available/new-site.conf /etc/nginx/sites-enabled/
|
||||||
|
sudo systemctl reload nginx.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Конфиг можно проверить на наличие синтаксических ошибок.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nginx -t
|
||||||
|
```
|
||||||
|
|
||||||
|
??? question "`open() "/etc/nginx/proxy_params" failed (2: No such file or directory)`"
|
||||||
|
|
||||||
|
Обычно файл `/etc/nginx/proxy_params` создаётся автоматически при установке Nginx, однако его несложно добавить самостоятельно, если по каким-то причинам он не был создан или был удалён.
|
||||||
|
|
||||||
|
=== "Терминал"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/nginx/proxy_params
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "proxy_params"
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
proxy_set_header Host $http_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;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Просмотр логов
|
||||||
|
|
||||||
|
По умолчанию логи находятся в `access.log` и `error.log` файлах.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo tail -n 20 /var/log/nginx/access.log
|
||||||
|
sudo tail -n 20 /var/log/nginx/error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## SSL-сертификат с certbot
|
||||||
|
|
||||||
|
!!! tip "Актуальная версия Python"
|
||||||
|
|
||||||
|
Обычно системный `Python` достаточно старый. Для установки `certbot` может потребоваться более новая версия. Минимальные требования можно узнать на [pypi](https://pypi.org/project/certbot/).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt update
|
||||||
|
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||||
|
|
||||||
|
# Вместо 3.1x нужно указать актуальную версию Python
|
||||||
|
sudo apt install python3.1x python3.1x-venv
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь вместо `python3` можно использовать `python3.1x`.
|
||||||
|
|
||||||
|
SSL-сертификат получается и устанавливается с помощью [certbot](https://github.com/certbot/certbot). На их сайте есть подробная пошаговая [инструкция](https://certbot.eff.org/instructions?ws=nginx&os=pip) о том, как правильно его установить, получить сертификаты и включить их автообновление.
|
||||||
|
|
||||||
|
??? note "Установка вкратце"
|
||||||
|
|
||||||
|
```sh { .code-wrap }
|
||||||
|
# Установили certbot в venv
|
||||||
|
# Вместо 3.1x нужно указать актуальную версию Python
|
||||||
|
sudo python3.x -m venv /opt/certbot/
|
||||||
|
sudo /opt/certbot/bin/pip install --upgrade pip
|
||||||
|
sudo /opt/certbot/bin/pip install certbot certbot-nginx
|
||||||
|
|
||||||
|
# Добавили в PATH
|
||||||
|
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
|
||||||
|
|
||||||
|
# Автообновление сертификатов
|
||||||
|
echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew -q" | sudo tee -a /etc/crontab > /dev/null
|
||||||
|
```
|
||||||
|
??? tip "Отключение UFW"
|
||||||
|
|
||||||
|
Может потребоваться временно отключить UFW.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ufw disable
|
||||||
|
|
||||||
|
# Получаем сертификат
|
||||||
|
|
||||||
|
sudo ufw enable
|
||||||
|
```
|
||||||
|
|
||||||
|
Либо насовсем открыть порт 80, тогда и `renew` точно будет работать.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ufw allow 80/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
Несколько полезных команд.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Получить сертификат для определённого домена. Предварительно нужно
|
||||||
|
# настроить конфиг nginx для этого домена.
|
||||||
|
sudo certbot --nginx -d example.com -d www.example.com
|
||||||
|
|
||||||
|
# Список сертификатов со сроками их жизни
|
||||||
|
sudo certbot certificates
|
||||||
|
|
||||||
|
# Удалить сертификат (команда предложит выбор)
|
||||||
|
sudo certbot delete
|
||||||
|
```
|
||||||
268
docs/servers/setup.md
Normal file
268
docs/servers/setup.md
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
# Начальная настройка сервера
|
||||||
|
|
||||||
|
## Первое подключение
|
||||||
|
Подключаемся к серверу к `root` по паролю, который должен выдаваться вместе с VDS.
|
||||||
|
```sh
|
||||||
|
ssh root@<IPv4>
|
||||||
|
```
|
||||||
|
|
||||||
|
Уже в терминале сервера выполняем.
|
||||||
|
```sh { .code-wrap }
|
||||||
|
# Создаём пользователя и наделяем правом использовать `sudo`.
|
||||||
|
adduser <user>
|
||||||
|
adduser <user> sudo
|
||||||
|
|
||||||
|
# Переключаемся на нового пользователя
|
||||||
|
su - <user>
|
||||||
|
|
||||||
|
# Все файлы, созданные пользователем, по умолчанию будут иметь права 600, а директории - 700.
|
||||||
|
echo 'umask 0077' >> .bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
|
||||||
|
# Добавляем свой публичный SSH ключ (cat ~/.ssh/id_rsa.pub), чтобы подключаться к пользователю по SSH напрямую.
|
||||||
|
mkdir .ssh
|
||||||
|
echo "<your-id-rsa.pub>" >> .ssh/authorized_keys
|
||||||
|
# Актуально, только если настройку umask не добавлять в .bashrc
|
||||||
|
# chmod 700 ~/.ssh
|
||||||
|
# chmod 600 ~/.ssh/authorized_keys
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь можно попробовать подключиться к серверу по SSH-ключу.
|
||||||
|
```sh
|
||||||
|
ssh <user>@<IPv4>
|
||||||
|
```
|
||||||
|
|
||||||
|
Желательно обновить все пакеты и перезагрузить сервер.
|
||||||
|
```sh
|
||||||
|
sudo apt update
|
||||||
|
sudo apt upgrade
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно придумать серверу имя, оно будет отображаться в терминале после `<user>@`.
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/hostname
|
||||||
|
sudo systemctl restart systemd-hostnamed
|
||||||
|
|
||||||
|
# В hosts тоже иногда есть строчка вида
|
||||||
|
# 127.0.1.1 <hostname>
|
||||||
|
sudo nano /etc/hosts
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Настройка конфига SSH
|
||||||
|
Открываем конфиг SSH.
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/ssh/sshd_config
|
||||||
|
```
|
||||||
|
|
||||||
|
- `Port <ssh-port>` - можно поменять со стандартного 22 на какой-нибудь другой. Лучше больше 10000, чтобы уменьшить вероятность конфликтов с другим ПО.
|
||||||
|
- `PermitRootLogin no` - запрещаем авторизацию по SSH под `root`.
|
||||||
|
- `PasswordAuthentication no` - запрещаем авторизацию по SSH по паролю.
|
||||||
|
|
||||||
|
После внесения изменений в конфиг, необходимо перезагрузить `sshd`.
|
||||||
|
```sh
|
||||||
|
# На некоторых системах ssh вместо sshd
|
||||||
|
sudo systemctl reload sshd
|
||||||
|
|
||||||
|
# Иногда дополнительно нужно выполнить
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart ssh.socket
|
||||||
|
```
|
||||||
|
|
||||||
|
??? question "А что будет, если потерять SSH-ключ?"
|
||||||
|
|
||||||
|
Хостинг предоставляет доступ к `VNC` или другие методы подключения к серверу, которые не требуют подключения по SSH. Однако в таком случае будет необходим доступ к личному кабинету хостинга.
|
||||||
|
|
||||||
|
|
||||||
|
На своей машине добавляем сервер в конфиг SSH.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# На Windows надо будет нажать на Tab, чтобы раскрыть `~`.
|
||||||
|
# code - VS Code
|
||||||
|
code ~/.ssh/config
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Host <host>
|
||||||
|
HostName <IPv4>
|
||||||
|
User <user>
|
||||||
|
Port <ssh-port>
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно проверить, что подключение проходит без ошибок.
|
||||||
|
```sh
|
||||||
|
# Если конфиг настроен
|
||||||
|
ssh <host>
|
||||||
|
```
|
||||||
|
```sh
|
||||||
|
# Без конфига
|
||||||
|
ssh <user>@<IPv4> -p <ssh-port>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Настройка фаерволла c UFW
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Установка UFW
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install ufw -y
|
||||||
|
|
||||||
|
# Открываем порт, используемый для SSH (по умолчанию 22)
|
||||||
|
sudo ufw allow <ssh-port>/tcp
|
||||||
|
|
||||||
|
# Закрываем все входные
|
||||||
|
sudo ufw default deny incoming
|
||||||
|
sudo ufw default allow outgoing
|
||||||
|
|
||||||
|
# Включаем фаерволл
|
||||||
|
sudo ufw enable
|
||||||
|
|
||||||
|
# Показать состояние ufw и активные правила
|
||||||
|
sudo ufw status verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
??? tip "Дополнительные команды `ufw`"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Отключить фаерволл
|
||||||
|
sudo ufw disable
|
||||||
|
|
||||||
|
# Удалить правило (будут применены настройки по умолчанию)
|
||||||
|
sudo ufw delete allow <port>/<protocol> # удалить разрешение
|
||||||
|
sudo ufw delete deny <port>/<protocol> # удалить запрет
|
||||||
|
|
||||||
|
# Сброс всех правил
|
||||||
|
sudo ufw reset
|
||||||
|
|
||||||
|
# Вывести логи ufw
|
||||||
|
sudo tail -f -n 100 /var/log/ufw.log
|
||||||
|
|
||||||
|
# Изменить уровень логирования
|
||||||
|
sudo ufw logging <low/medium/high>
|
||||||
|
|
||||||
|
# Разрешить доступ ко всем портам с определённого IP-адреса
|
||||||
|
sudo ufw allow from <IPv4>
|
||||||
|
|
||||||
|
# Разрешить доступ к порту с определённого IP-адреса
|
||||||
|
sudo ufw allow from <IPv4> to any port <port>
|
||||||
|
```
|
||||||
|
|
||||||
|
Некоторые приложения, например `OpenSSH` или `Nginx`, добавляют пресеты с правилами для `ufw`, которые точно так же можно разрешать и запрещать.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Вывести список пресетов
|
||||||
|
sudo ufw app list
|
||||||
|
|
||||||
|
# Открыть все соединения, которые нужны Nginx
|
||||||
|
sudo ufw allow "Nginx Full"
|
||||||
|
|
||||||
|
# Удалить правило для пресета
|
||||||
|
sudo ufw delete allow "Nginx Full"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Настройка Fail2Ban
|
||||||
|
|
||||||
|
[Fail2Ban](https://github.com/fail2ban/fail2ban) - базовая защита сервера от brute-force атак.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install fail2ban -y
|
||||||
|
sudo systemctl enable fail2ban
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь нужно [правильно настроить Fail2Ban](https://github.com/fail2ban/fail2ban/wiki/Proper-fail2ban-configuration).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Создаём файл с пользовательскими настройками
|
||||||
|
sudo nano /etc/fail2ban/jail.local
|
||||||
|
```
|
||||||
|
|
||||||
|
Настройка защиты SSH сервера.
|
||||||
|
```ini
|
||||||
|
[sshd]
|
||||||
|
# Единственный обязательный параметр
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
# Можно не указывать, если используется стандартный порт
|
||||||
|
port = <ssh-port>
|
||||||
|
|
||||||
|
# Пример настроек. Эти параметры можно не указывать, тогда будут использованы
|
||||||
|
# значения по умолчанию.
|
||||||
|
# Если в течении 24 часов
|
||||||
|
findtime = 86400
|
||||||
|
# произведено 3 неудачных попытки логина,
|
||||||
|
maxretry = 3
|
||||||
|
# то банить IP навсегда.
|
||||||
|
bantime = -1
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip "Более строгий конфиг для `fail2ban`"
|
||||||
|
|
||||||
|
Если на сервере настроена авторизация по ключу, можно смело использовать такой конфиг.
|
||||||
|
|
||||||
|
При авторизации по ключу, бан возможен только в случае указания неправильного имени пользователя, что исключено при использовании корректно настроенного SSH-конфига. В случае случайного бана всегда можно зайти на сервер через личный кабинет хостинга.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = <ssh-port>
|
||||||
|
# Если произведена хотя бы одна неудачная попытка логина,
|
||||||
|
maxretry = 1
|
||||||
|
# то банить IP навсегда.
|
||||||
|
bantime = -1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Применяем настройки
|
||||||
|
sudo fail2ban-client reload
|
||||||
|
```
|
||||||
|
|
||||||
|
После установки и первоначальной настройки `fail2ban` лучше перезагрузить сервер, иначе `fail2ban` может не заработать.
|
||||||
|
```sh
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
??? tip "Дополнительные команды `fail2ban`"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Вывести список активных jail's
|
||||||
|
sudo fail2ban-client status
|
||||||
|
|
||||||
|
# Вывести информацию по конкретному jail, в т. ч. список заблокированных IP
|
||||||
|
sudo fail2ban-client status <jail-name>
|
||||||
|
|
||||||
|
# Разблокировать IP
|
||||||
|
sudo fail2ban-client set <jail-name> unbanip <IP>
|
||||||
|
|
||||||
|
# Вывести логи fail2ban
|
||||||
|
sudo tail -f -n 100 /var/log/fail2ban.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Дополнительно
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Вывести записи о неудачных попытках входа в систему
|
||||||
|
sudo lastb | head -n 20
|
||||||
|
|
||||||
|
# Очистить записи о неудачных попытках входа в систему
|
||||||
|
sudo truncate -s 0 /var/log/btmp
|
||||||
|
|
||||||
|
# Показывает, кто в системе прямо сейчас
|
||||||
|
sudo w
|
||||||
|
|
||||||
|
# Логи попыток входа
|
||||||
|
sudo grep "Accepted password" /var/log/auth.log | tail -n 20
|
||||||
|
sudo grep "Failed password" /var/log/auth.log | tail -n 20
|
||||||
|
sudo grep "Invalid user" /var/log/auth.log | tail -n 20
|
||||||
|
|
||||||
|
# Очистить логи с попытками входа
|
||||||
|
sudo truncate -s 0 /var/log/auth.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
- [Initial Server Setup with Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04)
|
||||||
|
- [UFW Essentials: Common Firewall Rules and Commands](https://www.digitalocean.com/community/tutorials/ufw-essentials-common-firewall-rules-and-commands)
|
||||||
|
- [VPS cheatsheet](https://habr.com/ru/articles/756804/)
|
||||||
|
- [Fail2Ban](https://github.com/fail2ban/fail2ban)
|
||||||
49
docs/servers/ssh.md
Normal file
49
docs/servers/ssh.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Заметка по SSH
|
||||||
|
|
||||||
|
## SSH-agent
|
||||||
|
|
||||||
|
### Установка на Windows
|
||||||
|
|
||||||
|
`ssh-agent` является частью OpenSSH. Начиная с Windows 10, OpenSSH устанавливается вместе с системой, однако службу `ssh-agent` надо включить вручную. Для этого нужно запустить `Powershell` от имени администратора и выполнить несколько команд.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Set-Service -Name ssh-agent -StartupType Automatic
|
||||||
|
Start-Service ssh-agent
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить состояние `ssh-agent` можно с помощью команды.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-Service -Name ssh-agent | select -property status,name,starttype
|
||||||
|
```
|
||||||
|
|
||||||
|
На Windows `git` по умолчанию использует свою службу `ssh` вместо системной, поэтому `ssh-agent` не будет с ним работать и пароли всё равно придётся вводить вручную. Однако можно настроить `git` так, чтобы он использовал системный `ssh`.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Использование
|
||||||
|
|
||||||
|
!!! warning "Security warning"
|
||||||
|
|
||||||
|
На Windows доступ к ключам сохраняется даже после перезагрузки системы.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Добавляет ключи из ~/.ssh/
|
||||||
|
ssh-add
|
||||||
|
# Можно указать путь
|
||||||
|
ssh-add path/to/id_rsa
|
||||||
|
# Список добавленных ключей
|
||||||
|
ssh-add -l
|
||||||
|
# Удалить все ключи из памяти агента
|
||||||
|
ssh-add -D
|
||||||
|
```
|
||||||
|
|
||||||
|
Иногда нужно, чтобы при подключении на сервер, были доступны приватные ключи из локального `ssh-agent`. Для этого можно использовать команду `ssh -A`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh -A user@server
|
||||||
|
```
|
||||||
|
|
||||||
|
Либо указать `ForwardAgent yes` в конфиге `ssh`.
|
||||||
@@ -5,3 +5,17 @@
|
|||||||
.md-footer {
|
.md-footer {
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Достаточно добавить { .code-wrap } к блоку кода, чтобы включить автоперенос строк.
|
||||||
|
attr_list, очевидно, должен быть подключен в markdown_extensions.
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```sh { .code-wrap }
|
||||||
|
*very long line of code here*
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
.code-wrap code {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|||||||
11
mkdocs.yml
11
mkdocs.yml
@@ -1,4 +1,4 @@
|
|||||||
site_name: Tishenko's knowledge base
|
site_name: Tish Knowledge Base
|
||||||
site_url: https://kb.tishenko.dev
|
site_url: https://kb.tishenko.dev
|
||||||
copyright: Copyright © 2025
|
copyright: Copyright © 2025
|
||||||
repo_url: https://github.com/Arity-T/knowledge-base
|
repo_url: https://github.com/Arity-T/knowledge-base
|
||||||
@@ -7,12 +7,14 @@ edit_uri: edit/main/docs/
|
|||||||
theme:
|
theme:
|
||||||
name: material
|
name: material
|
||||||
language: ru
|
language: ru
|
||||||
|
logo: assets/logo.svg
|
||||||
favicon: assets/favicon.ico
|
favicon: assets/favicon.ico
|
||||||
|
|
||||||
icon:
|
icon:
|
||||||
repo: fontawesome/brands/github
|
repo: fontawesome/brands/github
|
||||||
|
|
||||||
features:
|
features:
|
||||||
|
- navigation.indexes
|
||||||
- navigation.instant
|
- navigation.instant
|
||||||
- navigation.tabs
|
- navigation.tabs
|
||||||
- content.code.copy
|
- content.code.copy
|
||||||
@@ -35,7 +37,7 @@ theme:
|
|||||||
icon: material/brightness-4
|
icon: material/brightness-4
|
||||||
name: Switch to light mode
|
name: Switch to light mode
|
||||||
extra:
|
extra:
|
||||||
generator: false
|
# generator: false
|
||||||
social:
|
social:
|
||||||
- icon: fontawesome/brands/github
|
- icon: fontawesome/brands/github
|
||||||
link: https://github.com/Arity-T
|
link: https://github.com/Arity-T
|
||||||
@@ -43,6 +45,11 @@ extra:
|
|||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- attr_list
|
- attr_list
|
||||||
- admonition
|
- admonition
|
||||||
|
- pymdownx.tabbed:
|
||||||
|
alternate_style: true
|
||||||
|
slugify: !!python/object/apply:pymdownx.slugs.slugify
|
||||||
|
kwds:
|
||||||
|
case: lower
|
||||||
- pymdownx.highlight:
|
- pymdownx.highlight:
|
||||||
anchor_linenums: true
|
anchor_linenums: true
|
||||||
line_spans: __span
|
line_spans: __span
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
mkdocs
|
mkdocs
|
||||||
|
pymdown-extensions
|
||||||
|
mkdocs-material
|
||||||
mkdocs-glightbox
|
mkdocs-glightbox
|
||||||
Reference in New Issue
Block a user