github.com/tommi2day/pwcli@v0.0.0-20240317203041-4d1177a5ab91/cmd/vault.go (about)

     1  package cmd
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"strings"
     9  
    10  	vault "github.com/hashicorp/vault/api"
    11  	log "github.com/sirupsen/logrus"
    12  	"github.com/spf13/cobra"
    13  	"github.com/tommi2day/gomodules/pwlib"
    14  )
    15  
    16  var vaultToken = os.Getenv("VAULT_TOKEN")
    17  var vaultAddr = os.Getenv("VAULT_ADDR")
    18  var vaultPath string
    19  var logical bool
    20  var kvMount = "secret/"
    21  var jsonOut = false
    22  var vaultCmd = &cobra.Command{
    23  	Use:   "vault",
    24  	Short: "handle vault functions",
    25  	Long:  `Allows list, read and write vault secrets`,
    26  }
    27  
    28  var vaultReadCmd = &cobra.Command{
    29  	Use:   "read",
    30  	Short: "read a vault secret",
    31  	Long: `
    32  read a secret from given path in KV2 or Logical mode
    33  list all data below path in list_password syntax or give a key as extra arg to return only this value`,
    34  	RunE:         vaultRead,
    35  	SilenceUsage: true,
    36  }
    37  var vaultListCmd = &cobra.Command{
    38  	Use:          "list",
    39  	Short:        "list secrets",
    40  	Long:         `list secrets one step below given path (without content)`,
    41  	RunE:         vaultList,
    42  	SilenceUsage: true,
    43  }
    44  
    45  var vaultWriteCmd = &cobra.Command{
    46  	Use:          "write",
    47  	Short:        "write json to vault path",
    48  	Long:         `write a secret to given path in KV2 or Logical mode with json encoded data`,
    49  	RunE:         vaultWrite,
    50  	SilenceUsage: true,
    51  	Args: func(cmd *cobra.Command, args []string) error {
    52  		f, e := cmd.Flags().GetString("data_file")
    53  		if len(args) < 1 && (f == "" || e != nil) {
    54  			return fmt.Errorf("requires data to write as second argument or 'data_file' set")
    55  		}
    56  		return nil
    57  	},
    58  }
    59  
    60  func init() {
    61  	vaultCmd.SetHelpFunc(func(command *cobra.Command, strings []string) {
    62  		// Hide flag for this command
    63  		_ = command.Flags().MarkHidden("app")
    64  		_ = command.Flags().MarkHidden("keydir")
    65  		_ = command.Flags().MarkHidden("datadir")
    66  		_ = command.Flags().MarkHidden("config")
    67  		_ = command.Flags().MarkHidden("method")
    68  		// Call parent help func
    69  		command.Parent().HelpFunc()(command, strings)
    70  	})
    71  	RootCmd.AddCommand(vaultCmd)
    72  
    73  	vaultCmd.PersistentFlags().StringVar(&vaultAddr, "vault_addr", vaultAddr, "VAULT_ADDR Url")
    74  	vaultCmd.PersistentFlags().StringVar(&vaultToken, "vault_token", vaultToken, "VAULT_TOKEN")
    75  	vaultCmd.PersistentFlags().StringVarP(&vaultPath, "path", "P", "", "Vault secret Path to Read/Write")
    76  	vaultCmd.PersistentFlags().BoolVarP(&logical, "logical", "L", false, "Use Logical Api, default is KV2")
    77  	vaultCmd.PersistentFlags().StringVarP(&kvMount, "mount", "M", kvMount, "Mount Path of the Secret engine")
    78  	_ = vaultCmd.MarkFlagRequired("path")
    79  	vaultReadCmd.Flags().BoolVarP(&jsonOut, "json", "J", false, "output as json")
    80  	vaultWriteCmd.Flags().String("data_file", "", "Path to the json encoded file with the data to read from")
    81  	vaultCmd.AddCommand(vaultReadCmd)
    82  	vaultCmd.AddCommand(vaultWriteCmd)
    83  	vaultCmd.AddCommand(vaultListCmd)
    84  }
    85  
    86  func vaultRead(_ *cobra.Command, args []string) error {
    87  	var (
    88  		err error
    89  		key string
    90  		vc  *vault.Client
    91  		vs  *vault.Secret
    92  		kvs *vault.KVSecret
    93  	)
    94  	log.Debugf("Vault Read entered for path '%s'", vaultPath)
    95  	vc, _ = pwlib.VaultConfig(vaultAddr, vaultToken)
    96  	if len(args) > 0 {
    97  		key = args[0]
    98  		log.Debugf("Selected Key '%s'", key)
    99  	}
   100  	if logical {
   101  		vs, err = pwlib.VaultRead(vc, vaultPath)
   102  		if err == nil {
   103  			if vs != nil {
   104  				log.Debug("Vault Read OK")
   105  				err = printData(vs.Data, key)
   106  			} else {
   107  				err = fmt.Errorf("no entries returned")
   108  			}
   109  		}
   110  	} else {
   111  		kvs, err = pwlib.VaultKVRead(vc, kvMount, vaultPath)
   112  		if err == nil {
   113  			if kvs != nil {
   114  				log.Debug("Vault KVRead OK")
   115  				err = printData(kvs.Data, key)
   116  			} else {
   117  				err = fmt.Errorf("no entries returned")
   118  			}
   119  		}
   120  	}
   121  	if err == nil {
   122  		log.Info("Vault Data successfully processed")
   123  	}
   124  	return err
   125  }
   126  
   127  func vaultWrite(cmd *cobra.Command, args []string) error {
   128  	var (
   129  		err       error
   130  		datafile  string
   131  		content   []byte
   132  		vaultData map[string]interface{}
   133  		vc        *vault.Client
   134  	)
   135  
   136  	log.Debug("Vault Write entered")
   137  	if len(args) > 0 {
   138  		content = []byte(args[0])
   139  	} else {
   140  		datafile, err = cmd.Flags().GetString("data_file")
   141  		if err == nil {
   142  			//nolint gosec
   143  			content, err = os.ReadFile(datafile)
   144  			if err != nil {
   145  				err = fmt.Errorf("could not read data file '%s': %s", datafile, err)
   146  				return err
   147  			}
   148  		} else {
   149  			return err
   150  		}
   151  	}
   152  	if len(content) < 3 {
   153  		err = fmt.Errorf("no input to write, use 'data_file' as file or Arg0 as string with json data")
   154  		return err
   155  	}
   156  	err = json.Unmarshal(content, &vaultData)
   157  	if err != nil {
   158  		err = fmt.Errorf("could not unmarshal json data: %s", err)
   159  		return err
   160  	}
   161  	vc, _ = pwlib.VaultConfig(vaultAddr, vaultToken)
   162  	if logical {
   163  		log.Debug("Write data with logical api")
   164  		err = pwlib.VaultWrite(vc, vaultPath, vaultData)
   165  	} else {
   166  		log.Debug("Write data with KV api")
   167  		err = pwlib.VaultKVWrite(vc, kvMount, vaultPath, vaultData)
   168  	}
   169  	if err == nil {
   170  		log.Info("Vault Write OK")
   171  		fmt.Println("OK")
   172  	}
   173  	return err
   174  }
   175  
   176  func vaultList(_ *cobra.Command, _ []string) error {
   177  	var (
   178  		err       error
   179  		vc        *vault.Client
   180  		vs        *vault.Secret
   181  		vaultkeys []interface{}
   182  	)
   183  
   184  	log.Debugf("Vault list entered for path '%s'", vaultPath)
   185  	vc, _ = pwlib.VaultConfig(vaultAddr, vaultToken)
   186  	vp := vaultPath
   187  	if vp == "" {
   188  		vp = "/"
   189  	}
   190  	if !logical {
   191  		vp = path.Join(kvMount, "metadata", vp)
   192  		log.Debugf("expand kv path for api to %s", vp)
   193  	}
   194  	vs, err = pwlib.VaultList(vc, vp)
   195  	if err == nil {
   196  		if vs != nil {
   197  			vaultkeys = vs.Data["keys"].([]interface{})
   198  			l := len(vaultkeys)
   199  			log.Infof("Vault List returned %d entries", l)
   200  			for _, k := range vaultkeys {
   201  				fmt.Println(k)
   202  			}
   203  		} else {
   204  			err = fmt.Errorf("no Entries returned")
   205  		}
   206  	} else {
   207  		err = fmt.Errorf("list command failed:%s", err)
   208  	}
   209  	return err
   210  }
   211  
   212  func printData(vaultData map[string]interface{}, key string) (err error) {
   213  	var jsonData []byte
   214  	sysKey := strings.ReplaceAll(vaultPath, ":", "_")
   215  	if key != "" {
   216  		value, ok := vaultData[key].(string)
   217  		if ok {
   218  			fmt.Printf("%s", value)
   219  		} else {
   220  			err = fmt.Errorf("key '%s' not found", key)
   221  		}
   222  	} else {
   223  		if len(vaultData) > 0 {
   224  			if jsonOut {
   225  				jsonData, err = json.Marshal(vaultData)
   226  				if err != nil {
   227  					err = fmt.Errorf("cannot generate json output:%s", err)
   228  					return
   229  				}
   230  				fmt.Printf("%s\n", jsonData)
   231  			} else {
   232  				for k, v := range vaultData {
   233  					fmt.Printf("%s:%s:%v\n", sysKey, k, v)
   234  				}
   235  			}
   236  		} else {
   237  			err = fmt.Errorf("no data found")
   238  		}
   239  	}
   240  	return
   241  }