エンジニア4年目になりました、中島です。
今回aws-privateca-issuerについて調べる機会がありましたので、備忘録もかねて記事を書きました。
拙い内容ですが、お付き合いいただけると幸いです。
aws-private-issuerって?
まず前提として、Kubernetes内で使用するtls通信用の証明書を管理するコンポーネントとして、cert-managerというものが存在します。
aws-privateca-issuerは、そのcert-managerにおいて
aws-private-caサービスをCAとして使う際に証明書リクエストをやってくれる拡張機能です。
証明書はsecretとして保存されて各Pod, Ingressによって参照されるため、証明書の定期更新を完全に自動化できます。
やること
前提:Kubernetes v1.34
Nginx PodとIngress作る
本筋じゃないのでざっくりと。
Ingres Controllerインストール
今回nginx
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.14.1/deploy/static/provider/cloud/deploy.yamnginxが動くPodを作成
apiVersion: v1
kind: Pod
metadata:
name: test-web
labels:
app: test-web
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80Service(ClusterIP)を作成して内部Networkで公開
apiVersion: v1
kind: Service
metadata:
name: test-service
spec:
type: NodePort
selector:
app: test-web
ports:
- protocol: TCP
port: 80
targetPort: 80リクエストをServiceへ転送するIngress作成
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts:
- example.local
rules:
- host: example.local
http:
paths:
- path: /top_page
pathType: Prefix
backend:
service:
name: test-service
port:
number: 80外部からアクセスするためのIP,Portを確認
$ kubectl get service -n ingress-nginxhttps://<NodeのIP>: 30238 でアクセス可能だと分かります。
TLSを使用する都合、URLにIPアドレスべた書きだと都合が悪いため、ClientPCのhostsファイル(windowsの場合C:\Windows\System32\drivers\etc\hosts)を編集して適当な名前(example.local)を付けてあげます。
<IPアドレス> example.localこれでアクセス可能に!
デフォルトだとIngressがフェイクの証明書を提示するため、警告が出ます。
Cert-managerセッティング
Helmでcert-managerインストール
helm install \
cert-manager oci://quay.io/jetstack/charts/cert-manager \
--version v1.19.2 \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=trueこれでcert-manager名前空間にPodが立ち
Certificate, Certificaterequestsといったカスタムリソースも使えるようになります。
AWS Private CAをセッティング
そのアカウントで最初に作成したPrivate CAに限り、30日間のトライアル期間が存在する模様。ただし証明書発行するたびの課金は発生するようです。
(詳細はこちら)
適当な情報を入れて作成。ARNを控えておきます。
また、ActionsボタンからCA証明書をダウンロードできるため、Client PCのブラウザにImportしておきます。
aws-privateca-issuerが使う認証情報をセッティング
※最近はIAM Anywhereを使った、secret_keyをトラフィックに流さない方式が推奨されているようです。今回は構成を分かりやすくするため、IAMユーザを作ってACCESS KEYをissuerに渡す方式を使います。AWS anywhereは、、そのうち記事にするかも…
IAMポリシー作成
以下が最小権限のようです。適当な名前で保存しましょう。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "awspcaissuer",
"Action": [
"acm-pca:DescribeCertificateAuthority",
"acm-pca:GetCertificate",
"acm-pca:IssueCertificate"
],
"Effect": "Allow",
"Resource": "<作成したprivate caのARN>"
}
]
}IAMユーザ作成
先ほど作成したIAMポリシーを直接アタッチして適当なユーザを作成
→アクセスキーを作成し、IDとSECRETを控えます。
※当然非推奨。本番環境では避けましょう。
aws-privateca-issuerのインストール
helmでaws-private-issuerをインストール
helm repo add awspca https://cert-manager.github.io/aws-privateca-issuer
helm install awspca/aws-privateca-issuer --generate-nameこれでdefault名前空間にaws-privateca-issuerのPodが立ちます。
Secret作成
IssuerがprivatecaにアクセスするためのSECRET
apiVersion: v1
kind: Secret
metadata:
name: pca-secret
namespace: default
data:
AWS_ACCESS_KEY_ID: <base64_encoded_access_key_id>
AWS_SECRET_ACCESS_KEY: <base64_encoded_secret_access_key>
※base64エンコードする際は以下コマンドを使います。
私は -n オプションを忘れたせいで半日溶けました。
$ echo -n "<encodeしたい文字列>" |base64Secretを参照するAWSPCAIssuerカスタムリソースを作成
apiVersion: awspca.cert-manager.io/v1beta1
kind: AWSPCAIssuer
metadata:
name: pca-issuer-rsa
namespace: default
spec:
arn: "<aws private caのARN>"
region: <region>
secretRef:
namespace: default
name: pca-secretAWSPCAIssuerとAWSPCAClusterIssuerの違いは、その名前空間のリソースかグローバルなリソースかというだけなので、どちらでもOKです。
Describeして認証が通っていることを確認
$ kubectl describe awspcaissuer証明書を発行する
証明書を発行するには、Certificateというカスタムリソースを作成し、そこでCSRに必要な情報を定義します。
以下、最小構成。
kind: Certificate
apiVersion: cert-manager.io/v1
metadata:
name: test-cert
namespace: default
spec:
#なんでもOK
commonName: Nara
#httpsアクセス時のurlのホスト名と照合するため、クライアントのhostsファイルに記載したホスト名
dnsNames:
- example.local
#有効期限
duration: 72h0m0s
issuerRef:
group: awspca.cert-manager.io
kind: AWSPCAIssuer
name: pca-issuer-rsa
#有効期限が切れるどれくらい前に証明書を再発行するか
renewBefore: 24h0m0s
#証明書のoutput先のsecret名
secretName: tls-test-secret
usages:
- server auth
#AWS Private CA側で定義したアルゴリズムを指定
privateKey:
algorithm: "RSA"
size: 2048READYがTrueになっていれば大丈夫です!
$ kubectl get certificateそしてサーバ証明書入りのsecretが作られているはず…
$ kubectl get secret tls-test-secret無事、作成されていました!
トラブルシューティング
上手くいかないときのチェック項目は以下です。
・Aws-private-issuer Podのログ
AWSとの疎通の過程で問題があれば、大体こちらにエラーを吐きます。
$ kubectl logs <Pod名>・CSRに問題がある場合
certificaterequest リソースのeventに何か書いてあるかも…
$ kubectl describe certificaterequest・Certificateオブジェクトのevent
こちらも一応確認します。
$ kubectl describe certificateIngressにサーバ証明書を読み込ませる
Spec.tls.hosts[].secretNameに参照先のSecretを入れるだけです。
サーバ証明書はIngressが読み込める形式で出力されているため、一発でした。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts:
- example.local
secretName: tls-test-secret
rules:
- host: example.local
http:
paths:
- path: /top_page
pathType: Prefix
backend:
service:
name: test-service
port:
number: 80
何故かエラー出てるけど、証明書の検証は成功してくれました…
エラー出てるけど。
FireFoxだとちゃんとHTTPS通信してくれました。何故でしょうか…
クリーニング
クリーニング対象は以下です。
特にAWS Private CAは高価なので、早急に削除しましょう!
- 作成したAWS Private CA
- 作成したIAM ポリシー
- 作成したIAMユーザとアクセスキー
- Kubernetesのリソース(作成した際と逆の順番でクリーニング)
あとがき
cert-managerの挙動含め、良い勉強になりました。
簡単に証明書周りを管理できてとっても便利だね、といった感想です。
以上、ご一読いただきありがとうございました。
参照情報
参考にしたAWSドキュメント
https://docs.aws.amazon.com/ja_jp/privateca/latest/userguide/PcaKubernetes.html
cert-manager
https://cert-manager.io/docs/
AWS Private CA Issuer
https://github.com/cert-manager/aws-privateca-issuer