This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new 7d32661c47 [ENHANCEMENT] DKIMHook - improve logging (#2702)
7d32661c47 is described below
commit 7d32661c47136c8dcf7b873dfc3fe9d447e30f4b
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Apr 18 08:50:05 2025 +0200
[ENHANCEMENT] DKIMHook - improve logging (#2702)
* [ENHANCEMENT] DKIMHook: fix inacurate warning log when multiple signature
The previous 'OR' was logging a failure if the first dtoken is not the
expected one which is misleading.
We also add a debug log upon hook
* [ENHANCEMENT] DKIMHook: log messageId
This ease correlation with 3rd party email system logs
---------
Co-authored-by: Rene Cordier <[email protected]>
---
.../org/apache/james/DKIMHookIntegrationTest.java | 2 +-
.../java/org/apache/james/smtpserver/DKIMHook.java | 68 +++++++++++-----------
.../org/apache/james/smtpserver/DKIMHookTest.java | 38 ++++++------
3 files changed, 53 insertions(+), 55 deletions(-)
diff --git
a/server/apps/memory-app/src/test/java/org/apache/james/DKIMHookIntegrationTest.java
b/server/apps/memory-app/src/test/java/org/apache/james/DKIMHookIntegrationTest.java
index ac092e7aae..33bb4a3193 100644
---
a/server/apps/memory-app/src/test/java/org/apache/james/DKIMHookIntegrationTest.java
+++
b/server/apps/memory-app/src/test/java/org/apache/james/DKIMHookIntegrationTest.java
@@ -187,7 +187,7 @@ public class DKIMHookIntegrationTest {
server.write(ByteBuffer.wrap(("QUIT\r\n").getBytes(StandardCharsets.UTF_8)));
assertThat(new String(dataResponse, StandardCharsets.UTF_8))
- .contains("530 DKIM check failed. Wrong d token. Expecting
avocat.fr");
+ .contains("530 DKIM check failed. Wrong d token. Expecting
[avocat.fr]. Got [preprod-avocat.fr]");
}
@Test
diff --git
a/server/protocols/protocols-smtp-dkim/src/main/java/org/apache/james/smtpserver/DKIMHook.java
b/server/protocols/protocols-smtp-dkim/src/main/java/org/apache/james/smtpserver/DKIMHook.java
index 9425ba9206..746a86ca42 100644
---
a/server/protocols/protocols-smtp-dkim/src/main/java/org/apache/james/smtpserver/DKIMHook.java
+++
b/server/protocols/protocols-smtp-dkim/src/main/java/org/apache/james/smtpserver/DKIMHook.java
@@ -146,29 +146,19 @@ public class DKIMHook implements JamesMessageHook {
@FunctionalInterface
interface SignatureRecordValidation {
static SignatureRecordValidation and(SignatureRecordValidation a,
SignatureRecordValidation b) {
- return (sender, records) -> {
- HookResult hookResult = a.validate(sender, records);
+ return (sender, messageId, records) -> {
+ HookResult hookResult = a.validate(sender, messageId, records);
if (hookResult.equals(HookResult.DECLINED)) {
- return b.validate(sender, records);
+ return b.validate(sender, messageId, records);
}
return hookResult;
};
}
- static SignatureRecordValidation or(SignatureRecordValidation a,
SignatureRecordValidation b) {
- return (sender, records) -> {
- HookResult hookResult = a.validate(sender, records);
- if (hookResult.equals(HookResult.DECLINED)) {
- return hookResult;
- }
- return b.validate(sender, records);
- };
- }
-
static SignatureRecordValidation signatureRequired(boolean required) {
- return (sender, records) -> {
+ return (sender, messageId, records) -> {
if (required && (records == null || records.isEmpty())) {
- LOGGER.warn("DKIM check failed. Expecting DKIM signatures.
Got none.");
+ LOGGER.warn("DKIM check failed for {}. Expecting DKIM
signatures. Got none.", messageId);
return HookResult.builder()
.hookReturnCode(HookReturnCode.deny())
.smtpReturnCode(AUTH_REQUIRED)
@@ -179,24 +169,35 @@ public class DKIMHook implements JamesMessageHook {
};
}
- static SignatureRecordValidation expectedDToken(String dToken) {
- return (sender, records) -> {
- if (records.stream().anyMatch(record ->
record.getDToken().equals(dToken))) {
- return HookResult.DECLINED;
- }
- LOGGER.warn("DKIM check failed. Wrong d token. Expecting {}.
Got {}.", dToken,
-
records.stream().map(SignatureRecord::getDToken).collect(ImmutableSet.toImmutableSet()));
- return HookResult.builder()
- .hookReturnCode(HookReturnCode.deny())
- .smtpReturnCode(AUTH_REQUIRED)
- .smtpDescription("DKIM check failed. Wrong d token.
Expecting " + dToken)
- .build();
- };
+ SignatureRecordValidation ALLOW_ALL = (sender, messageId, records) ->
HookResult.DECLINED;
+
+ HookResult validate(MaybeSender maybeSender, String messageId,
List<SignatureRecord> records);
+ }
+
+ static class DTokenSignatureRecordValidation implements
SignatureRecordValidation {
+ private final List<String> expectedDTokens;
+
+ DTokenSignatureRecordValidation(List<String> expectedDTokens) {
+ this.expectedDTokens = expectedDTokens;
}
- SignatureRecordValidation ALLOW_ALL = (sender, records) ->
HookResult.DECLINED;
+ DTokenSignatureRecordValidation(String expectedDToken) {
+ this(ImmutableList.of(expectedDToken));
+ }
- HookResult validate(MaybeSender maybeSender, List<SignatureRecord>
records);
+ @Override
+ public HookResult validate(MaybeSender maybeSender, String messageId,
List<SignatureRecord> records) {
+ if (records.stream().anyMatch(r ->
expectedDTokens.stream().anyMatch(d -> r.getDToken().equals(d)))) {
+ return HookResult.DECLINED;
+ }
+ ImmutableSet<CharSequence> actualDToken =
records.stream().map(SignatureRecord::getDToken).collect(ImmutableSet.toImmutableSet());
+ LOGGER.warn("DKIM check failed for {}. Wrong d token. Expecting
{}. Got {}.", messageId, expectedDTokens, actualDToken);
+ return HookResult.builder()
+ .hookReturnCode(HookReturnCode.deny())
+ .smtpReturnCode(AUTH_REQUIRED)
+ .smtpDescription("DKIM check failed. Wrong d token. Expecting
" + expectedDTokens + ". Got " + actualDToken)
+ .build();
+ }
}
public static class Config {
@@ -260,10 +261,7 @@ public class DKIMHook implements JamesMessageHook {
SignatureRecordValidation signatureRecordValidation() {
return SignatureRecordValidation.and(
SignatureRecordValidation.signatureRequired(signatureRequired),
- expectedDToken.map(tokens -> tokens.stream()
- .map(SignatureRecordValidation::expectedDToken)
- .reduce(SignatureRecordValidation::or)
- .get()).orElse(SignatureRecordValidation.ALLOW_ALL));
+
expectedDToken.<SignatureRecordValidation>map(DTokenSignatureRecordValidation::new).orElse(SignatureRecordValidation.ALLOW_ALL));
}
private ImmutableList<DKIMCheckNeeded> computeDKIMChecksNeeded(Domain
domain) {
@@ -326,7 +324,7 @@ public class DKIMHook implements JamesMessageHook {
return HookResult.DECLINED;
}
try {
- return signatureRecordValidation.validate(mail.getMaybeSender(),
+ return signatureRecordValidation.validate(mail.getMaybeSender(),
mail.getMessage().getMessageID(),
verifier.verify(mail.getMessage(), config.forceCRLF));
} catch (MessagingException e) {
LOGGER.warn("Error while verifying DKIM signatures", e);
diff --git
a/server/protocols/protocols-smtp-dkim/src/test/java/org/apache/james/smtpserver/DKIMHookTest.java
b/server/protocols/protocols-smtp-dkim/src/test/java/org/apache/james/smtpserver/DKIMHookTest.java
index 710dc09bf5..3838c5a967 100644
---
a/server/protocols/protocols-smtp-dkim/src/test/java/org/apache/james/smtpserver/DKIMHookTest.java
+++
b/server/protocols/protocols-smtp-dkim/src/test/java/org/apache/james/smtpserver/DKIMHookTest.java
@@ -122,7 +122,7 @@ class DKIMHookTest {
@Test
void shouldDenyWhenNoDkimRecordsWhenTrue() {
assertThat(DKIMHook.SignatureRecordValidation.signatureRequired(true)
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of()))
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of()))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.deny()))
.satisfies(hookResult ->
assertThat(hookResult.getSmtpDescription()).isEqualTo("DKIM check failed.
Expecting DKIM signatures. Got none."));
}
@@ -130,28 +130,28 @@ class DKIMHookTest {
@Test
void shouldDeclineWhenNoDkimRecordWhenTrue() {
assertThat(DKIMHook.SignatureRecordValidation.signatureRequired(false)
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of()))
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of()))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.declined()));
}
@Test
void shouldDeclineWhenDkimRecordWhenTrue() {
assertThat(DKIMHook.SignatureRecordValidation.signatureRequired(true)
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of(SIGNATURE_RECORD_1)))
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of(SIGNATURE_RECORD_1)))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.declined()));
}
@Test
void allShouldDeclineWhenNoDkimRecord() {
assertThat(DKIMHook.SignatureRecordValidation.ALLOW_ALL
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of()))
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of()))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.declined()));
}
@Test
void allShouldDeclineWhenDkimRecord() {
assertThat(DKIMHook.SignatureRecordValidation.ALLOW_ALL
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of(SIGNATURE_RECORD_1)))
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of(SIGNATURE_RECORD_1)))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.declined()));
}
@@ -160,7 +160,7 @@ class DKIMHookTest {
assertThat(DKIMHook.SignatureRecordValidation.and(
DKIMHook.SignatureRecordValidation.ALLOW_ALL,
DKIMHook.SignatureRecordValidation.ALLOW_ALL)
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of()))
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of()))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.declined()));
}
@@ -169,7 +169,7 @@ class DKIMHookTest {
assertThat(DKIMHook.SignatureRecordValidation.and(
DKIMHook.SignatureRecordValidation.signatureRequired(true),
DKIMHook.SignatureRecordValidation.ALLOW_ALL)
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of()))
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of()))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.deny()))
.satisfies(hookResult ->
assertThat(hookResult.getSmtpDescription()).isEqualTo("DKIM check failed.
Expecting DKIM signatures. Got none."));
}
@@ -179,7 +179,7 @@ class DKIMHookTest {
assertThat(DKIMHook.SignatureRecordValidation.and(
DKIMHook.SignatureRecordValidation.ALLOW_ALL,
DKIMHook.SignatureRecordValidation.signatureRequired(true))
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of()))
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of()))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.deny()))
.satisfies(hookResult ->
assertThat(hookResult.getSmtpDescription()).isEqualTo("DKIM check failed.
Expecting DKIM signatures. Got none."));
}
@@ -188,38 +188,38 @@ class DKIMHookTest {
void andShouldKeepOnlyTheFirstSMTPDescription() {
assertThat(DKIMHook.SignatureRecordValidation.and(
DKIMHook.SignatureRecordValidation.signatureRequired(true),
-
DKIMHook.SignatureRecordValidation.expectedDToken("wrong.com"))
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of()))
+ new DKIMHook.DTokenSignatureRecordValidation("wrong.com"))
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of()))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.deny()))
.satisfies(hookResult ->
assertThat(hookResult.getSmtpDescription()).isEqualTo("DKIM check failed.
Expecting DKIM signatures. Got none."));
}
@Test
void expectedDTokenShouldDenyWhenWrongDToken() {
-
assertThat(DKIMHook.SignatureRecordValidation.expectedDToken("wrong.com")
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of(SIGNATURE_RECORD_1)))
+ assertThat(new
DKIMHook.DTokenSignatureRecordValidation("wrong.com")
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of(SIGNATURE_RECORD_1)))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.deny()))
- .satisfies(hookResult ->
assertThat(hookResult.getSmtpDescription()).isEqualTo("DKIM check failed. Wrong
d token. Expecting wrong.com"));
+ .satisfies(hookResult ->
assertThat(hookResult.getSmtpDescription()).isEqualTo("DKIM check failed. Wrong
d token. Expecting [wrong.com]. Got [linagora.com]"));
}
@Test
void expectedDTokenShouldDeclineWhenGoodDToken() {
-
assertThat(DKIMHook.SignatureRecordValidation.expectedDToken("linagora.com")
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of(SIGNATURE_RECORD_1)))
+ assertThat(new
DKIMHook.DTokenSignatureRecordValidation("linagora.com")
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of(SIGNATURE_RECORD_1)))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.declined()));
}
@Test
void expectedDTokenShouldDeclineWhenGoodFirstDToken() {
-
assertThat(DKIMHook.SignatureRecordValidation.expectedDToken("linagora.com")
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of(SIGNATURE_RECORD_1, SIGNATURE_RECORD_2)))
+ assertThat(new
DKIMHook.DTokenSignatureRecordValidation("linagora.com")
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of(SIGNATURE_RECORD_1, SIGNATURE_RECORD_2)))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.declined()));
}
@Test
void expectedDTokenShouldDeclineWhenGoodSecondDToken() {
-
assertThat(DKIMHook.SignatureRecordValidation.expectedDToken("linagora.com")
- .validate(MaybeSender.getMailSender("bob@localhost"),
ImmutableList.of(SIGNATURE_RECORD_2, SIGNATURE_RECORD_1)))
+ assertThat(new
DKIMHook.DTokenSignatureRecordValidation("linagora.com")
+ .validate(MaybeSender.getMailSender("bob@localhost"),
"messageId", ImmutableList.of(SIGNATURE_RECORD_2, SIGNATURE_RECORD_1)))
.satisfies(hookResult ->
assertThat(hookResult.getResult()).isEqualTo(HookReturnCode.declined()));
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]