CentOS上のSamba3からSamba4へのtdbユーザ情報の移行

# 概要
200名規模の企業にてオンプレミスのCentOS6上のSamba3サーバを、CentOS7上のSamba4に移行しました。Samba3からSamba4、特にスタンドアローンのSambaに関する記事は日本語はもちろん英語も情報が極端に少ないので、メモとして残します。

間違った情報があればツッコミをお願いします。

**ここに書いてある手順はこちらの手元の環境で試した際のものです。他の環境での動作を保証するものではありませんし、問題が起こったとしても補償は出来かねますことを予めご了承下さい。また、ユーザ情報は追記ではなく上書きとなります。**

# 前提

<dl>
<dt>バージョン情報</dt>
<dd>移行元：<br/>
OS=CentOS6.5(Final), カーネル=2.6.32-431.3.1.el6.x86_64, Samba=3.6.23-12.el6.x86_64<br/>
移行先：<br/>
OS=CentOS Linux release 7.4.1708 (Core), 
カーネル=3.10.0-693.21.1.el7.x86_64,
Samba=4.6.2-12.el7_4.x86_64</dd>
<dt>Active Directoryが使えない</dt>
<dd>Samba4の売りはA/Dサーバになれることですが、今回はその機能は使えません。社内にはWindows ServerのA/Dサーバが居るのですが、Samba3環境がA/Dサーバと連携しておらず、スタンドアローンの設定になっています。また、独自のグループとACLが絡んだ複雑なアクセス権を設定しており、A/Dサーバと今更連携を取るのが非常に困難です。社内には同じようなスタンドアローンの複雑な設定のSambaサーバが複数おり、それらをA/D環境下に再構築することは負担が大きすぎました。</dd>
<dt>Samba3のバックエンドがtdb</dt>
<dd>バックエンドはsmbpasswdファイルではなくtdbを使っていました。バイナリのファイルなので中身を見るのも困難です。</dd>
<dt>ダウンタイムを最小にして欲しいとのユーザからの強い要望</dt>
<dd>業務が輻輳する中でパート社員も含めた各ユーザに何か手順をやってもらうのは非常に手間がかかるので、なるべく何もせずに移行できるようにして欲しいとの<strong>強い</strong>要望が出ていました。</dd>
<dt>ユーザ情報のみの移行で良い・パスなどの変更もある</dt>
<dd>共有しているディレクトリの配下の構成は変わりませんが、その上位のパスは整理のために移動したいです。ここはユーザからは見えないのでそれほど問題にはなりません。設定ファイル「smb.conf」も作り直します。<br/>
つまり純粋にユーザの認証情報を移行できれば、それ以外はあまり気にする必要はありません。</dd>
<dt>ユーザー認証情報のバックアップを取りたい</dt>
<dd>なるべくファイルの形でユーザー認証情報についてバックアップを取りたいです。</dd>
<dt>KVM上の仮想環境である</dt>
<dd>あまりSambaの動作には影響しないと思いますが、今回は移行元も移行先もKVM上のCentOSマシンです。</dd>
</dl>

# 検討

## Samba3のtdbをsmbpasswdにエクスポート→没

以下のコマンドでSamba3のtdbのユーザ情報をsmbpasswd形式でエクスポート出来ます。その後このファイルをSamba4に取り込もうと考えました。

```shell-session
[root]# pdbedit -e smbpasswd:/tmp/smbpasswd.txt
```

しかしこの場合、かなり多くのユーザアカウントで以下のエラーが出ます。

```shell-session
build_sam_pass: Failing attempt to store user with non-uid based user RID.
```

以下のSambaのメーリングリストの情報によると、smbpasswd形式ではLinuxのuidからRIDを自動計算しており、それがtdbに入っているものと一致しないというエラーのようです。しかもsmbpasswd形式にすることで失われる情報も多いようです。

https://lists.samba.org/archive/samba/2014-February/179090.html

以上のことから、このやり方はボツにしました。

## LDAPサーバを立ててそちらをバックエンドにする→没

移行専用のOpenLDAPサーバを立てて、社内のA/D環境と混ぜない独自のSamba用バックエンド環境に統一しようと考えました。あるいはすべての認証情報のみOpenLDAPサーバに移行して、Sambaの認証はLDAPで行うようにするといった方法も考えました。OpenLDAPであれば内容のダンプをldif形式のテキストで落とせるはずなのでバックアップも楽になる、というのも計算にありました。
しかし、以下の記事によるとその方法は無理だとわかりました。Samba4はA/Dサーバになる機能のために自前でDNS・Kerberos認証・LDAPなどの機能を実装していますが、Samba4のLDAPを主としてOpenLDAPにレプリケーションなどをすることは出来ても、認証そのものをOpenLDAPに代行させることは出来ないようです。プレーンテキストの認証を許可できないというような理由のようです。

https://lists.samba.org/archive/samba/2015-March/189871.html

というわけで、こちらの案もボツになりました。

## 必要なtdbファイルをSamba4にリストアする→採用

Samba3・4共に/var/lib/samba配下にたくさんのtdbファイルがありますが、tdbファイルバイナリのデータベースファイルですのでそのままcpでコピーすることは出来ません。tdb形式のデータの確認・バックアップ・リストアのために、まずは以下のツールをyumでインストールします。

```shell-session
# yum install -y tdb-tools
```

これでtdbdump、tdbbackup、tdbrestoreのコマンドが使えるようになりました。
tdbdumpはtdbの中身をテキストで吐き出します。
tdbbackupはSambaが稼働中でも安全にtdbファイルのコピーを取ります。
tdbrestoreはtdbdumpのテキストファイルからtdbファイルを作成します。

次に、各種tdbファイルの役割を調べました。Samba3については以下のサイトに情報がありました。（現実のtdbファイルの配置と完全に一致はしていませんでしたが…）

http://www.samba.gr.jp/project/translation/Samba3-HOWTO/tdb.html
https://wiki.samba.org/index.php/TDB_Locations

とりあえずバックエンドをtdbにした時にSambaのユーザ情報を格納しているのは「/var/lib/samba/private/passdb.tdb」で間違いないようです。
Samba3のpassdb.tdbとSamba4のpassdb.tdbをそれぞれtdbdumpコマンドで見てみましたが、データベースの形式（スキーマ）は大きな変化はなさそうです。

**passdb.tdbのスキーマの形**
1. key(数字)="値"とdata(数字)="値"で格納されている。
2. key(XX)="RID_XXXXXXXX\00"のdataにはユーザ名が入っている。
3. key(XX)="USER_XXXXX\00"のdataにはユーザの属性が入っている。
4. システム用の値としてkey(XX)="INFO/version", "INFO/minor_version", "NEXT_RID"がある。
5. version, minor_versionはSamba3とSamba4で変わらない値だった。

よっておそらくSamba3のpassdb.tdbをSamba4のpassdb.tdbにインポートすれば、ユーザ情報を持ってこれると考えました。
ちなみに他のtdbファイルはSamba3とSamba4で違っているものも多かったので、よく検証した上でないと持ってこられないと思います。

# やり方
## Linuxユーザの移行

Linuxユーザのuidやグループの関係などもそのまま持ってくる必要があります。これはいろんな方がすでにやっていらっしゃると思うので、簡単に書きます。

(1) 移行元のLinuxユーザ設定ファイルを移行先へコピー

```shell-session
[移行元のroot]# scp /etc/passwd 移行先:/root/tmp/
[移行元のroot]# scp /etc/shadow 移行先:/root/tmp/
[移行元のroot]# scp /etc/group 移行先:/root/tmp/
[移行元のroot]# scp /etc/gpasswd 移行先:/root/tmp/
```

(2) 移行先でvigrコマンドを使い、必要なグループ情報(gidが1000以上)をインポートする。

```shell-session
[移行先のroot]# awk -F ':' '$3 >= 1000' /root/tmp/group
[移行先のroot]# vigr
　awkで抽出された必要なグループの情報をviで/etc/groupに追記します。
[移行先のroot]# cat /root/tmp/gshadow
[移行先のroot]# vigr -s
　上で抽出されたグループのgshadow情報をviで/etc/gshadowに追記します。
```

(3) 移行先でvipwコマンドを使い、必要なユーザ情報(uidが1000以上)をインポートする。

```shell-session
[移行先のroot]# awk -F ':' '$3 >= 1000' /root/tmp/passwd
[移行先のroot]# vipw
　awkで抽出された必要なユーザの情報をviで/etc/passwdに追記します。
[移行先のroot]# cat /root/tmp/shadow
[移行先のroot]# vipw -s
　上で抽出されたユーザのshadow情報をviで/etc/shadowに追記します。
```

(4) 移行元、移行先で以下のコマンドを発行し、追加されたグループ情報とユーザ情報が一致することを確認します。

```shell-session
[root]# getent group
[root]# getent passwd
```

いくつかのユーザで、idコマンドでの情報が一致することや、パスワードが変わらずにログイン出来ることを試して下さい。以上でLinuxユーザの移行は完了です。

## Sambaユーザの移行

(1) 移行元でtdbbackupコマンドを発行し、/var/lib/samba/private/passdb.tdbのバックアップを取ります。以下コマンドを発行すると、同じディレクトリにpadddb.tdb.bakが作成されます。Sambaのデーモンを止める必要が無く、安全にバックアップを取ることが出来ます。

```shell-session
[移行元:root]# tdbbackup -v /var/lib/samba/private/passdb.tdb
　バックアップ元ファイルが壊れていないことをチェックします。
[移行元:root]# tdbbackup /var/lib/samba/private/passdb.tdb
　passdb.tdb.bakを作成する。
[移行元:root]# scp /var/lib/samba/private/passdb.tdb.bak 移行先:/root/tmp/
　passdb.tdb.bakを移行先に転送します。
```

(2) 移行先のsmb,nmbサービスが停止していることを確認後、cpコマンドで/var/lib/samba/private/passdb.tdbに移行元のtdb.bakファイルを上書きします。

```shell-session
[移行先:root]# systemctl status smb nmb
　smb,nmbのサービスが停止していることを確認します。停止していない場合は停止します。
[移行先:root]# mv /var/lib/samba/private/passdb.tdb{,.orig}
　元に戻せるように元のpassdb.tdbを残しておきます。
[移行先:root]# cp /root/tmp/passdb.tdb.bak /var/lib/samba/private/passdb.tdb
　passdb.tdbを移行元のファイルで上書きします。
```

(3) 移行元、移行先で以下のコマンドを発行し、Sambaのユーザ情報が一致することを確認します。

```shell-session
[root]# pdbedit -L
　Sambaユーザの一覧を表示する。
```

いくつかのユーザでSambaの共有に以前と同じパスワードで入れることを確認します。

(4) バックアップについては、/var/lib/samba/passdb.tdbをtdbbackupで定期的にバックアップすることで解決できそうです。念のため、他のtdbファイルもバックアップを取るようにします。

# まとめ

情報が非常に少なく、これで長期運用した時に支障が出るかどうかも不明なのですが、今の所は順調に動作しています。ユーザの負担を少なくしたまま環境を移行できました。

最後にSambaについて心の叫びを一つ。

**情報少なすぎ！**
**Active Directoryとか大げさなバックエンドの環境構築をしなくても閉じた世界で完結する「LinuxとWindowsのファイル共有」だけを実現するわかりやすい方法を提供して欲しい！**