1
0
Fork 0
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

245 lines
6.9 KiB
Go

package http
import (
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"net"
"net/http"
"os"
"testing"
"time"
"github.com/rqlite/rqlite/v8/rtls"
"golang.org/x/net/http2"
)
func Test_TLSServiceInsecure(t *testing.T) {
m := &MockStore{}
c := &mockClusterService{}
s := New("127.0.0.1:0", m, c, nil)
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)
}
s.CertFile = mustWriteTempFile(t, cert)
s.KeyFile = mustWriteTempFile(t, key)
s.BuildInfo = map[string]interface{}{
"version": "the version",
}
if err := s.Start(); err != nil {
t.Fatalf("failed to start service")
}
if !s.HTTPS() {
t.Fatalf("expected service to report HTTPS")
}
defer s.Close()
url := fmt.Sprintf("https://%s", s.Addr().String())
// Test connecting with an HTTP client.
tn := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tn}
resp, err := client.Get(url)
if err != nil {
t.Fatalf("failed to make HTTP request: %s", err)
}
if v := resp.Header.Get("X-RQLITE-VERSION"); v != "the version" {
t.Fatalf("incorrect build version present in HTTP response header, got: %s", v)
}
// Test connecting with an HTTP/2 client.
client = &http.Client{
Transport: &http2.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
resp, err = client.Get(url)
if err != nil {
t.Fatalf("failed to make HTTP/2 request: %s", err)
}
if v := resp.Header.Get("X-RQLITE-VERSION"); v != "the version" {
t.Fatalf("incorrect build version present in HTTP/2 response header, got: %s", v)
}
}
func Test_TLSServiceSecure(t *testing.T) {
m := &MockStore{}
c := &mockClusterService{}
s := New("127.0.0.1:0", m, c, nil)
cert, key, err := rtls.GenerateSelfSignedCertIPSAN(pkix.Name{CommonName: "rqlite.io"}, time.Hour, 2048, net.ParseIP("127.0.0.1"))
if err != nil {
t.Fatalf("failed to generate self-signed cert: %s", err)
}
s.CertFile = mustWriteTempFile(t, cert)
s.KeyFile = mustWriteTempFile(t, key)
s.BuildInfo = map[string]interface{}{
"version": "the version",
}
if err := s.Start(); err != nil {
t.Fatalf("failed to start service")
}
if !s.HTTPS() {
t.Fatalf("expected service to report HTTPS")
}
defer s.Close()
url := fmt.Sprintf("https://%s", s.Addr().String())
2 years ago
// Create a TLS Config which verfies server cert, and trusts the CA cert.
tlsConfig := &tls.Config{InsecureSkipVerify: false}
tlsConfig.RootCAs = x509.NewCertPool()
ok := tlsConfig.RootCAs.AppendCertsFromPEM(cert)
if !ok {
t.Fatalf("failed to parse CA certificate(s) for client verification in %q", cert)
}
2 years ago
// Test connecting with an HTTP1.1 client.
client := &http.Client{Transport: &http.Transport{
TLSClientConfig: tlsConfig,
}}
resp, err := client.Get(url)
if err != nil {
t.Fatalf("failed to make HTTP request: %s", err)
}
if v := resp.Header.Get("X-RQLITE-VERSION"); v != "the version" {
t.Fatalf("incorrect build version present in HTTP response header, got: %s", v)
}
// Test connecting with an HTTP/2 client.
client = &http.Client{
Transport: &http2.Transport{
2 years ago
TLSClientConfig: tlsConfig,
},
}
resp, err = client.Get(url)
if err != nil {
t.Fatalf("failed to make HTTP/2 request: %s", err)
}
if v := resp.Header.Get("X-RQLITE-VERSION"); v != "the version" {
t.Fatalf("incorrect build version present in HTTP/2 response header, got: %s", v)
}
}
func Test_TLSServiceSecureMutual(t *testing.T) {
// Generate a CA cert and key.
caCertPEM, caKeyPEM, err := rtls.GenerateCACert(pkix.Name{CommonName: "ca.rqlite.io"}, time.Hour, 2048)
if err != nil {
t.Fatalf("failed to generate CA cert: %s", err)
}
caCert, _ := pem.Decode(caCertPEM)
if caCert == nil {
t.Fatal("failed to decode certificate")
}
caKey, _ := pem.Decode(caKeyPEM)
if caKey == nil {
t.Fatal("failed to decode key")
}
// parse the certificate and private key
parsedCACert, err := x509.ParseCertificate(caCert.Bytes)
if err != nil {
t.Fatal(err)
}
parsedCAKey, err := x509.ParsePKCS1PrivateKey(caKey.Bytes)
if err != nil {
t.Fatal(err)
}
// Create a cert signed by the CA for the server
certServer, keyServer, err := rtls.GenerateCertIPSAN(pkix.Name{CommonName: "server.rqlite.io"}, time.Hour, 2048, parsedCACert, parsedCAKey, net.ParseIP("127.0.0.1"))
if err != nil {
t.Fatalf("failed to generate server cert: %s", err)
}
// Create a cert signed by the CA for the client
certClient, keyClient, err := rtls.GenerateCertIPSAN(pkix.Name{CommonName: "client.rqlite.io"}, time.Hour, 2048, parsedCACert, parsedCAKey, net.ParseIP("127.0.0.1"))
if err != nil {
t.Fatalf("failed to generate client cert: %s", err)
}
// Create and start the HTTP service.
m := &MockStore{}
c := &mockClusterService{}
s := New("127.0.0.1:0", m, c, nil)
s.CertFile = mustWriteTempFile(t, certServer)
s.KeyFile = mustWriteTempFile(t, keyServer)
s.CACertFile = mustWriteTempFile(t, caCertPEM) // Enables client verification by HTTP server
s.BuildInfo = map[string]interface{}{
"version": "the version",
}
s.ClientVerify = true
if err := s.Start(); err != nil {
t.Fatalf("failed to start service")
}
defer s.Close()
url := fmt.Sprintf("https://%s", s.Addr().String())
9 months ago
// Create a TLS Config which wil require verification of the server cert, and trusts the CA cert.
tlsConfig := &tls.Config{InsecureSkipVerify: false}
tlsConfig.RootCAs = x509.NewCertPool()
ok := tlsConfig.RootCAs.AppendCertsFromPEM(caCertPEM)
if !ok {
t.Fatalf("failed to parse CA certificate(s) for client verification in %q", caCertPEM)
}
client := &http.Client{Transport: &http.Transport{
TLSClientConfig: tlsConfig,
}}
_, err = client.Get(url)
if err == nil {
t.Fatalf("made successful HTTP request by untrusted client")
}
// Now set the client cert, which as also been signed by the CA. This should
// mean the HTTP server trusts the client.
tlsConfig.Certificates = make([]tls.Certificate, 1)
tlsConfig.Certificates[0], err = tls.X509KeyPair(certClient, keyClient)
if err != nil {
t.Fatalf("failed to set X509 key pair %s", err)
}
client = &http.Client{Transport: &http.Transport{
TLSClientConfig: tlsConfig,
}}
resp, err := client.Get(url)
if err != nil {
t.Fatalf("trusted client failed to make HTTP request: %s", err)
}
if v := resp.Header.Get("X-RQLITE-VERSION"); v != "the version" {
t.Fatalf("incorrect build version present in HTTP response header, got: %s", v)
}
}
// mustWriteTempFile writes the given bytes to a temporary file, and returns the
// path to the file. If there is an error, it panics. The file will be automatically
// deleted when the test ends.
func mustWriteTempFile(t *testing.T, b []byte) string {
f, err := os.CreateTemp(t.TempDir(), "rqlite-test")
if err != nil {
panic("failed to create temp file")
}
defer f.Close()
if _, err := f.Write(b); err != nil {
panic("failed to write to temp file")
}
return f.Name()
}