forked from hashicorp/vault-service-broker
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
240 lines (197 loc) · 5.44 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
package main
import (
"log"
"net/http"
"net/url"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"code.cloudfoundry.org/lager"
"github.com/hashicorp/vault/api"
"github.com/pivotal-cf/brokerapi"
)
const (
// DefaultListenAddr is the default address unless we get
// an override via PORT
DefaultListenAddr = ":8000"
// DefaultServiceID is the default UUID of the services
DefaultServiceID = "0654695e-0760-a1d4-1cad-5dd87b75ed99"
// DefaultVaultAddr is the default address to the Vault cluster.
DefaultVaultAddr = "https://127.0.0.1:8200"
// DefaultServiceName is the name of the service in the marketplace
DefaultServiceName = "hashicorp-vault"
// DefaultServiceDescription is the default service description.
DefaultServiceDescription = "HashiCorp Vault Service Broker"
// DefaultPlanName is the name of our plan, only one supported
DefaultPlanName = "shared"
// DefaultPlanDescription is the default description.
DefaultPlanDescription = "Secure access to Vault's storage and transit backends"
//DefaultShareable is the default configuration for the service metadata.
DefaultShareable = true
)
func main() {
// Setup the logger - intentionally do not log date or time because it will
// be prefixed in the log output by CF.
log := log.New(os.Stdout, "", 0)
// Ensure username and password are present
username := os.Getenv("SECURITY_USER_NAME")
if username == "" {
log.Fatal("[ERR] missing SECURITY_USER_NAME")
}
password := os.Getenv("SECURITY_USER_PASSWORD")
if password == "" {
log.Fatal("[ERR] missing SECURITY_USER_PASSWORD")
}
// Get a custom Shareable
shareable, parseErr := strconv.ParseBool(os.Getenv("SHAREABLE"))
if parseErr != nil {
shareable = DefaultShareable
}
// Get a custom GUID
serviceID := os.Getenv("SERVICE_ID")
if serviceID == "" {
serviceID = DefaultServiceID
}
// Get the service name
serviceName := os.Getenv("SERVICE_NAME")
if serviceName == "" {
serviceName = DefaultServiceName
}
// Get the service description
serviceDescription := os.Getenv("SERVICE_DESCRIPTION")
if serviceDescription == "" {
serviceDescription = DefaultServiceDescription
}
// Get the service tags
serviceTags := strings.Split(os.Getenv("SERVICE_TAGS"), ",")
// Get the plan name
planName := os.Getenv("PLAN_NAME")
if planName == "" {
planName = DefaultPlanName
}
// Get the plan description
planDescription := os.Getenv("PLAN_DESCRIPTION")
if planDescription == "" {
planDescription = DefaultPlanDescription
}
// Parse the port
port := os.Getenv("PORT")
if port == "" {
port = DefaultListenAddr
} else {
if port[0] != ':' {
port = ":" + port
}
}
// Check for vault address
vaultAddr := os.Getenv("VAULT_ADDR")
if vaultAddr == "" {
vaultAddr = "https://127.0.0.1:8200"
}
os.Setenv("VAULT_ADDR", normalizeAddr(vaultAddr))
// Get the vault advertise addr
vaultAdvertiseAddr := os.Getenv("VAULT_ADVERTISE_ADDR")
if vaultAdvertiseAddr == "" {
vaultAdvertiseAddr = normalizeAddr(vaultAddr)
}
// Check if renewal is enabled
renew := true
if s := os.Getenv("VAULT_RENEW"); s != "" {
b, err := strconv.ParseBool(s)
if err != nil {
log.Fatalf("[ERR] failed to parse VAULT_RENEW: %s", err)
}
renew = b
}
// Check for vault token
if v := os.Getenv("VAULT_TOKEN"); v == "" {
log.Fatal("[ERR] missing VAULT_TOKEN")
}
// Setup the vault client
client, err := api.NewClient(nil)
if err != nil {
log.Fatal("[ERR] failed to create api client", err)
}
// Setup the broker
broker := &Broker{
log: log,
client: client,
serviceID: serviceID,
serviceName: serviceName,
serviceDescription: serviceDescription,
serviceTags: serviceTags,
planName: planName,
planDescription: planDescription,
vaultAdvertiseAddr: vaultAdvertiseAddr,
vaultRenewToken: renew,
Metadata: &brokerapi.ServiceMetadata{Shareable: &shareable},
}
if err := broker.Start(); err != nil {
log.Fatalf("[ERR] failed to start broker: %s", err)
}
// Parse the broker credentials
creds := brokerapi.BrokerCredentials{
Username: username,
Password: password,
}
// Setup the HTTP handler
handler := brokerapi.New(broker, lager.NewLogger("vault-broker"), creds)
// Listen to incoming connection
serverCh := make(chan struct{}, 1)
go func() {
log.Printf("[INFO] starting server on %s", port)
if err := http.ListenAndServe(port, handler); err != nil {
log.Fatalf("[ERR] server exited with: %s", err)
}
close(serverCh)
}()
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, syscall.SIGTERM, syscall.SIGINT)
select {
case <-serverCh:
case s := <-signalCh:
log.Printf("[INFO] received signal %s", s)
}
if err := broker.Stop(); err != nil {
log.Fatalf("[ERR] faild to stop broker: %s", err)
}
os.Exit(0)
}
// normalizeAddr takes a string that represents a URL and ensures it has a
// scheme (defaulting to https), and ensures the path ends in a trailing slash.
func normalizeAddr(s string) string {
if s == "" {
return s
}
u, err := url.Parse(s)
if err != nil {
return s
}
if u.Scheme == "" {
u.Scheme = "https"
}
if strings.Contains(u.Scheme, ".") {
u.Host = u.Scheme
if u.Opaque != "" {
u.Host = u.Host + ":" + u.Opaque
u.Opaque = ""
}
u.Scheme = "https"
}
if u.Host == "" {
split := strings.SplitN(u.Path, "/", 2)
switch len(split) {
case 0:
case 1:
u.Host = split[0]
u.Path = "/"
case 2:
u.Host = split[0]
u.Path = split[1]
}
}
u.Path = strings.TrimRight(u.Path, "/") + "/"
return u.String()
}