Merge pull request #315 from Yan0613/master

[feature] enable zero trust security
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4e75804..32e9cca 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,10 +38,10 @@
 #        golang:
 #          - '1.21'
 #    steps:
-#      - uses: actions/setup-go@v3
+#      - uses: actions/setup-go@v5
 #        with:
 #          go-version: ${{ matrix.go_version }}
-#      - uses: actions/checkout@v3
+#      - uses: actions/checkout@v4
 #      - name: golangci-lint
 #        uses: golangci/golangci-lint-action@v3.4.0
 #        with:
@@ -56,7 +56,7 @@
     steps:
       - uses: actions/checkout@v4
       - name: Set up Go 1.x
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: go.mod
       - name: Download dependencies
@@ -67,7 +67,7 @@
           go test ./... -gcflags=-l -coverprofile=coverage.txt -covermode=atomic
       - name: "Upload test result"
         if: always()
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: test-coverage
           path: "**/coverage.txt"
@@ -81,7 +81,7 @@
     steps:
       - uses: actions/checkout@v4
       - name: Set up Go 1.x
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: go.mod
 
@@ -95,7 +95,7 @@
     steps:
       - uses: actions/checkout@v4
       - name: Set up Go 1.x
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: go.mod
       - name: Download dependencies
diff --git a/.github/workflows/dubbo-release.yaml b/.github/workflows/dubbo-release.yaml
index b25ea64..8bd682f 100644
--- a/.github/workflows/dubbo-release.yaml
+++ b/.github/workflows/dubbo-release.yaml
@@ -41,7 +41,7 @@
     steps:
       - uses: actions/checkout@v4
       - name: Set up Go 1.x
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: go.mod
 
diff --git a/.github/workflows/dubboctl-release.yaml b/.github/workflows/dubboctl-release.yaml
index e4d1468..b1c9e92 100644
--- a/.github/workflows/dubboctl-release.yaml
+++ b/.github/workflows/dubboctl-release.yaml
@@ -41,7 +41,7 @@
     steps:
       - uses: actions/checkout@v4
       - name: Set up Go 1.x
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version-file: go.mod
 
diff --git a/.gitignore b/.gitignore
index 08c1ddb..49e7864 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,4 +43,6 @@
 
 bin/
 build/
-vendor/
\ No newline at end of file
+vendor/
+
+dockercompose.yaml
\ No newline at end of file
diff --git a/api/common/v1alpha1/tls/tls.go b/api/common/v1alpha1/tls/tls.go
new file mode 100644
index 0000000..99df9c7
--- /dev/null
+++ b/api/common/v1alpha1/tls/tls.go
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+// +kubebuilder:object:generate=true
+package tls
+
+import (
+	"slices"
+
+	tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/validators"
+)
+
+// +kubebuilder:validation:Enum=TLSAuto;TLS10;TLS11;TLS12;TLS13
+type TlsVersion string
+
+const (
+	TLSVersionAuto TlsVersion = "TLSAuto"
+	TLSVersion10   TlsVersion = "TLS10"
+	TLSVersion11   TlsVersion = "TLS11"
+	TLSVersion12   TlsVersion = "TLS12"
+	TLSVersion13   TlsVersion = "TLS13"
+)
+
+var TlsVersionOrder = map[TlsVersion]int{
+	TLSVersion10: 0,
+	TLSVersion11: 1,
+	TLSVersion12: 2,
+	TLSVersion13: 3,
+}
+
+var allTlsVersions = []string{string(TLSVersionAuto), string(TLSVersion10), string(TLSVersion11), string(TLSVersion12), string(TLSVersion13)}
+
+type Version struct {
+	// Min defines minimum supported version. One of `TLSAuto`, `TLS10`, `TLS11`, `TLS12`, `TLS13`.
+	// +kubebuilder:default=TLSAuto
+	Min *TlsVersion `json:"min,omitempty"`
+	// Max defines maximum supported version. One of `TLSAuto`, `TLS10`, `TLS11`, `TLS12`, `TLS13`.
+	// +kubebuilder:default=TLSAuto
+	Max *TlsVersion `json:"max,omitempty"`
+}
+
+func ValidateVersion(version *Version) validators.ValidationError {
+	var verr validators.ValidationError
+	path := validators.Root()
+	specificMin := false
+	specificMax := false
+	if version.Min != nil {
+		if !slices.Contains(allTlsVersions, string(*version.Min)) {
+			verr.AddErrorAt(path.Field("min"), validators.MakeFieldMustBeOneOfErr("min", allTlsVersions...))
+		} else if *version.Min != TLSVersionAuto {
+			specificMin = true
+		}
+	}
+	if version.Max != nil {
+		if !slices.Contains(allTlsVersions, string(*version.Max)) {
+			verr.AddErrorAt(path.Field("max"), validators.MakeFieldMustBeOneOfErr("max", allTlsVersions...))
+		} else if *version.Max != TLSVersionAuto {
+			specificMax = true
+		}
+	}
+
+	if specificMin && specificMax && TlsVersionOrder[*version.Min] > TlsVersionOrder[*version.Max] {
+		verr.AddViolationAt(path.Field("min"), "min version must be lower than max")
+	}
+
+	return verr
+}
+
+func ToTlsVersion(version *TlsVersion) tlsv3.TlsParameters_TlsProtocol {
+	switch *version {
+	case TLSVersion13:
+		return tlsv3.TlsParameters_TLSv1_3
+	case TLSVersion12:
+		return tlsv3.TlsParameters_TLSv1_2
+	case TLSVersion11:
+		return tlsv3.TlsParameters_TLSv1_1
+	case TLSVersion10:
+		return tlsv3.TlsParameters_TLSv1_0
+	case TLSVersionAuto:
+		fallthrough
+	default:
+		return tlsv3.TlsParameters_TLS_AUTO
+	}
+}
+
+// +kubebuilder:validation:Enum=ECDHE-ECDSA-AES128-GCM-SHA256;ECDHE-ECDSA-AES256-GCM-SHA384;ECDHE-ECDSA-CHACHA20-POLY1305;ECDHE-RSA-AES128-GCM-SHA256;ECDHE-RSA-AES256-GCM-SHA384;ECDHE-RSA-CHACHA20-POLY1305
+type TlsCipher string
+
+const (
+	EcdheEcdsaAes128GcmSha256  TlsCipher = "ECDHE-ECDSA-AES128-GCM-SHA256"
+	EcdheEcdsaAes256GcmSha384  TlsCipher = "ECDHE-ECDSA-AES256-GCM-SHA384"
+	EcdheEcdsaChacha20Poly1305 TlsCipher = "ECDHE-ECDSA-CHACHA20-POLY1305"
+	EcdheRsaAes128GcmSha256    TlsCipher = "ECDHE-RSA-AES128-GCM-SHA256"
+	EcdheRsaAes256GcmSha384    TlsCipher = "ECDHE-RSA-AES256-GCM-SHA384"
+	EcdheRsaChacha20Poly1305   TlsCipher = "ECDHE-RSA-CHACHA20-POLY1305"
+)
+
+var AllCiphers = []string{
+	string(EcdheEcdsaAes128GcmSha256),
+	string(EcdheEcdsaAes256GcmSha384),
+	string(EcdheEcdsaChacha20Poly1305),
+	string(EcdheRsaAes128GcmSha256),
+	string(EcdheRsaAes256GcmSha384),
+	string(EcdheRsaChacha20Poly1305),
+}
+
+type TlsCiphers []TlsCipher
diff --git a/api/common/v1alpha1/tls/zz_generated.deepcopy.go b/api/common/v1alpha1/tls/zz_generated.deepcopy.go
new file mode 100644
index 0000000..7f73f6c
--- /dev/null
+++ b/api/common/v1alpha1/tls/zz_generated.deepcopy.go
@@ -0,0 +1,51 @@
+//go:build !ignore_autogenerated
+
+// Code generated by controller-gen. DO NOT EDIT.
+
+package tls
+
+import ()
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in TlsCiphers) DeepCopyInto(out *TlsCiphers) {
+	{
+		in := &in
+		*out = make(TlsCiphers, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TlsCiphers.
+func (in TlsCiphers) DeepCopy() TlsCiphers {
+	if in == nil {
+		return nil
+	}
+	out := new(TlsCiphers)
+	in.DeepCopyInto(out)
+	return *out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Version) DeepCopyInto(out *Version) {
+	*out = *in
+	if in.Min != nil {
+		in, out := &in.Min, &out.Min
+		*out = new(TlsVersion)
+		**out = **in
+	}
+	if in.Max != nil {
+		in, out := &in.Max, &out.Max
+		*out = new(TlsVersion)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Version.
+func (in *Version) DeepCopy() *Version {
+	if in == nil {
+		return nil
+	}
+	out := new(Version)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/api/mesh/options.pb.go b/api/mesh/options.pb.go
index da895ec..a738fc9 100644
--- a/api/mesh/options.pb.go
+++ b/api/mesh/options.pb.go
@@ -1,24 +1,19 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.1
-// 	protoc        v3.20.0
-// source: api/mesh/options.proto
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.12.4
+// source: options.proto
 
 package mesh
 
 import (
+	descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
 	sync "sync"
 )
 
-import (
-	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-
-	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-
-	descriptorpb "google.golang.org/protobuf/types/descriptorpb"
-)
-
 const (
 	// Verify that this generated code is sufficiently up-to-date.
 	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
@@ -67,7 +62,7 @@
 func (x *DubboResourceOptions) Reset() {
 	*x = DubboResourceOptions{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_api_mesh_options_proto_msgTypes[0]
+		mi := &file_options_proto_msgTypes[0]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -80,7 +75,7 @@
 func (*DubboResourceOptions) ProtoMessage() {}
 
 func (x *DubboResourceOptions) ProtoReflect() protoreflect.Message {
-	mi := &file_api_mesh_options_proto_msgTypes[0]
+	mi := &file_options_proto_msgTypes[0]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -93,7 +88,7 @@
 
 // Deprecated: Use DubboResourceOptions.ProtoReflect.Descriptor instead.
 func (*DubboResourceOptions) Descriptor() ([]byte, []int) {
-	return file_api_mesh_options_proto_rawDescGZIP(), []int{0}
+	return file_options_proto_rawDescGZIP(), []int{0}
 }
 
 func (x *DubboResourceOptions) GetName() string {
@@ -221,7 +216,7 @@
 func (x *DubboWsOptions) Reset() {
 	*x = DubboWsOptions{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_api_mesh_options_proto_msgTypes[1]
+		mi := &file_options_proto_msgTypes[1]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -234,7 +229,7 @@
 func (*DubboWsOptions) ProtoMessage() {}
 
 func (x *DubboWsOptions) ProtoReflect() protoreflect.Message {
-	mi := &file_api_mesh_options_proto_msgTypes[1]
+	mi := &file_options_proto_msgTypes[1]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -247,7 +242,7 @@
 
 // Deprecated: Use DubboWsOptions.ProtoReflect.Descriptor instead.
 func (*DubboWsOptions) Descriptor() ([]byte, []int) {
-	return file_api_mesh_options_proto_rawDescGZIP(), []int{1}
+	return file_options_proto_rawDescGZIP(), []int{1}
 }
 
 func (x *DubboWsOptions) GetName() string {
@@ -292,7 +287,7 @@
 func (x *DubboDdsOptions) Reset() {
 	*x = DubboDdsOptions{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_api_mesh_options_proto_msgTypes[2]
+		mi := &file_options_proto_msgTypes[2]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -305,7 +300,7 @@
 func (*DubboDdsOptions) ProtoMessage() {}
 
 func (x *DubboDdsOptions) ProtoReflect() protoreflect.Message {
-	mi := &file_api_mesh_options_proto_msgTypes[2]
+	mi := &file_options_proto_msgTypes[2]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -318,7 +313,7 @@
 
 // Deprecated: Use DubboDdsOptions.ProtoReflect.Descriptor instead.
 func (*DubboDdsOptions) Descriptor() ([]byte, []int) {
-	return file_api_mesh_options_proto_rawDescGZIP(), []int{2}
+	return file_options_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *DubboDdsOptions) GetSendToGlobal() bool {
@@ -350,7 +345,7 @@
 func (x *DubboPolicyOptions) Reset() {
 	*x = DubboPolicyOptions{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_api_mesh_options_proto_msgTypes[3]
+		mi := &file_options_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -363,7 +358,7 @@
 func (*DubboPolicyOptions) ProtoMessage() {}
 
 func (x *DubboPolicyOptions) ProtoReflect() protoreflect.Message {
-	mi := &file_api_mesh_options_proto_msgTypes[3]
+	mi := &file_options_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -376,7 +371,7 @@
 
 // Deprecated: Use DubboPolicyOptions.ProtoReflect.Descriptor instead.
 func (*DubboPolicyOptions) Descriptor() ([]byte, []int) {
-	return file_api_mesh_options_proto_rawDescGZIP(), []int{3}
+	return file_options_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *DubboPolicyOptions) GetSkipRegistration() bool {
@@ -393,137 +388,136 @@
 	return ""
 }
 
-var file_api_mesh_options_proto_extTypes = []protoimpl.ExtensionInfo{
+var file_options_proto_extTypes = []protoimpl.ExtensionInfo{
 	{
-		ExtendedType:  (*descriptorpb.MessageOptions)(nil),
+		ExtendedType:  (*descriptor.MessageOptions)(nil),
 		ExtensionType: (*DubboResourceOptions)(nil),
 		Field:         43534533,
 		Name:          "dubbo.mesh.resource",
 		Tag:           "bytes,43534533,opt,name=resource",
-		Filename:      "api/mesh/options.proto",
+		Filename:      "options.proto",
 	},
 	{
-		ExtendedType:  (*descriptorpb.MessageOptions)(nil),
+		ExtendedType:  (*descriptor.MessageOptions)(nil),
 		ExtensionType: (*DubboPolicyOptions)(nil),
 		Field:         43534534,
 		Name:          "dubbo.mesh.policy",
 		Tag:           "bytes,43534534,opt,name=policy",
-		Filename:      "api/mesh/options.proto",
+		Filename:      "options.proto",
 	},
 }
 
-// Extension fields to descriptorpb.MessageOptions.
+// Extension fields to descriptor.MessageOptions.
 var (
 	// optional dubbo.mesh.DubboResourceOptions resource = 43534533;
-	E_Resource = &file_api_mesh_options_proto_extTypes[0] // 'dubbo'
+	E_Resource = &file_options_proto_extTypes[0] // 'dubbo'
 	// optional dubbo.mesh.DubboPolicyOptions policy = 43534534;
-	E_Policy = &file_api_mesh_options_proto_extTypes[1] // 'dubbo'
+	E_Policy = &file_options_proto_extTypes[1] // 'dubbo'
 )
 
-var File_api_mesh_options_proto protoreflect.FileDescriptor
+var File_options_proto protoreflect.FileDescriptor
 
-var file_api_mesh_options_proto_rawDesc = []byte{
-	0x0a, 0x16, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e,
-	0x6d, 0x65, 0x73, 0x68, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72,
-	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe8, 0x04, 0x0a, 0x14, 0x44, 0x75, 0x62, 0x62, 0x6f,
-	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
-	0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
-	0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61,
-	0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12,
-	0x18, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x6b, 0x69,
-	0x70, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06,
-	0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
-	0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x03, 0x64, 0x64, 0x73, 0x18, 0x0a, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68,
-	0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x44, 0x64, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x52, 0x03, 0x64, 0x64, 0x73, 0x12, 0x2a, 0x0a, 0x02, 0x77, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44,
-	0x75, 0x62, 0x62, 0x6f, 0x57, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x02, 0x77,
-	0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73,
-	0x70, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x63, 0x6f, 0x70,
-	0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x6b,
-	0x69, 0x70, 0x5f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x77, 0x72,
-	0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x6b,
-	0x69, 0x70, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x57, 0x72, 0x61, 0x70,
-	0x70, 0x65, 0x72, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x6f,
-	0x5f, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e,
-	0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x27,
-	0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
-	0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
-	0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x70, 0x6c, 0x75, 0x72, 0x61,
-	0x6c, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x44, 0x69, 0x73, 0x70,
-	0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x65, 0x78,
-	0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x0e, 0x69, 0x73, 0x45, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c,
-	0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70,
-	0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x11,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
-	0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x21,
-	0x0a, 0x0c, 0x68, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x12,
-	0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x68, 0x61, 0x73, 0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74,
-	0x73, 0x22, 0x78, 0x0a, 0x0e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x57, 0x73, 0x4f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61,
-	0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x12,
-	0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01,
-	0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a,
-	0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x59, 0x0a, 0x0f, 0x44,
-	0x75, 0x62, 0x62, 0x6f, 0x44, 0x64, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24,
-	0x0a, 0x0e, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x47, 0x6c,
-	0x6f, 0x62, 0x61, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f, 0x5f,
-	0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64,
-	0x54, 0x6f, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0x59, 0x0a, 0x12, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x50,
-	0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x11,
-	0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f,
-	0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x67,
-	0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75,
-	0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61,
-	0x6c, 0x3a, 0x60, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e,
-	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
-	0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc5,
-	0x91, 0xe1, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e,
-	0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
-	0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75,
-	0x72, 0x63, 0x65, 0x3a, 0x5a, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x2e,
-	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
-	0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc6,
-	0x91, 0xe1, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e,
-	0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
-	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42,
-	0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70,
-	0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72,
-	0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+var file_options_proto_rawDesc = []byte{
+	0x0a, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+	0x0a, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x1a, 0x20, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73,
+	0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe8, 0x04,
+	0x0a, 0x14, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79,
+	0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16,
+	0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06,
+	0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
+	0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
+	0x12, 0x2b, 0x0a, 0x11, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x6b, 0x69,
+	0x70, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a,
+	0x03, 0x64, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x64, 0x75, 0x62,
+	0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x44, 0x64, 0x73,
+	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x03, 0x64, 0x64, 0x73, 0x12, 0x2a, 0x0a, 0x02,
+	0x77, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f,
+	0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x57, 0x73, 0x4f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x52, 0x02, 0x77, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x6f, 0x70,
+	0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28,
+	0x08, 0x52, 0x0e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
+	0x65, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e,
+	0x65, 0x74, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x16, 0x73, 0x6b, 0x69, 0x70, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65,
+	0x74, 0x65, 0x73, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x61,
+	0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x6f, 0x5f, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x18,
+	0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x54, 0x6f, 0x49, 0x6e,
+	0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
+	0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e,
+	0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e,
+	0x0a, 0x13, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
+	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x6c, 0x75,
+	0x72, 0x61, 0x6c, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27,
+	0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61,
+	0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x45, 0x78, 0x70, 0x65, 0x72,
+	0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x64, 0x64, 0x69, 0x74,
+	0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x6f,
+	0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x61, 0x64, 0x64,
+	0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x43, 0x6f,
+	0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x73,
+	0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x68, 0x61, 0x73,
+	0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x73, 0x22, 0x78, 0x0a, 0x0e, 0x44, 0x75, 0x62, 0x62,
+	0x6f, 0x57, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16,
+	0x0a, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+	0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f,
+	0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f,
+	0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x6f, 0x6e, 0x6c,
+	0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x4f, 0x6e,
+	0x6c, 0x79, 0x22, 0x59, 0x0a, 0x0f, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x44, 0x64, 0x73, 0x4f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f,
+	0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73,
+	0x65, 0x6e, 0x64, 0x54, 0x6f, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x73,
+	0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x08, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0x59, 0x0a,
+	0x12, 0x44, 0x75, 0x62, 0x62, 0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x67, 0x69,
+	0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
+	0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x06, 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0x3a, 0x60, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f,
+	0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc5, 0x91, 0xe1, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20,
+	0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62,
+	0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x5a, 0x0a, 0x06, 0x70, 0x6f,
+	0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xc6, 0x91, 0xe1, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e,
+	0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x75, 0x62, 0x62,
+	0x6f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x06,
+	0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
+	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62,
+	0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x61, 0x70, 0x69,
+	0x2f, 0x6d, 0x65, 0x73, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
-	file_api_mesh_options_proto_rawDescOnce sync.Once
-	file_api_mesh_options_proto_rawDescData = file_api_mesh_options_proto_rawDesc
+	file_options_proto_rawDescOnce sync.Once
+	file_options_proto_rawDescData = file_options_proto_rawDesc
 )
 
-func file_api_mesh_options_proto_rawDescGZIP() []byte {
-	file_api_mesh_options_proto_rawDescOnce.Do(func() {
-		file_api_mesh_options_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_mesh_options_proto_rawDescData)
+func file_options_proto_rawDescGZIP() []byte {
+	file_options_proto_rawDescOnce.Do(func() {
+		file_options_proto_rawDescData = protoimpl.X.CompressGZIP(file_options_proto_rawDescData)
 	})
-	return file_api_mesh_options_proto_rawDescData
+	return file_options_proto_rawDescData
 }
 
-var file_api_mesh_options_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
-var file_api_mesh_options_proto_goTypes = []interface{}{
-	(*DubboResourceOptions)(nil),        // 0: dubbo.mesh.DubboResourceOptions
-	(*DubboWsOptions)(nil),              // 1: dubbo.mesh.DubboWsOptions
-	(*DubboDdsOptions)(nil),             // 2: dubbo.mesh.DubboDdsOptions
-	(*DubboPolicyOptions)(nil),          // 3: dubbo.mesh.DubboPolicyOptions
-	(*descriptorpb.MessageOptions)(nil), // 4: google.protobuf.MessageOptions
+var file_options_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_options_proto_goTypes = []interface{}{
+	(*DubboResourceOptions)(nil),      // 0: dubbo.mesh.DubboResourceOptions
+	(*DubboWsOptions)(nil),            // 1: dubbo.mesh.DubboWsOptions
+	(*DubboDdsOptions)(nil),           // 2: dubbo.mesh.DubboDdsOptions
+	(*DubboPolicyOptions)(nil),        // 3: dubbo.mesh.DubboPolicyOptions
+	(*descriptor.MessageOptions)(nil), // 4: google.protobuf.MessageOptions
 }
-var file_api_mesh_options_proto_depIdxs = []int32{
+var file_options_proto_depIdxs = []int32{
 	2, // 0: dubbo.mesh.DubboResourceOptions.dds:type_name -> dubbo.mesh.DubboDdsOptions
 	1, // 1: dubbo.mesh.DubboResourceOptions.ws:type_name -> dubbo.mesh.DubboWsOptions
 	4, // 2: dubbo.mesh.resource:extendee -> google.protobuf.MessageOptions
@@ -537,13 +531,13 @@
 	0, // [0:2] is the sub-list for field type_name
 }
 
-func init() { file_api_mesh_options_proto_init() }
-func file_api_mesh_options_proto_init() {
-	if File_api_mesh_options_proto != nil {
+func init() { file_options_proto_init() }
+func file_options_proto_init() {
+	if File_options_proto != nil {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_api_mesh_options_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_options_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*DubboResourceOptions); i {
 			case 0:
 				return &v.state
@@ -555,7 +549,7 @@
 				return nil
 			}
 		}
-		file_api_mesh_options_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_options_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*DubboWsOptions); i {
 			case 0:
 				return &v.state
@@ -567,7 +561,7 @@
 				return nil
 			}
 		}
-		file_api_mesh_options_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_options_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*DubboDdsOptions); i {
 			case 0:
 				return &v.state
@@ -579,7 +573,7 @@
 				return nil
 			}
 		}
-		file_api_mesh_options_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_options_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*DubboPolicyOptions); i {
 			case 0:
 				return &v.state
@@ -596,19 +590,19 @@
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_api_mesh_options_proto_rawDesc,
+			RawDescriptor: file_options_proto_rawDesc,
 			NumEnums:      0,
 			NumMessages:   4,
 			NumExtensions: 2,
 			NumServices:   0,
 		},
-		GoTypes:           file_api_mesh_options_proto_goTypes,
-		DependencyIndexes: file_api_mesh_options_proto_depIdxs,
-		MessageInfos:      file_api_mesh_options_proto_msgTypes,
-		ExtensionInfos:    file_api_mesh_options_proto_extTypes,
+		GoTypes:           file_options_proto_goTypes,
+		DependencyIndexes: file_options_proto_depIdxs,
+		MessageInfos:      file_options_proto_msgTypes,
+		ExtensionInfos:    file_options_proto_extTypes,
 	}.Build()
-	File_api_mesh_options_proto = out.File
-	file_api_mesh_options_proto_rawDesc = nil
-	file_api_mesh_options_proto_goTypes = nil
-	file_api_mesh_options_proto_depIdxs = nil
+	File_options_proto = out.File
+	file_options_proto_rawDesc = nil
+	file_options_proto_goTypes = nil
+	file_options_proto_depIdxs = nil
 }
diff --git a/api/mesh/v1alpha1/dds.proto b/api/mesh/v1alpha1/dds.proto
index 6bd6f04..aab133b 100644
--- a/api/mesh/v1alpha1/dds.proto
+++ b/api/mesh/v1alpha1/dds.proto
@@ -3,7 +3,6 @@
 package dubbo.mesh.v1alpha1;
 
 option go_package = "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1";
-
 import "envoy/service/discovery/v3/discovery.proto";
 import "google/protobuf/any.proto";
 import "google/protobuf/duration.proto";
diff --git a/api/mesh/v1alpha1/mesh.pb.go b/api/mesh/v1alpha1/mesh.pb.go
index b6926cf..9cb07e5 100644
--- a/api/mesh/v1alpha1/mesh.pb.go
+++ b/api/mesh/v1alpha1/mesh.pb.go
@@ -1,29 +1,22 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.1
-// 	protoc        v3.20.0
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.12.4
 // source: api/mesh/v1alpha1/mesh.proto
 
 package v1alpha1
 
 import (
+	_ "github.com/apache/dubbo-kubernetes/api/mesh"
+	duration "github.com/golang/protobuf/ptypes/duration"
+	_struct "github.com/golang/protobuf/ptypes/struct"
+	wrappers "github.com/golang/protobuf/ptypes/wrappers"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
 	sync "sync"
 )
 
-import (
-	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-
-	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-
-	structpb "google.golang.org/protobuf/types/known/structpb"
-	wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
-)
-
-import (
-	_ "github.com/apache/dubbo-kubernetes/api/mesh"
-)
-
 const (
 	// Verify that this generated code is sufficiently up-to-date.
 	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
@@ -31,6 +24,58 @@
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
+type CertificateAuthorityBackend_Mode int32
+
+const (
+	// A STRICT mode implies that the server validates the connection and
+	// accepts only encrypted TLS traffic
+	CertificateAuthorityBackend_STRICT CertificateAuthorityBackend_Mode = 0
+	// A PERMISSIVE mode implies that the outbounds encrypt traffic the same way
+	// it happens in strict mode, but inbounds accept both TLS and plaintext
+	// traffic. This allows applications residing in the mesh to accept requests
+	// from outside of the mesh.
+	CertificateAuthorityBackend_PERMISSIVE CertificateAuthorityBackend_Mode = 1
+)
+
+// Enum value maps for CertificateAuthorityBackend_Mode.
+var (
+	CertificateAuthorityBackend_Mode_name = map[int32]string{
+		0: "STRICT",
+		1: "PERMISSIVE",
+	}
+	CertificateAuthorityBackend_Mode_value = map[string]int32{
+		"STRICT":     0,
+		"PERMISSIVE": 1,
+	}
+)
+
+func (x CertificateAuthorityBackend_Mode) Enum() *CertificateAuthorityBackend_Mode {
+	p := new(CertificateAuthorityBackend_Mode)
+	*p = x
+	return p
+}
+
+func (x CertificateAuthorityBackend_Mode) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (CertificateAuthorityBackend_Mode) Descriptor() protoreflect.EnumDescriptor {
+	return file_api_mesh_v1alpha1_mesh_proto_enumTypes[0].Descriptor()
+}
+
+func (CertificateAuthorityBackend_Mode) Type() protoreflect.EnumType {
+	return &file_api_mesh_v1alpha1_mesh_proto_enumTypes[0]
+}
+
+func (x CertificateAuthorityBackend_Mode) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use CertificateAuthorityBackend_Mode.Descriptor instead.
+func (CertificateAuthorityBackend_Mode) EnumDescriptor() ([]byte, []int) {
+	return file_api_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{1, 0}
+}
+
 // Mesh defines configuration of a single mesh.
 type Mesh struct {
 	state         protoimpl.MessageState
@@ -133,7 +178,10 @@
 	// Dataplane certificate settings
 	DpCert *CertificateAuthorityBackend_DpCert `protobuf:"bytes,3,opt,name=dpCert,proto3" json:"dpCert,omitempty"`
 	// Configuration of the backend
-	Conf *structpb.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
+	Conf *_struct.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
+	// Mode defines the behaviour of inbound listeners with regard to traffic
+	// encryption
+	Mode CertificateAuthorityBackend_Mode `protobuf:"varint,5,opt,name=mode,proto3,enum=dubbo.mesh.v1alpha1.CertificateAuthorityBackend_Mode" json:"mode,omitempty"`
 }
 
 func (x *CertificateAuthorityBackend) Reset() {
@@ -189,13 +237,20 @@
 	return nil
 }
 
-func (x *CertificateAuthorityBackend) GetConf() *structpb.Struct {
+func (x *CertificateAuthorityBackend) GetConf() *_struct.Struct {
 	if x != nil {
 		return x.Conf
 	}
 	return nil
 }
 
+func (x *CertificateAuthorityBackend) GetMode() CertificateAuthorityBackend_Mode {
+	if x != nil {
+		return x.Mode
+	}
+	return CertificateAuthorityBackend_STRICT
+}
+
 // Networking defines the networking configuration of the mesh
 type Networking struct {
 	state         protoimpl.MessageState
@@ -315,11 +370,11 @@
 	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 	// Percentage of traces that will be sent to the backend (range 0.0 - 100.0).
 	// Empty value defaults to 100.0%
-	Sampling *wrapperspb.DoubleValue `protobuf:"bytes,2,opt,name=sampling,proto3" json:"sampling,omitempty"`
+	Sampling *wrappers.DoubleValue `protobuf:"bytes,2,opt,name=sampling,proto3" json:"sampling,omitempty"`
 	// Type of the backend (Dubbo ships with 'zipkin')
 	Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
 	// Configuration of the backend
-	Conf *structpb.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
+	Conf *_struct.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
 }
 
 func (x *TracingBackend) Reset() {
@@ -361,7 +416,7 @@
 	return ""
 }
 
-func (x *TracingBackend) GetSampling() *wrapperspb.DoubleValue {
+func (x *TracingBackend) GetSampling() *wrappers.DoubleValue {
 	if x != nil {
 		return x.Sampling
 	}
@@ -375,7 +430,7 @@
 	return ""
 }
 
-func (x *TracingBackend) GetConf() *structpb.Struct {
+func (x *TracingBackend) GetConf() *_struct.Struct {
 	if x != nil {
 		return x.Conf
 	}
@@ -398,7 +453,7 @@
 	// Determines whether client and server spans will share the same span
 	// context. Default: true.
 	// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/trace/v3/zipkin.proto#config-trace-v3-zipkinconfig
-	SharedSpanContext *wrapperspb.BoolValue `protobuf:"bytes,4,opt,name=sharedSpanContext,proto3" json:"sharedSpanContext,omitempty"`
+	SharedSpanContext *wrappers.BoolValue `protobuf:"bytes,4,opt,name=sharedSpanContext,proto3" json:"sharedSpanContext,omitempty"`
 }
 
 func (x *ZipkinTracingBackendConfig) Reset() {
@@ -454,7 +509,7 @@
 	return ""
 }
 
-func (x *ZipkinTracingBackendConfig) GetSharedSpanContext() *wrapperspb.BoolValue {
+func (x *ZipkinTracingBackendConfig) GetSharedSpanContext() *wrappers.BoolValue {
 	if x != nil {
 		return x.SharedSpanContext
 	}
@@ -534,7 +589,7 @@
 	// Type of the backend (Dubbo ships with 'tcp' and 'file')
 	Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
 	// Configuration of the backend
-	Conf *structpb.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
+	Conf *_struct.Struct `protobuf:"bytes,4,opt,name=conf,proto3" json:"conf,omitempty"`
 }
 
 func (x *LoggingBackend) Reset() {
@@ -590,7 +645,7 @@
 	return ""
 }
 
-func (x *LoggingBackend) GetConf() *structpb.Struct {
+func (x *LoggingBackend) GetConf() *_struct.Struct {
 	if x != nil {
 		return x.Conf
 	}
@@ -820,6 +875,8 @@
 
 	// Rotation settings
 	Rotation *CertificateAuthorityBackend_DpCert_Rotation `protobuf:"bytes,1,opt,name=rotation,proto3" json:"rotation,omitempty"`
+	// Timeout on request to CA for DP certificate generation and retrieval
+	RequestTimeout *duration.Duration `protobuf:"bytes,2,opt,name=requestTimeout,proto3" json:"requestTimeout,omitempty"`
 }
 
 func (x *CertificateAuthorityBackend_DpCert) Reset() {
@@ -861,6 +918,13 @@
 	return nil
 }
 
+func (x *CertificateAuthorityBackend_DpCert) GetRequestTimeout() *duration.Duration {
+	if x != nil {
+		return x.RequestTimeout
+	}
+	return nil
+}
+
 // Rotation defines rotation settings for Dataplane certificate
 type CertificateAuthorityBackend_DpCert_Rotation struct {
 	state         protoimpl.MessageState
@@ -917,7 +981,7 @@
 	unknownFields protoimpl.UnknownFields
 
 	// Control the passthrough cluster
-	Passthrough *wrapperspb.BoolValue `protobuf:"bytes,1,opt,name=passthrough,proto3" json:"passthrough,omitempty"`
+	Passthrough *wrappers.BoolValue `protobuf:"bytes,1,opt,name=passthrough,proto3" json:"passthrough,omitempty"`
 }
 
 func (x *Networking_Outbound) Reset() {
@@ -952,7 +1016,7 @@
 	return file_api_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{2, 0}
 }
 
-func (x *Networking_Outbound) GetPassthrough() *wrapperspb.BoolValue {
+func (x *Networking_Outbound) GetPassthrough() *wrappers.BoolValue {
 	if x != nil {
 		return x.Passthrough
 	}
@@ -969,6 +1033,8 @@
 	0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72,
 	0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
 	0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
 	0x6f, 0x1a, 0x16, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69,
 	0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x87, 0x04, 0x0a, 0x04, 0x4d, 0x65,
 	0x73, 0x68, 0x12, 0x32, 0x0a, 0x04, 0x6d, 0x74, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
@@ -1003,7 +1069,7 @@
 	0x02, 0x18, 0x01, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x04, 0x52, 0x02, 0x10, 0x01, 0xaa, 0x8c, 0x89,
 	0xa6, 0x01, 0x08, 0x3a, 0x06, 0x0a, 0x04, 0x6d, 0x65, 0x73, 0x68, 0xaa, 0x8c, 0x89, 0xa6, 0x01,
 	0x0a, 0x3a, 0x08, 0x12, 0x06, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x73, 0xaa, 0x8c, 0x89, 0xa6, 0x01,
-	0x02, 0x68, 0x01, 0x22, 0xd8, 0x02, 0x0a, 0x1b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+	0x02, 0x68, 0x01, 0x22, 0x8a, 0x04, 0x0a, 0x1b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
 	0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x61, 0x63, 0x6b,
 	0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
 	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
@@ -1015,88 +1081,99 @@
 	0x43, 0x65, 0x72, 0x74, 0x52, 0x06, 0x64, 0x70, 0x43, 0x65, 0x72, 0x74, 0x12, 0x2b, 0x0a, 0x04,
 	0x63, 0x6f, 0x6e, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f,
 	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
-	0x75, 0x63, 0x74, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x1a, 0x92, 0x01, 0x0a, 0x06, 0x44, 0x70,
-	0x43, 0x65, 0x72, 0x74, 0x12, 0x5c, 0x0a, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d,
-	0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x65, 0x72,
-	0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
-	0x79, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x44, 0x70, 0x43, 0x65, 0x72, 0x74, 0x2e,
-	0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69,
-	0x6f, 0x6e, 0x1a, 0x2a, 0x0a, 0x08, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e,
-	0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x9c,
-	0x01, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x12, 0x44, 0x0a,
-	0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x28, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61,
-	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67,
-	0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f,
-	0x75, 0x6e, 0x64, 0x1a, 0x48, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12,
-	0x3c, 0x0a, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65,
-	0x52, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x22, 0x72, 0x0a,
-	0x07, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61,
-	0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
-	0x12, 0x3f, 0x0a, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e,
-	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67,
-	0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
-	0x73, 0x22, 0x9f, 0x01, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63,
-	0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x73, 0x61, 0x6d, 0x70,
-	0x6c, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
-	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75,
-	0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69,
-	0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x04,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x63,
-	0x6f, 0x6e, 0x66, 0x22, 0xbe, 0x01, 0x0a, 0x1a, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x54, 0x72,
-	0x61, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x03, 0x75, 0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x31,
-	0x32, 0x38, 0x62, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x74, 0x72, 0x61,
-	0x63, 0x65, 0x49, 0x64, 0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x70,
-	0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
-	0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x11, 0x73, 0x68,
-	0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18,
-	0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75,
-	0x65, 0x52, 0x11, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f, 0x6e,
-	0x74, 0x65, 0x78, 0x74, 0x22, 0x72, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12,
-	0x26, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e,
-	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
-	0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65,
-	0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x75, 0x62, 0x62,
-	0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
-	0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x08,
-	0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x22, 0x7d, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x67,
-	0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16,
-	0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
-	0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x6f,
-	0x6e, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
-	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63,
-	0x74, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0x2e, 0x0a, 0x18, 0x46, 0x69, 0x6c, 0x65, 0x4c,
-	0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x33, 0x0a, 0x17, 0x54, 0x63, 0x70, 0x4c, 0x6f,
-	0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x69, 0x0a, 0x07,
-	0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x1a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
-	0x69, 0x74, 0x79, 0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61,
-	0x6e, 0x63, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6c, 0x6f, 0x63,
+	0x75, 0x63, 0x74, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x12, 0x49, 0x0a, 0x04, 0x6d, 0x6f, 0x64,
+	0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e,
+	0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x65,
+	0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+	0x74, 0x79, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04,
+	0x6d, 0x6f, 0x64, 0x65, 0x1a, 0xd5, 0x01, 0x0a, 0x06, 0x44, 0x70, 0x43, 0x65, 0x72, 0x74, 0x12,
+	0x5c, 0x0a, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x40, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76,
+	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+	0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x61, 0x63, 0x6b,
+	0x65, 0x6e, 0x64, 0x2e, 0x44, 0x70, 0x43, 0x65, 0x72, 0x74, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x52, 0x08, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a,
+	0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
+	0x1a, 0x2a, 0x0a, 0x08, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a,
+	0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x22, 0x0a, 0x04,
+	0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, 0x10, 0x00,
+	0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x56, 0x45, 0x10, 0x01,
+	0x22, 0x9c, 0x01, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x12,
+	0x44, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x28, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76,
+	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69,
+	0x6e, 0x67, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x08, 0x6f, 0x75, 0x74,
+	0x62, 0x6f, 0x75, 0x6e, 0x64, 0x1a, 0x48, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
+	0x64, 0x12, 0x3c, 0x0a, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c,
+	0x75, 0x65, 0x52, 0x0b, 0x70, 0x61, 0x73, 0x73, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x22,
+	0x72, 0x0a, 0x07, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x65,
+	0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65,
+	0x6e, 0x64, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x02,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73,
+	0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x69,
+	0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65,
+	0x6e, 0x64, 0x73, 0x22, 0x9f, 0x01, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x42,
+	0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x73, 0x61,
+	0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,
+	0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x73, 0x61, 0x6d, 0x70,
+	0x6c, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x6f, 0x6e, 0x66,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52,
+	0x04, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0xbe, 0x01, 0x0a, 0x1a, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e,
+	0x54, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49,
+	0x64, 0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x74,
+	0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x12, 0x1e, 0x0a, 0x0a,
+	0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x11,
+	0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
+	0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61,
+	0x6c, 0x75, 0x65, 0x52, 0x11, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43,
+	0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x72, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e,
+	0x67, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b,
+	0x65, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75,
+	0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x61, 0x63,
+	0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x75,
+	0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+	0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
+	0x52, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x22, 0x7d, 0x0a, 0x0e, 0x4c, 0x6f,
+	0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04,
+	0x63, 0x6f, 0x6e, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
+	0x75, 0x63, 0x74, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0x2e, 0x0a, 0x18, 0x46, 0x69, 0x6c,
+	0x65, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x33, 0x0a, 0x17, 0x54, 0x63, 0x70,
+	0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x69,
+	0x0a, 0x07, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x1a, 0x6c, 0x6f, 0x63,
 	0x61, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61,
-	0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x7a, 0x6f, 0x6e, 0x65, 0x45,
-	0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x7a, 0x6f, 0x6e,
-	0x65, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75,
-	0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62,
-	0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x61, 0x70,
-	0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62,
-	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6c,
+	0x6f, 0x63, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64,
+	0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x7a, 0x6f, 0x6e,
+	0x65, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x7a,
+	0x6f, 0x6e, 0x65, 0x45, 0x67, 0x72, 0x65, 0x73, 0x73, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74,
+	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64,
+	0x75, 0x62, 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f,
+	0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+	0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1111,50 +1188,55 @@
 	return file_api_mesh_v1alpha1_mesh_proto_rawDescData
 }
 
+var file_api_mesh_v1alpha1_mesh_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_api_mesh_v1alpha1_mesh_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
 var file_api_mesh_v1alpha1_mesh_proto_goTypes = []interface{}{
-	(*Mesh)(nil),                                        // 0: dubbo.mesh.v1alpha1.Mesh
-	(*CertificateAuthorityBackend)(nil),                 // 1: dubbo.mesh.v1alpha1.CertificateAuthorityBackend
-	(*Networking)(nil),                                  // 2: dubbo.mesh.v1alpha1.Networking
-	(*Tracing)(nil),                                     // 3: dubbo.mesh.v1alpha1.Tracing
-	(*TracingBackend)(nil),                              // 4: dubbo.mesh.v1alpha1.TracingBackend
-	(*ZipkinTracingBackendConfig)(nil),                  // 5: dubbo.mesh.v1alpha1.ZipkinTracingBackendConfig
-	(*Logging)(nil),                                     // 6: dubbo.mesh.v1alpha1.Logging
-	(*LoggingBackend)(nil),                              // 7: dubbo.mesh.v1alpha1.LoggingBackend
-	(*FileLoggingBackendConfig)(nil),                    // 8: dubbo.mesh.v1alpha1.FileLoggingBackendConfig
-	(*TcpLoggingBackendConfig)(nil),                     // 9: dubbo.mesh.v1alpha1.TcpLoggingBackendConfig
-	(*Routing)(nil),                                     // 10: dubbo.mesh.v1alpha1.Routing
-	(*Mesh_Mtls)(nil),                                   // 11: dubbo.mesh.v1alpha1.Mesh.Mtls
-	(*CertificateAuthorityBackend_DpCert)(nil),          // 12: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
-	(*CertificateAuthorityBackend_DpCert_Rotation)(nil), // 13: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
-	(*Networking_Outbound)(nil),                         // 14: dubbo.mesh.v1alpha1.Networking.Outbound
-	(*structpb.Struct)(nil),                             // 15: google.protobuf.Struct
-	(*wrapperspb.DoubleValue)(nil),                      // 16: google.protobuf.DoubleValue
-	(*wrapperspb.BoolValue)(nil),                        // 17: google.protobuf.BoolValue
+	(CertificateAuthorityBackend_Mode)(0),               // 0: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.Mode
+	(*Mesh)(nil),                                        // 1: dubbo.mesh.v1alpha1.Mesh
+	(*CertificateAuthorityBackend)(nil),                 // 2: dubbo.mesh.v1alpha1.CertificateAuthorityBackend
+	(*Networking)(nil),                                  // 3: dubbo.mesh.v1alpha1.Networking
+	(*Tracing)(nil),                                     // 4: dubbo.mesh.v1alpha1.Tracing
+	(*TracingBackend)(nil),                              // 5: dubbo.mesh.v1alpha1.TracingBackend
+	(*ZipkinTracingBackendConfig)(nil),                  // 6: dubbo.mesh.v1alpha1.ZipkinTracingBackendConfig
+	(*Logging)(nil),                                     // 7: dubbo.mesh.v1alpha1.Logging
+	(*LoggingBackend)(nil),                              // 8: dubbo.mesh.v1alpha1.LoggingBackend
+	(*FileLoggingBackendConfig)(nil),                    // 9: dubbo.mesh.v1alpha1.FileLoggingBackendConfig
+	(*TcpLoggingBackendConfig)(nil),                     // 10: dubbo.mesh.v1alpha1.TcpLoggingBackendConfig
+	(*Routing)(nil),                                     // 11: dubbo.mesh.v1alpha1.Routing
+	(*Mesh_Mtls)(nil),                                   // 12: dubbo.mesh.v1alpha1.Mesh.Mtls
+	(*CertificateAuthorityBackend_DpCert)(nil),          // 13: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
+	(*CertificateAuthorityBackend_DpCert_Rotation)(nil), // 14: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
+	(*Networking_Outbound)(nil),                         // 15: dubbo.mesh.v1alpha1.Networking.Outbound
+	(*_struct.Struct)(nil),                              // 16: google.protobuf.Struct
+	(*wrappers.DoubleValue)(nil),                        // 17: google.protobuf.DoubleValue
+	(*wrappers.BoolValue)(nil),                          // 18: google.protobuf.BoolValue
+	(*duration.Duration)(nil),                           // 19: google.protobuf.Duration
 }
 var file_api_mesh_v1alpha1_mesh_proto_depIdxs = []int32{
-	11, // 0: dubbo.mesh.v1alpha1.Mesh.mtls:type_name -> dubbo.mesh.v1alpha1.Mesh.Mtls
-	3,  // 1: dubbo.mesh.v1alpha1.Mesh.tracing:type_name -> dubbo.mesh.v1alpha1.Tracing
-	6,  // 2: dubbo.mesh.v1alpha1.Mesh.logging:type_name -> dubbo.mesh.v1alpha1.Logging
-	2,  // 3: dubbo.mesh.v1alpha1.Mesh.networking:type_name -> dubbo.mesh.v1alpha1.Networking
-	10, // 4: dubbo.mesh.v1alpha1.Mesh.routing:type_name -> dubbo.mesh.v1alpha1.Routing
-	12, // 5: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.dpCert:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
-	15, // 6: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.conf:type_name -> google.protobuf.Struct
-	14, // 7: dubbo.mesh.v1alpha1.Networking.outbound:type_name -> dubbo.mesh.v1alpha1.Networking.Outbound
-	4,  // 8: dubbo.mesh.v1alpha1.Tracing.backends:type_name -> dubbo.mesh.v1alpha1.TracingBackend
-	16, // 9: dubbo.mesh.v1alpha1.TracingBackend.sampling:type_name -> google.protobuf.DoubleValue
-	15, // 10: dubbo.mesh.v1alpha1.TracingBackend.conf:type_name -> google.protobuf.Struct
-	17, // 11: dubbo.mesh.v1alpha1.ZipkinTracingBackendConfig.sharedSpanContext:type_name -> google.protobuf.BoolValue
-	7,  // 12: dubbo.mesh.v1alpha1.Logging.backends:type_name -> dubbo.mesh.v1alpha1.LoggingBackend
-	15, // 13: dubbo.mesh.v1alpha1.LoggingBackend.conf:type_name -> google.protobuf.Struct
-	1,  // 14: dubbo.mesh.v1alpha1.Mesh.Mtls.backends:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend
-	13, // 15: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.rotation:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
-	17, // 16: dubbo.mesh.v1alpha1.Networking.Outbound.passthrough:type_name -> google.protobuf.BoolValue
-	17, // [17:17] is the sub-list for method output_type
-	17, // [17:17] is the sub-list for method input_type
-	17, // [17:17] is the sub-list for extension type_name
-	17, // [17:17] is the sub-list for extension extendee
-	0,  // [0:17] is the sub-list for field type_name
+	12, // 0: dubbo.mesh.v1alpha1.Mesh.mtls:type_name -> dubbo.mesh.v1alpha1.Mesh.Mtls
+	4,  // 1: dubbo.mesh.v1alpha1.Mesh.tracing:type_name -> dubbo.mesh.v1alpha1.Tracing
+	7,  // 2: dubbo.mesh.v1alpha1.Mesh.logging:type_name -> dubbo.mesh.v1alpha1.Logging
+	3,  // 3: dubbo.mesh.v1alpha1.Mesh.networking:type_name -> dubbo.mesh.v1alpha1.Networking
+	11, // 4: dubbo.mesh.v1alpha1.Mesh.routing:type_name -> dubbo.mesh.v1alpha1.Routing
+	13, // 5: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.dpCert:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
+	16, // 6: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.conf:type_name -> google.protobuf.Struct
+	0,  // 7: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.mode:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.Mode
+	15, // 8: dubbo.mesh.v1alpha1.Networking.outbound:type_name -> dubbo.mesh.v1alpha1.Networking.Outbound
+	5,  // 9: dubbo.mesh.v1alpha1.Tracing.backends:type_name -> dubbo.mesh.v1alpha1.TracingBackend
+	17, // 10: dubbo.mesh.v1alpha1.TracingBackend.sampling:type_name -> google.protobuf.DoubleValue
+	16, // 11: dubbo.mesh.v1alpha1.TracingBackend.conf:type_name -> google.protobuf.Struct
+	18, // 12: dubbo.mesh.v1alpha1.ZipkinTracingBackendConfig.sharedSpanContext:type_name -> google.protobuf.BoolValue
+	8,  // 13: dubbo.mesh.v1alpha1.Logging.backends:type_name -> dubbo.mesh.v1alpha1.LoggingBackend
+	16, // 14: dubbo.mesh.v1alpha1.LoggingBackend.conf:type_name -> google.protobuf.Struct
+	2,  // 15: dubbo.mesh.v1alpha1.Mesh.Mtls.backends:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend
+	14, // 16: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.rotation:type_name -> dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
+	19, // 17: dubbo.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.requestTimeout:type_name -> google.protobuf.Duration
+	18, // 18: dubbo.mesh.v1alpha1.Networking.Outbound.passthrough:type_name -> google.protobuf.BoolValue
+	19, // [19:19] is the sub-list for method output_type
+	19, // [19:19] is the sub-list for method input_type
+	19, // [19:19] is the sub-list for extension type_name
+	19, // [19:19] is the sub-list for extension extendee
+	0,  // [0:19] is the sub-list for field type_name
 }
 
 func init() { file_api_mesh_v1alpha1_mesh_proto_init() }
@@ -1349,13 +1431,14 @@
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_api_mesh_v1alpha1_mesh_proto_rawDesc,
-			NumEnums:      0,
+			NumEnums:      1,
 			NumMessages:   15,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
 		GoTypes:           file_api_mesh_v1alpha1_mesh_proto_goTypes,
 		DependencyIndexes: file_api_mesh_v1alpha1_mesh_proto_depIdxs,
+		EnumInfos:         file_api_mesh_v1alpha1_mesh_proto_enumTypes,
 		MessageInfos:      file_api_mesh_v1alpha1_mesh_proto_msgTypes,
 	}.Build()
 	File_api_mesh_v1alpha1_mesh_proto = out.File
diff --git a/api/mesh/v1alpha1/mesh.proto b/api/mesh/v1alpha1/mesh.proto
index d884efc..160d2b7 100644
--- a/api/mesh/v1alpha1/mesh.proto
+++ b/api/mesh/v1alpha1/mesh.proto
@@ -6,6 +6,7 @@
 
 import "google/protobuf/wrappers.proto";
 import "google/protobuf/struct.proto";
+import "google/protobuf/duration.proto";
 import "api/mesh/options.proto";
 
 // Mesh defines configuration of a single mesh.
@@ -66,6 +67,8 @@
     }
     // Rotation settings
     Rotation rotation = 1;
+    // Timeout on request to CA for DP certificate generation and retrieval
+    google.protobuf.Duration requestTimeout = 2;
   }
 
   // Dataplane certificate settings
@@ -73,6 +76,21 @@
 
   // Configuration of the backend
   google.protobuf.Struct conf = 4;
+
+  enum Mode {
+    // A STRICT mode implies that the server validates the connection and
+    // accepts only encrypted TLS traffic
+    STRICT = 0;
+    // A PERMISSIVE mode implies that the outbounds encrypt traffic the same way
+    // it happens in strict mode, but inbounds accept both TLS and plaintext
+    // traffic. This allows applications residing in the mesh to accept requests
+    // from outside of the mesh.
+    PERMISSIVE = 1;
+  }
+
+  // Mode defines the behaviour of inbound listeners with regard to traffic
+  // encryption
+  Mode mode = 5;
 }
 
 // Networking defines the networking configuration of the mesh
diff --git a/api/mesh/v1alpha1/selector.pb.go b/api/mesh/v1alpha1/selector.pb.go
new file mode 100644
index 0000000..0894456
--- /dev/null
+++ b/api/mesh/v1alpha1/selector.pb.go
@@ -0,0 +1,156 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.12.4
+// source: api/mesh/v1alpha1/selector.proto
+
+package v1alpha1
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Selector defines structure for selecting tags for given dataplane
+type Selector struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Tags to match, can be used for both source and destinations
+	Match map[string]string `protobuf:"bytes,1,rep,name=match,proto3" json:"match,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *Selector) Reset() {
+	*x = Selector{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_api_mesh_v1alpha1_selector_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Selector) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Selector) ProtoMessage() {}
+
+func (x *Selector) ProtoReflect() protoreflect.Message {
+	mi := &file_api_mesh_v1alpha1_selector_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Selector.ProtoReflect.Descriptor instead.
+func (*Selector) Descriptor() ([]byte, []int) {
+	return file_api_mesh_v1alpha1_selector_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Selector) GetMatch() map[string]string {
+	if x != nil {
+		return x.Match
+	}
+	return nil
+}
+
+var File_api_mesh_v1alpha1_selector_proto protoreflect.FileDescriptor
+
+var file_api_mesh_v1alpha1_selector_proto_rawDesc = []byte{
+	0x0a, 0x20, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70,
+	0x68, 0x61, 0x31, 0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x12, 0x13, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76,
+	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x22, 0x84, 0x01, 0x0a, 0x08, 0x53, 0x65, 0x6c, 0x65,
+	0x63, 0x74, 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68,
+	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
+	0x6f, 0x72, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6d,
+	0x61, 0x74, 0x63, 0x68, 0x1a, 0x38, 0x0a, 0x0a, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74,
+	0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x36,
+	0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61,
+	0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e,
+	0x65, 0x74, 0x65, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31,
+	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_api_mesh_v1alpha1_selector_proto_rawDescOnce sync.Once
+	file_api_mesh_v1alpha1_selector_proto_rawDescData = file_api_mesh_v1alpha1_selector_proto_rawDesc
+)
+
+func file_api_mesh_v1alpha1_selector_proto_rawDescGZIP() []byte {
+	file_api_mesh_v1alpha1_selector_proto_rawDescOnce.Do(func() {
+		file_api_mesh_v1alpha1_selector_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_mesh_v1alpha1_selector_proto_rawDescData)
+	})
+	return file_api_mesh_v1alpha1_selector_proto_rawDescData
+}
+
+var file_api_mesh_v1alpha1_selector_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_api_mesh_v1alpha1_selector_proto_goTypes = []interface{}{
+	(*Selector)(nil), // 0: dubbo.mesh.v1alpha1.Selector
+	nil,              // 1: dubbo.mesh.v1alpha1.Selector.MatchEntry
+}
+var file_api_mesh_v1alpha1_selector_proto_depIdxs = []int32{
+	1, // 0: dubbo.mesh.v1alpha1.Selector.match:type_name -> dubbo.mesh.v1alpha1.Selector.MatchEntry
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_api_mesh_v1alpha1_selector_proto_init() }
+func file_api_mesh_v1alpha1_selector_proto_init() {
+	if File_api_mesh_v1alpha1_selector_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_api_mesh_v1alpha1_selector_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Selector); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_api_mesh_v1alpha1_selector_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_api_mesh_v1alpha1_selector_proto_goTypes,
+		DependencyIndexes: file_api_mesh_v1alpha1_selector_proto_depIdxs,
+		MessageInfos:      file_api_mesh_v1alpha1_selector_proto_msgTypes,
+	}.Build()
+	File_api_mesh_v1alpha1_selector_proto = out.File
+	file_api_mesh_v1alpha1_selector_proto_rawDesc = nil
+	file_api_mesh_v1alpha1_selector_proto_goTypes = nil
+	file_api_mesh_v1alpha1_selector_proto_depIdxs = nil
+}
diff --git a/api/mesh/v1alpha1/selector.proto b/api/mesh/v1alpha1/selector.proto
new file mode 100644
index 0000000..d370c14
--- /dev/null
+++ b/api/mesh/v1alpha1/selector.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package dubbo.mesh.v1alpha1;
+
+option go_package = "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1";
+
+// Selector defines structure for selecting tags for given dataplane
+message Selector {
+  // Tags to match, can be used for both source and destinations
+  map<string, string> match = 1;
+}
diff --git a/api/mesh/v1alpha1/timeout.pb.go b/api/mesh/v1alpha1/timeout.pb.go
new file mode 100644
index 0000000..09af4f1
--- /dev/null
+++ b/api/mesh/v1alpha1/timeout.pb.go
@@ -0,0 +1,570 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.12.4
+// source: api/mesh/v1alpha1/timeout.proto
+
+package v1alpha1
+
+import (
+	_ "github.com/apache/dubbo-kubernetes/api/mesh"
+	duration "github.com/golang/protobuf/ptypes/duration"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Timeout struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// List of selectors to match dataplanes that are sources of traffic.
+	Sources []*Selector `protobuf:"bytes,1,rep,name=sources,proto3" json:"sources,omitempty"`
+	// List of selectors to match services that are destinations of traffic.
+	Destinations []*Selector   `protobuf:"bytes,2,rep,name=destinations,proto3" json:"destinations,omitempty"`
+	Conf         *Timeout_Conf `protobuf:"bytes,3,opt,name=conf,proto3" json:"conf,omitempty"`
+}
+
+func (x *Timeout) Reset() {
+	*x = Timeout{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Timeout) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout) ProtoMessage() {}
+
+func (x *Timeout) ProtoReflect() protoreflect.Message {
+	mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout.ProtoReflect.Descriptor instead.
+func (*Timeout) Descriptor() ([]byte, []int) {
+	return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Timeout) GetSources() []*Selector {
+	if x != nil {
+		return x.Sources
+	}
+	return nil
+}
+
+func (x *Timeout) GetDestinations() []*Selector {
+	if x != nil {
+		return x.Destinations
+	}
+	return nil
+}
+
+func (x *Timeout) GetConf() *Timeout_Conf {
+	if x != nil {
+		return x.Conf
+	}
+	return nil
+}
+
+type Timeout_Conf struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// ConnectTimeout defines time to establish connection
+	ConnectTimeout *duration.Duration `protobuf:"bytes,1,opt,name=connect_timeout,json=connectTimeout,proto3" json:"connect_timeout,omitempty"`
+	Tcp            *Timeout_Conf_Tcp  `protobuf:"bytes,2,opt,name=tcp,proto3" json:"tcp,omitempty"`
+	Http           *Timeout_Conf_Http `protobuf:"bytes,3,opt,name=http,proto3" json:"http,omitempty"`
+	// Deprecated: set parameters through Http section
+	Grpc *Timeout_Conf_Grpc `protobuf:"bytes,4,opt,name=grpc,proto3" json:"grpc,omitempty"`
+}
+
+func (x *Timeout_Conf) Reset() {
+	*x = Timeout_Conf{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Timeout_Conf) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout_Conf) ProtoMessage() {}
+
+func (x *Timeout_Conf) ProtoReflect() protoreflect.Message {
+	mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout_Conf.ProtoReflect.Descriptor instead.
+func (*Timeout_Conf) Descriptor() ([]byte, []int) {
+	return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *Timeout_Conf) GetConnectTimeout() *duration.Duration {
+	if x != nil {
+		return x.ConnectTimeout
+	}
+	return nil
+}
+
+func (x *Timeout_Conf) GetTcp() *Timeout_Conf_Tcp {
+	if x != nil {
+		return x.Tcp
+	}
+	return nil
+}
+
+func (x *Timeout_Conf) GetHttp() *Timeout_Conf_Http {
+	if x != nil {
+		return x.Http
+	}
+	return nil
+}
+
+func (x *Timeout_Conf) GetGrpc() *Timeout_Conf_Grpc {
+	if x != nil {
+		return x.Grpc
+	}
+	return nil
+}
+
+// Tcp defines timeouts that are applied when the protocol is TCP
+type Timeout_Conf_Tcp struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// IdleTimeout is defined as the period in which there are no bytes sent
+	// or received on either the upstream or downstream connection
+	IdleTimeout *duration.Duration `protobuf:"bytes,1,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"`
+}
+
+func (x *Timeout_Conf_Tcp) Reset() {
+	*x = Timeout_Conf_Tcp{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Timeout_Conf_Tcp) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout_Conf_Tcp) ProtoMessage() {}
+
+func (x *Timeout_Conf_Tcp) ProtoReflect() protoreflect.Message {
+	mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout_Conf_Tcp.ProtoReflect.Descriptor instead.
+func (*Timeout_Conf_Tcp) Descriptor() ([]byte, []int) {
+	return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0, 0, 0}
+}
+
+func (x *Timeout_Conf_Tcp) GetIdleTimeout() *duration.Duration {
+	if x != nil {
+		return x.IdleTimeout
+	}
+	return nil
+}
+
+// Http defines timeouts that are applied when the protocol is HTTP
+type Timeout_Conf_Http struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// RequestTimeout is a span between the point at which the entire
+	// downstream request (i.e. end-of-stream) has been processed and when the
+	// upstream response has been completely processed
+	RequestTimeout *duration.Duration `protobuf:"bytes,1,opt,name=request_timeout,json=requestTimeout,proto3" json:"request_timeout,omitempty"`
+	// IdleTimeout is the time at which a downstream or upstream connection
+	// will be terminated if there are no active streams
+	IdleTimeout *duration.Duration `protobuf:"bytes,2,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"`
+	// StreamIdleTimeout is the amount of time that the connection manager
+	// will allow a stream to exist with no upstream or downstream activity
+	StreamIdleTimeout *duration.Duration `protobuf:"bytes,3,opt,name=stream_idle_timeout,json=streamIdleTimeout,proto3" json:"stream_idle_timeout,omitempty"`
+	// MaxStreamDuration is the maximum time that a stream’s lifetime will
+	// span
+	MaxStreamDuration *duration.Duration `protobuf:"bytes,4,opt,name=max_stream_duration,json=maxStreamDuration,proto3" json:"max_stream_duration,omitempty"`
+}
+
+func (x *Timeout_Conf_Http) Reset() {
+	*x = Timeout_Conf_Http{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Timeout_Conf_Http) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout_Conf_Http) ProtoMessage() {}
+
+func (x *Timeout_Conf_Http) ProtoReflect() protoreflect.Message {
+	mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout_Conf_Http.ProtoReflect.Descriptor instead.
+func (*Timeout_Conf_Http) Descriptor() ([]byte, []int) {
+	return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0, 0, 1}
+}
+
+func (x *Timeout_Conf_Http) GetRequestTimeout() *duration.Duration {
+	if x != nil {
+		return x.RequestTimeout
+	}
+	return nil
+}
+
+func (x *Timeout_Conf_Http) GetIdleTimeout() *duration.Duration {
+	if x != nil {
+		return x.IdleTimeout
+	}
+	return nil
+}
+
+func (x *Timeout_Conf_Http) GetStreamIdleTimeout() *duration.Duration {
+	if x != nil {
+		return x.StreamIdleTimeout
+	}
+	return nil
+}
+
+func (x *Timeout_Conf_Http) GetMaxStreamDuration() *duration.Duration {
+	if x != nil {
+		return x.MaxStreamDuration
+	}
+	return nil
+}
+
+// Grpc defines timeouts that are applied when the protocol is GRPC
+type Timeout_Conf_Grpc struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// StreamIdleTimeout is the amount of time that the connection manager
+	// will allow a stream to exist with no upstream or downstream activity
+	// Deprecated: use Http.StreamIdleTimeout instead
+	StreamIdleTimeout *duration.Duration `protobuf:"bytes,1,opt,name=stream_idle_timeout,json=streamIdleTimeout,proto3" json:"stream_idle_timeout,omitempty"`
+	// MaxStreamDuration is the maximum time that a stream’s lifetime will
+	// span
+	// Deprecated: use Http.MaxStreamDuration instead
+	MaxStreamDuration *duration.Duration `protobuf:"bytes,2,opt,name=max_stream_duration,json=maxStreamDuration,proto3" json:"max_stream_duration,omitempty"`
+}
+
+func (x *Timeout_Conf_Grpc) Reset() {
+	*x = Timeout_Conf_Grpc{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Timeout_Conf_Grpc) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Timeout_Conf_Grpc) ProtoMessage() {}
+
+func (x *Timeout_Conf_Grpc) ProtoReflect() protoreflect.Message {
+	mi := &file_api_mesh_v1alpha1_timeout_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Timeout_Conf_Grpc.ProtoReflect.Descriptor instead.
+func (*Timeout_Conf_Grpc) Descriptor() ([]byte, []int) {
+	return file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP(), []int{0, 0, 2}
+}
+
+func (x *Timeout_Conf_Grpc) GetStreamIdleTimeout() *duration.Duration {
+	if x != nil {
+		return x.StreamIdleTimeout
+	}
+	return nil
+}
+
+func (x *Timeout_Conf_Grpc) GetMaxStreamDuration() *duration.Duration {
+	if x != nil {
+		return x.MaxStreamDuration
+	}
+	return nil
+}
+
+var File_api_mesh_v1alpha1_timeout_proto protoreflect.FileDescriptor
+
+var file_api_mesh_v1alpha1_timeout_proto_rawDesc = []byte{
+	0x0a, 0x1f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70,
+	0x68, 0x61, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x12, 0x13, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31,
+	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x16, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68,
+	0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20,
+	0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+	0x31, 0x2f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x22, 0x96, 0x08, 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x37, 0x0a, 0x07,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
+	0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
+	0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x73, 0x6f,
+	0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0c, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x75,
+	0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+	0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x0c, 0x64, 0x65, 0x73, 0x74,
+	0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x04, 0x63, 0x6f, 0x6e, 0x66,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d,
+	0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x6d,
+	0x65, 0x6f, 0x75, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x1a,
+	0x80, 0x06, 0x0a, 0x04, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x42, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e,
+	0x65, 0x63, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x63, 0x6f,
+	0x6e, 0x6e, 0x65, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x37, 0x0a, 0x03,
+	0x74, 0x63, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x64, 0x75, 0x62, 0x62,
+	0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x2e, 0x54, 0x63, 0x70,
+	0x52, 0x03, 0x74, 0x63, 0x70, 0x12, 0x3a, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68,
+	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75,
+	0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x52, 0x04, 0x68, 0x74, 0x74,
+	0x70, 0x12, 0x3a, 0x0a, 0x04, 0x67, 0x72, 0x70, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x26, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x2e, 0x43, 0x6f,
+	0x6e, 0x66, 0x2e, 0x47, 0x72, 0x70, 0x63, 0x52, 0x04, 0x67, 0x72, 0x70, 0x63, 0x1a, 0x43, 0x0a,
+	0x03, 0x54, 0x63, 0x70, 0x12, 0x3c, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d,
+	0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f,
+	0x75, 0x74, 0x1a, 0x9e, 0x02, 0x0a, 0x04, 0x48, 0x74, 0x74, 0x70, 0x12, 0x42, 0x0a, 0x0f, 0x72,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+	0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12,
+	0x3c, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x52, 0x0b, 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x49, 0x0a,
+	0x13, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d,
+	0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x6c,
+	0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x49, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f,
+	0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x52, 0x11, 0x6d, 0x61, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x75, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x1a, 0x9c, 0x01, 0x0a, 0x04, 0x47, 0x72, 0x70, 0x63, 0x12, 0x49, 0x0a, 0x13,
+	0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+	0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61,
+	0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x6c, 0x65,
+	0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x49, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x73,
+	0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+	0x11, 0x6d, 0x61, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x3a, 0x55, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x11, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65,
+	0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0xaa, 0x8c, 0x89, 0xa6, 0x01,
+	0x09, 0x12, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x06,
+	0x22, 0x04, 0x6d, 0x65, 0x73, 0x68, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x04, 0x52, 0x02, 0x10, 0x01,
+	0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x0b, 0x3a, 0x09, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
+	0x74, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x02, 0x68, 0x01, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74,
+	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64,
+	0x75, 0x62, 0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f,
+	0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+	0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_api_mesh_v1alpha1_timeout_proto_rawDescOnce sync.Once
+	file_api_mesh_v1alpha1_timeout_proto_rawDescData = file_api_mesh_v1alpha1_timeout_proto_rawDesc
+)
+
+func file_api_mesh_v1alpha1_timeout_proto_rawDescGZIP() []byte {
+	file_api_mesh_v1alpha1_timeout_proto_rawDescOnce.Do(func() {
+		file_api_mesh_v1alpha1_timeout_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_mesh_v1alpha1_timeout_proto_rawDescData)
+	})
+	return file_api_mesh_v1alpha1_timeout_proto_rawDescData
+}
+
+var file_api_mesh_v1alpha1_timeout_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_api_mesh_v1alpha1_timeout_proto_goTypes = []interface{}{
+	(*Timeout)(nil),           // 0: dubbo.mesh.v1alpha1.Timeout
+	(*Timeout_Conf)(nil),      // 1: dubbo.mesh.v1alpha1.Timeout.Conf
+	(*Timeout_Conf_Tcp)(nil),  // 2: dubbo.mesh.v1alpha1.Timeout.Conf.Tcp
+	(*Timeout_Conf_Http)(nil), // 3: dubbo.mesh.v1alpha1.Timeout.Conf.Http
+	(*Timeout_Conf_Grpc)(nil), // 4: dubbo.mesh.v1alpha1.Timeout.Conf.Grpc
+	(*Selector)(nil),          // 5: dubbo.mesh.v1alpha1.Selector
+	(*duration.Duration)(nil), // 6: google.protobuf.Duration
+}
+var file_api_mesh_v1alpha1_timeout_proto_depIdxs = []int32{
+	5,  // 0: dubbo.mesh.v1alpha1.Timeout.sources:type_name -> dubbo.mesh.v1alpha1.Selector
+	5,  // 1: dubbo.mesh.v1alpha1.Timeout.destinations:type_name -> dubbo.mesh.v1alpha1.Selector
+	1,  // 2: dubbo.mesh.v1alpha1.Timeout.conf:type_name -> dubbo.mesh.v1alpha1.Timeout.Conf
+	6,  // 3: dubbo.mesh.v1alpha1.Timeout.Conf.connect_timeout:type_name -> google.protobuf.Duration
+	2,  // 4: dubbo.mesh.v1alpha1.Timeout.Conf.tcp:type_name -> dubbo.mesh.v1alpha1.Timeout.Conf.Tcp
+	3,  // 5: dubbo.mesh.v1alpha1.Timeout.Conf.http:type_name -> dubbo.mesh.v1alpha1.Timeout.Conf.Http
+	4,  // 6: dubbo.mesh.v1alpha1.Timeout.Conf.grpc:type_name -> dubbo.mesh.v1alpha1.Timeout.Conf.Grpc
+	6,  // 7: dubbo.mesh.v1alpha1.Timeout.Conf.Tcp.idle_timeout:type_name -> google.protobuf.Duration
+	6,  // 8: dubbo.mesh.v1alpha1.Timeout.Conf.Http.request_timeout:type_name -> google.protobuf.Duration
+	6,  // 9: dubbo.mesh.v1alpha1.Timeout.Conf.Http.idle_timeout:type_name -> google.protobuf.Duration
+	6,  // 10: dubbo.mesh.v1alpha1.Timeout.Conf.Http.stream_idle_timeout:type_name -> google.protobuf.Duration
+	6,  // 11: dubbo.mesh.v1alpha1.Timeout.Conf.Http.max_stream_duration:type_name -> google.protobuf.Duration
+	6,  // 12: dubbo.mesh.v1alpha1.Timeout.Conf.Grpc.stream_idle_timeout:type_name -> google.protobuf.Duration
+	6,  // 13: dubbo.mesh.v1alpha1.Timeout.Conf.Grpc.max_stream_duration:type_name -> google.protobuf.Duration
+	14, // [14:14] is the sub-list for method output_type
+	14, // [14:14] is the sub-list for method input_type
+	14, // [14:14] is the sub-list for extension type_name
+	14, // [14:14] is the sub-list for extension extendee
+	0,  // [0:14] is the sub-list for field type_name
+}
+
+func init() { file_api_mesh_v1alpha1_timeout_proto_init() }
+func file_api_mesh_v1alpha1_timeout_proto_init() {
+	if File_api_mesh_v1alpha1_timeout_proto != nil {
+		return
+	}
+	file_api_mesh_v1alpha1_selector_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_api_mesh_v1alpha1_timeout_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Timeout); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_api_mesh_v1alpha1_timeout_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Timeout_Conf); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_api_mesh_v1alpha1_timeout_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Timeout_Conf_Tcp); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_api_mesh_v1alpha1_timeout_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Timeout_Conf_Http); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_api_mesh_v1alpha1_timeout_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Timeout_Conf_Grpc); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_api_mesh_v1alpha1_timeout_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   5,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_api_mesh_v1alpha1_timeout_proto_goTypes,
+		DependencyIndexes: file_api_mesh_v1alpha1_timeout_proto_depIdxs,
+		MessageInfos:      file_api_mesh_v1alpha1_timeout_proto_msgTypes,
+	}.Build()
+	File_api_mesh_v1alpha1_timeout_proto = out.File
+	file_api_mesh_v1alpha1_timeout_proto_rawDesc = nil
+	file_api_mesh_v1alpha1_timeout_proto_goTypes = nil
+	file_api_mesh_v1alpha1_timeout_proto_depIdxs = nil
+}
diff --git a/api/mesh/v1alpha1/timeout.proto b/api/mesh/v1alpha1/timeout.proto
new file mode 100644
index 0000000..82ce555
--- /dev/null
+++ b/api/mesh/v1alpha1/timeout.proto
@@ -0,0 +1,71 @@
+syntax = "proto3";
+
+package dubbo.mesh.v1alpha1;
+
+option go_package = "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1";
+
+import "api/mesh/options.proto";
+import "api/mesh/v1alpha1/selector.proto";
+import "google/protobuf/duration.proto";
+
+message Timeout {
+
+  option (dubbo.mesh.resource).name = "TimeoutResource";
+  option (dubbo.mesh.resource).type = "Timeout";
+  option (dubbo.mesh.resource).package = "mesh";
+  option (dubbo.mesh.resource).dds.send_to_zone = true;
+  option (dubbo.mesh.resource).ws.name = "timeout";
+  option (dubbo.mesh.resource).allow_to_inspect = true;
+
+  // List of selectors to match dataplanes that are sources of traffic.
+  repeated Selector sources = 1;
+
+  // List of selectors to match services that are destinations of traffic.
+  repeated Selector destinations = 2;
+
+  message Conf {
+    // ConnectTimeout defines time to establish connection
+    google.protobuf.Duration connect_timeout = 1;
+
+    // Tcp defines timeouts that are applied when the protocol is TCP
+    message Tcp {
+      // IdleTimeout is defined as the period in which there are no bytes sent
+      // or received on either the upstream or downstream connection
+      google.protobuf.Duration idle_timeout = 1;
+    }
+    Tcp tcp = 2;
+
+    // Http defines timeouts that are applied when the protocol is HTTP
+    message Http {
+      // RequestTimeout is a span between the point at which the entire
+      // downstream request (i.e. end-of-stream) has been processed and when the
+      // upstream response has been completely processed
+      google.protobuf.Duration request_timeout = 1;
+      // IdleTimeout is the time at which a downstream or upstream connection
+      // will be terminated if there are no active streams
+      google.protobuf.Duration idle_timeout = 2;
+      // StreamIdleTimeout is the amount of time that the connection manager
+      // will allow a stream to exist with no upstream or downstream activity
+      google.protobuf.Duration stream_idle_timeout = 3;
+      // MaxStreamDuration is the maximum time that a stream’s lifetime will
+      // span
+      google.protobuf.Duration max_stream_duration = 4;
+    }
+    Http http = 3;
+
+    // Grpc defines timeouts that are applied when the protocol is GRPC
+    message Grpc {
+      // StreamIdleTimeout is the amount of time that the connection manager
+      // will allow a stream to exist with no upstream or downstream activity
+      // Deprecated: use Http.StreamIdleTimeout instead
+      google.protobuf.Duration stream_idle_timeout = 1;
+      // MaxStreamDuration is the maximum time that a stream’s lifetime will
+      // span
+      // Deprecated: use Http.MaxStreamDuration instead
+      google.protobuf.Duration max_stream_duration = 2;
+    }
+    // Deprecated: set parameters through Http section
+    Grpc grpc = 4;
+  }
+  Conf conf = 3;
+}
diff --git a/api/mesh/v1alpha1/timeout_helpers.go b/api/mesh/v1alpha1/timeout_helpers.go
new file mode 100644
index 0000000..d9b377e
--- /dev/null
+++ b/api/mesh/v1alpha1/timeout_helpers.go
@@ -0,0 +1,16 @@
+package v1alpha1
+
+import (
+	"time"
+)
+
+func (x *Timeout_Conf) GetConnectTimeoutOrDefault(defaultConnectTimeout time.Duration) time.Duration {
+	if x == nil {
+		return defaultConnectTimeout
+	}
+	connectTimeout := x.GetConnectTimeout()
+	if connectTimeout == nil {
+		return defaultConnectTimeout
+	}
+	return connectTimeout.AsDuration()
+}
diff --git a/api/mesh/v1alpha1/zoneegress_helpers.go b/api/mesh/v1alpha1/zoneegress_helpers.go
new file mode 100644
index 0000000..2adb036
--- /dev/null
+++ b/api/mesh/v1alpha1/zoneegress_helpers.go
@@ -0,0 +1,3 @@
+package v1alpha1
+
+const ZoneEgressServiceName = "zone-egress"
diff --git a/api/system/v1alpha1/datasource.pb.go b/api/system/v1alpha1/datasource.pb.go
index dbd31c5..6e04824 100644
--- a/api/system/v1alpha1/datasource.pb.go
+++ b/api/system/v1alpha1/datasource.pb.go
@@ -1,28 +1,20 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.1
-// 	protoc        v3.20.0
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.12.4
 // source: api/system/v1alpha1/datasource.proto
 
 package v1alpha1
 
 import (
+	_ "github.com/apache/dubbo-kubernetes/api/mesh"
+	wrappers "github.com/golang/protobuf/ptypes/wrappers"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
 	sync "sync"
 )
 
-import (
-	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-
-	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-
-	wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
-)
-
-import (
-	_ "github.com/apache/dubbo-kubernetes/api/mesh"
-)
-
 const (
 	// Verify that this generated code is sufficiently up-to-date.
 	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
@@ -37,7 +29,6 @@
 	unknownFields protoimpl.UnknownFields
 
 	// Types that are assignable to Type:
-	//
 	//	*DataSource_Secret
 	//	*DataSource_File
 	//	*DataSource_Inline
@@ -98,7 +89,7 @@
 	return ""
 }
 
-func (x *DataSource) GetInline() *wrapperspb.BytesValue {
+func (x *DataSource) GetInline() *wrappers.BytesValue {
 	if x, ok := x.GetType().(*DataSource_Inline); ok {
 		return x.Inline
 	}
@@ -129,7 +120,7 @@
 
 type DataSource_Inline struct {
 	// Data source is inline bytes.
-	Inline *wrapperspb.BytesValue `protobuf:"bytes,3,opt,name=inline,proto3,oneof"`
+	Inline *wrappers.BytesValue `protobuf:"bytes,3,opt,name=inline,proto3,oneof"`
 }
 
 type DataSource_InlineString struct {
@@ -192,8 +183,8 @@
 
 var file_api_system_v1alpha1_datasource_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
 var file_api_system_v1alpha1_datasource_proto_goTypes = []interface{}{
-	(*DataSource)(nil),            // 0: dubbo.system.v1alpha1.DataSource
-	(*wrapperspb.BytesValue)(nil), // 1: google.protobuf.BytesValue
+	(*DataSource)(nil),          // 0: dubbo.system.v1alpha1.DataSource
+	(*wrappers.BytesValue)(nil), // 1: google.protobuf.BytesValue
 }
 var file_api_system_v1alpha1_datasource_proto_depIdxs = []int32{
 	1, // 0: dubbo.system.v1alpha1.DataSource.inline:type_name -> google.protobuf.BytesValue
diff --git a/app/dubbo-ui/fs.go b/app/dubbo-ui/fs.go
index a53af4a..c80526f 100644
--- a/app/dubbo-ui/fs.go
+++ b/app/dubbo-ui/fs.go
@@ -28,10 +28,28 @@
 //go:embed dist/*
 var Data embed.FS
 
+type tryOrDefault struct {
+	fileSystem   fs.FS
+	fallbackFile string
+}
+
+func (t tryOrDefault) Open(name string) (fs.File, error) {
+	f, err := t.fileSystem.Open(name)
+	if err == nil {
+		return f, nil
+	}
+	return t.fileSystem.Open(t.fallbackFile)
+}
+
 var FS = func() fs.FS {
 	fsys, err := fs.Sub(Data, "dist/admin")
 	if err != nil {
 		panic(err)
 	}
+	// Adapt Vue's HTML5 history mode
+	fsys = tryOrDefault{
+		fileSystem:   fsys,
+		fallbackFile: "index.html",
+	}
 	return fsys
 }
diff --git a/dubboctl/cmd/manifest.go b/dubboctl/cmd/manifest.go
index fa4acec..c552300 100644
--- a/dubboctl/cmd/manifest.go
+++ b/dubboctl/cmd/manifest.go
@@ -16,18 +16,19 @@
 package cmd
 
 import (
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/manifest"
 	"github.com/spf13/cobra"
 )
 
-func addManifest(rootCmd *cobra.Command) {
+func AddManifest(rootCmd *cobra.Command) {
 	manifestCmd := &cobra.Command{
 		Use:   "manifest",
 		Short: "Commands related to manifest",
 		Long:  "Commands help user to generate manifest and install manifest",
 	}
-	ConfigManifestGenerateCmd(manifestCmd)
-	ConfigManifestInstallCmd(manifestCmd)
-	ConfigManifestUninstallCmd(manifestCmd)
-	ConfigManifestDiffCmd(manifestCmd)
+	manifest.ConfigManifestGenerateCmd(manifestCmd)
+	manifest.ConfigManifestInstallCmd(manifestCmd)
+	manifest.ConfigManifestUninstallCmd(manifestCmd)
+	manifest.ConfigManifestDiffCmd(manifestCmd)
 	rootCmd.AddCommand(manifestCmd)
 }
diff --git a/dubboctl/cmd/manifest_test.go b/dubboctl/cmd/manifest_test.go
index 4435414..29fe869 100644
--- a/dubboctl/cmd/manifest_test.go
+++ b/dubboctl/cmd/manifest_test.go
@@ -18,13 +18,16 @@
 import (
 	"bytes"
 	"os"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 	"strings"
 	"testing"
 )
 
-import (
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-)
+//var (
+//	// TestInstallFlag and TestCli are uses for black box testing
+//	TestInstallFlag bool
+//	TestCli         client.Client
+//)
 
 func TestManifestGenerate(t *testing.T) {
 	tests := []struct {
@@ -74,7 +77,7 @@
 	}
 	for _, test := range tests {
 		t.Run(test.desc, func(t *testing.T) {
-			testExecute(t, test.cmd, test.wantErr)
+			testManifestExecute(t, test.cmd, test.wantErr)
 			// remove temporary dir
 			if test.temp != "" {
 				os.RemoveAll(test.temp)
@@ -89,10 +92,10 @@
 		cmd     string
 		wantErr bool
 	}{
-		{
-			desc: "without any flag",
-			cmd:  "manifest install",
-		},
+		//{
+		//	desc: "without any flag",
+		//	cmd:  "manifest install",
+		//},
 	}
 	// For now, we do not use envTest to do black box testing
 	TestInstallFlag = true
@@ -100,7 +103,7 @@
 
 	for _, test := range tests {
 		t.Run(test.desc, func(t *testing.T) {
-			testExecute(t, test.cmd, test.wantErr)
+			testManifestExecute(t, test.cmd, test.wantErr)
 		})
 	}
 }
@@ -113,11 +116,11 @@
 		cmd     string
 		wantErr bool
 	}{
-		{
-			desc:   "without any flag",
-			before: "manifest install",
-			cmd:    "manifest uninstall",
-		},
+		//{
+		//	desc:   "without any flag",
+		//	before: "manifest install",
+		//	cmd:    "manifest uninstall",
+		//},
 	}
 	// For now, we do not use envTest to do black box testing
 	TestInstallFlag = true
@@ -126,8 +129,8 @@
 	for _, test := range tests {
 		t.Run(test.desc, func(t *testing.T) {
 			// prepare existing resources
-			testExecute(t, test.before, false)
-			testExecute(t, test.cmd, test.wantErr)
+			testManifestExecute(t, test.before, false)
+			testManifestExecute(t, test.cmd, test.wantErr)
 		})
 	}
 }
@@ -156,9 +159,9 @@
 	for _, test := range tests {
 		t.Run(test.desc, func(t *testing.T) {
 			for _, before := range test.befores {
-				testExecute(t, before, false)
+				testManifestExecute(t, before, false)
 			}
-			testExecute(t, test.cmd, test.wantErr)
+			testManifestExecute(t, test.cmd, test.wantErr)
 			for _, temp := range test.temps {
 				if temp != "" {
 					os.RemoveAll(temp)
@@ -168,16 +171,16 @@
 	}
 }
 
-func testExecute(t *testing.T, cmd string, wantErr bool) string {
+func testManifestExecute(t *testing.T, cmds string, wantErr bool) string {
 	var out bytes.Buffer
-	args := strings.Split(cmd, " ")
-	rootCmd := getRootCmd(args)
+	args := strings.Split(cmds, " ")
+	rootCmd := GetRootCmd(args)
 	rootCmd.SetOut(&out)
 	if err := rootCmd.Execute(); err != nil {
 		if wantErr {
 			return ""
 		}
-		t.Errorf("execute %s failed, err: %s", cmd, err)
+		t.Errorf("execute %s failed, err: %s", cmds, err)
 		return ""
 	}
 	if wantErr {
diff --git a/dubboctl/cmd/profile.go b/dubboctl/cmd/profile.go
index a59cf2e..3f4d2ce 100644
--- a/dubboctl/cmd/profile.go
+++ b/dubboctl/cmd/profile.go
@@ -16,6 +16,7 @@
 package cmd
 
 import (
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/profile"
 	"github.com/spf13/cobra"
 )
 
@@ -25,8 +26,8 @@
 		Short: "Commands related to profiles",
 		Long:  "Commands help user to list and describe profiles",
 	}
-	ConfigProfileListCmd(profileCmd)
-	ConfigProfileDiffCmd(profileCmd)
+	profile.ConfigProfileListCmd(profileCmd)
+	profile.ConfigProfileDiffCmd(profileCmd)
 
 	rootCmd.AddCommand(profileCmd)
 }
diff --git a/dubboctl/cmd/profile_test.go b/dubboctl/cmd/profile_test.go
index 3c0dd4d..00057d1 100644
--- a/dubboctl/cmd/profile_test.go
+++ b/dubboctl/cmd/profile_test.go
@@ -16,6 +16,8 @@
 package cmd
 
 import (
+	"bytes"
+	"strings"
 	"testing"
 )
 
@@ -67,7 +69,7 @@
 
 	for _, test := range tests {
 		t.Run(test.desc, func(t *testing.T) {
-			res := testExecute(t, test.cmd, test.wantErr)
+			res := testProfileExecute(t, test.cmd, test.wantErr)
 			if test.want != "" && test.want != res {
 				t.Errorf("want:\n%s\nbutgot:\n%s\n", test.want, res)
 				return
@@ -120,7 +122,7 @@
 
 	for _, test := range tests {
 		t.Run(test.desc, func(t *testing.T) {
-			res := testExecute(t, test.cmd, test.wantErr)
+			res := testProfileExecute(t, test.cmd, test.wantErr)
 			if test.want != "" && test.want != res {
 				t.Errorf("want:\n%s\nbutgot:\n%s\n", test.want, res)
 				return
@@ -128,3 +130,22 @@
 		})
 	}
 }
+
+func testProfileExecute(t *testing.T, cmds string, wantErr bool) string {
+	var out bytes.Buffer
+	args := strings.Split(cmds, " ")
+	rootCmd := GetRootCmd(args)
+	rootCmd.SetOut(&out)
+	if err := rootCmd.Execute(); err != nil {
+		if wantErr {
+			return ""
+		}
+		t.Errorf("execute %s failed, err: %s", cmds, err)
+		return ""
+	}
+	if wantErr {
+		t.Errorf("want err but got no err")
+		return ""
+	}
+	return out.String()
+}
diff --git a/dubboctl/cmd/repository_test.go b/dubboctl/cmd/repository_test.go
deleted file mode 100644
index 169af73..0000000
--- a/dubboctl/cmd/repository_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You 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 cmd
-
-import (
-	"testing"
-)
-
-// TestRepository_List ensures that the 'list' subcommand shows the client's
-// set of repositories by name for builtin repositories, by explicitly
-// setting the repositories' path to a new path which includes no others.
-func TestRepository_List(t *testing.T) {
-	_ = fromTempDirectory(t)
-
-	cmd := NewRepositoryListCmd(NewClient)
-	cmd.SetArgs([]string{}) // Do not use test command args
-
-	// Execute the command, capturing the output sent to stdout
-	stdout := piped(t)
-	if err := cmd.Execute(); err != nil {
-		t.Fatal(err)
-	}
-
-	// Assert the output matches expect (whitespace trimmed)
-	expect := "default"
-	output := stdout()
-	if output != expect {
-		t.Fatalf("expected:\n'%v'\ngot:\n'%v'\n", expect, output)
-	}
-}
diff --git a/dubboctl/cmd/root.go b/dubboctl/cmd/root.go
index 726a4d8..751c302 100644
--- a/dubboctl/cmd/root.go
+++ b/dubboctl/cmd/root.go
@@ -16,10 +16,11 @@
 package cmd
 
 import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/dashboard"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/deploy"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/generate"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/proxy"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/registry"
 )
 
 import (
@@ -29,21 +30,18 @@
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/pkg/core"
 	cmd2 "github.com/apache/dubbo-kubernetes/pkg/core/cmd"
 )
 
 type RootCommandConfig struct {
 	Name      string
-	NewClient ClientFactory
+	NewClient deploy.ClientFactory
 }
 
-var controlPlaneLog = core.Log.WithName("dubboctl")
-
 // Execute adds all child commands to the root command and sets flags appropriately.
 // This is called by main.main(). It only needs to happen once to the rootCmd.
 func Execute(args []string) {
-	rootCmd := getRootCmd(args)
+	rootCmd := GetRootCmd(args)
 	// when flag error occurs, print usage string.
 	// but if an error occurs when executing command, usage string will not be printed.
 	rootCmd.SetFlagErrorFunc(func(command *cobra.Command, err error) error {
@@ -55,7 +53,7 @@
 	cobra.CheckErr(rootCmd.Execute())
 }
 
-func getRootCmd(args []string) *cobra.Command {
+func GetRootCmd(args []string) *cobra.Command {
 	// rootCmd represents the base command when called without any subcommands
 	rootCmd := &cobra.Command{
 		Use:           "dubboctl",
@@ -76,7 +74,7 @@
 	viper.SetEnvPrefix("dubbo") // ensure that all have the prefix
 	newClient := cfg.NewClient
 	if newClient == nil {
-		newClient = NewClient
+		newClient = deploy.NewClient
 	}
 
 	addSubCommands(rootCmd, newClient)
@@ -84,132 +82,15 @@
 	return rootCmd
 }
 
-func addSubCommands(rootCmd *cobra.Command, newClient ClientFactory) {
-	addBuild(rootCmd, newClient)
-	addCreate(rootCmd, newClient)
-	addRepository(rootCmd, newClient)
-	addDeploy(rootCmd, newClient)
-	addManifest(rootCmd)
-	addGenerate(rootCmd)
+func addSubCommands(rootCmd *cobra.Command, newClient deploy.ClientFactory) {
+	deploy.AddBuild(rootCmd, newClient)
+	deploy.AddCreate(rootCmd, newClient)
+	deploy.AddRepository(rootCmd, newClient)
+	deploy.AddDeploy(rootCmd, newClient)
+	AddManifest(rootCmd)
+	generate.AddGenerate(rootCmd)
 	addProfile(rootCmd)
-	addDashboard(rootCmd)
-	addRegistryCmd(rootCmd)
-	addProxy(cmd2.DefaultRunCmdOpts, rootCmd)
-}
-
-// bindFunc which conforms to the cobra PreRunE method signature
-type bindFunc func(*cobra.Command, []string) error
-
-// bindEnv returns a bindFunc that binds env vars to the named flags.
-func bindEnv(flags ...string) bindFunc {
-	return func(cmd *cobra.Command, args []string) (err error) {
-		for _, flag := range flags {
-			if err = viper.BindPFlag(flag, cmd.Flags().Lookup(flag)); err != nil {
-				return
-			}
-		}
-		viper.AutomaticEnv()        // read in environment variables for DUBBO_<flag>
-		viper.SetEnvPrefix("dubbo") // ensure that all have the prefix
-		return
-	}
-}
-
-// addConfirmFlag ensures common text/wording when the --path flag is used
-func addConfirmFlag(cmd *cobra.Command, dflt bool) {
-	cmd.Flags().BoolP("confirm", "c", dflt, "Prompt to confirm options interactively ($DUBBO_CONFIRM)")
-}
-
-// addPathFlag ensures common text/wording when the --path flag is used
-func addPathFlag(cmd *cobra.Command) {
-	cmd.Flags().StringP("path", "p", "", "Path to the application.  Default is current directory ($DUBBO_PATH)")
-}
-
-// surveySelectDefault returns 'value' if defined and exists in 'options'.
-// Otherwise, options[0] is returned if it exists.  Empty string otherwise.
-//
-// Usage Example:
-//
-//	languages := []string{ "go", "node", "rust" },
-//	survey.Select{
-//	  Options: options,
-//	  Default: surveySelectDefaut(cfg.Language, languages),
-//	}
-//
-// Summary:
-//
-// This protects against an incorrectly initialized survey.Select when the user
-// has provided a nonexistant option (validation is handled elsewhere) or
-// when a value is required but there exists no defaults (no default value on
-// the associated flag).
-//
-// Explanation:
-//
-// The above example chooses the default for the Survey (--confirm) question
-// in a way that works with user-provided flag and environment variable values.
-//
-//	`cfg.Language` is the current value set in the config struct, which is
-//	   populated from (in ascending order of precedence):
-//	   static flag default, associated environment variable, or command flag.
-//	`languages` are the options which are being used by the survey select.
-//
-// This cascade allows for the Survey questions to be properly pre-initialzed
-// with their associated environment variables or flags.  For example,
-// A user whose default language is set to 'node' using the global environment
-// variable FUNC_LANGUAGE will have that option pre-selected when running
-// `dubbo create -c`.
-//
-// The 'survey' package expects the value of the Default member to exist
-// in the 'Options' member.  This is not possible when user-provided data is
-// allowed for the default, hence this logic is necessary.
-//
-// For example, when the user is using prompts (--confirm) to select from a set
-// of options, but the associated flag either has an unrecognized value, or no
-// value at all, without this logic the resulting select prompt would be
-// initialized with this as the default value, and the act of what appears to
-// be choose the first option displayed does not overwrite the invalid default.
-// It could perhaps be argued this is a shortcoming in the survey package, but
-// it is also clearly an error to provide invalid data for a default.
-func surveySelectDefault(value string, options []string) string {
-	for _, v := range options {
-		if value == v {
-			return v // The provided value is acceptable
-		}
-	}
-	if len(options) > 0 {
-		return options[0] // Sync with the option which will be shown by the UX
-	}
-	// Either the value is not an option or there are no options.  Either of
-	// which should fail proper validation
-	return ""
-}
-
-// cwd returns the current working directory or exits 1 printing the error.
-func cwd() (cwd string) {
-	cwd, err := os.Getwd()
-	if err != nil {
-		panic(fmt.Sprintf("Unable to determine current working directory: %v", err))
-	}
-	return cwd
-}
-
-// deriveNameAndAbsolutePathFromPath returns application name and absolute path
-// to the application project root. The input parameter path could be one of:
-// 'relative/path/to/foo', '/absolute/path/to/foo', 'foo' or ”.
-func deriveNameAndAbsolutePathFromPath(path string) (string, string) {
-	var absPath string
-
-	// If path is not specified, we would like to use current working dir
-	if path == "" {
-		path = cwd()
-	}
-
-	// Expand the passed function name to its absolute path
-	absPath, err := filepath.Abs(path)
-	if err != nil {
-		return "", ""
-	}
-
-	// Get the name of the function, which equals to name of the current directory
-	pathParts := strings.Split(strings.TrimRight(path, string(os.PathSeparator)), string(os.PathSeparator))
-	return pathParts[len(pathParts)-1], absPath
+	dashboard.AddDashboard(rootCmd)
+	registry.AddRegistryCmd(rootCmd)
+	proxy.AddProxy(cmd2.DefaultRunCmdOpts, rootCmd)
 }
diff --git a/dubboctl/pkg/common/common.go b/dubboctl/pkg/common/common.go
new file mode 100644
index 0000000..bc8b8e2
--- /dev/null
+++ b/dubboctl/pkg/common/common.go
@@ -0,0 +1,152 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You 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 common
+
+import (
+	"fmt"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	"github.com/ory/viper"
+	"github.com/spf13/cobra"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+var ControlPlaneLog = core.Log.WithName("dubboctl")
+
+// bindFunc which conforms to the cobra PreRunE method signature
+type bindFunc func(*cobra.Command, []string) error
+
+// BindEnv returns a bindFunc that binds env vars to the named flags.
+func BindEnv(flags ...string) bindFunc {
+	return func(cmd *cobra.Command, args []string) (err error) {
+		for _, flag := range flags {
+			if err = viper.BindPFlag(flag, cmd.Flags().Lookup(flag)); err != nil {
+				return
+			}
+		}
+		viper.AutomaticEnv()        // read in environment variables for DUBBO_<flag>
+		viper.SetEnvPrefix("dubbo") // ensure that all have the prefix
+		return
+	}
+}
+
+func WriteFile(filename string, data []byte, perm os.FileMode) error {
+	if err := os.MkdirAll(filepath.Dir(filename), perm); err != nil {
+		return err
+	}
+	return os.WriteFile(filename, data, perm)
+}
+
+// AddConfirmFlag ensures common text/wording when the --path flag is used
+func AddConfirmFlag(cmd *cobra.Command, dflt bool) {
+	cmd.Flags().BoolP("confirm", "c", dflt, "Prompt to confirm options interactively ($DUBBO_CONFIRM)")
+}
+
+// AddPathFlag ensures common text/wording when the --path flag is used
+func AddPathFlag(cmd *cobra.Command) {
+	cmd.Flags().StringP("path", "p", "", "Path to the application.  Default is current directory ($DUBBO_PATH)")
+}
+
+// SurveySelectDefault returns 'value' if defined and exists in 'options'.
+// Otherwise, options[0] is returned if it exists.  Empty string otherwise.
+//
+// Usage Example:
+//
+//	languages := []string{ "go", "node", "rust" },
+//	survey.Select{
+//	  Options: options,
+//	  Default: surveySelectDefaut(cfg.Language, languages),
+//	}
+//
+// Summary:
+//
+// This protects against an incorrectly initialized survey.Select when the user
+// has provided a nonexistant option (validation is handled elsewhere) or
+// when a value is required but there exists no defaults (no default value on
+// the associated flag).
+//
+// Explanation:
+//
+// The above example chooses the default for the Survey (--confirm) question
+// in a way that works with user-provided flag and environment variable values.
+//
+//	`cfg.Language` is the current value set in the config struct, which is
+//	   populated from (in ascending order of precedence):
+//	   static flag default, associated environment variable, or command flag.
+//	`languages` are the options which are being used by the survey select.
+//
+// This cascade allows for the Survey questions to be properly pre-initialzed
+// with their associated environment variables or flags.  For example,
+// A user whose default language is set to 'node' using the global environment
+// variable FUNC_LANGUAGE will have that option pre-selected when running
+// `dubbo create -c`.
+//
+// The 'survey' package expects the value of the Default member to exist
+// in the 'Options' member.  This is not possible when user-provided data is
+// allowed for the default, hence this logic is necessary.
+//
+// For example, when the user is using prompts (--confirm) to select from a set
+// of options, but the associated flag either has an unrecognized value, or no
+// value at all, without this logic the resulting select prompt would be
+// initialized with this as the default value, and the act of what appears to
+// be choose the first option displayed does not overwrite the invalid default.
+// It could perhaps be argued this is a shortcoming in the survey package, but
+// it is also clearly an error to provide invalid data for a default.
+func SurveySelectDefault(value string, options []string) string {
+	for _, v := range options {
+		if value == v {
+			return v // The provided value is acceptable
+		}
+	}
+	if len(options) > 0 {
+		return options[0] // Sync with the option which will be shown by the UX
+	}
+	// Either the value is not an option or there are no options.  Either of
+	// which should fail proper validation
+	return ""
+}
+
+// DeriveNameAndAbsolutePathFromPath returns application name and absolute path
+// to the application project root. The input parameter path could be one of:
+// 'relative/path/to/foo', '/absolute/path/to/foo', 'foo' or ”.
+func DeriveNameAndAbsolutePathFromPath(path string) (string, string) {
+	var absPath string
+
+	// If path is not specified, we would like to use current working dir
+	if path == "" {
+		path = cwd()
+	}
+
+	// Expand the passed function name to its absolute path
+	absPath, err := filepath.Abs(path)
+	if err != nil {
+		return "", ""
+	}
+
+	// Get the name of the function, which equals to name of the current directory
+	pathParts := strings.Split(strings.TrimRight(path, string(os.PathSeparator)), string(os.PathSeparator))
+	return pathParts[len(pathParts)-1], absPath
+}
+
+// cwd returns the current working directory or exits 1 printing the error.
+func cwd() (cwd string) {
+	cwd, err := os.Getwd()
+	if err != nil {
+		panic(fmt.Sprintf("Unable to determine current working directory: %v", err))
+	}
+	return cwd
+}
diff --git a/dubboctl/cmd/dashboard_all_cmds.go b/dubboctl/pkg/dashboard/all.go
similarity index 97%
rename from dubboctl/cmd/dashboard_all_cmds.go
rename to dubboctl/pkg/dashboard/all.go
index 6e31d63..c7c1837 100644
--- a/dubboctl/cmd/dashboard_all_cmds.go
+++ b/dubboctl/pkg/dashboard/all.go
@@ -13,11 +13,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package dashboard
 
 import (
 	"context"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/kube"
 	"io"
 	"net"
 	"os"
@@ -39,8 +41,6 @@
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
 	"github.com/apache/dubbo-kubernetes/pkg/core/logger"
 )
 
diff --git a/dubboctl/cmd/dashboard.go b/dubboctl/pkg/dashboard/dashboard.go
similarity index 95%
rename from dubboctl/cmd/dashboard.go
rename to dubboctl/pkg/dashboard/dashboard.go
index f2ecc63..46de0d3 100644
--- a/dubboctl/cmd/dashboard.go
+++ b/dubboctl/pkg/dashboard/dashboard.go
@@ -13,13 +13,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package dashboard
 
 import (
 	"github.com/spf13/cobra"
 )
 
-func addDashboard(rootCmd *cobra.Command) {
+func AddDashboard(rootCmd *cobra.Command) {
 	dashboardCmd := &cobra.Command{
 		Use:   "dashboard",
 		Short: "Commands related to control plane components dashboards",
diff --git a/dubboctl/cmd/build.go b/dubboctl/pkg/deploy/build.go
similarity index 80%
rename from dubboctl/cmd/build.go
rename to dubboctl/pkg/deploy/build.go
index 9d6278d..e0956c1 100644
--- a/dubboctl/cmd/build.go
+++ b/dubboctl/pkg/deploy/build.go
@@ -13,10 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package deploy
 
 import (
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+	dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/builders/dockerfile"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/builders/pack"
+	util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"os"
 	"strings"
 )
@@ -29,20 +34,13 @@
 	"github.com/spf13/cobra"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/dockerfile"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/pack"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
-func addBuild(baseCmd *cobra.Command, newClient ClientFactory) {
+func AddBuild(baseCmd *cobra.Command, newClient ClientFactory) {
 	cmd := &cobra.Command{
 		Use:        "build",
 		Short:      "Build the image for the application",
 		Long:       ``,
 		SuggestFor: []string{"biuld", "buidl", "built"},
-		PreRunE: bindEnv("useDockerfile", "image", "path", "push", "force", "envs",
+		PreRunE: common.BindEnv("useDockerfile", "image", "path", "push", "force", "envs",
 			"builder-image"),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			return runBuildCmd(cmd, newClient)
@@ -61,16 +59,16 @@
 		"Whether to force build")
 	cmd.Flags().StringArrayP("envs", "e", []string{},
 		"environment variable for an application, KEY=VALUE format")
-	addPathFlag(cmd)
+	common.AddPathFlag(cmd)
 	baseCmd.AddCommand(cmd)
 }
 
 func runBuildCmd(cmd *cobra.Command, newClient ClientFactory) error {
-	if err := util.CreatePaths(); err != nil {
+	if err := util2.CreatePaths(); err != nil {
 		return err
 	}
 	cfg := newBuildConfig(cmd)
-	f, err := dubbo.NewDubbo(cfg.Path)
+	f, err := dubbo2.NewDubbo(cfg.Path)
 	if err != nil {
 		return err
 	}
@@ -80,7 +78,7 @@
 		return err
 	}
 	if !f.Initialized() {
-		return dubbo.NewErrNotInitialized(f.Root)
+		return dubbo2.NewErrNotInitialized(f.Root)
 	}
 	cfg.Configure(f)
 
@@ -110,9 +108,9 @@
 	return nil
 }
 
-func (c *buildConfig) Prompt(d *dubbo.Dubbo) (*buildConfig, error) {
+func (c *buildConfig) Prompt(d *dubbo2.Dubbo) (*buildConfig, error) {
 	var err error
-	if !util.InteractiveTerminal() {
+	if !util2.InteractiveTerminal() {
 		return c, nil
 	}
 
@@ -135,13 +133,13 @@
 	return c, err
 }
 
-func (c buildConfig) buildclientOptions() ([]dubbo.Option, error) {
-	var o []dubbo.Option
+func (c buildConfig) buildclientOptions() ([]dubbo2.Option, error) {
+	var o []dubbo2.Option
 
 	if c.UseDockerfile {
-		o = append(o, dubbo.WithBuilder(dockerfile.NewBuilder()))
+		o = append(o, dubbo2.WithBuilder(dockerfile.NewBuilder()))
 	} else {
-		o = append(o, dubbo.WithBuilder(pack.NewBuilder()))
+		o = append(o, dubbo2.WithBuilder(pack.NewBuilder()))
 	}
 
 	return o, nil
@@ -181,7 +179,7 @@
 	return c
 }
 
-func (c *buildConfig) Configure(f *dubbo.Dubbo) {
+func (c *buildConfig) Configure(f *dubbo2.Dubbo) {
 	if c.Path == "" {
 		root, err := os.Getwd()
 		if err != nil {
@@ -209,9 +207,9 @@
 				envs[parts[0]] = parts[1]
 			}
 		}
-		f.Build.BuildEnvs = make([]dubbo.Env, 0, len(envs))
+		f.Build.BuildEnvs = make([]dubbo2.Env, 0, len(envs))
 		for k, v := range envs {
-			f.Build.BuildEnvs = append(f.Build.BuildEnvs, dubbo.Env{
+			f.Build.BuildEnvs = append(f.Build.BuildEnvs, dubbo2.Env{
 				Name:  &k,
 				Value: &v,
 			})
diff --git a/dubboctl/cmd/client.go b/dubboctl/pkg/deploy/client.go
similarity index 71%
rename from dubboctl/cmd/client.go
rename to dubboctl/pkg/deploy/client.go
index 71894a8..f9d8f41 100644
--- a/dubboctl/cmd/client.go
+++ b/dubboctl/pkg/deploy/client.go
@@ -13,57 +13,54 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package deploy
 
 import (
+	dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+	dubbohttp "github.com/apache/dubbo-kubernetes/operator/http"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/builders/pack"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker/creds"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker/prompt"
+	config "github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"net/http"
 	"os"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/cmd/prompt"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/pack"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker/creds"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-	dubbohttp "github.com/apache/dubbo-kubernetes/dubboctl/internal/http"
-	config "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 // ClientFactory defines a constructor which assists in the creation of a Client
 // for use by commands.
 // See the NewClient constructor which is the fully populated ClientFactory used
 // by commands by default.
 // See NewClientFactory which constructs a minimal ClientFactory for use
 // during testing.
-type ClientFactory func(...dubbo.Option) (*dubbo.Client, func())
+type ClientFactory func(...dubbo2.Option) (*dubbo2.Client, func())
 
-func NewClient(options ...dubbo.Option) (*dubbo.Client, func()) {
+func NewClient(options ...dubbo2.Option) (*dubbo2.Client, func()) {
 	var (
 		t = newTransport(false)
 		c = newCredentialsProvider(config.Dir(), t)
 		d = newDubboDeployer()
-		o = []dubbo.Option{
-			dubbo.WithRepositoriesPath(config.RepositoriesPath()),
-			dubbo.WithBuilder(pack.NewBuilder()),
-			dubbo.WithPusher(docker.NewPusher(
+		o = []dubbo2.Option{
+			dubbo2.WithRepositoriesPath(config.RepositoriesPath()),
+			dubbo2.WithBuilder(pack.NewBuilder()),
+			dubbo2.WithPusher(docker.NewPusher(
 				docker.WithCredentialsProvider(c),
 				docker.WithTransport(t))),
-			dubbo.WithDeployer(d),
+			dubbo2.WithDeployer(d),
 		}
 	)
 	// Client is constructed with standard options plus any additional options
 	// which either augment or override the defaults.
-	client := dubbo.New(append(o, options...)...)
+	client := dubbo2.New(append(o, options...)...)
 
 	cleanup := func() {}
 	return client, cleanup
 }
 
-func newDubboDeployer() dubbo.Deployer {
-	var options []dubbo.DeployerOpt
+func newDubboDeployer() dubbo2.Deployer {
+	var options []dubbo2.DeployerOpt
 
-	return dubbo.NewDeployer(options...)
+	return dubbo2.NewDeployer(options...)
 }
 
 // newTransport returns a transport with cluster-flavor-specific variations
diff --git a/dubboctl/cmd/create.go b/dubboctl/pkg/deploy/create.go
similarity index 89%
rename from dubboctl/cmd/create.go
rename to dubboctl/pkg/deploy/create.go
index b3ff738..605124c 100644
--- a/dubboctl/cmd/create.go
+++ b/dubboctl/pkg/deploy/create.go
@@ -13,11 +13,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package deploy
 
 import (
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+	dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+	util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"os"
 	"strings"
 	"text/tabwriter"
@@ -32,11 +35,6 @@
 	"github.com/spf13/cobra"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 // ErrNoRuntime indicates that the language runtime flag was not passed.
 type ErrNoRuntime error
 
@@ -47,7 +45,7 @@
 type ErrInvalidTemplate error
 
 // NewCreateCmd creates a create command using the given client creator.
-func addCreate(baseCmd *cobra.Command, newClient ClientFactory) {
+func AddCreate(baseCmd *cobra.Command, newClient ClientFactory) {
 	cmd := &cobra.Command{
 		Use:   "create",
 		Short: "Create an application",
@@ -94,7 +92,7 @@
 	  $ {{.Name}} create -l go -t common mydubbo
 		`,
 		SuggestFor: []string{"vreate", "creaet", "craete", "new"},
-		PreRunE:    bindEnv("language", "template", "repository", "confirm", "init"),
+		PreRunE:    common.BindEnv("language", "template", "repository", "confirm", "init"),
 		Aliases:    []string{"init"},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			return runCreate(cmd, args, newClient)
@@ -108,7 +106,7 @@
 	cmd.Flags().BoolP("init", "i", false,
 		"Initialize the current project directly into a dubbo project without using a template")
 
-	addConfirmFlag(cmd, false)
+	common.AddConfirmFlag(cmd, false)
 
 	// Help Action
 	cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { runCreateHelp(cmd, args, newClient) })
@@ -138,7 +136,7 @@
 	// From environment variables, flags, arguments, and user prompts if --confirm
 	// (in increasing levels of precedence)
 	client, done := newClient(
-		dubbo.WithRepository(cfg.Repository))
+		dubbo2.WithRepository(cfg.Repository))
 	defer done()
 
 	// Validate - a deeper validation than that which is performed when
@@ -148,13 +146,13 @@
 	}
 
 	// Create
-	_, err = client.Init(&dubbo.Dubbo{
+	_, err = client.Init(&dubbo2.Dubbo{
 		Name:     cfg.Name,
 		Root:     cfg.Path,
 		Runtime:  cfg.Runtime,
 		Template: cfg.Template,
-		Build: dubbo.BuildSpec{
-			CnMirror: dubbo.BooleanWithComment{
+		Build: dubbo2.BuildSpec{
+			CnMirror: dubbo2.BooleanWithComment{
 				Comment: "Specify `cnMirror: true` to use the mirror in mainland China",
 			},
 		},
@@ -199,7 +197,7 @@
 		path = args[0]
 	}
 
-	dirName, absolutePath = deriveNameAndAbsolutePathFromPath(path)
+	dirName, absolutePath = common.DeriveNameAndAbsolutePathFromPath(path)
 
 	// Config is the final default values based off the execution context.
 	// When prompting, these become the defaults presented.
@@ -223,7 +221,7 @@
 	defer done()
 
 	// IN confirm mode.  If also in an interactive terminal, run prompts.
-	if util.InteractiveTerminal() {
+	if util2.InteractiveTerminal() {
 		createdCfg, err := cfg.prompt(client)
 		if err != nil {
 			return createdCfg, err
@@ -270,9 +268,9 @@
 // can be used to determine possible values for runtime, templates, etc.  a
 // pre-client validation should not be required, as the Client does its own
 // validation.
-func (c createConfig) Validate(client *dubbo.Client) (err error) {
-	dirName, _ := deriveNameAndAbsolutePathFromPath(c.Path)
-	if err = util.ValidateApplicationName(dirName); err != nil {
+func (c createConfig) Validate(client *dubbo2.Client) (err error) {
+	dirName, _ := common.DeriveNameAndAbsolutePathFromPath(c.Path)
+	if err = util2.ValidateApplicationName(dirName); err != nil {
 		return
 	}
 
@@ -293,7 +291,7 @@
 }
 
 // isValidRuntime determines if the given language runtime is a valid choice.
-func isValidRuntime(client *dubbo.Client, runtime string) bool {
+func isValidRuntime(client *dubbo2.Client, runtime string) bool {
 	runtimes, err := client.Runtimes()
 	if err != nil {
 		return false
@@ -308,7 +306,7 @@
 
 // isValidTemplate determines if the given template is valid for the given
 // runtime.
-func isValidTemplate(client *dubbo.Client, runtime, template string) bool {
+func isValidTemplate(client *dubbo2.Client, runtime, template string) bool {
 	if !isValidRuntime(client, runtime) {
 		return false
 	}
@@ -324,7 +322,7 @@
 	return false
 }
 
-func noRuntimeError(client *dubbo.Client) error {
+func noRuntimeError(client *dubbo2.Client) error {
 	b := strings.Builder{}
 	fmt.Fprintln(&b, "Required flag \"language\" not set.")
 	fmt.Fprintln(&b, "Available language runtimes are:")
@@ -338,7 +336,7 @@
 	return ErrNoRuntime(errors.New(b.String()))
 }
 
-func newInvalidRuntimeError(client *dubbo.Client, runtime string) error {
+func newInvalidRuntimeError(client *dubbo2.Client, runtime string) error {
 	b := strings.Builder{}
 	fmt.Fprintf(&b, "The language runtime '%v' is not recognized.\n", runtime)
 	fmt.Fprintln(&b, "Available language runtimes are:")
@@ -352,7 +350,7 @@
 	return ErrInvalidRuntime(errors.New(b.String()))
 }
 
-func newInvalidTemplateError(client *dubbo.Client, runtime, template string) error {
+func newInvalidTemplateError(client *dubbo2.Client, runtime, template string) error {
 	b := strings.Builder{}
 	fmt.Fprintf(&b, "The template '%v' was not found for language runtime '%v'.\n", template, runtime)
 	fmt.Fprintln(&b, "Available templates for this language runtime are:")
@@ -370,7 +368,7 @@
 // mutating the values. The provided clientFn is used to construct a transient
 // client for use during prompt autocompletion/suggestions (such as suggesting
 // valid templates)
-func (c createConfig) prompt(client *dubbo.Client) (createConfig, error) {
+func (c createConfig) prompt(client *dubbo2.Client) (createConfig, error) {
 	var qs []*survey.Question
 
 	runtimes, err := client.Runtimes()
@@ -403,11 +401,11 @@
 				Default: c.Path,
 			},
 			Validate: func(val interface{}) error {
-				derivedName, _ := deriveNameAndAbsolutePathFromPath(val.(string))
-				return util.ValidateApplicationName(derivedName)
+				derivedName, _ := common.DeriveNameAndAbsolutePathFromPath(val.(string))
+				return util2.ValidateApplicationName(derivedName)
 			},
 			Transform: func(ans interface{}) interface{} {
-				_, absolutePath := deriveNameAndAbsolutePathFromPath(ans.(string))
+				_, absolutePath := common.DeriveNameAndAbsolutePathFromPath(ans.(string))
 				return absolutePath
 			},
 		}, {
@@ -415,7 +413,7 @@
 			Prompt: &survey.Select{
 				Message: "Language Runtime:",
 				Options: runtimes,
-				Default: surveySelectDefault(c.Runtime, runtimes),
+				Default: common.SurveySelectDefault(c.Runtime, runtimes),
 			},
 		},
 	}
@@ -479,7 +477,7 @@
 
 // return templates for language runtime whose full name (including repository)
 // have the given prefix.
-func templatesWithPrefix(prefix, runtime string, client *dubbo.Client) ([]string, error) {
+func templatesWithPrefix(prefix, runtime string, client *dubbo2.Client) ([]string, error) {
 	var (
 		suggestions    []string
 		templates, err = client.Templates().List(runtime)
@@ -510,7 +508,7 @@
 	failSoft(err)
 
 	client, done := newClient(
-		dubbo.WithRepository(cfg.Repository))
+		dubbo2.WithRepository(cfg.Repository))
 	defer done()
 
 	options, err := RuntimeTemplateOptions(client) // human-friendly
@@ -546,7 +544,7 @@
 // RuntimeTemplateOptions is a human-friendly table of valid Language Runtime
 // to Template combinations.
 // Exported for use in docs.
-func RuntimeTemplateOptions(client *dubbo.Client) (string, error) {
+func RuntimeTemplateOptions(client *dubbo2.Client) (string, error) {
 	runtimes, err := client.Runtimes()
 	if err != nil {
 		return "", err
@@ -560,7 +558,7 @@
 		templates, err := client.Templates().List(r)
 		// Not all language packs will have templates for
 		// all available runtimes. Without this check
-		if err != nil && !errors.Is(err, dubbo.ErrTemplateNotFound) {
+		if err != nil && !errors.Is(err, dubbo2.ErrTemplateNotFound) {
 			return "", err
 		}
 		for _, t := range templates {
diff --git a/dubboctl/cmd/create_test.go b/dubboctl/pkg/deploy/create_test.go
similarity index 65%
rename from dubboctl/cmd/create_test.go
rename to dubboctl/pkg/deploy/create_test.go
index 4c8974b..fe2c2eb 100644
--- a/dubboctl/cmd/create_test.go
+++ b/dubboctl/pkg/deploy/create_test.go
@@ -13,15 +13,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package deploy_test
 
 import (
 	"errors"
+	"github.com/apache/dubbo-kubernetes/dubboctl/cmd"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/deploy"
+	. "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"testing"
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
+	"github.com/ory/viper"
 )
 
 // TestCreate_Execute ensures that an invocation of create with minimal settings
@@ -29,9 +33,9 @@
 func TestCreate_Execute(t *testing.T) {
 	_ = fromTempDirectory(t)
 
-	cmd := getRootCmd([]string{"create", "--language", "go", "myfunc"})
+	cmds := cmd.GetRootCmd([]string{"create", "--language", "go", "myfunc"})
 
-	if err := cmd.Execute(); err != nil {
+	if err := cmds.Execute(); err != nil {
 		t.Fatal(err)
 	}
 }
@@ -41,10 +45,10 @@
 func TestCreate_NoRuntime(t *testing.T) {
 	_ = fromTempDirectory(t)
 
-	cmd := getRootCmd([]string{"create", "myfunc"})
+	cmds := cmd.GetRootCmd([]string{"create", "myfunc"})
 
-	err := cmd.Execute()
-	var e ErrNoRuntime
+	err := cmds.Execute()
+	var e deploy.ErrNoRuntime
 	if !errors.As(err, &e) {
 		t.Fatalf("Did not receive ErrNoRuntime. Got %v", err)
 	}
@@ -55,10 +59,10 @@
 func TestCreate_WithInvalidRuntime(t *testing.T) {
 	_ = fromTempDirectory(t)
 
-	cmd := getRootCmd([]string{"create", "--language", "invalid", "myfunc"})
+	cmds := cmd.GetRootCmd([]string{"create", "--language", "invalid", "myfunc"})
 
-	err := cmd.Execute()
-	var e ErrInvalidRuntime
+	err := cmds.Execute()
+	var e deploy.ErrInvalidRuntime
 	if !errors.As(err, &e) {
 		t.Fatalf("Did not receive ErrInvalidRuntime. Got %v", err)
 	}
@@ -69,10 +73,10 @@
 func TestCreate_InvalidTemplate(t *testing.T) {
 	_ = fromTempDirectory(t)
 
-	cmd := getRootCmd([]string{"create", "--language", "go", "--template", "invalid", "myfunc"})
+	cmds := cmd.GetRootCmd([]string{"create", "--language", "go", "--template", "invalid", "myfunc"})
 
-	err := cmd.Execute()
-	var e ErrInvalidTemplate
+	err := cmds.Execute()
+	var e deploy.ErrInvalidTemplate
 	if !errors.As(err, &e) {
 		t.Fatalf("Did not receive ErrInvalidTemplate. Got %v", err)
 	}
@@ -85,8 +89,8 @@
 
 	// Execute the command with a function name containing invalid characters and
 	// confirm the expected error is returned
-	cmd := getRootCmd([]string{"create", "invalid!"})
-	err := cmd.Execute()
+	cmds := cmd.GetRootCmd([]string{"create", "invalid!"})
+	err := cmds.Execute()
 	var e util.ErrInvalidApplicationName
 	if !errors.As(err, &e) {
 		t.Fatalf("Did not receive ErrInvalidApplicationName. Got %v", err)
@@ -100,11 +104,28 @@
 
 	t.Setenv("XDG_CONFIG_HOME", t.TempDir())
 
-	cmd := getRootCmd([]string{"create", "--language=go", "myfunc"})
-	if err := cmd.Execute(); err != nil {
+	cmds := cmd.GetRootCmd([]string{"create", "--language=go", "myfunc"})
+	if err := cmds.Execute(); err != nil {
 		t.Fatal(err)
 	}
 
 	// Not failing is success.  Config files or settings beyond what are
 	// automatically written to to the given config home are currently optional.
 }
+
+// fromTempDirectory is a test helper which endeavors to create
+// an environment clean of developer's settings for use during CLI testing.
+func fromTempDirectory(t *testing.T) string {
+	t.Helper()
+	ClearEnvs(t)
+
+	// By default unit tests presume no config exists unless provided in testdata.
+	t.Setenv("XDG_CONFIG_HOME", t.TempDir())
+
+	// creates and CDs to a temp directory
+	d, done := Mktemp(t)
+
+	// Return to original directory and resets viper.
+	t.Cleanup(func() { done(); viper.Reset() })
+	return d
+}
diff --git a/dubboctl/cmd/deploy.go b/dubboctl/pkg/deploy/deploy.go
similarity index 89%
rename from dubboctl/cmd/deploy.go
rename to dubboctl/pkg/deploy/deploy.go
index 2d27385..4f04fa2 100644
--- a/dubboctl/cmd/deploy.go
+++ b/dubboctl/pkg/deploy/deploy.go
@@ -13,11 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package deploy
 
 import (
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+	dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/kube"
+	util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -37,18 +41,12 @@
 	"k8s.io/client-go/util/homedir"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 const (
 	basePort  = 30000
 	portLimit = 32767
 )
 
-func addDeploy(baseCmd *cobra.Command, newClient ClientFactory) {
+func AddDeploy(baseCmd *cobra.Command, newClient ClientFactory) {
 	cmd := &cobra.Command{
 		Use:   "deploy",
 		Short: "Generate the k8s yaml of the application. By the way, you can choose to build the image, push the image and apply to the k8s cluster.",
@@ -60,7 +58,7 @@
 	dubboctl deploy [flags]
 `,
 		SuggestFor: []string{"delpoy", "deplyo"},
-		PreRunE: bindEnv("path", "output", "namespace", "image", "envs", "name", "containerPort",
+		PreRunE: common.BindEnv("path", "output", "namespace", "image", "envs", "name", "containerPort",
 			"targetPort", "nodePort", "apply", "useDockerfile", "force", "builder-image", "build", "context",
 			"kubeConfig", "push"),
 		RunE: func(cmd *cobra.Command, args []string) error {
@@ -106,17 +104,17 @@
 	cmd.Flags().StringP("portName", "", "http",
 		"Name of the port to be exposed")
 
-	addPathFlag(cmd)
+	common.AddPathFlag(cmd)
 	cmd.Flags().SetInterspersed(false)
 	baseCmd.AddCommand(cmd)
 }
 
 func runDeploy(cmd *cobra.Command, newClient ClientFactory) error {
-	if err := util.CreatePaths(); err != nil {
+	if err := util2.CreatePaths(); err != nil {
 		return err
 	}
 	cfg := newDeployConfig(cmd)
-	f, err := dubbo.NewDubbo(cfg.Path)
+	f, err := dubbo2.NewDubbo(cfg.Path)
 	if err != nil {
 		return err
 	}
@@ -129,7 +127,7 @@
 	}
 
 	if !f.Initialized() {
-		return dubbo.NewErrNotInitialized(f.Root)
+		return dubbo2.NewErrNotInitialized(f.Root)
 	}
 
 	cfg.Configure(f)
@@ -198,7 +196,7 @@
 	return nil
 }
 
-func (d DeployConfig) deployclientOptions() ([]dubbo.Option, error) {
+func (d DeployConfig) deployclientOptions() ([]dubbo2.Option, error) {
 	o, err := d.buildclientOptions()
 	if err != nil {
 		return o, err
@@ -212,11 +210,11 @@
 	if err != nil {
 		return o, err
 	}
-	o = append(o, dubbo.WithKubeClient(cli))
+	o = append(o, dubbo2.WithKubeClient(cli))
 	return o, nil
 }
 
-func applyTok8s(cmd *cobra.Command, d *dubbo.Dubbo) error {
+func applyTok8s(cmd *cobra.Command, d *dubbo2.Dubbo) error {
 	file := filepath.Join(d.Root, d.Deploy.Output)
 	c := exec.CommandContext(cmd.Context(), "kubectl", "apply", "-f", file)
 	c.Stdout = os.Stdout
@@ -233,9 +231,9 @@
 	return nil
 }
 
-func (c *DeployConfig) Prompt(d *dubbo.Dubbo) (*DeployConfig, error) {
+func (c *DeployConfig) Prompt(d *dubbo2.Dubbo) (*DeployConfig, error) {
 	var err error
-	if !util.InteractiveTerminal() {
+	if !util2.InteractiveTerminal() {
 		return c, nil
 	}
 	buildconfig, err := c.buildConfig.Prompt(d)
@@ -261,7 +259,7 @@
 	return c, err
 }
 
-func (c DeployConfig) Configure(f *dubbo.Dubbo) {
+func (c DeployConfig) Configure(f *dubbo2.Dubbo) {
 	c.buildConfig.Configure(f)
 	if c.Namespace != "" {
 		f.Deploy.Namespace = c.Namespace
diff --git a/dubboctl/cmd/repository.go b/dubboctl/pkg/deploy/repository.go
similarity index 94%
rename from dubboctl/cmd/repository.go
rename to dubboctl/pkg/deploy/repository.go
index 2714d5f..7384505 100644
--- a/dubboctl/cmd/repository.go
+++ b/dubboctl/pkg/deploy/repository.go
@@ -13,11 +13,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package deploy
 
 import (
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
+	util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"os"
 )
 
@@ -29,14 +32,9 @@
 	"github.com/spf13/cobra"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 // command constructors
 // --------------------
-func addRepository(baseCmd *cobra.Command, newClient ClientFactory) {
+func AddRepository(baseCmd *cobra.Command, newClient ClientFactory) {
 	cmd := &cobra.Command{
 		Short:   "Manage installed template repositories",
 		Use:     "repository",
@@ -165,13 +163,13 @@
 	  default
 `,
 		SuggestFor: []string{"repositories", "repos", "template", "templates", "pack", "packs"},
-		PreRunE:    bindEnv("confirm"),
+		PreRunE:    common.BindEnv("confirm"),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			return runRepository(cmd, args, newClient)
 		},
 	}
 
-	addConfirmFlag(cmd, false)
+	common.AddConfirmFlag(cmd, false)
 
 	cmd.AddCommand(NewRepositoryListCmd(newClient))
 	cmd.AddCommand(NewRepositoryAddCmd(newClient))
@@ -182,64 +180,64 @@
 }
 
 func NewRepositoryListCmd(newClient ClientFactory) *cobra.Command {
-	cmd := &cobra.Command{
+	cmds := &cobra.Command{
 		Short:   "List repositories",
 		Use:     "list",
 		Aliases: []string{"ls"},
-		PreRunE: bindEnv("confirm"),
+		PreRunE: common.BindEnv("confirm"),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			return runRepositoryList(cmd, args, newClient)
 		},
 	}
 
-	addConfirmFlag(cmd, false)
-	return cmd
+	common.AddConfirmFlag(cmds, false)
+	return cmds
 }
 
 func NewRepositoryAddCmd(newClient ClientFactory) *cobra.Command {
-	cmd := &cobra.Command{
+	cmds := &cobra.Command{
 		Short:      "Add a repository",
 		Use:        "add <name> <url>",
 		SuggestFor: []string{"ad", "install"},
-		PreRunE:    bindEnv("confirm"),
+		PreRunE:    common.BindEnv("confirm"),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			return runRepositoryAdd(cmd, args, newClient)
 		},
 	}
 
-	addConfirmFlag(cmd, false)
-	return cmd
+	common.AddConfirmFlag(cmds, false)
+	return cmds
 }
 
 func NewRepositoryRenameCmd(newClient ClientFactory) *cobra.Command {
-	cmd := &cobra.Command{
+	cmds := &cobra.Command{
 		Short:   "Rename a repository",
 		Use:     "rename <old> <new>",
 		Aliases: []string{"mv"},
-		PreRunE: bindEnv("confirm"),
+		PreRunE: common.BindEnv("confirm"),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			return runRepositoryRename(cmd, args, newClient)
 		},
 	}
 
-	addConfirmFlag(cmd, false)
-	return cmd
+	common.AddConfirmFlag(cmds, false)
+	return cmds
 }
 
 func NewRepositoryRemoveCmd(newClient ClientFactory) *cobra.Command {
-	cmd := &cobra.Command{
+	cmds := &cobra.Command{
 		Short:      "Remove a repository",
 		Use:        "remove <name>",
 		Aliases:    []string{"rm"},
 		SuggestFor: []string{"delete", "del"},
-		PreRunE:    bindEnv("confirm"),
+		PreRunE:    common.BindEnv("confirm"),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			return runRepositoryRemove(cmd, args, newClient)
 		},
 	}
 
-	addConfirmFlag(cmd, false)
-	return cmd
+	common.AddConfirmFlag(cmds, false)
+	return cmds
 }
 
 // command implementations
@@ -324,7 +322,7 @@
 	}
 
 	// Adding a repository requires there be a config path structure on disk
-	if err = util.CreatePaths(); err != nil {
+	if err = util2.CreatePaths(); err != nil {
 		return
 	}
 
@@ -368,7 +366,7 @@
 	// This is an expected case.  The empty value will be echoed to stdout, the
 	// API will be invoked, and a helpful error message will indicate that the
 	// request is missing required parameters.
-	if cfg.Confirm && util.InteractiveTerminal() {
+	if cfg.Confirm && util2.InteractiveTerminal() {
 		questions := []*survey.Question{
 			{
 				Name:     "Name",
@@ -439,7 +437,7 @@
 	}
 
 	// Confirm (interactive prompt mode)
-	if cfg.Confirm && util.InteractiveTerminal() {
+	if cfg.Confirm && util2.InteractiveTerminal() {
 		questions := []*survey.Question{
 			{
 				Name:     "Old",
@@ -515,7 +513,7 @@
 	}
 
 	// Confirm (interactive prompt mode)
-	if cfg.Confirm && util.InteractiveTerminal() {
+	if cfg.Confirm && util2.InteractiveTerminal() {
 		questions := []*survey.Question{
 			{
 				Name:     "Name",
diff --git a/dubboctl/cmd/root_test.go b/dubboctl/pkg/deploy/repository_test.go
similarity index 67%
rename from dubboctl/cmd/root_test.go
rename to dubboctl/pkg/deploy/repository_test.go
index 294b955..095c148 100644
--- a/dubboctl/cmd/root_test.go
+++ b/dubboctl/pkg/deploy/repository_test.go
@@ -13,38 +13,39 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package deploy_test
 
 import (
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/deploy"
+	. "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
+	"github.com/ory/viper"
 	"io"
 	"os"
 	"strings"
 	"testing"
 )
 
-import (
-	"github.com/ory/viper"
-)
+// TestRepository_List ensures that the 'list' subcommand shows the client's
+// set of repositories by name for builtin repositories, by explicitly
+// setting the repositories' path to a new path which includes no others.
+func TestRepository_List(t *testing.T) {
+	_ = FromTempDirectory(t)
 
-import (
-	. "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
-)
+	cmd := deploy.NewRepositoryListCmd(deploy.NewClient)
+	cmd.SetArgs([]string{}) // Do not use test command args
 
-// fromTempDirectory is a test helper which endeavors to create
-// an environment clean of developer's settings for use during CLI testing.
-func fromTempDirectory(t *testing.T) string {
-	t.Helper()
-	ClearEnvs(t)
+	// Execute the command, capturing the output sent to stdout
+	stdout := piped(t)
+	if err := cmd.Execute(); err != nil {
+		t.Fatal(err)
+	}
 
-	// By default unit tests presume no config exists unless provided in testdata.
-	t.Setenv("XDG_CONFIG_HOME", t.TempDir())
-
-	// creates and CDs to a temp directory
-	d, done := Mktemp(t)
-
-	// Return to original directory and resets viper.
-	t.Cleanup(func() { done(); viper.Reset() })
-	return d
+	// Assert the output matches expect (whitespace trimmed)
+	expect := "default"
+	output := stdout()
+	if output != expect {
+		t.Fatalf("expected:\n'%v'\ngot:\n'%v'\n", expect, output)
+	}
 }
 
 // pipe the output of stdout to a buffer whose value is returned
@@ -82,3 +83,20 @@
 		return strings.TrimSpace(b.String())
 	}
 }
+
+// fromTempDirectory is a test helper which endeavors to create
+// an environment clean of developer's settings for use during CLI testing.
+func FromTempDirectory(t *testing.T) string {
+	t.Helper()
+	ClearEnvs(t)
+
+	// By default unit tests presume no config exists unless provided in testdata.
+	t.Setenv("XDG_CONFIG_HOME", t.TempDir())
+
+	// creates and CDs to a temp directory
+	d, done := Mktemp(t)
+
+	// Return to original directory and resets viper.
+	t.Cleanup(func() { done(); viper.Reset() })
+	return d
+}
diff --git a/dubboctl/cmd/completion_util.go b/dubboctl/pkg/deploy/util.go
similarity index 95%
rename from dubboctl/cmd/completion_util.go
rename to dubboctl/pkg/deploy/util.go
index 96f4d8c..017364d 100644
--- a/dubboctl/cmd/completion_util.go
+++ b/dubboctl/pkg/deploy/util.go
@@ -13,10 +13,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package deploy
 
 import (
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
 	"os"
 	"strings"
 )
@@ -25,10 +26,6 @@
 	"github.com/spf13/cobra"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
 func CompleteRuntimeList(cmd *cobra.Command, args []string, toComplete string, client *dubbo.Client) (matches []string, directive cobra.ShellCompDirective) {
 	runtimes, err := client.Runtimes()
 	if err != nil {
diff --git a/dubboctl/cmd/generate_certificate.go b/dubboctl/pkg/generate/certificate.go
similarity index 99%
rename from dubboctl/cmd/generate_certificate.go
rename to dubboctl/pkg/generate/certificate.go
index b3fe333..b55a4b6 100644
--- a/dubboctl/cmd/generate_certificate.go
+++ b/dubboctl/pkg/generate/certificate.go
@@ -13,7 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package generate
 
 import (
 	"fmt"
diff --git a/dubboctl/cmd/generate.go b/dubboctl/pkg/generate/generate.go
similarity index 94%
rename from dubboctl/cmd/generate.go
rename to dubboctl/pkg/generate/generate.go
index 5c2bc6e..ae76936 100644
--- a/dubboctl/cmd/generate.go
+++ b/dubboctl/pkg/generate/generate.go
@@ -13,13 +13,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package generate
 
 import (
 	"github.com/spf13/cobra"
 )
 
-func addGenerate(rootCmd *cobra.Command) {
+func AddGenerate(rootCmd *cobra.Command) {
 	generateCmd := &cobra.Command{
 		Use:   "generate",
 		Short: "Generate resources, tokens, etc",
diff --git a/dubboctl/cmd/generate.go b/dubboctl/pkg/manifest/common.go
similarity index 72%
copy from dubboctl/cmd/generate.go
copy to dubboctl/pkg/manifest/common.go
index 5c2bc6e..8886181 100644
--- a/dubboctl/cmd/generate.go
+++ b/dubboctl/pkg/manifest/common.go
@@ -13,18 +13,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package manifest
 
 import (
-	"github.com/spf13/cobra"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-func addGenerate(rootCmd *cobra.Command) {
-	generateCmd := &cobra.Command{
-		Use:   "generate",
-		Short: "Generate resources, tokens, etc",
-		Long:  `Generate resources, tokens, etc.`,
-	}
-	rootCmd.AddCommand(generateCmd)
-	NewGenerateCertificateCmd(generateCmd)
-}
+var (
+	// TestInstallFlag and TestCli are uses for black box testing
+	TestInstallFlag bool
+	TestCli         client.Client
+)
diff --git a/dubboctl/cmd/manifest_diff.go b/dubboctl/pkg/manifest/diff.go
similarity index 97%
rename from dubboctl/cmd/manifest_diff.go
rename to dubboctl/pkg/manifest/diff.go
index b1942ff..b62981c 100644
--- a/dubboctl/cmd/manifest_diff.go
+++ b/dubboctl/pkg/manifest/diff.go
@@ -13,11 +13,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package manifest
 
 import (
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/kube"
 	"os"
 	"path/filepath"
 	"strings"
@@ -30,7 +31,6 @@
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
 	"github.com/apache/dubbo-kubernetes/pkg/core/logger"
 )
 
diff --git a/dubboctl/cmd/manifest_generate.go b/dubboctl/pkg/manifest/generate.go
similarity index 94%
rename from dubboctl/cmd/manifest_generate.go
rename to dubboctl/pkg/manifest/generate.go
index fd69e96..6e5431f 100644
--- a/dubboctl/cmd/manifest_generate.go
+++ b/dubboctl/pkg/manifest/generate.go
@@ -13,10 +13,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package manifest
 
 import (
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/manifest"
+	"github.com/apache/dubbo-kubernetes/operator/manifest/render"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/kube"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"os"
 	"path"
 	"sort"
@@ -32,12 +38,6 @@
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest/render"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
 	"github.com/apache/dubbo-kubernetes/pkg/core/logger"
 )
 
diff --git a/dubboctl/cmd/manifest_install.go b/dubboctl/pkg/manifest/install.go
similarity index 86%
rename from dubboctl/cmd/manifest_install.go
rename to dubboctl/pkg/manifest/install.go
index a767193..972be59 100644
--- a/dubboctl/cmd/manifest_install.go
+++ b/dubboctl/pkg/manifest/install.go
@@ -13,17 +13,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package manifest
 
 import (
+	"github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/kube"
 	"github.com/spf13/cobra"
 
 	"go.uber.org/zap/zapcore"
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
 	"github.com/apache/dubbo-kubernetes/pkg/core/logger"
 )
 
@@ -74,14 +74,14 @@
 
 func installManifests(miArgs *ManifestInstallArgs, cfg *v1alpha1.DubboConfig) error {
 	var cliOpts []kube.CtlClientOption
-	if TestInstallFlag {
-		cliOpts = []kube.CtlClientOption{kube.WithCli(TestCli)}
-	} else {
-		cliOpts = []kube.CtlClientOption{
-			kube.WithKubeConfigPath(miArgs.KubeConfigPath),
-			kube.WithContext(miArgs.Context),
-		}
-	}
+	//if cmd.TestInstallFlag {
+	//	cliOpts = []kube.CtlClientOption{kube.WithCli(cmd.TestCli)}
+	//} else {
+	//	cliOpts = []kube.CtlClientOption{
+	//		kube.WithKubeConfigPath(miArgs.KubeConfigPath),
+	//		kube.WithContext(miArgs.Context),
+	//	}
+	//}
 	cli, err := kube.NewCtlClient(cliOpts...)
 	if err != nil {
 		return err
diff --git a/dubboctl/cmd/manifest_uninstall.go b/dubboctl/pkg/manifest/uninstall.go
similarity index 86%
rename from dubboctl/cmd/manifest_uninstall.go
rename to dubboctl/pkg/manifest/uninstall.go
index 8d10215..5fefbb3 100644
--- a/dubboctl/cmd/manifest_uninstall.go
+++ b/dubboctl/pkg/manifest/uninstall.go
@@ -13,17 +13,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package manifest
 
 import (
+	"github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/kube"
 	"github.com/spf13/cobra"
 
 	"go.uber.org/zap/zapcore"
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
 	"github.com/apache/dubbo-kubernetes/pkg/core/logger"
 )
 
@@ -71,14 +71,14 @@
 
 func uninstallManifests(muArgs *ManifestUninstallArgs, cfg *v1alpha1.DubboConfig) error {
 	var cliOpts []kube.CtlClientOption
-	if TestInstallFlag {
-		cliOpts = []kube.CtlClientOption{kube.WithCli(TestCli)}
-	} else {
-		cliOpts = []kube.CtlClientOption{
-			kube.WithKubeConfigPath(muArgs.KubeConfigPath),
-			kube.WithContext(muArgs.Context),
-		}
-	}
+	//if cmd.TestInstallFlag {
+	//	cliOpts = []kube.CtlClientOption{kube.WithCli(cmd.TestCli)}
+	//} else {
+	//	cliOpts = []kube.CtlClientOption{
+	//		kube.WithKubeConfigPath(muArgs.KubeConfigPath),
+	//		kube.WithContext(muArgs.Context),
+	//	}
+	//}
 	cli, err := kube.NewCtlClient(cliOpts...)
 	if err != nil {
 		return err
diff --git a/dubboctl/cmd/profile_diff.go b/dubboctl/pkg/profile/diff.go
similarity index 93%
rename from dubboctl/cmd/profile_diff.go
rename to dubboctl/pkg/profile/diff.go
index 8080601..2b80d3a 100644
--- a/dubboctl/cmd/profile_diff.go
+++ b/dubboctl/pkg/profile/diff.go
@@ -13,11 +13,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package profile
 
 import (
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/manifest"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/kube"
 )
 
 import (
@@ -27,9 +30,6 @@
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
 	"github.com/apache/dubbo-kubernetes/pkg/core/logger"
 )
 
diff --git a/dubboctl/cmd/profile_list.go b/dubboctl/pkg/profile/list.go
similarity index 93%
rename from dubboctl/cmd/profile_list.go
rename to dubboctl/pkg/profile/list.go
index 695f1a7..8971694 100644
--- a/dubboctl/cmd/profile_list.go
+++ b/dubboctl/pkg/profile/list.go
@@ -13,10 +13,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package profile
 
 import (
 	"errors"
+	"github.com/apache/dubbo-kubernetes/operator/manifest"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"strings"
 )
 
@@ -27,9 +30,6 @@
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
 	"github.com/apache/dubbo-kubernetes/pkg/core/logger"
 )
 
diff --git a/dubboctl/cmd/proxy_context.go b/dubboctl/pkg/proxy/context.go
similarity index 97%
rename from dubboctl/cmd/proxy_context.go
rename to dubboctl/pkg/proxy/context.go
index 4de6264..69eeb57 100644
--- a/dubboctl/cmd/proxy_context.go
+++ b/dubboctl/pkg/proxy/context.go
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package cmd
+package proxy
 
 import (
+	"github.com/apache/dubbo-kubernetes/operator/pkg/envoy"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -33,7 +34,6 @@
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/envoy"
 	"github.com/apache/dubbo-kubernetes/pkg/config/app/dubboctl"
 	"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
 	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
diff --git a/dubboctl/cmd/proxy.go b/dubboctl/pkg/proxy/proxy.go
similarity index 97%
rename from dubboctl/cmd/proxy.go
rename to dubboctl/pkg/proxy/proxy.go
index 98f2918..3247c72 100644
--- a/dubboctl/cmd/proxy.go
+++ b/dubboctl/pkg/proxy/proxy.go
@@ -15,11 +15,13 @@
  * limitations under the License.
  */
 
-package cmd
+package proxy
 
 import (
 	"context"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/dubboctl/pkg/common"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/envoy"
 	"io"
 	"os"
 	"path/filepath"
@@ -36,7 +38,6 @@
 
 import (
 	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/envoy"
 	"github.com/apache/dubbo-kubernetes/pkg/config/app/dubboctl"
 	"github.com/apache/dubbo-kubernetes/pkg/core"
 	dubbo_cmd "github.com/apache/dubbo-kubernetes/pkg/core/cmd"
@@ -50,7 +51,7 @@
 	"github.com/apache/dubbo-kubernetes/pkg/util/template"
 )
 
-var runLog = controlPlaneLog.WithName("proxy")
+var runLog = common.ControlPlaneLog.WithName("proxy")
 
 type ResourceType string
 
@@ -95,7 +96,7 @@
 	return os.WriteFile(filename, data, perm)
 }
 
-func addProxy(opts dubbo_cmd.RunCmdOpts, cmd *cobra.Command) {
+func AddProxy(opts dubbo_cmd.RunCmdOpts, cmd *cobra.Command) {
 	proxyArgs := DefaultProxyConfig()
 	cfg := proxyArgs.Config
 	var proxyResource model.Resource
diff --git a/dubboctl/cmd/registry.go b/dubboctl/pkg/registry/registry.go
similarity index 95%
rename from dubboctl/cmd/registry.go
rename to dubboctl/pkg/registry/registry.go
index b47f649..f7ca561 100644
--- a/dubboctl/cmd/registry.go
+++ b/dubboctl/pkg/registry/registry.go
@@ -13,17 +13,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package registry
 
 import (
+	"github.com/apache/dubbo-kubernetes/operator/registry/zk"
 	"github.com/spf13/cobra"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/registry/zk"
-)
-
-func addRegistryCmd(rootCmd *cobra.Command) {
+func AddRegistryCmd(rootCmd *cobra.Command) {
 	addZkRegistryCmd(rootCmd)
 }
 
diff --git a/dubboctl/templates/go/README.md b/dubboctl/pkg/templates/go/README.md
similarity index 100%
rename from dubboctl/templates/go/README.md
rename to dubboctl/pkg/templates/go/README.md
diff --git a/dubboctl/templates/go/common/.gitignore b/dubboctl/pkg/templates/go/common/.gitignore
similarity index 100%
rename from dubboctl/templates/go/common/.gitignore
rename to dubboctl/pkg/templates/go/common/.gitignore
diff --git a/dubboctl/templates/go/common/api/api.pb.go b/dubboctl/pkg/templates/go/common/api/api.pb.go
similarity index 100%
rename from dubboctl/templates/go/common/api/api.pb.go
rename to dubboctl/pkg/templates/go/common/api/api.pb.go
diff --git a/dubboctl/templates/go/common/api/api.proto b/dubboctl/pkg/templates/go/common/api/api.proto
similarity index 100%
rename from dubboctl/templates/go/common/api/api.proto
rename to dubboctl/pkg/templates/go/common/api/api.proto
diff --git a/dubboctl/templates/go/common/api/api_triple.pb.go b/dubboctl/pkg/templates/go/common/api/api_triple.pb.go
similarity index 100%
rename from dubboctl/templates/go/common/api/api_triple.pb.go
rename to dubboctl/pkg/templates/go/common/api/api_triple.pb.go
diff --git a/dubboctl/templates/go/common/cmd/app.go b/dubboctl/pkg/templates/go/common/cmd/app.go
similarity index 100%
rename from dubboctl/templates/go/common/cmd/app.go
rename to dubboctl/pkg/templates/go/common/cmd/app.go
diff --git a/dubboctl/templates/go/common/conf/dubbogo.yaml b/dubboctl/pkg/templates/go/common/conf/dubbogo.yaml
similarity index 100%
rename from dubboctl/templates/go/common/conf/dubbogo.yaml
rename to dubboctl/pkg/templates/go/common/conf/dubbogo.yaml
diff --git a/dubboctl/templates/go/common/go.mod b/dubboctl/pkg/templates/go/common/go.mod
similarity index 100%
rename from dubboctl/templates/go/common/go.mod
rename to dubboctl/pkg/templates/go/common/go.mod
diff --git a/dubboctl/templates/go/common/go.sum b/dubboctl/pkg/templates/go/common/go.sum
similarity index 100%
rename from dubboctl/templates/go/common/go.sum
rename to dubboctl/pkg/templates/go/common/go.sum
diff --git a/dubboctl/templates/go/common/pkg/service/service.go b/dubboctl/pkg/templates/go/common/pkg/service/service.go
similarity index 100%
rename from dubboctl/templates/go/common/pkg/service/service.go
rename to dubboctl/pkg/templates/go/common/pkg/service/service.go
diff --git a/dubboctl/templates/java/README.md b/dubboctl/pkg/templates/java/README.md
similarity index 100%
rename from dubboctl/templates/java/README.md
rename to dubboctl/pkg/templates/java/README.md
diff --git a/dubboctl/templates/java/common/.gitignore b/dubboctl/pkg/templates/java/common/.gitignore
similarity index 100%
rename from dubboctl/templates/java/common/.gitignore
rename to dubboctl/pkg/templates/java/common/.gitignore
diff --git a/dubboctl/templates/java/common/pom.xml b/dubboctl/pkg/templates/java/common/pom.xml
similarity index 100%
rename from dubboctl/templates/java/common/pom.xml
rename to dubboctl/pkg/templates/java/common/pom.xml
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/DemoApplication.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/DemoApplication.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/DemoApplication.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/DemoApplication.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/BasicController.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/BasicController.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/BasicController.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/BasicController.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/User.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/User.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/demos/web/User.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/demos/web/User.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
diff --git a/dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java b/dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
similarity index 100%
rename from dubboctl/templates/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
rename to dubboctl/pkg/templates/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
diff --git a/dubboctl/templates/java/common/src/main/resources/application.yml b/dubboctl/pkg/templates/java/common/src/main/resources/application.yml
similarity index 100%
rename from dubboctl/templates/java/common/src/main/resources/application.yml
rename to dubboctl/pkg/templates/java/common/src/main/resources/application.yml
diff --git a/dubboctl/templates/java/common/src/main/resources/log4j.properties b/dubboctl/pkg/templates/java/common/src/main/resources/log4j.properties
similarity index 100%
rename from dubboctl/templates/java/common/src/main/resources/log4j.properties
rename to dubboctl/pkg/templates/java/common/src/main/resources/log4j.properties
diff --git a/dubboctl/templates/java/common/src/main/resources/static/index.html b/dubboctl/pkg/templates/java/common/src/main/resources/static/index.html
similarity index 100%
rename from dubboctl/templates/java/common/src/main/resources/static/index.html
rename to dubboctl/pkg/templates/java/common/src/main/resources/static/index.html
diff --git a/dubboctl/templates/java/common/src/test/java/com/example/demo/DemoApplicationTests.java b/dubboctl/pkg/templates/java/common/src/test/java/com/example/demo/DemoApplicationTests.java
similarity index 100%
rename from dubboctl/templates/java/common/src/test/java/com/example/demo/DemoApplicationTests.java
rename to dubboctl/pkg/templates/java/common/src/test/java/com/example/demo/DemoApplicationTests.java
diff --git a/dubboctl/templates/manifest.yaml b/dubboctl/pkg/templates/manifest.yaml
similarity index 100%
rename from dubboctl/templates/manifest.yaml
rename to dubboctl/pkg/templates/manifest.yaml
diff --git a/go.mod b/go.mod
index 74b5958..8fae009 100644
--- a/go.mod
+++ b/go.mod
@@ -58,6 +58,7 @@
 	github.com/google/uuid v1.6.0
 	github.com/google/yamlfmt v0.9.0
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
+	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
 	github.com/heroku/color v0.0.6
 	github.com/hoisie/mustache v0.0.0-20160804235033-6375acf62c69
 	github.com/jhump/protoreflect v1.16.0
diff --git a/go.sum b/go.sum
index 27760de..1195074 100644
--- a/go.sum
+++ b/go.sum
@@ -2006,3 +2006,7 @@
 sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
 sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
+xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM=
+xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
+xorm.io/xorm v1.3.9 h1:TUovzS0ko+IQ1XnNLfs5dqK1cJl1H5uHpWbWqAQ04nU=
+xorm.io/xorm v1.3.9/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
\ No newline at end of file
diff --git a/dubboctl/internal/dubbo/client.go b/operator/dubbo/client.go
similarity index 98%
rename from dubboctl/internal/dubbo/client.go
rename to operator/dubbo/client.go
index a9b0d99..0cfafaf 100644
--- a/dubboctl/internal/dubbo/client.go
+++ b/operator/dubbo/client.go
@@ -21,6 +21,8 @@
 	"bufio"
 	"context"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/kube"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"io"
 	"os"
 	"path/filepath"
@@ -34,11 +36,6 @@
 	"gopkg.in/yaml.v3"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/kube"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 const (
 	// DefaultTemplate is the default function signature / environmental context
 	// of the resultant function.  All runtimes are expected to have at least
diff --git a/dubboctl/internal/dubbo/client_test.go b/operator/dubbo/client_test.go
similarity index 100%
rename from dubboctl/internal/dubbo/client_test.go
rename to operator/dubbo/client_test.go
diff --git a/dubboctl/internal/dubbo/deploy.tpl b/operator/dubbo/deploy.tpl
similarity index 100%
rename from dubboctl/internal/dubbo/deploy.tpl
rename to operator/dubbo/deploy.tpl
diff --git a/dubboctl/internal/dubbo/deployer.go b/operator/dubbo/deployer.go
similarity index 96%
rename from dubboctl/internal/dubbo/deployer.go
rename to operator/dubbo/deployer.go
index ebd15ee..54d21ba 100644
--- a/dubboctl/internal/dubbo/deployer.go
+++ b/operator/dubbo/deployer.go
@@ -21,14 +21,11 @@
 	"context"
 	_ "embed"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"os"
 	template2 "text/template"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 const (
 	deployTemplateFile = "deploy.tpl"
 )
diff --git a/dubboctl/internal/dubbo/dockerfile.go b/operator/dubbo/dockerfile.go
similarity index 100%
rename from dubboctl/internal/dubbo/dockerfile.go
rename to operator/dubbo/dockerfile.go
diff --git a/dubboctl/internal/dubbo/dubbo.go b/operator/dubbo/dubbo.go
similarity index 100%
rename from dubboctl/internal/dubbo/dubbo.go
rename to operator/dubbo/dubbo.go
diff --git a/dubboctl/internal/dubbo/dubbo_test.go b/operator/dubbo/dubbo_test.go
similarity index 100%
rename from dubboctl/internal/dubbo/dubbo_test.go
rename to operator/dubbo/dubbo_test.go
diff --git a/dubboctl/internal/dubbo/errors.go b/operator/dubbo/errors.go
similarity index 100%
rename from dubboctl/internal/dubbo/errors.go
rename to operator/dubbo/errors.go
diff --git a/dubboctl/internal/dubbo/repositories.go b/operator/dubbo/repositories.go
similarity index 100%
rename from dubboctl/internal/dubbo/repositories.go
rename to operator/dubbo/repositories.go
diff --git a/dubboctl/internal/dubbo/repositories_test.go b/operator/dubbo/repositories_test.go
similarity index 90%
rename from dubboctl/internal/dubbo/repositories_test.go
rename to operator/dubbo/repositories_test.go
index 923ac83..203a00a 100644
--- a/dubboctl/internal/dubbo/repositories_test.go
+++ b/operator/dubbo/repositories_test.go
@@ -18,6 +18,8 @@
 package dubbo_test
 
 import (
+	dubbo2 "github.com/apache/dubbo-kubernetes/operator/dubbo"
+	. "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
 	"os"
 	"path/filepath"
 	"testing"
@@ -27,11 +29,6 @@
 	"github.com/google/go-cmp/cmp"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-	. "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
-)
-
 const RepositoriesTestRepo = "repository.git"
 
 // TestRepositories_List ensures the base case of listing
@@ -40,22 +37,22 @@
 	root, rm := Mktemp(t)
 	defer rm()
 
-	client := dubbo.New(dubbo.WithRepositoriesPath(root)) // Explicitly empty
+	client := dubbo2.New(dubbo2.WithRepositoriesPath(root)) // Explicitly empty
 
 	rr, err := client.Repositories().List()
 	if err != nil {
 		t.Fatal(err)
 	}
 	// Assert contains only the default repo
-	if len(rr) != 1 && rr[0] != dubbo.DefaultRepositoryName {
-		t.Fatalf("Expected repository list '[%v]', got %v", dubbo.DefaultRepositoryName, rr)
+	if len(rr) != 1 && rr[0] != dubbo2.DefaultRepositoryName {
+		t.Fatalf("Expected repository list '[%v]', got %v", dubbo2.DefaultRepositoryName, rr)
 	}
 }
 
 // TestRepositories_GetInvalid ensures that attempting to get an invalid repo
 // results in error.
 func TestRepositories_GetInvalid(t *testing.T) {
-	client := dubbo.New(dubbo.WithRepositoriesPath("testdata/repositories"))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath("testdata/repositories"))
 
 	// invalid should error
 	_, err := client.Repositories().Get("invalid")
@@ -66,7 +63,7 @@
 
 // TestRepositories_Get ensures a repository can be accessed by name.
 func TestRepositories_Get(t *testing.T) {
-	client := dubbo.New(dubbo.WithRepositoriesPath("testdata/repositories"))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath("testdata/repositories"))
 
 	// valid should not error
 	repo, err := client.Repositories().Get("customTemplateRepo")
@@ -87,14 +84,14 @@
 	root, rm := Mktemp(t)
 	defer rm()
 
-	client := dubbo.New(dubbo.WithRepositoriesPath(root))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
 
 	// Assert initially only the default is included
 	rr, err := client.Repositories().All()
 	if err != nil {
 		t.Fatal(err)
 	}
-	if len(rr) != 1 && rr[0].Name != dubbo.DefaultRepositoryName {
+	if len(rr) != 1 && rr[0].Name != dubbo2.DefaultRepositoryName {
 		t.Fatalf("Expected initial repo list to be only the default.  Got %v", rr)
 	}
 
@@ -112,7 +109,7 @@
 
 	// Assert it now includes both builtin and extended
 	if len(repositories) != 2 ||
-		repositories[0].Name != dubbo.DefaultRepositoryName ||
+		repositories[0].Name != dubbo2.DefaultRepositoryName ||
 		repositories[1].Name != "repository" {
 		t.Fatal("Repositories list does not pass shallow repository membership check")
 	}
@@ -126,7 +123,7 @@
 
 	// Instantiate the client using the current temp directory as the
 	// repositories' root location.
-	client := dubbo.New(dubbo.WithRepositoriesPath(root))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
 
 	// Add the repository, explicitly specifying a name.  See other tests for
 	// defaulting from repository names and manifest-defined name.
@@ -162,7 +159,7 @@
 	root, rm := Mktemp(t)
 	defer rm()
 
-	client := dubbo.New(dubbo.WithRepositoriesPath(root))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
 
 	name, err := client.Repositories().Add("", uri)
 	if err != nil {
@@ -197,7 +194,7 @@
 	root, rm := Mktemp(t)
 	defer rm()
 
-	client := dubbo.New(dubbo.WithRepositoriesPath(root))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
 
 	name, err := client.Repositories().Add("", uri)
 	if err != nil {
@@ -230,7 +227,7 @@
 
 	// Instantiate the client using the current temp directory as the
 	// repositories' root location.
-	client := dubbo.New(dubbo.WithRepositoriesPath(root))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
 
 	// Add twice.
 	name := "example"
@@ -264,7 +261,7 @@
 
 	// Instantiate the client using the current temp directory as the
 	// repositories' root location.
-	client := dubbo.New(dubbo.WithRepositoriesPath(root))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
 
 	// Add and Rename
 	if _, err := client.Repositories().Add("foo", uri); err != nil {
@@ -298,7 +295,7 @@
 
 	// Instantiate the client using the current temp directory as the
 	// repositories' root location.
-	client := dubbo.New(dubbo.WithRepositoriesPath(root))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
 
 	// Add and Remove
 	name := "example"
@@ -331,7 +328,7 @@
 	root, rm := Mktemp(t)
 	defer rm()
 
-	client := dubbo.New(dubbo.WithRepositoriesPath(root))
+	client := dubbo2.New(dubbo2.WithRepositoriesPath(root))
 
 	// Add the test repo
 	_, err := client.Repositories().Add("newrepo", uri)
@@ -359,7 +356,7 @@
 // first moving the defaulting path logic from CLI into the client lib.
 func TestRepositories_Missing(t *testing.T) {
 	// Client with no repositories path defined.
-	client := dubbo.New()
+	client := dubbo2.New()
 
 	// Get all repositories
 	_, err := client.Repositories().All()
diff --git a/dubboctl/internal/dubbo/repository.go b/operator/dubbo/repository.go
similarity index 99%
rename from dubboctl/internal/dubbo/repository.go
rename to operator/dubbo/repository.go
index c32f282..9080f7f 100644
--- a/dubboctl/internal/dubbo/repository.go
+++ b/operator/dubbo/repository.go
@@ -20,6 +20,7 @@
 import (
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
 	"net/url"
 	"os"
 	"path"
@@ -37,10 +38,6 @@
 	"gopkg.in/yaml.v3"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
 const (
 	repositoryManifest = "manifest.yaml"
 	templateManifest   = "manifest.yaml"
diff --git a/dubboctl/internal/dubbo/template.go b/operator/dubbo/template.go
similarity index 96%
rename from dubboctl/internal/dubbo/template.go
rename to operator/dubbo/template.go
index 3f1ecfd..f331551 100644
--- a/dubboctl/internal/dubbo/template.go
+++ b/operator/dubbo/template.go
@@ -19,13 +19,10 @@
 
 import (
 	"context"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
 	"path"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
 // Template is a function project template.
 // It can be used to instantiate new function project.
 type Template interface {
diff --git a/dubboctl/internal/dubbo/templates.go b/operator/dubbo/templates.go
similarity index 97%
rename from dubboctl/internal/dubbo/templates.go
rename to operator/dubbo/templates.go
index 8f4e7bb..5cf5535 100644
--- a/dubboctl/internal/dubbo/templates.go
+++ b/operator/dubbo/templates.go
@@ -19,13 +19,10 @@
 
 import (
 	"context"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"strings"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 // Templates Manager
 type Templates struct {
 	client *Client
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/operator/dubbo/templates_embedded.go
similarity index 89%
rename from dubboctl/internal/dubbo/templates_embedded.go
rename to operator/dubbo/templates_embedded.go
index 987f638..ef2a5f5 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/operator/dubbo/templates_embedded.go
@@ -20,11 +20,8 @@
 import (
 	"archive/zip"
 	"bytes"
-)
-
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/generated"
 )
 
 //go:generate go run ../../generated/templates/generate.go
diff --git a/dubboctl/internal/dubbo/templates_test.go b/operator/dubbo/templates_test.go
similarity index 97%
rename from dubboctl/internal/dubbo/templates_test.go
rename to operator/dubbo/templates_test.go
index eef7ded..6264633 100644
--- a/dubboctl/internal/dubbo/templates_test.go
+++ b/operator/dubbo/templates_test.go
@@ -29,8 +29,8 @@
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-	. "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
+	. "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
 )
 
 // TestTemplates_List ensures that all templates are listed taking into account
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/customRuntime/.gitinclude b/operator/dubbo/testdata/repositories/customTemplateRepo/customRuntime/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/customRuntime/.gitinclude
rename to operator/dubbo/testdata/repositories/customTemplateRepo/customRuntime/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/customRuntime/customTemplate/custom.impl b/operator/dubbo/testdata/repositories/customTemplateRepo/customRuntime/customTemplate/custom.impl
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/customRuntime/customTemplate/custom.impl
rename to operator/dubbo/testdata/repositories/customTemplateRepo/customRuntime/customTemplate/custom.impl
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/go/customTemplate/custom.go b/operator/dubbo/testdata/repositories/customTemplateRepo/go/customTemplate/custom.go
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/go/customTemplate/custom.go
rename to operator/dubbo/testdata/repositories/customTemplateRepo/go/customTemplate/custom.go
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/node/json/index.js b/operator/dubbo/testdata/repositories/customTemplateRepo/node/json/index.js
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/node/json/index.js
rename to operator/dubbo/testdata/repositories/customTemplateRepo/node/json/index.js
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/node/json/json.js b/operator/dubbo/testdata/repositories/customTemplateRepo/node/json/json.js
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/node/json/json.js
rename to operator/dubbo/testdata/repositories/customTemplateRepo/node/json/json.js
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tpla/customtpl.txt b/operator/dubbo/testdata/repositories/customTemplateRepo/test/tpla/customtpl.txt
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tpla/customtpl.txt
rename to operator/dubbo/testdata/repositories/customTemplateRepo/test/tpla/customtpl.txt
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tplb/executable.sh b/operator/dubbo/testdata/repositories/customTemplateRepo/test/tplb/executable.sh
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tplb/executable.sh
rename to operator/dubbo/testdata/repositories/customTemplateRepo/test/tplb/executable.sh
diff --git a/dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tplc/customtpl.txt b/operator/dubbo/testdata/repositories/customTemplateRepo/test/tplc/customtpl.txt
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repositories/customTemplateRepo/test/tplc/customtpl.txt
rename to operator/dubbo/testdata/repositories/customTemplateRepo/test/tplc/customtpl.txt
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/COMMIT_EDITMSG b/operator/dubbo/testdata/repository-a.git/COMMIT_EDITMSG
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/COMMIT_EDITMSG
rename to operator/dubbo/testdata/repository-a.git/COMMIT_EDITMSG
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/HEAD b/operator/dubbo/testdata/repository-a.git/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/HEAD
rename to operator/dubbo/testdata/repository-a.git/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/config b/operator/dubbo/testdata/repository-a.git/config
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/config
rename to operator/dubbo/testdata/repository-a.git/config
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/description b/operator/dubbo/testdata/repository-a.git/description
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/description
rename to operator/dubbo/testdata/repository-a.git/description
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/hooks/.gitinclude b/operator/dubbo/testdata/repository-a.git/hooks/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/hooks/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/hooks/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/index b/operator/dubbo/testdata/repository-a.git/index
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/index
rename to operator/dubbo/testdata/repository-a.git/index
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/info/exclude b/operator/dubbo/testdata/repository-a.git/info/exclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/info/exclude
rename to operator/dubbo/testdata/repository-a.git/info/exclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/logs/HEAD b/operator/dubbo/testdata/repository-a.git/logs/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/logs/HEAD
rename to operator/dubbo/testdata/repository-a.git/logs/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/logs/refs/heads/main b/operator/dubbo/testdata/repository-a.git/logs/refs/heads/main
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/logs/refs/heads/main
rename to operator/dubbo/testdata/repository-a.git/logs/refs/heads/main
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/logs/refs/remotes/origin/HEAD b/operator/dubbo/testdata/repository-a.git/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/logs/refs/remotes/origin/HEAD
rename to operator/dubbo/testdata/repository-a.git/logs/refs/remotes/origin/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/32/bad9e3775e80ae9b7483acf6d7630bf8b945e9 b/operator/dubbo/testdata/repository-a.git/objects/32/bad9e3775e80ae9b7483acf6d7630bf8b945e9
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/32/bad9e3775e80ae9b7483acf6d7630bf8b945e9
rename to operator/dubbo/testdata/repository-a.git/objects/32/bad9e3775e80ae9b7483acf6d7630bf8b945e9
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/88/3ff23f61018546cac0c302c5c75e3c65ed832d b/operator/dubbo/testdata/repository-a.git/objects/88/3ff23f61018546cac0c302c5c75e3c65ed832d
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/88/3ff23f61018546cac0c302c5c75e3c65ed832d
rename to operator/dubbo/testdata/repository-a.git/objects/88/3ff23f61018546cac0c302c5c75e3c65ed832d
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/91/f1295d025a69a8ae87296ea5fda680672275d9 b/operator/dubbo/testdata/repository-a.git/objects/91/f1295d025a69a8ae87296ea5fda680672275d9
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/91/f1295d025a69a8ae87296ea5fda680672275d9
rename to operator/dubbo/testdata/repository-a.git/objects/91/f1295d025a69a8ae87296ea5fda680672275d9
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/af/e663f9ef928be2b5cb93f836c0905e60f09ab2 b/operator/dubbo/testdata/repository-a.git/objects/af/e663f9ef928be2b5cb93f836c0905e60f09ab2
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/af/e663f9ef928be2b5cb93f836c0905e60f09ab2
rename to operator/dubbo/testdata/repository-a.git/objects/af/e663f9ef928be2b5cb93f836c0905e60f09ab2
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/d5/c0914f5e90ce1ccc3ee90e44ab610f466c774f b/operator/dubbo/testdata/repository-a.git/objects/d5/c0914f5e90ce1ccc3ee90e44ab610f466c774f
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/d5/c0914f5e90ce1ccc3ee90e44ab610f466c774f
rename to operator/dubbo/testdata/repository-a.git/objects/d5/c0914f5e90ce1ccc3ee90e44ab610f466c774f
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/dd/c6c5f6af77cfd92693c2b5b910b22a26379d01 b/operator/dubbo/testdata/repository-a.git/objects/dd/c6c5f6af77cfd92693c2b5b910b22a26379d01
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/dd/c6c5f6af77cfd92693c2b5b910b22a26379d01
rename to operator/dubbo/testdata/repository-a.git/objects/dd/c6c5f6af77cfd92693c2b5b910b22a26379d01
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/e7/ca8dffd312a48aac1f2aadd64a13ac34e021b9 b/operator/dubbo/testdata/repository-a.git/objects/e7/ca8dffd312a48aac1f2aadd64a13ac34e021b9
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/e7/ca8dffd312a48aac1f2aadd64a13ac34e021b9
rename to operator/dubbo/testdata/repository-a.git/objects/e7/ca8dffd312a48aac1f2aadd64a13ac34e021b9
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/ee/f72a905e2866c0e4b4d13cb14bf118c8e8aa0b b/operator/dubbo/testdata/repository-a.git/objects/ee/f72a905e2866c0e4b4d13cb14bf118c8e8aa0b
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/ee/f72a905e2866c0e4b4d13cb14bf118c8e8aa0b
rename to operator/dubbo/testdata/repository-a.git/objects/ee/f72a905e2866c0e4b4d13cb14bf118c8e8aa0b
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/ef/1e6c8ed28e25dec915e8cd7479dacf671665ed b/operator/dubbo/testdata/repository-a.git/objects/ef/1e6c8ed28e25dec915e8cd7479dacf671665ed
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/ef/1e6c8ed28e25dec915e8cd7479dacf671665ed
rename to operator/dubbo/testdata/repository-a.git/objects/ef/1e6c8ed28e25dec915e8cd7479dacf671665ed
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/info/.gitinclude b/operator/dubbo/testdata/repository-a.git/objects/info/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/info/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/objects/info/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/.gitinclude b/operator/dubbo/testdata/repository-a.git/objects/pack/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/objects/pack/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.idx b/operator/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.idx
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.idx
rename to operator/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.idx
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.pack b/operator/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.pack
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.pack
rename to operator/dubbo/testdata/repository-a.git/objects/pack/pack-3cc26077b1d72f3ffb9484d7baca188936db8c5e.pack
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/packed-refs b/operator/dubbo/testdata/repository-a.git/packed-refs
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/packed-refs
rename to operator/dubbo/testdata/repository-a.git/packed-refs
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/refs/heads/.gitinclude b/operator/dubbo/testdata/repository-a.git/refs/heads/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/refs/heads/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/refs/heads/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/refs/heads/main b/operator/dubbo/testdata/repository-a.git/refs/heads/main
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/refs/heads/main
rename to operator/dubbo/testdata/repository-a.git/refs/heads/main
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/refs/remotes/origin/HEAD b/operator/dubbo/testdata/repository-a.git/refs/remotes/origin/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/refs/remotes/origin/HEAD
rename to operator/dubbo/testdata/repository-a.git/refs/remotes/origin/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository-a.git/refs/tags/.gitinclude b/operator/dubbo/testdata/repository-a.git/refs/tags/.gitinclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository-a.git/refs/tags/.gitinclude
rename to operator/dubbo/testdata/repository-a.git/refs/tags/.gitinclude
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/HEAD b/operator/dubbo/testdata/repository.git/HEAD
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/HEAD
rename to operator/dubbo/testdata/repository.git/HEAD
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/config b/operator/dubbo/testdata/repository.git/config
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/config
rename to operator/dubbo/testdata/repository.git/config
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/description b/operator/dubbo/testdata/repository.git/description
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/description
rename to operator/dubbo/testdata/repository.git/description
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/applypatch-msg.sample b/operator/dubbo/testdata/repository.git/hooks/applypatch-msg.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/applypatch-msg.sample
rename to operator/dubbo/testdata/repository.git/hooks/applypatch-msg.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/commit-msg.sample b/operator/dubbo/testdata/repository.git/hooks/commit-msg.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/commit-msg.sample
rename to operator/dubbo/testdata/repository.git/hooks/commit-msg.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/fsmonitor-watchman.sample b/operator/dubbo/testdata/repository.git/hooks/fsmonitor-watchman.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/fsmonitor-watchman.sample
rename to operator/dubbo/testdata/repository.git/hooks/fsmonitor-watchman.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/post-update.sample b/operator/dubbo/testdata/repository.git/hooks/post-update.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/post-update.sample
rename to operator/dubbo/testdata/repository.git/hooks/post-update.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-applypatch.sample b/operator/dubbo/testdata/repository.git/hooks/pre-applypatch.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-applypatch.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-applypatch.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-commit.sample b/operator/dubbo/testdata/repository.git/hooks/pre-commit.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-commit.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-commit.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-merge-commit.sample b/operator/dubbo/testdata/repository.git/hooks/pre-merge-commit.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-merge-commit.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-merge-commit.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-push.sample b/operator/dubbo/testdata/repository.git/hooks/pre-push.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-push.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-push.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-rebase.sample b/operator/dubbo/testdata/repository.git/hooks/pre-rebase.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-rebase.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-rebase.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-receive.sample b/operator/dubbo/testdata/repository.git/hooks/pre-receive.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/pre-receive.sample
rename to operator/dubbo/testdata/repository.git/hooks/pre-receive.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/prepare-commit-msg.sample b/operator/dubbo/testdata/repository.git/hooks/prepare-commit-msg.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/prepare-commit-msg.sample
rename to operator/dubbo/testdata/repository.git/hooks/prepare-commit-msg.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/push-to-checkout.sample b/operator/dubbo/testdata/repository.git/hooks/push-to-checkout.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/push-to-checkout.sample
rename to operator/dubbo/testdata/repository.git/hooks/push-to-checkout.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/hooks/update.sample b/operator/dubbo/testdata/repository.git/hooks/update.sample
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/hooks/update.sample
rename to operator/dubbo/testdata/repository.git/hooks/update.sample
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/info/exclude b/operator/dubbo/testdata/repository.git/info/exclude
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/info/exclude
rename to operator/dubbo/testdata/repository.git/info/exclude
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/00/9641334a89d30963a68442a132e5f8ef389ba8 b/operator/dubbo/testdata/repository.git/objects/00/9641334a89d30963a68442a132e5f8ef389ba8
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/00/9641334a89d30963a68442a132e5f8ef389ba8
rename to operator/dubbo/testdata/repository.git/objects/00/9641334a89d30963a68442a132e5f8ef389ba8
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/27/d75d0e58009a59e1ff8a52a3297e1e0574138b b/operator/dubbo/testdata/repository.git/objects/27/d75d0e58009a59e1ff8a52a3297e1e0574138b
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/27/d75d0e58009a59e1ff8a52a3297e1e0574138b
rename to operator/dubbo/testdata/repository.git/objects/27/d75d0e58009a59e1ff8a52a3297e1e0574138b
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/43/0739f6d9930f74a6aef1a410f0f0727d171cef b/operator/dubbo/testdata/repository.git/objects/43/0739f6d9930f74a6aef1a410f0f0727d171cef
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/43/0739f6d9930f74a6aef1a410f0f0727d171cef
rename to operator/dubbo/testdata/repository.git/objects/43/0739f6d9930f74a6aef1a410f0f0727d171cef
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/44/788c5d0e56a8bb819c56a307b6c8de4045e020 b/operator/dubbo/testdata/repository.git/objects/44/788c5d0e56a8bb819c56a307b6c8de4045e020
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/44/788c5d0e56a8bb819c56a307b6c8de4045e020
rename to operator/dubbo/testdata/repository.git/objects/44/788c5d0e56a8bb819c56a307b6c8de4045e020
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/93/bd2fa63bb19bf11231dde8709672bd1428e57c b/operator/dubbo/testdata/repository.git/objects/93/bd2fa63bb19bf11231dde8709672bd1428e57c
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/93/bd2fa63bb19bf11231dde8709672bd1428e57c
rename to operator/dubbo/testdata/repository.git/objects/93/bd2fa63bb19bf11231dde8709672bd1428e57c
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/b2/7e306a552e80fcae0bfa959bd6e8652de70c69 b/operator/dubbo/testdata/repository.git/objects/b2/7e306a552e80fcae0bfa959bd6e8652de70c69
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/b2/7e306a552e80fcae0bfa959bd6e8652de70c69
rename to operator/dubbo/testdata/repository.git/objects/b2/7e306a552e80fcae0bfa959bd6e8652de70c69
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/b7/5832d0a4a4bb0409cca573b2c97b017d088f90 b/operator/dubbo/testdata/repository.git/objects/b7/5832d0a4a4bb0409cca573b2c97b017d088f90
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/b7/5832d0a4a4bb0409cca573b2c97b017d088f90
rename to operator/dubbo/testdata/repository.git/objects/b7/5832d0a4a4bb0409cca573b2c97b017d088f90
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/c8/fdee8e0a172c9e1544bc127b2bfb35829ffe1f b/operator/dubbo/testdata/repository.git/objects/c8/fdee8e0a172c9e1544bc127b2bfb35829ffe1f
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/c8/fdee8e0a172c9e1544bc127b2bfb35829ffe1f
rename to operator/dubbo/testdata/repository.git/objects/c8/fdee8e0a172c9e1544bc127b2bfb35829ffe1f
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/operator/dubbo/testdata/repository.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to operator/dubbo/testdata/repository.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/refs/heads/main b/operator/dubbo/testdata/repository.git/refs/heads/main
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/refs/heads/main
rename to operator/dubbo/testdata/repository.git/refs/heads/main
diff --git a/dubboctl/internal/dubbo/testdata/repository.git/refs/heads/master b/operator/dubbo/testdata/repository.git/refs/heads/master
similarity index 100%
rename from dubboctl/internal/dubbo/testdata/repository.git/refs/heads/master
rename to operator/dubbo/testdata/repository.git/refs/heads/master
diff --git a/dubboctl/internal/dubbo/validate.go b/operator/dubbo/validate.go
similarity index 100%
rename from dubboctl/internal/dubbo/validate.go
rename to operator/dubbo/validate.go
diff --git a/dubboctl/internal/http/transport.go b/operator/http/transport.go
similarity index 100%
rename from dubboctl/internal/http/transport.go
rename to operator/http/transport.go
diff --git a/dubboctl/internal/manifest/common.go b/operator/manifest/common.go
similarity index 91%
rename from dubboctl/internal/manifest/common.go
rename to operator/manifest/common.go
index e5326c2..9234cf6 100644
--- a/dubboctl/internal/manifest/common.go
+++ b/operator/manifest/common.go
@@ -17,6 +17,9 @@
 
 import (
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+	util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"path"
 	"strings"
 )
@@ -25,12 +28,6 @@
 	"sigs.k8s.io/yaml"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 func ReadOverlayProfileYaml(profilePath string, profile string) (string, error) {
 	filePath := path.Join(profilePath, profile+".yaml")
 	defaultPath := path.Join(profilePath, "default.yaml")
@@ -77,7 +74,7 @@
 			return "", err
 		}
 		// todo:// inspect that this file only contains one CR
-		output, err = util.OverlayYAML(output, string(file))
+		output, err = util2.OverlayYAML(output, string(file))
 		if err != nil {
 			return "", err
 		}
@@ -92,7 +89,7 @@
 	}
 	for _, setFlag := range setFlags {
 		key, val := SplitSetFlag(setFlag)
-		pathCtx, _, err := GetPathContext(baseMap, util.PathFromString(key), true)
+		pathCtx, _, err := GetPathContext(baseMap, util2.PathFromString(key), true)
 		if err != nil {
 			return "", err
 		}
diff --git a/dubboctl/internal/manifest/common_test.go b/operator/manifest/common_test.go
similarity index 100%
rename from dubboctl/internal/manifest/common_test.go
rename to operator/manifest/common_test.go
diff --git a/dubboctl/internal/manifest/render/render.go b/operator/manifest/render/render.go
similarity index 97%
rename from dubboctl/internal/manifest/render/render.go
rename to operator/manifest/render/render.go
index d6a69f2..5d7673f 100644
--- a/dubboctl/internal/manifest/render/render.go
+++ b/operator/manifest/render/render.go
@@ -18,6 +18,9 @@
 import (
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/manifest"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"io/fs"
 	"net/url"
 	"os"
@@ -41,12 +44,6 @@
 	"sigs.k8s.io/yaml"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 const (
 	YAMLSeparator       = "\n---\n"
 	NotesFileNameSuffix = ".txt"
diff --git a/dubboctl/internal/manifest/render/render_test.go b/operator/manifest/render/render_test.go
similarity index 99%
rename from dubboctl/internal/manifest/render/render_test.go
rename to operator/manifest/render/render_test.go
index 2a6a63d..64fd04c 100644
--- a/dubboctl/internal/manifest/render/render_test.go
+++ b/operator/manifest/render/render_test.go
@@ -16,14 +16,11 @@
 package render
 
 import (
+	"github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
 	"os"
 	"testing"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-)
-
 const (
 	TestDir       string = "testchart"
 	TestName      string = "nginx"
diff --git a/dubboctl/internal/manifest/render/testchart/.helmignore b/operator/manifest/render/testchart/.helmignore
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/.helmignore
rename to operator/manifest/render/testchart/.helmignore
diff --git a/dubboctl/internal/manifest/render/testchart/Chart.yaml b/operator/manifest/render/testchart/Chart.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/Chart.yaml
rename to operator/manifest/render/testchart/Chart.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/NOTES.txt b/operator/manifest/render/testchart/templates/NOTES.txt
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/NOTES.txt
rename to operator/manifest/render/testchart/templates/NOTES.txt
diff --git a/dubboctl/internal/manifest/render/testchart/templates/_helpers.tpl b/operator/manifest/render/testchart/templates/_helpers.tpl
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/_helpers.tpl
rename to operator/manifest/render/testchart/templates/_helpers.tpl
diff --git a/dubboctl/internal/manifest/render/testchart/templates/deployment.yaml b/operator/manifest/render/testchart/templates/deployment.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/deployment.yaml
rename to operator/manifest/render/testchart/templates/deployment.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/hpa.yaml b/operator/manifest/render/testchart/templates/hpa.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/hpa.yaml
rename to operator/manifest/render/testchart/templates/hpa.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/ingress.yaml b/operator/manifest/render/testchart/templates/ingress.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/ingress.yaml
rename to operator/manifest/render/testchart/templates/ingress.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/service.yaml b/operator/manifest/render/testchart/templates/service.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/service.yaml
rename to operator/manifest/render/testchart/templates/service.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/serviceaccount.yaml b/operator/manifest/render/testchart/templates/serviceaccount.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/serviceaccount.yaml
rename to operator/manifest/render/testchart/templates/serviceaccount.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/templates/tests/test-connection.yaml b/operator/manifest/render/testchart/templates/tests/test-connection.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/templates/tests/test-connection.yaml
rename to operator/manifest/render/testchart/templates/tests/test-connection.yaml
diff --git a/dubboctl/internal/manifest/render/testchart/values.yaml b/operator/manifest/render/testchart/values.yaml
similarity index 100%
rename from dubboctl/internal/manifest/render/testchart/values.yaml
rename to operator/manifest/render/testchart/values.yaml
diff --git a/dubboctl/internal/manifest/tree.go b/operator/manifest/tree.go
similarity index 89%
rename from dubboctl/internal/manifest/tree.go
rename to operator/manifest/tree.go
index f845b27..4446b9d 100644
--- a/dubboctl/internal/manifest/tree.go
+++ b/operator/manifest/tree.go
@@ -33,6 +33,7 @@
 	"encoding/json"
 	"errors"
 	"fmt"
+	util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"reflect"
 	"regexp"
 	"strconv"
@@ -45,10 +46,6 @@
 	yaml2 "sigs.k8s.io/yaml"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 // PathContext provides a means for traversing a tree towards the root.
 type PathContext struct {
 	// Parent in the Parent of this PathContext.
@@ -78,13 +75,13 @@
 // a malformed path.
 // It also creates a tree of PathContexts during the traversal so that Parent nodes can be updated if required. This is
 // required when (say) appending to a list, where the parent list itself must be updated.
-func GetPathContext(root interface{}, path util.Path, createMissing bool) (*PathContext, bool, error) {
+func GetPathContext(root interface{}, path util2.Path, createMissing bool) (*PathContext, bool, error) {
 	return getPathContext(&PathContext{Node: root}, path, path, createMissing)
 }
 
 // WritePathContext writes the given value to the Node in the given PathContext.
 func WritePathContext(nc *PathContext, value interface{}, merge bool) error {
-	if !util.IsValueNil(value) {
+	if !util2.IsValueNil(value) {
 		return setPathContext(nc, value, merge)
 	}
 
@@ -94,23 +91,23 @@
 
 	switch {
 	case isSliceOrPtrInterface(nc.Parent.Node):
-		if err := util.DeleteFromSlicePtr(nc.Parent.Node, nc.Parent.KeyToChild.(int)); err != nil {
+		if err := util2.DeleteFromSlicePtr(nc.Parent.Node, nc.Parent.KeyToChild.(int)); err != nil {
 			return err
 		}
 		if isMapOrInterface(nc.Parent.Parent.Node) {
-			return util.InsertIntoMap(nc.Parent.Parent.Node, nc.Parent.Parent.KeyToChild, nc.Parent.Node)
+			return util2.InsertIntoMap(nc.Parent.Parent.Node, nc.Parent.Parent.KeyToChild, nc.Parent.Node)
 		}
 		// TODO: The case of deleting a list.list.node element is not currently supported.
 		return fmt.Errorf("cannot delete path: unsupported parent.parent type %T for delete", nc.Parent.Parent.Node)
-	case util.IsMap(nc.Parent.Node):
-		return util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild)
+	case util2.IsMap(nc.Parent.Node):
+		return util2.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild)
 	default:
 	}
 	return fmt.Errorf("cannot delete path: unsupported parent type %T for delete", nc.Parent.Node)
 }
 
 // WriteNode writes value to the tree in root at the given path, creating any required missing internal nodes in path.
-func WriteNode(root interface{}, path util.Path, value interface{}) error {
+func WriteNode(root interface{}, path util2.Path, value interface{}) error {
 	pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true)
 	if err != nil {
 		return err
@@ -119,7 +116,7 @@
 }
 
 // MergeNode merges value to the tree in root at the given path, creating any required missing internal nodes in path.
-func MergeNode(root interface{}, path util.Path, value interface{}) error {
+func MergeNode(root interface{}, path util2.Path, value interface{}) error {
 	pc, _, err := getPathContext(&PathContext{Node: root}, path, path, true)
 	if err != nil {
 		return err
@@ -130,7 +127,7 @@
 // Find returns the value at path from the given tree, or false if the path does not exist.
 // It behaves differently from GetPathContext in that it never creates map entries at the leaf and does not provide
 // a way to mutate the parent of the found node.
-func Find(inputTree map[string]interface{}, path util.Path) (interface{}, bool, error) {
+func Find(inputTree map[string]interface{}, path util2.Path) (interface{}, bool, error) {
 	if len(path) == 0 {
 		return nil, false, fmt.Errorf("path is empty")
 	}
@@ -139,7 +136,7 @@
 }
 
 // Delete sets value at path of input untyped tree to nil
-func Delete(root map[string]interface{}, path util.Path) (bool, error) {
+func Delete(root map[string]interface{}, path util2.Path) (bool, error) {
 	pc, _, err := getPathContext(&PathContext{Node: root}, path, path, false)
 	if err != nil {
 		return false, err
@@ -149,7 +146,7 @@
 
 // getPathContext is the internal implementation of GetPathContext.
 // If createMissing is true, it creates any missing map (but NOT list) path entries in root.
-func getPathContext(nc *PathContext, fullPath, remainPath util.Path, createMissing bool) (*PathContext, bool, error) {
+func getPathContext(nc *PathContext, fullPath, remainPath util2.Path, createMissing bool) (*PathContext, bool, error) {
 	if len(remainPath) == 0 {
 		return nc, true, nil
 	}
@@ -159,7 +156,7 @@
 		if !createMissing {
 			return nil, false, fmt.Errorf("node %s is zero", pe)
 		}
-		if util.IsNPathElement(pe) || util.IsKVPathElement(pe) {
+		if util2.IsNPathElement(pe) || util2.IsKVPathElement(pe) {
 			nc.Node = []interface{}{}
 		} else {
 			nc.Node = make(map[string]interface{})
@@ -177,8 +174,8 @@
 	if lst, ok := ncNode.([]interface{}); ok {
 		// If the path element has the form [N], a list element is being selected by index. Return the element at index
 		// N if it exists.
-		if util.IsNPathElement(pe) {
-			idx, err := util.PathN(pe)
+		if util2.IsNPathElement(pe) {
+			idx, err := util2.PathN(pe)
 			if err != nil {
 				return nil, false, fmt.Errorf("path %s, index %s: %s", fullPath, pe, err)
 			}
@@ -205,7 +202,7 @@
 		for idx, le := range lst {
 			// non-leaf list, expect to match item by key:value.
 			if lm, ok := le.(map[interface{}]interface{}); ok {
-				k, v, err := util.PathKV(pe)
+				k, v, err := util2.PathKV(pe)
 				if err != nil {
 					return nil, false, fmt.Errorf("path %s: %s", fullPath, err)
 				}
@@ -226,7 +223,7 @@
 			// repeat of the block above for the case where tree unmarshals to map[string]interface{}. There doesn't
 			// seem to be a way to merge this case into the above block.
 			if lm, ok := le.(map[string]interface{}); ok {
-				k, v, err := util.PathKV(pe)
+				k, v, err := util2.PathKV(pe)
 				if err != nil {
 					return nil, false, fmt.Errorf("path %s: %s", fullPath, err)
 				}
@@ -245,7 +242,7 @@
 				continue
 			}
 			// leaf list, expect path element [V], match based on value V.
-			v, err := util.PathV(pe)
+			v, err := util2.PathV(pe)
 			if err != nil {
 				return nil, false, fmt.Errorf("path %s: %s", fullPath, err)
 			}
@@ -261,7 +258,7 @@
 		return nil, false, fmt.Errorf("path %s: element %s not found", fullPath, pe)
 	}
 
-	if util.IsMap(ncNode) {
+	if util2.IsMap(ncNode) {
 		var nn interface{}
 		if m, ok := ncNode.(map[interface{}]interface{}); ok {
 			nn, ok = m[pe]
@@ -284,7 +281,7 @@
 			if !ok {
 				// remainPath == 1 means the patch is creation of a new leaf.
 				if createMissing || len(remainPath) == 1 {
-					nextElementNPath := len(remainPath) > 1 && util.IsNPathElement(remainPath[1])
+					nextElementNPath := len(remainPath) > 1 && util2.IsNPathElement(remainPath[1])
 					if nextElementNPath {
 						m[pe] = make([]interface{}, 0)
 					} else {
@@ -302,7 +299,7 @@
 			Node:   nn,
 		}
 		// for slices, use the address so that the slice can be mutated.
-		if util.IsSlice(nn) {
+		if util2.IsSlice(nn) {
 			npc.Node = &nn
 		}
 		nc.KeyToChild = pe
@@ -395,12 +392,12 @@
 		} else {
 			// For map passed as string type, the root is the new key.
 			if mapFromString {
-				if err := util.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild); err != nil {
+				if err := util2.DeleteFromMap(nc.Parent.Node, nc.Parent.KeyToChild); err != nil {
 					return false, err
 				}
 				vm := vv.(map[string]interface{})
 				newKey := getTreeRoot(vm)
-				return false, util.InsertIntoMap(nc.Parent.Node, newKey, vm[newKey])
+				return false, util2.InsertIntoMap(nc.Parent.Node, newKey, vm[newKey])
 			}
 			parentNode[key] = vv
 			nc.Node = vv
@@ -419,30 +416,30 @@
 
 // mergeConditional returns a merge of newVal and originalVal if merge is true, otherwise it returns newVal.
 func mergeConditional(newVal, originalVal interface{}, merge bool) (interface{}, error) {
-	if !merge || util.IsValueNilOrDefault(originalVal) {
+	if !merge || util2.IsValueNilOrDefault(originalVal) {
 		return newVal, nil
 	}
 	newS, err := yaml.Marshal(newVal)
 	if err != nil {
 		return nil, err
 	}
-	if util.IsYAMLEmpty(string(newS)) {
+	if util2.IsYAMLEmpty(string(newS)) {
 		return originalVal, nil
 	}
 	originalS, err := yaml.Marshal(originalVal)
 	if err != nil {
 		return nil, err
 	}
-	if util.IsYAMLEmpty(string(originalS)) {
+	if util2.IsYAMLEmpty(string(originalS)) {
 		return newVal, nil
 	}
 
-	mergedS, err := util.OverlayYAML(string(originalS), string(newS))
+	mergedS, err := util2.OverlayYAML(string(originalS), string(newS))
 	if err != nil {
 		return nil, err
 	}
 
-	if util.IsMap(originalVal) {
+	if util2.IsMap(originalVal) {
 		// For JSON compatibility
 		out := make(map[string]interface{})
 		if err := yaml.Unmarshal([]byte(mergedS), &out); err != nil {
@@ -459,7 +456,7 @@
 }
 
 // find returns the value at path from the given tree, or false if the path does not exist.
-func find(treeNode interface{}, path util.Path) (interface{}, bool) {
+func find(treeNode interface{}, path util2.Path) (interface{}, bool) {
 	if len(path) == 0 || treeNode == nil {
 		return nil, false
 	}
diff --git a/dubboctl/internal/manifest/util.go b/operator/manifest/util.go
similarity index 100%
rename from dubboctl/internal/manifest/util.go
rename to operator/manifest/util.go
diff --git a/dubboctl/internal/mock/builder.go b/operator/mock/builder.go
similarity index 93%
rename from dubboctl/internal/mock/builder.go
rename to operator/mock/builder.go
index 1f7bd42..d571b4b 100644
--- a/dubboctl/internal/mock/builder.go
+++ b/operator/mock/builder.go
@@ -17,10 +17,7 @@
 
 import (
 	"context"
-)
-
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
 )
 
 type Builder struct {
diff --git a/dubboctl/internal/mock/client.go b/operator/mock/client.go
similarity index 96%
rename from dubboctl/internal/mock/client.go
rename to operator/mock/client.go
index 9a5627e..d7aa53c 100644
--- a/dubboctl/internal/mock/client.go
+++ b/operator/mock/client.go
@@ -18,7 +18,7 @@
 package mock
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
 )
 
 type Client struct {
diff --git a/dubboctl/internal/mock/pusher.go b/operator/mock/pusher.go
similarity index 93%
rename from dubboctl/internal/mock/pusher.go
rename to operator/mock/pusher.go
index a383633..ce64082 100644
--- a/dubboctl/internal/mock/pusher.go
+++ b/operator/mock/pusher.go
@@ -17,10 +17,7 @@
 
 import (
 	"context"
-)
-
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
 )
 
 type Pusher struct {
diff --git a/dubboctl/internal/apis/dubbo.apache.org/v1alpha1/types.go b/operator/pkg/apis/dubbo.apache.org/v1alpha1/types.go
similarity index 100%
rename from dubboctl/internal/apis/dubbo.apache.org/v1alpha1/types.go
rename to operator/pkg/apis/dubbo.apache.org/v1alpha1/types.go
diff --git a/dubboctl/internal/builders/builders.go b/operator/pkg/builders/builders.go
similarity index 97%
rename from dubboctl/internal/builders/builders.go
rename to operator/pkg/builders/builders.go
index 4d33cf9..b782990 100644
--- a/dubboctl/internal/builders/builders.go
+++ b/operator/pkg/builders/builders.go
@@ -19,14 +19,11 @@
 
 import (
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
 	"strconv"
 	"strings"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
 const (
 	Pack = "pack"
 )
diff --git a/dubboctl/internal/builders/builders_test.go b/operator/pkg/builders/builders_test.go
similarity index 94%
rename from dubboctl/internal/builders/builders_test.go
rename to operator/pkg/builders/builders_test.go
index 9313172..00597bf 100644
--- a/dubboctl/internal/builders/builders_test.go
+++ b/operator/pkg/builders/builders_test.go
@@ -19,15 +19,12 @@
 
 import (
 	"errors"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/builders"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/builders/pack"
 	"testing"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/builders"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/pack"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
 // TestImage_Named ensures that a builder image is returned when
 // it exists on the function for a given builder, no defaults.
 func TestImage_Named(t *testing.T) {
diff --git a/dubboctl/internal/builders/dockerfile/build.go b/operator/pkg/builders/dockerfile/build.go
similarity index 92%
rename from dubboctl/internal/builders/dockerfile/build.go
rename to operator/pkg/builders/dockerfile/build.go
index 5657d0f..46bdbfc 100644
--- a/dubboctl/internal/builders/dockerfile/build.go
+++ b/operator/pkg/builders/dockerfile/build.go
@@ -17,6 +17,8 @@
 
 import (
 	"context"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
 	"os"
 )
 
@@ -30,11 +32,6 @@
 	"github.com/moby/term"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
 type Builder struct{}
 
 func (b Builder) Build(ctx context.Context, f *dubbo.Dubbo) error {
diff --git a/dubboctl/internal/builders/pack/build.go b/operator/pkg/builders/pack/build.go
similarity index 96%
rename from dubboctl/internal/builders/pack/build.go
rename to operator/pkg/builders/pack/build.go
index 2632ede..d2664e5 100644
--- a/dubboctl/internal/builders/pack/build.go
+++ b/operator/pkg/builders/pack/build.go
@@ -20,6 +20,10 @@
 	"context"
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/builders"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/builders/pack/mirror"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
 	"io"
 	"runtime"
 	"strings"
@@ -39,13 +43,6 @@
 	"github.com/heroku/color"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/builders"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/builders/pack/mirror"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
 // DefaultName when no WithName option is provided to NewBuilder
 const DefaultName = builders.Pack
 
diff --git a/dubboctl/internal/builders/pack/build_test.go b/operator/pkg/builders/pack/build_test.go
similarity index 96%
rename from dubboctl/internal/builders/pack/build_test.go
rename to operator/pkg/builders/pack/build_test.go
index 9622172..c5539a0 100644
--- a/dubboctl/internal/builders/pack/build_test.go
+++ b/operator/pkg/builders/pack/build_test.go
@@ -17,6 +17,8 @@
 
 import (
 	"context"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/builders"
 	"reflect"
 	"testing"
 )
@@ -25,11 +27,6 @@
 	pack "github.com/buildpacks/pack/pkg/client"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/builders"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
 // TestBuild_BuilderImageUntrusted ensures that only known builder images
 // are to be considered trusted.
 func TestBuild_BuilderImageUntrusted(t *testing.T) {
diff --git a/dubboctl/internal/builders/pack/mirror/error.go b/operator/pkg/builders/pack/mirror/error.go
similarity index 100%
rename from dubboctl/internal/builders/pack/mirror/error.go
rename to operator/pkg/builders/pack/mirror/error.go
diff --git a/dubboctl/internal/builders/pack/mirror/mirror.go b/operator/pkg/builders/pack/mirror/mirror.go
similarity index 100%
rename from dubboctl/internal/builders/pack/mirror/mirror.go
rename to operator/pkg/builders/pack/mirror/mirror.go
diff --git a/dubboctl/internal/docker/creds/credentials.go b/operator/pkg/docker/creds/credentials.go
similarity index 99%
rename from dubboctl/internal/docker/creds/credentials.go
rename to operator/pkg/docker/creds/credentials.go
index 01a44ad..a4d90a0 100644
--- a/dubboctl/internal/docker/creds/credentials.go
+++ b/operator/pkg/docker/creds/credentials.go
@@ -20,6 +20,7 @@
 	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
 	"net"
 	"net/http"
 	"net/url"
@@ -42,10 +43,6 @@
 	"github.com/google/go-containerregistry/pkg/v1/remote/transport"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-)
-
 type CredentialsCallback func(registry string) (docker.Credentials, error)
 
 var ErrUnauthorized = errors.New("bad credentials")
diff --git a/dubboctl/internal/docker/creds/credentials_test.go b/operator/pkg/docker/creds/credentials_test.go
similarity index 98%
rename from dubboctl/internal/docker/creds/credentials_test.go
rename to operator/pkg/docker/creds/credentials_test.go
index fb08fde..19b46d9 100644
--- a/dubboctl/internal/docker/creds/credentials_test.go
+++ b/operator/pkg/docker/creds/credentials_test.go
@@ -26,6 +26,9 @@
 	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker/creds"
+	. "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
 	"io"
 	"math/big"
 	"net"
@@ -43,12 +46,6 @@
 	"github.com/docker/docker-credential-helpers/credentials"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker/creds"
-	. "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
-)
-
 func Test_registryEquals(t *testing.T) {
 	tests := []struct {
 		name string
diff --git a/dubboctl/internal/docker/docker_client.go b/operator/pkg/docker/docker_client.go
similarity index 94%
rename from dubboctl/internal/docker/docker_client.go
rename to operator/pkg/docker/docker_client.go
index 16345f6..3b9ae3d 100644
--- a/dubboctl/internal/docker/docker_client.go
+++ b/operator/pkg/docker/docker_client.go
@@ -19,6 +19,7 @@
 	"crypto/tls"
 	"crypto/x509"
 	"errors"
+	ssh2 "github.com/apache/dubbo-kubernetes/operator/pkg/ssh"
 	"io"
 	"net"
 	"net/http"
@@ -38,10 +39,6 @@
 	"github.com/docker/go-connections/tlsconfig"
 )
 
-import (
-	fnssh "github.com/apache/dubbo-kubernetes/dubboctl/internal/ssh"
-)
-
 var ErrNoDocker = errors.New("docker API not available")
 
 // NewClient creates a new docker client.
@@ -57,7 +54,7 @@
 
 	dockerHost := os.Getenv("DOCKER_HOST")
 	dockerHostSSHIdentity := os.Getenv("DOCKER_HOST_SSH_IDENTITY")
-	hostKeyCallback := fnssh.NewHostKeyCbk()
+	hostKeyCallback := ssh2.NewHostKeyCbk()
 
 	if dockerHost == "" {
 		_url, err = url.Parse(defaultHost)
@@ -109,14 +106,14 @@
 		return
 	}
 
-	credentialsConfig := fnssh.Config{
+	credentialsConfig := ssh2.Config{
 		Identity:           dockerHostSSHIdentity,
 		PassPhrase:         os.Getenv("DOCKER_HOST_SSH_IDENTITY_PASSPHRASE"),
-		PasswordCallback:   fnssh.NewPasswordCbk(),
-		PassPhraseCallback: fnssh.NewPassPhraseCbk(),
+		PasswordCallback:   ssh2.NewPasswordCbk(),
+		PassPhraseCallback: ssh2.NewPassPhraseCbk(),
 		HostKeyCallback:    hostKeyCallback,
 	}
-	contextDialer, dockerHostInRemote, err := fnssh.NewDialContext(_url, credentialsConfig)
+	contextDialer, dockerHostInRemote, err := ssh2.NewDialContext(_url, credentialsConfig)
 	if err != nil {
 		return
 	}
diff --git a/dubboctl/internal/docker/docker_client_ssh_test.go b/operator/pkg/docker/docker_client_ssh_test.go
similarity index 98%
rename from dubboctl/internal/docker/docker_client_ssh_test.go
rename to operator/pkg/docker/docker_client_ssh_test.go
index 4d577a7..0ca7194 100644
--- a/dubboctl/internal/docker/docker_client_ssh_test.go
+++ b/operator/pkg/docker/docker_client_ssh_test.go
@@ -24,6 +24,7 @@
 	"encoding/binary"
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
 	"io"
 	"net"
 	"net/http"
@@ -41,10 +42,6 @@
 	"golang.org/x/crypto/ssh"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-)
-
 func TestNewDockerClientWithSSH(t *testing.T) {
 	withCleanHome(t)
 
diff --git a/dubboctl/internal/docker/docker_client_test.go b/operator/pkg/docker/docker_client_test.go
similarity index 97%
rename from dubboctl/internal/docker/docker_client_test.go
rename to operator/pkg/docker/docker_client_test.go
index 3d4a4a9..b9f30ad 100644
--- a/dubboctl/internal/docker/docker_client_test.go
+++ b/operator/pkg/docker/docker_client_test.go
@@ -18,6 +18,7 @@
 import (
 	"context"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
 	"net"
 	"net/http"
 	"path/filepath"
@@ -31,10 +32,6 @@
 	"github.com/docker/docker/client"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-)
-
 // Test that we are creating client in accordance
 // with the DOCKER_HOST environment variable
 func TestNewClient(t *testing.T) {
diff --git a/dubboctl/internal/docker/docker_client_windows_test.go b/operator/pkg/docker/docker_client_windows_test.go
similarity index 95%
rename from dubboctl/internal/docker/docker_client_windows_test.go
rename to operator/pkg/docker/docker_client_windows_test.go
index 7267e39..6370e73 100644
--- a/dubboctl/internal/docker/docker_client_windows_test.go
+++ b/operator/pkg/docker/docker_client_windows_test.go
@@ -18,6 +18,7 @@
 import (
 	"context"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
 	"testing"
 	"time"
 )
@@ -28,10 +29,6 @@
 	"github.com/docker/docker/client"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-)
-
 func TestNewClientWinPipe(t *testing.T) {
 	const testNPipe = "test-npipe"
 
diff --git a/dubboctl/cmd/prompt/prompt.go b/operator/pkg/docker/prompt/prompt.go
similarity index 95%
rename from dubboctl/cmd/prompt/prompt.go
rename to operator/pkg/docker/prompt/prompt.go
index 91ea6f2..169762f 100644
--- a/dubboctl/cmd/prompt/prompt.go
+++ b/operator/pkg/docker/prompt/prompt.go
@@ -18,6 +18,8 @@
 import (
 	"bufio"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker/creds"
 	"io"
 	"os"
 	"strings"
@@ -30,11 +32,6 @@
 	"golang.org/x/term"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker/creds"
-)
-
 func NewPromptForCredentials(in io.Reader, out, errOut io.Writer) func(registry string) (docker.Credentials, error) {
 	firstTime := true
 	return func(registry string) (docker.Credentials, error) {
diff --git a/dubboctl/internal/docker/pusher.go b/operator/pkg/docker/pusher.go
similarity index 98%
rename from dubboctl/internal/docker/pusher.go
rename to operator/pkg/docker/pusher.go
index 1361d3c..fd87084 100644
--- a/dubboctl/internal/docker/pusher.go
+++ b/operator/pkg/docker/pusher.go
@@ -22,6 +22,7 @@
 	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
 	"io"
 	"net"
 	"net/http"
@@ -43,10 +44,6 @@
 	"golang.org/x/term"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
 type Opt func(*Pusher)
 
 type Credentials struct {
diff --git a/dubboctl/internal/docker/pusher_test.go b/operator/pkg/docker/pusher_test.go
similarity index 98%
rename from dubboctl/internal/docker/pusher_test.go
rename to operator/pkg/docker/pusher_test.go
index ea83783..ab15d7a 100644
--- a/dubboctl/internal/docker/pusher_test.go
+++ b/operator/pkg/docker/pusher_test.go
@@ -28,6 +28,8 @@
 	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/dubbo"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/docker"
 	"io"
 	"log"
 	"math/big"
@@ -46,11 +48,6 @@
 	"github.com/google/go-containerregistry/pkg/registry"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/docker"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/dubbo"
-)
-
 func TestGetRegistry(t *testing.T) {
 	tests := []struct {
 		name string
diff --git a/dubboctl/internal/envoy/envoy.go b/operator/pkg/envoy/envoy.go
similarity index 100%
rename from dubboctl/internal/envoy/envoy.go
rename to operator/pkg/envoy/envoy.go
diff --git a/dubboctl/internal/envoy/memory_limit_darwin.go b/operator/pkg/envoy/memory_limit_darwin.go
similarity index 100%
rename from dubboctl/internal/envoy/memory_limit_darwin.go
rename to operator/pkg/envoy/memory_limit_darwin.go
diff --git a/dubboctl/internal/envoy/memory_limit_linux.go b/operator/pkg/envoy/memory_limit_linux.go
similarity index 100%
rename from dubboctl/internal/envoy/memory_limit_linux.go
rename to operator/pkg/envoy/memory_limit_linux.go
diff --git a/dubboctl/internal/envoy/memory_limit_windows.go b/operator/pkg/envoy/memory_limit_windows.go
similarity index 100%
rename from dubboctl/internal/envoy/memory_limit_windows.go
rename to operator/pkg/envoy/memory_limit_windows.go
diff --git a/dubboctl/internal/envoy/remote_bootstrap.go b/operator/pkg/envoy/remote_bootstrap.go
similarity index 100%
rename from dubboctl/internal/envoy/remote_bootstrap.go
rename to operator/pkg/envoy/remote_bootstrap.go
diff --git a/dubboctl/internal/filesystem/filesystem.go b/operator/pkg/filesystem/filesystem.go
similarity index 100%
rename from dubboctl/internal/filesystem/filesystem.go
rename to operator/pkg/filesystem/filesystem.go
diff --git a/dubboctl/generated/templates/generate.go b/operator/pkg/generated/templates/generate.go
similarity index 98%
rename from dubboctl/generated/templates/generate.go
rename to operator/pkg/generated/templates/generate.go
index ec47441..62dff6e 100644
--- a/dubboctl/generated/templates/generate.go
+++ b/operator/pkg/generated/templates/generate.go
@@ -28,7 +28,7 @@
 	"path/filepath"
 )
 
-// relative path from /dubboctl/internal/dubbo/templates_embedded.go to the `dubboctl` folder
+// relative path from /dubboctl/operator/dubbo/templates_embedded.go to the `dubboctl` folder
 const relativePathToRoot = "../../"
 
 var templatesPath = filepath.Join(relativePathToRoot, "templates")
diff --git a/dubboctl/generated/zz_filesystem_generated.go b/operator/pkg/generated/zz_filesystem_generated.go
similarity index 100%
rename from dubboctl/generated/zz_filesystem_generated.go
rename to operator/pkg/generated/zz_filesystem_generated.go
diff --git a/dubboctl/identifier/const.go b/operator/pkg/identifier/const.go
similarity index 100%
rename from dubboctl/identifier/const.go
rename to operator/pkg/identifier/const.go
diff --git a/dubboctl/identifier/path.go b/operator/pkg/identifier/path.go
similarity index 95%
rename from dubboctl/identifier/path.go
rename to operator/pkg/identifier/path.go
index 3de9877..4d373d5 100644
--- a/dubboctl/identifier/path.go
+++ b/operator/pkg/identifier/path.go
@@ -16,11 +16,11 @@
 package identifier
 
 import (
+	"github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
 	"net/url"
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
 	"github.com/apache/dubbo-kubernetes/manifests"
 )
 
diff --git a/dubboctl/internal/kube/client.go b/operator/pkg/kube/client.go
similarity index 100%
rename from dubboctl/internal/kube/client.go
rename to operator/pkg/kube/client.go
diff --git a/dubboctl/internal/kube/common.go b/operator/pkg/kube/common.go
similarity index 100%
rename from dubboctl/internal/kube/common.go
rename to operator/pkg/kube/common.go
diff --git a/dubboctl/internal/kube/common_test.go b/operator/pkg/kube/common_test.go
similarity index 100%
rename from dubboctl/internal/kube/common_test.go
rename to operator/pkg/kube/common_test.go
diff --git a/dubboctl/internal/kube/component.go b/operator/pkg/kube/component.go
similarity index 94%
rename from dubboctl/internal/kube/component.go
rename to operator/pkg/kube/component.go
index fbfaea3..c4c931d 100644
--- a/dubboctl/internal/kube/component.go
+++ b/operator/pkg/kube/component.go
@@ -16,6 +16,12 @@
 package kube
 
 import (
+	manifest2 "github.com/apache/dubbo-kubernetes/operator/manifest"
+	"github.com/apache/dubbo-kubernetes/operator/manifest/render"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/filesystem"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
+	util2 "github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"path"
 	"strings"
 	"unicode/utf8"
@@ -29,15 +35,6 @@
 	"sigs.k8s.io/yaml"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/manifest/render"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 type ComponentName string
 
 const (
@@ -533,7 +530,7 @@
 	if addOn {
 		// see /deploy/addons
 		// values-*.yaml is the base yaml for addon bootstrap
-		valsYaml, err = manifest.ReadAndOverlayYamls([]string{
+		valsYaml, err = manifest2.ReadAndOverlayYamls([]string{
 			path.Join(identifier.Addons, "values-"+string(name)+".yaml"),
 		})
 		if err != nil {
@@ -542,12 +539,12 @@
 	}
 
 	// do not use spec != nil cause spec's type is not nil
-	if !util.IsValueNil(spec) {
+	if !util2.IsValueNil(spec) {
 		valsBytes, err = yaml.Marshal(spec)
 		if err != nil {
 			return "", err
 		}
-		valsYaml, err = util.OverlayYAML(valsYaml, string(valsBytes))
+		valsYaml, err = util2.OverlayYAML(valsYaml, string(valsBytes))
 		if err != nil {
 			return "", err
 		}
@@ -610,7 +607,7 @@
 // setNamespace split base and set namespace.
 func setNamespace(base string, namespace string) (string, error) {
 	var newSegs []string
-	segs, err := util.SplitYAML(base)
+	segs, err := util2.SplitYAML(base)
 	if err != nil {
 		return "", err
 	}
@@ -622,11 +619,11 @@
 		if err := yaml.Unmarshal([]byte(seg), &segMap); err != nil {
 			return "", err
 		}
-		pathCtx, _, err := manifest.GetPathContext(segMap, util.PathFromString("metadata.namespace"), true)
+		pathCtx, _, err := manifest2.GetPathContext(segMap, util2.PathFromString("metadata.namespace"), true)
 		if err != nil {
 			return "", err
 		}
-		if err := manifest.WritePathContext(pathCtx, manifest.ParseValue(namespace), false); err != nil {
+		if err := manifest2.WritePathContext(pathCtx, manifest2.ParseValue(namespace), false); err != nil {
 			return "", err
 		}
 		newSeg, err := yaml.Marshal(segMap)
@@ -635,6 +632,6 @@
 		}
 		newSegs = append(newSegs, string(newSeg))
 	}
-	final := util.JoinYAML(newSegs)
+	final := util2.JoinYAML(newSegs)
 	return final, nil
 }
diff --git a/dubboctl/internal/kube/object.go b/operator/pkg/kube/object.go
similarity index 98%
rename from dubboctl/internal/kube/object.go
rename to operator/pkg/kube/object.go
index ffd539e..0c9e5e8 100644
--- a/dubboctl/internal/kube/object.go
+++ b/operator/pkg/kube/object.go
@@ -18,6 +18,7 @@
 import (
 	"bytes"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"sort"
 	"strings"
 )
@@ -27,10 +28,6 @@
 	"k8s.io/apimachinery/pkg/util/yaml"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 const (
 	hashPrompt = "Namespace:Kind:Name=>"
 )
diff --git a/dubboctl/internal/kube/object_test.go b/operator/pkg/kube/object_test.go
similarity index 100%
rename from dubboctl/internal/kube/object_test.go
rename to operator/pkg/kube/object_test.go
diff --git a/dubboctl/internal/kube/operator.go b/operator/pkg/kube/operator.go
similarity index 97%
rename from dubboctl/internal/kube/operator.go
rename to operator/pkg/kube/operator.go
index d3d9de4..07ab00c 100644
--- a/dubboctl/internal/kube/operator.go
+++ b/operator/pkg/kube/operator.go
@@ -18,11 +18,11 @@
 import (
 	"errors"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/apis/dubbo.apache.org/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/operator/pkg/identifier"
 )
 
 import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/identifier"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/apis/dubbo.apache.org/v1alpha1"
 	"github.com/apache/dubbo-kubernetes/pkg/core/logger"
 )
 
diff --git a/dubboctl/internal/kube/port_foward.go b/operator/pkg/kube/port_foward.go
similarity index 100%
rename from dubboctl/internal/kube/port_foward.go
rename to operator/pkg/kube/port_foward.go
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-apply_manifest.yaml b/operator/pkg/kube/testdata/input/ctl_client-apply_manifest.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-apply_manifest.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-apply_manifest.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-apply_object-create.yaml b/operator/pkg/kube/testdata/input/ctl_client-apply_object-create.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-apply_object-create.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-apply_object-create.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-apply_object-update-before.yaml b/operator/pkg/kube/testdata/input/ctl_client-apply_object-update-before.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-apply_object-update-before.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-apply_object-update-before.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-apply_object-update.yaml b/operator/pkg/kube/testdata/input/ctl_client-apply_object-update.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-apply_object-update.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-apply_object-update.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-remove_manifest-before.yaml b/operator/pkg/kube/testdata/input/ctl_client-remove_manifest-before.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-remove_manifest-before.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-remove_manifest-before.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-remove_manifest.yaml b/operator/pkg/kube/testdata/input/ctl_client-remove_manifest.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-remove_manifest.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-remove_manifest.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-remove_object-delete-before.yaml b/operator/pkg/kube/testdata/input/ctl_client-remove_object-delete-before.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-remove_object-delete-before.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-remove_object-delete-before.yaml
diff --git a/dubboctl/internal/kube/testdata/input/ctl_client-remove_object-delete.yaml b/operator/pkg/kube/testdata/input/ctl_client-remove_object-delete.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/input/ctl_client-remove_object-delete.yaml
rename to operator/pkg/kube/testdata/input/ctl_client-remove_object-delete.yaml
diff --git a/dubboctl/internal/kube/testdata/want/admin_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/admin_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/admin_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/admin_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/ctl_client-apply_object-create.yaml b/operator/pkg/kube/testdata/want/ctl_client-apply_object-create.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/ctl_client-apply_object-create.yaml
rename to operator/pkg/kube/testdata/want/ctl_client-apply_object-create.yaml
diff --git a/dubboctl/internal/kube/testdata/want/ctl_client-apply_object-update.yaml b/operator/pkg/kube/testdata/want/ctl_client-apply_object-update.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/ctl_client-apply_object-update.yaml
rename to operator/pkg/kube/testdata/want/ctl_client-apply_object-update.yaml
diff --git a/dubboctl/internal/kube/testdata/want/nacos_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/nacos_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/nacos_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/nacos_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/prometheus_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/prometheus_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/prometheus_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/prometheus_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/skywalking_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/skywalking_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/skywalking_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/skywalking_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/zipkin_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/zipkin_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/zipkin_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/zipkin_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/kube/testdata/want/zookeeper_component-render_manifest.golden.yaml b/operator/pkg/kube/testdata/want/zookeeper_component-render_manifest.golden.yaml
similarity index 100%
rename from dubboctl/internal/kube/testdata/want/zookeeper_component-render_manifest.golden.yaml
rename to operator/pkg/kube/testdata/want/zookeeper_component-render_manifest.golden.yaml
diff --git a/dubboctl/internal/ssh/ssh_agent_conf.go b/operator/pkg/ssh/ssh_agent_conf.go
similarity index 100%
rename from dubboctl/internal/ssh/ssh_agent_conf.go
rename to operator/pkg/ssh/ssh_agent_conf.go
diff --git a/dubboctl/internal/ssh/ssh_agent_conf_windows.go b/operator/pkg/ssh/ssh_agent_conf_windows.go
similarity index 100%
rename from dubboctl/internal/ssh/ssh_agent_conf_windows.go
rename to operator/pkg/ssh/ssh_agent_conf_windows.go
diff --git a/dubboctl/internal/ssh/ssh_dialer.go b/operator/pkg/ssh/ssh_dialer.go
similarity index 87%
rename from dubboctl/internal/ssh/ssh_dialer.go
rename to operator/pkg/ssh/ssh_dialer.go
index 22bac84..c234450 100644
--- a/dubboctl/internal/ssh/ssh_dialer.go
+++ b/operator/pkg/ssh/ssh_dialer.go
@@ -33,9 +33,9 @@
 
 import (
 	"github.com/docker/cli/cli/connhelper"
-
+	dockerssh "github.com/docker/cli/cli/connhelper/ssh"
 	"github.com/docker/docker/pkg/homedir"
-
+	pkgerr "github.com/pkg/errors"
 	"golang.org/x/crypto/ssh"
 	"golang.org/x/crypto/ssh/agent"
 	"golang.org/x/crypto/ssh/knownhosts"
@@ -239,23 +239,17 @@
 }
 
 func stdioDialContext(url *urlPkg.URL, sshClient *ssh.Client, identity string) (DialContextFn, error) {
-	session, err := sshClient.NewSession()
+	cmd, err := checkStdioDialer(sshClient)
 	if err != nil {
 		return nil, err
 	}
-	defer session.Close()
-
-	out, err := session.CombinedOutput("docker system dial-stdio --help")
-	if err != nil {
-		return nil, fmt.Errorf("cannot use dial-stdio: %w (%q)", err, out)
-	}
 
 	var opts []string
 	if identity != "" {
 		opts = append(opts, "-i", identity)
 	}
 
-	connHelper, err := connhelper.GetConnectionHelperWithSSHOpts(url.String(), opts)
+	connHelper, err := getConnectionHelper(url.String(), cmd, opts)
 	if err != nil {
 		return nil, err
 	}
@@ -263,6 +257,56 @@
 	return connHelper.Dialer, nil
 }
 
+// dockerClients is a list of docker clients that support dial-stdio
+var dockerClients = []string{"docker", "podman"}
+
+// checkStdioDialer checks which docker client can be used to dial-stdio
+func checkStdioDialer(sshClient *ssh.Client) (string, error) {
+	var generalError error
+	var session *ssh.Session
+	var err error
+	defer func() {
+		if session != nil {
+			session.Close()
+		}
+	}()
+
+	for _, cmd := range dockerClients {
+		session, err = sshClient.NewSession()
+		if err != nil {
+			return "", err
+		}
+		out, err := session.CombinedOutput(fmt.Sprintf("%s system dial-stdio --help", cmd))
+		session.Close()
+		session = nil
+		if err == nil {
+			return cmd, nil
+		}
+		errors.Join(generalError, fmt.Errorf("checking client %s failed: %w (%q)", cmd, err, string(out)))
+	}
+
+	return "", fmt.Errorf("cannot use dial-stdio with remote docker clients: %v, errors: %w", dockerClients, generalError)
+}
+
+// getConnectionHelper returns Docker-specific connection helper for the given URL and docker client.
+//
+// This is a modified version of the function from pkg "github.com/docker/cli/cli/connhelper"
+func getConnectionHelper(daemonURL string, cmd string, sshFlags []string) (*connhelper.ConnectionHelper, error) {
+	u, err := urlPkg.Parse(daemonURL)
+	if err != nil {
+		return nil, err
+	}
+	switch scheme := u.Scheme; scheme {
+	case "ssh":
+		sp, err := dockerssh.ParseURL(daemonURL)
+		if err != nil {
+			return nil, pkgerr.Wrap(err, "ssh host connection is not valid")
+		}
+		return connhelper.GetCommandConnectionHelper("ssh", append(sshFlags, sp.Args(cmd, "system", "dial-stdio")...)...)
+	}
+	return nil, err
+}
+
 // Default key names.
 var knownKeyNames = []string{"id_rsa", "id_dsa", "id_ecdsa", "id_ecdsa_sk", "id_ed25519", "id_ed25519_sk"}
 
diff --git a/dubboctl/internal/ssh/terminal.go b/operator/pkg/ssh/terminal.go
similarity index 100%
rename from dubboctl/internal/ssh/terminal.go
rename to operator/pkg/ssh/terminal.go
diff --git a/dubboctl/internal/testing/testing.go b/operator/pkg/testing/testing.go
similarity index 100%
rename from dubboctl/internal/testing/testing.go
rename to operator/pkg/testing/testing.go
diff --git a/dubboctl/internal/util/config.go b/operator/pkg/util/config.go
similarity index 100%
rename from dubboctl/internal/util/config.go
rename to operator/pkg/util/config.go
diff --git a/dubboctl/internal/util/config_test.go b/operator/pkg/util/config_test.go
similarity index 94%
rename from dubboctl/internal/util/config_test.go
rename to operator/pkg/util/config_test.go
index 9c48864..ab4da55 100644
--- a/dubboctl/internal/util/config_test.go
+++ b/operator/pkg/util/config_test.go
@@ -18,16 +18,13 @@
 package util_test
 
 import (
+	. "github.com/apache/dubbo-kubernetes/operator/pkg/testing"
+	config "github.com/apache/dubbo-kubernetes/operator/pkg/util"
 	"os"
 	"path/filepath"
 	"testing"
 )
 
-import (
-	. "github.com/apache/dubbo-kubernetes/dubboctl/internal/testing"
-	config "github.com/apache/dubbo-kubernetes/dubboctl/internal/util"
-)
-
 // TestPath ensures that the Path accessor returns
 // XDG_CONFIG_HOME/.config/dubbo
 func TestPath(t *testing.T) {
diff --git a/dubboctl/internal/util/env.go b/operator/pkg/util/env.go
similarity index 100%
rename from dubboctl/internal/util/env.go
rename to operator/pkg/util/env.go
diff --git a/dubboctl/internal/util/file.go b/operator/pkg/util/file.go
similarity index 100%
rename from dubboctl/internal/util/file.go
rename to operator/pkg/util/file.go
diff --git a/dubboctl/internal/util/filter.go b/operator/pkg/util/filter.go
similarity index 100%
rename from dubboctl/internal/util/filter.go
rename to operator/pkg/util/filter.go
diff --git a/dubboctl/internal/util/filter_test.go b/operator/pkg/util/filter_test.go
similarity index 100%
rename from dubboctl/internal/util/filter_test.go
rename to operator/pkg/util/filter_test.go
diff --git a/dubboctl/internal/util/golden.go b/operator/pkg/util/golden.go
similarity index 100%
rename from dubboctl/internal/util/golden.go
rename to operator/pkg/util/golden.go
diff --git a/dubboctl/internal/util/golden_test.go b/operator/pkg/util/golden_test.go
similarity index 100%
rename from dubboctl/internal/util/golden_test.go
rename to operator/pkg/util/golden_test.go
diff --git a/dubboctl/internal/util/names.go b/operator/pkg/util/names.go
similarity index 100%
rename from dubboctl/internal/util/names.go
rename to operator/pkg/util/names.go
diff --git a/dubboctl/internal/util/names_test.go b/operator/pkg/util/names_test.go
similarity index 100%
rename from dubboctl/internal/util/names_test.go
rename to operator/pkg/util/names_test.go
diff --git a/dubboctl/internal/util/path.go b/operator/pkg/util/path.go
similarity index 100%
rename from dubboctl/internal/util/path.go
rename to operator/pkg/util/path.go
diff --git a/dubboctl/internal/util/reflect.go b/operator/pkg/util/reflect.go
similarity index 100%
rename from dubboctl/internal/util/reflect.go
rename to operator/pkg/util/reflect.go
diff --git a/dubboctl/internal/util/sortedset.go b/operator/pkg/util/sortedset.go
similarity index 100%
rename from dubboctl/internal/util/sortedset.go
rename to operator/pkg/util/sortedset.go
diff --git a/dubboctl/internal/util/testdata/TestLoad/dubbo/config.yaml b/operator/pkg/util/testdata/TestLoad/dubbo/config.yaml
similarity index 100%
rename from dubboctl/internal/util/testdata/TestLoad/dubbo/config.yaml
rename to operator/pkg/util/testdata/TestLoad/dubbo/config.yaml
diff --git a/dubboctl/internal/util/testdata/dubbo/config.yaml b/operator/pkg/util/testdata/dubbo/config.yaml
similarity index 100%
rename from dubboctl/internal/util/testdata/dubbo/config.yaml
rename to operator/pkg/util/testdata/dubbo/config.yaml
diff --git a/dubboctl/internal/util/yaml.go b/operator/pkg/util/yaml.go
similarity index 100%
rename from dubboctl/internal/util/yaml.go
rename to operator/pkg/util/yaml.go
diff --git a/dubboctl/internal/util/yaml_test.go b/operator/pkg/util/yaml_test.go
similarity index 100%
rename from dubboctl/internal/util/yaml_test.go
rename to operator/pkg/util/yaml_test.go
diff --git a/dubboctl/internal/registry/registry.go b/operator/registry/registry.go
similarity index 100%
rename from dubboctl/internal/registry/registry.go
rename to operator/registry/registry.go
diff --git a/dubboctl/internal/registry/zk/zk.go b/operator/registry/zk/zk.go
similarity index 97%
rename from dubboctl/internal/registry/zk/zk.go
rename to operator/registry/zk/zk.go
index 96338ca..f6d2b7c 100644
--- a/dubboctl/internal/registry/zk/zk.go
+++ b/operator/registry/zk/zk.go
@@ -18,6 +18,7 @@
 import (
 	"context"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/operator/registry"
 	"net/url"
 	"strings"
 	"time"
@@ -27,10 +28,6 @@
 	"github.com/dubbogo/go-zookeeper/zk"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/registry"
-)
-
 type zkRegistry struct {
 	client *zk.Conn
 }
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/api-server/authn/authenticator.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/api-server/authn/authenticator.go
index 987f638..69bb768 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/api-server/authn/authenticator.go
@@ -15,25 +15,10 @@
  * limitations under the License.
  */
 
-package dubbo
+package authn
 
 import (
-	"archive/zip"
-	"bytes"
+	"github.com/emicklei/go-restful/v3"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
-}
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+type Authenticator = restful.FilterFunction
diff --git a/pkg/api-server/authn/localhost.go b/pkg/api-server/authn/localhost.go
new file mode 100644
index 0000000..7532f24
--- /dev/null
+++ b/pkg/api-server/authn/localhost.go
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 authn
+
+import (
+	"net"
+
+	"github.com/emicklei/go-restful/v3"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+var log = core.Log.WithName("api-server").WithName("authn")
+
+func LocalhostAuthenticator(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
+	host, _, err := net.SplitHostPort(request.Request.RemoteAddr)
+	if err != nil {
+		//rest_errors.HandleError(request.Request.Context(), response, err, "Could not parse Remote Address from the Request")
+		panic(err)
+		return
+	}
+	if host == "127.0.0.1" || host == "::1" {
+		log.V(1).Info("authenticated as admin because requests originates from the same machine")
+		request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), user.Admin.Authenticated()))
+	}
+	chain.ProcessFilter(request, response)
+}
diff --git a/pkg/api-server/authn/skip.go b/pkg/api-server/authn/skip.go
new file mode 100644
index 0000000..c187a00
--- /dev/null
+++ b/pkg/api-server/authn/skip.go
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 authn
+
+import (
+	"strings"
+
+	"github.com/emicklei/go-restful/v3"
+)
+
+const (
+	MetadataAuthKey  = "auth"
+	MetadataAuthSkip = "skip"
+)
+
+// UnauthorizedPathPrefixes is another way to add path prefixes as unauthorized endpoint.
+// Prefer MetadataAuthKey, use UnauthorizedPathPrefixes if needed.
+var UnauthorizedPathPrefixes = map[string]struct{}{
+	"/gui": {},
+}
+
+func SkipAuth(request *restful.Request) bool {
+	if route := request.SelectedRoute(); route != nil {
+		if route.Metadata()[MetadataAuthKey] == MetadataAuthSkip {
+			return true
+		}
+	}
+	for prefix := range UnauthorizedPathPrefixes {
+		if strings.HasPrefix(request.Request.RequestURI, prefix) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/pkg/api-server/customization/api_manager.go b/pkg/api-server/customization/api_manager.go
new file mode 100644
index 0000000..69d4f4c
--- /dev/null
+++ b/pkg/api-server/customization/api_manager.go
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 customization
+
+import (
+	"github.com/emicklei/go-restful/v3"
+)
+
+type APIInstaller interface {
+	Install(container *restful.Container)
+}
+
+type APIManager interface {
+	APIInstaller
+	Add(ws *restful.WebService)
+	AddFilter(filter restful.FilterFunction)
+}
+
+type APIList struct {
+	list    []*restful.WebService
+	filters []restful.FilterFunction
+}
+
+func NewAPIList() *APIList {
+	return &APIList{
+		list:    []*restful.WebService{},
+		filters: []restful.FilterFunction{},
+	}
+}
+
+func (c *APIList) Add(ws *restful.WebService) {
+	c.list = append(c.list, ws)
+}
+
+func (c *APIList) AddFilter(f restful.FilterFunction) {
+	c.filters = append(c.filters, f)
+}
+
+func (c *APIList) Install(container *restful.Container) {
+	for _, ws := range c.list {
+		container.Add(ws)
+	}
+
+	for _, f := range c.filters {
+		container.Filter(f)
+	}
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/api-server/types/errors.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/api-server/types/errors.go
index 987f638..577e6cf 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/api-server/types/errors.go
@@ -15,25 +15,27 @@
  * limitations under the License.
  */
 
-package dubbo
+package types
 
 import (
-	"archive/zip"
-	"bytes"
+	"fmt"
+	"reflect"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+type InvalidPageSizeError struct {
+	Reason string
 }
 
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (a *InvalidPageSizeError) Error() string {
+	return a.Reason
+}
+
+func (a *InvalidPageSizeError) Is(err error) bool {
+	return reflect.TypeOf(a) == reflect.TypeOf(err)
+}
+
+func NewMaxPageSizeExceeded(pageSize, limit int) error {
+	return &InvalidPageSizeError{Reason: fmt.Sprintf("invalid page size of %d. Maximum page size is %d", pageSize, limit)}
+}
+
+var InvalidPageSize = &InvalidPageSizeError{Reason: "invalid format"}
diff --git a/pkg/config/access/config.go b/pkg/config/access/config.go
new file mode 100644
index 0000000..e476466
--- /dev/null
+++ b/pkg/config/access/config.go
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import (
+	"github.com/pkg/errors"
+
+	"github.com/apache/dubbo-kubernetes/pkg/config"
+)
+
+const StaticType = "static"
+
+func DefaultAccessConfig() AccessConfig {
+	return AccessConfig{
+		Type: StaticType,
+		Static: StaticAccessConfig{
+			AdminResources: AdminResourcesStaticAccessConfig{
+				Users:  []string{"mesh-system:admin"},
+				Groups: []string{"mesh-system:admin"},
+			},
+			GenerateDPToken: GenerateDPTokenStaticAccessConfig{
+				Users:  []string{"mesh-system:admin"},
+				Groups: []string{"mesh-system:admin"},
+			},
+			GenerateUserToken: GenerateUserTokenStaticAccessConfig{
+				Users:  []string{"mesh-system:admin"},
+				Groups: []string{"mesh-system:admin"},
+			},
+			GenerateZoneToken: GenerateZoneTokenStaticAccessConfig{
+				Users:  []string{"mesh-system:admin"},
+				Groups: []string{"mesh-system:admin"},
+			},
+			ViewConfigDump: ViewConfigDumpStaticAccessConfig{
+				Users:  []string{},
+				Groups: []string{"mesh-system:unauthenticated", "mesh-system:authenticated"},
+			},
+			ViewStats: ViewStatsStaticAccessConfig{
+				Users:  []string{},
+				Groups: []string{"mesh-system:unauthenticated", "mesh-system:authenticated"},
+			},
+			ViewClusters: ViewClustersStaticAccessConfig{
+				Users:  []string{},
+				Groups: []string{"mesh-system:unauthenticated", "mesh-system:authenticated"},
+			},
+			ControlPlaneMetadata: ControlPlaneMetadataStaticAccessConfig{
+				Users:  []string{},
+				Groups: []string{"mesh-system:unauthenticated", "mesh-system:authenticated"},
+			},
+		},
+	}
+}
+
+// AccessConfig defines a configuration for access control
+type AccessConfig struct {
+	config.BaseConfig
+
+	// Type of the access strategy (available values: "static")
+	Type string `json:"type" envconfig:"DUBBO_ACCESS_TYPE"`
+	// Configuration of static access strategy
+	Static StaticAccessConfig `json:"static"`
+}
+
+func (r AccessConfig) Validate() error {
+	if r.Type == "" {
+		return errors.New("Type has to be defined")
+	}
+	return nil
+}
+
+var _ config.Config = &AccessConfig{}
+
+// StaticAccessConfig a static access strategy configuration
+type StaticAccessConfig struct {
+	// AdminResources defines an access to admin resources (Secret/GlobalSecret)
+	AdminResources AdminResourcesStaticAccessConfig `json:"adminResources"`
+	// GenerateDPToken defines an access to generating dataplane token
+	GenerateDPToken GenerateDPTokenStaticAccessConfig `json:"generateDpToken"`
+	// GenerateUserToken defines an access to generating user token
+	GenerateUserToken GenerateUserTokenStaticAccessConfig `json:"generateUserToken"`
+	// GenerateZoneToken defines an access to generating zone token
+	GenerateZoneToken GenerateZoneTokenStaticAccessConfig `json:"generateZoneToken"`
+	// ViewConfigDump defines an access to getting envoy config dump
+	ViewConfigDump ViewConfigDumpStaticAccessConfig `json:"viewConfigDump"`
+	// ViewStats defines an access to getting envoy stats
+	ViewStats ViewStatsStaticAccessConfig `json:"viewStats"`
+	// ViewClusters defines an access to getting envoy clusters
+	ViewClusters ViewClustersStaticAccessConfig `json:"viewClusters"`
+	// ControlPlaneMetadata defines an access for control-plane metadata (for example config)
+	ControlPlaneMetadata ControlPlaneMetadataStaticAccessConfig `json:"controlPlaneMetadata"`
+}
+
+type AdminResourcesStaticAccessConfig struct {
+	// List of users that are allowed to access admin resources
+	Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_ADMIN_RESOURCES_USERS"`
+	// List of groups that are allowed to access admin resources
+	Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_ADMIN_RESOURCES_GROUPS"`
+}
+
+type GenerateDPTokenStaticAccessConfig struct {
+	// List of users that are allowed to generate dataplane token
+	Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_DP_TOKEN_USERS"`
+	// List of groups that are allowed to generate dataplane token
+	Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_DP_TOKEN_GROUPS"`
+}
+
+type GenerateUserTokenStaticAccessConfig struct {
+	// List of users that are allowed to generate user token
+	Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_USER_TOKEN_USERS"`
+	// List of groups that are allowed to generate user token
+	Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_USER_TOKEN_GROUPS"`
+}
+
+type GenerateZoneTokenStaticAccessConfig struct {
+	// List of users that are allowed to generate zone token
+	Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_ZONE_TOKEN_USERS"`
+	// List of groups that are allowed to generate zone token
+	Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_GENERATE_ZONE_TOKEN_GROUPS"`
+}
+
+type ViewConfigDumpStaticAccessConfig struct {
+	// List of users that are allowed to get envoy config dump
+	Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_GET_CONFIG_DUMP_USERS"`
+	// List of groups that are allowed to get envoy config dump
+	Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_GET_CONFIG_DUMP_GROUPS"`
+}
+
+type ViewStatsStaticAccessConfig struct {
+	// List of users that are allowed to get envoy config stats
+	Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_VIEW_STATS_USERS"`
+	// List of groups that are allowed to get envoy config stats
+	Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_VIEW_STATS_GROUPS"`
+}
+
+type ViewClustersStaticAccessConfig struct {
+	// List of users that are allowed to get envoy config clusters
+	Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_VIEW_CLUSTERS_USERS"`
+	// List of groups that are allowed to get envoy config clusters
+	Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_VIEW_CLUSTERS_GROUPS"`
+}
+
+type ControlPlaneMetadataStaticAccessConfig struct {
+	// List of users that are allowed to access control-plane metadata
+	Users []string `json:"users" envconfig:"DUBBO_ACCESS_STATIC_CONTROL_PLANE_METADATA_USERS"`
+	// List of groups that are allowed to access control-plane metadata
+	Groups []string `json:"groups" envconfig:"DUBBO_ACCESS_STATIC_CONTROL_PLANE_METADATA_GROUPS"`
+}
diff --git a/pkg/config/api-server/config.go b/pkg/config/api-server/config.go
new file mode 100644
index 0000000..c12ee73
--- /dev/null
+++ b/pkg/config/api-server/config.go
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 api_server
+
+import (
+	"net/url"
+
+	"github.com/pkg/errors"
+	"go.uber.org/multierr"
+
+	"github.com/apache/dubbo-kubernetes/pkg/config"
+	config_types "github.com/apache/dubbo-kubernetes/pkg/config/types"
+)
+
+var _ config.Config = &ApiServerConfig{}
+
+// ApiServerConfig defines API Server configuration
+type ApiServerConfig struct {
+	config.BaseConfig
+
+	// If true, then API Server will operate in read only mode (serving GET requests)
+	ReadOnly bool `json:"readOnly" envconfig:"dubbo_api_server_read_only"`
+	// Allowed domains for Cross-Origin Resource Sharing. The value can be either domain or regexp
+	CorsAllowedDomains []string `json:"corsAllowedDomains" envconfig:"dubbo_api_server_cors_allowed_domains"`
+	// HTTP configuration of the API Server
+	HTTP ApiServerHTTPConfig `json:"http"`
+	// HTTPS configuration of the API Server
+	HTTPS ApiServerHTTPSConfig `json:"https"`
+	// Authentication configuration for administrative endpoints like Dataplane Token or managing Secrets
+	Auth ApiServerAuth `json:"auth"`
+	// Authentication configuration for API Server
+	Authn ApiServerAuthn `json:"authn"`
+	// BasePath the path to serve the API from
+	BasePath string `json:"basePath" envconfig:"dubbo_api_server_base_path"`
+	// RootUrl can be used if you use a reverse proxy
+	RootUrl string `json:"rootUrl" envconfig:"dubbo_api_server_root_url"`
+	// GUI configuration specific to the GUI
+	GUI ApiServerGUI `json:"gui,omitempty"`
+}
+
+type ApiServerGUI struct {
+	// Enabled whether to serve to gui (if mode=zone this has no effect)
+	Enabled bool `json:"enabled" envconfig:"dubbo_api_server_gui_enabled"`
+	// RootUrl can be used if you set a reverse proxy or want to serve the gui from a different path
+	RootUrl string `json:"rootUrl" envconfig:"dubbo_api_server_gui_root_url"`
+	// BasePath the path to serve the GUI from
+	BasePath string `json:"basePath" envconfig:"dubbo_api_server_gui_base_path"`
+}
+
+func (a *ApiServerGUI) Validate() error {
+	var errs error
+	if a.RootUrl != "" {
+		_, err := url.Parse(a.RootUrl)
+		if err != nil {
+			errs = multierr.Append(errs, errors.New("RootUrl is not a valid url"))
+		}
+	}
+	if a.BasePath != "" {
+		_, err := url.Parse(a.BasePath)
+		if err != nil {
+			errs = multierr.Append(errs, errors.New("BaseGuiPath is not a valid url"))
+		}
+	}
+	return errs
+}
+
+// ApiServerHTTPConfig defines API Server HTTP configuration
+type ApiServerHTTPConfig struct {
+	// If true then API Server will be served on HTTP
+	Enabled bool `json:"enabled" envconfig:"dubbo_api_server_http_enabled"`
+	// Network interface on which HTTP API Server will be exposed
+	Interface string `json:"interface" envconfig:"dubbo_api_server_http_interface"`
+	// Port of the HTTP API Server
+	Port uint32 `json:"port" envconfig:"dubbo_api_server_http_port"`
+}
+
+func (a *ApiServerHTTPConfig) Validate() error {
+	var errs error
+	if a.Interface == "" {
+		errs = multierr.Append(errs, errors.New("Interface cannot be empty"))
+	}
+	if a.Port > 65535 {
+		errs = multierr.Append(errs, errors.New("Port must be in range [0, 65535]"))
+	}
+	return errs
+}
+
+// ApiServerHTTPSConfig defines API Server HTTPS configuration
+type ApiServerHTTPSConfig struct {
+	// If true then API Server will be served on HTTPS
+	Enabled bool `json:"enabled" envconfig:"dubbo_api_server_https_enabled"`
+	// Network interface on which HTTPS API Server will be exposed
+	Interface string `json:"interface" envconfig:"dubbo_api_server_https_interface"`
+	// Port of the HTTPS API Server
+	Port uint32 `json:"port" envconfig:"dubbo_api_server_https_port"`
+	// Path to TLS certificate file. Autoconfigured from dubbo_GENERAL_TLS_CERT_FILE if empty
+	TlsCertFile string `json:"tlsCertFile" envconfig:"dubbo_api_server_https_tls_cert_file"`
+	// Path to TLS key file. Autoconfigured from dubbo_GENERAL_TLS_KEY_FILE if empty
+	TlsKeyFile string `json:"tlsKeyFile" envconfig:"dubbo_api_server_https_tls_key_file"`
+	// TlsMinVersion defines the minimum TLS version to be used
+	TlsMinVersion string `json:"tlsMinVersion" envconfig:"dubbo_api_server_https_tls_min_version"`
+	// TlsMaxVersion defines the maximum TLS version to be used
+	TlsMaxVersion string `json:"tlsMaxVersion" envconfig:"dubbo_api_server_https_tls_max_version"`
+	// TlsCipherSuites defines the list of ciphers to use
+	TlsCipherSuites []string `json:"tlsCipherSuites" envconfig:"dubbo_api_server_https_tls_cipher_suites"`
+	// If true, then HTTPS connection will require client cert.
+	RequireClientCert bool `json:"requireClientCert" envconfig:"dubbo_api_server_https_require_client_cert"`
+	// Path to the CA certificate which is used to sign client certificates. It is used only for verifying client certificates.
+	TlsCaFile string `json:"tlsCaFile" envconfig:"dubbo_api_server_https_tls_ca_file"`
+}
+
+func (a *ApiServerHTTPSConfig) Validate() error {
+	var errs error
+	if a.Interface == "" {
+		errs = multierr.Append(errs, errors.New(".Interface cannot be empty"))
+	}
+	if a.Port > 65535 {
+		return errors.New("Port must be in range [0, 65535]")
+	}
+	if (a.TlsKeyFile == "" && a.TlsCertFile != "") || (a.TlsKeyFile != "" && a.TlsCertFile == "") {
+		errs = multierr.Append(errs, errors.New("Both TlsCertFile and TlsKeyFile has to be specified"))
+	}
+	if _, err := config_types.TLSVersion(a.TlsMinVersion); err != nil {
+		errs = multierr.Append(errs, errors.New(".TlsMinVersion"+err.Error()))
+	}
+	if _, err := config_types.TLSVersion(a.TlsMaxVersion); err != nil {
+		errs = multierr.Append(errs, errors.New(".TlsMaxVersion"+err.Error()))
+	}
+	if _, err := config_types.TLSCiphers(a.TlsCipherSuites); err != nil {
+		errs = multierr.Append(errs, errors.New(".TlsCipherSuites"+err.Error()))
+	}
+	return errs
+}
+
+// ApiServerAuth defines API Server authentication configuration
+type ApiServerAuth struct {
+	// Directory of authorized client certificates (only valid in HTTPS)
+	ClientCertsDir string `json:"clientCertsDir" envconfig:"dubbo_api_server_auth_client_certs_dir"`
+}
+
+// ApiServerAuthn defines Api Server Authentication configuration
+type ApiServerAuthn struct {
+	// Type of authentication mechanism (available values: "clientCerts", "tokens")
+	Type string `json:"type" envconfig:"dubbo_api_server_authn_type"`
+	// Localhost is authenticated as a user admin of group admin
+	LocalhostIsAdmin bool `json:"localhostIsAdmin" envconfig:"dubbo_api_server_authn_localhost_is_admin"`
+	// Configuration for tokens authentication
+	Tokens ApiServerAuthnTokens `json:"tokens"`
+}
+
+func (a ApiServerAuthn) Validate() error {
+	if a.Type == "tokens" {
+		if err := a.Tokens.Validate(); err != nil {
+			return errors.Wrap(err, ".Tokens is not valid")
+		}
+	}
+	return nil
+}
+
+type ApiServerAuthnTokens struct {
+	// If true then User Token with name admin and group admin will be created and placed as admin-user-token dubbo Global Secret
+	BootstrapAdminToken bool `json:"bootstrapAdminToken" envconfig:"dubbo_api_server_authn_tokens_bootstrap_admin_token"`
+	// If true the control plane token issuer is enabled. It's recommended to set it to false when all the tokens are issued offline.
+	EnableIssuer bool `json:"enableIssuer" envconfig:"dubbo_api_server_authn_tokens_enable_issuer"`
+	// Token validator configuration
+	Validator TokensValidator `json:"validator"`
+}
+
+func (a ApiServerAuthnTokens) Validate() error {
+	if err := a.Validator.Validate(); err != nil {
+		return errors.Wrap(err, ".Validator is not valid")
+	}
+	return nil
+}
+
+var _ config.Config = TokensValidator{}
+
+type TokensValidator struct {
+	config.BaseConfig
+
+	// If true then dubbo secrets with prefix "user-token-signing-key" are considered as signing keys.
+	UseSecrets bool `json:"useSecrets" envconfig:"dubbo_api_server_authn_tokens_validator_use_secrets"`
+	// List of public keys used to validate the token.
+	PublicKeys []config_types.PublicKey `json:"publicKeys"`
+}
+
+func (t TokensValidator) Validate() error {
+	for i, key := range t.PublicKeys {
+		if err := key.Validate(); err != nil {
+			return errors.Wrapf(err, ".PublicKeys[%d] is not valid", i)
+		}
+	}
+	return nil
+}
+
+func (a *ApiServerConfig) Validate() error {
+	var errs error
+	if err := a.HTTP.Validate(); err != nil {
+		errs = multierr.Append(err, errors.Wrap(err, ".HTTP not valid"))
+	}
+	if err := a.HTTPS.Validate(); err != nil {
+		errs = multierr.Append(err, errors.Wrap(err, ".HTTPS not valid"))
+	}
+	if err := a.GUI.Validate(); err != nil {
+		errs = multierr.Append(err, errors.Wrap(err, ".GUI not valid"))
+	}
+	if a.RootUrl != "" {
+		if _, err := url.Parse(a.RootUrl); err != nil {
+			errs = multierr.Append(err, errors.New("RootUrl is not a valid URL"))
+		}
+	}
+	if a.BasePath != "" {
+		_, err := url.Parse(a.BasePath)
+		if err != nil {
+			errs = multierr.Append(errs, errors.New("BaseGuiPath is not a valid url"))
+		}
+	}
+	if err := a.Authn.Validate(); err != nil {
+		errs = multierr.Append(err, errors.Wrap(err, ".Authn is not valid"))
+	}
+	return errs
+}
+
+func DefaultApiServerConfig() *ApiServerConfig {
+	return &ApiServerConfig{
+		ReadOnly:           false,
+		CorsAllowedDomains: []string{".*"},
+		BasePath:           "/",
+		HTTP: ApiServerHTTPConfig{
+			Enabled:   true,
+			Interface: "0.0.0.0",
+			Port:      5681,
+		},
+		HTTPS: ApiServerHTTPSConfig{
+			Enabled:         true,
+			Interface:       "0.0.0.0",
+			Port:            5682,
+			TlsCertFile:     "", // autoconfigured
+			TlsKeyFile:      "", // autoconfigured
+			TlsMinVersion:   "TLSv1_2",
+			TlsCipherSuites: []string{},
+		},
+		Auth: ApiServerAuth{
+			ClientCertsDir: "",
+		},
+		Authn: ApiServerAuthn{
+			Type:             "tokens",
+			LocalhostIsAdmin: true,
+			Tokens: ApiServerAuthnTokens{
+				BootstrapAdminToken: true,
+				EnableIssuer:        true,
+				Validator: TokensValidator{
+					UseSecrets: true,
+					PublicKeys: []config_types.PublicKey{},
+				},
+			},
+		},
+		GUI: ApiServerGUI{
+			Enabled:  true,
+			BasePath: "/gui",
+		},
+	}
+}
diff --git a/pkg/config/app/dubbo-cp/config.go b/pkg/config/app/dubbo-cp/config.go
index ab9a1ef..82efdd1 100644
--- a/pkg/config/app/dubbo-cp/config.go
+++ b/pkg/config/app/dubbo-cp/config.go
@@ -19,26 +19,27 @@
 
 import (
 	"time"
-)
 
-import (
-	"github.com/pkg/errors"
-
-	"go.uber.org/multierr"
-)
-
-import (
 	"github.com/apache/dubbo-kubernetes/pkg/config"
+	"github.com/apache/dubbo-kubernetes/pkg/config/access"
 	"github.com/apache/dubbo-kubernetes/pkg/config/admin"
+	"github.com/apache/dubbo-kubernetes/pkg/config/intercp"
+	"github.com/asaskevich/govalidator"
+	"github.com/pkg/errors"
+	"go.uber.org/multierr"
+
+	api_server "github.com/apache/dubbo-kubernetes/pkg/config/api-server"
 	"github.com/apache/dubbo-kubernetes/pkg/config/bufman"
 	"github.com/apache/dubbo-kubernetes/pkg/config/core"
 	"github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
 	"github.com/apache/dubbo-kubernetes/pkg/config/diagnostics"
+
 	dp_server "github.com/apache/dubbo-kubernetes/pkg/config/dp-server"
 	"github.com/apache/dubbo-kubernetes/pkg/config/dubbo"
 	"github.com/apache/dubbo-kubernetes/pkg/config/eventbus"
 	"github.com/apache/dubbo-kubernetes/pkg/config/multizone"
 	"github.com/apache/dubbo-kubernetes/pkg/config/plugins/runtime"
+
 	config_types "github.com/apache/dubbo-kubernetes/pkg/config/types"
 	"github.com/apache/dubbo-kubernetes/pkg/config/xds"
 	"github.com/apache/dubbo-kubernetes/pkg/config/xds/bootstrap"
@@ -55,6 +56,116 @@
 	SkipMeshCreation bool `json:"skipMeshCreation" envconfig:"DUBBO_DEFAULTS_SKIP_MESH_CREATION"`
 }
 
+type DataplaneMetrics struct {
+	config.BaseConfig
+
+	SubscriptionLimit int                   `json:"subscriptionLimit" envconfig:"Dubbo_metrics_dataplane_subscription_limit"`
+	IdleTimeout       config_types.Duration `json:"idleTimeout" envconfig:"Dubbo_metrics_dataplane_idle_timeout"`
+}
+
+type ZoneMetrics struct {
+	config.BaseConfig
+
+	SubscriptionLimit int                   `json:"subscriptionLimit" envconfig:"Dubbo_metrics_zone_subscription_limit"`
+	IdleTimeout       config_types.Duration `json:"idleTimeout" envconfig:"Dubbo_metrics_zone_idle_timeout"`
+	// CompactFinishedSubscriptions compacts finished metrics (do not store config and details of KDS exchange).
+	CompactFinishedSubscriptions bool `json:"compactFinishedSubscriptions" envconfig:"Dubbo_metrics_zone_compact_finished_subscriptions"`
+}
+
+type MeshMetrics struct {
+	config.BaseConfig
+
+	// Deprecated: use MinResyncInterval instead
+	MinResyncTimeout config_types.Duration `json:"minResyncTimeout" envconfig:"Dubbo_metrics_mesh_min_resync_timeout"`
+	// Deprecated: use FullResyncInterval instead
+	MaxResyncTimeout config_types.Duration `json:"maxResyncTimeout" envconfig:"Dubbo_metrics_mesh_max_resync_timeout"`
+	// BufferSize the size of the buffer between event creation and processing
+	BufferSize int `json:"bufferSize" envconfig:"Dubbo_metrics_mesh_buffer_size"`
+	// MinResyncInterval the minimum time between 2 refresh of insights.
+	MinResyncInterval config_types.Duration `json:"minResyncInterval" envconfig:"Dubbo_metrics_mesh_min_resync_interval"`
+	// FullResyncInterval time between triggering a full refresh of all the insights
+	FullResyncInterval config_types.Duration `json:"fullResyncInterval" envconfig:"Dubbo_metrics_mesh_full_resync_interval"`
+	// EventProcessors is a number of workers that process metrics events.
+	EventProcessors int `json:"eventProcessors" envconfig:"Dubbo_metrics_mesh_event_processors"`
+}
+
+type Metrics struct {
+	config.BaseConfig
+
+	Dataplane    *DataplaneMetrics    `json:"dataplane"`
+	Zone         *ZoneMetrics         `json:"zone"`
+	Mesh         *MeshMetrics         `json:"mesh"`
+	ControlPlane *ControlPlaneMetrics `json:"controlPlane"`
+}
+
+type ControlPlaneMetrics struct {
+	// ReportResourcesCount if true will report metrics with the count of resources.
+	// Default: true
+	ReportResourcesCount bool `json:"reportResourcesCount" envconfig:"Dubbo_metrics_control_plane_report_resources_count"`
+}
+
+type InterCpConfig struct {
+	// Catalog configuration. Catalog keeps a record of all live CP instances in the zone.
+	Catalog CatalogConfig `json:"catalog"`
+	// Intercommunication CP server configuration
+	Server InterCpServerConfig `json:"server"`
+}
+
+func (i *InterCpConfig) Validate() error {
+	if err := i.Server.Validate(); err != nil {
+		return errors.Wrap(err, ".Server validation failed")
+	}
+	if err := i.Catalog.Validate(); err != nil {
+		return errors.Wrap(err, ".Catalog validation failed")
+	}
+	return nil
+}
+
+type CatalogConfig struct {
+	// InstanceAddress indicates an address on which other control planes can communicate with this CP
+	// If empty then it's autoconfigured by taking the first IP of the nonloopback network interface.
+	InstanceAddress string `json:"instanceAddress" envconfig:"Dubbo_inter_cp_catalog_instance_address"`
+	// Interval on which CP will send heartbeat to a leader.
+	HeartbeatInterval config_types.Duration `json:"heartbeatInterval" envconfig:"Dubbo_inter_cp_catalog_heartbeat_interval"`
+	// Interval on which CP will write all instances to a catalog.
+	WriterInterval config_types.Duration `json:"writerInterval" envconfig:"Dubbo_inter_cp_catalog_writer_interval"`
+}
+
+func (i *CatalogConfig) Validate() error {
+	if i.InstanceAddress != "" && !govalidator.IsDNSName(i.InstanceAddress) && !govalidator.IsIP(i.InstanceAddress) {
+		return errors.New(".InstanceAddress has to be valid IP or DNS address")
+	}
+	return nil
+}
+
+type InterCpServerConfig struct {
+	// Port on which Intercommunication CP server will listen
+	Port uint16 `json:"port" envconfig:"Dubbo_inter_cp_server_port"`
+	// TlsMinVersion defines the minimum TLS version to be used
+	TlsMinVersion string `json:"tlsMinVersion" envconfig:"Dubbo_inter_cp_server_tls_min_version"`
+	// TlsMaxVersion defines the maximum TLS version to be used
+	TlsMaxVersion string `json:"tlsMaxVersion" envconfig:"Dubbo_inter_cp_server_tls_max_version"`
+	// TlsCipherSuites defines the list of ciphers to use
+	TlsCipherSuites []string `json:"tlsCipherSuites" envconfig:"Dubbo_inter_cp_server_tls_cipher_suites"`
+}
+
+func (i *InterCpServerConfig) Validate() error {
+	var errs error
+	if i.Port == 0 {
+		errs = multierr.Append(errs, errors.New(".Port cannot be zero"))
+	}
+	if _, err := config_types.TLSVersion(i.TlsMinVersion); err != nil {
+		errs = multierr.Append(errs, errors.New(".TlsMinVersion "+err.Error()))
+	}
+	if _, err := config_types.TLSVersion(i.TlsMaxVersion); err != nil {
+		errs = multierr.Append(errs, errors.New(".TlsMaxVersion "+err.Error()))
+	}
+	if _, err := config_types.TLSCiphers(i.TlsCipherSuites); err != nil {
+		errs = multierr.Append(errs, errors.New(".TlsCipherSuites "+err.Error()))
+	}
+	return errs
+}
+
 type GeneralConfig struct {
 	config.BaseConfig
 
@@ -93,12 +204,20 @@
 	Admin *admin.Admin `json:"admin"`
 	// DeployMode-specific configuration
 	Runtime *runtime.RuntimeConfig `json:"runtime,omitempty"`
+	// API Server configuration
+	ApiServer *api_server.ApiServerConfig `json:"apiServer,omitempty"`
 	// Multizone Config
 	Multizone *multizone.MultizoneConfig `json:"multizone,omitempty"`
 	// Default dubbo entities configuration
 	Defaults *Defaults `json:"defaults,omitempty"`
+	// Metrics configuration
+	Metrics *Metrics `json:"metrics,omitempty"`
+	// Intercommunication CP configuration
+	InterCp intercp.InterCpConfig `json:"interCp"`
 	// Diagnostics configuration
 	Diagnostics *diagnostics.DiagnosticsConfig `json:"diagnostics,omitempty"`
+	// Access Control configuration
+	Access access.AccessConfig `json:"access"`
 	// Proxy holds configuration for proxies
 	Proxy xds.Proxy `json:"proxy"`
 	// Dataplane Server configuration
@@ -172,6 +291,7 @@
 		DubboConfig:           dubbo.DefaultServiceNameMappingConfig(),
 		EventBus:              eventbus.Default(),
 		DDSEventBasedWatchdog: DefaultEventBasedWatchdog(),
+		ApiServer:             api_server.DefaultApiServerConfig(),
 	}
 }
 
diff --git a/pkg/config/intercp/config.go b/pkg/config/intercp/config.go
new file mode 100644
index 0000000..018ee67
--- /dev/null
+++ b/pkg/config/intercp/config.go
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 intercp
+
+import (
+	"time"
+
+	"github.com/asaskevich/govalidator"
+	"github.com/pkg/errors"
+	"go.uber.org/multierr"
+
+	config_types "github.com/apache/dubbo-kubernetes/pkg/config/types"
+)
+
+func DefaultInterCpConfig() InterCpConfig {
+	return InterCpConfig{
+		Catalog: CatalogConfig{
+			InstanceAddress:   "", // autoconfigured
+			HeartbeatInterval: config_types.Duration{Duration: 5 * time.Second},
+			WriterInterval:    config_types.Duration{Duration: 15 * time.Second},
+		},
+		Server: InterCpServerConfig{
+			Port:            5683,
+			TlsMinVersion:   "TLSv1_2",
+			TlsCipherSuites: []string{},
+		},
+	}
+}
+
+type InterCpConfig struct {
+	// Catalog configuration. Catalog keeps a record of all live CP instances in the zone.
+	Catalog CatalogConfig `json:"catalog"`
+	// Intercommunication CP server configuration
+	Server InterCpServerConfig `json:"server"`
+}
+
+func (i *InterCpConfig) Validate() error {
+	if err := i.Server.Validate(); err != nil {
+		return errors.Wrap(err, ".Server validation failed")
+	}
+	if err := i.Catalog.Validate(); err != nil {
+		return errors.Wrap(err, ".Catalog validation failed")
+	}
+	return nil
+}
+
+type CatalogConfig struct {
+	// InstanceAddress indicates an address on which other control planes can communicate with this CP
+	// If empty then it's autoconfigured by taking the first IP of the nonloopback network interface.
+	InstanceAddress string `json:"instanceAddress" envconfig:"kuma_inter_cp_catalog_instance_address"`
+	// Interval on which CP will send heartbeat to a leader.
+	HeartbeatInterval config_types.Duration `json:"heartbeatInterval" envconfig:"kuma_inter_cp_catalog_heartbeat_interval"`
+	// Interval on which CP will write all instances to a catalog.
+	WriterInterval config_types.Duration `json:"writerInterval" envconfig:"kuma_inter_cp_catalog_writer_interval"`
+}
+
+func (i *CatalogConfig) Validate() error {
+	if i.InstanceAddress != "" && !govalidator.IsDNSName(i.InstanceAddress) && !govalidator.IsIP(i.InstanceAddress) {
+		return errors.New(".InstanceAddress has to be valid IP or DNS address")
+	}
+	return nil
+}
+
+type InterCpServerConfig struct {
+	// Port on which Intercommunication CP server will listen
+	Port uint16 `json:"port" envconfig:"kuma_inter_cp_server_port"`
+	// TlsMinVersion defines the minimum TLS version to be used
+	TlsMinVersion string `json:"tlsMinVersion" envconfig:"kuma_inter_cp_server_tls_min_version"`
+	// TlsMaxVersion defines the maximum TLS version to be used
+	TlsMaxVersion string `json:"tlsMaxVersion" envconfig:"kuma_inter_cp_server_tls_max_version"`
+	// TlsCipherSuites defines the list of ciphers to use
+	TlsCipherSuites []string `json:"tlsCipherSuites" envconfig:"kuma_inter_cp_server_tls_cipher_suites"`
+}
+
+func (i *InterCpServerConfig) Validate() error {
+	var errs error
+	if i.Port == 0 {
+		errs = multierr.Append(errs, errors.New(".Port cannot be zero"))
+	}
+	if _, err := config_types.TLSVersion(i.TlsMinVersion); err != nil {
+		errs = multierr.Append(errs, errors.New(".TlsMinVersion "+err.Error()))
+	}
+	if _, err := config_types.TLSVersion(i.TlsMaxVersion); err != nil {
+		errs = multierr.Append(errs, errors.New(".TlsMaxVersion "+err.Error()))
+	}
+	if _, err := config_types.TLSCiphers(i.TlsCipherSuites); err != nil {
+		errs = multierr.Append(errs, errors.New(".TlsCipherSuites "+err.Error()))
+	}
+	return errs
+}
diff --git a/pkg/core/access/errors.go b/pkg/core/access/errors.go
new file mode 100644
index 0000000..5ec4bfb
--- /dev/null
+++ b/pkg/core/access/errors.go
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type AccessDeniedError struct {
+	Reason string
+}
+
+func (a *AccessDeniedError) Error() string {
+	return "access denied: " + a.Reason
+}
+
+func (a *AccessDeniedError) Is(err error) bool {
+	return reflect.TypeOf(a) == reflect.TypeOf(err)
+}
+
+func Validate(usernames map[string]struct{}, groups map[string]struct{}, user user.User, action string) error {
+	if _, ok := usernames[user.Name]; ok {
+		return nil
+	}
+	for _, group := range user.Groups {
+		if _, ok := groups[group]; ok {
+			return nil
+		}
+	}
+	return &AccessDeniedError{Reason: fmt.Sprintf("user %q cannot access %s", user, action)}
+}
diff --git a/pkg/core/access/metadata.go b/pkg/core/access/metadata.go
new file mode 100644
index 0000000..11f2925
--- /dev/null
+++ b/pkg/core/access/metadata.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import (
+	"context"
+
+	config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type ControlPlaneMetadataAccess interface {
+	ValidateView(ctx context.Context, user user.User) error
+}
+
+type staticMetadataAccess struct {
+	usernames map[string]struct{}
+	groups    map[string]struct{}
+}
+
+func NewStaticControlPlaneMetadataAccess(cfg config_access.ControlPlaneMetadataStaticAccessConfig) ControlPlaneMetadataAccess {
+	s := &staticMetadataAccess{
+		usernames: make(map[string]struct{}, len(cfg.Users)),
+		groups:    make(map[string]struct{}, len(cfg.Groups)),
+	}
+	for _, u := range cfg.Users {
+		s.usernames[u] = struct{}{}
+	}
+	for _, g := range cfg.Groups {
+		s.groups[g] = struct{}{}
+	}
+	return s
+}
+
+func (s staticMetadataAccess) ValidateView(ctx context.Context, user user.User) error {
+	return Validate(s.usernames, s.groups, user, "control-plane metadata")
+}
diff --git a/pkg/core/bootstrap/bootstrap.go b/pkg/core/bootstrap/bootstrap.go
index fc5bf34..b5248bc 100644
--- a/pkg/core/bootstrap/bootstrap.go
+++ b/pkg/core/bootstrap/bootstrap.go
@@ -23,24 +23,28 @@
 	"net/url"
 	"strings"
 	"sync"
-)
 
-import (
 	"dubbo.apache.org/dubbo-go/v3/common"
 	"dubbo.apache.org/dubbo-go/v3/common/extension"
 	"dubbo.apache.org/dubbo-go/v3/config/instance"
 	"dubbo.apache.org/dubbo-go/v3/config_center"
-
+	"github.com/apache/dubbo-kubernetes/pkg/core/access"
+	resources_access "github.com/apache/dubbo-kubernetes/pkg/core/resources/access"
+	"github.com/apache/dubbo-kubernetes/pkg/envoy/admin"
+	envoyadmin_access "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/access"
+	tokens_access "github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/access"
+	zone_access "github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/zone/access"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
 	"github.com/pkg/errors"
 
 	kube_ctrl "sigs.k8s.io/controller-runtime"
-)
 
-import (
 	dubbo_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
+
 	config_core "github.com/apache/dubbo-kubernetes/pkg/config/core"
 	"github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
 	"github.com/apache/dubbo-kubernetes/pkg/core"
+
 	config_manager "github.com/apache/dubbo-kubernetes/pkg/core/config/manager"
 	"github.com/apache/dubbo-kubernetes/pkg/core/consts"
 	"github.com/apache/dubbo-kubernetes/pkg/core/datasource"
@@ -48,28 +52,42 @@
 	"github.com/apache/dubbo-kubernetes/pkg/core/governance"
 	"github.com/apache/dubbo-kubernetes/pkg/core/logger"
 	"github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/condition_route"
+
 	dataplane_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/dataplane"
 	"github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/dynamic_config"
+
 	mapping_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/mapping"
+
 	mesh_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/mesh"
+
 	metadata_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/metadata"
 	"github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/tag_route"
 	"github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/zone"
+
 	core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+
 	dubbo_registry "github.com/apache/dubbo-kubernetes/pkg/core/registry"
 	"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
 	"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+
 	core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
 	"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
+
 	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+
 	core_runtime "github.com/apache/dubbo-kubernetes/pkg/core/runtime"
 	"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+
 	dds_context "github.com/apache/dubbo-kubernetes/pkg/dds/context"
 	"github.com/apache/dubbo-kubernetes/pkg/dp-server/server"
 	"github.com/apache/dubbo-kubernetes/pkg/events"
+
 	k8s_extensions "github.com/apache/dubbo-kubernetes/pkg/plugins/extensions/k8s"
+
 	mesh_cache "github.com/apache/dubbo-kubernetes/pkg/xds/cache/mesh"
+
 	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+
 	xds_server "github.com/apache/dubbo-kubernetes/pkg/xds/server"
 )
 
@@ -118,9 +136,16 @@
 
 	builder.WithDataSourceLoader(datasource.NewDataSourceLoader(builder.ReadOnlyResourceManager()))
 
+	//initializeCAManager
+	if err := initializeCaManagers(builder); err != nil {
+		return nil, err
+	}
+
 	leaderInfoComponent := &component.LeaderInfoComponent{}
 	builder.WithLeaderInfo(leaderInfoComponent)
 
+	caProvider, _ := secrets.NewCaProvider(builder.CaManagers())
+	builder.WithCAProvider(caProvider)
 	builder.WithDpServer(server.NewDpServer(*cfg.DpServer, func(writer http.ResponseWriter, request *http.Request) bool {
 		return true
 	}))
@@ -129,6 +154,28 @@
 	ddsContext := dds_context.DefaultContext(appCtx, resourceManager, cfg)
 	builder.WithDDSContext(ddsContext)
 
+	if cfg.DeployMode == config_core.UniversalMode {
+		//TODO: universalMode EnvoyAdminClient
+	} else {
+		builder.WithEnvoyAdminClient(admin.NewEnvoyAdminClient(
+			resourceManager,
+			builder.CaManagers(),
+			builder.Config().GetEnvoyAdminPort(),
+		))
+	}
+
+	builder.WithAccess(core_runtime.Access{
+		ResourceAccess:       resources_access.NewAdminResourceAccess(builder.Config().Access.Static.AdminResources),
+		DataplaneTokenAccess: tokens_access.NewStaticGenerateDataplaneTokenAccess(builder.Config().Access.Static.GenerateDPToken),
+		ZoneTokenAccess:      zone_access.NewStaticZoneTokenAccess(builder.Config().Access.Static.GenerateZoneToken),
+		EnvoyAdminAccess: envoyadmin_access.NewStaticEnvoyAdminAccess(
+			builder.Config().Access.Static.ViewConfigDump,
+			builder.Config().Access.Static.ViewStats,
+			builder.Config().Access.Static.ViewClusters,
+		),
+		ControlPlaneMetadataAccess: access.NewStaticControlPlaneMetadataAccess(builder.Config().Access.Static.ControlPlaneMetadata),
+	})
+
 	if err := initializeMeshCache(builder); err != nil {
 		return nil, err
 	}
@@ -486,3 +533,14 @@
 	builder.WithMeshCache(meshSnapshotCache)
 	return nil
 }
+
+func initializeCaManagers(builder *core_runtime.Builder) error {
+	for pluginName, caPlugin := range core_plugins.Plugins().CaPlugins() {
+		caManager, err := caPlugin.NewCaManager(builder, nil)
+		if err != nil {
+			return errors.Wrapf(err, "could not create CA manager for plugin %q", pluginName)
+		}
+		builder.WithCaManager(string(pluginName), caManager)
+	}
+	return nil
+}
diff --git a/pkg/core/ca/manager.go b/pkg/core/ca/manager.go
new file mode 100644
index 0000000..8ca456e
--- /dev/null
+++ b/pkg/core/ca/manager.go
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You 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 ca
+
+import mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+
+import (
+	"context"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/tls"
+)
+
+type Cert = []byte
+
+type KeyPair = tls.KeyPair
+
+// Manager manages CAs by creating CAs and generating certificate. It is created per CA type and then may be used for different CA instances of the same type
+type Manager interface {
+	// ValidateBackend validates that backend configuration is correct
+	ValidateBackend(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) error
+	// EnsureBackends ensures the given CA backends managed by this manager are available.
+	// Since the secrets are now explicitly children of mesh we need to pass the whole mesh object so that we can properly set the owner.
+	EnsureBackends(ctx context.Context, mesh model.Resource, backends []*mesh_proto.CertificateAuthorityBackend) error
+	// UsedSecrets returns a list of secrets that are used by the manager
+	UsedSecrets(mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]string, error)
+
+	// GetRootCert returns root certificates of the CA
+	GetRootCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]Cert, error)
+	// GenerateDataplaneCert generates cert for a dataplane with service tags
+	GenerateDataplaneCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend, tags mesh_proto.MultiValueTagSet) (KeyPair, error)
+}
+
+// Managers hold Manager instance for each type of backend available (by default: builtin, provided)
+type Managers = map[string]Manager
diff --git a/pkg/core/managers/apis/mesh/mesh_helpers.go b/pkg/core/managers/apis/mesh/mesh_helpers.go
new file mode 100644
index 0000000..596d0a3
--- /dev/null
+++ b/pkg/core/managers/apis/mesh/mesh_helpers.go
@@ -0,0 +1,28 @@
+package mesh
+
+import (
+	"context"
+
+	"github.com/pkg/errors"
+
+	"github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+)
+
+func EnsureCAs(ctx context.Context, caManagers core_ca.Managers, mesh *core_mesh.MeshResource, meshName string) error {
+	backendsForType := make(map[string][]*v1alpha1.CertificateAuthorityBackend)
+	for _, backend := range mesh.Spec.GetMtls().GetBackends() {
+		backendsForType[backend.Type] = append(backendsForType[backend.Type], backend)
+	}
+	for typ, backends := range backendsForType {
+		caManager, exist := caManagers[typ]
+		if !exist { // this should be caught by validator earlier
+			return errors.Errorf("CA manager for type %s does not exist", typ)
+		}
+		if err := caManager.EnsureBackends(ctx, mesh, backends); err != nil {
+			return errors.Wrapf(err, "could not ensure CA backends of type %s", typ)
+		}
+	}
+	return nil
+}
diff --git a/pkg/core/managers/apis/mesh/mesh_manager.go b/pkg/core/managers/apis/mesh/mesh_manager.go
index d1f79b4..4581c91 100644
--- a/pkg/core/managers/apis/mesh/mesh_manager.go
+++ b/pkg/core/managers/apis/mesh/mesh_manager.go
@@ -19,6 +19,8 @@
 
 import (
 	"context"
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	"github.com/apache/dubbo-kubernetes/pkg/core/validators"
 	"time"
 )
 
@@ -178,3 +180,26 @@
 	}
 	return meshes, nil
 }
+
+func ValidateMTLSBackends(ctx context.Context, caManagers core_ca.Managers, name string, resource *core_mesh.MeshResource) validators.ValidationError {
+	verr := validators.ValidationError{}
+	path := validators.RootedAt("mtls").Field("backends")
+
+	for idx, backend := range resource.Spec.GetMtls().GetBackends() {
+		caManager, exist := caManagers[backend.Type]
+		if !exist {
+			verr.AddViolationAt(path.Index(idx).Field("type"), "could not find installed plugin for this type")
+			return verr
+		}
+		//TODO:要不要支持skipvalidation?
+		if err := caManager.ValidateBackend(ctx, name, backend); err != nil {
+			if configErr, ok := err.(*validators.ValidationError); ok {
+				verr.AddErrorAt(path.Index(idx).Field("conf"), *configErr)
+			} else {
+				verr.AddViolationAt(path, err.Error())
+			}
+		}
+
+	}
+	return verr
+}
diff --git a/pkg/core/managers/apis/mesh/mesh_validator.go b/pkg/core/managers/apis/mesh/mesh_validator.go
index a195b24..387ece1 100644
--- a/pkg/core/managers/apis/mesh/mesh_validator.go
+++ b/pkg/core/managers/apis/mesh/mesh_validator.go
@@ -19,6 +19,10 @@
 
 import (
 	"context"
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/core/validators"
+	"github.com/pkg/errors"
 )
 
 import (
@@ -30,3 +34,59 @@
 	ValidateUpdate(ctx context.Context, previousMesh *core_mesh.MeshResource, newMesh *core_mesh.MeshResource) error
 	ValidateDelete(ctx context.Context, name string) error
 }
+
+type meshValidator struct {
+	CaManagers core_ca.Managers
+	Store      core_store.ResourceStore
+}
+
+func NewMeshValidator(caManagers core_ca.Managers, store core_store.ResourceStore) MeshValidator {
+	return &meshValidator{
+		CaManagers: caManagers,
+		Store:      store,
+	}
+}
+
+func (m *meshValidator) ValidateCreate(ctx context.Context, name string, resource *core_mesh.MeshResource) error {
+	var verr validators.ValidationError
+	if len(name) > 63 {
+		verr.AddViolation("name", "cannot be longer than 63 characters")
+	}
+	verr.Add(ValidateMTLSBackends(ctx, m.CaManagers, name, resource))
+	return verr.OrNil()
+}
+
+func (m *meshValidator) ValidateDelete(ctx context.Context, name string) error {
+	if err := ValidateNoActiveDP(ctx, name, m.Store); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (m *meshValidator) ValidateUpdate(ctx context.Context, previousMesh *core_mesh.MeshResource, newMesh *core_mesh.MeshResource) error {
+	var verr validators.ValidationError
+	verr.Add(m.validateMTLSBackendChange(previousMesh, newMesh))
+	verr.Add(ValidateMTLSBackends(ctx, m.CaManagers, newMesh.Meta.GetName(), newMesh))
+	return verr.OrNil()
+}
+
+func ValidateNoActiveDP(ctx context.Context, name string, store core_store.ResourceStore) error {
+	dps := core_mesh.DataplaneResourceList{}
+	validationErr := &validators.ValidationError{}
+	if err := store.List(ctx, &dps, core_store.ListByMesh(name)); err != nil {
+		return errors.Wrap(err, "unable to list Dataplanes")
+	}
+	if len(dps.Items) != 0 {
+		validationErr.AddViolation("mesh", "unable to delete mesh, there are still some dataplanes attached")
+		return validationErr
+	}
+	return nil
+}
+
+func (m *meshValidator) validateMTLSBackendChange(previousMesh *core_mesh.MeshResource, newMesh *core_mesh.MeshResource) validators.ValidationError {
+	verr := validators.ValidationError{}
+	if previousMesh.MTLSEnabled() && newMesh.MTLSEnabled() && previousMesh.Spec.GetMtls().GetEnabledBackend() != newMesh.Spec.GetMtls().GetEnabledBackend() {
+		verr.AddViolation("mtls.enabledBackend", "Changing CA when mTLS is enabled is forbidden. Disable mTLS first and then change the CA")
+	}
+	return verr
+}
diff --git a/pkg/core/plugins/interfaces.go b/pkg/core/plugins/interfaces.go
index 63e0a40..4ff79ae 100644
--- a/pkg/core/plugins/interfaces.go
+++ b/pkg/core/plugins/interfaces.go
@@ -18,16 +18,18 @@
 package plugins
 
 import (
-	"github.com/pkg/errors"
-)
+	"context"
 
-import (
+	"github.com/apache/dubbo-kubernetes/pkg/api-server/authn"
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
 	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
 	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
 	core_runtime "github.com/apache/dubbo-kubernetes/pkg/core/runtime"
+	secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
 	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
 	"github.com/apache/dubbo-kubernetes/pkg/events"
 	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+	"github.com/pkg/errors"
 )
 
 type Plugin interface{}
@@ -96,5 +98,52 @@
 
 type CaPlugin interface {
 	Plugin
-	NewCaManager(PluginContext, PluginConfig)
+	NewCaManager(PluginContext, PluginConfig) (core_ca.Manager, error)
+}
+
+// AuthnAPIServerPlugin is responsible for providing authenticator for API Server.
+
+type SecretStorePlugin interface {
+	Plugin
+	NewSecretStore(PluginContext, PluginConfig) (secret_store.SecretStore, error)
+}
+
+// AuthnAPIServerPlugin is responsible for providing authenticator for API Server.
+type AuthnAPIServerPlugin interface {
+	Plugin
+	NewAuthenticator(PluginContext) (authn.Authenticator, error)
+}
+
+type MatchedPoliciesOption func(*MatchedPoliciesConfig)
+
+func IncludeShadow() MatchedPoliciesOption {
+	return func(cfg *MatchedPoliciesConfig) {
+		cfg.IncludeShadow = true
+	}
+}
+
+type MatchedPoliciesConfig struct {
+	IncludeShadow bool
+}
+
+func NewMatchedPoliciesConfig(opts ...MatchedPoliciesOption) *MatchedPoliciesConfig {
+	cfg := &MatchedPoliciesConfig{}
+	for _, opt := range opts {
+		opt(cfg)
+	}
+	return cfg
+}
+
+type EgressPolicyPlugin interface {
+	PolicyPlugin
+	// EgressMatchedPolicies returns all the policies of the plugins' type matching the external service that
+	// should be applied on the zone egress.
+	EgressMatchedPolicies(tags map[string]string, resources xds_context.Resources, opts ...MatchedPoliciesOption) (core_xds.TypedMatchingPolicies, error)
+}
+
+// ProxyPlugin a plugin to modify the proxy. This happens before any `PolicyPlugin` or any envoy generation. and it is applied both for Dataplanes and ZoneProxies
+type ProxyPlugin interface {
+	Plugin
+	// Apply mutate the proxy as needed.
+	Apply(ctx context.Context, meshCtx xds_context.MeshContext, proxy *core_xds.Proxy) error
 }
diff --git a/pkg/core/plugins/registry.go b/pkg/core/plugins/registry.go
index ba6840a..bca54c0 100644
--- a/pkg/core/plugins/registry.go
+++ b/pkg/core/plugins/registry.go
@@ -35,6 +35,7 @@
 	runtimePlugin       pluginType = "runtime"
 	policyPlugin        pluginType = "policy"
 	caPlugin            pluginType = "ca"
+	secretStorePlugin   pluginType = "secret-store"
 )
 
 type PluginName string
@@ -48,7 +49,8 @@
 	Nacos       PluginName = "nacos"
 	MySQL       PluginName = "mysql"
 
-	CaBuiltin PluginName = "builtin"
+	CaBuiltin  PluginName = "builtin"
+	CaProvided PluginName = "provided"
 )
 
 type RegisteredPolicyPlugin struct {
@@ -62,6 +64,8 @@
 	ConfigStore(name PluginName) (ConfigStorePlugin, error)
 	RuntimePlugins() map[PluginName]RuntimePlugin
 	PolicyPlugins([]PluginName) []RegisteredPolicyPlugin
+	CaPlugins() map[PluginName]CaPlugin
+	SecretStore(name PluginName) (SecretStorePlugin, error)
 }
 
 type RegistryMutator interface {
@@ -80,6 +84,8 @@
 		configStore:        make(map[PluginName]ConfigStorePlugin),
 		runtime:            make(map[PluginName]RuntimePlugin),
 		registeredPolicies: make(map[PluginName]PolicyPlugin),
+		ca:                 make(map[PluginName]CaPlugin),
+		secretStore:        make(map[PluginName]SecretStorePlugin),
 	}
 }
 
@@ -91,6 +97,8 @@
 	configStore        map[PluginName]ConfigStorePlugin
 	runtime            map[PluginName]RuntimePlugin
 	registeredPolicies map[PluginName]PolicyPlugin
+	ca                 map[PluginName]CaPlugin
+	secretStore        map[PluginName]SecretStorePlugin
 }
 
 func (r *registry) ResourceStore(name PluginName) (ResourceStorePlugin, error) {
@@ -109,6 +117,10 @@
 	}
 }
 
+func (r *registry) CaPlugins() map[PluginName]CaPlugin {
+	return r.ca
+}
+
 func (r *registry) RuntimePlugins() map[PluginName]RuntimePlugin {
 	return r.runtime
 }
@@ -175,6 +187,12 @@
 		}
 		r.registeredPolicies[name] = policy
 	}
+	if cp, ok := plugin.(CaPlugin); ok {
+		if old, exists := r.ca[name]; exists {
+			return pluginAlreadyRegisteredError(caPlugin, name, old, cp)
+		}
+		r.ca[name] = cp
+	}
 	return nil
 }
 
@@ -186,3 +204,11 @@
 	return errors.Errorf("plugin with type=%q and name=%s has already been registered: old=%#v new=%#v",
 		typ, name, old, new)
 }
+
+func (r *registry) SecretStore(name PluginName) (SecretStorePlugin, error) {
+	if p, ok := r.secretStore[name]; ok {
+		return p, nil
+	} else {
+		return nil, noSuchPluginError(secretStorePlugin, name)
+	}
+}
diff --git a/pkg/core/resources/access/admin_resource_access.go b/pkg/core/resources/access/admin_resource_access.go
new file mode 100644
index 0000000..f58f9d2
--- /dev/null
+++ b/pkg/core/resources/access/admin_resource_access.go
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import (
+	"context"
+	"fmt"
+
+	config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type adminResourceAccess struct {
+	usernames map[string]struct{}
+	groups    map[string]struct{}
+}
+
+func NewAdminResourceAccess(cfg config_access.AdminResourcesStaticAccessConfig) ResourceAccess {
+	a := &adminResourceAccess{
+		usernames: make(map[string]struct{}),
+		groups:    make(map[string]struct{}),
+	}
+	for _, user := range cfg.Users {
+		a.usernames[user] = struct{}{}
+	}
+
+	for _, group := range cfg.Groups {
+		a.groups[group] = struct{}{}
+	}
+	return a
+}
+
+var _ ResourceAccess = &adminResourceAccess{}
+
+func (a *adminResourceAccess) ValidateCreate(ctx context.Context, _ model.ResourceKey, _ model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user user.User) error {
+	return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (a *adminResourceAccess) ValidateUpdate(ctx context.Context, _ model.ResourceKey, _ model.ResourceSpec, _ model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user user.User) error {
+	return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (a *adminResourceAccess) ValidateDelete(ctx context.Context, _ model.ResourceKey, _ model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user user.User) error {
+	return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (a *adminResourceAccess) ValidateList(ctx context.Context, _ string, descriptor model.ResourceTypeDescriptor, user user.User) error {
+	return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (a *adminResourceAccess) ValidateGet(ctx context.Context, _ model.ResourceKey, descriptor model.ResourceTypeDescriptor, user user.User) error {
+	return a.validateAdminAccess(ctx, user, descriptor)
+}
+
+func (r *adminResourceAccess) validateAdminAccess(_ context.Context, u user.User, descriptor model.ResourceTypeDescriptor) error {
+	if !descriptor.AdminOnly {
+		return nil
+	}
+	return access.Validate(r.usernames, r.groups, u, fmt.Sprintf("the resource of type %q", descriptor.Name))
+}
diff --git a/pkg/core/resources/access/resource_access.go b/pkg/core/resources/access/resource_access.go
new file mode 100644
index 0000000..2a6c679
--- /dev/null
+++ b/pkg/core/resources/access/resource_access.go
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import (
+	"context"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type ResourceAccess interface {
+	ValidateCreate(ctx context.Context, key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user user.User) error
+	ValidateUpdate(ctx context.Context, key model.ResourceKey, currentSpec model.ResourceSpec, newSpec model.ResourceSpec, desc model.ResourceTypeDescriptor, user user.User) error
+	ValidateDelete(ctx context.Context, key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user user.User) error
+	ValidateList(ctx context.Context, mesh string, desc model.ResourceTypeDescriptor, user user.User) error
+	ValidateGet(ctx context.Context, key model.ResourceKey, desc model.ResourceTypeDescriptor, user user.User) error
+}
diff --git a/pkg/core/resources/apis/system/global_secret.go b/pkg/core/resources/apis/system/global_secret.go
new file mode 100644
index 0000000..45c6e1c
--- /dev/null
+++ b/pkg/core/resources/apis/system/global_secret.go
@@ -0,0 +1,122 @@
+package system
+
+import (
+	"errors"
+
+	system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
+)
+
+const (
+	GlobalSecretType model.ResourceType = "GlobalSecret"
+)
+
+var _ model.Resource = &GlobalSecretResource{}
+
+type GlobalSecretResource struct {
+	Meta model.ResourceMeta
+	Spec *system_proto.Secret
+}
+
+func NewGlobalSecretResource() *GlobalSecretResource {
+	return &GlobalSecretResource{
+		Spec: &system_proto.Secret{},
+	}
+}
+
+func (t *GlobalSecretResource) GetMeta() model.ResourceMeta {
+	return t.Meta
+}
+
+func (t *GlobalSecretResource) SetMeta(m model.ResourceMeta) {
+	t.Meta = m
+}
+
+func (t *GlobalSecretResource) GetSpec() model.ResourceSpec {
+	return t.Spec
+}
+
+func (t *GlobalSecretResource) SetSpec(spec model.ResourceSpec) error {
+	value, ok := spec.(*system_proto.Secret)
+	if !ok {
+		return errors.New("invalid type of spec")
+	} else {
+		t.Spec = value
+		return nil
+	}
+}
+
+func (t *GlobalSecretResource) GetStatus() model.ResourceStatus {
+	return nil
+}
+
+func (t *GlobalSecretResource) SetStatus(_ model.ResourceStatus) error {
+	return errors.New("status not supported")
+}
+
+func (t *GlobalSecretResource) Validate() error {
+	return nil
+}
+
+func (t *GlobalSecretResource) Descriptor() model.ResourceTypeDescriptor {
+	return GlobalSecretResourceTypeDescriptor
+}
+
+var _ model.ResourceList = &GlobalSecretResourceList{}
+
+type GlobalSecretResourceList struct {
+	Items      []*GlobalSecretResource
+	Pagination model.Pagination
+}
+
+func (l *GlobalSecretResourceList) GetItems() []model.Resource {
+	res := make([]model.Resource, len(l.Items))
+	for i, elem := range l.Items {
+		res[i] = elem
+	}
+	return res
+}
+
+func (l *GlobalSecretResourceList) GetItemType() model.ResourceType {
+	return GlobalSecretType
+}
+
+func (l *GlobalSecretResourceList) NewItem() model.Resource {
+	return NewGlobalSecretResource()
+}
+
+func (l *GlobalSecretResourceList) AddItem(r model.Resource) error {
+	if trr, ok := r.(*GlobalSecretResource); ok {
+		l.Items = append(l.Items, trr)
+		return nil
+	} else {
+		return model.ErrorInvalidItemType((*GlobalSecretResource)(nil), r)
+	}
+}
+
+func (l *GlobalSecretResourceList) GetPagination() *model.Pagination {
+	return &l.Pagination
+}
+
+func (l *GlobalSecretResourceList) SetPagination(p model.Pagination) {
+	l.Pagination = p
+}
+
+var GlobalSecretResourceTypeDescriptor model.ResourceTypeDescriptor
+
+func init() {
+	GlobalSecretResourceTypeDescriptor = model.ResourceTypeDescriptor{
+		Name:            GlobalSecretType,
+		Resource:        NewGlobalSecretResource(),
+		ResourceList:    &GlobalSecretResourceList{},
+		ReadOnly:        false,
+		AdminOnly:       true,
+		Scope:           model.ScopeGlobal,
+		DDSFlags:        model.GlobalToAllZonesFlag,
+		WsPath:          "global-secrets",
+		DubboctlArg:     "global-secret",
+		DubboctlListArg: "global-secrets",
+	}
+	registry.RegisterType(GlobalSecretResourceTypeDescriptor)
+}
diff --git a/pkg/core/resources/model/resource.go b/pkg/core/resources/model/resource.go
index 01b425c..5bdc2f5 100644
--- a/pkg/core/resources/model/resource.go
+++ b/pkg/core/resources/model/resource.go
@@ -103,6 +103,7 @@
 }
 
 type ResourceSpec interface{}
+type ResourceStatus interface{}
 
 type Resource interface {
 	GetMeta() ResourceMeta
diff --git a/pkg/core/rest/errors/error_handler.go b/pkg/core/rest/errors/error_handler.go
new file mode 100644
index 0000000..60fd123
--- /dev/null
+++ b/pkg/core/rest/errors/error_handler.go
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 errors
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"github.com/emicklei/go-restful/v3"
+	"github.com/go-logr/logr"
+	"go.opentelemetry.io/otel/trace"
+
+	api_server_types "github.com/apache/dubbo-kubernetes/pkg/api-server/types"
+	"github.com/apache/dubbo-kubernetes/pkg/core/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model/rest"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/core/rest/errors/types"
+	"github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+	"github.com/apache/dubbo-kubernetes/pkg/core/validators"
+	kuma_log "github.com/apache/dubbo-kubernetes/pkg/log"
+)
+
+func HandleError(ctx context.Context, response *restful.Response, err error, title string) {
+	log := kuma_log.AddFieldsFromCtx(logr.FromContextOrDiscard(ctx), ctx, context.Background())
+
+	var kumaErr *types.Error
+	switch {
+	case store.IsResourceNotFound(err) || errors.Is(err, &NotFound{}):
+		kumaErr = &types.Error{
+			Status: 404,
+			Title:  title,
+			Detail: "Not found",
+		}
+	case errors.Is(err, &rest.InvalidResourceError{}), errors.Is(err, &registry.InvalidResourceTypeError{}), errors.Is(err, &store.PreconditionError{}), errors.Is(err, &BadRequest{}):
+		kumaErr = &types.Error{
+			Status: 400,
+			Title:  "Bad Request",
+			Detail: err.Error(),
+		}
+	case manager.IsMeshNotFound(err):
+		kumaErr = &types.Error{
+			Status: 400,
+			Title:  title,
+			Detail: "Mesh is not found",
+			InvalidParameters: []types.InvalidParameter{
+				{
+					Field:  "mesh",
+					Reason: fmt.Sprintf("mesh of name %s is not found", err.(*manager.MeshNotFoundError).Mesh),
+				},
+			},
+		}
+	case validators.IsValidationError(err):
+		kumaErr = &types.Error{
+			Status: 400,
+			Title:  title,
+			Detail: "Resource is not valid",
+		}
+		for _, violation := range err.(*validators.ValidationError).Violations {
+			kumaErr.InvalidParameters = append(kumaErr.InvalidParameters, types.InvalidParameter{
+				Field:  violation.Field,
+				Reason: violation.Message,
+			})
+		}
+	case err == store.ErrorInvalidOffset:
+		kumaErr = &types.Error{
+			Status: 400,
+			Title:  title,
+			Detail: "Invalid offset",
+			InvalidParameters: []types.InvalidParameter{
+				{
+					Field:  "offset",
+					Reason: "Invalid format",
+				},
+			},
+		}
+	case errors.Is(err, &api_server_types.InvalidPageSizeError{}):
+		kumaErr = &types.Error{
+			Status: 400,
+			Title:  title,
+			Detail: "Invalid page size",
+			InvalidParameters: []types.InvalidParameter{
+				{
+					Field:  "size",
+					Reason: err.Error(),
+				},
+			},
+		}
+	case tokens.IsSigningKeyNotFound(err):
+		kumaErr = &types.Error{
+			Status: 404,
+			Title:  "Signing Key not found",
+			Detail: err.Error(),
+		}
+	case errors.Is(err, &MethodNotAllowed{}):
+		kumaErr = &types.Error{
+			Status: 405,
+			Title:  "Method not Allowed",
+			Detail: err.Error(),
+		}
+	case errors.Is(err, &Conflict{}) || errors.Is(err, &store.ResourceConflictError{}):
+		kumaErr = &types.Error{
+			Status: 409,
+			Title:  "Conflict",
+			Detail: err.Error(),
+		}
+	case errors.Is(err, &ServiceUnavailable{}):
+		kumaErr = &types.Error{
+			Status: 503,
+			Title:  "Service unavailable",
+			Detail: err.Error(),
+		}
+	case errors.Is(err, &access.AccessDeniedError{}):
+		var accessErr *access.AccessDeniedError
+		errors.As(err, &accessErr)
+		kumaErr = &types.Error{
+			Status: 403,
+			Title:  "Access Denied",
+			Detail: accessErr.Reason,
+		}
+	case errors.Is(err, &Unauthenticated{}):
+		var unauthenticated *Unauthenticated
+		errors.As(err, &unauthenticated)
+		kumaErr = &types.Error{
+			Status: 401,
+			Title:  title,
+			Detail: unauthenticated.Error(),
+		}
+	case err == tokens.IssuerDisabled:
+		kumaErr = &types.Error{
+			Status: 400,
+			Title:  title,
+			Detail: err.Error(),
+		}
+	case errors.As(err, &kumaErr):
+	default:
+		log.Error(err, title)
+		kumaErr = &types.Error{
+			Status: 500,
+			Title:  title,
+			Detail: "Internal Server Error",
+		}
+	}
+	if ctx != nil {
+		span := trace.SpanFromContext(ctx)
+		span.RecordError(err, trace.WithStackTrace(true))
+		if span.IsRecording() {
+			kumaErr.Instance = span.SpanContext().TraceID().String()
+		}
+	}
+	// Fix to handle legacy errors
+	kumaErr.Type = "/std-errors"
+	kumaErr.Details = kumaErr.Detail
+	for _, ip := range kumaErr.InvalidParameters {
+		kumaErr.Causes = append(kumaErr.Causes, types.Cause{Field: ip.Field, Message: ip.Reason})
+	}
+	if err := response.WriteHeaderAndJson(kumaErr.Status, kumaErr, "application/json"); err != nil {
+		log.Error(err, "Could not write the error response")
+	}
+}
diff --git a/pkg/core/rest/errors/errors.go b/pkg/core/rest/errors/errors.go
new file mode 100644
index 0000000..9ff372d
--- /dev/null
+++ b/pkg/core/rest/errors/errors.go
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 errors
+
+import (
+	"fmt"
+	"reflect"
+)
+
+type Unauthenticated struct{}
+
+func (e *Unauthenticated) Error() string {
+	return "Unauthenticated"
+}
+
+type MethodNotAllowed struct{}
+
+func (e *MethodNotAllowed) Error() string {
+	return "Method not allowed"
+}
+
+type Conflict struct{}
+
+func (e *Conflict) Error() string {
+	return "Conflict"
+}
+
+type ServiceUnavailable struct{}
+
+func (e *ServiceUnavailable) Error() string {
+	return "Service unavailable"
+}
+
+type BadRequest struct {
+	msg string
+}
+
+func NewBadRequestError(msg string) error {
+	return &BadRequest{msg: msg}
+}
+
+func (e *BadRequest) Error() string {
+	if e.msg == "" {
+		return "bad request"
+	}
+	return fmt.Sprintf("bad request: %s", e.msg)
+}
+
+func (e *BadRequest) Is(err error) bool {
+	return reflect.TypeOf(e) == reflect.TypeOf(err)
+}
+
+func NewNotFoundError(msg string) error {
+	return &NotFound{msg: msg}
+}
+
+type NotFound struct {
+	msg string
+}
+
+func (e *NotFound) Error() string {
+	if e.msg == "" {
+		return "not found"
+	}
+	return fmt.Sprintf("not found: %s", e.msg)
+}
+
+func (e *NotFound) Is(err error) bool {
+	return reflect.TypeOf(e) == reflect.TypeOf(err)
+}
diff --git a/pkg/core/rest/errors/types/error.go b/pkg/core/rest/errors/types/error.go
new file mode 100644
index 0000000..5e4dade
--- /dev/null
+++ b/pkg/core/rest/errors/types/error.go
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 types
+
+import "fmt"
+
+// Error following https://kong-aip.netlify.app/aip/193/
+type Error struct {
+	// Type a unique identifier for this error.
+	Type string `json:"type"`
+	// Status The HTTP status code of the error.
+	Status int `json:"status"`
+	// Title A short, human-readable summary of the problem.
+	// It should not change between occurrences of a problem, except for localization.
+	// Should be provided as "Sentence case" for direct use in the UI
+	Title string `json:"title"`
+	// Detail A human readable explanation specific to this occurrence of the problem.
+	Detail string `json:"detail"`
+	// Instance Used to return the correlation ID back to the user if present
+	Instance string `json:"instance,omitempty"`
+	// InvalidParameters
+	InvalidParameters []InvalidParameter `json:"invalid_parameters,omitempty"`
+
+	// Deprecated
+	Details string `json:"details"`
+	// Deprecated
+	Causes []Cause `json:"causes,omitempty"`
+}
+
+type InvalidParameter struct {
+	Field   string   `json:"field"`
+	Reason  string   `json:"reason"`
+	Rule    string   `json:"rule,omitempty"`
+	Choices []string `json:"choices,omitempty"`
+}
+
+func (e *Error) Error() string {
+	msg := fmt.Sprintf("%s (%s)", e.Title, e.Detail)
+	for _, cause := range e.InvalidParameters {
+		msg += fmt.Sprintf(";%s=%s ", cause.Field, cause.Reason)
+	}
+	return msg
+}
+
+type Cause struct {
+	Field   string `json:"field"`
+	Message string `json:"message"`
+}
diff --git a/pkg/core/runtime/builder.go b/pkg/core/runtime/builder.go
index 6b3a3e9..8511da1 100644
--- a/pkg/core/runtime/builder.go
+++ b/pkg/core/runtime/builder.go
@@ -23,29 +23,36 @@
 	"os"
 	"sync"
 	"time"
-)
 
-import (
 	"dubbo.apache.org/dubbo-go/v3/config_center"
 	"dubbo.apache.org/dubbo-go/v3/metadata/report"
+	"github.com/apache/dubbo-kubernetes/pkg/envoy/admin"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
+
 	dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry"
-
 	"github.com/pkg/errors"
-)
 
-import (
+	api_server "github.com/apache/dubbo-kubernetes/pkg/api-server/customization"
+
 	dubbo_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
 	"github.com/apache/dubbo-kubernetes/pkg/core"
+
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+
 	config_manager "github.com/apache/dubbo-kubernetes/pkg/core/config/manager"
 	"github.com/apache/dubbo-kubernetes/pkg/core/datasource"
 	"github.com/apache/dubbo-kubernetes/pkg/core/dns/lookup"
 	"github.com/apache/dubbo-kubernetes/pkg/core/governance"
 	"github.com/apache/dubbo-kubernetes/pkg/core/reg_client"
 	"github.com/apache/dubbo-kubernetes/pkg/core/registry"
+
 	core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+
 	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
 	"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+
 	dds_context "github.com/apache/dubbo-kubernetes/pkg/dds/context"
+
 	dp_server "github.com/apache/dubbo-kubernetes/pkg/dp-server/server"
 	"github.com/apache/dubbo-kubernetes/pkg/events"
 	"github.com/apache/dubbo-kubernetes/pkg/xds/cache/mesh"
@@ -57,6 +64,7 @@
 	ResourceStore() core_store.CustomizableResourceStore
 	Transactions() core_store.Transactions
 	ConfigStore() core_store.ResourceStore
+	DataSourceLoader() datasource.Loader
 	ResourceManager() core_manager.CustomizableResourceManager
 	Config() dubbo_cp.Config
 	RegistryCenter() dubboRegistry.Registry
@@ -72,6 +80,7 @@
 	EventBus() events.EventBus
 	DpServer() *dp_server.DpServer
 	DataplaneCache() *sync.Map
+	CAProvider() secrets.CaProvider
 	DDSContext() *dds_context.Context
 	ResourceValidators() ResourceValidators
 	AppRegCtx() *registry.ApplicationContext
@@ -91,10 +100,14 @@
 	rom                  core_manager.ReadOnlyResourceManager
 	ext                  context.Context
 	meshCache            *mesh.Cache
+	eac                  admin.EnvoyAdminClient
 	lif                  lookup.LookupIPFunc
 	configm              config_manager.ConfigManager
 	leadInfo             component.LeaderInfo
 	erf                  events.EventBus
+	apim                 api_server.APIManager
+	cam                  core_ca.Managers
+	acc                  Access
 	dsl                  datasource.Loader
 	dps                  *dp_server.DpServer
 	registryCenter       dubboRegistry.Registry
@@ -106,6 +119,7 @@
 	ddsctx               *dds_context.Context
 	appCtx               context.Context
 	dCache               *sync.Map
+	cap                  secrets.CaProvider
 	regClient            reg_client.RegClient
 	serviceDiscover      dubboRegistry.ServiceDiscovery
 	appRegCtx            *registry.ApplicationContext
@@ -113,6 +127,14 @@
 	*runtimeInfo
 }
 
+func (b *Builder) Access() Access {
+	return b.acc
+}
+
+func (b *Builder) CAProvider() secrets.CaProvider {
+	return b.cap
+}
+
 func BuilderFor(appCtx context.Context, cfg dubbo_cp.Config) (*Builder, error) {
 	hostname, err := os.Hostname()
 	if err != nil {
@@ -157,6 +179,16 @@
 	return b
 }
 
+func (b *Builder) WithEnvoyAdminClient(eac admin.EnvoyAdminClient) *Builder {
+	b.eac = eac
+	return b
+}
+
+func (b *Builder) WithAccess(acc Access) *Builder {
+	b.acc = acc
+	return b
+}
+
 func (b *Builder) WithConfigStore(cs core_store.ResourceStore) *Builder {
 	b.cs = cs
 	return b
@@ -226,6 +258,15 @@
 	return b.meshCache
 }
 
+func (b *Builder) WithCAProvider(cap secrets.CaProvider) *Builder {
+	b.cap = cap
+	return b
+}
+
+func (b *Builder) CaManagers() core_ca.Managers {
+	return b.cam
+}
+
 func (b *Builder) WithDDSContext(ddsctx *dds_context.Context) *Builder {
 	b.ddsctx = ddsctx
 	return b
@@ -241,6 +282,16 @@
 	return b
 }
 
+func (b *Builder) WithCaManagers(cam core_ca.Managers) *Builder {
+	b.cam = cam
+	return b
+}
+
+func (b *Builder) WithCaManager(name string, cam core_ca.Manager) *Builder {
+	b.cam[name] = cam
+	return b
+}
+
 func (b *Builder) WithRegistryCenter(rg dubboRegistry.Registry) *Builder {
 	b.registryCenter = rg
 	return b
@@ -271,6 +322,10 @@
 	return b
 }
 
+func (b *Builder) APIManager() api_server.APIManager {
+	return b.apim
+}
+
 func (b *Builder) Build() (Runtime, error) {
 	if b.cm == nil {
 		return nil, errors.Errorf("ComponentManager has not been configured")
@@ -302,6 +357,12 @@
 	if b.meshCache == nil {
 		return nil, errors.Errorf("MeshCache has not been configured")
 	}
+	if b.acc == (Access{}) {
+		return nil, errors.Errorf("Access has not been configured")
+	}
+	if b.cap == nil {
+		return nil, errors.Errorf("CaProvider has not been configured")
+	}
 
 	return &runtime{
 		RuntimeInfo: b.runtimeInfo,
@@ -327,6 +388,7 @@
 			appCtx:               b.appCtx,
 			meshCache:            b.meshCache,
 			regClient:            b.regClient,
+			cap:                  b.cap,
 		},
 		Manager: b.cm,
 	}, nil
@@ -396,6 +458,10 @@
 	return b.cfg
 }
 
+func (b *Builder) DataSourceLoader() datasource.Loader {
+	return b.dsl
+}
+
 func (b *Builder) DDSContext() *dds_context.Context {
 	return b.ddsctx
 }
diff --git a/pkg/core/runtime/runtime.go b/pkg/core/runtime/runtime.go
index 4d380e1..cf1a80a 100644
--- a/pkg/core/runtime/runtime.go
+++ b/pkg/core/runtime/runtime.go
@@ -21,30 +21,47 @@
 	"context"
 	"sync"
 	"time"
-)
 
-import (
 	"dubbo.apache.org/dubbo-go/v3/config_center"
 	"dubbo.apache.org/dubbo-go/v3/metadata/report"
-	dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry"
-)
+	"github.com/apache/dubbo-kubernetes/pkg/core/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	"github.com/apache/dubbo-kubernetes/pkg/multitenant"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
 
-import (
+	dubboRegistry "dubbo.apache.org/dubbo-go/v3/registry"
+
 	dubbo_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
 	"github.com/apache/dubbo-kubernetes/pkg/config/core"
+
 	config_manager "github.com/apache/dubbo-kubernetes/pkg/core/config/manager"
 	"github.com/apache/dubbo-kubernetes/pkg/core/governance"
+
 	managers_dataplane "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/dataplane"
+
 	managers_mesh "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/mesh"
 	"github.com/apache/dubbo-kubernetes/pkg/core/reg_client"
 	"github.com/apache/dubbo-kubernetes/pkg/core/registry"
+
+	resources_access "github.com/apache/dubbo-kubernetes/pkg/core/resources/access"
+
 	core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+
 	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
 	"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+
 	dds_context "github.com/apache/dubbo-kubernetes/pkg/dds/context"
+
 	dp_server "github.com/apache/dubbo-kubernetes/pkg/dp-server/server"
+
+	envoyadmin_access "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/access"
 	"github.com/apache/dubbo-kubernetes/pkg/events"
+
+	tokens_access "github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/access"
+
+	zone_access "github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/zone/access"
 	"github.com/apache/dubbo-kubernetes/pkg/xds/cache/mesh"
+
 	xds_runtime "github.com/apache/dubbo-kubernetes/pkg/xds/runtime"
 )
 
@@ -85,10 +102,22 @@
 	AdminRegistry() *registry.Registry
 	RegClient() reg_client.RegClient
 	ResourceValidators() ResourceValidators
+	Tenants() multitenant.Tenants
 	// AppContext returns a context.Context which tracks the lifetime of the apps, it gets cancelled when the app is starting to shutdown.
 	AppContext() context.Context
 	XDS() xds_runtime.XDSRuntimeContext
 	MeshCache() *mesh.Cache
+	CAProvider() secrets.CaProvider
+	CaManagers() ca.Managers
+	Access() Access
+}
+
+type Access struct {
+	ResourceAccess             resources_access.ResourceAccess
+	DataplaneTokenAccess       tokens_access.DataplaneTokenAccess
+	ZoneTokenAccess            zone_access.ZoneTokenAccess
+	EnvoyAdminAccess           envoyadmin_access.EnvoyAdminAccess
+	ControlPlaneMetadataAccess access.ControlPlaneMetadataAccess
 }
 
 type ResourceValidators struct {
@@ -171,7 +200,27 @@
 	appCtx               context.Context
 	meshCache            *mesh.Cache
 	regClient            reg_client.RegClient
+	tenants              multitenant.Tenants
 	serviceDiscovery     dubboRegistry.ServiceDiscovery
+	cap                  secrets.CaProvider
+	cam                  ca.Managers
+	acc                  Access
+}
+
+func (rc *runtimeContext) Access() Access {
+	return rc.acc
+}
+
+func (rc *runtimeContext) CaManagers() ca.Managers {
+	return rc.cam
+}
+
+func (rc *runtimeContext) CAProvider() secrets.CaProvider {
+	return rc.cap
+}
+
+func (b *runtimeContext) Tenants() multitenant.Tenants {
+	return b.tenants
 }
 
 func (b *runtimeContext) RegClient() reg_client.RegClient {
diff --git a/pkg/core/secrets/cipher/cipher.go b/pkg/core/secrets/cipher/cipher.go
new file mode 100644
index 0000000..b4b5fd0
--- /dev/null
+++ b/pkg/core/secrets/cipher/cipher.go
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 cipher
+
+type Cipher interface {
+	Encryptor
+	Decryptor
+}
+
+type Encryptor interface {
+	Encrypt([]byte) ([]byte, error)
+}
+
+type Decryptor interface {
+	Decrypt([]byte) ([]byte, error)
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/core/secrets/cipher/none.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/core/secrets/cipher/none.go
index 987f638..a9f6bd5 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/core/secrets/cipher/none.go
@@ -15,25 +15,20 @@
  * limitations under the License.
  */
 
-package dubbo
+package cipher
 
-import (
-	"archive/zip"
-	"bytes"
-)
-
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func None() Cipher {
+	return &none{}
 }
 
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+var _ Cipher = &none{}
+
+type none struct{}
+
+func (_ none) Encrypt(data []byte) ([]byte, error) {
+	return data, nil
+}
+
+func (_ none) Decrypt(data []byte) ([]byte, error) {
+	return data, nil
+}
diff --git a/pkg/core/secrets/cipher/todo.go b/pkg/core/secrets/cipher/todo.go
new file mode 100644
index 0000000..8438b41
--- /dev/null
+++ b/pkg/core/secrets/cipher/todo.go
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 cipher
+
+func TODO() Cipher {
+	return None()
+}
diff --git a/pkg/core/secrets/manager/global_manager.go b/pkg/core/secrets/manager/global_manager.go
new file mode 100644
index 0000000..df6e317
--- /dev/null
+++ b/pkg/core/secrets/manager/global_manager.go
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 manager
+
+import (
+	"context"
+	"time"
+
+	secret_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	secret_cipher "github.com/apache/dubbo-kubernetes/pkg/core/secrets/cipher"
+	secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+)
+
+func NewGlobalSecretManager(secretStore secret_store.SecretStore, cipher secret_cipher.Cipher) manager.ResourceManager {
+	return &globalSecretManager{
+		secretStore: secretStore,
+		cipher:      cipher,
+	}
+}
+
+var _ manager.ResourceManager = &globalSecretManager{}
+
+type globalSecretManager struct {
+	secretStore secret_store.SecretStore
+	cipher      secret_cipher.Cipher
+}
+
+func (s *globalSecretManager) Get(ctx context.Context, resource model.Resource, fs ...core_store.GetOptionsFunc) error {
+	secret, ok := resource.(*secret_model.GlobalSecretResource)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	if err := s.secretStore.Get(ctx, secret, fs...); err != nil {
+		return err
+	}
+	return s.decrypt(secret)
+}
+
+func (s *globalSecretManager) List(ctx context.Context, resources model.ResourceList, fs ...core_store.ListOptionsFunc) error {
+	secrets, ok := resources.(*secret_model.GlobalSecretResourceList)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	if err := s.secretStore.List(ctx, secrets, fs...); err != nil {
+		return err
+	}
+	for _, secret := range secrets.Items {
+		if err := s.decrypt(secret); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (s *globalSecretManager) Create(ctx context.Context, resource model.Resource, fs ...core_store.CreateOptionsFunc) error {
+	secret, ok := resource.(*secret_model.GlobalSecretResource)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	if err := s.encrypt(secret); err != nil {
+		return err
+	}
+	if err := s.secretStore.Create(ctx, secret, append(fs, core_store.CreatedAt(time.Now()))...); err != nil {
+		return err
+	}
+	return s.decrypt(secret)
+}
+
+func (s *globalSecretManager) Update(ctx context.Context, resource model.Resource, fs ...core_store.UpdateOptionsFunc) error {
+	secret, ok := resource.(*secret_model.GlobalSecretResource)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	if err := s.encrypt(secret); err != nil {
+		return err
+	}
+	if err := s.secretStore.Update(ctx, secret, append(fs, core_store.ModifiedAt(time.Now()))...); err != nil {
+		return err
+	}
+	return s.decrypt(secret)
+}
+
+func (s *globalSecretManager) Delete(ctx context.Context, resource model.Resource, fs ...core_store.DeleteOptionsFunc) error {
+	secret, ok := resource.(*secret_model.GlobalSecretResource)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	return s.secretStore.Delete(ctx, secret, fs...)
+}
+
+func (s *globalSecretManager) DeleteAll(ctx context.Context, secrets model.ResourceList, fs ...core_store.DeleteAllOptionsFunc) error {
+	list := &secret_model.SecretResourceList{}
+	if err := s.secretStore.List(ctx, list); err != nil {
+		return err
+	}
+	for _, item := range list.Items {
+		if err := s.Delete(ctx, item, core_store.DeleteBy(model.MetaToResourceKey(item.Meta))); err != nil && !core_store.IsResourceNotFound(err) {
+			return err
+		}
+	}
+	return nil
+}
+
+func (s *globalSecretManager) encrypt(secret *secret_model.GlobalSecretResource) error {
+	if len(secret.Spec.GetData().GetValue()) > 0 {
+		value, err := s.cipher.Encrypt(secret.Spec.Data.Value)
+		if err != nil {
+			return err
+		}
+		secret.Spec.Data.Value = value
+	}
+	return nil
+}
+
+func (s *globalSecretManager) decrypt(secret *secret_model.GlobalSecretResource) error {
+	if len(secret.Spec.GetData().GetValue()) > 0 {
+		value, err := s.cipher.Decrypt(secret.Spec.Data.Value)
+		if err != nil {
+			return err
+		}
+		secret.Spec.Data.Value = value
+	}
+	return nil
+}
diff --git a/pkg/core/secrets/manager/manager.go b/pkg/core/secrets/manager/manager.go
new file mode 100644
index 0000000..cf48727
--- /dev/null
+++ b/pkg/core/secrets/manager/manager.go
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 manager
+
+import (
+	"context"
+	"time"
+
+	"github.com/pkg/errors"
+
+	secret_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	secret_cipher "github.com/apache/dubbo-kubernetes/pkg/core/secrets/cipher"
+	secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+)
+
+func NewSecretManager(secretStore secret_store.SecretStore, cipher secret_cipher.Cipher, validator SecretValidator, unsafeDelete bool) manager.ResourceManager {
+	return &secretManager{
+		secretStore:  secretStore,
+		cipher:       cipher,
+		validator:    validator,
+		unsafeDelete: unsafeDelete,
+	}
+}
+
+var _ manager.ResourceManager = &secretManager{}
+
+type secretManager struct {
+	secretStore  secret_store.SecretStore
+	cipher       secret_cipher.Cipher
+	validator    SecretValidator
+	unsafeDelete bool
+}
+
+func (s *secretManager) Get(ctx context.Context, resource model.Resource, fs ...core_store.GetOptionsFunc) error {
+	secret, ok := resource.(*secret_model.SecretResource)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	if err := s.secretStore.Get(ctx, secret, fs...); err != nil {
+		return err
+	}
+	return s.decrypt(secret)
+}
+
+func (s *secretManager) List(ctx context.Context, resources model.ResourceList, fs ...core_store.ListOptionsFunc) error {
+	secrets, ok := resources.(*secret_model.SecretResourceList)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	if err := s.secretStore.List(ctx, secrets, fs...); err != nil {
+		return err
+	}
+	for _, secret := range secrets.Items {
+		if err := s.decrypt(secret); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (s *secretManager) Create(ctx context.Context, resource model.Resource, fs ...core_store.CreateOptionsFunc) error {
+	secret, ok := resource.(*secret_model.SecretResource)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	if err := s.encrypt(secret); err != nil {
+		return err
+	}
+	if err := s.secretStore.Create(ctx, secret, append(fs, core_store.CreatedAt(time.Now()))...); err != nil {
+		return err
+	}
+	return s.decrypt(secret)
+}
+
+func (s *secretManager) Update(ctx context.Context, resource model.Resource, fs ...core_store.UpdateOptionsFunc) error {
+	secret, ok := resource.(*secret_model.SecretResource)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	if err := s.encrypt(secret); err != nil {
+		return err
+	}
+	if err := s.secretStore.Update(ctx, secret, append(fs, core_store.ModifiedAt(time.Now()))...); err != nil {
+		return err
+	}
+	return s.decrypt(secret)
+}
+
+func (s *secretManager) Delete(ctx context.Context, resource model.Resource, fs ...core_store.DeleteOptionsFunc) error {
+	secret, ok := resource.(*secret_model.SecretResource)
+	if !ok {
+		return newInvalidTypeError()
+	}
+	opts := core_store.NewDeleteOptions(fs...)
+	if !s.unsafeDelete {
+		if err := s.validator.ValidateDelete(ctx, opts.Name, opts.Mesh); err != nil {
+			return err
+		}
+	}
+	return s.secretStore.Delete(ctx, secret, fs...)
+}
+
+func (s *secretManager) DeleteAll(ctx context.Context, secrets model.ResourceList, fs ...core_store.DeleteAllOptionsFunc) error {
+	list := &secret_model.SecretResourceList{}
+	opts := core_store.NewDeleteAllOptions(fs...)
+	if err := s.secretStore.List(ctx, list, core_store.ListByMesh(opts.Mesh)); err != nil {
+		return err
+	}
+	for _, item := range list.Items {
+		if err := s.Delete(ctx, item, core_store.DeleteBy(model.MetaToResourceKey(item.Meta))); err != nil && !core_store.IsResourceNotFound(err) {
+			return err
+		}
+	}
+	return nil
+}
+
+func newInvalidTypeError() error {
+	return errors.New("resource has a wrong type")
+}
+
+func (s *secretManager) encrypt(secret *secret_model.SecretResource) error {
+	if len(secret.Spec.GetData().GetValue()) > 0 {
+		value, err := s.cipher.Encrypt(secret.Spec.Data.Value)
+		if err != nil {
+			return err
+		}
+		secret.Spec.Data.Value = value
+	}
+	return nil
+}
+
+func (s *secretManager) decrypt(secret *secret_model.SecretResource) error {
+	if len(secret.Spec.GetData().GetValue()) > 0 {
+		value, err := s.cipher.Decrypt(secret.Spec.Data.Value)
+		if err != nil {
+			return err
+		}
+		secret.Spec.Data.Value = value
+	}
+	return nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/core/secrets/manager/manager_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/core/secrets/manager/manager_suite_test.go
index 987f638..cd48142 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/core/secrets/manager/manager_suite_test.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package manager_test
 
 import (
-	"archive/zip"
-	"bytes"
+	"testing"
+
+	"github.com/apache/dubbo-kubernetes/pkg/test"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func TestSecretManager(t *testing.T) {
+	test.RunSpecs(t, "Secret Manager Suite")
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/core/secrets/manager/validator.go b/pkg/core/secrets/manager/validator.go
new file mode 100644
index 0000000..cd6d7f8
--- /dev/null
+++ b/pkg/core/secrets/manager/validator.go
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 manager
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/pkg/errors"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	"github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/core/validators"
+)
+
+var log = core.Log.WithName("secrets").WithName("validator")
+
+type SecretValidator interface {
+	ValidateDelete(ctx context.Context, secretName string, secretMesh string) error
+}
+
+type ValidateDelete func(ctx context.Context, secretName string, secretMesh string) error
+
+func (f ValidateDelete) ValidateDelete(ctx context.Context, secretName string, secretMesh string) error {
+	return f(ctx, secretName, secretMesh)
+}
+
+func NewSecretValidator(caManagers ca.Managers, store core_store.ResourceStore) SecretValidator {
+	return &secretValidator{
+		caManagers: caManagers,
+		store:      store,
+	}
+}
+
+type secretValidator struct {
+	caManagers ca.Managers
+	store      core_store.ResourceStore
+}
+
+func (s *secretValidator) ValidateDelete(ctx context.Context, name string, mesh string) error {
+	meshRes := core_mesh.NewMeshResource()
+	err := s.store.Get(ctx, meshRes, core_store.GetByKey(mesh, model.NoMesh))
+	if err != nil {
+		if core_store.IsResourceNotFound(err) {
+			return nil // when Mesh no longer exist we should be able to safely delete a secret because it's not referenced anywhere
+		}
+		return err
+	}
+
+	var verr validators.ValidationError
+	for _, backend := range meshRes.Spec.GetMtls().GetBackends() {
+		used, err := s.secretUsedByMTLSBackend(name, meshRes.GetMeta().GetName(), backend)
+		if err != nil {
+			log.Info("error while checking if secret is used by mTLS backend. Deleting secret anyway", "cause", err)
+		}
+		if used {
+			verr.AddViolation("name", fmt.Sprintf(`The secret %q that you are trying to remove is currently in use in Mesh %q in mTLS backend %q. Please remove the reference from the %q backend before removing the secret.`, name, mesh, backend.Name, backend.Name))
+		}
+	}
+	return verr.OrNil()
+}
+
+func (s *secretValidator) secretUsedByMTLSBackend(name string, mesh string, backend *mesh_proto.CertificateAuthorityBackend) (bool, error) {
+	caManager := s.caManagers[backend.Type]
+	if caManager == nil { // this should be caught earlier by validator
+		return false, errors.Errorf("manager of type %q does not exist", backend.Type)
+	}
+	secrets, err := caManager.UsedSecrets(mesh, backend)
+	if err != nil {
+		return false, errors.Wrapf(err, "could not retrieve secrets in use by backend %q", backend.Name)
+	}
+	for _, secret := range secrets {
+		if secret == name {
+			return true, nil
+		}
+	}
+	return false, nil
+}
diff --git a/pkg/core/secrets/manager/validator_test.go b/pkg/core/secrets/manager/validator_test.go
new file mode 100644
index 0000000..0b96397
--- /dev/null
+++ b/pkg/core/secrets/manager/validator_test.go
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 manager_test
+
+import (
+	"context"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"sigs.k8s.io/yaml"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_datasource "github.com/apache/dubbo-kubernetes/pkg/core/datasource"
+	core_managers "github.com/apache/dubbo-kubernetes/pkg/core/managers/apis/mesh"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/core/secrets/cipher"
+	secrets_manager "github.com/apache/dubbo-kubernetes/pkg/core/secrets/manager"
+	secrets_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+	ca_builtin "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/builtin"
+	ca_provided "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided"
+	provided_config "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided/config"
+	resources_memory "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/memory"
+	"github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var _ = Describe("Secret Validator", func() {
+	var validator secrets_manager.SecretValidator
+	var resManager core_manager.ResourceManager
+	var caManagers core_ca.Managers
+
+	BeforeEach(func() {
+		memoryStore := resources_memory.NewStore()
+		resManager = core_manager.NewResourceManager(memoryStore)
+		caManagers = core_ca.Managers{}
+		secrets_manager.NewSecretValidator(caManagers, memoryStore)
+		validator = secrets_manager.NewSecretValidator(caManagers, memoryStore)
+		secManager := secrets_manager.NewSecretManager(secrets_store.NewSecretStore(memoryStore), cipher.None(), validator, false)
+
+		caManagers["builtin"] = ca_builtin.NewBuiltinCaManager(secManager)
+		caManagers["provided"] = ca_provided.NewProvidedCaManager(core_datasource.NewDataSourceLoader(secManager))
+	})
+
+	type testCase struct {
+		mesh       *core_mesh.MeshResource
+		secretName string
+		expected   string
+	}
+	DescribeTable("should validate that secrets are in use",
+		func(given testCase) {
+			// given
+			err := resManager.Create(context.Background(), given.mesh, core_store.CreateByKey(model.DefaultMesh, model.NoMesh))
+			Expect(err).ToNot(HaveOccurred())
+			err = core_managers.EnsureCAs(context.Background(), caManagers, given.mesh, model.DefaultMesh)
+			Expect(err).ToNot(HaveOccurred())
+
+			// when
+			verr := validator.ValidateDelete(context.Background(), given.secretName, "default")
+
+			// then
+			actual, err := yaml.Marshal(verr)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(actual).To(MatchYAML(given.expected))
+		},
+		Entry("when secret is used in builtin CA", testCase{
+			mesh: &core_mesh.MeshResource{
+				Spec: &mesh_proto.Mesh{
+					Mtls: &mesh_proto.Mesh_Mtls{
+						EnabledBackend: "ca-1",
+						Backends: []*mesh_proto.CertificateAuthorityBackend{
+							{
+								Name: "ca-1",
+								Type: "builtin",
+							},
+						},
+					},
+				},
+			},
+			secretName: "default.ca-builtin-cert-ca-1",
+			expected: `
+            violations:
+            - field: name
+              message: The secret "default.ca-builtin-cert-ca-1" that you are trying to remove is currently in use in Mesh "default" in mTLS backend "ca-1". Please remove the reference from the "ca-1" backend before removing the secret.`,
+		}),
+		Entry("when secret is used in provided CA", testCase{
+			mesh: &core_mesh.MeshResource{
+				Spec: &mesh_proto.Mesh{
+					Mtls: &mesh_proto.Mesh_Mtls{
+						EnabledBackend: "ca-2",
+						Backends: []*mesh_proto.CertificateAuthorityBackend{
+							{
+								Name: "ca-2",
+								Type: "provided",
+								Conf: proto.MustToStruct(&provided_config.ProvidedCertificateAuthorityConfig{
+									Cert: &system_proto.DataSource{
+										Type: &system_proto.DataSource_Secret{
+											Secret: "my-ca-cert",
+										},
+									},
+									Key: &system_proto.DataSource{
+										Type: &system_proto.DataSource_Secret{
+											Secret: "my-ca-key",
+										},
+									},
+								}),
+							},
+						},
+					},
+				},
+			},
+			secretName: "my-ca-cert",
+			expected: `
+            violations:
+            - field: name
+              message: The secret "my-ca-cert" that you are trying to remove is currently in use in Mesh "default" in mTLS backend "ca-2". Please remove the reference from the "ca-2" backend before removing the secret.`,
+		}),
+	)
+
+	It("should pass validation of secrets that are not in use", func() {
+		// given
+		err := resManager.Create(context.Background(), core_mesh.NewMeshResource(), core_store.CreateByKey(model.DefaultMesh, model.NoMesh))
+		Expect(err).ToNot(HaveOccurred())
+
+		// when
+		err = validator.ValidateDelete(context.Background(), "some-not-used-secret", "default")
+
+		// then
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("should pass validation of secrets in mesh that non exist", func() {
+		// when
+		err := validator.ValidateDelete(context.Background(), "some-not-used-secret", "non-existing-mesh")
+
+		// then
+		Expect(err).ToNot(HaveOccurred())
+	})
+})
diff --git a/dubboctl/cmd/generate.go b/pkg/core/secrets/store/adapter.go
similarity index 72%
copy from dubboctl/cmd/generate.go
copy to pkg/core/secrets/store/adapter.go
index 5c2bc6e..c43421b 100644
--- a/dubboctl/cmd/generate.go
+++ b/pkg/core/secrets/store/adapter.go
@@ -13,18 +13,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package store
 
 import (
-	"github.com/spf13/cobra"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
 )
 
-func addGenerate(rootCmd *cobra.Command) {
-	generateCmd := &cobra.Command{
-		Use:   "generate",
-		Short: "Generate resources, tokens, etc",
-		Long:  `Generate resources, tokens, etc.`,
-	}
-	rootCmd.AddCommand(generateCmd)
-	NewGenerateCertificateCmd(generateCmd)
+// todo consider unifing SecretStore with ResourceStore
+func NewSecretStore(resourceStore core_store.ResourceStore) SecretStore {
+	return resourceStore
 }
diff --git a/dubboctl/cmd/generate.go b/pkg/core/secrets/store/store.go
similarity index 72%
copy from dubboctl/cmd/generate.go
copy to pkg/core/secrets/store/store.go
index 5c2bc6e..f9852ef 100644
--- a/dubboctl/cmd/generate.go
+++ b/pkg/core/secrets/store/store.go
@@ -13,18 +13,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package cmd
+package store
 
 import (
-	"github.com/spf13/cobra"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
 )
 
-func addGenerate(rootCmd *cobra.Command) {
-	generateCmd := &cobra.Command{
-		Use:   "generate",
-		Short: "Generate resources, tokens, etc",
-		Long:  `Generate resources, tokens, etc.`,
-	}
-	rootCmd.AddCommand(generateCmd)
-	NewGenerateCertificateCmd(generateCmd)
+type SecretStore interface {
+	core_store.ResourceStore
 }
diff --git a/pkg/core/tokens/default_signing_key.go b/pkg/core/tokens/default_signing_key.go
new file mode 100644
index 0000000..98e2445
--- /dev/null
+++ b/pkg/core/tokens/default_signing_key.go
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"context"
+	"time"
+
+	"github.com/go-logr/logr"
+	"github.com/pkg/errors"
+	"github.com/sethvargo/go-retry"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+	kuma_log "github.com/apache/dubbo-kubernetes/pkg/log"
+)
+
+type defaultSigningKeyComponent struct {
+	signingKeyManager SigningKeyManager
+	log               logr.Logger
+	ctx               context.Context
+	extensions        context.Context
+}
+
+var _ component.Component = &defaultSigningKeyComponent{}
+
+func NewDefaultSigningKeyComponent(
+	ctx context.Context,
+	signingKeyManager SigningKeyManager,
+	log logr.Logger,
+	extensions context.Context,
+) component.Component {
+	return &defaultSigningKeyComponent{
+		signingKeyManager: signingKeyManager,
+		log:               log,
+		ctx:               ctx,
+		extensions:        extensions,
+	}
+}
+
+func (d *defaultSigningKeyComponent) Start(stop <-chan struct{}) error {
+	ctx, cancelFn := context.WithCancel(user.Ctx(d.ctx, user.ControlPlane))
+	defer cancelFn()
+	errChan := make(chan error)
+	go func() {
+		defer close(errChan)
+		backoff := retry.WithMaxDuration(10*time.Minute, retry.NewConstant(5*time.Second)) // if after this time we cannot create a resource - something is wrong and we should return an error which will restart CP.
+		err := retry.Do(ctx, backoff, func(ctx context.Context) error {
+			return retry.RetryableError(CreateDefaultSigningKeyIfNotExist(ctx, d.log, d.signingKeyManager, d.extensions)) // retry all errors
+		})
+		if err != nil {
+			// Retry this operation since on Kubernetes, secrets are validated.
+			// This code can execute before the control plane is ready therefore hooks can fail.
+			errChan <- errors.Wrap(err, "could not create the default signing key")
+		}
+	}()
+	select {
+	case <-stop:
+		return nil
+	case err := <-errChan:
+		return err
+	}
+}
+
+func CreateDefaultSigningKeyIfNotExist(
+	ctx context.Context,
+	log logr.Logger,
+	signingKeyManager SigningKeyManager,
+	extensions context.Context,
+) error {
+	logger := kuma_log.AddFieldsFromCtx(log, ctx, extensions)
+	_, _, err := signingKeyManager.GetLatestSigningKey(ctx)
+	if err == nil {
+		logger.V(1).Info("signing key already exists. Skip creating.")
+		return nil
+	}
+	if _, ok := err.(*SigningKeyNotFound); !ok {
+		return err
+	}
+	if err := signingKeyManager.CreateDefaultSigningKey(ctx); err != nil {
+		logger.V(1).Info("could not create signing key", "err", err)
+		return err
+	}
+	logger.Info("default signing key created")
+	return nil
+}
+
+func (d *defaultSigningKeyComponent) NeedLeaderElection() bool {
+	return true
+}
diff --git a/pkg/core/tokens/issuer.go b/pkg/core/tokens/issuer.go
new file mode 100644
index 0000000..3de4303
--- /dev/null
+++ b/pkg/core/tokens/issuer.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"context"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	"github.com/golang-jwt/jwt/v4"
+	"github.com/pkg/errors"
+	"time"
+)
+
+// Issuer generates tokens.
+// Token is a JWT token with claims that is provided by the actual issuer (for example - Dataplane Token Issuer, User Token Issuer).
+// We place "kid" in token, so we don't have to validate the token against every single signing key.
+// Instead, we take "kid" from the token, retrieve signing key and validate only against this key.
+// A new token is always generated by using the latest signing key.
+type Issuer interface {
+	Generate(ctx context.Context, claims Claims, validFor time.Duration) (Token, error)
+}
+
+type jwtTokenIssuer struct {
+	signingKeyManager SigningKeyManager
+}
+
+func NewTokenIssuer(signingKeyAccessor SigningKeyManager) Issuer {
+	return &jwtTokenIssuer{
+		signingKeyManager: signingKeyAccessor,
+	}
+}
+
+var _ Issuer = &jwtTokenIssuer{}
+
+func (j *jwtTokenIssuer) Generate(ctx context.Context, claims Claims, validFor time.Duration) (Token, error) {
+	signingKey, keyID, err := j.signingKeyManager.GetLatestSigningKey(ctx)
+	if err != nil {
+		return "", err
+	}
+
+	now := core.Now()
+	claims.SetRegisteredClaims(jwt.RegisteredClaims{
+		ID:        core.NewUUID(),
+		IssuedAt:  jwt.NewNumericDate(now),
+		NotBefore: jwt.NewNumericDate(now.Add(time.Minute * -5)), // todo(jakubdyszkiewicz) parametrize via config and go through all clock skews in the project
+		ExpiresAt: jwt.NewNumericDate(now.Add(validFor)),
+	})
+
+	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
+	token.Header[KeyIDHeader] = keyID
+	tokenString, err := token.SignedString(signingKey)
+	if err != nil {
+		return "", errors.Wrap(err, "could not sign a token")
+	}
+	return tokenString, nil
+}
+
+var IssuerDisabled = errors.New("issuing tokens using the control plane is disabled.")
diff --git a/pkg/core/tokens/keys.go b/pkg/core/tokens/keys.go
new file mode 100644
index 0000000..37cc4e7
--- /dev/null
+++ b/pkg/core/tokens/keys.go
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"os"
+
+	config_types "github.com/apache/dubbo-kubernetes/pkg/config/types"
+)
+
+type PublicKey struct {
+	PEM string
+	KID string
+}
+
+func PublicKeyFromConfig(publicKeys []config_types.PublicKey) ([]PublicKey, error) {
+	var keys []PublicKey
+	for _, key := range publicKeys {
+		publicKey, err := configKeyToCoreKey(key)
+		if err != nil {
+			return nil, err
+		}
+		keys = append(keys, publicKey)
+	}
+	return keys, nil
+}
+
+func PublicKeyByMeshFromConfig(publicKeys []config_types.MeshedPublicKey) (map[string][]PublicKey, error) {
+	byMesh := map[string][]PublicKey{}
+	for _, key := range publicKeys {
+		keys, ok := byMesh[key.Mesh]
+		if !ok {
+			keys = []PublicKey{}
+		}
+		publicKey, err := configKeyToCoreKey(key.PublicKey)
+		if err != nil {
+			return nil, err
+		}
+		keys = append(keys, publicKey)
+		byMesh[key.Mesh] = keys
+	}
+	return byMesh, nil
+}
+
+func configKeyToCoreKey(key config_types.PublicKey) (PublicKey, error) {
+	publicKey := PublicKey{
+		KID: key.KID,
+	}
+	if key.KeyFile != "" {
+		content, err := os.ReadFile(key.KeyFile)
+		if err != nil {
+			return PublicKey{}, err
+		}
+		publicKey.PEM = string(content)
+	}
+	if key.Key != "" {
+		publicKey.PEM = key.Key
+	}
+	return publicKey, nil
+}
diff --git a/pkg/core/tokens/revocations.go b/pkg/core/tokens/revocations.go
new file mode 100644
index 0000000..20bb6e7
--- /dev/null
+++ b/pkg/core/tokens/revocations.go
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"context"
+	"strings"
+
+	system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+)
+
+// Revocations keeps track of revoked tokens.
+// If only one token is compromised, it's more convenient to revoke it instead of rotate signing key and regenerate all tokens.
+// Revocation list is stored as Secret (in case of mesh scoped tokens) or GlobalSecret (global scoped tokens).
+// IDs of token are stored in secret in comma separated format: "id1,id2".
+type Revocations interface {
+	IsRevoked(ctx context.Context, id string) (bool, error)
+}
+
+func NewRevocations(manager manager.ReadOnlyResourceManager, revocationKey core_model.ResourceKey) Revocations {
+	return &secretRevocations{
+		manager:       manager,
+		revocationKey: revocationKey,
+	}
+}
+
+type secretRevocations struct {
+	manager       manager.ReadOnlyResourceManager
+	revocationKey core_model.ResourceKey
+}
+
+func (s *secretRevocations) IsRevoked(ctx context.Context, id string) (bool, error) {
+	data, err := s.getSecretData(ctx)
+	if err != nil {
+		return false, err
+	}
+	if len(data) == 0 {
+		return false, nil
+	}
+	rawIds := strings.TrimSuffix(string(data), "\n")
+	ids := strings.Split(rawIds, ",")
+	for _, revokedId := range ids {
+		if revokedId == id {
+			return true, nil
+		}
+	}
+	return false, nil
+}
+
+func (s *secretRevocations) getSecretData(ctx context.Context) ([]byte, error) {
+	// Do a list operation instead of get because of the cache in ReadOnlyResourceManager
+	// For the majority of cases, users do not set revocation secret.
+	// We don't cache not found result of get operation, so with many execution to IsRevoked() we would send a lot of requests to a DB.
+	// We could do get requests and preserve separate cache here (taking into account resource not found),
+	// but there is a high chance that SecretResourceList is already in the cache, because of XDS reconciliation, so we can avoid I/O at all.
+	var resources core_model.ResourceList
+	if s.revocationKey.Mesh == "" {
+		resources = &system.GlobalSecretResourceList{}
+	} else {
+		resources = &system.SecretResourceList{}
+	}
+
+	if err := s.manager.List(ctx, resources, core_store.ListByMesh(s.revocationKey.Mesh)); err != nil {
+		return nil, err
+	}
+	for _, res := range resources.GetItems() {
+		if res.GetMeta().GetName() == s.revocationKey.Name {
+			return res.GetSpec().(*system_proto.Secret).GetData().GetValue(), nil
+		}
+	}
+	return nil, nil
+}
diff --git a/pkg/core/tokens/signing_key.go b/pkg/core/tokens/signing_key.go
new file mode 100644
index 0000000..5570b07
--- /dev/null
+++ b/pkg/core/tokens/signing_key.go
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"context"
+	"crypto/rsa"
+	"crypto/x509"
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/pkg/errors"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	util_rsa "github.com/apache/dubbo-kubernetes/pkg/util/rsa"
+)
+
+func NewSigningKey() ([]byte, error) {
+	key, err := util_rsa.GenerateKey(util_rsa.DefaultKeySize)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to generate RSA key")
+	}
+	return util_rsa.FromPrivateKeyToPEMBytes(key)
+}
+
+func SigningKeyResourceKey(signingKeyPrefix string, keyID KeyID, mesh string) model.ResourceKey {
+	name := fmt.Sprintf("%s-%s", signingKeyPrefix, keyID)
+	if keyID == KeyIDFallbackValue { // backwards compatibility with 1.3.x signing keys https://github.com/kumahq/kuma/issues/4006
+		name = signingKeyPrefix
+	}
+	return model.ResourceKey{
+		Name: name,
+		Mesh: mesh,
+	}
+}
+
+type SigningKeyNotFound struct {
+	KeyID  KeyID
+	Prefix string
+	Mesh   string
+}
+
+func (s *SigningKeyNotFound) Error() string {
+	key := SigningKeyResourceKey(s.Prefix, s.KeyID, s.Mesh)
+	if s.Mesh == "" {
+		return fmt.Sprintf("there is no signing key with KID %s. GlobalSecret of name %q is not found. If signing key was rotated, regenerate the token", s.KeyID, key.Name)
+	} else {
+		return fmt.Sprintf("there is no signing key with KID %s. Secret of name %q in mesh %q is not found. If signing key was rotated, regenerate the token", s.KeyID, key.Name, key.Mesh)
+	}
+}
+
+func IsSigningKeyNotFound(err error) bool {
+	target := &SigningKeyNotFound{}
+	return errors.As(err, &target)
+}
+
+func keyBytesToRsaPrivateKey(keyBytes []byte) (*rsa.PrivateKey, error) {
+	if util_rsa.IsPrivateKeyPEMBytes(keyBytes) {
+		key, err := util_rsa.FromPEMBytesToPrivateKey(keyBytes)
+		if err != nil {
+			return nil, err
+		}
+		return key, nil
+	}
+
+	// support non-PEM RSA key for legacy reasons
+	key, err := x509.ParsePKCS1PrivateKey(keyBytes)
+	if err != nil {
+		return nil, err
+	}
+	return key, nil
+}
+
+func keyBytesToRsaPublicKey(keyBytes []byte) (*rsa.PublicKey, error) {
+	if util_rsa.IsPublicKeyPEMBytes(keyBytes) {
+		key, err := util_rsa.FromPEMBytesToPublicKey(keyBytes)
+		if err != nil {
+			return nil, err
+		}
+		return key, nil
+	}
+
+	// support non-PEM RSA key for legacy reasons
+	key, err := x509.ParsePKCS1PublicKey(keyBytes)
+	if err != nil {
+		return nil, err
+	}
+	return key, nil
+}
+
+func signingKeySerialNumber(secretName string, signingKeyPrefix string) (int, error) {
+	serialNumberStr := strings.ReplaceAll(secretName, signingKeyPrefix+"-", "")
+	serialNumber, err := strconv.Atoi(serialNumberStr)
+	if err != nil {
+		return 0, err
+	}
+	return serialNumber, nil
+}
+
+func getKeyBytes(
+	ctx context.Context,
+	resManager manager.ReadOnlyResourceManager,
+	signingKeyPrefix string,
+	keyID KeyID,
+) ([]byte, error) {
+	resource := system.NewGlobalSecretResource()
+	if err := resManager.Get(ctx, resource, store.GetBy(SigningKeyResourceKey(signingKeyPrefix, keyID, model.NoMesh))); err != nil {
+		if store.IsResourceNotFound(err) {
+			return nil, &SigningKeyNotFound{
+				KeyID:  keyID,
+				Prefix: signingKeyPrefix,
+			}
+		}
+
+		return nil, errors.Wrap(err, "could not retrieve signing key")
+	}
+
+	return resource.Spec.GetData().GetValue(), nil
+}
diff --git a/pkg/core/tokens/signing_key_accessor.go b/pkg/core/tokens/signing_key_accessor.go
new file mode 100644
index 0000000..91dd9bf
--- /dev/null
+++ b/pkg/core/tokens/signing_key_accessor.go
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"context"
+	"crypto/rsa"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+)
+
+// SigningKeyAccessor access public part of signing key
+// In the future, we may add offline token generation (kumactl without CP running or external system)
+// In that case, we could provide only public key to the CP via static configuration.
+// So we can easily do this by providing separate implementation for this interface.
+type SigningKeyAccessor interface {
+	GetPublicKey(context.Context, KeyID) (*rsa.PublicKey, error)
+	// GetLegacyKey returns legacy key. In pre 1.4.x version of Kuma, we used symmetric HMAC256 method of signing DP keys.
+	// In that case, we have to retrieve private key even for verification.
+	GetLegacyKey(context.Context, KeyID) ([]byte, error)
+}
+
+type signingKeyAccessor struct {
+	resManager       manager.ReadOnlyResourceManager
+	signingKeyPrefix string
+}
+
+var _ SigningKeyAccessor = &signingKeyAccessor{}
+
+func NewSigningKeyAccessor(resManager manager.ReadOnlyResourceManager, signingKeyPrefix string) SigningKeyAccessor {
+	return &signingKeyAccessor{
+		resManager:       resManager,
+		signingKeyPrefix: signingKeyPrefix,
+	}
+}
+
+func (s *signingKeyAccessor) GetPublicKey(ctx context.Context, keyID KeyID) (*rsa.PublicKey, error) {
+	keyBytes, err := getKeyBytes(ctx, s.resManager, s.signingKeyPrefix, keyID)
+	if err != nil {
+		return nil, err
+	}
+
+	key, err := keyBytesToRsaPrivateKey(keyBytes)
+	if err != nil {
+		return nil, err
+	}
+	return &key.PublicKey, nil
+}
+
+func (s *signingKeyAccessor) GetLegacyKey(ctx context.Context, keyID KeyID) ([]byte, error) {
+	return getKeyBytes(ctx, s.resManager, s.signingKeyPrefix, keyID)
+}
diff --git a/pkg/core/tokens/signing_key_manager.go b/pkg/core/tokens/signing_key_manager.go
new file mode 100644
index 0000000..7719a19
--- /dev/null
+++ b/pkg/core/tokens/signing_key_manager.go
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"context"
+	"crypto/rsa"
+	"strconv"
+	"strings"
+
+	"github.com/pkg/errors"
+	"google.golang.org/protobuf/types/known/wrapperspb"
+
+	system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+)
+
+const (
+	DefaultKeyID = "1"
+)
+
+// SigningKeyManager manages tokens's signing keys.
+// We can have many signing keys in the system.
+// Example: "user-token-signing-key-1", "user-token-signing-key-2" etc.
+// "user-token-signing-key" has a serial number of 0
+// The latest key is  a key with a higher serial number (number at the end of the name)
+type SigningKeyManager interface {
+	GetLatestSigningKey(context.Context) (*rsa.PrivateKey, string, error)
+	CreateDefaultSigningKey(context.Context) error
+	CreateSigningKey(ctx context.Context, keyID KeyID) error
+}
+
+func NewSigningKeyManager(manager manager.ResourceManager, signingKeyPrefix string) SigningKeyManager {
+	return &signingKeyManager{
+		manager:          manager,
+		signingKeyPrefix: signingKeyPrefix,
+	}
+}
+
+type signingKeyManager struct {
+	manager          manager.ResourceManager
+	signingKeyPrefix string
+}
+
+var _ SigningKeyManager = &signingKeyManager{}
+
+func (s *signingKeyManager) GetLatestSigningKey(ctx context.Context) (*rsa.PrivateKey, string, error) {
+	resources := system.GlobalSecretResourceList{}
+	if err := s.manager.List(ctx, &resources); err != nil {
+		return nil, "", errors.Wrap(err, "could not retrieve signing key from secret manager")
+	}
+	return latestSigningKey(&resources, s.signingKeyPrefix, model.NoMesh)
+}
+
+func latestSigningKey(list model.ResourceList, prefix string, mesh string) (*rsa.PrivateKey, string, error) {
+	var signingKey model.Resource
+	highestSerialNumber := -1
+	for _, resource := range list.GetItems() {
+		if !strings.HasPrefix(resource.GetMeta().GetName(), prefix) {
+			continue
+		}
+		serialNumber, _ := signingKeySerialNumber(resource.GetMeta().GetName(), prefix)
+		if serialNumber > highestSerialNumber {
+			signingKey = resource
+			highestSerialNumber = serialNumber
+		}
+	}
+
+	if signingKey == nil {
+		return nil, "", &SigningKeyNotFound{
+			KeyID:  DefaultKeyID,
+			Prefix: prefix,
+			Mesh:   mesh,
+		}
+	}
+
+	key, err := keyBytesToRsaPrivateKey(signingKey.GetSpec().(*system_proto.Secret).GetData().GetValue())
+	if err != nil {
+		return nil, "", err
+	}
+
+	return key, strconv.Itoa(highestSerialNumber), nil
+}
+
+func (s *signingKeyManager) CreateDefaultSigningKey(ctx context.Context) error {
+	return s.CreateSigningKey(ctx, DefaultKeyID)
+}
+
+func (s *signingKeyManager) CreateSigningKey(ctx context.Context, keyID KeyID) error {
+	key, err := NewSigningKey()
+	if err != nil {
+		return err
+	}
+
+	secret := system.NewGlobalSecretResource()
+	secret.Spec = &system_proto.Secret{
+		Data: &wrapperspb.BytesValue{
+			Value: key,
+		},
+	}
+	return s.manager.Create(ctx, secret, store.CreateBy(SigningKeyResourceKey(s.signingKeyPrefix, keyID, model.NoMesh)))
+}
diff --git a/pkg/core/tokens/static_signing_key_accessor.go b/pkg/core/tokens/static_signing_key_accessor.go
new file mode 100644
index 0000000..09326a0
--- /dev/null
+++ b/pkg/core/tokens/static_signing_key_accessor.go
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"context"
+	"crypto/rsa"
+
+	"github.com/pkg/errors"
+
+	util_rsa "github.com/apache/dubbo-kubernetes/pkg/util/rsa"
+)
+
+type staticSigningKeyAccessor struct {
+	keys map[string]*rsa.PublicKey
+}
+
+var _ SigningKeyAccessor = &staticSigningKeyAccessor{}
+
+func NewStaticSigningKeyAccessor(keys []PublicKey) (SigningKeyAccessor, error) {
+	s := &staticSigningKeyAccessor{
+		keys: map[string]*rsa.PublicKey{},
+	}
+	for _, key := range keys {
+		publicKey, err := util_rsa.FromPEMBytesToPublicKey([]byte(key.PEM))
+		if err != nil {
+			return nil, err
+		}
+		s.keys[key.KID] = publicKey
+	}
+	return s, nil
+}
+
+func (s *staticSigningKeyAccessor) GetPublicKey(ctx context.Context, keyID KeyID) (*rsa.PublicKey, error) {
+	key, ok := s.keys[keyID]
+	if !ok {
+		return nil, &SigningKeyNotFound{
+			KeyID: keyID,
+		}
+	}
+	return key, nil
+}
+
+func (s *staticSigningKeyAccessor) GetLegacyKey(context.Context, KeyID) ([]byte, error) {
+	return nil, errors.New("not supported")
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/core/tokens/token.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/core/tokens/token.go
index 987f638..8e0a62d 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/core/tokens/token.go
@@ -15,25 +15,20 @@
  * limitations under the License.
  */
 
-package dubbo
+package tokens
 
-import (
-	"archive/zip"
-	"bytes"
-)
+import "github.com/golang-jwt/jwt/v4"
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+type Token = string
 
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+type Claims interface {
+	jwt.Claims
+	ID() string
+	SetRegisteredClaims(claims jwt.RegisteredClaims)
 }
 
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+type KeyID = string
+
+const KeyIDFallbackValue = "0"
+
+const KeyIDHeader = "kid" // standard JWT header that indicates which signing key we should use
diff --git a/pkg/core/tokens/validator.go b/pkg/core/tokens/validator.go
new file mode 100644
index 0000000..7823044
--- /dev/null
+++ b/pkg/core/tokens/validator.go
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"context"
+	"crypto/rsa"
+	errors2 "errors"
+	"fmt"
+
+	"github.com/go-logr/logr"
+	"github.com/golang-jwt/jwt/v4"
+	"github.com/pkg/errors"
+
+	store_config "github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
+)
+
+type Validator interface {
+	// ParseWithValidation parses token and fills data in provided Claims.
+	ParseWithValidation(ctx context.Context, token Token, claims Claims) error
+}
+
+type jwtTokenValidator struct {
+	keyAccessors []SigningKeyAccessor
+	revocations  Revocations
+	storeType    store_config.StoreType
+	log          logr.Logger
+}
+
+func NewValidator(log logr.Logger, keyAccessors []SigningKeyAccessor, revocations Revocations, storeType store_config.StoreType) Validator {
+	return &jwtTokenValidator{
+		log:          log,
+		keyAccessors: keyAccessors,
+		revocations:  revocations,
+		storeType:    storeType,
+	}
+}
+
+var _ Validator = &jwtTokenValidator{}
+
+func (j *jwtTokenValidator) ParseWithValidation(ctx context.Context, rawToken Token, claims Claims) error {
+	token, err := jwt.ParseWithClaims(rawToken, claims, func(token *jwt.Token) (interface{}, error) {
+		var keyID KeyID
+		kid, exists := token.Header[KeyIDHeader]
+		if !exists {
+			return 0, fmt.Errorf("JWT token must have %s header", KeyIDHeader)
+		} else {
+			keyID = kid.(string)
+		}
+		switch token.Method.Alg() {
+		case jwt.SigningMethodHS256.Name:
+			var key []byte
+			var err error
+			for _, keyAccessor := range j.keyAccessors {
+				key, err = keyAccessor.GetLegacyKey(ctx, KeyIDFallbackValue)
+				if err == nil {
+					return key, nil
+				}
+			}
+			return nil, err
+		case jwt.SigningMethodRS256.Name:
+			var key *rsa.PublicKey
+			var err error
+			for _, keyAccessor := range j.keyAccessors {
+				key, err = keyAccessor.GetPublicKey(ctx, keyID)
+				if err == nil {
+					return key, nil
+				}
+			}
+			return nil, err
+		default:
+			return nil, fmt.Errorf("unsupported token alg %s. Allowed algorithms are %s and %s", token.Method.Alg(), jwt.SigningMethodRS256.Name, jwt.SigningMethodHS256)
+		}
+	})
+	if err != nil {
+		signingKeyError := &SigningKeyNotFound{}
+		if errors2.As(err, &signingKeyError) {
+			return signingKeyError
+		}
+		if j.storeType == store_config.MemoryStore {
+			return errors.Wrap(err, "could not parse token. kuma-cp runs with an in-memory database and its state isn't preserved between restarts."+
+				" Keep in mind that an in-memory database cannot be used with multiple instances of the control plane")
+		}
+		return errors.Wrap(err, "could not parse token")
+	}
+	if !token.Valid {
+		return errors.New("token is not valid")
+	}
+
+	revoked, err := j.revocations.IsRevoked(ctx, claims.ID())
+	if err != nil {
+		return errors.Wrap(err, "could not check if the token is revoked")
+	}
+	if revoked {
+		return errors.New("token is revoked")
+	}
+	return nil
+}
diff --git a/pkg/core/xds/types.go b/pkg/core/xds/types.go
index 4035d13..674f984 100644
--- a/pkg/core/xds/types.go
+++ b/pkg/core/xds/types.go
@@ -19,6 +19,7 @@
 
 import (
 	"fmt"
+	util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
 	"strings"
 )
 
@@ -53,6 +54,24 @@
 	}
 }
 
+type MatchType string
+
+type SAN struct {
+	MatchType MatchType
+	Value     string
+}
+
+const (
+	SANMatchExact  MatchType = "Exact"
+	SANMatchPrefix MatchType = "Prefix"
+)
+
+type TlsVersion int32
+
+type CaSecret struct {
+	PemCerts [][]byte
+}
+
 // ServiceName is a convenience type alias to clarify the meaning of string value.
 type ServiceName = string
 
@@ -74,6 +93,10 @@
 	AllowRenegotiation       bool
 	SkipHostnameVerification bool
 	ServerName               string
+	FallbackToSystemCa       bool
+	SANs                     []SAN
+	MinTlsVersion            *TlsVersion
+	MaxTlsVersion            *TlsVersion
 }
 
 type Locality struct {
@@ -94,6 +117,11 @@
 	ExternalService *ExternalService
 }
 
+type IdentitySecret struct {
+	PemCerts [][]byte
+	PemKey   []byte
+}
+
 func (e Endpoint) Address() string {
 	return fmt.Sprintf("%s:%d", e.Target, e.Port)
 }
@@ -115,12 +143,13 @@
 // Proxy contains required data for generating XDS config that is specific to a data plane proxy.
 // The data that is specific for the whole mesh should go into MeshContext.
 type Proxy struct {
-	Id         ProxyId
-	APIVersion APIVersion
-	Dataplane  *core_mesh.DataplaneResource
-	Metadata   *DataplaneMetadata
-	Routing    Routing
-	Policies   MatchedPolicies
+	Id                  ProxyId
+	APIVersion          APIVersion
+	Dataplane           *core_mesh.DataplaneResource
+	Metadata            *DataplaneMetadata
+	Routing             Routing
+	Policies            MatchedPolicies
+	EnvoyAdminMTLSCerts ServerSideMTLSCerts
 
 	// SecretsTracker allows us to track when a generator references a secret so
 	// we can be sure to include only those secrets later on.
@@ -134,6 +163,11 @@
 	Zone string
 }
 
+type ServerSideMTLSCerts struct {
+	CaPEM      []byte
+	ServerPair util_tls.KeyPair
+}
+
 type ServerSideTLSCertPaths struct {
 	CertPath string
 	KeyPath  string
diff --git a/pkg/dds/context/context.go b/pkg/dds/context/context.go
index 9a9da71..fe4e64d 100644
--- a/pkg/dds/context/context.go
+++ b/pkg/dds/context/context.go
@@ -19,6 +19,7 @@
 
 import (
 	"context"
+	"github.com/apache/dubbo-kubernetes/pkg/dds/service"
 	"reflect"
 )
 
@@ -64,6 +65,7 @@
 
 	ServerStreamInterceptors []grpc.StreamServerInterceptor
 	ServerUnaryInterceptor   []grpc.UnaryServerInterceptor
+	EnvoyAdminRPCs           service.EnvoyAdminRPCs
 }
 
 func DefaultContext(
diff --git a/pkg/dds/service/envoy_admin_rpcs.go b/pkg/dds/service/envoy_admin_rpcs.go
new file mode 100644
index 0000000..a2a74ff
--- /dev/null
+++ b/pkg/dds/service/envoy_admin_rpcs.go
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 service
+
+import (
+	util_grpc "github.com/apache/dubbo-kubernetes/pkg/util/grpc"
+)
+
+const (
+	ConfigDumpRPC = "XDS Config Dump"
+	StatsRPC      = "Stats"
+	ClustersRPC   = "Clusters"
+)
+
+type EnvoyAdminRPCs struct {
+	XDSConfigDump util_grpc.ReverseUnaryRPCs
+	Stats         util_grpc.ReverseUnaryRPCs
+	Clusters      util_grpc.ReverseUnaryRPCs
+}
+
+func NewEnvoyAdminRPCs() EnvoyAdminRPCs {
+	return EnvoyAdminRPCs{
+		XDSConfigDump: util_grpc.NewReverseUnaryRPCs(),
+		Stats:         util_grpc.NewReverseUnaryRPCs(),
+		Clusters:      util_grpc.NewReverseUnaryRPCs(),
+	}
+}
diff --git a/pkg/dds/service/server.go b/pkg/dds/service/server.go
index f960f51..f465df6 100644
--- a/pkg/dds/service/server.go
+++ b/pkg/dds/service/server.go
@@ -20,6 +20,7 @@
 import (
 	"context"
 	"fmt"
+	"github.com/apache/dubbo-kubernetes/pkg/multitenant"
 	"time"
 )
 
@@ -52,6 +53,11 @@
 	InterceptServerStream(stream grpc.ServerStream) error
 }
 
+type TenantZoneClientID struct {
+	TenantID string
+	Zone     string
+}
+
 type GlobalDDSServiceServer struct {
 	resManager              manager.ResourceManager
 	instanceID              string
@@ -132,3 +138,21 @@
 func ZoneClientIDFromCtx(ctx context.Context, zone string) ZoneClientID {
 	return ZoneClientID{Zone: zone}
 }
+
+func (id TenantZoneClientID) String() string {
+	if id.TenantID == "" {
+		return id.Zone
+	}
+	return fmt.Sprintf("%s:%s", id.Zone, id.TenantID)
+}
+
+func TenantZoneClientIDFromCtx(ctx context.Context, zone string) TenantZoneClientID {
+	id := TenantZoneClientID{
+		Zone: zone,
+	}
+	tenantID, ok := multitenant.TenantFromCtx(ctx)
+	if ok {
+		id.TenantID = tenantID
+	}
+	return id
+}
diff --git a/pkg/defaults/components.go b/pkg/defaults/components.go
index f5a1e0c..59b31e4 100644
--- a/pkg/defaults/components.go
+++ b/pkg/defaults/components.go
@@ -19,6 +19,8 @@
 
 import (
 	"context"
+	config_core "github.com/apache/dubbo-kubernetes/pkg/config/core"
+	"github.com/apache/dubbo-kubernetes/pkg/core/tokens"
 	"sync"
 	"time"
 )
@@ -37,10 +39,12 @@
 	core_manager "github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
 	"github.com/apache/dubbo-kubernetes/pkg/core/runtime"
 	"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+	"github.com/apache/dubbo-kubernetes/pkg/tokens/builtin/zone"
 )
 
 var log = core.Log.WithName("defaults")
 
+// TODO:not exposed
 func Setup(runtime runtime.Runtime) error {
 	if !runtime.Config().IsFederatedZoneCP() { // Don't run defaults in Zone connected to global (it's done in Global)
 		defaultsComponent := NewDefaultsComponent(
@@ -49,11 +53,36 @@
 			runtime.Extensions(),
 		)
 
+		zoneSigningKeyManager := tokens.NewSigningKeyManager(runtime.ResourceManager(), zone.SigningKeyPrefix)
+		if err := runtime.Add(tokens.NewDefaultSigningKeyComponent(
+			runtime.AppContext(),
+			zoneSigningKeyManager,
+			log.WithValues("secretPrefix", zone.SigningKeyPrefix),
+			runtime.Extensions(),
+		)); err != nil {
+			return err
+		}
+
 		if err := runtime.Add(defaultsComponent); err != nil {
 			return err
 		}
 	}
 
+	if runtime.Config().Mode != config_core.Global { // Envoy Admin CA is not synced in multizone and not needed in Global CP.
+		envoyAdminCaDefault := &EnvoyAdminCaDefaultComponent{
+			ResManager: runtime.ResourceManager(),
+			Extensions: runtime.Extensions(),
+		}
+		zoneDefault := &ZoneDefaultComponent{
+			ResManager: runtime.ResourceManager(),
+			Extensions: runtime.Extensions(),
+			ZoneName:   runtime.Config().Multizone.Zone.Name,
+		}
+		if err := runtime.Add(envoyAdminCaDefault, zoneDefault); err != nil {
+			return err
+		}
+	}
+
 	return nil
 }
 
diff --git a/pkg/defaults/envoy_admin_ca.go b/pkg/defaults/envoy_admin_ca.go
new file mode 100644
index 0000000..6961708
--- /dev/null
+++ b/pkg/defaults/envoy_admin_ca.go
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 defaults
+
+import (
+	"context"
+	"time"
+
+	"github.com/pkg/errors"
+	"github.com/sethvargo/go-retry"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+	"github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+	dubbo_log "github.com/apache/dubbo-kubernetes/pkg/log"
+)
+
+type EnvoyAdminCaDefaultComponent struct {
+	ResManager manager.ResourceManager
+	Extensions context.Context
+}
+
+var _ component.Component = &EnvoyAdminCaDefaultComponent{}
+
+func (e *EnvoyAdminCaDefaultComponent) Start(stop <-chan struct{}) error {
+	ctx, cancelFn := context.WithCancel(user.Ctx(context.Background(), user.ControlPlane))
+	defer cancelFn()
+	logger := dubbo_log.AddFieldsFromCtx(log, ctx, e.Extensions)
+	errChan := make(chan error)
+	go func() {
+		errChan <- retry.Do(ctx, retry.WithMaxDuration(10*time.Minute, retry.NewConstant(5*time.Second)), func(ctx context.Context) error {
+			if err := EnsureEnvoyAdminCaExist(ctx, e.ResManager, e.Extensions); err != nil {
+				logger.V(1).Info("could not ensure that Envoy Admin CA exists. Retrying.", "err", err)
+				return retry.RetryableError(err)
+			}
+			return nil
+		})
+	}()
+	select {
+	case <-stop:
+		return nil
+	case err := <-errChan:
+		return err
+	}
+}
+
+func (e EnvoyAdminCaDefaultComponent) NeedLeaderElection() bool {
+	return true
+}
+
+func EnsureEnvoyAdminCaExist(
+	ctx context.Context,
+	resManager manager.ResourceManager,
+	extensions context.Context,
+) error {
+	logger := dubbo_log.AddFieldsFromCtx(log, ctx, extensions)
+	_, err := tls.LoadCA(ctx, resManager)
+	if err == nil {
+		logger.V(1).Info("Envoy Admin CA already exists. Skip creating Envoy Admin CA.")
+		return nil
+	}
+	if !store.IsResourceNotFound(err) {
+		return errors.Wrap(err, "error while loading envoy admin CA")
+	}
+	pair, err := tls.GenerateCA()
+	if err != nil {
+		return errors.Wrap(err, "could not generate envoy admin CA")
+	}
+	if err := tls.CreateCA(ctx, *pair, resManager); err != nil {
+		return errors.Wrap(err, "could not create envoy admin CA")
+	}
+	logger.Info("Envoy Admin CA created")
+	return nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/envoy/admin/access/access.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/envoy/admin/access/access.go
index 987f638..5d53071 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/envoy/admin/access/access.go
@@ -15,25 +15,17 @@
  * limitations under the License.
  */
 
-package dubbo
+package access
 
 import (
-	"archive/zip"
-	"bytes"
+	"context"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+// TODO: inspect admin envoy endpoint
+type EnvoyAdminAccess interface {
+	ValidateViewConfigDump(ctx context.Context, user user.User) error
+	ValidateViewStats(ctx context.Context, user user.User) error
+	ValidateViewClusters(ctx context.Context, user user.User) error
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/envoy/admin/access/static.go b/pkg/envoy/admin/access/static.go
new file mode 100644
index 0000000..949fab0
--- /dev/null
+++ b/pkg/envoy/admin/access/static.go
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import (
+	"context"
+
+	config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type staticEnvoyAdminAccess struct {
+	configDump accessMaps
+	stats      accessMaps
+	clusters   accessMaps
+}
+
+type accessMaps struct {
+	usernames map[string]struct{}
+	groups    map[string]struct{}
+}
+
+func (am accessMaps) Validate(user user.User) error {
+	return access.Validate(am.usernames, am.groups, user, "envoy proxy info")
+}
+
+var _ EnvoyAdminAccess = &staticEnvoyAdminAccess{}
+
+func NewStaticEnvoyAdminAccess(
+	configDumpCfg config_access.ViewConfigDumpStaticAccessConfig,
+	statsCfg config_access.ViewStatsStaticAccessConfig,
+	clustersCfg config_access.ViewClustersStaticAccessConfig,
+) EnvoyAdminAccess {
+	return &staticEnvoyAdminAccess{
+		configDump: mapAccess(configDumpCfg.Users, configDumpCfg.Groups),
+		stats:      mapAccess(statsCfg.Users, statsCfg.Groups),
+		clusters:   mapAccess(clustersCfg.Users, clustersCfg.Groups),
+	}
+}
+
+func mapAccess(users []string, groups []string) accessMaps {
+	m := accessMaps{
+		usernames: make(map[string]struct{}, len(users)),
+		groups:    make(map[string]struct{}, len(groups)),
+	}
+	for _, usr := range users {
+		m.usernames[usr] = struct{}{}
+	}
+	for _, group := range groups {
+		m.groups[group] = struct{}{}
+	}
+	return m
+}
+
+func (s *staticEnvoyAdminAccess) ValidateViewConfigDump(ctx context.Context, user user.User) error {
+	return s.configDump.Validate(user)
+}
+
+func (s *staticEnvoyAdminAccess) ValidateViewStats(ctx context.Context, user user.User) error {
+	return s.stats.Validate(user)
+}
+
+func (s *staticEnvoyAdminAccess) ValidateViewClusters(ctx context.Context, user user.User) error {
+	return s.clusters.Validate(user)
+}
diff --git a/pkg/envoy/admin/client.go b/pkg/envoy/admin/client.go
new file mode 100644
index 0000000..c78a674
--- /dev/null
+++ b/pkg/envoy/admin/client.go
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 admin
+
+import (
+	"context"
+	"crypto/tls"
+	"crypto/x509"
+	"fmt"
+	"io"
+	"net"
+	"net/http"
+	"net/url"
+	"time"
+
+	"github.com/pkg/errors"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	envoy_admin_tls "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+)
+
+type EnvoyAdminClient interface {
+	PostQuit(ctx context.Context, dataplane *core_mesh.DataplaneResource) error
+	ConfigDump(ctx context.Context, proxy core_model.ResourceWithAddress, includeEds bool) ([]byte, error)
+}
+
+type envoyAdminClient struct {
+	rm               manager.ResourceManager
+	caManagers       ca.Managers
+	defaultAdminPort uint32
+
+	caCertPool *x509.CertPool
+	clientCert *tls.Certificate
+}
+
+func NewEnvoyAdminClient(rm manager.ResourceManager, caManagers ca.Managers, adminPort uint32) EnvoyAdminClient {
+	client := &envoyAdminClient{
+		rm:               rm,
+		caManagers:       caManagers,
+		defaultAdminPort: adminPort,
+	}
+	return client
+}
+
+func (a *envoyAdminClient) buildHTTPClient(ctx context.Context) (*http.Client, error) {
+	caCertPool, clientCert, err := a.mtlsCerts(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	c := &http.Client{
+		Transport: &http.Transport{
+			DialContext: (&net.Dialer{
+				Timeout: 3 * time.Second,
+			}).DialContext,
+			TLSHandshakeTimeout: 3 * time.Second,
+			TLSClientConfig: &tls.Config{
+				MinVersion:   tls.VersionTLS12,
+				RootCAs:      &caCertPool,
+				Certificates: []tls.Certificate{clientCert},
+			},
+		},
+		Timeout: 5 * time.Second,
+	}
+	return c, err
+}
+
+func (a *envoyAdminClient) mtlsCerts(ctx context.Context) (x509.CertPool, tls.Certificate, error) {
+	if a.caCertPool == nil {
+		ca, err := envoy_admin_tls.LoadCA(ctx, a.rm)
+		if err != nil {
+			return x509.CertPool{}, tls.Certificate{}, errors.Wrap(err, "could not load the CA")
+		}
+		caCertPool := x509.NewCertPool()
+		caCert, err := x509.ParseCertificate(ca.Certificate[0])
+		if err != nil {
+			return x509.CertPool{}, tls.Certificate{}, errors.Wrap(err, "could not parse CA")
+		}
+		caCertPool.AddCert(caCert)
+
+		pair, err := envoy_admin_tls.GenerateClientCert(ca)
+		if err != nil {
+			return x509.CertPool{}, tls.Certificate{}, errors.Wrap(err, "could not generate a client certificate")
+		}
+		clientCert, err := tls.X509KeyPair(pair.CertPEM, pair.KeyPEM)
+		if err != nil {
+			return x509.CertPool{}, tls.Certificate{}, errors.Wrap(err, "could not parse the client certificate")
+		}
+
+		// cache the certs, so we don't have to load the CA and generate them on every single request.
+		// This means that if we want to change Envoy Admin CA, we need to restart all CP instances.
+		a.caCertPool = caCertPool
+		a.clientCert = &clientCert
+	}
+	return *a.caCertPool, *a.clientCert, nil
+}
+
+const (
+	quitquitquit = "quitquitquit"
+)
+
+func (a *envoyAdminClient) PostQuit(ctx context.Context, dataplane *core_mesh.DataplaneResource) error {
+	httpClient, err := a.buildHTTPClient(ctx)
+	if err != nil {
+		return err
+	}
+
+	url := fmt.Sprintf("https://%s/%s", dataplane.AdminAddress(a.defaultAdminPort), quitquitquit)
+	request, err := http.NewRequestWithContext(ctx, "POST", url, nil)
+	if err != nil {
+		return err
+	}
+
+	// Envoy will not send back any response, so do we not check the response
+	response, err := httpClient.Do(request)
+	if errors.Is(err, io.EOF) {
+		return nil // Envoy may not respond correctly for this request because it already started the shut-down process.
+	}
+	if err != nil {
+		return errors.Wrapf(err, "unable to send POST to %s", quitquitquit)
+	}
+	defer response.Body.Close()
+
+	if response.StatusCode != http.StatusOK {
+		return errors.Errorf("envoy response [%d %s] [%s]", response.StatusCode, response.Status, response.Body)
+	}
+
+	return nil
+}
+
+func (a *envoyAdminClient) ConfigDump(ctx context.Context, proxy core_model.ResourceWithAddress, includeEds bool) ([]byte, error) {
+	query := url.Values{}
+	if includeEds {
+		query.Add("include_eds", "true")
+	}
+	configDump, err := a.executeRequest(ctx, proxy, "config_dump", query)
+	if err != nil {
+		return nil, err
+	}
+
+	var value []byte
+	if value, err = Sanitize(configDump); err != nil {
+		return nil, err
+	}
+
+	return value, nil
+}
+
+func (a *envoyAdminClient) executeRequest(ctx context.Context, proxy core_model.ResourceWithAddress, path string, query url.Values) ([]byte, error) {
+	var httpClient *http.Client
+	var err error
+	u := &url.URL{}
+
+	switch proxy.(type) {
+	case *core_mesh.DataplaneResource:
+		httpClient, err = a.buildHTTPClient(ctx)
+		if err != nil {
+			return nil, err
+		}
+		u.Scheme = "https"
+	case *core_mesh.ZoneIngressResource, *core_mesh.ZoneEgressResource:
+		httpClient, err = a.buildHTTPClient(ctx)
+		if err != nil {
+			return nil, err
+		}
+		u.Scheme = "https"
+	default:
+		return nil, errors.New("unsupported proxy type")
+	}
+
+	if host, _, err := net.SplitHostPort(proxy.AdminAddress(a.defaultAdminPort)); err == nil && host == "127.0.0.1" {
+		httpClient = &http.Client{
+			Timeout: 5 * time.Second,
+		}
+		u.Scheme = "http"
+	}
+
+	u.Host = proxy.AdminAddress(a.defaultAdminPort)
+	u.Path = path
+	u.RawQuery = query.Encode()
+	request, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	response, err := httpClient.Do(request)
+	if err != nil {
+		return nil, errors.Wrapf(err, "unable to send GET to %s", "config_dump")
+	}
+	defer response.Body.Close()
+
+	if response.StatusCode != http.StatusOK {
+		return nil, errors.Errorf("envoy response [%d %s] [%s]", response.StatusCode, response.Status, response.Body)
+	}
+
+	resp, err := io.ReadAll(response.Body)
+	if err != nil {
+		return nil, err
+	}
+	return resp, nil
+}
diff --git a/pkg/envoy/admin/sanitize.go b/pkg/envoy/admin/sanitize.go
new file mode 100644
index 0000000..4757267
--- /dev/null
+++ b/pkg/envoy/admin/sanitize.go
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 admin
+
+import (
+	"strings"
+
+	envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
+
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+func Sanitize(configDump []byte) ([]byte, error) {
+	toReplace := []string{}
+	cd := &envoy_admin_v3.ConfigDump{}
+	if err := util_proto.FromJSON(configDump, cd); err != nil {
+		return nil, err
+	}
+	for _, config := range cd.Configs {
+		if config.MessageIs(&envoy_admin_v3.BootstrapConfigDump{}) {
+			bootstrapConfigDump := &envoy_admin_v3.BootstrapConfigDump{}
+			if err := config.UnmarshalTo(bootstrapConfigDump); err != nil {
+				return nil, err
+			}
+
+			for _, grpcService := range bootstrapConfigDump.GetBootstrap().GetDynamicResources().GetAdsConfig().GetGrpcServices() {
+				for i, initMeta := range grpcService.InitialMetadata {
+					if initMeta.Key == "authorization" {
+						toReplace = append(toReplace, grpcService.InitialMetadata[i].Value, "[redacted]")
+					}
+				}
+			}
+
+			for _, grpcService := range bootstrapConfigDump.GetBootstrap().GetHdsConfig().GetGrpcServices() {
+				for i, initMeta := range grpcService.InitialMetadata {
+					if initMeta.Key == "authorization" {
+						toReplace = append(toReplace, grpcService.InitialMetadata[i].Value, "[redacted]")
+					}
+				}
+			}
+		}
+	}
+	return []byte(strings.NewReplacer(toReplace...).Replace(string(configDump))), nil
+}
diff --git a/pkg/envoy/admin/sanitize_test.go b/pkg/envoy/admin/sanitize_test.go
new file mode 100644
index 0000000..522a125
--- /dev/null
+++ b/pkg/envoy/admin/sanitize_test.go
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 admin_test
+
+import (
+	"os"
+	"path"
+	"path/filepath"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	"github.com/apache/dubbo-kubernetes/pkg/envoy/admin"
+	"github.com/apache/dubbo-kubernetes/pkg/test/matchers"
+	_ "github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
+)
+
+var _ = Describe("Sanitize ConfigDump", func() {
+	type testCase struct {
+		configFile string
+		goldenFile string
+	}
+
+	DescribeTable("should redact sensitive information",
+		func(given testCase) {
+			// given
+			rawConfigDump, err := os.ReadFile(filepath.Join("testdata", given.configFile))
+			Expect(err).ToNot(HaveOccurred())
+
+			// when
+			sanitizedDump, err := admin.Sanitize(rawConfigDump)
+			Expect(err).ToNot(HaveOccurred())
+
+			// then
+			Expect(sanitizedDump).To(matchers.MatchGoldenJSON(path.Join("testdata", given.goldenFile)))
+		},
+		Entry("full config", testCase{
+			configFile: "full_config.json",
+			goldenFile: "golden.full_config.json",
+		}),
+		Entry("no hds", testCase{
+			configFile: "no_hds.json",
+			goldenFile: "golden.no_hds.json",
+		}),
+	)
+})
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/envoy/admin/suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/envoy/admin/suite_test.go
index 987f638..17b0b7a 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/envoy/admin/suite_test.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package admin_test
 
 import (
-	"archive/zip"
-	"bytes"
+	"testing"
+
+	"github.com/apache/dubbo-kubernetes/pkg/test"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func TestSanitize(t *testing.T) {
+	test.RunSpecs(t, "Sanitize Suite")
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/envoy/admin/testdata/full_config.json b/pkg/envoy/admin/testdata/full_config.json
new file mode 100644
index 0000000..b9e9e01
--- /dev/null
+++ b/pkg/envoy/admin/testdata/full_config.json
@@ -0,0 +1,1429 @@
+{
+  "configs": [
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
+      "bootstrap": {
+        "node": {
+          "id": "default.backend-1",
+          "cluster": "backend",
+          "metadata": {
+            "dataplane.proxyType": "dataplane",
+            "version": {
+              "envoy": {
+                "version": "1.19.0",
+                "build": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL"
+              },
+              "dependencies": {},
+              "kumaDp": {
+                "version": "dev-9fd12f2f3",
+                "buildDate": "2022-02-03T17:18:11Z",
+                "gitTag": "1.4.0-rc1-265-g9fd12f2f3",
+                "gitCommit": "9fd12f2f3adbb13dc8a724e4bd76ea149faf6c0c"
+              }
+            },
+            "dataplane.dns.port": "15054",
+            "dynamicMetadata": {
+              "version.dependencies.coredns": "1.8.3"
+            },
+            "dataplane.admin.port": "6606",
+            "dataplane.resource": "{\"type\":\"Dataplane\",\"mesh\":\"default\",\"name\":\"backend-1\",\"creationTime\":\"0001-01-01T00:00:00Z\",\"modificationTime\":\"0001-01-01T00:00:00Z\",\"networking\":{\"address\":\"127.0.0.1\",\"inbound\":[{\"port\":10010,\"servicePort\":10011,\"tags\":{\"kuma.io/protocol\":\"tcp\",\"kuma.io/region\":\"reg1\",\"kuma.io/service\":\"backend\",\"kuma.io/sub-zone\":\"subzone1\",\"version\":\"1\"}}],\"outbound\":[{\"port\":10006,\"tags\":{\"kuma.io/service\":\"gateway\"}}],\"admin\":{\"port\":6606}}}"
+          },
+          "hidden_envoy_deprecated_build_version": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL",
+          "user_agent_name": "envoy",
+          "user_agent_build_version": {
+            "version": {
+              "major_number": 1,
+              "minor_number": 19
+            },
+            "metadata": {
+              "build.type": "RELEASE",
+              "revision.sha": "68fe53a889416fd8570506232052b06f5a531541",
+              "revision.status": "Modified",
+              "ssl.version": "BoringSSL"
+            }
+          },
+          "extensions": [
+            {
+              "name": "envoy.filters.dubbo.router",
+              "category": "envoy.dubbo_proxy.filters"
+            },
+            {
+              "name": "envoy.filters.thrift.rate_limit",
+              "category": "envoy.thrift_proxy.filters"
+            },
+            {
+              "name": "envoy.filters.thrift.router",
+              "category": "envoy.thrift_proxy.filters"
+            },
+            {
+              "name": "composite-action",
+              "category": "envoy.matching.action"
+            },
+            {
+              "name": "skip",
+              "category": "envoy.matching.action"
+            },
+            {
+              "name": "envoy.rate_limit_descriptors.expr",
+              "category": "envoy.rate_limit_descriptors"
+            },
+            {
+              "name": "envoy.matching.common_inputs.environment_variable",
+              "category": "envoy.matching.common_inputs"
+            },
+            {
+              "name": "envoy.compression.brotli.decompressor",
+              "category": "envoy.compression.decompressor"
+            },
+            {
+              "name": "envoy.compression.gzip.decompressor",
+              "category": "envoy.compression.decompressor"
+            },
+            {
+              "name": "envoy.access_loggers.file",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.http_grpc",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.open_telemetry",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.stderr",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.stdout",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.tcp_grpc",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.wasm",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.file_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.http_grpc_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.open_telemetry_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.stderr_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.stdout_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.tcp_grpc_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.wasm_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.transport_sockets.alts",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.quic",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.raw_buffer",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.starttls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tap",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "raw_buffer",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "starttls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "tls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.dynamic.ot",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.lightstep",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.datadog",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.dynamic_ot",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.lightstep",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.opencensus",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.skywalking",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.xray",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.zipkin",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.zipkin",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.bootstrap.wasm",
+              "category": "envoy.bootstrap"
+            },
+            {
+              "name": "envoy.extensions.network.socket_interface.default_socket_interface",
+              "category": "envoy.bootstrap"
+            },
+            {
+              "name": "envoy.filters.listener.http_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.original_dst",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.original_src",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.proxy_protocol",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.tls_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.http_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.original_dst",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.original_src",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.proxy_protocol",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.tls_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.client_ssl_auth",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.echo",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.ext_authz",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.client_ssl_auth",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.connection_limit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.direct_response",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.dubbo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.echo",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.ext_authz",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.http_connection_manager",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.kafka_broker",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.local_ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.mongo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.mysql_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.postgres_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.rbac",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.redis_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.rocketmq_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.sni_cluster",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.sni_dynamic_forward_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.tcp_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.thrift_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.wasm",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.zookeeper_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.http_connection_manager",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.mongo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.redis_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.tcp_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.http.original_ip_detection.custom_header",
+              "category": "envoy.http.original_ip_detection"
+            },
+            {
+              "name": "envoy.http.original_ip_detection.xff",
+              "category": "envoy.http.original_ip_detection"
+            },
+            {
+              "name": "request-headers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "request-trailers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "response-headers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "response-trailers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "envoy.filters.udp.dns_filter",
+              "category": "envoy.filters.udp_listener"
+            },
+            {
+              "name": "envoy.filters.udp_listener.udp_proxy",
+              "category": "envoy.filters.udp_listener"
+            },
+            {
+              "name": "dubbo",
+              "category": "envoy.dubbo_proxy.protocols"
+            },
+            {
+              "name": "envoy.ip",
+              "category": "envoy.resolvers"
+            },
+            {
+              "name": "envoy.compression.brotli.compressor",
+              "category": "envoy.compression.compressor"
+            },
+            {
+              "name": "envoy.compression.gzip.compressor",
+              "category": "envoy.compression.compressor"
+            },
+            {
+              "name": "envoy.matching.matchers.consistent_hashing",
+              "category": "envoy.matching.input_matchers"
+            },
+            {
+              "name": "envoy.matching.matchers.ip",
+              "category": "envoy.matching.input_matchers"
+            },
+            {
+              "name": "preserve_case",
+              "category": "envoy.http.stateful_header_formatters"
+            },
+            {
+              "name": "envoy.watchdog.abort_action",
+              "category": "envoy.guarddog_actions"
+            },
+            {
+              "name": "envoy.watchdog.profile_action",
+              "category": "envoy.guarddog_actions"
+            },
+            {
+              "name": "envoy.filters.connection_pools.tcp.generic",
+              "category": "envoy.upstreams"
+            },
+            {
+              "name": "envoy.formatter.req_without_query",
+              "category": "envoy.formatter"
+            },
+            {
+              "name": "auto",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "framed",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "header",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "unframed",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "envoy.bandwidth_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.buffer",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.cors",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.csrf",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ext_authz",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ext_proc",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.fault",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.adaptive_concurrency",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.admission_control",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.alternate_protocols_cache",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.aws_lambda",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.aws_request_signing",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.bandwidth_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.buffer",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cache",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cdn_loop",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.composite",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.compressor",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cors",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.csrf",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.decompressor",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.dynamic_forward_proxy",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.dynamo",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ext_authz",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ext_proc",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.fault",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_http1_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_http1_reverse_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_json_transcoder",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_stats",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_web",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.header_to_metadata",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.health_check",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ip_tagging",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.jwt_authn",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.local_ratelimit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.lua",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.oauth2",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.on_demand",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.original_src",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ratelimit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.rbac",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.router",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.set_metadata",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.squash",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.tap",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.wasm",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_http1_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_json_transcoder",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_web",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.health_check",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.http_dynamo_filter",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ip_tagging",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.local_rate_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.lua",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.rate_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.router",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.squash",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "match-wrapper",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.extensions.http.cache.simple",
+              "category": "envoy.http.cache"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.allow_listed_routes",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.previous_routes",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.safe_cross_scheme",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.quic.proof_source.filter_chain",
+              "category": "envoy.quic.proof_source"
+            },
+            {
+              "name": "envoy.retry_priorities.previous_priorities",
+              "category": "envoy.retry_priorities"
+            },
+            {
+              "name": "envoy.wasm.runtime.null",
+              "category": "envoy.wasm.runtime"
+            },
+            {
+              "name": "envoy.wasm.runtime.v8",
+              "category": "envoy.wasm.runtime"
+            },
+            {
+              "name": "envoy.quic.crypto_stream.server.quiche",
+              "category": "envoy.quic.server.crypto_stream"
+            },
+            {
+              "name": "envoy.tls.cert_validator.default",
+              "category": "envoy.tls.cert_validator"
+            },
+            {
+              "name": "envoy.tls.cert_validator.spiffe",
+              "category": "envoy.tls.cert_validator"
+            },
+            {
+              "name": "envoy.dog_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.graphite_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.metrics_service",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.dog_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.graphite_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.hystrix",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.metrics_service",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.wasm",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "dubbo.hessian2",
+              "category": "envoy.dubbo_proxy.serializers"
+            },
+            {
+              "name": "envoy.cluster.eds",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.logical_dns",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.original_dst",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.static",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.strict_dns",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.aggregate",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.dynamic_forward_proxy",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.redis",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.health_checkers.redis",
+              "category": "envoy.health_checkers"
+            },
+            {
+              "name": "envoy.transport_sockets.alts",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.quic",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.raw_buffer",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.starttls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tap",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.upstream_proxy_protocol",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "raw_buffer",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "starttls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "tls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.resource_monitors.fixed_heap",
+              "category": "envoy.resource_monitors"
+            },
+            {
+              "name": "envoy.resource_monitors.injected_resource",
+              "category": "envoy.resource_monitors"
+            },
+            {
+              "name": "envoy.grpc_credentials.aws_iam",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "envoy.grpc_credentials.default",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "envoy.grpc_credentials.file_based_metadata",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "default",
+              "category": "envoy.dubbo_proxy.route_matchers"
+            },
+            {
+              "name": "envoy.request_id.uuid",
+              "category": "envoy.request_id"
+            },
+            {
+              "name": "auto",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "binary",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "binary/non-strict",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "compact",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "twitter",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "envoy.retry_host_predicates.omit_canary_hosts",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.retry_host_predicates.omit_host_metadata",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.retry_host_predicates.previous_hosts",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+              "category": "envoy.upstream_options"
+            },
+            {
+              "name": "envoy.upstreams.http.http_protocol_options",
+              "category": "envoy.upstream_options"
+            }
+          ]
+        },
+        "static_resources": {
+          "clusters": [
+            {
+              "name": "access_log_sink",
+              "type": "STATIC",
+              "connect_timeout": "1s",
+              "http2_protocol_options": {},
+              "upstream_connection_options": {
+                "tcp_keepalive": {
+                  "keepalive_probes": 3,
+                  "keepalive_time": 10,
+                  "keepalive_interval": 10
+                }
+              },
+              "load_assignment": {
+                "cluster_name": "access_log_sink",
+                "endpoints": [
+                  {
+                    "lb_endpoints": [
+                      {
+                        "endpoint": {
+                          "address": {
+                            "pipe": {
+                              "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                ]
+              }
+            },
+            {
+              "name": "ads_cluster",
+              "type": "STRICT_DNS",
+              "connect_timeout": "1s",
+              "http2_protocol_options": {},
+              "transport_socket": {
+                "name": "envoy.transport_sockets.tls",
+                "typed_config": {
+                  "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+                  "common_tls_context": {
+                    "tls_params": {
+                      "tls_minimum_protocol_version": "TLSv1_2"
+                    },
+                    "validation_context_sds_secret_config": {
+                      "name": "cp_validation_ctx"
+                    }
+                  },
+                  "sni": "localhost"
+                }
+              },
+              "upstream_connection_options": {
+                "tcp_keepalive": {
+                  "keepalive_probes": 3,
+                  "keepalive_time": 10,
+                  "keepalive_interval": 10
+                }
+              },
+              "load_assignment": {
+                "cluster_name": "ads_cluster",
+                "endpoints": [
+                  {
+                    "lb_endpoints": [
+                      {
+                        "endpoint": {
+                          "address": {
+                            "socket_address": {
+                              "address": "localhost",
+                              "port_value": 5678
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                ]
+              }
+            }
+          ],
+          "secrets": [
+            {
+              "name": "cp_validation_ctx",
+              "validation_context": {
+                "trusted_ca": {
+                  "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+                },
+                "match_subject_alt_names": [
+                  {
+                    "exact": "localhost"
+                  }
+                ]
+              }
+            }
+          ]
+        },
+        "dynamic_resources": {
+          "lds_config": {
+            "ads": {},
+            "resource_api_version": "V3"
+          },
+          "cds_config": {
+            "ads": {},
+            "resource_api_version": "V3"
+          },
+          "ads_config": {
+            "api_type": "GRPC",
+            "grpc_services": [
+              {
+                "envoy_grpc": {
+                  "cluster_name": "ads_cluster"
+                },
+                "initial_metadata": [
+                  {
+                    "key": "authorization",
+                    "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEiLCJ0eXAiOiJKV1QifQ.eyJOYW1lIjoiYmFja2VuZC0xIiwiTWVzaCI6ImRlZmF1bHQiLCJUYWdzIjp7fSwiVHlwZSI6IiIsImV4cCI6MTk1OTI3MDQ4OCwibmJmIjoxNjQzOTEwMTg4LCJpYXQiOjE2NDM5MTA0ODgsImp0aSI6IjgzOWQwMDc1LTJlNjEtNDQ0NC1iYjBiLWFkMTBjY2EwOTk3NyJ9.PrKwnx-EuKkIxpppTgdKzGqE3sPCyv2QDsLXW0EDkTfSzo3Rr39W1IXK3nEKSSDSX59QTzik7I_wy7MDtrlneZGN7amm8sXVkV5MwV-IkVnEuXAYMojLatOphdfyAiDZUnHP4kanpXeBz-qe23Vd2XRa788WCQle1BoHjVEfZpemcvDP_1ioCIi97_8T8C6OmMIDXR_VqA65EeKh-CjiexIxbwY_LHS2KeYwtSvq6-1egDiVBRxl1auei4L-IhrAZXtBiuQnvbw6Mh32FEb6o7V0oRky5MI_QHgxgxxWbGPY77rowHxWX-InAWir9X8Mtdi6A6tOU1Wq_trqWVRhwQ"
+                  }
+                ]
+              }
+            ],
+            "set_node_on_first_message_only": true,
+            "transport_api_version": "V3"
+          }
+        },
+        "admin": {
+          "access_log_path": "/dev/null",
+          "address": {
+            "socket_address": {
+              "address": "127.0.0.1",
+              "port_value": 6606
+            }
+          }
+        },
+        "stats_config": {
+          "stats_tags": [
+            {
+              "tag_name": "name",
+              "regex": "^grpc\\.((.+)\\.)"
+            },
+            {
+              "tag_name": "status",
+              "regex": "^grpc.*streams_closed(_([0-9]+))"
+            },
+            {
+              "tag_name": "kafka_name",
+              "regex": "^kafka(\\.(\\S*[0-9]))\\."
+            },
+            {
+              "tag_name": "kafka_type",
+              "regex": "^kafka\\..*\\.(.*)"
+            },
+            {
+              "tag_name": "worker",
+              "regex": "(worker_([0-9]+)\\.)"
+            },
+            {
+              "tag_name": "listener",
+              "regex": "((.+?)\\.)rbac\\."
+            }
+          ]
+        },
+        "hds_config": {
+          "api_type": "GRPC",
+          "grpc_services": [
+            {
+              "envoy_grpc": {
+                "cluster_name": "ads_cluster"
+              },
+              "initial_metadata": [
+                {
+                  "key": "authorization",
+                  "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEiLCJ0eXAiOiJKV1QifQ.eyJOYW1lIjoiYmFja2VuZC0xIiwiTWVzaCI6ImRlZmF1bHQiLCJUYWdzIjp7fSwiVHlwZSI6IiIsImV4cCI6MTk1OTI3MDQ4OCwibmJmIjoxNjQzOTEwMTg4LCJpYXQiOjE2NDM5MTA0ODgsImp0aSI6IjgzOWQwMDc1LTJlNjEtNDQ0NC1iYjBiLWFkMTBjY2EwOTk3NyJ9.PrKwnx-EuKkIxpppTgdKzGqE3sPCyv2QDsLXW0EDkTfSzo3Rr39W1IXK3nEKSSDSX59QTzik7I_wy7MDtrlneZGN7amm8sXVkV5MwV-IkVnEuXAYMojLatOphdfyAiDZUnHP4kanpXeBz-qe23Vd2XRa788WCQle1BoHjVEfZpemcvDP_1ioCIi97_8T8C6OmMIDXR_VqA65EeKh-CjiexIxbwY_LHS2KeYwtSvq6-1egDiVBRxl1auei4L-IhrAZXtBiuQnvbw6Mh32FEb6o7V0oRky5MI_QHgxgxxWbGPY77rowHxWX-InAWir9X8Mtdi6A6tOU1Wq_trqWVRhwQ"
+                }
+              ]
+            }
+          ],
+          "set_node_on_first_message_only": true,
+          "transport_api_version": "V3"
+        },
+        "layered_runtime": {
+          "layers": [
+            {
+              "name": "kuma",
+              "static_layer": {
+                "re2.max_program_size.error_level": "4294967295",
+                "re2.max_program_size.warn_level": 1000,
+                "envoy.restart_features.use_apple_api_for_dns_lookups": false
+              }
+            }
+          ]
+        }
+      },
+      "last_updated": "2022-02-03T17:48:08.328Z"
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
+      "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+      "static_clusters": [
+        {
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+            "name": "access_log_sink",
+            "type": "STATIC",
+            "connect_timeout": "1s",
+            "http2_protocol_options": {},
+            "upstream_connection_options": {
+              "tcp_keepalive": {
+                "keepalive_probes": 3,
+                "keepalive_time": 10,
+                "keepalive_interval": 10
+              }
+            },
+            "load_assignment": {
+              "cluster_name": "access_log_sink",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "pipe": {
+                            "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:08.373Z"
+        },
+        {
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+            "name": "ads_cluster",
+            "type": "STRICT_DNS",
+            "connect_timeout": "1s",
+            "http2_protocol_options": {},
+            "transport_socket": {
+              "name": "envoy.transport_sockets.tls",
+              "typed_config": {
+                "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+                "common_tls_context": {
+                  "tls_params": {
+                    "tls_minimum_protocol_version": "TLSv1_2"
+                  },
+                  "validation_context_sds_secret_config": {
+                    "name": "cp_validation_ctx"
+                  }
+                },
+                "sni": "localhost"
+              }
+            },
+            "upstream_connection_options": {
+              "tcp_keepalive": {
+                "keepalive_probes": 3,
+                "keepalive_time": 10,
+                "keepalive_interval": 10
+              }
+            },
+            "load_assignment": {
+              "cluster_name": "ads_cluster",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "localhost",
+                            "port_value": 5678
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:08.394Z"
+        }
+      ],
+      "dynamic_active_clusters": [
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "gateway",
+            "type": "EDS",
+            "eds_cluster_config": {
+              "eds_config": {
+                "ads": {},
+                "resource_api_version": "V3"
+              }
+            },
+            "connect_timeout": "5s",
+            "circuit_breakers": {
+              "thresholds": [
+                {
+                  "max_connections": 1024,
+                  "max_pending_requests": 1024,
+                  "max_requests": 1024,
+                  "max_retries": 3
+                }
+              ]
+            },
+            "outlier_detection": {
+              "enforcing_consecutive_5xx": 0,
+              "enforcing_success_rate": 0,
+              "enforcing_consecutive_gateway_failure": 0,
+              "enforcing_consecutive_local_origin_failure": 0,
+              "enforcing_failure_percentage": 0
+            },
+            "typed_extension_protocol_options": {
+              "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
+                "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+                "explicit_http_config": {
+                  "http2_protocol_options": {}
+                }
+              }
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.517Z"
+        },
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "kuma:envoy:admin",
+            "type": "STATIC",
+            "connect_timeout": "10s",
+            "alt_stat_name": "kuma_envoy_admin",
+            "load_assignment": {
+              "cluster_name": "kuma:envoy:admin",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "127.0.0.1",
+                            "port_value": 6606
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.471Z"
+        },
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "localhost:10011",
+            "type": "STATIC",
+            "connect_timeout": "10s",
+            "alt_stat_name": "localhost_10011",
+            "load_assignment": {
+              "cluster_name": "localhost:10011",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "127.0.0.1",
+                            "port_value": 10011
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.492Z"
+        }
+      ]
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
+      "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+      "dynamic_listeners": [
+        {
+          "name": "inbound:127.0.0.1:10010",
+          "active_state": {
+            "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+            "listener": {
+              "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+              "name": "inbound:127.0.0.1:10010",
+              "address": {
+                "socket_address": {
+                  "address": "127.0.0.1",
+                  "port_value": 10010
+                }
+              },
+              "filter_chains": [
+                {
+                  "filters": [
+                    {
+                      "name": "envoy.filters.network.tcp_proxy",
+                      "typed_config": {
+                        "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+                        "stat_prefix": "localhost_10011",
+                        "cluster": "localhost:10011"
+                      }
+                    }
+                  ]
+                }
+              ],
+              "traffic_direction": "INBOUND"
+            },
+            "last_updated": "2022-02-03T17:48:09.548Z"
+          }
+        },
+        {
+          "name": "outbound:127.0.0.1:10006",
+          "active_state": {
+            "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+            "listener": {
+              "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+              "name": "outbound:127.0.0.1:10006",
+              "address": {
+                "socket_address": {
+                  "address": "127.0.0.1",
+                  "port_value": 10006
+                }
+              },
+              "filter_chains": [
+                {
+                  "filters": [
+                    {
+                      "name": "envoy.filters.network.tcp_proxy",
+                      "typed_config": {
+                        "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+                        "stat_prefix": "gateway",
+                        "cluster": "gateway",
+                        "max_connect_attempts": 5,
+                        "idle_timeout": "3600s"
+                      }
+                    }
+                  ]
+                }
+              ],
+              "traffic_direction": "OUTBOUND"
+            },
+            "last_updated": "2022-02-03T17:48:09.550Z"
+          }
+        }
+      ]
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.SecretsConfigDump",
+      "static_secrets": [
+        {
+          "name": "cp_validation_ctx",
+          "secret": {
+            "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
+            "name": "cp_validation_ctx",
+            "validation_context": {
+              "trusted_ca": {
+                "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+              },
+              "match_subject_alt_names": [
+                {
+                  "exact": "localhost"
+                }
+              ]
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/pkg/envoy/admin/testdata/golden.full_config.json b/pkg/envoy/admin/testdata/golden.full_config.json
new file mode 100644
index 0000000..fcecb52
--- /dev/null
+++ b/pkg/envoy/admin/testdata/golden.full_config.json
@@ -0,0 +1,1429 @@
+{
+  "configs": [
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
+      "bootstrap": {
+        "node": {
+          "id": "default.backend-1",
+          "cluster": "backend",
+          "metadata": {
+            "dataplane.proxyType": "dataplane",
+            "version": {
+              "envoy": {
+                "version": "1.19.0",
+                "build": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL"
+              },
+              "dependencies": {},
+              "kumaDp": {
+                "version": "dev-9fd12f2f3",
+                "buildDate": "2022-02-03T17:18:11Z",
+                "gitTag": "1.4.0-rc1-265-g9fd12f2f3",
+                "gitCommit": "9fd12f2f3adbb13dc8a724e4bd76ea149faf6c0c"
+              }
+            },
+            "dataplane.dns.port": "15054",
+            "dynamicMetadata": {
+              "version.dependencies.coredns": "1.8.3"
+            },
+            "dataplane.admin.port": "6606",
+            "dataplane.resource": "{\"type\":\"Dataplane\",\"mesh\":\"default\",\"name\":\"backend-1\",\"creationTime\":\"0001-01-01T00:00:00Z\",\"modificationTime\":\"0001-01-01T00:00:00Z\",\"networking\":{\"address\":\"127.0.0.1\",\"inbound\":[{\"port\":10010,\"servicePort\":10011,\"tags\":{\"kuma.io/protocol\":\"tcp\",\"kuma.io/region\":\"reg1\",\"kuma.io/service\":\"backend\",\"kuma.io/sub-zone\":\"subzone1\",\"version\":\"1\"}}],\"outbound\":[{\"port\":10006,\"tags\":{\"kuma.io/service\":\"gateway\"}}],\"admin\":{\"port\":6606}}}"
+          },
+          "hidden_envoy_deprecated_build_version": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL",
+          "user_agent_name": "envoy",
+          "user_agent_build_version": {
+            "version": {
+              "major_number": 1,
+              "minor_number": 19
+            },
+            "metadata": {
+              "build.type": "RELEASE",
+              "revision.sha": "68fe53a889416fd8570506232052b06f5a531541",
+              "revision.status": "Modified",
+              "ssl.version": "BoringSSL"
+            }
+          },
+          "extensions": [
+            {
+              "name": "envoy.filters.dubbo.router",
+              "category": "envoy.dubbo_proxy.filters"
+            },
+            {
+              "name": "envoy.filters.thrift.rate_limit",
+              "category": "envoy.thrift_proxy.filters"
+            },
+            {
+              "name": "envoy.filters.thrift.router",
+              "category": "envoy.thrift_proxy.filters"
+            },
+            {
+              "name": "composite-action",
+              "category": "envoy.matching.action"
+            },
+            {
+              "name": "skip",
+              "category": "envoy.matching.action"
+            },
+            {
+              "name": "envoy.rate_limit_descriptors.expr",
+              "category": "envoy.rate_limit_descriptors"
+            },
+            {
+              "name": "envoy.matching.common_inputs.environment_variable",
+              "category": "envoy.matching.common_inputs"
+            },
+            {
+              "name": "envoy.compression.brotli.decompressor",
+              "category": "envoy.compression.decompressor"
+            },
+            {
+              "name": "envoy.compression.gzip.decompressor",
+              "category": "envoy.compression.decompressor"
+            },
+            {
+              "name": "envoy.access_loggers.file",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.http_grpc",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.open_telemetry",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.stderr",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.stdout",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.tcp_grpc",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.wasm",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.file_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.http_grpc_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.open_telemetry_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.stderr_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.stdout_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.tcp_grpc_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.wasm_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.transport_sockets.alts",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.quic",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.raw_buffer",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.starttls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tap",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "raw_buffer",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "starttls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "tls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.dynamic.ot",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.lightstep",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.datadog",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.dynamic_ot",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.lightstep",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.opencensus",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.skywalking",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.xray",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.zipkin",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.zipkin",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.bootstrap.wasm",
+              "category": "envoy.bootstrap"
+            },
+            {
+              "name": "envoy.extensions.network.socket_interface.default_socket_interface",
+              "category": "envoy.bootstrap"
+            },
+            {
+              "name": "envoy.filters.listener.http_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.original_dst",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.original_src",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.proxy_protocol",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.tls_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.http_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.original_dst",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.original_src",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.proxy_protocol",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.tls_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.client_ssl_auth",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.echo",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.ext_authz",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.client_ssl_auth",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.connection_limit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.direct_response",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.dubbo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.echo",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.ext_authz",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.http_connection_manager",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.kafka_broker",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.local_ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.mongo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.mysql_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.postgres_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.rbac",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.redis_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.rocketmq_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.sni_cluster",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.sni_dynamic_forward_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.tcp_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.thrift_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.wasm",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.zookeeper_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.http_connection_manager",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.mongo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.redis_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.tcp_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.http.original_ip_detection.custom_header",
+              "category": "envoy.http.original_ip_detection"
+            },
+            {
+              "name": "envoy.http.original_ip_detection.xff",
+              "category": "envoy.http.original_ip_detection"
+            },
+            {
+              "name": "request-headers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "request-trailers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "response-headers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "response-trailers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "envoy.filters.udp.dns_filter",
+              "category": "envoy.filters.udp_listener"
+            },
+            {
+              "name": "envoy.filters.udp_listener.udp_proxy",
+              "category": "envoy.filters.udp_listener"
+            },
+            {
+              "name": "dubbo",
+              "category": "envoy.dubbo_proxy.protocols"
+            },
+            {
+              "name": "envoy.ip",
+              "category": "envoy.resolvers"
+            },
+            {
+              "name": "envoy.compression.brotli.compressor",
+              "category": "envoy.compression.compressor"
+            },
+            {
+              "name": "envoy.compression.gzip.compressor",
+              "category": "envoy.compression.compressor"
+            },
+            {
+              "name": "envoy.matching.matchers.consistent_hashing",
+              "category": "envoy.matching.input_matchers"
+            },
+            {
+              "name": "envoy.matching.matchers.ip",
+              "category": "envoy.matching.input_matchers"
+            },
+            {
+              "name": "preserve_case",
+              "category": "envoy.http.stateful_header_formatters"
+            },
+            {
+              "name": "envoy.watchdog.abort_action",
+              "category": "envoy.guarddog_actions"
+            },
+            {
+              "name": "envoy.watchdog.profile_action",
+              "category": "envoy.guarddog_actions"
+            },
+            {
+              "name": "envoy.filters.connection_pools.tcp.generic",
+              "category": "envoy.upstreams"
+            },
+            {
+              "name": "envoy.formatter.req_without_query",
+              "category": "envoy.formatter"
+            },
+            {
+              "name": "auto",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "framed",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "header",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "unframed",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "envoy.bandwidth_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.buffer",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.cors",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.csrf",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ext_authz",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ext_proc",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.fault",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.adaptive_concurrency",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.admission_control",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.alternate_protocols_cache",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.aws_lambda",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.aws_request_signing",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.bandwidth_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.buffer",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cache",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cdn_loop",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.composite",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.compressor",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cors",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.csrf",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.decompressor",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.dynamic_forward_proxy",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.dynamo",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ext_authz",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ext_proc",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.fault",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_http1_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_http1_reverse_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_json_transcoder",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_stats",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_web",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.header_to_metadata",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.health_check",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ip_tagging",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.jwt_authn",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.local_ratelimit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.lua",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.oauth2",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.on_demand",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.original_src",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ratelimit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.rbac",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.router",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.set_metadata",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.squash",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.tap",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.wasm",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_http1_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_json_transcoder",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_web",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.health_check",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.http_dynamo_filter",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ip_tagging",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.local_rate_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.lua",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.rate_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.router",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.squash",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "match-wrapper",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.extensions.http.cache.simple",
+              "category": "envoy.http.cache"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.allow_listed_routes",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.previous_routes",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.safe_cross_scheme",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.quic.proof_source.filter_chain",
+              "category": "envoy.quic.proof_source"
+            },
+            {
+              "name": "envoy.retry_priorities.previous_priorities",
+              "category": "envoy.retry_priorities"
+            },
+            {
+              "name": "envoy.wasm.runtime.null",
+              "category": "envoy.wasm.runtime"
+            },
+            {
+              "name": "envoy.wasm.runtime.v8",
+              "category": "envoy.wasm.runtime"
+            },
+            {
+              "name": "envoy.quic.crypto_stream.server.quiche",
+              "category": "envoy.quic.server.crypto_stream"
+            },
+            {
+              "name": "envoy.tls.cert_validator.default",
+              "category": "envoy.tls.cert_validator"
+            },
+            {
+              "name": "envoy.tls.cert_validator.spiffe",
+              "category": "envoy.tls.cert_validator"
+            },
+            {
+              "name": "envoy.dog_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.graphite_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.metrics_service",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.dog_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.graphite_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.hystrix",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.metrics_service",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.wasm",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "dubbo.hessian2",
+              "category": "envoy.dubbo_proxy.serializers"
+            },
+            {
+              "name": "envoy.cluster.eds",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.logical_dns",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.original_dst",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.static",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.strict_dns",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.aggregate",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.dynamic_forward_proxy",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.redis",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.health_checkers.redis",
+              "category": "envoy.health_checkers"
+            },
+            {
+              "name": "envoy.transport_sockets.alts",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.quic",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.raw_buffer",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.starttls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tap",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.upstream_proxy_protocol",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "raw_buffer",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "starttls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "tls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.resource_monitors.fixed_heap",
+              "category": "envoy.resource_monitors"
+            },
+            {
+              "name": "envoy.resource_monitors.injected_resource",
+              "category": "envoy.resource_monitors"
+            },
+            {
+              "name": "envoy.grpc_credentials.aws_iam",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "envoy.grpc_credentials.default",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "envoy.grpc_credentials.file_based_metadata",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "default",
+              "category": "envoy.dubbo_proxy.route_matchers"
+            },
+            {
+              "name": "envoy.request_id.uuid",
+              "category": "envoy.request_id"
+            },
+            {
+              "name": "auto",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "binary",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "binary/non-strict",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "compact",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "twitter",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "envoy.retry_host_predicates.omit_canary_hosts",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.retry_host_predicates.omit_host_metadata",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.retry_host_predicates.previous_hosts",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+              "category": "envoy.upstream_options"
+            },
+            {
+              "name": "envoy.upstreams.http.http_protocol_options",
+              "category": "envoy.upstream_options"
+            }
+          ]
+        },
+        "static_resources": {
+          "clusters": [
+            {
+              "name": "access_log_sink",
+              "type": "STATIC",
+              "connect_timeout": "1s",
+              "http2_protocol_options": {},
+              "upstream_connection_options": {
+                "tcp_keepalive": {
+                  "keepalive_probes": 3,
+                  "keepalive_time": 10,
+                  "keepalive_interval": 10
+                }
+              },
+              "load_assignment": {
+                "cluster_name": "access_log_sink",
+                "endpoints": [
+                  {
+                    "lb_endpoints": [
+                      {
+                        "endpoint": {
+                          "address": {
+                            "pipe": {
+                              "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                ]
+              }
+            },
+            {
+              "name": "ads_cluster",
+              "type": "STRICT_DNS",
+              "connect_timeout": "1s",
+              "http2_protocol_options": {},
+              "transport_socket": {
+                "name": "envoy.transport_sockets.tls",
+                "typed_config": {
+                  "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+                  "common_tls_context": {
+                    "tls_params": {
+                      "tls_minimum_protocol_version": "TLSv1_2"
+                    },
+                    "validation_context_sds_secret_config": {
+                      "name": "cp_validation_ctx"
+                    }
+                  },
+                  "sni": "localhost"
+                }
+              },
+              "upstream_connection_options": {
+                "tcp_keepalive": {
+                  "keepalive_probes": 3,
+                  "keepalive_time": 10,
+                  "keepalive_interval": 10
+                }
+              },
+              "load_assignment": {
+                "cluster_name": "ads_cluster",
+                "endpoints": [
+                  {
+                    "lb_endpoints": [
+                      {
+                        "endpoint": {
+                          "address": {
+                            "socket_address": {
+                              "address": "localhost",
+                              "port_value": 5678
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                ]
+              }
+            }
+          ],
+          "secrets": [
+            {
+              "name": "cp_validation_ctx",
+              "validation_context": {
+                "trusted_ca": {
+                  "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+                },
+                "match_subject_alt_names": [
+                  {
+                    "exact": "localhost"
+                  }
+                ]
+              }
+            }
+          ]
+        },
+        "dynamic_resources": {
+          "lds_config": {
+            "ads": {},
+            "resource_api_version": "V3"
+          },
+          "cds_config": {
+            "ads": {},
+            "resource_api_version": "V3"
+          },
+          "ads_config": {
+            "api_type": "GRPC",
+            "grpc_services": [
+              {
+                "envoy_grpc": {
+                  "cluster_name": "ads_cluster"
+                },
+                "initial_metadata": [
+                  {
+                    "key": "authorization",
+                    "value": "[redacted]"
+                  }
+                ]
+              }
+            ],
+            "set_node_on_first_message_only": true,
+            "transport_api_version": "V3"
+          }
+        },
+        "admin": {
+          "access_log_path": "/dev/null",
+          "address": {
+            "socket_address": {
+              "address": "127.0.0.1",
+              "port_value": 6606
+            }
+          }
+        },
+        "stats_config": {
+          "stats_tags": [
+            {
+              "tag_name": "name",
+              "regex": "^grpc\\.((.+)\\.)"
+            },
+            {
+              "tag_name": "status",
+              "regex": "^grpc.*streams_closed(_([0-9]+))"
+            },
+            {
+              "tag_name": "kafka_name",
+              "regex": "^kafka(\\.(\\S*[0-9]))\\."
+            },
+            {
+              "tag_name": "kafka_type",
+              "regex": "^kafka\\..*\\.(.*)"
+            },
+            {
+              "tag_name": "worker",
+              "regex": "(worker_([0-9]+)\\.)"
+            },
+            {
+              "tag_name": "listener",
+              "regex": "((.+?)\\.)rbac\\."
+            }
+          ]
+        },
+        "hds_config": {
+          "api_type": "GRPC",
+          "grpc_services": [
+            {
+              "envoy_grpc": {
+                "cluster_name": "ads_cluster"
+              },
+              "initial_metadata": [
+                {
+                  "key": "authorization",
+                  "value": "[redacted]"
+                }
+              ]
+            }
+          ],
+          "set_node_on_first_message_only": true,
+          "transport_api_version": "V3"
+        },
+        "layered_runtime": {
+          "layers": [
+            {
+              "name": "kuma",
+              "static_layer": {
+                "re2.max_program_size.error_level": "4294967295",
+                "re2.max_program_size.warn_level": 1000,
+                "envoy.restart_features.use_apple_api_for_dns_lookups": false
+              }
+            }
+          ]
+        }
+      },
+      "last_updated": "2022-02-03T17:48:08.328Z"
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
+      "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+      "static_clusters": [
+        {
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+            "name": "access_log_sink",
+            "type": "STATIC",
+            "connect_timeout": "1s",
+            "http2_protocol_options": {},
+            "upstream_connection_options": {
+              "tcp_keepalive": {
+                "keepalive_probes": 3,
+                "keepalive_time": 10,
+                "keepalive_interval": 10
+              }
+            },
+            "load_assignment": {
+              "cluster_name": "access_log_sink",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "pipe": {
+                            "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:08.373Z"
+        },
+        {
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+            "name": "ads_cluster",
+            "type": "STRICT_DNS",
+            "connect_timeout": "1s",
+            "http2_protocol_options": {},
+            "transport_socket": {
+              "name": "envoy.transport_sockets.tls",
+              "typed_config": {
+                "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+                "common_tls_context": {
+                  "tls_params": {
+                    "tls_minimum_protocol_version": "TLSv1_2"
+                  },
+                  "validation_context_sds_secret_config": {
+                    "name": "cp_validation_ctx"
+                  }
+                },
+                "sni": "localhost"
+              }
+            },
+            "upstream_connection_options": {
+              "tcp_keepalive": {
+                "keepalive_probes": 3,
+                "keepalive_time": 10,
+                "keepalive_interval": 10
+              }
+            },
+            "load_assignment": {
+              "cluster_name": "ads_cluster",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "localhost",
+                            "port_value": 5678
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:08.394Z"
+        }
+      ],
+      "dynamic_active_clusters": [
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "gateway",
+            "type": "EDS",
+            "eds_cluster_config": {
+              "eds_config": {
+                "ads": {},
+                "resource_api_version": "V3"
+              }
+            },
+            "connect_timeout": "5s",
+            "circuit_breakers": {
+              "thresholds": [
+                {
+                  "max_connections": 1024,
+                  "max_pending_requests": 1024,
+                  "max_requests": 1024,
+                  "max_retries": 3
+                }
+              ]
+            },
+            "outlier_detection": {
+              "enforcing_consecutive_5xx": 0,
+              "enforcing_success_rate": 0,
+              "enforcing_consecutive_gateway_failure": 0,
+              "enforcing_consecutive_local_origin_failure": 0,
+              "enforcing_failure_percentage": 0
+            },
+            "typed_extension_protocol_options": {
+              "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
+                "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+                "explicit_http_config": {
+                  "http2_protocol_options": {}
+                }
+              }
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.517Z"
+        },
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "kuma:envoy:admin",
+            "type": "STATIC",
+            "connect_timeout": "10s",
+            "alt_stat_name": "kuma_envoy_admin",
+            "load_assignment": {
+              "cluster_name": "kuma:envoy:admin",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "127.0.0.1",
+                            "port_value": 6606
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.471Z"
+        },
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "localhost:10011",
+            "type": "STATIC",
+            "connect_timeout": "10s",
+            "alt_stat_name": "localhost_10011",
+            "load_assignment": {
+              "cluster_name": "localhost:10011",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "127.0.0.1",
+                            "port_value": 10011
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.492Z"
+        }
+      ]
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
+      "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+      "dynamic_listeners": [
+        {
+          "name": "inbound:127.0.0.1:10010",
+          "active_state": {
+            "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+            "listener": {
+              "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+              "name": "inbound:127.0.0.1:10010",
+              "address": {
+                "socket_address": {
+                  "address": "127.0.0.1",
+                  "port_value": 10010
+                }
+              },
+              "filter_chains": [
+                {
+                  "filters": [
+                    {
+                      "name": "envoy.filters.network.tcp_proxy",
+                      "typed_config": {
+                        "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+                        "stat_prefix": "localhost_10011",
+                        "cluster": "localhost:10011"
+                      }
+                    }
+                  ]
+                }
+              ],
+              "traffic_direction": "INBOUND"
+            },
+            "last_updated": "2022-02-03T17:48:09.548Z"
+          }
+        },
+        {
+          "name": "outbound:127.0.0.1:10006",
+          "active_state": {
+            "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+            "listener": {
+              "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+              "name": "outbound:127.0.0.1:10006",
+              "address": {
+                "socket_address": {
+                  "address": "127.0.0.1",
+                  "port_value": 10006
+                }
+              },
+              "filter_chains": [
+                {
+                  "filters": [
+                    {
+                      "name": "envoy.filters.network.tcp_proxy",
+                      "typed_config": {
+                        "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+                        "stat_prefix": "gateway",
+                        "cluster": "gateway",
+                        "max_connect_attempts": 5,
+                        "idle_timeout": "3600s"
+                      }
+                    }
+                  ]
+                }
+              ],
+              "traffic_direction": "OUTBOUND"
+            },
+            "last_updated": "2022-02-03T17:48:09.550Z"
+          }
+        }
+      ]
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.SecretsConfigDump",
+      "static_secrets": [
+        {
+          "name": "cp_validation_ctx",
+          "secret": {
+            "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
+            "name": "cp_validation_ctx",
+            "validation_context": {
+              "trusted_ca": {
+                "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+              },
+              "match_subject_alt_names": [
+                {
+                  "exact": "localhost"
+                }
+              ]
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/pkg/envoy/admin/testdata/golden.no_hds.json b/pkg/envoy/admin/testdata/golden.no_hds.json
new file mode 100644
index 0000000..3a957ec
--- /dev/null
+++ b/pkg/envoy/admin/testdata/golden.no_hds.json
@@ -0,0 +1,1411 @@
+{
+  "configs": [
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
+      "bootstrap": {
+        "node": {
+          "id": "default.backend-1",
+          "cluster": "backend",
+          "metadata": {
+            "dataplane.proxyType": "dataplane",
+            "version": {
+              "envoy": {
+                "version": "1.19.0",
+                "build": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL"
+              },
+              "dependencies": {},
+              "kumaDp": {
+                "version": "dev-9fd12f2f3",
+                "buildDate": "2022-02-03T17:18:11Z",
+                "gitTag": "1.4.0-rc1-265-g9fd12f2f3",
+                "gitCommit": "9fd12f2f3adbb13dc8a724e4bd76ea149faf6c0c"
+              }
+            },
+            "dataplane.dns.port": "15054",
+            "dynamicMetadata": {
+              "version.dependencies.coredns": "1.8.3"
+            },
+            "dataplane.admin.port": "6606",
+            "dataplane.resource": "{\"type\":\"Dataplane\",\"mesh\":\"default\",\"name\":\"backend-1\",\"creationTime\":\"0001-01-01T00:00:00Z\",\"modificationTime\":\"0001-01-01T00:00:00Z\",\"networking\":{\"address\":\"127.0.0.1\",\"inbound\":[{\"port\":10010,\"servicePort\":10011,\"tags\":{\"kuma.io/protocol\":\"tcp\",\"kuma.io/region\":\"reg1\",\"kuma.io/service\":\"backend\",\"kuma.io/sub-zone\":\"subzone1\",\"version\":\"1\"}}],\"outbound\":[{\"port\":10006,\"tags\":{\"kuma.io/service\":\"gateway\"}}],\"admin\":{\"port\":6606}}}"
+          },
+          "hidden_envoy_deprecated_build_version": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL",
+          "user_agent_name": "envoy",
+          "user_agent_build_version": {
+            "version": {
+              "major_number": 1,
+              "minor_number": 19
+            },
+            "metadata": {
+              "build.type": "RELEASE",
+              "revision.sha": "68fe53a889416fd8570506232052b06f5a531541",
+              "revision.status": "Modified",
+              "ssl.version": "BoringSSL"
+            }
+          },
+          "extensions": [
+            {
+              "name": "envoy.filters.dubbo.router",
+              "category": "envoy.dubbo_proxy.filters"
+            },
+            {
+              "name": "envoy.filters.thrift.rate_limit",
+              "category": "envoy.thrift_proxy.filters"
+            },
+            {
+              "name": "envoy.filters.thrift.router",
+              "category": "envoy.thrift_proxy.filters"
+            },
+            {
+              "name": "composite-action",
+              "category": "envoy.matching.action"
+            },
+            {
+              "name": "skip",
+              "category": "envoy.matching.action"
+            },
+            {
+              "name": "envoy.rate_limit_descriptors.expr",
+              "category": "envoy.rate_limit_descriptors"
+            },
+            {
+              "name": "envoy.matching.common_inputs.environment_variable",
+              "category": "envoy.matching.common_inputs"
+            },
+            {
+              "name": "envoy.compression.brotli.decompressor",
+              "category": "envoy.compression.decompressor"
+            },
+            {
+              "name": "envoy.compression.gzip.decompressor",
+              "category": "envoy.compression.decompressor"
+            },
+            {
+              "name": "envoy.access_loggers.file",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.http_grpc",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.open_telemetry",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.stderr",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.stdout",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.tcp_grpc",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.wasm",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.file_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.http_grpc_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.open_telemetry_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.stderr_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.stdout_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.tcp_grpc_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.wasm_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.transport_sockets.alts",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.quic",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.raw_buffer",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.starttls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tap",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "raw_buffer",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "starttls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "tls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.dynamic.ot",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.lightstep",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.datadog",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.dynamic_ot",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.lightstep",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.opencensus",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.skywalking",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.xray",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.zipkin",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.zipkin",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.bootstrap.wasm",
+              "category": "envoy.bootstrap"
+            },
+            {
+              "name": "envoy.extensions.network.socket_interface.default_socket_interface",
+              "category": "envoy.bootstrap"
+            },
+            {
+              "name": "envoy.filters.listener.http_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.original_dst",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.original_src",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.proxy_protocol",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.tls_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.http_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.original_dst",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.original_src",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.proxy_protocol",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.tls_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.client_ssl_auth",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.echo",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.ext_authz",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.client_ssl_auth",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.connection_limit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.direct_response",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.dubbo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.echo",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.ext_authz",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.http_connection_manager",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.kafka_broker",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.local_ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.mongo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.mysql_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.postgres_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.rbac",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.redis_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.rocketmq_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.sni_cluster",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.sni_dynamic_forward_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.tcp_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.thrift_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.wasm",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.zookeeper_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.http_connection_manager",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.mongo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.redis_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.tcp_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.http.original_ip_detection.custom_header",
+              "category": "envoy.http.original_ip_detection"
+            },
+            {
+              "name": "envoy.http.original_ip_detection.xff",
+              "category": "envoy.http.original_ip_detection"
+            },
+            {
+              "name": "request-headers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "request-trailers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "response-headers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "response-trailers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "envoy.filters.udp.dns_filter",
+              "category": "envoy.filters.udp_listener"
+            },
+            {
+              "name": "envoy.filters.udp_listener.udp_proxy",
+              "category": "envoy.filters.udp_listener"
+            },
+            {
+              "name": "dubbo",
+              "category": "envoy.dubbo_proxy.protocols"
+            },
+            {
+              "name": "envoy.ip",
+              "category": "envoy.resolvers"
+            },
+            {
+              "name": "envoy.compression.brotli.compressor",
+              "category": "envoy.compression.compressor"
+            },
+            {
+              "name": "envoy.compression.gzip.compressor",
+              "category": "envoy.compression.compressor"
+            },
+            {
+              "name": "envoy.matching.matchers.consistent_hashing",
+              "category": "envoy.matching.input_matchers"
+            },
+            {
+              "name": "envoy.matching.matchers.ip",
+              "category": "envoy.matching.input_matchers"
+            },
+            {
+              "name": "preserve_case",
+              "category": "envoy.http.stateful_header_formatters"
+            },
+            {
+              "name": "envoy.watchdog.abort_action",
+              "category": "envoy.guarddog_actions"
+            },
+            {
+              "name": "envoy.watchdog.profile_action",
+              "category": "envoy.guarddog_actions"
+            },
+            {
+              "name": "envoy.filters.connection_pools.tcp.generic",
+              "category": "envoy.upstreams"
+            },
+            {
+              "name": "envoy.formatter.req_without_query",
+              "category": "envoy.formatter"
+            },
+            {
+              "name": "auto",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "framed",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "header",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "unframed",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "envoy.bandwidth_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.buffer",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.cors",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.csrf",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ext_authz",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ext_proc",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.fault",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.adaptive_concurrency",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.admission_control",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.alternate_protocols_cache",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.aws_lambda",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.aws_request_signing",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.bandwidth_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.buffer",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cache",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cdn_loop",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.composite",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.compressor",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cors",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.csrf",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.decompressor",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.dynamic_forward_proxy",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.dynamo",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ext_authz",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ext_proc",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.fault",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_http1_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_http1_reverse_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_json_transcoder",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_stats",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_web",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.header_to_metadata",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.health_check",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ip_tagging",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.jwt_authn",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.local_ratelimit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.lua",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.oauth2",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.on_demand",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.original_src",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ratelimit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.rbac",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.router",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.set_metadata",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.squash",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.tap",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.wasm",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_http1_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_json_transcoder",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_web",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.health_check",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.http_dynamo_filter",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ip_tagging",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.local_rate_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.lua",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.rate_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.router",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.squash",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "match-wrapper",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.extensions.http.cache.simple",
+              "category": "envoy.http.cache"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.allow_listed_routes",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.previous_routes",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.safe_cross_scheme",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.quic.proof_source.filter_chain",
+              "category": "envoy.quic.proof_source"
+            },
+            {
+              "name": "envoy.retry_priorities.previous_priorities",
+              "category": "envoy.retry_priorities"
+            },
+            {
+              "name": "envoy.wasm.runtime.null",
+              "category": "envoy.wasm.runtime"
+            },
+            {
+              "name": "envoy.wasm.runtime.v8",
+              "category": "envoy.wasm.runtime"
+            },
+            {
+              "name": "envoy.quic.crypto_stream.server.quiche",
+              "category": "envoy.quic.server.crypto_stream"
+            },
+            {
+              "name": "envoy.tls.cert_validator.default",
+              "category": "envoy.tls.cert_validator"
+            },
+            {
+              "name": "envoy.tls.cert_validator.spiffe",
+              "category": "envoy.tls.cert_validator"
+            },
+            {
+              "name": "envoy.dog_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.graphite_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.metrics_service",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.dog_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.graphite_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.hystrix",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.metrics_service",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.wasm",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "dubbo.hessian2",
+              "category": "envoy.dubbo_proxy.serializers"
+            },
+            {
+              "name": "envoy.cluster.eds",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.logical_dns",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.original_dst",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.static",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.strict_dns",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.aggregate",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.dynamic_forward_proxy",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.redis",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.health_checkers.redis",
+              "category": "envoy.health_checkers"
+            },
+            {
+              "name": "envoy.transport_sockets.alts",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.quic",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.raw_buffer",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.starttls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tap",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.upstream_proxy_protocol",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "raw_buffer",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "starttls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "tls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.resource_monitors.fixed_heap",
+              "category": "envoy.resource_monitors"
+            },
+            {
+              "name": "envoy.resource_monitors.injected_resource",
+              "category": "envoy.resource_monitors"
+            },
+            {
+              "name": "envoy.grpc_credentials.aws_iam",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "envoy.grpc_credentials.default",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "envoy.grpc_credentials.file_based_metadata",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "default",
+              "category": "envoy.dubbo_proxy.route_matchers"
+            },
+            {
+              "name": "envoy.request_id.uuid",
+              "category": "envoy.request_id"
+            },
+            {
+              "name": "auto",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "binary",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "binary/non-strict",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "compact",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "twitter",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "envoy.retry_host_predicates.omit_canary_hosts",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.retry_host_predicates.omit_host_metadata",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.retry_host_predicates.previous_hosts",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+              "category": "envoy.upstream_options"
+            },
+            {
+              "name": "envoy.upstreams.http.http_protocol_options",
+              "category": "envoy.upstream_options"
+            }
+          ]
+        },
+        "static_resources": {
+          "clusters": [
+            {
+              "name": "access_log_sink",
+              "type": "STATIC",
+              "connect_timeout": "1s",
+              "http2_protocol_options": {},
+              "upstream_connection_options": {
+                "tcp_keepalive": {
+                  "keepalive_probes": 3,
+                  "keepalive_time": 10,
+                  "keepalive_interval": 10
+                }
+              },
+              "load_assignment": {
+                "cluster_name": "access_log_sink",
+                "endpoints": [
+                  {
+                    "lb_endpoints": [
+                      {
+                        "endpoint": {
+                          "address": {
+                            "pipe": {
+                              "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                ]
+              }
+            },
+            {
+              "name": "ads_cluster",
+              "type": "STRICT_DNS",
+              "connect_timeout": "1s",
+              "http2_protocol_options": {},
+              "transport_socket": {
+                "name": "envoy.transport_sockets.tls",
+                "typed_config": {
+                  "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+                  "common_tls_context": {
+                    "tls_params": {
+                      "tls_minimum_protocol_version": "TLSv1_2"
+                    },
+                    "validation_context_sds_secret_config": {
+                      "name": "cp_validation_ctx"
+                    }
+                  },
+                  "sni": "localhost"
+                }
+              },
+              "upstream_connection_options": {
+                "tcp_keepalive": {
+                  "keepalive_probes": 3,
+                  "keepalive_time": 10,
+                  "keepalive_interval": 10
+                }
+              },
+              "load_assignment": {
+                "cluster_name": "ads_cluster",
+                "endpoints": [
+                  {
+                    "lb_endpoints": [
+                      {
+                        "endpoint": {
+                          "address": {
+                            "socket_address": {
+                              "address": "localhost",
+                              "port_value": 5678
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                ]
+              }
+            }
+          ],
+          "secrets": [
+            {
+              "name": "cp_validation_ctx",
+              "validation_context": {
+                "trusted_ca": {
+                  "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+                },
+                "match_subject_alt_names": [
+                  {
+                    "exact": "localhost"
+                  }
+                ]
+              }
+            }
+          ]
+        },
+        "dynamic_resources": {
+          "lds_config": {
+            "ads": {},
+            "resource_api_version": "V3"
+          },
+          "cds_config": {
+            "ads": {},
+            "resource_api_version": "V3"
+          },
+          "ads_config": {
+            "api_type": "GRPC",
+            "grpc_services": [
+              {
+                "envoy_grpc": {
+                  "cluster_name": "ads_cluster"
+                },
+                "initial_metadata": [
+                  {
+                    "key": "authorization",
+                    "value": "[redacted]"
+                  }
+                ]
+              }
+            ],
+            "set_node_on_first_message_only": true,
+            "transport_api_version": "V3"
+          }
+        },
+        "admin": {
+          "access_log_path": "/dev/null",
+          "address": {
+            "socket_address": {
+              "address": "127.0.0.1",
+              "port_value": 6606
+            }
+          }
+        },
+        "stats_config": {
+          "stats_tags": [
+            {
+              "tag_name": "name",
+              "regex": "^grpc\\.((.+)\\.)"
+            },
+            {
+              "tag_name": "status",
+              "regex": "^grpc.*streams_closed(_([0-9]+))"
+            },
+            {
+              "tag_name": "kafka_name",
+              "regex": "^kafka(\\.(\\S*[0-9]))\\."
+            },
+            {
+              "tag_name": "kafka_type",
+              "regex": "^kafka\\..*\\.(.*)"
+            },
+            {
+              "tag_name": "worker",
+              "regex": "(worker_([0-9]+)\\.)"
+            },
+            {
+              "tag_name": "listener",
+              "regex": "((.+?)\\.)rbac\\."
+            }
+          ]
+        },
+        "layered_runtime": {
+          "layers": [
+            {
+              "name": "kuma",
+              "static_layer": {
+                "re2.max_program_size.error_level": "4294967295",
+                "re2.max_program_size.warn_level": 1000,
+                "envoy.restart_features.use_apple_api_for_dns_lookups": false
+              }
+            }
+          ]
+        }
+      },
+      "last_updated": "2022-02-03T17:48:08.328Z"
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
+      "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+      "static_clusters": [
+        {
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+            "name": "access_log_sink",
+            "type": "STATIC",
+            "connect_timeout": "1s",
+            "http2_protocol_options": {},
+            "upstream_connection_options": {
+              "tcp_keepalive": {
+                "keepalive_probes": 3,
+                "keepalive_time": 10,
+                "keepalive_interval": 10
+              }
+            },
+            "load_assignment": {
+              "cluster_name": "access_log_sink",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "pipe": {
+                            "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:08.373Z"
+        },
+        {
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+            "name": "ads_cluster",
+            "type": "STRICT_DNS",
+            "connect_timeout": "1s",
+            "http2_protocol_options": {},
+            "transport_socket": {
+              "name": "envoy.transport_sockets.tls",
+              "typed_config": {
+                "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+                "common_tls_context": {
+                  "tls_params": {
+                    "tls_minimum_protocol_version": "TLSv1_2"
+                  },
+                  "validation_context_sds_secret_config": {
+                    "name": "cp_validation_ctx"
+                  }
+                },
+                "sni": "localhost"
+              }
+            },
+            "upstream_connection_options": {
+              "tcp_keepalive": {
+                "keepalive_probes": 3,
+                "keepalive_time": 10,
+                "keepalive_interval": 10
+              }
+            },
+            "load_assignment": {
+              "cluster_name": "ads_cluster",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "localhost",
+                            "port_value": 5678
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:08.394Z"
+        }
+      ],
+      "dynamic_active_clusters": [
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "gateway",
+            "type": "EDS",
+            "eds_cluster_config": {
+              "eds_config": {
+                "ads": {},
+                "resource_api_version": "V3"
+              }
+            },
+            "connect_timeout": "5s",
+            "circuit_breakers": {
+              "thresholds": [
+                {
+                  "max_connections": 1024,
+                  "max_pending_requests": 1024,
+                  "max_requests": 1024,
+                  "max_retries": 3
+                }
+              ]
+            },
+            "outlier_detection": {
+              "enforcing_consecutive_5xx": 0,
+              "enforcing_success_rate": 0,
+              "enforcing_consecutive_gateway_failure": 0,
+              "enforcing_consecutive_local_origin_failure": 0,
+              "enforcing_failure_percentage": 0
+            },
+            "typed_extension_protocol_options": {
+              "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
+                "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+                "explicit_http_config": {
+                  "http2_protocol_options": {}
+                }
+              }
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.517Z"
+        },
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "kuma:envoy:admin",
+            "type": "STATIC",
+            "connect_timeout": "10s",
+            "alt_stat_name": "kuma_envoy_admin",
+            "load_assignment": {
+              "cluster_name": "kuma:envoy:admin",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "127.0.0.1",
+                            "port_value": 6606
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.471Z"
+        },
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "localhost:10011",
+            "type": "STATIC",
+            "connect_timeout": "10s",
+            "alt_stat_name": "localhost_10011",
+            "load_assignment": {
+              "cluster_name": "localhost:10011",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "127.0.0.1",
+                            "port_value": 10011
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.492Z"
+        }
+      ]
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
+      "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+      "dynamic_listeners": [
+        {
+          "name": "inbound:127.0.0.1:10010",
+          "active_state": {
+            "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+            "listener": {
+              "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+              "name": "inbound:127.0.0.1:10010",
+              "address": {
+                "socket_address": {
+                  "address": "127.0.0.1",
+                  "port_value": 10010
+                }
+              },
+              "filter_chains": [
+                {
+                  "filters": [
+                    {
+                      "name": "envoy.filters.network.tcp_proxy",
+                      "typed_config": {
+                        "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+                        "stat_prefix": "localhost_10011",
+                        "cluster": "localhost:10011"
+                      }
+                    }
+                  ]
+                }
+              ],
+              "traffic_direction": "INBOUND"
+            },
+            "last_updated": "2022-02-03T17:48:09.548Z"
+          }
+        },
+        {
+          "name": "outbound:127.0.0.1:10006",
+          "active_state": {
+            "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+            "listener": {
+              "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+              "name": "outbound:127.0.0.1:10006",
+              "address": {
+                "socket_address": {
+                  "address": "127.0.0.1",
+                  "port_value": 10006
+                }
+              },
+              "filter_chains": [
+                {
+                  "filters": [
+                    {
+                      "name": "envoy.filters.network.tcp_proxy",
+                      "typed_config": {
+                        "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+                        "stat_prefix": "gateway",
+                        "cluster": "gateway",
+                        "max_connect_attempts": 5,
+                        "idle_timeout": "3600s"
+                      }
+                    }
+                  ]
+                }
+              ],
+              "traffic_direction": "OUTBOUND"
+            },
+            "last_updated": "2022-02-03T17:48:09.550Z"
+          }
+        }
+      ]
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.SecretsConfigDump",
+      "static_secrets": [
+        {
+          "name": "cp_validation_ctx",
+          "secret": {
+            "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
+            "name": "cp_validation_ctx",
+            "validation_context": {
+              "trusted_ca": {
+                "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+              },
+              "match_subject_alt_names": [
+                {
+                  "exact": "localhost"
+                }
+              ]
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/pkg/envoy/admin/testdata/no_hds.json b/pkg/envoy/admin/testdata/no_hds.json
new file mode 100644
index 0000000..a818bb9
--- /dev/null
+++ b/pkg/envoy/admin/testdata/no_hds.json
@@ -0,0 +1,1411 @@
+{
+  "configs": [
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump",
+      "bootstrap": {
+        "node": {
+          "id": "default.backend-1",
+          "cluster": "backend",
+          "metadata": {
+            "dataplane.proxyType": "dataplane",
+            "version": {
+              "envoy": {
+                "version": "1.19.0",
+                "build": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL"
+              },
+              "dependencies": {},
+              "kumaDp": {
+                "version": "dev-9fd12f2f3",
+                "buildDate": "2022-02-03T17:18:11Z",
+                "gitTag": "1.4.0-rc1-265-g9fd12f2f3",
+                "gitCommit": "9fd12f2f3adbb13dc8a724e4bd76ea149faf6c0c"
+              }
+            },
+            "dataplane.dns.port": "15054",
+            "dynamicMetadata": {
+              "version.dependencies.coredns": "1.8.3"
+            },
+            "dataplane.admin.port": "6606",
+            "dataplane.resource": "{\"type\":\"Dataplane\",\"mesh\":\"default\",\"name\":\"backend-1\",\"creationTime\":\"0001-01-01T00:00:00Z\",\"modificationTime\":\"0001-01-01T00:00:00Z\",\"networking\":{\"address\":\"127.0.0.1\",\"inbound\":[{\"port\":10010,\"servicePort\":10011,\"tags\":{\"kuma.io/protocol\":\"tcp\",\"kuma.io/region\":\"reg1\",\"kuma.io/service\":\"backend\",\"kuma.io/sub-zone\":\"subzone1\",\"version\":\"1\"}}],\"outbound\":[{\"port\":10006,\"tags\":{\"kuma.io/service\":\"gateway\"}}],\"admin\":{\"port\":6606}}}"
+          },
+          "hidden_envoy_deprecated_build_version": "68fe53a889416fd8570506232052b06f5a531541/1.19.0/Modified/RELEASE/BoringSSL",
+          "user_agent_name": "envoy",
+          "user_agent_build_version": {
+            "version": {
+              "major_number": 1,
+              "minor_number": 19
+            },
+            "metadata": {
+              "build.type": "RELEASE",
+              "revision.sha": "68fe53a889416fd8570506232052b06f5a531541",
+              "revision.status": "Modified",
+              "ssl.version": "BoringSSL"
+            }
+          },
+          "extensions": [
+            {
+              "name": "envoy.filters.dubbo.router",
+              "category": "envoy.dubbo_proxy.filters"
+            },
+            {
+              "name": "envoy.filters.thrift.rate_limit",
+              "category": "envoy.thrift_proxy.filters"
+            },
+            {
+              "name": "envoy.filters.thrift.router",
+              "category": "envoy.thrift_proxy.filters"
+            },
+            {
+              "name": "composite-action",
+              "category": "envoy.matching.action"
+            },
+            {
+              "name": "skip",
+              "category": "envoy.matching.action"
+            },
+            {
+              "name": "envoy.rate_limit_descriptors.expr",
+              "category": "envoy.rate_limit_descriptors"
+            },
+            {
+              "name": "envoy.matching.common_inputs.environment_variable",
+              "category": "envoy.matching.common_inputs"
+            },
+            {
+              "name": "envoy.compression.brotli.decompressor",
+              "category": "envoy.compression.decompressor"
+            },
+            {
+              "name": "envoy.compression.gzip.decompressor",
+              "category": "envoy.compression.decompressor"
+            },
+            {
+              "name": "envoy.access_loggers.file",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.http_grpc",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.open_telemetry",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.stderr",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.stdout",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.tcp_grpc",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.access_loggers.wasm",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.file_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.http_grpc_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.open_telemetry_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.stderr_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.stdout_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.tcp_grpc_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.wasm_access_log",
+              "category": "envoy.access_loggers"
+            },
+            {
+              "name": "envoy.transport_sockets.alts",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.quic",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.raw_buffer",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.starttls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tap",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "raw_buffer",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "starttls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "tls",
+              "category": "envoy.transport_sockets.downstream"
+            },
+            {
+              "name": "envoy.dynamic.ot",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.lightstep",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.datadog",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.dynamic_ot",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.lightstep",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.opencensus",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.skywalking",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.xray",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.tracers.zipkin",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.zipkin",
+              "category": "envoy.tracers"
+            },
+            {
+              "name": "envoy.bootstrap.wasm",
+              "category": "envoy.bootstrap"
+            },
+            {
+              "name": "envoy.extensions.network.socket_interface.default_socket_interface",
+              "category": "envoy.bootstrap"
+            },
+            {
+              "name": "envoy.filters.listener.http_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.original_dst",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.original_src",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.proxy_protocol",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.filters.listener.tls_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.http_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.original_dst",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.original_src",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.proxy_protocol",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.listener.tls_inspector",
+              "category": "envoy.filters.listener"
+            },
+            {
+              "name": "envoy.client_ssl_auth",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.echo",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.ext_authz",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.client_ssl_auth",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.connection_limit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.direct_response",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.dubbo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.echo",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.ext_authz",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.http_connection_manager",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.kafka_broker",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.local_ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.mongo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.mysql_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.postgres_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.rbac",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.redis_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.rocketmq_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.sni_cluster",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.sni_dynamic_forward_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.tcp_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.thrift_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.wasm",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.filters.network.zookeeper_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.http_connection_manager",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.mongo_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.ratelimit",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.redis_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.tcp_proxy",
+              "category": "envoy.filters.network"
+            },
+            {
+              "name": "envoy.http.original_ip_detection.custom_header",
+              "category": "envoy.http.original_ip_detection"
+            },
+            {
+              "name": "envoy.http.original_ip_detection.xff",
+              "category": "envoy.http.original_ip_detection"
+            },
+            {
+              "name": "request-headers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "request-trailers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "response-headers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "response-trailers",
+              "category": "envoy.matching.http.input"
+            },
+            {
+              "name": "envoy.filters.udp.dns_filter",
+              "category": "envoy.filters.udp_listener"
+            },
+            {
+              "name": "envoy.filters.udp_listener.udp_proxy",
+              "category": "envoy.filters.udp_listener"
+            },
+            {
+              "name": "dubbo",
+              "category": "envoy.dubbo_proxy.protocols"
+            },
+            {
+              "name": "envoy.ip",
+              "category": "envoy.resolvers"
+            },
+            {
+              "name": "envoy.compression.brotli.compressor",
+              "category": "envoy.compression.compressor"
+            },
+            {
+              "name": "envoy.compression.gzip.compressor",
+              "category": "envoy.compression.compressor"
+            },
+            {
+              "name": "envoy.matching.matchers.consistent_hashing",
+              "category": "envoy.matching.input_matchers"
+            },
+            {
+              "name": "envoy.matching.matchers.ip",
+              "category": "envoy.matching.input_matchers"
+            },
+            {
+              "name": "preserve_case",
+              "category": "envoy.http.stateful_header_formatters"
+            },
+            {
+              "name": "envoy.watchdog.abort_action",
+              "category": "envoy.guarddog_actions"
+            },
+            {
+              "name": "envoy.watchdog.profile_action",
+              "category": "envoy.guarddog_actions"
+            },
+            {
+              "name": "envoy.filters.connection_pools.tcp.generic",
+              "category": "envoy.upstreams"
+            },
+            {
+              "name": "envoy.formatter.req_without_query",
+              "category": "envoy.formatter"
+            },
+            {
+              "name": "auto",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "framed",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "header",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "unframed",
+              "category": "envoy.thrift_proxy.transports"
+            },
+            {
+              "name": "envoy.bandwidth_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.buffer",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.cors",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.csrf",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ext_authz",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ext_proc",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.fault",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.adaptive_concurrency",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.admission_control",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.alternate_protocols_cache",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.aws_lambda",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.aws_request_signing",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.bandwidth_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.buffer",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cache",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cdn_loop",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.composite",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.compressor",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.cors",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.csrf",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.decompressor",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.dynamic_forward_proxy",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.dynamo",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ext_authz",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ext_proc",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.fault",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_http1_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_http1_reverse_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_json_transcoder",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_stats",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.grpc_web",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.header_to_metadata",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.health_check",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ip_tagging",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.jwt_authn",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.local_ratelimit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.lua",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.oauth2",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.on_demand",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.original_src",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.ratelimit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.rbac",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.router",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.set_metadata",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.squash",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.tap",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.filters.http.wasm",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_http1_bridge",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_json_transcoder",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.grpc_web",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.health_check",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.http_dynamo_filter",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.ip_tagging",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.local_rate_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.lua",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.rate_limit",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.router",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.squash",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "match-wrapper",
+              "category": "envoy.filters.http"
+            },
+            {
+              "name": "envoy.extensions.http.cache.simple",
+              "category": "envoy.http.cache"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.allow_listed_routes",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.previous_routes",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.internal_redirect_predicates.safe_cross_scheme",
+              "category": "envoy.internal_redirect_predicates"
+            },
+            {
+              "name": "envoy.quic.proof_source.filter_chain",
+              "category": "envoy.quic.proof_source"
+            },
+            {
+              "name": "envoy.retry_priorities.previous_priorities",
+              "category": "envoy.retry_priorities"
+            },
+            {
+              "name": "envoy.wasm.runtime.null",
+              "category": "envoy.wasm.runtime"
+            },
+            {
+              "name": "envoy.wasm.runtime.v8",
+              "category": "envoy.wasm.runtime"
+            },
+            {
+              "name": "envoy.quic.crypto_stream.server.quiche",
+              "category": "envoy.quic.server.crypto_stream"
+            },
+            {
+              "name": "envoy.tls.cert_validator.default",
+              "category": "envoy.tls.cert_validator"
+            },
+            {
+              "name": "envoy.tls.cert_validator.spiffe",
+              "category": "envoy.tls.cert_validator"
+            },
+            {
+              "name": "envoy.dog_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.graphite_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.metrics_service",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.dog_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.graphite_statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.hystrix",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.metrics_service",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.stat_sinks.wasm",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "envoy.statsd",
+              "category": "envoy.stats_sinks"
+            },
+            {
+              "name": "dubbo.hessian2",
+              "category": "envoy.dubbo_proxy.serializers"
+            },
+            {
+              "name": "envoy.cluster.eds",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.logical_dns",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.original_dst",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.static",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.cluster.strict_dns",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.aggregate",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.dynamic_forward_proxy",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.clusters.redis",
+              "category": "envoy.clusters"
+            },
+            {
+              "name": "envoy.health_checkers.redis",
+              "category": "envoy.health_checkers"
+            },
+            {
+              "name": "envoy.transport_sockets.alts",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.quic",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.raw_buffer",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.starttls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tap",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.tls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.transport_sockets.upstream_proxy_protocol",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "raw_buffer",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "starttls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "tls",
+              "category": "envoy.transport_sockets.upstream"
+            },
+            {
+              "name": "envoy.resource_monitors.fixed_heap",
+              "category": "envoy.resource_monitors"
+            },
+            {
+              "name": "envoy.resource_monitors.injected_resource",
+              "category": "envoy.resource_monitors"
+            },
+            {
+              "name": "envoy.grpc_credentials.aws_iam",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "envoy.grpc_credentials.default",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "envoy.grpc_credentials.file_based_metadata",
+              "category": "envoy.grpc_credentials"
+            },
+            {
+              "name": "default",
+              "category": "envoy.dubbo_proxy.route_matchers"
+            },
+            {
+              "name": "envoy.request_id.uuid",
+              "category": "envoy.request_id"
+            },
+            {
+              "name": "auto",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "binary",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "binary/non-strict",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "compact",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "twitter",
+              "category": "envoy.thrift_proxy.protocols"
+            },
+            {
+              "name": "envoy.retry_host_predicates.omit_canary_hosts",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.retry_host_predicates.omit_host_metadata",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.retry_host_predicates.previous_hosts",
+              "category": "envoy.retry_host_predicates"
+            },
+            {
+              "name": "envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+              "category": "envoy.upstream_options"
+            },
+            {
+              "name": "envoy.upstreams.http.http_protocol_options",
+              "category": "envoy.upstream_options"
+            }
+          ]
+        },
+        "static_resources": {
+          "clusters": [
+            {
+              "name": "access_log_sink",
+              "type": "STATIC",
+              "connect_timeout": "1s",
+              "http2_protocol_options": {},
+              "upstream_connection_options": {
+                "tcp_keepalive": {
+                  "keepalive_probes": 3,
+                  "keepalive_time": 10,
+                  "keepalive_interval": 10
+                }
+              },
+              "load_assignment": {
+                "cluster_name": "access_log_sink",
+                "endpoints": [
+                  {
+                    "lb_endpoints": [
+                      {
+                        "endpoint": {
+                          "address": {
+                            "pipe": {
+                              "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                ]
+              }
+            },
+            {
+              "name": "ads_cluster",
+              "type": "STRICT_DNS",
+              "connect_timeout": "1s",
+              "http2_protocol_options": {},
+              "transport_socket": {
+                "name": "envoy.transport_sockets.tls",
+                "typed_config": {
+                  "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+                  "common_tls_context": {
+                    "tls_params": {
+                      "tls_minimum_protocol_version": "TLSv1_2"
+                    },
+                    "validation_context_sds_secret_config": {
+                      "name": "cp_validation_ctx"
+                    }
+                  },
+                  "sni": "localhost"
+                }
+              },
+              "upstream_connection_options": {
+                "tcp_keepalive": {
+                  "keepalive_probes": 3,
+                  "keepalive_time": 10,
+                  "keepalive_interval": 10
+                }
+              },
+              "load_assignment": {
+                "cluster_name": "ads_cluster",
+                "endpoints": [
+                  {
+                    "lb_endpoints": [
+                      {
+                        "endpoint": {
+                          "address": {
+                            "socket_address": {
+                              "address": "localhost",
+                              "port_value": 5678
+                            }
+                          }
+                        }
+                      }
+                    ]
+                  }
+                ]
+              }
+            }
+          ],
+          "secrets": [
+            {
+              "name": "cp_validation_ctx",
+              "validation_context": {
+                "trusted_ca": {
+                  "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+                },
+                "match_subject_alt_names": [
+                  {
+                    "exact": "localhost"
+                  }
+                ]
+              }
+            }
+          ]
+        },
+        "dynamic_resources": {
+          "lds_config": {
+            "ads": {},
+            "resource_api_version": "V3"
+          },
+          "cds_config": {
+            "ads": {},
+            "resource_api_version": "V3"
+          },
+          "ads_config": {
+            "api_type": "GRPC",
+            "grpc_services": [
+              {
+                "envoy_grpc": {
+                  "cluster_name": "ads_cluster"
+                },
+                "initial_metadata": [
+                  {
+                    "key": "authorization",
+                    "value": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEiLCJ0eXAiOiJKV1QifQ.eyJOYW1lIjoiYmFja2VuZC0xIiwiTWVzaCI6ImRlZmF1bHQiLCJUYWdzIjp7fSwiVHlwZSI6IiIsImV4cCI6MTk1OTI3MDQ4OCwibmJmIjoxNjQzOTEwMTg4LCJpYXQiOjE2NDM5MTA0ODgsImp0aSI6IjgzOWQwMDc1LTJlNjEtNDQ0NC1iYjBiLWFkMTBjY2EwOTk3NyJ9.PrKwnx-EuKkIxpppTgdKzGqE3sPCyv2QDsLXW0EDkTfSzo3Rr39W1IXK3nEKSSDSX59QTzik7I_wy7MDtrlneZGN7amm8sXVkV5MwV-IkVnEuXAYMojLatOphdfyAiDZUnHP4kanpXeBz-qe23Vd2XRa788WCQle1BoHjVEfZpemcvDP_1ioCIi97_8T8C6OmMIDXR_VqA65EeKh-CjiexIxbwY_LHS2KeYwtSvq6-1egDiVBRxl1auei4L-IhrAZXtBiuQnvbw6Mh32FEb6o7V0oRky5MI_QHgxgxxWbGPY77rowHxWX-InAWir9X8Mtdi6A6tOU1Wq_trqWVRhwQ"
+                  }
+                ]
+              }
+            ],
+            "set_node_on_first_message_only": true,
+            "transport_api_version": "V3"
+          }
+        },
+        "admin": {
+          "access_log_path": "/dev/null",
+          "address": {
+            "socket_address": {
+              "address": "127.0.0.1",
+              "port_value": 6606
+            }
+          }
+        },
+        "stats_config": {
+          "stats_tags": [
+            {
+              "tag_name": "name",
+              "regex": "^grpc\\.((.+)\\.)"
+            },
+            {
+              "tag_name": "status",
+              "regex": "^grpc.*streams_closed(_([0-9]+))"
+            },
+            {
+              "tag_name": "kafka_name",
+              "regex": "^kafka(\\.(\\S*[0-9]))\\."
+            },
+            {
+              "tag_name": "kafka_type",
+              "regex": "^kafka\\..*\\.(.*)"
+            },
+            {
+              "tag_name": "worker",
+              "regex": "(worker_([0-9]+)\\.)"
+            },
+            {
+              "tag_name": "listener",
+              "regex": "((.+?)\\.)rbac\\."
+            }
+          ]
+        },
+        "layered_runtime": {
+          "layers": [
+            {
+              "name": "kuma",
+              "static_layer": {
+                "re2.max_program_size.error_level": "4294967295",
+                "re2.max_program_size.warn_level": 1000,
+                "envoy.restart_features.use_apple_api_for_dns_lookups": false
+              }
+            }
+          ]
+        }
+      },
+      "last_updated": "2022-02-03T17:48:08.328Z"
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.ClustersConfigDump",
+      "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+      "static_clusters": [
+        {
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+            "name": "access_log_sink",
+            "type": "STATIC",
+            "connect_timeout": "1s",
+            "http2_protocol_options": {},
+            "upstream_connection_options": {
+              "tcp_keepalive": {
+                "keepalive_probes": 3,
+                "keepalive_time": 10,
+                "keepalive_interval": 10
+              }
+            },
+            "load_assignment": {
+              "cluster_name": "access_log_sink",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "pipe": {
+                            "path": "/var/folders/wh/hc9z74vn6yn10zldy6qf3bn40000gn/T//kuma-al-backend-1-default.sock"
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:08.373Z"
+        },
+        {
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.api.v2.Cluster",
+            "name": "ads_cluster",
+            "type": "STRICT_DNS",
+            "connect_timeout": "1s",
+            "http2_protocol_options": {},
+            "transport_socket": {
+              "name": "envoy.transport_sockets.tls",
+              "typed_config": {
+                "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
+                "common_tls_context": {
+                  "tls_params": {
+                    "tls_minimum_protocol_version": "TLSv1_2"
+                  },
+                  "validation_context_sds_secret_config": {
+                    "name": "cp_validation_ctx"
+                  }
+                },
+                "sni": "localhost"
+              }
+            },
+            "upstream_connection_options": {
+              "tcp_keepalive": {
+                "keepalive_probes": 3,
+                "keepalive_time": 10,
+                "keepalive_interval": 10
+              }
+            },
+            "load_assignment": {
+              "cluster_name": "ads_cluster",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "localhost",
+                            "port_value": 5678
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:08.394Z"
+        }
+      ],
+      "dynamic_active_clusters": [
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "gateway",
+            "type": "EDS",
+            "eds_cluster_config": {
+              "eds_config": {
+                "ads": {},
+                "resource_api_version": "V3"
+              }
+            },
+            "connect_timeout": "5s",
+            "circuit_breakers": {
+              "thresholds": [
+                {
+                  "max_connections": 1024,
+                  "max_pending_requests": 1024,
+                  "max_requests": 1024,
+                  "max_retries": 3
+                }
+              ]
+            },
+            "outlier_detection": {
+              "enforcing_consecutive_5xx": 0,
+              "enforcing_success_rate": 0,
+              "enforcing_consecutive_gateway_failure": 0,
+              "enforcing_consecutive_local_origin_failure": 0,
+              "enforcing_failure_percentage": 0
+            },
+            "typed_extension_protocol_options": {
+              "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
+                "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
+                "explicit_http_config": {
+                  "http2_protocol_options": {}
+                }
+              }
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.517Z"
+        },
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "kuma:envoy:admin",
+            "type": "STATIC",
+            "connect_timeout": "10s",
+            "alt_stat_name": "kuma_envoy_admin",
+            "load_assignment": {
+              "cluster_name": "kuma:envoy:admin",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "127.0.0.1",
+                            "port_value": 6606
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.471Z"
+        },
+        {
+          "version_info": "9760ba3c-281c-4e54-b6eb-49dc480743c3",
+          "cluster": {
+            "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
+            "name": "localhost:10011",
+            "type": "STATIC",
+            "connect_timeout": "10s",
+            "alt_stat_name": "localhost_10011",
+            "load_assignment": {
+              "cluster_name": "localhost:10011",
+              "endpoints": [
+                {
+                  "lb_endpoints": [
+                    {
+                      "endpoint": {
+                        "address": {
+                          "socket_address": {
+                            "address": "127.0.0.1",
+                            "port_value": 10011
+                          }
+                        }
+                      }
+                    }
+                  ]
+                }
+              ]
+            }
+          },
+          "last_updated": "2022-02-03T17:48:09.492Z"
+        }
+      ]
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump",
+      "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+      "dynamic_listeners": [
+        {
+          "name": "inbound:127.0.0.1:10010",
+          "active_state": {
+            "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+            "listener": {
+              "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+              "name": "inbound:127.0.0.1:10010",
+              "address": {
+                "socket_address": {
+                  "address": "127.0.0.1",
+                  "port_value": 10010
+                }
+              },
+              "filter_chains": [
+                {
+                  "filters": [
+                    {
+                      "name": "envoy.filters.network.tcp_proxy",
+                      "typed_config": {
+                        "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+                        "stat_prefix": "localhost_10011",
+                        "cluster": "localhost:10011"
+                      }
+                    }
+                  ]
+                }
+              ],
+              "traffic_direction": "INBOUND"
+            },
+            "last_updated": "2022-02-03T17:48:09.548Z"
+          }
+        },
+        {
+          "name": "outbound:127.0.0.1:10006",
+          "active_state": {
+            "version_info": "dbc168e0-176a-4a52-929e-ddb29b554aed",
+            "listener": {
+              "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
+              "name": "outbound:127.0.0.1:10006",
+              "address": {
+                "socket_address": {
+                  "address": "127.0.0.1",
+                  "port_value": 10006
+                }
+              },
+              "filter_chains": [
+                {
+                  "filters": [
+                    {
+                      "name": "envoy.filters.network.tcp_proxy",
+                      "typed_config": {
+                        "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
+                        "stat_prefix": "gateway",
+                        "cluster": "gateway",
+                        "max_connect_attempts": 5,
+                        "idle_timeout": "3600s"
+                      }
+                    }
+                  ]
+                }
+              ],
+              "traffic_direction": "OUTBOUND"
+            },
+            "last_updated": "2022-02-03T17:48:09.550Z"
+          }
+        }
+      ]
+    },
+    {
+      "@type": "type.googleapis.com/envoy.admin.v3.SecretsConfigDump",
+      "static_secrets": [
+        {
+          "name": "cp_validation_ctx",
+          "secret": {
+            "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
+            "name": "cp_validation_ctx",
+            "validation_context": {
+              "trusted_ca": {
+                "inline_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVTRENDQXpDZ0F3SUJBZ0lSQUlWNWFaNE1YQmIySmZPLysyR0kyMWN3RFFZSktvWklodmNOQVFFTEJRQXcKSFRFYk1Ca0dBMVVFQXhNU2EzVnRZUzFqYjI1MGNtOXNMWEJzWVc1bE1CNFhEVEl4TURNd016RTFNVFkxT0ZvWApEVE14TURNd01URTFNVFkxT0Zvd0hURWJNQmtHQTFVRUF4TVNhM1Z0WVMxamIyNTBjbTlzTFhCc1lXNWxNSUlCCklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvVmNUcE5UY3dkWXZFdWt6ZVp6SldSUzQKT0VDRTBKY0RBdGU2YkJPOUo2VnQ3VEIrdkE5eW9rSE55Q04rM3FBQjRWemhjTGY3SjM2OFhmK1QvdDdPVFBjRApycGlLM29RcjhZWnVBczQwVUJpOGhTU3hNelk4eW1WRlhWSU0rUW9UcEF2LzFuRmVublBEOU4yL2tON1lDMExFCkRvZzROL2FQTXcvdklpbUFkWVhBL0s2Q2RydWZySmFENTJHbVFGMC9YS1cyT3hmdnUwRG1JUUZWdU43Qy83eUwKSG1oT1hzYVljZnZocFFoZi92b0hEN2EvM1pIL00vdVZveFFvUkZxV1huTkd6RmpSWi9UdmRZWjV2eTB5bWozegpUMGF0Slo3OEVGWXhSekRwZU9TK0ZvanFCcGFpNFNYSE5Lc1c1L2pkYlBSa1huV1NSS1pHQVhIUTdkM1FIUUlECkFRQUJvNElCZ1RDQ0FYMHdEZ1lEVlIwUEFRSC9CQVFEQWdLa01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUIKQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSd3pFbGhTUXdFNk1adQp2QXlUSFpSbElWaUR1VENDQVJvR0ExVWRFUVNDQVJFd2dnRU5naFpKYkdGekxVMWhZMEp2YjJzdFVISnZMbXh2ClkyRnNnZ2xzYjJOaGJHaHZjM1NIQkFvR0FqT0hCSDhBQUFHSEJNQ29BUzJIRUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFHSEVQNkFBQUFBQUFBQUFBQUFBQUFBQUFHSEVQNkFBQUFBQUFBQUZQN1J0OWYxKzY2SEVQNkFBQUFBQUFBQQpHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFHSm53Ly82TTlqdUhFUDZBQUFBQUFBQUFJaVVjdWpodjRzV0hFUDZBCkFBQUFBQUFBTGl3K2RDaWdVUW1IRVA2QUFBQUFBQUFBVFZnWENpdExsekdIRVA2QUFBQUFBQUFBZjFBZDhTd3YKckc2SEVQNkFBQUFBQUFBQWsyTVZEWlZ0d2l1SEVQNkFBQUFBQUFBQXJ0NUkvLzRBRVNLSEVQNkFBQUFBQUFBQQo5bG1rYTdxTENBc3dEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQXlIRmw2TlVqMXk0NkJvU01qWlBBK3FFNEt6Ck9naUZSZ2lFNmZSeTN1ZDNXQk1aS2piT1Y4cDhJcDAxQmw1ZnJWQ2lqQlZQelBpdzdlZVdYOVltVTkzTUtHUXYKNXRoMWx5bGtHRGZ2a2FzNW13dUdtdVkvaVlWWWdpc3JjQmppdjVaYUZwYmxldG8yVlkxOHdiNXdxdEo1dDV4WgpCTHZiSHpkZUxTTlJUVHU2SWlscEJwR3pWcjNvQ1JDQU9kaWhmbVBmUGFCa1Q3NWhSc2ZIdE5vcWdXZTRUbVM2CmsyUmdKdWtUbEJ6cHNJQXBxWXFqVFJvWXZHamhsSTZIb3NVUXpzZFJwTTROQnA4WHpwb04zWTluT1pnU1l1NHMKSHVmVEhsSW82aCszWXVucHBhdm9ybE5vcnZadlk2VzJmbnVlN0hldWc3YnIvUEJ6N2dUOUloY3ExYzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
+              },
+              "match_subject_alt_names": [
+                {
+                  "exact": "localhost"
+                }
+              ]
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/pkg/envoy/admin/tls/pki.go b/pkg/envoy/admin/tls/pki.go
new file mode 100644
index 0000000..d997df5
--- /dev/null
+++ b/pkg/envoy/admin/tls/pki.go
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tls
+
+import (
+	"context"
+	"crypto/rsa"
+	tls "crypto/tls"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
+
+	"google.golang.org/protobuf/types/known/wrapperspb"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
+)
+
+const (
+	ClientCertSAN = "dubbo-cp"
+)
+
+var GlobalSecretKey = model.ResourceKey{
+	Name: "envoy-admin-ca",
+}
+
+// GenerateCA generates CA for Envoy Admin communication (CP sending requests to Envoy Admin).
+// While we could reuse CA from enable mTLS backend on a Mesh object there are two problems
+//  1. mTLS on Mesh can be disabled and Envoy Admin communication needs security in place.
+//     Otherwise, malicious actor could execute /quitquitquit endpoint and perform DDoS
+//  2. ZoneIngress and ZoneEgress are not scoped to a Mesh.
+//
+// To solve this we need at least self-signed client certificate for the control plane.
+// But we can just as well have a CA and generate client and server certs from it.
+//
+// Rotation: users can change the CA. To do this, they can swap the secret and restart all instances of the CP.
+// Multizone: CA is generated for every zone. There is no need for it to be stable.
+func GenerateCA() (*util_tls.KeyPair, error) {
+	subject := pkix.Name{
+		Organization:       []string{"Dubbo"},
+		OrganizationalUnit: []string{"Mesh"},
+		CommonName:         "Envoy Admin CA",
+	}
+	return util_tls.GenerateCA(util_tls.DefaultKeyType, subject)
+}
+
+func LoadCA(ctx context.Context, resManager manager.ReadOnlyResourceManager) (tls.Certificate, error) {
+	globalSecret := system.NewGlobalSecretResource()
+	if err := resManager.Get(ctx, globalSecret, store.GetBy(GlobalSecretKey)); err != nil {
+		return tls.Certificate{}, err
+	}
+	bytes := globalSecret.Spec.GetData().GetValue()
+	certBlock, rest := pem.Decode(bytes)
+	keyBlock, _ := pem.Decode(rest)
+	return tls.X509KeyPair(pem.EncodeToMemory(certBlock), pem.EncodeToMemory(keyBlock))
+}
+
+func CreateCA(ctx context.Context, keyPair util_tls.KeyPair, resManager manager.ResourceManager) error {
+	bytes := append(keyPair.CertPEM, keyPair.KeyPEM...)
+	globalSecret := system.NewGlobalSecretResource()
+	globalSecret.Spec.Data = &wrapperspb.BytesValue{
+		Value: bytes,
+	}
+	return resManager.Create(ctx, globalSecret, store.CreateBy(GlobalSecretKey))
+}
+
+func GenerateClientCert(ca tls.Certificate) (util_tls.KeyPair, error) {
+	rootCert, err := x509.ParseCertificate(ca.Certificate[0])
+	if err != nil {
+		return util_tls.KeyPair{}, err
+	}
+	return util_tls.NewCert(*rootCert, ca.PrivateKey.(*rsa.PrivateKey), util_tls.ClientCertType, util_tls.DefaultKeyType, ClientCertSAN)
+}
+
+func GenerateServerCert(ca tls.Certificate, hosts ...string) (util_tls.KeyPair, error) {
+	rootCert, err := x509.ParseCertificate(ca.Certificate[0])
+	if err != nil {
+		return util_tls.KeyPair{}, err
+	}
+	return util_tls.NewCert(*rootCert, ca.PrivateKey.(*rsa.PrivateKey), util_tls.ServerCertType, util_tls.DefaultKeyType, hosts...)
+}
diff --git a/pkg/multitenant/multitenant.go b/pkg/multitenant/multitenant.go
new file mode 100644
index 0000000..e7cdd2e
--- /dev/null
+++ b/pkg/multitenant/multitenant.go
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 multitenant
+
+import (
+	"context"
+	"errors"
+
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/trace"
+)
+
+const spanAttributeName = "tenantID"
+
+// GlobalTenantID is a unique ID used for storing resources that are not tenant-aware
+var GlobalTenantID = ""
+
+type tenantCtx struct{}
+
+type Tenants interface {
+	// GetID gets id of tenant from context.
+	// Design: why not rely on TenantFromCtx? Different implementations of Tenants can have different error handling.
+	// Some may return error on missing tenant, whereas Kuma never requires tenant set in context.
+	GetID(ctx context.Context) (string, error)
+	GetIDs(ctx context.Context) ([]string, error)
+
+	// SupportsSharding returns true if Tenants implementation supports sharding.
+	// It means that GetIDs will return only subset of tenants.
+	SupportsSharding() bool
+	// IDSupported returns true if given tenant is in the current shard.
+	IDSupported(ctx context.Context, id string) (bool, error)
+}
+
+var SingleTenant = &singleTenant{}
+
+type singleTenant struct{}
+
+func (s singleTenant) GetID(context.Context) (string, error) {
+	return "", nil
+}
+
+func (s singleTenant) GetIDs(context.Context) ([]string, error) {
+	return []string{""}, nil
+}
+
+func (s singleTenant) SupportsSharding() bool {
+	return false
+}
+
+func (s singleTenant) IDSupported(ctx context.Context, id string) (bool, error) {
+	return true, nil
+}
+
+func WithTenant(ctx context.Context, tenantId string) context.Context {
+	if span := trace.SpanFromContext(ctx); span.IsRecording() {
+		span.SetAttributes(attribute.String(spanAttributeName, tenantId))
+	}
+
+	return context.WithValue(ctx, tenantCtx{}, tenantId)
+}
+
+func TenantFromCtx(ctx context.Context) (string, bool) {
+	value, ok := ctx.Value(tenantCtx{}).(string)
+	return value, ok
+}
+
+var TenantMissingErr = errors.New("tenant is missing")
diff --git a/pkg/plugins/authn/api-server/certs/authenticator.go b/pkg/plugins/authn/api-server/certs/authenticator.go
new file mode 100644
index 0000000..02be5e2
--- /dev/null
+++ b/pkg/plugins/authn/api-server/certs/authenticator.go
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 certs
+
+import (
+	"github.com/emicklei/go-restful/v3"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+func ClientCertAuthenticator(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
+	if user.FromCtx(request.Request.Context()).Name == user.Anonymous.Name && // do not overwrite existing user
+		request.Request.TLS != nil &&
+		request.Request.TLS.HandshakeComplete &&
+		len(request.Request.TLS.PeerCertificates) > 0 {
+		request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), user.Admin.Authenticated()))
+	}
+	chain.ProcessFilter(request, response)
+}
diff --git a/pkg/plugins/authn/api-server/certs/plugin.go b/pkg/plugins/authn/api-server/certs/plugin.go
new file mode 100644
index 0000000..a891135
--- /dev/null
+++ b/pkg/plugins/authn/api-server/certs/plugin.go
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 certs
+
+import (
+	"github.com/apache/dubbo-kubernetes/pkg/api-server/authn"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	"github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+)
+
+const PluginName = "adminClientCerts"
+
+var log = core.Log.WithName("plugins").WithName("authn").WithName("api-server").WithName("certs")
+
+type plugin struct{}
+
+func init() {
+	plugins.Register(PluginName, &plugin{})
+}
+
+var _ plugins.AuthnAPIServerPlugin = plugin{}
+
+func (c plugin) NewAuthenticator(_ plugins.PluginContext) (authn.Authenticator, error) {
+	log.Info("WARNING: admin client certificates are deprecated. Please migrate to user token as API Server authentication mechanism.")
+	return ClientCertAuthenticator, nil
+}
diff --git a/pkg/plugins/authn/api-server/tokens/access/access.go b/pkg/plugins/authn/api-server/tokens/access/access.go
new file mode 100644
index 0000000..aac7bfc
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/access/access.go
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import "github.com/apache/dubbo-kubernetes/pkg/core/user"
+
+type GenerateUserTokenAccess interface {
+	ValidateGenerate(user user.User) error
+}
diff --git a/pkg/plugins/authn/api-server/tokens/access/static.go b/pkg/plugins/authn/api-server/tokens/access/static.go
new file mode 100644
index 0000000..dde40c5
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/access/static.go
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import (
+	config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type staticGenerateUserTokenAccess struct {
+	usernames map[string]bool
+	groups    map[string]bool
+}
+
+var _ GenerateUserTokenAccess = &staticGenerateUserTokenAccess{}
+
+func NewStaticGenerateUserTokenAccess(cfg config_access.GenerateUserTokenStaticAccessConfig) GenerateUserTokenAccess {
+	s := &staticGenerateUserTokenAccess{
+		usernames: map[string]bool{},
+		groups:    map[string]bool{},
+	}
+	for _, user := range cfg.Users {
+		s.usernames[user] = true
+	}
+	for _, group := range cfg.Groups {
+		s.groups[group] = true
+	}
+	return s
+}
+
+func (s *staticGenerateUserTokenAccess) ValidateGenerate(user user.User) error {
+	allowed := s.usernames[user.Name]
+	for _, group := range user.Groups {
+		if s.groups[group] {
+			allowed = true
+		}
+	}
+	if !allowed {
+		return &access.AccessDeniedError{Reason: "action not allowed"}
+	}
+	return nil
+}
diff --git a/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap.go b/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap.go
new file mode 100644
index 0000000..0e27844
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap.go
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/pkg/errors"
+	"github.com/sethvargo/go-retry"
+	"google.golang.org/protobuf/types/known/wrapperspb"
+
+	dubbo_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
+	config_core "github.com/apache/dubbo-kubernetes/pkg/config/core"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/core/runtime/component"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+)
+
+var AdminTokenKey = model.ResourceKey{
+	Name: "admin-user-token",
+}
+
+type adminTokenBootstrap struct {
+	issuer     issuer.UserTokenIssuer
+	resManager manager.ResourceManager
+	cpCfg      dubbo_cp.Config
+}
+
+func NewAdminTokenBootstrap(issuer issuer.UserTokenIssuer, resManager manager.ResourceManager, cpCfg dubbo_cp.Config) component.Component {
+	return &adminTokenBootstrap{
+		issuer:     issuer,
+		resManager: resManager,
+		cpCfg:      cpCfg,
+	}
+}
+
+func (a *adminTokenBootstrap) Start(stop <-chan struct{}) error {
+	ctx, cancelFn := context.WithCancel(user.Ctx(context.Background(), user.ControlPlane))
+	go func() {
+		if err := a.generateTokenIfNotExist(ctx); err != nil {
+			// just log, do not exist control plane
+			log.Error(err, "could not generate Admin User Token")
+		} else {
+			msg := "bootstrap of Admin User Token is enabled. "
+			if a.cpCfg.DeployMode == config_core.KubernetesMode {
+				msg += fmt.Sprintf("To extract credentials execute 'kubectl get secret %s -n %s --template={{.data.value}} | base64 -d'. ", AdminTokenKey.Name, a.cpCfg.Store.Kubernetes.SystemNamespace)
+			} else {
+				msg += fmt.Sprintf("To extract admin credentials execute 'curl http://localhost:%d/global-secrets/%s | jq -r .data | base64 -d'. ", a.cpCfg.ApiServer.HTTP.Port, AdminTokenKey.Name)
+			}
+			msg += "You configure dubboctl with them 'dubboctl config control-planes add --auth-type=tokens --auth-conf token=YOUR_TOKEN'." +
+				" To disable bootstrap of Admin User Token set dubbo_API_SERVER_AUTHN_TOKENS_BOOTSTRAP_ADMIN_TOKEN to false."
+			log.Info(msg)
+		}
+	}()
+	<-stop
+	cancelFn()
+	return nil
+}
+
+func (a *adminTokenBootstrap) generateTokenIfNotExist(ctx context.Context) error {
+	secret := system.NewGlobalSecretResource()
+	err := a.resManager.Get(ctx, secret, core_store.GetBy(AdminTokenKey))
+	if err == nil {
+		return nil // already exists
+	}
+	if !core_store.IsResourceNotFound(err) {
+		return errors.Wrap(err, "could not check if token exist")
+	}
+
+	log.Info("Admin User Token not found. Generating.")
+	token, err := a.generateAdminToken(ctx)
+	if err != nil {
+		return errors.Wrap(err, "could not generate admin token")
+	}
+
+	log.Info("saving generated Admin User Token", "globalSecretName", AdminTokenKey.Name)
+	secret.Spec.Data = &wrapperspb.BytesValue{
+		Value: []byte(token),
+	}
+	if err := a.resManager.Create(ctx, secret, core_store.CreateBy(AdminTokenKey)); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (a *adminTokenBootstrap) generateAdminToken(ctx context.Context) (string, error) {
+	// we need retries because signing key may not be available yet
+	var token string
+	err := retry.Do(ctx, retry.WithMaxDuration(10*time.Minute, retry.NewConstant(time.Second)), func(ctx context.Context) error {
+		t, err := a.issuer.Generate(ctx, user.Admin, 24*365*10*time.Hour)
+		if err != nil {
+			return retry.RetryableError(err)
+		}
+		token = t
+		return nil
+	})
+	return token, err
+}
+
+func (a *adminTokenBootstrap) NeedLeaderElection() bool {
+	return true
+}
+
+var _ component.Component = &adminTokenBootstrap{}
diff --git a/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap_test.go b/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap_test.go
new file mode 100644
index 0000000..5d30733
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/admin_token_bootstrap_test.go
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens_test
+
+import (
+	"context"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	kuma_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
+	store_config "github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	core_tokens "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/resources/memory"
+)
+
+var _ = Describe("Admin Token Bootstrap", func() {
+	It("should bootstrap admin token", func() {
+		// given
+		ctx := context.Background()
+		resManager := manager.NewResourceManager(memory.NewStore())
+		signingKeyManager := core_tokens.NewSigningKeyManager(resManager, issuer.UserTokenSigningKeyPrefix)
+		tokenIssuer := issuer.NewUserTokenIssuer(core_tokens.NewTokenIssuer(signingKeyManager))
+		tokenValidator := issuer.NewUserTokenValidator(
+			core_tokens.NewValidator(
+				core.Log.WithName("test"),
+				[]core_tokens.SigningKeyAccessor{
+					core_tokens.NewSigningKeyAccessor(resManager, issuer.UserTokenSigningKeyPrefix),
+				},
+				core_tokens.NewRevocations(resManager, issuer.UserTokenRevocationsGlobalSecretKey),
+				store_config.MemoryStore,
+			),
+		)
+
+		component := tokens.NewAdminTokenBootstrap(tokenIssuer, resManager, kuma_cp.DefaultConfig())
+		err := signingKeyManager.CreateDefaultSigningKey(ctx)
+		Expect(err).ToNot(HaveOccurred())
+		stopCh := make(chan struct{})
+		defer close(stopCh)
+
+		// when
+		go func() {
+			_ = component.Start(stopCh) // it never returns an error
+		}()
+
+		// then token is created
+		Eventually(func(g Gomega) {
+			globalSecret := system.NewGlobalSecretResource()
+			err = resManager.Get(context.Background(), globalSecret, core_store.GetBy(tokens.AdminTokenKey))
+			g.Expect(err).ToNot(HaveOccurred())
+			user, err := tokenValidator.Validate(ctx, string(globalSecret.Spec.Data.Value))
+			g.Expect(err).ToNot(HaveOccurred())
+			g.Expect(user.Name).To(Equal("mesh-system:admin"))
+			g.Expect(user.Groups).To(Equal([]string{"mesh-system:admin"}))
+		}).Should(Succeed())
+	})
+})
diff --git a/pkg/plugins/authn/api-server/tokens/authenticator.go b/pkg/plugins/authn/api-server/tokens/authenticator.go
new file mode 100644
index 0000000..c7beac2
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/authenticator.go
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"strings"
+
+	"github.com/emicklei/go-restful/v3"
+
+	"github.com/apache/dubbo-kubernetes/pkg/api-server/authn"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	rest_errors "github.com/apache/dubbo-kubernetes/pkg/core/rest/errors"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+)
+
+const bearerPrefix = "Bearer "
+
+var log = core.Log.WithName("plugins").WithName("authn").WithName("api-server").WithName("tokens")
+
+func UserTokenAuthenticator(validator issuer.UserTokenValidator) authn.Authenticator {
+	return func(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
+		if authn.SkipAuth(request) {
+			chain.ProcessFilter(request, response)
+			return
+		}
+		authnHeader := request.Request.Header.Get("authorization")
+		if user.FromCtx(request.Request.Context()).Name == user.Anonymous.Name && // do not overwrite existing user
+			authnHeader != "" &&
+			strings.HasPrefix(authnHeader, bearerPrefix) {
+			token := strings.TrimPrefix(authnHeader, bearerPrefix)
+			u, err := validator.Validate(request.Request.Context(), token)
+			if err != nil {
+				rest_errors.HandleError(request.Request.Context(), response, &rest_errors.Unauthenticated{}, "Invalid authentication data")
+				log.Info("authentication rejected", "reason", err.Error())
+				return
+			}
+			request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), u.Authenticated()))
+		}
+		chain.ProcessFilter(request, response)
+	}
+}
diff --git a/pkg/plugins/authn/api-server/tokens/cli/plugin.go b/pkg/plugins/authn/api-server/tokens/cli/plugin.go
new file mode 100644
index 0000000..35eb758
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/cli/plugin.go
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 cli
+
+import (
+	"net/http"
+
+	"github.com/pkg/errors"
+
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api"
+	util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+)
+
+const (
+	AuthType = "tokens"
+	TokenKey = "token"
+)
+
+type TokenAuthnPlugin struct{}
+
+var _ api.AuthnPlugin = &TokenAuthnPlugin{}
+
+func (t *TokenAuthnPlugin) Validate(authConf map[string]string) error {
+	if authConf[TokenKey] == "" {
+		return errors.New("provide token=YOUR_TOKEN")
+	}
+	return nil
+}
+
+func (t *TokenAuthnPlugin) DecorateClient(delegate util_http.Client, authConf map[string]string) (util_http.Client, error) {
+	return util_http.ClientFunc(func(req *http.Request) (*http.Response, error) {
+		req.Header.Set("authorization", "Bearer "+authConf[TokenKey])
+		return delegate.Do(req)
+	}), nil
+}
diff --git a/pkg/plugins/authn/api-server/tokens/issuer/issuer.go b/pkg/plugins/authn/api-server/tokens/issuer/issuer.go
new file mode 100644
index 0000000..41eeb3f
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/issuer/issuer.go
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 issuer
+
+import (
+	"context"
+	"time"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type UserTokenIssuer interface {
+	Generate(ctx context.Context, identity user.User, validFor time.Duration) (tokens.Token, error)
+}
+
+type jwtTokenIssuer struct {
+	issuer tokens.Issuer
+}
+
+func NewUserTokenIssuer(issuer tokens.Issuer) UserTokenIssuer {
+	return &jwtTokenIssuer{
+		issuer: issuer,
+	}
+}
+
+var _ UserTokenIssuer = &jwtTokenIssuer{}
+
+func (j *jwtTokenIssuer) Generate(ctx context.Context, identity user.User, validFor time.Duration) (tokens.Token, error) {
+	c := UserClaims{
+		User: identity,
+	}
+	return j.issuer.Generate(ctx, &c, validFor)
+}
+
+type DisabledIssuer struct{}
+
+func (d DisabledIssuer) Generate(context.Context, user.User, time.Duration) (tokens.Token, error) {
+	return "", tokens.IssuerDisabled
+}
+
+var _ UserTokenIssuer = &DisabledIssuer{}
diff --git a/pkg/plugins/authn/api-server/tokens/issuer/token.go b/pkg/plugins/authn/api-server/tokens/issuer/token.go
new file mode 100644
index 0000000..329b53a
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/issuer/token.go
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 issuer
+
+import (
+	"github.com/golang-jwt/jwt/v4"
+
+	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+const UserTokenSigningKeyPrefix = "user-token-signing-key"
+
+var UserTokenRevocationsGlobalSecretKey = core_model.ResourceKey{
+	Name: "user-token-revocations",
+	Mesh: core_model.NoMesh,
+}
+
+type UserClaims struct {
+	user.User
+	jwt.RegisteredClaims
+}
+
+var _ tokens.Claims = &UserClaims{}
+
+func (c *UserClaims) ID() string {
+	return c.RegisteredClaims.ID
+}
+
+func (c *UserClaims) SetRegisteredClaims(claims jwt.RegisteredClaims) {
+	c.RegisteredClaims = claims
+}
diff --git a/pkg/plugins/authn/api-server/tokens/issuer/validator.go b/pkg/plugins/authn/api-server/tokens/issuer/validator.go
new file mode 100644
index 0000000..5f9479a
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/issuer/validator.go
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 issuer
+
+import (
+	"context"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type UserTokenValidator interface {
+	Validate(ctx context.Context, token tokens.Token) (user.User, error)
+}
+
+func NewUserTokenValidator(validator tokens.Validator) UserTokenValidator {
+	return &jwtTokenValidator{
+		validator: validator,
+	}
+}
+
+type jwtTokenValidator struct {
+	validator tokens.Validator
+}
+
+func (j *jwtTokenValidator) Validate(ctx context.Context, rawToken tokens.Token) (user.User, error) {
+	claims := &UserClaims{}
+	if err := j.validator.ParseWithValidation(ctx, rawToken, claims); err != nil {
+		return user.User{}, err
+	}
+	return claims.User, nil
+}
diff --git a/pkg/plugins/authn/api-server/tokens/plugin.go b/pkg/plugins/authn/api-server/tokens/plugin.go
new file mode 100644
index 0000000..a849226
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/plugin.go
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	go_context "context"
+
+	"github.com/pkg/errors"
+
+	"github.com/apache/dubbo-kubernetes/pkg/api-server/authn"
+	config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	"github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+	core_tokens "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/access"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws/server"
+)
+
+const PluginName = "tokens"
+
+type plugin struct {
+	// TODO: properly run AfterBootstrap - https://github.com/apache/dubbo-kubernetes/issues/6607
+	isInitialised bool
+}
+
+var (
+	_ plugins.AuthnAPIServerPlugin = &plugin{}
+	_ plugins.BootstrapPlugin      = &plugin{}
+)
+
+// We declare AccessStrategies and not into Runtime because it's a plugin.
+var AccessStrategies = map[string]func(*plugins.MutablePluginContext) access.GenerateUserTokenAccess{
+	config_access.StaticType: func(context *plugins.MutablePluginContext) access.GenerateUserTokenAccess {
+		return access.NewStaticGenerateUserTokenAccess(context.Config().Access.Static.GenerateUserToken)
+	},
+}
+
+var NewUserTokenIssuer = func(signingKeyManager core_tokens.SigningKeyManager) issuer.UserTokenIssuer {
+	return issuer.NewUserTokenIssuer(core_tokens.NewTokenIssuer(signingKeyManager))
+}
+
+func init() {
+	plugins.Register(PluginName, &plugin{})
+}
+
+func (c *plugin) NewAuthenticator(context plugins.PluginContext) (authn.Authenticator, error) {
+	publicKeys, err := core_tokens.PublicKeyFromConfig(context.Config().ApiServer.Authn.Tokens.Validator.PublicKeys)
+	if err != nil {
+		return nil, err
+	}
+	staticSigningKeyAccessor, err := core_tokens.NewStaticSigningKeyAccessor(publicKeys)
+	if err != nil {
+		return nil, err
+	}
+	accessors := []core_tokens.SigningKeyAccessor{staticSigningKeyAccessor}
+	if context.Config().ApiServer.Authn.Tokens.Validator.UseSecrets {
+		accessors = append(accessors, core_tokens.NewSigningKeyAccessor(context.ResourceManager(), issuer.UserTokenSigningKeyPrefix))
+	}
+	validator := issuer.NewUserTokenValidator(
+		core_tokens.NewValidator(
+			core.Log.WithName("tokens"),
+			accessors,
+			core_tokens.NewRevocations(context.ResourceManager(), issuer.UserTokenRevocationsGlobalSecretKey),
+			context.Config().Store.Type,
+		),
+	)
+	c.isInitialised = true
+	return UserTokenAuthenticator(validator), nil
+}
+
+func (c *plugin) BeforeBootstrap(*plugins.MutablePluginContext, plugins.PluginConfig) error {
+	return nil
+}
+
+func (c *plugin) AfterBootstrap(context *plugins.MutablePluginContext, config plugins.PluginConfig) error {
+	if !c.isInitialised {
+		return nil
+	}
+	ctx := go_context.Background()
+	signingKeyManager := core_tokens.NewSigningKeyManager(context.ResourceManager(), issuer.UserTokenSigningKeyPrefix)
+	component := core_tokens.NewDefaultSigningKeyComponent(ctx, signingKeyManager, log, context.Extensions())
+	if err := context.ComponentManager().Add(component); err != nil {
+		return err
+	}
+	accessFn, ok := AccessStrategies[context.Config().Access.Type]
+	if !ok {
+		return errors.Errorf("no Access strategy for type %q", context.Config().Access.Type)
+	}
+	tokenIssuer := NewUserTokenIssuer(signingKeyManager)
+	if !context.Config().ApiServer.Authn.Tokens.EnableIssuer {
+		tokenIssuer = issuer.DisabledIssuer{}
+	}
+	if context.Config().ApiServer.Authn.Tokens.BootstrapAdminToken {
+		if err := context.ComponentManager().Add(NewAdminTokenBootstrap(tokenIssuer, context.ResourceManager(), context.Config())); err != nil {
+			return err
+		}
+	}
+	webService := server.NewWebService(tokenIssuer, accessFn(context))
+	context.APIManager().Add(webService)
+	return nil
+}
+
+func (c *plugin) Name() plugins.PluginName {
+	return PluginName
+}
+
+func (c *plugin) Order() int {
+	return plugins.EnvironmentPreparedOrder + 1
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/authn/api-server/tokens/tokens_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/authn/api-server/tokens/tokens_suite_test.go
index 987f638..e2d5fd5 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/authn/api-server/tokens/tokens_suite_test.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package tokens_test
 
 import (
-	"archive/zip"
-	"bytes"
+	"testing"
+
+	"github.com/apache/dubbo-kubernetes/pkg/test"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func TestTokens(t *testing.T) {
+	test.RunSpecs(t, "Tokens Suite")
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/plugins/authn/api-server/tokens/ws/client/client.go b/pkg/plugins/authn/api-server/tokens/ws/client/client.go
new file mode 100644
index 0000000..6f03af7
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/client/client.go
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 client
+
+import (
+	"time"
+
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws"
+	"github.com/apache/dubbo-kubernetes/pkg/tokens"
+	util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+)
+
+type UserTokenClient interface {
+	Generate(name string, groups []string, validFor time.Duration) (string, error)
+}
+
+var _ UserTokenClient = &httpUserTokenClient{}
+
+func NewHTTPUserTokenClient(client util_http.Client) UserTokenClient {
+	return &httpUserTokenClient{
+		client: tokens.NewTokenClient(client, "user"),
+	}
+}
+
+type httpUserTokenClient struct {
+	client tokens.TokenClient
+}
+
+func (h *httpUserTokenClient) Generate(name string, groups []string, validFor time.Duration) (string, error) {
+	tokenReq := &ws.UserTokenRequest{
+		Name:     name,
+		Groups:   groups,
+		ValidFor: validFor.String(),
+	}
+	return h.client.Generate(tokenReq)
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/request.go b/pkg/plugins/authn/api-server/tokens/ws/request.go
new file mode 100644
index 0000000..c33db61
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/request.go
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 ws
+
+type UserTokenRequest struct {
+	Name     string   `json:"name"`
+	Groups   []string `json:"groups"`
+	ValidFor string   `json:"validFor"`
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/server/webservice.go b/pkg/plugins/authn/api-server/tokens/ws/server/webservice.go
new file mode 100644
index 0000000..053bd6e
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/server/webservice.go
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 server
+
+import (
+	"net/http"
+	"time"
+
+	"github.com/emicklei/go-restful/v3"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	"github.com/apache/dubbo-kubernetes/pkg/core/rest/errors"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+	"github.com/apache/dubbo-kubernetes/pkg/core/validators"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/access"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws"
+)
+
+var log = core.Log.WithName("user-token-ws")
+
+type userTokenWebService struct {
+	issuer issuer.UserTokenIssuer
+	access access.GenerateUserTokenAccess
+}
+
+func NewWebService(issuer issuer.UserTokenIssuer, access access.GenerateUserTokenAccess) *restful.WebService {
+	webservice := userTokenWebService{
+		issuer: issuer,
+		access: access,
+	}
+	return webservice.createWs()
+}
+
+func (d *userTokenWebService) createWs() *restful.WebService {
+	webservice := new(restful.WebService).
+		Consumes(restful.MIME_JSON).
+		Produces(restful.MIME_JSON)
+	webservice.Path("/tokens/user").
+		Route(webservice.POST("").To(d.handleIdentityRequest))
+	return webservice
+}
+
+func (d *userTokenWebService) handleIdentityRequest(request *restful.Request, response *restful.Response) {
+	if err := d.access.ValidateGenerate(user.FromCtx(request.Request.Context())); err != nil {
+		errors.HandleError(request.Request.Context(), response, err, "Could not issue a token")
+		return
+	}
+
+	idReq := ws.UserTokenRequest{}
+	if err := request.ReadEntity(&idReq); err != nil {
+		log.Error(err, "Could not read a request")
+		response.WriteHeader(http.StatusBadRequest)
+		return
+	}
+
+	verr := validators.ValidationError{}
+	if idReq.Name == "" {
+		verr.AddViolation("name", "cannot be empty")
+	}
+
+	var validFor time.Duration
+	if idReq.ValidFor == "" {
+		verr.AddViolation("validFor", "cannot be empty")
+	} else {
+		dur, err := time.ParseDuration(idReq.ValidFor)
+		if err != nil {
+			verr.AddViolation("validFor", "is invalid: "+err.Error())
+		}
+		validFor = dur
+		if validFor == 0 {
+			verr.AddViolation("validFor", "cannot be empty or nil")
+		}
+	}
+
+	if verr.HasViolations() {
+		errors.HandleError(request.Request.Context(), response, verr.OrNil(), "Invalid request")
+		return
+	}
+
+	token, err := d.issuer.Generate(request.Request.Context(), user.User{
+		Name:   idReq.Name,
+		Groups: idReq.Groups,
+	}, validFor)
+	if err != nil {
+		errors.HandleError(request.Request.Context(), response, err, "Could not issue a token")
+		return
+	}
+
+	response.Header().Set("content-type", "text/plain")
+	if _, err := response.Write([]byte(token)); err != nil {
+		log.Error(err, "Could write a response")
+	}
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-0-validFor.golden.json b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-0-validFor.golden.json
new file mode 100644
index 0000000..a6f4636
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-0-validFor.golden.json
@@ -0,0 +1,19 @@
+{
+  "type": "/std-errors",
+  "status": 400,
+  "title": "Invalid request",
+  "detail": "Resource is not valid",
+  "invalid_parameters": [
+    {
+      "field": "validFor",
+      "reason": "cannot be empty or nil"
+    }
+  ],
+  "details": "Resource is not valid",
+  "causes": [
+    {
+      "field": "validFor",
+      "message": "cannot be empty or nil"
+    }
+  ]
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-missing-validFor.golden.json b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-missing-validFor.golden.json
new file mode 100644
index 0000000..5820ad0
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-missing-validFor.golden.json
@@ -0,0 +1,19 @@
+{
+ "type": "/std-errors",
+ "status": 400,
+ "title": "Invalid request",
+ "detail": "Resource is not valid",
+ "invalid_parameters": [
+  {
+   "field": "validFor",
+   "reason": "cannot be empty"
+  }
+ ],
+ "details": "Resource is not valid",
+ "causes": [
+  {
+   "field": "validFor",
+   "message": "cannot be empty"
+  }
+ ]
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-no-name.golden.json b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-no-name.golden.json
new file mode 100644
index 0000000..0896791
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-no-name.golden.json
@@ -0,0 +1,19 @@
+{
+  "type": "/std-errors",
+  "status": 400,
+  "title": "Invalid request",
+  "detail": "Resource is not valid",
+  "invalid_parameters": [
+    {
+      "field": "name",
+      "reason": "cannot be empty"
+    }
+  ],
+  "details": "Resource is not valid",
+  "causes": [
+    {
+      "field": "name",
+      "message": "cannot be empty"
+    }
+  ]
+}
diff --git a/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-token-issuer-disabled.golden.json b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-token-issuer-disabled.golden.json
new file mode 100644
index 0000000..376f8a6
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/testdata/ws-token-issuer-disabled.golden.json
@@ -0,0 +1,7 @@
+{
+  "type": "/std-errors",
+  "status": 400,
+  "title": "Could not issue a token",
+  "detail": "issuing tokens using the control plane is disabled.",
+  "details": "issuing tokens using the control plane is disabled."
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/authn/api-server/tokens/ws/ws_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/authn/api-server/tokens/ws/ws_suite_test.go
index 987f638..39e6b00 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/authn/api-server/tokens/ws/ws_suite_test.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package ws_test
 
 import (
-	"archive/zip"
-	"bytes"
+	"testing"
+
+	"github.com/apache/dubbo-kubernetes/pkg/test"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func TestWS(t *testing.T) {
+	test.RunSpecs(t, "User Tokens WS Suite")
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/plugins/authn/api-server/tokens/ws/ws_test.go b/pkg/plugins/authn/api-server/tokens/ws/ws_test.go
new file mode 100644
index 0000000..f9d0602
--- /dev/null
+++ b/pkg/plugins/authn/api-server/tokens/ws/ws_test.go
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 ws_test
+
+import (
+	"context"
+	"encoding/json"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"path"
+	"strings"
+	"time"
+
+	"github.com/emicklei/go-restful/v3"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	store_config "github.com/apache/dubbo-kubernetes/pkg/config/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	core_tokens "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/issuer"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws/client"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/authn/api-server/tokens/ws/server"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/resources/memory"
+	"github.com/apache/dubbo-kubernetes/pkg/test/matchers"
+	util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+)
+
+type noopGenerateUserTokenAccess struct{}
+
+func (n *noopGenerateUserTokenAccess) ValidateGenerate(user.User) error {
+	return nil
+}
+
+var _ = Describe("Auth Tokens WS", func() {
+	var userTokenClient client.UserTokenClient
+	var userTokenValidator issuer.UserTokenValidator
+	var httpClient util_http.Client
+
+	BeforeEach(func() {
+		resManager := manager.NewResourceManager(memory.NewStore())
+		signingKeyManager := core_tokens.NewSigningKeyManager(resManager, issuer.UserTokenSigningKeyPrefix)
+		tokenIssuer := issuer.NewUserTokenIssuer(core_tokens.NewTokenIssuer(signingKeyManager))
+		userTokenValidator = issuer.NewUserTokenValidator(
+			core_tokens.NewValidator(
+				core.Log.WithName("test"),
+				[]core_tokens.SigningKeyAccessor{
+					core_tokens.NewSigningKeyAccessor(resManager, issuer.UserTokenSigningKeyPrefix),
+				},
+				core_tokens.NewRevocations(resManager, issuer.UserTokenRevocationsGlobalSecretKey),
+				store_config.MemoryStore,
+			),
+		)
+
+		Expect(signingKeyManager.CreateDefaultSigningKey(context.Background())).To(Succeed())
+		ws := server.NewWebService(tokenIssuer, &noopGenerateUserTokenAccess{})
+
+		container := restful.NewContainer()
+		container.Add(ws)
+		srv := httptest.NewServer(container)
+
+		baseURL, err := url.Parse(srv.URL)
+		Expect(err).ToNot(HaveOccurred())
+		httpClient = util_http.ClientWithBaseURL(http.DefaultClient, baseURL, nil)
+		userTokenClient = client.NewHTTPUserTokenClient(httpClient)
+
+		// wait for the server
+		Eventually(func() error {
+			_, err := userTokenClient.Generate("john.doe@example.com", []string{"team-a"}, time.Hour)
+			return err
+		}).ShouldNot(HaveOccurred())
+	})
+
+	It("should generate token", func() {
+		// when
+		token, err := userTokenClient.Generate("john.doe@example.com", []string{"team-a"}, 1*time.Hour)
+
+		// then
+		Expect(err).ToNot(HaveOccurred())
+		u, err := userTokenValidator.Validate(context.Background(), token)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(u.Name).To(Equal("john.doe@example.com"))
+		Expect(u.Groups).To(Equal([]string{"team-a"}))
+	})
+
+	It("should throw an error when name is not passed", func() {
+		// when
+		_, err := userTokenClient.Generate("", nil, 1*time.Hour)
+
+		// then
+		bytes, err := json.MarshalIndent(err, "", "  ")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(bytes).To(matchers.MatchGoldenJSON(path.Join("testdata", "ws-no-name.golden.json")))
+	})
+
+	It("should throw an error with 0 for validFor", func() {
+		// when
+		_, err := userTokenClient.Generate("foo@example.com", nil, 0)
+
+		// then
+		bytes, err := json.MarshalIndent(err, "", "  ")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(bytes).To(matchers.MatchGoldenJSON(path.Join("testdata", "ws-0-validFor.golden.json")))
+	})
+
+	It("should throw an error if validFor is not present", func() {
+		// given invalid request (cannot be implemented using UserTokenClient)
+		req, err := http.NewRequest("POST", "/tokens/user", strings.NewReader(`{"name": "xyz"}`))
+		req.Header.Add("content-type", "application/json")
+		Expect(err).ToNot(HaveOccurred())
+
+		// when
+		resp, err := httpClient.Do(req)
+		Expect(err).ToNot(HaveOccurred())
+		defer resp.Body.Close()
+
+		// then
+		bytes, err := io.ReadAll(resp.Body)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(bytes).To(matchers.MatchGoldenJSON(path.Join("testdata", "ws-missing-validFor.golden.json")))
+	})
+
+	It("should throw an error when issuer is disabled", func() {
+		container := restful.NewContainer()
+		ws := server.NewWebService(issuer.DisabledIssuer{}, &noopGenerateUserTokenAccess{})
+		container.Add(ws)
+		srv := httptest.NewServer(container)
+
+		baseURL, err := url.Parse(srv.URL)
+		Expect(err).ToNot(HaveOccurred())
+		httpClient := util_http.ClientWithBaseURL(http.DefaultClient, baseURL, nil)
+		userTokenClient := client.NewHTTPUserTokenClient(httpClient)
+
+		Eventually(func(g Gomega) {
+			_, err := userTokenClient.Generate("john.doe@example.com", []string{"team-a"}, 1*time.Hour)
+			bytes, err := json.MarshalIndent(err, "", "  ")
+			Expect(err).ToNot(HaveOccurred())
+			Expect(bytes).To(matchers.MatchGoldenJSON(path.Join("testdata", "ws-token-issuer-disabled.golden.json")))
+		}, "10s", "100ms").Should(Succeed())
+	})
+})
diff --git a/pkg/plugins/authn/api/authn.go b/pkg/plugins/authn/api/authn.go
new file mode 100644
index 0000000..09972f5
--- /dev/null
+++ b/pkg/plugins/authn/api/authn.go
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 api
+
+import util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+
+type AuthnPlugin interface {
+	Validate(map[string]string) error
+	DecorateClient(util_http.Client, map[string]string) (util_http.Client, error)
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/ca/builtin/builtin_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/ca/builtin/builtin_suite_test.go
index 987f638..589988b 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/ca/builtin/builtin_suite_test.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package builtin_test
 
 import (
-	"archive/zip"
-	"bytes"
+	"testing"
+
+	"github.com/apache/dubbo-kubernetes/pkg/test"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func TestCaBuiltin(t *testing.T) {
+	test.RunSpecs(t, "CA Builtin Suite")
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/plugins/ca/builtin/ca.go b/pkg/plugins/ca/builtin/ca.go
new file mode 100644
index 0000000..fc25ea3
--- /dev/null
+++ b/pkg/plugins/ca/builtin/ca.go
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 builtin
+
+import (
+	"crypto"
+	"crypto/rand"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"math/big"
+	"net/url"
+	"time"
+
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
+	util_rsa "github.com/apache/dubbo-kubernetes/pkg/util/rsa"
+	"github.com/pkg/errors"
+
+	"github.com/spiffe/go-spiffe/v2/spiffeid"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+)
+
+const (
+	DefaultAllowedClockSkew     = 10 * time.Second
+	DefaultCACertValidityPeriod = 10 * 365 * 24 * time.Hour
+)
+
+type certOptsFn = func(*x509.Certificate)
+
+func withExpirationTime(expiration time.Duration) certOptsFn {
+	return func(certificate *x509.Certificate) {
+		now := core.Now()
+		certificate.NotAfter = now.Add(expiration)
+	}
+}
+
+func newRootCa(mesh string, rsaBits int, certOpts ...certOptsFn) (*core_ca.KeyPair, error) {
+	if rsaBits == 0 {
+		rsaBits = util_rsa.DefaultKeySize
+	}
+	key, err := util_rsa.GenerateKey(rsaBits)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to generate a private key")
+	}
+	cert, err := newCACert(key, mesh, certOpts...)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to generate X509 certificate")
+	}
+	return util_tls.ToKeyPair(key, cert)
+}
+
+func newCACert(signer crypto.Signer, trustDomain string, certOpts ...certOptsFn) ([]byte, error) {
+	subject := pkix.Name{
+		Organization:       []string{"Dubbo"},
+		OrganizationalUnit: []string{"Mesh"},
+		CommonName:         trustDomain,
+	}
+	now := core.Now()
+	notBefore := now.Add(-DefaultAllowedClockSkew)
+	notAfter := now.Add(DefaultCACertValidityPeriod)
+
+	template, err := caTemplate(trustDomain, subject, signer.Public(), notBefore, notAfter, big.NewInt(0))
+	if err != nil {
+		return nil, err
+	}
+
+	for _, opt := range certOpts {
+		opt(template)
+	}
+	return x509.CreateCertificate(rand.Reader, template, template, signer.Public(), signer)
+}
+
+func caTemplate(trustDomain string, subject pkix.Name, publicKey crypto.PublicKey, notBefore, notAfter time.Time, serialNumber *big.Int) (*x509.Certificate, error) {
+	domain, err := spiffeid.TrustDomainFromString(trustDomain)
+	if err != nil {
+		return nil, err
+	}
+	uri, err := spiffeid.FromSegments(domain)
+	if err != nil {
+		return nil, err
+	}
+	return &x509.Certificate{
+		SerialNumber: serialNumber,
+		Subject:      subject,
+		URIs:         []*url.URL{uri.URL()},
+		NotBefore:    notBefore,
+		NotAfter:     notAfter,
+		KeyUsage: x509.KeyUsageCertSign |
+			x509.KeyUsageCRLSign,
+		BasicConstraintsValid: true,
+		IsCA:                  true,
+		PublicKey:             publicKey,
+	}, nil
+}
diff --git a/pkg/plugins/ca/builtin/config/builtin_ca_config.pb.go b/pkg/plugins/ca/builtin/config/builtin_ca_config.pb.go
new file mode 100644
index 0000000..95bac80
--- /dev/null
+++ b/pkg/plugins/ca/builtin/config/builtin_ca_config.pb.go
@@ -0,0 +1,237 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.12.4
+// source: builtin_ca_config.proto
+
+package config
+
+import (
+	wrappers "github.com/golang/protobuf/ptypes/wrappers"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// BuiltinCertificateAuthorityConfig defines configuration for Builtin CA
+// plugin
+type BuiltinCertificateAuthorityConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Configuration of CA Certificate
+	CaCert *BuiltinCertificateAuthorityConfig_CaCert `protobuf:"bytes,1,opt,name=caCert,proto3" json:"caCert,omitempty"`
+}
+
+func (x *BuiltinCertificateAuthorityConfig) Reset() {
+	*x = BuiltinCertificateAuthorityConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_builtin_ca_config_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuiltinCertificateAuthorityConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuiltinCertificateAuthorityConfig) ProtoMessage() {}
+
+func (x *BuiltinCertificateAuthorityConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_builtin_ca_config_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuiltinCertificateAuthorityConfig.ProtoReflect.Descriptor instead.
+func (*BuiltinCertificateAuthorityConfig) Descriptor() ([]byte, []int) {
+	return file_builtin_ca_config_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *BuiltinCertificateAuthorityConfig) GetCaCert() *BuiltinCertificateAuthorityConfig_CaCert {
+	if x != nil {
+		return x.CaCert
+	}
+	return nil
+}
+
+// CaCert defines configuration for Certificate of CA.
+type BuiltinCertificateAuthorityConfig_CaCert struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// RSAbits of the certificate
+	RSAbits *wrappers.UInt32Value `protobuf:"bytes,1,opt,name=RSAbits,proto3" json:"RSAbits,omitempty"`
+	// Expiration time of the certificate
+	Expiration string `protobuf:"bytes,2,opt,name=expiration,proto3" json:"expiration,omitempty"`
+}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) Reset() {
+	*x = BuiltinCertificateAuthorityConfig_CaCert{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_builtin_ca_config_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuiltinCertificateAuthorityConfig_CaCert) ProtoMessage() {}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) ProtoReflect() protoreflect.Message {
+	mi := &file_builtin_ca_config_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuiltinCertificateAuthorityConfig_CaCert.ProtoReflect.Descriptor instead.
+func (*BuiltinCertificateAuthorityConfig_CaCert) Descriptor() ([]byte, []int) {
+	return file_builtin_ca_config_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) GetRSAbits() *wrappers.UInt32Value {
+	if x != nil {
+		return x.RSAbits
+	}
+	return nil
+}
+
+func (x *BuiltinCertificateAuthorityConfig_CaCert) GetExpiration() string {
+	if x != nil {
+		return x.Expiration
+	}
+	return ""
+}
+
+var File_builtin_ca_config_proto protoreflect.FileDescriptor
+
+var file_builtin_ca_config_proto_rawDesc = []byte{
+	0x0a, 0x17, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x64, 0x75, 0x62, 0x62, 0x6f,
+	0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x63, 0x61, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61,
+	0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd9, 0x01, 0x0a, 0x21,
+	0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+	0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x12, 0x52, 0x0a, 0x06, 0x63, 0x61, 0x43, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x3a, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
+	0x73, 0x2e, 0x63, 0x61, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x43, 0x65, 0x72, 0x74,
+	0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x52, 0x06, 0x63,
+	0x61, 0x43, 0x65, 0x72, 0x74, 0x1a, 0x60, 0x0a, 0x06, 0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x12,
+	0x36, 0x0a, 0x07, 0x52, 0x53, 0x41, 0x62, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07,
+	0x52, 0x53, 0x41, 0x62, 0x69, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x70,
+	0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75,
+	0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62,
+	0x62, 0x6f, 0x2d, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x70, 0x6b,
+	0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x63, 0x61, 0x2f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_builtin_ca_config_proto_rawDescOnce sync.Once
+	file_builtin_ca_config_proto_rawDescData = file_builtin_ca_config_proto_rawDesc
+)
+
+func file_builtin_ca_config_proto_rawDescGZIP() []byte {
+	file_builtin_ca_config_proto_rawDescOnce.Do(func() {
+		file_builtin_ca_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_builtin_ca_config_proto_rawDescData)
+	})
+	return file_builtin_ca_config_proto_rawDescData
+}
+
+var file_builtin_ca_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_builtin_ca_config_proto_goTypes = []interface{}{
+	(*BuiltinCertificateAuthorityConfig)(nil),        // 0: dubbo.plugins.ca.BuiltinCertificateAuthorityConfig
+	(*BuiltinCertificateAuthorityConfig_CaCert)(nil), // 1: dubbo.plugins.ca.BuiltinCertificateAuthorityConfig.CaCert
+	(*wrappers.UInt32Value)(nil),                     // 2: google.protobuf.UInt32Value
+}
+var file_builtin_ca_config_proto_depIdxs = []int32{
+	1, // 0: dubbo.plugins.ca.BuiltinCertificateAuthorityConfig.caCert:type_name -> dubbo.plugins.ca.BuiltinCertificateAuthorityConfig.CaCert
+	2, // 1: dubbo.plugins.ca.BuiltinCertificateAuthorityConfig.CaCert.RSAbits:type_name -> google.protobuf.UInt32Value
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_builtin_ca_config_proto_init() }
+func file_builtin_ca_config_proto_init() {
+	if File_builtin_ca_config_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_builtin_ca_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuiltinCertificateAuthorityConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_builtin_ca_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuiltinCertificateAuthorityConfig_CaCert); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_builtin_ca_config_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_builtin_ca_config_proto_goTypes,
+		DependencyIndexes: file_builtin_ca_config_proto_depIdxs,
+		MessageInfos:      file_builtin_ca_config_proto_msgTypes,
+	}.Build()
+	File_builtin_ca_config_proto = out.File
+	file_builtin_ca_config_proto_rawDesc = nil
+	file_builtin_ca_config_proto_goTypes = nil
+	file_builtin_ca_config_proto_depIdxs = nil
+}
diff --git a/pkg/plugins/ca/builtin/config/builtin_ca_config.proto b/pkg/plugins/ca/builtin/config/builtin_ca_config.proto
new file mode 100644
index 0000000..fef3715
--- /dev/null
+++ b/pkg/plugins/ca/builtin/config/builtin_ca_config.proto
@@ -0,0 +1,22 @@
+syntax = "proto3";
+
+package dubbo.plugins.ca;
+
+option go_package = "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/config";
+
+import "google/protobuf/wrappers.proto";
+
+// BuiltinCertificateAuthorityConfig defines configuration for Builtin CA
+// plugin
+message BuiltinCertificateAuthorityConfig {
+  // CaCert defines configuration for Certificate of CA.
+  message CaCert {
+    // RSAbits of the certificate
+    google.protobuf.UInt32Value RSAbits = 1;
+    // Expiration time of the certificate
+    string expiration = 2;
+  }
+
+  // Configuration of CA Certificate
+  CaCert caCert = 1;
+}
diff --git a/pkg/plugins/ca/builtin/manager.go b/pkg/plugins/ca/builtin/manager.go
new file mode 100644
index 0000000..c75ca90
--- /dev/null
+++ b/pkg/plugins/ca/builtin/manager.go
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 builtin
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/asaskevich/govalidator"
+	"github.com/pkg/errors"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	ca_issuer "github.com/apache/dubbo-kubernetes/pkg/core/ca/issuer"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_system "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	core_validators "github.com/apache/dubbo-kubernetes/pkg/core/validators"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/ca/builtin/config"
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var log = core.Log.WithName("ca").WithName("builtin")
+
+const MaxBackendNameLength = 255
+
+type builtinCaManager struct {
+	secretManager manager.ResourceManager
+}
+
+func NewBuiltinCaManager(secretManager manager.ResourceManager) core_ca.Manager {
+	return &builtinCaManager{
+		secretManager: secretManager,
+	}
+}
+
+var _ core_ca.Manager = &builtinCaManager{}
+
+// EnsureBackends ensures the CA secrets for the given mesh and backends.
+func (b *builtinCaManager) EnsureBackends(ctx context.Context, mesh core_model.Resource, backends []*mesh_proto.CertificateAuthorityBackend) error {
+	for _, backend := range backends {
+		meshName := mesh.GetMeta().GetName()
+		_, err := b.getCa(ctx, meshName, backend.Name)
+		if err == nil {
+			log.V(1).Info("CA secrets already exist. Nothing to create", "mesh", mesh, "backend", backend.Name)
+			continue
+		}
+
+		if !core_store.IsResourceNotFound(err) {
+			return err
+		}
+
+		if err := b.create(ctx, mesh, backend); err != nil {
+			return errors.Wrapf(err, "failed to create CA for mesh %q and backend %q", mesh, backend.Name)
+		}
+		log.Info("CA created", "mesh", meshName)
+	}
+	return nil
+}
+
+// validate config,backend name
+func (b *builtinCaManager) ValidateBackend(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) error {
+	verr := core_validators.ValidationError{}
+	cfg := &config.BuiltinCertificateAuthorityConfig{}
+	if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+		verr.AddViolation("", "could not convert backend config: "+err.Error())
+	}
+	backendNameWithPrefix := createSecretName(mesh, backend.Name, "cert")
+	if len(backendNameWithPrefix) > MaxBackendNameLength {
+		verr.AddViolationAt(core_validators.RootedAt("mtls").Field("backends").Field("name"), fmt.Sprintf("Backend name is too long. Max length: %d", MaxBackendNameLength))
+	}
+	if !govalidator.IsDNSName(backendNameWithPrefix) || !govalidator.IsLowerCase(backendNameWithPrefix) {
+		verr.AddViolationAt(core_validators.RootedAt("mtls").Field("backends").Field("name"), fmt.Sprintf("%q name must be valid dns name", backend.Name))
+	}
+	return verr.OrNil()
+}
+
+func (b *builtinCaManager) UsedSecrets(mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]string, error) {
+	return []string{
+		certSecretResKey(mesh, backend.Name).Name,
+		keySecretResKey(mesh, backend.Name).Name,
+	}, nil
+}
+
+func (b *builtinCaManager) create(ctx context.Context, mesh core_model.Resource, backend *mesh_proto.CertificateAuthorityBackend) error {
+	meshName := mesh.GetMeta().GetName()
+	cfg := &config.BuiltinCertificateAuthorityConfig{}
+	if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+		return errors.Wrap(err, "could not convert backend config to BuiltinCertificateAuthorityConfig")
+	}
+
+	var opts []certOptsFn
+	if cfg.GetCaCert().GetExpiration() != "" {
+		duration, err := core_mesh.ParseDuration(cfg.GetCaCert().GetExpiration())
+		if err != nil {
+			return err
+		}
+		opts = append(opts, withExpirationTime(duration))
+	}
+	keyPair, err := newRootCa(meshName, int(cfg.GetCaCert().GetRSAbits().GetValue()), opts...)
+	if err != nil {
+		return errors.Wrapf(err, "failed to generate a Root CA cert for Mesh %q", meshName)
+	}
+
+	certSecret := &core_system.SecretResource{
+		Spec: &system_proto.Secret{
+			Data: util_proto.Bytes(keyPair.CertPEM),
+		},
+	}
+	if err := b.secretManager.Create(ctx, certSecret, core_store.CreateWithOwner(mesh), core_store.CreateBy(certSecretResKey(meshName, backend.Name))); err != nil {
+		if !errors.Is(err, &core_store.ResourceConflictError{}) {
+			return err
+		}
+		log.V(1).Info("CA certificate already exists. Nothing to create", "mesh", meshName, "backend", backend.Name)
+	}
+
+	keySecret := &core_system.SecretResource{
+		Spec: &system_proto.Secret{
+			Data: util_proto.Bytes(keyPair.KeyPEM),
+		},
+	}
+	if err := b.secretManager.Create(ctx, keySecret, core_store.CreateWithOwner(mesh), core_store.CreateBy(keySecretResKey(meshName, backend.Name))); err != nil {
+		if !errors.Is(err, &core_store.ResourceConflictError{}) {
+			return err
+		}
+		log.V(1).Info("CA secret key already exists. Nothing to create", "mesh", meshName, "backend", backend.Name)
+	}
+	return nil
+}
+
+func certSecretResKey(mesh string, backendName string) core_model.ResourceKey {
+	return core_model.ResourceKey{
+		Mesh: mesh,
+		Name: createSecretName(mesh, backendName, "cert"),
+	}
+}
+
+func keySecretResKey(mesh string, backendName string) core_model.ResourceKey {
+	return core_model.ResourceKey{
+		Mesh: mesh,
+		Name: createSecretName(mesh, backendName, "key"),
+	}
+}
+
+func createSecretName(mesh string, backendName string, secretType string) string {
+	return fmt.Sprintf("%s.ca-builtin-%s-%s", mesh, secretType, backendName) // we add mesh as a prefix to have uniqueness of Secret names on K8S
+}
+
+func (b *builtinCaManager) GetRootCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]core_ca.Cert, error) {
+	ca, err := b.getCa(ctx, mesh, backend.Name)
+	if err != nil {
+		return nil, errors.Wrapf(err, "failed to load CA key pair for Mesh %q and backend %q", mesh, backend.Name)
+	}
+	return []core_ca.Cert{ca.CertPEM}, nil
+}
+
+func (b *builtinCaManager) GenerateDataplaneCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend, tags mesh_proto.MultiValueTagSet) (core_ca.KeyPair, error) {
+	ca, err := b.getCa(ctx, mesh, backend.Name)
+	if err != nil {
+		return core_ca.KeyPair{}, errors.Wrapf(err, "failed to load CA key pair for Mesh %q and backend %q", mesh, backend.Name)
+	}
+
+	var opts []ca_issuer.CertOptsFn
+	if backend.GetDpCert().GetRotation().GetExpiration() != "" {
+		duration, err := core_mesh.ParseDuration(backend.GetDpCert().GetRotation().Expiration)
+		if err != nil {
+			return core_ca.KeyPair{}, err
+		}
+		opts = append(opts, ca_issuer.WithExpirationTime(duration))
+	}
+	keyPair, err := ca_issuer.NewWorkloadCert(ca, mesh, tags, opts...)
+	if err != nil {
+		return core_ca.KeyPair{}, errors.Wrapf(err, "failed to generate a Workload Identity cert for tags %q in Mesh %q using backend %q", tags.String(), mesh, backend)
+	}
+	return *keyPair, nil
+}
+
+func (b *builtinCaManager) getCa(ctx context.Context, mesh string, backendName string) (core_ca.KeyPair, error) {
+	certSecret := core_system.NewSecretResource()
+	if err := b.secretManager.Get(ctx, certSecret, core_store.GetBy(certSecretResKey(mesh, backendName))); err != nil {
+		return core_ca.KeyPair{}, err
+	}
+
+	keySecret := core_system.NewSecretResource()
+	if err := b.secretManager.Get(ctx, keySecret, core_store.GetBy(keySecretResKey(mesh, backendName))); err != nil {
+		return core_ca.KeyPair{}, err
+	}
+
+	return core_ca.KeyPair{
+		CertPEM: certSecret.Spec.Data.Value,
+		KeyPEM:  keySecret.Spec.Data.Value,
+	}, nil
+}
diff --git a/pkg/plugins/ca/builtin/manager_test.go b/pkg/plugins/ca/builtin/manager_test.go
new file mode 100644
index 0000000..ae100e6
--- /dev/null
+++ b/pkg/plugins/ca/builtin/manager_test.go
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 builtin_test
+
+import (
+	"context"
+	"crypto/x509"
+	"encoding/pem"
+	"time"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"sigs.k8s.io/yaml"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	"github.com/apache/dubbo-kubernetes/pkg/core/secrets/cipher"
+	secret_manager "github.com/apache/dubbo-kubernetes/pkg/core/secrets/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/ca/builtin"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/ca/builtin/config"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/resources/memory"
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var _ = Describe("Builtin CA Manager", func() {
+	var secretManager manager.ResourceManager
+	var caManager core_ca.Manager
+	var mesh *core_mesh.MeshResource
+
+	now := time.Now()
+
+	BeforeEach(func() {
+		core.Now = func() time.Time {
+			return now
+		}
+		memoryStore := memory.NewStore()
+		rm := manager.NewResourceManager(memoryStore)
+		secretManager = secret_manager.NewSecretManager(store.NewSecretStore(memoryStore), cipher.None(), nil, false)
+		caManager = builtin.NewBuiltinCaManager(secretManager)
+		mesh = core_mesh.NewMeshResource()
+		// Since mesh is the owner of secrets we can't operate on secrets without having the mesh in the store
+		err := rm.Create(context.Background(), mesh, core_store.CreateByKey(model.DefaultMesh, model.NoMesh))
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	AfterEach(func() {
+		core.Now = time.Now
+	})
+
+	Context("EnsureBackends", func() {
+		It("should create a CA", func() {
+			// given
+			backends := []*mesh_proto.CertificateAuthorityBackend{{
+				Name: "builtin-1",
+				Type: "builtin",
+			}}
+
+			// when
+			err := caManager.EnsureBackends(context.Background(), mesh, backends)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			// and key+cert are stored as a secrets
+			secretRes := system.NewSecretResource()
+			err = secretManager.Get(context.Background(), secretRes, core_store.GetByKey("default.ca-builtin-cert-builtin-1", "default"))
+			Expect(err).ToNot(HaveOccurred())
+			Expect(secretRes.Spec.GetData().GetValue()).ToNot(BeEmpty())
+
+			keyRes := system.NewSecretResource()
+			err = secretManager.Get(context.Background(), keyRes, core_store.GetByKey("default.ca-builtin-key-builtin-1", "default"))
+			Expect(err).ToNot(HaveOccurred())
+			Expect(keyRes.Spec.GetData().GetValue()).ToNot(BeEmpty())
+
+			// when called Ensured after CA is already created
+			err = caManager.EnsureBackends(context.Background(), mesh, backends)
+
+			// then no error happens
+			Expect(err).ToNot(HaveOccurred())
+
+			// and CA has default parameters
+			block, _ := pem.Decode(secretRes.Spec.Data.Value)
+			cert, err := x509.ParseCertificate(block.Bytes)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(cert.NotAfter).To(Equal(core.Now().UTC().Add(10 * 365 * 24 * time.Hour).Truncate(time.Second)))
+		})
+
+		It("should create a configured CA", func() {
+			// given
+			backends := []*mesh_proto.CertificateAuthorityBackend{{
+				Name: "builtin-1",
+				Type: "builtin",
+				Conf: util_proto.MustToStruct(&config.BuiltinCertificateAuthorityConfig{
+					CaCert: &config.BuiltinCertificateAuthorityConfig_CaCert{
+						RSAbits:    util_proto.UInt32(uint32(2048)),
+						Expiration: "1m",
+					},
+				}),
+			}}
+
+			// when
+			err := caManager.EnsureBackends(context.Background(), mesh, backends)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			// and CA has configured parameters
+			secretRes := system.NewSecretResource()
+			err = secretManager.Get(context.Background(), secretRes, core_store.GetByKey("default.ca-builtin-cert-builtin-1", "default"))
+			Expect(err).ToNot(HaveOccurred())
+			block, _ := pem.Decode(secretRes.Spec.Data.Value)
+			cert, err := x509.ParseCertificate(block.Bytes)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(cert.NotAfter).To(Equal(core.Now().UTC().Add(time.Minute).Truncate(time.Second)))
+		})
+
+		It("should ensure first backend and then second", func() {
+			// given
+			backends := []*mesh_proto.CertificateAuthorityBackend{{
+				Name: "builtin-1",
+				Type: "builtin",
+			}}
+
+			// when
+			err := caManager.EnsureBackends(context.Background(), mesh, backends)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			// when second one is added AFTER the CA for the first one was created
+			backends = append(backends, &mesh_proto.CertificateAuthorityBackend{
+				Name: "builtin-2",
+				Type: "builtin",
+			})
+			err = caManager.EnsureBackends(context.Background(), mesh, backends)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			// and both CAs have their keys
+			secretRes := system.NewSecretResource()
+			err = secretManager.Get(context.Background(), secretRes, core_store.GetByKey("default.ca-builtin-cert-builtin-1", "default"))
+			Expect(err).ToNot(HaveOccurred())
+
+			secretRes = system.NewSecretResource()
+			err = secretManager.Get(context.Background(), secretRes, core_store.GetByKey("default.ca-builtin-cert-builtin-2", "default"))
+			Expect(err).ToNot(HaveOccurred())
+		})
+	})
+
+	Context("ValidateBackend", func() {
+		It("should validate backend", func() {
+			// given
+			mesh := "default"
+			backend := &mesh_proto.CertificateAuthorityBackend{
+				Name: "builtin-1",
+				Type: "builtin",
+			}
+
+			// when
+			err := caManager.ValidateBackend(context.Background(), mesh, backend)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+		})
+
+		It("should fail validation when name too long", func() {
+			// given
+			mesh := "default"
+			backend := &mesh_proto.CertificateAuthorityBackend{
+				Name: "backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long.backend-long",
+				Type: "builtin",
+			}
+
+			// when
+			verr := caManager.ValidateBackend(context.Background(), mesh, backend)
+
+			// then
+			actual, err := yaml.Marshal(verr)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(actual).To(MatchYAML(`
+              violations:
+                - field: mtls.backends.name
+                  message: 'Backend name is too long. Max length: 255'`))
+		})
+
+		It("should fail validation when backend name contains uppercase letters", func() {
+			// given
+			mesh := "default"
+			backend := &mesh_proto.CertificateAuthorityBackend{
+				Name: "builtinCA",
+				Type: "builtin",
+			}
+
+			// when
+			verr := caManager.ValidateBackend(context.Background(), mesh, backend)
+
+			// then
+			actual, err := yaml.Marshal(verr)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(actual).To(MatchYAML(`
+              violations:
+                - field: mtls.backends.name
+                  message: '"builtinCA" name must be valid dns name'`))
+		})
+	})
+
+	Context("GetRootCert", func() {
+		It("should retrieve created certs", func() {
+			// given
+			backend := &mesh_proto.CertificateAuthorityBackend{
+				Name: "builtin-1",
+				Type: "builtin",
+			}
+			err := caManager.EnsureBackends(context.Background(), mesh, []*mesh_proto.CertificateAuthorityBackend{backend})
+			Expect(err).ToNot(HaveOccurred())
+
+			// when
+			certs, err := caManager.GetRootCert(context.Background(), mesh.GetMeta().GetName(), backend)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+			Expect(certs).To(HaveLen(1))
+			Expect(certs[0]).ToNot(BeEmpty())
+		})
+
+		It("should throw an error on retrieving certs on CA that was not created", func() {
+			// given
+			mesh := "default"
+			backend := &mesh_proto.CertificateAuthorityBackend{
+				Name: "builtin-non-existent",
+				Type: "builtin",
+			}
+
+			// when
+			_, err := caManager.GetRootCert(context.Background(), mesh, backend)
+
+			// then
+			Expect(err).To(MatchError(`failed to load CA key pair for Mesh "default" and backend "builtin-non-existent": Resource not found: type="Secret" name="default.ca-builtin-cert-builtin-non-existent" mesh="default"`))
+		})
+	})
+
+	Context("GenerateDataplaneCert", func() {
+		It("should generate dataplane certs", func() {
+			// given
+			backend := &mesh_proto.CertificateAuthorityBackend{
+				Name: "builtin-1",
+				Type: "builtin",
+				DpCert: &mesh_proto.CertificateAuthorityBackend_DpCert{
+					Rotation: &mesh_proto.CertificateAuthorityBackend_DpCert_Rotation{
+						Expiration: "1s",
+					},
+				},
+			}
+			err := caManager.EnsureBackends(context.Background(), mesh, []*mesh_proto.CertificateAuthorityBackend{backend})
+			Expect(err).ToNot(HaveOccurred())
+
+			// when
+			tags := map[string]map[string]bool{
+				"dubbo.io/service": {
+					"web":     true,
+					"web-api": true,
+				},
+				"version": {
+					"v1": true,
+				},
+			}
+			pair, err := caManager.GenerateDataplaneCert(context.Background(), mesh.GetMeta().GetName(), backend, tags)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+			Expect(pair.KeyPEM).ToNot(BeEmpty())
+			Expect(pair.CertPEM).ToNot(BeEmpty())
+
+			// and should generate cert for dataplane with spiffe URI
+			block, _ := pem.Decode(pair.CertPEM)
+			cert, err := x509.ParseCertificate(block.Bytes)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(cert.URIs).To(HaveLen(5))
+			Expect(cert.URIs[0].String()).To(Equal("spiffe://default/web"))
+			Expect(cert.URIs[1].String()).To(Equal("spiffe://default/web-api"))
+			Expect(cert.URIs[2].String()).To(Equal("dubbo://dubbo.io/service/web"))
+			Expect(cert.URIs[3].String()).To(Equal("dubbo://dubbo.io/service/web-api"))
+			Expect(cert.URIs[4].String()).To(Equal("dubbo://version/v1"))
+			Expect(cert.NotAfter).To(Equal(now.UTC().Truncate(time.Second).Add(1 * time.Second))) // time in cert is in UTC and truncated to seconds
+		})
+
+		It("should throw an error on generate dataplane certs on non-existing CA", func() {
+			// given
+			mesh := "default"
+			backend := &mesh_proto.CertificateAuthorityBackend{
+				Name: "builtin-non-existent",
+				Type: "builtin",
+			}
+
+			// when
+			_, err := caManager.GenerateDataplaneCert(context.Background(), mesh, backend, mesh_proto.MultiValueTagSet{})
+
+			// then
+			Expect(err).To(MatchError(`failed to load CA key pair for Mesh "default" and backend "builtin-non-existent": Resource not found: type="Secret" name="default.ca-builtin-cert-builtin-non-existent" mesh="default"`))
+		})
+	})
+})
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/ca/builtin/plugin.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/ca/builtin/plugin.go
index 987f638..f2d8787 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/ca/builtin/plugin.go
@@ -15,25 +15,21 @@
  * limitations under the License.
  */
 
-package dubbo
+package builtin
 
 import (
-	"archive/zip"
-	"bytes"
+	"github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+var _ core_plugins.CaPlugin = &plugin{}
 
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+type plugin struct{}
+
+func init() {
+	core_plugins.Register(core_plugins.CaBuiltin, &plugin{})
 }
 
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (p plugin) NewCaManager(context core_plugins.PluginContext, config core_plugins.PluginConfig) (ca.Manager, error) {
+	return NewBuiltinCaManager(context.ResourceManager()), nil
+}
diff --git a/pkg/plugins/ca/provided/ca_cert_validator.go b/pkg/plugins/ca/provided/ca_cert_validator.go
new file mode 100644
index 0000000..ff767e0
--- /dev/null
+++ b/pkg/plugins/ca/provided/ca_cert_validator.go
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 provided
+
+import (
+	"crypto/tls"
+	"crypto/x509"
+	"fmt"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/validators"
+	util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
+)
+
+func ValidateCaCert(signingPair util_tls.KeyPair) error {
+	err := validateCaCert(signingPair)
+	return err.OrNil()
+}
+
+func validateCaCert(signingPair util_tls.KeyPair) validators.ValidationError {
+	var verr validators.ValidationError
+	tlsKeyPair, err := tls.X509KeyPair(signingPair.CertPEM, signingPair.KeyPEM)
+	if err != nil {
+		verr.AddViolation("cert", fmt.Sprintf("not a valid TLS key pair: %s", err))
+		return verr
+	}
+	for i, certificate := range tlsKeyPair.Certificate {
+		path := validators.RootedAt("cert").Index(i)
+		cert, err := x509.ParseCertificate(certificate)
+		if err != nil {
+			verr.AddViolationAt(path, fmt.Sprintf("not a valid x509 certificate: %s", err))
+			return verr
+		}
+		if !cert.IsCA {
+			verr.AddViolationAt(path, "basic constraint 'CA' must be set to 'true' (see X509-SVID: 4.1. Basic Constraints)")
+		}
+		if cert.KeyUsage&x509.KeyUsageCertSign == 0 {
+			verr.AddViolationAt(path, "key usage extension 'keyCertSign' must be set (see X509-SVID: 4.3. Key Usage)")
+		}
+		if cert.KeyUsage&x509.KeyUsageKeyAgreement != 0 {
+			verr.AddViolationAt(path, "key usage extension 'keyAgreement' must NOT be set (see X509-SVID: Appendix A. X.509 Field Reference)")
+		}
+	}
+	return verr
+}
diff --git a/pkg/plugins/ca/provided/ca_cert_validator_test.go b/pkg/plugins/ca/provided/ca_cert_validator_test.go
new file mode 100644
index 0000000..820d680
--- /dev/null
+++ b/pkg/plugins/ca/provided/ca_cert_validator_test.go
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 provided_test
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"math/big"
+	"os"
+	"path/filepath"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"github.com/pkg/errors"
+	"sigs.k8s.io/yaml"
+
+	. "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided"
+	util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
+)
+
+var _ = Describe("ValidateCaCert()", func() {
+	It("should accept proper CA certificates", func() {
+		// when
+		cert, err := os.ReadFile(filepath.Join("testdata", "ca.pem"))
+		Expect(err).ToNot(HaveOccurred())
+		key, err := os.ReadFile(filepath.Join("testdata", "ca.key"))
+		Expect(err).ToNot(HaveOccurred())
+
+		signingPair := &util_tls.KeyPair{
+			CertPEM: cert,
+			KeyPEM:  key,
+		}
+		// then
+		Expect(err).ToNot(HaveOccurred())
+
+		// when
+		err = ValidateCaCert(*signingPair)
+		// then
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	NewSelfSignedCert := func(newTemplate func() *x509.Certificate) (*util_tls.KeyPair, error) {
+		key, err := rsa.GenerateKey(rand.Reader, 2048)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to generate a private key")
+		}
+		template := newTemplate()
+		template.PublicKey = key.Public()
+		cert, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to sign X509 certificate")
+		}
+		return util_tls.ToKeyPair(key, cert)
+	}
+
+	type testCase struct {
+		input       util_tls.KeyPair
+		expectedErr string
+	}
+
+	DescribeTable("should reject invalid input",
+		func(givenFunc func() testCase) {
+			given := givenFunc()
+
+			// when
+			err := ValidateCaCert(given.input)
+			// then
+			Expect(err).To(HaveOccurred())
+
+			// when
+			actual, err := yaml.Marshal(err)
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			// and
+			Expect(actual).To(MatchYAML(given.expectedErr))
+		},
+		Entry("empty key pair", func() testCase {
+			return testCase{
+				expectedErr: `
+                violations:
+                - field: cert
+                  message: 'not a valid TLS key pair: tls: failed to find any PEM data in certificate input'
+`,
+			}
+		}),
+		Entry("bad plain text values", func() testCase {
+			return testCase{
+				expectedErr: `
+                violations:
+                - field: cert
+                  message: 'not a valid TLS key pair: tls: failed to find any PEM data in certificate input'
+`,
+				input: util_tls.KeyPair{
+					CertPEM: []byte("CERT"),
+					KeyPEM:  []byte("KEY"),
+				},
+			}
+		}),
+		Entry("cert and key don't match", func() testCase {
+			return testCase{
+				expectedErr: `
+                violations:
+                - field: cert
+                  message: 'not a valid TLS key pair: tls: private key does not match public key'
+`,
+				input: util_tls.KeyPair{
+					CertPEM: []byte(`
+-----BEGIN CERTIFICATE-----
+MIIDGzCCAgOgAwIBAgIBADANBgkqhkiG9w0BAQsFADAwMQ0wCwYDVQQKEwRLdW1h
+MQ0wCwYDVQQLEwRNZXNoMRAwDgYDVQQDEwdkZWZhdWx0MB4XDTIwMDEyOTE2MDgw
+NFoXDTMwMDEyNjE2MDgxNFowMDENMAsGA1UEChMES3VtYTENMAsGA1UECxMETWVz
+aDEQMA4GA1UEAxMHZGVmYXVsdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBANhewNnHZI0f+55vsm+iGej9NAYFtCb2FNzFHZlGcu0F07YSAyorPJuM+6V3
+BFcY2IkWHL8WOooNmJ0X/yzBd4RSrb3TacGKtaDayRjTo8JOW7Nlh+WvwR18KHjC
+QXDjlqkmfExdYIUZjOqJOhu9nO59fqz0SJFvo2WKkkP7CaTLQXt1p3+Hm1Xo5WCX
+ZfD7W57YhNBZZLljip/N8pDL7b2Vkhe+txbv/PqVrDRMGoyRBnPNAfS7SPocRbcE
+S9th2CesNu+Iwltu4gJBOQbpydBIjJvr1zrx/zxbxM+EbqbGr6gwquGvKTyXHq20
+u5CE4tWy3GKSh5LEVItPS066d5UCAwEAAaNAMD4wDgYDVR0PAQH/BAQDAgEGMA8G
+A1UdEwEB/wQFMAMBAf8wGwYDVR0RBBQwEoYQc3BpZmZlOi8vZGVmYXVsdDANBgkq
+hkiG9w0BAQsFAAOCAQEAMvMqCzbjEveuMlTch9q+/6KcFVUkwQcTcxxs0MPnw5Lw
+hY6xo7FvIHNLJDRlShoAyjI6OJZobJ7PFaFnIXWlNcN1F+gA9OZSSWYgJNJl4zee
+eS2pHgbxZ6OJqCbDGYWekF3d1vEcI3gaRfxFDVa8eJFBq+B0v2Pho8A2bY5srO0S
+LG//nB619RAlzta6BxweiCmFxPyB+oqJl3X9NfWC7988CzfZAqHA+CqO6tJS5i53
+WMciH6+W7o9wdsBrfFVx2tGZqc4bZgoZptHFieqz7YBnT0Ozg+NwBU6apAtAc5Ym
+DMoTRP2Vo+BEm4uS4GcIFZYqrOsPuuyMuBd0NDE33g==
+-----END CERTIFICATE-----
+`),
+					KeyPEM: []byte(`
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvh4DhQDuE/oXjEJzt1TmgmM6s5DtsIYIEPssxdDiDEjmJzhG
+APd0BmAfrz3/fXZ6CqqqfAvTKkTDGjSN0hGtt1thF72weNfmQX9c1gUukEA0gERS
+OsSWlzp8Azc5fjXtl5jiQkvaapNoQ8PljLc8SEsgndvdxD1tDVWN6h90bMgtuqyK
+M7Fn/1+nwhRExKRTOSlR0iS9eddWPcN3HNGrl3vjCF0oUF/79+nJJqeaXawt0+2P
+CMkxY6T6yzFaS/h0Oy3mXLOQ1H5n3cZ2MSEMKQSs9ff8gzeHpWyXpgB1XEKNcUvi
+rVKfaJ/WE5v0rieG89B/YnQTduQnrwmPVuwm4wIDAQABAoIBAQCxKKrC7+DqwKvc
+ybem6Ph8HBeBaNX1HpC5sjVAiKt8IxpFBc1F7VEy97POywkfUp3a/rorKaG2y6i6
+7KoTTOIB8KcDRoIBub4Y3qQV03JWfV3vALtXhAWIGrmhDX8Hux0RnSeJ+8EmewI3
+034+qCkGfOuB7nYy/cJ3IHhD6NfG3Q3FrBrGfsI2TGEeGmPJ2Xi8ZyfbluRb/1Bt
+NesS6pDbRpZ5/IoauLUtITY3bazpzghm2tJNdrJIP7ohaoMF0WYciPyD5xpNlykt
+V8Q2jzNmPYVXuUpi4oPekq4Mg1vd/LPS/JE558Am1LEiXrycelGNrDvJW7hTDLVx
+DLRFuMMxAoGBAMkjupL3mxAfNytXM++WxJbdWPuw/vvAeN60ifFu6RUrMs/aXocn
+4xSunNF58O2aRfSq/B9LJ+pXtmdITV+Bu0Y1XefKtNUNoqIapAbA8gAWUcFSkDRd
+999rh0vWPbx4d3k69iS6xIjVaRcxeuaBbKRWqUcrxDuAydhwTLIRMD1vAoGBAPH4
+quLGkr1MdTeZ3qPAWc9mGelp0LhHukjnLB+nMdI73OH7IlX5or11yr6La/+sTmmQ
+fI+oITLuCyey7VnWBDhrPmWFGA1BmZIVDqjkJJNwyWQO7N27rQEQoNKm5n6Q+boy
+StNKa/ljduYXCjsBndOmF1wSrAwL+u9rQ3x4k9vNAoGAGY5vm1LYofDFar1WvP90
+FRMkxj4T99rZwLpBuKp19RmbCCvfzN51jOAuzrLmuNncP50mEbfT54OjinX2Vsc+
+C0qmltf7qAJmgqBN7QnA9d/gHWcnKXAzGXEpLKqZB4Rq8b1bHwmYBSbQhoDj87vI
+GQ1lzsQx17mia9zA8fMbJQMCgYB0D+2PpuW9rM3QpJp4+wtZAsVNAzddHPKKg2/T
+ovOvvoz9S+M1T+8yZyyfZuqfkTtvQSGuGlwKPMnW+ekFHTWbBj3Ani1iNmP+AOGu
+OvgcTI4c01fkJ2AdUaeCQxHuBYXzPKpNXLYbwgzG4qhCk0zrtxAfVsl1Yc20R0Pw
+kTmCxQKBgQCzd/OOLm7vDEUqYNUNgKlf8I9xM84IwUy+pJP8RaeFDtFj7tVDpY2P
+GXHBXcIBDRPnmBxC7cGHCB3KBWJp11smw2qA0ZgmBIShNm1RDHf/1h0yOxSz2+fB
+bgeEDefxTxoTMgJ1urwl0KX6R9dbv9YWZWJXk2DQj6UwkMEyXpc+kw==
+-----END RSA PRIVATE KEY-----
+`),
+				},
+			}
+		}),
+		Entry("certificate without basic constraint `CA`", func() testCase {
+			// when
+			keyPair, err := NewSelfSignedCert(func() *x509.Certificate {
+				return &x509.Certificate{
+					SerialNumber: big.NewInt(0),
+				}
+			})
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			return testCase{
+				expectedErr: `
+                violations:
+                - field: cert[0]
+                  message: "basic constraint 'CA' must be set to 'true' (see X509-SVID: 4.1. Basic Constraints)"
+                - field: cert[0]
+                  message: "key usage extension 'keyCertSign' must be set (see X509-SVID: 4.3. Key Usage)"
+`,
+				input: *keyPair,
+			}
+		}),
+		Entry("certificate without key usage extension `keyCertSign`", func() testCase {
+			// when
+			keyPair, err := NewSelfSignedCert(func() *x509.Certificate {
+				return &x509.Certificate{
+					SerialNumber:          big.NewInt(0),
+					BasicConstraintsValid: true,
+					IsCA:                  true,
+				}
+			})
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			return testCase{
+				expectedErr: `
+                violations:
+                - field: cert[0]
+                  message: "key usage extension 'keyCertSign' must be set (see X509-SVID: 4.3. Key Usage)"
+`,
+				input: *keyPair,
+			}
+		}),
+		Entry("certificate with key usage extension `keyAgreement`", func() testCase {
+			// when
+			keyPair, err := NewSelfSignedCert(func() *x509.Certificate {
+				return &x509.Certificate{
+					SerialNumber:          big.NewInt(0),
+					BasicConstraintsValid: true,
+					IsCA:                  true,
+					KeyUsage: x509.KeyUsageCertSign |
+						x509.KeyUsageCRLSign | x509.KeyUsageKeyAgreement,
+				}
+			})
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			return testCase{
+				expectedErr: `
+                violations:
+                - field: cert[0]
+                  message: "key usage extension 'keyAgreement' must NOT be set (see X509-SVID: Appendix A. X.509 Field Reference)"
+`,
+				input: *keyPair,
+			}
+		}),
+		Entry("certificate with multiple violations", func() testCase {
+			// when
+			keyPair, err := NewSelfSignedCert(func() *x509.Certificate {
+				return &x509.Certificate{
+					SerialNumber: big.NewInt(0),
+					IsCA:         false,
+					KeyUsage: x509.KeyUsageDigitalSignature |
+						x509.KeyUsageKeyAgreement | x509.KeyUsageKeyEncipherment,
+				}
+			})
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			return testCase{
+				expectedErr: `
+                violations:
+                - field: cert[0]
+                  message: "basic constraint 'CA' must be set to 'true' (see X509-SVID: 4.1. Basic Constraints)"
+                - field: cert[0]
+                  message: "key usage extension 'keyCertSign' must be set (see X509-SVID: 4.3. Key Usage)"
+                - field: cert[0]
+                  message: "key usage extension 'keyAgreement' must NOT be set (see X509-SVID: Appendix A. X.509 Field Reference)"
+`,
+				input: *keyPair,
+			}
+		}),
+	)
+})
diff --git a/pkg/plugins/ca/provided/config/provided_ca_config.pb.go b/pkg/plugins/ca/provided/config/provided_ca_config.pb.go
new file mode 100644
index 0000000..46ac283
--- /dev/null
+++ b/pkg/plugins/ca/provided/config/provided_ca_config.pb.go
@@ -0,0 +1,173 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.12.4
+// source: pkg/plugins/ca/provided/config/provided_ca_config.proto
+
+package config
+
+import (
+	v1alpha1 "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// ProvidedCertificateAuthorityConfig defines configuration for Provided CA
+// plugin
+type ProvidedCertificateAuthorityConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Data source for the certificate of CA
+	Cert *v1alpha1.DataSource `protobuf:"bytes,1,opt,name=cert,proto3" json:"cert,omitempty"`
+	// Data source for the key of CA
+	Key *v1alpha1.DataSource `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
+}
+
+func (x *ProvidedCertificateAuthorityConfig) Reset() {
+	*x = ProvidedCertificateAuthorityConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ProvidedCertificateAuthorityConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ProvidedCertificateAuthorityConfig) ProtoMessage() {}
+
+func (x *ProvidedCertificateAuthorityConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ProvidedCertificateAuthorityConfig.ProtoReflect.Descriptor instead.
+func (*ProvidedCertificateAuthorityConfig) Descriptor() ([]byte, []int) {
+	return file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *ProvidedCertificateAuthorityConfig) GetCert() *v1alpha1.DataSource {
+	if x != nil {
+		return x.Cert
+	}
+	return nil
+}
+
+func (x *ProvidedCertificateAuthorityConfig) GetKey() *v1alpha1.DataSource {
+	if x != nil {
+		return x.Key
+	}
+	return nil
+}
+
+var File_pkg_plugins_ca_provided_config_provided_ca_config_proto protoreflect.FileDescriptor
+
+var file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDesc = []byte{
+	0x0a, 0x37, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x63, 0x61,
+	0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x64, 0x75, 0x62, 0x62, 0x6f,
+	0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x63, 0x61, 0x1a, 0x24, 0x61, 0x70, 0x69,
+	0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x22, 0x90, 0x01, 0x0a, 0x22, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x43, 0x65,
+	0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+	0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x73,
+	0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44,
+	0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x12,
+	0x33, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64,
+	0x75, 0x62, 0x62, 0x6f, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+	0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52,
+	0x03, 0x6b, 0x65, 0x79, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d,
+	0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70,
+	0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2f, 0x63, 0x61, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescOnce sync.Once
+	file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescData = file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDesc
+)
+
+func file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescGZIP() []byte {
+	file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescOnce.Do(func() {
+		file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescData)
+	})
+	return file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDescData
+}
+
+var file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_pkg_plugins_ca_provided_config_provided_ca_config_proto_goTypes = []interface{}{
+	(*ProvidedCertificateAuthorityConfig)(nil), // 0: dubbo.plugins.ca.ProvidedCertificateAuthorityConfig
+	(*v1alpha1.DataSource)(nil),                // 1: dubbo.system.v1alpha1.DataSource
+}
+var file_pkg_plugins_ca_provided_config_provided_ca_config_proto_depIdxs = []int32{
+	1, // 0: dubbo.plugins.ca.ProvidedCertificateAuthorityConfig.cert:type_name -> dubbo.system.v1alpha1.DataSource
+	1, // 1: dubbo.plugins.ca.ProvidedCertificateAuthorityConfig.key:type_name -> dubbo.system.v1alpha1.DataSource
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_pkg_plugins_ca_provided_config_provided_ca_config_proto_init() }
+func file_pkg_plugins_ca_provided_config_provided_ca_config_proto_init() {
+	if File_pkg_plugins_ca_provided_config_provided_ca_config_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ProvidedCertificateAuthorityConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_pkg_plugins_ca_provided_config_provided_ca_config_proto_goTypes,
+		DependencyIndexes: file_pkg_plugins_ca_provided_config_provided_ca_config_proto_depIdxs,
+		MessageInfos:      file_pkg_plugins_ca_provided_config_provided_ca_config_proto_msgTypes,
+	}.Build()
+	File_pkg_plugins_ca_provided_config_provided_ca_config_proto = out.File
+	file_pkg_plugins_ca_provided_config_provided_ca_config_proto_rawDesc = nil
+	file_pkg_plugins_ca_provided_config_provided_ca_config_proto_goTypes = nil
+	file_pkg_plugins_ca_provided_config_provided_ca_config_proto_depIdxs = nil
+}
diff --git a/pkg/plugins/ca/provided/config/provided_ca_config.proto b/pkg/plugins/ca/provided/config/provided_ca_config.proto
new file mode 100644
index 0000000..a2846c6
--- /dev/null
+++ b/pkg/plugins/ca/provided/config/provided_ca_config.proto
@@ -0,0 +1,16 @@
+syntax = "proto3";
+
+package dubbo.plugins.ca;
+
+option go_package = "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/config";
+
+import "api/system/v1alpha1/datasource.proto";
+
+// ProvidedCertificateAuthorityConfig defines configuration for Provided CA
+// plugin
+message ProvidedCertificateAuthorityConfig {
+  // Data source for the certificate of CA
+  dubbo.system.v1alpha1.DataSource cert = 1;
+  // Data source for the key of CA
+  dubbo.system.v1alpha1.DataSource key = 2;
+}
diff --git a/pkg/plugins/ca/provided/manager.go b/pkg/plugins/ca/provided/manager.go
new file mode 100644
index 0000000..a7b48cf
--- /dev/null
+++ b/pkg/plugins/ca/provided/manager.go
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 provided
+
+import (
+	"context"
+
+	"github.com/pkg/errors"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	ca_issuer "github.com/apache/dubbo-kubernetes/pkg/core/ca/issuer"
+	"github.com/apache/dubbo-kubernetes/pkg/core/datasource"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/core/validators"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided/config"
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+type providedCaManager struct {
+	dataSourceLoader datasource.Loader
+}
+
+var _ ca.Manager = &providedCaManager{}
+
+func NewProvidedCaManager(dataSourceLoader datasource.Loader) ca.Manager {
+	return &providedCaManager{
+		dataSourceLoader: dataSourceLoader,
+	}
+}
+
+func (p *providedCaManager) ValidateBackend(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) error {
+	verr := validators.ValidationError{}
+
+	cfg := &config.ProvidedCertificateAuthorityConfig{}
+	if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+		verr.AddViolation("", "could not convert backend config: "+err.Error())
+		return verr.OrNil()
+	}
+
+	if cfg.GetCert() == nil {
+		verr.AddViolation("cert", "has to be defined")
+	} else {
+		verr.AddError("cert", datasource.Validate(cfg.GetCert()))
+	}
+	if cfg.GetKey() == nil {
+		verr.AddViolation("key", "has to be defined")
+	} else {
+		verr.AddError("key", datasource.Validate(cfg.GetKey()))
+	}
+
+	if !verr.HasViolations() {
+		pair, err := p.getCa(ctx, mesh, backend)
+		if err != nil {
+			verr.AddViolation("cert", err.Error())
+			verr.AddViolation("key", err.Error())
+		} else {
+			verr.AddError("", validateCaCert(pair))
+		}
+	}
+	return verr.OrNil()
+}
+
+func (p *providedCaManager) getCa(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) (ca.KeyPair, error) {
+	cfg := &config.ProvidedCertificateAuthorityConfig{}
+	if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+		return ca.KeyPair{}, errors.Wrap(err, "could not convert backend config to ProvidedCertificateAuthorityConfig")
+	}
+	key, err := p.dataSourceLoader.Load(ctx, mesh, cfg.Key)
+	if err != nil {
+		return ca.KeyPair{}, err
+	}
+	cert, err := p.dataSourceLoader.Load(ctx, mesh, cfg.Cert)
+	if err != nil {
+		return ca.KeyPair{}, err
+	}
+	pair := ca.KeyPair{
+		CertPEM: cert,
+		KeyPEM:  key,
+	}
+	return pair, nil
+}
+
+func (p *providedCaManager) EnsureBackends(ctx context.Context, mesh model.Resource, backend []*mesh_proto.CertificateAuthorityBackend) error {
+	return nil // Cert and Key are created by user and pointed in the configuration which is validated first
+}
+
+func (p *providedCaManager) UsedSecrets(mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]string, error) {
+	cfg := &config.ProvidedCertificateAuthorityConfig{}
+	if err := util_proto.ToTyped(backend.Conf, cfg); err != nil {
+		return nil, errors.Wrap(err, "could not convert backend config to ProvidedCertificateAuthorityConfig")
+	}
+	var secrets []string
+	if cfg.GetCert().GetSecret() != "" {
+		secrets = append(secrets, cfg.GetCert().GetSecret())
+	}
+	if cfg.GetKey().GetSecret() != "" {
+		secrets = append(secrets, cfg.GetKey().GetSecret())
+	}
+	return secrets, nil
+}
+
+func (p *providedCaManager) GetRootCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend) ([]ca.Cert, error) {
+	meshCa, err := p.getCa(ctx, mesh, backend)
+	if err != nil {
+		return nil, errors.Wrapf(err, "failed to load CA key pair for Mesh %q and backend %q", mesh, backend.Name)
+	}
+	return []ca.Cert{meshCa.CertPEM}, nil
+}
+
+func (p *providedCaManager) GenerateDataplaneCert(ctx context.Context, mesh string, backend *mesh_proto.CertificateAuthorityBackend, tags mesh_proto.MultiValueTagSet) (ca.KeyPair, error) {
+	meshCa, err := p.getCa(ctx, mesh, backend)
+	if err != nil {
+		return ca.KeyPair{}, errors.Wrapf(err, "failed to load CA key pair for Mesh %q and backend %q", mesh, backend.Name)
+	}
+
+	var opts []ca_issuer.CertOptsFn
+	if backend.GetDpCert().GetRotation().GetExpiration() != "" {
+		duration, err := core_mesh.ParseDuration(backend.GetDpCert().GetRotation().Expiration)
+		if err != nil {
+			return ca.KeyPair{}, err
+		}
+		opts = append(opts, ca_issuer.WithExpirationTime(duration))
+	}
+	keyPair, err := ca_issuer.NewWorkloadCert(meshCa, mesh, tags, opts...)
+	if err != nil {
+		return ca.KeyPair{}, errors.Wrapf(err, "failed to generate a Workload Identity cert for tags %q in Mesh %q using backend %q", tags.String(), mesh, backend.Name)
+	}
+	return *keyPair, nil
+}
diff --git a/pkg/plugins/ca/provided/manager_test.go b/pkg/plugins/ca/provided/manager_test.go
new file mode 100644
index 0000000..3458578
--- /dev/null
+++ b/pkg/plugins/ca/provided/manager_test.go
@@ -0,0 +1,300 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 provided_test
+
+import (
+	"context"
+	"crypto/x509"
+	"encoding/pem"
+	"os"
+	"path/filepath"
+	"time"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"google.golang.org/protobuf/types/known/structpb"
+	"sigs.k8s.io/yaml"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	"github.com/apache/dubbo-kubernetes/pkg/core/datasource"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided"
+	provided_config "github.com/apache/dubbo-kubernetes/pkg/plugins/ca/provided/config"
+	"github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var _ = Describe("Provided CA", func() {
+	var caManager core_ca.Manager
+
+	now := time.Now()
+
+	BeforeEach(func() {
+		core.Now = func() time.Time {
+			return now
+		}
+		caManager = provided.NewProvidedCaManager(datasource.NewDataSourceLoader(nil))
+	})
+
+	AfterEach(func() {
+		core.Now = time.Now
+	})
+
+	Context("ValidateBackend", func() {
+		type testCase struct {
+			configYAML string
+			expected   string
+		}
+
+		DescribeTable("should Validate invalid config",
+			func(given testCase) {
+				// given
+				str := structpb.Struct{}
+				err := proto.FromYAML([]byte(given.configYAML), &str)
+				Expect(err).ToNot(HaveOccurred())
+
+				// when
+				verr := caManager.ValidateBackend(context.Background(), "default", &mesh_proto.CertificateAuthorityBackend{
+					Name: "provided-1",
+					Type: "provided",
+					Conf: &str,
+				})
+
+				// then
+				actual, err := yaml.Marshal(verr)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(actual).To(MatchYAML(given.expected))
+			},
+			Entry("empty config", testCase{
+				configYAML: ``,
+				expected: `
+            violations:
+            - field: cert
+              message: has to be defined
+            - field: key
+              message: has to be defined`,
+			}),
+			Entry("config without data source", testCase{
+				configYAML: `
+            cert: {}
+            key: {}`,
+				expected: `
+            violations:
+            - field: cert
+              message: 'data source has to be chosen. Available sources: secret, file, inline'
+            - field: key
+              message: 'data source has to be chosen. Available sources: secret, file, inline'`,
+			}),
+			Entry("config with empty secret", testCase{
+				configYAML: `
+            cert:
+              secret:
+            key:
+              secret:`,
+				expected: `
+            violations:
+            - field: cert
+              message: 'data source has to be chosen. Available sources: secret, file, inline'
+            - field: key
+              message: 'data source has to be chosen. Available sources: secret, file, inline'`,
+			}),
+			Entry("config with empty secret", testCase{
+				configYAML: `
+            cert:
+              file: '/tmp/non-existing-file'
+            key:
+              file: /tmp/non-existing-file`,
+				expected: `
+            violations:
+            - field: cert
+              message: 'could not load data: open /tmp/non-existing-file: no such file or directory'
+            - field: key
+              message: 'could not load data: open /tmp/non-existing-file: no such file or directory'`,
+			}),
+			Entry("config with invalid cert", testCase{
+				configYAML: `
+            cert:
+              inline: dGVzdA==
+            key:
+              inline: dGVzdA==`,
+				expected: `
+            violations:
+            - field: cert
+              message: 'not a valid TLS key pair: tls: failed to find any PEM data in certificate input'`,
+			}),
+		)
+	})
+
+	var backendWithTestCerts *mesh_proto.CertificateAuthorityBackend
+	var backendWithInvalidCerts *mesh_proto.CertificateAuthorityBackend
+
+	BeforeEach(func() {
+		cfg := provided_config.ProvidedCertificateAuthorityConfig{
+			Cert: &system_proto.DataSource{
+				Type: &system_proto.DataSource_File{
+					File: filepath.Join("testdata", "ca.pem"),
+				},
+			},
+			Key: &system_proto.DataSource{
+				Type: &system_proto.DataSource_File{
+					File: filepath.Join("testdata", "ca.key"),
+				},
+			},
+		}
+		str, err := proto.ToStruct(&cfg)
+		Expect(err).ToNot(HaveOccurred())
+
+		backendWithTestCerts = &mesh_proto.CertificateAuthorityBackend{
+			Name: "provided-1",
+			Type: "provided",
+			Conf: str,
+			DpCert: &mesh_proto.CertificateAuthorityBackend_DpCert{
+				Rotation: &mesh_proto.CertificateAuthorityBackend_DpCert_Rotation{
+					Expiration: "1s",
+				},
+			},
+		}
+
+		invalidCfg := provided_config.ProvidedCertificateAuthorityConfig{
+			Cert: &system_proto.DataSource{
+				Type: &system_proto.DataSource_File{
+					File: filepath.Join("testdata", "invalid.pem"),
+				},
+			},
+			Key: &system_proto.DataSource{
+				Type: &system_proto.DataSource_File{
+					File: filepath.Join("testdata", "invalid.key"),
+				},
+			},
+		}
+		invalidStr, err := proto.ToStruct(&invalidCfg)
+		Expect(err).ToNot(HaveOccurred())
+
+		backendWithInvalidCerts = &mesh_proto.CertificateAuthorityBackend{
+			Name: "provided-2",
+			Type: "provided",
+			Conf: invalidStr,
+		}
+	})
+
+	Context("GetRootCert", func() {
+		It("should load return root certs", func() {
+			// given
+			expectedCert, err := os.ReadFile(filepath.Join("testdata", "ca.pem"))
+			Expect(err).ToNot(HaveOccurred())
+
+			// when
+			rootCerts, err := caManager.GetRootCert(context.Background(), "default", backendWithTestCerts)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+			Expect(rootCerts).To(HaveLen(1))
+			Expect(rootCerts[0]).To(Equal(expectedCert))
+		})
+
+		It("should throw an error on invalid certs", func() {
+			// when
+			_, err := caManager.GetRootCert(context.Background(), "default", backendWithInvalidCerts)
+
+			// then
+			Expect(err).To(MatchError(`failed to load CA key pair for Mesh "default" and backend "provided-2": could not load data: open testdata/invalid.key: no such file or directory`))
+		})
+	})
+
+	Context("GenerateDataplaneCert", func() {
+		It("should generate dataplane cert", func() {
+			// when
+			tags := map[string]map[string]bool{
+				"dubbo.io/service": {
+					"web":     true,
+					"web-api": true,
+				},
+				"version": {
+					"v1": true,
+				},
+			}
+			pair, err := caManager.GenerateDataplaneCert(context.Background(), "default", backendWithTestCerts, tags)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+			Expect(pair.KeyPEM).ToNot(BeEmpty())
+			Expect(pair.CertPEM).ToNot(BeEmpty())
+
+			// and should generate cert for dataplane with spiffe URI
+			block, _ := pem.Decode(pair.CertPEM)
+			cert, err := x509.ParseCertificate(block.Bytes)
+			Expect(err).ToNot(HaveOccurred())
+			Expect(cert.URIs).To(HaveLen(5))
+			Expect(cert.URIs[0].String()).To(Equal("spiffe://default/web"))
+			Expect(cert.URIs[1].String()).To(Equal("spiffe://default/web-api"))
+			Expect(cert.URIs[2].String()).To(Equal("dubbo://dubbo.io/service/web"))
+			Expect(cert.URIs[3].String()).To(Equal("dubbo://dubbo.io/service/web-api"))
+			Expect(cert.URIs[4].String()).To(Equal("dubbo://version/v1"))
+			Expect(cert.NotAfter).To(Equal(now.UTC().Truncate(time.Second).Add(1 * time.Second))) // time in cert is in UTC and truncated to seconds
+		})
+
+		It("should throw an error on invalid certs", func() {
+			// when
+			_, err := caManager.GenerateDataplaneCert(context.Background(), "default", backendWithInvalidCerts, mesh_proto.MultiValueTagSet{})
+
+			// then
+			Expect(err).To(MatchError(`failed to load CA key pair for Mesh "default" and backend "provided-2": could not load data: open testdata/invalid.key: no such file or directory`))
+		})
+	})
+
+	Context("UsedSecret", func() {
+		It("should return empty list when no secrets are used", func() {
+			// when
+			secrets, err := caManager.UsedSecrets("default", backendWithTestCerts)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+			Expect(secrets).To(BeEmpty())
+		})
+
+		It("should return list of secrets", func() {
+			// given
+			backend := &mesh_proto.CertificateAuthorityBackend{
+				Name: "provided-1",
+				Type: "provided",
+				Conf: proto.MustToStruct(&provided_config.ProvidedCertificateAuthorityConfig{
+					Cert: &system_proto.DataSource{
+						Type: &system_proto.DataSource_Secret{
+							Secret: "cert-sec",
+						},
+					},
+					Key: &system_proto.DataSource{
+						Type: &system_proto.DataSource_Secret{
+							Secret: "key-sec",
+						},
+					},
+				}),
+			}
+
+			// when
+			secrets, err := caManager.UsedSecrets("default", backend)
+
+			// then
+			Expect(err).ToNot(HaveOccurred())
+			Expect(secrets).To(HaveLen(2))
+			Expect(secrets[0]).To(Equal("cert-sec"))
+			Expect(secrets[1]).To(Equal("key-sec"))
+		})
+	})
+})
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/ca/provided/plugin.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/ca/provided/plugin.go
index 987f638..56f16a2 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/ca/provided/plugin.go
@@ -15,25 +15,21 @@
  * limitations under the License.
  */
 
-package dubbo
+package provided
 
 import (
-	"archive/zip"
-	"bytes"
+	"github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+var _ core_plugins.CaPlugin = &plugin{}
 
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+type plugin struct{}
+
+func init() {
+	core_plugins.Register(core_plugins.CaProvided, &plugin{})
 }
 
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (p plugin) NewCaManager(context core_plugins.PluginContext, config core_plugins.PluginConfig) (ca.Manager, error) {
+	return NewProvidedCaManager(context.DataSourceLoader()), nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/ca/provided/provided_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/ca/provided/provided_suite_test.go
index 987f638..30b6f69 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/ca/provided/provided_suite_test.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package provided_test
 
 import (
-	"archive/zip"
-	"bytes"
+	"testing"
+
+	"github.com/apache/dubbo-kubernetes/pkg/test"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func TestCaProvided(t *testing.T) {
+	test.RunSpecs(t, "CA Provided Suite")
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/plugins/ca/provided/testdata/ca.key b/pkg/plugins/ca/provided/testdata/ca.key
new file mode 100644
index 0000000..46e8605
--- /dev/null
+++ b/pkg/plugins/ca/provided/testdata/ca.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0Q6078jpbZfQ5ZzSyY65zEh2GMWmcMLcj2aeHYuClDwONbGY
+yqlIkoVwnF+8zxlShUTQqi6DuKV8VlWZPjD5gzOqIHgGfnQkEsUT4d53qcsRFqDQ
+PVndfyBp5aEJD7yrD1thxdbc8zPVHw5mcRy86BW5OcQy7k7qK/Ih8sYajaa11cjA
+jJSQVlJ+HxUa3utAKZ2YTh8nTjzvvyndNdPBzLY9n3TGQcPerbRgqXFrsAkWfzGA
+yeca7OK25wO9ig/C4Qqcjd3+JrihDpKj2hTntGznSPzQnSMkyw3YxMjYJRPT6jlS
+bvr/LxT2mYuLdxq2J1cIjd1s4pVqFsPhQOMbUQIDAQABAoIBABWf2tFuy859RKtr
+lFYrAEcEO8sGLtg9H8lpiPNvadthdurQ6wgTiKE3UlWqhYI6kVGds6PW2eMwovLf
+OdG4CScsIe3n3GwaIV0bq5nShDg4/BSGH+QotoOe27VX+fL5xgv5nNx1BDfX5bSn
+rasHmPa8wIMcK1SBLzHKgv50xWxfYVUnJEU1UZOBvjyPJxq3EPnm+nPMR2za/6JD
+/9OpLyDAhPfw/btZZy4fhWUvO47S/05ke30hsrc6BvTtw45edKvlZoKrm7iStyKA
+Lq+IvlDQEbr4V0GE5LsABkiii4cUo90yhKXOVM2DDpzeKjcXDLlRfIlGEon8sduI
+Oi/Bcn0CgYEA5UDDyNfgwNRYTcsiSmnhpxLek5OyFkWmbmUuQ2pO0baNxQRsl+vI
+RNC5bqbAjMcuaD25qh6dxciXpL4kDyekQ0I7d4dpQbc2Zh0bDzDfFv94uzbgU6Vc
+UO16GCfD4hsmbUtFHHXCXKFBldwqcgfuD3f95HHrwLwYSNq/AooZq+sCgYEA6XK/
+9RZC8W0U+RF4Pv8YVmY9jRnwxx/8QopfN8X3tasmLZbq0C2bvJ+pzzQZj7eNOPJv
+zKN2WkfNAkNH91bAIBpLLtdSi5ABu0GNSaxKZvKR0KS1C35JBwBnjuQ12+H3j7bE
+tcDql5vECocyYe5QkkdWziFb6KVArRCWa/0UMrMCgYEA2xrnBBGvMRzBuLgocc54
+aGHUcF98mWTACEqTyMLVygEbxezQjPJAWeTTFToyVVoYtHAp/rl8OQfaRw2jEQjm
+KZInm7nDk5czWqz+q6odzbElBmkQRWGJtZ8CTgvTX0prk5GY3/Z8aGOGd2ARASMr
+F4WrFqb6lx4uZ5MJc7fhiTkCgYB1+tE6atrAKrG7wnFz3PYOzDL1OZPu/qI9erLu
+e/VZcbqkta3MMhCP/l0SY7E7abdMfXG1CTOKItlf+GLJhKUqVD+E17tW3xuQ6Gsg
+PZuIdbTtrh/1RAhiKnkrxPfXPVcg4Wx+spWcs1MbQwJabdd4Zedf24oYhFOwYz6y
+Kr/ncwKBgQDVj70UeX0ueiMFi9Qglkl2PaDcpvAWiJfFwPzDQi4Iwi8FPDAy7m6D
+z97kxka4dORTEFNreni4hGXmYWUdrZWD73fuXN3+GGzEAf34yQyRKHO663FZrjm7
+226TBFum3JknVNlDenwYqW+rRJobA1pZJxjFom7nKH0lBzewpgfrmw==
+-----END RSA PRIVATE KEY-----
diff --git a/pkg/plugins/ca/provided/testdata/ca.pem b/pkg/plugins/ca/provided/testdata/ca.pem
new file mode 100644
index 0000000..f3e76ef
--- /dev/null
+++ b/pkg/plugins/ca/provided/testdata/ca.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDGzCCAgOgAwIBAgIBADANBgkqhkiG9w0BAQsFADAwMQ0wCwYDVQQKEwRLdW1h
+MQ0wCwYDVQQLEwRNZXNoMRAwDgYDVQQDEwdkZWZhdWx0MB4XDTIwMDQyMzA4NDkw
+MloXDTMwMDQyMTA4NDkxMlowMDENMAsGA1UEChMES3VtYTENMAsGA1UECxMETWVz
+aDEQMA4GA1UEAxMHZGVmYXVsdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBANEOtO/I6W2X0OWc0smOucxIdhjFpnDC3I9mnh2LgpQ8DjWxmMqpSJKFcJxf
+vM8ZUoVE0Koug7ilfFZVmT4w+YMzqiB4Bn50JBLFE+Hed6nLERag0D1Z3X8gaeWh
+CQ+8qw9bYcXW3PMz1R8OZnEcvOgVuTnEMu5O6ivyIfLGGo2mtdXIwIyUkFZSfh8V
+Gt7rQCmdmE4fJ048778p3TXTwcy2PZ90xkHD3q20YKlxa7AJFn8xgMnnGuzitucD
+vYoPwuEKnI3d/ia4oQ6So9oU57Rs50j80J0jJMsN2MTI2CUT0+o5Um76/y8U9pmL
+i3catidXCI3dbOKVahbD4UDjG1ECAwEAAaNAMD4wDgYDVR0PAQH/BAQDAgEGMA8G
+A1UdEwEB/wQFMAMBAf8wGwYDVR0RBBQwEoYQc3BpZmZlOi8vZGVmYXVsdDANBgkq
+hkiG9w0BAQsFAAOCAQEAaWBjvcumO4qnmhdLLeL3OnSQyoeS6lgG9VL/Dm4/3Dlw
+DkxpAQj27rKLCI7f+bACSG8abxvIEySVs6jlvlDnIpRQ07IXRkPm6osjFPsvk6EA
+PG0cJ48UoiICYEVnFssp+AyNBtiyRwK9S6hi/ipa3NBQjjzD1k/xIy+qKDvmOBh+
+WVfQOVdyZHR10Xf/cK5UtozOdq9fqpDfp2b4lw+1lI/CQh128qIPsBhFUhnjNj3+
+Tb2UrWtc+HEPjIxfr3J90ziSIbrhPQ/rJlfGyJuJk4PYME8KbBaXQhG4tYDeG8Hr
+mdFdBVEUtPHLq/dpu0+RYP6zddZEf/PfhTmcC40sSg==
+-----END CERTIFICATE-----
diff --git a/pkg/plugins/runtime/k8s/controllers/pod_status_controller.go b/pkg/plugins/runtime/k8s/controllers/pod_status_controller.go
new file mode 100644
index 0000000..cc8c95f
--- /dev/null
+++ b/pkg/plugins/runtime/k8s/controllers/pod_status_controller.go
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 controllers
+
+import (
+	"context"
+
+	"github.com/go-logr/logr"
+	"github.com/pkg/errors"
+	kube_core "k8s.io/api/core/v1"
+	kube_apierrs "k8s.io/apimachinery/pkg/api/errors"
+	kube_runtime "k8s.io/apimachinery/pkg/runtime"
+	kube_record "k8s.io/client-go/tools/record"
+	kube_ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/builder"
+	kube_client "sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/manager"
+	"github.com/apache/dubbo-kubernetes/pkg/envoy/admin"
+	k8s_common "github.com/apache/dubbo-kubernetes/pkg/plugins/common/k8s"
+	mesh_k8s "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s/native/api/v1alpha1"
+	util_k8s "github.com/apache/dubbo-kubernetes/pkg/plugins/runtime/k8s/util"
+)
+
+// PodStatusReconciler tracks pods status changes and signals dubbo-dp when it has to complete
+// but only when Dubbo isn't using the SidecarContainer feature
+type PodStatusReconciler struct {
+	kube_client.Client
+	kube_record.EventRecorder
+	Scheme            *kube_runtime.Scheme
+	ResourceManager   manager.ResourceManager
+	Log               logr.Logger
+	ResourceConverter k8s_common.Converter
+	EnvoyAdminClient  admin.EnvoyAdminClient
+}
+
+func (r *PodStatusReconciler) Reconcile(ctx context.Context, req kube_ctrl.Request) (kube_ctrl.Result, error) {
+	log := r.Log.WithValues("pod", req.NamespacedName)
+
+	// Fetch the Pod instance
+	pod := &kube_core.Pod{}
+	if err := r.Get(ctx, req.NamespacedName, pod); err != nil {
+		if kube_apierrs.IsNotFound(err) {
+			return kube_ctrl.Result{}, nil
+		}
+		log.Error(err, "unable to fetch Pod")
+		return kube_ctrl.Result{}, err
+	}
+
+	dataplane := &mesh_k8s.Dataplane{}
+	if err := r.Get(ctx, req.NamespacedName, dataplane); err != nil {
+		if kube_apierrs.IsNotFound(err) {
+			log.V(1).Info("dataplane not found")
+			return kube_ctrl.Result{}, nil
+		}
+		log.Error(err, "unable to fetch Dataplane")
+		return kube_ctrl.Result{}, err
+	}
+
+	dp := core_mesh.NewDataplaneResource()
+	if err := r.ResourceConverter.ToCoreResource(dataplane, dp); err != nil {
+		converterLog.Error(err, "failed to parse Dataplane", "dataplane", dataplane.Spec)
+		return kube_ctrl.Result{}, err
+	}
+
+	log.Info("sending request to terminate Envoy")
+	if err := r.EnvoyAdminClient.PostQuit(ctx, dp); err != nil {
+		return kube_ctrl.Result{}, errors.Wrap(err, "envoy admin client failed. Most probably the pod is already going down.")
+	}
+	return kube_ctrl.Result{}, nil
+}
+
+func (r *PodStatusReconciler) SetupWithManager(mgr kube_ctrl.Manager) error {
+	return kube_ctrl.NewControllerManagedBy(mgr).
+		For(&kube_core.Pod{}, builder.WithPredicates(
+			onlyUpdates,
+			onlySidecarContainerRunning,
+		)).
+		Complete(r)
+}
+
+var onlyUpdates = predicate.Funcs{
+	CreateFunc: func(event event.CreateEvent) bool {
+		return true // we need it in case of CP restart
+	},
+	DeleteFunc: func(deleteEvent event.DeleteEvent) bool {
+		return false
+	},
+	UpdateFunc: func(updateEvent event.UpdateEvent) bool {
+		return true
+	},
+	GenericFunc: func(genericEvent event.GenericEvent) bool {
+		return false
+	},
+}
+
+var onlySidecarContainerRunning = predicate.NewPredicateFuncs(
+	func(obj kube_client.Object) bool {
+		pod := obj.(*kube_core.Pod)
+		sidecarContainerRunning := false
+		if pod.Spec.RestartPolicy == kube_core.RestartPolicyAlways {
+			return false
+		}
+
+		for _, cs := range pod.Status.ContainerStatuses {
+			if cs.Name == util_k8s.DubboSidecarContainerName {
+				if cs.State.Terminated != nil {
+					return false
+				}
+				sidecarContainerRunning = true
+			} else {
+				switch pod.Spec.RestartPolicy {
+				case kube_core.RestartPolicyNever:
+					if cs.State.Terminated == nil {
+						// at least one non-sidecar container not terminated
+						// no need to tell envoy to quit yet
+						return false
+					}
+				default:
+					if cs.State.Terminated == nil || cs.State.Terminated.ExitCode != 0 {
+						// at least one non-sidecar container not terminated
+						// or did not completed successfully
+						// no need to tell envoy to quit yet
+						return false
+					}
+				}
+			}
+		}
+
+		return sidecarContainerRunning
+	},
+)
diff --git a/pkg/plugins/runtime/k8s/util/names.go b/pkg/plugins/runtime/k8s/util/names.go
index f4ed7f5..f68406c 100644
--- a/pkg/plugins/runtime/k8s/util/names.go
+++ b/pkg/plugins/runtime/k8s/util/names.go
@@ -18,6 +18,6 @@
 package util
 
 const (
-	dubboSidecarContainerName = "dubbo-sidecar"
+	DubboSidecarContainerName = "dubbo-sidecar"
 	dubboGatewayContainerName = "dubbo-gateway"
 )
diff --git a/pkg/plugins/secrets/k8s/mapper.go b/pkg/plugins/secrets/k8s/mapper.go
new file mode 100644
index 0000000..e2063c5
--- /dev/null
+++ b/pkg/plugins/secrets/k8s/mapper.go
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 k8s
+
+import (
+	"github.com/pkg/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	secret_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	common_k8s "github.com/apache/dubbo-kubernetes/pkg/plugins/common/k8s"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s"
+	k8s_model "github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s/native/pkg/model"
+)
+
+// NewKubernetesMapper creates a ResourceMapper that returns the k8s object as is. This is meant to be used when the underlying store is kubernetes
+func NewKubernetesMapper() k8s.ResourceMapperFunc {
+	return func(resource model.Resource, namespace string) (k8s_model.KubernetesObject, error) {
+		res, err := DefaultConverter().ToKubernetesObject(resource)
+		res.TypeMeta = metav1.TypeMeta{
+			APIVersion: "v1",
+			Kind:       "Secret",
+		}
+		if err != nil {
+			return nil, err
+		}
+		if namespace != "" {
+			res.SetNamespace(namespace)
+		}
+		return &Secret{Secret: *res}, nil
+	}
+}
+
+// NewInferenceMapper creates a ResourceMapper that infers a k8s resource from the core_model. Extract namespace from the name if necessary.
+// This mostly useful when the underlying store is not kubernetes but you want to show what a kubernetes version of the policy would be like (in global for example).
+func NewInferenceMapper(systemNamespace string) k8s.ResourceMapperFunc {
+	return func(resource model.Resource, namespace string) (k8s_model.KubernetesObject, error) {
+		var rs k8s_model.KubernetesObject
+		switch resource.Descriptor().Name {
+		case secret_model.SecretType:
+			rs = NewSecret(common_k8s.MeshSecretType)
+			rs.SetMesh(resource.GetMeta().GetMesh())
+		case secret_model.GlobalSecretType:
+			rs = NewSecret(common_k8s.GlobalSecretType)
+		default:
+			return nil, errors.New("invalid resource type")
+		}
+		if namespace != "" { // If the user is forcing the namespace accept it.
+			rs.SetNamespace(namespace)
+		} else {
+			rs.SetNamespace(systemNamespace)
+		}
+		rs.SetName(resource.GetMeta().GetName())
+		rs.SetCreationTimestamp(v1.NewTime(resource.GetMeta().GetCreationTime()))
+		rs.SetSpec(resource.GetSpec())
+		return rs, nil
+	}
+}
diff --git a/pkg/plugins/secrets/k8s/plugin.go b/pkg/plugins/secrets/k8s/plugin.go
new file mode 100644
index 0000000..ca18dd4
--- /dev/null
+++ b/pkg/plugins/secrets/k8s/plugin.go
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 k8s
+
+import (
+	"github.com/pkg/errors"
+
+	core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+	secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+	k8s_extensions "github.com/apache/dubbo-kubernetes/pkg/plugins/extensions/k8s"
+)
+
+var _ core_plugins.SecretStorePlugin = &plugin{}
+
+type plugin struct{}
+
+func init() {
+	core_plugins.Register(core_plugins.Kubernetes, &plugin{})
+}
+
+func (p *plugin) NewSecretStore(pc core_plugins.PluginContext, _ core_plugins.PluginConfig) (secret_store.SecretStore, error) {
+	mgr, ok := k8s_extensions.FromManagerContext(pc.Extensions())
+	if !ok {
+		return nil, errors.Errorf("k8s controller runtime Manager hasn't been configured")
+	}
+	client, ok := k8s_extensions.FromSecretClientContext(pc.Extensions())
+	if !ok {
+		return nil, errors.Errorf("secret client hasn't been configured")
+	}
+	return NewStore(client, client, mgr.GetScheme(), pc.Config().Store.Kubernetes.SystemNamespace)
+}
diff --git a/pkg/plugins/secrets/k8s/secret.go b/pkg/plugins/secrets/k8s/secret.go
new file mode 100644
index 0000000..4c582f7
--- /dev/null
+++ b/pkg/plugins/secrets/k8s/secret.go
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 k8s
+
+import (
+	"fmt"
+
+	"github.com/pkg/errors"
+	"google.golang.org/protobuf/types/known/wrapperspb"
+	v1 "k8s.io/api/core/v1"
+	k8s "k8s.io/apimachinery/pkg/apis/meta/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s/native/pkg/model"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/runtime/k8s/metadata"
+)
+
+// Secret is a KubernetesObject for Dubbo's Secret and GlobalSecret.
+// Note that it's not registered in TypeRegistry because we cannot multiply KubernetesObject
+// for a single Spec (both Secret and GlobalSecret has same Spec).
+type Secret struct {
+	v1.Secret
+}
+
+func NewSecret(typ v1.SecretType) *Secret {
+	return &Secret{
+		Secret: v1.Secret{
+			TypeMeta: metav1.TypeMeta{
+				APIVersion: v1.SchemeGroupVersion.String(),
+				Kind:       "Secret",
+			},
+			Type: typ,
+		},
+	}
+}
+
+var _ model.KubernetesObject = &Secret{}
+
+func (s *Secret) GetObjectMeta() *k8s.ObjectMeta {
+	return &s.ObjectMeta
+}
+
+func (s *Secret) SetObjectMeta(meta *k8s.ObjectMeta) {
+	s.ObjectMeta = *meta
+}
+
+func (s *Secret) GetMesh() string {
+	if mesh, ok := s.ObjectMeta.Labels[metadata.DubboMeshLabel]; ok {
+		return mesh
+	} else {
+		return core_model.DefaultMesh
+	}
+}
+
+func (s *Secret) SetMesh(mesh string) {
+	if s.ObjectMeta.Labels == nil {
+		s.ObjectMeta.Labels = map[string]string{}
+	}
+	s.ObjectMeta.Labels[metadata.DubboMeshLabel] = mesh
+}
+
+func (s *Secret) GetSpec() (core_model.ResourceSpec, error) {
+	bytes, ok := s.Data["value"]
+	if !ok {
+		return nil, nil
+	}
+	return &system_proto.Secret{
+		Data: &wrapperspb.BytesValue{
+			Value: bytes,
+		},
+	}, nil
+}
+
+func (s *Secret) SetSpec(spec core_model.ResourceSpec) {
+	if _, ok := spec.(*system_proto.Secret); !ok {
+		panic(fmt.Sprintf("unexpected protobuf message type %T", spec))
+	}
+	s.Data = map[string][]byte{
+		"value": spec.(*system_proto.Secret).GetData().GetValue(),
+	}
+}
+
+func (s *Secret) GetStatus() (core_model.ResourceStatus, error) {
+	return nil, nil
+}
+
+func (s *Secret) SetStatus(status core_model.ResourceStatus) error {
+	return errors.New("status not supported")
+}
+
+func (s *Secret) Scope() model.Scope {
+	return model.ScopeNamespace
+}
diff --git a/pkg/plugins/secrets/k8s/store.go b/pkg/plugins/secrets/k8s/store.go
new file mode 100644
index 0000000..5cdc316
--- /dev/null
+++ b/pkg/plugins/secrets/k8s/store.go
@@ -0,0 +1,335 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 k8s
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/pkg/errors"
+	kube_core "k8s.io/api/core/v1"
+	kube_apierrs "k8s.io/apimachinery/pkg/api/errors"
+	kube_meta "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	kube_client "sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+
+	system_proto "github.com/apache/dubbo-kubernetes/api/system/v1alpha1"
+	secret_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/system"
+	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store"
+	secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
+	common_k8s "github.com/apache/dubbo-kubernetes/pkg/plugins/common/k8s"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/resources/k8s"
+	"github.com/apache/dubbo-kubernetes/pkg/plugins/runtime/k8s/metadata"
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+var _ secret_store.SecretStore = &KubernetesStore{}
+
+type KubernetesStore struct {
+	reader           kube_client.Reader
+	writer           kube_client.Writer
+	scheme           *runtime.Scheme
+	secretsConverter Converter
+	// secrets have a special converter, and we need to convert the mesh object since it's the owner
+	resourcesConverter common_k8s.Converter
+	// Namespace to store Secrets in, e.g. namespace where Control Plane is installed to
+	namespace string
+}
+
+func NewStore(reader kube_client.Reader, writer kube_client.Writer, scheme *runtime.Scheme, namespace string) (secret_store.SecretStore, error) {
+	return &KubernetesStore{
+		reader:             reader,
+		writer:             writer,
+		scheme:             scheme,
+		secretsConverter:   DefaultConverter(),
+		resourcesConverter: k8s.NewSimpleConverter(),
+		namespace:          namespace,
+	}, nil
+}
+
+func (s *KubernetesStore) Create(ctx context.Context, r core_model.Resource, fs ...core_store.CreateOptionsFunc) error {
+	opts := core_store.NewCreateOptions(fs...)
+	secret, err := s.secretsConverter.ToKubernetesObject(r)
+	if err != nil {
+		return errors.Wrap(err, "failed to convert core Secret into k8s counterpart")
+	}
+	secret.Namespace = s.namespace
+	secret.Name = opts.Name
+	if r.Descriptor().Scope == core_model.ScopeMesh {
+		setMesh(secret, opts.Mesh, opts.Labels)
+	}
+
+	if opts.Owner != nil {
+		k8sOwner, err := s.resourcesConverter.ToKubernetesObject(opts.Owner)
+		if err != nil {
+			return errors.Wrap(err, "failed to convert core model into k8s counterpart")
+		}
+		if err := controllerutil.SetOwnerReference(k8sOwner, secret, s.scheme); err != nil {
+			return errors.Wrap(err, "failed to set owner reference for object")
+		}
+	}
+
+	if err := s.writer.Create(ctx, secret); err != nil {
+		if kube_apierrs.IsAlreadyExists(err) {
+			return core_store.ErrorResourceAlreadyExists(r.Descriptor().Name, secret.Name, opts.Mesh)
+		}
+		return errors.Wrap(err, "failed to create k8s Secret")
+	}
+	err = s.secretsConverter.ToCoreResource(secret, r)
+	if err != nil {
+		return errors.Wrap(err, "failed to convert k8s Secret into core counterpart")
+	}
+	return nil
+}
+
+func (s *KubernetesStore) Update(ctx context.Context, r core_model.Resource, fs ...core_store.UpdateOptionsFunc) error {
+	opts := core_store.NewUpdateOptions(fs...)
+
+	secret, err := s.secretsConverter.ToKubernetesObject(r)
+	if err != nil {
+		return errors.Wrap(err, "failed to convert core Secret into k8s counterpart")
+	}
+	secret.Namespace = s.namespace
+	if r.Descriptor().Scope == core_model.ScopeMesh {
+		setMesh(secret, r.GetMeta().GetMesh(), opts.Labels)
+	}
+	if err := s.writer.Update(ctx, secret); err != nil {
+		if kube_apierrs.IsConflict(err) {
+			return core_store.ErrorResourceConflict(r.Descriptor().Name, secret.Name, r.GetMeta().GetMesh())
+		}
+		return errors.Wrap(err, "failed to update k8s Secret")
+	}
+	err = s.secretsConverter.ToCoreResource(secret, r)
+	if err != nil {
+		return errors.Wrap(err, "failed to convert k8s Secret into core counterpart")
+	}
+	return nil
+}
+
+func setMesh(s *kube_core.Secret, mesh string, labels map[string]string) {
+	if labels == nil {
+		labels = map[string]string{}
+	}
+	labels[metadata.DubboMeshLabel] = mesh
+	s.SetLabels(labels)
+}
+
+func (s *KubernetesStore) Delete(ctx context.Context, r core_model.Resource, fs ...core_store.DeleteOptionsFunc) error {
+	opts := core_store.NewDeleteOptions(fs...)
+	if err := s.Get(ctx, r, core_store.GetByKey(opts.Name, opts.Mesh)); err != nil {
+		return errors.Wrap(err, "failed to delete k8s secret")
+	}
+
+	secret, err := s.secretsConverter.ToKubernetesObject(r)
+	if err != nil {
+		return errors.Wrap(err, "failed to convert core Secret into k8s counterpart")
+	}
+	secret.Namespace = s.namespace
+	secret.Name = opts.Name
+
+	if err := s.writer.Delete(ctx, secret); err != nil {
+		return errors.Wrap(err, "failed to delete k8s Secret")
+	}
+	return nil
+}
+
+func (s *KubernetesStore) Get(ctx context.Context, r core_model.Resource, fs ...core_store.GetOptionsFunc) error {
+	opts := core_store.NewGetOptions(fs...)
+	secret := &kube_core.Secret{}
+	if err := s.reader.Get(ctx, kube_client.ObjectKey{Namespace: s.namespace, Name: opts.Name}, secret); err != nil {
+		if kube_apierrs.IsNotFound(err) {
+			return core_store.ErrorResourceNotFound(r.Descriptor().Name, opts.Name, opts.Mesh)
+		}
+		return errors.Wrap(err, "failed to get k8s secret")
+	}
+	if err := s.secretsConverter.ToCoreResource(secret, r); err != nil {
+		return errors.Wrap(err, "failed to convert k8s Secret into core counterpart")
+	}
+	if err := assertFound(r, secret, opts.Name, opts.Mesh); err != nil {
+		return err
+	}
+	return nil
+}
+
+func assertFound(r core_model.Resource, secret *kube_core.Secret, name string, mesh string) error {
+	switch r.Descriptor().Name {
+	case secret_model.SecretType:
+		// secret must match mesh and be a proper type, otherwise return not found
+		if r.GetMeta().GetMesh() != mesh || secret.Type != common_k8s.MeshSecretType {
+			if err := r.SetSpec(&system_proto.Secret{}); err != nil {
+				return err
+			}
+			return core_store.ErrorResourceNotFound(r.Descriptor().Name, name, mesh)
+		}
+	case secret_model.GlobalSecretType:
+		// secret must be a proper type, otherwise return not found
+		if secret.Type != common_k8s.GlobalSecretType {
+			if err := r.SetSpec(&system_proto.Secret{}); err != nil {
+				return err
+			}
+			return core_store.ErrorResourceNotFound(r.Descriptor().Name, name, mesh)
+		}
+	}
+	return nil
+}
+
+func (s *KubernetesStore) List(ctx context.Context, rs core_model.ResourceList, fs ...core_store.ListOptionsFunc) error {
+	opts := core_store.NewListOptions(fs...)
+	secrets := &kube_core.SecretList{}
+
+	fields := kube_client.MatchingFields{} // list only Dubbo System secrets
+	labels := kube_client.MatchingLabels{}
+	switch rs.GetItemType() {
+	case secret_model.SecretType:
+		fields = kube_client.MatchingFields{ // list only Dubbo System secrets
+			"type": common_k8s.MeshSecretType,
+		}
+		if opts.Mesh != "" {
+			labels[metadata.DubboMeshLabel] = opts.Mesh
+		}
+	case secret_model.GlobalSecretType:
+		fields = kube_client.MatchingFields{ // list only Dubbo System secrets
+			"type": common_k8s.GlobalSecretType,
+		}
+	}
+	if err := s.reader.List(ctx, secrets, kube_client.InNamespace(s.namespace), labels, fields); err != nil {
+		return errors.Wrap(err, "failed to list k8s Secrets")
+	}
+	if err := s.secretsConverter.ToCoreList(secrets, rs); err != nil {
+		return errors.Wrap(err, "failed to convert k8s Secret into core counterpart")
+	}
+	return nil
+}
+
+var _ core_model.ResourceMeta = &KubernetesMetaAdapter{}
+
+type KubernetesMetaAdapter struct {
+	kube_meta.ObjectMeta
+	SecretType kube_core.SecretType
+}
+
+func (m *KubernetesMetaAdapter) GetNameExtensions() core_model.ResourceNameExtensions {
+	return common_k8s.ResourceNameExtensions(m.ObjectMeta.Namespace, m.ObjectMeta.Name)
+}
+
+func (m *KubernetesMetaAdapter) GetVersion() string {
+	return m.ObjectMeta.GetResourceVersion()
+}
+
+func (m *KubernetesMetaAdapter) GetMesh() string {
+	if m.SecretType == common_k8s.GlobalSecretType {
+		return ""
+	}
+	mesh, exist := m.Labels[metadata.DubboMeshLabel]
+	if !exist {
+		mesh = core_model.DefaultMesh
+	}
+	return mesh
+}
+
+func (m *KubernetesMetaAdapter) GetCreationTime() time.Time {
+	return m.GetObjectMeta().GetCreationTimestamp().Time
+}
+
+func (m *KubernetesMetaAdapter) GetModificationTime() time.Time {
+	return m.GetObjectMeta().GetCreationTimestamp().Time
+}
+
+type Converter interface {
+	ToKubernetesObject(resource core_model.Resource) (*kube_core.Secret, error)
+	ToCoreResource(secret *kube_core.Secret, out core_model.Resource) error
+	ToCoreList(list *kube_core.SecretList, out core_model.ResourceList) error
+}
+
+func DefaultConverter() Converter {
+	return &SimpleConverter{}
+}
+
+var _ Converter = &SimpleConverter{}
+
+type SimpleConverter struct{}
+
+func (c *SimpleConverter) ToKubernetesObject(r core_model.Resource) (*kube_core.Secret, error) {
+	secret := &kube_core.Secret{}
+	switch r.Descriptor().Name {
+	case secret_model.SecretType:
+		secret.Type = common_k8s.MeshSecretType
+		secret.Data = map[string][]byte{
+			"value": r.(*secret_model.SecretResource).Spec.GetData().GetValue(),
+		}
+	case secret_model.GlobalSecretType:
+		secret.Type = common_k8s.GlobalSecretType
+		secret.Data = map[string][]byte{
+			"value": r.(*secret_model.GlobalSecretResource).Spec.GetData().GetValue(),
+		}
+	default:
+		return nil, errors.Errorf("invalid type %s, expected %s or %s", r.Descriptor().Name, secret_model.SecretType, secret_model.GlobalSecretType)
+	}
+	if r.GetMeta() != nil {
+		if adapter, ok := r.GetMeta().(*KubernetesMetaAdapter); ok {
+			secret.ObjectMeta = adapter.ObjectMeta
+		} else {
+			return nil, fmt.Errorf("meta has unexpected type: %#v", r.GetMeta())
+		}
+	}
+	return secret, nil
+}
+
+func (c *SimpleConverter) ToCoreResource(secret *kube_core.Secret, out core_model.Resource) error {
+	out.SetMeta(&KubernetesMetaAdapter{
+		ObjectMeta: secret.ObjectMeta,
+		SecretType: secret.Type,
+	})
+	if secret.Data != nil {
+		_ = out.SetSpec(&system_proto.Secret{
+			Data: util_proto.Bytes(secret.Data["value"]),
+		})
+	}
+	return nil
+}
+
+func (c *SimpleConverter) ToCoreList(in *kube_core.SecretList, out core_model.ResourceList) error {
+	switch out.GetItemType() {
+	case secret_model.SecretType:
+		secOut := out.(*secret_model.SecretResourceList)
+		secOut.Items = make([]*secret_model.SecretResource, len(in.Items))
+		for i := range in.Items {
+			r := secret_model.NewSecretResource()
+			if err := c.ToCoreResource(&in.Items[i], r); err != nil {
+				return err
+			}
+			secOut.Items[i] = r
+		}
+	case secret_model.GlobalSecretType:
+		secOut := out.(*secret_model.GlobalSecretResourceList)
+		secOut.Items = make([]*secret_model.GlobalSecretResource, len(in.Items))
+		for i := range in.Items {
+			r := secret_model.NewGlobalSecretResource()
+			if err := c.ToCoreResource(&in.Items[i], r); err != nil {
+				return err
+			}
+			secOut.Items[i] = r
+		}
+	default:
+		return errors.Errorf("invalid type %s, expected %s or %s", out.GetItemType(), secret_model.SecretType, secret_model.GlobalSecretType)
+	}
+	return nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/plugins/secrets/universal/plugin.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/plugins/secrets/universal/plugin.go
index 987f638..77a8d24 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/plugins/secrets/universal/plugin.go
@@ -15,25 +15,21 @@
  * limitations under the License.
  */
 
-package dubbo
+package universal
 
 import (
-	"archive/zip"
-	"bytes"
+	core_plugins "github.com/apache/dubbo-kubernetes/pkg/core/plugins"
+	secret_store "github.com/apache/dubbo-kubernetes/pkg/core/secrets/store"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+var _ core_plugins.SecretStorePlugin = &plugin{}
 
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+type plugin struct{}
+
+func init() {
+	core_plugins.Register(core_plugins.Universal, &plugin{})
 }
 
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (p *plugin) NewSecretStore(pc core_plugins.PluginContext, _ core_plugins.PluginConfig) (secret_store.SecretStore, error) {
+	return secret_store.NewSecretStore(pc.ResourceStore().DefaultResourceStore()), nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/tokens/builtin/access/access.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/tokens/builtin/access/access.go
index 987f638..4471ca0 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/tokens/builtin/access/access.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package access
 
 import (
-	"archive/zip"
-	"bytes"
+	"context"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+type DataplaneTokenAccess interface {
+	ValidateGenerateDataPlaneToken(ctx context.Context, name string, mesh string, tag map[string][]string, user user.User) error
+	ValidateGenerateZoneIngressToken(ctx context.Context, name string, zone string, user user.User) error
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/tokens/builtin/access/noop.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/tokens/builtin/access/noop.go
index 987f638..d573664 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/tokens/builtin/access/noop.go
@@ -15,25 +15,21 @@
  * limitations under the License.
  */
 
-package dubbo
+package access
 
 import (
-	"archive/zip"
-	"bytes"
+	"context"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+type NoopDpTokenAccess struct{}
 
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+var _ DataplaneTokenAccess = NoopDpTokenAccess{}
+
+func (n NoopDpTokenAccess) ValidateGenerateDataPlaneToken(ctx context.Context, name string, mesh string, tag map[string][]string, user user.User) error {
+	return nil
 }
 
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (n NoopDpTokenAccess) ValidateGenerateZoneIngressToken(ctx context.Context, name string, mesh string, user user.User) error {
+	return nil
+}
diff --git a/pkg/tokens/builtin/access/static.go b/pkg/tokens/builtin/access/static.go
new file mode 100644
index 0000000..557b7de
--- /dev/null
+++ b/pkg/tokens/builtin/access/static.go
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import (
+	"context"
+	config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type staticGenerateDataplaneTokenAccess struct {
+	usernames map[string]struct{}
+	groups    map[string]struct{}
+}
+
+var _ DataplaneTokenAccess = &staticGenerateDataplaneTokenAccess{}
+
+func NewStaticGenerateDataplaneTokenAccess(cfg config_access.GenerateDPTokenStaticAccessConfig) DataplaneTokenAccess {
+	s := &staticGenerateDataplaneTokenAccess{
+		usernames: make(map[string]struct{}, len(cfg.Users)),
+		groups:    make(map[string]struct{}, len(cfg.Groups)),
+	}
+	for _, u := range cfg.Users {
+		s.usernames[u] = struct{}{}
+	}
+	for _, group := range cfg.Groups {
+		s.groups[group] = struct{}{}
+	}
+	return s
+}
+
+func (s *staticGenerateDataplaneTokenAccess) ValidateGenerateDataPlaneToken(ctx context.Context, name string, mesh string, tags map[string][]string, user user.User) error {
+	return access.Validate(s.usernames, s.groups, user, "generate dataplane token")
+}
+
+func (s *staticGenerateDataplaneTokenAccess) ValidateGenerateZoneIngressToken(ctx context.Context, name string, zone string, user user.User) error {
+	return access.Validate(s.usernames, s.groups, user, "generate zone ingress token")
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/tokens/builtin/zone/access/access.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/tokens/builtin/zone/access/access.go
index 987f638..5053ff9 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/tokens/builtin/zone/access/access.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package access
 
 import (
-	"archive/zip"
-	"bytes"
+	"context"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+type ZoneTokenAccess interface {
+	ValidateGenerateZoneToken(ctx context.Context, zone string, user user.User) error
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/tokens/builtin/zone/access/noop.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/tokens/builtin/zone/access/noop.go
index 987f638..13e32cc 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/tokens/builtin/zone/access/noop.go
@@ -15,25 +15,18 @@
  * limitations under the License.
  */
 
-package dubbo
+package access
 
 import (
-	"archive/zip"
-	"bytes"
+	"context"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+type NoopZoneTokenAccess struct{}
 
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+var _ ZoneTokenAccess = NoopZoneTokenAccess{}
+
+func (n NoopZoneTokenAccess) ValidateGenerateZoneToken(ctx context.Context, zone string, user user.User) error {
+	return nil
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/tokens/builtin/zone/access/static.go b/pkg/tokens/builtin/zone/access/static.go
new file mode 100644
index 0000000..e039873
--- /dev/null
+++ b/pkg/tokens/builtin/zone/access/static.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 access
+
+import (
+	"context"
+	"fmt"
+
+	config_access "github.com/apache/dubbo-kubernetes/pkg/config/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/access"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+)
+
+type staticZoneTokenAccess struct {
+	usernames map[string]struct{}
+	groups    map[string]struct{}
+}
+
+var _ ZoneTokenAccess = &staticZoneTokenAccess{}
+
+func NewStaticZoneTokenAccess(cfg config_access.GenerateZoneTokenStaticAccessConfig) ZoneTokenAccess {
+	s := &staticZoneTokenAccess{
+		usernames: map[string]struct{}{},
+		groups:    map[string]struct{}{},
+	}
+	for _, user := range cfg.Users {
+		s.usernames[user] = struct{}{}
+	}
+	for _, group := range cfg.Groups {
+		s.groups[group] = struct{}{}
+	}
+	return s
+}
+
+func (s *staticZoneTokenAccess) ValidateGenerateZoneToken(ctx context.Context, zone string, user user.User) error {
+	return access.Validate(s.usernames, s.groups, user, fmt.Sprintf("generate zone token for zone '%s'", zone))
+}
diff --git a/pkg/tokens/builtin/zone/token.go b/pkg/tokens/builtin/zone/token.go
new file mode 100644
index 0000000..f664133
--- /dev/null
+++ b/pkg/tokens/builtin/zone/token.go
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 zone
+
+import (
+	"github.com/golang-jwt/jwt/v4"
+
+	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	core_tokens "github.com/apache/dubbo-kubernetes/pkg/core/tokens"
+)
+
+const (
+	SigningKeyPrefix       = "zone-token-signing-key"
+	SigningPublicKeyPrefix = "zone-token-signing-public-key"
+)
+
+var TokenRevocationsGlobalSecretKey = core_model.ResourceKey{
+	Name: "zone-token-revocations",
+	Mesh: core_model.NoMesh,
+}
+
+type ZoneClaims struct {
+	Zone  string
+	Scope []string
+	jwt.RegisteredClaims
+}
+
+var _ core_tokens.Claims = &ZoneClaims{}
+
+func (c *ZoneClaims) ID() string {
+	return c.RegisteredClaims.ID
+}
+
+func (c *ZoneClaims) SetRegisteredClaims(claims jwt.RegisteredClaims) {
+	c.RegisteredClaims = claims
+}
diff --git a/pkg/tokens/client.go b/pkg/tokens/client.go
new file mode 100644
index 0000000..255c1e3
--- /dev/null
+++ b/pkg/tokens/client.go
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tokens
+
+import (
+	"bytes"
+	"encoding/json"
+	"io"
+	"net/http"
+
+	"github.com/pkg/errors"
+
+	error_types "github.com/apache/dubbo-kubernetes/pkg/core/rest/errors/types"
+	util_http "github.com/apache/dubbo-kubernetes/pkg/util/http"
+)
+
+type TokenClient struct {
+	client util_http.Client
+	url    string
+}
+
+func NewTokenClient(client util_http.Client, entity string) TokenClient {
+	return TokenClient{
+		client: client,
+		url:    "/tokens/" + entity,
+	}
+}
+
+func (tc TokenClient) Generate(tokenReq any) (string, error) {
+	reqBytes, err := json.Marshal(tokenReq)
+	if err != nil {
+		return "", errors.Wrap(err, "could not marshal token request to json")
+	}
+	req, err := http.NewRequest(http.MethodPost, tc.url, bytes.NewReader(reqBytes))
+	if err != nil {
+		return "", errors.Wrap(err, "could not construct the request")
+	}
+	req.Header.Set("content-type", "application/json")
+	resp, err := tc.client.Do(req)
+	if err != nil {
+		return "", errors.Wrap(err, "could not execute the request")
+	}
+	defer resp.Body.Close()
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return "", errors.Wrap(err, "could not read a body of the request")
+	}
+	if resp.StatusCode != http.StatusOK {
+		var kumaErr error_types.Error
+		if err := json.Unmarshal(body, &kumaErr); err == nil {
+			if kumaErr.Title != "" {
+				return "", &kumaErr
+			}
+		}
+		return "", errors.Errorf("(%d): %s", resp.StatusCode, body)
+	}
+	return string(body), nil
+}
diff --git a/pkg/xds/context/context.go b/pkg/xds/context/context.go
index 8537db0..88a66f2 100644
--- a/pkg/xds/context/context.go
+++ b/pkg/xds/context/context.go
@@ -19,9 +19,9 @@
 
 import (
 	"encoding/base64"
-)
 
-import (
+	"github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
+
 	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
 	"github.com/apache/dubbo-kubernetes/pkg/core/xds"
 	"github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
@@ -42,6 +42,7 @@
 type ControlPlaneContext struct {
 	CLACache envoy.CLACache
 	Zone     string
+	Secrets  secrets.Secrets
 }
 
 // GlobalContext holds resources that are Global
@@ -121,3 +122,15 @@
 	}
 	return nil
 }
+
+func (mc *MeshContext) GetTlsReadiness() map[string]bool {
+	tlsReady := map[string]bool{}
+	for serviceName, info := range mc.ServicesInformation {
+		if info != nil {
+			tlsReady[serviceName] = info.TLSReadiness
+		} else {
+			tlsReady[serviceName] = false
+		}
+	}
+	return tlsReady
+}
diff --git a/pkg/xds/context/resources.go b/pkg/xds/context/resources.go
index 7f4ac3d..68b73d5 100644
--- a/pkg/xds/context/resources.go
+++ b/pkg/xds/context/resources.go
@@ -19,9 +19,7 @@
 
 import (
 	"hash/fnv"
-)
 
-import (
 	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
 	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
 	"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
@@ -72,3 +70,11 @@
 func (r Resources) Dataplanes() *core_mesh.DataplaneResourceList {
 	return r.ListOrEmpty(core_mesh.DataplaneType).(*core_mesh.DataplaneResourceList)
 }
+
+func (r Resources) OtherMeshes() *core_mesh.MeshResourceList {
+	return r.ListOrEmpty(core_mesh.MeshType).(*core_mesh.MeshResourceList)
+}
+
+func (r Resources) Meshes() *core_mesh.MeshResourceList {
+	return r.ListOrEmpty(core_mesh.MeshType).(*core_mesh.MeshResourceList)
+}
diff --git a/pkg/xds/envoy/clusters/configurers.go b/pkg/xds/envoy/clusters/configurers.go
index 7d14dd9..bb987e0 100644
--- a/pkg/xds/envoy/clusters/configurers.go
+++ b/pkg/xds/envoy/clusters/configurers.go
@@ -18,14 +18,15 @@
 package clusters
 
 import (
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
 	envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
 
 	"google.golang.org/protobuf/types/known/wrapperspb"
-)
 
-import (
 	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+
 	v3 "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/clusters/v3"
+
 	envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
 )
 
@@ -36,6 +37,14 @@
 	})
 }
 
+func DefaultTimeout() ClusterBuilderOpt {
+	return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
+		builder.AddConfigurer(&v3.TimeoutConfigurer{
+			Protocol: core_mesh.ProtocolTCP,
+		})
+	})
+}
+
 // ProvidedEndpointCluster sets the cluster with the defined endpoints, this is useful when endpoints are not discovered using EDS, so we don't use EdsCluster
 func ProvidedEndpointCluster(hasIPv6 bool, endpoints ...core_xds.Endpoint) ClusterBuilderOpt {
 	return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
@@ -113,3 +122,37 @@
 		builder.AddConfigurer(&v3.HttpConfigurer{})
 	})
 }
+
+func ClientSideMTLS(tracker core_xds.SecretsTracker, mesh *core_mesh.MeshResource, upstreamService string, upstreamTLSReady bool, tags []envoy_tags.Tags) ClusterBuilderOpt {
+	return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
+		builder.AddConfigurer(&v3.ClientSideMTLSConfigurer{
+			SecretsTracker:   tracker,
+			UpstreamMesh:     mesh,
+			UpstreamService:  upstreamService,
+			LocalMesh:        mesh,
+			Tags:             tags,
+			UpstreamTLSReady: upstreamTLSReady,
+		})
+	})
+}
+
+func ClientSideTLS(endpoints []core_xds.Endpoint) ClusterBuilderOpt {
+	return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
+		builder.AddConfigurer(&v3.ClientSideTLSConfigurer{
+			Endpoints: endpoints,
+		})
+	})
+}
+
+func CrossMeshClientSideMTLS(tracker core_xds.SecretsTracker, localMesh *core_mesh.MeshResource, upstreamMesh *core_mesh.MeshResource, upstreamService string, upstreamTLSReady bool, tags []envoy_tags.Tags) ClusterBuilderOpt {
+	return ClusterBuilderOptFunc(func(builder *ClusterBuilder) {
+		builder.AddConfigurer(&v3.ClientSideMTLSConfigurer{
+			SecretsTracker:   tracker,
+			UpstreamMesh:     upstreamMesh,
+			UpstreamService:  upstreamService,
+			LocalMesh:        localMesh,
+			Tags:             tags,
+			UpstreamTLSReady: upstreamTLSReady,
+		})
+	})
+}
diff --git a/pkg/xds/envoy/clusters/v3/alt_stat_name_configurer.go b/pkg/xds/envoy/clusters/v3/alt_stat_name_configurer.go
index 8006f34..c21cb65 100644
--- a/pkg/xds/envoy/clusters/v3/alt_stat_name_configurer.go
+++ b/pkg/xds/envoy/clusters/v3/alt_stat_name_configurer.go
@@ -19,9 +19,7 @@
 
 import (
 	envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
-)
 
-import (
 	util_xds "github.com/apache/dubbo-kubernetes/pkg/util/xds"
 )
 
diff --git a/pkg/xds/envoy/clusters/v3/client_side_mtls_configurer.go b/pkg/xds/envoy/clusters/v3/client_side_mtls_configurer.go
new file mode 100644
index 0000000..36b2798
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/client_side_mtls_configurer.go
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 clusters
+
+import (
+	envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	"google.golang.org/protobuf/types/known/structpb"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	"github.com/apache/dubbo-kubernetes/pkg/util/proto"
+	envoy_metadata "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/metadata/v3"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
+	envoy_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ClientSideMTLSConfigurer struct {
+	SecretsTracker   core_xds.SecretsTracker
+	UpstreamMesh     *core_mesh.MeshResource
+	UpstreamService  string
+	LocalMesh        *core_mesh.MeshResource
+	Tags             []tags.Tags
+	SNI              string
+	UpstreamTLSReady bool
+	VerifyIdentities []string
+}
+
+var _ ClusterConfigurer = &ClientSideMTLSConfigurer{}
+
+func (c *ClientSideMTLSConfigurer) Configure(cluster *envoy_cluster.Cluster) error {
+	if !c.UpstreamMesh.MTLSEnabled() || !c.LocalMesh.MTLSEnabled() {
+		return nil
+	}
+	if c.UpstreamMesh.GetEnabledCertificateAuthorityBackend().Mode == mesh_proto.CertificateAuthorityBackend_PERMISSIVE &&
+		!c.UpstreamTLSReady {
+		return nil
+	}
+
+	meshName := c.UpstreamMesh.GetMeta().GetName()
+	// there might be a situation when there are multiple sam tags passed here for example two outbound listeners with the same tags, therefore we need to distinguish between them.
+	distinctTags := tags.DistinctTags(c.Tags)
+	switch {
+	case len(distinctTags) == 0:
+		transportSocket, err := c.createTransportSocket(c.SNI)
+		if err != nil {
+			return err
+		}
+		cluster.TransportSocket = transportSocket
+	case len(distinctTags) == 1:
+		sni := tls.SNIFromTags(c.Tags[0].WithTags("mesh", meshName))
+		transportSocket, err := c.createTransportSocket(sni)
+		if err != nil {
+			return err
+		}
+		cluster.TransportSocket = transportSocket
+	default:
+		for _, tags := range distinctTags {
+			sni := tls.SNIFromTags(tags.WithTags("mesh", meshName))
+			transportSocket, err := c.createTransportSocket(sni)
+			if err != nil {
+				return err
+			}
+			cluster.TransportSocketMatches = append(cluster.TransportSocketMatches, &envoy_cluster.Cluster_TransportSocketMatch{
+				Name: sni,
+				Match: &structpb.Struct{
+					Fields: envoy_metadata.MetadataFields(tags.WithoutTags(mesh_proto.ServiceTag)),
+				},
+				TransportSocket: transportSocket,
+			})
+		}
+	}
+	return nil
+}
+
+func (c *ClientSideMTLSConfigurer) createTransportSocket(sni string) (*envoy_core.TransportSocket, error) {
+	if !c.UpstreamMesh.MTLSEnabled() {
+		return nil, nil
+	}
+
+	ca := c.SecretsTracker.RequestCa(c.UpstreamMesh.GetMeta().GetName())
+	identity := c.SecretsTracker.RequestIdentityCert()
+
+	var verifyIdentities []string
+	if c.VerifyIdentities != nil {
+		verifyIdentities = c.VerifyIdentities
+	}
+	tlsContext, err := envoy_tls.CreateUpstreamTlsContext(identity, ca, c.UpstreamService, sni, verifyIdentities)
+	if err != nil {
+		return nil, err
+	}
+	if tlsContext == nil {
+		return nil, nil
+	}
+	pbst, err := proto.MarshalAnyDeterministic(tlsContext)
+	if err != nil {
+		return nil, err
+	}
+	transportSocket := &envoy_core.TransportSocket{
+		Name: "envoy.transport_sockets.tls",
+		ConfigType: &envoy_core.TransportSocket_TypedConfig{
+			TypedConfig: pbst,
+		},
+	}
+	return transportSocket, nil
+}
diff --git a/pkg/xds/envoy/clusters/v3/client_side_tls_configurer.go b/pkg/xds/envoy/clusters/v3/client_side_tls_configurer.go
new file mode 100644
index 0000000..4a019a5
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/client_side_tls_configurer.go
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 clusters
+
+import (
+	"github.com/asaskevich/govalidator"
+	envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	"google.golang.org/protobuf/types/known/structpb"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	"github.com/apache/dubbo-kubernetes/pkg/util/proto"
+	envoy_metadata "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/metadata/v3"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+	envoy_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ClientSideTLSConfigurer struct {
+	Endpoints           []xds.Endpoint
+	SystemCaPath        string
+	UseCommonTlsContext bool // used to handle MeshExternalService
+}
+
+var _ ClusterConfigurer = &ClientSideTLSConfigurer{}
+
+func (c *ClientSideTLSConfigurer) Configure(cluster *envoy_cluster.Cluster) error {
+	if c.UseCommonTlsContext && len(c.Endpoints) > 0 {
+		ep := c.Endpoints[0]
+		if ep.ExternalService != nil && ep.ExternalService.TLSEnabled {
+			tsm, err := c.createTransportSocketMatch(&ep, false)
+			if err != nil {
+				return err
+			}
+			cluster.TransportSocketMatches = append(cluster.TransportSocketMatches, tsm)
+		}
+	} else {
+		for i, ep := range c.Endpoints {
+			if ep.ExternalService != nil && ep.ExternalService.TLSEnabled {
+				tsm, err := c.createTransportSocketMatch(&c.Endpoints[i], true)
+				if err != nil {
+					return err
+				}
+				cluster.TransportSocketMatches = append(cluster.TransportSocketMatches, tsm)
+			}
+		}
+	}
+
+	return nil
+}
+
+func (c *ClientSideTLSConfigurer) createTransportSocketMatch(ep *xds.Endpoint, withMatch bool) (*envoy_cluster.Cluster_TransportSocketMatch, error) {
+	sni := ep.ExternalService.ServerName
+	if ep.ExternalService.ServerName == "" && govalidator.IsDNSName(ep.Target) {
+		// SNI can only be a hostname, not IP
+		sni = ep.Target
+	}
+
+	tlsContext, err := envoy_tls.UpstreamTlsContextOutsideMesh(
+		c.SystemCaPath,
+		ep.ExternalService.CaCert,
+		ep.ExternalService.ClientCert,
+		ep.ExternalService.ClientKey,
+		ep.ExternalService.AllowRenegotiation,
+		ep.ExternalService.SkipHostnameVerification,
+		ep.ExternalService.FallbackToSystemCa,
+		ep.Target,
+		sni,
+		ep.ExternalService.SANs,
+		ep.ExternalService.MinTlsVersion,
+		ep.ExternalService.MaxTlsVersion,
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	pbst, err := proto.MarshalAnyDeterministic(tlsContext)
+	if err != nil {
+		return nil, err
+	}
+
+	transportSocket := &envoy_core.TransportSocket{
+		Name: "envoy.transport_sockets.tls",
+		ConfigType: &envoy_core.TransportSocket_TypedConfig{
+			TypedConfig: pbst,
+		},
+	}
+
+	tsm := envoy_cluster.Cluster_TransportSocketMatch{
+		Name:            ep.Target,
+		TransportSocket: transportSocket,
+	}
+
+	if withMatch {
+		tsm.Match = &structpb.Struct{
+			Fields: envoy_metadata.MetadataFields(tags.Tags(ep.Tags).WithoutTags(mesh_proto.ServiceTag)),
+		}
+	}
+
+	return &tsm, nil
+}
diff --git a/pkg/xds/envoy/clusters/v3/configurer.go b/pkg/xds/envoy/clusters/v3/configurer.go
index 5a863ca..1031cdf 100644
--- a/pkg/xds/envoy/clusters/v3/configurer.go
+++ b/pkg/xds/envoy/clusters/v3/configurer.go
@@ -19,6 +19,7 @@
 
 import (
 	envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+	envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
 )
 
 // ClusterConfigurer is responsible for configuring a single aspect of the entire Envoy cluster,
@@ -28,6 +29,11 @@
 	Configure(cluster *envoy_cluster.Cluster) error
 }
 
+type FilterChainConfigurer interface {
+	// Configure configures a single aspect on a given Envoy filter chain.
+	Configure(filterChain *envoy_listener.FilterChain) error
+}
+
 // ClusterMustConfigureFunc adapts a configuration function that never
 // fails to the ListenerConfigurer interface.
 type ClusterMustConfigureFunc func(cluster *envoy_cluster.Cluster)
diff --git a/pkg/xds/envoy/clusters/v3/server_static_mtls.go b/pkg/xds/envoy/clusters/v3/server_static_mtls.go
new file mode 100644
index 0000000..258ca28
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/server_static_mtls.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 clusters
+
+import (
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+	envoy_tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+	envoy_type_matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
+
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	envoy_admin_tls "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+	tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ServerSideStaticMTLSConfigurer struct {
+	MTLSCerts core_xds.ServerSideMTLSCerts
+}
+
+var _ FilterChainConfigurer = &ServerSideStaticMTLSConfigurer{}
+
+func (c *ServerSideStaticMTLSConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
+	tlsContext := tls.StaticDownstreamTlsContextWithValue(&c.MTLSCerts.ServerPair)
+	tlsContext.RequireClientCertificate = util_proto.Bool(true)
+
+	tlsContext.CommonTlsContext.ValidationContextType = &envoy_tls.CommonTlsContext_ValidationContext{
+		ValidationContext: &envoy_tls.CertificateValidationContext{
+			TrustedCa: &envoy_core.DataSource{
+				Specifier: &envoy_core.DataSource_InlineBytes{
+					InlineBytes: c.MTLSCerts.CaPEM,
+				},
+			},
+			MatchTypedSubjectAltNames: []*envoy_tls.SubjectAltNameMatcher{{
+				SanType: envoy_tls.SubjectAltNameMatcher_DNS,
+				Matcher: &envoy_type_matcher.StringMatcher{
+					MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+						Exact: envoy_admin_tls.ClientCertSAN,
+					},
+					IgnoreCase: false,
+				},
+			}},
+		},
+	}
+
+	pbst, err := util_proto.MarshalAnyDeterministic(tlsContext)
+	if err != nil {
+		return err
+	}
+	filterChain.TransportSocket = &envoy_core.TransportSocket{
+		Name: "envoy.transport_sockets.tls",
+		ConfigType: &envoy_core.TransportSocket_TypedConfig{
+			TypedConfig: pbst,
+		},
+	}
+	return nil
+}
diff --git a/pkg/xds/envoy/clusters/v3/timeout_configurer.go b/pkg/xds/envoy/clusters/v3/timeout_configurer.go
new file mode 100644
index 0000000..fe95603
--- /dev/null
+++ b/pkg/xds/envoy/clusters/v3/timeout_configurer.go
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 clusters
+
+import (
+	envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_upstream_http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	policies_defaults "github.com/apache/dubbo-kubernetes/pkg/plugins/policies/core/defaults"
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+)
+
+type TimeoutConfigurer struct {
+	Protocol core_mesh.Protocol
+	Conf     *mesh_proto.Timeout_Conf
+}
+
+var _ ClusterConfigurer = &TimeoutConfigurer{}
+
+func (t *TimeoutConfigurer) Configure(cluster *envoy_cluster.Cluster) error {
+	cluster.ConnectTimeout = util_proto.Duration(t.Conf.GetConnectTimeoutOrDefault(policies_defaults.DefaultConnectTimeout))
+	switch t.Protocol {
+	case core_mesh.ProtocolHTTP, core_mesh.ProtocolHTTP2, core_mesh.ProtocolGRPC:
+		err := UpdateCommonHttpProtocolOptions(cluster, func(options *envoy_upstream_http.HttpProtocolOptions) {
+			if options.CommonHttpProtocolOptions == nil {
+				options.CommonHttpProtocolOptions = &envoy_core.HttpProtocolOptions{}
+			}
+
+			t.setIdleTimeout(options.CommonHttpProtocolOptions)
+			t.setMaxStreamDuration(options.CommonHttpProtocolOptions)
+		})
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (t *TimeoutConfigurer) setIdleTimeout(options *envoy_core.HttpProtocolOptions) {
+	options.IdleTimeout = util_proto.Duration(t.Conf.GetHttp().GetIdleTimeout().AsDuration())
+}
+
+func (t *TimeoutConfigurer) setMaxStreamDuration(options *envoy_core.HttpProtocolOptions) {
+	if msd := t.Conf.GetHttp().GetMaxStreamDuration(); msd != nil && msd.AsDuration() != 0 {
+		options.MaxStreamDuration = msd
+		return
+	}
+
+	// backwards compatibility
+	if t.Protocol == core_mesh.ProtocolGRPC {
+		if msd := t.Conf.GetGrpc().GetMaxStreamDuration(); msd != nil && msd.AsDuration() != 0 {
+			options.MaxStreamDuration = util_proto.Duration(msd.AsDuration())
+		}
+	}
+}
diff --git a/pkg/xds/envoy/listeners/filter_chain_configurers.go b/pkg/xds/envoy/listeners/filter_chain_configurers.go
index 50b75f0..16db7f8 100644
--- a/pkg/xds/envoy/listeners/filter_chain_configurers.go
+++ b/pkg/xds/envoy/listeners/filter_chain_configurers.go
@@ -18,19 +18,19 @@
 package listeners
 
 import (
-	envoy_config_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
-	envoy_extensions_compression_gzip_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/compressor/v3"
-	envoy_extensions_filters_http_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3"
-	envoy_hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
-)
-
-import (
+	common_tls "github.com/apache/dubbo-kubernetes/api/common/v1alpha1/tls"
 	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
 	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
 	envoy_common "github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
 	v3 "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners/v3"
 	envoy_routes "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/routes"
 	"github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+	envoy_config_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_extensions_compression_gzip_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/compressor/v3"
+	envoy_extensions_filters_http_compressor_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3"
+	envoy_hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
 )
 
 func GrpcStats() FilterChainBuilderOpt {
@@ -57,6 +57,28 @@
 	})
 }
 
+func ServerSideMTLS(mesh *core_mesh.MeshResource, secrets core_xds.SecretsTracker, tlsVersion *common_tls.Version, tlsCiphers common_tls.TlsCiphers) FilterChainBuilderOpt {
+	return AddFilterChainConfigurer(&v3.ServerSideMTLSConfigurer{
+		Mesh:           mesh,
+		SecretsTracker: secrets,
+		TlsVersion:     tlsVersion,
+		TlsCiphers:     tlsCiphers,
+	})
+}
+
+func ServerSideStaticMTLS(mtlsCerts core_xds.ServerSideMTLSCerts) FilterChainBuilderOpt {
+	return AddFilterChainConfigurer(&v3.ServerSideStaticMTLSConfigurer{
+		MTLSCerts: mtlsCerts,
+	})
+}
+
+func ServerSideStaticTLS(tlsCerts core_xds.ServerSideTLSCertPaths) FilterChainBuilderOpt {
+	return AddFilterChainConfigurer(&v3.ServerSideStaticTLSConfigurer{
+		CertPath: tlsCerts.CertPath,
+		KeyPath:  tlsCerts.KeyPath,
+	})
+}
+
 func HttpConnectionManager(statsName string, forwardClientCertDetails bool) FilterChainBuilderOpt {
 	return AddFilterChainConfigurer(&v3.HttpConnectionManagerConfigurer{
 		StatsName:                statsName,
diff --git a/pkg/xds/envoy/listeners/listener_configurers.go b/pkg/xds/envoy/listeners/listener_configurers.go
index b5de92f..8e3ef63 100644
--- a/pkg/xds/envoy/listeners/listener_configurers.go
+++ b/pkg/xds/envoy/listeners/listener_configurers.go
@@ -21,11 +21,11 @@
 	envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
 
 	"google.golang.org/protobuf/types/known/wrapperspb"
-)
 
-import (
 	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+
 	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+
 	v3 "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners/v3"
 )
 
diff --git a/pkg/xds/envoy/listeners/v3/server_mtls_configurer.go b/pkg/xds/envoy/listeners/v3/server_mtls_configurer.go
new file mode 100644
index 0000000..46725b0
--- /dev/null
+++ b/pkg/xds/envoy/listeners/v3/server_mtls_configurer.go
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 v3
+
+import (
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+
+	common_tls "github.com/apache/dubbo-kubernetes/api/common/v1alpha1/tls"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	"github.com/apache/dubbo-kubernetes/pkg/util/proto"
+	tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ServerSideMTLSConfigurer struct {
+	Mesh           *core_mesh.MeshResource
+	SecretsTracker core_xds.SecretsTracker
+	TlsVersion     *common_tls.Version
+	TlsCiphers     common_tls.TlsCiphers
+}
+
+var _ FilterChainConfigurer = &ServerSideMTLSConfigurer{}
+
+func (c *ServerSideMTLSConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
+	if !c.Mesh.MTLSEnabled() {
+		return nil
+	}
+	tlsContext, err := tls.CreateDownstreamTlsContext(c.SecretsTracker.RequestCa(c.Mesh.GetMeta().GetName()), c.SecretsTracker.RequestIdentityCert())
+	if err != nil {
+		return err
+	}
+	if tlsContext != nil {
+		pbst, err := proto.MarshalAnyDeterministic(tlsContext)
+		if err != nil {
+			return err
+		}
+		filterChain.TransportSocket = &envoy_core.TransportSocket{
+			Name: "envoy.transport_sockets.tls",
+			ConfigType: &envoy_core.TransportSocket_TypedConfig{
+				TypedConfig: pbst,
+			},
+		}
+	}
+	return nil
+}
diff --git a/pkg/xds/envoy/listeners/v3/server_static_mtls.go b/pkg/xds/envoy/listeners/v3/server_static_mtls.go
new file mode 100644
index 0000000..d586061
--- /dev/null
+++ b/pkg/xds/envoy/listeners/v3/server_static_mtls.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 v3
+
+import (
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+	envoy_tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+	envoy_type_matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
+
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	envoy_admin_tls "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+	tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ServerSideStaticMTLSConfigurer struct {
+	MTLSCerts core_xds.ServerSideMTLSCerts
+}
+
+var _ FilterChainConfigurer = &ServerSideStaticMTLSConfigurer{}
+
+func (c *ServerSideStaticMTLSConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
+	tlsContext := tls.StaticDownstreamTlsContextWithValue(&c.MTLSCerts.ServerPair)
+	tlsContext.RequireClientCertificate = util_proto.Bool(true)
+
+	tlsContext.CommonTlsContext.ValidationContextType = &envoy_tls.CommonTlsContext_ValidationContext{
+		ValidationContext: &envoy_tls.CertificateValidationContext{
+			TrustedCa: &envoy_core.DataSource{
+				Specifier: &envoy_core.DataSource_InlineBytes{
+					InlineBytes: c.MTLSCerts.CaPEM,
+				},
+			},
+			MatchTypedSubjectAltNames: []*envoy_tls.SubjectAltNameMatcher{{
+				SanType: envoy_tls.SubjectAltNameMatcher_DNS,
+				Matcher: &envoy_type_matcher.StringMatcher{
+					MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+						Exact: envoy_admin_tls.ClientCertSAN,
+					},
+					IgnoreCase: false,
+				},
+			}},
+		},
+	}
+
+	pbst, err := util_proto.MarshalAnyDeterministic(tlsContext)
+	if err != nil {
+		return err
+	}
+	filterChain.TransportSocket = &envoy_core.TransportSocket{
+		Name: "envoy.transport_sockets.tls",
+		ConfigType: &envoy_core.TransportSocket_TypedConfig{
+			TypedConfig: pbst,
+		},
+	}
+	return nil
+}
diff --git a/pkg/xds/envoy/listeners/v3/server_static_tls.go b/pkg/xds/envoy/listeners/v3/server_static_tls.go
new file mode 100644
index 0000000..6f1585a
--- /dev/null
+++ b/pkg/xds/envoy/listeners/v3/server_static_tls.go
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 v3
+
+import (
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+	envoy_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type ServerSideStaticTLSConfigurer struct {
+	CertPath string
+	KeyPath  string
+}
+
+var _ FilterChainConfigurer = &ServerSideStaticTLSConfigurer{}
+
+func (c *ServerSideStaticTLSConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
+	tlsContext := envoy_tls.StaticDownstreamTlsContextWithPath(c.CertPath, c.KeyPath)
+
+	pbst, err := util_proto.MarshalAnyDeterministic(tlsContext)
+	if err != nil {
+		return err
+	}
+	filterChain.TransportSocket = &envoy_core.TransportSocket{
+		Name: "envoy.transport_sockets.tls",
+		ConfigType: &envoy_core.TransportSocket_TypedConfig{
+			TypedConfig: pbst,
+		},
+	}
+	return nil
+}
diff --git a/pkg/xds/envoy/secrets.go b/pkg/xds/envoy/secrets.go
new file mode 100644
index 0000000..0a6450f
--- /dev/null
+++ b/pkg/xds/envoy/secrets.go
@@ -0,0 +1,105 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You 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 envoy
+
+import (
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/envoy/names"
+	xds_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
+)
+
+type identityCertRequest struct {
+	meshName string
+}
+
+func (r identityCertRequest) Name() string {
+	return names.GetSecretName(xds_tls.IdentityCertResource, "secret", r.meshName)
+}
+
+type caRequest struct {
+	meshName string
+}
+
+type allInOneCaRequest struct {
+	meshNames []string
+}
+
+func (r caRequest) Name() string {
+	return names.GetSecretName(xds_tls.MeshCaResource, "secret", r.meshName)
+}
+
+func (r caRequest) MeshName() []string {
+	return []string{r.meshName}
+}
+
+func (r allInOneCaRequest) Name() string {
+	return names.GetSecretName(xds_tls.MeshCaResource, "secret", "all")
+}
+
+func (r allInOneCaRequest) MeshName() []string {
+	return r.meshNames
+}
+
+type secretsTracker struct {
+	ownMesh   string
+	allMeshes []string
+
+	identity bool
+	meshes   map[string]struct{}
+	allInOne bool
+}
+
+func NewSecretsTracker(ownMesh string, allMeshes []string) core_xds.SecretsTracker {
+	return &secretsTracker{
+		ownMesh:   ownMesh,
+		allMeshes: allMeshes,
+
+		meshes: map[string]struct{}{},
+	}
+}
+
+func (st *secretsTracker) RequestIdentityCert() core_xds.IdentityCertRequest {
+	st.identity = true
+	return &identityCertRequest{
+		meshName: st.ownMesh,
+	}
+}
+
+func (st *secretsTracker) RequestCa(mesh string) core_xds.CaRequest {
+	st.meshes[mesh] = struct{}{}
+	return &caRequest{
+		meshName: mesh,
+	}
+}
+
+func (st *secretsTracker) RequestAllInOneCa() core_xds.CaRequest {
+	st.allInOne = true
+	return &allInOneCaRequest{
+		meshNames: st.allMeshes,
+	}
+}
+
+func (st *secretsTracker) UsedIdentity() bool {
+	return st.identity
+}
+
+func (st *secretsTracker) UsedCas() map[string]struct{} {
+	return st.meshes
+}
+
+func (st *secretsTracker) UsedAllInOne() bool {
+	return st.allInOne
+}
diff --git a/pkg/xds/envoy/secrets/v3/ca_secret.go b/pkg/xds/envoy/secrets/v3/ca_secret.go
new file mode 100644
index 0000000..5ebaea6
--- /dev/null
+++ b/pkg/xds/envoy/secrets/v3/ca_secret.go
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 v3
+
+import (
+	"bytes"
+
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+func CreateCaSecret(secret *core_xds.CaSecret, name string) *envoy_auth.Secret {
+	return &envoy_auth.Secret{
+		Name: name,
+		Type: &envoy_auth.Secret_ValidationContext{
+			ValidationContext: &envoy_auth.CertificateValidationContext{
+				TrustedCa: &envoy_core.DataSource{
+					Specifier: &envoy_core.DataSource_InlineBytes{
+						InlineBytes: bytes.Join(secret.PemCerts, []byte("\n")),
+					},
+				},
+			},
+		},
+	}
+}
diff --git a/pkg/xds/envoy/secrets/v3/identity_secret.go b/pkg/xds/envoy/secrets/v3/identity_secret.go
new file mode 100644
index 0000000..30278c0
--- /dev/null
+++ b/pkg/xds/envoy/secrets/v3/identity_secret.go
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 v3
+
+import (
+	"bytes"
+
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+func CreateIdentitySecret(secret *core_xds.IdentitySecret, name string) *envoy_auth.Secret {
+	return &envoy_auth.Secret{
+		Name: name,
+		Type: &envoy_auth.Secret_TlsCertificate{
+			TlsCertificate: &envoy_auth.TlsCertificate{
+				CertificateChain: &envoy_core.DataSource{
+					Specifier: &envoy_core.DataSource_InlineBytes{
+						InlineBytes: bytes.Join(secret.PemCerts, []byte("\n")),
+					},
+				},
+				PrivateKey: &envoy_core.DataSource{
+					Specifier: &envoy_core.DataSource_InlineBytes{
+						InlineBytes: secret.PemKey,
+					},
+				},
+			},
+		},
+	}
+}
diff --git a/pkg/xds/envoy/secrets/v3/server_certificate.go b/pkg/xds/envoy/secrets/v3/server_certificate.go
new file mode 100644
index 0000000..5ba5864
--- /dev/null
+++ b/pkg/xds/envoy/secrets/v3/server_certificate.go
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 v3
+
+import (
+	"bytes"
+	"encoding/pem"
+	"io"
+
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+)
+
+// NewServerCertificateSecret populates a new Envoy TLS certificate
+// secret containing the given key and chain of certificates.
+func NewServerCertificateSecret(key *pem.Block, certificates []*pem.Block) *envoy_auth.Secret {
+	mustEncode := func(out io.Writer, b *pem.Block) {
+		if err := pem.Encode(out, b); err != nil {
+			panic(err.Error())
+		}
+	}
+
+	keyBytes := &bytes.Buffer{}
+	certificateBytes := &bytes.Buffer{}
+
+	mustEncode(keyBytes, key)
+
+	for _, c := range certificates {
+		mustEncode(certificateBytes, c)
+		certificateBytes.WriteString("\n")
+	}
+
+	return &envoy_auth.Secret{
+		Type: &envoy_auth.Secret_TlsCertificate{
+			TlsCertificate: &envoy_auth.TlsCertificate{
+				CertificateChain: &envoy_core.DataSource{
+					Specifier: &envoy_core.DataSource_InlineString{
+						InlineString: certificateBytes.String(),
+					},
+				},
+				PrivateKey: &envoy_core.DataSource{
+					Specifier: &envoy_core.DataSource_InlineString{
+						InlineString: keyBytes.String(),
+					},
+				},
+			},
+		},
+	}
+}
diff --git a/pkg/xds/envoy/tls/sni.go b/pkg/xds/envoy/tls/sni.go
new file mode 100644
index 0000000..7c304d6
--- /dev/null
+++ b/pkg/xds/envoy/tls/sni.go
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tls
+
+import (
+	"fmt"
+	"hash/fnv"
+	_ "hash/fnv"
+	"strings"
+
+	"github.com/pkg/errors"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	_ "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/util/maps"
+	_ "github.com/apache/dubbo-kubernetes/pkg/util/maps"
+	envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+)
+
+func SNIFromTags(tags envoy_tags.Tags) string {
+	extraTags := tags.WithoutTags(mesh_proto.ServiceTag).String()
+	service := tags[mesh_proto.ServiceTag]
+	if extraTags == "" {
+		return service
+	}
+	return fmt.Sprintf("%s{%s}", service, extraTags)
+}
+
+func TagsFromSNI(sni string) (envoy_tags.Tags, error) {
+	parts := strings.Split(sni, "{")
+	if len(parts) > 2 {
+		return nil, errors.New(fmt.Sprintf("cannot parse tags from sni: %s", sni))
+	}
+	if len(parts) == 1 {
+		return envoy_tags.Tags{mesh_proto.ServiceTag: parts[0]}, nil
+	}
+	cleanedTags := strings.ReplaceAll(parts[1], "}", "")
+	tags, err := envoy_tags.TagsFromString(cleanedTags)
+	if err != nil {
+		return nil, err
+	}
+	tags[mesh_proto.ServiceTag] = parts[0]
+	return tags, nil
+}
+
+const (
+	sniFormatVersion = "a"
+	dnsLabelLimit    = 63
+)
+
+func SNIForResource(resName string, meshName string, resType model.ResourceType, port uint32, additionalData map[string]string) string {
+	var mapStrings []string
+	for _, key := range maps.SortedKeys(additionalData) {
+		mapStrings = append(mapStrings, fmt.Sprintf("%s=%s", key, additionalData[key]))
+	}
+
+	hash := fnv.New64a()
+	_, _ = hash.Write([]byte(fmt.Sprintf("%s;%s;%v", resName, meshName, strings.Join(mapStrings, ",")))) // fnv64a does not return error
+	hashBytes := hash.Sum(nil)
+
+	if len(resName) > dnsLabelLimit-1 {
+		resName = resName[:dnsLabelLimit-1] + "x"
+	}
+	if len(meshName) > dnsLabelLimit-1 {
+		meshName = meshName[:dnsLabelLimit-1] + "x"
+	}
+
+	resTypeAbbrv := ""
+	//TODO: multizone service, external service, mesh service
+	switch resType {
+	case "ms":
+		resTypeAbbrv = "ms"
+	case "mes":
+		resTypeAbbrv = "mes"
+	case "mzms":
+		resTypeAbbrv = "mzms"
+	default:
+		panic("resource type not supported for SNI")
+	}
+
+	return fmt.Sprintf("%s%x.%s.%d.%s.%s", sniFormatVersion, hashBytes, resName, port, meshName, resTypeAbbrv)
+}
diff --git a/pkg/xds/envoy/tls/sni_test.go b/pkg/xds/envoy/tls/sni_test.go
new file mode 100644
index 0000000..7c45f40
--- /dev/null
+++ b/pkg/xds/envoy/tls/sni_test.go
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 tls_test
+
+import (
+	"fmt"
+
+	"github.com/asaskevich/govalidator"
+	"github.com/google/uuid"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	//meshmzservice_api "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/meshmultizoneservice/api/v1alpha1"
+	//meshservice_api "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/meshservice/api/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
+)
+
+var _ = Describe("SNI", func() {
+	It("should convert tags to SNI", func() {
+		// given
+		tags := map[string]string{
+			"dubbo.io/service": "backend",
+			"version":          "v1",
+			"env":              "prod",
+			"region":           "eu",
+			"app":              "backend-app",
+		}
+		expected := "backend{app=backend-app,env=prod,region=eu,version=v1}"
+
+		// when
+		actual := tls.SNIFromTags(tags)
+
+		// then
+		Expect(actual).To(Equal(expected))
+	})
+
+	It("should convert tags to SNI with only service name", func() {
+		// given
+		tags := map[string]string{
+			"dubbo.io/service": "backend",
+		}
+		expected := "backend"
+
+		// when
+		actual := tls.SNIFromTags(tags)
+
+		// then
+		Expect(actual).To(Equal(expected))
+	})
+
+	It("should convert SNI to tags", func() {
+		// given
+		sni := "backend{app=backend-app,env=prod,region=eu,version=v1}"
+		expectedTags := envoy_tags.Tags{
+			"dubbo.io/service": "backend",
+			"version":          "v1",
+			"env":              "prod",
+			"region":           "eu",
+			"app":              "backend-app",
+		}
+
+		// when
+		tags, err := tls.TagsFromSNI(sni)
+
+		// then
+		Expect(err).ToNot(HaveOccurred())
+		Expect(tags).To(Equal(expectedTags))
+	})
+
+	It("should convert SNI to tags with only service name", func() {
+		// given
+		sni := "backend"
+		expectedTags := envoy_tags.Tags{
+			"dubbo.io/service": "backend",
+		}
+
+		// when
+		tags, err := tls.TagsFromSNI(sni)
+
+		// then
+		Expect(err).ToNot(HaveOccurred())
+		Expect(tags).To(Equal(expectedTags))
+	})
+
+	DescribeTable("should fail when converting SNI to tags", func(sni string, errorMessage string) {
+		// when
+		_, err := tls.TagsFromSNI(sni)
+
+		// then
+		Expect(err).To(HaveOccurred())
+		Expect(err.Error()).To(Equal(errorMessage))
+	},
+		Entry("broken tags", "backend{", "invalid format of tags, pairs should be separated by , and key should be separated from value by ="),
+		Entry("to many separators", "backend{mesh=default{mesh", "cannot parse tags from sni: backend{mesh=default{mesh"),
+	)
+
+	type testCase struct {
+		resName        string
+		meshName       string
+		resType        model.ResourceType
+		port           uint32
+		additionalData map[string]string
+		expected       string
+	}
+	DescribeTable("should convert SNI for resource",
+		func(given testCase) {
+			sni := tls.SNIForResource(
+				given.resName,
+				given.meshName,
+				given.resType,
+				given.port,
+				given.additionalData,
+			)
+
+			Expect(sni).To(Equal(given.expected))
+			Expect(govalidator.IsDNSName(sni)).To(BeTrue())
+		},
+		Entry("simple", testCase{
+			resName:        "backend",
+			meshName:       "demo",
+			resType:        "ms",
+			port:           8080,
+			additionalData: nil,
+			expected:       "ae10a8071b8a8eeb8.backend.8080.demo.ms",
+		}),
+		Entry("simple subset", testCase{
+			resName:  "backend",
+			meshName: "demo",
+			resType:  "ms",
+			port:     8080,
+			additionalData: map[string]string{
+				"x": "a",
+			},
+			expected: "a333a125865a97632.backend.8080.demo.ms",
+		}),
+		Entry("going over limit", testCase{
+			resName:  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-qwe",
+			meshName: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-qwe",
+			resType:  "ms",
+			port:     8080,
+			additionalData: map[string]string{
+				"x": "a",
+			},
+			expected: "a5b91d8a08567bf09.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax.8080.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbx.ms",
+		}),
+		Entry("mesh multizone service", testCase{
+			resName:        "backend",
+			meshName:       "demo",
+			resType:        "mzms",
+			port:           8080,
+			additionalData: nil,
+			expected:       "ae10a8071b8a8eeb8.backend.8080.demo.mzms",
+		}),
+	)
+
+	It("SNI hash does not easily collide of the same services with different tags", func() {
+		snis := map[string]struct{}{}
+		for i := 0; i < 100_000; i++ {
+			sni := tls.SNIForResource("backend", "demo", "ms", 8080, map[string]string{
+				"version": fmt.Sprintf("%d", i),
+			})
+			_, ok := snis[sni]
+			Expect(ok).To(BeFalse())
+			snis[sni] = struct{}{}
+		}
+	})
+
+	It("SNI hash does not easily collide of the services with very long names", func() {
+		snis := map[string]struct{}{}
+		serviceName := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+		for i := 0; i < 100_000; i++ {
+			sni := tls.SNIForResource(serviceName+uuid.New().String(), "demo", "ms", 8080, nil)
+			_, ok := snis[sni]
+			Expect(ok).To(BeFalse())
+			snis[sni] = struct{}{}
+		}
+	})
+})
diff --git a/pkg/xds/envoy/tls/tls.go b/pkg/xds/envoy/tls/tls.go
new file mode 100644
index 0000000..80deb17
--- /dev/null
+++ b/pkg/xds/envoy/tls/tls.go
@@ -0,0 +1,43 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You 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 tls
+
+import (
+	"fmt"
+)
+
+const (
+	MeshCaResource       = "mesh_ca"
+	IdentityCertResource = "identity_cert"
+	CpValidationCtx      = "cp_validation_ctx"
+)
+
+// DubbboALPNProtocols are set for UpstreamTlsContext to show that mTLS is created by mesh.
+// On the inbound side we have to distinguish Dubbo mTLS and application TLS to properly
+// support PERMISSIVE mode
+var DubboALPNProtocols = []string{"dubbo"}
+
+func MeshSpiffeIDPrefix(mesh string) string {
+	return fmt.Sprintf("spiffe://%s/", mesh)
+}
+
+func ServiceSpiffeID(mesh string, service string) string {
+	return fmt.Sprintf("spiffe://%s/%s", mesh, service)
+}
+
+func DubboID(tagName, tagValue string) string {
+	return fmt.Sprintf("dubbo://%s/%s", tagName, tagValue)
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/xds/envoy/tls/tls_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/xds/envoy/tls/tls_suite_test.go
index 987f638..dc6bff8 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/xds/envoy/tls/tls_suite_test.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package tls_test
 
 import (
-	"archive/zip"
-	"bytes"
+	"testing"
+
+	"github.com/apache/dubbo-kubernetes/pkg/test"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func Test(t *testing.T) {
+	test.RunSpecs(t, "Envoy TLS Suite")
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/xds/envoy/tls/v3/tls.go b/pkg/xds/envoy/tls/v3/tls.go
new file mode 100644
index 0000000..bb19457
--- /dev/null
+++ b/pkg/xds/envoy/tls/v3/tls.go
@@ -0,0 +1,294 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You 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 v3
+
+import (
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+	envoy_type_matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
+
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	"github.com/apache/dubbo-kubernetes/pkg/tls"
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+	xds_tls "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
+)
+
+// CreateDownstreamTlsContext creates DownstreamTlsContext for incoming connections
+// It verifies that incoming connection has TLS certificate signed by Mesh CA with URI SAN of prefix spiffe://{mesh_name}/
+// It secures inbound listener with certificate of "identity_cert" that will be received from the SDS (it contains URI SANs of all inbounds).
+func CreateDownstreamTlsContext(downstreamMesh core_xds.CaRequest, mesh core_xds.IdentityCertRequest) (*envoy_tls.DownstreamTlsContext, error) {
+	var validationSANMatchers []*envoy_tls.SubjectAltNameMatcher
+	meshNames := downstreamMesh.MeshName()
+	for _, meshName := range meshNames {
+		validationSANMatchers = append(validationSANMatchers, MeshSpiffeIDPrefixMatcher(meshName))
+	}
+
+	commonTlsContext := createCommonTlsContext(mesh, downstreamMesh, validationSANMatchers)
+	return &envoy_tls.DownstreamTlsContext{
+		CommonTlsContext:         commonTlsContext,
+		RequireClientCertificate: util_proto.Bool(true),
+	}, nil
+}
+
+// CreateUpstreamTlsContext creates UpstreamTlsContext for outgoing connections
+// It verifies that the upstream server has TLS certificate signed by Mesh CA with URI SAN of spiffe://{mesh_name}/{upstream_service}
+// The downstream client exposes for the upstream server cert with multiple URI SANs, which means that if DP has inbound with services "web" and "web-api" and communicates with "backend"
+// the upstream server ("backend") will see that DP with TLS certificate of URIs of "web" and "web-api".
+// There is no way to correlate incoming request to "web" or "web-api" with outgoing request to "backend" to expose only one URI SAN.
+//
+// Pass "*" for upstreamService to validate that upstream service is a service that is part of the mesh (but not specific one)
+func CreateUpstreamTlsContext(mesh core_xds.IdentityCertRequest, upstreamMesh core_xds.CaRequest, upstreamService string, sni string, verifyIdentities []string) (*envoy_tls.UpstreamTlsContext, error) {
+	var validationSANMatchers []*envoy_tls.SubjectAltNameMatcher
+	meshNames := upstreamMesh.MeshName()
+	for _, meshName := range meshNames {
+		if upstreamService == "*" {
+			if len(verifyIdentities) == 0 {
+				validationSANMatchers = append(validationSANMatchers, MeshSpiffeIDPrefixMatcher(meshName))
+			}
+			for _, identity := range verifyIdentities {
+				stringMatcher := ServiceSpiffeIDMatcher(meshName, identity)
+				matcher := &envoy_tls.SubjectAltNameMatcher{
+					SanType: envoy_tls.SubjectAltNameMatcher_URI,
+					Matcher: stringMatcher,
+				}
+				validationSANMatchers = append(validationSANMatchers, matcher)
+			}
+		} else {
+			stringMatcher := ServiceSpiffeIDMatcher(meshName, upstreamService)
+			matcher := &envoy_tls.SubjectAltNameMatcher{
+				SanType: envoy_tls.SubjectAltNameMatcher_URI,
+				Matcher: stringMatcher,
+			}
+			validationSANMatchers = append(validationSANMatchers, matcher)
+		}
+	}
+	commonTlsContext := createCommonTlsContext(mesh, upstreamMesh, validationSANMatchers)
+	commonTlsContext.AlpnProtocols = xds_tls.DubboALPNProtocols
+	return &envoy_tls.UpstreamTlsContext{
+		CommonTlsContext: commonTlsContext,
+		Sni:              sni,
+	}, nil
+}
+
+func createCommonTlsContext(ownMesh core_xds.IdentityCertRequest, targetMeshCa core_xds.CaRequest, matchers []*envoy_tls.SubjectAltNameMatcher) *envoy_tls.CommonTlsContext {
+	meshCaSecret := NewSecretConfigSource(targetMeshCa.Name())
+	identitySecret := NewSecretConfigSource(ownMesh.Name())
+
+	return &envoy_tls.CommonTlsContext{
+		ValidationContextType: &envoy_tls.CommonTlsContext_CombinedValidationContext{
+			CombinedValidationContext: &envoy_tls.CommonTlsContext_CombinedCertificateValidationContext{
+				DefaultValidationContext: &envoy_tls.CertificateValidationContext{
+					MatchTypedSubjectAltNames: matchers,
+				},
+				ValidationContextSdsSecretConfig: meshCaSecret,
+			},
+		},
+		TlsCertificateSdsSecretConfigs: []*envoy_tls.SdsSecretConfig{
+			identitySecret,
+		},
+	}
+}
+
+func NewSecretConfigSource(secretName string) *envoy_tls.SdsSecretConfig {
+	return &envoy_tls.SdsSecretConfig{
+		Name: secretName,
+		SdsConfig: &envoy_core.ConfigSource{
+			ResourceApiVersion:    envoy_core.ApiVersion_V3,
+			ConfigSourceSpecifier: &envoy_core.ConfigSource_Ads{},
+		},
+	}
+}
+
+func UpstreamTlsContextOutsideMesh(systemCaPath string, ca, cert, key []byte, allowRenegotiation, skipHostnameVerification, fallbackToSystemCa bool, hostname, sni string, sans []core_xds.SAN, minTlsVersion, maxTlsVersion *core_xds.TlsVersion) (*envoy_tls.UpstreamTlsContext, error) {
+	tlsContext := &envoy_tls.UpstreamTlsContext{
+		AllowRenegotiation: allowRenegotiation,
+		Sni:                sni,
+	}
+	if cert != nil && key != nil {
+		tlsContext.CommonTlsContext = &envoy_tls.CommonTlsContext{
+			TlsCertificates: []*envoy_tls.TlsCertificate{
+				{
+					CertificateChain: dataSourceFromBytes(cert),
+					PrivateKey:       dataSourceFromBytes(key),
+				},
+			},
+		}
+	}
+
+	if ca != nil || (fallbackToSystemCa && systemCaPath != "") {
+		if tlsContext.CommonTlsContext == nil {
+			tlsContext.CommonTlsContext = &envoy_tls.CommonTlsContext{}
+		}
+		var matchNames []*envoy_tls.SubjectAltNameMatcher
+		if !skipHostnameVerification {
+			subjectAltNameMatch := hostname
+			if len(sni) > 0 {
+				subjectAltNameMatch = sni
+			}
+			for _, typ := range []envoy_tls.SubjectAltNameMatcher_SanType{
+				envoy_tls.SubjectAltNameMatcher_DNS,
+				envoy_tls.SubjectAltNameMatcher_IP_ADDRESS,
+			} {
+				matchNames = append(matchNames, &envoy_tls.SubjectAltNameMatcher{
+					SanType: typ,
+					Matcher: &envoy_type_matcher.StringMatcher{
+						MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+							Exact: subjectAltNameMatch,
+						},
+					},
+				})
+			}
+			for _, typ := range []envoy_tls.SubjectAltNameMatcher_SanType{
+				envoy_tls.SubjectAltNameMatcher_DNS,
+				envoy_tls.SubjectAltNameMatcher_IP_ADDRESS,
+			} {
+				for _, san := range sans {
+					matcher := &envoy_tls.SubjectAltNameMatcher{
+						SanType: typ,
+					}
+					switch san.MatchType {
+					case core_xds.SANMatchExact:
+						matcher.Matcher = &envoy_type_matcher.StringMatcher{
+							MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+								Exact: san.Value,
+							},
+						}
+					case core_xds.SANMatchPrefix:
+						matcher.Matcher = &envoy_type_matcher.StringMatcher{
+							MatchPattern: &envoy_type_matcher.StringMatcher_Prefix{
+								Prefix: san.Value,
+							},
+						}
+					}
+					matchNames = append(matchNames, matcher)
+				}
+			}
+		}
+
+		var trustedCa *envoy_core.DataSource
+		if ca == nil {
+			trustedCa = &envoy_core.DataSource{
+				Specifier: &envoy_core.DataSource_Filename{
+					Filename: systemCaPath,
+				},
+			}
+		} else {
+			trustedCa = dataSourceFromBytes(ca)
+		}
+
+		tlsContext.CommonTlsContext.ValidationContextType = &envoy_tls.CommonTlsContext_ValidationContext{
+			ValidationContext: &envoy_tls.CertificateValidationContext{
+				TrustedCa:                 trustedCa,
+				MatchTypedSubjectAltNames: matchNames,
+			},
+		}
+
+		if minTlsVersion != nil {
+			tlsContext.CommonTlsContext.TlsParams = &envoy_tls.TlsParameters{
+				TlsMinimumProtocolVersion: envoy_tls.TlsParameters_TlsProtocol(*minTlsVersion),
+			}
+		}
+		if maxTlsVersion != nil {
+			if tlsContext.CommonTlsContext.TlsParams == nil {
+				tlsContext.CommonTlsContext.TlsParams = &envoy_tls.TlsParameters{
+					TlsMaximumProtocolVersion: envoy_tls.TlsParameters_TlsProtocol(*maxTlsVersion),
+				}
+			} else {
+				tlsContext.CommonTlsContext.TlsParams.TlsMaximumProtocolVersion = envoy_tls.TlsParameters_TlsProtocol(*maxTlsVersion)
+			}
+		}
+	}
+	return tlsContext, nil
+}
+
+func dataSourceFromBytes(bytes []byte) *envoy_core.DataSource {
+	return &envoy_core.DataSource{
+		Specifier: &envoy_core.DataSource_InlineBytes{
+			InlineBytes: bytes,
+		},
+	}
+}
+
+func MeshSpiffeIDPrefixMatcher(mesh string) *envoy_tls.SubjectAltNameMatcher {
+	stringMatcher := &envoy_type_matcher.StringMatcher{
+		MatchPattern: &envoy_type_matcher.StringMatcher_Prefix{
+			Prefix: xds_tls.MeshSpiffeIDPrefix(mesh),
+		},
+	}
+
+	return &envoy_tls.SubjectAltNameMatcher{
+		SanType: envoy_tls.SubjectAltNameMatcher_URI,
+		Matcher: stringMatcher,
+	}
+}
+
+// TODO:RBAC Control
+func ServiceSpiffeIDMatcher(mesh string, service string) *envoy_type_matcher.StringMatcher {
+	return &envoy_type_matcher.StringMatcher{
+		MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+			Exact: xds_tls.ServiceSpiffeID(mesh, service),
+		},
+	}
+}
+
+func DubboIDMatcher(tagName, tagValue string) *envoy_type_matcher.StringMatcher {
+	return &envoy_type_matcher.StringMatcher{
+		MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+			Exact: xds_tls.DubboID(tagName, tagValue),
+		},
+	}
+}
+
+func StaticDownstreamTlsContextWithPath(certPath, keyPath string) *envoy_tls.DownstreamTlsContext {
+	cert := &envoy_core.DataSource{
+		Specifier: &envoy_core.DataSource_Filename{
+			Filename: certPath,
+		},
+	}
+	key := &envoy_core.DataSource{
+		Specifier: &envoy_core.DataSource_Filename{
+			Filename: keyPath,
+		},
+	}
+	return staticDownstreamTlsContext(cert, key)
+}
+
+func StaticDownstreamTlsContextWithValue(keyPair *tls.KeyPair) *envoy_tls.DownstreamTlsContext {
+	cert := &envoy_core.DataSource{
+		Specifier: &envoy_core.DataSource_InlineBytes{
+			InlineBytes: keyPair.CertPEM,
+		},
+	}
+	key := &envoy_core.DataSource{
+		Specifier: &envoy_core.DataSource_InlineBytes{
+			InlineBytes: keyPair.KeyPEM,
+		},
+	}
+	return staticDownstreamTlsContext(cert, key)
+}
+
+func staticDownstreamTlsContext(cert *envoy_core.DataSource, key *envoy_core.DataSource) *envoy_tls.DownstreamTlsContext {
+	return &envoy_tls.DownstreamTlsContext{
+		CommonTlsContext: &envoy_tls.CommonTlsContext{
+			TlsCertificates: []*envoy_tls.TlsCertificate{
+				{
+					CertificateChain: cert,
+					PrivateKey:       key,
+				},
+			},
+		},
+	}
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/xds/envoy/tls/v3/tls_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/xds/envoy/tls/v3/tls_suite_test.go
index 987f638..86c672e 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/xds/envoy/tls/v3/tls_suite_test.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package v3_test
 
 import (
-	"archive/zip"
-	"bytes"
+	"testing"
+
+	"github.com/apache/dubbo-kubernetes/pkg/test"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func TestTLS(t *testing.T) {
+	test.RunSpecs(t, "Envoy TLS v3 Suite")
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/xds/envoy/tls/v3/tls_test.go b/pkg/xds/envoy/tls/v3/tls_test.go
new file mode 100644
index 0000000..ce22b99
--- /dev/null
+++ b/pkg/xds/envoy/tls/v3/tls_test.go
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 v3_test
+
+import (
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	test_model "github.com/apache/dubbo-kubernetes/pkg/test/resources/model"
+	util_proto "github.com/apache/dubbo-kubernetes/pkg/util/proto"
+	v3 "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls/v3"
+)
+
+type caRequest struct {
+	mesh string
+}
+
+func (r *caRequest) MeshName() []string {
+	return []string{r.mesh}
+}
+
+func (r *caRequest) Name() string {
+	return "mesh_ca:secret:" + r.mesh
+}
+
+type identityRequest struct {
+	mesh string
+}
+
+func (r *identityRequest) Name() string {
+	return "identity_cert:secret:" + r.mesh
+}
+
+var _ = Describe("CreateDownstreamTlsContext()", func() {
+	Context("when mTLS is enabled on a given Mesh", func() {
+		type testCase struct {
+			expected string
+		}
+
+		DescribeTable("should generate proper Envoy config",
+			func(given testCase) {
+				// given
+				mesh := &core_mesh.MeshResource{
+					Meta: &test_model.ResourceMeta{
+						Name: "default",
+					},
+					Spec: &mesh_proto.Mesh{
+						Mtls: &mesh_proto.Mesh_Mtls{
+							EnabledBackend: "builtin",
+							Backends: []*mesh_proto.CertificateAuthorityBackend{
+								{
+									Name: "builtin",
+									Type: "builtin",
+								},
+							},
+						},
+					},
+				}
+
+				// when
+				snippet, err := v3.CreateDownstreamTlsContext(
+					&caRequest{mesh: mesh.GetMeta().GetName()},
+					&identityRequest{mesh: mesh.GetMeta().GetName()},
+				)
+				// then
+				Expect(err).ToNot(HaveOccurred())
+				// when
+				actual, err := util_proto.ToYAML(snippet)
+				// then
+				Expect(err).ToNot(HaveOccurred())
+				// and
+				Expect(actual).To(MatchYAML(given.expected))
+			},
+			Entry("metadata is `nil`", testCase{
+				expected: `
+                commonTlsContext:
+                  combinedValidationContext:
+                    defaultValidationContext:
+                      matchTypedSubjectAltNames:
+                      - matcher:
+                          prefix: spiffe://default/
+                        sanType: URI
+                    validationContextSdsSecretConfig:
+                      name: mesh_ca:secret:default
+                      sdsConfig:
+                        ads: {}
+                        resourceApiVersion: V3
+                  tlsCertificateSdsSecretConfigs:
+                  - name: identity_cert:secret:default
+                    sdsConfig:
+                      ads: {}
+                      resourceApiVersion: V3
+                requireClientCertificate: true`,
+			}),
+		)
+	})
+})
+
+var _ = Describe("CreateUpstreamTlsContext()", func() {
+	Context("when mTLS is enabled on a given Mesh", func() {
+		type testCase struct {
+			upstreamService string
+			expected        string
+		}
+
+		DescribeTable("should generate proper Envoy config",
+			func(given testCase) {
+				// given
+				mesh := "default"
+
+				// when
+				snippet, err := v3.CreateUpstreamTlsContext(
+					&identityRequest{mesh: mesh},
+					&caRequest{mesh: mesh},
+					given.upstreamService,
+					"",
+					nil,
+				)
+				// then
+				Expect(err).ToNot(HaveOccurred())
+				// when
+				actual, err := util_proto.ToYAML(snippet)
+				// then
+				Expect(err).ToNot(HaveOccurred())
+				// and
+				Expect(actual).To(MatchYAML(given.expected))
+			},
+			Entry("metadata is `nil`", testCase{
+				upstreamService: "backend",
+				expected: `
+                commonTlsContext:
+                  alpnProtocols:
+                  - dubbo
+                  combinedValidationContext:
+                    defaultValidationContext:
+                      matchTypedSubjectAltNames:
+                      - matcher:
+                          exact: spiffe://default/backend
+                        sanType: URI
+                    validationContextSdsSecretConfig:
+                      name: mesh_ca:secret:default
+                      sdsConfig:
+                        ads: {}
+                        resourceApiVersion: V3
+                  tlsCertificateSdsSecretConfigs:
+                  - name: identity_cert:secret:default
+                    sdsConfig:
+                      ads: {}
+                      resourceApiVersion: V3`,
+			}),
+		)
+	})
+})
diff --git a/pkg/xds/generator/admin_proxy_generator.go b/pkg/xds/generator/admin_proxy_generator.go
new file mode 100644
index 0000000..e05cc69
--- /dev/null
+++ b/pkg/xds/generator/admin_proxy_generator.go
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 generator
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/asaskevich/govalidator"
+	"github.com/pkg/errors"
+
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	util_maps "github.com/apache/dubbo-kubernetes/pkg/util/maps"
+	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+	envoy_common "github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
+	envoy_clusters "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/clusters"
+	envoy_listeners "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners"
+	envoy_names "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/names"
+)
+
+// OriginAdmin is a marker to indicate by which ProxyGenerator resources were generated.
+const OriginAdmin = "admin"
+
+var staticEndpointPaths = []*envoy_common.StaticEndpointPath{
+	{
+		Path:        "/ready",
+		RewritePath: "/ready",
+	},
+}
+
+var staticTlsEndpointPaths = []*envoy_common.StaticEndpointPath{
+	{
+		Path:        "/",
+		RewritePath: "/",
+	},
+}
+
+// AdminProxyGenerator generates resources to expose some endpoints of Admin API on public interface.
+// By default, Admin API is exposed only on loopback interface because of security reasons.
+type AdminProxyGenerator struct{}
+
+var adminAddressAllowedValues = map[string]struct{}{
+	"127.0.0.1": {},
+	"0.0.0.0":   {},
+	"::1":       {},
+	"::":        {},
+	"":          {},
+}
+
+func (g AdminProxyGenerator) Generator(ctx context.Context, _ *core_xds.ResourceSet, xdsCtx xds_context.Context, proxy *core_xds.Proxy) (*core_xds.ResourceSet, error) {
+	if proxy.Metadata.GetAdminPort() == 0 {
+		// It's not possible to export Admin endpoints if Envoy Admin API has not been enabled on that dataplane.
+		return nil, nil
+	}
+
+	adminPort := proxy.Metadata.GetAdminPort()
+	// We assume that Admin API must be available on a loopback interface (while users
+	// can override the default value `127.0.0.1` in the Bootstrap Server section of `dubbo-cp` config,
+	// the only reasonable alternatives are `::1`, `0.0.0.0` or `::`).
+	// In contrast to `AdminPort`, we shouldn't trust `AdminAddress` from the Envoy node metadata
+	// since it would allow a malicious user to manipulate that value and use Prometheus endpoint
+	// as a gateway to another host.
+	envoyAdminClusterName := envoy_names.GetEnvoyAdminClusterName()
+	adminAddress := proxy.Metadata.GetAdminAddress()
+	if _, ok := adminAddressAllowedValues[adminAddress]; !ok {
+		var allowedAddresses []string
+		for _, address := range util_maps.SortedKeys(adminAddressAllowedValues) {
+			allowedAddresses = append(allowedAddresses, fmt.Sprintf(`"%s"`, address))
+		}
+		return nil, errors.Errorf("envoy admin cluster is not allowed to have addresses other than %s", strings.Join(allowedAddresses, ", "))
+	}
+	switch adminAddress {
+	case "", "0.0.0.0":
+		adminAddress = "127.0.0.1"
+	case "::":
+		adminAddress = "::1"
+	}
+	cluster, err := envoy_clusters.NewClusterBuilder(proxy.APIVersion, envoyAdminClusterName).
+		Configure(envoy_clusters.ProvidedEndpointCluster(
+			govalidator.IsIPv6(adminAddress),
+			core_xds.Endpoint{Target: adminAddress, Port: adminPort})).
+		Configure(envoy_clusters.DefaultTimeout()).
+		Build()
+	if err != nil {
+		return nil, err
+	}
+
+	resources := core_xds.NewResourceSet()
+
+	for _, se := range staticEndpointPaths {
+		se.ClusterName = envoyAdminClusterName
+	}
+
+	// We bind admin to 127.0.0.1 by default, creating another listener with same address and port will result in error.
+	if g.getAddress(proxy) != adminAddress {
+		filterChains := []envoy_listeners.ListenerBuilderOpt{
+			envoy_listeners.FilterChain(envoy_listeners.NewFilterChainBuilder(proxy.APIVersion, envoy_common.AnonymousResource).
+				Configure(envoy_listeners.StaticEndpoints(envoy_names.GetAdminListenerName(), staticEndpointPaths)),
+			),
+		}
+		for _, se := range staticTlsEndpointPaths {
+			se.ClusterName = envoyAdminClusterName
+		}
+		filterChains = append(filterChains, envoy_listeners.FilterChain(envoy_listeners.NewFilterChainBuilder(proxy.APIVersion, envoy_common.AnonymousResource).
+			Configure(envoy_listeners.MatchTransportProtocol("tls")).
+			Configure(envoy_listeners.StaticEndpoints(envoy_names.GetAdminListenerName(), staticTlsEndpointPaths)).
+			Configure(envoy_listeners.ServerSideStaticMTLS(proxy.EnvoyAdminMTLSCerts)),
+		))
+
+		listener, err := envoy_listeners.NewInboundListenerBuilder(proxy.APIVersion, g.getAddress(proxy), adminPort, core_xds.SocketAddressProtocolTCP).
+			WithOverwriteName(envoy_names.GetAdminListenerName()).
+			Configure(envoy_listeners.TLSInspector()).
+			Configure(filterChains...).
+			Build()
+		if err != nil {
+			return nil, err
+		}
+		resources.Add(&core_xds.Resource{
+			Name:     listener.GetName(),
+			Origin:   OriginAdmin,
+			Resource: listener,
+		})
+	}
+
+	resources.Add(&core_xds.Resource{
+		Name:     cluster.GetName(),
+		Origin:   OriginAdmin,
+		Resource: cluster,
+	})
+	return resources, nil
+}
+
+func (g AdminProxyGenerator) getAddress(proxy *core_xds.Proxy) string {
+	if proxy.Dataplane != nil {
+		return proxy.Dataplane.Spec.GetNetworking().Address
+	}
+
+	//TODO: ZoneEgressProxy
+	//if proxy.ZoneEgressProxy != nil {
+	//	return proxy.ZoneEgressProxy.ZoneEgressResource.Spec.GetNetworking().GetAddress()
+	//}
+
+	if proxy.ZoneIngressProxy != nil {
+		return proxy.ZoneIngressProxy.ZoneIngressResource.Spec.GetNetworking().GetAddress()
+	}
+
+	return ""
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/xds/generator/generator_suite_test.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/xds/generator/generator_suite_test.go
index 987f638..0c9c068 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/xds/generator/generator_suite_test.go
@@ -15,25 +15,14 @@
  * limitations under the License.
  */
 
-package dubbo
+package generator_test
 
 import (
-	"archive/zip"
-	"bytes"
+	"testing"
+
+	"github.com/apache/dubbo-kubernetes/pkg/test"
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
-
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
-	}
-	return filesystem.NewZipFS(archive)
+func TestGenerator(t *testing.T) {
+	test.RunSpecs(t, "Generator Suite")
 }
-
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
diff --git a/pkg/xds/generator/inbound_proxy_generator.go b/pkg/xds/generator/inbound_proxy_generator.go
index e811f5f..f8b5938 100644
--- a/pkg/xds/generator/inbound_proxy_generator.go
+++ b/pkg/xds/generator/inbound_proxy_generator.go
@@ -19,22 +19,28 @@
 
 import (
 	"context"
-)
 
-import (
 	"github.com/pkg/errors"
-)
 
-import (
 	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
 	"github.com/apache/dubbo-kubernetes/pkg/core/validators"
+
 	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
 	"github.com/apache/dubbo-kubernetes/pkg/util/net"
+
 	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+
 	envoy_common "github.com/apache/dubbo-kubernetes/pkg/xds/envoy"
+
 	envoy_clusters "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/clusters"
+
 	envoy_listeners "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners"
+
 	envoy_names "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/names"
+
+	"github.com/apache/dubbo-kubernetes/api/common/v1alpha1/tls"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
 )
 
 const OriginInbound = "inbound"
@@ -92,7 +98,7 @@
 		// generate LDS resource
 		service := iface.GetService()
 		inboundListenerName := envoy_names.GetInboundListenerName(endpoint.DataplaneIP, endpoint.DataplanePort)
-		filterChainBuilder := func(serverSideMTLS bool) *envoy_listeners.FilterChainBuilder {
+		filterChainBuilder := func(serverSideMTLS bool, ciphers tls.TlsCiphers, tlsVersion *tls.Version, xdsCtx xds_context.Context, endpoint mesh_proto.InboundInterface, service string, protocol core_mesh.Protocol, proxy *core_xds.Proxy) *envoy_listeners.FilterChainBuilder {
 			filterChainBuilder := envoy_listeners.NewFilterChainBuilder(proxy.APIVersion, envoy_common.AnonymousResource)
 			switch protocol {
 			case core_mesh.ProtocolTriple:
@@ -121,13 +127,23 @@
 				// configuration for non-HTTP cases
 				filterChainBuilder.Configure(envoy_listeners.TcpProxyDeprecated(localClusterName, envoy_common.NewCluster(envoy_common.WithService(localClusterName))))
 			}
+			if serverSideMTLS {
+				filterChainBuilder.Configure(envoy_listeners.ServerSideMTLS(xdsCtx.Mesh.Resource, proxy.SecretsTracker, tlsVersion, ciphers))
+			}
 			return filterChainBuilder
 		}
 
 		listenerBuilder := envoy_listeners.NewInboundListenerBuilder(proxy.APIVersion, endpoint.DataplaneIP, endpoint.DataplanePort, core_xds.SocketAddressProtocolTCP).
 			Configure(envoy_listeners.TagsMetadata(iface.GetTags()))
 
-		listenerBuilder.Configure(envoy_listeners.FilterChain(filterChainBuilder(false)))
+		switch xdsCtx.Mesh.Resource.GetEnabledCertificateAuthorityBackend().GetMode() {
+		case mesh_proto.CertificateAuthorityBackend_STRICT:
+			//TODO: implement the logic of STRICT
+		case mesh_proto.CertificateAuthorityBackend_PERMISSIVE:
+			//TODO: implement the logic of PERMISSIVE
+		}
+
+		listenerBuilder.Configure(envoy_listeners.FilterChain(filterChainBuilder(false, nil, nil, xdsCtx, endpoint, service, protocol, proxy)))
 
 		inboundListener, err := listenerBuilder.Build()
 		if err != nil {
diff --git a/pkg/xds/generator/outbound_proxy_generator.go b/pkg/xds/generator/outbound_proxy_generator.go
index c01e8bb..849b3b5 100644
--- a/pkg/xds/generator/outbound_proxy_generator.go
+++ b/pkg/xds/generator/outbound_proxy_generator.go
@@ -20,13 +20,9 @@
 import (
 	"context"
 	"fmt"
-)
 
-import (
 	"github.com/pkg/errors"
-)
 
-import (
 	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
 	"github.com/apache/dubbo-kubernetes/pkg/core"
 	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
@@ -57,7 +53,7 @@
 	}
 
 	// TODO: implement the logic of tlsReadiness
-	tlsReadiness := make(map[string]bool)
+	tlsReadiness := xdsCtx.Mesh.GetTlsReadiness()
 	servicesAcc := envoy_common.NewServicesAccumulator(tlsReadiness)
 
 	outboundsMultipleIPs := buildOutboundsWithMultipleIPs(proxy.Dataplane, outbounds)
@@ -156,6 +152,7 @@
 	for _, serviceName := range services.Sorted() {
 		service := services[serviceName]
 		protocol := ctx.Mesh.GetServiceProtocol(serviceName)
+		tlsReady := service.TLSReady()
 
 		for _, c := range service.Clusters() {
 			cluster := c.(*envoy_common.ClusterImpl)
@@ -163,17 +160,20 @@
 			edsClusterBuilder := envoy_clusters.NewClusterBuilder(proxy.APIVersion, clusterName)
 
 			// clusterTags := []envoy_tags.Tags{cluster.Tags()}
+			clusterTags := []envoy_tags.Tags{cluster.Tags()}
 
 			if service.HasExternalService() {
 				if ctx.Mesh.Resource.ZoneEgressEnabled() {
 					edsClusterBuilder.
-						Configure(envoy_clusters.EdsCluster())
+						Configure(envoy_clusters.EdsCluster()).
+						Configure(envoy_clusters.ClientSideMTLS(proxy.SecretsTracker, ctx.Mesh.Resource, mesh_proto.ZoneEgressServiceName, tlsReady, clusterTags))
 				} else {
 					endpoints := proxy.Routing.ExternalServiceOutboundTargets[serviceName]
 					isIPv6 := proxy.Dataplane.IsIPv6()
 
 					edsClusterBuilder.
-						Configure(envoy_clusters.ProvidedEndpointCluster(isIPv6, endpoints...))
+						Configure(envoy_clusters.ProvidedEndpointCluster(isIPv6, endpoints...)).
+						Configure(envoy_clusters.ClientSideTLS(endpoints))
 				}
 
 				switch protocol {
@@ -187,6 +187,22 @@
 				edsClusterBuilder.
 					Configure(envoy_clusters.EdsCluster()).
 					Configure(envoy_clusters.Http2())
+				if upstreamMeshName := cluster.Mesh(); upstreamMeshName != "" {
+					for _, otherMesh := range ctx.Mesh.Resources.Meshes().Items {
+						if otherMesh.GetMeta().GetName() == upstreamMeshName {
+							edsClusterBuilder.Configure(envoy_clusters.CrossMeshClientSideMTLS(proxy.SecretsTracker, ctx.Mesh.Resource, otherMesh, serviceName, tlsReady, clusterTags))
+							break
+						}
+					}
+				} else {
+					edsClusterBuilder.Configure(envoy_clusters.ClientSideMTLS(
+						proxy.SecretsTracker,
+						ctx.Mesh.Resource,
+						serviceName,
+						tlsReady,
+						clusterTags,
+					))
+				}
 			}
 
 			edsCluster, err := edsClusterBuilder.Build()
diff --git a/pkg/xds/generator/proxy_template.go b/pkg/xds/generator/proxy_template.go
index 6bcebc7..1073dda 100644
--- a/pkg/xds/generator/proxy_template.go
+++ b/pkg/xds/generator/proxy_template.go
@@ -20,11 +20,10 @@
 import (
 	"context"
 	"fmt"
-)
 
-import (
 	model "github.com/apache/dubbo-kubernetes/pkg/core/xds"
 	"github.com/apache/dubbo-kubernetes/pkg/plugins/policies/core/generator"
+
 	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
 	"github.com/apache/dubbo-kubernetes/pkg/xds/generator/core"
 )
@@ -58,6 +57,7 @@
 
 func NewDefaultProxyProfile() core.ResourceGenerator {
 	return core.CompositeResourceGenerator{
+		AdminProxyGenerator{},
 		InboundProxyGenerator{},
 		OutboundProxyGenerator{},
 		generator.NewGenerator(),
diff --git a/pkg/xds/generator/secrets/generator.go b/pkg/xds/generator/secrets/generator.go
new file mode 100644
index 0000000..f028863
--- /dev/null
+++ b/pkg/xds/generator/secrets/generator.go
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 secrets
+
+import (
+	"context"
+
+	"github.com/pkg/errors"
+
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+	envoy_secrets "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/secrets/v3"
+	generator_core "github.com/apache/dubbo-kubernetes/pkg/xds/generator/core"
+)
+
+// OriginSecrets is a marker to indicate by which ProxyGenerator resources were generated.
+const OriginSecrets = "secrets"
+
+type Generator struct{}
+
+var _ generator_core.ResourceGenerator = Generator{}
+
+func createCaSecretResource(name string, ca *core_xds.CaSecret) *core_xds.Resource {
+	caSecret := envoy_secrets.CreateCaSecret(ca, name)
+	return &core_xds.Resource{
+		Name:     caSecret.Name,
+		Origin:   OriginSecrets,
+		Resource: caSecret,
+	}
+}
+
+func createIdentitySecretResource(name string, identity *core_xds.IdentitySecret) *core_xds.Resource {
+	identitySecret := envoy_secrets.CreateIdentitySecret(identity, name)
+	return &core_xds.Resource{
+		Name:     identitySecret.Name,
+		Origin:   OriginSecrets,
+		Resource: identitySecret,
+	}
+}
+
+// GenerateForZoneEgress generates whatever secrets were referenced in the
+// zone egress config generation.
+func (g Generator) GenerateForZoneEgress(
+	ctx context.Context,
+	xdsCtx xds_context.Context,
+	proxyId core_xds.ProxyId,
+	zoneEgressResource *core_mesh.ZoneEgressResource,
+	secretsTracker core_xds.SecretsTracker,
+	mesh *core_mesh.MeshResource,
+) (*core_xds.ResourceSet, error) {
+	resources := core_xds.NewResourceSet()
+
+	log := core.Log.WithName("secrets-generator").WithValues("proxyID", proxyId.String())
+
+	if !mesh.MTLSEnabled() {
+		return nil, nil
+	}
+
+	meshName := mesh.GetMeta().GetName()
+
+	usedIdentity := secretsTracker.UsedIdentity()
+
+	identity, ca, err := xdsCtx.ControlPlane.Secrets.GetForZoneEgress(ctx, zoneEgressResource, mesh)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to generate ZoneEgress secrets")
+	}
+
+	if usedIdentity {
+		log.V(1).Info("added identity", "mesh", meshName)
+		resources.Add(createIdentitySecretResource(secretsTracker.RequestIdentityCert().Name(), identity))
+	}
+
+	if _, ok := secretsTracker.UsedCas()[meshName]; ok {
+		log.V(1).Info("added mesh CA resources", "mesh", meshName)
+		resources.Add(createCaSecretResource(secretsTracker.RequestCa(meshName).Name(), ca))
+	}
+
+	return resources, nil
+}
+
+// Generate uses the SecretsTracker on Proxy and
+// generates whatever secrets were used in the config generation.
+func (g Generator) Generator(
+	ctx context.Context,
+	_ *core_xds.ResourceSet,
+	xdsCtx xds_context.Context,
+	proxy *core_xds.Proxy,
+) (*core_xds.ResourceSet, error) {
+	resources := core_xds.NewResourceSet()
+
+	log := core.Log.WithName("secrets-generator").WithValues("proxyID", proxy.Id.String())
+
+	if proxy.Dataplane != nil {
+		log = log.WithValues("mesh", xdsCtx.Mesh.Resource.GetMeta().GetName())
+	}
+
+	usedIdentity := proxy.SecretsTracker.UsedIdentity()
+	usedCas := proxy.SecretsTracker.UsedCas()
+	usedAllInOne := proxy.SecretsTracker.UsedAllInOne()
+
+	if usedAllInOne {
+		otherMeshes := xdsCtx.Mesh.Resources.OtherMeshes().Items
+		identity, allInOneCa, err := xdsCtx.ControlPlane.Secrets.GetAllInOne(ctx, xdsCtx.Mesh.Resource, proxy.Dataplane, otherMeshes)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to generate all in one CA")
+		}
+
+		resources.Add(createCaSecretResource(proxy.SecretsTracker.RequestAllInOneCa().Name(), allInOneCa))
+		resources.Add(createIdentitySecretResource(proxy.SecretsTracker.RequestIdentityCert().Name(), identity))
+		log.V(1).Info("added all in one CA resources")
+	}
+
+	if usedIdentity || len(usedCas) > 0 {
+		var otherMeshes []*core_mesh.MeshResource
+		for _, otherMesh := range xdsCtx.Mesh.Resources.OtherMeshes().Items {
+			if _, ok := usedCas[otherMesh.GetMeta().GetName()]; ok {
+				otherMeshes = append(otherMeshes, otherMesh)
+			}
+		}
+		identity, generatedMeshCAs, err := xdsCtx.ControlPlane.Secrets.GetForDataPlane(ctx, proxy.Dataplane, xdsCtx.Mesh.Resource, otherMeshes)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to generate dataplane identity cert and CAs")
+		}
+
+		resources.Add(createIdentitySecretResource(proxy.SecretsTracker.RequestIdentityCert().Name(), identity))
+
+		var addedCas []string
+		for mesh := range usedCas {
+			if ca, ok := generatedMeshCAs[mesh]; ok {
+				resources.Add(createCaSecretResource(proxy.SecretsTracker.RequestCa(mesh).Name(), ca))
+			} else {
+				// We need to add _something_ here so that Envoy syncs the
+				// config
+				emptyCa := &core_xds.CaSecret{}
+				resources.Add(createCaSecretResource(proxy.SecretsTracker.RequestCa(mesh).Name(), emptyCa))
+			}
+			addedCas = append(addedCas, mesh)
+		}
+		log.V(1).Info("added identity and mesh CAs resources", "cas", addedCas)
+	}
+
+	return resources, nil
+}
diff --git a/pkg/xds/generator/zoneproxy/generator.go b/pkg/xds/generator/zoneproxy/generator.go
index 1187456..dd9d8f3 100644
--- a/pkg/xds/generator/zoneproxy/generator.go
+++ b/pkg/xds/generator/zoneproxy/generator.go
@@ -26,6 +26,7 @@
 	envoy_listeners "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/listeners"
 	envoy_names "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/names"
 	envoy_tags "github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tags"
+	"github.com/apache/dubbo-kubernetes/pkg/xds/envoy/tls"
 )
 
 func GenerateCDS(
@@ -109,6 +110,8 @@
 ) envoy_common.Services {
 	servicesAcc := envoy_common.NewServicesAccumulator(nil)
 
+	sniUsed := map[string]struct{}{}
+
 	for _, service := range availableServices {
 		serviceName := service.Tags[mesh_proto.ServiceTag]
 		destinations := destinationsPerService[serviceName]
@@ -117,6 +120,14 @@
 		serviceEndpoints := endpointMap[serviceName]
 
 		for _, destination := range destinations {
+			sni := tls.SNIFromTags(destination.
+				WithTags(mesh_proto.ServiceTag, serviceName).
+				WithTags("mesh", service.Mesh),
+			)
+			if _, ok := sniUsed[sni]; ok {
+				continue
+			}
+			sniUsed[sni] = struct{}{}
 
 			// relevantTags is a set of tags for which it actually makes sense to do LB split on.
 			// If the endpoint list is the same with or without the tag, we should just not do the split.
@@ -155,6 +166,8 @@
 
 			filterChain := envoy_listeners.FilterChain(
 				envoy_listeners.NewFilterChainBuilder(apiVersion, envoy_common.AnonymousResource).Configure(
+					envoy_listeners.MatchTransportProtocol("tls"),
+					envoy_listeners.MatchServerNames(sni),
 					envoy_listeners.TcpProxyDeprecatedWithMetadata(
 						clusterName,
 						cluster,
diff --git a/pkg/xds/secrets/ca_provider.go b/pkg/xds/secrets/ca_provider.go
new file mode 100644
index 0000000..1d6c68f
--- /dev/null
+++ b/pkg/xds/secrets/ca_provider.go
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 secrets
+
+import (
+	"context"
+	"time"
+
+	"github.com/pkg/errors"
+	"github.com/prometheus/client_golang/prometheus"
+
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+type CaProvider interface {
+	// Get returns all PEM encoded CAs, a list of CAs that were used to generate a secret and an error.
+	Get(context.Context, *core_mesh.MeshResource) (*core_xds.CaSecret, []string, error)
+}
+
+func NewCaProvider(caManagers core_ca.Managers) (CaProvider, error) {
+	return &meshCaProvider{
+		caManagers: caManagers,
+	}, nil
+}
+
+type meshCaProvider struct {
+	caManagers     core_ca.Managers
+	latencyMetrics *prometheus.SummaryVec
+}
+
+// Get retrieves the root CA for a given backend with a default timeout of 10
+// seconds.
+func (s *meshCaProvider) Get(ctx context.Context, mesh *core_mesh.MeshResource) (*core_xds.CaSecret, []string, error) {
+	backend := mesh.GetEnabledCertificateAuthorityBackend()
+	if backend == nil {
+		return nil, nil, errors.New("CA backend is nil")
+	}
+
+	//TODO:对用户自定义超时时间支持
+	timeout := 10 * time.Second
+	ctx, cancel := context.WithTimeout(ctx, timeout)
+	defer cancel()
+
+	caManager, exist := s.caManagers[backend.Type]
+	if !exist {
+		return nil, nil, errors.Errorf("CA manager of type %s not exist", backend.Type)
+	}
+
+	var certs [][]byte
+	var err error
+	func() {
+		start := time.Now()
+		defer func() {
+			s.latencyMetrics.WithLabelValues(backend.GetName()).Observe(float64(time.Since(start).Milliseconds()))
+		}()
+		certs, err = caManager.GetRootCert(ctx, mesh.GetMeta().GetName(), backend)
+	}()
+	if err != nil {
+		return nil, nil, errors.Wrap(err, "could not get root certs")
+	}
+
+	return &core_xds.CaSecret{
+		PemCerts: certs,
+	}, []string{backend.Name}, nil
+}
diff --git a/pkg/xds/secrets/identity_provider.go b/pkg/xds/secrets/identity_provider.go
new file mode 100644
index 0000000..522fbc6
--- /dev/null
+++ b/pkg/xds/secrets/identity_provider.go
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 secrets
+
+import (
+	"context"
+	"time"
+
+	"github.com/pkg/errors"
+	"github.com/prometheus/client_golang/prometheus"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	core_ca "github.com/apache/dubbo-kubernetes/pkg/core/ca"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+type Identity struct {
+	Mesh     string
+	Name     string
+	Services mesh_proto.MultiValueTagSet
+}
+
+type IdentityProvider interface {
+	// Get returns PEM encoded cert + key, backend that was used to generate this pair and an error.
+	Get(context.Context, Identity, *core_mesh.MeshResource) (*core_xds.IdentitySecret, string, error)
+}
+
+type identityCertProvider struct {
+	caManagers     core_ca.Managers
+	latencyMetrics *prometheus.SummaryVec
+}
+
+func NewIdentityProvider(caManagers core_ca.Managers) (IdentityProvider, error) {
+	return &identityCertProvider{
+		caManagers: caManagers,
+	}, nil
+}
+
+func (s *identityCertProvider) Get(ctx context.Context, requestor Identity, mesh *core_mesh.MeshResource) (*core_xds.IdentitySecret, string, error) {
+	backend := mesh.GetEnabledCertificateAuthorityBackend()
+	if backend == nil {
+		return nil, "", errors.Errorf("CA default backend in mesh %q has to be defined", mesh.GetMeta().GetName())
+	}
+
+	timeout := backend.GetDpCert().GetRequestTimeout()
+	if timeout != nil {
+		var cancel context.CancelFunc
+		ctx, cancel = context.WithTimeout(ctx, timeout.AsDuration())
+		defer cancel()
+	}
+
+	caManager, exist := s.caManagers[backend.Type]
+	if !exist {
+		return nil, "", errors.Errorf("CA manager of type %s not exist", backend.Type)
+	}
+
+	var pair core_ca.KeyPair
+	var err error
+	func() {
+		start := time.Now()
+		defer func() {
+			s.latencyMetrics.WithLabelValues(backend.GetName()).Observe(float64(time.Since(start).Milliseconds()))
+		}()
+		pair, err = caManager.GenerateDataplaneCert(ctx, mesh.GetMeta().GetName(), backend, requestor.Services)
+	}()
+
+	if err != nil {
+		return nil, "", errors.Wrapf(err, "could not generate dataplane cert for mesh: %q backend: %q services: %q", mesh.GetMeta().GetName(), backend.Name, requestor.Services)
+	}
+
+	return &core_xds.IdentitySecret{
+		PemCerts: [][]byte{pair.CertPEM},
+		PemKey:   pair.KeyPEM,
+	}, backend.Name, nil
+}
diff --git a/dubboctl/internal/dubbo/templates_embedded.go b/pkg/xds/secrets/kind.go
similarity index 60%
copy from dubboctl/internal/dubbo/templates_embedded.go
copy to pkg/xds/secrets/kind.go
index 987f638..70342e9 100644
--- a/dubboctl/internal/dubbo/templates_embedded.go
+++ b/pkg/xds/secrets/kind.go
@@ -15,25 +15,31 @@
  * limitations under the License.
  */
 
-package dubbo
+package secrets
 
-import (
-	"archive/zip"
-	"bytes"
+type ChangeKind int
+
+const (
+	IdentityChange ChangeKind = iota
+	OwnMeshChange
+	OtherMeshChange
 )
 
-import (
-	"github.com/apache/dubbo-kubernetes/dubboctl/generated"
-	"github.com/apache/dubbo-kubernetes/dubboctl/internal/filesystem"
-)
+type UpdateKinds map[ChangeKind]struct{}
 
-//go:generate go run ../../generated/templates/generate.go
-func newEmbeddedTemplatesFS() filesystem.Filesystem {
-	archive, err := zip.NewReader(bytes.NewReader(generated.TemplatesZip), int64(len(generated.TemplatesZip)))
-	if err != nil {
-		panic(err)
+func UpdateEverything() UpdateKinds {
+	return map[ChangeKind]struct{}{
+		IdentityChange:  {},
+		OwnMeshChange:   {},
+		OtherMeshChange: {},
 	}
-	return filesystem.NewZipFS(archive)
 }
 
-var EmbeddedTemplatesFS = newEmbeddedTemplatesFS()
+func (kinds UpdateKinds) HasType(kind ChangeKind) bool {
+	_, ok := kinds[kind]
+	return ok
+}
+
+func (kinds UpdateKinds) AddKind(kind ChangeKind) {
+	kinds[kind] = struct{}{}
+}
diff --git a/pkg/xds/secrets/secrets.go b/pkg/xds/secrets/secrets.go
new file mode 100644
index 0000000..df15766
--- /dev/null
+++ b/pkg/xds/secrets/secrets.go
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 secrets
+
+import (
+	"context"
+	"crypto/x509"
+	"encoding/pem"
+	"fmt"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/pkg/errors"
+	"github.com/prometheus/client_golang/prometheus"
+	"google.golang.org/protobuf/proto"
+
+	mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1"
+	"github.com/apache/dubbo-kubernetes/pkg/core"
+	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
+	"github.com/apache/dubbo-kubernetes/pkg/core/user"
+	core_xds "github.com/apache/dubbo-kubernetes/pkg/core/xds"
+)
+
+var log = core.Log.WithName("xds").WithName("secrets")
+
+type MeshCa struct {
+	Mesh     string
+	CaSecret *core_xds.CaSecret
+}
+
+type Secrets interface {
+	GetForDataPlane(ctx context.Context, dataplane *core_mesh.DataplaneResource, mesh *core_mesh.MeshResource, otherMeshes []*core_mesh.MeshResource) (*core_xds.IdentitySecret, map[string]*core_xds.CaSecret, error)
+	GetForZoneEgress(ctx context.Context, zoneEgress *core_mesh.ZoneEgressResource, mesh *core_mesh.MeshResource) (*core_xds.IdentitySecret, *core_xds.CaSecret, error)
+	GetAllInOne(ctx context.Context, mesh *core_mesh.MeshResource, dataplane *core_mesh.DataplaneResource, otherMeshes []*core_mesh.MeshResource) (*core_xds.IdentitySecret, *core_xds.CaSecret, error)
+	Info(mesh_proto.ProxyType, model.ResourceKey) *Info
+	Cleanup(mesh_proto.ProxyType, model.ResourceKey)
+}
+
+type MeshInfo struct {
+	MTLS *mesh_proto.Mesh_Mtls
+}
+
+type Info struct {
+	Expiration time.Time
+	Generation time.Time
+
+	Tags mesh_proto.MultiValueTagSet
+
+	IssuedBackend     string
+	SupportedBackends []string
+
+	OwnMesh        MeshInfo
+	OtherMeshInfos []MeshInfo
+	// this marks our info as having failed last time to get the mesh CAs that
+	// we wanted and so we should retry next time we want certs.
+	failedOtherMeshes bool
+}
+
+func (c *Info) CertLifetime() time.Duration {
+	return c.Expiration.Sub(c.Generation)
+}
+
+func (c *Info) ExpiringSoon() bool {
+	return core.Now().After(c.Generation.Add(c.CertLifetime() / 5 * 4))
+}
+
+func NewSecrets(caProvider CaProvider, identityProvider IdentityProvider) (Secrets, error) {
+
+	return &secrets{
+		caProvider:       caProvider,
+		identityProvider: identityProvider,
+		cachedCerts:      map[certCacheKey]*certs{},
+	}, nil
+}
+
+type certCacheKey struct {
+	resource  model.ResourceKey
+	proxyType mesh_proto.ProxyType
+}
+
+type secrets struct {
+	caProvider       CaProvider
+	identityProvider IdentityProvider
+
+	sync.RWMutex
+	cachedCerts           map[certCacheKey]*certs
+	certGenerationsMetric *prometheus.CounterVec
+}
+
+var _ Secrets = &secrets{}
+
+func (s *secrets) Info(proxyType mesh_proto.ProxyType, dpKey model.ResourceKey) *Info {
+	certs := s.certs(proxyType, dpKey)
+	if certs == nil {
+		return nil
+	}
+	return certs.info
+}
+
+type certs struct {
+	identity   *core_xds.IdentitySecret
+	ownCa      MeshCa
+	otherCas   []MeshCa
+	allInOneCa MeshCa
+	info       *Info
+}
+
+func (c *certs) Info() *Info {
+	if c == nil {
+		return nil
+	}
+	return c.info
+}
+
+func (s *secrets) certs(proxyType mesh_proto.ProxyType, dpKey model.ResourceKey) *certs {
+	s.RLock()
+	defer s.RUnlock()
+
+	return s.cachedCerts[certCacheKey{proxyType: proxyType, resource: dpKey}]
+}
+
+func (s *secrets) GetForDataPlane(
+	ctx context.Context,
+	dataplane *core_mesh.DataplaneResource,
+	mesh *core_mesh.MeshResource,
+	otherMeshes []*core_mesh.MeshResource,
+) (*core_xds.IdentitySecret, map[string]*core_xds.CaSecret, error) {
+	identity, cas, _, err := s.get(ctx, mesh_proto.DataplaneProxyType, dataplane, dataplane.Spec.TagSet(), mesh, otherMeshes)
+	return identity, cas, err
+}
+
+func (s *secrets) GetAllInOne(
+	ctx context.Context,
+	mesh *core_mesh.MeshResource,
+	dataplane *core_mesh.DataplaneResource,
+	otherMeshes []*core_mesh.MeshResource,
+) (*core_xds.IdentitySecret, *core_xds.CaSecret, error) {
+	identity, _, allInOne, err := s.get(ctx, mesh_proto.DataplaneProxyType, dataplane, dataplane.Spec.TagSet(), mesh, otherMeshes)
+	return identity, allInOne.CaSecret, err
+}
+
+func (s *secrets) GetForZoneEgress(
+	ctx context.Context,
+	zoneEgress *core_mesh.ZoneEgressResource,
+	mesh *core_mesh.MeshResource,
+) (*core_xds.IdentitySecret, *core_xds.CaSecret, error) {
+	tags := mesh_proto.MultiValueTagSetFrom(map[string][]string{
+		mesh_proto.ServiceTag: {
+			mesh_proto.ZoneEgressServiceName,
+		},
+	})
+
+	identity, cas, _, err := s.get(ctx, mesh_proto.EgressProxyType, zoneEgress, tags, mesh, nil)
+	return identity, cas[mesh.GetMeta().GetName()], err
+}
+
+func (s *secrets) get(
+	ctx context.Context,
+	proxyType mesh_proto.ProxyType,
+	resource model.Resource,
+	tags mesh_proto.MultiValueTagSet,
+	mesh *core_mesh.MeshResource,
+	otherMeshes []*core_mesh.MeshResource,
+) (*core_xds.IdentitySecret, map[string]*core_xds.CaSecret, MeshCa, error) {
+	if !mesh.MTLSEnabled() {
+		return nil, nil, MeshCa{}, nil
+	}
+
+	meshName := mesh.GetMeta().GetName()
+
+	resourceKey := model.MetaToResourceKey(resource.GetMeta())
+	resourceKey.Mesh = meshName
+	certs := s.certs(proxyType, resourceKey)
+
+	if updateKinds, debugReason := s.shouldGenerateCerts(
+		certs.Info(),
+		tags,
+		mesh,
+		otherMeshes,
+	); len(updateKinds) > 0 {
+		log.Info(
+			"generating certificate",
+			string(resource.Descriptor().Name), resourceKey, "reason", debugReason,
+		)
+
+		certs, err := s.generateCerts(ctx, tags, mesh, otherMeshes, certs, updateKinds)
+		if err != nil {
+			return nil, nil, MeshCa{}, errors.Wrap(err, "could not generate certificates")
+		}
+
+		key := certCacheKey{
+			resource:  resourceKey,
+			proxyType: proxyType,
+		}
+		s.Lock()
+		s.cachedCerts[key] = certs
+		s.Unlock()
+
+		caMap := map[string]*core_xds.CaSecret{
+			meshName: certs.ownCa.CaSecret,
+		}
+		for _, otherCa := range certs.otherCas {
+			caMap[otherCa.Mesh] = otherCa.CaSecret
+		}
+		return certs.identity, caMap, certs.allInOneCa, nil
+	}
+
+	if certs == nil { // previous "if" should guarantee that the certs are always there
+		return nil, nil, MeshCa{}, errors.New("certificates were not generated")
+	}
+
+	caMap := map[string]*core_xds.CaSecret{
+		meshName: certs.ownCa.CaSecret,
+	}
+	for _, otherCa := range certs.otherCas {
+		caMap[otherCa.Mesh] = otherCa.CaSecret
+	}
+
+	return certs.identity, caMap, certs.allInOneCa, nil
+}
+
+func (s *secrets) Cleanup(proxyType mesh_proto.ProxyType, dpKey model.ResourceKey) {
+	key := certCacheKey{
+		resource:  dpKey,
+		proxyType: proxyType,
+	}
+	s.Lock()
+	delete(s.cachedCerts, key)
+	s.Unlock()
+}
+
+func (s *secrets) shouldGenerateCerts(info *Info, tags mesh_proto.MultiValueTagSet, ownMesh *core_mesh.MeshResource, otherMeshInfos []*core_mesh.MeshResource) (UpdateKinds, string) {
+	if info == nil {
+		return UpdateEverything(), "mTLS is enabled and DP hasn't received a certificate yet"
+	}
+
+	var reason string
+	updates := UpdateKinds{}
+
+	if !proto.Equal(info.OwnMesh.MTLS, ownMesh.Spec.Mtls) {
+		updates.AddKind(OwnMeshChange)
+		reason = "Mesh mTLS settings have changed"
+	}
+
+	if len(info.OtherMeshInfos) != len(otherMeshInfos) || info.failedOtherMeshes {
+		updates.AddKind(OtherMeshChange)
+		reason = "Another mesh has been added or removed or we must retry"
+	} else {
+		for i, mesh := range info.OtherMeshInfos {
+			if !proto.Equal(mesh.MTLS, otherMeshInfos[i].Spec.Mtls) {
+				updates.AddKind(OtherMeshChange)
+				reason = "Another Mesh's mTLS settings have changed"
+				break
+			}
+		}
+	}
+
+	if tags.String() != info.Tags.String() {
+		updates.AddKind(IdentityChange)
+		reason = "DP tags have changed"
+	}
+
+	if info.ExpiringSoon() {
+		updates.AddKind(IdentityChange)
+		reason = fmt.Sprintf("the certificate expiring soon. Generated at %q, expiring at %q", info.Generation, info.Expiration)
+	}
+
+	return updates, reason
+}
+
+func (s *secrets) generateCerts(
+	ctx context.Context,
+	tags mesh_proto.MultiValueTagSet,
+	mesh *core_mesh.MeshResource,
+	otherMeshes []*core_mesh.MeshResource,
+	oldCerts *certs,
+	updateKinds UpdateKinds,
+) (*certs, error) {
+	ctx = user.Ctx(ctx, user.ControlPlane)
+	var identity *core_xds.IdentitySecret
+	var ownCa MeshCa
+	var otherCas []MeshCa
+	var allInOneCa MeshCa
+	info := &Info{}
+
+	if oldCerts != nil {
+		identity = oldCerts.identity
+		ownCa = oldCerts.ownCa
+		otherCas = oldCerts.otherCas
+		allInOneCa = oldCerts.allInOneCa
+		info = oldCerts.Info()
+	}
+
+	meshName := mesh.GetMeta().GetName()
+
+	if updateKinds.HasType(IdentityChange) || updateKinds.HasType(OwnMeshChange) {
+		requester := Identity{
+			Services: tags,
+			Mesh:     meshName,
+		}
+
+		identitySecret, issuedBackend, err := s.identityProvider.Get(ctx, requester, mesh)
+		if err != nil {
+			return nil, errors.Wrap(err, "could not get Dataplane cert pair")
+		}
+
+		s.certGenerationsMetric.WithLabelValues(requester.Mesh).Inc()
+
+		block, _ := pem.Decode(identitySecret.PemCerts[0])
+		cert, err := x509.ParseCertificate(block.Bytes)
+		if err != nil {
+			return nil, errors.Wrap(err, "could not extract info about certificate")
+		}
+
+		info.Tags = tags
+		info.IssuedBackend = issuedBackend
+		info.Expiration = cert.NotAfter
+		info.Generation = core.Now()
+		identity = identitySecret
+	}
+
+	if updateKinds.HasType(OwnMeshChange) {
+		caSecret, supportedBackends, err := s.caProvider.Get(ctx, mesh)
+		if err != nil {
+			return nil, errors.Wrap(err, "could not get mesh CA cert")
+		}
+
+		ownCa = MeshCa{
+			Mesh:     meshName,
+			CaSecret: caSecret,
+		}
+		info.SupportedBackends = supportedBackends
+		info.OwnMesh = MeshInfo{
+			MTLS: mesh.Spec.Mtls,
+		}
+	}
+
+	if updateKinds.HasType(OtherMeshChange) || updateKinds.HasType(OwnMeshChange) {
+		var otherMeshInfos []MeshInfo
+		var bytes [][]byte
+		var names []string
+		otherCas = []MeshCa{}
+
+		failedOtherMeshes := false
+		for _, otherMesh := range otherMeshes {
+			otherMeshInfos = append(otherMeshInfos, MeshInfo{
+				MTLS: otherMesh.Spec.GetMtls(),
+			})
+
+			// We need to track this mesh but we don't do anything with certs
+			if !otherMesh.MTLSEnabled() {
+				continue
+			}
+
+			otherCa, _, err := s.caProvider.Get(ctx, otherMesh)
+			if err != nil {
+				failedOtherMeshes = true
+				// The other CA is misconfigured but this can not affect
+				// generation in this mesh.
+				log.Error(err, "could not get other mesh CA cert")
+				continue
+			}
+
+			meshName := otherMesh.GetMeta().GetName()
+
+			otherCas = append(otherCas, MeshCa{
+				Mesh:     meshName,
+				CaSecret: otherCa,
+			})
+
+			names = append(names, meshName)
+			bytes = append(bytes, otherCa.PemCerts...)
+		}
+
+		names = append(names, meshName)
+		bytes = append(bytes, ownCa.CaSecret.PemCerts...)
+
+		sort.Strings(names)
+		allInOneCa = MeshCa{
+			Mesh: strings.Join(names, ":"),
+			CaSecret: &core_xds.CaSecret{
+				PemCerts: bytes,
+			},
+		}
+
+		info.failedOtherMeshes = failedOtherMeshes
+		info.OtherMeshInfos = otherMeshInfos
+	}
+
+	return &certs{
+		identity:   identity,
+		ownCa:      ownCa,
+		otherCas:   otherCas,
+		allInOneCa: allInOneCa,
+		info:       info,
+	}, nil
+}
diff --git a/pkg/xds/server/components.go b/pkg/xds/server/components.go
index 87926f5..ed5a22f 100644
--- a/pkg/xds/server/components.go
+++ b/pkg/xds/server/components.go
@@ -18,17 +18,21 @@
 package server
 
 import (
+	"github.com/apache/dubbo-kubernetes/pkg/xds/secrets"
 	"github.com/pkg/errors"
-)
 
-import (
 	core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh"
+
 	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
 	"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
+
 	core_runtime "github.com/apache/dubbo-kubernetes/pkg/core/runtime"
+
 	util_xds "github.com/apache/dubbo-kubernetes/pkg/util/xds"
 	"github.com/apache/dubbo-kubernetes/pkg/xds/cache/cla"
+
 	xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
+
 	v3 "github.com/apache/dubbo-kubernetes/pkg/xds/server/v3"
 )
 
@@ -49,9 +53,20 @@
 		return err
 	}
 
+	idProvider, err := secrets.NewIdentityProvider(rt.CaManagers())
+	if err != nil {
+		return err
+	}
+
+	secrets, err := secrets.NewSecrets(
+		rt.CAProvider(),
+		idProvider,
+	)
+
 	envoyCpCtx := &xds_context.ControlPlaneContext{
 		CLACache: claCache,
 		Zone:     "",
+		Secrets:  secrets,
 	}
 	if err := v3.RegisterXDS(statsCallbacks, envoyCpCtx, rt); err != nil {
 		return errors.Wrap(err, "could not register V3 XDS")
diff --git a/pkg/xds/sync/dataplane_watchdog.go b/pkg/xds/sync/dataplane_watchdog.go
index 72d239f..9604ac5 100644
--- a/pkg/xds/sync/dataplane_watchdog.go
+++ b/pkg/xds/sync/dataplane_watchdog.go
@@ -19,6 +19,8 @@
 
 import (
 	"context"
+	envoy_admin_tls "github.com/apache/dubbo-kubernetes/pkg/envoy/admin/tls"
+	util_tls "github.com/apache/dubbo-kubernetes/pkg/tls"
 )
 
 import (
@@ -71,6 +73,7 @@
 	dpType           mesh_proto.ProxyType
 	proxyTypeSettled bool
 	dpAddress        string
+	envoyAdminMTLS   *core_xds.ServerSideMTLSCerts
 }
 
 func NewDataplaneWatchdog(deps DataplaneWatchdogDependencies, dpKey core_model.ResourceKey) *DataplaneWatchdog {
@@ -130,7 +133,15 @@
 		ProxyType: mesh_proto.IngressProxyType,
 	}
 	syncForConfig := aggregatedMeshCtxs.Hash != d.lastHash
-	if !syncForConfig {
+	var syncForCert bool
+	for _, mesh := range aggregatedMeshCtxs.Meshes {
+		certInfo := d.EnvoyCpCtx.Secrets.Info(
+			mesh_proto.IngressProxyType,
+			core_model.ResourceKey{Mesh: mesh.GetMeta().GetName(), Name: d.key.Name},
+		)
+		syncForCert = syncForCert || (certInfo != nil && certInfo.ExpiringSoon()) // check if we need to regenerate config because identity cert is expiring soon.
+	}
+	if !syncForConfig && !syncForCert {
 		result.Status = SkipStatus
 		return result, nil
 	}
@@ -138,10 +149,22 @@
 	d.log.V(1).Info("snapshot hash updated, reconcile", "prev", d.lastHash, "current", aggregatedMeshCtxs.Hash)
 	d.lastHash = aggregatedMeshCtxs.Hash
 
+	if syncForCert {
+		d.log.V(1).Info("certs expiring soon, reconcile")
+	}
+
 	proxy, err := d.IngressProxyBuilder.Build(ctx, d.key, aggregatedMeshCtxs)
 	if err != nil {
 		return SyncResult{}, errors.Wrap(err, "could not build ingress proxy")
 	}
+
+	networking := proxy.ZoneIngressProxy.ZoneIngressResource.Spec.GetNetworking()
+	envoyAdminMTLS, err := d.getEnvoyAdminMTLS(ctx, networking.GetAddress(), networking.GetAdvertisedAddress())
+	if err != nil {
+		return SyncResult{}, errors.Wrap(err, "could not get Envoy Admin mTLS certs")
+	}
+	proxy.EnvoyAdminMTLSCerts = envoyAdminMTLS
+
 	proxy.Metadata = metadata
 	changed, err := d.IngressReconciler.Reconcile(ctx, *envoyCtx, proxy)
 	if err != nil {
@@ -166,27 +189,48 @@
 	if err != nil {
 		return SyncResult{}, errors.Wrap(err, "could not get mesh context")
 	}
+
+	certInfo := d.EnvoyCpCtx.Secrets.Info(mesh_proto.DataplaneProxyType, d.key)
+	// check if we need to regenerate config because identity cert is expiring soon.
+	syncForCert := certInfo != nil && certInfo.ExpiringSoon()
 	// check if we need to regenerate config because Dubbo policies has changed.
 	syncForConfig := meshCtx.Hash != d.lastHash
 	result := SyncResult{
 		ProxyType: mesh_proto.DataplaneProxyType,
 	}
-	if !syncForConfig {
+	if !syncForConfig && !syncForCert {
 		result.Status = SkipStatus
 		return result, nil
 	}
 	if syncForConfig {
 		d.log.V(1).Info("snapshot hash updated, reconcile", "prev", d.lastHash, "current", meshCtx.Hash)
 	}
+	if syncForCert {
+		d.log.V(1).Info("certs expiring soon, reconcile")
+	}
 
 	envoyCtx := &xds_context.Context{
 		ControlPlane: d.EnvoyCpCtx,
 		Mesh:         meshCtx,
 	}
+
+	if _, found := meshCtx.DataplanesByName[d.key.Name]; !found {
+		d.log.Info("Dataplane object not found. Can't regenerate XDS configuration. It's expected during Kubernetes namespace termination. " +
+			"If it persists it's a bug.")
+		result.Status = SkipStatus
+		return result, nil
+	}
+
 	proxy, err := d.DataplaneProxyBuilder.Build(ctx, d.key, meshCtx)
 	if err != nil {
 		return SyncResult{}, errors.Wrap(err, "could not build dataplane proxy")
 	}
+	networking := proxy.Dataplane.Spec.Networking
+	envoyAdminMTLS, err := d.getEnvoyAdminMTLS(ctx, networking.Address, networking.AdvertisedAddress)
+	proxy.EnvoyAdminMTLSCerts = envoyAdminMTLS
+	if !envoyCtx.Mesh.Resource.MTLSEnabled() {
+		d.EnvoyCpCtx.Secrets.Cleanup(mesh_proto.DataplaneProxyType, d.key) // we need to cleanup secrets if mtls is disabled
+	}
 	proxy.Metadata = metadata
 	changed, err := d.DataplaneReconciler.Reconcile(ctx, *envoyCtx, proxy)
 	if err != nil {
@@ -201,3 +245,37 @@
 	}
 	return result, nil
 }
+
+func (d *DataplaneWatchdog) getEnvoyAdminMTLS(ctx context.Context, address string, advertisedAddress string) (core_xds.ServerSideMTLSCerts, error) {
+	if d.envoyAdminMTLS == nil || d.dpAddress != address {
+		ca, err := envoy_admin_tls.LoadCA(ctx, d.ResManager)
+		if err != nil {
+			return core_xds.ServerSideMTLSCerts{}, errors.Wrap(err, "could not load the CA")
+		}
+		caPair, err := util_tls.ToKeyPair(ca.PrivateKey, ca.Certificate[0])
+		if err != nil {
+			return core_xds.ServerSideMTLSCerts{}, err
+		}
+		ips := []string{address}
+		if advertisedAddress != "" && advertisedAddress != address {
+			ips = append(ips, advertisedAddress)
+		}
+		serverPair, err := envoy_admin_tls.GenerateServerCert(ca, ips...)
+		if err != nil {
+			return core_xds.ServerSideMTLSCerts{}, errors.Wrap(err, "could not generate server certificate")
+		}
+
+		envoyAdminMTLS := core_xds.ServerSideMTLSCerts{
+			CaPEM:      caPair.CertPEM,
+			ServerPair: serverPair,
+		}
+		// cache the Envoy Admin MTLS and dp address, so we
+		// 1) don't have to do I/O on every sync
+		// 2) have a stable certs = stable Envoy config
+		// This means that if we want to change Envoy Admin CA, we need to restart all CP instances.
+		// Additionally, we need to trigger cert generation when DP address has changed without DP reconnection.
+		d.envoyAdminMTLS = &envoyAdminMTLS
+		d.dpAddress = address
+	}
+	return *d.envoyAdminMTLS, nil
+}