動かざることバグの如し

近づきたいよ 君の理想に

cloud-initで初期設定を爆速に終わらせる

環境

やりたいこと

VPSさくっと借りてすぐ捨てたい。が、その度にサーバーセットアップするの面倒。

GCPは自動でユーザー作成と公開鍵登録までやってくれるが、AWSとかlinodeとか個別に設定しなきゃいけないのダルくなってきた。

cloud-initと呼ばれる共通設定規格があるのを知ったのでGCPで動くようにした

cloud-initとは

cloud-initとはcloud-initは、クラウド環境でのインスタンス初期化を自動化するためのツールだ。これを使えば、VPSを立ち上げる際に毎回行う面倒なセットアップ作業を省略できる。YAML形式で設定を記述し、インスタンス起動時に自動的に環境構築が行われる。

設定したい要件

  • タイムゾーン設定: Asia/Tokyoに設定
  • ロケール設定: 英語(米国)に設定
  • パッケージ更新: システム起動時にパッケージリストの更新とアップグレードを実施
  • SSHパスワード認証無効化: セキュリティ向上のためパスワード認証を無効に設定
  • ユーザーとグループの設定: thr3aというユーザーを作成し、dockerグループに追加
  • 公開鍵の登録: thr3aユーザーのSSH公開鍵を登録
  • sudo権限の設定: thr3aユーザーにパスワードなしでsudoを実行する権限を付与
  • 必要なパッケージのインストール: apt-transport-https、ca-certificates、curlgnupg、lsb-release、unattended-upgrades、zshをインストール
  • Dockerのインストールと設定: Docker関連のパッケージをインストールし、サービスを有効化・起動
  • Oh My Zshのインストール: thr3aユーザーのシェルをZshに変更し、Oh My Zshをインストール

設定

#cloud-config
timezone: Asia/Tokyo
locale: en_US.UTF-8
package_update: true
package_upgrade: true

ssh_pwauth: false

groups:
  - docker

users:
  - lock_passwd: true
    name: thr3a
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGXSyuKIS8LBdrDVLn8GcjYqyG3dpGlP/iRVuctqMBhS thr3a@thr3a-m2.local
    sudo: "ALL=NOPASSWD:ALL"
    groups: ["docker"]

packages:
  - apt-transport-https
  - ca-certificates
  - curl
  - gnupg
  - lsb-release
  - unattended-upgrades
  - zsh

# Install docker
runcmd:
  - mkdir -p /etc/apt/keyrings
  - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
  - apt-get update
  - apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
  - systemctl enable docker
  - systemctl start docker
  - runuser -l thr3a sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
  - chsh -s /bin/zsh thr3a

確認コマンド

cloud-initの設定が正しいかどうかを確認するには、以下のコマンドを実行する。

cloud-init schema --config-file cloud-config.yml

このコマンドは、指定した設定ファイルがcloud-initのスキーマに従っているかを検証する。エラーがあれば、それを指摘してくれる。

VSCodeで設定する場合

VSCodeでcloud-initの設定を行う場合、以下の設定をsettings.jsonに追加する。

"yaml.schemas": {
    "https://raw.githubusercontent.com/canonical/cloud-init/main/cloudinit/config/schemas/versions.schema.cloud-config.json": "cloud-config.yml"
},

この設定により、VSCodeはcloud-initのスキーマを使用してYAMLファイルを検証し、オートコンプリートやエラーチェックを提供する。これで、cloud-initの設定ファイルをより簡単に作成・編集できるようになる。

TCP/UDP疎通確認をncコマンドで一発チェック

環境

やりたいこと

  • TCP/UDPの疎通確認をncコマンドで簡単に確認したい

ncコマンドとは

ncコマンドはネットワーク接続を扱うためのコマンドラインツールで、TCPUDPを使った通信テスト、ポートのリスニング、データの送受信などが可能だ。シンプルながらも強力な機能を持ち、ネットワークの診断やデバッグに使われる。

ncコマンドでTCPサーバーを構築する

while true; do printf 'HTTP/1.1 200 OK\n\nHello' | nc -l 8080; done

ncコマンドでUDPサーバーを構築する

ncコマンドを使ってUDPサーバーを立てる方法は、TCPサーバーと似ているが、通信プロトコルUDPだ。以下のコマンドでUDPサーバーを立てて、指定したポートでデータを待ち受ける。受信したデータはそのままシェルに渡され、コマンドとして実行される。

サーバー側

nc -ul 1234 | /bin/sh

クライアント側

クライアント側からは、以下のコマンドでサーバーにデータを送信する。dev35はサーバーのホスト名なので、実際の環境に合わせて適宜変更する必要がある。

nc -u 【サーバー側のIP】 1234

これで、ncコマンドを使ってTCPUDPの両方のサーバーを簡単に立てることができる。ネットワークのテストやデバッグに役立てよう。

LaTeX Listingの対応言語一覧

LaTeX Listing 対応言語」でググって最初に出てくるのが以下のサイト

LaTeX の Listing 対応言語一覧 | 晴耕雨読

非常に分かりやすいのだが、2016年版ということで古く、Swift等が入ってなかった。

2024年4月28日現在の最新版を一覧にする。

対応言語一覧

ソース

http://texdoc.net/texmf-dist/doc/latex/listings/listings.pdf

Chromeだと何故か開けなかったのでwgetでダウンロードした

PulseAudioを使って別のLinuxサーバーのスピーカーから音を再生したい

環境

やりたいこと

サーバーAとBがある。サーバーAにはtest.wavがあり、サーバーBにスピーカーがある。

サーバーAから aplay test.wavを実行し、サーバーBのスピーカーから音を鳴らしたい。

今回はPulseAudioを使って実現してみる。

  • サーバーB: PulseAudioサーバー
  • サーバーA: PulseAudioクライアント

サーバー側の設定

echo 'load-module module-native-protocol-tcp listen=0.0.0.0 auth-anonymous=1' > /etc/pulse/default.pa.d/my.pa

設定反映

$ systemctl --user restart pulseaudio.service
$ systemctl --user restart pulseaudio.socket

sshすると自動で起動するが、再起動後に反応しなくなってしまうので自動起動するようにしておく

$ systemctl --user enable pulseaudio
Created symlink /home/thr3a/.config/systemd/user/default.target.wants/pulseaudio.service → /usr/lib/systemd/user/pulseaudio.service.
Created symlink /home/thr3a/.config/systemd/user/sockets.target.wants/pulseaudio.socket → /usr/lib/systemd/user/pulseaudio.socket.

クライアント側の設定

設定前に確認

$ pacmd list-sinks
1 sink(s) available.
  * index: 0
    name: <auto_null>
    driver: <module-null-sink.c>
    flags: DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
    state: IDLE
    suspend cause: (none)
    priority: 1000
    volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
            balance 0.00
    base volume: 65536 / 100% / 0.00 dB
    volume steps: 65537
    muted: no
    current latency: 2045.22 ms
    max request: 375 KiB
    max rewind: 375 KiB
    monitor source: 0
    sample spec: s16le 2ch 48000Hz
    channel map: front-left,front-right
                 Stereo
    used by: 0
    linked by: 0
    configured latency: 2000.00 ms; range is 0.50 .. 2000.00 ms
    module: 13
    properties:
        device.description = "Dummy Output"
        device.class = "abstract"
        device.icon_name = "audio-card"

設定

pacmd load-module module-tunnel-sink server=192.168.16.16

いざ確認 問題なければこれでサーバーBから音が鳴る

aplay /tmp/test.wav

設定の永続化

ただこれだと毎回設定しなくちゃいけなくて面倒

/etc/pulse/client.conf.d/my.conf 作成して以下

default-server = 192.168.16.16

確認

$ pacmd list-sinks
1 sink(s) available.
  * index: 24
    name: <tunnel-sink.192.168.16.16>
    driver: <module-tunnel.c>
    flags: NETWORK HW_MUTE_CTRL HW_VOLUME_CTRL LATENCY
    state: SUSPENDED
    suspend cause: IDLE
    priority: 0
    volume: front-left: 65536 / 100%,   front-right: 65536 / 100%
            balance 0.00
    base volume: 65536 / 100%
    volume steps: 65537
    muted: no
    current latency: 0.00 ms
    max request: 0 KiB
    max rewind: 0 KiB
    monitor source: 24
    sample spec: s16le 2ch 44100Hz
    channel map: front-left,front-right
                 Stereo
    used by: 0
    linked by: 0
    fixed latency: 250.00 ms
    module: 47
    properties:
        device.description = "USB Audio and HID Analog Stereo on thr3a@NuxBox"
        tunnel.remote.server = "192.168.16.16"
        device.icon_name = "audio-card"
        tunnel.remote_version = "35"
        tunnel.remote.user = "thr3a"
        tunnel.remote.fqdn = "NuxBox"
        tunnel.remote.description = "USB Audio and HID Analog Stereo"

参考リンク

JavaScriptでパスワード生成するときはMath.random()を使うな

パスワード生成するメソッド

指定した桁数のパスワードを生成するメソッドをTypeScriptで実装した。

別にjsでも動くと思う

export const generatePassword = (length: number): string => {
  const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
  let password = '';
  for (let i = 0; i < length; i++) {
    const randomIndex = Math.floor(Math.random() * characters.length);
    password += characters[randomIndex];
  }
  return password;
};

console.log(generatePassword(8));

動作としては問題ないのだが、何回か実行しているとどうも同じ文字が連続して生成される気がする。

❯ bun index.ts
2xdd87lv
❯ bun index.ts
qeezy2ew

でChatGPTに聞いたら案の定 Math.random()を使うのは良くないらしい

代わりにCrypto.getRandomValues()使えと言われたので書き直してもらった

export const generatePassword = (length: number): string => {
  const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
  let password = '';
  const randomValues = new Uint32Array(length);
  crypto.getRandomValues(randomValues);
  for (let i = 0; i < length; i++) {
    const randomIndex = randomValues[i] % characters.length;
    password += characters[randomIndex];
  }
  return password;
};
  • const randomValues = new Uint32Array(length);
    • Uint32Arrayは、符号なし32ビット整数の配列を作成します。
    • lengthは配列の長さで、ここではパスワードの長さに相当します。
    • この行は、ランダムな値を格納するための空の配列を作成します。
  • crypto.getRandomValues(randomValues);
    • cryptoは、暗号化操作のためのグローバルオブジェクトです。
    • getRandomValuesメソッドは、引数として渡された配列を、暗号学的に安全なランダムな値で埋めます。
    • この行は、randomValues配列をランダムな数値で満たすために使用されます。

よさそう

❯ bun index.ts
n4je65la

てかググってたらもっとスマートに実装してる人いたわ

export const generatePassword = (length: number): string => {
  const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
  return Array.from(crypto.getRandomValues(new Uint32Array(length)))
    .map(value => characters[value % characters.length])
    .join('');
};

参考リンク