DevOps Server / Team Foundation Server でリポジトリの複製で "SSL certificate problem: unable to get local issuer certificate" が発生しリポジトリにアクセスできない現象について紹介します。
Team Foundation Server のGitリポジトリを複製すると、下記のエラーが表示され接続できません。
Visual Studioから利用しているGitは利用しているPCのWindowsにインストールされている証明書は利用せず、Git独自の証明書を利用しています。そのため、独自の認証局で署名された証明書や自己証明書をDevOps Serverの証明書に利用し、接続するPCにCAの証明書をインストールしても、接続先のDevOps Serverのサーバーの証明書が信頼されている証明書と判断できないため、署名の確認に失敗し、接続ができません。
Visual Studioから呼び出されるGitは下記のフォルダのGitです。
Gitが利用する証明書は下記のフォルダのバンドル証明書が利用されます。
以下の対策があります。
SymantecやLet's Encrypt などのメジャーな認証局によって署名されたSSL証明書を利用します。
メジャーな認証局であれば、Gitのバンドルされた証明書でも信頼されている証明書と判断でき接続ができます。
イントラネットのサーバーでは、Let's Encryptの証明書を取得しづらい問題がありましたが、DNS認証やワイルドカード証明書の取得がサポートされたため、
Let's Encrypt の証明書をDevOps Server に導入することも比較的簡単になりました。
Let's Encrypt のワイルドカード証明書の取得はこちらの記事を、
オフライン環境用にpfxファイルを取得する方法はこちらの記事を参照してください。
Gitの設定を変更してWindowsにインポートされた証明書を利用する動作に変更できます。変更するには下記のコマンドを実行します。
今回の例では下記のコマンドとなります。
なお、元に戻す場合は下記のコマンドとなります。
設定変更により動作するはずですが、実際に試してみたところ、下記のエラーが発生しVisual StudioからGitのリポジトリにはアクセスできませんでした。
または
内部のサーバーに費用をかけて証明書を取得したくない場合や、Let's Encrypt を利用して3か月ごとに証明書を更新する運用をしたくない場合、組織内のCAで署名された証明書を利用したいことがあります。この場合は、組織内のCAの証明書を指定することで上記の問題を解決できます。
認証局(CA)の証明書を取得し、クライアントマシンに配置します。証明書は Base 64でエンコードされた形式の必要があります。Base 64でエンコードされたCAの証明書の取得方法はこちらの記事を参照してください。
証明書を配置するパスはどこでも問題ありませんが、他の証明書と同じ位置にしておいたほうが良いため、下記のフォルダに配置します。
PowerShellを管理者権限で起動し、下記のコマンドを実行します。
今回の例では下記のコマンドとなります。
コマンドが実行できるとエラーなどは表示されず、次のコマンドの入力待ちになります。
または、サイトを指定する下記コマンドも利用可能です。(未検証)
Visual Studioでリポジトリの複製を実行します。エラーは発生せず、リポジトリの複製が実行できます。
先の方法で設定した証明書のリポジトリにはアクセスできるようになりますが、参照する証明書を変更したため、GitHubなどと併用する場合や、独自のCA証明書を持った他のリポジトリへのアクセスはできなくなってしまいます。複数の証明書を利用する場合は、ca-bundle.crtファイルを更新する方法が良いです。
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs\
ディレクトリを開きます。ca-bundle.crt ファイルが見つかります。
ca-bundle.crt を編集するため、いったんデスクトップなどの作業フォルダにファイルをコピーします。(Program File ディレクトリ配下のディレクトリでは直接編集できない場合があります。)
テキストエディタで"ca-bundle.crt"ファイルを開きます。
下にスクロールすると -----END CERTIFICATE-----
で区切りがあることがわかります。複数の証明書が"ca-bundle.crt"ファイル内に記述されていることがわかります。
追加したいCA証明書をテキストファイルで開きます。CA証明書はBASE64形式で出力しておく必要があります。BASE64形式でCAの証明書を出力する手順はこちらの記事を参照してください。
CAの証明書のテキストを"ca-bundle.crt"の末尾にペーストして追記します。複数の証明書を追加する場合は、続けて末尾に証明書のテキストを追加します。
ca-bundle.crtファイルを保存し、元のディレクトリC:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs\
に上書きします。
他の証明書を参照している設定になっている場合は、下記のコマンドを実行して参照する証明書をca-bundle.crtに変更します。
設定は以上です。Gitでリポジトリにアクセスできるかを確認します。
先のca-bundle.crtを更新する方法で問題なく動作しますが、Visual Studioのアップデートのたびにca-bundle.crtが更新されてしまうため、アップデートのたびにca-bundle.crtを更新する必要があります。
組織の証明書を別ファイルにして接続先のドメインごとにCAの証明書を切り替える方法を紹介します。(先の対策3のドメイン指定と同じものです。)
はじめに自組織のCA証明書を配置します。ca-bundle.crt ファイルのあるディレクトリに一緒に配置しておくのが良いかと思われます。
次に、C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\etc\
ディレクトリの gitconfigファイルを編集します。
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\etc
に移動している場合があります。下記のhttpセクションの手前に
[http]
sslCAinfo = /ssl/certs/ca-bundle.crt
以下の記述を追記します。
[http "接続先URL"]
sslCAinfo = (上記の接続先URLで利用する証明書ファイル)
[core]
symlinks = false
autocrlf = true
[color]
diff = auto
status = auto
branch = auto
interactive = true
[pack]
packSizeLimit = 2g
[help]
format = html
[http]
sslCAinfo = /ssl/certs/ca-bundle.crt
[diff "astextplain"]
textconv = astextplain
[rebase]
autosquash = true
[filter "lfs"]
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
process = git-lfs filter-process
required = true
[core]
symlinks = false
autocrlf = true
[color]
diff = auto
status = auto
branch = auto
interactive = true
[pack]
packSizeLimit = 2g
[help]
format = html
[http "(リポジトリのURL)"]
sslCAinfo = /ssl/certs/iPentec-CA.cer
[http]
sslCAinfo = /ssl/certs/ca-bundle.crt
[diff "astextplain"]
textconv = astextplain
[rebase]
autosquash = true
[filter "lfs"]
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
process = git-lfs filter-process
required = true
対策5でも問題なく利用できますが、Visual Studioのアップデートでgitconfigが移動してしまうなどの問題があります。また、特定のリポジトリのみの証明書のためグローバル設定に入れないほうが良いといった理由もあります。対策5の記述をグローバルのgitconfigではなく、リポジトリのローカルのconfigに記述する方法です。
(リポジトリのディレクトリ)\.git\config
にそのリポポジトリの設定が記載されていますので、その中に下記のセクションを記述します。
[http "接続先URL"]
sslCAinfo = (上記の接続先URLで利用する証明書ファイル)
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[submodule]
active = .
[remote "origin"]
url = (リポジトリのURL)
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[http "(リポジトリのURL)"]
sslCAinfo = /ssl/certs/iPentec-CA-ACA.cer
[submodule]
active = .
[remote "origin"]
url = (リポジトリのURL)
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master