feat: implement GPG encryption for SSH password management

Overview

.envファイルでの平文パスワード保存に代わる、GPG暗号化を使用したセキュアなパスワード管理機能を実装します。標準ライブラリを使用してクロスプラットフォーム対応し、既存の機能との後方互換性を維持します。

Implementation Tasks

Phase 1: Core GPG Integration

  • env.pyget_gpg_password()関数を追加
  • GPGパスワードファイルの配置戦略実装(~/.config/fnb/passwords/
  • ホスト名正規化とファイル名マッピング機能
  • subprocessを使用したGPG復号化機能
  • get_ssh_password()の検索順序拡張(env → GPG → default)

Phase 2: File Management and UX

  • GPGパスワードファイルの自動検出機能
  • ファイル名正規化ルールの実装(user@server.comuser_server_com.gpg
  • パスワードファイル存在確認とバリデーション
  • GPGキー存在確認と適切なエラーメッセージ
  • ファイル権限チェック(600権限推奨)

Phase 3: CLI Integration

  • パスワード設定用CLIコマンド追加(fnb password set --gpg user@server.com
  • 既存.envファイルからGPGファイルへの移行コマンド
  • パスワード一覧表示コマンド(fnb password list --gpg
  • パスワード削除コマンド(fnb password remove --gpg user@server.com

Phase 4: Security and Error Handling

  • GPG未インストール時の適切なエラーハンドリング
  • 暗号化ファイル破損検出と復旧ガイダンス
  • パスフレーズ入力タイムアウト処理
  • ファイル権限エラーの検出と修正提案
  • GPGキー有効期限チェック

Phase 5: Testing and Documentation

  • GPG統合のユニットテストスイート追加
  • 暗号化/復号化プロセスのテスト
  • エラーケースの包括的テストカバレッジ
  • GPGセットアップガイドをセキュリティドキュメントに追加
  • トラブルシューティングガイドの拡充

Phase 6: Advanced Features (Optional)

  • 複数GPGキー対応(チーム環境用)
  • パスワードローテーション支援機能
  • GPGエージェント統合(パスフレーズキャッシュ)
  • 設定ファイルでの暗号化方式優先順位指定

Technical Design

Current Architecture

def get_ssh_password(host: str) -> str | None:
    # 1. FNB_PASSWORD_{NORMALIZED_HOST}
    # 2. FNB_PASSWORD_DEFAULT

Proposed Architecture

def get_ssh_password(host: str) -> str | None:
    # 1. Environment variables (existing)
    # 2. GPG encrypted files (new)
    # 3. Default environment variable (existing fallback)

File Structure Strategy

~/.config/fnb/passwords/
├── user_server_com.gpg        # Host-specific encrypted passwords
├── admin_backup_example.gpg   # Multiple host support
└── default.gpg                # Default password (fallback)

GPG Integration Implementation

def get_gpg_password_file(host: str) -> Path:
    """Get GPG password file path for host"""
    config_dir = platformdirs.user_config_path("fnb")
    passwords_dir = config_dir / "passwords"
    normalized_host = normalize_host_for_filename(host)
    return passwords_dir / f"{normalized_host}.gpg"

def decrypt_gpg_file(file_path: Path) -> str | None:
    """Decrypt GPG file using subprocess"""
    try:
        result = subprocess.run([
            "gpg", "--decrypt", "--quiet", "--batch", str(file_path)
        ], capture_output=True, text=True, timeout=30)
        return result.stdout.strip() if result.returncode == 0 else None
    except (subprocess.TimeoutExpired, Exception):
        return None

Security Considerations

  • GPGファイルの権限管理(600推奨)
  • パスフレーズ入力時のメモリ保護
  • 一時的な復号化データの安全な削除
  • GPGキーの有効期限管理
  • 暗号化アルゴリズムの選択(AES-256推奨)

User Experience Goals

  • シームレスな統合 - 既存ワークフローを妨げない
  • 簡単なセットアップ - CLIヘルパーコマンド提供
  • 明確なエラーメッセージ - GPG関連問題の診断支援
  • 段階的移行 - .envからGPGへの安全な移行パス

Acceptance Criteria

  • GPG暗号化ファイルからパスワードを取得できる
  • 既存の.env機能が完全に保持されている
  • GPGエラー時に適切にフォールバックする
  • ホスト固有とデフォルトパスワードの両方をサポート
  • セットアップと移行用のCLIコマンドが提供されている
  • 包括的なエラーハンドリングとユーザーガイダンス
  • テストカバレッジが83%以上を維持している

Priority

Medium - セキュリティ改善、追加機能として位置づけ

Dependencies

  • GPGコマンドラインツール(ユーザー環境に依存)
  • 標準ライブラリのみ(追加Pythonパッケージ不要)

セキュリティ強化 - パスワード管理改善フェーズ macOSキーチェーン統合(issue #41 (closed))との相互補完