بخش ۳: فایلهای پیکربندی و زنجیرهها
فایل core.json، WaterWall را راهاندازی و فهرست فایلهای پیکربندی موردنیاز را
مشخص میکند. هر فایل موجود در این فهرست، یک یا چند زنجیرهی Node را تعریف میکند.
نمونهای از core.json:
{
"configs": [
"configs/server.json"
]
}
سپس Nodeهای واقعی در configs/server.json قرار میگیرند.
ساختار فایل پیکربندی
یک فایل پیکربندی فعلی WaterWall چنین ساختاری دارد:
{
"name": "example-config",
"author": "waterwall-user",
"config-version": 1,
"core-minimum-version": 0,
"encrypted": false,
"nodes": []
}
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
name | String | بله | نام خوانا برای این فایل پیکربندی. |
author | String | خیر | در صورت حذف، مقدار پیشفرض "EMPTY_AUTHOR" است. |
config-version | Integer | خیر | شمارهی نسخهی پیکربندی که کاربر تعیین میکند. |
core-minimum-version | Integer | خیر | نشانهی حداقل نسخهی هسته که کاربر تعیین میکند. |
encrypted | Boolean | خیر | Config Loader آن را برای روندهای مربوط به پیکربندی رمزنگاریشده Parse میکند. |
nodes | Array | بله | باید آرایهای غیرخالی از Objectهای Node باشد. |
variables | Object | خیر | مقادیر اختیاری که میتوان پیش از Parseشدن JSON جایگزین کرد. |
WaterWall پیش از Parseکردن فایل پیکربندی، Commentهای خطی // را نیز حذف میکند؛
به شرط آنکه Comment داخل یک String در JSON نباشد. با این حال، بهتر است در
نمونههای عمومی جدید Comment ننویسید تا فایل برای ابزارهای دیگر نیز JSON معتبر
باقی بماند.
Variableها
Config Loader از Variableهای سطح اول پشتیبانی میکند:
{
"variables": {
"listen_address": "0.0.0.0",
"listen_port": 8080,
"target_host": "example.com",
"target_port": 80
},
"name": "variable-example",
"nodes": [
{
"name": "in",
"type": "TcpListener",
"settings": {
"address": $listen_address$,
"port": $listen_port$
},
"next": "out"
},
{
"name": "out",
"type": "TcpConnector",
"settings": {
"address": $target_host$,
"port": $target_port$
}
}
]
}
Placeholderها بهشکل $name$ نوشته و با مقدار JSON همان Variable جایگزین
میشوند. Placeholder را داخل علامت نقلقول نگذارید. اگر Placeholder به Variableای
اشاره کند که تعریف نشده است، فایل پیکربندی بارگذاری نخواهد شد.
ساختار Node
بیشتر Nodeها چنین ساختاری دارند:
{
"name": "node-name",
"type": "NodeType",
"version": 0,
"settings": {},
"next": "next-node-name"
}
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
name | String | بله | باید در همان فایل پیکربندی یکتا باشد. |
type | String | بله | باید دقیقاً با نام یکی از انواع بارگذاریشدهی Node، مانند "TcpListener"، مطابقت داشته باشد. |
version | Integer | خیر | در صورت حذف، مقدار پیشفرض 0 است. |
settings | Object | بسته به Node | تنظیمات مختص همان Node. |
next | String | بسته بهجایگاه | نام Node بعدی در زنجیره. |
نام نوع Node به بزرگی و کوچکی حروف حساس است.
قواعد زنجیره
با اتصال Nodeها از طریق next یک زنجیره ساخته میشود:
TcpListener -> EncryptionClient -> TcpConnector
WaterWall پیش از اجرا Graph را اعتبارسنجی میکند:
- هر مقدار
nextباید به Node موجودی اشاره کند. - نام Nodeها نباید تکراری باشد.
- هر فایل پیکربندی باید دستکم یک Chain Head داشته باشد.
- Nodeهای معمولی نباید بیرون زنجیره باقی بمانند.
- زنجیره باید به Nodeای ختم شود که اجازه دارد Chain End باشد.
- زنجیرههای حلقوی رد میشوند.
در عمل، Nodeهای Listener معمولاً Chain Head، Nodeهای Connector معمولاً Chain End و Nodeهای Transform میان این دو هستند.
یک TCP Forward ساده
پیکربندی زیر روی TCP پورت 8080 بهصورت محلی گوش میدهد و به
example.com:80 متصل میشود.
{
"name": "simple-tcp-forward",
"author": "waterwall-user",
"config-version": 1,
"core-minimum-version": 0,
"nodes": [
{
"name": "listen-http",
"type": "TcpListener",
"settings": {
"address": "127.0.0.1",
"port": 8080,
"nodelay": true
},
"next": "connect-example"
},
{
"name": "connect-example",
"type": "TcpConnector",
"settings": {
"address": "example.com",
"port": 80,
"domain-strategy": "prefer-ipv4"
}
}
]
}
مسیر ترافیک:
local client -> TcpListener(127.0.0.1:8080) -> TcpConnector(example.com:80)
تونل رمزنگاریشدهی TCP میان دو سرور
برای یک تونل واقعی، معمولاً سمت Client و Server فایل پیکربندی جداگانه دارند.
سمت Client:
local app -> TcpListener -> EncryptionClient -> TcpConnector -> public server
سمت Server:
public server -> TcpListener -> EncryptionServer -> TcpConnector -> private service
پیکربندی Client
{
"name": "encrypted-client",
"author": "waterwall-user",
"config-version": 1,
"core-minimum-version": 0,
"nodes": [
{
"name": "local-entry",
"type": "TcpListener",
"settings": {
"address": "127.0.0.1",
"port": 1080,
"nodelay": true
},
"next": "encrypt"
},
{
"name": "encrypt",
"type": "EncryptionClient",
"settings": {
"algorithm": "chacha20-poly1305",
"password": "replace-with-a-strong-secret",
"salt": "chain-a",
"kdf-iterations": 20000
},
"next": "connect-server"
},
{
"name": "connect-server",
"type": "TcpConnector",
"settings": {
"address": "203.0.113.10",
"port": 443,
"nodelay": true
}
}
]
}
آدرس 203.0.113.10 را با IP سرور خود جایگزین کنید.
پیکربندی Server
{
"name": "encrypted-server",
"author": "waterwall-user",
"config-version": 1,
"core-minimum-version": 0,
"nodes": [
{
"name": "public-entry",
"type": "TcpListener",
"settings": {
"address": "0.0.0.0",
"port": 443,
"nodelay": true
},
"next": "decrypt"
},
{
"name": "decrypt",
"type": "EncryptionServer",
"settings": {
"algorithm": "chacha20-poly1305",
"password": "replace-with-a-strong-secret",
"salt": "chain-a",
"kdf-iterations": 20000
},
"next": "connect-private-service"
},
{
"name": "connect-private-service",
"type": "TcpConnector",
"settings": {
"address": "127.0.0.1",
"port": 22,
"nodelay": true
}
}
]
}
تنظیمات EncryptionClient و EncryptionServer باید با هم یکسان باشند. از Secret
قوی استفاده کنید و فایل پیکربندی حاوی Password واقعی را در فضای عمومی منتشر
نکنید.
اشتباههای رایج
استفاده از شکل نادرست حروف در type:
"TcpListener" معتبر است، اما "tcplistener" نیست.
فراموشکردن next در میانهی زنجیره:
فقط Nodeای که Chain End معتبر است باید بدون next باشد.
Bindکردن سرویس عمومی به Localhost:
آدرس 127.0.0.1 فقط محلی است. اگر Listener باید Clientهای راه دور را بپذیرد، از
0.0.0.0 یا آدرس مشخص یک Interface عمومی استفاده کنید.
ترکیب نادرست Nodeهای Client و Server:
Nodeهای جفت باید روبهروی یکدیگر قرار بگیرند. برای مثال، در یک سمت
EncryptionClient و در سمت دیگر EncryptionServer را با تنظیمات یکسان به کار
ببرید.
قرار دادن Secretها در مثالها: Passwordها، Keyها و Tokenهای واقعی را در Repositoryهای عمومی قرار ندهید.