ブログ

  • あらかた解決するアーキテクチャを考案した

    前置き

    クリーンアーキテクチャなどの一般的なアーキテクチャを適用するにあたり悩ませる問題がある。

    “EntityをWebAPIに乗せても良いか?”

    この問題は理想論とコストの兼ね合いがあり設計方針に委ねられる事となるが私は大反対である
    何故なら不必要なデータが含まれていたり構造が限定的であるからであり、一言でいうと”UserをWebAPIで取得したらPasswordも付いてきた”という事態になりえるからだ。

    それ以前に私はEntityが何処からでも参照できるようにしてはいけないと考えている。DDDでもUI層からEntityにアクセスするのはマナー違反とされています。しかし一般的なアーキテクチャの図説を見る限りDomain層は中心にあり参照しても良いように見えてしまう。

    一方、値オブジェクトとなると逆に利用されるべきであると考える。WebAPIに乗せても良いし、むしろシリアル化のために別クラスを定義するのは非常に無駄だ。

    つまりはEntityと値オブジェクトはスコープが大きく異なる。これは実現するに辺り一般的なアーキテクチャの図説のような円だけでは表現できない設計になる。しかしコードが増える訳でもないのでコストは増えないはず。
    Application層を全てMediatorパターンで構築した際はより良い完結したアーキテクチャとなりうると考える。

    概要

    Domain層にDomain.Common、Application層にApplication.Commonというモジュールを作り、そちらに値オブジェクト系を定義するようにした。たったそれだけだがEntityとアプリケーションサービスがサーバーサイドにのみ存在するようになる利点がある。もしサーバーサイドのみのWebページならWebAPI系のモジュールを消せばいいだけである。その際はUIはサーバー側になるがEntityに依存しない。当然だがMediatorパターンを適用しているためApplicationモジュールは何処にも依存しない

    Domain.Commonモジュール

    シリアライズ可能な不変オブジェクト、つまり値オブジェクトを定義する。モジュール名はCommonでなくValueObjectsでもいいと思う。私は値オブジェクトはJSONやMessagePackに依存させてしまった方結果的に楽だと考えている。それが嫌なら下記のWebAPIモジュールにシリアライザを定義しましょう。名前空間はCommonを端折るとコードが綺麗になる。

    Domainモジュール

    EntityやDomainServiceを定義する。IUnitOfWorkのようなインターフェイスもここ。

    Application.Commonモジュール

    Mediatorパターンの引数(Request)と戻り値(Response)を定義する。モジュール名はCommonでなくValueObjectsにしたかったがインターフェイスが入り込む可能性があるためCommonを採用した。RequestとResponseは可能な限りシリアライズ可能にすべき。非同期通信など複雑な機能を実装する際はインターフェイスを戻り値にしたいケースもあり、そのインターフェイスもここで定義する。名前空間はCommonを端折るとコードが綺麗になる。

    Applicationモジュール

    サーバー側のApplication層。Mediatorのハンドラを定義する。

    WebAPIモジュール

    定数やシリアライザを定義する。私は値オブジェクト等でシリアライズ系の属性を付けてシリアライザは書かない派。SignalRを使うならHubのインターフェイスをここで定義するとリファクタリングしやすくなる。

    WebAPI.Serverモジュール

    各URLに紐づくコントローラを定義する。

    WebAPI.Clientモジュール

    クライアント側のApplication層のような物。Mediatorのハンドラを定義する。基本的にWebAPIを呼ぶ処理だけ。

  • 【C#】LtQueryの書き込み処理が完成

    概要

    LtQuery(リトルクエリ)はEF CoreのようにLINQやInclude()でSQLを構築する機能が備わっていてかつ、パフォーマンスがDapperを凌いでいるといういいとこ取りの高速O/Rマッパー(ORM)です。

    それの書き込み処理(Inert/Update/Delete)が完成致しました。

    これでようやくv1.0.0です。

    使用例

    class BlogService
    {
        readonly ILtConnection _connection;
        public BlogService(ILtConnection connection)
        {
            _connection = connection;
        }
    
        // UnitsOfWorkを使用しないパターン
        public void AddBlog(Blog blog)
        {
            _connection.Add(blog); // 即時実行される
        }
    
        // UnitsOfWorkを使用するパターン
        public void AddBlog2(Blog blog)
        {
            using (var unitOfWork = _connection.CreateUnitOfWork())
            {
                unitOfWork.Add(blog);
    
                unitOfWork.Commit();  // まとめて実行される
            }
        }
    }

    connection.Add() を使用した場合はクエリを即発行します。こちらはトランザクションを自身で管理したい場合に使います。unitOfWork.Add() は標準の書き込み方です。Commit()時に蓄積したクエリをまとめて発行します。EF Core 同様Add()されたエンティティはIDが付与されます。EF Coreのようなトレース機能はありません。パフォーマンス第一ですのでこれで十分かと思います。

    class BlogApplicationService
    {
        readonly ILtUnitOfWork _unitOfWork;
        public BlogApplicationService(ILtUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

    ILtUnitOfWork はDIコンテナにScopedで登録されています。ILtUnitOfWorkILtConnection と同様Select/Single/Firstのメソッドを持っているためわざわざ ILtConnection と使い分ける必要はなく、上記コードのような使い方もできます。

    現状の問題点

    • 書き込み処置のSQLは1件ずつ発行しているためEF Coreより多少遅い問題が発生しています(少数の書き込みなら断然速い)。時期に改善致します。 改善しました。
    • 自動採番(IDENTITY)でない主キーのエンティティの書き込みは対応していません。これも時期に改善致します
  • 【C#】LtQueryなる高速なORMを作った

    概要

    LtQuery(リトルクエリ)はEF CoreのようにLINQやInclude()でSQLを構築する機能が備わっていてかつ、パフォーマンスがDapperを凌いでいるといういいとこ取りの高速O/Rマッパー(ORM)です。
    クエリキャッシュが強力で一度実行したクエリはADO.NET直書きに迫る速度が出ます。キャッシュ自体も遅くはなくEF Core よりも断然速いです。更に使用メモリ量もかなり抑えられています。

    ORMMeanErrorStdDevGen0Gen1Allocated
    ADO.NET3.883 ms0.0633 ms0.0592 ms296.8750203.12501.44 MB
    LtQuery3.906 ms0.0380 ms0.0337 ms296.8750195.31251.44 MB
    Dapper4.416 ms0.0255 ms0.0226 ms359.3750187.50001.62 MB
    EF Core6.816 ms0.0720 ms0.0673 ms554.6875367.18752.6 MB

    使用例

    詳細はGitHub参照。

    class BlogService
    {
        readonly ILtConnection _connection;
        public BlogService(ILtConnection connection)
        {
            _connection = connection;
        }
    
        // クエリ生成
        static readonly Query<Blog> _query = Lt.Query<Blog>()
            .Include(_ => _.Posts).Where(_ => _.UserId == Lt.Arg<int>("UserId"))
            .OrderByDescending(_ => _.Date).Take(20).ToImmutable();
    
        public IEnumerable<Blog> GetNewBlogs(int userId)
        {
            // クエリ実行
            return _connection.Select(_query, new { UserId = userId });
        }
    }

    パラメータにしたい箇所を Lt.Arg<int>("UserId") の様に記述し、クエリ実行時に匿名型で渡す仕組みです。クエリのキャッシュ方法は実に簡単で単にフィールドなどに保持しておくだけ。内部的に弱参照を使っているため保持していなければGC時にキャッシュごと消される仕組みです。LINQに依存しているように見えますがそれは生成の時だけで Query<> はほぼプリミティブ型の塊です。

    背景

    • 「Entity Framework ってCoreになっても遅いですよね?」
    • 「DapperはSQLすぎる」

    ってな訳でO/Rマッパー(ORM)を自作することにしました。

    Entity Framework Core の問題点

    LINQ to SQLに依存しきっている

    EF CoreLINQ to SQL が中枢に存在します。ということは Expression に依存しきっていることになり、 Expression はそこそこ重いためどうしても全体に影響が出てしまいます。システム的に限界が見えている訳です。

    明示的クエリキャッシュがくどい

    static readonly Func<TestContext, int, Blog> _selectSingle = 
        EF.CompileQuery((TestContext context, int id) => 
          context.Set<Blog>().Single(_ => _.Id == id));

    EF Coreは自動的にクエリキャッシュを行うため気にしなくてもいいです。ですが、あえて明示的に行おうとすると↑のようなコードになります。

    Dapper の問題点

    SQL依存

    DapperはSQLファーストなライブラリです。超シンプルで素晴らしいのですが、やはり機能面が物足りない。それを補うライブラリ( SqlKataDapper.FastCrud など)がありますが、SQLが中枢部にいると拡張しづらい問題があります。

    これらの問題を解決するORMのシステム

    まず、中枢から ExpressionSQL を排除します。代わりに軽量なクエリオブジェクトを中枢におき、すべての動作の核とします。そこからSQLを構築したり、読み取り処理のフロー決定に使ったりします。このクエリオブジェクトをキーにしてクエリキャッシュを行います

    そして、更にパフォーマンスを向上するため読み取り処理は完全IL実装にします。この辺はSQLファーストだったら実装しづらい機能です。Dapper に近いパフォーマンスを目標にします。Dapper を超しました。

    今後

    ベンチ見てやる気が出たので本格的なフルORMにする予定です。特にValue ObjectとかDDD絡みの機能を追加してDDDに最適なORM

  • Ubuntu 22.04にPostfix+Dovecot(SASL認証)を構築しGmail/Yahooメールの迷惑メール判定に合格するまで

    概要

    まっさらなUbuntuに実運用に適した設定のメールサーバーを構築するための流れを記述します。ユーザ管理は非UNIXアカウントをDBでなくファイルで管理する方式を採用しています。セキュリティ重視のため送受信共 SSL/TLS を使用します。Outlookだと STARTTLS でなければならないという情報を何処かで聞きました。実際本記事の設定ではOutlookでログインできなかったので注意。今では必須となっている送信ドメイン認証 DKIM を行うため OpenDKIM を使います。※DNSのサービスによっては送信ドメイン認証が使えないところもあります。

    要求

    • 実用的なセキュリティ
    • GmailとYahooメールの迷惑メール判定に合格する
    • メールアカウントとUNIXシステムアカウントを分ける
    • IMAPをサポートする
    • 迷惑メールをフィルタする
    • クライアントのアカウント設定のユーザ名にドメインを付けたくない

    要件

    • 送信ドメイン認証にSPFとDKIMを用いる
    • SPFとDKIMに対応したDNSサービス(ドメイン側とサーバ側)を利用する
    • Postfix+Dovecot(SASL認証)でサーバを構築する
    • Let’s Encryptのドメイン証明書を利用する
    • クライアント間の通信はSSL/TLSで行う

    送信ドメイン認証に対応したDNSサービス

    まず、DNSサービスによっては迷惑メール判定回避のための送信ドメイン認証(SPFDKIM)が設定不可能な所もあります。現時点での情報を載せます。

    ドメイン取得代行サービス

    SPFDKIM
    お名前.com
    さくらのドメイン×

    サーバー側サービス

    SPF(逆引き)
    さくらのVPS
    WebARENA Indigo×
    WebARENA IndigoPro不明

    最初に

    送受信用のサブドメインを分けるパターン(smtp.XXX.xxとかimap.XXX.xx)を良く見かけますが逆引きやら証明書やらリレーやらで煩わしいので理由がない限りサブドメインを作らない方が無難です。

    FQDNが正しく設定されているか確認します。ドメイン部が付いて無いとPostfixの初期設定が変化するので注意。

    $ hostname -f
    [ホスト名].[ドメイン(.xxなど)]
    
    インストールの前にファイアウォールを設定する事をお勧めします。

    インストールの前にファイアウォールを設定する事をお勧めします。

    $ sudo ufw allow ssh # まだufwを有効にしたことない場合
    $ sudo ufw allow smtp
    $ sudo ufw allow smtps
    $ sudo ufw allow imaps
    $ sudo ufw allow http
    $ sudo ufw allow 7080 # LiteSpeedの管理画面
    $ sudo ufw enable

    LiteSpeed

    ここではLet’s Encryptを使うために先にLiteSpeed(Webサーバ)をインストールします。Webサーバが必須な訳ではありませんが後でインストールするつもりならこの時点でインストールしちゃいましょう。

    $ wget -O - https://repo.litespeed.sh | sudo bash
    $ sudo apt update
    $ sudo apt install openlitespeed
    
    # 管理画面のユーザ・パスワードを設定する
    $ sudo /usr/local/lsws/admin/misc/admpass.sh
    
    # 起動
    $ sudo systemctl start lsws

    http://[ドメイン名]:7080 にアクセスしExampleのポートを80に変更します。

    Let’s Encrypt

    ここで指定するメールアドレスは証明書の有効期限が切れそうな時などのお知らせで使用されます。

    $ sudo apt install certbot
    $ sudo certbot certonly
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    
    How would you like to authenticate with the ACME CA?
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    1: Spin up a temporary webserver (standalone)
    2: Place files in webroot directory (webroot)
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 【 2 】
    Enter email address (used for urgent renewal and security notices)
     (Enter 'c' to cancel): 【メールアドレス(外部サーバでもOK)】
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Please read the Terms of Service at
    https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
    agree in order to register with the ACME server. Do you agree?
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    (Y)es/(N)o: 【 Y 】
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Would you be willing, once your first certificate is successfully issued, to
    share your email address with the Electronic Frontier Foundation, a founding
    partner of the Let's Encrypt project and the non-profit organization that
    develops Certbot? We'd like to send you email about our work encrypting the web,
    EFF news, campaigns, and ways to support digital freedom.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    (Y)es/(N)o: 【 N 】
    Account registered.
    Please enter the domain name(s) you would like on your certificate (comma and/or
    space separated) (Enter 'c' to cancel): 【ドメイン名】
    Requesting a certificate for xxxxx.xx
    Input the webroot for xxxxx.xx: (Enter 'c' to cancel): 【/usr/local/lsws/Example/html】
    
    Successfully received certificate.
    Certificate is saved at: /etc/letsencrypt/live/xxxxx.xx/fullchain.pem
    Key is saved at:         /etc/letsencrypt/live/xxxxx.xx/privkey.pem
    This certificate expires on 2023-03-13.
    These files will be updated when the certificate renews.
    Certbot has set up a scheduled task to automatically renew this certificate in the background.
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    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
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    Postfix

    UNIXシステムアカウントとは別にメールアカウントを作るためバーチャルメールボックスという機能を使っています。メール保管ディレクトリは /var/mail とします。

    $ sudo apt install postfix
    
    # SMTP-AUTH用ユーザとグループ追加
    $ sudo groupadd -g 10000 vmail
    $ sudo useradd -u 10000 -g vmail -s /sbin/nologin vmail
    
    # メール保管ディレクトリ作成
    $ sudo mkdir /var/mail
    $ sudo chown vmail:vmail /var/mail
    $ sudo nano /etc/postfix/main.cf
    ------------------------------------------
    # 詳細情報を隠すため簡略化
    smtpd_banner = $myhostname ESMTP
    
    # Let's Encryptの証明書に変更
    smtpd_tls_cert_file=/etc/letsencrypt/live/[ドメイン名]/fullchain.pem
    smtpd_tls_key_file=/etc/letsencrypt/live/[ドメイン名]/privkey.pem
    
    # バーチャルメールボックスを使用する場合はlocalhostのみを指定
    mydestination = localhost
    
    # ↓↓末尾に追記↓↓
    # SMTP-AUTH
    smtpd_sasl_auth_enable = yes
    smtpd_sasl_type = dovecot
    smtpd_sasl_path = private/auth
    smtpd_client_restrictions =
      permit_mynetworks
      permit_sasl_authenticated
      reject_unknown_client
    
    # Virtual Domain
    virtual_mailbox_domains = [ドメイン名]
    virtual_mailbox_base = /var/mail
    virtual_mailbox_maps = hash:/etc/postfix/vmailbox
    virtual_alias_maps = hash:/etc/postfix/virtual
    virtual_gid_maps = static:10000
    virtual_uid_maps = static:10000
    
    # DKIM
    milter_default_action = accept
    milter_protocol = 6
    smtpd_milters = unix:/opendkim/opendkim.sock
    non_smtpd_milters = $smtpd_milters
    
    # Other Security
    disable_vrfy_command = yes
    smtpd_helo_required = yes
    strict_rfc821_envelopes = yes
    smtpd_etrn_restrictions = permit_mynetworks, reject
    smtpd_helo_restrictions = permit_mynetworks, reject_invalid_hostname
    smtpd_sender_restrictions = reject_unknown_sender_domain
    smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination
    $ sudo nano /etc/postfix/master.cf
    ------------------------------------------
    # ↓コメントを外す
    smtps     inet  n       -       y       -       -       smtpd
      -o syslog_name=postfix/smtps
      -o smtpd_tls_wrappermode=yes
      -o smtpd_sasl_auth_enable=yes
    #  -o smtpd_reject_unlisted_recipient=no
    #  -o smtpd_client_restrictions=$mua_client_restrictions
    #  -o smtpd_helo_restrictions=$mua_helo_restrictions
    #  -o smtpd_sender_restrictions=$mua_sender_restrictions
    #  -o smtpd_recipient_restrictions=
      -o smtpd_relay_restrictions=permit_sasl_authenticated,reject # ←必須
    #  -o milter_macro_daemon_name=ORIGINATING

    バーチャルメールボックスの設定ファイルを作成していきます。複数のドメインを使い分けする場合はvirtualファイルはいりません(上記の virtual_alias_maps もコメントアウト)。

    $ sudo nano /etc/postfix/virtual
    ------------------------------------------
    [ユーザ名1]@[ドメイン名] [ユーザ名1]
    [ユーザ名2]@[ドメイン名] [ユーザ名2]

    $ sudo nano /etc/postfix/vmailbox
    ------------------------------------------
    [ユーザ名1]@[ドメイン名] [ユーザ名1]/
    [ユーザ名2]@[ドメイン名] [ユーザ名2]/
    # ↓複数のドメインを使い分けする例
    #[ユーザ名3]@[ドメイン名2] [ドメイン名2]/[ユーザ名3]/

    vmailboxの方では行末の / を忘れずに。

    $ sudo postmap /etc/postfix/virtual
    $ sudo postmap /etc/postfix/vmailbox
    
    
    
    
    

    vmailboxは必要か?

    無くても virtual_alias_maps を工夫すれば運用は可能ですが本来の使い方ではありません。調べると結構このパターンが多いですが、本記事ではvirtualとvmailboxを分けて設定します。こうする事でバーチャルドメインでも単一ドメインならユーザ名にドメインを付けなくても良くなります

    ↓つまり上記の設定をしない場合、クライアント側の設定がこうなってしまう。

    ホスト名[ドメイン]
    ユーザ名[ユーザ]@[ドメイン]
    パスワード[パスワード]

    Dovecot

    通信は必ず暗号化するためパスワード認証はCRAM-MD5形式でなく平文でも安全です。もしOutlookで使いたいならパスワード認証の暗号化が必須になります。本記事では平文を使います。ですが、CRAM-MD5形式にしてもデメリットは少ないかと思います。

    $ sudo apt install dovecot-imapd
    $ sudo nano /etc/dovecot/conf.d/10-master.conf
    ------------------------------------------
    service imap-login {
      inet_listener imap {
        port = 0 # コメントアウトするだけだとデフォルトポートで動作してしまう
      }
      inet_listener imaps {
        port = 993
        ssl = yes
      }
      :
      :
      :
      # Postfix smtp-auth
      unix_listener /var/spool/postfix/private/auth {
        mode = 0660
        user = postfix
        group = postfix
      }
    $ sudo nano /etc/dovecot/conf.d/10-mail.conf
    ------------------------------------------
    
    mail_location = maildir:/var/mail/%u
    $ sudo nano /etc/dovecot/conf.d/10-auth.conf
    ------------------------------------------
    #!include auth-system.conf.ext # コメントアウト
    #!include auth-sql.conf.ext
    #!include auth-ldap.conf.ext
    !include auth-passwdfile.conf.ext
    #!include auth-checkpassword.conf.ext
    !include auth-static.conf.ext
    $ sudo nano /etc/dovecot/conf.d/auth-passwdfile.conf.ext
    ------------------------------------------
    passdb {
      driver = passwd-file
      args = username_format=%u /etc/dovecot/users
    }
    # userdb をコメントアウト
    #userdb {
    #  :
    #  :
    #}
    $ sudo nano /etc/dovecot/conf.d/auth-static.conf.ext
    ------------------------------------------
    userdb {
      driver = static
      args = uid=vmail gid=vmail home=/var/mail/%u
    }
    $ sudo nano /etc/dovecot/conf.d/10-ssl.conf
    ------------------------------------------
    ssl_cert = </etc/letsencrypt/live/[ドメイン名]/fullchain.pem
    ssl_key = </etc/letsencrypt/live/[ドメイン名]/privkey.pem

    アカウントのパスワードを設定します。doveadm pw を使ってハッシュ化されたパスワードを生成し、usersファイルに保存します。

    $ sudo doveadm pw
    Enter new password:[パスワード]
    Retype new password:[パスワード]
    {CRYPT}$2y$05$tGPBaquyHNg0Sakkk0YZgeYIqyYvGnZnQaqeymVpdD5By15wwSpnS
    $ sudo nano /etc/dovecot/users
    ------------------------------------------
    [ユーザ名1]:{CRYPT}$2y$05$tGPBaquyHNg0Sakkk0YZgeYIqyYvGnZnQaqeymVpdD5By15wwSpnS
    [ユーザ名2]:{CRYPT}$2y$05$ZfFb0zOIY7KGGDjh5n4/8uuhFOoFEklVDjuUXn0ca8F5jKFW5/tQO
    $ sudo chown dovecot:dovecot /etc/dovecot/users
    $ sudo chmod go-rw /etc/dovecot/users

    メールの保管場所は/var/mail/%d/%n か /var/mail/%u か?

    本記事では単一ドメインなので /var/mail/%u にしています。複数のドメインを使い分けする場合は/var/mail/%d/%n にすると良いでしょう。/%d/%n形式にする場合はmail_locationの変更も忘れずに。

    $ sudo nano /etc/dovecot/conf.d/10-mail.conf
    ------------------------------------------
    mail_location = maildir:/var/mail/%d/%n
    ------------------------------------------

    OpenDKIM

    セレクタ名は任意ですが、本記事では単一のKeyしか扱わないため default にします。

    $ sudo apt install opendkim
    $ sudo adduser postfix opendkim # ユーザ[postfix]をグループ[opendkim]に追加
    
    # DKIM Milter用ディレクトリ作成
    $ sudo mkdir /var/spool/postfix/opendkim
    $ sudo chown opendkim:postfix /var/spool/postfix/opendkim
    $ sudo nano /etc/opendkim.conf
    ------------------------------------------
    Domain                  [ドメイン名]
    Selector                default
    KeyFile                 /etc/dkimkeys/[ドメイン名]/default.private
    
    Socket                  local:/var/spool/postfix/opendkim/opendkim.sock
    $ sudo mkdir /etc/dkimkeys/[ドメイン名]
    $ sudo opendkim-genkey -D /etc/dkimkeys/[ドメイン名] -d [ドメイン名]
    $ sudo chown -R opendkim:opendkim /etc/dkimkeys

    サーバ側の仕上げ

    # ↓何も出力されなければOK
    $ sudo postfix check
    
    # 再起動
    $ sudo systemctl restart postfix dovecot opendkim
    
    # ↓active (running)ならOK
    $ sudo systemctl status postfix
    $ sudo systemctl status dovecot
    
    # ↓これはまだOKにならない
    $ sudo systemctl status opendkim

    ここでエラーしても取り合えず進みましょう。

    メーラー(クライアント側)の設定

    メーラーはThunderbirdが通りやすいです。使う気が無くてもデバッグ目的に使うのもありかと。

    受信側

    サーバー名[ドメイン名]
    ポート993
    ユーザ名[ユーザ名]
    接続の保護SSL/TLS
    認証方式通常のパスワード認証

    送信側

    サーバー名[ドメイン名]
    ポート465
    接続の保護SSL/TLS
    認証方式通常のパスワード認証
    ユーザ名[ユーザ名]

    送受信テスト

    送信/受信共に可能かテストします。この時点ではまだ迷惑メール扱いされる可能性が高いです。送受信できなかったらずっと↓のFAQ参照。疲れたから止めた

    ドメイン側のDNSレコード設定

    送信ドメイン認証(SPFとDKIM)の設定をしていきます。/etc/dkimkeys/[ドメイン名]/default.txt の中身を整えてDNSレコードに追加する必要があります。作業をしやすくするためどうにかしてクライアント上のテキストエディタで開くことをお勧めします。盗聴されても問題無いデータなのでWebサーバに配置すると楽です。

    # Webサーバに置く例
    $ sudo cp /etc/dkimkeys/[ドメイン名]/default.txt /usr/local/lsws/Example/html/
    $ sudo chmod 666 /usr/local/lsws/Example/html/default.txt
    # コピーできたら削除する
    $ sudo rm /usr/local/lsws/Example/html/default.txt
    ↓のようなテキストです。
    default._domainkey      IN      TXT     ( "v=DKIM1; h=sha256; k=rsa; "
              "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqchsxtNg6uckZ2xH~~"
              "Uvm3fzUFOaHJ9g+nx1EQ7cezWmOKb6LCbhFbfxCDO7loxDV0wDpvpHbr5U/jCe~~" )  ;
    
    
    
    
    

    ( " から " ) ; の中を改行・ダブルクォーテーションを消して結合します。

    v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAO~~

    そうしたらこのテキストとその他諸々をDNSレコードに追加していきます。※DNSレコードのルールはサービス元によって異なります。お名前.comだとこんな感じ↓です。

    ホスト名TYPETTLVALUE
    @MX3600[ドメイン]
    @TXT3600v=spf1 +ip4:[IPv4アドレス] +ip6:[IPv6アドレス] -all
    _adsp._domainkey.@TXT3600dkim=unknown
    default._domainkey.@TXT3600v=DKIM1; h=sha256; k=rsa; p=MIIB~~

    MXはメールを送信するドメインを記載します。これは古い習慣のような物です。次の v=spf1 +ip4: はSPFの設定です。それ以降の2行はDKIMの設定です。

    DNSの更新は瞬時には反映されないので6分程待機。

    お名前.comの場合

    更新時に 転送用のネームサーバー にチェックが必要らしいです。

    サーバ側の逆引き

    さくらのVPSだとこんな感じ↓です。

    送信ドメイン認証のテスト

    クライアントからメールを送信します。送信先はYahooメールを使うと楽です。↓の画面に行き着けば目的達成です。これでももし迷惑メール判定されたら古いテストメールを消すなり文章をまともにすると通る確率が上がる気がします。

    ちなみに、Gmailで 高度な暗号化 と表示させるには S/MIME が必要で、おそらく無料では出来ないと思われる。

    FAQ

    domainkeys=neutral (no sig) は正常?

    DKIMdomainkeys上位互換なので無視しても良いかと思う。

    色々書こうと思ったけど疲れた

  • Hello world!

    WordPress へようこそ。こちらは最初の投稿です。編集または削除し、コンテンツ作成を始めてください。