
ターミナルでKoT稼働時間を確認するkotツールを二要素認証に対応させてみた
セキュリティ向上の一環にて、弊社環境でのKingOfTimeに二要素認証が設定されました。普通に使う分では1Passwordがやってくれるのですが、問題はターミナル上で稼働時間確認に使っていたツールの動作です。
以前スクレイピングの動作を正常にするために弄ったこともありましたが、今回は単なるHTML構造の取得変更程度には収まりません。
あれこれと弄ったところ想定していた動作にまで至ったので、修正点含めてのエントリーとなります。
二要素認証キーを渡す
一番の変更点は二要素認証キーを渡すところです。kotはpoetryで管理されたプロジェクト且つDocker上で動作するという構造になっています。Dockerfileそのものには引数で渡す手もありますが、問題はpoetryです。
poetry run
あるいはpoetry shell
で動作した場合、環境変数は接頭語としてPOETRY_
が必要との記載があります。
これをDockerfile経由で渡すことになりますが、動作検証が予想以上に難儀なものとなりました。os.environ.get
にて取得しても空になる状態です。これは接頭語をつけても外しても同じ。
よりシンプルにできないかと考え、思い切ってログイン情報とまとめることにしました。config.yaml内に二要素認証キーを追加してビルド、実行というわけです。
結果として動作しましたが、これまでdocker-compose経由で実行していた状態からpoetry runによる実行へと変わりました。
二要素認証のログイン対応修正
TOP_URL
のドメインは実際にご利用のもので書き換えてください。
--- a/kot/common/config.py
+++ b/kot/common/config.py
@@ -11,7 +11,7 @@ IS_AWS_LAMBDA_RUNTIME: bool = os.getenv("AWS_LAMBDA_FUNCTION_NAME") is not None
class Account:
id: str
password: str
-
+ tfa: str
@dataclass
class Slack:
@@ -37,6 +37,7 @@ class Config:
account:
id: id
password: passaword
+ tfa: tfa
scrapekot:
slack:
webhook_url: url
@@ -75,6 +76,7 @@ def generate_config(d: dict[str, Any]) -> Config:
account=Account(
id=d["account"]["id"],
password=d["account"]["password"],
+ tfa=d["account"].get("tfa", ""),
),
scrapekot=ScrapeKOT(
slack=Slack(
--- a/kot/scrapekot/crawl.py
+++ b/kot/scrapekot/crawl.py
@@ -2,13 +2,14 @@ from dataclasses import dataclass
from kot.common.crawl import BaseCrawler
-TOP_URL = "https://s3.kingtime.jp/admin"
+TOP_URL = "https://s2.ta.kingoftime.jp/admin"
@dataclass
class CrawlerParams:
account_id: str
account_password: str
+ account_tfa: str
@dataclass
@@ -30,8 +31,12 @@ class Crawler(BaseCrawler):
self.browser.send('//*[@id="login_password"]', params.account_password)
# ログイン
self.browser.click('//*[@id="login_button"]')
- # ログイン成功したか確認
url = self.browser.current_url
+ if params.account_tfa != "":
+ self.browser.send('//*[@id="authentication_code"]', params.account_tfa)
+ self.browser.click('//*[@id="app_auth_login_button"]')
+ url = self.browser.current_url
+ # ログイン成功したか確認
if url == TOP_URL:
raise Exception("login failed")
# ソースを取得
+++ b/kot/service.py
@@ -54,6 +54,7 @@ def scrape_kot(params: ScrapeKOTParams) -> str:
crawler_params = ScrapeKOTCrawlerParams(
account_id=cfg.account.id,
account_password=cfg.account.password,
+ account_tfa=cfg.account.tfa,
)
slack_client_params = ScrapeKOTSlackClientParams(
slack_webhook_url=cfg.scrapekot.slack.webhook_url,
@@ -90,6 +91,7 @@ def punch_myrecorder(params: MyRecorderParams) -> None:
crawler_params = MyRecorderCrawlerParams(
account_id=cfg.account.id,
account_password=cfg.account.password,
+ account_tfa=cfg.account.tfa,
command=params.command,
message=params.message,
yes=params.yes,
--- a/kot/service.py
+++ b/kot/service.py
@@ -54,6 +54,7 @@ def scrape_kot(params: ScrapeKOTParams) -> str:
crawler_params = ScrapeKOTCrawlerParams(
account_id=cfg.account.id,
account_password=cfg.account.password,
+ account_tfa=cfg.account.tfa,
)
slack_client_params = ScrapeKOTSlackClientParams(
slack_webhook_url=cfg.scrapekot.slack.webhook_url,
@@ -90,6 +91,7 @@ def punch_myrecorder(params: MyRecorderParams) -> None:
crawler_params = MyRecorderCrawlerParams(
account_id=cfg.account.id,
account_password=cfg.account.password,
+ account_tfa=cfg.account.tfa,
command=params.command,
message=params.message,
yes=params.yes,
config.yamlにaccount/tfaを追加します
account:
id:
password:
tfa:
scrapekot:
slack:
webhook_url:
channel:
icon_emoji:
username:
myrecorder:
slack:
webhook_url:
channel:
icon_emoji:
username:
実行用のスクリプトは以下の通り。
#/bin/sh
gsed -i "s/^ tfa:.*$/ tfa: $1/" config.yaml
poetry run invoke build
poetry run invoke scrapekot
sh kot_check.sh XXXXXX
二要素認証キーを取得後、期限が切れる前にビルドして実行する必要があります。キーが新しくなった直後であれば恐らく問題ないでしょう。
あとがき
1password-cliから引っ張って実行する手も考えましたが、キーそのものの期限が分からないのがネックです。ブラウザの1Passwordアドオンから期限更新直後にコピペするのが安牌としました。
同じく二要素認証によりkotツールが動作できずに悩んでいた人の参考になれば幸いです。