はじめに
サーバを運用保守する際に、特定のサーバに対して複数のユーザが自由にアクセス可能な状態にあると、不用意なコンソール接続が可能になり、サーバのセキュリティレベルが低下する。その為、サーバの位置づけによっては、ユーザごとに一定時間のみアクセスを許可し、それ以外の時間はアクセスができないような仕組みを構築したいことがある。
本記事はそのような要求に対して実現方法を検討した際のメモである。
想定環境
ローカル環境
- OS : Windows / macOS
- 条件 : sshコンソール接続が可能なツールが存在し、任意のネットワークに接続可能なこと。
リモート環境
- OS : Linux
- 条件 : sshdが可能し、sshによる接続が可能なこと。
- 備考 : 今回は、ローカル環境内に Vagrant で CentOS 6.9 環境を構築して動作検証を実施した。
構築手順
概要
今回の記事では、「sshd」及び「PAM(Pluggable Authentication Modules)」を使用した制御方法について記載する。
設定手順は以下の通りである。
- 環境の確認
- 環境の構築
- 手動による設定変更
- Scriptによる設定変更
- Script と Cronによる設定変更
- 履歴取得
概念図
対象ファイル
本操作によって修正を実施する ssh 及び PAM の設定ファイルは以下の通り。
対象ファイル | 解説 |
---|---|
/etc/security/sshd_access.conf | sshd用のアクセスの許可・禁止の設定を記載する設定ファイル。 デフォルトは「/etc/security/access.conf」だが、今回はsshd用にファイルを作成し、そちらを修正する。 |
/etc/pam.d/sshd | PAMの設定ファイル。設定対象のサービスごとに設定を記載する。 |
/etc/ssh/sshd_config | sshdの設定ファイル。 |
※設定順序やファイルを間違えた場合、sshアクセスが不可能になるため注意すること
手順1. 環境の確認
# ssh -V
OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013
# cat /etc/ssh/sshd_config | grep UsePAM
#UsePAM no
UsePAM yes
#%PAM-1.0
auth required pam_sepermit.so
auth include password-auth
account required pam_nologin.so
account include password-auth
password include password-auth
# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session required pam_selinux.so open env_params
session required pam_namespace.so
session optional pam_keyinit.so force revoke
session include password-auth
手順2. 環境の構築
2-1. 事前準備:動作検証用ユーザの作成
// 動作確認用ユーザの作成
# useradd sample-user
// 動作確認用ユーザのパスワード設定
# passwd sample-user
Changing password for user sample-user.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
2-2. アクセス制御ファイルの作成
# cat << EOT > /etc/security/sshd_access.conf
+ : vagrant : ALL
- : sample-user : ALL
EOT
2-3. PAMの設定ファイルにアクセス制御ファイルを追記
以下の記述を設定ファイルの適切な箇所に追記する。
account required pam_access.so accessfile=/etc/security/sshd_access.conf
※ /etc/ssh/sshd_config
に UsePAM yes
が設定されている場合、ファイル保存と同時に設定が反映される為、注意すること!
#%PAM-1.0
auth required pam_sepermit.so
auth include password-auth
account required pam_nologin.so
account required pam_access.so accessfile=/etc/security/sshd_access.conf
account include password-auth
password include password-auth
# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session required pam_selinux.so open env_params
session required pam_namespace.so
session optional pam_keyinit.so force revoke
session include password-auth
2-4. 動作確認
- リモートサーバに対し、「vagrant」ユーザでアクセスし、ログインが出来ることを確認
- リモートサーバに対し、「sample-user」ユーザでアクセスし、ログインが出来ないことを確認
手順3. 手動による設定変更
「sample-user」をアクセス可能にする場合は以下のようにする。
+ : vagrant : ALL
+ : sample-user : ALL
設定ファイルの書式については、以下のサイトを参照のこと。
手順4. Scriptによる設定変更
- Scriptの実行によってアクセス制御ファイルの該当行を修正
即席shellscript
#!/bin/bash
echo "[INFO] 特定ユーザに対するアクセス設定処理を開始します"
# Constant
#======================================================
FILE_ACCESS_CONF=/etc/security/sshd_access.conf
echo "[INFO] Target Configuration File : "${FILE_ACCESS_CONF}
# Arguments
#======================================================
ARGS=$#
ARG_USER_NAME=$1
ARG_ACCESS_PATTERN=$2
if [ ${ARGS} -ne 2 ]; then
echo "[ERROR] 引数の数が異なります"
exit 1
fi
echo "[INFO] User Name :" ${ARG_USER_NAME}
echo "[INFO] Access Pattern : " ${ARG_ACCESS_PATTERN}
# Procedure
#======================================================
# 設定ファイルに設定が存在するか確認
#------------------------------------------------------
grep -e ": ${ARG_USER_NAME} :" -n ${FILE_ACCESS_CONF} > /dev/null
RESULT_CHECK_USER_SETTING=$?
if [ ${RESULT_CHECK_USER_SETTING} -eq 0 ]; then
echo "[INFO] 設定が存在する為、既存の設定を削除して新しい設定を追加します"
sed -i".org" -e "/: ${ARG_USER_NAME} :/d" ${FILE_ACCESS_CONF}
else
echo "[INFO] 設定が存在しません"
fi
# 設定ファイルに新しい設定を追加
#------------------------------------------------------
ACCESS_PATTERN="-"
if [ ${ARG_ACCESS_PATTERN} = "allow" ]; then
echo "[INFO] 許可設定を追加します"
ACCESS_PATTERN="+"
elif [ ${ARG_ACCESS_PATTERN} = "deny" ]; then
echo "[INFO] 拒否設定を追加します"
else
echo "[ERROR] 権限の指定方法に誤りがあります"
exit 2
fi
echo "${ACCESS_PATTERN="-"}" ":" "${ARG_USER_NAME}" ":" "ALL" >> ${FILE_ACCESS_CONF}
echo "[INFO] cat" ${FILE_ACCESS_CONF}
cat ${FILE_ACCESS_CONF}
echo "[INFO] 特定ユーザに対するアクセス設定処理を終了します"
※アクセス元を ALL
としていますが、適切な設定に変更しましょう。
実行方法
許可する場合
# sh changeAccessAuth.sh sample-user allow
[INFO] 特定ユーザに対するアクセス設定処理を開始します
[INFO] Target Configuration File : /etc/security/sshd_access.conf
[INFO] User Name : sample-user
[INFO] Access Pattern : allow
[INFO] 設定が存在する為、既存の設定を削除して新しい設定を追加します
[INFO] 許可設定を追加します
[INFO] cat /etc/security/sshd_access.conf
+ : vagrant : ALL
+ : sample-user : ALL
[INFO] 特定ユーザに対するアクセス設定処理を終了します
拒否する場合
# sh changeAccessAuth.sh sample-user deny
[INFO] 特定ユーザに対するアクセス設定処理を開始します
[INFO] Target Configuration File : /etc/security/sshd_access.conf
[INFO] User Name : sample-user
[INFO] Access Pattern : deny
[INFO] 設定が存在する為、既存の設定を削除して新しい設定を追加します
[INFO] 拒否設定を追加します
[INFO] cat /etc/security/sshd_access.conf
+ : vagrant : ALL
- : sample-user : ALL
[INFO] 特定ユーザに対するアクセス設定処理を終了します
手順5. Script と Cronによる設定変更
- Script実行によってCronにアクセス終了時刻を設定し、アクセス不可の制御を自動設定するよう処理を実施
※以下のScriptは特定条件下では正常動作するが、もう少し精査と改修が必要な状態
#!/bin/bash
echo "[INFO] 特定ユーザに対するアクセス設定処理を開始します"
# Constant
#======================================================
# crontabファイル
cron_file="/var/spool/cron/root"
# Arguments
#======================================================
ARGS=$#
ARG_USER_NAME=$1
ARG_START_DATE=$2
ARG_START_TIME=$3
ARG_END_DATE=$4
ARG_END_TIME=$5
if [ ${ARGS} -ne 5 ]; then
echo "[ERROR] 引数の数が異なります"
exit 1
fi
# Variables
#======================================================
# 型 : "YYYYmmdd HHMM"
START_DATE="${ARG_START_DATE} ${ARG_START_TIME}"
END_DATE="${ARG_END_DATE} ${ARG_END_TIME}"
date +%Y%m%d-%H%M%S --date "${START_DATE}"
if [ $? -ne 0 ]; then
echo "[ERROR] 日時の指定に誤りがあります"
exit 2
fi
date +%Y%m%d-%H%M%S --date "${END_DATE}"
if [ $? -ne 0 ]; then
echo "[ERROR] 日時の指定に誤りがあります"
exit 2
fi
START_MONTH=`date +%m --date "${START_DATE}"`
START_DAY=`date +%d --date "${START_DATE}"`
START_HOUR=`date +%H --date "${START_DATE}"`
START_MINUTE=`date +%M --date "${START_DATE}"`
END_MONTH=`date +%m --date "${END_DATE}"`
END_DAY=`date +%d --date "${END_DATE}"`
END_HOUR=`date +%H --date "${END_DATE}"`
END_MINUTE=`date +%M --date "${END_DATE}"`
CRON_JOB_LINE_ALLOW_START="${START_MINUTE} ${START_HOUR} ${START_DAY} ${START_MONTH} * sh /root/sh/changeAccessAuth.sh ${ARG_USER_NAME} allow"
CRON_JOB_LINE_ALLOW_END="${END_MINUTE} ${END_HOUR} ${END_DAY} ${END_MONTH} * sh /root/sh/changeAccessAuth.sh ${ARG_USER_NAME} deny"
# Procedure
#======================================================
## Crontab ファイルが存在しない場合、作成する.
#------------------------------------------------------
[ -f ${cron_file} ] && touch ${cron_file}
## 既に登録されているかどうかを判定し、なければ登録する
#------------------------------------------------------
### アクセス許可のcron設定
cron_job_line_for_grep="${CRON_JOB_LINE_ALLOW_START//\\/\\\\}"
if [ `grep "${cron_job_line_for_grep}" "${cron_file}" | wc -l` -eq 0 ] ; then
echo "[INFO] Not registered yet. begin registering..."
# 追記
echo "${CRON_JOB_LINE_ALLOW_START}" >> "${cron_file}"
else
echo "[INFO] already registered."
fi
### アクセス拒否のcron設定
cron_job_line_for_grep="${CRON_JOB_LINE_ALLOW_END//\\/\\\\}"
if [ `grep "${cron_job_line_for_grep}" "${cron_file}" | wc -l` -eq 0 ] ; then
echo "[INFO] Not registered yet. begin registering..."
# 追記
echo "${CRON_JOB_LINE_ALLOW_END}" >> "${cron_file}"
else
echo "[INFO] already registered."
fi
# cronを再起動することで設定を反映する
/etc/init.d/crond restart
echo "[INFO] 特定ユーザに対するアクセス設定処理を終了します"
実行方法とCronの設定状況の確認
// アクセスの許可設定
# sh setAccessAuth.sh sample 20180714 0910 20180714 0911
[INFO] 特定ユーザに対するアクセス設定処理を開始します
20180714-091000
20180714-091100
[INFO] Not registered yet. begin registering...
[INFO] Not registered yet. begin registering...
Stopping crond: [ OK ]
Starting crond: [ OK ]
[INFO] 特定ユーザに対するアクセス設定処理を終了します
// Cronの設定状況
# cat /var/spool/cron/root
10 09 14 07 * sh /root/sh/changeAccessAuth.sh sample allow
11 09 14 07 * sh /root/sh/changeAccessAuth.sh sample deny
Cron処理実行後の設定の変更結果
// 許可処理の実行後
# cat /etc/security/sshd_access.conf
+ : vagrant : ALL
+ : sample : ALL
// 拒否処理の実行後
# cat /etc/security/sshd_access.conf
+ : vagrant : ALL
- : sample : ALL
手順6. 履歴取得
こういう仕組みを構築する場合は、過去のアクセスログを取得したいという要望も発生しがちなので、Script内に以下のようなコマンドを仕込んでログする処理を組み込んでおくと、内部監査などの際に便利である。
-
last
: 直近のログイン履歴を確認する -
lastlog
: すべてのユーザの最終ログインを確認する
# last
vagrant pts/0 10.0.2.2 Sat Jul 14 05:01 still logged in
vagrant pts/0 10.0.2.2 Sat Jul 14 05:00 - 05:01 (00:00)
vagrant pts/1 192.168.33.1 Sat Jul 14 04:47 - 04:47 (00:00)
sample-u pts/1 192.168.33.1 Sat Jul 14 04:47 - 04:47 (00:00)
vagrant pts/1 192.168.33.1 Sat Jul 14 04:47 - 04:47 (00:00)
sample-u pts/1 192.168.33.1 Sat Jul 14 04:43 - 04:45 (00:01)
vagrant pts/1 192.168.33.1 Sat Jul 14 04:43 - 04:43 (00:00)
sample-u pts/1 192.168.33.1 Sat Jul 14 04:40 - 04:40 (00:00)
sample-u pts/1 192.168.33.1 Sat Jul 14 04:38 - 04:39 (00:01)
vagrant pts/1 192.168.33.1 Sat Jul 14 04:37 - 04:37 (00:00)
vagrant pts/0 10.0.2.2 Sat Jul 14 04:08 - 05:00 (00:51)
reboot system boot 2.6.32-696.30.1. Sat Jul 14 04:08 - 05:30 (01:22)
reboot system boot 2.6.32-696.30.1. Fri Jun 8 16:29 - 23:33 (07:03)
reboot system boot 2.6.32-696.el6.x Fri Jun 8 16:25 - 16:28 (00:03)
# lastlog
Username Port From Latest
root **Never logged in**
bin **Never logged in**
daemon **Never logged in**
adm **Never logged in**
lp **Never logged in**
sync **Never logged in**
shutdown **Never logged in**
halt **Never logged in**
mail **Never logged in**
uucp **Never logged in**
operator **Never logged in**
games **Never logged in**
gopher **Never logged in**
ftp **Never logged in**
nobody **Never logged in**
rpc **Never logged in**
vcsa **Never logged in**
saslauth **Never logged in**
postfix **Never logged in**
rpcuser **Never logged in**
nfsnobody **Never logged in**
sshd **Never logged in**
vagrant pts/0 10.0.2.2 Sat Jul 14 05:01:59 +0000 2018
vboxadd **Never logged in**
sample-user pts/1 192.168.33.1 Sat Jul 14 04:47:16 +0000 2018
まとめ
- sshd と PAM を使用することで、ユーザのアクセス制御を簡単に設定することが出来る。
- Script と組み合わせることで、特定時間のアクセス制御などを設定することが出来る。
所感・メモ
この設定はクラウドで自動拡張・自動縮退するようなサーバでは使い勝手が悪い印象だ。
一方で、オンプレサーバやレンタルの専有サーバなど、特定用途のサーバでこういった制御により不正アクセスのリスクを軽減できるはずである。特に、Webホスティング系のサービスでは、コンテンツの更新時などに使用すると、運用担当者の「こっそりコンテンツ更新」みたいなことも制御することが出来る。
また、アプリケーション系のサーバシステムにおいては、プロキシサーバにこの設定をしておくことで、システム運用メンテのタイミングで担当者ごとのアクセス制御も可能になる。ただし、設定の変更にあたっては管理者アカウントによる設定ファイルが変更になるし、システムメンテを実施する担当者は相当なアクセス権限もあるはずなので、果たしてこのレイヤでの制御が妥当なセキュリティ制御かと考えると疑問が残る。(PCの紛失や情報漏えいの際、管理者権限さえ盗まれていなければアクセス出来ない状態を担保出来るというケースにおいては、有効かもしれない。)
この設定はあくまで、「iptablesなどのFirewallによって、接続可能な IP や Port を絞る・変更する」であったり、「rootでの直アクセス禁止する」だったりなど、基本的なアクセス制御をした上で、更にセキュリティレベルを上げたい時に使用する際に行う設定であるということは、最後に記しておこう。