mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-19 06:31:47 +00:00
Allow configuration of supervisor endpoints
This change allows configuration of the http and https listeners used by the supervisor. TCP (IPv4 and IPv6 with any interface and port) and Unix domain socket based listeners are supported. Listeners may also be disabled. Binding the http listener to TCP addresses other than 127.0.0.1 or ::1 is deprecated. The deployment now uses https health checks. The supervisor is always able to complete a TLS connection with the use of a bootstrap certificate that is signed by an in-memory certificate authority. To support sidecar containers used by service meshes, Unix domain socket based listeners include ACLs that allow writes to the socket file from any runAsUser specified in the pod's containers. Signed-off-by: Monis Khan <mok@vmware.com>
This commit is contained in:
@@ -316,9 +316,6 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
require.NotEmpty(t, supervisorPods.Items, "could not find supervisor pods")
|
||||
supervisorPod := supervisorPods.Items[0]
|
||||
|
||||
// make sure the supervisor has a default TLS cert during this test so that it can handle a TLS connection
|
||||
createSupervisorDefaultTLSCertificateSecretIfNeeded(ctx, t)
|
||||
|
||||
// Test that the user can perform basic actions through the client with their username and group membership
|
||||
// influencing RBAC checks correctly.
|
||||
t.Run(
|
||||
|
||||
@@ -108,9 +108,6 @@ func TestSecureTLSSupervisor(t *testing.T) { // does not run in parallel because
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
// make sure the supervisor has a default TLS cert during this test so that it can handle a TLS connection
|
||||
createSupervisorDefaultTLSCertificateSecretIfNeeded(ctx, t)
|
||||
|
||||
startKubectlPortForward(ctx, t, "10447", "443", env.SupervisorAppName+"-nodeport", env.SupervisorNamespace)
|
||||
|
||||
stdout, stderr := runNmapSSLEnum(t, "127.0.0.1", 10447)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -168,7 +169,7 @@ func TestSupervisorTLSTerminationWithSNI_Disruptive(t *testing.T) {
|
||||
requireStatus(t, pinnipedClient, federationDomain1.Namespace, federationDomain1.Name, v1alpha1.SuccessFederationDomainStatusCondition)
|
||||
|
||||
// The spec.tls.secretName Secret does not exist, so the endpoints should fail with TLS errors.
|
||||
requireEndpointHasTLSErrorBecauseCertificatesAreNotReady(t, issuer1)
|
||||
requireEndpointHasBootstrapTLSErrorBecauseCertificatesAreNotReady(t, issuer1)
|
||||
|
||||
// Create the Secret.
|
||||
ca1 := createTLSCertificateSecret(ctx, t, ns, hostname1, nil, certSecretName1, kubeClient)
|
||||
@@ -189,7 +190,7 @@ func TestSupervisorTLSTerminationWithSNI_Disruptive(t *testing.T) {
|
||||
}))
|
||||
|
||||
// The the endpoints should fail with TLS errors again.
|
||||
requireEndpointHasTLSErrorBecauseCertificatesAreNotReady(t, issuer1)
|
||||
requireEndpointHasBootstrapTLSErrorBecauseCertificatesAreNotReady(t, issuer1)
|
||||
|
||||
// Create a Secret at the updated name.
|
||||
ca1update := createTLSCertificateSecret(ctx, t, ns, hostname1, nil, certSecretName1update, kubeClient)
|
||||
@@ -252,7 +253,7 @@ func TestSupervisorTLSTerminationWithDefaultCerts_Disruptive(t *testing.T) {
|
||||
requireStatus(t, pinnipedClient, federationDomain1.Namespace, federationDomain1.Name, v1alpha1.SuccessFederationDomainStatusCondition)
|
||||
|
||||
// There is no default TLS cert and the spec.tls.secretName was not set, so the endpoints should fail with TLS errors.
|
||||
requireEndpointHasTLSErrorBecauseCertificatesAreNotReady(t, issuerUsingIPAddress)
|
||||
requireEndpointHasBootstrapTLSErrorBecauseCertificatesAreNotReady(t, issuerUsingIPAddress)
|
||||
|
||||
// Create a Secret at the special name which represents the default TLS cert.
|
||||
defaultCA := createTLSCertificateSecret(ctx, t, ns, "cert-hostname-doesnt-matter", []net.IP{ips[0]}, defaultTLSCertSecretName(env), kubeClient)
|
||||
@@ -320,22 +321,6 @@ func createTLSCertificateSecret(ctx context.Context, t *testing.T, ns string, ho
|
||||
return ca
|
||||
}
|
||||
|
||||
func createSupervisorDefaultTLSCertificateSecretIfNeeded(ctx context.Context, t *testing.T) {
|
||||
env := testlib.IntegrationEnv(t)
|
||||
adminClient := testlib.NewKubernetesClientset(t)
|
||||
|
||||
ns := env.SupervisorNamespace
|
||||
name := defaultTLSCertSecretName(env)
|
||||
|
||||
_, err := adminClient.CoreV1().Secrets(ns).Get(ctx, name, metav1.GetOptions{})
|
||||
|
||||
if k8serrors.IsNotFound(err) {
|
||||
_ = createTLSCertificateSecret(ctx, t, ns, "cert-hostname-doesnt-matter", nil, name, adminClient)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func temporarilyRemoveAllFederationDomainsAndDefaultTLSCertSecret(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
@@ -425,9 +410,14 @@ func requireEndpointNotFound(t *testing.T, url, host, caBundle string) {
|
||||
}, 2*time.Minute, 200*time.Millisecond)
|
||||
}
|
||||
|
||||
func requireEndpointHasTLSErrorBecauseCertificatesAreNotReady(t *testing.T, url string) {
|
||||
func requireEndpointHasBootstrapTLSErrorBecauseCertificatesAreNotReady(t *testing.T, url string) {
|
||||
t.Helper()
|
||||
httpClient := newHTTPClient(t, "", nil)
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // there is no way for us to know the bootstrap CA
|
||||
},
|
||||
}
|
||||
|
||||
testlib.RequireEventually(t, func(requireEventually *require.Assertions) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
@@ -437,11 +427,18 @@ func requireEndpointHasTLSErrorBecauseCertificatesAreNotReady(t *testing.T, url
|
||||
requireEventually.NoError(err)
|
||||
|
||||
response, err := httpClient.Do(request)
|
||||
if err == nil {
|
||||
requireEventually.NoError(err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = response.Body.Close()
|
||||
}
|
||||
requireEventually.Error(err)
|
||||
requireEventually.EqualError(err, fmt.Sprintf(`Get "%s": remote error: tls: unrecognized name`, url))
|
||||
})
|
||||
|
||||
requireEventually.Equal(http.StatusInternalServerError, response.StatusCode)
|
||||
|
||||
body, err := io.ReadAll(response.Body)
|
||||
requireEventually.NoError(err)
|
||||
|
||||
requireEventually.Equal("pinniped supervisor has invalid TLS serving certificate configuration\n", string(body))
|
||||
}, 2*time.Minute, 200*time.Millisecond)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -32,22 +33,55 @@ func TestSupervisorHealthz(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
requestHealthEndpoint, err := http.NewRequestWithContext(
|
||||
httpClient := &http.Client{}
|
||||
|
||||
httpGet(ctx, t, httpClient, fmt.Sprintf("http://%s/healthz", env.SupervisorHTTPAddress), http.StatusOK, "ok")
|
||||
}
|
||||
|
||||
// Never run this test in parallel since deleting all federation domains and the default TLS secret is disruptive, see main_test.go.
|
||||
func TestSupervisorHealthzBootstrap_Disruptive(t *testing.T) {
|
||||
env := testlib.IntegrationEnv(t)
|
||||
pinnipedClient := testlib.NewSupervisorClientset(t)
|
||||
kubeClient := testlib.NewKubernetesClientset(t)
|
||||
|
||||
ns := env.SupervisorNamespace
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
temporarilyRemoveAllFederationDomainsAndDefaultTLSCertSecret(ctx, t, ns, defaultTLSCertSecretName(env), pinnipedClient, kubeClient)
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // there is no way for us to know the bootstrap CA
|
||||
},
|
||||
}
|
||||
|
||||
const badTLSConfigBody = "pinniped supervisor has invalid TLS serving certificate configuration\n"
|
||||
|
||||
httpGet(ctx, t, httpClient, fmt.Sprintf("https://%s/healthz", env.SupervisorHTTPSAddress), http.StatusOK, "ok")
|
||||
httpGet(ctx, t, httpClient, fmt.Sprintf("https://%s", env.SupervisorHTTPSAddress), http.StatusInternalServerError, badTLSConfigBody)
|
||||
httpGet(ctx, t, httpClient, fmt.Sprintf("https://%s/nothealthz", env.SupervisorHTTPSAddress), http.StatusInternalServerError, badTLSConfigBody)
|
||||
httpGet(ctx, t, httpClient, fmt.Sprintf("https://%s/healthz/something", env.SupervisorHTTPSAddress), http.StatusInternalServerError, badTLSConfigBody)
|
||||
}
|
||||
|
||||
func httpGet(ctx context.Context, t *testing.T, client *http.Client, url string, expectedStatus int, expectedBody string) {
|
||||
t.Helper()
|
||||
|
||||
req, err := http.NewRequestWithContext(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
fmt.Sprintf("http://%s/healthz", env.SupervisorHTTPAddress),
|
||||
url,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
httpClient := &http.Client{}
|
||||
response, err := httpClient.Do(requestHealthEndpoint) //nolint:bodyclose
|
||||
response, err := client.Do(req) //nolint:bodyclose
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||
require.Equal(t, expectedStatus, response.StatusCode)
|
||||
|
||||
responseBody, err := ioutil.ReadAll(response.Body)
|
||||
require.NoError(t, err)
|
||||
err = response.Body.Close()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "ok", string(responseBody))
|
||||
require.Equal(t, expectedBody, string(responseBody))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user