Linuxのファイル権限:chmod、chown、umask
1. 概要
Permission deniedエラーは、アプリケーションをLinuxにデプロイする際に最も頻繁に発生するエラーの一つです。アプリケーションがログを書き込めない、設定ファイルを読み取れない、または秘密ファイルがすべてのユーザーに読み取られるといった問題が起こります。
この記事では、Linuxのファイル権限モデルと3つの主要なツールの使い方を説明します。chmodで権限を変更し、chownでオーナーを変更し、umaskで新しいファイルのデフォルト権限を制御します。この記事を読み終わると、任意のファイルの権限を読み取り、必要な権限を適切に設定し、JavaからPOSIX権限を操作できるようになります。
2. ls -lでファイル権限を読む
サンプルディレクトリで始めましょう。
mkdir -p shop && cd shop
echo "id,total" > order.csv
mkdir reports
ls -l
-rw-r--r-- 1 an dev 9 Oct 13 09:00 order.csv
drwxr-xr-x 2 an dev 4096 Oct 13 09:00 reports
最初の列は10文字です。
- 1文字目はタイプを示します。
-は通常のファイル、dはディレクトリ、lはシンボリックリンクです。 - 残りの9文字は3つのグループに分かれ、各グループは3文字です。user(所有者)、group(グループ)、other(その他のユーザー)です。
- 各グループは
r(読み取り)、w(書き込み)、x(実行)で構成されます。-はその権限がないことを意味します。
3番目と4番目の列はowner(an)とgroup(dev)です。したがって、order.csvでは、anは読み取りと書き込みができ、グループdevのメンバーは読み取りのみ、その他のユーザーも読み取りのみです。
数値形式で権限を見るには、statを使います。
stat -c '%a %A %U:%G %n' order.csv reports
644 -rw-r--r-- an:dev order.csv
755 drwxr-xr-x an:dev reports
カーネルは順序に従って権限をチェックします。あなたがオーナーの場合、user権限グループのみが適用されます。オーナーではないがグループに属している場合、group権限グループのみが適用されます。それ以外の場合はother権限が使用されます。グループは加算されません。したがって、---r--r--の権限を持つファイルは、オーナー自身が読み取ることができず、他のすべてのユーザーは読み取ることができます。
3. chmodで権限を変更する
3.1. シンボル形式
シンボル形式の構文は[ugoa][+-=][rwxX]です。ここでuはuser、gはgroup、oはother、aはallです。
chmod g+w order.csv # グループに書き込み権限を追加
chmod o-r order.csv # otherの読み取り権限を削除
chmod u=rw,go= order.csv # user: rw、group と other: 権限なし
stat -c '%a %A' order.csv
600 -rw-------
演算子+と-は指定された権限のみを追加または削除し、その他の権限は変わりません。演算子=はそのグループ全体を再割り当てします。シンボル形式は、現在の権限を知ることなく、部分的に変更したい場合に安全です。
3.2. 8進数形式
各権限には値があります。r = 4、w = 2、x = 1です。これらの値を加算すると、各グループに対して1つの数字が得られます。
| 数値 | 権限 | 使用対象 |
|---|---|---|
| 7 | rwx |
ディレクトリまたはスクリプトのオーナー |
| 6 | rw- |
データファイルのオーナー |
| 5 | r-x |
ディレクトリの読み取りとアクセス、スクリプト実行 |
| 4 | r-- |
読み取りのみ |
| 0 | --- |
権限なし |
例として、スクリプトとパスワードを含むファイルを作成します。
printf '#!/bin/bash\necho "Backup orders..."\n' > backup.sh
echo "db.password=s3cret" > secret.properties
chmod 755 backup.sh
chmod 600 secret.properties
./backup.sh
stat -c '%a %n' backup.sh secret.properties
Backup orders...
755 backup.sh
600 secret.properties
755はすべてのユーザーがスクリプトを実行できるようにしますが、オーナーのみが変更できます。600により、秘密ファイルはオーナーのみが読み書きできます。数値形式はすべての権限を一度に割り当てるため、正確に望ましい状態がわかっている場合に適しています。
3.3. 再帰的な権限変更と大文字のX
典型的なエラーは以下のコマンドを実行することです。
chmod -R 755 shop/
このコマンドはすべてのファイルに実行権限を割り当てます。これはorder.csvも含みます。正しい方法は大文字のXを使うことです。このシンボルはディレクトリにのみxを追加するか、すでに何らかのグループでxを持つファイルにのみ追加します。
chmod -R u=rwX,go=rX shop/
結果として、ディレクトリは755の権限を持ち、通常のファイルは644の権限を持ち、backup.shは755の権限を保持します。より厳密に制御する必要がある場合は、findを使って分離できます。
find shop -type d -exec chmod 750 {} +
find shop -type f -exec chmod 640 {} +
4. ディレクトリのファイルとは異なる権限
ディレクトリの場合、rwxは異なる意味を持ちます。
r: 内部のアイテムの名前を一覧表示(ls)。w: 内部のアイテムを作成、削除、名前変更。この権限は機能するためにxと組み合わせる必要があります。x: ディレクトリに入る(cd)し、名前がわかっていれば内部のアイテムにアクセスします。
予期しない結果の1つ:ファイルの削除はディレクトリの権限に依存し、ファイル自体の権限には依存しません。
mkdir inbox && echo "data" > inbox/invoice.txt
chmod 444 inbox/invoice.txt # ファイルは読み取り専用
rm -f inbox/invoice.txt # inboxにw権限があるため削除可能
ls inbox # 空
逆に、ディレクトリでx権限を削除すると、ファイルが644の権限を持っていても、内部のファイルを読み取ることができません。
echo "data" > inbox/invoice.txt
chmod 644 inbox/invoice.txt
chmod u-x inbox
cat inbox/invoice.txt
cat: inbox/invoice.txt: Permission denied
アプリケーションがPermission deniedを報告し、ファイルの権限が正しいように見える場合は、パス上のすべての親ディレクトリをチェックしてください。コマンドnamei -l /path/to/fileは各レベルの権限を一覧表示します。続行する前に、chmod u+x inboxでディレクトリの権限を復元します。
5. chownとchgrpでオーナーを変更する
chownはowner、group、またはその両方を変更します。
sudo chown appuser order.csv # ownerのみ変更
sudo chown appuser:dev order.csv # ownerとgroupを変更
sudo chown :dev order.csv # groupのみ変更
sudo chown -R appuser:appuser /opt/shop
いくつかの注意点:
rootだけがownerを変更できます。通常のユーザーは自分のファイルを他の人に「譲る」ことはできません。- 通常のユーザーは自分が所有するファイルのグループを変更できますが、自分がメンバーであるグループにのみ変更できます。コマンドは
chgrp dev order.csvです。 chown -Rはディレクトリツリーに従います。シンボリックリンクに遭遇した場合、デフォルトではリンクが指しているファイルのオーナーを変更します。リンク自体のオーナーを変更したい場合は、-hを追加してください。
デプロイ時によく使用されるパターンは、サービスを専用ユーザーで実行し、運用チームで共通グループを使用することです。
sudo chown -R appuser:ops /var/log/shop
sudo chmod -R u=rwX,g=rX,o= /var/log/shop
このセットアップで、サービスはログを書き込むことができ、opsグループはログを読むことができ、その他のユーザーはアクセスできません。
6. umaskでデフォルト権限を制御する
新しいファイルを作成するとき、プログラムは通常、ファイルに対して666権限、ディレクトリに対して777権限をリクエストします。その後、カーネルはumask内のビットを無効にします。結果は次のようになります。
実効権限 = リクエストされた権限 AND (NOT umask)
umask # 現在の値を表示
umask 027 # 現在のシェルで変更
touch customer.csv && mkdir archive
stat -c '%a %n' customer.csv archive
0022
640 customer.csv
750 archive
umask 027では、ファイルは666から027ビットを差し引いて640を受け取り、ディレクトリは777から027を差し引いて750を受け取ります。otherはすべての権限を失い、groupは書き込み権限を失います。
注意:
umaskはビット演算であり、算術減算ではありません。umask 033では、新しいファイルの権限は633ではなく644です。これは、ビットxが元々666に含まれていないため、無効にするものがないからです。
一般的な値をいくつか挙げます。
| umask | ファイル | ディレクトリ | 使用時期 |
|---|---|---|---|
022 |
644 | 755 | ほとんどのディストロのデフォルト |
027 |
640 | 750 | サーバー、グループ内でのみ共有 |
077 |
600 | 700 | 機密データ |
umaskコマンドは現在のプロセスとその子プロセスにのみ有効です。systemdサービスに対して永続的に設定するには、ユニットファイルで宣言します。
[Service]
User=appuser
UMask=0027
7. 3つの特別なビット:setuid、setgid、sticky
基本的な9ビットに加えて、Linuxには3つの特別なビットがあります。これらは8進数形式の先頭の4番目の数字として記述されます。
- setuid (4): プログラムはownerの権限で実行されます。例えば、
/usr/bin/passwdは-rwsr-xr-xと表示されます。 - setgid (2): ディレクトリに設定されている場合、その内部に作成された新しいファイルはディレクトリのグループを継承します。このビットは共有作業ディレクトリに非常に有用です。
- sticky (1): ディレクトリに設定されている場合、ファイルはファイルのownerのみが削除できます。
/tmpはこのビットを使用し、drwxrwxrwtと表示されます。
グループdev用の共有ディレクトリを作成する例:
sudo mkdir /srv/shared && sudo chown :dev /srv/shared
sudo chmod 2770 /srv/shared
stat -c '%a %A' /srv/shared
2770 drwxrws---
注意: リスクを本当に理解していない限り、自分で書いたスクリプトやファイルにsetuidを割り当てないでください。バグのあるsetuid rootプログラムは、古典的な権限昇格の道です。
8. JavaからPOSIX権限を操作する
Java NIOはPosixFilePermissionsを使用してPOSIX権限をサポートしています。次のクラスは秘密ファイルを書き込み、ownerのみが読み取ることができるようにします。
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.PosixFilePermissions;
public class SecretWriter {
public Path writeSecret(Path dir, String content) throws IOException {
Path file = dir.resolve("secret.properties");
Files.writeString(file, content);
Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("rw-------"));
return file;
}
}
fromString("rw-------")はシンボル文字列をSet<PosixFilePermission>に変換します。その後、setPosixFilePermissionsはchmod 600コマンドのように動作します。
Files.createFileにPosixFilePermissions.asFileAttribute(...)を渡そうとするかもしれません。この方法はumaskが依然として適用され、open()システムコール呼び出しと同じです。機密ファイルの場合は、作成後にsetPosixFilePermissionsを呼び出して結果を確認してください。さらに安全な方法は、最初から厳密な権限を持つファイルを作成して、他のユーザーがファイルを読み取るタイムウィンドウがないようにすることです。
JUnit 5とAssertJでのテスト:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.*;
import java.nio.file.attribute.PosixFilePermissions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
class SecretWriterTest {
@TempDir
Path tempDir;
@Test
void givenSecret_whenWritten_thenOnlyOwnerCanReadAndWrite() throws Exception {
assumeTrue(FileSystems.getDefault().supportedFileAttributeViews().contains("posix"));
Path file = new SecretWriter().writeSecret(tempDir, "db.password=s3cret");
String perms = PosixFilePermissions.toString(Files.getPosixFilePermissions(file));
assertThat(perms).isEqualTo("rw-------");
assertThat(Files.readString(file)).contains("db.password");
}
}
assumeTrueはWindowsでテストをスキップします。WindowsにはPOSIXのattribute viewがありません。この行がない場合、テストはUnsupportedOperationExceptionをスローします。
9. よくあるエラー
chmod 777を「クイックフィックス」として使用。 これは機械上のすべてのユーザーに書き込み権限を付与します。プロセスがどのユーザーで実行されているかを確認し(ps -o user= -p <pid>)、そのユーザーまたはグループのみに権限を付与します。- ディレクトリツリーで
chmod -R 755を実行。 このコマンドはすべてのファイルを実行可能ファイルにしてしまいます。セクション3.3に示すように、大文字のXまたはfindを使用してください。 - ファイルの権限のみをチェック。 親ディレクトリも
namei -lで確認する必要があります。 sudoを実行した後、ownerの変更を忘れる。sudoで作成されたファイルはrootに属しており、通常のユーザーで実行されるアプリケーションは書き込めません。すぐにchownを実行してください。- Dockerでボリュームをマウント。 コンテナ内のUIDはホスト上のUIDと異なる可能性があります。カーネルはUID番号で権限をチェックし、ユーザー名ではなく。
- SSHキーが拒否される。 SSH権限が広すぎる場合、
~/.ssh/authorized_keysまたは秘密鍵をスキップします。ディレクトリ~/.sshは権限700が必要で、秘密鍵は権限600が必要です。
10. まとめ
Linuxの権限モデルは3つのグループ(user、group、other)と3つの権限(r、w、x)で構成されています。これらの権限はファイルとディレクトリの間で異なる意味を持ちます。
chmodを使用して権限を変更します。部分的に変更する場合はシンボル形式を使用し、完全に割り当てる場合は数値形式を使用します。chownとchgrpを使用してファイルを適切なユーザーとグループに割り当てます。umaskを使用して、新しいファイルが最初から安全な権限で作成されるようにします。
全体的な原則は、最小限の権限を付与することです。サービスを専用ユーザーで実行し、グループを通じて共有し、機密情報に対してotherを0に設定します。