【オレオレ証明書】JenkinsのインストールからSSL/TLS証明書を反映させるまで

## 保護されていない通信
Chrome 68以降からSSL対応をしていないサイトにアクセスした場合、`保護されていない通信`という警告が表示されるようになりました。
<img width="459" alt="スクリーンショット 2018-09-17 11.16.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/282414/53cf3f32-f003-a3a7-85f4-98e183f31df2.png">


ローカル環境なのですが、上記画像のようになってしまうのは、誰かに見せる時に気になるかと思ったので、これを機会に対応しようと思います。

## やること
今回は、CentOS7（vagrant+virtualbox）に構築したJenkinsをhttps化し証明書を適用するまでをやっていきたいと思います。

#### 前提
Vagrantfileで以下を設定しています。

```Bash:Vagrantfile
config.vm.network "private_network", ip: "192.168.33.10"
```

## 大まかな流れ
- Jenkinsインストール
- 認証局(CA)の作成
- SSL/TLSサーバ証明書作成
- Jenkinsのhttps化（ポート変更、証明書適用等）

## 利用した環境
|OS - Software|version|
|---|---|
|CentOS|10.13.6|
|vagtant|2.1.2|
|virtualbox|5.2.18|
|openjdk|1.8.0|
|jenins|2.138.1|


## 1. jenkinsのインストール準備
#### OpenJDK1.8のインストール

```
$ sudo su - # 以降の手順もrootユーザーで実施します
# yum -y install java-1.8.0-openjdk java-1.8.0-openjdk-devel
```


#### 環境変数に設定
```Bash:/etc/profile
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-3.b13.el7_5.x86_64
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
```

※`dirname $(readlink $(readlink $(which java)))``Javaのインストール先を確認できます。


#### 設定反映
```
# source /etc/profile
# echo $JAVA_HOME
```

JAVA_HOMEに設定した「/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-3.b13.el7_5」が表示されれば反映されています。


#### Jenkinsが含まれるRPMを追加

```
# wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
# rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
```


## 2. Jeninsのインストール

```
# yum -y install jenkins
```

#### 起動/自動起動設定

```
# service jenkins start
# chkconfig jenkins on
```

#### インストール用のパスワードを確認

```
# cat /var/lib/jenkins/secrets/initialAdminPassword
```

#### ChromeでJenkinsへアクセス
表示にしたがってインストールを進めます。
<img width="994" alt="スクリーンショット 2018-09-17 10.25.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/282414/9c5119d5-2d33-edae-1a7d-9c7b3394f167.png">

インストールが完了し、ログインするとこんな感じになります。
<img width="979" alt="スクリーンショット 2018-09-17 11.03.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/282414/e1cdb8f6-4a54-4b5f-7bc0-dc81900e6b62.png">


※jenkinsの画面が真っ白な場合、`systemctl restart jenkins`で再起動してみると治ることがあります。

#### hosts設定　（ホスト側）
仮想マシンへのアクセスとDNSに変更します。

```Bash:/etc/hosts
# 追記
192.168.33.10 jenkinsci.com
```

#### 確認
```
$ ping jenkinsci.com
PING jenkinsci.com (192.168.33.10): 56 data bytes
64 bytes from 192.168.33.10: icmp_seq=0 ttl=64 time=0.364 ms
64 bytes from 192.168.33.10: icmp_seq=1 ttl=64 time=0.405 ms
```
リクエストが返ってくればOK


## 3. 認証局(CA）の作成

#### openssl.cnfのバックアップ
```
cp -pr /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl.cnf.org
```

#### openssl.cnfの設定
```Bash:openssl.cnfの変更箇所
[ CA_default ]
private_key    = $dir/private/cakey.pem #pemと[#]の間に半角スペースを入れる

default_md = sha256

[ req ]
default_md = sha256

[usr_cert ]
# add
subjectAltName = @alt_names # Chrome 58対応
```

#### CAの秘密鍵作成
RSA方式で2048bitのcakey.pemを作成。

```
# openssl genrsa \
　　 -aes256 \
　　 -passout 'pass:oreoreca' \ 
  -out /etc/pki/CA/private/cakey.pem 2048 \
　　 -days 3650

Generating RSA private key, 2048 bit long modulus
..........................................................................+++
...........................................................................................+++
e is 65537 (0x10001)
```

-  `-aes256` ：Advanced Encryption Standard (
高度暗号化標準)。 古くなってきたDESの代わりに考案された暗号化方式。
米国政府によって認められ、推奨されている暗号化標準。

- `-passout 'pass:設定するパスワード'`：ターミナルでのパスワード入力を省略できる。

- `-days`：有効期間の設定。今回は、3650日(約10年)で設定。[CA_default]セクションのdefault_daysで設定してもOK。

##### 注意：各ファイルの名称は、「openssl.cnf」に合わせて設定しています。
```Bash
[ CA_default ]

dir		= /etc/pki/CA		# Where everything is kept
certs		= $dir/certs		# Where the issued certs are kept
crl_dir		= $dir/crl		# Where the issued crl are kept
database	= $dir/index.txt	# database index file.
#unique_subject	= no			# Set to 'no' to allow creation of
					# several ctificates with same subject.
new_certs_dir	= $dir/newcerts		# default place for new certs.

certificate	= $dir/cacert.pem 	# The CA certificate
serial		= $dir/serial 		# The current serial number
crlnumber	= $dir/crlnumber	# the current crl number
					# must be commented out to leave a V1 CRL
crl		= $dir/crl.pem 		# The current CRL
private_key	= $dir/private/cakey.pem # The private key
RANDFILE	= $dir/private/.rand	# private random number file
```

#### 秘密鍵からCSR（証明書発行要求のファイル）を作成
```
# openssl req \
  -new \
  -key /etc/pki/CA/private/cakey.pem \
  -passout 'pass:oreoreca' \
  -out /etc/pki/CA/cacert.csr \
  -subj "/C=JP/ST=Tokyo/L=Shibuya-ku/O=OreOreCA inc./OU=OreOreCA labo/CN=oreoreca.com"

Enter pass phrase for /etc/pki/CA/private/cakey.pem: <-- 秘密鍵のパスワードを入力
```

#### CSRを使用してオレオレ証明書を作成
認証局の秘密鍵を使って、自分で証明書に署名する。

```
# openssl x509 \
　　　　-req \
　　 -in /etc/pki/CA/cacert.csr \
  -signkey /etc/pki/CA/private/cakey.pem \
  -out /etc/pki/CA/cacert.pem \
　　　　-days 3650

Signature ok
subject=/C=JP/ST=Tokyo/L=Shibuya-ku/O=OreOreCA inc./OU=OreOreCA labo/CN=oreoreca.com
Getting Private key
Enter pass phrase for /etc/pki/CA/private/cakey.pem:  <-- 秘密鍵のパスワードを入力
```

#### 認証局運用に必要なファイルの生成
```
touch /etc/pki/CA/index.txt
echo 00 > /etc/pki/CA/serial
```

- `index.txt`：履歴情報。
- `serial`：証明書のカウンタ。次に発行する番号を記載。

## 4. SSL/TLSサーバ証明書作成
先ほど作成したCAでJenkinsに適用するSSL/TLSサーバ証明書を発行します。

#### 秘密鍵作成

```
# openssl genrsa \
  -aes256 \
  -passout 'pass:jenkinscipass' \
  -out /etc/pki/tls/private/jenkinscikey.pem 2048

Generating RSA private key, 2048 bit long modulus
..........................................................................+++
...........................................................................................+++
e is 65537 (0x10001)
```

#### パスフレーズなしの秘密鍵にする
```
# openssl rsa \
  -passin 'pass:jenkinscipass' \
  -in /etc/pki/tls/private/jenkinscikey.pem \
  -out /etc/pki/tls/private/jenkinscikey.pem

writing RSA key
```
※jenkinsで利用する際にパスフレーズを聞かれないようにする為。

#### CSR作成
`O=`はオレオレ証明書と同じにすること。
`CN=`にhostsで設定した値を入れること。

```
# openssl req \
 -new \
 -sha256 \
 -key /etc/pki/tls/private/jenkinscikey.pem \
 -out /etc/pki/tls/certs/jenkinsci.csr \ 
 -subj "/C=JP/ST=Tokyo/L=Shibuya-ku/O=OreOreCA inc./OU=jenkinscilabo/CN=jenkinsci.com"
```

#### Chrome 58対応（san.ext作成）
Chrome 58 以降で、ドメイン名と証明書の照合にcommonNameが使われず、subjectAlternativeNameのみを使用するようになったとのこと。
回避策としてサーバ証明書発行する際に`-extfile san.ext`を指定して実行する。

```Bash:san.ext
subjectAltName=DNS:jenkinsci.com
```

#### オレオレ認証局でサーバ証明書作成
```
# openssl ca \
　　 -in /etc/pki/tls/certs/jenkinsci.csr \
　　 -out /etc/pki/tls/certs/jenkinsci.com.crt.pem \
　　 -days 3650 \
　　 -extfile san.ext

Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: ~~~
            Not After : ~~~
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = OreOreCA inc.
            organizationalUnitName    = jenkinsci labo
            commonName                = jenkinsci.com
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                DNS:jenkinsci.com
Certificate is to be certified until ~~~ (3650 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
```

## 5. Jenkinsのhttps化（証明書適用、ポート変更等）

#### キーストア配置先を作成
```
# mkdir -p /var/lib/jenkins/.ssl
```
※jenkinsが/var/lib/jenkins/にインストールされている前提


#### PKCS#12形式変換(秘密鍵とサーバ証明書をまとめる)
```
# openssl pkcs12 \
　　 -export \
  -in /etc/pki/tls/certs/jenkinsci.com.crt.pem \
　　　　-inkey /etc/pki/tls/private/jenkinscikey.pem \
　　 -out /var/lib/jenkins/.ssl/jenkins_keystore.p12


Enter Export Password: <-- 任意のパスワード
Verifying - Enter Export Password: <-- 再入力
```

#### PKCS#12形式からJKS形式のキーストアを作成
```
# keytool \
　　 -importkeystore \
  -srckeystore /var/lib/jenkins/.ssl/jenkins_keystore.p12 \
  -srcstorepass 'zamapass' -srcstoretype pkcs12 \
  -destkeystore /var/lib/jenkins/.ssl/jenkins_keystore.jks \
  -deststorepass 'zamapass' -deststoretype JKS
 ```
※パスワードを`zamapass`にした場合の例です

#### キーストアのオーナー権限変更
```
# chown -R jenkins:jenkins /var/lib/jenkins/.ssl
```

#### Jenkinsのconfig設定
```Bash:/etc/sysconfig/jenkins
JENKINS_PORT=-1
JENKINS_HTTPS_PORT="55443"
JENKINS_HTTPS_KEYSTORE="var/lib/jenkins/.ssl/jenkins_keystore.jks"
JENKINS_HTTPS_KEYSTORE_PASSWORD="zamapass"
```

#### jenkins の再起動
```
# systemctl restart jenkins
```

#### サーバ証明書を共有ディレクトリ(ホスト側とゲスト側)にコピー
```
cp jenkinsci.com.crt.pem /共有ディレクトリ/jenkinsci.com.crt
```

## 6. ホスト側で証明書を信頼させる
1. `/共有ディレクトリ/jenkinsci.com.crt` をダブルクリックし、キーチェーンアクセスへ証明書を追加。
1. 追加したキーチェーンアクセスをダブルクリックし、「信頼」の「この証明書を使用するとき」で`常に信頼`を選択。

## 7. Jenkinsのシステム設定
`https://jenkinsci.com:55443` にアクセス。

[Jenkinsの設定] - [システムの管理] - [Jenkins URL] の設定値を
`https://jenkinsci.com:55443/` に変更。

## 最後に
勉強の為に手動で色々調べながら設定しましたが、いくつも証明書を作成する場合は大変だと思います。
実際に運用するのであれば、ちゃんとお金を使いドメイン取得とDNS設定をして
[Let's Encrypt](https://letsencrypt.jp/)で証明書を取得した方が楽だったなーと思いました。


## 参考文献

- [CentOS7にJava OpenJDK8のインストール](https://qiita.com/Esfahan/items/60cf425514c66553bd42)
- [オレオレ認証局をつくろう](https://qiita.com/ll_kuma_ll/items/13c962a6a74874af39c6)
- [AESとは](https://www.ibm.com/support/knowledgecenter/ja/SSWPVP_3.0.0/com.ibm.sklm.doc/overview/cpt/cpt_ic_oview_tech_cryptographic_algorithm.html)
- [Running Jenkins with native SSL /HTTPS](https://wiki.jenkins.io/pages/viewpage.action?pageId=135468777)
- [電子証明書あれこれ](https://qiita.com/irohamaru/items/cfbb5aa4ff0dae56e360)
- [OSX:開発環境のオレオレSSL証明書をChromeやSafariに信頼させる](https://qiita.com/suin/items/be87a7a581f30b38c5f7)
- [Subject Alternative Name Missing](https://support.google.com/chrome/a/answer/7391219?hl=en)
- [pkcs#12形式について](https://xtrust.jp/support/tech_support/install/openssl_pkcs12)