aboutsummaryrefslogtreecommitdiff
path: root/secrets/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'secrets/client.go')
-rw-r--r--secrets/client.go112
1 files changed, 112 insertions, 0 deletions
diff --git a/secrets/client.go b/secrets/client.go
new file mode 100644
index 0000000..7a1f51b
--- /dev/null
+++ b/secrets/client.go
@@ -0,0 +1,112 @@
1package secrets
2
3import (
4 "context"
5 "crypto/rsa"
6 "crypto/x509"
7 "encoding/base64"
8 "fmt"
9 "sync"
10 "time"
11
12 "code.crute.us/mcrute/golib/log"
13 "code.crute.us/mcrute/golib/service"
14)
15
16type Handle interface {
17 Reference() string
18}
19
20type Renewal struct {
21 Name string
22 Critical bool
23 Time time.Time
24 Error error
25}
26
27type Credential struct {
28 Username string `json:"username"`
29 Password string `json:"password"`
30}
31
32type ApiKey struct {
33 Key string `json:"key"`
34}
35
36type RSAKey struct {
37 Key string `json:"key"`
38}
39
40func (k *RSAKey) RSAPrivateKey() (*rsa.PrivateKey, error) {
41 der, err := base64.StdEncoding.DecodeString(k.Key)
42 if err != nil {
43 return nil, err
44 }
45
46 pr, err := x509.ParsePKCS8PrivateKey(der)
47 if err != nil {
48 return nil, err
49 }
50
51 pk, ok := pr.(*rsa.PrivateKey)
52 if !ok {
53 return nil, fmt.Errorf("RSAKey: parsed key is not an rsa.PrivateKey")
54 }
55
56 return pk, nil
57}
58
59// Client is the interface that users of secrets returned by a secret
60// back-end should expect. This interface contains only secret related
61// functionality and none of the functions for running the back-end
62// itself. This is separate from the manager functions to make it easier
63// to inject stubs to code that doesn't care about the fact that a
64// manager may exist.
65type Client interface {
66 DatabaseCredential(context.Context, string) (*Credential, Handle, error)
67 Secret(context.Context, string, any) (Handle, error)
68 WriteSecret(context.Context, string, any) error
69 Destroy(Handle) error
70 MakeNonCritical(Handle) error
71}
72
73// ClientManager is like a Client, and contains a Client, but also
74// contains other runtime functionality for running the secret back-end
75// infrastructure that most consumers of secretes don't care about but
76// the main process runner does.
77type ClientManager interface {
78 Client
79 Authenticate(context.Context) error
80 Notifications() <-chan Renewal
81 Run(context.Context, *sync.WaitGroup) error
82}
83
84// MakeRenewalLogger subscribes to a ClientManager notification channel
85// and logs those to the logger. If a critical credential fails the
86// terminator callback will be called which should shut down the
87// application in an orderly fashion.
88func MakeRenewalLogger(cm ClientManager, log log.LeveledLogger, terminator func()) service.RunnerFunc {
89 return func(ctx context.Context, wg *sync.WaitGroup) error {
90 wg.Add(1)
91 defer wg.Done()
92
93 for {
94 select {
95 case r := <-cm.Notifications():
96 if r.Error != nil {
97 if r.Critical {
98 log.Errorf("Failed to renew critical secret %s due to %s", r.Name, r.Error)
99 terminator()
100 } else {
101 log.Errorf("Failed to renew non-critical secret %s", r.Name)
102 }
103 } else {
104 log.Infof("Renewing credential %s", r.Name)
105 }
106 case <-ctx.Done():
107 log.Infof("Shutting down secret renewal logger")
108 return nil
109 }
110 }
111 }
112}