chmod 666 /etc/sudoersしてしまった話

## はじめに
私はUbuntuの知識が皆無で、QiitaやAIなどから情報を収集し、サーバーを構築(?)しています。
その一環で `/etc/sudoers` を編集することがあり、件のコマンドを実行しました。それが失敗だったようです。
そのような失敗はなかなか起きないでしょうが、似たような状況が発生した時のために、また反省という意味も込めて、書き起こしておきます。

:::note alert
**本記事は、`chmod 666 /etc/sudoers` の実行を推奨するものではありません。**
このコマンドを実行するとかなりめんどくさいことになるので、やめましょう。
:::

:::note warn
- （特にUnix系・Ubuntuに精通している方は）本記事を読む上で、「なんでそんなことをするんだ?」「それはやっちゃダメだろう!」となる場面が多々あると思われますが、ご容赦ください。
- 本記事の中で、明らかに違う単語などを使っている箇所があるかもしれません。
もし間違っている場合は、コメント等で教えていただけると嬉しいです。
:::

:::note info
Ubuntu Serverにおけるリカバリーモードを用いたrootユーザーのパスワードの変更方法については、次の章を参考してください。なお、すでにrootユーザーのパスワードが設定され、有効化されている場合は、本記事に記載された方法は適用外となります。
- 第3章-1
- 第3章-2
- 第4章-1
- 第4章-2
- 第4章-4
- 第5章-4

Ubuntu Serverで `/etc/sudoers` が `world writable` であるために `sudo` が使用できない状態から修復する方法については、次の章を参考してください。なお、本記事に記載された方法は、rootユーザーのパスワードが必要です。
- 第5章-2
- 第5章-3
:::

## 実行環境
### サーバー
- OS
```bash:cat /etc/os-release (一部抜粋)
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.2 LTS (Noble Numbat)"
```

- sudoバージョン
```bash:sudo -V
Sudo version 1.9.15p5
Sudoers policy plugin version 1.9.15p5
Sudoers file grammar version 50
Sudoers I/O plugin version 1.9.15p5
Sudoers audit plugin version 1.9.15p5
```

## 説明の前に
この記事では、（パスワードの部分を除き）どうしても `chmod 666 /etc/sudoers` や `/etc/sudoers` という言葉を繰り返し使用します。
そのため、このコマンドの意味やファイルの内容をまとめます。
### 1. `/etc/sudoers` ってどんなファイル?
`/etc/sudoers` ファイルは、sudoコマンドの動作を制御する重要な設定ファイルです。
...しかし、実は筆者、このファイルのことをよく知りません。（まずい）
中途半端な知識で語ってはいけない系のファイルなので、ここでは詳しくは記入しません。
そのため、他の方が解説している記事を見ていただければと思います。

### 2. `chmod 666 /etc/sudoers` ってどんなコマンド?
`chmod 666 /etc/sudoers` は、簡単に言うと「すべてのユーザーが `/etc/sudoers` を読み書きできるようにする (実行の権限は与えない)」コマンドです。
例の如く筆者は `chmod` の使い方もよく知りません。（非常にまずい）
chmodはいろんな方が解説してくださっているぽいですので、そちらをご覧ください。

## 問題の状況・解決
### 第1章: 開けなかった `sudoers` 、使えなくなった `sudo`
筆者は `/etc/sudoers` を編集したかったので、次の手順で編集しようとしました。

#### 1. 最初に、サーバーにSSHと何か（よくわかっていない）で接続しよう。
クライアントからSSHと何か（NFS? SMB? SFTP?）で接続しました。

#### 2. とりあえず、Visual Studio Codeで開いてみよう。
Visual Studio Codeで `/etc/sudoers` を~~開きました。~~ 開こうとしました。
しかし、権限エラーに遭遇します。
```text:"Unable to open 'sudoers'"
Unable to read file '/Volumes/[Server]/etc/sudoers' (NoPermissions (FileSystemError): An unknown error occurred. Please consult the log for more details.)
```

#### 3. etcはお嫌いかい? ホームディレクトリにコピーしてみよう。
ファイルをホームディレクトリに移してみて、もう一度やってみることにしました。
```console:コピー
$ sudo cp /etc/sudoers ./
```        

希望を持って開いてみましたが... やっぱりエラーでした。

#### 4. 権限いじって直接編集すりゃいいよね。← あかん
「権限いじって直接編集すればいいのでは?」と思いつき、件のコマンドを実行するのではなく、AIに下のコードを書かせてました。
```sh:Unlocker.sh（権限解除プログラム）
#!/bin/bash

# スクリプトをrootとして実行していることを確認
if [ "$(id -u)" -ne 0 ]; then
    echo "このスクリプトはroot権限で実行する必要があります。"
    echo "sudo $0 を実行してください。"
    exit 1
fi

# ファイルの指定方法
if [ -z "$1" ]; then
    read -p "権限を変更するファイルのパスを入力してください: " TARGET_FILE
else
    TARGET_FILE=$1
fi

# ファイルが存在するか確認
if [ ! -f "$TARGET_FILE" ]; then
    echo "指定されたファイルが存在しません: $TARGET_FILE"
    exit 1
fi

# 元の権限を保存
ORIGINAL_PERMS=$(stat -c "%a" "$TARGET_FILE")
echo "元の$TARGET_FILEの権限: $ORIGINAL_PERMS"

# 権限を変更 (すべての人が読み書きできるように)
echo "$TARGET_FILEの権限を666に変更します..."
chmod 666 "$TARGET_FILE"
echo "現在の$TARGET_FILEの権限: $(stat -c "%a" "$TARGET_FILE)"

# 一時停止（ここで必要な作業を行うことができます）
echo ""
echo "警告: $TARGET_FILEファイルの権限が変更されました。"
echo "セキュリティリスクがあるため、必要な作業が完了したらすぐに元に戻してください。"
echo ""
read -p "元の権限に戻す準備ができたらEnterキーを押してください..."

# 元の権限に戻す
echo "$TARGET_FILEの権限を元の状態 ($ORIGINAL_PERMS) に戻します..."
chmod $ORIGINAL_PERMS "$TARGET_FILE"
echo "現在の$TARGET_FILEの権限: $(stat -c "%a" "$TARGET_FILE")"
echo "完了しました。"
```

:::note warn
このプログラムは実行するとそのファイルの安全性が著しく下がります。
実行しないことをおすすめします。
:::

:::note alert
**システムファイル、特に権限関係のファイルには使用しないでください。**
下手するとシステムが動かなくなります。
:::

プログラムをホームディレクトリに保存すれば、あとは実行するだけです。
```console:ファイルを実行
$ sudo bash ./Unlocker.sh
```
```bash:実行結果
権限を変更するファイルのパスを入力してください: /etc/sudoers
元の/etc/sudoersの権限: 440 /etc/sudoersの権限を666に変更します...
Unlocker.sh: line 44: unexpected EOF while looking for matching `)'
```
...もちろんエラーが出ますが。

:::note info
この時点で、 `chmod 666 /etc/sudoers` は実行されていたようです。
:::

#### 5. デバッグしよう! ← 問題はそこじゃない... ことはないがそうじゃない。
「なんでだろう」と思いつつAIにデバッグさせていました。
```sh:Unlocker.sh（権限解除プログラム・改変版）
#!/bin/bash

set -x

# ~~以下同じ~~
```

「これで行けるかなー」と実行してみましたが...
```console:ファイルを実行
$ sudo bash ./Unlocker.sh
```
```bash:実行結果
sudo: /etc/sudoers is world writable
sudo: error initializing audit plugin sudoers_audit
```
さっきとはまた違うエラーが出たわけです。

ここで私は `sudo: /etc/sudoers is world writable` に注目しました。
`/etc/sudoers` が `world writable` ? 妙ですね。書き込める（ `writable` ）って書いてあるのです。
「これ、変更できてるじゃん」と。この時点で気づきました。

### 第2章: `su` を使う...? パスワードわﾞかﾞんﾞなﾞいﾞ（泣）
無事に（無事じゃない）`/etc/sudoers` が編集できたことで、私はホクホク顔で戻ってきました。
「あとは戻すだけだ」と。そう信じて疑いませんでした。

#### 1. `sudo` 、死す!
「アプリケーションは途中で落ちてしまったが、`chmod 440 /etc/sudoers` をすればいいだろう」と思っていましたが...
```console:権限変更
$ chmod 440 /etc/sudoers
```
```bash:実行結果
chmod: changing permissions of '/etc/sudoers': Operation not permitted
```
...まあ、これは当たり前です。権限がなければ実行できるわけができません。

当然、`sudo` を使用すれば実行できるはずだと思いました。
```console:権限変更
$ sudo chmod 440 /etc/sudoers
```
```bash:実行結果
sudo: /etc/sudoers is world writable
sudo: error initializing audit plugin sudoers_audit
```
...あるぇ? `sudo` くん? し、死んでる…

#### 2. `sudo` が使えないなら `su` を使えばいいじゃない。rootユーザーのパスワード? 知らない子ですねぇ。
誰かは言いました。「`sudo` が使えないなら `su` を使えばいいじゃない。」と。
しかし、私は `su` に期待していませんでした。
```console:rootに切り替え
$ su
```
```bash:実行結果
Password: 
su: Authentication failure
```
貴様ァ... まさかよォ... 今ログインしているユーザーのパスワードじゃダメなのかァ...?

### 第3章: 起動しないGRUBと裏切りのシングルユーザーモード
もはや普通の起動方法では`/etc/sudoers`の権限を設定できないと考えた私は、「シングルユーザーモード」を使用することにしました。

#### 1. 再起動・ESC/Shift... 君は誰やねん?
GRUBを起動することになったのですが...
起動時にShiftを押しても反応なし、ESCを押すとノートパソコン本来の起動メニューが起動するというふうに、GRUBが起動できません。

#### 2. 電源ブッチで起動するGRUB
色々調べると、Windows Recovery Environmentと同じように、「起動中に電源を落とす」とGRUBが起動するという情報を得ました。
やってみたところ、起動したので、 `e` キーを押して起動構成の編集画面に入りました。

#### 3. 違う見解と起動しないシングルユーザーモード
しかしここからが問題で、この起動構成の編集内容が、AIモデルやWebサイトによって全く違います。
ChatGPTは次のように言いました:
```ini:ChatGPTの考える「シングルユーザーモードの起動方法」
# linux で始まる行の末尾に以下を追加する：
init=/bin/bash
```
Grokは次のように言いました:
```text:Grokの考える「シングルユーザーの起動方法」
ro（read-only）をrw single #に変更して、root権限でシステムを起動。
```
と、全く異なるやり方を示します。とりあえず私はChatGPTに従い、起動してみました...
が、目立った変化は見られず。
他にやったら起動できるのかもしれませんが、私にはもう一つの心当たりがありました。


### 第4章: 約束のリカバリーモード
もう一つの心当たり - rootユーザーのパスワード変更について書いてあったWebページに、「リカバリーモード」についてとその入り方の記述がありました。
シングルユーザーモードに見切りをつけた私は、流れるようにリカバリーモードに~~浮気~~ ダイバートしました。

#### 1. すんなり入るGRUB
またGRUBに入ります。2回目以降は簡単に入れるようになりました。

#### 2. どこぞのシングルユーザーモードとは違う、入りやすいリカバリーモード 
矢印キーと `Enter` キーを使用して `Advanced options for Ubuntu` を選択し `Ubuntu, with Linux 6.8.0-54-generic (recovery mode)` を選択するとリカバリーモードに入れます。
なんと簡単なことでしょう。

#### 3. rootでログイン、しかし一部しか使えない?
リカバリーモードというピンク色の画面に入ると、色々選べるメニューがありました。
先ほどのパスワードの変更について書いてあったページもターミナルを使っていたことを考え、そのページの通り `root` を選び `Press Enter for maintenance` と言われたので `Enter` キーを押しました。
するとターミナルにアクセスできました。
そこで、試しに `dir` をしてみると `snap` しか表示されませんでした。
「これじゃあ修復できないじゃないか。」と思いました。問題のファイルは `/etc/` の中にある `sudoers` なのだから。
... 正攻法で行くことにします。

#### 4. いけ、 `passwd` !
現在の状況を整理しましょう。
- `sudo` が使用できない → `/etc/sudoers` が権限設定が666(rw-rw-rw-)になっているから
- `su`が使用できない → rootユーザーのパスワードを忘れたから
- `chmod 440 /etc/sudoers` が使用できない → `sudo` や `su` が使えないから

これを置き換えると
- `su` が使用できなかったのはrootユーザーのパスワードを忘れたからです
- rootユーザーのパスワードがわかれば `su` を実行できます
- `su` を実行できれば `sudo` を用いずとも直接rootユーザーとして `/etc/sudoers` の権限設定を修正することができます

ということになります。
リカバリーモードではrootユーザーのパスワードをリセットすることができます。
```console:rootユーザーのパスワードリセット
$ passwd root
```
```bash:実行結果
New password: 
Retype new password: 
passwd: password updated successfully
```
やったぜ。

### 第5章: 終わり（終わらない［終わり］）
`su` が使用可能になった今、もはや後は流れ作業となりました。

#### 1. ひとりで通常起動できるかな?
早速電源ボタンを2回ポチり、起動。

#### 2. `su` 、君に決めた!
さっそくrootユーザーでログインしようとしましたが、なぜかログインできず。
一度自身のアカウントにログインし、ユーザー変更 ( `su` ) でログインしました。

#### 3. 奥義! `chmod` !
管理者権限で実行できる状態になった私は、目的の `chmod 440 /etc/sudoers` を実行しました。
```console:権限変更
$ chmod 440 /etc/sudoers
```
何も出ません。成功です。やったぜ。
```console:sudoの動作チェック
$ sudo ls /root
```
```bash:実行結果
snap
```

#### 4. ジ。~AIの助言について~
さて、解決したように思われたこの問題ですが、AIから一つ助言がありました。
「`chmod 440 /etc/sudoers` を実行した後は、次のコマンドを実行して無効化してください。」と。
```console:rootユーザーのパスワード無効化
$ passwd -l root
```
```bash:実行結果
passwd: password changed.
```
これで、修復は完了です。

#### 5. 考察（常識）
上で、rootユーザーのパスワードは無効化されました。
しかし、なぜ、AIは無効化するよう助言したのでしょうか。

それを考えると、次のようなことが考察されます。
「rootユーザーのパスワード、元々無効化されているのではないか。」
実際、それは事実のようです。
```text:Claudeとの会話
Q. Ubuntu Serverでは、初期状態でrootユーザーのパスワードは無効化されている?
A. はい、Ubuntu Serverでは初期状態でrootユーザーのパスワードは無効化されています。...
```
（初めてこれを知った...）

## 結論
私は、`chmod 666 /etc/sudoers` を実行し、それにより発生したエラーを修復したことから、以下のことを学びました。
1. **`chmod 666 /etc/sudoers` は実行しない。** (`/etc/sudoers` の権限を変更しない)
1. `/etc/sudoers` を編集したい時は、`visudo` を使用する
1. rootユーザーのパスワードは、初期状態で無効化されている

普通は行われないようなコマンドなので、このような状況になる方は限りなくゼロに近いでしょうが、もし、似たような状況に陥ってしまった方は本記事で修復できればと思います。
最後までご覧いただき、ありがとうございました。

## おまけ: 再現方法
問題の再現方法です。もっとも、再現する人もいないでしょうが...

:::note alert
再現してみたい方は、仮想環境を使用してください。
実機では行わないでください。
:::

- 次のプログラムをホームディレクトリかどこかに保存する
```sh:Unlocker.sh（権限解除プログラム）
#!/bin/bash

# スクリプトをrootとして実行していることを確認
if [ "$(id -u)" -ne 0 ]; then
    echo "このスクリプトはroot権限で実行する必要があります。"
    echo "sudo $0 を実行してください。"
    exit 1
fi

# ファイルの指定方法
if [ -z "$1" ]; then
    read -p "権限を変更するファイルのパスを入力してください: " TARGET_FILE
else
    TARGET_FILE=$1
fi

# ファイルが存在するか確認
if [ ! -f "$TARGET_FILE" ]; then
    echo "指定されたファイルが存在しません: $TARGET_FILE"
    exit 1
fi

# 元の権限を保存
ORIGINAL_PERMS=$(stat -c "%a" "$TARGET_FILE")
echo "元の$TARGET_FILEの権限: $ORIGINAL_PERMS"

# 権限を変更 (すべての人が読み書きできるように)
echo "$TARGET_FILEの権限を666に変更します..."
chmod 666 "$TARGET_FILE"
echo "現在の$TARGET_FILEの権限: $(stat -c "%a" "$TARGET_FILE)"

# 一時停止（ここで必要な作業を行うことができます）
echo ""
echo "警告: $TARGET_FILEファイルの権限が変更されました。"
echo "セキュリティリスクがあるため、必要な作業が完了したらすぐに元に戻してください。"
echo ""
read -p "元の権限に戻す準備ができたらEnterキーを押してください..."

# 元の権限に戻す
echo "$TARGET_FILEの権限を元の状態 ($ORIGINAL_PERMS) に戻します..."
chmod $ORIGINAL_PERMS "$TARGET_FILE"
echo "現在の$TARGET_FILEの権限: $(stat -c "%a" "$TARGET_FILE")"
echo "完了しました。"
```

:::note warn
このプログラムは実行するとそのファイルの安全性が著しく下がります。
実行しないことをおすすめします。
:::

:::note alert
**システムファイル、特に権限関係のファイルには使用しないでください。**
下手するとシステムが動かなくなります。
:::

- 実行する
```bash:実行するコマンド
sudo bash ./Unlocker.sh
```
```bash:成功例
権限を変更するファイルのパスを入力してください: /etc/sudoers
元の/etc/sudoersの権限: 440 /etc/sudoersの権限を666に変更します...
Unlocker.sh: line 44: unexpected EOF while looking for matching `)'
```

- もう一度実行する
```bash:実行するコマンド
sudo bash ./Unlocker.sh
```
```bash:成功例
sudo: /etc/sudoers is world writable
sudo: error initializing audit plugin sudoers_audit
```
やったね!