この記事はDeNA 24 新卒 Advent Calendar 2023の 4 日目の記事です。
TL;DR
- VPSを借りてどこからでもVimを触る.
- VPNサービス Tailscaleでよりセキュアに.
- Dockerのpublishオプション
-p
はufw allow
,ufw default deny routed
のフィルタリング設定に関わらずportを開ける- ufw-dockerの設定を使え.
- OSC52を使ってクリップボードをいい感じに.
- MarkdownのLSPやLinterを使うと尚良し.
Motivation
下記のようなモチベーションがありました. 結果的に今回紹介する構成を組むに至りました.
- 個人のナレッジや個人開発の作業ログのノートを付ける場所がほしい
- できるなら好きなエディタを使いたい.
- あらゆる端末からアクセスしたい (Desktop, Laptop, Tablet, SmartPhone...)
- Git & GitHubで管理してもいいが, commitやpushするほどでもない.
- 様々な環境で別々に編集したのち, ノートのためにコンフリクト解決をしたくない.
- できるだけ安く済ませたい.
- 外出時など, 回線が切れても編集状況が残っていてほしい.
-
HackMD など上記を満たすサービスはある.
- しかし, いつでも素のエディタを使いたい.
- なんならいつでもコードをかけるようにしたい.
現状の個人的な構成
以上のMotivationを達成するような下記のような環境を用意しました.
この記事も以下の環境で書かれています.
- IaaS: ConohaVPS
- OS: Ubuntu 22.04
- VPN: Tailscale
- Terminal Multiplexer: tmux
- ClientでもServerでも使っています. tmux in tmux.
- Shell: fish shell
- Editor: Neovim
- LSP on Markdown: Marksman
- Linter on Markdown: markdownlint
- Markdown Preview: Docusaurus
サーバーの設定
以降は環境設定時のメモやTips, 注意点を述べていきます.
万一参考にする場合は, 自己責任&個人情報などの重要な情報は置かないようにしてください.
もし, 誤りがあれば躊躇なく指摘してください.
まずは基本的なセキュリティ対策を実施します.
具体的には基本的なことではありますが, 下記などを実施します
- userを作成 & sshでrootログインさせないように
- Password認証ではなく公開鍵認証を使う
- 不必要なポートを開放しない
- 今回はufwを使いました.
Tailscale: VPNサービス
/var/log/auth.log には様々なログインの試みが見られます.
加えて開発用のサーバーを立てることなどを見据えて,
よりセキュアにしたいと考え, WireGuardプロトコルを利用したP2PのVPNサービスであるところのTailscaleを導入しました.
次の記事に沿ってTailscale経由でのアクセスのみを許可する設定を導入しました.
しかし, SSH接続に関しては, Tailscale SSHなるものがあるようで, これは
- 鍵管理をしなくて良い
- ブラウザのAdmin Consoleから ターミナルを開けるなどの利点があり便利です.
Tailscale SSH 参考記事:
ufw使用時 Dockerのpublishオプション-p
に注意
サーバー上でDockerを使った開発をしたくなる場合があります.
ですが, ufwでfirewallを作成している場合には注意すべき点があります.
Tailscale経由でアクセスできるDocker ContainerをVPSに建てることを考えます.
❌ 状況: 悪い例
ufw allow in on tailscale0 to any port 3000
- Dockerのpublishオプション
-p 3000:3000
で起動したコンテナ
このように設定すると, たしかにTailscaleクライアントからVPSの<VPNのip>:3000
に
接続できるのですが, Tailscaleに接続していなくても:3000にアクセスできてしまい危険です.
原因
これの原因は, iptables
を確認するとよくわかります.
iptablesの概観はこちらの記事が参考になりました.
図では太矢印が:3000へのパケットの流れを示します.
まず, パケットはiptablesのnatテーブルのPREROUTINGチェインに来ます.
PREROUTINGチェインには, Dockerのpublishオプション -p 3000:3000
によって
サーバーの:3000へのパケットをDocker ContainerのnetworkにFORWARDする処理が追加されています.
この結果, パケットはその後FORWARDチェインによって処理されることになります.
一方, ufw allow
はサーバーのローカルホスト内(≠Docker Container内)で処理されるINPUTチェインに対してのフィルタリングを設定しています.
そのため, この設定内容はFORWARDチェインで処理されるDocker Containerへのパケットには適用されません.
加えて, ufwでFORWARDチェインにフィルタリングを追加する ufw route
コマンドや ufw default deny routed
を設定しても機能しません.
iptablesを確認すると, ufwのFORWARDをフィルタリングするチェーンの前にDocker ContainerへのパケットをACCEPTする処理が設定されるようです.
参考: iptablesを確認してみる
natテーブルを見る.
sudo iptables -t nat -S
結果:
...(略)...
# パケットの宛先がローカルマシン宛ならDockerチェインに飛ばす
-A PREROUTING -m addrtype --dst-type LOCAL -j Docker
...(略)...
# Dockerチェイン: Dockerの特定のbridgeから来ていないあらゆるtcpポート3000宛のパケットをコンテナにフォワーディングする.
-A Docker ! -i br-************ -p tcp -m tcp --dport 3000 -j DNAT --to-destination 172.18.0.2:3000
...(略)...
filterテーブルを見る.
sudo iptables -t filter -S
結果:
...(略)...
# ufwのチェインの前にDOCKERチェインに飛ぶ.
-A FORWARD -o br-************ -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-************ -j DOCKER
-A FORWARD -i br-************ ! -o br-************ -j ACCEPT
-A FORWARD -i br-************ -o br-************ -j ACCEPT
-A FORWARD -j ufw-before-logging-forward
-A FORWARD -j ufw-before-forward
-A FORWARD -j ufw-after-forward
-A FORWARD -j ufw-after-logging-forward
-A FORWARD -j ufw-reject-forward
-A FORWARD -j ufw-track-forward
...(略)...
# DOCKERチェイン内で条件を満たすとACCEPTされる.
-A DOCKER -d 192.168.*.*/32 ! -i br-79eeb1c1c541 -o br-79eeb1c1c541 -p tcp -m tcp --dport 3000 -j ACCEPT
...(略)...
解決策
🤔 -p
オプションのホストをちゃんと設定しておく
対処方法として, -p <VPNのip>:3000:3000
などとホストを設定することがまず考えられますが, 設定を忘れてしまう可能性があります.
/etc/docker/daemon.json
のipプロパティでdefaultのホストを<VPNのip>
に設定して設定忘れを防止することも考えられます.
👍 ufw-docker
上記の課題を解決してくれるレポジトリがありました.
FORWARDチェイン内のDOCKERチェイン以前に実行されるDOCKER-USERチェインに設定を追加します.
DOCKER-USERチェインから, ufw route allow
コマンドで設定されたフィルタリングを行うufw-user-forwardチェインに
飛ばすようにします.
これによって, ufw route allow
コマンドでDocker ContainerのネットワークへのPort Forwardをフィルタリングしてくれます.
ufw-Dockerの設定を正しく適用できたのち, 以下のように設定すればTailscale経由でDocker Containerに安全にアクセスできるはずです.1
sudo ufw route allow from 100.64.0.0/10 to any port 3000
最初からiptablesのみを使ってFilteringをしろという話はあると思います.
iptablesのいい勉強になりました.
Terminal & Editorの設定
先述の通りTmux x Neovimを愛用しています.
拙いですがせっかくなのでdotfilesを置いておきます.
OSC52
ssh先の文字列をコピーする際,
クライアント端末の画面が横に短い場合, URLなどの長い文字列を一度にコピーすることができません.
技術メモを書く際, 多くのURLを貼る, 辿る必要あるため, ssh先のNeovimのクリップボードを簡単に取得できるようにしたいです.
そこでosc52対応ターミナル & neovimのosc52のpluginを使うのが便利です.
osc52はXTermのクリップボード制御シーケンスで, クライアント側のターミナルがシーケンスを処理できるものであれば, 利用することができます.
tmuxもOSC52に対応しており, tmux in tmuxの際の設定はこちらが参考になります.
LSP
Markdownでメモをとるのですが, 往々にして自分の書いたMarkdownのHeadingへのリンクを貼ります.
そのリンクの位置に即座にジャンプしたいという願いを叶えてくれるLSPがMarksmanです.
[inline link](/some-file.md#some-heading)
などの形式のリンクに対してGoToDefinitionのショートカットでそのHeadingの位置に飛ぶことができます.
また, Markdownファイル名の補完やHeadingの補完も行ってくれるため便利です.
まとめ
理想のリモート個人開発環境&メモ環境を作ることができました.
それぞれ詳細は省いていますが, メモ環境構築の取っ掛かりとしてお役に立てれば幸いです.
明日は nntto さんの記事です! お楽しみに.