LinuxでSSHを使ったGit bareリポジトリのセルフホスティングガイド:GiteaもGitHubも不要で小規模チームのコード共有を実現

Git tutorial - IT technology blog
Git tutorial - IT technology blog

課題:外部サービスに依存せずにコードを共有したい

3〜4人のチームで社内プロジェクトを進めているが、機密情報が含まれているためGitHub publicにはpushできない。GitHub Privateは費用がかかるか制限があり、GitLab self-hostedやGiteaはソフトウェアのインストール、データベース設定、nginx、HTTPSの構成など——最初のコードをpushするまでに膨大な準備が必要だ。

ある日の午後、遊んでいるVPSを眺めながらふと思い出した:Gitはもともとdistributed version controlだ。派手なweb UIもissue trackerも必要ない——メンバー間でコードをpush/pullできる場所さえあればいい。SSHを経由したGit bareリポジトリがその答えで、この方法を1年以上使っているが、問題が起きたことは一度もない。

分析:小規模チームにGiteaやGitHubが不要な理由

GiteaやGitHubはweb UI、pull request、code review、CI/CD integrationなど多くの大規模な課題を解決する。しかし、毎日顔を合わせて働く2〜5人のチームであれば、これらはオーバースペックだ。

Giteaのインストールには以下が必要:

  • バックグラウンドで動作するバイナリ(またはDockerコンテナ)
  • データベース——SQLiteでも可だが、それでも追加のdependencyが増える
  • Nginxリバースプロキシ + SSL証明書
  • web UIでのユーザー管理と定期的なソフトウェアアップデート

Git bareリポジトリなら:

  • 追加インストール不要——Gitはほとんどのディストリビューションに標準搭載
  • SSH keyによる認証——どのチームでも既に使っているもの
  • 10分でセットアップ完了
  • 外部に公開されるwebサービスがないため、attack surfaceが少ない

トレードオフは明確だ:web UIとpull requestは使えなくなる。しかし、直接顔を合わせて働く小規模チームなら、チャットとgit diffによるレビューで十分だ。

その他の選択肢

ガイドに入る前に、自分のニーズに合ったものを選べるよう、各オプションを簡単に確認しておこう:

  • GitHub/GitLab.com——便利だがコードがサードパーティに置かれる。data sovereigntyや社内情報のセキュリティ要件があるプロジェクトには不向き。
  • Gitea/Gogs self-hosted——GitHubライクな美しいweb UIを持つが、長期的なインストールと保守が必要。チームが大きくなるか、本格的なweb interfaceが必要な場合に適している。
  • SSH経由のGit bareリポジトリ——最も軽量でweb UIはないが、小規模チームには十分。以下で詳しく解説する。

最善策:SSHを使ったGit bareリポジトリのセットアップ

事前準備

必要なもの:

  • Linuxサーバー——VPS、社内サーバー、Raspberry Piでも可
  • そのサーバーへのSSHアクセス
  • Gitがインストールされていること(未インストールの場合はsudo apt install git

ステップ1:サーバーにgit専用ユーザーを作成する

ベストプラクティスとして、rootや個人ユーザーを使わず、Git専用のユーザーを作成する:

sudo adduser git
sudo mkdir -p /home/git/.ssh
sudo touch /home/git/.ssh/authorized_keys
sudo chmod 700 /home/git/.ssh
sudo chmod 600 /home/git/.ssh/authorized_keys
sudo chown -R git:git /home/git/.ssh

ステップ2:各メンバーのSSH public keyを追加する

各メンバーが自分のSSH public key(通常は~/.ssh/id_ed25519.pubにある)を送ってくる。そのkeyをサーバーのauthorized_keysファイルに追加する:

# サーバー上でsudo権限で実行
sudo bash -c 'echo "ssh-ed25519 AAAAC3Nza... member1@laptop" >> /home/git/.ssh/authorized_keys'
sudo bash -c 'echo "ssh-ed25519 AAAAC3Nza... member2@workstation" >> /home/git/.ssh/authorized_keys'

あるいは、メンバーが一時的にサーバーにSSHできる場合は、自分のマシンから以下のコマンドでkeyを追加できる:

ssh-copy-id git@your-server-ip

ステップ3:bareリポジトリを作成する

bareリポジトリとはworking treeを持たないリポジトリで、通常のリポジトリ内の.gitディレクトリに相当する純粋なGitデータのみを格納する。サーバー上に必要なのはこれだ。

# サーバー上で、gitユーザーに切り替え
sudo su - git

# 全リポジトリを格納するディレクトリを作成
mkdir -p ~/repos
cd ~/repos

# bareリポジトリを初期化
git init --bare myproject.git

慣例として、bareリポジトリであることがわかるよう.gitという拡張子を末尾につける。

ステップ4:メンバーのマシンからcloneする

サーバーにリポジトリが作成されたら、各メンバーが自分のマシンにcloneする:

# リポジトリをclone(your-server-ipを実際のIPアドレスに置き換える)
git clone git@your-server-ip:repos/myproject.git

# SSHがポート22を使用していない場合
git clone ssh://git@your-server-ip:2222/home/git/repos/myproject.git

以降のワークフローはGitHubとまったく同じだ:

git add .
git commit -m "feat: ログイン機能を追加"
git push origin main

# 同僚の最新コードを取得
git pull origin main

ステップ5:gitユーザーの権限を制限する(推奨)

gitユーザーはGit操作専用であるべきで、フルシェルは必要ない。git-shellで権限を制限しよう:

# git-shellのパスを確認
which git-shell
# /usr/bin/git-shell

# /etc/shellsに追加(まだ存在しない場合)
echo $(which git-shell) | sudo tee -a /etc/shells

# gitユーザーのログインシェルを変更
sudo chsh git -s $(which git-shell)

設定後、gitユーザーとしてSSHしてもGitコマンドの実行でなければ、以下が表示される:

fatal: Interactive git shell is not enabled.

小さいが有効なセキュリティレイヤーを一つ追加することになる。

ステップ6:必要に応じて新しいリポジトリを追加する

新しいプロジェクトができたら、ステップ3を繰り返すだけだ:

sudo su - git
cd ~/repos
git init --bare project2.git
git init --bare internal-tools.git

新メンバーがチームに加わった場合:SSH keyをauthorized_keysに追加するだけ——アカウント作成も追加設定も不要だ。

git push –forceで得た痛い教訓

間違ったブランチにforce pushして重要なコードを失ったことがある——それ以来git push --forceには細心の注意を払っている。具体的には、ローカルブランチをrebaseしてforce pushした際、同僚がそのブランチに新しいcommitを持っていたことを忘れていた。彼らのcommitは完全に上書きされ、git reflogで復元するのに2時間かかった。

bareリポジトリでこれを防ぐために、server-side hookでforce pushを禁止しよう——誰もbypassできない種類のhookだ:

# サーバー上で、bareリポジトリのディレクトリ内にhookを作成
cat > ~/repos/myproject.git/hooks/update << 'EOF'
#!/bin/bash
refname="$1"
oldrev="$2"
newrev="$3"

# mainブランチへのforce pushを禁止
if [ "$refname" = "refs/heads/main" ]; then
    if git merge-base --is-ancestor "$oldrev" "$newrev"; then
        exit 0
    else
        echo "mainブランチへのforce pushは禁止されています!"
        exit 1
    fi
fi
exit 0
EOF

chmod +x ~/repos/myproject.git/hooks/update

より快適に使うための追加ヒント

IPアドレスを覚えなくて済むSSHエイリアスを設定する

各メンバーのマシンの~/.ssh/configに以下を追加する:

Host gitserver
    HostName your-server-ip
    User git
    Port 22
    IdentityFile ~/.ssh/id_ed25519

これでより短くcloneできるようになる:

git clone gitserver:repos/myproject.git

シンプルなbareリポジトリのバックアップ

# bundleを作成——全履歴を含む単一ファイル
git -C ~/repos/myproject.git bundle create /backup/myproject-$(date +%Y%m%d).bundle --all

Giteaへのアップグレードはいつ検討すべきか?

SSH経由のGit bareリポジトリは、チームが5人以下でターミナルに慣れており、web UIやpull requestワークフローが不要で、少ないメンテナンスで素早くセットアップしたい場合に適している。

チームが大きくなった場合、新メンバーを頻繁にonboardする必要がある場合、inline commentによるcode reviewが必要な場合、またはメンバーがGit CLIに不慣れな場合はGiteaを検討すべきだ。

嬉しいのは、後で移行しても全く苦労しないことだ:アップグレードが必要になったら、bareリポジトリをGiteaにpushするだけ——全commitの履歴がそのまま保持され、何も失わない。

Share: