本記事は フューチャー Advent Calendar 2023 10日目の記事です。
前日の9日目は、@domori さんの「ISUCON入門」でした。パフォーマンスチューニング力を競う「ISUCON」の魅力発信に加えて、今後も ISUCON 対策で実践した内容を投稿されるとのことなので、どんなチューニングネタがあるのか楽しみになる記事でした!私もチャンスがあれば、ISUCON のような「技術スキルを競い合うイベント」に参加したいなと思いました。
はじめに
普段の開発は Mac オンリーにしたいのですが、現在は訳あって Windows で開発しています。Windows での開発事情に疎いため絶賛キャッチアップ中なのですが、WSL2(Windows Subsystem for Linux 2)を使えば「ほぼ Linux 環境」として環境構築とコーディングが進められることに(今更ながら)気付き、「もろもろの設定」に WSL2 を加えて、プロダクト開発を進めています。
WSL2 で Ubuntu や CentOS の環境構築を進める中で、何度か「Permission denied」にぶつかり、「ああ、sudo を忘れていたな」を繰り返す中で「そういえば、sudoって、どういう権限制御になっているんだっけ?」の疑問が浮かびましたので、Linux の知識整理を兼ねて sudo の実行制御を本記事にまとめました。
TL;DR
Q. Linux の一般ユーザが sudo を使える理由は?
A. /etc/sudores
に記載されているからです。
もう少しだけ踏み込むと
-
/etc/sudores
、及び、/etc/sudoers.d
配下のファイルに、sudo の実行制御が記載されています - 特定のユーザ、または、特定のグループに所属する全ユーザの sudo 実行権限は、上記ファイルを確認すれば分かります
Linux 前提知識
sudo の解説に入る前に、前提知識として必要なところを説明します。
- ユーザ情報
- グループ情報
ユーザ情報の確認
まずは、Linux の「ユーザ」についてです。
各ユーザの ID と所属グループは、以下のコマンドにより確認できます。
# コマンド
$ id # 現在ログイン中のユーザの情報
$ id <user_name> # <user_name>で指定したユーザの情報
実行例
$ id
uid=1000(main-user) gid=1000(main-user) groups=1000(main-user),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),116(netdev)
$ id sub-user
uid=1002(sub-user) gid=1002(sub-user) groups=1002(sub-user)
また、ユーザの一覧は /etc/passwd
に記載されています。
(※以下、本記事に記載されたファイルパスについて、一部の Linux ディストリビューションではパスが異なる場合があります)
$ sudo cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
...
main-user:x:1000:1000:,,,:/home/main-user:/bin/bash
sub-user:x:1002:1002:,,,:/home/sub-user:/bin/bash
各項目は「:」ごとに分かれて、それぞれは以下を意味しています。
ユーザ名:旧パスワード欄(現在は「x」で統一):ユーザID:グループID:コメント:ホームディレクトリ:デフォルトシェル
所属グループの確認
続いて「グループ」の確認方法です。
ユーザは必ず1つ以上のグループに所属します。
# コマンド
$ groups # 現在ログイン中のユーザが所属するグループ一覧
$ groups <user_name> # <user_name>で指定したユーザが所属するグループ一覧
実行例
$ groups
main-user adm dialout cdrom floppy sudo audio dip video plugdev netdev
$ groups sub-user
sub-user : sub-user
グループの一覧は、/etc/group
に記載されています。
$ cat /etc/group
root:x:0:
...
sudo:x:27:main-user
main-user:x:1000:
sub-user:x:1002:
それぞれの項目は、以下内容が対応しています。
グループ名:旧パスワード欄(現在は「x」で統一):グループID:グループ所属ユーザ
各ユーザを最低でも1つのグループに所属させるため、多くの Linux ディストリビューションでは「一般ユーザの追加時に、ユーザと同名のグループを作成して、そのグループにユーザ所属させる」という形を取ります。このため、グループ一覧にユーザ名と同名のグループが見つかります。(もちろん、全てのユーザを同じグループに追加する例外ケースもあります。)また、ユーザ名とグループ名が同名の場合は、「グループ所属ユーザ」の記入をスキップできます。(ex. main-userユーザはmain-userグループに所属していますが、/etc/group
に記載されたmain-userグループの「グループ所属ユーザ」には、main-userユーザが記載されていない)
以上で、前提知識の説明は終了です。
新規ユーザを作成して、sudo を使えるようにする
それでは、ハンズオンに入ります。
本ハンズオンでは、Linux に新規ユーザを作成し、そのユーザが sudo を使えるようになるまでのプロセスを辿ります。これにより「Linux ユーザが sudo を実行できる仕組み」を見ていきます。
各種コマンド操作のため「sudo が実行できるユーザ」で、以下の操作を進めてください。
新規ユーザを作成する
まず、新しいユーザ(今回は、beginner
)を追加します。
$ sudo adduser beginner
Adding user `beginner' ...
Adding new group `beginner' (1003) ...
Adding new user `beginner' (1003) with group `beginner' ...
Creating home directory `/home/beginner' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for beginner
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] Y
追加後、ユーザ情報とグループ情報を確認します。
$ id beginner
uid=1003(beginner) gid=1003(beginner) groups=1003(beginner)
$ sudo cat /etc/passwd | grep beginner
beginner:x:1003:1003::/home/beginner:/bin/sh
$ groups beginner
beginner : beginner
$ cat /etc/group | grep beginner
beginner:x:1003:
beginner
は、ユーザ名と同じ名前のグループに所属していますね。
この状況で beginner
にスイッチして、sudo
を実行してみます。
$ su beginner #beginnerにスイッチ
Password:
$ cat /etc/passwd
cat: /etc/passwd: Permission denied
$ sudo cat /etc/passwd #sudoでファイルの中身を見ようとするが、見れない。
[sudo] password for beginner:
beginner is not in the sudoers file. This incident will be reported.
$ exit #beginnerからログアウト
exit
アカウント作成後に、追加で何も設定していないため sudo
は実行できません。
エラーメッセージの「sudoers file」がキーポイントです。
sudo を使えるようにする
先ほど追加したユーザ beginner
が sudo
を実行できるように設定します。
sudo
の実行制御は /etc/sudores
に記載されていますので、まずは設定ファイルの中身を確認します。
$ sudo cat /etc/sudoers
~
# User privilege specification
root ALL=(ALL:ALL) ALL
~
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL
~
読み方
どのユーザへの設定か 接続ホストはどこか = (どのユーザとして:どのグループとして) どのような操作を許可するのか
sudores
の読み方を踏まえると、
現在の設定では「rootユーザ」と「sudoグループへの所属ユーザ」に権限が与えられていることが分かります。
# rootユーザは、どのホストからの接続でも、すべての操作ができる
root ALL=(ALL:ALL) ALL
# sudoグループの所属ユーザは、どのホストからの接続でも、すべての操作ができる
%sudo ALL=(ALL:ALL) ALL
今やりたいことは「beginner
が sudo
を使えるようにする」ことです。
sudoers
には「sudoグループの所属ユーザは、すべての操作ができる」と設定されているので、
-
beginner
をsudoグループ
に所属させる -
sudo
コマンドを実行する
を試してみます。
beginner
を sudoグループ
に所属させる
まずは、sudo グループの状況を確認します。
$ cat /etc/group | grep sudo
sudo:x:27:main-user
このコマンドにより、コマンド実行時点でsudoグループに所属しているユーザの一覧が確認できます。
さっそく、beginner
をグループに追加します。
$ sudo usermod -aG sudo beginner
$ groups beginner
beginner : beginner sudo
$ cat /etc/group | grep sudo
sudo:x:27:main-user,beginner
sudoグループに、beginner
が追加されました。
sudo
コマンドを実行する
beginner
は sudoers
に設定された条件を満たしたので、sudo
が実行可能になりました。
$ su beginner
Password:
$ sudo cat /etc/passwd
[sudo] password for beginner: #先ほどは正しいパスワードを入力しても閲覧できなかったが、現時点では閲覧可能に
root:x:0:0:root:/root:/bin/bash
...
main-user:x:1000:1000:,,,:/home/main-user:/bin/bash
beginner:x:1003:1003:,,,:/home/beginner:/bin/bash
「この時点」ではエラーが出ていましたが、グループに所属したことで sudo
が実行できるようになりました。
もちろんグループから外せば、元通り sudo
は実行できなくなります。
$ exit #beginnerからログアウト
exit
$ sudo gpasswd -d beginner sudo #beginnerをsudoグループから外す
Removing user beginner from group sudo
$ cat /etc/group | grep sudo #sudoグループにbeginnerは残っていない
sudo:x:27:main-user
$ groups beginner #beginnerの所属グループはbeginnerのみ
beginner : beginner
$ su beginner
Password: #beginnerにスイッチ
$ sudo cat /etc/passwd
beginner is not in the sudoers file. This incident will be reported.
以上により、
任意のユーザを sudo グループに追加することで、sudo コマンドが実行可能となることが分かりました。
特定ユーザ専用の定義を追加する
ここまでは「sudores
に記載されてるグループに、sudo
を実行したいユーザを所属させる」という方法を取りました。
もう1つの方法として、root ユーザのように「特定のユーザの設定を記載する」ことでも sudo
の実行権限を与えることが可能です。
sudores
の編集は visudo
を使います。vim
などのテキストエディタによる編集も可能ですが、sudores
自体が sudo
コマンドの動作を制御する重要なファイルなため、誤った編集によりシステムを不安定にする可能性があります。visudo
にはファイルを保存する際に構文エラーをチェックしたり、同時編集による問題を防ぐ機能がありますので、sudores
の編集にはこのエディタを利用します。
$ sudo cat /etc/sudoers
#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
~
@includedir /etc/sudoers.d
また、設定ファイルの注意書きに「/etc/sudores
を直接編集するのではなく、/etc/sudoers.d/
に追加してください」とありますので、この指示に従います。
$ sudo visudo -f /etc/sudoers.d/privilegedUsers
beginner ALL=(ALL:ALL) ALL
visudo
はファイル保存直前に構文チェックが起動します。文法ミスがあれば編集を再開して修正しましょう。
$ sudo visudo -f /etc/sudoers.d/privilegedUsers
/etc/sudoers.d/privilegedUsers:1:14: syntax error
beginner ALL-(ALL:ALL) ALL #「=」にすべきところ「-」となっている
^
What now?
Options are:
(e)dit sudoers file again
(x)it without saving changes to sudoers file
(Q)uit and save changes to sudoers file (DANGER!)
beginner
の設定追記が完了したら、ユーザを切り替えて動作確認します。
$ su beginner
Password:
念のため、sudoグループに所属していないことを確認します。
$ groups
beginner #beginnerユーザが所属するのは、beginnerグループのみ
$ cat /etc/group | grep sudo
sudo:x:27:main-user #sudoグループに所属するのは、main-userのみ
以下のコマンドにより、/etc/sudoers.d/privilegedUsers
への設定通り、beginner
が sudo
を実行出来ることが確認できました。
$ sudo cat /etc/passwd
[sudo] password for beginner:
root:x:0:0:root:/root:/bin/bash
~
main-user:x:1000:1000:,,,:/home/main-user:/bin/bash
beginner:x:1003:1003:,,,:/home/beginner:/bin/bash
ここまでの作業を整理すると
①グループに権限を付与して、そのグループへの所属を通して、ユーザにsudo実行権限を与える
②ユーザに直接直接sudo実行権限を付与する
の2パターンが確認してきました。
以降からは、sudores
で使える便利なオプションを解説します。
「NOPASSWD」の利用
「NOPASSWD」を設定すると、sudo
コマンド実行時のパスワード要求をスキップできます。
ex. beginner
に NOPASSWD を設定する
sudo visudo -f /etc/sudoers.d/privilegedUsers
beginner ALL=(ALL:ALL) NOPASSWD:ALL
NOPASSWD設定後からは、 sudo
利用時のパスワード要求が無くなります。
$ su beginner
Password:
$ sudo cat /etc/passwd #パスワード要求なし
~
main-user:x:1000:1000:,,,:/home/main-user:/bin/bash
beginner:x:1003:1003:,,,:/home/beginner:/bin/bash
利用場面には注意ですが、動作検証を優先する場合などで活用できます。
「エイリアス」の利用
sudores
には、以下4つのエイリアスがあります。
- User_Alias
- Runas_Alias
- Host_Alias
- Cmnd_Alias
例えば、sudores
の記載内容が以下の状態だったとします。
eins-user ALL=(ALL:ALL) ALL
zwei-user ALL=(ALL:ALL) ALL
drei-user ALL=(ALL:ALL) ALL
vier-user ALL=(ALL:ALL) ALL
これらはエイリアスを使うことで、以下のようにまとめられます。
User_Alias USERS = eins-user,zwei-user,drei-user,view-user
USERS ALL=(ALL:ALL) ALL
エイリアスの利用により、ファイルの見通しが良くなりました。
おわりに
本記事では「そういえば、sudoって、どういう権限制御になっているんだっけ?」という疑問から、sudo 権限周りの設定方法についてハンズオン形式で解説してきました。このような「枯れた技術への質問」であれば ChatGPT が高い精度で回答してくれるのは間違いありませんが、1つ1つのコマンドを実際に試すことで「知っている」から「使える」にまで昇華することは、引き続き重要だなと感じました。
フューチャー Advent Calendar 2023 はまだまだ続きます。明日の記事も、お楽しみに!