Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kory33/cf-worker-as-openssh-ca
Turning Cloudflare Workers into a Certificate Authority that verifies incoming requests and issues short-lived SSH certificates.
https://github.com/kory33/cf-worker-as-openssh-ca
Last synced: 12 days ago
JSON representation
Turning Cloudflare Workers into a Certificate Authority that verifies incoming requests and issues short-lived SSH certificates.
- Host: GitHub
- URL: https://github.com/kory33/cf-worker-as-openssh-ca
- Owner: kory33
- License: mit
- Created: 2023-03-02T19:11:11.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-10-24T14:02:16.000Z (18 days ago)
- Last Synced: 2024-10-25T17:11:19.991Z (17 days ago)
- Language: TypeScript
- Homepage:
- Size: 2.16 MB
- Stars: 9
- Watchers: 2
- Forks: 0
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# `cf-worker-as-openssh-ca`
Turning Cloudflare Workers into a OpenSSH Certificate Authority that verifies incoming requests and issues short-lived SSH certificates.
## How it works
A successfully deployed system should consist of two independent systems, namely the **Signer** and the **Authenticator**.
### Overview
The following sequence diagrams illustrate how the Signer, the Authenticator (whose implementation is set to `authenticator/remote-jwt`) and the external JWT issuer interact upon `GET /ca-public-key` and `POST /new-short-lived-certificate` requests.
#### `GET /ca-public-key`
```mermaid
sequenceDiagram
participant EC as End-Clientparticipant S as Signer
participant WKV as Workers KVEC->>+S: /GET ca-public-key
S->>+WKV: Fetch keypair
WKV->>-S: keypair
opt no keypair present
S-->>S: Generate keypair
S->>WKV: Write keypair
end
S->>-EC: public key (OpenSSH format)
```#### `POST /new-short-lived-certificate`
```mermaid
sequenceDiagram
participant EC as End-Client
participant JI as JWT Issuerparticipant S as Signer
(Worker)
participant A as Authenticator
(Worker)
participant WKV as Workers KVJI->>EC: Issue signed using
Issuer's private keyEC-)+S: /POST new-short-lived-certificate
Authorization: Bearer
S-)+A: Forward request
A-)+JI: "What keys do you use to sign your JWTs?"
JI-)-A:
A->>A: Validates against
A->>A: Generates principal list using 's claims
A-)-S: 200 OKalt empty
S-)EC: 401 Unauthorized
else nonempty
S-)+WKV: Fetch CA Keypair
WKV-)-S:
alt no CA keypair present
S-)EC: 400 Bad Request
else CA keypair present
S->>S: Generate
S->>S: Sign
using
with
S-)-EC: 200 OK
{ "privateKey": ,
"certificate": }
end
end
```### The Signer
The Signer receives all incoming requests from (potentially untrusted) end-clients. It has the following HTTP endpoints:
- `GET /ca-public-key`:
- retrieves an _OpenSSH CA keypair_ from the Workers KV
- if no keypair has been already stored in Workers KV, generates it on the fly
- returns the public key part in OpenSSH format (`"ssh-ed25519 AAAAC3N..."`)- `POST /new-short-lived-certificate`:
- authenticates the request by forwarding it to the Authenticator
- if the request cannot be authenticated, returns `401 Unauthorized`
- the authenticator returns a JSON Array in plaintext consisting of _Principal names_ to which the request qualifies
- if the OpenSSH CA keypair has not been generated yet, returns `400 Bad Request`
- generates a SSH key-pair
- signs the generated public key using the CA's private key, with a short validity duration (configurable, default is 60 seconds) and the _Principal names_ returned from the Authenticator
- returns a private key (in OpenSSH format) and a short-lived OpenSSH certificate signed using the OpenSSH CA keypair### The Authenticator
The Authenticator _authenticates_ the identity of the end-clients by examining requests forwarded from the Signer. This component also _authorizes_ the end-clients by declaring to which principles the end-clients are eligible.
The implementation of the Authenticator is pluggable, but it must satisfy the following HTTP interface:
- `GET /`:
- authenticates the request using the information present in the incoming request
- always returns a `200 OK` with the response body containing a JSON Array of strings (`["principal1", "principal2", ...]`) consisting of Principal names to which the request qualifies
- if the request cannot be authenticated or does not qualify any Principal name, an empty JSON Array (`[]`) must be returned.