はじめに

みなさんは、GPG 鍵やSSH 鍵はどのように管理していますか?

  • Dropbox等のクラウドストレージに秘密鍵を保存している
  • 全てのPC に同じ鍵をコピーして使い回している
  • PCごとに別々の鍵を作った結果、どの鍵がどのPCに入っているか把握できていない

これに当てはまる人、 弱いって。脆弱だって。

図1: ガチで危機感持った方が良いと思う

図1: ガチで危機感持った方が良いと思う

かくいう私も、passsopsで必要になってくるGPGや、 他のマシンの操作やコーヒーの注文に必須のSSHの鍵について、 デバイスが増えるにつれて以下の問題に悩んでいました。

  • 鍵管理の限界: 4台のPC、クラウドサーバー、研究室のサーバーを使い分けており、マシンごとに鍵を作るのは管理状況を覚えているのが辛いし、同じ秘密鍵を使い回すのはセキュリティ的に不安。
  • 2段階認証の苦痛: macOSならPasskeyが使えますが、LinuxではGitHub Mobileを開くためにiPhoneを手に取る必要があり、これが地味にダルい (最近 こんなのもあるらしいけど)

これらをYubiKeyの導入で解決した記録です。

できるようになったこと

  • 一元管理: YubiKey内にGPG鍵(署名・復号・認証)を閉じ込め、PC側には秘密鍵を置かない運用へ。
  • 2FAの突破: GitHubのログイン時、YubiKeyのボタンをポンと触るだけで認証完了。
  • リモートでのシームレスな認証: 手元のPCにYubiKeyを挿しておけば、SSH先のサーバー(自宅のMac miniや研究室のPC)上でも、手元のYubiKeyを使って git fetchcommit の署名の認証が可能(Agent Forwarding)。

やったこと

YubiKey の購入

日本の代理店のサイトやAmazon でも購入可能ですが、自分が欲しかった YubiKey 5C Nano が売っていなかったため、本家のYubico Store から直接購入しました。

図2: YubiKey 3本とストラップで送料込み32,154円でした

図2: YubiKey 3本とストラップで送料込み32,154円でした

なぜ3本も買ったのか?

①自宅のデスクトップ用、②外出用のノートPC用、③携帯用の予備、の3本体制にするためです。 外出先でYubiKeyを忘れて数時間、場合によっては数日間作業できなくなるリスクを鑑みれば、追加の1万円は安い!

スウェーデンからの発送で、到着までに6日ほどかかりました (意外と早い)。

注意

到着時にヤマト運輸から代引で輸入消費税と税関への立替手数料を別途請求されるので、予め準備しておきましょう (自分の場合3,000円かかりました)。

GPG 鍵の作成 & YubiKey への保存

既存のGPG 鍵をYubiKey に移行する方法もありますが、今回は新規にGPG 鍵を作成しました。

マスターキー(主鍵)はオフラインのUSBメモリに隔離し、YubiKeyには「署名(S)」「復号(E)」「認証(A)」のサブキーを書き込みました。

やり方については、AIに聞けば誰でもできると思うので、ここでは割愛します。

Agent Forwarding の設定 (for Nix users)

自分は出先で作業をすることが多く、 YubiKeyを挿したPCで、YubiKeyを挿していないPCにSSHで接続して作業する という運用をしたかったのですが、このための設定が唯一詰まったポイントでした。

結果、SSH_AUTH_SOCK を動的に切り替える設定をHome Managerに組み込むことでうまくいきました。

  home.file.".gnupg/public_key.asc".source = ./public_key.asc;

  home.activation = {
    importGpgKeys = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
      $DRY_RUN_CMD ${pkgs.gnupg}/bin/gpg --import ${config.home.homeDirectory}/.gnupg/public_key.asc

      # Trust the key ultimately (using full 40-char fingerprint)
      FPR="<your key fingerprint here>"
      echo "$FPR:6:" | $DRY_RUN_CMD ${pkgs.gnupg}/bin/gpg --import-ownertrust
    '';
  };

  # gpg-agent を SSH エージェントとして使用
  services.gpg-agent = {
    enable = true;
    enableSshSupport = true;
    defaultCacheTtl = 3600; # GPG操作(署名・復号)のキャッシュ有効期限。使うたびにタイマーがリセットされる。
    defaultCacheTtlSsh = 3600; # SSH操作(GitHubへのアクセス等)のキャッシュ有効期限。使うたびにタイマーがリセットされる。
    maxCacheTtl = 10800; # GPG操作の絶対的な最大期限。どれだけ頻繁に使っていても、この時間が経過すると再入力が必要。
    maxCacheTtlSsh = 10800; # SSH操作の絶対的な最大期限。どれだけ頻繁に使っていても、この時間が経過すると再入力が必要。
    pinentry.package = if pkgs.stdenv.isDarwin then pkgs.pinentry_mac else pkgs.pinentry-qt;
  };

  # zsh で SSH_AUTH_SOCK を gpg-agent に向ける(agent forwarding されていない場合のみ)
  # これが無いとmacOS でgpg経由でのSSH認証が動作せず、forwardingに失敗する (SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.XXXX/Listeners になる)
  programs.zsh.initContent = lib.mkAfter ''
    # SSH agent forwarding されていない場合のみ gpg-agent を使う
    if [[ -z "$SSH_CONNECTION" ]] || [[ "$SSH_AUTH_SOCK" == *"/gnupg/"* ]]; then
      export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
    fi
  '';

GitHub の 2FA に YubiKey を登録

Settings -> Password and authentication -> Security keys から登録可能。

断念したこと

iOSアプリ Pass で、YubiKeyをNFCで読み取って復号する運用は断念しました。

  • 理由: YubiKey をかざしてNFCで読み取ろうとすると、 Failed to decipher data というエラーが出た。 この件は issue にもあがってるアプリ側の暗号化サブキーの認識ミスが原因のようで、どうにもならなかったため断念。
  • 代替手段: iPhone専用のソフトウェア鍵(RSA 4096)を別途作成し、iPhone内のみにインポート。 YubiKey運用とは切り離すという妥協案を採用しました。

まとめ

まだYubiKey を導入してから日が浅いですが、今のところ非常に満足しています。 他にも色々な場面でYubiKeyを活用できそうなので、これからも色々試してみたいと思います。 一旦今回は以上です。 読んでくださりありがとうございました!