TCP Wrapperの構築のメモ
TCP Wrapperとは hosts.allow hosts.deny ファイルによって
リモートからの TCP接続を制限するアクセス制御機構です (Wikipediaが詳しい)
inetd やら SSH やらいろんなソフトに使われています
IPv6対応版の TCP Wrapperが存在するということで試してみました ビルドの手順をレポートします
CPU | PhenomII X4 905e (2.5GHz 4コア) |
---|---|
マザーボード | GA-880GM-USB3 |
メモリ | 8GB (DDR3-1066) |
イーサネット | Realtek8111D (R8168 GbE) |
グラフィック | Radeon HD 4250 オンボード |
サウンド | Realtek ALC892 codec (HD) オンボード |
Linuxカーネルバージョン | 2.6.37.1 (x86_64) |
GCC | 4.5.2 |
glibc | 2.13.1 |
構築
公式サイトである ftp.porcupine.org にソースがあります
「IPV6 version by Casper Dik (tcp_wrappers_7.6-ipv6.4.tar.gz)」 というのがあるので
これをダウンロードします
ソースアーカイブを展開します
$ tar -xzf tcp_wrappers_7.6-ipv6.4.tar.gz
$ cd tcp_wrappers_7.6-ipv6.4
$
$ tar -xzf tcp_wrappers_7.6-ipv6.4.tar.gz $ cd tcp_wrappers_7.6-ipv6.4 $
ソースコードは 一般的な configureスクリプトが付属していないため
手動で Makefileをカスタマイズする必要があります
REAL_DAEMON_DIR=/usr/sbin 〜 IPV6 = -DHAVE_IPV6 〜 TABLES = -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" 〜 CFLAGS = -O -DFACILITY=$(FACILITY) $(ACCESS) $(PARANOID) $(NETGROUP) \ $(BUGS) $(SYSTYPE) $(AUTH) $(UMASK) \ -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" $(STYLE) $(KILL_OPT) \ -DSEVERITY=$(SEVERITY) -DRFC931_TIMEOUT=$(RFC931_TIMEOUT) \ $(UCHAR) $(TABLES) $(STRINGS) $(TLI) $(EXTRA_CFLAGS) $(DOT) \ $(VSYSLOG) $(HOSTNAME) $(IPV6) -fPIC
まず REAL_DAEMON_DIR が設定されてないのでこれを設定する必要があります
これは tcpd などの実行プログラムのインストール先を指定します
また IPv6対応させるために IPV6 の記述を有効化しています
TABLES は既に設定されているので内容の確認です
hosts.allow hosts.deny のパスを修正したい場合はここをカスタマイズします
CFLAGSには -fPIC を追加しています
これは後述にもありますが 共有ライブラリ libwrap.so.0 を作るのに必要です
Makefileを終えたら make コマンドでビルドを開始します
ただし引数に OSの種類 (linux とか freebsd など) を指定する必要があります
$ CC=gcc make linux make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' に入ります gcc -O -DFACILITY=LOG_MAIL -DHOSTS_ACCESS -DPARANOID -DGETPEERNAME_BUG -DBROKEN_FGETS -DLIBC_CALLS_STRTOK -DDAEMON_UMASK=022 -DREAL_DAEMON_DIR=\"/usr/sbin\" -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" -DBROKEN_SO_LINGER -DUSE_STRERROR -Dvsyslog=myvsyslog -DALWAYS_HOSTNAME -DHAVE_IPV6 -fPIC -c tcpd.c gcc -O -DFACILITY=LOG_MAIL -DHOSTS_ACCESS -DPARANOID -DGETPEERNAME_BUG -DBROKEN_FGETS -DLIBC_CALLS_STRTOK -DDAEMON_UMASK=022 -DREAL_DAEMON_DIR=\"/usr/sbin\" -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" -DBROKEN_SO_LINGER -DUSE_STRERROR -Dvsyslog=myvsyslog -DALWAYS_HOSTNAME -DHAVE_IPV6 -fPIC -c hosts_access.c hosts_access.c: In function ‘ipv6_mask’: hosts_access.c:408:5: error: ‘uchar_t’ undeclared (first use in this function) hosts_access.c:408:5: note: each undeclared identifier is reported only once for each function it appears in hosts_access.c:408:14: error: ‘p’ undeclared (first use in this function) hosts_access.c:408:27: error: expected expression before ‘)’ token hosts_access.c:419:27: error: expected expression before ‘)’ token make[1]: *** [hosts_access.o] エラー 1 make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' から出ます make: *** [linux] エラー 2 $
未定義 uchar_t でいきなりこけました
ソースコードを確認したところ uchar_t は hosts_access.c の一部でしか使われてなく
文字列を 1バイト毎に操作する目的で使われているだけのようです
対処としては uchar_t を unsigned charに置換します
Makefileの CFLAGSに -Duchar_t=’unsigned char’ を追加します
CFLAGS = -O -DFACILITY=$(FACILITY) $(ACCESS) $(PARANOID) $(NETGROUP) \ $(BUGS) $(SYSTYPE) $(AUTH) $(UMASK) \ -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" $(STYLE) $(KILL_OPT) \ -DSEVERITY=$(SEVERITY) -DRFC931_TIMEOUT=$(RFC931_TIMEOUT) \ $(UCHAR) $(TABLES) $(STRINGS) $(TLI) $(EXTRA_CFLAGS) $(DOT) \ $(VSYSLOG) $(HOSTNAME) $(IPV6) -fPIC -Duchar_t='unsigned char'
再度 makeします
$ CC=gcc make linux 〜 gcc -O -DFACILITY=LOG_MAIL -DHOSTS_ACCESS -DPARANOID -DGETPEERNAME_BUG -DBROKEN_FGETS -DLIBC_CALLS_STRTOK -DDAEMON_UMASK=022 -DREAL_DAEMON_DIR=\"/usr/sbin\" -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" -DBROKEN_SO_LINGER -DUSE_STRERROR -Dvsyslog=myvsyslog -DALWAYS_HOSTNAME -DHAVE_IPV6 -fPIC -Duchar_t='unsigned char' -o tcpd tcpd.o libwrap.a libwrap.a(misc.o): In function `tcpd_gethostbyname': misc.c:(.text+0x1e0): undefined reference to `freehostent' misc.c:(.text+0x1fc): undefined reference to `getipnodebyname' misc.c:(.text+0x227): undefined reference to `freehostent' misc.c:(.text+0x243): undefined reference to `getipnodebyname' collect2: ld はステータス 1 で終了しました make[1]: *** [tcpd] エラー 1 make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' から出ます make: *** [linux] エラー 2 $
今度は freehostent() や getipnodebyname() の未解決エラーです
これら関数は IPv6の正引きを行うものですが 最近の glibcには含まれておらず
getaddrinfo() と getnameinfo() の利用が推奨されています
TCP Wrapperでは回避策が用意されており
getipnodebyname() の代わりに gethostbyname2() を使う -DUSE_GETHOSTBYNAME2 があります
Makefileを再度修正して IPV6の行を以下のようにします
# If your system does not have getipnodebyname() but uses the obsolete # gethostbyname2() instead, use this (AIX) IPV6 = -DHAVE_IPV6 -DUSE_GETHOSTBYNAME2
で 再度 makeすると
$ CC=gcc make linux 〜 scaffold.c:28:14: error: conflicting types for ‘malloc’ scaffold.c: In function ‘dup_hostent’: scaffold.c:62:2: 警告: incompatible implicit declaration of built-in function ‘exit’ scaffold.c: In function ‘check_dns’: scaffold.c:154:5: 警告: incompatible implicit declaration of built-in function ‘free’ scaffold.c: In function ‘clean_exit’: scaffold.c:176:5: 警告: incompatible implicit declaration of built-in function ‘exit’ make[1]: *** [scaffold.o] エラー 1 make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' から出ます make: *** [linux] エラー 2 $
malloc() の定義が矛盾していると言われました
scaffold.c を確認修正する必要あります
#ifndef INADDR_NONE #define INADDR_NONE (-1) /* XXX should be 0xffffffff */ #endif // extern char *malloc(); /* Application-specific. */ #include "tcpd.h" #include "scaffold.h"
char *malloc() の宣言が既に異なってるのでコメントアウトしました
glibcで宣言されている void *malloc( size_t ) が有効になるはずです
$ CC=gcc make linux 〜 gcc -O -DFACILITY=LOG_MAIL -DHOSTS_ACCESS -DPARANOID -DGETPEERNAME_BUG -DBROKEN_FGETS -DLIBC_CALLS_STRTOK -DDAEMON_UMASK=022 -DREAL_DAEMON_DIR=\"/usr/sbin\" -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" -DBROKEN_SO_LINGER -DUSE_STRERROR -Dvsyslog=myvsyslog -DALWAYS_HOSTNAME -DHAVE_IPV6 -DUSE_GETHOSTBYNAME2 -fPIC -Duchar_t='unsigned char' -o tcpdchk tcpdchk.o fakelog.o inetcf.o scaffold.o libwrap.a make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' から出ます $
やっとコンパイルが通りました
作られたファイルは
バイナリである safe_finger tcpd tcpdchk tcpdmatch try-from と
ライブラリ本体である libwrap.a
Cヘッダファイル tcpd.h
マニュアルページ tcpd.8 tcpdchk.8 tpcdmatch.8 hosts_access.5 hosts_options.5 hosts_access.3
これらをインストールします
しかも make install が使えなかったので 手動でインストールします
$ su # cp safe_finger /usr/sbin/ # cp tcpd /usr/sbin/ # cp tcpdchk /usr/sbin/ # cp tcpdmatch /usr/sbin/ # cp try-from /usr/sbin/ # cp libwrap.a /usr/lib64/ # cp tcpd.h /usr/include/ # cp tcpd.8 /usr/share/man/man8/ # cp tcpdchk.8 /usr/share/man/man8/ # cp tcpdmatch.8 /usr/share/man/man8/ # cp hosts_access.5 /usr/share/man/man5/ # cp hosts_options.5 /usr/share/man/man5/ # cp hosts_access.3 /usr/share/man/man3/ #
もうひとつ 共有ライブラリである libwrap.so.0 が欲しいのですが作られていません
不足ファイルの作成
まず /etc/hosts.allow /etc/hosts.deny を作成します
hosts.denyから
ALL : ALL
ALL : ALL
これにより hosts.allowにマッチしないアクセスは 全て遮断されます
hosts.allowです
ALL : 127.0.0.1 ALL : 192.168.0.0/255.255.255.0 ALL : [::1/128] ALL : [fe80::/16]
許可するアドレスとして
IPv4では ループバック 127.0.0.1 と LANのセグメント 192.168.0.0/24
IPv6では リンクローカル ::1 と サイトローカル fe80::
以後 アクセス許可したい アドレス ネットワークが増加すれば追記する形にします
最後に libwrap.so.0 ですがこれを作る方法は ソースアーカイブに記述はなさそうです
ただ現状 本検証環境(Gentoo stage3ベース)では libwrap.so.0 が存在し使われています
(どうやって作ったのでしょうか?)
先ほどの TCP Wrapperのビルドの過程を参考にします
ar rv libwrap.a hosts_access.o options.o shell_cmd.o rfc931.o eval.o hosts_ctl.o refuse.o percent_x.o clean_exit.o setenv.o fromhost.o fix_options.o socket.o tli.o workarounds.o update.o misc.o diag.o percent_m.o myvsyslog.o
amd64-linux-gnu-ar: creating libwrap.a
a - hosts_access.o
a - options.o
a - shell_cmd.o
a - rfc931.o
a - eval.o
a - hosts_ctl.o
a - refuse.o
a - percent_x.o
a - clean_exit.o
a - setenv.o
a - fromhost.o
a - fix_options.o
a - socket.o
a - tli.o
a - workarounds.o
a - update.o
a - misc.o
a - diag.o
a - percent_m.o
a - myvsyslog.o
ranlib libwrap.a
ar rv libwrap.a hosts_access.o options.o shell_cmd.o rfc931.o eval.o hosts_ctl.o refuse.o percent_x.o clean_exit.o setenv.o fromhost.o fix_options.o socket.o tli.o workarounds.o update.o misc.o diag.o percent_m.o myvsyslog.o amd64-linux-gnu-ar: creating libwrap.a a - hosts_access.o a - options.o a - shell_cmd.o a - rfc931.o a - eval.o a - hosts_ctl.o a - refuse.o a - percent_x.o a - clean_exit.o a - setenv.o a - fromhost.o a - fix_options.o a - socket.o a - tli.o a - workarounds.o a - update.o a - misc.o a - diag.o a - percent_m.o a - myvsyslog.o ranlib libwrap.a
この部分です libwrap.a をアーカイブするのに必要なオブジェクトが分かります
単純に考えれば これを参考に下記のように直接 libwrap.so.0 が作れるはずです
# gcc -shared -o /usr/lib64/libwrap.so.0 -Wl,-soname,libwrap.so.0 hosts_access.o options.o shell_cmd.o rfc931.o eval.o hosts_ctl.o refuse.o percent_x.o clean_exit.o setenv.o fromhost.o fix_options.o socket.o tli.o workarounds.o update.o misc.o diag.o percent_m.o myvsyslog.o #
(注意 上記コマンドで作った libwrap.so.0 はうまく動作しません)
たまたま SSHが動作検証に使えたのでリモート 192.168.0.3 から本システム 192.168.0.254
にアクセスしてみます
$ ssh 192.168.0.254 ssh_exchange_identification: Connection closed by remote host $
? 許可されません
先ほど libwrap.so.0 を作ったシステムで sshd を実行してみると分かりますが
libwrap.so.0 がうまく作れていません
# sshd -h sshd: symbol lookup error: sshd: undefined symbol: deny_severity #
このエラーメッセージの意味するところは
libwrap.so.0 で deny_severity (と allow_severity)が定義されていないため
sshd と libwrap.so.0 の実行時リンクに失敗しているのです
次のようにして deny_severity allow_severity を定義済にした libwrap.so.0 を作ります
# echo 'int allow_severity; int deny_severity;' | gcc -shared -o /usr/lib64/libwrap.so.0 -Wl,-soname,libwrap.so.0 hosts_access.o options.o shell_cmd.o rfc931.o eval.o hosts_ctl.o refuse.o percent_x.o clean_exit.o setenv.o fromhost.o fix_options.o socket.o tli.o workarounds.o update.o misc.o diag.o percent_m.o myvsyslog.o -x c -
これにより作られた libwrap.so.0 は allow_severity deny_severity が解決されています
# nm /usr/lib64/libwrap.so.0 | grep severity 00000000002085cc B allow_severity 00000000002085c8 B deny_severity 0000000000003af0 t severity_map 0000000000003b3c t severity_option #
また 遠隔 192.168.0.3 からの sshアクセスに成功しました
$ ssh 192.168.0.254 $
さらに検証可能ならば /etc/hosts.allow を一時的に修正した検証を行い
192.168.0.0/255.255.255.0 の行を削除すると リモートからのログインができなくなる
というような動作まで確認するとよいでしょう
とりあえず libwrap.so.0 は完成としますが 心に引っかかる点として
libwrap.a は未だに allow_severity deny_severity が未解決のままである
# nm /usr/lib64/libwrap.a | grep severity U allow_severity U deny_severity 0000000000000000 t severity_map 000000000000004c t severity_option U deny_severity #
というところが 後々問題になるかもといったところです
そもそも標準で libwrap.so.0 を作る手段が提供されていないといった点も 引っかかりますが