Skip to main content

Hello World with gRPC

Styra Load has a gRPC API, allowing low-latency, efficient communication between production systems. It emulates the OPA v1 REST API endpoints, and includes a few experimental endpoints which can provide improved performance for some workloads.

The following resources provide additional information:

Configuration

The gRPC service is provided by the grpc plug-in, and needs to be enabled in Styra Load's configuration before the gRPC endpoints are made available to the network.

load-conf.yaml

plugins:
grpc:
addr: localhost:9090

The minimalist configuration above will enable the gRPC service, and bind it to local port 9090.

Example with grpcurl

We'll follow the example from the OPA introductory tutorial, but with a twist: all of the network steps will be done through gRPC.

We'll need two files first: a policy file and a data file.

input.json

{
"servers": [
{"id": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"]},
{"id": "db", "protocols": ["mysql"], "ports": ["p3"]},
{"id": "cache", "protocols": ["memcache"], "ports": ["p3"]},
{"id": "ci", "protocols": ["http"], "ports": ["p1", "p2"]},
{"id": "busybox", "protocols": ["telnet"], "ports": ["p1"]}
],
"networks": [
{"id": "net1", "public": false},
{"id": "net2", "public": false},
{"id": "net3", "public": true},
{"id": "net4", "public": true}
],
"ports": [
{"id": "p1", "network": "net1"},
{"id": "p2", "network": "net3"},
{"id": "p3", "network": "net2"}
]
}

example.rego

package example

import future.keywords.if
import future.keywords.in

default allow := false # unless otherwise defined, allow is false

allow if count(violation) == 0 # allow is true if there are zero violations.

violation[server.id] { # a server is in the violation set if...
some server in public_server # it exists in the 'public_server' set and...
"http" in server.protocols # it contains the insecure "http" protocol.
}

violation[server.id] { # a server is in the violation set if...
some server in public_server # it exists in the input.servers collection and...
"telnet" in server.protocols # it contains the "telnet" protocol.
}

public_server[server] { # a server exists in the public_server set if...
some i, j
server := input.servers[_] # it exists in the input.servers collection and...
server.ports[_] == input.ports[i].id # it references a port in the input.ports collection and...
input.ports[i].network == input.networks[j].id # the port references a network in the input.networks collection and...
input.networks[j].public # the network is public.
}

Once we have these two files, we can then use grpcurl to follow the rest of the tutorial, although we'll need to alter most steps to work with the JSON formats that grpcurl expects to see.

We'll end up making three gRPC calls against the Load server:

Below is a Bash script that you can use to quickly generate the correct JSON request formats for grpcurl:

#!/bin/bash

# Policies are sent as strings, and because we're using grpcurl, we
# have to escape the policy text so that it can be used as a JSON value.
json_escape () {
python -c 'import json,sys; print(json.dumps(sys.stdin.read()))'
}

# Create a JSON-formatted request description for grpcurl, targeting the
# `load.policy.v1.PolicyService/CreatePolicy` endpoint.
cat <<EOF > v1-policy-input.json
{
"policy": {
"path": "/example",
"text": $(json_escape < example.rego)
}
}
EOF

# Create a JSON-formatted request description for grpcurl, targeting the
# `load.data.v1.DataService/GetData` endpoint.
cat <<EOF > v1-data-input-1.json
{
"path": "/example/violation",
"input": {
"document": $(cat input.json)
}
}
EOF

# Create a JSON-formatted request description for grpcurl, targeting the
# `load.data.v1.DataService/GetData` endpoint, using a different query path.
cat <<EOF > v1-data-input-2.json
{
"path": "/example/allow",
"input": {
"document": $(cat input.json)
}
}
EOF

We can then push up our policy, and query the two endpoints from the original tutorial, example/violation and example/allow as follows:

# Uploads a policy to the Load server.
grpcurl -d @ -plaintext localhost:9090 load.policy.v1.PolicyService/CreatePolicy <v1-policy-input.json

# Queries the policy's 'example/violation' rule.
grpcurl -d @ -plaintext localhost:9090 load.data.v1.DataService/GetData <v1-data-input-1.json

# Queries the policy's 'example/allow' rule.
grpcurl -d @ -plaintext localhost:9090 load.data.v1.DataService/GetData <v1-data-input-2.json

The output from grpcurl will look like:

{

}
{
"result": {
"path": "/example/violation",
"document": [
"busybox",
"ci"
]
}
}
{
"result": {
"path": "/example/allow",
"document": false
}
}

The results should be the same as we'd expect from the OPA REST APIs.