独自ドメインからRoute53でCloudFrontにAレコードを設定したときに、SSL関係のエラーが出た時の対処方法

現象

独自ドメインから、Route53でCloudFrontにAレコードを設定した その後に独自ドメインから、upした画像にアクセスするとSSL関係のエラーが発生する

curl -vv https://example.com/E4Tvj0mUUAEWnW8.jpeg
*   Trying ***.***.***.***...
* TCP_NODELAY set
* Connected to example.com (***.***.***.***) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* error:1400410B:SSL routines:CONNECT_CR_SRVR_HELLO:wrong version number
* Closing connection 0
curl: (35) error:1400410B:SSL routines:CONNECT_CR_SRVR_HELLO:wrong version number

CloudFrontのドメイン直接の場合はupした画像を見ることはできる

原因

CloudFront側で、CloudFrontのlternate Domain Names(CNAMEs)に、独自ドメインを設定していなかった

参考

blog.serverworks.co.jp

感想

SSLのハンドシェイクで失敗するので、なにか証明書関係のエラーと思って少しハマってしまった...

サーバメンテナンスのときには、503(Service Unavailable)を返して、さらに Retry-After ヘッダー をつけておくと安心かも

まとめ

  • メンテナンスページを出す時には、503(Service Unavailable) を返す
  • Retry-After ヘッダーに復旧日時を入れておくと、復旧後に見に来てくれるようになる
  • Retry-After ヘッダーが無いまま、長期間 503 を返すと、クローラーはサーバが復旧しないと思って index を外してしまう
  • また、200404 とかを返すとクローラーがメンテナンス中であることを理解してくれない

ソース

ちょっと古い

webmaster-ja.googleblog.com

RFCの Retry-After レスポンスヘッダの解説

HTTP/1.1: Header Field Definitions

EC2インスタンスの再起動と停止/起動のちがい

先に結論

ここに書いてあるとおり

docs.aws.amazon.com

  • 再起動: インスタンスは、同じホストコンピュータで保持される
  • 停止/開始: インスタンスは新しいホストコンピュータに移動されます

メンテナンスでEC2のハードウェア退役や再起動する場合は、停止/起動 の方がいいの?

イベントの告知でも、stopping and starting your instance と書かれているので、停止/起動のほうが良いと思う

そんなおぼえがき

複数バージョンのbundlerがインストールされている時の挙動おぼえがき

概要

複数バージョンのbundlerがインストールされているとき、どれが選択されるのか曖昧だったので実験

注意

公式ドキュメントの記述がみあたらなかったので、バージョンが変わると挙動が変わるかもしれないです

環境

ruby:3.0.1-alpine を使用。defaultで入っているbundlerは、2.2.15です

docker run --rm --name ruby -it ruby:3.0.1-alpine /bin/sh
/ # gem list --local | grep bundler
bundler (default: 2.2.15)
/ # bundler -v
Bundler version 2.2.15

先に結論

以下の優先順でバージョンが選ばれるようです。default は関係ない様子

  1. $BUNDLER_VERSION
  2. Gemfile.lock の BUNDLED WITH
  3. 一番新しいバージョン

複数バージョンを入れた場合、default や、インストール順は左右されず、最新のバージョンが参照される

/ # gem install bundler:2.2.20
Fetching bundler-2.2.20.gem
Successfully installed bundler-2.2.20
1 gem installed

/ # gem install bundler:2.2.19
Fetching bundler-2.2.19.gem
Successfully installed bundler-2.2.19
1 gem installed

/ # gem list --local | grep bundle
bundler (2.2.20, 2.2.19, default: 2.2.15)

/ # bundle -v
Bundler version 2.2.20

ディレクトリに Gemfile.lock がある場合、そこの BUNDLED WITH にかかれているバージョンが参照される

/ # cat <<EOS > Gemfile.lock
> GEM
>   remote: https://rubygems.org/
>   specs:
>     rack (2.0.6)
> 
> PLATFORMS
>   ruby
> 
> DEPENDENCIES
>   rack
> 
> BUNDLED WITH
>   2.2.19
> EOS
/ # bundle -v
Bundler version 2.2.20

環境変数 BUNDLER_VERSION があると、それが最優先される

/ # export BUNDLER_VERSION=2.2.15

/ # bundle -v
Bundler version 2.2.15

環境変数 BUNDLER_VERSION や、Gemfile.lock に書かれたバージョンがインストールされていな場合、最新のバージョンが参照される

bundle install しない限り、特に警告は出ませんでした

/ # export BUNDLER_VERSION=2.2.13

/ # cat <<EOS > Gemfile.lock
> GEM
>   remote: https://rubygems.org/
>   specs:
>     rack (2.0.6)
>
> PLATFORMS
>   ruby
>
> DEPENDENCIES
>   rack
>
> BUNDLED WITH
>   2.2.16
> EOS

/ # bundle -v
Bundler version 2.2.20

おまけ: bundle _version_ でバージョンを明示的に指定すると、そのバージョンが参照される

/ # bundle _2.2.15_ -v
Bundler version 2.2.15

存在しないバージョンを指定すると怒られます

/ # bundle _2.2.00_ -v
/usr/local/lib/ruby/3.0.0/rubygems.rb:281:in `find_spec_for_exe': can't find gem bundler (= 2.2.00) with executable bundle (Gem::GemNotFoundException)
        from /usr/local/lib/ruby/3.0.0/rubygems.rb:300:in `activate_bin_path'
        from /usr/local/bundle/bin/bundle:23:in `<main>'

古い ruby image だと、挙動が変わります

docker run --rm --name ruby -it ruby:2.5.1-alpine /bin/sh
/ # gem list --local | grep bundler
bundler (1.16.6, default: 1.16.2)
/ # bundler -v
Bundler version 1.16.6

2.2.20 をインストールしても、1.16.6 が参照されたまま

/ # gem install bundler:2.2.20
/ # gem list --local | grep bundler
bundler (2.2.20, 1.16.6, default: 1.16.2)
/ # bundler -v
Bundler version 1.16.6

これは、古い ruby の Dockerfile の中で BUNDLER_VERSION が設定されているため そのため、BUNDLER_VERSION を unset しないと想定通りの挙動してくれない

こちらのissueで議題になって、新しいバージョンでは BUNDLER_VERSION がセットされないようになっています

github.com

elasticbeanstalkのプラットフォーム(ruby puma)のバージョンを上げると、nio4r の依存関係でエラーが発生する件

elasticbeanstalkのプラットフォーム(ruby puma)のバージョンを上げると、こんなエラーが出るようになる

/opt/rubies/ruby-2.7.3/lib/ruby/site_ruby/2.7.0/bundler/runtime.rb:302:in `check_for_activated_spec!': You have already activated nio4r 2.5.7, but your Gemfile requires nio4r 2.5.5. Prepending `bundle exec` to your command may solve this. (Gem::LoadError)

原因

プラットフォームに入っている puma に依存している Gem と、自分が入れた Gem がコンフリクトしているために発生している模様

解決方法

アプリの puma のバージョンをプラットフォームの puma のバージョンと合わせれば解決

参考

おんなじ問題にハマった人

各環境ごとのpumaのバージョン

Nginxのデバッグログを取る方法

Nginxの内部転送の設定がうまく行かない時とかにデバッグログは便利なのですが、その設定方法をよく忘れるのでメモ

エラーログのログレベルを debug にする

error_log /dev/stdout debug;

内部転送を出力するように設定

rewrite_log on;

Nginxをデバッグモードで起動

nginx-debug -g "daemon off;"

いじょうです

CORSの理解が不足していたのでメモ

CORSって何?

CORS: Cross-Origin Resource Sharing

オリジン間リソース共有

オリジンって何?

同じところからのリクエストであるかを判別するための方法

URIの、scheme、host、portが一致していると、それは「同一オリジン」である

scheme、host、portとか、URIの呼称については、こちらを参照

kasei-san.hatenadiary.org

どういう時に使うの?

異なるオリジンにAjaxリクエストやWebフォントの読み込みを行う際に使われる

なんで必要なの?

悪意あるサイトがAjaxで勝手にどこかAPIを叩けないよう、同一オリジン以外へのリクエストはブロックされるようになっている

  • これを、Same Origin Policy(同一オリジンポリシー) と呼ぶ

ただし、異なるオリジンにリクエストを投げる必要もあるため、CORS という仕組みが生まれた

どういう風にやるの?

単純リクエストという方式の場合、以下のような流れ

  1. ブラウザ側からリクエスト。リクエストヘッダの中に Origin が含まれる
    • Origin は、リクエストの発生元がどこであるかを示す
  2. サーバ側からレスポンス。レスポンスヘッダの中に Access-Control-Allow-Origin が含まれる
    • Access-Control-Allow-Origin はサーバが許可するオリジンを入れる
    • * の場合は「すべて許可」となるが、非常に脆弱なのでやめるべき
    • 複数の値を含めることはできない。許可すべきオリジンが複数ある場合は、サーバ側で制御する
  3. ブラウザ側で OriginAccess-Control-Allow-Origin が一致していれば、処理が行われる

プリフライトリクエスト

クロスオリジンのリクエストが いろいろな条件を満たさなかった場合、上記の単純リクエストではなく、プリフライトリクエストが行われる

ユーザーデータに影響を与える可能性があるようなリクエストの場合、プリフライトが行われる(らしい)

プリフライトリクエストの流れ

OPTIONS メソッドをつかって、事前に安全にクロスオリジンの通信が行えるかチェックする

以下のような流れ

  1. ブラウザ側で、 OPTIONS メソッドで以下のリクエストを投げる
    • Origin : 単純リクエストと同じ
    • Access-Control-Request-Method : 送るメソッド
    • Access-Control-Request-Headers : 送るリクエストヘッダ
  2. サーバ側からレスポンスを投げる。その時、以下のレスポンスヘッダを含む
    • Access-Control-Allow-Origin : 単純リクエストと同じ
    • Access-Control-Allow-Methods : 許可するメソッド
    • Access-Control-Allow-Headers : 許可するリクエストヘッダ
    • Access-Control-Max-Age: プリフライトリクエストの結果をキャッシュして良い時間(秒)
  3. ブラウザ側でレスポンスヘッダを読み込んで、問題なさそうならば実際のリクエストを開始する

資格情報を含むリクエスト

標準では cookie や basic 認証を含むリクエストに関わる情報は送信されない

そのためには、JavaScript 側で withCredentials というオプションを true にする必要がある

さらに、cookie の情報をやり取りしたい場合は、レスポンスヘッダに Access-Control-Allow-Credentials: true をつけて、許可する必要がある

また、Set-Cookie については、サードパーティCookieと同じく、ドメインが異なるとブラウザの設定によっては読み込めない

あと、 資格情報を含むリクエストの場合 Access-Control-Allow-Origin* は使えない

参考

yamory.io

developer.mozilla.org