anything-llm/server/utils/comKey/index.js
Timothy Carambat dc4ad6b5a9
[BETA] Live document sync (#1719)
* wip bg workers for live document sync

* Add ability to re-embed specific documents across many workspaces via background queue
bgworkser is gated behind expieremental system setting flag that needs to be explictly enabled
UI for watching/unwatching docments that are embedded.
TODO: UI to easily manage all bg tasks and see run results
TODO: UI to enable this feature and background endpoints to manage it

* create frontend views and paths
Move elements to correct experimental scope

* update migration to delete runs on removal of watched document

* Add watch support to YouTube transcripts (#1716)

* Add watch support to YouTube transcripts
refactor how sync is done for supported types

* Watch specific files in Confluence space (#1718)

Add failure-prune check for runs

* create tmp workflow modifications for beta image

* create tmp workflow modifications for beta image

* create tmp workflow modifications for beta image

* dual build
update copy of alert modals

* update job interval

* Add support for live-sync of Github files

* update copy for document sync feature

* hide Experimental features from UI

* update docs links

* [FEAT] Implement new settings menu for experimental features (#1735)

* implement new settings menu for experimental features

* remove unused context save bar

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* dont run job on boot

* unset workflow changes

* Add persistent encryption service
Relay key to collector so persistent encryption can be used
Encrypt any private data in chunkSources used for replay during resync jobs

* update jsDOC

* Linting and organization

* update modal copy for feature

---------

Co-authored-by: Sean Hatfield <seanhatfield5@gmail.com>
2024-06-21 13:38:50 -07:00

87 lines
3.0 KiB
JavaScript

const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
const keyPath =
process.env.NODE_ENV === "development"
? path.resolve(__dirname, `../../storage/comkey`)
: path.resolve(
process.env.STORAGE_DIR ?? path.resolve(__dirname, `../../storage`),
`comkey`
);
// What does this class do?
// This class generates a hashed version of some text (typically a JSON payload) using a rolling RSA key
// that can then be appended as a header value to do integrity checking on a payload. Given the
// nature of this class and that keys are rolled constantly, this protects the request
// integrity of requests sent to the collector as only the server can sign these requests.
// This keeps accidental misconfigurations of AnythingLLM that leaving port 8888 open from
// being abused or SSRF'd by users scraping malicious sites who have a loopback embedded in a <script>, for example.
// Since each request to the collector must be signed to be valid, unsigned requests directly to the collector
// will be dropped and must go through the /server endpoint directly.
class CommunicationKey {
#privKeyName = "ipc-priv.pem";
#pubKeyName = "ipc-pub.pem";
#storageLoc = keyPath;
// Init the class and determine if keys should be rolled.
// This typically occurs on boot up so key is fresh each boot.
constructor(generate = false) {
if (generate) this.#generate();
}
log(text, ...args) {
console.log(`\x1b[36m[CommunicationKey]\x1b[0m ${text}`, ...args);
}
#readPrivateKey() {
return fs.readFileSync(path.resolve(this.#storageLoc, this.#privKeyName));
}
#generate() {
const keyPair = crypto.generateKeyPairSync("rsa", {
modulusLength: 2048,
publicKeyEncoding: {
type: "pkcs1",
format: "pem",
},
privateKeyEncoding: {
type: "pkcs1",
format: "pem",
},
});
if (!fs.existsSync(this.#storageLoc))
fs.mkdirSync(this.#storageLoc, { recursive: true });
fs.writeFileSync(
`${path.resolve(this.#storageLoc, this.#privKeyName)}`,
keyPair.privateKey
);
fs.writeFileSync(
`${path.resolve(this.#storageLoc, this.#pubKeyName)}`,
keyPair.publicKey
);
this.log(
"RSA key pair generated for signed payloads within AnythingLLM services."
);
}
// This instance of ComKey on server is intended for generation of Priv/Pub key for signing and decoding.
// this resource is shared with /collector/ via a class of the same name in /utils which does decoding/verification only
// while this server class only does signing with the private key.
sign(textData = "") {
return crypto
.sign("RSA-SHA256", Buffer.from(textData), this.#readPrivateKey())
.toString("hex");
}
// Use the rolling priv-key to encrypt arbitrary data that is text
// returns the encrypted content as a base64 string.
encrypt(textData = "") {
return crypto
.privateEncrypt(this.#readPrivateKey(), Buffer.from(textData, "utf-8"))
.toString("base64");
}
}
module.exports = { CommunicationKey };