mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Fix SigV4 request verification step for Amazon Elasticsearch Service (#28481)
* remove forward header and add extra steps for encoding * add comment * re-use forwarded header * fix service param * Make SDK the default auth option in UI * use SDK code instead * propagate err * refactor flow * fix prom service namespace
This commit is contained in:
parent
a8a3686785
commit
4d2b20f727
@ -13,7 +13,7 @@ It is used in a `ConfigEditor` for data source plugins. You can find more exampl
|
||||
### Example usage
|
||||
```jsx
|
||||
export const ConfigEditor = (props: Props) => {
|
||||
const { options, onOptionsChange, config } = props;
|
||||
const { options, onOptionsChange } = props;
|
||||
return (
|
||||
<>
|
||||
<DataSourceHttpSettings
|
||||
|
@ -100,7 +100,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
|
||||
authProvider => authProvider.value === dataSourceConfig.jsonData.sigV4AuthType
|
||||
)}
|
||||
options={authProviderOptions}
|
||||
defaultValue={dataSourceConfig.jsonData.sigV4AuthType || 'keys'}
|
||||
defaultValue={dataSourceConfig.jsonData.sigV4AuthType || authProviderOptions[0]}
|
||||
onChange={option => {
|
||||
onJsonDataChange('sigV4AuthType', option.value);
|
||||
}}
|
||||
|
@ -193,13 +193,14 @@ func (ds *DataSource) sigV4Middleware(next http.RoundTripper) http.RoundTripper
|
||||
|
||||
return &SigV4Middleware{
|
||||
Config: &Config{
|
||||
AccessKey: decrypted["sigV4AccessKey"],
|
||||
SecretKey: decrypted["sigV4SecretKey"],
|
||||
Region: ds.JsonData.Get("sigV4Region").MustString(),
|
||||
AssumeRoleARN: ds.JsonData.Get("sigV4AssumeRoleArn").MustString(),
|
||||
AuthType: ds.JsonData.Get("sigV4AuthType").MustString(),
|
||||
ExternalID: ds.JsonData.Get("sigV4ExternalId").MustString(),
|
||||
Profile: ds.JsonData.Get("sigV4Profile").MustString(),
|
||||
DatasourceType: ds.Type,
|
||||
AccessKey: decrypted["sigV4AccessKey"],
|
||||
SecretKey: decrypted["sigV4SecretKey"],
|
||||
Region: ds.JsonData.Get("sigV4Region").MustString(),
|
||||
AssumeRoleARN: ds.JsonData.Get("sigV4AssumeRoleArn").MustString(),
|
||||
AuthType: ds.JsonData.Get("sigV4AuthType").MustString(),
|
||||
ExternalID: ds.JsonData.Get("sigV4ExternalId").MustString(),
|
||||
Profile: ds.JsonData.Get("sigV4Profile").MustString(),
|
||||
},
|
||||
Next: next,
|
||||
}
|
||||
|
@ -5,17 +5,16 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/defaults"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/defaults"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
||||
)
|
||||
|
||||
type AuthType string
|
||||
@ -36,6 +35,8 @@ type Config struct {
|
||||
|
||||
Profile string
|
||||
|
||||
DatasourceType string
|
||||
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
|
||||
@ -63,15 +64,29 @@ func (m *SigV4Middleware) signRequest(req *http.Request) (http.Header, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.Body != nil {
|
||||
// consume entire request body so that the signer can generate a hash from the contents
|
||||
body, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signer.Sign(req, bytes.NewReader(body), "grafana", m.Config.Region, time.Now().UTC())
|
||||
body, err := replaceBody(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signer.Sign(req, nil, "grafana", m.Config.Region, time.Now().UTC())
|
||||
|
||||
if strings.Contains(req.URL.RawPath, "%2C") {
|
||||
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
|
||||
}
|
||||
|
||||
return signer.Sign(req, bytes.NewReader(body), awsServiceNamespace(m.Config.DatasourceType), m.Config.Region, time.Now().UTC())
|
||||
}
|
||||
|
||||
func (m *SigV4Middleware) signer() (*v4.Signer, error) {
|
||||
@ -108,3 +123,26 @@ func (m *SigV4Middleware) credentials() (*credentials.Credentials, error) {
|
||||
|
||||
return nil, fmt.Errorf("unrecognized authType: %s", authType)
|
||||
}
|
||||
|
||||
func replaceBody(req *http.Request) ([]byte, error) {
|
||||
if req.Body == nil {
|
||||
return []byte{}, nil
|
||||
}
|
||||
payload, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(payload))
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func awsServiceNamespace(dsType string) string {
|
||||
switch dsType {
|
||||
case DS_ES:
|
||||
return "es"
|
||||
case DS_PROMETHEUS:
|
||||
return "aps"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unsupported datasource %s", dsType))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user