Skip to content

Commit

Permalink
Support for @SUMMONVARNAMES
Browse files Browse the repository at this point in the history
  • Loading branch information
doodlesbykumbi committed Feb 2, 2021
1 parent 78b0ab9 commit 5980499
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 24 deletions.
10 changes: 6 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.15
FROM golang:1.15-alpine

WORKDIR /summon

Expand All @@ -7,9 +7,11 @@ ENV GOARCH=amd64

COPY go.mod go.sum ./

RUN apt update -y && \
apt install -y bash \
git && \
RUN apk add --no-cache bash \
build-base \
docker-cli \
git && \
go mod download && \
go mod download && \
go get -u github.com/jstemmer/go-junit-report && \
go get -u github.com/axw/gocov/gocov && \
Expand Down
1 change: 1 addition & 0 deletions Dockerfile.acceptance
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ FROM golang:1.15-alpine

RUN apk add --no-cache bash \
build-base \
docker-cli \
git \
libffi-dev \
ruby-bundler \
Expand Down
72 changes: 67 additions & 5 deletions docs/_includes/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,80 @@ Since Summon has pluggable providers, you aren't locked into any one solution fo
managing your secrets.

Summon makes it easy to inject secrets as environment variables into your Docker
containers by taking advantage of Docker's `--env-file` argument. This is done
on-demand by using the variable `@SUMMONENVFILE` in the arguments of the process
you are running with Summon. This variable points to a memory-mapped file containing
the variables and values from secrets.yml in VAR=VAL format.
containers by taking advantage of Docker's CLI arguments
(`--env-file` or `--env`). There are two options available.It's possible to mix
and match as you see fit.

## Docker --env arguments

This is done on-demand by using the value `@SUMMONVARNAMES` in the subprocess
arguments passed to Summon. This value is replaced by a space separated list of
the environment variables **names** injected by Summon. With the help of `xargs`
and `printf` we can easily generate the `--env` arguments necessary for the
Docker container to pickup the secrets injected by summon.

```bash
$ summon -p keyring.py -D env=dev env SUMMONVARNAMES=@SUMMONVARNAMES sh << EOL
docker run \$(printenv SUMMONVARNAMES | xargs printf -- '--env %s ' | xargs) deployer
EOL
Checking credentials
Deploying application
```

### Docker --env arguments example

Below we provide a complete example of using @SUMMONVARNAMES to generate the
Docker `--env` arguments. For the sake of brevity we use an inline `secrets.yml`
and the `/bin/echo` provider. Some points to note:

1. `summon` is invoking the `docker` CLI as a child process.
2. `@SUMMONVARNAMES` is transformed into to generate instances of the Docker
argument `--env`.

```bash
secretsyml='
A: |-
A_value with
multiple lines
B: B_value
C: !var C_value
'

# The substitution and transformation of @SUMMONVARNAMES in the `docker run`
# command below results in something of the form:
#
# docker run --rm \
# --env A --env B --env C \
# alpine ...
#
# The output from the command is shown below the command.

summon --provider /bin/echo --yaml "${secretsyml}" env SUMMONVARNAMES=@SUMMONVARNAMES sh << EOL
docker run --rm \$(printenv SUMMONVARNAMES | xargs printf -- '--env %s ' | xargs) alpine sh -c '
printenv A;
printenv B;
printenv C;
'
EOL

# A_value with
# multiple lines
# B_value
# C_value
```

## Docker --env-file argument
This is done on-demand by using the variable `@SUMMONENVFILE` in the arguments of
the process you are running with Summon. This variable points to a memory-mapped
file containing the variables and values from secrets.yml in VAR=VAL format.

```sh
$ summon -p keyring.py -D env=dev docker run --env-file @SUMMONENVFILE deployer
Checking credentials
Deploying application
```

## Example
### @SUMMONENVFILE Example

Let's say we have a deploy script that needs to access our application servers on
AWS and pull the latest version of our code. It should record the outcome of the
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ module github.com/cyberark/summon

require (
github.com/codegangsta/cli v1.20.0
github.com/docker/docker v20.10.2+incompatible
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/kr/pretty v0.1.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ github.com/codegangsta/cli v1.20.0 h1:iX1FXEgwzd5+XN6wk5cVHOGQj6Q3Dcp20lUeS4lHNT
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
github.com/docker/docker v20.10.2+incompatible h1:vFgEHPqWBTp4pTjdLwjAA4bSo3gvIGOYwuJTlEjVBCw=
github.com/docker/docker v20.10.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
Expand Down
51 changes: 42 additions & 9 deletions internal/command/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package command
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
Expand All @@ -12,16 +13,21 @@ import (
"syscall"

"github.com/codegangsta/cli"

prov "github.com/cyberark/summon/provider"
"github.com/cyberark/summon/secretsyml"
)

// ActionConfig is an object that holds all the info needed to run
// a Summon instance
type ActionConfig struct {
StdIn io.Reader
StdOut io.Writer
StdErr io.Writer
Args []string
Provider string
Filepath string
TmpPath string
YamlInline string
Subs map[string]string
Ignores []string
Expand All @@ -31,8 +37,9 @@ type ActionConfig struct {
ShowProviderVersions bool
}

const ENV_FILE_MAGIC = "@SUMMONENVFILE"
const SUMMON_ENV_KEY_NAME = "SUMMON_ENV"
const EnvFileMagic = "@SUMMONENVFILE"
const VarNamesMagic = "@SUMMONVARNAMES"
const SummonEnvKeyName = "SUMMON_ENV"

// Action is the runner for the main program logic
var Action = func(c *cli.Context) {
Expand Down Expand Up @@ -110,7 +117,7 @@ func runAction(ac *ActionConfig) error {
}

env := make(map[string]string)
tempFactory := NewTempFactory("")
tempFactory := NewTempFactory(ac.TmpPath)
defer tempFactory.Cleanup()

type Result struct {
Expand Down Expand Up @@ -145,6 +152,7 @@ func runAction(ac *ActionConfig) error {
}

k, v := formatForEnv(key, value, spec, &tempFactory)

results <- Result{k, v, nil}
wg.Done()
}(key, spec)
Expand Down Expand Up @@ -172,17 +180,42 @@ EnvLoop:

// Append environment variable if one is specified
if ac.Environment != "" {
env[SUMMON_ENV_KEY_NAME] = ac.Environment
env[SummonEnvKeyName] = ac.Environment
}

setupEnvFile(ac.Args, env, &tempFactory)
setupVarNames(ac.Args, secrets)

var e []string
for k, v := range env {
e = append(e, fmt.Sprintf("%s=%s", k, v))
}

return runSubcommand(ac.Args, append(os.Environ(), e...))
return runSubcommand(
ac.Args,
append(os.Environ(), e...),
ac.StdIn,
ac.StdOut,
ac.StdErr,
)
}

func setupVarNames(args []string, secrets secretsyml.SecretsMap) {
var varNames []string
for varName := range secrets {
varNames = append(varNames, varName)
}
sort.Strings(varNames)

// Inject @SUMMONVARNAMES
for idx, arg := range args {
// Replace argument substring
if strings.Contains(arg, VarNamesMagic) {
// Replace substring in argument with slice of docker options
args[idx] = strings.Replace(arg, VarNamesMagic, strings.Join(varNames, " "), -1)
continue
}
}
}

// formatForEnv returns a string in %k=%v format, where %k=namespace of the secret and
Expand All @@ -201,7 +234,7 @@ func joinEnv(env map[string]string) string {
for k, v := range env {
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}

// Sort to ensure predictable results
sort.Strings(envs)

Expand Down Expand Up @@ -245,20 +278,20 @@ func findInParentTree(secretsFile string, leafDir string) (string, error) {
}
}

// scans arguments for the magic string; if found,
// scans arguments for the envfile magic string; if found,
// creates a tempfile to which all the environment mappings are dumped
// and replaces the magic string with its path.
// Returns the path if so, returns an empty string otherwise.
func setupEnvFile(args []string, env map[string]string, tempFactory *TempFactory) string {
var envFile = ""

for i, arg := range args {
idx := strings.Index(arg, ENV_FILE_MAGIC)
idx := strings.Index(arg, EnvFileMagic)
if idx >= 0 {
if envFile == "" {
envFile = tempFactory.Push(joinEnv(env))
}
args[i] = strings.Replace(arg, ENV_FILE_MAGIC, envFile, -1)
args[i] = strings.Replace(arg, EnvFileMagic, envFile, -1)
}
}

Expand Down
Loading

0 comments on commit 5980499

Please sign in to comment.