SLSA(Supply-chain Levels for Software Artifacts)について

  •  
  •  

セキュリティが大事な昨今なので、SLSAというものについて調べてみた

SLSAとはソフトウェアサプライチェーンにおける完全製確保のためのセキュリティフレームワークとのこと
(セキュリティ分野で言うフレームワークはセキュリティを確保するために策定・定義された指針や基準、ガイドライン等のことを言うらしい)

SLSAが重視するのはサプライチェーンの完全性である。そして可用性に二番目の焦点を置いている。
ソフトウェアのライフサイクルのどの段階でも改竄から保護すること。
SLSAでは完全性をソース完全性とビルド完全性に分類している。

ソース完全性:ソースリビジョンが作者の意図を表している。期待されるプロセスが遵守されている。リビジョンが承認された後に変更されていない。

ビルド完全性:パッケージが作者によって定義された方法に従ってビルドされている。修正されていない正しいソースと依存関係からビルドされて開発段階間で成果物が渡されるときに成果物が修正されていないことを保証する

可用性:パッケージが将来にわたりビルドと保守を継続でき、全てのコードと変更履歴が調査やインシデント対応に利用できることを保証する

ShipIt

  •  
  •  
  • Category:

PerlモジュールのCPANへのアップロードはShipItというのが良いらしいので使ってみたのだが、ちょいちょい詰まりどころがあったのでメモ

ShipItのインストールにはcpanminusを使用します。
cpanminusのインストール等はここでは書きません。

ShipItのインストール

$ cpanm ShipIt

ShipItの準備

設定ファイルの作成

設定ファイル.shipitをプロジェクトディレクトリの直下に作成します。
0から書いても良いですが、コマンドで自動生成できます。

$ shipit --write-config

これで以下のようなファイルが生成されます。

# auto-generated shipit config file.
steps = FindVersion, ChangeVersion, CheckChangeLog, DistTest, Commit, Tag, MakeDist
# svn.tagpattern = MyProj-%v
# svn.tagpattern = http://code.example.com/svn/tags/MyProj-%v
# CheckChangeLog.files = ChangeLog, MyProj.CHANGES

stepsがshipitで実行する処理の内容です。左から順番に実行されます。
CPANにアップロードする際は UploadCPAN というステップを書き足します。

ちなみに私の設定は以下です。

steps = FindVersion, ChangeVersion, Commit, Tag, MakeDist, UploadCPAN
git.push_to = origin

それぞれのステップの意味は以下のような感じです。

  • FindVersion: 現在のバージョンを取得、リリースを行うバージョンをユーザーの入力から設定します
  • ChangeVersion: ソースコード内に記載されたバージョンを変更します
  • Commit: 変更をコミットしてGithubにプッシュします
  • Tag: gitのタグ付けを行いGithubにプッシュします
  • MakeDist: モジュールを作成します
  • UploadCPAN: CPANにアップロードします

ShipItの実行①

$ shipit
...
Running step ShipIt::Step::UploadCPAN=HASH(0x14dbf1a90)
Upload to CPAN? [Y/n] Y
Upload failed.

cpan-uploadのインストール

Uploadに失敗したのはアップロードするために必要なcpan-uploadコマンドが入っていないからです。
cpan-uploadは以下でインストールします。
古い記事では"cpan-upload-http"や"App::cpanupload"などをインストールすると書いてあったりしますが、時代は流れ現在は以下のモジュールをインストールするのが良さそうです。

$ cpanm CPAN::Uploader

ShipItの実行②

$ shipit
...
Upload to CPAN? [Y/n] Y
Please provide a value for --user
Upload failed.

PAUSEの設定

これはCPANアップロード先のPAUSEアカウントの設定がされてないからです。
以下の設定を~/.pauseとして保存します。

user <username>
password <password>

ShipItの実行③

$ shipit
...
Upload to CPAN? [Y/n] Y
registering upload with PAUSE web server
POSTing upload for /Users/gucchi/shipit-dist/Devel-PatchPerl-Plugin-Darwin-v0.1.2.tar.gz to https://pause.perl.org/pause/authenquery?ACTION=add_uri
request failed with error code 500
  Message: Can't verify SSL peers without knowing which Certificate Authorities to trust
Upload failed.

Mozilla::CAのインストール

これは信頼できるCAを見つけられなくて失敗したということです。
MacではCAはキーチェーンで管理されていますがPerlからは読み込めてないという感じですね。

これはMozilla::CAをのインストールで解決するようです。

$ cpanm Mozilla::CA

ShipItの実行④

$ shipit
...
Upload to CPAN? [Y/n] Y
registering upload with PAUSE web server
POSTing upload for ...
PAUSE add message sent ok [200]

成功です!

ありがとうございました!

Docker Registry API v2を触る

  •  
  •  

なぜか忘れましたがDocker Registry APIをいじります

今回はDockerHubから grafana/grafana:latest のイメージ情報を取得してみます。

とりあえずAPIには認証がかかっておりtokenが必要ですのでまずはtokenを取得します。
取得したtokenはtokenという変数にセットします。
ちなみにshellはfish shellです。

set token (curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:grafana/grafana:pull" | jq -r '.token')

次にmanifestのリストを取得します。

manifestのリストはDocker Registryにはイメージが各アーキテクチャ毎に登録されているので、そのリストになります。

tokenの値はAuthorizationヘッダーに"Bearer <token>"という形で設定します。

curl -sSL -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" -H "Authorization: Bearer $token" "https://registry-1.docker.io/v2/grafana/grafana/manifests/latest" | jq . 677ms  土 4/13 23:22:59 2024
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 2416,
"digest": "sha256:3aa2a03d88ad9628485f78e89adcd5b18bcde1aced3e6146bbbd7a718b82b797",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 2411,
"digest": "sha256:173b46f35cdbcb4b137868a5488be265eb0bb8487d959bb970e62171225f202f",
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 2411,
"digest": "sha256:06c16204fb61a9623d0a3910d0ca7933d751858b36ce0bc9676e507c9f9e3fec",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
}
}
]
}

次にアーキテクチャ別のmanifestを取得します。
今回はMacを利用しているということでarm64のイメージのmanifestを取得してみます。
digestの値を指定します。

curl -sSL -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -H "Authorization: Bearer $token" "https://registry-1.docker.io/v2/grafana/grafana/manifests/sha256:173b46f35cdbcb4b137868a5488be265eb0bb8487d959bb970e62171225f202f" | jq . 599ms  日 4/14 03:39:48 2024
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 7159,
"digest": "sha256:46becd7c39d0d018028287aa36463a747e4b1149abddeb494e66454849f67376"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3347715,
"digest": "sha256:bca4290a96390d7a6fc6f2f9929370d06f8dfcacba591c76e3d5c5044e7f420c"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 140,
"digest": "sha256:0b9a5f2b0f5d5270e3efb34422e3a485380df63bc44f805762a8278aae8a22d8"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 3264743,
"digest": "sha256:fcc6842772413979645ab1a68f5a281b53f5c1b7957d8c2afbec10d0eae3e467"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 92,
"digest": "sha256:e1ca30b94fb8c90a259a124eedd07ee51865707386ca6572d443e07b38a30966"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 47122,
"digest": "sha256:c9b151a9c147058dd576609bea6d5ab3e6a8e956ce510e64a6cc01fbee849eea"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 23287,
"digest": "sha256:0b708c20c8dfe23bb6c3a4cab29597599ccb1a0da3722d379a6c33f614c97f0d"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 54154543,
"digest": "sha256:d81743f85f1a571db9633f3972d118d2648dffe75d2958936c961e6fa66d6670"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 50166186,
"digest": "sha256:21c7139cb50e2c66ca3e2bf9ab5f8288a49d4faaebf7dbdaf28463f210ae24bd"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 11936,
"digest": "sha256:a049ab0dfb1f13f3fe22786ef0dcac844db1c55ef440e0007d8664b32176da1b"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1226,
"digest": "sha256:43862b0c980ecbfe03f1033b43f01f80e3f9396de639e141b8a56d03a492b6ff"
}
]
}

Movable TypeをPSGIで動かす

Movable TypeはCGIプログラムですが、
CGIだとリクエストのたびにPerlを起動してプログラムの動かすため非常に遅いです。

とうわけでもう少し高速化したいと思います。

perlの高速化というとmod_perlやFastCGIが真っ先に思い浮かびますが、今回はもっとナウでヤングなPSGIで動かしたいと思います。

PSGIとはPerl Web Server Gateway Interfaceの略で、WebサーバーとWebアプリケーションを繋ぐインターフェイスになります。
PythonのWSGI、RubyのRackをインスパイアしたものみたいです。

というわけでPSGIで高速化してみました。

今回はApacheをリバースプロキシとし、PSGIサーバーはStarmanを使用する。



MTの設定

mt-config.cgiにPIDを保存するファイルを設定する
以下を追記(別にパーミッションさえちゃんとしていればパスはどこでも良い)

PIDFilePath /var/run/mt/mt.pid



Starman


インストール

$ cpanm Task::Plack
$ cpanm XMLRPC::Transport::HTTP::Plack

Task::PlackをインストールすればStarman他関連モジュールがインストールされるとのこと。


systemctlの設定

[Unit]
Description=starman for mt
After=syslog.target
After=network.target
After=mysql.service
[Service]
User=apache
Group=apache
Restart=always
SyslogIdentifier=starman
Environment=MT_HOME=/var/www/cgi-bin/mt
Environment=MT_CONFIG=${MT_HOME}/mt-config.cgi
ExecStart=/bin/sh -c "/usr/local/perl5/perls/perl-5.30.0/bin/starman ${MT_HOME}/mt.psgi --access-log /var/log/starman/access_log --error-log /var/log/starman/error_log --pid `perl -ne 'next if /^#/; print $1 if /^\s*PIDFilePath\s+(.*)/i' ${MT_CONFIG}`"
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

Unitセクションには依存の設定等を書く
movable typeはmysqlを使用するのでmysql起動後に起動するようにする

Serviceセクションにはサービスの起動設定等を書く
今回はExecStartにstarmanの起動コマンドを書いた

あとは以下のようにして自動的に起動するようにしておけば安心

$ sudo systemctl enable starman



Apacheの設定

安全にsslでアクセスするようにしたので443のVirtualHostで以下を設定した

<VirtualHost _default_:443>
...
<IfModule proxy_module>
ProxyPass /cgi-bin/mt/ http://localhost:5000/cgi-bin/mt/
ProxyPassReverse /cgi-bin/mt/ http://localhost:5000/cgi-bin/mt/
</IfModule>
...
</VirtualHost>



これで完了!

apacheとstarmanを起動すれば無事CGI版より早くなったMTを味わえるはず。

Docker on MacのVolumeマウント先

  •  
  •  

Dockerのvolumeはどこにマウントされているか?と言うお話

$ docker volume inspect 
[
    {
        "CreatedAt": "2020-08-19T14:55:42Z",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "volume",
            "com.docker.compose.version": "1.26.2",
            "com.docker.compose.volume": "vol"
        },
        "Mountpoint": "/var/lib/docker/volumes/volume_vol/_data",
        "Name": "volume_vol",
        "Options": null,
        "Scope": "local"
    }
]

これで分かるじゃないか。/var/lib/docker/volumes/volume_vol/_dataだろ。と思った方は大間違い。

$ ls /var/lib/docker/volumes/volume_vol/_data
ls: /var/lib/docker/volumes/volume_vol/_data: No such file or directory

こういうオチが待っている。。。

ない。。。

googleさんに聞いてみると

「Docker for MacのDocker EngineはVM上で動いているので、このMountpointはMac上のPathではなくVM上のPathである」

ということらしい。

なのでscreenでVMに入ってみると良いらしい

$ screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty
Can not exec '~/Library/Containers/com.docker.docker/Data/vms/0/tty': Permission denied

。。。

$ ls ~/Library/Containers/com.docker.docker/Data/vms/0/tty
----------  1 user  staff  12  7 30 00:00 /Users/gucchi/Library/Containers/com.docker.docker/Data/vms/0/tty

というわけで、Permissionを変えたりsudoしてみたりしたがダメ


こんな時はnsenterを使えってVMに入るらしい。

$ docker run -it --privileged --pid=host alpine nsenter -t 1 -m -u -n -I sh
# ls /var/lib/docker/volumes/volume_vol/_data
hello.txt

あった!

しかし毎度毎度Docker for Macの特殊な点に悩まされるなぁ。。。