github.com/opendevstack/tailor@v1.3.5-0.20220119161809-cab064e60a67/pkg/commands/secrets.go (about)

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"regexp"
     8  	"strings"
     9  
    10  	"github.com/opendevstack/tailor/pkg/cli"
    11  	"github.com/opendevstack/tailor/pkg/openshift"
    12  	"github.com/opendevstack/tailor/pkg/utils"
    13  )
    14  
    15  // GenerateKey generates a GPG key using specified email (and optionally name).
    16  func GenerateKey(secretsOptions *cli.SecretsOptions, email, name string) error {
    17  	emailParts := strings.Split(email, "@")
    18  	if len(name) == 0 {
    19  		name = emailParts[0]
    20  	}
    21  	entity, err := utils.CreateEntity(name, email)
    22  	if err != nil {
    23  		return fmt.Errorf("Failed to generate keypair: %s", err)
    24  	}
    25  	publicKeyFilename := strings.Replace(emailParts[0], ".", "-", -1) + ".key"
    26  	if _, err := os.Stat(publicKeyFilename); err == nil {
    27  		return fmt.Errorf("'%s' already exists", publicKeyFilename)
    28  	}
    29  	err = utils.PrintPublicKey(entity, publicKeyFilename)
    30  	if err != nil {
    31  		return err
    32  	}
    33  	fmt.Printf("Public Key written to %s. This file can be committed.\n", publicKeyFilename)
    34  	privateKeyFilename := secretsOptions.PrivateKey
    35  	if _, err := os.Stat(privateKeyFilename); err == nil {
    36  		return fmt.Errorf("'%s' already exists", privateKeyFilename)
    37  	}
    38  	err = utils.PrintPrivateKey(entity, privateKeyFilename)
    39  	if err != nil {
    40  		return err
    41  	}
    42  	fmt.Printf("Private Key written to %s. This file MUST NOT be committed.\n", privateKeyFilename)
    43  	return nil
    44  }
    45  
    46  // Reveal prints the clear-text of an encrypted file to STDOUT.
    47  func Reveal(secretsOptions *cli.SecretsOptions, filename string) error {
    48  	if _, err := os.Stat(filename); os.IsNotExist(err) {
    49  		return fmt.Errorf("'%s' does not exist", filename)
    50  	}
    51  	encryptedContent, err := utils.ReadFile(filename)
    52  	if err != nil {
    53  		return fmt.Errorf("Could not read file: %s", err)
    54  	}
    55  	decryptedContent, err := openshift.DecryptedParams(
    56  		encryptedContent,
    57  		secretsOptions.PrivateKey,
    58  		secretsOptions.Passphrase,
    59  	)
    60  	if err != nil {
    61  		return fmt.Errorf("Could not decrypt file: %s", err)
    62  	}
    63  	fmt.Println(decryptedContent)
    64  	return nil
    65  }
    66  
    67  // ReEncrypt decrypts given file(s) and encrypts all params again.
    68  // This allows to share the secrets with a new keypair.
    69  func ReEncrypt(secretsOptions *cli.SecretsOptions, filename string) error {
    70  	if len(filename) > 0 {
    71  		err := reEncrypt(filename, secretsOptions.PrivateKey, secretsOptions.Passphrase, secretsOptions.PublicKeyDir)
    72  		if err != nil {
    73  			return err
    74  		}
    75  	} else {
    76  		paramDir := secretsOptions.ParamDir
    77  		files, err := ioutil.ReadDir(paramDir)
    78  		if err != nil {
    79  			return err
    80  		}
    81  		filePattern := ".*\\.env.enc$"
    82  		re := regexp.MustCompile(filePattern)
    83  		for _, file := range files {
    84  			matched := re.MatchString(file.Name())
    85  			if !matched {
    86  				continue
    87  			}
    88  			filename := paramDir + string(os.PathSeparator) + file.Name()
    89  			err := reEncrypt(filename, secretsOptions.PrivateKey, secretsOptions.Passphrase, secretsOptions.PublicKeyDir)
    90  			if err != nil {
    91  				return err
    92  			}
    93  		}
    94  	}
    95  	return nil
    96  }
    97  
    98  // Edit opens given filen in cleartext in $EDITOR, then encrypts the content on save.
    99  func Edit(secretsOptions *cli.SecretsOptions, filename string) error {
   100  	encryptedContent, err := utils.ReadFile(filename)
   101  	if err != nil {
   102  		if os.IsNotExist(err) {
   103  			cli.DebugMsg(filename, "does not exist, creating empty file")
   104  		} else {
   105  			return fmt.Errorf("Could not read file: %s", err)
   106  		}
   107  	}
   108  
   109  	cleartextContent, err := openshift.DecryptedParams(
   110  		encryptedContent,
   111  		secretsOptions.PrivateKey,
   112  		secretsOptions.Passphrase,
   113  	)
   114  	if err != nil {
   115  		return fmt.Errorf("Could not decrypt file: %s", err)
   116  	}
   117  
   118  	editedContent, err := cli.EditEnvFile(cleartextContent)
   119  	if err != nil {
   120  		return fmt.Errorf("Could not edit file: %s", err)
   121  	}
   122  
   123  	err = writeEncryptedContent(
   124  		filename,
   125  		editedContent,
   126  		encryptedContent,
   127  		secretsOptions.PrivateKey,
   128  		secretsOptions.Passphrase,
   129  		secretsOptions.PublicKeyDir,
   130  	)
   131  	if err != nil {
   132  		return fmt.Errorf("Could not write file: %s", err)
   133  	}
   134  	return nil
   135  }
   136  
   137  func reEncrypt(filename, privateKey, passphrase, publicKeyDir string) error {
   138  	encryptedContent, err := utils.ReadFile(filename)
   139  	if err != nil {
   140  		return fmt.Errorf("Could not read file: %s", err)
   141  	}
   142  
   143  	cleartextContent, err := openshift.DecryptedParams(
   144  		encryptedContent,
   145  		privateKey,
   146  		passphrase,
   147  	)
   148  	if err != nil {
   149  		return fmt.Errorf("Could not decrypt file: %s", err)
   150  	}
   151  
   152  	return writeEncryptedContent(
   153  		filename,
   154  		cleartextContent,
   155  		"", // empty because all values should be re-encrypted
   156  		privateKey,
   157  		passphrase,
   158  		publicKeyDir,
   159  	)
   160  }
   161  
   162  func writeEncryptedContent(filename, newContent, previousContent, privateKey, passphrase, publicKeyDir string) error {
   163  	updatedContent, err := openshift.EncryptedParams(
   164  		newContent,
   165  		previousContent,
   166  		publicKeyDir,
   167  		privateKey,
   168  		passphrase,
   169  	)
   170  	if err != nil {
   171  		return fmt.Errorf("Could not encrypt content: %s", err)
   172  	}
   173  
   174  	err = ioutil.WriteFile(filename, []byte(updatedContent), 0644)
   175  	if err != nil {
   176  		return fmt.Errorf("Could not write file: %s", err)
   177  	}
   178  	return nil
   179  }