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
问题根源
- 管道符作用域错误:
echo [PASSWORD] | sudo -S su - jboss的管道是在Jenkins本地执行的,而非远程SSH会话内。echo [PASSWORD]在本地输出密码,传给本地的sudo命令,远程机器的sudo根本拿不到密码,导致认证失败。 - 命令执行逻辑断裂:
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用户上下文执行
额外优化建议
- 避免明文传递密码:sshpass明文传密码存在安全风险,建议给Jenkins登录用户配置无需密码的sudo权限,在目标机器的
/etc/sudoers中添加:
之后可直接执行jenkins ALL=(jboss) NOPASSWD: ALLsudo su - jboss -c "...",无需传递密码。 - 整合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
相关产品推荐
相关产品推荐

