@@ -11,11 +11,16 @@ import (
11
11
"github.com/splunk/vault-plugin-splunk/clients/splunk"
12
12
)
13
13
14
+ const (
15
+ SEARCHHEAD = "search_head"
16
+ INDEXER = "indexer"
17
+ )
18
+
14
19
func (b * backend ) pathCredsCreate () * framework.Path {
15
20
return & framework.Path {
16
21
Pattern : "creds/" + framework .GenericNameRegex ("name" ),
17
22
Fields : map [string ]* framework.FieldSchema {
18
- "name" : & framework. FieldSchema {
23
+ "name" : {
19
24
Type : framework .TypeString ,
20
25
Description : "Name of the role" ,
21
26
},
@@ -30,7 +35,30 @@ func (b *backend) pathCredsCreate() *framework.Path {
30
35
}
31
36
}
32
37
33
- func (b * backend ) credsReadHandler (ctx context.Context , req * logical.Request , d * framework.FieldData ) (* logical.Response , error ) {
38
+ func (b * backend ) pathCredsCreateMulti () * framework.Path {
39
+ return & framework.Path {
40
+ Pattern : "creds/" + framework .GenericNameRegex ("name" ) + "/" + framework .GenericNameRegex ("node_fqdn" ),
41
+ Fields : map [string ]* framework.FieldSchema {
42
+ "name" : {
43
+ Type : framework .TypeString ,
44
+ Description : "Name of the role" ,
45
+ },
46
+ "node_fqdn" : {
47
+ Type : framework .TypeString ,
48
+ Description : "FQDN for the Splunk Stack node" ,
49
+ },
50
+ },
51
+
52
+ Callbacks : map [logical.Operation ]framework.OperationFunc {
53
+ logical .ReadOperation : b .credsReadHandler ,
54
+ },
55
+
56
+ HelpSynopsis : pathCredsCreateHelpSyn ,
57
+ HelpDescription : pathCredsCreateHelpDesc ,
58
+ }
59
+ }
60
+
61
+ func (b * backend ) credsReadHandlerStandalone (ctx context.Context , req * logical.Request , d * framework.FieldData ) (* logical.Response , error ) {
34
62
name := d .Get ("name" ).(string )
35
63
role , err := roleConfigLoad (ctx , req .Storage , name )
36
64
if err != nil {
@@ -50,7 +78,7 @@ func (b *backend) credsReadHandler(ctx context.Context, req *logical.Request, d
50
78
return nil , fmt .Errorf ("%q is not an allowed role for connection %q" , name , role .Connection )
51
79
}
52
80
53
- conn , err := b .ensureConnection (ctx , role . Connection , config )
81
+ conn , err := b .ensureConnection (ctx , config )
54
82
if err != nil {
55
83
return nil , err
56
84
}
@@ -100,6 +128,129 @@ func (b *backend) credsReadHandler(ctx context.Context, req *logical.Request, d
100
128
return resp , nil
101
129
}
102
130
131
+ func findNode (nodeFQDN string , hosts []splunk.ServerInfoEntry ) (bool , error ) {
132
+ for _ , host := range hosts {
133
+ // check if node_fqdn is in either of HostFQDN or Host. User might not always the FQDN on the cli input
134
+ if host .Content .HostFQDN == nodeFQDN || host .Content .Host == nodeFQDN {
135
+ // Return true if the requested node is a search head
136
+ for _ , role := range host .Content .Roles {
137
+ if role == SEARCHHEAD {
138
+ return true , nil
139
+ }
140
+ }
141
+ return false , fmt .Errorf ("host: %s isn't search head; creating ephemeral creds is only supported for search heads" , nodeFQDN )
142
+ }
143
+ }
144
+ return false , fmt .Errorf ("host: %s not found" , nodeFQDN )
145
+ }
146
+
147
+ func (b * backend ) credsReadHandlerMulti (ctx context.Context , req * logical.Request , d * framework.FieldData ) (* logical.Response , error ) {
148
+ name := d .Get ("name" ).(string )
149
+ node , _ := d .GetOk ("node_fqdn" )
150
+ nodeFQDN := node .(string )
151
+ role , err := roleConfigLoad (ctx , req .Storage , name )
152
+ if err != nil {
153
+ return nil , err
154
+ }
155
+ if role == nil {
156
+ return logical .ErrorResponse (fmt .Sprintf ("role not found: %q" , name )), nil
157
+ }
158
+
159
+ config , err := connectionConfigLoad (ctx , req .Storage , role .Connection )
160
+ if err != nil {
161
+ return nil , err
162
+ }
163
+ // Check if isStandalone is set
164
+ if config .IsStandalone {
165
+ return nil , fmt .Errorf ("expected is_standalone to be unset for connection: %q" , role .Connection )
166
+ }
167
+
168
+ // If role name isn't in allowed roles, send back a permission denied.
169
+ if ! strutil .StrListContains (config .AllowedRoles , "*" ) && ! strutil .StrListContainsGlob (config .AllowedRoles , name ) {
170
+ return nil , fmt .Errorf ("%q is not an allowed role for connection %q" , name , role .Connection )
171
+ }
172
+
173
+ conn , err := b .ensureConnection (ctx , config )
174
+ if err != nil {
175
+ return nil , err
176
+ }
177
+
178
+ nodes , _ , err := conn .Deployment .GetSearchPeers ()
179
+ if err != nil {
180
+ b .Logger ().Error ("Error while reading SearchPeers from cluster master" , err )
181
+ return nil , fmt .Errorf ("unable to read searchpeers from cluster master" )
182
+ }
183
+ _ , err = findNode (nodeFQDN , nodes )
184
+ if err != nil {
185
+ return nil , err
186
+ }
187
+
188
+ // Re-create connection for node
189
+ config .URL = "https://" + nodeFQDN + ":8089"
190
+ // XXX config.ID = ""
191
+ conn , err = config .newConnection (ctx ) // XXX cache
192
+ if err != nil {
193
+ return nil , err
194
+ }
195
+ // Generate credentials
196
+ userUUID , err := uuid .GenerateUUID ()
197
+ if err != nil {
198
+ return nil , err
199
+ }
200
+ userPrefix := role .UserPrefix
201
+ if role .UserPrefix == defaultUserPrefix {
202
+ userPrefix = fmt .Sprintf ("%s_%s" , role .UserPrefix , req .DisplayName )
203
+ }
204
+ username := fmt .Sprintf ("%s_%s" , userPrefix , userUUID )
205
+ passwd , err := uuid .GenerateUUID ()
206
+ if err != nil {
207
+ return nil , errwrap .Wrapf ("error generating new password {{err}}" , err )
208
+ }
209
+ conn .Params ().BaseURL = nodeFQDN
210
+ opts := splunk.CreateUserOptions {
211
+ Name : username ,
212
+ Password : passwd ,
213
+ Roles : role .Roles ,
214
+ DefaultApp : role .DefaultApp ,
215
+ Email : role .Email ,
216
+ TZ : role .TZ ,
217
+ }
218
+ if _ , _ , err := conn .AccessControl .Authentication .Users .Create (& opts ); err != nil {
219
+ return nil , err
220
+ }
221
+
222
+ resp := b .Secret (secretCredsType ).Response (map [string ]interface {}{
223
+ // return to user
224
+ "username" : username ,
225
+ "password" : passwd ,
226
+ "roles" : role .Roles ,
227
+ "connection" : role .Connection ,
228
+ "url" : conn .Params ().BaseURL ,
229
+ }, map [string ]interface {}{
230
+ // store (with lease)
231
+ "username" : username ,
232
+ "role" : name ,
233
+ "connection" : role .Connection ,
234
+ "node_fqdn" : nodeFQDN ,
235
+ })
236
+ resp .Secret .TTL = role .DefaultTTL
237
+ resp .Secret .MaxTTL = role .MaxTTL
238
+
239
+ return resp , nil
240
+ }
241
+
242
+ func (b * backend ) credsReadHandler (ctx context.Context , req * logical.Request , d * framework.FieldData ) (* logical.Response , error ) {
243
+ name := d .Get ("name" ).(string )
244
+ node_fqdn , present := d .GetOk ("node_fqdn" )
245
+ // if node_fqdn is specified then the treat the request for a multi-node deployment
246
+ if present {
247
+ b .Logger ().Debug (fmt .Sprintf ("node_fqdn: [%s] specified for role: [%s]. using clustered mode getting temporary creds" , node_fqdn .(string ), name ))
248
+ return b .credsReadHandlerMulti (ctx , req , d )
249
+ }
250
+ b .Logger ().Debug (fmt .Sprintf ("node_fqdn not specified for role: [%s]. using standalone mode getting temporary creds" , name ))
251
+ return b .credsReadHandlerStandalone (ctx , req , d )
252
+ }
253
+
103
254
const pathCredsCreateHelpSyn = `
104
255
Request Splunk credentials for a certain role.
105
256
`
0 commit comments