Skip to content

External posture check

The external posture check looks at a domain the way any visitor or mail server already does, and grades three areas: TLS, security headers and mail authentication. For a domain your workspace has verified, you can also ask it to check exposed ports. Open Vendors & trust > External posture check in the app (/oscuro/posture).

It is separate from the paid Oscuro add-on. Any member of a workspace on any plan can run it, except the auditor role, which is read-only.

Every area comes back as Pass, Fail or Could not check. Could not check means the site or its DNS did not give an answer Keel could grade, such as a timeout.

The area passes when all of these hold:

  • The site serves a certificate a browser trusts over HTTPS, with more than 14 days left before it expires.
  • The server refuses TLS 1.0 and TLS 1.1 handshakes.
  • The certificate is not signed with SHA-1 or MD5.
  • The response sends a Strict-Transport-Security header with a positive max-age.

If the site redirects to another host on the same domain, the response at the end of the redirect is graded too, so an edge that answers 525 or 526 for a failed origin handshake fails here. A site that cannot be reached over HTTPS at all fails this area. A timeout is Could not check.

The expiry, protocol and signature tests come from a separate TLS handshake. If it cannot tell whether TLS 1.0 or 1.1 is refused, or cannot read the signature algorithm, the area shows Could not check rather than a pass. The same happens when the handshake cannot run at all. A pass always means every condition above was confirmed.

Keel reads the headers from the one response the TLS area graded. The area passes when that response sends all of these:

  • HSTS with a max-age of at least 180 days.
  • X-Content-Type-Options: nosniff.
  • Frame protection: a Content-Security-Policy frame-ancestors directive that limits framing, or X-Frame-Options set to DENY or SAMEORIGIN. When a frame-ancestors directive is present it decides the result, because browsers ignore X-Frame-Options in that case.

Whether a Content-Security-Policy is set is reported for information and never changes the result. This is a sample of one host’s home page. Other hosts and paths on the domain are not checked.

SPF and DMARC decide the area, and both must pass:

  • SPF passes when the domain publishes exactly one v=spf1 record, it ends in ~all or -all (directly or through one redirect=), and evaluating it takes no more than 10 DNS lookups.
  • DMARC passes when _dmarc.<domain> holds exactly one record with p=quarantine or p=reject. A pct below 100 fails, because receivers then apply the policy to only part of the failing mail.

DKIM and MTA-STS are reported for information. Keel looks for a DKIM key at a short list of common selectors, and a miss reads “Not found” rather than as a failure, since a domain can sign with any selector name. Whether outbound mail is actually signed, and whether inbound mail is verified, cannot be seen from outside.

  • Certificate revocation.
  • Exposed ports, unless you ask for them on a verified domain (see Exposed ports). A normal check makes HTTPS requests to the site’s home page, following same-site redirects, fetches the MTA-STS policy file when one is published, looks up public DNS, and runs TLS handshakes on port 443 that close before any request is sent. It connects to no other port.

This is not a PCI ASV scan. Keel is not an Approved Scanning Vendor, and a result here does not count toward a PCI DSS external scan.

On a domain your workspace has verified, you can add a check of whether the apex and www accept TCP connections on a fixed list of ports. It is off unless you ask for it, and it runs alongside the three areas above, which are graded as usual.

In the Run a check form, pick a verified domain from the list, tick the consent box and click Run check including exposed ports. The passive areas and the exposed ports run together as one check. The box reads, for example.com:

I own example.com or am authorised by its owner, and I authorise Keel to attempt TCP connections to N listed ports on example.com and www.example.com. Keel sends no data, does not log in and does not test for vulnerabilities. I also confirm the servers example.com and www.example.com point to are mine or I am authorised to test them.

In the app, N is the number of ports in the two tables below.

Without the tick, the ports are not checked and the other three areas run as usual. Keel stores the version of this wording and the time you ticked it with the run, along with the addresses it was given to check.

Keel looks up the _oscuro-verify TXT record again when you run it. If the record is gone, or this workspace has not verified the domain, the ports are not checked and the three other areas run on their own, with a notice saying why.

Keel looks up every IPv4 and IPv6 address that the apex and www.<domain> resolve to, and connects to each one. Up to 8 distinct addresses are checked. A domain that resolves to more than 8 shows Could not check, because checking only some of them could pass a domain whose other addresses have a port open. If either name points to an address that is not public, such as a private or loopback range, Keel does not run the port check at all.

Each connection is a TCP connect and nothing more. Keel sends no data over it, does not log in, and does not test for vulnerabilities.

An open port on any address fails the area:

Port Commonly used for
21 FTP
23 Telnet
445 SMB
3389 RDP
3306 MySQL
5432 PostgreSQL
6379 Redis
9200 Elasticsearch
27017 MongoDB
11211 Memcached

Ports 22 (SSH), 25 (SMTP) and 587 (mail submission) are reported for information. An open one is listed in the result and never fails the area.

A port that accepts the connection is Open. A port that refuses it, or does not answer in time, counts as closed; the result shows these as Closed and Filtered. A port Keel could not get a definite answer from shows Could not check.

  • Any port from the fail list open on any address: Fail.
  • Otherwise, any port from the fail list that could not be checked, or any address or name that was not checked (for example because the domain started resolving to a different address during the run): Could not check.
  • Otherwise: Pass.

A port that could not be checked never counts as a pass.

The port result shows only inside the workspace that ran it, on the External posture check page. The public result page never shows it, not even as Pass or Fail.

  • A domain can have its ports checked once per hour across all of Keel. If any workspace checked it in the last hour, the ports are not checked and the notice says so.
  • Each workspace can run 10 port checks per UTC day, across all its domains.
  • Each workspace can connect to 40 distinct addresses per UTC day. If a run would go past that, its ports are not checked.
  • Any one address can be checked at most 10 times an hour across all of Keel. A domain behind a shared address, such as a CDN, may have to wait.

If Keel cannot confirm these limits, the ports are not checked.

The result covers the listed ports on the addresses of the apex and www, seen from one point on the internet. It does not cover:

  • Any other port.
  • UDP.
  • Any hostname other than the apex and www.
  • Internal networks.
  • What the servers allow from other source addresses. A firewall that admits only some addresses can look closed here and still be open to them.

You need a Keel sign-in to run a check. Checks of domains your workspace has not verified count toward a daily limit set by your plan:

Plan Unverified domains per day
Free 5
Starter 20
Pro 50
Enterprise 100
MSP / Partner 100

The limit counts domains, not runs. Checking the same domain again on the same day in the same workspace does not use another slot. Days are counted in UTC.

There is also a ceiling per person. Across every workspace you belong to, you can check no more unverified domains in a day than the highest single limit among those workspaces. Opening a second Free workspace does not give you a second 5.

If anyone checked an unverified domain in the last 24 hours, Keel shows that saved result instead of running again, and it does not count toward either limit.

Verified domains have no daily limit and are never served from the cache. Your workspace can run a check of the same verified domain once every 10 minutes.

An owner or admin verifies a domain on the Your domains tab of the posture check:

  1. Enter the domain and click Add domain. Keel shows a token that starts oscuro-verify=.
  2. Publish it as a DNS TXT record named _oscuro-verify.<domain>. For example.com that is _oscuro-verify.example.com. If your DNS panel wants the host on its own, enter _oscuro-verify.
  3. Click Verify. If the record has not propagated yet, Keel says the record did not match, and you can try again a few minutes later.

The domain list is the same one Oscuro exposure monitoring uses. Adding a domain here does not turn on monitoring for it.

Leave the TXT record in place. Keel looks it up again on every check. If the record is gone, or the lookup fails, the check runs as if the domain were unverified: it counts toward the daily limit, saves no findings and files no evidence. This is stricter than exposure monitoring, which checks the record only once, when you click verify.

For a domain your workspace has verified, the page lists each finding in every area with its detail (for example the HSTS max-age it saw, or how many SPF lookups the record used) and what the area did not check. Results for an unverified domain show only the result per area.

Each verified check records one automated check per area and files dated evidence against a control in your workspace:

Area Control
TLS Encryption in transit & at rest (encryption)
Security headers Secure configuration & baselines (secure-configuration-management)
Mail authentication Email authentication & anti-spoofing (email-authentication)
Exposed ports, when run Network security controls (network-security)

The evidence is partial, and its text says so: it records what was seen from outside and names what was not checked. Only a pass files evidence. A later check that fails or cannot check an area withdraws that area’s evidence, the same rule every automated check in Keel follows.

For exposed ports, a pass files partial evidence naming the ports and addresses checked and what was not checked. A later port run that fails or shows Could not check withdraws it. A check run without the exposed ports leaves that evidence as it was.

Every domain that has been checked has a page at /posture/<domain> on the app, which anyone can open without signing in. It shows the time of the last check and, for each area, Pass, Fail or Could not check, along with the pass rule and the limits above. It covers TLS, security headers and mail authentication only; exposed ports never appear. It never shows findings, so the page cannot be read as a list of weaknesses. The details are for the domain’s owner, once verified.

The page is not indexed by search engines; it exists for whoever ran the check to share it. A check where every area came back as Could not check does not replace the result already on the page.