Browse Source

Add TLS for mysql

Use ssl_mode for mysql and add docs
add docs for the new parameters in config

Tolerate ssl_mode without client authentication

Client cert is not necessary for a SSL connection. So we tolerate
failure if client cert is not provided.
Improve error message if missing server_cert_name and mode is not
skip-verify.
Daniel Low 10 years ago
parent
commit
ea566fff24

+ 7 - 1
conf/defaults.ini

@@ -63,9 +63,15 @@ name = grafana
 user = root
 password =
 
-# For "postgres" only, either "disable", "require" or "verify-full"
+# For "postgres", use either "disable", "require" or "verify-full"
+# For "mysql", use either "true", "false", or "skip-verify".
 ssl_mode = disable
 
+ca_cert_path =
+client_key_path =
+client_cert_path =
+server_cert_name =
+
 # For "sqlite3" only, path relative to data_path setting
 path = grafana.db
 

+ 18 - 1
docs/sources/installation/configuration.md

@@ -156,7 +156,24 @@ The database user's password (not applicable for `sqlite3`).
 
 ### ssl_mode
 
-For `postgres` only, either `disable`, `require` or `verify-full`.
+For Postgres, use either `disable`, `require` or `verify-full`.
+For MySQL, use either `true`, `false`, or `skip-verify`.
+
+### ca_cert_path 
+
+(MySQL only) The path to the CA certificate to use. On many linux systems, certs can be found in `/etc/ssl/certs`.
+
+### client_key_path 
+
+(MySQL only) The path to the client key. Only if server requires client authentication.
+
+### client_cert_path 
+
+(MySQL only) The path to the client cert. Only if server requires client authentication.
+
+### server_cert_name 
+
+(MySQL only) The common name field of the certificate used by the `mysql` server. Not necessary if `ssl_mode` is set to `skip-verify`.
 
 <hr />
 

+ 27 - 0
pkg/services/sqlstore/sqlstore.go

@@ -18,8 +18,17 @@ import (
 	"github.com/go-xorm/xorm"
 	_ "github.com/lib/pq"
 	_ "github.com/mattn/go-sqlite3"
+	"github.com/go-sql-driver/mysql"
 )
 
+type MySQLConfig struct {
+	SslMode string
+	CaCertPath string
+	ClientKeyPath  string
+	ClientCertPath string
+	ServerCertName string
+}
+
 var (
 	x       *xorm.Engine
 	dialect migrator.Dialect
@@ -30,6 +39,8 @@ var (
 		Type, Host, Name, User, Pwd, Path, SslMode string
 	}
 
+	mysqlConfig MySQLConfig
+
 	UseSQLite3 bool
 )
 
@@ -115,6 +126,14 @@ func getEngine() (*xorm.Engine, error) {
 	case "mysql":
 		cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8",
 			DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name)
+		if mysqlConfig.SslMode == "true" || mysqlConfig.SslMode == "skip-verify" {
+			tlsCert, err := makeCert("custom", mysqlConfig)
+			if err != nil {
+				return nil, err
+			}
+			mysql.RegisterTLSConfig("custom", tlsCert)
+			cnnstr += "&tls=custom"
+		}
 	case "postgres":
 		var host, port = "127.0.0.1", "5432"
 		fields := strings.Split(DbCfg.Host, ":")
@@ -156,4 +175,12 @@ func LoadConfig() {
 	}
 	DbCfg.SslMode = sec.Key("ssl_mode").String()
 	DbCfg.Path = sec.Key("path").MustString("data/grafana.db")
+
+	if DbCfg.Type == "mysql" {
+		mysqlConfig.SslMode = DbCfg.SslMode
+		mysqlConfig.CaCertPath = sec.Key("ca_cert_path").String()
+		mysqlConfig.ClientKeyPath = sec.Key("client_key_path").String()
+		mysqlConfig.ClientCertPath = sec.Key("client_cert_path").String()
+		mysqlConfig.ServerCertName = sec.Key("server_cert_name").String()
+	}
 }

+ 41 - 0
pkg/services/sqlstore/tls_mysql.go

@@ -0,0 +1,41 @@
+package sqlstore
+
+import (
+	"crypto/tls"
+	"crypto/x509"
+	"fmt"
+	"io/ioutil"
+)
+
+func makeCert(tlsPoolName string, config MySQLConfig) (*tls.Config, error) {
+	rootCertPool := x509.NewCertPool()
+	pem, err := ioutil.ReadFile(config.CaCertPath)
+	if err != nil {
+		return nil, fmt.Errorf("Could not read DB CA Cert path: %v", config.CaCertPath)
+	}
+	if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
+		return nil, err
+	}
+	clientCert := make([]tls.Certificate, 0, 1)
+	if (config.ClientCertPath != "" && config.ClientKeyPath != "") {
+
+		certs, err := tls.LoadX509KeyPair(config.ClientCertPath, config.ClientKeyPath)
+		if err != nil {
+			return nil, err
+		}
+		clientCert = append(clientCert, certs)
+	}
+	tlsConfig := &tls.Config{
+		RootCAs:      rootCertPool,
+		Certificates: clientCert,
+	}
+	tlsConfig.ServerName = config.ServerCertName
+	if config.SslMode == "skip-verify" {
+		tlsConfig.InsecureSkipVerify = true
+	}
+	// Return more meaningful error before it is too late
+	if config.ServerCertName == "" && !tlsConfig.InsecureSkipVerify{
+		return nil, fmt.Errorf("server_cert_name is missing. Consider using ssl_mode = skip-verify.")
+	}
+	return tlsConfig, nil
+}