direnvでNodeのバージョン切り替えを自動化する、ための.envrcを作成するスクリプト

参考:direnvでプロジェクトごとのnodeのバージョンを切り替える

 参加しているフロントエンドプロジェクトでは、使用するNodeのバージョンがpackage.jsonのengines.nodeに記述されている。
"engines": {

"node": "12.16.3"
}

 不等号などは使わず、単一バージョンで指定されている。これがプロジェクトごとに異なっており、切り替えが非常に面倒なので自動化したく↑にある記事を参考にした。

 そのあとに.nvmrcを書くのも面倒になってpackage.jsonのenginesから読み取るようにし、.envrcをコマンドで作成されるようにした。その結果物↓。
https://gist.github.com/hMatoba/f13f91813e66c1d11d4246902a7f05f7

 使い方は、必要なパッケージを入れてから、
npx https://gist.github.com/hMatoba/f13f91813e66c1d11d4246902a7f05f7




 package.jsonのengines.nodeのバージョン表記が単一バージョン指定であることのみを想定して書いている。本来ならそこに不等号などを使った指定記述もあるがそれには対応していない。
comment: 0

TypeScriptのOptional ChainingとNullish Coalescingを利用して、オブジェクトの末端ノードにキーがあるかチェックする

 自作ライブラリの中で、オブジェクトの子や孫などのノードがプロパティを持っているかチェックするコードがあった。
"bar" in foo && Object.keys(foo.bar).length

 ノードが子、孫、ひ孫と深くなるたびにnullガードが増えるのでしんどいコードになっていく。こいつをTypeScript 3.7で導入されたOptional ChainingNull Coalescingでスリムに書こうというところ。

 まずOptional Chainingを使うと、foo?.barと書いたときにオブジェクトfooにプロパティbarが未定義ならnullを返してくれる。foo?.bar?.doomとつなげていっても、途中で未定義プロパティになった時点でnullを返してくれる。doomまで定義されていればdoomの値を返してくれる。↑に出てきたコードに適用する。
Object.keys(foo?.bar).length

nullガードが外れた。しかしbarが未定義のとき、Object.keysにnullを渡ってしまいエラーになるので次に行く。

 Null Coalescingを使うと、"v1 ?? v2"と書いたとき、v1がnullならv2を返してくれる。三項演算子を使うようなケースをちょっと短くかける形。これをまた↑のコードに適用し、nullのときは空オブジェクトを返すようにする。
Object.keys(foo?.bar ?? {}).length

これでエラーにはならず、目的どおりにオブジェクトの末端ノードがプロパティを持っているかチェックできる。ノードを深くする手間も少なめ。
Object.keys(foo?.bar?.doom?.doon ?? {}).length

comment: 0

このブログのエンジンの.NET Coreを3.1に上げた

 祝!.NET Core 3.1(LTS)リリース。このブログも.NET Coreにお世話になっているのでさっそくバージョンアップをかけた。3.0 -> 3.1。

 公式の移行ドキュメントを読んでGUIでバージョンアップをかけつつ。


 Docker Hubで新しいイメージを確認しつつ。

 Snapを使った.NET Core SDKのインストールの仕方が変わらないことを確認しつつ。

 で、上記三つを終えてCIを走らせて、正常終了したので更新を終えた。ごく簡単な更新だった。
3.1へのバージョンアップコミット
comment: 0

cert-managerのバージョンを0.5から0.12まで上げた

 このブログはHTTPSで配信している。証明書はLet's Encryptを使っているが、Kubernetesでそこをよしなにやってくれるcert-managerを使っている。
 cert-managerが”ある状況下で証明書発行リクエストを送り過ぎる”ということがあったため、0.8未満のバージョンの発行リクエストが受け付けられなくなった。そのため証明書自動更新が働かず、証明書期限が近付いていた。なのでcert-managerのバージョンアップを行った。


 最初はcert-manager公式を読んで一つずつバージョンを上げていこうとしたが、0.6→0.7をやろうとしたところでエラーが出たので方法を切り替えた。

 端的にいえば、cert-managerを入れて、あとはClusterIssuerとCertificateを定義して、Secretへ証明書を吐き出しているというのが稼動中の状況。証明書が入っているSecretがおかしな内容に変更されない限りは正常に配信が続けられると判断して、まずcert-managerをアンインストールし、ClusterIssuerとCertificateも消した。
 そして新しくcert-managerの0.12を入れ、ClusterIssuerとCertificateも新しいSecretへ証明書を出力するように変更し、Kubernetesへ適用した。新しいSecretが出力されていることを確認し、Ingressで読み込むSecretを変更して証明書更新を行った。
というわけで以下詳細。

cert-manager0.12のインストール
kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.12/deploy/manifests/00-crds.yaml

kubectl create namespace cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install --name cert-manager --namespace cert-manager --version v0.12.0 jetstack/cert-manager


IssuerCertificate
apiVersion: cert-manager.io/v1alpha2

kind: ClusterIssuer
metadata:
name: tetsujin-issuer
spec:
acme:
email: ---
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: blog-tetsujin-net-tls
solvers:
- http01:
ingress:
class: nginx


apiVersion: cert-manager.io/v1alpha2

kind: Certificate
metadata:
name: blog-tetsujin-net
namespace: tetsujin
spec:
secretName: blog-tetsujin-net-tls
duration: 2160h # 90d
renewBefore: 360h # 15d
dnsNames:
- blog.hmatoba.net
issuerRef:
name: tetsujin-issuer
kind: ClusterIssuer


 上記リソースを適用して、新しいSecretが出力されているのを確認できたら、ingress設定を変えた。
  tls:

- hosts:
- blog.hmatoba.net
- secretName: tls-secret
+ secretName: blog-tetsujin-net-tls


 以上の手順で、cert-managerのバージョンアップを終えることができた。
comment: 0

このブログのCSRF対策を考え直した

 このブログはASP.NET Core MVCで作ってある。そんでもっていまはKubernetesでPodが二つになるようにスケールしている。開発時からからCSRF対策を入れてある、ASP.NET Core MVCのデフォで入れられるものを。コントローラのメソッドにトークン確認のための属性をつけて、ビューでトークンを仕込むメソッドを置くだけだった。
 Kubernetesで動かす前はコンテナホスティングサービスで複数台スケールさせずに動かしていた。その頃は問題なく記事の投稿ができていた。
 後にKubernetesでスケールさせたとき、記事の投稿でエラーが出るようになった。しばらく考えたのちに、CSRFトークンをサーバでメモリ上に持っていて照合に使っていないかと疑い、Podを一台に減らして投稿を試したところ成功した。どうやらメモリ上にもCSRFトークンを持ち、照合していたんだろう。フォームの隠し要素とクッキーにも入っていたが三点チェックなのか?

 ともあれ、このままではサーバのスケールができない仕組み。スケールさせる必要はないんだけど、自分で作ったからにはスケールさせられるようにしておきたい。
 トークンチェックのメソッドを変更するかーと思った。だが最近、CSRFの対策って増えてたなと思い出した。クッキーのSameSite属性とかLocalStorageとか。

 LocalStorageは個人的にはフロントエンドをやってる人がよく使おうとする印象がある。ここにセッショントークンを入れておけばJSを使わないと呼び出せないのでCSRFは起こせない。
 クッキーを使っているならSameSite属性を使う手も出てきている。このブログのような個人のものなら、外部ドメインからのアクセスに対してセッショントークンを使わせる必要はまったくない。なのでSameSite属性の値として、Strictをつけておけばいいだろう。この方法を取れば、改修はセッショントークンにSameSite属性をStrictでつけること、既存のCSRF対策を外すことの二点で済む。

 そういうわけで、CSRF対策を、セッショントークンのクッキーにSameSite属性をつける方法に変更した。

https://github.com/hMatoba/tetsujin/commit/bcf1334633fbf4c17df7b4dd8a359c3c6a5721e2
comment: 0