いつもは OS アップグレードしていたので気づかなかったですが、最近の Ubuntu はネットワークインターフェース設定が netplan というユーティリティに標準は置き換わっています。そのため、その設定の紹介になります。
さくら VPS のコントロールパネルで、スイッチを追加して、各サーバーのネットワーク接続画面でそれぞれ追加したスイッチに接続させます。(なお、現状はサーバーはシャットダウンしておかないとこの適用できません。)
1 | $ ip addr show |
でネットワークインターフェースを確認します。
1 | 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 |
今回は ens で始まる物理インターフェイスの2番目の ens4 をスイッチに接続したものとしてセットアップします。
1 | $ cat /etc/netplan/01-netcfg.yaml |
で現在の設定を確認します。
1 | # This file describes the network interfaces available on your system |
ens3 がデフォルトで存在します。ここに ens4 を追加します。スイッチに接続するということは、同一ネットワークアドレスを持っていればそれだけで通信できます。
よって至ってシンプルです。ここではクラス C のネットワークアドレス 192.168.200.0/24
でサーバーにIPアドレス 192.168.200.10
を付与します。
1 | # This file describes the network interfaces available on your system |
root 権限で最後の2行を追記しました。
netplan コマンドには try と apply サブコマンドがあります。
try は適用までに猶予があり、メイン側のインターフェースを変更する場合などで SSH 越しにターミナル操作を行っており失敗した場合に自動で revert するものです
1 | # netplan try |
タイムアウトするまでに Enter の入力しない設定が更新されません。
今回は 2番目のインターフェースを追記しただけなので即時反映の apply でもいいでしょう。
1 | # netplan apply |
先に設定済みのサーバーから ping 192.168.200.10
で疎通を確認します。
1 | # ufw allow from 192.168.200.0/24 |
とするとローカルネットワーク間の通信が全て許可されます。
役割に応じてサーバーを複数運用している場合は、それぞれ必要なポートとクライアントのアドレス空間だけを許可する方が良いでしょう。
例えば、ローカルネットワークとしてはネットワークアドレス 192.168.200.0/24 として設定し、アプリケーションサーバーにはネットワークアドレス 192.168.200.0/25 として 192.168.200.1 ~ 192.168.200.126 を割り当てて、データベースサーバーにはネットワークアドレス 192.168.200.128/25 として 192.168.200.129 ~ 192.168.200.254 を割り当ててます。
そしてデータベースサーバー内でアプリケーションサーバー 192.168.200.128/25 からの通信を許可すると必要最小限になります。
]]>JWT のメリットはアクセストークンそのものが有効期間やコンテクストを保持できるため、個別にリクエストごとにセッションIDからセッション情報を引くという機構を省くことができます。そのため、自前の API サーバにおいて、アクセストークンとして JWT の ID トークンを使えるとサーバ処理を省力化できます。
今回の例では、JWT の検証をグローバルにできるようにはせず、サーバ内でのみ行うこととします。一般的な JWT トークンは JSON Web Key (JWK) Set から提供されており、JWT の署名検証ができるようになっていますが、それは行いません。
Auth0 がものすごく便利なライブラリを提供してくれています。
https://github.com/auth0/node-jsonwebtoken
1 | $ npm i -save jsonwebtoken |
公開鍵と秘密鍵を別々にすることも可能ですが、分けると管理が面倒なのと同一サーバ内で処理するため、PEMファイルで管理します。
1 | $ openssl genrsa > auth.pem |
1 | import fs from 'fs' |
TokenExpiredError: jwt expired
がスローされます。TokenExpiredError: maxAge exceeded
がスローされます。1 | { |
ログイン成功時に issueToken を呼んで ID トークンをクライアントに発行します。クライアントは API リクエストの Authorization ヘッダに付与して、サーバ側は verifyToken で検証することで、認証および subject(sub)フィールドなどからユーザー情報を取得することができます。
]]>コントロールパネルから カスタムOSインストール Ubuntu 18.04 を行います。
途中の「Partition disks」で Guided - use entire disk and set up encrypted lvm を選択します。そしてパスフレーズを設定します。
VPSのコントロールパネルで緑の起動ボタンをクリックして、さらにコンソールボタンメニューのシリアルコンソールも即座に開いておきます。
シリアルコンソール画面がパスフレーズ入力で止まるので、ここで先ほどのパスフレーズを入力して起動させます。
1 | # cryptsetup status /dev/mapper/vda5_crypt |
現状は起動時にパスフレーズを入力するしかありません。ここにさらに別途鍵ファイルをbootパーティション内に追加して自動起動するようにします。
ここからは Debian/Ubuntuで暗号化 LVM を使いつつ自動起動する - @znz blog の受け売りが大半です。大変参考になりました。ありがとうございます。
/boot/keyfile
として鍵ファイルを作成します。
1 | # touch /boot/keyfile |
1 | # cryptsetup -v luksAddKey /dev/vda5 /boot/keyfile |
1 | # df |
1 | # ls -l /dev/disk/by-uuid |
/etc/crypttab
を書き換えます。
1 | vda5_crypt UUID=00000000-0000-0000-0000-000000000005 none luks,discard |
を下記のように、none と discard を置き換えます。
1 | vda5_crypt UUID=00000000-0000-0000-0000-000000000005 /dev/disk/by-uuid/00000000-0000-0000-0000-000000000001:/keyfile luks,keyscript=/lib/cryptsetup/scripts/passdev |
1 | # update-initramfs -u |
このままリブートすると途中 boot パーティションの準備ができるまで待ち状態になって、タイムアウトまで90秒も起動プロセスが止まってしまう問題があります。
そのためタイムアウトを短くします。 /etc/systemd/system.conf
の [Manager] セクションの DefaultTimeoutStartSec を有効にして編集します。
1 | #DefaultTimeoutStopSec=90s |
OS を再起動します。
正常に起動しない場合は、シリアルコンソールから編集する必要があります。
下記のように LUKS の適用状態がわかります。
1 | # cryptsetup luksDump /dev/vda5 |
厳密にはキー自体が生で保存されているため、機密が完全とは言い難いです。単にディスクをRAW読み取りして、データが見えたりしないようにするのが、この対応の目的となります。
元々データディスク(セカンドドライブ)として使っていた SSD を、新しい PC のシステムディスクとしてデータをすべてフォーマットせずに流用する場合の方法です。本来は一旦別の HDD 等にデータを移動しておくべきですが時間がかかるので、データが大きいのと空き容量がまだので既存データを後方に移動して、前方にシステムディスクとしてのパーティション構成を構築します。
EaseUS Partition Master Free をインストールします。
このソフトウェアを使うと、パーティションのサイズ変更が可能になります。それで前方に空きが出るようにパーティションを移動変更します。なお予約済みパーティションなどが先頭にあった場合は削除します。
続いて diskpart コマンドを使います。管理者権限で起動した PowerShell などで diskpart を起動します。
UEFI/GPT ベースのハードドライブパーティション を参考に構築します。
パーティション構成は先頭から下記のようにします。
list disk
でディスクを一覧します。 select disk 1
で編集するディスクを選択します(ここではディスク1)。list partition
でパーティションを一覧します。この時点でデータパーティションだけ存在するはずです。
create partition efi size=100
でパーティションを作成します。続けて format quick fs=fat32 label="System"
としてフォーマットします。
create partition msr size=16
でパーティションを作成します。フォーマットは不要です。
create partition primary size=768
でパーティションを作成します。続けて format quick fs=ntfs label="Recovery tools"
としてフォーマットします。このパーティションは、型 ID: DE94BBA4-06D1-4D40-A16A-BFD50179D6AC を使用する必要があるため set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac"
とします。さらにGPT属性に gpt attributes=0x8000000000000001
として 0X8000000000000001 をセットします。
create partition primary size
でパーティションを作成します。続けて format quick fs=ntfs label="Windows"
としてフォーマットします。
編集した SSD を装着した新しい PC に対して Windows 10 をインストールします。「カスタム:Windows のみをインストールする」を選択して、インストール場所で先に作った Windows パーティションを選択して進めます。Windows パーティションが C ドライブに、データパーティションが D ドライブとなります。
]]>Let’s Encrypt は certbot ツールが便利なのでこちらをインストールします。
SSL 証明書の発行には、ドメイン認証のために http 経由で http://対象ドメイン/.well-known/acme-challenge/
下にあるワンタイムトークンを取得できるようにする必要があります。このパスは、https 化した後に http アクセスをリダイレクトする場合も除外するようにしておかないといけません。
対象ドメインのルートディレクトリを作成します。
1 | # mkdir /var/www/example.tilfin.net |
対象ドメインの server (http) ディレクティブ内に、下記の location ディレクティブを追加します。Nginx をリロードします。
1 | server { |
1 | # certbot certonly -d "example.tilfin.net" -w /var/www/example.tilfin.net |
ここで 3
を入力します。
1 | Plugins selected: Authenticator webroot, Installer None |
となれば成功です。
さくらのクラウドの「ウェブアクセラレータ コントロールパネル」を開いて、サイトを追加します。
ドメイン種別は「独自ドメイン」にして、公開ドメインとホストヘッダは上記と同じドメインにします。リクエストプロトコルは「http/https」にして、オリジンプロトコルは「http」にします。
登録されたサイトの設定画面を開きます。公開ドメインを指定された xxxx.user.webaccel.jp
ドメインへの CNAME レコードとして設定します。サイトのステータスを 有効 にします。
ターミナルで cat /etc/letsencrypt/live/example.tilfin.net/fullchain.pem
して中身を証明書欄に貼り付けます。ターミナルで cat /etc/letsencrypt/live/example.tilfin.net/privkey.pem
して中身を秘密鍵欄に貼り付けます。保存します。
まずパス /ramdisk をマウントポイントとして RAM ディスクを設定することにします。
ディレクトリを作成して、スティッキービットを付けて誰でも書き込み可能にします。
1 | $ sudo mkdir /ramdisk |
/etc/fstab に下記を追記します。
1 | tmpfs /ramdisk tmpfs defaults 0 0 |
一旦、OSを再起動します。(fstab に設定しなくても一時的には mount コマンドで対応することも可能)
以下の設定中は Docker を停止しておきます。
1 | $ sudo mkdir -p /ramdisk/docker/containers |
/var/lib/docker の containers, volumes が /ramdisk/docker を見るようにします。
1 | $ sudo -i |
Docker を起動して、適当なコンテナを走らせて、 /ramdisk/docker 内にファイルが作られることを確認します。
/ramdisk/docker 内のディレクトリ構成を OS 起動時に作成するように設定します。
/etc/rc.local として下記を追記するか、コマンドを追記します。作成した場合は chmod +x /etc/rc.local
で実行権限を付与しておきます。
1 |
|
OS を再起動します。下記のそれぞれのコマンドが想定どおりの結果になっていれば OK です。
1 | $ ls -lR /ramdisk |
1 | $ ls -l /var/lib/docker |
1 | $ sudo docker ps |
これでローカル開発において、docker 上の依存サービスを高速に動作させることができるようになりました。なお自分の環境では VirtualBox に対してメモリ 8GB 割り当てた Ubuntu 18.04 がベースの Linux Mint 19.2 Tina インスタンスでこれを設定してます。
]]>下記の Ruby スクリプトは gem oauth
を使って、サーバを立てずにブラウザだけで認可してアクセストークンを取得するものです。認可後のリダイレクト時に付随するクエリ内容を適切に処理さえすれば、対象の Callback URL が 404 Not found
であろうとも問題ないです。一時的にサーバを立てる必要もなく、一連の本来サーバサイドで行う処理をローカルから実行して処理できます。
なお Callback URL が自分の持ち物でないとセキュリティ上問題があるのは当然です。
1 | require 'uri' |
スクリプトを実行すると authorize URLが生成されるので、それを認可したいアカウントでサインイン済みのブラウザに貼り付けて移動します。そのまま Twitter の認可画面でボタンを押すと、本来自分のサービスのURLにリダイレクトされます。この状態でURL欄を中身コピーして、貼り付けます。そうすると続きを処理してアクセストークンして出力します。
]]>GraphQL におけるデータ取得において内部で N+1 処理になるのを回避するためのツールです。例えば、あるリソース(A)一覧とそれに関連付けられた 1:1 のリソース(B)を一気に要求すること場合、最初のリソース(A)リストをクエリした後に、各レコードに対してリソース(B)を毎回クエリすると遅くなります。リソース(B)に対してそのキーをスタックしていき、まとめてクエリすることで N+1 回を 1+1 回に減らすことができます。この機能をまとめたものが DataLoader です。
DataLoader はあくまでも Key-value キャッシュ付きストアと考えた方がいいです。GraphQL の検索 Query で条件指定する場合は DataLoader を経由せずに直接行います。但し結果はその後の下階層で再度利用する可能性があるので prime メソッドでキャッシュしておきます。
この場合は、リクエストコンテキストに応じて DataLoader インスタンスを生成し付与します。コンテキストを生成するミドルウエア内で、DataLoader インスタンス自体をセットするか、それらインスタンスを生成できるメソッドを提供するといいでしょう。
GraphQL の Type とデータベースのレコードスキーマが完全に一致せずに、データの変換が必要となる場合の問題です。基本的にデータベースレイヤーに DataLoader を適用して、GraphQL Type には都度変換する方がいいと考えます。この理由としてリゾルバ周りの変更の影響を受けないことや、 N+1 はそもそもデータベース(ストレージ)への負荷であるからです。
コンテキストはリゾルバのパラメータとして受け取りますが、DataLoader インスタンスは下位レイヤーのモデル(ロジック層やストレージ層)で使いたいので、そのままストレートにモデルのコンストラクタなどで渡すのが一番だと思います。これに引っ張られて Active-Record パターンは JavaScript ベースでは使いずらいと思います。Ruby on Rails 等だとスレッドに情報をセットして、透過的に扱うことも可能ですが。
]]>このライブラリはテーブル作成・削除の機能もあるので、テスト用テーブル作成もこちらを使ってスクリプト化しようと考えました。
ただドキュメントが少なく、グローバルセカンダリインデックスを付与する方法を探すのに手間取ったため、ここにメモしておきます。
user_id
です。email
です。1 | import { attribute, hashKey, table } from '@aws/dynamodb-data-mapper-annotations' |
1 | $ aws dynamodb describe-table --table-name users --endpoint-url http://localhost:4569 |
1 | { |
Migrating from JavaScript | TypeScript オフィシャルサイトのこちらのぺージが参考になりますが、クライアントサイドを例書かれているので、サーバーサイドは勝手が違います。
まず typescript と ts-node しましょう。 ts-node はコンパイルせずに直接 ts ファイルを実行できます。
1 | $ npm install -g typescript ts-node |
または -g
ではなく --save-dev
としてローカルにインストールします。
1 | $ tsc --init |
tsconfig.json 生成されるので、 "target": "es2018"
, allowJs: true
, "outDir": "./dist"
にそれぞれ変更します。理由は Promise などを使えるようにするため、JavaScriptファイルはそのまま使うため、コンパイルしたものを dist ディレクトリに出力するためです。
1 | { |
例えばメインファイルの app.js を app.ts にリネームして、 tsc
で実行します。
1 | $ tsc app.ts |
上記のように require が失敗するので、 メッセージ通り @types/node
を追加します。
1 | $ npm install --save-dev @types/node |
下記の通り、再度コンパイルすると app.js
が生成されます。
1 | $ tsc app.ts |
1 | $ ts-node app.ts |
そのまま実行できます。これで最低限の導入が完了です。
1 | $ tsc |
これで tsconfig.json に従って、 ./dist
ディレクトリ以下に ts ファイル (及び js ファイル)はコンパイルされます。
最初に載せたページでは、 ts-loader/webpack による逐次コンパイルが前提のため src ディレクトリが出てきますが、 ts-node の登場によってコンパイルを意識せずに実行できるようになるため、 ./src
に既存の JS ファイルを全て移動する必要はないと考えます。
但し Web アプリケーション等のようにアセットファイルや設定ファイルを置くディレクトリなどがある場合は、 ソースディレクトリを切った方が対象ファイルの指定が楽になると思います。