Proxy authentication can impersonate internal users

Hello!

Versions:

2.13.0

Describe the issue:

What the title says, basically. So for example, if the proxy authenticates a user that happens to have the username admin, that user will have unlimited access to the cluster, regardless of their actual roles. I think external users should NOT be mapped to internal ones.

Can this behaviour be avoided somehow? I’m also using basic auth (see below), maybe I’m just doing something wrong?

Configuration:

config:
  dynamic:
    http:
      anonymous_auth_enabled: false
      xff:
        enabled: true
        # Clients will be limited via network policies
        internalProxies: '.*'
        remoteIpHeader:  'x-forwarded-for'
    authc:
      proxy_auth_domain:
        description: "Authenticate via proxy"
        http_enabled: true
        transport_enabled: true
        order: 1
        http_authenticator:
          type: extended-proxy
          challenge: false
          config:
            user_header: "x-proxy-user"
            roles_header: "x-proxy-roles"
            attr_header_prefix: "x-proxy-ext-"
        authentication_backend:
          type: noop
      basic_internal_auth_domain:
        description: "Authenticate via HTTP Basic against internal users database"
        http_enabled: true
        transport_enabled: true
        order: 2
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          type: intern
    authz:

@lpeter ,

How is your admin mapped to an admin (all_access) role?

Could you share the output of the following:

GET _plugins/_security/api/roles

and

GET _plugins/_security/api/rolesmapping

As well as x-proxy-roles header value passed with your proxy (not internal) admin user.

Best,
mj

I’m using file-based config, but I’ll attach the full API responses at the end.

all_access from roles_mapping.yml:

all_access:
  reserved: true
  backend_roles:
    - "admin"
  users:
    - "admin"

I can set the value of x-proxy-roles to anything and it will work, I’m currently testing with the Modheader browser plugin. You can say the value is the string none. :slight_smile: (Which maps to no roles.)

Roles:

{
  "readall_and_monitor": {
    "reserved": true,
    "hidden": false,
    "description": "Provide the minimum permissions for to readall indices and monitor the cluster",
    "cluster_permissions": [
      "cluster_monitor",
      "cluster_composite_ops_ro"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "read"
        ]
      }
    ],
    "tenant_permissions": [],
    "static": true
  },
  "kibana_user": {
    "reserved": true,
    "hidden": false,
    "description": "Provide the minimum permissions for a kibana user",
    "cluster_permissions": [
      "cluster_composite_ops"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          ".kibana",
          ".kibana-6",
          ".kibana_*",
          ".opensearch_dashboards",
          ".opensearch_dashboards-6",
          ".opensearch_dashboards_*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "read",
          "delete",
          "manage",
          "index"
        ]
      },
      {
        "index_patterns": [
          ".tasks",
          ".management-beats",
          "*:.tasks",
          "*:.management-beats"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "indices_all"
        ]
      }
    ],
    "tenant_permissions": [],
    "static": true
  },
  "own_index": {
    "reserved": true,
    "hidden": false,
    "description": "Allow all for indices named like the current user",
    "cluster_permissions": [
      "cluster_composite_ops"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "${user_name}"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "indices_all"
        ]
      }
    ],
    "tenant_permissions": [],
    "static": true
  },
  "all_access": {
    "reserved": true,
    "hidden": false,
    "description": "Allow full access to all indices and all cluster APIs",
    "cluster_permissions": [
      "*"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "*"
        ]
      }
    ],
    "tenant_permissions": [
      {
        "tenant_patterns": [
          "*"
        ],
        "allowed_actions": [
          "kibana_all_write"
        ]
      }
    ],
    "static": true
  },
  "central_reader": {
    "reserved": true,
    "hidden": false,
    "cluster_permissions": [
      "cluster_composite_ops_ro"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "logs-*",
          "probe-*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "read"
        ]
      }
    ],
    "tenant_permissions": [
      {
        "tenant_patterns": [
          "SIEM",
          "Probe"
        ],
        "allowed_actions": [
          "kibana_all_read"
        ]
      }
    ],
    "static": false
  },
  "readall": {
    "reserved": true,
    "hidden": false,
    "description": "Provide the minimum permissions for to readall indices",
    "cluster_permissions": [
      "cluster_composite_ops_ro"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "read"
        ]
      }
    ],
    "tenant_permissions": [],
    "static": true
  },
  "logstash_actual": {
    "reserved": true,
    "hidden": false,
    "cluster_permissions": [
      "cluster_composite_ops",
      "cluster_monitor",
      "indices:admin/template/get"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "logs-*",
          "probe-*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "create_index",
          "crud"
        ]
      }
    ],
    "tenant_permissions": [],
    "static": false
  },
  "manage_snapshots": {
    "reserved": true,
    "hidden": false,
    "description": "Provide the minimum permissions for managing snapshots",
    "cluster_permissions": [
      "manage_snapshots"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "indices:data/write/index",
          "indices:admin/create"
        ]
      }
    ],
    "tenant_permissions": [],
    "static": true
  },
  "logstash": {
    "reserved": true,
    "hidden": false,
    "description": "Provide the minimum permissions for logstash and beats",
    "cluster_permissions": [
      "cluster_monitor",
      "cluster_composite_ops",
      "indices:admin/template/get",
      "indices:admin/template/put",
      "cluster:admin/ingest/pipeline/put",
      "cluster:admin/ingest/pipeline/get"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "logstash-*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "crud",
          "create_index"
        ]
      },
      {
        "index_patterns": [
          "*beat*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "crud",
          "create_index"
        ]
      }
    ],
    "tenant_permissions": [],
    "static": true
  },
  "institute_reader": {
    "reserved": true,
    "hidden": false,
    "cluster_permissions": [
      "cluster_composite_ops_ro"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "logs-*-public",
          "probe-*-public"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "read"
        ]
      },
      {
        "index_patterns": [
          "logs-*-private",
          "probe-*-private"
        ],
        "dls": """{"term": {"soc.institute": "${attr.proxy.institute}"}}
""",
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "read"
        ]
      }
    ],
    "tenant_permissions": [
      {
        "tenant_patterns": [
          "SIEM",
          "Probe"
        ],
        "allowed_actions": [
          "kibana_all_read"
        ]
      }
    ],
    "static": false
  },
  "kibana_ro_user": {
    "reserved": true,
    "hidden": false,
    "cluster_permissions": [
      "cluster_composite_ops_ro"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          ".kibana*",
          ".opensearch_dashboards*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "read"
        ]
      }
    ],
    "tenant_permissions": [],
    "static": false
  },
  "central_editor": {
    "reserved": true,
    "hidden": false,
    "cluster_permissions": [
      "cluster_composite_ops_ro"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          "logs-*",
          "probe-*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "read"
        ]
      }
    ],
    "tenant_permissions": [
      {
        "tenant_patterns": [
          "SIEM",
          "Probe"
        ],
        "allowed_actions": [
          "kibana_all_write"
        ]
      }
    ],
    "static": false
  },
  "kibana_server": {
    "reserved": true,
    "hidden": false,
    "description": "Provide the minimum permissions for the Kibana server",
    "cluster_permissions": [
      "cluster_monitor",
      "cluster_composite_ops",
      "manage_point_in_time",
      "indices:admin/template*",
      "indices:admin/index_template*",
      "indices:data/read/scroll*"
    ],
    "index_permissions": [
      {
        "index_patterns": [
          ".kibana",
          ".opensearch_dashboards"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "indices_all"
        ]
      },
      {
        "index_patterns": [
          ".kibana-6",
          ".opensearch_dashboards-6"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "indices_all"
        ]
      },
      {
        "index_patterns": [
          ".kibana_*",
          ".opensearch_dashboards_*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "indices_all"
        ]
      },
      {
        "index_patterns": [
          ".tasks"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "indices_all"
        ]
      },
      {
        "index_patterns": [
          ".management-beats*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "indices_all"
        ]
      },
      {
        "index_patterns": [
          "*"
        ],
        "fls": [],
        "masked_fields": [],
        "allowed_actions": [
          "indices:admin/aliases*"
        ]
      }
    ],
    "tenant_permissions": [],
    "static": true
  }
}

Role mappings:

{
  "logstash_actual": {
    "hosts": [],
    "users": [
      "logstash"
    ],
    "reserved": true,
    "hidden": false,
    "backend_roles": [],
    "and_backend_roles": []
  },
  "institute_reader": {
    "hosts": [],
    "users": [],
    "reserved": true,
    "hidden": false,
    "backend_roles": [
      "incident-responder"
    ],
    "and_backend_roles": []
  },
  "kibana_user": {
    "hosts": [],
    "users": [],
    "reserved": true,
    "hidden": false,
    "backend_roles": [
      "l2-analyst"
    ],
    "and_backend_roles": []
  },
  "kibana_ro_user": {
    "hosts": [],
    "users": [],
    "reserved": true,
    "hidden": false,
    "backend_roles": [
      "l1-analyst",
      "coordinator",
      "incident-responder",
      "manager"
    ],
    "and_backend_roles": []
  },
  "all_access": {
    "hosts": [],
    "users": [
      "admin"
    ],
    "reserved": true,
    "hidden": false,
    "backend_roles": [
      "admin"
    ],
    "and_backend_roles": []
  },
  "central_editor": {
    "hosts": [],
    "users": [],
    "reserved": true,
    "hidden": false,
    "backend_roles": [
      "l2-analyst"
    ],
    "and_backend_roles": []
  },
  "kibana_server": {
    "hosts": [],
    "users": [
      "dashboards"
    ],
    "reserved": true,
    "hidden": false,
    "backend_roles": [],
    "and_backend_roles": []
  },
  "central_reader": {
    "hosts": [],
    "users": [],
    "reserved": true,
    "hidden": false,
    "backend_roles": [
      "l1-analyst",
      "coordinator",
      "manager"
    ],
    "and_backend_roles": []
  }
}

Thanks!

Your questions made me thinking. I moved the role assignment from the mappings to the user definition:

admin:
  hash: "..."
  reserved: true
  opendistro_security_roles:
    - all_access

It seems to have fixed the issue, thanks for your input.

I don’t think this difference between the two ways of assignments is well documented (or I just missed it), but it makes some sense.

this is what will grant a user with username admin the all_access to prevent that, use only backend_roles: admin:

all_access:
  reserved: true
  backend_roles:
    - "admin"

this will only give admin if you set x-proxy-roles to admin, but the internal user admin will maintain all_access as it has backend_role admin.

Best,
mj