Write and Run

it's a simple way, but the only way.

自宅の潤沢な計算資源に対して一時的にグローバルIPが欲しいとき

みなさんが夏の訪れを感じるのはいつですか。 私は自宅サーバーのファンの音がうるさくなったときです。

私が普段自宅サーバーでサービスを公開するときは1つだけのグローバル IP アドレスを使って Virtual Host でやりくりしています。 (ロードバランサとして k8s 上の Contour(Envoy) を使っています。便利ですよ)

しかし、HTTP(s) ではないサービスをちょっと露出させたかったりするときに困ります。

かといって固定 IP アドレスをたくさんもらえる ISP 契約というのは一時的な用途のために契約するのにはちょっとお値段が。

今回はそんな自宅サーバーでグローバル IP アドレスをちょっと使いたいときに使えるテクを紹介します。

TL;DR

AWS の Elastic IP を EC2 につけて、IPIP6 でトンネルする

やりかた

前提として、対象の自宅サーバーは IPv6 でインターネットに出られるものとします。

IPv4 connectivity しかない場合は IPIP6(4-over-6) を IPIP(4-over-4) に読み替えればできる気がします。

IPIP ではなく IPIP6 にしている理由はフレッツだと IPv6 の方が圧倒的に速いからです。

VPC の用意

まずは AWS 側で IPv6 を有効にした VPC を用意します。

既存のやつを流用してもいいし、新規で作ってもいいと思います。

アドレスプールは Amazon のやつでいいです。

サブネットに IPv6 の CIDR を設定するのも忘れずに。

Elastic Network Interface と Elastic IP の用意

次に Elastic Network Interface(ENI) を作ります。

サブネットはさっき作った VPC のやつをちゃんと選びましょう。

IPv4 Private も IPv6 も Auto-Assign でいいです。

Security Group は用途に合わせてよしなに。

ENI ができたら Elastic IP をアロケートして Associate してやります。

複数付けたければ Manage IP Addresses で Private IP を足して Associate します。

ここで気をつけなければならないのは、ENI に紐付けられる IP アドレスの数は EC2 によって制限されていることです。

この上限はインスタンスタイプによってちがうのでよく確認しましょう。

また、1つのインスタンスに紐付けられる ENI の数にも制限があるため、ENI を分割しても限界があります。

たくさん欲しい場合はインスタンスタイプを変えたりインスタンスを増やしたりする必要があるでしょう。

docs.aws.amazon.com

EC2 インスタンスの用意

用意した ENI をくっつけたインスタンスを立ち上げます。

VPC とサブネットが ENI と食い違っていると作成できないので注意しましょう。

また、OS は Amazon Linux 2 を使っておくのがオススメです。

Amazon Linux 2 以外だと複数の IP アドレスをいい感じにする設定がダルいので。

Ubuntu だとダルいという例: Make Secondary Network Interface Work in Ubuntu EC2 Instance

インスタンスタイプは前述のとおり ENI 数や IP アドレス数に合わせて選びましょう。

IP パケットを右から左(または左から右)に流すだけなので CPU もメモリも問題になりません。一番安いやつを選べばいいと思います。

t2.nano でも 2ENI x 2IP で 4IP いけるのでだいたいのケースで十分だと思います。

ディスクもどうせほとんど使わんし超小さくていいです。

IPIP6 トンネルを掘る

EC2 が起動したらトンネルを掘っていきます。

以下のような構成を想定しています:

  • 自宅側のトンネル終端はルーター(私は IX2105 使ってます)
  • 自宅側のトンネル終端の IPv6 アドレス: 2001:DB8::789
  • 自宅サーバーの CIDR : 192.168.0.0/24
  • 対象の自宅サーバーの Private IP アドレス(自宅内): 192.168.0.1
  • VPC の CIDR : 10.123.0.0/16
  • ENI の Private IPv4 アドレス: 10.123.0.1
  • ENI の Public IPv4 アドレス: 203.0.113.1
  • ENI の IPv6 アドレス 2001:DB8::EC2

適宜読み替えてください。

自宅側

先にトンネルの自宅側の設定をしましょう。

ここでは NEC の IX 系ルーターの設定例だけを紹介します。

tunnel sourceIPv6 アドレスがあるインターフェースに、ip unnumbered自宅サーバーがあるサブネットに繋がってるインターフェースにするとよいです。

interface Tunnel0.0
  tunnel mode 4-over-6
  tunnel adjust-mtu auto
  tunnel destination 2001:DB8::EC2
  tunnel source GigaEthernet0
  ip unnumbered GigaEthernet1
  ip tcp adjust-mss auto
  no shutdown

トンネルの設定を入れたらルーティングの設定もします。

ポリシールーティングを使って自宅サーバーからのトラフィックをトンネルに流すことにします。

ip access-list aws-public permit ip src 192.168.0.0/16 dest any
route-map aws-public permit 10
  match ip address access-list aws-public
  set interface Tunnel0.0
interface GigaEthernet1
  ip address 192.168.0.254/24
  ip policy route-map aws-public
  no shutdown

ここでは CIDR でガバっとやってますが、サブネット内に EC2 に流したいサーバーと流したくないサーバーがいる場合は /32 とかで細かくやることになると思います。

私は EC2 経由で公開するサーバーを置く専用の VLAN を切ってるのでガバっとやっています。

EC2 側設定

コマンドは sudo とか付けずに書きますが必要に応じて root でやるとか sudo つけるとかしてください。

まずは Linuxルーターっぽいことをするときに必ず必要なやつ。

sysctl -w net.ipv4.ip_forward=1

次に IPIP6 トンネルを作成します。今の時代は ip コマンドでなんでもできて便利。

# ip -6 tunnel add ipip0 mode ipip6 local <ENI IPv6 アドレス> remote <自宅終端 IPv6 アドレス>
ip -6 tunnel add ipip0 mode ipip6 local 2001:DB8::1 remote 2001:DB8::789 encaplimit none
ip link set ipip0 up

トンネルが掘れたらルーティングを設定します。

ip route add 192.168.0.0/24 dev ipip0

これで自宅宛てのトラフィックがトンネルに潜って抜けていくようになります。

試しに自宅サーバーの IPv4 アドレス宛に ping 送って試したりするとよいです。

ping 192.168.0.1

これで返ってこなかったら何かがミスっているので確認しましょう。

正しくトンネルを掘れてパケットが行って帰ってこられるようになったら NAT の設定をします。

Public IPv4 アドレス1つにつき、行きと帰りで計2行ずつ設定が必要です。

欲張ってたくさん Elastic IP を貼り付けたひとはがんばって設定しましょう。

# iptables -t nat -A POSTROUTING -s <自宅サーバー Private IP アドレス(自宅内)> -j SNAT --to-source <ENI Private IPv4 アドレス>
# iptables -t nat -A PREROUTING -d <ENI Private IPv4 アドレス> -j DNAT --to-destination <自宅サーバー Private IP アドレス(自宅内)>
iptables -t nat -A POSTROUTING -s 192.168.0.1 -j SNAT --to-source 10.123.0.1
iptables -t nat -A PREROUTING -d 10.123.0.1 -j DNAT --to-destination 192.168.0.1

これでインターネットから 203.0.113.1(ENI の Public IPv4 アドレス) を使って自宅サーバーにアクセスできるはずです。

このままだと EC2 インスタンスを再起動したときに EC2 側のトンネル設定が吹き飛ぶのでいい感じに永続化してください。

自宅サーバー側の設定

ハマりポイントとして、自宅内の DNSゾルバを見るようになってて名前解決ができねぇ、みたいなことがあったりします(ハマった)。

DNSゾルバが同じブロードキャストドメインにいるなら大丈夫なんですが、そうでない場合はポリシールーティングで全部 EC2 にぶん投げちゃってるので見えなくなって困ります(困った)。

ゾルバの設定を DHCP で配ってたりすると面倒ですね(面倒だった)。まぁうまくやってください。

まとめ

Elastic IP は文字通り Elastic なので、パッと調達して取り付けることができて便利ですね。

私は ISUCON の練習用 VM をチームメイトに貸すときに使っています。

一時的にしか使わないのに3台分のグローバル IP アドレスを調達するのってめんどうですからねぇ。

ではよき自宅サーバーライフを!

Special Thanks

このアイデアは同僚の id:sora_h にもらいました。 最初は真面目に暗号化された VPN を張ろうとがんばってたんですが

てかインターネットに出たいトラフィックなら無暗号の ipip tunnelやGREで十分よ

との助言でこのような雑トンネルとなりました。いやー賢いね。