uses protected memory buffers now for all secrets in ram
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"git.eeqj.de/sneak/secret/internal/secret"
|
||||
"git.eeqj.de/sneak/secret/internal/vault"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/awnumar/memguard"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -103,6 +103,20 @@ func newImportCmd() *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// updateBufferSize updates the buffer size based on usage pattern
|
||||
func updateBufferSize(currentSize int, sameSize *int) int {
|
||||
*sameSize++
|
||||
const doubleAfterBuffers = 2
|
||||
const growthFactor = 2
|
||||
if *sameSize >= doubleAfterBuffers {
|
||||
*sameSize = 0
|
||||
|
||||
return currentSize * growthFactor
|
||||
}
|
||||
|
||||
return currentSize
|
||||
}
|
||||
|
||||
// AddSecret adds a secret to the current vault
|
||||
func (cli *Instance) AddSecret(secretName string, force bool) error {
|
||||
secret.Debug("CLI AddSecret starting", "secret_name", secretName, "force", force)
|
||||
@@ -116,24 +130,82 @@ func (cli *Instance) AddSecret(secretName string, force bool) error {
|
||||
|
||||
secret.Debug("Got current vault", "vault_name", vlt.GetName())
|
||||
|
||||
// Read secret value from stdin
|
||||
secret.Debug("Reading secret value from stdin")
|
||||
value, err := io.ReadAll(cli.cmd.InOrStdin())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read secret value: %w", err)
|
||||
// Read secret value directly into protected buffers
|
||||
secret.Debug("Reading secret value from stdin into protected buffers")
|
||||
|
||||
const initialSize = 4 * 1024 // 4KB initial buffer
|
||||
const maxSize = 100 * 1024 * 1024 // 100MB max
|
||||
|
||||
type bufferInfo struct {
|
||||
buffer *memguard.LockedBuffer
|
||||
used int
|
||||
}
|
||||
|
||||
secret.Debug("Read secret value from stdin", "value_length", len(value))
|
||||
var buffers []bufferInfo
|
||||
defer func() {
|
||||
for _, b := range buffers {
|
||||
b.buffer.Destroy()
|
||||
}
|
||||
}()
|
||||
|
||||
// Remove trailing newline if present
|
||||
if len(value) > 0 && value[len(value)-1] == '\n' {
|
||||
value = value[:len(value)-1]
|
||||
secret.Debug("Removed trailing newline", "new_length", len(value))
|
||||
reader := cli.cmd.InOrStdin()
|
||||
totalSize := 0
|
||||
currentBufferSize := initialSize
|
||||
sameSize := 0
|
||||
|
||||
for {
|
||||
// Create a new buffer
|
||||
buffer := memguard.NewBuffer(currentBufferSize)
|
||||
n, err := io.ReadFull(reader, buffer.Bytes())
|
||||
|
||||
if n == 0 {
|
||||
// No data read, destroy the unused buffer
|
||||
buffer.Destroy()
|
||||
} else {
|
||||
buffers = append(buffers, bufferInfo{buffer: buffer, used: n})
|
||||
totalSize += n
|
||||
|
||||
if totalSize > maxSize {
|
||||
return fmt.Errorf("secret too large: exceeds 100MB limit")
|
||||
}
|
||||
|
||||
// If we filled the buffer, consider growing for next iteration
|
||||
if n == currentBufferSize {
|
||||
currentBufferSize = updateBufferSize(currentBufferSize, &sameSize)
|
||||
}
|
||||
}
|
||||
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to read secret value: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for trailing newline in the last buffer
|
||||
if len(buffers) > 0 && totalSize > 0 {
|
||||
lastBuffer := &buffers[len(buffers)-1]
|
||||
if lastBuffer.buffer.Bytes()[lastBuffer.used-1] == '\n' {
|
||||
lastBuffer.used--
|
||||
totalSize--
|
||||
}
|
||||
}
|
||||
|
||||
secret.Debug("Read secret value from stdin", "value_length", totalSize, "buffers", len(buffers))
|
||||
|
||||
// Combine all buffers into a single protected buffer
|
||||
valueBuffer := memguard.NewBuffer(totalSize)
|
||||
defer valueBuffer.Destroy()
|
||||
|
||||
offset := 0
|
||||
for _, b := range buffers {
|
||||
copy(valueBuffer.Bytes()[offset:], b.buffer.Bytes()[:b.used])
|
||||
offset += b.used
|
||||
}
|
||||
|
||||
// Add the secret to the vault
|
||||
secret.Debug("Calling vault.AddSecret", "secret_name", secretName, "value_length", len(value), "force", force)
|
||||
if err := vlt.AddSecret(secretName, value, force); err != nil {
|
||||
secret.Debug("Calling vault.AddSecret", "secret_name", secretName, "value_length", valueBuffer.Size(), "force", force)
|
||||
if err := vlt.AddSecret(secretName, valueBuffer, force); err != nil {
|
||||
secret.Debug("vault.AddSecret failed", "error", err)
|
||||
|
||||
return err
|
||||
@@ -298,14 +370,77 @@ func (cli *Instance) ImportSecret(cmd *cobra.Command, secretName, sourceFile str
|
||||
return err
|
||||
}
|
||||
|
||||
// Read secret value from the source file
|
||||
value, err := afero.ReadFile(cli.fs, sourceFile)
|
||||
// Read secret value from the source file into protected buffers
|
||||
file, err := cli.fs.Open(sourceFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read secret from file %s: %w", sourceFile, err)
|
||||
return fmt.Errorf("failed to open file %s: %w", sourceFile, err)
|
||||
}
|
||||
defer func() {
|
||||
if err := file.Close(); err != nil {
|
||||
secret.Debug("Failed to close file", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
const initialSize = 4 * 1024 // 4KB initial buffer
|
||||
const maxSize = 100 * 1024 * 1024 // 100MB max
|
||||
|
||||
type bufferInfo struct {
|
||||
buffer *memguard.LockedBuffer
|
||||
used int
|
||||
}
|
||||
|
||||
var buffers []bufferInfo
|
||||
defer func() {
|
||||
for _, b := range buffers {
|
||||
b.buffer.Destroy()
|
||||
}
|
||||
}()
|
||||
|
||||
totalSize := 0
|
||||
currentBufferSize := initialSize
|
||||
sameSize := 0
|
||||
|
||||
for {
|
||||
// Create a new buffer
|
||||
buffer := memguard.NewBuffer(currentBufferSize)
|
||||
n, err := io.ReadFull(file, buffer.Bytes())
|
||||
|
||||
if n == 0 {
|
||||
// No data read, destroy the unused buffer
|
||||
buffer.Destroy()
|
||||
} else {
|
||||
buffers = append(buffers, bufferInfo{buffer: buffer, used: n})
|
||||
totalSize += n
|
||||
|
||||
if totalSize > maxSize {
|
||||
return fmt.Errorf("secret file too large: exceeds 100MB limit")
|
||||
}
|
||||
|
||||
// If we filled the buffer, consider growing for next iteration
|
||||
if n == currentBufferSize {
|
||||
currentBufferSize = updateBufferSize(currentBufferSize, &sameSize)
|
||||
}
|
||||
}
|
||||
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to read secret from file %s: %w", sourceFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Combine all buffers into a single protected buffer
|
||||
valueBuffer := memguard.NewBuffer(totalSize)
|
||||
defer valueBuffer.Destroy()
|
||||
|
||||
offset := 0
|
||||
for _, b := range buffers {
|
||||
copy(valueBuffer.Bytes()[offset:], b.buffer.Bytes()[:b.used])
|
||||
offset += b.used
|
||||
}
|
||||
|
||||
// Store the secret in the vault
|
||||
if err := vlt.AddSecret(secretName, value, force); err != nil {
|
||||
if err := vlt.AddSecret(secretName, valueBuffer, force); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user