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**
|
**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
|
- **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
|
- **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
|
- **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
|
- **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 (
|
const (
|
||||||
Prog = "vault-sidekick"
|
Prog = "vault-sidekick"
|
||||||
Version = "v0.0.8"
|
Version = "v0.0.9"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// step: parse and validate the command line / environment options
|
// step: parse and validate the command line / environment options
|
||||||
|
|
||||||
if err := parseOptions(); err != nil {
|
if err := parseOptions(); err != nil {
|
||||||
showUsage("invalid options, %s", err)
|
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)
|
glog.V(10).Infof("resource: %s has a previous lease: %s", x.resource, leaseID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// step: retrieve the resource from vault
|
|
||||||
err := r.get(x)
|
err := r.get(x)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("failed to retrieve the resource: %s from vault, error: %s", x.resource, err)
|
glog.Errorf("failed to retrieve the resource: %s from vault, error: %s", x.resource, err)
|
||||||
|
@ -147,7 +146,7 @@ func (r *VaultService) vaultServiceProcessor() {
|
||||||
break
|
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
|
// step: if we had a previous lease and the option is to revoke, lets throw into the revoke channel
|
||||||
if leaseID != "" && x.resource.revoked {
|
if leaseID != "" && x.resource.revoked {
|
||||||
|
@ -315,6 +314,7 @@ func (r VaultService) revoke(lease string) error {
|
||||||
func (r VaultService) get(rn *watchedResource) (err error) {
|
func (r VaultService) get(rn *watchedResource) (err error) {
|
||||||
var secret *api.Secret
|
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
|
// 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)
|
params := make(map[string]interface{}, 0)
|
||||||
for k, v := range rn.resource.options {
|
for k, v := range rn.resource.options {
|
||||||
params[k] = interface{}(v)
|
params[k] = interface{}(v)
|
||||||
|
@ -366,6 +366,17 @@ func (r VaultService) get(rn *watchedResource) (err error) {
|
||||||
fallthrough
|
fallthrough
|
||||||
case "secret":
|
case "secret":
|
||||||
secret, err = r.client.Logical().Read(rn.resource.path)
|
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
|
// step: check the error if any
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -375,7 +386,7 @@ func (r VaultService) get(rn *watchedResource) (err error) {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if secret == nil && err != nil {
|
if secret == nil && err == nil {
|
||||||
return fmt.Errorf("the resource does not exist")
|
return fmt.Errorf("the resource does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,12 @@ const (
|
||||||
optionUpdate = "update"
|
optionUpdate = "update"
|
||||||
// optionsExec executes something on a change
|
// optionsExec executes something on a change
|
||||||
optionExec = "exec"
|
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 (
|
var (
|
||||||
|
@ -65,6 +71,7 @@ func defaultVaultResource() *VaultResource {
|
||||||
renewable: false,
|
renewable: false,
|
||||||
revoked: false,
|
revoked: false,
|
||||||
options: make(map[string]string, 0),
|
options: make(map[string]string, 0),
|
||||||
|
size: defaultSize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +91,10 @@ type VaultResource struct {
|
||||||
revokeDelay time.Duration
|
revokeDelay time.Duration
|
||||||
// the lease duration
|
// the lease duration
|
||||||
update time.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
|
// the filename to save the secret
|
||||||
filename string
|
filename string
|
||||||
// the template file
|
// the template file
|
||||||
|
|
|
@ -65,7 +65,7 @@ func (r *VaultResources) Set(value string) error {
|
||||||
name := kp[0]
|
name := kp[0]
|
||||||
value := strings.Replace(kp[1], "|", ",", -1)
|
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 {
|
switch name {
|
||||||
case optionFormat:
|
case optionFormat:
|
||||||
if matched := resourceFormatRegex.MatchString(value); !matched {
|
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)
|
return fmt.Errorf("the renewal option: %s is invalid, should be a boolean", value)
|
||||||
}
|
}
|
||||||
rn.renewable = choice
|
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:
|
case optionExec:
|
||||||
rn.execPath = value
|
rn.execPath = value
|
||||||
case optionFilename:
|
case optionFilename:
|
||||||
|
|
Reference in a new issue