История о том, как сисадмин борется с конторой с бюджетом в 60 млрд.
В продолжение комментария.
Введение
Есть грандиозный проект, и для его существования нужны 3 вещи:
— сервера
— люди
— vpn
Компания зарубежная, и мы с удовольствием используем AWS.
Требования регуляторов привели нас к OpenVPN.
А компания любит дешёвую рабочую силу, поэтому часть сотрудников живёт в РФ.
Получилась простая схема, на которой хотелось бы жить вечно.
client -> openvpn://bastion.example.com:1194Казалось бы, что может пойти не так? Именно тут появляется наш великолепный цензор в виде РКН и ставит палки в колёса.
Глава 1. Первые блокировки, obfs4proxy
В какой-то момент, клиенты в РФ начали отваливаться. Анализ быстро привёл нас к пониманию, что происходит обрыв tcp соединения на этапе подключения. Но только для OpenVPN, остальной траффик ходил как положено.
Значит нам нужно замаскировать траффик, и сделать это:
— максимально удобно для сотрудников, далеко не все кулхацкеры
— безопасно для инфры, лишных дырок появиться не должно
Так у нас появился obfs4proxy c связке с Viscosity
client -> obfs://127.0.0.1:1080 -> obfs://bastion.example.com:1195 -> openvpn://bastion.example.com:1194Конечно же никто не мог тогда предвидеть, на какую интересную дорожку мы встали.
Глава 2. Вынос части инфры из AWS.
Недолго мы испытывали счастье, и в какой-то момент траффик до AWS начал ходить с перебоями. На благоприятную рабочую обстановку это похоже не было, поэтому обфускацию решили вынести.
client -> obfs://127.0.0.1:1080 -> obfs://tunnel.example.com:1195 -> openvpn://bastion.example.com:1194Логика была банальна:
— поставщики виртуалок не обязаны ставить ТСПУ
— у хороших вендоров есть свой канал
— серваки светятся как отечественные в россии, но имеют зарубежный IP
Такое должно прожить долго и лавочку не прикроют, верно ведь?
Глава 3. Попытка попасть в белые списки.
Играть в кошки-мышки с государством интересно, но хотелось иногда просто поработать.
Поэтому следующим этапом была попытка попасть в белые списки. Мы легальные, аудионаркотики не продаём, неужели не мы можем работать как белые люди?
Спустя несколько месяцев ответ был получен, но результата это не дало.
Глава 4. Появление cloak/trojan/hysteria/vless
В какой-то момент часть сотрудников начала жаловаться на недоступность. Снова.
Ещё одна пачка анализов и приходим к пониманию, что траффик режется. Симпомы идентичны обычному использованию OpenVPN, всё намекает на то, что протокол не столь надёжен, как хотелось бы.
Начинаем искать альтернативы. Так в течении пологода у нас заводятся shadowsocks/amnezia/ipsec/cloak/trojan/hysteria/vless/vkturnproxy.
client -> ck-client://127.0.0.1:1080 -> ck-server://tunnel.example.com:443 -> openvpn://bastion.example.com:1194Решения которые прекрасно работали для меня одного, начинают отваливаться в разных регионах страны, на разных провайдерах, в разных случаях. И в этом чёрном ящике абсолютно не понятно:
— то ли накатывают белые списки
— то ли РКН сломал часть интернета
— то ли их DPI научился детектить протокол
— то ли мы провинились
— то ли кривые руки сотрудников
Всё приходит к тому, что у нас поднята пачка методов обхода блокировок (уже 10 в документации), а на сотрудниках я тестирую.
Глава 5. Белые списки, поднятие серверов в РФ
Научились обходить блокировки? Вот вам задачка со звёздочкой.
Видимо так решило моё любимое министерство цифрового отрицательного развития.
Появилась нужда резко найти IP из белых списков и использовать их. Решение пришло также быстро — обратный король мидас отечественного IT. cloud.vk.com позвляет быстро поднять VPS, а на их серваках внезапно доступны instagram/youtube/etc.
client -> ck-client://127.0.0.1:1080 -> ck-server://subway.example.com:443 -> openvpn://bastion.example.com:1194Появилось ложное ощущение, что вот он конец, решение всех проблем, один сервер чтобы обходить их все. Сотрудники получили отличную скорость, я получил спокойствие.
Глава 6. Отрыв IP, резервирование
В какой-то момент работа остановилась, и пришлось вспоминать основы high availability — наличие запасного сервера.
Сложно сказать, что послужило причиной блокровки:
— кто-то из сотрудников сидел в max с рабочего VPN’a
— DPI действительно научился палить vless
— мои кривые руки в конфигурации сервера
— активные пробы прошлись по всем портам и нашли лишнее
Причины были уже не важны, главное стало понимание, что нужно что-то делать. И самым простым оказалось поднять ещё чуть-чуть виртуалок.
client -> ck-client://127.0.0.1:1080 -> ck-server://{subway|underground}.example.com:443 -> openvpn://bastion.example.com:1194Я в своем познании настолько преисполнился, что я как будто бы уже сто триллионов миллиардов лет обхожу блокировки.
Глава 7. Multi-hop
Появление вестей о шпионском ПО, которое будет сливать данные IP выходного сервера дало ясно понять, что входной и выходной адреса должны различаться. Так в арсенале появился gost.
client -> ck-client://127.0.0.1:1080 -> gost://subway.example.com:443 -> ck-server://tunnel.example.com:443 -> openvpn://bastion.example.com:1194Latency слегка подрос, зато наши сотрудники живут в стабильных условиях (надолго ли?).
Конфиги
Для настройки всего этого счастья в любом случае придётся прильнуть к официанльной документации, но небольшую шапарглку оставлю.
Скрытый текст
gost.yaml
services: - name: cloak-tcp addr: :443 handler: type: tcp listener: type: tcp forwarder: nodes: - name: cloak-tcp addr: {{ gost_target }}:443
obfs4proxy.conf
TOR_PT_MANAGED_TRANSPORT_VER=1TOR_PT_STATE_LOCATION=/var/lib/obfs4TOR_PT_SERVER_TRANSPORTS=obfs4TOR_PT_SERVER_BINDADDR=obfs4-0.0.0.0:{{ obfs4_port }}TOR_PT_ORPORT={{ obfs4_target }}
ck-server.json
{ "ProxyBook": { "openvpn": [ "tcp", "{{ cloak_remote }}" ] }, "BindAddr": [ ":443" ], "BypassUID": [ {{ cloak_uid }} ], "RedirAddr": "www.example.com", "PrivateKey": "{{ cloak_private }}"}
ck-client.json
{ "RemoteHost": "subway.example.com", "Transport": "direct", "ProxyMethod": "openvpn", "EncryptionMethod": "plain", "UID": "{{ cloak_uid }}", "PublicKey": "{{ cloak_public }}", "ServerName": "www.example.com", "NumConn": 4, "BrowserSig": "chrome", "StreamTimeout": 300}
xray-server.json
{ "log": { "loglevel": "info" }, "routing": { "rules": [], "domainStrategy": "AsIs" }, "inbounds": [ { "listen": "0.0.0.0", "port": {{ xray_port }}, "protocol": "vless", "tag": "vless_tls", "settings": { "clients": [ { "id": "{{ xray_client }}", "email": "{{ xray_email }}", "flow": "xtls-rprx-vision" } ], "decryption": "none" }, "streamSettings": { "network": "tcp", "security": "reality", "realitySettings": { "show": false, "dest": "www.example.com:443", "xver": 0, "serverNames": [ "www.example.com", "example.com" ], "mldsa65Seed": "", "privateKey": "{{ xray_private }}", "minClientVer": "", "maxClientVer": "", "maxTimeDiff": 0, "shortIds": [ {{ xray_short }} ] } }, "sniffing": { "enabled": true, "destOverride": [ "http", "tls" ] } } ], "outbounds": [ { "protocol": "freedom", "tag": "direct" }, { "protocol": "blackhole", "tag": "block" } ]}
xray-client.json
{ "log": { "level": "info" }, "inbounds": [ { "listen": "127.0.0.1", "port": 1080, "protocol": "socks", "settings": { "udp": true }, "sniffing": { "enabled": true, "destOverride": [ "http", "tls" ] } } ], "outbounds": [ { "domain_strategy": "ipv4_only", "flow": "xtls-rprx-vision", "packet_encoding": "xudp", "server": "subway.example.com", "server_port": 8443, "tag": "proxy", "tls": { "alpn": [ "h2" ], "enabled": true, "insecure": true, "reality": { "enabled": true, "public_key": "{{ xray_public }}", "short_id": "{{ xray_short }}" }, "server_name": "www.example.com", "utls": { "enabled": true, "fingerprint": "chrome" } }, "type": "vless", "uuid": "{{ xray_uuid }}" }, { "tag": "direct", "protocol": "freedom" }, { "tag": "block", "protocol": "blackhole" } ]}
ss-server.json
{ "server": "0.0.0.0", "server_port": {{ ss_port }}, "password": "{{ ss_pass }}", "method": "aes-256-gcm", "mode":"tcp_and_udp", "fast_open":false}
ss-client.json
{ "server": "tunnel.example.com", "server_port": {{ ss_port }}, "password": "{{ ss_pass }}", "method": "aes-256-gcm", "local_address": "127.0.0.1", "local_port": 1080}
hysteria-server.yaml
listen: :{{ hysteria_port }}tls: cert: /etc/ssl/wildcard.crt key: /etc/ssl/wildcard.keyauth: type: userpass userpass: {{ hysteria_client }}: {{ hysteria_pass }}masquerade: type: proxy proxy: url: https://www.example.com rewriteHost: trueobfs: type: salamander salamander: password: {{ hysteria_obfs }}resolver: type: udp tcp: addr: 8.8.8.8:53 timeout: 4s udp: addr: 8.8.4.4:53 timeout: 4s tls: addr: 1.1.1.1:853 timeout: 10s sni: cloudflare-dns.com insecure: false https: addr: 1.1.1.1:443 timeout: 10s sni: cloudflare-dns.com insecure: false
hysteria-client.yaml
server: tunnel.example.com:1984auth: {{ hysteria_client }}: {{ hysteria_pass }}obfs: type: salamander salamander:password: {{ hysteria_obfs }}http: listen: 127.0.0.1:8080socks5: listen: 127.0.0.1:1080
trojan-server.json
{ "run_type": "server", "local_addr": "0.0.0.0", "local_port": {{ trojan_port }}, "remote_addr": "{{ trojan_remote }}", "remote_port": 443, "password": ["{{ trojan_password }}"], "ssl": { "cert": "/etc/ssl/wildcard.crt", "key": "/etc/ssl/wildcard.key", "cipher": "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384", "cipher_tls13": "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384", "prefer_server_cipher": true, "alpn": [ "http/1.1" ], "alpn_port_override": { "h2": 81 }, "reuse_session": true, "session_ticket": false, "session_timeout": 600, "plain_http_response": "", "curves": "", "dhparam": "" }, "tcp": { "prefer_ipv4": false, "no_delay": true, "keep_alive": true, "reuse_port": false, "fast_open": false, "fast_open_qlen": 20 }, "mysql": { "enabled": false, "server_addr": "127.0.0.1", "server_port": 3306, "database": "trojan", "username": "trojan", "password": "", "key": "", "cert": "", "ca": "" }}
trojan-client.json
{ "run_type": "client", "local_addr": "127.0.0.1", "local_port": 1080, "remote_addr": "{{ trojan_remote }}", "remote_port": {{ trojan_port }}, "password": [ "{{ trojan_password }}" ], "log_level": 1, "ssl": { "verify": true, "verify_hostname": true, "cert": "", "cipher": "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA", "cipher_tls13": "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384", "sni": "www.example.com", "alpn": [ "h2", "http/1.1" ], "reuse_session": true, "session_ticket": false, "curves": "" }, "tcp": { "no_delay": true, "keep_alive": true, "reuse_port": false, "fast_open": false, "fast_open_qlen": 20 }}
Вывод
Я рассказал последний год своего весёлого приключения. В этом простом рассказе я хотел бы акцентировать внимание, что вопрос выбора технологии не менее важен, чем вопрос архитектуры, выбора поставщика, и обучения персонала. Сложно гарантировать аптайм, когда ошибка на любом из этапов может внести тебя в бан.
Особое внимание:
— ТСПУ начали поставлять на сети межрегиональных провайдеров, желательно настраивать multihop, чтобы траффик из РФ выходил и шифрованным, и обфусцированным client -> vps rf -> vps eu -> vpn
— желательно не использовать обход блокировок на мобилках одновременно с небезопасными приложениями (макс/яндекс/банки), активно сливают, можно поймать бан на следующий день
— не забываем настраивать split-tunnel, чтобы траффик для отечественных сервисов шёл внутри страны
— РКН чувствителен к портам, для белых списков только 80/443, для обхода блокировок не использовать 1337/1984/3128/8080
— РКН быстро добавляет IP в блэклист сервера за рубежом, и рубит машину в РФ, если спалит
На данный момент самым живым является схема, когда:
— есть защита от активных проб
— есть валидные tls сертификаты и настоящий веб-сервер, который отвечает
— траффик шифруется лишь 1 раз
— траффик обфусцируется и очень похож на https
— входной и выходной адреса отличаются
— есть резервирование на несколько машин внутри страны

ссылка на оригинал статьи https://habr.com/ru/articles/1027990/