@andrewheberle/serverless-ssh-ca 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -1
- package/dist/api/operations.d.ts +50 -0
- package/dist/api/operations.d.ts.map +1 -0
- package/dist/api/operations.js +264 -0
- package/dist/api/operations.js.map +1 -0
- package/dist/api/schema.d.ts +72 -0
- package/dist/api/schema.d.ts.map +1 -0
- package/dist/api/schema.js +114 -0
- package/dist/api/schema.js.map +1 -0
- package/dist/api/v3/index.d.ts.map +1 -1
- package/dist/api/v3/index.js +13 -202
- package/dist/api/v3/index.js.map +1 -1
- package/dist/api/v3/schema.d.ts +23 -68
- package/dist/api/v3/schema.d.ts.map +1 -1
- package/dist/api/v3/schema.js +18 -104
- package/dist/api/v3/schema.js.map +1 -1
- package/dist/api/v4/index.d.ts +8 -0
- package/dist/api/v4/index.d.ts.map +1 -0
- package/dist/api/v4/index.js +155 -0
- package/dist/api/v4/index.js.map +1 -0
- package/dist/api/v4/schema.d.ts +268 -0
- package/dist/api/v4/schema.d.ts.map +1 -0
- package/dist/api/v4/schema.js +186 -0
- package/dist/api/v4/schema.js.map +1 -0
- package/dist/certificate.d.ts +9 -6
- package/dist/certificate.d.ts.map +1 -1
- package/dist/certificate.js +55 -99
- package/dist/certificate.js.map +1 -1
- package/dist/db/index.d.ts +68 -9
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +165 -49
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations/0003_krl_revision.d.ts +3 -0
- package/dist/db/migrations/0003_krl_revision.d.ts.map +1 -0
- package/dist/db/migrations/0003_krl_revision.js +14 -0
- package/dist/db/migrations/0003_krl_revision.js.map +1 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +2 -0
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/types.d.ts +2 -0
- package/dist/db/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/krl.d.ts +3 -2
- package/dist/krl.d.ts.map +1 -1
- package/dist/krl.js +12 -165
- package/dist/krl.js.map +1 -1
- package/dist/node/cache.d.ts +21 -0
- package/dist/node/cache.d.ts.map +1 -0
- package/dist/node/cache.js +49 -0
- package/dist/node/cache.js.map +1 -0
- package/dist/node/env.d.ts +6 -3
- package/dist/node/env.d.ts.map +1 -1
- package/dist/node/env.js +4 -3
- package/dist/node/env.js.map +1 -1
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +1 -0
- package/dist/node/index.js.map +1 -1
- package/dist/proof.d.ts +36 -7
- package/dist/proof.d.ts.map +1 -1
- package/dist/proof.js +53 -21
- package/dist/proof.js.map +1 -1
- package/dist/router.d.ts +25 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +41 -2
- package/dist/router.js.map +1 -1
- package/dist/ssh/certificate.d.ts +93 -0
- package/dist/ssh/certificate.d.ts.map +1 -0
- package/dist/ssh/certificate.js +178 -0
- package/dist/ssh/certificate.js.map +1 -0
- package/dist/ssh/encoding.d.ts +41 -0
- package/dist/ssh/encoding.d.ts.map +1 -0
- package/dist/ssh/encoding.js +70 -0
- package/dist/ssh/encoding.js.map +1 -0
- package/dist/ssh/keys.d.ts +92 -0
- package/dist/ssh/keys.d.ts.map +1 -0
- package/dist/ssh/keys.js +277 -0
- package/dist/ssh/keys.js.map +1 -0
- package/dist/ssh/signer.d.ts +35 -0
- package/dist/ssh/signer.d.ts.map +1 -0
- package/dist/ssh/signer.js +99 -0
- package/dist/ssh/signer.js.map +1 -0
- package/dist/types.d.ts +64 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +107 -21
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +251 -65
- package/dist/utils.js.map +1 -1
- package/dist/verify.d.ts +17 -5
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +47 -9
- package/dist/verify.js.map +1 -1
- package/openapi.json +1942 -1
- package/package.json +4 -3
- package/src/api/operations.ts +314 -0
- package/src/api/schema.ts +146 -0
- package/src/api/v3/index.ts +21 -248
- package/src/api/v3/schema.ts +36 -128
- package/src/api/v4/index.ts +266 -0
- package/src/api/v4/schema.ts +241 -0
- package/src/certificate.ts +62 -123
- package/src/db/index.ts +196 -56
- package/src/db/migrations/0003_krl_revision.ts +15 -0
- package/src/db/migrations/index.ts +2 -0
- package/src/db/types.ts +2 -0
- package/src/index.ts +6 -4
- package/src/krl.ts +13 -197
- package/src/node/cache.ts +62 -0
- package/src/node/env.ts +6 -5
- package/src/node/index.ts +1 -0
- package/src/proof.ts +65 -31
- package/src/router.ts +67 -3
- package/src/ssh/certificate.ts +236 -0
- package/src/ssh/encoding.ts +81 -0
- package/src/ssh/keys.ts +327 -0
- package/src/ssh/signer.ts +129 -0
- package/src/types.ts +60 -20
- package/src/utils.ts +285 -81
- package/src/verify.ts +54 -14
package/dist/node/env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/node/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,CAAC,MAAM,KAAK,CAAA;AAGnB,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAA;AAErE,qEAAqE;AACrE,MAAM,QAAQ,GAAG,CAAC,IAA4B,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;IACtE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IACpB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;AAC/D,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,2CAA2C,CAAC,CAAC,CAAC,kDAAkD,EAAE,CAAC,CAAA;AAElI,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC,CAAA;AAEvF;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,wBAAwB,EAAE,CAAC,CAAC,KAAK,CAAC;QACjC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;QACrB,wDAAwD;QACxD,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,0DAA0D,CAAC;KAC5E,EAAE,EAAE,OAAO,EAAE,qDAAqD,EAAE,CAAC;IACtE,0BAA0B,EAAE,CAAC,CAAC,MAAM,EAAE;IACtC,wBAAwB,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACxD,4BAA4B,EAAE,OAAO,CAAC,QAAQ,EAAE;IAChD,kCAAkC,EAAE,OAAO,CAAC,QAAQ,EAAE;IACtD,0BAA0B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjD,6BAA6B,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC7D,mCAAmC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1D,kCAAkC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzD,SAAS,EAAE,QAAQ;IACnB,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC;IACtF,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/node/env.ts"],"names":[],"mappings":"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"}
|
package/dist/node/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* @packageDocumentation
|
|
18
18
|
*/
|
|
19
|
+
export { cacheInMemory } from "./cache.js";
|
|
19
20
|
export { bindingsFromEnv, EnvSchema, InvalidConfigurationError } from "./env.js";
|
|
20
21
|
export type { CaEnv } from "./env.js";
|
|
21
22
|
export { secretFromFile, secretFromString } from "./secret.js";
|
package/dist/node/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAChF,YAAY,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAChF,YAAY,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/node/index.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* @packageDocumentation
|
|
18
18
|
*/
|
|
19
|
+
export { cacheInMemory } from "./cache.js";
|
|
19
20
|
export { bindingsFromEnv, EnvSchema, InvalidConfigurationError } from "./env.js";
|
|
20
21
|
export { secretFromFile, secretFromString } from "./secret.js";
|
|
21
22
|
export { fromNodeSqlite } from "./sqlite.js";
|
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAEhF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAEhF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/proof.d.ts
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { PublicKey } from "./ssh/keys.js";
|
|
2
2
|
import { Sig } from "./sshsig/sig.js";
|
|
3
3
|
import { Logger } from "@andrewheberle/ts-slog";
|
|
4
4
|
export declare const Namespace = "proof-of-possession@com.github.serverless-ssh-ca.andrewheberle";
|
|
5
|
+
/**
|
|
6
|
+
* The namespace used for proofs of possession that are bound to a request,
|
|
7
|
+
* kept separate from {@link Namespace} so a proof made for one form can never
|
|
8
|
+
* be accepted as the other.
|
|
9
|
+
*/
|
|
10
|
+
export declare const RequestNamespace = "request-proof-of-possession@com.github.serverless-ssh-ca.andrewheberle";
|
|
11
|
+
/**
|
|
12
|
+
* Returns the request a proof of possession is bound to, which is appended to
|
|
13
|
+
* the signed data as `${timestamp}.${fingerprint}.${binding}`.
|
|
14
|
+
*
|
|
15
|
+
* @param method The HTTP method of the request, for example "DELETE"
|
|
16
|
+
* @param path The path of the request with any parameters filled in, for example "/api/v4/user/certificates/123"
|
|
17
|
+
* @returns The request binding, for example "DELETE /api/v4/user/certificates/123"
|
|
18
|
+
*/
|
|
19
|
+
export declare const requestBinding: (method: string, path: string) => string;
|
|
5
20
|
export declare class PossessionParseError extends Error {
|
|
6
21
|
constructor(message: string, cause?: unknown);
|
|
7
22
|
}
|
|
@@ -21,9 +36,10 @@ export declare class PossessionParseError extends Error {
|
|
|
21
36
|
*/
|
|
22
37
|
export declare class ProofOfPossession {
|
|
23
38
|
readonly timestamp: number;
|
|
24
|
-
|
|
39
|
+
/** The SHA256 fingerprint of the key, for example "SHA256:79mq0wtpQMTvS4+Of8VzLN0qmYWNUyTXYmqKwhEgSLs" */
|
|
40
|
+
readonly fingerprint: string;
|
|
25
41
|
readonly signature: Sig;
|
|
26
|
-
readonly signaturePubkey:
|
|
42
|
+
readonly signaturePubkey: PublicKey;
|
|
27
43
|
readonly logger: Logger;
|
|
28
44
|
private readonly data;
|
|
29
45
|
constructor(proof: string, options?: {
|
|
@@ -36,19 +52,32 @@ export declare class ProofOfPossession {
|
|
|
36
52
|
* @returns true if signature is valid, false otherwise
|
|
37
53
|
*/
|
|
38
54
|
verify(namespace?: string): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Verifies the proof was made for a specific request, so it cannot be
|
|
57
|
+
* replayed against another endpoint or with other path parameters (such
|
|
58
|
+
* as a different certificate serial).
|
|
59
|
+
*
|
|
60
|
+
* The signed data is `${timestamp}.${fingerprint}.${METHOD} ${path}` using
|
|
61
|
+
* the {@link RequestNamespace} namespace.
|
|
62
|
+
*
|
|
63
|
+
* @param method The HTTP method of the request
|
|
64
|
+
* @param path The path of the request with any parameters filled in
|
|
65
|
+
* @returns true if signature is valid for the request, false otherwise
|
|
66
|
+
*/
|
|
67
|
+
verifyRequest(method: string, path: string): Promise<boolean>;
|
|
39
68
|
/**
|
|
40
69
|
*
|
|
41
70
|
* @param keys array of keys to verify finterprint against
|
|
42
71
|
* @returns true if the proof of possession fingerprint matches any of the provided keys, false otherwise
|
|
43
72
|
*/
|
|
44
|
-
matches(key:
|
|
45
|
-
matches(...keys:
|
|
73
|
+
matches(key: PublicKey): boolean;
|
|
74
|
+
matches(...keys: PublicKey[]): boolean;
|
|
46
75
|
}
|
|
47
76
|
export declare class PossessionMatchError extends Error {
|
|
48
77
|
constructor(message: string, cause?: unknown);
|
|
49
78
|
}
|
|
50
79
|
export declare class RenewalProofOfPossession extends ProofOfPossession {
|
|
51
|
-
matches(key:
|
|
52
|
-
matches(...keys:
|
|
80
|
+
matches(key: PublicKey): never;
|
|
81
|
+
matches(...keys: PublicKey[]): boolean;
|
|
53
82
|
}
|
|
54
83
|
//# sourceMappingURL=proof.d.ts.map
|
package/dist/proof.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,SAAS,EAAE,MAAM,eAAe,CAAA;AAGxD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAAS,MAAM,EAAY,MAAM,wBAAwB,CAAA;AAKhE,eAAO,MAAM,SAAS,mEAAmE,CAAA;AAEzF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,2EAA2E,CAAA;AAExG;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,WAAY,MAAM,QAAQ,MAAM,KAAG,MAE7D,CAAA;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAO3C;CACD;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAiB;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,0GAA0G;IAC1G,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAA;IACvB,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAQ;IAE9B,YAAY,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAiEpF;IAED;;;OAGG;IACG,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEjD;IAED;;;;;;;;;;;OAWG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElE;IAED;;;;OAIG;IACH,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAA;IAChC,OAAO,CAAC,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;CAuBtC;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAO3C;CACD;AAED,qBAAa,wBAAyB,SAAQ,iBAAiB;IACrD,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,KAAK,CAAA;IAC9B,OAAO,CAAC,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;CAwB/C"}
|
package/dist/proof.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import { ms } from "itty-time";
|
|
2
|
-
import
|
|
2
|
+
import { KeyParseError, PublicKey } from "./ssh/keys.js";
|
|
3
3
|
import { verify } from "./sshsig/index.js";
|
|
4
4
|
import { parse } from "./sshsig/sig_parser.js";
|
|
5
5
|
import { group, Logger, LogLevel } from "@andrewheberle/ts-slog";
|
|
6
|
-
//
|
|
7
|
-
const
|
|
6
|
+
// a SHA256 fingerprint as shown by ssh-keygen -l, which is unpadded base64
|
|
7
|
+
const FingerprintFormat = /^SHA256:[A-Za-z0-9+/]{43}$/;
|
|
8
8
|
export const Namespace = "proof-of-possession@com.github.serverless-ssh-ca.andrewheberle";
|
|
9
|
+
/**
|
|
10
|
+
* The namespace used for proofs of possession that are bound to a request,
|
|
11
|
+
* kept separate from {@link Namespace} so a proof made for one form can never
|
|
12
|
+
* be accepted as the other.
|
|
13
|
+
*/
|
|
14
|
+
export const RequestNamespace = "request-proof-of-possession@com.github.serverless-ssh-ca.andrewheberle";
|
|
15
|
+
/**
|
|
16
|
+
* Returns the request a proof of possession is bound to, which is appended to
|
|
17
|
+
* the signed data as `${timestamp}.${fingerprint}.${binding}`.
|
|
18
|
+
*
|
|
19
|
+
* @param method The HTTP method of the request, for example "DELETE"
|
|
20
|
+
* @param path The path of the request with any parameters filled in, for example "/api/v4/user/certificates/123"
|
|
21
|
+
* @returns The request binding, for example "DELETE /api/v4/user/certificates/123"
|
|
22
|
+
*/
|
|
23
|
+
export const requestBinding = (method, path) => {
|
|
24
|
+
return `${method.toUpperCase()} ${path}`;
|
|
25
|
+
};
|
|
9
26
|
export class PossessionParseError extends Error {
|
|
10
27
|
constructor(message, cause) {
|
|
11
28
|
super(message);
|
|
@@ -31,6 +48,7 @@ export class PossessionParseError extends Error {
|
|
|
31
48
|
*/
|
|
32
49
|
export class ProofOfPossession {
|
|
33
50
|
timestamp;
|
|
51
|
+
/** The SHA256 fingerprint of the key, for example "SHA256:79mq0wtpQMTvS4+Of8VzLN0qmYWNUyTXYmqKwhEgSLs" */
|
|
34
52
|
fingerprint;
|
|
35
53
|
signature;
|
|
36
54
|
signaturePubkey;
|
|
@@ -58,10 +76,9 @@ export class ProofOfPossession {
|
|
|
58
76
|
throw new PossessionParseError("timestamp was from the future");
|
|
59
77
|
}
|
|
60
78
|
try {
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
throw new PossessionParseError("fingerprint did not parse");
|
|
79
|
+
// check fingerprint
|
|
80
|
+
if (!FingerprintFormat.test(fingerprintString)) {
|
|
81
|
+
throw new PossessionParseError("proof of possession fingerprint was an invalid format");
|
|
65
82
|
}
|
|
66
83
|
// convert siganture from base64
|
|
67
84
|
const signature = Buffer.from(signatureBase64, "base64").toString();
|
|
@@ -73,19 +90,19 @@ export class ProofOfPossession {
|
|
|
73
90
|
catch (err) {
|
|
74
91
|
throw new PossessionParseError("proof of possession signature could not be parsed", err);
|
|
75
92
|
}
|
|
76
|
-
this.fingerprint =
|
|
93
|
+
this.fingerprint = fingerprintString;
|
|
77
94
|
this.data = `${timestamp}.${fingerprintString}`;
|
|
78
|
-
this.signaturePubkey =
|
|
95
|
+
this.signaturePubkey = PublicKey.parse(this.signature.publickey.toString());
|
|
79
96
|
// ensure the provided fingerprint matches the signature public key
|
|
80
|
-
if (
|
|
97
|
+
if (this.fingerprint !== this.signaturePubkey.fingerprint()) {
|
|
81
98
|
throw new PossessionParseError("proof of possession fingerprint does not match signature public key");
|
|
82
99
|
}
|
|
83
|
-
this.logger.debug("constructor completed", ...group("this", "timestamp", this.timestamp, "fingerprint", this.fingerprint
|
|
100
|
+
this.logger.debug("constructor completed", ...group("this", "timestamp", this.timestamp, "fingerprint", this.fingerprint, "signaturePubkey.fingerprint()", this.signaturePubkey.fingerprint()));
|
|
84
101
|
}
|
|
85
102
|
catch (err) {
|
|
86
103
|
switch (true) {
|
|
87
|
-
case (err instanceof
|
|
88
|
-
throw new PossessionParseError(
|
|
104
|
+
case (err instanceof KeyParseError):
|
|
105
|
+
throw new PossessionParseError(`proof of possession signature key could not be used: ${err.message}`, err);
|
|
89
106
|
default:
|
|
90
107
|
throw err;
|
|
91
108
|
}
|
|
@@ -98,17 +115,32 @@ export class ProofOfPossession {
|
|
|
98
115
|
async verify(namespace) {
|
|
99
116
|
return await verify(this.signature, this.data, { namespace: namespace || Namespace });
|
|
100
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Verifies the proof was made for a specific request, so it cannot be
|
|
120
|
+
* replayed against another endpoint or with other path parameters (such
|
|
121
|
+
* as a different certificate serial).
|
|
122
|
+
*
|
|
123
|
+
* The signed data is `${timestamp}.${fingerprint}.${METHOD} ${path}` using
|
|
124
|
+
* the {@link RequestNamespace} namespace.
|
|
125
|
+
*
|
|
126
|
+
* @param method The HTTP method of the request
|
|
127
|
+
* @param path The path of the request with any parameters filled in
|
|
128
|
+
* @returns true if signature is valid for the request, false otherwise
|
|
129
|
+
*/
|
|
130
|
+
async verifyRequest(method, path) {
|
|
131
|
+
return await verify(this.signature, `${this.data}.${requestBinding(method, path)}`, { namespace: RequestNamespace });
|
|
132
|
+
}
|
|
101
133
|
matches(...keys) {
|
|
102
134
|
this.logger.debug("matches() called", "in", "ProofOfPossession", "keys", keys.length);
|
|
103
135
|
try {
|
|
104
136
|
for (const key of keys) {
|
|
105
137
|
// confirm proof of possession fingerprint matches keys
|
|
106
|
-
this.logger.debug("
|
|
107
|
-
if (
|
|
138
|
+
this.logger.debug("comparing fingerprint", "in", "ProofOfPossession", "fingerprint", this.fingerprint, "key", key.toString());
|
|
139
|
+
if (this.fingerprint !== key.fingerprint())
|
|
108
140
|
return false;
|
|
109
141
|
// also confirm key used to sign proof of possession matches
|
|
110
|
-
this.logger.debug("
|
|
111
|
-
if (!this.signaturePubkey.
|
|
142
|
+
this.logger.debug("comparing signature public key", "in", "ProofOfPossession", "signaturePubkey", this.signaturePubkey.fingerprint(), "key", key.toString());
|
|
143
|
+
if (!this.signaturePubkey.equals(key))
|
|
112
144
|
return false;
|
|
113
145
|
}
|
|
114
146
|
this.logger.debug("matches() completed", "in", "ProofOfPossession", "keys", keys.length);
|
|
@@ -137,11 +169,11 @@ export class RenewalProofOfPossession extends ProofOfPossession {
|
|
|
137
169
|
}
|
|
138
170
|
try {
|
|
139
171
|
for (const key of keys) {
|
|
140
|
-
this.logger.debug("
|
|
141
|
-
if (
|
|
172
|
+
this.logger.debug("comparing fingerprint", "in", "RenewalProofOfPossession", "fingerprint", this.fingerprint, "key", key.toString());
|
|
173
|
+
if (this.fingerprint !== key.fingerprint())
|
|
142
174
|
return false;
|
|
143
|
-
this.logger.debug("
|
|
144
|
-
if (!this.signaturePubkey.
|
|
175
|
+
this.logger.debug("comparing signature public key", "in", "RenewalProofOfPossession", "signaturePubkey", this.signaturePubkey.fingerprint(), "key", key.toString());
|
|
176
|
+
if (!this.signaturePubkey.equals(key))
|
|
145
177
|
return false;
|
|
146
178
|
}
|
|
147
179
|
this.logger.debug("matches() completed", "in", "RenewalProofOfPossession", "keys", keys.length);
|
package/dist/proof.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAA;AAC9B,OAAO,
|
|
1
|
+
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"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"}
|
package/dist/router.d.ts
CHANGED
|
@@ -3,5 +3,29 @@ import type { SshCaBindings } from "./types.js";
|
|
|
3
3
|
export type AppContext = Context<{
|
|
4
4
|
Bindings: SshCaBindings;
|
|
5
5
|
}>;
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Returns the key a request is rate limited by, or undefined if the request
|
|
8
|
+
* should not be rate limited.
|
|
9
|
+
*/
|
|
10
|
+
export type RateLimitKey = (req: Request) => string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Options for the CA that are not part of its bindings.
|
|
13
|
+
*/
|
|
14
|
+
export interface SshCaOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Returns the key requests are rate limited by when `RATE_LIMITER` is
|
|
17
|
+
* set. Defaults to the client IP address in the `CF-Connecting-IP` header,
|
|
18
|
+
* which is set by Cloudflare, so other runtimes should provide one that
|
|
19
|
+
* suits their limiter, for example using a header set by a reverse proxy.
|
|
20
|
+
*/
|
|
21
|
+
rateLimitKey?: RateLimitKey;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Returns the client IP address set by Cloudflare in the `CF-Connecting-IP`
|
|
25
|
+
* header, which is the default key requests are rate limited by.
|
|
26
|
+
*/
|
|
27
|
+
export declare const cloudflareClientIp: RateLimitKey;
|
|
28
|
+
export declare const createApp: (env: SshCaBindings, options?: SshCaOptions) => import("chanfana").HonoOpenAPIRouterType<{
|
|
29
|
+
Bindings: SshCaBindings;
|
|
30
|
+
}, import("hono/types").BlankSchema, "/">;
|
|
7
31
|
//# sourceMappingURL=router.d.ts.map
|
package/dist/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,MAAM,CAAA;AAMzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,aAAa,CAAA;CAAE,CAAC,CAAA;AAE7D;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAA;AAE/D;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;CAC9B;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,YAAwE,CAAA;AAKzG,eAAO,MAAM,SAAS,QAAS,aAAa,YAAW,YAAY;cAC7B,aAAa;yCAgElD,CAAA"}
|
package/dist/router.js
CHANGED
|
@@ -1,10 +1,48 @@
|
|
|
1
1
|
import { fromHono } from "chanfana";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { createApi as apiv3 } from "./api/v3/index.js";
|
|
4
|
+
import { BasePath as v4BasePath, createApi as apiv4 } from "./api/v4/index.js";
|
|
4
5
|
import { HTTPException } from "hono/http-exception";
|
|
5
6
|
import { logger } from "./logger.js";
|
|
6
|
-
|
|
7
|
+
import { TooManyRequestsException } from "chanfana";
|
|
8
|
+
/**
|
|
9
|
+
* Returns the client IP address set by Cloudflare in the `CF-Connecting-IP`
|
|
10
|
+
* header, which is the default key requests are rate limited by.
|
|
11
|
+
*/
|
|
12
|
+
export const cloudflareClientIp = (req) => req.headers.get("CF-Connecting-IP") ?? undefined;
|
|
13
|
+
// requests that only read from the CA, which are not rate limited
|
|
14
|
+
const ReadMethods = new Set(["GET", "HEAD", "OPTIONS"]);
|
|
15
|
+
export const createApp = (env, options = {}) => {
|
|
7
16
|
const hono = new Hono();
|
|
17
|
+
const rateLimitKey = options.rateLimitKey ?? cloudflareClientIp;
|
|
18
|
+
// limit requests to issue, renew and revoke certificates
|
|
19
|
+
hono.use("/api/*", async (c, next) => {
|
|
20
|
+
const limiter = c.env.RATE_LIMITER;
|
|
21
|
+
if (limiter === undefined || ReadMethods.has(c.req.method)) {
|
|
22
|
+
return await next();
|
|
23
|
+
}
|
|
24
|
+
const l = logger(env).with("in", "rateLimit");
|
|
25
|
+
const key = rateLimitKey(c.req.raw);
|
|
26
|
+
if (key === undefined) {
|
|
27
|
+
l.debug("no rate limit key for request");
|
|
28
|
+
return await next();
|
|
29
|
+
}
|
|
30
|
+
let success;
|
|
31
|
+
try {
|
|
32
|
+
success = (await limiter.limit({ key })).success;
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
// a failing limiter must not stop certificates being issued or revoked
|
|
36
|
+
l.error("rate limiter failed", "error", err);
|
|
37
|
+
return await next();
|
|
38
|
+
}
|
|
39
|
+
if (!success) {
|
|
40
|
+
l.warn("request was rate limited", "key", key, "method", c.req.method, "path", c.req.path);
|
|
41
|
+
const err = new TooManyRequestsException();
|
|
42
|
+
return c.json({ success: false, errors: err.buildResponse(), result: {} }, 429);
|
|
43
|
+
}
|
|
44
|
+
return await next();
|
|
45
|
+
});
|
|
8
46
|
// add error handling
|
|
9
47
|
hono.onError((err, c) => {
|
|
10
48
|
if (err instanceof HTTPException) {
|
|
@@ -17,7 +55,7 @@ export const createApp = (env) => {
|
|
|
17
55
|
schema: {
|
|
18
56
|
info: {
|
|
19
57
|
title: "Serverless SSH CA API",
|
|
20
|
-
version: "
|
|
58
|
+
version: "4.0.0"
|
|
21
59
|
},
|
|
22
60
|
}
|
|
23
61
|
});
|
|
@@ -27,6 +65,7 @@ export const createApp = (env) => {
|
|
|
27
65
|
bearerFormat: 'JWT',
|
|
28
66
|
});
|
|
29
67
|
app.route("/api/v3", apiv3(env));
|
|
68
|
+
app.route(v4BasePath, apiv4(env));
|
|
30
69
|
return app;
|
|
31
70
|
};
|
|
32
71
|
//# sourceMappingURL=router.js.map
|
package/dist/router.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,IAAI,EAAgB,MAAM,MAAM,CAAA;AACzC,OAAO,EAAE,SAAS,IAAI,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { PublicKey } from "./keys.js";
|
|
2
|
+
import { type Signer } from "./signer.js";
|
|
3
|
+
/**
|
|
4
|
+
* Encoding, decoding and signing of OpenSSH certificates as described in
|
|
5
|
+
* PROTOCOL.certkeys from OpenSSH
|
|
6
|
+
*/
|
|
7
|
+
/** Whether a certificate is for a user or a host */
|
|
8
|
+
export type CertificateKind = "user" | "host";
|
|
9
|
+
/** The fields of a certificate that are set by the CA */
|
|
10
|
+
export type CertificateFields = {
|
|
11
|
+
/** Whether the certificate is for a user or a host */
|
|
12
|
+
kind: CertificateKind;
|
|
13
|
+
/** The public key being certified */
|
|
14
|
+
publicKey: PublicKey;
|
|
15
|
+
/** The serial number of the certificate */
|
|
16
|
+
serial: bigint;
|
|
17
|
+
/** The key ID of the certificate, which is logged by sshd when it is used */
|
|
18
|
+
keyId: string;
|
|
19
|
+
/** The users or hosts the certificate is valid for */
|
|
20
|
+
principals: string[];
|
|
21
|
+
/** The time from which the certificate is valid, to the second */
|
|
22
|
+
validAfter: Date;
|
|
23
|
+
/** The time until which the certificate is valid, to the second */
|
|
24
|
+
validBefore: Date;
|
|
25
|
+
/** The names of the extensions to include, which have no data */
|
|
26
|
+
extensions: string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Thrown when a certificate cannot be parsed
|
|
30
|
+
*/
|
|
31
|
+
export declare class CertificateParseError extends Error {
|
|
32
|
+
constructor(message: string, cause?: unknown);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A signed OpenSSH certificate
|
|
36
|
+
*/
|
|
37
|
+
export declare class SshCertificate {
|
|
38
|
+
/** The wire encoding of the certificate */
|
|
39
|
+
readonly bytes: Uint8Array;
|
|
40
|
+
/** The certificate type, for example "ssh-ed25519-cert-v01@openssh.com" */
|
|
41
|
+
readonly type: string;
|
|
42
|
+
readonly kind: CertificateKind;
|
|
43
|
+
/** The public key being certified */
|
|
44
|
+
readonly publicKey: PublicKey;
|
|
45
|
+
readonly serial: bigint;
|
|
46
|
+
readonly keyId: string;
|
|
47
|
+
readonly principals: string[];
|
|
48
|
+
readonly validAfter: Date;
|
|
49
|
+
readonly validBefore: Date;
|
|
50
|
+
/** The names of the extensions on the certificate */
|
|
51
|
+
readonly extensions: string[];
|
|
52
|
+
/** The wire encoding of the public key of the CA that signed the certificate */
|
|
53
|
+
readonly signatureKey: Uint8Array;
|
|
54
|
+
/** The signature of the CA in the SSH wire format */
|
|
55
|
+
readonly signature: Uint8Array;
|
|
56
|
+
/**
|
|
57
|
+
* Decodes a certificate from its wire encoding. This does not verify the
|
|
58
|
+
* signature on the certificate.
|
|
59
|
+
*
|
|
60
|
+
* @param bytes The wire encoding of the certificate
|
|
61
|
+
* @throws {@link CertificateParseError} when the certificate cannot be decoded or its type is not supported
|
|
62
|
+
*/
|
|
63
|
+
constructor(bytes: Uint8Array);
|
|
64
|
+
/**
|
|
65
|
+
* Parses a certificate in OpenSSH format, `${type} ${base64}[ ${comment}]`,
|
|
66
|
+
* as found in `-cert.pub` files. This does not verify the signature on the
|
|
67
|
+
* certificate.
|
|
68
|
+
*
|
|
69
|
+
* @param text The certificate
|
|
70
|
+
* @throws {@link CertificateParseError} when the certificate cannot be parsed or its type is not supported
|
|
71
|
+
*/
|
|
72
|
+
static parse(text: string): SshCertificate;
|
|
73
|
+
/**
|
|
74
|
+
* Returns whether the certificate was signed by a CA key
|
|
75
|
+
*
|
|
76
|
+
* @param ca The public key of the CA
|
|
77
|
+
*/
|
|
78
|
+
isSignedBy(ca: PublicKey): Promise<boolean>;
|
|
79
|
+
/**
|
|
80
|
+
* Returns the certificate in OpenSSH format, as used for `-cert.pub` files
|
|
81
|
+
*/
|
|
82
|
+
toString(): string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates and signs an OpenSSH certificate
|
|
86
|
+
*
|
|
87
|
+
* @param fields The fields of the certificate
|
|
88
|
+
* @param signer The CA key to sign the certificate with
|
|
89
|
+
* @returns The signed certificate
|
|
90
|
+
* @throws Error when the type of the public key being certified is not supported
|
|
91
|
+
*/
|
|
92
|
+
export declare const signCertificate: (fields: CertificateFields, signer: Signer) => Promise<SshCertificate>;
|
|
93
|
+
//# sourceMappingURL=certificate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"certificate.d.ts","sourceRoot":"","sources":["../../src/ssh/certificate.ts"],"names":[],"mappings":"AAUA,OAAO,EAAiB,SAAS,EAAE,MAAM,WAAW,CAAA;AACpD,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,aAAa,CAAA;AAEjD;;;GAGG;AAEH,oDAAoD;AACpD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAA;AAO7C,yDAAyD;AACzD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,sDAAsD;IACtD,IAAI,EAAE,eAAe,CAAA;IACrB,qCAAqC;IACrC,SAAS,EAAE,SAAS,CAAA;IACpB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,kEAAkE;IAClE,UAAU,EAAE,IAAI,CAAA;IAChB,mEAAmE;IACnE,WAAW,EAAE,IAAI,CAAA;IACjB,iEAAiE;IACjE,UAAU,EAAE,MAAM,EAAE,CAAA;CACpB,CAAA;AA+BD;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC/C,YAAY,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAM3C;CACD;AAED;;GAEG;AACH,qBAAa,cAAc;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAA;IAC7B,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAA;IACzB,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAA;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAA;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAA;IACjC,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;IAE9B;;;;;;OAMG;IACH,YAAY,KAAK,EAAE,UAAU,EA+B5B;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAmBzC;IAED;;;;OAIG;IACG,UAAU,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAShD;IAED;;OAEG;IACH,QAAQ,IAAI,MAAM,CAEjB;CACD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,WAAkB,iBAAiB,UAAU,MAAM,KAAG,OAAO,CAAC,cAAc,CAuBvG,CAAA"}
|