Article

PVE の unprivileged LXC コンテナに bind mount した volume を Samba サーバで共有する

Proxmox VE 上の LXC コンテナで Samba 共有を構築し、unprivileged LXC の UID/GID マッピング、bind mount したホストの volume 共有設定を検証した記録です。

はじめに

この記事では、Proxmox VE(以下 PVE)上に作成した LXC コンテナで Samba サーバを構築した検証内容をまとめます。

当初は「自宅内のネットワークだし、ゆるい設定で共有ファイルが見られればいいか」程度の気持ちで始めましたが、PVE や LXC ならではの問題に遭遇しました。

  • Samba 共有ディレクトリ自体の参照は可能だが、その配下のファイルやディレクトリが表示されない
  • LXC 内のユーザが全て nobody 扱いとなり、PVE ホスト上の volume に Samba 経由で書き込みできない

最終的には、unprivileged LXC における UID / GID マッピングを正しく設定したことで、期待通り動作しました。

構成は以下の通りです。

  • public 共有はゲスト読み取り専用
  • private 共有は特定ユーザのみアクセス可能
  • private 共有では Samba 経由の書き込みも可能
  • PVE の LXC mount point で共有対象ディレクトリを Samba 経由で共有
  • unprivileged LXC の UID/GID マッピング範囲を把握し、PVE ホスト側の volume 内で正しい UID/GID を指定し権限を調整

検証環境

項目内容
仮想化基盤Proxmox VE
コンテナDebian 系 LXC コンテナ
コンテナ名`debian-samba`
共有元ストレージPVE ホストにマウントした外部ディスク
PVE ホスト側パス例`/mnt/local/disk1`
LXC 内パス例`/shared/public`, `/shared/private`
Samba ユーザー`takashi`
LXC 内 `takashi` UID/GID`1000:1000`
PVE ホスト側対応 UID/GID`101000:101000`
接続元クライアント例`192.168.1.7`

最終的な設定

以下のように、読み取り専用の公開共有と、認証付きの書き込み可能な private 共有を分ける構成にしました。

ini
[public]
    path = /shared/public
    browseable = yes
    guest ok = yes
    read only = yes
    force user = nobody

[private]
    path = /shared/private
    browseable = no
    guest ok = no
    read only = no
    force user = takashi
    force group = takashi
    create mask = 0660
    directory mask = 0770
    force create mode = 0660
    force directory mode = 0770

private 共有は書き込み可能にするのと、ファイルやディレクトリの所有者を統一するため、PVE ホストの外部ディスク上の権限を LXC 内の takashi に対応する UID/GID に合わせました。

text
LXC 内 takashi: 1000:1000
PVE ホスト側対応 ID: 101000:101000

手順

1. 最初の Samba 共有設定

まずは以下のような読み取り専用の public 共有を設定します。

ini
[public]
    path = /shared/public
    browseable = yes
    guest ok = yes
    read only = yes
    force user = nobody
    inherit permissions = yes

この設定で、とりあえず外部 PC から /shared/public 自体は参照できましたが、その下のファイルやディレクトリが参照できませんでした。

2. nobody ユーザで見えるか確認

force user = nobody を設定したため、Samba は共有内のファイルを nobody として読みに行きます。

まず LXC 内で nobody ユーザーとして /shared/public を読めるか確認すると…

bash
runuser -u nobody -- ls -la /shared/public

実際の出力は以下です。

text
root@debian-samba:/shared/public# runuser -u nobody -- ls -la /shared/public
total 8
drwxrwxrwx 2 root root 4096 Apr  4 14:54 .
drwxr-xr-x 4 root root 4096 Apr  4 14:52 ..
lrwxrwxrwx 1 root root   24 Apr  4 14:52 pictures -> /mnt/bind/disk0/pictures
lrwxrwxrwx 1 root root   22 Apr  4 14:53 videos -> /mnt/bind/disk0/videos
root@debian-samba:/shared/public#

この時点では、/shared/public の中身は実体ディレクトリではなく、すべて外部ディスクへのsymbolic linkにしていましたが、wide links 設定をしていませんでした。

3.  /etc/pve/lxc/<CTID>.conf に mount point を直接記述

Samba では、共有ディレクトリ外を指す symbolic link を辿るには wide links を設定すべきですが、共有ルート外のパスを辿れるようにすると、意図しないパスを公開してしまうリスクがあるため、避けることにしました。

そこで、public に symbolic link を束ねる構成をやめて、PVE の LXC mount point でコンテナ内に通常のディレクトリとして見せることにしました。

PVE では、pct set コマンドを使って LXC の mount point を設定できます。

bash
pct set <CTID> -mp1 /mnt/host/disk0/pictures,mp=/shared/public/pictures
pct set <CTID> -mp2 /mnt/host/disk0/videos,mp=/shared/public/videos

今回は pct set の代わりに、/etc/pve/lxc/<CTID>.conf を直接編集して mount point を設定しました。

直接編集した場合、設定ファイル自体は保存時点で PVE に反映されます。

LXC が起動中の場合、mount point の追加・変更を有効にするには基本的にコンテナの再起動が必要でした。

bash
pct stop <CTID>
pct start <CTID>

これで、Samba 共有が期待通りに動作するようになりました。

4. 書き込み権限設定

外部 PC から Samba 共有ドライブを参照できるようになったあと、次に書き込みができない問題に遭遇しました。

PVE ホスト上で外部ディスクを確認すると、以下のように見えていました。

text
root@pve:~# ls -al /mnt/local/disk1/
total 56
drwxrwxr-x 11 _chrony _ssh  4096 Mar 30 21:40 .
drwxr-xr-x  6 root    root  4096 Mar 31 16:37 ..
drwxrwxr-x 14 _chrony _ssh  4096 Mar 30 21:35 pictures
drwxrwxr-x  5 _chrony _ssh  4096 Feb 11  2025 videos

_chrony ユーザーや _ssh グループが表示されていますが、これは以前この外部ディスクを接続していたマシン上の UID/GID が、PVE ホスト上では別のユーザー名・グループ名として解釈されていただけです。

一方、LXC 内では以下のように見えていました。

text
takashi@debian-samba:/shared/private/disk1$ ls -al
total 56
drwxrwxr-x 11 nobody nogroup  4096 Mar 30 12:40 .
drwxr-xr-x  4 root   root     4096 May  6 09:04 ..
drwxrwxr-x 14 nobody nogroup  4096 Mar 30 12:35 pictures
drwxrwxr-x  5 nobody nogroup  4096 Feb 11  2025 videos

この nobody:nogroup 表示は、「本当に nobody が所有している」というより、LXC の UID/GID マッピング上、コンテナ内から正しく扱えない UID/GID がそう見えているだけです。

privileged LXC と unprivileged LXC の確認

unprivileged LXC かどうかは、PVE ホスト上で以下のように確認できます。

bash
pct config <CTID>

以下の出力があれば unprivileged LXC です。

ini
unprivileged: 1

LXC 内で以下コマンドを実行すると、UID/GID マッピングを確認できます。

bash
cat /proc/self/uid_map
cat /proc/self/gid_map

unprivileged LXC では、以下のようなマッピングが典型例のようです。

text
         0     100000      65536

これは、LXC 内の UID 0 から 65536 個分が、PVE ホスト側の UID 100000 以降に対応することを意味します。

つまり、LXC 内の takashi が UID 1000 の場合、PVE ホスト側では UID 101000 として扱われます。

この UID/GID マッピングは PVE 独自の仕組みではなく、Linux カーネルの user namespace を LXC が利用しているものです。PVE はその設定を管理しやすく提供しているに過ぎません。

PVE ホスト側で UID/GID を指定して権限を変更

これまでの調査で、以下いずれかを実施すれば書き込み可能になると考えました。

  1. PVE ホスト側に UID 101000 のユーザーを作る
  2. PVE ホスト側で bind mount 対象 volume 内のファイルに UID/GID を指定して権限を変更する

今回採用したのは 2. で、以下のコマンドを実行しました。

bash
chown -R 101000:101000 /mnt/local/disk1

これにより、外部 PC から Samba 経由で問題なくファイルに書き込みできました。

ただ、PVEホスト上には UID 101000 ユーザが存在しないため、ファイルの所有者が 101000:101000 のような数値 ID として見えてしまいますが、それは許容することにしました。

5. Samba 設定の整理

public 共有

public はゲスト読み取り専用の共有として割り切りました。

ini
[public]
    path = /shared/public
    browseable = yes
    guest ok = yes
    read only = yes
    force user = nobody

read only = yes のため、以前設定していた inherit permissions = yes は不要と判断し、削除しました。

private 共有

private は認証済みユーザのみアクセス可能としました。

ini
[private]
    path = /shared/private
    browseable = no
    guest ok = no
    read only = no
    force user = takashi
    force group = takashi
    create mask = 0660
    directory mask = 0770
    force create mode = 0660
    force directory mode = 0770

force user = takashiforce group = takashi を入れた理由は、Samba 経由で作成されるファイルの所有者・グループを明確に揃えるためです。

今回の構成では、PVE ホスト側のファイル所有者を 101000:101000 に寄せています。これは LXC 内の 1000:1000 に対応します。そのため、Samba 側でも takashi に寄せた方が管理が楽と判断しました。

6. smbd 再起動

設定変更後、以下コマンドで Samba 設定が正しいか検証します。

bash
testparm

実際の出力は以下です。

text
root@debian-samba:~# testparm 
Load smb config files from /etc/samba/smb.conf
Loaded services file OK.

構文として問題ないことを確認した後、smbd を再起動します。

bash
systemctl restart smbd

ステータス確認結果は以下の通りで、smbd が正常に起動していることがわかります。

text
root@debian-samba:~# systemctl status smbd --no-pager
* smbd.service - Samba SMB Daemon
     Loaded: loaded (/usr/lib/systemd/system/smbd.service; enabled; preset: enabled)
     Active: active (running) since Sat 2026-05-23 10:22:41 UTC; 26s ago
 Invocation: d512a9f020824b79aa553afe81eb4d18
       Docs: man:smbd(8)
             man:samba(7)
             man:smb.conf(5)
    Process: 1022 ExecCondition=/usr/share/samba/is-configured smb (code=exited, status=0/SUCCESS)
    Process: 1025 ExecStartPre=/usr/share/samba/update-apparmor-samba-profile (code=exited, status=0/SUCCESS)
   Main PID: 1026 (smbd)
     Status: "smbd: ready to serve connections..."
      Tasks: 4 (limit: 37909)
     Memory: 11.5M (peak: 12.1M)
        CPU: 264ms
     CGroup: /system.slice/smbd.service
             |-1026 /usr/sbin/smbd --foreground --no-process-group
             |-1030 "smbd: notifyd" .
             |-1031 "smbd: cleanupd "
             `-1032 "smbd: client [192.168.1.7]"

May 23 10:22:41 debian-samba systemd[1]: Starting smbd.service - Samba ......
May 23 10:22:41 debian-samba systemd[1]: Started smbd.service - Samba S...on.
May 23 10:22:43 debian-samba smbd[1032]: pam_unix(samba:session): sessi...=0)
Hint: Some lines were ellipsized, use -l to show in full.

以下から 192.168.1.7 のクライアントから接続が来ていることも確認できます。

text
smbd: client [192.168.1.7]

最終確認結果

最終的に、外部 PC から private 共有にアクセスして以下を確認できました。

  • ゲストでは private にアクセス不可
  • takashi では private にアクセス可能
  • takashi では private に書き込み可能

得られた知見

今回の検証から得られた知見は以下です。

  1. Samba 共有内で symlink を使って共有ルート外を指す構成は、期待通りに動かないことがあります。
  2. PVE + LXC では、symlink で逃げるより mount point で正しく見せる方が堅実です。
  3. unprivileged LXC では、コンテナ内 UID/GID と PVE ホスト側 UID/GID がずれます。
  4. LXC 内 UID 1000 は、標準的なマッピングでは PVE ホスト側 UID 101000 として扱われます。
  5. Samba の書き込み問題は、Samba 設定だけでなく Linux ファイル権限と UID/GID マッピングを含めて見る必要があります。
  6. force user / force group を使うと、Samba 経由で作成されるファイルの所有者を揃えやすくなります。
  7. testparmsystemctl status smbd による確認は、設定反映後の基本チェックとして有効です。

参考文献