آشنایی با برنامه PF
برنامه PF به صورت پیش فرض فعال است. برای غیرفعال کردن آن باید خط pf=NO را به فایل /etc/rc.conf.local اضافه کنید و سیستم را دوباره راه اندازی کنید تا این خط تاثیر خودش را بر روی سیستم اعمال کند. همچین می توانید به صورت دستی PF را راهاندازی کنید. برای این منظور باید از برنامه pfctl استفاده کنید. لازم به ذکر است که با این فرمان شما توان غیرفعال کردن این برنامه را دارید که این امر در زیر نمایش داده شده:
# pfctl -e
# pfctl -d
این فرمانها به ترتیب برنامه pf را فعال و غیرفعال می کنند. به این نکته توجه داشته باشید که با این فرمانها فقط شما به فعال سازی و غیر فعال کردن پرداختید و برای راه اندازی کامل باید به اصطلاح ruleset را بارگذاری کنید.
پیکربندی:
فایل پیکربندی برنامه pf در مسیر /etc/pf.conf قرار دارد و در زمان راه اندازی سیستم به وسیله فرمان های rc بارگذاری می شود. فرمت این فایل به صورت متنی بوده و توسط برنامه pfctl تفسیر می شود.
فایل pf.conf به ۵ بخش تقسیم می شود. به شرح زیر:
بخش macros: در این بخش متغیرهای برنامه تعریف می شود.
بخش tables: در این بخش لیستی از آدرس های IP ذخیره می شود.
بخش options: در این بخش تنظیماتی قرار می گیرد که به رفتار برنامه pf نظارت دارد.
بخش Queueing: این بخش برای کنترل پهنای باند و اولویت در ارسال بسته به کار می رود.
بخش Filter Rules: این بخش به شما امکان مدیریت بستهها و برای رد کردن یا عبور دادن بسته ها به کار می رود. در این بخش شما همچنان می توانید حالات NAT و packet redirection را فعال کنید.
کنترل کردن برنامه:
pf با استفاده از برنامه pfctl مدیریت می شود. در زیر مثال های در این زمینه را مشاهده می کنید:
# pfctl -f /etc/pf.conf Load the pf.conf file
# pfctl -nf /etc/pf.conf Parse the file, but don’t load it
# pfctl -sr Show the current ruleset
# pfctl -ss Show the current state table
# pfctl -si Show filter stats and counters
# pfctl -sa Show EVERYTHING it can show
خط اول، فایل pf.conf را بارگذاری می کند. خط دوم، فایل مربوطه را تجزیه و تحلیل می کند ولی بارگذاری نمی کند. خط سوم ruleset فعلی را نمایش می دهد. خط چهارم وضعیت table را نمایش می دهد. خط پنج وضعیت filter و شمارنده آن را نمایش می دهد و در خط پایانی همه وضعیت های موجود را نمایش می دهد.
بخش مربوط به list:
شما با استفاده از این بخش می توانید از دوباره نویسی یک سری اطلاعات تکراری در بخش های مربوط به rules ها جلوگیری کنید. به جای نوشتن یک رول برای هر آدرس ip، میتوانید این بخش را در لیست قرار دهید تا هر زمان که به آن نیاز داشتید به آن مراجعه کنید. لیست ها با براکت مشخص می شوند. زمانی که برنامه pfctl به بخشی که در آن از list استفاده شده برای بارگذاری می رسد این برنامه برای هر بخش خودش به صورت اتوماتیک rule جداگانه ایجاد می کند. در زیر مثالی را در این زمینه مشاهده می کنید:
block out on fxp0 from { 192.168.0.1, 10.5.32.6 } to any
این خط به صورت زیر تفسیر می شود:
block out on fxp0 from 192.168.0.1 to any
block out on fxp0 from 10.5.32.6 to any
شما می توانید از چند لیست در یک rule استفاده کنید این امر در زیر نمایش داده شده:
match in on fxp0 proto tcp to port { 22 80 } rdr-to 192.168.0.6
block out on fxp0 proto { tcp udp } from { 192.168.0.1, \
۱۰٫۵٫۳۲٫۶ } to any port { ssh telnet }
البته لیست ها می توانند تو در تو هم باشند همانند مثال زیر:
trusted = “{ 192.168.1.2 192.168.5.36 }”
pass in inet proto tcp from { 10.10.0.0/24 $trusted } to port 22
شما در لیست می توانید از حالت نفی هم استفاده کنید. این امر در مثال زیر نمایش داده شده:
pass in on fxp0 from { 10.0.0.0/8, !10.1.2.3 }
فرمان بالا با استفاده از لیست می تواند به صورت زیر تفسیر شود:
pass in on fxp0 from 10.0.0.0/8
pass in on fxp0 from !10.1.2.3
این فرمان یعنی همه آدرس های ای پی شبکه ۱۰٫۰٫۰٫۰/۸ را رد کن به جز ۱۰٫۱٫۲٫۳٫
بخش macro:
در بخش macro می توانید متغیر تعریف کنید. از آدرس IP گرفته تا port numbers و یا حتی interface name. این بخش می تواند از پیچیدگی های رول نویسی در pf کم کند. تنها قاعده این بخش این است که نام macro باید با یک حرف شروع شود ولی باید به این نکته توجه کنید که از نام های رزرو شده مثل pass out queue نمی توانید استفاده کنید. در زیر یک مثال از این بخش نمایش داده شده:
ext_if = “fxp0”
block in on $ext_if from any to any
در این مثال یک macro به نام ext_if ایجاد شده و همان طور که در خط بعد مشاهده می کنید برای نمایش این متغییر در رول باید از کاراکتر $ استفاده کنید.
یک macro می تواند به صورت زیر گسترش پیدا کند این امر در زیر نماش داده شده:
friends = “{ 192.168.1.1, 10.0.2.5, 192.168.43.53 }”
از macro می توانید به صورت معکوس هم استفاده کنید این بخش برعکس خط بالا عمل می کند:
host1 = “192.168.1.1”
host2 = “192.168.1.2”
all_hosts = “{” $host1 $host2 “}”
حال شما با استفاده از متغیر all_hosts کار خود را در رول نویسی راحت کنید. حال شما می توانید بدون تغییر دادن فقط این متغیرها را تغییر دهید تا امکان خطا هم کم شود.
بخش Tables:
از tables برای ذخیره سازی یک گروه از آدرس های IP استفاده می شود. این کار در سرعت برنامه و میزان استفاده از حافظه RAN تاثیر زیادی دارد. tables در راهکارهای زیر میتوانند مورد استفاده قرار گیرند:
مبداء و مقصد آدرس ها در رول ها
برای استفاده از قابلیت های nat و redirection که در ادامه به صورت کامل توضیح داده می شود.
جدولها در فایل pf.conf و با استفاده از فرمان pfctl ایجاد می شوند.
برای ایجاد جدول در فایل pf.conf باید از کلمه کلیدی table استفاده کنید. برای هر جدول خواص زیر مورد استفاده می شود:
Const این خاصیت اگر به یک جدول اضافه شود، دیگر نمی توان در آن جدول تغییر ایجاد کرد. زمانی که از این قابلیت استفاده نشود شما می توانید با استفاده از برنامه pfctl به جدول آدرس اضافه و کم کنید حتی اگر شما سطح امنیت سیستم خود را در level 7 قرار دهید.
Persist این قابلیت باعث می شود که kernel جدول را در حافظه قرار بدهد حتی اگر هیچ رولی به آن اشاره نکند.
با هم مثال هایی در این زمینه مشاهده می کنید:
table { 192.0.2.0/24 }
table const { 192.168.0.0/16, 172.16.0.0/12, \
۱۰٫۰٫۰٫۰/۸ }
table persist
block in on fxp0 from { , } to any
pass in on fxp0 from to any
شما می توانید از نقیض آدرس دهی در این بخش به صورت زیر استفاده کنید:
table { 192.0.2.0/24, !192.0.2.5 }
جدول goodguys شامل همه آدرس های شبکه ۱۹۲٫۰٫۲٫۰/۲۴ به استثنای آدرس ۱۹۲٫۰٫۲٫۵٫ به این نکته توجه کنید که نام جدول در علامت های < > محصور می شود. همچنین شما می توانید لیست آدرس ها را در یک فایل متنی قرار دهید و با استفاده از فرمان زیر آنها را فراخوانی کنید:
table persist file “/etc/spammers”
block in on fxp0 from to any
فایل /etc/spammers می تواند شامل لیستی از آدرس های IP باشد که حتی می تواند به صورت CIDR هم نوشته شود. توضیحات با کاراکتر # مشخص میشوند.
مدیریت جدولها با استفاده از فرمان pfctl:
شما می توانید با استفاده از فرمان pfctl به مدیریت جداول بپردازید. برای مثال شما می توانید به جدول مثال قبلی آدرس جدیدی اضافه کنید:
# pfctl -t spammers -T add 218.70.0.0/16
برای مشاهده کردن آدرسهای موجود در جدول از فرمان زیر استفاده کنید:
# pfctl -t spammers -T show
برای پاک کردن آدرس ها از یک جدول از فرمان زیر استفاده کنید:
# pfctl -t spammers -T delete 218.70.0.0/16
مدیریت بسته ها:
فیلتر کردن بسته ها یا همان Packet filtering یک حالت انتخابی از رد کردن یا مسدود کردن بسته هایی است که از طریق کارت شبکه شما ارسال می شود، برنامه pf هم بسته هایی که در لایه سه شبکه که مربوط به آدرس های ورژن ۴ و ۶ می شوند و هم در لایه چهار یعنی پروتکل های TCP UDP ICMP می شوند را مدیریت می کند. در هر دوی این بخش ها هم به مبدا و مقصد سرویس ها و آدرس ها توجه می کند. برنامه فیلتر دو هدف یعنی رد کردن و بلوکه کردن را انجام می دهد البته این امر بسته به رولهایی که به کارت شبکه سیستم شما ارسال می شود و در فایل پیکربندی در ارتباط با این نوع از بسته ها رول هایی وجود دارد. برنامه pf همه رول ها را از ابتدا تا به انتها چک می کند و رولی که در پایان با بسته مچ شود برنده می شود مگر شما در یک رول بالاتر از کلمه quick استفاده کنید که در این صورت همان رول به بسته اعمال می شود در پایان این بحث برروی یک مثال کار خواهیم کرد. در سیستم های باز که قصد دارند همه بسته ها از بین نرود در ابتدای رول ها از رول pass all استفاده می کنند و در سیستم هایی که سخت گیرانه طراحی می شود از رول block all استفاده می کنند. در ادامه با قواعد رول نویسی آشنا می شویم تا بتوانیم به درک بهتری از مثال ها برسیم.
قواعد رول نویسی:
به طور کلی نحوه نوشتن قاعد در pf بسیار ساده است. در زیر یک مثال از این بخش را مشاهده می کنید:
action [direction] [log] [quick] [on interface] [af] [proto protocol] \
[from src_addr [port src_port]] [to dst_addr [port dst_port]] \
بخش action:
این بخش دو حالت بیشتر ندارد pass یا block این بخش شروع یک رول است که تصمیم شما را برای رد یا بلوکه کردن بسته مشخص میکند.
بخش direction:
در این بخش شما جهت حرکت بسته به سمت کارت شبکه سیستم خود را تعیین می کنید که دو حالت in و out دارد. بخش in مربوط به بسته هایی می شود که به سیستم شما ارسال می شوند و یا قصد استفاده از سرویس های سیستم شما را دارند. out هم مربوط به بسته هایی می شود که قصد دارند از سیستم شما یا شبکه که سیستم شما نقش GATWAY را دارند عبور کنند و به شبکه های دیگر برسند.
بخش LOG:
این بخش به راه اندازی برنامه pflogd می پردازد. با این برنامه همه بسته هایی که با این رول مچ می شوند log گیری می شوند. دربخشی دیگر کار با این برنامه توضیح داده خواهد شد. برای گزارش گیری کامل از log all باید استفاده کنید.
بخش quick:
همان طور که به این بخش در قسمت های قبلی اشاره شده این بخش تقدم رول را بالا می برد و با مچ شدن بسته با این رول دیگر برنامه pf به سراغ سایر رول ها نمی رود.
بخش interface:
در این بخش شما می توانید نام کارت شبکه خود را مورد استفاده قرار دهید. برای پیدا کردن نام کارت های شبکه می توانید از فرمان ifconfig استفاده کنید.
بخش af:
برنامه pf از هردو ورژن آدرس های شبکه پشتیبانی می کند. هم ورژن ۴ و هم ورژن ۶٫ برای ورژن ۴ از عبارت inet و برای ورژن ۶ از عبارت inet6 استفاده کنید، البته این بخش به صورت خودکار توسط برنامه مشخص می شود.
بخش protocol:
در این بخش شما می توانید سرویس های لایه ۴ شبکه را مشخص کنید این سرویس ها شامل TCP UDP ICMP و … می شوند که میتوانید با استفاده از فایل /etc/protocols نام همه سرویس ها را پیدا کنید. البته می توانید با استفاده از لیست که در بخش های قبلی توضیح داده شد، این پروتکل ها را طبقه بندی کنید.
بخش src_addr, dst_addr:
شما در این بخش می توانید مبدا و یا مقصد آدرس ها را تعیین کنید. این بخش می تواند یک آدرس، گروهی از آدرس ها و یا حتی نام کامل DNS یک آدرس را شامل شود. البته برای نوشتن گروهی از آدرس ها باید از روش CIDR استفاده کنید.
بخش src_port, dst_port:
شما می توانید در این بخش شماره پورت های مبدا و مقصد را تعیین کنید این عدد بین ۱ تا ۶۵۵۳۵ متغیر اس. شما می توانید با کمک گرفتن از فایل /etc/services با اسم سرویس ها آشنا شوید. شما می توانید با استفاده از بخش list تعداد دلخواه از این پورت ها را به صورت انتخابی مورد استفاده قرار دهید. برای استفاده از این بخش قواعدی وجود دارد:
- != یعنی مساوی نیست
- < کوچکتر از. * > بزرگتر از
- <= کوچکتر مساوی. * >= بزرگتر مساوی
- >< یک رنج
- <> معکوس یک رنج
رول نویسی شامل بخش های دیگری هست که در ادامه با آن بیشتر آشنا می شوید. هدف از firewall مسدود کردن ترافیک های ناخواسته است که به آن به اصطلاح حالت Default Deny می گویند و برای فعال کردن این بخش از رول های زیر استفاده کنید:
block in all
block out all
حال بعد از مسدود کردن ترافیک ها نیاز به عبور دادن ترافیک های سالم از طریق firewall خود دارید برای مثال به مبدا و مقصد یک سری از آدرس های خاص اجازه تردد دهید و یا یک سرویس بر روی یک پورت خاص را برای گروهی قابل دسترسی کنید. برای انجام این کارها باید از قاعده pass استفاده کنید. در زیر یک مثال در این زمینه را مشاهده می کنید:
# Pass traffic in on dc0 from the local network, 192.168.0.0/24,
# to the OpenBSD machine’s IP address 192.168.0.1. Also, pass the
# return traffic out on dc0.
pass in on dc0 from 192.168.0.0/24 to 192.168.0.1
pass out on dc0 from 192.168.0.1 to 192.168.0.0/24
# Pass TCP traffic in on fxp0 to the web server running on the
# OpenBSD machine. The interface name, fxp0, is used as the
# destination address so that packets will only match this rule if
# they’re destined for the OpenBSD machine.
pass in on fxp0 proto tcp from any to fxp0 port www
در ادامه با هم یک مثال در مورد استفاده از بخش quick می پردازیم:
در این مثال فرض کنید یک سرور دارید در یک شبکه ۳ کامپیوتری که قصد دارید در ابتدا سرویس icpm سرور خود را مسدود کنید، برای این منظور بخش زیر را در فایل /etc/pf.conf اضافه کنید:
Block in proto icmp from any to any
حال شما قصد دارید سرویس ICMP را برای شبکه باز کنید برای این کار از فرمان زیر استفاده کنید:
Pass in proto icmp from 192.168.126.0/24 to 192.168.126.1
در این بخش آدرس سرور ۱۹۲٫۱۶۸٫۱۲۶٫۱ است و سرویس برای شبکه ۱۹۲٫۱۶۸٫۱۲۶٫۰ باز شده است. حال در این بخش قصد داریم یکی از دستگاه ها را مسدود کنیم برای این منظور از فرمان زیر استفاده می کنیم:
Block in proto icmp from 192.168.126.10 to 192.168.126.1
حال اگر در رول قبلی از quick استفاده کنید این رول دیگر دیده نمی شود و همه می توانند در شبکه سرور را به اصطلاح ping کنند.
فقط یک نکته باقی ماند آن هم این که برای بارگذاری فایل تغییر داده شده pf.conf باید از فرمان pfctl به صورت زیر استفاده کنید و اگر پیغام خطایی دریافت نکردید فایل شما به صورت کامل بارگذاری شده است:
#pfctl -f /etc/pf.con