Commit 56b7e2df by Joe Lanford Committed by Daniel Lee

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

parent ad7ae1b9
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 ( ...@@ -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 ...@@ -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)
} }
} }
...@@ -17,14 +17,27 @@ import ( ...@@ -17,14 +17,27 @@ import (
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
var dataProxyTransport = &http.Transport{ func dataProxyTransport(ds *m.DataSource) (*http.Transport, error) {
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, transport := &http.Transport{
Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{
Dial: (&net.Dialer{ InsecureSkipVerify: true,
Timeout: 30 * time.Second, },
KeepAlive: 30 * time.Second, Proxy: http.ProxyFromEnvironment,
}).Dial, Dial: (&net.Dialer{
TLSHandshakeTimeout: 10 * time.Second, 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) { ...@@ -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")
} }
...@@ -33,6 +33,7 @@ func GetDataSources(c *middleware.Context) { ...@@ -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 { ...@@ -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,
......
...@@ -77,6 +77,9 @@ type DataSource struct { ...@@ -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"`
......
...@@ -43,6 +43,9 @@ type DataSource struct { ...@@ -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 { ...@@ -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 { ...@@ -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"`
......
...@@ -80,6 +80,9 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error { ...@@ -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 { ...@@ -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 { ...@@ -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)
......
...@@ -101,4 +101,15 @@ func addDataSourceMigration(mg *Migrator) { ...@@ -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,
}))
} }
...@@ -49,6 +49,10 @@ ...@@ -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 @@ ...@@ -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>
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