پرش به مطلب اصلی

بخش ۳: فایل‌های پیکربندی و زنجیره‌ها

فایل 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": []
}
فیلدنوعالزامیتوضیح
nameStringبلهنام خوانا برای این فایل پیکربندی.
authorStringخیردر صورت حذف، مقدار پیش‌فرض "EMPTY_AUTHOR" است.
config-versionIntegerخیرشماره‌ی نسخه‌ی پیکربندی که کاربر تعیین می‌کند.
core-minimum-versionIntegerخیرنشانه‌ی حداقل نسخه‌ی هسته که کاربر تعیین می‌کند.
encryptedBooleanخیرConfig Loader آن را برای روندهای مربوط به پیکربندی رمزنگاری‌شده Parse می‌کند.
nodesArrayبلهباید آرایه‌ای غیرخالی از Objectهای Node باشد.
variablesObjectخیرمقادیر اختیاری که می‌توان پیش از 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"
}
فیلدنوعالزامیتوضیح
nameStringبلهباید در همان فایل پیکربندی یکتا باشد.
typeStringبلهباید دقیقاً با نام یکی از انواع بارگذاری‌شده‌ی Node، مانند "TcpListener"، مطابقت داشته باشد.
versionIntegerخیردر صورت حذف، مقدار پیش‌فرض 0 است.
settingsObjectبسته به Nodeتنظیمات مختص همان Node.
nextStringبسته به‌جایگاهنام 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های عمومی قرار ندهید.