How can I extract secrets using GitHub Actions?
SshGithub ActionsSecretsmanagerSsh Problem Overview
I have a fairly basic scenario. I made a dedicated ssh key for this purpose and added it to my repository secrets.
-
Code gets pushed to
master
-
GitHub action uploads it to server using
ssh
by doingecho "${{ secrets.SSH_KEY }}" > key
. -
After that I can use this key to connect to my server e.g.
ssh -i key [email protected] lsb_release -a
The problem is that for some reason GitHub actions cannot write it to file, it writes characters ***
instead of the actual secret value into the file. Therefore obviously I cannot connect to my server.
How can I connect with ssh using this secret? Is there a way to connect without using a file? Can someone who did this common scenario using GitHub actions shed some light?
Ssh Solutions
Solution 1 - Ssh
GitHub Actions should be able to write a secret to a file this way. The reason you see the stars is that the log is filtered, so if a secret would be logged, it's replaced in the log with three asterisks instead. This is a security measure against an accidental disclosure of secrets, since logs are often publicly available.
However, it's a good idea to avoid writing the secret to the log anyway if possible. You can write your command like this so you don't write the secret to the log:
- run: 'echo "$SSH_KEY" > key'
shell: bash
env:
SSH_KEY: ${{secrets.SSH_KEY}}
All you'll see in the log is echo "$SSH_KEY" > key
, not the secret or any asterisks.
Note that you do want quotes here, since the >
character is special to YAML.
If this doesn't work to log into your server, there's likely a different issue; this technique does work for writing secrets in the general case.
Solution 2 - Ssh
I found a way to do this. We used GatsbyJS for a project and it relies on a
.env.production file for the env variables. I tried to pass them as
env:
to the github action, but that didn't work and they were ignored.
Here is what I did. I base 64 encoded the .env.production file:
base64 -i .env.production
Added the output to an env variable in github action. Then in my action I do:
echo ${{ secrets.ENV_PRODUCTION_FILE }} | base64 -d > .env.production
This way the contents of my .env.production file ended being written to the machine that executes the github action.
Solution 3 - Ssh
I used sed in a GHA to replace a TOKEN in a file like this :
run: |-
sed -i "s/TOKEN/${{secrets.MY_SECRET}}/g" "thefile"
The file looks like this:
credentials "app.terraform.io" {
token = "TOKEN"
}
Solution 4 - Ssh
The good solution is to use gpg for encrypting the key, adding it to a repo and decrypting it on the server using passphrase. The passprase should be stored as github project secret of course.
More info how I did it here: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets
Solution 5 - Ssh
Here is how to solve your actual problem of securely logging into an SSH server using a secret stored in GitHub Actions, named GITHUB_ACTIONS_DEPLOY
.
Let's call this "beep", because it will cause an audible bell on the server you login to. Maybe you use this literally ping a server in your house when somebody pushes code to your 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 }}
If, actually you are using SSH as part of a rsync push task, here is how to do that:
- 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"