Posts for: #Security

nginx の設定調査

開発合宿の準備をしていて今日のバドミントン練習はお休み。

nginx のリバースプロキシ

compose に起動している2つのサービスを同じポート番号で共有したい。nginx を tls 終端にしていてリバースプロキシとして構築している。ルーティングするには2つの方法がある。次のような compose.yml を作ってローカルのファイルシステムをマウントして設定変更しながら検証する。

services:
  proxy:
    container_name: proxy
    image: docker.io/library/nginx:stable
    ports:
      - 8443:8443
    network_mode: "host"
    restart: unless-stopped
    volumes:
      - ./nginx:/etc/nginx

sub-domain based routing

クラウドでは普通のやり方がサブドメインのホスト名でルーティングを行う。設定もシンプルでファイルも管理しやすいように分割できてよいと思える。システム変更時の移行も名前を切り替えればよいので移行しやすい。

  • nginx.conf
http {
    sendfile on;

    upstream web-api1 {
        server localhost:8801;
    }

    upstream web-api2 {
        server localhost:8802;
    }

    ssl_certificate /etc/nginx/ssl/sample.crt;
    ssl_certificate_key /etc/nginx/ssl/sample.key;

    include /etc/nginx/sites-enabled/*;
}
  • nginx/sites-enabled/www.sub1.example.com
server {
    listen 8443 ssl;
    server_name sub1.example.com;
    location / {
        include     /etc/nginx/common_proxy.conf;
        proxy_pass  http://web-api1;
    }
}

path based routing

サブドメインの方が私は好みではあるが、サブドメインをネームサーバーに登録したり、tls の証明書にも複数ホストの考慮が必要になってくる。パスでルーティングするなら1台のマシンのように仮想的にみせられるというメリットはある。

http {
    sendfile on;

    upstream web-api1 {
        server localhost:8801;
    }

    upstream web-api2 {
        server localhost:8802;
    }

    ssl_certificate /etc/nginx/ssl/sample.crt;
    ssl_certificate_key /etc/nginx/ssl/sample.key;

    server {
        listen 8443 ssl;

        location /app1/ {
            include     /etc/nginx/common_proxy.conf;
            proxy_pass  http://web-api1;
        }

        location /app2/ {
            include     /etc/nginx/common_proxy.conf;
            proxy_pass  http://web-api2;
        }
    }
}

コンテナの capability

docker compose を rootless mode で使っていて 443 ポートを使いたいと言われてどうしたらいいのだろう?といままで考えていないことに気付いた。調べたらすぐにやり方が書いてあった。

$ sudo setcap cap_net_bind_service=ep $(which rootlesskit)
$ systemctl --user restart docker

これだけで compose サービスの1つにポート設定できた。めちゃ簡単だった。いままで capability を設定したことがなかったのと、権限周りはややこしいという先入観もあって触る機会がなかった。いろいろ洗練されて抽象化されているのだと推測する。バックエンドの場合は任意のポート番号を使えばよいから capability の設定をして 1024 以下のポート番号を使わないといけない理由はあまりない気はするが、そういう設定もできるようにはみえる。そういう話題すら聞いたことがなかった。

gitlab ci/cd のトークン移行

今日は荷解きしていたので運動はお休み。収納はたくさんあるものの、まだ部屋に不慣れで使い勝手がわかってないからどこに何を入れるか、決まらなくてなかなか進まない。

gitlab ci/cd のセキュリティ対応

gitlab のアクセストークンが 16.0 以前までは無期限に利用できたのが最長1年の有効期限が設定されるようになった。16.0 移行で無期限のアクセストークンは移行後から1年の有効期限となる。

gitlab ci/cd のジョブでパッケージレジストリの操作が、これまでは ci job token (ジョブ実行のタイミングで自動生成される短期間有効なトークン) でアクセスできなかった。そこに project access token を発行して組み込んでいた。しかし、そのトークンが1年ごとに有効期限切れするようになったら保守コストがかかるからこれをやめたい。調べてみると、ちょうど ci job token も進化して 16.1 以上でパッケージレジストリへのアクセスができるようになった。

移行の注意事項として project access token は PRIVATE-TOKEN というヘッダーを使うが、ci job token は JOB-TOKEN というヘッダーを使う。curl なら次のようにして project access token を使わなくてもよくなった。

$ curl --fail-with-body -s -H "JOB-TOKEN: ${CI_JOB_TOKEN}" ...

しかし、ci job token でリポジトリへの push はまだできない。これもいま修正中なので 17.2 から対応されるようだ。

社宅既定の作成

家賃計算 を終えて社宅関連の手続きは完了かな?と税理士さんに相談していたら社宅既定を作るのが望ましいと教えてもらった。

うちの会社は就業規則もなく、既定はほとんど作っていないものの、社宅のような特性を、法人と個人の境界を曖昧に運用するのもガバナンス的によくないというのは理解できる。来月あたり時間のあるときにぼちぼち既定を作っていこうと思う。

google マップのクチコミ

社宅探しを手伝っていただいた 不動産仲介会社 さんに感謝を伝える意図も含めて google マップにクチコミを書いた。35レビューで 5.0 しかないということからしょうみさんの信頼の高さが伺える。このままクチコミが増えていくと、しょうみさんの会社がどのように成長していくのか。この先の展望も楽しみにしている。きっと最後はうまくいくと思う。

振り込みの強制中断エラー

今日の運動は縄跳び(両足跳),散歩,ジョギング,ハンドグリップ,ダンベルをした。統計を 運動の記録 にまとめる。

隔週の雑談

顧問のはらさんと隔週の打ち合わせ。今日の議題はこれら。

  • 資本金と住所変更の変更登記の共有
  • フルタイムではない契約提案のフィードバック

昨日の税理士さん打ち合わせ の内容も踏まえて変更登記の共有を行った。当初はまとめてやった方が登録免許税が安くなったりしないかな?と期待して、2つの変更登記を1回の申請で行うように段取りしていた。昨日、税理士さんから 登録免許税一覧 を共有してもらって、資本金の増加と代表社員の住所変更は別区分であるため、それぞれの登録免許税を支払う必要があることを教えてもらった。はらさんに説明しているうちに登録免許税が同じなら1つの変更登記の申請にこだわる必要がないことに気付いた。それぞれ別の変更登記で申請することに決めた。

今後の契約移行へ向けての提案について、いきなり契約変更を先方に伝えると、急過ぎて相手にショックを与えてしまう懸念があることを指摘してもらった。たしかにこちらの都合で契約を変更したいというところはあるので、丁寧な表現で段階を踏んで相互理解を深めていく取り組みにすることにした。こういうニュアンスのようなものは1人だと気付きにくい。相談相手がいると助かる。

資本金の増資

午後から資本金を増資しようと思って三菱 UFJ 銀行の口座からインターネットバンキングで会社の口座へ振り込みしたら強制的にエラーになってログインできない状態になった。サポートに電話したら、普段やらない手続きを検知したからセキュリティ機構により、振り込みをエラーにしましたとのこと。振り込め詐欺対策やろね。金額も大きかったし。サポートから電話が掛かってきて本人確認して90分後に復旧しますと言っていたが、実際には40分後にはログインできたので、そのまま同じ内容の振り込みを試したら2回目は成功した。手間暇かかったけど、大手銀行は振り込み手続きが正しくても強制振り込み停止のようなセキュリティ機構があるんやなというのを知ることができた。今回は正しい振り込みだったのでただひたすらに不便でストレスが溜まっただけだったけれど。

みなとのもりの運動

前回の所感 。19時前にはオフィスを出発して20時ぐらいにはいつもの運動を終えた。疲労がたまっていたのか、湿気が多くて暑かったせいか、縄跳びをしていてもいつもよりバテたり、カロリー消費もいまいちだった。終わってからそのまま家に帰って、晩ご飯を作って食べて、お風呂入って、オフィスへ戻ることもなく休むことにした。いつもより、オフィスでの作業時間が少なかった分、睡眠時間が増えて休養になったはず。

evernote から upnote への移行

6時に寝て8時半に起きた。オフィスで3時前ぐらいまで調べものして、帰ってからも眠れなくてネットしてたら朝になった。

今日の筋トレは腹筋:15x2,腕立て:10x1,スクワット10x1をした。

サンライズ出雲の予約

以前 やまさきさんに教えていただいた夜行列車 を予約してみた。列車は瀬戸と出雲の2つがある。岡山〜東京間は瀬戸と出雲が連結して運行するそうなのでどちらを選択しても同じだと思うけれど、出雲で予約してみた。乗り込む車両が異なるのかな?

三ノ宮(00:11発) => 東京 (07:08着)

  • 特急サンライズ出雲(シングル)
    • 料金券部分 10,460円 (三ノ宮 - 東京)
    • 乗車券部分 9,460円 (神戸市内 - 東京都区内 2月6日~2月9日まで有効)

前回に 専用の予約画面 があることを調査済みだったので迷わず予約を取れた。唯一、面倒なところがオンラインで予約した後に紙の切符を発行しないといけないところ。切符を発行するには緑の窓口・券売機へ行かないといけない。三ノ宮駅にはあるので問題はない。発行には次の3つが必要になる。初めてだから予約後、すぐに駅へ行って発行してみた。迷わず簡単にできたのでこれなら乗車する直前でもそう困らないように思える。

  • 予約したクレジットカード
  • 電話番号の下四桁
  • 予約番号

upnote への移行

数年前から evernote のビジネスが安定していないのは既知になっていたが、ここ1-2年でフリープランをどんどん縮小して投資回収的な値上げを強行している。

値上げするだけならまだしも、アプリを開くたびに有料プランにアップグレードするためのダイアログを表示して、意図的にユーザー体験を悪化させて、フリープランのユーザーを退会させたいようにみえる。ここまで露骨にやって既存の課金ユーザーだけで十分にビジネスはペイするという目論見なのか、経営陣が短期的にV字回復させたという実績作りなのか。いずれにしても、私のようなちょっと使いのフリープランユーザーはターゲットではないらしい。どうやら2011年6月から使っていたらしいが、件数にすると216件のノートしかない。週に1-2回は使うのだけど、なくなったら別アプリに移行しても困らない程度の用途にしか使っていない。

ずっと移行したいと思いつつ、なおざりにしていた理由として移行先のアプリを選定していなかった。私の用途としては次のような使い方をしている。

  • 買いものの備忘録として思いついたときにパソコンから記録し、スマホでチェックしながら買いものする
  • 法人番号、住所、家族の誕生日など、オンラインで手入力しないといけないときなどのコピペ元
  • 郵便受けのダイヤルロックを忘れたときのメモ
  • パソコンやデバイスを購入したときのスペックを記録しておく
  • オンラインの記事やサイトを保管しておくときの web クリップ

正直メモ帳アプリでなくても、他のアプリへ移行することもできるが、惰性で使っているのでできればそのまま移行したい。私の周りでは obsidian を使っている人も多く評判もいいからこれでもよかったんだけど、いくつか記事を読んでいると upnote の評判もよさそうにみえた。また upnote はサブスクリプションモデルではなく、パッケージライセンスなのでコストが安い。app store 経由でプレミアムのライセンスを購入する。為替によって値段が変わると思うが、いま買ったら3,500円だった。この値段で未来永劫 upnote のインフラコストを賄えるのかは懐疑的だが、3,500円ぐらいならそうなったときにまた別のメモ帳アプリへ移行するのでもよいかなというぐらいの気持ちで upnote を使ってみることにした。

web 版はなくて、それぞれのデバイスごとにインストールする。linux は snap パッケージが用意されているので ubuntu なら次のようにインストールする。

$ sudo snap install upnote

evernote から移行をするにあたり、.enex という xml ファイル形式でデータをエクスポートできる。これは macos または windows アプリでないとエクスポートできない。仕方ないから macos に evernote のアプリをインストールしてエクスポートする。ノートブック単位でエクスポートできる。

その後、upnote の macos アプリでインポートしようと試みるも macos 版にはインポート機能が見当たらない。linux 版にはインポート機能があったので .enex ファイルを linux にコピーしてきて linux 版でインポートしたら移行を完了した。移行の制約事項は次の通り。216件のノートしかないので10分もあれば完了した。同期もかなり速くて、すぐに iphone, macos, linux でノートが同期された。

最適なパフォーマンスを得るには、次の点に注意してください。

  • 多数のノートをインポートすると、アプリのパフォーマンスに影響を与える可能性があります。
  • 20MBを超えるファイルはインポートされません。
  • 300,000文字を超えるノートはインポートされません。

インストールしてから、このアプリの開発元はどこなんだろう?と調べてみるとベトナムの会社らしい。ややセキュリティは大丈夫なのかな?と不安に思ってしまう。Frequently Asked Questions から引用する。firebase を使っているならストレージは強固だし、同期も firebase に委譲すればおかしなことも起こらないだろう。

アップノートはどのように私のデータを保存するのですか?

UpNoteは、アカウントにサインアップしなくても確実に動作するように設計されています。サインインしない場合、メモはあなたのデバイスにローカルに残ります。デバイス間でメモを同期したい場合は、アカウントにサインアップすると、UpNoteが中央サーバーにメモを保存し、他のデバイスと同期できるようになります。

お客様はご自身のデータを完全に管理することができ、アップノートがお客様のデータにアクセスしたり、お客様のデータを第三者に販売したりすることは決してありません。プライバシーに関する詳細は、プライバシーポリシーをご覧ください。

アップノートのサーバーはどこにありますか?安全ですか?

UpNoteはFirebaseサーバー(Googleが提供するサービス)にデータを保存します。Firebaseプラットフォームは、主要なプライバシーおよびセキュリティ基準で認定されており、EU一般データ保護規則(GDPR)およびカリフォルニア州消費者プライバシー法(CCPA)を完全にサポートしています。Firebaseは、HTTPSを使用して転送中のデータを暗号化し、静止時のデータを暗号化します。詳しくは https://firebase.google.com/support/privacy をご覧ください。また、お客様のデータが安全に保護され、お客様だけがアクセスできるように細心の注意を払っています。

アップノートはエンドツーエンド暗号化に対応していますか?

エンドツーエンド暗号化(E2EE)は、データを暗号化・復号化するための高度なセキュリティ手法で、機密性の高い情報を保護するために設計されています。実装が複雑なため、アップノートでは現在E2EEをサポートする予定はありません。パスワードやクレジットカード番号などの機密情報を保存する場合は、機密情報を暗号化するために特別に設計されたパスワードマネージャーアプリケーションを使用することをお勧めします。

e2ee に対応していないところは残念なところ。未対応のセキュリティリスクの懸念としては、upnote 社の社員が私のノートの内容を閲覧できる可能性があったり、攻撃者が upnote 社のインフラに侵入してしまった場合、メモ帳の内容が外部に漏洩してしまう可能性がある。とはいえ、値段も安いのだからそこは割り切って機密情報はすべてパスワードマネージャに移行せよという戦略は理解できる。私の用途だと、メモ帳に機密情報はほとんどないけれど、賃貸の部屋のインターネット接続のアカウント情報や wifi パスワードなどを書いていたりする。こういった内容もこの機会に整理して 1password へ移行していくのがよいように思う。

RemoteAddr はほとんど役に立たない

0時に寝て何度か起きて7時に起きた。寒くて朝起きれなくなってきた。

ローカルネットワークからのリクエストのみを受け付けるミドルウェア

Request 構造体の中にある RemoteAddr を参照すればすぐできるだろ思って、すぐにできた。すぐにできたんだけど、これは実際の運用で使えるものではない。

type LocalNetworkConfig struct {
	Skipper middleware.Skipper
}

func localNetworkWithConfig(cfg LocalNetworkConfig) echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			if cfg.Skipper(c) {
				return next(c)
			}

			req := c.Request()
			addr, _, err := net.SplitHostPort(req.RemoteAddr)
			if err != nil {
				slog.Error("failed to get remote address",
					"err", err, "addr", addr)
				return echo.ErrForbidden
			}
			ip := net.ParseIP(addr)
			if ip.IsPrivate() || ip.IsLoopback() {
				return next(c)
			}

			slog.Error("requested from an external network", "ip", ip.String())
			return echo.ErrForbidden
		}
	}
}

func NewLocalNetwork() echo.MiddlewareFunc {
	return localNetworkWithConfig(LocalNetworkConfig{
		Skipper: func(c echo.Context) bool { return false },
	})
}

過去にも同じようなことをやらかしたような既視感がある。

golangのnet.Request構造体内にはRemoteAddr属性があり、当然のことながら要求元のリモートアドレスが含まれています。これで仕事は終わりですね?しかし、リバースプロキシやロードバランサーをアプリケーションに使っている場合はそうではありません。これはgoサーバには常に、すべてのリクエストがロードバランサから来ているかのように見えます。これは、これを何らかのスロットリングの指標として使いたいのであれば、最悪なことです。ですから、何らかの形のリバースプロキシの後ろでなく、goサーバーが1つだけ動いているのでない限り、私たちの目的には役に立たないとして、すぐにこれを捨てることができます。

https://husobee.github.io/golang/ip-address/2015/12/17/remote-ip-go.html

途中のロードバランサーやアプリケーションゲートウェイ、リバースプロキシなど、中継するサーバーの ip アドレスに置き換わってしまうため、ネットワークのインフラを管理していないとどこからリクエストされたかを追跡することはできない。X-Real-IPX-Forwarded-For という http ヘッダーに中継したサーバーの ip アドレスを記録するという慣習があるようだけど、これはクライアント側で書き換えできるのでこれ単体でアクセス制御のようなものに使うことはできない。

次に同じ失敗をしないようにここに書いておく。

パスワードリセットのテストのためのガイドライン

0時に寝て何度か起きて4時に起きて仮眠して6時に起きた。あまり寝た気がしない。

OWASP のパスワードリセットのガイドライン

パスワードリセットの仕組みをメンバーに開発してもらっている。そのコードレビューが先週から白熱している。セキュリティが関連するので堅牢に作る必要があるのでここはあまり妥協せきない。お手伝い先のシニアエンジニアから独自設計で作るのではなく、最低限、世の中の一般的なガイドラインに従っているかを確認するために OWASP のガイドラインを紹介してくれた。これはパスワードリセットの仕組みをテストするための要項をあげている。

これをメンバーに読んでもらって理解して実装しろと言いたいところでもあるが、私自身、読んだことがないとレビューできないことに気付いて、これは私が読んだ上で既存の設計や実装を見直すべきだと判断して deepl を駆使しながらほとんどを読んでみた。たしかに読んでみていくつか抜け・漏れに気付いたり、うちのセキュリティポリシーとして意図的に緩和しているところも認識できたりして、結論から言って読んでよかったと思う。当初はパスワードリセットのために一時トークンを1つだけ使っていたのだけど、それも2つを別々の経路に送って、割符のように組み合わせて認証する仕組みに変更した。よりセキュアにするという意図では一時トークンも1つよりも2つの方がよいというのは概ね正しいと思う。

ステートレスな認証という概念

0時に寝て4時ぐらいに起きてだらだらして7時半に起きた。やっぱりあまり眠れない。

ステートレスな認証という概念

次の開発フェーズが始まっていて、ちょっと時間が経ってしまったが、前開発フェーズのお披露目的な製品紹介をお手伝い先の全社向けに行った。主には直近の開発フェーズで追加した機能などを紹介した。その過程で新たに認証の仕組みを追加して jwt で認証するといった話しをしたところ、それはステートレスなのかどうかといった質問が出た。セキュリティを考慮して、アーキテクチャ的にフロントエンドの認証と api サーバーの認証は分けて実装しているのと、そのために仕組みも複雑になっているのだけど、ステートレスという言葉が指す意図を私がよくわかっていなくて、うまく説明できなかった。説明を終えた後にアーキテクチャのイメージ図と一緒に補足をしながらやり取りして次の記事を教えてもらった。

jwt は暗号化の技術で認証する仕組みなので有効期限が切れるまでは有効なアクセストークンとなる。そのため、jwt のみだとログアウトという概念はないため、そこをどうしているのか?という質問だった。フロントエンド/api サーバーともに session をオンメモリで保持して、ログインしたユーザーを管理しているため、ログアウトしたら session からレコードを削除することで有効な jwt のアクセストークンが来ても認証エラーにしてしまうことでステートをもった認証方式を実現している。とくまる先生が次のように説明しているところ。

「セッションIDをJWTに内包する」 という考え方です。

うちはこれをセッション ID ではなくユーザー名でやっている。とくに難しいことをやっているわけではなく、普通に実装したらそんな感じかな?と考えていたが、jwt = ステートレス認証だと思い込んでいる人たちがいるから ストートレスな認証 というキーワードが出てきたんだなと理解できた。最近のトレンドとしてはログアウトで jwt のアクセストークンを無効にできないと脆弱性と指摘される可能性がありそうとも書いてある。

ログアウト時にJWTを無効化できない実装は今後脆弱性診断で「OWASP Top 10 2021違反」と指摘されるようになりそう(今も個別にされてるかもしれないけど)

私はアーキテクチャ的にブラウザに api サーバーのアクセストークンをみせないというところに注力して認証機能の開発をサポートしていた。それ自体も間違っていないとは思うけど、今回の質問はその工夫とは異なるところの質問だった。認証は難しい。

縁の下のマネージャー

20時にホテルに戻ってきてのんびりしながら気付いたら22時ぐらいになって、少しテレビをみて0時に寝て4時ぐらいから起きてその後はあまり眠れなかった。それでも7時過ぎまでだらだらしていた。

7月後半に実装予定の新機能の設計

9月までに実装する新機能のうち、唯一、私の頭の中で設計の見通しをもっていなかった機能の設計を行うことにした。

ざっくりした機能概要から私がふわっと想定していたものはずっと複雑なものだったのだけど、プロダクトオーナーに要件をヒアリングしているうちにそんな高度なものは求められていないことに気付いた。逆にその高度な機能の仕組みを提供しても、実際に運用の現場で使うにあたって手間暇だけかかってそんなものを求めていないと言われそうな気がした。そこで私が作りたいなと思っていた設計のアイディアは封印することにした。既存の先行プロダクトがもっている機能とほぼ同様のものを、うちらの開発しているプロダクトで実現するだけでよさそうにみえた。そのシンプルな機能の設計を軽くやっておいた。詳細を詰めるのは次のマイルストーンで私ではないメンバーに実装してもらうことになるけれど、なんとなく当初の想定よりも早くできそうに思えた。

ログ出力のリファクタリング

id 連携の処理で複雑なリソースを map 型で扱うときデバッグ用途でリソースを丸ごと dump したい。しかし、パスワードのような機密情報が含まれる場合はそれらはログに出力したくない。この処理をいまは連携種別ごとに実装していて、本質じゃないところで個別実装の手間があるのと機密情報の出力というセキュリティに関するところを毎回プログラマーが手で実装するのもどうかな?という気がして汎用のログユーティリティとしてロガーのライブラリ側で提供することにした。インフラやプラットフォーム的な機能に私は積極的に開発に介入している。

やり方の1つとしてオリジナルのリソースをコピーして機密情報だけ削除した一時的なリソースコピーを dump してログ出力する。go 1.21 で標準ライブラリに追加される maps パッケージを使うと map の操作が簡単にできる。コピー関数もある。しかし、この機能は shallow copy なので map の値にネストした map が含まれる場合はオリジナルの値を書き換えてしまう。ネストした map を調べてそれらもクローンしていく処理を実装した。excludeKeys に除外したい任意のキーを渡し、map の値を再帰的にチェックして取り除く。最終的には次のようなコピーユーティリティになった。

func copyWithoutExcludeKeys(
	fields map[string]any, excludeKeys []string,
) map[string]any {
	cloned := maps.Clone(fields)
	for k, v := range cloned {
		switch t := v.(type) {
		case map[string]string:
			strMapCloned := maps.Clone(t)
			for _, sk := range maps.Keys(strMapCloned) {
				if slices.Contains(excludeKeys, sk) {
					delete(strMapCloned, sk)
				}
			}
			cloned[k] = strMapCloned
		case map[string]any:
			cloned[k] = copyWithoutExcludeKeys(t, excludeKeys)
		case []map[string]any:
			for i, v := range t {
				t[i] = copyWithoutExcludeKeys(v, excludeKeys)
			}
		default:
			if slices.Contains(excludeKeys, k) {
				delete(cloned, k)
			}
		}
	}
	return cloned
}

yubikey bio を完全にマスターした

2時に寝て遅くまで飲んでいたせいか、やや吐き気もしながら朝起きてだらだらして11時ぐらいに起きた。

1password のロック解除を yubikey で行う

先日購入して触っていた yubikey bio の設定 の続き。1password のロック解除を指紋認証で行いたかったが、1password の 8.10.4 のアプリではロック解除をシステム認証で行おうとするとエラーが発生していた (バグってた) 。5月の頭に 8.10.6 がリリースされていてシステム認証のバグが直っていることに気付いた。1password のアプリケーションがどうやって linux のシステム認証を使っているかは次の1文に書いてある。

システム認証は、Linux のユーザーアカウントに組み込まれたアクセス制御機構を使用します。これは polkit と PAM (Pluggable Authentication Modules) という2つの Linux 標準に依存しています。この2つを組み合わせることで、安全な認証サービスを提供します

https://support.1password.com/system-authentication-linux/#about-system-authentication-security

私は polkit を使ったことがなくて初見でよく分かっていないが、どうやら polkit から pam を介して認証しているようにみえる。pam.d 配下の設定を調べてみると /etc/pam.d/polkit-1 がある。前回の設定で pam.d の設定とテストの方法を理解していた。ここまでくれば Module Arguments のドキュメントをみながらオプションを設定するだけ。1Password のロック解除をYubiKeyでやる の記事で origin/appid にホスト名を指定しているが、最新のモジュールだと指定しないときはデフォルトでホスト名が使われるようにみえる。

  • origin: FIDO 認証手順の party ID を設定する、値が指定されない場合は識別子 pam://$HOSTNAME が使用される
  • appid: FIDO 認証手順の application ID を設定する、値を指定しない場合は origin で使用されたものと同じ値が使用される (origin も指定しない場合は pam://$HOSTNAME)
  • nouserok: 認証しようとするユーザーが authfile 内に存在しない場合や authfile が欠落/不正確な場合でも、認証の試行を成功させるように設定する
  • cue: タッチすることを促すメッセージを表示するように設定する

これらを踏まえて u2f で認証が成功したらそれ以降の処理をスキップするように次の設定を先頭に追加する。

$ sudo vi /etc/pam.d/polkit-1
auth   sufficient   pam_u2f.so nouserok cue
...

cue を指定したことでパスワードプロンプトを表示せず、デバイスをタッチするように促される。よい感じ。

$ pamtester polkit-1 t2y authenticate
Please touch the device.
pamtester: successfully authenticated

これで pam の polkit の設定が正しいことを検証できる。この後に 1password のアプリケーションでロック解除するときに u2f を使って指紋認証できた。めっちゃ便利。

yubikey bio を触ってみた

3時半に寝て7時に起きた。リリース終えたので晩ご飯を食べてから自分の会社のお仕事をしていたら遅くなった。

ストレッチ

今週もリリース明けでそれほど座っている時間が長かったわけでもないため、腰の張りはあまりなく、負荷は低くなっているのではないかと推測する。今日の開脚幅は開始前156cmで、ストレッチ後158cmだった。右股関節周りの硬さは相変わらず大きく変化はないが、他がよくなった分、右ふともも前の筋を伸ばすと張りがきつくてしんどいように感じた。しばらく余裕のある生活ができるので歩いたりストレッチしたりする余裕が出てくるかもしれない。

yubikey bio を触ってみた

先日 yubikey bio をオンラインストアで購入 した。会社の経費で業務で使うことを目的に購入したのでこの場合は商用利用の輸入にあたる。また別途、輸入についての会計処理を書く。yubico はスウェーデンの会社でどうやら日本向けはスウェーデンの首都ストックホルムから発送されてきたらしい。安いエコノミープランで注文していた。発送メールを受け取ったのが 4/17 でオフィスの郵便受けに入っていることに気付いたのが 4/29 になる。12日で届いたことになる。船便にしては早いからなにかしら航空便の安いプランで届いたのだと推測する。

Economy - 10-20 Working Days - No tracking available

早速、接続していろいろ触ってみた。結論から言って ubuntu 22.04 で fido2 pin を設定して u2f を使って2要素認証のメソッドとして pam や web アプリケーションから問題なく使えた。ubuntu 向けのアプリケーションも ppa のリポジトリを追加して apt からインストールできる。

このドキュメントには次の4つのアプリケーションのインストールが書いてある。

  • YubiKey Manager (CLI): sudo apt install yubikey-manager
  • YubiKey Personalization Tool: sudo apt install yubikey-personalization-gui
  • libpam-yubico: sudo apt install libpam-yubico
  • libpam-u2f: sudo apt install libpam-u2f

yubikey は他にもいろいろな認証の用途に使えるらしい。今回は fido2 の設定のみを紹介する。fido2 pin を登録するには YubiKey Manager の GUI を使った方が簡単なので次のアプリケーションも一緒にインストールするとよい。

$ sudo apt install -y yubikey-manager-qt
$ ykman-gui

おそらく ykman の cli でも登録できると思うけど、yubikey や認証設定に慣れていない初心者は gui のナビゲーションに従って作業して結果を確認した方が安心だと思う。fido2 pin は ssh でいうところのパスフレーズのようなものなのかな?デバイスが盗まれるのを防ぐために pin があるという。一方で web アプリケーションが fido2 で認証するときに pin を要求するかどうかは任意になるらしい。よくあるパターンは初めて使うときは pin を要求して2回目以降はスキップするといった用途が多いのではないかと推測する。

この fido2 pin を使って Universal 2nd Factor (u2f) の設定を行う。

$ mkdir -p ~/.config/Yubico
$ pamu2fcfg > ~/.config/Yubico/u2f_keys
Enter PIN for /dev/hidraw3: ***  <= このときに yubikey manager で設定した fido2 pin を入力

この後 yubikey デバイスが点滅するので丸いセンサー部分を指でタッチすると完了する。このときに指紋登録しているのか、物理的にタッチすることを要求しているだけなのか、よくわかっていない。この前に chrome で指紋登録 をしていたのでそれが使われているのかもしれない。いま ykman で設定をみたら Fingerprints registered とあるのでどこかしらに指紋登録されているらしい。おそらく chrome じゃないかと推測する。

$ ykman fido info
WARNING: PC/SC not available. Smart card (CCID) protocols will not function.
PIN is set, with 8 attempt(s) remaining.
Fingerprints registered, with 3 attempt(s) remaining.
Always Require User Verification is turned on.

linux で認証時に u2f を使いたいときは pam_u2f.so を使って pam の設定をするとよい。ubuntu だと /etc/pam.d/ 配下にいろんな認証設定がある。例えば login 時に2要素認証をしたい場合は次のようにフックする。パスワード入力した後に yubikey のセンサーを物理的にタッチして指紋認証を行える。セキュリティに厳しい会社はこういった運用をしているのかもしれない。

$ sudo vi /etc/pam.d/login
...
# Standard Un*x authentication.
@include common-auth
auth       required    pam_u2f.so

パスワード認証の代替として使いたい場合は通常の認証の前に sufficient として呼び出せば指紋認証が成功したときにそれ以降の処理がスキップされる。

auth       sufficient    pam_u2f.so
@include common-auth

これらの設定を確認にするには pamtester というツールを使うと簡単にできる。認証の設定を誤るとログインできなくなってしまうので慎重にテストして振る舞いを確認した上で実際の運用を行うとよい。

$ pamtester login t2y authenticate  <= このときに yubikey デバイスが点滅するので指紋認証する
pamtester: successfully authenticated

せっかく購入したので 1password アカウントや github の2要素認証に設定してみた。ワンタイムパスワードの otp の代替として使える。ワンタイムパスワードを入力するより yubikey のセンサーをタッチする方が日々の運用としては少しお手軽と言える。設定していて otp と yubikey でお互いに2要素認証のバックアップを兼ねることに気付いた。認証方法が増えるのでセキュリティ的には脆弱になるけれど、サービスとセキュリティのトレードオフの考え方から、yubikey の分だけ脆弱になっても物理的なセキュリティを担保できるのであれば利便性を考慮してよいように私には思えた。

rsync に daemon モードがあるらしい

23時に寝て2時半に起きて4時や5時に起きて7時に起きた。泊まっているホテルの低反発枕の寝心地がよい。

rsync daemon over ssh

外部向けのドキュメントを公開するための gitlab ci/cd を構築した。web サーバにドキュメントをアップロードする手段として rsync を使っている。rsync over ssh でデータを転送するときにさらに daemon モード (rsyncd) という仕組みがあって、権限や書き込み先の acl なども細かく制御できる。手順や設定は古の古臭い雰囲気はするけれど、実用的には ssh の秘密鍵を使ってちょっと高機能なアップロードを実現できる。ssh agent で鍵登録できていれば次のような cli でセキュアに rsync できる。全然知らない方法だったので学びの1つになった。

$ rsync \
    --verbose \
    --rsh ssh \
    --stats \
    --compress-choice=zstd \
    --compress-level=10 \
    --itemize-changes \
    --recursive \
    --checksum \
    --delete \
    local/ ${USER}@${HOST}::${RSYNC_DIR}

LLMを使ってみる会

LLMを使ってみる会 に参加した。私も chatgpt に調べものやちょっとしたことを聞くようになったりしているが、他の人たちがどんな用途に使っているのかも知りたくて参加してみた。fin-py のイベントだったのでみんな金融系のドキュメントの要約に使っているのが多そうにみえた。あとは研究テーマとして gpt/llm を取り上げている人たちも何人かいた。