Skip to content

Commit 5980499

Browse files
Support for @SUMMONVARNAMES
1 parent 78b0ab9 commit 5980499

File tree

9 files changed

+291
-24
lines changed

9 files changed

+291
-24
lines changed

Dockerfile

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.15
1+
FROM golang:1.15-alpine
22

33
WORKDIR /summon
44

@@ -7,9 +7,11 @@ ENV GOARCH=amd64
77

88
COPY go.mod go.sum ./
99

10-
RUN apt update -y && \
11-
apt install -y bash \
12-
git && \
10+
RUN apk add --no-cache bash \
11+
build-base \
12+
docker-cli \
13+
git && \
14+
go mod download && \
1315
go mod download && \
1416
go get -u github.com/jstemmer/go-junit-report && \
1517
go get -u github.com/axw/gocov/gocov && \

Dockerfile.acceptance

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ FROM golang:1.15-alpine
22

33
RUN apk add --no-cache bash \
44
build-base \
5+
docker-cli \
56
git \
67
libffi-dev \
78
ruby-bundler \

docs/_includes/docker.md

+67-5
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,80 @@ Since Summon has pluggable providers, you aren't locked into any one solution fo
1616
managing your secrets.
1717

1818
Summon makes it easy to inject secrets as environment variables into your Docker
19-
containers by taking advantage of Docker's `--env-file` argument. This is done
20-
on-demand by using the variable `@SUMMONENVFILE` in the arguments of the process
21-
you are running with Summon. This variable points to a memory-mapped file containing
22-
the variables and values from secrets.yml in VAR=VAL format.
19+
containers by taking advantage of Docker's CLI arguments
20+
(`--env-file` or `--env`). There are two options available.It's possible to mix
21+
and match as you see fit.
22+
23+
## Docker --env arguments
24+
25+
This is done on-demand by using the value `@SUMMONVARNAMES` in the subprocess
26+
arguments passed to Summon. This value is replaced by a space separated list of
27+
the environment variables **names** injected by Summon. With the help of `xargs`
28+
and `printf` we can easily generate the `--env` arguments necessary for the
29+
Docker container to pickup the secrets injected by summon.
30+
31+
```bash
32+
$ summon -p keyring.py -D env=dev env SUMMONVARNAMES=@SUMMONVARNAMES sh << EOL
33+
docker run \$(printenv SUMMONVARNAMES | xargs printf -- '--env %s ' | xargs) deployer
34+
EOL
35+
Checking credentials
36+
Deploying application
37+
```
38+
39+
### Docker --env arguments example
40+
41+
Below we provide a complete example of using @SUMMONVARNAMES to generate the
42+
Docker `--env` arguments. For the sake of brevity we use an inline `secrets.yml`
43+
and the `/bin/echo` provider. Some points to note:
44+
45+
1. `summon` is invoking the `docker` CLI as a child process.
46+
2. `@SUMMONVARNAMES` is transformed into to generate instances of the Docker
47+
argument `--env`.
48+
49+
```bash
50+
secretsyml='
51+
A: |-
52+
A_value with
53+
multiple lines
54+
B: B_value
55+
C: !var C_value
56+
'
57+
58+
# The substitution and transformation of @SUMMONVARNAMES in the `docker run`
59+
# command below results in something of the form:
60+
#
61+
# docker run --rm \
62+
# --env A --env B --env C \
63+
# alpine ...
64+
#
65+
# The output from the command is shown below the command.
66+
67+
summon --provider /bin/echo --yaml "${secretsyml}" env SUMMONVARNAMES=@SUMMONVARNAMES sh << EOL
68+
docker run --rm \$(printenv SUMMONVARNAMES | xargs printf -- '--env %s ' | xargs) alpine sh -c '
69+
printenv A;
70+
printenv B;
71+
printenv C;
72+
'
73+
EOL
74+
75+
# A_value with
76+
# multiple lines
77+
# B_value
78+
# C_value
79+
```
80+
81+
## Docker --env-file argument
82+
This is done on-demand by using the variable `@SUMMONENVFILE` in the arguments of
83+
the process you are running with Summon. This variable points to a memory-mapped
84+
file containing the variables and values from secrets.yml in VAR=VAL format.
2385

2486
```sh
2587
$ summon -p keyring.py -D env=dev docker run --env-file @SUMMONENVFILE deployer
2688
Checking credentials
2789
Deploying application
2890
```
2991

30-
## Example
92+
### @SUMMONENVFILE Example
3193

3294
Let's say we have a deploy script that needs to access our application servers on
3395
AWS and pull the latest version of our code. It should record the outcome of the

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ module github.com/cyberark/summon
22

33
require (
44
github.com/codegangsta/cli v1.20.0
5+
github.com/docker/docker v20.10.2+incompatible
6+
github.com/docker/go-connections v0.4.0 // indirect
7+
github.com/docker/go-units v0.4.0 // indirect
58
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
69
github.com/jtolds/gls v4.20.0+incompatible // indirect
710
github.com/kr/pretty v0.1.0 // indirect

go.sum

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ github.com/codegangsta/cli v1.20.0 h1:iX1FXEgwzd5+XN6wk5cVHOGQj6Q3Dcp20lUeS4lHNT
22
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
33
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
44
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
6+
github.com/docker/docker v20.10.2+incompatible h1:vFgEHPqWBTp4pTjdLwjAA4bSo3gvIGOYwuJTlEjVBCw=
7+
github.com/docker/docker v20.10.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
8+
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
9+
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
10+
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
11+
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
512
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
613
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
714
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=

internal/command/action.go

+42-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package command
33
import (
44
"bytes"
55
"fmt"
6+
"io"
67
"os"
78
"os/exec"
89
"path/filepath"
@@ -12,16 +13,21 @@ import (
1213
"syscall"
1314

1415
"github.com/codegangsta/cli"
16+
1517
prov "github.com/cyberark/summon/provider"
1618
"github.com/cyberark/summon/secretsyml"
1719
)
1820

1921
// ActionConfig is an object that holds all the info needed to run
2022
// a Summon instance
2123
type ActionConfig struct {
24+
StdIn io.Reader
25+
StdOut io.Writer
26+
StdErr io.Writer
2227
Args []string
2328
Provider string
2429
Filepath string
30+
TmpPath string
2531
YamlInline string
2632
Subs map[string]string
2733
Ignores []string
@@ -31,8 +37,9 @@ type ActionConfig struct {
3137
ShowProviderVersions bool
3238
}
3339

34-
const ENV_FILE_MAGIC = "@SUMMONENVFILE"
35-
const SUMMON_ENV_KEY_NAME = "SUMMON_ENV"
40+
const EnvFileMagic = "@SUMMONENVFILE"
41+
const VarNamesMagic = "@SUMMONVARNAMES"
42+
const SummonEnvKeyName = "SUMMON_ENV"
3643

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

112119
env := make(map[string]string)
113-
tempFactory := NewTempFactory("")
120+
tempFactory := NewTempFactory(ac.TmpPath)
114121
defer tempFactory.Cleanup()
115122

116123
type Result struct {
@@ -145,6 +152,7 @@ func runAction(ac *ActionConfig) error {
145152
}
146153

147154
k, v := formatForEnv(key, value, spec, &tempFactory)
155+
148156
results <- Result{k, v, nil}
149157
wg.Done()
150158
}(key, spec)
@@ -172,17 +180,42 @@ EnvLoop:
172180

173181
// Append environment variable if one is specified
174182
if ac.Environment != "" {
175-
env[SUMMON_ENV_KEY_NAME] = ac.Environment
183+
env[SummonEnvKeyName] = ac.Environment
176184
}
177185

178186
setupEnvFile(ac.Args, env, &tempFactory)
187+
setupVarNames(ac.Args, secrets)
179188

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

185-
return runSubcommand(ac.Args, append(os.Environ(), e...))
194+
return runSubcommand(
195+
ac.Args,
196+
append(os.Environ(), e...),
197+
ac.StdIn,
198+
ac.StdOut,
199+
ac.StdErr,
200+
)
201+
}
202+
203+
func setupVarNames(args []string, secrets secretsyml.SecretsMap) {
204+
var varNames []string
205+
for varName := range secrets {
206+
varNames = append(varNames, varName)
207+
}
208+
sort.Strings(varNames)
209+
210+
// Inject @SUMMONVARNAMES
211+
for idx, arg := range args {
212+
// Replace argument substring
213+
if strings.Contains(arg, VarNamesMagic) {
214+
// Replace substring in argument with slice of docker options
215+
args[idx] = strings.Replace(arg, VarNamesMagic, strings.Join(varNames, " "), -1)
216+
continue
217+
}
218+
}
186219
}
187220

188221
// formatForEnv returns a string in %k=%v format, where %k=namespace of the secret and
@@ -201,7 +234,7 @@ func joinEnv(env map[string]string) string {
201234
for k, v := range env {
202235
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
203236
}
204-
237+
205238
// Sort to ensure predictable results
206239
sort.Strings(envs)
207240

@@ -245,20 +278,20 @@ func findInParentTree(secretsFile string, leafDir string) (string, error) {
245278
}
246279
}
247280

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

255288
for i, arg := range args {
256-
idx := strings.Index(arg, ENV_FILE_MAGIC)
289+
idx := strings.Index(arg, EnvFileMagic)
257290
if idx >= 0 {
258291
if envFile == "" {
259292
envFile = tempFactory.Push(joinEnv(env))
260293
}
261-
args[i] = strings.Replace(arg, ENV_FILE_MAGIC, envFile, -1)
294+
args[i] = strings.Replace(arg, EnvFileMagic, envFile, -1)
262295
}
263296
}
264297

0 commit comments

Comments
 (0)