Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: Support tls for external #492 #518

Merged
merged 1 commit into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ bin/

# e2e logs
test/e2e/logs_*
# vscode local
.devcontainer

# vs debug files
__debug_*
3 changes: 3 additions & 0 deletions api/v1alpha1/mysqlcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ type MysqlClusterSpec struct {
// +optional
// +kubebuilder:default:=6
BackupScheduleJobsHistoryLimit *int `json:"backupScheduleJobsHistoryLimit,omitempty"`
// Containing CA (ca.crt) and server cert (tls.crt) ,server private key (tls.key) for SSL
//+optional
TlsSecretName string `json:"tlsSecretName,omitempty"`
}

// MysqlOpts defines the options of MySQL container.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,10 @@ spec:
description: Represents the name of the cluster restore from backup
path.
type: string
tlsSecretName:
description: Containing CA (ca.crt) and server cert (tls.crt) ,server
private key (tls.key) for SSL
type: string
xenonOpts:
default:
admitDefeatHearbeatCount: 5
Expand Down
4 changes: 4 additions & 0 deletions config/crd/bases/mysql.radondb.com_mysqlclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,10 @@ spec:
description: Represents the name of the cluster restore from backup
path.
type: string
tlsSecretName:
description: Containing CA (ca.crt) and server cert (tls.crt) ,server
private key (tls.key) for SSL
type: string
xenonOpts:
default:
admitDefeatHearbeatCount: 5
Expand Down
115 changes: 115 additions & 0 deletions docs/zh-cn/how_to_use_tls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
[TOC]

# 为MySQL客户端开启加密连接

# `TLS`(传输层加密)简介

RadonDB MySQL Operator 默认采用非加密连接,如果具备网络嗅探及监视的第三方工具可能截获服务端与客户端之间的数据,容易造成信息泄露,因此建议开启加密连接来确保数据安全。

RadonDB MySQL Operator 服务端支持`TLS`,协议为MySQL支持的加密协议,如`5.7`版本支持`TLS 1.0、TLS 1.1、TLS 1.2`、`8.0`版本支持`TLS 1.0、TLS 1.1、TLS 1.2、TLS 1.3`。

使用加密连接需要满足两个条件:

* MySQL Operator 服务端开启加密连接支持
* 客户端使用加密连接

# 配置`MySQL Operator`使用加密连接

## 准备证书

* `ca.crt` - 服务端`CA`证书
* `tls.key` - 服务端证书私钥
* `tls.crt` - 服务端证书

可以用`OpenSSL`生成,也可以用`MySQL`自带的`mysql_ssl_rsa_setup`快捷生成:

`mysql_ssl_rsa_setup --datadir=/tmp/certs`

运行该命令后会生成如下文件:

```shell
certs
├── ca-key.pem
├── ca.pem
├── client-cert.pem
├── client-key.pem
├── private_key.pem
├── public_key.pem
├── server-cert.pem
└── server-key.pem
```



### 根据证书文件创建secret

```shell
kubectl create secret generic sample-ssl --from-file=tls.crt=server.pem --
from-file=tls.key=server-key.pem --from-file=ca.crt=ca.pem --
type=kubernetes.io/tls
```

### 配置RadonDB MySQL 集群使用`TLS`

```shell
kubectl patch mysqlclusters.mysql.radondb.com sample --type=merge -p '{"spec":{"tlsSecretName":"sample-ssl"}}'
```

> 配置之后会触发`rolling update`即集群会重启

### 验证测试

* 不使用`SSL`连接

```shell
kubectl exec -it sample-mysql-0 -c mysql -- mysql -uradondb_usr -p"RadonDB@123" -e "\s"
mysql Ver 14.14 Distrib 5.7.34-37, for Linux (x86_64) using 7.0
Connection id: 7940
Current database:
Current user: radondb_usr@localhost
SSL: Not in use
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 5.7.34-37-log Percona Server (GPL), Release 37, Revision 7c516e9
Protocol version: 10
Connection: Localhost via UNIX socket
Server characterset: utf8mb4
Db characterset: utf8mb4
Client characterset: latin1
Conn. characterset: latin1
UNIX socket: /var/lib/mysql/mysql.sock
Uptime: 21 hours 49 min 36 sec

Threads: 5 Questions: 181006 Slow queries: 0 Opens: 127 Flush tables: 1 Open tables: 120 Queries per second avg: 2.303
```



* 使用`SSL`连接

```shell
kubectl exec -it sample-mysql-0 -c mysql -- mysql -uradondb_usr -p"RadonDB@123" --ssl-mode=REQUIRED -e "\s"
mysql: [Warning] Using a password on the command line interface can be insecure.
--------------
mysql Ver 14.14 Distrib 5.7.34-37, for Linux (x86_64) using 7.0

Connection id: 7938
Current database:
Current user: radondb_usr@localhost
SSL: Cipher in use is ECDHE-RSA-AES128-GCM-SHA256
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 5.7.34-37-log Percona Server (GPL), Release 37, Revision 7c516e9
Protocol version: 10
Connection: Localhost via UNIX socket
Server characterset: utf8mb4
Db characterset: utf8mb4
Client characterset: latin1
Conn. characterset: latin1
UNIX socket: /var/lib/mysql/mysql.sock
Uptime: 21 hours 49 min 26 sec

Threads: 5 Questions: 180985 Slow queries: 0 Opens: 127 Flush tables: 1 Open tables: 120 Queries per second avg: 2.303
```
12 changes: 11 additions & 1 deletion mysqlcluster/container/init_sidecar.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,17 @@ func (c *initSidecar) getVolumeMounts() []corev1.VolumeMount {
MountPath: utils.SysLocalTimeZoneMountPath,
},
}

if c.Spec.TlsSecretName != "" {
volumeMounts = append(volumeMounts,
corev1.VolumeMount{
Name: utils.TlsVolumeName + "-sidecar",
MountPath: "/tmp/mysql-ssl",
}, corev1.VolumeMount{
Name: utils.TlsVolumeName,
MountPath: utils.TlsMountPath,
},
)
}
if c.Spec.MysqlOpts.InitTokuDB {
volumeMounts = append(volumeMounts,
corev1.VolumeMount{
Expand Down
11 changes: 10 additions & 1 deletion mysqlcluster/container/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (c *mysql) getReadinessProbe() *corev1.Probe {

// getVolumeMounts get the container volumeMounts.
func (c *mysql) getVolumeMounts() []corev1.VolumeMount {
return []corev1.VolumeMount{
volumeMounts := []corev1.VolumeMount{
{
Name: utils.MysqlConfVolumeName,
MountPath: utils.MysqlConfVolumeMountPath,
Expand All @@ -151,4 +151,13 @@ func (c *mysql) getVolumeMounts() []corev1.VolumeMount {
MountPath: utils.SysLocalTimeZoneMountPath,
},
}
if c.Spec.TlsSecretName != "" {
volumeMounts = append(volumeMounts,
corev1.VolumeMount{
Name: utils.TlsVolumeName,
MountPath: utils.TlsMountPath,
},
)
}
return volumeMounts
}
16 changes: 16 additions & 0 deletions mysqlcluster/mysqlcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,22 @@ func (c *MysqlCluster) EnsureVolumes() []corev1.Volume {
},
})
}
// Add the ssl secret mounts.
if len(c.Spec.TlsSecretName) != 0 {
volumes = append(volumes, corev1.Volume{
Name: utils.TlsVolumeName + "-sidecar",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: c.Spec.TlsSecretName,
},
},
}, corev1.Volume{
Name: utils.TlsVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
})
}
return volumes
}

Expand Down
4 changes: 3 additions & 1 deletion mysqlcluster/syncer/mysql_cm.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ func buildMysqlConf(c *mysqlcluster.MysqlCluster) (string, error) {
log.Error(err, "failed to add boolean key to config section", "key", key)
}
}

if len(c.Spec.TlsSecretName) != 0 {
addKVConfigsToSection(sec, mysqlSSLConfigs)
}
data, err := writeConfigs(cfg)
if err != nil {
return "", err
Expand Down
7 changes: 7 additions & 0 deletions mysqlcluster/syncer/mysql_configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,10 @@ var mysqlBooleanConfigs = []string{
"log-slave-updates",
"!includedir /etc/mysql/conf.d",
}

// mysqlSSLConfigs is the ist of the mysql ssl configs.
var mysqlSSLConfigs = map[string]string{
"ssl_ca": "/etc/mysql-ssl/ca.crt",
"ssl_cert": "/etc/mysql-ssl/tls.crt",
"ssl_key": "/etc/mysql-ssl/tls.key",
}
21 changes: 20 additions & 1 deletion sidecar/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ func runInitCommand(cfg *Config) error {
return fmt.Errorf("failed to copy my.cnf: %s", err)
}

// SSL settings.
if exists, _ := checkIfPathExists(utils.TlsMountPath); exists {
buildSSLdata()
}
buildDefaultXenonMeta(uid, gid)

// build client.conf.
Expand All @@ -163,7 +167,6 @@ func runInitCommand(cfg *Config) error {
if err = os.Chown(extraConfPath, uid, gid); err != nil {
return fmt.Errorf("failed to chown %s: %s", dataPath, err)
}

// Run reset master in init-mysql container.
if err = ioutil.WriteFile(initFilePath+"/reset.sql", []byte("reset master;"), 0644); err != nil {
return fmt.Errorf("failed to write reset.sql: %s", err)
Expand Down Expand Up @@ -315,3 +318,19 @@ func buildDefaultXenonMeta(uid, gid int) error {
}
return nil
}

func buildSSLdata() error {
// cp -rp /tmp/myssl/* /etc/mysql/ssl/ Refer https://stackoverflow.com/questions/31467153/golang-failed-exec-command-that-works-in-terminal
shellCmd := "cp /tmp/mysql-ssl/* " + utils.TlsMountPath
cmd := exec.Command("sh", "-c", shellCmd)
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to copy ssl: %s", err)
}

cronCmd := "chown -R mysql.mysql " + utils.TlsMountPath
cmd = exec.Command("sh", "-c", cronCmd)
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to copy ssl: %s", err)
}
return nil
}
4 changes: 4 additions & 0 deletions utils/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ const (

// PluginConfigs is the alias for mysql plugin config.
PluginConfigs = "plugin.cnf"
// TlsVolumeName is the volume name for tls
TlsVolumeName = "tls"
// TlsMountPath is the volume mount path for tls
TlsMountPath = "/etc/mysql-ssl"
)

// ResourceName is the type for aliasing resources that will be created.
Expand Down