Aliases not working

Issue Description

I’m pretty sure this worked before, but now it’s not working:

My ldap filter for mailboxes and aliases is this:

(|(&(objectClass=postfixAlias)(postfixMailAlias=?)(status=TRUE))(&(objectClass=postfixMailBox)(postfixMailAddress=?)(status=TRUE)))

The ObjectClass postfixAlias has two attributes: postfixMailAlias and postfixMailAddress, which is the mailbox.

Even if I choose that the alias is on the postfixMailAddress, Stalwart returns mails sent to the alias.

It seems like a bug to me, when I tested this a few weeks ago, it worked.

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

FoundationDB

Blob Storage

S3-compatible

Search Engine

Internal

Directory Backend

LDAP

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Let me look into LDAP alias handling in Stalwart.

The key issue here is how Stalwart maps your custom LDAP schema to the concepts of “primary address” vs “alias”. Stalwart uses two separate attribute mappings:

  • attrEmail (default: ["mail"]) - the primary email address attribute
  • attrEmailAlias (default: ["mailAlias"]) - the alias attribute

Your postfixAlias entries use postfixMailAlias as the alias address and postfixMailAddress as the target mailbox. For Stalwart to correctly resolve these, you need to make sure your Directory object’s attribute mapping reflects your custom schema. Specifically, you likely need to configure:

{
  "@type": "Ldap",
  "attrEmail": ["postfixMailAddress"],
  "attrEmailAlias": ["postfixMailAlias"],
  "filterMailbox": "(|(&(objectClass=postfixAlias)(postfixMailAlias=?)(status=TRUE))(&(objectClass=postfixMailBox)(postfixMailAddress=?)(status=TRUE)))"
}

The critical part is that attrEmailAlias must point to the attribute holding the alias address (postfixMailAlias), and attrEmail must point to the attribute holding the primary/target address (postfixMailAddress). Stalwart uses these attribute mappings - not just the filter - to understand which address is the alias and which is the delivery target. If attrEmailAlias is still set to the default ["mailAlias"], Stalwart won’t recognize postfixMailAlias values as aliases even when your filter returns the entry.

You can verify and update these settings in the WebUI under your LDAP Directory object configuration.

Reference: LDAP Backend - Object attributes

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

I don’t think so - while it is logic that the `attrEmailAlias` should point to the alias, it never worked like this for me. My understanding is that the Mailbox filter finds the mailbox via the alias’ address, and find the mailbox the alias points to via the attribute pointing to the mailbox.

So I am not sure if the attrEmailAlias should point to the alias or to the mailbox. But once, when it was pointing to the mailbox, things worked. Now it doesn’t work neither way.

The way mailbox resolution works: filterMailbox matches an entry when the recipient equals any attribute in the filter (mail or the alias attribute). Once matched, the account’s primary address comes from attrEmail and aliases from attrEmailAlias, independently of which attribute matched, and delivery goes to that resolved account. So mail to either the primary or an alias landing on the same account is correct alias behavior.

Your schema has the alias address and the target mailbox on a single entry (postfixMailAlias and postfixMailAddress), so attrEmail and attrEmailAlias both resolve on one object and effectively collapse together. Can you post your full Directory config (the attr* mappings and filterMailbox), one example alias entry, and the expected vs actual delivery target? I suspect the attribute-to-field mapping changed. See stalw.art/docs/auth/backend/ldap#filtermailbox.

Hi @stalwart , thanks for your answer. I’m not sure I understand, but here’s how my configuration is:

Login filter:

(&(objectClass=postfixMailBox)(postfixMailAddress=?)(status=TRUE))

Mailbox filter:

(|(&(objectClass=postfixAlias)(postfixMailAlias=?)(status=TRUE))(&(objectClass=postfixMailBox)(postfixMailAddress=?)(status=TRUE)))

Primary email attribute:

postfixMailAddress

E-mail attribute:

postfixEmailAddress (I tried as postfixEmailAlias as well.

The behavior you’re seeing is how mailbox resolution is meant to work, and the config is the thing to fix. filterMailbox only decides which directory entry matches an incoming recipient. Once an entry matches, the account’s primary address is read from your attrEmail mapping and its aliases from your attrEmailAlias mapping, independently of which part of the filter matched. So mail to either the primary or an alias landing on that same account is correct.

The concrete problem is that your E-mail (alias) attribute is set to postfixEmailAddress, which isn’t an attribute in your schema (your alias attribute is postfixMailAlias). So attrEmailAlias reads nothing, and resolution falls back to whatever postfixMailAddress the matched entry carries.

Can you set the alias attribute to the real attribute that holds the alias address, then send me one full sample entry (all attributes) plus the attrEmail and attrEmailAlias values you end up with? Also note that with the alias address and the target mailbox on a single object, they collapse onto one account by design; to redirect an alias to a different account you’d model the alias as one of that target account’s attrEmailAlias values rather than as a separate object pointing across. The mapping reference is at stalw.art/docs/auth/backend/ldap#object-attributes.

Sorry, I’m having a hard time to understand what you’re asking me to do.

What do you mean by a “full sample entry” of “what I end up with”? I’m not sure what you mean here.

I have just ended the attrEmailAlias to `postfixMailAlias`, it didn’t help.

Sorry, I also don’t know what you mean by “Also note that with the alias address and the target mailbox on a single object, they collapse onto one account by design; to redirect an alias to a different account you’d model the alias as one of that target account’s attrEmailAlias values rather than as a separate object pointing across.”

What I have is objects that holds accounts and objects that holds aliases. Aliases have the `postfixMailAlias` and `postfixMailAddress` attributes. They don’t “point” to anything - they just have a value like “[email protected]”, because, when I used postfix, it would then query that address. Not sure what I could do differently here.

Let me restate the model more concretely, because I think the confusion is that Stalwart doesn’t do Postfix-style alias indirection. filterMailbox only decides which directory entry matches the address mail was sent to. Once an entry matches, Stalwart reads that entry’s primary address from your attrEmail mapping and its aliases from your attrEmailAlias mapping, no matter which part of the filter matched the lookup. So an alias is simply one of the addresses listed on an account’s own entry; there is no “alias object whose value points at another mailbox” that gets re-queried the way virtual_alias_maps works.

Two concrete things in your config: your E-mail (alias) attribute needs to name a real attribute in your schema that holds the alias address. You had it on postfixEmailAddress, which doesn’t exist (your alias attribute is postfixMailAlias), so attrEmailAlias read nothing. And because you keep the alias address and the target mailbox on a single object, both collapse onto one account; you can’t use that to redirect an alias to a different account.

To send mail addressed to X to the mailbox of account Y, add X as one of account Y’s own attrEmailAlias values on Y’s entry, rather than as a separate alias object. If you can paste one full sample entry (all attributes) for an alias and the account it should reach, I can tell you exactly what to set attrEmail and attrEmailAlias to. The mapping reference is at LDAP Server | Stalwart.

Thanks. So here is how it is configured now:

```
Attributes
Account Type Attribute: objectClass
Description Attribute: description
Primary E-mail Attribute: postfixMailAddress
E-mail Alias Attribute: postfixMailAlias
Member Of Attribute: memberOf
Password Attribute: userPassword
Password Changed Attribute:
Group Object Class: groupOfNames
```

But my objects are, splitter: I have objects which are mailboxes and objects that are mailboxes. In common with them is the presence of a `postfixMailAddress`. But aliases objects (objectClass==postfixAlias) have both attributes.
Shouldn’t it work anyway? Since when searching for a mailbox, it will find it by the alias, but already have an object pointing to a mailbox?

If I have a mailbox called mailboxa and it is has its own object (objectClass=postfixMailBox), does it really matter for Stalwart that the same mailbox is referred to on an alias object containing the same address, ie, “postfixMailAlias=myalias” and postfixMailAddress “mailboxa”?

It would be difficult to change my scheme to move the aliases to the mailboxes objects, since this is a plugin I wrote for Freeipa.

This is working as designed rather than a regression, Stalwart doesn’t do Postfix-style alias indirection. filterMailbox only picks which single directory entry matches the recipient; there’s no second lookup that then follows that entry’s postfixMailAddress to a different mailbox the way virtual_alias_maps does. Once an entry matches, the primary comes from attrEmail and the aliases from attrEmailAlias on that same entry.

So an alias has to live as one of the target account’s own attrEmailAlias values, on the mailbox entry itself, not as a separate alias object pointing at the mailbox. In your schema the alias objects carry both the alias and the target on one object, so mail to the alias just lands on that object and is never redirected. If you can have the FreeIPA side write the alias onto the mailbox entry (as attrEmailAlias), delivery will work. If you really need one address to reach several different accounts, that’s a mailing list instead. Docs: LDAP Server | Stalwart .

But they are in the same entry. The alias objectClass does have both attributes, the one from the alias and the one for the mailbox. Even if Stalwart doesn’t do a second query to find that mailbox, shouldn’t the attribute present there be enough for fulfilling the necessary attributes for mailbox delivery?

Let me make this concrete, because I think we’ve been talking past each other. filterMailbox matches exactly one directory entry for the incoming recipient. On that matched entry, the primary address is read from your attrEmail mapping (postfixMailAddress) and the aliases from attrEmailAlias (postfixMailAlias). There’s no second lookup that follows a value on the matched entry to a different DN, which is the part that differs from Postfix virtual_alias_maps.

Given your corrected mapping (primary = postfixMailAddress, alias = postfixMailAlias), the alias entry resolves to email = the postfixMailAddress value and alias = the postfixMailAlias value. So mail to the alias should land on whatever account has that postfixMailAddress as its primary, provided that value exactly equals the target mailbox’s primary address. If the alias entry’s postfixMailAddress holds the real mailbox’s address, delivery to that mailbox’s account is expected; if it holds something else, that’s where it breaks.

Rather than keep reasoning about it in the abstract, can you paste one full LDAP entry (every attribute) for one alias, plus the entry for the mailbox it should reach, and a TRACE-level store.ldap-query line for a message sent to that alias? That will show exactly what attrEmail and attrEmailAlias resolved to, and I can tell you the one thing to change. If instead you need one address to reach several different accounts, that’s a mailing list rather than an alias.

Hi,
Thanks for the help so far.
I’ll post the trace logs you asked for, but there are two things here that we might indeed be talking past each other:

1 - I’m not talking about two lookups. I’m talking about this:

```

mailboxa, fedisport.no, postfixadmin, mailserver, etc, ipa.med-lo

dn: uid=mailboxa,cn=fedisport.no,cn=postfixadmin,cn=mailserver,cn=etc,dc=ipa,d
c=med-lo
postfixPasswordChanged: 20260530130918Z
uid: mailboxa
givenName: Mailbox
sn: A
status: TRUE
objectClass: postfixMailbox
objectClass: person
objectClass: inetOrgPerson
objectClass: inetUser
objectClass: top
objectClass: organizationalPerson
cn: Mailbox A
postfixMailAddress: [email protected]
```

The alias:
```
a, fedisport.no, postfixadmin, mailserver, etc, ipa.med-lo

dn: uid=a,cn=fedisport.no,cn=postfixadmin,cn=mailserver,cn=etc,dc=ipa,dc=med-l
o
uid: a
postfixMailAddress: [email protected]
status: TRUE
objectClass: postfixAlias
objectClass: top
postfixMailAlias: [email protected]
```

So, while I have two objects, as you see, the alias object does have the attrEmail mapping to `postfixMailAddress`. So it doesn’t need a second lookup, does it? I’d imagine that, when a mail comes to `[email protected]`, it would be found due my mailbox filter, and the attribute for the mailbox is there. Wouldn’t this work? It isn’t working as of now, and I’m failing to see what’s the problem with my configuration.

2 - I do understand Stalwart’s concept on alias being just for one mail account. But other systems do allow admins to configure an alias for two or more accounts. Postfix, for example, has clear examples for that on their ldap documentation. One can create an alias for two mailboxes and have mail sent to these two. What you’re saying is that Stalwart doesn’t support it this way, so if I end up using Stalwart, I’ll have to cope with this, but it doesn’t mean that it necessarily need to be an e-mail list. It’d be really nice if an alias could point to more than one e-mails, because this is a good behavior for, for example, a company that needs incoming e-mails to be answered by more than one employee when one of them is on vacation.

I’ll provide the trace logs you requested later today. Thanks for your patience and attention so far.

Your two-object layout can actually work. With primary set to postfixMailAddress and alias to postfixMailAlias, the alias entry (uid=a) resolves to an account whose primary is [email protected] with [email protected] as an alias, and Stalwart keys the internal account on the primary’s local part and attaches the alias to it, so a@ should end up registered against the mailboxa account. The wrinkle is that RCPT acceptance is decided by the LDAP filter match, but the actual delivery resolves against Stalwart’s internal mirror of the directory, so an address can pass the filter yet still not deliver if it never got attached there as an enabled alias.

Two things usually explain that. First, did the server get reloaded or restarted after you changed the alias mapping? The directory objects are cached. Second, check the admin UI for any stray account or alias that already owns [email protected] from an earlier experiment: if that address is already registered somewhere, the sync skips re-attaching it to mailboxa. Remove any such leftover and let it re-sync.

If it still misbehaves after that, the trace-level store.ldap-query log for a message to a@ (which you offered to grab) will show exactly what the primary and alias attributes resolved to, and that’ll settle it. As a sturdier alternative you can also just put a@ directly as another mail-alias value on the mailbox entry itself, which avoids the two-object fragility. The mapping fields are documented at LDAP Server | Stalwart.

Hi.
I restarted the server. I then configured the logs to trace. I restarted the server.
It failed, so I was going to copy the logs, but they were huge. I then started to save the logs into a file, and then it suddenly worked!

I can still send the logs if you want, but it is working right now.
But maybe you want me to send a trace log anyway regarding this: "Account does not have a password" error when using the webui - #8 by stalwart ?

What most likely happened: RCPT acceptance is answered straight from LDAP, but the actual delivery resolves against Stalwart’s internal mirror of the directory, and the alias only becomes deliverable once a directory lookup has copied it onto the mailboxa account. Negative address lookups are also cached (an hour by default), so a “no such mailbox” answer can outlive the fix for a while. Your two-object layout is supported as it stands, so nothing needs restructuring on the FreeIPA side.

Worth confirming in the admin UI that [email protected] now shows as an alias on the mailboxa account. If an address is already registered against some other account or list from an earlier experiment, the sync skips re-attaching it and does so silently, and that is the one thing that would make this come back.

No need for the logs on this one. I have replied on the password thread with something more specific to try, so hold off on that trace until you have tried it. On one address reaching several accounts: that is a mailing list in Stalwart, since an alias belongs to exactly one account. The vacation-coverage case you described is precisely what a list is for, so you are not losing anything there.