Outbound Mail Sender Identity Provisioning Gate for Egress IPs
Pricing
from $2.00 / 1,000 run starteds
Outbound Mail Sender Identity Provisioning Gate for Egress IPs
SPF result for your egress IP, before the first mail. Give it the From domain, the egress IP addresses and the DKIM selectors of a new relay, tenant or email provider. It runs SPF check_host() for each IP (RFC 7208, with lookup counts), checks reverse DNS
Pricing
from $2.00 / 1,000 run starteds
Rating
0.0
(0)
Developer
kingii98
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Outbound Mail Sender Identity Provisioning Gate for Egress IPs, DKIM Selectors, and Reverse DNS
Get the SPF result for your egress IP and the reverse DNS result before you send the first mail.
You give the From domain, the egress IP addresses and the DKIM selectors of a new relay, tenant or email provider. For each sender identity, the Actor does these checks:
- It runs SPF
check_host()(RFC 7208) for each egress IP. It gives the result, the mechanism that matched, the DNS lookup count against the limit of 10 and the void lookup count against the limit of 2. - It finds the PTR names of each egress IP. It checks if each name resolves back to the IP (forward-confirmed reverse DNS). It also marks provider default names, for example
ec2-…amazonaws.com. This mark is a heuristic. - It checks that the HELO name resolves to the egress IP.
- It looks up each DKIM key at
<selector>._domainkey.<from domain>. It gives the key type, the key size and the revoked state (emptyp=). It also compares the key with the SHA-256 that you expect. - It finds the DMARC record and its policy, and it tells you if DMARC alignment is possible.
- It gives a ready or blocked verdict. The verdict lists each blocking code with a one-line fix.
Use it in a deploy pipeline. Call the Actor through the Apify API before the pipeline enables mail for a new environment, tenant, customer, relay or provider.
What the Actor does not do
- It does not open SMTP connections, and it does not send mail.
- It does not query commercial DNS blocklists, because their free use does not permit commercial use.
- It uses public DNS only: DNS-over-HTTPS at
dns.google, withcloudflare-dns.comas the fallback. It does not fetch URLs that you supply. - It does not check private or reserved egress addresses (for example
10.0.0.0/8,192.0.2.0/24,fe80::/10). Such an address gets the codeegress_ip_not_public, and the gate is blocked.
Input
| Field | Required | Description |
|---|---|---|
identities | yes (has a default) | 1 to 50 sender identities. |
identities[].from_domain | yes | The RFC 5322 From domain. |
identities[].egress_ips | yes | 1 to 16 IPv4 or IPv6 addresses. |
identities[].dkim_selectors | yes | 1 to 5 selectors. |
identities[].envelope_domain | no | The MAIL FROM (Return-Path) domain. The SPF check uses this domain. Default: from_domain. |
identities[].helo_name | no | The HELO or EHLO name of the relay. The %{h} SPF macro also uses it. |
identities[].expected_dkim_key_sha256 | no | An object selector -> SHA-256 of the base64-decoded p= value, in hex or base64. With one selector, a single string is also correct. |
fail_on | no | The check codes that set the gate to blocked. Default: spf_not_pass, dkim_missing, ptr_missing, ptr_not_forward_confirmed, dmarc_missing. |
Example:
{"identities": [{"from_domain": "example.com","envelope_domain": "bounce.example.com","egress_ips": ["198.51.100.25", "2001:db8:25::25"],"dkim_selectors": ["s2026"],"helo_name": "relay1.example.com","expected_dkim_key_sha256": {"s2026": "3f1c…64 hex characters…"}}],"fail_on": ["spf_not_pass", "dkim_missing", "ptr_missing", "ptr_not_forward_confirmed", "dmarc_missing", "ptr_generic"]}
The addresses in this example are documentation addresses. The Actor does not check them. Use your public egress addresses.
With an empty input, the Actor checks two sample identities: github.com (ready) and example.com (blocked, because its SPF record is -all and it has no DKIM key).
If the input is not valid, the Actor writes one run-error record with the reason, and the run ends SUCCEEDED. It checks nothing and charges nothing.
Output
The default dataset has four record types. The Gate verdicts, Egress IPs and DKIM selectors views show them as tables.
sender-ip: one record for each identity and egress IP
| Field | Meaning |
|---|---|
egressIp, ipVersion | The address. |
evaluated | false for a private or reserved address that the Actor did not check. |
spfResult | pass, fail, softfail, neutral, none, permerror or temperror. |
spfMatchedMechanism | The path to the mechanism that matched, for example include:_spf.google.com > ip4:209.85.128.0/17. |
spfReason, spfRecord | Why the result occurred, and the SPF record of the envelope domain. |
spfDnsLookups / spfDnsLookupLimit | The DNS lookups of the evaluation, and the limit of 10. |
spfVoidLookups / spfVoidLookupLimit | The lookups that returned no data, and the limit of 2. |
spfAlignment, spfAligned | strict, relaxed or none for the envelope domain against the From domain, and if this satisfies the DMARC aspf tag. |
ptrNames, fcrdnsResult, fcrdnsNames | The PTR names. The result is pass, fail, no_ptr or lookup_error. The names are the names that resolve back to the IP. |
genericPtr, genericPtrReason, genericPtrIsHeuristic | The generic-PTR heuristic (see below). |
heloResult, heloAddresses | matches_egress_ip, resolves_elsewhere, no_address, lookup_error or not_given. |
checkCodes | The check codes of this IP. |
dkim-selector: one record for each DKIM selector
selector, dnsName, txtFound, dkimRecord, keyType (rsa or ed25519), keySizeBits, revoked (empty p=), testMode (t=y), keySha256, expectedKeySha256, expectedKeyMatch, note and checkCodes.
sender-gate: one record for each identity
status (ready or blocked), blockingCodes, warningCodes, fixes (for each code: the code, blocking, a one-line fix, and where the code occurred), dmarcFound, dmarcRecordName, dmarcRecord, dmarcPolicy, dmarcSubdomainPolicy, dmarcPct, aspf, adkim, alignmentPossible (yes or no), spfPassIps, usableDkimSelectors, egressIpsRejected and failOn.
Alignment is possible if all checked egress IPs get SPF pass with an aligned envelope domain, or if one selector has a usable DKIM key under the From domain.
For a subdomain without its own DMARC record, the Actor uses the record of the organizational domain (Public Suffix List) and its sp= policy.
run-summary: one record for each run
The number of identities that are ready, blocked and skipped, the blocked identities with their codes, the checked IPs and selectors, the DNS lookup count and the notes.
The status message of the run gives the same summary, for example 2 sender identities: 1 ready, 1 blocked (example.com: dkim_missing, spf_not_pass). 2 egress IPs and 2 DKIM selectors checked.
A blocked gate is a result, not an error. The run ends SUCCEEDED. Your pipeline must read status from the sender-gate records.
Check codes
| Code | Blocks by default | Meaning |
|---|---|---|
spf_not_pass | yes | The SPF result for the egress IP is not pass. |
spf_lookup_limit_near | no | The evaluation used 8 or more of the 10 DNS lookups. |
spf_not_aligned | no | The envelope domain does not align with the From domain. |
dkim_missing | yes | The selector has no usable key: no record, an empty p=, a key that does not parse, or an RSA key under 1024 bits. |
dkim_revoked | no | The record has an empty p=. It also sets dkim_missing. |
dkim_invalid_key | no | The p= value is not a valid RSA or Ed25519 key. It also sets dkim_missing. |
dkim_weak_key | no | The RSA key has fewer than 1024 bits. It also sets dkim_missing. |
dkim_key_mismatch | no | The key is not the key that expected_dkim_key_sha256 names. |
dkim_test_mode | no | The record has t=y. |
ptr_missing | yes | The egress IP has no PTR record. |
ptr_not_forward_confirmed | yes | No PTR name resolves back to the egress IP. |
ptr_generic | no | The PTR name looks like a provider default (heuristic). |
helo_not_resolving | no | The HELO name has no A or AAAA record. |
helo_mismatch | no | The HELO name does not resolve to the egress IP. |
dmarc_missing | yes | No single valid DMARC record applies to the From domain. |
dmarc_alignment_impossible | no | Neither SPF nor DKIM can give DMARC alignment. |
egress_ip_not_public | always | The address is private or reserved. |
dns_lookup_error | always | A DNS lookup failed or timed out. The check is not proven, so the gate is blocked. Run the gate again. |
To make a code block the gate, add it to fail_on. egress_ip_not_public and dns_lookup_error always block.
The generic-PTR heuristic
A PTR name is generic when one of these rules is true:
- The name contains the IP address: the four octets in order or reversed, the first three octets, the 12 zero-padded digits, or the 8 hex digits. For IPv6, the name contains the last 12 hex nibbles.
- A part of a label is a word for an access or dynamic pool, for example
dynamic,dhcp,pool,dsl,cable,customerorclient. - The name ends with a provider default suffix, for example
.amazonaws.com,.googleusercontent.com,.your-server.de,.vultrusercontent.comor.linodeusercontent.com.
The rule is a heuristic, so ptr_generic does not block by default. If a result is incorrect, open an issue.
Limits
- 50 identities, 16 egress IPs and 5 selectors for each identity.
- Each DNS answer is cached for the run, so the same include chain across many IPs costs its lookups one time.
- One run has a limit of 15,000 DNS lookups. Each lookup has a timeout of 8 s. Each SPF evaluation has a timeout of 40 s, and after that the result is
temperror. - The DNS answers have a limit of 64 KB. The Actor does not follow redirects.
- The
%{p}SPF macro expands tounknown, as RFC 7208 section 7.3 permits.
Pricing
This Actor uses pay per event.
| Event | Unit | Price (USD) | When |
|---|---|---|---|
run-started | one Actor run | 0.002 | Once for each run with a valid input. |
sender-ip-evaluated | one egress IP evaluated against SPF and reverse DNS for one identity | 0.004 | Once for each public egress IP of each checked identity. A private or reserved address is not charged. |
dkim-selector-checked | one DKIM selector looked up and its key checked | 0.001 | Once for each selector of each checked identity. |
sender-gate-verdict | one ready or blocked verdict for one sender identity | 0.01 | Once for each checked identity. |
The Actor charges the events of an identity after it writes the records of that identity. The run-summary record and a run-error record are not charged.
What one provisioning call costs
1 identity, 2 egress IPs, 2 DKIM selectors:
| Event | Count | Price | Cost (USD) |
|---|---|---|---|
run-started | 1 | 0.002 | 0.0020 |
sender-ip-evaluated | 2 | 0.004 | 0.0080 |
dkim-selector-checked | 2 | 0.001 | 0.0020 |
sender-gate-verdict | 1 | 0.01 | 0.0100 |
| Total | 0.0220 |
How the size of the run changes the cost
"Charged" uses the default maximum charge of USD 2.00 for each run.
| Identities | IPs each | Selectors each | Uncapped (USD) | Charged (USD) | Identities checked |
|---|---|---|---|---|---|
| 1 | 2 | 2 | 0.0220 | 0.0220 | 1 |
| 10 | 4 | 2 | 0.2820 | 0.2820 | 10 |
| 50 | 2 | 2 | 1.0020 | 1.0020 | 50 |
| 50 | 16 | 5 | 3.9520 | 1.9770 | 25 |
What happens at the maximum charge
Before the Actor checks an identity, it makes sure that all events of that identity fit in the rest of the maximum charge. If they do not fit, the Actor stops. It does not check that identity or the identities after it. It writes a note in the run-summary record and in the status message. An identity is never half checked.
With the maximum input (50 identities with 16 IPs and 5 selectors each), a run at the default limit of USD 2.00 checks 25 identities. To check all 50 identities in one run, set the maximum charge to USD 3.96 or more, or split the identities into two runs.