privoxyとiptablesで透過型HTTPプロクシ

privoxy正規表現ベースのアクセス制限や強力なフィルタ機能のあるHTTPプロクシだ。iptablesと組み合わせると透過型プロクシとして設定することもできる。

今回は透過型プロクシを使って、特定ホストに対してのみHTTPリクエストが可能なように設定してみる。この例では内部ネットワークが 192.168.0.0/16 で、その中のPCでゲートウェイとして設定されているのが透過型プロクシを処理するマシンとなる。

/etc/privoxy/config

privoxyの待ち受けアドレスとポート番号を適当に設定する。

listen-address 0.0.0.0:1499

一般的なHTTPプロクシは「GET http://host/path...」というリクエストを受け取るのだが、透過型プロクシでは「GET /path..(改行)Host: host」のような、通常のリクエストを受け取る。

privoxyはaccept-intercepted-requestsを有効にすると、通常のHTTPリクエストに対しても Host ヘッダの情報を利用して中継処理を行うようになる。

accept-intercepted-requests 1

iptablesの設定

iptables の nat テーブルの PREROUTING チェインに、内部ネットワークから外部のポート80へのリクエストをマシン内部にリダイレクトするよう設定する。

iptables -t nat -A PREROUTING -s 192.168.0.0/16 -p tcp --dport 80 -j REDIRECT --to-ports 1499 

アクセス制限

次のようなアクションファイルを書いて、whitelist.actionという名前で保存する。

# 全てのURLをブロックする
{ +block }
/

# 除外するURL
{ -block }
.debian.org
.recaptcha.net

/etc/privoxy/config でアクションファイルを参照するよう設定する。

actionsfile whitelist.action

AIDEで改竄検知

改竄検知は頻繁に偽陽性(false positive)に煩わされることになるイヤな管理タスクだが、セキュリティ上避けて通れない。

ファイルアクセスをフックしてリアルタイム保護してくれるようなサービスがあるともっといいのだが、Linuxではあまり発達していないようだ。SELinuxを使うとrootに対してでもファイルアクセスを制限できるのだが、現状では仮想PCと相性が悪い。

今回はGPLライセンスの改竄検知ソフトウェアAIDE (Advanced intrusion detection environment)を使って改竄検知を仕込んでみる。

Debian, Ubuntu のaideはかなりdebianizeされてて気持ち悪い。…というか読み切れないので既存のパターン設定は全て捨ててしまった。

/etc/aide.conf

設定例を公開するのもアレなのでポイントだけ。

  • パスのパターンは全部/で始まっていないと怒られるようだ
  • 行頭の=で「枝刈りはしないけど特定のフォルダだけチェック種別を変える」ことができる。ただしパターン末尾に$をつけないと枝も同じ正規表現にマッチしてしまう

aideinit

/etc/aide.conf を書いたら aideinit でデータベースを初期化する。

/etc/default/aide

レポートの送信先メールアドレスなどを設定する。

MAILTO=tateisu@gmail.com

/etc/cron.daily/aide

このスクリプトが定期的に呼ばれる。手動で起動してもよい。

動作確認

実際に動かしてみると、ディレクトリのctime,mtimeが変化しただけなどの偽陽性(false positive)で数回は設定を見直すことになる。

あと困ったのが、手動でaideinitを起動した場合と /etc/cron.daily/aide とで結果が違う。仕方なくaideinitでスキャンした結果は使わないことにした。

DBリセットは次の手順で行う

  • 設定ファイルを変更していたら事前にaideinitを呼ぶ
  • /etc/cron.daily/aide ; mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db ; /etc/cron.daily/aide

Xenを使って Ubuntu 11.10 (oneiric) の上で Debian 6.0 (squeeze) を動かす

(ホスト側) 必要なパッケージ

  • lvm2 2.02.66-4ubuntu3
  • libc6-xen 2.13-20ubuntu5
  • libxenstore3.0 4.1.1-2ubuntu4.1
  • xen-docs-4.1 4.1.1-2ubuntu4.1
  • xen-hypervisor-4.1-i386 4.1.1-2ubuntu4.1
  • xen-tools 4.2.1-1
  • xen-utils-4.1 4.1.1-2ubuntu4.1
  • xen-utils-common 4.1.0~rc6-1ubuntu1
  • xenstore-utils 4.1.1-2ubuntu4.1
  • ほか、その依存関係

ubuntuのカーネルイメージはデフォルトでxenのdom0に使えるので、カーネルそのものは特に追加する必要はない。
今回はホスト側のカーネルは linux-image-3.0.0-16-generic-pae をそのまま使っている。

(ホスト側) LVMのボリュームグループを作成しておく

最初はループバックで動かして後からLVMのブロックデバイスを追加しよう…としたら、domUinitrdにブロックデバイス用のモジュールがうまく入らなかった。最初からLVMを前提にしてxen-create-imageを行う方が確実なようだ。

  • partedかfdiskで適当にパーティションを確保して
  • 再起動はしたくないので partprobe でパーティション情報を再ロードして
  • 「pvcreate パーティションのデバイス名」でLVMの物理ボリュームに仕立てて
  • 「vgcreate -s 128m vg1 パーティションのデバイス名」でLVMのボリュームグループを作成する。

ここで作ったボリュームグループに対して、後からxen-create-imageが自動的に論理ボリュームの切り出しを行うことになる。

(ホスト側) grubの設定

  • パッケージを入れた後に update-grub する
  • その後に grep menuentry /boot/grub/grub.cfg すると 「~ and XEN」のついた起動選択肢があるのを確認できる
  • その選択肢が自動的に選ばれるように、mv /etc/grub.d/ にある 10_linux か 20_linux_xen のどちらかをリネームして順序を入れ替える
  • 再度 update-grub してからリブート
  • xm list でdom0が表示されていればOK

dom0の最低メモリ量を調整するために、何回かここで再起動するかもしれない。
また、OSのループバックデバイスの数を変更するのに /etc/modprobe.d/loopback.conf を作って options loop max_loop=16 …等と書く必要があるかもしれない。

(ホスト側) xen-create-image

domU用の、OS入りのディスクイメージを生成する。
今回は --mirror=http://ftp.jp.debian.org/debian --dist=squeeze を指定することで、debianを入れたディスクイメージを作成する。
なんでoneiric じゃないのかと言われそうだが、その理由は /usr/share/debootstrap/scripts/ の下に oneiric が存在していなかったからだ。debianのsqueeze は提供されているので、ちゃんとdebian用のディスクイメージを作れる。おそらくOSのリリース時期と、それを利用したdebootstrap設定の提供時期が原因だと思われる。
…というのはどうでもよくて、本当は単にdebianの方が好みだからだ。選択できるのであればホストOSにもdebianを選んでいただろう。

/etc/xen-tools/xen-tools.conf に書くか、もしくはxen-create-imageのコマンドラインオプションにこんな感じで指定する。どの項目をどちらに書くかはお好みで。

# /etc/xen-tools/xen-tools.conf
arch = i386   # i386 or amd64
lvm = vg1     # イメージが出力されるLVMボリュームグループ
install-method = debootstrap
gateway    = 192.168.2.21
netmask    = 255.255.255.0
broadcast  = 192.168.2.255
nameserver = 210.224.163.4
mirror = http://ftp.us.debian.org/debian # パッケージを取ってくるリポジトリ
ext3_options     = noatime,nodiratime,errors=remount-ro
ext2_options     = noatime,nodiratime,errors=remount-ro
xfs_options      = defaults
reiserfs_options = defaults
btrfs_options    = defaults

# command to create image
xen-create-image --force --hostname=guest1 --ip=192.168.2.2 --pygrub --dist=squeeze --mirror=http://ftp.jp.debian.org/debian 

うまくいけばrootのパスワードが出力されて作成が完了する。

(ホスト側) ネットワークの設定

今回の環境ではホストOSにはプライベートIPアドレスが全く割り当てられていないので、ブリッジモードを使うわけにはいかない。NATも後で外す予定なので、ルータモードでネットワークを設定してみた。

ルータもードを選択する場合、/etc/xen/xend-config.sxp で

(network-script network-route)
(vif-script     vif-route)

が呼ばれるようにするだけではなく、/etc/xen/scripts/vif-route にも手を入れた方が便利だ。vif-routeスクリプトは仮想NICのホストPC側のアドレスに、ホストPCのeth0と同じアドレスを設定してしまうのだが、そうするよりは、仮想NICのゲストOS側のアドレスと同じネットワーク内のアドレスを割り当てた方が色々と捗る。

私が取った方法は、ゲストOS側のアドレスの末尾に文字列の1を連結するというものだった。

# main_ip=$(dom0_ip) 
main_ip="${ip}1"

この変更によって、もしゲストOSに設定したIPアドレスが192.168.2.2なら、その仮想NICのホストPC側の端点は 192.168.2.21で初期化されることになる。なんとも原始的だが、今回はここを洗練させる必要は特にない。

(ホスト側) マスカレードの設定

ゲストOSから外向きの通信が行えるようにするには、ホストOSに次のような設定を行う。

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

送信先をフィルタするためにNATではなくプロクシを使うケースもあるが、ここでは説明しない。

マスカレードの設定(B)

今回はjailの代わりに仮想PCを使うのが目的なので、ゲストから外向きの通信を制限したい。
ホスト側に適当なプロクシを入れて、送信先をフィルタすることで賄う。
ただしDNSに限っては引けるようにする。

iptables -t nat -A POSTROUTING -p tcp --dport 53 -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING -p udp --dport 53 -o eth0 -j MASQUERADE

ゲストPCを起動

先ほど作成したディスクイメージにつけたhostname がguest1だったので、/etc/xen/guest1.cfgが作成されているはずだ。
xm create guest1.cfg -c でゲストPCを起動して、そのコンソールを表示する。
なお、ゲストPCを起動したままコンソールをデタッチするにはCTRL+]を押す。

(ゲスト側) ネットワーク設定

ゲストOSの/etc/network/interfaceを編集する。この時点ではエディタはnanoかviの二択だ。

auto eth0
iface eth0 inet static
 address 192.168.2.2
 gateway 192.168.2.21
 netmask 255.255.255.0
 broadcast 192.168.2.255
 dns-nameservers 192.168.2.21

編集が終わったらゲストOS側で /etc/init.d/networking restart を実行してネットワーク設定を更新する。
仮想NICのゲストOS側のアドレスと同じネットワーク内のアドレスが仮想NICのホストOS側に割り当てていた場合は、これでゲートウェイが自動的にルーティングに設定されるはずだ。
適当な外部のサーバにpingを打ってみて、もしネームサーバが正しく設定されていない場合は手動で/etc/resolv.conf を編集する。
pingと名前解決さえできれば後は全く普通のdebianだ。

Ubuntuの遠隔アップグレード

そのサーバのOSに入れてもらったのは Ubuntu Linux 10.04 で、2年近く古いバージョンだった。でも大丈夫、Debianやその派生のディストリビューションはパッケージシステムがしっかりしているので、遠隔操作でも比較的容易にアップグレードが可能だ… とはいえレンタルサーバのOSをsshで遠隔アップグレードという作業は失敗した時のリスクが大きいので、仕事ならやりたくない部類に入る。まあ今回は仕事ではないし、大事なデータは初期前に全てバックアップ済みだ。丁度いい機会なので試してみることにした。

DebianUbuntuのメジャーアップグレードは、隣接する次のバージョンに対してのみ可能だ。途中を飛ばすことはできない。今回は最初の状態が Ubuntu 10.04 lucid で、その次が Ubuntu 10.10 maverick 、その次が Ubuntu 11.04 natty 、そして次の Ubuntu 11.10 oneiric が現在の安定版になる。

3回のメジャーアップグレードを遠隔操作で行って、失敗したら業者への再初期化依頼からやり直し。恐ろしい。

アップグレードガイドを見てみると、作業そのものは3回とも全く同じ手順が書かれている。

Ubuntuサーバーのネットワークアップグレード(推奨)
まだインストールしていなければ、update-manager-coreをインストールします:
sudo apt-get install update-manager-core
/etc/update-manager/release-upgradesを編集し、Prompt=normalを設定します
アップグレードツールを起動します:
sudo do-release-upgrade
画面に表示される手順に従ってください。 

なんともお手軽そうに見えるが、実際には各回のアップグレードの前に以下のような準備を行っている。

  • apt-get update
  • apt-get dist-upgrade
  • deborphanを使った不要パッケージの除去
  • apt-get clean
  • HDD全体のファイルをバックアップ
  • 空き容量の確認

maverick (10.10)

grubを入れるパーティションを選択するダイアログが表示される。ドライブ全体に相当するデバイス名を選択する。
以下のパッケージが自動的に削除される。

 Remove: libdns64 libntfs-3g75 
 Remove (was auto installed) libdirectfb-1.2-0 libgsf-1-114 
  libgsf-1-common libibus1 libts-0.0-0 libxcb-render-util0 
  linux-headers-2.6.32-38 linux-headers-2.6.32-38-generic-pae tsconf 

natty (11.04)

以下のパッケージが自動的に削除される。

 Remove: libdrm-nouveau1 
 Remove (was auto installed) libgirepository1.0-1 gir1.0-glib-2.0 

コンソールの言語を選ぶダイアログが表示されたが、遠隔操作なので実際のモニタにどのようなコンソールが表示されるのかさっぱり分からない。適当に選択したが、問題は特に出ていないようだ。

oneiric (11.10)

以下のパッケージが自動的に削除される。

 Remove: console-terminus 

キーボードのモデルを選ぶダイアログが表示されたが、遠隔操作なので実際のキーボードがどのようなモデルなのかさっぱり分からない。適当に選択したが、問題は特に出ていないようだ。
initramfs-tools が何かエラーを出していたのでパッケージを再設定してみたら

# dpkg-reconfigure initramfs-tools
/usr/sbin/dpkg-reconfigure: initramfs-tools is broken or not fully installed

という表示だった。 /boot にある古いカーネルのファイルを削除してから再設定すると解決した。

# cd /boot
# rm *2.6.32*
# rm *2.6.35*
# dpkg --configure -a

最後にremoveされたパッケージを全てpurgeする。

dpkg -l |awk '/^rc/ {print $2}' |xargs dpkg --purge

アップグレード結果

意外なほど問題がない。RAID管理に使う3dmdのWebUIにアクセスできなくなった。ただし状態の確認だけならtw_cliコマンドでも一応は可能だ。

レンタルサーバのOSを再初期化

rootつきのレンタルサーバを借りてるんだけど、割と情けない理由でOSを再初期化することになった。

  • 2012年2月16日22:24 業者さんのWebサイトに問い合わせを出した
  • 2012年2月17日3:28 業者さんから返事が返ってきた
  • 2012年2月17日6:09 書式をあわせてOS再インストール依頼を出した。作業時期はとくに指定せず。
  • 2012年2月17日10:30 業者さんから電話があった
  • 2012年2月17日11:06 サーバがシャットダウンした
  • 2012年2月17日13:39 作業完了報告のメールがきた

問い合わせを出してから12時間かかってない。閑散期とはいえ、これは迅速なんじゃないか。

 

Androidアプリから画面サイズを取得する方法

Androidアプリから画面サイズを取得する方法が、2.xと3.x以降で変わっていたのでメモしておきます。

実例

まずはテストアプリのスクリーンショットから。


表記の説明
  • layout root : View階層のルートのサイズ
  • DisplayMetrics : Context#getResources().getDisplayMetrics() で取得したサイズ
  • getDefaultDisplay : Activity#getWindowManager().getDefaultDisplay() で取得したサイズ
  • getDefaultDisplay raw : 3.x 以降で使える隠しメソッド、 Display#getRawWidth() で取得したサイズ(後述)
  • wallpaper: WallpaperManager.getInstance(activity).getDesiredMinimumWidth() で取得したサイズ

説明

Google Nexus で動かしてるので画面サイズは720x1280の筈なのですが、DisplayMetrics と getDefaultDisplay は両方共、端末の画像サイズと異なる数値を示しています。ホームボタン等を表示するナビゲーションバーの分を差し引いたサイズしか取得できません。2.xにはナビゲーションバーが存在しないため、互換性が考慮されたのだと思います。

画面全体のサイズを取得するには隠しメソッドを使う必要があります。なんでコレが非公開なのか…。

Display display = getWindowManager().getDefaultDisplay();
int raw_w = -1;
int raw_h = -1;
try{
	raw_w = (Integer)Display.class.getMethod("getRawWidth").invoke(display);
}catch(Throwable ex){}
try{
	raw_h = (Integer)Display.class.getMethod("getRawHeight").invoke(display);
}catch(Throwable ex){}

// 画面の向きによって、w,h が入れ替わっている場合があるようなので補正する
int orientation = getResources().getConfiguration().orientation;
if( orientation == Configuration.ORIENTATION_PORTRAIT ){
	if( rw > rh ){ int tmp = rw; rw = rh; rh = tmp; }
}else if( orientation == Configuration.ORIENTATION_LANDSCAPE ){{
	if( rw < rh ){ int tmp = rw; rw = rh; rh = tmp; }
}

3.xのシステムバーは非表示にすることができなかったのですが、4.xのナビゲーションバーは非表示にすることができます。View#setSystemUiVisibility() や View#setOnSystemUiVisibilityChangeListener() などのメソッドがあります。ただし、ナビゲーションバーを隠し続けたままユーザとのインタラクションを行うことはできません。画面をタップすると再度ナビゲーションバーが表示されます。

で、ナビゲーションバーを隠すとUIの再レイアウトが行われるのですが、「DisplayMetricsで取得できるサイズよりView階層のルートのサイズが大きくなる」という現象が発生します。これはひどい

一時的にでもナビゲーションバーを隠すことを想定したアプリでは、DisplayMetricsを信用してはいけない場合があるようです。フルスクリーンアプリを4.xに対応させる場合には気をつけた方がいいかもしれません。

Windowsから多段SSH

踏み台をいくつか超えてサーバに接続した時のメモ。

クライアント

ck3の使い方はzipファイル中のドキュメントで。
とりあえず色とフォントだけ変えれば普通にviやtopやlessが使えるみたい。

cygwin にはsshrsync を入れておく。

サーバ

  • 各踏み台にncコマンドが入っていること。centos ならyum install nc で入ると思う
  • 可能なら公開鍵認証できるようにしておく

設定ファイル

  • 2段の踏み台の後に目的のサーバにsshするための ~/.ssh/config の例
  • 起動例: ssh target-1-name
  • 起動例: rsync -e ssh *.pl target-1-name:/path/to/copy/
Host fumidai-1-name
	User user-name
	IdentityFile ~/.ssh/key-file
	TCPKeepAlive yes

Host fumidai-2-name
	User user-name
	IdentityFile ~/.ssh/key-file
	ProxyCommand  ssh fumidai-1-name nc -w 3600 (fumidai-2のプライベートIPアドレス) 22
	TCPKeepAlive yes

Host target-1-name
	User user-name
	IdentityFile ~/.ssh/key-file
	ProxyCommand ssh fumidai-2-name nc -w 3600 (target-1のプライベートIPアドレス) 22
	TCPKeepAlive yes
  • tail -f などする場合、 nc コマンドの -w オプションは長めに設定すること。最後にreadしてから指定病数が経過すると切れる。
  • ホストの名前部分は実在するホストでなくてもよい, nc コマンドのアドレスのところに好きなIPアドレスを書ける

SFTP

rsyncできるだけマシとはいえ、GUIなSFTPクライアントが使いたくなるが
どうもここまでやってくれるGUIなSFTPクライアントはないらしい。