From 31d64d90743db3367e42fdb5e86e553a8f940481 Mon Sep 17 00:00:00 2001 From: Will Browne Date: Wed, 9 Dec 2020 10:45:57 +0100 Subject: [PATCH] Auth: Add SigV4 header allowlist to reduce chances of verification issues (#29650) * enforce allowlist * fix default auth selection * add Host and comment --- .../DataSourceSettings/SigV4AuthSettings.tsx | 18 ++++++---- pkg/models/sigv4.go | 34 ++++++++++++------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/packages/grafana-ui/src/components/DataSourceSettings/SigV4AuthSettings.tsx b/packages/grafana-ui/src/components/DataSourceSettings/SigV4AuthSettings.tsx index 4bc6208bb6f..b842e40cbed 100644 --- a/packages/grafana-ui/src/components/DataSourceSettings/SigV4AuthSettings.tsx +++ b/packages/grafana-ui/src/components/DataSourceSettings/SigV4AuthSettings.tsx @@ -1,11 +1,11 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { HttpSettingsProps } from './types'; import { SelectableValue } from '@grafana/data'; import { Button, InlineFormLabel, Input } from '..'; import Select from '../Forms/Legacy/Select/Select'; export const SigV4AuthSettings: React.FC = props => { - const { dataSourceConfig } = props; + const { dataSourceConfig, onChange } = props; const authProviderOptions = [ { label: 'AWS SDK Default', value: 'default' }, @@ -42,6 +42,12 @@ export const SigV4AuthSettings: React.FC = props => { { value: 'us-west-2', label: 'us-west-2' }, ] as SelectableValue[]; + // Apply some defaults on initial render + useEffect(() => { + const sigV4AuthType = dataSourceConfig.jsonData.sigV4AuthType || 'default'; + onJsonDataChange('sigV4AuthType', sigV4AuthType); + }, []); + const onSecureJsonDataReset = (fieldName: string) => { const state = { ...dataSourceConfig, @@ -55,7 +61,7 @@ export const SigV4AuthSettings: React.FC = props => { }, }; - props.onChange(state); + onChange(state); }; const onSecureJsonDataChange = (fieldName: string, fieldValue: string) => { @@ -67,7 +73,7 @@ export const SigV4AuthSettings: React.FC = props => { }, }; - props.onChange(state); + onChange(state); }; const onJsonDataChange = (fieldName: string, fieldValue: string) => { @@ -79,7 +85,7 @@ export const SigV4AuthSettings: React.FC = props => { }, }; - props.onChange(state); + onChange(state); }; return ( @@ -100,7 +106,7 @@ export const SigV4AuthSettings: React.FC = props => { authProvider => authProvider.value === dataSourceConfig.jsonData.sigV4AuthType )} options={authProviderOptions} - defaultValue={dataSourceConfig.jsonData.sigV4AuthType || authProviderOptions[0]} + defaultValue={dataSourceConfig.jsonData.sigV4AuthType || ''} onChange={option => { onJsonDataChange('sigV4AuthType', option.value); }} diff --git a/pkg/models/sigv4.go b/pkg/models/sigv4.go index 2a8237e507f..1d1695ce933 100644 --- a/pkg/models/sigv4.go +++ b/pkg/models/sigv4.go @@ -24,6 +24,17 @@ const ( Credentials AuthType = "credentials" ) +// Host header is likely not necessary here +// (see https://github.com/golang/go/blob/cad6d1fef5147d31e94ee83934c8609d3ad150b7/src/net/http/request.go#L92) +// but adding for completeness +var permittedHeaders = map[string]struct{}{ + "Host": {}, + "Uber-Trace-Id": {}, + "User-Agent": {}, + "Accept": {}, + "Accept-Encoding": {}, +} + type SigV4Middleware struct { Config *Config Next http.RoundTripper @@ -72,18 +83,7 @@ func (m *SigV4Middleware) signRequest(req *http.Request) (http.Header, error) { req.URL.RawPath = rest.EscapePath(req.URL.RawPath, false) } - // if X-Forwarded-For header is present, omit during signing step as it breaks AWS request verification - forwardHeader := req.Header.Get("X-Forwarded-For") - if forwardHeader != "" { - req.Header.Del("X-Forwarded-For") - - header, err := signer.Sign(req, bytes.NewReader(body), awsServiceNamespace(m.Config.DatasourceType), m.Config.Region, time.Now().UTC()) - - // reset pre-existing X-Forwarded-For header value - req.Header.Set("X-Forwarded-For", forwardHeader) - - return header, err - } + stripHeaders(req) return signer.Sign(req, bytes.NewReader(body), awsServiceNamespace(m.Config.DatasourceType), m.Config.Region, time.Now().UTC()) } @@ -111,6 +111,8 @@ func (m *SigV4Middleware) signer() (*v4.Signer, error) { } return v4.NewSigner(s.Config.Credentials), nil + case "": + return nil, fmt.Errorf("invalid SigV4 auth type") } if m.Config.AssumeRoleARN != "" { @@ -149,3 +151,11 @@ func awsServiceNamespace(dsType string) string { panic(fmt.Sprintf("Unsupported datasource %s", dsType)) } } + +func stripHeaders(req *http.Request) { + for h := range req.Header { + if _, exists := permittedHeaders[h]; !exists { + req.Header.Del(h) + } + } +}