TlsServer
TlsServer لایه TLS سمت server در WaterWall است و از OpenSSL استفاده میکند. TLS recordهای رمزنگاریشده را از نود قبلی
میگیرد، یک handshake واقعی در سمت server انجام میدهد و application data رمزگشاییشده را به نود بعدی میفرستد. داده
cleartext برگشتی نیز دوباره رمزنگاری میشود و بهشکل TLS record به نود قبلی برمیگردد.
رفتار این نود شبیه یک TLS terminator عمومی مانند nginx stream است، نه nginx http. خود نود HTTP، HTTP/2، Trojan،
VLESS یا هر application protocol دیگری را که بعد از TLS قرار دارد تجزیه نمیکند.
جایگاه رایج
chain سمت server برای HTTP:
TcpListener -> TlsServer -> HttpServer
chain همراه با proxy protocol:
TcpListener -> TlsServer -> TrojanServer -> TcpUdpConnector
TcpListener -> TlsServer -> VlessServer -> TcpUdpConnector
TLS termination عمومی قبل از routing:
TcpListener -> TlsServer -> Router
نود بعدی application data را بهشکل cleartext دریافت میکند، نه TLS record.
نمونه ساده
[
{
"name": "tls-server",
"type": "TlsServer",
"settings": {
"cert-file": "/etc/waterwall/fullchain.pem",
"key-file": "/etc/waterwall/privkey.pem",
"min-version": "TLSv1.2",
"max-version": "TLSv1.3",
"select-alpns": ["http/1.1"],
"session-cache": "none",
"session-tickets": true,
"verbose": false
},
"next": "http-server"
},
{
"name": "http-server",
"type": "HttpServer",
"settings": {},
"next": "service"
}
]
نمونه Fallback
{
"name": "tls-server",
"type": "TlsServer",
"settings": {
"cert-file": "/etc/waterwall/fullchain.pem",
"key-file": "/etc/waterwall/privkey.pem",
"fallback-node-name": "nginx-fallback",
"fallback-intentional-delay-ms": 7,
"fallback-intentional-delay-jitter-ms": 1,
"select-alpns": ["http/1.1"]
},
"next": "protected-protocol"
}
Fallback فقط تا پیش از قطعی شدن مسیر TLS قابل انتخاب است. کاربردش رسیدگی به probeهای plaintext یا malformed روی یک پورت عمومی TLS است، نه مسیریابی application پس از authentication.
فیلدهای اجباری
فیلدهای top-level:
| Field | Type | توضیح |
|---|---|---|
name | string | نام دلخواه نود؛ باید داخل config یکتا باشد. |
type | string | باید دقیقاً "TlsServer" باشد. |
settings | object | تنظیمات TLS server؛ نباید خالی باشد. |
next | string | نودی که پس از موفقیت TLS داده cleartext را دریافت میکند؛ در استفاده معمول لازم است. |
فیلدهای اجباری داخل settings:
| Field | Type | توضیح |
|---|---|---|
cert-file | string | مسیر فایل PEM certificate سرور. |
key-file | string | مسیر فایل PEM private key سرور. |
هر دو فایل باید برای OpenSSL context معتبر باشند.
تنظیمات اختیاری
| Field | Default | توضیح |
|---|---|---|
sni | تنظیم نشده | فقط clientهایی را میپذیرد که SNI آنها دقیقاً برابر این مقدار باشد. |
select-alpns | ["http/1.1"] | فهرست انتخاب ALPN بهترتیب ترجیح server. |
alpns | تنظیم نشده | نام قدیمی تنظیم ALPN؛ بهتر است از select-alpns استفاده کنید. |
min-version | "TLSv1.2" | حداقل TLS version. |
max-version | "TLSv1.3" | حداکثر TLS version. |
ciphers | "HIGH:!aNULL:!MD5" | OpenSSL cipher string برای TLS 1.2 و قدیمیتر. |
prefer-server-ciphers | false | server cipher preference را فعال میکند. |
session-timeout | 300 | عمر TLS session به ثانیه. |
handshake-timeout-ms | 60000 | مهلت قطعی برای کامل شدن TLS handshake؛ مقدار 0 آن را غیرفعال میکند. |
session-cache | "none" | حالت session cache. |
session-cache-size | 20480 | اندازه cache داخلی OpenSSL. |
session-tickets | true | TLS session ticketها را فعال میکند. |
fallback-node-name | تنظیم نشده | branch جایگزین برای ورودیای که پیش از قطعی شدن مسیر TLS، آشکارا غیر TLS است. نامهای fallback-node و fallback هم پذیرفته میشوند. |
fallback-intentional-delay-ms | 7 | تأخیر عمدی برای payloadهای upstream در fallback؛ مقدار 0 آن را غیرفعال میکند. |
fallback-intentional-delay-jitter-ms | 1 | jitter تصادفی برای زمانبندی payloadهای fallback؛ اگر delay صفر باشد نادیده گرفته میشود. |
tls13-record-shaping | تنظیم نشده | تنظیم آزمایشی padding و delay سمت فرستنده برای TLS 1.3. |
verbose | false | جزئیات بیشتری از lifecycle مربوط به TLS را log میکند. |
نمیتوانید sni و fallback را همزمان تنظیم کنید؛ وگرنه ممکن بود یک ClientHello معتبر با SNI متفاوت بهعنوان plaintext
به fallback برسد و رفتار سرور را قابل fingerprint کند.
شکلدهی آزمایشی TLS 1.3 Record
تنظیم tls13-record-shaping بهصورت اختیاری نخستین application recordهای TLS 1.3 را که همین نود میفرستد pad میکند و
به تأخیر میاندازد. اگر این تنظیم وجود نداشته باشد، قابلیت غیرفعال است. این قابلیت هرگز ciphertext دریافتی، handshake
recordها، alertها، application recordهای خالی، byteهای fallback یا recordهای TLS 1.2 را تغییر نمیدهد. اگر باید هر دو
جهت ارسال شکل داده شوند، TlsServer محلی و TlsClient راه دور را جداگانه تنظیم کنید.
فرم سفارشی:
"tls13-record-shaping": {
"scope": {"first-application-records": 8},
"outcomes": [
{
"probability": 50,
"padding-bytes": [100, 200],
"delay": {"probability": 75, "ms": [10, 20]}
},
{"probability": 10, "padding-bytes": [400, 600]}
]
}
padding-bytes و delay.ms یک integer یا بازه integer شامل دو سر [minimum, maximum] میپذیرند. تعداد outcomeها باید
بین ۱ تا ۱۶ باشد؛ first-application-records بین ۱ تا ۱۰۲۴، padding بین ۱ تا ۴۰۹۶ byte و delay بین ۰ تا ۱۰۰۰
millisecond است. probabilityهای outcome بهصورت تجمعی و mutually exclusive تفسیر میشوند و مجموع آنها نباید از ۱۰۰
بیشتر باشد. هر بار حداکثر یک outcome انتخاب میشود و درصد استفادهنشده record را بدون تغییر میگذارد. probability مربوط
به delay فقط پس از انتخاب همان outcome بررسی میشود. key ناشناخته و مقدار غیرinteger یا خارج از محدوده باعث خطای
startup میشود. هر record واجد شرایط یک جایگاه از scope مصرف میکند، حتی اگر هیچ outcomeای انتخاب نشود.
در حال حاضر فقط فرم سفارشی شامل scope و outcomes پذیرفته میشود. تا زمانی که اندازهگیریهای representative برای
capture، overhead، موفقیت connection و classifier انتشار یک preset versionشده را توجیه نکنند، key به نام profile
رد میشود.
Padding از byteهای صفر استاندارد TLS 1.3 در TLSInnerPlaintext استفاده میکند و بهاندازه انتخابشده به ciphertext
میافزاید، البته تا ظرفیت قانونی باقیمانده در TLS record. Delay پس از رمزنگاری اعمال میشود و latency انتخابشده را
اضافه میکند. ترتیب recordها روی wire با رابطه
release_at = max(now + delay, previous_release_at) حفظ میشود. ciphertext صفشده برای هر line حداکثر ۱ MiB است؛ در
۷۶۸ KiB به producer backpressure داده میشود و در ۳۸۴ KiB دوباره آزاد میشود. انتقال Pause و Resume از state فعلی wire
پیروی میکند: اگر تخلیه هنگام Resume بهصورت re-entrant یک Pause دیگر از wire دریافت کند، Resume قدیمی تا رسیدن Resume
واقعی بعدی ارسال نمیشود. پس از آنکه close_notify همتا سمت cleartext را پایان داد، TlsServer هیچ Payload، Pause یا
Resumeای به آن سمت نمیفرستد؛ application byteهایی که بعداً دریافت شوند یا پیشتر توسط یک callback بیرونی رمزگشایی شده
باشند دور ریخته میشوند. اگر سمت cleartext پایان یابد، ciphertext پذیرفتهشده server و یک close_notify بدون padding پیش
از downstream Finish تخلیه میشوند. پایان سمت wire خروجی صفشده را لغو و دور میریزد و فقط به سمت cleartext منتقل میشود.
اگر ساخت timer شکست بخورد، recordها فوراً و بهترتیب تخلیه میشوند.
Record shaping میتواند اندازه و زمانبندی recordهای ابتدایی را کمتر قابل پیشبینی کند، اما نمیتواند یک round trip
داخلی handshake را حذف کند، burst را کوچکتر کند یا همه ویژگیهای جهت و زمانبندی را پنهان کند. وقتی multiplexing برای
deployment مناسب است، آن را همراه MuxClient/MuxServer استفاده کنید.
TLS Versionها
مقادیر قابل قبول:
TLSv1
TLSv1.1
TLSv1.2
TLSv1.3
TLS1.0
TLS1.1
TLS1.2
TLS1.3
1.0
1.1
1.2
1.3
min-version نباید از max-version بزرگتر باشد.
ALPN Selection
TlsServer از ALPN فقط برای انتخاب protocol استفاده میکند، نه برای کنترل دسترسی.
ترتیب اعضای array همان ترتیب ترجیح server است:
"select-alpns": ["h2", "http/1.1"]
اگر client هر دو مقدار را ارائه کند، h2 انتخاب میشود. اگر ALPN ارسالشده با هیچ گزینهای مشترک نباشد، handshake بدون
ALPN توافقشده ادامه پیدا میکند. وقتی client اصلاً ALPN نفرستد، callback انتخاب در OpenSSL اجرا نمیشود و handshake باز
هم بدون ALPN ادامه مییابد.
modeهای select-alpns:
| Configuration | رفتار |
|---|---|
| نوشته نشده | مقدار پیشفرض ["http/1.1"] به کار میرود. |
[] | انتخاب ALPN کاملاً غیرفعال میشود. |
| array غیرخالی | نخستین مقدار تنظیمشدهای انتخاب میشود که client نیز آن را ارائه کرده باشد. |
alpns قدیمی است، اما همچنان خوانده میشود. هر عضو میتواند یک string یا objectی با فیلد value باشد. alpns و
select-alpns را همزمان با مقدار غیرخالی تنظیم نکنید.
ALPN، protocol انتخابشده را به tunnelهای بعدی اعلام نمیکند. اگر h2 را ارائه میکنید، chain بعد از TlsServer باید
واقعاً بتواند رفتاری قابلقبول برای HTTP/2 نشان دهد؛ در غیر این صورت deployment راحتتر fingerprint میشود.
Session Cache
مقادیر پشتیبانیشده برای session-cache:
| Value | رفتار |
|---|---|
"none" | حالت server cache در OpenSSL، با lookup و store داخلی غیرفعال؛ مقدار پیشفرض. |
"off" | session cache را غیرفعال میکند. |
"builtin" | cache داخلی server در OpenSSL را با session-cache-size فعال میکند. |
"builtin:SIZE" | cache داخلی را فعال میکند و اندازه را همانجا میگیرد؛ برای مثال "builtin:4096". |
حالت shared در پیادهسازی فعلی پشتیبانی نمیشود.
رفتار در زمان اجرا
در upstream Init، TlsServer:
- state مربوط به OpenSSL را برای line میسازد
- memory BIOها را میسازد
- SSL object را در server mode قرار میدهد
- مگر با
handshake-timeout-ms: 0، deadline مربوط به handshake را فعال میکند - اگر fallback تنظیم نشده باشد، branch محافظتشده
nextرا بلافاصله راه میاندازد - در صورت وجود fallback، منتظر byteهای اولیه میماند تا مسیر TLS یا fallback را انتخاب کند
byteهای رمزنگاریشده upstream از نود قبلی وارد OpenSSL میشوند. پس از پایان TLS handshake، application data رمزگشاییشده
به next میرود.
داده cleartext برگشتی از next تا کامل شدن TLS handshake در صف میماند. سپس با SSL_write() رمزنگاری میشود و بهشکل
TLS record در جهت downstream فرستاده میشود.
مفهوم Establishment
در WaterWall، Est یعنی transport زیرین برقرار شده است؛ نه اینکه TLS handshake هم پایان یافته باشد.
TlsServer downstream Est را فقط یک بار میفرستد و ممکن است این اتفاق پیش از پایان TLS handshake رخ دهد. آماده بودن TLS
در state داخلی و پس از پایان handshake در OpenSSL مشخص میشود.
رفتار Fallback
Fallback فقط تا پیش از قطعی شدن مسیر TLS قابل انتخاب است.
classifier، byteهای ابتدایی upstream را بررسی میکند:
- byteهایی که آشکارا TLS نیستند به fallback میروند
- byteهایی که شبیه TLS handshake شروع میشوند، مانند
16 03، در مسیر TLS میمانند - با تولید ServerHello توسط OpenSSL، مسیر line روی branch محافظتشده TLS قطعی میشود
با شروع fallback، TlsServer منابع TLS مربوط به line را آزاد و branch جایگزین را مقداردهی اولیه میکند. byteهای ذخیرهشده
پس از delay و jitter تنظیمشده، بدون تغییر به آن branch فرستاده میشوند. از آن به بعد payloadها بدون رمزنگاری یا رمزگشایی
TLS از fallback عبور میکنند.
handshakeهایی که ظاهر TLS دارند اما malformed، بیشازحد بزرگ، ناقص یا کند هستند در همان مسیر TLS بسته میشوند؛ پس از تشخیص اولیه دیگر به fallback فرستاده نخواهند شد.
تأخیر fallback فقط روی payloadهای upstream اعمال میشود و پاسخهای downstream عمداً به تأخیر نمیافتند. اگر upstream
Finish در حالی برسد که payloadی هنوز در صف تأخیر است، Finish تا تحویل آن payload صبر میکند.
برای camouflage روی یک پورت عمومی، بهتر است fallback یک سرویس واقعی TLS، معمولاً nginx، با certificate، SNI، ALPN،
cipher، ticket و نسخه protocol مشابه TlsServer باشد. قرار دادن HTTP ساده پشت یک پورت عمومی TLS معمولاً خیلی راحتتر
fingerprint میشود.
Handshake Timeout
handshake-timeout-ms مهلتی قطعی است که با مقداردهی line آغاز میشود و دریافت byteهای جدید آن را تمدید نمیکند. این زمان
از timeoutهای listener، مانند initial-idle-timeout-ms و active-idle-timeout-ms در TcpListener، مستقل است.
فقط زمانی handshake-timeout-ms را 0 بگذارید که عمداً هیچ deadline قطعی برای TLS handshake نمیخواهید.
رفتار Finish
برای بستن تمیز جهت downstream، اگر TLS handshake کامل شده باشد TlsServer ابتدا alert مربوط به TLS close_notify را
میفرستد و flush میکند. سپس state محلی TLS را از بین میبرد و WaterWall Finish را در جهت downstream ادامه میدهد.
اگر peer یک TLS shutdown تمیز بفرستد، TlsServer آن را Finish در جهت upstream و به سمت نود بعدی در نظر میگیرد.
خطاهای fatal در OpenSSL باعث پاک شدن state محلی و بسته شدن جهتهای لازم میشوند. در fallback mode، Finish از branch
جایگزین عبور میکند و Finish جهت upstream، در صورت نیاز، منتظر payloadهای تأخیردار میماند.
Padding
TlsServer مستقیماً چیزی به ابتدای payloadهای WaterWall اضافه نمیکند، بنابراین مقدار زیر را اعلام میکند:
required_padding_left = 0
ساخت TLS recordها از طریق memory BIOهای OpenSSL انجام میشود.
Metadata نود
| Property | Value |
|---|---|
| Node flag | kNodeFlagChainHead |
| Previous node | مجاز، در استفاده عادی required |
| Next node | مجاز، در استفاده عادی required |
| Layer group | kNodeLayerAnything |
required_padding_left | 0 |
| Line state | OpenSSL SSL object، memory BIOها، handshake flagها، fallback state، pending downstream queue |
نکتههای Nginx Matching
مقادیر پیشفرض برای شباهت به رفتار عادی TLS server در nginx stream انتخاب شدهاند. تغییر گزینههای زیر ممکن است همچنان
TLS معتبری بسازد، اما خروجی دیگر شبیه آن baseline نخواهد بود:
ciphersmin-versionیاmax-versionprefer-server-cipherssession-cachesession-tickets- ALPN selection
sni- fallback target و fallback timing
اگر هدفتان شباهت در سطح wire به nginx پیشفرض است، این مقدارها را تغییر ندهید مگر اینکه اندازهگیری مشخصی دلیل آن را نشان داده باشد.
اشتباههای رایج
- انتظار نداشته باشید
TlsServerخودش HTTP را تجزیه کند؛ برای این کارHttpServerرا بعد از آن قرار دهید. sniرا با fallback ترکیب نکنید.- از ALPN برای authentication یا رد کردن اتصال استفاده نکنید؛ نبودن ALPN مشترک، handshake را بدون ALPN توافقشده ادامه میدهد.
h2را اعلام نکنید مگر اینکه هر دو مسیر محافظتشده و fallback بتوانند پاسخی قابلقبول برای HTTP/2 بدهند.- از
sharedبرای session cache استفاده نکنید؛ پیادهسازی نشده است.