tsnetを使ってTailScale VPN内部にGoのWebアプリケーションを公開する

tsnetを使ってTailScale VPN内部にGoのWebアプリケーションを公開する

author potpro(ぼとぷろ)
2023/12/09

tsnetを使ってVPN内部にGoのWebアプリケーションを公開する

せっかくなんで、軽いネタです。一切日本語の記事が無かったのでというのと、かなりのニッチネタで記事を書きたかったので。

TailScale

ts

TailScale、皆さん使っているでしょうか。

TailscaleはフルメッシュVPNをめちゃくちゃ簡単に作れることで有名なVPNサービスで、しかも個人利用であればほとんどの場合ほぼ無料(100台まで)で使用できます。

基本的にゼロコンフィグ(設定なし)で可能というのが売りのサービスで、技術者じゃなくとも入れるだけで大体の場合は動作します。本当に手軽。

iOS/Android向けにアプリが提供されていたり、MagicDNSというDNSサービスを内包している所も非常にありがたいです。これを使うことで、IPでのアクセスでわからなくなってしまうということも少なくなります。

自分も一昔前は既存のVPNのソフトウェアをインストールして何もわからない状態になっていましたので、初めてTailscaleを使った時はなんでもっと早く使わなかったんだ・・・という気持ちになりました。

tsnetとは

tsnet

tsnetはTailScaleが提供している、Go言語のライブラリです。

説明によると、Go プログラム内に Tailscaleを埋め込むことができるライブラリと書かれており、Goで動作するプログラム自体をTailscaleでクライアント化することが出来ます。

自分はTailScaleのAPIなんかを叩いて自動的にTailScale VPN内にアプリケーションを公開できる仕組みを探していたのですが・・・

アプリケーションごとTailScaleのクライアントに出来るというのは面白いですね。試してみましょう。

コード

公式でのサンプルコードが結構充実していて、そんなに書くことはないのですが、Webアプリケーションを起動する要領でtsnetを組み込み、動作させることが出来ます。

自分は今個人開発しているもので、WebフレームワークとしてFiberを使用していました。これで作成したWebアプリケーションを、TailScale用に公開したいと思います。

コードはこんな感じです。かなり簡単で導入できるように作られているのも素晴らしいですね。GoでWebアプリケーションを開発したことがあれば、誰でも行けると思います。

package main

import (
    "log"

    "github.com/gofiber/fiber/v2"
    "tailscale.com/tsnet"
)

func main() {
    s := new(tsnet.Server)
    s.Hostname = "test-server"
    defer s.Close()

    ln, err := s.ListenTLS("tcp", ":443")
    if err != nil {
        log.Fatal(err)
    }
    defer ln.Close()

    app := fiber.New()
    app.Static("/", "../public")
    app.Use("/health", func(c *fiber.Ctx) error {
        return c.SendString("ok")
    })
    log.Fatal(app.Listener(ln))
}

このコードはListenTLSを利用し、TLS(https)でVPN内部に公開するコードとなっています。

この機能はhttpのサーバもhttpsに自動的に変換してくれます。

TailScaleのDNS設定にて、HTTPS Certificatesの設定を有効にしないと動作しません。

これだけで内部でのhttps公開できるのは非常に楽です。VPNを通して内部の人だけアプリケーションをテストしたい、というようなニーズとしても使えると思います。

Webサーバを公開するだけのコードと違うところとして、tsnet.ListenTLSは通常のWebアプリケーションと違ってnet.Listenerを返します。

これをWebアプリケーションではこれをカスタムリスナーとして設定する必要があります。Fiberではapp.Listenerと言う関数に渡すだけです。他のWebフレームワークにも同じような機能があるはず。

ざっと調べた感じ、ginであれば、engine.RunListener、echoであればecho.Listenerを使えば良さそうです。

また、TailScaleの認証を起動時に求められます。こちらも未認証の場合はその場で認証できるので、安心設計でいいですね・・・。

動かしてみた

image

ちゃんと動作させると、MachinesとしてTailScaleの管理画面にも表示されます。Versionは1.54.1-ERR-BuildInfoになってるけどまあ普通にアクセスできるのでOK。

この場合、https://test-server.tail0cbcb.ts.net/ というURLでアクセスできるはず。 証明書はTailScaleがLet's Encryptのものを自動的に用意してくれます。素晴らしい。

(※当然、VPNに接続していないとアクセスできませんので、上のURLからアクセスは出来ません)

これはもちろんWebサーバ以外にも応用できるはずなので、VPNでネットワークを組んだIoTのネットワーク環境を自作したりするのも良いかも。セキュリティ的にも安心ですしね。

また、Tailscale Funnelと言う機能で内部だけでは無く外部公開することも可能です。設定は必要ですが、これもtsnetを使って公開することが可能のようです。

以上、TailScaleは技術的にもいろいろできて面白いよと言う記事でした。