You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jenkins流水线SSH执行su命令后连接异常断开问题求助

Jenkins SSH切换用户后连接断开问题排查与解决

问题背景

我正在开发一个Jenkins步骤,需要将文件传输到目标机器安装JBoss模块,计划通过SSH交互实现。流程是先SSH连接目标机器,切换到有权限访问JBoss目录/文件的用户,再用rsync把jar文件传到JBoss模块目录,但没办法用同一个用户完成SSH登录和JBoss操作。

当前遇到的问题:SSH能成功连接,但发送切换用户的命令后连接直接断开,后续操作无法执行,看起来像是在执行su命令前就断开了。同时需要检查模块目录是否存在,不存在则创建。

执行的Jenkins函数

def installModule(HOST, USER, PASSWORD) {
    sh set -x && sshpass -p [PASSWORD] ssh -v -tt -o StrictHostKeyChecking=no [USER]@[HOST] echo [PASSWORD] | sudo -S su - jboss && cd [MODULE_FOLDER] && if [[ ! -e [MODULE_VERSION] ]]; then mkdir [MODULE_VERSION]; fi
}

控制台输出

debug1: Authentication succeeded (keyboard-interactive).
Authenticated to [MACHINE_NAME_HERE] ([IP_HERE]:22).
debug1: channel 0: new [client-session]
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.
debug1: pledge: network
debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0
debug1: tty_make_modes: no fd or tio
debug1: Sending environment.
debug1: Sending env LANG = en_GB.UTF-8
debug1: Sending command: echo [PASSWORD_HERE]
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: channel 0: free: client-session, nchannels 1
debug1: fd 0 clearing O_NONBLOCK
debug1: fd 1 clearing O_NONBLOCK
debug1: fd 2 clearing O_NONBLOCK
Connection to [MACHINE_NAME_HERE] closed.
Transferred: sent 2180, received 3356 bytes, in 0.3 seconds
Bytes per second: sent 7006.2, received 10785.6
debug1: Exit status 0
Sorry, try again.
[sudo] password for jenkins: Sorry, try again.
[sudo] password for jenkins:
sudo: no password was provided
sudo: 2 incorrect password attempts

问题根源

  1. 管道符作用域错误:echo [PASSWORD] | sudo -S su - jboss 的管道是在Jenkins本地执行的,而非远程SSH会话内。echo [PASSWORD] 在本地输出密码,传给本地的sudo命令,远程机器的sudo根本拿不到密码,导致认证失败。
  2. 命令执行逻辑断裂:ssh命令仅执行了echo [PASSWORD]就退出了,后续的cd、mkdir都是在本地执行,完全没传到远程机器。

修复方案

方案1:用单引号包裹远程命令,确保逻辑在远程执行

将所有需要在远程运行的命令整体用单引号包裹,把管道放在远程命令内部:

def installModule(HOST, USER, PASSWORD) {
    sh '''set -x && sshpass -p "${PASSWORD}" ssh -v -tt -o StrictHostKeyChecking=no ${USER}@${HOST} 'echo "${PASSWORD}" | sudo -S su - jboss -c "cd ${MODULE_FOLDER} && if [[ ! -e ${MODULE_VERSION} ]]; then mkdir ${MODULE_VERSION}; fi"' '''
}
  • 单引号包裹远程命令串,确保所有操作在目标机器执行
  • su - jboss -c "..." 指定切换用户后执行的命令,避免进入交互式shell导致SSH会话异常
  • 外层用双引号让Groovy解析变量,远程内部用单引号避免变量冲突

方案2:使用Here Document传递多行命令(更易读)

如果命令逻辑复杂,推荐用Here Doc提升可读性:

def installModule(HOST, USER, PASSWORD) {
    sh """set -x && sshpass -p "${PASSWORD}" ssh -v -tt -o StrictHostKeyChecking=no ${USER}@${HOST} << 'EOF'
echo "${PASSWORD}" | sudo -S su - jboss -c "
cd ${MODULE_FOLDER}
if [[ ! -e ${MODULE_VERSION} ]]; then
    mkdir ${MODULE_VERSION}
fi
"
EOF
"""
}
  • << 'EOF' 表示Here Doc开始,单引号包裹EOF可避免本地变量解析,确保内容原封不动传到远程
  • 所有操作放在su - jboss -c的命令串里,确保在jboss用户上下文执行

额外优化建议

  1. 避免明文传递密码:sshpass明文传密码存在安全风险,建议给Jenkins登录用户配置无需密码的sudo权限,在目标机器的/etc/sudoers中添加:
    jenkins ALL=(jboss) NOPASSWD: ALL
    
    之后可直接执行sudo su - jboss -c "...",无需传递密码。
  2. 整合rsync传输逻辑:如果需要传输jar文件,可先执行rsync再执行目录操作:
    def installModule(HOST, USER, PASSWORD, LOCAL_JAR, REMOTE_JAR_PATH) {
        // 传输jar文件
        sh "sshpass -p \"${PASSWORD}\" rsync -avz ${LOCAL_JAR} ${USER}@${HOST}:${REMOTE_JAR_PATH}"
        // 执行目录检查与文件移动
        sh """sshpass -p "${PASSWORD}" ssh -v -tt -o StrictHostKeyChecking=no ${USER}@${HOST} << 'EOF'
    

sudo su - jboss -c "
cd ${MODULE_FOLDER}
if [[ ! -e ${MODULE_VERSION} ]]; then
mkdir ${MODULE_VERSION}
fi
mv ${REMOTE_JAR_PATH} ${MODULE_FOLDER}/${MODULE_VERSION}/
"
EOF
"""
}

内容的提问来源于stack exchange,提问作者Diego Senra
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 22:15:52