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

Proposal: Support Single single-on with SPNEGO and SSPI (Kerberos) on Windows #8462

Closed
quasoft opened this issue Oct 11, 2019 · 4 comments
Closed
Labels
type/feature Completely new functionality. Can only be merged if feature freeze is not active.
Milestone

Comments

@quasoft
Copy link
Contributor

quasoft commented Oct 11, 2019

Motivation

When using Gitea on premises in a Windows environment (with Active Directory), it would be very convenient to be able to use single sign-on authentication via Kerberos. Currently Gitea supports LDAP authentication to Active Directory (https://docs.gitea.io/en-us/authentication/), but LDAP is not single sign-on.

Short overview of proposal

The proposal is to support Single single-on authentication with Kerberos by implementing the protocol defined by RFC4559 (SPNEGO-based HTTP Authentication in Microsoft Windows) to exchange authentication data between the web browser and HTTP server, and the Security Support Provider Interface (SSPI) to perform the authentication.

Use cases

  1. When installing Gitea:

    • a configuration option named Enable SSPI authentication (Windows only) could be added to the Server and Third-Party Service Settings section:
      Install
      That option could be disabled by default.
  2. When opening the home page:

    • If the user follows any link on the home page (eg. the Register or Sign In buttons) authentication is performed and the user is automatically signed in with the Kerberos credentials he is currently using in the operating system.
      SignIn1

    SignedIn

  3. When SSPI authentication is enabled, but the user needs to temporary use another authentication method (eg. to login as local user with administrative rights):

    • The dashboard for anonymous users contains an additional button for temporary disabling single sing-on (the button Disable Single Sign-On appears only if SSPI authentication has been enabled in configuration):
      HomePage
    • The menu for users that are already signed in with SSO also have an option to temporary disable SSO:
      UserMenu
  4. The user finished his work as local user and wants to use single-sign on again (to login as the user currently signed in the operating system):

    • If SSPI authentication is enabled in configuration, but the user has temporary disabled it using the Disable Single Sign-On button, then both the anonymous dashboard and the user menu will contain a option Enable Single Sign-On, which restores SSPI authentication:
      UserMenuDisabledSSO

Configuration

The [service] section of the app.ini could contain the following additional options:

; Enable SPNEGO authentication via the built-in SSPI module in Windows.
; To use SSPI authentication you need to create suitable service principal name (SPN)
; for the user running gitea (see https://github.com/quasoft/websspi for more details)
ENABLE_SSPI = false

; Allow SSPI auth method to automatically create new users on the go
SSPI_AUTO_CREATE_USERS = true

; Allow SSPI auth method to automatically activate new users
SSPI_AUTO_ACTIVATE_USERS = true

; The character to use to replace the separators of down-level logon names (eg. the \
; in "DOMAIN\user") and user principal names (eg. the @ in "user@example.org")
SSPI_SEPARATOR_REPLACEMENT = _

; Default language for users automatically created by SSPI auth method
SSPI_DEFAULT_LANG = en-us

Those additional configuration values could be displayed in the Service configuration section of /admin/config page:
ServiceConfiguration

Design

Why SSPI and not a cross-platform kerberos library?

SSPI is chosen for verification because in Windows only environments (where both the client and server run Windows) it works without keytab files, which simplifies setup and improves security by not forcing you to store the user password in a keytab file.

How will this new authentication method be integrated into Gitea codebase?

The SignedInUser function of the auth module (https://github.com/go-gitea/gitea/blob/master/modules/auth/auth.go) is already doing authentication at the middleware level for:

SSPI authentication could be added to the same SignedInUser function, but that would increase the compexity of the file.

A better approach could be to introduce something like a plugin pattern for Single single sign-on authentication methods. A separate subpackage modules/auth/sso could be created with a common interface:

// SingleSignOn represents a SSO authentication method (plugin) for HTTP requests.
type SingleSignOn interface {
	// Init should be called exactly once before using any of the other methods,
	// in order to allow the plugin to allocate necessary resources
	Init() error

	// Free should be called exactly once before application closes, in order to
	// give chance to the plugin to free any allocated resources
	Free() error

	// IsEnabled checks if the current SSO method has been enabled in settings.
	IsEnabled() bool

	// VerifyAuthData tries to verify the SSO authentication data contained in the request.
	// If verification is successful returns either an existing user object (with id > 0)
	// or a new user object (with id = 0) populated with the information that was found
	// in the authentication data (username or email).
	// Returns nil if verification fails.
	VerifyAuthData(ctx *macaron.Context) *models.User
}

Specific implementations of that interface could be:

  • basic.go
  • oauth2.go
  • reverseproxy.go
  • sspi_windows.go

Basic, OAuth and Reverse proxy authentication will be moved from the modules/auth/auth.go file to those "plugin" implementations.

A new plugin implementing the interface will be introduced for SSPI authentication.

Authentication plugins would be used at the middleware level - inside the same SignedInUser function:

// Try to sign in with each of the enabled plugins
for _, ssoMethod := range sso.Methods() {
    if !ssoMethod.IsEnabled() {
        continue
    }
    user = ssoMethod.VerifyAuthData(ctx)
    if user != nil {
        handleSignIn(ctx, sess, user)
        _, isBasic := ssoMethod.(*sso.Basic)
        return user, isBasic
    }
}

Does it introduce new dependencies?

Yes, one additional direct dependency will be needed - github.com/quasoft/websspi (shameless plug, I am the author of this module).

Does SSPI require any complex setup in the AD environment?

No, the only configuration that is strictly required is to create a suitable Service Principal Name for the domain account that will be running the gitea.exe process.

That is usually achieved by running the following command with a priviledged user:

setspn -A HTTP/host.domain.local domain\user

There are some gotchas the user might crash into while setting it up, which will be described in the Authentication section of the documentation:

Gitea supports SPNEGO single sign-on authentication (the scheme defined by RFC4559) for the web part of the server via the Security Support Provider Interface (SSPI) built in Windows. SSPI works only in Windows environments - when both the server and the clients are running Windows.

Before activating SSPI single sign-on authentication (SSO) you have to prepare your environment:

  • Create a separate user account in active directory, under which the gitea.exe process will be running (eg. user under domain domain.local):
  • Create a service principal name for the host where gitea.exe is running with class HTTP:
  • Start Command Prompt or PowerShell as a priviledged domain user (eg. Domain Administrator)
  • Run the command below, replacing host.domain.local with the fully qualified domain name (FQDN) of the server where the web application will be running, and domain\user with the name of the account created in the previous step:
  setspn -A HTTP/host.domain.local domain\user
  • Sign in (sign out if you were already signed in) with the user created
  • Enable SSPI authentication by changing the ENABLE_SSPI value in custom/conf/app.ini to true
  • Start the web server (gitea.exe web)
  • Sign in to a client computer in the same domain with any domain user
  • If you are using Chrome, Edge or Internet Explorer, add the URL of the web app to the Local intranet sites (Internet Options -> Security -> Local intranet -> Sites)
  • Start Chrome, Edge or Internet Explorer and navigate to FQDN URL of gitea (eg. http://host.domain.local:3000)
  • Click the Sign In button on the dashboard and you should be automatically logged in with the same user that is currently logged on to the computer
  • If it does not work, make sure that:
    • You are not running the web browser on the same server where gitea is running. You should be running the web browser on a domain joined computer (client) that is different from the server
    • There is only one HTTP/... SPN for the host
    • The SPN contains only the hostname, without the port
    • You have added the URL of the web app to the Local intranet zone
    • The clocks of the server and client should not differ with more than 5 minutes (depends on group policy)
    • Integrated Windows Authentication should be enabled in Internet Explorer (under Advanced settings)
@quasoft
Copy link
Contributor Author

quasoft commented Oct 11, 2019

Sample implementation of the proposed changes is submitted as Pull request #8463

@lunny lunny added the type/proposal The new feature has not been accepted yet but needs to be discussed first. label Oct 11, 2019
@lafriks
Copy link
Member

lafriks commented Oct 11, 2019

It would be nice if it would also implement getting user details (name, surname, email) using LDAP technical user

@quasoft
Copy link
Contributor Author

quasoft commented Oct 11, 2019

That's an interesting idea, probably can make it get additional user attributes or membership to AD groups at a later stage. Have to think on how it will work more.

@guillep2k
Copy link
Member

First of all, kudos for the delightfully detailed documentation!! 🎉

I'm adding my comments to the PR directly so the discussion doesn't split in two threads.

@lafriks lafriks added this to the 1.10.1 milestone Nov 22, 2019
@lafriks lafriks added type/feature Completely new functionality. Can only be merged if feature freeze is not active. and removed type/proposal The new feature has not been accepted yet but needs to be discussed first. labels Nov 22, 2019
@lafriks lafriks modified the milestones: 1.10.1, 1.11.0 Nov 22, 2019
@lafriks lafriks closed this as completed Nov 22, 2019
@go-gitea go-gitea locked and limited conversation to collaborators Nov 24, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
type/feature Completely new functionality. Can only be merged if feature freeze is not active.
Projects
None yet
Development

No branches or pull requests

4 participants