Jenkinsでプラグインを使わずにSSHの接続先でスクリプトを（複数回）実行する

## SSHプラグインが更新されない

ここ最近、OpenSSL系のアップデートが激しく、更新されていないプラグインの対応が全然行われていない状況。
（Jenkinsに限らずNpmモジュールなども該当するが）

このSSHプラグインもすでに5年も更新されておらず、Jenkinsが警告出しまくりの、[せっかく修正してくれているGitのプルリク](https://github.com/jenkinsci/ssh-plugin/pulls)も放置されている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104999/efd727b8-3527-0c47-009a-4886d7f8773f.png)


現状、実用には問題はないし、信頼できる接続先同士なのだが、いい加減Jenkinsを開くたびに警告を見るのも嫌なので対応を考えた

1. 別のプラグインを探す
    - [Publich Over SSH](https://plugins.jenkins.io/publish-over-ssh/)
    - [SSH Pipeline Steps](https://plugins.jenkins.io/ssh-steps/)
2. プラグインの利用をやめる
3. Jenkinsから移行する（GitHub Actionとか）

1については上記2つプラグインが現状更新され続けていて、前者は乗り換え対象になりそうだが、それでも前回更新が8ヶ月でIssueも溜まっているのでちょっと怖いので却下。
Piplineも自分だけが使うなら良いが、できれば同僚（プログラマではない）でも使えるようにGUIで完結したいので却下。

3についてはちょっと乗り換えコストがきついし、Pipelineと同じ問題が出るのと、想定コストが現行VPSサーバーのコスト内で収まらないのでこちらも却下。

## SSHプラグインの利用をやめてシェルで解決しよう

「シェルで実行する」といっても方法は2つある

1. シェルスクリプトファイル（.sh）を作成して実行する
1. Jenkinsの「シェルの実行」部分に直書きする

まず、前者だがJenkinsサーバーにSSHで入ってリモートサーバー内で実行するスクリプトをまとめておくことになるが、ぶっちゃけ作ること自体がめんどくさいしエラーが出た時にファイルを見に行かなければならないので却下。
なので後者を採用。

## JenkinsからSSHで接続し、接続先でスクリプトを実行できるまでの手順

### 1. SSH接続設定を行う

いままでSSHプラグインと「Manage Credentials」で設定をしていたと思うが、SSHプラグインはサヨナラするので使えない。
そこで、こればかりはJenkinsサーバーにSSHで入って設定する必要がある。

```sh
# rootでの操作を想定
su -s /bin/bash - jenkins  # jenkinsユーザーは/bin/falseなのでsオプションが必要

# jenkinsユーザーになっていることを確認
# 以下、jenkinsユーザーで実行

ls -al      # .sshディレクトリが存在するか確認
mkdir .ssh  # .sshディレクトリがなければ作成
cd .ssh
vim hoge.pem       # 接続先サーバーの秘密鍵を作成(rsaとか)
vim config         # 接続設定ファイルを作成（内容は後述）
chmod 600 hoge.pem # パーミッション修正（644だとエラーになる）
ssh hoge           # 接続確認

# known_hostsへの登録を確認されるので許可
# 接続先に切り替わったか確認

exit          # リモートホスト接続終了
```

configの内容は以下

```~/.ssh/config
Host hoge
  port 22
  Hostname hoge.fuga.com
  IdentityFile ~/.ssh/hoge.pem
  IdentitiesOnly yes
  User hogeuser
```

上記スクリプトで**known_hostsへの登録を許可**という部分があるが、初回アクセス時に聞かれる文句で、これをやっておかないとJenkinsでSSH接続時にここで止まってしまうので注意。
（なお、直接 `~/.ssh/known_hosts` に接続先の公開鍵を記述しておけば素通りできる）
接続先の状況によっては configファイルに以下を追記しても良いかもしれない。

```config
# 接続先の公開鍵を問答無用で無視（許可）
StrictHostKeyChecking=no
UserKnownHostsFile=/dev/null
```

### 2. Jenkinsの設定

SSHプラグインの「リモートホストでシェルを実行」ブロックの前か後ろに「シェルを実行」ブロックを作成し、以下のようにする。

```sh
ssh hoge '
cd /path_to_project/
npm i
npm start
'
```

`hoge` は `~/.ssh/config` のHost（1行目）
あとはシングルクォーテーションの中にいままで実行していたスクリプトを貼り付ければ良い。
参考資料を探しても1行で1コマンドしか実行していないものが多く、勘違いしそうになるが、普通に**改行を入れれば複数のコマンドが実行できる**
（PHPならcomposerしてmigrateしてserveするとか）

なお、実行時にスクリプトに起因しないエラーが発生する場合は`-t`や`-t -t`オプションをつけてみると解消するかもしれない。


### 3. テスト

ここまでできたら、「リモートホストでシェルを実行」ブロックを削除して保存。
実際に走らせてみる。

### 4. SSHプラグインをアンインストール

SSHプラグインを利用しているすべてのプロジェクトの設定が完了したらプラグインを削除。
いままでありがとう:pray:


----

## 補足

これらはちょっと調べれていけばわかることなのだが、それぞれがまとまっておらず時間がかかったことと

- jenkinsユーザーでログインすること（/bin/falseのまま）
これを知らずにいままで /bin/bash に書き換えて操作していた。（戻すのを忘れるとセキュリティ問題）
- SSHコマンド内で複数のスクリプトを実行できること
1行で1コマンドだと思って何行も書いていた

を知っているとjenkinsに限らず色々応用できるなと思って備忘録として残しておくことにした。

:::note alert
サーバーにインストールしているOpenSSLやSSH自体に脆弱性があったらもちろん意味がない
バージョンを確認しておくこと
:::

----

## 参考資料

- ログインできるユーザをログイン出来ないようにする、あるいはログインできるユーザをログインできなくする
https://cameong.hatenablog.com/entry/2016/08/16/153449
- UserKnownHostsFileとStrictHostKeyChecking【ssh】
https://penpen-dev.com/blog/userknownhostsfile-stricthostkeychecking/
- 【Jenkins】JenkinsJobでssh接続先でコマンドを実行する方法
https://c-taquna.hatenablog.com/entry/2019/05/01/153235