Dovecot ACLs and XOAUTH2
Hi,
we're trying to set up an IMAP server using Dovecot as part of a new email system in a middle-sized company (around 150 employees). We have a keycloak identity server that is used for all logins and we would also like to use it for the new email system so that the non-technical employees (the majority) will have no issues using it.
It's very nice that XOAUTH2 is already included in Dovecot, it works very well with our Roundcube web client. However, it seems that other clients like Thunderbird or Android Apps like FairEmail are not easy to set up as they have their XOAUTH providers hardcoded somewhere (https://bugzilla.mozilla.org/show_bug.cgi?id=1602166). Are you aware of any solution to integrate a dovecot server using XOAUTH2 into local clients (like Thunderbird)? We are currently trying to do it with passwords that can be set in Keycloak only for the purpose of email that can then be used to receive a valid token via a direct access grant but it's not a really nice solution as it introduces an additional password.
Additionally, we would like to have all permission-related information saved in our identity server. For email, this includes shared mailboxes that a user is allowed to access. The ACL plugin that is used for shared mailboxes currently reads the permissions from disk which is not really feasible if the user base is large and setting these permissions should happen automatically. We are thinking the best way to do this is to encode the user's permissions in the token that dovecot receives in the login process. But as the token is only available in the authentication process and it does not seem to be intended to return such information from the password database (https://doc.dovecot.org/configuration_manual/authentication/password_databas...), we are unsure on how to process these information from the token. We did not find any plugin hooks in the authentication processes and the normal plugin hooks do not have access to information from the token anymore. What do you think is the best way to do this? We would be okay modifying some dovecot source code and contributing it back if desired.
Best regards, Felix Auringer
Gesellschaft für interkulturelles Zusammenleben gGmbH (GIZ) Felix Auringer IT Reformationsplatz 2 13597 Berlin
Tel: 030/513 0100 00; Fax: 030/513 0100 09 www.giz.berlin; felix.auringer@giz.berlin
Amtsgericht Charlottenburg HRB 200872 B Geschäftsführerin: Dr. Britta Marschke
On 14/06/2022 12:52 Felix Auringer felix.auringer@giz.berlin wrote:
Hi,
we're trying to set up an IMAP server using Dovecot as part of a new email system in a middle-sized company (around 150 employees). We have a keycloak identity server that is used for all logins and we would also like to use it for the new email system so that the non-technical employees (the majority) will have no issues using it.
It's very nice that XOAUTH2 is already included in Dovecot, it works very well with our Roundcube web client. However, it seems that other clients like Thunderbird or Android Apps like FairEmail are not easy to set up as they have their XOAUTH providers hardcoded somewhere (https://bugzilla.mozilla.org/show_bug.cgi?id=1602166). Are you aware of any solution to integrate a dovecot server using XOAUTH2 into local clients (like Thunderbird)? We are currently trying to do it with passwords that can be set in Keycloak only for the purpose of email that can then be used to receive a valid token via a direct access grant but it's not a really nice solution as it introduces an additional password.
Unfortunately this is a client-side restriction. You should use either device passwords or you can use the "password grant mode" where dovecot authenticates to keycloak with username & password.
Dovecot supports openid_configuration_url which should be supported eventually.
Additionally, we would like to have all permission-related information saved in our identity server. For email, this includes shared mailboxes that a user is allowed to access. The ACL plugin that is used for shared mailboxes currently reads the permissions from disk which is not really feasible if the user base is large and setting these permissions should happen automatically. We are thinking the best way to do this is to encode the user's permissions in the token that dovecot receives in the login process. But as the token is only available in the authentication process and it does not seem to be intended to return such information from the password database (https://doc.dovecot.org/configuration_manual/authentication/password_databas...), we are unsure on how to process these information from the token. We did not find any plugin hooks in the authentication processes and the normal plugin hooks do not have access to information from the token anymore. What do you think is the best way to do this? We would be okay modifying some dovecot source code and contributing it back if desired.
You can't really store this in your identity server. Currently the only supported backing for acl information is file, so there is no mechanism for passing ACL permissions via passdb, and it would not be that feasible either.
In general, dovecot makes fields present in JWT tokens available if you use local validation, you can use %{oauth2:field} to export them into mail process from passdb using pass_attrs:
pass_attrs = userdb_foo=%{oauth2:field}
Best regards, Felix Auringer
Regards,
Aki
Hi,
I tried to configure / implement the suggestions - thanks for them @Aki!
On 6/15/22 07:50, Aki Tuomi wrote:
On 14/06/2022 12:52 Felix Auringer felix.auringer@giz.berlin wrote:
Hi,
we're trying to set up an IMAP server using Dovecot as part of a new email system in a middle-sized company (around 150 employees). We have a keycloak identity server that is used for all logins and we would also like to use it for the new email system so that the non-technical employees (the majority) will have no issues using it.
It's very nice that XOAUTH2 is already included in Dovecot, it works very well with our Roundcube web client. However, it seems that other clients like Thunderbird or Android Apps like FairEmail are not easy to set up as they have their XOAUTH providers hardcoded somewhere (https://bugzilla.mozilla.org/show_bug.cgi?id=1602166). Are you aware of any solution to integrate a dovecot server using XOAUTH2 into local clients (like Thunderbird)? We are currently trying to do it with passwords that can be set in Keycloak only for the purpose of email that can then be used to receive a valid token via a direct access grant but it's not a really nice solution as it introduces an additional password.
Unfortunately this is a client-side restriction. You should use either device passwords or you can use the "password grant mode" where dovecot authenticates to keycloak with username & password.
I used the password grant mode and it works fine!
Dovecot supports openid_configuration_url which should be supported eventually.
Sadly, the clients we intend to use do not support this yet but I will keep it in mind to hopefully use it later on.
Additionally, we would like to have all permission-related information saved in our identity server. For email, this includes shared mailboxes that a user is allowed to access. The ACL plugin that is used for shared mailboxes currently reads the permissions from disk which is not really feasible if the user base is large and setting these permissions should happen automatically. We are thinking the best way to do this is to encode the user's permissions in the token that dovecot receives in the login process. But as the token is only available in the authentication process and it does not seem to be intended to return such information from the password database (https://doc.dovecot.org/configuration_manual/authentication/password_databas...), we are unsure on how to process these information from the token. We did not find any plugin hooks in the authentication processes and the normal plugin hooks do not have access to information from the token anymore. What do you think is the best way to do this? We would be okay modifying some dovecot source code and contributing it back if desired.
You can't really store this in your identity server. Currently the only supported backing for acl information is file, so there is no mechanism for passing ACL permissions via passdb, and it would not be that feasible either.
My plan is rather to implement a new ACL backend that does not read the permissions from files but instead uses the permissions encoded in the JWT Token.
In general, dovecot makes fields present in JWT tokens available if you use local validation, you can use %{oauth2:field} to export them into mail process from passdb using pass_attrs:
pass_attrs = userdb_foo=%{oauth2:field}
I setup dovecot to do local validation and this seems to work fine. The
auth process sends the configured oauth2 fields to its client (the log shows
client passdb out: OK 1 user=<...> email=felix.auringer@giz.berlin token=<...>
where the email field is extracted from the JWT token).
However, I can not really find the place in the imap-login client (if
that is even the right place to look) where the information are
processed. The result should be available in sasl_callback
in
src/login-common/client-common-auth.c
. But as far as I understand it,
this calls imap_client_auth_result
which does nothing with the reply
if the authentication was successful. I also tried to read the values
from the user struct using my plugin but could also not find any fields
that contain the fields from the JWT.
Could you explain where the JWT fields are available for plugins after
the login is finished? And / or point me to the place in the imap-login
source where the JWT fields from the authentication reply are processed.
Best regards, Felix Auringer
Regards,
Aki
Regards, Felix Auringer
Gesellschaft für interkulturelles Zusammenleben gGmbH (GIZ) Felix Auringer IT Reformationsplatz 2 13597 Berlin
Tel: 030/513 0100 00; Fax: 030/513 0100 09 www.giz.berlin; felix.auringer@giz.berlin
Amtsgericht Charlottenburg HRB 200872 B Geschäftsführerin: Dr. Britta Marschke
On 22/08/2022 11:11 EEST Felix Auringer felix.auringer@giz.berlin wrote:
Hi,
I tried to configure / implement the suggestions - thanks for them @Aki!
On 6/15/22 07:50, Aki Tuomi wrote:
On 14/06/2022 12:52 Felix Auringer felix.auringer@giz.berlin wrote:
Hi,
we're trying to set up an IMAP server using Dovecot as part of a new email system in a middle-sized company (around 150 employees). We have a keycloak identity server that is used for all logins and we would also like to use it for the new email system so that the non-technical employees (the majority) will have no issues using it.
It's very nice that XOAUTH2 is already included in Dovecot, it works very well with our Roundcube web client. However, it seems that other clients like Thunderbird or Android Apps like FairEmail are not easy to set up as they have their XOAUTH providers hardcoded somewhere (https://bugzilla.mozilla.org/show_bug.cgi?id=1602166). Are you aware of any solution to integrate a dovecot server using XOAUTH2 into local clients (like Thunderbird)? We are currently trying to do it with passwords that can be set in Keycloak only for the purpose of email that can then be used to receive a valid token via a direct access grant but it's not a really nice solution as it introduces an additional password.
Unfortunately this is a client-side restriction. You should use either device passwords or you can use the "password grant mode" where dovecot authenticates to keycloak with username & password.
I used the password grant mode and it works fine!
Dovecot supports openid_configuration_url which should be supported eventually.
Sadly, the clients we intend to use do not support this yet but I will keep it in mind to hopefully use it later on.
Additionally, we would like to have all permission-related information saved in our identity server. For email, this includes shared mailboxes that a user is allowed to access. The ACL plugin that is used for shared mailboxes currently reads the permissions from disk which is not really feasible if the user base is large and setting these permissions should happen automatically. We are thinking the best way to do this is to encode the user's permissions in the token that dovecot receives in the login process. But as the token is only available in the authentication process and it does not seem to be intended to return such information from the password database (https://doc.dovecot.org/configuration_manual/authentication/password_databas...), we are unsure on how to process these information from the token. We did not find any plugin hooks in the authentication processes and the normal plugin hooks do not have access to information from the token anymore. What do you think is the best way to do this? We would be okay modifying some dovecot source code and contributing it back if desired.
You can't really store this in your identity server. Currently the only supported backing for acl information is file, so there is no mechanism for passing ACL permissions via passdb, and it would not be that feasible either.
My plan is rather to implement a new ACL backend that does not read the permissions from files but instead uses the permissions encoded in the JWT Token.
In general, dovecot makes fields present in JWT tokens available if you use local validation, you can use %{oauth2:field} to export them into mail process from passdb using pass_attrs:
pass_attrs = userdb_foo=%{oauth2:field}
I setup dovecot to do local validation and this seems to work fine. The auth process sends the configured oauth2 fields to its client (the log shows
client passdb out: OK 1 user=<...> email=felix.auringer@giz.berlin token=<...>
where the email field is extracted from the JWT token). However, I can not really find the place in the imap-login client (if that is even the right place to look) where the information are processed. The result should be available insasl_callback
insrc/login-common/client-common-auth.c
. But as far as I understand it, this callsimap_client_auth_result
which does nothing with the reply if the authentication was successful. I also tried to read the values from the user struct using my plugin but could also not find any fields that contain the fields from the JWT. Could you explain where the JWT fields are available for plugins after the login is finished? And / or point me to the place in the imap-login source where the JWT fields from the authentication reply are processed.
Hi!
You need to export them in passdb. You can do userdb_some_field=%{oauth2:some_field}
.
Best regards, Felix Auringer
Regards,
Aki
Regards, Felix Auringer
Regards
Aki
On 8/22/22 10:14, Aki Tuomi wrote:
Hi!
You need to export them in passdb. You can do
userdb_some_field=%{oauth2:some_field}
.
That is exactly what I have been looking for, thank you! Is it also
possible to extract arrays and objects from the token with this syntax?
For example, I tried to save allowed-origins
which is a list of
strings but the field in the userdb was empty (but present). However,
the field was processed according to the logs.
Furthermore, it seems that only keys that have a string or an array value are processed, so it may not even be possible to extract a parent object. For a structure like this:
{
"azp": "roundcube-test",
"realm_access": {
"roles": [...]
},
"resource_access": {
"realm-management": {
"roles": [...]
},
"account": {
"roles": [...]
}
}
}
the log only shows:
auth: Debug: oauth2(...): Processing field azp auth: Debug: oauth2(...): Processing field roles
auth: Debug: oauth2(...): Processing field roles
auth: Debug: oauth2(...): Processing field roles
It also doesn't work to extract the whole token with
userdb_token=%{oauth2:access_token}
(this syntax however works for
proxy authentication). Otherwise, I could just save the whole token in
the user database.
Is there some syntax I did not find in the documentation that would enable me to extract either the whole token or a whole JSON object / array?
Best regards, Felix
Gesellschaft für interkulturelles Zusammenleben gGmbH (GIZ) Felix Auringer IT Reformationsplatz 2 13597 Berlin
Tel: 030/513 0100 00; Fax: 030/513 0100 09 www.giz.berlin; felix.auringer@giz.berlin
Amtsgericht Charlottenburg HRB 200872 B Geschäftsführerin: Dr. Britta Marschke
On 22/08/2022 14:32 EEST Felix Auringer felix.auringer@giz.berlin wrote:
On 8/22/22 10:14, Aki Tuomi wrote:
Hi!
You need to export them in passdb. You can do
userdb_some_field=%{oauth2:some_field}
.That is exactly what I have been looking for, thank you! Is it also possible to extract arrays and objects from the token with this syntax? For example, I tried to save
allowed-origins
which is a list of strings but the field in the userdb was empty (but present). However, the field was processed according to the logs.
Currently the support is very limited. You can extract strings and numbers from a flat object.
Furthermore, it seems that only keys that have a string or an array value are processed, so it may not even be possible to extract a parent object. For a structure like this:
{ "azp": "roundcube-test", "realm_access": { "roles": [...] }, "resource_access": { "realm-management": { "roles": [...] }, "account": { "roles": [...] } } }
the log only shows:
auth: Debug: oauth2(...): Processing field azp auth: Debug: oauth2(...): Processing field roles
auth: Debug: oauth2(...): Processing field roles
auth: Debug: oauth2(...): Processing field roles
It also doesn't work to extract the whole token with
userdb_token=%{oauth2:access_token}
(this syntax however works for proxy authentication). Otherwise, I could just save the whole token in the user database.
You should be able to extract the whole access token like that, although I didn't say in my previous mail that the %{oauth2:} is valid only within the oauth2 passdb currently.
Additionally, the user's token is available as %w / %{password} on all passdbs. The best way I can think of right now is to use Lua passdb to complex token handling.
Is there some syntax I did not find in the documentation that would enable me to extract either the whole token or a whole JSON object / array?
Best regards, Felix
Aki
Hi,
On 8/22/22 14:04, Aki Tuomi wrote:
Currently the support is very limited. You can extract strings and numbers from a flat object. > You should be able to extract the whole access token like that, although I didn't say in my previous mail that the %{oauth2:} is valid only within the oauth2 passdb currently.
I am using the oauth2 passdb and can extract other fields, for example
userdb_email=%{oauth2:email}
successfully. However,
userdb_token=%{oauth2:access_token}
does not work.
Additionally, the user's token is available as %w / %{password} on all passdbs. The best way I can think of right now is to use Lua passdb to complex token handling.
%{password}
on the other hand works fine, so it's not a problem that
%{oauth2:access_token}
isn't working.
Thank you very much for your help, Aki!
Felix
Gesellschaft für interkulturelles Zusammenleben gGmbH (GIZ) Felix Auringer IT Reformationsplatz 2 13597 Berlin
Tel: 030/513 0100 00; Fax: 030/513 0100 09 www.giz.berlin; felix.auringer@giz.berlin
Amtsgericht Charlottenburg HRB 200872 B Geschäftsführerin: Dr. Britta Marschke
participants (2)
-
Aki Tuomi
-
Felix Auringer