jenkins06-凭证管理

1、凭证管理介绍

1、为什么要管理凭证

  • 众所周知,在Jenkinsfile或部署脚本中使用明文密码会造成安全隐患。但是为什么还频繁出现明文密码被上传到GitHub上的情况呢?笔者认为有两个主要原因(当然,现实的原因可能更多):
    • (1)程序员或运维人员不知道如何保护密码。
    • (2)管理者没有足够重视,否则会给更多的时间让程序员或运维人员想办法隐藏明文密码。

2、凭证是什么

  • 凭证(cridential)是Jenkins进行受限操作时的凭据。比如使用SSH登录远程机器时,用户名和密码或SSH key就是凭证。而这些凭证不可能以明文写在Jenkinsfile中。Jenkins凭证管理指的就是对这些凭证进行管理。
    • 凭证可以用来存储需要密文保护的数据库密码、Gitlab密码、Docker私有仓库密码等,以便Jenkins可以和这些应用进行交互
  • 为了最大限度地提高安全性,在Jenkins master节点上对凭证进行加密存储(通过Jenkins实例ID加密),只有通过凭证ID才能在pipeline中使用,并且限制了将证书从一个Jenkins实例复制到另一个Jenkins实例的能力。
  • 因为所有的凭证都被存储在Jenkins master上,所以在Jenkins master上最好不要执行任务,以免被pipeline非法读取出来。那么在哪里执行pipeline呢?应该分配到Jenkins agent上执行。

2、创建凭证

2.1、安装插件

  • 可以使用"Credentials Binding"插件来管理Jenkins的凭证。
  • 安装插件后可以在"Jenkins --> Manage Jenkins"中看到多了两个选项。

2.2、创建凭证

  • 在创建凭证前,请确保当前用户有添加凭证的权限。
    • Jenkins --> Manage Jenkins --> Manage Credentials --> Stores scoped to Jenkins:[Jenkins] --> 全局凭证 (unrestricted) --> Add Credentials

  • Kind:选择凭证类型。
    • Username with password:用户名和密码
    • SSH Username with private key:使用SSH用户的公私密钥。使用时Jenkins会将SSH key复制到一个临时目录中,再将文件路径设置到一个变量中。
    • Secret text:需要保存的一个加密的文本串,如钉钉机器人或Github的API token。
    • Secret file:需要保密的文本文件。使用时Jenkins会将文件复制到一个临时目录中,再将文件路径设置到一个变量中,等构建结束后就会被删除。
    • Certificate:通过上传证书文件的方式。
  • Scope:凭证的作用域。有两种作用域:
    • Global,全局作用域。如果凭证用于pipeline,则使用此种作用域。
    • System,如果凭证用于Jenkins本身的系统管理,例如电子邮件身份验证、代理连接等,则使用此种作用域。
  • ID:在pipeline使用凭证的唯一标识。
  • 将凭证绑定到变量
    • 方法一:添加凭证后,安装Credentials Binding Plugin(credentials-binding)插件,通过其提供的withCredentials步骤就可以在pipeline中使用凭证了。
    • 方法二:声明式pipeline提供了credentials helper方法(只能在environment指令中使用)来简化凭证的使用。比方法一简明。
//方法一
withCredentials([usernameColonPassword(credentialsId: '18729147-da8c-49c5-8573-4bdb05984519', variable: 'BITBUCKET_CREDS')]) {
    echo "${BITBUCKET_CREDS}
}

//方法二
environment {
  BITBUCKET_CREDS = credentials('18729147-da8c-49c5-8573-4bdb05984519')
}

3、使用HashiCorp Vault插件管理凭证

  • 如果觉得Jenkins的凭证管理功能太弱,无法满足你的需求,则可以考虑使用HashiCorp Vault。
  • HashiCorp Vault是一款对敏感信息(密码、token、密钥等)进行存储,并进行访问控制的工具。它不仅可以存储敏感信息,还具有滚动更新、审计等功能。
  • HashiCorp Vault插件并没有提供pipeline步骤,提供此步骤的是Hashicorp Vault Pipeline 插件。
    • 但是它依赖的是2.138.1或以上的Jenkins版本。如果你的Jenkins版本低于2.138.1,但是又想用Hashicorp Vault Pipeline插件,可以将该插件的源码下载到本地,将pom.xml中的jenkins.version值从2.138.1修改成你的Jenkins版本,然后运行mvn clean package进行编译打包。如果没有报错, 则接着找到target/hashicorp-vaultpipeline.hpi进行手动安装即可。

4、在Jenkins日志中隐藏敏感信息

4.1、使用credentials helper方法或者withCredentials步骤

  • 如果使用的是credentials helper方法或者withCredentials步骤为变量赋值的,那么这个变量的值是不会被明文打印到Jenkins日志中的。除非使用以下方法:
pipeline {
    agent any
    stages {
        stage('Hidden information') {
            steps {
                script {
					def user      //定义变量,变量的作用域是script{}
					def passwd    //定义变量
                    withCredentials([usernamePassword(credentialsId: '18729147-da8c-49c5-8573-4bdb05984519', passwordVariable: 'passwdValue', usernameVariable: 'userValue')]) {
                        echo "${userValue}"      //输出的是密文:****
						echo "${passwdValue}"    //输出的是密文:****

                        user = "${userValue}"
                        passwd = "${passwdValue}"
						echo "${user}"          //输出的是密文:****
						echo "${passwd}"        //输出的是密文:****
                    }
					echo "${user}"              //输出的是明文:user01
					echo "${passwd}"            //输出的是明文:user01123
                }
            }
        }
    }
}

4.2、使用Masked Pass-word插件

  • 在没有使用credential的场景中,可以使用Masked Pass-word(mask-passwords)插件在日志中隐藏变量
    • 通过该插件提供的包装器,可以隐藏指定的敏感信息。
//该示例会抛出一个异常:java.lang.IllegalStateException: wrap step must be called with a body
pipeline {
    agent any
	environment {
		SECRET1 = "secret1"
		SECRET2 = "secret2"
		NOT_SECRET1 = "not secret"
	}
    stages {
        stage("read vault key") {
            steps {
				wrap(maskPasswords(varPasswordPairs: [
					[password: 'secret1', var: 's1'], 
					[password: 'secret2', var: 's2']
					]){
						echo "${SECRET1}"        //输出的是密文
						echo "${SECRET2}"        //输出的是密文
						echo "secret1"           //输出的是密文
						echo "${NOT_SECRET1}"    //输出的是明文
					}
				)
            }
        }
    }
}
  • 初次使用Masked Password插件很容易以为是使用s1和s2作为变量的,如 echo "${s1}和${s2}"。实际上,var参数只是用于方便在自由风格的Jenkins项目中区分不同的需要隐藏的密文。在pipeline中使用,它就没有存在的意义了。但是即使这样也不能省略它,必须传一个值。password参数传的是真正要隐藏的密文
  • 为什么echo "secret1"这条语句中并没有使用预定义的变量,也会被隐藏呢?这是由Masked Password插件的实现方式决定的。
  • Jenkins提供了ConsoleLogFilter接口,可以在日志打印阶段实现我们自己的业务逻辑。Masked Password插件实现了ConsoleLogFilter接口,然后利用正则表达式将匹配到的文本replaceAll成********。
  • MaskPasswordsBuildWrapper包装器除了支持varPasswordPairs参数,还支持varMask Regexes参数,使用自定义的正则表达式匹配需要隐藏的文本
//该示例会抛出一个异常:java.lang.IllegalStateException: wrap step must be called with a body
pipeline {
    agent any
	environment {
		SECRET1 = "abc-xxx"
		SECRET2 = "xxxabc-xxx"
		NOT_SECRET1 = "abcxxx"
	}
    stages {
        stage("read vault key") {
            steps {
				wrap(maskPasswords(
					varMaskRegexes: [[regex: 'abc-.*']]) {
						echo "${SECRET1}"        //输出的是密文
						echo "${SECRET1}"        //输出的是密文
						echo "${NOT_SECRET1}"    //输出的是明文
					}
				)
            }
        }
    }
}

4.3、设置全局级别的密文隐藏

  • 通过Masked Password插件还可以设置全局级别的密文隐藏。
  • Jenkins --> Manage Jenkins --> Configure System

1

#                                                                                                                          #
posted @ 2023-09-12 01:06  麦恒  阅读(66)  评论(0编辑  收藏  举报