Библиотека — Обратный прокси и TLS: nginx vs Caddy [X]
Шифр
reverse-proxy-tls
Разделы
network security tooling
Сверено
2 сентября — в этот день утверждения статьи сверялись с первоисточниками, а не правился текст
Объём
29 045 знаков · 18 разделов · чтения около 30 минут

Обратный прокси и TLS: nginx vs Caddy


Обзор

Типичный самостоятельно поднятый сервис слушает на 127.0.0.1 и каком-нибудь произвольном порту: Sonarr на 8989, Radarr на 7878, Prowlarr на 9696, Plex на 32400 (см. медиасервер). Пока к нему ходят с той же машины, этого достаточно. Как только сервис нужно открыть наружу, выясняется, что порт — плохая единица публикации: его нужно помнить, его нельзя защитить сертификатом, выданным на имя, и открывать десяток портов в firewall никто не хочет.

Обратный прокси решает эту задачу подменой единицы адресации: наружу смотрит один порт 443, а разбор идёт по имени хоста из TLS SNI и заголовка Host. Всё, что за прокси, остаётся на loopback и в firewall не появляется вовсе. Побочно прокси становится местом, где централизованы TLS, логи доступа, лимиты и заголовки — то есть всё, что не хочется реализовывать в каждом сервисе отдельно.

Практический выбор при старте сводится к двум серверам: nginx и Caddy. Разница между ними не столько в возможностях, сколько в том, что каждый считает работой администратора, а что — своей. Caddy получает и продлевает сертификаты сам, без внешних инструментов, и его конфиг для типовой задачи занимает три строки. nginx традиционно перекладывает TLS на отдельный ACME-клиент, зато даёт заметно более тонкий контроль над всем остальным. Начиная с версии 1.29.0 у nginx появился собственный ACME-модуль, и этот водораздел стал менее резким — но, как обычно, с оговорками.

Что именно делает обратный прокси

Терминация TLS — главная функция. Клиент устанавливает TLS-соединение с прокси, прокси расшифровывает трафик и передаёт его дальше по HTTP, обычно по loopback. Сертификат нужен только прокси; ни один из проксируемых сервисов о TLS не знает и знать не должен. Это же объясняет, почему X-Forwarded-Proto приходится проставлять руками: приложение видит обычный HTTP и без подсказки будет считать, что работает по незащищённому каналу.

Маршрутизация по имени идёт в два этапа, и их полезно различать. Сначала клиент в TLS-хендшейке передаёт SNI — по нему прокси выбирает, каким сертификатом отвечать. Только после расшифровки становится виден заголовок Host, по которому выбирается upstream. Отсюда следствие: сертификат должен существовать до того, как прокси увидит содержимое запроса, и «выдать сертификат по факту первого обращения» — это отдельный механизм (у Caddy — on-demand TLS), а не поведение по умолчанию.

Буферизация и таймауты — то место, где прокси перестаёт быть прозрачным. По умолчанию nginx накапливает ответ upstream в память и на диск, прежде чем отдавать клиенту. Для API это разумно: медленный клиент не держит воркер приложения. Для потокового видео или server-sent events это ломает всё — ответ никогда не заканчивается, буфер растёт, клиент ждёт. Поэтому Plex, стримы и SSE требуют явного отключения буферизации.

WebSocket проходит через прокси только при явном сотрудничестве. Протокол начинается с обычного HTTP-запроса с заголовками Upgrade: websocket и Connection: Upgrade, после чего соединение перестаёт быть HTTP. Connection — hop-by-hop заголовок, то есть прокси обязан его не пересылать, и без специальной настройки апгрейд молча не происходит. Внешне это выглядит как «интерфейс загрузился, но ничего не обновляется».

DNS: предварительное условие

Пока имя не резолвится в адрес прокси, ни сертификат, ни маршрутизация не заработают: HTTP-01 требует, чтобы удостоверяющий центр достучался до вас по этому имени.

Для сервера с постоянным адресом достаточно A-записи (и AAAA, если есть IPv6). Отдельные сервисы удобно вешать не отдельными записями, а CNAME на основное имя или wildcard-записью *.example.com — тогда добавление нового сервиса не требует правки DNS вообще.

TTL стоит понизить заранее, за сутки до планируемого переезда, а не в момент переезда: старое значение уже разошлось по резолверам и будет жить положенное ему время. Для домашнего сервера с меняющимся адресом нужен dynamic DNS — либо у регистратора с API, либо через сервис вроде duckdns; практическая часть здесь тривиальна, важно лишь помнить, что TTL таких записей должен быть минимальным.

Проверять стоит не тем резолвером, который настроен локально, а авторитетным сервером зоны — иначе легко полчаса смотреть на закешированный старый ответ:

dig +short sonarr.example.com
dig +short @1.1.1.1 sonarr.example.com          # мимо локального кеша
dig +trace sonarr.example.com | tail -5         # цепочка делегирования целиком

ACME: как появляется сертификат

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

HTTP-01 — центр обращается к http://<имя>/.well-known/acme-challenge/<token> и сверяет содержимое. Просто и не требует ничего, кроме доступного снаружи порта 80. Именно поэтому порт 80 нельзя закрывать «за ненадобностью»: без него продление сломается, причём молча и через два месяца после настройки.

DNS-01 — клиент создаёт TXT-запись _acme-challenge.<имя>. Требует API у DNS-провайдера, зато не требует вообще никакой доступности сервера снаружи. Это единственный способ получить wildcard-сертификат и единственный работающий вариант для сервиса, который наружу не смотрит (внутренний хост, сервер за NAT без проброса).

TLS-ALPN-01 — доказательство внутри самого TLS-хендшейка на порту 443, через отдельный ALPN-протокол. Полезен, когда 80-й порт занят или закрыт, но wildcard через него получить нельзя.

Сроки жизни и профили

Let's Encrypt переводит выдачу на короткие сертификаты поэтапно, через механизм ACME-профилей. По состоянию на август 2026 картина такая:

Профиль Срок Примечание
classic 90 дней используется по умолчанию, если профиль не запрошен явно
tlsserver 45 дней переведён на 45 дней 13 мая 2026
shortlived ~6 дней то же, что tlsserver, но с недельным сроком

Профиль classic перейдёт на 64 дня 10 февраля 2027 и на 45 дней 16 февраля 2028; параллельно сокращается срок переиспользования авторизации — с нынешних 30 дней до 7 часов к 2028 году. Практический вывод один и он не нов, но с каждым годом жёстче: продление обязано быть автоматическим и обязано мониториться. Схема «получил сертификат руками, поставил напоминание в календарь» перестаёт работать физически.

Лимиты выдачи считаются по registered domain и рассчитаны на то, что вы не упрётесь в них при нормальной эксплуатации; продления из-под лимита на новые имена выведены. Упираются в лимиты обычно при отладке — когда конфиг не работает и хочется перевыпустить сертификат ещё раз. Для этого есть staging-окружение: сертификаты из него невалидны для браузера, но лимиты там несопоставимо выше. Отлаживать нужно на staging, переключаясь на боевой ACME-эндпоинт только когда всё сошлось.

nginx vs Caddy

Разница между ними — это разница в ответе на вопрос «кто отвечает за сертификат».

Caddy считает, что HTTPS — не фича, а состояние по умолчанию. Он сам определяет, для каких имён нужны сертификаты, сам получает их при первом запуске, сам продлевает, сам держит редирект с 80 на 443 и сам выбирает разумные параметры TLS. Конфиг для типового случая — имя и адрес upstream, больше ничего. Пока задача типовая, писать нечего; ошибиться тоже почти негде.

nginx исходит из того, что решения принимает администратор. Он не будет получать сертификат, пока ему не скажут, и не поставит редирект, пока его не напишут. Взамен он даёт то, чего у Caddy нет в сопоставимом виде: кеширование, тонкое управление буферами и таймаутами, лимиты запросов и соединений, stream-модуль для проксирования произвольного TCP/UDP, огромный корпус готовых рецептов под любой софт и предсказуемое поведение под нагрузкой, изученное за два десятилетия.

Собственный ACME-модуль сблизил их, но не уравнял. Модуль ngx_http_acme_module написан на Rust, поставляется как динамический модуль (готовые пакеты — начиная с nginx 1.29.0) и умеет HTTP-01, а с версии модуля 0.2.0 — и TLS-ALPN-01. DNS-01 он не поддерживает, а значит wildcard-сертификаты по-прежнему требуют внешнего клиента вроде certbot, lego или acme.sh. Кроме того, модуль есть далеко не в каждом дистрибутивном пакете nginx, и это стоит проверить до того, как строить на нём конфигурацию.

nginx Caddy
TLS из коробки нет; ACME-модуль с 1.29.0 либо внешний клиент да, включая редирект с 80
DNS-01 / wildcard только внешним клиентом да, но провайдер вкомпилируется в бинарник
Объём конфига для типовой задачи ~20 строк ~3 строки
HTTP/3 с 1.25.0, включается явно, помечен экспериментальным включён по умолчанию
Кеширование, лимиты, TCP/UDP-проксирование да ограниченно
Доверие к X-Forwarded-* нужно настроить самому по умолчанию игнорируется
Готовые рецепты под конкретный софт практически под всё заметно меньше
Модули динамические, подключаются в рантайме вкомпилируются при сборке

Выбор по ситуации выходит довольно чёткий. Для домашнего сервера, VPS с несколькими сервисами и вообще для всего, где хочется настроить один раз и забыть, — Caddy: он закрывает 90 % задач тремя строками и не даёт забыть про продление. Для случаев, где нужен кеш, тонкие лимиты, проксирование не-HTTP или где уже есть работающая nginx-инфраструктура и штат рецептов, — nginx.

Отдельно стоит отметить асимметрию в расширяемости, которая всплывает не сразу. У nginx модули динамические: поставил пакет, добавил load_module, перезапустил. У Caddy DNS-провайдеры и прочие плагины вкомпилируются в бинарник — через xcaddy build --with ... либо командой caddy add-package, которая пересобирает бинарник на месте. Для одиночного сервера это разовое неудобство, но оно ломает привычную схему «обновляю пакеты пакетным менеджером»: собранный вручную Caddy обновляется отдельно.

Существенная поправка: то, что написано выше про возможности, относится к актуальным версиям upstream, а в дистрибутивах лежит не они. Разрыв здесь больше обычного. Встроенный ACME-модуль появился в nginx 1.29.0 — в стабильных дистрибутивах с замороженными версиями (Debian 13 — nginx 1.26.3, RHEL-семейство — как правило ещё старше) его просто нет, тогда как в rolling-дистрибутивах он уже доступен. С Caddy та же история наоборот: пакет может быть на несколько минорных версий позади (в Debian 13 — 2.6.2 при текущей ветке 2.11), и, что важнее, DNS-провайдеры в чужой бинарник не добавить.

Практический вывод один: прежде чем строить конфигурацию на встроенном ACME-модуле или на DNS-01 в Caddy, нужно проверить, что именно стоит в системе. Если версия не дотягивает, вариантов два — классическая связка nginx + certbot, которая работает на любой версии, либо upstream-репозиторий проекта. Второй путь всегда имеет цену: пакет выпадает из-под security-обновлений дистрибутива, и следить за ним теперь ваша забота.

Практика: один сервис, два сервера

Задача одна и та же — опубликовать Sonarr с 127.0.0.1:8989 под именем sonarr.example.com. Конфиги ниже от дистрибутива не зависят; различаются имена пакетов, пути и способ управления сервисом.

Установка

Дистрибутивный путь — всё из штатного пакетного менеджера, обновляется вместе с системой:

Дистрибутив nginx + ACME-клиент Caddy
Debian/Ubuntu apt install nginx certbot python3-certbot-nginx apt install caddy
RHEL/Fedora dnf install nginx certbot python3-certbot-nginx dnf install caddy
Arch pacman -S nginx certbot certbot-nginx pacman -S caddy

Ставить оба сервера сразу не нужно и вредно: они подерутся за 80 и 443.

Если версия в дистрибутиве не дотягивает до нужной возможности — встроенного ACME-модуля nginx, актуального HTTP/3, DNS-провайдеров Caddy — подключается репозиторий upstream. У nginx это официальный репозиторий nginx.org с отдельным пакетом nginx-module-acme, у Caddy — репозиторий проекта на Cloudsmith. Проверить, что реально стоит:

nginx -v
caddy version

Отдельный случай — Caddy с DNS-провайдером для wildcard: его не ставят, а собирают, потому что провайдеры вкомпилируются в бинарник (см. ниже).

Caddy

# /etc/caddy/Caddyfile
sonarr.example.com {
    reverse_proxy 127.0.0.1:8989
}

Это полный конфиг. Caddy получит сертификат при старте, поднимет редирект с 80 на 443, проставит X-Forwarded-For, X-Forwarded-Proto и X-Forwarded-Host (остальные заголовки, включая Host, уходят к upstream как есть), и будет продлевать сертификат сам. Остальные сервисы добавляются такими же блоками:

radarr.example.com {
    reverse_proxy 127.0.0.1:7878
}

plex.example.com {
    reverse_proxy 127.0.0.1:32400 {
        flush_interval -1          # отключить буферизацию: стриминг
    }
}

Перечитать конфиг без разрыва соединений:

caddy validate --config /etc/caddy/Caddyfile
systemctl reload caddy

nginx с внешним ACME-клиентом

Классический вариант — сертификат получает certbot, nginx только читает файлы:

certbot certonly --nginx -d sonarr.example.com
certbot renew --dry-run                   # проверить, что продление реально работает
systemctl list-timers 'certbot*'          # убедиться, что таймер существует и активен

Конфиг сайта:

# /etc/nginx/conf.d/upgrade-map.conf — один раз на весь сервер
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
# /etc/nginx/conf.d/sonarr.conf
server {
    listen 80;
    server_name sonarr.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;                             # с 1.25.1 вместо параметра listen ... http2
    server_name sonarr.example.com;

    ssl_certificate     /etc/letsencrypt/live/sonarr.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sonarr.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8989;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

Двадцать строк против трёх — и каждая из них может быть забыта. Именно этот разрыв Caddy и закрывает.

nginx -t && systemctl reload nginx

nginx со встроенным ACME-модулем

Готовые пакеты nginx-module-acme выпускаются начиная с nginx 1.29.0 (из исходников модуль собирается и против более старых веток, от 1.22.0), поэтому в дистрибутивах с замороженными версиями его обычно нет — там он появляется вместе с репозиторием nginx.org. Если версия подходит, внешний клиент не нужен вовсе:

load_module modules/ngx_http_acme_module.so;

http {
    resolver 127.0.0.1:53;                # модуль требует резолвер

    acme_issuer letsencrypt {
        uri                     https://acme-v02.api.letsencrypt.org/directory;
        state_path              /var/lib/nginx/acme-letsencrypt;
        accept_terms_of_service;
    }

    acme_shared_zone zone=acme_shared:1M;

    server {
        listen 443 ssl;
        server_name sonarr.example.com;

        acme_certificate      letsencrypt;
        ssl_certificate       $acme_certificate;
        ssl_certificate_key   $acme_certificate_key;
        ssl_certificate_cache max=2;

        location / {
            proxy_pass http://127.0.0.1:8989;
        }
    }

    server {
        listen 80;                        # обязателен: сюда приходит HTTP-01
        server_name sonarr.example.com;
        return 404;
    }
}

Запросы к /.well-known/acme-challenge/ модуль перехватывает сам, поэтому явного location для них не нужно — но сервер на 80-м порту существовать обязан. Каталог state_path хранит ключ аккаунта и выданные сертификаты; его нужно бэкапить наравне с конфигом и не давать читать кому попало.

Wildcard через DNS-01

Wildcard снимает необходимость трогать конфиг прокси и DNS при добавлении сервиса. У Caddy для этого нужен вкомпилированный DNS-провайдер:

xcaddy build --with github.com/caddy-dns/cloudflare
*.example.com {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }

    @sonarr host sonarr.example.com
    handle @sonarr {
        reverse_proxy 127.0.0.1:8989
    }

    @radarr host radarr.example.com
    handle @radarr {
        reverse_proxy 127.0.0.1:7878
    }
}

У nginx — внешним клиентом, поскольку встроенный модуль DNS-01 не умеет:

certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d 'example.com' -d '*.example.com'

Токен DNS-провайдера — полноценный секрет: он позволяет править зону, а значит выпускать сертификаты на любое имя в ней. Права на файл 0600, скоуп токена — по возможности только на нужную зону и только на запись TXT.

Firewall и автозапуск

Автозапуск — обычный systemd:

systemctl enable --now caddy        # либо nginx
systemctl status caddy

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

В firewall открываются ровно два порта. Порт 80 нужен не для сайта, а для редиректа и для HTTP-01 — закрывать его нельзя. На уровне nftables это одно правило:

nft add rule inet filter input tcp dport { 80, 443 } accept

Если поверх nftables стоит обёртка — ufw или firewalld — правило добавляется через неё, иначе оно потеряется при следующей перезагрузке правил:

ufw allow 80/tcp
ufw allow 443/tcp

Сами сервисы при этом наружу не открываются вовсе — они остаются на 127.0.0.1, и это стоит проверить явно, потому что многие из них по умолчанию слушают 0.0.0.0:

ss -tlnp | grep -E ':(8989|7878|9696|32400)'

Если в выводе 0.0.0.0:8989, а не 127.0.0.1:8989 — сервис доступен в обход прокси, и firewall остаётся единственным, что его прикрывает. Правильное лечение — заставить сервис слушать loopback, а не полагаться на правила.

Заголовки проксирования

После терминации TLS приложение видит соединение от 127.0.0.1 по обычному HTTP. Всё, что оно знает о настоящем клиенте, приходит в заголовках, которые проставил прокси: X-Forwarded-For — цепочка адресов, X-Forwarded-Proto — исходная схема, Host — запрошенное имя.

Отсюда два самых частых симптома. Если приложение генерирует ссылки с http:// вместо https:// или уходит в редирект-петлю — не проставлен X-Forwarded-Proto. Если в логах приложения у всех клиентов один и тот же адрес — либо не проставлен X-Forwarded-For, либо приложение ему не доверяет.

Доверие здесь — не формальность. Заголовок X-Forwarded-For присылает клиент, и подделать его тривиально. Если прокси или приложение принимают его без разбора, любой желающий подменит свой адрес в логах и обойдёт ограничения по IP. Правило простое: доверять X-Forwarded-* можно только от адресов, которые вы сами контролируете.

В nginx доверие настраивается явно и по умолчанию отсутствует:

set_real_ip_from  10.100.0.0/24;      # только адреса своего туннеля
real_ip_header    X-Forwarded-For;
real_ip_recursive on;

Caddy ведёт себя строже из коробки: входящие X-Forwarded-* он по умолчанию игнорирует именно для защиты от подделки. Список доверенных источников задаётся глобально, чтобы применяться ко всем обработчикам сразу:

{
    servers {
        trusted_proxies static 10.100.0.0/24
    }
}

Отдельный случай — когда перед вашим прокси стоит ещё один: CDN или VPS-точка входа. Тогда X-Forwarded-For приходит уже непустым, и настоящий адрес клиента находится не в конце цепочки, а по позиции, которая зависит от числа доверенных прокси. Считать «последний элемент» в такой схеме неверно.

Нюансы и грабли

Редирект-петля — почти всегда следствие того, что приложение настроено само редиректить на HTTPS, но X-Forwarded-Proto до него не доходит. Приложение видит HTTP, отправляет редирект на HTTPS, прокси снова приходит к нему по HTTP, и так до упора. Лечится заголовком, а не отключением редиректа в приложении.

WebSocket, не проходящий через прокси, диагностируется по симптому «страница открылась, данные не обновляются». В nginx нужны proxy_http_version 1.1 и пара Upgrade/Connection через map — без map конструкция ломает обычные запросы, потому что Connection: upgrade будет проставляться всегда. Начиная с 1.29.7 первая половина рецепта отпадает сама: nginx по умолчанию ходит к upstream по HTTP/1.1 и держит keepalive-кеш соединений, так что явный proxy_http_version 1.1 нужен только на старых ветках. Caddy делает это самостоятельно.

Буферизация бьёт по двум сценариям. Стриминг и SSE требуют её отключения в nginx (proxy_buffering off), иначе ответ не доходит до клиента; в Caddy отключать нечего — периодического сброса он не делает по умолчанию вовсе, а ответы с Content-Type: text/event-stream и ответы неизвестной длины сбрасывает клиенту немедленно, игнорируя flush_interval. Директива flush_interval -1 там нужна только для потоков, которые под эти условия не подпадают. Большие загрузки упираются в client_max_body_size, который по умолчанию равен одному мегабайту и отдаёт 413 — на медиасервере это всплывает сразу.

HSTS стоит включать осознанно. Заголовок Strict-Transport-Security говорит браузеру ходить на это имя только по HTTPS, и он кешируется на указанный срок. Если что-то пойдёт не так с сертификатом, откатиться на HTTP уже не получится — браузер не пустит. На поддомене, где вы экспериментируете, это лишний риск; preload без понимания последствий не включать вовсе.

Порт 80 нельзя закрывать при HTTP-01. Соблазн возникает после того, как всё заработало по HTTPS: 80-й вроде бы нужен только для редиректа. Но через него же проходит продление, и поломка обнаружится через два месяца, когда сертификат истечёт.

Мониторинг срока действия — единственная защита от тихого отказа автопродления. Автоматика ломается по причинам, никак не связанным с прокси: сменился API-токен DNS, провайдер закрыл порт, кончилось место на диске. Рассчитывать на письмо-напоминание больше нельзя: Let's Encrypt выключил рассылку уведомлений об истечении ещё 4 июня 2025 года и удалил адреса из базы ACME-аккаунтов. Простейшая проверка снаружи:

echo | openssl s_client -connect sonarr.example.com:443 -servername sonarr.example.com 2>/dev/null \
  | openssl x509 -noout -dates -subject

С переходом на 45-дневные сертификаты запас времени на реакцию сокращается вдвое, поэтому проверку разумно повесить на регулярный запуск и завести алерт.

HTTP/3 включается по-разному. Caddy поднимает его по умолчанию. nginx поддерживает QUIC и HTTP/3 начиная с 1.25.0; в официальных бинарных пакетах модуль собран, при сборке из исходников нужен --with-http_v3_module, а в конфиге — явный listen 443 quic рядом с обычным listen 443 ssl. Документация nginx до сих пор помечает поддержку экспериментальной, а для полноценного QUIC нужен OpenSSL 3.5.1 и новее: с более старой версией собирается слой совместимости, не умеющий early data.

Сравнение с FreeBSD

Принципиальной разницы в самой схеме нет: и nginx, и Caddy есть в портах и пакетах, конфиги идентичны. Отличается обвязка вокруг.

Сервисы включаются через rc.conf, а не systemd (см. rc-систему):

sysrc nginx_enable=YES
service nginx reload

Конфиги лежат в /usr/local/etc/nginx/ и /usr/local/etc/caddy/ — по общей логике размещения стороннего софта в /usr/local, разобранной в статье про файловую структуру.

Firewall — PF вместо nftables, и правила для 80/443 пишутся в /etc/pf.conf (см. сеть FreeBSD):

pass in on $ext_if proto tcp to port { 80, 443 }

Из ACME-клиентов на FreeBSD популярнее acme.sh и dehydrated — оба есть в портах, оба обходятся без Python-обвязки certbot, что на FreeBSD ощутимее, чем на Linux. Продление вешается на cron или periodic, а не на systemd-таймер.

Если прокси и сервисы разнесены по jail'ам, схема получается особенно опрятной: прокси в одном jail со своим адресом, сервисы в других, между ними — внутренняя сеть, наружу торчит только прокси. Подробности изоляции — в статье про jails.

Источники


Связанные заметки

Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.

Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.