Skip to content

Commit

Permalink
fix: encrypt certificate private key, read more than first line on en…
Browse files Browse the repository at this point in the history
…crypt command (#43)
  • Loading branch information
andrew-s authored Jan 30, 2025
1 parent da9434c commit 90df10f
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 6 deletions.
12 changes: 9 additions & 3 deletions commands/secrets/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func NewCreateCmd(printer *utils.Printer) *cobra.Command {
Use: "create",
Aliases: []string{"new"},
Short: "Create a new secret",
Long: `Create a new secret in your Qernal project.
Long: `Create a new secret in your Qernal project.
This command supports creating registry, environment, and certificate secrets.
The secret value is read from stdin, allowing for secure input methods.`,
Example: ` # Create a registry secret
Expand Down Expand Up @@ -111,8 +111,8 @@ The secret value is read from stdin, allowing for secure input methods.`,
encryptedValue, err := client.EncryptLocalSecret(dek.Payload.SecretMetaResponseDek.Public, plaintext)
if err != nil {
return charm.RenderError("unable to encrypt input", err)

}

encryptionRef := fmt.Sprintf(`keys/dek/%d`, dek.Revision)
_, _, err = qc.SecretsAPI.ProjectsSecretsCreate(ctx, projectID).SecretBody(openapi_chaos_client.SecretBody{
Name: strings.ToUpper(secretName),
Expand Down Expand Up @@ -143,6 +143,12 @@ The secret value is read from stdin, allowing for secure input methods.`,
return charm.RenderError("Unable to read private key file", err)
}

// encrypt private key
privateKeyEncrypted, err := client.EncryptLocalSecret(dek.Payload.SecretMetaResponseDek.Public, strings.TrimSpace(string(privateKeyContent)))
if err != nil {
return charm.RenderError("unable to private key", err)
}

encryptionRef := fmt.Sprintf(`keys/dek/%d`, dek.Revision)
_, _, err = qc.SecretsAPI.ProjectsSecretsCreate(ctx, projectID).SecretBody(openapi_chaos_client.SecretBody{
Name: strings.ToUpper(secretName),
Expand All @@ -151,7 +157,7 @@ The secret value is read from stdin, allowing for secure input methods.`,
Payload: openapi_chaos_client.SecretCreatePayload{
SecretCertificate: &openapi_chaos_client.SecretCertificate{
Certificate: strings.TrimSpace(string(publicKeyContent)),
CertificateValue: strings.TrimSpace(string(privateKeyContent)),
CertificateValue: privateKeyEncrypted,
},
},
}).Execute()
Expand Down
21 changes: 18 additions & 3 deletions commands/secrets/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package secrets
import (
"bufio"
"context"
"io"
"strings"

"github.com/qernal/cli-qernal/charm"
Expand All @@ -29,10 +30,24 @@ func NewEncryptCmd(printer *utils.Printer) *cobra.Command {

// Read from stdin
reader := bufio.NewReader(cmd.InOrStdin())
plaintext, err := reader.ReadString('\n')
if err != nil {
return charm.RenderError("Error reading input from stdin:", err)
var sb strings.Builder
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == bufio.ErrBufferFull {
sb.WriteString(line)
continue
} else if err == io.EOF {
sb.WriteString(line)
break
} else {
return charm.RenderError("Error reading input from stdin:", err)
}
}
sb.WriteString(line)
}
plaintext := sb.String()

// Remove trailing newline from input
plaintext = strings.TrimSpace(plaintext)
ctx := context.Background()
Expand Down
58 changes: 58 additions & 0 deletions commands/secrets/encrypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package secrets
import (
"bytes"
"encoding/json"
"os"
"strings"
"testing"

"github.com/qernal/cli-qernal/pkg/helpers"
Expand Down Expand Up @@ -58,3 +60,59 @@ func TestEncryptCmd(t *testing.T) {
helpers.DeleteOrg(orgID)
})
}

func TestEncryptCmdWithFile(t *testing.T) {
filePath := "/tmp/" + utils.GenerateRandomString(6) + ".txt"
randomStrings := make([]string, 10)
for i := range randomStrings {
randomStrings[i] = utils.GenerateRandomString(60)
}
fileContent := []byte(strings.Join(randomStrings, "\n"))

err := os.WriteFile(filePath, fileContent, 0644)
require.NoError(t, err)

outputPrinter := utils.NewPrinter()

orgID, _, err := helpers.CreateOrg()
if err != nil {
t.Fatalf("failed to create organization: %v", err)
}
projectID, _, err := helpers.CreateProj(orgID)
if err != nil {
t.Fatalf("failed to create project: %v", err)
}

commandArgs := []string{"--project", projectID, "--output", "json"}

// Set stdout to controlled buffer
var outputBuffer bytes.Buffer
outputPrinter.SetOut(&outputBuffer)

var encryptionOutput struct {
RevisionID int32 `json:"revision_id"`
EncryptedValue string `json:"encrypted_value"`
}

inputBuffer, err := os.ReadFile(filePath)
require.NoError(t, err)

encryptCmd := NewEncryptCmd(outputPrinter)
encryptCmd.SetArgs(commandArgs)
encryptCmd.SetIn(bytes.NewReader(inputBuffer))

err = encryptCmd.Execute()
require.NoError(t, err)

err = json.Unmarshal(outputBuffer.Bytes(), &encryptionOutput)
require.NoError(t, err)

println(len(encryptionOutput.EncryptedValue))

assert.Greater(t, len(encryptionOutput.EncryptedValue), 200)

t.Cleanup(func() {
helpers.DeleteOrg(orgID)
os.Remove(filePath)
})
}
1 change: 1 addition & 0 deletions pkg/helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ func GetDefaultHost(projid string) (string, error) {
return "", errors.New("no default host on project")
}

// specific to the secret type
func RandomSecretName() string {
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, 8)
Expand Down
11 changes: 11 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"log/slog"
math_rand "math/rand"
"os"
"strings"

Expand Down Expand Up @@ -145,3 +146,13 @@ func (p *Printer) PrintResource(data string) {
}
fmt.Fprintln(out, data)
}

// generate random strings of a given length, for testing
func GenerateRandomString(length int) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
for i := range b {
b[i] = charset[math_rand.Intn(len(charset))]
}
return string(b)
}

0 comments on commit 90df10f

Please sign in to comment.