ハトネコエ Web がくしゅうちょう

プログラミングやサーバー・Web制作、チームマネジメントなど得た技術のまとめ

ufw のログの読み方(SPT, DPT など)

1. ufw とは

ufwUbuntu Firewall の略……ではなく
Uncomplicated Firewall (複雑じゃないファイアウォール)の略です。

「昔は Ubuntu Firewall の略だったんじゃないの?」と邪推したんですが、
2008年1月24日時点の Wiki
『Name: ufw (Uncomplicated Firewall)』と記述してあるので、開発当初からその略みたいです。

iptables の操作が複雑なので、それを簡単に操作するためのラッパーです。

2. ログの場所

/var/log/syslog/var/log/ufw.log の両方に吐き出されます。
syslog は他のシステムログと混じるので、 ufw.log の方を見るのがわかりやすいと思います。

3. ログの読み方

読み方は、ここのサイトがわかりやすかったです。ありがとうございます。

Feb 10 06:30:51 the-host-name kernel: [716156.938348] [UFW BLOCK] 
IN=eth0 OUT= MAC=11:01:01:01:01:01:22:02:02:02:02:02:08:00 
SRC=189.23.45.67 DST=123.45.67.89 
LEN=40 TOS=0x00 PREC=0x00 TTL=244 ID=17582 PROTO=TCP 
SPT=40596 DPT=8088 WINDOW=1024 RES=0x00 SYN URGP=0 

例えばこんなログが出ていました。(読みやすさのため改行を入れてます)

IPのインターフェイス(Internet Protocol Interface)について記載している
RFC791 3.3 Interfaces の項で、
各々が何を指すのかほぼ確認できます。

順番に書いていきます。

3-1. Feb 10 06:30:51

日時ですね。これはそのまんまです。

3-2. the-host-name

そのマシンのホスト名です。 echo $(hostname) で出てくる値と同じだと思います。

3-3. kernel: [716156.938348]

brackets(角カッコ)の中身は、OSが起動してからの秒数を指しています。

716156 / 60 / 60 / 24 = 8.3 程度なので、およそ起動してから8日が経過していることがわかります。

3-4. [UFW BLOCK]

ufw が以下の通信をブロックしたことを示します。

3-5. IN=eth0 OUT=

IN が incoming interface , OUT が outgoing interface を指すようです。
……らしいんですが、ネットワークのことをわかっていなくて、それが具体的になにかわかっていないです。
こちらが参考になるかもしれません: IP マルチキャストのトラブルシューティング - Cisco

この場合は、OUT側が空白なので、 incomig (相手から自分宛てへ)の通信であったことがわかります。

3-6. MAC=11:01:01:01:01:01:22:02:02:02:02:02:08:00

これは先ほどの参考ページで本当に簡潔にまとめてくれているのですが、
先頭の 6 octet (11:01:01:01:01:01)が宛先のMACアドレス
その後の 6 octet (22:02:02:02:02:02)が送信元のMACアドレス

最後の 2 octet がイーサネットタイプを指すそう。(参考: RFC 1340 ETHERNET NUMBERS OF INTEREST
0800 が DOD IP (DoD参照モデルとしてのIP)として参考先には記載がありますね。

3-7. SRC=189.23.45.67 DST=123.45.67.89

SRC が source 、送信元のIPアドレス を、
DST が destination送信先IPアドレス を指します。

3-8. LEN=40

IPパケットの長さです。
IPヘッダとIPペイロード(データ)の合計値です。

IPヘッダについてはこの記事が詳しいです: TCP/IP - IPとは

3-9. TOS=0x00

Type of Service です。
0x00 の場合は CS0 らしいです。

Howto:Calculate Values for Type of Service (ToS) from DiffServ or DSCP Values - innovaphone-wiki

これは DSCP 値としては 000 000 にあたり、
処理の優先レベルがもっとも低く、破棄レベルも低く処理されることを示します。

参考: QoS - DSCP(Differentiated Services Code Point)とは

3-10. PREC=0x00

IP Precedence の値 を指すそう。(「Precedence」は「優先度」を指す英単語らしい…)

0〜7 の 8 段階あり、この例では 0x00 なので、やっぱり優先度は低いです。

参考: QoS - IP Precedence(IPプレシデンス)とは

3-11. TTL=244

おなじみ Time to live です。パケットが破棄されるまでの時間(秒数)を指します。
知ってる風に書いたけど、「パケットが破棄される」という概念を実はよくわかっていない……。

3-12. ID=17582

IPヘッダにおける識別番号(Identifier)です。
ちなみに16bitなので、IDの最大値は 65535 のはず。

3-13. PROTO=TCP

これはそのままプロトコルですね。
TCPプロトコルの通信であることを示しています。

3-14. SPT=40596 DPT=8088

SPT は Source Port、つまり送信元のポート番号を示し、
DPT は Destination Port送信先のポート番号を示します。

つまり相手側は、8088番ポートにアクセスしようとして、 ufw にブロックされたと言えます。

ログを見てみると、やはり 22番ポートへのアクセスが一番多く、
次に 8080番ポートへのアクセス、そして 8088番ポートや 3389番ポートへのアクセスが見られる印象でした。

3-15. WINDOW=1024

TCPヘッダにおけるウィンドウサイズを指します。

ウィンドウ制御についてはこちらを参考に: TCP/UDP - TCPウインドウ制御 - ネットワークエンジニアを目指して

3-16. RES=0x00

RES はTCPヘッダにある予約領域です。
将来的な拡張のために 6 bit 用意されていますが、今のところ使われていないので
常に 0 が入るようです。

3-17. SYN URGP=0

SYN はコントロールフラグにて「SYN(コネクションの確立を要求)」のフラグがONであることを示しています。

URGP は Urgent Pointer の略で、コントロールフラグで「URG(Urgent=緊急)」のフラグがONである場合には、
緊急データの位置を指す役目を持ちますが、そうでなければ常に 0 です。

参考: TCP/IP - TCPとは - TCPヘッダ

4. まとめ

詳細に書きましたが、ファイアウォールの動作ログとしては

の 4 つがわかっていれば、困ることはなさそうですね。

すごく久しぶりに IP と TCP のことを調べて、
「マスタリングTCP/IPのことほとんど覚えていないな……」と反省しました・・・。

マスタリングTCP/IP 入門編 第5版

マスタリングTCP/IP 入門編 第5版

IPヘッダやTCPヘッダに何があるかくらいは、頭に入れておくと楽そうです。

gRPC x Go x nginx での失敗談

この前こんな gRPC の入門記事を書いたわけですよ。

nekonenene.hatenablog.com

だからもう実際の運用も恐くないだろうな〜って思っていたんですけど、
実際にサーバーに置いて動かそうとしたらいろいろ失敗があったので書いておきます。

動かそうとしたコードはこんな感じなので、時間ある方は読んでもらえば。

失敗1: nginx と gRPC サーバー間は平文通信で大丈夫だった

接続としては

クライアント→(443番)→ nginx がプロキシ役 →(localhost:50123)→ Docker(50123:50051) → Go による gRPC サーバー

となっていて、とにかく上手く行かなかったので、
「もしやローカル通信部分も SSL/TLS 化しないと gRPC の場合は行けないのでは?」と、
自己署名証明書作ったり、無理やりサーバー証明を Docker にreadonlyでマウントしたり、けっこう迷走しました。

が、結果としては TLSで問題なかった
次に書くところに気付かなかったのが一番の問題でした。

失敗2: grpc_pass を用いる

いつも通り proxy_pass を用いて、

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name grpc-test.hatone.net;
# (中略)
    location / {
        proxy_pass http://localhost:50123;
    }
}

と nginx の設定を書いていたんですが、こうじゃないんですね!

www.nginx.com

NGINX 1.13.10 で gRPC サポートをしたよ〜!
という、上の記事に書かれていました。
(※ nginx -v で、お使いの nginx のバージョンが v1.13.10 以上かご確認ください)

proxy_pass ではなく grpc_pass 、これが答えだった・・・!

よって、nginx の設定はこうなります。

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name grpc-test.hatone.net;
# (中略)
    location / {
        grpc_pass grpc://localhost:50123;
    }
}

grpc:// 部分はオプションらしいので書かなくて大丈夫らしいんですが、なんとなく不安なので。
ちなみに localhost 部分で SSL/TLS 通信をおこなう場合は grpcs:// とするようです。(参考: IBM Blockchain Platform にアプリケーションをデプロイする

失敗3: grpcurl の使い方に失敗していた

動作確認には前回も紹介した grpcurl を使っていました。

正解から書くと、以下のようなリクエストで行けるんですが、

grpcurl -d '{"name": "Nanako", "age": 35}' grpc-test.hatone.net:443 hello.Greeter/SayHello

ここに行き着くまでかなりの失敗をしていて、
サーバー側の実装が失敗してるのかと勘違いして時間をけっこう無駄にしました。

例えば package name を helloworld から hello に変えたことを忘れて helloworld.Greeter/SayHello でリクエストしてたり、
:443 を付けずにリクエストして『missing port in address』のエラーを見たり。

grpcurl の README でも :443 を付けているんですけど、
「README ではポート番号を付けてなかったはず……」と間違った記憶をなぜか信じていました(笑)

エラーもちゃんと理解していなく、「ポート未指定の場合のデフォルトのポート番号がなにかあって、
それがファイアウォールで通信拒否されてる?」と間違った仮説を持っていました。

ファイアウォールのログを確認した結果、無く、
調べた末、ふつうに golang 側のエラーだと知りました・・・。
https://github.com/golang/go/blob/go1.11.5/src/net/ipsock.go#L157

失敗4: サーバー側の実装に失敗していた期間があった

サーバー側の実装をいろいろいじっているうちに、
ふつうにミスって動いていない状態になっていたことがありました。

他の人のコードを見てたら

lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))

となってるのあって、「あっ、localhost って付けても動くんだ」と思ったので付けたら
実際は正常動作していなかったという。

lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))

でないとダメでした。

「いろいろ変えたけど、改めてローカルで動くんだっけ……」と試して問題に気付けました。

失敗5: クライアントの実装も難しかった

grpcurl がどうしてもわからなくて、ちゃんとクライアントを実装しようとやってみたんですけど
けっこう難しかったです。

公式の実装例だとクライアント側も証明書を必要としていて、
「うーん、それは難しい……」と。

今回は実運用はまだですので、サーバー側の証明書の正当性を検証するステップはスキップするという方向の実装にしました。

InsecureSkipVerify: true がそれですね。

実運用のときは証明書の正当性の検証も入れたほうがいいとは思いますが、
Let's Encrypt で定期的に更新されるタイプの証明書の検証って運用負荷低く実現できるのか気になります。

まあ、TLS について理解していない部分が多いので、まずはそこの勉強から頑張らねばですね!

→証明書の正当性の検証について書きました

nekonenene.hatenablog.com

おわりに

上で書いた、nginx の gRPC サポート発表の記事が 2018/03/17 だったり、
gRPC が実運用として活発に使われるようになったのはここ1年だと思うのですが、
そのわりには情報が多く、けっこう助かっています。

でも上のように何度もつまずいて大変でした! 丸2日かかってしまいました。連休でよかった。
Golang 自体にまだ全然慣れていないので、gRPC 特有の型なのか Go 付属ライブラリの型なのかわからなくて、コード読むのに戸惑ったり…(笑)

次は、このサーバーに Android アプリからリクエストするような実装を作れればと思います。

→できました!!

http2 設定で cert renew に失敗

sudo cert renew --dry-run

に失敗。

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/xx.yy.com.conf
-------------------------------------------------------------------------------
Cert not due for renewal, but simulating renewal for dry run
Plugins selected: Authenticator nginx, Installer nginx
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for xx.yy.com
Waiting for verification...
Cleaning up challenges

Attempting to renew cert (xx.yy.com) from /etc/letsencrypt/renewal/xx.yy.com.conf produced an unexpected error: 
Failed authorization procedure. xx.yy.com (http-01): 
urn:ietf:params:acme:error:connection :: 
The server could not connect to the client to verify the domain :: 
Fetching http://xx.yy.com/.well-known/acme-challenge/2vTuPTMugzS_MiYSwr_zycQahYwUOjIb3JngrZ2D46I: 
Error getting validation data. Skipping.

今まで何度見たことか……。

nginx のアクセスログにアクセス履歴が残ってなかったので、
「ファイルの作成に失敗……? 権限周り……?」と疑ったのですが、そうではなく、
結論としては、gRPC用に 80 番ポートにも http2 設定を付けていたこと。

つまり

server {
    listen 80 http2;
    listen [::]:80 http2;
    server_name xx.yy.com;

となっているのを

server {
    listen 80;
    listen [::]:80;
    server_name xx.yy.com;

に戻してあげたら上手くいきました。

謎なのが、この xx.yy.com の認証だけでなくて yy.com や zz.yy.com なども失敗していたのですが、
この xx.yy.com 用の設定を直してあげただけで他も成功するようになったこと。

なんなの? 連帯責任なの・・・??

http2 設定を消したことによって 80 番での gRPC の通信ができなくなったけど、
まあ 443 番の方を使うのでそんなに問題は無しですね。

ところで、HTTP/2 って SSL/TLS 通信*1が必須だと思っていたので、
80 番ポートに http2 設定、というのを見て最初は「?」と思ったのですが、
平文(非TLS)の HTTP/2 通信も存在するんですね。

ただ、クライアント側(受け取り手)のサポートがあまりないので、
実質的には SSL/TLS 必須と。そういうことのようです。

基本的にサポートされているのはTLSを使用するh2のみで、多くのWebブラウザでは平文でやり取りを行うh2cについては未サポートとなっている。

https://knowledge.sakura.ad.jp/7734/ より引用 )

逆に言えば、送信側はなにも制限がないので、nginx の80番ポートに http2 設定を書けるのは何もおかしくはないし、
gRPC 用のクライアントが平文の HTTP/2 通信に対応しているのなら、そことやり取りできるわけですね。

*1:厳密にはTLS通信だが、SSL通信という言い方も歴史的に残っているのでこのように書いている