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

security tests refactoring - part 3 #40348

Merged
merged 4 commits into from
Aug 23, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ func (n EchoNamespace) build(b deployment.Builder, cfg Config) deployment.Builde
if config.Namespace == nil {
config.Namespace = n.Namespace
}
b = b.WithConfig(config)
if config.Namespace.Name() == n.Namespace.Name() {
b = b.WithConfig(config)
}
}

return b
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,22 @@
package filebasedtlsorigination

import (
"os"
"path"
"testing"

"istio.io/istio/pkg/config/protocol"
"istio.io/istio/pkg/test/echo/common"
"istio.io/istio/pkg/test/echo/common/scheme"
"istio.io/istio/pkg/test/env"
"istio.io/istio/pkg/test/framework"
"istio.io/istio/pkg/test/framework/components/echo"
"istio.io/istio/pkg/test/framework/components/echo/check"
"istio.io/istio/pkg/test/framework/components/echo/deployment"
"istio.io/istio/pkg/test/framework/components/namespace"
)

func mustReadFile(t framework.TestContext, f string) string {
b, err := os.ReadFile(path.Join(env.IstioSrc, "tests/testdata/certs/dns", f))
if err != nil {
t.Fatalf("failed to read %v: %v", f, err)
}
return string(b)
}

// TestDestinationRuleTls tests that MUTUAL tls mode is respected in DestinationRule.
// This sets up a client and server with appropriate cert config and ensures we can successfully send a message.
func TestDestinationRuleTls(t *testing.T) {
framework.
NewTest(t).
Features("security.egress.tls.filebased").
Run(func(t framework.TestContext) {
ns := namespace.NewOrFail(t, t, namespace.Config{
Prefix: "tls",
Inject: true,
})
ns := appNS

// Setup our destination rule, enforcing TLS to "server". These certs will be created/mounted below.
t.ConfigIstio().YAML(ns.Name(), `
Expand All @@ -70,63 +52,6 @@ spec:
caCertificates: /etc/certs/custom/root-cert.pem
`).ApplyOrFail(t)

var client, server echo.Instance
deployment.New(t).
With(&client, echo.Config{
Service: "client",
Namespace: ns,
Ports: []echo.Port{},
Subsets: []echo.SubsetConfig{{
Version: "v1",
// Set up custom annotations to mount the certs. We will re-use the configmap created by "server"
// so that we don't need to manage it ourselves.
// The paths here match the destination rule above
Annotations: echo.NewAnnotations().
Set(echo.SidecarVolume, `{"custom-certs":{"configMap":{"name":"server-certs"}}}`).
Set(echo.SidecarVolumeMount, `{"custom-certs":{"mountPath":"/etc/certs/custom"}}`),
}},
Cluster: t.Clusters().Default(),
}).
With(&server, echo.Config{
Service: "server",
Namespace: ns,
Ports: []echo.Port{
{
Name: "grpc",
Protocol: protocol.GRPC,
WorkloadPort: 8090,
TLS: true,
},
{
Name: "http",
Protocol: protocol.HTTP,
WorkloadPort: 8091,
TLS: true,
},
{
Name: "tcp",
Protocol: protocol.TCP,
WorkloadPort: 8092,
TLS: true,
},
},
// Set up TLS certs on the server. This will make the server listen with these credentials.
TLSSettings: &common.TLSSettings{
RootCert: mustReadFile(t, "root-cert.pem"),
ClientCert: mustReadFile(t, "cert-chain.pem"),
Key: mustReadFile(t, "key.pem"),
// Override hostname to match the SAN in the cert we are using
Hostname: "server.default.svc",
},
// Do not inject, as we are testing non-Istio TLS here
Subsets: []echo.SubsetConfig{{
Version: "v1",
Annotations: echo.NewAnnotations().SetBool(echo.SidecarInject, false),
}},
Cluster: t.Clusters().Default(),
}).
BuildOrFail(t)

for _, portName := range []string{"grpc", "http", "tcp"} {
portName := portName
t.NewSubTest(portName).Run(func(t framework.TestContext) {
Expand All @@ -141,7 +66,7 @@ spec:
if portName == "tcp" {
opts.Scheme = scheme.TCP
}
client.CallOrFail(t, opts)
client[0].CallOrFail(t, opts)
})
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,24 @@ import (
"fmt"
"html/template"
"net/http"
"os"
"path"
"testing"
"time"

envoyAdmin "github.com/envoyproxy/go-control-plane/envoy/admin/v3"

"istio.io/istio/pkg/config/protocol"
"istio.io/istio/pkg/http/headers"
"istio.io/istio/pkg/test"
echoClient "istio.io/istio/pkg/test/echo"
"istio.io/istio/pkg/test/echo/common"
"istio.io/istio/pkg/test/env"
"istio.io/istio/pkg/test/framework"
"istio.io/istio/pkg/test/framework/components/echo"
"istio.io/istio/pkg/test/framework/components/echo/check"
"istio.io/istio/pkg/test/framework/components/echo/deployment"
"istio.io/istio/pkg/test/framework/components/istio"
"istio.io/istio/pkg/test/framework/components/namespace"
"istio.io/istio/pkg/test/framework/resource"
"istio.io/istio/pkg/test/util/retry"
"istio.io/istio/pkg/test/util/structpath"
)

func mustReadCert(t framework.TestContext, f string) string {
b, err := os.ReadFile(path.Join(env.IstioSrc, "tests/testdata/certs/dns", f))
if err != nil {
t.Fatalf("failed to read %v: %v", f, err)
}
return string(b)
}

// TestEgressGatewayTls brings up an cluster and will ensure that the TLS origination at
// egress gateway allows secure communication between the egress gateway and external workload.
// This test brings up an egress gateway to originate TLS connection. The test will ensure that requests
Expand All @@ -62,9 +48,15 @@ func TestEgressGatewayTls(t *testing.T) {
framework.NewTest(t).
Features("security.egress.tls.filebased").
Run(func(t framework.TestContext) {
internalClient, externalServer, _, serviceNamespace := setupEcho(t, t)
// Apply Egress Gateway for service namespace to originate external traffic
createGateway(t, t, appNS, serviceNS)

if err := WaitUntilNotCallable(internalClient[0], externalService[0]); err != nil {
t.Fatalf("failed to apply sidecar, %v", err)
}

// Set up Host Name
host := "server." + serviceNamespace.Name() + ".svc.cluster.local"
host := "external-service." + serviceNS.Name() + ".svc.cluster.local"

testCases := map[string]struct {
destinationRuleMode string
Expand Down Expand Up @@ -121,15 +113,15 @@ func TestEgressGatewayTls(t *testing.T) {
for name, tc := range testCases {
t.NewSubTest(name).
Run(func(t framework.TestContext) {
bufDestinationRule := createDestinationRule(t, serviceNamespace, tc.destinationRuleMode, tc.fakeRootCert)
bufDestinationRule := createDestinationRule(t, serviceNS, tc.destinationRuleMode, tc.fakeRootCert)

istioCfg := istio.DefaultConfigOrFail(t, t)
systemNamespace := namespace.ClaimOrFail(t, t, istioCfg.SystemNamespace)

t.ConfigIstio().YAML(systemNamespace.Name(), bufDestinationRule.String()).ApplyOrFail(t)

opts := echo.CallOptions{
To: externalServer,
To: externalService[0],
Count: 1,
Port: echo.Port{
Name: "http",
Expand All @@ -151,7 +143,7 @@ func TestEgressGatewayTls(t *testing.T) {
})),
}

internalClient.CallOrFail(t, opts)
internalClient[0].CallOrFail(t, opts)
})
}
})
Expand All @@ -165,15 +157,15 @@ kind: DestinationRule
metadata:
name: originate-tls-for-server-filebased-simple
spec:
host: "server.{{.AppNamespace}}.svc.cluster.local"
host: "external-service.{{.AppNamespace}}.svc.cluster.local"
trafficPolicy:
portLevelSettings:
- port:
number: 443
tls:
mode: {{.Mode}}
caCertificates: {{.RootCertPath}}
sni: server.{{.AppNamespace}}.svc.cluster.local
sni: external-service.{{.AppNamespace}}.svc.cluster.local

`
// Destination Rule configs
Expand All @@ -183,14 +175,14 @@ kind: DestinationRule
metadata:
name: originate-tls-for-server-filebased-disabled
spec:
host: "server.{{.AppNamespace}}.svc.cluster.local"
host: "external-service.{{.AppNamespace}}.svc.cluster.local"
trafficPolicy:
portLevelSettings:
- port:
number: 443
tls:
mode: {{.Mode}}
sni: server.{{.AppNamespace}}.svc.cluster.local
sni: external-service.{{.AppNamespace}}.svc.cluster.local

`
DestinationRuleConfigMutual = `
Expand All @@ -199,7 +191,7 @@ kind: DestinationRule
metadata:
name: originate-tls-for-server-filebased-mutual
spec:
host: "server.{{.AppNamespace}}.svc.cluster.local"
host: "external-service.{{.AppNamespace}}.svc.cluster.local"
trafficPolicy:
portLevelSettings:
- port:
Expand All @@ -209,7 +201,7 @@ spec:
clientCertificate: /etc/certs/custom/cert-chain.pem
privateKey: /etc/certs/custom/key.pem
caCertificates: {{.RootCertPath}}
sni: server.{{.AppNamespace}}.svc.cluster.local
sni: external-service.{{.AppNamespace}}.svc.cluster.local
`
)

Expand Down Expand Up @@ -245,79 +237,6 @@ func createDestinationRule(t framework.TestContext, serviceNamespace namespace.I
return buf
}

// setupEcho creates two namespaces app and service. It also brings up two echo instances server and
// client in app namespace. HTTP and HTTPS port on the server echo are set up. Sidecar scope config
// is applied to only allow egress traffic to service namespace such that when client to server calls are made
// we are able to simulate "external" traffic by going outside this namespace. Egress Gateway is set up in the
// service namespace to handle egress for "external" calls.
func setupEcho(t framework.TestContext, ctx resource.Context) (echo.Instance, echo.Instance, namespace.Instance, namespace.Instance) {
appsNamespace := namespace.NewOrFail(t, ctx, namespace.Config{
Prefix: "app",
Inject: true,
})
serviceNamespace := namespace.NewOrFail(t, ctx, namespace.Config{
Prefix: "service",
Inject: true,
})

var internalClient, externalServer echo.Instance
deployment.New(ctx).
With(&internalClient, echo.Config{
Service: "client",
Namespace: appsNamespace,
Ports: []echo.Port{},
Subsets: []echo.SubsetConfig{{
Version: "v1",
}},
Cluster: ctx.Clusters().Default(),
}).
With(&externalServer, echo.Config{
Service: "server",
Namespace: serviceNamespace,
Ports: []echo.Port{
{
// Plain HTTP port only used to route request to egress gateway
Name: "http",
Protocol: protocol.HTTP,
ServicePort: 80,
WorkloadPort: 8080,
},
{
// HTTPS port
Name: "https",
Protocol: protocol.HTTPS,
ServicePort: 443,
WorkloadPort: 8443,
TLS: true,
},
},
// Set up TLS certs on the server. This will make the server listen with these credentials.
TLSSettings: &common.TLSSettings{
// Echo has these test certs baked into the docker image
RootCert: mustReadCert(t, "root-cert.pem"),
ClientCert: mustReadCert(t, "cert-chain.pem"),
Key: mustReadCert(t, "key.pem"),
// Override hostname to match the SAN in the cert we are using
Hostname: "server.default.svc",
},
Subsets: []echo.SubsetConfig{{
Version: "v1",
Annotations: echo.NewAnnotations().SetBool(echo.SidecarInject, false),
}},
Cluster: ctx.Clusters().Default(),
}).
BuildOrFail(t)

// Apply Egress Gateway for service namespace to originate external traffic
createGateway(t, ctx, appsNamespace, serviceNamespace)

if err := WaitUntilNotCallable(internalClient, externalServer); err != nil {
t.Fatalf("failed to apply sidecar, %v", err)
}

return internalClient, externalServer, appsNamespace, serviceNamespace
}

const (
Gateway = `
apiVersion: networking.istio.io/v1alpha3
Expand All @@ -333,7 +252,7 @@ spec:
name: https-filebased
protocol: HTTPS
hosts:
- server.{{.ServerNamespace}}.svc.cluster.local
- external-service.{{.ServerNamespace}}.svc.cluster.local
tls:
mode: ISTIO_MUTUAL
---
Expand All @@ -351,7 +270,7 @@ spec:
number: 443
tls:
mode: ISTIO_MUTUAL
sni: server.{{.ServerNamespace}}.svc.cluster.local
sni: external-service.{{.ServerNamespace}}.svc.cluster.local
`
VirtualService = `
apiVersion: networking.istio.io/v1alpha3
Expand All @@ -360,7 +279,7 @@ metadata:
name: route-via-egressgateway-filebased
spec:
hosts:
- server.{{.ServerNamespace}}.svc.cluster.local
- external-service.{{.ServerNamespace}}.svc.cluster.local
gateways:
- istio-egressgateway-filebased
- mesh
Expand All @@ -382,7 +301,7 @@ spec:
port: 443
route:
- destination:
host: server.{{.ServerNamespace}}.svc.cluster.local
host: external-service.{{.ServerNamespace}}.svc.cluster.local
port:
number: 443
weight: 100
Expand Down
Loading