Added a create option for generic secrets with an optional size parameter
This commit is contained in:
parent
e5aa48121b
commit
bcc4e3fe8f
|
@ -125,6 +125,7 @@ bundle format is very similar in the sense it similar takes the private key and
|
|||
**Resource Options**
|
||||
|
||||
- **file**: (filaname) by default all file are relative to the output directory specified and will have the name NAME.RESOURCE; the fn options allows you to switch names and paths to write the files
|
||||
- **create**: (create) create the resource
|
||||
- **update**: (update) override the lease time of this resource and get/renew a secret on the specified duration e.g 1m, 2d, 5m10s
|
||||
- **renew**: (renewal) override the default behavour on this resource, renew the resource when coming close to expiration e.g true, TRUE
|
||||
- **delay**: (renewal-delay) delay the revoking the lease of a resource for x period once time e.g 1m, 1h20s
|
||||
|
|
53
generate.go
Normal file
53
generate.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright 2015 Home Office All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"crypto/rand"
|
||||
|
||||
)
|
||||
|
||||
var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+,.?/:;{}[]`~")
|
||||
|
||||
func NewPassword(length int) string {
|
||||
return rand_char(length, StdChars)
|
||||
}
|
||||
|
||||
func rand_char(length int, chars []byte) string {
|
||||
new_pword := make([]byte, length)
|
||||
random_data := make([]byte, length+(length/4)) // storage for random bytes.
|
||||
clen := byte(len(chars))
|
||||
maxrb := byte(256 - (256 % len(chars)))
|
||||
i := 0
|
||||
for {
|
||||
if _, err := io.ReadFull(rand.Reader, random_data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, c := range random_data {
|
||||
if c >= maxrb {
|
||||
continue
|
||||
}
|
||||
new_pword[i] = chars[c%clen]
|
||||
i++
|
||||
if i == length {
|
||||
return string(new_pword)
|
||||
}
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
3
main.go
3
main.go
|
@ -26,11 +26,12 @@ import (
|
|||
|
||||
const (
|
||||
Prog = "vault-sidekick"
|
||||
Version = "v0.0.8"
|
||||
Version = "v0.0.9"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// step: parse and validate the command line / environment options
|
||||
|
||||
if err := parseOptions(); err != nil {
|
||||
showUsage("invalid options, %s", err)
|
||||
}
|
||||
|
|
17
vault.go
17
vault.go
|
@ -138,7 +138,6 @@ func (r *VaultService) vaultServiceProcessor() {
|
|||
glog.V(10).Infof("resource: %s has a previous lease: %s", x.resource, leaseID)
|
||||
}
|
||||
|
||||
// step: retrieve the resource from vault
|
||||
err := r.get(x)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to retrieve the resource: %s from vault, error: %s", x.resource, err)
|
||||
|
@ -147,7 +146,7 @@ func (r *VaultService) vaultServiceProcessor() {
|
|||
break
|
||||
}
|
||||
|
||||
glog.V(4).Infof("successfully retrieved resournce: %s, leaseID: %s", x.resource, x.secret.LeaseID)
|
||||
glog.V(4).Infof("successfully retrieved resource: %s, leaseID: %s", x.resource, x.secret.LeaseID)
|
||||
|
||||
// step: if we had a previous lease and the option is to revoke, lets throw into the revoke channel
|
||||
if leaseID != "" && x.resource.revoked {
|
||||
|
@ -315,6 +314,7 @@ func (r VaultService) revoke(lease string) error {
|
|||
func (r VaultService) get(rn *watchedResource) (err error) {
|
||||
var secret *api.Secret
|
||||
// step: not sure who to cast map[string]string to map[string]interface{} doesn't like it anyway i try and do it
|
||||
|
||||
params := make(map[string]interface{}, 0)
|
||||
for k, v := range rn.resource.options {
|
||||
params[k] = interface{}(v)
|
||||
|
@ -366,6 +366,17 @@ func (r VaultService) get(rn *watchedResource) (err error) {
|
|||
fallthrough
|
||||
case "secret":
|
||||
secret, err = r.client.Logical().Read(rn.resource.path)
|
||||
// We must generate the secret if we have the create flag
|
||||
if rn.resource.create && secret == nil && err == nil {
|
||||
glog.V(3).Infof("Create param specified, creating resource: %s", rn.resource.path)
|
||||
params["value"] = NewPassword(int(rn.resource.size))
|
||||
secret, err = r.client.Logical().Write(fmt.Sprintf(rn.resource.path), params)
|
||||
glog.V(3).Infof("Secret created: %s", rn.resource.path)
|
||||
if err == nil {
|
||||
// Populate the secret data as stored in Vault...
|
||||
secret, err = r.client.Logical().Read(rn.resource.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
// step: check the error if any
|
||||
if err != nil {
|
||||
|
@ -375,7 +386,7 @@ func (r VaultService) get(rn *watchedResource) (err error) {
|
|||
}
|
||||
return err
|
||||
}
|
||||
if secret == nil && err != nil {
|
||||
if secret == nil && err == nil {
|
||||
return fmt.Errorf("the resource does not exist")
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,12 @@ const (
|
|||
optionUpdate = "update"
|
||||
// optionsExec executes something on a change
|
||||
optionExec = "exec"
|
||||
// optionCreate creates a secret if it doesn't exist
|
||||
optionCreate = "create"
|
||||
// optionSize sets the initial size of a password secret
|
||||
optionSize = "size"
|
||||
// defaultSize sets the default size of a generic secret
|
||||
defaultSize = 20
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -65,6 +71,7 @@ func defaultVaultResource() *VaultResource {
|
|||
renewable: false,
|
||||
revoked: false,
|
||||
options: make(map[string]string, 0),
|
||||
size: defaultSize,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,6 +91,10 @@ type VaultResource struct {
|
|||
revokeDelay time.Duration
|
||||
// the lease duration
|
||||
update time.Duration
|
||||
// whether the resource should be created?
|
||||
create bool
|
||||
// the size of a secret to create
|
||||
size int64
|
||||
// the filename to save the secret
|
||||
filename string
|
||||
// the template file
|
||||
|
|
|
@ -65,7 +65,7 @@ func (r *VaultResources) Set(value string) error {
|
|||
name := kp[0]
|
||||
value := strings.Replace(kp[1], "|", ",", -1)
|
||||
|
||||
// step: extract the control options from the path resource parameteres
|
||||
// step: extract the control options from the path resource parameters
|
||||
switch name {
|
||||
case optionFormat:
|
||||
if matched := resourceFormatRegex.MatchString(value); !matched {
|
||||
|
@ -96,6 +96,21 @@ func (r *VaultResources) Set(value string) error {
|
|||
return fmt.Errorf("the renewal option: %s is invalid, should be a boolean", value)
|
||||
}
|
||||
rn.renewable = choice
|
||||
case optionCreate:
|
||||
choice, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the create option: %s is invalid, should be a boolean", value)
|
||||
}
|
||||
if rn.resource != "secret" {
|
||||
return fmt.Errorf("the create option is only supported for 'cn=secret' at this time")
|
||||
}
|
||||
rn.create = choice
|
||||
case optionSize:
|
||||
size, err := strconv.ParseInt(value, 10, 16)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the size option: %s is invalid, should be an integer", value)
|
||||
}
|
||||
rn.size = size
|
||||
case optionExec:
|
||||
rn.execPath = value
|
||||
case optionFilename:
|
||||
|
|
Reference in a new issue