Skip to content

Commit

Permalink
Encypt using pbkdf2 algo
Browse files Browse the repository at this point in the history
  • Loading branch information
sharathrnair87 committed Jun 1, 2024
1 parent f5abf0a commit 5fccd11
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 18 deletions.
7 changes: 6 additions & 1 deletion cmd/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import (
)

func Compare() {
passphrase := os.Getenv("PASSPHRASE")
if passphrase == "" {
fmt.Println("❌ Error: \033[33mPASSPHRASE\033[0m enironment variable not set.")
os.Exit(1)
}
if _, err := os.Stat("terraform.tfstate"); os.IsNotExist(err) {
fmt.Println("❌ Error: \033[33mLocal terraform.tfstate\033[0m file not found.")
return
Expand All @@ -26,7 +31,7 @@ func Compare() {
fmt.Println("Pulling state file from \033[33mVault\033[0m...")
Pull()

if err := unwrapAndSaveAs("terraform_remote_compare_pull.tfstate"); err != nil {
if err := unwrapAndSaveAs("terraform_remote_compare_pull.tfstate", passphrase); err != nil {
fmt.Println("❌ Error:", err)
os.Exit(1)
}
Expand Down
100 changes: 100 additions & 0 deletions cmd/crypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package cmd

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"golang.org/x/crypto/pbkdf2"
"io"
"os"
"strings"
)

func deriveKey(passphrase string, salt []byte) ([]byte, []byte, error) {
if salt == nil {
salt = make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
return nil, nil, fmt.Errorf("%w", err)
}
}
key := pbkdf2.Key([]byte(passphrase), salt, 4096, 32, sha256.New)
return key, salt, nil
}

func encryptContents(filestream []byte, passphrase string) ([]byte, error) {
key, salt, err := deriveKey(passphrase, nil)
if err != nil {
return nil, fmt.Errorf("❌ Error generating salt: %w", err)
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("❌ Error creating cipher: %w", err)
}

aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("❌ Error creating GCM: %w", err)
}

nonce := make([]byte, aesgcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, fmt.Errorf("❌ Error generating nonce: %w", err)
}

ciphertext := aesgcm.Seal(nonce, nonce, filestream, nil)

if ciphertext == nil {
return nil, fmt.Errorf("❌ Error creating output file: %w", err)
}

encryptStream := fmt.Sprintf("%s-%s", hex.EncodeToString(salt), hex.EncodeToString(ciphertext))

return []byte(encryptStream), nil
}

func decryptFile(filename string, passphrase string) (string, error) {
outfileName := strings.TrimSuffix(filename, ".enc")
data, err := os.ReadFile(filename)
if err != nil {
return "", fmt.Errorf("❌ Error reading file: %w", err)
}

parts := strings.Split(string(data), "-")
if len(parts) != 2 {
return "", fmt.Errorf("❌ Error invalid ciphertext format")
}

salt, _ := hex.DecodeString(parts[0])
ciphertext, _ := hex.DecodeString(parts[1])

key, _, err := deriveKey(passphrase, salt)
if err != nil {
return "", fmt.Errorf("❌ Error parsing salt: %w", err)
}
block, err := aes.NewCipher(key)
if err != nil {
return "", fmt.Errorf("❌ Error creating cipher: %w", err)
}

aesgcm, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("❌ Error creating GCM: %w", err)
}

nonceSize := aesgcm.NonceSize()
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", fmt.Errorf("❌ Error decrypting: %w", err)
}

err = os.WriteFile(outfileName, plaintext, 0644)
if err != nil {
return "", fmt.Errorf("❌ Error writing decrypted file: %w", err)
}

return "", nil
}
10 changes: 5 additions & 5 deletions cmd/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ func pullFromVault() {
fmt.Println("❌ Error: Specific \033[33mkey\033[0m not found in the data")
}

targetFilePath := "terraform.tfstate.gz.b64"
targetFilePath := "terraform.tfstate.gz.enc.b64"
if _, err := os.Stat(targetFilePath); err == nil {
fmt.Println("❌ Error: File \033[33mterraform.tfstate.gz.b64\033[0m already exists in the directory.")
fmt.Println("❌ Error: File \033[33mterraform.tfstate.gz.enc.b64\033[0m already exists in the directory.")
return
} else if !os.IsNotExist(err) {
fmt.Printf("❌ Error checking if file exists: %v\n", err)
Expand All @@ -138,7 +138,7 @@ func pullFromVault() {
return
}

fmt.Println("✅ Secret retrieved and saved as \033[33mterraform.tfstate.gz.b64\033[0m")
fmt.Println("✅ Secret retrieved and saved as \033[33mterraform.tfstate.gz.enc.b64\033[0m")
}

func pullBlobFromAzureStorage(accountName, key string) error {
Expand Down Expand Up @@ -187,7 +187,7 @@ func pullBlobFromAzureStorage(accountName, key string) error {
return fmt.Errorf("❌ Failed to download blob, status code: \033[33m%d\033[0m, response: \033[33m%s\033[0m", resp.StatusCode, string(responseBody))
}

outputFile, err := os.Create("terraform.tfstate.gz.b64")
outputFile, err := os.Create("terraform.tfstate.gz.enc.b64")
if err != nil {
return fmt.Errorf("❌ Error creating file to save downloaded blob: \033[33m%v\033[0m", err)
}
Expand All @@ -198,7 +198,7 @@ func pullBlobFromAzureStorage(accountName, key string) error {
return fmt.Errorf("❌ Error writing downloaded blob to file: \033[33m%v\033[0m", err)
}

fmt.Println("✅ Blob downloaded successfully and saved as \033[33mterraform.tfstate.gz.b64\033[0m")
fmt.Println("✅ Blob downloaded successfully and saved as \033[33mterraform.tfstate.gz.enc.b64\033[0m")
return nil
}

Expand Down
30 changes: 20 additions & 10 deletions cmd/unwrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ import (
)

func Unwrap() {
if _, err := os.Stat("terraform.tfstate.gz.b64"); os.IsNotExist(err) {
fmt.Println("❌ Error: \033[33mterraform.tfstate.gz.b64\033[0m file not found in the current directory.")
passphrase := os.Getenv("PASSPHRASE")
if passphrase == "" {
fmt.Println("❌ Error: \033[33mPASSPHRASE\033[0m enironment variable not set.")
os.Exit(1)
}
if _, err := os.Stat("terraform.tfstate.gz.enc.b64"); os.IsNotExist(err) {
fmt.Println("❌ Error: \033[33mterraform.tfstate.gz.enc.b64\033[0m file not found in the current directory.")
fmt.Println("⚠️ Please run vaultify pull to get this file from your vault, if it exists.")
os.Exit(1)
}
Expand All @@ -28,7 +33,7 @@ func Unwrap() {
return
} else if response == "rename" {
fmt.Println("Saving as terraform_remote_pull.tfstate instead.")
if err := unwrapAndSaveAs("terraform_remote_pull.tfstate"); err != nil {
if err := unwrapAndSaveAs("terraform_remote_pull.tfstate", passphrase); err != nil {
fmt.Println("❌ Error:", err)
os.Exit(1)
}
Expand All @@ -39,29 +44,34 @@ func Unwrap() {
}
}

if err := unwrapAndSaveAs("terraform.tfstate"); err != nil {
if err := unwrapAndSaveAs("terraform.tfstate", passphrase); err != nil {
fmt.Println("❌ Error:", err)
os.Exit(1)
}
}

func unwrapAndSaveAs(outputFileName string) error {
err := decodeBase64("terraform.tfstate.gz.b64", "terraform.tfstate.gz")
func unwrapAndSaveAs(outputFileName string, passphrase string) error {
err := decodeBase64("terraform.tfstate.gz.enc.b64", "terraform.tfstate.gz.enc")
if err != nil {
return fmt.Errorf("base64 decoding failed: %w", err)
}
// decrypt
_, err = decryptFile("terraform.tfstate.gz.enc", passphrase)
if err != nil {
return fmt.Errorf("%w", err)
}

err = gunzipFile("terraform.tfstate.gz", outputFileName)
if err != nil {
return fmt.Errorf("decompression failed: %w", err)
}

if err = os.Remove("terraform.tfstate.gz"); err != nil {
return fmt.Errorf("failed to delete terraform.tfstate.gz: %w", err)
if err = os.Remove("terraform.tfstate.gz.enc"); err != nil {
return fmt.Errorf("failed to delete terraform.tfstate.gz.enc: %w", err)
}

if err = os.Remove("terraform.tfstate.gz.b64"); err != nil {
return fmt.Errorf("failed to delete terraform.tfstate.gz.b64: %w", err)
if err = os.Remove("terraform.tfstate.gz.enc.b64"); err != nil {
return fmt.Errorf("failed to delete terraform.tfstate.gz.enc.b64: %w", err)
}

fmt.Printf("✅ Unwrapped state file saved as \033[33m%s\033[0m\n", outputFileName)
Expand Down
14 changes: 13 additions & 1 deletion cmd/wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@ import (
"path/filepath"
)

var encryptedStateFile string
var encodedStateFile string

func Wrap() {
passphrase := os.Getenv("PASSPHRASE")
if passphrase == "" {
fmt.Println("❌ Error: \033[33mPASSPHRASE\033[0m enironment variable not set.")
os.Exit(1)
}
files, err := filepath.Glob("*.tfstate")
if err != nil {
fmt.Println("❌ Error searching for .tfstate files:", err)
Expand All @@ -50,7 +56,13 @@ func Wrap() {
os.Exit(1)
}

encodedStateFile = base64.StdEncoding.EncodeToString(compressedStateFile)
encryptedFile, err := encryptContents(compressedStateFile, passphrase)
if err != nil {
fmt.Println("❌ Error encrypting contents of compressed state file:", err)
os.Exit(1)
}

encodedStateFile = base64.StdEncoding.EncodeToString(encryptedFile)

// Set the environment variable
os.Setenv("TERRAFORM_STATE_BASE64", encodedStateFile)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21.4
require (
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
github.com/hashicorp/vault/api v1.13.0
golang.org/x/crypto v0.19.0
)

require (
Expand Down Expand Up @@ -32,7 +33,6 @@ require (
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
Expand Down

0 comments on commit 5fccd11

Please sign in to comment.