sliftutils 1.7.131 → 1.7.133

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.
Files changed (38) hide show
  1. package/index.d.ts +6 -1
  2. package/misc/dist/ownIPs.ts.cache +2 -2
  3. package/misc/https/certs.d.ts +2 -1
  4. package/misc/https/certs.ts +9 -8
  5. package/misc/https/persistentLocalStorage.d.ts +4 -0
  6. package/misc/https/persistentLocalStorage.ts +7 -1
  7. package/package.json +3 -7
  8. package/security/authorizedKeys/daemon/dist/changes.ts.cache +2 -2
  9. package/security/authorizedKeys/daemon/dist/git.ts.cache +2 -2
  10. package/security/authorizedKeys/daemon/dist/notify.ts.cache +2 -2
  11. package/security/authorizedKeys/daemon/dist/paths.ts.cache +2 -2
  12. package/security/authorizedKeys/daemon/dist/repoFiles.ts.cache +2 -2
  13. package/security/authorizedKeys/daemon/dist/revocation.ts.cache +0 -0
  14. package/security/authorizedKeys/daemon/dist/sessions.ts.cache +2 -2
  15. package/security/authorizedKeys/daemon/dist/state.ts.cache +2 -2
  16. package/security/authorizedKeys/daemon/dist/trust.ts.cache +2 -2
  17. package/security/authorizedKeys/dist/authorizedKeys.ts.cache +2 -2
  18. package/security/authorizedKeys/dist/keysRepo.ts.cache +2 -2
  19. package/security/authorizedKeys/dist/revokeSource.ts.cache +2 -2
  20. package/security/authorizedKeys/dist/sources.ts.cache +2 -2
  21. package/security/helpers/dist/paths.ts.cache +2 -2
  22. package/security/helpers/dist/spawn.ts.cache +2 -2
  23. package/security/keys/dist/deriveKey.ts.cache +2 -2
  24. package/security/keys/dist/sshKeyFile.ts.cache +2 -2
  25. package/security/keys/sshKeyFile.ts +3 -1
  26. package/security/machines/addMachine.ts +38 -34
  27. package/security/machines/dist/identity.ts.cache +2 -2
  28. package/security/machines/dist/machines.ts.cache +2 -2
  29. package/security/machines/identity2.ts +39 -0
  30. package/security/machines/machines.ts +6 -2
  31. package/security/notifications/dist/discord.ts.cache +2 -2
  32. package/security/signedFiles/dist/manifest.ts.cache +2 -2
  33. package/storage/backblaze.ts +11 -0
  34. package/bin/machineclient.js +0 -9
  35. package/bin/machineserver.js +0 -10
  36. package/security/machines/identity.ts +0 -199
  37. package/security/machines/trustClient.ts +0 -51
  38. package/security/machines/trustServer.ts +0 -87
@@ -1,4 +1,4 @@
1
- "use strict"; var console = (function shimConsole() { let baseConsole = globalThis.console; if (typeof window !== "undefined") return baseConsole; let name = __filename.replaceAll("\\", "/").split("/").pop(); let dirName = __dirname.replaceAll("\\", "/").split("/").pop(); let context = { __FILE__: __filename, __DIR__: __dirname, __NAME__: name, __DIRNAME__: dirName }; let newConsole = { ...baseConsole }; for (let shim of ["log", "info", "warn", "error"]) { newConsole[shim] = (...args) => { args.push(context); baseConsole[shim](...args); }; } newConsole.__context = context; return newConsole; })(); "use strict";
1
+ "use strict";
2
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
4
4
  };
@@ -82,4 +82,4 @@ function formatManifest(manifest) {
82
82
  }
83
83
  exports.formatManifest = formatManifest;
84
84
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFuaWZlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJtYW5pZmVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvREFBNEI7QUFDNUIsMkRBQTZCO0FBQzdCLGdEQUF3QjtBQUN4Qiw0Q0FBZ0Q7QUFFaEQsa0dBQWtHO0FBQ2xHLGlHQUFpRztBQUNqRyxrR0FBa0c7QUFDbEcsdUJBQXVCO0FBRVYsUUFBQSxhQUFhLEdBQUcsa0JBQWtCLENBQUM7QUFDbkMsUUFBQSxjQUFjLEdBQUcsc0JBQXNCLENBQUM7QUFDckQsb0dBQW9HO0FBQ3ZGLFFBQUEsY0FBYyxHQUFHLFdBQVcsQ0FBQztBQUM3QixRQUFBLGdCQUFnQixHQUFHLENBQUMsQ0FBQztBQU9sQzs7Ozs7OzswRkFPMEY7QUFDbkYsS0FBSyxVQUFVLGFBQWEsQ0FBQyxRQUFnQjtJQUNoRCxJQUFJLE1BQU0sR0FBRyxNQUFNLElBQUEsb0JBQVksRUFBQztRQUM1QixPQUFPLEVBQUUsS0FBSztRQUNkLElBQUksRUFBRSxDQUFDLFVBQVUsRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLG9CQUFvQixDQUFDO1FBQ2hFLEdBQUcsRUFBRSxRQUFRO0tBQ2hCLENBQUMsQ0FBQztJQUNILElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN0QixNQUFNLElBQUksS0FBSyxDQUNYLGlDQUFpQyxRQUFRLHlCQUF5QixNQUFNLENBQUMsTUFBTSxJQUFJO2NBQ2pGLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUNoRCxDQUFDO0lBQ04sQ0FBQztJQUNELElBQUksTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQztTQUNqQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7U0FDeEIsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLElBQUksS0FBSyxxQkFBYSxJQUFJLElBQUksS0FBSyxzQkFBYyxDQUFDO1NBQ3pFLElBQUksRUFBRSxDQUFDO0lBRVosSUFBSSxPQUFPLEdBQWEsRUFBRSxDQUFDO0lBQzNCLEtBQUssSUFBSSxZQUFZLElBQUksTUFBTSxFQUFFLENBQUM7UUFDOUIsSUFBSSxDQUFDO1lBQ0QsTUFBTSxrQkFBRSxDQUFDLE1BQU0sQ0FBQyxjQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxZQUFZLENBQUMsQ0FBQyxDQUFDO1lBQ25ELE9BQU8sQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDL0IsQ0FBQztRQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDVCxPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsWUFBWSx3REFBd0QsQ0FBQyxDQUFDO1FBQ2pHLENBQUM7SUFDTCxDQUFDO0lBQ0QsT0FBTyxPQUFPLENBQUM7QUFDbkIsQ0FBQztBQTNCRCxzQ0EyQkM7QUFFRDs7OytCQUcrQjtBQUMvQixTQUFnQixnQkFBZ0IsQ0FBQyxRQUFnQjtJQUM3QyxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0FBQ3JGLENBQUM7QUFGRCw0Q0FFQztBQUVEOytGQUMrRjtBQUN4RixLQUFLLFVBQVUsVUFBVSxDQUFDLFFBQWdCO0lBQzdDLElBQUksUUFBUSxHQUFHLGdCQUFnQixDQUFDLE1BQU0sa0JBQUUsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztJQUM3RCxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLGdCQUFNLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztBQUN6RyxDQUFDO0FBSEQsZ0NBR0M7QUFFTSxLQUFLLFVBQVUsYUFBYSxDQUFDLFFBQWdCO0lBQ2hELElBQUksS0FBSyxHQUFzQixFQUFFLENBQUM7SUFDbEMsS0FBSyxJQUFJLFlBQVksSUFBSSxNQUFNLGFBQWEsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQ3JELElBQUksTUFBTSxHQUFHLE1BQU0sVUFBVSxDQUFDLGNBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7UUFDakUsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQ2pGLENBQUM7SUFDRCxPQUFPLEVBQUUsT0FBTyxFQUFFLHdCQUFnQixFQUFFLEtBQUssRUFBRSxDQUFDO0FBQ2hELENBQUM7QUFQRCxzQ0FPQztBQUVELG1HQUFtRztBQUNuRyxTQUFnQixjQUFjLENBQUMsUUFBa0I7SUFDN0MsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDO0FBQ3pELENBQUM7QUFGRCx3Q0FFQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjcnlwdG8gZnJvbSBcImNyeXB0b1wiO1xuaW1wb3J0IGZzIGZyb20gXCJmcy9wcm9taXNlc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IHNwYXduUHJvbWlzZSB9IGZyb20gXCIuLi9oZWxwZXJzL3NwYXduXCI7XG5cbi8vIFBPUlRFRCBDT0RFOiBzZWN1cml0eS9hdXRob3JpemVkS2V5cy9kYWVtb24vcG9ydHNlY3VyZURhZW1vbi5qcyBjb250YWlucyBhIHBsYWluIEpTIHBvcnQgb2YgdGhlXG4vLyB2ZXJpZnlpbmcgaGFsZiBvZiB0aGlzIGZpbGUsIHNvIGl0IGNhbiBjaGVjayBhIHNpZ25hdHVyZSB3aXRoIG5vIGRlcGVuZGVuY2llcy4gQm90aCBzaWRlcyBtdXN0XG4vLyBhZ3JlZSBvbiB0aGUgbWFuaWZlc3Qgc2hhcGUgYW5kIG9uIHdoaWNoIGZpbGVzIGl0IGNvdmVycyAtIGlmIHlvdSBjaGFuZ2Ugb25lLCBtYWtlIHRoZSBtYXRjaGluZ1xuLy8gY2hhbmdlIGluIHRoZSBvdGhlci5cblxuZXhwb3J0IGNvbnN0IE1BTklGRVNUX05BTUUgPSBcInNpZ25lZGZpbGVzLmpzb25cIjtcbmV4cG9ydCBjb25zdCBTSUdOQVRVUkVfTkFNRSA9IFwic2lnbmVkZmlsZXMuanNvbi5zaWdcIjtcbi8vIHNzaCBzaWduYXR1cmVzIGFyZSBuYW1lc3BhY2VkLCBzbyBhIHNpZ25hdHVyZSBtYWRlIGZvciBvbmUgcHVycG9zZSBjYW5ub3QgYmUgcmVwbGF5ZWQgYXMgYW5vdGhlci5cbmV4cG9ydCBjb25zdCBTSUdOX05BTUVTUEFDRSA9IFwic2lnbmZpbGVzXCI7XG5leHBvcnQgY29uc3QgTUFOSUZFU1RfVkVSU0lPTiA9IDE7XG5cbmV4cG9ydCB0eXBlIE1hbmlmZXN0ID0ge1xuICAgIHZlcnNpb246IG51bWJlcjtcbiAgICBmaWxlczogeyBwYXRoOiBzdHJpbmc7IHNpemU6IG51bWJlcjsgc2hhMjU2OiBzdHJpbmcgfVtdO1xufTtcblxuLyoqIEV2ZXJ5dGhpbmcgaW4gdGhlIHJlcG8gdGhhdCBpcyBub3QgaWdub3JlZDogd2hhdCBpcyBpbiB0aGUgaW5kZXgsIHBsdXMgd2hhdCBpcyBub3Qgc3RhZ2VkIHlldC5cbiAgICBHaXQgaXMgYXNrZWQgYmVjYXVzZSBnaXQgaXMgd2hhdCBrbm93cyBob3cgdG8gYXBwbHkgdGhlIGlnbm9yZSBydWxlcy5cblxuICAgIFdoYXQgY29tZXMgYmFjayBpcyBhIGhpbnQgdGhhdCBhIGZpbGUgbWlnaHQgYmUgdGhlcmUsIG5vdCB0aGF0IGl0IGlzLiBBIGZpbGUgZGVsZXRlZCBmcm9tIHRoZVxuICAgIHdvcmtpbmcgdHJlZSBzdGF5cyBpbiB0aGUgaW5kZXggdW50aWwgdGhhdCBkZWxldGlvbiBpcyBzdGFnZWQsIHNvIGdpdCBuYW1lcyBpdCBhbmQgdGhlcmUgaXNcbiAgICBub3RoaW5nIHRvIHJlYWQsIHdoaWNoIGlzIHdoeSBlYWNoIG9uZSBpcyBjaGVja2VkIGJlZm9yZSBpdCBnb2VzIGluLlxuXG4gICAgVGhlIG1hbmlmZXN0IGFuZCBpdHMgc2lnbmF0dXJlIGFyZSBsZWZ0IG91dCwgc2luY2UgdGhleSBjYW5ub3QgZGVzY3JpYmUgdGhlbXNlbHZlcy4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsaXN0UmVwb0ZpbGVzKHJlcG9QYXRoOiBzdHJpbmcpIHtcbiAgICBsZXQgcmVzdWx0ID0gYXdhaXQgc3Bhd25Qcm9taXNlKHtcbiAgICAgICAgY29tbWFuZDogXCJnaXRcIixcbiAgICAgICAgYXJnczogW1wibHMtZmlsZXNcIiwgXCItLWNhY2hlZFwiLCBcIi0tb3RoZXJzXCIsIFwiLS1leGNsdWRlLXN0YW5kYXJkXCJdLFxuICAgICAgICBjd2Q6IHJlcG9QYXRoLFxuICAgIH0pO1xuICAgIGlmIChyZXN1bHQuc3RhdHVzICE9PSAwKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBFeHBlY3RlZCB0byBsaXN0IHRoZSBmaWxlcyBpbiAke3JlcG9QYXRofSwgZ2l0IGxzLWZpbGVzIGV4aXRlZCAke3Jlc3VsdC5zdGF0dXN9LiBgXG4gICAgICAgICAgICArIGAkeyhyZXN1bHQuc3Rkb3V0ICsgcmVzdWx0LnN0ZGVycikudHJpbSgpfWBcbiAgICAgICAgKTtcbiAgICB9XG4gICAgbGV0IGxpc3RlZCA9IHJlc3VsdC5zdGRvdXQuc3BsaXQoXCJcXG5cIilcbiAgICAgICAgLm1hcChsaW5lID0+IGxpbmUudHJpbSgpKVxuICAgICAgICAuZmlsdGVyKGxpbmUgPT4gbGluZSAmJiBsaW5lICE9PSBNQU5JRkVTVF9OQU1FICYmIGxpbmUgIT09IFNJR05BVFVSRV9OQU1FKVxuICAgICAgICAuc29ydCgpO1xuXG4gICAgbGV0IHByZXNlbnQ6IHN0cmluZ1tdID0gW107XG4gICAgZm9yIChsZXQgcmVsYXRpdmVQYXRoIG9mIGxpc3RlZCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZnMuYWNjZXNzKHBhdGguam9pbihyZXBvUGF0aCwgcmVsYXRpdmVQYXRoKSk7XG4gICAgICAgICAgICBwcmVzZW50LnB1c2gocmVsYXRpdmVQYXRoKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYExlYXZpbmcgJHtyZWxhdGl2ZVBhdGh9IG91dCBvZiB0aGUgbWFuaWZlc3QsIGl0IGlzIGdvbmUgZnJvbSB0aGUgd29ya2luZyB0cmVlYCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHByZXNlbnQ7XG59XG5cbi8qKiBBIFdpbmRvd3MgY2hlY2tvdXQgdHVybnMgTEYgaW50byBDUkxGLCBzbyB0aGUgc2FtZSBjb21taXQgaGFzaGVzIGRpZmZlcmVudGx5IHRoZXJlIHRoYW4gaXRcbiAgICBkb2VzIG9uIHRoZSBtYWNoaW5lIHRoYXQgcHVsbHMgaXQuIE5vcm1hbGlzaW5nIGZpcnN0IG1ha2VzIHRoZSBkaWdlc3QgZGVzY3JpYmUgdGhlIGNvbnRlbnRcbiAgICByYXRoZXIgdGhhbiB3aGljaGV2ZXIgY2hlY2tvdXQgcHJvZHVjZWQgaXQuIGxhdGluMSByb3VuZCB0cmlwcyBldmVyeSBieXRlLCBzbyB0aGlzIGlzIHNhZmUgb25cbiAgICBmaWxlcyB0aGF0IGFyZSBub3QgdGV4dC4gKi9cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVDb250ZW50KGNvbnRlbnRzOiBCdWZmZXIpIHtcbiAgICByZXR1cm4gQnVmZmVyLmZyb20oY29udGVudHMudG9TdHJpbmcoXCJsYXRpbjFcIikucmVwbGFjZSgvXFxyXFxuL2csIFwiXFxuXCIpLCBcImxhdGluMVwiKTtcbn1cblxuLyoqIFRoZSBzaXplIGFuZCBoYXNoIG9mIGEgZmlsZSdzIG5vcm1hbGlzZWQgY29udGVudC4gVGhlIHNpemUgaXMgdGhlIG5vcm1hbGlzZWQgb25lIG9uIHB1cnBvc2UsXG4gICAgc28gaXQgYWdyZWVzIHdpdGggdGhlIGhhc2ggcmF0aGVyIHRoYW4gd2l0aCB3aGF0ZXZlciB0aGUgbG9jYWwgY2hlY2tvdXQgaGFwcGVucyB0byBob2xkLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGRpZ2VzdEZpbGUoZmlsZVBhdGg6IHN0cmluZykge1xuICAgIGxldCBjb250ZW50cyA9IG5vcm1hbGl6ZUNvbnRlbnQoYXdhaXQgZnMucmVhZEZpbGUoZmlsZVBhdGgpKTtcbiAgICByZXR1cm4geyBzaXplOiBjb250ZW50cy5sZW5ndGgsIHNoYTI1NjogY3J5cHRvLmNyZWF0ZUhhc2goXCJzaGEyNTZcIikudXBkYXRlKGNvbnRlbnRzKS5kaWdlc3QoXCJoZXhcIikgfTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJ1aWxkTWFuaWZlc3QocmVwb1BhdGg6IHN0cmluZykge1xuICAgIGxldCBmaWxlczogTWFuaWZlc3RbXCJmaWxlc1wiXSA9IFtdO1xuICAgIGZvciAobGV0IHJlbGF0aXZlUGF0aCBvZiBhd2FpdCBsaXN0UmVwb0ZpbGVzKHJlcG9QYXRoKSkge1xuICAgICAgICBsZXQgZGlnZXN0ID0gYXdhaXQgZGlnZXN0RmlsZShwYXRoLmpvaW4ocmVwb1BhdGgsIHJlbGF0aXZlUGF0aCkpO1xuICAgICAgICBmaWxlcy5wdXNoKHsgcGF0aDogcmVsYXRpdmVQYXRoLCBzaXplOiBkaWdlc3Quc2l6ZSwgc2hhMjU2OiBkaWdlc3Quc2hhMjU2IH0pO1xuICAgIH1cbiAgICByZXR1cm4geyB2ZXJzaW9uOiBNQU5JRkVTVF9WRVJTSU9OLCBmaWxlcyB9O1xufVxuXG4vKiogU29ydGVkIGtleXMgYW5kIGEgdHJhaWxpbmcgbmV3bGluZSwgc28gdGhlIHNhbWUgdHJlZSBhbHdheXMgcHJvZHVjZXMgdGhlIHNhbWUgYnl0ZXMgdG8gc2lnbi4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRNYW5pZmVzdChtYW5pZmVzdDogTWFuaWZlc3QpIHtcbiAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkobWFuaWZlc3QsIHVuZGVmaW5lZCwgNCkgKyBcIlxcblwiO1xufVxuIl19
85
- /* _JS_SOURCE_HASH = "201e773734f1a3a4a276457669646f936d46b81026afab845e5b8161d1b4310d"; */
85
+ /* _JS_SOURCE_HASH = "e58787cb53d84ba9647f11c90c737f7303e120b8e02ca37cc9f38202fce1929d"; */
@@ -562,6 +562,17 @@ export class ArchivesBackblaze implements IArchives {
562
562
  } catch (err: any) {
563
563
  if (retries <= 0) throw err;
564
564
 
565
+ // A file that does not exist is an answer, not a failure. It is the one error here that
566
+ // cannot become anything else by asking again, so it is thrown at once instead of being
567
+ // slept on twice - callers turn it into undefined, which is what they were asking.
568
+ if (
569
+ err.stack.includes(`"not_found"`)
570
+ || err.stack.includes(`file_not_found`)
571
+ || err.stack.includes(`"status": 404`)
572
+ ) {
573
+ throw err;
574
+ }
575
+
565
576
  // If it's a 503 and it's been a minute since we last reset, then Wait and reset.
566
577
  if (
567
578
  (err.stack.includes(`"status": 503`)
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- // Asks a server whether this machine is trusted.
4
- require("typenode");
5
-
6
- require("../security/machines/trustClient").main().catch(e => {
7
- console.error(`${e}`);
8
- process.exitCode = 1;
9
- }).finally(() => process.exit());
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- // Answers whether the machine asking is trusted, from the address it is asking from.
4
- require("typenode");
5
-
6
- // No exit on success: this one is a server, and returning from main only means it is listening.
7
- require("../security/machines/trustServer").main().catch(e => {
8
- console.error(`${e}`);
9
- process.exit(1);
10
- });
@@ -1,199 +0,0 @@
1
- import crypto from "crypto";
2
- import fs from "fs/promises";
3
- import os from "os";
4
- import path from "path";
5
- import { generateTestCA, getMachineId, identityStorageKey, IdentityStorageType, verifyMachineIdForPublicKey } from "../../misc/https/certs";
6
- import { runOverSSH, writeRemoteFile } from "../helpers/remoteSSH";
7
-
8
- // A machine's identity is the CA it generated for itself, kept in one file in the home directory
9
- // of whoever runs it. The name carries the domain it belongs to, so a machine can hold one per
10
- // domain, and the id itself is a hash of that CA's public key - which is why a machine cannot
11
- // claim to be another one without holding its key.
12
- const IDENTITY_PREFIX = "keystore_";
13
- const IDENTITY_SUFFIX = `_${identityStorageKey}.json`;
14
- // How stale a signed packet may be. Long enough for clock skew between two machines, short enough
15
- // that a captured packet is not worth replaying.
16
- export const PACKET_LIFETIME = 2 * 60 * 1000;
17
-
18
- export type Identity = {
19
- machineId: string;
20
- domain: string;
21
- fullDomain: string;
22
- fileName: string;
23
- };
24
-
25
- function identityFileName(domain: string) {
26
- return `${IDENTITY_PREFIX}${domain}${IDENTITY_SUFFIX}`;
27
- }
28
-
29
- /** The domain out of an identity file's name, or "" if it is not one. */
30
- function domainOfFileName(fileName: string) {
31
- if (!fileName.startsWith(IDENTITY_PREFIX) || !fileName.endsWith(IDENTITY_SUFFIX)) {
32
- return "";
33
- }
34
- return fileName.slice(IDENTITY_PREFIX.length, -IDENTITY_SUFFIX.length);
35
- }
36
-
37
- function describeIdentity(config: { domain: string; stored: IdentityStorageType }) {
38
- let { domain, stored } = config;
39
- return {
40
- machineId: getMachineId(stored.domain, domain),
41
- domain,
42
- fullDomain: stored.domain,
43
- fileName: identityFileName(domain),
44
- };
45
- }
46
-
47
- /** Every domain this machine holds an identity under. A machine has one identity per domain, and
48
- they are different machine ids, so which one is meant is a real question rather than a detail. */
49
- export async function localDomains() {
50
- let domains: string[] = [];
51
- for (let name of (await fs.readdir(os.homedir()).catch(() => [] as string[])).sort()) {
52
- let domain = domainOfFileName(name);
53
- if (domain) {
54
- domains.push(domain);
55
- }
56
- }
57
- return domains;
58
- }
59
-
60
- /** This machine's identity under one domain. Undefined when it has none under that one.
61
-
62
- The domain is required everywhere it appears. A machine has a different identity, and so a
63
- different machine id, under every domain it runs under, so "this machine" on its own does not
64
- name anything: answering under the wrong one means answering as a machine we are not. */
65
- export async function localIdentity(domain: string): Promise<Identity | undefined> {
66
- if (!domain) {
67
- throw new Error(`Expected a domain, was ${JSON.stringify(domain)}`);
68
- }
69
- let contents = await fs.readFile(path.join(os.homedir(), identityFileName(domain)), "utf8").catch(() => "");
70
- if (!contents) {
71
- return undefined;
72
- }
73
- return describeIdentity({ domain, stored: JSON.parse(contents) });
74
- }
75
-
76
- /** This machine's identity and its keys, for signing. */
77
- export async function localIdentityKeys(domain: string) {
78
- let identity = await localIdentity(domain);
79
- if (!identity) {
80
- throw new Error(
81
- `Expected an identity for ${domain} at ${path.join(os.homedir(), identityFileName(domain))},`
82
- + ` there is none.\n`
83
- + `This machine has: ${(await localDomains()).join(", ") || "no identities at all"}`
84
- );
85
- }
86
- let stored: IdentityStorageType = JSON.parse(
87
- await fs.readFile(path.join(os.homedir(), identity.fileName), "utf8")
88
- );
89
- return { identity, stored };
90
- }
91
-
92
- /** The identity of another machine under one domain, over ssh. Undefined when it has none under
93
- that domain, whatever else it may have under others. */
94
- export async function remoteIdentity(host: string, domain: string): Promise<Identity | undefined> {
95
- if (!domain) {
96
- throw new Error(`Expected a domain, was ${JSON.stringify(domain)}`);
97
- }
98
- let home = (await runOverSSH({ host, script: `echo $HOME` })).stdout.trim();
99
- if (!home) {
100
- throw new Error(`Expected ${host} to report a home directory, it reported nothing`);
101
- }
102
- let contents = await runOverSSH({
103
- host,
104
- script: `cat ${home}/${identityFileName(domain)} 2>/dev/null || true`,
105
- });
106
- if (!contents.stdout.trim()) {
107
- return undefined;
108
- }
109
- return describeIdentity({ domain, stored: JSON.parse(contents.stdout) });
110
- }
111
-
112
- /** Gives a machine an identity it does not have yet, by generating one here and writing it there.
113
-
114
- The CA is self signed and never leaves that machine afterwards, so generating it here is only a
115
- convenience: what matters is that the private key ends up in one place and the id is the hash
116
- of its public half. */
117
- export async function createRemoteIdentity(config: { host: string; domain: string }) {
118
- let { host, domain } = config;
119
- let generated = generateTestCA(domain);
120
- let stored: IdentityStorageType = {
121
- domain: generated.domain,
122
- certB64: generated.cert.toString("base64"),
123
- keyB64: generated.key.toString("base64"),
124
- };
125
- let home = (await runOverSSH({ host, script: `echo $HOME` })).stdout.trim();
126
- await writeRemoteFile({
127
- host,
128
- filePath: `${home}/${identityFileName(domain)}`,
129
- contents: JSON.stringify(stored),
130
- fileMode: "600",
131
- // The home directory is already there, this is only what it is created with if it is not.
132
- directoryMode: "700",
133
- });
134
- return describeIdentity({ domain, stored });
135
- }
136
-
137
- /** The raw ed25519 public key out of a certificate, which is what the machine id is a hash of. */
138
- function publicKeyBytes(certPEM: string) {
139
- let spki = new crypto.X509Certificate(certPEM).publicKey.export({ type: "spki", format: "der" });
140
- // An ed25519 SPKI is a fixed 12 byte header followed by the 32 byte key.
141
- return spki.subarray(spki.length - 32);
142
- }
143
-
144
- export type TrustPacket = {
145
- machineId: string;
146
- certB64: string;
147
- signedAt: number;
148
- signatureB64: string;
149
- };
150
-
151
- /** Proves we hold the private key behind our machine id, right now. The signature covers the id
152
- and the time, so the packet cannot be replayed later or reused for another id. */
153
- export async function signTrustPacket(domain: string): Promise<TrustPacket> {
154
- let { identity, stored } = await localIdentityKeys(domain);
155
- let signedAt = Date.now();
156
- let signature = crypto.sign(
157
- null,
158
- Buffer.from(`${identity.machineId} ${signedAt}`),
159
- crypto.createPrivateKey(Buffer.from(stored.keyB64, "base64").toString())
160
- );
161
- return {
162
- machineId: identity.machineId,
163
- certB64: stored.certB64,
164
- signedAt,
165
- signatureB64: signature.toString("base64"),
166
- };
167
- }
168
-
169
- /** Whether a packet really came from the machine it names. Says why when it did not, because
170
- "this is not that machine" and "this arrived too late" are different problems. */
171
- export function verifyTrustPacket(packet: TrustPacket) {
172
- if (!packet || !packet.machineId || !packet.certB64 || !packet.signatureB64) {
173
- return { valid: false, problem: "the packet is missing fields" };
174
- }
175
- if (Math.abs(Date.now() - packet.signedAt) > PACKET_LIFETIME) {
176
- return { valid: false, problem: `it was signed ${Math.round((Date.now() - packet.signedAt) / 1000)}s ago` };
177
- }
178
- let certPEM = Buffer.from(packet.certB64, "base64").toString();
179
- let publicKey: crypto.KeyObject;
180
- try {
181
- publicKey = new crypto.X509Certificate(certPEM).publicKey;
182
- } catch (e) {
183
- return { valid: false, problem: `its certificate could not be read, ${e}` };
184
- }
185
- // The id is a hash of the public key, so this is what stops a machine claiming another's id.
186
- if (!verifyMachineIdForPublicKey({ machineId: packet.machineId, publicKey: publicKeyBytes(certPEM) })) {
187
- return { valid: false, problem: "the machine id does not belong to that certificate" };
188
- }
189
- let signed = crypto.verify(
190
- null,
191
- Buffer.from(`${packet.machineId} ${packet.signedAt}`),
192
- publicKey,
193
- Buffer.from(packet.signatureB64, "base64")
194
- );
195
- if (!signed) {
196
- return { valid: false, problem: "the signature does not check out" };
197
- }
198
- return { valid: true, problem: "" };
199
- }
@@ -1,51 +0,0 @@
1
- import { signTrustPacket } from "./identity";
2
-
3
- // The other half of the demonstration: asks a server whether it trusts this machine. The answer
4
- // depends on the address this machine reaches the server from, which is why the client cannot
5
- // work it out alone.
6
- const TRUST_PATH = "/trusted";
7
- const USAGE = `Usage: yarn machineclient <url> <domain>
8
-
9
- Asks that server whether this machine is trusted, proving which machine it is with a signed packet.
10
- The server decides using the address we reach it from, so the answer is about how we are talking to
11
- it, not only about who we are.
12
-
13
- The domain is required: this machine has a different identity under each one, and the answer is
14
- about the identity we sign with.`;
15
-
16
- export type TrustAnswer = {
17
- trusted: boolean;
18
- machineId?: string;
19
- ip?: string;
20
- reason?: string;
21
- };
22
-
23
- /** Asks one server whether it trusts this machine, as it is under this domain. */
24
- export async function askIfTrusted(config: { baseURL: string; domain: string }): Promise<TrustAnswer> {
25
- let { baseURL, domain } = config;
26
- let packet = await signTrustPacket(domain);
27
- let url = `${baseURL.replace(/\/+$/, "")}${TRUST_PATH}`;
28
- let response = await fetch(url, {
29
- method: "POST",
30
- headers: { "Content-Type": "application/json" },
31
- body: JSON.stringify(packet),
32
- });
33
- return await response.json() as TrustAnswer;
34
- }
35
-
36
- export async function main() {
37
- let [baseURL, domain] = process.argv.slice(2);
38
- if (!baseURL || !domain) {
39
- throw new Error(USAGE);
40
- }
41
- let answer = await askIfTrusted({ baseURL, domain });
42
- console.log(`${answer.trusted && "TRUSTED" || "NOT TRUSTED"} by ${baseURL}`);
43
- console.log(` machine ${answer.machineId || "(not established)"}`);
44
- console.log(` seen coming from ${answer.ip || "(unknown)"}`);
45
- if (answer.reason) {
46
- console.log(` ${answer.reason}`);
47
- }
48
- if (!answer.trusted) {
49
- process.exitCode = 1;
50
- }
51
- }
@@ -1,87 +0,0 @@
1
- import http from "http";
2
- import { verifyTrustPacket } from "./identity";
3
- import { isMachineAccepted } from "./machines";
4
-
5
- // A demonstration of machine trust, and the smallest thing that exercises it end to end: a client
6
- // proves which machine it is, and this answers whether that machine is trusted from the address it
7
- // is actually talking from.
8
- //
9
- // Plain http on purpose. Nothing here is secret - the packet proves who the client is by signature
10
- // rather than by the channel, and the answer is a yes or no about a machine the caller already
11
- // knows the id of.
12
- const DEFAULT_PORT = 47812;
13
- const TRUST_PATH = "/trusted";
14
- const MAX_BODY_LENGTH = 64 * 1024;
15
-
16
- /** The address the client is actually talking from. Not read from anything the client sent,
17
- because that is the one half of the pair it must not get to choose. */
18
- function addressOf(request: http.IncomingMessage) {
19
- let address = request.socket.remoteAddress || "";
20
- // Node reports IPv4 over a dual stack socket this way.
21
- return address.replace(/^::ffff:/, "");
22
- }
23
-
24
- async function readBody(request: http.IncomingMessage) {
25
- let chunks: Buffer[] = [];
26
- let length = 0;
27
- for await (let chunk of request) {
28
- length += chunk.length;
29
- if (length > MAX_BODY_LENGTH) {
30
- throw new Error(`Expected at most ${MAX_BODY_LENGTH} bytes, was more`);
31
- }
32
- chunks.push(chunk);
33
- }
34
- return Buffer.concat(chunks).toString("utf8");
35
- }
36
-
37
- export function startTrustServer(config: { port: number }) {
38
- let server = http.createServer((request, response) => {
39
- let answer = async () => {
40
- if (request.url !== TRUST_PATH) {
41
- return { status: 404, body: { error: `POST a trust packet to ${TRUST_PATH}` } };
42
- }
43
- let packet = JSON.parse(await readBody(request));
44
- let ip = addressOf(request);
45
- let verified = verifyTrustPacket(packet);
46
- if (!verified.valid) {
47
- // Not a rejection of the machine, a packet that does not prove anything about one.
48
- return { status: 400, body: { trusted: false, ip, reason: verified.problem } };
49
- }
50
- let verdict = await isMachineAccepted({ machineId: packet.machineId, ip });
51
- return {
52
- status: 200,
53
- body: {
54
- trusted: verdict.accepted,
55
- machineId: packet.machineId,
56
- ip,
57
- // Whatever the check itself said. It is the only thing that knows which of the
58
- // ways to be refused this was.
59
- reason: verdict.reason,
60
- },
61
- };
62
- };
63
- answer().then(result => {
64
- console.log(`${request.method} ${request.url} from ${addressOf(request)}: ${JSON.stringify(result.body)}`);
65
- response.writeHead(result.status, { "Content-Type": "application/json" });
66
- response.end(JSON.stringify(result.body, undefined, 4));
67
- }).catch(e => {
68
- console.log(`${request.method} ${request.url} from ${addressOf(request)} failed. ${e}`);
69
- response.writeHead(500, { "Content-Type": "application/json" });
70
- response.end(JSON.stringify({ trusted: false, error: `${e}` }, undefined, 4));
71
- });
72
- });
73
- server.listen(config.port);
74
- return server;
75
- }
76
-
77
- export async function main() {
78
- let port = Number(process.argv[2] || DEFAULT_PORT);
79
- if (!Number.isFinite(port)) {
80
- throw new Error(`Usage: yarn machineserver [port]`);
81
- }
82
- startTrustServer({ port });
83
- console.log(`Answering trust checks on http://0.0.0.0:${port}${TRUST_PATH}`);
84
- console.log(`A client asks with: yarn machineclient http://<this machine>:${port}`);
85
- // Left running. Nothing else to do here, the server is the program.
86
- await new Promise(() => { });
87
- }