さくらのクラウドのIP制限はどこでやる?PFとOS・アプリ(ミドルウェア)で比較

ネットワーク # firewalld # パケットフィルタ

皆さんこんにちは!新人のアナサピです!
今回はPFとfirewalldとApacheでIP制御の比較を行いました!
皆さんは「IP制限」と聞くと、何を思い浮かべますか?

  1. さくらのクラウドのパケットフィルタ(PF)
  2. OS(firewalld)
  3. アプリ(Apache)
  4. WAF

等々、色々な方法があります。
ただ、どれも「アクセスを拒否する」という点では同じように見える一方で、「どこで通信を止めているのか」はかなり違います。
実際、制御する場所によって、サーバ負荷やログの残り方、防げる内容まで変わってきます。

今回は、「結局どこでIP制限をするのが良いのか?」をイメージしやすくするために、今回は実際 AlmaLinux + Apache + PHP の環境をさくらのクラウドで用意し、

  • どこで通信が止まるのか
  • CPU負荷はどう変わるのか
  • ログは残るのか

を実際に検証してみました。

結論としては、「どれが正解」というより、“何をどこまで到達させたくないか”などの目的によって使い分けるものだと感じました。
今回はその違いを、実際の負荷テスト結果と一緒に整理していきます。

「そもそもパケットフィルタってなに?」

弊社の社員が分かり易い記事を公開しておりますので、こちらの記事をご覧ください!

今回のポイント

単純な「制限できる / 制限できない」だけでは、今回の3つどれでも制御できますので、比較にはなりません。
そのため今回は、実際に負荷をかけながら、サーバ内部で何が起きているのかも合わせて確認していきます。
今回特に確認したのは以下のポイントです。

  • top
  • tcpdump
  • ログ(access_log)

今回の検証はCPU使用率などが分かりやすくする為に、意図的に重いPHPを設置しております。
あくまでも検証であり、実際はコンテンツの造り方などの環境次第で変わっていきますので、参考程度にご覧ください。

また、今回のポイントを画像にまとめましたので、検証内容をスキップしたい方は こちらから今回のまとめ までスキップできます。

今回の検証環境

スペック

メモリ:2 GB
CPU:2 コア

OS

AlmaLinux 9.7 を使用。

使用したもの

  • Apache(httpd)
  • php-fpm
  • firewalld
  • tcpdump

検証用PHP

今回の検証に必須ではないのですが、CPU負荷を分かりやすくするため、意図的に重いPHPを配置しております。

負荷テスト方法

実際に別のサーバから大量のアクセスを実施しております。
方法については今回の記事とは逸れる為、省略させていただきます。

① 制限なし(ノーガード)

まずは何も制限しない状態です。
※ポートを全開放してしまうと、他のIPからもアクセスできてしまうので、実際はアクセス元のIPのみ許可している状態です。

結果

CPU

%Cpu0  : 99.3 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.7 hi,  0.0 si,  0.0 st
%Cpu1  : 99.7 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.3 hi,  0.0 si,  0.0 st
topコマンド結果全文
[root@web01 ~]# top
top - 18:11:31 up 53 min,  3 users,  load average: 4.20, 1.79, 0.69
Tasks: 167 total,  10 running, 157 sleeping,   0 stopped,   0 zombie
%Cpu0  : 99.3 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.7 hi,  0.0 si,  0.0 st
%Cpu1  : 99.7 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.3 hi,  0.0 si,  0.0 st
MiB Mem :   1963.1 total,    585.2 free,    467.9 used,   1088.8 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   1495.1 avail Mem
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  45410 apache    20   0  178072  15700   9184 R  20.3   0.8   0:12.27 php-fpm
  45813 apache    20   0  178104  15072   8544 R  19.6   0.7   0:11.34 php-fpm
  45809 apache    20   0  178072  15064   8544 R  18.9   0.7   0:10.78 php-fpm
  45816 apache    20   0  178104  15072   8544 R  18.6   0.7   0:08.24 php-fpm
  45808 apache    20   0  178072  15064   8548 R  16.6   0.7   0:11.02 php-fpm
  45811 apache    20   0  178072  15700   9184 R  16.6   0.8   0:08.94 php-fpm
  45826 apache    20   0  178104  15072   8544 R  16.6   0.7   0:01.32 php-fpm
  45815 apache    20   0  178104  15076   8548 R  16.3   0.7   0:10.34 php-fpm
  45814 apache    20   0  178104  15072   8544 R  15.9   0.7   0:10.55 php-fpm
  45408 apache    20   0  178072  15056   8540 S   6.6   0.7   0:11.88 php-fpm
  45409 apache    20   0  178072  15056   8540 S   6.0   0.7   0:09.69 php-fpm
  45812 apache    20   0  178104  15072   8548 S   6.0   0.7   0:11.15 php-fpm
  45407 apache    20   0  178072  15056   8540 S   5.3   0.7   0:11.83 php-fpm
  45810 apache    20   0  178072  15060   8544 S   5.3   0.7   0:11.11 php-fpm
  45598 apache    20   0  178072  15700   9184 S   5.0   0.8   0:11.85 php-fpm
  45411 apache    20   0  178072  15700   9184 S   4.7   0.8   0:09.91 php-fpm
  45615 apache    20   0 1441756  11928   6832 S   0.3   0.6   0:00.13 httpd
  45825 root      20   0    7672   3988   3128 R   0.3   0.2   0:00.04 top
      1 root      20   0  175908  18780  11432 S   0.0   0.9   0:03.22 systemd
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.00 kthreadd
      3 root      20   0       0      0      0 S   0.0   0.0   0:00.00 pool_workqueue_
      4 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-rcu_g
      5 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-sync_
      6 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-slub_
      7 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-netns
     10 root      20   0       0      0      0 I   0.0   0.0   0:00.00 kworker/u8:0-events_unbound
     11 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-mm_pe
     12 root      20   0       0      0      0 I   0.0   0.0   0:00.00 kworker/u8:1-netns
     13 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tasks_kthre
     14 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tasks_rude_
     15 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tasks_trace
     16 root      20   0       0      0      0 S   0.0   0.0   0:00.08 ksoftirqd/0
     17 root       0 -20       0      0      0 S   0.0   0.0   0:00.30 pr/ttyS0
     18 root      20   0       0      0      0 I   0.0   0.0   0:00.31 rcu_preempt
     19 root      20   0       0      0      0 S   0.0   0.0   0:00.00 rcu_exp_par_gp_
     20 root      20   0       0      0      0 S   0.0   0.0   0:00.00 rcu_exp_gp_kthr
     21 root      rt   0       0      0      0 S   0.0   0.0   0:00.00 migration/0
     22 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 idle_inject/0
     24 root      20   0       0      0      0 S   0.0   0.0   0:00.00 cpuhp/0
     25 root      20   0       0      0      0 S   0.0   0.0   0:00.00 cpuhp/1
     26 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 idle_inject/1
     27 root      rt   0       0      0      0 S   0.0   0.0   0:00.05 migration/1
     28 root      20   0       0      0      0 S   0.0   0.0   0:00.07 ksoftirqd/1
     30 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/1:0H-events_highpri
     34 root      20   0       0      0      0 I   0.0   0.0   0:00.13 kworker/u10:1-events_unbound
     35 root      20   0       0      0      0 S   0.0   0.0   0:00.00 kdevtmpfs
     36 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-inet_
     37 root      20   0       0      0      0 S   0.0   0.0   0:00.00 kauditd
     38 root      20   0       0      0      0 S   0.0   0.0   0:00.00 khungtaskd
     40 root      20   0       0      0      0 I   0.0   0.0   0:00.13 kworker/u9:2-events_unbound
     41 root      20   0       0      0      0 S   0.0   0.0   0:00.00 oom_reaper
     42 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-write
     43 root      20   0       0      0      0 S   0.0   0.0   0:00.02 kcompactd0
[root@web01 ~]#

CPUは2コア両方でほぼ100%まで上昇しました。
特に使用率上位に php-fpm が来ております。
意図的に重いPHPを設置しておりますので、当然ではありますが、実際に

HTTP到達

Apache

PHP実行

CPU消費

まで完全に処理が進んでいるのが分かります。

各種確認まとめ

項目状態
CPU非常に高い
tcpdump見える
access_logステータスコード 200

これを基準に各制御方法を確認していきましょう!

IP制限② ApacheでIP制限

次は Apache 側で制御します。

設定

/etc/httpd/conf.d/ipdeny.conf

<Directory "/var/www/html">
Require ip 127.0.0.1
</Directory>

結果

CPU

%Cpu0  :  6.7 us, 16.7 sy,  0.0 ni, 71.3 id,  0.0 wa,  0.7 hi,  4.7 si,  0.0 st
%Cpu1  :  5.8 us, 13.6 sy,  0.0 ni, 74.9 id,  0.0 wa,  1.0 hi,  4.7 si,  0.0 st
topコマンド詳細
[root@web01 ~]# top
top - 18:56:12 up 14:38,  3 users,  load average: 0.00, 0.00, 0.00
Tasks: 166 total,   1 running, 165 sleeping,   0 stopped,   0 zombie
%Cpu0  :  6.7 us, 16.7 sy,  0.0 ni, 71.3 id,  0.0 wa,  0.7 hi,  4.7 si,  0.0 st
%Cpu1  :  5.8 us, 13.6 sy,  0.0 ni, 74.9 id,  0.0 wa,  1.0 hi,  4.7 si,  0.0 st
MiB Mem :   1963.1 total,    586.0 free,    469.5 used,   1086.4 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   1493.6 avail Mem
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  47008 tcpdump   20   0   19552  10208   8420 S   9.3   0.5   0:00.56 tcpdump
  46682 almalin+  20   0   22360   9360   5696 S   8.0   0.5   0:00.47 sshd
  46829 apache    20   0 1442032  14172   6528 S   5.0   0.7   0:00.31 httpd
  46827 apache    20   0 1441896  14588   6528 S   4.3   0.7   0:00.30 httpd
  47012 apache    20   0 1441896  13960   6532 S   4.3   0.7   0:00.16 httpd
  46828 apache    20   0 1573032  14088   6548 S   4.0   0.7   0:00.28 httpd
  46715 almalin+  20   0   21144   7900   5696 S   2.7   0.4   0:00.12 sshd
  46364 root      20   0       0      0      0 I   2.0   0.0   0:00.26 kworker/u9:1-events_unbound
  46569 root      20   0       0      0      0 I   2.0   0.0   0:00.10 kworker/u9:2-events_unbound
  47009 root      20   0    2692   1676   1560 S   1.7   0.1   0:00.07 tail
  46526 root      20   0       0      0      0 I   0.3   0.0   0:00.05 kworker/u10:2-events_unbound
  46554 root      20   0       0      0      0 I   0.3   0.0   0:00.04 kworker/u10:1-events_unbound
  47067 root      20   0    7668   3944   3080 R   0.3   0.2   0:00.03 top
      1 root      20   0  175908  18780  11432 S   0.0   0.9   0:05.83 systemd
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.01 kthreadd
      3 root      20   0       0      0      0 S   0.0   0.0   0:00.00 pool_workqueue_
      4 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-rcu_g
      5 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-sync_
      6 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-slub_
      7 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-netns
     10 root      20   0       0      0      0 I   0.0   0.0   0:00.00 kworker/u8:0-events_unbound
     11 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-mm_pe
     12 root      20   0       0      0      0 I   0.0   0.0   0:00.00 kworker/u8:1-netns
     13 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tasks_kthre
     14 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tasks_rude_
     15 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tasks_trace
     16 root      20   0       0      0      0 S   0.0   0.0   0:00.11 ksoftirqd/0
     17 root       0 -20       0      0      0 S   0.0   0.0   0:00.30 pr/ttyS0
     18 root      20   0       0      0      0 I   0.0   0.0   0:02.77 rcu_preempt
     19 root      20   0       0      0      0 S   0.0   0.0   0:00.00 rcu_exp_par_gp_
     20 root      20   0       0      0      0 S   0.0   0.0   0:00.00 rcu_exp_gp_kthr
     21 root      rt   0       0      0      0 S   0.0   0.0   0:00.03 migration/0
     22 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 idle_inject/0
     24 root      20   0       0      0      0 S   0.0   0.0   0:00.00 cpuhp/0
     25 root      20   0       0      0      0 S   0.0   0.0   0:00.00 cpuhp/1
     26 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 idle_inject/1
     27 root      rt   0       0      0      0 S   0.0   0.0   0:00.09 migration/1
     28 root      20   0       0      0      0 S   0.0   0.0   0:00.08 ksoftirqd/1
     30 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/1:0H-events_highpri
     35 root      20   0       0      0      0 S   0.0   0.0   0:00.00 kdevtmpfs
     36 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-inet_
     37 root      20   0       0      0      0 S   0.0   0.0   0:00.01 kauditd
     38 root      20   0       0      0      0 S   0.0   0.0   0:00.03 khungtaskd
     41 root      20   0       0      0      0 S   0.0   0.0   0:00.00 oom_reaper
     42 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-write
     43 root      20   0       0      0      0 S   0.0   0.0   0:00.45 kcompactd0
     44 root      25   5       0      0      0 S   0.0   0.0   0:00.00 ksmd
     45 root      39  19       0      0      0 S   0.0   0.0   0:02.33 khugepaged
     46 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-crypt
     47 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-kinte
     48 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-kbloc
     49 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 irq/9-acpi
     51 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/R-tpm_d
[root@web01 ~]#

先ほどと比べると、かなりユーザー処理が下がっています。
これはApache側で403を返しているため、PHPまで処理がとどいていないことが分かります。
しかし、アクセス自体はApacheまで到達しています。
そのため、access_log にはステータスコード403が記録されています。

"GET /index.php HTTP/1.0" 403

つまり、

HTTP到達
↓
Apache
↓
ステータスコード403返却

PHPは実行されない

という状態であることが分かります。

各種確認まとめ

項目状態
CPU少し上昇(30%前後)
tcpdump見える
access_logステータスコード 403

Apache制御のポイント

Apache制御はかなり柔軟です。
例えば、URL単位やディレクトリ単位での制御、Basic認証などのレベルの制御が可能です。
ただし、「Apacheまで到達はしている」のがポイントです。
今回の検証より多くのアクセスがあった場合、CPU使用率などは当然上昇します。

IP制限③ firewalld(OS)でDROP

次はOSレベルです。
今回は firewalld で明示的に DROP しました。

firewalld設定内容
[root@web01 ~]# firewall-cmd   --permanent   --add-rich-rule='rule family="ipv4" port port="80" protocol="tcp" drop'
success
[root@web01 ~]# firewall-cmd --reload
success
[root@web01 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources:
  services: cockpit dhcpv6-client
  ports:
  protocols:
  forward: yes
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
        rule family="ipv4" port port="80" protocol="tcp" drop
[root@web01 ~]#

結果

ここでかなり挙動が変わりました。

CPU

%Cpu0  :  0.0 us,  0.2 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.3 us,  0.3 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

これまでの2つの検証と違い、ほとんど0%です。

NIC
↓
Kernel(サーバ)
↓
DROP

Apacheは実行されない

という状態になっていることが分かります。
また、Apacheまで到達していない為、Apacheのアクセスログにも記録されていません。

tcpdumpで確認

Apacheのアクセスログに記録がされないため、tcpdumpというサーバの通信を確認するコマンドで確認します。

[root@web01 ~]# tcpdump -nn port 80
19:09:57.899570 IP "アクセス元のIP".46792 > "検証サーバのIP".80: Flags [S], seq 3334012566, win 64240, options [mss 1460,sackOK,TS val 2117990033 ecr 0,nop,wscale 7], length 0

実際に記録はされていたので、通信自体が届いていることが確認できます。

各種確認まとめ

項目状態
CPUほぼ使わない
tcpdump記録あり
access_log残らない

IP制限④ さくらのクラウド パケットフィルタ(PF)

最後はクラウド側です。
今回はさくらのクラウドのパケットフィルタで TCP/80 を deny しました。
今回初めて設定したのですが、下記の記事を参考に設定してみました!

結果

CPU

%Cpu0  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.3 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st

firewalldの時点で、ほぼ使用率0でしたがパケットフィルタはサーバーの外で制御しているため、使用率の変化は見られませんでした。

tcpdumpで確認

[root@web01 ~]# tcpdump -nn port 80
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel
[root@web01 ~]#

先ほどとは異なり、0 packets captured となっております。
つまり、サーバのNICまでHTTP通信が来ていない ということがわかります。

IP制限まとめ

制御箇所Apache到達アクセスログtcpdumpCPU
ノーガード○(200)非常に高い
Apache○(403)少し高い
firewalld××かなり低い
PF×××影響無

今回の検証は比較がしやすいように、IP制限に絞ってCPU使用率を中心に見てきました。
そのため、今回の結果だけを見るとパケットフィルタが一番いいように見えますが、実際は【制御できる範囲】が異なるため、目的にあった用途を選択する必要があります。

特にApacheでの制御は、
「/admin だけ制限したい」
「Basic認証と組み合わせたい」
など、アプリ寄りの細かい制御が得意です。

一方で、DDoSのような大量アクセスや、そもそも通信を通したくないケースでは、より手前で落とせるPFやfirewalldの方が有利になります。
実際の運用されているサーバだと、複数組み合わせて利用することも珍しくありません。

今回の比較結果まとめ

次回の記事について

今回登場した、パケットフィルタ(PF)、firewalld、Apacheの三つは、どちらかというと“サーバに近い層”の話であり、CPU使用率という数字での比較がやりやすかったです。
次回は、今回は敢えて触れてこなかった「制御範囲」という視点から、

「どこで通信を止めると、どこまで到達するのか?」
「WAFとPFは何が違うのか?」
「GSLBやDNSは“防御”なのか?」

といった部分も含めて、外側から内側までのレイヤー構造を整理しながら解説していきたいと思います!

この記事を書いた人

アナサピ

サービス業からのエンジニア転職。 クラウドに触れながら得た気付きや、初心者からの視点をお届け。 「これから触る人の参考になる情報」をまとめていきます! 【取得資格】 LinuC Level1 さくらのクラウド検定