Свой сайт на имени .lopux
У вас есть имя в Лопух-Интернете; эта инструкция ставит за ним настоящий сайт, доступный всем, у кого есть приложение. Если имени пока нет — сначала займите его.
Предполагается, что веб-сервер вы поднять умеете, и больше ничего. Всё, что специфично для Лопух-Интернета, собрано здесь: почему сертификат самоподписанный, что должно работать рядом с сайтом и какие две записи заставляют имя работать.
На скриншотах — английский интерфейс. Кабинет говорит на шести языках: выберите свой кнопкой EN в правом верхнем углу.
1. Что нужно и что не подойдёт
Машина, где TLS на порту 443 в ваших руках. VPS, выделенный сервер, коробка дома с доступным адресом. Это всё требование целиком, и оно настоящее.
Управляемые площадки для этого не годятся, по двум независимым причинам. Vercel, Netlify, Cloudflare Pages, большинство PaaS: вы добавляете свой домен, а сертификат получают и отдают они. Весь смысл здесь в том, что сервер доказывает владение ключом, который опубликовали вы; площадка, отдающая собственный ключ, такого доказательства не даёт, и приложение посетителя откажется открывать сайт — и будет право. Да и само имя они не примут: привязка домена проверяется через обычный DNS, где mysite.lopux не существует и существовать не будет.
CDN впереди тоже не помогает — он терминирует TLS, а это та же самая проблема.
Площадки, куда можно загрузить собственный сертификат вместе с ключом, технически сработают. Но тогда эта сторона сможет выдавать себя за ваш сайт перед каждым посетителем, незаметно, и ничто в устройстве системы этого не обнаружит. Относитесь к такому решению так же, как к передаче кому-либо своего закрытого ключа, потому что это оно и есть.
2. Почему сертификат самоподписанный
Ни один удостоверяющий центр не может выпустить сертификат на имя в Лопух-Интернете. Не потому, что это сложно, а потому, что это запрещено: центр вправе выпускать только на имена под обычным корнем DNS, а эти имена намеренно лежат вне его.
Поэтому вместо того, чтобы просить постороннего за вас поручиться, вы публикуете отпечаток собственного ключа в зоне своего имени, и приложение каждого посетителя сверяет сервер, до которого дошло, с этой записью. Запись и есть удостоверение.
В одном отношении это строже удостоверяющего центра. С центром сертификат на ваше имя могли бы выпустить сотни организаций, и любой из них поверили бы. Здесь работает только тот ключ, который опубликовали вы.
3. Сделайте сертификат
Десять лет, без пароля, и все имена, которые вы обслуживаете, перечислены в subjectAltName:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
-keyout site.key -out site.crt -days 3650 -nodes \
-subj "/CN=mysite.lopux" \
-addext "subjectAltName=DNS:mysite.lopux,DNS:www.mysite.lopux"
Три вещи про эту команду:
- сроки действия здесь ни на что не влияют. Приложения намеренно их игнорируют: годен ваш ключ или нет, решает опубликованная запись, а не дата внутри сертификата. Поэтому продлевать не нужно никогда. Десять лет — просто дольше, чем вам будет до этого дело;
-nodesозначает, что у ключа нет пароля, а веб-серверу это и нужно, чтобы стартовать без человека. Держите файл доступным только пользователю сервера (chmod 600) и вне образов, которые вы куда-то отправляете;- не кладите в сертификат ничего, что не готовы опубликовать. Любой, кто завершил рукопожатие с вашим сервером, получает сертификат и читает оттуда имя. Одно имя, без организации и почты.
4. Что работает рядом с сайтом
Ваш сайт говорит по обычному HTTP и сертификата вообще не видит. Перед ним стоит nginx, который терминирует TLS опубликованным ключом и передаёт запрос внутрь.
Этот фронт нужен потому, что у источника в Лопух-Интернете несколько настроек противоположны тому, что даёт обычный «защищённый» шаблон nginx, и каждая ломает всё так, что в браузере выглядит нормально:
| Правило | Чем оборачивается нарушение |
|---|---|
| никакого HSTS — и снимать его, если сайт присылает свой | заголовок намертво привязывает браузер к https:// для этого имени, а это ломает режим обычного http://, с которого посетитель начинает до установки чего бы то ни было |
никаких переадресаций на https:// — и сказать сайту, что запрос уже пришёл по TLS | иначе фреймворк выдаст такую переадресацию сам и сломает тот же режим изнутри |
| отклонять рукопожатия, где SNI не ваше имя | сканер, обходящий адреса, соберёт ваш сертификат, а в сертификате написано ваше имя |
| только 443 | приложения стучатся в 443 и никуда больше; порт 80 не обслуживает никого, кто пришёл через Лопух-Интернет |
| сроки на рукопожатие и чтение | одно соединение, которое открылось и молчит, способно занять сервер. Публичный адрес собирает такие в первые же минуты |
| ключ живёт на фронте, а путь внутрь идёт по обычному HTTP | значит, этот путь не должен покидать машину: не публикуйте наружу порт самого сайта |
Вот этот nginx целиком. Замените имя, пути и адрес внутреннего сайта:
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
server_tokens off;
# не ваше имя: закрытая дверь, до отправки сертификата
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_reject_handshake on;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name mysite.lopux;
ssl_certificate /etc/ssl/site.crt; # ключ, чей отпечаток вы публикуете
ssl_certificate_key /etc/ssl/site.key;
ssl_protocols TLSv1.2 TLSv1.3; # самоподписанный: сшивать не с кем
client_header_timeout 10s; # ими ограничено рукопожатие — см. ниже
client_body_timeout 10s;
send_timeout 10s;
keepalive_timeout 30s;
reset_timedout_connection on;
proxy_hide_header Strict-Transport-Security;
location / {
proxy_pass http://127.0.0.1:8080; # ваш сайт, обычный HTTP, не наружу
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https; # правило 2: без переадресации
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
}
Одна ловушка в этих таймаутах, из-за которой nginx просто не запустится: ssl_handshake_timeout в http-модуле nginx не существует — он относится к stream. В http рукопожатие ограничено значением client_header_timeout, и именно оно здесь выполняет эту работу.
Про запрет по SNI стоит сказать отдельно, потому что он делает кое-что менее очевидное, чем укрытие от сканеров. Без него сервер отдаёт один и тот же сертификат — и, значит, одно и то же предупреждение браузера — двум посетителям в противоположных положениях: тому, чьё приложение сверило ваш ключ с записью до того, как сдвинулся первый байт страницы, и тому, кто набрал ваш IP-адрес, где не сверялось вообще ничего. Запрет убирает второго. Он не превращает предупреждение в проверку: кто знает ваше имя, впишет его себе в hosts и придёт непроверенным ровно как раньше.
Одно правило про сами страницы, и его легко упустить: ставьте относительные ссылки. /about, а не https://mysite.lopux/about. Посетитель в режиме обычного http:// не сможет перейти по абсолютной https://-ссылке на ваш же сайт, а Лопух-Интернет намеренно не переписывает ваши страницы. Ссылки на другие сайты — ваше дело, их это не касается.
5. Опубликуйте отпечаток ключа
Откройте своё имя в кабинете и прокрутите до раздела Сертификаты. Пока ничего не опубликовано, он прямо об этом и говорит: сверять не с чем, поэтому приложение откажется открыть сайт, а не станет подключаться без проверки.
Загрузите site.crt кнопкой Выбрать файл сертификата… или вставьте его текстом. Только сертификат — файл, который начинается с -----BEGIN PRIVATE KEY-----, остаётся на вашем сервере, и кабинет откажется его принять, а не сохранит куда-нибудь.
Нажмите Опубликовать отпечаток ключа. Кабинет разберёт сертификат, возьмёт SHA-256 от его открытого ключа и запишет результат в вашу зону.
Сверьте этот отпечаток со своим — они должны совпасть:
openssl x509 -in site.crt -noout -pubkey \
| openssl pkey -pubin -outform der | openssl dgst -sha256
Панель — это парадный вход к обычной записи. То же значение лежит в списке записей как запись TLSA на _443._tcp, и его там видно:
6. Направьте имя на свой сервер
В том же разделе добавьте запись A, значение которой — публичный адрес вашего сервера. @ означает само имя.
Теперь в зоне обе записи, и вместе они — всё, что нужно приложению посетителя: запись A говорит, где ваш сервер, запись TLSA — какой ключ он обязан предъявить.
Заложите около десяти минут, прежде чем ждать, что изменение видно везде. На пути стоят два кэша — резолвера и приложения — по пять минут каждый.
7. Проверьте
Кабинет забирает ваш сайт так же, как это сделал бы посетитель, и сообщает, что помешает работе: ключ, не совпадающий с опубликованным, отсутствующий отпечаток, заголовок HSTS, переадресация на https://, абсолютные ссылки на себя или сервер, который не ответил.
На этом снимке проверка отказалась выполняться: адрес в примере — документационный, а кабинет не станет опрашивать то, что недоступно из интернета: направив имя на частный адрес или на петлевой, вы превратили бы проверку в зондирование чужой сети. С настоящим сервером на настоящем адресе она отвечает, что с сайтом всё в порядке, либо называет, что именно не так.
Две проверки, которые можно сделать самому, и это как раз самые важные:
# без SNI — отказ
openssl s_client -connect YOUR_ADDRESS:443 </dev/null
# ваш сертификат
openssl s_client -connect YOUR_ADDRESS:443 -servername mysite.lopux </dev/null
Вторая должна отдать сертификат, отпечаток которого вы опубликовали. Возьмите отпечаток его ключа так же, как в §5, и сравните: если строки разойдутся, каждому посетителю будет отказано, а выглядеть для него это будет ровно как попытка подмены.
8. Что видит посетитель
Правильно настроенный сайт открывается под своим именем, как любой другой:
Это lopux.ipvx — собственный сайт проекта. Он живёт в пространстве имён так же, как будет жить ваш, и развёрнут ровно по этой схеме. Ключ, который он отдаёт, совпадает с его опубликованным отпечатком — именно поэтому приложение открывает его без единого предупреждения.
Что получают разные посетители:
| Посетитель | Что он видит |
|---|---|
приложение, имя внутри открытой зоны вроде .lopux | обычный замочек, всё работает |
| приложение, собственное корневое имя | предупреждение браузера о сертификате каждый раз — отпечаток при этом всё равно сверяется |
приложение, в адресной строке http:// | страница выглядит простой; путь до вашего сервера всё равно зашифрован и ключ всё равно сверен |
| без приложения | ничего. Имя не разрешается за пределами Лопух-Интернета |
Последняя строка — это смысл пространства имён, а не его недостаток. Но обратите внимание на строку выше: ваш сервер — обычная публичная машина, и добраться до неё по адресу может кто угодно. Пространство имён его не прячет, а сертификат называет ваш сайт всякому, кто завершил рукопожатие — для этого и нужен запрет по SNI из §4.
9. Как беречь ключ
Ничто здесь не обнаружит кражу ключа; система лишь делает связь между вашим именем и вашим ключом удостоверенной. Тот, у кого есть ваш ключ, аутентифицируется безупречно, пока вы не опубликуете новый отпечаток.
- держите
site.keyдоступным только пользователю веб-сервера и вне каталога, который отдаёте; - сделайте зашифрованную резервную копию: потеря ключа стоит вам смены ключа, а не имени;
- смена ключа дёшева, поэтому меняйте при любом подозрении. Публикуйте новый сертификат до переключения: пока опубликованы два отпечатка, принимается любой из ключей, и момента, когда сайт недоступен, просто не возникает. Потом переключите сервер, подождите десять минут и удалите
старый;
- удаление отпечатка и есть отзыв. Ни у кого не надо спрашивать и нечего ждать: в пределах времени жизни кэша посетителю откажут, а не покажут того, у кого оказался старый ключ.
10. Когда что-то не так
Приложение называет неполадку, а не показывает пустую страницу. Что означает каждая:
| Что сказали посетителю | Что это значит | Чей ход |
|---|---|---|
| Для этого имени не опубликован отпечаток ключа | в зоне нет записи TLSA | ваш — §5 |
| Это не тот сайт, за который он себя выдаёт | ключ вашего сервера не тот, что опубликован | ваш — вы сменили сертификат, не опубликовав новый отпечаток |
| Сайт не ответил | с именем и ключом всё хорошо, сервер не отозвался | ваш — проверьте сервер и запись A |
| Эта страница не может работать по обычному http | абсолютные https://-ссылки на ваш же сайт | ваш — сделайте их относительными (§4) |
| Этому приложению нужно обновление, чтобы открыть этот сайт | приложение посетителя старше вашей зоны | его — пусть обновится; с сайтом всё в порядке |