エンディングはスタンド・バイ・ミーで宜しく頼む
常に冷静沈着、無表情、チート忍術で相棒のケン一氏のテンションを上げたり下げたりする伊賀の忍者ハットリくん。
普段の彼の姿からは想像もつかないが、ハットリくんが涙を流す回があるのをご存知だろうか。
アニメ第187話「忍法虫変化?!の巻」である。
ある日ケン一が学校から帰ると、テレビのクイズ番組に当選したことを知らせるハガキが届く。家族が勝手に応募していたのだ。
優勝賞品はヨーロッパ旅行とあって、お父さんは大張り切り。ケン一にクイズの猛特訓を施すが、人一倍のあがり症とあってケン一は本番前にハットリくんに「緊張をほぐす忍術を授けてくれ」と懇願する。
ハットリくんは「拙者がキリギリスに变化してついてゆくから安心するように」とケン一を諭す。果たしてハットリくんはキリギリスに变化したが、実際にはただのキリギリスを虫かごに入れて渡して自分は隠れ、ケン一を安心させただけなのであった。
特訓の甲斐とハットリくんの加護(と思い込んでいる)あってケン一は順調に正解を重ね、ヨーロッパ旅行にあと一歩まで迫る。
ところがテレビ収録のあまりの照明の熱にキリギリスが瀕死の状態に陥っていることに気付いたケン一は狼狽し、目前にある栄光をすべて放棄して泣きながらキリギリスを介抱することを選ぶ。
傍観していたハットリくんも「ケン一氏、そこまで拙者のことを…」と感涙する。そして、このままキリギリスが死んでは二度とケン一の前に姿を現すことができないと悟ったハットリくんは伊賀の里に伝わる秘伝の薬を使ってキリギリスを蘇生させ、元気になったキリギリスが飛び跳ねた瞬間煙幕を張ってキリギリスと入れ替わる。「いやあ、ご心配をかけたでござる…」
真の友情は伊賀忍者の覆面すらも湿らせるのでござった、ニンニン。
夢
「芸能人やからってかしこまらんでええからね☺️」
部屋に入るとチュートリアルの徳井じゃない方に着席を促された。
「気にせんでええよ、おれもITとか勉強して行きたいなと思てたとこやし☺️」
部屋にはチュートリアルの徳井じゃない方しかいない。
「ほんで、なに?本書いてんの?Androidの、テスト?
うーん、ええんちゃうかなと思うけどね俺は。
ほんで、なに?下世話なハナシやけどどのぐらい売れんの?
千?千かー。うーん。
まあ、アレやん?書くことが大事いうかね。ほら名刺代わりにもなるし」
名刺代わりなぁ。
それからチュートリアルの徳井じゃない方は、芸人の根性を見せるために僕の目の前で本物の中華鍋を使ってアッツアツの八宝菜を炒めはじめ、直接素手で掴んで白菜をムシャムシャ食べ始めた。こっち!めっちゃこっち見てるから!
「まあITやろ?わかるわかる、ロンブー淳にも言うとくし☺️」
という夢を見たよ。
「Androidテスト全書」アーリーアクセス公開と査読のお願い
いま書いている「Androidテスト全書」は初稿がほぼ出揃い、アーリーアクセスが始まりました。僕の担当分では1, 2章をお読みいただけます。
お時間のある方は是非ともフィードバックください。少し時間を置いて読み直してみると、既に自分でも構成のまずい部分や言葉足らずなところが見えてきて忸怩たる思いです。ただ、ここから逃げずに真摯に向き合わないと本は良くなりません。どんな細かいところでも、感じた違和感でも良いので教えていただけると嬉しいです。
それから個人的には3章が僕の担当分の山場でして既に数人の友人に査読をお願いしたのですが、こちらももし僕の知人で興味のある方は是非査読していただけると嬉しいです。
- 非同期処理のテスト
- DBのテスト
- RxJavaのテスト
- 多層アーキテクチャ(MVP)のテスト
- テストのないプロジェクトにテストを導入する
あたりを書いていて識者の意見を強く求めています。公開前原稿をお渡しする関係で、こちらに関しては知り合い限定となりますが、もしTwitter, Facebook, LinkedIn等でつながっている僕の知り合いでご協力いただける方はこそっと教えてください。
新規事業をひとりで作るノウハウ
生存報告も兼ねて。
カリフォルニアに来てもう半年ぐらい経った感覚ですが、実はまだ4ヶ月ほどでした。非常に多くの素敵な方々との出会いがあり、妻も僕も子供もこの皆さまの助けがあってどうにか生きております。どう感謝してよいか言葉にできないほどです。
さて、ビジネス上の僕のミッションは次の3つです。
- 主に投資や連携目的の交渉(の技術面のサポート)
- 日本との連携
- 新規事業の開発
どれもなかなか難しいです。会ってアポぐらいなら応じてくれる会社も多いですが、投資や連携といってもバブル崩壊以後経済成長できていない我が国はもはや「商習慣だけめんどくさいのに今やカネも持ってないから相手にしてられない連中」というのは肌で感じます。ご存知の通り、サンフランシスコ・ベイエリアはIT企業会社員が年収5000万円もらうような場所です。なかなか同じ規模感で会話するのが難しいレベルに達しています。
こみこみという噂のNetflixのPERMデータを見ても、ほんの2年前と比べても、かなり高騰してる気がする。(僕の給料はRSU考えるとむしろ下がっているが。。) pic.twitter.com/9t2nVdIjrn
— Takafumi Yonekura (@tyonekura) August 2, 2018
さて、クヨクヨもしていられないので、僕はプログラマの本分たるプログラミングによって少しでも生きた証を残すより他ありません。渡米以来自分の持ち時間の30-40%ぐらいを使って新規事業のプライベートアルファを公開するに至りました。対象ユーザが弊社内記者ということもあり、現時点でみなさまにお使いいただけないのは残念でしょうがないのですが、たったひとりでもサービスを公開まで持っていけるというのは我々にとって明るいニュースなので書いておこうと思います。
TL; DR いま新規事業やるならFirebase1択
今回自分が作ったのはウェブサービスですが、とにかくFirebaseが強いです。僕がこれから起業するなら、まずはFirebaseで作って運良く流行りそうならお金がかかる部分だけ書き換えるみたいなアプローチを間違いなく採るでしょう。今回使った技術スタックは次のとおりです。
- TypeScript
- Cloud Functions
- Cloud Firestore
- Firebase Authentication
- Google APIs Node.js Client
- Cloud Natural Language
TypeScript
後述するCloud FunctionsがNodeランタイムで動くのでJavaScriptは好むと好まざるとにかかわらず使わざるをえませんでした。僕は強い静的型付けが好きなのと、FunctionsにTypeScriptの例がたまたま載ってたのでTypeScriptを選びました。Flowを選ばなかったのは「店頭に並んでなかった」ぐらいのことで深い理由はありません。
まったく初めてでしたが次の2冊を読んで各30分の計60分でとりあえず書けるようになったのでおすすめです。そもそもC言語の子孫の言語なんてどれも青森弁と山形弁ぐらいの違いしかありません。
比較的快適に開発していますが、関数オーバーロードがさながらC言語のプロトタイプ宣言であるところや、これらで引数に取るインタフェース型がtype erasureによりinstanceofが使えないことから少し持って回ったようなハックが必要な点などいくつかまだ慣れないところはありますが、許容範囲というところです。
tsc watchは文句なく素晴らしく、息をするようにトランスパイルしてくれます。JSをまったく意識することがありません。
ユニットテスト
ユニットテストはchai, mocha, ts-nodeを使っています。このエントリを真似しました。
Unit testing node applications with TypeScript — using mocha and chai
async/awaitを多様しているのでプラスしてchai-as-promisedを入れています。次のように使います。
import * as chai from "chai"; import { expect } from 'chai'; import * as chaiAsPromised from "chai-as-promised"; import 'mocha'; import { NLP } from '../nlp'; before(() => { chai.use(chaiAsPromised); chai.should(); }); describe('NLP', () => { const nlp = new NLP(); it('should return [Amazon Alexa]', async () => { const keyword = "Amazon Alexa の取材"; const list: string[] = await nlp.analyzeEntities(keyword, defaultFilter); expect(list).is.not.empty; }); });
ts-nodeでテストケースを個別に実行する方法がよくわからんかったので
{ "name": "functions", "scripts": { "testall": "mocha -r ts-node/register src/**/*.spec.ts", "test": "mocha -r ts-node/register src/spec/$spec.spec.ts" } }
みたいにして全体実行はnpm run testall, 個別実行はspec=[クラス名] npm run testみたいにしています。
もっといい方法があったら是非コメントください。
namespaceがイマイチ使いこなせない
なにぶん全てひとりでやっており誰もコードレビューしてくれないので経験者からすると眉をひそめるような作法で書いている可能性が高いです。
たとえばnamespaceはKotlinのpackageの感覚でディレクトリを掘ってファイルも分けていますが、次のようにfoo.tsとbar.tsを別ファイルに分けて同じ名前空間の下にぶら下げた場合、
// my_service/foo.ts export namespace MyService { export class Foo {} } // my_service/bar.ts export namespace MyService { export class Bar {} }
利用する側で名前空間がバッティングしてるとimport * as aliasするしかなくてなんか不格好です。これはこういうもんなんでしょうか🤔
たぶんTSの作法がある気がするのでTS Wayをどなたか教えてください。
// import {MyService.Foo} from './my_service/foo' no good!! // import {MyService.Bar} from './my_service/bar' no good!! import * as foo from './my_service/foo' import * as bar from './my_service/bar' export class Baz { qux() { const hoge: foo.MyService.Foo = new foo.MyService.Foo() const fuga: bar.MyService.Bar = new bar.MyService.Bar() } }
Cloud Functions
いわゆるサーバレスにファンクション単位でデプロイできるやつです。これはとにかく素晴らしいです。
export const foo = functions.https.onRequest(async (request, response) => { try { const token: Credentials = await readToken(request.query.account) const resp = doSomething(token) response.send(resp); } catch (error) { sendError(response, error) } });
こんなの書いておくだけで設定したエンドポイントに対応するファンクションが起動します。
HTTPトリガの他にバックエンドで各種イベントトリガに応答して処理を行うことができます。後述の通りPub/Subも使えます。
これの何がいいって、フレームワークだのミドルウェアだのつまらない知識をためなくても一筆書きでとりあえずサービスを提供できるんですよね。
サーバレスというと弊社ではAWS Lambdaを使っていますが僕はCloud Functions 1択でした。それはFirebaseとの連携性です。FirebaseのリアルタイムDBであるFirestoreのドキュメントへのイベントを検知してFunctionをトリガできるのは便利です。他にも認証トリガなんてのもあってアツイです。
Cron Job
単独でcron jobはできませんが、これはGAEと組み合わせればできます。次のエントリに詳しいです。
端的にいうとGAEの持つCron機能をPub/Subで購読するというものです。GAEを一切使ったことがなくてもこのエントリの通りにすれば'hourly-tick'だの'minutely-tick'だのに反応するファンクションを書いておけばトリガされます。
export const batch = functions.pubsub.topic('hourly-tick').onPublish(async (event) => { // do whatever you like });
Local Emulation
ファンクションはデプロイする前にローカルでいくらでも試せます。
Run functions locally | Firebase
リンクにほとんど書いてあるので特に補足はないのですが、firebase serve でローカルにまるっきりfirebase hostingとcloud funcionsのローカル版が立ち上げるのでcurlなりブラウザなりで試すことができます。
firebase functions:shellとかするとTCP/IPではなく対話的シェルのような感じでファンクションごとにエミュレートができます。ここでFirestoreの読み書きもテストできます。
個人的には前者で実際にFirestoreにつなぎに行ってInstrumentedにテストできるので後者はほとんど使ってませんが便利な使い方を今後発見するかもしれません。
それから、OAuth2のリダイレクトとかをテストするときにLocal Emulator環境なのかデプロイ後だったのか区別する必要があり、これはちょっと簡単にわからなかったので環境変数のようなやつを使いました。
Environment configuration | Firebase
firebase functions:config:set my_service.is_local=true
みたいにして設定するとサーバに直接設定されるんですが、ファンクションのディレクトリに.runtimeconfig.jsonという形式で置いておいて適宜中身を書き換えるとローカルエミュレータではその値が使われます。アクセスするのはローカルもデプロイ後も同様です。
firebase functions:config:get > .runtimeconfig.json
const IS_LOCAL: boolean = JSON.parse(functions.config().my_service.is_local); const HOST_NAME = IS_LOCAL ? `http://localhost:5001/${process.env.GCLOUD_PROJECT}/us-central1` : `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com`;
Cloud Firestore
これはドキュメントベースのNoSQLなんですが、JSON状のデータ構造のあるパスを購読しておくとそこに対する読み/書き/削除/更新を全部通知してくれます。
オフライン対応してる、というか、利用者はすべてローカルコピーを真として読み書きする作りになっており、「オフラインになった!」という状態が存在しないと考えることができます。
結果、安全にデータの読み書きができます。
オンラインになるとこのローカルコピーは勝手にサーバのマスターデータともいうべきものと同期をかけます。同期後はそれが前述の更新イベントとして飛んでくるだけというわけです。
ここに敢えて貼りませんが、僕は2014-5年からFirebaseユーザでその頃はFirebaseといえばこのリアルタイムデータベース(いまのFirestoreはその後継)しかなく、僕はそれの連載記事を持っていたぐらい愛好者でした。
で、新規事業にあえてこのリアルタイムDBを使う必要もないんですが、別に使わない理由もなくて、割と雑になんでもデータを突っ込んでいます。ここも高くなったら置き換えるぐらいの気持ちです。早すぎる最適化は不要です。
Firebase Authentication
これは認証を楽ちんにしてくれるライブラリです。
Get Started with Firebase Authentication on Websites | Firebase
これもほとんどコメントの必要もないほど充実してるんですけど、認証時にFirebase内で一意なユーザ識別子を持って、あとからGoogleだのGitHubだの別の認証プロバイダで認証したときにそれらを全部ひもづけることができるので、複数の認証プロバイダに対応予定なら激つよです。
一点、この方法だとリフレッシュトークンがどうやらもらえない(リフレッシュ自体はできる)っぽくって、リフレッシュトークンがないと裏でCron Jobでトークンを更新しつつ他のAPIを叩くみたいなのができないので、僕は結局後述するGoogleのOAuthライブラリで手認証するのと組み合わせています。
Google APIs Node.js Client
これがとにかく素晴らしい。Google謹製のNodeライブラリ群ですけど、
- TypeScriptで書かれていて型情報がそのまま利用できるものがほとんど
- 非同期処理はほとんど余すところなくPromiseを返すようになっており、こちらが努力ゼロでasync/awaitできる
もう説明の必要もないですね。async/awaitできると同期的に待ち合わせるように非同期のコードが書けるし、他言語で同様の仕組みをもったプログラマが参入しやすいです。プログラムのバグと苦しみは非同期処理に根ざすものが非常に多いですからね。
一点、Listをmapしながら全てのasyncの完了を待ち合わせるような処理は書き方をすぐ忘れるのでメモしときます。
const tokens: [string, Credentials][] = await readTokens() const resultAll = await Promise.all( tokens.map(async token => { const identifier = token[0] const credential = token[1] return result = await doSomethingWithToken(credential) }) )
あとは複数の非同期処理を一気にdispatchしてzipper関数で待ち合わせてまとめるzipみたいなのはちょっとまだ見つけてません。
まあそのものズバリのRxJSってのもあるしなんとでもなりそうです。
まとめ
「ガチガチにロックインされてそうだけど大丈夫?」的な質問を飲み屋の会話(僕は一滴も飲めませんが)レベルでされたことがあったんですが、それが何か?という感じです。
言語、フレームワーク、思想とプログラマは色んなものにロックインされています。価値検証段階でそれらが何かにべったり依存していることは取るに足らないことだと思います。そもそも僕の稼働時間は僕一人が活動時間の3-4割で実質ひと月ぐらいで書いたので、本当にイケるとなったらいくらでも書き換えたらいいです。
最後にこの開発を開始して僕の人件費を仮にゼロと考えた場合の総コストは…!
ジャーン!開発期間中0ドル、サービスインして本日までの合計は77セントでした!ありがとうございます!
フォーチュンクッキーは恋をしない
ジョジョのサブタイトル風に。
中華料理屋でもらえるフォーチュンクッキーマジでいらないんだけど捨てるほどではない。で、ほっとくとパンダエクスプレスヘビーユーザーの私の机の上には一生食われないフォーチュンクッキーがうず高く積み上がってそのうちカリン塔に届きそうである。
これ何かに似てるなと思ったら、サービス開発時にユーザのことをよく考えずに"良かれと思って"機能を付け足しまくった挙げ句、捨てるには惜しいからそのまま残した結果、本当に必要な機能がウォーリーをさがせになるあの状況だ。要らないものは最初から付けないのがベストなのである。
サービス開発でもっとも重要なことは「足す」ことではなく「引く」勇気だ。従ってこれから僕は要るかどうか疑わしい自称・付加価値のことをフォーチュンクッキー・フィーチャーと呼称して、積極的に排除するキャンペーンを実施する。みんな積極的に使ってくれよな!
ドアは正面についてるとは限らない
を読んで「波が来たときにそれに乗れる準備をしておく」という部分に非常に共感したのでチャンスに対する自分の考えを残しておく。
ドアは正面についてるとは限らない
自分はこの事実に気付くまでに30年を要したが、物事を達成するために正面から玄関をノックしてお邪魔していく必要はない。自分がバカ正直に入り口の行列に並んでいる横で、風呂場の窓からひょいっと中に入っていく人を横目で眺めていた。昔は「あれは行儀が悪い。自分は絶対に待機列で耐える」と思っていたけど最近は違う。人生は短い。
これは架空の話だが、ある男がどうしても米国に行きたかったとする。会社でその枠を掴み取れるのは1名だとする。それに選ばれるにはどうするかという話だ。くじ引きで当選するのを待つ?いやいや。
自分が米国に行きたいことを1on1の度に、雑談の度に伝えるのだ。それだけじゃなくて、これこれしかじかのスキルを持つ自分を送り込むのがどれだけ会社にとって自然なことであり、将来的な利益になるのかストーリーを作るのだ。
エンジニアなら分かるだろう、
こんなん理由がないとブロックするだろう。
で、後はそれがどのくらい真剣で深刻で他に選択肢がないことなのか伝えるのだ。たとえば米国企業の内定がもうあるとか、ね。これはすべてフィクションだけど、そういうことだ。
で、これだけだと自分の都合だけを考えたクソ野郎なのでもちろんそんな幸運は降ってこない。やっぱり自分が何かを求めるからには、それと同等以上のことを相手に与えなければならない。普段から自分の責務を果たし、相手が困っているときにはそれを助け、言い方は悪いけど「貸し」を作っておくといざ自分がどうしても何かを相手にしてもらいたい時に利子つきで返ってくることもある。
で、冒頭の話に戻るんだけど、やっぱりチャンスというのは与えられるものじゃなくて自分からわしづかみするもんだと思うんだよね。みささんが
やっぱり波が来たときにちゃんとそれに乗れる準備をしとかないといけないと思うんですよね。
といみじくも仰ってるように、やはり帆を張ってるからこそ追い風は吹くのである。
ここまで書いたところで寝室のドアから愛娘がうなされる声が聞えるので添い寝してきます…じゃあの
拠点間VPNで日米それぞれのネット環境を活用する
様々な理由で日本経由でインターネットに接続したいケースがある*1。
ここでは日本(Bフレッツ)と米国(Xfinity)間でVPN接続し、ユーザは接続するWiFi SSIDを変えることで透過的にそれぞれの経路でインターネットに出ていけるようにすることをゴールとする。
なお、自分がたまたまYAMAHA RTX1100を2つ持っていたという理由だけでこれらを日米に配して拠点間VPNを構築したが、RTX1200以降のモデルは2つの拠点間で同一セグメントを構築できるらしい*2ので、もう少し簡略化できる可能性がある。また、最近はEdgeRouterXというルータが低価格かつ高性能らしいので、そういったものを使ったほうが近代的かも知れない。
ただ、基本的なルーティングの考え方は製品に依らず普遍的なので、同じようなことを検討している方には参考になるかも知れない。
Xfinity設定
Xfinity(Comcast)は本質的にケーブルテレビ網を使ったインターネット接続なので、WAN側アドレスはDHCPで割り振られる。そこを押さえておけば設定は容易い。
ComcastモデムのBridge Modeを有効化
Comcastのモデム(兼ルータ+WiFiスポット)はデフォルトで 10.0.0.0/24
のLANなので 10.0.0.1
でモデムの管理画面に入る。認証は初期状態で admin/password
Bridge Mode
を Enable
へ。
Turn Bridge Mode On and Off on Your Wireless Gateway
この時点でモデムはグローバルIPを失う(当然)ので注意。
ルータをとりあえずネット接続
前述の通りWAN側アドレスをDHCPでもらう。ここではLAN2ポートをモデムのLAN1*3に接続する。
LAN1の4ポートを室内LANとしてこのセグメントを 192.168.22.0/24
ゲートウェイを 192.168.22.1
とした。
ip lan1 address 192.168.22.1/24 ip lan2 address dhcp ip lan2 nat descriptor 1 ip route default gateway dhcp lan2 nat descriptor type 1 masquerade nat descriptor address outer 1 primary dhcp service server dhcp server rfc2131 compliant except remain-silent dhcp scope 1 192.168.22.100-192.168.22.199/24 expire 12:00 maxexpire 12:00 dns server 8.8.8.8 8.8.4.4 1.1.1.1 dns server dhcp lan2 dns private address spoof on
Bridge Modeを有効化した直後は設定が合っているのになぜかうまくWAN側アドレスをもらえなかったのでモデムを再起動したりしてるうちに5分ほどで降ってきた。
拠点間VPN設定
ネットボランチDNS
YAMAHAが無料で提供するDDNSサービス。VPNを利用するためには両拠点にグローバル固定IPが必要だけどこれでほぼ解決できる。
僕がYAMAHAのルータの大ファンなのはこのネットボランチDNSも理由のひとつ。
[PPPoEの場合] netvolante-dns hostname host pp [希望するアカウント名] netvolante-dns go pp 1
[CATVの場合] netvolante-dns hostname host lan2 [希望するアカウント名] netvolante-dns go lan2
振り出されたホスト名をメモしておく。
VPN設定
ここではまず簡易的にIPSecトンネルで設定しているが、適宜L2TP等利用して欲しい。
なお日本側LANは 192.168.11.0/24
ゲートウェイは 192.168.11.1
とした。
[米国側] tunnel select 1 tunnel name VPN-USA ipsec tunnel 1 ipsec sa policy 1 1 esp 3des-cbc sha-hmac ipsec ike hash 1 sha ipsec ike keepalive log 1 off ipsec ike keepalive use 1 on ipsec ike local address 1 192.168.22.1 ipsec ike pre-shared-key 1 [passphrase] ipsec ike remote address 1 tokyo.foo.netvolante.jp tunnel enable 1 nat descriptor masquerade static 1 1 192.168.22.1 udp 500 nat descriptor masquerade static 1 2 192.168.22.1 esp ipsec auto refresh on ip route 192.168.11.0/24 gateway tunnel 1
[日本側] tunnel select 1 tunnel name VPN-USA ipsec tunnel 1 ipsec sa policy 1 1 esp 3des-cbc sha-hmac ipsec ike hash 1 sha ipsec ike keepalive log 1 off ipsec ike keepalive use 1 on ipsec ike local address 1 192.168.11.1 ipsec ike pre-shared-key 1 [passphrase] ipsec ike remote address 1 usa.bar.netvolante.jp tunnel enable 1 nat descriptor masquerade static 1 1 192.168.11.1 udp 500 nat descriptor masquerade static 1 2 192.168.11.1 esp ipsec auto refresh on ip route 192.168.22.0/24 gateway tunnel 1
これでトンネルと相手側LANとのルーティングは完了。対向側にpingを投げてみて応答があるか確認する。
うまく通らない場合は show status tunnel 1
でトンネルインタフェースの状態確認してみたり nslookup
や traceroute
でどこに問題ががあるか切り分けていこう。
後述するが、フィルタの設定が不適切でネットボランチDNSで相手側ホストが名前解決できなかったりしたので、VPN対向側のネットボランチDNSホスト名と解決後のグローバルIPに対してそれぞれpingを打ってみても良い。
syslog debug on
して show log
すると名前解決ができなかったような事情もログに出ている。
米国のLAN3セグメントを日本経由にする
さて、VPNトンネル越しにお互いのセグメントを行き来できるようになったら、いよいよ本題の「特定のSSIDのWiFiに接続すれば何も考えずとも日本経由でインターネットに出ていく」件を設定する。
- LAN3セグメントを
192.168.33.0/24
ゲートウェイを192.168.33.1
とする - ソースアドレス
192.168.33.0/24
はトンネルインタフェースをゲートウェイとしてインターネットに出る
これを念頭に設定する。繰り返しになるが最近のルータは拠点間で同一セグメントを利用できたりするらしいので、その場合はこの設定は必要ない可能性がある。
[米国側] ip route default gateway dhcp lan2 filter 1 2 3 4 gateway tunnel 1 ip lan3 address 192.168.33.1/24 ip filter 1 pass * * udp * 500 ip filter 2 pass * * esp * * ip filter 3 pass * * udp,tcp domain * ip filter 4 pass 192.168.22.0/24 * dhcp scope 2 192.168.33.100-192.168.33.199/24 expire 12:00 maxexpire 12:00
[日本側] ip route 192.168.33.0/24 gateway tunnel 2
見ての通り、フィルタ1, 2でVPNのネゴシエーション用のパケットを、フィルタ3でDNS問い合わせのパケットを、フィルタ4でソースアドレス 192.168.22.0/24
のパケットをComcast側に通し、それ以外は全部トンネル経由で日本側から出すルールを書いた。
当初DNSを見落としており、なぜかVPN接続が確立できなくてログをみていたら、対向側ホスト名の名前解決に失敗していた^^;
あとは適当な無線LANルータを買ってきて米国側LAN3ポートにぶらさげれば*4、このWiFiルータに接続した端末は特に何も意識しなくても日本経由でインターネットに出ることができる。この方法だとデスティネーションアドレスごとにルーティングの設定を変えたりする必要がなくて非常に楽である。めでたしめでたし。
全体像
一応全体を見渡せるように。
言うまでもなく、これは必要な部分だけを抜粋しているので、実際の運用はご自身でルールを追加してください。当方は結果に一切責任を負いません。
[日本側] login password * administrator password * login user foo * security class 2 off on timezone +09:00 console character ascii console prompt [RTX] login timer 3600 ip route default gateway pp 1 ip route 192.168.22.0/24 gateway tunnel 1 ip route 192.168.33.0/24 gateway tunnel 1 ip lan1 address 192.168.11.1/24 pp select 1 pp name [Provider] description pp [Provider] pp always-on on pppoe use lan2 pppoe auto connect on pppoe auto disconnect off pp auth accept pap chap pp auth myname *** *** ppp lcp mru on 1454 ppp ipcp ipaddress on ppp ipcp msext on ppp ccp type none ip pp mtu 1454 ip pp nat descriptor 1 netvolante-dns hostname host pp server=1 japan.foo.netvolante.jp pp enable 1 tunnel select 1 tunnel name VPN-USA ipsec tunnel 1 ipsec sa policy 1 1 esp 3des-cbc sha-hmac ipsec ike hash 1 sha ipsec ike keepalive log 1 off ipsec ike keepalive use 1 on ipsec ike local address 1 192.168.11.1 ipsec ike pre-shared-key 1 *** ipsec ike remote address 1 usa.bar.netvolante.jp tunnel enable 1 nat descriptor type 1 masquerade nat descriptor address outer 1 ipcp nat descriptor address inner 1 auto nat descriptor masquerade incoming 1 discard nat descriptor masquerade static 1 1 192.168.11.1 udp 500 nat descriptor masquerade static 1 2 192.168.11.1 esp ipsec auto refresh on telnetd service off dhcp service server dhcp duplicate check 100 off dhcp server rfc2131 compliant except remain-silent dhcp scope 1 192.168.11.100-192.168.11.199/24 expire 12:00 maxexpire 12:00 dns server 8.8.8.8 8.8.4.4 1.1.1.1 dns server pp 1 dns private address spoof on httpd host lan sshd service on sshd host 192.168.11.1-192.168.11.254 sshd host key generate *
[米国側] login password * administrator password * login user foo * security class 2 off on timezone -07:00 console character ascii console prompt [RTX] login timer 3600 ip route default gateway dhcp lan2 filter 1 2 3 4 gateway tunnel 1 ip route 192.168.11.0/24 gateway tunnel 1 ip lan1 address 192.168.22.1/24 ip lan2 address dhcp ip lan2 nat descriptor 1 netvolante-dns hostname host lan2 server=1 usa.bar.netvolante.jp ip lan3 address 192.168.33.1/24 tunnel select 1 tunnel name VPN-USA ipsec tunnel 1 ipsec sa policy 1 1 esp 3des-cbc sha-hmac ipsec ike hash 1 sha ipsec ike keepalive log 1 off ipsec ike keepalive use 1 on ipsec ike local address 1 192.168.22.1 ipsec ike pre-shared-key 1 * ipsec ike remote address 1 japan.foo.netvolante.jp tunnel enable 1 ip filter 1 pass * * udp * 500 ip filter 2 pass * * esp * * ip filter 3 pass * * udp,tcp domain * ip filter 4 pass 192.168.22.0/24 *** nat descriptor type 1 masquerade nat descriptor address outer 1 primary nat descriptor masquerade static 1 1 192.168.22.1 udp 500 nat descriptor masquerade static 1 2 192.168.22.1 esp ipsec auto refresh on telnetd service off dhcp service server dhcp server rfc2131 compliant except remain-silent dhcp scope 1 192.168.22.100-192.168.22.199/24 expire 12:00 maxexpire 12:00 dhcp scope 2 192.168.33.100-192.168.33.199/24 expire 12:00 maxexpire 12:00 dns server 8.8.8.8 8.8.4.4 1.1.1.1 dns server dhcp lan2 dns private address spoof on httpd host lan sshd service on sshd host 192.168.22.1-192.168.22.254 sshd host key generate *
その他
久しぶりにRTXを触ったのと、シリアルクロスケーブルとかも忘れてきたので、その状態で設定をリセットしてIPを振ってファームウェアを更新するまでにちょっと苦労した。
自分のためにメモを残す。
リセット
背面INIT近くの穴をピンで推しながら電源を入れる。LAN側IPアドレスも失うので注意。
IPを振る
シリアルクロスケーブルがあれば最も話が早いんだけどそれがない場合、RARPを使って設定してもいいんだけどこれが結構面倒くさいのでIPv6のマルチキャストを使う。
RTX1100と自機をLANケーブルで接続して
# eth0はケーブルを挿したネットワークインターフェース名 ping6 -I eth0 ff02::2
すると 64 bytes from xxx::xxx:xxx:xxx:xxx%eth0: icmp_seq=1 ttl=64 time=1.00 ms
みたいに応答があるので telnet xxx::xxx:xxx:xxx:xxx%eth0
するだけ。あとは好きなように設定できる。超簡単!
ファームウェア更新
tftpを使うのが簡単。
- firmware release for Yamaha Network Products ここからファームウェアをダウンロード。md5sumを取って念の為確認。
- ルータで
tftp host any
ortftp host クライアントPCのIP
で許可 - 次のコマンドの通り
tftp ルータのIP tftp> mode binary tftp> put rtx1100.bin exec
mode binary
がミソ
僕が2005年ぐらいにプロになって初めて使ったルータなのでつい思い入れを持って10年以上愛用してしまっている。まあ良いルータですね!