Skip to content

Commit cb33c8d

Browse files
authored
Per target auth secrets (#51)
* started work on per-target auth secrets for #24 Adds an `A` configuration primitive that declares a set of options for getting Git credentials for targets. * implement per-target authentication
1 parent c97044b commit cb33c8d

File tree

6 files changed

+64
-8
lines changed

6 files changed

+64
-8
lines changed

config/config.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,17 @@ import (
2121

2222
// State represents a desired system state
2323
type State struct {
24-
Targets task.Targets `json:"targets"`
25-
Env map[string]string `json:"env"`
24+
Targets task.Targets `json:"targets"`
25+
AuthMethods []AuthMethod `json:"auths"`
26+
Env map[string]string `json:"env"`
27+
}
28+
29+
// AuthMethod represents a method of authentication for a target
30+
type AuthMethod struct {
31+
Name string `json:"name"` // name of the auth method
32+
Path string `json:"path"` // path within the secret store
33+
UserKey string `json:"user_key"` // key for username
34+
PassKey string `json:"pass_key"` // key for password
2635
}
2736

2837
// ConfigFromDirectory searches a directory for configuration files and
@@ -72,6 +81,7 @@ func (cb *configBuilder) construct(hostname string) (err error) {
7281
cb.vm.Run(`'use strict';
7382
var STATE = {
7483
targets: [],
84+
auths: [],
7585
env: {}
7686
};
7787
@@ -90,6 +100,15 @@ function T(t) {
90100
function E(k, v) {
91101
STATE.env[k] = v
92102
}
103+
104+
function A(a) {
105+
if(a.name === undefined) { throw "auth name undefined"; }
106+
if(a.path === undefined) { throw "auth path undefined"; }
107+
if(a.user_key === undefined) { throw "auth user_key undefined"; }
108+
if(a.pass_key === undefined) { throw "auth pass_key undefined"; }
109+
110+
STATE.auths.push(a);
111+
}
93112
`)
94113

95114
cb.vm.Set("HOSTNAME", hostname) //nolint:errcheck

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/picostack/pico
33
go 1.13
44

55
require (
6-
github.com/Southclaws/gitwatch v1.3.2
6+
github.com/Southclaws/gitwatch v1.3.3
77
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
88
github.com/eapache/go-resiliency v1.2.0
99
github.com/frankban/quicktest v1.4.1 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ github.com/Southclaws/gitwatch v1.3.1 h1:4XtiujsnxHKSKze3Tb5sWwTdBxSVW/JLbK54ruJ
99
github.com/Southclaws/gitwatch v1.3.1/go.mod h1:xCudUiwWxkDYZ69cEhlTwAKIzbG1OpnA/s/pjPIW6gU=
1010
github.com/Southclaws/gitwatch v1.3.2 h1:zmt571n8ItXgkRJPyCFtFjcymvsFOGcm7JnHNpFDP+8=
1111
github.com/Southclaws/gitwatch v1.3.2/go.mod h1:xCudUiwWxkDYZ69cEhlTwAKIzbG1OpnA/s/pjPIW6gU=
12+
github.com/Southclaws/gitwatch v1.3.3 h1:w5AI9IcMEVqb6cPyDjM9tvOI4r26m4UHAl5BVEvgKac=
13+
github.com/Southclaws/gitwatch v1.3.3/go.mod h1:xCudUiwWxkDYZ69cEhlTwAKIzbG1OpnA/s/pjPIW6gU=
1214
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
1315
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
1416
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=

service/service.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func Initialise(c Config) (app *App, err error) {
102102
app.config.Directory,
103103
app.bus,
104104
app.config.CheckInterval,
105-
authMethod,
105+
secretStore,
106106
)
107107

108108
return

task/target.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ type Target struct {
4848

4949
// Whether or not to run `Command` on first run, useful if the command is `docker-compose up`
5050
InitialRun bool `json:"initial_run"`
51+
52+
// Auth method to use from the auth store
53+
Auth string `json:"auth"`
5154
}
5255

5356
// Execute runs the target's command in the specified directory with the

watcher/git.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import (
1111
"github.com/pkg/errors"
1212
"go.uber.org/zap"
1313
"gopkg.in/src-d/go-git.v4/plumbing/transport"
14+
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
1415

1516
"github.com/picostack/pico/config"
17+
"github.com/picostack/pico/secret"
1618
"github.com/picostack/pico/task"
1719
)
1820

@@ -24,7 +26,7 @@ type GitWatcher struct {
2426
directory string
2527
bus chan task.ExecutionTask
2628
checkInterval time.Duration
27-
ssh transport.AuthMethod
29+
secrets secret.Store
2830

2931
targetsWatcher *gitwatch.Session
3032
state config.State
@@ -42,13 +44,13 @@ func NewGitWatcher(
4244
directory string,
4345
bus chan task.ExecutionTask,
4446
checkInterval time.Duration,
45-
ssh transport.AuthMethod,
47+
secrets secret.Store,
4648
) *GitWatcher {
4749
return &GitWatcher{
4850
directory: directory,
4951
bus: bus,
5052
checkInterval: checkInterval,
51-
ssh: ssh,
53+
secrets: secrets,
5254

5355
initialise: make(chan bool),
5456
newState: make(chan config.State, 16),
@@ -161,11 +163,16 @@ func (w *GitWatcher) watchTargets() (err error) {
161163
if t.Branch != "" {
162164
dir = fmt.Sprintf("%s_%s", t.Name, t.Branch)
163165
}
166+
auth, err := w.getAuthForTarget(t)
167+
if err != nil {
168+
return err
169+
}
164170
zap.L().Debug("assigned target", zap.String("url", t.RepoURL), zap.String("directory", dir))
165171
targetRepos[i] = gitwatch.Repository{
166172
URL: t.RepoURL,
167173
Branch: t.Branch,
168174
Directory: dir,
175+
Auth: auth,
169176
}
170177
}
171178

@@ -177,7 +184,7 @@ func (w *GitWatcher) watchTargets() (err error) {
177184
targetRepos,
178185
w.checkInterval,
179186
w.directory,
180-
w.ssh,
187+
nil,
181188
false)
182189
if err != nil {
183190
return errors.Wrap(err, "failed to watch targets")
@@ -211,6 +218,31 @@ func (w *GitWatcher) handle(e gitwatch.Event) (err error) {
211218
return nil
212219
}
213220

221+
func (w GitWatcher) getAuthForTarget(t task.Target) (transport.AuthMethod, error) {
222+
for _, a := range w.state.AuthMethods {
223+
if a.Name == t.Auth {
224+
s, err := w.secrets.GetSecretsForTarget(a.Path)
225+
if err != nil {
226+
return nil, err
227+
}
228+
username, ok := s[a.UserKey]
229+
if !ok {
230+
return nil, errors.Errorf("auth object 'user_key' did not point to a valid element in the specified secret at '%s'", a.Path)
231+
}
232+
password, ok := s[a.PassKey]
233+
if !ok {
234+
return nil, errors.Errorf("auth object 'pass_key' did not point to a valid element in the specified secret at '%s'", a.Path)
235+
}
236+
zap.L().Debug("using auth method for target", zap.String("name", a.Name))
237+
return &http.BasicAuth{
238+
Username: username,
239+
Password: password,
240+
}, nil
241+
}
242+
}
243+
return nil, nil
244+
}
245+
214246
func (w GitWatcher) executeTargets(targets []task.Target, shutdown bool) {
215247
zap.L().Debug("executing all targets",
216248
zap.Bool("shutdown", shutdown),

0 commit comments

Comments
 (0)