blob: a0716587a254dcb4c34de153cb9a1b0cfbf4a12a [file] [log] [blame]
//go:build integ
// +build integ
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cacustomroot
import (
"fmt"
"os"
"path"
"testing"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/test/echo/common/scheme"
"github.com/apache/dubbo-go-pixiu/pkg/test/env"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/check"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/match"
)
const (
httpPlaintext = "http-plaintext"
httpMTLS = "http-mtls"
tcpPlaintext = "tcp-plaintext"
tcpMTLS = "tcp-mtls"
tcpWL = "tcp-wl"
passThrough = "tcp-mtls-pass-through"
// policy to enable mTLS in client and server:
// ports with plaintext: 8090 (http) and 8092 (tcp)
// ports with mTLS: 8091 (http), 8093 (tcp) and 9000 (tcp passthrough).
policy = `
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: "mtls"
spec:
selector:
matchLabels:
app: server
mtls:
mode: STRICT
portLevelMtls:
8090:
mode: DISABLE
8092:
mode: DISABLE
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: server
spec:
host: server.%s.svc.cluster.local
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
portLevelSettings:
- port:
number: 8090
tls:
mode: DISABLE
- port:
number: 8092
tls:
mode: DISABLE
`
)
// TestTrustDomainValidation tests the trust domain validation when mTLS is enabled.
// The trust domain validation should reject a request if it's not from the trust domains configured in the mesh config.
// The test uses naked client (no sidecar) with custom certificates of different trust domains and covers the following:
// - plaintext requests are not affected
// - same trust domain (cluster.local) and aliases (trust-domain-foo and trust-domain-bar)
// - works for both HTTP and TCP protocol
// - works for pass through filter chains
func TestTrustDomainValidation(t *testing.T) {
framework.NewTest(t).Features("security.peer.trust-domain-validation").Run(
func(ctx framework.TestContext) {
if ctx.AllClusters().IsMulticluster() {
ctx.Skip("https://github.com/istio/istio/issues/37307")
}
testNS := apps.Namespace
ctx.ConfigIstio().YAML(testNS.Name(), fmt.Sprintf(policy, testNS.Name())).ApplyOrFail(ctx)
trustDomains := map[string]struct {
cert string
key string
}{
"foo": {
cert: readFile(ctx, "workload-foo-cert.pem"),
key: readFile(ctx, "workload-foo-key.pem"),
},
"bar": {
cert: readFile(ctx, "workload-bar-cert.pem"),
key: readFile(ctx, "workload-bar-key.pem"),
},
}
for _, cluster := range ctx.Clusters() {
ctx.NewSubTest(fmt.Sprintf("From %s", cluster.StableName())).Run(func(t framework.TestContext) {
// naked: only test app without sidecar, send requests from trust domain aliases
// client: app with sidecar, send request from cluster.local
// server: app with sidecar, verify requests from cluster.local or trust domain aliases
client := match.Cluster(cluster).FirstOrFail(t, apps.Client)
naked := match.Cluster(cluster).FirstOrFail(t, apps.Naked)
server := match.Cluster(cluster).FirstOrFail(t, apps.Server)
verify := func(ctx framework.TestContext, from echo.Instance, td, port string, s scheme.Instance, allow bool) {
ctx.Helper()
want := "allow"
if !allow {
want = "deny"
}
name := fmt.Sprintf("%s[%s]->server:%s[%s]", from.Config().Service, td, port, want)
ctx.NewSubTest(name).Run(func(t framework.TestContext) {
t.Helper()
opt := echo.CallOptions{
To: apps.Server,
Port: echo.Port{
Name: port,
},
Address: "server",
Scheme: s,
TLS: echo.TLS{
Cert: trustDomains[td].cert,
Key: trustDomains[td].key,
},
}
if port == passThrough {
// Manually make the request for pass through port.
opt = echo.CallOptions{
ToWorkload: server,
Port: echo.Port{Name: tcpWL},
TLS: echo.TLS{
Cert: trustDomains[td].cert,
Key: trustDomains[td].key,
},
Check: check.OK(),
}
}
if !allow {
opt.Check = check.ErrorContains("tls: unknown certificate")
}
from.CallOrFail(t, opt)
})
}
// Request using plaintext should always allowed.
verify(t, client, "plaintext", httpPlaintext, scheme.HTTP, true)
verify(t, client, "plaintext", tcpPlaintext, scheme.TCP, true)
verify(t, naked, "plaintext", httpPlaintext, scheme.HTTP, true)
verify(t, naked, "plaintext", tcpPlaintext, scheme.TCP, true)
// Request from local trust domain should always allowed.
verify(t, client, "cluster.local", httpMTLS, scheme.HTTP, true)
verify(t, client, "cluster.local", tcpMTLS, scheme.TCP, true)
// Trust domain foo is added as trust domain alias.
// Request from trust domain bar should be denied.
// Request from trust domain foo should be allowed.
verify(t, naked, "bar", httpMTLS, scheme.HTTPS, false)
verify(t, naked, "bar", tcpMTLS, scheme.TCP, false)
verify(t, naked, "bar", passThrough, scheme.TCP, false)
verify(t, naked, "foo", httpMTLS, scheme.HTTPS, true)
verify(t, naked, "foo", tcpMTLS, scheme.TCP, true)
verify(t, naked, "foo", passThrough, scheme.TCP, true)
})
}
})
}
func readFile(ctx framework.TestContext, name string) string {
data, err := os.ReadFile(path.Join(env.IstioSrc, "samples/certs", name))
if err != nil {
ctx.Fatal(err)
}
return string(data)
}