Selaa lähdekoodia

Added support for TLS client auth for datasource proxies (#5801)

Joe Lanford 9 vuotta sitten
vanhempi
commit
56b7e2dfaf

+ 16 - 1
pkg/api/app_routes.go

@@ -1,6 +1,11 @@
 package api
 package api
 
 
 import (
 import (
+	"crypto/tls"
+	"net"
+	"net/http"
+	"time"
+
 	"gopkg.in/macaron.v1"
 	"gopkg.in/macaron.v1"
 
 
 	"github.com/grafana/grafana/pkg/api/pluginproxy"
 	"github.com/grafana/grafana/pkg/api/pluginproxy"
@@ -11,6 +16,16 @@ import (
 	"github.com/grafana/grafana/pkg/util"
 	"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) {
 func InitAppPluginRoutes(r *macaron.Macaron) {
 	for _, plugin := range plugins.Apps {
 	for _, plugin := range plugins.Apps {
 		for _, route := range plugin.Routes {
 		for _, route := range plugin.Routes {
@@ -40,7 +55,7 @@ func AppPluginRoute(route *plugins.AppPluginRoute, appId string) macaron.Handler
 		path := c.Params("*")
 		path := c.Params("*")
 
 
 		proxy := pluginproxy.NewApiPluginProxy(c, path, route, appId)
 		proxy := pluginproxy.NewApiPluginProxy(c, path, route, appId)
-		proxy.Transport = dataProxyTransport
+		proxy.Transport = pluginProxyTransport
 		proxy.ServeHTTP(c.Resp, c.Req.Request)
 		proxy.ServeHTTP(c.Resp, c.Req.Request)
 	}
 	}
 }
 }

+ 26 - 9
pkg/api/dataproxy.go

@@ -17,14 +17,27 @@ import (
 	"github.com/grafana/grafana/pkg/util"
 	"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 {
 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 := 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)
 	proxy.ServeHTTP(c.Resp, c.Req.Request)
 	c.Resp.Header().Del("Set-Cookie")
 	c.Resp.Header().Del("Set-Cookie")
 }
 }

+ 4 - 0
pkg/api/datasources.go

@@ -33,6 +33,7 @@ func GetDataSources(c *middleware.Context) {
 			Database:  ds.Database,
 			Database:  ds.Database,
 			User:      ds.User,
 			User:      ds.User,
 			BasicAuth: ds.BasicAuth,
 			BasicAuth: ds.BasicAuth,
+			TlsAuth:   ds.TlsAuth,
 			IsDefault: ds.IsDefault,
 			IsDefault: ds.IsDefault,
 			JsonData:  ds.JsonData,
 			JsonData:  ds.JsonData,
 		}
 		}
@@ -165,6 +166,9 @@ func convertModelToDtos(ds *m.DataSource) dtos.DataSource {
 		BasicAuth:         ds.BasicAuth,
 		BasicAuth:         ds.BasicAuth,
 		BasicAuthUser:     ds.BasicAuthUser,
 		BasicAuthUser:     ds.BasicAuthUser,
 		BasicAuthPassword: ds.BasicAuthPassword,
 		BasicAuthPassword: ds.BasicAuthPassword,
+		TlsAuth:           ds.TlsAuth,
+		TlsClientCert:     ds.TlsClientCert,
+		TlsClientKey:      ds.TlsClientKey,
 		WithCredentials:   ds.WithCredentials,
 		WithCredentials:   ds.WithCredentials,
 		IsDefault:         ds.IsDefault,
 		IsDefault:         ds.IsDefault,
 		JsonData:          ds.JsonData,
 		JsonData:          ds.JsonData,

+ 3 - 0
pkg/api/dtos/models.go

@@ -77,6 +77,9 @@ type DataSource struct {
 	BasicAuth         bool             `json:"basicAuth"`
 	BasicAuth         bool             `json:"basicAuth"`
 	BasicAuthUser     string           `json:"basicAuthUser"`
 	BasicAuthUser     string           `json:"basicAuthUser"`
 	BasicAuthPassword string           `json:"basicAuthPassword"`
 	BasicAuthPassword string           `json:"basicAuthPassword"`
+	TlsAuth           bool             `json:"tlsAuth"`
+	TlsClientCert     string           `json:"tlsClientCert"`
+	TlsClientKey      string           `json:"tlsClientKey"`
 	WithCredentials   bool             `json:"withCredentials"`
 	WithCredentials   bool             `json:"withCredentials"`
 	IsDefault         bool             `json:"isDefault"`
 	IsDefault         bool             `json:"isDefault"`
 	JsonData          *simplejson.Json `json:"jsonData,omitempty"`
 	JsonData          *simplejson.Json `json:"jsonData,omitempty"`

+ 9 - 0
pkg/models/datasource.go

@@ -43,6 +43,9 @@ type DataSource struct {
 	BasicAuth         bool
 	BasicAuth         bool
 	BasicAuthUser     string
 	BasicAuthUser     string
 	BasicAuthPassword string
 	BasicAuthPassword string
+	TlsAuth           bool
+	TlsClientCert     string
+	TlsClientKey      string
 	WithCredentials   bool
 	WithCredentials   bool
 	IsDefault         bool
 	IsDefault         bool
 	JsonData          *simplejson.Json
 	JsonData          *simplejson.Json
@@ -87,6 +90,9 @@ type AddDataSourceCommand struct {
 	BasicAuth         bool             `json:"basicAuth"`
 	BasicAuth         bool             `json:"basicAuth"`
 	BasicAuthUser     string           `json:"basicAuthUser"`
 	BasicAuthUser     string           `json:"basicAuthUser"`
 	BasicAuthPassword string           `json:"basicAuthPassword"`
 	BasicAuthPassword string           `json:"basicAuthPassword"`
+	TlsAuth           bool             `json:"tlsAuth"`
+	TlsClientCert     string           `json:"tlsClientCert"`
+	TlsClientKey      string           `json:"tlsClientKey"`
 	WithCredentials   bool             `json:"withCredentials"`
 	WithCredentials   bool             `json:"withCredentials"`
 	IsDefault         bool             `json:"isDefault"`
 	IsDefault         bool             `json:"isDefault"`
 	JsonData          *simplejson.Json `json:"jsonData"`
 	JsonData          *simplejson.Json `json:"jsonData"`
@@ -108,6 +114,9 @@ type UpdateDataSourceCommand struct {
 	BasicAuth         bool             `json:"basicAuth"`
 	BasicAuth         bool             `json:"basicAuth"`
 	BasicAuthUser     string           `json:"basicAuthUser"`
 	BasicAuthUser     string           `json:"basicAuthUser"`
 	BasicAuthPassword string           `json:"basicAuthPassword"`
 	BasicAuthPassword string           `json:"basicAuthPassword"`
+	TlsAuth           bool             `json:"tlsAuth"`
+	TlsClientCert     string           `json:"tlsClientCert"`
+	TlsClientKey      string           `json:"tlsClientKey"`
 	WithCredentials   bool             `json:"withCredentials"`
 	WithCredentials   bool             `json:"withCredentials"`
 	IsDefault         bool             `json:"isDefault"`
 	IsDefault         bool             `json:"isDefault"`
 	JsonData          *simplejson.Json `json:"jsonData"`
 	JsonData          *simplejson.Json `json:"jsonData"`

+ 7 - 0
pkg/services/sqlstore/datasource.go

@@ -80,6 +80,9 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error {
 			BasicAuth:         cmd.BasicAuth,
 			BasicAuth:         cmd.BasicAuth,
 			BasicAuthUser:     cmd.BasicAuthUser,
 			BasicAuthUser:     cmd.BasicAuthUser,
 			BasicAuthPassword: cmd.BasicAuthPassword,
 			BasicAuthPassword: cmd.BasicAuthPassword,
+			TlsAuth:           cmd.TlsAuth,
+			TlsClientCert:     cmd.TlsClientCert,
+			TlsClientKey:      cmd.TlsClientKey,
 			WithCredentials:   cmd.WithCredentials,
 			WithCredentials:   cmd.WithCredentials,
 			JsonData:          cmd.JsonData,
 			JsonData:          cmd.JsonData,
 			Created:           time.Now(),
 			Created:           time.Now(),
@@ -126,6 +129,9 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error {
 			BasicAuth:         cmd.BasicAuth,
 			BasicAuth:         cmd.BasicAuth,
 			BasicAuthUser:     cmd.BasicAuthUser,
 			BasicAuthUser:     cmd.BasicAuthUser,
 			BasicAuthPassword: cmd.BasicAuthPassword,
 			BasicAuthPassword: cmd.BasicAuthPassword,
+			TlsAuth:           cmd.TlsAuth,
+			TlsClientCert:     cmd.TlsClientCert,
+			TlsClientKey:      cmd.TlsClientKey,
 			WithCredentials:   cmd.WithCredentials,
 			WithCredentials:   cmd.WithCredentials,
 			JsonData:          cmd.JsonData,
 			JsonData:          cmd.JsonData,
 			Updated:           time.Now(),
 			Updated:           time.Now(),
@@ -133,6 +139,7 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error {
 
 
 		sess.UseBool("is_default")
 		sess.UseBool("is_default")
 		sess.UseBool("basic_auth")
 		sess.UseBool("basic_auth")
+		sess.UseBool("tls_auth")
 		sess.UseBool("with_credentials")
 		sess.UseBool("with_credentials")
 
 
 		_, err := sess.Where("id=? and org_id=?", ds.Id, ds.OrgId).Update(ds)
 		_, err := sess.Where("id=? and org_id=?", ds.Id, ds.OrgId).Update(ds)

+ 11 - 0
pkg/services/sqlstore/migrations/datasource_mig.go

@@ -101,4 +101,15 @@ func addDataSourceMigration(mg *Migrator) {
 	mg.AddMigration("Add column with_credentials", NewAddColumnMigration(tableV2, &Column{
 	mg.AddMigration("Add column with_credentials", NewAddColumnMigration(tableV2, &Column{
 		Name: "with_credentials", Type: DB_Bool, Nullable: false, Default: "0",
 		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,
+	}))
 }
 }

+ 18 - 0
public/app/features/plugins/partials/ds_http_settings.html

@@ -49,6 +49,10 @@
 									label="With Credentials"
 									label="With Credentials"
 				 checked="current.withCredentials" switch-class="max-width-6">
 				 checked="current.withCredentials" switch-class="max-width-6">
 		</gf-form-switch>
 		</gf-form-switch>
+		<gf-form-switch class="gf-form" ng-if="current.access=='proxy'"
+									label="TLS Client Auth"
+				 checked="current.tlsAuth" switch-class="max-width-6">
+		</gf-form-switch>
 	</div>
 	</div>
 
 
 	<div class="gf-form" ng-if="current.basicAuth">
 	<div class="gf-form" ng-if="current.basicAuth">
@@ -64,5 +68,19 @@
 		</span>
 		</span>
 		<input class="gf-form-input max-width-21" type="password" ng-model='current.basicAuthPassword' placeholder="password" required></input>
 		<input class="gf-form-input max-width-21" type="password" ng-model='current.basicAuthPassword' placeholder="password" required></input>
 	</div>
 	</div>
+
+	<div class="gf-form" ng-if="current.tlsAuth && current.access=='proxy'">
+		<span class="gf-form-label width-7">
+			Client Cert
+		</span>
+		<input class="gf-form-input max-width-23" type="text"  ng-model='current.tlsClientCert' placeholder="cert path" required></input>
+	</div>
+
+	<div class="gf-form" ng-if="current.tlsAuth && current.access=='proxy'">
+		<span class="gf-form-label width-7">
+			Client Key
+		</span>
+		<input class="gf-form-input max-width-23" type="text" ng-model='current.tlsClientKey' placeholder="key path" required></input>
+	</div>
 </div>
 </div>