diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go
index 8f20efc6..75d55a5b 100644
--- a/internal/rpc/rpc_client.go
+++ b/internal/rpc/rpc_client.go
@@ -332,6 +332,10 @@ func (this *RPCClient) AuthorityKeyRPC() pb.AuthorityKeyServiceClient {
 	return pb.NewAuthorityKeyServiceClient(this.pickConn())
 }
 
+func (this *RPCClient) AuthorityNodeRPC() pb.AuthorityNodeServiceClient {
+	return pb.NewAuthorityNodeServiceClient(this.pickConn())
+}
+
 // Context 构造Admin上下文
 func (this *RPCClient) Context(adminId int64) context.Context {
 	ctx := context.Background()
diff --git a/internal/web/actions/default/settings/authority/index.go b/internal/web/actions/default/settings/authority/index.go
index 8bf85536..30e43031 100644
--- a/internal/web/actions/default/settings/authority/index.go
+++ b/internal/web/actions/default/settings/authority/index.go
@@ -15,7 +15,7 @@ type IndexAction struct {
 }
 
 func (this *IndexAction) Init() {
-	this.Nav("", "", "")
+	this.Nav("", "", "index")
 }
 
 func (this *IndexAction) RunGet(params struct{}) {
@@ -47,5 +47,23 @@ func (this *IndexAction) RunGet(params struct{}) {
 	}
 	this.Data["key"] = keyMap
 
+	// 检查是否有认证节点,如果没有认证节点,则自动生成一个
+	countResp, err := this.RPC().AuthorityNodeRPC().CountAllEnabledAuthorityNodes(this.AdminContext(), &pb.CountAllEnabledAuthorityNodesRequest{})
+	if err != nil {
+		this.ErrorPage(err)
+		return
+	}
+	if countResp.Count == 0 {
+		_, err = this.RPC().AuthorityNodeRPC().CreateAuthorityNode(this.AdminContext(), &pb.CreateAuthorityNodeRequest{
+			Name:        "默认节点",
+			Description: "系统自动生成的默认节点",
+			IsOn:        true,
+		})
+		if err != nil {
+			this.ErrorPage(err)
+			return
+		}
+	}
+
 	this.Show()
 }
diff --git a/internal/web/actions/default/settings/authority/nodes/delete.go b/internal/web/actions/default/settings/authority/nodes/delete.go
new file mode 100644
index 00000000..afd587cb
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/delete.go
@@ -0,0 +1,28 @@
+package nodes
+
+import (
+	"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+)
+
+type DeleteAction struct {
+	actionutils.ParentAction
+}
+
+func (this *DeleteAction) RunPost(params struct {
+	NodeId int64
+}) {
+	// TODO 检查权限
+
+	_, err := this.RPC().AuthorityNodeRPC().DeleteAuthorityNode(this.AdminContext(), &pb.DeleteAuthorityNodeRequest{NodeId: params.NodeId})
+	if err != nil {
+		this.ErrorPage(err)
+		return
+	}
+
+	// 创建日志
+	defer this.CreateLog(oplogs.LevelInfo, "删除认证节点 %d", params.NodeId)
+
+	this.Success()
+}
diff --git a/internal/web/actions/default/settings/authority/nodes/helper.go b/internal/web/actions/default/settings/authority/nodes/helper.go
new file mode 100644
index 00000000..b676b483
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/helper.go
@@ -0,0 +1,15 @@
+package nodes
+
+import (
+	"github.com/iwind/TeaGo/actions"
+)
+
+type Helper struct {
+}
+
+func NewHelper() *Helper {
+	return &Helper{}
+}
+
+func (this *Helper) BeforeAction(action *actions.ActionObject) {
+}
diff --git a/internal/web/actions/default/settings/authority/nodes/index.go b/internal/web/actions/default/settings/authority/nodes/index.go
new file mode 100644
index 00000000..08937b9f
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/index.go
@@ -0,0 +1,75 @@
+package nodes
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
+	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+	"github.com/iwind/TeaGo/logs"
+	"github.com/iwind/TeaGo/maps"
+	"time"
+)
+
+type IndexAction struct {
+	actionutils.ParentAction
+}
+
+func (this *IndexAction) Init() {
+	this.Nav("", "node", "node")
+}
+
+func (this *IndexAction) RunGet(params struct{}) {
+	countResp, err := this.RPC().AuthorityNodeRPC().CountAllEnabledAuthorityNodes(this.AdminContext(), &pb.CountAllEnabledAuthorityNodesRequest{})
+	if err != nil {
+		this.ErrorPage(err)
+		return
+	}
+	count := countResp.Count
+	page := this.NewPage(count)
+	this.Data["page"] = page.AsHTML()
+
+	nodeMaps := []maps.Map{}
+	if count > 0 {
+		nodesResp, err := this.RPC().AuthorityNodeRPC().ListEnabledAuthorityNodes(this.AdminContext(), &pb.ListEnabledAuthorityNodesRequest{
+			Offset: page.Offset,
+			Size:   page.Size,
+		})
+		if err != nil {
+			this.ErrorPage(err)
+			return
+		}
+
+		for _, node := range nodesResp.Nodes {
+			// 状态
+			status := &nodeconfigs.NodeStatus{}
+			if len(node.StatusJSON) > 0 {
+				err = json.Unmarshal(node.StatusJSON, &status)
+				if err != nil {
+					logs.Error(err)
+					continue
+				}
+				status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
+			}
+
+			nodeMaps = append(nodeMaps, maps.Map{
+				"id":   node.Id,
+				"isOn": node.IsOn,
+				"name": node.Name,
+				"status": maps.Map{
+					"isActive":     status.IsActive,
+					"updatedAt":    status.UpdatedAt,
+					"hostname":     status.Hostname,
+					"cpuUsage":     status.CPUUsage,
+					"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
+					"memUsage":     status.MemoryUsage,
+					"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
+					"buildVersion": status.BuildVersion,
+				},
+			})
+		}
+	}
+	this.Data["nodes"] = nodeMaps
+
+	this.Show()
+}
diff --git a/internal/web/actions/default/settings/authority/nodes/init.go b/internal/web/actions/default/settings/authority/nodes/init.go
new file mode 100644
index 00000000..d50148ef
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/init.go
@@ -0,0 +1,23 @@
+package nodes
+
+import (
+	"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/authority/nodes/node"
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
+	"github.com/iwind/TeaGo"
+)
+
+func init() {
+	TeaGo.BeforeStart(func(server *TeaGo.Server) {
+		server.
+			Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
+			Helper(NewHelper()).
+			Helper(settingutils.NewAdvancedHelper("authority")).
+			Prefix("/settings/authority/nodes").
+			Get("", new(IndexAction)).
+			GetPost("/node/createPopup", new(node.CreatePopupAction)).
+			Post("/delete", new(DeleteAction)).
+			EndAll()
+	})
+}
diff --git a/internal/web/actions/default/settings/authority/nodes/node/createPopup.go b/internal/web/actions/default/settings/authority/nodes/node/createPopup.go
new file mode 100644
index 00000000..034285e4
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/node/createPopup.go
@@ -0,0 +1,47 @@
+package node
+
+import (
+	"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+	"github.com/iwind/TeaGo/actions"
+)
+
+type CreatePopupAction struct {
+	actionutils.ParentAction
+}
+
+func (this *CreatePopupAction) Init() {
+	this.Nav("", "node", "create")
+}
+
+func (this *CreatePopupAction) RunGet(params struct{}) {
+	this.Show()
+}
+
+func (this *CreatePopupAction) RunPost(params struct {
+	Name        string
+	Description string
+	IsOn        bool
+
+	Must *actions.Must
+}) {
+	params.Must.
+		Field("name", params.Name).
+		Require("请输入认证节点名称")
+
+	createResp, err := this.RPC().AuthorityNodeRPC().CreateAuthorityNode(this.AdminContext(), &pb.CreateAuthorityNodeRequest{
+		Name:        params.Name,
+		Description: params.Description,
+		IsOn:        params.IsOn,
+	})
+	if err != nil {
+		this.ErrorPage(err)
+		return
+	}
+
+	// 创建日志
+	defer this.CreateLog(oplogs.LevelInfo, "创建认证节点 %d", createResp.NodeId)
+
+	this.Success()
+}
diff --git a/internal/web/actions/default/settings/authority/nodes/node/helper.go b/internal/web/actions/default/settings/authority/nodes/node/helper.go
new file mode 100644
index 00000000..689a22cc
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/node/helper.go
@@ -0,0 +1,21 @@
+package node
+
+import (
+	"github.com/iwind/TeaGo/actions"
+	"net/http"
+)
+
+type Helper struct {
+}
+
+func NewHelper() *Helper {
+	return &Helper{}
+}
+
+func (this *Helper) BeforeAction(action *actions.ActionObject) (goNext bool) {
+	if action.Request.Method != http.MethodGet {
+		return true
+	}
+
+	return true
+}
diff --git a/internal/web/actions/default/settings/authority/nodes/node/index.go b/internal/web/actions/default/settings/authority/nodes/node/index.go
new file mode 100644
index 00000000..085413d6
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/node/index.go
@@ -0,0 +1,39 @@
+package node
+
+import (
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+	"github.com/iwind/TeaGo/maps"
+)
+
+type IndexAction struct {
+	actionutils.ParentAction
+}
+
+func (this *IndexAction) Init() {
+	this.Nav("", "", "index")
+}
+
+func (this *IndexAction) RunGet(params struct {
+	NodeId int64
+}) {
+	nodeResp, err := this.RPC().AuthorityNodeRPC().FindEnabledAuthorityNode(this.AdminContext(), &pb.FindEnabledAuthorityNodeRequest{NodeId: params.NodeId})
+	if err != nil {
+		this.ErrorPage(err)
+		return
+	}
+	node := nodeResp.Node
+	if node == nil {
+		this.NotFound("authorityNode", params.NodeId)
+		return
+	}
+
+	this.Data["node"] = maps.Map{
+		"id":          node.Id,
+		"name":        node.Name,
+		"description": node.Description,
+		"isOn":        node.IsOn,
+	}
+
+	this.Show()
+}
diff --git a/internal/web/actions/default/settings/authority/nodes/node/init.go b/internal/web/actions/default/settings/authority/nodes/node/init.go
new file mode 100644
index 00000000..489ebfbf
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/node/init.go
@@ -0,0 +1,25 @@
+package node
+
+import (
+	"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
+	"github.com/iwind/TeaGo"
+)
+
+func init() {
+	TeaGo.BeforeStart(func(server *TeaGo.Server) {
+		server.
+			Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
+			Helper(settingutils.NewAdvancedHelper("authority")).
+			Prefix("/settings/authority/nodes/node").
+
+			// 节点相关
+			Helper(NewHelper()).
+			Get("", new(IndexAction)).
+			GetPost("/update", new(UpdateAction)).
+			Get("/install", new(InstallAction)).
+
+			EndAll()
+	})
+}
diff --git a/internal/web/actions/default/settings/authority/nodes/node/install.go b/internal/web/actions/default/settings/authority/nodes/node/install.go
new file mode 100644
index 00000000..47f2675d
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/node/install.go
@@ -0,0 +1,57 @@
+package node
+
+import (
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+	"github.com/iwind/TeaGo/maps"
+	"strings"
+)
+
+type InstallAction struct {
+	actionutils.ParentAction
+}
+
+func (this *InstallAction) Init() {
+	this.Nav("", "", "install")
+}
+
+func (this *InstallAction) RunGet(params struct {
+	NodeId int64
+}) {
+	// 认证节点信息
+	nodeResp, err := this.RPC().AuthorityNodeRPC().FindEnabledAuthorityNode(this.AdminContext(), &pb.FindEnabledAuthorityNodeRequest{NodeId: params.NodeId})
+	if err != nil {
+		this.ErrorPage(err)
+		return
+	}
+	node := nodeResp.Node
+	if node == nil {
+		this.NotFound("authorityNode", params.NodeId)
+		return
+	}
+
+	this.Data["node"] = maps.Map{
+		"id":       node.Id,
+		"name":     node.Name,
+		"uniqueId": node.UniqueId,
+		"secret":   node.Secret,
+	}
+
+	// API节点列表
+	apiNodesResp, err := this.RPC().APINodeRPC().FindAllEnabledAPINodes(this.AdminContext(), &pb.FindAllEnabledAPINodesRequest{})
+	if err != nil {
+		this.ErrorPage(err)
+		return
+	}
+	apiNodes := apiNodesResp.Nodes
+	apiEndpoints := []string{}
+	for _, apiNode := range apiNodes {
+		if !apiNode.IsOn {
+			continue
+		}
+		apiEndpoints = append(apiEndpoints, apiNode.AccessAddrs...)
+	}
+	this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\""
+
+	this.Show()
+}
diff --git a/internal/web/actions/default/settings/authority/nodes/node/update.go b/internal/web/actions/default/settings/authority/nodes/node/update.go
new file mode 100644
index 00000000..bd32a258
--- /dev/null
+++ b/internal/web/actions/default/settings/authority/nodes/node/update.go
@@ -0,0 +1,73 @@
+package node
+
+import (
+	"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
+	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
+	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
+	"github.com/iwind/TeaGo/actions"
+	"github.com/iwind/TeaGo/maps"
+)
+
+type UpdateAction struct {
+	actionutils.ParentAction
+}
+
+func (this *UpdateAction) Init() {
+	this.Nav("", "", "update")
+}
+
+func (this *UpdateAction) RunGet(params struct {
+	NodeId int64
+}) {
+	nodeResp, err := this.RPC().AuthorityNodeRPC().FindEnabledAuthorityNode(this.AdminContext(), &pb.FindEnabledAuthorityNodeRequest{
+		NodeId: params.NodeId,
+	})
+	if err != nil {
+		this.ErrorPage(err)
+		return
+	}
+	node := nodeResp.Node
+	if node == nil {
+		this.WriteString("要操作的节点不存在")
+		return
+	}
+
+	this.Data["node"] = maps.Map{
+		"id":          node.Id,
+		"name":        node.Name,
+		"description": node.Description,
+		"isOn":        node.IsOn,
+	}
+
+	this.Show()
+}
+
+// 保存基础设置
+func (this *UpdateAction) RunPost(params struct {
+	NodeId      int64
+	Name        string
+	Description string
+	IsOn        bool
+
+	Must *actions.Must
+}) {
+	params.Must.
+		Field("name", params.Name).
+		Require("请输入认证节点名称")
+
+	_, err := this.RPC().AuthorityNodeRPC().UpdateAuthorityNode(this.AdminContext(), &pb.UpdateAuthorityNodeRequest{
+		NodeId:      params.NodeId,
+		Name:        params.Name,
+		Description: params.Description,
+		IsOn:        params.IsOn,
+	})
+	if err != nil {
+		this.ErrorPage(err)
+		return
+	}
+
+	// 创建日志
+	defer this.CreateLog(oplogs.LevelInfo, "修改认证节点 %d", params.NodeId)
+
+	this.Success()
+}
diff --git a/internal/web/import.go b/internal/web/import.go
index 260e53df..fdcb58b1 100644
--- a/internal/web/import.go
+++ b/internal/web/import.go
@@ -85,6 +85,8 @@ import (
 	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/stat"
 	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings"
 	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/authority"
+	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/authority/nodes"
+	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/authority/nodes/node"
 	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/backup"
 	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/database"
 	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ip-library"
diff --git a/web/views/@default/settings/authority/@menu.html b/web/views/@default/settings/authority/@menu.html
new file mode 100644
index 00000000..43beab87
--- /dev/null
+++ b/web/views/@default/settings/authority/@menu.html
@@ -0,0 +1,4 @@
+
暂时还没有节点。
+ +| 节点名称 | +版本号 | +CPU | +内存 | +状态 | +操作 | +
|---|---|---|---|---|---|
| {{node.name}} | ++ v{{node.status.buildVersion}} + - + | ++ {{node.status.cpuUsageText}} + - + | ++ {{node.status.memUsageText}} + - + | +
+             
+                运行中
+             
+            已断开
+            未连接
+         | 
+		+ 详情 + 删除 + | +
| 节点名称 | ++ {{node.name}} + | +
| 状态 | +
+			 | 
+	
| 描述 | ++ {{node.description}} + 暂时还没有描述。 + | +
| configs/api.yaml | 
+		
+			rpc:
+  endpoints: [ {{apiEndpoints}} ]
+nodeId: "{{node.uniqueId}}"
+secret: "{{node.secret}}"
+		 | 
+