From 98f70abfd7a396618fcdf940896b005be8fde0d6 Mon Sep 17 00:00:00 2001 From: Arthur Busser <22616578+busser@users.noreply.github.com> Date: Sun, 28 May 2023 20:42:33 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9A=20Rewrite=20documentation=20to=20f?= =?UTF-8?q?ocus=20on=20use-cases=20(#356)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .goreleaser.yaml | 68 ++- Dockerfile | 6 +- Makefile | 2 +- README.md | 421 ++++++++++++------ examples/dockerfile/Dockerfile | 5 - examples/dockerfile/README.md | 12 - internal/cmd/murmur.go | 1 + internal/cmd/murmur_exec.go | 20 + internal/cmd/murmur_run.go | 22 +- internal/murmur/filters/jsonpath/filter.go | 4 +- .../murmur/filters/jsonpath/filter_test.go | 18 +- 11 files changed, 390 insertions(+), 189 deletions(-) delete mode 100644 examples/dockerfile/Dockerfile delete mode 100644 examples/dockerfile/README.md create mode 100644 internal/cmd/murmur_exec.go diff --git a/.goreleaser.yaml b/.goreleaser.yaml index a7a5e8f0..c02e44ee 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,31 +1,71 @@ before: hooks: - go mod tidy + builds: - - env: - - CGO_ENABLED=0 - goos: - - linux - - windows - - darwin - main: ./ + - id: murmur binary: murmur + env: [CGO_ENABLED=0] + goos: [linux, windows, darwin] + main: ./ + # The project was renamed "murmur" in May 2023. For backwards compatibility, + # we continue to publish artifacts named "whisper". + - id: whisper + binary: whisper + env: [CGO_ENABLED=0] + goos: [linux, windows, darwin] + main: ./ + archives: - - replacements: - darwin: Darwin - linux: Linux - windows: Windows - 386: i386 - amd64: x86_64 + - id: murmur + builds: [murmur] + name_template: >- + {{- .ProjectName }}_ + {{- .Version }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + format_overrides: + - goos: windows + format: zip + # The project was renamed "murmur" in May 2023. For backwards compatibility, + # we continue to publish artifacts named "whisper". + - id: whisper + builds: [whisper] + name_template: >- + whisper_ + {{- .Version }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} format_overrides: - goos: windows format: zip + dockers: - - image_templates: + - id: murmur + ids: [murmur] + build_flag_templates: + - --build-arg=BINARY=murmur + image_templates: - ghcr.io/busser/murmur:{{ .Tag }} - ghcr.io/busser/murmur:v{{ .Major }}.{{ .Minor }} - ghcr.io/busser/murmur:v{{ .Major }} - ghcr.io/busser/murmur:latest + # The project was renamed "murmur" in May 2023. For backwards compatibility, + # we continue to publish artifacts named "whisper". + - id: whisper + ids: [whisper] + build_flag_templates: + - --build-arg=BINARY=whisper + image_templates: + - ghcr.io/busser/whisper:{{ .Tag }} + - ghcr.io/busser/whisper:v{{ .Major }}.{{ .Minor }} + - ghcr.io/busser/whisper:v{{ .Major }} + - ghcr.io/busser/whisper:latest + checksum: name_template: "checksums.txt" snapshot: diff --git a/Dockerfile b/Dockerfile index abcb146c..d58490af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,10 @@ FROM scratch +ARG BINARY=murmur + LABEL org.opencontainers.image.source=https://github.com/busser/murmur # The binary is built beforehand. -COPY murmur / +COPY ${BINARY} / -ENTRYPOINT ["/murmur"] +ENTRYPOINT ["/${BINARY}"] diff --git a/Makefile b/Makefile index 6e025993..30b9d1d6 100644 --- a/Makefile +++ b/Makefile @@ -56,4 +56,4 @@ build: fmt vet ## Build murmur binary. release: test ## Release a new version. git tag -a "$(VERSION)" -m "$(VERSION)" git push origin "$(VERSION)" - goreleaser release --rm-dist + goreleaser release --clean diff --git a/README.md b/README.md index 07f3bd8a..82cb3b5d 100644 --- a/README.md +++ b/README.md @@ -4,62 +4,76 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/busser/murmur)](https://goreportcard.com/report/github.com/busser/murmur) ![tests-passing](https://github.com/busser/murmur/actions/workflows/ci.yml/badge.svg) -Plug-and-play entrypoint to pass secrets as environment variables to a process. - -- [How it works](#how-it-works) -- [Using murmur locally](#using-murmur-locally) -- [Including murmur in a Docker image](#including-murmur-in-a-docker-image) -- [Secret providers](#secret-providers) - - [Scaleway Secret Manager](#scaleway-secret-manager) - - [Azure Key Vault](#azure-key-vault) - - [AWS Secrets Manager](#aws-secrets-manager) - - [Google Secret Manager](#google-secret-manager) - - [Hashicorp Vault](#hashicorp-vault) - - [Passthrough](#passthrough) -- [Filters](#filters) - - [JSONPath](#jsonpath) -- [Troubleshooting](#troubleshooting) - - [`Error: unknown flag`](#error-unknown-flag) - -## How it works - -Murmur must run as your application's entrypoint. This means that instead of -running this command to start your application: +Plug-and-play executable to pass secrets as environment variables to a process. + +Murmur is a small binary that reads its environment variables, replaces +references to secrets with the secrets' values, and passes the resulting +variables to your application. Variables that do not reference secrets are +passed as-is. + +Several tools like Murmur exist, each supporting a different secret provider. +Murmur aims to support as many providers as possible, so you can use Murmur no +matter which provider you use. + +| | Scaleway | AWS | Azure | GCP | Vault | 1Password | Doppler | +| ------------------------------------------------------------- | -------- | --- | ----- | --- | ----- | --------- | ------- | +| 🤫 Murmur | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| [Berglas](https://github.com/GoogleCloudPlatform/berglas) | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | +| [Bank Vaults](https://github.com/GoogleCloudPlatform/berglas) | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | +| [1Password CLI](https://developer.1password.com/docs/cli/) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | +| [Doppler CLI](https://github.com/DopplerHQ/cli) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | + +_If you know of a similar tool that is not listed here, please open an issue so +that we can add it to the list._ + +_If you use a secret provider that is not supported by Murmur, please open an +issue so that we can track demand for it._ + +- [Fetching a database password](#fetching-a-database-password) +- [Adding Murmur to a container image](#adding-murmur-to-a-container-image) +- [Adding Murmur to a Kubernetes pod](#adding-murmur-to-a-kubernetes-pod) +- [Parsing JSON secrets](#parsing-json-secrets) +- [Providers and filters](#providers-and-filters) + - [`scwsm` provider: Scaleway Secret Manager](#scwsm-provider-scaleway-secret-manager) + - [`awssm` provider: AWS Secrets Manager](#awssm-provider-aws-secrets-manager) + - [`azkv` provider: Azure Key Vault](#azkv-provider-azure-key-vault) + - [`gcpsm` provider: GCP Secret Manager](#gcpsm-provider-gcp-secret-manager) + - [`passthrough` provider: no-op](#passthrough-provider-no-op) + - [`jsonpath` filter: JSON parsing and templating](#jsonpath-filter-json-parsing-and-templating) +- [Changes from v0.4 to v0.5](#changes-from-v04-to-v05) + +## Fetching a database password + +Murmur runs as a wrapper around any command. For example, if you want to connect +to a PostgreSQL database, instead of running this command: ```bash -/bin/run-my-app +export PGPASSWORD="Q-gVzyDPmvsX6rRAPVjVjvfvR@KGzPJzCEg2" +psql -h 10.1.12.34 -U my-user -d my-database ``` -Run this instead: +You run this instead: ```bash -murmur run -- /bin/run-my-app +export PGPASSWORD="scwsm:database-password" +murmur run -- psql -h 10.1.12.34 -U my-user -d my-database ``` -Murmur reads its environment variables, replaces references to secrets with -the secrets' values, and passes the resulting variables to your application. -Variables that are not references to secrets are passed as is. See -[Secret providers](#secret-providers) below for more details. +Murmur will fetch the value of the `database-password` secret from Scaleway +Secret Manager, set the `PGPASSWORD` environment variable to that value, and +then run `psql`. -Environment variable values can also contain filters that transform the secret's -value. See [Filters](#filters) below for more details. +## Adding Murmur to a container image -## Using murmur locally - -Download the `murmur` binary for your OS and architecture on the -[project's releases page](https://github.com/busser/murmur/releases) and put -the binary in your PATH. - -## Including murmur in a Docker image - -For convenience, the murmur binary is also released as a Docker image. In your -application's Dockerfile, simply add the following line: +Murmur is a static binary, so you can simply copy it into your container image +and use it as your entrypoint. For convenience, the murmur binary is released as +a container image you can copy from in your Dockerfile: ```dockerfile COPY --from=ghcr.io/busser/murmur:latest /murmur /bin/murmur ``` -And then change your image's entrypoint: +Then you can change your image's entrypoint: ```dockerfile # from this: @@ -68,149 +82,282 @@ ENTRYPOINT ["/bin/run-my-app"] ENTRYPOINT ["/bin/murmur", "run", "--", "/bin/run-my-app"] ``` -See [examples/dockerfile](./examples/dockerfile) for actual code. +## Adding Murmur to a Kubernetes pod + +You can use Murmur in a Kubernetes pod even if your application's container +image does not include Murmur. To do so, you can use an init container that +copies Murmur into an emptyDir volume, and then use that volume in your +application's container. + +Here is an example: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: my-app +spec: + initContainers: + - name: copy-murmur + image: ghcr.io/busser/murmur:latest + command: ["cp", "/murmur", "/shared/murmur"] + volumeMounts: + - name: murmur + mountPath: /shared + containers: + - name: my-app + image: my-app:latest + command: ["/shared/murmur", "run", "--", "/bin/run-my-app"] + volumeMounts: + - name: murmur + mountPath: /shared + volumes: + - name: shared + emptyDir: {} +``` -## Secret providers +## Parsing JSON secrets -Murmur supports fetching secrets from the following providers. +Storing secrets as JSON is a common pattern. For example, a secret might contain +a JSON object with multiple fields: -### Scaleway Secret Manager +```json +{ + "host": "10.1.12.34", + "port": 5432, + "database": "my-database", + "username": "my-user", + "password": "Q-gVzyDPmvsX6rRAPVjVjvfvR@KGzPJzCEg2" +} +``` -Murmur will fetch secrets from Scaleway Secret Manager for all environment -variables that start with `scwsm:`. What follows the prefix should reference a -secret. +Murmur can parse that JSON and set environment variables for each field by using +the `jsonpath` filter: -Here are some examples: +```bash +export PGHOST="scwsm:database-credentials|jsonpath:{.host}" +export PGPORT="scwsm:database-credentials|jsonpath:{.port}" +export PGDATABASE="scwsm:database-credentials|jsonpath:{.database}" +export PGUSER="scwsm:database-credentials|jsonpath:{.username}" +export PGPASSWORD="scwsm:database-credentials|jsonpath:{.password}" +murmur run -- psql +``` -- `scwsm:secret-sauce` references the latest value of the secret in the default - region named `secret-sauce`. -- `scwsm:fr-par/secret-sauce` references the latest value of the secret in the - `fr-par` region named `secret-sauce`. -- `scwsm:fr-par/3f34b83f-47a6-4344-bcd4-b63721481cd3` references the latest value - of the secret in the `fr-par` region ID'd by - `3f34b83f-47a6-4344-bcd4-b63721481cd3`. -- `scwsm:secret-sauce#123` references version `123` of the secret in the default - region named `secret-sauce`. -- `scwsm:fr-par/secret-sauce#123` references version `123` of the secret in the - `fr-par` region named `secret-sauce`. +If you have multiple references to the same secret, Murmur will fetch the secret +only once to avoid unnecessary API calls. -The string that comes before `#` could be a name or an ID. If the string is a -UUID, then murmur assumes it is an ID. Otherwise, it assumes it is a name. +Alternatively, you can use the `json` filter to set a single environment +variable with the entire JSON object: -Murmur uses the environment's default credentials to authenticate to Scaleway. -You can configure murmur the same way you can [configure the `scw` CLI](https://github.com/scaleway/scaleway-cli/blob/master/docs/commands/config.md). +```bash +# psql supports connection strings, so we can use a single variable +export PGDATABASE="scwsm:database-credentials|jsonpath:{.username}:{password}@{.host}:{.port}/{.database}" +murmur run -- psql +``` -### Azure Key Vault +Murmur uses the Kubernetes JSONPath syntax for the `jsonpath` filter. See the +[Kubernetes documentation](https://kubernetes.io/docs/reference/kubectl/jsonpath/) +for a full list of capabilities. -Murmur will fetch secrets from Azure Key Vault for all environment variables -that start with `azkv:`. What follows the prefix should reference a secret. +## Providers and filters -Here are some examples: +Murmur's architecture is built around providers and filters. Providers fetch +secrets from a secret manager, and filters parse and transform the secrets. -- `azkv:example.vault.azure.net/secret-sauce` references the latest value of the - `secret-sauce` secret in the `example` Key Vault. -- `azkv:example.vault.azure.net/secret-sauce#5ddc29704c1c4429a4c53605b7949100` - references a specific version of the `secret-sauce` secret in the `example` - Key Vault. +Murmur only edits environment variables which contain valid queries. A valid +query is structured as follows: -Murmur uses the environment's default credentials to authenticate to Azure. You -can set these credentials with the [environment variables listed here](https://github.com/Azure/azure-sdk-for-go/wiki/Set-up-Your-Environment-for-Authentication#configure-defaultazurecredential), -or with workload identity. +```plaintext +provider_id:secret_ref|filter_id:filter_rule +``` -### AWS Secrets Manager +Using a filter is optional, so this is also a valid query: -Murmur will fetch secrets from AWS Secrets Manager for all environment -variables that start with `awssm:`. What follows the prefix should reference a -secret. +```plaintext +provider_id:secret_ref +``` -Here are some examples: +Murmur does not support chaining multiple filters yet. -- `awssm:secret-sauce` references the current value of the `secret-sauce` secret - in the region and account defined by the environment. -- `awssm:secret-sauce#9517cc59-646a-4393-81d7-5e6f2d43cbe7` references a - specific version of the `secret-sauce` secret in the region and account - defined by the environment. -- `awssm:secret-sauce#my-label` references a specific staging label of the - `secret-sauce` secret in the region and account defined by the environment. -- `awssm:arn:aws:secretsmanager:us-east-1:123456789012:secret:secret-sauce-abcdef` - references the secret with the specified ARN. -- `awssm:arn:aws:secretsmanager:us-east-1:123456789012:secret:secret-sauce-abcdef#my-label` - references a specific staging label of the secret with the specified ARN. +### `scwsm` provider: Scaleway Secret Manager -The string that comes after `#` could be a version ID or a version label. If the -string is a UUID, then murmur assumes it is a version ID. Otherwise, it assumes -it is a version label. +To fetch a secret from [Scaleway Secret Manager](https://www.scaleway.com/en/secret-manager/), +the query must be structured as follows: -Murmur uses the environment's default credentials to authenticate to AWS. +```plaintext +scwsm:[region/]{name|id}[#version] +``` -### Google Secret Manager +If `region` is not specified, Murmur will delegate region selection to the +Scaleway SDK. The SDK determines the region based on the environment, by looking +at environment variables and configuration files. -Murmur will fetch secrets from Google Cloud Platform's Secret Manager for all -environment variables that start with `gcpsm:`. What follows the prefix should -reference a secret. +One of `name` or `id` must be specified. Murmur guesses whether the string is a +name or an ID depending on whether it is a valid UUID. UUIDs are treated as IDs, +and other strings are treated as names. -Here are some examples: +The `version` must either be a positive integer or the "latest" string. If +`version` is not specified, Murmur defaults to "latest". -- `gcpsm:example/secret-sauce` references the latest value of the - `secret-sauce` secret in the `example` project. -- `gcpsm:example/secret-sauce#123` references a specific version of the -- `secret-sauce` secret in the `example` project. +Examples: -Murmur uses the environment's default credentials to authenticate to Google -Cloud. You can set these with the `gcloud` CLI, with environment variables, -with Google Cloud's environment service accounts, or with workload identity. +```plaintext +scwsm:my-secret +scwsm:my-secret#123 +scwsm:my-secret#latest -An alternative to murmur, specific to Google Cloud, is [berglas](https://github.com/GoogleCloudPlatform/berglas). +scwsm:fr-par/my-secret +scwsm:fr-par/my-secret#123 +scwsm:fr-par/my-secret#latest -### Hashicorp Vault +scwsm:3f34b83f-47a6-4344-bcd4-b63721481cd3 +scwsm:3f34b83f-47a6-4344-bcd4-b63721481cd3#123 +scwsm:3f34b83f-47a6-4344-bcd4-b63721481cd3#latest -Not yet supported. +scwsm:fr-par/3f34b83f-47a6-4344-bcd4-b63721481cd3 +scwsm:fr-par/3f34b83f-47a6-4344-bcd4-b63721481cd3#123 +scwsm:fr-par/3f34b83f-47a6-4344-bcd4-b63721481cd3#latest +``` -You mat want to have a look at [bank-vaults](https://github.com/banzaicloud/bank-vaults) -in the mean time. +Murmur uses the environment's default credentials to authenticate to Scaleway. +You can configure Murmur the same way you can [configure the `scw` CLI](https://github.com/scaleway/scaleway-cli/blob/master/docs/commands/config.md). -### Passthrough +### `awssm` provider: AWS Secrets Manager -The `passthrough:` prefix is special: it does not fetch secrets from anywhere. -Murmur uses the secret's reference as its value. In effect, this simply removes -the `passthrough:` prefix from any environment variables. +To fetch a secret from [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/), +the query must be structured as follows: -## Filters +```plaintext +awssm:{name|arn}[#{version_id|version_stage}] +``` -Murmur supports transforming secrets with the following filters. +One of `name` or `arn` must be specified. You can use a full or partial ARN. +However, if your secret's name ends with a hyphen followed by six characters, +you should not use a partial ARN. See [these AWS docs](https://docs.aws.amazon.com/secretsmanager/latest/userguide/troubleshoot.html#ARN_secretnamehyphen) +for more information. -### JSONPath +You can optionally specify one of `version_id` or `version_stage`. Murmur +guesses whether the string is an ID or a stage depending on whether it is a +valid UUID. UUIDs are treated as version IDs, and other strings are treated as +version stages. If neither `version_id` or `version_stage` are specified, Murmur +defaults to "AWSCURRENT". -Murmur embeds the [Kubernetes JSONPath](https://kubernetes.io/docs/reference/kubectl/jsonpath/) -library. You can use it to extract specific fields from a JSON-encoded secret. -For example, if you have a secret with a value of `{"sauce": "szechuan"}`, the -`jsonpath` filter can extract the `sauce` field's value: +Examples: ```plaintext -awssm:secret-sauce|jsonpath:{.sauce} +awssm:my-secret +awssm:my-secret#MY_VERSION_STAGE +awssm:my-secret#9517cc59-646a-4393-81d7-5e6f2d43cbe7 + +awssm:arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret +awssm:arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret#MY_VERSION_STAGE +awssm:arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret#9517cc59-646a-4393-81d7-5e6f2d43cbe7 ``` -## Troubleshooting +Murmur uses the environment's default credentials to authenticate to AWS. +You can configure Murmur the same way you can [configure the `aws` CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html). -### `Error: unknown flag` +### `azkv` provider: Azure Key Vault -Your application may use flags, like this: +To fetch a secret from [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/), +the query must be structured as follows: -```bash -murmur run /bin/run-my-app --port=3000 +```plaintext +azkv:keyvault_hostname/name[#version] +``` + +The `keyvault_hostname` must be the fully qualified domain name of the Key +Vault. For example, if your Key Vault's URL is `https://example.vault.azure.net/`, +then the `keyvault_hostname` is `example.vault.azure.net`. + +The `name` is the name of the secret. + +The `version` must be a valid version ID. If `version` is not specified, Murmur +defaults to the latest version of the secret. + +Examples: + +```plaintext +azkv:example.vault.azure.net/my-secret +azkv:example.vault.azure.net/my-secret#5ddc29704c1c4429a4c53605b7949100 ``` -Murmur then picks up the `--port` flag and returns an error: +Murmur uses the environment's default credentials to authenticate to Azure. You +can set these credentials with the [environment variables listed here](https://github.com/Azure/azure-sdk-for-go/wiki/Set-up-Your-Environment-for-Authentication#configure-defaultazurecredential), +or with workload identity. + +### `gcpsm` provider: GCP Secret Manager + +To fetch a secret from [GCP Secret Manager](https://cloud.google.com/secret-manager), +the query must be structured as follows: ```plaintext -Error: unknown flag: --port +gcpsm:project/name[#version] ``` -Murmur ignores any flags that come after a special `--` argument. So simply run -this command instead: +The `project` must be either a project ID or a project number. -```bash -murmur run -- /bin/run-my-app --port=3000 +The `name` is the name of the secret. + +The `version` must be a valid version number. If `version` is not specified, +Murmur defaults to the latest version of the secret. + +### `passthrough` provider: no-op + +This provider is meant for demo and testing purposes. It does not fetch any +secrets and simply returns the secret reference as the secret's value. + +This provider, like all other providers, is fully tested. It is safe to use in +production, although why would you? + +Examples: + +```plaintext +passthrough:my-not-so-secret-value ``` -Any flags after the `--` argument will still be passed to your application. +### `jsonpath` filter: JSON parsing and templating + +To parse a JSON secret and extract a value from it, or to use a secret value in +a template, the query must be stuctured as follows: + +```plaintext +provider_id:secret_ref|jsonpath:template +``` + +The `provider_id` and `secret_ref` can be any valid secret reference. + +The `template` is a [JSONPath template](https://kubernetes.io/docs/reference/kubectl/jsonpath/). +Murmur uses the Kubernetes JSONPath implementation, so you can use any feature +described in the Kubernetes docs. + +If the secret's value is not valid JSON, Murmur will treat it as a string and +execute the template anyway. This means that you can use JSONPath templates with +non-JSON secrets. + +Examples: + +```plaintext +scwsm:my-secret|jsonpath:{.password} +scwsm:my-secret|jsonpath:{.username}:{.password}@{.hostname}:{.port}/{.database} +scwsm:my-secret|jsonpath:the secret is {@} +``` + +## Changes from v0.4 to v0.5 + +Following community feedback, we have made two significant changes in v0.5: + +1. We have renamed the project from "Whisper" to "Murmur", to make the project + documentation easier to find on search engines. +2. We have renamed the `exec` command to `run`, to make it clear that we are not + executing the command directly, but rather running it as a subprocess. + +We have made it so that none of these changes are breaking. You can upgrade to +v0.5 without changing anything in how you use Whisper/Murmur. + +We now publish binaries and container images with both names. The `exec` command +is still available, but it will log a warning message telling you to use the new +`run` command instead. + +We recommend that you update your scripts to use the new name and command, but +you have all the time you need to do so. diff --git a/examples/dockerfile/Dockerfile b/examples/dockerfile/Dockerfile deleted file mode 100644 index a75330ed..00000000 --- a/examples/dockerfile/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM alpine - -COPY --from=ghcr.io/busser/murmur:latest /murmur /bin/murmur - -ENTRYPOINT ["/bin/murmur", "run", "--", "sh", "-c", "echo The secret sauce is $SECRET_SAUCE."] diff --git a/examples/dockerfile/README.md b/examples/dockerfile/README.md deleted file mode 100644 index acbd62e0..00000000 --- a/examples/dockerfile/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Example: Dockerfile - -This directory contains an example of using murmur inside a Dockerfile. - -To run the example: - -```bash -docker build -t murmur-example . -docker run \ - -e SECRET_SAUCE=passthrough:szechuan \ - murmur-example -``` diff --git a/internal/cmd/murmur.go b/internal/cmd/murmur.go index c9f05037..d23cc14d 100644 --- a/internal/cmd/murmur.go +++ b/internal/cmd/murmur.go @@ -22,6 +22,7 @@ func rootCmd() *cobra.Command { } cmd.AddCommand(runCmd()) + cmd.AddCommand(execCmd()) // Deprecated return cmd } diff --git a/internal/cmd/murmur_exec.go b/internal/cmd/murmur_exec.go new file mode 100644 index 00000000..b0876bd6 --- /dev/null +++ b/internal/cmd/murmur_exec.go @@ -0,0 +1,20 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +func execCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "exec -- command [args...]", + Args: cobra.MinimumNArgs(1), + + DisableFlagsInUseLine: true, + + Deprecated: "command \"run\" has the same behavior.", + + RunE: runCmd().RunE, + } + + return cmd +} diff --git a/internal/cmd/murmur_run.go b/internal/cmd/murmur_run.go index cb5c6df8..4e4a206e 100644 --- a/internal/cmd/murmur_run.go +++ b/internal/cmd/murmur_run.go @@ -9,19 +9,21 @@ import ( func runCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "run", - Short: "Run a command with secrets injected", - Long: `Run any command with updated environment variables. Any variables containing -a reference to an externally-stored secret will be overwritten with the secret's -value. + Use: "run -- command [args...]", + Args: cobra.MinimumNArgs(1), -Examples: + DisableFlagsInUseLine: true, - # Azure Key Vault - export SECRET_SAUCE="azkv:example.vault.azure.net/secret-sauce" - murmur run -- sh -c 'echo The secret sauce is $SECRET_SAUCE.'`, + Short: "Run a command with secrets injected into its environment variables", + + Example: ` # Fetch a database password from Scaleway Secret Manager: + export PGPASSWORD="scwsm:database-password" + murmur run -- psql -h 10.1.12.34 -U my-user -d my-database + + # Build a connection string from a JSON secret: + export PGDATABASE="scwsm:database-credentials|jsonpath:{.username}:{password}@{.host}:{.port}/{.database}" + murmur run -- psql`, - Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { exitCode, err := murmur.Run(args[0], args[1:]...) if err != nil { diff --git a/internal/murmur/filters/jsonpath/filter.go b/internal/murmur/filters/jsonpath/filter.go index ba58b266..3259bc23 100644 --- a/internal/murmur/filters/jsonpath/filter.go +++ b/internal/murmur/filters/jsonpath/filter.go @@ -3,7 +3,6 @@ package jsonpath import ( "bytes" "encoding/json" - "errors" "fmt" "k8s.io/client-go/util/jsonpath" @@ -22,7 +21,8 @@ func Filter(value, template string) (string, error) { var parsedValue any if err := json.Unmarshal([]byte(value), &parsedValue); err != nil { - return "", errors.New("value is not valid JSON") + // If the value is not valid JSON, we can still use it as a string. + parsedValue = value } var buf bytes.Buffer diff --git a/internal/murmur/filters/jsonpath/filter_test.go b/internal/murmur/filters/jsonpath/filter_test.go index 941afd08..0f21fdeb 100644 --- a/internal/murmur/filters/jsonpath/filter_test.go +++ b/internal/murmur/filters/jsonpath/filter_test.go @@ -30,6 +30,18 @@ func TestFilter(t *testing.T) { template: "foobar={ .foo.bar }", want: "foobar=baz", }, + { + name: "integer value", + value: `{"port": 5432}`, + template: "port={ .port }", + want: "port=5432", + }, + { + name: "not json", + value: "hello", + template: "the value is { @ }", + want: "the value is hello", + }, { name: "missing value", value: `{"foo": "bar"}`, @@ -42,12 +54,6 @@ func TestFilter(t *testing.T) { template: "", want: "", }, - { - name: "invalid json", - value: "foo", - template: "", - wantErr: true, - }, { name: "invalid template", value: `{"foo": "bar"}`,