README.md
Rama main · réplica de lectura del repositorio original.
<p align="center">
<img src="internal/web/static/keymaker-icon.svg" alt="" width="120">
</p>
<h1 align="center">Keymaker</h1>
<p align="center">
<a href="https://github.com/kentrow/keymaker/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/kentrow/keymaker/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://github.com/kentrow/keymaker/releases/latest"><img alt="Latest release" src="https://img.shields.io/github/v/release/kentrow/keymaker"></a>
<a href="LICENSE"><img alt="License: Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-blue.svg"></a>
<a href="https://scorecard.dev/viewer/?uri=github.com/Kentrow/keymaker"><img alt="Scorecard" src="https://api.scorecard.dev/projects/github.com/Kentrow/keymaker/badge"></a>
<a href="https://www.bestpractices.dev/projects/15213"><img alt="OpenSSF Best Practices" src="https://www.bestpractices.dev/projects/15213/badge"></a>
</p>
<p align="center">
A local web interface to inventory, audit, revoke and create OVHcloud API keys.
</p>
<p align="center">
English · <a href="README.fr.md">Français</a>
</p>
> [!IMPORTANT]
> Keymaker is an independent project. It is not affiliated with, endorsed by or sponsored by
> OVHcloud. OVHcloud is a trademark of its owner, used here only to identify the API this
> tool works with.
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/images/inventory-en-dark.png">
<img src="docs/images/inventory-en-light.png"
alt="The inventory: the account split by band, the quick filters, the first keys.">
</picture>
</p>
<p align="center">
The inventory, taken with <code>make demo</code>: every key, name and address in it is invented.
</p>
## Features
Keymaker works with classic OVHcloud API keys: an application key, an application secret and
a consumer key.
- **Inventory** of every key on the account, with its access rules, allowed addresses,
creation, expiry and last use, and the application it belongs to, including applications
the account does not own, such as the OVHcloud API console. The key Keymaker itself uses is
marked, and any rule it holds beyond what Keymaker needs is named. A key whose application
was deleted, which OVHcloud keeps listing as active, is shown as opening nothing. The whole
inventory, with its findings, downloads as a JSON or CSV report that holds no key value.
- **Audit** that flags keys reaching the whole account, able to change who can access it or
reaching billing and payments, working from any address, never expiring or expiring within a
week, never used, dormant for six months, created by OVHcloud support rather than by you,
never validated, indistinguishable from another key of the same application, or without a
description, and sorts them into "at risk", "to watch" and "nothing flagged".
- **Route explorer** over the whole published API, searchable by route or by purpose, to
build a set of access rules, with a warning when a rule reaches the whole account, can
change who can access it, or reaches billing and payments.
- **Key creation** through the OVHcloud `createToken` page, opened with the chosen rules
already filled in. The values of the new key never pass through Keymaker.
- **Replacement** of a key whose rules no longer fit, since the API cannot change the rules of
an existing key. The rules of the old key are the starting point, its allowed addresses are
listed to be entered again, and revoking it is the last step.
- **Allowed addresses** of an existing key changed in place, the one thing about a key the API
lets anyone change. The list is shown as OVHcloud will store it before it is saved, and the
key Keymaker uses is only ever restricted to a list covering the address it is seen from: a
key restricted away from its own address cannot even undo it.
- **Revocation** behind typing the key's identifier, and in one pass for every expired or
refused key. The key Keymaker uses is never revoked by mistake: it has its own way out,
revoke and leave, for when you are done with the tool.
- **Applications left without a key**, which no inventory of keys can show. Revoking a key
leaves its application behind, and an application is a key and a secret a new credential can
still be requested under. They can be deleted from here, one at a time or all at once, and an
application still holding a key never is, because OVHcloud would revoke that key along with
it.
- **An Understand screen** that lays out what an application, a key and an access rule each
are, on a made-up example, and what follows from it: why a revocation leaves an application
behind, why rules cannot be edited, and why some applications cannot be deleted from here.
- **Nothing written to disk**: no database, no cache, and a configuration file mounted
read-only.
- English and French interface, light and dark themes, list and card layouts.
## Security model
In short, with the details in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#security-model) and
the reporting policy in [SECURITY.md](SECURITY.md):
- Keymaker is a local, single-user tool. Anyone who can reach the interface can act with its
management key, so it listens on `127.0.0.1` and the container port must be published on
`127.0.0.1` only.
- A random access token is generated at every start and printed once in the address to open.
Every route but `GET /healthz` requires it.
- Every change also requires a second token that other origins cannot read.
- The content security policy allows nothing the binary does not serve.
- The application secret and the consumer key are redacted from every log line.
- Nothing is persisted, and created keys never reach the process.
- The only hosts contacted are the OVHcloud API and, when you ask for your public address or
restrict the key Keymaker uses, `api.ipify.org`. `KEYMAKER_IP_LOOKUP=off` removes the latter.
## Quick start
### 1. Create the management key
Keymaker authenticates with a key of its own, which needs read access to your credentials and
applications:
```text
GET /me/api/credential
GET /me/api/credential/*
GET /me/api/application
GET /me/api/application/*
DELETE /me/api/credential/*
DELETE /me/api/application/*
```
Three rules are optional, and Keymaker says which one is missing instead of failing:
`DELETE /me/api/credential/*` for revoking keys, `GET /me/api/application` for listing the
applications holding no key, and `DELETE /me/api/application/*` for deleting those. Never grant
this key `/me/*` or `/*`.
A seventh rule, `PUT /me/api/credential/*`, lets Keymaker change the allowed addresses of your
keys. The links below leave it out on purpose: the same rule can widen the reach of any key, so
Keymaker's audit flags a key holding it, this one included, as able to change access to the
account. Add it on the OVHcloud page if you want that trade, or follow the link Keymaker shows
when you ask to edit addresses without it.
Each link below opens the OVHcloud page of one endpoint with those rules filled in. Take the
one matching the region of your account, as the official SDKs name it.
**`ovh-eu`**
<https://eu.api.ovh.com/createToken/?GET=/me/api/credential&GET=/me/api/credential/*&GET=/me/api/application&GET=/me/api/application/*&DELETE=/me/api/credential/*&DELETE=/me/api/application/*>
**`ovh-ca`**
<https://ca.api.ovh.com/createToken/?GET=/me/api/credential&GET=/me/api/credential/*&GET=/me/api/application&GET=/me/api/application/*&DELETE=/me/api/credential/*&DELETE=/me/api/application/*>
**`ovh-us`**
<https://api.us.ovhcloud.com/createToken/?GET=/me/api/credential&GET=/me/api/credential/*&GET=/me/api/application&GET=/me/api/application/*&DELETE=/me/api/credential/*&DELETE=/me/api/application/*>
The page also asks for a validity. A key that never expires is one Keymaker's own audit flags,
so give this one an expiry you are willing to renew. Keymaker offers the right link for its own
endpoint whenever the API refuses its key.
### 2. Write `ovh.conf`
```ini
[default]
endpoint=ovh-eu
[ovh-eu]
application_key=<application key>
application_secret=<application secret>
consumer_key=<consumer key>
```
[`ovh.conf.example`](ovh.conf.example) is this file with every value explained. Keep the file
out of version control and readable by you alone:
```bash
cp ovh.conf.example ovh.conf
chmod 600 ovh.conf
```
### 3. Run the container
```bash
docker run --rm \
-p 127.0.0.1:8080:8080 \
--user "$(id -u):$(id -g)" \
-v ./ovh.conf:/config/ovh.conf:ro \
--read-only \
--cap-drop=ALL \
--security-opt no-new-privileges \
ghcr.io/kentrow/keymaker:1
```
The process prints one address, token included. Open it: the token moves into a session cookie
and disappears from the address bar. Stopping the process ends the session.
`--user` runs the container as you, so it can read an `ovh.conf` that only you can read. The
image otherwise runs as its own unprivileged user.
The tag `1` follows every 1.x release, which the [compatibility promise](#compatibility) keeps
safe to take. To stay on one release, name it instead, for example
`ghcr.io/kentrow/keymaker:1.0.0`.
### Or with Docker Compose
```yaml
# compose.yaml
services:
keymaker:
image: ghcr.io/kentrow/keymaker:1
user: "${KEYMAKER_UID:?run export KEYMAKER_UID=$(id -u)}:${KEYMAKER_GID:?run export KEYMAKER_GID=$(id -g)}"
ports:
- "127.0.0.1:8080:8080"
volumes:
- ./ovh.conf:/config/ovh.conf:ro
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
```
```bash
export KEYMAKER_UID=$(id -u) KEYMAKER_GID=$(id -g)
docker compose up
```
### Check where the image comes from
Every release image carries a build provenance and an SBOM, signed and attested by GitHub.
With the [GitHub CLI](https://cli.github.com/):
```bash
gh attestation verify oci://ghcr.io/kentrow/keymaker:1 --repo kentrow/keymaker
```
A successful check proves that the image was built by the release workflow of this repository,
from the tagged commit it names, and has not been changed since. It does not prove that the
code is free of bugs: it tells you what you run, not that it is right. Each release on GitHub
also carries the same signed provenance as a file, `keymaker-X.Y.Z.intoto.jsonl`.
## Configuration
### `ovh.conf`
The file follows the format the official OVHcloud SDKs read. `endpoint` in `[default]` names
the section to use, and must be `ovh-eu`, `ovh-ca` or `ovh-us`. Keymaker reads only the file it
is given: it ignores `~/.ovh.conf`, `/etc/ovh.conf` and the `OVH_*` environment variables.
### Environment variables
| Variable | Default | Purpose |
| --- | --- | --- |
| `KEYMAKER_CONFIG` | `/config/ovh.conf` | Path to the configuration file, opened read-only. |
| `KEYMAKER_ADDR` | `127.0.0.1:8080` for the binary, `0.0.0.0:8080` in the image | Listen address. Any address other than loopback logs a warning. |
| `KEYMAKER_PUBLIC_URL` | derived from the listen address | Address the interface is reached on, used to print the startup link. Set it when the published port differs from 8080, for example `http://127.0.0.1:9000`. An `https` address, behind a reverse proxy, also marks the session cookie `Secure`. |
| `KEYMAKER_IP_LOOKUP` | enabled | `off` removes the public address lookup, so the OVHcloud API is the only host contacted. |
| `KEYMAKER_LOG_LEVEL` | `info` | `debug`, `info`, `warn` or `error`. `debug` adds one line per request, without its query string. |
### Command line
| Command | Purpose |
| --- | --- |
| `keymaker` | Start the server. |
| `keymaker --version` | Print the version, commit and build date, then exit. |
| `keymaker healthcheck` | Ask `GET /healthz` of the running server on the loopback, and exit 0 when it answers. |
### Health check
`GET /healthz` answers `ok` without the access token, for orchestrators and probes. The image
declares a `HEALTHCHECK` that runs `keymaker healthcheck`: the binary asks that route on the
loopback and exits 0 or 1, since the image has no shell and no HTTP client. `docker ps` and
Compose show the container as healthy without any further setting.
## Compatibility
From 1.0, Keymaker follows [semantic versioning](https://semver.org/), and every 1.x release
keeps the following as they are:
- the `ovh.conf` format and the keys it reads: `endpoint`, `application_key`,
`application_secret` and `consumer_key`;
- the `KEYMAKER_*` environment variables, their defaults and the values they accept;
- the command line: `keymaker`, `--version` and `healthcheck`;
- `GET /healthz`, its path and its `ok` answer;
- the image: its name, its tags (`X.Y.Z`, `X.Y`, `X` and `latest`), its platforms
(`linux/amd64` and `linux/arm64`), port 8080, and running as an unprivileged user on a
read-only root filesystem, as the commands above start it;
- the access rules a management key needs: a 1.x release may only make use of a new rule as an
optional one, without which everything that worked keeps working;
- the downloadable report: fields may be added, and existing ones keep their name and meaning.
Everything else may change in a minor release: the interface and its layout, the `/api/*`
routes it talks to, the wording and shape of log lines, and the audit, which may raise new
findings. Something on the list above that has to go is first deprecated, in a release whose
notes say so and which logs a warning when it is used, and removed in 2.0 at the earliest.
Keymaker is tried on `ovh-eu`. `ovh-ca` and `ovh-us` go through the same code and are supported
on a best-effort basis; a report on either is welcome in
[#50](https://github.com/kentrow/keymaker/issues/50). The interface needs a current browser:
the latest two major versions of Chrome, Edge, Firefox or Safari.
## Building from source
Requirements: Go (the version in [`go.mod`](go.mod)), Docker, and
[golangci-lint](https://golangci-lint.run/) for `make lint`.
```bash
make help # list the targets
make build # build ./bin/keymaker
make test # run the tests with the race detector
make lint # gofmt, go mod tidy and golangci-lint
make vuln # govulncheck
make docker # build the image for the local platform
make demo # run the interface on invented data, no account needed
```
Run the binary against your configuration:
```bash
KEYMAKER_CONFIG=./ovh.conf ./bin/keymaker
```
## Documentation
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): how Keymaker is built and what it calls
- [CONTRIBUTING.md](CONTRIBUTING.md): how to contribute
- [SECURITY.md](SECURITY.md): security policy and vulnerability reporting
- [CHANGELOG.md](CHANGELOG.md): release notes
- [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md): community guidelines
## License
Keymaker is licensed under the [Apache License 2.0](LICENSE). Third-party components and
attributions are listed in [NOTICE](NOTICE).
The Keymaker mascot is licensed under the Creative Commons Attribution 4.0 license. It was
inspired by the Go gopher, designed by [Renee French](https://reneefrench.blogspot.com/),
whose artwork is licensed under the
[Creative Commons Attribution 4.0](https://go.dev/blog/gopher) license. The icons come from
[Lucide](https://lucide.dev), under the ISC license.