# Issue TLS Certificates Using CFSSL
> - **Objective:** Generate Certificate for TLS Using cfssl
> - **Prerequisite:**
> - None
> - **Optionality:** Optional
> - **Estimated time:** TBD
## Introduction
TLS certificates can be issued by multiple methods. This document describes how to use [CFSSL](https://github.com/cloudflare/cfssl) to issue certificates.
CFSSL is CloudFlare's PKI/TLS swiss army knife. It is both a command line tool and an HTTP API server for signing, verifying, and bundling TLS certificates.
## Install CFSSL
To install cfssl:
```
$ mkdir -p ~/bin
$ curl -s -L -o ~/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
$ curl -s -L -o ~/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
$ chmod +x ~/bin/{cfssl,cfssljson}
$ export PATH=$PATH:~/bin
```
Verify the installation:
```
$ cfssl -h
```
## Issue Certificate
### Create Certificate Directory
We create a directory to hold all certificates.
```
$ mkdir -p ~/cfssl
$ export certificate_path=~/cfssl
$ cd ${certificate_path}
```
**Note that the following operations are performed under `${certificate_path}`.**
### Configure Certificate Authority (CA)
A Certificate Authority (CA) is an entity that issues digital certificates. A digital certificate certifies the ownership of a public key by the named subject of the certificate. This allows others (relying parties) to rely upon signatures or on assertions made about the private key that corresponds to the certified public key. A CA acts as a trusted third party—trusted both by the subject (owner) of the certificate and by the party relying upon the certificate. The format of these certificates is specified by the X.509 or EMV standard.
We first need to create and customize the `ca-config.json` configuration file to define our CA.
```
$ cfssl print-defaults config > ca-config.json
$ vim ca-config.json
```
The changes we need to make are
- Change `sigining.default.expiry` to `8760h`.
- Change `signing.profiles.server.usage` to add `server auth` and `client auth`
```
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"server": {
"expiry": "8760h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
},
"client": {
"expiry": "8760h",
"usages": [
"signing",
"key encipherment",
"client auth"
]
}
}
}
}
```
**Note that we need to add Add `client auth` in profiles - server - usages, because this server-side certificate is also used as the client-side certificate.**
Next we need to configure the certificate signing request (CSR).
```
$ cfssl print-defaults csr > ca-csr.json
```
We need to make necessary modifications to CSR. The changes we need to make are:
- Change the `CN` field to `TIDB`.
- Change the `key.algo` filed to `rsa` and `key.size` to `2048`.
- Change the names to the values below.
```
{
"CN": "TiDB",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "CA",
"O": "PingCAP",
"ST": "Beijing",
"OU": "TiDB"
}
]
}
```
Now generate the self-signed root CA certificate and private key:
```
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
2020/04/28 11:32:55 [INFO] generating a new CA key and certificate from CSR
2020/04/28 11:32:55 [INFO] generate received request
2020/04/28 11:32:55 [INFO] received CSR
2020/04/28 11:32:55 [INFO] generating key: rsa-2048
2020/04/28 11:32:55 [INFO] encoded CSR
2020/04/28 11:32:55 [INFO] signed certificate with serial number 312652930147915274584885165112010854762084891048
```
### Issue Certificates
We need to generate certificates for inter and intra component communication. In summary, we need to generate the following certificates:
- PD Server certificate for communicating between PD instances.
- TiKV Server certificate for communicating between TiKV instances.
- TiDB Server certificate for communicating between MySQL client and TiDB Cluster.
- Cluster Client certificate for communicating between components.
- Client certificate for communicating between MySQL client and TiDB Cluster.
#### Issue PD Server Certificate
```
$ cfssl print-defaults csr > pd-server.json
```
Edit `pd-server.json` to make modifications. The changes we need to make are:
- Change the `CN` field to `TiDB`.
- Change the `hosts` field to add the following. Note that the entries in hosts assumes the TiDB cluster name to be `basic` and namespace to be `demo`. You need to make corresponding changes.
```
{
"CN": "TiDB",
"hosts": [
"127.0.0.1",
"::1",
"basic-pd",
"basic-pd.demo",
"basic-pd.demo.svc",
"basic-pd-peer",
"basic-pd-peer.demo",
"basic-pd-peer.demo.svc",
"*.basic-pd-peer",
"*.basic-pd-peer.demo",
"*.basic-pd-peer.demo.svc"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "CA",
"O": "PingCAP",
"ST": "Beijing",
"OU": "TiDB"
}
]
}
```
We can then generate the PD Server certificate with the CA:
```
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server pd-server.json | cfssljson -bare pd-server
2020/04/23 22:55:06 [INFO] generate received request
2020/04/23 22:55:06 [INFO] received CSR
2020/04/23 22:55:06 [INFO] generating key: rsa-2048
2020/04/23 22:55:07 [INFO] encoded CSR
2020/04/23 22:55:07 [INFO] signed certificate with serial number 214389999857155365314767437017019726628791998243
```
We can inspect the certificate using `openssl`:
```
$ openssl x509 -text -in pd-server.pem -noout
ertificate:
Data:
Version: 3 (0x2)
Serial Number:
42:a7:a6:4e:32:9a:42:5c:3f:a0:66:da:60:d0:82:d7:5d:ed:54:c7
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=Beijing, L=CA, O=PingCAP, OU=TiDB, CN=TiDB
Validity
Not Before: Apr 25 03:27:00 2020 GMT
Not After : Apr 25 03:27:00 2021 GMT
Subject: C=US, ST=Beijing, L=CA, O=PingCAP, OU=TiDB, CN=TiDB
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:dc:f9:9f:74:ac:eb:2d:66:44:81:b2:ba:e4:f5:
71:a5:3b:b1:ef:0f:f8:d2:0f:26:9a:b8:6e:03:3b:
65:74:73:ea:a2:ae:0f:93:d8:6a:a9:a3:a5:7f:eb:
a1:10:76:43:1f:25:dd:2c:1f:f5:23:a1:43:6f:e9:
30:d8:d4:ac:e2:a3:53:08:28:82:dc:d1:bf:e8:59:
96:f8:3c:dd:a4:65:53:02:a6:ed:66:13:7d:16:10:
ad:ad:b3:f5:50:fd:fb:fb:74:cb:c3:b3:90:d7:b1:
0f:22:85:6d:13:2f:c0:7d:ee:8b:17:cd:75:82:ea:
16:db:ca:97:4c:6c:00:94:74:c2:da:07:48:6f:db:
90:75:19:58:54:16:de:08:bc:c5:a0:62:e7:f4:69:
4f:57:31:5f:e4:ce:ca:83:6d:84:dd:64:39:d8:1e:
04:e1:64:70:ec:f6:47:b3:22:d1:57:4f:bc:a9:4f:
54:b8:29:c9:8b:64:7a:96:90:3f:98:14:be:26:b1:
a3:7c:fa:a1:4d:df:9a:c5:e4:37:2a:2b:41:70:ae:
57:82:7f:89:a3:50:56:5e:80:82:e9:e4:09:7e:d8:
8f:4f:ab:03:87:09:af:f7:31:bc:0a:2a:20:6e:3a:
e5:a3:95:4d:f5:19:71:cc:6d:d3:a9:88:29:21:ba:
51:e1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
3C:29:6B:01:2D:98:59:D8:E5:A7:F0:10:7E:8F:41:E9:98:F1:F1:ED
X509v3 Subject Alternative Name:
DNS:basic-pd, DNS:basic-pd.demo, DNS:basic-pd.demo.svc, DNS:basic-pd-peer, DNS:basic-pd-peer.demo, DNS:basic-pd-peer.demo.svc, DNS:*.basic-pd-peer, DNS:*.basic-pd-peer.demo, DNS:*.basic-pd-peer.demo.svc, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
Signature Algorithm: sha256WithRSAEncryption
94:cf:8f:af:15:b2:4d:de:64:8c:b0:58:dd:b8:ab:8d:99:48:
23:50:29:47:27:59:af:3c:f8:17:06:ba:a8:0b:be:30:03:b0:
c0:f2:60:1c:60:4c:cd:0a:bb:9b:3d:35:7c:43:74:87:1d:51:
6e:ed:d8:d4:fe:88:04:d9:95:58:4e:8d:40:92:6e:b8:84:13:
b3:c2:83:d7:dc:38:41:7b:ce:8a:99:6f:13:fb:eb:d4:a6:2e:
f7:80:75:71:8d:03:56:66:a6:d3:1c:f9:83:3e:76:b7:d3:68:
6b:46:fe:d5:9d:7b:e2:60:f9:d3:f5:40:fd:42:de:ed:b5:02:
3b:4f:15:87:25:fc:90:5d:2d:71:74:0b:b8:56:6b:bd:33:74:
8e:76:ce:54:83:a8:0a:1e:dc:60:f0:73:d0:a1:cf:01:00:c6:
2a:f4:8a:25:c2:d9:50:39:08:f2:8f:fb:21:6e:9a:9d:04:c6:
86:5a:b4:c9:c8:c4:66:a6:9d:5d:0d:66:2e:92:83:c9:62:8f:
21:cb:68:08:2d:8c:bb:bc:7d:39:c5:6c:04:a0:aa:bc:2c:0d:
6c:39:50:12:7d:37:a5:e3:ba:5e:09:2f:a7:07:67:68:3b:67:
6e:db:f6:f8:75:8e:1b:13:94:80:1d:f8:05:8a:2d:94:70:ea:
04:c7:09:8c
```
TODO: explain the meaning of different fields.
#### Issue TiKV Server certificate
```
$ cfssl print-defaults csr > tikv-server.json
```
Edit `tikv-server.json` to make modifications. The changes we need to make are:
- Change the `CN` field to `TiDB`.
- Change the `hosts` field to add the following. Note that the entries in hosts assumes the TiDB cluster name to be `basic` and namespace to be `demo`. You need to make corresponding changes.
```
{
"CN": "TiDB",
"hosts": [
"127.0.0.1",
"::1",
"basic-tikv",
"basic-tikv.demo",
"basic-tikv.demo.svc",
"basic-tikv-peer",
"basic-tikv-peer.demo",
"basic-tikv-peer.demo.svc",
"*.basic-tikv-peer",
"*.basic-tikv-peer.demo",
"*.basic-tikv-peer.demo.svc"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "CA",
"O": "PingCAP",
"ST": "Beijing",
"OU": "TiDB"
}
]
}
```
We can then generate the TiKV Server certificate with the CA:
```
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server tikv-server.json | cfssljson -bare tikv-server
2020/04/28 12:55:42 [INFO] generate received request
2020/04/28 12:55:42 [INFO] received CSR
2020/04/28 12:55:42 [INFO] generating key: rsa-2048
2020/04/28 12:55:42 [INFO] encoded CSR
2020/04/28 12:55:42 [INFO] signed certificate with serial number 425207623158563460200719671690261814210346624866
```
We can inspect the certificate using `openssl`:
```
$ openssl x509 -text -in tikv-server.pem -noout
```
#### Issue TiDB Server Certificate
```
$ cfssl print-defaults csr > tidb-server.json
```
Edit `tidb-server.json` to make modifications. The changes we need to make are:
- Change the `CN` field to `TiDB`.
- Change the `hosts` field to add the following. Note that the entries in hosts assumes the TiDB cluster name to be `basic` and namespace to be `demo`. You need to make corresponding changes.
```
{
"CN": "TiDB",
"hosts": [
"127.0.0.1",
"::1",
"basic-tidb",
"basic-tidb.demo",
"basic-tidb.demo.svc",
"*.basic-tidb",
"*.basic-tidb.demo",
"*.basic-tidb.demo.svc"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "CA",
"O": "PingCAP",
"ST": "Beijing",
"OU": "TiDB"
}
]
}
```
We can then generate the TiDB Server certificate with the CA:
```
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server tidb-server.json | cfssljson -bare tidb-server
2020/04/28 13:02:06 [INFO] generate received request
2020/04/28 13:02:06 [INFO] received CSR
2020/04/28 13:02:06 [INFO] generating key: rsa-2048
2020/04/28 13:02:07 [INFO] encoded CSR
2020/04/28 13:02:07 [INFO] signed certificate with serial number 677609381869553541316763926377530091236235435217
```
We can inspect the certificate using `openssl`:
```
$ openssl x509 -text -in tidb-server.pem -noout
```
#### Issue Client Certificate
```
$ cfssl print-defaults csr > client.json
```
Edit `client.json` to make modifications. The changes we need to make are:
- Change the `CN` field to `TiDB`.
- Change the `hosts` field to be empty.
```
{
"CN": "TiDB",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "CA",
"O": "PingCAP",
"ST": "Beijing",
"OU": "TiDB"
}
]
}
```
```
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client
2020/04/28 13:18:12 [INFO] generate received request
2020/04/28 13:18:12 [INFO] received CSR
2020/04/28 13:18:12 [INFO] generating key: rsa-2048
2020/04/28 13:18:12 [INFO] encoded CSR
2020/04/28 13:18:12 [INFO] signed certificate with serial number 18096070442089977328487840082232485650921646783
2020/04/28 13:18:12 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
```
We can inspect the certificate using `openssl`:
```
$ openssl x509 -text -in client.pem -noout
```
### Create Kubernetes Secret
Before performing the opertions, please ensure that a Kubernetes cluster is up and running. Also the following environment has been set.
```
$ export cluster_name=basic
$ export namespace=demo
$ export certificate_path= # Path to the certificate
```
#### Create Secret for TiDB Cluster Components
Create PD Server certificate:
```
$ kubectl create secret generic ${cluster_name}-pd-cluster-secret --namespace=${namespace} --from-file=tls.crt=${certificate_path}/pd-server.pem --from-file=tls.key=${certificate_path}/pd-server-key.pem --from-file=ca.crt=${certificate_path}/ca.pem
```
Create TiKV Server certificate:
```
$ kubectl create secret generic ${cluster_name}-tikv-cluster-secret --namespace=${namespace} --from-file=tls.crt=${certificate_path}/tikv-server.pem --from-file=tls.key=${certificate_path}/tikv-server-key.pem --from-file=ca.crt=${certificate_path}/ca.pem
```
Create TiDB Server certificate:
```
$ kubectl create secret generic ${cluster_name}-tidb-cluster-secret --namespace=${namespace} --from-file=tls.crt=${certificate_path}/tidb-server.pem --from-file=tls.key=${certificate_path}/tidb-server-key.pem --from-file=ca.crt=${certificate_path}/ca.pem
```
Create Cluster Client certificate:
```
$ kubectl create secret generic ${cluster_name}-cluster-client-secret --namespace=${namespace} --from-file=tls.crt=${certificate_path}/client.pem --from-file=tls.key=${certificate_path}/client-key.pem --from-file=ca.crt=${certificate_path}/ca.pem
```
#### Create Secret for MySQL Client and TiDB Cluster
Create Server certificate
```
$ kubectl create secret generic ${cluster_name}-tidb-server-secret --namespace=${namespace} --from-file=tls.crt=${certificate_path}/tidb-server.pem --from-file=tls.key=${certificate_path}/tidb-server-key.pem --from-file=ca.crt=${certificate_path}/ca.pem
```
Create Client certificate
```
$ kubectl create secret generic ${cluster_name}-tidb-client-secret --namespace=${namespace} --from-file=tls.crt=${certificate_path}/client.pem --from-file=tls.key=${certificate_path}/client-key.pem --from-file=ca.crt=${certificate_path}/ca.pem
```
### Verify that the secrets are created
```
$ kubectl get secrets -n demo
NAME TYPE DATA AGE
basic-cluster-client-secret Opaque 3 27s
basic-pd-cluster-secret Opaque 3 51s
basic-tidb-client-secret Opaque 3 14s
basic-tidb-cluster-secret Opaque 3 33s
basic-tidb-server-secret Opaque 3 20s
basic-tikv-cluster-secret Opaque 3 44s
default-token-jwh9v kubernetes.io/service-account-token 3 56s
```