CI/CD 构建中能保护好 SSHKEY吗?

目录

背景

方案

编码存储

逐行存储

合并存储

打马赛克

结论


背景


使用极狐GitLab CI/CD,在部署方面,主要有两种方式:

部署到K8S集群

  • Push模式:流水线通过kubectl执行命令部署,这需要把K8S的权限给流水线,存在安全风险

  • Pull模式:使用极狐GitLab Agent for Kubernetes 或ArgoCD,通过GitOps的方式“监听”极狐GitLab的变化,触发部署

图片

部署到服务器

目前仍有不少企业因为行业性质或者场景所限,没有使用K8S等云原生技术,还在采用传统的服务器方式进行部署。一般使用ssh、scp、rsync等命令部署到服务器。极狐GitLab也提供了基于SSH keys的部署。详见:Using SSH keys with JiHu GitLab CI/CD。

需要说明的是,如果是使用专用的编译机进行编译构建,然后部署到指定的服务器,只需要实现编译机和部署服务器的免密SSH登陆即可,相对简单。但如果使用容器进行编译构建,然后部署到服务器,就需要按照上面文档中提到的,配合极狐GitLab CI/CD环境变量,将SSH_PRIVATE_KEY等变量存储到极狐GitLab Project、Group或Instance中,实现复用。且可以通过极狐GitLab CI/CD环境变量的Mask设置,掩藏这些变量在CI/CD日志中的显示。详见:极狐GitLab CI/CD variables。

但遗憾的是Mask功能目前是有限制的,对于SSH_PRIVATE_KEY这种多行的变量无法直接使用Mask功能。这样开发人员就可以在.gitlab-cti.yml文件的脚本中执行echo $SSH_PRIVATE_KEY,在流水线的日志中输出SSH Keys,存在密钥泄露风险。

The value of the variable must:

  • Be a single line.

  • Be 8 characters or longer, consisting only of:

Characters from the Base64 alphabet (RFC4648).

The @ and : characters (In GitLab 12.2 and later).

The . character (In GitLab 12.10 and later).

The ~ character (In GitLab 13.12 and later).

  • Not match the name of an existing predefined or custom CI/CD variable.

图片

图片

这个问题在极狐GitLab的Issue上挂了有一年多 ,看样子短时间没法解决。有没有其他方式Mask SSH_PRIVATE_KEY?于是开始了各种折腾。

方案


编码存储

SSH Keys不能直接Mask,但Mask的要求里面是支持Base64的。所以把SSH Keys先用Base64编码,存到CI/CD环境变量中,这样就可以Mask了,然后在.gitlab-ci.yml中解码,就可以在不影响功能的前提下实现效果。看看操作步骤:

  • Base64编码SSH_PRIVATE_KEY

# 输入示例   
echo "-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsWchpjSe6HW8dS/DdmokMqET2+eCvD8ysOeju3Ur3cbXtZF1
*****
pbPfj6i+faMGc1wbP+Svh8P5bcWTJZvZcP87D/HRmSFz6xcT014=
-----END RSA PRIVATE KEY-----" | base64 
# 输出示例:Base64编码
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBc1djaHBqU2U2SFc4ZFMvRG
*****
cjRzNmVjY25ZRUZxb1NSTGVNU2xMb1ZreU5VZEpQUjJRa1djQzRkVDVQZwpwYlBmajZpK2ZhTUdjMXdiUCtTdmg4UDViY1dUSlp2WmNQODdEL0hSbVNGejZ4Y1QwMTQ9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
  • 将输出的Base64编码作为SSH_PRIVATE_KEY存储在极狐GitLab CI/CD环境变量,并Mask

图片

  • 修改.gitlab-ci.yml

before_script:- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'- eval $(ssh-agent -s)# - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -- echo "$SSH_PRIVATE_KEY" | base64 -d | ssh-add -- mkdir -p ~/.ssh- chmod 700 ~/.ssh- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts- chmod 644 ~/.ssh/known_hosts
  • 运行测试,大功告成

这看上去是个不错的方案,但真的保证了SSH_PRIVATE_KEY安全么?我们本着Geek(作死)精神,测试一下:

  • 修改.gitlab-ci.yml,通过各种方式看看能不能打印出SSH_PRIVATE_KEY

before_script:- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'- eval $(ssh-agent -s)# --- test begin ---- echo "$SSH_PRIVATE_KEY" - echo "$SSH_PRIVATE_KEY" >> output.txt- cat output.txt- echo "$SSH_PRIVATE_KEY" | base64 -d# --- test end ---- echo "$SSH_PRIVATE_KEY" | base64 -d | ssh-add -- mkdir -p ~/.ssh- chmod 700 ~/.ssh- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts- chmod 644 ~/.ssh/known_hosts
  • 运行测试

图片

直接输出SSH_PRIVATE_KEY是被Mask了,但执行echo "$SSH_PRIVATE_KEY" | base64 -d,居然把SSH_PRIVATE_KEY打印了出来,所以这个方法还是存在一定的问题。

逐行存储

SSH Keys头部和尾部的-----BEGIN RSA PRIVATE KEY-----、-----END RSA PRIVATE KEY-----不能Mask,但里面的内容,每一行可以单独作为一个环境变量存储并Mask,使用的时候再进行拼接,看看操作步骤:

  • 将SSH_PRIVATE_KEY每一行拆分成一个变量,进行存储,有多少行就要存多少变量,为了偷懒,此处只列了3行,实际上我这个SSH_PRIVATE_KEY除去头尾,有26行……

图片

图片

  • 修改.gitlab-ci.yml,并加入一些测试

before_script:- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'- eval $(ssh-agent -s)# 拼接SSH_PRIVATE_KEY- |SSH_PRIVATE_KEY=$'-----BEGIN RSA PRIVATE KEY-----\n'SSH_PRIVATE_KEY=$SSH_PRIVATE_KEY$SSH_PRIVATE_KEY1$'\n'SSH_PRIVATE_KEY=$SSH_PRIVATE_KEY$SSH_PRIVATE_KEY2$'\n'SSH_PRIVATE_KEY=$SSH_PRIVATE_KEY$SSH_PRIVATE_KEY3$'\n'SSH_PRIVATE_KEY=$SSH_PRIVATE_KEY$'-----END RSA PRIVATE KEY-----'# --- test begin ---- echo "$SSH_PRIVATE_KEY" - echo "$SSH_PRIVATE_KEY" >> output.txt- cat output.txt# --- test end ---- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -- mkdir -p ~/.ssh- chmod 700 ~/.ssh- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts- chmod 644 ~/.ssh/known_hosts
  • 运行测试

图片

方案行是行,实际操作起来要存26个变量然后还要拼起来,实在是太土了,能不能减少行数,存一行。

合并存储

Mask不支持空格,只支持@:.~,那我们尝试把SSH_PRIVATE_KEY除了头尾的部分合并成一行,把换行符替换成支持的符号,如.,然后再与头尾进行拼接。操作步骤如下:

  • 合并SSH_PRIVATE_KEY

# 输入示例
echo "-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsWchpjSe6HW8dS/DdmokMqET2+eCvD8ysOeju3Ur3cbXtZF1
*****
pbPfj6i+faMGc1wbP+Svh8P5bcWTJZvZcP87D/HRmSFz6xcT014=
-----END RSA PRIVATE KEY-----" | tr -d '\r' | tr "\n" "."#输出示例
-----BEGIN RSA PRIVATE KEY-----.MIIEogIBAAKCAQEAsWchpjSe6HW8dS/DdmokMqET2+eCvD8ysOeju3Ur3cbXtZF1.LMb2Rq68/FPXsteLr4Y1ECKoy/YhFpyDw1h3cLm2WBUtRjt/Tq0ASbQCWAVkDsmx.uy28WofwfEKktzy3FmDSCXbvcOQgjChAmMbALWyH****=.-----END RSA PRIVATE KEY-----.
  • 将除头尾部分存入环境变量并Mask

图片

  • 修改.gitlab-ci.yml

before_script:- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'- eval $(ssh-agent -s)# 拼接SSH_PRIVATE_KEY- SSH_PRIVATE_KEY=$'-----BEGIN RSA PRIVATE KEY-----\n'$SSH_PRIVATE_KEY$'\n-----END RSA PRIVATE KEY-----'# --- test begin ---- echo "$SSH_PRIVATE_KEY" - echo "$SSH_PRIVATE_KEY" >> output.txt- cat output.txt- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | tr "." "\n" # --- test end ---- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | tr "." "\n" | ssh-add -- mkdir -p ~/.ssh- chmod 700 ~/.ssh- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts- chmod 644 ~/.ssh/known_hosts
  • 运行测试

图片

和“编码存储”的方案一样,跑的通,但依旧可以通过对应的方式,打印出SSH_PRIVATE_KEY。

到这里,可以隐约猜到Mask变量的原理是简单做了一个是否包含字符串的判断。如果与环境变量的值匹配就显示[MASKED],如果不匹配就直接将变量显示出来。这也是为什么目前只允许值是单行且没有太多特殊符号的环境变量才可以MASK的原因。

打马赛克

为了验证上一步留下来的猜想,我设计了一个实验:

  • 恢复环境变量中的SSH_PRIVATE_KEY为原始内容,并且不做Mask,当然也无法Mask

图片

  • 新建一个环境变量,值为SSH_PRIVATE_KEY的一部分内容,这里设置的是SSH_PRIVATE_KEY内容的第一行,然后设置为Mask

图片

  • 恢复.gitlab-ci.yml文件,需要注意的是这里面没有任何关于MOSAIC环境变量的使用

before_script:- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'- eval $(ssh-agent -s)- echo "$SSH_PRIVATE_KEY" - echo "$SSH_PRIVATE_KEY" | tr -d '\r'| ssh-add -- mkdir -p ~/.ssh- chmod 700 ~/.ssh- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts- chmod 644 ~/.ssh/known_hosts
  • 运行测试

图片

正如猜想一样,即便没有使用MOSAIC环境变量,但它依然作为判断是否包含字符串而被执行了。

利用这个特性,我们可以通过设置几个马赛克变量,给SSH_PRIVATE_KEY的部分内容打码,就可以一定程度上防止SSH_PRIVATE_KEY的泄露。

结论


作为一般场景下使用,上面的四种方式任意选一个都可以实现基本的安全防护,正所谓防君子不防小人。如果要进一步提高安全性,还是如官方所说,上专业的密钥管理工具,如Vault,或者期待下极狐GitLab在管理密钥这块功能的完善。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/204511.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2. 两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外,这两个数都不会以 0 …

C#面向对象

过程类似函数只能执行没有返回值 函数不仅能执行,还可以返回结果 1、面向过程 a 把完成某一需求的所有步骤 从头到尾 逐步实现 b 根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数 c 最后完成的代码就是顺序的调用不同的函数 特点 1、…

vue项目多个不同的服务器请求地址管理

vue项目多个不同的服务器请求地址管理 在vue项目开发过程中,获取不同的数据可能会出现需要请求多个不同服务器地址的域名,这个时候需要对不同域名的请求地址进行管理以及跨域的代理。 一、单服务器域名地址的跨域代理和请求配置: 跨域配置&…

【算法萌新闯力扣】:卡牌分组

力扣热题:卡牌分组 一、开篇 今天是备战蓝桥杯的第22天。这道题触及到我好几个知识盲区,以前欠下的债这道题一并补齐,哈希表的遍历、最大公约数与最小公倍数,如果你还没掌握,这道题练起来! 二、题目链接:…

为Oracle链接服务器使用分布式事务

1 现象 在SQL Server中创建指向Oracle的链接服务器,SQL语句在事务中向链接服务器插入数据。返回链接服务器无法启动分布式事务的报错。 2 解决 在Windows平台下,SQL Server依赖分布式事务协调器(MSDTC)来使用分布式事务&#xff0…

【ArcGIS Pro微课1000例】0038:基于ArcGIS Pro的人口密度分析与制图

文章目录 一、人口密度二、人口密度分析1. 点密度分析2. 核密度分析三、结果比对一、人口密度 人口密度是指单位土地面积上居住的人口数,通常以每平方千米或每公顷内的常住人口为单位计算。人口密度同资源、经济密切结合,因此,科学准确地分析人口密度的分布情况,对合理制定…

亚信科技AntDB数据库与库瀚存储方案完成兼容性互认证

近日,亚信科技AntDB数据库与苏州库瀚信息科技有限公司自主研发的RISC-V数据库存储解决方案进行了产品兼容测试。经过双方团队的严格测试,亚信科技AntDB数据库与库瀚数据库存储解决方案完全兼容、运行稳定。除高可用性测试外,双方进一步开展TP…

0 NLP: 数据获取与EDA

0数据准备与分析 二分类任务,正负样本共计6W; 数据集下载 https://github.com/SophonPlus/ChineseNlpCorpus/raw/master/datasets/online_shopping_10_cats/online_shopping_10_cats.zip 样本的分布 正负样本中评论字段的长度 ,超过500的都…

Redis深入理解-主从架构下内核数据结构、主从同步以及主节点选举

Redis 主从挂载后的内核数据结构分析 主节点中,会通过 clusteNode 中的 slaves 来记录该主节点包含了哪些从节点,这个 slaves 是一个指向 *clusterNode[] 数组的数据结构从节点中,会通过 clusterNode 中的 slaveof 来记录该从节点属于哪个主…

webpack项目工程初始化

一、初始化项目 默认系统已经安装node //初始化 pnpm init//安装webpack pnpm i -D webpack webpack-cli 新建一个index.html的入口文件 新建一个src文件存放js代码,src里面新建一个index.js package.josn配置打包命令 {"name": "webpack-cs&q…

【数据结构】八大排序(二)

目录 前言: 冒泡排序 冒泡排序代码实现 冒泡排序特性总结 快速排序 单趟排序hoare版本 单趟排序挖坑法 单趟排序快慢指针法 快速排序整体概览 快排的优化 三数取中法选key 小区间优化 前言: 上文介绍了直接插入排序,希尔排序&…

2 线、3 线和 4 线 RTD 配置之间有什么区别?

电阻温度检测器 (RTD) 是温度传感器的一种,由于其准确性、可重复性和稳定性而广泛应用于各种工业应用。这些设备通过感测材料温度变化时电阻的变化来测量温度。 RTD 探头有多种配置,包括 2 线、3 线和 4 线型号。这些类型之间存在显着差异,在…

浅析函数防抖节流

防抖和节流都是前端开发中常用的优化性能的技术。 一、定义 防抖: 防抖指的是在事件触发后,在规定的时间内若再次触发,则重新计时,直到规定时间内没有再次触发事件,才执行事件处理。这样可以避免在短时间内频繁地触发…

ArrayList与顺序表的简单理解

前言----list 在集合框架中,List是一个接口,继承自Collection。Collection也是一个接口,该接口中规范了后序容器中常用的一些方法,具体如下所示: Iterable也是一个接口,表示实现该接口的类是可以逐个元素进…

编写安全 JavaScript 代码的最佳实践

编写安全 JavaScript 代码的最佳实践 JavaScript 的动态特性使其成为事实上的浏览器语言和世界上最流行的编程语言。 JS 最受欢迎的有用功能之一是即时分析。这意味着浏览器在下载内容的同时执行代码,这显然有其优势。然而,这种程度的自由也伴随着问题…

Redis实战命令

实战命令 单值缓存 set key value get key 对象缓存 (1)set user:1 value(json格式) (2)mset user:1:name junfeng user:1:age 18 mget user:1:name user:1:age 分布式锁 分布式锁解决了什么问题? 分布式锁解…

[带余除法寻找公共节点]二叉树

二叉树 题目描述 如上图所示,由正整数1, 2, 3, ...组成了一棵无限大的二叉树。从某一个结点到根结点(编号是1的结点)都有一条唯一的路径,比如从10到根结点的路径是(10, 5, 2, 1),从4到根结点的路径是(4, 2, 1)&#x…

【中间件】服务化中间件理论intro

中间件middleware 内容管理 intro服务化middleware架构注册中心intro服务治理系统intro 本文主要intro服务化中间件的探讨 去年cfeng写了一篇博客走马观花般阐述了Spring Cloud下面的各种中间件,连深入使用都谈不上,只能说intro,在实际work中…

linux用户身份切换su和 sudo

su 切换root,但是,环境变量是之前用户的 可以看到利用su切换,根目录还是pro1的 su - 连同环境一起切换成root,切换后工作目录都不一样了,看输入内容左侧信息,和第一个图片比较 -c仅执行一次命令&#xff0…

面试必须要知道的MySQL知识--索引

10 索引 10.1 数据页存储结构 10.1.1 数据页的各个部分 在讲索引之前,让我们看看一个单独的数据页是什么样子的 去除掉一些我们不太需要那么关注的部分后,简化如下: 也就是说平时我们在一个表里插入的一行一行的数据会存储在数据页里&#…