Linux

awk

Статья на хабре от ruvds
Статья на сайте ibm


Синтаксис

~$ awk OPTIONS PROGRAM FILE


Опции (основные)


Применение

По умолчанию, каждое слово заносится в переменную с именем $<номер слова в строке>, а через переменную $0 можно обратиться ко всей текущей строке, например $3 в текущей строке содержит значение каждое (под словом подразумевается поле выделенное из строки с помощью разделителя (по умолчанию пробел или табуляция))

Снимок-экрана-от-2019-05-27-10-29-03.png

Скрипты выглядят следующим образом: (точкой с запятой нужно разделять строки если они записаны в одну строку, а если нет то достаточно переноса строки) (чтобы выполнить какие-то инструкции до или после начала обработки текста, нужно добавить перед блоком такого кода соответственно слово BEGIN или END)

BEGIN {
  print "sosamba niga mazafaka"
}
{
  text = " has a  home directory at "
  print $1 text $6
}
END {
  print "konets sosamby"
}

Переменная $NF хранит последнее слово из строки

Снимок-экрана-от-2019-05-27-12-34-37.png

Переменные для обработки:

Пременные окружающей среды:

Например можно узнавать внутри скрипта об указанных ключах и значениях в команде или о домашей директории


Условный оператор

Однострочный вариант

if (УСЛОВИЕ) ДЕЙСТВИЕ

Обычный (если нужно выполниь несколько строк кода, то их нужно объединить фигурными скобками)

if (УСЛОВИЕ)
{
  ДЕЙСТВИЕ1
  ДЕЙСТВИЕ2
}

Снимок-экрана-от-2019-05-27-13-56-12.png


Циклы

Можно использовать оператор break и continue

while (УСЛОВИЕ)
{
  ДЕЙСТВИЕ1
  ДЕЙСТВИЕ2
}
for (ИНИЦИАЛИЗАЦИЯ_СЧЕТЧИКА; УСЛОВИЕ; ИНКРЕМЕНТАЦИЯ)	# как в С-подобных языках
{
  ДЕЙСТВИЕ1
  ДЕЙСТВИЕ2
}

Фарматированный вывод printf

Вывод настраивается через спецификаторы в формате %[modifier]control-letter
Список спецификаторов:

Пример:

printf "The result is: %e\n", x

Встроенные функции

Ссылка на gnu.org на страницу с функциями

Пользовательские функции

function ИМЯ_ФУНКЦИИ()
{
  ТЕЛО_ФУНКЦИИ
}

Цветной вывод в консоли

Escape-последовательность для указания цвета текста:

\x1b[...m

\x1b[ - начало
... - место для твоих параметров
m - конец

Коды и атрибуты перечисляются через ;
Пример:

[vandud@vps9104 ~]$ echo -e "\x1b[4;1;41;32mNigaMazafaka\nNigaMazafaka\x1b[0m"

Screenshot-from-2019-07-16-19-42-37.png

атрибут значение
0 нормальный режим
1 жирный
4 подчеркнутый
5 мигающий
7 инвертированные цвета
8 невидимый
код для фона код для текста цвет
40 30 черный
41 31 красный
42 32 зеленый
43 33 желтый
44 34 синий
45 35 пурпурный
46 36 голубой
47 37 белый

Последовательность \x1b[0m - Сбрасывает изменения к дефолтным

Screenshot-from-2019-07-16-19-59-16.png

Кракозябры в терминале


Пример, который был получен выводом командой cat файлов из директории /bin:

for i in `ls -1 /bin`; do cat /bin/$i; done

Такое происходит из-за управляющих последовательностей (escape-последовательностей), которые были посланы в терминал.

https://www.opennet.ru/man.shtml?topic=console_codes&category=4&russian=0

При выводе какого-либо бинарного файла, символы в нем могут образовывать такие последовательности. Из-за этого терминал меняет свое поведение и начинает показывать кракозябры.

Это можно починить перезайдя в оболочку (так как эти последовательности применяют изменения только в рамках текущей сессии).
Либо если возможности перезайти нет, можно выполнить команду reset, несмотря на то что в поле ввода команды будут кракозябры, эта команда выполнится и перезапустит оболочку (без перелогинивания).

Об этом написано тут http://www.linuxcookbook.ru/howto/Text-Terminal/Text-Terminal-14.html

OpenVPN in Docker container

https://www.digitalocean.com/community/tutorials/how-to-run-openvpn-in-a-docker-container-on-ubuntu-14-04

https://habr.com/ru/post/354632/

Копируем следующие строчки в созданный docker-compose.yml

version: '2'  
services:  
  openvpn:
    cap_add:
     - NET_ADMIN
    image: kylemanna/openvpn
    container_name: openvpn
    ports:
     - "1194:1194/udp"
    restart: always
    volumes:
     - /home/vandud/docker/openvpn/config:/etc/openvpn

Далее

docker-compose run --rm openvpn ovpn_genconfig -u udp://vandud.ru
docker-compose run --rm openvpn ovpn_initpki

Enter New CA Passphrase: niga

Common Name (eg: your user, host, or server name) [Easy-RSA CA]:vandudca

Потом дважды ввел пароль niga

[vandud@vps9104 openvpn]$ docker-compose up -d openvpn
Creating openvpn ... done

Создание серта

docker-compose run --rm openvpn easyrsa build-client-full vandud-phone nopass  

Ввести пароль niga

docker-compose run --rm openvpn ovpn_getclient vandud-phone > certificate.ovpn

В получившемся файле только сертификаты и ключи, но нет параметров подключения
Нужно добавить в начало файла следующее

dev tun
proto udp

remote 51.255.215.177

port 1194
client
resolv-retry infinite
tls-client
key-direction 1
auth SHA1
cipher BF-CBC

persist-key
persist-tun

verb 3

Все работает


Чтобы добавить в автозагрузку с systemd
Надо в /etc/openvpn/client.conf положить конфиг (.ovpn)
И добавить в автозагрузку systemctl enable openvpn@client.service

Вот тут написано как делать
https://www.smarthomebeginner.com/configure-openvpn-to-autostart-linux/


Fri Mar  5 04:17:57 2021 write to TUN/TAP : Invalid argument (code=22)

Возникла такая ошибка (сыпется подряд и ничего не грузится)
Клиент - старый дебиан, Сервер - новый докер-контейнер

Решилась так

openvpn --config zabbix-server-1.8.22-web.ovpn --comp-lzo

SSH Tunneling

https://habr.com/ru/post/435546/

Все очень просто.
Подключаемся по ssh к серверу или какому либо хосту (через который будем проксировать трафик) с добавлением ключа -D и вместе с ним указываем порт.

~$ ssh connect@vandud.ru -D 8888

Screenshot-from-2019-09-14-17-49-45.png

После этого нужно настроить подключение в приложении трафик которого нужно проксировать. Например firefox
В настройках подключения указываем localhost и указанный ранне порт 8888, все.

Screenshot-from-2019-09-14-17-52-33.png

Готово, трафик со всех приложений идет по-старому, кроме настроенного приложения (firefox)

Screenshot-from-2019-09-14-17-54-16.png
Screenshot-from-2019-09-14-17-54-54.png

~$ ssh -L 8888:127.0.0.1:80 root@45.8.228.69

Эта команда создает защищенный туннель между локальным хостом и удаленным (но только между определенными портами).
То есть при подключении к порту 8888 на локальной машине, запрос идет на 80 порт удаленной машины (конкретно в этом примере). Вместо 127.0.0.1 можно указать какой либо другой адрес, и запрос будет идти на него (от имени удаленной машины)
На скриншоте это видно (на удаленной машине установлен nginx и при запросе на localhost:80 отдается страница wp)

Screenshot-from-2019-09-14-18-37-13.png

Еще пример
Делаю вот такую команду

vandud@vandud-workstation:~$ ssh -L 8888:95.214.62.136:443 root@45.8.228.69

Она означает, что при подключении к localhost:8888 подключаться к 95.214.62.136:443 от имени 45.8.228.69. Это действительно работает.

Screenshot-from-2019-09-14-18-44-45.png

Можно также указать локальный адрес

vandud@vandud-workstation:~$ ssh -4 -L 0.0.0.0:8888:95.214.62.136:443 root@45.8.228.69

Получается то же самое

vandud@vandud-workstation:~$ openssl s_client -connect 0.0.0.0:8888 | grep "subject"
Can't use SSL_get_servername
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = alexgyver.ru
verify return:1
subject=CN = alexgyver.ru
^C

Но не работает когда указываешь белые адреса (не локальные)

С помощью обратного ssh-туннеля можно получать доступ к мошине за NAT
В качестве примера, сделаю это с рабочим компьютером (он за NAT) и VPS сервером (с него буду подключаться к рабочему компу).

На рабочем компе делаю следующее

vandud@vandud-workstation:~$ ssh root@45.8.228.69 -R localhost:222:localhost:22 -N
root@45.8.228.69's password: 

Конструкция -R localhost:222:localhost:22 означает, что при подключении на удаленной машине к localhost:222 подключаться к рабочему компьютеру на порт 22.

Ключ -N указывает, что не нужно запускать оболочку (после такого подключения работать с машиной по ssh не получится, так как нет приглашения -> некуда вводить команды).

Далее нужно с VPS ввести следующую команду

root@vandud-main-server:~# ssh vandud@localhost -p 222

Ввести пароль и все. Таким образом можно подключаться по ssh к рабочему компьютеру с удаленной VPS

Screenshot-from-2019-09-14-20-30-40.png

Memcached

Про команды в memcached

Запущенный memcached

root@vandud:/etc# systemctl status memcached
● memcached.service - memcached daemon
   Loaded: loaded (/lib/systemd/system/memcached.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2019-11-12 10:46:25 UTC; 9min ago
     Docs: man:memcached(1)
 Main PID: 9066 (memcached)
    Tasks: 10 (limit: 1073)
   CGroup: /system.slice/memcached.service
           └─9066 /usr/bin/memcached -m 64 -p 11211 -u memcache -l 127.0.0.1 -P /var/run/memcached/memcached.pid

Nov 12 10:46:25 vandud systemd[1]: Started memcached daemon.

Конфиг memcached'a

root@vandud:/etc# grep -vE '^#|^$' memcached.conf 
-d
logfile /var/log/memcached.log
-m 64
-p 11211
-u memcache
-l 127.0.0.1
-P /var/run/memcached/memcached.pid

Как видно, он берет данные из конфига и запускает с ними себя.
Для работы с memcached нужно подключиться к сервису на порт на котором он запущен (например через nc, можно также через telnet)


Запись данных

root@vandud:~# echo -e 'add TEST 0 0 3\r\n101\r' | nc localhost 11211
STORED
^C
root@vandud:~# echo -e 'get TEST\r' | nc localhost 11211
VALUE TEST 0 3
101
END

Пример запроса

echo -e 'add KEY 0 0 5\r\nVALUE\r' | nc localhost 11211

В нем указано, что нужно создать ключ KEY который имеет


Также нужно обратить внимание, что memcached сначала проверяет корректность данных (например указано что будет 5 байт данных, а передано 6, такой запрос завершится с ошибкой данных), а потом пытается их засунуть в ключ (то есть потом уже он смотрит на существование запрашиваемого ключа (на случай запроса к несуществующему ключу))


Замена значений

root@vandud:~# echo -e 'get KEY\r' | nc localhost 11211
VALUE KEY 0 5
TESTS
END
^C

root@vandud:~# echo -e 'set KEY 0 0 3\r\nkey\r' | nc localhost 11211
STORED
^C

root@vandud:~# echo -e 'get KEY\r' | nc localhost 11211
VALUE KEY 0 3
key
END

Удаление ключа со значением

root@vandud:~# echo -e 'delete KEY\r' | nc localhost 11211
DELETED
^C

root@vandud:~# echo -e 'get KEY\r' | nc localhost 11211
END
^C

Инкремен\Декремент (только если значение ключа числовое, иначе ошибка)

root@vandud:~# echo -e 'set KEY 0 0 3\r\n333\r' | nc localhost 11211
STORED
^C
root@vandud:~# echo -e 'get KEY\r' | nc localhost 11211
VALUE KEY 0 3
333
END
^C

root@vandud:~# echo -e 'incr KEY 333\r' | nc localhost 11211
666
^C
root@vandud:~# echo -e 'get KEY\r' | nc localhost 11211
VALUE KEY 0 3
666
END
^C

root@vandud:~# echo -e 'decr KEY 111\r' | nc localhost 11211
555
^C
root@vandud:~# echo -e 'get KEY\r' | nc localhost 11211
VALUE KEY 0 3
555
END

MongoDB

В качестве идентификатора используется поле _id (именно с подчеркиванием)


Для того чтобы войти в MongoShell, нужно ввести команду mongo, также можно сразу указать имя базы mongo mydb (это заменяет команду use mydb)


Для того чтобы импортировать свою базу нужно выполнить следующую команду

~# mongoimport -v --file=zips.json
2019-11-12T16:05:10.757+0000	no collection specified
2019-11-12T16:05:10.758+0000	using filename 'zips' as collection
2019-11-12T16:05:10.760+0000	filesize: 3182409 bytes
2019-11-12T16:05:10.761+0000	using fields: 
2019-11-12T16:05:10.781+0000	connected to: localhost
2019-11-12T16:05:10.782+0000	ns: test.zips
2019-11-12T16:05:10.782+0000	connected to node type: standalone
2019-11-12T16:05:10.784+0000	using write concern: w='1', j=false, fsync=false, wtimeout=0
2019-11-12T16:05:10.784+0000	using write concern: w='1', j=false, fsync=false, wtimeout=0
2019-11-12T16:05:11.758+0000	imported 29353 documents

Посмотреть список коллекций в базе

> use test
switched to db test
> show collections
zips
> 

Поиск по базе

> db.zips.find({'_id': '01001'})
{ "_id" : "01001", "city" : "AGAWAM", "loc" : [ -72.622739, 42.070206 ], "pop" : 15338, "state" : "MA" }
> 

Показать все документы в базе

> db.zips.find({})
{ "_id" : "01001", "city" : "AGAWAM", "loc" : [ -72.622739, 42.070206 ], "pop" : 15338, "state" : "MA" }
{ "_id" : "01002", "city" : "CUSHMAN", "loc" : [ -72.51565, 42.377017 ], "pop" : 36963, "state" : "MA" }
{ "_id" : "01005", "city" : "BARRE", "loc" : [ -72.108354, 42.409698 ], "pop" : 4546, "state" : "MA" }
{ "_id" : "01007", "city" : "BELCHERTOWN", "loc" : [ -72.410953, 42.275103 ], "pop" : 10579, "state" : "MA" }
{ "_id" : "01008", "city" : "BLANDFORD", "loc" : [ -72.936114, 42.182949 ], "pop" : 1240, "state" : "MA" }
{ "_id" : "01010", "city" : "BRIMFIELD", "loc" : [ -72.188455, 42.116543 ], "pop" : 3706, "state" : "MA" }
{ "_id" : "01011", "city" : "CHESTER", "loc" : [ -72.988761, 42.279421 ], "pop" : 1688, "state" : "MA" }
{ "_id" : "01012", "city" : "CHESTERFIELD", "loc" : [ -72.833309, 42.38167 ], "pop" : 177, "state" : "MA" }
{ "_id" : "01013", "city" : "CHICOPEE", "loc" : [ -72.607962, 42.162046 ], "pop" : 23396, "state" : "MA" }
{ "_id" : "01020", "city" : "CHICOPEE", "loc" : [ -72.576142, 42.176443 ], "pop" : 31495, "state" : "MA" }
{ "_id" : "01022", "city" : "WESTOVER AFB", "loc" : [ -72.558657, 42.196672 ], "pop" : 1764, "state" : "MA" }
{ "_id" : "01026", "city" : "CUMMINGTON", "loc" : [ -72.905767, 42.435296 ], "pop" : 1484, "state" : "MA" }
{ "_id" : "01027", "city" : "MOUNT TOM", "loc" : [ -72.679921, 42.264319 ], "pop" : 16864, "state" : "MA" }
{ "_id" : "01028", "city" : "EAST LONGMEADOW", "loc" : [ -72.505565, 42.067203 ], "pop" : 13367, "state" : "MA" }
{ "_id" : "01030", "city" : "FEEDING HILLS", "loc" : [ -72.675077, 42.07182 ], "pop" : 11985, "state" : "MA" }
{ "_id" : "01031", "city" : "GILBERTVILLE", "loc" : [ -72.198585, 42.332194 ], "pop" : 2385, "state" : "MA" }
{ "_id" : "01032", "city" : "GOSHEN", "loc" : [ -72.844092, 42.466234 ], "pop" : 122, "state" : "MA" }
{ "_id" : "01033", "city" : "GRANBY", "loc" : [ -72.520001, 42.255704 ], "pop" : 5526, "state" : "MA" }
{ "_id" : "01034", "city" : "TOLLAND", "loc" : [ -72.908793, 42.070234 ], "pop" : 1652, "state" : "MA" }
{ "_id" : "01035", "city" : "HADLEY", "loc" : [ -72.571499, 42.36062 ], "pop" : 4231, "state" : "MA" }
Type "it" for more
> 

Показывает порциями, чтобы показать еще нужно набрать it


Поиск документов по условиям через И

> db.zips.find({'city':'GOSHEN','state':'MA'})
{ "_id" : "01032", "city" : "GOSHEN", "loc" : [ -72.844092, 42.466234 ], "pop" : 122, "state" : "MA" }
> 

Через ИЛИ

> db.zips.find({$or: [{'city':'GOSHEN'},{'state':'MA'}]})
{ "_id" : "01001", "city" : "AGAWAM", "loc" : [ -72.622739, 42.070206 ], "pop" : 15338, "state" : "MA" }
{ "_id" : "01002", "city" : "CUSHMAN", "loc" : [ -72.51565, 42.377017 ], "pop" : 36963, "state" : "MA" }
{ "_id" : "01005", "city" : "BARRE", "loc" : [ -72.108354, 42.409698 ], "pop" : 4546, "state" : "MA" }
{ "_id" : "01007", "city" : "BELCHERTOWN", "loc" : [ -72.410953, 42.275103 ], "pop" : 10579, "state" : "MA" }
{ "_id" : "01008", "city" : "BLANDFORD", "loc" : [ -72.936114, 42.182949 ], "pop" : 1240, "state" : "MA" }
{ "_id" : "01010", "city" : "BRIMFIELD", "loc" : [ -72.188455, 42.116543 ], "pop" : 3706, "state" : "MA" }
{ "_id" : "01011", "city" : "CHESTER", "loc" : [ -72.988761, 42.279421 ], "pop" : 1688, "state" : "MA" }
{ "_id" : "01012", "city" : "CHESTERFIELD", "loc" : [ -72.833309, 42.38167 ], "pop" : 177, "state" : "MA" }
{ "_id" : "01013", "city" : "CHICOPEE", "loc" : [ -72.607962, 42.162046 ], "pop" : 23396, "state" : "MA" }
{ "_id" : "01020", "city" : "CHICOPEE", "loc" : [ -72.576142, 42.176443 ], "pop" : 31495, "state" : "MA" }
{ "_id" : "01022", "city" : "WESTOVER AFB", "loc" : [ -72.558657, 42.196672 ], "pop" : 1764, "state" : "MA" }
{ "_id" : "01026", "city" : "CUMMINGTON", "loc" : [ -72.905767, 42.435296 ], "pop" : 1484, "state" : "MA" }
{ "_id" : "01027", "city" : "MOUNT TOM", "loc" : [ -72.679921, 42.264319 ], "pop" : 16864, "state" : "MA" }
{ "_id" : "01028", "city" : "EAST LONGMEADOW", "loc" : [ -72.505565, 42.067203 ], "pop" : 13367, "state" : "MA" }
{ "_id" : "01030", "city" : "FEEDING HILLS", "loc" : [ -72.675077, 42.07182 ], "pop" : 11985, "state" : "MA" }
{ "_id" : "01031", "city" : "GILBERTVILLE", "loc" : [ -72.198585, 42.332194 ], "pop" : 2385, "state" : "MA" }
{ "_id" : "01032", "city" : "GOSHEN", "loc" : [ -72.844092, 42.466234 ], "pop" : 122, "state" : "MA" }
{ "_id" : "01033", "city" : "GRANBY", "loc" : [ -72.520001, 42.255704 ], "pop" : 5526, "state" : "MA" }
{ "_id" : "01034", "city" : "TOLLAND", "loc" : [ -72.908793, 42.070234 ], "pop" : 1652, "state" : "MA" }
{ "_id" : "01035", "city" : "HADLEY", "loc" : [ -72.571499, 42.36062 ], "pop" : 4231, "state" : "MA" }
Type "it" for more
> 

То есть используется оператор $or, далее в квадратных скобках идет список значений в фигурных скобках


Поиск документов, где одно поле равно либо одному значению либо другому

> db.zips.find({'city':{$in:['AGAWAM','CUSHMAN']}})
{ "_id" : "01001", "city" : "AGAWAM", "loc" : [ -72.622739, 42.070206 ], "pop" : 15338, "state" : "MA" }
{ "_id" : "01002", "city" : "CUSHMAN", "loc" : [ -72.51565, 42.377017 ], "pop" : 36963, "state" : "MA" }
{ "_id" : "72526", "city" : "CUSHMAN", "loc" : [ -91.776455, 35.869663 ], "pop" : 336, "state" : "AR" }
> 

Проецирование (что отображать, а что нет (при поиске))

Отобразить только айди и город

> db.zips.find({'city':{$in:['AGAWAM','CUSHMAN']}},{'_id':1,'city':1})
{ "_id" : "01001", "city" : "AGAWAM" }
{ "_id" : "01002", "city" : "CUSHMAN" }
{ "_id" : "72526", "city" : "CUSHMAN" }
> 

При этом если указать только city

> db.zips.find({'city':{$in:['AGAWAM','CUSHMAN']}},{'city':1})
{ "_id" : "01001", "city" : "AGAWAM" }
{ "_id" : "01002", "city" : "CUSHMAN" }
{ "_id" : "72526", "city" : "CUSHMAN" }
> 

То id все равно отображается (поэтому можно указать "не отображать id")

> db.zips.find({'city':{$in:['AGAWAM','CUSHMAN']}},{'city':1,'_id':0})
{ "city" : "AGAWAM" }
{ "city" : "CUSHMAN" }
{ "city" : "CUSHMAN" }
> 

Но это сработает только с id, потому что это особое поле, например следующий запрос будет с ошибкой (потому что при проецировании поля должны иметь одинаковые значения (1 или 0))

> db.zips.find({'city':{$in:['AGAWAM','CUSHMAN']}},{'city':1,'pop':0})
Error: error: {
	"ok" : 0,
	"errmsg" : "Projection cannot have a mix of inclusion and exclusion.",
	"code" : 2,
	"codeName" : "BadValue"
}
> 

Сортировка

> db.zips.find().sort({'_id':-1})
{ "_id" : "99950", "city" : "KETCHIKAN", "loc" : [ -133.18479, 55.942471 ], "pop" : 422, "state" : "AK" }
{ "_id" : "99929", "city" : "WRANGELL", "loc" : [ -132.352918, 56.433524 ], "pop" : 2573, "state" : "AK" }
{ "_id" : "99927", "city" : "POINT BAKER", "loc" : [ -133.376372, 56.307858 ], "pop" : 426, "state" : "AK" }
{ "_id" : "99926", "city" : "METLAKATLA", "loc" : [ -131.579001, 55.121491 ], "pop" : 1469, "state" : "AK" }
{ "_id" : "99925", "city" : "KLAWOCK", "loc" : [ -133.055503, 55.552611 ], "pop" : 851, "state" : "AK" }
{ "_id" : "99923", "city" : "HYDER", "loc" : [ -130.124915, 55.925867 ], "pop" : 116, "state" : "AK" }
{ "_id" : "99922", "city" : "HYDABURG", "loc" : [ -132.633175, 55.137406 ], "pop" : 891, "state" : "AK" }
{ "_id" : "99921", "city" : "CRAIG", "loc" : [ -133.117081, 55.47317 ], "pop" : 1398, "state" : "AK" }
{ "_id" : "99919", "city" : "THORNE BAY", "loc" : [ -132.513815, 55.66086 ], "pop" : 744, "state" : "AK" }
{ "_id" : "99901", "city" : "KETCHIKAN", "loc" : [ -131.683175, 55.372028 ], "pop" : 13886, "state" : "AK" }
{ "_id" : "99840", "city" : "SKAGWAY", "loc" : [ -135.301794, 59.468471 ], "pop" : 692, "state" : "AK" }
{ "_id" : "99835", "city" : "SITKA", "loc" : [ -135.316569, 57.051436 ], "pop" : 8638, "state" : "AK" }
{ "_id" : "99833", "city" : "PETERSBURG", "loc" : [ -133.160683, 56.827134 ], "pop" : 4253, "state" : "AK" }
{ "_id" : "99829", "city" : "HOONAH", "loc" : [ -135.558435, 58.032237 ], "pop" : 1670, "state" : "AK" }
{ "_id" : "99827", "city" : "HAINES", "loc" : [ -135.542032, 59.251886 ], "pop" : 2246, "state" : "AK" }
{ "_id" : "99826", "city" : "GUSTAVUS", "loc" : [ -135.761542, 58.42835 ], "pop" : 258, "state" : "AK" }
{ "_id" : "99824", "city" : "DOUGLAS", "loc" : [ -134.395041, 58.275597 ], "pop" : 1802, "state" : "AK" }
{ "_id" : "99820", "city" : "ANGOON", "loc" : [ -134.371052, 57.569832 ], "pop" : 1002, "state" : "AK" }
{ "_id" : "99801", "city" : "JUNEAU", "loc" : [ -134.529429, 58.362767 ], "pop" : 24947, "state" : "AK" }
{ "_id" : "99789", "city" : "NUIQSUT", "loc" : [ -150.997119, 70.192737 ], "pop" : 354, "state" : "AK" }
Type "it" for more
> 

У функции sort указывается поле по которому нужно сортировать, и указывается как сортировать (возрастание\убывание)(1-1 соотв.)


Подсчитать количество документов в выборке

> db.zips.find().sort({'_id':-1}).count()
29353
> 

Также есть функции skip(N) и limit(N), для того чтобы пропустить первые N документов и вывести только N первых документов в выборке


Вставить значение

> db.zips.insertOne({
... 'field1':'val1',
... 'field2':'val2'
... })
{
	"acknowledged" : true,
	"insertedId" : ObjectId("5dcae56343b4da469bb68a61")
}
> db.zips.find({'field1':'val1'})
{ "_id" : ObjectId("5dcae56343b4da469bb68a61"), "field1" : "val1", "field2" : "val2" }
> 

Замена значения

> db.zips.find({'_id':'01001'},{'state':1})
{ "_id" : "01001", "state" : "niga" }

> db.zips.updateOne({'_id':'01001'},{$set: {'state':'mazafaka'}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.zips.find({'_id':'01001'},{'state':1})
{ "_id" : "01001", "state" : "mazafaka" }
> 

Удаление документа

> db.zips.remove({'_id':'01001'})
WriteResult({ "nRemoved" : 1 })
> db.zips.find({'_id':'01001'})
> 

Информация о базе (размеры и пр.)

> db.stats()
{
	"db" : "test",
	"collections" : 1,
	"views" : 0,
	"objects" : 29353,
	"avgObjSize" : 94.50815930228596,
	"dataSize" : 2774098,
	"storageSize" : 1724416,
	"numExtents" : 0,
	"indexes" : 1,
	"indexSize" : 364544,
	"fsUsedSize" : 1849679872,
	"fsTotalSize" : 15426048000,
	"ok" : 1
}

SED

i.dudin@idudin-vb:~/Documents$ echo "test test niga mazafaka" | sed 's/test/azaza/'
azaza test niga mazafaka
i.dudin@idudin-vb:~/Documents$ echo "test test niga mazafaka" | sed 's/test/azaza/g'
azaza azaza niga mazafaka

Если применять команду к файлу (а не к stdin), то содержимое файла не изменяется (он просто берет текст из файла на обработку и результат выводит)

Чтобы выполнить несколько инструкций нужно использовать ключ -e

i.dudin@idudin-vb:~/Documents$ cat test
test1
niga
mazafaka
test
niga
niga
mazafaka
i.dudin@idudin-vb:~/Documents$ sed -e 's/test/niga/g; s/mazafaka/niga/g' test 
niga1
niga
niga
niga
niga
niga
niga

Ключ -f нужен для указания файла со скриптом

Кроме флага g у команды замены (s) есть и другие флаги, например можно указывать номер вхождения которое нужно заменить или записать вывод в файл (sed 's/test/new test/w filename' text)

Можно указывать диапазоны обрабатываемых строк

Во второй строке (если в ней есть вхождение) сделать замену

i.dudin@idudin-vb:~/Documents$ sed '3s/niga/1/' test 
test1
niga
mazafaka
test
niga
niga
mazafaka

Начиная с 5 и до конца сделать замену

i.dudin@idudin-vb:~/Documents$ sed '5,$s/niga/1/' test 
test1
niga
mazafaka
test
1
1
mazafaka

Первое значение не может быть больше второго при указании диапазона, также при указании диапазона не работает флаг g

Поиск подходящей строки по фильтру
Перед командой замены указывается фильтр
Пример: (если в строке встречается test1, то в этой строке заменить первое вхождение mazafaka на 000) (работает со всеми подходящими по фильтр строками)

i.dudin@idudin-vb:~/Documents$ cat newtest 
test1 niga test1 mazafaka test1
niga niga niga mazafaka niga
mazafaka niga mazafaka mazafaka mazafaka
test1 niga test mazafaka test
niga niga niga mazafaka niga
niga niga niga mazafaka niga
mazafaka niga mazafaka mazafaka mazafaka
i.dudin@idudin-vb:~/Documents$ sed '/test1/s/mazafaka/000/' newtest 
test1 niga test1 000 test1
niga niga niga mazafaka niga
mazafaka niga mazafaka mazafaka mazafaka
test1 niga test 000 test
niga niga niga mazafaka niga
niga niga niga mazafaka niga
mazafaka niga mazafaka mazafaka mazafaka
i.dudin@idudin-vb:~/Documents$ 

Чтобы удалить строку (строки) можно использовать команду d (нужно указывать номер удаляемой строки, если не указать то будут удалены все строки)

i.dudin@idudin-vb:~/Documents$ sed '1d' test
niga
mazafaka
test
niga
niga
mazafaka
i.dudin@idudin-vb:~/Documents$ sed '1,5d' test
niga
mazafaka

Можно удалять по фильтру

i.dudin@idudin-vb:~/Documents$ cat test
test1
niga
mazafaka
test
niga
niga
mazafaka
i.dudin@idudin-vb:~/Documents$ sed '/mazafaka/d' test
test1
niga
test
niga
niga

Можно указать диапазон строк по фильтру (указывается два фильтра через запятую и будут удалены все строки между этими фильтрами)

i.dudin@idudin-vb:~/Documents$ cat test
test1
niga
mazafaka
test22
niga
niga
mazafaka
test
test22
niga
mazafaka
aseohtu
aeou
aoeustnh

i.dudin@idudin-vb:~/Documents$ sed '/test22/,/test22/d' test
test1
niga
mazafaka
niga
mazafaka
aseohtu
aeou
aoeustnh

Ключ -i позволяет записать изменения в исходный файл (а не выводить на экран)

Чтобы вставить строку в поток можно использовать команду i или a
(по умолчанию она вставит текст после или до каждой входящей строки (см. пример))

i.dudin@idudin-vb:~/Documents$ cat test
test1
niga
mazafaka
niga
mazafaka
aseohtu
aeou
aoeustnh

i.dudin@idudin-vb:~/Documents$ sed -e 'i0000000' test
0000000
test1
0000000
niga
0000000
mazafaka
0000000
niga
0000000
mazafaka
0000000
aseohtu
0000000
aeou
0000000
aoeustnh
0000000

Вставить строку до указанной

i.dudin@idudin-vb:~/Documents$ cat test
test1
niga
mazafaka
i.dudin@idudin-vb:~/Documents$ sed -e '2i\00000' test
test1
00000
niga
mazafaka

Вставить строку перед строкой по фильтру

i.dudin@idudin-vb:~/Documents$ cat test
test1
niga
mazafaka
i.dudin@idudin-vb:~/Documents$ sed -e '/niga/i\00000' test
test1
00000
niga
mazafaka

Вставить в множество файлов (в начало или конец) определенную строку

i.dudin@idudin-vb:~/Documents$ ls
1   100  12  14  16  18  2   21  23  25  27  29  30  32  34  36  38  4   41  43  45  47  49  50  52  54  56  58  6   61  63  65  67  69  70  72  74  76  78  8   81  83  85  87  89  90  92  94  96  98
10  11   13  15  17  19  20  22  24  26  28  3   31  33  35  37  39  40  42  44  46  48  5   51  53  55  57  59  60  62  64  66  68  7   71  73  75  77  79  80  82  84  86  88  9   91  93  95  97  99
i.dudin@idudin-vb:~/Documents$ cat 1
testovaya stroka
1
$i
1
i.dudin@idudin-vb:~/Documents$ for var in `ls -1`; do sed -ie '$a'"$var nigerskaya stroka" $var; done
i.dudin@idudin-vb:~/Documents$ cat 1
testovaya stroka
1
$i
1
1 nigerskaya stroka
i.dudin@idudin-vb:~/Documents$ 

Заменить целую строку можно через команду c (так же как с командой d, если не указывать конкретные строки для обработки, заменит все входящие строки)

i.dudin@idudin-vb:~/Documents$ cat 11
testovaya stroka
11
$i
11
11 nigerskaya stroka
i.dudin@idudin-vb:~/Documents$ sed -e 'c\test test test' 11
test test test
test test test
test test test
test test test
test test test
i.dudin@idudin-vb:~/Documents$ 

Номер строки указывается перед командой

i.dudin@idudin-vb:~/Documents$ sed -ie '2c\test test test' 11
i.dudin@idudin-vb:~/Documents$ cat 11
testovaya stroka
test test test
$i
11
11 nigerskaya stroka

Можно воспользоваться фильтром для замены только в нужных строках

i.dudin@idudin-vb:~/Documents$ cat 11
testovaya stroka
test test test
$i
11
11 nigerskaya stroka
i.dudin@idudin-vb:~/Documents$ sed -ie '/11/c\test test test' 11
i.dudin@idudin-vb:~/Documents$ cat 11
testovaya stroka
test test test
$i
test test test
test test test
i.dudin@idudin-vb:~/Documents$ 

Замена символов

i.dudin@idudin-vb:~/Documents$ sed -ie 'y/e/1/' 11
i.dudin@idudin-vb:~/Documents$ cat 11
t1stovaya stroka
t1st t1st t1st
$i
t1st t1st t1st
t1st t1st t1st

Команда = показывает номер строки перед строкой

Ключ -n запрещает вывод потока (выводит только обработанные строки)

i.dudin@idudin-vb:~/Documents$ sed -ne '3p' 11
$i

iptables

Тоже хорошая статья (более короткая)
https://wiki.archlinux.org/index.php/Iptables_(Русский)
Большая и подробная статья на русском


rtfm-1
rtfm-2
rtfm-3
rtfm-4


Чтобы посмотреть текущие правила

iptables -nvL

Что бы сохранить правила в отдельный файл — используйте:

iptables-save > /root/iptables_bkp

Что бы восстановить из него:

iptables-restore < /root/iptables_bkp

Привилегированные порты - порты для использования которых нужно иметь права суперпользователя
Это порты с 0 по 1023

[vandud@desktop ~]$ nc -l -p 88
Error: Couldn't setup listening socket (err=-3) # не разрешило слушать привилегированный порт

[vandud@desktop ~]$ nc -l -p 1088               # прекрасно повисло на прослушивание порта
^C

При добавления правил нужно понимать что после рестарта они пропадут. Поэтому нужно сохранять их в /etc/iptables/iptables.rules


docker,ferm,iptables

При использовании ferm совместно с docker возникает проблема. Докер создает правила на ходу (при создании контейнеров), а ферм про них ничего не знает
Для решения этой проблемы можно использовать ferment (github.com/diefans/ferment)

Лучше использовать ferment-ng, так как в новых версия докера правила создаются иначе

Он ставится через pip и умеет генерировать ферм конфиг для докера

Достаточно в конец ferm.conf дописать

@include '/usr/local/bin/ferment docker config|';

Tables, chains, rules

Каждое правило состоит из критерия (набора условий), действия и счетчика пакетов попавших под это правило. Правило применяется к пакетам подходящим под все условия (критерия)

Каждая таблица служит конкретной цели. Таблицы составляют набор цепочек, которые содержат список правил, организованных в определенном порядке

Цепочки - это упорядоченные последовательности правил (их можно разделить на базовые и пользовательские)

Базовые цепочки создаются по-умолчанию, пользовательские цепочки создаются пользователем)

Принято, что имена базовых цепочек пишутся в верхнем регистре, а имена пользовательских в нижнем

Таблица - это совокупность базовых и пользовательских цепочек, объединенных общим фунуциональным назначением. Имена таблиц пишутся в нижнем регистре

2020-09-08-214650_1077x550_scrot.png

Таблицы:

Таблицы состоят из цепочек правил (правила в цепочках расположены в определенном порядке)

Например таблица по умолчанию filter содержит 3 встроенные цепочки:

А таблица nat содержит:

5 базовых цепочек:

Также можно создавать собственных цепочки


У всех цепочек есть стандартное правило (политика). Оно применяется к пакету, когда он прошел все остальные правила. То есть политика - это правило по умолчанию


Критерий (условие)

Критерий это логическое условие, которое определяет, соответствует ли пакет этому правилу. В одном правиле может быть несколько критериев (они неявно объединяются логическим AND). Если критерий не указан, то предполагается что правило действует для всех пакетов

Критерий можно инвертировать знаком ! перед ним (логическое НЕ)
Восклицательный знак нужно ставить перед названием критерия, а не между названием критерия и его значением (так стало в новых версиях):
--option ! this - is deprecated
! --option this - is actual

Универсальные критерии

Универсальные критерии применимы к пакетам независимо от протокола транспортного уровня и они не требуют подключения модулей через ключ -m

Критерии специфичные для протоколов

TCP

Можно указывать как диапазон - --sport 1:20

UDP
IPv4
ICMP

Критерии состояния соединения - ConnTrack

В netfilter также есть механизм определения состояний. Он позволяет определить какому состоянию пренадлежит пакет

ConnTrack позволяет делать stateful-фильтрацию

Для протоколов в которых есть понятие "соединение" (tcp), conntrack активно его использует, тесно взаимодействуя с базовой сетевой подсистемой ядра Linux. А в тех протоколах где соединений нет (udp) он вводит его искусственно

У conntrack есть разные модули, которые помогают ему анализировать пакеты и понимать разные факты о них. Например протокол FTP использует два соединения, conntrack это понимает и, проанализировав пакеты управляющего соединения, знает куда будет устанавливаться передающее

С помощью утилит conntrack и iptstate можно наблюдать таблицы состояний соединений
Примерно то же самое что и conntrack показывает cat /proc/net/nf_conntrack

Типы состояний (критерий --ctstate):

Типы статусов (критерий --ctstatus):


Существует также понятие «связанных соединений». Например, когда в ответ на UDP-пакет с нашего хоста удаленный хост отвечает ICMP-пакетом icmp-port-unreachable, формально этот ответ является отдельным соединением, так как использует совсем другой протокол. netfilter отслеживает подобные ситуации и присваивает таким соединениям статус «связанных» (RELATED), позволяя корректно пропускать их через фильтры фаервола.


Красными ромбами обозначен момент передачи пакета в механизм определения состояния
Синими кругами обозначены точки маршрутизации: направлен пакет локальному хосту или это проходящий пакет. На какой сетевой интерфейс нужно направить исходящий пакет, итд

2020-09-08-232019_1202x527_scrot.png

На схеме видно что и входящие и исходящие пакеты первым делом попадают в таблицу raw

Эта таблица предназначена для выполнения действий с пакетами до их обработки системой conntrack
В ней невозможно использовать критерии связанные с conntrack'ом (т.к. до него очередь еще не дошла)


Критерий conntrack предоставляет:

Дополнительные критерии

Дополнительные критерии можно подгружать с помощью параметра -m. Можно подгружать сразу несколько модулей в одной команде (в одном правиле), но нельзя смешивать параметры (критерии) этих модулей, после каждого модуля строго его параметры
Также допустимо использовать отрицание для каждого критерия (параметра) в отдельности:

-m conntrack ! --ctstate NEW

Справку по модулю можно получить так:

iptables -m название_критерия -h

Список критериев:


Если действия не указано, то правило будет работать как счетчик

Путь пакета по цепочкам и таблицам

Схема прохождения пакета:

2020-09-08-215602_1223x368_scrot.png

Чуть более удобная схема:

2020-09-08-232019_1202x527_scrot.png

Как видно, пакет вначале проходит по цепочкам PREROUTING в трех таблицах (raw, mangle, nat)
Важно понимать, что это три разные цепочки (с одинаковым именем) в трех разных таблицах

Далее, пройдя по трем цепочкам, пакет уходит в цепочки INPUT или FORWARD, таблиц mangle и filter

И т.д.

Не важно с какого интерфейса был получен пакет. В каждом случае он обрабатывается одинакого и проходит одинаковый путь по таблицами и цепочкам


Действие над пакетом указывается через ключ -j или --jump. В него передается либо стандартное действие, либо действие расширения, либо переход на пользовательскую цепочку
Примеры стандартных действий:

Примеры действий расширений:

Если выбрано стандартное действие, то участь пакета ясна (он допускается, отбрасывается, итд)

Если выбран переход на пользовательскую цепочку, то пакет проходит по всем правилам пользовательской цепочки и если ни одно правило из нее не подошло ему, то он возвращается на следующее после перехода правило (см. картинку)
table_subtraverse.jpg

А действия расширений могут вести себя и как переход на пользовательскую цепочку, и как стандартное действие (зависит от расширения)
Более правильными словами: действия расширений могут быть терминальными (как стандартные действия) или нетерминальными (как пользовательские цепочки)
Более подробно про расширения man iptables-extensions


Пользовательские цепочки не могут иметь политику. А также важно, что пакет попадает в пользовательскую цепочку из стандартной. И если ни одно правило из стандартной цепочки не подошло, по пакет просто возвращается в стандартную цепочку как показано на схеме выше


Также важно, если пакет отброшен действием DROP, то он отброшен окончательно. Но если пакет принят действием ACCEPT, то он принят на уровне этой таблицы и цепочки в ней. Он просто продолжает свой путь по следующим цепочкам (как указано на общей схеме)


Комментарии

Можно добавлять комментарии к правилам
Это делается через модуль comment и его параметр --comment (max-len 256 символов)
Пример:

iptables -A DOCKER-USER -p tcp --dport 77 -j DROP -m comment --comment "supper comment"

Результат:

Chain DOCKER-USER (1 references)
target     prot opt source               destination         
RETURN     all  --  anywhere             anywhere            
DROP       tcp  --  anywhere             anywhere             tcp dpt:77 /* supper comment */

Основные действия

Работа с цепочками




Работа с правилами

Действие (target)


Wi-Fi через терминал

В этой статье рассматривается еще один способ подключения к wi-fi (wpasupplicant)

Через утилиту nmcli (скорее всего нет по умолчанию)

Найти нужную wi-fi сеть и информацию о ней

Можно использовать утилиту nmcli

nmcli device wifi

Ключевое слово wifi означает тип девайса (можно увидеть в выводе команды nmcli device)
В выводе список wi-fi точек со следующими полями (пример вывода):

IN-USE	SSID	MODE	CHAN	RATE		SIGNAL	BARS	SECURITY
	iFree	Infra	52	270 Mbit/s	100	▂▄▆█	WPA1 WPA2 802.1X

Поле BARS это (насколько я понял) просто график поля SIGNAL, а значение в поле SIGNAL указано в процентах.

Подключиться к выбранной точке

Используя ту же утилиту nmcli

nmcli device wifi connect WiFi-Name password Password

После выполнения этой команды даже не нужно вызывать dhclient для получения параметров dhcp (они настраиваются автоматически)

Утилита iwctl/iwd

https://ctlos.github.io/wiki/packages/iwd/

Поднимаем беспроводной интерфейс (если не поднят)

[vandud@thinkpad ~]$ sudo ip link set wlp3s0 up

Подключение

iwctl station wlan0 scan
iwctl station wlan0 get-networks
iwctl station wlan0 connect COSMOPOLITAN

После команды connect будет запрошен пароль от точки доступа.

Далее нужно получить ip-адрес. Для этого достаточно выполнить команду dhclient


Для автоподключения к wi-fi нужно добавить сервис iwd в автозагрузку:

systemctl enable iwd

И добавить в файл /var/lib/iwd/NETWORK.psk такую директиву:

[Settings]
AutoConnect=true

Этот файл уже будет содержать пароль от этой сети (создается после первого подключения)

Для автоматического получения ip-адреса нужно добавить в /etc/iwd/main.conf вот такое:

[General]
EnableNetworkConfiguration=true

Теперь при перезагрузке сервер будет автоматически подключаться к wi-fi
(это нужно для автонастройки ip, по умолчанию dhclient не запускается)


Если указанный выше метод не сработал для dhcp, то можно сделать так:

cat /etc/systemd/network/20-dhcp.network
[Match]
Name=enp*

[Network]
DHCP=ipv4

И нужно сделать reload/enable/start для systemd-networkd.service

Трекпад на thinkpad x240

Очень полезная статья

Сначала нужно установить драйвер

# pacman -S xf86-input-synaptics

Далее нужно изменить параметры загрузки ядра. Происходит тайм-аут по бездействию устройства (трекпоинт или трекпад). Нужно добавить в /etc/default/grub следующие параметры

GRUB_CMDLINE_LINUX_DEFAULT="[уже присутствующие параметры] i8042.nomux=1 i8042.noloop=1 i8042.notimeout=1 i8042.reset=1"

Далее

sudo update-grub

И рестарт системы (чтобы она загрузилась уже с новыми параметрами)


После этих действий у меня стало работать (но вскоре проблема вернулась)


Документация про параметры ядра (искать по i8042)


Вот еще
https://www.linux.org.ru/forum/desktop/9066459

В итоге видимо проблема не на таком низком уровне как я подумал ранее.
Файл /etc/X11/xorg.conf.d/30-touchpad.conf привел к следующему виду:

Section "InputDevice"
    Identifier     "TouchPad"
    Driver         "synaptics"
EndSection

Перезагрузил ноут и команда synclient -l стала показывать вывод переменных драйвера synaptics (ранее драйвер не был задействован (возможно в этом была проблема))

Через драйвер synaptics можно настроить все (я был шокирован когда увидел количество различных параметров)
Утилита synclient позволяет менять настройки на лету (без перезагрузки X)

Чтобы настроить клики мыши по тапам (там по тачпаду) нужно выполнить следующую команду

synclient TapButton1=1 TapButton2=3 TapButton3=2

Эти клики можно настроить через утилиту synclient, либо указать в ранее упомянутом конфиге xorg.conf.d

Section "InputDevice"
    Identifier "touchpad"
    Driver "synaptics"
    Option "TapButton1" "1"
    Option "TapButton2" "3"
    Option "TapButton3" "2"
EndSection

Домашняя система [configs]

Список файлов и директорий, которые нужно бэкапировать:

Язык (раскладка + переключение)

Вот такая команда работает корректно, пока не определился куда именно ее поместить, в данный момент она находится в ~/.i3/config

exec setxkbmap -layout us,ru -variant dvorak, -option grp:win_space_toggle

exec - для выполнения через конфиг i3 (если надумаю разместить в каком-нибудь bash конфиге, то она не нужна)

Firefox

Директория с конфигами ~/.mozilla/firefox, проще копировать целую ~/.mozilla

При первом запуске, firefox создает в домашней папке эту директорию с дефолтными конфигами. Перед копированием своего конфига, старую директорию нужно удалить.

После копирования своей ~/.mozilla, firefox запускается сразу с нужными настройками, то есть кроме копирования конфигов, больше ничего делать не нужно.


С помощью плагина Firefox color создал тему. Плагин может сразу применять новую тему на лету, а также позволяет сохранить тему как архив.

Вся тема состоит из двух файлов:

  1. json-манифест
  2. Картинка (используется для фона)
├── images
│   └── bg-039-717a66ba4d455686088c80f391cb3264.svg
├── manifest.json

manifest.json:

{
  "manifest_version": 2,
  "version": "1.0",
  "name": "vandud-firefox-theme-manjaro-i3",
  "theme": {
    "images": {
      "additional_backgrounds": [
        "images/bg-039-717a66ba4d455686088c80f391cb3264.svg"
      ]
    },
    "properties": {
      "additional_backgrounds_alignment": [
        "top"
      ],
      "additional_backgrounds_tiling": [
        "repeat"
      ]
    },
    "colors": {
      "toolbar": "rgb(39, 65, 81)",
      "toolbar_text": "rgb(100, 152, 154)",
      "frame": "rgb(37, 57, 69)",
      "tab_background_text": "rgb(100, 152, 154)",
      "toolbar_field": "rgba(0, 0, 0, 0.2)",
      "toolbar_field_text": "rgb(100, 152, 154)",
      "tab_line": "rgb(28, 171, 176)",
      "popup": "rgb(49, 67, 78)",
      "popup_text": "rgb(100, 152, 154)",
      "tab_loading": "rgb(28, 171, 176)"
    }
  }

bg-039-717a66ba4d455686088c80f391cb3264.svg:

<svg width="144" height="96" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" opacity=".3"><path fill-opacity=".1" d="M0 9h180v24H0z"/><path fill-opacity=".2" d="M0-15h180V9H0zm0 96h180v24H0z"/><path fill-opacity=".1" d="M0 0h24v96H0zm48 0h24v96H48zm48 0h24v96H96z"/><path fill-opacity=".1" d="M0 57h180v24H0z"/><path fill-opacity=".2" d="M0 33h180v24H0z"/></g></svg>

Так и не понял как ее установить отдельно


Bash

Все конфиги раскиданы по хомяку


i3wm

IPsec tunnel [ubuntu - ubuntu]

https://www.gypthecat.com/ipsec-vpn-host-to-host-on-ubuntu-14-04-with-strongswan
По этой статье все заработало

deb-пакет

Вообще это просто архив типа ar (с сигнатурой !<arch>. [точка это на самом деле 0a - \n])

Буду разбирать на основе zabbix_1.8.22_amd64.deb

Deb_File_Structure.svg.png

Состоит из трех частей:

  1. debian-binary - текстовый файл содержащий версию deb
    Вот такой командой выделил из основного файла только текстовый файл debian-binary
root@test-slave:/tmp/test# dd if=zabbix_1.8.22-1_amd64.deb of=debian-binary skip=68 count=4 iflag=skip_bytes,count_bytes

Его содержимое

root@test-slave:/tmp/test# xxd debian-binary
0000000: 322e 300a                                2.0.

Как видно он содержит 2.0 (0a это \n)

  1. control.tar.gz - содержит информацию о deb-пакете и скрипты установки
    Выделил из основного файла так
root@test-slave:/tmp/test# dd if=zabbix_1.8.22-1_amd64.deb of=control.tar.gz skip=132 count=299 iflag=skip_bytes,count_bytes

Распаковка

root@test-slave:/tmp/test/control# tar -xzvf control.tar.gz
./
./control
./conffiles

Control содержит следующее

root@test-slave:/tmp/test/control# cat control
Package: zabbix
Priority: extra
Section: checkinstall
Installed-Size: 916
Maintainer: root@test-slave
Architecture: amd64
Version: 1.8.22-1
Provides: zabbix
Description: Zabbix Agent

Conffiles в моем случае был пустым

  1. data.tar.xz - содержит файлы для установки
    Выделил из основного файла так
dd if=zabbix_1.8.22-1_amd64.deb of=data.tar.xz skip=476 iflag=skip_bytes

Распаковал архив, получилось следующее

root@test-slave:/tmp/test/data# tree .
.
├── data.tar.xz
└── usr
  ├── local
  │   ├── bin
  │   │   ├── zabbix_get
  │   │   └── zabbix_sender
  │   ├── sbin
  │   │   ├── zabbix_agent
  │   │   └── zabbix_agentd
  │   └── share
  │       └── man
  │           ├── man1
  │           │   ├── zabbix_get.1.gz
  │           │   └── zabbix_sender.1.gz
  │           └── man8
  │               └── zabbix_agentd.8.gz
  └── share
      └── doc
          └── zabbix
              ├── AUTHORS
              ├── COPYING
              ├── CREDITS
              ├── ChangeLog
              ├── INSTALL
              ├── NEWS
              └── README

В архиве лежат файлы с абсолютными путями, поэтому они просто разархивируются относительно корня и все.

LUKS Cryptsetup

https://wiki.debian.org/ru/Crypt

Подключить диск

cryptsetup luksOpen /dev/sda2 sda2_crypt

Примонтиовать куда надо

mount /dev/mapper/sda2_crypt /mnt/data

Отмотировать

umount /mnt

Отлючить диск

cryptsetup luksClose sdb1_crypt

Для автомонтирования сделал так
В /etc/crypttab

sdb1_crypt	/dev/sdb1	none	luks

В /etc/fstab

/dev/mapper/sdb1_crypt /mnt/hdd_1tb ext4 defaults 0 0

При загрузке системы будет спрашиваться пароль от диска

Postgresql

https://postgrespro.ru/docs/postgresql/9.5/install-short

Установка

apt install postgresql-9.5

После установки бинарники постгреса хранятся тут: /usr/lib/postgresql/9.5/bin

Создание директории в которой будут храниться базы

mkdir -p /usr/local/pgsql/data

Можно выбрать любую иную директорию

Назначение соответствующих прав на эту директорию:

chown -R postgres:postgres /usr/local/pgsql/data

Создание нового кластера базы данных в созданной директории

sudo -u postgres /usr/lib/postgresql/9.5/bin/initdb -D /usr/local/pgsql/data/

Запуск постгреса

sudo -u postgres /usr/lib/postgresql/9.5/bin/pg_ctl -D /usr/local/pgsql/data/ -l /var/log/postgresql/postgresql-9.5-main.log start

Создание базы

sudo -u postgres /usr/lib/postgresql/9.5/bin/createdb zabbix

Подключение к консоли постгреса

sudo -u postgres psql
psql (9.5.19)
Type "help" for help.

postgres=#

Дамп большой базы по сети

При дампе большой базы может возникнуть проблема с нехваткой места для файла дампа на сервере с базой. Тогда можно дампить сразу на удаленное хранилище.

Можно на сервере с базой запустить вот такую команду (ее можно сделать еще проще, но в моем случае были вынужденные усложнения)

root@cm-db-pgsql-01x:/home/i.dudin_pro# pg_dump -U postgres notify -Fc -E UTF-8 | bzip2 | ssh i.dudin_pro@bk02.g02.i-free.ru -i /home/i.dudin_pro/.ssh/id_rsa "bunzip2 > /var/backups-remote/cm-db-pgsql-01x_UPEADM-9992/notify.2020-06-07.dump"; echo $?

Bash Tricks

Убрать префикс из имен файлов

Например есть куча файлов у которых в названии есть перфикс
В моем случае префикс это _frem_iptables.txt

...
vendors-highscoring-01x.g01.i-free.ru_frem_iptables.txt
vendors-msgpro-filterws-01.g01.i-free.ru_frem_iptables.txt
vendors-web-backend-01x.g01.i-free.ru_frem_iptables.txt
voiceassistant-vas01.aws-eu-west-1.i-free.ru_frem_iptables.txt
vrs02.g01.i-free.ru_frem_iptables.txt
web-backend-01x.g01.i-free.ru_frem_iptables.txt
web-backend-01y.g01.i-free.ru_frem_iptables.txt
web-backend-02x.g01.i-free.ru_frem_iptables.txt
web-backend-02y.g01.i-free.ru_frem_iptables.txt
web-srv2.pro.i-free.ru_frem_iptables.txt
web-srv3.pro.i-free.ru_frem_iptables.txt
wf-vas01.g01.i-free.ru_frem_iptables.txt
zabbix-proxy.l5.i-free.local_frem_iptables.txt
...

Делаю вот так:

for name in `ls -1`; do mv $name ${name%_frem_iptables.txt}; done

Более подробно это описано в части "Parameter Expansion" в man bash

sshpass и как выполнить команду через sudo

SSHPass

for servername in `cat /tmp/servernames-for-ferm-reload`; do sshpass -p'ПАРОЛЬ' ssh -tt i.dudin_pro@${servername} 'echo ПАРОЛЬ | sudo -S -s /bin/bash -c "/etc/init
.d/ferm reload"';echo $servername; done

<<<

В баше есть перенаправления потоков, и среди них есть штука их трех стрелок
Она добавляет перенос строки в конец переданного ей (но это не точно)

[vandud@desktop ~]$ cat < "zhopa"
bash: zhopa: No such file or directory
[vandud@desktop ~]$ cat << "zhopa"
> ^C
[vandud@desktop ~]$ cat <<< "zhopa"
zhopa
[vandud@desktop ~]$ 

Это можно использовать в командах которые ждут ввода пользователя
Например openssl s_client после вывода ожидает от пользователя "Q" и только после этого завершается

[vandud@desktop ~]$ openssl s_client -connect smsservisy.ru:443 2>/dev/null | openssl x509 -noout -subject
subject=OU = Domain Control Validated, OU = Gandi Standard Wildcard SSL, CN = *.i-free.com
Q # ввел руками и нажал enter

А если передать туда "Q" через <<<, то работает само

[vandud@desktop ~]$ openssl s_client -connect smsservisy.ru:443 2>/dev/null <<< "Q" | openssl x509 -noout -subject
subject=OU = Domain Control Validated, OU = Gandi Standard Wildcard SSL, CN = *.i-free.com
[vandud@desktop ~]$ 

А еще в случае с openssl можно сделать так:

echo | openssl s_client aoseuhosaetuaoue

Сжатие/поворот jpeg

Сжатие

Утилита jpegoptim
Пример использования

[vandud@thinkpad Downloads]$ jpegoptim --size=250K IMG_0892.jpg 
IMG_0892.jpg 3421x1460 24bit N Exif IPTC ICC JFIF  [OK] 1060271 --> 253677 bytes (76.07%), optimized.

Размер можно не указывать, тогда будет оптимизация без потерь качества, но сжатие совсем не значительное (около 1-2 процентов).

Поворот

Утилита convert

convert [input-options] input-file [output-options] output-file

Пример:

convert IMG_0191.jpg -rotate -90 IMG_0191r.jpg

Крутит по часовой стрелке

heic -> jpg

$ heif-convert IMG_0583.heic recomendations.jpg

LA

https://firstvds.ru/technology/nagruzka-na-server-opredelenie-prichin

В выводе top есть строка CPU:

Командой atop -l -c -d1 можно посмотреть кто грузит диск

VIM

Как установить плагин в vim

curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

Эта команда добавит менеджер плагинов.

Далее в .vimrc добавляем плагины вот так:

set nu
set expandtab
set tabstop=2
set incsearch
syntax on

call plug#begin('~/.vim/plugged')
Plug 'stephpy/vim-yaml'
Plug 'preservim/nerdtree'
Plug 'morhetz/gruvbox'
call plug#end()

colorscheme gruvbox
set background=dark

map <C-n> :NERDTreeToggle<CR>

Строки с call нужны обязательно, между ними и указываются плагины.

И в конце чтобы новые плагины установились нужно:

  1. Подгрузить конфиг файл :source % или :source PATH_TO_vimrc (если конфиг был изменен вимом же только что), либо перезайти в вим.
  2. Выполнить :PluginInstall

Два монитора

xrandr --auto --output DP2 --mode 1366x768 --left-of eDP1

Внешний справа:

xrandr --output eDP1 --auto --pos 0x312 --output DP2 --auto --pos 1366x0

Внешний сверху:

xrandr --output eDP1 --auto --pos 0x1080 --output DP2 --auto --pos -276x0

CAPS -> ESC

setxkbmap -option "caps:swapescape"

Что это было перманентным, можно засунуть в .bashrc

Bluetooth

Запустите интерактивную команду bluetoothctl. После этого можно ввести help для получения списка доступных команд:

# bluetoothctl
[bluetooth]# agent KeyboardOnly
[bluetooth]# default-agent
[bluetooth]# power on
[bluetooth]# scan on
[bluetooth]# pair 00:12:34:56:78:90
[bluetooth]# connect 00:12:34:56:78:90
[bluetooth]# trust  00:12:34:56:78:90

Чтобы устройство было активно после перезагрузки, необходимо включить автовключение bluetooth:

/etc/bluetooth/main.conf

[Policy]
AutoEnable=true

Firefox error: NS_ERROR_FILE_CORRUPTED

С такой ошибкой сайт не загружается корректно (не подгружается часть страницы). Я выполнил в консоли браузера такое:

localStorage.clear()
sessionStorage.clear()

И стало работать как надо.

DNS NetworkManager

По умолчанию NetworkManager сам настраивает DNS и у него есть конфиги где можно указать как именно ему это нужно делать.
Но мне было проще отключить автонастройку и делать это через /etc/resolv.conf

Для этого нужно в /etc/NetworkManager/NetworkManager.conf внести:

[main]
dns=none

И все, теперь после перезагрузки NM не будет затирать мои изменения в resolv.conf

Сколько ресурсов "продано" на хостере

Виртуализация qemu/kvm. Команда ниже посчитает проданные ядра.

SUM=0;for i in `sudo grep vcpu /etc/libvirt/qemu/autostart/* | grep -o ">[[:digit:]]<" | grep -o "[[:digit:]]"`; do let SUM+=$i; done; echo $SUM

Vagrant

Инициализация

Чтобы инициализировать виртуалку, нужно ввести команду:

vagrant init ubuntu/xenial64

Она создаст файл Vagrantfile в текущей дериктории. В этом файле содержится конфигурация (на ruby). А также после этой команды будет скачан образ указанной системы с серверов vagrant

Примерно по такому пути находится скачанный образ виртуалки: /home/vandud/.vagrant.d/boxes/ubuntu-VAGRANTSLASH-xenial64/20200617.0.0/virtualbox

Вот тут можно выбрать подходящий образ

В конфиге можно редактировать параметры виртуалки (добавить памяти и прочее)

Старт выполняется командой vagrant up

Для подключения к виртуалке нужно использовать команду vagrant ssh (виртуалка слушает порт для ssh на хостовой машине, но там по-умолчанию доступ только по ключу, который есть у vagrant'a)

Традиционно в вагранте пользователь по-умолчанию - vagrant (пароль такой же как имя)

Директория из которой запускалась виртуалка (в которой лежит Vagrantfile) прокидывается в виртуалку по пути /vagrant (доступ и на чтение и на запись, поэтому нужно быть аккуратным)

[vandud@desktop vagrant]$ touch testfile

[vandud@desktop vagrant]$ vagrant ssh

vagrant@ubuntu-xenial:~$ cd /vagrant/

vagrant@ubuntu-xenial:/vagrant$ ll
total 60
drwxr-xr-x  1 vagrant vagrant  4096 Jun 23 15:01 ./
drwxr-xr-x 24 root    root     4096 Jun 23 14:53 ../
-rw-r--r--  1 vagrant vagrant     0 Jun 23 15:01 testfile
-rw-------  1 vagrant vagrant 42191 Jun 23 14:53 ubuntu-xenial-16.04-cloudimg-console.log
drwxr-xr-x  1 vagrant vagrant  4096 Jun 23 14:32 .vagrant/
-rw-r--r--  1 vagrant vagrant  3010 Jun 23 15:01 Vagrantfile

Команды управления

$ vagrant box list
ubuntu/xenial64 (virtualbox, 20200617.0.0)

Приватный ключ для подклюлчения по ssh находится примерно по такому пути: /home/vandud/Documents/vagrant/.vagrant/machines/default/virtualbox/private_key

Плагины

vagrant scp

Плагины устанавливаются так (устанавливаю vagrant-scp):

$ vagrant plugin install vagrant-scp
Installing the 'vagrant-scp' plugin. This can take a few minutes...
Fetching vagrant-scp-0.5.7.gem
Installed the plugin 'vagrant-scp (0.5.7)'!

Работа vagrant scp:

$ vagrant scp testfile default:/tmp/testfile
load pubkey "/home/vandud/Documents/vagrant/.vagrant/machines/default/virtualbox/private_key": invalid format
Warning: Permanently added '[127.0.0.1]:2222' (ECDSA) to the list of known hosts.
testfile                                                                       100%    0     0.0KB/s   00:00

Информацию о боксе (имя или id) можно получить из $ vagrant global-status

vagrant sahara

vagrant sandbox (sahara-plugin):
Этот плагин позволяет делать снапшоты. Пример использования:

Provisioning (подготовка)

Для провижининга (подготовки) можно использовать встроенные средства, или такие системы как ansible/puppet

На примере ansible:

Прокидывание директорий

В Vagrantfile нужно дописать следующее:

config.vm.synced_folder "src/", "/tmp/src/"

Эта настройка указывает откуда и куда примонтировать директорию
Далее нужно сделать reload и настройка применится

Результат (сверху команда watch tree на виртуалке):
2020-06-23-194725_710x299_scrot.png

Прокидывание портов

config.vm.network "forwarded_port", guest: 80, host: 8080

2020-06-23-203047_1028x433_scrot.png

Vagrant

CLI

Packer

https://www.packer.io/docs
https://rtfm.co.ua/packer-vvedenie-primery/

Терминология

CLI

Packer имеет очень человеко-читаемый вывод. Но он также имеет и машино-читаемый режим.
Его можно включить флагом -machine-readable для любой команды и не важно в каком порядке, команда флаг или флаг команда, главное чтобы он присутствовал.


Шаблоны

Шаблоны это json-файлы которые конфигурируют разные компоненты packer'а и описывают создание одного или более образов ВМ
Компоненты шаблона:

Так как JSON не предоставляет комментарии, то чтобы оставить комментарий в шаблоне packer нужно добавить корневой элемент, имя которого начинается с подчеркивания. Пример:

{
 "_super-comment": "My cool comment", 
 "variables": {
   "aws_access_key": "",
   "aws_secret_key": ""
 ...

Только такие элементы могут быть комментариями. Другие будут вызывать ошибки при валидации

builders

Билдер (сборщик) - это объект, который определяет параметры сборки
Выглядит так:

{
  "builders": [
    {
      "type": "amazon-ebs",
      "access_key": "...",
      "secret_key": "...",
      // еще куча разных аттрибутов
    }
  ]
}

Поле type является обязательным и по умолчанию является именем по которому можно обратиться к билдеру. Имя используется в выводе при сборке, чтобы было видно что сейчас происходит. Если просто типа билдера не достаточно, то можно и явно указать имя билдера через аттрибут name

communicators

Коммуникаторы это механизм используемый packer'ом для загрузки файлов, запуска скриптов и прочего провижининга запущенной машины. Каждый билд ассоциируется с каким-то коммуникатором для связи, обычно это SSH. Коммуникатор конфигурируется в секции builders и может быть трех видов (это значения ключа communicator):

Аттрибут pause_before_connecting позволяет указать паузу перед подключением к собранной машине. На самом деле это время ожидания перед повторным подключением после первого (тестового) успешного подключения.

template engine

В шаблонах используются следующие постановления:

Функции

Имеется множество разных функций, которые могут быть использованы в шаблоне

post-processors

Секция пост-процессинга в шаблоне определяет какие дополнительные действия должны сделаны билдерами при сборке образа. Например сжатие образа или загрузка его куда-либо

Каждый описанный пост-процессор будет выполнен для каждого описанного билдера. То есть при двух билдерах и одном пост-процессоре, пост-процессор будет выполнен дважды (по одному разу для каждого билдера)

Есть три варианта описания пост-процессора: простой, детализированный и последовательный. Можно представлять, что простой и детализированный варианты это ссылки на последовательный вариант

Чтобы сохранять промежуточные артефакты во время пост-процессинга нужно указать true в аттрибуте keep_input_artifact для нужного пост-проссинга

Провижининг

{
  "provisioners": [
    // ... one or more provisioner definitions here
  ]
}

Провизоры будут запускаться для каждой сборки в том порядке в котором они определены в шаблоне

В определение провизора должен содержаться аттрибут type, оно определяет имя используемого провизора. У разных провизоров разные обязательные поля, например провизор shell требует аттрибута shell

Можно использовать аттрибуты only или except в объекте провизора, чтобы избрать нужные билды. Они принимают в себя список имен билдов (не типов билдеров, нужно указать билдам имена и использовать их тут)

Наравне с обычными провизорами, можно указать провижининг для обработки ошибок обычного провизора. Он сработает при возникновении ошибки в работе провизора

Можно делать переопределения в провизорах. Это может быть нужно когда мы хотим чтобы создаваемые машины отличались

{
  "type": "shell",
  "script": "script.sh",
  "override": {
    "vmware-iso": {
      "execute_command": "echo 'password' | sudo -S bash {{.Path}}"
    }
  }
}

Иногда нужно делать паузу при сборке. Например когда ваш скрипт в провизоре делает ребут, тогда нужно подождать некоторое время перед запуском следующего провизора
Это можно сделать с помощью аттрибута pause_before, он указывается в провизоре который идет после перезагрузки и принимает время в секундах

max_retries - определяет количество повторных попыток после первой неудачной

timeout - это таймаут на выполнение провизора прежде чем он будет считаться зафейленым

Пользовательские переменные

Переменные позволяют шаблонам быть параметризированными и не хранить в шаблонах секретные данные (например токены)

Переменные объявляются в секции variables, а также могут быть переданны через файл или флаг -var

Если переменная будет передваться как аргумент, то лучше ее объявить пустой в шаблоне. Тогда будет понятно, что эта переменная будет заменена

Например тут мы сразу видим какие переменные нам нужно передать аргументами

{
  "variables": {
    "aws_access_key": "",
    "aws_secret_key": ""
  },

  "builders": [
    {
      "type": "amazon-ebs",
      "access_key": "{{user `aws_access_key`}}",
      "secret_key": "{{user `aws_secret_key`}}"
      // ...
    }
  ]
}

Устанавливать переменные можно из файла или через командную строку

Через командную строку это делается через ключ -var которому передается строка в формате "var_name=var_value". Ключи -var может быть передан сколько угодна раз (в зависимости от того как много у вас переменных). Переменные из командной строки переопределяют любые ранее заданные переменные

Чтобы определить переменные в отдельном файле, нужно в нем описать объект variables из шаблона:

{
  "aws_access_key": "foo",
  "aws_secret_key": "bar"
}

Переменные из файла с переменными указанного в конце переопределят предыдущие
То же и с комбинированием -var-file и -var, то что указано позже - важнее. Не важно файл это или параметр команды

Существует секция sensitive-variables, в ней можно указывать чувствительные переменные, которые не должны быть видно в журналах и прочих местах (например это токены)

{
  "variables": {
    "my_secret": "{{env `MY_SECRET`}}",
    "not_a_secret": "plaintext",
    "foo": "bar"
  },

  "sensitive-variables": ["my_secret", "foo"],
}

На работу приложения это никак не влияет, влияет только на вывод и журналы

Коммуникаторы

Коммуникаторы это механизм для загрузки файлов, выполнения скриптов и прочих действий в созданной машине. Коммуникаторы конфигурируются в секции builders

Поддерживаются три вида коммуникаторов:

Иногда ssh может быть не сконфигурирован в дефолтном образе, тогда нужно использовать файл preseed или kickstart для предконфигурирования

SSH коммуникотор имеет огромное количество опций, которые можно посмотреть здесь https://www.packer.io/docs/communicators/ssh

Builders

Билдеры отвечают за создание машины и генерацию образа из нее для различных платформ. Packer может работать с различными билдерами по умолчанию и может быть расширен для добавления новых билдеров

По умолчанию в packer доступно огромное количество билдеров для огромного количества платформ. Опишу здесь лишь то что интересно мне:

docker

Docker билдер собирает образы для докера. Он стартует докер контейнер, запускает провижининг внутри этого контейнера и экспортирует контейнер в образ

Провижининг в этом случае происходит без использования Dockerfile, это происходит так же как если бы вы настраивали железный или виртуальный сервер

Для работы docker-builder'а требуется docker engine

Обязательные опции:

Также есть еще миллион опциональных параметров
https://www.packer.io/docs/builders/docker

Packer умеет добавлять к образу различную информацию (тэги, и прочее) и импортировать его сразу в репозиторий образов

file

file-builder - то не настоящий билдер, он просто создает артефакты из файла и может быть использован для отладки пост-процессоров без длительного ожидания

null

Это тоже не настоящий билдер. Он просто настраивает ssh-подключение и запускает провижининг. Полезно для дебагинга провизоров так как занимает мало времени

Vagrant

Обязательные:

Две эти ^ опции - взаимоисключающие, возможна только одна из них

Остальные опции:

Рекомендуется проверять чексумму, потому что большие файлы могут быть легко повреждены при загрузке

Эквиваленты можно посмотреть здесь https://www.vagrantup.com/docs/cli/box.html#box-add

virtualbox

VirtualBox builder может создавать ВМ и экспортировать их в OVA/OVF формат

provisioners

Провизоры это встроенное и стороннее ПО для конфигурирования и установки машины после загрузки
Обычно они устанавливают пакеты, патчат ядро, создают юзеров, загружают код приложений

ansible-local

В машину будет закинут плейбук и запущен ансибл в локальном режиме (то есть виртуалка сама себя сконфигурирует [без ssh])

{
  "type": "ansible-local",
  "playbook_file": "local.yml"
}

ansible-remote

{
  "type": "ansible",
  "playbook_file": "./playbook.yml"
}

file

В машину будет загружен файл

Для загрузки файла будет использовать пользователь для провижининга (обычно это не рут), поэтому рекомендуется загрузить таким образом файл куда-нибудь, а потом уже с помощью модуля shell делать с ним что надо (установить права, положить в нужно место и тд)

{
  "type": "file",
  "source": "app.tar.gz",
  "destination": "/tmp/app.tar.gz"
}

shell

{
  "type": "shell",
  "inline": ["echo foo"]
}

Вместо инлайн можно указывать скрипты (которые там уже лежат)

post-proccessors

Пост-процессоры запускаются после сборки и провижининга
Могут загружать собранный образ куда-то (в облако), сжимать, или что угодно еще

BIND9

BIND - Berkeley Internet Name Daemon

bind имеет утилиты для проверки конфига и зоны:

rndc - утилита для управления биндом (remote name daemon control)


Зонный трансфер - один из механизмов репликации баз DNS между серверами. Бывает полным (AXFR) и инкрементальным (IXFR)

Трансфер происходит по TCP в отличие от обычных DNS-запросов


Результат работы команды dig выводится в формате зонного файла


Настройки корневой зоны всегда доступны по адресу ftp://ftp.internic.net/domain/named.root


Комментарии в зонном файле вводятся с помощью ;


В зонном файле можно указать переменные ORIGIN и TTL и не указывать для каждой записи TTL и ORIGIN (ориджин можно итак не указывать, но имея переменную это выглядит более очевидно)

$ORIGIN	testzone.vandud.ru.
$TTL	3600

@	IN	SOA	(
	phobos.vandud.ru.
	dns-admin.dns
	2020082011
	7200
	1200
	64800
	3600
)
@	IN	NS	phobos.vandud.ru.
web	IN	TXT	"sosamba; nstshsnthst pidor"
test1	IN	CNAME	test2
test2	IN	CNAME	test1

Glue records

Итак, есть существующая зона minsk.by. Мы подаем заявку на делегирование домена 3-го уровня nestor.minsk.by. В качестве неймсерверов будут выступать хосты с именами ns1.nestor.minsk.by и ns2.nestor.minsk.by. Чтобы все заработало, администратор зоны minsk.by должен прописать у себя примерно следующее:

$ORIGIN = minsk.by
nestor IN NS ns1.nestor
nestor IN NS ns2.nestor
ns1.nestor IN A 194.226.121.90
ns2.nestor IN A 194.158.200.188

Последние две записи и есть glue records, без которых запросы будут зацикливаться

Личный пример

root@phobos:/var/named/zones# cat testzone.vandud.ru 
...
testns	3600	IN 	NS	testns.testzone.vandud.ru.
testns	3600	IN	A	185.176.25.117 <- без этой записи BIND не заведется

И на вот такой запрос получаем ответ:

$ dig ns testns.testzone.vandud.ru @phobos.vandud.ru.
...
;; QUESTION SECTION:
;testns.testzone.vandud.ru.	IN	NS

;; AUTHORITY SECTION:
testns.testzone.vandud.ru. 3600	IN	NS	testns.testzone.vandud.ru.

;; ADDITIONAL SECTION:
testns.testzone.vandud.ru. 3600	IN	A	185.176.25.117

2020-08-09-233731_2023x757_scrot.png

Как видно, на запрос в котором запрашивается только NS, сервер также подсовывает нам и additional records, в которых указан адрес сервера из NS записи, потому что иначе его получить нельзя

Простой конфиг

# cat /etc/bind/named.conf <- положение этого файла зависит от того как собран bind  
# (может быть /etc/named.conf, а может быть /etc/bind/named.conf;  
# посмотреть как он собран можно через named -V
options {
  directory "/var/named";
};

zone testzone.vandud.ru. { <- имя зоны за которую отвечает файл из директивы file
  type master;
  file "zones/testzone.vandud.ru";
};

В /var/named/zones/testzone.vandud.ru имена могут быть абсолютными (с точкой на конце) или относительными, тогда к ним будет добавляться зона
Примеры:

Чтобы запись SOA была более читаемой (ведь она очень длинная) можно разбить ее на несколько строк
Для этого нужно использовать круглые скобки

@	3600	IN	SOA	(
	phobos.vandud.ru.
	dns-admin.dns
	2020082002
	7200
	1200
	64800
	3600
)
@	3600	IN	NS	phobos.vandud.ru.
web	3600	IN	A	8.8.8.8

Вторичный DNS-сервер

# cat /etc/bind/named.conf
options {directory "/var/named";};

zone testzone.vandud.ru. {
  type slave;
  file "zones/testzone.vandud.ru";
  masters { 185.176.25.117; }; <- хз почему, но домен указать нельзя
};

Далее запускаем сервер убедившись что все окей с правами на директории и видим что файл зоны подгрузился в /var/named/zones/testzone.vandud.ru

При настройке вторичного сервера нужно учитывать права на файлы и директории в системе. Нужно чтобы процессу named была доступна запись в указанные в конфигах директории, чтобы он мог записывать туда скачанные с первичного сервера зоны
Также можно использовать setuid/setgid для дополнительной безопасности


Кэширующий DNS-сервер

Базовый конфиг кэширующего сервера

options {
  directory "/var/named/zones";
  forwarders { 1.1.1.1; };
  allow-query { any; };
  allow-query-cache { any; };
};

Или

zone "." {
        type hint;
        file "/etc/bind/db.root"; # <- путь к файлу с рут-зоной (с адресами корневых)  
        # а так же в этом варианте требуются некоторые настройки в options (мне лень искать и писать)
};

Работает)

$ dig a mail.ru @deimos.vandud.ru +short
217.69.139.202
94.100.180.201
94.100.180.200
217.69.139.200

rndc

Управление сервером BIND 9 производится с помощью специальной утилиты rndc, которая соединяется с сервером (по умолчанию - TCP порт 953) и использует специальный протокол для передачи ему команд

Файл /etc/rndc.conf имеет тот же синтаксис что и /etc/named.conf и содержит в себе настройки подключения к BIND (адрес, порт, пароль, алгоритм, etc)

Имеет следующие команды:


ssh tricks

Когда виснет ssh после потери коннекта

Когда при потере соединения ssh-клиент зависает (и замораживает терминал) нужно ввести в замороженном терминале ~. то есть нажать shift+` . после этих символов ssh-клиент завершится несмотря на свои внутренние таймауты

Как узнать кто из админов сейчас на сервере

Админы заходят на сервера сразу как root, поэтому не очень легко понять кто конкретно сейчас работает на сервере

Можно сделать следующим образом:

Смотрим на вывод команды w:

i.dudin_pro@db13y:~$ w
22:43:12 up 5:12, 2 users, load average: 0.38, 0.71, 1.25
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 172.27.249.190 21:44 59.00s 0.31s 0.31s -bash
i.dudin_ pts/1 172.27.0.22 22:27 0.00s 0.43s 0.01s sshd: i.dudin_pro [priv]

Видим там ip-адрес

Ищем этот ip в auth.log:

i.dudin_pro@db13y:~$ sudo grep '172.27.249.190' /var/log/auth.log
Mar 16 21:44:18 db13y sshd[24290]: Accepted publickey for root from 172.27.249.190 port 59107 ssh2: RSA 5c:88:17:2b:e8:62:ad:c5:cc:32:c4:f2:e0:0e:bb:db
Mar 16 22:47:46 db13y sudo: i.dudin_pro : TTY=pts/1 ; PWD=/home/i.dudin_pro ; USER=root ; COMMAND=/bin/grep 172.27.249.190 /var/log/auth.log

Видим там отпечаток ключа с которым подключался админ

Дальше можно выполнить вот такую штуку. Она выдаст отпечатки на основе ключей (отпечатки будут в том порядке, в котором они указаны в authorized_keys):

i.dudin_pro@db13y:~$ sudo cat /root/.ssh/authorized_keys | xargs -I% bash -c 'ssh-keygen -l -f /dev/stdin <<<"%"'
4096 26:c0:4f:97:c9:15:7a:38:9c:52:2c:97:b2:18:5d:24 /dev/stdin (RSA)
4096 89:87:b1:92:ca:d3:f3:4c:c1:ec:f3:f0:43:6f:ad:e6 /dev/stdin (RSA)
4096 d3:96:ca:42:95:1d:ab:4c:c4:c9:6b:01:09:96:79:5e /dev/stdin (RSA)
4096 82:2b:49:a7:c1:72:06:4e:1f:21:c2:f6:3c:ae:f0:f6 /dev/stdin (RSA)
4096 c4:11:53:10:2e:e0:d3:6a:b7:5e:0f:48:10:6b:fc:28 /dev/stdin (RSA)
4096 8e:df:71:3a:39:cd:ca:41:23:5c:96:87:aa:66:f7:a8 /dev/stdin (RSA)
4096 38:05:c0:7d:ea:78:d1:79:1c:71:f8:51:bb:6b:57:a6 /dev/stdin (RSA)
2048 5c:88:17:2b:e8:62:ad:c5:cc:32:c4:f2:e0:0e:bb:db /dev/stdin (RSA)

В полученном списке находим подходящий отпечаток (в нашем случае это самый последний ключ):

i.dudin_pro@db13y:~$ sudo cat /root/.ssh/authorized_keys | tail -n1
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJ************************************************************************ZV4hkYxqdXrjzwB8IMLegLc/CQjSbGAzcafbHE9iOs1 root_a.seliverstov

Теперь нам известно кто именно сейчас ломает сервер

Bacula/Bareos

https://workaround.org/bacula-cheatsheet/

Общее

Бэкапы бывают:

Компоненты

Bacula состоит из трех основных компонентов и базы данных (для хранения метаданных бэкапов)

2020-08-30-073315_603x364_scrot.png

Сокращенно называются fd, sd - file daemon, storage daemon

Различия BareOS/Bacula

Изначально была бакула, но со временем она перестала быть крутой и появился bareos

The situation between Bareos and Bacula is very similar to the situation of Nextcloud and OwnCloud. Bacula have open source free Community version and Enterprise paid version. In 2012 then Bacula Enterprise version was still open source but since 2012 its not. Some features from free Community version has been CUT/MOVED into Bacula Enterprise version only. People sent patches for Bacula Community edition but they were not merged for YEARS, so some developers got sick a tired of that situation and they created a fork named Bareos

Job

Job levels

Level Description
Backup Levels
F Full backup: Every files
I Incremental: Files modified since last backup
D Differential: Files modified since last full backup
S Since: Not used
f Virtual full backup
Verification levels
C Verify from Catalog
V Verify: Init database
O Verify volume to Catalog entries
d Verify disk attributes to Catalog
A Verify data on volume
Others
B Base level job
-- None: for Restore and Admin

Job types

Type Description
B Backup Job
V Verify Job
R Restore Job
D Admin job
C Copy of a Job
c Copy Job
M A previous backup job that was migrated
g Migration Job
A Archive Job
S Scan Job
U Console program
I Internal system “job”

Job status

Status Description
A Job canceled by user
B Job blocked
C Job created but not yet running
D Verify differences
E Job terminated in error
F Job waiting on File daemon
I Incomplete Job
L Committing data (last despool)
M Job waiting for Mount
R Job running
S Job waiting on the Storage daemon
T Job terminated normally
W Job terminated normally with warnings
a SD despooling attributes
c Waiting for Client resource
d Waiting for maximum jobs
e Non-fatal error
f Fatal error
i Doing batch insert file records
j Waiting for job resource
l Doing data despooling
m Waiting for new media
p Waiting for higher priority jobs to finish
q Queued waiting for device
s Waiting for storage resource
t Waiting for start time

Установка

По официальной документации bareos'a он ставится не сложно (есть затыки, но они преодолимы)

bconsole

Для управления директором используется bconsole

$ bconsole
*status
Status available for:
     1: Director
     2: Storage
     3: Client
     4: Scheduler
     5: All
Select daemon type for status (1-5): 1
bareos-dir Version: 19.2.7 (16 April 2020) Linux-3.10.0-1062.18.1.el7.x86_64 debian Debian GNU/Linux 10 (buster)
Daemon started 01-Sep-20 01:35. Jobs: run=4, running=0 db:postgresql, bareos.org build binary

Scheduled Jobs:
Level          Type     Pri  Scheduled          Name               Volume
===================================================================================
Incremental    Backup    10  03-Sep-20 21:00    backup-bareos-fd   Incremental-0002
Full           Backup    11  03-Sep-20 21:10    BackupCatalog      Incremental-0002
====

Running Jobs:
Console connected at 03-Sep-20 01:33
No Jobs running.
====

Terminated Jobs:
 JobId  Level    Files      Bytes   Status   Finished        Name 
====================================================================
     1  Full        319    124.0 M  OK       01-Sep-20 21:00 backup-bareos-fd
     2  Full         59    11.87 K  OK -- with warnings  01-Sep-20 21:10 BackupCatalog
     3  Incr          0         0   OK       02-Sep-20 21:00 backup-bareos-fd
     4  Full         59    11.87 K  OK -- with warnings  02-Sep-20 21:10 BackupCatalog


Client Initiated Connections (waiting for jobs):
Connect time        Protocol            Authenticated       Name                                    
====================================================================================================
====
*

Команда help в bconsole покажет доступные команды

Восстановление

Для восстановления вводим команду restore в bconsole
Это интерактивная команда (как и почти все остальные), она будет предлагать пункты на выбор

Восстановленные из бэкапа данные по умолчанию будут расположены в /tmp/bareos-restores

linux tricks

Восстановление дефолтного файла из пакета

(рассматривается debian)

В случае когда удалил или изменил что-то и хочешь вернуть к дефолтному виду, можно сделать так:

  1. Смотрим/ищем что за файл и из какого именно пакета
root@mars:~# dpkg -S RestoreFiles.conf
bareos-director: /usr/lib/bareos/defaultconfigs/bareos-dir.d/job/RestoreFiles.conf
  1. Видим точное имя пакета bareos-director
  2. Скачиваем этот пакет
root@mars:~# apt download bareos-director
  1. Превращаем deb в tar
root@mars:~# dpkg --fsys-tarfile bareos-director_19.2.7-2_amd64.deb > bareos.tar
  1. Все) Теперь у нас есть tar со всеми файлами

Docker and iptables

При использовании докера нужно быть внимательным с правилами
Докер создает правила и цепочки в таблице nat, поэтому какие-либо запрещающие правила в цепочке INPUT таблицы filter не сработают

Кейс:
Поднят контейнер с nginx и проброшен порт 80

  1. Пакет приходит в nat PREROUTING где указано
    DOCKER all -- anywhere anywhere ADDRTYPE match dst-type LOCAL
    Это правило отправляет в цепочку DOCKER любой пакет который назначен машине, адрес которой назначен нашей машине. Т.е. все что назначено нам
  2. В цепочке DOCKER есть следующее правило
    DNAT tcp -- anywhere anywhere tcp dpt:http to:172.17.0.3:80
    То есть все что приходит на наш 80 порт нужно отправлять на 80 порт контейнера
  3. Далее, т.к. это транзитный пакет, он уходит в filter FORWARD и спустя некоторый путь попадает в следующее правило
    ACCEPT tcp -- anywhere 172.17.0.3 tcp dpt:http
  4. Ну и по всей видимости попадает в контейнер (я не разобрался на 100% как именно он туда попадает)

В общем filter INPUT не сработает

Ferm

Название

ferm - парсер файерволл-правил для линукс

Синтаксис

ferm options inputfile

Описание

ferm это фронтенд для iptables. Он читает правила из структурированного конфигурационного файла и вызывает iptables(8) для вставки их в запущенное ядро.

Задача ferm'а сделать файерволл-правила более простыми для написания и чтения. Он пытается сократить утомительную задачу записи правил, так что это позволяет файерволл-администратору тратить больше времени на разработку хороших правил чем на надлежащую имплементацию обычных правил.

Для достижения этого, ferm использует простой, но мощный язык конфигурирования, в котором доступны переменные, функции, массивы и блоки. Он также позволяет тебе инклудить другие файлы, разрешая этим создавать библиотеки часто используемых структур и функций.

ferm произносится как "firm", и означает "For Easy Rule Making".

Предупреждение

Этот мануал не стремится научить тебя тому как работает файерволл и как писать хорошие правила. На эту тему уже есть достаточно документации.

Вступление

Начнем с простого примера:

chain INPUT {
  proto tcp ACCEPT;
}

Здесь добавляется правило во встроенную цепочку INPUT, которое сопоставляет и принимает все TCP пакеты. Хорошо, давайте усложним его:

chain (INPUT OUTPUT) {
        proto (udp tcp) ACCEPT;
}

Здесь будет вставлено 4 правила, а именно 2 правила в цепочку INPUT и 2 в OUTPUT. Правила будут принимать UDP и TCP пакеты.
Обычно ты бы напечатал так:

iptables -A INPUT -p tcp -j ACCEPT
iptables -A OUTPUT -p tcp -j ACCEPT
iptables -A INPUT -p udp -j ACCEPT
iptables -A OUTPUT -p udp -j ACCEPT

Заметил насколько меньше нам нужно печатать чтобы сделать это? :-)

В основном это все что нужно делать, однако, ты можешь сделать его более сложным. Что-то похожее на:

chain INPUT {
    policy ACCEPT;
    daddr 10.0.0.0/8 proto tcp dport ! ftp jump mychain sport :1023 TOS 4 settos 8 mark 2;
    daddr 10.0.0.0/8 proto tcp dport ftp REJECT;
}

Моя мысль здесь в том, что тебе нужно писать хорошие правила, оставлять их читаемыми для тебя и других и не превращать их в бардак.

Наличие результирующих правил здесь для справки, помогло бы читателю. Также ты можешь заинклудить вложенную версию для большей читабельности.

Попробуй использовать комментарии, чтобы показать что ты делаешь:

# this line enables transparent http-proxying for the internal network:
proto tcp if eth0 daddr ! 192.168.0.0/255.255.255.0
    dport http REDIRECT to-ports 3128;

Потом сам скажешь спасибо!

chain INPUT {
    policy ACCEPT;
    interface (eth0 ppp0) {
        # deny access to notorious hackers, return here if no match
        # was found to resume normal firewalling
        jump badguys;

        protocol tcp jump fw_tcp;
        protocol udp jump fw_udp;
    }
}

Чем больше вложенность, тем лучше это выглядит. Убедись что твоя последовательность верна, ведь ты бы не хотел сделать такое:

chain FORWARD {
    proto ! udp DROP;
    proto tcp dport ftp ACCEPT;
}

потому что второе правило никогда не сработает. Лучший путь это сперва определить все что разрешено, а затем запретить все остальное. Смотри на примеры, чтобы было больше "хороших снимков". Многие люди делают что-то похожее на это:

proto tcp {
    dport (
        ssh http ftp
    ) ACCEPT;
    dport 1024:65535 ! syn ACCEPT;
    DROP;
}

Структура конфигурационного файла

Структура корректного файерволл-файла похожа на упрощенный C-код. Лишь несколько синтаксических символов используются в конфигурационных файлах ferm. Кроме этих специальных символов, ferm использует 'keys' и 'values', думай об этом как об опциях и параметрах или как о переменных и значениях.

С этими словами ты опишешь свойства своего файерволла. Каждый файерволл состоит из двух вещей: Первое, проверка подходит ли сетевой трафик под конкретные условия, и второе, что делать с этим трафиком.

Ты можешь описать условия которые будут валидны для kernel interface program которую ты используешь, скорее всего это iptables(8). Например, в iptables, если ты попытаешься сопоставить TCP пакеты, ты бы написал:

iptables --protocol tcp

В ferm это превращается в:

protocol tcp;

Просто набранное это, не сделает ничего, тебе нужно сказать ferm'у (на самом деле нужно сказать iptables(8) и ядру), что делать с трафиком который совпадает с этим условием:

iptables --protocol tcp -j ACCEPT

Или, в переводе ferm'a:

protocol tcp ACCEPT;

Символ ; есть на конце каждого ferm-правила. Ferm игнорирует переносы строк, значит пример выше идентичен следующему:

protocol tcp
  ACCEPT;

Далее список специальных символов:

;

Этот символ заканчивает правило. Разделяя точкой с запятой, ты можешь писать множество правил в одну строку, хоть это и уменьшает читабельность:

protocol tcp ACCEPT; protocol udp DROP;
{}

Символ вложенности определяет 'блок' правил.

Фигурные скобки содержат некоторое количество вложенных правил. Все совпадения до блока переносятся к ним.

Закрывающая фигурная скобка завершает набор правил. Тебе не нужно писать ';' после нее потому что это будет пустым правилом.

Пример:

chain INPUT proto icmp {
    icmp-type echo-request ACCEPT;
    DROP;
}

Этот блок показывает два правила внутри блока, они оба будут объединены с чем-либо перед этим блоком и ты получишь два правила:

iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p icmp -j DROP

Здесь может быть множество уровней вложенности:

chain INPUT {
    proto icmp {
        icmp-type echo-request ACCEPT;
        DROP;
    }
    daddr 172.16.0.0/12 REJECT;
}

Заметь что на правило 'REJECT' не влияет 'proto icmp', однако там нет ';' после закрывающей фигурной скобки. В переводе на iptables:

iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p icmp -j DROP
iptables -A INPUT -d 172.16.0.0/12 -j REJECT
$

Раскрытие переменной. Заменяет '$FOO' на значение переменной. Смотри в секцию VARIABLES для деталей.

&

Вызов функции. Смотри секцию FUNCTIONS для деталей.

()

Символ массива. Используя круглые скобки ты можешь определить 'список' значений которые должны быть применены к ключу слева от него.

Пример:

protocol ( tcp udp icmp )

результатом этого будут три правила:

... -p tcp ...
... -p udp ...
... -p icmp ...

Только значения могут буть 'засписочены', поэтому ты не сможешь сделать что-то похожее на это:

proto tcp ( ACCEPT LOG );

но ты можешь сделать так:

chain (INPUT OUTPUT FORWARD) proto (icmp udp tcp) DROP;

(что приведет к девяти правилам!)

Значения разделяются пробелами. Массив одновременно лево- и право-ассоциативный, в отличие от блока вложенности, который может быть только лево-ассоциативным.

#

Символ комментария. Все что следует за этим символом до конца строки - игнорируется.

`command`

Выполнить команду в shell и вставить вывод процесса. Смотри секцию backticks для деталей.

'string'

Закавыченная строка, которая может содержать пробелы, знаки доллара и т.д.

LOG log-prefix ' hey, this is my log prefix!';
"string"

Закавыченная строка (см. выше), но вызовы переменных через знак доллара остаются вычислимыми:

DNAT to "$myhost:$myport";

Ключевые слова

В предыдущей секции мы уже представили некоторые базовые ключевые слова, такие как "chain", "protocol" и "ACCEPT". Давайте исследуем их природу.

Здесь три вида ключевых слов:

Каждое правило состоит из location и target, плюс некоторое количество matches:

table filter                  # location
proto tcp dport (http https)  # match
ACCEPT;                       # target

Строго говоря, существует четвертый вид ключевых слов ferm (которые управляют внутренним поведением ferm), но они будут объяснены позже.

Параметры

Множество ключевых слов используют параметры. Они могут быть определены как константы, вызовы переменных или списки (массивы):

proto udp
saddr $TRUSTED_HOSTS;
proto tcp dport (http https ssh);
LOG log-prefix "funky wardriver alert: ";

Некоторые из них могут быть инвертированы (списки не могут):

proto !esp;
proto udp dport !domain;

Ключевые слова, которые не принимают параметров отрицаются префиксом '!':

proto tcp !syn;

Читай iptables(8) чтобы увидеть где может быть использован !.

Базовые ключевые слова

location keywords

Basic iptables match keywords

Basic target keywords

ADDITIONAL KEYWORDS

Netfilter модульный. Модули погут предоставлять дополнительные критерии. Список модулей netfilter постоянно растет, а ferm пытается сохранить поддержку их всех.

iptables match modules

iptables target modules

Следующие дополнительные цели доступны в ferm, при условии что ты включил их в своем ядре:

ДРУГИЕ ДОМЕНЫ

Начиная с версии 2.0, ferm поддерживает не только ip и ip6, но также arp (ARP таблицы) и eb (ethernet bridging таблицы). Эти концепции аналогичны iptables.

Ключевые слова arptables

Ключевые слова ebtables

ДОПОЛНИТЕЛЬНЫЕ ВОЗМОЖНОСТИ

Переменные

В сложных файервольных файлах полезно использовать переменные, например чтобы дать сетевому интерфейсу осмысленное имя.

Чтобы указать переменную, напиши:

@def $DEV_INTERNET = eth0;
@def $PORTS = (http ftp);
@def $MORE_PORTS = ($PORTS 8080);

В настоящем ferm коде, переменные используются как параметры:

chain INPUT interface $DEV_INTERNET proto tcp dport $MORE_PORTS ACCEPT;

Заметь, что переменные могуть использоваться только в параметрах ("192.168.1.1", "http"); они не могут содержать ключевых слов как "proto" или "interface".

Переменные действительны только в текущем блоке:

@def $DEV_INTERNET = eth1;
chain INPUT {
    proto tcp {
        @def $DEV_INTERNET = ppp0;
        interface $DEV_INTERNET dport http ACCEPT;
    }
    interface $DEV_INTERNET DROP;
}

будет перобразован в:

chain INPUT {
    proto tcp {
        interface ppp0 dport http ACCEPT;
    }
    interface eth1 DROP;
}

"def $DEV_INTERNET = ppp0" валидно только в блоке "proto tcp"; родительский блок остается с "set $DEV_INTERNET = eth1".

Подключаемые файлы с описанными переменными остаются доступными в вызвавшем блоке. Это полезно когда ты инклудишь файлы в которых описаны только переменные.

Автоматические переменные

Некоторые переменные устанавливаются ferm'ом изнутри. Ferm скрипты могут использовать их просто как любые другие переменные.

Функции

Функции похожи на переменные, за исключением того что они могут принимать параметры и они предоставляют ferm'у команды а не значения.

@def &FOO() = proto (tcp udp) dport domain;
&FOO() ACCEPT;

@def &TCP_TUNNEL($port, $dest) = {
    table filter chain FORWARD interface ppp0 proto tcp dport $port daddr $dest outerface eth0 ACCEPT;
    table nat chain PREROUTING interface ppp0 proto tcp dport $port daddr 1.2.3.4 DNAT to $dest;
}

&TCP_TUNNEL(http, 192.168.1.33);
&TCP_TUNNEL(ftp, 192.168.1.30);
&TCP_TUNNEL((ssh smtp), 192.168.1.2);

Вызов функции, который содержит блок (как '{...}') должен быть последней командой в ferm правиле, например за ним должна следовать ';'. '$FOO()' не содержит блок и поэтому ты можешь написать 'ACCEPT' после ее вызова. Для обхода этого ты можешь реорганизовать ключевые слова:

@def &IPSEC() = { proto (esp ah); proto udp dport 500; }
chain INPUT ACCEPT &IPSEC();

Апострофы

С апострофами ты можешь использовать вывод внешних команд:

@def $DNSSERVERS = `grep nameserver /etc/resolv.conf | awk '{print $2}'`;
chain INPUT proto tcp saddr $DNSSERVERS ACCEPT;

Команды выполняются в shell (/bin/sh), просто как апострофы в Perl. Ferm не разворачивает никакие переменные в них.

Затем вывод помечается и сохраняется как ferm список (массив). Строки начинающиеся с '#' игнорируются; другие строки могут содержать любое количество значений разделенных пробелом.

Включения

Ключевое слово @include позволяет тебе включать внешние файлы:

@include 'vars.ferm';

Имя файла относительно к вызывающему файлу, например выражение выше включает /etc/ferm/vars.ferm когда включение происходит из /etc/ferm/ferm.conf.
Переменные и функции описанные во включенном файле остаются доступными в вызвавшем файле.

include работает в блоках:

chain INPUT {
    @include 'input.ferm';
}

Если ты укажешь директорию (с '/' на конце), все файлы из этой директории будут включены в алфавитном порядке:

@include 'ferm.d/';

Функция @glob может быть использована для разворачивания вайлдкардов:

@include @glob('*.include');

С пайп-символом на конце, ferm выполняет shell команду и парсит вывод:

@include "/root/generate_ferm_rules.sh $HOSTNAME|"

ferm прервется если код возврата не 0.

Условия

Ключевое слово @if предоставляет условное выражение:

@if $condition DROP;

Значение расценивается как true также как в Perl: ноль, пустой список, пустая строка это ложь, все остальное это истина. Примеры для истинных значений:

(a b); 1; 'foo'; (0 0)

Примеры для ложных значений:

(); 0; '0'; ''

Также здесь есть @else:

@if $condition DROP; @else REJECT;

Учитывай точку с запятой перед @else.

Можно использовать фигурные скобки перед @if и @else:

@if $condition {
    MARK set-mark 2;
    RETURN;
} @else {
    MARK set-mark 3;
}

С закрывающейся фигурной скобкой также завершается команда, поэтому точка с запятой здесь не нужна.

Здесь нет @elsif, используй вместо этого @else @if.

Пример:

@def $have_ipv6 = `test -f /proc/net/ip6_tables_names && echo 1 || echo`;
@if $have_ipv6 {
    domain ip6 {
        # ....
    }
}

Хуки

Для запуска кастомных команд ты можешь устанавливать хуки:

@hook pre "echo 0 >/proc/sys/net/ipv4/conf/eth0/forwarding";
@hook post "echo 1 >/proc/sys/net/ipv4/conf/eth0/forwarding";
@hook flush "echo 0 >/proc/sys/net/ipv4/conf/eth0/forwarding";

Описанные команды выполняются используя shell. "pre" значит запустить команду до применения файерволл правил, а "post" значит запустить команду после. "flush" хук запускается после того как ferm зафлашит файерволл правила (опция --flush). Ты можешь установить любое количество хуков.

Встроенные функции

Здесь некоторые встроенные функции, которые ты можешь найти полезными.

@defined($name), @defined(&name)

Тестирует определена ли функция или переменная.

@def $a = 'foo';
@if @defined($a) good;
@if @not(@defined($a)) bad;
@if @defined(&funcname) good;

@eq(a,b)

Тестирует два значения на равенство. Например:

@if @eq($DOMAIN, ip6) DROP;

@ne(a,b)

Похоже на @eq, оно тестирует на неравенство.

@not(x)

Инвертирует логическое значение.

@resolve((hostname1 hostname2 ...), [type])

Обычно, хостнэймы резолвятся iptables'ом. Чтобы позволить ferm'у резолвить хостнэймы, используй функцию @resolve:

saddr @resolve(my.host.foo) proto tcp dport ssh ACCEPT;
saddr @resolve((another.host.foo third.host.foo)) proto tcp dport openvpn ACCEPT;
daddr @resolve(ipv6.google.com, AAAA) proto tcp dport http ACCEPT;

Отметь двойные скобки на второй строке: внутренняя пара создает список, в внешняя как разделитель параметров функции.

Второй параметр опционален и определяет тип DNS записи. По умолчанию это "A" для домена ip и "AAAA" для домена ip6.

Будь аккуратен с зарезолвленными хостнэймами в конфигурации файерволла. DNS запросы могут блокировать конфигурирование файерволла на долгое время, оставляя машину уязвимой, или они могут фэйлиться.

@cat(a, b, ...)

Конкатенирует все параметры в одну строку.

@join(separator, a, b, ...)

Объединяет все параметры в одну строку разделяя указанным разделителем.

@substr(expression, offset, length)

Извлекает подстроку из выражения и возвращает ее. Первый символ имеет отступ 0. Если OFFSET отрицательный, то отсчитывается настолько же далеко от конца строки.

@length(expression)

Возвращает длину выражения в символах.

@basename(path)

Возвращает базовое имя файла для указанного пути (File::Spec::splitpath).

@dirname(path)

Возвращает имя последней директории для указанного пути, предполагая что последний компонент это имя файла (File::Spec::splitpath).

@glob(path)

Разворачивает shell wildcard'ы в указанных путях (предполагается что они относительны к текущему скрипту). Возвращает список заматченных файлов. Эта функция полезна как параметр для @include.

@ipfilter(list)

Отфильтровывает IP адреса которые, очевидно, не подходят под текущий домен. Это полезно для создания общих переменных и правил для IPv4 и IPv6:

@def $TRUSTED_HOSTS = (192.168.0.40 2001:abcd:ef::40);

domain (ip ip6) chain INPUT {
    saddr @ipfilter($TRUSTED_HOSTS) proto tcp dport ssh ACCEPT;
}

РЕЦЕПТЫ

Директория ./examples/ содержит многочисленные ferm конфигурации, которые могут быть использованы для начала в новом файерволле. Эта секция содержит много примеров, рецептов и трюков.

Легкий проброс портов

Ferm функции делают рутинные задачи быстрыми и легкими:

@def &FORWARD_TCP($proto, $port, $dest) = {
    table filter chain FORWARD interface $DEV_WORLD outerface $DEV_DMZ daddr $dest proto $proto dport $port ACCEPT;
    table nat chain PREROUTING interface $DEV_WORLD daddr $HOST_STATIC proto $proto dport $port DNAT to $dest;
}

&FORWARD_TCP(tcp, http, 192.168.1.2);
&FORWARD_TCP(tcp, smtp, 192.168.1.3);
&FORWARD_TCP((tcp udp), domain, 192.168.1.4);

Удаленный ferm

Если удаленная машина не способна запустить ferm по каким-то причинам (может быть не имеется Perl), то ты можешь редактировать конфигурационный файл ferm на другом компьютере и дать ferm'у сгенерировать shell скрипт.

Пример для OpenWRT:

ferm --remote --shell mywrt/ferm.conf >mywrt/firewall.user
chmod +x mywrt/firewall.user
scp mywrt/firewall.user mywrt.local.net:/etc/
ssh mywrt.local.net /etc/firewall.user

ОПЦИИ

СМОТРИ ТАКЖЕ

iptables(8)

ТРЕБОВАНИЯ

Операционная система

Linux 2.4 или новее, с поддержкой netfilter и всеми модулями которые используются твоими скриптами

Программное обеспечение

iptables и perl 5.6

БАГИ

Баги? Какие еще баги?
Если ты нашел баг, пожалуйста уведоми об этом на GitHub: https://github.com/MaxKellermann/ferm/issues

КОПИРАЙТ

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

АВТОР

Max Kellermann max.kellermann@gmail.com, Auke Kok sofar@foo-projects.org

openssl

Проверить соответствие Root.crt - User.crt

openssl verify -CAfile root.crt user.crt

Проверить соответствие user.crt - user.key

openssl x509 -noout -modulus -in certificate_name.crt | openssl md5
openssl rsa -noout -modulus -in certificate_name.key | openssl md5

Процесс загрузки ядра Linux

systemd/systemctl

Команда systemctl без параметров или с подкомандой list-units выведет список запущенных юнитов

Вывод выглядит так
Screenshot_2.png
boot.mount loaded active mounted /boot

  1. UNIT - boot.mount - Название юнита и его тип
  2. LOAD - loaded - Состояние юнита, загружен или нет
  3. ACTIVE - active - Обобщенный статус
  4. SUB - mounted - Текущий статус
  5. DESCRIPTION - /boot - Описание

LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.
JOB = Pending job for the unit.


Параметр --failed покажет список юнитов, запуск которых не удался

root@mars:~# systemctl --failed
0 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.

Файлы юнитов находятся в /usr/lib/systemd/system/ и /etc/systemd/system/ (последний каталог имеет приоритет). Команда systemctl list-unit-files покажет список установленных юнитов


Юнитами могут быть сервисы, точки монтирования, устройства или сокеты

Обычно при использовании systemctl нужно указывать полное имя файла юнита (вметсе с суффиксом типа - sshd.socket). Но есть сокращения:

Некоторые юниты содержат в названии символ @ - название@строка.service
Это значит что они являются экземплярами юнита-шаблона. Имя юнита шаблона не содержит части строка - название@.service
строка - это идентификатор экземпляра



systemctl mask/unmask ntp.service - маскирует юнит. Это делает невозможным его запуск

root@mars:/etc/systemd/system# systemctl mask ntp.service
Created symlink /etc/systemd/system/ntp.service → /dev/null.

root@mars:/etc/systemd/system# systemctl stop ntp.service

root@mars:/etc/systemd/system# systemctl start ntp.service
Failed to start ntp.service: Unit ntp.service is masked.

Файлы юнитов загружаются из разных мест, и чтобы увидеть эти места можно выполнить такую команду

root@mars:/etc/systemd# systemctl show --property=UnitPath
UnitPath=/etc/systemd/system.control /run/systemd/system.control /run/systemd/transient /etc/systemd/system /run/systemd/system /run/systemd/generator /lib/systemd/system /run/systemd/generator.late

В файлах юнитов допустимы комментарии. Но каждый комментарий на отдельной строке. Комментарий начинается с решетки


Юнит файлы пакетов редактировать напрямую не стоит. Это вызовет проблемы с пакетным менеджером. Есть два пути редактирования юнит файла пакета (см. документацию)


Файлы из /etc/systemd/system/ переопределяют /usr/lib/systemd/system
А для того чтобы новый файл подтянулся, нужно выполнить systemctl reenable UNIT

Это можно сделать через утилиту systemctl edit --full UNIT
Она либо откроет файл из /etc/..., либо скопирует файл из /usr/lib/... в /etc/... и после редактирования автоматически перезагрузит все что нужно

Но этот метод может вызвать проблемы с пакетным менеджером


Также можно переопределять юнит файлы через drop-in файлы. Нужно создать каталог /etc/systemd/system/UNIT.d/ и в нем создать .conf файлы (описываются так же как и юнит-файлы). systemd будет применять эти файлы поверх оригинального

Это делает команда systemctl edit UNIT (без флага --full)

Чтобы отменить изменения сделанные через edit нужно выполнить systemctl revert UNIT


Для просмотра юнитов можно использовать systemctl cat ntp.service


systemd-delta - позволяет увидеть какие есть оверрайды для каких юнитов

root@mars:/etc/systemd# systemd-delta
[OVERRIDDEN] /etc/systemd/system/ntp.service → /usr/lib/systemd/system/ntp.service

Files /usr/lib/systemd/system/ntp.service and /etc/systemd/system/ntp.service are identical

[EXTENDED]   /usr/lib/systemd/system/rc-local.service → /usr/lib/systemd/system/rc-local.service.d/debian.conf
[EXTENDED]   /usr/lib/systemd/system/systemd-resolved.service → /usr/lib/systemd/system/systemd-resolved.service.d/resolvconf.conf
[EXTENDED]   /usr/lib/systemd/system/systemd-timesyncd.service → /usr/lib/systemd/system/systemd-timesyncd.service.d/disable-with-time-daemon.conf

4 overridden configuration files found.

Цели ( target ) используются для группировки юнитов по зависимостям в качестве точек синхронизации (как уровни запуска)

Показать все текущие цели можно такой командой

systemctl list-units --type=target

Уровни запуска, имеющие определённое значение в sysvinit (0, 1, 3, 5 и 6), один в один соответствуют конкретным целям systemd

Уровнень запуска SysV Цель systemd Примечания
0 runlevel0.target, poweroff.target Выключение системы
1, s, single runlevel1.target, rescue.target Однопользовательский уровень запуска
2, 4 runlevel2.target, runlevel4.target, multi-user.target Уровни запуска, определенные пользователем/специфичные для узла. По умолчанию соответствует уровню запуска 3
3 runlevel3.target, multi-user.target Многопользовательский режим без графики. Пользователи, как правило, входят в систему при помощи множества консолей или через сеть
5 runlevel5.target, graphical.target Многопользовательский режим с графикой. Обычно эквивалентен запуску всех служб на уровне 3 и графического менеджера входа в систему
6 runlevel6.target, reboot.target Перезагрузка
emergency emergency.target Аварийная оболочка

Переключаться между режимами можно так

systemctl isolate default.target
systemctl isolate rescue.target
systemctl isolate emergency.target

systemctl default
systemctl rescue
systemctl emergency

systemctl reboot
systemctl poweroff

Создаёте новый юнит-цель с названием /etc/systemd/system/цель, который берет за основу один из существующих уровней запуска (взгляните, например, на /usr/lib/systemd/system/graphical.target), создаёте каталог /etc/systemd/system/цель.wants, а после этого — символические ссылки на те службы из каталога /usr/lib/systemd/system/, которые вы хотите включить при загрузке

Зависимости, о которых сказано выше, можно добавлять с помощью команд

systemctl add-wants <target-unit-name> <requirement-name>
systemctl add-requires <target-unit-name> <requirement-name>

Изменить текущую цель можно командой systemctl isolate UNIT.target
Это не повлияет на последующие загрузки (только на текущую)


Стандартная цель это default.target
Узнать текущую цель можно так

# systemctl get-default
graphical.target

А указать цель можно через systemctl set-default UNIT

Также это можно указать через параметры ядра


systemd.mount - отвечает за монтирование разделов и ФС из файла /etc/fstab


systemd-tmpfiles позволяет управлять временными файлами и каталогами (пример: samba предполагает что существует каталог /run/samba с нужными правами доступа)


systemctl preset <unit-name>

Восстановит параметры автозагрузки по умолчанию, т. е. если изначально юнит не загружался автоматически, а это было изменено администратором, то юнит будет извлечён из автозагрузки


Бывает так что юнит файл находится вне путей стандартного авто-поиска юнитов
В этом случае можно слинковать этот файл в нужно место

systemctl preset <unit-name>

Эта команда создаст ссылку в на файл в нужной папке


systemctl revert <unit-name>

Восстановит юнит-файл к изначальному состоянию. Сотрет drop-in юниты, отменит результаты команд edit, set-property, mask


С помощью systemctl show-environment можно увидеть переменные окружения которые доступны всем запускаемым юнитам

Устанавливать переменные можно через set-environment

systemctl set-environment MY_VAR=test

А удалять через unset-environment

systemctl unset-environment MY_VAR

LVM

https://www.dmosk.ru/instruktions.php?object=lvm

Base

Существует проблема правильного разбиения дисков на разделы. В какой-то момент во время работы системы одни разделы остаются свободными, а другие забиваются под завязку. Эту проблему решает LVM

LVM - Logical Volume Manager
Позволяет делать снапшоты, легко добавлять новые физические диски, позволяет уменьшать и расширять тома без размонтирования

Работа с томати организована на трех уровнях:

  1. Физический том (PV) - физический диск и раздел на диске
  2. Группа томов (VG) - объединение физических томов
  3. Логический том (LV) - раздел группы томов

Схема уровней:
lvm-01.jpg

PV и LV делятся на экстенды PE и LE (физические и логический экстенд соответственно)

Создание разделов

  1. Помечаем нужные диски как PV
root@osboxes:~# pvcreate /dev/sdb /dev/sdc
Physical volume "/dev/sdb" successfully created.
Physical volume "/dev/sdc" successfully created.
  1. Проверяем командой pvdisplay
  2. Создаем из этих дисков VG
root@osboxes:~# vgcreate vg01 /dev/sdb /dev/sdc                                                                           Volume group "vg01" successfully created    
  1. Проверяем командой vgdisplay
  2. Создаем логический раздел
root@osboxes:~# lvcreate -L 2.19G vg01
Rounding up size to full physical extent 2.19 GiB
Logical volume "lvol0" created.
  1. Проверяем командой lvdisplay
    В выводе будет путь до раздела LV Path /dev/vg01/lvol0
  2. Создаем ФС
mkfs.ext4 vg01/lvol0
  1. Монтируем
mount /dev/vg01/lvol0 /mnt
  1. Готово
lsblk
sdb            8:16   0     1G  0 disk
└─vg01-lvol0 254:0    0   2.2G  0 lvm  /mnt
sdc            8:32   0   1.2G  0 disk
└─vg01-lvol0 254:0    0   2.2G  0 lvm  /mnt
df -h /mnt
Filesystem              Size  Used Avail Use% Mounted on
/dev/mapper/vg01-lvol0  2.1G  150M  1.9G   8% /mnt

Командой pvs можно получить краткую информацию о lvm дисках

pvs
  PV         VG   Fmt  Attr PSize  PFree
  /dev/sdb   vg01 lvm2 a--   1.03g 8.00m
  /dev/sdc   vg01 lvm2 a--  <1.17g    0

pvdisplay покажет информацию о дисках в развернутом виде


vgs покажет информацию о группах

vgs
  VG   #PV #LV #SN Attr   VSize  VFree
  vg01   2   1   0 wz--n- <2.20g 8.00m

vgdisplay - более подробно


lvs покажет информацию о логических томах (подробный вывод в lvdisplay)


lvmdiskscan покажет lvm диски


Расширить логический диск за счет свободного места в волюм группе можно так

root@osboxes:~# vgs
  VG   #PV #LV #SN Attr   VSize  VFree
  vg01   2   1   0 wz--n- <2.20g 8.00m

root@osboxes:~# lvextend -l +100%FREE /dev/vg01/lvol0
  Size of logical volume vg01/lvol0 changed from 2.19 GiB (561 extents) to <2.20 GiB (563 extents).
  Logical volume vg01/lvol0 successfully resized.

root@osboxes:~# vgs
  VG   #PV #LV #SN Attr   VSize  VFree
  vg01   2   1   0 wz--n- <2.20g    0

Расширить группу томов за счет нового диска

vgextend vg01 /dev/sdd

Для уменьшения логического тома требуется его отмонтировать

network

For up interface on boot and DHCP configuring you need edit /etc/network/interfaces

auto eth0
iface eth0 inet dhcp

For static configuration

auto eth0
iface eth0 inet static
address 192.168.1.254
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1

After editing conf files you need restart network service

sudo /etc/init.d/networking restart

dvorak in console

Чтобы сработала команда loadkeys dvorak нужно чтобы был установлен пакет console-data

ini

Комментарии

Обозначаются точкой с запятой ;
От точки с запятой до конца строки

Идентификаторы

Могут содержать в себе a-z, A-Z, 0-9, _, ~, -, ., :, $, space, начинаются с a-z, A-Z, . , $, :
Регистр важен

Секции

Это часть конфига обозначенная идентификатором
Секция обозначается заголовком в квадратных скобках
Пример

[Defaults]

Одна секция заканчивается там где начинается другая секция

Секция не может быть разделена на несколько частей, каждый идентификатор секции может встречаться в конфиге лишь однажды (в рамках одного файла)

Секция может быть пустой (т.е. содержать в себе 0+ опций)

Опции

Опция это пара идентификатор=значение в какой-либо секции

Пробелы в начале или конце идентификатора или значения нужно экранировать

Пробелы между словами в идентификаторе или значении экранировать не надо

Значение может быть представлено одним или несколькими элементами разделенными запятой , или двоеточием :. Если в значении присутствуют оба разделителя, то будет выбрана запятая (у нее приоритет выше)

Элемент

Элемент это текст стоящий в паре с идентификатором опции

Текст элемента может быть связан с другим элементом (даже в разных секциях)

Типы данных

Элемент может быть определенного типа

Примеры

[Section 1]
; comment
Option 1 = value 1                     ; option 'Option 1' has value 'value 1'
oPtion 1    =  \ value 2\ \ \          ; option 'oPtion 1' has value ' value 2   ', 'oPtion 1' and 'Option 1' are different

[$Section::subsection]                 ; no subsection, only valid identifier of section
Option 2=value 1:value 2:value 3       ; option 'Option 2' is list of 'value 1', 'value 2' and 'value 3'
Option 3 =value 1, ${Section 1#Option 1} ; option 'Option 3' is list of 'value 1' and 'value 1'
Option 4= v1,${$Section::subsection#Option 3},v2 ; option 'Option 4' is list of 'v1', 'value 1', 'value 1', 'v2'
Option 5= v1, v2:v3                    ; option 'Option 5' is list of 'v1' a 'v2:v3'

[Numbers]
num = -1285
num_bin = 0b01101001
num_hex = 0x12ae,0xAc2B
num_oct = 01754

float1 = -124.45667356
float2 = +4.1234565E+45
float3 = 412.34565e45
float4 = -1.1245864E-6

[Other]
bool1 = 1
bool2 = on
bool3=f

apt sources list

https://wiki.debian.org/ru/SourcesList

/etc/apt/sources.list - apt использует этот файл в качестве хранилища источников пакетов

Строки в этом файле выглядят примерно так

deb http://mirror.yandex.ru/debian buster main contrib
deb-src http://mirror.yandex.ru/debian buster main contrib

deb http://security.debian.org/debian-security buster/updates main contrib
deb-src http://security.debian.org/debian-security buster/updates main contrib

Первое слово в каждой строке это тип архивов в этом репозитории

Дальше идет ссылка на репозиторий

За ссылкой идет псевдоним релиза (jessie, stretch, buster, sid), либо класс релиза (stable, oldstable, testing, unstable)

Потом идут компоненты


Бывают репы из тора, apt умеет работать и с ними

MSSQL

Как подключиться к mssql с linux

# Сперва поднял туннель со своей машины до mssql базы через сервер с репликатором (чтобы как будто мы с репликатора ходим)
root@c58882dda0d8:~# ssh -4 -L localhost:1234:srv-pro-db07.i-free.pro:1433 i.dudin_pro@vas73.g01.i-free.ru -N &
[1] 1818
# Проверяем что все слушается
root@c58882dda0d8:~# ss -tulpn
Netid  State     Recv-Q Send-Q    Local Address:Port    Peer Address:Port
tcp    LISTEN    0      128       127.0.0.1:1234        *:*                  users:(("ssh",pid=1818,fd=4))
# Подключаемся
root@c58882dda0d8:~# sqlcmd -S tcp:127.0.0.1,1234 -d DB2DB_Replicator -U db2db_agent_msg3_interactive -P PASSWORD
1> :setvar SQLCMDMAXVARTYPEWIDTH 30
2> select top 10 id, name, rule_id from filter_log;
3> go
id                   name                           rule_id    
-------------------- ------------------------------ -----------
          1930921024 DBInboundFilter                        308
          1930921025 DBOutboundFilter                     80643
          1930921026 DBInboundFilter                      81028
          1930921027 DBOutboundFilter                     79961
          1930921028 DBOutboundFilter                     79961
          1930921029 DBOutboundFilter                     79961
          1930921030 DBInboundFilter                      79858
          1930921031 DBInboundFilter                      79858
          1930921032 DBOutboundFilter                     80799
          1930921033 DBOutboundFilter                     79859
(10 rows affected)

BASH

Заметки из книги "BASH. Карманный справочник системного администратора"

BASH

bash-huyash

Команды оболочки bash можно выполнять с терминала, из файла (когда первым аргументом передано имя файла) или можно подавать баш скрипты на вход команде bash:

macbook:~ vandud$ echo "echo govno" | bash
govno
macbook:~ vandud$ echo "echo govno" | bash -s
govno

Ключ -i запустит оболочку и покажет приглашение:

macbook:~ vandud$ bash -i

The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
bash-3.2$ echo "echo 'mocha i govno'" | bash -s
mocha i govno
bash-3.2$ 

Исходные оболочки читают содержимое файлов /etc/profile и -/.profile, а обычные оболочки - содержимое переменной окружения $ENV, если она установлена

Исходная оболочка это та которая служит для входа в систему


--init-file/--rcfile - переопределить ~/.bashrc



macbook:test vandud$ cat test
echo "govno"
macbook:test vandud$ ls -l test
-rw-r--r--  1 vandud  wheel  13 Feb 19 07:22 test
macbook:test vandud$ ./test
-bash: ./test: Permission denied
macbook:test vandud$ bash test
govno

Код завершения команды доступен в переменной $?
Внутренние команды оболочки передают этот код непосредственно оболочке, а внешние команды передают его операционной системе
Значение кода завершения может находиться в диапазоне от 0 до 255
Preview_2021-02-19-07-29-58.png


Инит файлы читаются в следующем порядке

  1. /etc/profile
  2. Первый найденный из ~/.bash_profile, ~/.bash_login или ~/.profile
  3. ~/.bashrc читается неисходными оболочками, но не читается если оболочка запущена как sh или с параметром --posix (в этих двух случаях читается $ENV)

При выходе из интерактивной оболочки выполняется ~/.bash_logout (при наличии)


Интересный момент про ссылки

macbook:test vandud$ cat test
echo $0
macbook:test vandud$ ls -lh
total 8
-rwxr--r--  1 vandud  wheel     8B Feb 19 10:22 test
lrwxr-xr-x  1 vandud  wheel     4B Feb 19 10:22 test2 -> test
macbook:test vandud$ ./test
./test
macbook:test vandud$ ./test2
./test2

Существует сокращение для получения хомяка пользователя

vandud@macbook test % grep logd /etc/passwd
_logd:*:272:272:Log Daemon:/var/db/diagnostics:/usr/bin/false
vandud@macbook test % echo ~_logd
/var/db/diagnostics

Немного про глоббинг
Базовые штуки

Для этих нужно чтоб был включен параметр extglob (в bash он ставится через shopt -s extglob, в zsh через setopt)

Шаблонов может быть много, тогда их нужно разделять пайпом


Классы символов (для них тоже требуется extglob)
Указываются в [[:<class>:]]


Скрипты читаются построчно
Каждая строка сначала читается, потом проверяется на синтаксис, а потом уже выполняется
Поэтому, например, нельзя определить алиас и вызвать его в одной строке
Нужно учитывать аналогичные примеру операции


Подстановки (выше) и раскрытие скобок отличаются
При раскрытии скобок образуется текст, который не обязательно должен совпадать с уже существующими файлами

Есть два варианта раскрытия скобок

vandud@macbook test % echo head{A,B,C}tail
headAtail headBtail headCtail
vandud@macbook test % echo head{A..C}tail
headAtail headBtail headCtail

vandud@macbook test % echo head{1..5..2}tail
head1tail head3tail head5tail

В числовом выражении предел start может предваряться нулями

vandud@macbook test % echo head{001..5..2}tail
head001tail head003tail head005tail

vandud@macbook test % echo head{01..005..2}tail
head01tail head03tail head05tail

По длине числа start будут выровняны раскрытые числа

vandud@macbook test % echo {0..10..5}
0 5 10
vandud@macbook test % echo {00..10..5}
00 05 10
vandud@macbook test % echo {000..10..5}
000 005 010
vandud@macbook test % echo {0000..10..5}
0000 0005 0010

Скобки могут быть вложенными

vandud@macbook test % echo {a,b,{1..4}}
a b 1 2 3 4

vandud@macbook test % echo {a..b..{1..4}}
{a..b..1} {a..b..2} {a..b..3} {a..b..4}

vandud@macbook test % echo {a..b..{1..5..2}}
{a..b..1} {a..b..3} {a..b..5}

Скобки должны быть без кавычек, иначе баш не распарсит

Подстановки команд будут проигнорированы при раскрытии скобок (но у меня в zsh работает, в баше не смог)

vandud@macbook test % echo {1..10..$(echo 3)} 
1 4 7 10
vandud@macbook test % echo {1..10..`echo 3`} 
1 4 7 10

vandud@macbook test % echo hi{DD,BB,CC,AA}bitch
hiDDbitch hiBBbitch hiCCbitch hiAAbitch

vandud@macbook test % ls LKz{QQ,Qt}e0f         
LKzQQe0f	LKzQte0f

В качестве элемента внутри скобок может быть и пустота

vandud@macbook test % ls zhopa*  
zhopa
vandud@macbook test % mv zhopa{,_s_gavnoy}
vandud@macbook test % ls zhopa*           
zhopa_s_gavnoy

Управляющие последовательности распознаются и интерпретируются в след. контекстах

Terminal_2021-02-19-17-09-25.png


В кавычках отменяется спец. значение символов, например скобок, стрелок, итд
Teampaper-Snap_2021-02-19-17-29-21.png
Teampaper-Snap_2021-02-19-17-29-35.png


Внутри двойных кавычек действует доллар и тик

Внутри одинарных кавычек все становится обычным текстом

\ - используется для экранирования



cmd < filename

vandud@macbook test % cat zhopa_s_gavnoy | cat
asonetuhaoesntuhsanoethustaoehustnaoehusnaeohus

vandud@macbook test % cat < zhopa_s_gavnoy                                                 
asonetuhaoesntuhsanoethustaoehustnaoehusnaeohus

vandud@macbook test % cat zhopa_s_gavnoy | grep -o 'aso'
aso

vandud@macbook test % grep -o 'aso' < zhopa_s_gavnoy                                       
aso

То же самое что и выше, только данные берутся не из файла а из текста
cmd << text

vandud@macbook test % cat <<< "niga mazafaka bitch"
niga mazafaka bitch
vandud@macbook test % echo "niga mazafaka bitch" | cat
niga mazafaka bitch

cmd << EOF

vandud@macbook test % cat << ZHOPA | grep -i niga
pipe heredoc> niga1
pipe heredoc> niiga2
pipe heredoc> zhopa
pipe heredoc> ZHOPA
niga1

>| - позволяет перезаписать файл даже если включен параметр noclobber

bash-3.2$ set -o | grep clobber 
noclobber      	off

bash-3.2$ cat test
govno
bash-3.2$ echo "zhopa" > test
bash-3.2$ cat test
zhopa

bash-3.2$ set -o noclobber
bash-3.2$ set -o | grep clobber
noclobber      	on

bash-3.2$ cat test
zhopa
bash-3.2$ echo "govno" > test
bash: test: cannot overwrite existing file
bash-3.2$ cat test
zhopa
bash-3.2$ echo "govno" >| test # SUCCESS
bash-3.2$ cat test
govno
bash-3.2$ 

О параметрах
У оболочки есть разные параметры
Один из них упоминался ранее (extglob)
Для просмотра и установки этих параметров можно использовать команду set или shopt

Существует сразу две команды потому что set (и параметры с которыми можно взаимодействовать через него) унаследовался вместе с bourne-style (от sh), а shopt появился в bash. Поэтому некоторые параметры могут быть не видны в одном и видны в другом

Посмотреть текущее состояние можно так

set -o
shopt

Активировать так

set -o noclobber # enable
set +o noclobber # disable

shopt -s extglob # enable
shopt -u extglob # disable

Параметров всяких целая куча, тут парочку показал для примера


Перенаправление вывода по дескриптору

Пробел между стрелкой и ФД не нужен


stderr в stdout

root@mars:/var/tmp/test# nginx 2>&1 | tee nginx.out
root@mars:/var/tmp/test# nginx |& tee nginx.out 
BASH

bash doc

http://www.gnu.org/software/bash/manual/bash.html

1 Introduction

1.1 Что такое Bash

Bash это шелл, или командный интерпретатор для операционных систем GNU. Имя является акронимом от 'Bourne-Again SHell', игрой слов на Стивена Борна, автора прямого предка текущего Unix шелла - sh, который появился в Седьмом Издании Bell Labs Research версии Unix.

Bash хорошо совместим с sh и объединяет в себе полезные фишки из Korn shell ksh и C shell csh. Он направлен на то чтобы быть совместимой имплементацией 'IEEE POSIX Shell and Tools' которая является частью спецификации POSIX. Он предлагает улучшения функционала поверх sh сразу для интерактивности и использования в программировании.

Операционные системы GNU предоставляют другие шеллы, включая csh, Bash является шеллом по умолчанию. Как и другое ПО GNU, Bash полностью портабельный. На данный момент он запускается на почти каждом Unix'e и нескольких других ОС - существуют порты для MS-DOS, OS/2 и Windows которые поддерживаются независимыми разработчиками.


1.2 Что такое Шелл

В основе, шелл это макропроцессор который выполняет команды. Термин макропроцессор обозначает функциональность при которой текст и символы расширяются до больших выражений.

Unix шелл одновременно и интерпретатор команд и язык программирования. Как интерпретатор команд шелл предоставляет пользовательский интерфейс к богатому набору GNU утилит. Как язык программирования он позволяет комбинировать эти утилиты. Можно создавать файлы с командами и делать их командами. Эти новые команды могут иметь такой же статус как и системные команды в таких директориях как /bin, позволяя пользователям или группам создавать кастомные условия для автоматизации их повседневных задач.

Шеллы могут использоваться интерактивно или неинтерактивно. В интерактивном режиме они позволяют делать ввод с клавиатуры. В неинтерактивном шеллы выполняют команды читая их из файла.

Шелл позволяет выполнять GNU команды синхронно и асинхронно. Шелл ждет окончания выполнения синхронной команды прежде чем разрешить вводить новые команды; асинхронные команды продолжают выполнение в параллели с шеллом пока он читает и выполняет дополнительные команды. Перенаправления позволяют тонко контролировать ввод и вывод команд. Кроме того шелл позволяет контролировать окружения команд.

Шеллы также предоставляют небольшой набор встроенных команд (builtins) давая функциональность которую невозможно или неудобно получить через выделенные утилиты. Например, cd, break, continue и exec не могут быть выполнены снаружи шелла, потому что они направлены на управление самим шеллом. Встроенные history, getopts, kill или pwd могут быть имплементированы в выделенных утилитах, но их гораздо удобнее использовать как встроенные команды. Все встроенные команды будут описаны ниже.

Хоть выполнение команд это и суть оболочки, основная мощь (и сложность) связана с внутренним языком программирования. Как и любой высокоуровневый язык, шелл предоставляет переменные, конструкции для конроля над потоком выполнения, кавычки и функции.

Шеллы предлагают функции специфические для интерактивного использования, а не для расширения возможностей языка программирования. Эти интерактивные функции включают контроль над задачами, редактирование командной строки, историю команд и алиасы. Каждая из этих функций будет описана в этом мануале.


2 Определения

Эти определения используются во всей оставшейся части этого манула.


3 Базовые Функции Оболочки

Bash это акроним для 'Bourne-Again SHell'. Шелл Борна это традиционный Unix шелл написанный Стивеном Борном. Все встроенные команды Шелла Борна доступны в Bash, Правила оценки и цитирования взяты из спецификации POSIX для 'стандартной' оболочки Unix.

Эта глава кратко резюмирует 'строительные блоки' шелла: команды, структуры управления, функции шелла, параметры шелла, расширения, перенаправления, которые являются способом прямого ввода и вывода из и в именованный файл, и то как шелл выполняет команды.

3.1 Синтакс Шелла

Когда шелл читает ввод, он делает это через последовательность операций. Если ввод показывает что начинается комментарий, то шелл игнорирует символ комментария ('#') и остальную строку.

Иначе, грубо говоря, шелл читает инпут и разделяет его на слова и операторы, применяя правила для выбора того какие значения назначать различным словам и символам.

Затем шелл парсит эти токены в команды и другие конструкции, убирает особый смысл у одних слов и символов, расширяет другие, перенаправляет ввод и вывод как надо, выполняет указанные команды, ждет код ответа этих команд и делает его доступным для последующей инспекции или обработки.


3.1.1 Работа Шелла

Далее краткое описание работы шелла когда он читает и выполняет команду. Обычно шелл делает следующее:

  1. Читает ввод из файла (см. '3.8 Шелл скрипты'), из строки которая передана аргументом в -c опцию вызова (см. '6.1 Вызов Bash') или из пользовательского терминала
  2. Разделяет ввод на слова и операторы, используя правила кавычек описанные в '3.1.2 Кавычинг'. Эти токены выделяются с помощью metacharacters. Разворачивание алиасов выполняется на этом этапе (см. '6.6 Алиасы')
  3. Парсинг токенов в простые и сложные команды (см. '3.2 Команды Шелла')
  4. Выполнение различных расширений (см. '3.5 Шелл Расширения'), разбиение расширенных токенов в список имен файлов (см. '3.5.8 Расширение Имен Файлов'), команд и аргументов
  5. Выполнение необходимых перенаправлений (см. '3.6 Перенаправления') и удаление операторов перенаправления и их операндов из списка аргументов
  6. Выполнение команд (см. '3.7 Выполнение Команд')
  7. Опциональное ожидание завершения команд и сбор их статускодов (см. '3.7.5 Статускод')

3.1.2 Кавычинг

Кавычинг используется для удаления специальных значений определенных символов и слов в шелле. Кавычинг может быть использован для отключения специальной обработки для специльных символов, для предотвращения от распознавания зарезервированых слов как таковых, и для предотвращения разворачивания параметров.

Каждый из метасимволов (см. '2 Определения') имеет специальное значение и поэтому должен быть закавычен если он должен отображать себя. Когда используется символ разворачивания из истории команд, обычно это '!', он должен быть закавычен для предотвращения разворачивания.

Имеется три кавычинговых механизма: escape character, одиночные кавычки, двойные кавычки.


3.1.2.1 Эскейп Символы

Незакавыченный бэкслэш '\' это Bash escape символ. Он отображает буквальное значение следующего за ним символа, символ новой строки - исключение. Если встречается пара \newline в которой бэкслэш незакавычен, то символ новой строки рассматривается как продолжение строки (он удаляется из введенной строки и по факту игнорируется).

root@mars:~# echo niga\ #enter
>pidor #enter
nigapidor

3.1.2.2 Одиночные Кавычки

Символы заключенные в одиночные кавычки (" ' ") отоборажают буквальное значение каждого символа в кавычках. Одиночная кавычка не может быть между одиночными кавычками, даже если она заэкранирована бэкслэшем.

root@mars:~# echo 'niga'pidor'
> ^C
root@mars:~# echo 'niga\'pidor'
> ^C

3.1.2.3 Двойные Кавычки

Заключенные в двойные кавычки символы (' " ') отображаются буквально за исключением $, ~ и \ и если разворачивание из истории включено, то еще !. Когда шелл в режиме POSIX, то '!' не имеет специального значения в двойных кавычках, даже когда разворачивание из истории включено. '$' и '~' сохраняют свой особый смысл в двойных кавычках. Бэкслэш сохраняет специальное значение только когда за ним следует один из следующих символов: $, ~, ", \ или newline. В двойных кавычках бэкслэши, после которых идут перечисленные символы, будут удалены. Бэкслэши предшествующие символам без специального значения будут оставаться неизменными. Двойная кавычка может быть закавычена в двойные кавычки, с использованием предваряющего ее бэкслэша. Если разворачивание из истории включено, то оно будет выполняться пока '!' встречающийся в двойных кавычках не будет заэкранирован бэкслэшем. Предшествующий восклицательному знаку бэкслэш не удаляется.

Специальные параметры * и @ имеют специальное значение в двойных кавычках.


3.1.2.4 ANSI-C Кавычинг

Слова в форме $'string' обрабатываются по особому. Строка развернется в string, и заменит заэкранированные символы так как описано в ANSI C стандарте. Бэкслэшем заэкранированные последовательности, если они есть, будут декодированы следующим образом:

Результат разворачивания будет одинарно-закавыченный, как если бы знака доллара не было.

root@mars:~# echo $'aa\bb'
ab
root@mars:~# echo $'aa\fb'
aa
  b
root@mars:~# echo $'aa\nb'
aa
b
root@mars:~# echo $'aa\rb'
ba
root@mars:~# echo $'aa\tb'
aa	b
root@mars:~# echo $'aa\vb'
aa
  b
root@mars:~# echo $'aa\044b'
aa$b
root@mars:~# echo $'aa\x24b'
aa$b
root@mars:~# echo $'aa\u0024b'
aa$b
root@mars:~# echo $'aa\U00000024b'
aa$b

3.1.2.5 Локаль-специфичные Переводы

Строка в двойных кавычках с предшествующим долларом ('$') будет переведена основываясь на текущей локали. gettext выполнит поиск message catalog'a и перевод, используя переменные LC_MESSAGES и TEXTDOMAIN как объясняется ниже. Смотри документацию к gettext для дополнительных деталей. Если текущая локаль C или POSIX или если нет доступных переводов, то знак доллара будет проигнорирован. Если строка переведена и в ней выполнена замена, то результат будет в двойных кавычках.

Некоторые системы используют message catalog выбранный с помощью переменной LC_MESSAGES. Другие создают имя message catalog'a из значения переменной TEXTDOMAIN, возможно добавляя суффикс '.mo'. Если ты используешь переменную TEXTDOMAIN, тебе может потребоваться установить в переменную TEXTDOMAINDIR расположение файла message catalog'a. Некоторые все еще используют обе переменные таким образом: TEXTDOMAINDIR/LC_MESSAGES/LC_MESSAGES/TEXTDOMAIN.mo.


3.1.3 Комментарии

В неинтерактивном шелле или интерактивном, но с включенной shopt опцией interactive_comments, слова начинающиеся с '#' и вся дальнейшая строка за этим словом будет игнорироваться. В интерактивном шелле без опции interactive_comments комментарии не разрешены. Опция interactive_comments по умолчанию включена.

root@mars:~# shopt interactive_comments 
interactive_comments	off

root@mars:~# echo niga # pidor
niga # pidor

root@mars:~# shopt -s interactive_comments 

root@mars:~# shopt interactive_comments 
interactive_comments	on

root@mars:~# echo niga # pidor
niga

3.2 Команды Шелла

Такие простые команды как echo a b c состоят из самой команды и следующих за ней аргументов разделенных пробелами.

Множество комплексных шелл команд состоят из простых команд объединенных вместе различными способами: пайплайном в котором вывод одной команды входит в ввод следующей, циклом или условной конструкцией, или каким-то другим способом.


3.2.1 Зарезервированные Слова

Зарезервированные слова это слова которые имеют специальное значение для шелла. Они используются для начала и окончания составных команд.

Следующие слова известны как зарезервированные когда они незаковычены и являются первым словом в команде:

if then elif else fi time
for in until while do done
case esac coproc select function
{ } [[ ]] !

in распознается как зарезервированное слово если оно третье после команды case или select. in и do распознаются как зарезервированные слова если они третьи после команды for.


3.2.2 Простые Команды

Простые команды это вид команд который встречается чаще всего. Это просто последовательность слов разделенная с помощью blanks и заканчивающаяся одним из контрольных операторов шелла. Первое слово обычно определяет выполняемую команду, оставшиеся слова будут аргументами команды.

Статускод простой команды это exit status предоставляемый функцией waitpid стандарта POSIX 1003.1, или 128+n если команда была завершена сигналом n.


3.2.3 Пайплайны

Пайплайн это последовательность из одной или более команд разделенных операторами управления '|' или '|&'.

Формат пайплайнов

[time [-p]] [!] command1 [ | or |& command2 ] …

Вывод каждой команды в пайплайне соединяется через пайп с вводом следующей команды. То есть каждая команда читает аутпут предыдущей команды. Это соединение устанавливается до каких-либо перенаправлений определенных командой.

Если используется |&, то stderr вместе с stdout command1, подключается к stdin command2 через пайп; это сокращение для 2>&1 |. Это подразумевает что перенаправление stderr в stdout будет выполнено после редиректов определенных командой.

Я не понял два абзаца выше, поэтому вот оригинал:

The output of each command in the pipeline is connected via a pipe to the input of the next command. That is, each command reads the previous command’s output. This connection is performed before any redirections specified by the command.

If ‘|&’ is used, command1’s standard error, in addition to its standard output, is connected to command2’s standard input through the pipe; it is shorthand for 2>&1 |. This implicit redirection of the standard error to the standard output is performed after any redirections specified by the command.

Зарезервированное слово time выводит статистику времени выполнения пайплайна. Статистика содержит затраченное время и время в user и system спейсах. Опция -p изменяет формат вывода на определенный POSIX'ом. Когда шелл в POSIX режиме ...

OpenSSL

OpenSSL

OpenSSL

http://www.opennet.ru/base/sec/openssl.txt.html
RFC про PKI: https://tools.ietf.org/html/rfc5280


Рекомендуется использовать бинари одной версии
Иначе все может сломаться и раздебажить будет крайне сложно


Перед началом работы нужно понимать какая версия openssl у тебя установлена

root@postgres-client:~# openssl version
OpenSSL 1.1.1d  10 Sep 2019

Ключ -a покажет много дополнительной информации
Например OPENSSLDIR в которую будет ходить openssl за конфигурацией и сертами

-d - покажет только ssldir

root@postgres-client:~# openssl version -d
OPENSSLDIR: "/usr/lib/ssl"
root@postgres-client:~# ls -lh /usr/lib/ssl
total 4.0K
lrwxrwxrwx 1 root root   14 Feb 17 01:08 certs -> /etc/ssl/certs
drwxr-xr-x 2 root root 4.0K Mar  4 12:48 misc
lrwxrwxrwx 1 root root   20 Feb 17 01:08 openssl.cnf -> /etc/ssl/openssl.cnf
lrwxrwxrwx 1 root root   16 Feb 17 01:08 private -> /etc/ssl/private

Обычно в ней каходятся ссылки на /etc/ssl/
Так как это дефолтная папка для сертов в линуксе

Папка misc содержит несколько скриптов, некоторые из них нужны для создания CA


Скачать исходники можно тут
https://www.openssl.org/source/

Простейшая сборка

./config
make
make install

config - имеет разные параметры, можно собрать под себя

После сборки и установки у меня стало доступно два openssl

root@postgres-client:~# /usr/bin/openssl version
OpenSSL 1.1.1d  10 Sep 2019
root@postgres-client:~# /usr/local/bin/openssl version
OpenSSL 1.1.1j  16 Feb 2021 (Library: OpenSSL 1.1.1d  10 Sep 2019)

Папки private/ и certs/ изначально пустые, это потому что изначально у вас еще нет приватных ключей и доверенных сертификатов
Но так как это лишь ссылки на такие же папки в /etc/, то в certs уже могут быть доверенные сертификаты которые туда положила ОС


openssl это криптографический тулкит, который состоит из множества различных утилит (ниже видно насколько их много)

root@postgres-client:~# /usr/local/bin/openssl help
Standard commands
asn1parse         ca                ciphers           cms               
crl               crl2pkcs7         dgst              dhparam           
dsa               dsaparam          ec                ecparam           
enc               engine            errstr            gendsa            
genpkey           genrsa            help              list              
nseq              ocsp              passwd            pkcs12            
pkcs7             pkcs8             pkey              pkeyparam         
pkeyutl           prime             rand              rehash            
req               rsa               rsautl            s_client          
s_server          s_time            sess_id           smime             
speed             spkac             srp               storeutl          
ts                verify            version           x509              

Message Digest commands (see the `dgst' command for more details)
blake2b512        blake2s256        gost              md4               
md5               mdc2              rmd160            sha1              
sha224            sha256            sha3-224          sha3-256          
sha3-384          sha3-512          sha384            sha512            
sha512-224        sha512-256        shake128          shake256          
sm3               

Cipher commands (see the `enc' command for more details)
aes-128-cbc       aes-128-ecb       aes-192-cbc       aes-192-ecb       
aes-256-cbc       aes-256-ecb       aria-128-cbc      aria-128-cfb      
aria-128-cfb1     aria-128-cfb8     aria-128-ctr      aria-128-ecb      
aria-128-ofb      aria-192-cbc      aria-192-cfb      aria-192-cfb1     
aria-192-cfb8     aria-192-ctr      aria-192-ecb      aria-192-ofb      
aria-256-cbc      aria-256-cfb      aria-256-cfb1     aria-256-cfb8     
aria-256-ctr      aria-256-ecb      aria-256-ofb      base64            
bf                bf-cbc            bf-cfb            bf-ecb            
bf-ofb            camellia-128-cbc  camellia-128-ecb  camellia-192-cbc  
camellia-192-ecb  camellia-256-cbc  camellia-256-ecb  cast              
cast-cbc          cast5-cbc         cast5-cfb         cast5-ecb         
cast5-ofb         des               des-cbc           des-cfb           
des-ecb           des-ede           des-ede-cbc       des-ede-cfb       
des-ede-ofb       des-ede3          des-ede3-cbc      des-ede3-cfb      
des-ede3-ofb      des-ofb           des3              desx              
idea              idea-cbc          idea-cfb          idea-ecb          
idea-ofb          rc2               rc2-40-cbc        rc2-64-cbc        
rc2-cbc           rc2-cfb           rc2-ecb           rc2-ofb           
rc4               rc4-40            seed              seed-cbc          
seed-cfb          seed-ecb          seed-ofb          sm4-cbc           
sm4-cfb           sm4-ctr           sm4-ecb           sm4-ofb  

Вырезка выше состоит из нескольких частей:


openssl не поставляет с собой каких либо корневых сертов
Но, как описано выше, можно использовать ОСные серты, это считается нормой

Но они могут быть устаревшими, например когда мы установили ультрадревний дистрибутив

Есть вот такой регулярно обновляемый сертстор от мозиллы:
https://hg.mozilla.org/mozilla-central/raw-file/tip/security/nss/lib/ckfw/builtins/certdata.txt

Но он в их личном формате, что может быть не очень удобно

Есть еще от курла:
http://curl.haxx.se/docs/caextract.html

Он в формате PEM (Privacy-Enhanced Mail)

Но если сильно хочется мозилловский, то есть масса конвертеров на разных языках

Perl: https://raw.githubusercontent.com/bagder/curl/master/lib/mk-ca-bundle.pl

root@postgres-client:~# perl mk-ca-bundle.pl 
SHA256 of old file: 0
Downloading certdata.txt ...
Get certdata with curl!
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1141k  100 1141k    0     0   125k      0  0:00:09  0:00:09 --:--:--  203k
Downloaded certdata.txt
SHA256 of new file: 3bdc63d1de27058fec943a999a2a8a01fcc6806a611b19221a7727d3d9bbbdfd
Processing  'certdata.txt' ...
Done (129 CA certs processed, 20 skipped).

Он сам скачает и создаст ca-bundle.crt в котором куча сертов

Можно в крон засунуть и он будет апдейтить


Создание сертификата состоит из нескольких этапов:

Прежде чем генерировать приватный ключ нужно принять несколько решений:

Для генерации rsa ключа:

root@mars:/var/tmp/test# /usr/local/bin/openssl genrsa -aes128 -out fd.key 16384
Generating RSA private key, 16384 bit long modulus (2 primes)
........................+++
..........................................................................................................................................................................................+++
e is 65537 (0x010001)
Enter pass phrase for fd.key:
Verifying - Enter pass phrase for fd.key:

root@mars:/var/tmp/test# ls -lh
total 16K
-rw------- 1 root root 13K Mar 15 14:00 fd.key

Выше я сгенерил максимально длинный (можно и длинее, но не рекомендуется)

Может показаться что ключ это просто кусок рандомных данных, на самом деле у него есть структура которую можно посмотреть так:
(на примере более короткого ключа, у длинного который выше слишком длинный вывод)

root@mars:/var/tmp/test# openssl rsa -in small.key -noout -text 
Enter pass phrase for small.key:
RSA Private-Key: (512 bit, 2 primes)
modulus:
    00:92:8f:7d:3f:54:d1:ea:4a:e9:5f:25:f4:24:5e:
    3d:35:60:75:63:1b:20:4c:29:9a:c5:69:a7:4d:2b:
    f2:e6:8e:04:c7:74:86:d8:ec:55:5b:1a:18:fa:1d:
    4d:2e:68:b0:3c:b3:e1:a3:d3:af:00:81:89:0b:3b:
    40:bb:4c:0e:21
publicExponent: 65537 (0x10001)
privateExponent:
    57:44:f9:df:6c:32:52:c0:a8:95:ef:93:a0:d4:3c:
    be:3b:e1:aa:51:cc:b9:8d:4c:8b:a0:8d:ee:75:ec:
    fd:3b:4d:0e:f7:b1:af:ba:c3:ee:67:30:b8:ea:2a:
    51:04:9e:e1:e1:a8:76:19:88:44:8c:55:60:78:cd:
    3c:8e:0d:c1
prime1:
    00:c2:16:d7:a0:25:17:1e:a1:37:56:3c:b4:ac:53:
    42:66:d8:89:b6:6e:5c:bc:4d:d5:68:f9:f9:79:b6:
    59:40:4f
prime2:
    00:c1:4f:7e:24:af:8d:80:59:3f:a8:2f:1d:74:7d:
    37:65:5f:8a:fa:e0:39:22:fe:1b:f2:d9:a3:b8:9a:
    c2:3e:8f
exponent1:
    57:3d:91:3a:88:b1:75:b1:1d:80:83:d4:ea:48:9a:
    66:44:1a:d2:87:94:e3:87:50:cf:d8:dc:cc:5a:ac:
    8f:7d
exponent2:
    2d:ee:89:76:86:10:05:33:2f:a0:f2:b0:f9:00:d7:
    ac:ca:aa:e7:39:67:da:1c:ae:df:61:3f:39:99:ec:
    15:99
coefficient:
    48:dd:15:cd:36:d6:72:cd:a8:47:9d:36:8b:b9:b8:
    9b:3f:63:ee:02:0a:22:e9:a6:95:6c:aa:ba:4c:52:
    ab:62

Вытащить из него публичный можно так:

root@mars:/var/tmp/test# openssl rsa -in small.key -pubout -out small.pub
Enter pass phrase for small.key:
writing RSA key

root@mars:/var/tmp/test# ls -lh
total 24K
-rw------- 1 root root 576 Mar 15 14:09 small.key
-rw-r--r-- 1 root root 182 Mar 15 14:24 small.pub

"Вытащить" это условно, приватный ключ содержит в себе компоненты (некоторые из которых нужны для ускорения шифрования/расшифрования)
Из этих компонентов можно получить публичный ключ (в случае rsa, у dsa другой механизм, что там да как я не знаю)

openssl rsa -in private.pem -text -noout | less

modulus         - n
privateExponent - d
publicExponent  - e
prime1          - p
prime2          - q
exponent1       - d mod (p-1)
exponent2       - d mod (q-1)
coefficient     - (q^-1) mod p

Поэтому DSA используется для электронной подписи и процесс генерации у него иной:

openssl dsaparam -genkey 2048 | openssl dsa -out dsa.key -aes128
  Generating DSA parameters, 2048 bit long prime
  This could take some time
  [...]
  read DSA key
  writing DSA key
  Enter PEM pass phrase: ****************
  Verifying - Enter PEM pass phrase: ****************

Как видно для разных алгоритмов используются разные команды (генерация rsa не похожа на генерацию dsa, легко запутаться)
Чтобы экономить время можно использовать универсальную команду openssl genpkey для генерации приватных ключей


Имея приватный ключ можно сгенерировать CSR
Ты формируешь что-то типо сертификата и отправляешь его на подпись в CA, чтобы он стал доверенным

root@mars:/var/tmp/test# openssl genrsa -out test.key 2048 ^C
root@mars:/var/tmp/test# openssl rsa -in test.key -pubout -out test.pub ^C
root@mars:/var/tmp/test# openssl req -new -key test.key -out test.csr ^C
root@mars:/var/tmp/test# openssl req -in test.csr -noout -text
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = RU, L = Saint-Petersburg, O = Vandud Ltd., CN = vandud.ru, emailAddress = test@vandud.ru
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:ae:ce:40:5c:7a:ca:47:07:e7:8d:dc:e0:1e:91:
                    ba:e8:f5:61:66:ac:3c:c2:91:02:e0:68:92:29:ba:
                    01:a7:53:a1:2c:5a:eb:42:fc:37:cb:22:5b:41:92:
                    4d:b4:00:40:bd:18:52:7c:30:46:f9:03:97:06:d0:
                    2c:72:c6:26:49:be:ac:21:a1:48:fc:0d:33:44:2f:
                    8f:fe:ff:86:80:7f:d4:55:3e:9f:1d:cb:98:03:df:
                    3e:22:d4:47:a5:cf:2f:ea:9f:01:4b:e4:ac:9c:38:
                    48:33:e6:8e:16:6f:63:94:7d:33:04:ce:df:55:7c:
                    e8:42:d8:3c:cd:54:ad:44:44:ba:46:9b:91:72:9f:
                    55:8e:bc:a5:17:6c:e1:fb:1e:63:db:43:fc:0f:b1:
                    bf:7c:30:40:6e:92:2d:e5:f7:0a:3d:fe:4d:51:d2:
                    a6:4c:fb:34:43:3d:b8:d0:1d:80:31:d5:82:b1:8d:
                    61:4e:6e:c6:ad:79:28:d4:5f:b9:c9:c1:bc:23:34:
                    6d:8c:31:19:d8:42:ca:21:81:ff:c4:e5:2e:9a:b0:
                    21:80:52:36:89:6c:17:d6:f2:b5:ec:0a:6b:1c:eb:
                    19:41:20:59:ce:58:45:22:0a:05:de:a6:1f:82:d5:
                    31:fb:3d:7e:65:d6:fb:38:9d:f8:39:da:87:7c:72:
                    56:01
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha256WithRSAEncryption
         98:ea:53:c2:dd:00:0a:e1:0d:10:0e:f5:85:b4:84:2b:99:ed:
         69:f9:3e:af:2b:d3:22:83:84:c1:64:47:b1:8e:40:3d:78:ce:
         8c:a0:3a:b1:35:1c:3c:97:a2:e7:63:cf:cd:58:1b:29:c6:97:
         ca:2c:09:67:08:6d:f1:8a:a3:80:c3:de:27:0a:cf:5e:77:a0:
         38:94:8f:0d:0d:32:f0:40:da:5c:c6:de:a7:a8:a8:f2:45:60:
         ce:a6:be:50:29:1b:08:21:e6:06:4a:b7:84:1f:87:2b:a3:b6:
         18:ca:ef:7f:a8:33:60:9a:fe:32:18:a4:87:4b:e0:31:e6:de:
         87:10:33:b5:9e:f7:4e:ab:fc:a4:53:88:34:74:cd:1b:e7:71:
         11:60:6b:e8:e5:66:9e:43:55:4f:e5:fa:56:35:6b:25:e0:7c:
         cf:a0:94:d0:ed:66:31:29:bd:46:2b:46:8e:13:f3:d0:99:8f:
         5d:e6:bf:84:84:9a:fb:89:b7:e7:be:d9:65:02:66:4c:a5:12:
         f4:f8:fe:b7:ec:cc:59:2d:af:1c:0f:75:b0:57:b5:f8:66:36:
         12:16:49:ab:1e:9e:c5:bd:1d:15:7c:f9:34:f0:21:fa:0f:3f:
         31:2f:d8:73:33:7f:89:01:6c:af:fd:3a:b0:61:d7:7a:68:83:
         27:ce:f7:34

Генерация CSR это интерактивный процесс (то есть ты отвечаешь на вопросы)
В какой-то момент предлагается ввести пароль для CSR, его рекомендуется опустить, так как безопасности это не прибавляет, а проблем может доставить

CSR с дефолтными значениями (если ничего не вписывать, а просто щелкать enter, то openssl проставит свои дефолты, если все таки хочется оставить поле пустым то нужно вписать точку):

root@mars:/var/tmp/test# openssl req -new -key test.key -out test1.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
root@mars:/var/tmp/test# openssl req -in test1.csr -noout -text
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:c4:48:03:11:5c:96:7a:5a:76:12:d2:65:16:e9:
                    a9:43:46:31:fb:67:83:62:e2:6b:62:b5:a9:7e:d1:
                    09:de:f5:22:2b:ce:20:38:3e:b9:50:87:0d:7e:b8:
                    90:11:07:ed:eb:85:2a:0a:eb:15:fb:17:83:39:28:
                    58:a1:0e:cc:5f:70:04:dc:f4:78:f2:cb:27:88:62:
                    cd:67:16:ca:42:c2:a5:a0:11:8b:35:10:c8:86:80:
                    12:b0:72:00:bb:8d:d5:b5:47:72:50:4f:81:ca:29:
                    72:c3:4f:1a:22:be:86:dc:2d:b9:b7:3b:59:32:45:
                    66:c5:7c:a3:26:37:fc:97:c3:ea:cc:1d:e5:ca:58:
                    7d:fa:53:0e:53:f3:94:8c:80:b7:8d:17:0a:6f:d1:
                    f0:cf:e9:47:bf:7a:69:62:82:90:4d:72:93:94:1d:
                    00:d8:9b:a6:57:e6:a8:e1:ad:26:5b:54:49:10:f9:
                    05:ed:8a:cd:93:20:de:76:a1:c3:e0:79:8e:01:7a:
                    96:5c:bf:e7:13:1a:68:f7:b8:30:07:c6:ce:7a:28:
                    1e:6e:82:ab:5a:98:f4:9b:a3:17:35:56:52:49:12:
                    73:41:2d:a8:d7:00:b1:02:52:5d:56:eb:f2:ca:d2:
                    4a:44:18:ab:c9:55:b3:38:37:09:64:87:e9:7b:fb:
                    ca:a7
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha256WithRSAEncryption
         4f:f8:28:32:3b:e4:09:1c:58:1d:7c:00:bf:92:3d:d2:18:e9:
         50:12:a4:29:83:fe:be:a4:02:79:12:d1:04:4c:7f:ae:3d:ba:
         45:c7:53:14:11:d3:b5:91:5f:a5:83:fb:84:d6:2e:3b:a7:7d:
         5c:e5:18:48:e0:d6:17:7a:55:f1:31:71:96:6b:51:a0:1d:a1:
         99:f8:e1:94:1e:02:c0:86:56:f1:7f:1e:73:42:16:dd:55:fb:
         19:af:37:81:35:27:96:97:b1:35:ef:a3:0d:94:87:ea:d8:3c:
         c4:13:c0:86:38:00:94:45:99:e8:6d:be:26:47:13:ee:cc:28:
         bb:b8:5b:0a:71:44:76:92:39:bd:8b:55:0e:35:c1:30:8b:5e:
         7d:7b:ba:7b:57:cb:8e:1f:30:73:06:cf:d1:6d:69:9e:14:84:
         b3:82:19:56:73:88:8f:24:1b:21:ff:58:b6:49:dd:cf:7c:0b:
         a2:84:fb:d6:67:db:7e:5e:1a:f3:8f:00:40:04:ad:7f:2c:2f:
         c8:a9:5a:b8:78:73:4e:ce:79:12:64:ae:44:cf:27:d1:54:f9:
         5e:1d:d9:b9:ed:0e:44:08:e8:9b:b9:2e:cc:6a:13:1e:47:ab:
         7d:11:d4:e7:8a:16:9d:14:ac:12:f3:e7:37:13:4e:8c:78:b3:
         bd:9d:74:f8

Дальше этот CSR отправляем на подпись в CA и получаем в ответ сертификат


csr to crt

root@mars:/var/tmp/test# openssl x509 -req -in test.csr -signkey test.key -out test.crt
Signature ok
subject=C = RU, L = Saint-Petersburg, O = Vandud Ltd., CN = vandud.ru, emailAddress = test@vandud.ru
Getting Private key

Для перевыпуска можно не писать кучу команд, а сформировать CSR на основе старого сертификата (в котором можно заменить ключ)

openssl x509 -x509toreq -in fd.crt -out fd.csr -signkey fd.key

Можно автоматизировать генерацию CSR с помощью конфигa:

root@mars:/var/tmp/test# cat test.conf 
[req]
prompt = no
distinguished_name = dn
req_extensions = ext
[dn]
CN = www.vandudwithconf.com
emailAddress = webmaster@feistyduck.com
O = Feisty Duck Ltd
L = London
C = GB
[ext]
subjectAltName = DNS:www.feistyduck.com,DNS:feistyduck.com


root@mars:/var/tmp/test# openssl req -new -config test.conf -key test.key -out test3.csr


root@mars:/var/tmp/test# openssl req -in test3.csr -noout -text
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: CN = www.vandudwithconf.com, emailAddress = webmaster@feistyduck.com, O = Feisty Duck Ltd, L = London, C = GB
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:c4:48:03:11:5c:96:7a:5a:76:12:d2:65:16:e9:
                    a9:43:46:31:fb:67:83:62:e2:6b:62:b5:a9:7e:d1:
                    09:de:f5:22:2b:ce:20:38:3e:b9:50:87:0d:7e:b8:
                    90:11:07:ed:eb:85:2a:0a:eb:15:fb:17:83:39:28:
                    58:a1:0e:cc:5f:70:04:dc:f4:78:f2:cb:27:88:62:
                    cd:67:16:ca:42:c2:a5:a0:11:8b:35:10:c8:86:80:
                    12:b0:72:00:bb:8d:d5:b5:47:72:50:4f:81:ca:29:
                    72:c3:4f:1a:22:be:86:dc:2d:b9:b7:3b:59:32:45:
                    66:c5:7c:a3:26:37:fc:97:c3:ea:cc:1d:e5:ca:58:
                    7d:fa:53:0e:53:f3:94:8c:80:b7:8d:17:0a:6f:d1:
                    f0:cf:e9:47:bf:7a:69:62:82:90:4d:72:93:94:1d:
                    00:d8:9b:a6:57:e6:a8:e1:ad:26:5b:54:49:10:f9:
                    05:ed:8a:cd:93:20:de:76:a1:c3:e0:79:8e:01:7a:
                    96:5c:bf:e7:13:1a:68:f7:b8:30:07:c6:ce:7a:28:
                    1e:6e:82:ab:5a:98:f4:9b:a3:17:35:56:52:49:12:
                    73:41:2d:a8:d7:00:b1:02:52:5d:56:eb:f2:ca:d2:
                    4a:44:18:ab:c9:55:b3:38:37:09:64:87:e9:7b:fb:
                    ca:a7
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name: 
                DNS:www.feistyduck.com, DNS:feistyduck.com
    Signature Algorithm: sha256WithRSAEncryption
         b8:f9:45:9e:2c:af:80:7b:06:3a:eb:77:3b:86:b6:16:2d:bb:
         71:56:c4:fd:e4:25:db:39:be:3f:3e:45:32:f9:df:61:dd:0d:
         ab:51:42:f0:ad:ba:25:42:5b:49:14:e0:ad:2d:9c:14:35:93:
         ea:ea:4c:96:ad:da:20:95:fd:ea:4e:4f:05:a5:64:d0:37:18:
         98:60:91:3a:15:aa:15:3a:80:df:44:d6:57:6c:a8:16:6d:ba:
         ff:ba:b3:42:84:5c:86:8c:75:70:08:d9:b1:88:d8:97:10:fd:
         5c:d2:28:9a:64:c7:1a:6a:ee:f3:2b:4a:f7:40:74:e3:ef:7c:
         f1:fc:f2:c9:97:c2:97:4b:50:7a:1a:4f:e7:a2:96:12:65:db:
         75:24:0b:0c:f4:0c:a8:6f:66:9c:3f:16:27:9e:d4:20:15:ca:
         6a:36:58:0a:40:82:c8:22:e5:ac:6e:08:17:83:1f:8a:2b:64:
         ba:4c:c1:74:40:91:4c:00:8f:f4:1c:92:75:ba:4a:26:55:48:
         3c:76:72:52:3f:33:2e:e5:22:bd:01:33:ac:78:9f:70:68:54:
         24:d1:3b:d2:2e:92:a4:db:34:6f:be:ec:ff:cd:6d:08:6a:62:
         88:07:81:12:b3:52:2f:33:cb:d6:71:0a:6a:b6:f5:85:7a:1c:
         26:51:dd:a5

Сгенерить самосерт так:

root@mars:~/nginx# openssl genrsa -out vandud.key 4096 
root@mars:~/nginx# openssl req -new -x509 -days 10 -key vandud.key -out vandud.crt 

Дефолтный серт поддерживает один хостнейм
Но в наши дни такой подход не годится, требуется мультидоменность

Расширения X509 позволяет это сделать

X509 дает еще массу полезных возможностей
Например CRL и механизм проверки пути сертификации

Когда серт имеет alternative names, common names игнорятся. Поэтому нужно перечислять все домены в altnames

Можно перечислить все расширения которые тебе нужны в текстовом файле

Пример:

root@mars:/var/tmp/test# cat test.ext 
subjectAltName = DNS:ext.vandud.ru, DNS:*.vandud.ru


# Подписываю CSR
root@mars:/var/tmp/test# openssl x509 -req -in test.csr -signkey ca.key -out test.crt -extfile test.ext 
Signature ok
subject=C = RU, ST = SAINT-P., L = SAINT-P., O = Sub Unit, OU = au, CN = auo.aeu.au
Getting Private key


# Получили Subject Alternative Name в серте
root@mars:/var/tmp/test# openssl x509 -in test.crt -noout -text | grep X509 -A1
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:ext.vandud.ru, DNS:*.vandud.ru

У сертификатов есть basic constraints, оно указывет пренадлежит ли серт CA и годен ли он для подписи других сертов
У не CA сертов это расширение опущено или установлено в FALSE

X509v3 Basic Constraints: critical
      CA:FALSE

Key Usage (KU) и Extended Key Usage (EKU) ограничивают места применения серта
Серты могут использоваться для подписи других сертов, для подписи файлов, для подписи ...

Расширение CRL Distribution Points указывет где можно найти CRL этого CA

root@mars:/var/tmp/test# wget crl.starfieldtech.com/sfs3-20.crl -q

root@mars:/var/tmp/test# openssl crl -in sfs3-20.crl -inform der -noout -text
Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = Arizona, L = Scottsdale, O = "Starfield Technologies, Inc.", OU = http://certificates.starfieldtech.com/repository, CN = Starfield Secure Certification Authority, serialNumber = 10688435
        Last Update: Mar 16 12:05:46 2021 GMT
        Next Update: Mar 23 12:05:46 2021 GMT
        CRL extensions:
            X509v3 Authority Key Identifier: 
                keyid:49:4B:52:27:D1:1B:BC:F2:A1:21:6A:62:7B:51:42:7A:8A:D7:D5:56
                DirName:/C=US/O=Starfield Technologies, Inc./OU=Starfield Class 2 Certification Authority
                serial:02:01
            X509v3 CRL Number: 
                3588
            X509v3 Issuing Distribution Point: critical
                Full Name:
                  URI:http://crl.starfieldtech.com/sfs3-20.crl
No Revoked Certificates.
    Signature Algorithm: sha256WithRSAEncryption
         75:2f:f7:ba:da:67:88:7d:f8:a5:02:9c:9d:0d:8c:4b:e9:6c:
         56:2c:b7:77
         ...
         :78:76:9e:d1:5e:f9:6b:ab:a3:47:d0:fd:45:4a:
         fe:1e:de:6c

CRL распрастраняются по незащищенному соединению неспроста
За безопасность беспокоиться не стоит, потому что файл CRL подписан CA которому пренадлежит этот CRL
А распрастранять его по https нельзя, потому что может возникнуть проблема курицы и яйца (чтобы установить https подключение для получения crl нужно иметь этот crl)

CRL обновляется регулярно, обычно еженедельно

[22:36:30] vandud@macbook: tmp [0]$ openssl crl -in gds1-31.crl -inform DER -text -noout
Certificate Revocation List (CRL):
        Version 2 (0x1)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=07969287
        Last Update: Mar 28 15:41:03 2021 GMT
        Next Update: Apr  4 15:41:03 2021 GMT
        CRL extensions:
            X509v3 Authority Key Identifier:
                keyid:FD:AC:61:32:93:6C:45:D6:E2:EE:85:5F:9A:BA:E7:76:99:68:CC:E7
                DirName:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
                serial:03:01

            X509v3 CRL Number:
                3748
            X509v3 Issuing Distribution Point: critical
                Full Name:
                  URI:http://crl.godaddy.com/gds1-31.crl

No Revoked Certificates.
    Signature Algorithm: sha256WithRSAEncryption
         bd:9e:86:f8:bf:5d:3a:0f:12:b2:b3:48:a1:0f:88:96:43:f4:
         d7:ea:dc:73:a5:44:0b:c7:e6:d6:fc:95:52:66:bc:8b:a9:02:
         d3:73:68:dc:f5:fd:e3:f1:0a:c7:c7:da:5f:7e:47:8f:8e:44:
         ad:53:79:b9:75:b1:98:f6:74:b9:65:bd:40:51:fe:1f:20:b6:
         50:a7:63:2f:1e:83:00:f2:94:4a:48:3c:df:4e:0b:8f:6c:75:
         c2:d8:cc:2b:75:c7:61:ad:a3:2b:7d:78:d7:69:cf:8a:8b:11:
         3b:57:23:2a:53:c3:f3:9e:6e:a2:53:c1:d6:2c:5d:da:e3:0c:
         38:85:f7:24:16:0e:47:d9:f7:76:87:ad:ae:63:2d:46:2a:34:
         0e:ba:16:4a:07:76:60:34:e4:07:d0:84:a4:0e:46:6e:95:1a:
         26:0b:cf:f0:e9:d0:53:8b:4f:ce:ec:86:04:13:9d:e1:1f:58:
         d3:a1:d0:6a:ad:23:a6:1a:a5:77:6e:b0:eb:a3:a8:ce:e9:c4:
         a6:ca:a8:31:d6:2e:3a:46:9b:e9:1f:d7:9f:20:76:75:13:eb:
         f2:fd:af:15:b4:db:a4:d0:be:a7:39:73:51:5a:82:c0:b2:2f:
         e2:bc:dd:03:cc:aa:02:9e:c3:db:0a:3a:d6:27:eb:9e:1c:9d:
         f4:44:a5:79

Certificate Policies и CPS (certificate policy statment)

X509v3 Certificate Policies:
Policy: 2.16.840.1.114413.1.7.23.1
CPS: https://certs.godaddy.com/repository/

Это расширение используется для указания политики под которой был выдан серт
Например это то место по которому бразуер понимер DV, OV или EV серт перед ним

Браузер понимает что перед ним EV сертификат благодаря то что в коде браузера зашиты OID'ы
https://hg.mozilla.org/mozilla-central/file/eab4a81e4457/security/certverifier/ExtendedValidation.cpp
Firefox_2021-03-28-23-50-56.png

Authority Information Access (AIA) - Это расширение обычно содержит два важных куска информации

  1. Список адресов OCSP респондеров центра сертификации (Online Certificate Status Protocol) которые могут быть использованы для проверки того отозван ли серт в реальном времени
  2. Также это расширение может содержать ссылку на сертификат эмитента (следующий в цепочке серт)
Authority Information Access:
    CA Issuers - URI:http://crt.usertrust.com/GandiStandardSSLCA2.crt
    OCSP - URI:http://ocsp.usertrust.com

Пользователи часто ошибаются (или забыват) и кладут невалидные серты в цепочку, браузер благодаря этому расширению может самостоятельно собрать валидную цепочку

Subject Key Identifier и Authority Key Identifier это уникальные идентификаторы которые помогают выбрать нужный серт для построения цепочки
Дело в том что ЦА часто используют один приватный ключ в множестве сертификатов
В реальном мире много цепочек поставляемых серверами - невалидные, но это не мешает браузерам строить правильные цепочки самостоятельно по альтернативным путям

root@mars:/var/tmp/a# echo | openssl s_client -connect online.sberbank.ru:443 -servername online.sberbank.ru > online.sberbank.ru.crt 2>/dev/null
root@mars:/var/tmp/a# openssl x509 -in online.sberbank.ru.crt -noout -ext authorityInfoAccess
Authority Information Access:
    OCSP - URI:http://status.thawte.com
    CA Issuers - URI:http://cacerts.thawte.com/ThawteEVRSACA2018.crt

root@mars:/var/tmp/a# wget http://cacerts.thawte.com/ThawteEVRSACA2018.crt -q
root@mars:/var/tmp/a# openssl x509 -in ThawteEVRSACA2018.crt -inform der -out ThawteEVRSACA2018.pem.crt -outform pem
root@mars:/var/tmp/a# openssl x509 -in online.sberbank.ru.crt -noout -ext authorityKeyIdentifier
X509v3 Authority Key Identifier:
    keyid:E7:01:FC:0C:16:18:CA:7D:B2:8C:EC:87:27:A3:6F:61:81:3B:84:39

root@mars:/var/tmp/a# openssl x509 -in ThawteEVRSACA2018.pem.crt -noout -ext subjectKeyIdentifier
X509v3 Subject Key Identifier:
    E7:01:FC:0C:16:18:CA:7D:B2:8C:EC:87:27:A3:6F:61:81:3B:84:39

Subject Alternative Name это расширение позволяет перечислить все имена для которых этот сертификат валиден
Если его нет то используется поле CN (Common Name) из Subject
Если расширение Subject Alt Name есть, то Common Name игнорируется и инфа берется только из расширения

root@mars:/var/tmp/a# openssl x509 -in online.sberbank.ru.crt -noout -subject
subject=businessCategory = Private Organization, jurisdictionC = RU, serialNumber = 1027700132195, C = RU, L = Moscow, O = Sberbank of Russia, CN = online.sberbank.ru
root@mars:/var/tmp/a# openssl x509 -in online.sberbank.ru.crt -noout -ext subjectAltName
X509v3 Subject Alternative Name:
    DNS:online.sberbank.ru

Ключи и сертификаты могут храниться в разных форматах, поэтому часто можно иметь необходимость преобразовывать одно в другое


Для конвертации из PEM в DER и наоборот есть очень простые команды

openssl x509 -inform PEM -in fd.pem -outform DER -out fd.der
openssl x509 -inform DER -in fd.der -outform PEM -out fd.pem

Для ключей то же самое

root@mars:/var/tmp/a# openssl rsa -in key.pem -inform PEM -out key.der -outform DER
writing RSA key
root@mars:/var/tmp/a# openssl rsa -in key.der -inform DER -out key.pem2 -outform PEM
writing RSA key
root@mars:/var/tmp/a# diff key.pem key.pem2

Чтобы засунуть PEM серт, ключ и промежуточные сертификаты в PFX

openssl pkcs12 -export \
  -name "My Certificate" \
  -out fd.p12 \
  -inkey fd.key \
  -in fd.crt \
  -certfile fd-chain.crt
Enter Export Password: ****************
Verifying - Enter Export Password: ****************

Вернуть обратно не так просто
Нужно выполнить команду которая преобразует все в PEM но засунет это в один файл

openssl pkcs12 -in fd.p12 -out fd.pem -nodes

А потом ты вручную все разделишь на серты и ключи

Есть еще такой вариант

$ openssl pkcs12 -in fd.p12 -nocerts -out fd.key -nodes
$ openssl pkcs12 -in fd.p12 -nokeys -clcerts -out fd.crt
$ openssl pkcs12 -in fd.p12 -nokeys -cacerts -out fd-chain.crt

Посмотреть доступные комплекты алгоритмов шифрования можно командой openssl ciphers, ключ -v для вербозности

root@mars:/var/tmp/a# openssl ciphers -v | head -3
TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD

Вывод содерджит:

1. Suite name 2. Required minimum protocol version 3. Key exchange algorithm 4. Authentication algorithm 5. Cipher algorithm and strength 6. MAC (integrity) algorithm 7. Export suite indicator
TLS_AES_256_GCM_SHA384 TLSv1.3 Kx=any Au=any Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any Au=any Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256 TLSv1.3 Kx=any Au=any Enc=AESGCM(128) Mac=AEAD

Можно передать какое-то ключевое слово для поискa:

root@mars:/var/tmp/a# openssl ciphers -v 'SSLv3'
TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
DHE-RSA-AES256-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA1
...

Есть имена групп, которыми обозначены некоторые часто импользуемые наборы шифров
Например по слову HIGH будут отобраны очень сильные шифры

root@mars:/var/tmp/a# openssl ciphers 'HIGH'
TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:...
Keyword Meaning
DEFAULT Список дефолтных шифров. Они определяются во время компиляции
COMPLEMENTOFDEFAULT То что есть в ALL но не включено в DEFAULT
ALL Все шифры кроме eNULL
COMPLEMENTOFALL То что не включено в ALL - eNULL
HIGH Сильнокриптильные шифры, сейчас это те у которых длина ключа больше 128 бит
MEDIUM Среднекриптильные шифры
LOW Слабокриптильные шифры, 64 или 56 битные алгоритмы
EXP, EXPORT
TLSv1,SSLv3,SSLv2 Тут понятно

Можно делать выборку по алгоритму дайджеста

root@mars:/var/tmp/a# openssl ciphers -v 'MD5'
TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
NULL-MD5                SSLv3 Kx=RSA      Au=RSA  Enc=None      Mac=MD5
Keyword Meaning
MD5 Cipher suites using MD5. Obsolete and insecure
SHA, SHA1 Cipher suites using SHA1
SHA256 (v1.0.0+) Cipher suites using SHA256
SHA384 (v1.0.0+) Cipher suites using SHA384
Authentication keyword Meaning
aDH Cipher suites effectively using DH authentication, i.e., the certificates carry DH keys.(v1.0.2+)
aDSS, DSS Cipher suites using DSS authentication, i.e., the certificates carry DSS keys
aECDH (v1.0.0+) Cipher suites that use ECDH authentication
aECDSA (v1.0.0+) Cipher suites that use ECDSA authentication
aNULL Cipher suites offering no authentication. This is currently the anonymous DH algo-rithms. Insecure
aRSA Cipher suites using RSA authentication, i.e., the certificates carry RSA keys
PSK Cipher suites using PSK (Pre-Shared Key) authentication
SRP Cipher suites using SRP (Secure Remote Password) authentication

Key exchange algorithms:

Keyword Meaning
ADH Anonymous DH cipher suites. Insecure
AECDH (v1.0.0+) UAnonymous ECDH cipher suites. Insecure
DH Cipher suites using DH (includes ephemeral and anonymous DH)
ECDH (v1.0.0+) Cipher suites using ECDH (includes ephemeral and anonymous ECDH)
EDH (v1.0.0+) Cipher suites using ephemeral DH key agreement
EECDH (v1.0.0+) Cipher suites using ephemeral ECDH
kECDH (v1.0.0+) Cipher suites using ECDH key agreement
kEDH Cipher suites using ephemeral DH key agreements (includes anonymous DH)
kEECDH (v1.0.0+) Cipher suites using ephemeral ECDH key agreement (includes anonymous ECDH)
kRSA, RSA Cipher suites using RSA key exchange

Cipher keywords:

Keyword Meaning
3DES Cipher suites using triple DES
AES Cipher suites using AES
AESGCM (v1.0.0+) Cipher suites using AES GCM
CAMELLIA Cipher suites using Camellia
DES Cipher suites using single DES. Obsolete and insecure
eNULL, NULL Cipher suites that don’t use encryption. Insecure
IDEA Cipher suites using IDEA
RC2 Cipher suites using RC2. Obsolete and insecure
RC4 Cipher suites using RC4. Insecure
SEED Cipher suites using SEED

Гостовские шифры

Keyword Meaning
@STRENGTH Sorts the current cipher suite list in order of encryption algorithm key length
aGOST Cipher suites using GOST R 34.10 (either 2001 or 94) for authentication. Requires aGOST-capable engine
aGOST01 Cipher suites using GOST R 34.10-2001 authentication
aGOST94 Cipher suites using GOST R 34.10-94 authentication. Obsolete. Use GOST R 34.10-2001 instead
kGOST Cipher suites using VKO 34.10 key exchange, specified in RFC 4357
GOST94 Cipher suites using HMAC based on GOST R 34.11-94
GOST89MAC Cipher suites using GOST 28147-89 MAC instead of HMAC

Для поиска можно комбинировать кейворды через '+'

root@mars:/var/tmp/a# openssl ciphers -v 'AES+RSA'
TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
AES256-GCM-SHA384       TLSv1.2 Kx=RSA      Au=RSA  Enc=AESGCM(256) Mac=AEAD
AES256-CCM8             TLSv1.2 Kx=RSA      Au=RSA  Enc=AESCCM8(256) Mac=AEAD
AES256-CCM              TLSv1.2 Kx=RSA      Au=RSA  Enc=AESCCM(256) Mac=AEAD
...

Через плюс выбирается "пересечение", а через двоеточие или пробел можно сложить два множества

root@mars:/var/tmp/a# openssl ciphers -v 'AES+RSA:AES+DH+SHA'
TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
AES256-GCM-SHA384       TLSv1.2 Kx=RSA      Au=RSA  Enc=AESGCM(256) Mac=AEAD
AES256-CCM8             TLSv1.2 Kx=RSA      Au=RSA  Enc=AESCCM8(256) Mac=AEAD
AES256-CCM              TLSv1.2 Kx=RSA      Au=RSA  Enc=AESCCM(256) Mac=AEAD
AES128-GCM-SHA256       TLSv1.2 Kx=RSA      Au=RSA  Enc=AESGCM(128) Mac=AEAD
AES128-CCM8             TLSv1.2 Kx=RSA      Au=RSA  Enc=AESCCM8(128) Mac=AEAD
AES128-CCM              TLSv1.2 Kx=RSA      Au=RSA  Enc=AESCCM(128) Mac=AEAD
AES256-SHA256           TLSv1.2 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA256
AES128-SHA256           TLSv1.2 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA256
AES256-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA1
AES128-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA1
DHE-RSA-AES256-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA1
DHE-DSS-AES256-SHA      SSLv3 Kx=DH       Au=DSS  Enc=AES(256)  Mac=SHA1
ADH-AES256-SHA          SSLv3 Kx=DH       Au=None Enc=AES(256)  Mac=SHA1
DHE-RSA-AES128-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA1
DHE-DSS-AES128-SHA      SSLv3 Kx=DH       Au=DSS  Enc=AES(128)  Mac=SHA1
ADH-AES128-SHA          SSLv3 Kx=DH       Au=None Enc=AES(128)  Mac=SHA1

Модификаторы
Есть несколько модификаторов с помощью которых можно формировать итоговый список шифров

Keyword Meaning
Append Добавит в конец списка. Если такой уже есть в списке, то изменит его положение в нем
Delete (-) Удалит все вхождения шифра в списке
Permanently delete (!) То же что выше, но в добавок еще и не даст добавить удаленный шифр в будущем
Move to the end (+) Переместит в конец списка. Полезно когда ты хочешь чтобы какой-то набор был включен, но использовался в последнюю очередь

Сортировать по силе шифра можно словом @STRENGTH
Это работает плохо, поэтому вопрос приоритетности различных алгоритмов лучше контролировать вручную


Есть две типичных ошибки которые можно получить при работе с утилитой

root@mars:/var/tmp/a# openssl ciphers -v 'DES-CBC-SHA'
Error in cipher list
139928168608896:error:1410D0B9:SSL routines:SSL_CTX_set_cipher_list:no cipher match:ssl/ssl_lib.c:2560:
root@mars:/var/tmp/a# openssl ciphers -v '@DES-CBC-SHA'
Error in cipher list
140285746746496:error:140E6118:SSL routines:ssl_cipher_process_rulestr:invalid command:ssl/ssl_ciph.c:1193:

Если присмотреться то видно:

  1. no cipher match
  2. invalid command

openssl имеет встроенную утилиту для бенчмаркинга speed
Вызов без агрументов выдаст большой вывод в котором мало интересного

root@three:~# openssl speed rc4 aes
Doing md4 for 3s on 16 size blocks: 10747405 md4's in 2.98s
Doing md4 for 3s on 64 size blocks: 9176936 md4's in 3.00s
Doing md4 for 3s on 256 size blocks: 5508233 md4's in 2.99s
Doing md4 for 3s on 1024 size blocks: ^C
root@three:~#

В конце тестинга выдаст такой вывод

...
OpenSSL 1.1.1d  10 Sep 2019
built on: Mon Mar 22 23:08:47 2021 UTC
options:bn(64,64) rc4(16x,int) des(int) aes(partial) blowfish(ptr)
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-AWQHHq/openssl-1.1.1d=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAESNI_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes  16384 bytes
rc4             353922.28k   541043.50k   623075.05k   643742.57k   658066.09k   672067.30k
aes-128 cbc     166991.50k   173247.44k   171517.44k   173914.79k   172188.17k   174058.15k
aes-192 cbc     145605.16k   148606.40k   148814.17k   149922.50k   147187.50k   155242.51k
aes-256 cbc     128802.94k   131946.26k   133212.90k   135339.69k   130652.81k   131705.51k

В первой части вывода содержится всякая инфа о сборке openssl
Это полезно для сравнения разных сборок и разных версий

Вторая часть содержит бенчмарк симетричной криптографии

А третья часть про ассиметричную криптографию

Не стоит полагаться только на результаты, в реальной жизни на перфоманс влияет множетсво факторов
Но можно на них полагаться для того чтобы прикинуть примерные эстимэйты

Также нужно учитывать что по умолчанию команда speed использует один процесс, а сервера имеют много ядер, поэтому можно указать сколько экземпляров speed'a запустить в параллель через опцию -multi

root@three:~# ps aux | grep multi
root     31984  0.0  0.2   6844  5000 pts/0    S+   07:44   0:00 openssl speed -multi 8 rc4 aes rsa ecdh sha
root     31985 24.8  0.1   6844  2940 pts/0    R+   07:44   0:23 openssl speed -multi 8 rc4 aes rsa ecdh sha
root     31986 24.8  0.1   6844  2940 pts/0    R+   07:44   0:23 openssl speed -multi 8 rc4 aes rsa ecdh sha
root     31987 24.8  0.1   6844  2940 pts/0    R+   07:44   0:23 openssl speed -multi 8 rc4 aes rsa ecdh sha
root     31988 24.8  0.1   6844  2940 pts/0    R+   07:44   0:23 openssl speed -multi 8 rc4 aes rsa ecdh sha
root     31989 24.8  0.1   6844  2940 pts/0    R+   07:44   0:23 openssl speed -multi 8 rc4 aes rsa ecdh sha
root     31990 24.8  0.1   6844  2940 pts/0    R+   07:44   0:23 openssl speed -multi 8 rc4 aes rsa ecdh sha
root     31991 24.8  0.1   6844  2940 pts/0    R+   07:44   0:23 openssl speed -multi 8 rc4 aes rsa ecdh sha
root     31992 24.9  0.1   6844  2940 pts/0    R+   07:44   0:23 openssl speed -multi 8 rc4 aes rsa ecdh sha

Еще по дефолту не будет использовать самые быстрые из доступных шифров
Например если сервер поддерживает aes-ni (хардварное ускорение шифрования), то для бенчмарка нужно будет явно указать ключ -evp чтобы использовать его


Если ты захочешь поднять свой собственный CA, то все что тебе понадобится уже есть в openssl

Самое сложное в поднятии приватного ЦА это сохранение безопасности. Например приватный ключ должен оставаться оффлайн, потому что вся безопасность зависит от него

С другой стороны CRL и OCSP должны регулярно обновляться


Создание ЦА ключает несколько шагов:

Конфигурационные файлы не нужны большую часть времени, но они необходимы для некоторых операций

Рекомендуется почитать man config -> OpenSSL Config

Root CA Configuration

В первой части описывается базовая инфа об УЦ

[default]
name                    = root-ca
domain_suffix           = example.com
aia_url                 = http://$name.$domain_suffix/$name.crt
crl_url                 = http://$name.$domain_suffix/$name.crl
ocsp_url                = http://ocsp.$name.$domain_suffix:9080
default_ca              = ca_default
name_opt                = utf8,esc_ctrl,multiline,lname,align

[ca_dn]
countryName             = "GB"
organizationName        = "Example"
commonName              = "Root CA"

Вторая часть контролирует работу УЦ
Большая часть параметров self-explanatory
Мы просто указываем куда мы хотим складывать наши файлы

А в полиси указываем что CN и ON для подписываемых сертов будут браться из корневого серта (это просто потому что у нас private CA)

[ca_default]
home                    = .
database                = $home/db/index
serial                  = $home/db/serial
crlnumber               = $home/db/crlnumber
certificate             = $home/$name.crt
private_key             = $home/private/$name.key
RANDFILE                = $home/private/random
new_certs_dir           = $home/certs
unique_subject          = no
copy_extensions         = none
default_days            = 3650
default_crl_days        = 365
default_md              = sha256
policy                  = policy_c_o_match

[policy_c_o_match]
countryName             = match
stateOrProvinceName     = optional
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

Третьей частью идет секция которая конфигурирует команду req
Которой мы воспользуемся лишь один раз (для создания корневого сертификата)
Наиболее важная часть это extensions'ы. В них и задается что это CA-cert (basicConstraint) и что им можно подписывать другие серты (keyUsage)

[req]
default_bits            = 4096
encrypt_key             = yes
default_md              = sha256
utf8                    = yes
string_mask             = utf8only
prompt                  = no
distinguished_name      = ca_dn
req_extensions          = ca_ext

[ca_ext]
basicConstraints        = critical,CA:true
keyUsage                = critical,keyCertSign,cRLSign
subjectKeyIdentifier    = hash

Четвертая часть описывает информацию которая будет использована при контруировании сертификатов выпускаемых этим корневым CA
Все выпускаемые сертификаты будут CA (basicConstraints - CA=true), и они не смогут так же как корневой выпускать CA-сертификаты, так как pathlen=0

Также, подписываемые sub-CA сертами серты, смогут использоваться для TLS клиент- и сервер-аутентификации

В теории nameConstraints ограничивает допустимые доменные имена, серты для которых смогут подписать sub-CA
Это позволяет делегировать возможность подписывать сертификаты и быть уверенным что sub-CA не сможет подписывать что-попало
Но на деле на это расширение мало кто смотрит

[sub_ca_ext]
authorityInfoAccess     = @issuer_info
authorityKeyIdentifier  = keyid:always
basicConstraints        = critical,CA:true,pathlen:0
crlDistributionPoints   = @crl_info
extendedKeyUsage        = clientAuth,serverAuth
keyUsage                = critical,keyCertSign,cRLSign
nameConstraints         = @name_constraints
subjectKeyIdentifier    = hash

[crl_info]
URI.0                   = $crl_url

[issuer_info]
caIssuers;URI.0         = $aia_url
OCSP;URI.0              = $ocsp_url

[name_constraints]
permitted;DNS.0=example.com
permitted;DNS.1=example.org
excluded;IP.0=0.0.0.0/0.0.0.0
excluded;IP.1=0:0:0:0:0:0:0:0/0:0:0:0:0:0:0:0

И наконец пятая часть конфига, она содержит расширения которые будут задействованы в сертификате для подписы OCSP ответов
Мы сгенерим специальный серт и делегируем ему возможность подписывать OCSP
Этот серт не будет CA как другие

[ocsp_ext]
authorityKeyIdentifier  = keyid:always
basicConstraints        = critical,CA:false
extendedKeyUsage        = OCSPSigning
keyUsage                = critical,digitalSignature
subjectKeyIdentifier    = hash

Root CA Directory Structure

Следующий шаг это создание структуры директорий которую мы описали в конфиге и инициализация некоторых файлов которые будут использоваться на протяжении работы CA

root@three:~/openssl_ca# mkdir root-ca
root@three:~/openssl_ca# cd root-ca/
root@three:~/openssl_ca/root-ca# ls
root@three:~/openssl_ca/root-ca# mkdir certs db private
root@three:~/openssl_ca/root-ca# chmod 700 private/
root@three:~/openssl_ca/root-ca# touch db/index

root@three:~/openssl_ca/root-ca# openssl rand -hex 16 > db/serial # большое число из 16-ти 16-ти-битных чисел (hex 16)
# если смотреть через x509 -text то выглядит так
# Serial Number:
#            9a:9c:cd:76:9c:cb:72:ee:9d:82:80:ac:4b:e7:de:ab

root@three:~/openssl_ca/root-ca# echo 1001 > db/crlnumber

root@three:~/openssl_ca/root-ca# tree
.
├── certs
├── db
│   ├── crlnumber
│   ├── index
│   └── serial
└── private

Root CA Generation
Мы уже выполнили два шага (собрали конфиг и насоздавали папок)
Теперь генерируем ключ и CSR

root@three:~/openssl_ca/root-ca# openssl req -new -config root-ca.conf -out root-ca.csr -keyout private/root-ca.key
Generating a RSA private key
.......................................................................................................................................................++++
.....................................................++++
writing new private key to 'private/root-ca.key'
Enter PEM pass phrase: #test
Verifying - Enter PEM pass phrase: #test
-----

root@three:~/openssl_ca/root-ca# tree
.
├── certs
├── db
│   ├── crlnumber
│   ├── index
│   └── serial
├── private
│   └── root-ca.key
├── root-ca.conf
└── root-ca.csr

И выпускаем самоподписанный серт

openssl ca -selfsign -config root-ca.conf -in root-ca.csr -out root-ca.crt -extensions ca_ext

После подписи index и serial поменялись сами

root@three:~/openssl_ca/root-ca# tree
.
├── certs
│   └── 9A9CCD769CCB72EE9D8280AC4BE7DEAB.pem
├── db
│   ├── crlnumber
│   ├── index
│   ├── index.attr
│   ├── index.old
│   ├── serial
│   └── serial.old
├── private
│   └── root-ca.key
├── root-ca.conf
├── root-ca.crt
└── root-ca.csr

Как видно серт положился и в certs и в корень
Потому что в конфиге написано "класть в certs", а в опции cli указано "-out root-ca.crt"


Structure of the Database File

root@three:~/openssl_ca/root-ca# cat db/index
V	310403100538Z		9A9CCD769CCB72EE9D8280AC4BE7DEAB	unknown	/C=GB/O=Example/CN=Root CA

База данных это просто текстовый файл
Содержит информацию о сертах
Одна строка - один серт

Каждая строка содержит 6 полей разделенных табами:

  1. status: V - valid, R - revoked, E - expired
  2. expiration date: YYMMDDHHMMSSZ
  3. дата отзыва серта, пусто пока не отозван
  4. серийник серта
  5. путь до файла или unknown если не известно
  6. Distinguished name

Root CA Operations

Для генерации CRL для нового CA используется -gencrl

openssl ca -gencrl -config root-ca.conf -out root-ca.crl

Вот что получается

root@three:~/openssl_ca/root-ca# cat root-ca.crl
-----BEGIN X509 CRL-----
MIICijB0AgEBMA0GCSqGSIb3DQEBCwUAMDExCzAJBgNVBAYTAkdCMRAwDgYDVQQK
DAdFeGFtcGxlMRAwDgYDVQQDDAdSb290IENBFw0yMTA0MDUxMDI1NTRaFw0yMjA0
MDUxMDI1NTRaoA8wDTALBgNVHRQEBAICEAEwDQYJKoZIhvcNAQELBQADggIBAKGK
be+WkQPWgOQX9N/Yenj8WoCylFDRjIW/6lmTBZ6oO18MWRDnRZ1DAzopStILmuq4
SobGg/BggZJnPaSk7aarwPYVSHRCKoyjEeM1ib/qHjcSEHXfE77i6K9fUiQ8zeNY
Czj+/F2lYVDz+fKkHtQBdL6HErpSAyNQXH7EshWORS2uJ9Kto4YgzId0/yUZUIk8
9daHRy1uJzxzlasFoAKDSl8Ht1iEpv5zyD4KV5i+RtEzhrJiwLEd1yQ8N5FNU4X+
A95iuqRUOLugI4iLM9/l9Xbqvv9E9yyCf1JiUntbTBcHJQ7qsyo7QfJ7fFpGx9Em
YhiM1AHOYObmU9cxsoLFk3g5ZAcr2rpEzJHXoKClbVhzRVLzFL+3VkOUpOOF1pEu
23kOy8v45c/gLDQV+qC/gdmbKd+nWTcRZ+lvOyOVjNTqc8S3oyebKEY3VTJ2H8cx
LYTXFy5oSvTYFsqobRCIaF8U+wu18y6N0RsMlrbw8leU+Z9GN/zfuf/Nzsf7vVzg
Dvk0mgsFLD7+z4X8T6Fy33ZZK7qj2cxk+k2ZauEaNajQKBOsLOaddQVn2lHPzRlY
pO402E1EFYaNUhT9FCPRdXsKc8g49YpQQjzolp/flaoJNstlp3Gdy3VBzWAZII5V
+/X2TbZ6aIcSPGA3QnocJZeUP6Wa+5G9mEFjMh/R
-----END X509 CRL-----

А внутри вот так

root@three:~/openssl_ca/root-ca# openssl crl -in root-ca.crl -text -noout
Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = GB, O = Example, CN = Root CA
        Last Update: Apr  5 10:25:54 2021 GMT
        Next Update: Apr  5 10:25:54 2022 GMT
        CRL extensions:
            X509v3 CRL Number:
                4097
No Revoked Certificates.
    Signature Algorithm: sha256WithRSAEncryption
         a1:8a:6d:ef:96:91:03:d6:80:e4:17:f4:df:d8:7a:78:fc:5a:
         80:b2:94:50:d1:8c:85:bf:ea:59:93:05:9e:a8:3b:5f:0c:59:
         10:e7:45:9d:43:03:3a:29:4a:d2:0b:9a:ea:b8:4a:86:c6:83:
         f0:60:81:92:67:3d:a4:a4:ed:a6:ab:c0:f6:15:48:74:42:2a:
         8c:a3:11:e3:35:89:bf:ea:1e:37:12:10:75:df:13:be:e2:e8:
         af:5f:52:24:3c:cd:e3:58:0b:38:fe:fc:5d:a5:61:50:f3:f9:
         f2:a4:1e:d4:01:74:be:87:12:ba:52:03:23:50:5c:7e:c4:b2:
         15:8e:45:2d:ae:27:d2:ad:a3:86:20:cc:87:74:ff:25:19:50:
         89:3c:f5:d6:87:47:2d:6e:27:3c:73:95:ab:05:a0:02:83:4a:
         5f:07:b7:58:84:a6:fe:73:c8:3e:0a:57:98:be:46:d1:33:86:
         b2:62:c0:b1:1d:d7:24:3c:37:91:4d:53:85:fe:03:de:62:ba:
         a4:54:38:bb:a0:23:88:8b:33:df:e5:f5:76:ea:be:ff:44:f7:
         2c:82:7f:52:62:52:7b:5b:4c:17:07:25:0e:ea:b3:2a:3b:41:
         f2:7b:7c:5a:46:c7:d1:26:62:18:8c:d4:01:ce:60:e6:e6:53:
         d7:31:b2:82:c5:93:78:39:64:07:2b:da:ba:44:cc:91:d7:a0:
         a0:a5:6d:58:73:45:52:f3:14:bf:b7:56:43:94:a4:e3:85:d6:
         91:2e:db:79:0e:cb:cb:f8:e5:cf:e0:2c:34:15:fa:a0:bf:81:
         d9:9b:29:df:a7:59:37:11:67:e9:6f:3b:23:95:8c:d4:ea:73:
         c4:b7:a3:27:9b:28:46:37:55:32:76:1f:c7:31:2d:84:d7:17:
         2e:68:4a:f4:d8:16:ca:a8:6d:10:88:68:5f:14:fb:0b:b5:f3:
         2e:8d:d1:1b:0c:96:b6:f0:f2:57:94:f9:9f:46:37:fc:df:b9:
         ff:cd:ce:c7:fb:bd:5c:e0:0e:f9:34:9a:0b:05:2c:3e:fe:cf:
         85:fc:4f:a1:72:df:76:59:2b:ba:a3:d9:cc:64:fa:4d:99:6a:
         e1:1a:35:a8:d0:28:13:ac:2c:e6:9d:75:05:67:da:51:cf:cd:
         19:58:a4:ee:34:d8:4d:44:15:86:8d:52:14:fd:14:23:d1:75:
         7b:0a:73:c8:38:f5:8a:50:42:3c:e8:96:9f:df:95:aa:09:36:
         cb:65:a7:71:9d:cb:75:41:cd:60:19:20:8e:55:fb:f5:f6:4d:
         b6:7a:68:87:12:3c:60:37:42:7a:1c:25:97:94:3f:a5:9a:fb:
         91:bd:98:41:63:32:1f:d1
openssl req -new -config root-ca.conf -out sub-ca.csr -keyout private/sub-ca.key
openssl ca -config root-ca.conf -in sub-ca.csr -out sub-ca.crt -extensions sub_ca_ext

Отзыв делается так

root@three:~/openssl_ca/root-ca# openssl ca -config root-ca.conf -revoke certs/9A9CCD769CCB72EE9D8280AC4BE7DEAC.pem -crl_reason keyCompromise
Using configuration from root-ca.conf
Enter pass phrase for ./private/root-ca.key:
Revoking Certificate 9A9CCD769CCB72EE9D8280AC4BE7DEAC.
Data Base Updated

root@three:~/openssl_ca/root-ca# cat db/index
V	310403100538Z		9A9CCD769CCB72EE9D8280AC4BE7DEAB	unknown	/C=GB/O=Example/CN=Root CA
R	310403103149Z	210405103713Z,keyCompromise	9A9CCD769CCB72EE9D8280AC4BE7DEAC	unknown	/C=GB/O=Example/CN=Root CA

Важно указать правильный резон для ревока
Возможные варианты:

После отзыва перегенерируем CRL

root@three:~/openssl_ca/root-ca# openssl ca -gencrl -config root-ca.conf -out root-ca.crl
Using configuration from root-ca.conf
Enter pass phrase for ./private/root-ca.key:

И видим наш отозванный серт в CRL

root@three:~/openssl_ca/root-ca# openssl crl -in root-ca.crl -text -noout
Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = GB, O = Example, CN = Root CA
        Last Update: Apr  5 10:40:26 2021 GMT
        Next Update: Apr  5 10:40:26 2022 GMT
        CRL extensions:
            X509v3 CRL Number:
                4098
Revoked Certificates:
    Serial Number: 9A9CCD769CCB72EE9D8280AC4BE7DEAC
        Revocation Date: Apr  5 10:37:13 2021 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code:
                Key Compromise
    Signature Algorithm: sha256WithRSAEncryption
         77:ca:42:a4:97:08:36:00:ad:ba:6b:f6:ed:a6:a4:7f:e5:2e:
         b3:7f:3c:63:7b:a7:83:d0:c5:08:ca:2e:3d:1e:2d:51:f7:97:
         cb:2a:30:58:3d:32:57:ab:c0:c5:6e:50:cb:00:04:df:43:73:
         99:b1:61:2e:d0:ab:5e:bf:e2:a0:9c:e5:42:b1:8b:b0:ad:be:
         88:0d:29:34:81:e5:2e:4e:8a:99:19:ec:89:95:b8:dc:17:46:
         28:03:ad:88:15:ca:9d:c8:82:53:53:3a:08:23:ae:40:3a:09:
         bb:02:cb:9d:c4:45:01:07:4a:0c:d6:58:11:82:5a:4b:13:9d:
         49:53:8a:51:10:90:8f:1d:bd:da:34:b4:b2:f3:46:94:84:d0:
         05:10:c9:2e:90:be:12:0a:f6:36:de:14:ec:12:d0:f3:5e:9c:
         37:ec:57:21:6c:79:dc:45:7b:62:c0:85:26:19:0a:4e:dc:47:

Create a Certificate for OCSP Signing

Выпускаем CSR

openssl req -new -newkey rsa:2048 -subj "/C=GB/O=Example/CN=OCSP Root Responder" -keyout private/root-ocsp.key -out root-ocsp.csr

Подписываем его рутовым сертом

openssl ca -config root-ca.conf -in root-ocsp.csr -out root-ocsp.crt -extensions ocsp_ext -days 30

Лайфтайм уменьшен до 30 дней в отличие от дефолтных 3650 дней потому что этот серт не имеет revocation information и не может быть отозван
Поэтому чем меньше тем лучше

Вот доказательство того что он не может быть отозван

root@three:~/openssl_ca/root-ca# openssl x509 -in sub-ca.crt -noout -ext crlDistributionPoints
X509v3 CRL Distribution Points:
    Full Name:
      URI:http://root-ca.example.com/root-ca.crl

root@three:~/openssl_ca/root-ca# openssl x509 -in root-ocsp.crt -noout -ext crlDistributionPoints
root@three:~/openssl_ca/root-ca#

Теперь мы имеем все для старта ocsp responder'a
По-уму респондерские серты и ключи нужно уносить с CA на отдельную машину
Но в тестовых целях это не важно

Запускаем респондера

openssl ocsp -port 9080 -index db/index -rsigner root-ocsp.crt -rkey private/root-ocsp.key -CA root-ca.crt -text

Он начинает слушать порт и писать лог в stdout

Дергаем его из сосендей консоли

root@three:~/openssl_ca/root-ca# openssl ocsp -issuer root-ca.crt -CAfile root-ca.crt -cert sub-ca.crt -url http://localhost:9080
Response verify OK
sub-ca.crt: good
	This Update: Apr  5 11:11:43 2021 GMT

Получаем ответ что все ок

Отзываем только что проверенный серт

root@three:~/openssl_ca/root-ca# openssl ca -config root-ca.conf -revoke certs/9A9CCD769CCB72EE9D8280AC4BE7DEAF.pem -crl_reason keyCompromise
Using configuration from root-ca.conf
Enter pass phrase for ./private/root-ca.key:
Revoking Certificate 9A9CCD769CCB72EE9D8280AC4BE7DEAF.
Data Base Updated

Перегенерируем CRL

root@three:~/openssl_ca/root-ca# openssl ca -gencrl -config root-ca.conf -out root-ca.crl
Using configuration from root-ca.conf
Enter pass phrase for ./private/root-ca.key:

Проверяем снова и видим что он отoзван

root@three:~/openssl_ca/root-ca# openssl ocsp -issuer root-ca.crt -CAfile root-ca.crt -cert sub-ca.crt -url http://localhost:9080
Response verify OK
sub-ca.crt: revoked
	This Update: Apr  5 11:13:33 2021 GMT
	Reason: keyCompromise
	Revocation Time: Apr  5 11:12:32 2021 GMT

Creating a Subordinate CA

За основу конфига для sub-ca можно взять конфиг корневого ca и изменить некоторые поля

[default]
name                    = sub-ca
ocsp_url                = http://ocsp.$name.$domain_suffix:9081

[ca_dn]
countryName             = "GB"
organizationName        = "Example"
commonName              = "Sub CA"

[ca_default]
default_days            = 365
default_crl_days        = 30
copy_extensions         = copy

В конец добавляем два новых профиля

[server_ext]
authorityInfoAccess     = @issuer_info
authorityKeyIdentifier  = keyid:always
basicConstraints        = critical,CA:false
crlDistributionPoints   = @crl_info
extendedKeyUsage        = clientAuth,serverAuth
keyUsage                = critical,digitalSignature,keyEncipherment
subjectKeyIdentifier    = hash

[client_ext]
authorityInfoAccess     = @issuer_info
authorityKeyIdentifier  = keyid:always
basicConstraints        = critical,CA:false
crlDistributionPoints   = @crl_info
extendedKeyUsage        = clientAuth
keyUsage                = critical,digitalSignature
subjectKeyIdentifier    = hash

Они отличаются только keyUsage и extendedKeyUsage

Далее можно генерить csr и подписывать его рутовым сертом

openssl req -new -config sub-ca.conf -out sub-ca.csr2 -keyout private/sub-ca.key2
openssl ca -config root-ca.conf -in sub-ca.csr2 -out sub-ca.crt2 -extensions sub_ca_ext

Subordinate CA Operations

Теперь можно подписывать клиентские серты

openssl ca \
  -config sub-ca.conf \
  -in server.csr \
  -out server.crt \
  -extensions server_ext # здесь можно указывать нужную директиву из конфига (server_ext, client_ext)

Генерация CRL и отзыв сертов делается аналогично тому что было описано выше для рутового CA

OpenSSL

OpenSSL Config

Заметки по man config

Description

Конфигурационный файл разделен на несколько секций
Каждая секция начинается со строки [ section_name ] и заканчивается началом новой секции или концом файла
Имя секции может содержать буквы, цифры и подчеркивание

Первая секция обычно дефолтная и начинается от начала файла до первой именованой секции

Окружение мапится на секцию ENV

Комменты пишутся через решетку

Можно делать импорты через .include filename

Дефолтный конфиг /usr/lib/ssl/openssl.cnf

В инклудах рекомендуется использовать абсолютные пути, потому что относительные пути будут меняться в зависимости от приложения и в итоге не будут работать как ожидается

Каждая секция содержит некоторое количество пар name=value
name содержит цифробуквы и может содержать знаки: ., ,, ;, _

Поле value умеет раскрывать переменные если они указаны через доллар $var и ${var}
Переменная заменится на значение ключа из текущей секции
Можно также подставлять значения из других секций, это делается так $section::name или ${section::name}

Через $ENV::var можно подставлять переменные окружения

Также можно использовать ENV::name для назначения значений переменным окружения, но это будет работать только если программа использует библиотеку CONF для поиска переменных окружения вместо вызова getenv

value не должно превышать 64к в длину после раскрытия всех переменных, иначе все сломается

Есть возможность пропускать некоторые символы используя какие-то кавычки или \
Например бэкслеш в конце строки позволяет сделать value многострочным
Также можно использовать \n, \r, \b, \t

Описанные выше правила экранирования и разворачивания применимы и к .include

OpenSSL Library Configuration

Разные приложения могут ориентироваться на конфиги openssl

Например cli команды OpenSSL смотрят в мастер конфиг (дефолтный) (если не указана опция смотреть в какой-то другой)

В конфиге можно задать парметры для библиотеки
В дефолтной секции должно содержаться имя openssl_conf в значении которого указывается имя главной конф. секции
Некоторые приложения могут ориентироваться на какое-то другое имя вместо openssl_conf

Ключ отображает имя конфигурируемого модуля, а возможное значение зависит от этого модуля

ASN1 Object Configuration Module

Этот модуль имеет имя oid_section
Это имя указывает на секцию в которой содержатся пары oid'ов
Например

openssl_conf = openssl_init

[openssl_init]
oid_section = new_oids

[new_oids]
some_new_oid = 1.2.3.4
some_other_oid = 1.2.3.5
shortName = some object long name, 1.2.3.6

Engine Configuration Module

Этот модуль имеет имя engines которое ссылается на секцию в которой содержится конфигурационная информация движков

Эта секция представляет из себя таблицу из имен движков и секций описывающих эти движки

openssl_conf = openssl_init

[openssl_init]
engines = engine_section

[engine_section]
# Configure ENGINE named "foo"
foo = foo_section
# Configure ENGINE named "bar"
bar = bar_section

[foo_section]
... foo ENGINE specific commands ...

[bar_section]
... "bar" ENGINE specific commands ...

ENGINE specific commands:

[engine_section]
# Configure ENGINE named "foo"
foo = foo_section

[foo_section]
# Load engine from DSO
dynamic_path = /some/path/fooengine.so
# A foo specific ctrl.
some_ctrl = some_value
# Another ctrl that doesn't take a value.
other_ctrl = EMPTY
# Supply all default algorithms
default_algorithms = ALL

EVP Configuration Module

Этот модуль имеет имя alg_section и указывает на секцию с командами алгоритмов
На текущий момент имеется только одна команда: fips_mode (принимает on\off)
Если fips включен, но библиотека не поддерживает fips, то все свалится с ошибкой

SSL Configuration Module

Этот модуль имеет имя ssl_conf и указывает на секцию с ssl конфигурацией

Каждая строка в этой секции содержит имя конфигурации и секцию содержащую ее

ssl_conf = ssl_sect

[ssl_sect]
server = server_section

[server_section]
RSA.Certificate = server-rsa.pem
ECDSA.Certificate = server-ecdsa.pem
Ciphers = ALL:!RC4

Дефолтная секция

ssl_conf = ssl_sect

[ssl_sect]
system_default = system_default_sect

[system_default_sect]
MinProtocol = TLSv1.2

Notes

Если в конфиге указано развернуть переменную окружения которой не существует, то все свалится с ошибкой

Например предыдущие версии openssl использовали в дефолтном конфиге перенную HOME, а в non-Unix системах ее нет

Это можно обойти указывая в дефолтной секции дефолтные значения
И когда поиск в окружении сфейлится, будет использовано дефолтное значение
Но нужно чтобы место определения дефолтного значения в конфиге было раньше чем место вызова переменной
Пример безопасного использования переменной окружения

TMP=/tmp
TEMP=$ENV::TMP
tmpfile=${ENV::TEMP}/tmp.filename

Если в рамках одной секции несколько раз описывается одна и та же переменная, то засчитается только последнее описание

OpenSSL

PKI

https://pki-tutorial.readthedocs.io/en/latest/

OpenSSL

Testing with OpenSSL

OpenSSL имеет встроенную клиентскую утилиту для подключения к защищенным серверам

openssl s_client -connect server.com:443

Она похожа на telnet или nc
Позволяет контролировать следующий за SSL/TLS уровень
На вход требует сервер и порт

Пример
Дернем страничку через TELNET

root@three:~# telnet example.com 80
Trying 93.184.216.34...
Connected to example.com.
Escape character is '^]'.
GET / HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
...
Content-Length: 1256

<!doctype html>
<html>
<head>
    <title>Example Domain</title>
...
    <style type="text/css">
    body {
        background-color: #f0f0f2;
        ...
    </style>
</head>

<body>
<div>
    <h1>Example Domain</h1>
    <p>This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.</p>
    <p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>

^CConnection closed by foreign host.

То же самое через S_CLIENT

root@three:~# openssl s_client -connect example.com:443
CONNECTED(00000003)
... # разная отладачная инфа про серты
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, CN = www.example.org
issuer=C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
---
... # еще какая-то инфа
---
SSL handshake has read 4654 bytes and written 719 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
...
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    ...
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
...
---
read R BLOCK ### И наконец полезная инфа (делаем свой запрос)
GET / HTTP/1.1
Host: example.com

HTTP/1.1 200 OK # (получаем ответ)
... # headers

<!doctype html>
<html>
<head>
    <title>Example Domain</title>

    <meta cha...
</head>

<body>
<div>
    <h1>Example Domain</h1>
    <p>This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.</p>
    <p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
^C

остановился на странице 49

OpenSSL

CA,CSR,CRT

CA

openssl req -sha256 -new -x509 -days 365 -nodes -out server-ca.crt -keyout server-ca.key

CSR

openssl req -sha256 -new -nodes -subj "/CN=two.vandud.ru" -out server.csr -keyout server.key

Sign CSR

root@two:~# openssl x509 -req -sha256 -days 365 -in server.csr -CA server-ca.crt -CAkey server-ca.key -CAcreateserial -out server.crt
Signature ok
subject=CN = two.vandud.ru
Getting CA Private Key

LVS

jq

https://stedolan.github.io/jq/manual/

Certbot

Сертбот нужно запускать от рута, потому что ему нужно запускать свои скрипты и иметь доступ к директориям с сертами

Требуется:

Если эти требования невыполнимы, то можно воспользоваться letsencrypt_nosudo или simp_le


Certbot можно установить через пакетный менеджер твоей ОС, snap или docker (крайне не рекомендуется, потому что сложно)

Обычно достаточно apt install certbot

Есть пакеты которые помогают certbot'у интегрироваться с apache/nginx и прочими сервисами

root@four:~# apt-cache search certbot | grep -v doc
certbot - automatically configure HTTPS using Let's Encrypt
python3-certbot - main library for certbot
python3-certbot-apache - Apache plugin for Certbot
python3-certbot-dns-cloudflare - Cloudflare DNS plugin for Certbot
python3-certbot-dns-digitalocean - DigitalOcean DNS plugin for Certbot
python3-certbot-dns-dnsimple - DNSimple DNS plugin for Certbot
python3-certbot-dns-gandi - Gandi LiveDNS plugin for Certbot
python3-certbot-dns-gehirn - Gehirn DNS plugin for Certbot
python3-certbot-dns-google - Google DNS plugin for Certbot
python3-certbot-dns-linode - Linode DNS plugin for Certbot
python3-certbot-dns-ovh - OVH DNS plugin for Certbot
python3-certbot-dns-rfc2136 - RFC 2136 DNS plugin for Certbot
python3-certbot-dns-route53 - Route53 DNS plugin for Certbot
python3-certbot-dns-sakuracloud - SakuraCloud DNS plugin for Certbot
python3-certbot-nginx - Nginx plugin for Certbot

Также можно установить через pip


Сертбот использует некоторое количество различных подкомманд для запроса различных действий: выпуск, перевыпуск, отзыв

Сертбот имеет два вида плагинов: аутентификаторы и инсталлеры

Аутентификаторы используются с командой certonly для получения сертификата:

Инсталлеры используются с командой install для установки, и они уже могут модифицировать конфигурацию вебсервера

Плагины делающие и то и то вызываются через certbot run (run - дефолт, если не указана другая подкоманда). Так же в команде run можно определять четкие комбинации плагинов auth&install

Plugin Auth Inst Notes Use
apache Y Y Получение и установка сертификата для apache --apache
nginx Y Y Получение и установка сертификата для nginx certbot --nginx
webroot Y N Получение сертификата через запись в webroot запущенного вебсервера certonly --webroot \
--webroot-path PATH
standalone Y N Получение сертификата. Certbot вешается на 80 порт и таким образом проводит auth certonly --standalone
DNS plugins Y N Категория плагинов автоматизирующих получение сертификата через изменение DNS записей для подтверждения контроля над доменом. Это единственный способ получить wildcard сертификат
manual Y N Помогает получить сертификат давая инструкции для самостоятельной валидации домена. Позволяет использовать скрипты certonly --manual

Под капотом плагины используют один из нескольких acme протоколов для подтверждения контроля над доменом
Некоторые плагины поддерживает больше одного и тогда можно указать предпочитаемый через --preferred-challenges

Каждый домен будет использовать последний определенный перед ним webroot

certbot certonly --webroot -w /var/www/example -d www.example.com -d example.com \
    -w /var/www/other -d other.example.net -d another.other.example.net

Команда выше создаст один серт на все перечисленные домены. Если нужны раздельные серты, то команду нужно запускать несколько раз


webroot плагин создает временный файл для каждого запрошенного домена в ${webroo-path}/.well-known/acme-challenge/
А проверяющий сервер lets encrypt сделает запрос до этого временного файла

"GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1"

Важно чтобы скрытая папка .well-known и ее содержимое было доступно через интернет для проверки (может потребоваться править конфиг вебсервера)


Плагин nginx корректно работает для большинства конфигураций, но нужно делать бэкап конфигов (несмотря на то что сертботовские изменения можно откатить через certbot --nginx rollback)


standalone режим позволяет получить сертификат не полагаясь на существующий вебсервер
Плагин вешается на 80/443 порт и сам становится вебсервером

--<challenge-type>-address позволяет указать интерфес и протокол на который нужно биндиться


Если нужно получить wildcard сертификат или сертбот запускается на сервере с которого нельзя провести проверку через webroot, то можно воспользоваться dns плагинами
Эти плагины устанавливаются отдельно и у них свои доки


manual плагин умеет http и dns проверку, можно указать желаемую через --preferred-challenges
Это то же самое что и автоматическая проверка, только не автоматическая
Проверка по http попросит положить временный файл в well-known сайта
А проверка по dns попросит добавить TXT запись

manual плагину можно указывать на скрипты предподготовки и очистки мусора после получения сертификата


Иногда нужно четко определить auth и install плагины, это можно делать через ключи

Пример

certbot run -a webroot -i apache -w /var/www/html -d example.com

Или можно пройти проверку вручную, а установить через плагин nginx (но автоматически такое не продлить)

Пример полной процедуры

root@four:~# certbot run -a manual -i nginx -d four.vandud.ru
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer nginx
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): s@vandud.ru

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for four.vandud.ru

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:

bBGyo54bGqOsHD0tj9javAZLxoh6L4_OJHqY4cDadmg._OridQhxi5LDrFVF7NMSrzUfVRDYUuSXKz7wSkmQWYg

And make it available on your web server at this URL:

http://four.vandud.ru/.well-known/acme-challenge/bBGyo54bGqOsHD0tj9javAZLxoh6L4_OJHqY4cDadmg

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue 
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/default

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/default

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://four.vandud.ru

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=four.vandud.ru
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/four.vandud.ru/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/four.vandud.ru/privkey.pem
   Your cert will expire on 2021-08-12. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

root@four:~#

Чтобы отобразить список сертификатов о которых знает сертбот нужно запустить подкоманду certificates

root@four:~# certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: four.vandud.ru
    Domains: four.vandud.ru
    Expiry Date: 2021-08-12 21:07:09+00:00 (VALID: 89 days)
    Certificate Path: /etc/letsencrypt/live/four.vandud.ru/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/four.vandud.ru/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Поле 'Certificate Name' содержит имя сертификата, которое можно использовать для перевыпуска (сертбот возьмет всю инфу из старого серта и перевыпустит)

--force-renewal, --duplicate и --expand контролируют поведение при пересоздании с существующим именем сертификата

Рекомендуется использовать --cert-name вместо --expand, потому что больше контроля


Есть ограничение скорости перевыпуска сертификатов
Оно нужно для ограничения злоупотребления сервисом
Лимиты описаны здесь: https://letsencrypt.org/docs/rate-limits/


--cert-name позволяет модифицировать домены в сертификате
Например команда:

certbot certonly --cert-name example.com -d example.com

Перевыпустит сертификат example.com в котором два домена (example.com и www.example.com) только с example.com (уберет www.)


Сертбот поддерживате два типа приватных ключей: rsa и ecdsa
Через --key-type и --elliptic-curve можно этим управлять


Для отзыва нужно использовать подкоманду revoke
То какой сертификат отзывать, указывается двумя способами

Также при отзыве можно указать причину отзыва во флаге --reason


По умолчанию сертбот для отзыва сертификата использует acme account key. Если сертификат был создан с того же аккаунта что и удаляется, то все получится

А если аккаунты разные, то для отзыва потребуется передать команде revoke еще и приватный ключ сертификата через --key-path

certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem \
    --key-path /etc/letsencrypt/live/example.com/privkey.pem

Если запустить команду certbot renew, то она попытается обновить сертификаты которые истекают меньше чем через 30 дней

renew в отличие от certonly изначально дизайнилась для автоматического использования и она может взаимодействовать сразу с множеством сертификатов


У renew есть pre и post хуки. Они позволяют выполнять команды до перевыпуска и после (например релоадить вебсервер)

Если хук свалится с ошибкой, то ошибка будет напечатана на экран, но обновление все равно продолжится


/etc/letsencrypt - директория с конфигами сертбота

root@four:/etc/letsencrypt# tree -a
.
|-- accounts
|   `-- acme-v02.api.letsencrypt.org
|       `-- directory
|           `-- 1da4ab1e38f6367e285fbd28fc2ae0ca
|               |-- meta.json
|               |-- private_key.json
|               `-- regr.json
|-- archive
|   `-- four.vandud.ru
|       |-- cert1.pem
|       |-- chain1.pem
|       |-- fullchain1.pem
|       `-- privkey1.pem
|-- cli.ini
|-- csr
|   `-- 0000_csr-certbot.pem
|-- keys
|   `-- 0000_key-certbot.pem
|-- live
|   |-- four.vandud.ru
|   |   |-- cert.pem -> ../../archive/four.vandud.ru/cert1.pem
|   |   |-- chain.pem -> ../../archive/four.vandud.ru/chain1.pem
|   |   |-- fullchain.pem -> ../../archive/four.vandud.ru/fullchain1.pem
|   |   |-- privkey.pem -> ../../archive/four.vandud.ru/privkey1.pem
|   |   `-- README
|   `-- README
|-- options-ssl-nginx.conf
|-- renewal
|   `-- four.vandud.ru.conf
|-- renewal-hooks
|   |-- deploy
|   |-- post
|   `-- pre
|-- ssl-dhparams.pem
|-- .updated-options-ssl-nginx-conf-digest.txt
`-- .updated-ssl-dhparams-pem-digest.txt

Некоторые выполняемые файлы могут быть найдены в renewal-hooks
Хуки из этих папок (deploy,post,pre) только для renew и выполняются в алфавитном порядке
Это поведение выключается ключом --no-directory-hooks

Если команда certbot renew выполняется корректно и без вмешательста человека, то ее можно засунуть в cron

Если сертификат выпускается через certbot certonly в котором через ключ -d перечисляется все домены, то можно использовать ключ -n/--nonenteractive чтобы отключить запрос пользовательского ввода

--force-renewal - позволяет обновить серт игнорируя дату истечения (раньше чем за месяц обновить нельзя)
Это сделано специально, потому что у УЦ есть лимиты, и если все будут перевыпускать серты каждый день, то у УЦ что-то закончится

ЦА отправит алерт на почту если сертификат не будет продлен

Linux hostname (как правильно изменить)

Чтобы было так

root@anxiety:~# hostname
anxiety
root@anxiety:~# hostname -f
anxiety.vandud.ru

Нужно сделать так

root@anxiety:~# cat /etc/hostname
anxiety
root@anxiety:~# cat /etc/hosts
127.0.0.1	localhost
127.0.0.1	anxiety.vandud.ru	anxiety
...

При загрузке системы из файла /etc/hostname хостнейм положится в /proc/sys/kernel/hostname
Дальше утилиты типа hostname используют именно имя из /proc

root@anxiety:~# hostname test
root@anxiety:~# cat /proc/sys/kernel/hostname
test
root@anxiety:~# hostname
test

А hostname -f берет инфу из файла /etc/hosts
Он находит в файле /etc/hosts подходящую строку и берет из нее первый хостнейм

root@anxiety:~# hostname
test
root@anxiety:~# hostname -f
testtesttest
root@anxiety:~# cat /etc/hosts
127.0.0.1	localhost
127.0.0.1	testtesttest	anxiety.vandud.ru	anxiety	test
...

В примере выше он нашел последнее имя test и как полное имя взял первое testtesttest


Кстати hostnamectl покажет какое имя статическое, а какое временное

root@anxiety:~# hostnamectl
   Static hostname: anxiety
Transient hostname: test
         Icon name: computer-vm
           Chassis: vm
        Machine ID: ec697124f3421da2bd81ecfae2cab1fc
           Boot ID: f47d3ce45e3642c3a30e2ca26902264a
    Virtualization: kvm
  Operating System: Debian GNU/Linux 11 (bullseye)
            Kernel: Linux 5.10.0-20-amd64
      Architecture: x86-64

Можно сделать и так

root@anxiety:~# echo 'vandud' > /proc/sys/kernel/hostname
root@anxiety:~# hostname
vandud

Своя инфраструктура

Имеется 3 машины:

hostname ipv4 ipv6 cpu mem disk
anxiety.vandud.ru 77.91.123.180 2a09:7c44:0:2c::1
2a09:7c44:0:2c::
4 8 80
blame.vandud.ru 77.91.123.182 2a09:7c44:0:53::1
2a09:7c44:0:53::
4 8 80
contempt.vandud.ru 77.91.123.184 2a09:7c44:0:55::1
2a09:7c44:0:55::
4 8 80
one.vandud.ru 51.178.9.167 - 1 1 25

Reproxy

https://reproxy.io/

Reproxy это простой edge HTTP(s) сервер / reverse proxy поддерживающий множество провайдеров (docker, static, file, consul catalog). Один или более провайдеров предоставляют информацию о запрашиваемых серверах, запрашиваемых URL, конечных URL и health check URL. Он распрастраняется в виде одиночного бинарного файла или как docker image


Сервер (host) может быть указан как FQDN (s.example.com, * (catch all)) или как регулярное выражение. Приоритетнее будет полное совпадение, так, если у нас будет два правила с серверами example.com и example\.(com|org), то запрос example.com/some/url уйдет на первый сервер. Запрашиваемый URL тоже может быть указан как регулярное выражение, например ^api/(.*), а конечный URL может содержать группы из регулярного выражения запрашиваемого URL, например http://d.example.com:8080/$1. В итоге http://s.example.com/api/something?foo=bar будет отпроксирован в http://d.example.com:8080/something?foo=bar

Для удобства, запросы с / на конце и без regex группы автоматически расширяются до /(.*), а конечные URL в таком случае расширяются до /$1. То есть /api/ -> http://localhost/service будет переведен в ^/api/(.*) -> http://localhost/service/$1

Поддерживается как HTTP так и HTTPS. Для HTTPS могут быть использованы как статичные (пользовательские) сертификаты, так и автоматически выпущенные через ACME (Let's Encrypt). Опциональный сервер ассетов может быть использован для раздачи статических файлов. Чтобы запустить Reproxy нужен как минимум один провайдер. Остальные параметры опциональны и имеют разумные дефолтные значения

Примеры:

Install

Reproxy распространяется как маленький сдержанный бинарный файл, а также как docker image. И бинарь и докер имадж поддерживают множество архитектур и множество ОС, также есть deb/rpm пакеты

Providers

Правила проксирования предоставляются провайдерами. Сейчас в приложение включены - file, docker, static и consul-catalog. Каждый провайдер может предоставлять множество правил роутинга и для проксируемых запросов и для раздачи статики (assets). Пользователь может указывать множество провайдеров одновременно

Static provider

Это простейший провайдер который указывает правила прямо в командной строке (или через переменные окружения). Поддерживается возможность указания множества правил. Каждое правило это 3 или 4 разделенных запятой элемента server,sourceurl,destination[,ping-url]. Например:

Последний (четвертый) элемент определяет опциональный ping url

File provider

Этот провайдер использует yaml файл с правилами роутинга

reproxy --file.enabled --file.name=config.yml

Пример конфига:

default: # the same as * (catch-all) server
  - { route: "^/api/svc1/(.*)", dest: "http://127.0.0.1:8080/blah1/$1" }
  - {
      route: "/api/svc3/xyz",
      dest: "http://127.0.0.3:8080/blah3/xyz",
      ping: "http://127.0.0.3:8080/ping",
    }
srv.example.com:
  - { route: "^/api/svc2/(.*)", dest: "http://127.0.0.2:8080/blah2/$1/abc" }
  - { route: "^/web/", dest: "/var/www", "assets": true }

Изменения в файле подтягиваются автоматически

Docker provider

https://reproxy.io/#docker-provider

Consul Catalog provider

https://reproxy.io/#consul-catalog-provider

Compose-specific details

https://reproxy.io/#compose-specific-details

SSL support

Режим работы по SSL может быть выставлен в auto (ACME/LE сертификаты), static (существующие сертификаты) или none (по умолчанию none). Если стоит auto, то сертификаты будут выпущены автоматически для всех найденных server names. Пользователь может изменить это поведение явно указав нужные домены в --ssl.fqdn

Headers

Reproxy позволяет удалять входящие заголовки с помощью опции --drop-header которая может быть указана множество раз. Этот параметр может быть полезен чтобы быть уверенным что некоторые заголовки установлены внутренними сервисами, а не подсунуты пользователем. Например, если какой-то из сервисов ответственный за аутентификацию подставляет заголовки X-Auth-User и X-Auth-Token, то может быть полезно удалять такие заголовки у входящих от пользователей запросов, это можно делать так --drop-header=X-Auth-User --drop-header=X-Auth-Token или через переменную окружения DROP_HEADERS=X-Auth-User,X-Auth-Token

Также доступна противополжная функция подстановки заголовков в исходящие ответы. Это можно делать с помощью параметра --header который можно указывать много раз или через переменную HEADER
Пример того как это выглядит в docker-compose:

environment:
      - HEADER=
          X-Frame-Options:SAMEORIGIN,
          X-XSS-Protection:1; mode=block;,
          Content-Security-Policy:default-src 'self'; style-src 'self' 'unsafe-inline';

Logging

По умолчанию запросы не логируются, но это поведение можно изменить с помощью опции --logger.enabled. Лог ротируется автоматически и имеет Apache Combined Log Format

С помощью опции --logger.stdout можно сказать Reproxy выводить краткий лог зарпосов в stdout (это не влияет на логирование в файл, а просто добавляет краткий лог в stdout)

Assets Server

Пользователь может включить сервер ассетов для обслуживания запросов к статике (по умолчанию он выключен). Пока установлен параметр --assets.location он будет пытаться обработать запросы по пути assets.root как запросы к статике (assets.root - часть урла, assets.location - путь до файлов). Сервер ассетов может быть использован без каких-либо провайдеров, в этом режиме Reproxy работает как простой web-server для раздачи статического контента. Также есть режим SPA который можно включить параметром --assets.spa, в таком случае каждый запрос к ненайденному файлу будет перенаправляться на index.html

В дополнение к основному серверу статики поддерживается множество кастомных. Каждый провайдер имеет собственный вариант указания его, а некоторые провайдеры его и вовсе не имеют

Caching

Сервер ассетов имеет контроль кэширования, который задается через опцию --assets.cache=<duration>. 0s в этой опции отключит кэширование. <duration> это последовательность десятичных чисел, которые могут дробными и имеют суффикс обозначающий величину, например “300ms”, “1.5h” или “2h45m”. Валидные суффиксы: “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h” и “d”

Есть два варианта указания длительности кэширования:

Кастомная страница для ответа 404 может быть задана через опцию --assets.404=<path>. Этот путь указывается относительно пути assets root

Using reproxy as a base image

https://reproxy.io/#using-reproxy-as-a-base-image

SPA-friendly mode

Некоторые SPA приложения полагаются на прокси для обработки 404 особым способом - перенаправляя на /index.html. Это похоже на nginx'овую директиву try_files $uri $uri/ ... и предположительно эта функция что-то важное для современных web приложений

По умолчанию в Reproxy эта функция выключена, но может быть включена с помощью --assets.spa или ASSETS_SPA=true

Redirects

По умолчанию Reproxy пробует destination как proxy location, то есть делает внутренний http запрос и возвращает ответ клиенту Однако это поведение можно изменить префиксируя destination url с помощью @code. Таким образом если указать @301 https://example.com/something, то получим редирект на Location: https://example.com/something
Поддерживаемые коды:

More options

Default ports

Чтобы избежать необходимости прописывать кастомные параметры/окружения, дефолтный --listen - динамичный, он пытается быть разумным и полезным для типичных кейсов:

Another default set in the similar dynamic way is --ssl.http-port. For run inside of the docker container it set to 8080 and without to 80.

Ping, health checks and fail-over

Reproxy предоставляет два эндпоинта для этих целей:

В дополнение к эндпоинтам выше, Reproxy поддерживает проверки в реальном времени. В таком случае (если они включены) каждый конечный url периодически проверяется на ping ответ и в случае ошибки исключается из роутинга. Это дает возможность возвращать множество идентичных конечных url из одного или множества провайдеров, и будут выбраны только прошедшие проверки. Если множество подходящих было обнаружено и проверки были пройдены - только один будет использоваться в соответствии со стратегией lb-type (по умолчанию случайный выбор)

Чтобы включить проверки в реальном времени, пользователю нужно указать --health-check.enabled (или через переменную HEALTH_CHECK_ENABLED=true). Для кастомизации интервала - --health-check.interval=

Management API

Опционально с помощью флага --mgmt.enabled можно включить два эндпоинта которые будут доступны на mgmt.listen:

Errors reporting

Reproxy вернет 502 (Bad Gateway) в случае если запрос не матчится ни на один имеющийся роут или ассет. В случае неожиданных внутренних ошибок он вернет 500. По умолчанию он рендерит простейшую текстовую версию страницы ошибки - "Server error". Настройка --error.enabled включит дефолтную html страницу, а с помощью --error.template пользователь может установить любой кастомный html шаблон для ошибок. В таком темплейте будут доступны две переменные {{.ErrCode}} и {{.ErrMessage}}
Например шаблон oh my! {{.ErrCode}} - {{.ErrMessage}} будет отрендерен в oh my! 502 - Bad Gateway

Throttling

Reproxy позволяет задать ограничение в max req/sec как для всей системы, так и для пользователей. Значение 0 воспринимается как - неограничено

Ограничение на пользовательскую активность (запросы) распространяется как на сматченные, так и несматченные роуты. Все несматченные роуты рассматриваются как "single destination group" и ограничиваются в rate*3. Это значит что если указано --throttle.user=10 то конечный пользователь сможет делать вплоть до 30 запросов в секунду на какие-то статические ассеты или несматченные роуты. Для сматченных роутов это ограничение выставляется per destination, таким образом запросы проксируемые на s1.example.com/api будут ограничены 10r/s, а запросы проксируемые на s2.example.com будут ограничены другими 10r/s

Basic auth

Reproxy поддерживает Basic Auth для всех запросов. Это полезно для защиты эндпоинтов которые находятся в разработке или тестировании до запуска их в публичный доступ. Эта функция выключена по умолчанию и не достаточно гранулярна для настройки ее на каждый роут отдельно
Таким образом включенный Basic Auth будет влиять на все запросы

Чтобы включить Basic Auth для всех запросов пользователю нужно указать типичный htpasswd файл в опции --basic-htpasswd=<file location> или с помощью переменной BASIC_HTPASSWD=<file location>

Reproxy ожидает htpasswd файл в следующем формате:

username1:bcrypt(password2)
username2:bcrypt(password2)
...

Его можно сгенерировать с помощью htpasswd -nbB

Plugins support

https://reproxy.io/#plugins-support

Container security

https://reproxy.io/#container-security

Options

Каждая опция может быть указана двумя способами: опция командной строки и переменная окружения. Некоторые cli опции имеют короткую форму, как -l localhost:8080 и --listen localhost:8080. Имя соответствующей переменной окружения указано в конце описания каждой опции

Все опции описывающие размеры поддерживают суффикс величины 10K (or 10k) for kilobytes, 16M (or 16m) for megabytes, 10G (or 10g) for gigabytes. Отсутствие суффикса воспринимается как байты

Некоторые опции могут быть указаны несколько раз, в таком случае пользователь может несколько раз указать cli опцию, либо через запятую перечислить значения в переменной окружения. Например опция --ssl.fqdn одна из таких и может быть указана как --ssl.fqdn=a1.example.com --ssl.fqdn=a2.example.com или как переменная SSL_ACME_FQDN=a1.example.com,a2.example.com

Ниже список всех таких опций:

All Application Options

  -l, --listen=                     listen on host:port (default: 0.0.0.0:8080/8443 under docker, 127.0.0.1:80/443 without) [$LISTEN]
  -m, --max=                        max request size (default: 64K) [$MAX_SIZE]
  -g, --gzip                        enable gz compression [$GZIP]
  -x, --header=                     outgoing proxy headers to add [$HEADER]
      --drop-header=                incoming headers to drop [$DROP_HEADERS]
      --basic-htpasswd=             htpasswd file for basic auth [$BASIC_HTPASSWD]      
      --lb-type=[random|failover]   load balancer type (default: random) [$LB_TYPE]
      --signature                   enable reproxy signature headers [$SIGNATURE]
      --dbg                         debug mode [$DEBUG]

ssl:
      --ssl.type=[none|static|auto] ssl (auto) support (default: none) [$SSL_TYPE]
      --ssl.cert=                   path to cert.pem file [$SSL_CERT]
      --ssl.key=                    path to key.pem file [$SSL_KEY]
      --ssl.acme-location=          dir where certificates will be stored by autocert manager (default: ./var/acme) [$SSL_ACME_LOCATION]
      --ssl.acme-email=             admin email for certificate notifications [$SSL_ACME_EMAIL]
      --ssl.http-port=              http port for redirect to https and acme challenge test (default: 8080 under docker, 80 without) [$SSL_HTTP_PORT]
      --ssl.fqdn=                   FQDN(s) for ACME certificates [$SSL_ACME_FQDN]

assets:
  -a, --assets.location=            assets location [$ASSETS_LOCATION]
      --assets.root=                assets web root (default: /) [$ASSETS_ROOT]
      --assets.spa                  spa treatment for assets [$ASSETS_SPA]
      --assets.cache=               cache duration for assets [$ASSETS_CACHE]
      --assets.not-found=           path to file to serve on 404, relative to location [$ASSETS_NOT_FOUND]

logger:
      --logger.stdout               enable stdout logging [$LOGGER_STDOUT]
      --logger.enabled              enable access and error rotated logs [$LOGGER_ENABLED]
      --logger.file=                location of access log (default: access.log) [$LOGGER_FILE]
      --logger.max-size=            maximum size before it gets rotated (default: 100M) [$LOGGER_MAX_SIZE]
      --logger.max-backups=         maximum number of old log files to retain (default: 10) [$LOGGER_MAX_BACKUPS]

docker:
      --docker.enabled              enable docker provider [$DOCKER_ENABLED]
      --docker.host=                docker host (default: unix:///var/run/docker.sock) [$DOCKER_HOST]
      --docker.network=             docker network [$DOCKER_NETWORK]
      --docker.exclude=             excluded containers [$DOCKER_EXCLUDE]
      --docker.auto                 enable automatic routing (without labels) [$DOCKER_AUTO]
      --docker.prefix=              prefix for docker source routes [$DOCKER_PREFIX]

consul-catalog:
      --consul-catalog.enabled      enable consul catalog provider [$CONSUL_CATALOG_ENABLED]
      --consul-catalog.address=     consul address (default: http://127.0.0.1:8500) [$CONSUL_CATALOG_ADDRESS]
      --consul-catalog.interval=    consul catalog check interval (default: 1s) [$CONSUL_CATALOG_INTERVAL]

file:
      --file.enabled                enable file provider [$FILE_ENABLED]
      --file.name=                  file name (default: reproxy.yml) [$FILE_NAME]
      --file.interval=              file check interval (default: 3s) [$FILE_INTERVAL]
      --file.delay=                 file event delay (default: 500ms) [$FILE_DELAY]

static:
      --static.enabled              enable static provider [$STATIC_ENABLED]
      --static.rule=                routing rules [$STATIC_RULES]

timeout:
      --timeout.read-header=        read header server timeout (default: 5s) [$TIMEOUT_READ_HEADER]
      --timeout.write=              write server timeout (default: 30s) [$TIMEOUT_WRITE]
      --timeout.idle=               idle server timeout (default: 30s) [$TIMEOUT_IDLE]
      --timeout.dial=               dial transport timeout (default: 30s) [$TIMEOUT_DIAL]
      --timeout.keep-alive=         keep-alive transport timeout (default: 30s) [$TIMEOUT_KEEP_ALIVE]
      --timeout.resp-header=        response header transport timeout (default: 5s) [$TIMEOUT_RESP_HEADER]
      --timeout.idle-conn=          idle connection transport timeout (default: 90s) [$TIMEOUT_IDLE_CONN]
      --timeout.tls=                TLS hanshake transport timeout (default: 10s) [$TIMEOUT_TLS]
      --timeout.continue=           expect continue transport timeout (default: 1s) [$TIMEOUT_CONTINUE]

mgmt:
      --mgmt.enabled                enable management API [$MGMT_ENABLED]
      --mgmt.listen=                listen on host:port (default: 0.0.0.0:8081) [$MGMT_LISTEN]

error:
      --error.enabled               enable html errors reporting [$ERROR_ENABLED]
      --error.template=             error message template file [$ERROR_TEMPLATE]

health-check:
      --health-check.enabled        enable automatic health-check [$HEALTH_CHECK_ENABLED]
      --health-check.interval=      automatic health-check interval (default: 300s) [$HEALTH_CHECK_INTERVAL]

throttle:
      --throttle.system=            throttle overall activity' (default: 0) [$THROTTLE_SYSTEM]
      --throttle.user=              limit req/sec per user and per proxy destination (default: 0) [$THROTTLE_USER]

plugin:
      --plugin.enabled              enable plugin support [$PLUGIN_ENABLED]
      --plugin.listen=              registration listen on host:port (default: 127.0.0.1:8081) [$PLUGIN_LISTEN]

Help Options:
  -h, --help                        Show this help message

Status

https://reproxy.io/#status

Borg

Overview

Что такое BorgBackup

BorgBackup (коротко: Borg) это программа для дедуплицирующего резервного копирования. Опционально она поддерживает сжатие и шифрование

Главная цель Borg - предоставить эффективный и безопасный способ бэкапить данные. Дедуплицирование делает Borg пригодным для ежедневных бэкапов, так как будут сохранены только изменения. Шифрование позволяет хранить бэкапы на не полностью доверенных хранилищах

Main features

Easy to use

Инициализируй репозиторий (see borg init --help for encryption options):

root@borg-server:~# mkdir -p /var/lib/borg/my-test-backup-repo
root@borg-server:~# borg init -e none /var/lib/borg/my-test-backup-repo
root@borg-server:~# tree /var/lib/borg/my-test-backup-repo
/var/lib/borg/my-test-backup-repo
├── config
├── data
│   └── 0
│       ├── 0
│       └── 1
├── hints.1
├── index.1
├── integrity.1
└── README

2 directories, 7 files

Создай бэкап:

root@borg-server:~# borg create /var/lib/borg/my-test-backup-repo::vandud-test-backup-1 /var/log/
root@borg-server:~# borg create -v --stats /var/lib/borg/my-test-backup-repo::vandud-test-backup-2 /var/log/
Creating archive at "/var/lib/borg/my-test-backup-repo::vandud-test-backup-2"
------------------------------------------------------------------------------
Repository: /var/lib/borg/my-test-backup-repo
Archive name: vandud-test-backup-2
Archive fingerprint: 98dafada55752be228cdfc709639295b36b9161f4169152b05e707a44ecd4bb2
Time (start): Wed, 2023-12-13 16:34:54
Time (end):   Wed, 2023-12-13 16:34:54
Duration: 0.70 seconds
Number of files: 24
Utilization of max. archive size: 0%
------------------------------------------------------------------------------
                       Original size      Compressed size    Deduplicated size
This archive:               53.54 MB              4.68 MB                511 B
All archives:              107.08 MB              9.35 MB              4.68 MB

                       Unique chunks         Total chunks
Chunk index:                      34                   66
------------------------------------------------------------------------------

Есть еще BorgWeb
Заброшенная штука, функционал сомнительной полезности

Installation

Есть три разных пути установить Borg:

Рассмотрим подробнее первые два

Distribution Package

Многие дистрибутивы уже имеют в репозиториях готовый к использованию пакет borgbackup который можно просто установить через пакетный менеджер

Может быть доступна далеко не последняя версия

Для Debian: apt install borgbackup

Standalone Binary

Есть готовый бинарь собранный через http://www.pyinstaller.org/
Доступен тут https://github.com/borgbackup/borg/releases

Работает очень даже хорошо

Надо держать в голове что эта штука распаковывает себя в /tmp, и если там не будет места или будет стоять опция noexec то все сломается. Можно изменить директорию для распаковки задав переменную TEMP перед запуском бинаря

Quick Start

Эта часть поможет начать работать с Borg и покрывает разные кейсы

A step by step example

  1. Перед тем как делать бэкап должен быть инициализирован репозиторий:
    $ borg init --encryption=repokey /path/to/repo
    
  2. Бэкап директорий ~/src и ~/Documents в архив с именем Monday:
    $ borg create /path/to/repo::Monday ~/src ~/Documents
    
  3. На следующий день создается архив с именем Tuesday:
    $ borg create --stats /path/to/repo::Tuesday ~/src ~/Documents
    
    Этот бэкап будет значительно быстрее и меньше, потому что будут сохранены только новые ранее не сохраненные данные. Флаг --stats выводит статистику по созданному архиву (например такая информация как занятое место и время выполнения)
  4. Список всех архивов в репозитории:
    $ borg list /path/to/repo
    
  5. Список файлов конкретного архива:
    $ borg list /path/to/repo::Monday
    
  6. Извлечь из архива файлы относительно текущей директории:
    $ borg extract /path/to/repo::Monday
    
  7. Удалить архив (это не освобождает диск):
    $ borg delete /path/to/repo::Monday
    
  8. Сжатие сегментов в репозитории:
    $ borg compact /path/to/repo
    
    Пример:
    root@borg-server:~# du -sch /var/lib/borg/my-test-backup-repo
    262M	/var/lib/borg/my-test-backup-repo
    262M	total
    root@borg-server:~# borg compact /var/lib/borg/my-test-backup-repo
    root@borg-server:~# du -sch /var/lib/borg/my-test-backup-repo
    6.0M	/var/lib/borg/my-test-backup-repo
    6.0M	total
    

По умолчанию Borg все делает молча, чтобы он давал какой-то вывод нужно использовать специальные флаги: --progress, --list, --verbose и --info

Archives and repositories

Borg-архив это результат бэкапа (результат команды borg create). Архив хранит снапшот данных файлов внутри себя. Который потом можно извлечь или примонтировать для восстановления

Репозитории это директории в файловой системе которые являются хранилищами архивов. Доступ в репозиторий может быть получен как локально так и по ссш. Под капотом репозиторий хранит блоки данных и манифест отслеживающий какой блок из какого архива. Если какие-то данные не изменялись из одного бэкапа к другому, то Borg просто ссылается на уже загруженые блоки данных (дедупликация)

Important note about free space

Нужно следить что на сервере с репозиторием всегда достаточно свободного диска (а также в ~/.cache). Несколько гигабайтов должно быть достаточно в общем случае

Borg не использует диск зарезервированный для рута (даже когда запущен от рута). Рекомендуется зарезервировать некоторое пространство в самом Borg через опцию additional_free_space, начать можно с 2G

borg config /path/to/repo additional_free_space 2G

Если Borg запускается без свободного диска, он попытается высвободить диск (столько сколько сможет), а пока будет безопасно отвергать текущие операции, это позволит пользователю освободить больше диска с помощью удаления архивов
Если у вас реально кончится диск, то ему будет сложно или даже невозможно освободить себе диск, потому что доп место на диске нужно для всех операций, даже для операции удаления

Ты можешь использовать мониторинг или просто включить инфу о свободном месте в логи

Important note about permissions

Чтобы избежать проблем с правами (в репозитории или в кэше) всегда работай с репозиторием из под одного и того же пользователя

Если ты хочешь бэкапить файлы других пользователей или операционных систем, скорее всего потребуется запускать Borg от рута. Если бэкапите только свои файлы то запускайте Borg как обычный пользователь

Для локального репозитория просто всегда используй одного и того же пользователя для работы с Borg

Для удаленного репозитория: всегда используй напр. borg@remote_host. Вы можете использовать это и от разных локальных пользователей, удаленный пользователь запускающий Borg и работающий с репозиторием, всегда будет borg

Если нужно получить доступ к локальному репозиторию от другого пользователя, можно использовать этот же метод через ссш до borg@localhost

Important note about files changing during the backup process

Borg ничего не делает с консистентностью данных которые он бэкапит. Он просто читается и бэкапит каждый файл независимо от того в каком состоянии этот файл пребывает во время бэкапирования. На активных системах это может привести в двум видам неконсистентности:

Если у вас есть набор файлов и нужно быть уверенным что они забэкапятся в конкретном или консистентном состоянии, нужно предпринять шаги для предотвращения изменения этих файлов во время бэкапа. Есть несколько общих техник для достижения этой цели

Для некоторых систем Borg может работать хорошо и без этих предосторожностей. Если ты просто бэкапишь файлы не не очень активной системе, то Borg отработает хорошо без дополнительной заботы о консистентности. Лог файлы и кэши могут быть в не идеальном состоянии, но это и не проблема

Для баз данных, виртуальных машин и контейнеров есть специальные техники бэкапинга, которые не просто бэкапят нижележащую ФС. Для баз данных ищи в документации техники бэкапирования которые позволят сохранять состояние данных между транзакциями. Для виртуальных машин рассмотри бэкапинг машины с нее же самой или монтаж диска выключенной ВМ. Для контейнеров поможет команда docker save

Automating backups

Ниже простой скрипт который подразумевается что будет запускаться ежедневно от рута на разных локальных машинах. Он бэкапит важные файлы машины в репозиторий ~/backup/main на удаленном сервере. Некоторые файлы исключаются из бэкапа
После бэкапирования этот скрипт также запускает borg prune для сохранения только некоторого количества последних архивов и удаления остальных
В конце он запускает borg compact для удаления уже удаленных объектов из сегментов файлов в репозитории для сохранения диска

Перед запуском убедись что репозиторий инициализирован и что скрипт имеет правильные права и может быть запущен рутом но не может быть запущен или прочитан кем либо еще (root:root 0700)

Можно использовать этот скрипт как стартовую точку и модифицировать его под себя
Не забудь протестировать свои бэкапы, убедись что prune удаляет то что нужно и что в архивах забэкаплено то что нужно

#!/bin/sh

# Setting this, so the repo does not need to be given on the commandline:
export BORG_REPO=ssh://username@example.com:2022/~/backup/main

# See the section "Passphrase notes" for more infos.
export BORG_PASSPHRASE='XYZl0ngandsecurepa_55_phrasea&&123'

# some helpers and error handling:
info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; }
trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM

info "Starting backup"

# Backup the most important directories into an archive named after
# the machine this script is currently running on:

borg create                         \
    --verbose                       \
    --filter AME                    \
    --list                          \
    --stats                         \
    --show-rc                       \
    --compression lz4               \
    --exclude-caches                \
    --exclude 'home/*/.cache/*'     \
    --exclude 'var/tmp/*'           \
                                    \
    ::'{hostname}-{now}'            \
    /etc                            \
    /home                           \
    /root                           \
    /var

backup_exit=$?

info "Pruning repository"

# Use the `prune` subcommand to maintain 7 daily, 4 weekly and 6 monthly
# archives of THIS machine. The '{hostname}-*' matching is very important to
# limit prune's operation to this machine's archives and not apply to
# other machines' archives also:

borg prune                          \
    --list                          \
    --glob-archives '{hostname}-*'  \
    --show-rc                       \
    --keep-daily    7               \
    --keep-weekly   4               \
    --keep-monthly  6

prune_exit=$?

# actually free repo disk space by compacting segments

info "Compacting repository"

borg compact

compact_exit=$?

# use highest exit code as global exit code
global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit ))
global_exit=$(( compact_exit > global_exit ? compact_exit : global_exit ))

if [ ${global_exit} -eq 0 ]; then
    info "Backup, Prune, and Compact finished successfully"
elif [ ${global_exit} -eq 1 ]; then
    info "Backup, Prune, and/or Compact finished with warnings"
else
    info "Backup, Prune, and/or Compact finished with errors"
fi

exit ${global_exit}

Pitfalls with shell variables and environment variables

Это относится ко всем переменным окружения, которые вы хотите, чтобы Borg видел, а не только к BORG_PASSPHRASE. Краткое объяснение таково: всегда экспортируйте свою переменную и используйте одинарные кавычки, если вы не уверены в деталях поведения расширения вашей оболочки. Например.:

export BORG_PASSPHRASE='complicated & long'

Это потому что экспорт дает доступ к переменным дочерним процессам одним из которых может быть Borg

Внимательнее с тем как sudo работает с экспортами
Например ты можешь удивиться что в примере ниже скрипту не будет доступна переменная и он будет запрашивать ввод пароля

export BORG_PASSPHRASE='complicated & long'
sudo ./yourborgwrapper.sh  # still prompts for password

Можно обойти это так

vandud@macbook: ~ 🚀 export MYVAR=test1
vandud@macbook: ~ 🚀 sudo -E bash -c 'echo $MYVAR'
test1
vandud@macbook: ~ 🚀 sudo bash -c 'echo $MYVAR'

Passphrase notes

Если ты используешь шифрование (или аутентификацию), Borg интерактивно спросит у тебя пароль

Подробнее тут https://borgbackup.readthedocs.io/en/stable/quickstart.html#passphrase-notes

Backup compression

По умолчанию для сжатия используется lz4 (очень быстро, но слабое сжатие), но так же доступны и другие методы сжатия
Можно использовать zstd с большим диапазоном от высокой скорости (N=1) до высокой компрессии (N=22)

zstd это современный алгоритм сжатия, который предпочтительнее zlib и lzma (исключая случаи когда нужна обратная совместимость со старыми версиями Borg)

$ borg create --compression zstd,N /path/to/repo::arch ~

Можно не использовать сжатие, например когда есть быстрый сторадж и не хочется расходовать cpu

$ borg create --compression none /path/to/repo::arch ~

Если у тебя менее быстрый сторадж под репозиторием и ты хочешь чуть большую компрессию (степень компрессии можно регулировать, у zlib это диапазон от 0 до 9)

root@borg-server:~# borg create --compression zlib,9 /var/lib/borg/my-test-backup-repo/::my-test-super-backup /var/log/

Если у тебя сильно медленный сторадж и ты хочешь сильную компрессию то можно использовать lzma
Для каждого конкретного кейса нужно искать параметры опытным путем (во время опытов держи один глаз на нагрузке на ЦПУ а второй на нагрузке на сеть)

Repository encryption

Во время создания репозитория можно сделать его шифрованным
Подробности тут https://borgbackup.readthedocs.io/en/stable/quickstart.html#repository-encryption

Remote repositories

Borg может инициализировать и работать с репозиториями на удаленных хостах если хост доступен по ssh. Быстрее и проще когда Borg установлен на удаленном хосте. В таком случае используется следующий синтаксис:

root@borg-client:~# borg init -e none root@borg-server:/var/lib/borg/remotely-initialized-repository
...
root@borg-server:/var/lib/borg# ls
my-test-backup-repo  remotely-initialized-repository

Удаленные операции через SSH могут быть автоматизированы с помощью ключей. Ты можешь ограничить использование пары ключей предварив описание публичного ключа командой (на удаленном сервере). Пример нижу запустит Borg в режиме сервера и ограничит его работу конкретным путем:

root@borg-server:~# cat .ssh/authorized_keys
command="borg serve --restrict-to-path /var/lib/borg/",restrict ssh-rsa AAAAB3N...yxHgs= root@borg-client
root@borg-client:~# borg init -e none root@borg-server:/var/tmp/remotely-initialized-repository
Repository path not allowed: /var/tmp/remotely-initialized-repository
root@borg-client:~# borg init -e none root@borg-server:/var/lib/borg/remotely-initialized-repository
root@borg-client:~#

Если нет возможности установить Borg на удаленный хост то можно использовать sshfs:

$ sshfs user@hostname:/path/to /path/to
$ borg init /path/to/repo
$ fusermount -u /path/to

Restoring a backup

Для восстановления обычно используется тот же самый хост и пользователь как когда бэкап был сделан. Такой расклад позволит избежать некоторых проблем:

Пользователь может быть:

Borg репозиторий может быть:

Если репозиторий шифруется тебе так же потребуется ключ и контрольная фраза (которая защищает ключ)
Ключ может быть расположен:

Контрольная фраза для ключа может быть:


Есть два путя при восстановлении файлов из бэкапа:

Пример borg mount:

root@borg-client:~# mkdir /mnt/vandud-test-backup-dirs
root@borg-client:~# borg mount root@borg-server:/var/lib/borg/my-test-backup-repo::vandud-test-backup-dirs /mnt/vandud-test-backup-dirs
root@borg-client:~# ls /mnt/vandud-test-backup-dirs
bin  etc  sbin

root@borg-client:~# borg umount /mnt/vandud-test-backup-dirs

root@borg-client:~# borg mount root@borg-server:/var/lib/borg/my-test-backup-repo /mnt/vandud-test-backup-dirs
root@borg-client:~# ls /mnt/vandud-test-backup-dirs/
localhost-2023-12-13-16:50:28  localhost-2023-12-13-16:51:29  my-test-super-backup  vandud-test-backup-2  vandud-test-backup-dirs

Пример borg extract:

# borg extract always extracts into current directory and that directory
# should be empty (borg does not support transforming a non-empty dir to
# the state as present in your backup archive).
mkdir borg_restore
cd borg_restore

# now we find out the archive names we have in the repo:
borg list /mnt/backup/borg_repo

# we could find out the archive contents, esp. the path layout:
borg list /mnt/backup/borg_repo::myserver-system-2019-08-11

# we extract only some specific path (note: no leading / !):
borg extract /mnt/backup/borg_repo::myserver-system-2019-08-11 path/to/extract

# alternatively, we could fully extract the archive:
borg extract /mnt/backup/borg_repo::myserver-system-2019-08-11

# now move the files to the correct place...

Отличия использования удаленного сервера:
Базово все то же самое как если бы использовался локальный репозиторий, только нужно ссылаться на удаленный репозиторий по ссылке вида ssh://...


Остальные подробности начиная отсюда https://borgbackup.readthedocs.io/en/stable/usage/general.html

Borgmatic

Borgmatic это обертка вокруг Borg
В документации Borg есть такой раздел https://borgbackup.readthedocs.io/en/stable/quickstart.html#automating-backups, в нем предоставлен скрипт для автоматизации бэкапирования
По сути Borgmatic это и есть подобный скрипт - красивая запускалка для Borg


Можно установить через пакетный менеджер
Установится скорее всего старая версия в которой нет команды borgmatic config generate
Надо использовать отдельный скрипт generate-borgmatic-config (он уже идет в пакете borgmatic)

Запускаем:

root@borg-server:~# generate-borgmatic-config
Generated a sample configuration file at /etc/borgmatic/config.yaml.

Please edit the file to suit your needs. The values are representative.
All fields are optional except where indicated.

If you ever need help: https://torsion.org/borgmatic/#issues

Получаем огромный конфиг в котором все закомментировано и прокомментировано
То есть мы получаем базу на основе которой можно собирать свой конфиг


Ansible Playbook

Предлагаю использовать следующий плейбук

---

- name: Borg
  hosts: all
  become: true
  vars:
    borg_data_dir: /opt/data/borg
    borg_encryption_passphrase: CHANGEME
    borg_repository: "root@{{ hostvars[groups['borg'][0]].ansible_host }}:{{ borg_data_dir }}/{{ inventory_hostname }}"
    borg_ssh_key_file_path: "/root/.ssh/id_ed25519"
    borg_ssh_command: "ssh -o StrictHostKeyChecking=accept-new"
    borg_install_method: "pip"
      #    borg_source_directories: # this overrides values from inventory
      #      - /home
      #      - /etc
    borg_exclude_patterns:
      - "{{ borg_data_dir }}"
  tasks:
    - name: "Ensure {{ borg_data_dir }} exists"
      ansible.builtin.file:
        path: "{{ borg_data_dir }}"
        state: directory
        mode: "0755"
        owner: root
      tags:
        - never
        - backup_init_repo

    - name: Configure Borgbackup and Borgmatic
      tags:
        - always
        - install_backup
      ansible.builtin.include_role:
        name: borgbase.ansible_role_borgbackup
        apply:
          tags:
            - always
    - name: Copy SSH-Key to Target {{ borg_repository }} and Init Repo
      tags:
        - never
        - backup_init_repo
      block:
        - name: Read ssh key
          ansible.builtin.slurp:
            src: "{{ borg_ssh_key_file_path }}.pub"
          register: backup_local_ssh_key

        - name: Set authorized key taken from file
          ansible.posix.authorized_key:
            user: "{{ borg_repository | regex_search('(.*)@', '\\1') | first }}" # part a)
            state: present
            key_options: "command=\"borg serve --restrict-to-path {{ borg_data_dir }}\",restrict"
            key: "{{ backup_local_ssh_key['content'] | b64decode }}"
            comment: "{{ inventory_hostname }}"
          delegate_to: "{{ borg_repository | regex_search('@(.*):', '\\1') | first }}" # part b)

        - name: Init repository
          ansible.builtin.command:
            cmd: "sudo -u root borgmatic init --encryption none --append-only"

Ansible Inventory

Инвентори для этого плейбука прост (в базовом случае)
Главное определить группу borg и один хост в ней
Этот хост будет хранить бэкапы
Группа nodes может называться как угодно, потому что плейбук катится на all и использует хост из borg как адрес borg_repository в конфиге borgmatic

Ниже пример ini-inventory:

[borg] # хост из этой группы будет Borg-сервером
borg-server ansible_host=62.84.119.86

[nodes] # не важно как называется эта группа, плейбук катится на 'all'
borg-client-1 ansible_host=51.250.87.234
borg-client-2 ansible_host=51.250.66.184
borg-client-3 ansible_host=51.250.6.55

В самом низу страницы описан пример yaml-inventory который позволяет прописать кастомные параметры разным хостам (можно сделать и полноценный инвентори с host_vars/group_vars, тут как душе угодно)

Commands

Прикатить это все можно так
Сперва установим официальную роль конкретной версии (в моем случае были машины с ubuntu-18, актуальная версия роли не катится на такое старье):

ansible-galaxy role install borgbase.ansible_role_borgbackup,v0.9.4 --force # в моем случае были машины с ubuntu 18.04, поэтому пришлось использовать старую версию роли (новая не катится на такие старые машины)

Далее катим плейбук сперва базово, потом с тэгом который синхронизирует ssh-ключ и создаст репы на сервере из группы borg:

ANSIBLE_HOST_KEY_CHECKING=false ansible-playbook -i inventory.mkrf-test.yaml -kK playbook.yaml
ANSIBLE_HOST_KEY_CHECKING=false ansible-playbook -i inventory.mkrf-prod.yaml -kK -t backup_init_repo playbook.yaml

Что бэкапить задается через переменную borg_source_directories


Проверка:

root@mon-01:~# borgmatic
Fri Dec 22 17:44:08 MSK 2023 - Starting backup.
Fri Dec 22 17:44:08 MSK 2023 - Run pg_dumpall command.
could not change directory to "/root": Permission denied
Fri Dec 22 17:44:37 MSK 2023 - Command pg_dumpall is done.
Ignoring check_last option, as "archives" is not in consistency checks.
Ignoring consistency prefix option, as "archives" is not in consistency checks.
Fri Dec 22 18:32:27 MSK 2023 - Remove pg_dumpall file.
Fri Dec 22 18:32:27 MSK 2023 - Finished backup.

root@mon-01:~# cat /etc/cron.d/borgmatic
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
#Ansible: borgmatic
53 4 * * * root borgmatic -c /etc/borgmatic/config.yaml -C -p
#Ansible: borgmatic-check
53 22 27 * * root borgmatic -c /etc/borgmatic/config.yaml -k
root@vm-bkpp-01:~# du -sch /opt/data/borg/vm-mon-01/
18G	/opt/data/borg/vm-mon-01/
18G	total

root@vm-bkpp-01:~# borg list /opt/data/borg/vm-mon-01/
Warning: Attempting to access a previously unknown unencrypted repository!
Do you want to continue? [yN] y
mon-01-2023-12-22-174440     Fri, 2023-12-22 17:44:41 [dd517ed98188876bc68704f5882c1610421aa9e742584ee4acb27ff8e5ab7a89]

root@vm-bkpp-01:~# borg list /opt/data/borg/vm-mon-01/::mon-01-2023-12-22-174440 | head
drwxr-xr-x root   root          0 Fri, 2023-12-22 17:39:38 etc
drwxr-xr-x root   root          0 Tue, 2019-12-03 12:12:55 etc/update-manager
-rw-r--r-- root   root        235 Thu, 2019-06-06 21:46:11 etc/update-manager/meta-release
-rw-r--r-- root   root        807 Wed, 2019-06-19 19:49:56 etc/update-manager/release-upgrades
drwxr-xr-x root   root          0 Wed, 2019-06-19 19:49:56 etc/update-manager/release-upgrades.d
drwxr-xr-x root   root          0 Tue, 2019-12-03 11:54:23 etc/update-notifier
-rw-r--r-- root   root          0 Tue, 2019-12-03 11:54:23 etc/update-notifier/hooks_seen
drwxr-xr-x root   root          0 Tue, 2019-12-03 11:53:08 etc/gss
drwxr-xr-x root   root          0 Fri, 2019-01-11 18:48:01 etc/gss/mech.d
drwxr-xr-x root   root          0 Tue, 2019-12-03 11:53:22 etc/polkit-1

Ansible Inventory YAML

borg:
  hosts:
    vm-bkp-01:
      ansible_host: 192.168.50.140
nodes:
  hosts:
    grun-01:
      ansible_host: 192.168.50.129
    vm-db-01:
      ansible_host: 192.168.50.124
      borgmatic_hooks:
        on_error:
          - echo "`date` - Error while creating a backup."
        before_backup:
          - echo "`date` - Starting backup."
          - echo "`date` - Run pg_dumpall command."
          - mkdir -p /tmp/pg_dumpall; chmod 777 /tmp/pg_dumpall; sudo -u postgres pg_dumpall -f "/tmp/pg_dumpall/`hostname`_`date +%d-%m-%Y`_dumpall.sql"
          - echo "`date` - Command pg_dumpall is done."
        after_backup:
          - echo "`date` - Remove pg_dumpall file."
          - rm /tmp/pg_dumpall/`hostname`_`date +%d-%m-%Y`_dumpall.sql
          - echo "`date` - Finished backup."
      borg_source_directories:
        - /etc/
        - /home/
        - /tmp/pg_dumpall/
    vm-docs-01:
      ansible_host: 192.168.50.125
      borg_source_directories:
        - /etc/
        - /home/
        - /opt/
    vm-glog-01:
      ansible_host: 192.168.50.127
      borgmatic_hooks:
        on_error:
          - echo "`date` - Error while creating a backup."
        before_backup:
          - echo "`date` - Starting backup."
          - echo "`date` - Run mongodump command."
          - mkdir -p /tmp/mongodump; chmod 777 /tmp/mongodump; mongodump --gzip --archive=/tmp/mongodump/`hostname`_`date +%d-%m-%Y`_mongodump.gz
          - echo "`date` - Command mongodump is done."
        after_backup:
          - echo "`date` - Remove mongodump file."
          - rm /tmp/mongodump/`hostname`_`date +%d-%m-%Y`_mongodump.gz
          - echo "`date` - Finished backup."
      borg_source_directories:
        - /etc/
        - /home/
        - /tmp/mongodump/
        - /usr/share/elasticsearch/
        - /usr/share/graylog-server/
    vm-k8sm-01:
      ansible_host: 192.168.50.121
    vm-k8sw-01:
      ansible_host: 192.168.50.122
    vm-k8sw-02:
      ansible_host: 192.168.50.123
    vm-mon-01:
      ansible_host: 192.168.50.128
      borgmatic_hooks:
        on_error:
          - echo "`date` - Error while creating a backup."
        before_backup:
          - echo "`date` - Starting backup."
          - echo "`date` - Run pg_dumpall command."
          - mkdir -p /tmp/pg_dumpall; chmod 777 /tmp/pg_dumpall; sudo -u postgres pg_dumpall -f "/tmp/pg_dumpall/`hostname`_`date +%d-%m-%Y`_dumpall.sql"
          - echo "`date` - Command pg_dumpall is done."
        after_backup:
          - echo "`date` - Remove pg_dumpall file."
          - rm /tmp/pg_dumpall/`hostname`_`date +%d-%m-%Y`_dumpall.sql
          - echo "`date` - Finished backup."
      borg_source_directories:
        - /etc/
        - /home/
        - /var/lib/grafana/
        - /tmp/pg_dumpall/
        - /opt/
      borg_exclude_patterns:
        - /opt/data/

    vm-mq-01:
      ansible_host: 192.168.50.126
all:
  vars:
    ansible_user: user # от этого пользователя ансибл спросит пароль
    borg_source_directories:
      - /etc
      - /home

mdadm

https://raid.wiki.kernel.org/index.php/A_guide_to_mdadm


mdadm заменил все предыдущие тулы для управления рейдами. Он менеджит почти всю юзер-спейсную часть рейдов. Есть только немного вещей которые требуют записи в /proc, но их немного


Modes

У mdadm есть 7 режимов. Но для нормальной (обычной) работы тебе потребуется лишь несколько из них:



Superblocks and Raid versions

Важная глава
Объясняет многое

cloud-init

Cloud-init - это индустриальный кросс-платформенный мульти-дистрибутивный стандарт для инициализации инстансов
Он поддерживается во всех крупных cloud-провайдерах, в системах провижининга для приватных инфраструктур и на bare-metal

При загрузке инстанса cloud-init понимает в каком облаке он запущен, читает предоставленную метадату и инициализирует систему соответствующим образом
Он может сетапить сеть, девайсы, ssh-ключи и много всего другого

Getting Started

Tutorial

Для обучения и тестирования cloud-init скриптов удобно использовать lxd (там из коробки хорошая поддержка cloud-init)

Вот такой терраформ манифест создает подходящую виртуалку

terraform {
  required_providers {
    yandex = {
      source = "yandex-cloud/yandex"
    }
  }
}

provider "yandex" {
  token     = "AQAAAAAayaoeuWVmM00"
  cloud_id  = "b1gaeounndt"
  folder_id = "b1gaaoeuobf5v"
  zone      = "ru-central1-c"
}

data "yandex_compute_image" "compute_image_search" {
  family = "ubuntu-2004-lts"
}

resource "yandex_compute_instance" "test" {
  platform_id = "standard-v1"
  zone        = "ru-central1-a"

  resources {
    cores         = 4
    core_fraction = 20
    memory        = 4
  }

  boot_disk {
    initialize_params {
      image_id = "${data.yandex_compute_image.compute_image_search.image_id}"
      size     = 30
    }
  }

  network_interface {
    subnet_id = "${yandex_vpc_subnet.test_network_subnet.id}"
    nat       = true
  }
  metadata = {
    ssh-keys = "ubuntu:${file("~/.ssh/id_rsa.pub")}"
  }
}

resource "yandex_vpc_network" "test_network" {}

resource "yandex_vpc_subnet" "test_network_subnet" {
  zone           = "ru-central1-a"
  network_id     = "${yandex_vpc_network.test_network.id}"
  v4_cidr_blocks = ["10.2.0.0/16"]
}

Далее заходим на нее, и налаживаем lxd

root@fhmkp99o5k8l3iultpf3:~# apt install snapd
root@fhmkp99o5k8l3iultpf3:~# snap install lxd
root@fhmkp99o5k8l3iultpf3:~# lxd init --minimal

Далее подготавливаем cloud-init конфиг

root@fhmkp99o5k8l3iultpf3:~# cat <<EOF > /tmp/user-data
> #cloud-config
> runcmd:
>   - echo 'Hello, World!' > /var/tmp/hello-world.txt
> EOF

Запускаем контейнер с этим конфигом

root@fhmkp99o5k8l3iultpf3:~# lxc launch ubuntu:focal my-test --config=user.user-data="$(cat /tmp/user-data)"

Идем внутрь и проверяем как оно там отработало

root@fhmkp99o5k8l3iultpf3:~# lxc shell my-test

root@my-test:~# cloud-init status -l
status: done
time: Wed, 10 Aug 2022 11:11:58 +0000
detail:
DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud-net][dsmode=net]

root@my-test:~# cloud-init query userdata
#cloud-config
runcmd:
  - echo 'Hello, World!' > /var/tmp/hello-world.txt

root@my-test:~# cloud-init schema --system --annotate
Valid cloud-config: system userdata

root@my-test:~# cat /var/tmp/hello-world.txt
Hello, World!

Сносим этот контейнер

root@fhmkp99o5k8l3iultpf3:~# lxc stop my-test
root@fhmkp99o5k8l3iultpf3:~# lxc rm my-test

Boot Stages

При загрузке из под systemd у cloud-init есть пять стадий:


Cloud-init понимает запускается он впервые на новом инстансе или нет, в зависимости от этого применяется соответсвующая конфигурация
При первой загрузке применяется вся per-instance конфигурация, а при повторной загрузке применяются per-boot конфиги

При запуске cloud-init оставляет кэш который хранит внутреннее состояние и используется между загрузками

Соответственно если кэш присутствует, значит cloud-init уже запускался на этой системе
Это могло случится если система была перезагружена или если файловая система была приаттачена к новому инстансу и фактически это первая загрузка системы но кэш в файловой системе уже присутствует (такое бывает когда инстанс запускается из образа созданного из уже запровижененной машины)
По умолчанию cloud-init пытается понять что за кейс у него сейчас, для этого он проверяет instance id из кэша и сравнивает с id который определяется окружением, если разные то это первая загрузка. Внутри это поведение называется check
Instance ID находится в /var/lib/cloud:

root@fhmkp99o5k8l3iultpf3:~# cat /var/lib/cloud/data/instance-id
fhmkp99o5k8l3iultpf3

Возможны ситуации когда это поведение нужно изменить, это измененное поведение называется trust и заставляет cloud-init безусловно доверять найденной в кеше инфе

Управлять этим можно через опцию manual_cache_clean:

Можно вручную снести кэш через cloud-init clean
Эта команда сносит все из /var/lib/cloud

root@fhmkp99o5k8l3iultpf3:~# tree /var/lib/cloud/ | wc -l
78
root@fhmkp99o5k8l3iultpf3:~# cloud-init clean
root@fhmkp99o5k8l3iultpf3:~# tree /var/lib/cloud/ | wc -l
4

CLI Interface

Актуальный список подкомманд доступен в хелпе cloud-init --help

User Data

Make

CouchDB

CouchDB схожа с MongoDB

CouchDB слушает 5984 порт
Работает по HTTP
В качестве языка запросов используется JavaScript

Terminal_2021-02-11-06-52-39.png


По такому адресу можно найти futon (панель управления)
http://servername.ru:5984/_utils/