AWS Certificate Manger へスイッチ

このブログでも何度か Let’s Encrypt について触れてきたけど、ここで AWS Certificate Switch にスイッチすることにした。

Let’s Encrypt の取り組みには強く共感するものの、ふたつ問題があった。

  • サービスの特性上、trial & error を繰り返すとアカウントをロックされる (SSL 証明書の自動更新のためのスクリプトをデバッグ中にロックされるのはちょっと…)
  • ワイルドカード証明書を利用できない

一方の AWS Certificate Manger だが、これは驚くほど簡単に設定できた。単に AWS コンソールでぽちぽちクリックして、ALB に「この証明書を使ってちょうだい」と設定するだけで設定が終わった。

そういうわけで、ようやく僕のブログも HTTPS でホストできるようになった。ワイルドカード証明書さまさまだ。

実は WordPress と縁を切ろうと思っており、ごにょごにょしていたんだけど、前述の通り Let’s Encrypt でアカウントをロックされたあたりで気持ちが萎えていたのであった。AWS にロックインされるのは好ましくはないけど、もはや仕事でも AWS にどっぷり浸かっているし、逃げられないところまで来てしまった気がする。そうであれば、逆にどんどん AWS を使い倒せるように自分を持っていく方がいいかもしれない。

Let’s Encrypt が証明書の更新時に “Could not connect to example.com” とエラーを返す場合の対処

mahata.org は Let’s Encrypt で SSL 証明書を作成/更新している。

Cron に仕込んでいた SSL 証明書の更新スクリプトが “Could not connect to mahata.org” というエラーメッセージで失敗していたので、少し調べてみた。全体のエラーメッセージは次の通りだ:

Failed authorization procedure. www.mahata.org (http-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Could not connect to www.mahata.org, mahata.org (http-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Could not connect to mahata.org

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: www.mahata.org
   Type:   connection
   Detail: Could not connect to www.mahata.org

   Domain: mahata.org
   Type:   connection
   Detail: Could not connect to mahata.org

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A record(s) for that domain
   contain(s) the right IP address. Additionally, please check that
   your computer has a publicly routable IP address and that no
   firewalls are preventing the server from communicating with the
   client. If you're using the webroot plugin, you should also verify
   that you are serving files from the webroot path you provided.

Let’s Encrypt の更新スクリプトは GitHub に上げてある。これを何もオプションをつけずに実行するだけだ (注意: スクリプト先頭の環境変数は僕の環境固有のもの)。

少し調べてみたところ、Let’s Encrypt の認証サーバが mahata.org ドメインのバリデーションをするとき、80 番と 443 番ポートのどちらでバリデートしてもらうか選択できるようであった。公式のドキュメントには次のように書かれている:

To obtain a cert using a “standalone” webserver, you can use the standalone plugin by including certonly and –standalone on the command line. This plugin needs to bind to port 80 or 443 in order to perform domain validation, so you may need to stop your existing webserver. To control which port the plugin uses, include one of the options shown below on the command line.

  • –preferred-challenges http to use port 80
  • –preferred-challenges tls-sni to use port 443

僕は --preferred-challenges を指定せず、手を抜いて 80 番ポートを公開することで問題を解決した。

`aws ecr get-login` のトラブルシュート

ECR サーバにログインするとき、 aws ecr get-login コマンドでログインに必要なコマンドを取得できる。単にこのコマンドの結果を eval すればログインが完了するので、通常は eval $(aws ecr get-login) とすればよい。

しかし、次のようなエラーに遭遇してしまった。

$ eval $(aws ecr get-login)
Flag --email has been deprecated, will be removed in 1.13.
error getting credentials - err: exit status 1, out: `2017-04-13T08:27:26Z [ERROR] docker-credential-ecr-login can only be used with Amazon EC2 Container Registry.
credentials not found in native keychain`

credentials not found in native keychain の意味がわからず、「キーチェーンなんて動いてないけど…」と時間を無駄に費やしてしまった。

今回のケースでは、~/.docker/config.json に次のような情報が詰まっていたことが問題であった (どのタイミングでこの情報が入ったのか不明だけど…)。

$ cat ~/.docker/config.json
{"credsStore":"ecr-login"}

これを削除して再度 eval $(aws ecr get-login) を実行することで、ECR にログインできた。

JVM オプションでの SOCKS プロキシの指定

Java (or JVM 言語) で SOCKS プロキシを経由して外部リソースにアクセスしたい場合を考えてみよう。

まずは次のようなコマンドを実行して、localhost の 1080 番ポートに SOCKS サーバを立ち上げる。

$ ssh -f -N -D 1080 bastion.example.com
$ sudo lsof -i :1080
COMMAND  PID            USER   FD   TYPE            DEVICE SIZE/OFF NODE NAME
ssh     4006 yasunori.mahata    8u  IPv6 0x103xxxxxxxxxxxx      0t0  TCP localhost:socks (LISTEN)
ssh     4006 yasunori.mahata    9u  IPv4 0x103xxxxxxxxxxxx      0t0  TCP localhost:socks (LISTEN)

Java アプリケーションにこの SOCKS プロキシを使わせたいとしよう。例えば、bastion.example.com からしかアクセスできないリソースにさわりたい場合だ。

そういう場合は、JVM に次のオプションを渡せばよい。

-DsocksProxyHost=localhost -DsocksProxyPort=1080

参考記事

Route 53 で Zone Apex を ELB に振り向けるときの設定

僕は mahata.org というドメインを所持している。

mahata.org のウェブサーバは ELB の背後にあり、mahata.org を名前解決した結果はこの ELB を指してほしい。これを実現するため AWS 固有の知識が必要だったので、後々のためにメモを残しておく。

直面した課題

Zone Apex とは「サブドメインを含まないドメイン名」を示す言葉である。例えば “www.mahata.org” は Zone Apex ではなく、 “mahata.org” は Zone Apex だ。

Amazon Route 53 では Zone Apex に対して CNAME の設定が許可されていない。mahata.org を ELB の CNAME にしたかったんだけど…。ELB の IP アドレスは動的に変わるので、mahata.org の A レコードを設定するのは筋が悪い。

さあ、どうしよう。

CNAME レコード以外の選択肢

少しぐぐった結果、Amazon Route 53 では ALIAS レコードという DNS レコードを設定できることがわかった。リンク先のオフィシャルドキュメントでは、CNAME レコードとの比較の中で「エイリアスリソースレコードセットは Zone Apex に作成できます」と明記されている。

これを使えばよさそうだ。

ALIAS レコードの設定

ALIAS レコードは限られた AWS リソースにしか設定できない。例えば ELB や CloudFront ディストリビューションなどが「限られた AWS リソース」である。

対象となる AWS リソースによってレコードの設定方法は少しずつ異なる。ALIAS レコードで ELB を指す場合は DNS 値の先頭に dualstack. を付ける。例えば dualstack.elb-example-0000000000.ap-northeast-1.elb.amazonaws.com. のような感じ。

具体的には Amazon Route 53 のコントロールパネルで次のようにする。

  1. Create Record Set をクリックする
  2. “Type: A” で “Alias” は “Yes” にチェックを入れる
  3. “Alias Target” に dualstack.elb-example-0000000000.ap-northeast-1.elb.amazonaws.com. のような値をセットする

これで Zone Apex が ELB を指せるようになる。

CheckiO で Python を学ぶ

この記事は Python Advent Calendar の15日目の記事として書かれています。

CheckiO とは何か?

CheckiO は Python を書くことでステージを攻略していく、ゲームサービスでした。今は Python と JavaScript のどちらか好きな方でプログラムを書くことができます。

姉妹サービスとして、Empire of Code というものもあります。こちらの方では、日本のソシャゲーのように、現実世界で一定時間が経過すると回復アイテムがもらえる仕組みがあり、定期的にログインさせるインセンティブを持たせたゲームデザインになっています。

この記事では CheckiO に絞って話を進めていきます。

CheckiO のよいところは何か?

ゲームを通じてプログラミングを学ぶというコンセプトはそれほど目新しいものではありません。例えば CodinGameCodeCombat など、様々なプログラミングゲームが存在します。“Programming Games” というクエリで Google 検索する と、このジャンルで一旗揚げようとしているサービスがとても多いことが分かると思います。

このように多数のプログラミングゲームが存在する中で、僕が CheckiO を特に気に入っている理由がいくつかあります。

問題が段階的に難しくなる

このゲームでは、いきなり高難度の問題に取り組むことができないようになっています。

ゲームを攻略していくことでスコアを獲得し、ある一定値を超えた段階で、より難しい問題に挑戦できるようになります。初学者が「どこから初めていいか分からない…」と途方に暮れることのないような設計になっています。

「とりあえず要件を満たすコード」を提出できれば他人のコードを読める

簡単な問題であっても、エキスパートのコードと初心者のコードではアプローチが大きく異なります。

「とりあえず動く」コードさえ提出できれば、エキスパートのコードを読み、どこに改善の余地があるのか学べます。

逆に言うと、「とりあえず動く」ところまでは自分で考え抜かなければなりません。そこまで自力で行ければ、基本的なロジックは理解できているということなので、他人のコードを消化する準備ができたと言えるわけです。よくできています。

時間制限がない

コーディング時間の制約が一切ないので、とことん考え抜くことができます。

仕事では往々にして決められたデッドラインに向けて、妥協をしつつプログラムを作成します。CheckiO では、時間的なプレッシャーがない中、閃きがあるまでずっと問題に取り組むことができます。

再提出回数の制限も存在しないので、「これで万全!」と思ったものであっても、さらに後で改良することができます。

出題に貢献できる

企業がホストしているゲームにも関わらず、問題文に “How to improve this question?” のリンクが存在し、プルリクエストベースで問題文の編集をリクエストすることができます。コミュニティベースでサービスをよくしていきたいという姿勢には好感が持てます。

(必要があれば) ヒントが与えられる

もし全く問題を解く糸口が見つからない場合、”I have no idea how to start solving this mission (どこから手をつけていいか分かりません)” というリンクをクリックすると、出題者からのヒントが読めます。

最初の一歩を踏み出す

CheckiO でアカウントを作ると、まずは「HOME」と「ELEMENTARY」というステージに行けるようになります。例として「ELEMENTARY」にある FizzBuzz の問題を取り上げましょう。

“Fizz buzz” is a word game we will use to teach the robots about division. Let’s learn computers.

You should write a function that will receive a positive integer and return:
“Fizz Buzz” if the number is divisible by 3 and by 5;
“Fizz” if the number is divisible by 3;
“Buzz” if the number is divisible by 5;
The number as a string for other cases.

典型的な FizzBuzz の要件です。”Solve It” ボタンをクリックして、解いてみましょう。

ひな形のコードが次のように与えられています。

def checkio(number):
    #Your code here  (コードはここに書いてください)
    #It's main function. Don't remove this function  (この関数が評価対象なので、削除しないでください)
    #It's using for auto-testing and must return a result for check.  (返り値を自動テストが確認するので、値を返してください)

    #replace this for solution  (ここを書き換えて解答を作成してください)
    return str(number)

ベタに書くと、こんな感じになるでしょうか。

def checkio(number):
    if number % 15 == 0:
        return "Fizz Buzz"
    if number % 5 == 0:
        return "Buzz"
    if number % 3 == 0:
        return "Fizz"

    return str(number)

コードを書き換えたら、画面左上の “Check” をクリックしてみましょう。

正解の場合は添付画像のように “View other solutions (他人の解答を見る)” というリンクが表示されます。

CheckiO の猛者について

“View other solutions (他人の解答を見る)” を実際にクリックしてみましょう。

各人の答えは「Speedy」や「Creative」などのように、カテゴリ分けされます。

本記事の執筆時点で最も Creative なスコアが高い解答は veky による次のコードです。

checkio=lambda n:("Fizz "*(1-n%3)+"Buzz "*(1-n%5))[:-1]or str(n)

ちょっと文章では説明しづらいですが、読解してみましょう。lambda で関数オブジェクトを作り、引数 n が 1-n%3 == 1 のときだけ "Fizz " を、1-n%5 == 1 のときだけ "Buzz " を作り出し文字列結合します。そして、最後の空白を削るために [:-1] しています。"Fizz " でも "Buzz " でもないとき、すなわち "" のときは bool 評価をすると False なので or で結んだ str(n) が返り値になります。

じっくり読めば理解できるけど、これを頭の中からひねり出すのは困難でしょう。勉強になります。

まとめ

「Python を学んで次の一歩を踏み出したいけど、適当なお題が見つからない」なら、CheckiO を試してみるのも一興かもしれません。ゲーム感覚で楽しめるし、他人のコードを読むことでプログラミング力の向上も望めることでしょう。

Linux の /sys 以下のファイル変更を再起動後にも保つ方法

Linux のパフォーマンスチューニングをするとき、/sys ディレクトリ以下のファイルを編集することがままある。しかし、Linux のディストリビューションによっては、ここに書いた設定が再起動時にデフォルトに戻される場合がある。そういうときは、/etc/rc.local にマシン起動時の処理を書けばよい。

例えば Amazon Linux の /etc/rc.local のヘッダコメントには次のように書かれている。

!/bin/sh

 This script will be executed *after* all the other init scripts.
 You can put your own initialization stuff in here if you don't
 want to do the full Sys V style init stuff.

要約すると「Sys V の init とは別にマシン起動 の処理を書きたいときは、このファイルに書いていいんだよ」という感じだ。

ここに書いたシェルスクリプトは、起動するたびに毎回実行される。/sys 以下に存在するファイルの変更などは、ここに書いてしまえばよい。