# Qcontrol (Beta)

## Introduction

Rulekit is the expression engine that powers Qcontrol's rule-based traffic management system. It allows you to define precise conditions for controlling network traffic using a simple, intuitive syntax. This guide will help you understand how to effectively use Rulekit expressions within Qcontrol to implement security policies and traffic controls.

## Understanding the Qcontrol Context

Qcontrol uses Rulekit to evaluate and enforce rules against network traffic, primarily focusing on:

* **Outbound connections**: Monitoring and controlling where your applications can connect
* **Process attribution**: Understanding which processes initiated connections
* **Security enforcement**: Allowing or denying traffic based on precise conditions

Rules are evaluated in order, with allow rules typically placed before deny rules to ensure critical services remain accessible.

## Expression Syntax

RuleKit expressions follow a straightforward pattern:

```
<field> <operator> <value>
```

{% hint style="info" %}
**Important**: RuleKit uses **dot notation** for accessing nested fields and map values, not square bracket notation:

* ✅ Correct: `src.pod.labels.app == "frontend"`
* ❌ Incorrect: `src.pod.labels["app"] == "frontend"`
  {% endhint %}

Multiple conditions can be combined using logical operators:

```
<field1> <operator1> <value1> and <field2> <operator2> <value2>
```

### Logical Operators

<table><thead><tr><th width="127.68023681640625">Operator</th><th width="104.2196044921875">Aliases</th><th width="148.3924560546875">Description</th><th>Example</th></tr></thead><tbody><tr><td>and</td><td>&#x26;&#x26;</td><td>Logical AND</td><td><code>dst.port == 443 and tls.version >= 1.3</code></td></tr><tr><td>or</td><td>||</td><td>Logical OR</td><td><code>dst.port == 80 or dst.port == 443</code></td></tr><tr><td>not</td><td>!</td><td>Logical NOT</td><td><code>not src.pod.namespace == "kube-system"</code></td></tr></tbody></table>

### Comparison Operators

<table><thead><tr><th width="106.488525390625">Operator</th><th width="101.33026123046875">Aliases</th><th width="246.0489501953125">Description</th><th>Example</th></tr></thead><tbody><tr><td><code>==</code></td><td>eq</td><td>Equal to</td><td><code>dst.port == 443</code></td></tr><tr><td><code>!=</code></td><td>ne</td><td>Not equal to</td><td><code>src.pod.namespace != "kube-system"</code></td></tr><tr><td><code>></code></td><td>gt</td><td>Greater than</td><td><code>dst.port > 1024</code></td></tr><tr><td><code>>=</code></td><td>ge</td><td>Greater than or equal to</td><td><code>tls.version >= 1.3</code></td></tr><tr><td><code>&#x3C;</code></td><td>lt</td><td>Less than</td><td><code>dst.port &#x3C; 1024</code></td></tr><tr><td><code>&#x3C;=</code></td><td>le</td><td>Less than or equal to</td><td><code>tls.version &#x3C;= 1.2</code></td></tr><tr><td><code>=~</code></td><td>matches</td><td>Matches regex pattern</td><td><code>dst.domain matches /.example.com$/</code></td></tr><tr><td><code>contains</code></td><td></td><td>Contains substring or element</td><td><code>dst.domain contains "example"</code></td></tr><tr><td><code>in</code></td><td></td><td>Is contained in array</td><td><code>dst.port in [80, 443, 8080]</code></td></tr></tbody></table>

### Value Types

**bool**

A boolean. For example: `true`, `false`.

```perl
tls.enabled || allow_insecure == true
```

**number**

An integer or floating-point number. For example: `8080`, `1.2`.

```perl
tls.version >= 1.2
```

**string**

A double-quoted string. For example: `"tcp"`, `"{\"content\": \"example\"}"`.

```perl
dst.domain == "example.com"
```

**regex pattern**

A Golang/RE2 regular expression surrounded by forwarded-slashes `/` or straight pipes `|`. Can only be compared against string values. For example: `\.domain\.com$`.

```perl
dst.domain matches /\.domain\.com$/
```

```perl
src.process.path matches |^/usr/local/|
```

**IP**

An IPv4, IPv6, or an IPv6 dual address. For example:

* `192.168.1.1`
* `2001:db8:3333:4444:cccc:dddd:eeee:ffff`

```perl
dst.ip != 10.0.0.1
```

**CIDR**

An IPv4 or IPv6 CIDR block. For example:

* `192.168.0.0/16`
* `2001:db8:3333:4444:cccc:dddd:eeee:0000/48`

```sql
dst.ip != 10.0.0.0/8
```

### Arrays

Arrays may be expressed using square bracket notation. Arrays may contain mixed types and can be used to make rules more readable.

```sql
dst.port in [80, 8080, 53]
-- instead of
dst.port == 80 or dst.port == 8080 or dst.port == 53
```

```sql
dst.ip != [10.0.0.0/8, 192.168.0.0/16, 1.2.3.4]
-- instead of
dst.ip != 10.0.0.0/8 and dst.ip != 192.168.0.0/16 and dst.ip != 1.2.3.4
```

```sql
dst.domain in ["example.com", /\.example\.com$/]
-- instead of
dst.domain == "example.com" or dst.domain matches /\.example\.com$/
```

### Comments and Whitespace

Expressions may contain single-line or multiline comments. Whitespace around values and operators will be ignored.

```sql
-- common services
dst.port == 80 /* http */ or dst.port == 53 /* dns */
-- database services
or dst.port in [
  3306,  -- MySQL
  5432,  -- PostgreSQL
  27017, -- MongoDB
  6379   -- Redis
]
```

## Qcontrol Field Reference

This document serves as a reference for the fields available in Qcontrol rules that can be used to monitor and control network traffic.

### Source Fields (src.\*)

| Field                 | Type               | Example                     | Description                  |
| --------------------- | ------------------ | --------------------------- | ---------------------------- |
| src.process.path      | string             | `/usr/bin/curl`             | Full path to the executable  |
| src.process.binary    | string             | `curl`                      | Binary name without path     |
| src.process.user.id   | int                | `1000`                      | Numeric user ID              |
| src.process.user.name | string             | `app-user`                  | Username                     |
| src.process.env       | map\[string]string | `{"HOME": "/home/user"}`    | Environment variables        |
| src.process.hostname  | string             | `worker-pod-1`              | Process hostname             |
| src.container.id      | string             | `a72dfe9c43a2`              | Container ID (short or full) |
| src.container.name    | string             | `app-frontend`              | Container name               |
| src.container.image   | string             | `nginx:1.21`                | Container image with tag     |
| src.container.labels  | map\[string]string | `{"app": "frontend"}`       | Container labels             |
| src.pod.name          | string             | `frontend-5d4d7d4b45-2jrvs` | Kubernetes pod name          |
| src.pod.namespace     | string             | `production`                | Kubernetes namespace         |
| src.pod.labels        | map\[string]string | `{"app": "frontend"}`       | Kubernetes pod labels        |
| src.ip                | IP                 | `10.0.0.1`                  | Source IP address            |
| src.port              | uint               | `55123`                     | Source port number           |

### Destination Fields (dst.\*)

<table><thead><tr><th>Field</th><th width="158.708251953125">Type</th><th>Example</th><th>Description</th></tr></thead><tbody><tr><td>dst.ip</td><td>IP</td><td><code>192.168.1.10</code></td><td>Destination IP address</td></tr><tr><td>dst.port</td><td>uint</td><td><code>443</code></td><td>Destination port number</td></tr><tr><td>dst.domain</td><td>string</td><td><code>api.example.com</code></td><td>Destination domain name</td></tr></tbody></table>

### Connection Fields

<table><thead><tr><th width="162.258544921875">Field</th><th width="97.3526611328125">Type</th><th width="178.2222900390625">Example</th><th>Description</th></tr></thead><tbody><tr><td>protocol</td><td>string</td><td><code>http2</code></td><td><p>L7 protocol</p><p>values: <code>unknown</code>, <code>http1</code>, <code>http2</code>, <code>dns</code>, <code>grpc</code></p></td></tr><tr><td>type</td><td>string</td><td><code>tcp</code></td><td>Socket type<br>values: <code>tcp</code>, <code>udp</code>, <code>raw</code>, <code>icmp</code></td></tr><tr><td>direction</td><td>string</td><td><code>egress</code></td><td>Traffic direction<br>values: <code>egress-external</code>, <code>egress-internal</code>, <code>egress</code></td></tr></tbody></table>

### TLS Fields (tls.\*)

| Field       | Type      | Example              | Description                                   |
| ----------- | --------- | -------------------- | --------------------------------------------- |
| tls.enabled | bool      | `true`               | Whether TLS is enabled                        |
| tls.version | float     | `1.2`                | TLS protocol version                          |
| tls.sni     | string    | `api.example.com`    | TLS Server Name Indication                    |
| tls.alpn    | \[]string | `["h2", "http/1.1"]` | Application-Layer Protocol Negotiation values |

### Metadata Fields

| Field | Type      | Example                        | Description            |
| ----- | --------- | ------------------------------ | ---------------------- |
| tags  | \[]string | `["app:frontend", "env:prod"]` | List of key-value tags |

## Common Security Use Cases

### Allow Essential Services

Place allow rules at the top of your configuration to ensure critical services remain accessible:

```yaml
- name: allow essential services
  expr: dst.domain in ["registry.k8s.io", "k8s.gcr.io", "gcr.io", "docker.io"]
  actions: [allow]
  
- name: allow documentation sites
  expr: dst.domain in ["kubernetes.io", "helm.sh", "prometheus.io"]
  actions: [allow]
```

### Enforce TLS Security

Block connections using outdated TLS versions:

```yaml
- name: block legacy tls
  expr: tls.enabled and tls.version <= 1.2
  actions: [deny]
```

### Protect Internal Services

Control access to internal network services:

```yaml
- name: restrict sensitive ports
  expr: dst.port in [22, 3306, 6379, 27017] and dst.ip != 10.0.0.0/8
  actions: [deny]
```

### Control Process Access

Restrict what specific applications can connect to:

```yaml
- name: restrict curl
  expr: src.process.binary == "curl" and not dst.domain in ["api.example.com", "updates.example.com"]
  actions: [deny]
```

### Prevent Data Exfiltration

Block access to cloud storage services except from authorized sources:

```yaml
- name: block cloud storage
  expr: dst.domain matches /(\.s3\.amazonaws\.com|\.storage\.googleapis\.com|\.blob\.core\.windows\.net)$/ and not src.pod.namespace == "backup"
  actions: [deny]
```

## Best Practices

### Rule Organization

* **Put allow rules first**: Define what should be explicitly allowed before setting up blocking rules
* **Order by priority**: Most critical rules should be evaluated first
* **Group related rules**: Keep similar rules together for easier management

### Rule Writing

* **Be specific**: Write targeted rules that address particular security concerns
* **Use descriptive names**: Make rule names clear and self-explanatory
* **Comment complex rules**: Add YAML comments to explain reasoning behind rules
* **Test thoroughly**: Validate rules in a non-production environment first
* **Readability:** Use whitespace and comments within rule expressions for clarity

### Performance Tips

* **Use exact matching**: Prefer `==` and `in` over regex patterns when possible
* **Limit regex complexity**: Simple patterns are more efficient

## Comprehensive Production Example

Here's an example of a production rule set for Kubernetes environments:

```yaml
control:
  rules:
    # Allow rules (highest priority)
    - name: allow essential services
      expr: dst.domain in ["registry.k8s.io", "k8s.gcr.io", "gcr.io", "docker.io"]
      actions: [allow]
      
    - name: allow documentation sites
      expr: dst.domain in ["kubernetes.io", "helm.sh", "prometheus.io", "pypi.org"]
      actions: [allow]
    
    - name: allow monitoring traffic
      expr: dst.port in [9090, 9100, 3000]
      actions: [allow]
    
    # Internal service protection
    - name: restrict sensitive ports
      expr: dst.port in [22, 3306, 6379, 27017] and dst.ip != 10.0.0.0/8
      actions: [deny]
    
    - name: enforce secure communications
      expr: tls.enabled and tls.version <= 1.2
      actions: [deny]
    
    # Prevent data exfiltration
    - name: block cloud storage 
      expr: dst.domain matches /(\.s3\.amazonaws\.com|\.storage\.googleapis\.com|\.blob\.core\.windows\.net)$/ and src.pod.namespace != "backup"
      actions: [deny]
    
    # Additional security rules
    - name: block cryptocurrency mining
      expr: dst.domain in ["pool.minergate.com", "cryptonight.net", "coinhive.com", "crypto-loot.com"]
      actions: [deny]
    
    - name: restrict database connections
      expr: |
        dst.port in [
          3306,  -- MySQL
          5432,  -- PostgreSQL
          27017, -- MongoDB
          6379   -- Redis
        ]
        and not src.pod.namespace in ["api", "backend", "monitoring"]
      actions: [deny]
```

## Troubleshooting

If your rules aren't working as expected:

* **Check rule ordering**: Rules are evaluated in order - earlier rules take precedence
* **Verify field names**: Ensure fields referenced in rules match the actual data
* **Test rule patterns**: Validate regex patterns against sample data
* **Check logs**: Look for logs showing which rules were matched
* **Simplify and iterate**: Start with simple rules and build complexity gradually
