前回の続きのようなもの。
はじめに
新しい要件を抜粋すると以下の通りです。
- sshログイン可能なユーザを一部に限定したい
- シェルログインが必要なユーザはsshログインできるユーザからsuする
- ログイン可能なユーザへの直接のsuは不可とする
- rootにsuできるユーザは限定する
- suする際はパスワード認証を不要とする
これを受けて自分が設計したのが以下のユーザ構成です。
個人的な意見(主にセキュリティ面で)は差し控えますが、最後の要件が曲者でパッと思いつくのはsudoのNOPASSWD
ラベルを使うことでした。
しかしながらrootにsuできるユーザを限定する要件でホワイトリストにせざるを得ず、su先のユーザが増えると地獄なので不採用としました。
結局、みんなが大好きなPAMで制御する方針で実装。
検証
事前準備
検証用のユーザを作成します(前述の図に出てきたグループはPrimaryGroupとしては使わないのがポイント)。
# for usr in admin opusr appX appY appZ ; do useradd -g users $usr && echo "$usr:secret" | chpasswd && id $usr ; done
uid=1001(admin) gid=100(users) groups=100(users)
uid=1002(opusr) gid=100(users) groups=100(users)
uid=1003(appX) gid=100(users) groups=100(users)
uid=1004(appY) gid=100(users) groups=100(users)
uid=1005(appZ) gid=100(users) groups=100(users)
制御用グループを作成します。内訳は以下の通り。
GroupName | ssh | su(root) | su(admins) | su(ops) | su(works) | Desc |
---|---|---|---|---|---|---|
rusers | ○ | × | × | × | × | sshログイン許可グループ |
admins | × | ○ | - | × | ○ | su許可グループ① |
ops | × | × | × | - | ○ | su許可グループ② |
works | × | × | × | × | - | su対象グループ |
# for grp in rusers admins ops works ; do groupadd $grp && grep "^$grp" /etc/group ; done
rusers:x:1001:
admins:x:1002:
ops:x:1003:
works:x:1004:
sshログインできるユーザの制御
sshd_configでAllowGroups
を設定します。
+AllowGroups rusers
# sshd -t && echo OK
OK
# systemctl reload sshd.service
# systemctl status sshd.service
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since ...
sshログインを許可したいユーザにrusers
を付与します。
# for usr in admin opusr ; do usermod -aG rusers $usr && id $usr ; done
uid=1001(admin) gid=100(users) groups=100(users),1001(rusers)
uid=1002(opusr) gid=100(users) groups=100(users),1001(rusers)
クライアントから接続テスト。
❯ ssh 192.168.1.30 -l admin
Warning: Permanently added '192.168.1.30' (ECDSA) to the list of known hosts.
admin@192.168.1.30's password:
[admin@clt00 ~]$
❯ ssh 192.168.1.30 -l appX
Warning: Permanently added '192.168.1.30' (ECDSA) to the list of known hosts.
appX@192.168.1.30's password:
Permission denied, please try again.
AllowGroups
によって制限された場合は以下のログが出ます。
Sep 17 10:37:56 clt00 sshd[701]: User appX from 192.168.1.1 not allowed because none of user's groups are listed in AllowGroups
suできるユーザの制御
PAMの設定ファイルを更新します。デフォルトの設定との差分は以下の通り。
#%PAM-1.0
auth sufficient pam_rootok.so
# Uncomment the following line to implicitly trust users in the "wheel" group.
#auth sufficient pam_wheel.so trust use_uid
# Uncomment the following line to require a user to be in the "wheel" group.
#auth required pam_wheel.so use_uid
+auth sufficient pam_succeed_if.so use_uid user ingroup admins
+auth sufficient pam_succeed_if.so use_uid user ingroup ops
+auth required pam_deny.so
auth substack system-auth
auth include postlogin
+
account sufficient pam_succeed_if.so uid = 0 use_uid quiet
+account [success=ignore default=1] \
+ pam_succeed_if.so use_uid user ingroup admins
+account sufficient pam_succeed_if.so user in root
+account sufficient pam_succeed_if.so user ingroup works
+account required pam_deny.so
account include system-auth
+
password include system-auth
+
session include system-auth
session include postlogin
session optional pam_xauth.so
追加したエントリをざっくり説明すると以下の通りです。
-
auth
でsuできるユーザを制御する - adminsグループのユーザはsuを許可する
- opsグループのユーザはsuを許可する
-
account
でsu先のユーザを制御する - adminsグループのユーザはsu先がrootユーザの場合、即時OKとする
- su先がworksグループのユーザの場合、即時OKとする
- system-authの
account
はユーザの正統性が確認されるとOKを返してしまうので、その前にNGを入れておく(system-authのincludeは正直消しても良いです)
エントリを書く際に注意したいのが、pam_succeed_if
モジュールのuse_uid
の有無です。モジュールの説明がちょっとわかりづらいんですが、suで利用した場合は以下の挙動となります。
-
use_uid
をセットする → suで切り替える前のユーザで判定 -
use_uid
をセットしない → suで切り替えた後のユーザで判定
これでグループに対する制御設定が完了したので、あとはグループの定義に応じてSubGroupに追加していくだけです。
# for usr in admin ; do usermod -aG admins $usr ; id $usr ; done
uid=1001(admin) gid=100(users) groups=100(users),1001(rusers),1002(admins)
# for usr in opusr ; do usermod -aG ops $usr ; id $usr ; done
uid=1002(opusr) gid=100(users) groups=100(users),1001(rusers),1003(ops)
# for usr in appX appY appZ ; do usermod -aG works $usr ; id $usr ; done
uid=1003(appX) gid=100(users) groups=100(users),1004(works)
uid=1004(appY) gid=100(users) groups=100(users),1004(works)
uid=1005(appZ) gid=100(users) groups=100(users),1004(works)
各ユーザでsuのテスト。
[admin@clt00 ~]$ su -
[root@clt00 ~]# logout
// admin->root: OK
[admin@clt00 ~]$ su - opusr
su: Authentication failure
// admin->opusr: NG
[admin@clt00 ~]$ su - appX
[appX@clt00 ~]$ logout
[admin@clt00 ~]$ su - appY
[appY@clt00 ~]$ logout
[admin@clt00 ~]$ su - appZ
[appZ@clt00 ~]$ logout
// admin->app[XYZ]: OK
[opusr@clt00 ~]$ su -
su: Authentication failure
// opusr->root: NG
[opusr@clt00 ~]$ su - admin
su: Authentication failure
// opusr->admin: NG
[opusr@clt00 ~]$ su - appX
[appX@clt00 ~]$ logout
[opusr@clt00 ~]$ su - appY
[appY@clt00 ~]$ logout
[opusr@clt00 ~]$ su - appZ
[appZ@clt00 ~]$ logout
// opusr->app[XYZ]: OK
最後に
「グループに権限を持たせて、その付け外しでユーザの権限制御を行う」のはAWSなどのクラウドを始めとして、一般的なやり方になってきたと思いますが、今回はそれを利用してみました。
今回の設定の場合、直接admin→opusrでsuはできませんがrootを経由すればopusrにsuできてしまうことには留意してください(一応防ぐ手立てはありますが、rootユーザにsuできる時点でPAMの設定も更新できてしまうのであまり意味はありません・・・)。
また、suする頻度が多いとログもその分増えるので、ある程度検証ができたらquiet
オプションを足してログの出力を抑えることも検討した方がよいです。