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>
This commit is contained in:
Jeremy Alvis 2020-12-10 08:07:05 -07:00 committed by GitHub
parent ed9076da29
commit f021f97c31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 5 deletions

View File

@ -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 |

View File

@ -25,6 +25,7 @@ const settingsMock: DataSourceSettings<any, any> = {
timeInterval: '15s',
httpMode: 'GET',
keepCookies: ['cookie1', 'cookie2'],
serverName: '',
},
secureJsonData: {
password: true,

View File

@ -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"

View File

@ -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 {

View File

@ -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() {