Secure and monitor IPC connections¶
This page explains how to secure a q server with authentication and authorization hooks, and how to monitor and interrupt IPC connections in production.
A q process with an open port accepts whatever a client that can connect chooses to send. Interprocess communication (IPC) has no security of its own. By default it evaluates that input, so authentication and authorization are what stand between a listening port and arbitrary code execution.
Authentication and authorization¶
q provides callbacks in the .z namespace for your own security logic.
Authentication¶
For basic username and password authentication, start the q process with a credentials file, using the -u or -U option. q then validates each client's credentials against that file as it connects.
For anything more, such as Lightweight Directory Access Protocol (LDAP) or Kerberos, override the .z.pw callback. It receives the username and password, and returns a boolean that either admits or rejects the connection.
Authorization¶
Once a user is authenticated, use the .z.pg (synchronous) and .z.ps (asynchronous) callbacks for finer-grained permissions. Both can restrict access to particular functions or data based on the user, .z.u, or the connection handle, .z.w.
The server below admits an allowlist of functions and rejects everything else. It parses the incoming request into a parse tree, walks the tree, and checks every function it finds:
q)\p 5000
q)allowedFns: (`func1; `func2; `func3; +; -) / the functions a client may call
/ signal an error if a call is not allowed
q)checkFn: {if[not x in allowedFns; '(.Q.s1 x), " not allowed"]}
/ walk the parse tree: check the head of each general list, then recurse
q)validatePT: {if[0h = type x; if[(not 0h = type first x) & 1 = count first x; checkFn first x]; .z.s each x where 0h = type each x]}
/ parse a string request, validate it, then evaluate it
q).z.pg: {if[10h = type x; x: parse x]; validatePT x; eval x}
A client that calls a function outside the allowlist gets an error instead of a result:
q)h: hopen `::5000
q)h"1+1" / allowed
2
q)h"1*1" / rejected by the custom .z.pg
'* not allowed
[0] h"1*1"
^
Weigh the check against the message rate
Every message pays for this validation. On a high-volume feed — a tickerplant publishing through .z.ps, for example — consider skipping the check for handles you have already established as trusted, rather than validating every update.
Monitor connections¶
Track open connections¶
To inspect the connections a server holds:
.z.Hreturns the active socket handles-38!xreturns a dictionary describing the open sockets in detail
For your own tracking, maintain a table of connected clients from the .z.po (open) and .z.pc (close) callbacks.
Interrupt a request¶
To abandon a long-running synchronous query, send an interrupt signal to the client process, such as kill -s INT <PID>. The client receives an 'rcv handle error, and the handle becomes unusable:
q)h"system\"sleep 30\"" / a long-running query blocks the client
Interrupting the client process from another terminal ends the wait, and the client console reports:
'rcv handle: 4. OS reports: Interrupted system call
[0] h"system\"sleep 30\""
^
The handle is broken from that point on, so reconnect before you send anything else:
q)h"1+1"
'Cannot write to handle 4. OS reports: Bad file descriptor
[0] h"1+1"
^
The interrupt stops the client waiting, not the server working
The server carries on evaluating the query it was sent, so the interrupt doesn't free the server. To cap server-side work, start the server with the -T timeout option, which abandons a query that runs too long.
Next steps¶
- IPC basics — connections, synchronous requests, and the
.zserver callbacks. - Asynchronous communication — why an unprotected server that runs client-supplied functions is a particular risk.
- Configure listening ports — narrow a process's exposure by binding to localhost.
- Encrypt connections with SSL/TLS — protect credentials and data in transit.
- Permissions with q — a white paper on building a permission layer.