diff --git a/.nox.yaml b/.nox.yaml index d8bb5c7..2730554 100644 --- a/.nox.yaml +++ b/.nox.yaml @@ -2,7 +2,7 @@ interval: "10m" age: identity: "keys/key.txt" recipients: - - "age1nuxu3q9wr5wrd53dj8hj5flhz86q2dpjyuq7agseh0wzwq5t696s2dm0ht" + - "age1qq5sazxv755u2vs5ulyl486jxhlg7ztrvm27nya47aln668xldkqsm4kn5" statePath: ".nox-state.json" defaultRepo: git@github.com:ShorkBytes/nox-secrets.git diff --git a/cmd/nox/main.go b/cmd/nox/main.go index 919ec4c..32d4a8b 100644 --- a/cmd/nox/main.go +++ b/cmd/nox/main.go @@ -36,6 +36,7 @@ func main() { Flags: []cli.Flag{ &cli.StringFlag{ Name: "config", + Aliases: []string{"c"}, Value: constants.DefaultConfigPath, Usage: "path to config file", Destination: &configPath, @@ -98,30 +99,28 @@ func main() { }, }, Action: func(ctx context.Context, cmd *cli.Command) error { - fmt.Println("encrypting file", inputPath) - fmt.Println("writing to", outputPath) recipients, err := crypto.StringsToRecipients(cmd.StringSlice("recipient")) if err != nil { return err } - out, err := crypto.EncryptFile(inputPath, recipients) - if err != nil { + if err := processor.IOWrapper(inputPath, outputPath, recipients, crypto.EncryptBytes); err != nil { return err } - fmt.Println(string(out)) - // priv, pub, err := crypto.GenerateAndWriteX25519Identity("test.key") - // if err != nil { - // return err - // } - // fmt.Println(priv) - // fmt.Println(pub) - return nil }, }, { - Name: "generate", + Name: "decrypt", + Aliases: []string{"dec"}, + Usage: "Decrypt a file", Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "input", + Usage: "path to input file", + Aliases: []string{"i"}, + Value: constants.StandardInput, + Destination: &inputPath, + }, &cli.StringFlag{ Name: "output", Usage: "path to output file", @@ -131,19 +130,48 @@ func main() { }, }, Action: func(ctx context.Context, cmd *cli.Command) error { - priv, pub, err := crypto.GenerateIdentity(cmd.String("output")) + identities, err := crypto.LoadAgeIdentitiesFromPaths(identityPaths) if err != nil { return err } - fmt.Println(priv) - fmt.Println(pub) + return processor.IOWrapper(inputPath, constants.StandardOutput, identities, crypto.DecryptBytes) + }, + }, + { + Name: "generate", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "output", + Usage: "path to output file", + Aliases: []string{"o"}, + Value: constants.StandardOutput, + Destination: &outputPath, + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + + output := cmd.String("output") + switch output { + case constants.StandardOutput: + priv, pub, err := crypto.GenerateIdentity("") + if err != nil { + return err + } + fmt.Println("Public key:\n", pub, "\nPrivate Key:\n", priv) + default: + _, _, err := crypto.GenerateIdentity(cmd.String("output")) + if err != nil { + return err + } + } + return nil }, }, { - Name: "decrypt", - Aliases: []string{"d"}, - Usage: "Decrypts all secrets of one or all apps", + Name: "sync", + Aliases: []string{"fetch"}, + Usage: "Fetches and decrypts all secrets of one or all apps", Flags: []cli.Flag{ &cli.StringFlag{ Name: "app", diff --git a/internal/crypto/encrypt.go b/internal/crypto/encrypt.go index 5584de1..b0721ed 100644 --- a/internal/crypto/encrypt.go +++ b/internal/crypto/encrypt.go @@ -29,7 +29,7 @@ func EncryptBytes(data []byte, recipients []age.Recipient) ([]byte, error) { } if _, err := io.Copy(enc, src); err != nil { - enc.Close() // ensure resources are freed + enc.Close() return nil, fmt.Errorf("encryption failed: %w", err) } diff --git a/internal/crypto/identity.go b/internal/crypto/identity.go index 15241be..92caae3 100644 --- a/internal/crypto/identity.go +++ b/internal/crypto/identity.go @@ -47,6 +47,10 @@ func GenerateIdentity(path string) (priv string, pub string, err error) { priv = id.String() // "AGE-SECRET-KEY-1..." pub = id.Recipient().String() // "age1..." + if path == "" { + return priv, pub, nil + } + // ensure directory exists and apply permissions if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { return "", "", fmt.Errorf("mkdir %s: %w", filepath.Dir(path), err) diff --git a/internal/processor/file.go b/internal/processor/fsutil.go similarity index 50% rename from internal/processor/file.go rename to internal/processor/fsutil.go index 1be792a..7690e61 100644 --- a/internal/processor/file.go +++ b/internal/processor/fsutil.go @@ -2,10 +2,12 @@ package processor import ( "fmt" + "io" "os" "path/filepath" "github.com/aottr/nox/internal/config" + "github.com/aottr/nox/internal/constants" ) type FileProcessorOptions struct { @@ -33,3 +35,37 @@ func WriteToFile(data []byte, file config.FileConfig, opts *FileProcessorOptions } return nil } + +// IOWrapper is a generic wrapper for reading/writing files/STDIN/STDOUT +func IOWrapper[T any](input, output string, additional T, process func([]byte, T) ([]byte, error)) error { + var inputBytes []byte + var err error + + if input == constants.StandardInput { + inputBytes, err = io.ReadAll(os.Stdin) + if err != nil { + return err + } + } else { + inputBytes, err = os.ReadFile(input) + if err != nil { + return err + } + } + + outbutBytes, err := process(inputBytes, additional) + if err != nil { + return err + } + + if output == constants.StandardOutput { + if _, err = os.Stdout.Write(outbutBytes); err != nil { + return err + } + } else { + if err = os.WriteFile(output, outbutBytes, 0600); err != nil { + return err + } + } + return nil +}