117 lines
3.2 KiB
Go
117 lines
3.2 KiB
Go
|
// Copyright (c) HashiCorp, Inc.
|
||
|
// SPDX-License-Identifier: MPL-2.0
|
||
|
|
||
|
package http
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"crypto/tls"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
|
||
|
"github.com/hashicorp/go-secure-stdlib/tlsutil"
|
||
|
"github.com/hashicorp/vault/physical/raft"
|
||
|
"github.com/hashicorp/vault/vault"
|
||
|
)
|
||
|
|
||
|
func handleSysRaftBootstrap(core *vault.Core) http.Handler {
|
||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
switch r.Method {
|
||
|
case "POST", "PUT":
|
||
|
if core.Sealed() {
|
||
|
respondError(w, http.StatusBadRequest, errors.New("node must be unsealed to bootstrap"))
|
||
|
}
|
||
|
|
||
|
if err := core.RaftBootstrap(context.Background(), false); err != nil {
|
||
|
respondError(w, http.StatusInternalServerError, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
respondError(w, http.StatusBadRequest, nil)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func handleSysRaftJoin(core *vault.Core) http.Handler {
|
||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
switch r.Method {
|
||
|
case "POST", "PUT":
|
||
|
handleSysRaftJoinPost(core, w, r)
|
||
|
default:
|
||
|
respondError(w, http.StatusMethodNotAllowed, nil)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func handleSysRaftJoinPost(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||
|
// Parse the request
|
||
|
var req JoinRequest
|
||
|
if _, err := parseJSONRequest(core.PerfStandby(), r, w, &req); err != nil && err != io.EOF {
|
||
|
respondError(w, http.StatusBadRequest, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if req.NonVoter && !nonVotersAllowed {
|
||
|
respondError(w, http.StatusBadRequest, errors.New("non-voting nodes not allowed"))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
var tlsConfig *tls.Config
|
||
|
var err error
|
||
|
if len(req.LeaderCACert) != 0 || len(req.LeaderClientCert) != 0 || len(req.LeaderClientKey) != 0 {
|
||
|
tlsConfig, err = tlsutil.ClientTLSConfig([]byte(req.LeaderCACert), []byte(req.LeaderClientCert), []byte(req.LeaderClientKey))
|
||
|
if err != nil {
|
||
|
respondError(w, http.StatusBadRequest, err)
|
||
|
return
|
||
|
}
|
||
|
tlsConfig.ServerName = req.LeaderTLSServerName
|
||
|
}
|
||
|
|
||
|
if req.AutoJoinScheme != "" && (req.AutoJoinScheme != "http" && req.AutoJoinScheme != "https") {
|
||
|
respondError(w, http.StatusBadRequest, fmt.Errorf("invalid scheme %q; must either be http or https", req.AutoJoinScheme))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
leaderInfos := []*raft.LeaderJoinInfo{
|
||
|
{
|
||
|
AutoJoin: req.AutoJoin,
|
||
|
AutoJoinScheme: req.AutoJoinScheme,
|
||
|
AutoJoinPort: req.AutoJoinPort,
|
||
|
LeaderAPIAddr: req.LeaderAPIAddr,
|
||
|
TLSConfig: tlsConfig,
|
||
|
Retry: req.Retry,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
joined, err := core.JoinRaftCluster(context.Background(), leaderInfos, req.NonVoter)
|
||
|
if err != nil {
|
||
|
respondError(w, http.StatusInternalServerError, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
resp := JoinResponse{
|
||
|
Joined: joined,
|
||
|
}
|
||
|
respondOk(w, resp)
|
||
|
}
|
||
|
|
||
|
type JoinResponse struct {
|
||
|
Joined bool `json:"joined"`
|
||
|
}
|
||
|
|
||
|
type JoinRequest struct {
|
||
|
AutoJoin string `json:"auto_join"`
|
||
|
AutoJoinScheme string `json:"auto_join_scheme"`
|
||
|
AutoJoinPort uint `json:"auto_join_port"`
|
||
|
LeaderAPIAddr string `json:"leader_api_addr"`
|
||
|
LeaderCACert string `json:"leader_ca_cert"`
|
||
|
LeaderClientCert string `json:"leader_client_cert"`
|
||
|
LeaderClientKey string `json:"leader_client_key"`
|
||
|
LeaderTLSServerName string `json:"leader_tls_servername"`
|
||
|
Retry bool `json:"retry"`
|
||
|
NonVoter bool `json:"non_voter"`
|
||
|
}
|