Skip to content
4 changes: 4 additions & 0 deletions gcp-auth-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ Here is a list of required and optional configuration available for the extensio

- Can also be configured using `google.cloud.quota.project` system property.

- `GOOGLE_OTEL_AUTH_TARGET_SIGNALS`: Environment variable that specifies a comma-separated list of OpenTelemetry signals for which this authentication extension should be active. Valid values contain - `metrics`, `traces` or `all`. If left unspecified, `all` is assumed meaning the extension will attempt to apply authentication to exports for all signals.

- Can also be configured using `google.otel.auth.target.signals` system property.

## Usage

### With OpenTelemetry Java agent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,27 @@ public enum ConfigurableOption {
* href="https://cloud.google.com/docs/quotas/set-quota-project">official GCP client
* libraries</a>.
*/
GOOGLE_CLOUD_QUOTA_PROJECT("Google Cloud Quota Project ID");
GOOGLE_CLOUD_QUOTA_PROJECT("Google Cloud Quota Project ID"),

/**
* Specifies a comma-separated list of OpenTelemetry signals for which this authentication
* extension should be active. The authentication mechanisms provided by this extension will only
* be applied to the listed signals. If not set, {@code all} is assumed to be set which means
* authentication is enabled for all supported signals.
*
* <p>Valid signal values are:
*
* <ul>
* <li>{@code metrics} - Enables authentication for metric exports.
* <li>{@code traces} - Enables authentication for trace exports.
* <li>{@code all} - Enables authentication for all exports.
* </ul>
*
* <p>The values are case-sensitive. Whitespace around commas and values is ignored. Can be
* configured using the environment variable `GOOGLE_OTEL_AUTH_TARGET_SIGNALS` or the system
* property `google.otel.auth.target.signals`.
*/
GOOGLE_OTEL_AUTH_TARGET_SIGNALS("Target Signals for Google Auth Extension");

private final String userReadableName;
private final String environmentVariableName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,28 @@
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.contrib.gcp.auth.GoogleAuthException.Reason;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;

/**
* An AutoConfigurationCustomizerProvider for Google Cloud Platform (GCP) OpenTelemetry (OTLP)
Expand All @@ -46,13 +53,29 @@ public class GcpAuthAutoConfigurationCustomizerProvider
static final String QUOTA_USER_PROJECT_HEADER = "x-goog-user-project";
static final String GCP_USER_PROJECT_ID_KEY = "gcp.project_id";

static final String SIGNAL_TYPE_TRACES = "traces";
static final String SIGNAL_TYPE_METRICS = "metrics";
static final String SIGNAL_TYPE_ALL = "all";

/**
* Customizes the provided {@link AutoConfigurationCustomizer}.
* Customizes the provided {@link AutoConfigurationCustomizer} such that authenticated exports to
* GCP Telemetry API are possible from the configured OTLP exporter.
*
* <p>This method attempts to retrieve Google Application Default Credentials (ADC) and performs
* the following: - Adds authorization headers to the configured {@link SpanExporter} based on the
* retrieved credentials. - Adds default properties for OTLP endpoint and resource attributes for
* GCP integration.
* the following:
*
* <ul>
* <li>Verifies whether the configured OTLP endpoint (base or signal specific) is a known GCP
* endpoint.
* <li>If the configured base OTLP endpoint is a known GCP Telemetry API endpoint, customizes
* both the configured OTLP {@link SpanExporter} and {@link MetricExporter}.
* <li>If the configured signal specific endpoint is a known GCP Telemetry API endpoint,
* customizes only the signal specific exporter.
* </ul>
*
* The 'customization' performed includes customizing the exporters by adding required headers to
* the export calls made and customizing the resource by adding required resource attributes to
* enable GCP integration.
*
* @param autoConfiguration the AutoConfigurationCustomizer to customize.
* @throws GoogleAuthException if there's an error retrieving Google Application Default
Expand All @@ -61,7 +84,7 @@ public class GcpAuthAutoConfigurationCustomizerProvider
* not configured through environment variables or system properties.
*/
@Override
public void customize(AutoConfigurationCustomizer autoConfiguration) {
public void customize(@Nonnull AutoConfigurationCustomizer autoConfiguration) {
GoogleCredentials credentials;
try {
credentials = GoogleCredentials.getApplicationDefault();
Expand All @@ -70,7 +93,10 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) {
}
autoConfiguration
.addSpanExporterCustomizer(
(exporter, configProperties) -> addAuthorizationHeaders(exporter, credentials))
(spanExporter, configProperties) -> customizeSpanExporter(spanExporter, credentials))
.addMetricExporterCustomizer(
(metricExporter, configProperties) ->
customizeMetricExporter(metricExporter, credentials))
.addResourceCustomizer(GcpAuthAutoConfigurationCustomizerProvider::customizeResource);
}

Expand All @@ -79,6 +105,34 @@ public int order() {
return Integer.MAX_VALUE - 1;
}

private static SpanExporter customizeSpanExporter(
SpanExporter exporter, GoogleCredentials credentials) {
if (isSignalTargeted(SIGNAL_TYPE_TRACES)) {
return addAuthorizationHeaders(exporter, credentials);
}
return exporter;
}

private static MetricExporter customizeMetricExporter(
MetricExporter exporter, GoogleCredentials credentials) {
if (isSignalTargeted(SIGNAL_TYPE_METRICS)) {
return addAuthorizationHeaders(exporter, credentials);
}
return exporter;
}

// Checks if the auth extension is configured to target the passed signal for authentication.
private static boolean isSignalTargeted(String checkSignal) {
String userSpecifiedTargetedSignals =
ConfigurableOption.GOOGLE_OTEL_AUTH_TARGET_SIGNALS.getConfiguredValueWithFallback(
() -> SIGNAL_TYPE_ALL);
return Arrays.stream(userSpecifiedTargetedSignals.split(","))
.map(String::trim)
.anyMatch(
targetedSignal ->
targetedSignal.equals(checkSignal) || targetedSignal.equals(SIGNAL_TYPE_ALL));
}

// Adds authorization headers to the calls made by the OtlpGrpcSpanExporter and
// OtlpHttpSpanExporter.
private static SpanExporter addAuthorizationHeaders(
Expand All @@ -97,6 +151,24 @@ private static SpanExporter addAuthorizationHeaders(
return exporter;
}

// Adds authorization headers to the calls made by the OtlpGrpcMetricExporter and
// OtlpHttpMetricExporter.
private static MetricExporter addAuthorizationHeaders(
MetricExporter exporter, GoogleCredentials credentials) {
if (exporter instanceof OtlpHttpMetricExporter) {
OtlpHttpMetricExporterBuilder builder =
((OtlpHttpMetricExporter) exporter)
.toBuilder().setHeaders(() -> getRequiredHeaderMap(credentials));
return builder.build();
} else if (exporter instanceof OtlpGrpcMetricExporter) {
OtlpGrpcMetricExporterBuilder builder =
((OtlpGrpcMetricExporter) exporter)
.toBuilder().setHeaders(() -> getRequiredHeaderMap(credentials));
return builder.build();
}
return exporter;
}

private static Map<String, String> getRequiredHeaderMap(GoogleCredentials credentials) {
Map<String, List<String>> gcpHeaders;
try {
Expand Down
Loading
Loading