Skip to main content

8. Rest API

ll interaction with SafeDNS Shield is performed via a REST API, which typically responds on port 8080 from the same IP address that handles user requests. By default, the API only responds to requests from whitelisted IP addresses specified during the server deployment.

Use case

To understand how to work with it, let's consider a specific scenario for initial configuration. This requires sending the following request:

 
  • Type: POST
  • URL: <domain>/init/
  • Data:
{
    "profiles": [
        {
            "profile": {
                "id": 1,
                "page_id": 1
            },
            "cat_ids": [3, 4, 12],
            "app_ids": [1, 12, 93]
        }
    ],
    "blockpages": [
        {
            "id": 1,
            "type": 0
        },
        {
            "id": 2,
            "type": 1
        }
    ],
    "bw_lists": [
       {
            "profile_id": 1,
            "type": "deny",
            "domains": [
                "example1.com",
                "example2.com",
                "example3.com"
            ]
        }
    ],
    "nets": [
        {
            "ip": "100.100.100.100",
            "profile_id": 1,
            "prefix_len": 32
        },
        {
            "ip": "100.110.110.0",
            "profile_id": 1,
            "prefix_len": 24
        },
        {
            "ip": "100.120.0.0",
            "profile_id": 1,
            "prefix_len": 16
        }
    ],
    "nets6": [],
    "napts": []
}

In this init file (in sequence), we configure the SafeDNS On-Prem as follows:

  • Create a single filtering profile with id=1 and a blocking page with id=1 [profiles → profile].
  • In this profile’s settings, specify three blocked categories (in this example: malware, phishing, and botnets) [profiles → cat_ids].
  • Specify three blocked AppBlocker categories (in this example: Slack, AnyDesk, Netflix) [profiles → app_ids].
  • Create two blocking pages of different types (type 0 - displays the blocked domain, type 1 - without showing the blocked domain) [blockpages].
  • Add three domains to the blacklist for the filtering profile with id=1 [bw_lists].
  • Assign IP addresses to the filtering profile with id=1, so requests from these IPs are filtered according to the profile’s policy (in this example: 1 specific IP and 2 subnets with /16 and /24 masks) [nets].
  • [nets6] (same as nets but for IPv6) and [napts] (user identification behind NAT via port) are not filled in this example, but must be sent (empty) to initiate the creation of the initial configuration.

Once this request is sent, and a successful response with status code 204 is received, the initial configuration is complete, and all DNS traffic to this server will be processed according to the specified rules.


Modifying Filtering Categories

Let’s now consider typical use cases for changing or updating filtering settings. For instance, if you want to modify the categories being filtered, you’ll need to update the entire list of categories for the relevant profile. Based on the example above, we had a single profile created with two categories. Suppose we want to add three more categories (Cryptojacking, DGA, and Ransomware). To do this, you need to send the following request:

 
  • Type: PATCH
  • URL: <domain>/profiles/1/
  • Data:
{
  "cat_ids": [3, 4, 12, 66, 70, 71],
  "app_ids": [1, 12, 93],
  "profile": {
    "page_id": 1
  }
}

That is, we specified the profile ID directly in the URL, and in the request body, we provided the updated list of categories (the previous 3 plus the new 3), while keeping the same AppBlocker categories and blockpage as before.


Adding an Address to the Allowlist

Now, let’s consider a scenario where a specific domain from the blocked list needs to be added to the whitelist to bypass category-based blocking. To achieve this, send the following request:

 
  • Type: POST
  • URL: <domain>/profile/1/bw_list
  • Data:
{
  "type": "allow",
  "domain": "example4.com"
}

Thus, for the filtering profile with id=1, we added the domain to the allowlist, giving it priority over category-based blocking.


Adding Domains to the Denylist

Now, suppose we need to block certain domains that are not part of the filtered categories. To do this, we’ll add them to the denylist using a batch method:

 

Type: POST
URL: <domain>/profile/1/bw_list/batch
Data:

{
  "type": "deny",
  "domains": ["example5.com", "example6.com", "example7.com", "example8.com", "example9.com"]
}
Adding a New Network (or IP) for Filtering:
 

Type: POST
URL: <domain>/net/
Data:

{
    "ip": "100.100.100.101",
    "profile_id": 1,
    "prefix_len": 32
}

Thus, we added a new IP to our filtering profile. In this case, we specified a network prefix of /32. If we needed to specify an entire subnet, we could have sent, for example:

{
    "ip": "100.100.101.0",
    "profile_id": 1,
    "prefix_len": 24
}

Removing an IP from the List

Adding a new network (or IP) for filtering:

  • Type: DELETE
  • URL: <domain>/net/1684300901
  • Data: None

This request has no body. The only requirement is to provide the IP address in decimal format within the URL. You must use the exact IP previously submitted (without the mask), e.g., 100.100.100.101 or 100.100.101.0 for the earlier examples. To convert an IP address to decimal format, you can use a converter like [this one].


Modifying the Filtering Profile for a Specific IP
 

Type: PATCH
URL: <domain>/net/1684301056
Data:

{
    "ip": "100.100.101.0",
    "profile_id": 2,
    "prefix_len": 24
}

In this case, we addressed the record for 100.100.101.0 (in decimal format in the URL) and changed the profile from id=1 to id=2. To modify the prefix, for instance, from /24 to /16, we would need to delete this record and add a new one with the appropriate prefix, replacing 100.100.101.0 with 100.100.0.0.


Creating a New Filtering Profile

In the example above, we changed the filtering profile for a specific IP. However, such a profile hasn’t been created yet. Here’s how you can create it:

 
  • Type: POST
  • URL: <domain>/profiles/
  • Data:
    {
        "profile": {
            "id": 2,
            "page_id": 1
        },
        "cat_ids": [3, 4, 12, 66, 70, 71, 13],
        "app_ids": [1, 12, 93]
    }

Thus, we created a filtering profile with id=2, specified the same type of blocking page, and assigned the same categories and AppBlocker settings as in the first profile, adding a new category — "Adult Related".


Complete API Methods Description

User data methods

 
  • Initialize user data database (batch data creation):
    • Type: POST
    • URL: <domain>/init/
    • Data:
      {
          'profiles': [
              {
                  'cat_ids': [<cat_id>,],
                  'app_ids': [<app_id>,],
                  'profile': {
                      'page_id': <page_id>,
                      'id': <id>
                  }
              }, ...
          ],
          'nets': [{'ip': <ipv4>, 'profile_id': <profile_id>, 'prefix_len': <prefix_len4>}, ...],
          'nets6': [{'ip': <ipv6>, 'profile_id': <profile_id>, 'prefix_len': <prefix_len6>}, ...],
          'blockpages': [{'id': <page_id>, 'type': <blockpage_type>}, ...],
          'napts': [
              {
                  'ip': <ipv4>,
                  "profile_id": <profile_id>,
                  "lower_port_bound": <lower_port_bound>,
                  "upper_port_bound": <upper_port_bound>
              },
          ]
      }
    • Result: 204 No body

Profile methods

 
  • Fetch all profiles:
    • Type: GET
    • URL: <domain>/profiles
    • Result: 200 OK
  • Add a profile:
    • Type: POST
    • URL: <domain>/profiles
    • Data:
      {
          "profile": {
              "id": int,
              "page_id": int
          },
          "cat_ids": [int, ],
          "app_ids": [int, ],
      }
    • Result: 201 Created
  • Fetch a specific profile:
    • Type: GET
    • URL: <domain>/profiles/<id>
    • Result: 200 OK
  • Update a specific profile:
    • Type: PATCH
    • URL: <domain>/profiles
    • Data:
      {
        "app_ids": [int, ],
        "cat_ids": [int, ],
        "profile": {
          "plan_id": int,
          "provider_id": int,
          "white_list_only": bool,
          "hide_block_reason": bool,
          "empty_dns_answer": bool,
          "page_id": int,
          "tls": bool
        }
      }
    • Result: 200 OK

Blockpage methods

 
  • Get all blockpages:
    • Type: GET
    • URL: <domain>/blockpage
    • Result: 200 OK
  • Create a blockpage:
    • Type: POST
    • URL: <domain>/blockpage
    • Data:
      {
        "type": int,
        "id": int
      }
    • Result: 200 OK
  • Get a specific blockpage:
    • Type: GET
    • URL: <domain>/blockpage/<page_id>
    • Result: 200 OK
  • Update a blockpage:
    • Type: PATCH
    • URL: <domain>/blockpage/<page_id>
    • Data:
      {
        "type": int
      }
    • Result: 200 OK
  • Delete a blockpage:
    • Type: GET
    • URL: <domain>/blockpage/<page_id>
    • Result: 204 No Content, no body

Lists methods

 
  • Create an allow- or denylist:
    • Type: POST
    • URL: <domain>/profile/<profile_id>/bw_list
    • Data:
      {
        "type": "string", //"allow" or "deny" only
        "domains": ["string", "string", ]
      }
    • Result: 201 Created
  • Create an allow- or denylist with multiple entries:
    • Type: POST
    • URL: <domain>/profile/<profile_id>/bw_list/batch
    • Data:
      {
        "type": "string", //"allow" or "deny" only
        "domains": ["string", "string", ]
      }
    • Result: 201 Created
  • Update a domain in an allow- or denylist:
    • Type: PATCH
    • URL: <domain>/profile/<profile_id>/bw_list/<domain>
    • Data:
      {
        "type": "string", //"allow" or "deny" only
        "domain": "string",
        "profile_id": int
      }
    • Result: 200 OK
  • Delete a domain from an allow- or denylist:
    • Type: DELETE
    • URL: <domain>/profile/<profile_id>/bw_list/<domain>
    • Result: 204 No Content, no body

IPv4 network methods

 
  • Create an IPv4 address entry:
    • Type: POST
    • URL: <domain>/net
    • Data:
      {
        "ip": "string", //IPv4 address in canonical form
        "profile_id": int,
        "prefix_len": int
      }
    • Result: 201 Created
  • Get an IPv4 address entry:
    • Type: GET
    • URL: <domain>/net/<int_ip>
    • Result: 200 OK
  • Update an IPv4 address entry:
    • Type: PATCH
    • URL: <domain>/net/<int_ip>
    • Data:
      {
        "ip": "string", //IPv4 address in canonical form
        "profile_id": int,
        "prefix_len": int
      }
    • Result: 200 OK
  • Delete an IPv4 address:
    • Type: DELETE
    • URL: <domain>/net/<int_ip>
    • Result: 204 No Content, no body

IPv6 network methods

 
  • Create an IPv6 address entry:
    • Type: POST
    • URL: <domain>/net6
    • Data:
      {
        "ip": "string", //IPv6 address in canonical form
        "prefix_len": int,
        "profile_id": int
      }
    • Result: 201 Created
  • Get an IPv6 address entry:
    • Type: GET
    • URL: <domain>/net6/<ipv6>
    • Result: Dictionary with IPv6 and profile_id data
  • Update an IPv6 address entry:
    • Type: PATCH
    • URL: <domain>/net6/<ipv6>
    • Data:
      {
        "ip": "string", //IPv6 address in canonical form
        "prefix_len": int,
        "profile_id": int
      }
    • Result: 200 OK
  • Delete an IPv6 address:
    • Type: DELETE
    • URL: <domain>/net6/<ipv6>
    • Result: 204 No Content, no body

IPv4 NAT methods

 
  • Create an IPv4 NAT entry:
    • Type: POST
    • URL: <domain>/napt/
    • Data:
      {
        "ip": "string", //IPv4 address in canonical form
        "lower_port_bound": int,
        "upper_port_bound": int,
        "profile_id": int
      }
    • Result: 201 Created
  • Create multiple IPv4 NAT entries:
    • Type: POST
    • URL: <domain>/napt/batch
    • Data:
      [
        {
        	"ip": "string", //IPv4 address in canonical form
        	"lower_port_bound": int,
        	"upper_port_bound": int,
        	"profile_id": int
        },
        
      ]
    • Result: 201 Created
  • Get all IPv4 NAT entries by IP:
    • Type: GET
    • URL: <domain>/napt/<int_ip>
    • Result: 200 OK
  • Get a specific IPv4 NAT entry:
    • Type: GET
    • URL: <domain>/napt/<int_ip>/<lower_port_bound>/<upper_port_bound>
    • Result: 200 OK
  • Update an IPv4 NAT entry:
    • Type: PATCH
    • URL: <domain>/napt/<int_ip>/<lower_port_bound>/<upper_port_bound>
    • Data:
      {
        "ip": "string", //IPv4 address in canonical form
        "lower_port_bound": int,
        "upper_port_bound": int,
        "profile_id": int
      }
    • Result: 200 OK
  • Delete an IPv4 NAT entry:
    • Type: DELETE
    • URL: <domain>/napt/<int_ip>/<lower_port_bound>/<upper_port_bound>
    • Result: 204 No Content, no body
  • Delete multiple IPv4 NAT entries:
    • Type: DELETE
    • URL: <domain>/napt/batch
    • Data:
      [
        {
        	"ip": "string", //IPv4 address in canonical form
        	"lower_port_bound": int,
        	"upper_port_bound": int,
        	"profile_id": int
        },
        
      ]
    • Result: 204 No Content, no body

AppBlocker methods

  • Get a list of all available AppBlocker categories:
    • Type: GET
    • URL: <domain>/app_aware/application/
    • Result: 200 OK