http: init endpoints

This commit is contained in:
Mitchell Hashimoto
2015-03-12 12:37:41 -07:00
parent ba702b4ff4
commit 637b939113
4 changed files with 204 additions and 0 deletions

View File

@@ -89,6 +89,44 @@ The following HTTP status codes are used throughout the API.
- `503` - Vault is down for maintenance or is currently sealed.
Try again later.
# Group Initialization
## Initialization [/sys/init]
### Initialization Status [GET]
Returns the status of whether the vault is initialized or not. The
vault doesn't have to be unsealed for this operation.
+ Response 200 (application/json)
{
"initialized": true
}
### Initialize [POST]
Initialize the vault. This is an unauthenticated request to initially
setup a new vault. Although this is unauthenticated, it is still safe:
data cannot be in vault prior to initialization, and any future
authentication will fail if you didn't initialize it yourself.
Additionally, once initialized, a vault cannot be reinitialized.
This API is the only time Vault will ever be aware of your keys, and
the only time the keys will ever be returned in one unit. Care should
be taken to ensure that the output of this request is never logged,
and that the keys are properly distributed.
+ Request (application/json)
{
"secret_shares": 5,
"secret_threshold": 3,
}
+ Response 200 (application/json)
{
"keys": ["one", "two", "three"]
}
# Group Seal/Unseal
## Seal Status [/sys/seal-status]

View File

@@ -11,6 +11,7 @@ import (
// its own to mount the Vault API within another web server.
func Handler(core *vault.Core) http.Handler {
mux := http.NewServeMux()
mux.Handle("/v1/sys/init", handleSysInit(core))
mux.Handle("/v1/sys/seal-status", handleSysSealStatus(core))
mux.Handle("/v1/sys/seal", handleSysSeal(core))
mux.Handle("/v1/sys/unseal", handleSysUnseal(core))

75
http/sys_init.go Normal file
View File

@@ -0,0 +1,75 @@
package http
import (
"encoding/hex"
"net/http"
"github.com/hashicorp/vault/vault"
)
func handleSysInit(core *vault.Core) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
handleSysInitGet(core, w, r)
case "PUT":
handleSysInitPut(core, w, r)
default:
respondError(w, http.StatusMethodNotAllowed, nil)
}
})
}
func handleSysInitGet(core *vault.Core, w http.ResponseWriter, r *http.Request) {
init, err := core.Initialized()
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}
respondOk(w, &InitStatusResponse{
Initialized: init,
})
}
func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request) {
// Parse the request
var req InitRequest
if err := parseRequest(r, &req); err != nil {
respondError(w, http.StatusBadRequest, err)
return
}
// Initialize
result, err := core.Initialize(&vault.SealConfig{
SecretShares: req.SecretShares,
SecretThreshold: req.SecretThreshold,
})
if err != nil {
respondError(w, http.StatusBadRequest, err)
return
}
// Encode the keys
keys := make([]string, 0, len(result.SecretShares))
for _, k := range result.SecretShares {
keys = append(keys, hex.EncodeToString(k))
}
respondOk(w, &InitResponse{
Keys: keys,
})
}
type InitRequest struct {
SecretShares int `json:"secret_shares"`
SecretThreshold int `json:"secret_threshold"`
}
type InitResponse struct {
Keys []string `json:"keys"`
}
type InitStatusResponse struct {
Initialized bool `json:"initialized"`
}

90
http/sys_init_test.go Normal file
View File

@@ -0,0 +1,90 @@
package http
import (
"encoding/hex"
"net/http"
"reflect"
"testing"
)
func TestSysInit_get(t *testing.T) {
core := testCore(t)
ln, addr := testServer(t, core)
defer ln.Close()
{
// Pre-init
resp, err := http.Get(addr + "/v1/sys/init")
if err != nil {
t.Fatalf("err: %s", err)
}
var actual map[string]interface{}
expected := map[string]interface{}{
"initialized": false,
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}
testCoreInit(t, core)
{
// Post-init
resp, err := http.Get(addr + "/v1/sys/init")
if err != nil {
t.Fatalf("err: %s", err)
}
var actual map[string]interface{}
expected := map[string]interface{}{
"initialized": true,
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}
}
func TestSysInit_put(t *testing.T) {
core := testCore(t)
ln, addr := testServer(t, core)
defer ln.Close()
resp := testHttpPut(t, addr+"/v1/sys/init", map[string]interface{}{
"secret_shares": 5,
"secret_threshold": 3,
})
var actual map[string]interface{}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
keysRaw, ok := actual["keys"]
if !ok {
t.Fatalf("no keys: %#v", actual)
}
for _, key := range keysRaw.([]interface{}) {
keySlice, err := hex.DecodeString(key.(string))
if err != nil {
t.Fatalf("bad: %s", err)
}
if _, err := core.Unseal(keySlice); err != nil {
t.Fatalf("bad: %s", err)
}
}
seal, err := core.Sealed()
if err != nil {
t.Fatalf("err: %s", err)
}
if seal {
t.Fatal("should not be sealed")
}
}