Настраиваем outline websockets + nginx на своем сервере

У бабушки перестал правильно работать Outline, и я перенастроил ей соединение через новый рекомендуемый способ - websockets + outline.

Сейчас расскажу, как я сделал.

Тут большой пререквизит в том, чтобы поднять полноценный https-сайт. Поэтому сначала займемся созданием сайта.

Начнем с установки nginx: apt install nginx-light

Домен

Раз мы делаем сайт, значит у него должен быть домен. Я завел для этого проекта поддомен line.codepoetry.ru - просто сделал днс-записи вида

;; ANSWER SECTION:
line.codepoetry.ru.	3485	IN	A	65.20.113.21
line.codepoetry.ru.	3600	IN	AAAA	2a01:4a7:c112:834e::1

которые указывают на мой сервер, где я все настраиваю.

Если у вас нет своего домена, то вы можете или заиспользовать домен сервера, который дает ваш хостинг-провайдер (обычно это что-то вроде myserver.hosting.company.com). Или, теоретически, еще можно вообще домен не использовать, а только айпи-адрес, но за такой вариант не ручаюсь, не проверял.

После настройки домена и установки nginx первая контрольная точка. Проверяем, что наш новенький сайт открывается: http://line.codepoetry.ru/

Уже неплохо. Значит, домен работает и nginx тоже работает.

Nginx site config

Давайте сделаем для нашего сайта персональный конфиг:

nano /etc/nginx/conf.d/line.codepoetry.ru.conf
server {
	listen 80;
	listen [::]:80;

	server_name line.codepoetry.ru;

	root /var/www/html;
	index index.html index.nginx-debian.html;

	location / {
		try_files $uri $uri/ =404;
	}
}

И рестарт nginx: nginx -s reload

Тут очень важный параметр - webroot.

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

В конфиге я указал дефолтную папку /var/www/html, поэтому если положить любой файл echo some > /var/www/html/some.txt , то он должен открыться по адресу http://line.codepoetry.ru/some.txt

Проверяем:

Отлично! Ставим галочку и идем дальше.

Certbot

Дальше устанавливаем сертбот по инструкции - https://certbot.eff.org/instructions?ws=other&os=pip

Интересно, что способ установки не apt и даже не snap, а просто нужно создать venv, туда поставить питонячий пакет certbot. Вот примерный слепок команд, хотя полная версия по ссылке выше:

sudo apt update
sudo apt install python3 python3-dev python3-venv libaugeas-dev gcc
sudo python3 -m venv /opt/certbot/
sudo /opt/certbot/bin/pip install --upgrade pip
sudo /opt/certbot/bin/pip install certbot
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
which certbot

Если сертбот запускается, то переходим к выпуску SSL сертификата.

Сертификат

Чтобы наш новый модный сайт line.codepoetry.ru мог работать по https, ему требуется сертификат. Выпустим его с помощью сертбота.

certbot certonly --webroot

Там из основного - домен, контактный емейл и вебрут (тот самый /var/www/html).

Если сертификат выписался, то поздравляю. Осталось только добавить его автообновление в крон в соответствии с шагом 9 из инструкции по ссылке выше. На момент написания этой статьи это была команда

echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew -q" | sudo tee -a /etc/crontab > /dev/null

Back to nginx again

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

nano /etc/nginx/conf.d/line.codepoetry.ru.conf

Добавляем ssl и пути к файлам:

server {
	listen 80;
	listen [::]:80;
	listen 443 ssl;
 	listen [::]:443 ssl;

	server_name line.codepoetry.ru;

	ssl_certificate /etc/letsencrypt/live/line.codepoetry.ru/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/line.codepoetry.ru/privkey.pem;

	root /var/www/html;
	index index.html index.nginx-debian.html;

	location / {
		try_files $uri $uri/ =404;
	}
}

И еще раз релоад:

nginx -s reload

Проверяем: https://line.codepoetry.ru/some.txt

Иконочка залилась надежным серым замочком. А значит, работает! Если вы дошли до этого шага, поздравляю! Значительная часть конфигурации сделана.

Если что-то в nginx не получается, то ищите причину ошибки в логе: /var/log/nginx/error.log

Дальше можно переключиться на поднятие Outline сервера.

Outline server

Ребята непонятно написали про это у себя в анонсе, но чтобы пользоваться этим методом, не получится использовать их обычный докер и outline manager. Придется самому запустить сервер.

Я сделал так. Пошел на гитхаб в разделе Releases нашел последнюю линуксовую версию, скачал и запустил бинарь

mkdir outline-ss-server
cd outline-ss-server
wget https://github.com/Jigsaw-Code/outline-ss-server/releases/download/v1.9.2/outline-ss-server_1.9.2_linux_x86_64.tar.gz
tar -xf outline-ss-server_1.9.2_linux_x86_64.tar.gz
./outline-ss-server --help

Если работает, то создадим конфиг командой nano config.yaml вот такого содержимого:

web:
  servers:
    - id: server1
      listen:
        - "127.0.0.1:17543"

services:
  - listeners:
      - type: websocket-stream
        web_server: server1
        path: "/qk1vrmm1zj9y1qd4xn/tcp"
      - type: websocket-packet
        web_server: server1
        path: "/qk1vrmm1zj9y1qd4xn/udp"
    keys:
      - id: '1'
        cipher: chacha20-ietf-poly1305
        secret: just-generate-any-random-string-of-length-20

Тут из важного - нужно сгенерировать любой путь рандомной строкой и никому его не показывать. А также сгенерировать секрет ключа.

Дальше запускаем сервер командой ./outline-ss-server --config config.yaml --replay_history 10000

А чтобы он работал так вечно и не погас, можно было бы запустить его через systemd, но я выбрал вариант проще - я просто запустил его внутри tmux

Снова к Nginx

Возвращаемся к nginx и дописываем конфиг, чтобы прокинуть два пути в ss server. Надо добавить апстрим и два локейшна:

upstream outline {
    server localhost:17543;
}

server {
	listen 80;
	listen [::]:80;
	listen 443 ssl;
 	listen [::]:443 ssl;

	server_name line.codepoetry.ru;

	ssl_certificate /etc/letsencrypt/live/line.codepoetry.ru/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/line.codepoetry.ru/privkey.pem;

	root /var/www/html;
	index index.html index.nginx-debian.html;

	location / {
		try_files $uri $uri/ =404;
	}

	location /qk1vrmm1zj9y1qd4xn/tcp {
		proxy_pass http://outline;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
	}

	location /qk1vrmm1zj9y1qd4xn/udp {
		proxy_pass http://outline;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
	}
}

Не забудьте сделать релоад: systemctl reload nginx

Проверить, что работает можно такой командой: curl https://line.codepoetry.ru/path/tcp - если выдает Bad Request, значит соединение есть.

Осталось только лишь настроить клиент.

Outline Client Configuration

Но и тут нас ждет работа руками. Если вы раньше работали только с ключами вида ss://xxxx, то вас может удивить, что аутлайн клиент поддерживает и более сложный конфиг - в формате yaml. И он нам понадобится.

Создадим такой конфиг для подключения к нашему серверу:

transport:
  $type: tcpudp

  tcp:
    $type: shadowsocks

    endpoint:
      $type: websocket
      url: wss://line.codepoetry.ru/qk1vrmm1zj9y1qd4xn/tcp
    cipher: chacha20-ietf-poly1305
    secret: just-generate-any-random-string-of-length-20

  udp:
    $type: shadowsocks

    endpoint:
      $type: websocket
      url: wss://line.codepoetry.ru/qk1vrmm1zj9y1qd4xn/udp
    cipher: chacha20-ietf-poly1305
    secret: just-generate-any-random-string-of-length-20

И осталось дело за малым - этот конфиг нужно дать клиенту. Для меня удобный способ - это просто положить его в файл на том же самом сервере, сгенерировав рандомное имя файла. Для примера я использую имя example.txt:

nano /var/www/html/example.txt

Проверим, что файл открывается: https://line.codepoetry.ru/example.txt

И если да, то просто меняем https на ssconf и вот он наш ключ:

ssconf://line.codepoetry.ru/example.txt

Именно этот "ключ" можно добавлять на бабушкином компьютере и проверять соединение.

Работает!