1
0
Fork 0

Merge pull request #671 from rqlite/fix_https_redirect

Set Location for HTTP redirect properly for secure nodes
master
Philip O'Toole 4 years ago committed by GitHub
commit 19f69393b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -231,8 +231,13 @@ func main() {
if httpAdv != "" {
apiAdv = httpAdv
}
apiProto := "http"
if x509Cert != "" {
apiProto = "https"
}
meta := map[string]string{
"api_addr": apiAdv,
"api_addr": apiAdv,
"api_proto": apiProto,
}
// Execute any requested join operation.

@ -326,13 +326,14 @@ func (s *Service) handleJoin(w http.ResponseWriter, r *http.Request) {
if err := s.store.Join(remoteID.(string), remoteAddr.(string), voter.(bool), m); err != nil {
if err == store.ErrNotLeader {
leader := s.leaderAPIAddr()
if leader == "" {
leaderAPIAddr := s.leaderAPIAddr()
leaderProto := s.leaderAPIProto()
if leaderAPIAddr == "" {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
redirect := s.FormRedirect(r, leader)
redirect := s.FormRedirect(r, leaderProto, leaderAPIAddr)
http.Redirect(w, r, redirect, http.StatusMovedPermanently)
return
}
@ -380,13 +381,14 @@ func (s *Service) handleRemove(w http.ResponseWriter, r *http.Request) {
if err := s.store.Remove(remoteID); err != nil {
if err == store.ErrNotLeader {
leader := s.leaderAPIAddr()
if leader == "" {
leaderAPIAddr := s.leaderAPIAddr()
leaderProto := s.leaderAPIProto()
if leaderAPIAddr == "" {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
redirect := s.FormRedirect(r, leader)
redirect := s.FormRedirect(r, leaderProto, leaderAPIAddr)
http.Redirect(w, r, redirect, http.StatusMovedPermanently)
return
}
@ -460,13 +462,14 @@ func (s *Service) handleLoad(w http.ResponseWriter, r *http.Request) {
results, err := s.store.ExecuteOrAbort(&store.ExecuteRequest{queries, timings, false})
if err != nil {
if err == store.ErrNotLeader {
leader := s.leaderAPIAddr()
if leader == "" {
leaderAPIAddr := s.leaderAPIAddr()
leaderProto := s.leaderAPIProto()
if leaderAPIAddr == "" {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
redirect := s.FormRedirect(r, leader)
redirect := s.FormRedirect(r, leaderProto, leaderAPIAddr)
http.Redirect(w, r, redirect, http.StatusMovedPermanently)
return
}
@ -607,13 +610,14 @@ func (s *Service) handleExecute(w http.ResponseWriter, r *http.Request) {
results, err := s.store.Execute(&store.ExecuteRequest{queries, timings, isTx})
if err != nil {
if err == store.ErrNotLeader {
leader := s.leaderAPIAddr()
if leader == "" {
leaderAPIAddr := s.leaderAPIAddr()
leaderProto := s.leaderAPIProto()
if leaderAPIAddr == "" {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
redirect := s.FormRedirect(r, leader)
redirect := s.FormRedirect(r, leaderProto, leaderAPIAddr)
http.Redirect(w, r, redirect, http.StatusMovedPermanently)
return
}
@ -675,13 +679,14 @@ func (s *Service) handleQuery(w http.ResponseWriter, r *http.Request) {
results, err := s.store.Query(&store.QueryRequest{queries, timings, isTx, lvl, frsh})
if err != nil {
if err == store.ErrNotLeader {
leader := s.leaderAPIAddr()
if leader == "" {
leaderAPIAddr := s.leaderAPIAddr()
leaderProto := s.leaderAPIProto()
if leaderAPIAddr == "" {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
redirect := s.FormRedirect(r, leader)
redirect := s.FormRedirect(r, leaderProto, leaderAPIAddr)
http.Redirect(w, r, redirect, http.StatusMovedPermanently)
return
}
@ -738,11 +743,7 @@ func (s *Service) Addr() net.Addr {
}
// FormRedirect returns the value for the "Location" header for a 301 response.
func (s *Service) FormRedirect(r *http.Request, host string) string {
protocol := "http"
if s.credentialStore != nil {
protocol = "https"
}
func (s *Service) FormRedirect(r *http.Request, protocol, host string) string {
rq := r.URL.RawQuery
if rq != "" {
rq = fmt.Sprintf("?%s", rq)
@ -772,6 +773,19 @@ func (s *Service) leaderAPIAddr() string {
return s.store.Metadata(id, "api_addr")
}
func (s *Service) leaderAPIProto() string {
id, err := s.store.LeaderID()
if err != nil {
return "http"
}
p := s.store.Metadata(id, "api_proto")
if p == "" {
return "http"
}
return "https"
}
// addBuildVersion adds the build version to the HTTP response.
func (s *Service) addBuildVersion(w http.ResponseWriter) {
// Add version header to every response, if available.

@ -446,12 +446,12 @@ func Test_RegisterStatus(t *testing.T) {
func Test_FormRedirect(t *testing.T) {
m := &MockStore{}
s := New("127.0.0.1:0", m, nil)
req := mustNewHTTPRequest("http://foo:4001")
req := mustNewHTTPRequest("http://qux:4001")
if rd := s.FormRedirect(req, "foo:4001"); rd != "http://foo:4001" {
if rd := s.FormRedirect(req, "http", "foo:4001"); rd != "http://foo:4001" {
t.Fatal("failed to form redirect for simple URL")
}
if rd := s.FormRedirect(req, "bar:4002"); rd != "http://bar:4002" {
if rd := s.FormRedirect(req, "http", "bar:4002"); rd != "http://bar:4002" {
t.Fatal("failed to form redirect for simple URL with new host")
}
}
@ -459,16 +459,29 @@ func Test_FormRedirect(t *testing.T) {
func Test_FormRedirectParam(t *testing.T) {
m := &MockStore{}
s := New("127.0.0.1:0", m, nil)
req := mustNewHTTPRequest("http://foo:4001/db/query?x=y")
req := mustNewHTTPRequest("http://qux:4001/db/query?x=y")
if rd := s.FormRedirect(req, "foo:4001"); rd != "http://foo:4001/db/query?x=y" {
if rd := s.FormRedirect(req, "http", "foo:4001"); rd != "http://foo:4001/db/query?x=y" {
t.Fatal("failed to form redirect for URL")
}
if rd := s.FormRedirect(req, "bar:4003"); rd != "http://bar:4003/db/query?x=y" {
if rd := s.FormRedirect(req, "http", "bar:4003"); rd != "http://bar:4003/db/query?x=y" {
t.Fatal("failed to form redirect for URL with new host")
}
}
func Test_FormRedirectHTTPS(t *testing.T) {
m := &MockStore{}
s := New("127.0.0.1:0", m, nil)
req := mustNewHTTPRequest("http://qux:4001")
if rd := s.FormRedirect(req, "https", "foo:4001"); rd != "https://foo:4001" {
t.Fatal("failed to form redirect for simple URL")
}
if rd := s.FormRedirect(req, "https", "bar:4002"); rd != "https://bar:4002" {
t.Fatal("failed to form redirect for simple URL with new host")
}
}
type MockStore struct {
executeFn func(queries []string, tx bool) ([]*sql.Result, error)
queryFn func(queries []string, tx, leader, verify bool) ([]*sql.Rows, error)

@ -43,6 +43,15 @@ func Test_MultiNodeCluster(t *testing.T) {
t.Fatalf("failed to find cluster leader: %s", err.Error())
}
// Get a follower and confirm redirects work properly.
followers, err := c.Followers()
if err != nil {
t.Fatalf("failed to get followers: %s", err.Error())
}
if len(followers) != 1 {
t.Fatalf("got incorrect number of followers: %d", len(followers))
}
node3 := mustNewNode(false)
defer node3.Deprovision()
if err := node3.Join(leader); err != nil {

Loading…
Cancel
Save