github.com/wynshop-open-source/gomplate@v3.5.0+incompatible/libkv/consul.go (about) 1 package libkv 2 3 import ( 4 "fmt" 5 "net/url" 6 "os" 7 "time" 8 9 "github.com/pkg/errors" 10 11 // XXX: replace once https://github.com/go-yaml/yaml/issues/139 is solved 12 yaml "gopkg.in/hairyhenderson/yaml.v2" 13 14 "github.com/docker/libkv" 15 "github.com/docker/libkv/store" 16 "github.com/docker/libkv/store/consul" 17 "github.com/hairyhenderson/gomplate/conv" 18 "github.com/hairyhenderson/gomplate/env" 19 "github.com/hairyhenderson/gomplate/vault" 20 consulapi "github.com/hashicorp/consul/api" 21 ) 22 23 const ( 24 http = "http" 25 https = "https" 26 ) 27 28 // NewConsul - instantiate a new Consul datasource handler 29 func NewConsul(u *url.URL) (*LibKV, error) { 30 consul.Register() 31 c, err := consulURL(u) 32 if err != nil { 33 return nil, err 34 } 35 config, err := consulConfig(c.Scheme == https) 36 if err != nil { 37 return nil, err 38 } 39 if role := env.Getenv("CONSUL_VAULT_ROLE", ""); role != "" { 40 mount := env.Getenv("CONSUL_VAULT_MOUNT", "consul") 41 42 var client *vault.Vault 43 client, err = vault.New(nil) 44 if err != nil { 45 return nil, err 46 } 47 err = client.Login() 48 defer client.Logout() 49 if err != nil { 50 return nil, err 51 } 52 53 path := fmt.Sprintf("%s/creds/%s", mount, role) 54 55 var data []byte 56 data, err = client.Read(path) 57 if err != nil { 58 return nil, errors.Wrapf(err, "vault consul auth failed") 59 } 60 61 decoded := make(map[string]interface{}) 62 err = yaml.Unmarshal(data, &decoded) 63 if err != nil { 64 return nil, errors.Wrapf(err, "Unable to unmarshal object") 65 } 66 67 token := decoded["token"].(string) 68 69 // nolint: gosec 70 _ = os.Setenv("CONSUL_HTTP_TOKEN", token) 71 } 72 var kv store.Store 73 kv, err = libkv.NewStore(store.CONSUL, []string{c.String()}, config) 74 if err != nil { 75 return nil, errors.Wrapf(err, "Consul setup failed") 76 } 77 return &LibKV{kv}, nil 78 } 79 80 // -- converts a gomplate datasource URL into a usable Consul URL 81 func consulURL(u *url.URL) (*url.URL, error) { 82 addrEnv := env.Getenv("CONSUL_HTTP_ADDR") 83 c, err := url.Parse(addrEnv) 84 if err != nil { 85 return nil, errors.Wrapf(err, "invalid URL '%s' in CONSUL_HTTP_ADDR", addrEnv) 86 } 87 if c.Scheme == "" { 88 c.Scheme = u.Scheme 89 } 90 switch c.Scheme { 91 case "consul+http", http: 92 c.Scheme = http 93 case "consul+https", https: 94 c.Scheme = https 95 case "consul": 96 if conv.Bool(env.Getenv("CONSUL_HTTP_SSL")) { 97 c.Scheme = https 98 } else { 99 c.Scheme = http 100 } 101 } 102 103 if c.Host == "" && u.Host == "" { 104 c.Host = "localhost:8500" 105 } else if c.Host == "" { 106 c.Host = u.Host 107 } 108 109 return c, nil 110 } 111 112 func consulConfig(useTLS bool) (*store.Config, error) { 113 t := conv.MustAtoi(env.Getenv("CONSUL_TIMEOUT")) 114 config := &store.Config{ 115 ConnectionTimeout: time.Duration(t) * time.Second, 116 } 117 if useTLS { 118 tconf := setupTLS("CONSUL") 119 var err error 120 config.TLS, err = consulapi.SetupTLSConfig(tconf) 121 if err != nil { 122 return nil, errors.Wrapf(err, "TLS Config setup failed") 123 } 124 } 125 return config, nil 126 } 127 128 func setupTLS(prefix string) *consulapi.TLSConfig { 129 tlsConfig := &consulapi.TLSConfig{ 130 Address: env.Getenv(prefix + "_TLS_SERVER_NAME"), 131 CAFile: env.Getenv(prefix + "_CACERT"), 132 CAPath: env.Getenv(prefix + "_CAPATH"), 133 CertFile: env.Getenv(prefix + "_CLIENT_CERT"), 134 KeyFile: env.Getenv(prefix + "_CLIENT_KEY"), 135 } 136 if v := env.Getenv(prefix + "_HTTP_SSL_VERIFY"); v != "" { 137 verify := conv.Bool(v) 138 tlsConfig.InsecureSkipVerify = !verify 139 } 140 return tlsConfig 141 }