Okada Hiroshi の blog

typo が多いです

はてなダイアリーをインポートしたらカテゴリーがカオスになったので Python と テキストエディタ(Vim) の力を借りて修正しました

はてなダイアリー」が終了するので、インポート機能を使って、過去の記事を「はてなブログ」にインポートしました。

ところが、「はてなダイアリー」と「はてなブログ」でカテゴリーが統一されていなかったため、カテゴリーがカオスになってしまいました。

また、初期には「はてなダイアリー」を日記モードで書いていたため、タイトルが無い記事も多数存在していました。

これらを「はてなブログ」の Web 画面上から変更することは(僕には)苦痛だったので、 一括して変更するために、記事(entry)のタイトルとカテゴリーを読み書きするツールを Python で作成し、 テキストエディタで編集して変更しました。

github.com

まず、get-tc.py というスクリプトでタイトルとカテゴリーを読み出しました。

python get-tc.py --api-key=<APIキー> <ルートエントリーポイント> <出力ファイル名>

APIキーと、ルートエントリーポイントは、はてなブログの設定画面から取得できます。

Screenshot (API_KEY)

ファイルの中身は yaml 形式で、記事(entry)の配列になっており、ひとつの記事は以下の用になっています。

- categories:
  - カテゴリ1
  - カテゴリ2
  entry_id: エントリID
  link: 記事のURL
  published: 最初に書いた日付
  summary: 記事のサマリー
  title: 記事のタイトル

テキストエディタでこのファイルを書き換えました。 なお、タイトルとカテゴリ以外の項目は変更しても反映しないようにしました。

編集が終わったら、以下のコマンドで更新しました。

python update-tc.py --api-key=<APIキー> <ルートエントリーポイント> <入力ファイル名>

なお、エディタは vim をつかって次のような使い捨ての vimscript を作成し読み込んでから編集しました。

nnoremap q#  o- C#<ESC>
nnoremap qB  o- bash<ESC>
nnoremap qC  o- C<ESC>
nnoremap qD  o- Docker<ESC>
nnoremap qJ  o- JavaScript<ESC>
nnoremap qP  o- Python<ESC>
nnoremap qR  o- Ruby<ESC>
nnoremap qS  o- SSL<ESC>
nnoremap qd  o- 読書・マンガ・アニメ・映画<ESC>
nnoremap qi  o- IT<ESC>
nnoremap qk  o- 購入<ESC>
nnoremap ql  o- 生活・健康・旅行<ESC>
nnoremap qm  o- MS Office・Googleドキュメント<ESC>
nnoremap qn  o- ニュース・時代<ESC>
nnoremap qt  o- 投資・経済<ESC>
nnoremap q/  /^- categories<CR>

3万円弱の中国製のWindows PCを購入しました。

普段はデスクトップもノートPCもUbuntuマシンを使っているのですが、たまに Windows マシンが必要になります。

今までは5年ほど前に買った Dell のノートブックを騙しだまし使っていました、性能はそれ程問題ではなかたのですが、ハードウエア的に色々壊れてきたので、 あたらしく Windows PC を購入することにしました。

予算も限られている上、普段US配列のキーボードをつかっているので日本語キーボードは嫌だったので、中国メーカ製のノートPCを購入してみました。

一応、初期不良等が不安だったので、で評判が良さそうな物を検索して Amazon で購入しました。

上記のリンクの価格は 33,991円 ですがタイムセールで5000円引きだったので 3万円以下で買えました。

CPU は Celeron N4100 というショボい中ではマシな方という評価のものです、メモリは 4GB 、ストレージは メーカーのサイト では 64GB eMMC となっていましたが 128GB の SSD です。

色々インストールした後ですが、一応 CrystalDiskMark ストレージの速度を計ってみました、SSD といえる程度の速度は出ているようです。 f:id:OkadaHiroshi:20190207175705p:plain

起動時に Windows のセットアップ画面から始まる真っ当なタイプです。余計なものも入っていないようです。

f:id:OkadaHiroshi:20190207174514j:plain

なお、キーボードに貼って日本語キーボード化するシールもついていましたが、今回はUS配列のまま使っています。

Office は Office 2019 を使ってみたかったののあって、 ウチダの学割|Microsoft Office学割|内田洋行 で購入しました (僕は放送大学の学生です)。18,144円でした。

Twitter の自動更新ブックマークレット

今日は小ネタを

Twitter の自動更新 ブックマークレット (Bookmarklet) です。ツイッターの Web ページの 「新しいツイート○件を見る」 が表示されていたらクリックして、広告は非表示にします。

下のボタンっぽいリンクをブックマークバーにドラッグするか、右クリックで「名前をつけてリンク先を保存」で保存してください。(Google Chrome 用です。)

🐦自動読込

  • スクロールして過去のツイートを見ているときは自動更新しません。
  • ブックマークレットをクリックする毎に自動更新 ON/OFF が切り替わります。
  • 自動読み込み中は鳥アイコンの周りに点線が表示されます。
  • 広告は非表示にしているだけで、内部的には読み込んでいます。

ソースコード

(() => {
  // タイマー呼び出しが定義済みならキャンセルして終了
  if (window.autoReloadId) {
    clearInterval(window.autoReloadId);
    delete window.autoReloadId;
    // 自動読み込み中であることを示すため表示した点線を削除
    document.querySelector('h1').style.outline = "";
    return;
  }
  
  // 後で参照するために一番上のツィートの id を保存
  let topItemIdSave = document.querySelector("li.stream-item").id;

  // 広告を隠す
  const hideAd = () =>
    Array.from(
      document.querySelectorAll("a.js-promoted-badge"),
      e =>
        (e.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.style.display =
          "none")
    );
  hideAd();

  window.autoReloadId = setInterval(() => {
    // スクロール中ならば何もしない(ほんの少しだけならスクロー中でない事にする)
    if (document.scrollingElement.scrollTop > 15) {
      return;
    }
    const topItemId = document.querySelector("li.stream-item").id;
    if (topItemIdSave === topItemIdSave) {
      // 「新しいツイートをn件を見る」が表示されていたらクリックする
      const e = document.querySelector(".new-tweets-bar");
      e && e.click();
    } else {
      topItemIdSave = topItemId;
      hideAd();
    }
  }, 200);
  
  // 自動読み込み中であることを示すためh1(鳥アイコン)の周りに点線を表示
  document.querySelector('h1').style.outline = "dotted 1px #00f";
})();

URL

javascript:(()=%3E%7Bif(window.autoReloadId)return%20clearInterval(window.autoReloadId),delete%20window.autoReloadId,void(document.querySelector(%22h1%22).style.outline=%22%22);let%20e=document.querySelector(%22li.stream-item%22).id;const%20t=()=%3EArray.from(document.querySelectorAll(%22a.js-promoted-badge%22),e=%3Ee.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.style.display=%22none%22);t(),window.autoReloadId=setInterval(()=%3E%7Bif(document.scrollingElement.scrollTop%3E15)return;const%20o=document.querySelector(%22li.stream-item%22).id;if(e==e)%7Bconst%20e=document.querySelector(%22.new-tweets-bar%22);e&&e.click()%7Delse%20e=o,t()%7D,200),document.querySelector(%22h1%22).style.outline=%22dotted%201px%20#00f%22%7D)();

同様の機能をもった機能拡張もありますが、機能拡張用よりはブックマークレットのほうが安全性は高いし、起動していないときの負荷も少ないです。

https (ssl) サーバーのサンプル

ローカルネットで開発する時に使用するワイルドカード SSL 証明書の取得方法の記事を書いたので、いくつかの言語で簡単な https サーバープログラムを記述しようと思います。 最近の言語は標準ライブラリや定番のライブラリが充実しているので簡単に https サーバーを書くことができます。

コンテンツ

なお、折角 https サーバーで配信するのですから、加速度センサーを使うサンプルコンテンツにしようと思います。

以下のように Android からアクセスするとスマホを回転させても頭が上でありつづけます。

残念ながら iOSバイスでは動作しません。 PC では API は使えるようですが物理的に加速度センサーがほぼ付いていないので使えません。 Accelerometer | MDN

とりあえず簡単のために index.html に JavaScriptCSS も書いてしまいました。 ( index.htmlpublic という名前のフォルダーに入れました。 )

<!DOCTYPE html>
<html lang="ja">

<head>

<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="UTF-8">
<title>Stick figure</title>

<style>
#stick-figure {
  width: 50%;
  margin: 0 auto;
  transform: rotate(0rad);
  transform-origin: center;
}
#message {
  color: red;
  font-size: 2rem;
}
</style>

</head>

<body>

<svg viewBox="0 0 200 200" id="stick-figure">
  <g stroke="green" fill="none" stroke-width="5">
    <circle cx="100" cy="30" r="20" />
    <line x1="100" y1="50" x2="100" y2="110" />
    <polyline points="80,190 100,110 120,190" />
    <polyline points="40,75 100,65 160,75" />
  </g>
</svg>

<p id="message"></p>

<script>
const main = () => {
  const stickFigure = document.getElementById('stick-figure');
  const message = document.getElementById('message');

  if (!('Accelerometer' in window)) {
    message.innerHTML = 'no Accelerometer';
    return;
  }
  navigator.permissions.query({ name: 'accelerometer' }).then(result => {
    if (result.state !== 'granted') {
      message.innerHTML = result.state;
      return;
    }
    const acc = new Accelerometer({ frequency: 60 });
    acc.addEventListener('reading', e => {
      const { x, y } = acc;
      const rad = -Math.atan2(y, x) + Math.PI / 2.0;
      stickFigure.style.transform = `rotate(${rad}rad)`;
    });
    acc.start();
  });
};

main();
</script>

</body>
</html>

Ruby

標準のモジュールだけで完結したかったので WEBrick を使いました。 WEBrick モジュールだけで完結するので、リファレンス等が分かりやすかったです。 ( この辺は大クラス主義のおかげかもしれません。 ) 証明書と中間認証局は別ファイルにしないといけないようです。

#!/usr/bin/env ruby

require 'webrick'
require 'webrick/https'

port = (ENV['PORT'] || 8443).to_i

puts "port=#{port}"

httpd = WEBrick::HTTPServer.new({
  DocumentRoot: './public',
  BindAddress: '0.0.0.0',
  Port: port,
  Logger: WEBrick::Log.new(nil, level=WEBrick::BasicLog::WARN),
  SSLEnable: true,
  SSLCertificate: OpenSSL::X509::Certificate.new(File.open('./certs/cert.pem').read),
  SSLExtraChainCert: [OpenSSL::X509::Certificate.new(File.open('./certs/chain.pem').read)],
  SSLPrivateKey: OpenSSL::PKey::RSA.new(File.open('./certs/privkey.pem').read)
})

Signal.trap('INT') {
  httpd.shutdown
}

httpd.start()

Python

ssl.wrap_socket というのは Python 3.2 以上では非推奨で Python 3.7 からは禁止だそうです。 まずはじめに ssl context というのを作らなくてはいけないのですが、ドキュメントではクライアント側とサーバー側両方の解説があるので初心者にはわかりにくかったです。 しかもサーバー側を作る時に ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) とするのはチョッとした罠です。

あと、SimpleHTTPRequestHandler がカレントディレクトリからコンテンツを配信すること前提のようなので chdir するという汚いサンプルになってしまいました。

#!/usr/bin/env python3

from http import server
import ssl
import os

if __name__ == '__main__':
    address = ('0.0.0.0', 8443)

    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    context.load_cert_chain('certs/fullchain.pem', 'certs/privkey.pem')

    os.chdir('public')
    httpd = server.HTTPServer(address, server.SimpleHTTPRequestHandler)
    httpd.socket = context.wrap_socket(httpd.socket)

    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass

    httpd.server_close()

Node.js

手抜きして express を使ってしましました。そのおかげで少ない行数で済んでいます。

#!/usr/bin/env node

const fs = require('fs')
const express = require('express')
const https = require('https')
const app = express()

const port = Number(process.env.PORT || 8443)

const options = {
  cert: fs.readFileSync('./certs/fullchain.pem'),
  key: fs.readFileSync('./certs/privkey.pem')
}

app.use(express.static('public'))

server = https
  .createServer(options, app)
  .listen(port, () => console.log(`start port=${port}`))

Docker (alpine + lighttpd)

プログラム言語ではありませんが、 Docker (alpine + lighttpd) でもやってみました。 lighttpd では鍵ファイルと証明書ファイルを連結する必要があるので起動時にやっています。

Dockerfile

FROM alpine:3.8

RUN apk --update add lighttpd && rm -rf /var/cache/apk/*

COPY ./public /var/www/localhost/htdocs

RUN cd /etc/lighttpd &&\
    sed -e '/^server.modules = (/a"mod_openssl",' \
        -e '/^# server\.port/aserver.port = 443' \
        -e '/^# ssl.engine/assl.engine = "enable"' \
        -e '/^# ssl.pemfile/assl.pemfile = "/etc/ssl/certs/server.pem"' \
        -e '/^# ssl.pemfile/assl.ca-file = "/etc/ssl/certs/chain.pem"'\
        --in-place \
        lighttpd.conf &&\
        echo -e '#/bin/sh\n'\
                'cat /certs/privkey.pem /certs/cert.pem > /etc/ssl/certs/server.pem\n'\
                'cat /certs/chain.pem >/etc/ssl/certs/chain.pem\n'\
                'chmod 600 /etc/ssl/certs/*.pem\n'\
                'lighttpd -f /etc/lighttpd/lighttpd.conf\n'\
                'sleep 1\n'\
                'tail -f /var/log/lighttpd/error.log -f /var/log/lighttpd/access.log\n' > /start.sh &&\
        chmod +x /start.sh

EXPOSE 443

CMD "/start.sh"

起動

$ docker build . -t <タグ名>
$ docker run --rm -it -p 443:443 -v "$(pwd)/certs:/certs:ro" --name <コンテナ名> <タグ名>

リポジトリ

なお、以上のスクリプトHiroshiOkada/https-example: https 静的サーバーサンプル に置きました。

ローカルで開発中のコンテンツも初期の段階から https (ssl) で配信する (その3) Let's Encrypt を使ってワイルドカード証明書を取得しました。

5. Let's Encrypt のクライアントソフト Certbot をインストールしました。

PPA を追加して certbot をインストールしました。

$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt update
$ sudo apt install certbot

6. certbot を使って証明書を取得しました。

証明書を要求するために次のようなコマンドを入力しました。

sudo certbot certonly --manual --preferred-challenges=dns-01 --server=https://acme-v02.api.letsencrypt.org/directory --agree-tos -m okadahiroshi@mibox.jp -d '*.local.toycode.com'

すると以下のように表示されます。途中で Are you OK with your IP being logged? 「あなたのIPアドレスを記録しますがよろしいですか?」と聞かれるので y と答えました。

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for local.toycode.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.local.toycode.com with the following value:

1QjY1MdTuF1gjGmJexopl7N9DosUTaSyGz7gDCGlck0

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

ここで、DNS の TXT フィールドに貼り付ける認証用の文字列 が表示されました。

ここでもう一つ別なターミナルを開いて zone ファイル /etc/nsd/local.toycode.com.zone に TXT フィールドを加えました。 もちろん serial も増やしました。

$TTL 1m
local.toycode.com. IN SOA ns-local.toycode.com. postmaster.toycode.com. (
  2  ; serial
  5m ; refresh
  2m ; retry
  6h ; expire
  1m ; minimum
  )

@ IN NS ns-local.toycode.com.

ns-local IN A 35.199.184.64 

_acme-challenge.local.toycode.com. IN TXT "1QjY1MdTuF1gjGmJexopl7N9DosUTaSyGz7gDCGlck0"

a192-168-1-1 IN A 192.168.1.1
a192-168-1-2 IN A 192.168.1.2
以下略

/etc/nsd/local.toycode.com.zone を編集し終わったら、$ sudo nsd-control reloadで zone ファイルを読み込ませました。 そして、$ dig txt _acme-challenge.local.toycode.com +noedns +short"1QjY1MdTuF1gjGmJexopl7N9DosUTaSyGz7gDCGlck0" が返ってくることを確認しました。

zone ファイルの更新が終わったので、もとのターミナルに戻って、enter キーを押しました。すると

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/local.toycode.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/local.toycode.com/privkey.pem
   Your cert will expire on 2019-04-15. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

と表示され、/etc/letsencrypt/live/local.toycode.com/fullchain.pemドメインの証明書と中間認証局の証明書の連結されたものが、 /etc/letsencrypt/live/local.toycode.com/privkey.pem秘密鍵ファイルが保存されました。(実際にはこれらはシンボリックリンクで実体は別な場所に保管されています。)

なお上には表示されていませんが。/etc/letsencrypt/live/local.toycode.com/ ディレクトリには

ファイル名 内容
cert.pem ドメインの証明書
chain.pem 中間認証局の証明書
fullchain.pem ドメインの証明書と中間認証局の証明書の連結されたもの
privkey.pem 秘密鍵

の4個のファイルが保存されていました。

以上でワイルドカード証明書の取得ができました。

ローカルで開発中のコンテンツも初期の段階から https (ssl) で配信する (その2) NSD のインストールと設定

4. NSD をインストールしました。

今回はマシンのリソースも少ないので DNSコンテンツサーバ(権威DNSサーバ) として軽量だと言われている NSD https://unbound.jp/nsd4/ https://www.nlnetlabs.nl/projects/nsd/ を使ってみました。

インストール自体は apt で簡単にインストールできます。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install nsd

今回 DNS をセットアップ中に自分自身の名前解決等ができないとエラー等がこったり反応が悪くなるので /etc/hosts に以下のエントリーを追記しました。

35.199.184.64 ns-local.toycode.com
127.0.1.1 ns-local

/etc/nsd/nsd.conf を次のようにしました。

server:
        do-ip4: yes
        do-ip6: no
        port: 53
        username: nsd
        zonesdir: "/etc/nsd"
        pidfile: "/run/nsd/nsd.pid"
        logfile: "/var/log/nsd.log"

zone:
        name: "local.toycode.com"
        zonefile: "local.toycode.com.zone"

/etc/nsd/local.toycode.com.zone を作成し、次のようにしました。

$TTL 1m
local.toycode.com. IN SOA ns-local.toycode.com. postmaster.toycode.com. (
  1  ; serial
  5m ; refresh
  2m ; retry
  6h ; expire
  1m ; minimum
  )

@ IN NS ns-local.toycode.com.

ns-local IN A 35.199.184.64 

a127-0-0-1 IN A 127.0.0.1

a192-168-1-1 IN A 192.168.1.1
a192-168-1-2 IN A 192.168.1.2
a192-168-1-3 IN A 192.168.1.3
a192-168-1-4 IN A 192.168.1.4
a192-168-1-5 IN A 192.168.1.5
a192-168-1-6 IN A 192.168.1.6
a192-168-1-7 IN A 192.168.1.7
a192-168-1-8 IN A 192.168.1.8
a192-168-1-9 IN A 192.168.1.9
a192-168-1-10 IN A 192.168.1.10
a192-168-1-11 IN A 192.168.1.11
a192-168-1-12 IN A 192.168.1.12
a192-168-1-13 IN A 192.168.1.13
以下略

TTL は後で修正が早く反映されるように極端に短い値にしています。ある程度たったら大きな値 (1dとか) に書き換える予定です。シリアルは後でスクリプト等で修正するかもしれないので、一般的な日付をベースにしたものにしていません。

使いそうなアドレスは全て登録しました、僕がよく利用するネットワークは 192.168.1.0/24 192.168.2.0/24 192.168.100.0/24 192.168.101.0/24 なので すべて登録しました。

NSD は BIND とちがって $GENERATE が無いので一々記述する必要があります。スクリプト言語等で生成しても良いでしょう。ぼくは vim を使っているので

良く使うアドレスは個別にわかりやすい名前を割り当てても良いとおもいます。

:for net in [1,2,100,101] | for host in range(1,254) | put='a192-168-' . net . '-' . host . ' IN A 192.168.' . net . '.' . host | endfor | endfor

で生成しました。

また port 53 がバッティングして NSD が起動できなかったので、systemd-resolved.service を止めました。そして resolve.conf を手動で書き換えて 8.8.8.8 を参照するようにしました。

$ sudo systemctl disable systemd-resolved.service 
$ sudo service systemd-resolved stop
$ sudo rm /etc/resolv.conf 
$ echo nameserver 8.8.8.8 | sudo tee /etc/resolv.conf 

この辺はちょっとハマりました、以上の設定で動いていますが正当なやり方かどうかはわかりません。

参考: dns - How to disable systemd-resolved in Ubuntu? - Ask Ubuntu

5. ちゃんと名前が引けているかどうか確認しました。

nsd の起動は $ sudo nsd-control start zone ファイルの再読込は(シリアルを増やして) $ sudo nsd-control reload です。

zone ファイルの編集が終わって読み込んだら以下のようなコマンドで手元のコンピュータで 名前が引けるか確認しました。

$ dig A a192-168-1-1.local.toycode.com +short
$ nslookup -type=A a192-168-1-1.local.toycode.com
$ getent hosts a192-168-1-3.local.toycode.com

ローカルで開発中のコンテンツも初期の段階から https (ssl) で配信する (その1)

はてなブックマーク などごく一部のサービスを除いて多くの Web サービスが https で配信されるようになりました。 これはセキュリティ的には非常に良いことなのですが、開発している時には不便な事もあります。

例えば

  • OAuth 2.0 のコールバック URL は大抵の場合 localhost 以外だと https である必要がる。けれども、開発中にも隣においてある PC やスマホからアスセスした。
  • Web ブラウザから加速度センサー等のセンサーを読み出すためには https でないと動作しない。 Sensors For The Web!  |  Web  |  Google Developers
  • html 文書中で間違えて http で供給されるコンテンツを参照していても (エラーが出ないので) 気が付かず https 環境にアップロードして初めて気がつく。

などのです。

これを防ぐために開発の初期の段階から ssl 化しておく事が望ましいです。

https で配信するためには、ホスト名とそれに対応した ssl 証明書が必要です。例えば僕の自宅 LAN の場合 192.168.100.* というアドレスが DHCP dで配布されますので、DNS サーバーにこれらのホストを登録して、対応する ssl 証明書を取得すれば、ローカルで開発中のコンテンツも ssl で配信できます。

具体的には僕の所有しているドメイン toycode.com のサブドメイン local.toycode.com というのを作って、 a192-168-100-2.local.toycode.com なら 192.168.100.2、 a192-168-100-3.local.toycode.com なら 192.168.100.3 というアドレスが引けるようにして、さらに *.local.toycode.com のワイルドカード証明書を取得することにより ssl 化を行うことにしました。

今回は Google Compute Engine (GCE) の無料枠の f1-micro インスタンス上に DNS サーバーを立てて、let's encrypt でワイルドカード証明書を取得することにより、ほとんど追加の費用なしで上記の事を実現できましたので、手順を説明します。

この方法はオレオレ証明書を使う方法やトンネリングを使う方法に比べてクライアント側の設定がいらないのが利点だと思います。

1. GCE のインスタンスを作成しました。

GCE の無料枠の f1-micro インスタンスの立て方はネットワーク上に山ほど記事があるので、割愛します。今回は DNS サーバーとして利用しますので、固定IP を取得します。固定IPはインスタンスに割当ないと課金されるので確実にインスタンスに割り当てる必要があります。

ブートディスクは Ubuntu 18.04 LTS を使用しました。名前は nc-local としました。今回 IP アドレスは 35.199.181.64 でした。

f:id:OkadaHiroshi:20190116145210p:plain

2. ファイアーウォールのルールを作成してポート 53 を開けました。

ファイアーウォールのルールを作成します。名前とか説明は何でも良いのですが、注意点としては UDP 53、TCP 53 の両方を開けるルールにすることです。 ターゲットタグの名前は覚えやすいように dns-server にしました。

f:id:OkadaHiroshi:20190116150214p:plain

指定したターゲットタグを VM インスタンス nc-local に割り当てました。 スクリーンショットでは http と https のポートも開いていますが今回の目的には不要です。

f:id:OkadaHiroshi:20190116150450p:plain

3. toycode.com の DNS サーバーから local.toycode.com のサブドメインの権限を移譲しました。

toycode.com の DNS サーバーは amazon の route 53 を使っています。local.toycode.com の権限を移譲させるために、aws の Route 53 Management Console を使って次の2つのレコード を追加しました。

f:id:OkadaHiroshi:20190116151616p:plain

他のサービスを使っていてもほぼ同様の事ができると思います。

ドメインのメインのネームサーバーは2つ以上必要ですが、サブドメインのネームサーバーは(冗長性を考えなければ)1つで OK です。