ペパボでは新型コロナウィルスをきっかけに、全社でのリモートワークを前提とした勤務体制を取っています。それにあたり、いろいろな工夫をしているわけですが、開発者が利用するイントラAPIなどを安全に利用するために、VPNを利用するケースは非常に多いと思います。しかし、VPNについてはネットワークによる認可の細かい制御が難しく、基本的にVPNのエンドポイントにつなげると何でもできてしまうという状態になってしまいがちです。そういった課題を解決するために、僕が社内に提供するサービスはなるべくTLSクライアント認証を利用して、ネットワークの認証を行ったあとに、更にアプリケーションで認証・認可を行うようにしています。
またTLSクライアント認証に利用するクライアント証明書、鍵のセットはHashicorp VaultのPKI基盤を利用して払い出しており、こちらもGHEのOrganization/Repository/Teamの情報とリンクする形式で認可を行っています。
一方でHashicorp VaultはAPIを実行したり、クライアントCLIを利用したり、または管理者向けのWEBUIはあれど、この手のミドルウェアを利用したことがないエンジニアに、ここから認証後に鍵取得して、あとはよろーというには少々厳しい現実があります。
それらを解決するために開発したのがKagianaです。
Kagiana自体はGitHub(厳密にはGitHub Enterpriseを利用しています) の提供するOAuth認証を行ったあとに、Kagianaで指定したPKIエンドポイントから証明書と鍵のダウンロードを行います。イメージとしては下記のとおりです。要するにKagianaの設定ファイルで指定してある証明書、鍵をWEBUIで認証し、払い出すことができる仕組みです。
ペパボではKagianaで認証後に、Vaultに接続が可能な証明書、鍵をダウンロードすることで、利用者をVaultに接続可能にします。その後、Vaultで認証・認可を行うことで、そのユーザーが利用可能な証明書と鍵がダウンロードできるようになります。
更に、上記図の5にあたる証明書の管理については consul-template
を手元の端末で起動できるスクリプトをLinux、MacOSの環境それぞれ準備しており、インターナルなKagianaの管理リポジトリで、 make install
と実行するだけで、利用者の手元で consul-template
が起動し、自動で証明書の取得、更新管理を行ってくれます。配布している consul-temlate
の設定は下記のようなものです。
max_stale = "10m"
log_level = "info"
pid_file = "/tmp/consul-template.pid"
vault {
address = "https://vault.example.com"
renew_token = true
ssl {
enabled = true
verify = true
ca_path = "~/.kagiana/vault.example.com.ca"
cert = "~/.kagiana/vault.example.com.cert"
key = "~/.kagiana/vault.example.com.key"
}
}
template {
contents = "{{ with secret "example.com/issue/example.com" "common_name=vault.example.com" }}{{ .Data.issuing_ca }}{{ end }}"
destination = "~/.kagiana/vault.example.com.ca"
}
template {
contents = "{{ with secret "example.com/issue/example.com" "common_name=vault.example.com" }}{{ .Data.certificate }}{{ end }}"
destination = "~/.kagiana/vault.example.com.cert"
}
template {
contents = "{{ with secret "example.com/issue/example.com" "common_name=vault.example.com" }}{{ .Data.private_key }}{{ end }}"
destination = "~/.kagiana/vault.example.com.key"
}
このような設定ファイルを定義して起動すると、Vaultから取得した証明書の有効期限を consul-template
が監視し、自動で証明書を失効前に更新してくれます。さらには必要に応じて、更新したタイミングで何かしらのコマンドを実行することができます。例えばWEBサーバであれば証明書を更新したあとにWEBサーバをリロードするというような定義をすることはよくあります。
ちなみに、なぜ consul-template
を利用するのかというと、これらの仕組みで提供する証明書の有効期限を1日程度と非常に短い値にすることで、主に退職時などにおける鍵の失効管理をなくしたいという狙いがあるためです。実態としてはVaultでアカウントが認証できなくなることで鍵の更新ができなくなり、自動的に失効するような運用を行っています。一方であまりに短い期間で証明書を手動で更新するのは非常に手間なので、 consul-template
で更新を自動化しているということです。更にこの仕組を利用すればリポジトリにおいてある、 consul-template
の設定ファイルを更新、配布するだけで全開発者に利用を許可する証明書を一括で配布することができるので管理者としても非常に運用が楽です。
ここで一つ与太話なのですが、Linux、MacOSの各環境の consul-template
の起動スクリプト生成に Foreman
を利用しています。僕も今回知ったのですが、 Foreman
には export
というサブコマンドがあり、下記のようなProcfileをまず定義します。
consul-template: /usr/bin/env PATH=$PATH:/usr/local/bin consul-template -config=./consul-template/my-config.hcl
そして foreman export systemd <dest> <option> -f myProcfile
と実行するとsystemd
の起動スクリプトを出力してくれます。そしてこれが launchd
などにも対応しているので、各環境の起動スクリプトのグルー技術として非常に便利でした。下記のリンクにある通り、いろいろな出力が可能なので知っておくと、今後Linuxデスクトップも増えてくると思うので便利だと思います。
最後に
今回はTLSクライアント認証に利用する証明書、鍵を何かしらの認証後に取得し、その証明書を利用して必要な認証、認可をしていくというようなまさに鍵穴となるようなソフトウェアを開発しました。おそらくこの仕組はしばらくペパボ内でレバレッジを効かせながら使われていくソフトウェアになると思うので継続開発していく予定です。同じような課題をお持ちの方、認証方法の追加のご希望などあればIssueを書いていただければ諸々検討するので、ぜひお気軽にご連絡ください。