百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

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

bigegpt 2024-09-18 08:03 3 浏览

背景

使用极狐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=#39;-----BEGIN RSA PRIVATE KEY-----\n'
      SSH_PRIVATE_KEY=$SSH_PRIVATE_KEY$SSH_PRIVATE_KEY1#39;\n'
      SSH_PRIVATE_KEY=$SSH_PRIVATE_KEY$SSH_PRIVATE_KEY2#39;\n'
      SSH_PRIVATE_KEY=$SSH_PRIVATE_KEY$SSH_PRIVATE_KEY3#39;\n'
      SSH_PRIVATE_KEY=$SSH_PRIVATE_KEY#39;-----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=#39;-----BEGIN RSA PRIVATE KEY-----\n'$SSH_PRIVATE_KEY#39;\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在管理密钥这块功能的完善。

相关推荐

方差分析简介(方差分析通俗理解)

介绍方差分析(ANOVA,AnalysisofVariance)是一种广泛使用的统计方法,用于比较两个或多个组之间的均值。单因素方差分析是方差分析的一种变体,旨在检测三个或更多分类组的均值是否存在...

正如404页面所预示,猴子正成为断网元凶--吧嗒吧嗒真好吃

吧嗒吧嗒,绘图:MakiNaro你可以通过加热、冰冻、水淹、模塑、甚至压溃压力来使网络光缆硬化。但用猴子显然是不行的。光缆那新挤压成型的塑料外皮太尼玛诱人了,无法阻挡一场试吃盛宴的举行。印度政府正...

Python数据可视化:箱线图多种库画法

概念箱线图通过数据的四分位数来展示数据的分布情况。例如:数据的中心位置,数据间的离散程度,是否有异常值等。把数据从小到大进行排列并等分成四份,第一分位数(Q1),第二分位数(Q2)和第三分位数(Q3)...

多组独立(完全随机设计)样本秩和检验的SPSS操作教程及结果解读

作者/风仕在上一期,我们已经讲完了两组独立样本秩和检验的SPSS操作教程及结果解读,这期开始讲多组独立样本秩和检验,我们主要从多组独立样本秩和检验介绍、两组独立样本秩和检验使用条件及案例的SPSS操作...

方差分析 in R语言 and Excel(方差分析r语言例题)

今天来写一篇实际中比较实用的分析方法,方差分析。通过方差分析,我们可以确定组别之间的差异是否超出了由于随机因素引起的差异范围。方差分析分为单因素方差分析和多因素方差分析,这一篇先介绍一下单因素方差分析...

可视化:前端数据可视化插件大盘点 图表/图谱/地图/关系图

前端数据可视化插件大盘点图表/图谱/地图/关系图全有在大数据时代,很多时候我们需要在网页中显示数据统计报表,从而能很直观地了解数据的走向,开发人员很多时候需要使用图表来表现一些数据。随着Web技术的...

matplotlib 必知的 15 个图(matplotlib各种图)

施工专题,我已完成20篇,施工系列几乎覆盖Python完整技术栈,目标只总结实践中最实用的东西,直击问题本质,快速帮助读者们入门和进阶:1我的施工计划2数字专题3字符串专题4列表专题5流程控制专题6编...

R ggplot2常用图表绘制指南(ggplot2绘制折线图)

ggplot2是R语言中强大的数据可视化包,基于“图形语法”(GrammarofGraphics),通过分层方式构建图表。以下是常用图表命令的详细指南,涵盖基本语法、常见图表类型及示例,适合...

Python数据可视化:从Pandas基础到Seaborn高级应用

数据可视化是数据分析中不可或缺的一环,它能帮助我们直观理解数据模式和趋势。本文将全面介绍Python中最常用的三种可视化方法。Pandas内置绘图功能Pandas基于Matplotlib提供了简洁的绘...

Python 数据可视化常用命令备忘录

本文提供了一个全面的Python数据可视化备忘单,适用于探索性数据分析(EDA)。该备忘单涵盖了单变量分析、双变量分析、多变量分析、时间序列分析、文本数据分析、可视化定制以及保存与显示等内容。所...

统计图的种类(统计图的种类及特点图片)

统计图是利用几何图形或具体事物的形象和地图等形式来表现社会经济现象数量特征和数量关系的图形。以下是几种常见的统计图类型及其适用场景:1.条形图(BarChart)条形图是用矩形条的高度或长度来表示...

实测,大模型谁更懂数据可视化?(数据可视化和可视化分析的主要模型)

大家好,我是Ai学习的老章看论文时,经常看到漂亮的图表,很多不知道是用什么工具绘制的,或者很想复刻类似图表。实测,大模型LaTeX公式识别,出乎预料前文,我用Kimi、Qwen-3-235B...

通过AI提示词让Deepseek快速生成各种类型的图表制作

在数据分析和可视化领域,图表是传达信息的重要工具。然而,传统图表制作往往需要专业的软件和一定的技术知识。本文将介绍如何通过AI提示词,利用Deepseek快速生成各种类型的图表,包括柱状图、折线图、饼...

数据可视化:解析箱线图(box plot)

箱线图/盒须图(boxplot)是数据分布的图形表示,由五个摘要组成:最小值、第一四分位数(25th百分位数)、中位数、第三四分位数(75th百分位数)和最大值。箱子代表四分位距(IQR)。IQR是...

[seaborn] seaborn学习笔记1-箱形图Boxplot

1箱形图Boxplot(代码下载)Boxplot可能是最常见的图形类型之一。它能够很好表示数据中的分布规律。箱型图方框的末尾显示了上下四分位数。极线显示最高和最低值,不包括异常值。seaborn中...