Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Jan 13, 2021 (更新: Dec 22, 2025) - 日記

SSH設定ファイル(~/.ssh/config)を分割管理する完全ガイド

管理するサーバが増えると、~/.ssh/configがどんどん肥大化して管理しづらくなりませんか?

この記事では、SSH設定ファイルをプロジェクトごと・環境ごとに分割して管理する方法を、2025年の最新ベストプラクティスとともに解説します。設定を分割することで、Git管理やチーム共有が容易になり、SSHの運用が格段に楽になります。

なぜSSH設定を分割するのか?

SSH設定ファイル(~/.ssh/config)を分割管理するメリットは以下の通りです:

メリット

  1. 可読性の向上 - プロジェクトや環境ごとにファイルを分けることで、どこに何が書いてあるか一目瞭然
  2. Git管理が容易 - プロジェクトごとの設定をリポジトリで管理、バージョン管理可能
  3. チーム共有 - 特定プロジェクトの設定だけをチームメンバーと共有できる
  4. 柔軟な構成 - 個人設定と共有設定を明確に分離
  5. メンテナンス性 - サーバの追加・削除が簡単、影響範囲が明確

SSH設定ファイルの分割管理の概念図

具体的なユースケース

  • 個人プロジェクト、業務プロジェクトA、業務プロジェクトBで設定を分離
  • 本番環境、ステージング環境、開発環境で鍵や設定を使い分け
  • 会社支給のPCと個人PCで一部設定を共有

方法1: Includeディレクティブを使う(推奨)

OpenSSH 7.3p1(2016年リリース)以降で使用可能なIncludeディレクティブを使う方法が、現在の標準的なアプローチです。

ディレクトリ構成

~/.ssh/
├── config              # メイン設定ファイル
├── conf.d/             # 分割設定ディレクトリ
│   ├── personal.conf   # 個人用サーバ設定
│   ├── work.conf       # 業務用サーバ設定
│   ├── project-a.conf  # プロジェクトA専用設定
│   └── bastion.conf    # 踏み台サーバ設定
└── keys/               # 秘密鍵を整理(オプション)
    ├── personal_rsa
    ├── work_ed25519
    └── project-a_ed25519

メイン設定ファイル(~/.ssh/config)

# ~/.ssh/config

# 分割設定ファイルを読み込み(先頭に配置)
Include ~/.ssh/conf.d/*.conf

# グローバル設定(すべての接続に適用)
Host *
  ServerAliveInterval 60
  ServerAliveCountMax 3
  Compression yes
  
  # macOSの場合、キーチェーン統合
  UseKeychain yes
  AddKeysToAgent yes

重要: Includeは設定ファイルの先頭に配置してください。SSH設定は「最初にマッチしたものが優先」されるため、分割ファイルの具体的なHost設定をHost *のグローバル設定より先に読み込む必要があります。

分割設定ファイルの例

~/.ssh/conf.d/personal.conf

# 個人サーバ設定
Host home-server
  HostName 192.168.1.100
  User pi
  Port 22
  IdentityFile ~/.ssh/keys/personal_rsa

Host vps
  HostName vps.example.com
  User admin
  IdentityFile ~/.ssh/keys/personal_rsa

~/.ssh/conf.d/work.conf

# 業務サーバ設定
Host work-*
  User deploy
  IdentityFile ~/.ssh/keys/work_ed25519
  ForwardAgent yes

Host work-web01
  HostName web01.company.internal
  ProxyJump bastion

Host work-db01
  HostName db01.company.internal
  ProxyJump bastion
  LocalForward 5432 localhost:5432

~/.ssh/conf.d/bastion.conf

# 踏み台サーバ設定
Host bastion
  HostName bastion.company.com
  User jumper
  IdentityFile ~/.ssh/keys/work_ed25519
  ControlMaster auto
  ControlPath ~/.ssh/sockets/%r@%h:%p
  ControlPersist 10m

Includeディレクティブの便利な使い方

ワイルドカード読み込み

# すべての.confファイルを読み込む
Include ~/.ssh/conf.d/*.conf

# 特定のパターンだけ読み込む
Include ~/.ssh/conf.d/work-*.conf
Include ~/.ssh/conf.d/project-*.conf

環境変数の活用

# 環境変数を使った動的読み込み(OpenSSH 8.4以降)
Include ~/.ssh/conf.d/${USER}/*.conf

読み込み順序の制御

ファイル名を工夫することで読み込み順序を制御できます:

~/.ssh/conf.d/
├── 00-global.conf      # グローバル設定(優先度高)
├── 10-bastion.conf     # 踏み台サーバ
├── 20-work.conf        # 業務サーバ
└── 99-fallback.conf    # フォールバック設定(優先度低)

方法2: シェルスクリプトで自動生成(レガシー)

OpenSSH 7.3以前や、動的に設定を生成したい場合のアプローチです。

sshconfスクリプト

サーバ追加を簡略化するヘルパースクリプトを作成します:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/bin/bash
# sshconf - SSH設定ファイル管理ツール

ssh_config_dir=~/.ssh
ssh_configs_dir=${ssh_config_dir}/conf.d
ssh_config_file=${ssh_config_dir}/config

function usage() {
    echo "Usage: $(basename $0) add user@hostname alias"
    echo "       $(basename $0) list"
    echo "       $(basename $0) edit alias"
}

function add() {
    dest=$1
    name=$2
    file="${ssh_configs_dir}/${name}.conf"
    
    if [ -f "$file" ]; then
        echo "Error: ${file} already exists"
        exit 1
    fi
    
    # ユーザー名とホスト名を分割
    if [[ $dest =~ ^([^@]+)@(.+)$ ]]; then
        user="${BASH_REMATCH[1]}"
        host="${BASH_REMATCH[2]}"
        
        cat > "$file" << EOF
Host ${name}
  User ${user}
  HostName ${host}
  IdentityFile ~/.ssh/keys/${name}_ed25519
EOF
    else
        host="$dest"
        cat > "$file" << EOF
Host ${name}
  HostName ${host}
EOF
    fi
    
    echo "Created: $file"
    echo "Next steps:"
    echo "  1. Edit: $file"
    echo "  2. Generate key: ssh-keygen -t ed25519 -f ~/.ssh/keys/${name}_ed25519"
    echo "  3. Copy key: ssh-copy-id -i ~/.ssh/keys/${name}_ed25519 ${dest}"
}

function list() {
    echo "Configured hosts in ${ssh_configs_dir}:"
    for file in "${ssh_configs_dir}"/*.conf; do
        [ -e "$file" ] || continue
        echo "  - $(basename "$file" .conf)"
    done
}

function edit() {
    name=$1
    file="${ssh_configs_dir}/${name}.conf"
    
    if [ ! -f "$file" ]; then
        echo "Error: ${file} not found"
        exit 1
    fi
    
    ${EDITOR:-vim} "$file"
}

# メインロジック
case "$1" in
    add)
        [ $# -eq 3 ] || { usage; exit 1; }
        add "$2" "$3"
        ;;
    list)
        list
        ;;
    edit)
        [ $# -eq 2 ] || { usage; exit 1; }
        edit "$2"
        ;;
    *)
        usage
        exit 1
        ;;
esac

使い方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 実行権限を付与
chmod +x ~/bin/sshconf

# サーバ追加
sshconf add [email protected] web01

# 一覧表示
sshconf list

# 設定編集
sshconf edit web01

Git管理のベストプラクティス

リポジトリ構成

~/.ssh/
├── .gitignore
├── config
├── conf.d/
│   ├── personal.conf    # 個人設定(リポジトリ管理外)
│   └── work.conf        # 業務設定(プライベートリポジトリ)
└── keys/
    ├── *.pub            # 公開鍵のみコミット
    └── *_rsa            # 秘密鍵は.gitignoreで除外

.gitignoreの例

# 秘密鍵は絶対にコミットしない
*_rsa
*_ed25519
*_ecdsa
id_*
!*.pub

# 一時ファイル
known_hosts*
*.swp
*.tmp

# 個人設定は除外
conf.d/personal.conf

# SSH接続の制御ソケット
sockets/

Git初期化

1
2
3
4
5
6
7
8
cd ~/.ssh
git init
git add config conf.d/*.conf keys/*.pub
git commit -m "Initial SSH config"

# プライベートリポジトリにプッシュ
git remote add origin [email protected]:yourname/ssh-config-private.git
git push -u origin main

セキュリティ上の注意

  1. 秘密鍵は絶対にコミットしない - .gitignoreで確実に除外
  2. プライベートリポジトリを使う - 設定ファイルには内部ホスト名やIPが含まれる
  3. パスワードを含めない - 認証は秘密鍵ベースに統一
  4. 機密情報の確認 - git diff --cachedでコミット前に必ず確認

チーム共有の方法

プロジェクト固有のSSH設定をチームで共有する場合:

1. プロジェクトリポジトリに設定を含める

your-project/
├── .ssh/
│   └── config.d/
│       └── project.conf
└── docs/
    └── ssh-setup.md

2. セットアップスクリプトを提供

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/bash
# scripts/setup-ssh.sh

SSH_CONFIG_DIR="$HOME/.ssh"
PROJECT_SSH_DIR="$(dirname "$0")/../.ssh/config.d"

# conf.dディレクトリを作成
mkdir -p "$SSH_CONFIG_DIR/conf.d"

# プロジェクト設定をシンボリックリンク
ln -sf "$(realpath "$PROJECT_SSH_DIR/project.conf")" \
       "$SSH_CONFIG_DIR/conf.d/myproject.conf"

echo "SSH config linked successfully"
echo "Make sure your ~/.ssh/config includes: Include ~/.ssh/conf.d/*.conf"

3. チームメンバーへの指示

1
2
3
4
5
# SSH設定のセットアップ

1. メイン設定ファイルを確認:
   ```bash
   cat ~/.ssh/config

以下の行が含まれていることを確認(なければ追加):

Include ~/.ssh/conf.d/*.conf
  1. プロジェクト設定をリンク:

    1
    
    ./scripts/setup-ssh.sh
    
  2. SSH鍵を生成(未作成の場合):

    1
    
    ssh-keygen -t ed25519 -f ~/.ssh/keys/myproject_ed25519
    
  3. 公開鍵をサーバに登録(管理者に依頼):

    1
    
    cat ~/.ssh/keys/myproject_ed25519.pub
    

## 実践的な設定例

### 踏み台サーバ経由のマルチホップ接続

```ssh_config
# 踏み台サーバ
Host bastion
  HostName bastion.example.com
  User jumper
  IdentityFile ~/.ssh/keys/work_ed25519
  
  # 接続の多重化で高速化
  ControlMaster auto
  ControlPath ~/.ssh/sockets/%r@%h:%p
  ControlPersist 10m

# 踏み台経由で内部サーバに接続
Host internal-*
  ProxyJump bastion
  User admin
  IdentityFile ~/.ssh/keys/work_ed25519

# 具体的なホスト設定
Host internal-web01
  HostName 10.0.1.10

Host internal-db01
  HostName 10.0.1.20
  LocalForward 5432 localhost:5432

GitHub 複数アカウントの使い分け

# 個人アカウント
Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/keys/github_personal_ed25519

# 業務アカウント
Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/keys/github_work_ed25519

使い方:

1
2
3
4
5
# 個人リポジトリ
git clone [email protected]:username/repo.git

# 業務リポジトリ
git clone git@github-work:company/repo.git

AWS EC2インスタンスへの接続

# AWS踏み台サーバ
Host aws-bastion
  HostName bastion.aws.example.com
  User ec2-user
  IdentityFile ~/.ssh/keys/aws-bastion.pem

# EC2インスタンス(踏み台経由)
Host ec2-*
  ProxyJump aws-bastion
  User ec2-user
  IdentityFile ~/.ssh/keys/aws-instances.pem
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null

# 具体的なインスタンス
Host ec2-web
  HostName 10.0.1.100

Host ec2-worker
  HostName 10.0.2.50

トラブルシューティング

設定が反映されない

1
2
3
4
5
6
7
8
# 設定ファイルの構文チェック
ssh -G hostname

# デバッグモードで接続
ssh -vvv hostname

# Includeの読み込み状況を確認
ssh -G hostname | grep -i include

ファイルのパーミッション警告

1
2
3
4
5
6
# SSH設定ファイルのパーミッションを修正
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/conf.d/*.conf
chmod 600 ~/.ssh/keys/*
chmod 644 ~/.ssh/keys/*.pub

Includeが機能しない

OpenSSHのバージョンを確認:

1
2
ssh -V
# OpenSSH_7.3p1以降であることを確認

7.3未満の場合はアップグレードを検討してください。

まとめ

SSH設定ファイルの分割管理は、以下の点で開発効率を大きく向上させます:

  • Includeディレクティブで簡潔に分割管理
  • Gitでバージョン管理、変更履歴を追跡
  • チーム共有でプロジェクト全体の生産性向上
  • セキュリティを保ちながら柔軟に運用

OpenSSH 7.3以降であれば、Includeを使った方法が最もシンプルで推奨されます。古いバージョンの環境では、シェルスクリプトによる自動生成も選択肢として有効です。

複数サーバを管理するエンジニアにとって、SSH設定の整理は必須のスキルです。ぜひこの記事を参考に、自分に合った管理方法を確立してください。


関連記事

参考リンク