背景と要件
社内でFTPによるファイル共有を行っており、以前は共通アカウントを使用していましたが、これによりアカウントとパスワードが広く共有され、権限管理が困難になっていました。そのため、FTPサービスをLDAP(社内のドメインコントローラ)と連携させ、個人ドメインアカウントでのログインを実現することを計画しました。
具体的な要件
- ドメインアカウントでのログイン
- 未承認のユーザーはFTPにログインできるが、共有コンテンツへのアクセス権がない
環境構成
オペレーティングシステム
# lsb_release -a LSB Version: :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64:desktop-4.1-noarch:languages-4.1-amd64:languages-4.1-noarch:printing-4.1-amd64:printing-4.1-noarch Distributor ID: CentOS Description: CentOS Linux release 7.9.2009 (Core) Release: 7.9.2009 Codename: Core
実装手順
手順概要
- 基本パッケージのインストール
- vsftpdの設定
- LDAP接続設定のデバッグ
手順1: 基本パッケージのインストール
# yum install nscd nss-pam-ldapd -y # yum -y install vsftpd ftp openldap-clients
手順2: vsftpdの設定
まず、設定ファイルを示します。
# cat /etc/vsftpd/vsftpd.conf use_localtime=YES listen=YES connect_from_port_20=YES ftpd_banner=Welcome to virtual FTP service. anonymous_enable=NO local_enable=YES write_enable=NO anon_upload_enable=NO anon_mkdir_write_enable=NO anon_other_write_enable=NO chroot_local_user=YES local_umask=022 guest_enable=YES guest_username=ftpuser tcp_wrappers=YES use_localtime=yes max_per_ip=20 pasv_enable=YES pasv_min_port=3001 pasv_max_port=5100 idle_session_timeout=600 allow_writeable_chroot=YES pam_service_name=vsftpd # ここが重要な設定で、/etc/pam.d/vsftpdファイルに対応 xferlog_enable=YES xferlog_file=/var/log/vsftpd/xferlog.log dual_log_enable=yes # 仮想ユーザーとローカルユーザーは同じ権限を持つ virtual_use_local_privs=YES user_sub_token=$USER local_root=/home/noaccess # 未承認ユーザーの共通rootディレクトリ(中身なし) user_config_dir=/etc/vsftpd/vusers # ここに承認済みユーザーの設定を配置し、ユーザーのrootディレクトリを制御 anon_world_readable_only=YES anon_upload_enable=NO anon_mkdir_write_enable=YES anon_other_write_enable=NO
次に、PAM設定ファイルです。
# cat /etc/pam.d/vsftpd # LDAP認証を使用する設定 #%PAM-1.0 auth required /usr/lib64/security/pam_ldap.so account required /usr/lib64/security/pam_ldap.so
承認済みユーザー用の設定ディレクトリを作成し、テストアカウントファイルを作成します。共有ディレクトリを`/opt`と仮定します。
# mkdir -p /etc/vsftpd/vusers # cd /etc/vsftpd/vusers # testuser local_root=/opt
手順3: LDAP接続設定とデバッグ
vsftpdを直接起動すると、FTPアクセス時にシステムログに以下のエラーが表示されます。
# tail -f /var/log/secure Sep 18 15:30:21 server vsftpd[311630]: pam_ldap(vsftpd:auth): Authentication failure; user=testuser Sep 18 15:48:51 server vsftpd[316172]: pam_ldap(vsftpd:auth): error opening connection to nslcd: No such file or directory
この問題を解決するために、nslcdサービスを使用する必要があります。設定ファイルは`/etc/nslcd.conf`です。
# cat /etc/nslcd.conf uid nslcd gid ldap uri ldap://domain-controller.example.com # ドメインコントローラーのアドレスを実際の環境に合わせて設定 base DC=example,DC=com # 実際の環境に合わせて設定 binddn CN=service-account,OU=Services,DC=example,DC=com # 読み取り専用アカウント(管理者アカウントでも可) bindpw password # 上記アカウントのパスワード ssl no filter passwd (objectClass=user) # 実際の環境に合わせて設定 map passwd uid sAMAccountName # 上記filterと連携して設定
問題と解決策
- フィルタとマッピングの未設定
フィルタとマッピングを設定しない場合、正しいアカウントとパスワードでFTPにログインできません。
# ftp localhost Trying 127.0.0.1... Connected to localhost (127.0.0.1). 220 Welcome to virtual FTP service. Name (localhost:root): testuser 331 Please specify the password. Password: 530 Login incorrect. Login failed. ftp>
アカウントとパスワードが正しいにもかかわらずログインできない場合、FTPサーバーが使用するアカウントとドメインコントローラーの間のマッピングが不一致である可能性があります。
- nslcdサービスのデバッグ
/var/log/secureからnslcdサービスがLDAPにアクセスしていることがわかります。nslcdのヘルプオプションにdebugオプションがあるので、これを使用します。
# nslcd --help
Usage: nslcd [OPTION]...
Name Service LDAP connection daemon.
-c, --check check if the daemon already is running
-d, --debug don't fork and print debugging to stderr
--help display this help and exit
--version output version information and exit
Report bugs to
サービスを停止し、デバッグモードで起動します。
# systemctl stop nslcd.service
# nslcd -d
nslcd: DEBUG: add_uri(ldap://domain-controller.example.com)
nslcd: version 0.8.13 starting
nslcd: DEBUG: unlink() of /var/run/nslcd/socket failed (ignored): No such file or directory
nslcd: DEBUG: initgroups("nslcd",55) done
nslcd: DEBUG: setgid(55) done
nslcd: DEBUG: setuid(65) done
nslcd: accepting connections
この状態でFTPにアクセスすると、詳細なログが表示されます。
# nslcd -d
nslcd: DEBUG: add_uri(ldap://domain-controller.example.com)
nslcd: version 0.8.13 starting
nslcd: DEBUG: unlink() of /var/run/nslcd/socket failed (ignored): No such file or directory
nslcd: DEBUG: initgroups("nslcd",55) done
nslcd: DEBUG: setgid(55) done
nslcd: DEBUG: setuid(65) done
nslcd: accepting connections
nslcd: [8b4567] DEBUG: connection from pid=321676 uid=0 gid=0
nslcd: [8b4567] DEBUG: nslcd_pam_authc("testuser","vsftpd","***")
nslcd: DEBUG: accept() failed (ignored): Resource temporarily unavailable
nslcd: [8b4567] DEBUG: myldap_search(base="DC=example,DC=com", filter="(&(objectClass=posixAccount)(uid=testuser))")
nslcd: [8b4567] DEBUG: ldap_initialize(ldap://domain-controller.example.com)
nslcd: [8b4567] DEBUG: ldap_set_rebind_proc()
nslcd: [8b4567] DEBUG: ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,3)
nslcd: [8b4567] DEBUG: ldap_set_option(LDAP_OPT_DEREF,0)
nslcd: [8b4567] DEBUG: ldap_set_option(LDAP_OPT_TIMELIMIT,0)
nslcd: [8b4567] DEBUG: ldap_set_option(LDAP_OPT_TIMEOUT,0)
nslcd: [8b4567] DEBUG: ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,0)
nslcd: [8b4567] DEBUG: ldap_set_option(LDAP_OPT_REFERRALS,LDAP_OPT_ON)
nslcd: [8b4567] DEBUG: ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)
nslcd: [8b4567] DEBUG: ldap_simple_bind_s("CN=service-account,OU=Services,DC=example,DC=com","***") (uri="ldap://domain-controller.example.com")
nslcd: [8b4567] DEBUG: rebinding to ldap://DomainDnsZones.example.com/DC=DomainDnsZones,DC=example,DC=com
nslcd: [8b4567] DEBUG: ldap_simple_bind_s("CN=service-account,OU=Services,DC=example,DC=com","***") (uri="ldap://DomainDnsZones.example.com/DC=DomainDnsZones,DC=example,DC=com")
nslcd: [8b4567] DEBUG: rebinding to ldap://ForestDnsZones.example.com/DC=ForestDnsZones,DC=example,DC=com
nslcd: [8b4567] DEBUG: ldap_simple_bind_s("CN=service-account,OU=Services,DC=example,DC=com","***") (uri="ldap://ForestDnsZones.example.com/DC=ForestDnsZones,DC=example,DC=com")
nslcd: [8b4567] DEBUG: rebinding to ldap://example.com/CN=Configuration,DC=example,DC=com
nslcd: [8b4567] DEBUG: ldap_simple_bind_s("CN=service-account,OU=Services,DC=example,DC=com","***") (uri="ldap://example.com/CN=Configuration,DC=example,DC=com")
nslcd: [8b4567] DEBUG: ldap_result(): end of results (0 total)
nslcd: [8b4567] DEBUG: "testuser": user not found: No such object
重要なのはこの行です:myldap_search(base="DC=example,DC=com", filter="(&(objectClass=posixAccount)(uid=testuser))")。以前にLDAPに接続する際の経験から、このフィルターが問題であることがわかります。この環境ではuidではなくsAMAccountNameを使用し、objectClassも異なるため、実際の環境に合わせて以下のように変更しました。
filter passwd (objectClass=user) map passwd uid sAMAccountName
設定を更新した後、再度FTPにアクセスします。
# ftp localhost Trying 127.0.0.1... Connected to localhost (127.0.0.1). 220 Welcome to virtual FTP service. Name (localhost:root): testuser 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp> ls 227 Entering Passive Mode (127,0,0,1,13,225). 150 Here comes the directory listing. -rw-r--r-- 1 0 0 0 Sep 18 09:50 a # ls -alrt /opt total 4 dr-xr-xr-x. 21 root root 4096 Sep 18 09:50 .. -rw-r--r-- 1 root root 0 Sep 18 09:50 a drwxr-xr-x 2 root root 15 Sep 18 09:50 .
正常にログインできていることが確認できます。次に、未承認のアカウントでテストします。
# ftp localhost Trying 127.0.0.1... Connected to localhost (127.0.0.1). 220 Welcome to virtual FTP service. Name (localhost:root): unauthuser 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp> ls 227 Entering Passive Mode (127,0,0,1,14,172). 150 Here comes the directory listing. -rw-r--r-- 1 0 0 0 Sep 18 15:29 アクセス権限なし.txt 226 Directory send OK. ftp> ftp> quit 221 Goodbye. # ls /home/noaccess/ アクセス権限なし.txt
未承認ユーザーは`/home/noaccess/`ディレクトリ(vsftpd.confで設定された)にアクセスしていることが確認できます。これで、VSFTP + LDAPの連携は完了です。
今後の運用とまとめ
新しいユーザーを承認する必要がある場合、すべてのユーザーが同じ`local_root`を使用するため、testuserファイルのシンボリックリンクを作成するだけで済みます。例えば、新しいユーザー"newuser"を追加するには、以下のコマンドを実行します。
# cd /etc/vsftpd/vusers # ln -s testuser newuser
今後、ユーザーの一括追加または削除を行うスクリプトを作成することも可能です。
この設定の核心は、nslcdをLDAPサーバーに接続する際のデバッグです。デバッグ情報がない場合、多くの時間をかけて設定を調整する必要があるかもしれません。