2015/08/26
CentOS 6 に rssh を導入する
rssh は ssh (1) を利用したアクセスのうち
scp (1) や sftp (1) といった
特定のコマンドのみの実行を許可したり
chroot 環境を提供するログインシェルで、
セキュアなファイル転送は提供したいがシェルアカウントへの
ログインを拒否したい場合などに非常に有効なログインシェルだ。
scp (1) や sftp (1) のみの実行を許可する場合は
アカウントのログインシェルを rssh に変更して
設定ファイルで実行を許可するコマンドを指定するだけで設定は完了する。
しかし chroot 環境を提供する場合はそのための環境設定が必要である。
$ sudo yum -y install rssh
:
:
:
インストール:
rssh.x86_64 0:2.3.4-1.el6
完了しました!
# This is the default rssh config file
# set the log facility. "LOG_USER" and "user" are equivalent.
logfacility = LOG_USER
# Leave these all commented out to make the default action for rssh to lock
# users out completely...
allowscp # scpを許可
allowsftp # sftpを許可
#allowcvs
#allowrdist
#allowrsync
# set the default umask
umask = 022
# If you want to chroot users, use this to set the directory where the root of
# the chroot jail will be located.
#
# if you DO NOT want to chroot users, LEAVE THIS COMMENTED OUT.
chrootpath = /opt/chroot # chroot先ディレクトリ
# You can quote anywhere, but quotes not required unless the path contains a
# space... as in this example.
#chrootpath = "/usr/local/my chroot"
##########################################
# EXAMPLES of configuring per-user options
:
:
:
いくつかエラーが表示されるがここでは気にしなくても大丈夫
$ sudo sh /usr/share/doc/rssh-2.3.4/mkchroot.sh /opt/chroot
NOT changing owner of root jail.
NOT changing perms of root jail.
setting up /opt/chroot/usr/bin
setting up /opt/chroot/usr/libexec/openssh
setting up /opt/chroot/usr/libexec
Copying libraries for /usr/bin/scp.
(0x00007fff541ff000)
cp: cannot stat `(0x00007fff541ff000)': そのようなファイルやディレクトリはありません
/usr/lib64/libcrypto.so.10
/lib64/libutil.so.1
/lib64/libz.so.1
/lib64/libnsl.so.1
/lib64/libcrypt.so.1
/lib64/libresolv.so.2
/lib64/libgssapi_krb5.so.2
/lib64/libkrb5.so.3
/lib64/libk5crypto.so.3
/lib64/libcom_err.so.2
/usr/lib64/libnss3.so
/lib64/libc.so.6
/lib64/libdl.so.2
/lib64/libfreebl3.so
/lib64/libkrb5support.so.0
/lib64/libkeyutils.so.1
/lib64/libpthread.so.0
/usr/lib64/libnssutil3.so
/lib64/libplc4.so
/lib64/libplds4.so
/lib64/libnspr4.so
/lib64/libselinux.so.1
/lib64/librt.so.1
Copying libraries for /usr/libexec/openssh/sftp-server.
(0x00007fff577f1000)
cp: cannot stat `(0x00007fff577f1000)': そのようなファイルやディレクトリはありません
/usr/lib64/libcrypto.so.10
/lib64/libutil.so.1
/lib64/libz.so.1
/lib64/libnsl.so.1
/lib64/libcrypt.so.1
/lib64/libresolv.so.2
/lib64/libgssapi_krb5.so.2
/lib64/libkrb5.so.3
/lib64/libk5crypto.so.3
/lib64/libcom_err.so.2
/usr/lib64/libnss3.so
/lib64/libc.so.6
/lib64/libdl.so.2
/lib64/libfreebl3.so
/lib64/libkrb5support.so.0
/lib64/libkeyutils.so.1
/lib64/libpthread.so.0
/usr/lib64/libnssutil3.so
/lib64/libplc4.so
/lib64/libplds4.so
/lib64/libnspr4.so
/lib64/libselinux.so.1
/lib64/librt.so.1
Copying libraries for /usr/bin/rssh.
(0x00007fff00bff000)
cp: cannot stat `(0x00007fff00bff000)': そのようなファイルやディレクトリはありません
/lib64/libc.so.6
Copying libraries for /usr/libexec/rssh_chroot_helper.
(0x00007fff3e152000)
cp: cannot stat `(0x00007fff3e152000)': そのようなファイルやディレクトリはありません
/lib64/libc.so.6
copying name service resolution libraries...
tar: メンバ名から先頭の `/' を取り除きます
tar: /lib/libnss_files*: stat 不能: そのようなファイルやディレクトリはありません
tar: /lib/libnss1_files*: stat 不能: そのようなファイルやディレクトリはありません
tar: 前のエラーにより失敗ステータスで終了します
Setting up /etc in the chroot jail
cp: omitting directory `/etc/ld.so.conf.d'
Chroot jail configuration completed.
NOTE: if you are not using the passwd file for authentication,
you may need to copy some of the /lib/libnss_* files into the jail.
NOTE: you must MANUALLY edit your syslog rc script to start syslogd
with appropriate options to log to /opt/chroot/dev/log. In most cases,
you will need to start syslog as:
/sbin/syslogd -a /opt/chroot/dev/log
NOTE: we make no guarantee that ANY of this will work for you... if it
doesn't, you're on your own. Sorry!
まず chroot 環境に /dev/null を作成する。
$ ls -l /dev/null
crw-rw-rw-. 1 root root 1, 3 8月 26 10:58 2015 /dev/null
$ sudo mknod /opt/chroot/dev/null c 1 3
$ sudo chmod 666 /opt/chroot/dev/null
$ ls -l /opt/chroot/dev/null
crw-rw-rw-. 1 root root 1, 3 8月 26 11:59 2015 /opt/chroot/dev/null
chroot 環境にコピーされるのは以下のコマンドなので、
それぞれのコマンドの ldd の結果を参照して
不足しているライブラリをコピーする。
ldd の出力結果からコピーが必要なライブラリを決定しているのだが
linux-vdso.so.1 と ld-linux-x86-64.so.2 は
表示形式が通常と異なっているためにコピーできていない様。
$ ldd /usr/bin/scp
linux-vdso.so.1 => (0x00007ffef53f0000)
libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00007fbd8e06a000)
libutil.so.1 => /lib64/libutil.so.1 (0x00007fbd8de67000)
libz.so.1 => /lib64/libz.so.1 (0x00007fbd8dc50000)
libnsl.so.1 => /lib64/libnsl.so.1 (0x00007fbd8da37000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007fbd8d800000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x00007fbd8d5e5000)
libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007fbd8d3a1000)
libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007fbd8d0ba000)
libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007fbd8ce8d000)
libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007fbd8cc89000)
libnss3.so => /usr/lib64/libnss3.so (0x00007fbd8c94a000)
libc.so.6 => /lib64/libc.so.6 (0x00007fbd8c5b5000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fbd8c3b1000)
libfreebl3.so => /lib64/libfreebl3.so (0x00007fbd8c1ae000)
libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007fbd8bfa2000)
libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007fbd8bd9f000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fbd8bb82000)
/lib64/ld-linux-x86-64.so.2 (0x00007fbd8e662000)
libnssutil3.so => /usr/lib64/libnssutil3.so (0x00007fbd8b955000)
libplc4.so => /lib64/libplc4.so (0x00007fbd8b750000)
libplds4.so => /lib64/libplds4.so (0x00007fbd8b54c000)
libnspr4.so => /lib64/libnspr4.so (0x00007fbd8b30d000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fbd8b0ee000)
librt.so.1 => /lib64/librt.so.1 (0x00007fbd8aee5000)
$ ldd /usr/libexec/openssh/sftp-server
linux-vdso.so.1 => (0x00007ffe2abcf000)
libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00007fbca7520000)
libutil.so.1 => /lib64/libutil.so.1 (0x00007fbca731d000)
libz.so.1 => /lib64/libz.so.1 (0x00007fbca7106000)
libnsl.so.1 => /lib64/libnsl.so.1 (0x00007fbca6eed000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007fbca6cb6000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x00007fbca6a9b000)
libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007fbca6857000)
libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007fbca6570000)
libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007fbca6343000)
libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007fbca613f000)
libnss3.so => /usr/lib64/libnss3.so (0x00007fbca5e00000)
libc.so.6 => /lib64/libc.so.6 (0x00007fbca5a6b000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fbca5867000)
libfreebl3.so => /lib64/libfreebl3.so (0x00007fbca5664000)
libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007fbca5458000)
libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007fbca5255000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fbca5038000)
/lib64/ld-linux-x86-64.so.2 (0x00007fbca7b18000)
libnssutil3.so => /usr/lib64/libnssutil3.so (0x00007fbca4e0b000)
libplc4.so => /lib64/libplc4.so (0x00007fbca4c06000)
libplds4.so => /lib64/libplds4.so (0x00007fbca4a02000)
libnspr4.so => /lib64/libnspr4.so (0x00007fbca47c3000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fbca45a4000)
librt.so.1 => /lib64/librt.so.1 (0x00007fbca439b000)
$ ldd /usr/bin/rssh
linux-vdso.so.1 => (0x00007ffffe781000)
libc.so.6 => /lib64/libc.so.6 (0x00007f823c297000)
/lib64/ld-linux-x86-64.so.2 (0x00007f823c630000)
$ ldd /usr/libexec/rssh_chroot_helper
linux-vdso.so.1 => (0x00007fffb99e9000)
libc.so.6 => /lib64/libc.so.6 (0x00007fde8f0ff000)
/lib64/ld-linux-x86-64.so.2 (0x00007fde8f498000)
$ ls -1 /opt/chroot/lib64
libc.so.6
libcom_err.so.2
libcrypt.so.1
libdl.so.2
libfreebl3.so
libgssapi_krb5.so.2
libk5crypto.so.3
libkeyutils.so.1
libkrb5.so.3
libkrb5support.so.0
libnsl.so.1
libnspr4.so
libplc4.so
libplds4.so
libpthread.so.0
libresolv.so.2
librt.so.1
libselinux.so.1
libutil.so.1
libz.so.1
linux-vdso.so.1 は各プログラムのアドレス空間のみに常駐する
Virtual Dynamic Shared Object なので実体は存在しなくて問題ない。
それ以外に不足しているライブラリ(今回はld-linux-x86-64.so.2) を
chroot 環境にコピーする。
なおここで設定したファイルは後に削除する事。
# 不足しているライブラリをコピー
$ sudo cp -p /lib64/ld-linux-x86-64.so.2 /opt/chroot/lib64
# /bin/bash の実行を可能に
$ sudo mkdir /opt/chroot/bin
$ sudo cp -p /bin/bash /opt/chroot/bin/
$ sudo cp -p /lib64/libtinfo.so.5 /opt/chroot/lib64
# /usr/bin/ldd の実行を可能に
$ sudo cp -p /usr/bin/ldd /opt/chroot/usr/bin
$ sudo cp -p /bin/cat /opt/chroot/bin
# /usr/bin/strace の実行を可能に
$ sudo cp -p /usr/bin/strace /opt/chroot/usr/bin
scp (1) でアカウント情報を正しく取得できていない事が確認できる。
strace (1) で動作を確認してみると
/lib64/libnss_files.so.2 が open (2)エラーになっている事が判る。
# chroot /opt/chroot /bin/sh
# 必要なライブラリーを確認
# ldd /usr/bin/scp
linux-vdso.so.1 => (0x00007fff00345000)
libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00007f2b2ca5b000)
libutil.so.1 => /lib64/libutil.so.1 (0x00007f2b2c858000)
libz.so.1 => /lib64/libz.so.1 (0x00007f2b2c641000)
libnsl.so.1 => /lib64/libnsl.so.1 (0x00007f2b2c428000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f2b2c1f1000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f2b2bfd6000)
libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007f2b2bd92000)
libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007f2b2baab000)
libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007f2b2b87e000)
libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007f2b2b67a000)
libnss3.so => /usr/lib64/libnss3.so (0x00007f2b2b33b000)
libc.so.6 => /lib64/libc.so.6 (0x00007f2b2afa6000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f2b2ada2000)
libfreebl3.so => /lib64/libfreebl3.so (0x00007f2b2ab9f000)
libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007f2b2a993000)
libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007f2b2a790000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f2b2a573000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2b2d053000)
libnssutil3.so => /usr/lib64/libnssutil3.so (0x00007f2b2a346000)
libplc4.so => /lib64/libplc4.so (0x00007f2b2a141000)
libplds4.so => /lib64/libplds4.so (0x00007f2b29f3d000)
libnspr4.so => /lib64/libnspr4.so (0x00007f2b29cfe000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f2b29adf000)
librt.so.1 => /lib64/librt.so.1 (0x00007f2b298d6000)
# /usr/bin/scp -t .
unknown user 0
# strace /usr/bin/scp -t .
:
:
:
open("/lib64/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib64/tls/x86_64/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/lib64/tls/x86_64", 0x7ffc3cabffb0) = -1 ENOENT (No such file or directory)
open("/lib64/tls/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/lib64/tls", 0x7ffc3cabffb0) = -1 ENOENT (No such file or directory)
open("/lib64/x86_64/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/lib64/x86_64", 0x7ffc3cabffb0) = -1 ENOENT (No such file or directory)
open("/lib64/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/lib64", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/usr/lib64/tls/x86_64/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/tls/x86_64", 0x7ffc3cabffb0) = -1 ENOENT (No such file or directory)
open("/usr/lib64/tls/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/tls", 0x7ffc3cabffb0) = -1 ENOENT (No such file or directory)
open("/usr/lib64/x86_64/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/x86_64", 0x7ffc3cabffb0) = -1 ENOENT (No such file or directory)
open("/usr/lib64/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
munmap(0x7f86d27d9000, 13667) = 0
write(2, "unknown user 0\r\n", 16unknown user 0
) = 16
exit_group(255) = ?
+++ exited with 255 +++
附属のスクリプトでメッセージに表示されてる様に
必要な libnss_* をコピーする。
$ sudo cp -p /lib64/libnss_files.so.2 /opt/chroot/lib64
これで chroot された環境に scp (1)、sftp (1) での
アクセスが可能となる。
正常な動作が確認できたら検証のためのコマンドを削除する。
$ sudo rm /opt/chroot/bin/bash $ sudo rm /opt/chroot/lib64/libtinfo.so.5 $ sudo rm /opt/chroot/usr/bin/ldd $ sudo rm /opt/chroot/bin/cat $ sudo rm /opt/chroot/usr/bin/strace
2015/06/03
ランダムなパスワード生成
標準的なコマンドを使用してランダムなパスワードを生成する方法を検討。
自分で考えたのは以下のコマンド
$ cat /dev/random | strings | dd bs=11 count=1 2> /dev/null| tr -d '\n'
これでも一応ランダムなパスワード的な文字列が取得できる。
対して 上田会長のアイデアはこちら。
$ cat /dev/urandom | tr -dc 'a-z0-9A-Z' | fold -b10
tr (1) コマンドの -c オプションは思い浮かばなかった。
まさに目から鱗な気分です。さすがシェル芸の家元だけあって目の付け所がシャープ
ちなみに Mac だと tr (1) や fold (1) の挙動が異なるので以下になります。
$ cat /dev/urandom | LC_CTYPE=C tr -dc 'a-z0-9A-Z' | fold -w 10
2014/06/19
Amazon AWS 上でホスト情報を取得する
Amazon AWS で ELB + AutoScaling を利用している場合など、
IP アドレスが頻繁に変わってしまうので、
グローバルIP、プライベートIP を取得して一覧表示するスクリプト。
ホスト名は Name タグの Value に格納されている必要がある。
aws コマンドは EC2 インスタンスで Amazon Linux を起動した場合は
標準でインストールされている筈。
EC2 インスタンスに適切な role が設定されていれば aws はそのまま動作するが、
role が設定されていない場合でも設定する事で利用可能になる。
引数でホスト名 (Name タグで指定された値) が指定された場合、 デフォルトではグローバル IP のみを出力するが -p オプションでプライベートIP のみを表示する。
1#!/bin/sh 2 3# リージョン 4region="--region ap-northeast-1" 5# 出力はテキスト形式 6output="--output text" 7# 出力項目を指定 (ホスト名(=タグ)、インスタンスID、パブリックIP、プライベートIP 8base="--query Reservations[*].Instances[*]." 9# Name タグの値表示 10name="Tags[?Key==\`Name\`].Value," 11# インスタンスID、パブリックIP、プライベートIP 12default="${name}InstanceId,PublicIpAddress,PrivateIpAddress" 13# フィルタ指定 (running 状態のみ出力) 14filter="--filter Name=instance-state-name,Values=running" 15 16# aws コマンド 17aws="/usr/bin/aws ec2 describe-instances ${region} ${output}" 18 19# メイン関数 20function doit() 21{ 22 23 local _filter _query 24 25 if [ -n "${1}" ] 26 then 27 _filter="${filter} Name=tag:Name,Values=${1}" 28 _query=${base}[${3}${2}] 29 else 30 _filter=${filter} 31 _query=${base}[${default}] 32 fi 33 34 ${aws} ${_filter} ${_query} 35 36} 37 38# オプション解析 39if opt=`getopt p $*` 40then 41 set -- ${opt} 42 while [ -n "${1}" ] 43 do 44 case "${1}" in 45 -p ) 46 target=PrivateIpAddress;; 47 -- ) 48 shift 49 break;; 50 esac 51 shift 52 done 53else 54 echo "Usage: ${0##*/} [-p][host [... host]" 1>&2 55 exit 255 56fi 57 58if [ $# -eq 1 ] 59then 60 doit ${1} ${target:-PublicIpAddress} 61elif [ $# -gt 0 ] 62then 63 for i in $* 64 do 65 doit ${i} ${target:-PublicIpAddress} ${name} 66 done | awk '{ if(NR % 2) ip = $0; else printf "%s %s\n", $0, ip }' 67else 68 doit | awk '{ if(NR % 2) ip = $0; else printf "%s %s\n", $1, ip }' 69fi
2013/11/23 追記
複数ホスト名が指定された場合はホスト名も表示する様変更。2014/05/23
checkinstall で rpm パッケージの作成がエラーになる場合
rpm パッケージを手軽に作成するために checkinstall は非常に便利だが、 現行の checkinstall にはインストール対象となるソフトウェアに ファイルに対するシンボリックリンクが含まれると rpm パッケージが作成できなくなる不具合がある様だ。
この不具合は checkinstall が rpm パッケージ用の spec ファイルを生成する際に、 ターゲットとなるオブジェクトがディレクトリかシンボリックリンクなら 無条件にディレクトリと仮定して %dir と出力しているために発生している。 そのために、例えばインストール時に libhoge.so.X.Y を libhoge.so にシンボリックリンクする様なソフトウェアの rpm パッケージを作成しようとするとエラーとなってしまう。
そこでターゲットなるオブジェクトがシンボリックリンクの場合、
リンク先を確認してディレクトリだった場合のみ
%dir を出力する様にするパッチを作成した。
*** /usr/sbin/checkinstall~ 2014-05-21 12:29:30.000000000 +0900
--- /usr/sbin/checkinstall 2014-05-23 23:51:09.131081380 +0900
***************
*** 2428,2434 ****
# Prepare directories to be included in the .spec file
mv ${TMP_DIR}/newfiles ${TMP_DIR}/newfiles.tmp
cat ${TMP_DIR}/newfiles.tmp | while read line; do
! [ -d "${BUILD_DIR}/${line}" -o -L "${BUILD_DIR}/${line}" ] && echo -n "%dir " >> ${TMP_DIR}/newfiles
echo "\"/${line}\"" >> ${TMP_DIR}/newfiles
done
--- 2428,2435 ----
# Prepare directories to be included in the .spec file
mv ${TMP_DIR}/newfiles ${TMP_DIR}/newfiles.tmp
cat ${TMP_DIR}/newfiles.tmp | while read line; do
! [ -d "${BUILD_DIR}/${line}" ] && echo -n "%dir " >> ${TMP_DIR}/newfiles
! [ -L "${BUILD_DIR}/${line} -a -d `ls -l ${BUILD_DIR}/${line} | awk '$0=$NF'` ] && echo -n "%dir" >> ${TMP_DIR}/newfiles
echo "\"/${line}\"" >> ${TMP_DIR}/newfiles
done
/usr/sbin/checkinstall にこのパッチを適用する事で
シンボリックリンク先がディレクトリの場合のみ
%dir を出力する様に変更される。
シンボリックリンクがネストしている場合は正しく動作しないので
あくまでも暫定的なパッチだが、
取りあえず rpm パッケージを作成した場合にはそれなりに有用だと思う。
一応 checkinstall の配布元の bugzilla にはパッチを添えて報告してある。
ちなみに checkinstall で rpm の作成がエラーになる場合は 殆どが自動生成された spec ファイルが問題な場合なので、 --review-spec オプションを指定して checkinstall を実行する事で、 rpm パッケージの作成処理を実行する前に 自動生成された spec ファイルを編集できるので問題を回避できる事が多い。
# checkinstall --review-spec</i>
2014/05/22
Amazon Linux AMI 2014.03.1 (64bit) への checkinstall 導入
Amazon AWS の EC2 インスタンスで動作している Amazon Linux AMI 2014.03.1
(Linux ****** 3.10.40-50.136.amzn1.x86_64 #1 SMP Tue May 13 21:35:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux) への
checkinstall の導入時にエラーとなった場合の対処方法。
Amazon AWS の EC2 インスタンスに checkinstall コマンドを導入しようとして、 ネットなどの情報に基づいて以下のコマンドを実行した所コンパイルエラーが発生した。
$ git clone http://checkinstall.izto.org/checkinstall.git $ cd checkinstall $ vi Makefile checkinstall checkinstallrc-dist installwatch/Makefile $ make : : make -C installwatch make[1]: ディレクトリ `/home/mitz/checkinstall/installwatch' に入ります gcc -Wall -c -D_GNU_SOURCE -DPIC -fPIC -D_REENTRANT -DVERSION=\"0.7.0beta7\" installwatch.c installwatch.c:2960:5: error: conflicting types for ‘readlink’ int readlink(const char *path,char *buf,size_t bufsiz) { ^ In file included from installwatch.c:41:0: /usr/include/unistd.h:831:16: note: previous declaration of ‘readlink’ was here extern ssize_t readlink (const char *__restrict __path, ^ installwatch.c:3098:5: error: conflicting types for ‘scandir’ int scandir( const char *dir,struct dirent ***namelist, ^ In file included from installwatch.c:49:0: /usr/include/dirent.h:255:12: note: previous declaration of ‘scandir’ was here extern int scandir (const char *__restrict __dir, ^ installwatch.c:3714:5: error: conflicting types for ‘scandir64’ int scandir64( const char *dir,struct dirent64 ***namelist, ^ In file included from installwatch.c:49:0: /usr/include/dirent.h:278:12: note: previous declaration of ‘scandir64’ was here extern int scandir64 (const char *__restrict __dir, ^ make[1]: *** [installwatch.o] エラー 1 make[1]: ディレクトリ `/home/mitz/checkinstall/installwatch' から出ます make: *** [all] エラー 2 $ざっと調べてみると glibc のバージョン情報を格納していると思われるマクロ GLIBC_MINOR の値が不正で readlink()、 及び内部で定義している scandir()、scandir64() のプロトタイプ宣言が正しく展開されていない様だったので、 他にもいくつか発生したコンパイルエラーを修正して 取りあえずコンパイルしてインストール可能なパッチを作成した。
本来は Amazon LINUX 上でも glibc のバージョンを正しく取得できる様にするべきなのだが、 諸処の事情でそこまで修正する余裕がないので暫定的な対策のみとなっている。
1diff -rc checkinstall/installwatch/installwatch.c checkinstall~/installwatch/installwatch.c 2*** checkinstall/installwatch/installwatch.c 2014-05-21 12:41:51.659879736 +0900 3--- checkinstall~/installwatch/installwatch.c 2014-05-21 12:41:16.107577033 +0900 4*************** 5*** 99,105 **** 6 static int (*true_xstat)(int,const char *,struct stat *); 7 static int (*true_lxstat)(int,const char *,struct stat *); 8 9! #if(GLIBC_MINOR >= 10) 10 11 static int (*true_scandir)( const char *,struct dirent ***, 12 int (*)(const struct dirent *), 13--- 99,105 ---- 14 static int (*true_xstat)(int,const char *,struct stat *); 15 static int (*true_lxstat)(int,const char *,struct stat *); 16 17! #if 1 18 19 static int (*true_scandir)( const char *,struct dirent ***, 20 int (*)(const struct dirent *), 21*************** 22*** 130,136 **** 23 static int (*true_open64)(const char *, int, ...); 24 static struct dirent64 *(*true_readdir64)(DIR *dir); 25 26! #if(GLIBC_MINOR >= 10) 27 static int (*true_scandir64)( const char *,struct dirent64 ***, 28 int (*)(const struct dirent64 *), 29 int (*)(const struct dirent64 **,const struct dirent64 **)); 30--- 130,136 ---- 31 static int (*true_open64)(const char *, int, ...); 32 static struct dirent64 *(*true_readdir64)(DIR *dir); 33 34! #if 1 35 static int (*true_scandir64)( const char *,struct dirent64 ***, 36 int (*)(const struct dirent64 *), 37 int (*)(const struct dirent64 **,const struct dirent64 **)); 38*************** 39*** 2545,2551 **** 40 } 41 42 FILE *fopen(const char *pathname, const char *mode) { 43! FILE *result; 44 instw_t instw; 45 int status=0; 46 47--- 2545,2551 ---- 48 } 49 50 FILE *fopen(const char *pathname, const char *mode) { 51! FILE *result = NULL; 52 instw_t instw; 53 int status=0; 54 55*************** 56*** 2956,2962 **** 57 return result; 58 } 59 60! #if (GLIBC_MINOR <= 4) 61 int readlink(const char *path,char *buf,size_t bufsiz) { 62 int result; 63 #else 64--- 2956,2962 ---- 65 return result; 66 } 67 68! #if 0 69 int readlink(const char *path,char *buf,size_t bufsiz) { 70 int result; 71 #else 72*************** 73*** 3096,3103 **** 74 } 75 76 int scandir( const char *dir,struct dirent ***namelist, 77! int (*select)(const struct dirent *), 78! #if (GLIBC_MINOR >= 10) 79 int (*compar)(const struct dirent **,const struct dirent **) ) { 80 #else 81 int (*compar)(const void *,const void *) ) { 82--- 3096,3103 ---- 83 } 84 85 int scandir( const char *dir,struct dirent ***namelist, 86! int (*filter)(const struct dirent *), 87! #if 1 88 int (*compar)(const struct dirent **,const struct dirent **) ) { 89 #else 90 int (*compar)(const void *,const void *) ) { 91*************** 92*** 3114,3124 **** 93 /* We were asked to work in "real" mode */ 94 if( !(__instw.gstatus & INSTW_INITIALIZED) || 95 !(__instw.gstatus & INSTW_OKWRAP) ) { 96! result=true_scandir(dir,namelist,select,compar); 97 return result; 98 } 99 100! result=true_scandir(dir,namelist,select,compar); 101 102 return result; 103 } 104--- 3114,3124 ---- 105 /* We were asked to work in "real" mode */ 106 if( !(__instw.gstatus & INSTW_INITIALIZED) || 107 !(__instw.gstatus & INSTW_OKWRAP) ) { 108! result=true_scandir(dir,namelist,filter,compar); 109 return result; 110 } 111 112! result=true_scandir(dir,namelist,filter,compar); 113 114 return result; 115 } 116*************** 117*** 3712,3719 **** 118 } 119 120 int scandir64( const char *dir,struct dirent64 ***namelist, 121! int (*select)(const struct dirent64 *), 122! #if (GLIBC_MINOR >= 10) 123 int (*compar)(const struct dirent64 **,const struct dirent64 **) ) { 124 #else 125 int (*compar)(const void *,const void *) ) { 126--- 3712,3719 ---- 127 } 128 129 int scandir64( const char *dir,struct dirent64 ***namelist, 130! int (*filter)(const struct dirent64 *), 131! #if 1 132 int (*compar)(const struct dirent64 **,const struct dirent64 **) ) { 133 #else 134 int (*compar)(const void *,const void *) ) { 135*************** 136*** 3730,3740 **** 137 /* We were asked to work in "real" mode */ 138 if( !(__instw.gstatus & INSTW_INITIALIZED) || 139 !(__instw.gstatus & INSTW_OKWRAP) ) { 140! result=true_scandir64(dir,namelist,select,compar); 141 return result; 142 } 143 144! result=true_scandir64(dir,namelist,select,compar); 145 146 return result; 147 } 148--- 3730,3740 ---- 149 /* We were asked to work in "real" mode */ 150 if( !(__instw.gstatus & INSTW_INITIALIZED) || 151 !(__instw.gstatus & INSTW_OKWRAP) ) { 152! result=true_scandir64(dir,namelist,filter,compar); 153 return result; 154 } 155 156! result=true_scandir64(dir,namelist,filter,compar); 157 158 return result; 159 }2014/05/22 に git clone した checkinstall のソースに このパッチを適用してコンパイル、動作する事は確認できている。
Amazon Linux で checkinstall の導入に困っている方の参考になればと思い公開してみる。
2014/03/27
LDAP 登録用スクリプト
以前、某社内のユーザ管理を ldap 化した際に作成した
ldap 登録用の ldif ファイルを生成するスクリプト。
漢字の名字と名前を入力すると kakashi を利用して
ひらがなの名字、名前、ローマ字の名字、名前を自動的に取得し
ldap 登録用の ldif ファイルを生成して ldap 登録まで自動で行う。
当初は csv 型式のファイルから必要な情報を取得して
連続で ldap に登録するバッチスクリプトとして作成したのだが、
初期登録が完了した後はたまに発生するユーザの追加処理にのみ使用するので
対話的に実行するコマンドに作り直した。
漢字をひらがな/ローマ字に変換するために kakashi とnkf が必要になる。
漢字のよみは自動では完全には取得できないので変換した値を確認する様にしている。
ローマ字の氏名は私の趣味で名字は全て大文字、名前はキャピタライズとしている。
1#!/bin/sh 2# All rights reserved, copyright (C) 2014, Mitzyuki IMAIZUMI 3# 4 5# ldap 用設定 6passwd="password" 7org="dc=example,dc=com" 8 9# コマンド定義 10id="`/usr/bin/id -u`" 11kakasi=/usr/bin/kakasi 12slappasswd="/usr/sbin/slappasswd -h {md5}" 13toutf="/usr/bin/nkf -w" 14toeuc="/usr/bin/nkf -e" 15ldapadd="/usr/bin/ldapadd -x -D cn=Manager,${org}" 16ldappasswd="/usr/bin/ldappasswd -x -D cn=Manager,${org}" 17ldapsearch="/usr/bin/ldapsearch -LL -x -D cn=Manager,${org}" 18 19# uid 格納ファイル 20uidnum=/etc/uid 21 22# OU 選択肢 23oulist="Board Sales Develop Affair" 24 25# 入力用タイトル 26_uid="ユーザID" 27_sn="漢字名字" 28_gn="漢字名前" 29_hsn="名字よみ" 30_hgn="名前よみ" 31_tel="内線番号" 32_ou="所属" 33 34# 35# 汎用入力処理 36# $1: 入力された値を格納する変数名 37# $2: 必須フラグ 38# 39getans() 40{ 41 42 title=`eval "echo \\$_$1"` 43 var=$1 44 require=${2:-true} 45 46 while true 47 do 48 echo -n "${title} を入力して下さい: " 49 read ans 50 51 if [ -z "${ans}" ] 52 then 53 if ${require} 54 then 55 echo "${title} の入力は必須です" 56 else 57 echo "${titoe} なし" 58 break 59 fi 60 else 61 break 62 fi 63 done 64 65 eval "$var=${ans}" 66 67} 68 69# 70# カナ確認/入力処理 71# $1: 入力された値を格納する変数名 72# 73getkana() 74{ 75 76 title=`eval "echo \\$_$1"` 77 var=$1 78 79 echo -n "${title} は \"${2}\" で正しいですか?([y]/n): " 80 read ans 81 if [ "${ans}" = "n" ] 82 then 83 getans ${var} 84 else 85 eval "$var=${2}" 86 fi 87 88} 89 90# 91# 所属選択処理 92# $1: 入力された値を格納する変数名 93# 94getou() 95{ 96 97 title=`eval "echo \\$_$1"` 98 var=$1 99 100 while true 101 do 102 i=1 103 set ${oulist} 104 for o 105 do 106 echo "${i} ) ${o}" 107 i=`expr ${i} + 1` 108 done 109 echo -n "${title} を選択して下さい: " 110 read ans 111 if [ "${ans}" -ge 1 -a "${ans}" -lt $i ] 112 then 113 eval "$var=\${$ans}" 114 break 115 else 116 echo "入力が不正です" 117 fi 118 done 119 120} 121 122# 123# UID 取得処理 124# 125getuid() 126{ 127 128 num=`cat ${uidnum}` 129 expr ${num} + 1 > ${uidnum} 130 131} 132 133# 134# People 用 ldif ファイルの生成 135# 136ladduser() 137{ 138 139 cat <<-EOF | ${toutf} 140 dn: uid=${uid},ou=People,${org} 141 uid: ${uid} 142 objectclass: posixAccount 143 objectclass: top 144 objectclass: shadowAccount 145 objectclass: inetOrgPerson 146 cn: ${sn} ${gn} 147 cn: ${hsn} ${hgn} 148 cn: ${rgn} ${rsn} 149 sn: ${rsn} 150 givenname: ${rgn} 151 mail: ${uid}@example.com 152 o: Example, Inc. 153 ou: ${ou} 154 loginShell: /bin/bash 155 uidNumber: ${num} 156 gidNumber: ${num} 157 homeDirectory: /home/${uid} 158 gecos: ${rgn} ${rsn} 159EOF 160 test -n "${tel}" && echo "telephoneNumber: ${tel}" 161 echo 162 163} 164 165# 166# Group 用 ldif ファイルの生成 167# 168laddgroup() 169{ 170 171 cat <<-EOF | ${toutf} 172 dn: cn=${uid},ou=Group,${org} 173 objectclass: posixGroup 174 objectclass: top 175 cn: ${uid} 176 userPassword: {crypt}x 177 gidNumber: ${num} 178 memberUid: ${uid} 179 180EOF 181 182} 183 184# 185# ID が既に使用済みか確認する 186# $1: ID 187# 188existldap() 189{ 190 191 if ! egrep -q "^${1}:" /etc/passwd 192 then 193 return `${ldapsearch} uid=${1} -w ${passwd} | grep uid | wc -l` 194 else 195 return 1 196 fi 197 198} 199 200# 201# メイン処理 202# 203if [ "${id}" != "0" ] 204then 205 echo "root にて実行して下さい" 1>&2 206 exit 255 207fi 208 209# 情報入力 210while true 211do 212 while true 213 do 214 getans "uid" 215 if existldap ${uid} 216 then 217 break 218 fi 219 echo "${uid} は既に使用されています" 1>&2 220 done 221 getans "sn" 222 getans "gn" 223 gettel "tel" false 224 getkana "hsn" "`echo ${sn} | ${toeuc} | ${kakasi} -JH | ${toutf}`" 225 getkana "hgn" "`echo ${gn} | ${toeuc} | ${kakasi} -JH | ${toutf}`" 226 getou "ou" 227 228 echo 229 for i in uid sn gn hsn hgn tel 230 do 231 eval "echo \$_$i: \$$i" 232 done 233 234 echo -n "この情報で正しいですか?([y]/n): " 235 read ans 236 if [ "${ans}" != "n" ] 237 then 238 break 239 fi 240done 241 242# ローマ字表記の名字取得(全て大文字) 243rsn=`echo ${hsn} | ${toeuc} | ${kakasi} -Ha -Ka -Ja -Ea -ka | tr '[a-z]' '[A-Z]'` 244# ローマ字表記の名前取得(キャピタライズ) 245rgn=`echo ${hgn} | ${toeuc} | ${kakasi} -Ha -Ka -Ja -Ea -ka | awk '{ print toupper(substr($0, 1, 1)) substr($0, 2, length($0) - 1) }'` 246getuid 247# People 登録 248ladduser | ${ldapadd} -w ${passwd} 249# Group 登録 250laddgroup | ${ldapadd} -w ${passwd} 251# パスワード自動生成 252${ldappasswd} uid=${uid},ou=People,${org} -w ${passwd}
あまり需要がないとは思うけど折角なので公開してみる。
2014/03/25
キャピタライズ
英単語の先頭を大文字に置換する処理をキャピタライズというが、
このキャピタライズ処理を unix 標準のコマンドで実装しようという話。
置換と言えばまず思い浮かぶのが sed (1) なのだが、
GNU 拡張された sed (1) の場合は簡単に置換が可能なのだが、
FreeBSD の標準の sed (1) では簡単にいかない。
- GNU sed の場合
-
$ echo "foo" | sed 's/\(.\)\(.*\)/\U\1\L\2/g' Foo
GNU 拡張された sed (1) には \U と /L という 演算子があるので大文字、小文字変換が簡単に可能である。
- FreeBSD 標準の sed の場合
-
$ echo "foo" | sed 's/\(.\)\(.*\)/\U\1\L\2/g' UfLoo
FreeBSD 標準の sed (1) だと正しく変換されない。
- awk を利用した場合
-
sed (1) ではなく awk (1) を利用すれば
FreeBSD でもキャピタライズ処理は簡単にできる。
$ echo "foo" | awk '{ print toupper(substr($0, 1, 1)) substr($0, 2, length($0) - 1) }' Foo
awk (1) には大文字変換関数が実装されているので、 切り出した1文字目を大文字変換すれば簡単に実現できる。
- FreeBSD で sed を利用した場合
-
どうしても FreeBSD の sed (1) を利用したい場合、
tr (1) も併用する事にはなるが以下の処理で可能だ。
$ eval `echo "foo" | sed 's/\(.\)\(.*\)/\/bin\/echo -n \1 | tr "[a-z]" "[A-Z]"; echo \2/g'` Foo
sed (1) でtr (1) を利用した置換スクリプトを出力して eval (1) で実行した結果を表示しているのだが もはやネタとしか思えなくもない。
良い子は真似しないで下さい(笑
2013/11/23
Exif 情報から GPS の緯度/経度情報を削除する
例によって こちら のブログをネタに
他人の褌で相撲を取る記事です。
元記事では Exif 情報を操作する時に、
バイナリを直接操作するのではなく
od (1) を利用してバイナリをテキスト型式に変換し、
テキスト操作した後で再度バイナリ型式に戻すという
正統的な手法でのアプローチをしています。
しかし dd (1) を利用すると
シェルスクリプトからバイナリを操作できるので、
バイナリ型式の画像を直接操作してしまうというアプローチを取ってみます。
画像ファイルに格納されている Exif 情報を簡単に解析して
GPS 情報を格納している領域 (GPSInfoIFD) から
緯度/経度情報だけをゼロに置換する処理となっています。
流石にシェル芸では無理なのでシェルスクリプトです。
1#!/bin/sh 2 3# 4# Endian を考慮して 4 バイト数字を取得する 5# 一度 od(1) で 16 進数に変換しバイト並びを修正した後に 10 進数に変換する 6# 7# $1: ファイル 8# $2: オフセット 9# 10get4Byte() 11{ 12 13 if [ ${endian} = "MM" ] 14 then 15 printf "%d" `dd if="${1}" bs=1 skip=${2} count=4 2> /dev/null | od -x | 16 sed -n "s/^0000000 *\(..\)\(..\)[ ]*\(..\)\(..\)/0x\2\1\4\3/p"` 17 else 18 printf "%d" `dd if="${1}" bs=1 skip=${2} count=4 2> /dev/null | od -x | 19 sed -n "s/^0000000 *\(..\)\(..\)[ ]*\(..\)\(..\)/0x\1\2\3\4/p"` 20 fi 21 22 23} 24 25# 26# Endian を考慮して 2 バイト数字を取得する 27# 一度 od(1) で 16 進数に変換しバイト並びを修正した後に 10 進数に変換する 28# 29# $1: ファイル 30# $2: オフセット 31# 32get2Byte() 33{ 34 35 if [ ${endian} = "MM" ] 36 then 37 printf "%d" `dd if="${1}" bs=1 skip=${2} count=2 2> /dev/null | od -x | 38 sed -n "s/^0000000 *\(..\)\(..\)/0x\2\1/p"` 39 else 40 printf "%d" `dd if="${1}" bs=1 skip=${2} count=2 2> /dev/null | od -x | 41 sed -n "s/^0000000 *\(..\)\(..\)/0x\1\2/p"` 42 fi 43 44} 45 46# 47# 任意の長さの文字列を取得する 48# 49# $1: ファイル 50# $2: オフセット 51# $3: 文字列長 52# 53getString() 54{ 55 56 dd if="${1}" bs=1 skip=${2} count=${3} 2> /dev/null 57 58} 59 60# 61# GPSInfoIFD の解析を行い緯度/経度情報をクリアする 62# 63# $1: ファイル 64# $2: GPSInfoIFD のタグ数 65# $3: GPSInfoIFD のオフセット位置 66# 67gps() 68{ 69 70 local i j offset tag val 71 72 i=0 73 74 while [ ${i} -lt ${2} ] 75 do 76 # GPSInfoIFD のタグ情報 77 offset=`expr ${3} + 14 + \( ${i} \* 12 \)` 78 tag=`get2Byte "${1}" ${offset}` 79 # タグ種類 80 # 2: 緯度 81 # 4: 経度 82 if [ ${tag} -eq 2 -o ${tag} -eq 4 ] 83 then 84 # データの数とデータのオフセットを取得 85 # 緯度/経度情報は値のタイプに RATIONAL(8バイト長) が指定されている 86 num=`get4Byte "${1}" \`expr ${offset} + 4\`` 87 val=`get4Byte "${1}" \`expr ${offset} + 8\`` 88 89 # 情報を削除するためにオフセットから 8 * データの数をゼロクリア 90 dd if=/dev/zero of="${1}" bs=1 conv=notrunc \ 91 seek=`expr ${val} + 12` count=`expr ${num} \* 8` 2> /dev/null 92 fi 93 i=`expr $i + 1` 94 done 95 96} 97 98# 99# 0th IFD データの解析処理 100# 101# $1: ファイル 102# $2: 0th IFD のタグ数 103# 104ifd() 105{ 106 107 local i offset count position 108 109 i=0 110 111 while [ ${i} -lt ${2} ] 112 do 113 # 0th IFD のタグ情報 114 offset=`expr 22 + \( ${i} \* 12 \)` 115 # GPSInfoIFDPointer の場合はオフセットとタグ数を取得 116 if [ `get2Byte "${1}" ${offset}` -eq 34853 ] 117 then 118 position=`get4Byte "${1}" \`expr ${offset} + 8\`` 119 count=`get2Byte "${1}" \`expr ${position} + 12\`` 120 121 gps "${1}" ${count} ${position} 122 fi 123 i=`expr $i + 1` 124 done 125 126} 127 128# 処理開始 129if [ "`getString "${1}" 6 4`" = "Exif" ] 130then 131 endian=`getString "${1}" 12 2` 132 133 ifd "${1}" `get2Byte "${1}" 20` 134fi
2013/11/11
grep -o
少々前の話ではあるが、某ブログ にて
grep -o について言及する記事が出ていた。
非常に便利そうな機能ではあるのだが、
POSIX (IEEE Std 1003.1, 2013 Edition) 準拠ではなく独自拡張の機能の様なので
POSIX の範囲での実現方法を考えてみた。
例えば
$ grep -o 'href="[^"]*"'
は
$ sed -n 's/.*\(href="[^"]*"\).*/\1/p'
で代用可能なので、
$ cat << EOF | sed -n 's/.*\(href="[^"]*"\).*/\1/p' <!DOCTYPE html> <html> <body> <a href="http://aho.aho" target="_blank">あほ</a> <img src="aho.jpg" alt="aho">エロい画像</a> </body> </html> EOF href="http://aho.aho"という結果が得られる。
素晴らしい! POSIX 準拠なコマンドで POSIX 非準拠をぶち破ったぞ!
論破! 論破! 論破!
ただし grep (1) の -E オプションは POSIX に準拠しているが、 sed (1) の -E オプションは POSIX に定義されていないので、
$ grep -o -E '(src|href)="[^"]*"'
には対応できない。FreeBSD の sed (1) など -E オプションが利用できれば
$ sed -n -E 's/.*((src|href)="[^"]*").*/\1/p'
とする事で対応できるのだが、
$ cat << EOF | sed -n -E 's/.*((src|href)="[^"]*").*/\1/p' <!DOCTYPE html> <html> <body> <a href="http://aho.aho" target="_blank">あほ</a> <img src="aho.jpg" alt="aho">エロい画像</a> </body> </html> EOF href="http://aho.aho" src="aho.jpg"POSIX の範疇を逸脱した時点で grep -o を使えば良いとなってしまう。
残念っ!
2013/11/23 追記
頑張る必要は全くありません(笑)普段は GNU の便利なオプションも使っていますし、 POSIX 準拠していないコマンドやオプションも使ってます。
純粋に楽しいからやってるだけです。
2013/10/02
国別フィルタの自動生成ツール
bsdhack.org ドメインは FreeBSD でサーバを構築しているのだが、
特定の国からのアタックや迷惑メイルが非常に多いので
ipfw (8) によるフィルタリングを利用して
指定した国からのパケットを拒否する設定を自動生成するツールを作成した。
APNIC から IPアドレスの割当リストを取得して、
IPアドレスを CIDR 型式に修正した上で
ipfw (8) のコマンドラインパラメタを自動生成している。
スクリプトは FreeBSD の ipfw (8) 向けだが、
出力部分を適宜修正する事で Linux の iptables (8) 向けの
設定も出力可能だと思う。
1#!/usr/bin/perl 2use Socket; 3 4# IP アドレス割当リスト取得 URL 5$url = "http://ftp.apnic.net/stats/apnic/delegated-apnic-latest"; 6 7# FreeBSD の ipfw (8) のルール番号 8if(@ARGV < 1){ 9 $rule = 2000; 10} 11else{ 12 $rule = $ARGV[0]; 13} 14 15# 拒否対象の国コード 16@country = ('KR', 'CN'); 17 18foreach $i (@country){ 19 $country{$i} = 1; 20} 21 22if(open(IN, "fetch -q -o - $url|")){ 23 # 拒否対象の国の場合は IP アドレスを保存 24 while(<IN>){ 25 if(/^apnic\|(..)\|ipv4\|(\d+.\d+.\d+.\d)\|(\d+)/){ 26 if($country{$1}){ 27 $table{inet_aton($2)} = $3; 28 } 29 } 30 } 31 close(IN); 32 33 # 取得した IP 割当リストから開始アドレスと個数を取得 34 foreach $net (sort keys %table){ 35 $addr = unpack('N', $net); 36 $num = $table{$net}; 37 while($num == $num[0] && ($addr ^ $addr[0]) == $num){ 38 shift @addr; 39 shift @num; 40 $addr &= ~$num; 41 $num <<= 1; 42 } 43 unshift(@addr, $addr); 44 unshift(@num, $num); 45 } 46 47 # IP アドレスと個数を CIDR 型式に変換して出力 48 while (@addr){ 49 for($num = pop(@num), $mask = 32; $num > 1; $num >>= 1, $mask--){} 50 $filt = inet_ntoa(pack('N', pop(@addr))) . "/$mask"; 51 # FreeBSD の ipfw(8) 向け出力 52 print "\${fwcmd} add $rule deny ip from $filt to any in recv \${wan}\n"; 53 # Linux の iptables(8) 向け出力 54 # print "\${iptables} -A INPUT -s $filt -j DROP\n"; 55 $rule++; 56 } 57} 58 590;
FreeBSD の場合は WAN 側のインタフェイス名が '${wan}' 変数に、 iptables コマンドの実行ファイル名が '${ipfw}' 変数に それぞれ格納されている事を前提としたスクリプトになっているので、 利用する際は適宜変更しながら使って下さい。



