initial commit
This commit is contained in:
		
							
								
								
									
										55
									
								
								cmd/getvaultpw/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								cmd/getvaultpw/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"time" | ||||
|  | ||||
| 	"getvaultpw/internal/config" | ||||
| 	"getvaultpw/internal/protectString" | ||||
| 	"getvaultpw/internal/vault" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	// initialize application configuration | ||||
| 	cfg := config.Init() | ||||
|  | ||||
| 	defer func(cfg *config.Config) { | ||||
| 		if err := cfg.WriteConfig(cfg.ConfigFile); err != nil { | ||||
| 			log.Fatalf("[WARNING] Unable to update configuration file: %v", err) | ||||
| 		} | ||||
| 		log.Println("[DEBUG] shutdown sequence complete") | ||||
| 	}(cfg) | ||||
|  | ||||
| 	// if we are passed a vault instance and a password we need to update the password for a environment | ||||
| 	if len(cfg.VaultInstance) != 0 && len(cfg.VaultPass) != 0 { | ||||
| 		cfg.ConfigFileData.VaultEnvironment[cfg.VaultInstance].EPass = protectString.Encrypt(cfg.VaultPass, cfg.ConfigFileData.Salt) | ||||
| 	} | ||||
|  | ||||
| 	// get the password for a secret | ||||
| 	if len(cfg.SecretID) != 0 { | ||||
| 		for k, v := range cfg.ConfigFileData.Credential { | ||||
| 			if k == cfg.SecretID { | ||||
| 				if (v.TimeStamp+(60*60*2)) < time.Now().UTC().Unix() || len(cfg.ConfigFileData.Credential[k].Cache) == 0 { | ||||
| 					o, err := vault.GetCredential(cfg.ConfigFileData.VaultEnvironment[v.VaultEnv].Host, | ||||
| 						cfg.ConfigFileData.VaultEnvironment[v.VaultEnv].User, | ||||
| 						protectString.Decrypt(cfg.ConfigFileData.VaultEnvironment[v.VaultEnv].EPass, cfg.ConfigFileData.Salt), | ||||
| 						v.ID, | ||||
| 						v.Path) | ||||
|  | ||||
| 					if err != nil { | ||||
| 						log.Fatalf("[ERROR] %v", err) | ||||
| 					} | ||||
| 					cfg.ConfigFileData.Credential[k].Cache = protectString.Encrypt(o, cfg.ConfigFileData.Salt) | ||||
| 					cfg.ConfigFileData.Credential[k].TimeStamp = time.Now().UTC().Unix() | ||||
| 					fmt.Println(o) | ||||
| 				} else { | ||||
| 					o := protectString.Decrypt(cfg.ConfigFileData.Credential[k].Cache, cfg.ConfigFileData.Salt) | ||||
| 					fmt.Println(o) | ||||
| 				} | ||||
| 			} else { | ||||
| 				log.Fatalf("[ERROR] Unable to find secret (%s) in config file: %s", cfg.SecretID, cfg.ConfigFile) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| module getvaultpw | ||||
|  | ||||
| go 1.19 | ||||
|  | ||||
| require ( | ||||
| 	github.com/hashicorp/logutils v1.0.0 | ||||
| 	gopkg.in/yaml.v2 v2.4.0 | ||||
| ) | ||||
							
								
								
									
										5
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= | ||||
| github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | ||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||
							
								
								
									
										109
									
								
								internal/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								internal/config/config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/hashicorp/logutils" | ||||
| ) | ||||
|  | ||||
| type Config struct { | ||||
| 	// time configuration | ||||
| 	TimeFormat    string         `env:"TIME_FORMAT" default:"2006-01-02 15:04:05"` | ||||
| 	TimeZoneLocal string         `env:"TIME_ZONE" default:"America/Chicago"` | ||||
| 	TZoneLocal    *time.Location `ignored:"true"` | ||||
| 	TZoneUTC      *time.Location `ignored:"true"` | ||||
|  | ||||
| 	// logging | ||||
| 	LogLevel int                   `env:"LOG_LEVEL" default:"0"` | ||||
| 	Log      *logutils.LevelFilter `ignored:"true"` | ||||
|  | ||||
| 	// configuration file | ||||
| 	ConfigFile        string           `ignored:"true" env:"CONFIG_FILE" required:"false"` | ||||
| 	ConfigFileExample string           `ignored:"true"` | ||||
| 	ConfigFileData    ConfigFileStruct `ignored:"true"` | ||||
|  | ||||
| 	// misc | ||||
| 	VaultUser     string `env:"VAULT_USER" required:"false" default:""` | ||||
| 	VaultPass     string `env:"VAULT_PASS" required:"false" default:""` | ||||
| 	SecretID      string `env:"VAULT_SECRET_ID" required:"false" default:""` | ||||
| 	VaultInstance string `env:"VAULT_INSTANCE" required:"false" default:"dev"` | ||||
| } | ||||
|  | ||||
| // DefaultConfig initializes the config variable for use with a prepared set of defaults. | ||||
| func DefaultConfig() *Config { | ||||
| 	home, err := os.UserHomeDir() | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("[FATAL] Unable to determine user home directory for config file: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return &Config{ | ||||
| 		Log: &logutils.LevelFilter{ | ||||
| 			Levels: []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARNING", "ERROR"}, | ||||
| 			Writer: os.Stderr, | ||||
| 		}, | ||||
| 		ConfigFile: home + "/.getvaultpw.yml", | ||||
| 		ConfigFileExample: `--- | ||||
| salt: exampleSalt | ||||
|  | ||||
| vaultEnvironment: | ||||
|   dev: | ||||
|     host: vault.dev.example.com | ||||
|     user: userName | ||||
|   test: | ||||
|     host: vault.test.example.com | ||||
|     user: userName | ||||
|   stage: | ||||
|     host: vault.stage.example.com | ||||
|     user: userName | ||||
|   prod: | ||||
|     host: vault.prod.example.com | ||||
|     user: userName | ||||
|  | ||||
| credentials: | ||||
|   serviceAccountNumberOne: | ||||
|     vaultPath: /secrets/serviceAccountNumberOne/credentials | ||||
|     vaultID: password | ||||
| 		vaultInstance: dev | ||||
|   serviceAccountNumberTwo: | ||||
|     vaultPath: /secrets/serviceAccountNumberTwo/credentials | ||||
|     vaultID: password | ||||
| `, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (cfg *Config) setLogLevel() { | ||||
| 	switch { | ||||
| 	case cfg.LogLevel <= 20: | ||||
| 		cfg.Log.SetMinLevel(logutils.LogLevel("ERROR")) | ||||
| 	case cfg.LogLevel > 20 && cfg.LogLevel <= 40: | ||||
| 		cfg.Log.SetMinLevel(logutils.LogLevel("WARNING")) | ||||
| 	case cfg.LogLevel > 40 && cfg.LogLevel <= 60: | ||||
| 		cfg.Log.SetMinLevel(logutils.LogLevel("INFO")) | ||||
| 	case cfg.LogLevel > 60 && cfg.LogLevel <= 80: | ||||
| 		cfg.Log.SetMinLevel(logutils.LogLevel("DEBUG")) | ||||
| 	case cfg.LogLevel > 80: | ||||
| 		cfg.Log.SetMinLevel(logutils.LogLevel("TRACE")) | ||||
| 	} | ||||
| 	log.SetOutput(cfg.Log) | ||||
| } | ||||
|  | ||||
| func (cfg *Config) printRunningConfig(cfgInfo []StructInfo) { | ||||
| 	log.Printf("[DEBUG] Current Running Configuration Values:") | ||||
| 	for _, info := range cfgInfo { | ||||
| 		switch info.Type.String() { | ||||
| 		case "string": | ||||
| 			p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string) | ||||
| 			log.Printf("[DEBUG]\t%s\t\t= %s\n", info.Alt, *p) | ||||
| 		case "bool": | ||||
| 			p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool) | ||||
| 			log.Printf("[DEBUG]\t%s\t\t= %s\n", info.Alt, strconv.FormatBool(*p)) | ||||
| 		case "int": | ||||
| 			p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int) | ||||
| 			log.Printf("[DEBUG]\t%s\t\t= %s\n", info.Alt, strconv.FormatInt(int64(*p), 10)) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										43
									
								
								internal/config/configFile.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								internal/config/configFile.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"os" | ||||
|  | ||||
| 	"gopkg.in/yaml.v2" | ||||
| ) | ||||
|  | ||||
| type ConfigFileStruct struct { | ||||
| 	Salt             string                       `yaml:"salt"` | ||||
| 	VaultEnvironment map[string]*VaultEnvStruct   `yaml:"vaultEnvironment"` | ||||
| 	Credential       map[string]*CredentialStruct `yaml:"credentials"` | ||||
| } | ||||
|  | ||||
| type VaultEnvStruct struct { | ||||
| 	Host  string `yaml:"host"` | ||||
| 	User  string `yaml:"user"` | ||||
| 	EPass string `yaml:"encryptedPassword"` | ||||
| } | ||||
|  | ||||
| type CredentialStruct struct { | ||||
| 	Path      string `yaml:"vaultPath"` | ||||
| 	ID        string `yaml:"vaultID"` | ||||
| 	TimeStamp int64  `yaml:"timestamp"` | ||||
| 	Cache     string `yaml:"cachedValue"` | ||||
| 	VaultEnv  string `yaml:"vaultInstance"` | ||||
| } | ||||
|  | ||||
| func (cfg *Config) WriteConfig(dest string) error { | ||||
| 	file, err := os.OpenFile(dest, os.O_WRONLY, 0600) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("[ERROR] Unable to open the config file %s: %v", dest, err) | ||||
| 	} | ||||
| 	defer file.Close() | ||||
|  | ||||
| 	data, _ := yaml.Marshal(cfg.ConfigFileData) | ||||
| 	if _, err = file.Write(data); err != nil { | ||||
| 		log.Fatalf("[ERROR] Unable to update config file %s: %v", cfg.ConfigFile, err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										111
									
								
								internal/config/envconfig.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								internal/config/envconfig.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| type StructInfo struct { | ||||
| 	Name         string | ||||
| 	Alt          string | ||||
| 	Key          string | ||||
| 	Field        reflect.Value | ||||
| 	Tags         reflect.StructTag | ||||
| 	Type         reflect.Type | ||||
| 	DefaultValue interface{} | ||||
| } | ||||
|  | ||||
| func getStructInfo(spec interface{}) ([]StructInfo, error) { | ||||
| 	s := reflect.ValueOf(spec) | ||||
|  | ||||
| 	if s.Kind() != reflect.Pointer { | ||||
| 		return []StructInfo{}, fmt.Errorf("getStructInfo() was sent a %s instead of a pointer to a struct.\n", s.Kind()) | ||||
| 	} | ||||
|  | ||||
| 	s = s.Elem() | ||||
| 	if s.Kind() != reflect.Struct { | ||||
| 		return []StructInfo{}, fmt.Errorf("getStructInfo() was sent a %s instead of a struct.\n", s.Kind()) | ||||
| 	} | ||||
| 	typeOfSpec := s.Type() | ||||
|  | ||||
| 	infos := make([]StructInfo, 0, s.NumField()) | ||||
| 	for i := 0; i < s.NumField(); i++ { | ||||
| 		f := s.Field(i) | ||||
| 		ftype := typeOfSpec.Field(i) | ||||
|  | ||||
| 		ignored, _ := strconv.ParseBool(ftype.Tag.Get("ignored")) | ||||
| 		if !f.CanSet() || ignored { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		for f.Kind() == reflect.Pointer { | ||||
| 			if f.IsNil() { | ||||
| 				if f.Type().Elem().Kind() != reflect.Struct { | ||||
| 					break | ||||
| 				} | ||||
| 				f.Set(reflect.New(f.Type().Elem())) | ||||
| 			} | ||||
| 			f = f.Elem() | ||||
| 		} | ||||
|  | ||||
| 		info := StructInfo{ | ||||
| 			Name:  ftype.Name, | ||||
| 			Alt:   strings.ToUpper(ftype.Tag.Get("env")), | ||||
| 			Key:   ftype.Name, | ||||
| 			Field: f, | ||||
| 			Tags:  ftype.Tag, | ||||
| 			Type:  ftype.Type, | ||||
| 		} | ||||
| 		if info.Alt != "" { | ||||
| 			info.Key = info.Alt | ||||
| 		} | ||||
| 		info.Key = strings.ToUpper(info.Key) | ||||
| 		if ftype.Tag.Get("default") != "" { | ||||
| 			v, err := typeConversion(ftype.Type.String(), ftype.Tag.Get("default")) | ||||
| 			if err != nil { | ||||
| 				return []StructInfo{}, err | ||||
| 			} | ||||
| 			info.DefaultValue = v | ||||
| 		} | ||||
| 		infos = append(infos, info) | ||||
| 	} | ||||
| 	return infos, nil | ||||
| } | ||||
|  | ||||
| func typeConversion(t, v string) (interface{}, error) { | ||||
| 	switch t { | ||||
| 	case "string": | ||||
| 		return v, nil | ||||
| 	case "int": | ||||
| 		return strconv.ParseInt(v, 10, 0) | ||||
| 	case "int8": | ||||
| 		return strconv.ParseInt(v, 10, 8) | ||||
| 	case "int16": | ||||
| 		return strconv.ParseInt(v, 10, 16) | ||||
| 	case "int32": | ||||
| 		return strconv.ParseInt(v, 10, 32) | ||||
| 	case "int64": | ||||
| 		return strconv.ParseInt(v, 10, 64) | ||||
| 	case "uint": | ||||
| 		return strconv.ParseUint(v, 10, 0) | ||||
| 	case "uint16": | ||||
| 		return strconv.ParseUint(v, 10, 16) | ||||
| 	case "uint32": | ||||
| 		return strconv.ParseUint(v, 10, 32) | ||||
| 	case "uint64": | ||||
| 		return strconv.ParseUint(v, 10, 64) | ||||
| 	case "float32": | ||||
| 		return strconv.ParseFloat(v, 32) | ||||
| 	case "float64": | ||||
| 		return strconv.ParseFloat(v, 64) | ||||
| 	case "complex64": | ||||
| 		return strconv.ParseComplex(v, 64) | ||||
| 	case "complex128": | ||||
| 		return strconv.ParseComplex(v, 128) | ||||
| 	case "bool": | ||||
| 		return strconv.ParseBool(v) | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("Unable to identify type.") | ||||
| } | ||||
							
								
								
									
										161
									
								
								internal/config/initialize.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								internal/config/initialize.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"crypto/rand" | ||||
| 	"encoding/hex" | ||||
| 	"io/ioutil" | ||||
|  | ||||
| 	"gopkg.in/yaml.v2" | ||||
| ) | ||||
|  | ||||
| // getEnvString returns string from environment variable | ||||
| func getEnvString(env, def string) (val string) { //nolint:deadcode | ||||
| 	val = os.Getenv(env) | ||||
|  | ||||
| 	if val == "" { | ||||
| 		return def | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // getEnvInt returns int from environment variable | ||||
| func getEnvInt(env string, def int) (ret int) { | ||||
| 	val := os.Getenv(env) | ||||
|  | ||||
| 	if val == "" { | ||||
| 		return def | ||||
| 	} | ||||
|  | ||||
| 	ret, err := strconv.Atoi(val) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("[ERROR] Environment variable is not numeric: %v\n", env) | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // getEnvBool returns boolean from environment variable | ||||
| func getEnvBool(env string, def bool) bool { | ||||
| 	var ( | ||||
| 		err    error | ||||
| 		retVal bool | ||||
| 		val    = os.Getenv(env) | ||||
| 	) | ||||
|  | ||||
| 	if len(val) == 0 { | ||||
| 		return def | ||||
| 	} else { | ||||
| 		retVal, err = strconv.ParseBool(val) | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("[ERROR] Environment variable is not boolean: %v\n", env) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return retVal | ||||
| } | ||||
|  | ||||
| // Init initializes the application configuration by reading default values from the struct's tags | ||||
| // and environment variables. Tags processed by this process are as follows: | ||||
| // `ignored:"true" env:"ENVIRONMENT_VARIABLE" default:"default value"` | ||||
| func Init() *Config { | ||||
| 	var cryptovault string | ||||
| 	cfg := DefaultConfig() | ||||
|  | ||||
| 	cfgInfo, err := getStructInfo(cfg) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("[FATAL] %v", err) | ||||
| 	} | ||||
|  | ||||
| 	for _, info := range cfgInfo { | ||||
| 		switch info.Type.String() { | ||||
| 		case "string": | ||||
| 			var dv string | ||||
|  | ||||
| 			if info.DefaultValue != nil { | ||||
| 				dv = info.DefaultValue.(string) | ||||
| 			} | ||||
| 			p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string) | ||||
| 			flag.StringVar(p, info.Name, getEnvString(info.Name, dv), "("+info.Key+")") | ||||
| 		case "bool": | ||||
| 			var dv bool | ||||
|  | ||||
| 			if info.DefaultValue != nil { | ||||
| 				dv = info.DefaultValue.(bool) | ||||
| 			} | ||||
| 			p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool) | ||||
| 			flag.BoolVar(p, info.Name, getEnvBool(info.Name, dv), "("+info.Key+")") | ||||
| 		case "int": | ||||
| 			var dv int | ||||
|  | ||||
| 			if info.DefaultValue != nil { | ||||
| 				dv = int(info.DefaultValue.(int64)) | ||||
| 			} | ||||
| 			p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int) | ||||
| 			flag.IntVar(p, info.Name, getEnvInt(info.Name, dv), "("+info.Key+")") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	flag.StringVar(&cryptovault, "label", "", "Deprecated support feature. Please use -SecretID.\nCryptovault compatibility feature used to identify the ID of the secret") | ||||
| 	flag.Parse() | ||||
|  | ||||
| 	if len(cryptovault) != 0 { | ||||
| 		cfg.SecretID = cryptovault | ||||
| 	} | ||||
|  | ||||
| 	// set logging level | ||||
| 	cfg.setLogLevel() | ||||
|  | ||||
| 	// check if configuration file is present, if not create the example config file | ||||
| 	if _, err := os.Stat(cfg.ConfigFile); os.IsNotExist(err) { | ||||
| 		log.Printf("[WARNING] No configuration file present. Creating example configuration file at %s", cfg.ConfigFile) | ||||
| 		file, err := os.Create(cfg.ConfigFile) | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("[ERROR] Unable to create the config file %s: %v", cfg.ConfigFile, err) | ||||
| 		} | ||||
| 		defer file.Close() | ||||
|  | ||||
| 		file.Write([]byte(cfg.ConfigFileExample)) | ||||
| 	} | ||||
|  | ||||
| 	// readConfiguration File | ||||
| 	fileData, err := ioutil.ReadFile(cfg.ConfigFile) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("[ERROR] Unable to read the config file %s: %v", cfg.ConfigFile, err) | ||||
| 	} | ||||
| 	err = yaml.Unmarshal(fileData, &cfg.ConfigFileData) | ||||
| 	if err != nil { | ||||
| 		log.Printf("[ERROR] Unable to process the config file %s: %v", cfg.ConfigFile, err) | ||||
| 	} | ||||
|  | ||||
| 	// make sure there is a secure salt | ||||
| 	if cfg.ConfigFileData.Salt == "" || cfg.ConfigFileData.Salt == "exampleSalt" { | ||||
| 		s := make([]byte, 32) | ||||
| 		rand.Read(s) | ||||
| 		cfg.ConfigFileData.Salt = hex.EncodeToString(s) | ||||
| 	} | ||||
|  | ||||
| 	// timezone & format configuration | ||||
| 	cfg.TZoneUTC, _ = time.LoadLocation("UTC") | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("[ERROR] Unable to parse timezone string. Please use one of the timezone database values listed here: %s", "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") | ||||
| 	} | ||||
| 	cfg.TZoneLocal, err = time.LoadLocation(cfg.TimeZoneLocal) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("[ERROR] Unable to parse timezone string. Please use one of the timezone database values listed here: %s", "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") | ||||
| 	} | ||||
| 	time.Now().Format(cfg.TimeFormat) | ||||
|  | ||||
| 	// print running config | ||||
| 	cfg.printRunningConfig(cfgInfo) | ||||
|  | ||||
| 	log.Println("[INFO] initialization complete") | ||||
| 	return cfg | ||||
| } | ||||
							
								
								
									
										137
									
								
								internal/httpclient/httpclient.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								internal/httpclient/httpclient.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| package httpclient | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"net" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| ) | ||||
|  | ||||
| // HTTPClient is an interface for initializing the http client library. | ||||
| type HTTPClient struct { | ||||
| 	Client  *http.Client | ||||
| 	Data    *bytes.Buffer | ||||
| 	Headers map[string]string | ||||
|  | ||||
| 	Username string | ||||
| 	Password string | ||||
| } | ||||
|  | ||||
| // DefaultClient is a function for defining a basic HTTP client with standard timeouts. | ||||
| func DefaultClient() *HTTPClient { | ||||
| 	return &HTTPClient{ | ||||
| 		Client: &http.Client{ | ||||
| 			Timeout: 60 * time.Second, | ||||
| 			Transport: &http.Transport{ | ||||
| 				Dial: (&net.Dialer{ | ||||
| 					Timeout: 5 * time.Second, | ||||
| 				}).Dial, | ||||
| 				TLSHandshakeTimeout: 5 * time.Second, | ||||
| 				IdleConnTimeout:     300 * time.Second, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewClient Create an HTTPClient with a user-provided net/http.Client | ||||
| func NewClient(httpClient *http.Client) *HTTPClient { | ||||
| 	return &HTTPClient{Client: httpClient} | ||||
| } | ||||
|  | ||||
| // SetBasicAuth is a chaining function to set the username and password for basic | ||||
| // authentication | ||||
| func (c *HTTPClient) SetBasicAuth(username, password string) *HTTPClient { | ||||
| 	c.Username = username | ||||
| 	c.Password = password | ||||
|  | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| // SetPostData is a chaining function to set POST/PUT/PATCH data | ||||
| func (c *HTTPClient) SetPostData(data string) *HTTPClient { | ||||
| 	c.Data = bytes.NewBufferString(data) | ||||
|  | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| // SetHeader is a chaining function to set arbitrary HTTP Headers | ||||
| func (c *HTTPClient) SetHeader(label string, value string) *HTTPClient { | ||||
| 	if c.Headers == nil { | ||||
| 		c.Headers = map[string]string{} | ||||
| 	} | ||||
|  | ||||
| 	c.Headers[label] = value | ||||
|  | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| // Get calls the net.http GET operation | ||||
| func (c *HTTPClient) Get(url string) ([]byte, error) { | ||||
| 	return c.do(url, http.MethodGet) | ||||
| } | ||||
|  | ||||
| // Patch calls the net.http PATCH operation | ||||
| func (c *HTTPClient) Patch(url string) ([]byte, error) { | ||||
| 	return c.do(url, http.MethodPatch) | ||||
| } | ||||
|  | ||||
| // Post calls the net.http POST operation | ||||
| func (c *HTTPClient) Post(url string) ([]byte, error) { | ||||
| 	return c.do(url, http.MethodPost) | ||||
| } | ||||
|  | ||||
| // Put calls the net.http PUT operation | ||||
| func (c *HTTPClient) Put(url string) ([]byte, error) { | ||||
| 	return c.do(url, http.MethodPut) | ||||
| } | ||||
|  | ||||
| func (c *HTTPClient) do(url string, method string) ([]byte, error) { | ||||
| 	var ( | ||||
| 		req    *http.Request | ||||
| 		res    *http.Response | ||||
| 		output []byte | ||||
| 		err    error | ||||
| 	) | ||||
|  | ||||
| 	// NewRequest knows that c.data is typed *bytes.Buffer and will SEGFAULT | ||||
| 	// if c.data is nil. So we create a request using nil when c.data is nil | ||||
| 	if c.Data != nil { | ||||
| 		req, err = http.NewRequest(method, url, c.Data) | ||||
| 	} else { | ||||
| 		req, err = http.NewRequest(method, url, nil) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if (len(c.Username) > 0) && (len(c.Password) > 0) { | ||||
| 		req.SetBasicAuth(c.Username, c.Password) | ||||
| 	} | ||||
|  | ||||
| 	if c.Headers != nil { | ||||
| 		for label, value := range c.Headers { | ||||
| 			req.Header.Set(label, value) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if res, err = c.Client.Do(req); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	defer res.Body.Close() | ||||
|  | ||||
| 	if output, err = ioutil.ReadAll(res.Body); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// check status | ||||
| 	if res.StatusCode < 200 || res.StatusCode >= 300 { | ||||
| 		return nil, errors.New("non-successful status code received [" + strconv.Itoa(res.StatusCode) + "]") | ||||
| 	} | ||||
|  | ||||
| 	return output, nil | ||||
| } | ||||
							
								
								
									
										281
									
								
								internal/httpclient/httpclient_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								internal/httpclient/httpclient_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,281 @@ | ||||
| package httpclient | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"encoding/json" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| ) | ||||
|  | ||||
| type Data struct { | ||||
| 	Greeting string            `json:"greeting"` | ||||
| 	Headers  map[string]string `json:"headers"` | ||||
| 	Method   string            `json:"method"` | ||||
| 	Username string            `json:"username"` | ||||
| 	Password string            `json:"password"` | ||||
| 	PostData string            `json:"postdata"` | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	greeting    = "Hello world" | ||||
| 	postData    = "Test data" | ||||
| 	authUser    = "testuser" | ||||
| 	authPass    = "testpass" | ||||
| 	headerLabel = "Test-Header" | ||||
| 	headerValue = "Test-Value" | ||||
| ) | ||||
|  | ||||
| func httpTestHandler(w http.ResponseWriter, r *http.Request) { | ||||
| 	var ( | ||||
| 		b    []byte | ||||
| 		user string | ||||
| 		pass string | ||||
| 		body []byte | ||||
| 	) | ||||
|  | ||||
| 	data := Data{ | ||||
| 		Greeting: greeting, | ||||
| 		Headers:  map[string]string{}, | ||||
| 		Method:   r.Method, | ||||
| 	} | ||||
|  | ||||
| 	user, pass, ok := r.BasicAuth() | ||||
| 	if ok { | ||||
| 		data.Username = user | ||||
| 		data.Password = pass | ||||
| 	} | ||||
|  | ||||
| 	body, err := ioutil.ReadAll(r.Body) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprint(w, "ioutil.ReadAll failed") | ||||
| 	} | ||||
| 	data.PostData = string(body) | ||||
|  | ||||
| 	for h := range r.Header { | ||||
| 		data.Headers[h] = r.Header.Get(h) | ||||
| 	} | ||||
|  | ||||
| 	b, err = json.MarshalIndent(data, "", "  ") | ||||
| 	if err != nil { | ||||
| 		fmt.Fprint(w, "Json marshal failed somehow") | ||||
| 	} | ||||
| 	fmt.Fprint(w, string(b)) | ||||
| } | ||||
|  | ||||
| func checkMethod(t *testing.T, data Data, method string) { | ||||
| 	if data.Method != method { | ||||
| 		t.Errorf("data.Method(%s) != method(%s)", data.Method, method) | ||||
| 	} | ||||
| 	t.Log("checkMethod() success") | ||||
| } | ||||
|  | ||||
| func checkGreeting(t *testing.T, data Data) { | ||||
| 	if data.Greeting != greeting { | ||||
| 		t.Errorf("data.Greeting(%s) != greeting(%s)", data.Greeting, greeting) | ||||
| 	} | ||||
| 	t.Log("checkGreeting() success") | ||||
| } | ||||
|  | ||||
| func checkBasicAuth(t *testing.T, data Data) { | ||||
| 	if data.Username != authUser { | ||||
| 		t.Errorf("data.Username(%s) != authUser(%s)", data.Username, authUser) | ||||
| 	} | ||||
| 	if data.Password != authPass { | ||||
| 		t.Errorf("data.Password(%s) != authPass(%s)", data.Password, authPass) | ||||
| 	} | ||||
| 	t.Log("checkBasicAuth() success") | ||||
| } | ||||
|  | ||||
| func checkPostData(t *testing.T, data Data) { | ||||
| 	if data.PostData != postData { | ||||
| 		t.Errorf("data.PostData(%s) != postData(%s)", data.PostData, postData) | ||||
| 	} | ||||
| 	t.Log("checkPostData() success") | ||||
| } | ||||
|  | ||||
| func TestGet(t *testing.T) { | ||||
| 	var data Data | ||||
|  | ||||
| 	ts := httptest.NewServer(http.HandlerFunc(httpTestHandler)) | ||||
| 	defer ts.Close() | ||||
|  | ||||
| 	output, err := DefaultClient().Get(ts.URL) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	if err = json.Unmarshal(output, &data); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	checkMethod(t, data, http.MethodGet) | ||||
| 	checkGreeting(t, data) | ||||
| } | ||||
|  | ||||
| func TestGetAuth(t *testing.T) { | ||||
| 	var data Data | ||||
|  | ||||
| 	ts := httptest.NewServer(http.HandlerFunc(httpTestHandler)) | ||||
| 	defer ts.Close() | ||||
|  | ||||
| 	output, err := DefaultClient().SetBasicAuth(authUser, authPass).Get(ts.URL) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	if err = json.Unmarshal(output, &data); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	checkMethod(t, data, http.MethodGet) | ||||
| 	checkGreeting(t, data) | ||||
| 	checkBasicAuth(t, data) | ||||
| } | ||||
|  | ||||
| func TestPut(t *testing.T) { | ||||
| 	var data Data | ||||
|  | ||||
| 	ts := httptest.NewServer(http.HandlerFunc(httpTestHandler)) | ||||
| 	defer ts.Close() | ||||
|  | ||||
| 	output, err := DefaultClient().SetPostData(postData).Put(ts.URL) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	if err = json.Unmarshal(output, &data); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	checkMethod(t, data, http.MethodPut) | ||||
| 	checkGreeting(t, data) | ||||
| 	checkPostData(t, data) | ||||
| } | ||||
|  | ||||
| func TestPutAuth(t *testing.T) { | ||||
| 	var data Data | ||||
|  | ||||
| 	ts := httptest.NewServer(http.HandlerFunc(httpTestHandler)) | ||||
| 	defer ts.Close() | ||||
|  | ||||
| 	output, err := DefaultClient().SetBasicAuth(authUser, authPass).SetPostData(postData).Put(ts.URL) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	if err = json.Unmarshal(output, &data); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	checkMethod(t, data, http.MethodPut) | ||||
| 	checkGreeting(t, data) | ||||
| 	checkBasicAuth(t, data) | ||||
| 	checkPostData(t, data) | ||||
| } | ||||
|  | ||||
| func TestPost(t *testing.T) { | ||||
| 	var data Data | ||||
|  | ||||
| 	ts := httptest.NewServer(http.HandlerFunc(httpTestHandler)) | ||||
| 	defer ts.Close() | ||||
|  | ||||
| 	output, err := DefaultClient().SetPostData(postData).Post(ts.URL) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	if err = json.Unmarshal(output, &data); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	checkMethod(t, data, http.MethodPost) | ||||
| 	checkGreeting(t, data) | ||||
| 	checkPostData(t, data) | ||||
| } | ||||
|  | ||||
| func TestPostAuth(t *testing.T) { | ||||
| 	var data Data | ||||
|  | ||||
| 	ts := httptest.NewServer(http.HandlerFunc(httpTestHandler)) | ||||
| 	defer ts.Close() | ||||
|  | ||||
| 	output, err := DefaultClient().SetBasicAuth(authUser, authPass).SetPostData(postData).Post(ts.URL) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	if err = json.Unmarshal(output, &data); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	checkMethod(t, data, http.MethodPost) | ||||
| 	checkGreeting(t, data) | ||||
| 	checkBasicAuth(t, data) | ||||
| 	checkPostData(t, data) | ||||
| } | ||||
|  | ||||
| func TestPatch(t *testing.T) { | ||||
| 	var data Data | ||||
|  | ||||
| 	ts := httptest.NewServer(http.HandlerFunc(httpTestHandler)) | ||||
| 	defer ts.Close() | ||||
|  | ||||
| 	output, err := DefaultClient().SetPostData(postData).Patch(ts.URL) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	if err = json.Unmarshal(output, &data); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	checkMethod(t, data, http.MethodPatch) | ||||
| 	checkGreeting(t, data) | ||||
| 	checkPostData(t, data) | ||||
| } | ||||
|  | ||||
| func TestPatchAuth(t *testing.T) { | ||||
| 	var data Data | ||||
|  | ||||
| 	ts := httptest.NewServer(http.HandlerFunc(httpTestHandler)) | ||||
| 	defer ts.Close() | ||||
|  | ||||
| 	output, err := DefaultClient().SetBasicAuth(authUser, authPass).SetPostData(postData).Patch(ts.URL) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	if err = json.Unmarshal(output, &data); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	checkMethod(t, data, http.MethodPatch) | ||||
| 	checkGreeting(t, data) | ||||
| 	checkBasicAuth(t, data) | ||||
| 	checkPostData(t, data) | ||||
| } | ||||
|  | ||||
| func TestSetHeader(t *testing.T) { | ||||
| 	var data Data | ||||
|  | ||||
| 	ts := httptest.NewServer(http.HandlerFunc(httpTestHandler)) | ||||
| 	defer ts.Close() | ||||
|  | ||||
| 	output, err := DefaultClient().SetHeader(headerLabel, headerValue).Get(ts.URL) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	if err = json.Unmarshal(output, &data); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	checkMethod(t, data, http.MethodGet) | ||||
| 	checkGreeting(t, data) | ||||
| 	if data.Headers[headerLabel] != headerValue { | ||||
| 		t.Errorf("SetHeader values not set in header: %+v", data.Headers) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										39
									
								
								internal/protectString/decrypt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								internal/protectString/decrypt.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| package protectString | ||||
|  | ||||
| import ( | ||||
| 	"crypto/aes" | ||||
| 	"crypto/cipher" | ||||
| 	"encoding/hex" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| func Decrypt(encryptedString string, keyString string) (decryptedString string) { | ||||
| 	key, _ := hex.DecodeString(keyString) | ||||
| 	enc, _ := hex.DecodeString(encryptedString) | ||||
|  | ||||
| 	//Create a new Cipher Block from the key | ||||
| 	block, err := aes.NewCipher(key) | ||||
| 	if err != nil { | ||||
| 		panic(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	//Create a new GCM | ||||
| 	aesGCM, err := cipher.NewGCM(block) | ||||
| 	if err != nil { | ||||
| 		panic(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	//Get the nonce size | ||||
| 	nonceSize := aesGCM.NonceSize() | ||||
|  | ||||
| 	//Extract the nonce from the encrypted data | ||||
| 	nonce, ciphertext := enc[:nonceSize], enc[nonceSize:] | ||||
|  | ||||
| 	//Decrypt the data | ||||
| 	plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil) | ||||
| 	if err != nil { | ||||
| 		panic(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf("%s", plaintext) | ||||
| } | ||||
							
								
								
									
										37
									
								
								internal/protectString/encrypt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								internal/protectString/encrypt.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| package protectString | ||||
|  | ||||
| import ( | ||||
| 	"crypto/aes" | ||||
| 	"crypto/cipher" | ||||
| 	"crypto/rand" | ||||
| 	"encoding/hex" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| func Encrypt(stringToEncrypt string, keyString string) (encryptedString string) { | ||||
| 	key, _ := hex.DecodeString(keyString) | ||||
| 	plaintext := []byte(stringToEncrypt) | ||||
|  | ||||
| 	//Create a new Cipher Block from the key | ||||
| 	block, err := aes.NewCipher(key) | ||||
| 	if err != nil { | ||||
| 		panic(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	//Create a new GCM | ||||
| 	aesGCM, err := cipher.NewGCM(block) | ||||
| 	if err != nil { | ||||
| 		panic(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	//Create a nonce. Nonce should be from GCM | ||||
| 	nonce := make([]byte, aesGCM.NonceSize()) | ||||
| 	if _, err = io.ReadFull(rand.Reader, nonce); err != nil { | ||||
| 		panic(err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	//Encrypt the data using aesGCM.Seal | ||||
| 	ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil) | ||||
| 	return fmt.Sprintf("%x", ciphertext) | ||||
| } | ||||
							
								
								
									
										116
									
								
								internal/vault/vault.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								internal/vault/vault.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| package vault | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"regexp" | ||||
| 	"time" | ||||
|  | ||||
| 	"encoding/json" | ||||
|  | ||||
| 	"getvaultpw/internal/httpclient" | ||||
| ) | ||||
|  | ||||
| type authRespStruct struct { | ||||
| 	Auth struct { | ||||
| 		Accessor      string `json:"accessor"` | ||||
| 		ClientToken   string `json:"client_token"` | ||||
| 		EntityID      string `json:"entity_id"` | ||||
| 		LeaseDuration int64  `json:"lease_duration"` | ||||
| 		Metadata      struct { | ||||
| 			Username string `json:"username"` | ||||
| 		} `json:"metadata"` | ||||
| 		MfaRequirement interface{} `json:"mfa_requirement"` | ||||
| 		NumUses        int64       `json:"num_uses"` | ||||
| 		Orphan         bool        `json:"orphan"` | ||||
| 		Policies       []string    `json:"policies"` | ||||
| 		Renewable      bool        `json:"renewable"` | ||||
| 		TokenPolicies  []string    `json:"token_policies"` | ||||
| 		TokenType      string      `json:"token_type"` | ||||
| 	} `json:"auth"` | ||||
| 	Data          struct{}    `json:"data"` | ||||
| 	LeaseDuration int64       `json:"lease_duration"` | ||||
| 	LeaseID       string      `json:"lease_id"` | ||||
| 	Renewable     bool        `json:"renewable"` | ||||
| 	RequestID     string      `json:"request_id"` | ||||
| 	Warnings      interface{} `json:"warnings"` | ||||
| 	WrapInfo      interface{} `json:"wrap_info"` | ||||
| } | ||||
|  | ||||
| type secretV2Struct struct { | ||||
| 	RequestID     string `json:"request_id"` | ||||
| 	LeaseID       string `json:"lease_id"` | ||||
| 	Renewable     bool   `json:"renewable"` | ||||
| 	LeaseDuration int    `json:"lease_duration"` | ||||
| 	Data          struct { | ||||
| 		Data     map[string]string `json:"data"` | ||||
| 		Metadata struct { | ||||
| 			CreatedTime    time.Time   `json:"created_time"` | ||||
| 			CustomMetadata interface{} `json:"custom_metadata"` | ||||
| 			DeletionTime   string      `json:"deletion_time"` | ||||
| 			Destroyed      bool        `json:"destroyed"` | ||||
| 			Version        int         `json:"version"` | ||||
| 		} `json:"metadata"` | ||||
| 	} `json:"data"` | ||||
| 	WrapInfo interface{} `json:"wrap_info"` | ||||
| 	Warnings interface{} `json:"warnings"` | ||||
| 	Auth     interface{} `json:"auth"` | ||||
| } | ||||
|  | ||||
| func login(host, user, pass string) (string, error) { | ||||
| 	c := httpclient.DefaultClient() | ||||
| 	c.SetHeader("Accept", "application/json") | ||||
| 	c.SetHeader("Content-Type", "application/json") | ||||
| 	c.SetPostData(fmt.Sprintf("{ \"password\":\"%s\"}", pass)) | ||||
|  | ||||
| 	log.Printf("[TRACE] LOGIN URL   : %s", fmt.Sprintf("%s/v1/auth/ldap/login/%s", host, user)) | ||||
| 	log.Printf("[TRACE] LOGIN USER  : %s", user) | ||||
| 	log.Printf("[TRACE] LOGIN PASS  : %s", pass) | ||||
| 	o, err := c.Post(fmt.Sprintf("%s/v1/auth/ldap/login/%s", host, user)) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	var output authRespStruct | ||||
| 	if err := json.Unmarshal(o, &output); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return output.Auth.ClientToken, nil | ||||
| } | ||||
|  | ||||
| func GetCredential(host, user, pass, store, path string) (string, error) { | ||||
| 	token, err := login(host, user, pass) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	c := httpclient.DefaultClient() | ||||
| 	c.SetHeader("Accept", "application/json") | ||||
| 	c.SetHeader("Content-Type", "application/json") | ||||
| 	c.SetHeader("X-Vault-Token", token) | ||||
|  | ||||
| 	log.Printf("[TRACE] SECRET URL  : %s", fmt.Sprintf("%s/v1/%s/data/%s", host, store, path)) | ||||
| 	log.Printf("[TRACE] SECRET TOKEN: %s", token) | ||||
| 	o, err := c.Get(fmt.Sprintf("%s/v1/%s/data/%s", host, store, path)) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	var output secretV2Struct | ||||
| 	if err := json.Unmarshal(o, &output); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	for k, v := range output.Data.Data { | ||||
| 		r, err := regexp.Compile(`(p|P)(a|A)(s|S)(s|S)((w|W)(o|O)(r|R)(d|D))?`) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		if r.Match([]byte(k)) { | ||||
| 			return v, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return "", fmt.Errorf("no password credential located in secret store") | ||||
| } | ||||
		Reference in New Issue
	
	Block a user