Tech Racho エンジニアの「?」を「!」に。
  • 開発

Rails 5.2のcredential機能をKubernetesで使う(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

credential(認証情報)は英ママとしました。

Rails 5.2のcredential機能をKubernetesで使う(翻訳)

Rails 5.2で導入された新しいcredentials機能で遊んでみた結果、これが実にクールだと思えました。暗号化されたcredential(secretとも呼ばれます)をリポジトリに保存しておき、読み出す必要が生じたらマスターキーで暗号化を解除できます。

Rails 5.1で導入されたsecrets.ymlとの違いを知りたい方は、「Rails Encrypted Credentials on Rails 5.2」をご覧ください。

RailsのアプローチはShopifyのejsonと非常に似通っていますが、ejsonのような非対称暗号化を採用していない点が異なります。

新しいcredential管理は、Kubernetes上で動作するコンテナ化Railsアプリでどのように機能するでしょうか。結論から言うと、驚くほどスムースでした

ロケット科学のような難しいセットアップは不要です。Rails 5.2 credentialのデプロイがどれほど簡単であるかを本記事でご説明いたします。

$ gem install --pre rails

$ rails -v
Rails 5.2.0.rc2

$ rails new secretland --skip-javascript --skip-spring --skip-coffee --skip-turbolinks --skip-action-cable

$ bin/rails credentials:edit
# 暗号化済みcredentialをvimで開く

$ cat config/master.key
3bed2fdcb0261e6f48850de01a85fb5b
# このアプリのcredential向けマスターキーは.gitignoreにも記載されるのでgitにpushされない

お次はコンテナをビルドしましょう。まず、.dockerignoreファイルにマスターキーを追加し、コンテナにうっかり置かれないようにします(このキーをコンテナレジストリに公開したくありません)。

$ echo config/master.key > .dockerignore

コンテナのビルドには、こちらの最小限のDockerfileを用います。

FROM ruby:2.5

RUN mkdir -p /app
WORKDIR /app

ENV RAILS_ENV production
ENV RAILS_SERVE_STATIC_FILES true
ENV RAILS_LOG_TO_STDOUT true

COPY Gemfile /app/
COPY Gemfile.lock /app/
RUN bundle config --global frozen 1
RUN bundle install --without development test

COPY . /app

EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
$ docker build -t kirshatrov/secretland:v1 .

このマスターキーを環境変数として実行します。

$ docker run -i -t -p 3000:3000 -e RAILS_MASTER_KEY=3bed2fdcb0261e6f48850de01a85fb5b kirshatrov/secretland:v1

大事な秘密情報をうっかり出力する残念なコントローラを書くと、以下のようになってしまいます。

このコンテナをKubenetesノードからダウンロードおよび実行できるよう、Dockerレジストリにpushします。

$ docker push kirshatrov/secretland:v1

Kubernetesリソースを作成する前に、そのための秘密情報の作成が必要です(私の場合Kubenetesの秘密情報は実際にはこれが初めてでしたが)。

$ kubectl create secret generic secretland-secrets --from-literal=rails-master-key=3bed2fdcb0261e6f48850de01a85fb5b
secret "secretland-secrets" created

$ kubectl describe secret secretland-secrets
Name:         secretland-secrets
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
rails-master-key:  32 bytes

Deploymentのspecは次のとおりです。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: secretland
  labels:
    app: secretland
spec:
  selector:
    matchLabels:
      app: secretland
  template:
    metadata:
      labels:
        app: secretland
    spec:
      containers:
      - image: kirshatrov/secretland:v1
        name: rails
        ports:
        - containerPort: 3000
        env:
          - name: RAILS_MASTER_KEY
            valueFrom:
              secretKeyRef:
                name: secretland-secrets
                key: rails-master-key

ここにトリックが潜んでいます。環境変数(RAILS_MASTER_KEY)の設定には、事前に作成した秘密情報の値から取り出したものを使っています。こうすることで秘密情報をDeploymentから切り離し、Deploymentリソースにマスターキーが漏洩することを回避できます。このYAMLをDeployment specと一緒にアプリのリポジトリにpushすることすら可能です。

追記: 本記事コメントでVictorが指摘したように、config/master.keyにファイルとして秘密情報キーをマウントする方がよいかもしれません。これにより、Railsは環境変数ではなくこの情報を使うようになります。この場合、YAMLのspec.templateの部分は以下のようになります。

spec:
  containers:
  - image: kirshatrov/secretland:v1
    name: rails
    ports:
    - containerPort: 3000
  volumes:
  - name: secrets
    secret:
      secretName: secretland-secrets
      items:
      - key: rails-master-key
        path: /app/config/master.key

このDeploymentを適用してインターネット上に公開します。

$ kubectl apply -f deployment.yml

$ kubectl expose deployment secretland --type=LoadBalancer --port=80 --target-port=3000

見事に動きました!

本記事で用いたコードはGitHubのkirs/secretlandリポジトリでご覧いただけます。

正直、こんなに何もかもがスムースに行くとは想像もできませんでした。Rails 5.2のcredential管理はコンテナ化されたアプリでも実にうまくいきますし、Kubernetesへの秘密情報のpushもコマンド一発で完了します。

今後は、このcredential情報を編集してからbin/rails credentials:editgit pushを実行するだけでproduction環境を更新できるでしょう。

本ブログの更新情報については、Twitterで@kirshatrovをフォローして下さい。

関連記事

minikubeでDockerコンテナをクラスタ構成した運用とDockerコンテナを手作業で地道に管理の運用とどっちが楽か検証しながらまとめてみた

Kubernetesでのデプロイ中に’db:migrate’や’db:seed’などのRailsタスクを管理する(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。