独自(root)CA のインストール方法

独自の root CA 証明書をインストールする必要があり、まとめました。
色々とツールやアプリケーションを使っていると「OS に設定すればひと安心」というわけにはいかず、OS の設定に加えて、それぞれ個別に設定する必要があるものが少なくありません。

この記事では、以下の環境、ツールへの独自 root CA 証明書を設定する方法を記載します。

1. Ubuntu (18.04)
2. Red Hat / CentOS (7)
3. Python - certifi/requests/ssl
4. Mozilla Firefox
6. Mozilla Thunderbird
7. Windows 10
8. Splunk








# Ubuntu (18.04)

1. `/usr/share/ca-certificates/` の下に適当な(いい加減な、という意味ではなく)ディレクトリを作成します。
ここでは、*mylocal* とします。（以下、`/usr/share/ca-certificates/`*mylocal*`/` とする)
ディレクトリは管理組織単位などでまとめるためのもの。
(おそらく、`mozilla/` というディレクトリがあると思われますが、これは触らない)
ディレクトリは無くても構いませんが、ローカルなものは、ディレクトリでまとめた方が、後々、管理しやすいと思います。

2. 作成したフォルダ *mylocal* の下に、証明書ファイルを置きます。
ここでは、*mylocal-root-cacert.crt* とします。(`/usr/share/ca-certificates/`*mylocal*/*mylocal-root-cacert.crt*)

3. `/etc/ca-certificates.conf` に、追加したファイルの `/usr/share/ca-certificates/` 下の相対パスを追加します。
つまり、上の例では、 "*mylocal*/*mylocal-root-cacert.crt*" を追加します。

4. `update-ca-certificates` (`/usr/sbin/update-ca-certificates`) を実行します。

5. `/etc/ssl/certs/` 下に `/usr/share/ca-certificates/`*mylocal*/*mylocal-root-cacert.crt* へのシンボリックリンクができていることを確認してください。

```shell_session:Ubuntu18.04におけるCA更新手順
# cd /usr/share/ca-certificates
# mkdir mylocal
# cp somewhere/mylocal-root-cacert.crt mylocal/
# cp -p /etc/ca-certificates.conf /etc/ca-certificates.conf.bak
# echo "mylocal/mylocal-root-cacert.crt" >> /etc/ca-certificates.conf
# update-ca-certificates
# ls -l /etc/ssl/certs/ | grep mylocal-root-cacert
```

余談ですが、これが正しく行われると、CA bundle としてすべての CA 証明書が含まれる `/etc/ssl/certs/ca-certificates.crt` が作成されているので、必要に応じて、これを参照すると良いでしょう。例えば、python の `requests()` の引数に CA Bundle のパスを指定するような場合です。

`update-ca-certificates` が自動的に `/etc/ssl/certs/ca-certificates.crt` をアップデートするので、独自に追加するのは、厳禁です。








# Red Hat / CentOS (7)

参照: [Red Hat Enterprise Linux 7 | Red Hat Customer Portal - 共有システムの証明書の使用](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/security_guide/sec-shared-system-certificates)

`/usr/share/pki/ca-trust-source/anchors/` 下に証明書ファイルを置き、`update-ca-trust` を実行します。

```
# cp somewhere/mylocal-root-cacert.crt /usr/share/pki/ca-trust-source/anchors/
# update-ca-trust
# ls -l /etc/pki/ca-trust/extracted/openssl/
```

`/etc/pki/ca-trust/source/anchors/` 下でも同様です。
`/usr/share/pki` はバックワードコンパチビリティのためにあり、`/etc/share/pki` の設定が優先されます。
どちらにも設定されるように、ここでは `/usr/share/pki/ca-trust-source/anchors/` 下に証明書を設置しています。

現在は、`update-ca-trust` に引数を与えても無視されますが、スクリプトの中を覗くと、コメントに将来的に "`update-ca-trust extract`" をサポートするとの記述があります。

```shell:(CentOS7)&nbsp;/bin/update-ca-trust
#!/bin/sh

#set -vx

# At this time, while this script is trivial, we ignore any parameters given.
# However, for backwards compatibility reasons, future versions of this script must
# support the syntax "update-ca-trust extract" trigger the generation of output
# files in $DEST.

DEST=/etc/pki/ca-trust/extracted

# OpenSSL PEM bundle that includes trust flags
# (BEGIN TRUSTED CERTIFICATE)
/usr/bin/p11-kit extract --comment --format=openssl-bundle --filter=certificates --overwrite $DEST/openssl/ca-bundle.trust.crt
/usr/bin/p11-kit extract --comment --format=pem-bundle --filter=ca-anchors --overwrite --purpose server-auth $DEST/pem/tls-ca-bundle.pem
/usr/bin/p11-kit extract --comment --format=pem-bundle --filter=ca-anchors --overwrite --purpose email $DEST/pem/email-ca-bundle.pem
/usr/bin/p11-kit extract --comment --format=pem-bundle --filter=ca-anchors --overwrite --purpose code-signing $DEST/pem/objsign-ca-bundle.pem
/usr/bin/p11-kit extract --format=java-cacerts --filter=ca-anchors --overwrite --purpose server-auth $DEST/java/cacerts
```

設定した信頼済み CA 証明書の管理は `trust` コマンドで行います。

```shell_session:trustコマンド
$ trust
usage: trust command <args>...

Common trust commands are:
  list             List trust or certificates
  extract          Extract certificates and trust
  extract-compat   Extract trust compatibility bundles
  anchor           Add, remove, change trust anchors
  dump             Dump trust objects in internal format

See 'trust <command> --help' for more information
```

trust コマンドを用いることで、設定済み CA の削除、変更ができますが、ここでは、省略します。

# Python - certifi/requests/ssl

**注意！！: OS の設定を確認してから、Python 環境の証明書追加作業をしてください。**

---
python で使われる certifi/requests/ssl モジュールの設定をまとめます。







## python - certifi/requests の CA 設定
requests を使用している場合には、certifi パッケージに依存している (requests 2.4 から、certifi を使用しているとのこと。参照: [CA Certificates](https://2.python-requests.org/en/v2.7.0/user/advanced/#ca-certificates)) ので、OS の参照先と異なる場合には、 certifi の CA bundle に追加します。


> From Requests version 2.4.0 onwards, Requests will attempt to use certificates from certifi if it is present on the system. This allows for users to update their trusted certificates without having to change the code that runs on their system.

以下のようにして、参照先を確認できます。

```shell_session:certifi/requests用証明書bundleの確認
$ python3 -c "import certifi; print(certifi.where())"
/etc/ssl/certs/ca-certificates.crt

$ python3 -c "import requests;print(requests.__version__)"
2.22.0

$ python3 -c "import requests; print(requests.certs.where())"
/etc/ssl/certs/ca-certificates.crt
```

OS の設定(例えば、Ubuntu 18.04 の openssl の場合には、`/etc/ssl/certs/ca-certificates.crt`) と同じものを参照している場合には、独自に追加する必要はありません。

OS と異なるファイルを参照している場合 (例えば、仮想環境など) には、表示されたパスのファイルに、text 形式の証明書を追加すれば、終了です。









以下の python スクリプトで同じことをしています。
カレントディレクトリにインストール証明書を `mylocal-root-cacert.crt` という名前で置き、Python スクリプトを実行します。

```python:local_ca_install.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import certifi

cabundle = certifi.where()
local_rootCA = 'mylocal-root-cacert.crt'

print( 'read from {}'.format( local_rootCA ) )
with open( local_rootCA, 'rb' ) as infile:
    myrootca = infile.read()

print( 'append to {}'.format( cabundle ) )
with open( cabundle, 'ab' ) as outfile:
    outfile.write( myrootca )

print( '{} has been imported.'.format( local_rootCA ) )
```

```shell_session:実行結果
(pythonsample) $ python local_ca_install.py
read from mylocal-root-cacert.crt
append to /home/.../envs/pythonsample/lib/python3.6/site-packages/certifi/cacert.pem
mylocal-root-cacert.crt has been imported.
(pythonsample) $
```
















## python - ssl の設定

ssl モジュールの参照先を確認します。
参照先のディレクトリが違っても、シンボリックリンクで、requests/certifi と同じ実体のディレクトリ/ファイルを参照していることがありますので、参照先も確認します。

```shell_session:ssl用証明書bundleの確認
$ python3 -c "import ssl; print(ssl.get_default_verify_paths())"; (set -x; ls -l /usr/lib/ssl)
DefaultVerifyPaths(cafile=None, capath='/usr/lib/ssl/certs', openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/usr/lib/ssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/usr/lib/ssl/certs')
+ ls -l /usr/lib/ssl
total 4
lrwxrwxrwx 1 root root   14 Apr 23  2018 certs -> /etc/ssl/certs
drwxr-xr-x 2 root root 4096 Nov 20 06:52 misc
lrwxrwxrwx 1 root root   20 Nov 13 01:58 openssl.cnf -> /etc/ssl/openssl.cnf
lrwxrwxrwx 1 root root   16 Apr 23  2018 private -> /etc/ssl/private
```

参照先の実体が異なる場合には、openssl のディレクトリと同じ構成か、CA bundle を参照するように構成します。


## python - 環境変数による変更

環境変数によって、参照先を変更できます。





### 環境変数 `REQUESTS_CA_BUNDLE` - requests
参照: [SSL Cert Verification](https://2.python-requests.org/en/v2.7.0/user/advanced/?highlight=requests_ca_bundle#ssl-cert-verification)

* requests を使用する場合は、環境変数 `REQUESTS_CA_BUNDLE` に CA Bundle (つまり、上の例では `/home/.../envs/pythonsample/lib/python3.6/site-packages/certifi/cacert.pem` を設定することで、requests() はその CA を参照します。

* また、環境変数ではありませんが、 requests.get/post の引数として、CA Bundle ファイルのパスを指定することができます( requests.get(..., verify='*CA Bundle ファイルへのパス*' ) です)。
ただし、パスをハードコードした場合には、汎用性が低くなりますので、注意が必要です。

* verify を False にするのは最後の手段。一時的なテスト等に留めておくことをおすすめします。





### 環境変数 `SSL_CERT_DIR` と `SSL_CERT_FILE` - ssl

* ssl モジュールで参照している環境変数は `SSL_CERT_DIR` と `SSL_CERT_FILE` です。  
(ネットに色々と情報が転がっているものの、`SSL_CERT_DIR` と `SSL_CERT_FILE` に関する一次ソースが見つけられませんでした)




## Conda/Anaconda 環境
Conda/Anaconda でも同様です。

Anaconda 利用の場合には、conda 環境で、certifi の cabundle に証明書を追加します。
Windows などでは、インストールしている場所によっては、AppData 下など、隠しファイル (フォルダ) の表示が必要になるかもしれません。



## python 仮想環境
Python 仮想環境を使用している場合には、それぞれの仮想環境で設定が必要です。







# Mozilla Firefox

Microsoft Windows 上の Firefox は、 Microsoft Windows とは別の証明書ストアで管理している (Linux, MacOS では未確認) ので、Microfoft Windows への証明書の設定とは別に、管理が必要です。(未確認ですが、プロファイルごとに設定する必要があると思われます)

`[オプション]` - `[プライバシーとセキュリティ]` - 「証明書」- `[証明書を表示...(C)]` - `[認証局証明書]` - `[インポート(M)...]` で、証明書の読み込みです。

Firefox を使っている方なら、この説明で十分でしょう。


---

[Firefox: How to audit & reset the list of trusted servers/CAs - Red Hat Customer Portal](https://access.redhat.com/solutions/1549043)

---

# Mozilla Thunderbird

Thunderbird は、Microsoft Windows とは別の証明書ストアで管理している (Linux, MacOS では未確認) ので、Microfoft Windows への証明書の設定とは別に、管理が必要です。(未確認ですが、プロファイルごとに設定する必要があると思われます)

`[オプション]` - `[詳細]` - `[証明書]`(タブ) - `[証明書を管理(M)]` - `[認証局証明書]` - `[インポート(M)...]` で、証明書の読み込みです。


Thunderbird を使っている方なら、この説明で十分でしょう。




# Windows 10

Windowsキーを 押しながら、"`R`" キーを押します。
「ファイル名を指定して実行」ダイアログが現れるので、`certmgr.msc` と入力して実行します。

![win-certmgr_msc.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555032/48bbcadc-af98-9d96-25ec-f3acd8d199de.png)















(UAC (ユーザアクセス制御) ダイアログが表示された場合は、「はい」を選択して、実行します。)

「証明書 - 現在のユーザー」が表示されます。



![信頼されたルート証明機関-処理前.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555032/eacf12b9-39c9-7186-a953-621dd46e1277.png)



「信頼されたルート証明機関」-「証明書」を右クリックして、「すべてのタスク」-「インポート...」をクリックします。

![信頼されたルート証明書-インポートメニュー.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555032/f1fa2fbe-88dc-1697-68dd-15c5904aaaa0.png)







証明書のインポートウィザードのダイアログが表示されますので、ウィザードに従って、証明書をインポートします。

![証明書のインポートウィザードの開始.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555032/0852e0a9-4f16-55aa-6bdf-9bde4a532628.png)





























[次へ] をクリックし、読み込む証明書を [参照] で選択します。

![インポートするファイルの指定.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555032/9a332a8f-b5f5-322a-4abc-71c3f06be300.png)

































証明書を選択し、[開く]をクリックします。もとのウィンドウに戻りますので、[次へ] をクリックします。

![証明書選択.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555032/7364c402-d907-b6ac-15e4-4d44fe97b183.png)



























「証明書ストア」の選択では、「証明書をすべて次のストアに配置する」を選択し、プルダウンメニューから「信頼されたルート証明機関」を選びます。[次へ] をクリック。

![証明書ストア.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555032/e23e3f39-e10c-3c7a-6659-3333dc517c30.png)



























確認画面になりますので、間違いがなければ、[完了] をクリックします。

![証明書ストア完了.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555032/fc9f9c32-5f45-80fe-0380-a8b0142437f2.png)

























「正しくインポートされました」と表示されれば、終了です。

![正しくインポートされました.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555032/d76fd500-be76-6db0-d98d-43d9ff915fd3.png)




不要になった、certmgr は閉じてください。






















余談ですが、ローカルな環境でリモートデスクトップを利用する際に警告が出る場合の証明書も、表示させてそのままインポートするのではなく、一旦、ファイルにエクスポートして、上の方法で設定することで、警告が出なくなります。
(理由はわかっていませんが、警告ダイアログから証明書を表示させて、そのままインポートさせても警告が消えないことがありました)




# Splunk

Splunk では、SSL 通信については、以下の設定を行う必要があります。

1. Splunk Apps の(python)環境設定
2. Splunk がクライアントとなって外部サーバに接続に行く場合の設定
3. Splunk が提供する SSL の設定

3番目の設定は、ここでは省略します。

## Splunk Apps の(python)環境設定
以下の例では、`SPLUNK_HOMOE=/opt/splunk` です。

```shell_session
$ /opt/splunk/bin/splunk cmd python -c "import requests; print(requests.certs.where())"
/opt/splunk/lib/python2.7/site-packages/certifi/cacert.pem
$ /opt/splunk/bin/splunk cmd python -c "import certifi; print(certifi.where())"
/opt/splunk/lib/python2.7/site-packages/certifi/cacert.pem
$ 
$ /opt/splunk/bin/splunk cmd python3 -c "import requests; print(requests.certs.where())"
/opt/splunk/lib/python3.7/site-packages/certifi/cacert.pem
$ /opt/splunk/bin/splunk cmd python3 -c "import certifi; print(certifi.where())"
/opt/splunk/lib/python3.7/site-packages/certifi/cacert.pem
$
```

今後は、Python3 に移っていくことになると思いますが、しばらくは既存の App が python2 に依存しているので、python2 についても考慮が必要です。

Apps によっては、独自に site-packages に cacert.pem を持っていて参照しているものがありますので、これらにも対応する必要があります。

```shell_session:splunk&nbsp;ホーム下の&nbsp;cacert.pem&nbsp;の検索例
$ (cd /opt/splunk; sudo find . -name cacert.pem -ls)
 49024396    276 -r--r--r--   1 splunk   splunk     282085  6月 16  2019 ./lib/python2.7/site-packages/certifi/cacert.pem
 48893114    268 -r--r--r--   1 splunk   splunk     271088  8月  9 03:32 ./lib/python2.7/site-packages/botocore/cacert.pem
 49024315    268 -r--r--r--   1 splunk   splunk     271088  8月  9 03:32 ./lib/python2.7/site-packages/botocore/vendored/requests/cacert.pem
 49155480    276 -r--r--r--   1 splunk   splunk     282085  6月 16  2019 ./lib/python3.7/site-packages/certifi/cacert.pem
 49026412    268 -r--r--r--   1 splunk   splunk     271088  8月  9 03:32 ./lib/python3.7/site-packages/botocore/cacert.pem
 49155400    268 -r--r--r--   1 splunk   splunk     271088  8月  9 03:32 ./lib/python3.7/site-packages/botocore/vendored/requests/cacert.pem
 39325129      4 -rw-------   1 splunk   splunk       1265  8月  9  2018 ./etc/auth/cacert.pem
 41816513    344 -rw-r--r--   1 splunk   splunk     348799  2月 20  2019 ./etc/apps/splunk_app_addon-builder/bin/splunk_app_add_on_builder/requests/cacert.pem
 41943696    344 -rw-r--r--   1 splunk   splunk     348799  2月 20  2019 ./etc/apps/splunk_app_addon-builder/bin/ta_generator/resources_lib/requests/cacert.pem
 40370693    304 -rw-r--r--   1 splunk   splunk     308434  9月 18  2018 ./etc/apps/Splunk_SA_Scientific_Python_linux_x86_64/bin/linux_x86_64/lib/python2.7/site-packages/pip/_vendor/requests/cacert.pem
 40371155    340 -rw-r--r--   1 splunk   splunk     344712  9月 18  2018 ./etc/apps/Splunk_SA_Scientific_Python_linux_x86_64/bin/linux_x86_64/lib/python2.7/site-packages/requests/cacert.pem
$
```

## Splunk がクライアントとなって外部サーバに接続に行く場合の設定

OS に設定を合わせるのであれば、(Ubuntuの場合) `/etc/ssl/certs/ca-certificates.crt` を参照するのがよいでしょう。

```ini:${SPLUNK_HOME}/etc/system/local/server.conf
[sslConfig]
sslRootCAPath = /etc/ssl/certs/ca-certificates.crt
```

公式にどのように設定すべきか、については、記述が見当たりませんでした。(要再調査)