Pre-requisites

If you haven't configured your Keycloak instance, check our other articles:

Set up the plugin on the Redmine host server - Part 1: Get the plugin

Connect through your SSH client to your Redmine host to install the plugin.

In November 2023, I've forked version 1.0.6 of the official plugin to enable Redmine Administrator creation and add a template to the plugin's login button.

Please check the status of official Redmine SAML plugin before going further.

Install from the forked repository


cd /YOUR_REDMINE_PLUGIN_DIR/
sudo git clone https://github.com/nicolasmetters/redmine_saml.git

Select SAML SSO button visual

Wanting to keep a visual harmony to the login page, I updated the visual:

To switch visuals, simply rename the .CSS file: the plugin will call content from the file named login.css in the directory /assets/stylesheets/.

Install from the official repository


cd /YOUR_REDMINE_PLUGIN_DIR/
sudo git clone https://github.com/alphanodes/redmine_saml.git

My Redmine setup only requires that I restart the container (check the Redmine setup article to know more).

If you are not using a container-based Redmine instance, install the plugin the usual way.

cd /YOUR_REDMINE_DIR
bundle install
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
rake redmine:plugins RAILS_ENV=production
sudo touch /opt/redmine/tmp/restart.txt

Configure Keycloak

For this example we have set up a Keycloak instance :

Parameter Value
Redmine base URL https://redmine.nicksopenworld.com
Keycloak base URL https://broker.nicksopenworld.com
REALM nicksopenworld
Client ID Redmine SAML Metadata URL value

Get signing certificate information

In the SAML configuration you will be asked to provide either the x509 full certificate detail or just it's SHA-1 formated fingerprint.

The certificate can be found in the REALM details:

Generate x509 certificate fingerprint using Ubuntu & OpenSSL

Once you have the x509 certificate value, you can either transform it yourself on your Linux server using OpenSSL:

  • Create a .crt file:
    sudo vi x509.crt
  • Type in the content:
    -----BEGIN CERTIFICATE-----
    FormattedX509CertificateValue
    -----END CERTIFICATE-----

    FormattedX509CertificateValue is the long string value between the "\ds:X509Certificate>" tags.
  • Run the command:
    openssl x509 -sha1 -fingerprint -in path/to/x509.crt -noout

    The fingerprint will be provided in the terminal.

    Generate x509 certificate fingerprint online ressources

    An easier solution is using online websites such as:
    https://www.samltool.com/format_x509cert.php

    Copy the long string value between the "\ds:X509Certificate>" tags from the SAML descriptor into the input box at the top:

    Copy the X.509 cert with header value provided.
    And then get the fingerprint itself:
    https://www.samltool.com/format_x509cert.php

    And save the Formatted FingerPrint value provided.

    Signing certificate fingerprint must be provided in SHA-1.
    This is due to the fact that the SAML plugin relies on omniauth-saml which itself only support SHA-1 as of November 2023.
    Fun fact: this impacts other solutions also using omniauth-saml such as Gitlab.

    Creating a dedicated SAML client

    Create a new SAML client with the following parameters:

Field Value
Client type
Client ID Mandatory.
Structure: https://REDMINE_URL/auth/saml/metadata
For our example: https://redmine.nicksopenworld.com/auth/saml/metadata
Name redmine
Description Whatever text you want
Always display in UI For our example: Off

Click Next and finish configuration:

Field Value
Root URL Used to build URLs on the fly.
For our example: empty
Home URL The Redmine instance URL.
For our example: https://redmine.nicksopenworld.com
Valid redirect URIs Authorized redirect URL. Wildcard using URL can be used.
Structure: https://REDMINE_URL/auth/saml/callback
For our example: https://redmine.nicksopenworld.com/auth/saml/callback
Valid post logout redirect URIs Authorized URLs to manage logout.
For our example: empty
IDP-Initiated SSO URL name To be used if supported by the SAML client.
For our example: empty
IDP Initiated SSO Relay State To be used if supported by the SAML client.
For our example: empty
Master SAML Processing URL Main SAML management URL.
Structure: https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/saml
For our example: https://broker.nicksopenworld.com/auth/realms/nicksopenworld/protocol/saml

SAML capabilities

Here we have to set a bit more configuration:

Field Value
Name ID format presistent
Force name ID format On
Force POST binding On
Force artifact binding Off
Include AuthnStatement On
Include OneTimeUse Condition Off
Optimize REDIRECT signing key lookup Off
Allow ECP flow Off

Certificate encryption configuration

Once the client is created, make sure Signature and Encryption is set to use SHA-1:

Field Value
Sign documents On
Sign assertions On
Signature algorithm Mandatory.
RSA-SHA1
SAML signature key name Optional.
NONE
Canonicalization method EXCLUSIVE

Login/logout client configuration

You also have Login and Logout parameters. We will leave default values for our current setup.

Client scope: decide the attributes pushed by the client

Make sure you have the following attributes configured:

Name Mapper type User Attribute Friendly name SAML attribute name SAML attribute name format Aggergate
last_name User Attribute lastName Family name lastname Basic Off
name User Attribute username Full name username Basic Off
first_name User Attribute firstName Given name firstname Basic Off
email User Attribute email Email address email Basic Off
admin User Attribute admin admin admin Basic Off

If an attribute is missing, you can create it:

Configure the "Redmine Administrator" access through SAML SSO

WARNING:
As of Novembre 2023, this configuration requires using the plugin code from nicolasmetters/redmine_saml
A pull request has been done to the official repository but is pending review.

By default, the Redmine SAML plugin will grand access to users as standard users in Redmine, even if they have been set as Administrator in Redmine's Users.

In order to be able to create Administrators, a dedicated admin attribute must be set up in the client:

Then, you must configure the attribute at the user level:

The plugin code is set to log users:

  • as Administrator if attribute is present and set to true
  • as Standard user if attribute is missing
  • as Standard user if attribute ispresent and set to false

    Switching from Standard user to Administrator only requires updating the attribute.
    This can be done either directly on the user entry or by creating a dedicated group to which you assign the attribute:

    And then you only have to assign the user to that group:

    Set up the plugin on the Redmine host server - Part 2: Create your SAML initializer

    Once the client is set up, we should have all the information needed to finish the configuration on the Redmine host server.
    The plugin requires an initializer .rb file: it can have any name but must be in Redmine's config/initializers/ directory.

    An example can be found in the Git repository in redmine_saml/contrib/.

    For example in my docker-compose.yml is declared:

        volumes:
    - ../config/initializers/saml.rb:/usr/src/redmine/config/initializers/saml.rb

    The content for our example:

    # frozen_string_literal: true
    require Rails.root.join('plugins/redmine_saml/lib/redmine_saml')
    require Rails.root.join('plugins/redmine_saml/lib/redmine_saml/base')

    RedmineSaml::Base.configure do |config| config.saml = { # Redmine callback URL assertion_consumer_service_url: "https://redmine.nicksopenworld.com#{RedmineSaml::CALLBACK_PATH}",
    # The issuer name / entity ID. Must be an URI as per SAML 2.0 spec. sp_entity_id: "https://redmine.nicksopenworld.com#{RedmineSaml::METADATA_PATH}",
    # The SLS (logout) callback URL single_logout_service_url: "https://redmine.nicksopenworld.com#{RedmineSaml::LOGOUT_SERVICE_PATH}",
    # SSO login endpoint idp_sso_service_url: 'https://broker.nicksopenworld.com/realms/nicksopenworld/protocol/saml/clients/redmine',
    # SSO SSL certificate SHA-1 fingerprint idp_cert_fingerprint: '14:20:C3:EA:D9:51:31:4B:EB:42:99:EC:5A:40:A3:1D:EE:FC:93:9B',
    name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
    # Optional signout URL, not supported by all identity providers signout_url: 'https://broker.nicksopenworld.com/realms/nicksopenworld/protocol/saml/clients/redmine',
    idp_slo_service_url: 'https://broker.nicksopenworld.com/realms/nicksopenworld/protocol/saml/clients/redmine',
    # Which redmine field is used as name_identifier_value for SAML logout name_identifier_value: 'mail',
    # overwrite mapping seperator, if required # attribute_mapping_sep: '|',
    attribute_mapping: { # How will we map attributes from SSO to redmine attributes using urn:oid:identifier or friendly names # mail: 'extra|raw_info|urn:oid:0.9.2342.19200300.100.1.3' # or # mail: 'extra|raw_info|email' login: 'extra|raw_info|username', mail: 'extra|raw_info|email', firstname: 'extra|raw_info|firstname', lastname: 'extra|raw_info|lastname', admin: 'extra|raw_info|admin' } } config.on_login do |omniauth_hash, user| # Implement any hook you want here end end

    Once the .rb file is set up, the Redmine SAML plugin should be accessible and usable.

    Configure the Redmine SAML plugin

Plugin general configuration

Log into your Redmine using Administrator access and make sure the plugin is visible in the /admin/plugins page:

Go the the Redmine SAML plugin configuration page /settings/plugin/redmine_saml?tab=settings.

Field Value
Enable SAML authentication Disable it if you want to block SAML SSO without having to uninstall the plugin.
For our example: On
Login page text Whatever text you want.
For our example: SAML SSO
Replace Redmine login page If enabled, the username & password native login method for Redmine is disabled.
For our example: Off
Create users automatically? If disabled, users need to be created in Keycloak and in Redmine separately.
Disabling it greatly reduces the gain of setting up SSO but must be considered based on your security setup and limitations.
For our example: SAML SSO

Redmine & SAML general configuration

You can also check the configuration and informatin for the plugin on the Information tab:

On this page you can see the values that have been written to the .rb initializer file but also a few verifications:

This is not necessarily an error on the Redmine host server: in Redmine's Settings ( page "/settings" ), the Host name and path had been saved with an extra "/" symbol inducing a failure of the URL format test.
Once the Settings have been updated accordingly, we can confirm all is OK:

Test the SSO connection & debug

Once all is set, create a new user in your Keycloak platform that doesn't exist in Redmine and set up a password for it.
In a private navigation browser session, go to your Redmine login page and click on the SSO button. You should be redirected to the Keycloak login page.

Connect with the username and password you have just set and you should be redirected to your Redmine as a logged in user:

If the connection fails, check your Redmine logs: they should contain the SAML assertion received but can also provide error details.
For example:

[2023-11-20T20:12:33.142974 #1] DEBUG -- omniauth: (saml) Callback phase initiated.
[2023-11-20T20:12:33.165051 #1] ERROR -- omniauth: (saml) Authentication failure! invalid_ticket: OneLogin::RubySaml::ValidationError, Fingerprint mismatch
[2023-11-20T20:12:33.335371 #1]  INFO -- : Started GET "/auth/failure?message=invalid_ticket&origin=https%3A%2F%2Fredmine.nicksopenworld.com%2F&strategy=saml" 
[2023-11-20T20:12:33.337142 #1]  INFO -- : Processing by AccountController#login_with_saml_failure as HTML
[2023-11-20T20:12:33.337301 #1]  INFO -- :   Parameters: {"message"=>"invalid_ticket", "origin"=>"https://redmine.nicksopenworld.com/", "strategy"=>"saml"}
[2023-11-20T20:12:33.367781 #1]  INFO -- :   Current user: anonymous
[2023-11-20T20:12:33.368497 #1]  WARN -- : login_with_saml_failure: error_saml_invalid_ticket
[2023-11-20T20:12:33.369124 #1]  INFO -- : Redirected to https://redmine.nicksopenworld.com/login
[2023-11-20T20:12:33.369426 #1]  INFO -- : Completed 302 Found in 32ms (ActiveRecord: 1.6ms | Allocations: 1110)

Here we can see invalid_ticket but also the reason of it: OneLogin::RubySaml::ValidationError, Fingerprint mismatch

In this case, the reason was that I was checking SHA-256 support (which it is not) so the system was not capable of decoding the fingerprint provided.
But I've also have the error when putting the wrong fingerprint string value in my saml.rb initializer configuration file.

Conclusion

With this, you should now have a simplified way to manage your Redmine Users.

Please note that this plugin can be used on a Redmine instance on which are also installed:

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x