- 云原生
- DevOps
- 运维
【免费下载链接】devtron
The only Kubernetes dashboard you need
导读
Devtron v2.1.0 是围绕「权限自动化、集群容量数据可靠性与 API 安全」三个方向的一次集中迭代:新增基于 SSO group claims 的权限组自动分配能力,让用户登录后无需管理员手工授权即可获得对应角色组权限;重构集群容量(Cluster Capacity)数据链路,引入内存缓存与后台定时刷新,使/orchestrator/k8s/capacity系列接口能快速、稳定地返回原始集群容量详情,并在集群连接失败时返回明确的失败状态;同时封堵了内部专属属性(internal-only attributes)在 API 中可读可写的问题,并在审计日志中补全 clientIP 字段。阅读本文后,你将掌握 v2.1.0 各项变更的触发场景、对应源码位置与配置方式,能够据此评估升级影响并正确配置新特性。
一、版本概况
依据仓库 CHANGELOG/release-notes-v2.1.0.md,v2.1.0 共包含 1 项增强(Enhancements)、4 项缺陷修复(Bugs)与 4 项其他变更(Others),具体清单如下:
| 类型 | 变更内容 | 关联 Issue/PR |
|---|---|---|
| Enhancements | 支持按 SSO 登录时携带的 group claims 自动分配权限组 | #6911 |
| Bugs | 防止内部专属属性在 API 响应与请求中暴露 | #6917 |
| Bugs | 在 capacity handler 中将过滤后的集群详情追加到集群详情列表 | #6915 |
| Bugs | 增强 cluster overview 响应,返回原始集群容量详情并支持缓存 | #6914 |
| Bugs | 集群容量获取失败时返回详细的连接失败状态 | #6912 |
| Others | 主干同步 | #6920 |
| Others | 增加 scarf pixel 统计埋点 | #6918 |
| Others | 审计日志中记录 clientIP | #6908 |
| Others | 重构漏洞(vulnerability)查询实现并清理无用代码 | #6907 |
下面逐项结合仓库源码展开解读。
二、增强:按 SSO group claims 自动分配权限组
2.1 功能背景
在企业中通过 SSO(如 Dex / OIDC)登录 Devtron 时,IdP 返回的 JWT 中通常携带groupsclaims(用户所属的组)。此前这些组信息只用于「登录后映射角色」,而 v2.1.0 将其扩展为「自动分配权限组」:当配置了 group claims 与权限组(Permission Group / Role Group)映射后,用户每次登录/刷新 token 时,Devtron 会自动将其归入对应的权限组,无需管理员手工逐个添加。
2.2 数据模型:user_auto_assigned_groups表
该特性的存储层由 pkg/auth/user/repository/UserAutoAssignGroupMapRepository.go 承载,核心模型为UserAutoAssignedGroup:
type UserAutoAssignedGroup struct { TableName struct{} `sql:"user_auto_assigned_groups"` Id int `sql:"id,pk"` UserId int32 `sql:"user_id"` GroupName string `sql:"group_name,notnull"` IsGroupClaimsData bool `sql:"is_group_claims_data,notnull"` Active bool `sql:"active,notnull"` User *UserModel `pg:"fk:user_id"` sql.AuditLog }关键字段含义:
user_id:被自动分配权限组的用户 ID;group_name:来自 SSO group claims 的组名;is_group_claims_data:标记该条映射是否由 group claims 驱动(区别于管理员手工分配的权限组);active:映射是否生效,用户在最新 claims 中不再出现的组会被置为false实现自动回收。
建表/迁移脚本位于 scripts/sql/283_user_group.up.sql,其中包含权限组主表user_group、权限组-用户关联表user_group_mapping,并在脚本末尾将原user_groups表重命名为user_auto_assigned_groups,与上述模型保持一致。
仓库接口UserAutoAssignGroupMapRepository提供以下查询能力(见同文件第 26-33 行):
GetByUserId(userId):查询某用户全部自动分配映射;GetActiveByUserId(userId):仅查询生效中的映射;GetAllActiveWithUser():联查所有生效映射及其用户(用于权限同步)。
2.3 核心流程:登录时的映射同步
自动分配逻辑的触发入口在 pkg/auth/user/UserSelfRegistrationService.go 的CheckAndCreateUserIfConfigured(第 138-163 行):用户通过 SSO 完成认证后,系统从 claims 中提取 email 与 groups;若用户不存在则自动注册,若已存在且启用了 group claims 配置(IsGroupClaimsConfigActive()),则调用UpdateUserGroupMappingIfActiveUser(emailId, groups)同步映射。
映射同步的实质实现在 pkg/auth/user/UserService_ent.go 第 186-249 行的UpdateUserGroupMappingIfActiveUser与updateDataForUserGroupClaimsMap:
- 按 email 查出活跃用户;
- 以最新 claims 中的组集合构建
mapOfGroups; - 遍历库中已有映射:仍在 claims 中的组置
active = true并从 map 中删除,已不在 claims 中的组置active = false(自动回收),统一更新UpdatedOn/UpdatedBy(系统用户); - 对 map 中剩余(全新出现)的组批量插入新映射,
IsGroupClaimsData = true、Active = true; - 整个过程在单个数据库事务中完成(
dbConnection.Begin()+ 延迟Rollback())。
结合 pkg/auth/user/UserService.go 中UserServiceImpl对UserAutoAssignGroupMapRepository的注入(第 109 行、第 119 行),可以确认该能力已完整接入用户服务主链路。
2.4 使用前提
- 需要在全局授权配置中启用 group claims 映射(
IsGroupClaimsConfigActive()对应的配置开关); - SSO 返回的 JWT 必须包含
groupsclaims; - 权限组名称需与 claims 中的组名保持约定的命名映射关系(
GetGroupCasbinName负责把 claims 组名转换为 casbin 角色名,见 pkg/auth/user/UserService_ent.go 第 172-180 行)。
三、Bug 修复:内部专属属性不再通过 API 暴露
3.1 问题描述
Devtron 的全局属性(Attributes)体系中存在一类「仅供系统内部使用」的键,例如apiTokenSecret(用于 API Token 加密的密钥)。此前这些键在 API 响应中可能被返回、在请求中可能被创建或修改,存在安全风险。v2.1.0 通过 #6917 修复了该问题。
3.2 内部键清单定义
内部键清单定义在 pkg/attributes/bean/bean.go 第 27-30 行:
// InternalOnlyKeys are the internal attribute keys - cannot be read or written via API var InternalOnlyKeys = map[string]bool{ API_SECRET_KEY: true, // "apiTokenSecret" }同文件中还声明了其他全局属性键,如url、enforceDeploymentTypeConfig、priorityDeploymentCondition等,但只有apiTokenSecret被标记为 internal-only,即禁止通过 API 读写。
3.3 RestHandler 侧的双向拦截
在 api/restHandler/AttributesRestHandlder.go 中实现了完整的防护:
isInternalOnlyKey(key)(第 63-66 行):判定键是否内部专属;filterInternalAttributes(attributes)(第 68-74 行):对查询结果执行过滤,把内部键从响应列表中剔除,保证响应不泄露;AddAttributes(第 97-102 行):创建属性前检查键名,若为内部键直接返回403 Forbidden,保证请求无法写入;UpdateAttributes(同文件第 114 行起)同样对键名做一致性校验,防止通过更新路径写入内部键。
该修复同时覆盖了「读」与「写」两个方向,是典型的内部配置项隔离模式。升级到 v2.1.0 后,任何通过 attributes API 读取或写入apiTokenSecret的尝试都会被拒绝。
四、Bug 修复:集群容量(Cluster Capacity)链路增强
v2.1.0 的三项修复(#6914、#6915、#6912)共同完善了集群容量数据链路,从「响应内容」与「异常处理」两个维度提升可靠性。
4.1 原始集群容量详情与缓存支持(#6914)
响应结构扩展
集群总览响应在 pkg/overview/bean/OverviewBean.go 中新增了RawClusterCapacityDetails字段:
ClusterCapacityDistribution []ClusterCapacityDistribution `json:"clusterCapacityDistribution"` RawClusterCapacityDetails []*capacityBean.ClusterCapacityDetail `json:"rawClusterCapacityDetails"`其中ClusterCapacityDetail定义于 pkg/k8s/capacity/bean/bean.go 第 88-102 行,包含集群 ID/名称、节点数、节点详情、ServerVersion、CPU/内存的ResourceDetailObject、状态(Status)以及虚拟集群/生产标记等。ResourceDetailObject(第 158-174 行)同时提供字符串形式(capacity、allocatable、usage、request、limit及对应百分比)和字节形式(CapacityInBytes等)两种表达,便于前端排序展示。
聚合写入逻辑位于 pkg/overview/ClusterOverviewService.go 的addRawClusterCapacityDetails(第 484-487 行),由buildClusterOverviewResponse在构建响应时统一填充(第 396 行)。
缓存机制
为降低频繁直连各集群 k8s API 的开销,v2.1.0 引入了内存缓存,实现见 pkg/overview/cache/ClusterCacheService.go:
GetClusterOverview()/SetClusterOverview():基于sync.RWMutex保护的内存缓存读写,缓存条目带lastUpdated时间戳;InvalidateClusterOverview()/InvalidateAll():主动失效接口;IsRefreshing()/SetRefreshing():刷新互斥标记,防止并发重复刷新;GetCacheAge():返回缓存数据年龄,供陈旧性判断。
后台刷新由 pkg/overview/ClusterOverviewService.go 的StartBackgroundRefresh(第 81-117 行)驱动:启动时先做一次初始填充,随后按ticker周期执行refreshCache;刷新期间通过IsRefreshing防止并发,并记录刷新耗时与集群数。数据抓取采用 worker pool 并行拉取(fetchClusterDataParallel,第 164 行起),并对连接失败的集群单独打上占位详情(errorClusterDetails)。
缓存相关配置由 pkg/overview/config/ClusterOverviewConfig.go 提供,全部通过环境变量注入:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
CLUSTER_OVERVIEW_CACHE_ENABLED | true | 是否启用 cluster overview 缓存 |
CLUSTER_OVERVIEW_BACKGROUND_REFRESH_ENABLED | true | 是否启用后台定时刷新 |
CLUSTER_OVERVIEW_REFRESH_INTERVAL_SECONDS | 15 | 后台刷新间隔(秒) |
CLUSTER_OVERVIEW_MAX_PARALLEL_CLUSTERS | 15 | 刷新时并行拉取的集群数上限 |
CLUSTER_OVERVIEW_MAX_STALE_DATA_SECONDS | 30 | 缓存超过该年龄即视为陈旧(记录警告但仍返回) |
缓存命中的降级策略见handleCacheHit(第 300-312 行):即使缓存年龄超过阈值,也仅记录 Warn 日志并继续返回缓存数据,保证可用性优先。
4.2 过滤后的集群详情追加到列表(#6915)
集群容量列表接口在缓存命中时,需要把缓存的原始容量详情转换为ClusterCapacityDetail列表,并按当前用户的 RBAC 授权范围过滤。该逻辑位于 api/k8s/capacity/k8sCapacityRestHandler.go 的filterAuthorizedClusterDetails(第 494-527 行),其处理规则(代码注释明确列出)为:
- 来自缓存、健康且有容量数据的集群;
- 虚拟集群(来自数据库,不在缓存中);
- 连接出错的集群(来自数据库,不在缓存中)。
实现上先构建authenticatedClusterIds授权集合,再遍历cachedOverview.RawClusterCapacityDetails,仅保留授权范围内的集群详情并追加到结果列表。配合GetClusterListWithDetail(第 136-183 行)的完整调用链:登录态校验 →FindAllExceptVirtual拉取全部非虚拟集群 → 逐集群 RBAC 鉴权 → 缓存命中则走filterAuthorizedClusterDetails,未命中则回退到k8sCapacityService.GetClusterCapacityDetailList直连各集群。
该接口挂载于api/router/router.go第 427-428 行的/orchestrator/k8s/capacity路由前缀下。
4.3 连接失败时返回明确状态(#6912)
在 pkg/k8s/capacity/k8sCapacityService.go 的GetClusterCapacityDetailList(第 83-108 行)中,对每类失败场景给出显式状态:
if len(cluster.ErrorInConnecting) > 0 { clusterCapacityDetail.ErrorInConnection = cluster.ErrorInConnecting clusterCapacityDetail.Status = bean.ClusterStatusConnectionFailed } else { clusterCapacityDetail, err = impl.GetClusterCapacityDetail(ctx, cluster, true) if err != nil { clusterCapacityDetail = &bean.ClusterCapacityDetail{ ErrorInConnection: err.Error(), Status: bean.ClusterStatusConnectionFailed, } } }即:集群本就存在连接错误(ErrorInConnecting)时直接标记;调用 k8s API 出错(如集群不可达)时同样以错误信息 +connection failed状态返回,而不是让整个列表请求失败。ClusterStatus的取值定义于 pkg/k8s/capacity/bean/bean.go 第 114-120 行:
ClusterStatusHealthy ClusterStatus = "healthy" ClusterStatusUnHealthy ClusterStatus = "unhealthy" ClusterStatusConnectionFailed ClusterStatus = "connection failed"在单集群详情接口GetClusterCapacityDetail(第 110 行起)中,若集群不可达(IsClusterUnReachableError)则返回带400 Bad Request与用户可读信息的ApiError,供上层精准呈现。SetStatus方法(bean.go 第 104-112 行)则根据节点错误集合将集群判定为 healthy / unhealthy。
五、其他变更
5.1 审计日志补全 clientIP(#6908)
用户在登录及关键操作时的来源 IP 此前未被完整记录。v2.1.0 在 pkg/auth/user/UserService.go 的SaveLoginAudit(emailId, clientIp string, id int32)(第 1184 行起)与登录审计相关方法(第 1670-1684 行)中,通过util2.GetClientIP(r)提取请求客户端 IP 并写入审计对象;UserAuditService(pkg/auth/user/UserAuditService.go)携带ClientIp字段完成落库,对应数据库字段client_ip定义于 pkg/auth/user/repository/UserAuditRepository.go 第 32 行。
该变更还联动到 API Token 模块:ApiTokenService在更新 token 最近使用信息时,会取最新审计日志的ClientIp作为LastUsedByIp(见 pkg/apiToken/ApiTokenService.go 第 179 行),使安全审计链路更完整。
5.2 漏洞查询实现重构(#6907)
该变更对安全总览中的漏洞趋势查询做了实现级重构并清理无用代码。当前查询入口在 pkg/overview/SecurityOverviewService.go 的GetVulnerabilityTrend(第 272 行起):
- 根据
envType(prod / non-prod / all)构造isProd过滤条件(nil 表示全部环境); - 调用
imageScanResultRepository.GetVulnerabilityTrendDataByFilters(from, to, isProd)单次取数; - 按时间桶与严重级别聚合为
VulnerabilityTrendResponse.Trend(aggregateVulnerabilitiesByTime)。
上层接口由 pkg/overview/OverviewService.go 的GetVulnerabilityTrend暴露(第 133-134 行),供仪表盘安全总览消费。重构后查询职责更集中、过滤逻辑更清晰,便于后续维护。
5.3 其他
sync: main(#6920):主干同步,通常为合并主干分支代码以消除差异;chore: Adds scarf pixel(#6918):在文档站点引入 scarf 统计像素(本项目仓库内未发现对应源码引用,属于站点/文档层面的埋点变更,不影响后端运行行为);- 其余杂项(#6908、#6907)已在上文展开。
六、升级与验证建议
- 权限组自动分配:升级后需在全局授权配置中确认 group claims 开关状态,并用一个含
groupsclaims 的测试账号完成一次登录,观察user_auto_assigned_groups表中是否生成is_group_claims_data = true的映射; - 内部属性保护:可尝试通过 attributes API 对
apiTokenSecret执行读取或写入,预期分别被过滤与返回403 Forbidden; - 集群容量缓存:在环境变量中确认
CLUSTER_OVERVIEW_CACHE_ENABLED/CLUSTER_OVERVIEW_REFRESH_INTERVAL_SECONDS的取值,观察/orchestrator/k8s/capacity列表接口命中缓存时响应速度的提升,以及部分集群连接失败时返回的status: "connection failed"与errorInNodeListing字段; - 审计日志:登录一次后检查审计记录中的
client_ip字段是否已正确写入。
七、小结
Devtron v2.1.0 是一个「安全加固 + 可观测性 + 性能优化」并重的版本:SSO group claims 驱动的权限组自动分配(pkg/auth/user/UserService_ent.go)显著降低大规模用户接入的授权运维成本;attributes API 对内部键的读写拦截(api/restHandler/AttributesRestHandlder.go)封堵了敏感配置泄露通道;集群容量链路通过内存缓存(pkg/overview/cache/ClusterCacheService.go)、原始详情字段(pkg/overview/bean/OverviewBean.go)与明确的连接失败状态(pkg/k8s/capacity/bean/bean.go)提升了多集群场景下的响应能力与排障体验;clientIP 审计与漏洞查询重构则为安全合规与代码可维护性添砖加瓦。对于运行在多个集群、通过 SSO 管理大量用户的生产环境,本版本值得优先评估升级。
- 云原生
- DevOps
- 运维
【免费下载链接】devtron
The only Kubernetes dashboard you need
相关推荐
Devtron v2.1.1 版本发布说明:权限组自动分配修复与集群级通知触发器校验
Devtron v2.1.1 版本发布说明:权限组自动分配修复与集群级通知触发器校验 本篇文章基于 Devtron 开源仓库 CHANGELOG/release
云原生DevOps运维CoreDNS 1.14.4 发布详解:传输安全加固与缓存、转发、DNSSEC 关键改进
CoreDNS 1.14.4 发布详解:传输安全加固与缓存、转发、DNSSEC 关键改进 导读 本文基于 CoreDNS 1.14.4 的官方发布说明( not
后端网络云原生eksctl 集群安全加固指南:`withOIDC` 与 `disablePodIMDS` 配置详解
eksctl 集群安全加固指南: withOIDC 与 disablePodIMDS 配置详解 eksctl 是 Amazon EKS 的官方命令行工具,其在
云原生容器编排DevOps
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考