一人のユーザに複数のエントリがマッチするときは、順番に適用される。 複数の指定がマッチしている箇所については、最後にマッチしたものが 使用される (それが一番明示的なマッチだとはかぎらないが)。
以下では sudoers の文法を拡張 Backus-Naur 記法 (EBNF) を 使って記述する。EBNF がどんなものか知らないからといって、 あきらめないでいただきたい。わりと簡単なものだし、以下に出てくる定義には 詳しい説明を付けておきますから。
シンボル ::= 定義 | 別の定義 1 | 別の定義 2 ...
個々の生成規則は、ほかの生成規則を参照し、そのようにして言語の文法を 作り上げている。また EBNF には以下の演算子が含まれるが、 正規表現で御存じの読者も多いだろう。だが、いわゆる「ワイルドカード」文字と 混同しないでいただきたい。あれは別の意味を持っている。
丸カッコを使うと、複数のシンボルをグループにまとめることができる。 なお混乱を避けるため、以下の定義で (シンボル名ではなく) 文字どおりの 文字列や記号を示す場合は、それをシングルクォート ('') で囲むことにする。
Alias ::= 'User_Alias' User_Alias (':' User_Alias)* |
'Runas_Alias' Runas_Alias (':' Runas_Alias)* |
'Host_Alias' Host_Alias (':' Host_Alias)* |
'Cmnd_Alias' Cmnd_Alias (':' Cmnd_Alias)*
User_Alias ::= NAME '=' User_List
Runas_Alias ::= NAME '=' Runas_List
Host_Alias ::= NAME '=' Host_List
Cmnd_Alias ::= NAME '=' Cmnd_List
NAME ::= [A-Z]([A-Z][0-9]_)*
個々のエイリアスの定義は、次の形をとる。
Alias_Type NAME = item1, item2, ...
上記において、Alias_Type は
User_Alias
,
Runas_Alias
,
Host_Alias
,
Cmnd_Alias
のうちの一つである。
NAME
はアルファベットの大文字、数字、
アンダースコア ('_') からなる文字列であるが、先頭の文字は大文字で
なければならない。同じタイプのエイリアス定義を、コロンで (':') つないで、
一行に複数書くこともできる。たとえば、
Alias_Type NAME = item1, item2, item3 : NAME = item4, item5
以下では、エイリアスの有効な要素となるものを定義する。
User_List ::= User |
User ',' User_List
User ::= '!'* username |
'!'* '#'uid |
'!'* '%'group |
'!'* '+'netgroup |
'!'* '%:'nonunix_group |
'!'* User_Alias
User_List
は一個以上の、ユーザ名、uid ('#' が
頭に付く)、システムグループ名 ('%' が頭に付く) 、ネットグループ名
('+' が頭に付く)、非 UNIX グループ名 ('%:' が頭に付く)、
User_Alias
からなる。
リストの各項目の前には一個以上の '!' 演算子を付けてもよい。
奇数個の '!' はその項目の値を否定する。偶数個の場合は互い相殺するだけだ。
ユーザ名、グループ名、ネットグループ名、非 UNIX グループ名は、 ダブルクォートで囲めば、特殊文字をエスケープしないですむ。 ダブルクォートで囲まずに特殊文字を使いたいなら、エスケープした 16 進数を 指定してやればよい。たとえば、スペースなら \x20 という具合だ。
非 UNIX グループ名の書式は、利用するサービスの実装によって決まる。たとえば、 Quest Authentication Services の AD バックエンドは、 以下の書式をサポートしている。
グループ名を囲む引用符は任意であることに注意してほしい。文字列を 引用符で囲まない場合は、スペースや '@' 記号をエスケープするために、 バックスラッシュ (\) を使わなければならない。
Runas_List ::= Runas_Member |
Runas_Member ',' Runas_List
Runas_Member ::= '!'* username |
'!'* '#'uid |
'!'* '%'group |
'!'* +netgroup |
'!'* Runas_Alias
Runas_List
は
User_List
に似ている。
違うのは、
User_Alias
ではなく、
Runas_Alias
が使えることだ。ユーザ名やグループ名の
マッチは文字列として行われることに気を付けてほしい。言い換えると、
二つのユーザ名 (あるいはグループ名) は、かりに同じ uid (gid) を
持っていても、別個のものと見なされるのである。
だから、もし同じ uid を持ったすべてのユーザ名にマッチさせたかったら
(たとえば、root と toor がそうだとしよう)、ユーザ名の代わりに uid を
使えばよい (この例なら、#0 である)。
Host_List ::= Host |
Host ',' Host_List
Host ::= '!'* hostname |
'!'* ip_addr |
'!'* network(/netmask)? |
'!'* '+'netgroup |
'!'* Host_Alias
Host_List
は一個以上の、ホスト名、IP アドレス、
ネットワークアドレス、ネットグループ名 (頭に '+' が付く)、および
他のエイリアスからなる。ここでもまた、'!' 演算子を付けて、項目の値を
否定することができる。ネットワークアドレスにネットマスクを
指定しなかった場合は、sudo がローカルホストの
ネットワークインターフェースを一つ一つ参照し、指定された
ネットワークアドレスと同じアドレスを持つインターフェースがあれば、
そのネットマスクを使用することになる。ネットマスクの指定は、
標準の IP アドレス表記 (たとえば 255.255.255.0 とか
ffff:ffff:ffff:ffff:: とか) でもよく、CIDR 表記 (ビット数、
たとえば 24 とか 64 とか) でもよい。ホスト名の一部にシェル風の
ワイルドカードを使用することができるが (下記のワイルドカードのセクションを
参照)、使用マシンの
hostname
コマンドが
完全修飾ドメイン名を返さない場合、ワイルドカードを
利用するには fqdn オプションを使う必要がある。
Cmnd_List ::= Cmnd |
Cmnd ',' Cmnd_List
commandname ::= filename |
filename args |
filename '""'
Cmnd ::= '!'* commandname |
'!'* directory |
'!'* "sudoedit" |
'!'* Cmnd_Alias
Cmnd_List
は一個以上の、コマンド名、ディレクトリ、
他のエイリアスからなるリストである。コマンド名は絶対パスのファイル名で
あり、シェル風のワイルドカードを含んでいても構わない (下記のワイルドカードの
セクションを参照)。単にファイル名だけ指定した場合、ユーザはお望みの
どんな引き数でも付けてそのコマンドを実行することができる。とは言え、
コマンドライン引き数を (ワイルドカードを含めて) 指定しても構わないし、
また、引き数に "" を指定して、そのコマンドは
コマンドライン引き数なしの実行のみが可能だと指示することもできる。
ディレクトリは '/' で終わる絶対パス名である。
Cmnd_List
に
ディレクトリを指定すると、ユーザーはそのディレクトリ内の任意のファイルを
実行できるようになる (だが、そのサブディレクトリにあるファイルは実行できない)。
Cmnd
がコマンドライン引き数を伴っている場合は、
Cmnd
中の引き数は、ユーザがコマンドラインに打ち込む
引き数と正確に一致しなければならない (Cmnd 中の引き数にワイルドカードが
あるならば、それがコマンドラインの引き数とマッチしなければならない)。
以下に挙げる文字をコマンド引き数の中で用いるときは、'\' によって
エスケープしなければならないことに注意してほしい。 ',', ':', '=', '\' が
それである。スペシャルコマンド "sudoedit" は、ユーザが
sudo を -e オプション付きで (あるいは、sudoedit という
コマンド名で) 実行することを許可するために使用する。この場合、
コマンドライン引き数を取ることができるのは、普通のコマンドとまったく
同様である。
Default_Type ::= 'Defaults' |
'Defaults' '@' Host_List |
'Defaults' ':' User_List |
'Defaults' '!' Cmnd_List |
'Defaults' '>' Runas_List
Default_Entry ::= Default_Type Parameter_List
Parameter_List ::= Parameter |
Parameter ',' Parameter_List
Parameter ::= Parameter '=' Value |
Parameter '+=' Value |
Parameter '-=' Value |
'!'* Parameter
パラメータはフラグ、整数値、文字列、リスト の
どれでもよい。フラグは要するにブーリアン (真偽値) であり、'!' 演算子で
off にできる。整数値、文字列、リストのパラメータにも、真偽値として使用して、
それを無効にできるものがいくつか存在する。パラメータの値が複数の単語を
含むときは、値をダブルクオート (
"
) で囲むとよい。
特殊文字はバックスラッシュ (
\
) でエスケープすることが
できる。
リストには代入演算子が
=
のほかにもう二つある。
+=
と
-=
である。こうした演算子は
それぞれリストに付け加えたり、リストから削除したりするのに使用する。
-=
演算子を使って、リストに存在しない要素を
消去しようとしても、エラーにはならない。
Defaults 行の解析は、次の順序で行われる。最初に汎用、Host、User の Defaults が解析され、それから Runas、最後にコマンドの Defaults の順番になる。
Defaults 行で使用できるパラメータのリストについては、 「SUDOERS のオプション」を御覧いただきたい。
User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \
(':' Host_List '=' Cmnd_Spec_List)*
Cmnd_Spec_List ::= Cmnd_Spec |
Cmnd_Spec ',' Cmnd_Spec_List
Cmnd_Spec ::= Runas_Spec? Tag_Spec* Cmnd
Runas_Spec ::= '(' Runas_List? (':' Runas_List)? ')'
Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' |
'SETENV:' | 'NOSETENV:' )
ユーザの設定は、あるユーザが指定されたホストで (どのユーザに 変身して) どのコマンドを実行できるかを決定する。デフォルトでは、 コマンドは root に変身して実行されるが、これはコマンドごとに 変更することができる。
ユーザの設定の基本構造は、「誰が どこで = (誰に変身して) 何を」 である (who where = (as_whom) what)。 構成部分に分けて説明しよう。
Runas_Spec
は、それに続くコマンドに対してデフォルトを
定める。それはどういうことかと言うと、次のようなエントリがあったとしよう。
dgb boulder = (operator) /bin/ls, /bin/kill, /usr/bin/lprm
ユーザ dgb は /bin/ls, /bin/kill, /usr/bin/lprm を実行することができる。ただし、operator として実行できるだけだ。たとえば、次のようにである。
$ sudo -u operator /bin/ls
エントリの後ろの方の
Runas_Spec
を変更することも可能だ。
上のエントリをこんなふうに書き変えたとしよう。
dgb boulder = (operator) /bin/ls, (root) /bin/kill, /usr/bin/lprm
すると、ユーザ dgb は、/bin/ls こそ operator としてだが、 /bin/kill や /usr/bin/lprm は root の資格で 実行できるようになる。
dgb が
/bin/ls
を実行するとき、
変身対象ユーザとグループのどちらでも operator にできるように、
この記述を拡張することもできる。
dgb boulder = (operator : operator) /bin/ls, (root) /bin/kill, \
/usr/bin/lprm
次の例では、ユーザ tcm が モデムのデバイスファイルにアクセスする コマンドを dialer グループとして実行できるようにしている。 この例では、グループしか指定できないことに注意してほしい。コマンドは ユーザ tcm の資格で実行されるのである。
tcm boulder = (:dialer) /usr/bin/tip, /usr/bin/cu, \
/usr/local/bin/minicom
デフォルトでは、sudo はコマンドを実行する前に、
ユーザが本人であることを証明するように求める。この振舞いは
NOPASSWD
タグによって変更することができる。
Runas_Spec
と同様、
NOPASSWD
タグも
Cmnd_Spec_List
中のそれに続くコマンドに対して
デフォルトを定める。
PASSWD
の働きは反対であり、
振舞いを元に戻したいときに使える。たとえば、
ray rushmore = NOPASSWD: /bin/kill, /bin/ls, /usr/bin/lprm
とすれば、ユーザ ray はマシン rushmore 上で認証をしないでも root として /bin/kill, /bin/ls, /usr/bin/lprm を実行できるようになる。もし ray が パスワードなしで実行できるコマンドを /bin/kill だけに 絞りたいのなら、エントリはこうなるだろう。
ray rushmore = NOPASSWD: /bin/kill, PASSWD: /bin/ls, /usr/bin/lprm
ただし、ユーザが exempt_group オプションで指定されているグループに
属する場合は、
PASSWD
タグが効果を持たないことに
注意してほしい。
デフォルトでは、現在使用中のホストに関するユーザのエントリのうちに
NOPASSWD
タグが指定されているものが一つでもあれば、
そのユーザはパスワードなしで
sudo -l
を実行できる。
なお、ユーザがパスワードなしで
sudo -v
を実行できるのは、
現在使用中のホストに関するそのユーザのエントリのすべてで
NOPASSWD
タグが生きているときのみである。この動作は、
verifypw や listpw オプションによって変更できる。
sudo が noexec サポートつきでコンパイルされ、
下で稼働しているオペレーティングシステムがそれに対応している場合、
NOEXEC
タグを利用すれば、動的にリンクされた実行ファイルが
そこからさらにコマンドを実行するのを防ぐことができる。
次の例では、ユーザ aaron は /usr/bin/more と /usr/bin/vi を実行できるが、シェル・エスケープは利用できない。
aaron shanty = NOEXEC: /usr/bin/more, /usr/bin/vi
NOEXEC
がどんなふうに働くのか、お使いのシステムで
利用できるかどうか、などについてさらに詳しく知りたかったら、
「シェル・エスケープを防止する」のセクションを御覧になっていただきたい。
上記のタグは setenv オプションの値をコマンドごとに変更する。
注意すべきは、あるコマンドに対して
SETENV
が
設定されていると、コマンドラインから設定するどんな環境変数も env_check,
env_delete, env_keep による規制を受けないということである。
だから、そうした形で環境変数を設定することを許可するのは、
信用できるユーザだけに限るべきだ。なお、マッチするコマンドが
ALL だった場合は、暗黙のうちに
SETENV
タグが
そのコマンドに付けられるが、このデフォルトの動作は
UNSETENV
タグを使えば打ち消すことができる。
使用システムの glob(3) や fnmatch(3) 関数が POSIX の文字クラスに対応しているなら、文字クラスも使用できる。 ただし、':' 文字は、sudoers で特別な意味を 持っているので、エスケープしなければならない。一例を上げると、
/bin/ls [[\:alpha\:]]*
上記は、文字で始まるどんなファイル名にもマッチするだろう。
コマンドのパス名に使われたワイルドカードはフォワードスラッシュ ('/') にマッチしないことに注意してほしい。だが、コマンドライン引き数との マッチングでは、ワイルドカードはスラッシュとしっかりマッチする。 そこで、
/usr/bin/*
というコマンドパスは、/usr/bin/who とマッチするが、 /usr/bin/X11/xterm とはマッチしないことになる。
この方法を使えば、たとえば、サイト全体で使用する sudoers ファイルの ほかに、マシンごとのローカルな sudoers ファイルを持つことができる。 ここでは、サイト全体の sudoers を /etc/sudoers とし、 マシンごとの方は /etc/sudoers.local とすることにしよう。 /etc/sudoers に /etc/sudoers.local をインクルードするには、 /etc/sudoers 中に次の行を書き込む。
sudo は解析中この行に出会うと、カレントファイル (/etc/sudoers だ) の処理を一時中止し、処理の対象を /etc/sudoers.local に切り替える。そして、 /etc/sudoers.local の末尾まで達したら、/etc/sudoers の 残りを処理するのだ。インクルードされたファイルがさらに他のファイルを インクルードしてもよい。インクルートのネストには 128 ファイルまでという ハード・リミットがあって、インクルードファイルのループが起きないように なっている。
ファイル名には %h エスケープが使える。これはホスト名の短縮形を 示している。たとえば、マシンのホスト名が ``xerxes'' のとき、
#include /etc/sudoers.%h
と書けば、sudo はファイル /etc/sudoers.xerxes をインクルード することになる。
#includedir
命令を使えば、sudo.d ディレクトリを
作っておいて、システムのパッケージ管理者がパッケージをインストールする過程で
sudoers のルールを記したファイルをそこに入れてやる、といったことが
可能になる。たとえば、次のように書くと、
#includedir /etc/sudoers.d
sudo は /etc/sudoers.d にあるファイルを一つづつ読み込む。
ただし、末尾が
~
だったり、
.
文字を
含んでいたりするファイル名はスキップするが、これは パッケージマネージャや
エディタが作った、テンポラリファイルやバックアップファイルを読み込むような
問題を起こさないためである。ファイルは辞書順にソートされて、解析される。
すなわち、/etc/sudoers.d/01_first が /etc/sudoers.d/10_second
より前に解析されるということだ。ソートは辞書順であって、数値の順では
ないので、/etc/sudoers.d/1_whoops というファイルがあっても、
/etc/sudoers.d/10_second より後でロードされることに
注意してほしい。ファイル名の先頭を 0 で埋めて数字の桁を揃えれば、
こうした問題を回避することができる。
気を付けてほしいが、
#include
でインクルードされた
ファイルとは違って (訳注: visudo は /etc/sudoers を
編集するとき、
#include
で指定したファイルがあれば、
続けてそれも編集する)、
visudo が
#includedir
で指定したディレクトリの
ファイルまで編集するのは、シンタクスエラーを含むものがあるときだけである。
とは言え、visudo を
-f
オプション付きで実行して、
ディレクトリ中のファイルを直接編集することは可能だ。
予約語 ALL は組込みのエイリアスであり、何に対してでも
マッチする。ALL は、
Cmnd_Alias
,
User_Alias
,
Runas_Alias
,
Host_Alias
を代わりに使えるところなら、どこでも
使用できる。ALL という名前のエイリアスを自分で定義しようと
してはいけない。組込みのエイリアスの方が、自分で作ったエイリアスより
優先して使われるからだ。ALL の使用には危険が伴うことが
あるのを忘れないでいただきたい。なぜなら、ALL を
コマンドに関して使うと、ユーザにシステム上のどんなコマンドでも
実行することを許してしまうからである。
エクスクラメーションマーク ('!') は、エイリアスでも
Cmnd
の前でも論理 not 演算子として使用できる。
これによってある値を除外することが可能だ。しかしながら、
組込みエイリアス
ALL
と
!
を
組み合わせて、「二三のコマンド以外のすべての」コマンドの実行をあるユーザに
許可しようとしても、思いどおりの動きになることはめったにないことに
気を付けてほしい (下記の「セキュリティに関する注意点」を参照)。
長い行は、行末にバックスラッシュ ('\') を置けば、継続することができる。
リストにおける要素間やユーザ設定における構文用特殊文字 ('=', ':', '(', ')') の前後に空白文字 (whitespace)を入れることは、 任意である。
次の文字を単語 (ユーザ名とかホスト名とか) の一部として使うときは、 バックスラッシュ ('\') でエスケープしなければならない。 '@', '!', '=', ':', ',', '(', ')', '\' がそれである。
フラグ:
整数:
真偽値としても使用できる整数:
文字列:
デフォルトの値は「
Password:
」である。
真偽値としても使用できる文字列:
値を指定しないと、once を指定したことになる。頭に '!' を付けて、 このオプションを否定すると、値に never が使用される。 デフォルトの値は once である。
値を指定しないと、値は any だと見なされる。'!' を頭に付けて、 このオプションを否定すると、値に never が使われることになる。 デフォルトは any である。
値を指定しないと、値は all だと見なされる。'!' を頭に付けて、 このオプションを否定すると、値に never が使われることになる。 デフォルトは all である。
真偽値としても使用できるリスト:
syslog(3) 経由でログを記録する場合、sudo は syslog のファ シリティ (facility: syslog パラメータの値) として次の値を受け付 ける。authpriv (ただし、OS がサポートしているならばだが)、 auth, daemon, user, local0, local1, local2, local3, local4, local5, local6, local7。syslog の優先順位 (priority) については、 次のものに対応している。alert, crit, debug, emerg, err, info, notice, warning。
# User alias の指定
User_Alias FULLTIMERS = millert, mikef, dowdy
User_Alias PARTTIMERS = bostley, jwfox, crawl
User_Alias WEBMASTERS = will, wendy, wim
# Runas alias の指定
Runas_Alias OP = root, operator
Runas_Alias DB = oracle, sybase
Runas_Alias ADMINGRP = adm, oper
# Host alias の指定
Host_Alias SPARC = bigtime, eclipse, moet, anchor :\
SGI = grolsch, dandelion, black :\
ALPHA = widget, thalamus, foobar :\
HPPA = boa, nag, python
Host_Alias CUNETS = 128.138.0.0/255.255.0.0
Host_Alias CSNETS = 128.138.243.0, 128.138.204.0/24, 128.138.242.0
Host_Alias SERVERS = master, mail, www, ns
Host_Alias CDROM = orion, perseus, hercules
# Cmnd alias の指定
Cmnd_Alias DUMPS = /usr/bin/mt, /usr/sbin/dump, /usr/sbin/rdump,\
/usr/sbin/restore, /usr/sbin/rrestore
Cmnd_Alias KILL = /usr/bin/kill
Cmnd_Alias PRINTING = /usr/sbin/lpc, /usr/bin/lprm
Cmnd_Alias SHUTDOWN = /usr/sbin/shutdown
Cmnd_Alias HALT = /usr/sbin/halt
Cmnd_Alias REBOOT = /usr/sbin/reboot
Cmnd_Alias SHELLS = /usr/bin/sh, /usr/bin/csh, /usr/bin/ksh, \
/usr/local/bin/tcsh, /usr/bin/rsh, \
/usr/local/bin/zsh
Cmnd_Alias SU = /usr/bin/su
Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
以下では、コンパイル時に埋め込まれたデフォルト値のいくつかを変更している。
sudo には syslog(3) 経由でログを記録し、ファシリティには
すべての場合に auth を使用させたい。フルタイムのスタッフには
sudo の訓戒を出さないようにしたい。ユーザ millert は
パスワードを入力しないでよい。コマンドを root として実行するときは、
環境変数
LOGNAME
,
USER
,
USERNAME
を変更したくない。さらに、
SERVERS という
Host_Alias
に属する
マシンでは、ローカルなログファイルを副本として作り、ログの記入事項は
数年に渡って保存されるので、ログの各行に間違いなく年度が入るようにする。
最後に PAGERS という
Cmnd_Alias
に属する
コマンド (/usr/bin/more, /usr/bin/pg, /usr/bin/less)
については、シェル・エスケープを無効にする。
# built-in defaults の変更 Defaults syslog=auth Defaults>root !set_logname Defaults:FULLTIMERS !lecture Defaults:millert !authenticate Defaults@SERVERS log_year, logfile=/var/log/sudo.log Defaults!PAGERS noexec
ユーザ設定が、誰が何を実行できるかを実際に決めている部分だ。
root ALL = (ALL) ALL %wheel ALL = (ALL) ALL
root と wheel グループのすべてのユーザには、どのホストでも 任意のユーザとしていかなるコマンドでも実行することを認める。
FULLTIMERS ALL = NOPASSWD: ALL
フルタイムのシステム管理者 (millert, mikef, dowdy) は、どのホストでも任意のコマンドを認証なしで実行できる。
PARTTIMERS ALL = ALL
パートタイムのシステム管理者 ((bostley, jwfox,
crawl) は、どのホストでも任意のコマンドを実行できるが、
その際に認証をしなければならない (このエントリには
NOPASSWD
タグが指定されていないので)。
jack CSNETS = ALL
ユーザ jack は、CSNETS というエイリアスに属するマシンで 任意のコマンドを実行できる (すなわち、ネットワークが 128.138.243.0, 128.138.204.0, 128.138.242.0 のマシンだ)。この内、 128.138.204.0 にのみ class C のネットワークであることを示す 明示的な (CIDR 表記の) netmask がある。CSNETS の ほかのネットワークについては、ローカルマシンの netmask がマッチングの際に 使われることになる。
lisa CUNETS = ALL
ユーザ lisa はエイリアスが CUNETS のいかなるホストでも 任意のコマンドを実行することができる (すなわち、 128.138.0.0 という class B ネットワークのマシンだ)。
operator ALL = DUMPS, KILL, SHUTDOWN, HALT, REBOOT, PRINTING,\
sudoedit /etc/printcap, /usr/oper/bin/
ユーザ operator は、用途が簡単な保守管理に限定されたコマンドを 実行できる。この場合それは、バックアップしたり、プロセスを kill したり、 印刷システムを操作したり、システムをシャットダウンしたりするのに 関係するコマンドと、/usr/oper/bin/ ディレクトリにある任意のコマンド である。
joe ALL = /usr/bin/su operator
ユーザ joe は su(1) を使って operator になることしか できない。
%opers ALL = (: ADMINGRP) /usr/sbin/
opers グループのユーザは、/usr/sbin/ にあるコマンドを
自分自身の資格で、
Runas_Alias
ADMINGRP に属する
任意のグループ (すなわち、adm か oper グループ) として
実行できる。(訳注: 実のところ、sudo-1.7.2p1 では、
/etc/sudoers で変身可能グループに
Runas_Alias
を
まだ指定できないようだ。今のところ、この例で言うなら、ADMINGRP
ではなく、adm や oper を直接指定しなければならない。)
pete HPPA = /usr/bin/passwd [A-Za-z]*, !/usr/bin/passwd root
ユーザ pete は HPPA に属するマシンで root 以外なら 誰のパスワードでも変更することを許されている。上記の指定は、 passwd(1) がコマンドラインで複数のユーザ名を受け付けないことを 前提としている点に注意してほしい。
bob SPARC = (OP) ALL : SGI = (OP) ALL
ユーザ bob は SPARC や SGI に属する
マシンで
Runas_Alias
OP に登録されている
任意のユーザとして (root と operator である) どんなコマンドでも
実行できる。
jim +biglab = ALL
ユーザ jim は biglab ネットグループに属するマシンで どんなコマンドでも実行できる。sudo は、``biglab'' に '+' の 接頭辞が付いているので、それをネットグループだと認識する。
+secretaries ALL = PRINTING, /usr/bin/adduser, /usr/bin/rmuser
secretaries ネットグループのユーザは、ユーザの追加や削除は もちろん、プリンタの管理にも協力する必要がある。そこで、すべてのマシンで その種のコマンドの実行を認められている。
fred ALL = (DB) NOPASSWD: ALL
ユーザ fred は
Runas_Alias
DB の
任意のユーザとして (oracle か sybase だ) パスワードを
入力しないでもコマンドを実行することができる。
john ALPHA = /usr/bin/su [!-]*, !/usr/bin/su *root*
ユーザ john は ALPHA に属するマシンで su(1) を 使って root 以外の誰にでもなることができるが、su にオプションを 指定することは許されていない。
jen ALL, !SERVERS = ALL
ユーザ jen は
Host_Alias
SERVERS に
属するマシン (master, mail, www, ns) を除くいかなるマシンでも
任意のコマンドを実行できる。
jill SERVERS = /usr/bin/, !SU, !SHELLS
jill は
Host_Alias
SERVERS の
いかなるマシンでも /usr/bin/ ディレクトリにある任意のコマンドを
実行できるが、SU や SHELLS という
Cmnd_Aliases
に属するコマンドは実行できない。
steve CSNETS = (operator) /usr/local/op_commands/
ユーザ steve はディレクトリ /usr/local/op_commands/ にある 任意のコマンドを実行できるが、operator というユーザとして実行できるだけだ。
matt valkyrie = KILL
matt も自分用のワークステーション valkyrie で ハングしたプロセスの kill ぐらいはできる必要がある。
WEBMASTERS www = (www) ALL, (root) /usr/bin/su www
ホスト www で
User_Alias
WEBMASTERS に
属するいかなるユーザも (will, wendy, wim だ)、ユーザ www (web ページの
所有者) として任意のコマンドを実行することができる。
単に su(1) で www になってもよい。
ALL CDROM = NOPASSWD: /sbin/umount /CDROM,\
/sbin/mount -o nosuid\,nodev /dev/cd0a /CDROM
いかなるユーザも
Host_Alias
が CDROM の
マシンで (orion, perseus, hercules)、パスワードを入力することなく
CD-ROM をマウント、アンマウントできる。上記のコマンドを打ち込むのは
ユーザにとっていささか面倒なので、シェルスクリプトとして
カプセル化してしまうのがよいだろう。
それでは、何故、上記の「用例」で自ホスト以外の設定が行われて いるのか? そもそも、sudoers の書式で自ホスト以外のホストを 指定できるのは、何故なのか? それは、管理しているサイトの すべてのホストの設定を記した sudoers ファイルを一つ作って、それを すべてのホストにコピーして使う、そういった使い方を想定しているからだ。 もし、サイト中のすべてのホストの設定を一ヶ所にまとめて置き、 それをすべてのホストに共有させたいのなら (すなわち、sudo の設定の 集中管理がしたいのなら)、LDAP の採用を考えるべきである。
bill ALL = ALL, !SU, !SHELLS
という行は、SU や SHELLS に列記されている コマンドの bill による実行を本当に阻止することにはならない。 なぜなら、bill としては、そうしたコマンドを単に名前を変えて コピーすればよいし、エディタやほかのプログラムからシェル・エスケープを 利用することもできるからだ。だから、この種の制限はやった方がまし程度に 考えておくべきだ (そして、しっかりした運用方針によって制限の実効力を 上げるべきである)。
この問題に対処するには、基本的に二つの方法がある。
sudo が noexec に対応しているかどうかを知りたかったら、 root ユーザになって次のように実行してみればよい。
sudo -V | grep "dummy exec"
その出力にこんなふうに始まる行があれば、
File containing dummy exec functions:
そのときは、たぶん sudo が標準ライブラリにある exec ファミリーの関数を、
単にエラーを返すだけの自分自身の関数で置き換えられるということだ。
残念ながら、noexec が有効かどうか、コンパイル時に確かめる
絶対確実な方法はない。noexec は SunOS, Solaris, *BSD, Linux,
IRIX, Tru64 UNIX, MacOS X, HP-UX 11.x で使えるはずだ。
AIX と UnixWare では使えないことがわかっている。環境変数
LD_PRELOAD
をサポートしているたいていの OS なら、
noexec が使えると思う。使用しているオペレーティングシステムの
マニュアルページを調べて、ダイナミック・リンカについて (通例 ld.so,
ld.so.1, dyld, dld.sl, rld, loader といった名前になっている)
LD_PRELOAD
がサポートされているかどうか確認してほしい。
あるコマンドに対して noexec を有効にするには、
上記「ユーザの設定」セクションで述べたように、
NOEXEC
タグを
使用する。そのときの例を再掲しよう。
aaron shanty = NOEXEC: /usr/bin/more, /usr/bin/vi
この例では、ユーザ aaron 対して、noexec を有効にした上で、 /usr/bin/more と /usr/bin/vi の実行を許可している。 このようにすれば、この二つのコマンドから (シェルのような) ほかのコマンドを 実行することができなくなるわけだ。使用しているシステムが noexec に 対応する能力があるかどうか、よくわからない場合は、取りあえず noexec を試して、効果があるかどうか確かめてみればよい。それなら いつだってできるはずだ。
注意してほしいが、シェル・エスケープの禁止は万能薬ではない。ルートの権限で 動いているプログラムには、ほかにも、危険性のあるさまざまな作業 (ファイルの 中身を入れ替えるとか、上書きするとか) が可能であり、思いがけずに 権限を拡大してしまうこともありえるのだ。特にエディタについて言うと、 ユーザには sudoedit を実行する許可を与えるのが、より安全な方法である。
ネットグループを (ユーザについてではなく) マシンについて使用し、
netgroup ファイルに完全修飾ホスト名を記載する場合は (たいてい
そうするものだが)、そのマシンのホスト名を
hostname
コマンドが出力する通りの完全修飾名で書くか、さもなければ
sudoers ファイルで fqdn オプションを使うかしなければなら
ない。