# Authorization

Authorization of users and services in Grey Matter is handled by setting up chains of Filters in each Sidecar. These filter chains can be composed into whatever way best suites the needs of the business, but a few reference flows are outlined here.

The contents here focus on HTTP level authorization. Authorization at the TCP level is also supported through the [Envoy TCP RBAC filter](https://github.com/greymatter-io/gm-gitbook-sync/tree/a36d354058c7abbfff58b20908ff27c2ed1c6077/reference/api/fabric-api/filters/network.md#role-based-access-control).

## Key Components

| Filter               | Description                                                                 | Configuration                                                                                                                     |
| -------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `gm.inheaders`       | Set up request headers from certificates to allow authorization actions     | [link](https://greymatter.gitbook.io/grey-matter-documentation/1.7-beta/reference/api/fabric-api/filters/http/gm-inheaders)       |
| `gm.ensurevariables` | Ensure headers/cookies/query strings are present at needed locations.       | [link](https://greymatter.gitbook.io/grey-matter-documentation/1.7-beta/reference/api/fabric-api/filters/http/gm-ensurevariables) |
| `gm.jwtsecurity`     | Establish JWT token for user/service in request for later AuthZ.            | [link](https://greymatter.gitbook.io/grey-matter-documentation/1.7-beta/reference/api/fabric-api/filters/http/gm-jwt-security)    |
| `gm.listauth`        | Apply whitelist or blacklist authentication based on USER\_DN header field. | [link](https://greymatter.gitbook.io/grey-matter-documentation/1.7-beta/reference/api/fabric-api/filters/http/gm-listauth)        |
| `envoy.jwt_authn`    | Authorize requests to route/method based on user/role                       |                                                                                                                                   |
| `envoy.rbac`         | Authorize requests to route/method based on user/role                       | [link](https://greymatter.gitbook.io/grey-matter-documentation/1.7-beta/reference/api/fabric-api/filters/envoy-rbac)              |

## Flows

### Users, Services, Guests

This authorization flow determines access to the service with 3 different policies. A policy for authenticated users coming through the edge, a policy for authenticated services, and a default policy for anything else.

The `service-admin` policy in the RBAC filter below allows any request coming in with the header `USER_DN: CN=quickstart,OU=Engineering,O=Decipher Technology Studios,L=Alexandria,ST=Virginia,C=US`. This policy requires the `gm-inheaders` filter to be set up at the Edge so that incoming requests have the USER\_DN appropriately set from the incoming certificates.

A `services` policy then follows. This is configured to allow any service using certs issued by the SPIRE integration to access the service. The regex matching permits any service as shown, but exact matches or more restrictive regex can be used to tune it to permit/deny specific services. E.g. exact matches on`spiffe://quickstart.greymatter.io/data` or `spiffe://quickstart.greymatter.io/catalog` would permit just those services to perform actions.

The last policy, `product-viewer`, allows any request that does not match either of the two preceding policies to perform only GET requests. Any other method will be denied. This is useful when only certain users/services should be permitted to perform modifications, but anyone should be able to read.

#### Config

```javascript
  "active_http_filters": [
    "envoy.rbac"
  ],
  "http_filters": {
    "envoy_rbac": {
      "rules": {
        "action": 0,
        "policies": {
          "service-admin": {
            "permissions": [
              {
                "any": true
              }
            ],
            "principals": [
              {
                "header": {
                  "name": "user_dn",
                  "exact_match": "CN=quickstart,OU=Engineering,O=Decipher Technology Studios,L=Alexandria,ST=Virginia,C=US"
                }
              }
            ]
          },
          "services": {
            "permissions": [
              {
                "any": true
              }
            ],
            "principals": [
              {
                "authenticated": {
                  "principal_name": {
                    "regex": "spiffe:\/\/quickstart.greymatter.io\/(.+)"
                  }
                }
              }
            ]
          },
          "product-viewer": {
            "permissions": [
              {
                "header": {
                  "name": ":method",
                  "exact_match": "GET"
                }
              }
            ],
            "principals": [
              {
                "any": true
              }
            ]
          }
        }
      }
    },
```

### Simple Access List

This authorization flow is a simple setup to only establish standard headers and then perform whitelisting. The `gm.inheaders` filter sets up the `USER_DN` header from incoming user's PKI certificates, and the `gm.listauth` will allow or deny based on the set whitelist and blacklist.

* [`gm.inheadeers` docs](https://greymatter.gitbook.io/grey-matter-documentation/1.7-beta/reference/api/fabric-api/filters/http/gm-inheaders)
* [`gm.listauth` docs](https://greymatter.gitbook.io/grey-matter-documentation/1.7-beta/reference/api/fabric-api/filters/http/gm-listauth)

> NOTE: in a typical setup, the `gm.inheaders` filter is setup on the originating Sidecar (usually the Edge node or an internal egress listener) rather than the destination. This allows common origination points to setup the needed header fields, and then each destination to setup its own allow/deny rules. Both are shown in the same config below for reference, and because this is also permissible if desired.

#### Simple Access List Configuration

```javascript
  "active_http_filters": [
    "gm.inheaders",
    "gm.listauth"
  ],
  "http_filters": {
    "gm_listauth": {
      "whitelist": "CN=quickstart,OU=Engineering,O=Decipher Technology Studios,L=Alexandria,ST=Virginia,C=US",
      "blacklist": "CN=catalog,OU=Engineering,O=Decipher Technology Studios,L=Alexandria,ST=Virginia,C=US"
    }
  }
```

### inheaders + jwt-security + AuthN + rbac

This flow composes a number of filters to perform RBAC on user attributes in JWT tokens from Grey Matter's JWT Security Service. The filter chain here performs the following in sequence:

1. `gm.inheaders`: Establish USER\_DN header from incoming certs
2. `gm.jwtsecurity`: Exchange USER\_DN header for a JWT token with complete user claims.
3. `envoy.jwt_authn`: Verifies the JWT token and populates internal metadata with the claims.
4. `envoy.rbac`: Accesses the `envoy.jwt_authn` metadata from the JWT token to verify that the user's email address contains the greymatter.io domain and allow access.

#### Configuration

```javascript
  "active_http_filters": [
    "gm.inheaders",
    "gm.jwtsecurity",
    "envoy.jwt_authn"
    "envoy.rbac"
  ],
  "http_filters": {
    "gm.inheaders": {},
    "gm.jwtsecurity": {
      "apiKey": "dm9sY2Fuby1lcGlkZW1pYy10d2VsZLXRhbWFsZQ==",
      "endpoint": "https://localhost:10909/jwt",
      "jwtHeaderName": "userpolicy",
      "useTls": true,
      "caPath": "/etc/proxy/tls/sidecar/ca.crt",
      "certPath": "/etc/proxy/tls/sidecar/server.crt",
      "keyPath": "/etc/proxy/tls/sidecar/server.key",
      "insecureSkipVerify": true
    },
    "envoy_jwt_authn": {
      "providers": {
        "greymatter": {
          "issuer": "greymatter.io",
          "from_headers": [
            {
              "name": "userpolicy"
            }
          ],
          "payload_in_metadata": "claims",
          "local_jwks": {
            "inline_string": "{'keys':[{'crv': 'P-521','kid': '1','kty': 'EC','x': 'DxZd8I_IS4Am6jfjKNaqsAxWfxhweN6I081jLgq6hTL-qlReYXd62kH3v-chAWtqWKILz1CM-reeh5hlZ3qsDf4','y': 'AWs6sDyue4kBEM90K7IVweZ674QIyn4hEqPvsxZpGVAKoE466MdhCVI7RxceNGGxXtVa3zevbnP1Grju-DymFkVl'}]}"
          }
        }
      },
      "rules": [
        {
          "match": {
            "prefix": "/"
          },
          "requires": {
            "provider_name": "greymatter"
          }
        }
      ]
    },
    "envoy_rbac": {
      "rules": {
        "action": 0,
        "policies": {
          "greymatter-developer": {
            "permissions": [
              {
                "any": true
              }
            ],
            "principals": [
              {
                "metadata": {
                  "filter": "envoy.filters.http.jwt_authn",
                  "path": [
                    {
                      "key": "claims"
                    },
                    {
                      "key": "email"
                    }
                  ],
                  "value": {
                    "string_match": {
                      "suffix": "greymatter.io"
                    }
                  }
                }
              }
            ]
          },
          "admin_dn": {
            "permissions": [
              {
                "any": true
              }
            ],
            "principals": [
              {
                "header": {
                  "name": "user_dn",
                  "exact_match": "CN=quickstart,OU=Engineering,O=Decipher Technology Studios,L=Alexandria,ST=Virginia,C=US"
                }
              }
            ]
          },
          "anonymous": {
            "permissions": [
              {
                "header": {
                  "name": ":method",
                  "exact_match": "GET"
                }
              }
            ],
            "principals": [
              {
                "any": true
              }
            ]
          }
        }
      }
    }
  }
```

### Allow/Deny Requests Based on IP Address

Another way to create an access/block list is by using [RBAC filter](https://www.envoyproxy.io/docs/envoy/v1.15.0/configuration/listeners/network_filters/rbac_filter) to allow/deny requests based on remote IP addresses.

#### Network filter vs. HTTP filter

You will notice that there are [RBAC Network filter](https://www.envoyproxy.io/docs/envoy/v1.15.0/configuration/listeners/network_filters/rbac_filter) and [RBAC HTTP filter](https://www.envoyproxy.io/docs/envoy/v1.15.0/configuration/http/http_filters/rbac_filter) in Envoy. We can use either filter for checking the remote IP address and determining a policy. We chose a network filter for this guide as we do not need to do any sophisticated analysis of the traffic and it can handle larger load. If you need information in HTTP headers, for example, you want to use a HTTP filter.

#### Role Based Access Control (RBAC) Filter Configuration

> The RBAC filter is used to authorize actions (permissions) by identified downstream clients (principals).

In other words, we want to set up our RBAC filter which will grant/deny access permission based on the IP address of the principals. The simple set up looks like the following:

```javascript
  "active_network_filters": [
    "envoy.rbac"
  ],
  "network_filters": {
    "envoy_rbac": {
      "stat_prefix": "api-safe-list",
      "rules": {
        "action": "ALLOW",
        "policies": {
          "allow-trusted-ips": {
            "permissions": [
              {
                "any": true
              }
            ],
            "principals": [
              {
                "remote_ip": {
                  "address_prefix": "192.0.0.1",
                  "prefix_len": 32
                }
              }
            ]
          }
        }
      }
    }
  }
```

Note that there is a configuration for `envoy_rbac` filter, and that filter is set to active by being listed under `active_network_filters`. In above case, the listener will reject any access unless it is coming from a remote IP address of 192.0.0.1 ([CIDR](https://www.envoyproxy.io/docs/envoy/v1.15.0/api-v2/api/v2/core/address.proto#core-cidrrange): 192.0.0.1/32).

RBAC supports enforcement and shadow mode. If you want to do a final check of the configuration and expected behavior in production, you can start with shadow mode which will not affect the actual traffic. By replacing the above `"rules"` with `"shadow_rules"`, it will only emits logs and stats without enforcing the rule.

If you want to deny any access from a given IP, you can set the action to `"DENY"` instead of `"ALLOW"`. If you have a list of IP addresses you want to block the access, you can list them all in `"principals"`.

As you can see, it is straight forward to allow/deny access based on the IP address.
