Skip to main content

In this article

コンテナ向けコマンドラインツール—Buildah、Podman、SkopeoでSnykを活用

2020年12月9日

0 分で読めます

コンテナエコシステムが成熟するにつれ、ソフトウェアの選択肢も、それらを組み合わせる方法も、ますます豊富になっています。

選択肢のひとつが、Buildah、Podman、Skopeoの組み合わせです。これらはRed Hatのエコシステムから生まれた、3つのオープンソースのコマンドラインツールです。名前のとおり、BuildahはOCI準拠のコンテナやイメージを構築するための幅広い機能を提供し、PodmanはOCIコンテナの管理と実行を担います。また、Skopeoはコンテナレジストリを操作するためのツールです。

この記事では、オープンスタンダードを活用することで、Snykのコンテナスキャンがこれらすべてのテクノロジーと連携する仕組みを紹介します。

まずはSkopeoを見てみましょう。Skopeoはコンテナイメージをリポジトリ間で移動するコマンドラインツールで、イメージをローカルに保存することもできます。イメージはDockerアーカイブまたはOCIアーカイブとして出力でき、どちらもSnykでファイルシステムからスキャンできます。Quay.ioからイメージをダウンロードし、ローカルのファイルシステムからスキャンしてみましょう。

$ skopeo copy docker://quay.io/tutum/hello-world oci-archive:hello-world.tar
$ snyk container test oci-archive:hello-world.tar 

Testing oci-archive:hello-world.tar…

{-----OUTPUT SNIPPED-----}

✗ High severity vulnerability found in openssl
  Description: Out-of-Bounds
  Info: https://snyk.io/vuln/SNYK-UBUNTU1210-OPENSSL-374571
  Introduced through: openssl@1.0.1c-3ubuntu2.6, ca-certificates@20120623, ssl-cert@1.0.32, meta-common-packages@meta
  From: openssl@1.0.1c-3ubuntu2.6
  From: ca-certificates@20120623 > openssl@1.0.1c-3ubuntu2.6
  From: ssl-cert@1.0.32 > openssl@1.0.1c-3ubuntu2.6
  and 1 more...
  Fixed in: 1.0.1c-3ubuntu2.7

Organization:      matt-jarvis-snyk
Package manager:   deb
Project name:      docker-image|hello-world.tar
Docker image:      oci-archive:hello-world.tar
Licenses:          enabled

Tested 237 dependencies for known issues, found 25 issues.

Ubuntu 12.10 is no longer supported by the Ubuntu maintainers. Vulnerability detection may be affected by a lack of security updates.

ご覧のとおり、このイメージはサポート終了(EOL)バージョンのUbuntuを使用しているため、修正されない複数の脆弱性が含まれています。このようにSkopeoを使えば、標準に準拠したあらゆるコンテナレジストリから、Snykがサポートする形式でイメージをダウンロードし、ローカルでスキャンできます。

Buildah

次に、Buildahを見てみましょう。Buildahはイメージを構築するコマンドラインツールで、実行はしないため、Podmanと組み合わせて使うことを想定しています。また、Linuxカーネルのユーザーネームスペースを利用して、ユーザーレベルの権限だけを持つroot相当のシェルを分離する、root権限なしのモードで動作するよう設計されています。これにより管理者は、最小権限の原則をビルド環境で守りながら、開発者にコンテナイメージの構築と管理を任せられます。

BuildahはDockerと同様にDockerfileを使えますが、たとえばBashやPythonなど、ほかのワークフローを使ってコンテナやイメージを開発することもできます。柔軟性が高まるだけでなく、コンテナのビルドをデバッグしやすくなる場合もあります。Buildahではビルド中に中間コンテナをマウントできるため、スクリプトで処理が正しく実行されたかを確認してから続行したり、ソフトウェアをホスト上でビルドしたり、ユーザー入力を受け付けたりできます。これは、Dockerのベストプラクティスとして定着した、複雑になりがちなマルチステージビルドに代わる方法です。

実際にBuildahを使う例を見てみましょう。

まず、Buildahでは、使い慣れたワークフローであればDockerfileをそのまま使ってコンテナをビルドできます。例として、次のDockerfileを見てみましょう。

$ cat Dockerfile 
FROM centos:8
LABEL maintainer Matt Jarvis <matt@mattjarvis.org.uk>

RUN dnf install -y git gcc findutils make help2man texinfo gperf gettext-devel autoconf automake && dnf clean all

RUN git clone https://git.savannah.gnu.org/git/hello.git /opt/hello

WORKDIR /opt/hello

RUN ./bootstrap --skip-po && ./configure && make && make install

RUN ./hello
ENTRYPOINT "/usr/local/bin/hello"

ここではCentOS 8イメージをベースに、GNU Helloのソースコードをクローンし、前提ソフトウェアをインストールしたうえで、バイナリをビルドしてインストールしています。

bud(build-using-dockerfile)オプションを使うと、Buildahで自動的にビルドできます。

$ buildah bud

しかし、ソースコードとビルドに必要なソフトウェアがどちらも最終イメージに含まれるため、これは明らかに望ましい方法ではありません。また、このイメージはセキュアなイメージ開発のベストプラクティスの大半に反しています。Dockerを使う場合、ここでのベストプラクティスはマルチステージビルドです。BuildahでマルチステージDockerfileを使うこともできますが、さらに興味深いのは、Buildahのネイティブコマンドを使ってスクリプトを作成する方法です。上のDockerfileで行ったことをそのまま再現するには、次のスクリプトを実行します。

$ cat single-stage.sh 
#!/usr/bin/env bash

set -o errexit

# Create a container
container=$(buildah from centos:8)

# Labels are part of the "buildah config" command
buildah config --label maintainer="Matt Jarvis <matt@mattjarvis.org.uk>" $container

# Install pre-reqs
buildah run $container dnf install -y git gcc findutils make help2man texinfo gperf gettext-devel autoconf automake
buildah run $container dnf clean all

# Clone the git repository
buildah run $container git clone https://git.savannah.gnu.org/git/hello.git /opt/hello

# Workingdir is also a "buildah config" command
buildah config --workingdir /opt/hello $container

buildah run $container ./bootstrap --skip-po
buildah run $container ./configure
buildah run $container make
buildah run $container make install

# Entrypoint is also a “buildah config” command
buildah config --entrypoint /usr/local/bin/hello $container

# Finally save the running container to an image
buildah commit --format docker $container hello:latest

さらにBuildahでは、ビルド中に中間コンテナをマウントできます。そのため、ソースコードのビルドをホスト上で行い、イメージとして確定する前に、生成したバイナリだけをコンテナにコピーできます。こうすれば、ビルド時にホストのリソースを活用できるほか、ホストのパッケージマネージャーを使ってコンテナにパッケージをインストールすることもできます。その場合もroot権限は不要です。Buildahはunshareコマンドを使い、ユーザーネームスペースによって特権なしでのマウントをサポートしています。

$ buildah unshare
$ cat host-mount.sh 
#!/usr/bin/env bash

set -o errexit

# Create a container
container=$(buildah from centos:8)
mountpoint=$(buildah mount $container)

buildah config --label maintainer="Matt Jarvis <matt@mattjarvis.org.uk>" $container

# Clone the git repository to the host
git clone https://git.savannah.gnu.org/git/hello.git hello

pushd hello
./bootstrap --skip-po
./configure
make
# Install to the container
make install DESTDIR=${mountpoint}
popd

chroot $mountpoint bash -c "/usr/local/bin/hello"

buildah config --entrypoint "/usr/local/bin/hello" $container
buildah commit --format docker $container hello
buildah unmount $container

上の例のように、unshareコマンドを実行するとrootシェルが使えるようになりますが、これは完全なroot権限を持たないユーザーネームスペース内のものです。これにより、ビルド中にコンテナのファイルシステムをマウントできます。Bashスクリプトなので、プログラミング言語で実現できる機能も活用できます。条件分岐やループを使ったり、処理中にユーザーからの入力を求めたりすることも可能です。

Podman

このスクリプトを実行してイメージをビルドしたら、Snykでスキャンできます。ここで活躍するのがPodmanです。Podmanは、OCIコンテナの開発、管理、実行に使えるデーモンレスのコンテナエンジンです。BuildahとPodmanはオープンスタンダードに基づいているため、Podmanでイメージをディスクに保存し、ファイルシステムからスキャンする方法で、SnykはこれまでもPodmanで作成または取得したイメージをスキャンできました。Podmanでは、標準的なDockerアーカイブ形式とOCIアーカイブ形式のどちらでもイメージを保存でき、どちらもSnykでサポートされています。

[root@localhost buildah_test]# podman images
REPOSITORY                         TAG     IMAGE ID      CREATED         SIZE
localhost/hello                    latest  975f60fc1f19  15 minutes ago  223 MB
[root@localhost buildah_test]# podman save 975f60fc1f19 -o hello.tar
[root@localhost buildah_test]# snyk container test docker-archive:hello.tar

Testing docker-archive:hello.tar...

{-----OUTPUT SNIPPED-----}

✗ High severity vulnerability found in librepo
  Description: RHSA-2020:3658
  Info: https://snyk.io/vuln/SNYK-CENTOS8-LIBREPO-610057
  Introduced through: librepo@1.11.0-2.el8
  From: librepo@1.11.0-2.el8
  Fixed in: 0:1.11.0-3.el8_2

Organization:      matt-jarvis-snyk
Package manager:   rpm
Project name:      docker-image|hello.tar
Docker image:      docker-archive:hello.tar
Platform:          linux/amd64
Licenses:          enabled

Tested 172 dependencies for known issues, found 28 issues.

Pro tip: use `--file` option to get base image remediation advice.
Example: $ snyk test --docker docker-archive:hello.tar --file=path/to/Dockerfile

To remove this message in the future, please run `snyk config set disableSuggestions=true`

ベースイメージはDocker Hubで公開されているため、このイメージをSnyk CLIでスキャンするだけなら、次の方法も使えます。

[root@localhost buildah_test]# snyk container test centos:8

この場合、SnykはDocker Hubに直接アクセスし、上流のイメージをスキャンします。

しかし、イメージがPodmanのローカル環境にしかない場合や、ログインが必要なDocker Hubのプライベートリポジトリにある場合は、どちらの方法も使えません。例として、Podmanからビルドしたイメージを直接スキャンしてみましょう。

[matt@localhost buildah_test]# podman images
REPOSITORY                         TAG     IMAGE ID      CREATED         SIZE
localhost/hello                    latest  975f60fc1f19  20 minutes ago  223 MB

[matt@localhost buildah_test]# snyk container test localhost/hello
connect ECONNREFUSED 127.0.0.1:443

このようなケースでは、SnykはRegistry APIまたはDockerソケットを介して、ローカルのDockerクライアントを使います。システムにDockerバイナリがないため、この例ではSnykがRegistry APIを使おうとしましたが、接続に失敗しました。デフォルトでは、SnykはPodmanのローカル環境にイメージがあることも、Podmanを使って上流のレジストリにアクセスする方法も認識できません。

幸い、Podmanのpodman-dockerパッケージを使えば、簡単に互換性を確保できます。このパッケージは、Podmanバイナリを呼び出すシェルラッパーにすぎない疑似Dockerバイナリを提供し、Podman APIソケットとDockerソケットのデフォルトの場所をシンボリックリンクでつなぎます。インストール直後の設定では、root権限でPodman APIを実行する際のデフォルトである/var/run/podman/podman.sockにリンクが作成されます。

[matt@localhost buildah_test]$ sudo dnf install podman-docker

Now let’s try and scan our local image again :

[matt@localhost buildah_test]$ snyk container test localhost/hello
connect EACCES /var/run/docker.sock

まだ動作しませんが、今度は別のエラーが発生しました。Snykはdockerというバイナリを検出し、特権付きのDockerソケットのデフォルトの場所に接続しようとしています。まだPodman APIを実行していないため接続できず、再び失敗します。

先ほど見たように、podman-dockerパッケージは、Podman APIを特権付きで実行する想定で、/var/run/podman/podman.sockから/var/run/docker.sockへのシンボリックリンクを作成します。

[matt@localhost buildah_test]$ ls -l /var/run/docker.sock 
lrwxrwxrwx. 1 root root 23 Nov 25 07:51 /var/run/docker.sock -> /run/podman/podman.sock

Dockerソケットの主な問題のひとつは、デフォルトで特権付きで実行されることで、環境によってはセキュリティリスクになります。一般的なローカル開発環境では、Podman APIをそのように実行する必要はありません。別の場所で、特権なしのソケットを実行できます。また、DOCKER_HOST環境変数を使えば、その場所をSnykに伝えられます。この変数はtcp://とunix://の両方のURL形式をサポートしています。

別のシェルを開き、特権なしのソケットでPodman APIを実行しましょう。

[matt@localhost ~]$ podman system service --time=0 unix://home/matt/podman.sock

これはフォアグラウンドで実行されるため、テストが終わったらctrl-Cでプロセスを終了してください。次に、DOCKER_HOST環境変数を設定します。

[matt@localhost buildah_test]$ export DOCKER_HOST=unix:///home/matt/podman.sock

最後に、Snykでローカルイメージをもう一度スキャンしてみましょう。

[matt@localhost buildah_test]$ snyk container test localhost/hello

Testing localhost/hello...

{ ----SNIPPED OUTPUT----}

✗ High severity vulnerability found in librepo
  Description: RHSA-2020:3658
  Info: https://snyk.io/vuln/SNYK-CENTOS8-LIBREPO-610057
  Introduced through: librepo@1.11.0-2.el8
  From: librepo@1.11.0-2.el8
  Fixed in: 0:1.11.0-3.el8_2

Organization:      matt-jarvis-snyk
Package manager:   rpm
Project name:      docker-image|localhost/hello
Docker image:      localhost/hello
Platform:          linux/amd64
Licenses:          enabled

Tested 172 dependencies for known issues, found 28 issues.

Pro tip: use `--file` option to get base image remediation advice.
Example: $ snyk test --docker localhost/hello --file=path/to/Dockerfile

To remove this message in the future, please run `snyk config set disableSuggestions=true`

成功です。Snykが正しく動作し、特権なしのソケットを使ってPodmanと通信できています。

シェルでDOCKER_HOSTを恒久的に設定するには、exportコマンドを.bashrcファイルに追加します。これで設定が保持されます。

最後に、ログインするたびにこの設定全体が自動的に利用できるようにしたいところです。そのためには、systemdのソケットアクティベーション機能を活用し、ログイン後にソケットへ接続しようとしたとき、自動的にソケットが起動するようにします。

まず、ソケットを設定します。

[matt@localhost ~]$ cat .config/systemd/user/podman.socket
[Unit]
Description=Podman API Socket
Documentation=man:podman-api(1)

[Socket]
ListenStream=/home/matt/podman.sock
SocketMode=0660

[Install]
WantedBy=sockets.target

次に、ソケットをサービスに関連付けます。

[matt@localhost ~]$ cat .config/systemd/user/podman.service
[Unit]
Description=Podman API Service
Requires=podman.socket
After=podman.socket
Documentation=man:podman-api(1)
StartLimitIntervalSec=0

[Service]
Type=oneshot
Environment=REGISTRIES_CONFIG_PATH=/etc/containers/registries.conf
ExecStart=/usr/bin/podman system service unix:///home/matt/podman.sock
TimeoutStopSec=30
KillMode=process

[Install]
WantedBy=multi-user.target
Also=podman.socket

最後に、systemdの設定を再読み込みして、サービスを有効にします。

[matt@localhost ~]$ systemctl --user daemon-reload
[matt@localhost ~]$ systemctl --user enable --now podman.socket

まとめ

以上のように、Podmanを使ったSnyk CLIのイメージスキャンはDockerの場合と同じように機能します。開発者は開発ワークフローの一環として、権限を昇格させることなく、ローカルのDockerイメージまたはOCIイメージを手軽に包括的にスキャンできます。また、オープンスタンダードの力を活用し、SkopeoとBuildahもSnykと連携できることを紹介しました。

Snykアカウントをまだお持ちでない方は、無料で登録して、コンテナイメージとオープンソースの依存関係の両方をスキャンしましょう。

キャプチャー・ザ・フラッグを始めよう

オンデマンドのバーチャル入門ワークショップを視聴して、キャプチャー・ザ・フラッグの課題の解き方を学びましょう。

続きを読む

feature customer snowflake
Article

開発初期からセキュリティを確保:Snowflake Cortex Code向けSnyk Studioインテグレーションを発表

Snyk StudioがSnowflake Cortex Codeと連携し、開発中にAI生成コード、依存関係、コンテナの脆弱性をスキャンします。

blog feature pypi spoof
Article

オープンソースのメンテナーに、Snyk AI Security Platformをすべて無料で提供

オープンソースのメンテナーは、実際の脆弱性レポートに追われ、優先順位付けや修正、修正版のリリースを迅速に進めるための支援を必要としています。SnykのSecure Developer Programでは、条件を満たすプロジェクトにSnyk AI Security Platformを無料で提供します。

feature insights announcement
Blog

Node-gypサプライチェーン侵害:binding.gypに潜む自己増殖型npmワーム

新たなnpmワームがbinding.gypを悪用し、インストール時にnode-gypを起動。ライフサイクルスクリプトを使わずに悪意あるパッケージからコードを実行します。認証情報を窃取し、GitHub上に潜伏し、メンテナー間で自己増殖します。