Using Jenkins CasC to approve Script Signatures

Jenkins setup:


Hello,

I am using Jenkins CasC as well as some SSH agents via AWS SSM.

In order to connect to these agents, I needed to write an ssh config file placed at ~/.ssh/config with the following contents.

# SSH over Session Manager
host i-* mi-*
    ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

I also needed to create a helper script that would transfer the Jenkins.jar file to the remote host and execute it, ref Command Agent Launcher

sshHelper.sh

#!/usr/bin/env bash
# $1 = hostname (mi-abcdefg)
# $2 = username for host
set -euxo pipefail
# get copy of agent.jar
local_tmp_dir=$(mktemp -d -t agent-XXXX)
cd "$local_tmp_dir"
agent_fname="${STAGE}-agent.jar"
wget http://127.0.0.1:8080/jnlpJars/agent.jar -O "${agent_fname}"
# get remote directory to transfer agent to
agent_remote_dir=$(ssh "$2@$1" "mktemp -d -t agent-remote-XXXX" < /dev/null)
# SCP to that remote directory
scp -v "$local_tmp_dir/$agent_fname" "$2@$1:$agent_remote_dir/"
# connect stdout / stderr to the java agent, and let Jenkins take the wheel
ssh -v "$2@$1" java -jar "$agent_remote_dir/$agent_fname"

This allowed me to use the system SSH to connect to my agents using AWS SSM

When connecting to my agents, I find that I need to approve the script to run manually, and this results in the hash of the script being added to the approved signatures XML file.

What I would like to do is to add the path to the script itself to approved Signatures block and use CasC to define this, eg

security:
  scriptApproval:
    approvedSignatures:
    - "method hudson.model.Computer isOnline"

but for my script’s path

From looking at the documentation, and the traceback encountered, I should be allowing the Hudson CommandLauncher function, but I am not sure

2024-02-27 09:09:53.555+0000 [id=173]   SEVERE  hudson.slaves.CommandLauncher#launch: Unable to launch the agent for pi-001
java.io.EOFException: unexpected stream termination
        at hudson.remoting.ChannelBuilder.negotiate(ChannelBuilder.java:459)
        at hudson.remoting.ChannelBuilder.build(ChannelBuilder.java:404)
        at hudson.slaves.SlaveComputer.setChannel(SlaveComputer.java:440)
        at hudson.slaves.CommandLauncher.launch(CommandLauncher.java:170)
        at hudson.slaves.SlaveComputer.lambda$_connect$0(SlaveComputer.java:297)
        at jenkins.util.ContextResettingExecutorService$2.call(ContextResettingExecutorService.java:46)
        at jenkins.security.ImpersonatingExecutorService$2.call(ImpersonatingExecutorService.java:80)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:829)

The config file that my agents are defined in is similar to this

jenkins:
  nodes:
    - permanent:
        name: "dev-001"
        nodeDescription: "dev-001 machine in the lab"
        numExecutors: 1
        remoteFS: "/home/user"
        labelString: "dev-001"
        retentionStrategy: 'always'
        launcher:
          command:
            command: "/apollo/env/AlFpgaJenkinsConfig/bin/sshHelper.sh mi-redacted-id user"
        mode: EXCLUSIVE

How do I find the right classpath to add to Jenkins’s CasC that will let me allow all invocations of my custom script and not just the specific invocation?
redefined in the config file

I want to use approvedSignatures and not approvedScriptHashes so that when I add more machines to my config, they will already be approved and not need their hashes to be approved in the config file.

The sshHelper.sh script doesn’t need to be approved anywhere afaik. This is not groovy code running in the Jenkins controller jvm.
The error is not related to a missing approval I would say.
Or maybe you missed to add the information where you use a groovy script that needs approval.

The error that I was seeing when launching the agent was “script is not yet approved”, and after approving the script I was able to launch the agent.

you’re right the command needs approval. You only approve the command, but not the sh script that is called in the command.
This is not the same as approval of signatures.
What you would need to do is to calculate the hash of the command
/apollo/env/AlFpgaJenkinsConfig/bin/sshHelper.sh mi-redacted-id user
and add this to the JCasC something like

scriptApproval:
    approvedScriptHashes:
    - "SHA512:41261ac83aef6df3b776a1765c57d407b087a8c28462f197473b045698ecf118a366da45ec84d8dc4df978ff97dcee6c352c0fe29ab2ebd8b2083be5c204e75e"

See script-security-plugin/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java at dbc154de8669bc551dc946a81b306dbef2c39c2a · jenkinsci/script-security-plugin · GitHub how the hash is calculated. The the language here is “system-command” plus the command you specified, e.g system-command:/apollo/env/AlFpgaJenkinsConfig/bin/sshHelper.sh mi-redacted-id user

I’m doing something similar for my Jenkins instance where during startup of the docker container before the java process starts we create jobs that contain system groovy.

running this in the script console returned me the correct hash that was approved.

import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval

println(ScriptApproval.DEFAULT_HASHER.hash("java -jar c:\\temp\\agent\\agent.jar", "system-command"))