hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

ACL(アクセス制御リスト)の使い方

1 ACL(アクセス制御リスト)とは?

Linuxのファイルやディレクトリに対するアクセス制御方式には、以下の方式があります。任意アクセス制御方式では、ファイルやディレクトリの所有者自身が、それらに対するアクセス権を設定します。

方式 実装
任意アクセス制御(DAC) パーミッション(rwx)、ACL(アクセス制御リスト)
強制アクセス制御(MAC): SELinux,AppArmor,Smack

本記事では、ACLで使うgetfacl,setfaclコマンドの使い方を説明します。ACLは、パーミッションによるアクセス方式に比べ、粒度の細かいアクセス制御ができます。

コマンド名 概要
setfacl ファイルやディレクトリにACLを設定します
getfacl ファイルやディレクトリに設定されたACLを取得します

2 検証環境

CentOS版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

3 インストール方法

aclパッケージをインストールします。

[root@server ~]# yum -y install acl

getfaclコマンドの版数を確認します。

[root@server ~]# getfacl -v
getfacl 2.2.51

setfaclコマンドの版数を確認します。

[root@server ~]# setfacl -v
setfacl 2.2.51

4 ユーザのACL設定、削除方法

4.1 事前準備

テスト用ユーザ(usere1)を作成します。なお、ユーザの作成、削除方法は、useradd/groupaddコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# useradd user1

テスト用ユーザ(user2)を作成します。

[root@server ~]# useradd user2

テスト用ファイルを作成します。

[root@server ~]# touch /tmp/test.txt

test.txtのACLを確認します。ACLはまだ設定されていないことがわかります。

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
group::r--
other::r--

テスト用ファイルの所有者、グループを確認します。ともにrootであることがわかります。

[root@server ~]# ls -l /tmp/test.txt
-rw-r--r--. 1 root root 0  5月 31 19:50 /tmp/test.txt

4.2 設定方法(-m)

test.txtに対してuser1のみ、リード/ライトができるようにACLを設定します。

[root@server ~]# setfacl -m u:user1:rw /tmp/test.txt

test.txtに対してuser2のみ、リードができるようにACLを設定します。

[root@server ~]# setfacl -m u:user2:r /tmp/test.txt

test.txtのACLを確認します。test.txtに対して、各ユーザは次のことができることがわかります。
・user1はリード/ライト
・user2はリードのみ

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
user:user1:rw-
user:user2:r--
group::r--
mask::rw-
other::r--

なお、ACLを設定すると、パーミッションの右端に+が表示されることがわかります。

[root@server ~]# ls -l /tmp/test.txt
-rw-rw-r--+ 1 root root 0  5月 31 19:50 /tmp/test.txt

4.3 削除方法(-x)

test.txtからuser1のACLを削除します。

[root@server ~]# setfacl -x u:user1 /tmp/test.txt

test.txtのACLを確認します。test.txtからuser1のACLが削除されたことがわかります。

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
user:user2:r--
group::r--
mask::r--
other::r--

test.txtからuser2のACLを削除します。

[root@server ~]# setfacl -x u:user2 /tmp/test.txt

test.txtのACLを確認します。test.txtからuser2のACLが削除されたことがわかります。

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
group::r--
mask::r--
other::r--

5 グループのACL設定、削除方法

グループに対して、ACLの設定、削除をおこなってみます。

5.1 事前準備

テスト用のファイルを作成します。

[root@server ~]# touch /tmp/test.txt

テスト用のグループ(staff)を作成します。

[root@server ~]# groupadd staff

作成したグループを確認します。

[root@server ~]# getent group|grep staff
staff:x:1001:

5.2 設定方法(-m)

test.txtに対して、staffグループはリード/ライトできるようにACLを設定してみます。

[root@server ~]# setfacl -m g:staff:rw /tmp/test.txt

ACLを確認します。staffグループは、test.txtに対してリード/ライトができることがわかります。

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
group::r--
group:staff:rw-
mask::rw-
other::r--

5.3 削除方法(-x)

[root@server ~]# setfacl -x g:staff /tmp/test.txt

ACLを確認します。

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
group::r--
mask::r--
other::r--

6 ACLを一括削除する方法(-b)

test.txtに設定されているACLを確認します。user1,user2のACLが設定されていることがわかります。

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
user:user1:rw-
user:user2:r--
group::r--
mask::rw-
other::r--

ACLを一括削除します。

[root@server ~]# setfacl -b /tmp/test.txt

test.txtのACLを確認します。user1,user2のACLが削除されたことがわかります。

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
group::r--
other::r--

7 再帰的に表示する方法(-R)

ファイルやディレクトリのACL再帰的に表示してみます。まず、テスト用に使うディレクトリを作成します。

[root@server ~]# mkdir -p dir1/dir2

dir1,dir2のACLを確認します。dir1,dir2のACLが表示されていることがわかります。

[root@server ~]# getfacl -R dir1/
# file: dir1/
# owner: root
# group: root
user::rwx
group::r-x
other::r-x

# file: dir1//dir2
# owner: root
# group: root
user::rwx
group::r-x
other::r-x

8 ファイルを使ってACLを設定する方法(-M)

ACLを定義したファイルを作成します。

[root@server ~]# vi acl.txt
[root@server ~]# cat acl.txt
user:user1:rw
user:user2:rwx

file.txtのACLを確認します。ACLは、まだ設定されていないことがわかります。

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
group::r--
other::r--

test.txtにACLを設定します。

[root@server ~]# setfacl -M acl.txt /tmp/test.txt

file.txtに設定されているACLを確認します。user1とuser2のACLが設定されたことがわかります。

[root@server ~]# getfacl /tmp/test.txt
getfacl: Removing leading '/' from absolute path names
# file: tmp/test.txt
# owner: root
# group: root
user::rw-
user:user1:rw-
user:user2:rwx
group::r--
mask::rwx
other::r--

9 デフォルトACLを設定、削除する方法

dir1に対し、user1がリード/ライトができるようにデフォルトACLを設定します。

[root@server ~]# setfacl -m d:u:user1:rw dir1/

dir1に設定したデフォルトACLを確認します。デフォルトACLが設定されたことがわかります。

[root@server ~]# getfacl dir1/
# file: dir1/
# owner: root
# group: root
user::rwx
group::r-x
other::r-x
default:user::rwx
default:user:user1:rw-
default:group::r-x
default:mask::rwx
default:other::r-x

ディレクトリ(dir1)に設定されたデフォルトACLを削除します。

[root@server ~]# setfacl -k dir1

dir1に設定したデフォルトACLを確認します。

[root@server ~]# getfacl dir1/
# file: dir1/
# owner: root
# group: root
user::rwx
group::r-x
other::r-x

10 実験

test.txtに対して、user1はリード/ライト、user2はリードができるようにACLを設定してみます。

[root@server ~]# setfacl -m u:user1:rw /tmp/test.txt
[root@server ~]# setfacl -m u:user2:r /tmp/test.txt

user1のfile.txtに対するリード/ライトを確認します。file.txtに対して、リード/ライトができることがわかります。

[root@server ~]# su - user1
最終ログイン: 2022/05/28 (土) 20:45:07 JST日時 pts/1

テスト用ファイルに書き込みをします。user1はファイルへの書き込みが許可されているので、エラーは発生しません。

[user1@server ~]$ echo "12345" > /tmp/test.txt

テスト用ファイルから読み込みをします。user1はファイルからの読み込みが許可されているので、エラーは発生しません。

[user1@server ~]$ cat /tmp/test.txt
12345

user1からrootに切り替えます。

[user1@server ~]$ exit
logout

次に、user2に切り替えます。

[root@server ~]# su - user2

テスト用ファイルに書き込みをします。user2はファイルへの書き込みが禁止されているので、書き込みができません。

[user2@server ~]$ echo "12345" > /tmp/test.txt
-bash: /tmp/test.txt: Permission denied

テスト用ファイルから読み込みをします。user2はファイルからの読み込みが許可されているので、ファイルの中身を読み込むことができます。

[user2@server ~]$ cat /tmp/test.txt
12345

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

auditctlコマンドの使い方

1 auditctlコマンドとは?

Auditの動作に関する設定、およびAuditルールの定義を行うコマンドです。Auditとは、いつ、だれが、何をしたか?という記録を取り、不正なアクセスがなかったかを監査することです。監査した結果はログに記録されます。デフォルトは/var/log/audit/audit.logに出力されます。

Auditに関連するコマンドとして以下のものがあります。

コマンド名 概要
ausearch 監査ログを検索するためのコマンドです。詳細は、ausearchコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
aureport audit.logファイルから監査のサマリを作成するコマンドです
auditctl Auditデーモンの動作に関する設定、およびAuditルールを定義するコマンドです

2 検証環境

CentOS版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

auditctlコマンドの版数は以下のとおりです。

[root@server ~]# auditctl -v
auditctl version 2.8.5

3 オプション一覧

[root@server ~]#  auditctl -h
usage: auditctl [options]
    -a <l,a>            Append rule to end of <l>ist with <a>ction
    -A <l,a>            Add rule at beginning of <l>ist with <a>ction
    -b <backlog>        Set max number of outstanding audit buffers
                        allowed Default=64
    -c                  Continue through errors in rules
    -C f=f              Compare collected fields if available:
                        Field name, operator(=,!=), field name
    -d <l,a>            Delete rule from <l>ist with <a>ction
                        l=task,exit,user,exclude
                        a=never,always
    -D                  Delete all rules and watches
    -e [0..2]           Set enabled flag
    -f [0..2]           Set failure flag
                        0=silent 1=printk 2=panic
    -F f=v              Build rule: field name, operator(=,!=,<,>,<=,
                        >=,&,&=) value
    -h                  Help
    -i                  Ignore errors when reading rules from file
    -k <key>            Set filter key on audit rule
    -l                  List rules
    -m text             Send a user-space message
    -p [r|w|x|a]        Set permissions filter on watch
                        r=read, w=write, x=execute, a=attribute
    -q <mount,subtree>  make subtree part of mount point's dir watches
    -r <rate>           Set limit in messages/sec (0=none)
    -R <file>           read rules from file
    -s                  Report status
    -S syscall          Build rule: syscall name or number
    -t                  Trim directory watches
    -v                  Version
    -w <path>           Insert watch at <path>
    -W <path>           Remove watch at <path>
    --loginuid-immutable  Make loginuids unchangeable once set
    --reset-lost         Reset the lost record counter

4 ルールについて

4.1 ルールの種類

Auditルールには、以下の3つがあります。

ルール 概要
システムコールルール システムコールに対して監査を行います
ファイルシステムルール ファイル、ディレクトリへのアクセスに対して監査を行います
制御ルール Auditに関するルールを設定します

4.4 ルールの設定方法

Auditのルールは、以下の方法で設定することができます。

方法 概要
auditctlコマンドでルールを設定 OSを再起動すると、ルールが消えます。テスト目的で使います
/etc/audit/rules.d配下にルールを作成 OSを再起動しても、ルールは消えません。auditctlコマンドで正しく動作することが確認できたら、こちらの方法を使ってルールを作成します

5 システムコールルールの使い方

5.1 オプション一覧

オプション 意味
-a <アクション>,<リスト> 追加するアクションとリストをカンマ区切りで指定します。アクションとリストで、特定のイベントが監査ログに記録されるタイミングを指定します
-d <アクション>,<リスト> 削除するアクションとリストを指定します
-k <キー> ルールを識別するための任意の識別子を指定します
-F <フィールド=値> フィールドには、arch,pid,uid等があります。またarchにはb32,b64を指定します。これらを指定することで、発生するイベントを絞り込むことができます
-S <システムコール名> システムコール名またはシステムコール番号、またはallを指定します
  • アクション
アクション 意味
always 常に監査ログを生成します
never 監査ログを生成しません
  • リスト
リスト 意味
task 子プロセスの生成時、プロセスのコピー時に発生するイベント
exit システムコール終了時に発生するイベント
user ユーザ空間で発生するイベント
exclude 特定のイベントを監査から除外

コマンドの書式は以下になります。

# auditctl -a <アクション、リスト> -S <システムコール> -F <フィールド=値> -k <キー>

5.2 監査ログを出力する方法(always)

mkdirシステムコールを実行したら監査ログを出力する、というルールを作成します。

[root@server ~]# auditctl -a always,exit -F arch=b64 -S mkdir -k test1

作成したルールを確認します。

[root@server ~]# auditctl -l
-a always,exit -F arch=b64 -S mkdir -F key=test1

テスト用のディレクトリを作成します。

[root@server ~]# mkdir /test

ausearchコマンドを実行して監査ログを確認します。mkdirシステムコール(syscall=mkdir)を実行して、/testディレクトリの作成に成功(success=yes)していることがわかります。

なお、各typeの意味は次のとおりです。
・PROCTITLE:実行したコマンドラインイメージ
・PATH:監査対象のファイル、ディレクト
・CWD:コマンドを実行したディレクト
・ SYSCALL:実行したシステムコール、実行結果、実行したユーザのuid,gid

[root@server ~]# mkdir /test
[root@server ~]# ausearch -i -k test1
----
type=CONFIG_CHANGE msg=audit(2022年05月28日 20:07:42.033:121) : auid=root ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 op=add_rule key=test1 list=exit res=yes
----
type=PROCTITLE msg=audit(2022年05月28日 20:07:54.453:122) : proctitle=mkdir /test
type=PATH msg=audit(2022年05月28日 20:07:54.453:122) : item=1 name=/test inode=8447635 dev=fd:03 mode=dir,755 ouid=root ogid=root rdev=00:00 obj=unconfined_u:object_r:default_t:s0 objtype=CREATE cap_fp=none cap_fi=none cap_fe=0 cap_fver=0
type=PATH msg=audit(2022年05月28日 20:07:54.453:122) : item=0 name=/ inode=64 dev=fd:03 mode=dir,555 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:root_t:s0 objtype=PARENT cap_fp=none cap_fi=none cap_fe=0 cap_fver=0
type=CWD msg=audit(2022年05月28日 20:07:54.453:122) :  cwd=/root
type=SYSCALL msg=audit(2022年05月28日 20:07:54.453:122) : arch=x86_64 syscall=mkdir success=yes exit=0 a0=0x7ffc765bb83f a1=0777 a2=0x1ff a3=0x7ffc765b9d20 items=2 ppid=1086 pid=1111 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts0 ses=1 comm=mkdir exe=/usr/bin/mkdir subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test1

テスト用に作成したルールを削除します。

[root@server ~]# auditctl -D

5.3 監査ログを出力しない方法(never)

mkdirシステムコールを実行したら監査ログを出力しない、というルールを作成します。

[root@server ~]# auditctl -a never,exit -F arch=b64 -S mkdir -k test2

作成したルールを確認します。

[root@server ~]# auditctl -l
-a never,exit -F arch=b64 -S mkdir -F key=test2

テスト用のディレクトリを作成します。

[root@server ~]# mkdir /test

ausearchコマンドを実行して監査ログを確認します。mkdirシステムコールの監査ログは出力されないことがわかります。ルールを変更した旨の監査ログだけが出力されています。

[root@server ~]# ausearch -k test2
----
time->Sat May 28 19:08:01 2022
type=CONFIG_CHANGE msg=audit(1653732481.674:192): auid=0 ses=4 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 op=add_rule key="test2" list=4 res=1

テスト用のディレクトリを削除します。

[root@server ~]# rmdir /test

テスト用に作成したルールを削除します。

[root@server ~]# auditctl -D

5.4 uidの使い方

テスト用のディレクトリを作成します。

[root@server ~]# mkdir /test

テスト用のユーザを作成します。

[root@server ~]# useradd user1

テスト用に作成したユーザのuidを確認します。user1ユーザのuidは、1000であることがわかります。

[root@server ~]# id user1
uid=1000(user1) gid=1000(user1) groups=1000(user1)

uidが1000のユーザがopenシステムコールを実行したら監査ログを出力する、というルールを作成します。

[root@server ~]# auditctl -a always,exit -F uid=1000 -F arch=b64 -S open -k test3

作成したルールを確認します。

[root@server ~]# auditctl -l
-a always,exit -F arch=b64 -S open -F uid=1000 -F key=test3

rootで/tmpディレクトレイに対してlsコマンドを実行します。

[root@server ~]# ls /test

rootユーザの場合、監査ログが出力されないことがわかります(期待値)。

[root@server ~]# ausearch -i -k test3
----
type=CONFIG_CHANGE msg=audit(2022年05月28日 19:13:43.463:140) : auid=root ses=2 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 op=add_rule key=test3 list=exit res=yes

ユーザをuser1に切り替えます。

[root@server ~]# su - user1
[user1@server ~]$

user1で/testディレクトレイに対してlsコマンドを実行します。

[user1@server ~]$ ls /test

ausearchコマンドを実行して監査ログを確認します。user1ユーザの場合、監査ログが出力されることがわかります(期待値)。

[root@server ~]# ausearch -i -k test3
-snip-
----
type=PROCTITLE msg=audit(2022年05月28日 20:45:24.973:283) : proctitle=ls --color=auto /test 
type=PATH msg=audit(2022年05月28日 20:45:24.973:283) : item=0 name=/etc/ld.so.cache inode=4961070 dev=fd:03 mode=file,644 ouid=root ogid=root rdev=00:00 obj=unconfined_u:object_r:ld_so_cache_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 
type=CWD msg=audit(2022年05月28日 20:45:24.973:283) :  cwd=/home/user1 
type=SYSCALL msg=audit(2022年05月28日 20:45:24.973:283) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7f67bac5b5a4 a1=O_RDONLY|O_CLOEXEC a2=0x1 a3=0x7f67bae614f8 items=1 ppid=1215 pid=1240 auid=root uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=2 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test3 
----
type=PROCTITLE msg=audit(2022年05月28日 20:45:24.973:284) : proctitle=ls --color=auto /test 
type=PATH msg=audit(2022年05月28日 20:45:24.973:284) : item=0 name=/lib64/libselinux.so.1 inode=35076 dev=fd:03 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 
type=CWD msg=audit(2022年05月28日 20:45:24.973:284) :  cwd=/home/user1 
type=SYSCALL msg=audit(2022年05月28日 20:45:24.973:284) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7f67bae5e640 a1=O_RDONLY|O_CLOEXEC a2=0x7f67bae61150 a3=0x7f67bae5e640 items=1 ppid=1215 pid=1240 auid=root uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=2 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test3 
----
type=PROCTITLE msg=audit(2022年05月28日 20:45:24.974:285) : proctitle=ls --color=auto /test 
type=PATH msg=audit(2022年05月28日 20:45:24.974:285) : item=0 name=/lib64/libcap.so.2 inode=35212 dev=fd:03 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 
type=CWD msg=audit(2022年05月28日 20:45:24.974:285) :  cwd=/home/user1 
type=SYSCALL msg=audit(2022年05月28日 20:45:24.974:285) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7f67bae5eb08 a1=O_RDONLY|O_CLOEXEC a2=0x7f67bae61150 a3=0x7f67bae5eb08 items=1 ppid=1215 pid=1240 auid=root uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=2 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test3 
----
type=PROCTITLE msg=audit(2022年05月28日 20:45:24.974:286) : proctitle=ls --color=auto /test 
type=PATH msg=audit(2022年05月28日 20:45:24.974:286) : item=0 name=/lib64/libacl.so.1 inode=35209 dev=fd:03 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 
type=CWD msg=audit(2022年05月28日 20:45:24.974:286) :  cwd=/home/user1 
type=SYSCALL msg=audit(2022年05月28日 20:45:24.974:286) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7f67bae5efd0 a1=O_RDONLY|O_CLOEXEC a2=0x7f67bae61150 a3=0x7f67bae5efd0 items=1 ppid=1215 pid=1240 auid=root uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=2 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test3 
----
type=PROCTITLE msg=audit(2022年05月28日 20:45:24.974:287) : proctitle=ls --color=auto /test 
type=PATH msg=audit(2022年05月28日 20:45:24.974:287) : item=0 name=/lib64/libc.so.6 inode=34967 dev=fd:03 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 
type=CWD msg=audit(2022年05月28日 20:45:24.974:287) :  cwd=/home/user1 
type=SYSCALL msg=audit(2022年05月28日 20:45:24.974:287) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7f67bae564b0 a1=O_RDONLY|O_CLOEXEC a2=0x7f67bae61150 a3=0x7f67bae564b0 items=1 ppid=1215 pid=1240 auid=root uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=2 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test3 
----
type=PROCTITLE msg=audit(2022年05月28日 20:45:24.976:288) : proctitle=ls --color=auto /test 
type=PATH msg=audit(2022年05月28日 20:45:24.976:288) : item=0 name=/lib64/libpcre.so.1 inode=35060 dev=fd:03 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 
type=CWD msg=audit(2022年05月28日 20:45:24.976:288) :  cwd=/home/user1 
type=SYSCALL msg=audit(2022年05月28日 20:45:24.976:288) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7f67bae56978 a1=O_RDONLY|O_CLOEXEC a2=0x7f67bae5e658 a3=0x7f67bae56978 items=1 ppid=1215 pid=1240 auid=root uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=2 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test3 
----
type=PROCTITLE msg=audit(2022年05月28日 20:45:24.976:289) : proctitle=ls --color=auto /test 
type=PATH msg=audit(2022年05月28日 20:45:24.976:289) : item=0 name=/lib64/libdl.so.2 inode=34973 dev=fd:03 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 
type=CWD msg=audit(2022年05月28日 20:45:24.976:289) :  cwd=/home/user1 
type=SYSCALL msg=audit(2022年05月28日 20:45:24.976:289) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7f67bae56e40 a1=O_RDONLY|O_CLOEXEC a2=0x7f67bae5e658 a3=0x7f67bae56e40 items=1 ppid=1215 pid=1240 auid=root uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=2 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test3 
----
type=PROCTITLE msg=audit(2022年05月28日 20:45:24.976:290) : proctitle=ls --color=auto /test 
type=PATH msg=audit(2022年05月28日 20:45:24.976:290) : item=0 name=/lib64/libattr.so.1 inode=35207 dev=fd:03 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 
type=CWD msg=audit(2022年05月28日 20:45:24.976:290) :  cwd=/home/user1 
type=SYSCALL msg=audit(2022年05月28日 20:45:24.976:290) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7f67bae55508 a1=O_RDONLY|O_CLOEXEC a2=0x7f67bae5eb20 a3=0x7f67bae55508 items=1 ppid=1215 pid=1240 auid=root uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=2 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test3 
----
type=PROCTITLE msg=audit(2022年05月28日 20:45:24.976:291) : proctitle=ls --color=auto /test 
type=PATH msg=audit(2022年05月28日 20:45:24.976:291) : item=0 name=/lib64/libpthread.so.0 inode=34993 dev=fd:03 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 
type=CWD msg=audit(2022年05月28日 20:45:24.976:291) :  cwd=/home/user1 
type=SYSCALL msg=audit(2022年05月28日 20:45:24.976:291) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7f67bae55a68 a1=O_RDONLY|O_CLOEXEC a2=0x7f67bae56990 a3=0x7f67bae55a68 items=1 ppid=1215 pid=1240 auid=root uid=user1 gid=user1 euid=user1 suid=user1 fsuid=user1 egid=user1 sgid=user1 fsgid=user1 tty=pts1 ses=2 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test3 
----
-snip-

テスト用に作成したルールを削除します。

[root@server ~]# auditctl -D

6 ファイルシステムルールの使い方

6.1 オプション一覧

オプション 意味
-w <ファイル名> 監査対象とするファイルやディレクトリを指定します
-k <キー> ルールを識別するための任意の識別子を指定します
-p <パーミッション> パーミッションを指定します。-r:読み取り、w:書き込み、x:実行、a:属性変更

コマンドの書式は以下になります。

# auditctl -w <ファイル/ディレクトリ> -p <パーミッション> -k <キー>

6.2 ファイルに対する監査

ファイルをリードしたら、監査結果をログに出力してみます。

テスト用のディレクトリを作成します。

[root@server ~]# mkdir /test

テスト用のファイルを作成します。

[root@server ~]# touch /test/aa.txt

テスト用ファイルをリードしたら監査ログを出力するルールを作成します。

[root@server ~]# auditctl -w /test/aa.txt -p r -k test4

作成したルールを確認します。

[root@server ~]# auditctl -l
-w /test/aa.txt -p r -k test4

テスト用ファイルを読み込んでみます。

[root@server ~]# cat /test/aa.txt
1

ausearchコマンドを実行して監査ログを確認します。openシステムコールで/test/aa.txtをオープンしていることがわかります。そして、openシステムコールの実行は成功(success=yes)しており、戻り値(exit)としてファイルディスクリプタ3を返していることがわかります。なお、ausearchコマンドの使い方は、ausearchコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# ausearch -i -k test4
----
type=CONFIG_CHANGE msg=audit(2022年05月28日 19:34:28.689:273) : auid=root ses=2 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 op=add_rule key=test4 list=exit res=yes
----
type=PROCTITLE msg=audit(2022年05月28日 19:35:01.662:274) : proctitle=cat /test/aa.txt
type=PATH msg=audit(2022年05月28日 19:35:01.662:274) : item=0 name=/test/aa.txt inode=8447636 dev=fd:03 mode=file,644 ouid=root ogid=root rdev=00:00 obj=unconfined_u:object_r:default_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0
type=CWD msg=audit(2022年05月28日 19:35:01.662:274) :  cwd=/root
type=SYSCALL msg=audit(2022年05月28日 19:35:01.662:274) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7ffe34a2383c a1=O_RDONLY a2=0x1fffffffffff0000 a3=0x7ffe34a21820 items=1 ppid=1110 pid=1172 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts1 ses=2 comm=cat exe=/usr/bin/cat subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test4

テスト用ファイルを削除します。

[root@server ~]# rm /test/aa.txt

テスト用に作成したルールを削除します。

[root@server ~]# auditctl -D

6.3 ディレクトリに対する監査

テスト用ディレクトリ(/test)をリードしたら監査ログを出力するルールを作成します。

[root@server ~]# auditctl -w /test -p r -k test5

作成したルールを確認します。

[root@server ~]# auditctl -l
-w /test -p r -k test5

/testディレクトリをリードしてみます。

[root@server ~]# ls /test

ausearchコマンドを実行して監査ログを確認します。openatシステムコールを使って、/testディレクトリをオープンしていることがわかります。そして、openatシステムコールの実行は成功(success=yes)しており、戻り値(exit)としてファイルディスクリプタ3を返していることがわかります。

[root@server ~]# ausearch -i -k test5
----
type=CONFIG_CHANGE msg=audit(2022年05月28日 19:42:36.951:140) : auid=root ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 op=add_rule key=test5 list=exit res=yes
----
type=PROCTITLE msg=audit(2022年05月28日 19:42:46.477:141) : proctitle=ls --color=auto /test
type=PATH msg=audit(2022年05月28日 19:42:46.477:141) : item=0 name=/test inode=8447635 dev=fd:03 mode=dir,755 ouid=root ogid=root rdev=00:00 obj=unconfined_u:object_r:default_t:s0 objtype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0
type=CWD msg=audit(2022年05月28日 19:42:46.477:141) :  cwd=/root
type=SYSCALL msg=audit(2022年05月28日 19:42:46.477:141) : arch=x86_64 syscall=openat success=yes exit=3 a0=0xffffffffffffff9c a1=0x19bd5e0 a2=O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC a3=0x0 items=1 ppid=1082 pid=1130 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts0 ses=1 comm=ls exe=/usr/bin/ls subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=test5

テスト用に作成したルールを削除します。

[root@server ~]# auditctl -D

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

カーネルモジュールのロード、アンロード方法

1 はじめに

カーネルモジュール(以降、モジュールと呼びます)のロード、アンロードをmodprobe,insmod/rmmodコマンドを使って確認してみます。
modprobeはモジュールの依存関係を考慮して依存するモジュールを自動的にロードします。一方、insmodは依存するモジュールを手動で先にロードする必要があります。この違いについて確認してみます。なお、カーネルモジュールの作成方法は、カーネルモジュールの作り方 - hana_shinのLinux技術ブログを参照してください。

2 検証環境

CentOS版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

3 コマンド一覧

モジュールのロード、アンロードに関係するコマンド一覧を以下に示します。

コマンド 概要
lsmod ロードされているモジュールの一覧を表示する
depmod モジュールの依存関係の作成/表示を行う
modprobe 依存するモジュールも含めて自動でロードする。-rを付けるとアンロードする
insmod モジュールをロードする。依存するモジュールがロードされていない場合、エラーになる
rmmod モジュールをアンロードする
modinfo モジュールの情報を表示する

4 モジュールの格納場所

モジュールは、「/lib/modules/カーネル版数」以下の場所に格納されています。本環境のカーネル版数は、uname -rの実行結果より、3.10.0-1160.el7.x86_64です。

モジュールが格納されているディレクトリのパスを確認します。

[root@server 3.10.0-1160.el7.x86_64]# pwd
/lib/modules/3.10.0-1160.el7.x86_64

「/lib/modules/3.10.0-1160.el7.x86_64」配下には、以下のファイル、ディレクトリがあります。

[root@server 3.10.0-1160.el7.x86_64]# ls -F
build@         modules.alias.bin    modules.dep      modules.modesetting  modules.symbols      vdso/
extra/         modules.block        modules.dep.bin  modules.networking   modules.symbols.bin  weak-updates/
kernel/        modules.builtin      modules.devname  modules.order        source@
modules.alias  modules.builtin.bin  modules.drm      modules.softdep      updates/

5 modprobeコマンドによるモジュールのロード/アンロード方法

5.1 依存関係の確認

depmodコマンドを使って依存関係を確認してみます。tcp_diagモジュールは、inet_diagモジュールに依存していることがわかります。

[root@server ~]# depmod -n|grep tcp_diag
kernel/net/ipv4/tcp_diag.ko.xz: kernel/net/ipv4/inet_diag.ko.xz

「依存」とは、tcp_diagモジュールをロードする時、inet_diagモジュールが先にロードされていなければいけない、ということ意味します。

5.2 ロード方法

ここでは、tcp_diagモジュールをロードします。まず、初期状態を確認します。tcp_diagモジュールもinet_diagモジュールもロードされていないことがわかります。

[root@server ~]# lsmod |grep -E 'tcp_diag|inet_diag'
[root@server ~]#

次に、tcp_diagモジュールをロードします。

[root@server ~]# modprobe tcp_diag

ロードされたモジュールを確認します。tcp_diagモジュールとinet_diagモジュールがロードされています。inet_diagモジュールが、自動的にロードされたことがわかります、

[root@server ~]# lsmod |grep -E 'tcp_diag|inet_diag'
tcp_diag               12591  0
inet_diag              18949  1 tcp_diag

5.3 アンロード方法

初期状態を確認します。tcp_diagとinet_diagがロードされていることがわかります。

[root@server ~]# lsmod |grep -E 'tcp_diag|inet_diag'
tcp_diag               12591  0
inet_diag              18949  1 tcp_diag

tcp_diagモジュールをアンロードします。

[root@server ~]# modprobe -r tcp_diag

tcp_diagもinet_diagもロードされていないことがわかります。

[root@server ~]# lsmod |grep -E 'tcp_diag|inet_diag'
[root@server ~]#

6 insmod,rmmodコマンドによるモジュールのロード/アンロード方法

insmod,rmmodコマンドによるモジュールのロード/アンロードは、モジュールの依存関係を考慮して実行する必要があります。たとえば、tcp_diagモジュールをロードする場合、さきにinet_diagモジュールをinsmodコマンドでロードする必要があります。

初期状態を確認します。tcp_diagモジュールもinet_diagモジュールもロードされていないことがわかります

[root@server ~]# lsmod |grep -E 'tcp_diag|inet_diag'
[root@server ~]#

tcp_diagモジュールをロードしてみます。inet_diagモジュールがロードされいないので、エラーが発生します。

[root@server ~]# insmod /lib/modules/3.10.0-1160.el7.x86_64/kernel/net/ipv4/tcp_diag.ko.xz
insmod: ERROR: could not insert module /lib/modules/3.10.0-1160.el7.x86_64/kernel/net/ipv4/tcp_diag.ko.xz: Unknown symbol in module

inet_diagモジュールをロードします。inet_diagモジュールは、依存するモジュールがないので、エラーは発生しません。

[root@server ~]# insmod /lib/modules/3.10.0-1160.el7.x86_64/kernel/net/ipv4/inet_diag.ko.xz

モジュールを確認します。inet_diagモジュールがロードされたことがわかります。

[root@server ~]# lsmod |grep -E 'tcp_diag|inet_diag'
inet_diag              18949  0

tcp_diagモジュールをロードします。先にinet_diagモジュールがロードされているので、エラーは発生しません。

[root@server ~]# insmod /lib/modules/3.10.0-1160.el7.x86_64/kernel/net/ipv4/tcp_diag.ko.xz
[root@server ~]#

モジュールを確認します。tcp_diagモジュールがロードされたことがわかります。

[root@server ~]# lsmod |grep -E 'tcp_diag|inet_diag'
tcp_diag               12591  0
inet_diag              18949  1 tcp_diag

7 modinfoコマンドの使い方

modinfoコマンドを実行して、tcp_diagモジュールの情報を確認してみます。depends行にinet_diagと記載されていることがわかります。つまり、tcp_diagモジュールはinet_diagモジュールに依存していることがわかります。

[root@server ~]# modinfo tcp_diag
filename:       /lib/modules/3.10.0-1160.el7.x86_64/kernel/net/ipv4/tcp_diag.ko.xz
alias:          net-pf-16-proto-4-type-2-6
license:        GPL
retpoline:      Y
rhelversion:    7.9
srcversion:     CE94B895ACFDEA7DE6FB8E5
depends:        inet_diag
intree:         Y
vermagic:       3.10.0-1160.el7.x86_64 SMP mod_unload modversions
signer:         CentOS Linux kernel signing key
sig_key:        E1:FD:B0:E2:A7:E8:61:A1:D1:CA:80:A2:3D:CF:0D:BA:3A:A4:AD:F5
sig_hashalgo:   sha256

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

sudoコマンドの使い方

1 sudoコマンドとは?

現在ログインしている利用者が別の利用者(特に特権ユーザー)の権限でプログラムを実行するためのコマンドです。

2 検証環境

CentOS版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

3 事前準備

テスト用のユーザを作成します。なお、useraddコマンド使い方は、useradd/groupaddコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# useradd test1

作成したユーザのパスワードを設定します。

[root@server ~]# passwd test1
ユーザー test1 のパスワードを変更。
新しいパスワード:
新しいパスワードを再入力してください:
passwd: すべての認証トークンが正しく更新できました。

作成したユーザを確認します。test1が作成されたことがわかります。

[root@server ~]# id test1
uid=1000(test1) gid=1000(test1) groups=1000(test1)

4 sudoersファイルの書式

sudoersファイルは/etc/sudoers.d配下に作成します。書式は以下になります。

ユーザに対して実行可能なコマンドを定義する場合は以下の書式になります。

ユーザ名 ホスト名=(実行ユーザ名) コマンド1,コマンド2,...

グループに対して実行可能なコマンドを定義する場合は以下の書式になります。

%グループ名 ホスト名=(実行ユーザ名) コマンド1,コマンド2,...

下記定義は、「test1ユーザは、全てのホスト(ALL)で、root権限でcatコマンドを実行できる」と解釈します。

[root@server ~]# cat /etc/sudoers.d/test1
test1 ALL=(root) /usr/bin/cat

5 特定ユーザに全てのコマンドの実行を許可する方法

test1ユーザに対して、全てのコマンドの実行を許可してみます。

[root@server ~]# visudo -f /etc/sudoers.d/test
test1 ALL=(ALL) ALL

設定内容を確認します。

[root@server ~]# cat /etc/sudoers.d/test
test1 ALL=(ALL) ALL

test1ユーザに切り替えます。

[root@server ~]# su - test1

tcpdumpコマンドを実行します。パスワードを入力すると、tcpdumpコマンドが実行できることがわかります。

[test1@server ~]$ sudo tcpdump -i eth0 icmp

あなたはシステム管理者から通常の講習を受けたはずです。
これは通常、以下の3点に要約されます:

    #1) 他人のプライバシーを尊重すること。
    #2) タイプする前に考えること。
    #3) 大いなる力には大いなる責任が伴うこと。

[sudo] test1 のパスワード:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

6 パスワード入力を要求しないようにする方法(NOPASSWD)

test1ユーザに対して、全てのコマンドの実行を許可してみます。このとき、パスワードの入力を求められないようにします。

[root@server ~]# visudo -f /etc/sudoers.d/test
test1 ALL=(ALL) NOPASSWD:ALL

登録した設定内容を確認します。

[root@server ~]# cat /etc/sudoers.d/test
test1 ALL=(ALL) NOPASSWD:ALL

test1ユーザに切り替えます。

[root@server ~]# su - test1
最終ログイン: 2022/05/22 (日) 21:35:37 JST日時 pts/0

tcpdumpコマンドを実行します。パスワードの入力を求められることなく、tcpdumpコマンドが実行できることがわかります。

[test1@server ~]$  sudo tcpdump -i eth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

7 特定コマンドの実行を許可する方法

7.1 単一コマンドの実行を許可する方法

test1ユーザがtcpdumpコマンドだけを実行できるようにしてみます。

[root@server ~]# visudo -f /etc/sudoers.d/test
[root@server ~]# cat /etc/sudoers.d/test
test1 ALL=(ALL) NOPASSWD:/usr/sbin/tcpdump

test1ユーザに切り替えます。

[root@server ~]# su - test1
最終ログイン: 2022/05/22 (日) 21:39:04 JST日時 pts/0
  • tcpdumpコマンドを実行した場合

test1ユーザは、tcpdumpコマンドを実行できることがわかります。

[test1@server ~]$ sudo tcpdump -i eth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
  • useraddコマンドを実行した場合

test1ユーザは、useraddコマンドを実行できないことがわかります。

[test1@server ~]$ sudo useradd test2
[sudo] test1 のパスワード:
残念、また試してください。
[sudo] test1 のパスワード:
残念ですが、ユーザー test1 は'/sbin/useradd test2' を root として server 上で実行することは許可されていません。

7.2 複数コマンドの実行を許可する方法

test1ユーザがtcpdump、useraddコマンドを実行できるようにしてみます。

[root@server ~]# visudo -f /etc/sudoers.d/test
[root@server ~]# cat /etc/sudoers.d/test
test1 ALL=(ALL) NOPASSWD:/usr/sbin/tcpdump,/usr/sbin/useradd

test1ユーザに切り替えます。

[root@server ~]# su - test1
最終ログイン: 2022/05/22 (日) 21:41:10 JST日時 pts/0

test1ユーザの権限でtcpdumpコマンドが実行できることがわかります。

[test1@server ~]$ sudo tcpdump -i eth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

test1ユーザの権限でuseradd コマンドが実行できることがわかります。

[test1@server ~]$ sudo useradd test2
[test1@server ~]$

8 グループに対してコマンドの実行を許可する方法

test1,test2ユーザを作成します。それぞれがtestというプライマリグループに所属するようにします。そして、testグループに対してtcpdumpの実行を許可してみます。

8.1 事前準備

testグループを作成します。なお、groupaddコマンド使い方は、useradd/groupaddコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# groupadd test

test1,test2ユーザを作成します。

[root@server ~]# useradd test1
[root@server ~]# useradd test2

test1,test2ユーザをプライマリグループに所属するようにします。

[root@server ~]# usermod -g test test1
[root@server ~]# usermod -g test test2

test1,test2ユーザがtestグループに所属していることがわかります。

[root@server ~]# id test1
uid=1000(test1) gid=1000(test) groups=1000(test)
[root@server ~]# id test2
uid=1001(test2) gid=1000(test) groups=1000(test)

8.2 実行結果

testグループに所属するユーザがtcpdumpコマンドを実行できるようにします。

[root@server ~]# visudo -f /etc/sudoers.d/test
[root@server ~]# cat /etc/sudoers.d/test
%test ALL=(ALL) NOPASSWD:/usr/sbin/tcpdump

test1ユーザに切り替えます。

[root@server ~]# su - test1

test1ユーザ権限でtcpdumpコマンドが実行できることがわかります。

[test1@server ~]$ sudo tcpdump -i eth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

test2ユーザに切り替えます。

[root@server ~]# su - test2

test2ユーザ権限でtcpdumpコマンドが実行できることがわかります。

[test2@server ~]$  sudo tcpdump -i eth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

9 エイリアス

エイリアスは、定義ファイルを簡潔に記述するためのものです。/etc/sudoersに登録されていて、ホスト、ユーザ、コマンド用のものがあります。それぞれ、Host_Alias,User_Alias,Cmnd_Aliasを用いて定義します。また、エイリアスを自身で定義することもできます。

9.1 コマンドエイリアスの種類

エイリアス エイリアスに含まれるコマンド
NETWORKING route,ifconfig,ping,dhclient,net,iptables, rfcomm,wvdial,iwconfig,mii-tool
SOFTWARE rpm,up2date,yum
SERVICES service,chkconfig,systemctl start,stop等
LOCATE updatedb
STORAGE fdisk,sfdisk,parted,partprobe,mount,umount
DELEGATING visudo,chown,chmod,chgrp
PROCESSES nice,kill,killall
DRIVERS modprobe

9.2 実行結果

ここでは、SOFTWAREエイリアスを使ってみます。SOFTWAREエイリアスを有効にすることで、rpm,yum,up2dateを個々に定義する必要がなくなります。

visudoコマンドを実行して、/etc/sudoersのSOFTWAREを有効('#'を削除する)にします。

[root@server ~]# visudo
## Installation and management of software
# Cmnd_Alias SOFTWARE = /bin/rpm, /usr/bin/up2date, /usr/bin/yum
Cmnd_Alias SOFTWARE = /bin/rpm, /usr/bin/up2date, /usr/bin/yum

test1ユーザがSOFTWAREエイリアスを実行できるようにしてみます。

[root@server ~]# visudo -f /etc/sudoers.d/test
test1 ALL=(ALL) NOPASSWD:SOFTWARE

設定内容を確認します。test1ユーザが、パスワードなしでSOFTWAREエイリアスを実行できることがわかります。

[root@server ~]# cat /etc/sudoers.d/test
test1 ALL=(ALL) NOPASSWD:SOFTWARE

test1ユーザに切り替えます。

[root@server ~]# su - test1

ユーザtest1の権限でbcパッケージをインストールできることがわかります。

[test1@server ~]$ sudo yum -y install bc

10 ログの保存場所

sudoコマンドの実行結果は、/var/log/secureに保存されます。
test1ユーザに対して、全てのコマンドの実行を許可してみます。このとき、パスワードの入力を求められないようにします。

[root@server ~]# cat /etc/sudoers.d/test
test1 ALL=(ALL) NOPASSWD:ALL

test1ユーザに切り替えます。

[root@server ~]# su - test1

test1ユーザ権限でtcpdumpコマンドを実行します。

[test1@server ~]$ sudo tcpdump -i eth0 icmp

test1ユーザがtcpdumpコマンドを実行すると、以下のメッセージがログに記録されます。

[root@server ~]# tail -f /var/log/secure
May 23 20:05:22 server sudo:   test1 : TTY=pts/0 ; PWD=/home/test1 ; USER=root ; COMMAND=/sbin/tcpdump -i eth0 icmp

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

psコマンドの使い方

1 psコマンドとは?

プロセスの状態を確認するコマンドです。

2 検証環境

CentOS版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

搭載CPU数は4個です。

[root@server ~]# cat /proc/cpuinfo |grep processor
processor       : 0
processor       : 1
processor       : 2
processor       : 3

搭載メモリ量は4Gです。

[root@server ~]# free
              total        used        free      shared  buff/cache   available
Mem:        3861292      370416     3181612        3660      309264     3248008
Swap:        131068           0      131068

3 プロセスの状態を表示する方法

3.1 コマンド名を表示する方法(comm)

sleepコマンドを実行します。

[root@server ~]# sleep 300&
[2] 2440

プロセスの状態を確認します。COMMAND列がsleepになっていることがわかります。

[root@server ~]# ps -C sleep -o comm
COMMAND
sleep

あと始末をします。

[root@server ~]# kill -9 2440
[root@server ~]#
[2]+  強制終了            sleep 300

3.2 プロセスのPID,PPIDを表示する方法(pid,ppid)

sleepコマンドを実行します。

[root@server ~]# sleep 300&
[1] 2085

プロセスの状態を確認します。PIDはsleepプロセス自身のPID、PPIDはsleepプロセスの親プロセスのPIDを表しています。

[root@server ~]# ps -C sleep -o comm,pid,ppid
COMMAND            PID   PPID
sleep             2085   1965

あと始末をします。

[root@server ~]# kill -9 2085
[root@server ~]#
[1]+  強制終了            sleep 300

3.3 プロセスの仮想メモリサイズ、実メモリサイズを表示する方法(vsz,rss)

sleepコマンドを実行します。

[root@server ~]# sleep 300&
[1] 2096

プロセスの状態を確認します。VSZが仮想メモリサイズ、RSS実メモリサイズを表しています。RSSは、プロセスに割り当てられている実メモリ量を表しています。

[root@server ~]# ps -C sleep -o comm,pid,vsz,rss
COMMAND            PID    VSZ   RSS
sleep             2096 108052   356

あと始末をします。

[root@server ~]# kill -9 2096
[root@server ~]#
[1]+  強制終了            sleep 300

3.4 コマンドの実行時刻を表示する方法(lstart)

sleepコマンドを実行します。

[root@server ~]# date;sleep 300 &
2022年  5月 15日 日曜日 21:14:52 JST
[1] 2100

プロセスの状態を確認します。sleepコマンドの実行時刻が、21:14:51 であることがわかります。

[root@server ~]# ps -C sleep -o comm,pid,lstart
COMMAND            PID                  STARTED  START
sleep             2100 Sun May 15 21:14:51 2022  21:14

あと始末をします。

[root@server ~]# kill -9 2100
[root@server ~]#
[1]+  強制終了            sleep 300

3.5 コマンド実行開始からの経過時間を表示する方法(etime)

sleepコマンドを実行します。

[root@server ~]# date;sleep 300 &
2022年  5月 15日 日曜日 21:32:20 JST
[1] 2154

プロセスの状態を確認します。sleepコマンドを実行してから8秒経過していることがわかります。

[root@server ~]#  date;ps -C sleep -o comm,pid,etime
2022年  5月 15日 日曜日 21:32:27 JST
COMMAND            PID     ELAPSED
sleep             2154       00:08

あと始末をします。

[root@server ~]# kill -9 2154
[root@server ~]#
[1]+  強制終了            sleep 300

3.6 CPU使用率を表示する方法(%cpu)

プロセスのCPU使用率が10%になるように、stress-ngコマンドを実行します。なお、stress-ngコマンドのインストール方法、使い方は、stress-ngコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# stress-ng -k -c 1 -l 10 -q &
[1] 2181

プロセスの状態を確認します。stress-ngプロセスのCPU使用率が10.2%であることがわかります。stress-ngコマンドで指定したCPU使用率におおよそ等しいことがわかります。

[root@server ~]# ps -C stress-ng -o comm,pid,ppid,wchan,%cpu
COMMAND            PID   PPID WCHAN  %CPU
stress-ng         2181   1965 do_wai  0.0
stress-ng         2182   2181 poll_s 10.2

あと始末をします。

[root@server ~]# pkill stress-ng
[root@server ~]#
[1]+  終了                  stress-ng -k -c 1 -l 10 -q

3.7 メモリ使用率を表示する方法(%mem)

搭載メモリ量を確認します。3861292(KB)であることがわかります。

[root@server ~]# free -k
              total        used        free      shared  buff/cache   available
Mem:        3861292      405288     3189320        3692      266684     3212048
Swap:        131068           0      131068
  • 256Mのメモリを獲得した場合

256Mのメモリを獲得するように、stressコマンドを実行します。なお、stressコマンドのインストール方法、使い方は、stressコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# stress -m 1 --vm-bytes 268435456 --vm-hang 0 -q &

プロセスの状態を確認します。搭載メモリの6.7%を使用していることがわかります。

[root@server ~]# ps -C stress -o comm,pid,ppid,%mem,rss
COMMAND            PID   PPID %MEM   RSS
stress            2346   1960  0.0   428
stress            2347   2346  6.7 262296

搭載メモリ(3861292KB)の6.7%は、およそ252Mであることがわかります。stressコマンドで獲得した物理メモリ量におおよそ等しいことがわかります。

[root@server ~]# bc -q
3861292*0.067
258706.564

あと始末をします。

[root@server ~]# pkill stress
[1]+  Terminated              stress -m 1 --vm-bytes 268435456 --vm-hang 0 -q
  • 128Mのメモリを獲得した場合

128Mのメモリを獲得するように、stressコマンドを実行します。

[root@server ~]# stress -m 1 --vm-bytes 134217728 --vm-hang 0 -q &

プロセスの状態を確認します。搭載メモリの3.3%を使用していることがわかります。

[root@server ~]# ps -C stress -o comm,pid,ppid,%mem,rss
COMMAND            PID   PPID %MEM   RSS
stress            2391   1960  0.0   428
stress            2392   2391  3.3 131216

搭載メモリ(3861292KB)の3.3%は、およそ124Mであることがわかります。stressコマンドで獲得した物理メモリ量におおよそ等しいことがわかります。

[root@server ~]# bc -q
3861292*0.033
127422.636

あと始末をします。

[root@server ~]# pkill stress
[1]+  Terminated              stress -m 1 --vm-bytes 134217728 --vm-hang 0 -q

3.8 プロセスがスリープしている関数を表示する方法(wchan)

sleepコマンドを実行します。

[root@server ~]#  sleep 300&
[1] 2285

sleepプロセスは、hrtimer_nanosleep関数を呼び出してスリープ(S列がS)していることがわかります。

[root@server ~]# ps -C sleep -o comm,pid,state,wchan
COMMAND            PID S WCHAN
sleep             2285 S hrtimer_nanosleep

参考までに、以下にhrtimer_nanosleep関数を抜粋します。hrtimer_nanosleep関数は、kernel/hrtimer.cで定義されています。

long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
                       const enum hrtimer_mode mode, const clockid_t clockid)
{
        struct restart_block *restart;
        struct hrtimer_sleeper t;
        int ret = 0;
        unsigned long slack;

        slack = current->timer_slack_ns;
        if (dl_task(current) || rt_task(current))
                slack = 0;

        hrtimer_init_on_stack(&t.timer, clockid, mode);
        hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack);
        if (do_nanosleep(&t, mode))
                goto out;

        /* Absolute timers do not update the rmtp value and restart: */
        if (mode == HRTIMER_MODE_ABS) {
                ret = -ERESTARTNOHAND;
                goto out;
        }

        if (rmtp) {
                ret = update_rmtp(&t.timer, rmtp);
                if (ret <= 0)
                        goto out;
        }

        restart = &current_thread_info()->restart_block;
        restart->fn = hrtimer_nanosleep_restart;
        restart->nanosleep.clockid = t.timer.base->clockid;
        restart->nanosleep.rmtp = rmtp;
        restart->nanosleep.expires = hrtimer_get_expires_tv64(&t.timer);

        ret = -ERESTART_RESTARTBLOCK;
out:
        destroy_hrtimer_on_stack(&t.timer);
        return ret;
}

3.9 プロセスが動作しているCPUを表示する方法(psr)

sleepコマンドを実行します。このとき、tasksetコマンドを使って、sleepプロセスをCPU0で動作させます。なお、tasksetコマンドのインストール方法、使い方は、tasksetコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# taskset -c 0 sleep 300&
[1] 2327

プロセスの状態を確認します。sleepプロセスがCPU0で動作していることがわかります。

[root@server ~]# ps -C sleep -o comm,pid,psr
COMMAND            PID PSR
sleep             2327   0

後始末をします。

[root@server ~]# kill -9 2327
[root@server ~]#
[1]+  強制終了            taskset -c 0 sleep 300

次に、sleepプロセスをCPU2で動作させます。

[root@server ~]# taskset -c 2 sleep 300&
[1] 2357

プロセスの状態を確認します。sleepプロセスがCPU2で動作していることがわかります。

[root@server ~]# ps -C sleep -o comm,pid,psr
COMMAND            PID PSR
sleep             2357   2

後始末をします。

[root@server ~]# kill -9 2357
[root@server ~]#
[1]+  強制終了            taskset -c 2 sleep 300

3.10 スレッドIDを表示する方法(lwp,spid,tid)

スレッドIDを表示するためのオプションとして、lwp,spid,tidがあります。spid, tidはlwpの別名です。

テストプログラムを作成します。

[root@server ~]# vi tp.c
[root@server ~]# cat tp.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>

void *threadFunc1(void)
{
    int fd;
    struct timespec req;
    char str[] = "0123456789";

    req.tv_sec = 300;
    req.tv_nsec = 0;

    for(;;) {
        fd = open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);

        write(fd, str, (size_t)sizeof(str));
        close(fd);

        nanosleep(&req, NULL);
    }
}

int main(int argc, char *argv[])
{
    pthread_t t1;
    void *res;

    pthread_create(&t1, NULL, (void *)&threadFunc1, NULL);

    pthread_join(t1, &res);
    exit(0);
}

テストプログラムをコンパイルします。

[root@server ~]# gcc -Wall -pthread -o tp tp.c

テストプログラムを実行します。

[root@server ~]# ./tp

プロセスの状態を確認します。このテストプログラムを実行すると、プロセスが1つ(PID=2571)、スレッドが2つ(LWP=2571,2572)生成されることがわかります。

[root@server ~]# ps -LC tp -o comm,pid,lwp,spid,tid,wchan
COMMAND            PID    LWP   SPID    TID WCHAN
tp                2571   2571   2571   2571 futex_wait_queue_me
tp                2571   2572   2572   2572 hrtimer_nanosleep

4 プロセスのスケジューリング・ポリシー、優先度を表示する方法

4.1 リアルタイムプロセスの場合(rtprio)

テストプログラムを作成します。

[root@server ~]# vi rt.c
[root@server ~]# cat rt.c
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    struct sched_param sp;
    int policy;

    if(argv[1][0] == 'r')
        policy = SCHED_RR ;
    else if (argv[1][0] == 'f')
        policy = SCHED_FIFO ;
    else {
        fprintf(stderr, "Error\n") ;
        exit(1);
    }

    sp.sched_priority = atoi(argv[2]);
    printf("policy=%d,priority=%d\n", policy,sp.sched_priority);

    sched_setscheduler(0, policy, &sp);
    sleep(60*10) ;

    return 0;
}

テストプログラムをコンパイルします。

[root@server ~]# gcc -Wall -o rt rt.c
  • SCHED_FIFOの場合
[root@server ~]# ./rt f 10
policy=1,priority=10

プロセスの状態を確認します。プロセスのスケジューリング・ポリシー(CLS列)がSCHED_FIFO(FF)、優先度(RTPRIO列)が10であることがわかります。

[root@server ~]# ps -C rt -o comm,cls,rtprio
COMMAND         CLS RTPRIO
rt               FF     10
  • SCHED_RRの場合
[root@server ~]# ./rt r 99
policy=2,priority=99

プロセスの状態を確認します。プロセスのスケジューリング・ポリシー(CLS列)がSCHED_RR(RR)、優先度(RTPRIO列)が99であることがわかります。

[root@server ~]# ps -C rt -o comm,cls,rtprio
COMMAND         CLS RTPRIO
rt               RR     99
  • watchdogプロセスの場合

watchdogプロセスの優先度を確認してみます。watchdogプロセスは、SCHED_FIFO、優先度が99であることがわかります

[root@server ~]# ps -C watchdog/0 -o comm,cls,rtprio
COMMAND         CLS RTPRIO
watchdog/0       FF     99

4.2 タイムスライスプロセスの場合(ni)

テストプログラムを作成します。なお、タイムスライスプロセスは、カーネルソースコードでは、SCHED_OTHER というマクロで定義されています。

[root@server ~]# vi ts.c
[root@server ~]# cat ts.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/resource.h>

int main(int argc, char *argv[])
{
    int nice;

    printf("Initial nice value is %d\n", getpriority(0, 0)) ;

    setpriority(0, 0, atoi(argv[1]));
    nice = getpriority(0, 0) ;
    printf("nice value is %d\n", nice) ;

    sleep(60*10) ;
    return 0;
}

テストプログラムをコンパイルします。

[root@server ~]# gcc -Wall -o ts ts.c
  • 最高優先度(NICE値=-20)

プロセスのNICE値が-20になるようにテストプログラムを実行します。NICE値-20は、スケジューリングポリシーがSCHED_OTHERの中で最高優先度になります。

[root@server ~]# ./ts -20
Initial nice value is 0
nice value is -20

プロセスの状態を確認します。プロセスのスケジューリング・ポリシー(CLS列)がタイムスライス(TS)、NICE値が-20であることがわかります。

[root@server ~]# ps -C ts -o comm,cls,ni
COMMAND         CLS  NI
ts               TS -20
  • デフォルト値の場合(NICE値=0)

プロセスのNICE値が0になるようにテストプログラムを実行します。

[root@server ~]# ./ts 0
Initial nice value is 0
nice value is 0

プロセスの状態を確認します。プロセスのスケジューリング・ポリシー(CLS列)がタイムスライス(TS)、NICE値が0であることがわかります。

[root@server ~]# ps -C ts -o comm,cls,ni
COMMAND         CLS  NI
ts               TS   0
  • 最低優先度の場合(NICE値=19)

プロセスのNICE値が19になるようにテストプログラムを実行します。NICE値19は、スケジューリングポリシーがSCHED_OTHERの中で最低優先度になります。

[root@server ~]# ./ts 19
Initial nice value is 0
nice value is 19

プロセスの状態を確認します。プロセスのスケジューリング・ポリシー(CLS列)がタイムスライス(TS)、NICE値が19であることがわかります。

[root@server ~]# ps -C ts -o comm,cls,ni
COMMAND         CLS  NI
ts               TS  19

5 ソートする方法(--sort Keyword)

--sortオプションを使うと、プロセスのCPU使用率やメモリ使用量(rss)等を昇順、降順に並べ替えることができます。

5.1 CPU使用率を昇順、降順に表示する方法

プロセスのCPU使用率が10%,20%,30%になるようにstress-ngコマンドを実行します。

[root@server ~]# stress-ng -k -c 1 -l 10 -q &
[1] 2710
[root@server ~]# stress-ng -k -c 1 -l 20 -q &
[2] 2712
[root@server ~]# stress-ng -k -c 1 -l 30 -q &
[3] 2714

CPU使用率を降順に表示してみます。

[root@server ~]# ps -C stress-ng -o comm,%cpu --sort -%cpu
COMMAND         %CPU
stress-ng       29.9
stress-ng       19.8
stress-ng       10.3
stress-ng        0.0
stress-ng        0.0
stress-ng        0.0

CPU使用率を昇順に表示してみます。

[root@server ~]# ps -C stress-ng -o comm,%cpu --sort +%cpu
COMMAND         %CPU
stress-ng        0.0
stress-ng        0.0
stress-ng        0.0
stress-ng        9.9
stress-ng       20.0
stress-ng       29.8

stress-ngプロセス以外のプロセスも含めて、CPU使用率を降順に表示してみます。

[root@server ~]# ps ax -o comm,%cpu --sort -%cpu |head -n 10
COMMAND         %CPU
stress-ng       29.9
stress-ng       19.9
stress-ng       10.1
vmtoolsd         0.1
systemd          0.0
kthreadd         0.0
kworker/0:0H     0.0
ksoftirqd/0      0.0
migration/0      0.0

後始末をします。

[root@server ~]# pkill stress-ng
[root@server ~]#
[1]   終了                  stress-ng -k -c 1 -l 10 -q
[2]-  終了                  stress-ng -k -c 1 -l 20 -q
[3]+  終了                  stress-ng -k -c 1 -l 30 -q

5.2 メモリ使用量を昇順、降順に表示する方法

プロセスが獲得する実メモリサイズが128Mになるようにstressコマンドを実行します。
なお、stressコマンドのインストール、使い方は、stressコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# stress -m 1 --vm-bytes 134217728 --vm-hang 0 -q &
[1] 2795

プロセスが獲得する実メモリサイズが64Mになるようにstressコマンドを実行します。

[root@server ~]# stress -m 1 --vm-bytes 67108864 --vm-hang 0 -q &
[2] 2797

メモリ使用量を降順に表示してみます。

[root@server ~]# ps -C stress -o comm,pid,rss --sort -rss
COMMAND            PID   RSS
stress            2796 131212
stress            2798 65544
stress            2797   428
stress            2795   424

メモリ使用量を昇順に表示してみます。

[root@server ~]# ps -C stress -o comm,pid,rss --sort +rss
COMMAND            PID   RSS
stress            2795   424
stress            2797   428
stress            2798 65544
stress            2796 131212

5.3 PIDを昇順、降順に表示する方法

  • PIDを昇順に表示した場合
[root@server ~]# ps ax -o comm,pid --sort pid |head -n 5
COMMAND            PID
systemd              1
kthreadd             2
kworker/0:0H         4
ksoftirqd/0          6
  • PIDを降順に表示した場合
[root@server ~]# ps ax -o comm,pid --sort -pid |head -n 5
COMMAND            PID
head              2741
ps                2740
kworker/0:2       2724
kworker/0:0       2703

6 実行ユーザ名、実行グループ名を表示する方法

[root@server ~]# systemctl start httpd.service
[root@server ~]# ps -C httpd -o comm,pid,ppid,user,euser,group,egroup
COMMAND            PID   PPID USER     EUSER    GROUP    EGROUP
httpd             2759      1 root     root     root     root
httpd             2760   2759 apache   apache   apache   apache
httpd             2761   2759 apache   apache   apache   apache
httpd             2762   2759 apache   apache   apache   apache
httpd             2763   2759 apache   apache   apache   apache
httpd             2764   2759 apache   apache   apache   apache

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

inotifywaitコマンドの使い方

1 inotifywaitコマンドとは?

ファイルやディレクトの作成/削除等で発生するイベントを監視するコマンドです。以下のシステムコールを使ってイベントを監視しています。
・inotify_init
・inotify_add_watch

2 検証環境

CentOS版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

3 inotifywaitコマンドのインストール方法

inotify-toolsパッケージはepelレポジトリにあるので、まずepel-releaseパッケージをインストールします。

[root@server ~]# yum -y install epel-release

次に、notify-toolsパッケージをインストールします。

[root@server ~]# yum -y install inotify-tools

inotifywaitコマンドの版数を確認します。

[root@server ~]# inotifywait --help
inotifywait 3.14
-snip-

4 オプション一覧

オプションは以下のとおりです。

[root@server ~]# inotifywait --help
inotifywait 3.14
Wait for a particular event on a file or set of files.
Usage: inotifywait [ options ] file1 [ file2 ] [ file3 ] [ ... ]
Options:
        -h|--help       Show this help text.
        @<file>         Exclude the specified file from being watched.
        --exclude <pattern>
                        Exclude all events on files matching the
                        extended regular expression <pattern>.
        --excludei <pattern>
                        Like --exclude but case insensitive.
        -m|--monitor    Keep listening for events forever.  Without
                        this option, inotifywait will exit after one
                        event is received.
        -d|--daemon     Same as --monitor, except run in the background
                        logging events to a file specified by --outfile.
                        Implies --syslog.
        -r|--recursive  Watch directories recursively.
        --fromfile <file>
                        Read files to watch from <file> or `-' for stdin.
        -o|--outfile <file>
                        Print events to <file> rather than stdout.
        -s|--syslog     Send errors to syslog rather than stderr.
        -q|--quiet      Print less (only print events).
        -qq             Print nothing (not even events).
        --format <fmt>  Print using a specified printf-like format
                        string; read the man page for more details.
        --timefmt <fmt> strftime-compatible format string for use with
                        %T in --format string.
        -c|--csv        Print events in CSV format.
        -t|--timeout <seconds>
                        When listening for a single event, time out after
                        waiting for an event for <seconds> seconds.
                        If <seconds> is 0, inotifywait will never time out.
        -e|--event <event1> [ -e|--event <event2> ... ]
                Listen for specific event(s).  If omitted, all events are
                listened for.

Exit status:
        0  -  An event you asked to watch for was received.
        1  -  An event you did not ask to watch for was received
              (usually delete_self or unmount), or some error occurred.
        2  -  The --timeout option was given and no events occurred
              in the specified interval of time.

Events:
        access          file or directory contents were read
        modify          file or directory contents were written
        attrib          file or directory attributes changed
        close_write     file or directory closed, after being opened in
                        writeable mode
        close_nowrite   file or directory closed, after being opened in
                        read-only mode
        close           file or directory closed, regardless of read/write mode
        open            file or directory opened
        moved_to        file or directory moved to watched directory
        moved_from      file or directory moved from watched directory
        move            file or directory moved to or from watched directory
        create          file or directory created within watched directory
        delete          file or directory deleted within watched directory
        delete_self     file or directory was deleted
        unmount         file system containing file or directory unmounted

4 指定したイベントを監視する方法(-e)

4.1 イベントの種類

イベントには以下のものがあります。

Events:
        access          file or directory contents were read
        modify          file or directory contents were written
        attrib          file or directory attributes changed
        close_write     file or directory closed, after being opened in
                        writeable mode
        close_nowrite   file or directory closed, after being opened in
                        read-only mode
        close           file or directory closed, regardless of read/write mode
        open            file or directory opened
        moved_to        file or directory moved to watched directory
        moved_from      file or directory moved from watched directory
        move            file or directory moved to or from watched directory
        create          file or directory created within watched directory
        delete          file or directory deleted within watched directory
        delete_self     file or directory was deleted
        unmount         file system containing file or directory unmounted

4.2 openイベントの使い方(-e open)

openイベントは、ファイルやディレクトリをオープンしたときに発生します。

まず、テスト用のディレクトリを作成します。

[root@server ~]# mkdir /test

ファイルを作成します。

[root@server ~]# touch /test/test.txt

inotifywaitコマンドを実行します。このとき、ファイル(test.txt)をopenしたときのイベントを監視します。

[root@server ~]# inotifywait -e open /test/test.txt
Setting up watches.
Watches established.

もう1つターミナルを開きます。そして、catコマンドでファイルをリードします。

[root@server ~]# cat /test/test.txt
[root@server ~]#

inotifywaitコマンドの実行結果を確認します。OPENイベントが発生していることがわかります。

[root@server ~]# inotifywait -e open /test/test.txt
Setting up watches.
Watches established.
/test/test.txt OPEN

lsコマンドでディレクトリの中身を表示しても、OPENイベントが発生します。lsコマンドで/tmpの中身を表示してみます。

[root@server ~]# inotifywait -e open /test
Setting up watches.
Watches established.

/tmpディレクトリに対して、lsコマンドを実行します。

[root@server ~]# ls /test/
test.txt

inotifywaitコマンドの実行結果を確認します。OPENイベントが発生していることがわかります。

[root@server ~]# inotifywait -e open /test
Setting up watches.
Watches established.
/test/ OPEN,ISDIR

4.3 attribイベントの使い方(-e attrib)

attribイベントは、ファイルやディレクトリの属性が変更されたときに発生します。

[root@server ~]# inotifywait -e attrib /test/test.txt
Setting up watches.
Watches established.

ファイルの属性を確認します。

[root@server ~]# ls -l /test/test.txt
-rw-r--r--. 1 root root 0  4月 25 19:09 /test/test.txt

ファイルの属性を変更します。

[root@server ~]# chmod 666 /test/test.txt

inotifywaitコマンドの実行結果を確認します。ATTRIBイベントが発生していることがわかります。

[root@server ~]# inotifywait -e attrib /test/test.txt
Setting up watches.
Watches established.
/test/test.txt ATTRIB

4.4 modifyイベントの使い方(-e modify)

MODIFYイベントは、ファイルを作成したりファイルに書き込みをすると発生します。
ここでは、/testディレクトリ配下にファイルが作成されたら発生するMODIFYイベントを監視してみます。

[root@server ~]# inotifywait -e modify /test
Setting up watches.
Watches established.

ファイルに書き込みをします。

[root@server ~]# echo 11 > /test/aa

inotifywaitコマンドの実行結果を確認します。MODIFYイベントが発生していることがわかります。

[root@server ~]# inotifywait -e modify /test
Setting up watches.
Watches established.
/test/ MODIFY aa

4.5 deleteイベントの使い方(-e delete)

DELETEイベントは、指定したディレクト配下のファイルやディレクトリが削除されると発生します。

[root@server ~]# inotifywait -e delete /test
Setting up watches.
Watches established.

テスト用のファイルを作成します。

[root@server ~]# touch /test/test.txt

テスト用のファイルを削除します。

[root@server ~]# rm /test/test.txt
rm: 通常の空ファイル `/test/test.txt' を削除しますか? y

inotifywaitコマンドの実行結果を確認します。DELETEイベントが発生していることがわかります。

[root@server ~]# inotifywait -e delete /test
Setting up watches.
Watches established.
/test/ DELETE test.txt

4.6 delete_selfイベントの使い方(-e delete_self)

DELETE_SELFイベントは、指定したファイルやディレクトリが削除されると発生します。

テスト用のファイルを作成します。

[root@server ~]# touch /test/test.txt

ファイルが削除されたら発生するDELETE_SELFイベントを監視してみます。

[root@server ~]# inotifywait -m -e delete_self /test/test.txt
Setting up watches.
Watches established.

ファイルを削除します。

[root@server ~]# rm /test/test.txt
rm: 通常の空ファイル `/test/test.txt' を削除しますか? y

inotifywaitコマンドの実行結果を確認します。DELETE_SELFイベントが発生していることがわかります。

[root@server ~]# inotifywait -m -e delete_self /test/test.txt
Setting up watches.
Watches established.
/test/test.txt DELETE_SELF

4.7 createイベントの使い方(-e create)

CREATEイベントは、指定したディレクトリ配下にファイルやディレクトリを作成すると発生します。

テスト用のディレクトリを作成します。

[root@server ~]# mkdir /a

ディレクトリを作成したら発生するCREATEイベントを監視してみます。

[root@server ~]# inotifywait -m -e create /a
Setting up watches.
Watches established.

/aディレクトリに/bディレクトリを作成します。

[root@server ~]# mkdir /a/b

inotifywaitコマンドの実行結果を確認します。CREATEイベントが発生していることがわかります。

[root@server ~]# inotifywait -m -e create /a
Setting up watches.
Watches established.
/a/ CREATE,ISDIR b

4.8 moved_fromイベントの使い方(-e moved_from)

MOVED_FROMイベントは、指定したディレクトリ配下からファイルやディレクトを移動すると発生します。

テスト用のファイルを作成します。

[root@server ~]# touch /test/test.txt

/testディレクトリ配下にファイルが移動されたら発生するMOVED_FROMイベントを監視してみます。

[root@server ~]# inotifywait -m -e moved_from /test
Setting up watches.
Watches established.

ファイルを/testディレクトリから/tmpディレクトリに移動します。

[root@server ~]# mv /test/test.txt /tmp/

inotifywaitコマンドの実行結果を確認します。MOVED_FROMイベントが発生していることがわかります。

[root@server ~]# inotifywait -m -e moved_from /test
Setting up watches.
Watches established.
/test/ MOVED_FROM test.txt

4.9 moved_toイベントの使い方(-e moved_to)

moved_toイベントは、指定したディレクトリ配下へファイルやディレクトを移動すると発生します。

テスト用のファイルを作成します。

[root@server ~]# touch /test/test.txt

/tmpディレクトリ配下にファイルが移動されたら発生するMOVED_TOイベントを監視してみます。

[root@server ~]# inotifywait -m -e moved_to /tmp
Setting up watches.
Watches established.

ファイルを/testディレクトリから/tmpディレクトリに移動します。

[root@server ~]# mv /test/test.txt /tmp/

inotifywaitコマンドの実行結果を確認します。MOVED_TOイベントが発生していることがわかります。

[root@server ~]# inotifywait -m -e moved_to /tmp
Setting up watches.
Watches established.
/tmp/ MOVED_TO test.txt

4.10 unmountイベントの使い方(-e )

unmountイベントは、ファイルやディレクトリを含んだファイルシステムをアンマウントすると発生します。

まず、ループバックデバイスとして使うファイルを作成します。なお、ファイルの作成方法は、ファイルの作り方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# fallocate -l 100M disk.img

ループバックデバイスを作成します。ループバックデバイスの使い方は、losetupコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# losetup -f /root/disk.img

ループバックデバイスを確認します。

[root@server ~]# losetup -l
NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE
/dev/loop0         0      0         0  0 /root/disk.img

ループバックデバイスファイルシステムを作成します。

[root@server ~]# mkfs.ext4 /dev/loop0 

作成したファイルシステムを/mntにマウントします。

[root@server ~]# mount -t ext4 /dev/loop0 /mnt

ファイルシステムを確認します。ファイルシステムが/mntにマウントされたことがわかります。

[root@server ~]# df -ht ext4
ファイルシス   サイズ  使用  残り 使用% マウント位置
/dev/loop0        93M  1.6M   85M    2% /mnt

ファイルを作成します。

[root@server ~]# touch test.txt /mnt

ファイルシステムをアンマウントしたら発生するUNMOUNTイベントを監視してみます。

[root@server ~]# inotifywait -e unmount /mnt
Setting up watches.
Watches established.

作成したフィルシステムをアンマウントします。

[root@server ~]# umount /mnt

inotifywaitコマンドの実行結果を確認します。UNMOUNTイベントが発生していることがわかります。

[root@server ~]# inotifywait -e unmount /mnt
Setting up watches.
Watches established.
/mnt/ UNMOUNT

5 リカーシブオプションの使い方(-r)

-rはディレクトリ配下の全てのファイル、ディレクトリを監視対象にするオプションです。

5.1 事前準備

テスト用のディレクトリを作成します。

[root@server ~]# mkdir -p /a/b/c

テスト用のファイルを作成します。

[root@server ~]# touch /a/b/c/test.txt

作成したファイルを確認します。

[root@server ~]# ls -l /a/b/c/test.txt
-rw-r--r--. 1 root root 0  5月 14 09:48 /a/b/c/test.txt

5.2 リカーシブオプションを指定しない場合

[root@server ~]# inotifywait -m /a
Setting up watches.
Watches established.

ファイルに書き込みをします。

[root@server ~]# echo 11 > /a/b/c/test.txt

ファイルに書き込みをしてもイベントが発生しないことがわかります(期待値)。

[root@server ~]# inotifywait -m /a
Setting up watches.
Watches established.

5.3 リカーシブオプションを指定した場合

[root@server ~]# inotifywait -m -r /a
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.

ファイルに書き込みをします。

[root@server ~]# echo 11 > /a/b/c/test.txt

MODIFY,OPEN,CLOSE_WRITE,CLOSEのイベントが表示されていることがわかります(期待値)。

[root@server ~]# inotifywait -m -r /a
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.
/a/b/c/ MODIFY test.txt
/a/b/c/ OPEN test.txt
/a/b/c/ MODIFY test.txt
/a/b/c/ CLOSE_WRITE,CLOSE test.txt

6 時刻を表示する方法(timefmt)

timefmtは、時刻の表示形式を指定するオプションです。timefmtは、formatオプションと一緒に使う必要があります。formatは、表示項目を指定します。表示項目としては、時刻(%T)、監視対象(%w)、監視イベント(%e)等が指定できます。

[root@server ~]# inotifywait -m --format '%T %w %f %e' --timefmt '%F %T' /a
Setting up watches.
Watches established.

/aディレクトリ配下のファイルを表示します。

[root@server ~]# ls /a

1列目に時刻が表示されていることがわかります。

[root@server ~]# inotifywait -m --format '%T %w %f %e' --timefmt '%F %T' /a
Setting up watches.
Watches established.
2022-05-14 18:14:44 /a/  OPEN,ISDIR
2022-05-14 18:14:44 /a/  CLOSE_NOWRITE,CLOSE,ISDIR

7 出力メッセージをファイルに保存する方法(o)

-cは、inotifywait が出力するメッセージをファイルに保存するオプションです。

[root@server ~]# inotifywait -m /a -o event.log
Setting up watches.
Watches established.

/aディレクトリ配下のファイルを表示します。

[root@server ~]# ls /a

フメッセージを保存したファイルを確認します。

[root@server ~]# cat event.log
/a/ OPEN,ISDIR
/a/ CLOSE_NOWRITE,CLOSE,ISDIR

8 デーモンプロセスとして起動する方法(-d)

-dはinotifywaitプロセスをデーモンプロセスとして動かすオプションです。このとき、-oオプションも一緒に指定する必要があります。-oオプションで、デーモンプロセスが出力するメッセージの出力先を指定します。

まず、inotifywaitコマンドを実行して、inotifywaitをデーモンプロセスとして起動します。

[root@server ~]# inotifywait -m -d -o /tmp/event.log /a

psコマンドを実行して、プロセスの状態を確認します。TTY列が?となっているので、デーモンプロセスであることがわかります。なお、デーモンプロセスとは、標準入出力を持たないプロセスのことです。

[root@server ~]# ps -C inotifywait -o comm,pid,ppid,tty
COMMAND            PID   PPID TT
inotifywait       2131      1 ?

inotifywaitプロセスを終了します。

[root@server ~]# kill -9 2131
[root@server ~]#

9 csvフォーマットで出力する方法(-c)

-cはinotifywaitが出力するメッセージを","で区切るオプションです。

[root@server ~]# inotifywait -m -c /a
Setting up watches.
Watches established.

/aディレクトリ配下のファイルを表示します。

[root@server ~]# ls /a

各列が","で区切られていることがわかります。

[root@server ~]# inotifywait -m -c /a
Setting up watches.
Watches established.
/a/,"OPEN,ISDIR",
/a/,"CLOSE_NOWRITE,CLOSE,ISDIR",

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

Tera Termマクロの使い方

1 Tera Termマクロとは?

対話的なやりとりを自動化する言語です。Tera Termマクロは、ttlという拡張子を持ったファイルに記述します。そして、このファイルをクリックすると、たとえば、ネットワーク経由でホストに自動でログインすることができるようになります。

2 検証環境

PCでTera Termマクロを作成し、実行します。

    PC                      server                   client
(Windows10)              (CentOS7.9)                (CentOS7.9)
    |                         | .100                     | .105
  --------------------------------------------------------------
                        192.168.2.0/24

サーバ、クライアントのCentOS版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

Tera Termの版数は4.99です。

3 ホストへの自動ログイン方法

Tera Termマクロ を実行すると、PCからホストにログインできます。

3.1 パスワード認証でログインする方法

パスワード認証でサーバにログインするマクロを以下に示します。なお、connectの使い方は、connectを参照してください。

connect '192.168.2.100:22 /ssh /2 /auth=password /user=root /passwd=xxxxx'
wait '#'

Windows上でテキストエディタを使って、上記マクロをファイルに保存します。そのとき、拡張子はttlとします。以下のようになります。

3.2 チャレンジ・レスポンス認証でログインする方法

チャレンジ・レスポンス認証でサーバにログインするマクロを以下に示します。

connect '192.168.2.100:22 /ssh /2 /auth=challenge /user=root /passwd=xxxxx'
wait '#'

3.3 クライアント認証でログインする方法

クライアント証明書を使って、サーバに自動ログインするマクロを以下に示します。クライアントの秘密鍵は\pub_key\pub.ppkとして保存します。

connect '192.168.2.100:22 /ssh /2 /auth=publickey /user=hoge /keyfile="C:\pub_key\pub.ppk"
wait '#'

4 ファイルを作成する方法

viでファイルを作成する方法について説明します。ここでは、test.txtという名前のファイルを作成します。そして、その内容を表示してみます。
なお、sendとsendlnの違いは、sendは文字列だけ、sendlnは文字列に続けて改行文字を送信する、という点です。

connect '192.168.2.100:22 /ssh /2 /auth=password /user=root /passwd=xxxxx'
wait '#'

;viでファイルを開く。ファイルを開いたら1秒待つ。
sendln 'vi test.txt'
pause 1

;viを入力モードにする。入力モードにしたら1秒待つ。
send 'i'
pause 1

;ファイルに文字列を書き込む。文字列を書き込んだら1秒待つ。
send 'Hello'
pause 1

;viを終了する。ESC=$1B :=$3A w=$77 q=$71 RET=$0D $0A
send $1B $3A $77 $71 $0D $0A
wait '#'

;ファイルの中身を表示する。ログインプロンプト#がでるまで待つ。
sendln 'cat test.txt'
wait '#'

上記Tera Termマクロを実行すると、サーバに test.txtが作成されます。そして、Helloが表示されることがわかります。

[root@server ~]# cat test.txt
Hello

5 manページをスクロールする方法

ipコマンドのmanページをスクロールしてみます。そして、manページ中の"ip route"文字列が表示されるまで、manページをスクロールしてみます。なお、waitregexの使い方はwaitregexを参照してください。

connect '192.168.2.100:22 /ssh /2 /auth=password /user=root /passwd=xxxxx'
wait '#'

; ipコマンドのmanページを表示する。
sendln 'man ip'
pause 1

; ip routeという文字列が表示されたら、スクロールを停止します。
; Manual pageという文字列は、manページの左下に表示される文字列です。
do
    waitregex 'ip route' 'Manual page'
    if result == 2 then ; 'Manual page'が表示されたときの処理
        send ' '        ; スペースを送信する
        pause 1         ; 1秒待つ
    endif
loop while result != 1  ; Manual page'が表示されたたループ処理を抜ける。

; 5秒間待ちます。
pause 5

; manの参照を終了する。
send 'q'
wait '#'

6 ネットワーク上のホストの情報を取得する方法

下記環境で、クライアントで実行したコマンドの実行結果をサーバで参照してみます。

    PC                      server                   client
(Windows10)              (CentOS7.9)                (CentOS7.9)
    |                         | .100                     | .105
  --------------------------------------------------------------
                        192.168.2.0/24
connect '192.168.2.100:22 /ssh /2 /auth=password /user=root /passwd=xxxxx'
wait '#'

; クライアントにsshログインする。
sendln 'ssh 192.168.2.105'
wait 'password'

; クライアントのパスワードを入力する。
sendln 'xxxxx'
wait '#'

; クライアントでhostnameコマンドを実行する。
sendln 'hostname > client.txt'
wait '#'

; クライアントからサーバにファイルを送信する。
sendln 'scp client.txt root@192.168.2.100:/root'
wait 'password'

; サーバのパスワードを入力する。
sendln 'xxxxx'
wait '#'

; クライアントからログアウトする。
sendln 'exit'
wait '#'

; クライアントのホスト名を表示する。
sendln 'cat client.txt'
wait '#'

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

Tera Term Open Source Project