SAML Plugin Basics

Article ID:227202668
3 minute readKnowledge base

Issue

We want to use SAML SSO, but we do not know how it works and how to configure SAML Plugin. We do not know how to create the IdP file.

Resolution

As of version 1.0.4 SAML plugin is verified by the CloudBees Assurance Program. If you are using CloudBees Jenkins Solution of version 2.73.2.1 or newer we recommend using the SAML plugin version that comes verified with your release.

SAML Plugin only supports Authentication and Authorization challenges. It does not synchronize "External" groups or roles in Jenkins. Nevertheless, if your SAML Identity Provider (IdP) supports Group attribute, you can configure it in the SAML Security Realm, and these groups are going to be retained and can be used in the configuration of Authorization Strategies.

IdP metadata must look like in the example below. Usually, an IdP server can generate it for you, or you can request it from your provider. In case it’s not available, it could make manually with the metadata from the server.

In the example below EntityDescriptor contains data about the identity provider. The entityID attribute is its unique identifier. The rest of the IdP metadata contains information about how to sign and encrypt messages and the URLs to log in and log out. For more information and further references see SAML 2.0 Metadata

SAML Plugin 1.0.4 and less only supports urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect SingleSignOnService Binding.

SAML Plugin 1.0.5 adds support for the urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST Binding

IdP metadata has to be configured in Manage Jenkins > Configure Global Security > Security Realm > SAML > SAML Identity Provider Settings > IdP Metadata.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<EntityDescriptor xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
    xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
    entityID="https://SAML_SERVER/idp/">
    <!-- The SSO service at the identity provider -->
    <IDPSSODescriptor WantAuthnRequestsSigned="false"
        protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
        <KeyDescriptor use="signing">
            <ds:KeyInfo xmlns:ds="https://www.w3.org/2000/09/xmldsig#">
                <ds:X509Data>
                    <ds:X509Certificate>sEqZ5xC8GFgcPcUOI6CnUYDnO630cAqHjLdVp82eaxFRr7F8j75Go7b/HiWcp87iOAzQ8NrCWUwYzny7mogvopNq2/oONqNXML4pM6v8epHWAZePBcW+o5tVJMYseAl7bwX/VE3Ro8LakHoBbDlRaiHT3A55p4Vyp0OZUGHLazrV9yCKvvDPU+pPocB4PKR3nBOQ9AyF0r1a8T8/y90X59BpHJxhXfJSgT8U/95KIV9+cEb11BApr5a3KrxIZAep6CC4C9MVmIsUSjpM6bOt4qqiQC9WVbD5i5ZnimiFYvHt/QOvyFT751T9QylWz2SGwzyqwG6+LZXswbeITjcrSZkdInkkWybqC+igvOrSOi6sSn5GjSHQqskCI6GwYNQ9ndAsWBwRdyx+ydZGVo0riZurc/YdhH13VpLnx6Vrk8+Sbf0oHqr7BSdSnl1bi/qptIg9ksF5Zw8Rkep9118A88w7uEEBO3q+fGfE72FYMxsp5k/MSLgKkwqlCpqzhmCd2L6ZU/g45sEQSwdaS9YzsY7o4kGrzSzCGxsinP/67UddiTFiJNan2zzVyeVUdKthbek5hHNord9durQXN8O4t5wlMFS+67+ReCs1g/S+eKJelvH5aPtbPE7lttt/hZ8LgKX2vGT5yFcQmT46Kj6u3tSbNGnipl4BQ1ItbQZoe1g=
                    </ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </KeyDescriptor>
    <!-- Supported Name Identifier Formats -->
    <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
    <!-- AuthenticationRequest Consumer endpoint -->
    <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://SAML_SERVER/idp"/>
    <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://SAML_SERVER/idp"/>
</IDPSSODescriptor>
</EntityDescriptor>

When the AuthRequest is validated the identity provider send a SAMLResponse to [JENKINS_LOCATION]/securityRealm/finishLogin. This response contains the information about the user, session ID, authorization groups, signature of message and expiration of session. An XML message similar the one below is expected:

<Response
    xmlns="urn:oasis:names:tc:SAML:2.0:protocol"
    Destination="https://JENKINS_SERVER/securityRealm/finishLogin"
    ID="_c266abbff66bba8bcd763443655ea1c5861d"
    InResponseTo="_75a5cb8c9514c22751e05b29e698e0e8"
    IssueInstant="2016-04-18T19:04:53Z"
    Version="2.0">
    <ns1:Issuer xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion"
        Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://SAML_SERVER/idp/</ns1:Issuer>
    <Status>
        <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
    </Status>
    <ns2:Assertion xmlns:ns2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_4d406d6505202232c48a50726c55d58f548c"
        IssueInstant="2016-04-18T19:04:53Z" Version="2.0">
        <ns2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://SAML_SERVER/idp/</ns2:Issuer>
        <ds:Signature xmlns:ds="https://www.w3.org/2000/09/xmldsig#">
            <ds:SignedInfo>
                <ds:CanonicalizationMethod Algorithm="https://www.w3.org/2001/10/xml-exc-c14n#"/>
                <ds:SignatureMethod Algorithm="https://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
                <ds:Reference URI="#_4d406d6505202232c48a50726c55d58f548c">
                    <ds:Transforms>
                        <ds:Transform Algorithm="https://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                        <ds:Transform Algorithm="https://www.w3.org/2001/10/xml-exc-c14n#"/>
                    </ds:Transforms>
                    <ds:DigestMethod Algorithm="https://www.w3.org/2001/04/xmlenc#sha256"/>
                    <ds:DigestValue>B+nZTeDSNSpigeyDg2475274242ARIw6ttEXHY3PMk=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>VTCuyYj09/CbuU7+pX6g3wjTlocTH83RkWEG6xy2t1ZSDPS0Q0gjfmh8/HMNSOoold9i2zY5Qi4/idZ7yKBe0nR7WDZDPkc3FSovvX73FThJEZ5aJk/6uhr5yUzj3qypA9bLsHdMO75SfaDzotb0c4mIBWLuPX245sZretx6pNRHDYntgQB9ikYC6UQPuSwn1+p/iq1B+GnbNp7m+og0rL5ooc7jPnpqiWBn2648ZCSsnoemrCiSmDVR90XJ7GFEz27W7BH8ZH49DdML6xmqiBvWmZC7LpfkcoF54mLZMdVYM=
            </ds:SignatureValue>
            <ds:KeyInfo>
                <ds:X509Data>
                    <ds:X509Certificate>gpEQ4+mCQGMhwPtrqp1fPXpocgNZ9NkH/FZ62bzYTswVBF6VJPm5VuslmxGTVOMBd/qNKin/xlX2nL5J4mABXZ3OrUcyX//cwK3zqyS2Gn9LaAQnwOdkpXVQzeufCS0agrpfOtEKwWHgbs1m4Dfcl82SWOSbBZhLOUmtDMa0y1DqMB2nxVwJY8ULD+p3HUJGnGxx7JvGBo3OM/tZ3DC7zC0QGlPfuMFPT1GuKsdG11OZ1kWNa9XxQj8pOpPDuiBrxCJDz5vM00ThgnkORITYSkWPO05oe+RBms/qcfYH5JOiR5NJMxojAv2jqvAT8YES1l376yaHWEampzH1nWdW9XQvpr9l297yK7GxGU3Pj4M7ryalYXyH/5tqGFkcQQsX6TDUcmy4M6DlRTe3f7U3duEA1KlQApShDU7lYt41g8vv7pVFN14z9ZNhztJIErmkvR0H/QHR2SVg+WWM0Ql+rMzgsYVfPa4xCIpfEmiWujeeboJay+492k3Im5XbUUCG49UHigyoaAbLIwrCpnFLd3bGlAjun75WWbAHlaILkXAhTNMSPpbWBXrfhwgLwYK5zLGgkpsQPKzzAvQoLHT0wP0R1CbHWGmyNE45ArY3QK0BFb0IRxysNjYIu276JkDjxpdK93ofnWImwE9NLxWh/rqJ2IJ/+6dl8tnYVoT1adg=
                    </ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </ds:Signature>
        <!-- User information -->
        <ns2:Subject>
            <ns2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">USER_NAME</ns2:NameID>
            <ns2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
            <ns2:SubjectConfirmationData InResponseTo="_75a5cb8c9514c22751e05b29e698e0e8" NotOnOrAfter="2016-04-18T19:06:23Z"
                Recipient="https://JENKINS_SERVER/securityRealm/finishLogin"/>
            </ns2:SubjectConfirmation>
        </ns2:Subject>
        <!-- expiration of session -->
        <ns2:Conditions NotBefore="2016-04-18T19:04:23Z" NotOnOrAfter="2016-04-18T19:06:23Z">
            <ns2:AudienceRestriction>
                <ns2:Audience>https://JENKINS_SERVER/securityRealm/finishLogin</ns2:Audience>
            </ns2:AudienceRestriction>
        </ns2:Conditions>
        <ns2:AuthnStatement AuthnInstant="2016-04-18T19:04:53Z" SessionIndex="/47O5ynZIyr+2365762LqnEmAZs=JI+mPg=="
            SessionNotOnOrAfter="2016-04-18T19:06:23Z">
            <ns2:AuthnContext>
                <ns2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</ns2:AuthnContextClassRef>
                </ns2:AuthnContext>
        </ns2:AuthnStatement>
        <!-- Authorization Groups -->
        <ns2:AttributeStatement>
            <ns2:Attribute name="groups">
                <ns2:AttributeValue>groupOne</ns2:AttributeValue>
                <ns2:AttributeValue>groupTwo</ns2:AttributeValue>
                <ns2:AttributeValue>groupThree</ns2:AttributeValue>
            </ns2:Attribute>
        </ns2:AttributeStatement>
    </ns2:Assertion>
</Response>

Additional Configurations

There are some special configurations where even after validating the IdP metadata and perform other tests, the authentication does not work. If you are using a Federation Service Provider (FSP) such as SiteMinder, and your profile configuration requires that the SAML request includes certificate information, you will most likely get an error 500. But on the Jenkins side, you will not see any exception as the FSP is just returning a 500 Server Error. You will see some errors in the FSP like the ones shown below:

Example output for a Federation Service Provider

[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][resource is: /]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][resolved variable list is: ]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][Calling authorizeEx to invoke SAML2 assertion generator.]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][Request to policy server for generating saml2 assertion/artifact based on selected profile. [CHECKPOINT = SSOSAML2_GENERATEASSERTIONORARTIFACT_REQ]]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][Transient IP check: false]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][Result of authorizeEx call is: 1.]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][Received the assertion/artifact response based on profile selected. [CHECKPOINT = SSOSAML2_RECEIVEDASSERTION_RSP]]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][Not enforcing ForceAuthnTimeouts.]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][Received the following response from SAML2 assertion generator: SAML2Response=NO.]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][Transaction with ID: ID failed. Reason: FAILED_INVALID_RESPONSE_RETURNED]
[DATE][TIME][1044][4500]Transaction_ID][SSO.java][processAssertionGeneration][Denying request due to "NO" returned from SAML2 assertion generator.]
[DATE][TIME][1044][4500]Transaction_ID][ErrorRedirectionHandler.java][redirectToErrorPage][Sending HTTP Error 500 ]

You will need to enable the Encryption Configuration check and provide the Keystore path and password so that the certificates needed can be used by the plugin, and enable the Auth Request Signature so that the key-pair can be used to authenticate and encrypt messages between Service Providers and Identity Providers as allowed by the SAML standard. Once done, your configuration should work as expected.