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

WireGuardDevice

WireGuardDevice پیاده‌سازی WireGuard درون chain WaterWall است. این node IP packet‌های داخلی را به پیام‌های WireGuard مانند handshake، cookie و transport تبدیل می‌کند و مسیر برعکس را نیز انجام می‌دهد.

این node نه UDP socket می‌سازد و نه WireGuard interface سیستم‌عامل را ایجاد می‌کند. تقسیم مسئولیت معمول به این شکل است:

  • TunDevice، RawSocket، PacketsToConnection یا یک packet node دیگر سمت packet داخلی را فراهم می‌کند
  • WireGuardDevice crypto، state مربوط به peer، مسیریابی allowed IP، handshake، keepalive، rekey و endpoint roaming را مدیریت می‌کند
  • UdpStatelessSocket مسئول UDP socket بیرونی است

packet داخلیِ رمزگشایی‌شده روی packet line همان worker دریافت‌کننده ارسال می‌شود. رمزگشایی، بررسی replay و هماهنگی state مربوط به peer انتقال جداگانه‌ای بر اساس جریان TCP/UDP اضافه نمی‌کنند؛ مصرف‌کنندهٔ stateful بعدی خودش مالکیت worker جریان را برقرار می‌کند.

جایگاه رایج​

UdpStatelessSocket فقط در ابتدای chain پشتیبانی می‌شود؛ هنگام استفاده از آن، سمت transport وایرگارد به prev متصل است.

UdpStatelessSocket -> WireGuardDevice -> TunDevice

UdpListener نیز فقط در ابتدای chain قرار می‌گیرد و نمی‌تواند پس از WireGuardDevice باشد. این node جایگزین پشتیبانی‌شده‌ای برای UdpStatelessSocket در سمت transport نیست: WireGuard یک companion transport line به آن سمت می‌سازد، اما UdpListener callback مربوط به downstream Init را رد می‌کند. برای UDP transport در این توپولوژی از UdpStatelessSocket استفاده کنید.

layer solver می‌تواند سمت transport لایهٔ L4 را، در صورتی که node‌های مجاور اجازه دهند، در next یا prev قرار دهد. این نقش از لایه‌های حل‌شده استخراج می‌شود: L3 سمت packet داخلی و L4 سمت transport بیرونی است. تعیین جهت، محدودیت جایگاه یا قرارداد callback node مجاور را تغییر نمی‌دهد؛ به‌ویژه UdpListener نمی‌تواند adapter انتقال WireGuard باشد.

اگر auth-client-node-name تنظیم شود، WireGuardDevice یک UserController داخلی فقط در سمت transport می‌سازد:

UdpStatelessSocket -> UserController -> WireGuardDevice -> packet side node

سمت packet عمداً دست‌نخورده می‌ماند. packet line‌های worker از UserController داخلی عبور نمی‌کنند؛ فقط companion transport line‌های معمولی و تحت مالکیت WireGuardDevice به آن داده می‌شوند.

نمونه تنظیم​

{
"name": "wg-device",
"type": "WireGuardDevice",
"settings": {
"privatekey": "<base64-32-byte-private-key>",
"peers": [
{
"publickey": "<base64-32-byte-peer-public-key>",
"allowedips": "10.44.0.2/32,10.44.1.0/24,fd00::2/128",
"endpoint": "vpn.example.com:51820",
"presharedkey": "<optional-base64-32-byte-psk>",
"persistentkeepalive": 25
}
]
},
"next": "packet-side"
}

تنظیمات ضروری​

فیلدنوعتوضیح
privatekeystringprivate key دستگاه WireGuard، به‌صورت base64 و 32 بایتی.
peersarrayآرایه غیرخالی از peer‌ها. حداکثر فعلی 32 peer است.

object مربوط به settings الزامی است و نباید خالی باشد.

تنظیمات اختیاری​

فیلدپیش‌فرضتوضیح
auth-client-node-nameنداردیک UserController داخلی در سمت transport می‌سازد و از این AuthenticationClient استفاده می‌کند.
sweep-interval-ms1000با تنظیم auth-client-node-name، این مقدار به UserController داخلی داده می‌شود.
verbosefalseبا تنظیم auth-client-node-name، این مقدار به UserController داخلی داده می‌شود.

هیچ تنظیم جهت یا جستجوی نوع node‌ای وجود ندارد. پس از ساخت chain، layer solver باید دو لبه مجاور WireGuard را دقیقاً به یک سمت L3 و یک سمت L4 تفکیک کند. لبه L3 سمت packet داخلی و لبه L4 سمت transport بیرونی می‌شود. chain‌های مبهم به‌جای پیش‌فرض قرار دادن next، در راه‌اندازی با خطا مواجه می‌شوند؛ در صورت نیاز در توپولوژی‌های پیچیده، یک bridge صریح packet/stream مانند PacketsToStream یا StreamToPackets اضافه کنید.

در حال حاضر UserController داخلی فقط ساختار chain را آماده می‌کند. authentication در WireGuard همچنان بر پایه peers تنظیم‌شده است و WireGuardDevice هنوز peer‌ها را به user‌های database متصل نمی‌کند. تا زمان پیاده‌سازی این بخش، line‌های transport بدون مدیریت از UserController داخلی عبور می‌کنند.

تنظیمات peer​

فیلدهای ضروری هر peer:

فیلدنوعتوضیح
publickeystringpublic key peer به‌صورت base64 و 32 بایتی.
allowedipsstringفهرستی از CIDR‌ها با جداکننده comma؛ IPv4 و IPv6 پشتیبانی می‌شوند. هر peer حداقل یک و حداکثر 16 entry دارد.
endpointstringendpoint peer به شکل host:port، ipv4:port یا [ipv6]:port. hostname در زمان ساخت tunnel resolve می‌شود.

فیلدهای اختیاری هر peer:

فیلدپیش‌فرضتوضیح
presharedkeyنداردPSK اختیاری WireGuard به‌صورت base64 و 32 بایتی.
persistentkeepalive0فاصله keepalive بر حسب ثانیه. بازه معتبر 0 تا 65535 است؛ 0 یعنی غیرفعال.

فاصله‌های داخل allowedips نادیده گرفته می‌شوند؛ بنابراین مقداری مثل 10.0.0.2/32, 10.1.0.0/16 معتبر است.

چه چیزهایی را تنظیم نمی‌کند​

WireGuardDevice جایگزین wg-quick نیست و این موارد را مدیریت نمی‌کند:

  • Address interface
  • ListenPort
  • MTU
  • route table سیستم
  • PostUp یا PostDown
  • firewall، NAT یا forwarding
  • OS WireGuard interface

برای address و route داخلی از TunDevice، برای bind کردن UDP از UdpStatelessSocket و برای firewall یا NAT از تنظیمات host یا script استفاده کنید.

مسیر packet داخلی​

وقتی IP packet داخلی به WireGuardDevice برسد:

  1. packet باید IPv4 یا IPv6 باشد
  2. destination IP از packet خوانده می‌شود
  3. peer با longest-prefix match روی AllowedIPs انتخاب می‌شود
  4. packet به WireGuard transport-data message رمز می‌شود
  5. endpoint مربوط به peer در destination routing context مربوط به transport line نوشته می‌شود
  6. پیام WireGuard به سمت UDP transport فرستاده می‌شود

اگر مقصد داخلی با هیچ peer متناظر نباشد، packet دور ریخته می‌شود.

در نتیجه، AllowedIPs مسیریابی outbound بر اساس cryptokey را کنترل می‌کند و prefix مشخص‌تر اولویت دارد.

مسیر transport​

سمت transport پیام‌های خام WireGuard را حمل می‌کند. وقتی datagram از UdpStatelessSocket برسد:

  1. source IP و port از routing context خوانده می‌شود
  2. نوع پیام WireGuard تشخیص داده می‌شود
  3. handshake، cookie یا transport data پردازش می‌شود
  4. transport data معتبر decrypt می‌شود
  5. plaintext باید IPv4 یا IPv6 معتبر باشد
  6. source IP داخل plaintext باید با AllowedIPs همان peer match شود
  7. در پایان، packet داخلی به سمت packet فرستاده می‌شود

بنابراین AllowedIPs دو کاربرد دارد: در outbound، peer را بر اساس destination انتخاب می‌کند؛ در inbound نیز پس از decrypt شدن packet، source را اعتبارسنجی می‌کند.

endpoint و Roaming​

هر peer یک endpoint تنظیم‌شده و یک endpoint زنده دارد. اولی از JSON می‌آید و هنگام ساخت resolve می‌شود. پس از دریافت ترافیک معتبر inbound، endpoint زنده با آدرس و پورت فرستنده به‌روزرسانی می‌شود.

این همان roaming معمول WireGuard را ممکن می‌کند: پس از آنکه peer از آدرس یا پورت تازه‌ای ترافیک معتبر بفرستد، packet‌های بعدی می‌توانند به همان endpoint یادگرفته‌شده برگردند. اگر state مربوط به peer یا session به‌اندازه کافی reset شود، live endpoint دوباره به configured endpoint برمی‌گردد.

hostname مربوط به endpoint فقط هنگام ساخت tunnel resolve می‌شود و به‌طور پیوسته دوباره resolve نخواهد شد.

startup و line‌ها​

هنگام start، node:

  1. برای هر worker یک normal companion transport line می‌سازد
  2. peer‌های تنظیم‌شده را با wireguardifConnect() فعال می‌کند
  3. loop دوره‌ای device را روی worker 0 شروع می‌کند
  4. loop هر 400 ms اجرا می‌شود

این loop handshake اولیه، keepalive، retry، rekey و پاک‌سازی session‌ها را انجام می‌دهد.

WireGuardDevice با packet-tunnel API WaterWall ساخته می‌شود و per-line packet state ندارد. سمت داخلی از packet line‌های worker در chain استفاده می‌کند. سمت transport نیز line‌های معمولی تحت مالکیت WireGuardDevice را به کار می‌گیرد تا node‌های UDP، پیام WireGuard را با packet line داخلی اشتباه نگیرند.

اگر سمت transport برابر prev باشد، node ممکن است هنگام startup مقداردهی اولیه موردنیاز worker packet line را به سمت packet در next بفرستد.

اگر UserController داخلی فعال باشد، همین companion transport line‌های معمولی از مسیر آن مقداردهی می‌شوند؛ مقداردهی packet line همچنان از آن عبور نمی‌کند.

انتخاب جهت​

اگر transport side برابر next باشد:

  • upstream payload به‌عنوان packet داخلی رمزنگاری می‌شود
  • downstream payload به‌عنوان transport data رمزگشایی می‌شود
  • packet داخلی رمزگشایی‌شده در جهت downstream به‌سمت prev فرستاده می‌شود

اگر transport side برابر prev باشد:

  • upstream payload به‌عنوان transport data رمزگشایی می‌شود
  • downstream payload به‌عنوان packet داخلی رمزنگاری می‌شود
  • packet داخلی رمزگشایی‌شده در جهت upstream به‌سمت next فرستاده می‌شود

این جهت‌ها منحصراً از نقش‌های حل‌شده لایه‌های مجاور انتخاب می‌شوند.

padding​

WireGuardDevice به 16 بایت left padding نیاز دارد. پیش از فرستادن ciphertext به سمت UDP، header مربوط به WireGuard transport-data در این فضا قرار می‌گیرد.

auth tag رمزنگاری نیز به انتهای packet اضافه می‌شود.

نمونه کامل​

سمت client:

{
"name": "tun-client",
"type": "TunDevice",
"settings": {
"device-name": "tun0",
"device-ip": "10.44.0.1/24"
}
}
{
"name": "wg-client",
"type": "WireGuardDevice",
"settings": {
"privatekey": "<client-private-key>",
"peers": [
{
"publickey": "<server-public-key>",
"allowedips": "10.44.0.2/32,10.55.0.0/16",
"endpoint": "198.51.100.20:51820",
"persistentkeepalive": 25
}
]
},
"next": "tun-client"
}
{
"name": "udp-client",
"type": "UdpStatelessSocket",
"settings": {
"listen-address": "0.0.0.0",
"listen-port": 51820
},
"next": "wg-client"
}

سمت server:

{
"name": "udp-server",
"type": "UdpStatelessSocket",
"settings": {
"listen-address": "0.0.0.0",
"listen-port": 51820
},
"next": "wg-server"
}
{
"name": "wg-server",
"type": "WireGuardDevice",
"settings": {
"privatekey": "<server-private-key>",
"peers": [
{
"publickey": "<client-public-key>",
"allowedips": "10.44.0.1/32",
"endpoint": "203.0.113.10:51820"
}
]
},
"next": "tun-server"
}

خطاهای رایج​

  • انتظار اینکه این node UDP socket بسازد. برای آن از UdpStatelessSocket استفاده کنید.
  • انتظار اینکه OS WireGuard interface ساخته شود. برای رفتار اینترفیس از TunDevice یا شبکه سیستم‌عامل میزبان استفاده کنید.
  • مبهم گذاشتن هر دو دامنه لبه مجاور WireGuard به‌جای استفاده از یک bridge صریح packet/stream.
  • قرار دادن دستی UserController در سمت transport در حالی که auth-client-node-name هم تنظیم شده است؛ WireGuardDevice خودش این node داخلی را می‌سازد.
  • فراموش کردن اینکه در نسخه فعلی هر peer به endpoint نیاز دارد.
  • تنظیم نکردن AllowedIPs مطابق destination‌های داخلی مورد نیاز.
  • انتظار re-resolve شدن مداوم hostname endpoint‌ها.

متادیتای node​

متادیتای برگرفته از کد منبع:

ویژگیمقدار
node flagskNodeFlagNone
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayerAnything
layer_group_prev_nodekNodeLayerAnything | kNodeLayerOppositeNext
layer_group_next_nodekNodeLayerAnything | kNodeLayerOppositePrev
required_padding_left16 بایت