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 }