VPSのファイアウォール設定
今回は有償のサービスの話です。過去にGCPでクラウドサーバーを構築してみました。今回は「さくらインターネット」で構築してみたいと思います。日本の上場企業で言語の壁というものがない分サポート面も安心です。 さくらのVPSの申し込みの流れと、初期設定で必要なiptablesの設定を公式のチュートリアルに即して行いたいと思います。
申し込み
申し込みの前に仮想PCのスペックを決めます。メモリの容量やハードディスクの容量のプランが段階的に設定されているので最適なプランを選択しましょう。スペックはのちにスケールアップもできるようです。
申し込みは簡単です。さくらのVPSから目的のプランを選択するだけです。
VPNの契約には通常の会員登録のほかに、携帯または固定電話での認証が必要になります。しかし、会員情報が登録できていてクレジット払いなら5分もかからずサーバーの設定を触れるようになります。
筆者は「石狩第一」「メモリ1G」「SSD50G」のプランを契約してみました。IPv4のアドレスとIPv6のドメイン名とアドレスを割り当てられ、(仮想)NICはインターネット側が1個、内側が2個ついていました。
これにApacheやPHP、MariaDBをインストールしてWebサーバーをたてたり、家のPCとクラウドの内側のNICとでVPNを組んでみたいと思います。
サーバー初期設定
「サーバー情報」、「各種設定」「サーバー情報編集」からサーバー名を設定します。
「サーバー情報」、「各種設定」「OSインストール」からOSをインストールします。ここでは「カスタムOS」から「Debian10」をインストールしました。
インストールや以降の操作は「VNCコンソール」というコンソールで行います。先の「サーバー情報編集」でVNCコンソール用のキーボードレイアウトや言語が設定できましたが、カスタムOSだからなのか、それらの設定を日本語にしてあっても「Debian」のインストールの時の言語は英語で、かつキーボードも日本語キーボードではありませんでした。
インストールが終了すると、いったんサーバーが停止するので、「起動」ボタンを押します。そのあと「コンソール」から再びVNCコンソールを開くと、今度はDebianのログイン画面となります。
インストール後まずやるべきは「ファイアウォール」の設定なのですが、デフォルトではDebianの言語が英語になっているので、先にそれを日本語します。キー配列は英語配列が操作しづらかったらローカルからTeraTerm等を使った接続にします。ネットワーク情報のIPv4のアドレスを入力すればSSH経由でアクセスできます。
日本語化は@IT:「WSLのDebian環境を日本語化する」に操作方法が紹介されています。
$ su ... # apt update ... # apt install task-japanese ... # /sbin/dpkg-reconfigure locales ... # systemctl reboot
dpkg-reconfigureで表示される画面では「ja_JP.UTF-8 UTF-8」を選択して「OK」を押します。選択はスペースキー、決定はエンターキーです。次の画面でも「ja_JP.UTF-8 UTF-8」を選択します。
筆者も遭遇しましたが、もし処理の途中で「...locale-gen: not found」というエラー出たらPATHの問題が疑われます。PATHにsbinディレクトリが含まれていかチェック(echo $PATH)して、なければ「PATH=$PATH:/sbin」を実行した後に再度同じ処理をすればエラーがでなくなると思います。
参考サイトにあるように、「dpkg-reconfigure tzdata」でタイムゾーンも変更しておきます。
ちなみに日本語化すると、VNCコンソール側で日本語が化けました。VNCコンソールで利用する際は「LANG=C」と入力して言語を英語に戻しましょう。
ファイアウォール
立ち上がったVPSは誰でもアクセスできるようになっています。悪意のあるユーザーはこのようなサーバーをなんとか利用できないかと常に不正アクセスを試みます。
そのような悪意のあるユーザーの行為を通称で「アタック」と呼びますが、このアタックによる弊害をなるべく減らすためにファイアウォールを設定します。
Debianにおいてはデフォルトで「iptables」というファイアウォールが用意されていますが、初期設定はすべての通信を許可するようになっている為これを修正します。
Debian(Linux)におけるiptableの基本についてはリンク先の記事にて紹介していますので、よろしければそちらも参考にしてください。
ファイアウォールの設定のパターンはサーバーの数だけあると思いますが、ここではさうらのナレッジ:「ファイアウォールiptablesを簡単解説...」を参考に、SSH接続だけを許可するごく基本的な設定をしたいと思います。
またiptablesの設定は、サーバーを再起動すると元に戻ってしまうので「iptables-persistent」というアプリを使って、再起動後も設定値が再度反映されるようにします。
追記
Debian10から iptables にかえて nftables の利用が推奨されています。 nftabls については別記事でまとめていますのでそちらも合わせて参照していただけると幸いです。
iptablesではポリシーと呼ばれる基本ルールと、明細ルールの2階層に分けてファイアウォールを管理しています。
ポリシーでは、入力、転送、出力の3種のチェインで、明細ルールがなかった場合の挙動を指定します。初期状態はすべて許可になっています。iptables -Lコマンドを使って、現在の状態を確認してみましょう。
/sbin/iptables -L Chain INPUT (policy ACCEPT) ... Chain FORWARD (policy ACCEPT) ... Chain OUTPUT (policy ACCEPT) ...
INPUT(入力)、FORWARD(転送)、OUTPUT(出力)ともpolicy(ポリシー)がACCEPT(許可)になっているのが確認できると思います。
これをiptablesのコマンドで変更するには次のようにします。
ここではコマンドを入力しません。入力してしまうと、すぐに反映してローカルPCからのSSH接続が切断されてしまうからです。
ちなみにもしここで「iptables -P INPUT DROP」と入力してしまった場合は、先ほどまで操作していた「VNCコンソール」から接続し先のコマンドのDROPの部分をACCEPTにしたコマンドを入力するか、サーバーを再起動すれば元に戻ります。
iptables-persistent経由で設定を行う場合は/etc/iptables/rules.v4またはrules.v6ファイルを編集します。v4はIPv4、v6はIPv6の設定を表します。このファイルでの編集は保存しても即座には反映されません。再起動するか「netfilter-persistent reload」が実行された時にiptablesに反映されます。
rules.v4ファイルは初期状態ではiptables同様すべて次のようにすべてを許可する設定になっていると思います。
rules.v4
*filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT
:(半角コロン)から始まるエントリーがポリシーを指定している部分です。一般的なポリシー設定は、入力・転送は拒否、出力は許可です。
明細部はポリシーとCOMMITの間に記述します。これはiptablesのコマンドをほぼそのまま入力します。
基本的な使い方は、-Aでルール追加(add)、-Dで削除(del)となり、その後にチェイン(INPUT|FORWARD|OUTPUT)を指定し、開始ポートや終了ポートなどのオプションを指定した後、-jオプションで許可(ACCEPT)なのか拒否(DROP)なのかを指定します。
明細部に該当しなかった通信はポリシーで設定したルールに従います。
rules.v4
*filter # ポリシー :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] # 以降明細 # ループバックを許可 -A INPUT -i lo -j ACCEPT # データのないパケットを破棄する -A INPUT -p tcp --tcp-flags ALL NONE -j DROP # アタックを破棄する -A INPUT -p tcp ! --syn -m state --state NEW -j DROP # ステルススキャンを破棄 -A INPUT -p tcp --tcp-flags ALL ALL -j DROP # icmp設定 # -m hashlimit hashlimitモジュールを利用 # -hashlimit-name t_icmp 記録用ファイル # -hashlimit 1/m リミットを1分間に1パケットを上限に # -hashlimit-burst 10 規定時間内に10パケット受信でリミット発動 # -hashlimit-mode srcip ソースIPを元にアクセスを制限する # -hashlimit-htable-expire 120000 リミットの有効期間。単位はms -A INPUT -p icmp --icmp-type echo-request -m hashlimit --hashlimit-name t_icmp --hashlimit 1/m --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-htable-expire 120000 -j ACCEPT # 確立済みの通信は許可 -A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT # DNSの通信を許可 -A INPUT -p udp --sport 53 -j ACCEPT # SSH設定 -A INPUT -p tcp -m state --syn --state NEW --dport 22 -m hashlimit --hashlimit-name t_sshd --hashlimit 1/m --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-htable-expire 120000 -j ACCEPT COMMIT
設定中で使われているオプションの説明は次の通りです。
- !
!は否定を表し直後のルールを反転させます。
- -p
プロトコルを指定するオプションです。tcp、udp,、udplite、icmp、esp、ah、sctpとallを指定できます。
ここで-p tcp と指定した時だけ--tcp-flagとすることでチェックするフラグと値について指定できます。
フラグとはTCPヘッダにおけるコントロールフラグの6ビットのことで、順にURG(Urgent)、ACK(Acknowledgement)、PSH(Push)、RST(Reset)、SYN(Synchronize)、FIN(Fin)となっています。「ALL NONE」とすると、フラグ6ビットすべてをチェックして、何もフラグが立っていないものという意味になります。「ALL SYN,ACK」とするとSYNとACKだけがセットされているものが対象になります。
「--syn」は「--tcp-flags SYN,RST,ACK SYN」の省略形です。これはSYN、RST、ACKフラグをチェックしてSYNだけフラグが立っているものを意味します。
コントロールフラグをどのように使って通信をしているかという話はmasai.devのページが参考になります。
- -m
matchのmです。後に通信を検査する拡張モジュールを指定します。複数指定した場合はfalseとなった段階で終了します。
stateは拡張モジュールでstatus(ステータス)による判定をします。--stateのあとに対象のステータスを指定します。NEWが接続開始、ESTABLISHEDが応答または接続確立中、他、RELATEDやINVALID等を指定できます。
上記の設定では、新規接続開始なのにSYNフラグでなかったり、すべてのフラグがたっていたりするパケットを破棄しています。そのようなおかしなパケットに応答してしまうと攻撃者にサーバーがパケットを待ち受けているという事を知られてしまい攻撃の増加につながります。
hashlimitはmオプションで使える拡張機能のひとつで接続先毎の設定です。ICMP(ping)やSSHでの接続にたいして指定した値を超えて試行しているパケットを拒否します。
- --dport
dportは宛先ポートを指定するオプションです。-p tcpまはたudpと共に使います。
- -j
jはjumpのjで、ターゲットを設定します。ACCEPTやDROPの他、ユーザー定義のチェインがターゲットになることもあります。
他まだまだオプションは存在しますが、man iptablesを参考にしてください。
また現時点では利用しないIPv6の通信は一旦すべて遮断しておきます。
追記:通信が安定しない場合、ICMPの制限「--icmp-type echo-request」の部分を外してみるといいかもしれません。Path MTU Discoveryが遮断されていてMTUが適正になっていない可能性があります。
rules.v6
*filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT DROP [0:0] COMMIT
設定が終わったら、反映コマンドを入力して、内容を確認しています。
/sbin/netfilter-persistent reload ... /sbin/iptables -L ... /sbin/ip6tables -L ...
rules.v4やrules.v6ファイルの書式はiptables-saveを実行した時に出力される内容と一緒になります。
その前提のもとでポリシーにある[0:0]を説明すると、これらは「パケットカウンタ:バイトカウンタ」です。フィルタにマッチしたパケット数とバイト数を表しています。
iptables-saveと対になっている復元コマンドiptables-restoreでは-cオプションでカウンタ値を復元できますが、rules.v4やrules.v6で[0:0]以外の値を設定しても、再起動時には0に戻るようです。
チェインとは
チェインに関してもう少し詳しく書いておきます。これはその名の通り鎖をイメージしてもらっていいと思います。OUTPUTチェインは出力に関係する設定が鎖のように記述され、上から順に判定されていきます。同様に、INPUTなら入力、FORWARDなら転送のチェインとなっています。
チェインには他にPOSTROUTINGとPREROUTINGというものがあります。またユーザーが独自にチェインを設定することもできます。
テーブル
ここまで触れてきませんでしたがiptablesにはtableという概念があります。ここまでのtableはすべてfilterテーブルについて書いてきました。rules.v4ファイルの記述の最初にある*filterはその意味です。
filter tableは通信の可否を決めるテーブルです。他にNATを管理するnat table、TOSの管理をするmangle table、後raw tableというものもあります。
処理の過程
iptablesがどの段階でどのtable・チェインを使ってデータ処理するかですが、次の図が参考になります。
Flow of network packets through Netfilter by Jan Engelhardt (CC BY-SA 3.0)
iptablesのリセット
ファイアウォールを稼働させると、通信できない原因の切り分けにそれをOFFにしたいケースがでてくると思います。その際は次のようにコマンドを入力することで解除されます。
iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT iptables -F
先にポリシーを許可にしておかないと途中で接続できなくなることがあります。そのあとで-Fオプションで設定をクリアしています。このクリアはfilter tableだけに対応します。もしnatやmangleテーブルの設定をクリアしたいなら -tオプションを使ってテーブルを指定した後-Fオプションを指定してクリアします。また、独自のチェインを削除したい場合はiptables -Xとします。
再びファイアウォールを稼働させたいなら「netfilter-persistent reload」コマンドを入力します。
ポート22番について
運用上検討したほうがいい22番ポートについての話を書いて終わりにします。このポートはSSHで使われると攻撃者もわかっているので、SSHを22番で待ち受けていると攻撃が多くなる場合があります。その際は/etc/ssh/ssh_configのPortエントリーの値を22から他の値にするか、もう一つPortエントリーを作って外部からの接続を待ち受けるのは新たに作成したポートのみにします。
参考にさせていただいたサイトの皆様、ありがとうございました。