aoirint.com 個人用メールサーバ

2022-12-09より、aoirint.comにて、個人用メールサーバの運用を始めました。 aoirint.comアドレス宛てにメールが送信できない、受信できない、不正中継されている、セキュリティ上の問題などがあれば、Twitter、Mastodon、Keybaseなどにご連絡ください。 更新履歴 2022-12-09 運用開始 構成 DNS DNSでは、MXレコード、SPFレコードを設定しています。 外部からのメールを内部に転送するSMTPサーバ mx01.aoirint.com SMTP 暗号化 STARTTLS(必須) 認証 なし ホスト名(TLS証明書のコモンネーム) mx01.aoirint.com ポート番号 25 外部へのメール送信 禁止 SMTPS 暗号化 暗黙的TLS 認証 なし ホスト名(TLS証明書のコモンネーム) mx01.aoirint.com ポート番号 465 外部へのメール送信 禁止 外部にメールを送信するSMTPサーバ smtp.aoirint.com Submission 暗号化 STARTTLS(必須) 認証 平文パスワード ホスト名(TLS証明書のコモンネーム) smtp.aoirint.com ポート番号 587 外部へのメール送信 許可(認証必須) IMAPサーバ imap.aoirint.com IMAPS 暗号化 暗黙的TLS 認証 平文パスワード ホスト名(TLS証明書のコモンネーム) imap.aoirint.com ポート番号 993 POP3サーバ pop.aoirint.com POP3S 暗号化 暗黙的TLS 認証 平文パスワード ホスト名(TLS証明書のコモンネーム) pop.aoirint.com ポート番号 995

2022年12月9日 · aoirint

死活監視サービスBetter Uptimeを導入した

aoirint.com関連サービスにBetter Uptimeを導入し、死活監視情報の提供を開始しました。 登録時に電話番号が必要なのがネックだけれど、URLを入力して、オプションでカスタムドメインを使うためにDNSにCNAMEレコードを追加したくらいで、結構簡単に使い始められた。 プランは以下を参照。URL 10個まで無料で監視させられる(3分間隔)。 https://betterstack.com/better-uptime/pricing カスタムドメインのステータスページ。 https://status.aoirint.com

2022年11月23日 · aoirint

自宅サーバのWebサービスをVPSで中継するようにした

これまで自宅サーバへの外部からの接続には、 数年前から自宅のグローバルIPアドレスにフリーのドメインを紐づけて使っていたのですが、 3月末に更新を忘れてこのドメインを失効させてしまいました。 自宅サーバでは、主に個人用のWebサービスや身内用のゲームサーバが動いています。 同じドメインを再取得するには追加の料金がかかりますし、(若干用途が規約的に怪しいこともあり)新しくドメインを取り直す気にもなれなかったので、 以前から契約していたVPSをTCP/UDPプロキシとして使ってみることにしました。 この記事では、nginx.confのhttpディレクティブとstreamディレクティブそれぞれについて、拡張子でincludeを切り分けるようにする構成にしています。 基本的には、nginx.confに以下のように記述するイメージです。 http { include /etc/nginx/conf.d/*.http; } stream { include /etc/nginx/conf.d/*.stream; } 関連ツイート https://twitter.com/aoirint/status/1510309731642257408 https://twitter.com/aoirint/status/1511896279731040261 また、この記事の内容には関係ありませんが、envsubstでconfigファイルに環境変数を注入する、nginxのDocker Hub公式Dockerイメージのtemplate機能を使用しているため、 nginx設定ファイルの拡張子がtemplateになっています。 上のツイートに関連して、certbot --nginxで自動生成される/etc/letsencrypt/options-ssl-nginx.confを手動でnginx.confに統合しているので、 以下の設定ではコメントアウトされているincludeがあります。 構成 自宅サーバ側:systemd + autosshによる自動SSH接続&リモートポート転送 VPS側:nginxによるHTTPリバースプロキシ・UDPプロキシ 自宅サーバ側 ~/.ssh/config Host vps* HostName VPS_IPADDR Port VPS_SSH_PORT ServerAliveInterval 10 ServerAliveCountMax 5 TCPKeepAlive yes IdentitiesOnly yes IdentityFile ~/.ssh/vps Host vps-forwarding-homeserver # SERVICE1 RemoteForward 127.0.0.1:VPS_SERVICE1_PORT 127.0.0.1:LOCAL_SERVICE1_PORT # SERVICE2 RemoteForward 127.0.0.1:VPS_SERVICE2_PORT 127.0.0.1:LOCAL_SERVICE2_PORT /etc/systemd/system/autossh-vps.service [Unit] Description=AutoSSH VPS [Service] Type=simple Restart=always User=user Group=user WorkingDirectory=/home/user ExecStart=/usr/bin/autossh -N vps-forwarding-homeserver [Install] WantedBy=multi-user.target VPS側 HTTPリバースプロキシ(SSHポート転送使用): myservice.http.template server { server_name myservice.example.com; client_max_body_size 500M; add_header Strict-Transport-Security 'max-age=15552000; includeSubDomains; preload'; location / { #auth_basic "Auth required"; #auth_basic_user_file /secrets/myservice.htpasswd; proxy_pass http://127.0.0.1:VPS_MYSERVICE_PORT; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; send_timeout 3600s; proxy_connect_timeout 3600s; proxy_send_timeout 3600s; proxy_read_timeout 3600s; #proxy_max_temp_file_size 2048m; } location /.well-known/acme-challenge/ { root /letsencrypt-webroot; } listen 443 ssl; ssl_certificate /etc/letsencrypt/live/myservice.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/myservice.example.com/privkey.pem; # include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; } server { if ($host = myservice.example.com) { return 301 https://$host$request_uri; } server_name myservice.example.com; listen 80; return 404; } UDPポート転送(SSHポート転送不使用): myserviceudp.stream.template server { listen VPS_SERVICEUDP_PORT udp reuseport; proxy_pass HOME_IPADDR:HOME_SERVICEUDP_PORT; proxy_connect_timeout 3600s; proxy_timeout 3600s; #proxy_max_temp_file_size 2048m; }

2022年4月7日 · aoirint

はてなブログから古い記事を移した

新しい記事 blog.aoirint.com へ投稿しつつ、 古い記事は aoirint.hatenablog.com に残っていましたが、 古い記事を blog.aoirint.com に移しました。 古い記事にはリダイレクトと移動先へのリンクを設置しています。 https://github.com/x-motemen/blogsync blogsyncで記事を取得したあと、以下のようなスクリプトでfrontmatterを変換しました。 以前の移行時の作業ミスでMarkdown記事がHTMLになってしまっていたので、 手動でMarkdownに書き戻しました。 import glob import yaml from datetime import datetime from pathlib import Path import frontmatter as FM for path in glob.glob('**/*.md', recursive=True): if path.startswith('output'): continue print(path) with open(path, 'r') as fp: frontmatter = FM.load(fp) # docs = yaml.safe_load_all(fp) # frontmatter = next(docs) # body = next(docs) body = frontmatter.content print(frontmatter) print(body) title = frontmatter['Title'] if title.startswith('(移動済)'): print(f'Skipped: {title}') continue print(f'Processing: {title}') original_url = frontmatter['URL'] date: datetime = frontmatter['Date'] draft = frontmatter.get('Draft', False) tags = frontmatter.get('Category') category = tags[0] if tags else None new_frontmatter = { 'title': title, 'date': date.strftime('%Y-%m-%d %H:%M:%S'), 'draft': draft, 'channel': '技術ノート', } if category: new_frontmatter['category'] = category if tags: new_frontmatter['tags'] = tags output = yaml.dump(new_frontmatter, default_flow_style=False, sort_keys=False, allow_unicode=True) output_lines = output.split('\n') output_lines.insert(0, f'---') output_lines.insert(1, f'# moved from {original_url}') output_lines.insert(-1, f'---') output = '\n'.join(output_lines) output += f'# {title}\n\n' + body + '\n' dest = Path('output', path) dest.parent.mkdir(parents=True, exist_ok=True) with open(dest, 'w') as fp: fp.write(output)

2022年2月22日 · aoirint

えやみぐさの改修ログ 2022-02-20

えやみぐさ: blog.aoirint.com えやみぐさのGatsby設定ファイルのTypeScript化 レイアウト用の型定義をgatsby-nodeやgatsby-configで利用するために、これらのGatsby設定ファイルをTypeScript化することになりました。 以下の記事が参考になりました。 https://miyauchi.dev/ja/posts/gatsby-typescript/ えやみぐさに「チャンネル」機能を追加 トピックの異なる記事を分離するために、「チャンネル」という単位で記事を分類するようにしました。 これまで、えやみぐさの記事は、カテゴリとタグによって分類していました。 しかし、この2つだけでは、例えば日記と技術記事の記事一覧を分離することができません(RSSや最近の記事一覧で問題になる)。 えやみぐさに異なるトピック・ジャンルの記事を統合するために、既存のカテゴリとタグによる分類を維持し、新しい階層を加えることにしました。 これまでのカテゴリ・タグは、チャンネル別に機能するようにしました。 いまのところ、この記事は作業ログというチャンネルに属しています。 通常の技術ノートに比べて、気軽にページを作ることができるようにと考えています。 あとで内容の一部を技術ノートに移す・写すこともあるかもしれません。 現在の技術ノートはあまり品質がよくない(メモ書きレベル)ので、作業ログに移してもいいかなと思っているのですが、そこそこ記事数があり、 中身を確認しながら再分類するのが手間なのでやっていません。 作業ログは、作業を中断したり、力尽きたりしたりして、中途半端な内容や矛盾した文章が載っていることがあるかもしれませんが、自分用のメモなので気にしないでいきたいと思っています。 えやみぐさの記事管理方法とURLフォーマットについて えやみぐさでは、記事のURLをentry/<作成年>/<slug>というフォーマットにしています。 このフォーマットは、えやみぐさの記事をファイルシステム上で管理しやすくするために選んだものです。 えやみぐさの記事を作成するときは、URLと対応したパスにディレクトリを作成し、index.mdファイルを配置します。 これは、記事に付随するリソース(画像や音声ファイルなど)と記事本体をまとめて管理するためにそうしています。 比較として、entry/<slug>方式では、ふつうに構成すると1ディレクトリにすべての記事ディレクトリを配置することになります。 例えば、2020年には下書きを含めて31件の記事があるのですが、 次の年も同じ数の記事を書いたとすると、計62件、その次の年なら計93件になることが予想されます。 昔書いた記事に補足を入れたいときや、新しい記事を書きたいときに、100件のサブディレクトリがあるディレクトリを操作するのは、 誤操作が起きやすい点、目に入る情報量が多く気が散る点から、望ましくない・苦手です。 entry/<作成年>/<作成月>/<slug>やentry/<作成年>/<作成月>/<作成日>/<slug>も考えられますが、 自分の記事作成ペースと、手作業で管理するのが手間に感じる点から採用しませんでした。 えやみぐさのURL永続性について インターネット上のURLは、永続化することが望ましいという考え方があります。 例えば、ブックマークしておいたページが消えていると困りますし、リンクが貼られているとリンク切れを起こしてしまいます。 記事のURLの永続化には、URLの割り当て方法が関わってきます。 ブログシステムでのURLの割り当て方法としては、 記事タイトルの一部などによる「スラグ」を利用する方法、 DB上の連番IDを利用する方法、 時刻や日付を利用する方法、 ランダムなIDを利用する方法、 が思い付きます。 えやみぐさでは、記事のURLをentry/<作成年>/<slug>というフォーマットにしています。 CMS・Wikiなどでは、URLを永続化するためにリダイレクトが自動で貼られる仕組みが用意されている場合がありますが、 えやみぐさのコンテンツ管理方式(GitリポジトリにMarkdownファイル)では、リダイレクトによってURLを永続化するのは難しく、 実装しても運用するのはそれなりの手間になります(例えば、リダイレクトだけの実体のない記事ページが記事ファイル一覧に含まれる、増え続けるリダイレクトリストの管理など)。 できるだけ記事URLを書き換えないようにしてはいますが、リンクが貼られにくい雑記については、 URL永続性を維持する動機がないので、適当に管理すると思います。 記事のURL名前空間は、チャンネル間で共通にしています(entry/slugのこと)。 えやみぐさでは、チャンネル・カテゴリ・タグのようなタクソノミーは、あとで書き換えることがある、という運用を考えています。 えやみぐさの記事一覧について これまで、えやみぐさのトップページでは、カテゴリの一覧と、各カテゴリの全記事リストを表示する方式をとっていました。 これは、過去に書いた技術記事を素早く参照するためのものでした。 この記事の執筆時点でも、技術記事用のチャンネル「技術ノート」では、同様の方式で記事一覧を表示するようにしています。 えやみぐさのデプロイ自動化について えやみぐさを管理するGitリポジトリは、Gatsbyプロジェクトリポジトリと記事リポジトリの2つに分かれています(submoduleで管理しています)。 記事の可搬性とレイアウト・記事の分離のためにそうしていますが、固有記法を使うこともあるかもしれませんし、 まああまり細かいところは気にしないで運用しています。 えやみぐさのデプロイは、GatsbyプロジェクトリポジトリにpushされたときにはGitHub Actionsが走りますが、 記事リポジトリにpushされたときには走りません。 記事を更新したときには、親のGatsbyプロジェクトリポジトリのsubmoduleを更新する手間があります。 Webサイトのレイアウトを調整すること、記事を更新すること、をうまく共存させながら、この手間を解消したいのですが、 あんまりいい方法が思い付いていません。。。 例えば、記事を書くときにいちいちGatsbyでレンダリングするのは重くて手間なので、Gatsbyプロジェクトをcloneせずに 記事を追加するような運用をしたいと思っていますが、 レイアウトをいじるときには実際の記事があった方がいい、というような感じで、どうしたらいいのかなと思っています。 workflow_dispatchをうまく組めば、片方のリポジトリが更新されたときにデプロイ走らせることはできると思うけれど、 ローカルで動かすときに微妙な気がしている… えやみぐさのデプロイにおける「2重コミット問題」を解消した 「2重コミット問題」:テーマリポジトリと記事リポジトリを分離するため、submoduleを使って管理するとき、submoduleと親リポジトリの2つにそれぞれコミットするのが手間になる問題 API経由でWorkflowを実行する。スコープの広いPATが必要なのが難しいところなので、抵抗あったのだけれど、まあ便利なので仕方がない。 ...

2022年2月20日 · aoirint