feat: implement GPG encryption for SSH password management
Overview
.envファイルでの平文パスワード保存に代わる、GPG暗号化を使用したセキュアなパスワード管理機能を実装します。標準ライブラリを使用してクロスプラットフォーム対応し、既存の機能との後方互換性を維持します。
Implementation Tasks
Phase 1: Core GPG Integration
-
env.pyにget_gpg_password()関数を追加 - GPGパスワードファイルの配置戦略実装(
~/.config/fnb/passwords/) - ホスト名正規化とファイル名マッピング機能
-
subprocessを使用したGPG復号化機能 -
get_ssh_password()の検索順序拡張(env → GPG → default)
Phase 2: File Management and UX
- GPGパスワードファイルの自動検出機能
- ファイル名正規化ルールの実装(
user@server.com→user_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_DEFAULTProposed 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 NoneSecurity 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パッケージ不要)
Related
セキュリティ強化 - パスワード管理改善フェーズ macOSキーチェーン統合(issue #41 (closed))との相互補完