Cyral
Get Started Sign In

Protect REST API endpoints with Cyral

Cyral monitors and protects REST API endpoints based on access policies you create.


Configure interception of REST API calls

To protect a REST endpoint, the main steps are:

  1. Install a Cyral sidecar to intercept and monitor REST API calls from your users and applications. REST endpoints are protected by the same sidecars as other repositories; you can skip this step if you’d like to use an existing sidecar to intercept traffic for your REST API service.

  2. Track your REST API server as a repository by telling Cyral how to connect to it.

  3. Assign your REST API server (recognized as a repository because you tracked it in Step 1) to the sidecar so that the sidecar can intercept API requests.

  4. In Cyral, create a data map and policy that specify who can take which actions on which API endpoint URIs. See below for syntax.

  5. Set all API clients to connect to your protected REST API endpoints through the Cyral sidecar

Track your REST API server and assign it to a sidecar

The first step in adding Cyral to protect a REST API server is to create an entry for it in Cyral's inventory or repositories.

  1. In the Cyral management console, navigate to the Data Repositories tab and click the plus button.

  2. In the pop-up dialog, specify the details needed to connect to the API server:

    1. set type to REST Service.

    2. hostname is the address or hostname of the API server (without the http(s) prefix).

    3. port is the port on which the API server is listening for requests.

    4. name is a name you choose to represent this API server in Cyral. It's the name you'll use in your data map, and it will appear in logs and other controls.

  3. Click Track.

  4. If your Rest API server only allows incoming traffic using TLS (HTTPS), please configure it as follows:

    1. Select the Data Repository created after you clicked on the Track button.

    2. Go to the Advanced tab and select the Enabled option in the Repository TLS section.

    3. Click Save at the bottom of the page.

Next, make sure a Cyral sidecar is installed in a location where it can receive all API requests bound for the API server you're tracking.

  • In the Sidecars tab, select the sidecar to which you'd like to assign the API server and click its name to edit it.

  • In the Data Repositories tab, click the plus sign.

  • In the Assign a Repository window, choose the API server you tracked earlier. Set the listening port to one of the ports exposed for HTTP traffic — by default this includes ports 80, 81, 443, and 444.

  • Click Track.

Identity Attribution

Attributing an identity to each incoming query or REST API call is known as "identity attribution." When monitoring a REST API service, Cyral provides JWT-based identity attribution. This is a built-in feature and comes configured by default. 


The default JWT-based Identity attribution module in sidecar does the following:

  1. Fetches the bearer token (JWT) from the Authorization header of the incoming REST API call

  2. Decodes the JWT

  3. Collects the emailpreferred_username, and realm_access.roles fields from the JWT and passes them to the Cyral data activity log, in the identity block. See the section, "REST API call-related fields in the data activity log" for details.


You can also use these values in your policy evaluation. 


Cyral reads the field azp ("authorized party") from the claims and uses that value as the service name


Note that the default identity attribution module does not validate the signature of the JWT. Any changes to the default behavior can be made possible by writing a Rego-based custom identity plugin. Please contact the Cyral support team for more information on this feature.


Identity Plugin Selection

When you create a repository entry in Cyral to track your REST API service, it is configured to use Cyral's default identity attribution plugin. In most cases, no additional configuration is required. If you wish to use a custom identity plugin, open the repo in Cyral's control plane UI, go to the Advanced tab, and associate the desired plugin with the repo.



Data maps and policies to protect REST API endpoints

You protect a REST endpoint in Cyral in the same way that you protect any data repository: 

  • Create a data map listing the REST API endpoints to be protected, applying a label or labels to each endpoint. Policies refer to endpoints using only labels. Note that many endpoints can be grouped under one label, so you can write an easy-to-read policy that treats your endpoints consistently, even when your API has many possible endpoint URIs. 

  • Write your policy as you would for a data repository, specifying the users and groups who can access each label, and which actions they can perform. 

See the sections below to learn how to create data maps and policies that protect REST API endpoints. (For an introduction to the structure of data maps and policies, see the policy guide.)

Data maps for REST API endpoints

Data map structure

Data maps follow the structure shown below. Within a label declaration you can add as many repos and services as you like. Each service entry represents an API server you'll include in this label. (Each repo entry represents one or more locations in a database or data collection; the repo block is not used for API endpoints but is shown here for completeness.)

{LABEL}:

  • repo: {REPOSITORY_NAME}

     attributes:

        - {ATTRIBUTE_LOCATION_1}

        - {ATTRIBUTE_LOCATION_2}

        ...

     ...

  • service: {SERVICE_NAME}

     endpoints:

        - uri: {REST_ENDPOINT_1}

          method: {OPERATION_1}

          deletedCount: {RESPONSE_FIELD}

          readCount: {RESPONSE_FIELD}

          updatedCount: {RESPONSE_FIELD}

        - uri: {REST_ENDPOINT_2}

          method: {OPERATION_1}

          updatedCount: {RESPONSE_FIELD}

        - uri: {REST_ENDPOINT_3}

          method: {OPERATION_1}

          deletedCount: {RESPONSE_FIELD}

         ...

    ...


The elements in the data map are:

  • the label is the name you'll use in your policy to refer to all the REST endpoints and repository locations you include in the repo and service blocks

  • the service block lists the REST endpoints in this label. Each entry in the list consists of:

    • the name of a REST API server whose endpoints you'll protect, as specified in the Data Repositories list in Cyral

    • endpoints is a list of all this API server's REST endpoints that you want to include in this label. Each endpoint consists of:

      • the uri of the API endpoint

      • the method (for example, GET)

      • a counter definition (readCountcreatedCountupdatedCount, and/or deletedCount)

See the next section for details.

Note: Your data map can also include a repo block as a peer to the service block. The repo block lists the data repository locations covered by this label. For details, see Data Map in the Cyral documentation.


The endpoints specification

Specifying API endpoint URIs in data maps

uri field

The uri field specifies the endpoint. For variable portions of the URI (for instance, in /v1/patients/1qa32109, the value "1qa32109" is the patient id, which varies from call to call. You use wildcard elements to represent the variable parts of URIs. There are three ways to represent a wildcard:

  • a single asterisk (*) represents one variable field

  • a double asterisk (**) represents one or more variable fields. The double asterisk can only be placed at the end of a URI, and it means, "all URIs that fall under this URI"

  • a parameter name in curly braces (for example, {patient_id}). This acts the same as a single asterisk, but has the advantage of forcing Cyral to emit the {parameter-name} into the logs. For example, if you want to create a label that includes all operations on the "patients" endpoint, regardless of which "patient_id" is involved, then you would write:

- uri: /v1/patients/{patient_id}/items**

  method: GET


Method field

The method specifies the HTTP method that this label applies to. It can be GET, POST, PUT, PATCH or DELETE. 

Counter definition

The counter definition (readCountcreatedCountupdatedCount, and/or deletedCount) specifies how Cyral will count the number of records accessed (cardinality) when a user calls this URI. 

Using counters

These counts appear in the activity log, and, more importantly, these counts are available for evaluation in your policy. This allows you to write policies that depend on the number of elements a REST API call creates, reads, updates, or deletes. The syntax for this follows the pattern:

<counter name>: <counter definition>


where:

  • counter name is one of readCountupdatedCount, or deletedCount

  • counter definition is 

    • the name of a request field whose elements are to be counted; 

    • the name of a response field whose elements are to be counted; or 

    • constant. For some REST API calls, the number to be counted is a constant, so you would put that constant in the "count" field.

Computing cardinality or record count

The Cyral sidecar computes the cardinality or record count based on the counter definition in your data map. The following rules apply to this calculation:

  1. If the counter name and definition are omitted in the datamap then the cardinality is reported as 1.

  2. If the counter definition is a constant then the specified constant value will be the cardinality.

  3. The counter definition can reference fields in the API request payload or the response payload

    1. if the element is present in the response:

response.field1.field2. ... . fieldN 

  1. if the element is present in the request:

request.field1.field2. ... . fieldN

  1. The literal value of the specified field will be interpreted as record count if the counter definition is prefixed with an exclamatory sign !. For example, consider a response payload of a DELETE API with a field called recordsDeleted, indicating the number of records deleted.

{

    "Id": "123e4567-e89b-12d3-a456-426614174000",

    "recordsDeleted": 2

}

If the counter definition in the datamap is specified as 
deletedCount: !response.recordsDeleted, then the sidecar would report a cardinality of 2

  1. When the request or response fields are of type lists, the fields specified in the counter definition can include square brackets []. For example, consider a response payload

{

        "Id": "123e4567-e89b-12d3-a456-426614174000",

        "customers": [{

"name": "John Smith",

            "creditScore": 670

         },{

"name": "Frank Hardy"

        }]

}

Counter definition response.customers[].creditScore will result in a cardinality of 1. 

Counter definition response.customers[] will result in a cardinality of 2. 

Counter definition response.customers will result in a cardinality of 1. 

  1. Consider one more example below where the API response is a list (as opposed to a dictionary in the previous example)
    [

        {"name": "John Smith", "creditScore": 670},

        [

            {"name": "Frank Hardy", "creditScore": 710},

            {"name": "Nancy Drew","creditScore": 700} 

        ],

[

            {"name": "Frank Hardy"},

            {"name": "Nancy Drew","creditScore": 700} 

        ]

]


Counter definitions and cardinality values

Below are the different possible counter definitions one could specify in the datamaps and the values of the cardinality the sidecar computes in each case:

Counter Definition

Cardinality

None specified

1

Constant value

Specified Constant

response 

1

response[]

3

response[][]

4

response[][].creditScore

3

 

Data map example for protecting a REST API endpoint

In the below example, we assign a label, "PATIENT" to REST API actions on three endpoints on a REST API server called, "patient_manager". (This assumes we've tracked the API server in Cyral under the repository name, "patient_manager".) 

The label PATIENT is assigned to:

  • GET calls to the /v1/patients API endpoint, in which case Cyral returns a count of the records retrieved;

  • DELETE calls to the /v1/patients API endpoint, in which case Cyral returns a count of the records deleted;

  • PUT or POST calls to the /v1/patients URI and all URIs that begin with the pattern, "/v1/patients", in which case Cyral returns a count of the records created or updated;

PATIENT:

  • service: patient_manager

     endpoints:

       - uri: /v1/patients 

         method: GET

         readCount: response.patients

       - uri: /v1/patients/{patient_id}

         method: DELETE

         deletedCount: response.patients

       - uri: /v1/patients**

         method: PUT,POST

         updatedCount: response.patients


Tips for specifying API endpoints

Wildcards: In nearly all cases, you should use a double asterisk (**) when you want to include a wildcard in a URI. For example, to create a label that includes all possible REST calls whose URI starts with /v1/store, you would specify the URI in your data map as: 

- uri: /v1/store**

  method: GET


Policies for REST API endpoints

Policies work as they do for data repositories. Here's the policy syntax for protecting REST API endpoints:

data:

  - {LABEL}

rules:

  - identities: 

      users: [{USER_1}, {USER_2}, ...]  

      services: [{SERVICE_1}, {SERVICE_2}, ...] 

      groups: [{GROUP_1}, {GROUP_2}, ...] 

    reads:

      - data: [{LABEL_1}, {LABEL_2}, ...]

        rows: {ROW_LIMIT}

    updates:

      - data: [{LABEL_1}, {LABEL_2}, ...]

        rows: {ROW_LIMIT}

        severity: {SEVERITY_LEVEL}

    deletes:

      - data: [{LABEL_1}, {LABEL_2}, ...]

        rows: {ROW_LIMIT}

        severity: {SEVERITY_LEVEL}



For example:

data:

  - STORE_OPS

rules:

  - identities: 

      groups: [retail_analysts]

    reads:

      - data: [{LABEL_1}, {LABEL_2}, ...]

        rows: {ROW_LIMIT}

    updates:

      - data: [{LABEL_1}, {LABEL_2}, ...]

        rows: {ROW_LIMIT}

        severity: {SEVERITY_LEVEL}

    deletes:

      - data: [{LABEL_1}, {LABEL_2}, ...]

        rows: {ROW_LIMIT}

        severity: {SEVERITY_LEVEL}


 


Turn on logging for a REST API endpoint

To turn on logging for a REST endpoint:

  1. Open the REST API server's entry in the Data Repositories section of the Cyral control plane UI.

  2. Click the Log Settings tab. Under Volume Settings, select Log Everything.

  3. Click Save.

Connect to a protected REST API endpoint

Below, we show how to connect to a Cyral-protected REST API endpoint both with and without TLS. In all the examples shown here, we use an example sidecar address of "sidecar.example.com".

Connecting to a sidecar API endpoint using HTTP (without TLS)

Bind the REST repo to the sidecar to the HTTP ports on the sidecar. These ports are defined in the sidecar deployment template. 

To connect in this way, first go to the advanced settings for your repo in Cyral UI, and ensure that “Client TLS” is disabled.

curl -H "Content-Type: application/json"  http://sidecar.example.com/v1/customers


Connecting to a sidecar API endpoint using TLS

There are two ways to connect using TLS.

Using a certificate in the sidecar

By default the sidecar packs a built-in, self-signed certificate, and the TLS connection will be directly established with the sidecar. The load balancer, if present, should be configured without a certificate. 

To connect in this way, first go to the advanced settings for your repo in Cyral UI, and ensure that “Client TLS” is enabled.

curl -k -H "Content-Type: application/json" https://sidecar.example.com/v1/customers


curl --insecure -H "Content-Type: application/json" https://sidecar.example.com/v1/customers


curl --cacert ./cyral_ca_bundle.pem -k -H "Content-Type: application/json" https://sidecar.example.com/v1/customers


Using a load balancer certificate

If the sidecar is deployed with a load balancer and a TLS certificate, then the client can establish a TLS connection with the load balancer. In this configuration, TLS terminates at the load balancer itself, and the sidecar is not involved in TLS handshake.  

To connect in this way, first go to the advanced settings for your repo in Cyral UI, and ensure that “Client TLS” is disabled.

curl -H "Content-Type: application/json" https://sidecar.example.com/v1/customers


REST API activity in the Cyral data activity logs

In the Cyral activity log, activity on a REST endpoint generates log entries similar to those for queries on a database. 

REST API call-related fields in the data activity log

The following fields that are specific to log entries for API calls:

  • repo:type will show "rest" for all REST API calls

  • In the identity block, shows identity information collected from the JWT used to authenticate:

    • email is shown as "endUserEmail"

    • preferred_username is shown as "endUser" and as "repoUser"

    • realm_access.roles is shown as "group". By default, Cyral uses the first entry from the realm_access.roles array.

  • In the request block,

    • endpoint shows the REST API URI of the request. For example:

      https://hhiu.cyral.com:8000/v1/patients/1vOya32109


    • matchedRoute shows the URI from your data map that matched the user's API request. For example: /v1/patients/{patient_id}

    • method shows which HTTP operation the user requested. For example: GET

    • fieldsAccessed lists the labels (as established in your data map) that the request operated on, and what type of operation the API call performed on each. For example: "label": "PATIENT", "accessType": "read"

    • parameters:uri lists the arguments present in the REST API request. For example: 
      "patient_id": "1vOya32109"

Example log entry for a GET request 

For example, let's consider the following REST API request, which is a GET call:

curl -H "Authorization: Bearer $token" https://hhiu.cyral.com:8000/v1/patients/1vOya32109



Datamap

We assume a data map exists to protect the URI, /v1/patients:

PATIENT:

  • service: patient_manager

endpoints:

       - uri: /v1/patients/{patient_id}

         method: GET


Sample Log:

{

  "activityId": "127.0.0.1:53890:1627946100378537864:1",

  "activityTime": "2021-08-02 23:15:00.380682539 +0000 UTC",

  "activityTimeNanos": 1627946100380682500,

  "activityTypes": [

    "query"

  ],

  "identity": {

    "endUser": "nancy.drew@hhiu.us",

    "endUserEmail": "nancy.drew@hhiu.us",

    "group": "_Access Support",

    "repoUser": "nancy.drew@hhiu.us"

  },

  "repo": {

    "id": "1vY0Bt1JsRhb5LBQLiqUdYZdkSm",

    "name": "patient_manager",

    "type": "rest",

    "host": "hhiu.cyral.com",

    "port": 8000

  },

  "client": {

    "connectionId": "127.0.0.1:53890:1627946100378537864",

    "connectionTime": "2021-08-02 23:15:00.378537864 +0000 UTC",

    "connectionTimeNanos": 1627946100378538000,

    "host": "127.0.0.1",

    "port": 53890,

    "applicationName": "management-console"

  },

  "sidecar": {

    "id": "1vOy4ZQ6ZDmIRhnfzmA8TSoycG0",

    "name": "sidecar-east",

    "autoScalingGroupInstance": "10.1.1.207"

  },

  "request": {

    "endpoint": "https://hhiu.cyral.com:8000/v1/patients/1vOyGa3210",

    "matchedRoute": "/v1/patients/{patient_id}", 

    "method": "GET",

    "isSensitive": yes,

    "fieldsAccessed": [ 

      { 

        "label": "PATIENT",

        "accessType": "read"

      }

    ],

    "parameters": {

      "uri": {

        "patient_id": "1vOyGa3210"

      }

    }

  },

  "response": {

    "message": "OK",

    "isError": false,

    "records": 1,

    "bytes": 1460,

    "executionTime": "0.941074599s",

    "executionTimeNanos": 941074599

  },

  "policyViolated": false

}


Did you find it helpful? Yes No

Send feedback
Sorry we couldn't be helpful. Help us improve this article with your feedback.