自分専用のVPNリモートアクセスのために、VyattaでL2TP/IPSecを簡易設定して乗り切る

諸事情で、自宅に一定期間帰れないということになったため、外部から、とりあえず自宅ネットワークに簡単にアクセスできる方法を準備することにしました。

簡易的に自宅のMac miniやらMacbook Airなんかにもアクセスしたいとなると、いつも外からのアクセスでサーバを晒してる側の回線からではそちらのネットワークにはアクセスできるようになっておらず、簡易的にとりあえずアクセスするなら、そのクライアント達が外に抜けているVyattaにVPNを設定してしまって、むりやりネットワークに参加させるのが楽そうということで、その方向で設定を行いました。

Vyattaはフレッツ光ネクストで、PPPoEでIIJmioに接続されています。また、Bitcasa用に別途かもめインターネットにも接続されていますが、今回はより安定しているだろうIIJmio側に接続できるようにという想定をしておきます。(一応、モバイル回線がIIJ SIMなんで経路的にもお得かなというのもあって)

PPTPでなく、L2TP/IPSecを設定するのは主にiPhone向けの対策で、プライベートアドレスが割り当てられるIIJ SIM経由で、iPhoneからVPNを張る可能性もあるために、選択肢としてL2TP/IPSecを設定しました。 とはいえ、IPSecのセキュリティ周りの設定として、わざわざ証明書をつかった認証やら、というあたりは簡易的にとりあえず使えればいいので、とりあえず最も簡単なPreSharedなシークレットをつかって、ユーザ名/パスワードで認証できればOKということにしてしまいます。

というわけで、Vyattaの設定はシンプルで下記のような形。 基本的に、ipsecは単純にWAN側のインターフェース(今回はpppoe0)を指定して、nat-traversalを有効にします。(クライアントもNAT下に居る可能性が高いため)

微妙なハマリポイントは、allowed-networkでこれはちゃんとドキュメント読んだり、仕組みを考えれば当然なのですが、クライアント側のネットワークアドレスを記載する必要があります。始め、接続される側の自宅のネットワークアドレスを書いていてハマリました。とりあえず、モバイルルータが利用するネットワーク範囲をそのまま記載しています。

で、リモートアクセス側、これは、local-usersで適当に自分が使うアカウントを1つ設定することと、ipsec-settingsではmode pre-shared-secretで適当なsecretを設定しておくぐらいで、あまりハマリポイントはなかった記憶です。ただ、mtuをフレッツに合わせてそれなりに小さくしておくことと、outside-addressはWAN側に設定されているIIJmioから配布されているアドレスを手動で設定してやる必要があるようです。ドキュメントなどには、outside-next-hopも指定していたりしますが、こちらは必須ではないようです。

PPPoEで動的なアドレスが割り振られる環境なので、IPアドレスをベタに固定で書いてしまうのは若干抵抗があったのでsぐあ、いまのところ、ここを自動で設定してくれるようなオプションはVyattaにはないようです。まあ、L2TP/IPSecを利用をVyattaで利用するような場合は普通は固定IPなんでしょうね。まあ、PPPoEはめったに切れるものでもないので、とりあえずはハードコードしています。

とりあえず、これでMacとかでVPNを設定すればさっくりとつながりました。いやー、Vyatta簡単でいいですね。

ipsec {
    ipsec-interfaces {
        interface pppoe0
    }
    nat-networks {
        allowed-network 172.16.240.0/24 {
        }
        allowed-network 172.16.241.0/24 {
        }
    }
    nat-traversal enable
}
l2tp {
    remote-access {
        authentication {
            local-users {
                username dummy {
                    password dummy
                }
            }
            mode local
        }
        client-ip-pool {
            start 192.168.99.170
            stop 192.168.99.180
        }
        dns-servers {
            server-1 8.8.8.8
            server-2 8.8.4.4
        }
        ipsec-settings {
            authentication {
                mode pre-shared-secret
                pre-shared-secret dummy
            }
            ike-lifetime 7200
        }
        mtu 1280
        outside-address 1.2.3.4
    }
}

Rotue53でダイナミックDNS

PPPoE側のグローバルIPは動的に割り振られる契約なので、とりあえずIPを知るためにダイナミックDNS的なことをしないといけません。dyndnsとかのサービスを使ってもいいのですが、そもそも私はRotue53で自分のゾーンを管理しているので、Route53のAPIを叩いて適当に更新をします。 といっても、そのへんは「route53 dynamicdns python」で検索した結果、最初にでてくる`setting up dyndns service with route53 <http://blog.vrypan.net/2012/12/20/dynamic-dns-with-route53/>`_ のスクリプトをほぼ丸パクリしています。

変更点は、Public IPを取得するのは、http://ifconfig.me/ipに変更していることくらいです。 botoなので、boto.cfgなり、.botoにAWSのキーなどは設定しておく必要があります。

import requests
import sys

# Modified from https://markcaudill.me/blog/2012/07/dynamic-route53-dns-updating-with-python/
# Modified from https://gist.github.com/vrypan/4341878

domain = 'example.com'
subdomain = 'dummy'

def get_public_ip():
    r = requests.get('http://ifconfig.me/ip')
    return r.text.rstrip()

fqdn = '%s.%s' % (subdomain, domain)
zone = route53.get_zone(domain)
arec = zone.get_a(fqdn)
new_value = get_public_ip()
datestr = '"Last update %s."' % datetime.utcnow().strftime('%Y-%m-%d %H:%M')

if arec:
    old_value = arec.resource_records[0]

    if old_value == new_value:
        print '%s is current. (%s)' % (fqdn, new_value)
        sys.exit(0)

    print 'Updating %s: %s -> %s' % (fqdn, old_value, new_value)

    try:
        zone.update_a(fqdn, new_value, 60)
        zone.update_txt(fqdn, datestr, 60)
    except DNSServerError:
        # This can happen if the record did not already exist. Let's
        # try to add_a in case that's the case here.
        zone.add_a(fqdn, new_value, 60)
        zone.add_txt(fqdn, datestr, 60)
else:
        zone.add_a(fqdn, new_value, 60)
        zone.add_txt(fqdn, datestr, 60)

これでDynamicDNSが簡単に実現できる楽な時代ですねー。