nanoで保存できない Permission denied の原因と対処
nanoで保存時に「Permission denied」と表示される原因を4つに整理します。所有者、親ディレクトリ、読み取り専用や容量不足、コンテナ内のuidを順に確認する方法を解説します。
nano がファイルを保存できない理由
nano がファイルを保存できない理由は4つあります。ファイルの所有者でない、親ディレクトリで nano が実行しようとしている操作が許可されていない、ファイルシステムが読み取り専用または容量不足である、または別のユーザー ID で実行されているコンテナ内にいる、のいずれかです。最初の2つは権限の問題で、後の2つは権限の問題ではありません。この順番で確認してください。最初の理由がほとんどのケースに該当し、1つのコマンドで確認でき、解決方法は sudo nano ではなく sudoedit だからです。
エディターを開いたままなら、内容は失われません。テキストはメモリ上にあるため、ファイルを開いたまま、自分が所有するパスにバッファを書き出し、後で所定の場所に移せます。この回避方法については、このガイドの後半で説明します。
権限を変更する前に、次の確認を実行します
各コマンドは、編集対象の実際のパスに対して実行してください。これらはそれぞれ異なる点を確認するため、何かに手を加える前にすべて実行します。どの確認が失敗しているかを把握せずに権限を変更すると、通常は最初の問題に別の問題を重ねることになります。
id
ls -l /etc/nginx/nginx.conf
ls -ld /etc/nginx
namei -l /etc/nginx/nginx.conf
findmnt -no SOURCE,FSTYPE,OPTIONS -T /etc/nginx/nginx.conf
df -h /etc/nginx
df -i /etc/nginxidは、現在のユーザー ID と、そのユーザーが保持しているグループ ID を表示します。ls -lは、ファイル自体の所有者、グループ、権限ビットを表示します。ls -ldは、そのファイルを格納しているディレクトリについて同じ情報を表示します。これは別の確認であり、結果も別に得られます。namei -lはパスの各要素を順に確認し、それぞれの所有者と権限を一覧表示します。そのため、2つの確認結果を1つの出力で得られます。findmntは、そのパスが属するファイルシステムと、マウント時に指定されたオプションを表示します。df -hは空き容量を、df -iは空き inode 数を表示します。inode は容量とは別に枯渇します。これらの権限文字列にまだ慣れていない場合は、まず ls -l が表示する権限文字列の読み方を確認してください。
原因 1: ファイルの所有者が root で、自分は root ではない
読み取りと書き込みは別の権限です。/etc 配下の多くのファイルは、誰でも読み取れる設定になっています。そのため nano はファイルを開いて内容を表示し、自由に入力できます。これらの操作はディスクに書き込みません。拒否されるのは保存時です。この時点でカーネルが、ファイルの所有者、グループ、その他に対する権限ビットと、自分のユーザー ID およびグループ ID を比較します。nano はカーネルから返された結果を表示しているだけです。そのため、nano のオプションを変更しても結果は変わりません。
id と ls -l を確認すれば原因が分かります。ファイルの所有者は root で、自分は root ではなく、その他に対する権限ビットにも書き込み権限がありません。Ctrl-O をもう一度押しても解決しません。
sudoedit が root 所有ファイルの編集に適している理由
SUDO_EDITOR=nano sudoedit /etc/nginx/nginx.confsudo はファイルの一時コピーを作成し、その所有者をユーザー自身に設定してから、そのコピーをユーザー自身として nano で開きます。エディターの終了後、sudo は root 権限で編集結果を元の場所に戻します。エディターが root として実行されることはありません。sudo -e は、名前が異なるだけで同じコマンドです。エディターは SUDO_EDITOR、次に VISUAL、次に EDITOR の順に選択されるため、シェルプロファイルで export EDITOR=nano を設定すれば、どこでもこれがデフォルトになります。sudoers で env_editor フラグが無効になっている場合、これらの変数は無視され、代わりに sudoers の editor 設定からエディターが選択されます。
sudo nano でもファイルは保存できますが、そこが問題です。セッションが続く間、ファイルシステム全体に対する完全な対話型エディターの root 権限が与えられます。そのため、保存プロンプトでパスを入力し間違えると、別のシステムファイルにテキストを root として上書きしてしまいます。必要な手順に限って sudo を呼び出す一般ユーザーとして作業する習慣を身につけることが重要です。設定ファイルを編集するとき、その習慣を実践する方法が sudoedit です。
sudoedit のルールには、意外に思われるものが2つあります。シンボリックリンクの編集は拒否されます。また、root でない限り、自分が書き込み可能なディレクトリ内にあるファイルの編集も拒否されます。2つ目のルールがあるのは、そのディレクトリに書き込めるユーザーが、エディターの実行中にファイルを別のものへ置き換えられるためです。どちらも sudoers のデフォルト動作です(sudoedit_follow は無効、sudoedit_checkdir は有効)。まだ存在しないファイルは作成されます。
原因 2: 親ディレクトリが実際に制御するもの
他のエディタ向けの説明では、保存にはディレクトリへの書き込み権限が必要だとされています。多くのエディタは、新しいファイルを書き込み、古いファイルの上に名前変更して置き換えるためです。nano はこの方式では動作しません。指定されたファイルを開き、そのファイルに直接書き込みます。そのため、すでに存在するファイルでは、ディレクトリの書き込みビットは確認されません。
ただし、ディレクトリは他の動作を決定します。そのため、ls -ld は確認項目に含まれています。
- まだ存在しないファイルを作成するには、ディレクトリへの書き込み権限と実行権限が必要です。新しい名前をディレクトリに追加する必要があるためです。umask は新しいファイルの初期権限を決定します。
- ファイルに到達するには、パス上のすべてのディレクトリに実行権限が必要です。これは検索権限とも呼ばれます。1 つでも権限がないディレクトリがあると、その下にあるものへはすべてアクセスできません。
namei -lを使うと、該当するディレクトリを確認できます。 - バックアップを有効にして保存する場合や、ファイルロックを有効にして保存する場合は、元のファイルと同じ場所に 2 つ目のファイルを書き込みます。そのため、これらの機能には書き込み可能なディレクトリが必要です。バックアップは nanorc の
-Bオプションまたはset backupで、ロックは-Gまたはset lockingです。これらは、ユーザーまたはディストリビューションが有効にしない限り無効です。
ディレクトリ権限は、システム上の他の場所でも同じように重要です。SSH サーバーは、home directory または .ssh directory が他のユーザーによって書き込み可能になっている場合、key を拒否します。これは、ログイン時に SSH が key を拒否する一般的な原因の 1 つです。
nano は既存のファイルに書き込むため、そのファイルは inode を維持します。inode は、名前の背後にあるディスク上の識別情報です。ファイルを開いて保持しているプロセスは、そのファイルを追跡し続けます。また、ファイル単体をコンテナに bind mount している場合も、その mount は機能し続けます。ファイルを置き換えて保存するエディタでは、この mount が壊れます。mount は名前ではなく inode を追跡するためです。
原因 3: ファイルシステムが読み取り専用になっている、または空き容量がない
findmnt の出力に ro が含まれている場合、その書き込みは最初から成功しません。ファイルシステムがその状態でマウントされていた可能性があります。/etc/fstab によるマウント、または読み取り専用の bind mount が原因です。あるいは、ディスクエラーの後にカーネルが読み取り専用で再マウントした可能性もあります。後者は深刻です。sudo dmesg -T | tail -50 で、再マウントの原因になった入出力エラーとファイルシステムエラーを確認できます。修復には、アンマウントした状態でファイルシステムチェックを実行します。VPS では、プロバイダーの rescue console から起動する必要があります。
ファイルシステムの空き容量がない場合も、同じ書き込みが別の理由で失敗します。通常のケースは df -h で確認できます。見落としやすいケースは df -i で確認できます。inode はファイルシステムの作成時に確保された固定プールから割り当てられます。そのため、小さなファイルが大量にあるディレクトリツリーによって inode を使い切ることがあります。この場合、df -h にはまだ数 GB の空き容量が表示されます。空き容量がなく、原因になりそうなものも見つからない場合は、満杯のディスクで df と du の値が一致しない場合で、削除済みだが開いたままのファイルを確認します。
ここでは、紛らわしい症状を説明する重要な点があります。ext4 は、ファイルシステムの作成時にブロックの一部を root 用に予約します。そのため、通常のユーザーが拒否された後も root は書き込みを続けられます。このとき sudo が解決策に見えますが、ディスクは残りの容量まで埋まり、問題はさらに悪い状態で再発します。
nano は新しい内容を書き込む前にファイルを切り詰めます。そのため、書き込みの途中で空き容量がなくなると、ファイルが以前より短くなることがあります。ほぼ満杯のファイルシステム上で編集する場合は、重要な設定ファイルを編集前にコピーしてください。sudo cp -a /etc/nginx/nginx.conf /root/nginx.conf.bak により、コピー先の所有者、グループ、権限を維持できます。
原因 4: コンテナ内で bind mount を編集している
ファイルの所有者は数値で管理されます。カーネルが保持するのはユーザー ID であり、表示される名前は、検索を実行する /etc/passwd によって決まります。そのため、同じファイルでも、ホストでは名前が表示され、コンテナ内では別の名前や数値だけが表示されることがあります。名前ではなく数値を比較します。コンテナ内で id -u を実行し、ファイルに対して ls -ln を実行します。
bind mount されたファイルは、ホスト上で設定された所有権を保持します。ホスト上ではファイルが自分のユーザーに属していても、コンテナのプロセスが別のユーザーとして実行されている場合、コンテナ内での書き込みは拒否されます。また、コンテナ内で sudo を実行しても、ホスト上の所有者は変わりません。ホスト側で所有者をコンテナの実行 ID に変更するか、すでにファイルを所有している ID でコンテナを実行します。linuxserver.io などのプロジェクトのイメージでは、プロセスが実行されるユーザーを指定する PUID と PGID の変数 が提供されています。
コンテナに関して、さらに2つのケースを知っておく必要があります。:ro で読み取り専用にした mount や、--read-only を指定して起動したコンテナでは、所有権に関係なく書き込みが拒否されます。コンテナ内で cat /proc/mounts を実行すると、そのフラグを確認できます。rootless Podman では、ユーザー名前空間によってコンテナのユーザー ID がホスト上の ID 範囲にマッピングされます。そのため、コンテナ内で root に属しているように見えるファイルが、コンテナ外では権限のない自分のアカウントに属していることがあります。
編集が成功した後に変更が消えるケースもあります。mount されていないパスにあるファイルをコンテナ内で変更すると、そのファイルはコンテナの書き込み可能レイヤーに保存されます。このレイヤーはコンテナを再作成すると破棄されます。変更を永続化する場合は、mount のホスト側にあるファイルを変更するか、イメージのビルド時に変更を加えます。
回避策: 自分が所有するパスに保存する
エディター内から権限を取得しようとしないでください。Ctrl-O を押し、プロンプトでパスを消去して、/home/you/nginx.conf.new などのホームディレクトリ配下のパスを入力し、Enter を押します。その後、Ctrl-X で終了します。これで作業内容はディスク上に保存され、自分が所有する状態になります。あとは通常のファイルコピーです。
sudo cp /home/you/nginx.conf.new /etc/nginx/nginx.conf
sudo nginx -tここでは mv ではなく cp を使用します。cp は既存のファイルを通して書き込むため、そのファイルの所有者、グループ、権限が維持されます。同じファイルシステム上で mv を使うと、既存のファイルが自分のファイルに置き換わります。その結果、/etc の設定ファイルが自分のユーザーアカウントの所有になり、次に解決すべき権限問題になります。
何かを再読み込みする前に、そのファイルを管理するツールで結果を確認してください。sudo nginx -t は nginx の設定を解析し、sudo sshd -t は SSH サーバーの設定を解析します。2 つのファイルには、この手順全体を代行する専用エディターがあります。/etc/sudoers には sudo visudo を、独自の cron ジョブには crontab -e を使用します。いずれも一時コピーを編集し、構文を確認したうえで、解析に成功した場合だけファイルをインストールします。
FAQ
sudo nano と sudoedit のどちらを使ってシステムファイルを編集すべきですか?
sudoedit を使用してください。これはファイルを一時コピーとしてあなたが所有する状態にし、自分のユーザーでエディターを実行します。エディターの終了時に結果を root として書き戻すため、エディター自体が root 権限を保持することはありません。SUDO_EDITOR、VISUAL、または EDITOR を nano に設定すると nano を選択できます。sudo nano も動作しますが、セッション中は対話型エディターにシステム上のすべてのパスへの root アクセスを与えます。そのため、保存プロンプトでファイル名を 1 文字誤入力するだけで、システムファイルを破損させる可能性があります。
nano でファイルを保存するには、ディレクトリへの書き込み権限が必要ですか?
すでに存在するファイルでは必要ありません。nano はファイル自体に書き込むため、カーネルはファイルの書き込みビットと、パス上にある各ディレクトリの実行ビットを確認します。ファイルがまだ存在しない場合は、新しい名前を作成する必要があるため、ディレクトリの書き込みビットが重要です。バックアップまたはファイルロックを有効にしている場合も同様です。どちらも元のファイルの隣に 2 つ目のファイルを書き込みます。
所有者は正しく、ディスクも満杯ではありません。それでも書き込みを妨げる原因は何ですか?
4 つあります。ファイルシステムが read only でマウントされている可能性があります。findmnt -no OPTIONS -T /etc/nginx/nginx.conf で確認できます。ファイルに immutable 属性が付いている可能性があります。lsattr で確認し、sudo chattr -i で削除できます。この属性が設定されている間は、root でもファイルに書き込めません。空き容量が残っていても inode プールが枯渇している可能性があります。df -i で確認できます。SELinux または AppArmor が、権限ビットで許可されていても書き込みを拒否する場合があります。この拒否は、試行したパスに対する監査ログに記録されます。
ファイルをまったく保存できない場合、変更内容はどこに保存すればよいですか?
Ctrl-O を押し、自分が所有するパスを指定してください。ホームディレクトリの下でも、ユーザーが書き込める別の場所でも構いません。バッファーはメモリ上に残っているため、入力した内容は失われません。その後、sudo cp を使用して保存したファイルを所定の場所へコピーします。これにより、元のファイルの所有者と権限が維持されます。続いて、サービス固有のテストコマンドで確認してからサービスを reload してください。
Docker コンテナ内で編集した内容が消えるのはなぜですか?
そのパスが mount でない場合、編集内容はそのコンテナの書き込み可能レイヤーに保存されます。コンテナを置き換えると、このレイヤーは破棄されます。bind mount または volume のホスト側でファイルを編集するか、イメージに組み込んでください。パスが bind mount で、保存が拒否される場合は、コンテナ内の id -u と ls -ln で確認した数値の所有者を比較してください。ファイルの所有権はホスト側のものが維持されるため、コンテナのプロセスも同じ所有者に一致している必要があります。