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
+}