保守注(2026年9月1日):本ページは現在、再現可能な現行手順を先に示し、末尾に2019年のソース・エクスポート全文を原様保存しています。旧いブランチ前提、組版による誤コマンド、プライベートLANの例、出典を明記したCSDN抜粋も記録の一部です。アーカイブのコマンドは実行しないでください。保守部分はプレースホルダーと公式資料を使い、アフィリエイトリンクを含みません。
この設計は、Raspberry Pi OS上の個人サーバーまたは相互に信頼できる小規模チーム向けです。GitクライアントはSSH経由でベアリポジトリへアクセスできますが、対話シェルは得られません。デプロイは二通り示します。
- 低リスクの静的ディレクトリ向けの短い
post-receivehook - Gitアクセスと公開アカウントを分離するsystemd引き渡し方式
どちらも完全なフォージではありません。Web UI、Pull Request、リポジトリ別認可、シークレットスキャンはありません。互いに十分信頼できない複数ユーザーには、一つの無制限な git アカウントを共有せず、保守中のGitoliteまたはフォージを利用してください。
Table of Contents
1. インストール前に信頼境界を決める
最初は信頼できるLANまたはVPN内でPiを使います。この手順を終えるためだけにSSHをインターネットへポート転送しないでください。後に公開アクセスが必要なら、先にファイアウォール規則、更新責任、監視、バックアップ、鍵失効手順を定義します。
以下を仮定します。
sudoを使える管理アカウントはadmin- サーバー名は
pi-git.local、または自分が管理する別名 - リポジトリは個人ホームではなく
/srv/git - 専用の非対話アカウントは
git - 既定ブランチ名は明示的に
main
名前とパスは一貫して置き換えてください。アプリのシークレット、アップロード、データベース、可変ランタイム状態をデプロイ作業ツリーへ置いてはいけません。
2. Gitをインストールし、SSHを有効にする
新規Raspberry Pi OSではSSHが既定で無効です。Raspberry Pi Imagerは書き込み時に公開鍵専用SSHを設定でき、既存システムでは公式文書どおり raspi-config またはシステムサービスを利用できます。
Pi上で管理ユーザーとして実行します。
sudo apt update
sudo apt install --yes git openssh-server
sudo systemctl enable --now ssh
git --version
systemctl is-active ssh
OSの通常の更新方針に沿ってセキュリティ更新を継続してください。クライアントがSSHホスト鍵を初めて受け入れる前に、信頼できる別経路でPiのアドレスとホスト識別情報を確認します。
3. Git専用アカウントを作る
利用可能なパスワードを持たないアカウントを作り、git-shell を見つけてログインシェルにします。
sudo adduser --disabled-password --gecos '' git
command -v git-shell
grep -Fx /usr/bin/git-shell /etc/shells || printf '%sn' /usr/bin/git-shell | sudo tee -a /etc/shells
sudo chsh -s /usr/bin/git-shell git
getent passwd git
command -v git-shell が /usr/bin/git-shell 以外を示したら、以後はその実パスへ置き換えます。Git公式文書では、git-shell は push、fetch、リモートアーカイブに必要なサーバー側コマンドだけを許す制限付きログインシェルです。通常の対話シェルではありません。
この変更のために /etc/passwd を直接編集しないでください。chsh ならアカウントデータベースを手作業で書き換えずに検証・記録できます。
4. 制限付きSSH鍵を追加する
クライアントごとに専用鍵を作ります。現在のOpenSSHはEd25519をサポートし、Raspberry PiのSSH文書も基本RSA例より安全性を高める選択肢として挙げています。
ssh-keygen -t ed25519 -a 64 -C 'laptop-to-pi-git'
無人処理に別途文書化された鍵保護策がない限り、パスフレーズを設定します。秘密鍵はクライアントだけに置き、サーバーには .pub 公開鍵だけを渡します。
サーバー側のディレクトリを準備します。
sudo install -d -m 0700 -o git -g git /home/git/.ssh
sudo touch /home/git/.ssh/authorized_keys
sudo chown git:git /home/git/.ssh/authorized_keys
sudo chmod 0600 /home/git/.ssh/authorized_keys
クライアントから公開鍵へOpenSSHの restrict オプションを付け、管理アカウント経由で追記します。
{ printf 'restrict '; cat ~/.ssh/id_ed25519.pub; }
| ssh admin@pi-git.local
'sudo tee -a /home/git/.ssh/authorized_keys >/dev/null'
restrict は、その鍵のポート転送、agent転送、X11転送、PTY、~/.ssh/rc を無効にしつつ、Gitに必要なリモートコマンドは許します。人・自動化ジョブごとに識別用コメント付きの独立した鍵行を使えば、他の鍵を交換せず一資格だけ失効できます。
制限をテストします。
ssh -T git@pi-git.local
対話Git shellが無効だというメッセージは想定どおりです。認証に成功した後、対話セッションが拒否されます。
任意:SSH全体の強化
まず管理アカウントにも鍵を設定し、別端末でテストします。その後にだけ /etc/ssh/sshd_config.d/00-local-hardening.conf のようなローカル設定を検討します。
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
再読み込み前に構文と実効値の両方を確認します。
sudo sshd -t
sudo sshd -T | grep -E 'passwordauthentication|kbdinteractiveauthentication|permitrootlogin'
sudo systemctl reload ssh
新しい鍵認証の管理セッションが動くまで、すでに認証済みの管理セッションを閉じないでください。構文が正しくても、意図どおりのアクセス方針とは限りません。
5. ベアリポジトリを作成・検証する
ベアリポジトリはrefs、objects、サーバー側hooksを保存しますが、チェックアウト済み作業ツリーを持ちません。Gitバージョンの既定値に依存せず、ブランチ名を明示します。
sudo install -d -m 0750 -o git -g git /srv/git
sudo -u git git init --bare --initial-branch=main /srv/git/sample.git
sudo -u git git --git-dir=/srv/git/sample.git config receive.denyNonFastForwards true
sudo -u git git --git-dir=/srv/git/sample.git config receive.denyDeletes true
sudo -u git git --git-dir=/srv/git/sample.git config --get-regexp '^receive.'
既存リポジトリを持つクライアントでは次のとおりです。
git remote add pi ssh://git@pi-git.local/srv/git/sample.git
git push -u pi HEAD:main
git ls-remote pi
別の場所へクローンする場合:
git clone ssh://git@pi-git.local/srv/git/sample.git
この二つの receive 設定は、通常ブランチの強制改変と削除を防ぎますが、身元別の認可ではありません。この共有 git アカウントで受理される鍵は、アカウント自身が読書きできる全リポジトリへ到達できます。
6. 選択肢A:小規模な静的ディレクトリ用Hook
main へpushできる全員が公開先へデプロイする権限も持つ場合だけ使います。対象にはデプロイ可能ファイルだけを置き、アップロード、データベース、手編集設定を混在させません。
対象とサーバー管理hookを作ります。
sudo install -d -m 0755 -o git -g git /srv/www/sample
sudoedit /srv/git/sample.git/hooks/post-receive
#!/bin/sh
set -eu
repo=/srv/git/sample.git
target=/srv/www/sample
deploy_ref=refs/heads/main
lock=/srv/git/sample.deploy.lock
while read -r oldrev newrev refname
do
[ "$refname" = "$deploy_ref" ] || continue
if ! git --git-dir="$repo" cat-file -e "$newrev^{commit}" 2>/dev/null
then
printf '%sn' 'The deploy branch was deleted or does not name a commit; skipping.' >&2
continue
fi
(
flock -x 9
git --git-dir="$repo" --work-tree="$target" checkout --force main
) 9>"$lock"
printf 'Deployed %s to %sn' "$newrev" "$target"
done
所有者と実行権限を設定します。
sudo chown git:git /srv/git/sample.git/hooks/post-receive
sudo chmod 0755 /srv/git/sample.git/hooks/post-receive
command -v flock
ブランチ条件はtagや別ブランチのデプロイを防ぎ、commit検証はブランチ削除を処理します。引用したパスは分割を防ぎ、flock はほぼ同時のcheckoutを直列化します。
Gitはrefsを更新した後に post-receive を呼びます。出力はpush元へ転送されますが、非ゼロ終了でも受理済みpushを戻せません。したがって Permission denied は「push保存済み・デプロイ失敗」であり、「push拒否」ではありません。pushを拒否すべき方針には pre-receive または update を使います。
Hooksはサーバー側ファイルで、通常のcloneやpushでは導入されません。インフラ設定としてバックアップ・レビューしてください。
7. 選択肢B:Commitをキューへ入れ、systemdで公開する
アプリや価値の高い静的サイトでは、SSH/Gitアクセスをrelease所有権から分けます。hookは検証済みcommit IDだけをキューへ書き、systemd.path がキューを検知して sample-deploy のサンドボックス化oneshotサービスを起動します。サービスはcommitを新しい不変releaseディレクトリへ展開し、current シンボリックリンクを原子的に切り替えます。
この例も main へpushできる人は全員デプロイを依頼できるという前提です。リポジトリ内のビルドスクリプトは実行しません。
IDとディレクトリを作る
sudo groupadd --system sample-web
sudo useradd --system --home-dir /nonexistent --no-create-home
--shell /usr/sbin/nologin --gid sample-web --groups git sample-deploy
sudo usermod -aG sample-web www-data
sudo chmod -R g+rX /srv/git/sample.git
sudo install -d -m 2770 -o git -g git /var/lib/sample-deploy/queue
sudo install -d -m 0750 -o sample-deploy -g sample-web /srv/www/sample
sudo install -d -m 0750 -o sample-deploy -g sample-web /srv/www/sample/releases
www-data は実際の読み取り専用Webサービスユーザーに置き換え、補助グループ変更後にそのサービスを再起動します。サーバーは /srv/www/sample/current を読み、releasesへの書込み権限を持たないようにします。
単純Hookをキュー専用Hookへ置き換える
#!/bin/sh
set -eu
repo=/srv/git/sample.git
queue=/var/lib/sample-deploy/queue
deploy_ref=refs/heads/main
umask 027
while read -r oldrev newrev refname
do
[ "$refname" = "$deploy_ref" ] || continue
git --git-dir="$repo" cat-file -e "$newrev^{commit}" 2>/dev/null || continue
temporary="$queue/.${newrev}.$$"
printf '%sn' "$newrev" >"$temporary"
mv "$temporary" "$queue/$newrev"
printf 'Queued deployment of %sn' "$newrev"
done
デプロイプログラムを導入する
次を /usr/local/sbin/deploy-sample として保存し、root所有、モード 0755 にします。
#!/bin/sh
set -eu
repo=/srv/git/sample.git
queue=/var/lib/sample-deploy/queue
root=/srv/www/sample
releases=$root/releases
deploy_ref=refs/heads/main
temporary=
archive=
cleanup()
{
[ -z "$archive" ] || rm -f -- "$archive"
[ -z "$temporary" ] || rm -rf -- "$temporary"
}
trap cleanup EXIT
trap 'exit 1' HUP INT TERM
for job in "$queue"/*
do
[ -f "$job" ] || exit 0
commit=${job##*/}
case "$commit" in
''|*[!0-9a-fA-F]*)
printf 'Ignoring invalid deployment job: %sn' "$commit" >&2
rm -f -- "$job"
continue
;;
esac
if ! git --git-dir="$repo" cat-file -e "$commit^{commit}" 2>/dev/null
then
printf 'Commit is unavailable: %sn' "$commit" >&2
rm -f -- "$job"
continue
fi
current=$(git --git-dir="$repo" rev-parse "$deploy_ref^{commit}")
if [ "$commit" != "$current" ]
then
printf 'Skipping superseded deployment: %sn' "$commit"
rm -f -- "$job"
continue
fi
release=$releases/$commit
if [ ! -d "$release" ]
then
temporary=$releases/.${commit}.$$
archive=$releases/.${commit}.$$.tar
install -d -m 0750 "$temporary"
git --git-dir="$repo" archive --format=tar --output="$archive" "$commit"
tar -xf "$archive" -C "$temporary"
if find "$temporary" -type l -print -quit | grep -q .
then
printf '%sn' 'Symlinks are not permitted in this static release.' >&2
rm -f -- "$job"
exit 1
fi
rm -f -- "$archive"
archive=
mv "$temporary" "$release"
temporary=
fi
current=$(git --git-dir="$repo" rev-parse "$deploy_ref^{commit}")
if [ "$commit" != "$current" ]
then
printf 'Branch advanced while preparing: %sn' "$commit"
rm -f -- "$job"
continue
fi
next=$root/.current.$$
ln -s "$release" "$next"
mv -Tf "$next" "$root/current"
rm -f -- "$job"
printf 'Published %sn' "$commit"
done
このプログラムは追跡済み内容をエクスポートするだけで、実行しません。2回のブランチ先端チェックで古くなったキュー項目を捨て、ハッシュ名順の処理で古いコミットが最後に公開されることを防ぎます。最終的なシンボリックリンク切り替え中に新しいpushが届いた場合も、そのキュー項目が残って次のサービス実行を起動します。ビルドが必要なら、明示した脅威モデルと資源制限の下で別途レビューしたビルド段階を追加します。pushは送信者が認証済みでも信頼できない入力です。
Path・Serviceユニットを加える
/etc/systemd/system/sample-deploy.path:
[Unit]
Description=Watch for queued sample deployments
[Path]
DirectoryNotEmpty=/var/lib/sample-deploy/queue
Unit=sample-deploy.service
[Install]
WantedBy=multi-user.target
/etc/systemd/system/sample-deploy.service:
[Unit]
Description=Publish a queued sample Git commit
[Service]
Type=oneshot
User=sample-deploy
Group=sample-web
SupplementaryGroups=git
ExecStart=/usr/local/sbin/deploy-sample
NoNewPrivileges=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
ReadOnlyPaths=/srv/git/sample.git
ReadWritePaths=/srv/www/sample /var/lib/sample-deploy
CapabilityBoundingSet=
RestrictAddressFamilies=AF_UNIX
検証して有効化します。
sudo systemd-analyze verify
/etc/systemd/system/sample-deploy.path
/etc/systemd/system/sample-deploy.service
sudo systemctl daemon-reload
sudo systemctl enable --now sample-deploy.path
sudo systemd-analyze security sample-deploy.service
sudo systemctl status sample-deploy.path
systemd-analyze verify はユニット記述の誤りを検出し、systemd-analyze security はサンドボックス設定から推定される露出を報告します。しかし、どちらもデプロイプログラムの正しさを証明しません。実際にpushした後、次を確認します。
sudo journalctl -u sample-deploy.service --since today
readlink -f /srv/www/sample/current
8. 検証・復旧チェックリスト
初期設定後と重要な変更後に実行します。
sudo sshd -t
sudo -u git git --git-dir=/srv/git/sample.git fsck --full
git ls-remote pi
次の運用事実も確認します。
- 対話的な
ssh git@hostが拒否される - 許可したpushとcloneが動く
- tagや非デプロイブランチが公開されない
- デプロイブランチ削除でサイトが空にならない
- hook・デプロイエラーがログに残り、push拒否と誤認されない
currentを以前のreleaseへ戻してロールバックできる- ベアリポジトリ、hooks、SSH鍵、ユニット、デプロイスクリプトがバックアップ対象である
- 別パスまたは別マシンで復元を実際に試している
2019年手順から変更した点
| 2019年アーカイブ | 2026年保守版 |
|---|---|
| パスフレーズ方針なしでRSA鍵を生成 | 専用Ed25519鍵を使い、所有、パスフレーズ、制限、失効を記録 |
/etc/passwd を直接編集 | chsh と導入済み git-shell の実パスを使用 |
| リポジトリと公開先が複数ホームに混在 | ベアリポジトリは /srv/git、公開先は /srv/www、所有者を明示 |
常に master と仮定 | main を明示し、完全なref refs/heads/main を判定 |
GIT_DIR の例が先に作ったリポジトリと不一致 | 一貫した絶対リポジトリパスを使用 |
| 公開権限エラーの後にもpush成功行 | post-receive は受理済みpushを戻せないと説明 |
chown git:git を一般的な修正にする | 低リスク静的公開だけに限定し、強い分離では専用デプロイIDを使用 |
| コピーしたコマンドに組版ダッシュと曲線引用符 | アーカイブだけに保存し、保守コマンドは実行可能なASCIIを使用 |
| 第三者ホストとプライベートIPをコピー用に見せる | 歴史文字列として扱い、現行推奨・推薦とはしない |
権威ある資料
- Raspberry Pi文書:SSHの有効化と公開鍵認証
- Git文書:`git init`、ベアリポジトリ、初期ブランチ指定
- Git文書:受信側の履歴保護設定
- Git文書:`git-shell` による制限付きSSHアクセス
- Git文書:Hookの実行と `post-receive` の意味
- OpenSSH手引:`ssh-keygen` の鍵種別と秘密鍵保護
- OpenSSH手引:`authorized_keys` の制限
- OpenSSH手引:サーバー設定
- systemd上流マニュアルソース:実行サンドボックス指令
- systemd上流マニュアルソース:Pathユニット
- systemd上流マニュアルソース:Serviceユニット
- systemd上流マニュアルソース:`systemd-analyze verify` と `security`
パッケージの版と既定値は変わります。公開・自動化の前にPi上の実マニュアル(man git-init、man githooks、man sshd_config、man systemd.exec)を読み、導入版で検証してください。
2019年ソース・エクスポート全文(原様保存・実行禁止)
1, Set up git envioronment
sudo apt-get install git
sudo adduser git
2, Generate public and private key
ssh-keygen -t rsa -C “user@lazying.art”
Client:
/home/pi/.ssh/id_rsa
Server: add public key of each client to the file below
/home/git/.ssh/authorized_keys
3, Initialize server end envioronment
mkdir Git
cd Git
sudo git init --bare sample.git
sudo chown -R git:git sample.git
vim /etc/passwd
git:x:1001:1001:,,,:/home/git:/bin/bash
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
4, Upload code to your github server
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git add .
git commit -m "init commit"
git push -u origin master
5, Set-up Hooks
vim sapmle.git/hooks/post-receive
Add code below to post-receive
#!/bin/bash
TARGET="/home/webuser/deploy-folder"
GIT_DIR="/home/webuser/www.git"
BRANCH="master"
while read oldrev newrev ref
do
# only checking out the master (or whatever branch you would like to deploy)
if [ "$ref" = "refs/heads/$BRANCH" ];
then
echo "Ref $ref received. Deploying ${BRANCH} branch to production..."
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f $BRANCH
else
echo "Ref $ref received. Doing nothing: only the ${BRANCH} branch may be deployed on this server."
fi
done
chown git:git hooks/post-receive
chmod +x hooks/post-receive
Counting objects: 2, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 237 bytes | 23.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
remote: Ref refs/heads/master received. Deploying master branch to production…
remote: error: unable to create file main.py (Permission denied)
remote: error: unable to create file test.py (Permission denied)
remote: Already on 'master'
To 192.168.1.108:/home/pi/Git/printer.git
2e6a796..6085cd0 master -> master
Causing of the hooks script cannot access deployment files, you should change the owner of deployment directory to git
chown git:git /path/to/deployment/directory/
Counting objects: 2, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 239 bytes | 21.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
remote: Ref refs/heads/master received. Deploying master branch to production…
remote: Already on 'master'
To 192.168.1.108:/home/pi/Git/printer.git
6085cd0..b83c0d5 master -> master
________
1.在文件所在位置打开终端,输入如下命令,进行git全局设置:
git config –global user.name “用户名”
git config –global user.email “用户邮箱”
1. 安装如下命令创建新的仓库,网址为自己新建项目的网址,cd转到自己要上传的项目文件夹:
git clone https://git.aiiage.com:9999/song.yl/ReID.git
cd ReID
touch README.md
git add README.md
git commit -m “add README”
git push -u origin master
1. 对已经存在的文件夹进行操作, 可以不用cd命令转到文件夹,直接到文件夹下打开终端执行如下命令,其中git commit命令后引号里面的内容可以自己命名:
cd existing_folder
git init
git remote add origin https://git.aiiage.com:9999/song.yl/ReID.git
git add .
git commit -m “Initial commit”
git push -u origin master
1. 对存在的git仓库进行操作。依然可以直接在所在文件夹直接打开终端执行命令:
cd existing_foloder
git remote rename origin old-origin
git remote add origin https://git.aiiage.com:9999/song.yl/ReID.git
git push -u origin —all
git push -u origin –tags
## 作者:yllifesong
来源:CSDN
原文:https://blog.csdn.net/yllifesong/article/details/81041156
版权声明:本文为博主原创文章,转载请附上博文链接!
