Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1986228: NE-310 e2e test for HSTS
- Loading branch information
Showing
3 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// +build e2e | ||
|
||
package e2e | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
configv1 "github.com/openshift/api/config/v1" | ||
operatorv1 "github.com/openshift/api/operator/v1" | ||
routev1 "github.com/openshift/api/route/v1" | ||
"github.com/openshift/cluster-ingress-operator/pkg/operator/controller" | ||
|
||
appsv1 "k8s.io/api/apps/v1" | ||
corev1 "k8s.io/api/core/v1" | ||
|
||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/apimachinery/pkg/util/wait" | ||
) | ||
|
||
// Helper function for int32 pointers | ||
func intPtr(s int32) *int32 { | ||
return &s | ||
} | ||
|
||
func TestHstsPolicyWorks(t *testing.T) { | ||
icName := types.NamespacedName{Namespace: operatorNamespace, Name: "hsts-policy"} | ||
domain := icName.Name + "." + dnsConfig.Spec.BaseDomain | ||
domain2 := icName.Name + "2." + dnsConfig.Spec.BaseDomain | ||
|
||
// Setup a Required HSTS Policy for the ingress config | ||
maxAgePolicy := configv1.MaxAgePolicy{LargestMaxAge: intPtr(99999), SmallestMaxAge: intPtr(1)} | ||
domainPatterns := []string{domain, domain2} | ||
hstsPolicy := configv1.RequiredHSTSPolicy{ | ||
DomainPatterns: domainPatterns, // this policy will only validate routes with hosts in the DomainPatterns | ||
PreloadPolicy: configv1.RequirePreloadPolicy, | ||
IncludeSubDomainsPolicy: configv1.RequireIncludeSubDomains, | ||
MaxAge: maxAgePolicy, | ||
} | ||
|
||
ing := &configv1.Ingress{} | ||
|
||
// Update the ingress config with the new HSTS policy | ||
if err := wait.PollImmediate(1*time.Second, 1*time.Minute, func() (bool, error) { | ||
// Get the ingress config | ||
if err := kclient.Get(context.TODO(), clusterName, ing); err != nil { | ||
t.Logf("Get ingress config failed: %v, retrying...", err) | ||
return false, nil | ||
} | ||
if ing.Spec.RequiredHSTSPolicies == nil { | ||
ing.Spec.RequiredHSTSPolicies = []configv1.RequiredHSTSPolicy{} | ||
} | ||
ing.Spec.RequiredHSTSPolicies = append(ing.Spec.RequiredHSTSPolicies, hstsPolicy) | ||
// Update the ingress config | ||
if err := kclient.Update(context.TODO(), ing); err != nil { | ||
t.Logf("failed to update ingress config: %v", err) | ||
return false, nil | ||
} | ||
return true, nil | ||
}); err != nil { | ||
t.Fatalf("failed to update ingress controller: %v", err) | ||
} | ||
|
||
p := ing.Spec.RequiredHSTSPolicies[0] | ||
t.Logf("created a RequiredHSTSPolicy with DomainPatterns: %v, preload policy: %s, includeSubDomains policy: %s, largest age: %d, smallest age: %d", p.DomainPatterns, p.PreloadPolicy, p.IncludeSubDomainsPolicy, *p.MaxAge.LargestMaxAge, *p.MaxAge.SmallestMaxAge) | ||
|
||
conditions := []operatorv1.OperatorCondition{ | ||
{Type: operatorv1.IngressControllerAvailableConditionType, Status: operatorv1.ConditionTrue}, | ||
{Type: operatorv1.LoadBalancerManagedIngressConditionType, Status: operatorv1.ConditionFalse}, | ||
{Type: operatorv1.DNSManagedIngressConditionType, Status: operatorv1.ConditionFalse}, | ||
} | ||
|
||
// Create a new ingress controller, so we don't disrupt the test system's ingress controller | ||
ic := newPrivateController(icName, domain) | ||
if err := wait.PollImmediate(1*time.Second, 1*time.Minute, func() (bool, error) { | ||
if err := kclient.Create(context.TODO(), ic); err != nil { | ||
t.Logf("failed to create ingresscontroller %s: %v, retrying...", icName, err) | ||
return false, nil | ||
} | ||
return true, nil | ||
}); err != nil { | ||
t.Fatalf("failed to create ingress controller: %v", err) | ||
} | ||
defer assertIngressControllerDeleted(t, kclient, ic) | ||
// Wait for it to come up to speed | ||
if err := waitForIngressControllerCondition(t, kclient, 5*time.Minute, icName, conditions...); err != nil { | ||
t.Fatalf("failed to observe expected conditions: %v", err) | ||
} | ||
|
||
// Get router deployment info | ||
deployment := &appsv1.Deployment{} | ||
if err := kclient.Get(context.TODO(), controller.RouterDeploymentName(ic), deployment); err != nil { | ||
t.Fatalf("failed to get ingresscontroller deployment: %v", err) | ||
} | ||
|
||
// Get ingress service info | ||
service := &corev1.Service{} | ||
if err := kclient.Get(context.TODO(), controller.InternalIngressControllerServiceName(ic), service); err != nil { | ||
t.Fatalf("failed to get ingresscontroller service: %v", err) | ||
} | ||
|
||
// Create some pods in the deployment namespace | ||
echoPod := buildEchoPod("hsts-policy-echo", deployment.Namespace) | ||
if err := kclient.Create(context.TODO(), echoPod); err != nil { | ||
t.Fatalf("failed to create pod %s/%s: %v", echoPod.Namespace, echoPod.Name, err) | ||
} | ||
defer func() { | ||
if err := kclient.Delete(context.TODO(), echoPod); err != nil { | ||
t.Fatalf("failed to delete pod %s/%s: %v", echoPod.Namespace, echoPod.Name, err) | ||
} | ||
}() | ||
|
||
echoService := buildEchoService(echoPod.Name, echoPod.Namespace, echoPod.ObjectMeta.Labels) | ||
if err := kclient.Create(context.TODO(), echoService); err != nil { | ||
t.Fatalf("failed to create service %s/%s: %v", echoService.Namespace, echoService.Name, err) | ||
} | ||
defer func() { | ||
if err := kclient.Delete(context.TODO(), echoService); err != nil { | ||
t.Fatalf("failed to delete service %s/%s: %v", echoService.Namespace, echoService.Name, err) | ||
} | ||
}() | ||
|
||
// Create a route that should pass the HSTS policy validation | ||
t.Logf("Creating first route at %v", time.Now()) | ||
echoRoute := buildRouteWithHSTS(echoPod.Name, echoPod.Namespace, echoService.Name, domain, "max-age=99999;preload;includesubdomains") | ||
|
||
if err := kclient.Create(context.TODO(), echoRoute); err != nil { | ||
t.Fatalf("failed to create route %s/%s: %v", echoRoute.Namespace, echoRoute.Name, err) | ||
} else { | ||
t.Logf("created a route at %v: %s/%s with annotation %s", time.Now(), echoRoute.Namespace, echoRoute.Name, echoRoute.Annotations) | ||
} | ||
|
||
// Create a route that should fail the HSTS policy validation | ||
t.Logf("Creating second route at %v", time.Now()) | ||
echoRoute2 := buildRouteWithHSTS(echoPod.Name+"2", echoPod.Namespace, echoService.Name, domain2, "max-age=99999999") | ||
|
||
if err := kclient.Create(context.TODO(), echoRoute2); err == nil { | ||
t.Fatalf("failed to reject invalid route %s/%s, max-age 99999999", echoRoute2.Namespace, echoRoute2.Name) | ||
} else { | ||
t.Logf("rejected an invalid route at %v: %s/%s with annotation %s: %v", time.Now(), echoRoute2.Namespace, echoRoute2.Name, echoRoute2.Annotations, err) | ||
} | ||
|
||
defer func() { | ||
if err := kclient.Delete(context.TODO(), echoRoute); err != nil { | ||
t.Fatalf("failed to delete route %s/%s: %v", echoRoute.Namespace, echoRoute.Name, err) | ||
} | ||
// okay if this fails, it shouldn't have been created anyway | ||
kclient.Delete(context.TODO(), echoRoute2) | ||
}() | ||
} | ||
|
||
func buildRouteWithHSTS(podName, namespace, serviceName, domain, annotation string) *routev1.Route { | ||
route := buildRoute(podName, namespace, serviceName) | ||
route.Spec.Host = domain | ||
route.Spec.TLS = &routev1.TLSConfig{Termination: routev1.TLSTerminationReencrypt} | ||
if route.Annotations == nil { | ||
route.Annotations = map[string]string{} | ||
} | ||
route.Annotations["haproxy.router.openshift.io/hsts_header"] = annotation | ||
|
||
return route | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters