Skip to content

Commit 55ccbf1

Browse files
myleshortonclaude
andcommitted
config: add Aliyun (Alibaba Cloud) CDN domain-fronting provider
Alibaba Cloud / Aliyun CDN edges route by the HTTP Host header (h1) / :authority (h2) and ignore the TLS SNI for origin selection, so a censor filtering on SNI sees only an innocent front domain (img.alicdn.com) while the request reaches the real target. Verified 2026-06-23 cross-organization: a TLS session bearing Alibaba's *.tbcdn.cn cert (SNI img.alicdn.com) reached Bilibili (s1.hdslb.com) and Momo (img.momocdn.com) origins purely by Host. This adds aliyun-provider.yaml: a complete, parseable Config with the GlobalSign Root CA - R3 (anchors the Alibaba leaf chain), a verified seed pool of edge IPs across three /24s, and frontingsnis driving img/gw/a .alicdn.com SNIs. hostaliases + testurl are placeholders until Lantern origins are onboarded as Aliyun CDN distributions (the edge silently drops non-customer Host values). aliyun_live_test.go (guarded by DOMAINFRONT_LIVE=1) drives the real roundTripper: every edge completes TLS + GlobalSign verification under the production Chrome_131 hello and fronts a cross-org Host over HTTP/2 (200). Depends on #9 (HTTP/2 fronting) — Aliyun negotiates h2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0580002 commit 55ccbf1

2 files changed

Lines changed: 195 additions & 0 deletions

File tree

aliyun-provider.yaml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Alibaba Cloud (Aliyun) CDN domain-fronting provider.
2+
#
3+
# Built & verified 2026-06-23 (US vantage point). Aliyun CDN edges route by the
4+
# HTTP Host header and ignore the TLS SNI for origin selection, so a censor doing
5+
# SNI filtering sees only the innocent front domain (img.alicdn.com) while the
6+
# request reaches the real target. Confirmed cross-organization: a TLS session
7+
# bearing Alibaba's *.tbcdn.cn cert (SNI img.alicdn.com) reached Bilibili
8+
# (s1.hdslb.com) and Momo (img.momocdn.com) origins purely via the Host header.
9+
#
10+
# Two operational constraints discovered:
11+
# 1. TLS layer — the edge node must hold a cert valid for the SNI you present.
12+
# img.alicdn.com is ideal: Alibaba's own *.tbcdn.cn cert (SAN *.alicdn.com,
13+
# *.taobao.com, ...) is provisioned on ~every Aliyun node.
14+
# 2. The target Host must be an onboarded Aliyun CDN customer. A non-customer
15+
# Host (e.g. www.example.com) makes the edge silently drop the connection.
16+
# => Lantern must onboard its own distribution to Aliyun CDN; the
17+
# hostaliases + testurl below are PLACEHOLDERS until that exists.
18+
#
19+
# This file is a complete, standalone Config (parseable via ParseConfigYAML) so
20+
# it can be loaded directly for testing. The `aliyun:` block under `providers:`
21+
# is what gets merged into the upstream getlantern/fronted pipeline output.
22+
23+
trustedcas:
24+
# Anchor for the Alibaba leaf cert chain:
25+
# *.tbcdn.cn -> GlobalSign GCC R3 OV TLS CA 2024 -> GlobalSign Root CA - R3
26+
- commonname: GlobalSign Root CA - R3
27+
cert: |
28+
-----BEGIN CERTIFICATE-----
29+
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
30+
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
31+
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
32+
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
33+
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
34+
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
35+
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
36+
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
37+
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
38+
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
39+
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
40+
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
41+
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
42+
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
43+
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
44+
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
45+
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
46+
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
47+
WD9f
48+
-----END CERTIFICATE-----
49+
50+
providers:
51+
aliyun:
52+
# ----------------------------------------------------------------------
53+
# PLACEHOLDER — replace with real Lantern origins mapped to the Aliyun CDN
54+
# distribution domains (the CNAME targets, typically *.w.kunlunaq.com or a
55+
# custom CNAME) you create when onboarding each origin to Aliyun CDN.
56+
# ----------------------------------------------------------------------
57+
hostaliases:
58+
config.getiantem.org: REPLACE-ME.w.kunlunaq.com
59+
api.getiantem.org: REPLACE-ME.w.kunlunaq.com
60+
# testurl must resolve to an Aliyun-onboarded host that returns HTTP 202
61+
# on POST (see verifyWithPost). Point this at your distribution's /ping.
62+
testurl: https://REPLACE-ME.w.kunlunaq.com/ping
63+
# frontingsnis drives the wire SNI. ExpandedProvider regenerates each
64+
# masquerade's SNI from this list (deterministic by IP hash), so the
65+
# per-masquerade `sni:` values below are placeholders and get overwritten.
66+
# NOTE: a non-empty country code MUST be passed (WithCountryCode) for SNI
67+
# to be emitted; otherwise the no-SNI fallback verifies the cert against
68+
# the masquerade Domain (img.alicdn.com), which also works here.
69+
frontingsnis:
70+
default:
71+
usearbitrarysnis: true
72+
arbitrarysnis:
73+
- img.alicdn.com
74+
- gw.alicdn.com
75+
- a.alicdn.com
76+
# Masquerades: Aliyun edge IPs verified 2026-06-23 to present the Alibaba
77+
# *.tbcdn.cn cert. The upstream pipeline should harvest a much larger pool
78+
# from in-region vantage points; this is a seed set across 3 /24 blocks.
79+
masquerades:
80+
- domain: img.alicdn.com
81+
ipaddress: 155.102.54.137
82+
sni: ""
83+
verifyhostname: null
84+
- domain: img.alicdn.com
85+
ipaddress: 155.102.54.138
86+
sni: ""
87+
verifyhostname: null
88+
- domain: img.alicdn.com
89+
ipaddress: 163.181.66.200
90+
sni: ""
91+
verifyhostname: null
92+
- domain: img.alicdn.com
93+
ipaddress: 163.181.66.201
94+
sni: ""
95+
verifyhostname: null
96+
- domain: img.alicdn.com
97+
ipaddress: 8.25.82.183
98+
sni: ""
99+
verifyhostname: null
100+
- domain: img.alicdn.com
101+
ipaddress: 8.25.82.184
102+
sni: ""
103+
verifyhostname: null

aliyun_live_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package domainfront
2+
3+
import (
4+
"context"
5+
"io"
6+
"net/http"
7+
"os"
8+
"strings"
9+
"testing"
10+
"time"
11+
12+
utls "github.com/refraction-networking/utls"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
// TestAliyunProviderLive exercises aliyun-provider.yaml against the real Aliyun
17+
// CDN. It proves the security-critical path end to end: the config's GlobalSign
18+
// root verifies the live Alibaba *.tbcdn.cn chain, the generated SNI is accepted
19+
// by the edge, and the edge routes a *cross-organization* Host (Bilibili's
20+
// s1.hdslb.com) over a TLS session that presents Alibaba's certificate — i.e.
21+
// domain fronting works through this library's own dial + verify code.
22+
//
23+
// Skipped by default (it talks to the public internet). Run with:
24+
//
25+
// DOMAINFRONT_LIVE=1 go test -run TestAliyunProviderLive -v
26+
func TestAliyunProviderLive(t *testing.T) {
27+
if os.Getenv("DOMAINFRONT_LIVE") == "" {
28+
t.Skip("live network test; set DOMAINFRONT_LIVE=1 to run")
29+
}
30+
31+
raw, err := os.ReadFile("aliyun-provider.yaml")
32+
require.NoError(t, err)
33+
34+
cfg, err := ParseConfigYAML(raw)
35+
require.NoError(t, err)
36+
37+
// The GlobalSign root parses into a usable pool.
38+
pool, err := cfg.CertPool()
39+
require.NoError(t, err)
40+
41+
// Expand with a country code so FrontingSNIs drives the wire SNI
42+
// (config.go only emits SNI when countryCode != "").
43+
p := ExpandedProvider(cfg.Providers["aliyun"], "cn")
44+
require.NotEmpty(t, p.Masquerades)
45+
46+
const crossOrgHost = "s1.hdslb.com" // Bilibili — unrelated to Alibaba
47+
48+
// Phase 1: prove the security-critical path through the library's own
49+
// dialFront — the config's GlobalSign root verifies the live Alibaba
50+
// *.tbcdn.cn chain under the production Chrome_131 ClientHello, for every
51+
// masquerade, using the SNI that FrontingSNIs generated.
52+
// For each masquerade: dial with the production Chrome_131 ClientHello
53+
// (which Aliyun answers with HTTP/2 over ALPN), then drive the library's
54+
// real doRequest — proving the h2 transport path fronts a cross-org Host
55+
// (Bilibili) over a TLS session bearing Alibaba's certificate.
56+
var dialed int
57+
var frontedOK bool
58+
for _, m := range p.Masquerades {
59+
require.NotEmpty(t, m.SNI, "expanded masquerade should carry an SNI")
60+
61+
f := newFront(m, "aliyun")
62+
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
63+
res := dialFront(ctx, f, pool, utls.HelloChrome_131, NetDialer{})
64+
if res.err != nil {
65+
cancel()
66+
t.Logf("dial %s (SNI %s) failed: %v", m.IpAddress, m.SNI, res.err)
67+
continue
68+
}
69+
dialed++
70+
proto := negotiatedProtocol(res.conn)
71+
t.Logf("TLS+verify OK: ip=%s sni=%s alpn=%s (cert chained to config's GlobalSign root)", m.IpAddress, m.SNI, proto)
72+
73+
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://"+crossOrgHost+"/", nil)
74+
resp, rerr := (&roundTripper{}).doRequest(req, res.conn, crossOrgHost, nil)
75+
if rerr != nil {
76+
res.conn.Close()
77+
cancel()
78+
t.Logf("fronted GET via %s failed: %v", m.IpAddress, rerr)
79+
continue
80+
}
81+
body, _ := io.ReadAll(io.LimitReader(resp.Body, 256))
82+
resp.Body.Close() // h2Body close tears down the conn
83+
cancel()
84+
t.Logf("fronted %s via %s (SNI %s, %s): HTTP %d, proto=HTTP/%d, body=%q",
85+
crossOrgHost, m.IpAddress, m.SNI, proto, resp.StatusCode, resp.ProtoMajor, strings.TrimSpace(string(body)))
86+
if resp.StatusCode == http.StatusOK {
87+
frontedOK = true
88+
}
89+
}
90+
require.NotZero(t, dialed, "no Aliyun edge IP completed TLS + GlobalSign verification")
91+
require.True(t, frontedOK, "no edge served the cross-org Host (%s) — h2 fronting did not route", crossOrgHost)
92+
}

0 commit comments

Comments
 (0)