Teleportで最強の踏み台サーバーを作ろう

この記事は[STECH Advent Calendar 2024](https://qiita.com/advent-calendar/2024/stech)の22日目の記事です。
投稿が遅れてしまい申し訳ありません...

# 自己紹介

皆様初めまして、Voidと申します。STECHに所属しているB4の学生です。
普段は主に自宅サーバーなどインフラ周りを触ってます。
最近個人的に熱いワードが「クラウドネイティブ」です。なお、去年は「オンプレ最強」でした。（オンプレ運用の苦しさからマネージドサービスの素晴らしさを改めて実感しました）

PromoxやKubernetesなどを主に触っていて、最近は同人誌の執筆などもやってます。ご興味あれば是非🙏
https://techbookfest.org/product/dRrkud2mGwsmUJs9JJp1g1?productVariantID=adt3c5XaJaxsVeLTYiDArK

# はじめに

今回は私が大学のとある組織でアルバイトしているのですが、そこでの開発環境をいい感じにするためTeleportと呼ばれる最強踏み台サーバーを導入したというお話です。

私のバイト先では、各所に設置したセンサーから情報を集約してAIが自律的にエレベータやエアコン照明などさまざまな建物の機器を操作するといったようなことを行っています。
IoTシステムなど外部から侵入されると様々なリスクがあるため、開発を行っているネットワーク環境は学内のネットワークとは分離されています。
開発をする際には、学内ネットワークとIoTシステムのネットワークを持った踏み台サーバーを経由する必要があるのですが、通常の踏み台サーバーでは、
- 新しい人が入るたびに鍵を登録する必要がある
- 踏み台サーバーと中継先のサーバーの両方に鍵を登録する必要がある

といったような問題があります。こうした問題を解決するために今回はTeleportと呼ばれるOSSを活用しました。

# ネットワーク環境
アルバイト先の実際のネットワーク環境とは異なりますが、今回は以下のようなネットワーク構成を例とします。

![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/2fa1369f-4804-d90c-cfd1-99a894536cd2.png)

ネットワーク構成としては、
- 学内ネットワーク(172.21.100.0/24)
- IoTシステム用ネットワーク(192.168.3.0/24)

の2つのネットワークが存在します。
そして、以下のサーバーが登場します。
- Bastion Server
踏み台サーバー(両方のネットワークのIPを持っている)
    - 172.21.100.10/24
    - 192.168.3.10/24
- Server1
開発用サーバー1
    - 192.168.3.11/24
- Server2
開発用サーバー2
    - 192.168.3.12/24
- Server3
開発用サーバー3
    - 192.168.3.13/24

利用者は学内のネットワーク(172.21.100.0/24)から踏み台サーバー経由で開発用サーバーにアクセスします。

# Teleportとは？

[Teleport](https://goteleport.com)は、サーバーやアプリケーションに対してゼロトラストのアクセスを提供するオープンソースのツールです。ゼロトラストとは、「決して信用せず、常に検証せよ」という考え方に基づいたセキュリティモデルで、社内LANアド許可されたネットワーク内であっても必ずデバイスの相互検証を行います。

Teleportでは、PostgresqlやKubernetes、GrafanaといったDBやアプリケーション、SSHで接続するサーバーなどさまざまなリソースに対してアクセスを提供してくれます。
例えば、DBの場合だと接続をする際にユーザーやアプリケーションごとにアカウントを払い出すのではなく、接続する時だけ有効なアカウントをオンデマンドで払出し、セッションが終了するとアカウントが削除されます。
同じように、サーバーの例でもサーバーに接続した時だけそのユーザーのアカウントを作成し、切断後に削除するといったような運用も可能になります。

非常に多くの機能を持っているTeleportですが、今回はこの一部の機能を利用して開発サーバー上にすでにあるユーザーのアカウントへ鍵認証でアクセスできるようにしてみます。

ちなみに、Teleportは有料のEnterprise版とCommunity版があり、Community版の方だとSSOがGitHubに限定されるなどいくつかの機能が制限されます。もし、Community版を導入する場合には要件を満たしているか事前に確認することをお勧めします。

# Teleportの導入

Teleportの導入自体は簡単に行えるのですが、その前に以下のもの2つを用意しておく必要があります。
- ドメインの紐付け
- TLS証明書

## ドメインの紐付け

今回は、踏み台サーバーに`bastion.k1h.dev`というドメインを紐づけることにします。
Teleportを使う際には踏み台サーバーに対して以下のようにDNSを設定しておきます。

| Domain | 理由 |
|:-:|:-:|
|  bastion.k1h.dev | ユーザーやサービスからのトラフィックを受けるため  |
|  *.bastion.k1h.dev | Teleportに登録したアプリケーションへのトラフィックを受けるため(アプリケーションごとにサブドメインが発行される)   |

## 自己証明書の発行

踏み台サーバーグローバルIPアドレスを持っている場合ならLet's Encryptなどを利用できますが、今回の検証では内部ネットワーク限定となるので証明書を用意する必要があります。
いくつか証明書を用意する方法はありますが、今回は検証ということで自己証明書を発行します。
本来はサブドメインのワイルドカード証明書を発行した方が良いですが、今回の検証ではアプリケーションは使わないので`bastion.k1h.dev`のみを発行します。

今回は以下の記事などを参考にしました。
https://www.server-world.info/query?os=Ubuntu_22.04&p=ssl&f=1
https://qiita.com/matarillo/items/c04d8afd09eada27a2b2

まずは、踏み台サーバー上で以下のようにopensslを用いて証明書を発行します。

まず、`/etc/ssl/openssl.cnf`に以下の行を追加します
```diff
+ # Append for Teleport
+ [ k1h.dev ]
+ subjectAltName = DNS:bastion.k1h.dev
```
続いて、秘密鍵を作成します。
```bash
$ cd /etc/ssl/private
$ openssl genrsa -aes128 2048 > server.key
Enter PEM pass phrase: 
Verifying - Enter PEM pass phrase:
$ openssl rsa -in server.key -out server.key
Enter pass phrase for server.key: #同じパスフレーズ
writing RSA key 
```
証明書署名要求(CSR)を作成します。
実験で使う自己証明書なので適当でも大丈夫です。
```bash
$ openssl req -utf8 -new -key server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:JP
State or Province Name (full name) []:Gifu
Locality Name (eg, city) [Default City]:Hinamizawa
Organization Name (eg, company) [Default Company Ltd]:HomeCloudLab
Organizational Unit Name (eg, section) []:CloudSection
Common Name (eg, your name or your server's hostname) []:bastion.k1h.dev
Email Address []:admin@k1h.dev

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
```
次に、サーバー証明書を発行します。
extensionsは最初のopensslの設定のセクションにしてください。

```bash
$ openssl x509 -in server.csr -out server.crt -req -signkey server.key -extfile /etc/ssl/openssl.cnf -extensions k1h.dev -days 3650
Certificate request self-signature ok
subject=C = JP, ST = Gifu, L = Hinamizawa, O = HomeCloudLab, OU = CloudSection, CN = bastion.k1h.dev, emailAddress = admin@k1h.dev
```

以上でサーバー証明書が発行できました。
`chmod 600 server.key`で秘密鍵の権限を変えておきます。

最後に作成した証明書を信頼するようにホストに信頼設定をしましょう。

:::note warn
これ以降に登場するホスト全てで同じ設定をする必要があります。
:::

作成した`server.crt`を`/usr/local/share/ca-certificates`以下にコピーして信頼設定を更新します。bastion以外のホストでは、scpなどを用いて`server.crt`を配置してください。
```bash
sudo cp /etc/ssl/private/server.crt /usr/local/share/ca-certificates/server.crt
sudo update-ca-certificates
```

また、ブラウザ側でも同じく証明書を信頼するように設定しておきましょう。

## Teleportのインストール

以下の公式ドキュメントが参考になりました。

https://goteleport.com/docs/ver/17.x/admin-guides/deploy-a-cluster/linux-demo

まずはTeleportをインストールします。ワンライナーなのでシンプルです。
```bash
$ curl https://cdn.teleport.dev/install-v17.1.1.sh | bash -s 17.1.1
```
次に設定ファイルを作成します。ここで、先ほど発行した証明書を用意します。
予め必要な場所にコピーしておきましょう。
```bash
sudo cp /etc/ssl/private/server.crt /var/lib/teleport/server.crt
sudo cp /etc/ssl/private/server.key /var/lib/teleport/server.key
```

```bash
$ sudo teleport configure -o file \
    --cluster-name=bastion.k1h.dev \
    --public-addr=bastion.k1h.dev:443 \
    --cert-file=/var/lib/teleport/server.crt \
    --key-file=/var/lib/teleport/server.key
```

これで設定ファイルができたので、systemdで再起動しましょう。
```bash
$ sudo systemctl enable teleport
$ sudo systemctl start teleport
```

`https://bastion.k1h.dev`にアクセスして画面が見られることを確認しましょう。

:::note warn
これは例のためこのアドレスにアクセスしても接続できません
:::


![スクリーンショット 2024-12-24 20.05.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/3a357de5-7c8e-e3ae-3bf0-da565d8e8acf.png)

# Teleportを利用する

## Teleportのユーザー作成

Teleportでは初期ユーザーをコマンド上で作成しましょう。
`root`や`user`は接続先のサーバーに存在するユーザー名を指定します。
今回はteleport-adminというユーザーを作成します。

```bash
$ sudo tctl users add teleport-admin --roles=editor,access --logins=root,user
User "teleport-admin" has been created but requires a password. Share this URL with the user to complete user setup, link is valid for 1h:
https://bastion.k1h.dev:443/web/invite/123abc456def789ghi123abc456def78

NOTE: Make sure teleport.example.com:443 points at a Teleport proxy which users can access.
```

次に表示されているURLからアクセスします。
以降は指示に従ってパスワードやOTPの設定をします。Teleportでは、TOTPの設定が必須です。

![スクリーンショット 2024-12-24 20.09.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/5e4c0510-3377-b9c6-f41b-66efbabbbaad.png)
![スクリーンショット 2024-12-24 20.09.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/5eadb383-6bdf-d2ea-9165-d5b5c951856c.png)
![スクリーンショット 2024-12-24 20.10.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/aade5d72-24b9-bbd3-ece1-683491e825d4.png)

ログインすると以下のような画面が出ます
※ここでは幾つかサーバーを追加済みですが、初期状態はbastionのみ表示されます。
![スクリーンショット 2024-12-24 20.10.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/a6a4f86d-b5b8-1098-1f08-6c99e784e330.png)

## ホストの登録

続いて実際に利用するホストを登録してみます。
右上の`Enroll New Resouerce`を推します。

![スクリーンショット 2024-12-24 20.15.31 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/b51974ff-9536-c363-e36c-c991bf8c8ce5.png)

色々なリソースがありますね。今回は`Ubuntu 18.04+`というリソースを選択します。
![スクリーンショット 2024-12-24 20.15.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/4e1f1b2b-14e8-2621-b075-66de3624a034.png)

以下のように登録用のコマンドが表示されます。
![スクリーンショット 2024-12-24 20.15.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/1c876dce-c9ff-abb9-5139-0b81e2cfcdcd.png)

ここで表示されている登録コマンドを対象となるサーバーで実行するだけで自動的にTeleportがセットアップされます。

```bash
$ sudo bash -c "$(curl -fsSL https://bastion.i4s.uec.ac.jp/scripts/31bb25de70ae86472869c2f8ec4cf5dd/install-node.sh)"
2024-12-24 11:23:12 UTC [teleport-installer] TELEPORT_VERSION: 17.1.1
2024-12-24 11:23:12 UTC [teleport-installer] TARGET_HOSTNAME: bastion.i4s.uec.ac.jp
2024-12-24 11:23:12 UTC [teleport-installer] TARGET_PORT: 443
2024-12-24 11:23:12 UTC [teleport-installer] JOIN_TOKEN: 31bb25de70ae86472869c2f8ec4cf5dd
2024-12-24 11:23:12 UTC [teleport-installer] CA_PIN_HASHES: sha256:9be699e242b87833a9aea7b7f84e32367c54cd090733a93b8dc38ad73607fba2
2024-12-24 11:23:12 UTC [teleport-installer] Checking TCP connectivity to Teleport server (bastion.i4s.uec.ac.jp:443)
2024-12-24 11:23:12 UTC [teleport-installer] Connectivity to Teleport server (via nc) looks good
2024-12-24 11:23:12 UTC [teleport-installer] Detected host: linux-gnu, using Teleport binary type linux
2024-12-24 11:23:12 UTC [teleport-installer] Detected arch: x86_64, using Teleport arch amd64
2024-12-24 11:23:12 UTC [teleport-installer] Detected distro type: debian
2024-12-24 11:23:12 UTC [teleport-installer] Using Teleport distribution: deb
2024-12-24 11:23:12 UTC [teleport-installer] Created temp dir /tmp/teleport-4vv4K3hEQZ
2024-12-24 11:23:12 UTC [teleport-installer] Installing repo for distro ubuntu.
Hit:1 https://download.docker.com/linux/ubuntu noble InRelease
Hit:2 http://security.ubuntu.com/ubuntu noble-security InRelease                                                                                   
Get:3 https://apt.releases.teleport.dev/ubuntu noble InRelease [95.2 kB]                                                                           
Hit:4 http://jp.archive.ubuntu.com/ubuntu noble InRelease                                  
Get:5 https://apt.releases.teleport.dev/ubuntu noble/stable/v17 amd64 Packages [6,997 B]
Get:6 http://jp.archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB]
Hit:7 http://jp.archive.ubuntu.com/ubuntu noble-backports InRelease
Get:8 http://jp.archive.ubuntu.com/ubuntu noble-updates/main amd64 Packages [761 kB]
Fetched 990 kB in 4s (258 kB/s)   
Reading package lists... Done
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  teleport
0 upgraded, 1 newly installed, 0 to remove and 9 not upgraded.
Need to get 178 MB of archives.
After this operation, 676 MB of additional disk space will be used.
Get:1 https://apt.releases.teleport.dev/ubuntu noble/stable/v17 amd64 teleport amd64 17.1.1 [178 MB]
Fetched 178 MB in 13s (13.5 MB/s)                                                                                                                  
Selecting previously unselected package teleport.
(Reading database ... 124809 files and directories currently installed.)
Preparing to unpack .../teleport_17.1.1_amd64.deb ...
Unpacking teleport (17.1.1) ...
Setting up teleport (17.1.1) ...
Scanning processes...                                                                                                                               
Scanning linux images...                                                                                                                            

Pending kernel upgrade!
Running kernel version:
  6.8.0-50-generic
Diagnostics:
  The currently running kernel version is not the expected kernel version 6.8.0-51-generic.

Restarting the system to load the new kernel will not be handled automatically, so you should consider rebooting.

No services need to be restarted.

No containers need to be restarted.

No user sessions are running outdated binaries.

No VM guests are running outdated hypervisor (qemu) binaries on this host.
2024-12-24 11:23:46 UTC [teleport-installer] Found: Teleport v17.1.1 git:v17.1.1-0-g73233d4 go1.23.4
2024-12-24 11:23:46 UTC [teleport-installer] Writing Teleport node service config to /etc/teleport.yaml
2024-12-24 11:23:47 UTC [teleport-installer] Host is using systemd
2024-12-24 11:23:47 UTC [teleport-installer] Starting Teleport via systemd. It will automatically be started whenever the system reboots.
Created symlink /etc/systemd/system/multi-user.target.wants/teleport.service → /usr/lib/systemd/system/teleport.service.

Teleport has been started.

View its status with 'sudo systemctl status teleport.service'
View Teleport logs using 'sudo journalctl -u teleport.service'
To stop Teleport, run 'sudo systemctl stop teleport.service'
To start Teleport again if you stop it, run 'sudo systemctl start teleport.service'

You can see this node connected in the Teleport web UI or 'tsh ls' with the name 'server-01'
Find more details on how to use Teleport here: https://goteleport.com/docs/user-manual/
```

プログラムが終了すると、

:::note info
Successfully detected your new Teleport instance
:::

と表示され、Nextに進めます。

![スクリーンショット 2024-12-24 20.25.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/e2d277e3-4cb4-2c86-9f36-195f0f98f5a1.png)

ここで、OS上に存在するユーザーがあっているか確認します。
デフォルトではこのユーザーでインスタンスにログインできます。

![スクリーンショット 2024-12-24 20.25.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/6116d50d-f5d8-8ba4-a650-7aa89779bed7.png)

実際にユーザーを選択して接続確認をしてみましょう。`Tesintg Complete`とでたら成功です。

![スクリーンショット 2024-12-24 20.25.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/7fe3e21e-f1a3-87bb-347b-a7be41b7b9f5.png)

最後にStep3の`Connect to the Server`を押すと、ブラウザ上で実際にSSHのセッションを始められます。
かなり良い操作感です。

![スクリーンショット 2024-12-24 20.26.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414515/ff5410e6-fc55-63c3-f766-18f6e2ecc4d3.png)

## Teleport CLIで接続する

Teleportでは、ブラウザ以外にも手元の端末から接続することができます。
まず、クライアントにCLIをインストールします。Macの場合はbrewで
```bash
$ brew install teleport
```
で導入できます。他のOSはドキュメントを参照してください。

https://goteleport.com/docs/installation/

インストールしたら、`tsh`コマンドをテストします。
```bash
$ tsh version
Teleport v17.1.1 git: go1.23.4
```

まずは、bastionに接続しましょう。
CLI上でブラウザと同じくユーザーにログインします。
ログイン時には先ほど作成したユーザーを利用します。

```bash
$ tsh login --proxy=bastion.k1h.dev --user=teleport-admin
Enter password for Teleport user teleport-admin: # 先ほど登録したパスワード
Enter an OTP code from a device: # 先ほど登録したTOTP
> Profile URL:        https://bastion.k1h.dev:443
  Logged in as:       teleport-admin
  Cluster:            bastion.k1h.dev
  Roles:              access, editor
  Logins:             root, user
  Kubernetes:         enabled
  Valid until:        2024-12-25 08:35:13 +0900 JST [valid for 12h0m0s]
  Extensions:         login-ip, permit-agent-forwarding, permit-port-forwarding, permit-pty, private-key-policy
```

ログインが完了すると、接続可能なリソースの一覧を確認できます。

```bash
$ tsh ls                                                                 
Node Name       Address        Labels 
--------------- -------------- ------            
server-01       ⟵ Tunnel              
bastino         127.0.0.1:3022           
```

実際に接続する場合は、以下のようにするだけで簡単に接続できます。
```bash
$ tsh ssh user@server-01
user@aserver-01:~$
```

ちなみに、VSCodeのRemoteSSHで開発する時などは拡張機能が用意されているので、これらを使えば普通にサーバーにSSHするのと同じように利用できます。

https://goteleport.com/docs/enroll-resources/server-access/guides/vscode/

# 終わりに

今回はTeleportを利用したSSHの踏み台サーバーの構築について紹介しました。
実際に初めてTeleportを触ってみて、昨日の多さに驚いたのでDBやKubernetes,AWSなど他のリソースを色々と試してみたいと思います。
また、BitbucketやGitHubのCI/CD用にOIDCで一時的にアクセスするTokenを払い出す機能などもあるようで、そちらも色々と試してみたいと思います！
皆さんも踏み台サーバーでお困りのことがあれればTeleportを試してみてはいかがでしょうか？
(※別にTeleportの回し者じゃありません...)