mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
http: init endpoints
This commit is contained in:
38
api/SPEC.md
38
api/SPEC.md
@@ -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]
|
||||
|
||||
@@ -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
75
http/sys_init.go
Normal 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
90
http/sys_init_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user