Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Action Mailbox in Action

sylph01
October 03, 2020

Action Mailbox in Action

@ Kaigi on Rails, 2020/10/03

sylph01

October 03, 2020
Tweet

More Decks by sylph01

Other Decks in Programming

Transcript

  1. ΋͏ͪΐͬͱਅ໘໨ʹ • ϓϩάϥϚɻ11݄͔Β໺ੜʹͳΓ·͢ • "Dark Depths of SMTP" (2017) ͱ͍͏

    SMTPͷബ͍ຊΛॻ͖·ͨ͠ • W3C, IETFͳͲͰηΩϡϦςΟدΓͷ ϓϩτίϧͷඪ४Խͷ͓ख఻͍Λͯ͠ ͍·ͨ͠ • HTTPS in Local Network, Messaging Layer SecurityͳͲ • 2020೥͔ΒISOC-JPͷofficerΛ΍ͬͯ ·͢
  2. ͪΌΜͱͨ͠಺༰঺հ • Action Mailboxͱ͸Կ͔ • ϝʔϧʹؔ܎͢Δ֤छ֓೦ • ಛʹɺSPF, DKIM, DMARC

    • (ϝΠϯσΟογϡ) PostfixΛAction Mailboxʹͭͳ͙ • αϯϓϧΞϓϦ • ϝʔϧΛ࢖ͬͨిࢠॻ੶ͷ"social DRM"
  3. Action Mailboxͱ͸Կ͔ • Rails 6Ͱ௥Ճ͞Εͨ৽ػೳ • Action Mailer͸ϝʔϧΛ ૹ৴͢Δ ͨΊͷ΋ͷ͕ͩɺAction

    Mailbox͸RailsͰϝʔϧΛ ड৴͢Δ ͜ͱ͕Ͱ͖Δ • ड৴ϝʔϧΛActiveRecordͷΦϒδΣΫτʹม׵ • ҰఆظؒܦͬͨΒࣗಈম٫ʢ࡟আʣ
  4. ࣮ࡍͷઃఆ $ dig TXT kaigionrails.s01.ninja @8.8.8.8 $ dig TXT mail._domainkey.kaigionrails.s01.ninja

    @8.8.8.8 $ dig TXT _dmarc.kaigionrails.s01.ninja @8.8.8.8 ͰͦΕͧΕSPF, DKIM, DMARCͷઃఆ஋͕ݟΕ·͢ɻ
  5. ઃఆखॱ: ૹ৴ଆ ҎԼͷ৘ใΛυϝΠϯϨίʔυʹॻ͘ɻ • SPF: ڐՄ/ෆڐՄ͢ΔIPΞυϨε • DKIM: DKIM signer͕༻͍Δ伴ϖΞͷެ։伴

    • Αͬͯɺૹ৴ଆMTAʹDKIM signerΛΠϯετʔϧ͠ɺ伴ϖΞ Λ࡞Δඞཁ͕͋Δ • DMARC: DMARCͷೝূʹࣦഊͨ͠৔߹ͷڍಈ ड৴ଆͷઃఆ͸ޙͰઆ໌͠·͢ɻ
  6. ͜͜·ͰRails Guidesͷ௨Γ • config.action_mailbox.ingress = :relay • rails credentials:edit ͯ͠

    ingress_password Ληοτ • Α͠ɺ࣍ʹॻ͍ͯ͋Δbin/railsͷίϚϯυଧͬͨΒͳΜ͔ܨ ͕ΔΑ͏ʹͳΔϋζʂ • ͳΓ·ͤΜ
  7. bin/rails action_mailbox:ingress:postfix ͱ͸Կ͔ • ඪ४ೖྗʹ༩͑ΒΕͨϝʔϧຊจʢϔομؚΉʣΛ • ༩͑ΒΕͨ INGRESS_PASSWORD Λ࢖ͬͯೝূ͠ •

    Rails͕ղऍͰ͖ΔΑ͏ʹࢦఆͨ͠ URL ʹ౉͢ ΋ͷɻ͜Ε͚ͩΛίϚϯυϥΠϯͰ࣮ߦ͢Δͱඪ४ೖྗ͕ऴΘΔ ͷΛ଴ͪଓ͚·͢
  8. Postfixଆͷઃఆ هड़͢΂͖ઃఆϑΝΠϧͱͯ͠ओͳ΋ͷʹ main.cf, master.cf ͱ ͍͏΋ͷ͕͋Γ·͢ɻ • શମʹؔ͢Δઃఆ͸ main.cf ʹॻ͘

    • master ϓϩηεͷಈ࡞ʹؔ͢Δઃఆ͸ master.cf ʹॻ͘ • ͍͍ͩͨʮ௨৴ͷ੍ޚʯʮϝʔϧͷϧʔςΟϯάʯ
  9. master.cf ͷهड़ forward_to_rails unix - n n - - pipe

    flags=Xhq user=sylph01:sylph01 argv=/home/sylph01/keine/forward.sh ${nexthop} ${user} • forward_to_rails ͸໊শͳͷͰ೚ҙ • unix: UNIXυϝΠϯιέοτʹϝʔϧΛྲྀ͢ɻ • ͦͷޙͷྻ: unpriv ͕noʢಛݖ͕ඞཁʣɺchroot ͕no • flags: X͸ίϚϯυ͕࠷ऴ഑৴ઌͰ͋Δ͜ͱΛࣔ͢ɻhͱq͸Ξυ Ϩεͷѻ͍
  10. transport_maps, virtual_alias_maps main.cf ʹ transport_maps = hash:/etc/postfix/transport virtual_alias_maps = hash:/etc/postfix/virtual_aliases

    ͱهड़͠ɺҎԼΛ࣮ߦ $ sudo postmap /etc/postfix/transport $ sudo postmap /etc/postfix/virtual_aliases
  11. ͜Ε͸Կʁ • virtual_alias_maps: ΤΠϦΞεʢผ໊ʣͷׂ౰Λߦ͏ • ͜͜Ͱ͸ɺ@server_name ʹདྷͨϝʔϧ͕ϢʔβʔෆࡏͰό ΢ϯε͠ͳ͍Α͏ɺଘࡏ͢ΔϢʔβʔ໊ʹରͯ͠഑৴Λߦ͏ Α͏ʹ͍ͯ͠Δ •

    transport_maps: ഑ૹܦ࿏ࢦఆΛߦ͏ • ͜͜Ͱ͸ɺserver_name (ࣗ෼ࣗ਎) ʹདྷͨ΋ͷΛ͢΂ͯ master.cf ʹॻ͍ͨ forward_to_rails: ʹྲྀ͢
  12. forward.shͷத਎ #!/bin/sh HOME=/home/sylph01 PATH=/usr/local/bin:$PATH cd /home/sylph01/keine && bin/rails action_mailbox:ingress:postfix ...

    ͜͜ʹઌ΄ͲͷingressίϚϯυΛॻ͘ɻ ͪΌΜͱRubyͱRails͕ݟ͔ͭΔΑ͏ʹͯ͋͛͠Δඞཁ͕͋Δɻ
  13. ϩʔΧϧʹରͯ͠ϝʔϧͷς ετΛ͢Δ৔߹ mail ίϚϯυΛ࢖͏ͷ͕Α͍Ͱ͢ɻ UbuntuͰ͸ mailutils ͷapt-get͕ඞཁɻ echo "hogefuga" |

    mail -s "Subject" -r from- address@yourdomain destination-address@server EOF͕͸͖ͬΓ͢ΔͷͰecho͋Δ͍͸catͰຊจΛ౉͢͜ͱΛ͓͢ ͢Ί͠·͢ɻ
  14. ࣮ࡍͲΜͳ΋ͷ͕Ͱ͖ͯΔ͔ rails c ͯ͠ m = ActionMailbox::InboundEmail.all ͱ͢Δͱड৴ͨ͠ϝʔϧ͢΂͕ͯऔΕ·͢ɻ ActionMailbox::InboundEmail ͕

    ActiveRecord ͳΫϥεͳͷͰ ͍ͭ΋ͷ ActiveRecord ͷૢ࡞ͰϝʔϧΛऔΓग़ͤ·͢ɻ த਎Λݟͯॲཧ͢ΔͨΊʹ͸ Mail::Message ΦϒδΣΫτ͕ཉ͠ ͍ͷͰɺ #mail ΛݺΜͰऔΓ·͢ɻ
  15. ϝʔϧ͔Β৘ใΛಘΔ • ૹ৴ݩΞυϨε: mail.from ʢ഑ྻͰ༩͑ΒΕΔ͜ͱʹ஫ҙʣ • ૹ৴ઌΞυϨε: mail.to ʢ͜Ε΋഑ྻʹͳΔʣ •

    ϔομ: mail.header['header_name'].value • Mail::Field ͕ฦͬͯ͘ΔͷͰ#value Ͱੜͷ஋ΛಘΔ • ໊݅: mail.subject • multipartͰ͋Δ͔Ͳ͏͔: mail.multipart?
  16. application_mailbox.rb class ApplicationMailbox < ActionMailbox::Base routing /^(UUIDͷਖ਼نදݱ)@/i => :registration routing

    /^sylph01@/i => :greet end ଞʹ΋ɺ routing -> { (inbound_email) do_something } => :mailbox Έ͍ͨʹϝʔϧͷத਎Λ࢖ͬͯϚονϯά͢Δ͜ͱ͕Ͱ͖·͢ɻ
  17. registration_mailbox.rb from_address = mail.from.first local_part_of_to = mail.to.first.split("@").first user = User.find_by(uuid:

    local_part_of_to) if user != nil if user.redeemed? == false user.email = from_address user.save IssuerMailer.with(user: user).issue(inbound_email).deliver_now else if user.email == from_address IssuerMailer.with(user: user).issue(inbound_email).deliver_now else bounce_with BounceMailer.already_redeemed(inbound_email) end end else bounce_with BounceMailer.bounce(inbound_email) end
  18. registration_mailbox.rb from_address = mail.from.first local_part_of_to = mail.to.first.split("@").first user = User.find_by(uuid:

    local_part_of_to) ϝʔϧΞυϨεͷϩʔΧϧύʔτ(UUID)Λ࢖ͬͯొ࿥͞Ε͍ͯΔ UserΛ୳͢ɻ
  19. registration_mailbox.rb if user.redeemed? == false user.email = from_address user.save IssuerMailer.with(user:

    user).issue(inbound_email).deliver_now else if user.email == from_address IssuerMailer.with(user: user).issue(inbound_email).deliver_now else bounce_with BounceMailer.already_redeemed(inbound_email) end end Ҿ͖׵͑ࡁΈ͔Ͳ͏͔ͷ൑ఆɺҾ͖׵͑ͨΒϝʔϧΞυϨεΛอ ଘɻ