Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JAMES-3823 SMTP Require TLS Option #2460

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This page details standards implemented by the {server-name}.
- link:https://datatracker.ietf.org/doc/html/rfc2197[RFC-2197] SMTP Service Extension for Command Pipelining
- link:https://datatracker.ietf.org/doc/html/rfc2554[RFC-2554] ESMTP Service Extension for Authentication
- link:https://datatracker.ietf.org/doc/rfc6710/[RFC-6710] SMTP Extension for Message Transfer Priorities
- link:https://datatracker.ietf.org/doc/rfc8689/[RFC-8689] SMTP Require TLS Option

== LMTP

Expand Down
22 changes: 22 additions & 0 deletions docs/modules/servers/partials/configure/smtp-hooks.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ The Distributed server has optional support for SMTP Extension for Message Trans

The SMTP server does not allow positive priorities from unauthorized sources and sets the priority to the default value (0).

[source,xml]
....
<smtpserver enabled="true">
<...> <!-- The rest of your SMTP configuration, unchanged -->
Expand All @@ -347,6 +348,27 @@ The SMTP server does not allow positive priorities from unauthorized sources and
</smtpserver>
....

== SMTP Require TLS Option hooks

These hooks are designed to support the SMTP service extension, REQUIRETLS, and a TLS-Required message header field.
(link:https://www.rfc-editor.org/rfc/rfc8689.html[RFC-8689])

[source,xml]
....
<smtpserver enabled="true">
<...> <!-- The rest of your SMTP configuration, unchanged -->
<tls socketTLS="false" startTLS="true">``
<!-- ... -->
</tls>
<handlerchain>
<handler class="org.apache.james.smtpserver.tls.SmtpTlsEhloHook"/>
<handler class="org.apache.james.smtpserver.tls.SmtpTlsParameterHook"/>
<handler class="org.apache.james.smtpserver.tls.SmtpTlsMessageHook"/>
<handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/>
</handlerchain>
</smtpserver>
....

== DKIM checks hooks

Hook for verifying DKIM signatures of incoming mails.
Expand Down
4 changes: 2 additions & 2 deletions server/apps/jpa-app/src/test/resources/smtpserver.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
<jmxName>smtpserver-authenticated</jmxName>
<bind>0.0.0.0:0</bind>
<connectionBacklog>200</connectionBacklog>
<tls socketTLS="false" startTLS="false">
<tls socketTLS="false" startTLS="true">
<keystore>file://conf/keystore</keystore>
<secret>james72laBalle</secret>
<provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider>
Expand All @@ -92,7 +92,7 @@
<connectionLimitPerIP>0</connectionLimitPerIP>
<auth>
<announce>forUnauthorizedAddresses</announce>
<requireSSL>false</requireSSL>
<requireSSL>true</requireSSL>
<plainAuthEnabled>true</plainAuthEnabled>
</auth>
<!-- Trust authenticated users -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,17 @@ public static class Builder {
private static final String DEFAULT_DISABLED = "0";

private Optional<Boolean> authRequired;
private Optional<Boolean> startTls;
private Optional<String> maxMessageSize;
private Optional<SMTPConfiguration.SenderVerificationMode> verifyIndentity;
private Optional<Boolean> bracketEnforcement;
private Optional<String> authorizedAddresses;
private ImmutableList.Builder<HookConfigurationEntry> additionalHooks;
private final ImmutableList.Builder<HookConfigurationEntry> additionalHooks;

public Builder() {
authorizedAddresses = Optional.empty();
authRequired = Optional.empty();
startTls = Optional.empty();
verifyIndentity = Optional.empty();
maxMessageSize = Optional.empty();
bracketEnforcement = Optional.empty();
Expand All @@ -101,6 +103,11 @@ public Builder requireAuthentication() {
return this;
}

public Builder requireStartTls() {
this.startTls = Optional.of(true);
return this;
}

public Builder requireBracketEnforcement() {
this.bracketEnforcement = Optional.of(true);
return this;
Expand Down Expand Up @@ -138,11 +145,12 @@ public Builder addHook(String hookFQCN, Map<String, String> hookConfig) {

public SmtpConfiguration build() {
return new SmtpConfiguration(authorizedAddresses,
authRequired.orElse(!AUTH_REQUIRED),
bracketEnforcement.orElse(true),
verifyIndentity.orElse(SMTPConfiguration.SenderVerificationMode.DISABLED),
maxMessageSize.orElse(DEFAULT_DISABLED),
additionalHooks.build());
authRequired.orElse(!AUTH_REQUIRED),
startTls.orElse(false),
bracketEnforcement.orElse(true),
verifyIndentity.orElse(SMTPConfiguration.SenderVerificationMode.DISABLED),
maxMessageSize.orElse(DEFAULT_DISABLED),
additionalHooks.build());
}
}

Expand All @@ -152,13 +160,15 @@ public static Builder builder() {

private final Optional<String> authorizedAddresses;
private final boolean authRequired;
private final boolean startTls;
private final boolean bracketEnforcement;
private final SMTPConfiguration.SenderVerificationMode verifyIndentity;
private final String maxMessageSize;
private final ImmutableList<HookConfigurationEntry> additionalHooks;

private SmtpConfiguration(Optional<String> authorizedAddresses,
boolean authRequired,
boolean startTls,
boolean bracketEnforcement,
SMTPConfiguration.SenderVerificationMode verifyIndentity,
String maxMessageSize,
Expand All @@ -168,6 +178,7 @@ private SmtpConfiguration(Optional<String> authorizedAddresses,
this.bracketEnforcement = bracketEnforcement;
this.verifyIndentity = verifyIndentity;
this.maxMessageSize = maxMessageSize;
this.startTls = startTls;
this.additionalHooks = additionalHooks;
}

Expand All @@ -180,10 +191,11 @@ public String serializeAsXml() throws IOException {
scopes.put("verifyIdentity", verifyIndentity.toString());
scopes.put("maxmessagesize", maxMessageSize);
scopes.put("bracketEnforcement", bracketEnforcement);
scopes.put("startTls", startTls);

List<Map<String, Object>> additionalHooksWithConfig = additionalHooks.stream()
.map(HookConfigurationEntry::asMustacheScopes)
.collect(ImmutableList.toImmutableList());
.map(HookConfigurationEntry::asMustacheScopes)
.collect(ImmutableList.toImmutableList());

scopes.put("hooks", additionalHooksWithConfig);

Expand All @@ -193,14 +205,14 @@ public String serializeAsXml() throws IOException {
Mustache mustache = mf.compile(getPatternReader(), "example");
mustache.execute(writer, scopes);
writer.flush();
return new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8);
return byteArrayOutputStream.toString(StandardCharsets.UTF_8);
}

private StringReader getPatternReader() throws IOException {
InputStream patternStream = ClassLoader.getSystemResourceAsStream("smtpserver.xml");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
IOUtils.copy(patternStream, byteArrayOutputStream);
String pattern = new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8);
String pattern = byteArrayOutputStream.toString(StandardCharsets.UTF_8);
return new StringReader(pattern);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<jmxName>smtpserver-global</jmxName>
<bind>0.0.0.0:0</bind>
<connectionBacklog>200</connectionBacklog>
<tls socketTLS="false" startTLS="false">
<tls socketTLS="false" startTLS="{{startTls}}">
<keystore>file://conf/keystore</keystore>
<secret>james72laBalle</secret>
<provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ public void authenticationCanBeRequired() throws IOException {
is("true")));
}

@Test
public void startTlsDisabledByDefault() throws IOException {

assertThat(SmtpConfiguration.builder()
.build()
.serializeAsXml(),
hasXPath("//tls/@startTLS", is("false")));
}

@Test
public void startTlsCanBeCustomized() throws IOException {

assertThat(SmtpConfiguration.builder()
.requireStartTls()
.build().serializeAsXml(),
hasXPath("//tls/@startTLS", is("true")));
}

@Test
public void maxMessageSizeCanBeCustomized() throws IOException {
assertThat(SmtpConfiguration.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import org.apache.james.util.AuditTrail;
import org.apache.james.util.MDCBuilder;
import org.apache.mailet.Attribute;
import org.apache.mailet.AttributeName;
import org.apache.mailet.AttributeValue;
import org.apache.mailet.Mail;
import org.apache.mailet.MailetContext;
Expand All @@ -66,7 +65,6 @@ public class DeliveryRunnable implements Disposable {
public static final Supplier<Date> CURRENT_DATE_SUPPLIER = Date::new;
public static final String OUTGOING_MAILS = "outgoingMails";
public static final String REMOTE_DELIVERY_TRIAL = "RemoteDeliveryTrial";
private static final AttributeName MAIL_PRIORITY_ATTRIBUTE_NAME = AttributeName.of("MAIL_PRIORITY");

private final MailQueue queue;
private final RemoteDeliveryConfiguration configuration;
Expand Down
Loading