Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[extension/sigv4authextension] Initial implementation #8263

Merged
merged 14 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ extension/observer/ecstaskobserver/ @open-telemetry/collector-c
extension/observer/hostobserver/ @open-telemetry/collector-contrib-approvers @MovieStoreGuy
extension/observer/k8sobserver/ @open-telemetry/collector-contrib-approvers @rmfitzpatrick @dmitryax
extension/oidcauthextension/ @open-telemetry/collector-contrib-approvers @jpkrohling
extension/sigv4authextention/ @open-telemetry/collector-contrib-approvers @Aneurysm9 @anuraaga @erichsueh3
extension/pprofextension/ @open-telemetry/collector-contrib-approvers @MovieStoreGuy
extension/storage/dbstorage/ @open-telemetry/collector-contrib-approvers @dmitryax @atoulme
extension/storage/filestorage/ @open-telemetry/collector-contrib-approvers @djaglowski
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
cpu metrics as a double values.

### 🚀 New components 🚀
- `sigv4authextension`: New Component: Sigv4 Authenticator Extension (#8263)

## v0.46.0

Expand Down Expand Up @@ -192,6 +193,8 @@
### 🛑 Breaking changes 🛑

- `resourcedetectionprocessor`: Update `os.type` attribute values according to semantic conventions (#7544)
- `awsprometheusremotewriteexporter`: Deprecation notice; may be removed after v0.49.0
- Switch to using the `prometheusremotewriteexporter` + `sigv4authextension` instead

### 🧰 Bug fixes 🧰

Expand Down
6 changes: 6 additions & 0 deletions exporter/awsprometheusremotewriteexporter/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## DEPRECATION NOTICE

The AWS Prometheus Remote Write Exporter will be removed at some point after `v0.49.0`. Users who want to send metrics to Amazon Managed Service
for Prometheus will need to instead use the [Prometheus Remote Write Exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/prometheusremotewriteexporter/README.md) along with the [Sigv4 Authenticator Extension](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/extension/sigv4authauthextension/README.md)
to achieve the same result.

# AWS Prometheus Remote Write Exporter

AWS Prometheus Remote Write Exporter sends metrics in remote write format to
Expand Down
1 change: 1 addition & 0 deletions exporter/awsprometheusremotewriteexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func (af *awsFactory) Type() config.Type {

func (af *awsFactory) CreateMetricsExporter(ctx context.Context, params component.ExporterCreateSettings,
cfg config.Exporter) (component.MetricsExporter, error) {
params.Logger.Warn("Deprecation notice: The AWS PRW Exporter will be removed in the near future, see the README and the CHANGELOG for more information.")
return af.ExporterFactory.CreateMetricsExporter(ctx, params, &cfg.(*Config).Config)
}

Expand Down
1 change: 1 addition & 0 deletions extension/sigv4authextension/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
45 changes: 45 additions & 0 deletions extension/sigv4authextension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Authenticator - Sigv4

This extension provides Sigv4 authentication for making requests to AWS services. For more information on the Sigv4 process, please look [here](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html).

## Configuration

The configuration fields are as follows:

* `role_arn`: **Optional**. The Amazon Resource Name (ARN) of a role to assume
* `region`: **Optional**. The AWS region for AWS Sigv4
* Note that an attempt will be made to obtain a valid region from the endpoint of the service you are exporting to
* [List of AWS regions](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html)
* `service`: **Optional**. The AWS service for AWS Sigv4
* Note that an attempt will be made to obtain a valid service from the endpoint of the service you are exporting to
* `role_session_name`: **Optional**. The name of a role session. If not provided, one will be constructed with a semi-random identifier.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this up to be right after role_arn

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also wondering if it should be

assume_role:
  arn:
  session_name:

since session_name doesn't make sense without arn

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, pairing the ARN and session name like that sounds good to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made the changes and now include an AssumeRole struct with an ARN and a SessionName



```yaml
extensions:
sigv4auth:
role_arn: "arn:aws:iam::123456789012:role/aws-service-role/access"

receivers:
hostmetrics:
scrapers:
memory:

exporters:
prometheusremotewrite:
endpoint: "https://aps-workspaces.us-west-2.amazonaws.com/workspaces/ws-XXX/api/v1/remote_write"
auth:
authenticator: sigv4auth

service:
extensions: [sigv4auth]
pipelines:
metrics:
receivers: [hostmetrics]
processors: []
exporters: [prometheusremotewrite]
```

## Notes

* The collector must have valid AWS credentials as used by the [AWS SDK for Go](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-credentials)
52 changes: 52 additions & 0 deletions extension/sigv4authextension/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sigv4authextension // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/sigv4authextension"

import (
"errors"

"github.com/aws/aws-sdk-go-v2/aws"
"go.opentelemetry.io/collector/config"
)

var (
errBadCreds = errors.New("bad AWS credentials")
)

// Config stores the configuration for the Sigv4 Authenticator
type Config struct {
config.ExtensionSettings `mapstructure:",squash"`
Region string `mapstructure:"region,omitempty"`
Service string `mapstructure:"service,omitempty"`
RoleARN string `mapstructure:"role_arn,omitempty"`
RoleSessionName string `mapstructure:"role_session_name,omitempty"`
credsProvider *aws.CredentialsProvider
}

// compile time check that the Config struct satisfies the config.Extension interface
var _ config.Extension = (*Config)(nil)

// Validate checks that the configuration is valid.
// We aim to catch most errors here to ensure that we
// fail early and to avoid revalidating static data.
func (cfg *Config) Validate() error {
credsProvider, err := getCredsProviderFromConfig(cfg)
if credsProvider == nil || err != nil {
return errBadCreds
}
cfg.credsProvider = credsProvider

return nil
}
84 changes: 84 additions & 0 deletions extension/sigv4authextension/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sigv4authextension

import (
"context"
"path"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config"
"go.opentelemetry.io/collector/service/servicetest"
)

func TestLoadConfig(t *testing.T) {
awsCredsProvider := mockCredentials()
awsCreds, _ := (*awsCredsProvider).Retrieve(context.Background())

t.Setenv("AWS_ACCESS_KEY_ID", awsCreds.AccessKeyID)
erichsueh3 marked this conversation as resolved.
Show resolved Hide resolved
t.Setenv("AWS_SECRET_ACCESS_KEY", awsCreds.SecretAccessKey)

factories, err := componenttest.NopFactories()
assert.NoError(t, err)

factory := NewFactory()
factories.Extensions[typeStr] = factory
cfg, err := servicetest.LoadConfigAndValidate(path.Join(".", "testdata", "config.yaml"), factories)

require.NoError(t, err)
require.NotNil(t, cfg)

expected := factory.CreateDefaultConfig().(*Config)
expected.Region = "region"
expected.Service = "service"
expected.RoleSessionName = "role_session_name"

ext := cfg.Extensions[config.NewComponentID(typeStr)]
// Ensure creds are the same for load config test; tested in extension_test.go
expected.credsProvider = ext.(*Config).credsProvider
assert.Equal(t, expected, ext)

assert.Equal(t, 1, len(cfg.Service.Extensions))
assert.Equal(t, config.NewComponentID(typeStr), cfg.Service.Extensions[0])
}

func TestLoadConfigError(t *testing.T) {
factories, err := componenttest.NopFactories()
assert.NoError(t, err)

tests := []struct {
name string
expectedErr error
}{
{
"missing_credentials",
errBadCreds,
},
}
for _, testcase := range tests {
t.Run(testcase.name, func(t *testing.T) {
factory := NewFactory()
factories.Extensions[typeStr] = factory
cfg, _ := servicetest.LoadConfig(path.Join(".", "testdata", "config_bad.yaml"), factories)
extension := cfg.Extensions[config.NewComponentIDWithName(typeStr, testcase.name)]
verr := extension.Validate()
require.ErrorIs(t, verr, testcase.expectedErr)
})

}
}
Loading