zaki work log

作業ログやら生活ログやらなんやら

共用アカウント環境における個人用GitとSSHの設定

本記事は「エーピーコミュニケーションズ Advent Calendar 2022」の2日目のエントリとなります。

Gitを使う作業を個人に与えられたOSアカウントで行う分には何も不都合はないけれど、共用サーバーの共用アカウントを複数人で使用する場合、コミット時の名前やリモートリポジトリへの認証の設定を工夫しないと他の人と設定が混じるので注意が必要。

よくあるユースケースとしては、プロダクション環境やステージング環境で作業用のOSアカウントをプロジェクトメンバ毎に作らずに、運用に使用する最小限の作業・管理用アカウントしかホスト上にない、とか。場合によっては開発環境でも。
イメージとしては、作業ホストはAWSのEC2上でec2-userユーザーをプロジェクトメンバ全員で共用するような感じ。でもGitのリモートリポジトリはGitHubやGitLab上にプロジェクトメンバ個人ごとのアカウントがある、という構成。

「私はこうやってる」という話なので、こっちの方法が簡単だよーとかあったら教えてください。($HOMEを更新するのは影響が大きそうなので避けてます)

(おさらい) 普通にGitを使う場合のユーザー設定

Gitのリポジトリを作るかGitHubからリポジトリをクローンして、何も設定してない状態でコミットしようとすると、

Author identity unknown

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

みたいなエラーメッセージが表示される。
出力されている通りにユーザー名とメールアドレスを設定するとコミット時のユーザー名・アドレスが記録されるようになるが、このコマンドを実行すると、実行ユーザーのホームディレクトリ直下の.gitconfigファイルに設定が保存される。

$ git config --global user.name zaki
$ cat $HOME/.gitconfig
[user]
        name = zaki

これはOSのアカウントが自分専用であれば何も問題ないが、前述の通り共用アカウント環境で設定すると他の利用者(例えばプロジェクトメンバ)も同じ設定になってしまうし、逆に他の利用者がその人の名前で設定しまうと、自分のコミットが他の利用者の名前になってしまい都合が非常に悪い。

(解決策1) リポジトリ単位のユーザー設定

「Gitリポジトリ作成(またはクローン)のあと」に、git configのオプションで--globalでなく--localを付与してユーザー設定することで、$HOME/.gitconfigでなく、リポジトリローカルの.git/configへ設定保存できる。

$ git config --local user.name zaki
$ cat .git/config 
:
:
[user]
        name = zaki

このファイルの設定内容は$HOME/.gitconfigより優先されるので、OSアカウントの設定にかかわらずにクローンしたリポジトリ内に設定されたユーザー名でコミットを実行できるため、他の利用者の設定と区別できる。
(※ あくまでクローンしたリポジトリは自分しか使わないという前提で…)

ただしこの方法にも事故りやすい以下の欠点がある。

  • クローンするたびにgit config --localを使ったユーザー設定が必要で面倒
  • git config --localするのを忘れてかつ、$HOME/.gitconfigに他の利用者の設定があると気付かないうちに他の利用者名でコミットしてしまう

後者については、コミット直後であれば--amend--authorを使って比較的修正は簡単だけど、いくつもコミットがある場合はrebaseが必要なのでちょっと大変。この場合は「git commit author 変更」あたりでググると情報がヒットする。

(解決策2) 環境変数を使ったGitユーザー設定

git configを使った設定の欠点を回避する方法としては、環境変数の設定が個人的にはお勧め。

  • git config --globalはもちろんgit config --localの必要もないのでクローンの度に設定する必要がない
  • $HOME/.gitconfigが存在していても設定を上書きする

設定する環境変数は以下の通り。ユーザー名とメールアドレスでCommiterとAuthorの2つずつある。
(ComitterとAuthorの違いはググってね)

  • GIT_AUTHOR_NAME
  • GIT_COMMITTER_NAME
  • GIT_AUTHOR_EMAIL
  • GIT_COMMITTER_EMAIL

ただしこの環境変数の設定を$HOME/.bashrcとかのシェル起動時に設定するファイルで行うと(全利用者が同じユーザー設定になってしまって)無意味なので、ちょっと面倒だけど「自分用の環境変数ファイルを別途作成」し、ログインのたびに読み込むようにする。
私の場合はいつもホスト上に$HOME/(自分の名前)-work/というディレクトリを作って、そこに個人用設定ファイルなどを作成してる。環境変数の場合はだいたいいつもenv.sourceというファイルに記述している。
(名前にそんなに意味はなくてsourceコマンドで読み込むから、程度。rcの方が良いかもね。実際のところsou[tab]より.の方がタイプが速いのでsourceコマンドは使ってない笑)

Gitコミット関連の環境変数はこんな感じ。

export GIT_AUTHOR_NAME=zaki
export GIT_COMMITTER_NAME=zaki
export GIT_AUTHOR_EMAIL=zaki@email.example.org
export GIT_COMMITTER_EMAIL=zaki@email.example.org

この設定が必要な作業ホストへSSH接続するクライアントが自分専用アカウント($HOME/.ssh/configを自分専用にイジれる個人用に貸与されたPCなど)なのであれば、次のような設定でSSH接続後に任意のコマンドを自動実行することもできる。

Host target
    hostname 192.168.0.0
    identityfile ~/.ssh/id_rsa
    RemoteCommand . ~/zaki-work/env.source; bash
    RequestTTY true

共用アカウント環境におけるGit + SSHの個人設定

結論から言うと、環境変数GIT_SSH_COMMANDを使って設定すれば良い感じにできる。

共用アカウント環境のコミット時のユーザー設定は前述の通り環境変数を使って良い感じに個人設定できるけど、リモートリポジトリへプッシュするときに困ったのがSSHの設定(というか秘密鍵設定)。デフォルトでは$HOME/.ssh/configを参照するし、ssh実行時に設定ファイルを変更したい場合は-Fオプションでファイル変更できるけどgit実行時にSSHの設定ファイルを指定する専用オプションもぱっと見では見当たらず。かといってHTTPSでリモートリポジトリ設定すると、認証情報をリポジトリURLへ埋め込むかpush/pullの度に毎回入力するハメになるため煩わしい。

そこで使うのが試行錯誤した結果、環境変数GIT_SSH_COMMANDを使うことで、Git実行時に参照するSSHの設定ファイル(デフォルト$HOME/.ssh/config)をカスタムするオプションを渡すことができる。
具体的には、例えばSSHの設定ファイルとしてデフォルトでなく$HOME/zaki-work/.ssh/configを使用したい場合(sshの場合であれば-F $HOME/zaki-work/.ssh/configとする場合)は、以下の通り。

export GIT_SSH_COMMAND="ssh -F $HOME/zaki-work/.ssh/config"

これで、GitコマンドからSSHを使う場合に$HOME/zaki-work/.ssh/configを参照して実行できる。
$HOME/zaki-work/.ssh/configファイルには、$HOME/.ssh/configと同じ書式で自分が使用したい秘密鍵やユーザー名などを設定すればOK。

Host github.com
    identityfile ~/zaki-work/.ssh/my_id_rsa

GIT_SSH_COMMANDに直接鍵ファイルのパスも指定できるけどssh_configファイルを指定しておくといろいろ設定の幅が広がると思う。

秘密鍵へのアクセスについては共用アカウントの利用者は全員見ることができるけどそこは妥協点(どう考えても仕方ない)。そもそも個人アカウントがあったとしても同じホストでrootに昇格できるなら条件は同じ。

その他のGit便利設定(git-prompt)

共用アカウントで「自分用環境変数ファイル」を読み込んだときにPS1環境変数を細工してプロンプトを変化させておけば、自分用の環境がセットされていることが分かりやすい。
そこで、設定自体は共用アカウント特有の内容じゃないけど、git promptを使用し便利なGitリポジトリの状態を表示するプロンプトも設定しておくと一石二鳥。
設定を共用アカウント全体でやるなら他利用者とネゴっておきましょう。

インストールは通常はGitHubにあるソースを直接ダウンロードしてるが、疎通のない環境であればコピペで作成するのも可。

curl -L https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh -o .git-prompt.sh
chmod 755 .git-prompt.sh

主な設定は以下。(.git-prompt.sh~/zaki-work以下にある場合の設定)

if [ -f ~/zaki-work/.git-prompt.sh ]; then
    source ~/zaki-work/.git-prompt.sh
    PS1='\[\e[31m\]$(__git_ps1 "(%s) ")\[\e[0m\]'"$PS1"
    GIT_PS1_SHOWDIRTYSTATE=true
    GIT_PS1_SHOWUNTRACKEDFILES=true
    GIT_PS1_SHOWSTASHSTATE=true
    GIT_PS1_SHOWCONFLICTSTATE=yes
    GIT_PS1_SHOWUPSTREAM=auto
fi

プロンプトがこのようになり、今いるブランチや、未コミットのファイルやコンフリクトの有無などを表示してくれる。

(master *%>) [zaki@cloud-dev dev]$ 

環境変数を使った主な表示フラグについては以下。

設定 効果
GIT_PS1_SHOWDIRTYSTATE unstage(*)、staged(+)のファイルの変更の有無を表示
GIT_PS1_SHOWUNTRACKEDFILES untrackedなファイルの有無を文字'%`で表示
GIT_PS1_SHOWSTASHSTATE stashの有無を文字$で表示
GIT_PS1_SHOWCONFLICTSTATE yesにすると未解決のコンフリクトが残ってる場合にCONFLICT表示
GIT_PS1_SHOWUPSTREAM autoにするとHEADとupstream(リモートリポジトリ)との差分の有無を確認できる。未pushがあるかどうか等

ちなみにtureをセットしている環境変数は、falseにするとオフになるわけでなく、「空欄で無ければ有効」という動作。無効にするには環境変数を削除するか、空白文字をセットする。

その他の設定や詳細についてはソースのコメント参照。

github.com

設定の確認

Gitの設定ファイルによる現在の設定値は以下のコマンドで確認可能。

git config -l

--global--localを付与することで、設定箇所個別に確認もできる。

git config --system -l
git config --global -l
git config --local -l

ただしあくまで設定ファイルの状態が出力されるため、環境変数による上書きはこのコマンドでは確認できない。
環境変数の設定はenv | grep -i gitなどで確認するしかない(多分)のでその点は注意。

参考

manの日本語訳ページは便利だけど古い場合が多いので気を付けましょう:)
(SSHとか便利な新機能が結構あったりするけど20世紀版とかヒットするので)

環境

  • OS: Fedora 35
  • Git: git version 2.37.3