マストドンと改正プロバイダ責任制限法 鯖管が知っておくべき義務と権利

概要

Twitterイーロン・マスク氏が買収したこと*1により、マストドンをはじめとする分散SNSへアカウントを作る動きが加速*2しています。現在はサーバの処理能力についての話題が多いですが、人が増えればTwitterで起きていたトラブルが分散SNSでも起きるようになると思われます。

そこでこの記事では、分散SNS上でなにかしらの権利侵害が起きた場合に、安心して問題に対処できるようになることを目的として、プロバイダ責任制限法のもとでサーバ管理者の義務と権利、取るべき対応を解説します。

対象の読者

  • 個人でマストドン/Misskeyのサーバを運用しているサーバ管理者

この記事での前提

この記事では読みやすくなるように以下の前提を置いています。

  • 分散SNSマストドン
    • マストドンの用語を使うだけでMisskeyやPleromaでも一緒です。
  • マストドンのサーバは日本国内に設置されている
  • サーバ管理者(以下鯖管)と紛争当事者はすべて日本国内に在住している個人

免責事項

この記事は @ashphy が書いていますが、弁護士資格を持っていません。 また実務経験も持っておりません。 正確な内容を書くようには努めていますが、この記事の内容について何らかの責任を負うものではありません。 紛争の当事者になった場合は弁護士にご相談ください。 記載内容に間違いがあればコメント欄までお願いします。

もしマストドン上で権利侵害が発生したら?

SNS上で発生しうる権利侵害とはなにかというと、名誉毀損であるとか、プライバシーの侵害や、殺害予告、著作物の無断転載などが考えられると思います。ここではマストドン上で事実でない事柄を投稿され名誉毀損が発生したケースを考えてみます。

まずあることないこと書かれた被害者は鯖管に対して投稿の削除を求めるでしょう。 削除すると、あることないこと書いた発信者は削除するなと怒るかもしれません。

板挟みになる鯖管

投稿を削除しないと名誉毀損鯖管が訴えられる可能性がありますし、反対に投稿を削除すると言論の自由を侵害されたとして訴えられる可能性*3*4もあります。2chの管理人だった時代のひろゆきが訴えられまくっていた*5ことを思い出しますね。

このようなリスクを抱えた状態でSNSを運用することは難しいので、間に立つ鯖管が適切な方法で対応すれば損害賠償を免責される法律ができました。それがプロバイダ責任制限法です。

プロバイダ責任制限法とは

プロバイダ責任制限法とは正式名称を「特定電気通信役務提供者の損害賠償責任の制限及び発信者情報の開示に関する法律」と言います。ここでいうプロバイダ(特定電気通信役務提供者)とはインターネットサービスプロバイダ(以下ISP)のことではなく、コンテンツプロバイダ、つまりマストドンサーバのことを指しています。

この法律では鯖管が損害賠償をしなくてもよくなることと、被害者が裁判するときに必要な発信者の情報を開示する方法を定めています。鯖管は法律に則った運用をすることで損害賠償のリスクを無くすことができますが、代わりに利用者からは開示請求を受けて対応することになりますので、プロバイダ責任制限法がどのような法律であるかを把握することは大変重要です。

具体的に鯖管はどのようなことをすればいいのでしょうか? 例を上げてみていきたいと思います。

削除依頼

なにかしらの権利侵害が起きた場合、1番に考えることは削除依頼かと思います。法律では「送信防止措置*6」と呼びます。

裁判外での送信防止措置依頼の流れ

マストドンでは削除のための専用窓口を設けていることは少なく*7、その場合に削除依頼は鯖管に対して直接投げられることになります。マストドンaboutページには管理者のアカウントと、連絡先のメールアドレスも載っていることがある*8のでここに連絡をします。

削除依頼を受けた鯖管はサーバのルールに照らし合わせながら削除するかどうかを決めます。しかし削除してもよいか決めるのが難しい場合が多いと思います。この場合、発信者に「該当の投稿を削除してもよいか?」と意見照会をすることができます。このとき同意があれば削除すればよいですし、7日以内(リベンジポルノの場合は特例で2日以内*9)に返信がない場合は削除しても損害賠償責任が発生しません*10ので安心して削除することができます。

実際のところ、この方法で削除依頼を行っても削除されないこともあります*11名誉毀損などは第三者からは明確に判断することがとても難しいのです。これで削除されない場合は裁判になります。

裁判での削除依頼

裁判をする場合には通常の民事訴訟をすることも可能ですが、時間がかかってしまうので削除依頼をする場合は仮処分の申し立てをすることが多いです。

裁判には訴える相手の住所と氏名が必要*12になります。 削除の仮処分申し立ては鯖管に対して行われますので、被害者は鯖管の住所・氏名を手に入れる必要があります。サイト管理者の住所を調べる方法としてはWHOISがあります。しかしほとんどのマストドンサーバではWHOISプライバシーが使われているためそのままでは住所・氏名がわかりません。そこでドメイン登録者の情報を持っているレジストラに対して問い合わせをすることになるのですが、レジストラマストドンの投稿に関して直接通信に関与しているわけではないため、プロバイダ責任制限法の対象外*13になり開示してくれません。

この場合弁護士会照会*14がよく使われます*15弁護士会照会は弁護士が担当する事件の証拠を集める際に利用できる制度で、弁護士会を通して調査を行うことができます。

弁護士会照会を利用した鯖管の氏名・住所の開示方法

鯖管の住所・氏名がわかったら鯖管に対して裁判を起こすことになります。 流れは以下のようになります。

裁判での送信防止措置仮処分申し立ての流れ

まず被害者は裁判所に対して「削除命令を出して欲しい」と申し立てを行います。通常の裁判だと判決まで1年以上かかる場合もあり、その間削除されないと被害が拡大してしまうため、仮処分の申し立てを行うことがほとんどです。そのあと申し立てを行った被害者と、鯖管の両方から意見を聞く審尋が行われます*16。その後裁判所が被害者の申し立てを認めると、被害者は担保金を供託します。供託がされると裁判所は鯖管に対して削除の仮処分命令を発令します。ほとんどのコンテンツプロバイダは、この仮処分で投稿が削除するようです*17。ここまでに通常1〜2ヶ月かかります。

裁判所が「この投稿は権利を侵害している!」と判断してくれるので、それに従って削除すればよく、安心して削除することができるはずです。

発信者情報開示請求

削除で済めばよいですが、「名誉回復のために謝罪して欲しい」や「慰謝料を請求したい」となった場合は裁判を起こす必要があります。裁判には訴える相手の氏名、住所が必要*18ですが、マストドンユーザーは匿名でアカウントを開設していることが多いので、氏名と住所を知ることは難しいはずです。被害者が鯖管に問い合わせたとしても、鯖管には通信の秘密を守る義務がある*19ため開示することができません。

被害者と通信の秘密との間で板挟みになる鯖管

このままでは裁判を起こすことができないので、プロバイダ責任制限法では発信者情報の開示ができることが定められています。 具体的な手順を見てみましょう。

裁判外での発信者情報開示の流れ

まずは被害者が鯖管に対して発信者情報開示請求を行います。記録が残るように原則として書面で行います*20

まず請求者の本人確認を行います。発信者の個人情報を開示することになるわけですから関係のない第三者に対して開示してはいけません。そのため請求者が被害者本人であるかどうかの確認を行います。ただマストドンの場合、本人確認書類とアカウントの対応が取れるわけではないため、アカウントに登録されたメールアドレスからの請求を受け付けたほうが本人確認しやすいでしょう。

次に発信者情報の保有の有無の確認を行います。請求を受けた時点ではアカウントを削除している可能性もあります。管理画面から情報が残っているか確認しておきましょう。

さらに侵害情報等の確認を行います。請求内容を見ただけだと具体的にどの投稿が問題となったのかわからないことがあります。投稿を特定しておきましょう。

プロバイダ責任制限法第6条第1項では発信者情報の開示に当たっては、発信者に対して「開示請求に応じてもよいか?」と意見を聞くことが規定されています*21。発信者と連絡が取れないとか、そもそも開示請求がデタラメだとかいう場合には意見聴取を行わなくても良いとされています*22。発信者から開示に同意する旨の返答があった場合はここで開示を行います。

開示に同意しない旨の返答があったり、2週間経過しても返答がない場合は、請求資料等から「権利が侵害されたことが明らか」であるかどうかについて検討をします。権利侵害が明らかであるとは、不法行為等の成立を阻却する事由の存在をうかがわせるような事情が存在しないことまでを意味していると解釈されています*23名誉毀損は被害者の社会的評価を低下させる行為のことを言いますが、それが真実で、公共性があり、公益目的であることが認められれば名誉毀損が成立しなくなります*24

もし「権利が侵害されたことが明らか」であると判断できた場合は発信者情報の開示を受けるべき正当な理由があるかどうか判断します。 例えば「削除を要求するため」との理由で請求があった場合、すでに投稿が削除されていれば開示する理由がなくなってしまいます。また開示された情報をネットに晒してやる!といった理由も認められません*25

ここまで判断されれば発信者情報開示することになります。ここで鯖管が開示して良い情報はIPアドレス、メールアドレスとタイムスタンプ(ログイン日時)のみです*26。開示を行った場合は、発信者に対して開示されたことを通知する必要があります*27

ここまでの手順に従えば開示しても鯖管は責任を問われることはありません。逆に言えば「権利が侵害されたことが明らか」の判断を間違え、誤って開示してしまった場合には発信者に対する損害賠償責任を負う可能性がありますし、通信の秘密を犯したことで刑事上の責任も問われる可能性があります。「権利が侵害されたことが明らか」の判断それ自体がかなり難しく、間違えれば損害賠償責任の可能性があるため、実際には 裁判外で任意の開示が行われることはほとんどありません*28

発信者情報不開示決定通知書の標準書式に用意されている「権利が侵害されたことが明らか」と判断できない理由欄

裁判での発信者情報開示請求

鯖缶が発信者情報を開示してくれない場合は裁判を起こす必要があります。 裁判の流れは削除のときと同じです。

鯖缶に対する発信者情報開示請求仮処分の流れ

この開示請求でコンテンツプロバイダは意外と争ってくることがあるようです。発信者はサービスの利用者ですから、自分のところの利用者の権利を守るためだと思われます。コンテンツプロバイダに対する裁判は一般に仮処分ですが、一度開示されてしまうとなかったことにはできないため仮処分でも開示の基準が下がることはありません。そのため仮処分が決定すればほとんどのコンテンツプロバイダはそのまま開示するようです*29

さてマストドンではユーザーの住所氏名を管理しているわけではないため、開示できる情報としてはIPアドレスくらいになります。このままでは住所・氏名を手に入れることができません。そこで開示されるIPアドレスを所有しているISP*30に対して再度発信者情報開示請求を行うことになります。

発信者情報開示請求の裁判の流れ

鯖管に対する開示請求は仮処分で行いましたが、それはISPに保存されているログが3ヶ月ほどで消えてしまう*31ため早期に開示されなければ手遅れになってしまうからです。ISPは発信者の住所・氏名を持っているためここで開示されると身元が判明します。そのためISPに対する開示請求は仮処分で行うことができず、本訴しなければなりません*32。この裁判は時間がかかるためこの間にログが消えてしまわないようにISPに対して発信者情報の消去禁止仮処分を申し立てます。このように最低でも3つの裁判を起こす必要があります。このため開示までは9ヶ月程度かかると言われています*33

実はDMは対象外

実はプロバイダ責任制限法では公開投稿のみが対象で、DMは対象に含まれていません*34。そのためDMを対象とした開示請求には応じてはいけません。 名誉棄損の成立要件である公然性が認められないため慰謝料の請求も難しい*35ようです。

改正プロバイダ責任制限法

このように時間も手間もかかると被害者救済の支障になってしまいます。令和4年10月1日改正プロバイダ責任制限法が施行され、新たな開示手続きが創設されました*36

これまでの手続きは発信者の身元が判明するまでに最低でも3回の裁判を必要としましたが、新しい制度では訴訟ではない非訟手続きによって1度の手続きで開示を行うことができます。非訟手続きは非公開*37で審理も簡略化*38されています。これにより一月半で開示される事例*39も出てきました。

改正プロバイダ責任制限法で創設された発信者情 報開示についての新たな裁判手続

この手続きの特徴は鯖管が被害者に対してIPアドレスを直接公開せずに、ISPに渡してしまうところです。 鯖管アクセスログを確認したら、記録されているIPアドレスからアクセスプロバイダーを特定しそれを被害者に伝えます。 その後、ISPに対する申し立てをした旨の通知を受けたらIPアドレスをアクセスプロバイダーに対して直接通知します。 これにより発信者の身元特定につながるIPアドレスを被害者に公開することなく手続きを行うことができるため、既存の手続きより開示条件が緩和され迅速な手続きが可能となっています*40

この手続きは追加になったため、前に述べた手続きも使うことができます。プロバイダ側が強く争ってくる場合にはかえって手続きが長期化してしまうため、従来の手続きも残しているようです*41。どちらの手続きかによって鯖管が取るべき行動が変わるので注意しましょう。

ログイン時情報の開示

さてこの改正ではログイン時情報の開示ができることが明確化されました*42

実はプロバイダ責任制限法ができたのは2chのような掲示板が全盛期だった2001年で、アカウントを作らずに書き込めるような匿名型掲示板を対象とした法律になっていました。2chは投稿ごとにIPアドレスを保持しており、これを開示することを前提としています。 ただ最近はTwitterのようなアカウントを作成し、ログインして書き込むようなサービス(ログイン型サービス)が主流になってきています。Twitterは投稿ごとにIPアドレスを記録しているわけではなく、ログイン時のIPアドレスを記録しています。法律の条文をそのまま読むと、権利侵害が行われた投稿とは別の通信であるログイン時の情報を開示できるかどうか明確ではなく、判例も分かれている状態*43でした。

この改正ではログイン時のIPアドレスしか記録していない場合、これを開示できることが明確化されました。マストドンやMisskeyはログイン時のIPアドレスしか保持していませんので確実に開示できることになりました。

サービスが保持している情報の確認方法

実際に開示請求を受けた場合にどこを見ればよいか確認しておきましょう。

マストドン

マストドンではモデレーションの画面からログイン時IPアドレスを確認することができます。

マストドンでのログイン時IPアドレス確認画面

マストドンX-Forwarded-For ヘッダを読んでくれるので、前段にリバースプロキシがあっても大丈夫です。 IPアドレスの保持期間はデフォルトで1年間*44です。アカウントが削除されてしまった場合は、IPアドレスの記録も削除されてしまいます。開示請求が届いた時点で記録を別の場所に控えておいたほうがよいでしょう。

Misskey

MisskeyではIPアドレスの記録機能はデフォルトでオフになっています。コントロールパネルのセキュリティの項目から有効にする必要があります。

MisskeyのIPアドレス記録設定画面

設定後はモデレーションの画面からIPを確認することができます。

Misskeyでのログイン時IPアドレス確認画面

Misskeyでは投稿に添付された画像や動画がドライブへ格納されますが、ドライブに格納されたファイルは個別にIPアドレスを確認することができます。

Misskeyのドライブに格納された添付ファイルのIPアドレス確認画面

IPアドレスの保持期限は90日間*45です。アカウントを削除するとIPアドレスは確認できなくなります*46。開示請求を受けた時点で情報を控えておきましょう。

まとめ

それではよき鯖管ライフを

鯖管が知っておくべきガイドライン

読んでおく必要は必ずしもないけどあることを知っておくことが大事。

*1:マスク氏、ツイッターの買収完了 CEOはただちに解雇と - BBCニュース

*2:ツイッター・ユーザーが飛びつくSNS「マストドン」、どんなプラットフォームなのか - BBCニュース

*3:電子掲示板や口コミサイトに違法な書き込みがなされた場合に、管理者はどのように対応すればよいのか? | EC法務ドットコム~弁護士が運営するIT法律サイト~

*4:【ネット上の名誉棄損など|運営者・管理人の責任|損害賠償・削除義務】 | IT,動画等撮影,著作権,肖像権 | 東京・埼玉の理系弁護士

*5:ネット空間の海賊のユートピアをつくったひろゆき氏の厚顔無恥な「才覚」 - 清義明|論座 - 朝日新聞社の言論サイト

*6:特定電気通信役務提供者の損害賠償責任の制限及び発信者情報の開示に関する法律 第三条 第2項の二 当該特定電気通信役務提供者に対し侵害情報の送信を防止する措置(以下この号において「送信防止措置」という。)

*7:Misskey.ioのように専用のサポートページを設けている場合もあります

*8:mstdn.jpの場合は記載がありますが、バージョンによっては記載がされてないサーバーも見かけます。

*9: 私事性的画像記録の提供等による被害の防止に関する法律 第四条 第3項 当該発信者が当該照会を受けた日から二日を経過しても当該発信者から当該私事性的画像侵害情報送信防止措置を講ずることに同意しない旨の申出がなかったとき

*10:特定電気通信役務提供者の損害賠償責任の制限及び発信者情報の開示に関する法律 第3条第2項第2号 当該特定電気通信役務提供者が、当該侵害情報の発信者に対し当該侵害情報等を示して当該送信防止措置を講ずることに同意するかどうかを照会した場合において、当該発信者が当該照会を受けた日から七日を経過しても当該発信者から当該送信防止措置を講ずることに同意しない旨の申出がなかったとき。

*11:削除請求の5つの方法|ベリーベスト法律事務所

*12:民事訴訟規則第二条

*13:独自ドメイン登録者調査の「ANSI Whois」の検索方法や見方 - モノリス法律事務所

*14:弁護士法 第二十三条の二 弁護士は、受任している事件について、所属弁護士会に対し、公務所又は公私の団体に照会して必要な事項の報告を求めることを申し出ることができる。

*15:弁護士を使った風評被害・誹謗中傷対策(総論) - ネット上の誹謗中傷・風評被害対策/削除【IT弁護士 神田知宏】

*16:2_6.債務者審尋 | 裁判所

*17:清水陽平 (2022) サイト別 ネット中傷・炎上対応マニュアル <第4版> p61

*18:民事訴訟規則第二条

*19:電気通信事業法第四条 電気通信事業者の取扱中に係る通信の秘密は、侵してはならない。

*20:プロバイダ責任制限法 発信者情報開示関係ガイドライン 請求手続は、原則として書面によって行う。ただし、一定の場合には、必要に応じて、電子メール、ファックス等による請求が認められる。

*21:とは言っても調べた限りではTwitterは意見照会を行わないようです。法律相談 | Twitter 意見紹介状はどういうふうに来るのか ただISPは意見照会をするようです。法律では「聴かなければならない」となっていますが、発信者情報開示関係ガイドラインでは「プロバイダ等が発信者に対して負う一般的な注意義務を規定しており、同項が発信者情報開示の要件となっているわけではない。」との記載がありどうやらしなくても問題はないらしいです(どういうこと?)

*22:プロバイダ責任制限法 発信者情報開示関係ガイドライン 請求者の主張する事実関係及び証拠資料によっては、情報の流通により権利が侵害されたとは認められないことが明確に判断できる場合にも、発信者に対して意見聴取を行わないでよい。

*23:プロバイダ責任制限法 発信者情報開示関係ガイドライン 6 権利侵害の明白性の判断

*24:真実性の証明による名誉棄損罪の不処罰|横浜の弁護士による無料相談|横浜ロード法律事務所

*25:特定電気通信役務提供者の損害賠償責任の制限及び発信者情報の開示に関する法律 第七条 第五条第一項又は第二項の規定により発信者情報の開示を受けた者は、当該発信者情報をみだりに用いて、不当に当該発信者情報に係る発信者の名誉又は生活の平穏を害する行為をしてはならない。

*26:法律上は電話番号なども開示できますが、マストドンやMisskeyではSMS認証には対応していないので保持していません。 特定電気通信役務提供者の損害賠償責任の制限及び発信者情報の開示に関する法律第四条第一項の発信者情報を定める省令

*27:特定電気通信役務提供者の損害賠償責任の制限及び発信者情報の開示に関する法律 第六条第2項 開示関係役務提供者は、発信者情報開示命令を受けたときは、前項の規定による意見の聴取(当該発信者情報開示命令に係るものに限る。)において前条第一項又は第二項の規定による開示の請求に応じるべきでない旨の意見を述べた当該発信者情報開示命令に係る侵害情報の発信者に対し、遅滞なくその旨を通知しなければならない。ただし、当該発信者に対し通知することが困難であるときは、この限りでない。

*28:プロバイダ責任制限法による発信者情報開示 | 渡辺健寿法律事務所

*29:清水陽平 (2022) サイト別 ネット中傷・炎上対応マニュアル <第4版> p97

*30:ここではわかりやすいようにISPと書きましたが、実際にはISPのアドレスでないことも多く、例えばネットカフェであったり、公衆Wifiだったり、MVNEだったり、VPNだったりするためアクセスプロバイダと記載されることが多いです。

*31:ネット中傷対策実務の基礎(後編)|第二東京弁護士会 ISPのログも3か月程度で消える例が多くあります。

*32:発信者情報開示の仮処分・訴訟|ベリーベスト法律事務所

*33:発信者情報開示請求の流れと手続き | アイシア法律事務所公式ページ

*34:特定電気通信役務提供者の損害賠償責任の制限及び発信者情報の開示に関する法律 第三条 特定電気通信 特定電気通信による情報の流通により他人の権利が侵害されたときは

*35:DMで誹謗中傷された場合、相手の特定や慰謝料請求はできるの? | 弁護士法人PRESIDENT(プレジデント)

*36:プロバイダ責任制限法の一部を改正する法律(概要)

*37:非訟事件手続法 第三十条 非訟事件の手続は、公開しない。ただし、裁判所は、相当と認める者の傍聴を許すことができる。

*38:非訟事件手続法 第五十四条 裁判所は、非訟事件の手続においては、決定で、裁判をする。

*39:イーロン・マスクの影響なし? ツイッター社が「プロバイダ情報」を開示、東京地裁の提供命令に応じる - 弁護士ドットコム

*40:発信者情報開示の在り方に関する研究会 最終とりまとめ - 提供命令と消去禁止命令の発令要件

*41:プロバイダ責任制限法の改正で開示請求がスムーズに!ポイントを解説 | リーガライフラボ

*42:特定電気通信役務提供者の損害賠償責任の制限及び発信者情報の開示に関する法律 第五条

*43:発信者情報開示の在り方に関する研究会 最終とりまとめ - 第2章 発信者情報の開示対象の拡大

*44:Configuring your environment - Mastodon documentation

*45:misskey/CleanProcessorService.ts at develop · misskey-dev/misskey · GitHub

*46:DBを直接のぞけば確認できるかもしれないけど難しそう

Amplifyのローカルモック機能でREST APIはモックできない

タイトルで言いたいことは全部言ってしまったので解説を書きます。

AmplifyでAPIを追加するにはamplify add apiコマンドを使うことでGraphQLかRESTかを選んで追加できます。 ローカル環境でこのAPIをテストするにはamplify mock apiコマンドを使います*1。 しかしこのコマンド、GraphQLしかモックできません

GraphQL APIを追加せずに、REST APIをモックしようとすると以下のようなエラーが出ます。

Failed to start API Mocking. Running cleanup tasks.
TypeError: Cannot read property 'stop' of undefined
    at APITest.stop (/snapshot/repo/build/node_modules/amplify-util-mock/lib/api/api.js:187:33)
    at APITest.start (/snapshot/repo/build/node_modules/amplify-util-mock/lib/api/api.js:150:18)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async start (/snapshot/repo/build/node_modules/amplify-util-mock/lib/api/index.js:18:5)
    at async Object.run (/snapshot/repo/build/node_modules/amplify-util-mock/lib/commands/mock/api.js:21:5)
    at async Object.executeAmplifyCommand (/snapshot/repo/build/node_modules/amplify-util-mock/lib/amplify-plugin-index.js:47:3)
    at async executePluginModuleCommand (/snapshot/repo/build/node_modules/@aws-amplify/cli-internal/lib/execution-manager.js:142:5)
    at async executeCommand (/snapshot/repo/build/node_modules/@aws-amplify/cli-internal/lib/execution-manager.js:40:9)
    at async Object.run (/snapshot/repo/build/node_modules/@aws-amplify/cli-internal/lib/index.js:117:5)

このエラーでは何が起きているのかわかりませんね。 これは作成したモックを、途中で発生したエラーにより片付けようとしたときにさらにエラーが発生してしまい、別のエラーが表示されてしまっているためです。内部的には MockProcessFault: Failed to start API Mocking.. Reason: No AppSync API is added to the project というエラーが出ています。

とりあえず正しいエラーを表示するようなPull Requestを出しておきました。そのうち直ると思います(直れ)。

github.com

ウェブIDフェデレーションするときCognito IDプールではIDプロバイダURLに「/」を入れてしまうとIssuer検証時にURL末尾の「/」がダブってしまい必ず検証に失敗してしまう

TL;DR

  • Cognito IDプールではIDプロバイダURLに「/」を入れてしまうとIssuer検証時にURL末尾の「/」がダブってしまい必ず検証に失敗してしまう。
  • OpenID Connect (OIDC) IDプロバイダを利用して一時認証キーを発行するとき、同一IDプロバイダを使う場合であってもCognito IDプールとSTSを直接使う方法で別々のIDプロバイダを追加しなければならない。

OIDCプロバイダで認証したユーザーに対してIAM一時認証キーを発行する

Auth0などのOpenID Connect ID プロバイダーで認証されたユーザーに対してAWSリソースへのアクセスキーを発行する方法として、Congito IDプールを使う方法と、STS (Security Token Service)を使う方法の2つがあります。

Cognito IDプールを利用する場合でも内部ではSTSを呼んでいるので大まかな処理は変わりませんが、IDトークンの検証にはなぜか違いがあります。

Cognito IDプールの場合

ここでは認証にAuth0を使い、OpenID Connectで連携する例で説明します。 Auth0はIAMのIDプロバイダのページから以下のように登録します。

IAM IDプロバイダの設定例

  • プロバイダのタイプ: OpenID Connnect
  • プロバイダURL: Auth0のApplicationからDomainの設定をコピーします。
  • 対象者: Auth0のClient IDをコピーします。

プロバイダURLの末尾に「/」を入れないようにしてください。

次にCognito IDプールを作成するか、使いたいものを選び、「認証プロバイダー」→「OpenID」から上で設定したIDプロバイダを選んで有効化します。

実際にCognito IDプールから一時認証キーを取得するには ID プール (フェデレーティッドアイデンティティ) の認証フロー に従って取得します。

JavaScriptで書くと以下のようになります。

const client = new CognitoIdentityClient({ region: "us-east-1" });
const getIdCommand = new GetIdCommand({
  IdentityPoolId: "us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  Logins: {
    "AUTH0_DOMAIN.auth0.com": idToken,
  },
});
const cognitoId = await client.send(getIdCommand);
const getCredentialsForIdentityCommand = new GetCredentialsForIdentityCommand(
  {
    IdentityId: cognitoId.IdentityId,
    Logins: {
      "AUTH0_DOMAIN.auth0.com": idToken,
    },
  }
);
const awsCredentials = await client.send(getCredentialsForIdentityCommand);

awsCredentialsAWSにアクセス可能な一時認証キーが入ってきます。 IDプールの認証されたロールに紐づくポリシーで許可されたAWSリソースにアクセスすることができます。

STSから直接一時認証キーを取得する

STSから直接一時認証キーを取得するには AssumeRoleWithWebIdentity を使います。

Cognito IDプールを利用しない場合はAssumeRoleするロールが無いので、ウェブ ID または OpenID Connect フェデレーション用のロールの作成 (コンソール) - AWS Identity and Access Management の通りに作成しておきます。

JavaScriptで書いた場合は以下のようなコードになります。

const client = new STSClient({ region: "us-east-1" });
const command = new AssumeRoleWithWebIdentityCommand({
  RoleArn: "arn:aws:iam::XXXXXXXXXXXX:role/Auth0SampleRole",
  RoleSessionName: "Auth0AssumeRoleSession",
  WebIdentityToken: idToken,
});

IDプロバイダの設定も必要ですが、Cognito IDプールを使うところで設定したはずなので大丈夫なはずです。

さてこれを実行してみましょう。するとうまく...いかずに以下のエラーが発生してしまいます。

{
    "errorType": "InvalidIdentityTokenException",
    "errorMessage": "No OpenIDConnect provider found in your account for https://AUTH0_DOMAIN.auth0.com/",
    "name": "InvalidIdentityTokenException",
    "$fault": "client",
    "$metadata": {
        "httpStatusCode": 400,
        "requestId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        "attempts": 1,
        "totalRetryDelay": 0
    },
    "Error": {
        "Type": "Sender",
        "Code": "InvalidIdentityToken",
        "Message": "No OpenIDConnect provider found in your account for https://AUTH0_DOMAIN.auth0.com/",
        "message": "No OpenIDConnect provider found in your account for https://AUTH0_DOMAIN.auth0.com/"
    },
    ...snip...  
}

https://AUTH0_DOMAIN.auth0.com/」という名前のOpenIDConnect providerが見つからないと言われているようです。IDプロバイダは設定したはずで、Cognito IDプールでは動いていたはずなのでおかしいですね。

答え合わせ

実はこれCognito IDプールとは別のIDプロバイダを追加する必要があります。 以下のようにIDプロバイダを追加してみてください。プロバイダのURLに「/」をつけるのを忘れないでください。

STS向けIAM IDプロバイダの設定

IDプロバイダを変更したのでロールも編集します。「/」がついているところが変わっていますのでよくみて変更してください。うまくいかない場合はロールを再作成した方がよいでしょう。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam:XXXXXXXXXXXX:oidc-provider/dev-AUTH0_DOMAIN.auth0.com/"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "AUTH0_DOMAIN.auth0.com/:aud": "AUTH0_CLIENT_ID"
                }
            }
        }
    ]
}

するとなんと検証に成功します。

逆にCognito IDプールを使う場合、プロバイダURLに「/」を付けてしまった場合はどうなるでしょうか。

{
    "errorType": "NotAuthorizedException",
    "errorMessage": "Token is not from a supported provider of this identity pool.",
    "name": "NotAuthorizedException",
    "$fault": "client",
    "$metadata": {
        "httpStatusCode": 400,
        "requestId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        "attempts": 1,
        "totalRetryDelay": 0
    },
    "__type": "NotAuthorizedException",
    ...snip...
}

"Token is not from a supported provider of this identity pool." はIDプールに紐付いた認証プロバイダ名と、GetIdをしたときのLoginsオプションが違うと言われているようです。

コードを以下のように変更して/を付けて実行してみます。

const getIdCommand = new GetIdCommand({
  IdentityPoolId: "us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  Logins: {
-    "AUTH0_DOMAIN.auth0.com": idToken,
+    "AUTH0_DOMAIN.auth0.com/": idToken,
  },
});

const getCredentialsForIclient. Sendand = new GetCredentialsForIdentityCommand(
  {
    IdentityId: cognitoId.IdentityId,
    Logins: {
-    "AUTH0_DOMAIN.auth0.com": idToken,
+    "AUTH0_DOMAIN.auth0.com/": idToken,
    },
  }
);

すると以下のようなエラーになります。

{
    "errorType": "NotAuthorizedException",
    "errorMessage": "Invalid login token. Issuer doesn't match providerName",
    "name": "NotAuthorizedException",
    "$fault": "client",
    "$metadata": {
        "httpStatusCode": 400,
        "requestId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        "attempts": 1,
        "totalRetryDelay": 0
    },
    "__type": "NotAuthorizedException",
    ...snip...
}

今度は認証プロバイダを見つけられたようですが、/を付けてしまったことによりIDトークン中のIssuerと一致しなくなってしまったようです。 ちなみにAuth0が発行するIDトークンのIssuerは以下のようになっており、「/」まで含まれています。

{
  "iss": "https://AUTH0_DOMAIN.auth0.com/",
}

どうやらCognito IDプールでは https://${IDプロバイダのプロバイダURL}/ とIDトークンのissクレームを比較しているようで、IDプロバイダ名に「/」を付けてしまうと末尾の「/」がダブって検証に失敗してしまうようです。これはかなりバグっぽい挙動なので直してほしいですね。issクレームのURLに/が付かないIDプロバイダの場合はまた挙動が変わるかもしれません。

上記のエラーが出た場合はここまでの情報でデバッグできると思うのでがんばって直してみてください。

Auth0で発行したアクセストークンで直接AWS STSを叩いて一時的な認証情報を取得できない

TL;DR

  • AWS STS (Security Token Service) で AssumeRoleWithWebIdentity するとき、Auth0が発行したアクセストークンに含まれるaudクレームをSTSが正しく解釈することができずにエラーになる
  • 解決法としてはIDトークンを用いるか、Lambda Authorizerを利用する

今回問題となるケース

ここではサンプルとして、ユーザー認証をAuth0で行い、AWSのリソースにアクセスして結果を返すAPIを作成します。

フロントエンドの構築

まずはAuth0のQuickstartsを読んでAuth0のアプリ設定とフロントエンドの構築をします。

auth0.com

このあと構築するAPIをフロントエンドから呼び出す場合は以下のドキュメントを参照してください。

auth0.com

Auth0 APIの設定

Auth0で認証したユーザーに対してAPIを公開したいので、Auth0のダッシュボードからAPIを作成します。

Auth0でのAPI設定

Identifierは好きなものを設定できますが、APIのエンドポイントを指定することが推奨されています。 ここでは https://example.com/ にします。 次のステップで使うので控えておきます。

AWSの設定

IDプロバイダの追加

このあとのステップでAuth0で発行したアクセストークンをSTSに渡すことになるため、AWSにAuth0をIDプロバイダとして使うことを登録します。

docs.aws.amazon.com

IAMのページからIDプロバイダを選び、以下のように登録します。

IAM IDプロバイダの設定

  • プロバイダのタイプ: OpenID Connnect
  • プロバイダURL: Auth0のApplicationからDomainの設定をコピーします。
  • 対象者: Auth0のAPI Audienceをコピーします。
一時認証キーで引き受けるロールを作成する

IDプロバイダの画面から「ロールの割り当て」を選び、新しいロールを作成します。 利用したいAWSリソースに対するポリシーを割り当てておきます。

docs.aws.amazon.com

信頼されたエンティティは下のような感じになります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/YOUR_AUTH0_DOMAIN.auth0.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "YOUR_AUTH0_DOMAIN.auth0.com:aud": "https://example.com/"
                }
            }
        }
    ]
}

API構築

APIはLambdaで構築すれば、ポリシーをアタッチすることでAWSのリソースへアクセスすることができますが、 AWS以外の自分で建てたサーバや、GCPのCloud Functionsで構築するケースの場合はこれができません。

このような場合にAWSリソースへアクセスする方法としてAWS STSにより一時認証キーを発行し、そのキーを用いてアクセスする方法があります。

docs.aws.amazon.com

リクエスト時にアクセストークンをもらって、それをSTSAWSの一時認証キーに引き換え、それを用いてAWSリソースにアクセスするサンプルは以下のようになります。(コードはめんどくさかったのでLambdaで書いています)

const jose = require("jose");
const {
  STSClient,
  AssumeRoleWithWebIdentityCommand,
} = require("@aws-sdk/client-sts");

exports.handler = async (event, context) => {
  const accessToken = event.headers.authorization.split(" ")[1];
  console.log("[Access Token] ", accessToken);

  const JWKS = jose.createRemoteJWKSet(
    new URL("https://YOUR_AUTH0_DOMAIN.auth0.com/.well-known/jwks.json")
  );

  // アクセストークンの検証
  const { payload, protectedHeader } = await jose.jwtVerify(accessToken, JWKS, {
    issuer: "https://YOUR_AUTH0_DOMAIN.auth0.com/",
    audience: "https://example.com/",
  });
  console.log("[Protected Header] ", protectedHeader);
  console.log("[Payload] ", payload);

  // STSで一時認証キーを取得する
  const client = new STSClient({ region: "us-east-1" });
  const command = new AssumeRoleWithWebIdentityCommand({
    RoleArn: "arn:aws:iam::XXXXXXXXXXXX:role/Auth0SampleRole",
    RoleSessionName: "Auth0AssumeRoleSession",
    WebIdentityToken: accessToken,
  });

  try {
    const awsCredentials = await client.send(command);
    console.log("[STS Credentials] ", awsCredentials);

    // DO Something with AWS Resource
  } catch (error) {
    // error handling.
    console.log("[STS Error] ", error);
  }

  return {};
};

エラーが発生する

このAPIを実行すると以下のような InvalidIdentityTokenException が発生します。

InvalidIdentityTokenException: Incorrect token audience
{
  '$fault': 'client',
  '$metadata': {
    httpStatusCode: 400,
    requestId: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
  Error: {
    Type: 'Sender',
    Code: 'InvalidIdentityToken',
    Message: 'Incorrect token audience',
    message: 'Incorrect token audience'
  },
  RequestId: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
  xmlns: 'https://sts.amazonaws.com/doc/2011-06-15/'
}

Incorrect token audience とメッセージが出ており、audienceが違うと言われています。

なぜこのエラーが発生するのか?

Auth0で発行したアクセストークンを JSON Web Tokens - jwt.io で見てみると、audクレームが以下のようになっています。

"aud": [
  "https://example.com/",
  "https://YOUR_AUTH0_DOMAIN.auth0.com/userinfo"
],

audクレームはアクセストークンが誰に対して発行されたものであるかが記されています。 STSでAssumeRoleWithWebIdentityをするとき、STSはIAM IDプロバイダの対象者に登録した値と、アクセストークンの中のaudトークンを比較して値が一致するかどうかを検証しています。

通常 Incorrect token audience のメッセージが出た場合は2つの値が一致していることを確認します。 しかし今回のケースではaudトークンの中にIDプロバイダーの対象者に設定した値が含まれているため、トークンが検証できるはずですがなぜか失敗してしまいます。

実はSTSはaudトークンが配列で指定されている場合には必ず検証に失敗します

audトークンはRFC 7519で文字列か、文字列の配列が指定できることになっています*1が、STSでは文字列のものしか受け付けてくれません。 試しにIDトークンを投げつけてみると検証に成功することがわかると思います。

AWSサポートに問い合わせたところ、STSではaudクレームが配列のものを受け付けられない仕様とのことでした。 この動作はCognito IDプールでも同様です。

回避策

今回紹介したケースは、あまり遭遇しないかも知れないですが、回避策も紹介しておきます。 基本的にはIDトークンが利用できるような構成を取るのが良いようです。

IDトークンを用いる

フロントエンドと同じドメインAPIが公開される場合はIDトークンを利用するのが良いでしょう。 Auth0のIDトークンはaudクレームがClient IDだけになるのでSTSでの検証をパスできます。

Lambda Authorizerを使う

アクセストークンの検証をLambda Authorizerを使って自分でやれば回避できます。

Secure AWS API Gateway Endpoints Using Custom Authorizers

マイナンバーカード作成時に氏名の常用漢字「樹」が代替文字になる問題

重い腰を上げマイナンバーカードを作成しようと申請したところ、気になることが発生しました。

f:id:ashphy22:20220311174836p:plain

代替文字とはなんでしょうか。

署名用電子証明書は、コンピュータで使用されるものであるため、氏名、住所は、文字化けせずにコンピュータで表示されるものとする必要があります。このため署名用電子証明書で利用可能な文字は、JIS(日本工業規格)の漢字の規格の内、日常的に使用頻度の高い常用漢字人名用漢字を含むJIS第一水準、第二水準(JISX0208)と補助漢字(JISX0212)の範囲としています。 住民基本台帳の氏名または住所が電子証明書に記載するコード取扱範囲外の場合は、コンピュータでの表示に類似の字形を指定し、代替文字として登録します。(公的個人認証サービスポータルサイトより)

平たく言えば、「パソコンで普通に打てない名前は置き換えるよ」という意味です。 名字が旧字体で変換しても出てこない人も多いでしょう。そういう人対策ですね。

これを踏まえてよく見てみましょう。

ん? なにか違いある? 正解を見てみましょう。

f:id:ashphy22:20220311234646p:plain:w800

左の樹は「士(し)」で、右の樹は「土(つち)」になっています。サイゼリヤの間違い探し並みの難易度ですね。 常用漢字では士(し)になっていますし、パソコンで打って変換に出てくるのも士(し)の方です。ただ歴史的に見ればどちらの文字もかなり流通しており漢和辞典により、どちらが採用されているかは分かれていた*1ようです。

なにが問題か?

さて問題はこの「樹」が使われているのは名字ではなく、名前であるというところです。というのも名前は戸籍法により「常用平易な文字を用いなければならない*2」と定められていて、士の方の樹しか使えない*3*4ことになっています。

つまり「常用平易な文字でPCでも普通に変換できる漢字」しか名前として受理されないはずなのに「あなたの名前の漢字はPCで使えないので別によく似た字に置き換えます」と言われています。おかしいですね。

ここで住民票の記載がおかしい可能性が浮上します。実は以前引っ越ししたときに間違われたことがあります。

f:id:ashphy22:20220312002026p:plain
墨田区役所の住民票訂正

ここでは土(つち)の方の樹で誤って住民票に記載されていたものを、常用漢字の士(し)へ修正がされました。住民票を扱うシステムは当然電子化されていますから、自治体のシステムでは2種類の樹を扱うことができるようです。

実は住民票を扱うシステムは自治体が個別に管理しており、文字セットも自治体任せです。紙の住民票に書かれた文字を職員が読んでシステムにない字だと判断されれば外字をどんどん作っていったようです。なのでどんな文字があるのかはの仕様は公開されていません。しかし住民基本台帳ネットワークシステムを作るときに問題になります。オンラインで全国の自治体が情報をやり取りするためのシステムですから文字コードが揃っていなければなりません。そこで作られたのが住民基本台帳ネットワーク統一文字です。実はこれも詳しい仕様は公開されていないのですが、文字情報基盤システム*5で検索することができます。ここを見ると樹が2つあることが確認できます。

区役所に確認してもらう

さっそく区役所に確認してもらうことにしました。すると意外な答えが

「足立区では樹は1種類しか扱えないので間違っていることはありません」

墨田区では2種類の樹が使えるのに、足立区では樹は1種類しかないのです。なんて無駄な

さらにマイナンバー問い合わせ窓口と戸籍住民課住民記録係をはしごし詳細に調べてもらったところ驚きの事実が。

解答編 住基ネットとの文字マッピングが変

区役所担当者「区のシステム上では間違いなく士(し)ですが、住基ネット上では土(つち)の方になってますね。」

f:id:ashphy22:20220312005505p:plain

区のシステムと住基ネットは別システムなので通信時に文字コードの変換が行われていますが、このとき樹(足立区)を樹(住基ネット)に変換するときに土(つち)の方にマッピングしているようです。なんで?担当者も理由は不明とおっしゃっていましたが、この変換ルールを定めるのは足立区側な気がします。。。詳しい人教えてください。足立区が間違えたんじゃねーの

実務上はどれくらい字形が違うと別の字としてみなすかというのはかなり難しい問題で、戸籍と住民票の間ですら見解が統一されていません。実際、土(つち)の樹は戸籍には存在しないのです。なので文字の対応を考える担当者によって差が生まれてしまいます。

これによりまともにパソコンでは打てない土(つち)の樹で住基ネットに登録された名前は電子証明書を作成する際に平易な文字に置き換えられることになりました。

ちなみにマイナンバーカードの券面は自治体が印刷するので、住民票と同じ文字になりますとのことでした。ここは住基ネット使ってないんですね。

わけわからない変換を経て最終的にはもとに戻って通常のマイナンバーカードが発行されるわけですが、なんだか腑に落ちない結論になってしまいました。検索すると同じ事象にあたっている人が複数見受けられました。足立区で名前に樹が入っている人はもれなく全員プレゼントでこの通知が来ています。気持ち悪いのでどうにかなりませんかね。

まとめ

*1:漢字文化資料館 Q0228 「樹」の真ん中の一番上にある部分は、「土」ですか、それとも「士」ですか?https://kanjibunka.com/kanji-faq/jitai/q0228/

*2:戸籍法 第五十条 https://elaws.e-gov.go.jp/document?lawid=322AC0000000224

*3:戸籍法施行規則 第六十条 https://elaws.e-gov.go.jp/document?lawid=322AC0000000224

*4:平成22年内閣告示第2号 常用漢字表 https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/

*5:https://moji.or.jp/mojikibansearch/basic

画面が縦横切り替わるDMM版ウマ娘をOBSで配信する方法

ウマ娘1周年おめでとうございます。

ガチャ無料で配信する人も増えていますが、ウマ娘は育成とライブで画面が縦横切り替わるため、 配信画面をどのようにすればよいか悩んでいる人もいるかと思います。

f:id:ashphy22:20220305160346p:plain

ウマ娘は縦画面に固定することもできるのでこれで配信してもよいのですが、せっかくのライブは大きく映したいはずです。 そこでウマ娘用のOBS設定をご紹介します。主に凝った配信画面を使用しているVTuberさん向けの設定です。

ウマ娘用のOBS設定方法

結論から言ってしまうと縦画面と横画面でそれぞれシーンを用意すればOKです。

まずは縦画面を設定する

シーン「ウマ娘ー縦画面」を新規作成し、通常通り配信画面を作成します。

f:id:ashphy22:20220305164911p:plain:w600

ウィンドウキャプチャは新規作成し、ウマ娘のウィンドウを選択しておきます。 ウマ娘は「キャプチャ方法」で「Windows10(1903以降)」を選択しないと正しくキャプチャできません。 

横画面を設定する

横画面用のシーンを作成します。

シーン「ウマ娘ー横画面」を新規作成します。次にウィンドウキャプチャを設定しますが、「新規作成」ではなく「既存を追加」から縦画面シーンで作成したウィンドウキャプチャを選択します。

f:id:ashphy22:20220305163705p:plain:w400

ウマ娘のライブシアターでライブを横画面で再生しながら、OBS上で画面の位置とサイズを調整します。

f:id:ashphy22:20220305164414p:plain

あとはシーンを切り替えるだけ

ライブが終わってゲームが縦画面になったら、OBSのシーンから「ウマ娘ー縦画面」シーンをクリックしてシーンを切り替えて見ましょう。 これで縦画面と横画面でそれぞれちょうどいい場所に表示されるはずです。お好みでシーントランジションも設定しておくと良いでしょう。

シーンの切り替えは手動になりますが、全レースのライブを見るわけではないと思うのでそこまで手間にはならないかと思います。

Dockerはネットワークを31個しか作れない

作れないと書いたけど実は作れる。どういうことか見てみよう。

まずはデフォルトのネットワークに何があるのかを見てみる。

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
d2ab6f31d291        bridge              bridge              local
74a5d829719c        host                host                local
f58f8b205d14        none                null                local

bridge, host, null の3つ存在している。

じゃあネットワークをいっぱい作ってみよう。

$ bash -ec 'for i in {1..100} ; do echo ${i}; docker network create bridge${i} ; done'
1
593eec165f64fa41c45f1588d629a456ab702e44b50444c0f7fd30e871bf0de2
2
8e1fe158f726c84e1838e1b616f7a8a61534a047d77534e94e4262264e25d256
3
f56360c828311c69a75726684f310b3a6cdba72f925bba16e87881b049c1e471
4
c0884ad0b5dc1981ee850b28ba22ef2708962245bc9e909213f139a9d80b37a9
5
26405e901d9dde116499b77ff563090cfd22916cafacc2543925b39744c69271
6
9911f60841b8af356f5d087a699001e349b098e18afa65d0c1ec6c62d6ee2452
7
61bfbe1fa7295e661123ebca37ceacc57399090888e9fefe27181167330af9a8
8
76d45aa9dc3e38af60b0b58e922c43df3c78a382afbe8bfa70e85d03b158ea5d
9
8675f8dc856d6d97fd8e8986df12dfe3555cc6c68cec5dba94b51b5642f95dd1
10
14d4411c28185d09ea90720f564fbe243792c3b2dc76e39245b0ab211be2ed05
11
420fb50af6890c820e4568328a09392ad606fe3c0c94d2856ff3945ee11f68af
12
3838220f3cecac3cfb5e53351021fa8a3c10645ae17f7c3a157d230594db538d
13
735921a582e179132631743354638c7103659c5b2f269b76cf573e37cc13a0b0
14
a049c5ad758bd0af9e29390060c5f927a3c51e204ba8d4cc2a2609b7a0cc115a
15
eb87107535976ee356bc5e1ae1b6de7e7316da7712901f7a3affd79f37ca9c20
16
ebdb696f7427070a537d7187a230166f5f1a64dc546c4db68c22a744cb95d551
17
6af9a3dd72c5a63d07a213444edc02a4a069b181782bbc776a63e28616e35fa9
18
035dbb7b09a8d1654c42fbad74ee2677768ccd3683141d0f1c97c40f922b5e71
19
48e7d32d9d29b2d5d9477d5f0319c6895ddd8465dae7596b5bd9bef2b3e7156d
20
cb5ea88da4ee9a96296fe583adc3d270be91a7c7af8f13d75bac1d34ec27f68d
21
74a254cef5e2b4554c70341f27340f1fe75d170ff4b7a8e5903a8d6c63c9685e
22
aca6864cfbeccb5c35278a1b335514548c2f5a1fa4a40e340588b463c9e73038
23
386a2908546c44565bbbb61579e88a1f2b80dcc0f1a69e7b3d864bec7414d590
24
e8f6e69834bda931e0872c85402eebc98566ae0a50015021a861eec04d3687c5
25
0e0e0ae97ea1dc960383f36df590627df60ca5bb5310045f0099a26a35a8a433
26
60524f4f7430134d7c62b896a5864c3b5e42e4767ad99061f0124c6631a25248
27
4dff28f173bde5a053584f7bf3780872037b028546cd50363dc4ab333da6b3c3
28
06c3e76d652813b17cfc9cd9afeebdd048f6b8a6f61ab5dd590e44b955f4d980
29
85ea0ddc2ea2f321c6b084a6b76c62e90d1ed25abb7c6a6e9bafe8f1e11b16b8
30
Error response from daemon: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

30個目を作ろうとしてエラーになる。

実は作られたネットワークに割り当てられたサブネットを見てみると以下のようになっている。

$ docker network inspect $(docker network ls -q) | grep -E "Subnet" | sort -V
                    "Subnet": "172.17.0.0/16",
                    "Subnet": "172.18.0.0/16",
                    "Subnet": "172.19.0.0/16",
                    "Subnet": "172.20.0.0/16",
                    "Subnet": "172.21.0.0/16",
                    "Subnet": "172.22.0.0/16",
                    "Subnet": "172.23.0.0/16",
                    "Subnet": "172.24.0.0/16",
                    "Subnet": "172.25.0.0/16",
                    "Subnet": "172.26.0.0/16",
                    "Subnet": "172.27.0.0/16",
                    "Subnet": "172.28.0.0/16",
                    "Subnet": "172.29.0.0/16",
                    "Subnet": "172.30.0.0/16",
                    "Subnet": "172.31.0.0/16",
                    "Subnet": "192.168.0.0/20",
                    "Subnet": "192.168.16.0/20",
                    "Subnet": "192.168.32.0/20",
                    "Subnet": "192.168.48.0/20",
                    "Subnet": "192.168.80.0/20",
                    "Subnet": "192.168.96.0/20",
                    "Subnet": "192.168.112.0/20",
                    "Subnet": "192.168.128.0/20",
                    "Subnet": "192.168.144.0/20",
                    "Subnet": "192.168.160.0/20",
                    "Subnet": "192.168.176.0/20",
                    "Subnet": "192.168.192.0/20",
                    "Subnet": "192.168.208.0/20",
                    "Subnet": "192.168.224.0/20",
                    "Subnet": "192.168.240.0/20",

なんだか大きいネットワークが割り当てられている。 デフォルトではどこから払い出されるのかは書かれていないが、以下のチケットに記載されている通り

Document the default address pool for local networks · Issue #8663 · docker/docker.github.io · GitHub

Type Default Size Default Pool
global /24 10.0.0.0/8
local /16 172.17.0.0/12
local* /20 192.168.0.0/16

になる。172.17.0.0/12 とかぱっと計算できないが、172.16.0.0172.31.255.255までになる。 つまりlocalで払い出されるネットワークをすべて使い切ってしまったことになる。

通常は使ってないネットワークを整理すればいいはずだが、本当に31本以上のネットワークを使いたい場合は--subnetオプションを使って細かく切ることができる。

$ docker network create --driver=bridge --subnet=192.168.0.0/24 br0

またデフォルトのプールと切り出すブロックサイズをdaemon.jsonで変更することができる

{
    "default-address-pools": [
        { "base": "172.17.0.0/12", "size": 24 },
        { "base": "192.168.0.0/16", "size": 24 }
    ]
}