いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-324 (go-packetメモ-15)(DNSSECの情報を表示)

関連記事

Goメモ-306 (go-packetメモ-01)(ネットワークインターフェースを表示) - いろいろ備忘録日記

Goメモ-307 (go-packetメモ-02)(流れるパケットをキャプチャする)(OpenLive) - いろいろ備忘録日記

Goメモ-308 (go-packetメモ-03)(pcapファイルを読み込み)(OpenOffline) - いろいろ備忘録日記

Goメモ-309 (go-packetメモ-04)(BPFフィルタを設定)(SetBPFFilter) - いろいろ備忘録日記

Goメモ-310 (go-packetメモ-05)(*pcap.Packetの中身を表示) - いろいろ備忘録日記

Goメモ-311 (go-packetメモ-06)(*layers.Ethernetの情報を表示) - いろいろ備忘録日記

Goメモ-312 (go-packetメモ-07)(*layers.ARPの情報を表示) - いろいろ備忘録日記

Goメモ-313 (go-packetメモ-08)(*layers.ICMPv4の情報を表示) - いろいろ備忘録日記

Goメモ-314 (go-packetメモ-09)(*layers.IPv4の情報を表示) - いろいろ備忘録日記

Goメモ-315 (go-packetメモ-10)(*layers.TCPの情報を表示) - いろいろ備忘録日記

Goメモ-316 (go-packetメモ-11)(*layers.UDPの情報を表示) - いろいろ備忘録日記

Goメモ-317 (go-packetメモ-12)(アプリケーションレイヤーの情報を表示) - いろいろ備忘録日記

Goメモ-321 (go-packetメモ-13)(HTTPの情報を表示) - いろいろ備忘録日記

Goメモ-323 (go-packetメモ-14)(DNSの情報を表示) - いろいろ備忘録日記

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。忘れないうちにメモメモ。。。

GoでWireSharkやtcpdumpのようにパケットを直接見たいときなどに利用できるライブラリに

github.com

pkg.go.dev

というのがあります。

今まで使ったこと無かったのですが、使うと面白かったので自分用のメモ代わりにちょこちょこ残しておこうと思います。

Linux (Ubuntu) 上で遊んでいますので、Windowsの場合はWinPcap (WireSharkをインストールするときについでにインストールできたはず)が必要になると思います。

インストール

libpcap が必要ですので、以下でインストールします。

$ sudo apt install libpcap-dev

あと、tcpdumpが入っていない場合は以下もついでに入れておきます。(これはオプショナルです)

$ sudo apt install tcpdump

あと、dnsのリクエストを投げるためにdigコマンドを使うので以下も入れておきます。

$ sudo apt install dnsutils

試してみる

使い方に関しては、上に挙げている go-packet の godoc の方に詳しく書かれています。

今回は、DNSSECの中身を見てみます。

アプリケーションレイヤーを取得して、ペイロードが取れれば後はそれをDNSSECだと解釈してやれば良いはずです。

DNSSECのペイロードを処理するのは、以下のライブラリにお任せ。

pkg.go.dev

$ go get github.com/miekg/dns@latest

main.go

// Package main is the example of DNSSEC using go-packet.
package main

import (
    "fmt"
    "log"
    "os"
    "os/signal"
    "time"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
    "github.com/miekg/dns"
)

var (
    appLog = log.New(os.Stderr, "", 0)
)

func main() {
    if err := run(); err != nil {
        panic(err)
    }
}

func run() error {
    const (
        device      = "eth0"
        filter      = "udp and port 53"
        snapshotLen = int32(1600)
        promiscuous = false
        timeout     = 1 * time.Second
    )

    defer func() { appLog.Println("DONE") }()

    // --------------------------------------
    // Open capture handle
    // --------------------------------------
    var (
        handle *pcap.Handle
        err    error
    )

    handle, err = pcap.OpenLive(device, snapshotLen, promiscuous, timeout)
    if err != nil {
        return fmt.Errorf("error open handle: %w", err)
    }
    defer handle.Close()

    // --------------------------------------
    // Apply capture filter (optional)
    // --------------------------------------
    if filter != "" {
        err = handle.SetBPFFilter(filter)
        if err != nil {
            return fmt.Errorf("error apply filter: %w", err)
        }
    }

    // --------------------------------------
    // Set signal handler
    // --------------------------------------
    var (
        doneCh = make(chan struct{})
        sigCh  = make(chan os.Signal, 1)
    )

    signal.Notify(sigCh, os.Interrupt)

    // --------------------------------------
    // Make packet source and display.
    // --------------------------------------
    var (
        dataSource   gopacket.PacketDataSource = handle
        decoder      gopacket.Decoder          = handle.LinkType()
        packetSource *gopacket.PacketSource    = gopacket.NewPacketSource(dataSource, decoder)
        packetCh     <-chan gopacket.Packet    = packetSource.Packets()
    )
    appLog.Println("START")

LOOP:
    for {
        select {
        case <-sigCh:
            close(doneCh)
            break LOOP
        case p, ok := <-packetCh:
            if !ok {
                break LOOP
            }

            if err = see(p); err != nil {
                return err
            }
        }
    }

    return nil
}

func see(p gopacket.Packet) error {
    //
    // レイヤーを確認
    //
    udpLayer := p.Layer(layers.LayerTypeUDP)
    if udpLayer == nil {
        return nil
    }

    //
    // ペイロードを取得
    //
    var (
        udp     = udpLayer.(*layers.UDP)
        payload = udp.LayerPayload()
    )

    appLog.Printf("[Src Port       ] %v", udp.SrcPort)
    appLog.Printf("[Dst Port       ] %v", udp.DstPort)
    appLog.Printf("[Length         ] %v", udp.Length)
    appLog.Printf("[Checksum       ] %v", udp.Checksum)

    if len(payload) == 0 {
        return nil
    }

    defer func() { appLog.Println("------------------------------------") }()

    //
    // DNSペイロードとして解釈する
    //
    var (
        msg = new(dns.Msg)
        err error
    )

    err = msg.Unpack(payload)
    if err != nil {
        return err
    }

    var (
        hasQuestion = len(msg.Question) > 0
        hasAnswer   = len(msg.Answer) > 0
    )

    // DNS Question
    if hasQuestion {
        appLog.Println("[DNS Questions]")
        for _, q := range msg.Question {
            appLog.Printf("\t%v\n", q.String())
        }
    }

    // DNS Answer
    if hasAnswer {
        appLog.Println("[DNS Answers]")
        for _, a := range msg.Answer {

            // 単純なDNS要求の場合は *dns.A のみとなるが
            // DNSSECの場合は, *dns.DNSKEY または *dns.RRSIG もあり得る
            switch t := a.(type) {
            case *dns.A:
                appLog.Printf("\t[A     ] %v\n", t.String())
            case *dns.DNSKEY:
                appLog.Printf("\t[DNSKEY] %v\n", t.String())
            case *dns.RRSIG:
                appLog.Printf("\t[RRSIG ] %v\n", t.String())
            }
        }
    }

    return nil
}

Taskfile.yml

タスク定義は以下のような感じ。

  applayer-dnssec:
    desc: See DNSSEC info
    dir: cmd/applayer/dnssec
    cmds:
      - go build -o dnssec main.go
      - sudo ./dnssec &
      - cmd: sleep 1
        silent: true
        # https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/security_guide/sec-security_guide-using-dig-with-dnssec
      - dig +noall +dnssec iij.ad.jp
      - cmd: sleep 3
        silent: true
        # https://eng-blog.iij.ad.jp/archives/7689
      - dig +noall iij.ad.jp dnskey
      - cmd: sleep 3
        silent: true
      - sudo pkill dnssec

以下、Gitpod上で実行してみた結果です。

DNSSECに対応してて、RRSIGが返ってくるところ無いのかなって探してたら以下のページを発見。

eng-blog.iij.ad.jp

この記事の中で使われている iij.ad.jp ドメインさんを、そのまま使わせてもらってます m( )m

gitpod /workspace/go-gopacket-example (main) $ task applayer-dnssec
task: [applayer-dnssec] go build -o dnssec main.go
task: [applayer-dnssec] sudo ./dnssec &
START
task: [applayer-dnssec] dig +noall +dnssec iij.ad.jp
[Src Port       ] 51846
[Dst Port       ] 53(domain)
[Length         ] 58
[Checksum       ] 4431
[DNS Questions]
        ;iij.ad.jp.     IN       A
------------------------------------
[Src Port       ] 53(domain)
[Dst Port       ] 51846
[Length         ] 231
[Checksum       ] 21922
[DNS Questions]
        ;iij.ad.jp.     IN       A
[DNS Answers]
        [A     ] iij.ad.jp.     300     IN      A       202.232.2.191
        [RRSIG ] iij.ad.jp.     300     IN      RRSIG   A 8 3 300 20230705151005 20230605151005 13308 iij.ad.jp. YwFvGRQq3lIcOhmEX2cGSQ4j/fE6M1CwtG4hFWjKaz0q2vzEjfEMrXJnWquRH+CrgvErimIPptXmHoTHa2azamDd2zUTLZrXdafXjp6PZ5EVxs0iCJq3WpG1Y/cZX7P49oYY4WkQvGXDd3OAhtPZuVTALgHDdSI8PE9pVHhEGDw=
------------------------------------
task: [applayer-dnssec] dig +noall iij.ad.jp dnskey
[Src Port       ] 53908
[Dst Port       ] 53(domain)
[Length         ] 58
[Checksum       ] 4431
[DNS Questions]
        ;iij.ad.jp.     IN       DNSKEY
------------------------------------
[Src Port       ] 53(domain)
[Dst Port       ] 53908
[Length         ] 618
[Checksum       ] 53795
[DNS Questions]
        ;iij.ad.jp.     IN       DNSKEY
[DNS Answers]
        [DNSKEY] iij.ad.jp.     2763    IN      DNSKEY  256 3 8 AwEAAcMokStGwwAuCMQfU0wpPEeDUscXalO36kNPm/ZbUHC3aZC48cCOIva1qc1JpvgqMeiuoMufWudZ9rQVE9IubfQhz0cG0PAHkOvSufpYJ/Dg4gys2JQVZtdUWuXpPX2DflElV3iXS0SoEE0bEVpzFb/mlJ7YkhUfnj7kq1KIr4mv
        [DNSKEY] iij.ad.jp.     2763    IN      DNSKEY  256 3 8 AwEAAc+XQtBUShkl6DXaYIRr+2q0EC34XeLQNY5+UnxPavo6MLd0v7LTpd6UpP02TH7nPZYrjGKUlNovMV0EQ46GEzEZexj8aQeqUD1P/0a/gQBknU3K8MKEn3Tuytqcx2nEtT7RBnfxnVgmzJqStM5cfY/LqXC/8EU7dKcRjRD14tUF
        [DNSKEY] iij.ad.jp.     2763    IN      DNSKEY  257 3 8 AwEAAd5lYXd3r4sru3TmsRNnQn7vG3R6HbGx1LSXOktO1GBbbTpUh0s5lI6dBqbaL+NiaQ9nvI9r9InOXOIxW6UvU2Mvx0N0KRkeZvk4e4xmZx2IWxA7Nx+lQJyEjmGRdfNHgjAww99fycolKvm1fTunWwKtoqR6KsiiFDQW8x1yYWJJhqGV0G2PTyQBUBLfyEaG15+a9jGAC907GOs5W3zHGKU0xbzaq5BoddvHoNoUqKDnbCBG8qWunm/tXxSSelrlWLA5nDB19NQrxuGzCIpw44WrqWANTFGmPQ61e+qr6RfBOGHgUFPsiYOi87vu/lKy2zZYB/W32A4P2Sp3e8mzwfk=
------------------------------------
task: [applayer-dnssec] sudo pkill dnssec

ちゃんとAとRRSIGとDNSKEYが現れていますね。

リポジトリ

上のサンプルなどは、以下のリポジトリでアップしています。ご参考までに。

github.com

参考情報

Goのおすすめ書籍

Go言語による並行処理

Go言語による並行処理

Amazon


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。