diff --git a/pkg/api/app_routes.go b/pkg/api/app_routes.go
index 7923b0475a3..aee67ac8478 100644
--- a/pkg/api/app_routes.go
+++ b/pkg/api/app_routes.go
@@ -1,6 +1,11 @@
package api
import (
+ "crypto/tls"
+ "net"
+ "net/http"
+ "time"
+
"gopkg.in/macaron.v1"
"github.com/grafana/grafana/pkg/api/pluginproxy"
@@ -11,6 +16,16 @@ import (
"github.com/grafana/grafana/pkg/util"
)
+var pluginProxyTransport = &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ Proxy: http.ProxyFromEnvironment,
+ Dial: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ }).Dial,
+ TLSHandshakeTimeout: 10 * time.Second,
+}
+
func InitAppPluginRoutes(r *macaron.Macaron) {
for _, plugin := range plugins.Apps {
for _, route := range plugin.Routes {
@@ -40,7 +55,7 @@ func AppPluginRoute(route *plugins.AppPluginRoute, appId string) macaron.Handler
path := c.Params("*")
proxy := pluginproxy.NewApiPluginProxy(c, path, route, appId)
- proxy.Transport = dataProxyTransport
+ proxy.Transport = pluginProxyTransport
proxy.ServeHTTP(c.Resp, c.Req.Request)
}
}
diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go
index 34c4271ebf6..7f527ed3c74 100644
--- a/pkg/api/dataproxy.go
+++ b/pkg/api/dataproxy.go
@@ -17,14 +17,27 @@ import (
"github.com/grafana/grafana/pkg/util"
)
-var dataProxyTransport = &http.Transport{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
- Proxy: http.ProxyFromEnvironment,
- Dial: (&net.Dialer{
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
- }).Dial,
- TLSHandshakeTimeout: 10 * time.Second,
+func dataProxyTransport(ds *m.DataSource) (*http.Transport, error) {
+ transport := &http.Transport{
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: true,
+ },
+ Proxy: http.ProxyFromEnvironment,
+ Dial: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ }).Dial,
+ TLSHandshakeTimeout: 10 * time.Second,
+ }
+
+ if ds.TlsAuth {
+ cert, err := tls.LoadX509KeyPair(ds.TlsClientCert, ds.TlsClientKey)
+ if err != nil {
+ return nil, err
+ }
+ transport.TLSClientConfig.Certificates = []tls.Certificate{cert}
+ }
+ return transport, nil
}
func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy {
@@ -128,7 +141,11 @@ func ProxyDataSourceRequest(c *middleware.Context) {
}
proxy := NewReverseProxy(ds, proxyPath, targetUrl)
- proxy.Transport = dataProxyTransport
+ proxy.Transport, err = dataProxyTransport(ds)
+ if err != nil {
+ c.JsonApiErr(400, "Unable to load TLS certificate", err)
+ return
+ }
proxy.ServeHTTP(c.Resp, c.Req.Request)
c.Resp.Header().Del("Set-Cookie")
}
diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go
index 2b9964f7a71..6d30a7dc44a 100644
--- a/pkg/api/datasources.go
+++ b/pkg/api/datasources.go
@@ -33,6 +33,7 @@ func GetDataSources(c *middleware.Context) {
Database: ds.Database,
User: ds.User,
BasicAuth: ds.BasicAuth,
+ TlsAuth: ds.TlsAuth,
IsDefault: ds.IsDefault,
JsonData: ds.JsonData,
}
@@ -165,6 +166,9 @@ func convertModelToDtos(ds *m.DataSource) dtos.DataSource {
BasicAuth: ds.BasicAuth,
BasicAuthUser: ds.BasicAuthUser,
BasicAuthPassword: ds.BasicAuthPassword,
+ TlsAuth: ds.TlsAuth,
+ TlsClientCert: ds.TlsClientCert,
+ TlsClientKey: ds.TlsClientKey,
WithCredentials: ds.WithCredentials,
IsDefault: ds.IsDefault,
JsonData: ds.JsonData,
diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go
index 170a5a868fc..d902f4a58a0 100644
--- a/pkg/api/dtos/models.go
+++ b/pkg/api/dtos/models.go
@@ -77,6 +77,9 @@ type DataSource struct {
BasicAuth bool `json:"basicAuth"`
BasicAuthUser string `json:"basicAuthUser"`
BasicAuthPassword string `json:"basicAuthPassword"`
+ TlsAuth bool `json:"tlsAuth"`
+ TlsClientCert string `json:"tlsClientCert"`
+ TlsClientKey string `json:"tlsClientKey"`
WithCredentials bool `json:"withCredentials"`
IsDefault bool `json:"isDefault"`
JsonData *simplejson.Json `json:"jsonData,omitempty"`
diff --git a/pkg/models/datasource.go b/pkg/models/datasource.go
index 883cc3a90bd..c4400e9f3dd 100644
--- a/pkg/models/datasource.go
+++ b/pkg/models/datasource.go
@@ -43,6 +43,9 @@ type DataSource struct {
BasicAuth bool
BasicAuthUser string
BasicAuthPassword string
+ TlsAuth bool
+ TlsClientCert string
+ TlsClientKey string
WithCredentials bool
IsDefault bool
JsonData *simplejson.Json
@@ -87,6 +90,9 @@ type AddDataSourceCommand struct {
BasicAuth bool `json:"basicAuth"`
BasicAuthUser string `json:"basicAuthUser"`
BasicAuthPassword string `json:"basicAuthPassword"`
+ TlsAuth bool `json:"tlsAuth"`
+ TlsClientCert string `json:"tlsClientCert"`
+ TlsClientKey string `json:"tlsClientKey"`
WithCredentials bool `json:"withCredentials"`
IsDefault bool `json:"isDefault"`
JsonData *simplejson.Json `json:"jsonData"`
@@ -108,6 +114,9 @@ type UpdateDataSourceCommand struct {
BasicAuth bool `json:"basicAuth"`
BasicAuthUser string `json:"basicAuthUser"`
BasicAuthPassword string `json:"basicAuthPassword"`
+ TlsAuth bool `json:"tlsAuth"`
+ TlsClientCert string `json:"tlsClientCert"`
+ TlsClientKey string `json:"tlsClientKey"`
WithCredentials bool `json:"withCredentials"`
IsDefault bool `json:"isDefault"`
JsonData *simplejson.Json `json:"jsonData"`
diff --git a/pkg/services/sqlstore/datasource.go b/pkg/services/sqlstore/datasource.go
index 0e6219785bd..29581c03096 100644
--- a/pkg/services/sqlstore/datasource.go
+++ b/pkg/services/sqlstore/datasource.go
@@ -80,6 +80,9 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error {
BasicAuth: cmd.BasicAuth,
BasicAuthUser: cmd.BasicAuthUser,
BasicAuthPassword: cmd.BasicAuthPassword,
+ TlsAuth: cmd.TlsAuth,
+ TlsClientCert: cmd.TlsClientCert,
+ TlsClientKey: cmd.TlsClientKey,
WithCredentials: cmd.WithCredentials,
JsonData: cmd.JsonData,
Created: time.Now(),
@@ -126,6 +129,9 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error {
BasicAuth: cmd.BasicAuth,
BasicAuthUser: cmd.BasicAuthUser,
BasicAuthPassword: cmd.BasicAuthPassword,
+ TlsAuth: cmd.TlsAuth,
+ TlsClientCert: cmd.TlsClientCert,
+ TlsClientKey: cmd.TlsClientKey,
WithCredentials: cmd.WithCredentials,
JsonData: cmd.JsonData,
Updated: time.Now(),
@@ -133,6 +139,7 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error {
sess.UseBool("is_default")
sess.UseBool("basic_auth")
+ sess.UseBool("tls_auth")
sess.UseBool("with_credentials")
_, err := sess.Where("id=? and org_id=?", ds.Id, ds.OrgId).Update(ds)
diff --git a/pkg/services/sqlstore/migrations/datasource_mig.go b/pkg/services/sqlstore/migrations/datasource_mig.go
index 90f7dac85e6..58205bbb196 100644
--- a/pkg/services/sqlstore/migrations/datasource_mig.go
+++ b/pkg/services/sqlstore/migrations/datasource_mig.go
@@ -101,4 +101,15 @@ func addDataSourceMigration(mg *Migrator) {
mg.AddMigration("Add column with_credentials", NewAddColumnMigration(tableV2, &Column{
Name: "with_credentials", Type: DB_Bool, Nullable: false, Default: "0",
}))
+
+ // add columns to activate TLS client auth option
+ mg.AddMigration("Add column tls_auth", NewAddColumnMigration(tableV2, &Column{
+ Name: "tls_auth", Type: DB_Bool, Nullable: false, Default: "0",
+ }))
+ mg.AddMigration("Add column tls_client_cert", NewAddColumnMigration(tableV2, &Column{
+ Name: "tls_client_cert", Type: DB_NVarchar, Length: 255, Nullable: true,
+ }))
+ mg.AddMigration("Add column tls_client_key", NewAddColumnMigration(tableV2, &Column{
+ Name: "tls_client_key", Type: DB_NVarchar, Length: 255, Nullable: true,
+ }))
}
diff --git a/public/app/features/plugins/partials/ds_http_settings.html b/public/app/features/plugins/partials/ds_http_settings.html
index 0022f6e6c19..5d980f43fdd 100644
--- a/public/app/features/plugins/partials/ds_http_settings.html
+++ b/public/app/features/plugins/partials/ds_http_settings.html
@@ -49,6 +49,10 @@
label="With Credentials"
checked="current.withCredentials" switch-class="max-width-6">
+