Jenkinsfile からコマンドを sudo で実行させたいときがあります。 そういうときの対処法を askpass をはじめ数パターンまとめてみました。


ユーザjenkinsの設定

まずはそもそもユーザ jenkins がsudoできるように設定が必要です。 下記のコマンドでまずユーザjenkins のパスワードを設定します。 パスワードは任意のパスワードでOKです。

1
sudo passwd jenkins

つぎに jenkins を sudo できるグループに追加します。Ubuntuのときは下記でOKです。 他のディストリビューションについてはググってみてください。

1
sudo usermod -aG sudo jenkins

ここまで来たら jenkins で sudo できるはずです。下記のように jenkinsにスイッチして whoami してみてください。 root と返ってくるはずです。

1
2
3
4
$ su jenkins
$ sudo whoami
root
$ exit

ここまでは下記のどの方法をするにしても必要な作業です。

パスワード無しでsudoできるようにする

1つ目の方法は、ユーザ jenkins でパスワードなしでsudoできるようにしてしまう方法です。 乱暴すぎるのであまりおすすめはしませんが、まずは紹介します。 sudo の設定を変更する下記のコマンドを実行します。

1
sudo visudo

すべてのコマンドをパスワード無しで実行するには下記のように記述します。

1
2
Defaults:jenkins !requiretty
jenkins ALL=(ALL) NOPASSWD:ALL

全部のコマンドは必要ない場合は必要なコマンドだけパスワードなしにもできます。 下記の例ではコマンド /path/to/command だけパスワードなしにする設定です。 詳細は「sudoers NOPASSWD」でググるといろいろ出てきます。

1
2
Defaults:jenkins !requiretty
jenkins ALL=(ALL) NOPASSWD: /path/to/command

expectコマンドを使う

対話的コマンドを自動化するツールにexpectがあります。 対話的なコマンドを自動化できるすぐれものです。ベースがTclなので結構とっつきにくいです。

Jenkinsfile にかくときはシェバンを /usr/bin/expect にすればOKです。下記の例はsudoするときの例です。 spawnがプロセス実行、expectがコマンドからのレス待ち、sendがコマンドへの入力です。 下記のコードを見ればなんとなくわかるかと思います。 最後のexpectはプロンプトの\$が表示されるのを待っています。

1
2
3
4
5
6
7
8
withCredentials([usernamePassword(credentialsId: 'user_jenkins', passwordVariable: 'jenkins_password', usernameVariable: 'jenkins_username')]) {
    sh """\
       |#!/usr/bin/expect -f
       |spawn sudo bash -c \"rm -rf *\"
       |expect -re "password for $jenkins_username"
       |send -- "$jenkins_password\\r"
       |expect "\$"
       """.stripMargin()}

askpassを使う

これが一番おすすめです。 askpassについてはなんかかきたい - askpassのことを書いておくに紹介記事がありました。 パスワードを標準出力に吐くスクリプトを作って、環境変数SUDO_ASKPASS にそのスクリプトのパスを設定すればOKです。 sudo するときに -A をつけます。

Jenkinsfile の例

Jenkinsで書くと下記のようになります。 パスワードを吐くスクリプトをワークスペースにおいてしまうと、ブラウザからアクセスできてしまうのでWORKSPACE_TMP を利用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
withCredentials([usernamePassword(credentialsId: 'user_jenkins', passwordVariable: 'jenkins_password', usernameVariable: 'jenkins_username')]) {
    script {
        // パスワードを標準出力に吐き出すスクリプト作成
        // sudo には -A オプションが必要
        // WORKSPACEの下にスクリプトを置くと、そのスクリプトをブラウザから参照できてしまうので
        // WORKSPACE_TMP を利用している
       sh  "mkdir -p $env.WORKSPACE_TMP"
        String sudoAskpass = sh(returnStdout: true, returnStatus: false, script: "mktemp --tmpdir=${env.WORKSPACE_TMP}/ ${BUILD_TAG}.XXXXXXXXXX").trim()
        sh """\
           |#!/bin/bash -eu
           |echo -e '#!/bin/bash\\necho $jenkins_password' > $sudoAskpass
           |chmod u+x $sudoAskpass
           |export SUDO_ASKPASS=$sudoAskpass
           |sudo -A whoami
           |""".stripMargin()

        // もう一度sudoを使うにはexportが必要
        sh """\
           |#!/bin/bash -eu
           |export SUDO_ASKPASS=$sudoAskpass
           |sudo -A whoami
           |""".stripMargin()
    }
}

まとめ

Jenkinsfile から sudo する方法を説明しました。expectaskpass を使うのがいいかなと思います!