Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

server: move AuthenticationTLS test to e2e #2747

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 0 additions & 126 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@ package server

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"sort"
"strings"
Expand Down Expand Up @@ -3097,128 +3093,6 @@ func mustUnmarshalTrace(t types.TraceV1) (trace types.TraceV1Raw) {
return trace
}

func TestAuthenticationTLS(t *testing.T) {
ctx := context.Background()
store := inmem.New()
m, err := plugins.New([]byte{}, "test", store)
if err != nil {
t.Fatal(err)
}

if err := m.Start(ctx); err != nil {
t.Fatal(err)
}

txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)

authzPolicy := `package system.authz
import input.identity
default allow = false
allow {
identity = "CN=my-client"
}`

if err := store.UpsertPolicy(ctx, txn, "test", []byte(authzPolicy)); err != nil {
t.Fatal(err)
}

if err := store.Commit(ctx, txn); err != nil {
t.Fatal(err)
}

caCertPEM, err := ioutil.ReadFile("testdata/ca.pem")
if err != nil {
t.Fatal(err)
}
pool := x509.NewCertPool()
if ok := pool.AppendCertsFromPEM(caCertPEM); !ok {
t.Fatal("failed to parse CA cert")
}
cert, err := tls.LoadX509KeyPair("testdata/server-cert.pem", "testdata/server-key.pem")
if err != nil {
t.Fatal(err)
}

server, err := New().
WithAddresses([]string{"https://127.0.0.1:8182"}).
WithStore(store).
WithManager(m).
WithCertificate(&cert).
WithCertPool(pool).
WithAuthentication(AuthenticationTLS).
WithAuthorization(AuthorizationBasic).
Init(ctx)
if err != nil {
t.Fatal(err)
}

// Replicating some of what happens in the server's HTTPS listener
s := httptest.NewUnstartedServer(server.Handler)
s.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: pool,
}
s.StartTLS()
defer s.Close()
endpoint := s.URL + "/v1/data/foo"

t.Run("happy path", func(t *testing.T) {
c := newClient(t, pool, "testdata/client-cert.pem", "testdata/client-key.pem")
resp, err := c.Get(endpoint)
if err != nil {
t.Fatalf("GET: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("expected status 200, got %s", resp.Status)
}
})

t.Run("authn successful, authz failed", func(t *testing.T) {
c := newClient(t, pool, "testdata/client-cert-2.pem", "testdata/client-key-2.pem")
resp, err := c.Get(endpoint)
if err != nil {
t.Fatalf("GET: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("expected status 401, got %s", resp.Status)
}
})

t.Run("client trusts server, but doesn't provide client cert", func(t *testing.T) {
c := newClient(t, pool)
_, err := c.Get(endpoint)
if _, ok := err.(*url.Error); !ok {
t.Errorf("expected *url.Error, got %T: %v", err, err)
}
})
}

func newClient(t *testing.T, pool *x509.CertPool, clientKeyPair ...string) *http.Client {
t.Helper()
c := *http.DefaultClient
// Note: zero-values in http.Transport are bad settings -- they let the client
// leak connections -- but it's good enough for these tests. Don't instantiate
// http.Transport without providing non-zero values in non-test code, please.
// See https://github.com/golang/go/issues/19620 for details.
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
},
}
if len(clientKeyPair) == 2 {
clientCert, err := tls.LoadX509KeyPair(clientKeyPair[0], clientKeyPair[1])
if err != nil {
t.Fatalf("read test client cert/key: %v", err)
}
tr.TLSClientConfig.Certificates = []tls.Certificate{clientCert}
}
c.Transport = tr
return &c
}

func TestShutdown(t *testing.T) {
f := newFixture(t, func(s *Server) {
s.WithDiagnosticAddresses([]string{":8443"})
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
144 changes: 144 additions & 0 deletions test/e2e/tls/tls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package tls

import (
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"testing"

"github.com/open-policy-agent/opa/server"
"github.com/open-policy-agent/opa/test/e2e"
)

var testRuntime *e2e.TestRuntime
var pool *x509.CertPool

// print error to stderr, exit 1
func fatal(err interface{}) {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}

func TestMain(m *testing.M) {
flag.Parse()

caCertPEM, err := ioutil.ReadFile("testdata/ca.pem")
if err != nil {
fatal(err)
}
pool = x509.NewCertPool()
if ok := pool.AppendCertsFromPEM(caCertPEM); !ok {
fatal("failed to parse CA cert")
}
cert, err := tls.LoadX509KeyPair("testdata/server-cert.pem", "testdata/server-key.pem")
if err != nil {
fatal(err)
}

// We need the policy to be present already, otherwise authorization
// for the health endpoint is going to fail on server startup.
authzPolicy := []byte(`package system.authz
import input.identity
default allow = false
allow {
identity = "CN=my-client"
}`)

tmpfile, err := ioutil.TempFile("", "authz.*.rego")
if err != nil {
fatal(err)
}
defer os.Remove(tmpfile.Name())

if _, err := tmpfile.Write(authzPolicy); err != nil {
fatal(err)
}
if err := tmpfile.Close(); err != nil {
fatal(err)
}

testServerParams := e2e.NewAPIServerTestParams()
testServerParams.Addrs = &[]string{"https://127.0.0.1:0"}
testServerParams.CertPool = pool
testServerParams.Certificate = &cert
testServerParams.Authentication = server.AuthenticationTLS
testServerParams.Authorization = server.AuthorizationBasic
testServerParams.Paths = []string{"system.authz:" + tmpfile.Name()}

testRuntime, err = e2e.NewTestRuntime(testServerParams)
if err != nil {
fatal(err)
}

// We need a client with proper TLS setup, otherwise the health check
// that loops to determine if the server is ready will fail.
testRuntime.Client = newClient(pool, "testdata/client-cert.pem", "testdata/client-key.pem")

os.Exit(testRuntime.RunTests(m))
}

func TestAuthenticationTLS(t *testing.T) {
endpoint := testRuntime.URL() + "/v1/data/foo"

// Note: This test is redundant. When the testRuntime starts the server, it
// already queries the health endpoint using a properly authenticated, and
// authorized, http client.
t.Run("happy path", func(t *testing.T) {
c := newClient(pool, "testdata/client-cert.pem", "testdata/client-key.pem")
resp, err := c.Get(endpoint)
if err != nil {
t.Fatalf("GET: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("expected status 200, got %s", resp.Status)
}
})

t.Run("authn successful, authz failed", func(t *testing.T) {
c := newClient(pool, "testdata/client-cert-2.pem", "testdata/client-key-2.pem")
resp, err := c.Get(endpoint)
if err != nil {
t.Fatalf("GET: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("expected status 401, got %s", resp.Status)
}
})

t.Run("client trusts server, but doesn't provide client cert", func(t *testing.T) {
c := newClient(pool)
_, err := c.Get(endpoint)
if _, ok := err.(*url.Error); !ok {
t.Errorf("expected *url.Error, got %T: %v", err, err)
}
})
}

func newClient(pool *x509.CertPool, clientKeyPair ...string) *http.Client {
c := *http.DefaultClient
// Note: zero-values in http.Transport are bad settings -- they let the client
// leak connections -- but it's good enough for these tests. Don't instantiate
// http.Transport without providing non-zero values in non-test code, please.
// See https://github.com/golang/go/issues/19620 for details.
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
},
}
if len(clientKeyPair) == 2 {
clientCert, err := tls.LoadX509KeyPair(clientKeyPair[0], clientKeyPair[1])
if err != nil {
panic(err)
}
tr.TLSClientConfig.Certificates = []tls.Certificate{clientCert}
}
c.Transport = tr
return &c
}