Commit ea566fff by Daniel Low

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.
parent 3dc3d363
...@@ -63,9 +63,15 @@ name = grafana ...@@ -63,9 +63,15 @@ name = grafana
user = root user = root
password = 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 ssl_mode = disable
ca_cert_path =
client_key_path =
client_cert_path =
server_cert_name =
# For "sqlite3" only, path relative to data_path setting # For "sqlite3" only, path relative to data_path setting
path = grafana.db path = grafana.db
......
...@@ -156,7 +156,24 @@ The database user's password (not applicable for `sqlite3`). ...@@ -156,7 +156,24 @@ The database user's password (not applicable for `sqlite3`).
### ssl_mode ### 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 /> <hr />
......
...@@ -18,8 +18,17 @@ import ( ...@@ -18,8 +18,17 @@ import (
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
_ "github.com/lib/pq" _ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3" _ "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 ( var (
x *xorm.Engine x *xorm.Engine
dialect migrator.Dialect dialect migrator.Dialect
...@@ -30,6 +39,8 @@ var ( ...@@ -30,6 +39,8 @@ var (
Type, Host, Name, User, Pwd, Path, SslMode string Type, Host, Name, User, Pwd, Path, SslMode string
} }
mysqlConfig MySQLConfig
UseSQLite3 bool UseSQLite3 bool
) )
...@@ -115,6 +126,14 @@ func getEngine() (*xorm.Engine, error) { ...@@ -115,6 +126,14 @@ func getEngine() (*xorm.Engine, error) {
case "mysql": case "mysql":
cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8",
DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name) 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": case "postgres":
var host, port = "127.0.0.1", "5432" var host, port = "127.0.0.1", "5432"
fields := strings.Split(DbCfg.Host, ":") fields := strings.Split(DbCfg.Host, ":")
...@@ -156,4 +175,12 @@ func LoadConfig() { ...@@ -156,4 +175,12 @@ func LoadConfig() {
} }
DbCfg.SslMode = sec.Key("ssl_mode").String() DbCfg.SslMode = sec.Key("ssl_mode").String()
DbCfg.Path = sec.Key("path").MustString("data/grafana.db") 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()
}
} }
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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment