mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-31 14:12:35 +00:00
d882f223f4
* feat(api-tokens): switch to sen_sk_ prefixed opaque keys
Replace JWT-shaped API tokens with 56-char opaque keys of the form
`sen_sk_<43-char base62 random><6-char base62 checksum>` (256-bit
entropy, sha256-truncated checksum). Node-proxy tokens stay JWTs.
Why:
* The api_token path was already a sha256 DB lookup; the JWT signature
was wasted work and the 400d JWT ceiling vs DB expires_at was a
confusing dual bound.
* Opaque tokens carry a verifiable checksum so malformed/typoed values
are rejected before any SQLite lookup.
* `sen_sk_` prefix is recognizable to GitHub, TruffleHog, GitGuardian
and makes the on-wire shape visually distinct from node_proxy JWTs.
Changes:
* New `utils/apiTokenFormat.ts` (generate + checksum-verify, CSPRNG via
randomInt, timingSafeEqual on the checksum compare).
* `middleware/auth.ts` and `websocket/upgradeHandler.ts` route opaque
tokens before any jwt.verify; 401 messages unified to avoid a
token-existence oracle.
* `middleware/rateLimiters.ts` short-circuits opaque tokens in the
node_proxy detection and keys per-token via a non-reversible sha256
slice so each token keeps its own bucket without a DB hit.
* All six existing tests migrated from jwt.sign({scope:'api_token'})
to generateApiToken(); new format-only test suite covering prefix,
length, alphabet, checksum reject paths, and a 10k-iteration
collision/integrity loop.
* Docs (features/api-tokens.mdx, api-reference/overview.mdx) describe
the shape and drop the obsolete JWT-ceiling note.
* fix(api-tokens): clear CI lint and CodeQL false positives
* Drop unused TEST_USERNAME import in remote-console-session.test.ts;
the migration to generateApiToken() left it orphaned.
* Add a CodeQL barrier model so `generateApiToken`'s ReturnValue does
not flow into the `insufficient-password-hash` query. The function
emits 256-bit CSPRNG opaque keys; sha256 of the raw token is the
correct construction for high-entropy API tokens (bcrypt-class
hashes target low-entropy human passwords). CodeQL's name heuristic
was treating "Token" as a password source and flagging the standard
sha256 wrapping at all 9 call sites.
* ci(codeql): exclude js/insufficient-password-hash for token paths
The previous barrierModel data extension was a no-op for this rule: the
js/insufficient-password-hash query identifies its "password" sources via
SensitiveExpr's name heuristic ("token", "secret", "key" substrings),
which is upstream of the taint-tracking layer where barrierModel applies.
Verified by post-push re-analysis: 9 alerts still open, all undismissed.
Replace the dead extension with a path-scoped query-filter in
codeql-config.yml so the rule no longer fires on apiTokenFormat,
apiTokens, and the test directory. Real user-password hashing code
elsewhere in the repo (auth, users, setup routes) remains analyzed.
The 9 existing alerts on PR #1062 are dismissed via API as false
positives with a justification pointing at this config. Future runs
will not re-flag them because of the path filter.
115 lines
5.6 KiB
Plaintext
115 lines
5.6 KiB
Plaintext
---
|
|
title: API Tokens
|
|
description: Generate scoped API tokens for CI/CD pipelines, scripts, and automation workflows with granular permission control.
|
|
---
|
|
|
|
<Note>
|
|
API Tokens require a Sencho **Admiral** license. Skipper and Community Edition do not include this feature.
|
|
</Note>
|
|
|
|
API tokens let you authenticate external tools (CI/CD pipelines, deployment scripts, monitoring integrations) without sharing user credentials. Each token is scoped to a specific permission level so you can follow the principle of least privilege.
|
|
|
|
## Permission scopes
|
|
|
|
Every token is created with one of three permission levels:
|
|
|
|
| Scope | Allowed actions |
|
|
|-------|----------------|
|
|
| **Read Only** | `GET` requests only: view stacks, containers, metrics, and settings |
|
|
| **Deploy Only** | Everything in Read Only, plus stack lifecycle operations: deploy, down, restart, stop, start, update |
|
|
| **Full Admin** | All read and write operations except the universal restrictions listed below |
|
|
|
|
Choose the narrowest scope that fits your use case. A CI pipeline that only deploys stacks should use **Deploy Only**, not Full Admin.
|
|
|
|
### Universal restrictions
|
|
|
|
Regardless of scope, **all** API tokens are blocked from:
|
|
|
|
| Category | Description |
|
|
|----------|-------------|
|
|
| **Password management** | Changing user passwords |
|
|
| **User management** | Creating, updating, deleting, or listing user accounts |
|
|
| **SSO configuration** | Viewing, creating, updating, deleting, or testing SSO providers |
|
|
| **Node management** | Adding, updating, or deleting remote nodes |
|
|
| **License management** | Activating or deactivating license keys |
|
|
| **Token management** | Creating, listing, or revoking API tokens |
|
|
| **Registry management** | Viewing, creating, updating, deleting, or testing registry credentials |
|
|
| **Console access** | Generating console session tokens for interactive terminals |
|
|
|
|
These restrictions ensure that API tokens cannot escalate privileges or modify the identity and infrastructure configuration of your Sencho instance. These operations require a human user session (browser login).
|
|
|
|
## Creating a token
|
|
|
|
1. Open **Settings Hub** and navigate to the **API Tokens** tab (visible to Admiral admins only).
|
|
2. Click **Create Token**.
|
|
3. Fill in the creation form:
|
|
- **Name**: A descriptive label (e.g., "GitHub Actions deploy")
|
|
- **Permission Scope**: Select Read Only, Deploy Only, or Full Admin
|
|
- **Expiration**: Choose 30 days, 60 days, 90 days, 1 year, or no expiration. Tokens without an expiration must be revoked manually.
|
|
4. Click **Create**. A green banner appears with the raw token value. Copy it immediately using the copy button.
|
|
|
|
<Note>
|
|
Each user can have up to **25 active API tokens**. Token names must be unique among your active tokens. If you need to reuse a name, revoke the existing token first.
|
|
</Note>
|
|
|
|
<Frame>
|
|
<img src="/images/api-tokens/api-tokens-overview.png" alt="API Tokens management view in Settings Hub showing the token list and create form" />
|
|
</Frame>
|
|
|
|
<Warning>
|
|
The token value is shown only at creation time. Sencho stores a SHA-256 hash of the token, not the token itself. If you lose it, revoke and create a new one.
|
|
</Warning>
|
|
|
|
## Managing tokens
|
|
|
|
The token list displays each active token as a card showing:
|
|
|
|
- **Name** and **scope badge** (color-coded: teal for Read Only, gray for Deploy Only, red for Full Admin)
|
|
- **Created date**
|
|
- **Last used** timestamp (relative, e.g., "3h ago"), or "Never" if unused
|
|
- **Expiration date**, if one was set. Expired tokens are marked in red.
|
|
|
|
## Using a token
|
|
|
|
Pass the token as a Bearer token in the `Authorization` header:
|
|
|
|
<CodeGroup>
|
|
```bash curl
|
|
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
|
https://your-sencho-instance/api/stacks
|
|
```
|
|
|
|
```yaml GitHub Actions
|
|
- name: Deploy stack
|
|
run: |
|
|
curl -X POST \
|
|
-H "Authorization: Bearer ${{ secrets.SENCHO_TOKEN }}" \
|
|
https://your-sencho-instance/api/stacks/my-app/deploy
|
|
```
|
|
</CodeGroup>
|
|
|
|
### Scope enforcement
|
|
|
|
If a token attempts an action outside its scope, Sencho returns a `403` response with a `SCOPE_DENIED` error code:
|
|
|
|
```json
|
|
{
|
|
"error": "API token scope \"read-only\" only allows GET requests.",
|
|
"code": "SCOPE_DENIED"
|
|
}
|
|
```
|
|
|
|
## Revoking a token
|
|
|
|
Click the trash icon next to any token in the API Tokens settings tab. A confirmation dialog warns that revoking the token will immediately invalidate it and break any pipelines or scripts using it. After confirmation, revocation is instant: any in-flight or future requests using the revoked token will receive a `401` response.
|
|
|
|
## Security model
|
|
|
|
- **Recognizable format**: Tokens are 56 characters and begin with the `sen_sk_` prefix, followed by 49 base62 (alphanumeric) characters. The last six characters are a checksum, so malformed or typoed values are rejected before any database lookup. The prefix makes Sencho tokens easy to spot in logs, code, and secret-scanning tools (GitHub, TruffleHog, GitGuardian).
|
|
- **Hashed storage**: Only a SHA-256 hash of the token is stored in the database. The raw token is never persisted.
|
|
- **Audit trail**: All actions performed via API tokens are recorded in the [Audit Log](/features/audit-log) under the creating user's username.
|
|
- **Optional expiry**: Tokens can be created with an expiration period (30 days, 60 days, 90 days, or 1 year). Tokens without an expiry must be revoked manually when no longer needed.
|
|
- **Per-user limits**: Each user can create up to 25 active tokens. Token names must be unique among active tokens for the same user.
|
|
- **Usage tracking**: Each token tracks when it was last used, visible in the token list.
|
|
- **Scope enforcement**: Permission checks happen at the middleware level before any route handler executes, ensuring consistent enforcement across all endpoints.
|