kWatanabe 記事一覧へ

kWatanabe の 技術帖

某企業でOSや仮想化の研究をやっているインフラ系エンジニア。オンプレとクラウドのコラボレーションなど、興味ある技術を綴る。

KVMでTPMをエミュレートしてWindows11を動かす

  • KVM (Proxmox VE) で TPM 2.0 をエミュレートする環境を構築した
  • KVM (Proxmox VE) で TPM 2.0 をエミュレートしたVMを作り、Windows11を動かしてみた

Windows11のシステム要件

先日、Windows11がリリースされたが、その動作条件の厳しさに一部で話題になっている。

www.microsoft.com

上記の Microsoft のWebサイトから一部引用すると以下の通り。

項目 要件
CPU 1GHz で 2コア以上
Intel なら Coffee Lake 以降
AMD なら Zen+ 以降
RAM 4GB 以上
Storage 64GB 以上
Firmware UEFI / Secureboot 対応
TPM 2.0 以降

CPUの世代は、KabyLake も Zen も 5年前 のものだし、Windows10も継続提供されるので、「新しいOSを使いたければ、新しいマシンを使ってください」という主張はまぁ良しとする。(なんだかんだで動くでしょうし)

ただ、TPM が必須となっている点はちょっと気になるところ。VMTPMのエミュレーションって、可能だったかな。

VMでのTPMエミュレーション

ざっと調べてみただけでも、イマドキのハイパーバイザでは vTPM のサポートに特に問題はなさそう。

QEMU/KVM

QEMU/KVM では、swtpm というモジュールで vTPM をサポートしている。

qemu-project.gitlab.io

Hyper-V

Hyper-V では、Windows Server 2016 以降で vTPM をサポートしている。

docs.microsoft.com

VMware vSphere

VMware vSphere では、vSphere 6.7 以降で vTPM をサポートしている。

docs.vmware.com

検証

QEMU/KVM ( Proxmox VE ) でvTPM をエミュレートできる環境を構築して、Windows11 を動かしてみる。

…つもりだったのだけれども、モタモタしていたら、先日の ProxmoxVE のアップデートで vTPM が標準でサポートされてしまった。哀しみ。

手順

元々やろうとしていたこと

本来は以下のような手順で vTPM 環境を構築するつもりだった。

# swtpm socket --tpmstate dir=/var/run/tpm --tpm2 --ctrl type=unixio,path=/var/run/tpm/sock
  • QEMUの起動オプションに TPM 用のオプションを追加して起動
...
-bios ビルドしたUEFIファーム
-chardev socket,id=tpmc,path=/var/run/tpm/sock
-tpmdev emulator,id=tpm,chardev=tpmc
-device tpm-tis,tpmdev=tpm
...
今やればいいこと

しかし、最近のProxmox VEのアップデートで、vTPMを追加するメニューが追加されたので、今ならここをポチーするだけで終了。ポコペン

f:id:kWatanabe:20211009173605p:plain

一応、注意点としては以下。

  • BIOSOVMF(UEFI) にする
  • マシンは q35 にする
  • TPMバージョンは 2.0 にする

結果

f:id:kWatanabe:20211009181530j:plain
KVM上のWindows11

f:id:kWatanabe:20211009181551j:plain
vTPM2.0も有効

とんだ肩透かしだったけど。まぁ、こんな感じ。

Pythonでブラウザで動く "ぷよぷよっぽいもの" を作る

ぷよぷよeスポーツ×プログラミング

…という教材があるんです。殆ど読んでないんですが、曰く、以下のような代物のようです。

プログラミング学習環境『Monaca Education』において、セガが展開するアクションパズルゲームぷよぷよ』をプログラミング学習できる教材です。 製品版と同じ画像素材を利用して、世界中で使われるコンピュータ言語を使い、プロが使う開発環境で本物のプログラミングをお楽しみください。

https://puyo.sega.jp/program_2020/ より引用

puyo.sega.jp

面白そうじゃないですか。やってみようと思い、著作物利用許諾書に目を通すと、以下の記述がありました。

本利用条件に基づき改変した本プログラム等の著作権著作権法第21条から第28条までに定めるすべての権利を意味します)は、当社に無償にて譲渡及び移転するものとします。また、使用者は、当社が当該本プログラム等を、当社の判断で、商用であるか否かにかかわらず、いかなる方法にて、利用、公開、公表、販売もしくは頒布等を行うことができることを承諾するものとします。

https://puyo.sega.jp/program_2020/dl/puyo-programming-code.pdf より引用

この教材には、ソースコードに加え、画像ファイルなども含まれているようなので、改変した成果物を自由に扱える権利を与えるわけにはいかないのは分かるんですけど。その成果物の権利を逆に召し上げるっていうのはどうなんだ。

なんとなく、ムッとしたので、この教材に頼らずに、「ぷよぷよっぽい何か」を独力で作ってみようと思いました。

教材に頼らず独力で作る

どうやって作るか?

kWatanabe の知るブラウザのプラットフォームと言えば Flash でした。今は HTML5 か。

ただ、kWatanabe は Low-Level な開発を生業にしており、扱える高級言語Python くらいです。ブラウザで走る Python の処理系を探しました。

brython.info

Brython という JavaScript で実装された Python 処理系です。ライセンスは 3条項BSD

brython.jsbrython_stdlib.js という js ファイルをロードして、<canvas><div> で描画領域を用意してあげれば、あとはインラインで Python コードを生で書いていくことができます。

例えば以下のような感じでしょうか。

<!DOCTYPE html>
<html>
    <head>
        <title>Brythonサンプル</title>
        <meta charset="utf-8">
        <script type="text/javascript" src="brython.js"></script>
        <script type="text/javascript" src="brython_stdlib.js"></script>
    </head>
    <body onload="brython()">
        <canvas id="drawarea" width="192" height="384" style="border: 1px solid black;"></canvas>
...
描画先となる HTML5 のオブジェクトを定義する。
...
        <script type="text/python">
from browser import document
from browser import html
...
Python コードを記述する。
...
        </script>
    </body>
</html>

JavaScript と同様、HTMLファイルにソースコードを記述していく*1ので、中身はモロバレです。隠したいなら難読化などの、工夫が必要です。

あと、brython_stdlib.js に実装されていないモジュールは使えません。なので、Python 標準のモジュール以外は基本的に使用できないですが、それでも処理系としては JavaScript と比べてかなり協力です。

kWatanabe のように JavaScript をよく知らない人間や、 JavaScript にアレルギーを持つような人には、選択肢のひとつになるんじゃないでしょうか。

成果物

プレビュー

とりあえず、こんな感じになりました。連鎖で石を消すときの処理が何か怪しい?かも。

f:id:kWatanabe:20210919233648g:plain
Brythonで実装したぷよぷよっぽいもの

デモ

近日中に、期間限定でデモを置く予定です。

操作は以下のとおりです。

キー 効果
W ゲームの開始
A 石を左に動かす
S 押し続けている間、落下速度が増す
D 石を右に動かす
スペース 石を回転させる

ソースコード

GitHubで公開します。ライセンスは GPL Version.2 です。

github.com

所感

  • JavaScript を一切書かなくても、ブラウザのフロントエンドやアプリを作れる方法があることを知った
  • とはいえ、処理系のために数MBの js をロードさせるのは、ユーザフレンドリーではなさそうなので、使いどころは選ぶかも

*1:srcタグでロードすることもできますが、そのファイルを直接開かれると結局同じ。

Zabbixで任意のWebサイトの更新を監視する

  • ZabbixのHTTPエージェントで、Webサイトの更新を監視させる
  • 更新があれば、Slack で通知を投げて貰い、日々のWebサイトの巡回を省力化する

日々のWebサイト巡礼を機械に任せたい

デジタル世界にどっぷり浸かった kWatanabe は、株価情報とか、某ワクチンの予約サイトとか、いくつかのWebサイトの更新を日々確認している。毎日更新されるものなら良いけども、立会外分売の情報のようにそうとは限らないものも多い。

意味の無いルーチンワークはできるだけ機械に任せてしまい、人間様はもっと実になることに時間を使いたい。

Zabbix にWebサイトを監視させる

Zabbix はシステム監視ツールのひとつで、サーバやサービスの負荷状況や障害、その兆候となる事象を監視し、その結果を通知できる。

www.zabbix.com

Zabbix 3.x 以前は、サーバ本体やネットワークなどの機材の監視が主な機能だったが、Zabbix 4.0 以降ではシステムのクラウド化・API化に伴い、監視も通知も Web サービスを意識したものが実装されている。

この中での「HTTPエージェント」と「Slack通知」を使って、手軽にWebサイトの監視システムを構築できる*1

検証

ここでは、最もシンプルに「何でもいいのでWebサイトが更新されていたら Slack へ通知」する場合を考える*2。対象は、現在、日本でホットなWebサイトと思われる自衛隊の某ワクチン接種に関するサイトを監視してみる。

www.mod.go.jp

検証環境

Zabbixのインストール・Slackアカウントの作成は割愛。

Slack にアクセスするための権限を設定

Slack のアカウントから、ディベロッパー用サイトにアクセスする。

api.slack.com

「Create New App」→「From Scratch」を押下し、アプリの名称と連携させるSlack Workspaceを選択する。

アプリが作成されたら、「アプリ名」→「Basic Information」→「Add features and functionality」を押下して、Bots を選択する。

「OAuth & Permissions」→「Scopes」→「Add an Oauth Scope」を押下して、chat: write を追加する。その後、「OAuth Tokens for Your Workspace」の「Bot User OAuth Token」の「Copy」を押下し、テキトーなファイルに貼り付けておく。

Slack に通知用チャンネルを作成

Slack にログインし、Zabbix からの通知を受け取るためのチャンネルを作成する。名前は必ず英数字のみにする。

その後、チャンネルを右クリック「チャンネル詳細を開く」→「インテグレーション」→「App」→「アプリを追加する」を押下して、作成したアプリを登録する。

Slack 通知の設定

Zabbix の画面で「管理」→「メディアタイプ」→「Slack」→一番下の「複製」を押下し、以下を入力する。

  • 名前:Slack以外の任意の名前(ここでは、Slack (Simple)
  • bot_token:Slackからコピーした「Bot User OAuth Token」の値
  • event_opdata:適当な文字列
    • 本来は、取得したデータを挿入するマクロが登録されているが、64KBを超えるとSlackへの投稿に失敗するため、テキトーな値に置き換えておく。

問題なければ「追加」を押下する。

登録されたメディアの右端にある「テスト」リンクを押下し、以下を入力した後に「テスト」ボタンを押下する。

  • channel:追加したSlackチャンネル名
  • event_id:1
  • event_source:1
  • zabbix_url:ZabbixのトップページのURL

ここまでの設定が問題なければ、Slack経由でテスト通知が送られてくるはず。

f:id:kWatanabe:20210810121641p:plain
ZabbixからのSlack通知

f:id:kWatanabe:20210810121752p:plain
ZabbixからのSlack投稿

テストで問題なければ、「管理」→「ユーザ」→利用しているZabbixユーザ→「メディア」で以下を入力し、「追加」を押下する。

  • タイプ:追加したメディア(ここでは、Slack (Simple)
  • 送信先:追加したSlackチャンネル名

Webサイトの監視の設定

Zabbixの画面から、「設定」→「ホスト」→「Zabbix server」→「アイテム」→「アイテムの作成」を選択。

以下のパラメータを入力する。

  • 名前:わかりやすい任意の名前(ここでは、自衛隊ワクチン更新
  • タイプ:HTTPエージェント
  • キー:わかりやすい英数字の名前(ここでは、jgsdf_vaccine
  • URL:監視対象のWebページのアドレス
  • 監視間隔:情報を点検する時間間隔(ここでは、30m)
    • 人力で行ったとしても不自然ではない程度にする。
    • 高頻度での監視は、BANされる可能性もある。また、「前回の監視結果から変化があった」ことを判断基準とするため、短く設定しすぎるとすぐ「解決」状態になってしまい、訳が分からなくなる。
  • データ型:テキスト
  • ヒストリの保存期間:14d
  • 有効:チェックする

f:id:kWatanabe:20210810113525p:plain
アイテムの登録例

設定できたら、「テスト」→「値の取得とテスト」を押下して、「結果」欄にWebサイトのソースが表示されることを確認する。問題なければ「キャンセル」した後、「監視データ取得」と「追加」を押下する。

監視と通知を紐付ける設定

Zabbixの画面から、「設定」→「ホスト」→「Zabbix server」→「トリガー」→「トリガーの作成」を選択。

以下のパラメータを入力し、「追加」を押下する。

  • 名前:わかりやすい任意の名前(ここでは、自衛隊ワクチン更新
  • 深刻度:情報
  • 条件式:「追加」ボタンを押下して、以下を入力
    • アイテム:作成したアイテム(ここでは、Zabbix server: 自衛隊ワクチン更新
    • 関数:change() - 最新値と前回値との差分
    • 結果:<>0

再び、「設定」→「ホスト」→「Zabbix server」→「アイテム」を選択し、作成したアイテムを探し、「トリガー」にトリガー1が表示されており、クリックすると作成したトリガーが表示されることを確認する。

f:id:kWatanabe:20210810124207p:plain
アイテムとトリガーの紐付け

最後に、Zabbixの画面から、「設定」→「アクション」→「トリガーアクション」→「アクションの作成」を押下して、以下を入力し、「追加」を押下する。

  • 「アクション」タブ
    • 名前:わかりやすい任意の名前(ここでは、Report problems to Slack (Simple)
    • 計算のタイプ:Or
    • 実行条件の「追加」を押下して以下を入力
      • タイプ:トリガー
      • オペレータ:等しい
      • トリガー:作成したトリガー(ここでは、Zabbix server: 自衛隊ワクチン更新
    • 有効:チェックする
  • 「実行内容」タブ
    • 実行内容の「追加」を押下して以下を入力
      • ユーザに送信:Slack通知の設定で登録したZabbixユーザ
      • 次のメディアのみ使用:追加したメディア(ここでは、Slack (Simple)

結果

上記の設定が全て完了していれば、Webページの更新されると以下のような通知が Slack にやってくる。

f:id:kWatanabe:20210810131420p:plain
更新通知

これで、人間様はWebサイトに貼りつきになって無駄な時間を浪費することなく、もっと実のあるステキな時間をすごすことができるようになる。

なお、本ブログは全体を通じて無保証です!

*1:本来のZabbixの使い方としては、おおいに間違っている気がしないでもない。

*2:設定を作り込めば、取得した情報を加工して「特定の箇所がこのように変化した場合に通知」なども実現できる。

Majestouch MINILA のキートップを新品に交換する

  • 在宅勤務で持ち帰っていた Majestouch MINILA のキートップを新品にしてみた。
  • 既に8年選手のキーボードが、新品のようによみがえった。

Majestouch MINILA Air専用 Keycap Set 英語配列

今回使ったのは、メーカーが展開している交換用キートップ。カラフルなカスタム向けの商品もありますが、今回購入したのはノーマルの新品キャップ。

商品名には「MINILA Air 専用」とあるが、USB版の「MINILA」にも使える。

f:id:kWatanabe:20210503104257j:plain

www.diatec.co.jp

交換中の様子

f:id:kWatanabe:20210503104736j:plain

キャップを交換した MINILA は、2013年の新発売当時に会社にお願いして買って貰った*1もので、もう8年選手になる。購入後、しばらく別の職場に異動して不在の期間もあったけども、その間もちゃんと保管していてくれた今の職場には感謝している。

f:id:kWatanabe:20210503105049j:plain
交換前

f:id:kWatanabe:20210503105111j:plain
交換後

新品のように綺麗になった。このモデルは、既に絶版なので大切に使っていきたい。

思ったよりよかったので、プライベートで使っている MINILA も交換しようかしら。でも3台あるから、全部交換するとキャップだけで1万円近くになるという・・・。

*1:なお、プライベートでは予約して購入して既に使っていたので、使い勝手の良さはもう分かっており、会社のカネで冒険したわけではない。

LXC非特権コンテナでIntel CPU内蔵GPUを使ってGPGPUする

以前 LXCの非特権コンテナで CUDA する記事を書いた

以前、こんな記事を書いた。

kwatanabe.hatenablog.jp

kWatanabeは、主に waifu2x と ffmpeg を使って動画ファイルを 4K 化するために、LXCゲストでCUDAを動かしている。

github.com

github.com

waifu2xは動画を扱えないので、一度 ffmpegbmp にバラしてから1フレームずつ処理させてるんだけども、そうなるとフレーム同士に依存関係がないので、複数のGPUやCPUを混ぜて並列処理させることができる。

これまで、GeForceとCPUで処理をしていたわけだけど、ふとCPU内蔵GPUも同じように使えるのではと思ったので、試してみた。

検証環境

ホストの設定

GPUの特定と有効化

IntelGPU ドライバは Proxmox VE のカーネルにマージされているので、GeForce のようにドライバを導入する必要はない。

確認する場合は、lspci-v オプションで確認すればよい。まず、PCIのバス番号を調べておいて。

$ lspci | grep VGA
00:02.0 VGA compatible controller: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller (rev 06)

バス番号を指定して詳細を確認する。

$ lspci -v -s 00:02.0
00:02.0 VGA compatible controller: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller (rev 06) (prog-if 00 [VGA controller])
        Subsystem: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller
        Flags: bus master, fast devsel, latency 0, IRQ 44
        Memory at f7400000 (64-bit, non-prefetchable) [size=4M]
        Memory at e8000000 (64-bit, prefetchable) [size=128M]
        I/O ports at f000 [size=64]
        [virtual] Expansion ROM at 000c0000 [disabled] [size=128K]
        Capabilities: <access denied>
        Kernel driver in use: i915
        Kernel modules: i915

見つからないようなら、GeForce のような dGPU のために iGPU が無効化されていないか、BIOS/UEFI などで確認する。

なお、ここで調べたPCIバス番号 ( 上記の場合は 00:02.0 ) は後で必要なのでメモしておく。

GPUをパススルー

GeForce の時と同様に、/dev/dri 配下をゲストに bind マウントする。素のLXC の場合は config ファイル、Proxmox VE の場合は /etc/pve/lxc 配下の設定ファイルに以下を書き加える。

lxc.cgroup.devices.allow: c 226:* rwm
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir

そして、次がハマりポイント

/dev/dri のファイルは root ユーザ、video グループ、render グループが所有者となっているが、LXCの非特権コンテナの中からは nobodynogroup にリマップされるため、このままで権限不足でアクセスできない。

これを解決するためには以下の2通りの方法がある。

  • LXC がゲストに bind するのをフックして所有者を変更する
  • アクセス権を 660 から 666 に変えてしまう

セキュリティ的にはよくないが、後者の方が簡単。メモしておいた PCI バス番号 ( 今回は 00:02.0 ) を頼りにデバイスファイルのアタリをつける。

$ ls -l /dev/dri/by-path
lrwxrwxrwx 1 root root  8  2月  7 01:06 pci-0000:00:02.0-card -> ../card0
lrwxrwxrwx 1 root root 13  2月  7 01:06 pci-0000:00:02.0-render -> ../renderD128
...

アタリをつけた GPUパーミッションを変更する。

$ sudo chmod 666 /dev/dri/card0 /dev/dri/renderD128

ゲストの設定

まずは、デバイスファイルが bind マウントされており、アクセス権があることを確認する。

$ ls -l /dev/dri        
合計 0
drwxr-xr-x 2 root root         80  2月  7 01:06 by-path/
crw-rw-rw- 1 nobody nogroup 226,   0  2月  7 01:06 card0
crw-rw-rw- 1 nobody nogroup 226, 128  2月  7 01:06 renderD128

問題なければ、実機と同じようにセットアップする。Intel CPU 内蔵 GPU 用の OpenCL ドライバは beignetになる。

$ sudo apt install beignet beignet-opencl-icd opencl-headers clinfo

clinfoOpenCLバイスとして認識できていることを確認する。

$ clinfo -l
Platform #0: Intel Gen OCL Driver
 `-- Device #0: Intel(R) HD Graphics Haswell GT2 Desktop

以上で、LXCの非特権ゲストに Intel CPU 内蔵 GPU をパススルーして、OpenCL できるようになる。

おわりに

  • GeForceをパススルーしてCUDAした以前の記事に引き続き、LXCゲストに Intel CPU 内蔵 GPU をパススルーして OpenCL する方法を整理した
  • バイスファイルのアクセス権の設定を忘れると、デバイスファイルは見えているのに、clinfoからはデバイスが見えないといったことになるので注意
  • waifu2xのように工夫次第で複数、かつ、異種のGPUを使い分けられるワークロードであれば、少しの性能の足しにはなるかもしれない

Azure Spot VM の強制削除イベントをハンドルして安全に終了する

  • Azure や AWS には大幅なディスカウントのある Spot VM インスタンスがある
  • Azure の Spot VM は、インフラの負荷が高まった際、事前通知のうえ強制終了される
  • 事前通知されるイベントを検知する仕組みを作り、安全に終了できるようにした

Azure Spot VM

Azure Virtual Machine には、インフラに余力がある場合にのみ利用でき、従量課金制と比べて大幅にディスカウントされる「Azure Spot VM」というサービスがある。

azure.microsoft.com

ディスカウント幅は、インフラ側の都合で動的に決定される。Microsoft は最大で90% ディスカウントすると主張している。 一方、インフラの余力が無くなった場合には、30秒前に事前通知したうえで、強制的にシャットダウンするとしている。

また、ユーザから「ここまでディスカウントしてくれるなら使ってもいい。そうでないなら、使うのを止めさせてもらう。」といった宣言も可能で、その場合、インフラの余力がまだ残っていても、価格がユーザ指定値以上になった際にシャットダウンされるようになる。

Azure Metadata Service の Scheduled Events

Spot VM に限らず Azure Virtual Machine には、保守のための再デプロイ(ライブマイグレーション)や再起動など、データがロストする可能性があるイベントが予定された際に、それを VM の内部から検知できる仕組みがある。

docs.microsoft.com

以下の様に VM の内部からエンドポイントにアクセスすると、予定されているイベントがあれば JSON で取得できる。

$ curl -H "MetaData:true" "http://169.254.169.254/metadata/scheduledevents?api-version=2019-08-01"
{
    "DocumentIncarnation": {IncarnationID},
    "Events": [
        {
            "EventId": ce5f797a-7369-11eb-ba65-a706ed727702,
            "EventType": "Preempt",
            "ResourceType": "VirtualMachine",
            "Resources": ["SpotVM_TEST"],
            "EventStatus": "Scheduled",
            "NotBefore": Mon, 15 Feb, 2021 18:29:47 GMT,
            "Description": "",
            "EventSource" : "Platform",
        }
    ]
}

もし、Spot VM がインフラ側の余力不足などで強制削除される場合、その30秒前に EventTypePreempt なイベントが通知される。また、Preempt 以外にも以下のイベントが発生する可能性がある。

EventType 概要 ロストするデータ
Freeze 一時停止が予定されている。約15分前に発生 特になし
Reboot 再起動が予定されている。約15分前に発生 オンメモリ
Redeploy ライブマイグレーションが予定されている。約10分前に発生 一時ストレージ
Preempt Spot VMの削除が予定されている。約30秒前に発生 すべて
Terminate ユーザによる削除が予定されている。(発生タイミングは設定による) すべて

エンドポイントをポーリングして、もし、これらのイベントを検知すれば、動かしているプログラムを安全に終了させたり、必要なデータを退避するなどの対応が採れる。

検証環境

検証

Azure Spot VM を作る

具体的な作り方は割愛。閾値を現在価格以下にしてしまうと、そもそも起動できないので、1円上で指定してみた。

検知スクリプトを書く

やっつけだけど、こういうのを書いてみた。とりあえずイベントを検知したら、wall で警告を挙げて、プロセスを pkill するだけのシンプルなもの。

MITライセンスで。

github.com

結果

まだ、発動していないので、絶賛放置中。かなり渋い価格にしたので直ぐ発動すると思ったんだけど。それとも何かミスったか。

まとめ

  • Azure Spot VM の強制終了前に、それを検知し後片付けするスクリプトを書いてみた。
  • これで、Spot VM を安全に使えるはず・・・だが、まだ結果は分からない。

nftablesでシンプルなステートフルファイアウォールを作る

iptables から nftables への移行

Debian 11 (bullseye) の Freeze がはじまったので、自宅サーバ移行検証をしていたところ、ちょっとファイアウォールが気になった。

Debian では、10 (buster) で既に nftables がデフォルトになっており、iptables は nftables の互換レイヤーとして機能している。

wiki.debian.org

wiki.debian.org

今のところ、bullseye でも iptables が無くなるという話はないので、もう1回引っ張っても問題は無いのだが、nftables がデフォルトになった次のバージョンで移行というのはタイミングとしても良いので、ここで移行することにした。

検証環境

想定する構成

目指すはシンプルな2ファイアウォール型のDMZを構成すること。抽象化したモデルとしては以下のような形になる。

f:id:kWatanabe:20210213122100p:plain
シンプルな2ファイアウォールDMZ

なお、我が家はいわゆる「逸般の誤家庭」ではないので、Y社 や C社 のエンタープライズ向けルーターなんてものはない*1。 そのため、実態としては以下のような形になる。

f:id:kWatanabe:20210213123055p:plain
本記事の想定環境

この図の「Linux server」が今回のターゲットになる。 簡単のため、以下 DMZ 側の NICeth-dmz、Trust Zone 側の NICeth-trust と記載する。目指すルールは以下。

  • Trust Zone から DMZ への通信は全て許可
  • DMZ から Trust Zone へのセッション確立済みの通信を許可
  • DMZ から Trust Zone への特定のマシンから、特定のマシンの特定のポートへの通信を許可

nftables で作るステートフルファイアウォール

IP forward の有効化

まずは、eth-dmzeth-trust 間のパケット転送を有効にする。ここは nftables に限った話じゃないので詳細は触れない。

$ sudo vim /etc/sysctl.conf
#net.ipv4.ip_forward=1
↓
net.ipv4.ip_forward=1

適用。

$ sudo sysctl -p

nftables の導入

Debian buster および bullseye では、iptables と nftables の両方が提供されているため、明示的に nftables を導入する。

なお、パッケージは nftables のみでよい。iptablesiptables-persistent のように、管理インタフェースと永続化のための仕組みで分かれていたりはしない。

$ sudo apt install nftables

nftables のルールを書く

nftables は、全ての操作が nft コマンドで完結する。

iptables と同様、テーブル、チェイン、ルールの概念がある。ただし、デフォルトのテーブルやチェインは無い。そのため「nftables はなにもしない」状態から設定を作り込んでいく*2

大まかな流れは以下のようになる。 なお、<> はパラメータ、[]は省略可能な引数を指す。

1. 設定済みのルールをクリア
$ sudo nft flush ruleset
2. テーブルを作成
$ sudo nft add table [<アドレスファミリ>] <テーブル名>

アドレスファミリには以下が設定できる。

  • ip : IPv4
  • ip6 : IPv6
  • inet : IPv4IPv6
  • arp : ARP
  • bridge : ネットワークブリッジ
  • netdev : ネットワークインタフェース

IP層でのステートフルファイアウォールでは inet を使えば事足りる。具体例は以下。

$ sudo nft add table inet FIREWALL
3. テーブルにベースチェインを登録
$ sudo nft add chain [アドレスファミリ] <テーブル名> <ベースチェイン名> \
      { type <タイプ> hook <フック> [device <デバイス>] priority <優先度>; [policy <ポリシ> ;] }

ベースチェインは、初めに呼び出されるチェイン。Cでいう main() みたいなもの*3

「タイプ」は、チェインの用途を指定する。

タイプ 用途とアドレスファミリ 利用できるフック
filter 汎用 (全ファミリ) 全て
nat NAT (ip, ip6) prerouting, input, output, postrouting
route ルーティング (ip, ip6) output

「フック」は、チェインの実行タイミングを指定する。

フック タイミング 利用できるアドレスファミリ
ingress バイスがパケットを受信した際 netdev
prerouting パケット受信後、ルーティング判定の前 ip, ip6, inet, bridge
input ルーティング判定後、宛先が自分自身と判断した際 ip, ip6, inet, bridge, arp
forward ルーティング判定後、宛先が他ホストと判断した際 ip, ip6, inet, bridge
output 自分自身が他ホスト宛のパケットを生成した際 ip, ip6, inet, bridge
postrouting 他ホストへパケットを送信する直前 ip, ip6, inet, bridge

「デバイス」は、NIC 名を指定する。

「プライオリティ」は、同じフックが指定されたベースチェインが複数ある場合の優先度を指定する。値が小さいほど、先に実行される。

「ポリシ」は、チェインの中で取り扱いが決まらなかった際の取り扱いを acceptdrop で指定する。未指定だと accept になる。

具体例は以下の通り。

$ sudo nft add chain inet FIREWALL FILTER { type filter hook prerouting priority 0 \; }
  • type filter (汎用) で
  • hook prerouting (ルーティング判定前) に
  • priority 0 (最優先) で処理するベースチェインで、
  • 合致するルールが無い場合は受け入れる

policy を accept もしくは省略した場合は、確実に不要なパケットを落とすルールを入れることを忘れてはいけない。

4. テーブルにレギュラーチェインを登録
$ sudo nft add chain [アドレスファミリ] <テーブル名> <レギュラーチェイン名>

レギュラーチェインは、他のチェインから明示的に呼び出されるチェイン。呼び出し元のチェインの設定を引き継ぐため、チェイン名の登録のみ。具体例は割愛。

5. チェインにルールを登録
$ sudo nft add rule [<アドレスファミリ>] <テーブル名> <チェイン名> <ステートメント>

当該チェインが呼び出された際に、適用するルールを実行順に登録する。

ステートメント」は、具体的な制御内容を記載するが、網羅すると膨大な数になるため参考ページを記載し、ここでは具体例の記述にとどめる。

wiki.nftables.org

「確立済みの通信を許可する」ための設定例は以下になる。

$ sudo nft add rule inet FIREWALL FILTER \
    ct state related,established accept
  • ct (コネクション) の
  • state (状態) が
  • relatedestablished なものを
  • accept (許可) する。

「特定のマシンから、特定のマシンの特定のポートへのアクセスを許可する」ための設定例は以下になる。

$ sudo nft add rule inet FIREWALL FILTER \
    ip saddr 10.0.0.100 ip daddr 172.16.0.100 tcp dport 443 accept
  • ip (IPv4ヘッダ) の
  • saddr (送信元IPアドレス) が
  • 10.0.0.100 であって
  • ip (IPv4ヘッダ) の
  • daddr (送信先IPアドレス) が
  • 172.16.0.100 であって
  • tcp (TCPヘッダ) の
  • dport (接続先ポート番号) が
  • 443 のものを
  • accept (許可) する。

「(先のステートメントから漏れたパケットで、)特定のNICから入力された全てのパケットを破棄する」ための設定例は以下になる。

$ sudo nft add rule inet FIREWALL FILTER \
    meta iifname eth-dmz drop
  • meta (その他のメタデータ) の
  • iifname (入力インターフェース名) が
  • eth-dmz のものを
  • drop (破棄) する

設定済みのルールを確認

$ sudo nft list ruleset

上記のコマンドにて、現在の設定を出力できる。出力結果の具体例は以下になる。

table inet FIREWALL {
        chain FILTER {
                type filter hook prerouting priority 0; policy accept;
                ct state established,related accept
                ip saddr 10.0.0.100 ip daddr 172.16.0.100 tcp dport https accept
                iifname "eth-dmz" drop
        }
}

ルールの永続化

list ruleset の出力は、そのまま nftables の設定ファイルとして用いることができる。

Debian の場合、nftables サービスが起動する際に /etc/nftables.conf があればそれをロードする。そのため、以下のようにすることで設定を永続化できる。

$ sudo nft list ruleset | sudo tee /etc/nftables.conf
$ sudo systemctl enable nftables
$ sudo systemctl restart nftables

なお、手動で設定ファイルをロードしたい場合は以下のようにする。

$ sudo nft -f <ファイル名>

まとめ

最後に、上記のような設定を一発で行うためのスクリプトを作ったので公開しておく。

ライセンスは、MITライセンスで。

github.com

所感

  • nftables で単純なステートフルファイアウォールを作るための最低限の設定を整理した
  • iptables と比べて、設定ファイルや構文も構造化されており、人間様にわかりやすい
  • なにより、ツールが nft 1本に統一されているのがよい

*1:10GbE を敷くのが精々である。

*2:全て許可でも、全て禁止でも、全て転送でも無い。nftables 以外の仕組みが動いていれば、その設定が有効になる。本文に「nftablesは」とあえて書いた理由はここにある。

*3:_start()じゃないのか、とか面倒なコメントは受け付けない。

RHEL互換ディストリの作り方を手探ってみる2(大晦日ハッカソン)

  • 2020年の大晦日に大晦日ハッカソンなるイベントに参加した
  • CentOS のようなRHEL互換ディストリの作り方を1日で出来る範囲で手探ってみた
  • 1日でできる限られた範囲だけど、得られた知見をここで整理する

はじめに

「大晦日ハッカソンとは何ぞや」については、過去記事を参照。

今回は、そのRHEL*1互換ディストリビューションを自製するためには、どうすればいいのかを手探ってみることにしました。

kwatanabe.hatenablog.jp

RHEL互換ディストリビューションとは

過去記事にも書きましたが、RHEL互換ディストリビューションとは、商用ディストリビューションである RHEL から、RedHatの商標や独占的な権限を有するものを削除し、オープンソースライセンスの下に公開されているもののみで再構成したものです。*2

RHEL互換ディストリビューションを作成するための大まかな手順は以下になります。

  1. RHELサブスクリプションを得る
  2. RHEL のリビルド環境を用意する
  3. RHELソースコード (SRPM) を入手
  4. ソースコードの全てから、RedHat に関わる商標やその他権利を有するモノを仕分け・排除
  5. 上記の結果、RHELとの互換性が崩れてしまうものがあれば、それを補填するモノを開発
  6. リビルドする

以下、実際に大晦日ハッカソンで、kWatanabe が取り組んだ内容を整理していきます。

てさぐれ!RHELもの

RHELサブスクリプションを得る

RHELサブスクリプションを得るには以下の方法があります。

  • RedHatから購入する
  • リセラーを介して調達する
  • 評価版を入手する
  • Red Hat Developers に登録して入手する

また、RHELサブスクリプションは、用途や導入するマシンの種別に応じて、多種多様なものがあります。詳細はサブスクリプションガイドに記載があります。

www.redhat.com

Red Hat Enterprise Linux Developer Suite 以外のサブスクリプションは、RedHatもしくはリセラーから調達する必要があります。Red Hat Enterprise Linux Developer Suiteは、開発用途専用で、サポートもありませんが、Red Hat Developers にエントリすることで無償で入手できます。今回はこのサブスクリプションを利用しました*3

なお、Red Hat Developers には以下からエントリできます。

developers.redhat.com

RHEL のリビルド環境を用意する

バージョン齟齬や変な依存関係で悩みたくなかったため、リビルド対象の RHEL 8.3 をそのまま実機にインストールして準備しました。

RedHatカスタマーポータル*4にログインすれば、RHEL のインストールISOを入手できます。9GBを超え、DVD-R DLでも焼けないサイズのため、BalenaEtcher *5 を使って16GBの USB メモリに焼きました。

インストールが終われば、サブスクリプションを認証し、リビルドに必要なパッケージを導入します。この時、CodeReady Linux Builder リポジトリ*6を一緒に有効化しました。

$ sudo subscription-manager register

$ sudo subscription-manager attach

$ sudo subscription-manager repos --enable codeready-builder-for-rhel-8-x86_64-rpms

$ sudo dnf update

$ sudo dnf groupinstall "Development Tools"

$ sudo dnf install rpm-utils rpm-build texinfo

RHELソースコード (SRPM) を入手

ソースコードもまた、RedHatカスタマーポータルから ISO で入手できます。ISO に含まれるものは BaseOSリポジトリとAppStreamリポジトリのみですが、それでも 20GB ほどあります。

これをマシンにコピーし、loop マウントしておきます。

$ sudo mkdir /media/rhel-source

$ sudo vim /etc/fstab
/home/kwatanabe/rhel-8.3-source-dvd.iso /media/rhel-source iso9660 ro,nofail,loop 0 0

$ sudo mount -a

なお、通常は不要ですが、手探るうえで必要になり CentOS のソースも入手しました。 CentOS は ソース ISOを配布していないため、rsyncリポジトリをミラーしました。

$ sudo mkdir -p /media/centos-source/

$ sudo rsync -avzP rsync://vault.centos.org/8.3.2011/BaseOS /media/centos-source/

RedHat に関わる商標やその他権利を有するモノを仕分け・排除

RHELCentOSを比較

環境が整えば、実際に手探っていきます。全パッケージを対象にすると、プロジェクトが立ち上がる位の作業量になってしまうので、BaseOS リポジトリに絞って作業します。

まずは RHEL にあって、CentOS にないものを探しました。もちろん、これはチートです。本来は、削除しないとダメなモノを全部自力で調べなければなりません。

f:id:kWatanabe:20210107120056p:plain
RHELCentOSSRPMを比較

CentOSリポジトリには過去バージョンも含まれていますが、それ以外で RHEL にあって、CentOS にない SRPM は以下の通りでした。

  • libica
  • libzfcphbaapi
  • memstrack
  • openssl-ibmca
  • qclib
  • redhat-indexhtml
  • redhat-logos
  • redhat-release
  • s390utils
諸元から削除された理由を調べる

アタリが付いたら、rpm コマンドを使って中身を調べていきます。まずは、rpm -p -qi を使って諸元を調べました。例えば、libicaだと以下の結果となります。

$ rpm -p -qi libica-3.7.0-2.el8.src.rpm
Name        : libica
Version     : 3.7.0
Release     : 2.el8
Architecture: s390x
Install Date: (not installed)
Group       : System Environment/Libraries
Size        : 552759
License     : CPL
Signature   : RSA/SHA256, 2020年07月21日 17時52分31秒, Key ID 199e2f91fd431d51
Source RPM  : (none)
Build Date  : 2020年07月20日 21時56分29秒
Build Host  : s390-046.build.eng.bos.redhat.com
Relocations : (not relocatable)
Packager    : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>
Vendor      : Red Hat, Inc.
URL         : https://github.com/opencryptoki/
Summary     : Library for accessing ICA hardware crypto on IBM z Systems
Description :
A library of functions and utilities for accessing ICA hardware crypto on
IBM z Systems.

Architectureや、Description の内容から、これは IBM z 向けのパッケージと分かります。CentOSIBM z をサポートしないため、含まれていないようです。同様に、libzfcphbaapi、openssl-ibmca、qclib、s390utilsもアーキテクチャ違いによるものでした。

中身からCentOSで削除された理由を調べる

x86_64向けやアーキテクチャ非依存にもかかわらず CentOS に含まれていないものは、RedHatの商用や権利関係で削除された可能性があります。rpm -ivhSRPMを展開し、rpmbuild -bpでパッチ群をマージして、実物を確認します。

$ rpm -ivh redhat-indexhtml

$ rpmbuild -bp ~/rpmbuild/SPECS/redhat-indexhtml.spec

$ ls ~/rpmbuild/BUILD/redhat-indexhtml

確認の結果、redhat-indexhtml、redhat-logos は RedHat の商標やロゴが含まれており、NGなものでした。redhat-release は、RHELリポジトリ登録のためのパッケージで RHEL互換ディストリビューション では置き換えが必要なものです。

一方、memstrack は問題があるパッケージには見えませんでした。また、大元のソースコードGitHub で公開されています。

github.com

CentOSリポジトリを見直すと、 BaseOS リポジトリから AppStream リポジトリに移動されていました。理由は不明ですが、削除しなければならないパッケージではなさそうです。

これらの結果から、RHEL 8.3 の BaseOS リポジトリでは以下のものを削除し、オリジナルのものに置き換える必要があると分かりました。

パッケージ 概要
redhat-indexhtml RHELのオフラインマニュアル
redhat-logos RHELのロゴ・商標の画像集
redhat-release RHELリポジトリ情報

互換性が崩れてしまうものがあれば補填するモノを開発

抽出したモノのうち、redhat-logosとredhat-releaseは代替となる画像データや、リポジトリを用意しないとディストリビューションとして完結しなくなりそうです。

redhat-indexhtmlは、無くても良い気がしますが、未確認です。

リビルド

だいたいの流れ

リビルド’作業そのものは、通常のRPMのリビルドと大きな差はありません。dnf builddepで依存パッケージを導入して、RPMをビルドしていきます。

しかし、大きな注意点があります。RHELとの互換性を担保するために、RedHatがビルドに使ったライブラリと同じものを使わなければなりません。これはバージョンや適用されているパッチも完全に一致しなければダメです。

REHLのリポジトリに依存パッケージがあればいいですが、無い場合何が使われているのかを調査し、環境を整える必要があります。ここが、RHEL互換ディストリビューション構築で重要かつ困難な所になります。

依存パッケージを調べる

私は以下の流れで作業を進めました。

  1. dnf builddepを試す → 何事も無く完了したら、rpmbuild -baでビルドする
  2. RedHatのマニュアル「RHEL 8 の導入における検討事項」で、RHEL8.0~8.3で統廃合されたパッケージに該当しないか調べる
  3. FedoraやEPELなど、RHELの upstream に含まれていないか調べる
  4. RPM pbone.net などの RPM 検索サイトなどから調べる
  5. Google 検索する

結果としては、大きく以下の4種類(AND/OR含む)となりました。

もしかしたら、RedHat はビルド環境を RHEL ではなく、upstream の Fedora をベースに構築しているのかもしれません。なお、具体的なパッケージ名は当日ツイートした以下の通りです。

なお、調査に使ったリソースは以下からたどれます。

access.redhat.com

getfedora.org

fedoraproject.org

rpm.pbone.net

ここで終了・おわりに

ここまで絞り込めれば、ビルドが通らなかったパッケージで使われているライブラリのバージョンを調べ、ビルドできるように進めていくところですが、ここで2020年が終わりました。

真に難しいところの1歩手前で時間切れとなりましたが、それでも CentOS という先人の成果をフル活用してもかなりパワーを使ったので、世のRHEL互換ディストリビューションの構築がいかに大変かがうかがい知れます。

それでは。

*1:RedHat Enterprise Linux

*2:単なる再構成ではなく、RHEL とソフトウェア的な互換性が担保されている必要があります。RHELエンタープライズディストリビューションとしてデファクトに近い存在であるので、RHEL と同じ機能・品質を持ち、かつ特定の組織にロックインされないディストリビューションを求める人が大勢いるのです。

*3:RHEL互換ディストリビューションの作成が「開発用途」に含まれるかどうかは不明。限りなく黒に近いグレーな気がしますので、このライセンスで用達したソースを元に再構築したディストリビューションの再配布は止めた方がいいとおもいます。

*4:https://access.redhat.com

*5:https://www.balena.io/etcher/

*6:ドライバやパッケージのビルド環境など、開発用途のツールキットを収録した追加リポジトリCentOSでいうところの PowerTools に相当。

RHEL互換ディストリの作り方を手探ってみる1(大晦日ハッカソン)

  • 2020年はSTAY HOMEのため、帰省できないため何かやりたかった
  • ひとりでもくもく会をやってもいいのだが、折角だから大晦日ハッカソンに参加してみる
  • 自己啓発として、RHEL互換(かも知れない)ディストリの作り方を手探ってみることにした

晦日ハッカソン

2012年より、毎年大晦日に開催されているイベントっぽい何かです。

omisoka-hackathon.connpass.com

内容としては、

以上。

2020年は、コロナ渦対策のSTAY HOMEで、旅行も帰省もできないので。ちょっと参加してみようと思ったのです。

何をやるか

とはいいつつも、何かをこれまで作ってきたわけでもなく、何か作りたいものがあるわけでもない。1日で材料から集めてモノを作っても、中途半端になるに決まってる。

そこで、「何かモノを作るのではなく、知見をためよう。知見であれば中間報告であっても、ブログで公開するなり、会社の勉強会で語るなりすれば、それはれっきとした成果物になるだろう。」と言い訳をして、手を動かして何かを手探っていこうと思った次第です。

ネタ探し

1ヶ月前のとある出来事

ところで、1月ほど前にある出来事があり、業界でちょっとした騒動になりました。

blog.centos.org

超意訳すると、RHEL*1という商用Linuxディストリビューションのクローンとして無償公開されていた CentOS が、突如2021年に開発終了するとアナウンスされたものです*2

元々、CentOS はクローン元の RHEL と同様に2028年まで開発されると予想されていたので、これに期待して構築されたシステムは RHEL の購入、 Oracle Linux などの他のクローンへの移行、Debian などの他のディストリへの移行のいずれかを強いられることになりました。

RHELクローン(互換ディストリビューション)とは

そもそも、RHELクローンとは何でしょうか。

RHELは商用のLinuxディストリビューションですが、製品の多くはオープンソースプロジェクトの成果物のうえに成り立っています。そのため、RHEL はそれ自身もオープンソースとして公開されています*3

そして、RHELEULA*4には、以下の記載があります。

1. License Grant. Subject to the following terms, Red Hat, Inc. ("Red
   Hat") grants to you a perpetual, worldwide license to the Programs
   (most of which include multiple software components) pursuant to
   the GNU General Public License v.2. The license agreement for each
   software component is located in the software component's source
   code and permits you to run, copy, modify, and redistribute the
   software component (subject to certain obligations in some cases),
   both in source code and binary code forms, with the exception of
   (a) certain binary only firmware components and (b) the images
   identified in Section 2 below. The license rights for the binary
   only firmware components are located with the components
   themselves. This EULA pertains solely to the Programs and does not
   limit your rights under, or grant you rights that supersede, the
   license terms of any particular component.
 
2. Intellectual Property Rights. The Programs and each of their
   components are owned by Red Hat and other licensors and are
   protected under copyright law and under other laws as
   applicable. Title to the Programs and any component, or to any
   copy, modification, or merged portion shall remain with Red Hat and
   other licensors, subject to the applicable license. The "Red Hat"
   trademark and the "Shadowman" logo are registered trademarks of Red
   Hat in the U.S. and other countries. This EULA does not permit you
   to distribute the Programs or their components using Red Hat's
   trademarks, regardless of whether the copy has been modified. You
   may make a commercial redistribution of the Programs only if (a)
   permitted under a separate written agreement with Red Hat
   authorizing such commercial redistribution, or (b) you remove and
   replace all occurrences of Red Hat trademarks. Modifications to the
   software may corrupt the Programs. You should read the information
   found at http://www.redhat.com/about/corporate/trademark/ before
   distributing a copy of the Programs.

要するに、オープンソースライセンスの下に公開されているソースコードは、RedHatの商標やロゴ、その他独占的な権限を有するものを全て排除した場合に限り、そのライセンスに抵触しない範囲で再利用して良いことになっています。

そこで、RHELソースコードから RedHat が権利を有するものを削除し、再配布されている Linux ディストリビューションがあります。それが、RHEL クローンと呼ばれるものです。

過去には様々な RHEL クローンがありましたが、2020年現在、プロダクトに耐えうるものは Oracle LinuxAsianux です*5Asianux は有償のため、無償のものは Oracle Linux のみです。なお、このアナウンスを受けて、CentOS に代わる新たな RHEL クローンを作ろうというプロジェクトがいくつか立ち上がっています。

rockylinux.org

www.projectlenix.org

RHELクローンの作り方

さて、やっと本題になりました。大晦日ハッカソンでは、この RHELクローンの作り方を手探っていくことにします。

大まかには以下の流れになるはずです。

  1. RHELサブスクリプションを得る
  2. RHELソースコード (SRPM) を入手
  3. ソースコードの全てから、RedHat に関わる商標やその他権利を有するモノを仕分け・排除
  4. 上記の結果、動かなくなってしまうものがあれば、それを補填するモノを開発
  5. リビルド

SRPMを入手してビルドするだけなら、RHEL を開発で使ったことがある人なら難なくできる作業です。しかし、RedHat が権利を持つものを削除したうえで、RedHatの権利を侵害すること無く同等の機能を補填し、RHEL と同じ動きをするものを作るとなると、簡単にはいきません。

RHEL クローンは、莫大な資金と人的リソースを費やして開発されているものなので、大晦日にちょっと手を出したくらいで何か形になるとは思いません。ひとつでも知見を得られれば、それで成功だと思っています*6

おわりに

  • 2020年の大晦日ハッカソンでは、RHEL クローン(RHEL 互換ディストリビューション)の作り方を手探っていく
  • 目標は、何らかの知見を得ることで、RHEL クローンを作ることでは無い(そもそも無理)
  • 得られた知見は、別途整理して、何らかの形でまとめる

*1:Red Hat Enterprise Linux

*2:より正確には、RHEL のクローンとしての CentOS が開発終了。RHEL のアップストリームとしての CentOS Stream は継続される。

*3: RHELサブスクリプションを持っている人には全てのソースコードを詰め込んだ 20GB強もの ISO ファイルがダウンロードできる

*4:End User License Agreement。https://www.redhat.com/ja/about/agreements

*5: ただし、汎用品として。他にも Red Hawk Linux などの特定用途に特化した製品はあります。

*6:そもそも環境の構築方法を調べるだけで1日終わるかもしれないし…

Proxmox VEのLXCでホストにあるISOをマウントする

  • Proxmox VEのLXCゲストは、ホストに保存されているISOファイルをマウントできない
  • メディアからaptやyumをしたい場合など、ニッチだけど地味に困る
  • ホストに保存されているISOの中身を、LXCゲストに見せる方法を整理する

検証環境

  • Proxmox VE 6.3

Proxmox VEでは、LXCゲストでISOをマウントできない

Proxmox VEは、KVMゲストにISOファイルをマウントさせることはできるが、LXCゲストではWebインタフェースに項目が無く、マウントさせることはできない。

LXCでは、ISOからOSをインストールする必要が無いため、割り切った仕様にしているのだとは思うが、DVDメディアやISOファイルをリポジトリとして yum や apt を使いたい場合に困る*1

ダメな例1

Webインタフェースで設定する代わりに、以下のように/etc/pve/lxc/<VMID>.confに設定を書き込んでも、エラーになりゲストが起動しなくなる。

mp0: /path/to/isofile,mp=/path/to/mountpoint,ro=1 ★ダメな例1★

ダメな例2

Porxmox VE のLXCゲストの設定ファイルには、通常のLXCのオプションをそのまま書くこともできるので、以下のようにloopマウントをやりたくなるが、これもうまくいかない。なお、bindマウントはできる。

lxc.mount.entry = /path/to/isofile path/to/mountpoint iso9660 loop,nofail,ro 0 0 ★ダメな例2★

ISOファイルをLXCゲストに送り込んで、ゲストの中からloopマウントする方法も考えられるが、これは Permission Denied になる。回避するためには、apparmorの設定を緩めたうえで、特権コンテナにし、さらにケーパビリティを調整しないといけない(ケーパビリティに関しては過去記事参照)。

kwatanabe.hatenablog.jp

素のLXCならともかく、Proxmox VE のようなプラットフォームでホストの設定を変えると、不具合の原因になりかねないので避けたい。

解決策

「ダメな例2」でしれっと書いたが、bindマウントはできる。そこで、力業だけどもホストでloopマウントして、そのディレクトリをbindマウントして投影すればよい。

なお、loopマウントのマウントポイントをbindマウントのマウント元に指定しないと、ISOファイルの中身は見えない。横着して、複数のマウントポイントを集めた親ディレクトリをbindマウントしてもISOの中は見えない。

最終的にはホストで以下のようにする。

  • ISOファイルをloopマウント
$ sudo vim /etc/fstab
/path/to/isofile /path/to/mountpoint-host iso9660 ro,loop,nofail 0 0
$ sudo vim /etc/pve/lxc/<VMID>.conf
lxc.mount.entry = /path/to/mountpoint-host path/to/mountpoint-guest none ro,bind,nofail 0 0

これで、ISOファイルの中身がゲストでも中身が見えるようになる。なお、Readonlyとはいえど、ホストとゲストの間で穴を開けることになるので、セキュリティ面では大変よろしくない。使い終わったら、さっさと元に戻すのが吉。

おわりに

  • Proxmox VEでは、LXCゲストにISOファイルをマウントする手段を提供していない
  • ゲストの中からマウントするには、セキュリティ設定をかなり調整しないとダメ
  • ホストでloopマウントしてから、bindマウントすれば間接的にISOファイルをマウントできる
  • セキュリティ面では大変宜しくないため、使い終わったら元に戻すこと

*1:CentOS 7のEverything ISOを使う時とか、ISOの中身をHTTPで公開してPXEサーバを作りたい時とか、ニッチだけど地味に困る

aptリポジトリにおける優先度のルールと確認方法

  • apt には、リポジトリやパッケージが競合する場合に、自動選択する仕組みがある
  • これは優先度に基づいて行われるが、デフォルトの優先度は状況によって異なり「リポジトリを登録したのに使われていない」なんてことがまれに生じる
  • apt のリポジトリ優先度の考え方と、設定の確認・変更方法をまとめる

検証環境

apt における優先度付けのルール

apt の優先度は静的な順序付けではない

実は apt リポジトリの優先度は、静的に順序付けされるものではない。ユーザが設定ファイルで指定しない限り、優先度は動的に設定される。また、優先度の絶対値にも意味がある。

apt_preferencesのmanページを見れば、具体的なルールが記載されている。

manpages.debian.org

デフォルトの優先度

特に指定のない場合、apt は以下の優先度を付与する。

優先度 諸元
1 Releaseファイルに NotAutomatic: yes が指定されているが、ButAutomaticUpgrades: yes が指定されていないリポジトリのパッケージ
100 (1) Releaseファイルに NotAutomatic: yesButAutomaticUpgrades: yes が指定されているリポジトリのパッケージ
(2) 既にシステムにインストール済みのパッケージ
500 他の条件に合致していないパッケージ
990 ターゲットリリースとして指定されたリポジトリのパッケージ

なお、ターゲットリリースとは、/etc/apt/apt.confAPT::Default-Releaseに指定しているリリースか、コマンド実行時に -t オプションで指定したリリースを指す。

絶対値に応じた制御

apt は、リポジトリ同士の相対的な比較とは独立して、絶対値による評価も行う。

  • 優先度が負値のリポジトリからは、パッケージをインストールしない
  • 優先度が1000以上のリポジトリは、たとえパッケージをダウングレードすることになってもインストールする
  • 優先度0は未定義動作

ただし、デフォルトの優先度でこれらの値になることはない。ユーザが /etc/apt/preferences で明示的に優先度を設定して、パッケージやリポジトリブラックリスト化や優先付けができるようになっている。

優先度を手動で設定する方法

リポジトリの優先度をaptに決めさせるのでなく、手動で設定したい場合は /etc/apt/preferencesに設定を追記する。

なお、優先度はリポジトリではなくパッケージごとに設定できる。

Package: <パッケージ>
Pin: release a=<リリース名>
Pin-Priority: <優先度の値>

例えば、testing リポジトリexperimental リポジトリと同様に、「明示的にターゲットリリースにしない限り、インストールもアップグレードもされないようにする」には、以下のようにする。

Package: *
Pin: release a=testing
Pin-Priority: 1

また、一時的にターゲットリリースとして設定するには、-t オプションを指定する。

$ sudo apt install -t testing ...

恒久的にターゲットリリースとして設定するには、/etc/apt/apt.confAPT::Default-Releaseを設定する。

APT::Default-Release "testing";

要するに

これらのルールに基づいて整理すると、以下のようになる。

リポジトリの優先度 指定方法 挙動
1000~ /etc/apt/preferences ダウングレードであっても採用する
991~999 /etc/apt/preferences ダウングレードで無い限り、ターゲットリリースにパッケージがあっても、こちらを採用する
990 -tオプション
/etc/apt/apt.conf
ターゲットリリース。明示的に指定した際に採用する
501~989 /etc/apt/preferences ターゲットリリースを指定したものの、目的のパッケージが無い場合に採用する
500 自動 一般的なリポジトリのデフォルト値
101~499 /etc/apt/preferences 他のリポジトリに目的のパッケージが無い場合に採用する
100 自動 インストール済みのパッケージ
1~99 /etc/apt/preferences
1は自動
他のリポジトリに存在せず、また、まだインストールされていない場合(アップグレードでない場合)に限り採用する
0 使用禁止 未定義動作
負値 /etc/apt/preferences いかなる場合でも採用しない

設定の確認方法

特に何も指定しない場合

現状の設定は apt-cache policyコマンドで確認できる。

$ apt policy
Package files:
 100 /var/lib/dpkg/status
     release a=now
   1 http://deb.debian.org/debian experimental/main amd64 Packages
     release o=Debian,a=experimental,n=experimental,l=Debian,c=main,b=amd64
     origin deb.debian.org
 100 http://deb.debian.org/debian buster-backports/main amd64 Packages
     release o=Debian Backports,a=buster-backports,n=buster-backports,l=Debian Backports,c=main,b=amd64
     origin deb.debian.org
 500 http://ftp.debian.org/debian buster/main amd64 Packages
     release v=10.6,o=Debian,a=stable,n=buster,l=Debian,c=main,b=amd64
     origin ftp.debian.org
...(略)...

この環境の例では、以下の設定となっていることが分かる。

  • 不安定なexplerimentalリポジトリは、インストール時に-t experimentalと明示的に指定しない限り、過去に導入済みであったとしても、自動では導入・更新されない。
  • 将来バージョンからのバックポートであるbuster-backportsは、安定版リポジトリと競合するため、インストール時に-t buster-backportsと明示的に指定しないと導入されないが、一度導入した後は自動更新の対象とされる。
  • 標準の安定版リポジトリは、他のリポジトリがターゲットリリースとして指定されない限り、これを使う。

なお、experimentalリポジトリについては以下の過去記事参照。 kwatanabe.hatenablog.jp

ターゲットリリースを明示的に指定した場合

ここで、-t experimentalを付けて、明示的にexperimentalリポジトリをターゲットリリースにしてみる。

$ apt policy -t experimental
Package files:
 100 /var/lib/dpkg/status
     release a=now
 990 http://deb.debian.org/debian experimental/main amd64 Packages
     release o=Debian,a=experimental,n=experimental,l=Debian,c=main,b=amd64
     origin deb.debian.org
 100 http://deb.debian.org/debian buster-backports/main amd64 Packages
     release o=Debian Backports,a=buster-backports,n=buster-backports,l=Debian Backports,c=main,b=amd64
     origin deb.debian.org
 500 http://ftp.debian.org/debian buster/main amd64 Packages
     release v=10.6,o=Debian,a=stable,n=buster,l=Debian,c=main,b=amd64
     origin ftp.debian.org
...(略)...

experimentalリポジトリの優先度が990になっていることが分かる。

まとめ

  • apt リポジトリの優先度は、インストール状況やパッケージの品質に応じて動的に調整されるようになっている
  • 一時的にリポジトリの優先度を高めたい場合は、-tオプションでターゲットリリースとして指定する
  • リポジトリによっては、インストール済みのパッケージのアップグレードでも、都度ターゲットリリースと指定しないと更新対象とならない場合もある
  • 現状の設定状況は apt-cache policy コマンドで確認できる

LXCコンテナの中から、chronydでホストの時刻を補正・配信する

  • LXCの標準設定ではセキュリティ上の理由から chrony が動かせないようになっている
  • コンテナはホストの時刻を使うため、NTPクライアント(chronyc)は必要はないが、NTPサーバ(chronyd)をコンテナで運用したいことはままある*1
  • そこで、セキュリティリスクを承知のうえで、コンテナの中で chronyd を動かして、ホストの時刻を修正したり、時刻を配信したり、できるようにする

検証環境

  • Debian 10.6 (buster) amd64 (ホスト、コンテナ共に)
  • LXC 3.1.0
  • 特権コンテナ

LXCコンテナ内でのchronyの挙動

とりあえず動かして見る

普通にLXCでコンテナを作った場合、たとえそれが特権コンテナでも chronyd は動作しない。

$ sudo apt install chrony
$ sudo systemctl restart chronyd
$ sudo systemctl status chronyd
● chrony.service - chrony, an NTP client/server
   Loaded: loaded (/lib/systemd/system/chrony.service; enabled; vendor preset: enabled)
   Active: inactive (dead)
Condition: start condition failed at Wed 2020-11-18 04:18:48 UTC; 2min 14s ago
           └─ ConditionCapability=CAP_SYS_TIME was not met
     Docs: man:chronyd(8)
           man:chronyc(1)
           man:chrony.conf(5)

Nov 18 04:18:48 tim systemd[1]: Condition check resulted in chrony, an NTP client/server being skipped.

どうして?

キモになるのはここ。

ConditionCapability=CAP_SYS_TIME was not met

LXCの特権コンテナでは、init が root 権限で実行されている。そのため、何も制限をしないとコンテナの root はホストの root と同等の権限になる*2

そこでセキュリティ担保のため、コンテナ内の root からは特にセキュリティリスクの高い権限が剥奪されている。CAP_SYS_TIMEはシステムタイマやタイマデバイスを操作するための権限。他にどのような権限があるかは以下の man ページを参照。

linuxjm.osdn.jp

自分の環境では、全てのコンテナで共通的に読み込まれる config ファイルで以下のようにいくつかの権限が剥奪されていた。

$ cat /usr/share/lxc/config/common.conf
...
# Drop some harmful capabilities
lxc.cap.drop = mac_admin mac_override sys_time sys_module sys_rawio
...

lxc.cap.dropは、ここに定義した権限を剥奪するパラメータ。ここで sys_time が設定されているので、CAP_SYS_TIMEが剥奪されている。これが、本事象の原因となる。

対策

上記のパラメータから sys_time を外してしまいたいけども、それをすると全てのコンテナに権限が付与されてしまう。また、困った事にLXCに権限を与えるパラメータはない*3

仕方がないので、lxc.cap.dropを再定義する。基本的に lxc.cap.drop の設定は重ねがけだが、空欄を指定するとそれまで指定した全ての設定がクリアされるという仕様になっている。

linuxcontainers.org

そこで、共有の config は手を付けずに、chronyd を導入したいコンテナの config の一番末尾で空欄指定で一度パラメータをクリアしてから、sys_time を除いた権限を指定する。

$ sudo vim /var/lib/lxc/【コンテナ名】/config
...
# Drop some harmful capabilities
lxc.cap.drop = 
lxc.cap.drop = mac_admin mac_override sys_module sys_rawio
...

動作確認

権限を付与するためにコンテナを再起動する。

$ sudo lxc-stop -n 【コンテナ名】
$ sudo lxc-start -n 【コンテナ名】

確認してみる。

$ sudo systemctl status chronyd
● chrony.service - chrony, an NTP client/server
   Loaded: loaded (/lib/systemd/system/chrony.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2020-11-18 07:04:41 UTC; 10s ago
     Docs: man:chronyd(8)
           man:chronyc(1)
           man:chrony.conf(5)
  Process: 112 ExecStart=/usr/sbin/chronyd $DAEMON_OPTS (code=exited, status=0/SUCCESS)
  Process: 124 ExecStartPost=/usr/lib/chrony/chrony-helper update-daemon (code=exited, status=0/SUCCESS)
 Main PID: 122 (chronyd)
    Tasks: 2 (limit: 4915)
   Memory: 1.3M
   CGroup: /system.slice/chrony.service
           ├─122 /usr/sbin/chronyd -F -1
           └─123 /usr/sbin/chronyd -F -1

Nov 18 07:04:41 tim systemd[1]: Starting chrony, an NTP client/server...
Nov 18 07:04:41 tim chronyd[122]: chronyd version 3.4 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SCFILTER +SIGND +ASY
NCDNS +SECHASH +IPV6 -DEBUG)
Nov 18 07:04:41 tim chronyd[122]: Initial frequency -10.270 ppm
Nov 18 07:04:41 tim chronyd[122]: Loaded seccomp filter
Nov 18 07:04:41 tim systemd[1]: Started chrony, an NTP client/server.
Nov 18 07:04:47 tim chronyd[122]: Selected source 23.131.160.7

これで、コンテナの中から、ホストのシステムタイマーを制御できるようになる。

余談だが、ホストで既に chronyd や systemd-timesyncd など、タイマを制御するデーモンが動いているなら、止めておかないと大変なことになる。

おわりに

  • LXCでは、セキュリティ上の理由から、標準でタイマを制御する権限が剥奪されている
  • コンテナ内で chronyd を動かすために、特定のコンテナからタイマを制御する権限を復活させるための手順を整理した
  • タイマの制御は、かなりプリミティブな権限なのでくれぐれも自己責任

*1:少なくとも kWatanabe は

*2:root以外のユーザはuser namespaceによりremapされるので、コンテナ内のユーザとホストのユーザは、同じユーザ名でも異なるユーザとして扱われる。

*3:lxc.cap.keep というパラメータがあるが、これは「指定した権限以外を全て剥奪する」効果なので、今回の用途に適さない。

systemdで多段プロキシ・リバースプロキシを作る

  • systemd だけを使って、多段プロキシやリバースプロキシを作る
  • 用途はいろいろあるけど、深く突っ込んじゃイケない

背景

ユースケースはあえて挙げないけど、多段プロキシできるフォワードプロキシとか、一時的なリバースプロキシなど、便利なプロキシサーバが時たま必要になる。

Squidやnginxなどの汎用のものから、Poundやdelegateなどの専用のものまでいろいろな方法があるけども、諸事情*1でこれらが使えない時もある。

そんな時には systemd が使える。systemd は Linux の比較的新しい*2 init システムだが、 init とは思えないほど多機能*3で、実はプロキシサーバが作れてしまう。その手順をまとめる。

検証環境

systemd でプロキシサーバを作る

socket ファイル

まず、リクエストを受け付けるための socket のユニットファイルを用意する。

ListenStreamにリクエストを待ち受けるインタフェースのIPアドレスとポート番号を指定する。

$ sudo vim /etc/systemd/system/proxy.socket
[Unit]
Description=HTTP Proxy Socket

[Socket]
ListenStream=0.0.0.0:80

service ファイル

次に、実際にリクエストを経由する service のユニットファイルを用意する。

ExecStartsystemd-socket-proxydのパスを指定し、第一引数に転送先(多段プロキシなら次のプロキシサーバ、リバースプロキシなら内側のWebサーバ)のIPアドレスとポート番号を指定する。

注意点としては、2つのユニットファイル名はそろえておかないとダメ。(今回は proxy

$ sudo vim /etc/systemd/system/proxy.service
[Unit]
Description=HTTP Proxy Service

[Service]
ExecStart=/usr/lib/systemd/systemd-socket-proxyd 192.168.1.1:5963
PrivateTmp=yes

起動

後はお約束。

systemdをリフレッシュして、

$ sudo systemctl daemon-reload

socketを起動して、

$ sudo systemctl start proxy.socket

うまくいっているようなら、永続化する。

$ sudo systemctl enable proxy.socket

まとめ

  • systemd 単体で多段プロキシもしくはリバースプロキシとして使える、プロキシサーバを構築した

*1:手間をかけたくない場合や、顧客の環境で余計なソフトを入れられない場合、など、いろいろと。

*2:sysvinitやupstartと比べて

*3:プロキシのほかに systemd 単体で Docker みたいなコンテナを作れたりもする。systemd-nspawn とかでググるとよろしい。

ローカルのGitBucketをGitHubに移行する(その3)

  • ローカルサーバのGitBucketで管理していたリポジトリGitHubに移行したい
  • 過去記事では、リポジトリの移行と、Issue と Pull Request のダンプを行った
  • 今回は、GitBacket からダンプした Issue と Pull Request を GitHub に投稿する
  • なお、既に close され branch が消えていた Pull Requset は登録できないので、他のIssueやPull Requestの番号に齟齬が生じないように調整するにとどめる

その1とその2はこちら。

kwatanabe.hatenablog.jp

kwatanabe.hatenablog.jp

背景

過去記事参照。

GitHubAPI

Issue と Pull Request を作って閉じるために必要なAPIは以下の通り。

Issue の投稿

GitHub で Issue を作るときは、 Create an issueAPI をコールする。

developer.github.com

パラメータは以下の通り。残念ながら、MileStone との対応づけはない。

コール先:POST /repos/:owner/:repo/issues

パラメータ 概要 GitBucketからダンプしたモノとの対応
title string Issueのタイトル issue['titile']
body string 本文 issue['body']
milestone integer マイルストン番号 なし
labels array of strings ラベル名 issue['labels'][N]['name']
assignees array of strings アサイン先 issue['assignees'][N]['login']

Issue へのコメントの投稿

作った Issue にコメントする場合は、 Create an issue commentAPI をコールする。

developer.github.com

コール先:POST /repos/:owner/:repo/issues/:issue_number/comments

パラメータは以下の通り。issue_comments は、issuecomment_url に格納されているURLから、更にAPIをコールして取得したもの。その2 では、ダンプした issue の中に comments という key を追加して埋め込んでいる。

パラメータ 概要 GitBucketからダンプしたモノとの対応
body string コメント本文 issue_comments[N]['body']

Issue のクローズ

Issue をクローズする場合は、Update an issueAPI をコールする。

developer.github.com

コール先:PATCH /repos/:owner/:repo/issues/:issue_number

パラメータは以下の通り。クローズしたいだけなら、state だけ送ればいい。

パラメータ 概要 GitBucketからダンプしたモノとの対応
title string Issueのタイトル issue['titile']
body string 本文 issue['body']
milestone integer マイルストン番号 なし
labels array of strings ラベル名 issue['labels'][N]['name']
assignees array of strings アサイン先 issue['assignees'][N]['login']
state string openclosed issue['state']

Pull Request の投稿

Pull Request を作るときは、 Create a pull requestAPI をコールする。

コール先:POST /repos/:owner/:repo/pulls

developer.github.com

パラメータは以下の通り。

パラメータ 概要 GitBucketからダンプしたモノとの対応
title string タイトル pull['title']
body string 本文 pull['body']
head string 改修後のブランチ名 pull['head']['ref']
base string 改修前のブランチ名 pull['base']['ref']
draft boolean Draft Pull Requestか pull['draft']

Pull Request へのコメントの投稿

Pull Request にコメントを投稿するときは、Issue の時と同様に Create an issue commentAPI をコールする。

Pull Request のマージ

Pull Request をマージするときは、 Merge a pull requestAPI をコールする。

コール先:PUT /repos/:owner/:repo/pulls/:pull_number/merge

developer.github.com

パラメータは以下の通り。ただマージしたいだけなら sha だけ合わせておけばいい。

パラメータ 概要 GitBucketからダンプしたモノとの対応
commit_title string コミットメッセージのタイトル -
commit_message string コミットメッセージ -
sha string Pull RequestのheadのSHA pull['head']['sha']
merge_method string マージする方法 -

Pull Request のクローズ

Pull Request をクローズする場合は、Update a pull requestAPI をコールする。

developer.github.com

コール先:PATCH /repos/:owner/:repo/pulls/:pull_number

パラメータは以下の通り。クローズしたいだけなら、state だけ送ればいい。

パラメータ 概要 GitBucketからダンプしたモノとの対応
title string タイトル pull['title']
body string 本文 pull['body']
base string 改修前のブランチ名 pull['base']['ref']
draft boolean Draft Pull Requestか pull['draft']
state string openclosed pull['state']

手順

これらAPIを用いてGitHubに Issue と Pull Request を移行する。

アクセストークンを発行する

その1で移行したリポジトリに、その2でダンプした Issue を投稿する。

GitHubでもGitBucketの時と同じように、アクセストークンを発行する。

  1. GitHubにログインする
  2. アカウントのマイページにアクセスして
  3. Settings を選択
  4. Personal access tokens を選択
  5. Generate new token を選択
  6. Note にテキトーな名前を入れて
  7. repo にチェックをして
  8. Generation Token を選択する

するとアクセストークンが発行されるので、メモしておく。

f:id:kWatanabe:20201011220840p:plain
GitHubのアクセストーク

ダンプした Issue と Pull Request を登録する

注意点は以下の通り。

  • Issue 番号 と Pull Request 番号は同じ採番体系になっており、これらは投稿した順に採番される。そのため、元のIssue番号、Pull Request番号の通りに投稿する。
  • GitBucket で使っていたユーザ名が、GitHub でも使えるとは限らない。Issue の assignees には GitHub に存在するユーザ名に置換した上で投稿する。
  • クローズ済みの Pull Request について、Marge した後、リポジトリから head となる Branch が削除されていたりすると投稿できなくなる。私のリポジトリの場合、クローズ済みの Pull Request にあまり価値はない*1ので、ダミーの Issue を投稿して番号をずらして回避する。

特に工夫点はなく、愚直に API をコールし続ける。その2で作ったコードとマージして完成。

実際のコード

長くなったので、GitHubに公開します。

github.com

結果

  • GitBacket 上の Issue と open な Pull Request は Issue 番号やコメントの内容も含めて移行できた。
  • クローズした Pull Request はダミーIssue として移行した。
  • これで、ローカルのGitBucketのGitHubへの移行は完了となった。

*1:一方で、クローズされていても検討の課程が記録されている Issue は、ある意味リポジトリ本体よりも大切。

Debian10でGeForce RTX 3080を使ってCUDAするための準備

先日、激戦を勝ち抜き GeForce RTX 3080 を買いました。

とはいいつつも、私はPCでゲームを殆どしないので、メインマシンは75W級グラボの最高傑作だと思っている Quadro P2200 をそのまま使います。

では、なぜ GeForce RTX 3080 を調達したかなんですが、自宅のなんちゃって IaaS の GPUコンテナ で CUDA するためです。しかし、GeForce RTX 3080 はリリースされたばかり。debian-backportsでもSidでもドライバが古く、3080 を動かせません。

なので、3080 を動かせるドライバに入れ替えることにしました。

2021/02/03 追記

  • 半年の歳月を経て、buster-backports にも、GeForce RTX 3080 対応のドライバがリリースされました。現在は、以下の記事に頼らなくても、debian-backports リポジトリで対応できます。

GeForce RTX 3080 を動かせるLinuxドライバ

GeForce RTX 3080 を使うためには、455.23 以上でなければなりません。2020/10/15現在、Debian の各リポジトリnvidia-driver のバージョンは以下の通りです。

リポジトリ バージョン
stable (buster) 418.152.00-1
backports (buster-backports) 450.66-1~bpo10+1
testing (bullseye) 450.66-1
unstable (sid) 450.66-1

不安定版のSidを以てしても、まだ足りません。NVIDIAから公式バイナリを入手する手もあるのですが、できれば apt で一元管理したい。そこで experimental リポジトリを使うことにしました。

experimental リポジトリ

要するに、Sid (不安定版)に取り込む前の開発用リポジトリです。ページには「一般ユーザはこのリポジトリのパッケージを使ってくれるな」(意訳)と書いてあります。

また、各種パッケージのページにも以下の警告が記述されています。

  • 警告: このパッケージは experimental ディストリビューションのものです。つまり、おそらく不安定でバグがあり、それどころかデータの損失を起こすかもしれません。使用前には、変更履歴やその他の参照可能なドキュメントを必ず調べてください。

wiki.debian.org

2020/10/15現在、experimental リポジトリnvidia-driver は455.23です。ちょうど GeForce RTX 3000 をサポートし始めたバージョンです。

導入

手順自体は、debian-backports や Sid を使う時と変わりません。/etc/apt/sources.listにexperimentalリポジトリを追加して、apt するだけです。

$ sudo vim /etc/apt/sources.list
deb http://deb.debian.org/debian/ experimental main non-free contrib

$ sudo apt update

$ sudo apt -t experimental install nvidia-driver

運が良ければ、ちゃんと稼働してくれることでしょう。

2020/11/28 追記

  • もし依存パッケージが解決できないようなら testingbuster-backports リポジトリも追加してください。
  • testing を登録した際、システム全体 testing にアップグレードされることを防ぐために /etc/apt/apt.confAPT::Default-Release "stable"; を付けると良いと思います。

2021/01/20 追記

  • ついに、testing に 460 のドライバが降りてきました。
  • なので、危ない橋を渡って experimental リポジトリを使わなくても、testing リポジトリを足すだけで大丈夫になりました。
  • また、今年の夏頃にリリース予定の Debian 11 では、何もしなくても RTX 3080 が使えるようになります。

2021/02/03 追記

  • 半年の歳月を経て、buster-backports にも、460 のドライバが降りてきました。
  • なので、今は Debian10 でも、buster-backports リポジトリを足すだけで、RTX 3080 が使えるようになりました。

結果

まだ、GeForce RTX 3080 は組み込んでいませんが、ドライバだけ入れ替えました。GPUをパススルーしたコンテナからも、ちゃんと新しいドライバが動いています。

$ sudo nvidia-smi
Thu Oct 15 00:43:32 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 455.23.04    Driver Version: 455.23.04    CUDA Version: 11.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GTX 105...  On   | 00000000:0A:00.0 Off |                  N/A |
| 29%   31C    P8    N/A /  75W |      1MiB /  4040MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

あとは、GeForce RTX 3080 を組み込んで CUDA が動けば問題なしですね。

2020/10/16追記

GeForce RTX 3080 組み込み完了しました。 コンテナの中から、ちゃんと GeForce RTX 3080 を認識できています。バッチリですね。

f:id:kWatanabe:20201016214235p:plain
nvidia-smiにてGeForce RTX 3080を認識している様子

参考

大元のGPUコンテナを作った時の記事はこちら。 kwatanabe.hatenablog.jp