zowe / api-layer

The API Mediation Layer provides a single point of access for mainframe service REST APIs.
Eclipse Public License 2.0
56 stars 63 forks source link

X.509 client certificate authentication with SCGW header forwarding enabled #3321

Open pablocarle opened 8 months ago

pablocarle commented 8 months ago

Describe the bug

Gateway configuration used:

components:

  # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  gateway:
    enabled: true
    port: 10010
    debug: true

    spring:
      profiles:
        active: securityFilterchainDebug

    apiml:
      security:
        useInternalMapper: false
        auth:
          provider: zosmf
          zosmf:
            jwtAutoconfiguration: auto
            serviceId: ibmzosmf
        authorization:
          endpoint:
            enabled: false
          provider: ""
        x509:
          enabled: true
          certificatesUrl: https://host:<scgw port>gateway/certificates
          acceptForwardedCert: true
        personalAccessToken:
          enabled: true
    server:
      internal:
      # gateway supports internal connector
        enabled: false
        port: 10010
        ssl:
          enabled: false
          # internal connector can use different certificate
          # certificate:
          #   keystore:
          #     alias: ""

With this configuration, a request with x.509 authentication credentials go through the CategorizeCertsFilter and then a call to CertificatesValidator.isTrusted() happens. This method then verifies if the client cert is trusted (by scgw server cert chain).

    /**
     * Compare given certificates with a list of trusted certs.
     *
     * @param certs Certificates to compare with known trusted ones
     * @return true if all given certificates are known false otherwise
     */
    public boolean isTrusted(X509Certificate[] certs) {
        List<Certificate> trustedCerts = trustedCertificatesProvider.getTrustedCerts(proxyCertificatesEndpoint);
        for (X509Certificate cert : certs) {
            if (!trustedCerts.contains(cert)) {
                apimlLog.log("org.zowe.apiml.security.common.verify.untrustedCert");
                log.debug("Untrusted certificate is {}", cert);
                return false;
            }
        }
        log.debug("All certificates are trusted.");
        return true;
    }

In our example we have client cert: root -> intermediate 1 -> clientauth

and scgw has root -> intermediate 2 -> server published by certificatesUrl: https://host:<scgw port>gateway/certificates.

With these settings it seems the validation cannot succeed as neither the client cert nor the intermediate are part of the supplied certificates from SCGW.

Expected behavior X.509 client cert authentication to GW should work the same with and without the SCGW header forwarding

Logs

(o.s.s.w.FilterChainProxy) Securing POST /gateway/api/v1/auth/access-token/generate
(o.s.s.w.c.SecurityContextPersistenceFilter) Set SecurityContextHolder to empty SecurityContext
(o.z.a.s.c.v.CertificateValidator) ZWEAT505E Incoming request certificate is not one of the trusted certificates provided by the central Gateway.
(o.z.a.s.c.v.CertificateValidator) Untrusted certificate is .

Details

pablocarle commented 1 week ago

Hi, if I remember right, the scenario was trying to generate a Personal Access Token by calling a Zuul Gateway that had Multi tenancy enabled and was importing the trusted certs from a central gateway (Spring Cloud Gateway) The chains were not matching between the central and domain and the client cert matched the one in Zuul Gateway.