自分なりにあちこちからかき集めて作ったセキュアなCentOS7サーバ

More than 1 year has passed since last update.

この記事は

スマホゲームを作っている中でサーバが要るので、
WebAPIサーバとDBサーバの土台を作ろうと思い立ち、
いろいろなサイトと自分の過去の経験・知識からのサーバを構築した日記です。

主に自分がやった事を中心に書いています。
設定内容についての説明はかなり省略して書いております。
ご不明な点がございましたら、コメントお願いします。

本記事でやっている事

MacOSX 10.11.3 をホストOSとし、VirtualBox 5.0.16 上に CentOS7.1 (1503) minimal をインストール1
その状態でセキュアにやれる設定に手を入れています。
* ネットワークサービスはsshのみです。(SNMP、Webサーバ、DB等はインストールしていません)
* SELinuxは有効にしていますが、ゆるめの設定で使っています。
* その他監視系のソフトはインストールしていません。

設定項目概要

ip

タイトル 概要
IP設定 軽く設定

SELinux

タイトル 概要
ポート設定 ssh用のポート番号を変対応 

sshd

タイトル 概要
sshポート変更 接続先のポート番号を変更
sshのプロトコルの制限 sshdのプロトコルを2に限定
公開鍵の設定 RSAの鍵ではなくてED25519で鍵を作成
パスワード認証を切る 公開鍵でログインするように設定
rootでlogin出来なくする 左記の通り
認証周りの設定 認証の猶予時間や認証回数など
ログインユーザの制限 指定ユーザのみが接続できるように
接続プロトコルの制限 X11、仮想端末の使用制限

firewall ( iptables )

タイトル 概要
破棄設定 ip spoofing、port scan、Flood攻撃 等をDROP
条件付きの接続 1回目はよくても立て続けに来た攻撃等の対応
接続設定 自分が使うものは無条件に接続 

TCPWrapper

タイトル 概要
host.allow サービスとユーザを紐付けて接続の許可
host.deny allowで許可されなかった通信の拒否

sudo

タイトル 概要
利用できるコマンドの制限 実行できるコマンドを制限する
接続制限 コンソールを持たない接続は拒否
シェル実行禁止 sudo でのシェルの実行を禁止
セッションタイムアウトの時間設定 認証有効期間の設定

カーネルの設定

タイトル 概要
ネットワーク対応 TCP SYN Flood、Sumrf攻撃対応等々

その他

タイトル 概要
最新バージョンの適用 yum update
不要なサービスの停止 不要なサービスの停止
logwatchの導入 今回は見送っています
使わないユーザーのログイン不可 使わないユーザーのログイン不可
umaskの設定 関係ないユーザのファイルアクセス制限

ipアドレスの設定

コマンドだけでやれたらカッコよかったけど、、、TUIで設定(゜゜

# nmtui

Edit a Connection ←選択
(NICカードを選択)
IPv4 Configuration <Show> ←選択
Require IPv4 addressing for this connection ← チェック
Automatically Connect ← チェック
OK ← 選択
Quit ← 選択

確認

# ip addr

参考
CentOS7 ネットワークの設定変更

SELinuxの設定

ここではポートの穴あけ をやっています。
sshで通信する予定のポート(=49152)になります。
まずこれをやっておかないとssh通信ができず、慌てることに。
(というか実際、数時間iptablesとsshd_configの設定の見直しばっかりやって時間を浪費してしまいました。)

編集用パッケージインストール

$ yum install policycoreutils-python

ポートの追加

$ semanage port -a -t ssh_port_t -p tcp 49152

設定確認

$ semanage port -l | grep ssh

SELinuxのログ

$ cat /var/log/audit/audit.log

sshの設定

sshの設定を仕上げてないと何かと不便なので、
最初にsshの設定をしていきます。

ssh接続用ユーザ(=ssher)の作成

# groupadd -g 1900 mainte
# useradd -G mainte,wheel -u 1901  ssher #<sshで接続するユーザ and ルート権限に昇格できるユーザ> 
# mkdir /home/ssher/.ssh
# chmod 700 /home/ssher/.ssh                          # <- 忘れがちなので注意

秘密鍵/公開鍵の作成

RSAはもう古いのでED25519で作ります。(ミーハー)
MacOSXで鍵(秘密鍵/公開鍵)を作ってサーバ側へ公開鍵をコピーします。
(サーバ側で作ってMacOSXに秘密鍵をコピーって方法もやりましたが、
MacOSXのssh接続と相性が悪くてうまく接続できませんでした。)

$ sshe-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/Users/hogehoge/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/hogehoge/.ssh/id_ed25519.
Your public key has been saved in /Users/hogehoge/.ssh/id_ed25519.pub.
The key fingerprint is:
SHA256:FiublKpEQNgXnoVhogehogefugafugayWT74RSIRJEY hogehoge@MacOSX.local
The key's randomart image is:
+--[ED25519 256]--+
|      .   +oo    |
|     . . = * .   |
|    . sos = =    |
|   . o = B + .   |
|. ... o S * o    |
| o .o.   = o     |
|..++.   +        |
|.oBoss o         |
|Eo.=*+o          |
+----[SHA256]-----+
$
$ # サーバ側へ公開鍵をコピー
$ scp /Users/hogehoge/.ssh/id_ed25519.pub root@localhost:/home/ssher/.ssh/
root@localhost's password:
$

(ちなみに上記で作成した鍵は作り直しています。一部表示を伏せています。)

サーバ側で受け取った公開鍵をauthorized_keysへ登録

sshの設定(公開鍵の登録)
# su - ssher
$ cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
$ chmod 0600 ~/.ssh/authorized_keys
$ chown ssher:ssher ~/.ssh/authorized_keys
$ exit       # ssherユーザを抜ける

sshd_configの設定

基本設定は終わったのでここから本格的にセキュリティ設定に入る

sshの設定(設定ファイルでできるセキュリティ)
# vi /etc/ssh/sshd_config
 Protocol 2                              # Protocol 1 は使わない(Protocol 1で繋ぐソフトを使用する予定が一切無い)
 Port 49152                             # ssh標準の番号を使わない(自由に使えるポート番号 = 49152 〜 65535 )

# ed25519の鍵だけ有効にする
# HostKeys for protocol version 2
# HostKey /etc/ssh/ssh_host_rsa_key
# HostKey /etc/ssh/ssh_host_dsa_key
# HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key 

# Logging
SyslogFacility AUTHPRIV

# 認証時の挙動設定
 LoginGraceTime 10               # コネクションを貼って認証するまでの猶予時間(s)
 PermitRootLogin no              # root ユーザがsshでログインできないようにする
 MaxAuthTries 3                     # 最大認証試行回数
 MaxStartups 2:90:4              # 2つを超える認証受付は90%の確率で遮断。4つを超えると全て遮断

# 認証方式の設定
 PasswordAuthentication no  # パスワードによるログイン禁止
 ChallengeResponseAuthentication no # チャレンジレスポンスによるログイン禁止
 PermitEmptyPasswords no   # 空のパスワード設定は接続拒否
 RSAAuthentication no           # おそらくProtocol 1の時代専用パラメータ
 PubkeyAuthentication yes    # 鍵認証によるログインを許可

# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
AuthorizedKeysFile      .ssh/authorized_keys


# シングルサインオンの設定(GSSAPI options) 使用予定無し
GSSAPIAuthentication no
GSSAPICleanupCredentials no


# WARNING: 'UsePAM no' is not supported in Red Hat Enterprise Linux
# and may cause several problems.
UsePAM yes  # 無効にすると挙動不審になるくさいのでyesのまま

# 接続制限
AllowUsers ssher                # 接続できるユーザの制限
#AllowAgentForwarding no
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding no                 # GUIでサーバを操作しない
PermitTTY yes                      # 仮想端末の許可。tmux?なにそれ?な方はno

# DNS接続設定
UsePrivilegeSeparation sandbox          # Default for new installations.
UseDNS  no                           # IPアドレスを使用して接続するため、FQDNの検索にかかる待機時間を無くす。

# 以下、デフォルトのまま。(正直よくわかってない)
# Accept locale-related environment variables
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS

# override default of no subsystems
Subsystem       sftp    /usr/libexec/openssh/sftp-server

パスワード認証の件ですが、有効にしたままだと鍵認証に失敗した時、
パスワード認証に流れていくようです。鍵認証にする場合、必ず無効にしましょう。

設定の確認

$ sshd -t

設定にミスがある場合、以下の様に出力されます。
(問題ない場合、何も出力されません)

/etc/ssh/sshd_config: line 36: Bad configuration option: hogehoge
/etc/ssh/sshd_config: terminating, 1 bad configuration options

設定の反映

$ systemctl restart sshd

繋ぐときのコマンド

$ ssh -i ~/.ssh/id_ed25519 ssher@接続先IPアドレス -p 49152

ちなみに、クライアント側に設定をぶち込んでおくとコマンドが短くなって幸せです。
(クライアント側を乗っ取られた時は、簡単に接続されてしまうのでセキュリテイを高く保つならオススメしません)

~/.ssh/config
Host 接続先の任意の名前
  HostName 接続先のIPアドレス
  User ssher
  IdentityFile ~/.ssh/id_ed25519
  Port 49152

接続コマンド

$ ssh 接続先の任意の名前

失敗メモ

環境構築中、サーバに疎通できるにも関わらず、証明書を受け付けてもらえないことがありました。
/var/log/secure に認証がうまくいかなかったログ(以下内容)を見つけた。

Authentication refused: bad ownership or modes for directory /home/ssher/.ssh

原因はディレクトリの権限設定でした。

755で動いた方もおられるみたいですが、私の場合すでに755になっていたので、

700に変更しました。

$ chmod 700 .ssh

ssh参考サイト

iptablesの設定

CentOS7からは従来のiptables に変わり、firewall-cmd になってますので、
こちらで設定します。

firewall-cmd パッケージが入ってなかったり、情報が少なかったり、これが本当に色々苦労しました。1

破棄設定
DROPの設定はdirectコマンドでガツガツ入れていきます。
(directコマンドは優先度が高いのでこちらに設定します。)

firewall穴あけ
# firewall-cmd --remove-service=ssh --permanent   # 通常のsshポートは塞ぐ

# # データを持たないパケットの接続を破棄する
# firewall-cmd  --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --tcp-flags ALL NONE -j DROP 
# # SYNflood攻撃と思われる接続を破棄する
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp ! --syn -m state --state NEW -j DROP 
# # ステルススキャンと思われる接続を破棄する
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --tcp-flags ALL ALL -j DROP  
# # ブロードキャストアドレスを破棄する
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m pkttype --pkt-type broadcast -j DROP
# # マルチキャストアドレスを破棄する
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m pkttype --pkt-type multicast -j DROP 
# # IP Spoofing(なりすまし) の対応(外部からやってくるローカルIPは破棄)
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i enp0s3 -s 127.0.0.1/8 -j DROP
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i enp0s3 -s 10.0.0.0/8 -j DROP #開発環境では許可
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i enp0s3 -s 172.16.0.0/12 -j DROP
# # firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i enp0s3 -s 192.168.0.0/16 -j DROP  #開発環境では許可
# # firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i enp0s3 -s 192.168.0.0/24  -j DROP #開発環境では許可
# # Ping of Death 攻撃対策(firewall-cmdに修正するのがめんどくなったので割愛。参考サイトを参照してください。)
# # Pingに不要なicmpタイプのブロック(echo-request、echo-reply 以外)
# firewall-cmd --permanent --add-icmp-block=destination-unreachable
# firewall-cmd --permanent --add-icmp-block=parameter-problem
# firewall-cmd --permanent --add-icmp-block=redirect
# firewall-cmd --permanent --add-icmp-block=router-advertisement
# firewall-cmd --permanent --add-icmp-block=router-solicitation
# firewall-cmd --permanent --add-icmp-block=source-quench
# firewall-cmd --permanent --add-icmp-block=time-exceeded
# # invalid パケットを破棄
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m state --state INVALID -j DROP
# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -m state --state INVALID -j DROP
# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -m state --state INVALID -j DROP

条件付きで許可

# # Ping Flood攻撃対策(4回以上pingを受信した場合、以降は1秒間に1度だけ許可します。)
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p icmp --icmp-type 8 -m length --length :85 -m limit --limit 1/s --limit-burst 4 -j ACCEPT
# # flooding of RST packets, smurf attack Rejection
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp -m tcp --tcp-flags RST RST -m limit --limit 2/second --limit-burst 2 -j ACCEPT

# # Protecting portscans
# # Attacking IP will be locked for 24 hours (3600 x 24 = 86400 Seconds)
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m recent --name portscan --rcheck --seconds 86400 -j DROP
# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -m recent --name portscan --rcheck --seconds 86400 -j DROP

# Remove attacking IP after 24 hours
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m recent --name portscan --remove
# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD  0 -m recent --name portscan --remove

許可

# firewall-cmd --add-port=49152/tcp --permanent  # 49152番の通信許可(このサーバのsshのポート番号)

基本動作
下記設定はsshの接続が確認されてから行う事。(でないと、ターミナルからログインできなくなる。)

# # ルールに当てはまらない受信を破棄(これで、INPUT、FORWARD両方にDROP)
# firewall-cmd --permanent --zone=public --set-target=DROP  

設定反映

# firewall-cmd --reload

ログの設定
気力が尽きたのでその内、下記サイトを参考に設定します

参考サイト:
* iptablesでログ出力設定
* iptablesの設定ファイルをシェルスクリプトを利用して動的に作成
* そこそこセキュアなlinuxサーバーを作る
* * How to protect from port scanning and smurf attack in Linux Server by iptables
* さくらのサポート情報 iptablesの設定方法
* 自分仕様のboxファイルを作成する
* 立ち上げ直後のiptablesを設定する。
* iptablesの設定ファイルをシェルスクリプトを利用して動的に作成

TCPWrapper(hosts.allow、hosts.deny)の設定

sshdの接続は許可し、その他の接続は拒否します
hosts.allow → hosts.deny の順に処理します

/etc/hosts.allow
sshd : ALL
/etc/hosts.deny

ALL  : ALL

sudoの設定

sudo を使ってシェルログインされるとログにも残らないまま、  
全権限を持ってコマンドを実行されると 具合が悪いので実行禁止にします
ついでに他の設定もちょこっとだけ設定しています。

shell コマンド

sudoの編集
# visudo

記述内容

Defaults    requiretty   # コンソールを持たないsshの接続はさせない
#Defaults:vagrant    !requiretty   # vagrantユーザだけ許可する場合はこの行のコメントを外す。

Defaults timestamp_timeout = 0  # sudoすると毎回必ずパスワードを求めるようにする
Defaults rootpw                 # sudoを実行する際はrootパスワードを求める
# 最初に実行できるコマンドをある程度制限する
Cmnd_Alias ALLOWCMND = /usr/sbin/
# sudoでシェルを実行させないようにする。(起動したシェル内で実行したコマンドがログに残らないため。)
Cmnd_Alias SHELLS = /bin/sh, /bin/csh, /bin/tcsh, /bin/bash, /usr/local/bin/bash
Cmnd_Alias SHELLS_NONINTERACTIVE = /bin/sh -c *, /bin/csh -c *, /bin/tcsh -c *, /bin/bash -c *
Cmnd_Alias SU = /bin/su, /usr/sbin/visudo
# %wheel ALL=(ALL)       NOPASSWD: ALL, !SU, !SHELLS, SHELLS_NONINTERACTIVE
%wheel ALL=(ALL)       ALLOWCMND, !SU, !SHELLS, SHELLS_NONINTERACTIVE

参考サイト:
* サービスをセキュアにするための利用制限(3)
* sudoのセキュリティ

カーネル設定

設定始めた当初はここまで手を出す予定はありませんでしたが、
調べてくうちに出てきちゃったので設定します。

# SYN Cookiesを有効にする
# ※TCP SYN Flood攻撃対策
sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.conf
echo "net.ipv4.tcp_syncookies=1" >> /etc/sysctl.conf

# ブロードキャストアドレス宛pingには応答しない
# ※Smurf攻撃対策
sed -i ‘/net.ipv4.icmp_echo_ignore_broadcasts/d’ /etc/sysctl.conf
echo “net.ipv4.icmp_echo_ignore_broadcasts=1” >> /etc/sysctl.conf

# ICMP Redirectパケットは拒否
sed -i '/net.ipv4.conf.*.accept_redirects/d' /etc/sysctl.conf
sed -i '/net.ipv6.conf.*.accept_redirects/d' /etc/sysctl.conf
for dev in `ls /proc/sys/net/ipv4/conf/`
do
sysctl -w net.ipv4.conf.$dev.accept_redirects=0 > /dev/null
echo "net.ipv4.conf.$dev.accept_redirects=0" >> /etc/sysctl.conf
done
for dev in `ls /proc/sys/net/ipv6/conf/`
do
sysctl -w net.ipv6.conf.$dev.accept_redirects=0 > /dev/null
echo "net.ipv6.conf.$dev.accept_redirects=0" >> /etc/sysctl.conf
done

# Source Routedパケットは拒否
sed -i ‘/net.ipv4.conf.*.accept_source_route/d’ /etc/sysctl.conf
for dev in `ls /proc/sys/net/ipv4/conf/`
do
sysctl -w net.ipv4.conf.$dev.accept_source_route=0 > /dev/null
echo “net.ipv4.conf.$dev.accept_source_route=0” >> /etc/sysctl.conf
done

設定反映

# sysctl -p

確認方法
SYN Cookiesを有効にする

# sysctl -a | grep net.ipv4.tcp_syncookies
net.ipv4.tcp_syncookies = 1

上記はサイトから拾ってきた内容そのままですが、
設定変更前に必要なのは ICMPリダイレクトパケットの拒否のみでした。
その他は設定済みでした。

参考サイト:
* RHEL 7 betaに挑戦その4(firewalld)
* Linuxで作るファイアウォール[パケットフィルタリング設定編]

その他

ここから一気に 手抜き、、、ゲフンゲフン

ソフトウェア更新

パッチ当て
$ yum update

不要なサービスの停止

systemctl でactiveなサービスを確認しつつ
不要なサービスを停止していきます。
が、ちょっと力尽きたので省略
気力が出てきたら更新します。

logwatchを入れる

とりあえず防げてたらいいのと、
まだローカル環境の開発ようにしか使わないので、見送りします。
公開前ぐらいにまた見直します

使わないユーザーをログイン不可にする

割とどのユーザも/nologinになってたのでとりあえずは良さげ。

umaskを027

othersからは好きに見れないように007、027に設定します。

# By default, we want umask to get set. This sets it for login shell
# Current threshold for system reserved uid/gids is 200
# You could check uidgid reservation validity in
# /usr/share/doc/setup-*/uidgid file

# Before
# if [ $UID -gt 199 ] && [ "`id -gn`" = "`id -un`" ]; then
#     umask 007
# else
#     umask 027
# fi

# After
if [ $UID -gt 199 ] && [ "`id -gn`" = "`id -un`" ]; then
    umask 007
else
    umask 027
fi

参考サイト:
* そこそこセキュアなlinuxサーバーを作る


終わりに

ここまで読んで頂きありがとうございます。
最後の方は明らかに手抜き感が漂っていますが、
また機会を見つけて見直していこうかと思います。

セキュリティの世界は楽しいですよね。
本記事ではやってる事ばかり書いていますが、 設定の必要性から深く勉強していくと、
人と人のガチの知恵比べの歴史が凝縮されている様が見えてきたりします。
まさに叡智の結晶。
(不謹慎ですみません)

ここで書いた設定も時間が経てば、それを破る攻撃方法とか見つけられている可能性も大いにあります。
そしてその攻撃を防ぐための新しい設定ができている事になると思います。

やることが変われば、設定も変わります。
この設定を鵜呑みに設定しないで、ご自身の環境に合わせて
検討してくださいますようお願いします。2

皆様の何かのお役に立てれば幸いです。
また、サーバの構築及び本記事を書くにあたり、多くのサイトから参考にさせていただき、サイト管理者様に感謝いたします。



  1. CentOS7.2 minimalはfirewalldが入っていないので、インストールする必要があります。参考サイト  

  2. ハードウェアの関係上コピー&ペーストでは高い確率で失敗する箇所が本記事内にもあります。 表記に悩んだので あえてどこかは書きません。 

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.