7
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

suするuserを限定したい

Posted at

はじめに

個人的には「サーバのアカウント管理はシンプルにすべき」で、SSHログイン可能なユーザは

  • 管理ユーザ(wheelあり)
  • 構成管理用ユーザ(wheelなし、sudo設定あり)

の2ユーザを共通で用意しておけば十分だと考えています。1
ミドルウェアが利用するユーザのように増減することが滅多にないユーザは構成管理ツールを使えば管理コストはあまりかかりませんが、人間がログインして利用するユーザ(=増減する可能性が高いユーザ)をサーバ毎に管理するのは運用が辛くなります。

ただ、最近は1つのサーバに対して複数の組織体が関与して運用するプロジェクトに関わっており、管理ユーザを共用するのは主に監査の面でよろしくないため、組織毎に管理ユーザを作成しています。
このとき組織毎の管理ユーザは「wheelあり」とするか「wheelなし、sudo設定あり」のどちらかにするかと思いますが、前述したプロジェクトでは後者の方が一般的な設定かと思います。2
前者の場合、rootパスワードを知っている人がrootにスイッチ可能な状態ですが、特権ユーザを利用可能な組織が複数に渡るのは事故の元であり、「利用可能な人=パスワードを知っている人」という曖昧なグループは管理不可能だからです。

ところが「運用上の都合」でsuを組織毎の管理ユーザについても使えるようにしなければならないケースが出てきました。
単純にwheelグループに追加すると前述の問題があるので、

  • suを使うことができるユーザ
  • スイッチ先のユーザ

のそれぞれを管理できるようにしたいと考えていたところ、幸いにして昔からそういう要件はあったようで、今回はPermitting or Restricting a User's su Access to Privileged Accountsを参考にして

  • 組織A用ユーザ(wheelなし、ミドルウェアユーザへのスイッチ可)

というユーザを作成していきます。

PAMの動作概要(一部)

検証する前にPAMの設定ファイルの書式や動作概要を理解しておきます。
参考にしたドキュメントを以下に挙げておきます。

今回は認証周りなのでmodule interfaceはauthだけに絞って整理していきます。

PAM の設定ファイル

PAMの設定ファイルの書式は以下の通りです。

module_interface  control_flag  module_name  module_arguments

このうちControl Flagはmoduleの戻り値に応じた処理を定義します。
Control Flagの書式は

[value1=action1 value2=action2 ...]

となっており、"moduleの戻り値がvalueの場合はactionを実行する"という意味になります。
原則各行の処理結果をスタックしていき、それらが総合的に判断されてPAMの処理結果になります。
valueはPAMモジュールの戻り値の定義が多いので割愛しますが、actionとして利用できるものは以下の通りです。

Action 処理
ignore この行を無視する
bad この行を失敗とする
die badと同じだが、即時PAMの処理を終了する
ok この行を成功とする(直前の処理結果が失敗でない限り、この行の戻り値で上書きする)
done okと同じだが、即時PAMの処理を終了する
N この行を成功とし、次のN個のmoduleをスキップする(N>0としなければならない)
reset これまでの処理結果のスタックを初期化する

一般的にPAMのポリシーで使われているrequiredなどは利用頻度の高い処理を省略した表記になっています。

表記 本来の表記 処理
required [success=ok new_authtok_reqd=ok ignore=ignore default=bad] moduleの処理が成功しなかった場合、失敗とする
requisite [success=ok new_authtok_reqd=ok ignore=ignore default=die] moduleの処理が成功しなかった場合、失敗として即終了する
sufficient [success=done new_authtok_reqd=done default=ignore] moduleの処理が成功した場合、成功として即終了する
optional [success=ok new_authtok_reqd=ok default=ignore] moduleの処理結果は無視する

また、他のファイルの内容を読み込む用のフラグとしてincludesubstackがあります。

表記 意味
include 引数に指定されたファイルを読み込む(ファイルの内容をそのまま展開するようなイメージ)
substack 引数に指定されたファイルを読み込み、その処理結果をこの行の処理結果とする

上記の情報をもとにいくつかPAMの定義を見ていきます。

例1. system-auth

最終行で常にNGになるので、PAMの処理がOKになるのは3行目が成功したときだけになります。

auth        required      pam_env.so
auth        required      pam_faildelay.so delay=2000000
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
auth        required      pam_deny.so
  1. 環境変数をセットできなかったらNG
  2. 失敗時の遅延をセットできなかったらNG
  3. パスワード認証できたら即時OK
  • パスワードなし(nullok)
  • ユーザから取得済みのパスワードを利用し、取得していない場合は入力を促す(try_first_pass)
  1. UIDが1000未満の場合は即時NG
  • 成功ログは記録しない(quiet_success)
  1. 常にNG

例2. su

rootユーザ以外でPAMの処理がOKになるためは「wheelグループに所属していること」と「パスワード認証に通ること(=system-authの結果がOK)」の2つが必要となります。

auth		sufficient	pam_rootok.so
auth		required	pam_wheel.so use_uid
auth		substack	system-auth
auth		include		postlogin
  1. rootユーザの場合はOK
  2. 現在のUIDをもとに判定し、wheelグループに所属していない場合はNG
  3. system-authを読み込み、その結果をこの行の判定結果とする
  4. postloginを読み込む

例3. Knowledgebaseの設定

skipを使ってgroupaに所属していない場合は影響がでないポリシーになっています。

auth           [success=2 default=ignore] pam_succeed_if.so use_uid user notingroup groupa
auth           required pam_wheel.so use_uid group=groupa
auth           required pam_listfile.so item=user sense=allow onerr=fail file=/etc/security/su-groupa-access
  1. 現在のUIDをもとに判定し、groupaに所属していない場合は次の2つのモジュールをスキップする
  2. 現在のUIDをもとに判定し、groupaに所属していない場合はNG
  3. スイッチ先のユーザ名が/etc/security/su-groupa-accessに含まれていない場合はNG

実際にやってみる

  • OS: CentOS 7.6 (vagrant box)

事前準備

検証用のユーザの作成は以下の通り。

  • admin(管理者: wheelあり)
  • auser01(groupaユーザ, ansibleにのみスイッチ可能)
  • buser01(groupbユーザ, スイッチ禁止)
  • ansible(ミドルウェアユーザ)
グループ/ユーザ作成
# groupadd -g 1001 groupa
# groupadd -g 1002 groupb
# useradd -g users -G wheel admin
# useradd -g groupa auser01
# useradd -g groupb buser01
# useradd ansible
# echo 'admin:********' | chpasswd
# echo 'auser01:********' | chpasswd
# echo 'buser01:********' | chpasswd
# echo 'ansible:********' | chpasswd
確認
# id admin
uid=2004(admin) gid=100(users) groups=100(users),10(wheel)
# id auser01
uid=2005(auser01) gid=1001(groupa) groups=1001(groupa)
# id buser01
uid=2006(buser01) gid=1002(groupb) groups=1002(groupb)
# id ansible
uid=2007(ansible) gid=2007(ansible) groups=2007(ansible)

groupaに所属するユーザがスイッチ可能なユーザ一覧を記載するファイルを作成。

/etc/security/su-groupa-access
ansible

PAMの設定は以下の通り。

/etc/pam.d/su
 #%PAM-1.0
 auth		sufficient	pam_rootok.so
+auth		[success=1 ignore=1 default=ignore]	pam_wheel.so use_uid group=groupa
+auth		required	pam_wheel.so use_uid
+auth		[success=1 default=ignore]	pam_succeed_if.so use_uid user notingroup groupa
+auth		required	pam_listfile.so item=user sense=allow onerr=fail file=/etc/security/su-groupa-access
 auth		substack	system-auth
 auth		include		postlogin

2-3行目がsuできるユーザの判定、4-5行目がgroupaに対するスイッチ先のユーザの判定になっています。
pam_wheel.soが若干曲者でwheelグループに所属していると判定された場合、原則PAM_IGNOREを返す(debugオプションをつけると分かる)ので、ignoreに対してもskipを記載する必要があリます。

なおvagrant box利用時にrootまたはvagrant以外のユーザからrootユーザへのスイッチがaccountで許可されていない場合があるので注意して下さい。修正する箇所は以下の通り。

/etc/pam.d/su
 account		sufficient	pam_succeed_if.so uid = 0 use_uid quiet
 account		[success=1 default=ignore] pam_succeed_if.so user = vagrant use_uid quiet
-account		required	pam_succeed_if.so user notin root:vagrant
+account		required	pam_succeed_if.so user notin vagrant
 account		include		system-auth

確認

adminはrootもansibleもスイッチOK。

admin
[admin@localhost ~]$ su -
Password:
Last login: 日  4月  7 07:22:43 UTC 2019 on pts/1
[root@localhost ~]# logout
[admin@localhost ~]$ su - ansible
Password:
Last login: 日  4月  7 07:22:31 UTC 2019 on pts/1
[ansible@localhost ~]$ logout

auser01はrootはスイッチNG, ansibleはスイッチOK。

auser01
[auser01@localhost ~]$ su -
Password:
su: Authentication failure
[auser01@localhost ~]$ su - ansible
Password:
Last login: 日  4月  7 07:23:54 UTC 2019 on pts/1
[ansible@localhost ~]$ logout
[auser01@localhost ~]$

buser01はroot, ansibleともにスイッチNG。

buser01
[buser01@localhost ~]$ su -
Password:
su: Authentication failure
[buser01@localhost ~]$ su - ansible
Password:
su: Authentication failure
[buser01@localhost ~]$

ちなみにpam_wheel.soで弾かれた場合でもパスワードを間違えた場合でも、エラーメッセージは「Authentication failure」が出ますが、どちらで拒否されたか確認したいときは/var/log/secureを確認してください。

/var/log/secure(パスワードを間違えた場合)
Apr  7 07:49:13 localhost su: pam_unix(su-l:auth): authentication failure; logname=auser01 uid=2005 euid=0 tty=pts/1 ruser=auser01 rhost=  user=ansible
  1. 各サーバの前段にJump Box(踏み台)があり、そのJump Boxで個人ユーザが識別、認証できていることが前提です。

  2. 複数の組織体が関わる場合、作業範囲が定められており、必要な権限も決まっているのでsudoによるコマンドレベルの制御で大体事足りるはず。

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?