An executable rule which can be executed via bazel run
is the natural
way to model interactions with external systems in Bazel such as uploading
build artifacts to a remote artifact repository. For example, imagine
a rules_artifactory
ruleset which includes a rule artifactory_push()
executable rule which uploads a compiled .dpkg
to an Artifactory
apt repository, or a rules_docker
ruleset which has a rule
docker_push()
which pushes a Docker image to a remote image repository.
One key design consideration for these rules is “how will users securely pass authentication credentials (e.g. a username and a password) to the push job”? After all, it is impractical to have the rules know about every possible secret management system that a user might use. For example, one user could be using HashiCorp’s Vault, another Azure’s Key Vault, another Thycotic’s Secret Server, and there are probably dozens more.
A commonly-used technique is for these tools to allow users pass in
secrets via environment variables. For example, Terraform allows
specifying an Azure service principal using the ARM_CLIENT_ID
,
ARM_CLIENT_SECRET
, and ARM_TENANT_ID
environment variables.
But how to pass these environment variables to the bazel run
job?
One option is to use a env
rule attribute, as in:
|
|
But this is obviously insecure, as we’d be storing secrets in source control.
The technique I’ve been using lately is to wrap the executable rule
in a sh_binary()
and have the wrapping sh_binary()
be responsible
for retrieving the secrets from the secret management system, setting
the appropriate environment variables, and then invoking the original
bazel run
job.
For example, let’s say you store secrets in Azure Key Vault and you have an authenticated Azure CLI set up (likely using interactive authentication for individual developers and Azure Managed Service Identity in CI). Here’s some example code on how to apply the above technique:
|
|
|
|
You can then perform an authenticated push using
bazel run //:authenticated_my_push
.
This technique can be applied to wrap any bazel run
action with
arbitrary code.