Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/rtb-buddy
/.idea/
/coverage.out
28 changes: 28 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.PHONY: all build test fmt lint vet fix clean

BIN := rtb-buddy
MODULE := ./cmd/rtb-buddy

all: fix lint test build

build:
go build -o $(BIN) $(MODULE)

test:
go test -cover -coverprofile=coverage.out ./...
#go tool cover -func=coverage.out

fmt:
gofmt -s -w .

lint:
go tool golangci-lint run ./...

vet:
go vet ./...

fix:
go fix ./...

clean:
rm -f $(BIN) coverage.out
100 changes: 100 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
= rtbrick/tools
:toc: left
:toclevels: 3
:sectnums:
:source-highlighter: rouge
:icons: font
:experimental:

// Badges
image:https://img.shields.io/badge/license-BSD_3--Clause-blue.svg[License]
image:https://img.shields.io/badge/go-1.27+-00ADD8.svg[Go Version]
image:https://img.shields.io/badge/module-github.com%2Frtbrick%2Ftools-green.svg[Module]

== Overview

`rtbrick/tools` is a **collection of CLI tools** for the RtBrick platform.

== Tools

|===
| Tool | Description

| `rtb-buddy`
| Multipurpose helper tool for the RtBrick fullstack. See link:cmd/rtb-buddy/README.adoc[documentation].

|===

== Development

=== Prerequisites

* Go 1.27+
* Git
* https://golangci-lint.run/[golangci-lint] (optional, for `make lint`)

=== Make Targets

[source,sh]
----
make build # Build the rtb-buddy binary
make test # Run all tests
make fmt # Format source code
make lint # Run golangci-lint
make vet # Run go vet
make clean # Remove built binaries
----

== Contributing

We welcome contributions! Please follow these guidelines:

=== Reporting Issues

* Search existing issues first to avoid duplicates
* Use the issue template if available
* Include: steps to reproduce, expected vs actual behavior, environment

=== Pull Requests

1. Fork the repository
2. Create a feature branch: `git checkout -b feature/my-change`
3. Make your changes with clear, focused commits
4. Run `make fmt` and `make lint` before committing
5. Add tests for new functionality
6. Open a PR with a descriptive title and description

=== Code Style

* Follow standard Go conventions (`gofmt`, `golint`)
* Keep functions small and focused
* Document exported types and functions with Go doc comments
* Update documentation when behavior changes

=== Commit Messages

Use conventional commits format:

[source,sh]
----
<type>(<scope>): <subject>

<body>

<footer>
----

Examples:
* `feat(rtb-buddy): add --aud flag for token audience`
* `fix(utils): handle empty KID in JWKS lookup`
* `docs: update shell completion instructions`

== License

BSD 3-Clause License — see the LICENSE file for details.

Copyright (c) 2026, RtBrick

=== SPDX Identifier

`BSD-3-Clause`
242 changes: 242 additions & 0 deletions cmd/rtb-buddy/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
= rtb-buddy

Multipurpose helper tool for the RtBrick fullstack.

== Installation

=== From Source

.Requires Go 1.27+
[source,sh]
----
git clone https://github.com/rtbrick/tools.git
cd tools
make build
----

The binary `rtb-buddy` is placed in the project root.

=== Go Install

[source,sh]
----
go install github.com/rtbrick/tools/cmd/rtb-buddy@latest
----

The binary is placed in `$(go env GOPATH)/bin`.

== Usage

[source,sh]
----
# Generate key pair
rtb-buddy apigw generate jwks

# Issue a token
rtb-buddy apigw generate token --sub alice --name Alice --preferred-username alice --scope supervisor

# Verify a token
echo "<token>" | rtb-buddy apigw inspect token
----

== Shell Completion

[source,sh]
----
# zsh (current session)
source <(rtb-buddy completion zsh)

# zsh (persistent - Linux)
rtb-buddy completion zsh > "${fpath[1]}/_rtb-buddy"

# zsh (persistent - macOS)
rtb-buddy completion zsh > "$(brew --prefix)/share/zsh/site-functions/_rtb-buddy"

# bash
source <(rtb-buddy completion bash)
----

== Commands

=== apigwd

APIGWD relevant commands.

==== apigw generate jwks

Generate an RSA key pair as two JWKS files.

* `--priv` — output path for the private JWKS (default: `apigw-jwks-priv.json`)
* `--pub` — output path for the public JWKS (default: `apigw-jwks.json`)
* `--kid` — key ID for the JWKS entries (default: `access`)

The private JWKS contains two entries (private key + public key, same KID).
The public JWKS contains one entry (public key only, for distribution).

[source,sh]
----
rtb-buddy apigw generate jwks
rtb-buddy apigw generate jwks --kid "prod" --priv prod-jwks-priv.json --pub prod-jwks.json
----

=== apigw generate token

Generate a signed JWT. The private key is read from the JWKS file.

* `--priv` — private JWKS file path (default: `apigw-jwks-priv.json`)
* `--kid` — key ID to select from the JWKS (empty = first key)
* `--override-kid` — override the kid in the JWT header (default: use the JWKS entry's kid)
* `--sub` — token subject (required)
* `--name` — name claim (required)
* `--preferred-username` — preferred username claim (required)
* `--scope` — scope claim (default: `user`)
* `--iss` — issuer (default: `rtbrick`)
* `--dur` — token duration (default: `1h`)
[source,sh]
----
# Single key in JWKS — --kid not needed
rtb-buddy apigw generate token \
--sub 1234567890 \
--name rtbrick \
--preferred-username rtbrick \
--scope supervisor

# Multiple keys — select by KID
rtb-buddy apigw generate token \
--kid "prod" \
--sub 1234567890 \
--name rtbrick \
--preferred-username rtbrick

# Override the kid in the JWT header
rtb-buddy apigw generate token \
--kid "access" \
--override-kid "rotated" \
--sub 1234567890 \
--name rtbrick \
--preferred-username rtbrick
----

==== apigw generate tls

[source,sh]
----
rtb-buddy apigw generate tls --org "rtbrick"
----

==== apigw inspect token

Decode a JWT from stdin. Verify signature if public JWKS is available. Reports expiration status.

* `--pub` — public JWKS file path (default: `apigw-jwks.json`)
* `--override-kid` — override the kid for key lookup in the JWKS
[source,sh]
----
# Verify with matching kid
rtb-buddy apigw generate token --kid access --sub 123 --name admin --preferred-username admin \
| rtb-buddy apigw inspect token

# Override kid for key lookup (e.g. token has kid "test", JWKS has kid "access")
rtb-buddy apigw generate token --kid access --override-kid test --sub 123 --name admin --preferred-username admin \
| rtb-buddy apigw inspect token --override-kid access
----

=== lix1

LI X1 mTLS certificate management commands for Lawful Intercept.

==== mTLS Setup

LI X1 uses mutual TLS (mTLS) between ADMF (Administrative Function / management system) and RBFS (network device).

CA:: Trust anchor — issues certificates for all components
RBFS:: Server — presents certificate, verifies ADMF clients
ADMF:: Client — presents certificate, verifies RBFS server

===== Generate certificates

[source,sh]
----
# 1. Generate CA
rtb-buddy lix1 generate ca --cn "LI X1 CA" --org "RtBrick" --ou "Engineering"

# 2. Generate RBFS certificate (server)
rtb-buddy lix1 generate cert --cn "rbfs-router-01" --org "RtBrick" --ou "Engineering" --crt rbfs.crt --crt-key rbfs.key

# 3. Generate ADMF certificate (client)
rtb-buddy lix1 generate cert --cn "admf-prod" --org "RtBrick" --ou "Engineering" --crt admf.crt --crt-key admf.key
----

===== Deploy

* **RBFS**: `rbfs.crt` + `rbfs.key` + `ca.crt`
* **ADMF**: `admf.crt` + `admf.key` + `ca.crt`

Both sides need `ca.crt` to verify each other's certificates.

==== lix1 generate ca

Generate a root CA certificate and private key.

* `--ca-crt` — CA certificate output path (default: `ca.crt`)
* `--ca-crt-key` — CA private key output path (default: `ca.key`)
* `--cn` — Common Name (required)
* `--org` — Organization (required)
* `--ou` — Organizational Unit (required)
* `--duration` — Certificate validity duration (default: `87600h` / 10 years)

[source,sh]
----
rtb-buddy lix1 generate ca --cn "RtBrick LIX1" --org "RtBrick" --ou "Engineering"
rtb-buddy lix1 generate ca --cn "My CA" --org "My Org" --ou "Eng" --ca-crt my-ca.crt --ca-crt-key my-ca.key
----

==== lix1 generate cert

Generate a certificate signed by the CA.

* `--ca-crt` — CA certificate input path (default: `ca.crt`)
* `--ca-crt-key` — CA private key input path (default: `ca.key`)
* `--crt` — Certificate output path (required)
* `--crt-key` — Private key output path (required)
* `--cn` — Common Name (required)
* `--org` — Organization (required)
* `--ou` — Organizational Unit (required)
* `--duration` — Certificate validity duration (default: `87600h` / 10 years)

[source,sh]
----
rtb-buddy lix1 generate cert --cn "Test Device 1" --org "RtBrick" --ou "Testing" --crt device_test_1.crt --crt-key device_test_1.key
rtb-buddy lix1 generate cert --cn "ADMF" --org "RtBrick" --ou "Engineering" --crt admf.crt --crt-key admf.key
rtb-buddy lix1 generate cert --cn "RBFS" --org "RtBrick" --ou "Engineering" --crt rbfs.crt --crt-key rbfs.key
----

==== lix1 test cert

Test certificate validity and chain verification.

* `--crt` — Certificate to test (required)
* `--ca-crt` — CA certificate for chain validation (default: `ca.crt`)

[source,sh]
----
rtb-buddy lix1 test cert --crt device_test_1.crt
rtb-buddy lix1 test cert --crt admf.crt --ca-crt ca.crt
----

.Example output:
[source]
----
Subject: CN=Test Device 1,OU=Testing,O=RtBrick
Issuer: CN=RtBrick LIX1,OU=Engineering,O=RtBrick
Serial: 1658
Signature Algorithm: SHA256-RSA
Not Before: 2026-09-16T17:11:06Z
Not After: 2036-09-13T17:11:06Z
Is CA: false

Status: VALID (expires in 87600h0m0s)

Verifying certificate chain against CA...
Chain verification: OK
----
Loading