ConsulをDNSサーバとして使う方法
やりたいこと
「iPhoneからテスト用のWEBサーバにhttps接続したいけど、DNSサーバがない…WEBサーバにbindを入れるほどのことでもないし…ファイル1つで動くDNSサーバがあればな…」
というようなニッチな悩みに対する解決策を調べていると、ConsulのDNSインターフェースの説明ページがヒットしたので、WEBサーバにConsulをインストールしてWEB兼DNS(Consul)サーバ化することにしてみた。
初期設定
名前解決リクエストを53ポートで受けるように指定して、Consulが動作している端末以外への名前解決リクエストは8.8.8.8に任せる。
$ sudo mkdir /etc/consul.d $ sudo chown hoge:hoge /etc/consul.d cat << JSON >> /etc/consul.d/consul.json\n{\n "ports": {\n "dns": 53\n },\n "recursor": "8.8.8.8"\n}\nJSON $ sudo mkdir /tmp/consul $ sudo chown hoge:hoge /tmp/consul
起動
nodeとdomainを指定することでconsul1.node.kintoki.xyz
というコモンネームに対する名前解決リクエストを応答することが可能。
$ sudo ./consul agent -dev -server -domain=kintoki.xyz -client=0.0.0.0 \ -node=consul1 -data-dir=/tmp/consul -bind=192.168.10.224 \ -config-dir /etc/consul.d \ -config-file /etc/consul.d/consul.json & [1] 6598 Downloads$ ==> Starting Consul agent... ==> Starting Consul agent RPC... ==> Consul agent running! Node name: 'consul1' Datacenter: 'dc1' Server: true (bootstrap: false) Client Addr: 0.0.0.0 (HTTP: 8500, HTTPS: -1, DNS: 53, RPC: 8400) Cluster Addr: 192.168.10.224 (LAN: 8301, WAN: 8302) Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false Atlas: <disabled>
dnsオプション
-domain - By default, Consul responds to DNS queries in the "consul." domain. This flag can be used to change that domain. All queries in this domain are assumed to be handled by Consul and will not be recursively resolved. Configuration - Consul by HashiCorp
bindオプション
-bind - The address that should be bound to for internal cluster communications. This is an IP address that should be reachable by all other nodes in the cluster. By default, this is "0.0.0.0", meaning Consul will use the first available private IPv4 address. If you specify "[::]", Consul will use the first available public IPv6 address. Consul uses both TCP and UDP and the same port for both. If you have any firewalls, be sure to allow both protocols. Configuration - Consul by HashiCorp
clientオプション
-client - The address to which Consul will bind client interfaces, including the HTTP, DNS, and RPC servers. By default, this is "127.0.0.1", allowing only loopback connections. The RPC address is used by other Consul commands, such as consul members, in order to query a running Consul agent. Configuration - Consul by HashiCorp
config-fileオプション
-config-file - A configuration file to load. For more information on the format of this file, read the Configuration Files section. This option can be specified multiple times to load multiple configuration files. If it is specified multiple times, configuration files loaded later will merge with configuration files loaded earlier. During a config merge, single-value keys (string, int, bool) will simply have their values replaced while list types will be appended together. Configuration - Consul by HashiCorp
config-dirオプション
-config-dir - A directory of configuration files to load. Consul will load all files in this directory with the suffix ".json". The load order is alphabetical, and the the same merge routine is used as with the config-file option above. This option can be specified multiple times to load multiple directories. Sub-directories of the config directory are not loaded. For more information on the format of the configuration files, see the Configuration Files section. Configuration - Consul by HashiCorp
data-dirオプション
-data-dir - This flag provides a data directory for the agent to store state. This is required for all agents. The directory should be durable across reboots. This is especially critical for agents that are running in server mode as they must be able to persist cluster state. Additionally, the directory must support the use of filesystem locking, meaning some types of mounted folders (e.g. VirtualBox shared folders) may not be suitable. Configuration - Consul by HashiCorp
名前解決テスト(WEB兼DNSサーバ)
Consulサーバ上で名前解決テストを実施する。
$ dig @192.168.10.224 consul1.node.kintoki.xyz 2016/07/09 00:45:50 [DEBUG] dns: request for {consul1.node.kintoki.xyz. 1 1} (1.364404ms) from client 192.168.10.224:50229 (udp) ; <<>> DiG 9.8.3-P1 <<>> @192.168.10.224 consul1.node.kintoki.xyz ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24592 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;consul1.node.kintoki.xyz. IN A ;; ANSWER SECTION: consul1.node.kintoki.xyz. 0 IN A 192.168.10.224 ;; Query time: 4 msec ;; SERVER: 192.168.10.224#53(192.168.10.224) ;; WHEN: Sat Jul 9 00:45:50 2016 ;; MSG SIZE rcvd: 82
名前解決テスト(iPhoneからConsulサーバに接続)
ConsulサーバとiPhoneを同一NWに所属させ、iPhone側でhttp://consul1.node.kintoki.xyz:8000
を指定してCosulサーバ(WEB兼DNSサーバ)に接続することができるか確認する。iPhoneが使用するDNSサーバは忘れずConsulサーバに設定すること。hogefile
が表示されたら成功。
~$ mkdir hoge ~$ cd hoge hoge$ touch hogefile # pythonの簡易WEBサーバを起動 hoge$ python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ...
まとめ
ConsulはDNSサーバとして使える!iPhoneのhostsファイルを書き換えるという危ない技を使わなくて済んだ。 あとは証明書を作ってWEBサーバにインストールするだけだ。
Herokuアプリを無料でSSL/TLS化する
経緯
趣味で作っているRailsアプリ(ユーザ数1名)をそろそろ独自ドメイン対応させようかなと、情報収集しているとHerokuのSSLアドオンでSSL/TLS対応するには2千円/月の費用が発生することに気付きました。
HerokuをやめてさくらVPSにでも引っ越してLet's Encrypt
でも試してみようかな〜なんて思ってたらこんな記事を見つけてしまいました。
DNS機能やFirewall機能を兼ね備えたCDN「CloudFlare」経由でアクセスしちゃえば無料でSSL/TLS対応することができるよという記事です。試してみるしかないですね。
設定手順
1. Railsアプリの設定変更
config/environments/production.rbで強制SSL化させる。
config.force_ssl = true
コミットしてHerokuにプッシュ。
$ git push heroku master
2. Herokuのドメイン設定
お名前.com等で取得した独自ドメインをHerokuに登録。これをしとかないとHostヘッダチェックでリクエストが弾かれる。
$ heroku domains:add mydomain.net
3. cloudflareのレコード設定
事前にサインアップしていること。
- ドメイン情報スキャン お名前.com等で取得した独自ドメイン名を入力することでドメイン情報がスキャンされる。
- ルートドメインをCNAMEとして登録
zone apexなのにCNAME登録はできないでしょ?と思っていたらどうやら
CNAME Flattening
という機能が実装されており、問い合わせごとにCNAME情報を辿ってAレコードを応答してくれるらしい。
設定内容のサンプルは以下。
Type: CNAME Name: mydomain.net Content: appname.herokuapp.com
4. お名前.comのネームサーバ設定変更
CloudFlareの指示通りのネームサーバを設定する。NSレコードのTTLだけ待たないと反映されないかも。
5. CloudFlareのSSL設定
Full SSL
に設定するだけ。
以上で設定は完了。https://mydomain.net
にエラーなく接続できるようになった。
気づいたこと
接続経路は?
日本からCloudFlareを利用すると香港経由で接続するっぽい。Herokuアプリがスリープしてた時にたまたま表示されたエラー画面を見て気付いた。HerokuはUSリージョンとEUリージョンしか対応してないから香港経由での接続でもいいかな。
というかこのエラー画面はあまり表示してほしくないな。
SSLv3.0は無効になってる?
以下のサイトで確認したところSSLv2.0と3.0は無効になっていることがわかった。評価はA+。 www.ssllabs.com
さいごに
本当に無料でSSL/TLS化できてしまいました。しばらくこのまま様子を見つつCloudFlareの他の機能も試してみたいと思います。
RPMを理解する
1. はじめに
yumコマンド(例えばyum install httpd
)を実行するとパッケージがインストールされて、サービスコマンドを実行すると(例えばservice httpd start
)Webサーバが起動してApacheのデフォルトページが表示される。単純なコマンドでWebサーバが起動しちゃったけど、そこに至るまでの処理ってどうなってるんでしょうか。少し気になって調べてみると、どうやらRPMという仕組みが関係しているようです。「Fedora 22より、デフォルトのパッケージマネージャが変更されるらしい」という話もありますので、ひとつ下のレイヤーの勉強をして、来たる変化に備えておきたいところ。
2. RPMとは
- RPMとはパッケージ管理の仕組み
- インストールされたパッケージのデータベースを生成し、そこにそれぞれのバージョン情報が記録される。パッケージのインストール、アンインストール、アップデート、ソースRPMからはアプリケーションを再コンパイルすることも可能
- 個別のアプリケーションを管理単位とすることにより、アプリケーションのインストールとアンインストールを簡単に行うことができる
- RPMパッケージとは圧縮されたアーカイブファイルを指し、相互に関係するファイル、ドキュメント、設定情報の集合を含む
- アプリケーションではなく、設定ファイルを集約したパッケージも存在する(/root/.bashrcを含むrootfilesパッケージ等)。また、パッケージ名にnoarchという文字が含まれていれば、バイナリファイルを含まずスクリプトまたはドキュメントからのみ構成されている環境非依存のパッケージであると判断できる。複数のアプリケーションによって共用できるライブラリのみをまとめたパッケージも存在する
- Red Hat Package Managerを表していたが、Linuxの他のディストリビューションでも採用され始めたため、RPM Package Mangerという名称に変更された
- RPMの前身であるRPP(Red Hat Software Packages)では複数のアーキテクチャに対応することが難しかったため、RPMが開発された
3. RPMの設計思想
*使いやすいこと
*パッケージに焦点を合わせる
*パッケージをアップグレードできる
手動でアプリケーションをインストールした場合、アップグレード時に設定ファイルが上書きされ、カスタマイズしていた設定情報が失われる場合がある。一方RPMでアップグレードした場合は、カスタマイズ情報は保持される
*パッケージの依存関係をトラッキングする
パッケージ間の依存関係情報をトラッキングし、インストール時やアンインストールに警告を表示させる
*クエリを実行できる
インストールされたすべてのパッケージに関するデータベースがシステム上に作成され、各ファイルがどのパッケージに属するファイルか記録される。RPMは簡単にクエリできるようになっており、データベースに問い合わせてシステムにインストールされているアプリケーションを検索し、各ファイルがどのパッケージに属するか調べることができる
*検証
データベースに記録されたパッケージに関する情報をもとにアプリケーションに属するファイルに問題がないか(すべてのファイルのパーミッションが正しく設定されているか等)チェックし、改ざんされたり壊れたりしているファイルがないか調べることができる
*複数のアーキテクチャをサポート
*クリーンなソース
RPM(バイナリパッケージ版)とソースRPM(ソースパッケージ版)の2種類のパッケージを作成する。ソースRPMはオリジナルのソースコードに変更を加えることなく、クリーンなソースコードからパッケージを作成することが可能
4. RPMデータベース
- RPMデータベースは/var/lib/rpmに格納されている
- __db.からはじまる名前のファイルはRPMシステムが利用しているロックファイル。その他のファイルはBerkeleyDBフォーマットのデータベースとなっている
- RPMデータベースは注意深く扱う必要がある。 パッケージをアップグレード、インストール、アンインストールしたあとには、Packagesファイルをバックアップしておくといいかも
5. パッケージのインストール/アンインストール
パッケージのインストールはrpm -U
コマンドかrpm -i
コマンドを実行する。rpmコマンドは以下のステップでパッケージをインストールする。
- インストールすべきパッケージとその中身をチェックする(依存関係、パッケージ間の競合)
- インストール前のタスクを実行する
- 圧縮されているファイルを展開して、適切な場所に配置する
- RPMデータベースを更新する
なお、パッケージを一度に複数インストールする場合はrpmのコマンドラインに各ファイルをスペース区切りで列挙すればよい。インストール順序はrpmコマンドが自動判別してくれる。
カスタマイズされた設定ファイルの扱い
パッケージをインストール後、設定ファイルを編集すると、パッケージのアップグレード時に変更を加えた設定ファイルが維持される。(.rpmsaveという新しい拡張子に名前変更されたうえ、バックアップとして保存されることもある。また、.rpmnewという拡張子付きで新しい設定ファイルがインストールされることもある)
5.1. コマンド例(基本)
インストールされているパッケージ一覧を表示する
rpm -qa
パッケージに含まれるファイルの一覧を表示する
# -vオプションを加えるファイルに関する詳細情報が表示される rpm -qlpi <パッケージ名>.prm rpm -ql <パッケージ名>
パッケージの設定ファイル一覧を表示する
※設定ファイルを備えていないパッケージも存在する
# -vオプションを加えるファイルに関する詳細情報が表示される rpm -qc <パッケージ名>
パッケージ内のファイルの状態を表示する
以下、サポートしているファイル状態
- normal: ファイルがインストールされている
- not installed: ファイルがパッケージから削除されている
- replaced: ファイルが別のファイルに置き換えられた
rpm -qs <パッケージ名> rpm -qsf <ファイル名> # ファイルを指定してそのファイルを含むパッケージのコンフィグファイルのみの情報を表示 rpm -qcfs <ファイル名>
パッケージ内のスクリプトを表示する
ファイルのインストール/アンインストール時に実行されるスクリプト
rpm -q —scripts <パッケージ名>
パッケージのchangelogを表示する
※対応していないパッケージもある
rpm -q —changelog <パッケージ名>
パッケージの概要情報を表示する
rpm -qi <パッケージ名>
ファイルが属しているパッケージを見つける
rpm -qf <ファイル名>
依存関係を検証する(実際にはインストトールされず依存関係等のチェックのみを行うモード)
# インストールの検証 rpm -Uvh —test <パッケージ名>.rpm # アンインストールの検証 rpm -e —test <パッケージ名>
インストールディレクトリを変更する
※—root
で指定した仮想rootディレクトリにC標準ライブラリを入れておく必要がある
# /tmpを仮想rootディレクトリとして、RPMデータベースは/var/lib/rpmを利用しインストールする rpm -U --root /tmp —dpath /var/lib/rpm <パッケージ名>.rpm
古いバージョンのパッケージをインストールする
rpm -Uvh —oldpackage <パッケージ名>.rpm
/var/spool/packageにバックアップを残してアンインストールする
rpm -e —repackage <パッケージ名>
最近インストールしたパッケージを表示する
rpm -qa —last | head
5.2. コマンド例(クエリフォーマット)
パッケージ名のみ一覧表示
—qf
がクエリフォーマットオプション
# 結果はpfintf形式で表示されるため改行されない。 rpm -qa —qf “%{NAME}" # 改行したいならこう書く rpm -qa —qf “%{NAME}¥n"
表示幅の指定
20文字でフォーマットする
rpm -qa —qf “%-20{NAME} %-20{PLATFORM}¥n"
利用できるタグ一覧
- NAME: パッケージ名
- VERSION: バージョン情報
- RELEASE: リリース番号
- SUMMARY: パッケージの内容を1行に要約
- DESCRIPTION: パッケージを説明するテキスト
- BULDTIME: パッケージが構築された時刻
- SIZE: ペイロード内の通常ファイルのサイズ
- GROUP: パッケージのグループ(カテゴリ)名
- OS: パッケージの構築ターゲットとなるOS
- ARCH: アーキテクチャ(i386など)
- PREIN: インストール前のスクリプト
- POSTIN: インストール後のスクリプト
- PREUN: アンインストール前のスクリプト
- POSTUN: アンインストール後のスクリプト
- PLATFORM: プラットフォーム
# 以下のコマンドでタグ一覧を表示 rpm --querytags
依存情報の表示
$ rpm -qa --qf "[%-10{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\n]" postfix /bin/bash /bin/sh /sbin/chkconfig /sbin/service /usr/sbin/alternatives /usr/sbin/groupadd /usr/sbin/useradd config(postfix) = 2:2.6.6-6.el6_5 cyrus-sasl >= 2.1.10 libc.so.6()(64bit) libc.so.6(GLIBC_2.11)(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libc.so.6(GLIBC_2.3)(64bit) libc.so.6(GLIBC_2.3.2)(64bit) libc.so.6(GLIBC_2.3.4)(64bit) libc.so.6(GLIBC_2.4)(64bit) libc.so.6(GLIBC_2.7)(64bit) libcrypto.so.10()(64bit) libcrypto.so.10(OPENSSL_1.0.1)(64bit) libcrypto.so.10(OPENSSL_1.0.1_EC)(64bit) libcrypto.so.10(libcrypto.so.10)(64bit)
ファイル情報一覧の表示
$ rpm -q --qf "[%-15{NAME} %-36{FILENAMES} %{FILEMODES:perms}\n]" postfix postfix /etc/pam.d/smtp.postfix -rw-r--r-- postfix /etc/postfix drwxr-xr-x postfix /etc/postfix/access -rw-r--r-- postfix /etc/postfix/canonical -rw-r--r--
6. パッケージの検証
- S: ファイルサイズが異なる
- M: ファイルのモードが異なる
- 5: MD5チェックサムが異なる
- D: デバイスファイルのバージョンが異なる
- L: リンクのミスマッチ
- U: ファイルを所有しているユーザが異なる
- G: ファイルを所有しているグループが異なる
- T: ファイルの時刻(mtime)が異なる
$ rpm -V httpd ..5....T. c /etc/httpd/conf/httpd.conf ..?...... /usr/sbin/suexec # ServerTokens OSをProdに変更 $ vi /etc/httpd/conf/httpd.conf # 再度検証してみるとSフラグが立っていることがわかる $ rpm -V httpd S.5....T. c /etc/httpd/conf/httpd.conf ..?...... /usr/sbin/suexec # システム全体を検証する時は以下のコマンドを良く使う $ rpm -Va --nofiles
7. RPMデータベースのメンテナンス
RPMデータベースのバックアップ
# cd /var/lib # tar cvzf rpmdb.tar.gz ./rpm
RPMデータベース再構築
rpm —rebuilddb
RPMデータベースの再構築
rpm —initdb
8. パッケージの依存関係
依存関係には大きく以下の4つのタイプを定義できる
- 必要(Requires): パッケージが必要とする機能
- 提供(Provides):パッケージが他のパッケージに提供する機能
- 競合(Conflicts):インストールすれば他のパッケージと強豪する機能
- 廃止(Obsoletes):該当のパッケージによって陳腐化する機能
パッケージが必要とする機能を表示する
インストール済みパッケージに対しても実行可能(-pオプションは省くこと)
$ rpm -qp --requires /tmp/ruby-2.1.5-1.el6.x86_64.rpm /usr/bin/env /usr/bin/pkg-config /usr/bin/ruby ld-linux-x86-64.so.2()(64bit) ld-linux-x86-64.so.2(GLIBC_2.2.5)(64bit) libc.so.6()(64bit) libc.so.6(GLIBC_2.10)(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libc.so.6(GLIBC_2.3)(64bit) libc.so.6(GLIBC_2.3.4)(64bit)
パッケージが提供している機能を表示する
$ rpm -qp --provides /tmp/ruby-2.1.5-1.el6.x86_64.rpm big5.so()(64bit) bigdecimal.so()(64bit) bubblebabble.so()(64bit) callback.so()(64bit) chinese.so()(64bit) complex.so()(64bit) console.so()(64bit) continuation.so()(64bit)
競合しているパッケージを表示する
$ rpm -qp --conflicts /tmp/ruby-2.1.5-1.el6.x86_64.rpm
特定の機能を必要とするパッケージを表示する
[vagrant@localhost ~]$ rpm -q --whatrequires /bin/bash dkms-2.2.0.3-2.el6.noarch initscripts-9.03.46-1.el6.centos.1.x86_64 dracut-004-356.el6.noarch rsyslog-5.8.10-10.el6_6.x86_64 cronie-1.4.4-12.el6.x86_64 lvm2-2.02.111-2.el6_6.1.x86_64
特定の機能を提供するパッケージを調べる
$ rpm -q --whatprovides webserver httpd-2.2.15-39.el6.centos.x86_64 # 特定ファイルを提供しているパッケージを調べる時は以下を実行する rpm -qf /etc/httpd/conf/httpd.conf
9. トリガ
パッケージのインストール時、またはアンインストール時に実行されるスクリプトを確認することが可能
$ rpm -q --triggers iptables-ipv6 triggerpostun scriptlet (using /bin/sh) -- iptables-ipv6 < 1.4.7-7 ip6tables=`readlink /etc/alternatives/ip6tables.x86_64` if [ -z "$ip6tables" -o "$ip6tables" == "/sbin/ip6tables-1.4.7" ]; then /usr/sbin/alternatives --set ip6tables.x86_64 /sbin/ip6tables-1.4.7 fi
10. 参考

Red Hat RPM Guide (redhat PRESS)
- 作者: エリックフォスター・ジョンソン,Eric Foster‐Johnson,中川和夫,ヴァインカーブ
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2003/11
- メディア: 単行本
- クリック: 28回
- この商品を含むブログ (11件) を見る
spacewalkの仕組み
はじめに
spacewalkは構成管理サーバ的なもので、パッチ当てをスケジュールしたり設定ファイルの配信が可能です。クラサバ間の通信にはXMPPを使ってるようです。
(もちろんAnsibleとの連携(Dynamic Inventory)もバッチリですよ!)
んで、この記事は以下の内部的な仕組みを解説した公式サイトを適当に解釈した結果をまとめてます。
OsadHowTo – spacewalk
https://fedorahosted.org/spacewalk/wiki/OsadHowTo
Jabberについては以下のサイトが参考になりました。
XMPP(Jabberのプロトコル)技術メモ — ありえるえりあ http://dev.ariel-networks.com/column/tech/xmpp/view
構成
osad(クライアント)
Python製のクライアントサイドサービ ス。osa-dispatchaerからping応答およびrhn_checkコマンドを実行する。
osa-dispatcher(サーバ)
Python製のサーバサイドサービス。クライアント上で動作しているosadに向けてpingやrhn_check実行を命令する。
jabberd(クライアント/サーバ)
osadやosa-dispatcherはそれぞれの環境で動作するjabberd(XMPP(エックスエムピーピー、eXtensible Messaging and Presence Protocol)の実装)を介して通信を行う。サーバ-クライアント間の認証する。使用ポートは5222。
動作概要
osa-dispatcherはクライアントに対し定期的にpingを実行し、応答に遅延がないかを確認している。(ping応答はosadが動作しているクライアントからjabberdを介して行われる) ping応答を受け取ったosa-dispatcherは応答を返してきたクライアントを”online"状態としてマークする。ある程度以内に応答がなかった場合は”offline"状態としてマークする。
<通信概要図>
サーバ(osa-dispatcher<->jabberd)<->クライアント(jabberd<->osad)
osa-dispatcherはデータベース上で管理されている クライアントへの命令がないかを定期的に確認する。もし命令がある場合はjabber経由でosadにrhn_checkコマンドを実行するよう呼びかける。そこから命令を受けたosadはrhn_checkコマンドを実行することで処理を引き継ぐ。
RHN Proxyは上位で動作するjabberサーバに命令をフォワードする。
ファイル
osad
ソースコード
/usr/share/rhn/osad/
設定ファイル
- /etc/sysconfig/rhn/osad.conf
- /etc/sysconfig/rhn/up2date
実行ファイル
/usr/sbin/osad
ログファイル
/var/log/osad ※osad.confでlogレベルを引き上げないと何も出力されないので注意
osa-dispatcher
ソースコード
/usr/share/rhn/osad/
設定ファイル
/etc/rhn/rhn.conf ※"OSA Configuration"セクション
# grep osa /etc/rhn/rhn.conf osa-dispatcher.jabber_server = yourspacewalk.example.org osa-dispatcher.osa_ssl_cert = /var/www/html/pub/RHN-ORG-TRUSTED-SSL-CERT osa-dispatcher.jabber_username = rhn-dispatcher-sat osa-dispatcher.jabber_password = rhn-dispatcher-a44631
osa-dispatcher.debugでロギングレベルを設定することが可能
実行ファイル
/usr/sbin/osa-dispatcher
ログファイル
/var/log/rhn/osa-dispatcher.log
jabberd
コンポーネント
jabberを実行すると以下の6つのプログラムが起動する
/usr/bin/jabberd
/usr/bin/c2s(/etc/jabberd/c2s.xml)
クライアントとサーバ間の通信を制御する/usr/bin/sm(/etc/jabberd/sm.xml)
jabberコンポーネントとjabber router間のセッションを管理する/usr/bin/router(/etc/jabberd/router.xml)
どのコンポーネントにメッセージを連携すべきかを管理する/usr/bin/resolver(/etc/jabberd/resolver.xml)
ホスト名を解決する。s2s向け。
暗号化
c2sとsmを通過した際に通信が暗号化されるよう構成されている。証明書および鍵は以下のファイルに記載されている。
/etc/jabberd/server.pem
ログファイル
基本的jabberdコンポーネントは以下のファイルにロギングされる。
/var/log/messages
組み込みデータベース
jabberはBerkeley databaseを使用している。
/var/lib/jabberd/
トラブルシューティング
名前解決
クライアントのhostsファイルにサーバのFQDNを予め設定しておくこと(jabberの仕様上必要)
ntp設定
osadの認証は時間に依存している。クライアントとサーバ間の時間の差が120秒以内である必要でなければ認証に失敗する。これはセキュリティを考慮した結果の仕様である。
ネットワークの問題
大抵の場合はFWのタイムアウトによって発生する。
- FW等でosad用の通信を許可されていない場合
- コネクションが切断されたがクライアントが再接続処理を行わない場合
netstat -an | grep 5222
の実行結果が"ESTABLISHED"であってもサーバに接続できていない場合
基本的にはkeepaliveのため2分間隔でホワイトスペースのみのデータを送信しているので この仕様をベースに各種設定を少し修正するとよい。以下の設定を行うことで5分以内にスケジュールしたイベントが実行されるようになった。
サーバ側
- タイムアウト設定
sed -i 's/<interval>.*/<interval>120<\/interval>/' /etc/jabberd/*.xml* sed -i 's/<keepalive>.*/<keepalive>120<\/keepalive>/' /etc/jabberd/*.xml* sed -i 's/<idle>.*/<idle>200<\/idle>/' /etc/jabberd/*.xml*
- サービスを再起動
/usr/sbin/spacewalk-service restart
- cronによるjabber DB関連の初期化設定
SAMPLE # Restart jabber everyday @6am 00 06 * * * /sbin/service jabberd stop ; /sbin/service osa-dispatcher stop ; rm -Rf /var/lib/jabberd/db/* ; /sbin/service jabberd start ; /sbin/service osa-dispatcher start
手動実行する場合。
/sbin/service jabberd stop ; /sbin/service osa-dispatcher stop ; rm -Rf /var/lib/jabberd/db/* ; /sbin/service jabberd start ; /sbin/service osa-dispatcher start
クライアント側
- cronによるosad再起動&rhn_check実行設定 rhnsdをオフにする。
SAMPLE # Check in with spacewalk and restart osad every ~4 hours 0 */3 * * * sleep `expr ${RANDOM:0:4} / 2` ; /sbin/service osad restart ; /usr/sbin/rhn_check ; /usr/sbin/rhn-profile-sync
- osadの再起動
/sbin/service osad restart
詳細については以下参照。 https://fedorahosted.org/spacewalk/wiki/JabberAndOSAD
Berkeley Database
Berkeley Databaseが何らかの理由でロックされると、ping応答はするが、スケジュールされたイベントに気付くことができなくなる。 そういった場合は一度jabberdを再起動することで /var/lib/jabberd/内の不要なファイルが削除される。
cuckooとJubatusによるマルウェア解析
3月29日(土)、セキュリティ勉強会に参加するため、香川の高松に行ってきました。一番の目的はメインセッションであるFFRI 村上純一さんの「ビッグデータ分析によるマルウェア検知・分析への挑戦」です。真面目な勉強会の参加は初めてだったので緊張しました。
セキュリティうどんのご紹介
セキュリティうどんの主旨はこーんな感じ。なんかとっても真面目そうなイメージを受けました。運営スタッフさんは「セキュリティ系勉強会一のゆるい勉強会」だと言ってましたけどねw
今回の勉強会のテーマはこちら。
おやつタイムって何だ、うどんでも食べるのか?なんて思っていましたが、普通に美味しい洋菓子が振る舞われました。参加費無料でおやつ付き…勉強会っていいもんですね。
ビッグデータ分析によるマルウェア検知・分析への挑戦
FFRI 村上さんによると、cukoo(かっこう)でファイルの挙動を確認した結果を、jubatus(ゆばたす)で機械学習させマルウェア検知を行い、その検知精度を調査したとのこと。
cuckooとは?
1つのファイルを解析する度に新しくVM(仮想環境)を起動させ、ファイル実行時の挙動をログとして出力することが可能です。サンドボックスや動的解析、ビヘイビアとも呼ばれています。
複数のアンチウィルスエンジンでの解析を行うクラウドサービス「VirusTotal」との連携も可能です。
# Pythonで実装されてるので、Pythonの知識があれば色々楽しめるかもしれません。
自分もcukooには1年ほど前に手を出したことがありますが、得られるのはファイルの実行過程の情報のみで、それがマルウェアであるかどうかの条件は自分で考えなければいけないため、上手く活用することができませんでした。
その解決策のひとつとして村上さんが考えたのが、jubatusによる機械学習でした。
jubatusとは?
jubatusを利用すると大規模データ(今回だとcukooの解析結果)をリアルタイムに解析することができる、”大規模分散リアルタイム機械学習基盤”です。
こちらは実際に触ったことがないので詳しい解説はできませんが、イメージとしてはHadoop/Mahoutのリアルタイム版ですかね?和製ソフトなので日本語情報を得やすいというのもjubatusの特徴だと思います。
機会学習の技術を応用することでアンチスパムメールやECサイトのレコメンド機能、将棋ソフトのAIなどを実現させることができます。
身近な例だと、iPhoneアプリのAkinatorをイメージしてもらえばいいかと思います。「性別:男性」「名前:漢字」「年齢:25才以上」「癖:倍返しする」等の情報を学習させ、特定の人物を定義することができます。同じようにマルウェアも定義できそうですよね。
機械学習とは?
- 人間がなんとなくできていること(ファジーな物事)を機械にもやらせようという意味。
- 医療業界では昔から使われている技術らしい。
- 様々なテーマ、手法を包含した総称である。
なぜ機械学習なのか?
既存のアンチウィルスソフトじゃダメなの?高価/高学習コストなサンドボックスに投資する必要あるの?という疑問が湧いてくるかと思います。その理由を説明するにはまず、マルウェアのトレンドについて知る必要があります。
機械学習による検知テスト
様々なアプローチがあるみたいですが今回は以下の感じでテストしたそうです。
1. サンプルの取得
まずは学習させるためのデータ(マルウェア/正常なソフトウェア)を一定数準備する必要があります。どの程度のデータを集めるかは以下のようなポイントを考慮するといいらしいです。
- 信頼区間と呼ばれる概念
- どれくらいの誤差があってもよいか
- 数が多ければ多いほど誤差は減る
- 偏りがないよう、長い期間/様々な場所から少しずつデータを取得する(たぶん重要!)
今回のテストでは、データを15000個(学習用5000個/テスト10000個)準備したそうです。
2. cukooによる解析
cuckooではAPIコールの傾向についての情報を抽出します。多くのAPIコールは1秒で完了するため、5秒以上経っても完了しないものについてはテストの対象外としたそうです。
3. jubatusによる学習/解析
APIコールを3-gramモデルによって解析し、正常/異常のラベル(条件)を登録。(N-gramのNが3なのは、3-gramの精度が一番よかったからとのこと)
「VirtualProtectEX(難読化を解除するAPIコール)というAPIコールが3回続いたらマルウェアの動き」というようにラベルを登録して学習モデルを作成して行きます。
コマンドライン上から、ラベル情報(正常系/異常系)を元に学習モデルをダンプできるようです。→人間がモデルのダンプを確認して新ロジックを開発!
4. クラスタリングによる分類
マルウェアをクラスタリング(グルーピング)し、ユニークデータを抽出(亜種や新種である可能性を見極める)することで、優先して解析すべき検体の選定を行え、解析の効率化が可能となります。
適用に伴う弊害
- 学習データ/評価データの傾向が著しく異なる場合、分類精度が著しく低下する。
- 正常系のソフトウェアは類似度の分布が広いため、学習データ/評価データの精度が違う可能性あり。
要するに、こういうことでしょうか?
- ユニークなデータを学習データやテストデータにする割合が増える→誤検知率(FPR)が上がる
- 亜種情報が増える→正しい検知率(TPR)が上がる
- 質のいいデータを集めるべき
感想
- 未知のマルウェア/カスタムマルウェアを機械学習によって判別するのは難しい。今回村上さんにお話いただいた特徴とは別のファクターを考える必要あり。
- 学習するためのサンプルデータ集めが死ぬほど大変そう。
- この手の話はいたちごっこだから低コストでサンドボックスを自作しようなんて甘い考えは捨てて、専門家集団に任せる。自分たちが考えなければいけないのはマルウェア発見時の運用フロー。(とは言うもののマルウェアと判断することがきる人員の育成/確保もできた方がいいかも)
- 個人で遊ぶ分にはjubatus面白そう!
勉強会の運営スタッフのみなさま、各セッションで登壇いただいたみなさま、本当にありがとうございました。ドーナツ美味しかったです!
おまけ
香川と言ったらうどん。高松市のうどん屋ってなかなか探すのに苦労しますよね。
セキュリティうどんの公式サイトに掲載されているうどんマップが参考になりました。
より大きな地図で うどんマップ を表示
http://sec-udon.jpn.org/doku.php?id=%E3%83%AA%E3%83%B3%E3%82%AF:%E3%81%86%E3%81%A9%E3%82%93%E3%83%9E%E3%83%83%E3%83%97
ArduinoとRaspberry Piで作るスマート家電(寝返り扇風機)(3)
前回の記事の続きです。もうちょっとで完成なので頑張りましょう。はやく快適な夏の夜を手に入れようぜ!
Arduinoで扇風機の電源切り替え
Raspberry Piから寝返り通知を受け取ったらサーボモータが動くようにしましょう。下記サンプルコードを新しいスケッチに貼付けてArduinoに書き込みましょう。(通信内容自体は全く見ていない残念なコードです。)※XBeeシールドを接続したままではスケッチの書き込みに失敗するので注意が必要です。
#include <Servo.h> #define RUNTIME 10 // ここで扇風機の稼働時間(秒)調節できます Servo myservo; unsigned long latest_time; // 現座時刻を保存する変数 unsigned long int_time; // 扇風機を稼働し始めた時間を保存する変数 double passed_time; // 経過時間を保存する変数 int mode=0; // 0で大気状態、1で稼働状態を表す // // 初期化関数 // void setup(){ myservo.attach(9); // 9番のアナログ出力ピンを使用してサーボモータを操作する myservo.write(5); // サーボモータのプロペラの初期角度 Serial.begin(9600); // ログをシリアルモニタに表示させる } // // メインループ // void loop(){ // Raspberry Piからシリアルパケットを受け取った場合の処理 if(Serial.available() > 0){ mode = 1; // 稼働状態に切り替え int_time = millis(); // 現在時刻を取得 myservo.write(50); // プロペラの角度を変更(扇風機のスイッチをオンにする) Serial.read(); } // 扇風機が稼働中の場合の処理 if(mode == 1){ latest_time = millis(); // 現在時刻を取得 passed_time = (double)(latest_time - int_time) / 1000.0; // 経過時間を算出 Serial.print(passed_time); Serial.print("\n"); // 一定時間が経過したら扇風機の電源をオフにする if(passed_time >= RUNTIME) { mode = 0; // 待機状態にに切り替える myservo.write(5); // プロペラの角度を変更する(扇風機の電源をオフにする) } } }
下記サイトを参考に接続してください。ジャンパワイヤが3本必要です。
糞の足しにもならないArduino講座:その3
- スイッチ付きコンセントとサーボモータの接着
こんな感じで泥臭く接着しました。AndroidのUSBアダプタを犠牲にして高さを調節しています。
これでArduino側の準備は完了です。
- WEBサーバーを起動
起動したらスマホからWEBサーバ(http://192.168.64.42:11337)に接続して、スマホを振って振動を与えてみましょう。サーボモータのプロペラが回転して扇風機が回り始めるはずです。あと、実際にベッドで使用する際の注意なのですが、スマホのディスプレイ電源の自動オフ機能は切っておいてください。
root@raspberrypi:/var/node/negasen# node app.js info - socket.io started
最初の記事でお見せした感じで動きましたか?
振り返り
2013年度の目標が「電子工作デビュー」だったので、なんとか目標達成のボーダーは超えられたんじゃないかと思います。それにしてもグーグル先生は偉大ですね。Arduinoに関する情報は少ないものの、欲しい情報はほとんど無料で手に入ってしまうなんて。ほんと、すごい時代に生まれてきてしまったなあ、と今更ながら感動しております。
失敗談的なもの
実はサーボモータを使う前に、ソリッドステート・リレーという技術を使って電源のON/OFF機能を実現しようとしていました。(参考:連載(26)Arduinoで何でも制御 いろんなArduinoがある(8))電源ケーブルのカバーを剥いて、はんだ付けなんかにも初挑戦したのですが、結果的に失敗に終わってしまいました。得られたものは ”感電しかけたという経験” のみでした。次に電子工作を挑戦する機械があったらはんだ付けに再チャレンジしてみようと思います。
ArduinoとRaspberry Piで作るスマート家電(寝返り扇風機)(2)

前回の続き記事です。Raspberry PIでWEBサーバを構築して、ソケット通信を試してみましょう。しれっと途中でXBeeの設定なんかも挟んでいますが、気にしないでください。
OSのインストール
1. 自分の作業用PCの環境に合った「SD Formatter」をダウンロードする。
2. 「SD Formatter」でOSをインストールするSDカードをフォーマットする。
3. こちら からOSのインストールツール「NOOBS」をダウンロードする。圧縮ファイルになっているので予め解凍しておく。
4. 解凍してできたファイルをすべて選択し、SDカードにコピーする。
5. Raspberry Pi に挿し込む。
6. HDMIケーブル(対向には電源オン状態のディスプレイを接続)、LANケーブル、USBキーボード、を Raspberry Pi に接続してから、microUSB電源ケーブル(対向には2AのUSBアダプタを接続)を接続する。(Raspberry Pi には電源ボタンが存在しないので、電源ケーブルうを接続した瞬間に電源がオンになります。)
7. インストールメニュー画面が表示されたら、Raspbianを選択し、インストールを開始する。
8. キーボードとタイムゾーンの設定
[Configure keyboard]>[Generic 105-key(Intl) PC]>[Other]>[Japanese]>[Japanese]>[The default for the keyboard layout]>[No compose key]>[No]
[change_timezone]>[Asia][Tokyo]
初期設定
- rootユーザのパスワードを設定し、スイッチする
pi@raspberrypi ~ $ sudo passwd root 新しいUNIXパスワードを入力してください: 新しいUNIX パスワードを再入力してください: passwd: パスワードは正しく更新されました
pi@raspberrypi ~ $ su - パスワード: root@raspberrypi:~#
- ネットワークの設定
自分の環境ではデフォルト設定のDHCPを使用しています。一応、設定ファイルの中身を記載しておきます。
root@raspberrypi:~# cat /etc/network/interfaces auto lo iface lo inet loopback allow-hotplug eth0 iface eth0 inet dhcp allow-hotplug wlan0 # iface wlan0 inet manual iface wlan0 inet dhcp # wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf iface default inet d
- アップデート
root@raspberrypi:~# apt-get update
- vimのインストール
Raspbianには初期でvimがインストールされています。しかし、これの実体は最低限の機能のみを持ったvim-tinyであり、今一つ使い勝手が良くありません。なので、普通のvimをインストールしましょう。
root@raspberrypi:~# apt-get install vim
- 作業用PCからSSHログイン
特に不要な作業ですが、慣れ親しんだ環境で作業したい場合や、ログを編集しながら作業をしたい場合にこの手順を実施してください。ifconfigを実行し、表示されたinetアドレスに作業用PCにインストールしたTera Term(macならターミナル)などからSSHしましょう。
root@raspberrypi:~# ifconfig eth0 Link encap:イーサネット ハードウェアアドレス ********* inetアドレス:192.168.64.42 ブロードキャスト:192.168.64.255 マスク:255.255.255.0
Node.jsの設定
- Node.jsのインストール
root@raspberrypi:~# git clone https://github.com/creationix/nvm.git ~/.nvm root@raspberrypi:~# source ~/.nvm/nvm.sh root@raspberrypi:~# nvm install v0.10.4 root@raspberrypi:~# node -v v0.10.4 root@raspberrypi:~# npm -v 1.2.18 root@raspberrypi:~# nvm alias default 0.10.4 default -> 0.10.4 (-> v0.10.4) root@raspberrypi:~# echo 'source ~/.nvm/nvm.sh' >> .bashrc
- 作業用ディレクトリの作成
root@raspberrypi:~# mkdir -p /var/node/negasen root@raspberrypi:~# cd /var/node/negasen
- socket.ioのインストール
Node.js⇔他デバイス間でのソケット通信を可能にするライブラリです。
root@raspberrypi:/var/node/negasen# npm install -g socket.io
- node-serialportのインストール
Node.jsでシリアルポートを制御できるようにしたライブラリです。
root@raspberrypi:/var/node/negasen# npm install serialport
XBee連携
- 初期設定
下記サイトにて、準備していた2個のXBeeモジュールがお互いを認識し、通信が行えるように初期設定をする。※WindowsのPCが必要になります。
XBeeをはじめてみよう(ZB編)
WEBアプリ作成
- Node.jsのメイン処理部分を「app.js」というファイル名で作成
root@raspberrypi:/var/node/negasen# vi app.js root@raspberrypi:/var/node/negasen# cat app.js var http = require("http"); var socketIO = require("socket.io"); var fs = require("fs"); // クライアントからアクセスが会った場合に表示させるページを指定 var server = http.createServer(function (req, res) { res.writeHead(200, {"Content-Type":"text/html"}); var output = fs.readFileSync("./negasen.html", "utf-8"); res.end(output); }); server.listen(11337); // シリアルポートの設定 var serialport = require('serialport'); var SerialPort = serialport.SerialPort; var sp = new SerialPort('/dev/ttyUSB0', { parser: serialport.parsers.readline('\r\n') }); var io = socketIO.listen(server); // 接続中の処理 io.sockets.on("connection", function (socket) { console.log("connected"); // スマホからWebSocketのパケットが来た時の処理 socket.on('value', function(data) { console.log(data); // 書き込みデータをつくって var xbeewrite = ["ON", Math.pow(2,data.value)-1]; // XBeeに送信 sp.write(xbeewrite); }); // 取得させて加速度をブロードキャストしてブラウザに表示させる socket.on("accel", function(accel) { socket.broadcast.emit("accel", accel); }); });
- WEBアプリの表示部分を「negasen.html」というファイル名で作成
ソケット通信関連のコードは下記記事を参考にしています。
[node] socket.IOとHTML5 Canvasを用いた手書きチャットアプリを作ってみた
root@raspberrypi:/var/node/negasen# vi negasen.html root@raspberrypi:/var/node/negasen# cat negasen.html <html> <head> <meta charset="utf-8"> <title>Negaeri Senpuuki</title> </head> <body> <!-- Header --> <h1 class="title">Negaeri Senpuuki</h1> <!-- Main --> <script src="/socket.io/socket.io.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script> <script type="text/javascript"> window.addEventListener("load", function () { // サーバーサイドのsocket.IOに接続する // 接続出来たら、サーバー側のコンソールにconnected!と表示させる var socket = io.connect("/"); var $id = function(id) { return document.getElementById(id); } window.addEventListener('devicemotion', function(e){ // 重力加速度(3軸) var gravity = e.accelerationIncludingGravity; $id("gravity").innerHTML = gravity.x + ', ' + gravity.y + ', ' + gravity.z; // 加速度(3軸) var accele = e.acceleration; $id("accele").innerHTML = accele.x + ', ' + accele.y + ', ' + accele.z; // 回転加速度 var rotation = e.rotationRate; $id("rotation").innerHTML = rotation.alpha + ', ' + rotation.beta + ', ' + rotation.gamma; socket.emit("accel", {gravity:gravity, accele:accele, rotation:rotation}); // 寝返りを打った場合(加速度センサーで取得した値gしきい値を超えた場合)のみArduinoに通信を行う // 評価に使用する加速度やしきい値は調整の必要があるかも if (gravity.x >= 2 || gravity.y <= -2) { socket.emit("value", "P1"); } }); socket.on("accel", function (accel) { // 重力加速度(3軸) var gravity = accel.gravity; $id("gravity").innerHTML = gravity.x + ', ' + gravity.y + ', ' + gravity.z; // 加速度(3軸) var accele = accel.accele; $id("accele").innerHTML = accele.x + ', ' + accele.y + ', ' + accele.z; // 回転加速度 var rotation = accel.rotation; $id("rotation").innerHTML = rotation.alpha + ', ' + rotation.beta + ', ' + rotation.gamma; }); }, false); </script> 重力加速度(3軸)<br> <div id="gravity"></div> 加速度(3軸)<br> <div id="accele"></div> 回転加速度<br> <div id="rotation"></div> </body> </html>
- 実行
下記コマンドを実行して、Wi-Fiルータに接続済みのスマホからWEBサーバに接続(http://192.168.64.42:11337)してみましょう。ソケット通信の確認用にPCのブラウザからもWEBサーバにアクセスしておくといいかもしれません。
※IPアドレス部分は自分が構築したサーバのものに置き換えてください。
root@raspberrypi:/var/node/negasen# node app.js info - socket.io started
うまく動けばの↓のような動作をします。iPad miniで試してみたのですが、加速度センサーがついてるデバイスなら何でもOKみたいです。
ちなみに、Raspberry Pi はこんな感じで衣装棚の上で動いてます。コンパクトでいいですねえ。
次回はいよいよ、Arduinoをいじってサーボモータを動かします。