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

Update: proxy authentication #1241

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open

Conversation

till
Copy link

@till till commented May 17, 2023

I made a quick pass to enable proxy auth (aka external-auth or forward-auth).

The way this works is, once enabled, it'll listen to the specific headers in the the request and will try to find a user based on that, or create one on the fly.

I made this as a first pass to gather feedback. Any comments appreciated.

Related: #735

I made a quick pass to enable proxy auth (aka external-auth or forward-auth).

The way this works is, once enabled, it'll listen to the specific headers in the
the request and will try to find a user based on that, or create one on the fly.

I made this as a first pass to gather feedback. Any comments appreciated.

Related: semaphoreui#735

log.Debug(username + " does not exist yet, creating it")

externalAuthUser := db.UserWithPwd{
Copy link
Author

Choose a reason for hiding this comment

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

I should probably check if this is the "first" user and give them .Admin?

@@ -209,6 +218,20 @@ func validateConfig() {
}
}

func validateExternalAuth() {
Copy link
Author

Choose a reason for hiding this comment

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

I just thought of it now, but I think it should be "external auth" or LDAP or internal. Not on top of it.

So, on that note, it should probably "error" when LDAP is enabled as well. Haven't found a simple way to determine "internal auth" yet.

var authCookieName = "semaphore"

func determineAuthType(r *http.Request) authType {
if len(r.Header.Get("authorization")) > 0 && strings.Contains(r.Header.Get("authorization"), "bearer") {
Copy link
Author

Choose a reason for hiding this comment

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

Probably needs a strings.ToLower() as well.

Username: username,
Created: time.Now(),
Name: username,
Email: username + "@example.org", // FIXME
Copy link
Author

Choose a reason for hiding this comment

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

Not entirely sure how to deal with that yet. Maybe have to read how LDAP works. But I guess there's also generally more data available from an external directory.

@spali
Copy link

spali commented Jun 20, 2023

Currently using rundeck and would love to switch to semaphore as lightweight alternative.
Maybe the approach of rundeck (how I use it right now) can work here too.

Rundeck allows to define in the configuration or via env variable (i.e. for docker) "preauth" settings. These define how the headers get's read for proxy auth.
Basically you can configure the header names for all required attributes like username, email, name and groups/roles and a logout url to forward to logout of the proxy auth.
It then also creates on the fly the user with the minimal required set of attributes.
In case of semaphore, this would require username,name,email and maybe a role header which just has to include "admin" if the user is admin.

example of settings (env vars in this case) for rundeck behind authelia authentification:

RUNDECK_PREAUTH_ENABLED: true
RUNDECK_PREAUTH_DELIMITER: "," # for reading groups
RUNDECK_PREAUTH_USERNAME_HEADER: Remote-User
RUNDECK_PREAUTH_ROLES_HEADER: Remote-Groups
RUNDECK_PREAUTH_USERSYNC_ENABLED: true
RUNDECK_PREAUTH_USERSYNC_EMAIL: Remote-Email
RUNDECK_PREAUTH_REDIRECT_URL: https://myproxyauthdomain/logout

the proxy then sends the following headers to rundeck set by the forward auth:

Remote-User: myuser
Remote-Email: [email protected]
Remote-Groups: admin,user

RUNDECK_PREAUTH_USERSYNC_ENABLED defines if an existing user is updated in case of the headers do not match the existing user. This is for changing any attribute including group/role.

For semaphore I suggest the following extension of config.json:

{
...
  "proxy_auth": {
    "user_header": "Remote-User",
    "name_header": "Remote-Name",
    "mail_header": "Remote-Email",
    "role_header": "Remote-Groups",
    "role_header_sep": ",",
    "role_header_admin_group": "admin", // semaphore should search in the delimited list of `role_header` for this group
    "sync": true // defines if already existing users get's updated
                 // should only support local and not ldap (so this `true` and ldap enabled should fail or silently not sync).
                 // But with "false" I see no reason to not use only the `Remote-User` as authenticated user and use ldap for reading the attributes.
    "logout_url": "https://myauthserver/logout" // optional but nice feature
  }
...
}

@spali
Copy link

spali commented Jun 26, 2023

Tried to play a bit around, but had the problem, that the authenticationHandler does get called before the first login attempt.
I assume because the client side check for a cookie starting with semaphore=.

@till Did you observe the same?

I think any form of "pre-authentication" would first need to let the client side always call the backend to check for authentication and route based on the result not the pure existence of the cookie.
I just don't know enough about vue2 to get this "fixed".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants