2016-05-28 23:35:17 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2016-06-25 12:24:13 +00:00
|
|
|
"io/ioutil"
|
2016-05-28 23:35:17 +00:00
|
|
|
"log"
|
2016-05-29 00:04:23 +00:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
2016-05-28 23:35:17 +00:00
|
|
|
|
2016-05-29 00:04:23 +00:00
|
|
|
"github.com/Luzifer/go_helpers/env"
|
2016-05-28 23:35:17 +00:00
|
|
|
"github.com/Luzifer/rconfig"
|
|
|
|
"github.com/hashicorp/vault/api"
|
2016-06-25 12:24:13 +00:00
|
|
|
"github.com/mitchellh/go-homedir"
|
2016-05-28 23:35:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
cfg = struct {
|
2016-05-29 00:04:23 +00:00
|
|
|
VaultAddress string `flag:"vault-addr" env:"VAULT_ADDR" default:"https://127.0.0.1:8200" description:"Vault API address"`
|
|
|
|
VaultAppID string `flag:"vault-app-id" env:"VAULT_APP_ID" default:"" description:"The app-id to use for authentication"`
|
|
|
|
VaultUserID string `flag:"vault-user-id" env:"VAULT_USER_ID" default:"" description:"The user-id to use for authentication"`
|
2016-06-25 12:24:13 +00:00
|
|
|
VaultToken string `flag:"vault-token" env:"VAULT_TOKEN" vardefault:"vault-token" description:"Specify a token to use instead of app-id auth"`
|
2016-05-29 00:04:23 +00:00
|
|
|
Export bool `flag:"export,e" default:"false" description:"Show export statements instead of running the command specified"`
|
|
|
|
VersionAndExit bool `flag:"version" default:"false" description:"Print program version and exit"`
|
2016-05-28 23:35:17 +00:00
|
|
|
}{}
|
|
|
|
version = "dev"
|
|
|
|
)
|
|
|
|
|
2016-06-25 12:24:13 +00:00
|
|
|
func vaultTokenFromDisk() string {
|
|
|
|
vf, err := homedir.Expand("~/.vault-token")
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := ioutil.ReadFile(vf)
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(data)
|
|
|
|
}
|
|
|
|
|
2016-05-28 23:35:17 +00:00
|
|
|
func init() {
|
2016-06-25 12:24:13 +00:00
|
|
|
rconfig.SetVariableDefaults(map[string]string{
|
|
|
|
"vault-token": vaultTokenFromDisk(),
|
|
|
|
})
|
2016-05-28 23:35:17 +00:00
|
|
|
rconfig.Parse(&cfg)
|
|
|
|
|
2016-05-29 00:04:23 +00:00
|
|
|
if cfg.VersionAndExit {
|
|
|
|
fmt.Printf("vault2env %s\n", version)
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2016-05-29 00:17:04 +00:00
|
|
|
if (cfg.VaultAppID == "" || cfg.VaultUserID == "") && cfg.VaultToken == "" {
|
|
|
|
log.Fatalf("[ERR] You need to either set vault-app-id and vault-user-id or set vault-token")
|
2016-05-28 23:35:17 +00:00
|
|
|
}
|
|
|
|
|
2016-05-29 00:04:23 +00:00
|
|
|
if cfg.Export {
|
|
|
|
if len(rconfig.Args()) != 2 {
|
|
|
|
log.Fatalf("[ERR] Usage: vault2env --export [secret path]")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if len(rconfig.Args()) < 3 {
|
|
|
|
log.Fatalf("[ERR] Usage: vault2env [secret path] [command]")
|
|
|
|
}
|
2016-05-28 23:35:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
client, err := api.NewClient(&api.Config{
|
|
|
|
Address: cfg.VaultAddress,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Unable to create client: %s", err)
|
|
|
|
}
|
|
|
|
|
2016-05-29 00:17:04 +00:00
|
|
|
if cfg.VaultToken == "" {
|
2016-05-29 00:21:29 +00:00
|
|
|
loginSecret, lserr := client.Logical().Write("auth/app-id/login/"+cfg.VaultAppID, map[string]interface{}{
|
2016-05-29 00:17:04 +00:00
|
|
|
"user_id": cfg.VaultUserID,
|
|
|
|
})
|
2016-05-29 00:21:29 +00:00
|
|
|
if lserr != nil || loginSecret.Auth == nil {
|
|
|
|
log.Fatalf("Unable to fetch authentication token: %s", lserr)
|
2016-05-29 00:17:04 +00:00
|
|
|
}
|
2016-05-28 23:35:17 +00:00
|
|
|
|
2016-05-29 00:17:04 +00:00
|
|
|
client.SetToken(loginSecret.Auth.ClientToken)
|
|
|
|
defer client.Auth().Token().RevokeSelf(client.Token())
|
|
|
|
} else {
|
2016-05-29 00:21:29 +00:00
|
|
|
client.SetToken(cfg.VaultToken)
|
2016-05-29 00:17:04 +00:00
|
|
|
}
|
2016-05-28 23:35:17 +00:00
|
|
|
|
|
|
|
data, err := client.Logical().Read(rconfig.Args()[1])
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Unable to fetch data: %s", err)
|
|
|
|
}
|
|
|
|
|
2016-05-29 00:04:23 +00:00
|
|
|
if cfg.Export {
|
|
|
|
for k, v := range data.Data {
|
|
|
|
fmt.Printf("export %s=\"%s\"\n", k, v)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
emap := env.ListToMap(os.Environ())
|
2016-05-28 23:35:17 +00:00
|
|
|
for k, v := range data.Data {
|
2016-05-29 00:04:23 +00:00
|
|
|
emap[k] = v.(string)
|
2016-05-28 23:35:17 +00:00
|
|
|
}
|
|
|
|
|
2016-05-29 00:23:19 +00:00
|
|
|
cmd := exec.Command(rconfig.Args()[2], rconfig.Args()[3:]...)
|
2016-05-29 00:04:23 +00:00
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
cmd.Stdin = os.Stdin
|
|
|
|
cmd.Env = env.MapToList(emap)
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
log.Fatal("Command exitted unclean (code != 0)")
|
2016-05-28 23:35:17 +00:00
|
|
|
}
|
|
|
|
}
|