@@ -28,6 +28,7 @@ import (
2828 "fmt"
2929 "net/http"
3030 "net/netip"
31+ "os/exec"
3132 "sort"
3233 "strings"
3334 "testing"
@@ -618,6 +619,141 @@ func TestMixNsAndServiceWaypoint(t *testing.T) {
618619 })
619620}
620621
622+ func TestAuthorizationL4 (t * testing.T ) {
623+ framework .NewTest (t ).Run (func (t framework.TestContext ) {
624+ t .NewSubTest ("L4 Authorization" ).Run (func (t framework.TestContext ) {
625+ // Enable authorizaiton offload to xdp.
626+
627+ if len (apps .ServiceWithWaypointAtServiceGranularity ) == 0 {
628+ t .Fatal (fmt .Errorf ("need at least 1 instance of apps.ServiceWithWaypointAtServiceGranularity" ))
629+ }
630+ src := apps .ServiceWithWaypointAtServiceGranularity [0 ]
631+
632+ clients := src .WorkloadsOrFail (t )
633+ dst := apps .EnrolledToKmesh
634+
635+ addresses := clients .Addresses ()
636+ if len (addresses ) < 2 {
637+ t .Fatal (fmt .Errorf ("need at least 2 clients" ))
638+ }
639+ selectedAddress := addresses [0 ]
640+
641+ authzCases := []struct {
642+ name string
643+ spec string
644+ }{
645+ {
646+ name : "allow" ,
647+ spec : `
648+ action: ALLOW
649+ ` ,
650+ },
651+ {
652+ name : "deny" ,
653+ spec : `
654+ action: DENY
655+ ` ,
656+ },
657+ }
658+
659+ chooseChecker := func (action string , ip string ) echo.Checker {
660+ switch action {
661+ case "allow" :
662+ if ip != selectedAddress {
663+ return check .NotOK ()
664+ } else {
665+ return check .OK ()
666+ }
667+ case "deny" :
668+ if ip != selectedAddress {
669+ return check .OK ()
670+ } else {
671+ return check .NotOK ()
672+ }
673+ default :
674+ t .Fatal ("invalid action" )
675+ }
676+
677+ return check .OK ()
678+ }
679+
680+ count := 0
681+ workloads := dst .WorkloadsOrFail (t )
682+ for _ , client := range workloads {
683+ if count == len (workloads ) {
684+ break
685+ }
686+ podName := client .PodName ()
687+ namespace := apps .Namespace .Name ()
688+ timeout := time .After (5 * time .Second )
689+ ticker := time .NewTicker (500 * time .Millisecond )
690+ defer ticker .Stop ()
691+ InnerLoop:
692+ for {
693+ select {
694+ case <- timeout :
695+ t .Fatalf ("Timeout: XDP eBPF program not found on pod %s" , podName )
696+ case <- ticker .C :
697+ cmd := exec .Command ("kubectl" , "exec" , "-n" , namespace , podName , "--" , "sh" , "-c" , "ip a | grep xdp" )
698+ output , err := cmd .CombinedOutput ()
699+ if err == nil && len (output ) > 0 {
700+ t .Logf ("XDP program is loaded on pod %s" , podName )
701+ count ++
702+ break InnerLoop
703+ }
704+ t .Logf ("Waiting for XDP program to load on pod %s: %v" , podName , err )
705+ }
706+ }
707+ }
708+
709+ for _ , tc := range authzCases {
710+ t .ConfigIstio ().Eval (apps .Namespace .Name (), map [string ]string {
711+ "Destination" : dst .Config ().Service ,
712+ "Ip" : selectedAddress ,
713+ }, `apiVersion: security.istio.io/v1beta1
714+ kind: AuthorizationPolicy
715+ metadata:
716+ name: policy
717+ spec:
718+ selector:
719+ matchLabels:
720+ app: "{{.Destination}}"
721+ ` + tc .spec + `
722+ rules:
723+ - from:
724+ - source:
725+ ipBlocks:
726+ - "{{.Ip}}"
727+ ` ).ApplyOrFail (t )
728+
729+ for _ , client := range clients {
730+ opt := echo.CallOptions {
731+ To : dst ,
732+ Port : echo.Port {Name : "tcp" },
733+ Scheme : scheme .TCP ,
734+ NewConnectionPerRequest : true ,
735+ // Due to the mechanism of Kmesh L4 authorization, we need to set the timeout slightly longer.
736+ Timeout : time .Minute * 2 ,
737+ }
738+
739+ var name string
740+ if client .Address () != selectedAddress {
741+ name = tc .name + ", not selected address"
742+ } else {
743+ name = tc .name + ", selected address"
744+ }
745+
746+ opt .Check = chooseChecker (tc .name , client .Address ())
747+
748+ t .NewSubTestf ("%v" , name ).Run (func (t framework.TestContext ) {
749+ src .WithWorkloads (client ).CallOrFail (t , opt )
750+ })
751+ }
752+ }
753+ })
754+ })
755+ }
756+
621757func TestBookinfo (t * testing.T ) {
622758 framework .NewTest (t ).Run (func (t framework.TestContext ) {
623759 namespace := apps .Namespace .Name ()
0 commit comments