Jenkinsをcronの代わりに使う(セキュリティ編)

2021/10/06

自動化

t f B! P L

Jenkinsをcron代わりに使う

ふむ、、ならJenkinsを使えばいいのではないか…?

ワイ「JenkinsのようなUIで、cronの実行履歴とかを管理したい」

案件でJenkinsを使い、GUIやっぱりええなぁという気持ちになる。
個人的な用途でも、定期実行のスクリプトが30個くらいあり、実行履歴とかをグラフィカルに管理したくなった。
(ほぼすべて単体で動作するスクリプトで、長くても数秒で処理が終わるもの)

その他、調べたcron代替ソフトウェア
airflowとrundeckは試したが、どちらもしっくりこなかった。

airflowはPythonで書けるので使いやすかったが、Jobをすべてコードで作る必要があり(GUIからは作れない)、この点が微妙だった。

rundeckは使用感がJenkinsに近くて良かったが、なぜかメモリ消費量が半端なかったため不採用。
上記ジョブたちは軽いものばかりなのでメモリ1GBのVMで動かしている…。

ワイの中の賢者「ふむ、、ならJenkinsを使えばいいのではないか…?」

Jenkinsには「定期実行機能」があり、cronとしても使える。
プラグインは特に入れず、ほぼデフォルトで使っている。

以下、インストールはできて、ダッシュボードにログインできてる状態とする。

最低限のセキュリティ設定

ユーザー

以下の作業はミスるとログインできなくなるらしいので注意

  • 「Jenkinsの設定」>「グローバルセキュリティの設定」>「ユーザー情報」は「Jenkinsのユーザーデータベース」を選択
  • 「ユーザーにサインアップを許可」のチェックを外す

自分の環境ではデフォルトでこうなっていた。

次に

  • 「管理権限」は「行列による管理権限」を選択
  • 自分のユーザー名を追加し、チェックボックのすべての権限を付与
  • それ以外のエントリーの権限はすべてチェックを外す

自分しか使わない場合は上記のようにする。
「行列による」というのはマトリックステーブルで管理するという意味です。

JenkinsにSSH port forwardingで接続する

ダッシュボードはWebベースなので、HTTPSでブラウザからアクセスしたい。
しかし、リモートマシンのポートを開けて、ファイアウォールで0.0.0.0:443を許可するのはダメすぎる。
ふつうは、VPNの中とかにおいておくものと思うが、私はそんなもの用意していない。
Jenkinsのログイン認証はパスワード認証しかなく、もし侵入された場合、マシン上でコマンドを実行できるので洒落にならない。

そこで、sshが通る前提で、ssh port forwardingでローカルとリモートマシンを接続する。
これならsshが通るポートだけ開いていればいいので、443をあける必要はなく、sshは公開鍵認証になるので安全性が高い。
さらに、Jenkins自体もアクセスを受け付けるホストを「0.0.0.0」ではなく「localhost」で起動できるので、より安心ではある。

SSHの設定

  • RootLogin禁止
  • パスワード認証禁止
  • 公開鍵認証必須
  • ポートを22番から変更

等の基本的なSSHのセキュリティ対策は実施済みとします。

Ubuntu系であれば、リモートマシンのファイアウォールにて変更したポート番号に対するアクセスのみ許可。

クラウドサービスのVMであれば、クラウド側(VMの外)のFirewallにも同様の設定をしておく。
クラウドサービスのファイアウォールはAPIで許可するIPアドレスを変更できるので、ローカルマシンのIPアドレスを定期的に送信し(これはローカルマシンのcronで行うと良い)、自分のIPアドレスのみ許可するとさらに安全。

Jenkinsの設定

/etc/default/jenkins

Ubuntu系のVMでは、上記がJenkinsの起動パラメータを記述する設定ファイルになる。
ここで起動時のHOSTとPORTを設定する。

例えば、以下のように記載する。

HTTP_PORT=18080
HTTP_HOST=127.0.0.1

JENKINS_ARGS="--httpListenAddress=$HTTP_HOST --httpPort=$HTTP_PORT"

ホストはlocalhost(127.0.0.1)を指定、ポートも念の為変えておく。
例は18080だが、他のプロセスのポートと被らなければ何でもいい。Jenkinsのデフォルトは8080。

sudo service jenkins restart

設定の反映には再起動が必要。

ssh port forwarding

ssh -fNL <local-port>:<remote-host>:<remote-port> <host-name>
  • host-name: ssh <host-name>でssh接続できる状態にしておく
  • local-port: リモートマシンに繋ぐローカルマシンの任意port
  • remote-host:「localhost」にする
  • remote-port: リモートマシンでJenkinsがListenしているport(例では18080)

local-portはremote-portと同じでも別でもOK。
例えば、18080にした場合、リモートマシンでJenkinsのサーバーが立ち上がっていれば

http://localhost:18080

でJenkinsのダッシュボードが開ける。

やめるときは、ssh port fowardingを実行しているローカルマシンのプロセスをkillする。
このプロセスはlocal-portに紐付いたプロセスなので、以下のコマンドでkill可能(local-portが18080の場合)。

kill -9 $(lsof -t -i:18080)

リモートマシンのJenkinsは、定期実行するためのものなので、基本的にはプロセスを起動したままにしておくものと思います。

ダイナミックDNS

順番がめちゃくちゃだが、Jenkinsの動いているVMのIPアドレスが変わる問題をダイナミックDNS(DDNS)で解決する。

クラウドのVMは、一旦停止した後、再度起動すると外部IPアドレスが変わってしまう(ものもある)。

具体的にどんな不都合が生じるかと言うと、sshのconfigファイルに書くIPアドレスの書き換えが必要。これはさすがにめんどくさい。

DNSというとWebサーバー(Webサイト)のためにいじるものという印象が強かったが、VMのIPアドレスと契約しているドメインをDNSで対応づけておき、ドメイン名でsshサーバーに接続することももちろん可能である。

この紐付けにDDNSを利用すれば、IPアドレスが変わっても同じドメインからアクセスできる。

DDNSの機能は、ドメイン取得代行業者でも無料で対応しているところが増えてきているので、そういった業者を利用する。例えば、Value-DomainやGoogle Domainsは対応している。

使用方法は簡単で、各業者が用意しているDDNS用のURLに、VMからcurl等でAPIの認証キーなどの情報を含めてアクセスするのが方式が一般的。
アクセスしてきたVMの外部IPアドレスがそのままドメインに紐付けられる。
各社の手順に従えば簡単にできる。

この処理はIPアドレスの変更に備えて定期実行する必要があるので、まさにJenkinsで自動化するのにぴったり。

お叱り

JenkinsはCI/CDツールで、ジョブ管理ツールとは異なるという主張はあります。
実際そのとおりなのですが、貧乏な私のメモリ要件を満たしつつ、ちょっとしたスクリプトを定期実行できて、ログが残るという機能を実現できたので満足しています。

ラベル

QooQ