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

🚧 Add step to wrap the OTEL context and mask the credentials #206

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void enrichOpenTelemetryAutoConfigureConfigProperties(Map<String, String>
@Override
public void enrichOtelEnvironmentVariables(Map<String, String> environmentVariables) {
// TODO don't overwrite OTEL_EXPORTER_OTLP_HEADERS if already defined, just append to it
environmentVariables.put("OTEL_EXPORTER_OTLP_HEADERS", "authorization=Bearer " + this.getAuthenticationHeaderValue());
environmentVariables.put(OTelEnvironmentVariablesConventions.OTEL_EXPORTER_OTLP_HEADERS, "authorization=Bearer " + this.getAuthenticationHeaderValue());
}

public String getTokenId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void enrichOpenTelemetryAutoConfigureConfigProperties(Map<String, String>
@Override
public void enrichOtelEnvironmentVariables(Map<String, String> environmentVariables) {
// TODO don't overwrite OTEL_EXPORTER_OTLP_HEADERS if already defined, just append to it
environmentVariables.put("OTEL_EXPORTER_OTLP_HEADERS", this.getHeaderName() + "=" + this.getAuthenticationHeaderValue());
environmentVariables.put(OTelEnvironmentVariablesConventions.OTEL_EXPORTER_OTLP_HEADERS, this.getHeaderName() + "=" + this.getAuthenticationHeaderValue());
}

public String getHeaderName() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class OTelEnvironmentVariablesConventions {
public static final String OTEL_EXPORTER_OTLP_ENDPOINT = "OTEL_EXPORTER_OTLP_ENDPOINT";
public static final String OTEL_EXPORTER_OTLP_INSECURE = "OTEL_EXPORTER_OTLP_INSECURE";
public static final String OTEL_EXPORTER_OTLP_TIMEOUT = "OTEL_EXPORTER_OTLP_TIMEOUT";
public static final String OTEL_EXPORTER_OTLP_HEADERS = "OTEL_EXPORTER_OTLP_HEADERS";

public static final String SPAN_ID = "SPAN_ID";
public static final String TRACE_ID = "TRACE_ID";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright The Original Author or Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.jenkins.plugins.opentelemetry.steps;

import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.console.ConsoleLogFilter;
import hudson.console.LineTransformationOutputStream;
import hudson.model.AbstractProject;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.tasks.BuildWrapperDescriptor;
import io.jenkins.plugins.opentelemetry.JenkinsOpenTelemetryPluginConfiguration;
import io.jenkins.plugins.opentelemetry.semconv.OTelEnvironmentVariablesConventions;
import jenkins.tasks.SimpleBuildWrapper;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import javax.inject.Inject;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

/**
* A simple build wrapper to contribute OpenTelemetry environment variables.
*/
public class OpenTelemetryEnvBuildWrapper extends SimpleBuildWrapper {
private final static Logger LOGGER = Logger.getLogger(OpenTelemetryEnvBuildWrapper.class.getName());

private JenkinsOpenTelemetryPluginConfiguration jenkinsOpenTelemetryPluginConfiguration;

private String passwordToMask;
private boolean override;

@DataBoundConstructor
public OpenTelemetryEnvBuildWrapper() {
}

@DataBoundSetter
public void setOverride(boolean override) {
this.override = override;
}

@Override
public void setUp(Context context, Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener, EnvVars initialEnvironment) throws IOException, InterruptedException {
Map<String, String> otelConfiguration = jenkinsOpenTelemetryPluginConfiguration.getOtelConfigurationAsEnvironmentVariables();
for (Map.Entry<String, String> otelEnvironmentVariable : otelConfiguration.entrySet()) {
String envVarValue = otelEnvironmentVariable.getValue();
String envVarName = otelEnvironmentVariable.getKey();
if (envVarValue != null) {
// TODO: allow to override them option.
if (context.getEnv().containsKey(envVarName)) {
LOGGER.log(Level.FINE, () -> "Overwrite environment variable '" + envVarName + "'");
}
context.env(envVarName, envVarValue);
}
}

String header = otelConfiguration.get(OTelEnvironmentVariablesConventions.OTEL_EXPORTER_OTLP_HEADERS);
if (header != null) {
this.passwordToMask = header;
}
}

@Override
public ConsoleLogFilter createLoggerDecorator(Run<?, ?> build) {
return new FilterImpl(passwordToMask);
}

@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
}

@Symbol("withOtelEnv")
@Extension
public static final class DescriptorImpl extends BuildWrapperDescriptor {

@Override
public boolean isApplicable(AbstractProject<?, ?> item) {
return true;
}

@Override
public String getDisplayName() {
return "Binds OpenTelemetry Environment Variables";
}
}

private static final class FilterImpl extends ConsoleLogFilter implements Serializable {
private static final long serialVersionUID = 10L;

private final String passwordToMask;

FilterImpl(String passwordToMask) {
this.passwordToMask = passwordToMask;
}

@Override
public OutputStream decorateLogger(Run _ignore, OutputStream logger) throws IOException, InterruptedException {
return new PasswordsMaskOutputStream(logger, passwordToMask);
}
}

/** Similar to {@code MaskPasswordsOutputStream}. */
public static final class PasswordsMaskOutputStream extends LineTransformationOutputStream {
private static final String MASKED_PASSWORD = "******";
private final OutputStream logger;
private final Pattern passwordsAsPattern;

public PasswordsMaskOutputStream(OutputStream logger, String passwordToMask) {
this.logger = logger;

if (StringUtils.isNotEmpty(passwordToMask)) {
StringBuilder regex = new StringBuilder().append('(');
regex.append(Pattern.quote(passwordToMask));
regex.append(')');
this.passwordsAsPattern = Pattern.compile(regex.toString());
} else{
this.passwordsAsPattern = null;
}
}

@Override
protected void eol(byte[] bytes, int len) throws IOException {
String line = new String(bytes, 0, len, StandardCharsets.UTF_8);
if(passwordsAsPattern != null) {
line = passwordsAsPattern.matcher(line).replaceAll(MASKED_PASSWORD);
}
logger.write(line.getBytes(StandardCharsets.UTF_8));
}
}

@Inject
public void setJenkinsOpenTelemetryPluginConfiguration(JenkinsOpenTelemetryPluginConfiguration jenkinsOpenTelemetryPluginConfiguration) {
this.jenkinsOpenTelemetryPluginConfiguration = jenkinsOpenTelemetryPluginConfiguration;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
This step allows users to bind the OpenTelemetry configuration to environment variables.
</div>