WIUNIX ACADEMY
احراز هویت OpenVPN با گواهی و Google Authenticator
پایلوت مستقل OpenVPN را با TOTP شخصی، PAM، تطبیق CN و نام کاربر، کنترل کد تکراری و بازیابی خصوصی بسازید؛ بدون حذف اعتبارسنجی گواهی.
محیطت را انتخاب کنآموزش متناسب با محیط تو
کجا اجرا میکنی؟
سیستمعامل و نسخه را انتخاب کن تا مراحل، دستورها و راهحلها برای همان محیط نمایش داده شوند.
آموزش متناسب با مقادیر تو
تنظیمات این آموزش
مقادیر نمونه را برای محیط خودت تنظیم کن؛ متن و دستورهای مرتبط با هم تغییر میکنند. رمز، کلید خصوصی و توکن وارد نکن. مقادیر فقط در همین زبانه نگهداری میشوند و به سرور یا لینک اشتراکگذاری فرستاده نمیشوند. پیش از اجرا، DNS، گواهیها و سیاست شبکهٔ واقعی را بررسی کن.
آیپی، دامنه یا پورت مثالها با محیط تو فرق دارد؟ دکمهٔ زیر را باز کن، مقادیر را تغییر بده و «اعمال در آموزش» را بزن؛ سپس دستورهای بهروزشده را کپی کن.
تنظیم مقادیر برای محیط من7 مقدار قابلتغییر
از اینجا شروع کن
در پایان چه چیزی خواهی داشت؟
الزام گواهی معتبر و کد شخصی TOTP با تطبیق نام کاربر و CN
سیستمعاملها و نسخههای پوششدادهشده
مسیر OSها مستند است؛ حدود آزمون واقعی و مسئولیت پذیرش پایلوت در پایان آمده است.
قبل از شروع
- VM تازه با sudo، کنسول مستقل، DNS و سیاست فایروال مشخص؛ رنج ثابت 10.93.0.0/24 بدون تداخل.
- فرایند CA معتبر، گواهی اختصاصی کلاینت، CRL جاری، کانال انتقال خصوصی و برنامهٔ TOTP شخصی.
- فرم تنظیمات را باز کنید؛ ویرایش، Apply، سپس کپی. مقدارها فقط در همین تب، زبان، مقاله و محیط نگه داشته میشوند؛ seed، کد، رمز و کلید هرگز وارد فرم نشوند.
گواهی معتبر و TOTP شخصی را با هم الزامی کنید
یک نمونهٔ آزمایشی مستقل روی VM تازه میسازید. اتصال به گواهی و کلید معتبر، نام کاربر دقیقاً برابر CN گواهی و کد شخصی TOTP نیاز دارد. در این روش فیلد password کلاینت فقط کد یکبارمصرف را میگیرد؛ گذرواژهٔ لینوکس درخواست نمیشود. کلید گواهی و برنامهٔ تولید کد را جدا نگه دارید؛ قرارگرفتن هر دو روی لپتاپ آلوده، استقلال عاملها را کاهش میدهد.
مخاطب، مدیر لینوکس آشنا با PKI است؛ زمان حدود ۷۰ دقیقه بهعلاوهٔ ثبت دستگاه و آمادهسازی گواهیهاست. TOTP در برابر فیشینگ مصون نیست. MFA جای مجوز دسترسی به مقصدهای تونل یا حفاظت از نشست فعال را نمیگیرد.
مرز اصطلاح MFA: کلید نرمافزاری گواهی و TOTP بدون فعالسازی مستقل، هر دو میتوانند مبتنی بر «داشتن» باشند. این پایلوت بهتنهایی انطباق با NIST AAL2 یا دو نوع عامل متفاوت را ثابت نمیکند. اگر سیاست سازمان چنین چیزی میخواهد، فعالسازی کلید یا authenticator با عامل دانستنی یا زیستسنجی باید واقعاً الزام و جداگانه ارزیابی شود.
هویت گواهی را تا پایان هر دو کنترل حفظ کنید
کلاینت از vpn.example.net به 192.0.2.40 و UDP 11940 میرسد؛ NAT تنها با طرح صریح شبکه انجام شود. ابتدا گواهی، سپس seed شخصی از طریق auth-pam بررسی میشود. هوک احراز هویت دوم، نام کاربر را با CN اصلی گواهی مقایسه میکند؛ موفقیت هر دو لازم است. username-as-common-name عمداً فعال نیست؛ این گزینه نام مورد استفادهٔ هوکهای بعدی و CCD را عوض میکند و بهخودیخود برابری نامها را کنترل نمیکند.
رنج ثابت این پایلوت 10.93.0.0/24 و آدرس سرور 10.93.0.1 است. با شبکههای کلاینت و سرور تداخل نداشته باشد. مسیر پیشفرض، DNS push، forwarding، NAT و client-to-client در این راهنما فعال نمیشوند. آزمون اول فقط یک سرویس مجاز روی خود سرور VPN را هدف میگیرد.
قدم بعدی
زمان و تداخلهای VM تازه را بررسی کنید
کنسول مستقل و VM تازه با sudo لازم است؛ مراحل ساخت را روی VPN موجود اجرا نکنید. 192.0.2.40 باید روی خود VM باشد و UDP 11940 آزاد باشد. DNS باید ورودی عمومی واقعی را نشان دهد که ممکن است با IP محلی فرق کند. وجود حساب academyvpn، رابط tun-mfa، مسیر /srv/academy-openvpn-mfa، PAM یا واحد academy-mfa باعث توقف است. والدهای مسیر نیز نباید symlink باشند.
همگامسازی زمان باید yes باشد و ساعت گوشی روی حالت خودکار و دقیق قرار داشته باشد. سرویس زمان موجود را با مسئول آن اصلاح کنید؛ daemon دوم نصب نکنید و برای آزمون کد ساعت میزبان را جابهجا نکنید. مجوز UDP پایلوت و دسترسی محدود INPUT روی تونل را با مسئول فایروال هماهنگ کنید؛ هیچ ruleset را پاک یا جایگزین نکنید.
VM مستقل پایلوت؛ ترمینال خصوصی برای ثبت دستگاه
set -eu
cat /etc/os-release
test "$(ps -p 1 -o comm=)" = systemd
ip -br address
ip route show table all
sudo ss -lunp
getent passwd 'academyvpn' && exit 1 || test "$?" = 2
for path in '/srv/academy-openvpn-mfa' '/etc/pam.d/academy-mfa' '/etc/systemd/system/academy-mfa.service'; do
test ! -e "$path" && test ! -L "$path"
done
ip link show 'tun-mfa' >/dev/null 2>&1 && exit 1 || test "$?" = 1
timedatectl status
timedatectl show -p NTPSynchronized --value
تداخل وجود ندارد؛ IP محلی، رنج بدون تداخل، کنسول مستقل و زمان همگام تأیید شدهاند.
قدم بعدی
بستههای امضاشدهٔ نسخهٔ انتخابی را نصب کنید
از بستهٔ توزیع و بهروزرسانی امنیتی آن استفاده کنید، نه اسکریپت نصب یکخطی. Ubuntu 26.10 نسخهٔ پایدار بررسیشده و پیشفرض است و تا ژوئیهٔ ۲۰۲۷ پشتیبانی دارد؛ 26.04 و 24.04 گزینههای LTS هستند. Debian 13 پایدار است و برای Debian 12 پوشش بسته و معماری در LTS را بررسی کنید. تنظیمات برای OpenVPN Community سازگار با 2.6 است؛ Access Server یا تمام GUIها را پوشش نمیدهد.
مسیرهای سیستمعامل مستندند. اجرای واقعی محدود به بستههای Ubuntu 24.04 در محیط ایزوله بوده است؛ نصب و بوت تمام نسخهها ادعا نمیشود.
VM مستقل پایلوت؛ ترمینال خصوصی برای ثبت دستگاه
set -eu
. /etc/os-release
test "$VERSION_CODENAME" = 'bookworm'
sudo apt-get update
apt-cache policy openvpn libpam-google-authenticator python3
sudo apt-get install --no-install-recommends openvpn libpam-google-authenticator python3 openssl ca-certificates nano
openvpn --version | head -n 2
dpkg-query -W openvpn libpam-google-authenticator python3
guard نسخه عبور کرده و نسخهٔ واقعی بستههای امضاشده مشخص است.
قدم بعدی
حساب مستقل بسازید و مسیر واقعی افزونه را پیدا کنید
ساخت محافظتشده، مسیر یا حساب موجود و والد symlink را رد میکند. شکست میانی نیازمند بررسی همان وضعیت است، نه اجرای دوبارهٔ کورکورانه. academyvpn حساب تازهٔ قفلشده با shell غیرقابلورود است؛ مجوز shell و sudo نمیگیرد. مسیر افزونه از فهرست بسته پیدا، به یک فایل واقعی با مالک root و بدون امکان نوشتن عمومی resolve و به آن لینک داده میشود؛ مسیر multiarch حدس زده نمیشود.
پوشهٔ موقت احراز هویت فقط برای UID کاهشیافتهٔ OpenVPN باز است. helper بستهٔ auth-pam با مدل split-process امتیاز لازم را حفظ میکند؛ برای خواندن seed لازم نیست دسترسی nobody را باز کنید. در این نمونه chroot یا کتابخانهٔ نامرتبط اضافه نکنید.
VM مستقل پایلوت؛ ترمینال خصوصی برای ثبت دستگاه
sudo python3 - <<'PY'
import os, pathlib, pwd, subprocess
root=pathlib.Path('/srv/academy-openvpn-mfa')
assert root.is_absolute()
for p in [root, *root.parents]:
assert not p.is_symlink(), 'Symlink path refused'
assert not root.exists(), 'Existing root refused'
for p in ['/etc/pam.d/academy-mfa','/etc/systemd/system/academy-mfa.service']:
assert not os.path.lexists(p), 'Existing service refused'
try:
pwd.getpwnam('academyvpn')
except KeyError:
pass
else:
raise SystemExit('Existing user refused')
root.mkdir(mode=0o755)
os.chmod(root,0o755)
for name,mode in [('pki',0o700),('users',0o755),('auth-tmp',0o700)]:
(root/name).mkdir(mode=mode)
os.chmod(root/name,mode)
subprocess.run(['useradd','--system','--user-group','--home-dir',str(root/'users/academyvpn'),'--create-home','--shell','/usr/sbin/nologin','academyvpn'],check=True)
u=pwd.getpwnam('academyvpn')
os.chmod(root/'users/academyvpn',0o700)
os.chown(root/'users/academyvpn',u.pw_uid,u.pw_gid)
n=pwd.getpwnam('nobody')
os.chown(root/'auth-tmp',n.pw_uid,n.pw_gid)
paths=[p for p in subprocess.check_output(['dpkg','-L','openvpn'],text=True).splitlines() if p.endswith('/openvpn-plugin-auth-pam.so') and pathlib.Path(p).is_file()]
targets={pathlib.Path(p).resolve() for p in paths}
assert len(targets)==1, 'Ambiguous/missing package plugin'
plugin=next(iter(targets))
assert plugin.stat().st_uid==0 and not plugin.stat().st_mode & 0o022
(root/'auth-pam.so').symlink_to(plugin)
print('Dedicated account/tree created; packaged plugin:',plugin)
PY
هویت قفلشده، home خصوصی، پوشهٔ موقت ۰۷۰۰ و لینک افزونهٔ معتبر ساخته شدهاند.
قدم بعدی
دستگاه هر فرد را خصوصی ثبت کنید
این مرحله را فقط در ترمینال خصوصیِ بدون ضبط و با حضور خود کاربر انجام دهید. دستور تعاملی عمداً QR، seed و کد بازیابی را نشان میدهد؛ اجرای آن از این صفحه، هنگام اشتراک صفحه یا در اتوماسیونِ ذخیرهکنندهٔ خروجی مجاز نیست. نبود seed قبلی را کنترل و Google Authenticator یا برنامهٔ سازگار با TOTP را ثبت کنید. حالت زمانی، منع استفادهٔ مجدد، پنجرهٔ کوچک سهکدی و سه تلاش در ۳۰ ثانیه انتخاب شدهاند؛ پس از تطبیق برنامه، ذخیرهٔ فایل را تأیید کنید.
برای ساخت QR از URLای که seed را به سرویس بیرونی میفرستد استفاده نکنید. کدهای بازیابی را آفلاین و جدا از گواهی و گوشی نگه دارید. seed، QR، کد جاری و بازیابی نباید در فرم، JSON، اسکرینشات یا لاگ پشتیبانی باشند. هر هویت seed منحصربهفرد دارد؛ seed مشترک توزیع نکنید.
VM مستقل پایلوت؛ ترمینال خصوصی برای ثبت دستگاه
set -eu
sudo test ! -e '/srv/academy-openvpn-mfa/users/academyvpn/.google_authenticator'
sudo test ! -L '/srv/academy-openvpn-mfa/users/academyvpn/.google_authenticator'
sudo -H -u 'academyvpn' google-authenticator -t -d -w 3 -r 3 -R 30
sudo chmod 0600 '/srv/academy-openvpn-mfa/users/academyvpn/.google_authenticator'
sudo stat -c '%U %a %n' '/srv/academy-openvpn-mfa/users/academyvpn/.google_authenticator'
ثبت خصوصی با seed متعلق به کاربر و مجوز ۰۶۰۰ انجام شده؛ هیچ کدی وارد شواهد عمومی نشده است.