IBM Technical Exchange India Security User Group

 View Only

Authentication and Authorization of Ceph Object storage using Open ID Connect (OIDC)

By Deepak Thorat posted Wed March 27, 2024 07:41 AM

  

Acknowledgment:
Sandeep Patil (STSM, IBM Storage), Vivek Jain (Senior Engineer - IBM Security Verify), Daniel Alexander Parkes (IBM Storage Ceph Technical Product Manager)

Content:

Introduction
Background
IBM Ceph Storage Configuration Steps
IBM Security Verify Configuration Steps
Validation of Authentication to Ceph Object Storage: 
Conclusion
Authors

Introduction: 
  
IBM Storage Ceph IBM Storage Ceph is a versatile and open multi-protocol storage solution designed to consolidate data from anywhere across the globe. It's a software-defined object storage solution optimized for enterprise, easy to use, and flexible. IBM Storage Ceph is engineered to be self-healing and self-managing with no single point of failure, providing you with a reliable and scalable data platform. 

 

 

IBM Security Verify (ISV) protects users and applications inside and outside the enterprise while enabling technical agility and operational efficiency as a cloud-native solution. Beyond single-sign-on and multifactor authentication, ISV is a modernized, modular IDaaS that provides deep AI-powered context for risk-based authentication and adaptive access decisions, guided experiences for developer time-to-value, and comprehensive cloud IAM capabilities. From privacy and consent management to holistic risk detection and identity analytics, ISV centralizes workforce and consumer IAM for any hybrid cloud deployment. 

 

 

The goal of this article is to provide a step-by-step guide for ISV tenant admin to configure Single-Sign-On (SSO) between ISV and Ceph Storage Object so that it allows end-user to access Ceph Object Storage using ISV-provided JSON Web Token (JWT). 

Background:

Ceph Object Gateway:

The Ceph Object Gateway node runs the ceph-radosgw daemon and is an object storage interface built on top of librados to provide applications with a RESTful access point to the Ceph storage cluster. The Ceph Object Gateway supports two interfaces: 

  • S3: Provides object storage functionality with an interface compatible with a large subset of the Amazon S3 RESTful API. 

  • Swift: Provides object storage functionality with an interface compatible with a large subset of the OpenStack Swift API. 

In this article, we are using the S3 interface. 

IBM Storage Ceph Object Storage can be integrated with IBM Security Verify through Open ID Connect 1.0 (OIDC); S3 users who want to access their object storage in Ceph can authenticate against an SSO using ISV. 

 

IBM Storage Ceph Object utilizing Secure Token Service (STS) and IAM roles for authorization. IAM allows us to implement an authorization model based on roles(RBAC) or attributes(ABAC); the user can assume an IAM Role based on the information provided in his SSO/JWT token.   

 

At a high level, the workflow works in the following way:   

  • The user authenticates against ISV and ISV provides a JWT token  

  • The user requests that Ceph (STS) assume an IAM role and provide the JWT to Ceph.   

  • Ceph introspects the user-provided token and checks if the JWT attributes match the role policy.   

  • Ceph (STS) provides the user temporary keys to access the object storage endpoint and the S3 resources to his assumed role.  
     

In this scenario, ISV acts as the Identity Provider, while Ceph is configured as a Service Provider, operating as an application in ISV. To enable user authentication, the identity of the users must exist in ISV. Moreover, the user account must also be present on the target - Ceph.   

IBM Security Verify configuration steps: 

We will register Ceph Dashboard as a custom application using IBM Security Verify (ISV) 'Custom application' capability. Below are the steps to follow for ISV-Ceph OpenID connect integration. 

  
Step 1: Create Custom Application in ISV Admin console:  

  • Login to the ISV Admin console and select Application tab -- > Click Add Application  → Select Application type as Custom Application.

  • In the Application SettingGeneral tab, provide the Application name and Company name 

  • Then, in the Sign-On tab, select/ fill in the below details: 

  • Sign-on method: Open ID Connect 1.0 

  • Application URL : https://<ceph_dashboard_host>:<port>/ 

    • Select below Grant types:  

    • Authorization code  

    • Implicit  

    • Resource owner password credentials (ROPC)  

    • JWT bearer  

  • Redirect URIs: https://<ceph_dashboard_host>:<port>/  

  • Client authentication method: Default  

  • Add below attribute mapping  

    • username: preferred_username  

    • email: email  

    • groups: groupIds  

  • Access Policies:  

    • Use default Policy  

  • Note: Keep another configuration as it is.  

    Then, Save the Application.  


    Below is a sample snapshot for your reference.  

    Note: Some of the above settings may change based on your requirements and system details.  

     

    Then, the SSO tab provides configuration details under the OpenID Connect single sign-on (SSO) configuration section. Under this section, there is an option with the name Provide the IBM Security Verify endpoint by using the following format:    

     

    This endpoint contains the following information:  

    • Authorization endpoint URL  

    • Token endpoint URL  

    • User Info endpoint URL  

    • JSON Web Key Set (JWKS) endpoint URL  

    • Device Flow user authorization endpoint URL  

    • Device Flow device authorization endpoint URL  

    These details are required at Ceph to configure SSO.  


    IBM Ceph Storage Configuration Steps:

    From the IBM Storage Ceph cluster, we need to have connectivity against ISV; as an initial test, we are going to be using curl to query the well-known URL of our ISV tenant: 

     

    # curl -s https://<isv-tenant>/v1.0/endpoint/default/.well-known/openid-configuration | jq . | grep endpoint
    "introspection_endpoint": "https://<isv-tenant>/v1.0/endpoint/default/introspect",
    "issuer": "https://<isv-tenant>/oidc/endpoint/default",
    "authorization_endpoint": "https://<isv-tenant>/v1.0/endpoint/default/authorize",
    "device_authorization_endpoint": "https://<isv-tenant>/v1.0/endpoint/default/device_authorization",
    "token_endpoint": "https://<isv-tenant>/v1.0/endpoint/default/token",
    "user_authorization_endpoint": "https://<isv-tenant>/v1.0/endpoint/default/user_authorization",
    "revocation_endpoint": "https://<isv-tenant>/v1.0/endpoint/default/revoke",
    "userinfo_endpoint": "https://<isv-tenant>/v1.0/endpoint/default/userinfo",
    "jwks_uri": "https://<isv-tenant>/v1.0/endpoint/default/jwks",
    "registration_endpoint": "https://<isv-tenant>/v1.0/endpoint/default/client_registration",

    Once we have confirmed connectivity from the Ceph cluster, we want to test the ISV app user and password provided using the password credential grant type to retrieve a JWT token. We will create a simple script with the credentials that we can reuse if needed. 

    # cat << EOF > get_web_token.sh
    curl -k -q -s -L -X POST "https://<isv-tenant>/v1.0/endpoint/default/token" \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    --data-urlencode 'client_id=89cbca68-c6f6-4b60-8fdb-d728adbd7e6e' \
    --data-urlencode 'grant_type=password' \
    --data-urlencode 'client_secret=#####' \
    --data-urlencode 'scope=openid' \
    --data-urlencode "username=user@gmail.com" \
    --data-urlencode "password=XXXX"
    EOF
    # bash get_web_token_isv.sh | jq .
    {
      "access_token": "owTttsRJjdereKMjbKuRF3toV_MhAA7dLxfG2U-buY0.A3naOOLA8x-PH5eMAGWPlOFp9N4N7ZOlAz8y6RfN7m0MgO-yOXrW4OXkQH3N-xDN-XJ5FHn9brFDA0pqTR6Tfg.M18xNzAxMDczMDY0XzQy",
      "scope": "openid",
    "grant_id": "228bb255-d2a7-41bc-b9d8-9176d64e582c",
      "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNlcnZlciJ9.eyJ1c2VyVHlwZSI6InJlZ3VsYXIiLCJ1bmlxdWVTZWN1cml0eU5hbWUiOiI2MDkwMDA3MEw3IiwiZGlzcGxheU5hbWUiOiJEZWVwYWsgUiBUIiwianRpIjoiTERSbHZCSEF6RlNoRnBnSXZiM0Y4YTdXdnIwa01oIiwicmVhbG1OYW1lIjoiY2xvdWRJZGVudGl0eVJlYWxtIiwiYXRfaGFzaCI6IlhJRlRlWjNGa0I3SGxObWhjVFQtT2ciLCJuYW1lIjoiRGVlcGFrIFIgVCIsInByZWZlcnJlZF91c2VybmFtZSI6ImRlZXBhay40NjQxQGdtYWlsLmNvbSIsImlzcyI6Imh0dHBzOi8vYXV0aHN2Yy1pbmxpbmVtZmEuZGV2LnZlcmlmeS5pYm1jbG91ZHNlY3VyaXR5LmNvbS9vaWRjL2VuZHBvaW50L2RlZmF1bHQiLCJhdWQiOlsiODljYmNhNjgtYzZmNi00YjYwLThmZGItZDcyOGFkYmQ3ZTZlIl0sInN1YiI6IjYwOTAwMDcwTDciLCJpYXQiOjE3MDEwNzMwNjQsImV4cCI6MTcwMTA4MDI2NH0.etfFx3zruenKZYcaBu6V95j6psXD7_HjRjpDirqFPZ_3RUFC9oZYagVMjrsRhCV4TpdGYLnZMLcYg4tRfNhduvdmDsLbdGc32mqxLs2-Zrv1v0FZjE-Hc2WUG21QiBxLOYRHUR56jB_bQJ_av9UAvnxB39yvx4rYrQyBivoMkNGbgiq2EV4lz7S2vl4IRL1zN_0gph27lEONZTrs8318IcgiIvi6IcAF4zSZMmmGW4h11DMLNOvINcP6-NpAOiAXPfKe_v6JJRs9WiDugkNcSsr-q_y53BxZQtXoBG0-hoPfAw74LFY9qtbxGDw8XZGFLNYLZkH25TsLm5Mah7x3Sg",
      "token_type": "Bearer",
      "expires_in": 7199
    }

    Validation of Authentication to Ceph Object Storage: 

    We can retrieve an access token successfully, so we have confirmed our ISV setup and credentials are correct; we will do a final validation step, which is to introspect our JWT to see all the attributes the token contains; we will be using the previous retrieve token script, to access the token and introspect it. 

    # cat << EOF > instropect_token_isv.sh
    KC_CLIENT="89cbca68-c6f6-4b60-8fdb-d728adbd7e6e"
    KC_CLIENT_SECRET="####"
    KC_ACCESS_TOKEN="$(./get_web_token_isv.sh | jq -r '.access_token')"
    curl -s -k -q \
         -X POST \
         -u "$KC_CLIENT:$KC_CLIENT_SECRET" \
    -d "token=$KC_ACCESS_TOKEN" \
    "https://<isv-tenant>/v1.0/endpoint/default/introspect" | jq .
    EOF  

    # bash instropect_token_isv.sh
    {
      "at_hash": "P9u_zC8dJKnzrWBC5mTj8Q",
      "sub": "60900070L7",
      "realmName": "cloudIdentityRealm",
      "uniqueSecurityName": "60900070L7",
      "iss": "https://<isv-tenant>/oidc/endpoint/default",
      "groups": [
        "admin",
        "developer",
        "Cloud Directory only",
        "architect"
      ],
      "active": true,
      "preferred_username": "XXXXX@gmail.com",
      "token_type": "Bearer",
      "client_id": "89cbca68-c6f6-4b60-8fdb-d728adbd7e6e",
      "aud": [
        "89cbca68-c6f6-4b60-8fdb-d728adbd7e6e"
      ],
      "grant_type": "resource_owner",
      "token_use": "access_token",
      "grant_id": "5bc9c58f-6aad-4ef8-b53a-05ef6cda6c22",
      "scope": "openid",
      "userType": "regular",
      "exp": 1701080301,
      "app_id": "8892470245991660012",
      "iat": 1701073101,
      "email": "XXXX@gmail.com",
      "username": "XXXX@gmail.com"
    } 

    From the introspection, you can see that our user has many attributes defined. These attributes can be passed on from the enterprise IDP like LDAP/AD or configured on the ISV. We can use these attributes in Ceph to define what S3 resources the user can access; for example, in the introspection of the JWT we have a group attribute listing all the groups to which this user belongs; we will use that attribute to define what S3 resources users belonging to that group can access, following a traditional RBAC authentication model. 

     

    Now that we have verified the ISV configuration, let’s connect Ceph to the ISV using the IAM API to create a new OIDC provider. To be able to achieve this, we need to create a fingerprint from the x5c certificate that we can retrieve from the /jwks well-known URL: 

    # cat get_fingerprint_isv.sh
    #!/bin/bash 
    set -o pipefail
    CERT_FILE=$(mktemp)
    cat << EOF > ${CERT_FILE}
    -----BEGIN CERTIFICATE-----
    $(curl -k https://<isv-tenant>/v1.0/endpoint/default/jwks | jq -r .keys[0].x5c[])
    -----END CERTIFICATE-----
    EOF
    openssl x509 -in ${CERT_FILE} -fingerprint -noout | awk -F = '{ print $NF }' | sed 's/://g'
    rm -f ${CERT_FILE} 

    # bash get_fingerprint_isv.sh
    C8C4A3A283F8A4CBD04B46E2215EA98B18748574 

      

    We already possess the x5c certificate. To obtain the ISV OIDC provider URL, you can check the ISS attribute in your JWT, perform an initial introspection, or rerun the script. 

    # bash instropect_token_isv.sh | jq .iss
    "https://<isv-tenant>/oidc/endpoint/default"

    To create an OIDC provider, a dedicated s3 user with OIDC permissions be created and assigned the OIDC caps. 

    # radosgw-admin user create --uid oidc --display-name "Open ID Connect Provider" --access-key oidc --secret oidc
    # radosgw-admin caps add --uid="oidc" --caps="oidc-provider=*"

    Now that we have all the required pre-requisites:  

    • x5c certificate thumbprint 

    • S3 user with OIDC permissions 

    • The ISV OIDC Url 

      User consent: Do not ask for consent 

    Let’s create the OIDC provider in IBM Storage Ceph Object. I will use the aws cli, but you could use any other method. 

    # export AWS_ACCESS_KEY_ID=oidc 
    # export AWS_SECRET_ACCESS_KEY=oidc 
    # export AWS_ENDPOINT_URL=https://cephproxy1.stg.local:8443 
    # export URL="https://<isv-tenant>/oidc/endpoint/default" 
    # export THUMBPRINT=C8C4A3A283F8A4CBD04B46E2215EA98B18748574 
    # aws iam create-open-id-connect-provider --url $URL --thumbprint-list $THUMBPRINT 
    # aws --profile oidc2 --endpoint https://cephproxy1.stg.local:8443 iam list-open-id-connect-providers  --region default 

        "OpenIDConnectProviderList": [ 
            { 
                "Arn": "arn:aws:iam:::oidc-provider/<isv-tenant>/oidc/endpoint/default" 
            }, 
        ] 

     

    We have completed the setup of our OIDC provider. In the next step, we are required to define an IAM role in a JSON file. We are setting up a statement to allow the "AssumeRoleWithWebIdentity" action if the JWT token presented by the user matches the "admin" group attribute condition. 

    # cat << EOF > role-rgwadmins-app_id_isv.json 

     "Version": "2012-10-17", 
     "Statement": [ 
       { 
         "Effect": "Allow", 
         "Principal": { 
           "Federated": [ 
             "arn:aws:iam:::oidc-provider/<isv-tenant>/oidc/endpoint/default" 
           ] 
         },
         "Action": [ 
           "sts:AssumeRoleWithWebIdentity" 
         ], 
         "Condition": {   
           "StringLike": { "<isv-tenant>/oidc/endpoint/default:sub":"60900070L7" } 
         } 
       } 
     ] 

    EOF 
     
    # radosgw-admin role create --role-name rgwadmins \
    --assume-role-policy-doc=$(jq -rc . /root/role-rgwadmins.json) 
    {
        "RoleId": "e5e2ab57-f57f-46ad-a486-2a5e1435522d", 
        "RoleName": "rgwadmins4", 
        "Path": "/", 
        "Arn": "arn:aws:iam:::role/rgwadmins", 
        "CreateDate": "2023-11-27T08:28:44.877Z", 
        "MaxSessionDuration": 3600, 
        "AssumeRolePolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Federated\":[\"arn:aws:iam:::oidc-provider/keycloak-sso.apps.ocp.stg.local/auth/realms/ceph\"]},\"Action\":[\"sts:AssumeRoleWithWebIdentity\"],\"Condition\":{\"StringEquals\":{\"keycloak-sso.apps.ocp.stg.local/auth/realms/ceph:groups:groups\":\"rgwadmins\"}}}]}" 
    }

    Once the role is successfully created, we need to specify what S3 resources the user will have access to once he assumes the IAM rgwadmins role. We provide a simple example in JSON format by which the user assumes the rgwadmins role will have access to all S3 resources, but we can be as granular as we need. 

    # cat << EOF > policy-rgwadmin.json

      "Version": "2012-10-17", 
      "Statement": [ 
        { "Effect": "Allow", 
          "Action": [ "s3:*" ], 
          "Resource": "arn:aws:s3:::*" 
        } 
    ] } 
    EOF 

    # radosgw-admin role policy put --role-name=rgwadmins 
    --policy-name=admin --policy-doc=$(jq -rc . /root/policy-rgwadmin.json)  
     

    We have completed the configuration of the IAM role. Now, we can perform an end-to-end test to authenticate against the ISV and obtain a JWT. After that, we can assume the rgwadmin role using the JWT token, and STS will provide us with credentials to access the role's S3 resources. To create an example script, we'll call it assume_role_rgwadmin.sh. During runtime, we need to specify the IAM role name we want to assume, which, in this example, is rgwadmins. 

    # cat << EOF > assume_role.sh
    #!/bin/bash 
    export AWS_CA_BUNDLE="/etc/pki/ca-trust/source/anchors/cert.pem" 
    unset AWS_ACCESS_KEY_ID 
    unset AWS_SECRET_ACCESS_KEY 
    unset AWS_SESSION_TOKEN 
    echo -e "\n" 

     
    echo "Getting the JWT from SSO..." 
    KC_ACCESS_TOKEN="$(./get_web_token_isv.sh | jq -r '.id_token')" 
    echo -e "\n" 
    if [ $? != 0 ] ; then echo "Failed to get token from SSO" ; exit 1  ; fi 

     
    echo "Trying to Assume Role $1 using provided JWT token.." 
    echo -e "\n" 
    IDM_ASSUME_ROLE_CREDS=$(aws sts assume-role-with-web-identity --role-arn "arn:aws:iam:::role/$1" --role-session-name testbr --endpoint=https://cephproxy1.stg.local:8443 --web-identity-token="$KC_ACCESS_TOKEN") 


    echo "Export AWS ENV variables to use the AWS CLI with the STS creds.. " 
    export AWS_ACCESS_KEY_ID=$(echo $IDM_ASSUME_ROLE_CREDS | jq -r .Credentials.AccessKeyId) 
    export AWS_SECRET_ACCESS_KEY=$(echo $IDM_ASSUME_ROLE_CREDS | jq -r .Credentials.SecretAccessKey) 
    export AWS_SESSION_TOKEN=$(echo $IDM_ASSUME_ROLE_CREDS | jq -r .Credentials.SessionToken) 
    export AWS_ENDPOINT_URL=https://cephproxy1.stg.local:8443 
     
    echo "Also providing the keys to use in the AWS credentials file.." 
    echo -e "\n" 
    echo "AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID" 
    echo "AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY" 
    echo "AWS_SESSION_TOKEN: $AWS_SESSION_TOKEN" 
    EOF 

     
    # . ./assume_role.sh 

     
    Getting the JWT from SSO... 

    Trying to Assume Role rgwadmins using provided JWT token.. 
     
    Export AWS ENV variables to use the AWS CLI with the STS creds..  
    Also, providing the keys to use in the AWS credentials file.. 

    AWS_ACCESS_KEY_ID: KfnALjZDshGBCAbKxVU
    AWS_SECRET_ACCESS_KEY: 024CJPUDJ7REHRZSSLB581KC7B0TVTCAAFRWSIP 
    AWS_SESSION_TOKEN: b52ruURQaSrb3iKk1ZolkR471mWsYC7dGEkJj3FxiWTj39TB5tpEA0Zk2EFKdqzjO5##### 

    After executing the previous script, we successfully exported the required S3 access and secret keys into our environment session. Consequently, we can now access the IBM Ceph Object resources using our STS temporary token credentials: 

    # aws s3 ls 
    # aws s3 mb s3://bucket1 
    # aws s3 ls 
    2023-11-23 19:48:21 bucket1 

      

    Conclusion:  

    1. This article aims to help Ceph administrators set up Single Sign-On (SSO) between IBM Security Verify (ISV) and Ceph easily and straightforwardly.  
    1. Using the ISV admin console capability, Ceph administrators can manage users with specific roles efficiently.  

    1. Ceph administrators can implement a powerful and granular authorization model based on JWT attributes and IAM roles.  

    1. End-users can effortlessly authenticate in Single Sign-On and access Ceph Object Storage.  

    1. Furthermore, in the future, Ceph administrators will easily introduce Multi-Factor Authentication (MFA) for end users to ensure secure access to Ceph Object Storage. 

     

    Authors:  

    • Vivek Jain - vivjain2@in.ibm.com  

    • Deepak Thorat – dthorat1@in.ibm.com 

    • Daniel Alexander Parkes - dparkes@ibm.com

0 comments
35 views

Permalink