Skip to content
This repository has been archived by the owner on Jul 25, 2023. It is now read-only.

Encrypt values and save as secrets #252

Merged
merged 5 commits into from
Apr 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion app/safe/internal/server/route/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,40 @@ func Secret(w http.ResponseWriter, r *http.Request, svid string) {
namespace := sr.Namespace
template := sr.Template
format := sr.Format
encrypt := sr.Encrypt

if workloadId == "" && encrypt {
if value == "" {
j.Event = audit.EventNoValue
audit.Log(j)

w.WriteHeader(http.StatusBadRequest)
_, err := io.WriteString(w, "")
if err != nil {
log.InfoLn("Secret: Problem sending response", err.Error())
}
return
}

encrypted, err := state.EncryptValue(value)
if err != nil {
j.Event = audit.EventEncryptionFailed
audit.Log(j)

w.WriteHeader(http.StatusInternalServerError)
_, err := io.WriteString(w, "")
if err != nil {
log.InfoLn("Secret: Problem sending response", err.Error())
}
return
}

_, err = io.WriteString(w, encrypted)
if err != nil {
log.InfoLn("Secret: Problem sending response", err.Error())
}
return
}

if namespace == "" {
namespace = "default"
Expand All @@ -110,15 +144,34 @@ func Secret(w http.ResponseWriter, r *http.Request, svid string) {
"backingStore:", backingStore,
"template:", template,
"format:", format,
"encrypt:", encrypt,
"useK8s", useK8s)

if workloadId == "" {
if workloadId == "" && !encrypt {
j.Event = audit.EventNoWorkloadId
audit.Log(j)

return
}

// `encrypt` means that the value is encrypted, so we need to decrypt it.
if encrypt {
decrypted, err := state.DecryptValue(value)
if err != nil {
j.Event = audit.EventDecryptionFailed
audit.Log(j)

w.WriteHeader(http.StatusInternalServerError)
_, err := io.WriteString(w, "")
if err != nil {
log.InfoLn("Secret: Problem sending response", err.Error())
}
return
}

value = decrypted
}

state.UpsertSecret(entity.SecretStored{
Name: workloadId,
Meta: entity.SecretMeta{
Expand Down
26 changes: 26 additions & 0 deletions app/safe/internal/state/internal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* .-'_.---._'-.
* ||####|(__)|| Protect your secrets, protect your business.
* \\()|##// Secure your sensitive data with Aegis.
* \\ |#// <aegis.ist>
* .\_/.
*/

package state

import "encoding/json"

const selfName = "aegis-safe"

type AegisInternalCommand struct {
LogLevel int `json:"logLevel"`
}

func evaluate(data string) *AegisInternalCommand {
var command AegisInternalCommand
err := json.Unmarshal([]byte(data), &command)
if err != nil {
return nil
}
return &command
}
88 changes: 47 additions & 41 deletions app/safe/internal/state/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,31 +41,18 @@ func ageKeyPair() (string, string) {
return parts[0], parts[1]
}

func decryptDataFromDisk(key string) ([]byte, error) {
dataPath := path.Join(env.SafeDataPath(), key+".age")

if _, err := os.Stat(dataPath); os.IsNotExist(err) {
log.TraceLn("decryptDataFromDisk: No file at:", dataPath)
return nil, err
}

data, err := os.ReadFile(dataPath)
if err != nil {
log.WarnLn("decryptDataFromDisk: Error reading file:", err.Error())
return nil, err
}

func decryptBytes(data []byte) ([]byte, error) {
privateKey, _ := ageKeyPair()

identity, err := age.ParseX25519Identity(privateKey)
if err != nil {
log.WarnLn("Failed to parse private key", privateKey, err)
return nil, err
return []byte{}, err
}

if len(data) == 0 {
log.WarnLn("file on disk appears to be empty")
return nil, err
return []byte{}, err
}

out := &bytes.Buffer{}
Expand All @@ -74,17 +61,34 @@ func decryptDataFromDisk(key string) ([]byte, error) {
r, err := age.Decrypt(f, identity)
if err != nil {
log.WarnLn("Failed to open encrypted file", err.Error())
return nil, err
return []byte{}, err
}

if _, err := io.Copy(out, r); err != nil {
log.WarnLn("Failed to read encrypted file", err.Error())
return nil, err
return []byte{}, err
}

return out.Bytes(), nil
}

func decryptDataFromDisk(key string) ([]byte, error) {
dataPath := path.Join(env.SafeDataPath(), key+".age")

if _, err := os.Stat(dataPath); os.IsNotExist(err) {
log.TraceLn("decryptDataFromDisk: No file at:", dataPath)
return nil, err
}

data, err := os.ReadFile(dataPath)
if err != nil {
log.WarnLn("decryptDataFromDisk: Error reading file:", err.Error())
return nil, err
}

return decryptBytes(data)
}

// readFromDisk returns a pointer to a secret.
// It returns a nil pointer if secret cannot be read
// // readFromDisk returns a pointer to a secret.
Expand All @@ -106,53 +110,55 @@ func readFromDisk(key string) *entity.SecretStored {

var lastBackedUpIndex = make(map[string]int)

func saveSecretToDisk(secret entity.SecretStored, dataPath string) error {
data, err := json.Marshal(secret)
func encryptToWriter(out io.Writer, data string) error {
_, publicKey := ageKeyPair()
recipient, err := age.ParseX25519Recipient(publicKey)
if err != nil {
log.WarnLn("persist: failed to marshal secret", err.Error())
log.WarnLn("Failed to parse public key", publicKey, err.Error())
return err
}

file, err := os.Create(dataPath)
wrappedWriter, err := age.Encrypt(out, recipient)
if err != nil {
log.WarnLn("persist: problem creating file", err.Error())
log.WarnLn("Failed to create encrypted file", err.Error())
return err
}

if _, err := io.WriteString(wrappedWriter, data); err != nil {
log.FatalLn("Failed to write to encrypted file: %v", err.Error())
return err
}

defer func() {
err := file.Close()
err := wrappedWriter.Close()
if err != nil {
log.InfoLn("problem closing file", err.Error())
log.InfoLn("problem closing stream", err.Error())
}
}()

_, publicKey := ageKeyPair()
recipient, err := age.ParseX25519Recipient(publicKey)
if err != nil {
log.WarnLn("Failed to parse public key", publicKey, err.Error())
return err
}

out := file
return nil
}

w, err := age.Encrypt(out, recipient)
func saveSecretToDisk(secret entity.SecretStored, dataPath string) error {
data, err := json.Marshal(secret)
if err != nil {
log.WarnLn("Failed to create encrypted file", err.Error())
log.WarnLn("persist: failed to marshal secret", err.Error())
return err
}

if _, err := io.WriteString(w, string(data)); err != nil {
log.FatalLn("Failed to write to encrypted file: %v", err.Error())
file, err := os.Create(dataPath)
if err != nil {
log.WarnLn("persist: problem creating file", err.Error())
return err
}

defer func() {
err := w.Close()
err := file.Close()
if err != nil {
log.InfoLn("problem closing stream", err.Error())
log.InfoLn("problem closing file", err.Error())
}
}()

return nil
return encryptToWriter(file, string(data))
}

const InitialSecretValue = `{"empty":true}`
Expand Down
Loading