├── .gitignore ├── .goxc.json ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── root.go ├── status.go └── version.go ├── example └── config.yaml ├── glide.lock ├── glide.yaml ├── gpg └── decrypt.go ├── homebrew └── unseal.rb ├── main.go └── vault ├── client.go ├── status.go └── types.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Vendor ops 27 | vendor/ 28 | dist 29 | downloads 30 | 31 | config.yaml 32 | .goxc.local.json 33 | -------------------------------------------------------------------------------- /.goxc.json: -------------------------------------------------------------------------------- 1 | { 2 | "AppName": "unseal", 3 | "ArtifactsDest": "downloads", 4 | "Tasks": [ 5 | "interpolate-source", 6 | "xc", 7 | "archive-zip", 8 | "archive-tar-gz", 9 | "publish-github" 10 | ], 11 | "BuildConstraints": "linux,amd64 windows,amd64 darwin,amd64", 12 | "ResourcesInclude": "README*", 13 | "BuildSettings": { 14 | "LdFlagsXVars": { 15 | "Version": "main.Version" 16 | } 17 | }, 18 | "TaskSettings": { 19 | "publish-github": { 20 | "apikey": "", 21 | "body": "See: https://github.com/jaxxstorm/unseal/blog/master/CHANGELOG.md", 22 | "include": "*.zip,*.tar.gz", 23 | "outputFormat": "by-file-extension", 24 | "owner": "jaxxstorm", 25 | "repository": "unseal" 26 | } 27 | }, 28 | "ConfigVersion": "0.9" 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2017 Lee Briggs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | @rm -rf ./dist 3 | 4 | build: clean 5 | @goxc -pv=$(version) 6 | 7 | version: 8 | @echo $(RELEASE) 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unseal 2 | 3 | Unseal is a small, simple go binary that takes a yaml config file and unseals vault servers. 4 | 5 | # Status 6 | 7 | Unseal will not be accepting any new features. All future work will be rolled into [hookpick](https://github.com/jaxxstorm/hookpick) 8 | 9 | # Why? 10 | 11 | When initially deploying vault across multiple sites, you're probably deploying it in a HA config (ie with multiple vault servers in the cluster) and you'll need several people to unseal all of them to get started. This got quite annoying over multiple vault servers and multiple sites, so in order to speed it up, I wrote this little tool. 12 | 13 | # Features 14 | 15 | Some of the advantages you might gain over using the vault HTTP API or the standard vault binary 16 | 17 | - Zero touch interaction. Once you've written your yaml config, you can simply invoke the command and it'll unseal all the servers that need to be unsealed 18 | - Parallel execution. Each unseal command runs in a goroutine, meaning you can unseal multiple servers in a matter of seconds 19 | - Overwriting of unseal key stored in memory. The unseal key you use is zeroed out when the unseal operation is completed, meaning it can't be hijacked by malware etc (see considerations for more info) 20 | 21 | # Usage 22 | 23 | In order to use unseal, simply create a config file. Here's an example: 24 | 25 | 26 | ```yaml 27 | hosts: 28 | - name: vault-server-1 29 | port: 8200 30 | key: 31 | - name: vault-server-2 32 | port: 8200 33 | key: 34 | - name: different-site-vault-server.example.com 35 | port: 8200 36 | key: 37 | ``` 38 | 39 | The app will look for the config file in the following directories, in order: 40 | 41 | - `/etc/unseal/config.yaml` 42 | - `$HOME/.unseal/config.yaml` 43 | - `config.yaml` (in the directory you're running the binary from) 44 | 45 | Once that's done, simply run the binary: 46 | 47 | ```bash 48 | ./unseal 49 | INFO[0007] Unseal operation performed host=site1-consulserver-1 progress=2 threshold=3 50 | INFO[0007] Unseal operation performed host=site1-consulserver-2 progress=2 threshold=3 51 | INFO[0008] Unseal operation performed host=site1-consulserver-3 progress=2 threshold=3 52 | INFO[0008] Vault is unsealed! host=site2-consulserver-2 progress=0 threshold=3 53 | INFO[0008] Vault is unsealed! host=site2-consulserver-1 progress=0 threshold=3 54 | INFO[0008] Vault is unsealed! host=site2-consulserver-3 progress=0 threshold=3 55 | INFO[0008] Vault is unsealed! host=site3-consulserver-1 progress=0 threshold=3 56 | INFO[0008] Vault is unsealed! host=site3-consulserver-3 progress=0 threshold=3 57 | INFO[0008] Vault is unsealed! host=site3-consulserver-2 progress=0 threshold=3 58 | ``` 59 | 60 | Your vault server progress is now 1 of 3. Yay! 61 | 62 | ## GPG Support 63 | 64 | While you _can_ of course store the unseal keys in plaintext in your `config.yaml` - *it is a really bad idea*. 65 | 66 | With that in mind, Unseal supports GPG decryption. If you've initialized your Vault servers using [PGP/GPG](https://www.vaultproject.io/docs/concepts/pgp-gpg-keybase.html) (and in my opinion, you really _should_) you can specify the base64 encrypted unseal token for your host, and `unseal` will prompt you for your GPG passphrase to decrypt the key. 67 | 68 | An example config would look like this: 69 | ``` 70 | gpg: true 71 | hosts: 72 | - name: test 73 | - port: 8200 74 | - key: 75 | ``` 76 | 77 | **Note** - if you have a GPG agent running and you've put the unseal keys in your `config.yaml` - anyone with access to your machine can easily decrypt the values without having to know your GPG password. Be warned. 78 | 79 | ### Troubleshooting 80 | 81 | Unseal simply executes the gpg command to decrypt keys. If you're having any issues with GPG support, I'd suggest doing the following: 82 | 83 | 1) Ensure you can decrypt the keys manually. Use `echo | base64 -D | gpg -dq`. If this doesn't work, unseal won't work either 84 | 2) Ensure you have gpg-agent running, and have a valid `gpg-agent.conf` 85 | 3) Ensure your key is a valid base64 encoded string. Again, `echo | base64 -D | gpg -dq` will verify this 86 | 87 | ## CAPath 88 | 89 | Unseal does not support unsecured HTTP API calls, and you probably shouldn't be using Vault over HTTP anyway :) 90 | 91 | All your vault servers may use different CA certs, so you can specify a directory with CA certs in it which vault will read and use to attempt to verify the vault server. 92 | 93 | Simple specify it like this in your config file: 94 | 95 | ```yaml 96 | capath: "/path/to/ca/certs" 97 | hosts: 98 | - name: test 99 | - port: 8200 100 | - key: 101 | ``` 102 | 103 | ## Environment Variables 104 | 105 | By default, vault will read some environment variables to do the unseal config. You can find them [here](https://www.vaultproject.io/docs/commands/environment.html) 106 | 107 | You can use _some_ of these environment variables if you wish when using unseal. 108 | 109 | - `VAULT_CACERT`: Set this to the path of a CA Cert you wish to use to verify the vault connection. Note, this will use the same CA cert for all vaults 110 | - `VAULT_CAPATH`: An alternative to the above CA Path config option. 111 | - `VAULT_CLIENT_CERT`: An SSL client cert to use when connecting to your vaults. Note, this will use the same cert for all vaults 112 | - `VAULT_CLIENT_KEY`: An SSL client key to use when connecting to your vaults. Note, this will use the same key for all vaults 113 | - `VAULT_SKIP_VERIFY`: Skip SSL verification. This is not recommended in production use. 114 | 115 | # Considerations 116 | 117 | A few security considerations before you use this tool. 118 | 119 | - Your unseal key is clearly stored in plaintext in the yaml file. This is clearly a security issue. Please don't store your unseal key in plaintext permanantly. 120 | - While I've taken steps to overwrite the unseal key in memory, I am not a Golang expert and it may not be fool proof. If you think you can improve the implementation, pull requests will be warmly welcomed 121 | - I am just getting started with Golang, and therefore there may be errors, security issues and gremlins in this code. Again, pull requests are much appreciated. 122 | - There is currently no way of setting HTTPS certificates, so you must trust the certificate presented by the vault API 123 | 124 | 125 | # Building 126 | 127 | If you want to contribute, we use [glide](https://glide.sh/) for dependency management, so it should be as simple as: 128 | 129 | - cloning this repo into `$GOPATH/src/github.com/jaxxstorm/unseal` 130 | - run `glide install` from the directory 131 | - run `go build -o unseal main.go` 132 | 133 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Lee Briggs 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package cmd 22 | 23 | import ( 24 | "fmt" 25 | "os" 26 | "sync" 27 | 28 | g "github.com/jaxxstorm/unseal/gpg" 29 | v "github.com/jaxxstorm/unseal/vault" 30 | 31 | log "github.com/Sirupsen/logrus" 32 | "github.com/spf13/cobra" 33 | "github.com/spf13/viper" 34 | ) 35 | 36 | var cfgFile string 37 | var unsealKey string 38 | var vaultHost string 39 | var vaultPort int 40 | 41 | var caPath string 42 | 43 | type Host struct { 44 | Name string 45 | Port int 46 | Key string 47 | } 48 | 49 | var hosts []Host 50 | 51 | var Version string 52 | 53 | // RootCmd represents the base command when called without any subcommands 54 | var RootCmd = &cobra.Command{ 55 | Use: "unseal", 56 | Short: "Unseal a set of vault servers", 57 | Long: `Unseal allows you to unseal a large set of vault servers using the HTTP API.`, 58 | 59 | // Uncomment the following line if your bare application 60 | // has an action associated with it: 61 | Run: func(cmd *cobra.Command, args []string) { 62 | 63 | // unmarshal config file 64 | err := viper.UnmarshalKey("hosts", &hosts) 65 | 66 | // check for valid config file 67 | if err != nil { 68 | log.Fatal("Unable to read hosts key in config file: %s", err) 69 | } 70 | 71 | caPath = viper.GetString("capath") 72 | 73 | gpg := viper.GetBool("gpg") 74 | 75 | if gpg == true { 76 | log.Info("Using GPG") 77 | } 78 | 79 | if os.Getenv("VAULT_ADDR") != "" { 80 | log.Warning("Warning VAULT_ADDR environment variable is set. This will override the hostname in your config file, it's probably not what you want") 81 | } 82 | 83 | var wg sync.WaitGroup 84 | 85 | for _, h := range hosts { 86 | 87 | hostName := h.Name 88 | hostPort := h.Port 89 | key := h.Key 90 | 91 | var vaultKey string 92 | 93 | if gpg == true { 94 | vaultKey, err = g.Decrypt(key) 95 | if err != nil { 96 | log.Fatal("GPG Decrypt Error: ", err) 97 | } 98 | } else { 99 | vaultKey = key 100 | } 101 | 102 | wg.Add(1) 103 | 104 | go func(hostName string, hostPort int) { 105 | defer wg.Done() 106 | 107 | // create a vault client 108 | client, err := v.VaultClient(hostName, hostPort, caPath) 109 | if err != nil { 110 | log.WithFields(log.Fields{"host": hostName, "port": hostPort}).Error(err) 111 | } 112 | // get the current status 113 | init := v.InitStatus(client) 114 | if init.Ready == true { 115 | if vaultKey != "" { 116 | result, err := client.Sys().Unseal(vaultKey) 117 | // error while unsealing 118 | if err != nil { 119 | log.WithFields(log.Fields{"host": hostName}).Error("Error running unseal operation") 120 | } 121 | 122 | // if it's still sealed, print the progress 123 | if result.Sealed == true { 124 | log.WithFields(log.Fields{"host": hostName, "progress": result.Progress, "threshold": result.T}).Info("Unseal operation performed") 125 | // otherwise, tell us it's unsealed! 126 | } else { 127 | log.WithFields(log.Fields{"host": hostName, "progress": result.Progress, "threshold": result.T}).Info("Vault is unsealed!") 128 | } 129 | // zero out the key 130 | // FIXME: is this the best way to do this? 131 | // Is it safe? 132 | key = "" 133 | } else { 134 | log.WithFields(log.Fields{"host": hostName}).Error("No Key Provided") 135 | } 136 | } else { 137 | // sad times, not ready to be unsealed 138 | log.WithFields(log.Fields{"host": hostName}).Error("Vault is not ready to be unsealed") 139 | } 140 | 141 | }(hostName, hostPort) 142 | 143 | } 144 | wg.Wait() 145 | }, 146 | } 147 | 148 | // Execute adds all child commands to the root command sets flags appropriately. 149 | // This is called by main.main(). It only needs to happen once to the rootCmd. 150 | func Execute(version string) { 151 | Version = version 152 | if err := RootCmd.Execute(); err != nil { 153 | fmt.Println(err) 154 | os.Exit(-1) 155 | } 156 | } 157 | 158 | func init() { 159 | cobra.OnInitialize(initConfig) 160 | 161 | // define flags 162 | RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.unseal/config.yaml)") 163 | } 164 | 165 | // initConfig reads in config file and ENV variables if set. 166 | func initConfig() { 167 | if cfgFile != "" { // enable ability to specify config file via flag 168 | viper.SetConfigFile(cfgFile) 169 | } else { 170 | viper.SetConfigName("config") // name of config file (without extension) 171 | viper.AddConfigPath("/etc/unseal") 172 | viper.AddConfigPath("$HOME/.unseal") // adding home directory as first search path 173 | viper.AddConfigPath(".") 174 | viper.AutomaticEnv() // read in environment variables that match 175 | } 176 | 177 | // If a config file is found, read it in. 178 | if err := viper.ReadInConfig(); err != nil { 179 | log.Fatal("Error reading config file: ", err) 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /cmd/status.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "sync" 6 | 7 | log "github.com/Sirupsen/logrus" 8 | v "github.com/jaxxstorm/unseal/vault" 9 | "github.com/spf13/cobra" 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | func init() { 14 | RootCmd.AddCommand(statusCmd) 15 | } 16 | 17 | var statusCmd = &cobra.Command{ 18 | Use: "status", 19 | Short: "Print the vault status for all configured vaults", 20 | Long: `Print the current status for all configured vaults, without passing a key`, 21 | Run: func(cmd *cobra.Command, args []string) { 22 | 23 | err := viper.UnmarshalKey("hosts", &hosts) 24 | 25 | caPath = viper.GetString("capath") 26 | 27 | // check for valid config file 28 | if err != nil { 29 | log.Fatal("Unable to read hosts key in config file: %s", err) 30 | } 31 | 32 | if os.Getenv("VAULT_ADDR") != "" { 33 | log.Warning("Warning VAULT_ADDR environment variable is set. This will override the hostname in your config file, it's probably not what you want") 34 | } 35 | 36 | var wg sync.WaitGroup 37 | 38 | // loop through the hosts 39 | for _, h := range hosts { 40 | 41 | // set hostnames for waitgroup 42 | hostName := h.Name 43 | hostPort := h.Port 44 | 45 | wg.Add(1) 46 | 47 | go func(hostName string, hostPort int) { 48 | defer wg.Done() 49 | 50 | // create a vault client 51 | client, err := v.VaultClient(hostName, hostPort, caPath) 52 | 53 | // issue creating vault client for this host 54 | if err != nil { 55 | log.WithFields(log.Fields{"host": hostName}).Error("Error creating vault client: ", err) 56 | } 57 | 58 | // get the seal status 59 | result, err := client.Sys().SealStatus() 60 | 61 | if err != nil { 62 | log.WithFields(log.Fields{"host": hostName}).Error("Error getting seal status: ", err) 63 | } else { 64 | // only check the seal status if we have a client 65 | if result.Sealed == true { 66 | log.WithFields(log.Fields{"host": hostName, "progress": result.Progress, "threshold": result.T}).Error("Vault is sealed!") 67 | } else { 68 | log.WithFields(log.Fields{"host": hostName, "progress": result.Progress, "threshold": result.T}).Info("Vault is unsealed!") 69 | } 70 | } 71 | 72 | }(hostName, hostPort) 73 | } 74 | wg.Wait() 75 | 76 | }, 77 | } 78 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func init() { 9 | RootCmd.AddCommand(versionCmd) 10 | } 11 | 12 | var versionCmd = &cobra.Command{ 13 | Use: "version", 14 | Short: "Print the version number of unseal", 15 | Long: `All software has versions. This is unseal's`, 16 | Run: func(cmd *cobra.Command, args []string) { 17 | fmt.Println(RootCmd.Use + " " + Version) 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /example/config.yaml: -------------------------------------------------------------------------------- 1 | # Should include all the PEM encoded certs 2 | # for all your vault servers 3 | capath: "/etc/pki/tls/cacerts" 4 | # GPG enabled. Set to false for off 5 | gpg: true 6 | # Path to GPG secret keyring 7 | gpgsecretkeyring: "/Users/jaxxstorm/.gnupg/secring.gpg" 8 | # Path to GPG public keyring 9 | gpgpublickeyring: "/Users/jaxxstorm/.gnupg/pubring.gpg" 10 | # An array of keys containing hosts. 11 | # Keys and ports can differ per host 12 | hosts: 13 | - name: consulserver1 14 | port: 8200 15 | key: YV92ZXJ5X2xvbmdfc2FtcGxlX2tleV90aGF0X2lzX25vdF9hbl9hY3R1YWxfdmF1bHRfa2V5Cg 16 | - name: consulserver2 17 | port: 8200 18 | key: YV92ZXJ5X2xvbmdfc2FtcGxlX2tleV90aGF0X2lzX25vdF9hbl9hY3R1YWxfdmF1bHRfa2V5Cg 19 | - name: consulserver2 20 | port: 8200 21 | key: YV92ZXJ5X2xvbmdfc2FtcGxlX2tleV90aGF0X2lzX25vdF9hbl9hY3R1YWxfdmF1bHRfa2V5Cg 22 | - name: consulserver3 23 | port: 8200 24 | key: YV92ZXJ5X2xvbmdfc2FtcGxlX2tleV90aGF0X2lzX25vdF9hbl9hY3R1YWxfdmF1bHRfa2V5Cg 25 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: de6accabb0f0b4aaa7eff1fa3d83e170eb72b806c9087c97a122d0cd7636462a 2 | updated: 2017-03-16T14:16:55.894170592Z 3 | imports: 4 | - name: github.com/bgentry/speakeasy 5 | version: 675b82c74c0ed12283ee81ba8a534c8982c07b85 6 | - name: github.com/fatih/structs 7 | version: a720dfa8df582c51dee1b36feabb906bde1588bd 8 | - name: github.com/fsnotify/fsnotify 9 | version: a904159b9206978bb6d53fcc7a769e5cd726c737 10 | - name: github.com/hashicorp/errwrap 11 | version: 7554cd9344cec97297fa6649b055a8c98c2a1e55 12 | - name: github.com/hashicorp/go-cleanhttp 13 | version: 3573b8b52aa7b37b9358d966a898feb387f62437 14 | - name: github.com/hashicorp/go-multierror 15 | version: ed905158d87462226a13fe39ddf685ea65f1c11f 16 | - name: github.com/hashicorp/go-rootcerts 17 | version: 6bb64b370b90e7ef1fa532be9e591a81c3493e00 18 | - name: github.com/hashicorp/hcl 19 | version: 372e8ddaa16fd67e371e9323807d056b799360af 20 | subpackages: 21 | - hcl/ast 22 | - hcl/parser 23 | - hcl/scanner 24 | - hcl/strconv 25 | - hcl/token 26 | - json/parser 27 | - json/scanner 28 | - json/token 29 | - name: github.com/hashicorp/vault 30 | version: ebb798ec1b2c93a4607d44438c6f56438a17b4cc 31 | subpackages: 32 | - api 33 | - helper/compressutil 34 | - helper/jsonutil 35 | - name: github.com/inconshreveable/mousetrap 36 | version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 37 | - name: github.com/magiconair/properties 38 | version: b3b15ef068fd0b17ddf408a23669f20811d194d2 39 | - name: github.com/mitchellh/go-homedir 40 | version: b8bc1bf767474819792c23f32d8286a45736f1c6 41 | - name: github.com/mitchellh/mapstructure 42 | version: db1efb556f84b25a0a13a04aad883943538ad2e0 43 | - name: github.com/pelletier/go-buffruneio 44 | version: df1e16fde7fc330a0ca68167c23bf7ed6ac31d6d 45 | - name: github.com/pelletier/go-toml 46 | version: d1fa2118c12c44e4f5004da216d1efad10cb4924 47 | - name: github.com/sethgrid/pester 48 | version: 2c5fb962da6113d0968907fd81dba3ca35151d1c 49 | - name: github.com/Sirupsen/logrus 50 | version: c078b1e43f58d563c74cebe63c85789e76ddb627 51 | - name: github.com/spf13/afero 52 | version: 72b31426848c6ef12a7a8e216708cb0d1530f074 53 | subpackages: 54 | - mem 55 | - name: github.com/spf13/cast 56 | version: d1139bab1c07d5ad390a65e7305876b3c1a8370b 57 | - name: github.com/spf13/cobra 58 | version: fcd0c5a1df88f5d6784cb4feead962c3f3d0b66c 59 | - name: github.com/spf13/jwalterweatherman 60 | version: fa7ca7e836cf3a8bb4ebf799f472c12d7e903d66 61 | - name: github.com/spf13/pflag 62 | version: 5ccb023bc27df288a957c5e994cd44fd19619465 63 | - name: github.com/spf13/viper 64 | version: 5ed0fc31f7f453625df314d8e66b9791e8d13003 65 | - name: golang.org/x/crypto 66 | version: 1f22c0103821b9390939b6776727195525381532 67 | subpackages: 68 | - cast5 69 | - openpgp 70 | - openpgp/armor 71 | - openpgp/elgamal 72 | - openpgp/errors 73 | - openpgp/packet 74 | - openpgp/s2k 75 | - name: golang.org/x/net 76 | version: e90d6d0afc4c315a0d87a568ae68577cc15149a0 77 | subpackages: 78 | - http2 79 | - http2/hpack 80 | - lex/httplex 81 | - name: golang.org/x/sys 82 | version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9 83 | subpackages: 84 | - unix 85 | - name: golang.org/x/text 86 | version: 2910a502d2bf9e43193af9d68ca516529614eed3 87 | subpackages: 88 | - transform 89 | - unicode/norm 90 | - name: gopkg.in/yaml.v2 91 | version: 53feefa2559fb8dfa8d81baad31be332c97d6c77 92 | testImports: [] 93 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/jaxxstorm/unseal 2 | import: 3 | - package: github.com/spf13/cobra 4 | - package: github.com/spf13/viper 5 | - package: github.com/bgentry/speakeasy 6 | -------------------------------------------------------------------------------- /gpg/decrypt.go: -------------------------------------------------------------------------------- 1 | package gpg 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "os/exec" 7 | ) 8 | 9 | func Decrypt(key string) (string, error) { 10 | 11 | var cmd exec.Cmd 12 | var output bytes.Buffer 13 | 14 | gpgCmd, err := exec.LookPath("gpg") 15 | 16 | if err != nil { 17 | return "", err 18 | } 19 | 20 | cmd.Path = gpgCmd 21 | cmd.Args = []string{"--decrypt", "--quiet"} 22 | 23 | dec, err := base64.StdEncoding.DecodeString(key) 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | // return the reader interface for dec (byte array) 29 | d := bytes.NewReader(dec) 30 | 31 | // pipe d to gpg commands stdin 32 | cmd.Stdin = d 33 | cmd.Stdout = &output 34 | 35 | if err := cmd.Run(); err != nil { 36 | return "", err 37 | } 38 | 39 | // return the output from the gpg command 40 | return output.String(), nil 41 | 42 | } 43 | -------------------------------------------------------------------------------- /homebrew/unseal.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 Lee Briggs 2 | # 3 | # This software is released under the MIT License. 4 | # 5 | # http://opensource.org/licenses/mit-license.php 6 | # 7 | class Unseal < Formula 8 | desc "Unseal multiple vault servers quickly and easily" 9 | homepage "https://github.com/jaxxstorm/unseal" 10 | version "v0.3-beta" 11 | 12 | if Hardware::CPU.is_64_bit? 13 | url "https://github.com/jaxxstorm/unseal/releases/download/v0.3-beta/unseal-v0.3-beta_darwin_amd64" 14 | sha256 "f25061683b741ad394efd4277bb076a7a1c095b98a53c76470da70aa37f08d8e" 15 | #else 16 | # url "https://github.com/jaxxstorm/unseal/releases/download/v0.3-beta/unseal-v0.3-beta_darwin_amd64" 17 | # sha256 "" 18 | end 19 | 20 | def install 21 | bin.install "unseal" 22 | end 23 | 24 | test do 25 | system "unseal version" 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2017 Lee Briggs 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package main 22 | 23 | import "github.com/jaxxstorm/unseal/cmd" 24 | 25 | var Version = "v0.0.1" 26 | 27 | func main() { 28 | cmd.Execute(Version) 29 | } 30 | -------------------------------------------------------------------------------- /vault/client.go: -------------------------------------------------------------------------------- 1 | package vault 2 | 3 | import ( 4 | "fmt" 5 | "github.com/hashicorp/vault/api" 6 | ) 7 | 8 | func VaultClient(hostName string, hostPort int, caPath string) (*api.Client, error) { 9 | 10 | // format the URL with the passed host and por 11 | url := fmt.Sprintf("https://%s:%v", hostName, hostPort) 12 | 13 | // create a vault config 14 | config := &api.Config{Address: url} 15 | 16 | // read in any environment variables that might be set 17 | if err := config.ReadEnvironment(); err != nil { 18 | return nil, err 19 | } 20 | 21 | // Set the CA path, if it's present 22 | if err := config.ConfigureTLS(&api.TLSConfig{CAPath: caPath}); err != nil { 23 | return nil, err 24 | } 25 | 26 | // create the client 27 | client, err := api.NewClient(config) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | return client, nil 33 | 34 | } 35 | -------------------------------------------------------------------------------- /vault/status.go: -------------------------------------------------------------------------------- 1 | package vault 2 | 3 | import ( 4 | log "github.com/Sirupsen/logrus" 5 | "github.com/hashicorp/vault/api" 6 | ) 7 | 8 | func InitStatus(client *api.Client) Status { 9 | 10 | // statuses 11 | init, err := client.Sys().InitStatus() 12 | 13 | if err != nil { 14 | log.WithFields(log.Fields{"host": client.Address()}).Error(err) 15 | return Status{ 16 | Ready: false, 17 | Reason: "Error while retrieving initstatus", 18 | } 19 | } 20 | if init == false { 21 | return Status{ 22 | Ready: false, 23 | Reason: "Vault is not initialized", 24 | } 25 | } 26 | 27 | seal, err := client.Sys().SealStatus() 28 | 29 | if err != nil { 30 | log.WithFields(log.Fields{"host": client.Address()}).Error(err) 31 | } 32 | 33 | if seal.Sealed != true { 34 | return Status{ 35 | Ready: true, 36 | Reason: "Vault is already unsealed", 37 | } 38 | } 39 | 40 | return Status{ 41 | Ready: true, 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /vault/types.go: -------------------------------------------------------------------------------- 1 | package vault 2 | 3 | // A struct to return the current status of the Vault server 4 | type Status struct { 5 | Ready bool 6 | Reason string 7 | } 8 | --------------------------------------------------------------------------------