Skip to content

Commit

Permalink
endpoints with https should have tls server name set automatically (#167
Browse files Browse the repository at this point in the history
)
  • Loading branch information
wasaga committed Mar 18, 2022
1 parent 3aae68f commit 8516d17
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 8 deletions.
2 changes: 2 additions & 0 deletions model/ingress_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const (
TLSClientSecret = "tls_client_secret"
// TLSDownstreamClientCASecret replaces https://pomerium.io/reference/#tls-downstream-client-certificate-authority
TLSDownstreamClientCASecret = "tls_downstream_client_ca_secret"
// TLSServerName is annotation to override TLS server name
TLSServerName = "tls_server_name"
// SecureUpstream indicate that service communication should happen over HTTPS
SecureUpstream = "secure_upstream"
// PathRegex indicates that paths of ImplementationSpecific type should be treated as regular expression
Expand Down
17 changes: 9 additions & 8 deletions pomerium/ingress_to_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,10 @@ func pathToRoute(r *pb.Route, name types.NamespacedName, host string, p networki
return fmt.Errorf("setRouteNameID: %w", err)
}

svcURLs, err := getServiceURLs(name.Namespace, p, ic)
if err != nil {
if err := setServiceURLs(r, name.Namespace, p, ic); err != nil {
return fmt.Errorf("backend: %w", err)
}

r.To = svcURLs
return nil
}

Expand All @@ -173,10 +171,10 @@ func setRouteNameID(r *pb.Route, name types.NamespacedName, u url.URL) error {
return nil
}

func getServiceURLs(namespace string, p networkingv1.HTTPIngressPath, ic *model.IngressConfig) ([]string, error) {
func setServiceURLs(r *pb.Route, namespace string, p networkingv1.HTTPIngressPath, ic *model.IngressConfig) error {
svc := p.Backend.Service
if svc == nil {
return nil, errors.New("service is missing")
return errors.New("service is missing")
}

serviceName := types.NamespacedName{Namespace: namespace, Name: svc.Name}
Expand All @@ -185,7 +183,7 @@ func getServiceURLs(namespace string, p networkingv1.HTTPIngressPath, ic *model.
var err error
port, err = ic.GetServicePortByName(serviceName, svc.Port.Name)
if err != nil {
return nil, err
return err
}
}

Expand All @@ -196,7 +194,7 @@ func getServiceURLs(namespace string, p networkingv1.HTTPIngressPath, ic *model.

service, ok := ic.Services[serviceName]
if !ok {
return nil, fmt.Errorf("service %s was not fetched, this is a bug", serviceName.String())
return fmt.Errorf("service %s was not fetched, this is a bug", serviceName.String())
}

var hosts []string
Expand All @@ -212,6 +210,8 @@ func getServiceURLs(namespace string, p networkingv1.HTTPIngressPath, ic *model.
// this can happen if no endpoints are ready, or none match, in which case we fallback to the Kubernetes DNS name
if len(hosts) == 0 {
hosts = append(hosts, fmt.Sprintf("%s.%s.svc.cluster.local:%d", svc.Name, namespace, port))
} else if ic.IsSecureUpstream() && r.TlsServerName == "" {
r.TlsServerName = fmt.Sprintf("%s.%s.svc.cluster.local", svc.Name, namespace)
}
}

Expand All @@ -224,7 +224,8 @@ func getServiceURLs(namespace string, p networkingv1.HTTPIngressPath, ic *model.
}
sort.Strings(urls)

return urls, nil
r.To = urls
return nil
}

func getEndpointsURLs(ingressServicePort networkingv1.ServiceBackendPort, servicePorts []corev1.ServicePort, endpointSubsets []corev1.EndpointSubset) []string {
Expand Down
151 changes: 151 additions & 0 deletions pomerium/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,157 @@ func TestServicePortsAndEndpoints(t *testing.T) {
}]
require.NotNil(t, route, "route not found in %v", routes)
require.ElementsMatch(t, tc.expectTO, route.To)
require.Empty(t, route.TlsServerName)
})
}
}

// TestEndpointsHTTPS verifies that endpoints would correctly set
// https://github.com/pomerium/ingress-controller/issues/164
func TestEndpointsHTTPS(t *testing.T) {
for _, tc := range []struct {
name string
ingressAnnotations map[string]string
ingressPort networkingv1.ServiceBackendPort
svcPorts []corev1.ServicePort
endpointSubsets []corev1.EndpointSubset
expectTO []string
expectTLSServerName string
}{
{
"HTTPS multiple IPs - no annotations",
map[string]string{
fmt.Sprintf("p/%s", model.SecureUpstream): "true",
},
networkingv1.ServiceBackendPort{Name: "https"},
[]corev1.ServicePort{{
Name: "https",
Port: 443,
TargetPort: intstr.IntOrString{IntVal: 443},
}},
[]corev1.EndpointSubset{{
Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.5"}},
Ports: []corev1.EndpointPort{{Name: "https", Port: 443}},
}},
[]string{
"https://1.2.3.4:443",
"https://1.2.3.5:443",
},
"service.default.svc.cluster.local",
},
{
"HTTPS multiple IPs - override",
map[string]string{
fmt.Sprintf("p/%s", model.SecureUpstream): "true",
fmt.Sprintf("p/%s", model.TLSServerName): "custom-service.default.svc.cluster.local",
},
networkingv1.ServiceBackendPort{Name: "https"},
[]corev1.ServicePort{{
Name: "https",
Port: 443,
TargetPort: intstr.IntOrString{IntVal: 443},
}},
[]corev1.EndpointSubset{{
Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.5"}},
Ports: []corev1.EndpointPort{{Name: "https", Port: 443}},
}},
[]string{
"https://1.2.3.4:443",
"https://1.2.3.5:443",
},
"custom-service.default.svc.cluster.local",
},
{
"HTTPS use service proxy",
map[string]string{
fmt.Sprintf("p/%s", model.SecureUpstream): "true",
fmt.Sprintf("p/%s", model.UseServiceProxy): "true",
},
networkingv1.ServiceBackendPort{Name: "https"},
[]corev1.ServicePort{{
Name: "https",
Port: 443,
TargetPort: intstr.IntOrString{IntVal: 443},
}},
[]corev1.EndpointSubset{{
Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.5"}},
Ports: []corev1.EndpointPort{{Name: "https", Port: 443}},
}},
[]string{
"https://service.default.svc.cluster.local:443",
},
"",
},
} {
t.Run(tc.name, func(t *testing.T) {
pathTypePrefix := networkingv1.PathTypePrefix
ic := &model.IngressConfig{
AnnotationPrefix: "p",
Ingress: &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress",
Namespace: "default",
Annotations: tc.ingressAnnotations,
},
Spec: networkingv1.IngressSpec{
TLS: []networkingv1.IngressTLS{{
Hosts: []string{"service.localhost.pomerium.io"},
SecretName: "secret",
}},
Rules: []networkingv1.IngressRule{{
Host: "service.localhost.pomerium.io",
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{{
Path: "/a",
PathType: &pathTypePrefix,
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: "service",
Port: tc.ingressPort,
},
},
}},
},
},
}},
},
},
Endpoints: map[types.NamespacedName]*corev1.Endpoints{
{Name: "service", Namespace: "default"}: {
ObjectMeta: metav1.ObjectMeta{
Name: "service",
Namespace: "default",
},
Subsets: tc.endpointSubsets,
}},
Services: map[types.NamespacedName]*corev1.Service{
{Name: "service", Namespace: "default"}: {
ObjectMeta: metav1.ObjectMeta{
Name: "service",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: tc.svcPorts,
},
Status: corev1.ServiceStatus{},
},
},
}

cfg := new(pb.Config)
require.NoError(t, upsertRoutes(context.Background(), cfg, ic))
routes, err := routeList(cfg.Routes).toMap()
require.NoError(t, err)
route := routes[routeID{
Name: "ingress",
Namespace: "default",
Path: "/a",
Host: "service.localhost.pomerium.io",
}]
require.NotNil(t, route, "route not found in %v", routes)
require.ElementsMatch(t, tc.expectTO, route.To)
require.Equal(t, tc.expectTLSServerName, route.TlsServerName)
})
}
}

0 comments on commit 8516d17

Please sign in to comment.