Dockerで開発しているWebアプリのデバッグをHTTPSで行えるようにする

 VisualStudioでDockerサポートのASP.NET Core MVCアプリを作り始めた。

 イマドキWebサイトは全ページHTTPSがめずらしくない。それがSEO上は有利でもあるし、Let'sEncryptという無料で証明書を取得する手段も出てきたのでやらない手はない。
 全ページHTTPSを見越し、開発環境でのデバッグもHTTPSでできるようにしておく。Dockerサポートを有効にしているので、コンテナを一個つけて、VisualStudioのデバッグ設定を一つ行うだけ。

 まずデバッグ用のcomposeファイルを編集する。docker-compose.override.ymlにリバースプロキシをやってくれるコンテナを加える。あとWebアプリコンテナのポートはホストにバインドしておく必要がなくなったので設定を削除する。
docker-compose.override.yml

version: '3'

services:
tetsujin:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "80:80"
   ↓
version: '3'


services:
tetsujin:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ssldevenv:
image: matoba/ssldevenv
ports:
- "443:443"
links:
- tetsujin:webapp


追加されたssldevenvというコンテナは、OpenSSLで発行した証明書を入れたもの。中ではNginXがリバースプロキシの役割で動いている。リバースプロキシはwebappという名前で解決されるホストへつながれるようになっている。なのでlinksでWebアプリコンテナの名前認識を変えてつないでいる。
 実際の中身は下記リポジトリを参照。
https://github.com/hMatoba/ssldevenv



 あとはVisualStudioのデバッグ設定を行う。これまではデバッグ実行ごとにブラウザが立ち上がるようになっていたのでこれを切る。




 VisualStudioでデバッグを実行する。コンテナのビルドなどが終わってローカルにデプロイされるのを待つ。




 ブラウザでhttps://localhostへアクセスしてみる。Webアプリへの接続が確認できるはず。




 開発環境でのデバッグをHTTPS接続でできるようにした。Dockerコンテナを使っているので書き換えるところは多くなく楽だったはず。本番環境でもこれと同様に楽に証明書を取得して接続できるコンテナを用意してある。
https://absurd.azurewebsites.net/Article/121


今回のコミット
comment: 0

継続的デプロイと継続認証のコンテナ

 DockerコンテナでLet's Encryptと継続的デプロイをなにも考えずに組み合わせたら大失敗だった。Let's Encryptには証明書取得数の上限があった。
https://absurd.azurewebsites.net/Article/118

 上記の問題を、証明書を外部ボリュームに入れて運用するDockerイメージを作ることで解決した。
https://github.com/hMatoba/cdcc

 下記はコンテナホスティングサービスHyper.sh上で、このDockerイメージを作って運用するときのdocker-compose。証明書を自動で取りに行って、WebアプリをHTTPS運用するコンテナとしてよしなにやってくれる。
docker-compose.yml

version: '2'

services:
cdcc:
image: matoba/cdcc
ports:
- '80:80'
- '443:443'
fip: 199.245.56.122
size: s4
links:
- webapp:webapp1
volumes:
- cdcc:/etc/letsencrypt
environment:
DOMAIN: 'example.com'
E_MAIL: 'foobar@example.com'
LINK_NAME: 'webapp1'
webapp:
image: some_webapp
ports:
- '80:80'
comment: 0

Let's Encryptで証明書を取ってみた(DockerのNginXコンテナ(Alpine)から)

 タイトルの通り、Let's Encryptで証明書を取ってみた。DockerのNginXコンテナ(Alpine)から。メモ。ちなみにHyper.shでコンテナのホスティングは行っている。なのでコンテナを動かすコマンド体系はHyper.sh準拠。ほぼDockerコマンドのまんま。

 まずコンテナを用意。
hyper run -d --name foo -p 80:80 -p 443:443 nginx:alpine


 IPをつける。DNS設定はしてあるものとする。
hyper fip attach xxx.xxx.xxx.xxx foo


 NginXコンテナが動いていて、そこへDNS設定してあるIPをつけたのだからアクセスできるはず。URLを入れてアクセスできるか確認しておく。



 コンテナへ入る。
hyper exec -it foo /bin/sh


 認証を実行してくれるライブラリを入れる。
apk update

apk add certbot


 下記でヘルプが表示される。
certbot -h


 すでにこのコンテナではNginXが動いている。デフォルトの設定で。だからcertbotには、NginXを使った認証コマンドを実行してもらう。
certbot certonly --webroot -w /usr/share/nginx/html -d hmatoba.net

これでインストラクションに従って入力していくだけ。成功すれば発行されたというメッセージとともにファイルが出力される。


 自動化のために、インストラクションで進める形はやめて、オプションを与えていかなければならない。しかしまあ証明書は取れた。めでたしめでたし。
comment: 0

AzureFileStorageのエミュレータにlocalhostじゃないIPを使ってコンニチワ

'17/9/21追記
IPを使っても、結局エミュレータにアクセスするマシンを仮想化で外に置いたら、エミュレータが使えなくなった。だから外の環境からエミュレータを使うのは無理かも。
********


Azure Storage Emulatorにコンテナ内からつなごうとして、結局リバースプロキシを立てた話

 少し前に、開発に使うAzureのストレージエミュレータがローカルからしか使えん、けどアプリはDockerコンテナにしたいから、Fiddlerでリバースプロキシするってのを書いた。書いておいて、やっぱFiddlerを開発の度に立ち上げるのもアレだなーと思ったので、一般的なサーバソフトで、まずはNginXで同じことをやってみることにした。今回は.NET CoreでC#によるエミュレータへの書き込み、ブラウザで書き込んだファイルの読み出しまでやる。

 .NET CoreのC#で、Azureのストレージエミュレータにアクセスして、適当なテキストファイルを書き込むコードを用意する。
using Microsoft.WindowsAzure.Storage;

using Microsoft.WindowsAzure.Storage.Blob;

using System;
using System.IO;
using System.Threading.Tasks;

namespace PlayAzureStorage
{
class Program
{
private static async Task Func2Async()
{
var accountName = "devstoreaccount1";
var key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
var connectionString = $"AccountName={accountName};"
+ $"AccountKey={key};"
+ $"BlobEndpoint=http://192.168.11.17:20000/devstoreaccount1;";
//+ "BlobEndpoint=http://127.0.0.1:1000/devstoreaccount1;";

var account = CloudStorageAccount.Parse(connectionString);
Console.WriteLine("connect: " + account.BlobStorageUri);
var blobClient = account.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("test");
var isCreated = await container.CreateIfNotExistsAsync();
if (isCreated)
{
var permissions = await container.GetPermissionsAsync();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
await container.SetPermissionsAsync(permissions);
}

var name = "foo.txt";
using (var stream = File.OpenRead(name))
{
var blockBlob = container.GetBlockBlobReference(name);
blockBlob.Properties.CacheControl = "public, max-age=2678400";
blockBlob.Properties.ContentType = "text/plain";
await blockBlob.UploadFromStreamAsync(stream);
Console.WriteLine("Uploaded.");
}
}


static void Main(string[] args)
{
Func2Async().Wait();
}
}
}

 適当なテキストファイルfoo.txtをプロジェクトに用意しておく。あと接続文字列にあるIPとポート番号は自分の環境に合ったもので。今回はリバプロで、ポート20000に来たものをポート10000に飛ばす。

 NginXを落としてきて、解凍したディレクトリ内にあるnginx.confをリバプロとして動作するように書き換える。その辺に転がってるリバプロサンプルを書き換えて、外部からのC#コードによる書き込みを実行したところ二度エラーが出た。一つはヘッダをもとのままになるように設定することで解決した。もう一つはNginXがデフォではHTTPのバージョンを1.0に書き換えてしまうためにエラーになったので、バージョン1.1で動くように設定。
 最終的に設定ファイルは下記のとおり。
nginx.conf

worker_processes 1;

events {
worker_connections 1024;
}

http {
sendfile on;

upstream app_servers {
server 127.0.0.1:10000;
}

server {
listen 20000;

location / {
proxy_pass http://app_servers;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
}
}

 nginx.confを用意したら、"start nginx"で起動しておく。

 まずストレージエミュレータを起動する。次に、用意しておいたC#コードを実行して、ストレージエミュレータへの書き込みを実行してみる。エラーなく処理が終了するはず。エラーが出てくる場合、Azureのライブラリがエラーを出すので、本来エミュレータから送られてくるエラーメッセージはそのままでは見られない。ここらはFiddlerを使ってキャプチャしておけば、デバッグに役立つ。


 ストレージエミュレータへの書き込みが成功したら、ブラウザからそのファイルを参照してみる。
http://192.168.11.17:20000/devstoreaccount1/test/foo.txt

成功。

 Azureを使ったWebアプリの開発をしたいが、その開発環境で使うストレージエミュレータがローカルからのアクセスしか受け付けないので、NginXでリバプロを立てて外部(Dockerのコンテナ内を想定)からのアクセスを受け付けるようにしてみた。うまくいった。
 しかしこれ、けっこうメンドウだったのでエミュレータがコンテナで用意できたらなーと思っている。
comment: 0

NginXコンテナを使って、コンテナからコンテナへリバースプロキシ

 さくらのArukasではWebアプリのコンテナをのっけたとき、HTTPSでアクセスするようになっている。アプリ側に証明書を入れて設定していなくてもそうなるので、リバースプロキシが適用されているんだろう。すごくありがたいことなのだが、デバッグがちょいややこしい。開発環境でもリバースプロキシを入れて、HTTPS→WebアプリコンテナへHTTPという流れを作ってやらにゃいかん。そうしなきゃcookieにトークンを入れてsecureフラグを立てたとき、そいつがクライアントとアプリケーションサーバのあいだで交換できなくなってしまう。
 リバースプロキシを設置する場所は二か所が考えられる。アプリケーションと同一のコンテナ内、あるいはその外。このリバースプロキシは環境によっては不必要。今回はアプリケーションのコンテナの外に置く。そういうわけで、リバースプロキシコンテナを用意して、それをHTTPSで動かすまでのメモ。

 とりあえず下記サイトにHTTPでのコンテナを分けたリバースプロキシの方法が載っているので同じものを準備。ただし二点だけ変更点あり。
https://www.sep.com/sep-blog/2017/02/27/nginx-reverse-proxy-to-asp-net-core-separate-docker-containers/
 ASP.NETのWebテンプレートの準備コマンドは変更
dotnet new -t web → dotnet new web
 あとDockerfileで指定しているaspnetcoreのバージョンは、1.0ではエラーが出たのでこれを1.1を使用した。

 で、とりあえず書いてあるとおりのコマンドを打って、composeでDockerを動かせばHTTP→HTTPをやってくれるリバースプロキシサンプルができあがる。次は左辺のHTTPをHTTPSにする。

 まず証明書を準備。本番では使わないのでオレオレで。ぼくはWindows10でLinuxSubsystemを使って生成してみた。
openssl genrsa -aes128 -out server.key 2048

openssl req -new -key server.key -sha256 -out server.csr
openssl x509 -in server.csr -days 365 -req -signkey server.key -sha256 -out server.crt

 上記で作った.keyからパスフレーズを抜くために、下記コマンドを実行した。
openssl rsa -in server.key -out  sserver.key

パスフレーズを抜いておかないと、NginXに.keyを設定するときにパスフレーズを求められてエラーが出る。
 ファイルが生成できたらsserver.keyとserver.crtをディレクトリnginxの中へ。

 あとは三つほどファイルを書き換え。
docker-compose.yml
コンテナnginxの待ち受けポート変更
docker-compose.yml

- "80:80"

  ↓
docker-compose.yml

- "443:443"


Dockerfile
オレオレ証明書の配置
Dockerfile

COPY server.crt /data/certs/server.crt
COPY sserver.key /data/certs/server.key


nginx.conf
SSLを使う設定
nginx.conf

listen 80;

   ↓
nginx.conf

listen *:443;
ssl on;
ssl_certificate /data/certs/server.crt;
ssl_certificate_key /data/certs/server.key;


 すべて済んだら"docker-compose build"をしてから"docker-compose up"。うまくいってればlocalhostへHTTPSアクセスでレスポンスが得られる。





おまけ
 nginxのコンテナはhubに何種類かあるが、OSがalpineのイメージもある。alpineはとても軽量で5MB程度のサイズしかないので、そっちを使うと合理的だったり。
Dockerfile

FROM nginx

  ↓
Dockerfile

FROM nginx:alpine
comment: 0