diff --git a/cli/remotebazel/remotebazel.go b/cli/remotebazel/remotebazel.go index 9abf46e5fc2..f117fb233fd 100644 --- a/cli/remotebazel/remotebazel.go +++ b/cli/remotebazel/remotebazel.go @@ -78,6 +78,7 @@ var ( remoteRunner = RemoteFlagset.String("remote_runner", defaultRemoteExecutionURL, "The Buildbuddy grpc target the remote runner should run on.") timeout = RemoteFlagset.Duration("timeout", 0, "If set, requests that have exceeded this timeout will be canceled automatically. (Ex. --timeout=15m; --timeout=2h)") execPropsFlag = bbflag.New(RemoteFlagset, "runner_exec_properties", []string{}, "Exec properties that will apply to the *ci runner execution*. Key-value pairs should be separated by '=' (Ex. --runner_exec_properties=NAME=VALUE). Can be specified more than once. NOTE: If you want to apply an exec property to the bazel command that's run on the runner, just pass at the end of the command (Ex. bb remote build //... --remote_default_exec_properties=OSFamily=linux).") + remoteHeaders = bbflag.New(RemoteFlagset, "remote_run_header", []string{}, "Remote headers to be applied to the execution request for the remote run. Can be used to set platform properties containing secrets (Ex. --remote_run_header=x-buildbuddy-platform.SECRET_NAME=SECRET_VALUE). Can be specified more than once.") runRemotely = RemoteFlagset.Bool("run_remotely", true, "For `run` commands, whether the target should be run remotely. If false, the target will be built remotely, and then fetched and run locally.") useSystemGitCredentials = RemoteFlagset.Bool("use_system_git_credentials", false, "Whether to use github auth pre-configured on the remote runner. If false, require https and an access token for git access.") runFromBranch = RemoteFlagset.String("run_from_branch", "", "A GitHub branch to base the remote run off. If unset, the remote workspace will mirror your local workspace.") @@ -792,6 +793,7 @@ func Run(ctx context.Context, opts RunOpts, repoConfig *RepoConfig) (int, error) ContainerImage: *containerImage, Env: envVars, ExecProperties: platform.Properties, + RemoteHeaders: *remoteHeaders, RunRemotely: *runRemotely, } req.GetRepoState().Patch = append(req.GetRepoState().Patch, repoConfig.Patches...) diff --git a/docs/remote-bazel-introduction.md b/docs/remote-bazel-introduction.md index c4948cb38d5..b6d06662a2e 100644 --- a/docs/remote-bazel-introduction.md +++ b/docs/remote-bazel-introduction.md @@ -183,6 +183,13 @@ The following configuration options are supported: - `--runner_exec_properties`: Platform properties to configure the remote runner. - Ex. To run on a self-hosted executor pool, you could use `--runner_exec_properties=use-self-hosted-executors=true --runner_exec_properties=Pool=custom-pool` +- `--remote_run_header`: Remote headers to be applied to the execution request for the remote runner. + - These are useful for passing platform properties containing secrets. Platform + properties set via remote header will not be displayed on the UI and will not + be included in the snapshot key (which contains regular platform properties). + This is helpful when passing short-lived credentials that you don't want invalidating + your snapshots. + - See `Private Docker images` below for an example. - `--timeout` (Ex. '30m', '1h'): If set, remote runs that have been running for longer than this duration will be canceled automatically. This only applies to a single attempt, and does not include multiple retry attempts. @@ -220,6 +227,24 @@ https://app.buildbuddy.io/api/v1/Run If your GitHub repo is private, you must first link it at https://app.buildbuddy.io/workflows/ to authorize the remote runner to access it. +### Private Docker images + +If you would like the remote runner to start from a private container image, you +can pass credentials via remote headers. + +See https://www.buildbuddy.io/docs/rbe-platforms/#passing-credentials-for-docker-images +for more details on passing credentials for private images. + +See `Configuring the remote runner` above for more information about remote headers. + +```bash +bb remote \ + --container_image=docker:// \ + --remote_run_header=x-buildbuddy-platform.container-registry-username=USERNAME \ + --remote_run_header=x-buildbuddy-platform.container-registry-password=PASSWORD \ + build //... +``` + ### GitHub Enterprise In order to use Remote Bazel with GitHub Enterprise, you must set `--use_system_git_credentials` diff --git a/enterprise/server/api/api_server.go b/enterprise/server/api/api_server.go index d67a5118a09..b03420e91fa 100644 --- a/enterprise/server/api/api_server.go +++ b/enterprise/server/api/api_server.go @@ -603,6 +603,7 @@ func (s *APIServer) Run(ctx context.Context, req *apipb.RunRequest) (*apipb.RunR Env: req.GetEnv(), Timeout: req.GetTimeout(), ExecProperties: execProps, + RemoteHeaders: req.GetRemoteHeaders(), RunRemotely: true, }) if err != nil { diff --git a/enterprise/server/hostedrunner/BUILD b/enterprise/server/hostedrunner/BUILD index ada4eb279a1..9981804b2d9 100644 --- a/enterprise/server/hostedrunner/BUILD +++ b/enterprise/server/hostedrunner/BUILD @@ -31,6 +31,7 @@ go_library( "@com_github_google_uuid//:uuid", "@in_gopkg_yaml_v2//:yaml_v2", "@org_golang_google_genproto//googleapis/longrunning", + "@org_golang_google_grpc//metadata", "@org_golang_google_grpc//status", "@org_golang_google_protobuf//types/known/durationpb", ], diff --git a/enterprise/server/hostedrunner/hostedrunner.go b/enterprise/server/hostedrunner/hostedrunner.go index 07badf1b925..a230c8492a8 100644 --- a/enterprise/server/hostedrunner/hostedrunner.go +++ b/enterprise/server/hostedrunner/hostedrunner.go @@ -28,6 +28,7 @@ import ( "github.com/buildbuddy-io/buildbuddy/server/util/status" "github.com/google/uuid" "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc/metadata" "google.golang.org/protobuf/types/known/durationpb" "gopkg.in/yaml.v2" @@ -375,6 +376,17 @@ func (r *runnerService) Run(ctx context.Context, req *rnpb.RunRequest) (*rnpb.Ru if err != nil { return nil, status.WrapError(err, "add request metadata to ctx") } + // Apply remote headers + for _, h := range req.GetRemoteHeaders() { + parts := strings.SplitN(h, "=", 2) + if len(parts) != 2 { + return nil, status.InvalidArgumentErrorf("malformed remote header %s: key-value pairs should be separated by '='", h) + } + headerKey := parts[0] + headerVal := parts[1] + execCtx = metadata.AppendToOutgoingContext(execCtx, headerKey, headerVal) + } + execCtx, err = r.withCredentials(execCtx, req) if err != nil { return nil, status.WrapError(err, "authenticate ctx") diff --git a/enterprise/server/test/integration/remote_bazel/BUILD b/enterprise/server/test/integration/remote_bazel/BUILD index 9b1c84abe1f..785818fa07c 100644 --- a/enterprise/server/test/integration/remote_bazel/BUILD +++ b/enterprise/server/test/integration/remote_bazel/BUILD @@ -4,6 +4,7 @@ package(default_visibility = ["//enterprise:__subpackages__"]) go_test( name = "remote_bazel_test", + size = "enormous", srcs = ["remote_bazel_test.go"], exec_properties = { "include-secrets": "true", diff --git a/proto/api/v1/remote_runner.proto b/proto/api/v1/remote_runner.proto index 8e5ceba4ee2..0c3cdc32669 100644 --- a/proto/api/v1/remote_runner.proto +++ b/proto/api/v1/remote_runner.proto @@ -28,6 +28,13 @@ message RunRequest { // Ex. {"OSFamily":"linux", "Arch":"amd64"} map platform_properties = 6; + // Remote headers to be applied to the execution request for the remote + // runner. + // + // Can be used to set platform properties containing secrets. + // Ex. --remote_headers=x-buildbuddy-platform.SECRET_NAME=SECRET_VALUE + repeated string remote_headers = 10; + // Max time before run should be canceled. // Ex. "15s", "2h" string timeout = 7; diff --git a/proto/runner.proto b/proto/runner.proto index d6a3b5b70d4..e9a7b031665 100644 --- a/proto/runner.proto +++ b/proto/runner.proto @@ -54,6 +54,13 @@ message RunRequest { repeated build.bazel.remote.execution.v2.Platform.Property exec_properties = 13; + // Remote headers to be applied to the execution request for the remote + // runner. + // + // Can be used to set platform properties containing secrets. + // Ex. --remote_headers=x-buildbuddy-platform.SECRET_NAME=SECRET_VALUE + repeated string remote_headers = 17; + // If true, binaries generated by a `bazel run` command will be executed // remotely. If false or unset, run metadata (runtime flags, files, etc) will // be collected so the binary can be run elsewhere.