1
0
Fork 0

Merge pull request #1430 from rqlite/join-preflight

Check that Join addresses are not serving HTTP
master
Philip O'Toole 10 months ago committed by GitHub
commit 3fe6e3f122
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -47,6 +47,7 @@ When officially released 8.0 will support (mostly) seamless upgrades from the 7.
- [PR #1413](https://github.com/rqlite/rqlite/pull/1413): Remove `-raft-no-freelist-sync` command line flag.
- [PR #1420](https://github.com/rqlite/rqlite/pull/1420): Nodes join a cluster using the Raft address, not the HTTP API.
- [PR #1426](https://github.com/rqlite/rqlite/pull/1426): 'go mod' updates, including moving to Raft 1.6.
- [PR #1430](https://github.com/rqlite/rqlite/pull/1430): Check that any supplied Join addresses are not HTTP servers.
## 7.21.4 (July 8th 2023)
### Implementation changes and bug fixes

@ -26,6 +26,7 @@ import (
"github.com/rqlite/rqlite/cmd"
"github.com/rqlite/rqlite/db"
"github.com/rqlite/rqlite/disco"
"github.com/rqlite/rqlite/http"
httpd "github.com/rqlite/rqlite/http"
"github.com/rqlite/rqlite/rtls"
"github.com/rqlite/rqlite/store"
@ -451,6 +452,10 @@ func createClusterClient(cfg *Config, clstr *cluster.Service) (*cluster.Client,
func createCluster(cfg *Config, hasPeers bool, client *cluster.Client, str *store.Store, httpServ *httpd.Service, credStr *auth.CredentialsStore) error {
joins := cfg.JoinAddresses()
if err := networkCheckJoinAddrs(cfg, joins); err != nil {
return err
}
if joins == nil && cfg.DiscoMode == "" && !hasPeers {
if cfg.RaftNonVoter {
return fmt.Errorf("cannot create a new non-voting node without joining it to an existing cluster")
@ -579,3 +584,16 @@ func createCluster(cfg *Config, hasPeers bool, client *cluster.Client, str *stor
}
return nil
}
func networkCheckJoinAddrs(cfg *Config, joinAddrs []string) error {
if len(joinAddrs) == 0 {
return nil
}
for _, addr := range joinAddrs {
if http.IsServingHTTP(addr) {
return fmt.Errorf("join address %s appears to be serving HTTP when it should be Raft", addr)
}
}
return nil
}

@ -0,0 +1,37 @@
package http
import (
"crypto/tls"
"net/http"
"time"
)
// IsServingHTTP returns true if there appears to be a HTTP or HTTPS server
// running on the given address.
func IsServingHTTP(addr string) bool {
client := http.Client{
Timeout: 2 * time.Second,
}
resp, err := client.Get("http://" + addr)
if err == nil {
resp.Body.Close()
return true
}
// Check for a HTTPS server listening on the same address, using the same URL.
// Don't check the certificate, as we're only interested in whether there's
// a server running.
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client = http.Client{
Transport: tr,
Timeout: 2 * time.Second,
}
resp, err = client.Get("https://" + addr + "/status")
if err == nil {
resp.Body.Close()
return true
}
return false
}

@ -0,0 +1,119 @@
package http
import (
"crypto/tls"
"crypto/x509/pkix"
"net"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/rqlite/rqlite/rtls"
)
// Test_IsServingHTTP_HTTPServerOnly tests only HTTP server running.
func Test_IsServingHTTP_HTTPServer(t *testing.T) {
httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer httpServer.Close()
addr := httpServer.Listener.Addr().String()
if !IsServingHTTP(addr) {
t.Errorf("Expected true for HTTP server running on %s", addr)
}
}
// Test_IsServingHTTP_HTTPSServerOnly tests only HTTPS server running.
func Test_IsServingHTTP_HTTPSServer(t *testing.T) {
httpsServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer httpsServer.Close()
addr := httpsServer.Listener.Addr().String()
if !IsServingHTTP(addr) {
t.Error("Expected true for HTTPS server running")
}
}
// Test_IsServingHTTP_NoServersRunning tests no servers running.
func Test_IsServingHTTP_NoServersRunning(t *testing.T) {
addr := "127.0.0.1:9999" // Assume this address is not used
if IsServingHTTP(addr) {
t.Error("Expected false for no servers running")
}
}
// Test_IsServingHTTP_InvalidAddress tests invalid address format.
func Test_IsServingHTTP_InvalidAddress(t *testing.T) {
addr := "invalid-address"
if IsServingHTTP(addr) {
t.Error("Expected false for invalid address")
}
}
// Test_IsServingHTTP_HTTPErrorStatusCode tests HTTP server returning error status code.
func Test_IsServingHTTP_HTTPErrorStatusCode(t *testing.T) {
httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer httpServer.Close()
addr := httpServer.Listener.Addr().String()
if !IsServingHTTP(addr) {
t.Error("Expected true for HTTP server running, even with error status code")
}
}
// Test_IsServingHTTP_HTTPSSuccessStatusCode tests HTTPS server running with success status code.
func Test_IsServingHTTP_HTTPSSuccessStatusCode(t *testing.T) {
httpsServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer httpsServer.Close()
addr := httpsServer.Listener.Addr().String()
if !IsServingHTTP(addr) {
t.Error("Expected true for HTTPS server running with success status code")
}
}
func Test_IsServingHTTP_OpenPort(t *testing.T) {
// Create a TCP listener on a random port
ln, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
addr := ln.Addr().String()
if IsServingHTTP(addr) {
t.Error("Expected false for open port")
}
}
func Test_IsServingHTTP_OpenPortTLS(t *testing.T) {
cert, key, err := rtls.GenerateSelfSignedCert(pkix.Name{CommonName: "rqlite"}, time.Hour, 2048)
if err != nil {
t.Fatalf("failed to generate self-signed cert: %s", err)
}
certFile := mustWriteTempFile(t, cert)
keyFile := mustWriteTempFile(t, key)
tlsConfig, err := rtls.CreateServerConfig(certFile, keyFile, "", false)
if err != nil {
t.Fatalf("failed to create TLS config: %s", err)
}
ln, err := tls.Listen("tcp", ":0", tlsConfig)
if err != nil {
t.Fatalf("failed to create TLS listener: %s", err)
}
defer ln.Close()
addr := ln.Addr().String()
if IsServingHTTP(addr) {
t.Error("Expected false for open TLS port")
}
}
Loading…
Cancel
Save