mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Added support for TLS client auth for datasource proxies (#5801)
This commit is contained in:
parent
ad7ae1b912
commit
56b7e2dfaf
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
Dial: (&net.Dialer{
|
Dial: (&net.Dialer{
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
KeepAlive: 30 * time.Second,
|
KeepAlive: 30 * time.Second,
|
||||||
}).Dial,
|
}).Dial,
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
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")
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
|||||||
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"`
|
||||||
|
@ -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)
|
||||||
|
@ -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 @@
|
|||||||
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>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user