-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Marco Antonio Blanco <[email protected]>
- Loading branch information
1 parent
d920d8a
commit 9623e63
Showing
9 changed files
with
296 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package cli | ||
|
||
import ( | ||
"encoding/base64" | ||
"fmt" | ||
|
||
"github.com/nearform/initium-cli/src/services/secrets" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
func (c icli) generateKeys(ctx *cli.Context) error { | ||
keys, err := secrets.GenerateKeys() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Fprintf(c.Writer, "Secret key: %q\n", keys.Private) | ||
fmt.Fprintf(c.Writer, "Public key: %q\n", keys.Public) | ||
return nil | ||
} | ||
|
||
func (c icli) encrypt(ctx *cli.Context) error { | ||
publicKey := ctx.String(publicKeyFlag) | ||
secret := ctx.String(plainSecretFlag) | ||
base64Secret := ctx.String(base64PlainSecretFlag) | ||
|
||
if base64Secret == "" { | ||
base64Secret = base64.StdEncoding.EncodeToString([]byte(secret)) | ||
} | ||
|
||
result, err := secrets.Encrypt(publicKey, base64Secret) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Fprintf(c.Writer, "%s\n", result) | ||
return nil | ||
} | ||
|
||
func (c icli) decrypt(ctx *cli.Context) error { | ||
privateKey := ctx.String(privateKeyFlag) | ||
secret := ctx.String(base64EncryptedSecretFlag) | ||
result, err := secrets.Decrypt(privateKey, secret) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Fprintf(c.Writer, "%s\n", result) | ||
return nil | ||
} | ||
|
||
func (c icli) SecretsCMD() *cli.Command { | ||
|
||
return &cli.Command{ | ||
Name: "secrets", | ||
Usage: "A series of command to generate age keys, encrypt and decrypt secrets", | ||
Subcommands: []*cli.Command{ | ||
{ | ||
Name: "generate-keys", | ||
Usage: "Generate the public and private keys and output them on stdout", | ||
Action: c.generateKeys, | ||
Before: c.baseBeforeFunc, | ||
}, | ||
{ | ||
Name: "encrypt", | ||
Usage: "Encrypt a secret, if the secret flag is used the secret is first encoded in base64 and then encrypted", | ||
Action: c.encrypt, | ||
Flags: c.CommandFlags([]FlagsType{Encrypt}), | ||
Before: func(ctx *cli.Context) error { | ||
if err := c.loadFlagsFromConfig(ctx); err != nil { | ||
return err | ||
} | ||
|
||
ignoredFlags := []string{} | ||
|
||
if ctx.IsSet(plainSecretFlag) { | ||
ignoredFlags = append(ignoredFlags, base64PlainSecretFlag) | ||
} | ||
if ctx.IsSet(base64PlainSecretFlag) { | ||
ignoredFlags = append(ignoredFlags, plainSecretFlag) | ||
} | ||
|
||
return c.checkRequiredFlags(ctx, ignoredFlags) | ||
}, | ||
}, | ||
{ | ||
Name: "decrypt", | ||
Usage: "Decrypt a base64 encoded secret and output the base64 encoded value", | ||
Action: c.decrypt, | ||
Flags: c.CommandFlags([]FlagsType{Decrypt}), | ||
Before: c.baseBeforeFunc, | ||
}, | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package secrets | ||
|
||
import ( | ||
"bytes" | ||
"encoding/base64" | ||
"fmt" | ||
"io" | ||
"strings" | ||
|
||
"filippo.io/age" | ||
) | ||
|
||
type Keys struct { | ||
Private string | ||
Public string | ||
} | ||
|
||
func GenerateKeys() (Keys, error) { | ||
identity, err := age.GenerateX25519Identity() | ||
if err != nil { | ||
return Keys{}, err | ||
} | ||
|
||
keys := Keys{ | ||
Private: identity.String(), | ||
Public: identity.Recipient().String(), | ||
} | ||
return keys, nil | ||
} | ||
|
||
func Decrypt(privateKey string, secret string) (string, error) { | ||
identity, err := age.ParseX25519Identity(privateKey) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to parse private key %q: %v", privateKey, err) | ||
} | ||
|
||
s, err := base64.StdEncoding.DecodeString(secret) | ||
if err != nil { | ||
return "", fmt.Errorf("cannot decode base64 secret %v", err) | ||
} | ||
out := &bytes.Buffer{} | ||
f := strings.NewReader(string(s)) | ||
|
||
r, err := age.Decrypt(f, identity) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to open encrypted file: %v", err) | ||
} | ||
if _, err := io.Copy(out, r); err != nil { | ||
return "", fmt.Errorf("failed to read encrypted file: %v", err) | ||
} | ||
|
||
return out.String(), nil | ||
} | ||
|
||
func Encrypt(publicKey string, secret string) (string, error) { | ||
recipient, err := age.ParseX25519Recipient(publicKey) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to parse public key %q: %v", publicKey, err) | ||
} | ||
|
||
buf := &bytes.Buffer{} | ||
// armorWriter := armor.NewWriter(buf) | ||
|
||
w, err := age.Encrypt(buf, recipient) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to create encrypted file: %v", err) | ||
} | ||
defer w.Close() | ||
|
||
if _, err := io.WriteString(w, secret); err != nil { | ||
return "", fmt.Errorf("failed to write to encrypted file: %v", err) | ||
} | ||
if err := w.Close(); err != nil { | ||
return "", fmt.Errorf("failed to close encrypted file: %v", err) | ||
} | ||
|
||
return base64.StdEncoding.EncodeToString(buf.Bytes()), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package secrets | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"gotest.tools/v3/assert" | ||
) | ||
|
||
func TestGenerateKeys(t *testing.T) { | ||
keys, err := GenerateKeys() | ||
secretKeyPrefix := "AGE-SECRET-KEY-" | ||
publicKeyPrefix := "age1" | ||
|
||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
if !strings.HasPrefix(keys.Private, secretKeyPrefix) { | ||
t.Errorf("Secret key doesn't start with %s", secretKeyPrefix) | ||
} | ||
|
||
if !strings.HasPrefix(keys.Public, publicKeyPrefix) { | ||
t.Errorf("Public key doesn't start with %s", publicKeyPrefix) | ||
} | ||
} | ||
|
||
func TestDecrypt(t *testing.T) { | ||
keys, err := GenerateKeys() | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
expected := "simple" | ||
|
||
secret, err := Encrypt(keys.Public, expected) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
plain, err := Decrypt(keys.Private, secret) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
assert.Assert(t, plain == expected, "Expected %q, got %q", expected, plain) | ||
} |