Skip to content

Commit 1f5952d

Browse files
committed
Make key for seal/unseal positional argument
* No current users so not a breaking change. * Easier UX with less typing required. Signed-off-by: Alex Ellis (OpenFaaS Ltd) <[email protected]>
1 parent 4889ec1 commit 1f5952d

File tree

4 files changed

+189
-28
lines changed

4 files changed

+189
-28
lines changed

commands/secret_seal.go

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,38 @@ import (
1010
)
1111

1212
var (
13-
sealPublicKeyPath string
14-
sealKeyID string
15-
sealOutput string
16-
sealFromLiteral []string
17-
sealFromFile []string
13+
sealKeyID string
14+
sealOutput string
15+
sealFromLiteral []string
16+
sealFromFile []string
1817
)
1918

2019
var secretSealCmd = &cobra.Command{
21-
Use: "seal",
20+
Use: "seal [public-key-file]",
2221
Short: "Seal build secrets into an encrypted file",
2322
Long: "Seal key/value pairs using a public key. The output file can be included in a build tar or committed to git.",
2423
Example: ` # Seal literal values
25-
faas-cli secret seal \
26-
--public-key ./key.pub \
24+
faas-cli secret seal key.pub \
2725
--from-literal pip_token=s3cr3t \
2826
--from-literal npm_token=tok123
2927
3028
# Seal from files (binary-safe)
31-
faas-cli secret seal \
32-
--public-key ./key.pub \
29+
faas-cli secret seal key.pub \
3330
--from-file ca.crt=./certs/ca.crt \
3431
--from-literal api_key=sk-1234
3532
3633
# Specify key ID and output path
37-
faas-cli secret seal \
38-
--public-key ./key.pub \
34+
faas-cli secret seal key.pub \
3935
--key-id builder-key-1 \
4036
--from-literal token=s3cr3t \
4137
-o ./build/com.openfaas.secrets
4238
`,
39+
Args: cobra.ExactArgs(1),
4340
RunE: runSecretSeal,
4441
PreRunE: preRunSecretSeal,
4542
}
4643

4744
func init() {
48-
secretSealCmd.Flags().StringVar(&sealPublicKeyPath, "public-key", "", "Path to the recipient's public key file")
4945
secretSealCmd.Flags().StringVar(&sealKeyID, "key-id", "", "Key ID for rotation tracking (optional)")
5046
secretSealCmd.Flags().StringVarP(&sealOutput, "output", "o", "com.openfaas.secrets", "Output file path")
5147
secretSealCmd.Flags().StringArrayVar(&sealFromLiteral, "from-literal", nil, "Literal secret in key=value format (can be repeated)")
@@ -55,10 +51,6 @@ func init() {
5551
}
5652

5753
func preRunSecretSeal(cmd *cobra.Command, args []string) error {
58-
if sealPublicKeyPath == "" {
59-
return fmt.Errorf("--public-key is required")
60-
}
61-
6254
if len(sealFromLiteral) == 0 && len(sealFromFile) == 0 {
6355
return fmt.Errorf("provide at least one secret via --from-literal or --from-file")
6456
}
@@ -67,7 +59,7 @@ func preRunSecretSeal(cmd *cobra.Command, args []string) error {
6759
}
6860

6961
func runSecretSeal(cmd *cobra.Command, args []string) error {
70-
pubKey, err := os.ReadFile(sealPublicKeyPath)
62+
pubKey, err := os.ReadFile(args[0])
7163
if err != nil {
7264
return fmt.Errorf("reading public key: %w", err)
7365
}

commands/secret_seal_test.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,12 @@ func TestSecretSealFromLiteral(t *testing.T) {
2323

2424
outPath := filepath.Join(dir, "com.openfaas.secrets")
2525

26-
sealPublicKeyPath = pubPath
2726
sealKeyID = "test-key"
2827
sealOutput = outPath
2928
sealFromLiteral = []string{"pip_token=s3cr3t", "npm_token=tok123"}
3029
sealFromFile = nil
3130

32-
if err := runSecretSeal(nil, nil); err != nil {
31+
if err := runSecretSeal(nil, []string{pubPath}); err != nil {
3332
t.Fatalf("runSecretSeal: %v", err)
3433
}
3534

@@ -73,13 +72,12 @@ func TestSecretSealFromFile(t *testing.T) {
7372

7473
outPath := filepath.Join(dir, "com.openfaas.secrets")
7574

76-
sealPublicKeyPath = pubPath
7775
sealKeyID = ""
7876
sealOutput = outPath
7977
sealFromLiteral = []string{"token=abc"}
8078
sealFromFile = []string{"ca.crt=" + certPath}
8179

82-
if err := runSecretSeal(nil, nil); err != nil {
80+
if err := runSecretSeal(nil, []string{pubPath}); err != nil {
8381
t.Fatalf("runSecretSeal: %v", err)
8482
}
8583

@@ -102,15 +100,9 @@ func TestSecretSealFromFile(t *testing.T) {
102100
}
103101

104102
func TestSecretSealPreRunValidation(t *testing.T) {
105-
sealPublicKeyPath = ""
106103
sealFromLiteral = nil
107104
sealFromFile = nil
108105

109-
if err := preRunSecretSeal(nil, nil); err == nil {
110-
t.Fatal("expected error when --public-key is missing")
111-
}
112-
113-
sealPublicKeyPath = "some.pub"
114106
if err := preRunSecretSeal(nil, nil); err == nil {
115107
t.Fatal("expected error when no secrets provided")
116108
}

commands/secret_unseal.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/openfaas/go-sdk/seal"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
var (
12+
unsealInput string
13+
unsealKey string
14+
)
15+
16+
var secretUnsealCmd = &cobra.Command{
17+
Use: "unseal [private-key-file]",
18+
Short: "Unseal and inspect a sealed secrets file",
19+
Long: "Decrypt a sealed secrets file using a private key and print the key/value pairs",
20+
Example: ` # Print all secrets
21+
faas-cli secret unseal key
22+
23+
# Print a single secret value
24+
faas-cli secret unseal key --key pip_token
25+
26+
# Specify a different sealed file
27+
faas-cli secret unseal key --in ./build/com.openfaas.secrets
28+
`,
29+
Args: cobra.ExactArgs(1),
30+
RunE: runSecretUnseal,
31+
}
32+
33+
func init() {
34+
secretUnsealCmd.Flags().StringVar(&unsealInput, "in", "com.openfaas.secrets", "Path to the sealed secrets file")
35+
secretUnsealCmd.Flags().StringVar(&unsealKey, "key", "", "Unseal a single key (omit to print all)")
36+
37+
secretCmd.AddCommand(secretUnsealCmd)
38+
}
39+
40+
func runSecretUnseal(cmd *cobra.Command, args []string) error {
41+
privKey, err := os.ReadFile(args[0])
42+
if err != nil {
43+
return fmt.Errorf("reading private key: %w", err)
44+
}
45+
46+
envelope, err := os.ReadFile(unsealInput)
47+
if err != nil {
48+
return fmt.Errorf("reading sealed file: %w", err)
49+
}
50+
51+
if unsealKey != "" {
52+
value, err := seal.UnsealKey(privKey, envelope, unsealKey)
53+
if err != nil {
54+
return err
55+
}
56+
fmt.Print(string(value))
57+
return nil
58+
}
59+
60+
values, err := seal.Unseal(privKey, envelope)
61+
if err != nil {
62+
return err
63+
}
64+
65+
for k, v := range values {
66+
fmt.Printf("%s=%s\n", k, string(v))
67+
}
68+
69+
return nil
70+
}

commands/secret_unseal_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package commands
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/openfaas/go-sdk/seal"
9+
)
10+
11+
func TestSecretUnsealAll(t *testing.T) {
12+
dir := t.TempDir()
13+
14+
pub, priv, err := seal.GenerateKeyPair()
15+
if err != nil {
16+
t.Fatalf("GenerateKeyPair: %v", err)
17+
}
18+
19+
privPath := filepath.Join(dir, "key")
20+
if err := os.WriteFile(privPath, priv, 0600); err != nil {
21+
t.Fatalf("WriteFile: %v", err)
22+
}
23+
24+
sealed, err := seal.Seal(pub, map[string][]byte{
25+
"token": []byte("s3cr3t"),
26+
"url": []byte("https://example.com"),
27+
}, "test")
28+
if err != nil {
29+
t.Fatalf("Seal: %v", err)
30+
}
31+
32+
sealedPath := filepath.Join(dir, "com.openfaas.secrets")
33+
if err := os.WriteFile(sealedPath, sealed, 0600); err != nil {
34+
t.Fatalf("WriteFile: %v", err)
35+
}
36+
37+
unsealInput = sealedPath
38+
unsealKey = ""
39+
40+
if err := runSecretUnseal(nil, []string{privPath}); err != nil {
41+
t.Fatalf("runSecretUnseal: %v", err)
42+
}
43+
}
44+
45+
func TestSecretUnsealSingleKey(t *testing.T) {
46+
dir := t.TempDir()
47+
48+
pub, priv, err := seal.GenerateKeyPair()
49+
if err != nil {
50+
t.Fatalf("GenerateKeyPair: %v", err)
51+
}
52+
53+
privPath := filepath.Join(dir, "key")
54+
if err := os.WriteFile(privPath, priv, 0600); err != nil {
55+
t.Fatalf("WriteFile: %v", err)
56+
}
57+
58+
sealed, err := seal.Seal(pub, map[string][]byte{
59+
"token": []byte("s3cr3t"),
60+
}, "")
61+
if err != nil {
62+
t.Fatalf("Seal: %v", err)
63+
}
64+
65+
sealedPath := filepath.Join(dir, "com.openfaas.secrets")
66+
if err := os.WriteFile(sealedPath, sealed, 0600); err != nil {
67+
t.Fatalf("WriteFile: %v", err)
68+
}
69+
70+
unsealInput = sealedPath
71+
unsealKey = "token"
72+
73+
if err := runSecretUnseal(nil, []string{privPath}); err != nil {
74+
t.Fatalf("runSecretUnseal: %v", err)
75+
}
76+
}
77+
78+
func TestSecretUnsealMissingKey(t *testing.T) {
79+
dir := t.TempDir()
80+
81+
pub, priv, err := seal.GenerateKeyPair()
82+
if err != nil {
83+
t.Fatalf("GenerateKeyPair: %v", err)
84+
}
85+
86+
privPath := filepath.Join(dir, "key")
87+
if err := os.WriteFile(privPath, priv, 0600); err != nil {
88+
t.Fatalf("WriteFile: %v", err)
89+
}
90+
91+
sealed, err := seal.Seal(pub, map[string][]byte{"a": []byte("b")}, "")
92+
if err != nil {
93+
t.Fatalf("Seal: %v", err)
94+
}
95+
96+
sealedPath := filepath.Join(dir, "com.openfaas.secrets")
97+
if err := os.WriteFile(sealedPath, sealed, 0600); err != nil {
98+
t.Fatalf("WriteFile: %v", err)
99+
}
100+
101+
unsealInput = sealedPath
102+
unsealKey = "nonexistent"
103+
104+
if err := runSecretUnseal(nil, []string{privPath}); err == nil {
105+
t.Fatal("expected error for missing key")
106+
}
107+
}

0 commit comments

Comments
 (0)