お世話になっております。株式会社Joolenの白井です。
いつもOSS開発部にてEC-CUBEのカスタマイズ開発とかをやっています。ときどき本体へのコントリビュ〜ションなどもやっています(もうちょっと増やしたいです)。
そんなに派手なことはやっておりませんので、ちまちました設定の話をします。
最近カスタマイズ案件でやった話で、AzureにEC-CUBEを載せるというのがありました。
わたくしどもは大体の場合、EC-CUBEのDBとしてMySQLをセレクトしておりますので、当然のようにAzure Database for MySQLを使用する運びとなりました。
それで、知らなかったのですが、Azure Database for MySQLでは、アプリケーションとの通信でSSLを使うのがデフォルトなんだそうです。
同じネットワークの中なんだし細けぇこたぁ良いじゃねえかという気もしたんですが、そこはやはり現代のゼロトラストネットワークなんですかね。
ちょうど親方日の丸であるところのIPAからもゼロトラスト推進すべしというような文書も公開されていて、タイムリーですね。
ゼロトラスト導入指南書 〜情報系・制御系システムへのゼロトラスト導入〜
目次
準備
それでアプリケーション(EC-CUBE)とDBの間でSSL通信したいわけなんですが、
まず、接続先のサーバであるDBの身元を証明するために、この通信のクライアントであるEC-CUBEが、ルート証明書を持つ必要がある 、ということです。
SSL/TLS暗号化通信の仕組み|GMOグローバルサイン【公式】(https://jp.globalsign.com/ssl-pki-info/ssl_practices/ssl_encryption.html )から引用
この必要なルート証明書というやつは、マイクロソフトが配ってくれています。
Azure Database for MySQL に安全に接続するためにアプリケーションで SSL 接続を構成する
(BaltimoreCyberTrustRoot.crt.pemってやつです。なんでボルチモアって書いてあるのかはよくわかんないです。デトロイトメタルシティみたいなことだと思います。)
そんでこの証明書を使ってEC-CUBE(Symfony)がよしなに通信してくれたらそれでもう解決するのですが、残念なことにEC-CUBEがDBアクセスをする方法は 一種類ではない ので、それぞれの設定のところでそれぞれに証明書をご案内する必要があるのでした?
(それぞれにきっちり3日ぐらいずつハマりました)
なお、わたしのやったプロジェクトがEC-CUBEなのでEC-CUBEと言ってますが、おおよそはSymfonyで通用する話だと思います。2021-06-30時点でEC-CUBE4が使用してるのはSymfony3なので、新しいSymfonyでどうかとかはちょっとわからないですが……。
# Doctrine
まずは一番よく使うところであろうところのDoctrineの設定です。
↑偉大なる先達のおかげでここはわりとすぐクリアできました。正直言ってこの記事の内容のほとんどは上記の記事を読めば書いてあります。ありがとうございます。
[/app/config/eccube/packages/doctrine.yaml]
doctrine: dbal: options: !php/const PDO::MYSQL_ATTR_SSL_CA: '%env(SSL_CA)%'
環境がいい感じだったらここだけで普通に動くと思います?
ぼくもそうだと思ってました。
(ちなみに、このyamlの中でphpの定数呼び出すのもわりとアレコレ試してようやっとできました。(つらいです。))
あと、envから値を読むようにしたので、ルート証明書の実際のパスは.envに書いておく必要がありますね。
[/.env]
SSL_CA=/absolute/path/to/certs/BaltimoreCyberTrustRoot.crt.pem
# PDOSessionHandler
今回やってたプロジェクトでは、EC-CUBEのあるWebApサーバを複数台構成にしております。
当世、複数台構成ぐらいは珍しくない(EC-CUBEでやるのはそんなにないかもしれない)ので、このプロジェクト特有の問題だったというほどではないのではないでしょうか。
なんで、わりとハマりどころなのでは?って思ってます??
で、複数台構成にした時にセッションの持ち方というのがいろいろあると思うですが、今回はDBにおくという方法を採用してます。
セッションをファイルではなくデータベースに保存する方法 PdoSessionHandler
DBにセッションがあるわけなんで、PDOSessionHandlerはしょっちゅうDBアクセスをします。その時にルート証明書の場所をご案内さしていただくのでございます。
[/app/config/eccube/packages/session.yaml]
services:
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
public: false
arguments:
- '%env(DATABASE_URL)%'
-
db_connection_options:
!php/const PDO::MYSQL_ATTR_SSL_CA: '%env(SSL_CA)%'
これ、キーのある配列とない配列が入り乱れてて何だかよくわからなくなってますが、phpの連想配列に直すとこうです。
services => [ Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler => [ public => false, arguments => [ '%env(DATABASE_URL)%’, [ db_connection_options => [ !php/const PDO::MYSQL_ATTR_SSL_CA => '%env(SSL_CA)%’, ], ], ], ], ]
(やっぱり複雑)
# インストーラ
まあ実際のところもうデプロイの段階の訳なので、インストーラを叩く必要があるかというと別にない場合も多いと思うのですが、諸般の事情でインストールからやりたかったため、ここも対応する必要がありました?
ありがてえことにEccubeのインストールコマンド(bin/console eccube:install)は基本的にはDoctrineを通してやってくれるのですが、一箇所だけ独力でDBとのコネクションを作ってるところがあり、そこでルート証明書が必要になります。
[src/Eccube/Command/InstallerCommand.php getDatabaseServerVersion() 266行あたり]
$conn = DriverManager::getConnection([ 'url' => $databaseUrl, 'driverOptions' => [ \PDO::MYSQL_ATTR_SSL_CA =>'/etc/ssl/certs/BaltimoreCyberTrustRoot.crt.pem', ], ]);
われわれはコマンドを使用したのでこの一箇所だったのですが、もしWeb画面からインストールをしたいという向きがありましたら他のとこも対応する必要がありそうです。
でもたぶんDriverManager::getConnection()のとこを変えたらいいはず。たぶん?
# まとめ
実際にはAzureのネットワークの中で完結する通信だと思うので、あんまり一生懸命やんなくてもいいんじゃないの?って思っちゃうんですけど、まあそこはゼロトラストネットワークでやっていきましょうねってのが令和スタンダードなんですかね?
まあでも、AWSとか他のクラウドもそうなる可能性ありますし、「“man in the middle” 攻撃から保護されます。」(https://docs.microsoft.com/ja-jp/azure/mysql/howto-configure-ssl より)とのことですから、やった方がいいことなんでしょうね。SPAみたいなやつだとクライアントサイドから直接DBつなぐことだってないわけではない(ないか?)と思いますし、あとは何かしらデータセンターで障害起きた時にアベイラビリティゾーンまたいでDBとAPが通信することになったら途中でクラウドから出ちゃうかもとか(これもなさそうだけど)……
まあ何が起きるかわからないですからね、リアルワールドは……
ネットワークはゼロトラストでも、私たちは人と人の間の”きずな”を保っていきたいものですね?
以上です!