Commit f021f97c by Jeremy Alvis Committed by GitHub

DataSourceSettings: Add servername field to DataSource TLS config (#29279)

* DataSourceSettings: Add servername field to DataSource TLS config

A DNS lookup URL can be provided in the DataSource URL field in order
to dynamically load balance between multiple instances of a DataSource.
When using mutual TLS, Golang's TLS config implementation checks that
the certificate's common name (< 1.15) or subject alternative name
(>= 1.15) has the same value as the domain being accessed. If the DNS
entry is dynamically generated for a specific environment, the
certificate cannot be generated with a name matching the dynamic
DNS URL. As such, Golang offers a servername field that can be set to
overwrite what value is used when checking against the certificate's
common name (or subject alternative name). Without this change, Skip
TLS Verify must be set to true in order for the DataSource to work,
removing some of the benefits gained by using mutual TLS.

This commit adds the ability to set Grafana's internal Golang TLS config
servername field from the UI or a provisioned DataSource.

The servername field is optional and the existing behavior is retained if
the field is not set.

Co-authored-by: Dana Pruitt <dpruitt@vmware.com>
Co-authored-by: Jeremy Alvis <jalvis@pivotal.io>

* Update docs with PR review changes

Co-authored-by: Jeremy Alvis <jalvis@pivotal.io>
Co-authored-by: Dana Pruitt <dpruitt@vmware.com>

* Update with additional PR requested changes

* Minor updates based on PR change requests

Co-authored-by: Dana Pruitt <dpruitt@vmware.com>
parent ed9076da
......@@ -146,6 +146,7 @@ Since not all datasources have the same configuration settings we only have the
| tlsAuth | boolean | _All_ | Enable TLS authentication using client cert configured in secure json data |
| tlsAuthWithCACert | boolean | _All_ | Enable TLS authentication using CA cert |
| tlsSkipVerify | boolean | _All_ | Controls whether a client verifies the server's certificate chain and host name. |
| serverName | string | _All_ | Optional. Controls the server name used for certificate common name/subject alternative name verification. Defaults to using the data source URL. |
| graphiteVersion | string | Graphite | Graphite version |
| timeInterval | string | Prometheus, Elasticsearch, InfluxDB, MySQL, PostgreSQL and MSSQL | Lowest interval/step value that should be used for this data source. |
| httpMode | string | Influxdb | HTTP Method. 'GET', 'POST', defaults to GET |
......
......@@ -25,6 +25,7 @@ const settingsMock: DataSourceSettings<any, any> = {
timeInterval: '15s',
httpMode: 'GET',
keepCookies: ['cookie1', 'cookie2'],
serverName: '',
},
secureJsonData: {
password: true,
......
......@@ -5,11 +5,13 @@ import { Tooltip } from '../Tooltip/Tooltip';
import { Icon } from '../Icon/Icon';
import { CertificationKey } from './CertificationKey';
import { HttpSettingsBaseProps } from './types';
import { FormField } from '../FormField/FormField';
export const TLSAuthSettings: React.FC<HttpSettingsBaseProps> = ({ dataSourceConfig, onChange }) => {
const hasTLSCACert = dataSourceConfig.secureJsonFields && dataSourceConfig.secureJsonFields.tlsCACert;
const hasTLSClientCert = dataSourceConfig.secureJsonFields && dataSourceConfig.secureJsonFields.tlsClientCert;
const hasTLSClientKey = dataSourceConfig.secureJsonFields && dataSourceConfig.secureJsonFields.tlsClientKey;
const hasServerName = dataSourceConfig.jsonData && dataSourceConfig.jsonData.serverName;
const onResetClickFactory = (field: string) => (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
......@@ -31,6 +33,18 @@ export const TLSAuthSettings: React.FC<HttpSettingsBaseProps> = ({ dataSourceCon
});
};
const onServerNameLabelChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
const newJsonData = {
...dataSourceConfig.jsonData,
serverName: event.currentTarget.value,
};
onChange({
...dataSourceConfig,
jsonData: newJsonData,
});
};
return (
<div className="gf-form-group">
<div
......@@ -65,6 +79,16 @@ export const TLSAuthSettings: React.FC<HttpSettingsBaseProps> = ({ dataSourceCon
{dataSourceConfig.jsonData.tlsAuth && (
<>
<div className="gf-form">
<FormField
label="ServerName"
labelWidth={7}
inputWidth={30}
placeholder="domain.example.com"
value={hasServerName && dataSourceConfig.jsonData.serverName}
onChange={onServerNameLabelChange}
/>
</div>
<CertificationKey
hasCert={!!hasTLSClientCert}
label="Client Cert"
......
......@@ -208,14 +208,18 @@ func (ds *DataSource) sigV4Middleware(next http.RoundTripper) http.RoundTripper
func (ds *DataSource) GetTLSConfig() (*tls.Config, error) {
var tlsSkipVerify, tlsClientAuth, tlsAuthWithCACert bool
var serverName string
if ds.JsonData != nil {
tlsClientAuth = ds.JsonData.Get("tlsAuth").MustBool(false)
tlsAuthWithCACert = ds.JsonData.Get("tlsAuthWithCACert").MustBool(false)
tlsSkipVerify = ds.JsonData.Get("tlsSkipVerify").MustBool(false)
serverName = ds.JsonData.Get("serverName").MustString()
}
tlsConfig := &tls.Config{
InsecureSkipVerify: tlsSkipVerify,
ServerName: serverName,
}
if tlsClientAuth || tlsAuthWithCACert {
......
......@@ -129,16 +129,19 @@ func TestDataSourceProxyCache(t *testing.T) {
json := simplejson.New()
json.Set("tlsAuthWithCACert", true)
json.Set("serverName", "server-name")
tlsCaCert, err := util.Encrypt([]byte(caCert), "password")
So(err, ShouldBeNil)
ds := DataSource{
Id: 1,
Url: "http://k8s:8001",
Type: "Kubernetes",
JsonData: json,
SecureJsonData: map[string][]byte{"tlsCACert": tlsCaCert},
Id: 1,
Url: "http://k8s:8001",
Type: "Kubernetes",
JsonData: json,
SecureJsonData: map[string][]byte{
"tlsCACert": tlsCaCert,
},
}
tr, err := ds.GetHttpTransport()
......@@ -150,6 +153,9 @@ func TestDataSourceProxyCache(t *testing.T) {
Convey("Should have a TLS CA configured", func() {
So(len(tr.transport.TLSClientConfig.RootCAs.Subjects()), ShouldEqual, 1)
})
Convey("Should include a server name if one is provided", func() {
So(tr.transport.TLSClientConfig.ServerName, ShouldEqual, "server-name")
})
})
Convey("When caching a datasource proxy when user skips TLS verification", t, func() {
......
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