Oracle Functions で Let's Encrypt を使用した、自動SSL証明書更新をつくってみた


はじめに

Oracle Functions を使用して、自動的にSSL証明書を発行する自動化(oci-lego-sslupdate)を作りました。
Let's Encrypt からSSL証明書を発行し、OCIのLoadBalancerに設定し、ObjectStorageへ保存することが出来ます。
Oracle Functions を使用しているので、ComputeInstanceを必要としないサーバレスなアーキテクチャとなっています。

この記事では、使い方と、技術的な解説を書きます。
作成した自動化(oci-lego-sslupdate)は、GItHubにアップロードしているので、適宜ご利用ください。
https://github.com/Sugi275/oci-lego-sslupdate

概要

  • OCIR (Docker Registry) にアップロードしている、自動化用の DockerImage を Oracle Functions に Pull
  • Fn 上でDockerImageを実行
  • Let's Encryptから新たなSSL証明書を取得
  • OCIのLoadBalancerに、新たなSSL証明書を設定
  • OCIのObjectStorageに、新たなSSL証明書と、SSL証明書に紐づく秘密鍵を保存

使い方

前提条件

  • 取得したいSSL証明書のドメインを、OCIのDNSにあるZoneで管理していること。設定方法例はこちら を参照 (Let's Encryptの認証方式をDNS-01としているため)

  • OCI上にLoadBalancerを作成して、SSL用のListenerを作成していること

  • Oracle Functions を使用可能なこと。設定方法はこちらを参照

oci-lego-sslupdateを Oracle Functions に設定

Gitからoci-lego-sslupdateをclone

git clone https://github.com/Sugi275/oci-lego-sslupdate.git

Oracle Functions 用の設定ファイルを、必要に応じて変更します

> cat func.yaml
schema_version: 20180708
name: oci-lego-sslupdate     <=== Functionの名前を変更したい場合は変更する
version: 0.0.20              <=== バージョンも変更したい場合は変更する
runtime: docker
entrypoint: ./func
format: http-stream
timeout: 120

Oracle Functions のコンソール画面で、適当なアプリケーション(枠組み)を作成します。今回の例では、env-app という名前を使用しています。
アプリケーションを作成後、fn deploy コマンドで、Oracle Functions にデプロイします

fn --verbose deploy --app env-app

function 一覧を確認します。oci-lego-sslupdate が存在していることを確認します

> fn ls functions env-app
NAME                    IMAGE                                                                   ID
custom_runtime          phx.ocir.io/<tenancy>/oracle-function/custom_runtime:0.0.2             ocid1.fnfunc.oc1.phx.aaaaaaaaadizxuar3gv6ubeoztffm5l2nfnujna6vbnkc67g7td2c722jg2a
env-print               phx.ocir.io/<tenancy>/oracle-function/env-print:0.0.7                  ocid1.fnfunc.oc1.phx.aaaaaaaaadu6qtn6zyn754zbiqi3boavtunsdbkwfllggixg7hkwjihfo5ma
oci-lb-list             phx.ocir.io/<tenancy>/oracle-function/oci-lb-list:0.0.24               ocid1.fnfunc.oc1.phx.aaaaaaaaaaialw3wgcnm2ytn7cveaaqmlmtaniilu3qy7k2bmxkbn2tgnpiq
oci-lego-sslupdate      phx.ocir.io/<tenancy>/oracle-function/oci-lego-sslupdate:0.0.21        ocid1.fnfunc.oc1.phx.aaaaaaaaaaz4bvn4oz2sp4d3zzfuaefkui2be7b5vp4cgayrdmltj6f7yeuq <=== 表示されていればおk
test                    phx.ocir.io/<tenancy>/oracle-function/test:0.0.6                       ocid1.fnfunc.oc1.phx.aaaaaaaaaatajmkauwajgxbjjm2p23kiu2do7nz76psxcldwv5w2palfatpq

function に環境変数を設定します。以下に例を記載します。適宜環境に合わせて変更をする必要があります
bashの場合

# oci
export OCI_PRIVKEY_BASE64=`base64 $HOME/.oci/oci_api_key.pem`
fn config func env-app oci-lego-sslupdate OCI_PRIVKEY_BASE64 $OCI_PRIVKEY_BASE64
# fnでは値が空白だと、反映されない。パスフレーズを指定する場合はコメントアウトを外して指定
# fn config func env-app oci-lego-sslupdate OCI_PRIVKEY_PASS ""
fn config func env-app oci-lego-sslupdate OCI_TENANCY_OCID 'ocid1.tenancy.oc1..secret'
fn config func env-app oci-lego-sslupdate OCI_USER_OCID 'ocid1.user.oc1..secret'
fn config func env-app oci-lego-sslupdate OCI_PUBKEY_FINGERPRINT '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00'
fn config func env-app oci-lego-sslupdate OCI_REGION 'us-phoenix-1'
fn config func env-app oci-lego-sslupdate OCI_COMPARTMENT_OCID 'ocid1.tenancy.oc1..secret'

# lego (let's encrypt client)
fn config func env-app oci-lego-sslupdate OCI_TTL 1
fn config func env-app oci-lego-sslupdate OCI_PROPAGATION_TIMEOUT 120
fn config func env-app oci-lego-sslupdate OCI_POLLING_INTERVAL 10
fn config func env-app oci-lego-sslupdate LETSENCRYPT_DOMAINS 'sugi.tokyo,*.sugi.tokyo'
fn config func env-app oci-lego-sslupdate LETSENCRYPT_MY_MAILADDRESS '[email protected]'
# fn config func env-app oci-lego-sslupdate LETSENCRYPT_CA_URL 'https://acme-staging-v02.api.letsencrypt.org/directory'

# oci lb 
fn config func env-app oci-lego-sslupdate OCI_LB_OCID 'ocid1.loadbalancer.oc1.phx.secret'
fn config func env-app oci-lego-sslupdate OCI_LISTENERS 'listener1,listener2'

# oci ObjectStorage
fn config func env-app oci-lego-sslupdate OCI_OS_NAMESPACE '<tenancy>'
fn config func env-app oci-lego-sslupdate OCI_OS_BUCKETNAME 'lego-certificate4'

fishの場合は、若干書式を変更する必要があります

# oci
set IFS
set -x OCI_PRIVKEY_BASE64 (base64 $HOME/.oci/oci_api_key.pem)
set IFS \n" "\t
# fnでは値が空白だと、反映されない。パスフレーズを指定する場合はコメントアウトを外して指定
# fn config func env-app oci-lego-sslupdate OCI_PRIVKEY_PASS ""
fn config func env-app oci-lego-sslupdate OCI_PRIVKEY_BASE64 $OCI_PRIVKEY_BASE64
fn config func env-app oci-lego-sslupdate OCI_TENANCY_OCID 'ocid1.tenancy.oc1..secret'
fn config func env-app oci-lego-sslupdate OCI_USER_OCID 'ocid1.user.oc1..secret'
fn config func env-app oci-lego-sslupdate OCI_PUBKEY_FINGERPRINT '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00'
fn config func env-app oci-lego-sslupdate OCI_REGION 'us-phoenix-1'
fn config func env-app oci-lego-sslupdate OCI_COMPARTMENT_OCID 'ocid1.tenancy.oc1..secret'

# lego (let's encrypt client)
fn config func env-app oci-lego-sslupdate OCI_TTL 1
fn config func env-app oci-lego-sslupdate OCI_PROPAGATION_TIMEOUT 120
fn config func env-app oci-lego-sslupdate OCI_POLLING_INTERVAL 10
fn config func env-app oci-lego-sslupdate LETSENCRYPT_DOMAINS 'sugi.tokyo,*.sugi.tokyo'
fn config func env-app oci-lego-sslupdate LETSENCRYPT_MY_MAILADDRESS '[email protected]'
# fn config func env-app oci-lego-sslupdate LETSENCRYPT_CA_URL 'https://acme-staging-v02.api.letsencrypt.org/directory'

# oci lb 
fn config func env-app oci-lego-sslupdate OCI_LB_OCID 'ocid1.loadbalancer.oc1.phx.secret'
fn config func env-app oci-lego-sslupdate OCI_LISTENERS 'listener1,listener2'

# oci ObjectStorage
fn config func env-app oci-lego-sslupdate OCI_OS_NAMESPACE '<tenancy>'
fn config func env-app oci-lego-sslupdate OCI_OS_BUCKETNAME 'lego-certificate4'

環境変数について

各環境変数の説明を記載します

Key 内容 必須 or 任意 Default値
OCI_PRIVKEY_BASE64 OCIに接続するための秘密鍵をbase64でエンコードした文字列 必須 なし
OCI_PRIVKEY_PASS 秘密鍵のパスフレーズ 必須 なし
OCI_TENANCY_OCID 操作するテナンシーのOCID 必須 なし
OCI_USER_OCID OCIで公開鍵を設定した、ユーザのOCID 必須 なし
OCI_PUBKEY_FINGERPRINT 公開鍵のフィンガープリント 必須 なし
OCI_REGION リージョン名 必須 なし
OCI_COMPARTMENT_OCID コンパートメントのOCID 必須 なし
OCI_TTL Let's Encryptでdns-01認証をする際に、一時的に作成するレコードのTTL 任意 30
OCI_PROPAGATION_TIMEOUT Let's Encryptでdns-01認証をするときの、タイムアウト 任意 60
OCI_POLLING_INTERVAL Let's Encryptでdns-01認証をするときの、インターバル 任意 2
LETSENCRYPT_DOMAINS SSL証明書を取得したいドメイン名。カンマ区切りで複数指定可能 必須 なし
LETSENCRYPT_MY_MAILADDRESS SSL証明書に紐づくメールアドレス 必須 なし
LETSENCRYPT_CA_URL Let's Encrypt を始めとしたCA URLを指定。動作確認をするときには、stagingを指定すると便利 任意 https://acme-v02.api.letsencrypt.org/directory
OCI_LB_OCID SSL証明書を設定したいLoadBalancerのOCID 必須 なし
OCI_LISTENERS SSL証明書を設定したいLoadBanancerに所属している、ListenerのOCID 必須 なし
OCI_OS_NAMESPACE ObjectStorageのネームスペース名 必須 なし
OCI_OS_BUCKETNAME ObjectStorageにSSL証明書や秘密鍵を保存するときのバケット名。指定したバケットが存在しない場合は、自動的にPrivateバケットを作成 任意 lego-cert

loggingの設定 (任意)

現在、Oracle Functions は Limited Available となっており、logを確認したい場合は、logging用のsyslogを用意する必要があります。
Papertrail というサービスを使うことで、簡単に無料でsyslogを表示することが出来ます。
設定方法などは、こちらを参照してください。

今回の自動SSL設定は、2分くらい実行時間が必要なので、loggingしておくと進捗がわかって便利です。

Functionの実行

fn invoke コマンドで、Functionを実行できます。実行してから2分弱ほど経過すると、成功した旨のメッセージが表示されます。
また、実行中の経過はloggingを確認するとよいです。

fn invoke env-app oci-lego-sslupdate

なお、現在のOracle Functions では、定期実行することは制限されていて出来ませんが、GAされれば定期実行できるため、非常にお手軽にSSL証明書を自動的に更新し続けることが出来ます。