توی این درس یاد میگیری فایروال (firewall) تعیین میکند چه ترافیکی به ماشین برسد و چه ترافیکی رد شود. در Ubuntu ابزار سادهاش ufw (Uncomplicated Firewall) است: سیاست پیشفرض (همهی ورودیها رد، مگر مجاز) را میگذاری، فقط پورتهای لازم را با ufw allow باز میکنی (مثلاً sudo ufw allow OpenSSH)، با sudo ufw enable فعالش میکنی و با sudo ufw status verbose وضعیت را میخوانی؛ sudo ufw deny را هم میشناسی. مهمتر از همه یاد میگیری چطور خودت را از SSH بیرون نیندازی. همهچیز را از یک ماشین دیگر روی یک سرور واقعی آزمایش میکنم. تمرین: فایروال را طوری تنظیم کن که فقط SSH و HTTP باز باشند.
مسئله: هر پورت باز، یک در باز است
Section titled “مسئله: هر پورت باز، یک در باز است”در درس پورتها دیدی هر سرویس روی یک پورت گوش میدهد. ولی سرویسی که فقط برای خودت لازم است (یک پنل مدیریت، دیتابیس، یک برنامهی تستی که فراموش کردهای) اگر روی همهی آدرسها گوش بدهد، برای کل اینترنت باز است. بهمحض اینکه یک سرور آدرس عمومی بگیرد، رباتها شروع میکنند پورتهایش را اسکن و رمزها را حدس بزنند. فایروال میگوید: «از بیرون فقط همین پورتها؛ بقیه رد».
تشبیه: نگهبان درِ ساختمان
Section titled “تشبیه: نگهبان درِ ساختمان”فایروال مثل نگهبان در ورودی ساختمان است. او یک فهرست قاعده دارد که از بالا به پایین میخواند: «واحد ۲۲: بگذر. واحد ۸۰: بگذر. …». هر کسی (بستهای) که برسد، با اولین قاعدهی منطبق تصمیم گرفته میشود. و اگر هیچ قاعدهای نخواندش، قانون پیشفرض: «هر که اسمش در فهرست نیست، رد». اگر نگهبان را بیاوری ولی فراموش کنی اسم خودت را در فهرست بنویسی، فردا صبح هم تو را راه نمیدهد.
مثالهای عملی
Section titled “مثالهای عملی”برای آزمایش واقعی فایروال به دو ماشین نیاز داریم، چون ترافیک خود ماشین به خودش (از lo) از فایروال معاف است. سرور همان ماشین systemdدار آزمایشی من است (با sshd و nginx واقعی و ufw نصبشده) و کلاینت ماشین دیگری است که نقش لپتاپ تو را دارد و از بیرون به سرور وصل میشود. چون این دو ماشین جدا هستند، دستورها را از بیرون با docker exec میزنم؛ برای خوانایی دو تابع تعریف میکنم: server یعنی «این دستور را روی سرور اجرا کن» و client یعنی «روی ماشین دیگر». روی سرور واقعی خودت server را نمینویسی؛ فقط خود دستور (با sudo) را میزنی.
مثال ۱: قبل از فایروال، همهچیز باز است
Section titled “مثال ۱: قبل از فایروال، همهچیز باز است”server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')CIP=$(client hostname -I | awk '{print $1}')echo "آدرس سرور: $SIP | آدرس کلاینت: $CIP"echo "--- فایروال خاموش:"server ufw statusecho "--- از کلاینت: پورت 22 (ssh) و 80 (nginx که روی سرور اجراست)"client nc -zv -w 2 $SIP 22 2>&1client nc -zv -w 2 $SIP 80 2>&1آدرس سرور: 172.17.0.4 | آدرس کلاینت: 172.17.0.2--- فایروال خاموش:Status: inactive--- از کلاینت: پورت 22 (ssh) و 80 (nginx که روی سرور اجراست)Connection to 172.17.0.4 22 port [tcp/ssh] succeeded!Connection to 172.17.0.4 80 port [tcp/http] succeeded!Status: inactive: فایروال خاموش است و هر دو پورت از بیرون باز. (آزمایش را با nc -zv از درس پورتها انجام میدهم: succeeded یعنی باز.)
مثال ۲: ufw را بشناس، سیاست پیشفرض و پروفایل برنامهها
Section titled “مثال ۲: ufw را بشناس، سیاست پیشفرض و پروفایل برنامهها”ufw برای برنامههای رایج پروفایل (profile) آماده دارد: بهجای شمارهی پورت میتوانی اسم برنامه را بگویی:
server() { docker exec lxsys "$@"; }echo "--- پروفایلهای موجود:"server ufw app listecho "--- یک پروفایل چه پورتی را باز میکند؟"server ufw app info OpenSSH--- پروفایلهای موجود:Available applications: Nginx Full Nginx HTTP Nginx HTTPS OpenSSH--- یک پروفایل چه پورتی را باز میکند؟Profile: OpenSSHTitle: Secure shell server, an rshd replacementDescription: OpenSSH is a free implementation of the Secure Shell protocol.
Port: 22/tcpپروفایل OpenSSH یعنی پورت 22/tcp. حالا سیاست پیشفرض را تعیین میکنم که پایهی هر فایروال خوب است: ورودیها رد (deny) و خروجیها مجاز (allow):
server() { docker exec lxsys "$@"; }server ufw default deny incomingserver ufw default allow outgoingDefault incoming policy changed to 'deny'(be sure to update your rules accordingly)Default outgoing policy changed to 'allow'(be sure to update your rules accordingly)هنوز ufw فعال نشده، فقط سیاست ذخیره شد. incoming ترافیکی که به سرور میرسد و outgoing ترافیکی که از سرور بیرون میرود (مثلاً apt update، که اگر ببندیش، سرور برای بهروزرسانی هم راهی ندارد).
مثال ۳: اول SSH را مجاز کن، بعد فایروال را روشن کن
Section titled “مثال ۳: اول SSH را مجاز کن، بعد فایروال را روشن کن”مهمترین قاعدهی این درس: قبل از ufw enable، پورت SSH را باز کن، وگرنه بعد از فعال شدن، دیگر نمیتوانی از راه دور وارد شوی. با ufw allow OpenSSH شروع میکنم و ufw show added قاعدههای اضافهشده را نشان میدهد:
server() { docker exec lxsys "$@"; }server ufw allow OpenSSHecho "--- قاعدههای اضافهشده (هنوز اعمال نشدهاند):"server ufw show addedRules updatedRules updated (v6)--- قاعدههای اضافهشده (هنوز اعمال نشدهاند):Added user rules (see 'ufw status' for running firewall):ufw allow OpenSSHدو خط Rules updated را ببین: یکی برای IPv4 و یکی (v6) برای IPv6؛ ufw هر قاعده را برای هر دو مینویسد. حالا روشنش میکنم. روی سرور واقعی معمولاً از راه دور و با ssh وارد شدهای، پس همین را هم شبیهسازی میکنم: از کلاینت با ssh به سرور وارد میشوم (یک کاربر deploy با دسترسی sudo میسازم) و داخل همان نشست ssh فایروال را فعال میکنم، دقیقاً مثل کار روی یک سرور دور (ترمینال مجازی tmux تا صفحهی واقعی را ببینی):
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')PW=lab-passserver useradd -m -s /bin/bash -G sudo deployserver sh -c "echo deploy:$PW | chpasswd"server touch /home/deploy/.hushloginserver chown deploy:deploy /home/deploy/.hushlogincscreen() { client tmux capture-pane -t c -p | sed -e :a -e '/^[[:space:]]*$/{$d;N;ba' -e '}'; }client tmux -u new-session -d -s c -x 100 -y 20 "bash --norc"; sleep 1client tmux send-keys -t c "ssh -o StrictHostKeyChecking=accept-new deploy@$SIP" Enter; sleep 2client tmux send-keys -t c "$PW" Enter; sleep 3client tmux send-keys -t c "sudo ufw enable" Enter; sleep 2client tmux send-keys -t c "$PW" Enter; sleep 2cscreenbash-5.2$ ssh -o StrictHostKeyChecking=accept-new deploy@172.17.0.4deploy@172.17.0.4's password:deploy@server:~$ sudo ufw enable[sudo] password for deploy:Command may disrupt existing ssh connections. Proceed with operation (y|n)?دو چیز مهم:
ufwوقتی دید از یک نشست ssh صدایش میزنی هشدار داد: «Command may disrupt existing ssh connections، ادامه بدهم؟». این همان خطری است که بعداً میبینی.- رمز دوم، رمز
sudoبود (فقط برایdeploy؛ خودufw enableroot میخواهد).
حالا تأیید میکنم (y) و بلافاصله میبینیم نشست ssh خودمان هنوز زنده است:
client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }cscreen() { client tmux capture-pane -t c -p | sed -e :a -e '/^[[:space:]]*$/{$d;N;ba' -e '}'; }client tmux send-keys -t c "y" Enter; sleep 3client tmux send-keys -t c "clear" Enterclient tmux send-keys -t c "echo نشست هنوز زنده است: \$(whoami)@\$(hostname)" Enter; sleep 1client tmux send-keys -t c "sudo ufw status verbose" Enter; sleep 2cscreendeploy@server:~$ echo نشست هنوز زنده است: $(whoami)@$(hostname)نشست هنوز زنده است: deploy@serverdeploy@server:~$ sudo ufw status verboseStatus: activeLogging: on (low)Default: deny (incoming), allow (outgoing), deny (routed)New profiles: skip
To Action From-- ------ ----22/tcp (OpenSSH) ALLOW IN Anywhere22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)
deploy@server:~$Firewall is active and enabled on system startup: فعال شد و بعد از ریبوت هم فعال میماند. نشست ssh من هم قطع نشد (چرا؟ در «پشت پرده»). و status verbose وضعیت کامل را میدهد:
Default: deny (incoming), allow (outgoing), deny (routed): همان سیاستی که گذاشتم؛routedترافیکی است که فقط از این ماشین عبور میکند (وقتی نقش مسیریاب دارد) و آن هم پیشفرض رد است.Logging: on (low): ثبت لاگ روشن است.- جدول قاعدهها:
22/tcp (OpenSSH)برای IPv4 و IPv6.
مثال ۴: باز کردن یک پورت دیگر، ufw allow
Section titled “مثال ۴: باز کردن یک پورت دیگر، ufw allow”nginx روی سرور بالاست (پورت ۸۰)، ولی فایروال فقط ۲۲ را اجازه داده. از کلاینت امتحان کن، بعد پورت ۸۰ را باز کن:
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')echo "--- پورت 22 (مجاز) و 80 (هنوز مجاز نیست):"client nc -zv -w 2 $SIP 22 2>&1client nc -zv -w 2 $SIP 80 2>&1echo "--- ufw allow 80/tcp:"server ufw allow 80/tcpclient nc -zv -w 2 $SIP 80 2>&1echo "--- جدول شمارهدار قاعدهها:"server ufw status numbered--- پورت 22 (مجاز) و 80 (هنوز مجاز نیست):Connection to 172.17.0.4 22 port [tcp/ssh] succeeded!nc: connect to 172.17.0.4 port 80 (tcp) timed out: Operation now in progress--- ufw allow 80/tcp:Rule addedRule added (v6)Connection to 172.17.0.4 80 port [tcp/http] succeeded!--- جدول شمارهدار قاعدهها:Status: active
To Action From -- ------ ----[ 1] OpenSSH ALLOW IN Anywhere[ 2] 80/tcp ALLOW IN Anywhere[ 3] OpenSSH (v6) ALLOW IN Anywhere (v6)[ 4] 80/tcp (v6) ALLOW IN Anywhere (v6)timed out: پورت ۸۰ با اینکه nginx پشتش زنده بود از بیرون بیصدا دور ریخته میشد (چون سیاست پیشفرض ورودی «رد» است)؛ بعد از ufw allow 80/tcp باز شد. بهجای عدد میشد نوشت sudo ufw allow 'Nginx HTTP' (پروفایل)، یا sudo ufw allow http (اسم سرویس از /etc/services). شکلهای رایج:
| دستور | معنی |
|---|---|
ufw allow 80/tcp |
پورت ۸۰ فقط TCP، از هرجا |
ufw allow 53 |
پورت ۵۳ هم TCP و هم UDP |
ufw allow 6000:6010/tcp |
یک بازه از پورتها |
ufw allow OpenSSH |
با پروفایل برنامه |
ufw allow from 203.0.113.5 |
همهی پورتها فقط از یک IP |
ufw allow from 203.0.113.5 to any port 22 proto tcp |
یک پورت، فقط از یک IP |
ufw allow from 203.0.113.0/24 to any port 5432 |
از یک شبکه |
ufw deny 23/tcp |
رد (بیصدا) |
ufw reject 23/tcp |
رد با پاسخ خطا (مثال ۶) |
ufw delete allow 80/tcp |
پاککردن قاعده |
ufw status numbered |
نمایش با شماره (برای ufw delete N و ufw insert N) |
مثال ۵: ترتیب قاعدهها، اولین منطبق برنده است
Section titled “مثال ۵: ترتیب قاعدهها، اولین منطبق برنده است”قاعدهها از بالا به پایین خوانده میشوند و اولین منطبق حکم میدهد. پس یک deny که بعد از یک allow بنویسی بیاثر است. میخواهم کلاینت (که آدرسش را دارم) از پورت ۸۰ ممنوع شود:
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')CIP=$(client hostname -I | awk '{print $1}')echo "--- deny را بعد از allow میگذارم:"server ufw deny from $CIP to any port 80 proto tcpclient nc -zv -w 2 $SIP 80 2>&1server ufw status numbered | grep -E '80/tcp|^Status'--- deny را بعد از allow میگذارم:Rule addedConnection to 172.17.0.4 80 port [tcp/http] succeeded!Status: active[ 2] 80/tcp ALLOW IN Anywhere[ 3] 80/tcp DENY IN 172.17.0.2[ 5] 80/tcp (v6) ALLOW IN Anywhere (v6)با اینکه قاعدهی DENY برای کلاینت هست، کلاینت هنوز وصل میشود، چون ALLOW 80/tcp (برای همه) بالاتر است و اول منطبق میشود. راهحل: deny را بالا بگذار با ufw insert 1. (اول قاعدهی بیاثر را پاک میکنم، چون ufw قاعدهی تکراری را قبول نمیکند.)
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')CIP=$(client hostname -I | awk '{print $1}')server ufw delete deny from $CIP to any port 80 proto tcpserver ufw insert 1 deny from $CIP to any port 80 proto tcpecho "--- حالا اولین قاعده deny است:"server ufw status numbered | grep -E '80/tcp|^Status'client nc -zv -w 2 $SIP 80 2>&1echo "--- برمیگردانم:"server ufw delete deny from $CIP to any port 80 proto tcpclient nc -zv -w 2 $SIP 80 2>&1Rule deletedRule inserted--- حالا اولین قاعده deny است:Status: active[ 1] 80/tcp DENY IN 172.17.0.2[ 3] 80/tcp ALLOW IN Anywhere[ 5] 80/tcp (v6) ALLOW IN Anywhere (v6)nc: connect to 172.17.0.4 port 80 (tcp) timed out: Operation now in progress--- برمیگردانم:Rule deletedConnection to 172.17.0.4 80 port [tcp/http] succeeded!بعد از insert 1، قاعدهی DENY بالای فهرست رفت و کلاینت timed out شد. این الگوی «استثنا بالا، قاعدهی کلی پایین» در هر فایروالی هست.
مثال ۶: deny در برابر reject
Section titled “مثال ۶: deny در برابر reject”هر دو ترافیک را رد میکنند، ولی شکل رد کردن فرق دارد و روی تجربهی کلاینت اثر میگذارد. یک وبسرور ساده روی پورت ۸۰۹۹ سرور راه میاندازم (که تا الآن هم بسته است چون مجاز نیست) و هر سه حالت را از کلاینت میسنجم:
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')server sh -c 'python3 -m http.server 8099 --bind 0.0.0.0 > /dev/null 2>&1 &'sleep 1echo "--- 1) مجاز (allow):"server ufw allow 8099/tcp > /dev/nullclient nc -zv -w 2 $SIP 8099 2>&1echo "--- 2) deny (بستهها بیصدا دور ریخته میشوند):"server ufw deny 8099/tcp > /dev/nullclient nc -zv -w 2 $SIP 8099 2>&1echo "--- 3) reject (به فرستنده خطا برمیگردد):"server ufw reject 8099/tcp > /dev/nullclient nc -zv -w 2 $SIP 8099 2>&1--- 1) مجاز (allow):Connection to 172.17.0.4 8099 port [tcp/*] succeeded!--- 2) deny (بستهها بیصدا دور ریخته میشوند):nc: connect to 172.17.0.4 port 8099 (tcp) timed out: Operation now in progress--- 3) reject (به فرستنده خطا برمیگردد):nc: connect to 172.17.0.4 port 8099 (tcp) failed: Connection refusedاین همان فرق «timed out» و «Connection refused» درس پورتهاست، این بار از طرف فایروال: deny بیصدا دور میریزد (کلاینت باید تا timeout صبر کند؛ برای فرد مهاجم اسکن را کند میکند و وجود ماشین را کمتر لو میدهد) و reject با یک پاسخ «نه» فوراً خطا میدهد (برای شبکهی داخلی که نمیخواهی کاربران معطل شوند). برای اینترنت عمومی معمولاً deny (پیشفرض) بهتر است. و حالا محدودکردن به یک آدرس (مثلاً «فقط دفتر شرکت»): ابتدا قاعدهی قبلی را عوض میکنم و ۸۰۹۹ را فقط برای یک IP دیگر (که کلاینت من نیست) باز میکنم تا اثرش را ببینیم، بعد برای کلاینت:
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')CIP=$(client hostname -I | awk '{print $1}')server ufw delete reject 8099/tcp > /dev/nullecho "--- فقط از 10.0.0.5 (IP دفتر) مجاز:"server ufw allow from 10.0.0.5 to any port 8099 proto tcp > /dev/nullclient nc -zv -w 2 $SIP 8099 2>&1echo "--- حالا فقط از IP خود کلاینت ($CIP) هم مجاز:"server ufw allow from $CIP to any port 8099 proto tcp > /dev/nullclient nc -zv -w 2 $SIP 8099 2>&1server ufw status | grep -E '8099|^Status'--- فقط از 10.0.0.5 (IP دفتر) مجاز:nc: connect to 172.17.0.4 port 8099 (tcp) timed out: Operation now in progress--- حالا فقط از IP خود کلاینت (172.17.0.2) هم مجاز:Connection to 172.17.0.4 8099 port [tcp/*] succeeded!Status: active8099/tcp ALLOW 10.0.0.58099/tcp ALLOW 172.17.0.2allow from IP to any port N: «از این IP، به هر آدرس این ماشین، پورت N». اول از آدرس دیگری اجازه داده شد و کلاینت من رد شد؛ بعد خودش اضافه شد و رسید. (این همان الگوی «پورت SSH فقط از IP دفتر» است.)
مثال ۷: خطر قفلشدن بیرون از SSH
Section titled “مثال ۷: خطر قفلشدن بیرون از SSH”حالا چیزی که تمام این درس برایش است. فرض کن به هر دلیلی قاعدهی SSH پاک شد (یا از اول نبود). نشست ssh که الآن باز دارم کار میکند، ولی ببین یک اتصال جدید چه میشود:
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')cscreen() { client tmux capture-pane -t c -p | sed -e :a -e '/^[[:space:]]*$/{$d;N;ba' -e '}'; }echo "--- قاعدهی SSH را پاک میکنم (در عمل: فراموش کرده بودم باز کنم):"server ufw delete allow OpenSSHecho "--- نشست ssh باز قبلی هنوز جواب میدهد؟"client tmux send-keys -t c "clear" Enterclient tmux send-keys -t c "echo هنوز وصلم: \$(date +%T)" Enter; sleep 1cscreenecho "--- ولی یک اتصال جدید به پورت 22:"client nc -zv -w 3 $SIP 22 2>&1client ssh -o ConnectTimeout=3 -o BatchMode=yes deploy@$SIP true 2>&1--- قاعدهی SSH را پاک میکنم (در عمل: فراموش کرده بودم باز کنم):Rule deletedRule deleted (v6)--- نشست ssh باز قبلی هنوز جواب میدهد؟deploy@server:~$ echo هنوز وصلم: $(date +%T)هنوز وصلم: 07:19:31deploy@server:~$--- ولی یک اتصال جدید به پورت 22:nc: connect to 172.17.0.4 port 22 (tcp) timed out: Operation now in progressssh: connect to host 172.17.0.4 port 22: Connection timed outنشست قبلی کار کرد (چرا؟ در «پشت پرده») ولی اتصال جدید timed out شد: ssh به ماشین راه ندارد. اگر آن نشست را ببندم، دیگر نمیتوانم وارد شوم و باید به کنسول سرور (پنل ارائهدهندهی VPS) بروم. به همین دلیل:
- قبل از
ufw enableهمیشهufw allow OpenSSH(یا22/tcp) را بزن و باufw show addedببین. - هر تغییر فایروال را با نشست دوم آزمایش کن و نشست اول را باز نگه دار.
- اگر SSH را روی پورت دیگری بردهای، آن پورت را باز کن.
حالا از «کنسول» (همان server) درستش میکنم و ورود را دوباره آزمایش میکنم:
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')echo "--- از کنسول: ufw allow OpenSSH"server ufw allow OpenSSHclient nc -zv -w 3 $SIP 22 2>&1--- از کنسول: ufw allow OpenSSHRule addedRule added (v6)Connection to 172.17.0.4 22 port [tcp/ssh] succeeded!قبل از sudo ufw enable روی یک سرور دور، مهمترین کار چیست؟
سیاست پیشفرض ufw «رد کردن ورودیها» است. بدون قاعدهی SSH، اتصالهای جدید ssh رد میشوند و از راه دور راهی برای درست کردن باقی نمیماند.
پشت پرده: ufw چطور کار میکند؟
Section titled “پشت پرده: ufw چطور کار میکند؟”ufw یک پوشش ساده روی iptables (فایروال خود هستهی لینوکس) است: قاعدههایی را که مینویسی به قاعدههای iptables ترجمه و در زنجیرههایی مثل ufw-user-input میگذارد. ببین:
server() { docker exec lxsys "$@"; }echo "--- ufw show added (آنچه تو نوشتهای):"server ufw show addedecho "--- زنجیرهی iptables که ufw برایت ساخته (ufw-user-input):"server iptables -S ufw-user-inputecho "--- چرا نشست ssh باز بعد از فعالشدن قطع نشد؟ قاعدهی conntrack در before.rules:"server grep -n 'ctstate RELATED,ESTABLISHED' /etc/ufw/before.rules | head -2echo "--- فایلهای تنظیمات:"server sh -c "ls /etc/ufw | grep -v '[0-9]\{8\}_'"--- ufw show added (آنچه تو نوشتهای):Added user rules (see 'ufw status' for running firewall):ufw allow 80/tcpufw allow from 10.0.0.5 to any port 8099 proto tcpufw allow from 172.17.0.2 to any port 8099 proto tcpufw allow OpenSSH--- زنجیرهی iptables که ufw برایت ساخته (ufw-user-input):-N ufw-user-input-A ufw-user-input -p tcp -m tcp --dport 80 -j ACCEPT-A ufw-user-input -s 10.0.0.5/32 -p tcp -m tcp --dport 8099 -j ACCEPT-A ufw-user-input -s 172.17.0.2/32 -p tcp -m tcp --dport 8099 -j ACCEPT-A ufw-user-input -p tcp -m tcp --dport 22 -m comment --comment "\'dapp_OpenSSH\'" -j ACCEPT--- چرا نشست ssh باز بعد از فعالشدن قطع نشد؟ قاعدهی conntrack در before.rules:25:-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT26:-A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT--- فایلهای تنظیمات:after.initafter.rulesafter6.rulesapplications.dbefore.initbefore.rulesbefore6.rulessysctl.confufw.confuser.rulesuser6.rulesنکتهها:
- هر
ufw allowیک خط-A ufw-user-input ... -j ACCEPTشد؛ یکی برای هر پورت. ترتیب خطها همان ترتیب قاعدههاست (اولین منطبق برنده). - بالای همهی قاعدههای تو،
before.rulesترافیکESTABLISHED,RELATEDرا میپذیرد: یعنی اتصالهایی که از قبل برقرار بودند (مثل نشست ssh باز) عبور میکنند و فقط اتصالهای جدید با قاعدهها سنجیده میشوند. برای همین فایروال نشست باز را قطع نکرد ولی اتصال تازه را رد کرد. - قاعدههای تو در
/etc/ufw/user.rulesوuser6.rules(برای IPv6)، و تنظیمات عمومی (مثلIPV6=yes) در/etc/default/ufwاست. ufwفقط یک ابزار ورودی/خروجی ماشین خودت است؛ بعضی برنامهها (مثل Docker) خودشان قاعدهیiptablesمینویسند که ممکن است ازufwرد شود؛ در درسهای Docker روی این جزئیات مفصل نیستم و برای اطمینان همیشه از بیرون پورتها را باnc -zvآزمایش کن.
جدولهای مرجع
Section titled “جدولهای مرجع”| دستور | کار |
|---|---|
sudo ufw status verbose |
وضعیت، سیاستها و قاعدهها |
sudo ufw status numbered |
قاعدهها با شماره |
sudo ufw default deny incoming / allow outgoing |
سیاست پیشفرض |
sudo ufw allow OpenSSH / 22/tcp |
باز کردن SSH |
sudo ufw allow 80/tcp |
باز کردن یک پورت |
sudo ufw allow from IP to any port N proto tcp |
یک پورت فقط از یک IP |
sudo ufw deny ... / sudo ufw reject ... |
رد بیصدا / رد با خطا |
sudo ufw insert 1 deny ... |
افزودن قاعده در بالای فهرست |
sudo ufw delete allow 80/tcp |
حذف با شرح قاعده |
sudo ufw show added |
قاعدههای نوشتهشده |
sudo ufw app list / app info NAME |
پروفایل برنامهها |
sudo ufw enable / disable |
روشن/خاموش (پایدار بعد از ریبوت) |
sudo ufw reset |
پاککردن همهچیز (و خاموشکردن) |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) روشنکردن فایروال بدون مجازکردن SSH
Section titled “۱) روشنکردن فایروال بدون مجازکردن SSH”مثال ۷. راهحل: ufw allow OpenSSH قبل از ufw enable، و نشست دوم برای آزمایش. اگر قفل شدی: کنسول ارائهدهندهی سرور.
۲) نام پروفایل اشتباه
Section titled “۲) نام پروفایل اشتباه”docker exec lxsys ufw allow ssh2ERROR: Could not find a profile matching 'ssh2'ERROR: Could not find a profile matching 'ssh2': ufw app list را ببین (اسمها حساس به حروفاند: OpenSSH، Nginx HTTP با فاصله و داخل گیومه). راهحل: اسم دقیق یا شمارهی پورت (22/tcp).
۳) deny بعد از allow (ترتیب)
Section titled “۳) deny بعد از allow (ترتیب)”مثال ۵: قاعدهی اول برنده است. راهحل: ufw insert 1 deny ... برای استثناها.
۴) فراموشکردن پروتکل: allow 7000 هم TCP هم UDP
Section titled “۴) فراموشکردن پروتکل: allow 7000 هم TCP هم UDP”docker exec lxsys sh -c 'ufw allow 7000 > /dev/null; ufw allow 7001/tcp > /dev/null; ufw status | grep -E "7000|7001" | grep -v v6; ufw delete allow 7000 > /dev/null; ufw delete allow 7001/tcp > /dev/null'7000 ALLOW Anywhere7001/tcp ALLOW Anywhereبدون /tcp، قاعده برای هر دو پروتکل است (بازتر از لازم). راهحل: همیشه پروتکل را بنویس، مگر واقعاً هر دو را بخواهی (مثل DNS روی ۵۳).
۵) پورت باز، سرویس بسته
Section titled “۵) پورت باز، سرویس بسته”فایروال را درست باز کردهای ولی Connection refused میگیری: یعنی فایروال مشکلی ندارد و هیچ برنامهای گوش نمیدهد (درس پورتها: ss -tlnp). فایروال فقط میگوید ترافیک اجازه دارد برسد، نه اینکه کسی آنجاست.
۶) فایروال را ابزار «امنیت کامل» دانستن
Section titled “۶) فایروال را ابزار «امنیت کامل» دانستن”فایروال راه ورود را میبندد، ولی رمز ضعیف SSH، نرمافزار بیبهروزرسانی یا سرویس آسیبپذیر روی پورت باز را درست نمیکند. لایههای دیگر (کلید SSH، fail2ban، بهروزرسانی خودکار) در پروژهی پایانی میآیند.
وضعیت فعلی ufw را بخوان و بگو سیاست پیشفرض ورودی و خروجی چیست و چند قاعده دارد.
دیدن جواب
docker exec lxsys ufw status verboseStatus: activeLogging: on (low)Default: deny (incoming), allow (outgoing), deny (routed)New profiles: skip
To Action From-- ------ ----80/tcp ALLOW IN Anywhere8099/tcp ALLOW IN 10.0.0.58099/tcp ALLOW IN 172.17.0.222/tcp (OpenSSH) ALLOW IN Anywhere80/tcp (v6) ALLOW IN Anywhere (v6)22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)اگر Status: active و Default: deny (incoming), allow (outgoing) است، یعنی ورودیها رد و خروجیها مجازند؛ جدول پایین قاعدههاست (هر قاعده یک خط برای IPv4 و یک خط (v6)).
تمرین اصلی: فایروال را طوری تنظیم کن که فقط SSH و HTTP باز باشند. از صفر (reset) شروع کن، سیاستها را بگذار، SSH و ۸۰ را مجاز کن، فعال کن، و از کلاینت ثابت کن ۲۲ و ۸۰ باز و ۸۰۹۹ (وبسرور آزمایشی) بسته است.
دیدن جواب
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')server ufw --force reset > /dev/nullserver ufw default deny incomingserver ufw default allow outgoingserver ufw allow OpenSSHserver ufw allow 80/tcpserver ufw --force enableserver ufw status verboseecho "--- آزمایش از کلاینت:"client nc -zv -w 2 $SIP 22 2>&1client nc -zv -w 2 $SIP 80 2>&1client nc -zv -w 2 $SIP 8099 2>&1Default incoming policy changed to 'deny'(be sure to update your rules accordingly)Default outgoing policy changed to 'allow'(be sure to update your rules accordingly)Rules updatedRules updated (v6)Rules updatedRules updated (v6)Firewall is active and enabled on system startupStatus: activeLogging: on (low)Default: deny (incoming), allow (outgoing), deny (routed)New profiles: skip
To Action From-- ------ ----22/tcp (OpenSSH) ALLOW IN Anywhere80/tcp ALLOW IN Anywhere22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)80/tcp (v6) ALLOW IN Anywhere (v6)
--- آزمایش از کلاینت:Connection to 172.17.0.4 22 port [tcp/ssh] succeeded!Connection to 172.17.0.4 80 port [tcp/http] succeeded!nc: connect to 172.17.0.4 port 8099 (tcp) timed out: Operation now in progressSSH را فقط از IP کلاینت مجاز کن (نه همهجا)، HTTP را برای همه، و با یک deny صریح 23/tcp (telnet) را ببند. ثابت کن کلاینت به ۲۲ و ۸۰ میرسد و سپس SSH را برای یک IP دیگر (10.0.0.5) بگذار تا ببینی کلاینت رد میشود. در پایان فایروال را reset کن.
دیدن جواب
server() { docker exec lxsys "$@"; }client() { docker exec -u ali -e TERM=xterm lxlab "$@"; }SIP=$(server hostname -I | awk '{print $1}')CIP=$(client hostname -I | awk '{print $1}')server ufw --force reset > /dev/nullserver ufw default deny incoming > /dev/nullserver ufw allow from $CIP to any port 22 proto tcp > /dev/nullserver ufw allow 80/tcp > /dev/nullserver ufw deny 23/tcp > /dev/nullserver ufw --force enable > /dev/nullserver ufw status | grep -vE 'v6|^$'echo "--- کلاینت مجاز:"client nc -zv -w 2 $SIP 22 2>&1client nc -zv -w 2 $SIP 80 2>&1echo "--- حالا SSH فقط برای 10.0.0.5:"server ufw delete allow from $CIP to any port 22 proto tcp > /dev/nullserver ufw allow from 10.0.0.5 to any port 22 proto tcp > /dev/nullclient nc -zv -w 2 $SIP 22 2>&1server ufw --force reset > /dev/nullStatus: activeTo Action From-- ------ ----22/tcp ALLOW 172.17.0.280/tcp ALLOW Anywhere23/tcp DENY Anywhere--- کلاینت مجاز:Connection to 172.17.0.4 22 port [tcp/ssh] succeeded!Connection to 172.17.0.4 80 port [tcp/http] succeeded!--- حالا SSH فقط برای 10.0.0.5:nc: connect to 172.17.0.4 port 22 (tcp) timed out: Operation now in progressآزمونک
Section titled “آزمونک”سیاست پیشفرض ufw برای ترافیک ورودی چیست و چرا برای امنیت خوب است؟
با «رد مگر مجاز» هر سرویس فراموششدهای خودکار از بیرون بسته میماند.
قبل از sudo ufw enable روی یک سرور دور چه باید کرد؟
بدون آن اتصالهای جدید ssh رد میشوند و از راه دور راه برگشتی نمیماند.
اگر allow 80/tcp بالاتر از deny from IP to any port 80 باشد، آن IP چه میشود؟
برای استثنا از ufw insert 1 deny ... استفاده کن.
فرق deny و reject؟
برای اینترنت عمومی معمولاً deny؛ برای شبکهی داخلی گاهی reject.
ufw allow 53 چه میکند؟
بدون /tcp یا /udp هر دو پروتکل؛ برای باز کردن حداقل لازم پروتکل را بنویس.
چرا بعد از فعالشدن فایروال نشست ssh باز قطع نشد ولی اتصال جدید رد شد؟
به همین دلیل خطرِ قفلشدن وقتی نشست قدیمی را ببندی ظاهر میشود.
جمعبندی
Section titled “جمعبندی”- فایروال ترافیک ورودی (و خروجی) را با قاعدهها کنترل میکند؛
ufwپوششی ساده رویiptablesاست. سیاست پایه:deny incoming،allow outgoing. - ترتیب کار:
ufw default deny incoming←ufw allow OpenSSH← سایر پورتهای لازم (ufw allow 80/tcp) ←ufw enable←ufw status verbose. - قاعدهها از بالا به پایین خوانده میشوند و اولین منطبق برنده است (
ufw insert 1برای استثنا).denyبیصدا دور میریزد (timeout)،rejectخطا برمیگرداند (refused). - بدون پروتکل، قاعده برای TCP و UDP هر دو است؛
from IP to any port Nبرای محدود کردن به یک آدرس. - خطر قفلشدن: بدون قاعدهی SSH اتصالهای جدید رد میشوند (نشست باز تا بستهشدنش زنده میماند). همیشه نشست دوم برای آزمایش و راه کنسول را داشته باش.
- با
nc -zvاز ماشین دیگر آزمایش کن؛ آزمایش از خود سرور از فایروال معاف است.
| دستور | کاری که میکند |
|---|---|
sudo ufw status verbose / status numbered | وضعیت / با شماره |
sudo ufw default deny incoming | ورودیها رد، مگر مجاز |
sudo ufw default allow outgoing | خروجیها آزاد |
sudo ufw allow OpenSSH | اول این! |
sudo ufw allow 80/tcp / sudo ufw allow "Nginx HTTP" | باز کردن وب |
sudo ufw allow from IP to any port 22 proto tcp | SSH فقط از یک IP |
sudo ufw deny 23/tcp / sudo ufw reject 23/tcp | رد بیصدا / رد با خطا |
sudo ufw insert 1 deny from IP | استثنا در بالای فهرست |
sudo ufw delete allow 80/tcp | حذف قاعده |
sudo ufw show added | قاعدههای نوشتهشده |
sudo ufw enable / sudo ufw disable / sudo ufw reset | روشن / خاموش / پاککردن همه |
nc -zv -w 2 SERVER 80 | آزمایش از بیرون |