opensearch-project / security

🔐 Secure your cluster with TLS, numerous authentication backends, data masking, audit logging as well as role-based access control on indices, documents, and fields
https://opensearch.org/docs/latest/security-plugin/index/
Apache License 2.0
180 stars 264 forks source link

[BUG] During proxy-based authentication, remoteIpHeader seems not being verified against internalProxies #4414

Closed simonelbaz closed 3 weeks ago

simonelbaz commented 3 weeks ago

Describe the bug

Hi,

During proxy-based authentication, the IP address contained in remoteIpHeader seems not verified with the list of internalProxies. User can set any IP address, the request is authorized. From my understanding, only the internalProxies list should be accepted.

It verifies the remoteIpHeader presence, the IP address format but not the address value.

Thanks for your feedback

Related component

Clients

To Reproduce

config.yml:

---

_meta:
  type: "config"
  config_version: 2

config:
  dynamic:
    http:
      xff:
        enabled: true
        internalProxies: '192\.168\.176\.40' # regex pattern
        remoteIpHeader:  'x-forwarded-for-dashboards'
    authc:
      proxy_auth_domain:
        description: "Authenticate via proxy"
        http_enabled: true
        transport_enabled: true
        order: 0
        http_authenticator:
          type: proxy
          challenge: false
          config:
            user_header: "x-proxy-user"
            roles_header: "x-proxy-roles"
        authentication_backend:
          type: noop
      kerberos_auth_domain:
        http_enabled: false
        transport_enabled: false
        order: 2
        http_authenticator:
          type: kerberos
          challenge: true
          config:
            # If true a lot of kerberos/security related debugging output will be logged to standard out
            krb_debug: false
            # If true then the realm will be stripped from the user name
            strip_realm_from_principal: true
        authentication_backend:
          type: noop
      basic_internal_auth_domain:
        description: "Authenticate via HTTP Basic against internal users database"
        http_enabled: true
        transport_enabled: true
        order: 1
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          type: intern

The curl command:

curl -vL "https://oc01.mydomain.test:9200" -H "x-proxy-user: xxx" -H "x-proxy-roles: admin" -H "x-forwarded-for-dashboards: 1.1.1.1"

Result of the command:

*   Trying 192.168.176.52:9200...
* Connected to oc01.mydomain.test (192.168.176.52) port 9200 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Request CERT (13):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: O=xxx; CN=oc01.mydomain.test
*  start date: Jun  4 18:43:36 2024 GMT
*  expire date: Jun  3 18:43:36 2034 GMT
*  subjectAltName: host "oc01.mydomain.test" matched cert's "oc01.mydomain.test"
*  issuer: C=FR; ST=France; L=Paris; O=xxx; OU=xxx; CN=xxx; emailAddress=xxx
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: oc01.mydomain.test:9200
> User-Agent: curl/7.74.0
> Accept: */*
> x-proxy-user: ELBAZsi
> x-proxy-roles: admin
> x-forwarded-for-dashboards: 1.1.1.1
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: application/json; charset=UTF-8
< content-length: 571
<
{
  "name" : "oc01.mydomain.test",
  "cluster_name" : "xxx-logs",
  "cluster_uuid" : "1qfaG3b4R02_zOrzDSfgAg",
  "version" : {
    "distribution" : "opensearch",
    "number" : "2.14.0",
    "build_type" : "deb",
    "build_hash" : "aaa555453f4713d652b52436874e11ba258d8f03",
    "build_date" : "2024-05-09T18:50:48.052504416Z",
    "build_snapshot" : false,
    "lucene_version" : "9.10.0",
    "minimum_wire_compatibility_version" : "7.10.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}
* Connection #0 to host oc01.mydomain.test left intact

Expected behavior

Opensearch should not authorize the request if remoteIpHeader value does not match internalProxies list

Additional Details

Plugins Please list all plugins currently enabled.

Screenshots If applicable, add screenshots to help explain your problem.

Host/Environment (please complete the following information):

Additional context Add any other context about the problem here.

peterzhuamazon commented 3 weeks ago

Seems like related to security plugin. Transfer the issue.

Thanks.

derek-ho commented 3 weeks ago

Hi @simonelbaz would you mind sharing your opensearch_dashboards.yml config? Just re-read the issue and it seems related to backend only

cwperks commented 3 weeks ago

@simonelbaz From my understanding of the code, its the IP address of the initiator of the request that's verified against internalProxies not what's passed in the XFF header. See https://github.com/opensearch-project/security/blob/main/src/main/java/org/opensearch/security/http/RemoteIpDetector.java#L119-L144

// originalRemoteAddr need to be in the list of internalProxies

The XFF header comes into the picture in the Backend Registry here. The XFFResolver is responsible for getting the original IP Address of the request. For non-proxied requests that's the IP address of the web request, but for proxied requests it extracts that through the XFF header.

In your example, where are you sending the request from?

simonelbaz commented 3 weeks ago

In your example, where are you sending the request from?

The curl command is ran from the proxy having the following IP address: 192.168.176.40 (the same as internalProxies)

So there is no security issue from the code snippet you mention.

Thanks for your feedback