「code」と一致するもの


ShipIt

  •  
  •  
  • Category:

PerlモジュールのCPANへのアップロードはShipItというのが良いらしいので使ってみたのだが、ちょいちょい詰まりどころがあったのでメモ

ShipItのインストールにはcpanminusを使用します。
cpanminusのインストール等はここでは書きません。

ShipItのインストール

$ cpanm ShipIt

ShipItの準備

設定ファイルの作成

設定ファイル.shipitをプロジェクトディレクトリの直下に作成します。
0から書いても良いですが、コマンドで自動生成できます。

$ shipit --write-config

これで以下のようなファイルが生成されます。

# auto-generated shipit config file.
steps = FindVersion, ChangeVersion, CheckChangeLog, DistTest, Commit, Tag, MakeDist
# svn.tagpattern = MyProj-%v
# svn.tagpattern = http://code.example.com/svn/tags/MyProj-%v
# CheckChangeLog.files = ChangeLog, MyProj.CHANGES

stepsがshipitで実行する処理の内容です。左から順番に実行されます。
CPANにアップロードする際は UploadCPAN というステップを書き足します。

ちなみに私の設定は以下です。

steps = FindVersion, ChangeVersion, Commit, Tag, MakeDist, UploadCPAN
git.push_to = origin

それぞれのステップの意味は以下のような感じです。

  • FindVersion: 現在のバージョンを取得、リリースを行うバージョンをユーザーの入力から設定します
  • ChangeVersion: ソースコード内に記載されたバージョンを変更します
  • Commit: 変更をコミットしてGithubにプッシュします
  • Tag: gitのタグ付けを行いGithubにプッシュします
  • MakeDist: モジュールを作成します
  • UploadCPAN: CPANにアップロードします

ShipItの実行①

$ shipit
...
Running step ShipIt::Step::UploadCPAN=HASH(0x14dbf1a90)
Upload to CPAN? [Y/n] Y
Upload failed.

cpan-uploadのインストール

Uploadに失敗したのはアップロードするために必要なcpan-uploadコマンドが入っていないからです。
cpan-uploadは以下でインストールします。
古い記事では"cpan-upload-http"や"App::cpanupload"などをインストールすると書いてあったりしますが、時代は流れ現在は以下のモジュールをインストールするのが良さそうです。

$ cpanm CPAN::Uploader

ShipItの実行②

$ shipit
...
Upload to CPAN? [Y/n] Y
Please provide a value for --user
Upload failed.

PAUSEの設定

これはCPANアップロード先のPAUSEアカウントの設定がされてないからです。
以下の設定を~/.pauseとして保存します。

user <username>
password <password>

ShipItの実行③

$ shipit
...
Upload to CPAN? [Y/n] Y
registering upload with PAUSE web server
POSTing upload for /Users/gucchi/shipit-dist/Devel-PatchPerl-Plugin-Darwin-v0.1.2.tar.gz to https://pause.perl.org/pause/authenquery?ACTION=add_uri
request failed with error code 500
  Message: Can't verify SSL peers without knowing which Certificate Authorities to trust
Upload failed.

Mozilla::CAのインストール

これは信頼できるCAを見つけられなくて失敗したということです。
MacではCAはキーチェーンで管理されていますがPerlからは読み込めてないという感じですね。

これはMozilla::CAをのインストールで解決するようです。

$ cpanm Mozilla::CA

ShipItの実行④

$ shipit
...
Upload to CPAN? [Y/n] Y
registering upload with PAUSE web server
POSTing upload for ...
PAUSE add message sent ok [200]

成功です!

ありがとうございました!

Movable TypeをPSGIで動かす

Movable TypeはCGIプログラムですが、
CGIだとリクエストのたびにPerlを起動してプログラムの動かすため非常に遅いです。

とうわけでもう少し高速化したいと思います。

perlの高速化というとmod_perlやFastCGIが真っ先に思い浮かびますが、今回はもっとナウでヤングなPSGIで動かしたいと思います。

PSGIとはPerl Web Server Gateway Interfaceの略で、WebサーバーとWebアプリケーションを繋ぐインターフェイスになります。
PythonのWSGI、RubyのRackをインスパイアしたものみたいです。

というわけでPSGIで高速化してみました。

今回はApacheをリバースプロキシとし、PSGIサーバーはStarmanを使用する。



MTの設定

mt-config.cgiにPIDを保存するファイルを設定する
以下を追記(別にパーミッションさえちゃんとしていればパスはどこでも良い)

PIDFilePath /var/run/mt/mt.pid



Starman


インストール

$ cpanm Task::Plack
$ cpanm XMLRPC::Transport::HTTP::Plack

Task::PlackをインストールすればStarman他関連モジュールがインストールされるとのこと。


systemctlの設定

[Unit]
Description=starman for mt
After=syslog.target
After=network.target
After=mysql.service
[Service]
User=apache
Group=apache
Restart=always
SyslogIdentifier=starman
Environment=MT_HOME=/var/www/cgi-bin/mt
Environment=MT_CONFIG=${MT_HOME}/mt-config.cgi
ExecStart=/bin/sh -c "/usr/local/perl5/perls/perl-5.30.0/bin/starman ${MT_HOME}/mt.psgi --access-log /var/log/starman/access_log --error-log /var/log/starman/error_log --pid `perl -ne 'next if /^#/; print $1 if /^\s*PIDFilePath\s+(.*)/i' ${MT_CONFIG}`"
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

Unitセクションには依存の設定等を書く
movable typeはmysqlを使用するのでmysql起動後に起動するようにする

Serviceセクションにはサービスの起動設定等を書く
今回はExecStartにstarmanの起動コマンドを書いた

あとは以下のようにして自動的に起動するようにしておけば安心

$ sudo systemctl enable starman



Apacheの設定

安全にsslでアクセスするようにしたので443のVirtualHostで以下を設定した

<VirtualHost _default_:443>
...
<IfModule proxy_module>
ProxyPass /cgi-bin/mt/ http://localhost:5000/cgi-bin/mt/
ProxyPassReverse /cgi-bin/mt/ http://localhost:5000/cgi-bin/mt/
</IfModule>
...
</VirtualHost>



これで完了!

apacheとstarmanを起動すれば無事CGI版より早くなったMTを味わえるはず。

Docker on MacのVolumeマウント先

  •  
  •  

Dockerのvolumeはどこにマウントされているか?と言うお話

$ docker volume inspect 
[
    {
        "CreatedAt": "2020-08-19T14:55:42Z",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "volume",
            "com.docker.compose.version": "1.26.2",
            "com.docker.compose.volume": "vol"
        },
        "Mountpoint": "/var/lib/docker/volumes/volume_vol/_data",
        "Name": "volume_vol",
        "Options": null,
        "Scope": "local"
    }
]

これで分かるじゃないか。/var/lib/docker/volumes/volume_vol/_dataだろ。と思った方は大間違い。

$ ls /var/lib/docker/volumes/volume_vol/_data
ls: /var/lib/docker/volumes/volume_vol/_data: No such file or directory

こういうオチが待っている。。。

ない。。。

googleさんに聞いてみると

「Docker for MacのDocker EngineはVM上で動いているので、このMountpointはMac上のPathではなくVM上のPathである」

ということらしい。

なのでscreenでVMに入ってみると良いらしい

$ screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty
Can not exec '~/Library/Containers/com.docker.docker/Data/vms/0/tty': Permission denied

。。。

$ ls ~/Library/Containers/com.docker.docker/Data/vms/0/tty
----------  1 user  staff  12  7 30 00:00 /Users/gucchi/Library/Containers/com.docker.docker/Data/vms/0/tty

というわけで、Permissionを変えたりsudoしてみたりしたがダメ


こんな時はnsenterを使えってVMに入るらしい。

$ docker run -it --privileged --pid=host alpine nsenter -t 1 -m -u -n -I sh
# ls /var/lib/docker/volumes/volume_vol/_data
hello.txt

あった!

しかし毎度毎度Docker for Macの特殊な点に悩まされるなぁ。。。

X509v3 Subject Key Identifier

X509証明書のv3拡張にはSubject Key Identifier (SKI)というものがある。

このSKIはそのままでX509証明書に含まれる公開鍵の識別子である。

$ openssl x509 -text -noout -in server.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 13130459247485585328 (0xb638c31e101f07b0)
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: CN=root ca
        Validity
            Not Before: Aug  4 14:53:26 2020 GMT
            Not After : Aug  4 14:53:26 2021 GMT
        Subject: CN=server
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:18:76:24:20:7e:90:d5:8e:a9:76:3d:1b:0f:56:
                    01:f1:4e:6f:7f:43:71:2c:3f:86:15:45:08:03:2e:
                    d4:2d:d1:12:c9:f1:9e:4f:a6:03:dc:6b:50:ab:a7:
                    c0:4a:62:cc:0f:64:7b:00:fe:9a:d7:84:89:5b:35:
                    e4:e8:c2:50:32
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                DNS:localhost
            X509v3 Subject Key Identifier:
                5B:77:97:D1:B9:03:E2:C4:31:F6:84:3F:E6:70:51:BB:86:DD:DD:87
    Signature Algorithm: ecdsa-with-SHA256
         30:46:02:21:00:e0:39:08:21:72:e8:8c:0c:65:5f:e4:34:ed:
         04:bb:69:80:75:e8:d8:05:23:33:e3:6c:27:5d:49:50:a4:45:
         72:02:21:00:af:e8:84:50:95:fc:36:6f:01:13:03:90:f2:f0:
         21:20:cc:0d:42:70:98:c4:af:6a:78:5d:ef:7f:cf:d8:d3:a8

このSKIはopensslコマンドを使用する場合、証明書署名時に以下のような設定のextfileをオプションで与えると書き込まれる

subjectKeyIdentifier = hash

そしてこのhash、ただ公開鍵のハッシュを求めるのかと思ったら違った。。。

OpenSSLの説明(x509v3_config)を見ると2つの設定があるらしい。

  • hashが設定されていたらRFC5258に従って計算したハッシュ値が入る
  • 他の設定(違う文字列?)なら16進の文字列が入る(非推奨)

とのこと

そしてRFC5258には公開鍵からSKIを求める方法は一般的に2つあると書いてある

  • 1つは公開鍵のBIT STRING(excluding the tag, length and number of unused bits)のSHA1ハッシュ
  • もう1つは4bitのタイプフィールドとそれに続く公開鍵のBIT STRINGのSHA1ハッシュの最下位60bit

らしいが、

あくまで一般的な方法の例なので、ユニークな識別子を生成できるなら他の方法でも良い。

ここでopensslのhashという設定は前者になる模様

そんでもってここで言うBIT STRINGとは公開鍵ファイルからタグや長さ等のメタ情報を除いたもの。

これはopensslのasn1parseで求めることができる。

  1. まずパースして構造をチェック
    $ openssl asn1parse -in pub.key
        0:d=0  hl=2 l=  89 cons: SEQUENCE
        2:d=1  hl=2 l=  19 cons: SEQUENCE
        4:d=2  hl=2 l=   7 prim: OBJECT            :id-ecPublicKey
       13:d=2  hl=2 l=   8 prim: OBJECT            :prime256v1
       23:d=1  hl=2 l=  66 prim: BIT STRING
    ここでは23がBIT STRINGのオフセットである(オフセットは鍵のアルゴリズムによって変わる)
  2. BIT STRINGのオフセットを設定してパース
    $ openssl asn1parse -in pub.key -strparse 23 -noout -out pub.bin


これでBIT STRINGが求められたのであとはハッシュ(SHA1)の計算をするだけ

$ openssl sha1 pub.bin
SHA1(pub.bin)= 5b7797d1b903e2c431f6843fe67051bb86dddd87


↓ 証明書のSKI(再掲)

X509v3 Subject Key Identifier:
                5B:77:97:D1:B9:03:E2:C4:31:F6:84:3F:E6:70:51:BB:86:DD:DD:87


ちょっと表現が違いますが、見比べてみると全く同じSHA1の160bitのハッシュ値ですねヽ(・∀・)ノ ワチョーイ♪

DenyHostsでSSHブルートフォースアタックに対抗 CentOS

たまたま/var/log/secureを除いたら怪しいログが!

sshd[4113]: Invalid user shashi from 114.141.167.190 port 57982
sshd[4113]: input_userauth_request: invalid user shashi [preauth]
sshd[4113]: pam_unix(sshd:auth): check pass; user unknown
sshd[4113]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=114.141.167.190
sshd[4113]: Failed password for invalid user shashi from 114.141.167.190 port 57982 ssh2
sshd[4113]: Received disconnect from 114.141.167.190 port 57982:11: Bye Bye [preauth]
sshd[4113]: Disconnected from 114.141.167.190 port 57982 [preauth]
sshd[5949]: Invalid user joerg from 181.49.246.20 port 33874
sshd[5949]: input_userauth_request: invalid user joerg [preauth]
sshd[5949]: pam_unix(sshd:auth): check pass; user unknown
sshd[5949]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=181.49.246.20
sshd[5949]: Failed password for invalid user joerg from 181.49.246.20 port 33874 ssh2
sshd[5949]: Received disconnect from 181.49.246.20 port 33874:11: Bye Bye [preauth]
sshd[5949]: Disconnected from 181.49.246.20 port 33874 [preauth]
sshd[18062]: Invalid user admin from 106.241.33.158 port 23663
sshd[18062]: input_userauth_request: invalid user admin [preauth]
sshd[18062]: pam_unix(sshd:auth): check pass; user unknown
sshd[18062]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=106.241.33.158
sshd[18062]: Failed password for invalid user admin from 106.241.33.158 port 23663 ssh2
sshd[18062]: Received disconnect from 106.241.33.158 port 23663:11: Bye Bye [preauth]
sshd[18062]: Disconnected from 106.241.33.158 port 23663 [preauth]

明らかな攻撃ですね。

何も対策してなかったので対策しないといけないですね。

調べると色々あるようです。
日本以外からのアクセスを拒否するとか、手動で拒否するIPを追加するとか。。。

まあエンジニアなので自動化します!

自動化するソフトとしてはDenyHosts, Fail2banといったところが有名なようです。

DenyHostsはSSH用で拒否するIPをhosts.deny(拒否IPリスト)に追加していくという単純なもの。

Fail2banはiptableベースでSSH以外にも対応しているらしい。

今回はDenyHostsで対応することに。

昔はepelのリポジトリにあったようですが、現在は削除されている模様。

というわけでgithubのリポジトリにrpmがあるのでgithubからインストールします。(2020-07現在の最新版は3.1.2)
https://github.com/denyhosts/denyhosts/releases

$ yum install https://github.com/denyhosts/denyhosts/releases/download/v3.1/DenyHosts-3.1.2-1.noarch.rpm
$ $ rpm -ql DenyHosts
/etc/denyhosts.conf
/usr/local/bin/daemon-control-dist
/usr/local/bin/denyhosts.py
/usr/local/lib/python2.7/dist-packages/DenyHosts-3.1.2.egg-info
/usr/local/lib/python2.7/dist-packages/DenyHosts/__init__.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/__init__.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/__init__.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/allowedhosts.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/allowedhosts.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/allowedhosts.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/constants.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/constants.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/constants.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/counter.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/counter.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/counter.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/daemon.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/daemon.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/daemon.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/deny_hosts.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/deny_hosts.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/deny_hosts.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/denyfileutil.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/denyfileutil.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/denyfileutil.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/filetracker.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/filetracker.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/filetracker.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/lockfile.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/lockfile.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/lockfile.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/loginattempt.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/loginattempt.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/loginattempt.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/plugin.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/plugin.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/plugin.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/prefs.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/prefs.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/prefs.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/purgecounter.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/purgecounter.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/purgecounter.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/python_version.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/python_version.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/python_version.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/regex.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/regex.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/regex.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/report.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/report.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/report.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/restricted.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/restricted.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/restricted.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/sync.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/sync.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/sync.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/util.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/util.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/util.pyo
/usr/local/lib/python2.7/dist-packages/DenyHosts/version.py
/usr/local/lib/python2.7/dist-packages/DenyHosts/version.pyc
/usr/local/lib/python2.7/dist-packages/DenyHosts/version.pyo
/usr/share/man/man8
/usr/share/man/man8/denyhosts.8.gz

これでインストール完了。

本体は/usr/local/bin/denyhosts.pyというpythonスクリプトです。

いちいち起動するのはアホなんでsystemdに登録します。

そこで使用するのが/usr/local/bin/daemon-control-dist

こちらを/etc/init.d以下にコピーします

$ cp /usr/local/bin/daemon-control-dist /etc/init.d/denyhosts

ちょこっと中身も編集します。

###############################################
# Edit these to suit your configuration #
###############################################
DENYHOSTS_BIN = "/usr/sbin/denyhosts.py"
DENYHOSTS_LOCK = "/run/denyhosts.pid"
DENYHOSTS_CFG = "/etc/denyhosts.conf"
PYTHON_BIN = "/usr/bin/python3"

ここのDENYHOSTS_BINが異なるので実際のパスに変更

python3はデフォでは入ってないのでPYTHON_BINをpythonに

編集した結果が以下

###############################################
#    Edit these to suit your configuration    #
###############################################
DENYHOSTS_BIN = "/usr/local/bin/denyhosts.py"
DENYHOSTS_LOCK = "/run/denyhosts.pid"
DENYHOSTS_CFG = "/etc/denyhosts.conf"
PYTHON_BIN = "/usr/bin/python"

これでOK.

余談ですが、実はこのコードの下にOS判定のコードがあるんですが、いけてなくてうまくCentOSだと認識してくれません。
しかも、判定されても/usr/bin/denyhosts.pyに設定されるので使えない。。。

これで使えると思いきや、実はさっきyumでインストールしたDenyHostsのライブラリにパスが通ってない。。。
pythonのモジュール検索パスに含まれていないところにインストールされています。。。

というわけで、検索パス上にリンクを作ります(もちろんパスを追加してもOK)

$ ln -s /usr/local/lib/python2.7/dist-packages/DenyHosts /usr/lib/python2.7/site-packages/DenyHosts


そして、DenyHostsで使用しているpythonライブラリをインストールします。
基本的にはipaddrだけだと思います。

$ pip install ipaddr




そして最後に肝心のdenyhostsの設定です。
/etc/denyhosts.confを編集します

デフォルトでは監視するログファイルが/var/log/auth.logになっているので/var/log/secureに変更します

SECURE_LOG = /var/log/secure

これでsystemdでdenyhostsを起動すれば動くはず。

$ sudo systemctl start denyhosts


ちゃんと動いていれば、しばらく攻撃を受け続けた後に/etc/hosts.denyにIPが追加されているはず。

我が家では1日で約300件追加されてました。。。(それでもまだ攻撃を受けている。。。)

ちなみに主な設定項目の意味はこちら

SECURE_LOG 監視するsshdのログファイル
HOST_DENY アクセス拒否リスト
PURGE_DENY アクセス拒否リストにから削除する経過時間 空の場合永遠
BLOCK_SERVICE アクセス拒否リストを使用して拒否するサービス
DENY_THRESHOLD_INVALID アクセス拒否リストに入れる存在しないユーザーでのログイン試行回数
DENY_THRESHOLD_VALID アクセス拒否リストに入れるログイン失敗回数
DENY_THRESHOLD_ROOT アクセス拒否リストに入れるrootでのログイン試行回数
IPTABLES iptablesのパス iptablesを使って拒否する場合
DAEMON_SLEEP
DAEMON_PURGE アクセス拒否の解除を判断する間隔 PURGE_DENYが空の場合は無意味
SYNC_SERVER アクセス拒否リストを同期するサーバー 同期機能を使用する場合
SYNC_INTERVAL アクセス拒否リストの同期間隔
SYNC_UPLOAD アクセス拒否リストをサーバーに送るかどうか
SYNC_DOWNLOAD アクセス拒否リストをサーバーから取ってくるかどうか

というわけでグッドラック!

If is Evil...

  •  
  •  
  • Category:

軽くNGINXを使う機会があった。

NGINXの設定ファイルではif文が使える!便利!

と思ったら、公式がIf is Evil... when used in location contextという記事を出している。。。

怖い。。。

というわけでこの記事を読み解く。。。

簡単に言うと。と言うかはじめに書いてあるが、

「If文はlocationディレクティブ内で使用すると問題が発生することがある。期待しない動作をすることや場合によってはsegfaultすることもある。できるなら使わないで。」ってさ。

なんだよその機能。ガクブル :(;゙゚'ω゚'):

代わりにtry_files, return, rewrite...lastを使用しろってさ。

場合によってはembeded perlや3rd Party Moduleを使用しろと。

以下のようないくつかおかしな挙動をする例が紹介されている。


ifとadd_headerの組み合わせ

location /only-one-if {
    set $true 1;
    set $a 0;
    add_header X-First 1;
    if ($true) {
        add_header X-Second 2;
    }
    if ($true) {
        add_header X-Third 3;
    }
    add_header X-Fourth 4;
    return 204;
}

コレはX-Thirdしかヘッダーが追加されない。
謎仕様。
最後のifが優先され、if外のadd_headerは無視される。
もちろん2つのif文をなくせばX-First, X-Fourthの2つが追加される。


proxy_passの無視

location /proxy-pass-uri {
    proxy_pass http://127.0.0.1:8080/;
    set $true 1;
    if ($true) {
        # nothing?
        return 400;
    }
}

コレはオフィシャルの記事が間違っていた。
オフィシャルはif文の中で何もしていなかった。
この場合はちゃんとproxy_passが働いていた。
問題?なのはif文の中で返却ページに関する処理を行うときだ。
if文の中でreturnやrewriteなどを使用するとif文内の処理が優先されproxy_passは無視されることになる。


try_filesの無視

location /if-try-files {
    try_files  /2.html /1.html;
    set $true 1;
    if ($true) {
        # nothing
    }
}

こちらはなぜかif文の中で何もしなくてもifがあるだけでtry_filesが働かなくなる。
ここまで読んでいれば理解できると思うがもちろんif文の中にif文の中で返却ページに関する処理を書けば実行される。
ちなみにadd_header等は無視される。
proxy_passとの挙動の違いはなぜ。。。


SIGSEGV

location /crash {
    set $true 1;
    if ($true) {
        fastcgi_pass  127.0.0.1:9000;
    }
    if ($true) {
        # no handler here
    }
}

SIGSEGVって書いてあるけどnginxはクラッシュしなかった。
がしかし、エラーは起きる。fastcgi_passされることもなくnot foundが返るでもない。。。
無論、if文内にreturnやrewriteを記述すればそちらが実行される。


if-and-alias

location ~* ^/if-and-alias/(?.*) {
    alias /usr/local/opt/openresty/nginx/html/tmp/$file;
    set $true 1;
    if ($true) {
        # nothing
    }
}

この問題は指摘されているが特に何も問題なさそうだった。



最後になんでこの問題を修正しないのかと言う問いに対する答えが書かれていた。以下。

ifは強制的に評価して書き換えるもの、一方nginxの設定は宣言型。
ユーザーの要望によりif内で一部の非書き換えディレクティブを有効にしてしまったことが問題で、今に至る。。。
唯一の正しい修正はif内の非書き換えディレクティブをなくすことであるが、多くの設定が壊れるのでまだ完了してない。

それでもlocationディレクティブ内でifを使いたいなら

  • ifの挙動を理解してね(ココにヒントがあるかも)
  • 適切なテストしてね

だってさ

以上でnginxのevilなifの調査を終わります!

便利な機能なのでうまく理解して使いましょう。

golangとX.509証明書とopenssl

golangでX.509証明書をいじってみる...

まずはX.509証明書のお話を...

X.509証明書とは

X.509証明書は公開鍵証明書の標準としてITU-Tが策定したもの
基本的に証明書と言ったら公開鍵証明書を指すが、属性証明書、特定証明書というものもある。

公開鍵証明書はさらにCA(Certification Authority)証明書とEE(End Entity)証明書という2種類に分けられる。

CA証明書は認証局に対して発行する証明書。CA自身の秘密鍵で署名した自己署名証明書と他のCAで発行された証明書がある。

それに対してEE証明書はPKIユーザー向けの証明書。Webクライアント、Webサーバーで利用されるもの。

詳しいお話は日本語だとIPAの資料がまとまっている気がする
3.3 電子証明書

証明書の作成

まずはCA秘密鍵の作成
ここからはopensslコマンドで色々やっていく

$ openssl genres -out ca.key


CA公開鍵の作成

$ openssl rsa -in ca.key -pubout -out ca.pub.key


自己証明書署名要求の作成

$ openssl req -new -key ca.key -out ca.csr
...


証明書署名要求の内容確認

$ openssl req -text -noout -in ca.csr
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b5:74:92:48:77:60:d4:14:8a:50:ad:6c:01:ed:
                    4d:6f:0d:dd:99:4a:d1:8a:eb:61:78:d2:71:47:2c:
                    4f:d9:8b:41:9b:20:16:02:df:b5:0a:3f:12:35:5a:
                    1d:c1:c3:58:a9:b2:9f:60:41:32:cf:3b:b8:a0:ce:
                    4f:be:bd:28:6d:83:f5:ca:84:56:60:ed:4f:53:cc:
                    4c:37:1a:55:de:06:5b:f8:3b:0b:7c:c9:fe:bf:fc:
                    03:04:06:ef:91:dd:e4:39:b7:82:71:bd:40:2b:c1:
                    b8:f8:3d:84:6e:fe:b0:02:7d:15:f9:c7:1c:44:17:
                    08:b0:f4:4e:91:0b:0c:69:45:01:3a:30:8e:49:8a:
                    19:0c:97:56:a6:ea:e6:34:78:64:a9:96:fe:3d:72:
                    3d:1d:e2:99:62:72:14:fa:f8:29:c5:1a:c7:c0:61:
                    d0:07:70:4d:5a:fe:d2:43:67:5a:27:7f:8c:51:ca:
                    f5:6c:0b:c6:86:a0:af:6f:d4:78:10:6e:47:f0:89:
                    06:3d:d0:e8:ce:2c:50:81:13:94:7b:7d:46:36:f6:
                    d3:86:d0:cb:0e:0f:6e:cf:0b:8f:6f:bf:d1:30:bc:
                    2c:4d:f0:64:2d:c6:5f:2f:14:7b:18:a0:b2:2c:b4:
                    74:67:14:55:81:44:9e:bc:97:f1:42:c6:15:d1:f7:
                    d8:23
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha256WithRSAEncryption
         8a:be:80:c9:3e:18:3c:58:2a:c1:88:fa:82:d1:f6:ea:89:4d:
         5d:b4:d5:f7:4b:78:a8:47:d2:c5:89:d5:47:c6:86:df:9a:ee:
         27:56:9a:b5:bd:16:e5:f7:97:b0:41:c9:66:18:fc:72:b7:c2:
         97:3f:03:b6:38:ad:f8:5d:99:6c:b4:7e:4a:2d:e7:65:14:55:
         ea:89:ff:3c:32:28:dd:81:0f:de:bd:58:16:99:6d:cf:f2:45:
         25:88:d8:d6:6d:29:39:ce:1b:ce:1c:ca:56:93:b4:82:b3:6d:
         37:d1:b8:90:db:2d:8b:f5:26:16:8a:c5:de:58:52:db:44:c1:
         ca:70:48:3f:d2:c1:60:bd:93:94:c1:52:a7:f0:f9:73:0a:cd:
         be:a4:7c:6f:97:72:7c:37:60:2e:a6:b3:f5:62:9d:30:11:a0:
         0c:b4:29:98:ae:96:df:6d:e5:5e:af:ac:c6:3f:bc:9e:3b:75:
         6c:60:d9:d1:64:ed:2a:30:f6:57:ef:3b:98:20:4b:16:9b:93:
         94:4b:1a:1e:2d:14:b2:4b:ad:fd:b9:81:49:34:cf:78:97:e0:
         a6:4a:5a:24:a3:45:a3:9c:89:ee:47:a1:72:10:02:15:48:17:
         12:49:a0:c0:50:82:9b:1c:d0:0d:fd:78:f3:a4:21:44:ea:ba:
         32:d6:7d:fb


証明書署名要求を使用して署名された公開鍵証明書を作成

$ openssl x509 -req -in ca.csr -signkey ca.key -days 10000 -out ca.crt


証明書の内容確認

$ openssl x509 -text -noout -in ca.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 11311120728934412255 (0x9cf92c5fc3b16fdf)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Validity
            Not Before: Jun  8 16:45:52 2020 GMT
            Not After : Oct 25 16:45:52 2047 GMT
        Subject: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b5:74:92:48:77:60:d4:14:8a:50:ad:6c:01:ed:
                    4d:6f:0d:dd:99:4a:d1:8a:eb:61:78:d2:71:47:2c:
                    4f:d9:8b:41:9b:20:16:02:df:b5:0a:3f:12:35:5a:
                    1d:c1:c3:58:a9:b2:9f:60:41:32:cf:3b:b8:a0:ce:
                    4f:be:bd:28:6d:83:f5:ca:84:56:60:ed:4f:53:cc:
                    4c:37:1a:55:de:06:5b:f8:3b:0b:7c:c9:fe:bf:fc:
                    03:04:06:ef:91:dd:e4:39:b7:82:71:bd:40:2b:c1:
                    b8:f8:3d:84:6e:fe:b0:02:7d:15:f9:c7:1c:44:17:
                    08:b0:f4:4e:91:0b:0c:69:45:01:3a:30:8e:49:8a:
                    19:0c:97:56:a6:ea:e6:34:78:64:a9:96:fe:3d:72:
                    3d:1d:e2:99:62:72:14:fa:f8:29:c5:1a:c7:c0:61:
                    d0:07:70:4d:5a:fe:d2:43:67:5a:27:7f:8c:51:ca:
                    f5:6c:0b:c6:86:a0:af:6f:d4:78:10:6e:47:f0:89:
                    06:3d:d0:e8:ce:2c:50:81:13:94:7b:7d:46:36:f6:
                    d3:86:d0:cb:0e:0f:6e:cf:0b:8f:6f:bf:d1:30:bc:
                    2c:4d:f0:64:2d:c6:5f:2f:14:7b:18:a0:b2:2c:b4:
                    74:67:14:55:81:44:9e:bc:97:f1:42:c6:15:d1:f7:
                    d8:23
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha1WithRSAEncryption
         a1:84:89:20:5b:c5:b1:c7:ea:90:4c:af:2f:aa:98:29:70:a5:
         cf:14:b0:a3:a5:82:66:71:2e:54:93:ba:02:4f:80:bb:c2:fa:
         6f:1b:61:a9:1d:34:03:0c:7f:ae:ee:0a:e4:98:9d:d3:f5:d0:
         79:37:0f:c9:87:44:e7:25:ca:bc:45:9c:a9:09:26:a5:82:22:
         e4:8d:46:32:b4:27:fe:1f:96:a3:d9:66:f7:22:49:9c:f2:5e:
         63:73:c3:dd:a0:a7:38:f3:13:d2:ec:26:67:a8:9d:f8:b2:33:
         62:79:2d:89:3c:ed:1a:0d:94:44:54:57:ad:3d:5d:74:11:74:
         b9:ee:f6:ff:7e:90:0d:a5:76:80:af:a9:8f:75:cb:28:9e:66:
         ca:b2:07:b5:b2:c4:20:9f:55:f5:93:36:30:b6:78:93:c9:d6:
         97:a5:3f:4f:55:4f:25:9f:0f:6d:40:0e:3d:72:ca:63:87:8b:
         8f:12:31:28:ce:6d:e4:a8:c3:eb:09:a7:12:9a:28:f0:7d:4d:
         ea:6f:d7:2a:af:44:53:92:47:20:62:c6:db:0c:eb:ba:70:8d:
         c9:37:4b:8a:df:13:4c:44:c2:4b:27:62:d4:20:5d:78:29:ad:
         fa:7c:4c:1b:6e:6f:35:59:4a:5a:84:04:45:56:26:73:3e:02:
         e2:16:15:b3

見ての通りIssuerとSubjectが同じである。
自己署名証明書ってやつですね。
コレでCAの証明書生成は終わりです。


次はユーザー用の証明書を作成する

まずユーザー用の秘密鍵の生成

$ openssl genres -out private.key


証明書署名要求の作成

$ openssl req -new -key private.key -out x509.csr


証明書署名要求の内容確認

$ openssl req -text -noout -in x509.csr
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=JP, ST=Tokyo, O=gucchisk, CN=gucchi.info
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c1:88:ef:3c:a4:c7:59:3f:00:88:39:20:37:19:
                    8d:18:0e:ee:3e:2e:1d:ec:78:90:c5:31:a0:f6:6d:
                    4d:8c:22:ba:94:05:f5:da:9a:94:65:86:66:16:9a:
                    2c:cb:0f:3f:ac:66:fd:a5:3b:0f:06:fe:5c:02:a5:
                    6c:13:5d:50:ad:65:05:b4:43:26:04:8a:34:69:68:
                    a7:29:c2:f6:a0:05:4d:a0:23:bf:05:70:70:5f:27:
                    d1:d3:b9:b0:ab:51:1d:bd:62:a3:27:9a:a2:0c:ab:
                    c5:23:79:d7:c0:69:1d:77:23:34:f4:30:e2:17:28:
                    21:ed:84:6b:55:40:99:f6:40:aa:79:14:86:71:b6:
                    40:45:bf:3b:89:c8:b0:d0:20:b6:c1:0a:8f:39:9f:
                    a6:4f:06:11:22:db:0d:cc:8b:8b:44:46:74:61:88:
                    7b:c8:8c:11:bb:f5:f4:ab:ad:98:90:8e:0c:0d:21:
                    10:5f:62:97:83:df:94:ca:19:ee:1b:25:5a:cc:33:
                    ca:b7:f0:63:35:96:6e:9e:6d:56:a6:4d:ca:6d:9c:
                    ca:f0:8a:81:33:13:04:44:bb:38:fd:d5:fa:76:c1:
                    75:57:6b:8a:a6:c6:6d:04:c1:ba:5e:6c:c3:ea:db:
                    8e:2a:84:9b:5e:d8:2e:a8:81:44:af:60:67:7d:83:
                    07:09
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha256WithRSAEncryption
         44:fb:d4:1b:30:f6:54:bd:5f:b2:3c:83:b4:e1:0a:37:72:ad:
         60:8e:0c:c3:3c:8c:d0:d6:69:3a:1b:4b:19:c8:7f:5f:62:52:
         ad:3f:a9:50:61:7c:3e:31:20:99:b5:bd:8b:22:00:37:45:71:
         3b:67:1f:48:66:3d:c5:c9:0a:11:d1:02:11:37:28:bc:90:12:
         1d:b0:7b:9d:1f:8e:09:d3:1a:78:51:56:c7:bc:4e:24:2b:0e:
         33:d5:2a:59:d8:1b:11:52:87:3e:0f:22:91:ca:a3:e9:03:1b:
         3b:c4:96:33:0a:25:e5:ee:63:df:e8:27:93:b7:de:9c:0e:31:
         41:d1:80:8e:d0:ff:e8:8e:e7:d3:b5:d5:c4:f8:50:e7:99:86:
         dc:73:3b:c6:6c:4f:76:02:61:e6:44:49:b1:21:c3:ca:f2:7f:
         79:7b:ba:94:47:05:77:1a:48:5c:63:04:b5:07:0c:23:3b:17:
         29:75:81:9c:28:1d:0e:df:f0:50:f3:9f:fe:5a:70:9b:99:f8:
         6c:9f:7a:b9:29:03:97:6c:50:e2:ec:27:6f:4d:3d:d4:d1:42:
         e3:50:72:29:4c:1f:89:9d:12:f2:00:26:35:7c:f4:e1:97:05:
         e7:25:e9:51:41:b6:cc:e5:73:6f:79:5f:a2:c5:6c:15:5c:1b:
         f4:85:b1:cb


CA秘密鍵で署名した証明書を生成

$ openssl x509 -req -in x509.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -out x509.crt


証明書の内容確認

$ openssl x509 -text -noout -in x509.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 12170714301618296499 (0xa8e7102c92b996b3)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Validity
            Not Before: Jun  9 15:38:57 2020 GMT
            Not After : Jun  9 15:38:57 2021 GMT
        Subject: C=JP, ST=Tokyo, O=gucchisk, CN=gucchi.info
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c1:88:ef:3c:a4:c7:59:3f:00:88:39:20:37:19:
                    8d:18:0e:ee:3e:2e:1d:ec:78:90:c5:31:a0:f6:6d:
                    4d:8c:22:ba:94:05:f5:da:9a:94:65:86:66:16:9a:
                    2c:cb:0f:3f:ac:66:fd:a5:3b:0f:06:fe:5c:02:a5:
                    6c:13:5d:50:ad:65:05:b4:43:26:04:8a:34:69:68:
                    a7:29:c2:f6:a0:05:4d:a0:23:bf:05:70:70:5f:27:
                    d1:d3:b9:b0:ab:51:1d:bd:62:a3:27:9a:a2:0c:ab:
                    c5:23:79:d7:c0:69:1d:77:23:34:f4:30:e2:17:28:
                    21:ed:84:6b:55:40:99:f6:40:aa:79:14:86:71:b6:
                    40:45:bf:3b:89:c8:b0:d0:20:b6:c1:0a:8f:39:9f:
                    a6:4f:06:11:22:db:0d:cc:8b:8b:44:46:74:61:88:
                    7b:c8:8c:11:bb:f5:f4:ab:ad:98:90:8e:0c:0d:21:
                    10:5f:62:97:83:df:94:ca:19:ee:1b:25:5a:cc:33:
                    ca:b7:f0:63:35:96:6e:9e:6d:56:a6:4d:ca:6d:9c:
                    ca:f0:8a:81:33:13:04:44:bb:38:fd:d5:fa:76:c1:
                    75:57:6b:8a:a6:c6:6d:04:c1:ba:5e:6c:c3:ea:db:
                    8e:2a:84:9b:5e:d8:2e:a8:81:44:af:60:67:7d:83:
                    07:09
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha1WithRSAEncryption
         a7:a7:14:04:53:d7:68:cb:fd:32:cb:3a:8d:6d:6c:5a:1f:8f:
         58:04:0d:9d:ad:c0:2d:d6:79:4e:90:3f:97:29:6a:80:51:d1:
         04:d6:27:67:62:d9:21:0e:0d:66:d6:0b:89:e3:f5:98:c8:7e:
         0a:57:be:40:6c:bc:df:65:ac:32:59:ea:46:30:7b:96:ee:bc:
         bd:24:d3:df:6c:4c:56:3e:29:7c:db:5d:ed:93:19:3a:9d:12:
         6a:4e:81:53:2d:f0:24:25:3b:f3:bc:1b:b4:0d:31:ed:a8:48:
         b5:45:d1:59:3b:50:23:ef:a0:25:90:40:ee:4e:af:43:54:40:
         dc:1b:33:05:aa:e1:5f:17:8c:ba:e2:22:54:d4:7c:07:2f:c9:
         15:24:a8:3f:9d:6b:7e:a0:05:bd:0f:00:0e:82:d3:83:2b:9d:
         b6:a2:2b:10:b2:42:65:ee:6b:36:81:a9:3f:f5:4b:2d:3a:ee:
         46:03:b3:d1:c7:e6:7f:e4:7c:97:aa:cc:7f:50:ff:c8:95:bc:
         6d:84:a7:ff:17:ff:2b:60:2e:3d:e2:93:8b:df:5d:c3:24:a8:
         f4:e8:16:01:8a:43:95:e6:7c:87:f0:ba:4d:9a:b3:81:b9:50:
         46:33:d0:f8:1e:98:6e:01:9b:7d:2a:91:1c:e7:82:4a:12:74:
         ef:65:ef:3a

SANSを設定した証明書を作る場合はこちら

$ openssl x509 -req -in x509.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days  -out x509_sans.crt -extfile sans.txt

sans.txtの中身はこんな感じ

subjectAltName = DNS:gucchi.info, DNS:*.gucchi.info


証明書の内容確認

$ openssl x509 -text -noout -in x509_sans.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 12170714301618296498 (0xa8e7102c92b996b2)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=JP, ST=Tokyo, O=CA, CN=ca.com
        Validity
            Not Before: Jun  9 15:37:20 2020 GMT
            Not After : Jun  9 15:37:20 2021 GMT
        Subject: C=JP, ST=Tokyo, O=gucchisk, CN=gucchi.info
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c1:88:ef:3c:a4:c7:59:3f:00:88:39:20:37:19:
                    8d:18:0e:ee:3e:2e:1d:ec:78:90:c5:31:a0:f6:6d:
                    4d:8c:22:ba:94:05:f5:da:9a:94:65:86:66:16:9a:
                    2c:cb:0f:3f:ac:66:fd:a5:3b:0f:06:fe:5c:02:a5:
                    6c:13:5d:50:ad:65:05:b4:43:26:04:8a:34:69:68:
                    a7:29:c2:f6:a0:05:4d:a0:23:bf:05:70:70:5f:27:
                    d1:d3:b9:b0:ab:51:1d:bd:62:a3:27:9a:a2:0c:ab:
                    c5:23:79:d7:c0:69:1d:77:23:34:f4:30:e2:17:28:
                    21:ed:84:6b:55:40:99:f6:40:aa:79:14:86:71:b6:
                    40:45:bf:3b:89:c8:b0:d0:20:b6:c1:0a:8f:39:9f:
                    a6:4f:06:11:22:db:0d:cc:8b:8b:44:46:74:61:88:
                    7b:c8:8c:11:bb:f5:f4:ab:ad:98:90:8e:0c:0d:21:
                    10:5f:62:97:83:df:94:ca:19:ee:1b:25:5a:cc:33:
                    ca:b7:f0:63:35:96:6e:9e:6d:56:a6:4d:ca:6d:9c:
                    ca:f0:8a:81:33:13:04:44:bb:38:fd:d5:fa:76:c1:
                    75:57:6b:8a:a6:c6:6d:04:c1:ba:5e:6c:c3:ea:db:
                    8e:2a:84:9b:5e:d8:2e:a8:81:44:af:60:67:7d:83:
                    07:09
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                DNS:gucchi.info, DNS:*.gucchi.info
    Signature Algorithm: sha1WithRSAEncryption
         90:a6:d9:91:29:87:fb:46:32:b4:8a:77:a0:9c:c1:a7:38:ce:
         ea:4a:9d:aa:0a:5a:21:7f:59:a9:ce:89:7a:e3:03:2e:18:07:
         67:d2:f8:19:10:4b:7f:9e:2e:ba:d9:ba:ae:14:a0:9f:fc:e3:
         08:8b:87:37:f3:6b:80:de:75:a8:43:cd:3d:b1:50:27:be:05:
         0a:20:3c:2e:cf:2c:d0:91:43:47:3b:61:c8:d6:46:8c:a0:6f:
         40:06:4e:f5:27:f4:6d:00:26:a0:dd:65:a8:30:17:d1:11:87:
         24:2e:76:c9:9a:7e:fa:67:2f:52:79:a4:39:6b:53:0e:a1:af:
         10:2a:ef:22:75:14:ce:e8:b7:ee:ac:bb:a9:18:5a:e1:44:48:
         02:b5:63:e5:3b:bf:3e:92:37:3a:c2:e4:42:e9:76:24:79:ac:
         c0:f1:95:da:10:44:69:0c:32:d4:ec:e6:86:40:1d:84:2c:53:
         4e:6f:29:62:16:19:dd:38:0c:1d:08:44:8c:8a:d2:8c:d0:3e:
         26:cc:67:6f:61:1b:8e:6a:53:b2:20:fc:2e:d1:36:66:7c:39:
         0b:cf:cb:9d:e0:e6:a7:ac:bc:7f:f8:de:34:92:97:32:09:86:
         30:d5:93:49:20:22:b1:3e:f6:f8:47:9c:27:f8:f9:dd:b0:78:
         d8:74:72:39

こんな感じでX509v3 extensions以下にSANsが設定されている


最後に証明書の署名を検証する

$ openssl verify -CAfile ca.crt x509.crt
x509.crt: OK

OK!!(当たり前)

golangでX.509証明書をゴニョゴニョ

やっとここからgolangで作成した証明書を見ていく
もちろん検証も行う


まずはCAの公開鍵証明書の登録
ファイルを読み込んで証明書のpoolに加える(登録)

f, err := os.Open("ca.crt")
b, err := ioutil.ReadAll(f)
pool := x509.NewCertPool()
ok := pool.AppendCertsFromPEM(b)


次に検証したい証明書を読み込む

f, err = os.Open(certFile)
b, err = ioutil.ReadAll(f)
block, _ := pem.Decode(b)
cert, err := x509.ParseCertificate(block.Bytes)

証明書プールへの追加はPEM用のインターフェイスがあったが、直接PEMフォーマットからx509.Certificate型にするインターフェイスはないのでpemモジュールを使って一度pem.Blockにしてからx509.Certificateにする。


そして検証

hostname := "gucchi.info"
opts := x509.VerifyOptions{
	Roots: pool,
	DNSName: hostname,
}
if _, err := cert.Verify(opts); err == nil {
	fmt.Println("Verify: OK")
} else {
	fmt.Printf("Verify: NG, %s\n", err.Error())
}

x509.VerifyOptionsに必須のRootsプロパティに先程の証明書プールをセットし、ホスト名もチェックしたければホスト名をDNSNameに設定する。
そしてcert.Verify(opts)
成功ならばnilが返ってきて、失敗ならば理由が入ったerrorが返ってくる。

ちなみに期限切れの証明書だった場合はこんな感じに表示される。

Verify: NG, x509: certificate has expired or is not yet valid: current time 2020-06-12T23:14:49+09:00 is after 2020-06-10T15:39:58Z


VerifyHostname()はホスト名のチェックのみで署名の検証を行わない。

SANsが設定されている場合はCommonNameに適したホスト名かチェックする。
SANsが設定されていない場合はCommonNameは無視してSANsの設定に適したホスト名かチェックする。

X.509証明書の扱いはこんな感じ。
結構簡単にできますね。

今回のソースコードはこちらに置いておきます

https://github.com/gucchisk/go-samples/tree/master/x509cert

PKCS#1v1.5

  •  
  •  

RSAのデジタル署名は

private keyの生成


$ openssl genres -out private.key

public keyの生成


$ openssl rsa -in private.key -pubout > public.key

$ echo -n "hello world" > test.txt

$ openssl dgst -sha1 -sign private.key -out signature test.txt



$ openssl rsautl -verify -in signature -pubin -inkey public.key | hexdump
0000000 30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 2a
0000010 ae 6c 35 c9 4f cf b4 15 db e9 5f 40 8b 9c e9 1e
0000020 e8 46 ed
0000023


$ shamus -a 1 text.txt
2aae6c35c94fcfb415dbe95f408b9ce91ee846ed  test.txt


opensslではパディングのアルゴリズムとしてPKCS#1 v1.5(-pkcs), PKCS#1 OAEP(-oaep), SSLv2(-ssl), パディング無し(-raw)に対応している。

boost::variantのバージョン互換性

  •  
  •  
  • Category:

boost::variantを使用して共有ライブラリを作成、 そしてそのライブラリを使用するアプリケーションでboostの新しめのバージョンが必要だったので異なるバージョンのboostを使用してコンパイル。 すると、リンク時に

undefined reference to `hello(...

ソースはこんな感じ(例)

#include <boost/variant.hpp>
typedef boost::variant<int, std::string, char*> target;
std::string hello(target t);


とりあえず検証してみる

・リンクに失敗したオブジェクト

$ nm --demangle main.cc.o | grep hello
                 U hello(boost::variant<int, std::string, char*>)

・ライブラリ

$ nm --demangle libhello.so | grep hello
000000000000283c T hello(boost::variant<int, std::string, char*,
boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_>)

違う。。。

ってかboost::detail::variant::void_がいっぱい。。。

google先生に聞いても良い答えが返ってこないのでソースを見ていたら発見!
どうやらコレだ!

boost-1.56.0から可変引数テンプレートに対応したようだ。 しかしgcc-4.7未満だと使用しないらしい。ややこしや。。。

gcc-4.7未満 gcc-4.7以上
boost-1.56.0未満 × ×
boost-1.56.0以上 ×

ライブラリとそのライブラリをリンクするアプリでこの条件を揃えないといけないという事である。

boost::variantを使用したライブラリを作成するときは気をつけよう。

ちなみに○のところを×にする(可変引数テンプレートを使わないようにする)ためにはBOOST_NO_CXX11_VARIADIC_TEMPLATESマクロを設定すれば良い。
(参照:boost/variant/variant_fwd.hpp

CLion (IntelliJ) で Dockerを使ってリモート開発 (Full Remote Mode)

  •  
  •  

Linux用のライブラリを開発。

しかし、開発用のPCはMac...

まあMacはUnixベースなので基本的には同じようにできるが、細かいところで問題があったりする。

というわけで、やはりLinux上で開発した方が良い。 Linuxサーバーにログインして開発しても良いが、環境構築をミスった時などに消したり、違う環境を用意しなくてはいけない時に簡単に用意できるようにDockerを使って開発環境を構築する。

CLionにはフルリモートモードというのがあるので、コレを用いる。 Linuxが動くDockerコンテナでsshdを起動し、CLionからsshでリモートログインして開発するという流れ。

CentOSイメージでの開発サンプルはこちら https://github.com/gucchisk/clion-docker-remote

  1. サンプルをclone
  2. イメージを作成しコンテナを起動する
    • CLIの場合
      1. docker build . --tag centos-build
      2. docker run -td -p 22:22 centos-build
    • CLionのRun/Debug Configurationsを設定・実行
      1. 設定
      2. 実行
        ^R or ▶️
  3. フルリモートモードの設定

コレでOK!!
CMake -> target実行

快適開発!