Skip to content

Commit

Permalink
SHIELD Core Rekeying Support
Browse files Browse the repository at this point in the history
The API now features a rekeying endpoint for re-encrypting the Vault credentials with a new master password.

The CLI now features a `shield rekey` command that facilitates calling the API to do the rekeying.

The Web UI now features a `#!/admin/rekey-master` page for effecting a rekey operation from the front-end.

As an added bonus, the UI source code has been modified to better handle folding in vscode, if you're into that sort of thing.
  • Loading branch information
daviddob authored and jhunt committed Oct 3, 2017
1 parent 46449d0 commit 6034037
Show file tree
Hide file tree
Showing 7 changed files with 3,497 additions and 3,275 deletions.
28 changes: 28 additions & 0 deletions api/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,31 @@ func Init(master string) error {

return nil
}

func Rekey(curmaster string, newmaster string) error {
uri, err := ShieldURI("/v2/rekey-master")
if err != nil {
return err
}

respMap := make(map[string]string)
creds := struct {
CurMaster string `json:"current_master_password"`
NewMaster string `json:"new_master_password"`
}{
CurMaster: curmaster,
NewMaster: newmaster,
}
contentJSON, err := json.Marshal(creds)
if err != nil {
return err
}
if err := uri.Post(&respMap, string(contentJSON)); err != nil {
if rekey_error, present := respMap["error"]; present {
return errors.New(rekey_error)
}
return err
}

return nil
}
2 changes: 1 addition & 1 deletion cmd/shield/commands/access/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func cliInit(opts *commands.Options, args ...string) error {
master = a
break
}
ansi.Fprintf(os.Stderr, "\n@Y{oops, try again }(Ctrl-C to cancel)\n\n")
ansi.Fprintf(os.Stderr, "\n@Y{oops, passwords do not match: try again }(Ctrl-C to cancel)\n\n")
}

if err := api.Init(master); err != nil {
Expand Down
47 changes: 47 additions & 0 deletions cmd/shield/commands/access/rekey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package access

import (
"os"

"github.com/starkandwayne/goutils/ansi"
"github.com/starkandwayne/shield/api"
"github.com/starkandwayne/shield/cmd/shield/commands"
"github.com/starkandwayne/shield/cmd/shield/commands/internal"
"github.com/starkandwayne/shield/cmd/shield/log"
"golang.org/x/crypto/ssh/terminal"
)

//Rekey - Rekeys the encryption database keys
var Rekey = &commands.Command{
Summary: "Rekey the encryption database keys",
Help: &commands.HelpInfo{},
RunFn: cliRekey,
Group: commands.AccessGroup,
}

func cliRekey(opts *commands.Options, args ...string) error {
log.DEBUG("running 'rekey' command")

internal.Require(len(args) == 0, "USAGE: shield rekey")

curmaster := SecurePrompt("%s @Y{[hidden]:} ", "current_master_password")

newmaster := ""
for {
a := SecurePrompt("%s @Y{[hidden]:} ", "master_password")
b := SecurePrompt("%s @C{[confirm]:} ", "master_password")

if a != "" && (a == b || !terminal.IsTerminal(int(os.Stdin.Fd()))) {
ansi.Fprintf(os.Stderr, "\n")
newmaster = a
break
}
ansi.Fprintf(os.Stderr, "\n@Y{oops, passwords do not match: try again }(Ctrl-C to cancel)\n\n")
}
if err := api.Rekey(curmaster, newmaster); err != nil {
return err
}

commands.OK("Successfully rekeyed the encryption database")
return nil
}
1 change: 1 addition & 0 deletions cmd/shield/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ func addCommands() {

cmds.Add("unlock", access.Unlock).AKA("unseal")
cmds.Add("init", access.Init).AKA("initialize")
cmds.Add("rekey", access.Rekey).AKA("rekey-master")

}

Expand Down
40 changes: 40 additions & 0 deletions core/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ func (core *Core) ServeHTTP(w http.ResponseWriter, req *http.Request) {
core.v2Init(w, req)
return

case match(req, `POST /v2/rekey-master`):
core.v2Rekey(w, req)
return

//All api endpoints below have the mustBeUnlocked requirement such that if vault
// is sealed or uninitialized they will return a 403
case match(req, `GET /v1/meta/pubkey`):
Expand Down Expand Up @@ -2189,3 +2193,39 @@ func (core *Core) v2Init(w http.ResponseWriter, req *http.Request) {
}
JSONLiteral(w, `{"ok":"Initialized Key Database"}`)
}

func (core *Core) v2Rekey(w http.ResponseWriter, req *http.Request) {
if req.Body == nil {
w.WriteHeader(400)
return
}
var params struct {
CurMaster string `json:"current_master_password"`
NewMaster string `json:"new_master_password"`
}
if err := json.NewDecoder(req.Body).Decode(&params); err != nil && err != io.EOF {
bailWithError(w, ClientErrorf("bad JSON payload: %s", err))
return
}
e := MissingParameters()
e.Check("current_master_password", params.CurMaster)
e.Check("new_master_password", params.NewMaster)
if e.IsValid() {
bailWithError(w, e)
return
}

sealCreds, err := core.vault.ReadConfig(core.vaultKeyfile, params.CurMaster)
if err != nil {
bailWithError(w, ClientErrorf("%s", err))
return
}

err = core.vault.WriteConfig(core.vaultKeyfile, params.NewMaster, sealCreds)
if err != nil {
bailWithError(w, ClientErrorf("%s", err))
return
}

JSONLiteral(w, `{"ok":"Shield encryption database has been rekeyed successfully"}`)
}
5 changes: 4 additions & 1 deletion crypter/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ func (vault *Vault) Init(store string, master string) error {
RootToken: keys.RootToken,
}

vault.WriteConfig(store, master, creds)
err = vault.WriteConfig(store, master, creds)
if err != nil {
return err
}

vault.Token = creds.RootToken
return vault.Unseal(creds.SealKey)
Expand Down
Loading

0 comments on commit 6034037

Please sign in to comment.