Jenkins Pipeline用戶權(quán)限管理新技巧:打造安全高效的流水線!
什么是RBAC
基于角色的訪問控制(Role-based access control,簡稱 RBAC),指的是通過用戶的角色(Role)授權(quán)其相關(guān)權(quán)限,這實現(xiàn)了更靈活的訪問控制,相比直接授予用戶權(quán)限,要更加簡單、高效、可擴展。
圖片
當(dāng)使用 RBAC 時,通過分析系統(tǒng)用戶的實際情況,基于共同的職責(zé)和需求,授予他們不同角色。你可以授予給用戶一個或多個角色,每個角色具有一個或多個權(quán)限,這種 用戶-角色、角色-權(quán)限 間的關(guān)系,讓我們可以不用再單獨管理單個用戶,用戶從授予的角色里面繼承所需的權(quán)限。
大家可以看一下的案例更容易理解:
用戶角色分為管理員、開發(fā)、運維,各個角色并具備不同的權(quán)限。每個用戶也具備單個與多個角色。
圖片
需求說明
本章節(jié)是通過一個企業(yè)案例進行講解,需求如下:
圖片
接下來,我們根據(jù)上圖的組織架構(gòu)來創(chuàng)建用戶與組。
Jenkins權(quán)限如何分配:
- 開發(fā)組:只讀權(quán)限
- 運維組:管理員權(quán)限
- 測試組:執(zhí)行權(quán)限
配置權(quán)限
配置角色
圖片
分配權(quán)限
圖片
權(quán)限驗證
張三(管理員),下圖可以看到什么權(quán)限都有:
圖片
李四(只讀),下圖可以看到只有只讀權(quán)限:
圖片
張三(執(zhí)行權(quán)限),下圖可以看到是有執(zhí)行權(quán)限的:
圖片
配置Pipeline權(quán)限
需求說明
實際情況中,我們是通過Pipeline進行管理流水線的,接下來咱們針對Pipeline進行配置權(quán)限控制,詳情如下圖:
圖片
權(quán)限配置:
- 運維組:管理員權(quán)限
- 開發(fā)組:非生產(chǎn)環(huán)境只讀權(quán)限
- 測試組:非生產(chǎn)環(huán)境執(zhí)行權(quán)限
權(quán)限配置
以Ruoyi- Gateway為例,在Pipeline里配置權(quán)限:
DeployDev階段(修改submitter配置):
...
stage('DeployDev'){
steps {
echo "部署開發(fā)環(huán)境"
script {
def userInput = input (
message: '確定要發(fā)布到DEV環(huán)境嗎?',
parameters:[
choice(name: '操作', choices: ['發(fā)布', '跳過'])
],
ok: '確定',
submitter: 'ops,qa', // 配置ops,qa組即可
submitterParameter: 'APPROVER'
)
if (userInput['操作'] == '發(fā)布'){
echo "部署Dev環(huán)境開始"
....
DeployUat階段(修改submitter配置):
....
stage('DeployUat'){
steps {
echo "部署測試環(huán)境"
script {
def userInput = input (
message: '確定要發(fā)布到UAT環(huán)境嗎?',
parameters:[
choice(name: '操作', choices: ['發(fā)布', '跳過'])
],
ok: '確定',
submitter: 'ops,qa', // 配置ops,qa組即可
submitterParameter: 'APPROVER'
)
if (userInput['操作'] == '發(fā)布'){
echo "發(fā)布"
....
DeployGray階段(修改submitter配置):
stage('DeployGray'){
steps {
echo "部署灰度環(huán)境"
script {
def GraysMode = input (
message: '確定要灰度驗證嗎?',
parameters:[
choice(name: 'operation', choices: ['基于權(quán)重灰度','基于請求頭灰度','跳過'])
],
ok: '確定',
submitter: 'ops',
submitterParameter: 'APPROVER'
)
if (GraysMode['operation'] == '基于權(quán)重灰度'){
def WeightMode = input (
message: '請輸入權(quán)重比例!',
parameters:[
string(name: 'workload_weight',defaultValue: '',description: ''),
string(name: 'grayload_weight',defaultValue: '',description: '')
],
ok: '確定',
submitter: 'ops',
submitterParameter: 'APPROVER'
)
sh """
echo $pipeline_dir
echo "打印編排文件詳細(xì)信息"
if [ -e "$pipeline_dir/prod/$Project_Name/deployment-gray.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed "s/TAG/${Tag}/g"
cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi
echo "配置權(quán)重"
echo ${WeightMode['grayload_weight']}
if [ -e "$pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml | sed "s/WEIGHT-VALUE/${WeightMode['grayload_weight']}/g"
cat $pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml | sed "s/WEIGHT-VALUE/${WeightMode['grayload_weight']}/g" | /usr/bin/kubectl apply -f -
fi
"""
}
if (GraysMode['operation'] == '基于請求頭灰度'){
GrayHeaderMode = input (
message: '請輸入請求頭!',
parameters:[
string(name: 'header_key',defaultValue: '',description: ''),
string(name: 'header_value',defaultValue: '',description: '')
],
ok: '確定',
submitter: 'ops',
submitterParameter: 'APPROVER'
)
sh """
echo ${GrayHeaderMode['header_value']}
echo $pipeline_dir
echo "打印編排文件詳細(xì)信息"
if [ -e "$pipeline_dir/prod/$Project_Name/deployment-gray.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed "s/TAG/${Tag}/g"
cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi
echo "配置請求頭"
echo ${GrayHeaderMode['header_key']}
echo ${GrayHeaderMode['header_value']}
if [ -e "$pipeline_dir/prod/$Project_Name/ingress-gray-header.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | sed "s/header-key/${GrayHeaderMode['header_key']}/g" | sed "s/header-value/${GrayHeaderMode['header_value']}/g"
cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | sed "s/header-value/${GrayHeaderMode['header_key']}/g" | sed "s/header-value/${GrayHeaderMode['header_value']}/g" | /usr/bin/kubectl apply -f -
fi
"""
}
// 默認(rèn)模式為yes,如果跳過為no
if (GraysMode['operation'] == '跳過'){
GrayEnable='no'
}
}
}
DeployProd階段(修改submitter配置):
stage('DeployProd'){
steps {
echo "部署生產(chǎn)環(huán)境"
script {
def userInput = input (
message: '確定要發(fā)布到生產(chǎn)環(huán)境嗎?',
parameters:[
choice(name: '操作', choices: ['發(fā)布', '跳過'])
],
ok: '確定',
submitter: 'ops',
submitterParameter: 'APPROVER'
)
if (userInput['操作'] == '發(fā)布'){
echo "發(fā)布"
Namespace_Prod = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep namespace | awk -F ':' '{print \$2}'", returnStdout: true).trim()
DeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
Revsion_Prod = sh(script: "kubectl get deployment $DeploymentName -n ${Namespace_Prod} -o=jsnotallow='{.spec.template.spec.containers[*].image}' | awk -F ':' '{print \$NF}'", returnStdout: true).trim()
GrayDeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
GrayServiceName = sh(script: "cat $pipeline_dir/prod/$Project_Name/service-gray.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
GrayIngressName = sh(script: "cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
sh '''
echo $pipeline_dir
echo "開始部署生產(chǎn)環(huán)境"
echo "打印編排文件詳細(xì)信息"
if [ -e "$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g"
cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi
if [ -e "$pipeline_dir/prod/$Project_Name/service.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/service.yml
cat $pipeline_dir/prod/$Project_Name/service.yml | /usr/bin/kubectl apply -f -
fi
if [ -e "$pipeline_dir/prod/$Project_Name/ingress.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/ingress.yml
cat $pipeline_dir/prod/$Project_Name/ingress.yml | /usr/bin/kubectl apply -f -
fi
'''
if (GrayEnable == 'yes'){
sh """
kubectl delete deployment ${GrayDeploymentName} -n ${Namespace_Prod}
kubectl delete service ${GrayServiceName} -n ${Namespace_Prod}
kubectl delete ingress ${GrayIngressName} -n ${Namespace_Prod}
"""
}
} else {
echo "不發(fā)布"
}
}
}
post {
success {
wrap([$class: 'BuildUser']) {
lark (
robot: "2026ab67-7d07-46ec-a309-bebebaeaffbc",
type: "CARD",
title: "?? Jenkins 應(yīng)用發(fā)布成功",
text: [
"?? **應(yīng)用名稱**:[${JOB_NAME}](${JOB_URL})",
"?? **應(yīng)用環(huán)境**:Prod",
"?? **任務(wù)編號**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})",
"?? **發(fā)布狀態(tài)**: <font color='green'>成功</font>",
"?? **鏡像版本**: $Tag",
"?? **鏡像倉庫**: harbor.kubesre.com:8443/kubesre/$Project_Name",
"?? **執(zhí) 行 者**: ${env.BUILD_USER}",
"<at id=all></at>"
],
buttons: [
[
title: "更改記錄",
url: "${BUILD_URL}changes"
],
[
title: "控制臺",
type: "danger",
url: "${BUILD_URL}console"
]
]
)}
}
}
}
驗證
觸發(fā)流水線,開發(fā)組李四用戶登錄:
圖片
觸發(fā)流水線,測試組王五用戶登錄:
圖片
觸發(fā)流水線,運維組張三用戶登錄: