Skip to content

keygen-sh/keygen-relay

Repository files navigation

Keygen Relay

CI

Warning

During the beta period, the master branch may be unstable. The master branch will likely include unreleased code, and may not build at all. Please use the latest release for a stable build.

Note

During the beta, your feedback is greatly appreciated! We'd love to hear about your use-cases and how you're running Relay inside of your customer environments — from networking to deployment. Feel free to open a discussion, or reach out to us on Discord.

Relay is an offline-first on-premise licensing server backed by Keygen. Use Relay to securely manage distribution of cryptographically signed and encrypted license files across nodes in an offline or air-gapped environment. Relay does not require or utilize an internet connection — it is meant to be used stand-alone in an offline or air-gapped network.

Relay has a vendor-facing CLI that can be used to onboard a customer's air-gap environment. An admin can initialize Relay with N licenses to be distributed across M nodes, ensuring that only N nodes are licensed at one time.

Relay provides an app-facing REST API that allows nodes to claim a lease on a license and release it when no longer needed.

Background

Relay was born out of a limitation in Keygen — and really, a limitation in all licensing APIs — the limitation being that implementing a node-based licensing model, e.g. floating licenses, is hard in an air-gapped or otherwise offline environment using an external API, due to the nature of APIs needing an internet connection. Whether self-hosting Keygen EE, or using Keygen Cloud, the issue remains the same.

Since Keygen is an API, it can't communicate to the nodes inside these isolated environments, and that means it can't easily track which nodes are being used and which nodes are not. It also has no visibility into how many nodes there are currently vs how many nodes are allowed in total. Some vendors may be able to whitelist Keygen in the customer's firewall, but that's rare.

In the past, we've seen workarounds for this problem. Most of them consist of using an intermediary between the offline world and the online world — typically a mobile device or a tablet. In this case, the intermediary acts on behalf of the offline node, activating it via an online portal, and passing on a signed payload, e.g. a license file, for verification.

As an alternative, some customers have even asked if they can self-host Keygen on-premise for customers — but that's inherently unsafe, since customers would have full access to Keygen, thus access to granting themselves licenses, adjusting policy rules, etc.

While the aforementioned intermediary-based workaround can work — it's brittle. And it requires human intervention, which just doesn't really work in the age of cloud computing and autoscaling. For example, you couldn't use this workaround to license on-premise software, where you wanted to only allow the customer to use 20 concurrent processes at one time — it just wouldn't be feasible to ask a human to hop on their phone and activate nodes in an autoscaling k8s cluster as it autoscales.

Thus, the idea for Relay was born — a bridge between Keygen and the offline universe, secured via cryptography.

Installation

To install Relay, you can follow the instructions and run the command below. Alternatively, you can install manually by downloading a prebuilt binary and following the install instructions here.

Automatically detect and install relay on the current platform:

curl -sSL https://raw.pkg.keygen.sh/keygen/relay/latest/install.sh | sh

This will install relay in /usr/local/bin.

Missing a platform? Open an issue.

Usage

For all available commands and flags, run relay --help.

CLI

The CLI can be used by the vendor to setup and manage customer environments.

Add license

You can add a new license to the pool using the add command:

relay add --file license.lic --key xxx --public-key xxx

The add command supports the following flags:

Flag Description
--file Path to the license file to add to the pool.
--key License key for decryption.
--public-key Your account public key for license verification.

Delete license

To delete a license from the pool, use the del command:

relay del --license xxx

The del command supports the following flags:

Flag Description
--license The unique ID of the license to delete from the pool.

List licenses

To list all the licenses in the pool, use the ls command:

relay ls

The ls command supports the following flags:

Flag Description
--plain Print results non-interactively in plaintext.

Stat license

To retrieve the status of a specific license, use the stat command:

relay stat --license xxx

The stat command supports the following flags:

Flag Description
--license The unique ID of the license to retrieve info about.
--plain Print results non-interactively in plaintext.

Server

To start the relay server, use the following command:

relay serve --port 6349

The serve command supports the following flags:

Flag Description Default
--port, -p Specifies the port on which the relay server will run. 6349
--no-heartbeats Disables the heartbeat system. When this flag is enabled, the server will not automatically release inactive or dead nodes, and leases cannot be extended. false
--strategy Specifies the license distribution strategy. Options: fifo, lifo, rand. fifo
--ttl, -t Sets the time-to-live for leases. Licenses will be automatically released after the time-to-live if a node heartbeat is not maintained. Options: e.g. 30s, 1m, 1h, etc. 30s
--cull-interval Specifies how often the server should check for and deactivate inactive or dead nodes. 15s
--database Specify a custom database file for storing the license and node data. ./relay.sqlite

E.g. to start the server on port 8080, with a 30 second node TTL and FIFO distribution strategy:

relay serve --port 8080 --ttl 30s --strategy fifo

API

The API can be consumed by the vendor's application to claim a lease on a license, and also release the lease, on behalf of a node.

Health check

The Relay server's health can be checked with the following endpoint:

curl -v -X GET "http://localhost:6349/v1/health"

Returns a 200 OK status code.

Claim license

Nodes can claim a lease on a license by sending a PUT request to the /v1/nodes/{fingerprint} endpoint:

curl -v -X PUT "http://localhost:6349/v1/nodes/$(cat /etc/machine-id)"

Accepts a fingerprint, an arbitrary string identifying the node.

Returns 201 Created with a license_file and license_key for new nodes. If a lease already exists for the node, the lease is extended by --ttl and the server will return 202 Accepted, unless heartbeats are disabled and in that case a 409 Conflict will be returned. If no licenses are available to be leased, i.e. no licenses exist or all are being actively leased, the server will return 410 Gone.

{
  "license_file": "LS0tLS1CRUdJTiBMSUNFTlNFIEZJTEUtLS0tL...S0NCg0K",
  "license_key": "9A96B8-FD08CD-8C433B-7657C8-8A8655-V3"
}

The license_file will be base64 encoded.

Release license

Nodes can release a license when no longer needed by sending a DELETE request to the same endpoint:

curl -v -X DELETE "http://localhost:6349/v1/nodes/$(cat /etc/machine-id)"

Accepts a fingerprint, the node fingerprint used for the lease.

Returns 204 No Content with no content. If a lease does not exist for the node, the server will return a 404 Not Found.

Logs

Relay comes equipped with audit logs out-of-the-box, allowing the full history of the Relay server to be audited. They can be viewed using a sqlite3 client, providing the path to the Relay database file:

sqlite3 ./relay.sqlite
-- recent events
SELECT
  audit_logs.*
FROM
  audit_logs
ORDER BY
  audit_logs.created_at DESC
LIMIT
  25;

-- recently leased licenses
SELECT
  audit_logs.*
FROM
  audit_logs
JOIN
  event_types ON event_types.id = audit_logs.event_type_id
WHERE
  event_types.name = 'license.leased'
ORDER BY
  audit_logs.created_at DESC
LIMIT
  5;

-- entire history in chronological order
SELECT
  datetime(audit_logs.created_at, 'unixepoch') AS created_at,
  event_types.name AS event_type,
  entity_types.name AS entity_type,
  audit_logs.entity_id
FROM
  audit_logs
JOIN
  event_types ON event_types.id = audit_logs.event_type_id
JOIN
  entity_types ON entity_types.id = audit_logs.entity_type_id
ORDER BY
  audit_logs.created_at ASC;

If you have concerns about storage, or do not wish to keep audit logs, use Relay's --no-audit flag to disable them.

Developing

Building

To build Keygen Relay from the source, clone this repository and run:

go build -o relay ./cmd/relay

# or...
make build

Alternatively, you can build binaries for specific platforms and architectures using the provided make commands:

make build

# or specific platform...
make build-linux-amd64

# or all platforms...
make build-all

Releasing

To cut and publish a new release of Relay, update the VERSION file and run the following make command:

make release

Releases are uploaded and published using the Keygen CLI. Releases are hosted and distributed by Keygen Cloud. You will need credentials and permission to upload to our production Keygen Cloud account.

Testing

Keygen Relay comes with a suite of tests, including integration tests that verify behavior with the real server.

To run regular tests:

make test

To run integration tests, tagged with // +build integration:

make test-integration

License

This project is licensed under the MIT License. See the LICENSE file for details.

Contributing

If you discover an issue, or are interested in a new feature, please open an issue. If you want to contribute code, feel free to open a pull request. If the PR is substantial, it may be beneficial to open an issue beforehand to discuss.

The CLA is available here.

Security

We take security at Keygen very seriously. If you believe you've found a vulnerability, please see our SECURITY.md file.

Is it any good?

Yes.