at posts/single.html

Mac OS X で自動的に公衆無線LANへログインするツール (wifi_login) を作った

外出先でもインターネットが使える公衆無線LANサービスは便利なんだけど、認証のためにわざわざWebブラウザを立ち上げてIDとパスワードを入力するのが面倒だったりする。なので、自動ログインするツールを Ruby で作って、 GitHubRubyGems で公開した。

今のところ、docomoの公衆無線LANのみに対応している。

自動ログインツールを作った動機

Windowsであれば、IEEE802.1Xを設定することで自動ログオンが可能。そう。Windowsならね。

Windows® 7 : ネットワークの設定方法(自動ログイン機能) | サービス・機能 | NTTドコモ

【24】docomo Wi-Fiのエリア内に入ると、自動的に接続・ログインを開始します。

Mac OS XでもIEEE802.1Xを設定すればいい…と思っていたら、10.7 (Lion) 以降では追加のツールをダウンロードしないと設定できなくなっていた。

Macで楽しくお仕事: MacOSX 10.7 (Lion) での 802.1X 認証の設定

MacOSX 10.7 (Lion) での 802.1X 認証の設定は、MacOSX 10.6 (Snow Leopard) の時と大きく異なっており、MacOSX のシステム環境設定では行うことができなくなっています。 設定を行うには、まず「iPhone 構成ユーティリティ」をインストールし、Wi-Fi 構成プロファイルを作成し、その構成プロファイルを MacOSX にインストールします。

Macの設定なのに、「iPhone 構成ユーティリティ」という名前なのはどうしたものか。という訳で、自前でログオンツールを作ることにした。

使い方

gem をダウンロードすると wifi_login というコマンドが使えるようになる。

基本的には wifi_login installpit set docomo を実行すれば設定完了。詳しい使い方は GitHub のページ を参照。ここでは、ツールを作る上で調べた技術的なことをメモしておく。

自動ログインの仕組み

ログイン画面のHTMLを読んでいて気がついたんだけど、公衆無線LANの自動ログインはIEEE802.1Xだけじゃなく、 WISPr (Wireless Internet Service Provider roaming) という仕組みも使えるらしい。docomoも最近対応している。

au Wi-Fi SPOTにLinuxから接続できるようにしてみた - Dマイナー志向

実は今回のau Wi-Fi SPOTだけでなく、ソフトバンクのモバイルポイントなどもWISPrプロトコルに対応しています。しかしWISPr対応クライアント実装はあまり見つかりません。その理由は、ほとんどのホットスポットはWebブラウザを開くと自動的にユーザ情報入力フォームが開くようになっており、わざわざWISPrクライアントを実装する必要性がないんですね。ねるほどねー。

Docomo Wi-Fi のWISPr対応について - eggshell blue

先日、Docomo Wi-Fi に接続したところ、表記のとおりWISPrプロトコルに対応されていることが確認できました。 これで、国内携帯キャリアが提供する公衆無線LANサービスはすべてWISPrプロトコルに対応したことになります。

WISPr を使えばキャリアに関係なく汎用的に作れそうだったけど、仕組みがよく分からなかったため、泥臭く Mechanize をつかって実装しちゃった。エラー処理を除けばこんなにシンプルに書ける。

 url = 'https://wlan.m-zone.jp/wlan/portal.jsp'
 agent = Mechanize.new
 page = agent.get(url)
 form = page.form
 form.user = id
 form.password = password
 page = agent.submit(form)

無線LANへの接続を検知して自動的に起動させたい

ツールから自動的にIDとパスワードを入力できるようになったけど、無線LANに繋ぐたびにわざわざツールを実行していたら面倒なのは変わらない。そこで、 Mac OS X の launchd を使って、無線LANへの接続を検知して自動的にツールを起動するようにした。基本的なアイデアはこちらのページを参考にさせてもらった。

無線APによって自動でhostsを変える方法 - unknownplace.org

/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist を監視しそれが更新されたら /Users/typester/dev/scratch/wifi_switch/switch.pl を実行する。と言うことが実現できます。 この監視先のファイルは OSX の無線ネットワーク設定なので、これが更新されると言うことは無線通信の状態が変わったときと言うことを意味します。

APの情報は /System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -I と言うコマンドで取得可能なのでそれをパースすればオッケーです。

今回はバックグラウンドでログインするだけなので、LaunchDeamonではなくLaunchAgentとして登録すればよい。ユーザ単位なので、 $HOME/Library/LaunchAgents にplistファイルを生成する。 rbenv の環境で実行させるためにちょっと悩んだけど、「Macのlaunchdからrbenv環境のRubyスクリプトを起動する」の方法で解決した。

コマンドラインのインタフェースを用意したい

せっかくなので、 LaunchAgent の登録などもコマンドラインで提供したい。ちまちまと書いていくのは面倒だな…と思ってBundlerのソースを読んでいたら、 Thorライブラリ を見つけた。これを使えば簡単にコマンドラインのインタフェースを作れる。本当に便利。

Thor クラスを継承して、 WifiLogin::CLI クラスを作る。1メソッドが1つのコマンドに対応する。メソッドの前にdescで説明文を書く。

module WifiLogin
  class CLI < Thor
    desc "ssid", "Show current SSID"
    def ssid
      say "current SSID: #{WifiLogin.ssid}"
    end

    desc "login", "Login public wi-fi access point."
    def login
      success = WifiLogin.login
      say 'Login success!', Thor::Shell::Color::GREEN if success
    end
  end
end

あとは bin/wifi_login を作って、内部で WifiLogin::CLI.start と書くだけ。これで help コマンドも生成してくれる。

$ wifi_login help
Tasks:
  wifi_login help [TASK]  # Describe available tasks or one specific task
  wifi_login login        # Login public wi-fi access point.
  wifi_login ssid         # Show current SSID

さらにコマンドラインからファイルのコピーやテンプレートの展開をしたい

Thorが便利なのはコマンドラインインタフェースだけでなく、ファイルのコピー、テンプレートの展開など、よく使われるタスクを簡単に作れるライブラリ(Action)が提供されていること。 rails や bundle gem のアレみたいなの。

Actionを使うには、 Thor::Actions をインクルードする。

module WifiLogin
  class CLI < Thor
    include Thor::Actions

    desc "install", "Install trigger to your Mac OS X System (launchd)."
    def install
      empty_directory LOG_DIR
      template "templates/jp.machu.wifi_login.plist.tt", "#{LAUNCH_AGENT_FILE}"
      run "launchctl load #{LAUNCH_AGENT_FILE}"
   end
end
  • empty_directory: ディレクトリを作成
  • template: テンプレートを展開してファイルを作成
  • run: コマンドを実行

たった3行で実現できてしまった。すでにディレクトリやファイルが存在した場合にもエラーにならずにうまいことやってくれる。

$ wifi_login install
       exist  /Users/machu/Library/Logs/jp.machu.wifi_login
      create  /Users/machu/Library/LaunchAgents/jp.machu.wifi_login.plist
         run  launchctl load /Users/machu/Library/LaunchAgents/jp.machu.wifi_login.plist from "."

IDとパスワードを管理する

ログインするためのIDとパスワードは Pit というライブラリで管理することにした。

gemで公開する

作成したライブラリはgemで公開した。gemの作り方はBundlerを使ったGem作成メモ (自分用) - ただのにっき(2012-02-18)を参考に bundle gem を使うことにした。

ちょっと修正して、 rake install でローカルにインストールして試す、の繰り返し。キリがいいところで version.rb のバージョンをあげて rake relase で公開する。gemの公開だけでなくGitHubへのpushとタグ付けまでやってくれるので便利。

テストを書く

rspec で spec を書いた。「RSpec で TDiary::Plugin のテストを書く」で少し慣れていたので良かった。RSpec Kerry Buckley IPRUG, 4 January 2011のページがとても参考になる。

公開してみて

とりあえず自分が欲しくて作ってみたけど、こうやって gem にまとめるまでの過程を経験できたのは良かった。ちょこちょこ修正していこうと思う。

関連する日記