I sprained my hand yesterday so won’t be able to type in detail for this one.
I have a Runbook I am running on a Linux Hybrid Runbook Worker and it is quite sensitive in that it handles passwords etc. and I wanted to lock it down such that only the Runbook script I am creating will get executed and no one (not even anyone with access to the Automation Account) will be able to modify the script or create a new one and get access to the passwords etc. The way to do this is sign the Runbook script and have Automation only execute validly signed scripts.
I was expecting this to be more work (like getting a code signing cert maybe) but it turned out to be quite straightforward.
Official MS instructions are at this link. Those assume you have an Agent based Hybrid Runbook Worker while I have an Extension based one. Also, those instructions generate the private and public keys on the Hybrid Runbook Worker VM itself and sign things there, whereas I wanted to keep the private key on my machine so I can easily sign stuff and upload to Azure.
First step: generate a new key for yourself. I am on macOS so I do the following: gpg --generate-key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
$ gpg --generate-key gpg (GnuPG) 2.3.3; Copyright (C) 2021 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Note: Use "gpg --full-generate-key" for a full featured key generation dialog. GnuPG needs to construct a user ID to identify your key. Real name: Rakhesh Sasidharan Email address: rakhesh@heaven.org You selected this USER-ID: "Rakhesh Sasidharan <rakhesh@heaven.org>" Change (N)ame, (E)mail, or (O)kay/(Q)uit? O We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. gpg: key B54A63395AD39CC8 marked as ultimately trusted gpg: revocation certificate stored as '/Users/rakhesh/.gnupg/openpgp-revocs.d/5A1331EF88C58BB4718AA369B54A63395AD39CC8.rev' public and secret key created and signed. pub ed25519 2021-11-01 [SC] [expires: 2023-11-01] 5A1331EF88C58BB4718AA369B54A63395AD39CC8 uid Rakhesh Sasidharan <rakhesh@heaven.org> sub cv25519 2021-11-01 [E] [expires: 2023-11-01] |
Now let’s export the public key:
1 |
gpg --armor --export rakhesh@heaven.org |
This will output the key in ASCII to the terminal. We’ll come back to this.
On the Worker VM run the following:
1 |
sudo su - hweautomation |
I am using a different account compared to the official instructions because this is what Extension based Workers use. Stick with the official instructions if you are Agent based.
I copy pasted the key from my machine to a file – let’s call it import.gpg
. And I imported it:
1 |
gpg --import import.gpg |
So now my Worker has my public key. I can verify it thus:
1 |
gpg --list-keys |
Next I opened up this file in an editor: /home/hweautomation/state/worker.conf
and changed these lines:
1 2 3 4 5 6 7 |
[worker-optional] proxy_configuration_path = /home/hweautomation/state/proxy.conf state_directory_path = /home/hweautomation/state gpg_public_keyring_path = /home/hweautomation/.gnupg/pubring.kbx bypass_certificate_verification = True debug_traces = True enforce_runbook_signature_validation = True |
As you can see I am enabling signature validation and also telling the worker the location to my public keyring. The latter contains my public key of course.
That’s it, now I can sign my script on the work machine thus:
1 |
gpg --clear-sign --local-user rakhesh@heaven.org <script> |
This creates a file with a .asc
extension. Example:
1 2 3 4 5 6 7 8 9 10 11 12 |
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 Write-Host "test" -----BEGIN PGP SIGNATURE----- iJUEARYKAD0WIQRaEzHviMWLtHGKo2m1SmM5WtOcyAUCYX+Y1R8ccmFraGVzaC5z YXNpZGhhcmFuQGRlbnRvbnMuY29tAAoJELVKYzla05zIVc0BAIjhjl9ScBg5IXz7 dQx0ogYP8j7hGxOQuiLwaGDLdBteAQCj60Ue9w45WIoA5EmfJPOWQrqLNx8kQtm1 Aw7zWc/KDQ== =oFJ/ -----END PGP SIGNATURE----- |
Notice the one line of PowerShell surrounded by a signature. This is signed with my private key, that’s only present on my work machine so no one else should be able to sign a script as me. If I run this in the Runbook it will verify the signature and only then execute it.
If you want to see this in action the following command should help:
1 |
tail -f /home/hweautomation/run/worker.log |
It shows the output of the Worker log file and you can see it checking for signatures when you submit something. If the signature doesn’t match the Portal throws an error and you can see it in these logs too.