この記事はmisskey.dev ユーザー Advent Calendar 2023の24日目の記事です! メリークリスマス🎉
はじめに
検証に関する業務でログインが必要な外部サービスを利用することがある。検証用のアカウントをよりセキュアな状態で運用したいけど、2段階認証を利用すると複数人でのアクセスがめんどくさくなりそう。なので社内の人なら誰でも使える方法でトークンが生成できるようにしたくなった。社内専用のGoogle Authenticatorを用意する的な感じ。
やりたかったこと
2段階認証で要求されるTOTPのトークンを社内で利用しているJenkinsで取得できるようにする
与えられたシークレットからトークンを生成できるようにする
Google Authenticatorなどで使われているTOTP(Time-based One-time Password)はシークレット+現在時刻から認証トークンを生成する手法らしい。ちょっと調べたらPythonでこれをいい感じにできる素敵なライブラリpyotpがあったのでこれを使ってシークレットからトークンを作るコードをちまっと書いた。
import sys
import pyotp
secret = sys.argv[1]
print(pyotp.TOTP(secret).now())
実行するとこんな感じでトークンが出力される。
❯ python totp_gen.py I65VU7K5ZQL7WB4E
286263
このTOTP MFA Authentication Challengeというページでトークンが正常に生成できているかお試しすることができる。👈ログイン成功ヨシ!
Dockerで実行できるようにする
JenkinsにDocker連携プラグインを入れているとDockerfileを指定して最初からdockerで実行してくれるので、とりあえずこのコードだけが動く環境のDockerfileを書く。pyotpがあればいいので簡単じゃ。
FROM python:3.11-alpine3.18
RUN pip install --upgrade pip pyotp
Jenkinsジョブを組み立てる
Jenkinsジョブの中身をJenkinsfileで設定することができるのすてき、もっと使ってください先輩。
pipeline {
agent {
dockerfile {
filename 'Dockerfile'
}
}
parameters {
choice choices: ['hoge', 'fuga'], description: 'ログインするユーザーID', name: 'USERID'
}
stages {
stage('Generate OTP'){
steps {
script {
sh "echo ${USERID}"
withCredentials([
string(
credentialsId: "2FA_${USERID}",
variable: 'secret')]) {
def result = sh script: 'python totp_gen.py ${secret} || echo error', returnStdout: true
currentBuild.displayName = "${result} : ${USERID}"
}
}
}
}
}
}
特記するとしたら3点。
- 前述のとおりDocker Pipelineがあればagentにdockerfileやimageを指定しておくと最初からそのコンテナでビルドしてくれるので、dockerfileをリポジトリルートからのパスで指定した
- 実行時のパラメーターでトークンを生成するアカウントを指定できるように設定、トークン生成時にはパラメーターのアカウント名を使って認証情報からシークレットを引っ張り出すようにした
- 実行完了時、ビルドの表示名が"{トークン} : {ユーザーID}"となるようにした
ここで3つのファイルtotp_gen.py
, Dockerfile
, Jenkinsfile
を突っ込んだgitリポジトリを社内のgitサーバーをにpushした。
Jenkins側の準備をする
2FAのシークレットを認証情報に登録する
外部サービスでの2FA認証設定時に表示されるsecretをJenkinsのCredentialsにsecret text
として登録。Jenkinsfileと不整合が起こらないようにIDを2FA_{外部サービスのid}
にした。もし設定時にQRコードしか表示されない場合はQRコードを読み取れば中にシークレットが入ってる。
Jenkinsジョブを登録する
新規ジョブ作成から、新しくパイプラインのジョブを作成。ジョブの設定に移行したら、一番下のパイプラインの定義を"Pipeline script from SCM"にしてさっきのファイルがあるリポジトリを指定して保存。
完成!
作成したジョブで"パラメーター付きビルド"を実行すると、数秒でビルド履歴にこういうのが現れる。
実際にログインするときは直前にこのジョブを実行しておいて、いざ求められたらビルド履歴を確認するって感じで使うことができる
実行にかかる時間も気にならないレベルのようで、いまのところクレームはない。
おわりに
実際に構想してから完成まで2時間もかからなかった。こういうことをサクサクできるようにしてくれるPythonのライブラリ群やdockerのすごさとありがたみを感じました。スクショのため手元に環境を再現するのにちょっと時間がかかったのでやや遅刻です(いいわけ)
Qiitadonの滅亡からしばらく経ちましてmisskey.devもいいところです。運営継続とアドカレ企画に感謝ァ...それではよいクリスマスを〜