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

Multi-auth. provider / using separate DBs for usernames and passwords. #56

Closed
foxcpp opened this issue Apr 27, 2019 · 4 comments
Closed
Labels
auth Related to authentication providers new feature New feature. rfc Request For Comments (ongoing discussion / research needed).

Comments

@foxcpp
Copy link
Owner

foxcpp commented Apr 27, 2019

I remember having a use-case where I wanted to give email accounts to all PAM users but use passwords from a separate database. And also people on IRC channels for dovecot and postfix often ask questions regarding weird mixes of authentication data sources. So I guess we should have a generic tool to handle such cases.

We create the multi module that implements authentication provider interface (so it can be used in SMTP, IMAP, etc):

multi instance_name {
  user pam
  user virtual { file /etc/maddy/userlist }
  pass virtual { file /etc/maddy/passwd }
}

user directive here refers to a module implementing the following interface:

type UserDB interface {
  HasUser(name string) bool
}

pass directive refers to an authentication provider.

If there is at least one user directive - then at least one "userdb" module should say that the user exists.
Then the user password is also checked against providers listed using pass directives, at least one provider should accept the password.

multi module itself also implements the UserDB interface, this allows mixing things together in more complicated use-cases to get the right results.

@foxcpp foxcpp added auth Related to authentication providers rfc Request For Comments (ongoing discussion / research needed). labels Apr 27, 2019
@foxcpp foxcpp added the new feature New feature. label May 25, 2019
@foxcpp
Copy link
Owner Author

foxcpp commented May 26, 2019

Another thing is that users may want to have different storage associated with accounts coming from different auth. backends. Currently, authentication is fully separate from storage and we have the concept of "auth account" and "storage account". This makes it difficult to build some sort of association.

We can allow authentication modules to return some kind of "association tag", which could then be consumed by storage backend.
There are multiple ways to implement such association tags. Depending on the level of abstraction you want them to be on.

SMTP, Pipeline: Allow branching directives to inspect association tag created by authentication provider (using syntax described in #32):

if $authsource = pam {
}

Approach A: Extend authentication provider interface:

type AuthProvider interface {
  CheckPlain(username, password) (associationTag string, err error)
}

Extend storage IMAP interface in similar way:

type StorageBackend interface {
  GetUser(assocationTag, username, password string) (backend.User, error)
}

Then implement multi storage backend and authentication provider capable of making use of these association tags for purposes of dispatching.

@foxcpp
Copy link
Owner Author

foxcpp commented May 26, 2019

Approach B: Move dispatching logic to the upper level.

Do all dispatching at the endpoint (or pipeline) module level, using auth. provider instance names as "association tags".

imap ... {
  auth multi local_users {
      user pam
      pass virtual { file /var/lib/maddy/local-passwd }
  }
  auth multi virtual_users {
      user virtual { file /var/lib/maddy/virtual-users }
      pass virtual { file /var/lib/maddy/virtual-passwd }
  }

  storage {
    auth local_users maildir { root /var/spool/ }
    auth virtual_users sql
  }
}
submission ... {
  auth multi local_users {
      user pam
      pass virtual { file /var/lib/maddy/local-passwd }
  }
  auth multi virtual_users {
      user virtual { file /var/lib/maddy/virtual-users }
      pass virtual { file /var/lib/maddy/virtual-passwd }
  }

  if $auth = virtual_users {
    ...
  } 
}

Side-note: Common auth expressions can be factored out into a snippet:

(auth) {
  auth multi local_users {
      user pam
      pass virtual { file /var/lib/maddy/local-passwd }
  }
  auth multi virtual_users {
      user virtual { file /var/lib/maddy/virtual-users }
      pass virtual { file /var/lib/maddy/virtual-passwd }
  }
}

Or we might want to allow specificing auth directive at top level.

Okay, I think Approach B is more clear and Approach A basically ruins all existing abstractions, while B builds on them.

@foxcpp
Copy link
Owner Author

foxcpp commented Jun 13, 2019

multi instance_name {
 user pam
 user virtual { file /etc/maddy/userlist }
 pass virtual { file /etc/maddy/passwd }
}

Side note: Not all PAM modules differentiate between "user doesn't exist" and "invalid password" so this setup may be problematic in reality.

@foxcpp
Copy link
Owner Author

foxcpp commented Jun 14, 2019

  auth multi local_users {
      user pam
      pass virtual { file /var/lib/maddy/local-passwd }
  }
  auth multi virtual_users {
      user virtual { file /var/lib/maddy/virtual-users }
      pass virtual { file /var/lib/maddy/virtual-passwd }
  }

Also this needs extension of config.Map to allow multiple values for directives.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth Related to authentication providers new feature New feature. rfc Request For Comments (ongoing discussion / research needed).
Projects
None yet
Development

No branches or pull requests

1 participant