diff --git a/conf/defaults.ini b/conf/defaults.ini index 0693044dbbb..851c88efc7c 100644 --- a/conf/defaults.ini +++ b/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 diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index daa2d665e07..247cfbb0d19 100644 --- a/docs/sources/installation/configuration.md +++ b/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`.
diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 50b6e931c7c..3ab4bf1c00b 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -14,12 +14,21 @@ import ( "github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/setting" + "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql" "github.com/go-xorm/xorm" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" ) +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 ) @@ -120,6 +131,15 @@ func getEngine() (*xorm.Engine, error) { cnnstr = fmt.Sprintf("%s:%s@%s(%s)/%s?charset=utf8", DbCfg.User, DbCfg.Pwd, protocol, 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, ":") @@ -161,4 +181,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() + } } diff --git a/pkg/services/sqlstore/tls_mysql.go b/pkg/services/sqlstore/tls_mysql.go new file mode 100644 index 00000000000..fb55eb401c9 --- /dev/null +++ b/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 +}