mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Add SigV4 header allowlist to reduce chances of verification issues (#29650)
* enforce allowlist * fix default auth selection * add Host and comment
This commit is contained in:
parent
770e8e4a0b
commit
31d64d9074
@ -1,11 +1,11 @@
|
|||||||
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 => {
|
|||||||
{ 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 => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
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 => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
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 => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
props.onChange(state);
|
onChange(state);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -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 (
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user