Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 限制用户可访问的项目资源 #1335 #1337

Merged
merged 1 commit into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import com.tencent.bkrepo.common.security.crypto.CryptoConfiguration
import com.tencent.bkrepo.common.security.exception.SecurityExceptionHandler
import com.tencent.bkrepo.common.security.http.HttpAuthConfiguration
import com.tencent.bkrepo.common.security.http.core.HttpAuthProperties
import com.tencent.bkrepo.common.security.interceptor.devx.DevxProperties
import com.tencent.bkrepo.common.security.interceptor.devx.DevxSrcIpInterceptor
import com.tencent.bkrepo.common.security.manager.AuthenticationManager
import com.tencent.bkrepo.common.security.manager.PermissionManager
import com.tencent.bkrepo.common.security.manager.edge.EdgePermissionManager
Expand All @@ -49,10 +51,14 @@ import com.tencent.bkrepo.common.security.service.ServiceAuthConfiguration
import com.tencent.bkrepo.common.service.cluster.ClusterProperties
import com.tencent.bkrepo.repository.api.NodeClient
import com.tencent.bkrepo.repository.api.RepositoryClient
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
@ConditionalOnWebApplication
Expand All @@ -65,6 +71,7 @@ import org.springframework.context.annotation.Import
ActuatorAuthConfiguration::class,
CryptoConfiguration::class
)
@EnableConfigurationProperties(DevxProperties::class)
class SecurityAutoConfiguration {

@Bean
Expand Down Expand Up @@ -100,4 +107,18 @@ class SecurityAutoConfiguration {
)
}
}

@Bean
@ConditionalOnProperty(value = ["devx.enabled"])
fun devxSrcIpInterceptorConfigure(properties: DevxProperties): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
// 不应用到服务间调用
registry.addInterceptor(DevxSrcIpInterceptor(properties))
// 需要在[httpAuthInterceptor]之后执行才能取得用户信息, 100为随机取值
.order(properties.interceptorOrder.toInt())
.excludePathPatterns("/service/**", "/replica/**")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@

package com.tencent.bkrepo.common.security.interceptor.devx

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.core.Ordered

/**
* 云研发配置
* */
@ConfigurationProperties("devx")
data class DevxProperties(
/**
* 是否开启云研发相关配置
Expand All @@ -52,4 +56,28 @@ data class DevxProperties(
* key 为项目ip, value为CVM配置
*/
var projectCvmWhiteList: Map<String, Set<String>> = emptyMap(),
/**
* 可以从任意来源访问的用户
*/
var userWhiteList: Set<String> = emptySet(),
/**
* 访问受限的用户ID前缀
*/
var restrictedUserPrefix: Set<String> = emptySet(),
/**
* 访问受限的用户ID后缀
*/
var restrictedUserSuffix: Set<String> = emptySet(),
/**
* 请求来源区分header-name
*/
var srcHeaderName: String? = null,
/**
* 请求来源区分header-value
*/
var srcHeaderValues: List<String> = emptyList(),
/**
* devx拦截器优先级,如果需要取用户信息优先级需要比[HttpAuthInterceptor]拦截器低
*/
var interceptorOrder: Long = (Ordered.LOWEST_PRECEDENCE - 100).toLong(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ package com.tencent.bkrepo.common.security.interceptor.devx
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import com.tencent.bkrepo.common.api.exception.SystemErrorException
import com.tencent.bkrepo.common.api.message.CommonMessageCode
import com.tencent.bkrepo.common.api.util.readJsonString
import com.tencent.bkrepo.common.api.util.toJsonString
import com.tencent.bkrepo.common.artifact.constant.PROJECT_ID
import com.tencent.bkrepo.common.security.exception.PermissionException
import com.tencent.bkrepo.common.security.util.SecurityUtils
import com.tencent.bkrepo.common.service.util.HttpContextHolder
import okhttp3.OkHttpClient
import okhttp3.Request
Expand All @@ -55,18 +58,53 @@ open class DevxSrcIpInterceptor(private val devxProperties: DevxProperties) : Ha
.build(CacheLoader.from { key -> listIpFromProject(key) })

override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if (!devxProperties.enabled) {
val user = SecurityUtils.getUserId()
if (!devxProperties.enabled || user in devxProperties.userWhiteList) {
return true
}

val projectId = getProjectId(request) ?: return false
val srcIp = HttpContextHolder.getClientAddress()
if (devxProperties.srcHeaderName.isNullOrEmpty() || devxProperties.srcHeaderValues.size < 2) {
throw SystemErrorException(
CommonMessageCode.SYSTEM_ERROR,
"devx srcHeaderName or srcHeaderValues not configured"
)
}

val headerValue = request.getHeader(devxProperties.srcHeaderName)
return when (headerValue) {
devxProperties.srcHeaderValues[0] -> {
getProjectId(request)?.let { projectId ->
val srcIp = HttpContextHolder.getClientAddress()
checkIpBelongToProject(projectId, srcIp)
}
true
}

devxProperties.srcHeaderValues[1] -> {
devxProperties.restrictedUserPrefix.forEach { checkUserSuffixAndPrefix(user, prefix = it) }
devxProperties.restrictedUserSuffix.forEach { checkUserSuffixAndPrefix(user, suffix = it) }
true
}

else -> true
}
}

private fun checkIpBelongToProject(projectId: String, srcIp: String) {
if (!inWhiteList(srcIp, projectId)) {
logger.info("Illegal src ip[$srcIp] in project[$projectId].")
throw PermissionException()
}
logger.info("Allow ip[$srcIp] to access $projectId.")
return true
}

private fun checkUserSuffixAndPrefix(user: String, prefix: String? = null, suffix: String? = null) {
val matchPrefix = prefix?.let { user.startsWith(it) } ?: false
val matchSuffix = suffix?.let { user.endsWith(it) } ?: false

if (matchPrefix || matchSuffix) {
logger.info("User[$user] access from src ip[${HttpContextHolder.getClientAddress()}] was forbidden")
throw PermissionException()
}
}

protected open fun getProjectId(request: HttpServletRequest): String? {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.tencent.bkrepo.git.config

import com.tencent.bkrepo.git.constant.HubType
import com.tencent.bkrepo.common.security.interceptor.devx.DevxProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.NestedConfigurationProperty

Expand All @@ -11,8 +10,6 @@ data class GitProperties(
var locationDir: String? = null,
@NestedConfigurationProperty
var hub: Hub = Hub(),
@NestedConfigurationProperty
var devx: DevxProperties = DevxProperties()
) {

data class Hub(var github: String? = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.tencent.bkrepo.git.config

import com.tencent.bkrepo.git.artifact.GitRepoInterceptor
import com.tencent.bkrepo.git.interceptor.ContextSettingInterceptor
import com.tencent.bkrepo.common.security.interceptor.devx.DevxSrcIpInterceptor
import com.tencent.bkrepo.git.interceptor.ProxyInterceptor
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
Expand All @@ -13,9 +12,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
@EnableConfigurationProperties(GitProperties::class)
class GitWebConfig(
private val properties: GitProperties
) : WebMvcConfigurer {
class GitWebConfig : WebMvcConfigurer {

override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(repoInterceptor())
Expand All @@ -27,15 +24,9 @@ class GitWebConfig(
registry.addInterceptor(ProxyInterceptor())
.addPathPatterns("/**")
.order(Ordered.HIGHEST_PRECEDENCE + 1)
registry.addInterceptor(devxSrcIpInterceptor(properties))
.addPathPatterns("/**")
.order(Ordered.HIGHEST_PRECEDENCE)
super.addInterceptors(registry)
}

@Bean
fun repoInterceptor() = GitRepoInterceptor()

@Bean
fun devxSrcIpInterceptor(properties: GitProperties) = DevxSrcIpInterceptor(properties.devx)
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
package com.tencent.bkrepo.svn.config

import com.tencent.bkrepo.svn.interceptor.ChangeAncestorProxyHandler
import com.tencent.bkrepo.svn.interceptor.DevxSrcIpInterceptor
import com.tencent.bkrepo.svn.interceptor.ProxyInterceptor
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Configuration
Expand All @@ -42,9 +41,6 @@ class SvnConfiguration(
private val properties: SvnProperties,
) : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(DevxSrcIpInterceptor(properties.devx))
.addPathPatterns("/**")
.order(Ordered.HIGHEST_PRECEDENCE)
registry.addInterceptor(ProxyInterceptor(ChangeAncestorProxyHandler(properties)))
.addPathPatterns("/**")
.order(Ordered.HIGHEST_PRECEDENCE + 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@

package com.tencent.bkrepo.svn.config

import com.tencent.bkrepo.common.security.interceptor.devx.DevxProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.NestedConfigurationProperty

@ConfigurationProperties("svn")
data class SvnProperties(
Expand All @@ -38,8 +36,6 @@ data class SvnProperties(
* 比如SVN客户端访问http://bkrepo.exmaple.com/svn/projectId/repoName,此时需要配置repoPrefix为/svn
*/
var repoPrefix: String = "",
@NestedConfigurationProperty
var devx: DevxProperties = DevxProperties(),
/**
* 例如 http://bkrepo.example.com
*/
Expand Down
Loading