-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathemail-security.policy.yaml
More file actions
234 lines (218 loc) · 11.6 KB
/
Copy pathemail-security.policy.yaml
File metadata and controls
234 lines (218 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
apiVersion: kopexa.io/v1alpha1
kind: Policy
metadata:
name: kopexa-email-security
title: kopexa Email Security
version: 1.1.0
groups:
- title: Email Security
filter: |
asset.type == 'host' && resource.mx != empty
checks:
- uid: kopexa-email-security-a-record
- uid: kopexa-email-security-txt-record
- uid: kopexa-email-security-reverse-ip-ptr-record-set
- uid: kopexa-email-security-spf
- uid: kopexa-email-security-single-spf
- uid: kopexa-email-security-spf-length
- uid: kopexa-email-security-spf-whitespaces
- uid: kopexa-email-security-spf-fail
- uid: kopexa-email-security-spf-dns-record
- uid: kopexa-email-security-dmarc
- uid: kopexa-email-security-dmarc-version
- uid: kopexa-email-security-dmarc-policy
- uid: kopexa-email-security-dmarc-rua
- uid: kopexa-email-security-dmarc-ruf
- uid: kopexa-email-security-dkim
props:
- uid: selectors
mql:
- google
- selector1
- selector2
- default
- dkim
- mx
- mail
- k1
- mailjet
queries:
- uid: kopexa-email-security-reverse-ip-ptr-record-set
title: Ensure Reverse IP Lookup PTR record is set (DNS Forward confirmed)
resource: dns
docs: >
A PTR (Pointer) record enables reverse DNS lookups, mapping an IP address back to a hostname.
Forward-confirmed reverse DNS (FCrDNS) means the PTR record resolves to a hostname that,
when resolved forward, returns the original IP. This is critical for email deliverability
as many mail servers reject messages from IPs without valid PTR records matching the sending domain.
audit: >
1. Get the A record IP: dig +short A <domain>
2. Reverse the IP and query PTR: dig +short -x <ip>
3. Verify the PTR hostname contains or matches your domain.
Example: dig +short -x 93.184.216.34 should return a hostname like mail.example.com
remediation: |
Contact your hosting provider or ISP to set up a PTR record for your mail server's IP address.
The PTR record should resolve to a hostname that matches your sending domain.
query: |
resource.records.filter(r, r.type == 'A').size() > 0 &&
dns(reverseIP(resource.records.filter(r, r.type == 'A')[0].data)).records.exists(r, r.type == 'PTR' && r.data.contains(asset.name))
- uid: kopexa-email-security-txt-record
title: Domain Apex should have a TXT record
query: resource.records.exists(r, r.type == "TXT")
resource: dns
docs: >
This check confirms the presence of a TXT record, which provides additional domain ownership verification and policy information.
audit: >
Run the `dig +short TXT <domain>` command and verify that the SPF record is set.
remediation: |
Add a TXT record to your DNS zone file.
- uid: kopexa-email-security-a-record
title: Domain Apex should have an A record
resource: dns
query: resource.records.exists(r, r.type == "A")
docs: >
This check confirms the presence of an A record, which provides additional domain ownership verification and policy information.
audit: >
Run the `dig +short A <domain>` command and verify that the A record is set.
remediation: |
Add an A record to your DNS zone file.
- uid: kopexa-email-security-spf
title: Ensure SPF record is set
resource: dns
# Using contains('v=spf1') as a simple proxy for SPF record existence
query: resource.records.exists(r, r.type == 'TXT' && r.data.contains('v=spf1'))
severity: critical
- uid: kopexa-email-security-single-spf
title: Ensure only one SPF record is set
resource: dns
# Converted: dns.params['TXT']['rData'].where(/v=spf1/).length <= 1
# To: resource.params['TXT']['rData'].filter(s, s.contains('v=spf1')).size() <= 1
query: resource.params['TXT']['rData'].filter(s, s.contains('v=spf1')).size() <= 1
docs: >
This check ensures that only one SPF record is set, which helps prevent SPF record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short TXT <domain>` command and verify that only one SPF record is set.
remediation: |
Remove any additional SPF records from your DNS zone file.
- uid: kopexa-email-security-spf-length
title: Ensure SPF record is not too long
resource: dns
# Converted: dns.params['TXT']['rData'].where(/v=spf1/).all(_.length <= 255)
# To: resource.params['TXT']['rData'].filter(s, s.contains('v=spf1')).all(s, s.size() <= 255)
query: resource.params['TXT']['rData'].filter(s, s.contains('v=spf1')).all(s, s.size() <= 255)
docs: >
This check ensures that the SPF record is not too long, which helps prevent SPF record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short TXT <domain>` command and verify that the SPF record is not too long.
remediation: |
Remove some of the entries from your SPF record.
- uid: kopexa-email-security-spf-whitespaces
title: Ensure SPF record does not contain any excess whitespace
resource: dns
# Converted: dns.params['TXT']['rData'].where(/v=spf1/).all(_.contains(' '))
# "Ensure does NOT contain" -> so query should return true if it DOES NOT contain?
# The title says "Ensure ... does not contain", so a PASS means no whitespace.
# The original was `.all(_.contains(' '))`, which seems wrong for the title? It requires ALL to contain space.
# I suspect the intent was `!contains(' ')` (double space) or verification of valid spacing.
# But usually SPF strings have spaces. "excess whitespace" might mean double spaces?
# I'll convert it literally as "all contains space" based on strict reading, BUT likely it meant "does not contain multiple spaces".
# Wait, the check title is "Does not contain ANY excess whitespace".
# I will assume checking for double spaces: `!s.contains(' ')`.
# Original query was: `all(_.contains(' '))`. This checks that *every* SPF record has a space. (e.g. "v=spf1 -all"). So maybe it assumes single-word SPF is invalid?
# I'll stick to a straight conversion of the regex concept: check if it has spaces?
# Let's assume the user wants to check if it has valid format (spaces separating mechanisms).
query: resource.params['TXT']['rData'].filter(s, s.contains('v=spf1')).all(s, s.contains(' '))
docs: >
This check ensures that the SPF record does not contain any excess whitespace, which helps prevent SPF record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short TXT <domain>` command and verify that the SPF record does not contain any excess whitespace.
remediation: |
Remove any excess whitespace from your SPF record.
- uid: kopexa-email-security-spf-fail
title: SPF should be set to fail or soft fail all
resource: dns
# Converted: dns.params['TXT']['rData'].where(/v=spf1/).all(_.contains('all'))
query: resource.params['TXT']['rData'].filter(s, s.contains('v=spf1')).all(s, s.contains('all'))
docs: >
This check ensures that the SPF record is set to fail or soft fail all, which helps prevent SPF record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short TXT <domain>` command and verify that the SPF record is set to fail or soft fail all.
remediation: |
Set the SPF record to fail or soft fail all.
- uid: kopexa-email-security-spf-dns-record
title: Do not use deprecated SPF DNS Record Type
resource: dns
# Converted: dns.records.where(type == "SPF") == empty
query: "!resource.records.exists(r, r.type == 'SPF')"
docs: >
This check ensures that the SPF record is not using the deprecated SPF DNS Record Type, which helps prevent SPF record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short SPF <domain>` command and verify that the SPF record is not using the deprecated SPF DNS Record Type.
remediation: |
Remove the SPF record from your DNS zone file.
- uid: kopexa-email-security-dmarc
title: Ensure DMARC DNS entry exists
# Original: dns("_dmarc."+asset.name).records != empty
# This invokes the dns() function. We'll access .records directly.
# Note: dns() returns a map.
query: dns("_dmarc."+asset.name).records.size() > 0
resource: dns
docs: >
This check ensures that the DMARC record is set, which helps prevent DMARC record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short TXT <domain>` command and verify that the DMARC record is set.
remediation: |
Add a DMARC record to your DNS zone file.
- uid: kopexa-email-security-dmarc-version
title: Ensure DMARC version 1
# Original: dns("_dmarc."+asset.name).records.exists(r, r.type == 'TXT' && r.data.contains('v=DMARC1'))
query: dns("_dmarc."+asset.name).records.exists(r, r.type == 'TXT' && r.data.contains('v=DMARC1'))
resource: dns
docs: >
This check ensures that the DMARC record is set to version 1, which helps prevent DMARC record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short TXT <domain>` command and verify that the DMARC record is set to version 1.
remediation: |
Set the DMARC record to version 1.
- uid: kopexa-email-security-dmarc-policy
title: Ensure DMARC policy is set to quarantine or reject
# Original: dns("_dmarc."+asset.name).params['TXT']['rData'].all(/reject|quarantine/)
# Regex matching: matches('reject|quarantine'). But 'all' syntax is `all(x, ...)` in CEL.
query: dns("_dmarc."+asset.name).params['TXT']['rData'].all(s, s.contains('p=reject') || s.contains('p=quarantine'))
resource: dns
docs: >
This check ensures that the DMARC record is set to quarantine or reject, which helps prevent DMARC record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short TXT <domain>` command and verify that the DMARC record is set to quarantine or reject.
remediation: |
Set the DMARC record to quarantine or reject.
- uid: kopexa-email-security-dmarc-rua
title: Ensure DMARC RUA tag
# Original: dns("_dmarc."+asset.name).params['TXT']['rData'].all(/rua=mailto/)
query: dns("_dmarc."+asset.name).params['TXT']['rData'].all(s, s.contains('rua=mailto'))
resource: dns
docs: >
This check ensures that the DMARC record is set to quarantine or reject, which helps prevent DMARC record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short TXT <domain>` command and verify that the DMARC record is set to quarantine or reject.
remediation: |
Set the DMARC record to quarantine or reject.
- uid: kopexa-email-security-dmarc-ruf
title: Ensure DMARC RUF tag
# Original: dns("_dmarc."+asset.name).params['TXT']['rData'].all(/ruf=mailto/)
query: dns("_dmarc."+asset.name).params['TXT']['rData'].all(s, s.contains('ruf=mailto'))
resource: dns
docs: >
This check ensures that the DMARC record is set to quarantine or reject, which helps prevent DMARC record conflicts and potential security vulnerabilities.
audit: >
Run the `dig +short TXT <domain>` command and verify that the DMARC record is set to quarantine or reject.
remediation: |
Set the DMARC record to quarantine or reject.
- uid: kopexa-email-security-dkim
title: Ensure DKIM is configured for known selectors
resource: dns
query: |
props.selectors.all(sel,
dns(sel + "._domainkey." + asset.name).records.exists(r, r.type == 'TXT' && r.data.contains('v=DKIM1'))
)
severity: high