Skip to content

Commit

Permalink
adding vault id functionality and vault access revocation functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
shibme committed Mar 5, 2024
1 parent 9455cc5 commit 3e024ce
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 72 deletions.
File renamed without changes.
124 changes: 124 additions & 0 deletions core/vaults/access.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package vaults

import "savesecrets.org/slv/core/crypto"

func (vlt *Vault) Share(publicKey *crypto.PublicKey) (bool, error) {
if vlt.IsLocked() {
return false, errVaultLocked
}
if publicKey.Type() == VaultKey {
return false, errVaultCannotBeSharedWithVault
}
for _, wrappedKeyStr := range vlt.Config.WrappedKeys {
wrappedKey := &crypto.WrappedKey{}
if err := wrappedKey.FromString(wrappedKeyStr); err != nil {
return false, err
}
if wrappedKey.IsEncryptedBy(publicKey) {
return false, nil
}
}
wrappedKey, err := publicKey.EncryptKey(*vlt.secretKey)
if err == nil {
vlt.Config.WrappedKeys = append(vlt.Config.WrappedKeys, wrappedKey.String())
err = vlt.commit()
}
return err == nil, err
}

func (vlt *Vault) Revoke(publicKey *crypto.PublicKey, rotateVaultKeyPair bool) error {
if rotateVaultKeyPair {
if vlt.IsLocked() {
return errVaultLocked
}
accessors, err := vlt.ListAccessors()
if err != nil {
return err
}
var newAccessors []crypto.PublicKey
for _, accessor := range accessors {
if accessor.String() != publicKey.String() {
newAccessors = append(newAccessors, accessor)
}
}
if len(newAccessors) == len(accessors) {
return nil
}
secretsMap, err := vlt.GetAllSecrets()
if err != nil {
return err
}
vaultSecretKey, err := crypto.NewSecretKey(VaultKey)
if err != nil {
return err
}
vaultPublicKey, err := vaultSecretKey.PublicKey()
if err != nil {
return err
}
vlt.publicKey = vaultPublicKey
vlt.Config.PublicKey = vaultPublicKey.String()
vlt.secretKey = vaultSecretKey
vlt.Config.WrappedKeys = []string{}
for _, accessor := range newAccessors {
wrappedKey, err := accessor.EncryptKey(*vlt.secretKey)
if err == nil {
vlt.Config.WrappedKeys = append(vlt.Config.WrappedKeys, wrappedKey.String())
} else {
return err
}
}
for secretName, secretValue := range secretsMap {
if err = vlt.putSecretWithoutCommit(secretName, secretValue); err != nil {
return err
}
}
return vlt.commit()
} else {
for i, wrappedKeyStr := range vlt.Config.WrappedKeys {
wrappedKey := &crypto.WrappedKey{}
if err := wrappedKey.FromString(wrappedKeyStr); err != nil {
return err
}
if !wrappedKey.IsEncryptedBy(publicKey) {
vlt.Config.WrappedKeys = append(vlt.Config.WrappedKeys[:i], vlt.Config.WrappedKeys[i+1:]...)
return vlt.commit()
}
}
}
return nil
}

func (vlt *Vault) ListAccessors() ([]crypto.PublicKey, error) {
var accessors []crypto.PublicKey
for _, wrappedKeyStr := range vlt.Config.WrappedKeys {
wrappedKey := &crypto.WrappedKey{}
err := wrappedKey.FromString(wrappedKeyStr)
if err != nil {
return nil, err
}
accessors = append(accessors, wrappedKey.EncryptedBy())
}
return accessors, nil
}

func (vlt *Vault) Unlock(secretKey crypto.SecretKey) error {
publicKey, err := secretKey.PublicKey()
if err != nil || (!vlt.IsLocked() && *vlt.unlockedBy == publicKey.String()) {
return err
}
for _, wrappedKeyStr := range vlt.Config.WrappedKeys {
wrappedKey := &crypto.WrappedKey{}
if err = wrappedKey.FromString(wrappedKeyStr); err != nil {
return err
}
decryptedKey, err := secretKey.DecryptKey(*wrappedKey)
if err == nil {
vlt.secretKey = decryptedKey
vlt.unlockedBy = new(string)
*vlt.unlockedBy = publicKey.String()
return nil
}
}
return errVaultNotAccessible
}
8 changes: 5 additions & 3 deletions core/vaults/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ import (
const (
vaultFileNameEnding = config.AppNameLowerCase
VaultKey crypto.KeyType = 'V'
vaultIdLength = 30
secretNamePattern = "[a-zA-Z]([a-zA-Z0-9_]*[a-zA-Z0-9])?"
secretRefAbbrev = "VSR" // VSR = Vault Secret Reference
secretRefPatternBase = `\{\{\s*VAULTID\.` + secretNamePattern + `\s*\}\}`
vaultIdAbbrev = "VID"
)

var (
secretNameRegex = regexp.MustCompile(secretNamePattern)
secretRefRegex = regexp.MustCompile(strings.ReplaceAll(secretRefPatternBase, "VAULTID", "SLV_VPK_[A-Za-z0-9]+"))
secretRefRegex = regexp.MustCompile(strings.ReplaceAll(secretRefPatternBase, "VAULTID", config.AppNameUpperCase+"_"+vaultIdAbbrev+"_[A-Za-z0-9]+"))

errGeneratingVaultId = errors.New("error in generating a new vault id")
errInvalidVaultFileName = errors.New("invalid vault file name [vault file name must end with " + vaultFileNameEnding + ".yml or " + vaultFileNameEnding + ".yaml]")
errVaultDirPathCreation = errors.New("error in creating a new vault directory path")
errVaultNotAccessible = errors.New("vault is not accessible using the given environment key")
Expand All @@ -32,6 +34,6 @@ var (
errVaultSecretExistsAlready = errors.New("secret exists already for the given name")
errVaultSecretNotFound = errors.New("no secret found for the given name")
errVaultPublicKeyNotFound = errors.New("vault public key not found")
errInvalidReferenceFormat = errors.New("invalid reference format. references must follow the pattern {{SLV_VSR_VAULTID.secretName}} to allow dereferencing")
errInvalidReferenceFormat = errors.New("invalid reference format. references must follow the pattern {{" + config.AppNameUpperCase + "_" + vaultIdAbbrev + "_ABCXYZ.secretName}} to allow dereferencing")
errInvalidImportDataFormat = errors.New("invalid import data format - expected a map of string to string [secretName: secretValue] in YAML/JSON format")
)
12 changes: 5 additions & 7 deletions core/vaults/deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ func (v *Vault) DeepCopyInto(out *Vault) {
for key, val := range v.Secrets {
out.Secrets[key] = val
}
out.Config = vaultConfig{}
if v.Config.PublicKey != "" {
out.Config.PublicKey = v.Config.PublicKey
out.Config = vaultConfig{
Id: v.Config.Id,
PublicKey: v.Config.PublicKey,
HashLength: v.Config.HashLength,
WrappedKeys: make([]string, len(v.Config.WrappedKeys)),
}
if v.Config.HashLength != 0 {
out.Config.HashLength = v.Config.HashLength
}
out.Config.WrappedKeys = make([]string, len(v.Config.WrappedKeys))
copy(out.Config.WrappedKeys, v.Config.WrappedKeys)
out.vaultSecretRefRegex = v.vaultSecretRefRegex
}
76 changes: 17 additions & 59 deletions core/vaults/vault.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package vaults

import (
"crypto/rand"
"os"
"path"
"regexp"
"strings"

"savesecrets.org/slv/core/commons"
"savesecrets.org/slv/core/config"
"savesecrets.org/slv/core/crypto"
)

type vaultConfig struct {
Id string `json:"id" yaml:"id"`
PublicKey string `json:"publicKey" yaml:"publicKey"`
HashLength uint8 `json:"hashLength,omitempty" yaml:"hashLength,omitempty"`
WrappedKeys []string `json:"wrappedKeys" yaml:"wrappedKeys"`
Expand All @@ -29,7 +32,7 @@ type Vault struct {
}

func (vlt *Vault) Id() string {
return vlt.Config.PublicKey
return vlt.Config.Id
}

func (vlt *Vault) getPublicKey() (publicKey *crypto.PublicKey, err error) {
Expand All @@ -51,6 +54,14 @@ func isValidVaultFileName(fileName string) bool {
strings.HasSuffix(fileName, vaultFileNameEnding+".yml")
}

func newVaultId() (string, error) {
idBytes := make([]byte, vaultIdLength)
if _, err := rand.Read(idBytes); err != nil {
return "", errGeneratingVaultId
}
return config.AppNameUpperCase + "_" + vaultIdAbbrev + "_" + commons.Encode(idBytes), nil
}

// Returns new vault instance and the vault contents set into the specified field. The vault file name must end with .slv.yml or .slv.yaml.
func New(filePath, objectField string, hashLength uint8, rootPublicKey *crypto.PublicKey, publicKeys ...*crypto.PublicKey) (vlt *Vault, err error) {
if !isValidVaultFileName(filePath) {
Expand All @@ -70,9 +81,14 @@ func New(filePath, objectField string, hashLength uint8, rootPublicKey *crypto.P
if err != nil {
return nil, err
}
vauldId, err := newVaultId()
if err != nil {
return nil, err
}
vlt = &Vault{
publicKey: vaultPublicKey,
Config: vaultConfig{
Id: vauldId,
PublicKey: vaultPublicKey.String(),
HashLength: hashLength,
},
Expand Down Expand Up @@ -134,40 +150,6 @@ func (vlt *Vault) UnlockedBy() (id *string) {
return vlt.unlockedBy
}

func (vlt *Vault) ListAccessors() ([]crypto.PublicKey, error) {
var accessors []crypto.PublicKey
for _, wrappedKeyStr := range vlt.Config.WrappedKeys {
wrappedKey := &crypto.WrappedKey{}
err := wrappedKey.FromString(wrappedKeyStr)
if err != nil {
return nil, err
}
accessors = append(accessors, wrappedKey.EncryptedBy())
}
return accessors, nil
}

func (vlt *Vault) Unlock(secretKey crypto.SecretKey) error {
publicKey, err := secretKey.PublicKey()
if err != nil || (!vlt.IsLocked() && *vlt.unlockedBy == publicKey.String()) {
return err
}
for _, wrappedKeyStr := range vlt.Config.WrappedKeys {
wrappedKey := &crypto.WrappedKey{}
if err = wrappedKey.FromString(wrappedKeyStr); err != nil {
return err
}
decryptedKey, err := secretKey.DecryptKey(*wrappedKey)
if err == nil {
vlt.secretKey = decryptedKey
vlt.unlockedBy = new(string)
*vlt.unlockedBy = publicKey.String()
return nil
}
}
return errVaultNotAccessible
}

func (vlt *Vault) commit() error {
if vlt.objectField == "" {
return commons.WriteToYAML(vlt.path,
Expand All @@ -191,27 +173,3 @@ func (vlt *Vault) reset() error {
vlt.clearSecretCache()
return commons.ReadFromYAML(vlt.path, &vlt)
}

func (vlt *Vault) Share(publicKey *crypto.PublicKey) (bool, error) {
if vlt.IsLocked() {
return false, errVaultLocked
}
if publicKey.Type() == VaultKey {
return false, errVaultCannotBeSharedWithVault
}
for _, wrappedKeyStr := range vlt.Config.WrappedKeys {
wrappedKey := &crypto.WrappedKey{}
if err := wrappedKey.FromString(wrappedKeyStr); err != nil {
return false, err
}
if wrappedKey.IsEncryptedBy(publicKey) {
return false, nil
}
}
wrappedKey, err := publicKey.EncryptKey(*vlt.secretKey)
if err == nil {
vlt.Config.WrappedKeys = append(vlt.Config.WrappedKeys, wrappedKey.String())
err = vlt.commit()
}
return err == nil, err
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ require (
golang.org/x/tools v0.19.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.32.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
Expand Down
3 changes: 3 additions & 0 deletions operator/config/crd/bases/slv.savesecrets.org_slvs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@ spec:
properties:
hashLength:
type: integer
id:
type: string
publicKey:
type: string
wrappedKeys:
items:
type: string
type: array
required:
- id
- publicKey
- wrappedKeys
type: object
Expand Down
1 change: 1 addition & 0 deletions operator/config/samples/slv_v1_slv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ spec:
dog7: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAA6ZS2LSPURVVTZGLAIQX6KUF2HJ7OAC36XIGVJUSPFWQ5NWJFRZIY4QLLDF467B67ISEUNMTPHTMMDA2L5WZR2NDHLIAVTXNA765F5Z7AJXRPBJXCRGPUGM3U5QVLNZDUG2L24Q7ZUBT4Z3Q
dog8: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAASXY3S325JRWOGC2LW2JTTZABWFOWHOT5WSY74ZJATOWT7IPY2JISZEGMNN7R7UGPAQCHT5KRVNT6MFENBBJB3DTC7UA55W5LSYSLBNF57YC2YYJZDQ3JJA2LJEF7OZJF2EEZO2LQ5J6OPBA
slvConfig:
id: SLV_ID_UETC5IMQC376IJ7GZZDPBHGHKTBNCOFBOYKSZ3MITQA2LGWG
publicKey: SLV_VPK_AEAVMAF7YJOKIUCN2WWOARRP2O2FRKC3UP2ZKDMKPNNUYQXEGXNCZXQBLQ
wrappedKeys:
- SLV_EWK_AFCWLVMKRUACIAIBIUAIXDGQXVQRO2763RHRAYPJP76DMVUXPEVO3YMPFIIEG2IT7OKIEWIA2QG6EVXOHHKNZVUG73LXSQ5OD2XW3QEXKN5FYWLVMT67E2RHN5NKCEQ2EY4CRN3M5LGVPTAWMEIE37NT7Y5QH6AKMMA6NNASASUMJ3KNEPHS5BOQ43ROZGLDFVWC53HOUI3H6L32D5VOTLZWDT6N62QBYU6AH6IRBNKSUV6TSFEU2ZE7VVXDZXQ2APYURJHRFOYM4

0 comments on commit 3e024ce

Please sign in to comment.