Skip to content

Commit

Permalink
Change keys from snake_case to camelCase
Browse files Browse the repository at this point in the history
  • Loading branch information
dschniepp committed May 13, 2020
1 parent 5577562 commit face58b
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 64 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Renamed cert source `controller` to `kubernetes`
- Moved `maxAge` one level up
- keys from snake_case to camelCase
### Fixed
- Flaky verify command

Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ The filename can be overwritten by setting the `--config` flag.
A sample configuration file can be created via `sealit init`.

```yaml
sealing_rules:
- file_regex: \.dev\.yaml$ # Regex pattern for which files this rules are applied
sealingRules:
- fileRegex: \.dev\.yaml$ # Regex pattern for which files this rules are applied
name: secret # Name of the future secret
namespace: default # Namespace of the future secret
encrypt_regex: (password|pin)$ # Regex of the key names which should be encrypted
secretsRegex: (password|pin)$ # Regex of the key names which should be encrypted
maxAge: 720h0m0s
cert:
url: https://example.org
Expand All @@ -75,7 +75,7 @@ Otherwise the cert from the meta field within the `values.yaml` file is used for
#### Local cert file

```yaml
sealing_rules:
sealingRules:
- ...
cert:
...
Expand All @@ -85,7 +85,7 @@ sealing_rules:
#### Remote cert file

```yaml
sealing_rules:
sealingRules:
- ...
cert:
...
Expand All @@ -95,7 +95,7 @@ sealing_rules:
#### Remote cert from Kubernetes

```yaml
sealing_rules:
sealingRules:
- ...
cert:
...
Expand Down
10 changes: 5 additions & 5 deletions example/.sealit.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
sealing_rules:
- file_regex: \.dev\.yaml$
sealingRules:
- fileRegex: \.dev\.yaml$
name: mysecret
namespace: default
encrypt_regex: (password|pin)$
secretsRegex: (password|pin)$
maxAge: 720h
cert:
path: cert.pem
- file_regex: \.prod\.yaml$
- fileRegex: \.prod\.yaml$
name: mysecret
namespace: default
encrypt_regex: (password|pin)$
secretsRegex: (password|pin)$
maxAge: 720h
cert:
path: cert.pem
2 changes: 1 addition & 1 deletion example/values.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ env:
password: secret!
username: john
secret: john
asd_username: john
pin: 1234
4 changes: 2 additions & 2 deletions internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
var kubeConfig string

type Config struct {
SealingRuleSets []SealingRuleSet `yaml:"sealing_rules"`
SealingRuleSets []SealingRuleSet `yaml:"sealingRules"`
}

// ExampleConfig Provide an example config of the `.sealit.yaml`
Expand All @@ -25,7 +25,7 @@ func ExampleConfig() Config {
FileRegex: "\\.dev\\.yaml$",
Name: "secret",
Namespace: "default",
EncryptRegex: "(password|pin)$",
SecretsRegex: "(password|pin)$",
MaxAge: d,
CertSources: CertSources{
Url: "https://example.org",
Expand Down
6 changes: 3 additions & 3 deletions internal/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package internal
import "testing"

var basicConfig = []byte(`
sealing_rules:
- file_regex: \.dev\.yaml$
sealingRules:
- fileRegex: \.dev\.yaml$
name: mysecret
namespace: default
encrypt_regex: (password|pin)$
secretsRegex: (password|pin)$
maxAge: 720h
cert:
kubernetes:
Expand Down
69 changes: 39 additions & 30 deletions internal/sealer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ const (
validCert
)

const encodeIdentifier = "ENC:"

type Sealer struct {
regexp *regexp.Regexp
publicKey *rsa.PublicKey
label []byte
metadata *Metadata
secretsRegexp *regexp.Regexp
publicKey *rsa.PublicKey
label []byte
metadata *Metadata
}

func NewSealer(srs *SealingRuleSet, m *Metadata, fetchCert bool) (s *Sealer, err error) {
Expand Down Expand Up @@ -72,47 +74,54 @@ func NewSealer(srs *SealingRuleSet, m *Metadata, fetchCert bool) (s *Sealer, err
}

return &Sealer{
regexp: regexp.MustCompile(srs.EncryptRegex),
publicKey: pKey,
label: m.getLabel(),
metadata: m,
secretsRegexp: srs.GetRegexForSecrets(),
publicKey: pKey,
label: m.getLabel(),
metadata: m,
}, nil
}

func (s *Sealer) valueNeedsToBeSealed(key *yaml.Node, value *yaml.Node) bool {
if s.secretsRegexp.MatchString(key.Value) {
if !strings.HasPrefix(value.Value, encodeIdentifier) {
return true
}
log.Printf("[DEBUG] Value of `%s` was already encrypted", key.Value)

return false
}
log.Printf("[DEBUG] `%s` did not match regex %s", key.Value, s.secretsRegexp.String())

return false
}

func (s *Sealer) Verify(key *yaml.Node, value *yaml.Node) error {
if s.regexp.MatchString(key.Value) && !strings.HasPrefix(value.Value, "ENC:") {
if s.valueNeedsToBeSealed(key, value) {
return fmt.Errorf("key `%s` is not encrypted", key.Value)
}

return nil
}

func (s *Sealer) Seal(key *yaml.Node, value *yaml.Node) error {
if s.regexp.MatchString(key.Value) {
if !strings.HasPrefix(value.Value, "ENC:") {
ciphertext, err := crypto.HybridEncrypt(rand.Reader, s.publicKey, []byte(value.Value), s.label)

if err != nil {
return err
}

if value.Value == "" {
log.Printf("[WARNING] Value of `%s` is an empty string", key.Value)
} else if value.Value != strings.TrimSpace(value.Value) {
log.Printf("[WARNING] Value of `%s` is padded with whitespace", key.Value)
}
if s.valueNeedsToBeSealed(key, value) {
ciphertext, err := crypto.HybridEncrypt(rand.Reader, s.publicKey, []byte(value.Value), s.label)

encodedSecret := base64.StdEncoding.EncodeToString(ciphertext)
value.SetString(fmt.Sprintf("ENC:%s", encodedSecret))
s.metadata.SealedAt = time.Now().Format(time.RFC3339)
log.Printf("[DEBUG] Encrypted value of `%s`", key.Value)
if err != nil {
return err
}

return nil
if value.Value == "" {
log.Printf("[WARNING] Value of `%s` is an empty string", key.Value)
} else if value.Value != strings.TrimSpace(value.Value) {
log.Printf("[WARNING] Value of `%s` is padded with whitespace", key.Value)
}
log.Printf("[DEBUG] Value of `%s` was already encrypted", key.Value)

return nil
encodedSecret := base64.StdEncoding.EncodeToString(ciphertext)
value.SetString(fmt.Sprintf("%s%s", encodeIdentifier, encodedSecret))
s.metadata.SealedAt = time.Now().Format(time.RFC3339)
log.Printf("[DEBUG] Encrypted value of `%s`", key.Value)
}
log.Printf("[DEBUG] `%s` did not match regex %s", key.Value, s.regexp.String())

return nil
}
Expand Down
30 changes: 15 additions & 15 deletions internal/sealer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ func TestSealSecrets(t *testing.T) {
key, _ := testGeneratePrivateKey()

s := Sealer{
regexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
secretsRegexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
}

k := &yaml.Node{Value: "test_password"}
Expand All @@ -35,9 +35,9 @@ func TestVerifySecrets(t *testing.T) {
key, _ := testGeneratePrivateKey()

s := Sealer{
regexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
secretsRegexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
}

k := &yaml.Node{Value: "test_password"}
Expand All @@ -53,9 +53,9 @@ func TestVerifyUnsealedSecrets(t *testing.T) {
key, _ := testGeneratePrivateKey()

s := Sealer{
regexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
secretsRegexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
}

k := &yaml.Node{Value: "test_password"}
Expand All @@ -71,9 +71,9 @@ func TestSealingOfAlreadySealedSecrets(t *testing.T) {
key, _ := testGeneratePrivateKey()

s := Sealer{
regexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
secretsRegexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
}

k := &yaml.Node{Value: "test_password"}
Expand All @@ -89,9 +89,9 @@ func TestSealNonSecrets(t *testing.T) {
key, _ := testGeneratePrivateKey()

s := Sealer{
regexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
secretsRegexp: regexp.MustCompile(`(password|pin)$`),
publicKey: &key.PublicKey,
metadata: &Metadata{},
}

k := &yaml.Node{Value: "test"}
Expand Down
9 changes: 7 additions & 2 deletions internal/sealing_rule_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"log"
"net/http"
"os"
"regexp"
"time"

corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
Expand All @@ -22,10 +23,10 @@ type certSource interface {
}

type SealingRuleSet struct {
FileRegex string `yaml:"file_regex"`
FileRegex string `yaml:"fileRegex"`
Name string `yaml:"name"`
Namespace string `yaml:"namespace"`
EncryptRegex string `yaml:"encrypt_regex"`
SecretsRegex string `yaml:"secretsRegex"`
MaxAge time.Duration `yaml:"maxAge"`
CertSources CertSources `yaml:"cert"`
}
Expand All @@ -46,6 +47,10 @@ type KubernetesCertSource struct {
Namespace string `yaml:"namespace"`
}

func (srs *SealingRuleSet) GetRegexForSecrets() *regexp.Regexp {
return regexp.MustCompile(srs.SecretsRegex)
}

// GetCert fetches the cert from different sources
// Prio:
// 1. fetch from Kubernetes cluster
Expand Down

0 comments on commit face58b

Please sign in to comment.