如何使用GitHub Actions提取秘密信息?

71
我有一个相当基本的场景。我为此制作了专用的ssh密钥,并将其添加到我的存储库机密中。
  1. 代码被推送到master

  2. Github操作使用ssh将其上传到服务器,方法是执行echo "${{ secrets.SSH_KEY }}" > key

  3. 之后,我可以使用此密钥连接到我的服务器,例如:ssh -i key devops@myserver.com lsb_release -a

问题在于,由于某种原因,GitHub操作无法将其写入文件,它会将字符***而不是实际秘密值写入文件。因此,显然我无法连接到我的服务器。
如何使用此密钥进行ssh连接?是否有一种不使用文件的方法进行连接?是否有人使用GitHub操作完成这种常见场景并能提供帮助?

我认为这更多是因为GitHub停止记录秘密信息,而不是实际上没有秘密信息。 - BenKoshy
8个回答

99

这是如何通过GitHub Actions将一个秘密写入文件的方法。如果在日志中记录了该秘密,将用三个星号替换它,这是一种针对意外泄露机密的安全措施,因为日志通常是公开的,所以你看到星星。

然而,如果可能的话,最好避免将秘密写入日志。你可以像这样编写命令,以便不将秘密写入日志:

    - run: 'echo "$SSH_KEY" > key'
      shell: bash
      env:
        SSH_KEY: ${{secrets.SSH_KEY}}

在日志中你只会看到 echo "$SSH_KEY" > key,而不是密码或任何星号。

请注意,在这里你确实需要引号,因为 > 字符对于YAML来说是特殊的。

如果这不能让你登录服务器,那么可能有不同的问题;通常情况下,这种技术可以用于写入密码。


8
我曾经遇到过困难,发现我漏了双引号。echo "$SSH_KEY"。重要的是转义多行字符。 - Shinebayar G
4
我无法验证。我已更新 run'echo "$SSH_KEY" > key && tail key',但仍显示 ***。操作运行程序如何知道文件应该仍然加密? - Dida
@Dida动作执行器知道所有的机密。如果这些机密需要输出,它会用星号替换。但是它无法检测机密的变体。因此,ROT13有助于为调试或紧急恢复输出机密。 - koppor

19

我找到了一种方法来实现这个。我们在一个项目中使用了 GatsbyJS,它依赖于一个 .env.production 文件来存储环境变量。我试图将它们作为 env: 传递给 Github Action,但是失败了,被忽略了。

这是我做的事情。我对 .env.production 文件进行了 base64 编码:

base64 -i .env.production

在 Github Action 中将输出添加到环境变量中。然后在我的操作中执行:

echo ${{ secrets.ENV_PRODUCTION_FILE }} | base64 -d > .env.production

这样一来,我的.env.production文件的内容就被写入了执行github action的机器上。


5

你可以通过在 Python shell 中循环遍历 secret 来解码它,就像这样:

    - name: Set env as secret
      env:
        MY_VAL: ${{ secrets.SUPER_SECRET }}
      run: |
        import os
        data = open("file", "w")
        for q in (os.getenv("MY_VAL")):
          print(q)
          data.write(q)
      shell: python

这将会同时将每个字符打印到标准输出和存储在名为file的文件中。stdout的输出应该像这样,而文件中应该保存着秘密字符串。

s
e
c
r
e
t

它每天在我的存储库上运行,以检查它是否仍在工作:这里


对我来说,需要将 print q 改为 print(q) - tjheslin1

3

对其进行编码和解码

- run: 'echo "$SSH_KEY" | base64'
       shell: bash
       env:
         SSH_KEY: ${{ secrets.PRIVATE_KEY }}

并将其解码:echo "<编码字符串>" | base64 -d


1
我在一个GHA中使用sed命令替换文件中的TOKEN,方法如下:

run: |-
     sed -i "s/TOKEN/${{secrets.MY_SECRET}}/g" "thefile"

文件看起来像这样:

    credentials "app.terraform.io" {
       token = "TOKEN"
    }

0
echo ${{secrets.AWS_ACCESS_KEY_ID}} | sed 's/./& /g'

0
这里是如何解决您实际的问题,即使用存储在 GitHub Actions 中的秘密(名为 GITHUB_ACTIONS_DEPLOY)安全登录到 SSH 服务器。
我们称之为“beep”,因为它会在您登录的服务器上发出可听的铃声。也许您会使用它来直接ping您家中的服务器,当有人向您的repo推送代码时。
      - name: Beep    
        # if: github.ref == 'refs/heads/XXXX' # Maybe limit only this step to some branches
        run: |
          eval $(ssh-agent)
          ssh-add - <<< "$SSH_KEY"
          echo "* ssh-rsa XXX" >> /tmp/known_hosts # Get from your local ~/.ssh/known_hosts or from ssh-keyscan
          ssh -o UserKnownHostsFile=/tmp/known_hosts user@example.com "echo '\a'"
        env:
          SSH_KEY: ${{ secrets.PMT_GITHUB_ACTIONS_DEPLOY }}

如果你正在使用SSH作为rsync推送任务的一部分,那么以下是如何实现的方法:
      - name: Publish    
        if: github.ref == 'refs/heads/XXX'
        run: |
          eval $(ssh-agent)
          ssh-add - <<< "$SSH_KEY"
          echo "* ssh-rsa XXX" >> /tmp/known_hosts
          rsync $FROM user@server:
        env:
          SSH_KEY: ${{ secrets.GITHUB_ACTIONS_DEPLOY }}
          RSYNC_RSH: "ssh -o UserKnownHostsFile=/tmp/known_hosts"

-1

2
就像链接页面上所提到的那样,这只是针对大型密钥的一种解决方法,并不能保护您免受在日志中暴露密钥的风险。在我看来,@bk2204 应该成为被采纳的答案。 - Stefan Haberl
如果你这样做,你的秘密就无法保护不被打印到日志中。这不是“正确的解决方案”,如果你的秘密超过了64kb,那么这是一种有些危险的解决方法。 - René Roth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接