関連記事
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のようにパケットを直接見たいときなどに利用できるライブラリに
というのがあります。
今まで使ったこと無かったのですが、使うと面白かったので自分用のメモ代わりにちょこちょこ残しておこうと思います。
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のペイロードを処理するのは、以下のライブラリにお任せ。
$ 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が返ってくるところ無いのかなって探してたら以下のページを発見。
この記事の中で使われている 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が現れていますね。
リポジトリ
上のサンプルなどは、以下のリポジトリでアップしています。ご参考までに。
参考情報
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。