Неработоспособность шифрованных протоколов (ShadowSocks/VMESS) (25.04.2024 +)

Where was your test network?

Remote Testing with the Outline SDK

You can use the Outline SDK Tools to test different strategies remotely.

Here I test a fetch from Bee Line:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE"  https://ipinfo.io/org
AS16345 PJSC "Vimpelcom"

$SERVER_BEELINE is a socks5://... url pointing to a SOCKS5 server running on Bee Line.

Here is a blocked website:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|override:host=cloudflare.net" -method HEAD -v https://meduza.io
2024/04/29 15:49:50 HTTP request failed: Head "https://meduza.io": EOF

(I’m using override to bypass DNS blocking)

TLS Record fragmentation bypasses blocking:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|override:host=cloudflare.net|tlsfrag:1" -method HEAD -v https://meduza.io
[DEBUG] 2024/04/29 15:50:35.534519 main.go:106: Expires: [Mon, 29 Apr 2024 19:51:05 GMT]
[DEBUG] 2024/04/29 15:50:35.535194 main.go:106: Alt-Svc: [h3=":443"; ma=86400]
[DEBUG] 2024/04/29 15:50:35.535205 main.go:106: Cache-Control: [public, max-age=30]
[DEBUG] 2024/04/29 15:50:35.535211 main.go:106: Last-Modified: [Mon, 29 Apr 2024 19:25:15 GMT]
[DEBUG] 2024/04/29 15:50:35.535215 main.go:106: Cf-Cache-Status: [HIT]
[DEBUG] 2024/04/29 15:50:35.535220 main.go:106: X-Content-Type-Options: [nosniff]
[DEBUG] 2024/04/29 15:50:35.535225 main.go:106: Date: [Mon, 29 Apr 2024 19:50:35 GMT]
[DEBUG] 2024/04/29 15:50:35.535229 main.go:106: X-Cg-Cache-Status: [HIT]
[DEBUG] 2024/04/29 15:50:35.535233 main.go:106: Age: [168]
[DEBUG] 2024/04/29 15:50:35.535237 main.go:106: Strict-Transport-Security: [max-age=31536000; includeSubDomains; preload]
[DEBUG] 2024/04/29 15:50:35.535243 main.go:106: Server: [cloudflare]
[DEBUG] 2024/04/29 15:50:35.535247 main.go:106: Cf-Ray: [87c1d2664d33614a-KJA]
[DEBUG] 2024/04/29 15:50:35.535250 main.go:106: Content-Type: [text/html; charset=utf-8]
[DEBUG] 2024/04/29 15:50:35.535311 main.go:106: Connection: [keep-alive]

Testing Shadowsocks

To fetch with Shadowsocks:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$OUTLINE_SERVER" https://ipinfo.io/org
AS14061 DigitalOcean, LLC

Where $OUTLINE_SERVER is a ss:// Shadowsocks key.

Now we can do a two-hop test by piping the transports. Here we connect to Bee Line, then fetch ipinfo.io via the Outline Server. We seem to get timeouts for HTTP:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER" http://ipinfo.io/org
2024/04/29 15:58:02 HTTP request failed: Get "http://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1

And HTTPS:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER" https://ipinfo.io/org
2024/04/29 16:00:07 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1

When I use a prefix that simulates a TLS Handshake Record the HTTP fetch succeeds:

$ go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER?prefix=%16%03%01" http://ipinfo.io/org
AS14061 DigitalOcean, LLC

$ go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER?prefix=%16%03%01" https://ipinfo.io/org
AS14061 DigitalOcean, LLC

Note that my Outline Server is running on port 443.

I was able to get 10 successes in a row:

for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER?prefix=%16%03%01" https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: AS14061 DigitalOcean, LLC

Test 3: AS14061 DigitalOcean, LLC

Test 4: AS14061 DigitalOcean, LLC

Test 5: AS14061 DigitalOcean, LLC

Test 6: AS14061 DigitalOcean, LLC

Test 7: AS14061 DigitalOcean, LLC

Test 8: AS14061 DigitalOcean, LLC

Test 9: AS14061 DigitalOcean, LLC

Test 10: AS14061 DigitalOcean, LLC

But it’s not always successful. I don’t know if that’s an issue with my Bee Line being slow or if it’s inconsistent blocking:

$ for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER?prefix=%16%03%01" https://ipinfo.io/org; done
Test 1: 2024/04/29 16:10:14 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 2: 2024/04/29 16:10:20 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 3: 2024/04/29 16:10:26 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 4: AS14061 DigitalOcean, LLC

Test 5: AS14061 DigitalOcean, LLC

Test 6: AS14061 DigitalOcean, LLC

Test 7: AS14061 DigitalOcean, LLC

Test 8: 2024/04/29 16:10:41 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 9: 2024/04/29 16:10:47 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 10: AS14061 DigitalOcean, LLC

I’ll see if I can test in more networks. but it would be great if others could test too.

Did some testing on Megafon:

$ go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON"  https://ipinfo.io/org
AS31224 PJSC MegaFon

It’s not completely clear to me if the timeouts are blocking, or just slowness. I’ll need to change the code to configure that.

HTTPS, no prefix, port 443

for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER" https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: 2024/04/29 16:16:13 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 3: AS14061 DigitalOcean, LLC

Test 4: AS14061 DigitalOcean, LLC

Test 5: 2024/04/29 16:16:23 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 6: AS14061 DigitalOcean, LLC

Test 7: 2024/04/29 16:16:34 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 8: AS14061 DigitalOcean, LLC

Test 9: 2024/04/29 16:16:42 HTTP request failed: Get "https://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 10: AS14061 DigitalOcean, LLC

HTTP, no prefix, port 443

for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER" http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: AS14061 DigitalOcean, LLC

Test 3: AS14061 DigitalOcean, LLC

Test 4: 2024/04/29 16:17:04 HTTP request failed: Get "http://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 5: AS14061 DigitalOcean, LLC

Test 6: AS14061 DigitalOcean, LLC

Test 7: AS14061 DigitalOcean, LLC

Test 8: AS14061 DigitalOcean, LLC

Test 9: AS14061 DigitalOcean, LLC

Test 10: AS14061 DigitalOcean, LLC

HTTPS, TLS prefix, port 443

for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER?prefix=%16%03%01" https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: AS14061 DigitalOcean, LLC

Test 3: AS14061 DigitalOcean, LLC

Test 4: AS14061 DigitalOcean, LLC

Test 5: AS14061 DigitalOcean, LLC

Test 6: AS14061 DigitalOcean, LLC

Test 7: AS14061 DigitalOcean, LLC

Test 8: AS14061 DigitalOcean, LLC

Test 9: AS14061 DigitalOcean, LLC

Test 10: AS14061 DigitalOcean, LLC

HTTP, TLS Prefix, Port 443

$ for i in $(seq 1 10); do echo -n "Test $i: "; go run github.com/Jigsaw-Code/outline-sdk/x/examples/fetch@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER?prefix=%16%03%01" http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC

Test 2: AS14061 DigitalOcean, LLC

Test 3: AS14061 DigitalOcean, LLC

Test 4: 2024/04/29 16:20:20 HTTP request failed: Get "http://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 5: AS14061 DigitalOcean, LLC

Test 6: 2024/04/29 16:20:32 HTTP request failed: Get "http://ipinfo.io/org": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
Test 7: AS14061 DigitalOcean, LLC

Test 8: AS14061 DigitalOcean, LLC

Test 9: AS14061 DigitalOcean, LLC

Test 10: AS14061 DigitalOcean, LLC

While the fetch tool doesn’t have a timeout option, you can use our http2transport proxy + curl. I’m now getting more consistent results. I’m not seeing consistent blocking in either Bee Line or Megafon, so I think the timeouts were issues with my remote proxy. Again, it would be great to see direct measurements.

Run the local proxy:

go run github.com/Jigsaw-Code/outline-sdk/x/examples/http2transport@latest -transport "$SERVER_BEELINE|$OUTLINE_SERVER"
2024/04/29 16:42:08 Proxy listening on 127.0.0.1:1080

Then use curl.

Bee Line

HTTP

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: curl: (52) Empty reply from server
Test 8: curl: (52) Empty reply from server
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

HTTPS

for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: AS14061 DigitalOcean, LLC
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

Megafon

$ go run github.com/Jigsaw-Code/outline-sdk/x/examples/http2transport@latest -transport "socks5://$SERVER_MEGAFON|$OUTLINE_SERVER"
2024/04/29 16:47:08 Proxy listening on 127.0.0.1:1080

HTTP

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: AS14061 DigitalOcean, LLC
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

HTTPS

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: AS14061 DigitalOcean, LLC
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

MTS

go run github.com/Jigsaw-Code/outline-sdk/x/examples/http2transport@latest -transport "socks5://$SERVER_MTS|$OUTLINE_SERVER"
2024/04/29 16:59:14 Proxy listening on 127.0.0.1:1080

HTTP

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS http://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: AS14061 DigitalOcean, LLC
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: AS14061 DigitalOcean, LLC
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

HTTPS

$ for i in $(seq 1 10); do echo -n "Test $i: "; curl -p -x http://localhost:1080 -sS https://ipinfo.io/org; done
Test 1: AS14061 DigitalOcean, LLC
Test 2: AS14061 DigitalOcean, LLC
Test 3: AS14061 DigitalOcean, LLC
Test 4: AS14061 DigitalOcean, LLC
Test 5: curl: (56) Failure when receiving data from the peer
Test 6: AS14061 DigitalOcean, LLC
Test 7: AS14061 DigitalOcean, LLC
Test 8: curl: (56) Recv failure: Connection reset by peer
Test 9: AS14061 DigitalOcean, LLC
Test 10: AS14061 DigitalOcean, LLC

Note: Shadowsocks doesn’t communicate TCP resets, so those resets are from the proxy in Russia, not the target website.

Для многочисленных тестов с подключением к одному dest_addr:dest_port возможен выбор src_addr:src_port из уже блокируемой 4-tuple (src_addr, src_port, dest_addr, dest_port). Новое подключение не отменяет блокировку.

It’s Tele2 cellular, Saint Petersburg.
I’ve also tested Beeline, MTS, Megafon and Yota @ Saint Petersburg, all are blocked.

You need to absolutely make sure that your ClientHello is large enough, as curl with mbedtls for example does not add padding extension and it does not hit 411 bytes length needed to trigger the block.

shadowsocks-notblock-yota-28.04.2024.pcapng (105,4 КБ)

Прикрепляю дамп с удачным подключением к серверу Hetzner из Уфы (Yota), подключение к Outline 162.55.42.178:24475 без префикса.

Блокировки прекратились с 30-го апреля на территории Башкортостана и Татарстана по всем провайдерам. Есть клиенты, которые жалуются на подключение с Билайна из Пензы, при этом чистый SS-блочится, а Outline с префиксом ClientHello не блокируется.

This is Outline traffic from opt-in metrics. You can see a gradual decline, supporting a partial block, and then it starts to go back up, aligning with @murka’s statement that blocking stopped on April 30.

Oh, I missed that the 411 bytes was required. With the Outline SDK, we can add a split to the Shadowsocks stream. The Outline Client doesn’t support that yet, but we want to move towards that. Perhaps the client can automatically try different splits if the connection fails.

На 2024-05-05T17:54:00Z блокировки полностью шифрованных протоколов сохраняются на мобильных Теле2, Мегафон, МТС, Билайн и Йота Санкт-Петербург.

Проверял на Shadowsocks, VMESS и собственном генераторе пакетов. Фильтруются все зарубежные направления (Россию не проверял).

Для эксперимента, развернул на московском vps shadowsocks. С мобильного Билайна не работает.

TCP. А UDP проверяли какие-нибудь для интереса?

How did you test Shadowsocks?

I tested Outline Shadowsocks on Tele2, Megafon, MTS and Beeline in St Petersburg, and was not able to observe blocking.

I tested using soax.com, an Outline Server, and the Outline SDK fetch tool. I tried both fetching a HTTPS page and a HTTP page. The HTTPS fetch doesn’t trigger the 411 bytes in the first packet, but the HTTP fetch was done with padding headers to force over 800 bytes in the first packet, and I still couldn’t trigger the block.

5 posts were merged into an existing topic: Обсуждение: неработоспособность ShadowSocks (30.10.2023 +)

A post was merged into an existing topic: Обсуждение: неработоспособность ShadowSocks (30.10.2023 +)

I’m using proxies on a probe (you have access to it) with v2fly configured with shadowsocks over probe’s proxy as an upstream, and sending the requests with regular curl (built with openssl) to HTTPS websites.

HTTP websites indeed do not trigger the block because the filter expects at least two client-server packets with sufficient amount of data (first is ClientHello, second is HTTP request), and HTTP has only one.

Check the PCAP in the top post.

Проверил QUIC (HTTPS-запросами) через Shadowsocks, на этих же каналах (Теле2, Билайн, МТС, Мегафон Санкт-Петербург) блокировки не вижу.

А фильтр проверяет только первые несколько пакетов (как при wireguard блокировках было) или все время за потоком данных следит?

4 posts were merged into an existing topic: Обсуждение: неработоспособность ShadowSocks (30.10.2023 +)

Вроде только начало сессии. Первый (+ возможно, второй) пакет от клиента и как минимум 2 ответных пакета.

I think that pcap was for your random packet generator, given the two packets with exactly 411 bytes.
It would be helpful to see the packets with the Shadowsocks set up.

Whether it triggers is heavily dependent on the implementation, and I have no idea what your set up is doing.

Some implementations will send IV, connect request and application data as 3 separate packets. Some will merge the first two, and some, such as Outline, will merge the 3 of them.

I don’t think the IV+connect reaches 411 bytes, so it shouldn’t trigger the block. Is the pattern detected after the first packet?

It would be really nice to reproduce the blocking with Outline code. I wasn’t able to.