mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
server: move AuthenticationTLS test to e2e
Signed-off-by: Stephan Renatus <srenatus@chef.io>
This commit is contained in:
committed by
Patrick East
parent
23b5987e94
commit
2928df76bc
@@ -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"
|
||||
@@ -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"})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user