Example #1
0
func realMain() int {
	var mode string
	var value string
	var keystore string
	var keyname string
	var rotate bool
	var fileName string
	flag.Usage = usage
	flag.StringVar(
		&mode, "mode", "encrypt",
		"mode of operation, either keygen, encrypt, or decrypt; defaults to encrypt")
	flag.StringVar(
		&value, "value", "",
		"value to encrypt/decrypt in lieu of file")
	flag.StringVar(
		&keystore, "keystore", "/keys/",
		"directory in which keys are stored")
	flag.StringVar(
		&keyname, "key", "",
		"name of a key file to use for encryption")
	flag.BoolVar(
		&rotate, "rotate", true,
		"if encrypting, whether to rotate any already-encrypted tags to the new key")
	flag.Parse()
	if value == "" {
		if flag.NArg() != 1 {
			flag.Usage()
			return 1
		} else {
			fileName = flag.Args()[0]
		}
	}
	if mode == "encrypt" {
		if keyname == "" {
			fmt.Println("A -key must be provided for encryption")
			return 2
		}
		rawBytes := getBytes(value, fileName)

		fileContents, err := gosecret.EncryptTags(rawBytes, keyname, keystore, rotate)
		if err != nil {
			fmt.Println("encryption failed", err)
			return 4
		}

		data := string(fileContents)

		// Create a template, add the function map, and parse the text.
		// FuncMap maps the goEncrypt (and goDecrypt below) names to functions so that the
		// template recognizes and knows what to do when it encounters such tags during parsing
		funcs := template.FuncMap{
			// Template functions
			"goEncrypt": goEncryptFunc(keystore),
		}

		tmpl, err := template.New("encryption").Funcs(funcs).Parse(data)
		if err != nil {
			fmt.Println("Could not parse template", err)
			return 99
		}

		// Run the template to verify the output.
		buff := new(bytes.Buffer)
		err = tmpl.Execute(buff, nil)
		if err != nil {
			fmt.Println("Could not execute template", err)
			return 98
		}

		fmt.Printf(string(buff.Bytes()))

	} else if mode == "decrypt" {
		rawBytes := getBytes(value, fileName)
		fileContents, err := gosecret.DecryptTags(rawBytes, keystore)
		if err != nil {
			fmt.Println("err", err)
			return 8
		}

		data := string(fileContents)

		funcs := template.FuncMap{
			// Template functions
			"goDecrypt": goDecryptFunc(keystore),
		}

		tmpl, err := template.New("decryption").Funcs(funcs).Parse(data)
		if err != nil {
			fmt.Println("Could not parse template", err)
			return 99
		}

		// Run the template to verify the output.
		buff := new(bytes.Buffer)
		err = tmpl.Execute(buff, nil)
		if err != nil {
			fmt.Println("Could not execute template", err)
			return 98
		}

		fmt.Printf(string(buff.Bytes()))

	} else if mode == "keygen" {
		key := gosecret.CreateKey()
		encodedKey := make([]byte, base64.StdEncoding.EncodedLen(len(key)))
		base64.StdEncoding.Encode(encodedKey, key)
		ioutil.WriteFile(fileName, encodedKey, 0666)
	} else {
		fmt.Println("Unknown mode", mode)
		return 16
	}

	return 0
}
Example #2
0
// Connects to Consul and watches a given K/V prefix and uses that to
// write to the filesystem.
func watchMappingAndExec(config *WatchConfig, mappingConfig *MappingConfig) (int, error) {
	client, err := buildConsulClient(config.Consul)
	if err != nil {
		return 0, err
	}

	// If prefix starts with /, trim it.
	if mappingConfig.Prefix[0] == '/' {
		mappingConfig.Prefix = mappingConfig.Prefix[1:]
	}

	// If the config path is lacking a trailing separator, add it.
	if mappingConfig.Path[len(mappingConfig.Path)-1] != os.PathSeparator {
		mappingConfig.Path += string(os.PathSeparator)
	}

	isWindows := os.PathSeparator != '/'

	// Remove an unhandled trailing quote, which presented itself on Windows when
	// the given path contained spaces (requiring quotes) and also had a trailing
	// backslash.
	if mappingConfig.Path[len(mappingConfig.Path)-1] == 34 {
		mappingConfig.Path = mappingConfig.Path[:len(mappingConfig.Path)-1]
	}

	// Start the watcher goroutine that watches for changes in the
	// K/V and notifies us on a channel.
	errCh := make(chan error, 1)
	pairCh := make(chan consulapi.KVPairs)
	quitCh := make(chan struct{})

	// Defer close of quitCh if we're running more than once
	if !config.RunOnce {
		defer close(quitCh)
	}

	go watch(
		client, mappingConfig.Prefix, mappingConfig.Path, config.Consul.Token, pairCh, errCh, quitCh)

	var env map[string]string
	for {
		var pairs consulapi.KVPairs

		// Wait for new pairs to come on our channel or an error
		// to occur.
		select {
		case pairs = <-pairCh:
		case err := <-errCh:
			return 0, err
		}

		newEnv := make(map[string]string)
		for _, pair := range pairs {
			log.WithFields(log.Fields{
				"key": pair.Key,
			}).Debug("Key present in source")
			k := strings.TrimPrefix(pair.Key, mappingConfig.Prefix)
			k = strings.TrimLeft(k, "/")
			newEnv[k] = string(pair.Value)
		}

		// If the variables didn't actually change,
		// then don't do anything.
		if reflect.DeepEqual(env, newEnv) {
			continue
		}

		// Iterate over all objects in the current env.  If they are not in the newEnv, they
		// were deleted from Consul and should be deleted from disk.
		for k := range env {
			if _, ok := newEnv[k]; !ok {
				log.WithFields(log.Fields{
					"key": k,
				}).Debug("Key no longer present locally")
				// Write file to disk
				keyfile := fmt.Sprintf("%s%s", mappingConfig.Path, k)
				if isWindows {
					keyfile = strings.Replace(keyfile, "/", "\\", -1)
				}

				err := os.Remove(keyfile)
				if err != nil {
					log.WithFields(log.Fields{
						"error": err,
					}).Error("Failed to remove key")
				}
			}
		}

		// Replace the env so we can detect future changes
		env = newEnv

		// Write the updated keys to the filesystem at the specified path
		for k, v := range newEnv {
			// Write file to disk
			keyfile := fmt.Sprintf("%s%s", mappingConfig.Path, k)

			// if Windows, replace / with windows path delimiter
			if isWindows {
				keyfile = strings.Replace(keyfile, "/", "\\", -1)
				// mkdirp the file's path
				err := mkdirp.Mk(keyfile[:strings.LastIndex(keyfile, "\\")], 0777)
				if err != nil {
					log.WithFields(log.Fields{
						"error": err,
					}).Error("Failed to create parent directory for key")
				}
			} else {
				// mkdirp the file's path
				err := mkdirp.Mk(keyfile[:strings.LastIndex(keyfile, "/")], 0777)
				if err != nil {
					log.WithFields(log.Fields{
						"error": err,
					}).Error("Failed to create parent directory for key")
				}
			}

			f, err := os.Create(keyfile)
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"file":  keyfile,
				}).Error("Failed to create file")
				continue
			}

			defer f.Close()

			log.WithFields(log.Fields{
				"length": len(v),
			}).Debug("Input value length")

			buff := new(bytes.Buffer)
			buff.Write([]byte(v))

			if len(mappingConfig.Keystore) > 0 {
				decryptedValue, err := gosecret.DecryptTags([]byte(v), mappingConfig.Keystore)
				if err != nil {
					log.WithFields(log.Fields{
						"error": err,
					}).Error("Failed to decrypt value")
					continue
				}

				log.WithFields(log.Fields{
					"length": len(decryptedValue),
				}).Debug("Output value length")

				data := string(decryptedValue)

				funcs := template.FuncMap{
					// Template functions
					"goDecrypt": goDecryptFunc(mappingConfig.Keystore),
				}

				tmpl, err := template.New("decryption").Funcs(funcs).Parse(data)
				if err != nil {
					log.WithFields(log.Fields{
						"error": err,
					}).Error("Could not parse template")
					continue
				}

				// Run the template to verify the output.
				buff = new(bytes.Buffer)
				err = tmpl.Execute(buff, nil)
				if err != nil {
					log.WithFields(log.Fields{
						"error": err,
					}).Error("Could not execute template")
					continue
				}
			}

			wrote, err := f.Write(buff.Bytes())
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"file":  keyfile,
				}).Error("Failed to write to file")
				continue
			}

			log.WithFields(log.Fields{
				"length": wrote,
				"file":   keyfile,
			}).Debug("Successfully wrote value to file")

			err = f.Sync()
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"file":  keyfile,
				}).Error("Failed to sync file")
			}

			err = f.Close()
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"file":  keyfile,
				}).Error("Failed to close file")
			}
		}

		// Configuration changed, run our onchange command, if one was specified.
		if mappingConfig.OnChange != nil {
			var cmd = exec.Command(mappingConfig.OnChange[0], mappingConfig.OnChange[1:]...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			// Always wait for the forked process to exit.  We may wish to revisit this, but I think
			// it's the safest approach since it avoids a case where rapid key updates DOS a system
			// by slurping all proc handles.
			err = cmd.Run()

			if err != nil {
				return 111, err
			}
		}

		// If we are only running once, close the channel on this watcher.
		if config.RunOnce {
			close(quitCh)
			return 0, nil
		}
	}
}