8000 Allow and control sending UTF-8 emails in the default email sender impl by rmartinc · Pull Request #41715 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

Allow and control sending UTF-8 emails in the default email sender impl - #41715

Merged
ahus1 merged 4 commits into
keycloak:mainfrom
rmartinc:issue-41023
Aug 15, 2025
Merged

ahus1 merged 4 commits into
keycloak:mainfrom
rmartinc:issue-41023

Conversation

@rmartinc
@rmartinc rmartinc commented Aug 7, 2025
Copy link
Copy Markdown
Contributor

Closes #41023

Adding the allowutf8 option to the smtp configuration to configure mail.mime.allowutf8 in the jakarta configuration with it. This allows to send and control emails with UTF-8 characters if the SMTP server supports it. Test added.

@rmartinc
rmartinc requested review from a team as code owners August 7, 2025 09:27
jonkoops
jonkoops previously approved these changes Aug 7, 2025
@jonkoops jonkoops left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

ahus1
ahus1 previously requested changes Aug 8, 2025
@ahus1 ahus1 left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR has this setting now to "false", which might lead to some emails no longer being handed over to the SMTP-server.

So I wonder:

  • Should the default be "true" for new SMTP configurations?
  • Is this a notable change, that should mention the new option, as those migrating should review their configuration and set it to "true"?

Something that is IMHO missing:

  • If we validate the "from" address like above, the validation should also be performed when we save the configuration.

Something that we could test:

  • Does the server support UTF8 - this can be tested by the following - which unfortunately calls the implementation, and not the interface. The Angus mail implementation only logs an INFO is there is a mismatch.
    ((SMTPTransport) transport).supportsExtension("SMTPUTF8")

See some more comments below. Feel free to split those ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message is not actionable as it doesn't give a hint what to change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed to: Invalid address '%s'. If the address contains UTF-8 characters in the local part please ensure the SMTP server supports SMTPUTF8 extensions and enable 'Allow UTF-8' in the email realm configuration.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, this is not actionable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to: Invalid sender address '%s'. If the address contains UTF-8 characters in the local part please ensure the SMTP server supports SMTPUTF8 extensions and enable 'Allow UTF-8' in the email realm configuration.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is IMHO only half the truth: When not enabling allowuft8, I won't be able to send emails from or to an email address that contains UTF8 characters before the "@". So any user that has entered a domain with such a special character will not be able to receive emails.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have just copied the description of the mail.mime.allowutf8 option. What do you prefer prefer here? Say that is only for local part?

@ahus1 ahus1 Aug 12, 2025 •
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would bring this into context for a Keycloak admin. For Keycloak, this would only be about addresses, and no other headers.

It would be good to make it more specific about the local parts.

"Enable to allow UTF-8 characters in the local part of the email address. This should only be enabled if the mail server supports UTF-8 via the SMTPUTF8 extension. If disabled, domain names containing UTF-8 characters will be encoded using punycode, and addresses containing UTF-8 characters in the local part of the address will return an error."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okis, changed in the documentation and for the admin console help message.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too restrictive, and IMHO points to a different problem: you can have an email address with a domain name that is IDN encoded.
It seems to me that we should pny-encode those domain names before sending them.

Interesting enough the frontend for setting the sender address will encode the email address in pnycode automatically, while a user's email address when fetched from the database when sending the test email will not be pny-encoded.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okis, I have changed to use allowutf8 and, if not set, change to the IDN.toASCCI method in the domain part. Note that this was exactly the same before.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to make this option "true" by default? Is the UTF8 mode supported by the recent versions of the popular SMTP servers?

@rmartinc rmartinc Aug 11, 2025 •
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to not do that, at least for now. If we set it to true by default we need to add a task to migrate the realms and add the false (if not, when edited by the admin console it would change to true). We can do in a follow-up only for the main branch. But as commented to @ahus1 we can do it, but I would not backport that part.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm absolutely fine with this approach!

@rmartinc
Copy link
Copy Markdown
Contributor Author

@ahus1 I have done some changes but not all of them 😄. In general the allowutf8 is false by default because it was false before (it was not set and it's false by default in the jakarta mail impl). So we are no limiting previous configurations, it is working as before but now you can configure that prop to allow UTF-8. Previously there was an error sending the email (check issue) or worse because it was not controlled, and now there is a specific check that throws an exception.

Should the default be "true" for new SMTP configurations?

I wouldn't do that, nobody complained about this until now. We can do it, but then I need to add a migration task to add false to current realms. If everybody agrees, I can work on it.

Is this a notable change, that should mention the new option, as those migrating should review their configuration and set it to "true"?

No, I don't think that it is a notable change. The option is false as before, so we are not changing the behavior.

If we validate the "from" address like above, the validation should also be performed when we save the configuration.

I added that part. But I'm not testing the email completely, just statically. If I add a full test (test SMTP connection, username/password and so on and so forth), there are tons of tests that fail (because they are importing a realm with email configuration and the SMTP server is not setup).

Does the server support UTF8 - this can be tested by the following - which unfortunately calls the implementation, and not the interface. The Angus mail implementation only logs an INFO is there is a mismatch.

We could add something similar to that, but I have not done it, mainly because the greenmail we use for testing does not announce SMTPUTF8 (I prefer testing something). The admin should know if they want/support UTF-8 or not. We can check if SMTPUTF8 extension is listed when connecting to send the email, and throw an exception if it is configured and not present. But, as commented, that would break testing. If everybody thinks that it's better, I can add the check when sending an email.

@rmartinc
Copy link
Copy Markdown
Contributor Author

It seems CI fails because we are waiting for #41794. I will rebase tomorrow.

@rmartinc
rmartinc force-pushed the issue-41023 branch 3 times, most recently from e669fe8 to 9363b25 Compare August 12, 2025 08:49
@ahus1
ahus1 commented Aug 12, 2025
Copy link
Copy Markdown
Member

I disagree with the following statement:

Previously there was an error sending the email (check issue) or worse because it was not controlled, and now there is a specific check that throws an exception.

We know about one email server that is supporting the SMTPUTF8 extension that is throwing an exception. Other email servers might just accept it. Now we are always throwing an exception when there is a local part with UTF-8 characters. So to me this is a breaking change.

I vote to remove the check if UTF-8 characters are part of the local part of the email to be backwards compatible. No-one complained that we didn't do that for the past 10 years.

@rmartinc
Copy link
Copy Markdown
Contributor Author

We know about one email server that is supporting the SMTPUTF8 extension that is throwing an exception. Other email servers might just accept it. Now we are always throwing an exception when there is a local part with UTF-8 characters. So to me this is a breaking change.

I vote to remove the check if UTF-8 characters are part of the local part of the email to be backwards compatible. No-one complained that we didn't do that for the past 10 years.

@ahus1 The angus mail implementation sends the email when the allowutf8 option is false to just the lower byte (if there are two in UTF-8). If you send an email for example to admin@localhostñ.com you will see this admin@localhost�.com in the log:

DEBUG SMTP: connected to host "localhost", port: 25
EHLO localhost
250-li-a1c93acc-2366-11b2-a85c-b79ae5c81821.ibm.com Hello localhost [127.0.0.1])
250-PIPELINING
250-ENHANCEDSTATUSCODES
250 8BITMIME
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: use8bit false
MAIL FROM:<keycloak@localhost.com>
250 2.1.0 Sender <keycloak@localhost.com> OK
RCPT TO:<admin@localhost�.com>
553 5.1.3 Syntax error in recipient address
DEBUG SMTP: Invalid Addresses
DEBUG SMTP:   admin@localhostñ.com
DEBUG SMTP: Sending failed because of invalid destination addresses
RSET
250 2.0.0 OK
DEBUG SMTP: MessagingException while sending, THROW: 
jakarta.mail.SendFailedException: Invalid Addresses;
  nested exception is:
	org.eclipse.angus.mail.smtp.SMTPAddressFailedException: 553 5.1.3 Syntax error in recipient address

This means that you can do weird things and the reason that this is managed as important issue (not an enhancement), we have a moderate CVE-2025-8419 related to this (UTF-8 characters are transformed without any control and an attacker can inject into the SMTP, not much because the email is very short, but it's moderate). So, the important part of this PR is that we should control the email and reject invalid ones to avoid the issue.

@rmartinc
Copy link
Copy Markdown
Contributor Author

I'm going to add an upgrading note just in case. I don't think that this will affect a lot of people, but it's not the first time I've failed in a prediction like this one. 😄
@ahus1 Don't approve it yet, I'll add the note during the day.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: Maybe validate would be a better name. At least it's more commonly used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, you are right. Changed!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a valid Javadoc would be useful.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: These seem to be separate test cases. Perhaps it would be useful to put them in separate test methods? Maybe we could even execute them in parallel?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it's better here, we have everything to test email in this class and the class is fast to test (it's not one of the worse ones9.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach works for me as well. Resolving.

@rmartinc
rmartinc commented Aug 12, 2025 •
Copy link
Copy Markdown
Contributor Author

Ok, I have added the upgrading note and done 2 of 3 of the suggestions from @slaskawi. Let's wait the CI now. I have also squashed the commits into just one.

@mposolda mposolda self-assigned this Aug 12, 2025
@ahus1
ahus1 commented Aug 12, 2025
Copy link
Copy Markdown
Member

@rmartinc - I've added one more commit to the docs to give admin who are migrating some hands-on advice. For example, without updating the email validation rule, users might register with an email address that contains non-ASCII characters, and then they wouldn't be able to confirm their email address.

I also fixed something that I missed before: While there are ASCII and non-ASCII characters, UTF-8 is an encoding and therefore doesn't have any characters. So I've update the statements.

It is getting quite late here, so please re-read those paragraphs. I'll mark my earlier review now resolved, so Marek can eventually proceed.

@ahus1
ahus1 dismissed their stale review August 12, 2025 16:48

resolved

@slaskawi slaskawi left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@rmartinc
Copy link
Copy Markdown
Contributor Author

@ahus1 LGTM and adding another check for emails if you don't support non-ascii in the local part makes lot of sense. I have just changed the expression to use entities and avoid the issue with the variables.

@rmartinc
Copy link
Copy Markdown
Contributor Author

One more comment, note that we can create a new email validator that, if smtp configuration in the realm does not allow UTF-8, checks that the email local part has only ascii characters. This validator can be assigned automatically using User Profile. But I don't know if we are complicating this too much. But just in case.

mposolda
mposolda previously approved these changes Aug 15, 2025
@mposolda mposolda left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rmartinc @ahus1 @slaskawi @jonkoops PR looks good to me, Thanks! It would just need one additional rebase due the conflict in the documentation.

@ahus1 @slaskawi Are you also ok with the PR now? Feel free to re-approve or comment if it looks OK to you.

rmartinc and others added 3 commits August 15, 2025 11:13
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
…detail that admins should not care about.

Recommending to enable UTF-8. For the other scenario, describe the steps necessary to prevent having email addresses in the system that can't receive emails from Keycloak

Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
ahus1
ahus1 previously approved these changes Aug 15, 2025
Signed-off-by: rmartinc <rmartinc@redhat.com>
@ahus1
ahus1 commented Aug 15, 2025
Copy link
Copy Markdown
Member

I'm ok with the change, and just did the rebase.

@ahus1
ahus1 commented Aug 15, 2025
Copy link
Copy Markdown
Member

@rmartinc / @mposolda - it would be nice to have a follow-up issue as setting up SMTP now got quite complicated :(

@slaskawi slaskawi left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mposolda LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't send e-mails to international e-mail addresses: bad UTF-8 syntax

5 participants

0