Repository: kmesh-net/kmesh

Issue:
`kmeshctl authz` can't trigger offload authorization enable
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks!
-->

**What happened**:

I tried to write a E2E test for offload authorization #641, but it would fail sometimes.

For the failed case, it seems that `kmeshctl authz` can never trigger the offload authorizaiton unless Kmesh is reinstalled.

**What you expected to happen**:

`kmeshctl authz` should always work fine.

**How to reproduce it (as minimally and precisely as possible)**:

```bash
root@dev:~/kmesh# ./test/e2e/run_test.sh --skip-install-dep --skip-build -run "TestAuthorizationL4"
```

Based on PR #641 , continue to run the above command, which is used to reinstall Kmesh and run `TestAuthorizationL4`. When the test case failed, the environment can no longer use `kmeshctl authz` to trigger offload authorization.

**Anything else we need to know?**:

**Environment**:

- Kmesh version:
- Kmesh mode(kmesh has `Kernel-Native Mode` and `Duel-Engine Mode`):
- Istio version:
- Kernel version:
- Others:


Hints from issue comments:
@hzxuzhonghu: Did you check the km_config map to confrim it

Solution patch:
diff --git a/bpf/kmesh/workload/include/authz.h b/bpf/kmesh/workload/include/authz.h
index 0db08768..97e8b496 100644
--- a/bpf/kmesh/workload/include/authz.h
+++ b/bpf/kmesh/workload/include/authz.h
@@ -35,6 +35,7 @@ struct match_context {
     __u8 policy_index;
     bool need_tailcall_to_userspace;
     __u8 n_rules;
+    int auth_result;
     wl_policies_v *policies;
     void *rulesPtr;
 };
@@ -573,7 +574,14 @@ int policies_check(struct xdp_md *ctx)
     }
     policy = map_lookup_authz(policyId);
     if (!policy) {
-        return XDP_PASS;
+        // Currently, authz in xdp only support ip and port,
+        // if any principal or namespace type policy is configured,
+        // we need to tailcall to userspace.
+        if (match_ctx->need_tailcall_to_userspace) {
+            bpf_tail_call(ctx, &map_of_xdp_tailcall, TAIL_CALL_AUTH_IN_USER_SPACE);
+            return XDP_PASS;
+        }
+        return match_ctx->auth_result;
     } else {
         rulesPtr = KMESH_GET_PTR_VAL(policy->rules, void *);
         if (!rulesPtr) {
@@ -672,15 +680,10 @@ int policy_check(struct xdp_md *ctx)
         }
         return match_ctx->action == ISTIO__SECURITY__ACTION__DENY ? XDP_DROP : XDP_PASS;
     }
-
-    match_ctx->policy_index++;
-    if (match_ctx->policy_index >= MAX_MEMBER_NUM_PER_POLICY) {
-        if (match_ctx->need_tailcall_to_userspace) {
-            bpf_tail_call(ctx, &map_of_xdp_tailcall, TAIL_CALL_AUTH_IN_USER_SPACE);
-            return XDP_PASS;
-        }
-        return XDP_PASS;
+    if (match_ctx->auth_result == XDP_PASS) {
+        match_ctx->auth_result = match_ctx->action == ISTIO__SECURITY__ACTION__DENY ? XDP_PASS : XDP_DROP;
     }
+    match_ctx->policy_index++;
 
     ret = bpf_map_update_elem(&kmesh_tc_args, &tuple_key, match_ctx, BPF_ANY);
     if (ret < 0) {
diff --git a/bpf/kmesh/workload/xdp.c b/bpf/kmesh/workload/xdp.c
index 28629b16..befc72d0 100644
--- a/bpf/kmesh/workload/xdp.c
+++ b/bpf/kmesh/workload/xdp.c
@@ -132,6 +132,7 @@ int xdp_authz(struct xdp_md *ctx)
         match_ctx.policies = policies;
         match_ctx.need_tailcall_to_userspace = false;
         match_ctx.policy_index = 0;
+        match_ctx.auth_result = XDP_PASS;
         ret = bpf_map_update_elem(&kmesh_tc_args, &tuple_key, &match_ctx, BPF_ANY);
         if (ret < 0) {
             BPF_LOG(ERR, AUTH, "Failed to update map, error: %d", ret);
diff --git a/go.mod b/go.mod
index 36ed0d86..50dc4e83 100644
--- a/go.mod
+++ b/go.mod
@@ -16,8 +16,8 @@ require (
 	github.com/fsnotify/fsnotify v1.8.0
 	github.com/hashicorp/go-multierror v1.1.1
 	github.com/miekg/dns v1.1.62
-	github.com/prometheus/client_golang v1.20.5
-	github.com/prometheus/common v0.61.0
+	github.com/prometheus/client_golang v1.21.0
+	github.com/prometheus/common v0.62.0
 	github.com/safchain/ethtool v0.5.10
 	github.com/sirupsen/logrus v1.9.3
 	github.com/spf13/cobra v1.8.1
@@ -25,10 +25,10 @@ require (
 	github.com/stretchr/testify v1.10.0
 	github.com/vishvananda/netlink v1.3.0
 	golang.org/x/sys v0.30.0
-	google.golang.org/grpc v1.69.4
+	google.golang.org/grpc v1.70.0
 	google.golang.org/protobuf v1.36.3
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1
-	istio.io/api v1.24.2
+	istio.io/api v1.24.3
 	istio.io/istio v0.0.0-20241214032803-7754674f65d3
 	istio.io/pkg v0.0.0-20231221211216-7635388a563e
 	k8s.io/api v0.32.2
@@ -82,7 +82,7 @@ require (
 	github.com/florianl/go-nflog/v2 v2.1.0 // indirect
 	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
 	github.com/go-errors/errors v1.5.1 // indirect
-	github.com/go-jose/go-jose/v3 v3.0.3 // indirect
+	github.com/go-jose/go-jose/v3 v3.0.4 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-openapi/jsonpointer v0.21.0 // indirect
diff --git a/go.sum b/go.sum
index 1f28bb4e..e6348e47 100644
--- a/go.sum
+++ b/go.sum
@@ -128,8 +128,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
 github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
 github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
 github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
-github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
-github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
+github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
+github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
 github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
 github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -354,13 +354,13 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240409071808-615f978279ca/go.mod h1
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
+github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
+github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
-github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/prometheus/prometheus v0.300.1 h1:9KKcTTq80gkzmXW0Et/QCFSrBPgmwiS3Hlcxc6o8KlM=
@@ -624,8 +624,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -663,8 +663,8 @@ helm.sh/helm/v3 v3.16.3 h1:kb8bSxMeRJ+knsK/ovvlaVPfdis0X3/ZhYCSFRP+YmY=
 helm.sh/helm/v3 v3.16.3/go.mod h1:zeVWGDR4JJgiRbT3AnNsjYaX8OTJlIE9zC+Q7F7iUSU=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-istio.io/api v1.24.2 h1:jYjcN6Iq0RPtQj/3KMFsybxmfqmjGN/dxhL7FGJEdIM=
-istio.io/api v1.24.2/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I=
+istio.io/api v1.24.3 h1:iwWWPM0uEQ+oxRHvIWoB8MQ4bjF3dRQj+M5IDVczg0M=
+istio.io/api v1.24.3/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I=
 istio.io/client-go v1.24.2-0.20241206152608-3892aa679051 h1:KgYFUC2tfDQnq0usZPUsAcCwXoHVX+m9TmOYcC7uL44=
 istio.io/client-go v1.24.2-0.20241206152608-3892aa679051/go.mod h1:dgZ9EmJzh1EECzf6nQhwNL4R6RvlyeH/RXeNeNp/MRg=
 istio.io/istio v0.0.0-20241214032803-7754674f65d3 h1:remhEc2PJofM+dUQezuaESREq/QuBlRdaT7R90mQW1w=
diff --git a/pkg/auth/policy_store.go b/pkg/auth/policy_store.go
index 0bec9aac..50f9ebfa 100644
--- a/pkg/auth/policy_store.go
+++ b/pkg/auth/policy_store.go
@@ -124,3 +124,15 @@ func (ps *policyStore) getByNamespace(namespace string) []string {
 	}
 	return nil
 }
+
+// List returns a copied list of all policies
+func (p *policyStore) list() []*security.Authorization {
+	p.rwLock.RLock()
+	defer p.rwLock.RUnlock()
+	out := make([]*security.Authorization, 0, len(p.byKey))
+	for _, pol := range p.byKey {
+		out = append(out, pol)
+	}
+
+	return out
+}
diff --git a/pkg/auth/rbac.go b/pkg/auth/rbac.go
index 438b6489..b5c21e16 100644
--- a/pkg/auth/rbac.go
+++ b/pkg/auth/rbac.go
@@ -516,3 +516,8 @@ func (r *Rbac) getIdentityByIp(ip []byte) Identity {
 		serviceAccount: workload.GetServiceAccount(),
 	}
 }
+
+// List returns a copied list of all policies
+func (r *Rbac) PoliciesList() []*security.Authorization {
+	return r.policyStore.list()
+}
diff --git a/pkg/controller/workload/workload_processor.go b/pkg/controller/workload/workload_processor.go
index 57e3ad74..7bce75d2 100644
--- a/pkg/controller/workload/workload_processor.go
+++ b/pkg/controller/workload/workload_processor.go
@@ -909,6 +909,7 @@ func (p *Processor) handleAuthorizationTypeResponse(rsp *service_discovery_v3.De
 		if err := rbac.UpdatePolicy(authPolicy); err != nil {
 			return err
 		}
+
 		policyKey := authPolicy.ResourceName()
 		if err := maps_v2.AuthorizationUpdate(p.hashName.Hash(policyKey), authPolicy); err != nil {
 			return fmt.Errorf("AuthorizationUpdate %s failed %v ", policyKey, err)
diff --git a/pkg/status/api.go b/pkg/status/api.go
index 415ae2a8..0a53df2f 100644
--- a/pkg/status/api.go
+++ b/pkg/status/api.go
@@ -20,6 +20,7 @@ import (
 	"net"
 
 	"kmesh.net/kmesh/api/v2/workloadapi"
+	"kmesh.net/kmesh/api/v2/workloadapi/security"
 )
 
 type Workload struct {
@@ -75,6 +76,14 @@ type Service struct {
 	Waypoint     *Waypoint           `json:"waypoint"`
 }
 
+type AuthorizationPolicy struct {
+	Name      string           `json:"name"`
+	Namespace string           `json:"namespace"`
+	Scope     string           `json:"scope"`
+	Action    string           `json:"action"`
+	Rules     []*security.Rule `json:"rules"`
+}
+
 type NetworkAddress struct {
 	// Network represents the network this address is on.
 	Network string
@@ -162,3 +171,15 @@ func ConvertService(s *workloadapi.Service) *Service {
 
 	return out
 }
+
+func ConvertAuthorizationPolicy(p *security.Authorization) *AuthorizationPolicy {
+	out := &AuthorizationPolicy{
+		Name:      p.GetName(),
+		Namespace: p.GetNamespace(),
+		Scope:     p.GetScope().String(),
+		Action:    p.GetAction().String(),
+		Rules:     p.Rules,
+	}
+
+	return out
+}
diff --git a/pkg/status/status_server.go b/pkg/status/status_server.go
index 9b0fecaa..e12e9332 100644
--- a/pkg/status/status_server.go
+++ b/pkg/status/status_server.go
@@ -31,7 +31,6 @@ import (
 	"google.golang.org/protobuf/encoding/protojson"
 
 	adminv2 "kmesh.net/kmesh/api/v2/admin"
-	"kmesh.net/kmesh/api/v2/workloadapi/security"
 	"kmesh.net/kmesh/daemon/options"
 	"kmesh.net/kmesh/pkg/bpf"
 	bpfads "kmesh.net/kmesh/pkg/bpf/ads"
@@ -461,8 +460,7 @@ func (s *Server) configDumpAds(w http.ResponseWriter, r *http.Request) {
 type WorkloadDump struct {
 	Workloads []*Workload
 	Services  []*Service
-	// TODO: add authorization
-	Policies []*security.Authorization
+	Policies  []*AuthorizationPolicy
 }
 
 func (s *Server) configDumpWorkload(w http.ResponseWriter, r *http.Request) {
@@ -474,9 +472,11 @@ func (s *Server) configDumpWorkload(w http.ResponseWriter, r *http.Request) {
 
 	workloads := client.WorkloadController.Processor.WorkloadCache.List()
 	services := client.WorkloadController.Processor.ServiceCache.List()
+	policies := client.WorkloadController.Rbac.PoliciesList()
 	workloadDump := WorkloadDump{
 		Workloads: make([]*Workload, 0, len(workloads)),
 		Services:  make([]*Service, 0, len(services)),
+		Policies:  make([]*AuthorizationPolicy, 0, len(policies)),
 	}
 	for _, w := range workloads {
 		workloadDump.Workloads = append(workloadDump.Workloads, ConvertWorkload(w))
@@ -484,6 +484,9 @@ func (s *Server) configDumpWorkload(w http.ResponseWriter, r *http.Request) {
 	for _, s := range services {
 		workloadDump.Services = append(workloadDump.Services, ConvertService(s))
 	}
+	for _, p := range policies {
+		workloadDump.Policies = append(workloadDump.Policies, ConvertAuthorizationPolicy(p))
+	}
 	printWorkloadDump(w, workloadDump)
 }
 



Test patch:
diff --git a/pkg/status/status_server_test.go b/pkg/status/status_server_test.go
index a6f4034b..4e47c72b 100644
--- a/pkg/status/status_server_test.go
+++ b/pkg/status/status_server_test.go
@@ -37,7 +37,9 @@ import (
 	"kmesh.net/kmesh/api/v2/core"
 	"kmesh.net/kmesh/api/v2/listener"
 	"kmesh.net/kmesh/api/v2/workloadapi"
+	"kmesh.net/kmesh/api/v2/workloadapi/security"
 	"kmesh.net/kmesh/daemon/options"
+	"kmesh.net/kmesh/pkg/auth"
 	"kmesh.net/kmesh/pkg/bpf"
 	maps_v2 "kmesh.net/kmesh/pkg/cache/v2/maps"
 	"kmesh.net/kmesh/pkg/constants"
@@ -198,10 +200,18 @@ func TestServer_configDumpWorkload(t *testing.T) {
 				},
 			},
 		}}
+	policy := &security.Authorization{
+		Name:      "policy",
+		Namespace: "ns",
+		Scope:     security.Scope_GLOBAL,
+		Action:    security.Action_ALLOW,
+	}
 	fakeWorkloadCache := cache.NewWorkloadCache()
 	fakeServiceCache := cache.NewServiceCache()
 	fakeWorkloadCache.AddOrUpdateWorkload(w1)
 	fakeServiceCache.AddOrUpdateService(svc)
+	fakeAuth := auth.NewRbac(fakeWorkloadCache)
+	fakeAuth.UpdatePolicy(policy)
 	// Create a new instance of the Server struct
 	server := &Server{
 		xdsClient: &controller.XdsClient{
@@ -210,6 +220,7 @@ func TestServer_configDumpWorkload(t *testing.T) {
 					WorkloadCache: fakeWorkloadCache,
 					ServiceCache:  fakeServiceCache,
 				},
+				Rbac: fakeAuth,
 			},
 		},
 	}
diff --git a/pkg/status/testdata/workload_configdump.json b/pkg/status/testdata/workload_configdump.json
index 2ac5286f..ad0288c3 100644
--- a/pkg/status/testdata/workload_configdump.json
+++ b/pkg/status/testdata/workload_configdump.json
@@ -51,5 +51,13 @@
             }
         }
     ],
-    "Policies": null
+    "Policies": [
+        {
+            "name": "policy",
+            "namespace": "ns",
+            "scope": "GLOBAL",
+            "action": "ALLOW",
+            "rules": null
+        }
+    ]
 }
\ No newline at end of file