EOL検知ツールXEOLの使い方

# EOL検知ツールXEOLの使い方

xeolはソフトウェアのEOL (End of Life)を検出するツールの一つです。xeolは、SBOMやコンテナイメージ、ディレクトリをスキャンすることで使用しているソフトウェアのバージョンがEOLであるかを検知できます。ちなみに、xeolは"zee-oh-el”と発音します。

xeolのソースコードやコマンドはgrypeと似ています。また、SBOM生成ツールsyftが生成するSBOMと連携することを意識して作られているようです。

> xeol supports input of [Syft](https://github.com/anchore/syft), [SPDX](https://spdx.dev/), and [CycloneDX](https://cyclonedx.org/) SBOM formats. If Syft has generated any of these file types, they should have the appropriate information to work properly with xeol.
    
    

[xeol公式ドキュメント](https://github.com/xeol-io/xeol)には、xeolの様々な機能詳細や仕様、環境変数の設定について記述されています。この記事では、自分が使う上で、使用頻度の高い機能について、紹介します。

# 検証環境

検証環境について、xeolが対応しているsyftのバージョン0.91.0以下のため、最新バージョンのsyftが生成するSBOMに対応していません。

- Ubuntu 22.04.3 LTS (WSL2)

| コンポーネント | バージョン | 使用用途 |
| --- | --- | --- |
| Docker | 24.0.5 | コンテナイメージのビルド, pull |
| syft | v0.91.0 | SBOM生成ツール |
| xeol | v0.9.15 | EOL検知 |


# EOL (End of Life)って何？
EOL (End Of Life)とは、ベンダーが該当するソフトウェアの `useful lifespan (耐用年数)`が終了したと判断されたことを意味します。この特定の日付が過ぎると、製造元はその製品の販売、販売、テクニカル・サポートの提供、保守、強化、修理などを行わなくなります。EOLとサポート終了を表すEOS （End of Support）は、様々なベンダーが異なる意味として使用しています。しかし、xeolではこの二つ用語は同じものとして扱われています。EOLとなったソフトウェアは、もはや保守されておらず、セキュリティアップデートを受けていないため、セキュリティリスクがあります。

# install方法

まず、xeolを使うためにそのインストール方法について説明します。インストール方法はsyftやgrypeの場合とほぼ同じです。

## 0. 解凍用の一時的なファイルの作成

まず、xeolを解凍するための一時的なディレクトリを作成し、そのディレクトリへ移動します：

```bash
$ mkdir /tmp/xeol/ && cd /tmp/xeol/
```

## 1. xeolのバイナリファイルの取得

xeolの[リリース情報](https://github.com/xeol-io/xeol/releases/)から、アーカイブファイルを取得します。
今回は、[xeol-v0.9.15](https://github.com/xeol-io/xeol/releases/tag/v0.9.15)から、バージョン0.9.15のxeolのアーカイブである`xeol_0.9.15_linux_amd64.tar.gz`を取得します：

```bash
$ wget https://github.com/xeol-io/xeol/releases/download/v0.9.15/xeol_0.9.15_linux_amd64.tar.gz
```

## 2. xeolのバイナリファイルの解凍

そして、解凍行います：

```bash
$ tar xvf xeol_0.9.15_linux_amd64.tar.gz
```

解凍後、以下のように結果が出力されます：

```bash
CHANGELOG.md
LICENSE
README.md
xeol
```

## 3. xeolのpathを通す

最後にxeolを実行するために適切な場所へxeolバイナリを移動します：

```bash
$ sudo mv xeol /usr/local/bin
```

今回は、`/usr/local/bin`へxeolを配置しました。
一時的なディレクトリ`/tmp/xeol`は必要ないので削除してしまってもよいです：

```bash
$ cd ~
$ rm -rf /tmp/xeol/
```

xeolの場所とバージョンを確認し、以下のように出力されていれば、xeolのインストールは成功です：

```bash
$ xeol version
Application:         xeol
Version:             0.9.15
BuildDate:           2024-04-27T01:18:39Z
GitCommit:           d60c12a433d120bc863292f857075f9214aba5be
GitDescription:      v0.9.15
Platform:            linux/amd64
GoVersion:           go1.21.9
Compiler:            gc
Syft Version:        v0.92.0
Supported DB Schema: 1
```

```bash
$ which xeol
/usr/local/bin/xeol
```

# xeol DB (データベース)

xeolは専用のEOLデータベースを用いて、EOLの検知を行います。ここでは、xeolでスキャンをする前に、xeol DBの使い方を説明します。xeolをインストール後、自分のローカル環境にxeol DBを配置する必要があります。`xeol db`コマンドを用いてxeolデータベースに関する操作を行うことができます。

xeol DBは[endoflife.date](https://endoflife.date/) というEOLのデータベースから情報を取得し、構築されています。[endoflife.date](https://endoflife.date/)にはサポート延長日もデータに含まれています。しかし、xeolの現在バージョンでは、これをサポートしていないため、サポート延長日が必要な場合は、[endoflife.date](https://endoflife.date/)で調べる必要があります。

### 1. DBリスト取得

まず、以下のコマンドで、xeol DBのリストを取得します：

```bash
xeol db list
```

このとき、以下のようにxeol DBの一覧が取得できます

```
Built:    2024-04-27 03:51:15.855588 +0000 UTC
URL:      https://data.xeol.io/xeol/databases/xeol-db_v1_2024-04-27T03:51:15.855588Z.tar.gz
Checksum: sha256:c8ae8ccfeea4cca29c90e3e16ec298a17bdb56e83a107cbdaa2154b14e250ab8

Built:    2024-04-26 03:51:15.846861 +0000 UTC
URL:      https://data.xeol.io/xeol/databases/xeol-db_v1_2024-04-26T03:51:15.846861Z.tar.gz
Checksum: sha256:71129db1b0e3192721fa79b4080f2410d164512ac98888c5b67fe7cc68f6cc89

Built:    2024-04-25 03:51:15.580408 +0000 UTC
URL:      https://data.xeol.io/xeol/databases/xeol-db_v1_2024-04-25T03:51:15.580408Z.tar.gz
Checksum: sha256:6dd34fa02990e7e9eba31974350651046c81ad084667ad2eeea0a41d03f257ca
.............
.............
```

xeolのデータベースは毎日更新されています。

### 2. xeol DBの設置

リストを取得後、以下のコマンドを用いて最新のデータベースを取得し、配置します：

```bash
$ wget https://data.xeol.io/xeol/databases/xeol-db_v1_2024-04-27T03:51:15.855588Z.tar.gz

$ xeol db import xeol-db_v1_2024-04-27T03:51:15.855588Z.tar.gz
EOL database imported
```

これで、ローカル上にgrype DBを設置することができました。DBの場所はユーザーのディレクトリ`home/usr名`の`.cache/xeol/db/1`というディレクトリに存在しています。

2024/04/27 現在では、xeol DBのバージョンは1です。

### 3. xeol DBのバージョン確認

以下のコマンドを実行すると、現在使用しているxeol DBの情報を見ることができます：

```bash
$ xeol db status
Location:  /home/usr名/.cache/xeol/db/1
Built:     2024-04-27 03:51:15.855588 +0000 UTC
Schema:    1
Checksum:  sha256:73d8294c23d86f410aa061081895d2834e5b2ce5fcbf4619edcf6b67c54672c7
Status:    valid
```

## xeol DB管理用のコマンド

xeol DBを管理するためのコマンドをここでまとめておきます。脆弱性検知ツールgrypeとほとんど同じコマンドです。
 `xeol db status` : 現在のxeol DBの状態を確認できる。DBの場所、ビルドされた日付、checksumを確認できる。

`xeol db check`  DBに対して、アップデートが可能かどうかを見ることができます。

`xeol db update` : 最新のlatestのDBが`.cache`ディレクトリへダウンロードされる。デフォルトでxeolではスキャンするたびに最新のDBにアップデートされる。

`xeol db list` : ダウンロード可能なDB、そのURLを確認することができる。

`xeol db import` : xeol DBのアーカイブを使って、DBをダウンロードできる。

`xeol db --help` : コマンドに関する情報を確認することができる。

## xeol DBの注意点

xeol DBは毎日最新のDBに更新されており、正確なEOL情報を手に入れるために、xeolスキャン時に最新のDBにアップデートしていく必要があります。また、ローカルのDBが過去5日以内にbuildされていない場合、xeolのスキャン実行に失敗するため注意が必要です。

<!-- Grypeが正確なマッチを提供するためには、最新の脆弱性情報が必要です。デフォルトでは、ローカルデータベースが過去5日以内に構築されていない場合、実行に失敗します。データの陳腐化チェックは環境変数`GRYPE_DB_MAX_ALLOWED_BUILT_AGE` と `GRYPE_DB_VALIDATE_AGE` 、または `db` 配下のフィールド `max-allowed-built-age` と `validate-age` で設定できます。golang's time duration syntax](https:／／pkg.go.dev／time＃ParseDuration)を使う。GRYPE_DB_VALIDATE_AGE`または`validate-age`に`false`を設定すると、stalenessチェックが無効になる。  -->

## xeolのスキャン対象

xeolのスキャン対象は以下です。

```bash
podman:yourrepo/yourimage:tag          use images from the Podman daemon
docker:yourrepo/yourimage:tag          use images from the Docker daemon
docker-archive:path/to/yourimage.tar   use a tarball from disk for archives created from "docker save"
oci-archive:path/to/yourimage.tar      use a tarball from disk for OCI archives (from Skopeo or otherwise)
oci-dir:path/to/yourimage              read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
singularity:path/to/yourimage.sif      read directly from a Singularity Image Format (SIF) container on disk
dir:path/to/yourproject                read directly from a path on disk (any directory)
sbom:path/to/syft.json                 read Syft JSON from path on disk
registry:yourrepo/yourimage:tag        pull image directly from a registry (no container runtime required)
att:attestation.json --key cosign.pub  explicitly use the input as an attestation
```

基本的に

```bash
xeol スキャンしたいリソース:スキャンしたいイメージ、ファイル、ディレクトリ名
```

でスキャンすることが可能です。ただし、コンテナイメージをxeolで直接スキャンする場合は

```bash
xeol <image name>
```

imge:<image 名>とする必要がありません。

# xeolでSBOMをスキャンしてみる

チュートリアルとして、SBOM生成ツール**syft**で生成したSBOMをスキャンし、使用している環境内に潜むEOL パッケージを見ていきます。

### SBOMを生成

今回は、pythonとdebianのEOLを検知してきます。

まず、EOLのパッケージを含むコンテナイメージをpullします。

```bash
 docker pull django:1.7.1-python2
```

次に、pullしてきたイメージをsyftでスキャンし、SBOMを生成します。

```bash
syft django:1.7.1-python2 -o cyclonedx-json > sbom_django:1.7.1-python2.json
```

### SBOMをスキャンする

先ほどスキャンしたSBOM sbom_django:1.7.1-python2.json をxeolでスキャンします。

```bash
$ xeol sbom:sbom_django:1.7.1-python2.json
 ✔ EOL DB                          [no update available]
 ✔ Scanned for EOL                 [2 eol matches]
NAME    VERSION  EOL         DAYS EOL  TYPE
debian  8        2018-06-17  2141      os
python  2.7.9    2020-01-01  1578      UnknownPackage
```

OSのディストリビューションであるdebian バージョン8と言語のランタイムであるpython バージョン2.7.9のEOLを検知することができました。

ここで、スキャン結果のテーブルのヘッダーの項目はそれぞれ以下を表しています：

- NAME : パッケージの名前
- VERSION : 使用しているバージョン
- EOL : EOLとなった日付
- DAYS EOL : EOLとなってから今日までの日数
- TYPE : パッケージのタイプ

もしも、結果をjsonで出力したい場合は、

```bash
xeol sbom:sbom_django:1.7.1-python2.json -o json=xeol_django:1.7.1-python2.json
```

とオプションを付けてください。

すると以下のようにより詳細な情報を得ることができます。

Debianの場合は

```json
{
   "Cycle": {
    "ProductName": "Debian",
    "ProductPermalink": "https://endoflife.date/debian",
    "ReleaseCycle": "8",
    "Eol": "2018-06-17",
    "LatestRelease": "",
    "LatestReleaseDate": "2018-06-23",
    "ReleaseDate": "2015-04-25"
   },
   "Package": {
    "ID": "",
    "Name": "",
    "Version": "",
    "Locations": {},
    "Language": "",
    "Licenses": null,
    "Type": "",
    "CPEs": null,
    "PURL": "",
    "Upstreams": null,
    "MetadataType": "",
    "Metadata": null
   },
   "artifact": {
    "name": "debian",
    "version": "8",
    "type": "os",
    "locations": [],
    "language": "",
    "licenses": [],
    "cpes": [],
    "purl": "",
    "upstreams": []
   }
  },
```

pythonの場合は

```json
{
   "Cycle": {
    "ProductName": "Python",
    "ProductPermalink": "https://endoflife.date/python",
    "ReleaseCycle": "2.7",
    "Eol": "2020-01-01",
    "LatestRelease": "",
    "LatestReleaseDate": "2020-04-19",
    "ReleaseDate": "2010-07-03"
   },
   "Package": {
    "ID": "",
    "Name": "",
    "Version": "",
    "Locations": {},
    "Language": "",
    "Licenses": null,
    "Type": "",
    "CPEs": null,
    "PURL": "",
    "Upstreams": null,
    "MetadataType": "",
    "Metadata": null
   },
   "artifact": {
    "name": "python",
    "version": "2.7.9",
    "type": "UnknownPackage",
    "locations": [
     {
      "path": "/usr/local/bin/python2.7",
      "layerID": "sha256:f3e766555345cb150d2b85d4cf8abb3077cf31685e22ca98bac37fbd4bd8a8c3"
     },
     {
      "path": "/usr/local/lib/libpython2.7.so.1.0",
      "layerID": "sha256:f3e766555345cb150d2b85d4cf8abb3077cf31685e22ca98bac37fbd4bd8a8c3"
     }
    ],
    "language": "",
    "licenses": [],
    "cpes": [],
    "purl": "pkg:generic/python@2.7.9",
    "upstreams": []
   }
  }
```

出力を結果をみると、OSはnameとversion、言語はpurlを使ってデータベースとのマッチングを行っていそうですね。

次回は、実際にソースコードを確認して、マッチング方法について調べたり、EOL検知範囲について、実際にデータベースを見て、調査していきたいと思います。