Esegui l'autenticazione utilizzando i token tramite la federazione di Workload Identity

Questo documento descrive come eseguire l'autenticazione in Google Cloud utilizzando i token tramite la federazione di Workload Identity. Con Workload Identity Federation puoi concedere ai carichi di lavoro SAP on-premise o multi-cloud l'accesso alle risorse Google Cloud senza utilizzare una chiave dell'account di servizio. Puoi utilizzare Workload Identity Federation con Amazon Web Services (AWS) o con qualsiasi provider di identità (IdP) che supporti OpenID Connect (OIDC), come Microsoft Azure o SAML 2.0.

La federazione di Workload Identity segue la specifica relativa allo scambio di token OAuth 2.0. Devi fornire una credenziale del tuo IdP al servizio token di sicurezza, che verifica l'identità della credenziale e, in cambio, restituisce un token federato. Puoi utilizzare questo token per impersonare un account di servizio e ottenere un token di accesso OAuth 2.0 di breve durata. Il token di accesso di breve durata consente di chiamare qualsiasi API Google Cloud a cui ha accesso l'account di servizio.

Per l'autenticazione mediante token tramite la federazione di Workload Identity, i passaggi di configurazione di alto livello sono i seguenti:

  1. Prepara l'IdP esterno.
  2. In Google Cloud, configura la federazione di Workload Identity.
  3. In Google Cloud, crea un account di servizio.
  4. In Google Cloud, consenti al carico di lavoro esterno di impersonare l'account di servizio.
  5. Nell'SDK ABAP per Google Cloud, implementa il codice ABAP per recuperare i token di sicurezza dall'IdP.
  6. Nell'SDK ABAP per Google Cloud, configura la chiave client.

Non tutti i prodotti Google Cloud supportano la federazione di Workload Identity. Prima di configurare l'autenticazione utilizzando la federazione di Workload Identity, esamina l'elenco dei prodotti supportati e delle limitazioni. Per ulteriori informazioni, consulta Federazione delle identità per la forza lavoro: prodotti supportati e limitazioni.

Prepara l'IdP esterno

Devi preparare l'IdP in modo che il carico di lavoro SAP possa ottenere le credenziali che possono essere scambiate con un token di sicurezza Google OAuth 2.0.

Per preparare l'IdP esterno, esegui i passaggi in base al tuo IdP:

Configura federazione di Workload Identity

In Google Cloud, configura il pool di identità per i carichi di lavoro e i provider.

Puoi configurare un pool di identità, ovvero un'entità che consente di gestire le identità esterne. Devi inoltre configurare un provider di pool di identità per i carichi di lavoro, ovvero un'entità che descrive una relazione tra Google Cloud e il tuo IdP.

Per configurare la federazione di Workload Identity, esegui i passaggi in base al tuo IdP esterno:

Tieni presente quanto segue:

  • Numero di progetto: il numero del progetto Google Cloud in cui hai creato il pool di identità per i carichi di lavoro.
  • ID pool: un ID univoco che identifica il pool di identità per i carichi di lavoro.
  • ID provider: un ID che identifica il provider del pool di identità per i carichi di lavoro.

Ti servono per la configurazione della chiave client dell'SDK ABAP.

Crea un service account

Nella console Google Cloud, crea un account di servizio IAM dedicato per accedere alle API Google Cloud. Questo account di servizio deve essere un'entità nel progetto Google Cloud che contiene le API Google Cloud che prevedi di utilizzare con l'SDK.

  1. Nella console Google Cloud, attiva l'API IAM Service Account Credentials, l'API Security Token Service e qualsiasi altra API supportata a cui prevedi di accedere utilizzando l'SDK.

    Vai alla libreria API

    Per informazioni su come abilitare le API Google Cloud, consulta Abilitazione delle API.

  2. Crea un account di servizio che rappresenti il carico di lavoro.

  3. Concedi all'account di servizio i ruoli IAM richiesti per accedere alla funzionalità API. Per comprendere i requisiti dei ruoli per le API Google Cloud, consulta la documentazione relativa alle singole API e segui il principio del privilegio minimo. Per ulteriori informazioni sui ruoli predefiniti specifici delle API, consulta Trovare i ruoli IAM per le API Google Cloud.

Consenti al carico di lavoro esterno di impersonare l'account di servizio

Per consentire al carico di lavoro esterno di impersonare l'account di servizio, esegui i passaggi in base al tuo IdP esterno:

Implementa il codice ABAP per recuperare i token di sicurezza dall'IdP

L'SDK ABAP per Google Cloud fornisce una classe astratta /GOOG/CL_AUTH_WIF_BASE, che ha la logica per recuperare i token di sicurezza OAuth 2.0 dal Security Token Service e i token di accesso OAuth 2.0 dall'API IAM Service Account Credentials. In qualità di sviluppatore, devi creare una classe figlio nel tuo spazio dei nomi che eredita dalla classe astratta /GOOG/CL_AUTH_WIF_BASE.

Per richiamare Cloud Functions dall'SDK ABAP per Google Cloud utilizzando la federazione delle identità per i carichi di lavoro, l'SDK fornisce un'altra classe astratta /GOOG/CL_AUTH_WIF_ID_TOKEN. Se configuri l'autenticazione utilizzando la federazione delle identità per i carichi di lavoro, devi creare un'altra classe figlio nello spazio dei nomi che eredita dalla classe astratta /GOOG/CL_AUTH_WIF_ID_TOKEN. Puoi specificare questa classe figlio nel campo Classe di autorizzazione durante la configurazione della chiave client per richiamare Cloud Functions.

Assicurati di implementare il metodo GET_EXT_IDP_TOKEN nella classe figlio e scrivi la logica per ottenere il token di sicurezza dall'IdP. Compila i seguenti campi:

  • CV_TOKEN: il token recuperato dall'IdP nel formato string.
  • CV_TOKEN_TYPE: il tipo di token di sicurezza recuperato dal tuo IdP. I tipi di token supportati sono:
    • urn:ietf:params:oauth:token-type:jwt
    • urn:ietf:params:oauth:token-type:id_token
    • urn:ietf:params:aws:token-type:aws4_request
    • urn:ietf:params:oauth:token-type:access_token
    • urn:ietf:params:oauth:token-type:saml2

I valori compilati in CV_TOKEN e CV_TOKEN_TYPE vengono quindi utilizzati dai metodi della classe astratta /GOOG/CL_AUTH_WIF_BASE per scambiare e recuperare il token OAuth 2.0 finale, che viene utilizzato nelle chiamate API.

L'esempio seguente mostra le implementazioni di esempio del metodo GET_EXT_IDP_TOKEN per altri cloud provider come AWS e Azure.

AWS

class ZCL_AUTH_WIF_AWS definition
  public
  inheriting from /GOOG/CL_AUTH_WIF_BASE
  final
  create public .

public section.

  types:
    BEGIN OF t_header_field,
      key type string,
      value TYPE string,
    END OF t_header_field .
  types:
    tt_header_field type STANDARD TABLE OF t_header_field WITH DEFAULT KEY .
  types:
    BEGIN OF t_token_request,
     url type string,
     method type string,
     headers type tt_header_field,
   END OF t_token_request .
protected section.

  methods GET_EXT_IDP_TOKEN
    redefinition .
private section.
ENDCLASS.



CLASS ZCL_AUTH_WIF_AWS IMPLEMENTATION.


METHOD get_ext_idp_token.
**********************************************************************
*  Copyright 2024 Google LLC                                         *
*                                                                    *
*  Licensed under the Apache License, Version 2.0 (the "License");   *
*  you may not use this file except in compliance with the License.  *
*  You may obtain a copy of the License at                           *
*      https://www.apache.org/licenses/LICENSE-2.0                   *
*  Unless required by applicable law or agreed to in writing,        *
*  software distributed under the License is distributed on an       *
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,      *
*  either express or implied.                                        *
*  See the License for the specific language governing permissions   *
*  and limitations under the License.                                *
**********************************************************************

  DATA: ls_key       TYPE /goog/client_key.

  /goog/cl_utility=>get_client_key( EXPORTING iv_keyname    = iv_keyname
                                        IMPORTING es_client_key = ls_key ).


  DATA: lv_awsdate TYPE string.

  DATA: lv_date         TYPE dats,
        lv_time         TYPE tims,
        lv_timestamp    TYPE timestampl,
        lv_tz_utc       TYPE timezone VALUE 'UTC',
        lv_awsts        TYPE string,
        lv_timechar(32) TYPE c.

  GET TIME STAMP FIELD lv_timestamp.

  CONVERT TIME STAMP lv_timestamp TIME ZONE lv_tz_utc INTO DATE lv_date TIME lv_time.
  MOVE lv_timestamp TO lv_timechar.
  CONDENSE lv_timechar.

  lv_awsdate = lv_date(4) &&
               lv_date+4(2) &&
               lv_date+6(2) &&
               'T' &&
               lv_time(2) &&
               lv_time+2(2) &&
               lv_time+4(2) &&
               'Z'.

  TRANSLATE lv_awsdate TO UPPER CASE.

  DATA: lv_lf TYPE string.
  DATA: lv_secret_key TYPE string.
  DATA: lv_accesskey TYPE string.
  DATA: lv_datepart TYPE string.
  DATA: lv_service TYPE string.
  DATA: lv_method TYPE string.

  lv_lf = cl_abap_char_utilities=>newline.
  lv_accesskey = '<Populate AWS Access Key>'.
  lv_secret_key = '<Populate AWS Secret Access Key>'.
  lv_datepart = lv_awsdate(8).
  lv_service = 'sts'.
  lv_method = 'GET'.


  DATA: lv_canonical_query_params TYPE string.
  DATA: lv_host TYPE string.
  DATA: lv_region TYPE string.
  DATA: lv_canonical_resource_path TYPE string.

  lv_canonical_query_params = 'Action=GetCallerIdentity&Version=2011-06-15'.
  lv_host = 'sts.amazonaws.com'.
  lv_region = '<Populate your AWS Region>'.   "Example: 'us-east-1'
  lv_canonical_resource_path = '/'.

  DATA: lv_canonical_header_names TYPE string.
  DATA: lv_canonical_headers TYPE string.

  lv_canonical_header_names = 'host;x-amz-date'.
  lv_canonical_headers = 'host:' && lv_host && lv_lf && 'x-amz-date:' && lv_awsdate && lv_lf.

  DATA: lv_canonical_request TYPE string.

  CONCATENATE lv_method lv_lf
              lv_canonical_resource_path lv_lf
              lv_canonical_query_params lv_lf
              lv_canonical_headers lv_lf
              lv_canonical_header_names
              INTO lv_canonical_request.

  DATA: lv_canonical_request_hash TYPE string.

  TRY.
      cl_abap_message_digest=>calculate_hash_for_char(
       EXPORTING
         if_algorithm = 'SHA-256'
         if_data = lv_canonical_request
       IMPORTING
         ef_hashstring = lv_canonical_request_hash ).
    CATCH cx_abap_message_digest.
      "Handle error
      RETURN.
  ENDTRY.

  TRANSLATE lv_canonical_request_hash TO LOWER CASE.

  DATA: lv_algorithm TYPE string.

  lv_algorithm = 'AWS4-HMAC-SHA256'.

  DATA: lv_credential_scope TYPE string.

  CONCATENATE lv_datepart '/' lv_region '/' lv_service '/' 'aws4_request' INTO lv_credential_scope.

  DATA: lv_string_to_sign TYPE string.

  CONCATENATE lv_algorithm lv_lf
              lv_awsdate lv_lf
              lv_credential_scope lv_lf
              lv_canonical_request_hash
              INTO lv_string_to_sign.

  DATA: lv_awskey TYPE string.

  CONCATENATE 'AWS4' lv_secret_key INTO lv_awskey.

  DATA: lv_ksecret TYPE xstring.

  TRY.
      lv_ksecret = cl_abap_hmac=>string_to_xstring( lv_awskey ).
    CATCH cx_abap_message_digest .
      "Handle error
      RETURN.
  ENDTRY.

  DATA: lv_kdate  TYPE xstring.
  TRY.
      cl_abap_hmac=>calculate_hmac_for_char(
        EXPORTING
           if_algorithm = 'SHA256'
           if_key = lv_ksecret
           if_data = lv_datepart
        IMPORTING
           ef_hmacxstring = lv_kdate ).
    CATCH cx_abap_message_digest. "
      "Handle error
      RETURN.
  ENDTRY.

  DATA: lv_kregion TYPE xstring.
  TRY.
      cl_abap_hmac=>calculate_hmac_for_char(
        EXPORTING
           if_algorithm = 'SHA256'
           if_key = lv_kdate
           if_data = lv_region
        IMPORTING
             ef_hmacxstring = lv_kregion ).
    CATCH cx_abap_message_digest.
      "Handle error
      RETURN.
  ENDTRY.

  DATA: lv_kservice TYPE xstring.
  TRY.
      cl_abap_hmac=>calculate_hmac_for_char(
         EXPORTING
           if_algorithm = 'SHA256'
           if_key = lv_kregion
           if_data = lv_service
           IMPORTING
             ef_hmacxstring = lv_kservice ).
    CATCH cx_abap_message_digest.
      "Handle error
      RETURN.
  ENDTRY.

  DATA: lv_ksigningkey TYPE xstring.
  TRY.
      cl_abap_hmac=>calculate_hmac_for_char(
         EXPORTING
           if_algorithm = 'SHA256'
           if_key = lv_kservice
           if_data = 'aws4_request'
         IMPORTING
             ef_hmacxstring = lv_ksigningkey ).
    CATCH cx_abap_message_digest.
      "Handle error
      RETURN.
  ENDTRY.

  DATA: lv_stringtosign TYPE string.

  lv_stringtosign = 'AWS4-HMAC-SHA256' && lv_lf &&
                   lv_awsdate && lv_lf &&
                   lv_datepart && '/' &&
                   lv_region && '/' &&
                   lv_service && '/aws4_request' && lv_lf &&
                   lv_canonical_request_hash.

  DATA: lv_ssignature TYPE string.

  TRY.
      cl_abap_hmac=>calculate_hmac_for_char(
         EXPORTING
           if_algorithm = 'SHA256'
           if_key = lv_ksigningkey
           if_data = lv_stringtosign
         IMPORTING
           ef_hmacstring = lv_ssignature ).
    CATCH cx_abap_message_digest.
      "Handle error
      RETURN.
  ENDTRY.

  TRANSLATE lv_ssignature TO LOWER CASE.

  DATA: lv_authorization_header TYPE string.

  lv_authorization_header = 'AWS4-HMAC-SHA256 Credential=' &&
                            lv_accesskey && '/' &&
                            lv_credential_scope &&
                            ', SignedHeaders=' &&
                            lv_canonical_header_names &&
                            ', Signature=' &&
                            lv_ssignature.

  DATA: ls_token_request TYPE t_token_request.

  ls_token_request-url = 'https://sts.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15'.
  ls_token_request-method = 'POST'.

  DATA: ls_header_field TYPE t_header_field.
  ls_header_field-key = 'Authorization'.
  ls_header_field-value = lv_authorization_header.
  APPEND ls_header_field TO ls_token_request-headers.

  CLEAR: ls_header_field.
  ls_header_field-key = 'host'.
  ls_header_field-value = 'sts.amazonaws.com'.
  APPEND ls_header_field TO ls_token_request-headers.

  CLEAR: ls_header_field.
  ls_header_field-key = 'x-amz-date'.
  ls_header_field-value = lv_awsdate.
  APPEND ls_header_field TO ls_token_request-headers.

  CLEAR: ls_header_field.
  ls_header_field-key = 'x-goog-cloud-target-resource'.
  ls_header_field-value = '//iam.googleapis.com/projects/' &&
                               ls_key-project_id &&
                               '/locations/global/workloadIdentityPools/' &&
                               ls_key-auth_param1 &&
                               '/providers/' &&
                               ls_key-auth_param2.
  APPEND ls_header_field TO ls_token_request-headers.

  cv_token = /ui2/cl_json=>serialize(  ls_token_request ).
  cv_token_type = 'urn:ietf:params:aws:token-type:aws4_request'.

ENDMETHOD.
ENDCLASS.

Azure

class ZCL_AUTH_WIF_AZURE definition
  public
  inheriting from /GOOG/CL_AUTH_WIF_BASE
  final
  create public .

public section.
protected section.

  methods GET_EXT_IDP_TOKEN
    redefinition .
private section.
ENDCLASS.



CLASS ZCL_AUTH_WIF_AZURE IMPLEMENTATION.


  METHOD GET_EXT_IDP_TOKEN.
**********************************************************************
*  Copyright 2024 Google LLC                                         *
*                                                                    *
*  Licensed under the Apache License, Version 2.0 (the "License");   *
*  you may not use this file except in compliance with the License.  *
*  You may obtain a copy of the License at                           *
*      https://www.apache.org/licenses/LICENSE-2.0                   *
*  Unless required by applicable law or agreed to in writing,        *
*  software distributed under the License is distributed on an       *
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,      *
*  either express or implied.                                        *
*  See the License for the specific language governing permissions   *
*  and limitations under the License.                                *
**********************************************************************

    TYPES:
      BEGIN OF t_azure_resp,
        access_token TYPE string,
      END OF t_azure_resp.

    DATA: lo_client TYPE REF TO if_http_client.

    DATA: lv_url type string.
    lv_url = 'http://169.254.169.254/metadata/identity/oauth2/token?resource=<APP_ID_URI>&api-version=2018-02-01'.
    "Replace <APP_ID_URI> with the value of Application ID URI of the application that you've configured for workload identity federation.

    cl_http_client=>create_by_url(
       EXPORTING
         url                        = lv_url
       IMPORTING
         client                     = lo_client
       EXCEPTIONS
         argument_not_found         = 1
         plugin_not_active          = 2
         internal_error             = 3
         pse_not_found              = 4
         pse_not_distrib            = 5
         pse_errors                 = 6
         oa2c_set_token_error       = 7
         oa2c_missing_authorization = 8
         oa2c_invalid_config        = 9
         oa2c_invalid_parameters    = 10
         oa2c_invalid_scope         = 11
         oa2c_invalid_grant         = 12
         OTHERS                     = 13 ).

    IF sy-subrc <> 0.
      RETURN.
    ENDIF.

    lo_client->request->set_method( 'GET' ).
    lo_client->request->set_header_field( name = 'Metadata' value = 'true' ).

    lo_client->send(
      EXCEPTIONS
        http_communication_failure = 1
        http_invalid_state         = 2
        http_processing_failed     = 3
        http_invalid_timeout       = 4
        OTHERS                     = 5 ).

    lo_client->propertytype_logon_popup = lo_client->co_disabled.

    lo_client->receive(
      EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state         = 2
      http_processing_failed     = 3 ).

    DATA: lv_json TYPE string.

    lv_json = lo_client->response->get_cdata( ).

    DATA: ls_azure_resp TYPE t_azure_resp.

    /goog/cl_json=>deserialize(
      EXPORTING
        json             = lv_json
      CHANGING
        data             = ls_azure_resp ).
    cv_token = ls_azure_resp-access_token.
    cv_token_type = 'urn:ietf:params:oauth:token-type:jwt'.

  ENDMETHOD.
ENDCLASS.

Configura chiave client

  1. Nella GUI di SAP, esegui il codice transazione /GOOG/SDK_IMG.

    In alternativa, esegui il codice transazione SPRO e fai clic su Riferimento SAP IMG.

  2. Fai clic su SDK ABAP per Google Cloud > Impostazioni di base > Configura chiave client.
  3. Fai clic su Nuove voci.
  4. Inserisci valori nei seguenti campi:

    Campo Descrizione
    Nome chiave Google Cloud Specifica un nome della configurazione della chiave client.
    Nome dell'account di servizio Google Cloud Specifica il nome dell'account di servizio, nel formato dell'indirizzo email, che è stato creato per accedere alle API Google Cloud nel passaggio Crea un account di servizio. Ad esempio: sap-example-svc-acct@example-project-123456.iam.gserviceaccount.com.
    Ambito di Google Cloud Lascia vuoto questo campo.
    Identificatore progetto Google Cloud Specifica l'ID del progetto Google Cloud in cui hai creato il pool di identità per i carichi di lavoro.
    Nome comando Lascia vuoto questo campo.
    Classe di autorizzazione Specifica la classe secondaria, che contiene l'implementazione della classe /GOOG/CL_AUTH_WIF_BASE. Per maggiori informazioni, consulta Implementare il codice ABAP per recuperare i token di sicurezza dall'IdP.
    Memorizzazione nella cache dei token Lascia vuoto questo campo.
    Secondi di aggiornamento dei token Lascia vuoto questo campo.
    Parametro di autorizzazione 1 Specifica l'ID pool di identità per i carichi di lavoro.
    Parametro di autorizzazione 2 Specifica l'ID provider di identità per i carichi di lavoro.
  5. Salva la voce.

Richiedi assistenza

Se hai bisogno di aiuto per risolvere problemi con l'SDK ABAP per Google Cloud: