Jenkins CLI

Jenkins有一个内置的命令行界面,允许用户和管理员从脚本或shell环境中访问Jenkins。这可以方便的编写日常任务, 批量更新, 故障排除等等。

可以通过SSH 或 Jenkins CLI客户端访问命令行界面, Jenkins分布式的的.jar 文件。

由于安全原因,not recommended使用 Jenkins 2.53 及以下和Jenkins LTS 2.46.1 及以下分布式的CLI 客户端:虽然目前没有已知的漏洞,但之前有几个漏洞被报告并修补过, 而且它使用的Jenkins远程协议 很容易受到远程代码执行bug的影响,is inherently vulnerable to remote code execution bugs,甚至是 “预认证” 漏洞 (匿名用户能够物理访问Jenkins 网络)。Jenkins 2.5 及以上和Jenkins LTS 2.46.2 及以上分发的客户端被认为在它的默认 (-http) 或 -ssh 模式是安全的,如使用标准 ssh 命令。

通过SSH使用 CLI

在新的Jenkins安装中, SSH 服务默认禁用。管理员可以选择设置一个特定的端口,或让Jenkins在 配置全局安全 页面选择一个随机的端口。

为了确定随机分配的SSH端口,请检查Jenkins URL返回的头,例如:

  1. % curl -Lv https://JENKINS_URL/login 2>&1 | grep 'X-SSH-Endpoint'
  2. < X-SSH-Endpoint: localhost:53801
  3. %

使用随机 SSH 端口 (本例中为53801 ), 和配置的 [Authentication], 任何现代的 SSH 客户端都可以安全的执行 CLI 命令。

身份验证

无论哪个在Jenkins 主机身份验证的用户,为了access CLI,都必须具有Overall/Read 权限。根据执行的命令,用户可能需要其他的权限。

身份认证依赖于基于SSH公共/私有密钥对身份认证。要为合适的用户添加 SSH 公共密钥, 导航到https://JENKINS_URL/user/USERNAME/configure 并将 SSH 公共密钥粘贴到合适的文本域。

Adding public SSH keys for a user

常用命令

Jenkins 有许多内置的 CLI 命令,这些命令可以在任何Jenkins 环境中找到, 比如 buildlist-jobs。插件也提供CLI 命令; 为了确定给定Jenkins环境中可用命令的完整列表, 执行CLI help 命令:

  1. % ssh -l kohsuke -p 53801 localhost help

下面的命令列表并不全面, 但他是Jenkins CLI 使用的起点。

build

最常见和有用的 CLI命令之一是 build, 它允许用户触发拥有权限的任何作业或流水线。

最基本的调用将简单的触发 作业或流水线并退出, 但是通过额外的选项,用户也可以传递参数, 轮询 SCM, 甚至可以跟踪触发的构建或流水线的运行的控制台输出。

  1. % ssh -l kohsuke -p 53801 localhost help build
  2. java -jar jenkins-cli.jar build JOB [-c] [-f] [-p] [-r N] [-s] [-v] [-w]
  3. Starts a build, and optionally waits for a completion. Aside from general
  4. scripting use, this command can be used to invoke another job from within a
  5. build of one job. With the -s option, this command changes the exit code based
  6. on the outcome of the build (exit code 0 indicates a success) and interrupting
  7. the command will interrupt the job. With the -f option, this command changes
  8. the exit code based on the outcome of the build (exit code 0 indicates a
  9. success) however, unlike -s, interrupting the command will not interrupt the
  10. job (exit code 125 indicates the command was interrupted). With the -c option,
  11. a build will only run if there has been an SCM change.
  12. JOB : Name of the job to build
  13. -c : Check for SCM changes before starting the build, and if there's no
  14. change, exit without doing a build
  15. -f : Follow the build progress. Like -s only interrupts are not passed
  16. through to the build.
  17. -p : Specify the build parameters in the key=value format.
  18. -s : Wait until the completion/abortion of the command. Interrupts are passed
  19. through to the build.
  20. -v : Prints out the console output of the build. Use with -s
  21. -w : Wait until the start of the command
  22. % ssh -l kohsuke -p 53801 localhost build build-all-software -f -v
  23. Started build-all-software #1
  24. Started from command line by admin
  25. Building in workspace /tmp/jenkins/workspace/build-all-software
  26. [build-all-software] $ /bin/sh -xe /tmp/hudson1100603797526301795.sh
  27. + echo hello world
  28. hello world
  29. Finished: SUCCESS
  30. Completed build-all-software #1 : SUCCESS
  31. %

console

同样有用的是console 命令, 它检索指定的构建或流水线运行的控制台输出。当没有提供构建编号时,console 命令将会输出最后完成的构建的控制台输出。

  1. % ssh -l kohsuke -p 53801 localhost help console
  2. java -jar jenkins-cli.jar console JOB [BUILD] [-f] [-n N]
  3. Produces the console output of a specific build to stdout, as if you are doing 'cat build.log'
  4. JOB : Name of the job
  5. BUILD : Build number or permalink to point to the build. Defaults to the last
  6. build
  7. -f : If the build is in progress, stay around and append console output as
  8. it comes, like 'tail -f'
  9. -n N : Display the last N lines
  10. % ssh -l kohsuke -p 53801 localhost console build-all-software
  11. Started from command line by kohsuke
  12. Building in workspace /tmp/jenkins/workspace/build-all-software
  13. [build-all-software] $ /bin/sh -xe /tmp/hudson1100603797526301795.sh
  14. + echo hello world
  15. yes
  16. Finished: SUCCESS
  17. %

who-am-i

who-am-i 有助于列出当前用户的可用凭证和权限。这对调试时由于没有某些权限而缺少 CLI 命令的情况很有帮助。

  1. % ssh -l kohsuke -p 53801 localhost help who-am-i
  2. java -jar jenkins-cli.jar who-am-i
  3. Reports your credential and permissions.
  4. % ssh -l kohsuke -p 53801 localhost who-am-i
  5. Authenticated as: kohsuke
  6. Authorities:
  7. authenticated
  8. %

使用 CLI 客户端

尽管基于SSH的CLI快速且覆盖了大多数需求, 但是Jenkins分布式的CLI客户端是更好的选择。 例如, CLI客户端的默认传输是HTTP,这意味着不需要在防火墙中打开其他端口以供其使用。

下载客户端

CLI 客户端可以直接在URL/jnlpJars/jenkins-cli.jar从Jenkins主机下载, 实际上https://JENKINS_URL/jnlpJars/jenkins-cli.jar

.jar 用于 Jenkins的不同版本时, 在使用时出现兼容性问题, 请重新从Jenkins主机下载最新的 .jar文件。

使用客户端

调用客户端的一般语法如下:

  1. java -jar jenkins-cli.jar [-s JENKINS_URL] [global options...] command [command options...] [arguments...]

JENKINS_URL 可以通过环境变量 $JENKINS_URL来指定。其他通用选项的摘要可以通过运行客户端来显示,而不需要任何参数。

客户端连接模式

在2.54+ / 2.46.2+ 客户端,有三种基本模式可以使用 , 可通过全局变量选择:-http; -ssh; 和 -remoting

HTTP 连接模式

这是2.54 和 2.46.2的默认模式, 不过你可以明确地传递 -http 选项。

身份认证最好使用 -auth 选项, 它接受 username:apitoken 参数。从/me/configure获取你的API令牌:

  1. java -jar jenkins-cli.jar [-s JENKINS_URL] -auth kohsuke:abc1234ffe4a command ...

(实际上也接受密码, 但这是不鼓励的)

你还可以在带 @的参数之前从文件加载相关的内容:

  1. java -jar jenkins-cli.jar [-s JENKINS_URL] -auth @/home/kohsuke/.jenkins-cli command ...

一般来说,不需要特别的参数配置来启动基于 HTTP的CLI 连接。如果你在HTTP 反向代理后台运行Jenkins,e确保它不缓存请求或响应体。

Jenkins 2.54及以上的CLI的HTTP连接模式不能在使用mod_proxy的Apache HTTP 反向代理服务器上正常工作。工作区包括使用不同的反向代理,如 Nginx 或 HAProxy, 或在可能的情况下使用 SSH 连接模式。参考JENKINS-47279.

SSH连接模式

身份验证是通过 SSH 密钥对进行的。你还必须选择Jenkins用户ID:

  1. java -jar jenkins-cli.jar [-s JENKINS_URL] -ssh -user kohsuke command ...

在这种模式下, 客户机基本上像一个本地 ssh 命令。

默认情况下,客户端尝试连接在 JENKINS_URL使用的相同主机上的SSH端口。如果Jenkins在 HTTP 反向代理的后面, 这通常不会起作用,所以用系统属性 -Dorg.jenkinsci.main.modules.sshd.SSHD.hostName=ACTUALHOST运行Jenkins,为SSH端点定义一个主机名或IP地址

远程连接模式

这是客户端支持从pre-2.54 / pre-2.46.2 Jenkins 服务器下载的唯一模式(引入 -remoting 选项之前)。由于安全和性能原因它被弃用了。也就是说, 某些命令或命令模式 only 能在远程模式下运行,通常是由于命令功能包括在客户端机器上运行的服务器提供的代码。

该模式在服务器端被禁用,用于2.54+ 和 2.46.2的新的安装。如果你必须使用它, 并接受风险, 可在 配置全局安全性中启用。

身份验证最好通过 SSH 密钥对进行。login命令和—username / —password 命令(注意: not global) 选项也是可用的;由于无法使用非基于密码的安全域,这些命令是无效的。如果匿名用户缺乏整体或工作对权限,那么某些命令参数无法得到适当的解析,并且在脚本中保存人工选择的密码用于使用被认为是不安全的。

注意,此模式有两种传输方式: 通过HTTP, 或通过 专用的TCP 套接。如果TCP 端口可用 并且看起来有效, 客户端将使用此传输。如果TCP 端口被禁用, 或者这样一个端口被广告但不接受连接(例如,你使用带有防火墙的 HTTP 反向代理),客户端将会自动地退回到效率较低的HTTP传输。

基于远程的客户端的常见问题

在运行CLI客户端时可能会遇到很多常见问题。

操作超时

如果你在服务器上使用防火墙请检查 HTTP 或 TCP 端口是否打开。你可以在Jenkins配置中配置它的值。 默认情况下,它将设置一个随机的端口。

  1. % java -jar jenkins-cli.jar -s JENKINS_URL help
  2. Exception in thread "main" java.net.ConnectException: Operation timed out
  3. at java.net.PlainSocketImpl.socketConnect(Native Method)
  4. at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
  5. at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
  6. at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
  7. at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:432)
  8. at java.net.Socket.connect(Socket.java:529)
  9. at java.net.Socket.connect(Socket.java:478)
  10. at java.net.Socket.<init>(Socket.java:375)
  11. at java.net.Socket.<init>(Socket.java:189)
  12. at hudson.cli.CLI.<init>(CLI.java:97)
  13. at hudson.cli.CLI.<init>(CLI.java:82)
  14. at hudson.cli.CLI._main(CLI.java:250)
  15. at hudson.cli.CLI.main(CLI.java:199)
No X-Jenkins-CLI2-Port

进入 Manage Jenkins > Configure Global Security 并选择TCP port for JNLP agents下面的 "Fixed" 或"Random"。

  1. java.io.IOException: No X-Jenkins-CLI2-Port among [X-Jenkins, null, Server, X-Content-Type-Options, Connection,
  2. X-You-Are-In-Group, X-Hudson, X-Permission-Implied-By, Date, X-Jenkins-Session, X-You-Are-Authenticated-As,
  3. X-Required-Permission, Set-Cookie, Expires, Content-Length, Content-Type]
  4. at hudson.cli.CLI.getCliTcpPort(CLI.java:284)
  5. at hudson.cli.CLI.<init>(CLI.java:128)
  6. at hudson.cli.CLIConnectionFactory.connect(CLIConnectionFactory.java:72)
  7. at hudson.cli.CLI._main(CLI.java:473)
  8. at hudson.cli.CLI.main(CLI.java:384)
  9. Suppressed: java.io.IOException: Server returned HTTP response code: 403 for URL: http://citest.gce.px/cli
  10. at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1840)
  11. at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
  12. at hudson.cli.FullDuplexHttpStream.<init>(FullDuplexHttpStream.java:78)
  13. at hudson.cli.CLI.connectViaHttp(CLI.java:152)
  14. at hudson.cli.CLI.<init>(CLI.java:132)
  15. ... 3 more
服务器密钥未验证

你可能会得到以下的错误并找到一个关于 mismatched keys的日志条目:

  1. org.apache.sshd.common.SshException: Server key did not validate
  2. at org.apache.sshd.client.session.AbstractClientSession.checkKeys(AbstractClientSession.java:523)
  3. at org.apache.sshd.common.session.helpers.AbstractSession.handleKexMessage(AbstractSession.java:616)
  4. ...

这意味着你的SSH 配置不承认服务器提供的公钥。当你在开发模式下运行Jenkins,应用程序的多个实例会随着时间的推移运行在同一个SSH端口上。在开发环境中, 访问你的 ~/.ssh/known_hosts (或 C:/Users/<your_name>/.ssh/known_hosts )并删除与你当前 SSH 端口对应的行 (比如 [localhost]:3485)。在生产环境中, 如果服务器的公钥在最近发生改变,请与Jenkins管理员联系。如果是这样, a请管理员执行上面描述的步骤。

UsernameNotFoundException

如果你的客户端显示如下的堆栈跟踪:

  1. org.acegisecurity.userdetails.UsernameNotFoundException: <name_you_used>
  2. ...

这意味着SSH密钥被识别并对存储的用户进行了验证,但是用户名对当前应用程序正在使用的安全域无效 。这可能发生在最初使用Jenkins数据库时, 配置了你的用户, 然后切换到另一个安全域 (如 LDAP等) ,在该安全域中,你定义的用户还不存在。

要解决这个问题, 就要确保你的用户在你配置的安全域中存在。

故障诊断日志

为了获取认证过程的更多信息:

  • 进入 Manage Jenkins > System Log > Add new log recorder

  • 输入你想要的名称,点击 Ok

  • 点击 Add

  • 输入 org.jenkinsci.main.modules.sshd.PublicKeyAuthenticatorImpl (或者输入 PublicKeyAuth ,然后选择全名)

  • 设置等级为ALL

  • hudson.model.User重复之前的三个步骤。

  • 点击Save

当你尝试身份验证时, 你可以刷新页面并查看内部发生了什么。