-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an experimental googleclientauth extension (#703)
* add gcp client auth extension
- Loading branch information
Showing
14 changed files
with
1,016 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Authenticator - Google Client Credentials | ||
|
||
| Status | | | ||
| ------------- |-----------| | ||
| Stability | [alpha] | | ||
|
||
[alpha]: https://github.com/open-telemetry/opentelemetry-collector#alpha | ||
|
||
This extension provides Google OAuth2 Client Credentials and Metadata for gRPC and http based exporters. | ||
|
||
The authenticator type has to be set to `googleclientauth`. | ||
|
||
## Configuration | ||
|
||
```yaml | ||
extensions: | ||
googleclientauth: | ||
|
||
receivers: | ||
otlp: | ||
protocols: | ||
grpc: | ||
|
||
exporters: | ||
otlp/withauth: | ||
endpoint: 0.0.0.0:5000 | ||
ca_file: /tmp/certs/ca.pem | ||
auth: | ||
authenticator: googleclientauth | ||
|
||
service: | ||
extensions: [googleclientauth] | ||
pipelines: | ||
metrics: | ||
receivers: [otlp] | ||
processors: [] | ||
exporters: [otlp/withauth] | ||
``` | ||
Following are the configuration fields: | ||
- **project** - The Google Cloud Project telemetry is sent to if the gcp.project.id resource attribute is not set. If unspecified, this is determined using application default credentials. | ||
- [**scopes**](https://datatracker.ietf.org/doc/html/rfc6749#section-3.3) - The oauth 2.0 scopes requested by the extension. | ||
- [**quota_project**](https://cloud.google.com/apis/docs/system-parameters) - The project for quota and billing purposes. The caller must have serviceusage.services.use permission on the project. | ||
## Building into a Collector | ||
This extension is not included in any collector distributions today. To build a collector with this component, add the following to your collector builder configuration: | ||
```yaml | ||
extensions: | ||
- import: github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension | ||
gomod: github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension v0.43.0 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// 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 googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension" | ||
|
||
import ( | ||
"go.opentelemetry.io/collector/component" | ||
) | ||
|
||
// Config stores the configuration for GCP Client Credentials. | ||
type Config struct { | ||
// Project is the project telemetry is sent to if the gcp.project.id | ||
// resource attribute is not set. If unspecified, this is determined using | ||
// application default credentials. | ||
Project string `mapstructure:"project"` | ||
|
||
// QuotaProject specifies a project for quota and billing purposes. The | ||
// caller must have serviceusage.services.use permission on the project. | ||
// | ||
// For more information please read: | ||
// https://cloud.google.com/apis/docs/system-parameters | ||
QuotaProject string `mapstructure:"quota_project"` | ||
|
||
// Scope specifies optional requested permissions. | ||
// See https://datatracker.ietf.org/doc/html/rfc6749#section-3.3 | ||
Scopes []string `mapstructure:"scopes,omitempty"` | ||
|
||
// TODO: Support impersonation, similar to what exists in the googlecloud collector exporter. | ||
} | ||
|
||
var _ component.Config = (*Config)(nil) | ||
|
||
// Validate checks if the extension configuration is valid. | ||
func (cfg *Config) Validate() error { | ||
return nil | ||
} | ||
|
||
// defaultScopes are the scopes required for writing logs, metrics, and traces. | ||
var defaultScopes = []string{ | ||
"https://www.googleapis.com/auth/cloud-platform", | ||
"https://www.googleapis.com/auth/logging.write", | ||
"https://www.googleapis.com/auth/monitoring.write", | ||
"https://www.googleapis.com/auth/trace.append", | ||
} | ||
|
||
func createDefaultConfig() component.Config { | ||
return &Config{ | ||
Scopes: defaultScopes, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// 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 googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension" | ||
|
||
import ( | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.opentelemetry.io/collector/component" | ||
"go.opentelemetry.io/collector/confmap/confmaptest" | ||
|
||
"github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension/internal/metadata" | ||
) | ||
|
||
func TestLoadConfig(t *testing.T) { | ||
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) | ||
require.NoError(t, err) | ||
factory := NewFactory() | ||
cfg := factory.CreateDefaultConfig() | ||
|
||
sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "").String()) | ||
require.NoError(t, err) | ||
require.NoError(t, component.UnmarshalConfig(sub, cfg)) | ||
|
||
assert.Equal(t, cfg.(*Config), factory.CreateDefaultConfig().(*Config)) | ||
|
||
sub, err = cm.Sub(component.NewIDWithName(metadata.Type, "customname").String()) | ||
require.NoError(t, err) | ||
require.NoError(t, component.UnmarshalConfig(sub, cfg)) | ||
|
||
assert.Equal(t, | ||
&Config{ | ||
Project: "my-project", | ||
Scopes: []string{"https://www.something.com/hello", "https://www.something.com/world"}, | ||
QuotaProject: "other-project", | ||
}, | ||
cfg, | ||
) | ||
} | ||
|
||
func TestValidate(t *testing.T) { | ||
assert.NoError(t, NewFactory().CreateDefaultConfig().(*Config).Validate()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// 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 googleclientauthextension provides gRPC and HTTP credentials and metadata | ||
// using Application Default Credentials: https://cloud.google.com/docs/authentication#adc | ||
package googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// 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 googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension" | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"errors" | ||
|
||
"go.opentelemetry.io/collector/component" | ||
"go.opentelemetry.io/collector/extension" | ||
"go.opentelemetry.io/collector/extension/auth" | ||
"golang.org/x/oauth2/google" | ||
"google.golang.org/grpc/credentials/oauth" | ||
|
||
"github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension/internal/metadata" | ||
) | ||
|
||
// NewFactory creates a factory for the GCP Auth extension. | ||
func NewFactory() extension.Factory { | ||
return extension.NewFactory( | ||
metadata.Type, | ||
createDefaultConfig, | ||
createExtension, | ||
metadata.ExtensionStability, | ||
) | ||
} | ||
|
||
func createExtension(ctx context.Context, set extension.CreateSettings, cfg component.Config) (extension.Extension, error) { | ||
config := cfg.(*Config) | ||
creds, err := google.FindDefaultCredentials(ctx, config.Scopes...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if config.Project == "" { | ||
config.Project = creds.ProjectID | ||
} | ||
if config.Project == "" { | ||
return nil, errors.New("no project set in config or found with application default credentials") | ||
} | ||
if config.QuotaProject == "" { | ||
config.QuotaProject = quotaProjectFromCreds(creds) | ||
} | ||
|
||
ca := clientAuthenticator{ | ||
TokenSource: oauth.TokenSource{TokenSource: creds.TokenSource}, | ||
config: config, | ||
} | ||
|
||
return auth.NewClient( | ||
auth.WithClientRoundTripper(ca.roundTripper), | ||
auth.WithClientPerRPCCredentials(ca.perRPCCredentials), | ||
), nil | ||
} | ||
|
||
// clientAuthenticator supplies credentials from an oauth.TokenSource. | ||
type clientAuthenticator struct { | ||
oauth.TokenSource | ||
config *Config | ||
} | ||
|
||
// quotaProjectFromCreds retrieves quota project from the credentials file. | ||
// Based on how the google go client gets quota project: | ||
// https://github.com/googleapis/google-api-go-client/blob/113082d14d54f188d1b6c34c652e416592fc51b5/internal/creds.go#L159 | ||
func quotaProjectFromCreds(creds *google.Credentials) string { | ||
if creds == nil { | ||
return "" | ||
} | ||
var v struct { | ||
QuotaProject string `json:"quota_project_id"` | ||
} | ||
if err := json.Unmarshal(creds.JSON, &v); err != nil { | ||
return "" | ||
} | ||
return v.QuotaProject | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// 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 googleclientauthextension // import "github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension" | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"go.opentelemetry.io/collector/component/componenttest" | ||
"go.opentelemetry.io/collector/extension" | ||
) | ||
|
||
func TestCreateDefaultConfig(t *testing.T) { | ||
factory := NewFactory() | ||
cfg := factory.CreateDefaultConfig() | ||
assert.NotNil(t, cfg, "failed to create default config") | ||
assert.NoError(t, componenttest.CheckConfigStruct(cfg)) | ||
} | ||
|
||
func TestNewFactory(t *testing.T) { | ||
f := NewFactory() | ||
assert.NotNil(t, f) | ||
} | ||
|
||
func TestCreateExtension(t *testing.T) { | ||
t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "testdata/fake_creds.json") | ||
ext, err := NewFactory().CreateExtension(context.Background(), extension.CreateSettings{}, createDefaultConfig()) | ||
assert.NotNil(t, ext) | ||
assert.NoError(t, err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
module github.com/GoogleCloudPlatform/opentelemetry-operations-go/extension/googleclientauthextension | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/stretchr/testify v1.8.4 | ||
go.opentelemetry.io/collector/component v0.82.0 | ||
go.opentelemetry.io/collector/confmap v0.82.0 | ||
go.opentelemetry.io/collector/extension v0.82.0 | ||
go.opentelemetry.io/collector/extension/auth v0.82.0 | ||
golang.org/x/oauth2 v0.10.0 | ||
google.golang.org/grpc v1.57.0 | ||
) | ||
|
||
require ( | ||
cloud.google.com/go/compute v1.20.1 // indirect | ||
cloud.google.com/go/compute/metadata v0.2.3 // indirect | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/gogo/protobuf v1.3.2 // indirect | ||
github.com/golang/protobuf v1.5.3 // indirect | ||
github.com/knadh/koanf v1.5.0 // indirect | ||
github.com/knadh/koanf/v2 v2.0.1 // indirect | ||
github.com/mitchellh/copystructure v1.2.0 // indirect | ||
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect | ||
github.com/mitchellh/reflectwalk v1.0.2 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
go.opentelemetry.io/collector/config/configtelemetry v0.82.0 // indirect | ||
go.opentelemetry.io/collector/featuregate v1.0.0-rcv0014 // indirect | ||
go.opentelemetry.io/collector/pdata v1.0.0-rcv0014 // indirect | ||
go.opentelemetry.io/otel v1.16.0 // indirect | ||
go.opentelemetry.io/otel/metric v1.16.0 // indirect | ||
go.opentelemetry.io/otel/trace v1.16.0 // indirect | ||
go.uber.org/multierr v1.11.0 // indirect | ||
go.uber.org/zap v1.25.0 // indirect | ||
golang.org/x/net v0.12.0 // indirect | ||
golang.org/x/sys v0.10.0 // indirect | ||
golang.org/x/text v0.11.0 // indirect | ||
google.golang.org/appengine v1.6.7 // indirect | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect | ||
google.golang.org/protobuf v1.31.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) | ||
|
||
retract ( | ||
v0.76.2 | ||
v0.76.1 | ||
v0.65.0 | ||
) |
Oops, something went wrong.