Commit 31d64d90 by Will Browne Committed by GitHub

Auth: Add SigV4 header allowlist to reduce chances of verification issues (#29650)

* enforce allowlist

* fix default auth selection

* add Host and comment
parent 770e8e4a
import React from 'react'; import React, { useEffect } from 'react';
import { HttpSettingsProps } from './types'; import { HttpSettingsProps } from './types';
import { SelectableValue } from '@grafana/data'; import { SelectableValue } from '@grafana/data';
import { Button, InlineFormLabel, Input } from '..'; import { Button, InlineFormLabel, Input } from '..';
import Select from '../Forms/Legacy/Select/Select'; import Select from '../Forms/Legacy/Select/Select';
export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => { export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
const { dataSourceConfig } = props; const { dataSourceConfig, onChange } = props;
const authProviderOptions = [ const authProviderOptions = [
{ label: 'AWS SDK Default', value: 'default' }, { label: 'AWS SDK Default', value: 'default' },
...@@ -42,6 +42,12 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => { ...@@ -42,6 +42,12 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
{ value: 'us-west-2', label: 'us-west-2' }, { value: 'us-west-2', label: 'us-west-2' },
] as SelectableValue[]; ] as SelectableValue[];
// Apply some defaults on initial render
useEffect(() => {
const sigV4AuthType = dataSourceConfig.jsonData.sigV4AuthType || 'default';
onJsonDataChange('sigV4AuthType', sigV4AuthType);
}, []);
const onSecureJsonDataReset = (fieldName: string) => { const onSecureJsonDataReset = (fieldName: string) => {
const state = { const state = {
...dataSourceConfig, ...dataSourceConfig,
...@@ -55,7 +61,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => { ...@@ -55,7 +61,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
}, },
}; };
props.onChange(state); onChange(state);
}; };
const onSecureJsonDataChange = (fieldName: string, fieldValue: string) => { const onSecureJsonDataChange = (fieldName: string, fieldValue: string) => {
...@@ -67,7 +73,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => { ...@@ -67,7 +73,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
}, },
}; };
props.onChange(state); onChange(state);
}; };
const onJsonDataChange = (fieldName: string, fieldValue: string) => { const onJsonDataChange = (fieldName: string, fieldValue: string) => {
...@@ -79,7 +85,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => { ...@@ -79,7 +85,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
}, },
}; };
props.onChange(state); onChange(state);
}; };
return ( return (
...@@ -100,7 +106,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => { ...@@ -100,7 +106,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
authProvider => authProvider.value === dataSourceConfig.jsonData.sigV4AuthType authProvider => authProvider.value === dataSourceConfig.jsonData.sigV4AuthType
)} )}
options={authProviderOptions} options={authProviderOptions}
defaultValue={dataSourceConfig.jsonData.sigV4AuthType || authProviderOptions[0]} defaultValue={dataSourceConfig.jsonData.sigV4AuthType || ''}
onChange={option => { onChange={option => {
onJsonDataChange('sigV4AuthType', option.value); onJsonDataChange('sigV4AuthType', option.value);
}} }}
......
...@@ -24,6 +24,17 @@ const ( ...@@ -24,6 +24,17 @@ const (
Credentials AuthType = "credentials" 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 { type SigV4Middleware struct {
Config *Config Config *Config
Next http.RoundTripper Next http.RoundTripper
...@@ -72,18 +83,7 @@ func (m *SigV4Middleware) signRequest(req *http.Request) (http.Header, error) { ...@@ -72,18 +83,7 @@ func (m *SigV4Middleware) signRequest(req *http.Request) (http.Header, error) {
req.URL.RawPath = rest.EscapePath(req.URL.RawPath, false) 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 stripHeaders(req)
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
}
return signer.Sign(req, bytes.NewReader(body), awsServiceNamespace(m.Config.DatasourceType), m.Config.Region, time.Now().UTC()) 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) { ...@@ -111,6 +111,8 @@ func (m *SigV4Middleware) signer() (*v4.Signer, error) {
} }
return v4.NewSigner(s.Config.Credentials), nil return v4.NewSigner(s.Config.Credentials), nil
case "":
return nil, fmt.Errorf("invalid SigV4 auth type")
} }
if m.Config.AssumeRoleARN != "" { if m.Config.AssumeRoleARN != "" {
...@@ -149,3 +151,11 @@ func awsServiceNamespace(dsType string) string { ...@@ -149,3 +151,11 @@ func awsServiceNamespace(dsType string) string {
panic(fmt.Sprintf("Unsupported datasource %s", dsType)) 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)
}
}
}
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