Dockerfile 中で鍵サーバーから公開鍵の取得を試みるとエラーになる

Dockerfile の中で gpg --recv-keys を実行して公開鍵を取得しようとすると、エラーになったりならなかったりということがあったので、試行錯誤しながら対策してみました。

原因はおそらく IPv6 で鍵サーバーに接続しようとしたことで、 IPv6 を無効化することでひとまずエラーはなくなりなりました。

今回採用した IPv6 を無効化する方法は、 dirmngr.conf

disable-ipv6

を書くというものです。 gpg のオプションではなかったので、これにたどり着くまでは少し遠回りでしたね。

追記

エラー メッセージはこうでした。

gpg: keyserver receive failed: Cannot assign requested address

Azure VM で IPv6 に対応するには

この年末年始に Azure VM の構築に挑戦してみたのだが、その際に気づいた Azure VM での IPv6 対応について少し。

  1. Azure VM には直接公開 IPv6 アドレスを付与することができず、Load Balancer (無料) に付与する必要があること。
  2. Load Balancer では IPv6 通信を終端せず、NAT により内部 IPv6 アドレス (とポート番号) に変換されること。
  3. Azure VM で DHCPv6 により内部 IPv6 アドレスの利用ができるようになっている必要があること。

    資料: Azure Load Balancer の IPv6 の概要

    IPv6 ソケット API プログラミングの基礎知識 (シリーズ第 2 回)

    IPv6 ソケット API を使うための構造体を紹介した前回に続きまして、第 2 回はサーバーでは必須となる特定ポートへのバインドの基本的なやり方について書いてみます。

    まず、sockaddr_in6 構造体を用意しなくてはなりませんが、今回はよく利用されるバインドするアドレスを指定しないやり方を扱うことにします。一般的なソケット API プログラミングと同様に <netinet/in.h> ヘッダーをインクルードすると (前回は書きましたっけ? ^^;)、in6addr_any オブジェクトを利用することができます。

    const struct in6_addr in6addr_any;
    

    このオブジェクトは IPv4 での INADDR_ANY に相当するものですが、IPv6 では構造体になので少しばかり勝手が異なります。例としてはこのように使います:

    #include <netinet/in.h>
    #include <arpa/inet.h> /* htons のため */
    
    #define PORT ポート番号
    
    int bind_in6_example1(int socket_fd) {
        struct sockaddr_in6 sa = {0}; /* 全メンバーを 0 に初期化 */
        sa.sin6_family = AF_INET6;
        sa.sin6_port = htohs(PORT);
        sa.sin6_flowinfo = 0; /* 初期化済みなので実際には不要 */
        sa.sin6_addr = in6addr_any;
        sa.sin6_scope_id = 0; /* 初期化済みなので実際には不要 */
        return bind(socket_fd, (const struct sockaddr *) &sa,
                sizeof (struct sockaddr_in6));
    }
    

    in6addr_any はオブジェクトですが、初期化子として利用する場合のために IN6ADDR_ANY_INIT マクロも用意されています。C99 で導入された記法を使うと、例えば次のように書くことができます:

    #include <netinet/in.h>
    #include <arpa/inet.h> /* for htons */
    
    #define PORT ポート番号
    
    static const struct sockaddr_in6 sa_init = {
        .sin6_family = AF_INET6,
        .sin6_flowinfo = 0, /* 暗黙的に初期化されるので実際には不要 */
        .sin6_addr = INADDR6_ANY_INIT,
        .sin6_scope_id = 0, /* 暗黙的に初期化されるので実際には不要 */
    };
    
    int bind_in6_example2(int socket_fd) {
        struct sockaddr_in6 sa = sa_init;
        sa.sin6_port = htohs(PORT);
        return bind(socket_fd, (const struct sockaddr *) &sa,
                sizeof (struct sockaddr_in6));
    }
    

    IPv4 の INADDR_ANY とは違って、初期化と代入で書き方が異なることに注目してくださいね。

    それでは第 2 回はここまでです。例によって次回も期待しないで待っていてください。

    IPv6 ソケット API プログラミングの基礎知識 (シリーズ第 1 回)

    xllmnrd を製作する過程で習得した IPv6 ソケット API に関する知見を、少しずつ書いていこうという不定期連載企画です。反応が薄ければ うやむやにするかもしれませんが…。

    さて記念すべき第 1 回ですが、まずは sockaddr_in6 構造体の紹介で始めたいと思います。IPv4 の sockaddr_in 構造体に対応するものですが、この構造体には以下のメンバーがあります (OS によっては他のメンバーがあることもありますが ここでは触れません):

    sa_family_t sin6_family;
    アドレスファミリーです。IPv6 では AF_INET6 を指定します。
    in_port_t sin6_port;
    ポート番号です。IPv4 と同様にネットワーク バイト オーダーで指定します。
    uint32_t sin6_flowinfo;
    これは IPv6 に特有のメンバーで、トラフィック クラスとフロー情報を指定します。とりあえず 0 としておきましょう。
    struct in6_addr sin6_addr;
    お待ちかね、IPv6 アドレスです。in6_addr 構造体については後述します。
    uint32_t sin6_scope_id;
    これも IPv6 に特有のメンバーで、スコープ ID を指定します。スコープを指定する必要がない場合は 0 としておきます。

    IPv6 アドレスを指定する in6_addr 構造体には以下のメンバーがあります:

    uint8_t s6_addr[16];
    IPv6 アドレスをバイト列で指定します。IPv4 とは違ってバイト列なので、バイト オーダーは意識する必要がありません。

    第 1 回はここまでにしておきます。次回はいつになるか分かりませんが、期待しないで待っていてください。

    xllmnrd の日本語情報 #linux

    現状あまり注目を集めていないが,xllmnrdの日本語情報はこっちに書いていく予定。もっとも日本語で書いても注目されないようならやめるけど。

    ということで,まずこのプログラムの目標を書いておくと,こんなところでしょうかね。

    1. NetBIOSとの決別。
    2. IPv6のLAN内利用促進。

    IPv6アドレスは長くて覚えにくいのに,手軽にホスト名から引くための手段が少ないと言うことで,ちょっとはましにしたいというのいうところですね。

    GNU/Linux用に #LLMNR はじめました

    ということで,オープンソース実装をはじめてみました。すでに誰か作ってるんじゃないかと探してみても見つからなかったのと,マルチキャストUDPの受信が意外に簡単にできてしまったので。

    とりあえずLinuxだとインターフェースのアドレスを引くのにrtnetlinkを使わないといけないみたいなので,そっちも何とかしないといけないが。

    プログラム言語はこの程度でC++にすることもないかと考えて,今回はC99で行ってみるつもり。

    Freecodeにも登録済み。