Skip to content

Commit fcfaf5c

Browse files
authored
Fix AI Gateway logpush decryption tutorial (#17578)
1 parent d5128a0 commit fcfaf5c

File tree

1 file changed

+87
-28
lines changed
  • src/content/docs/ai-gateway/observability/logging

1 file changed

+87
-28
lines changed

src/content/docs/ai-gateway/observability/logging/logpush.mdx

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,24 @@ You can toggle Logpush on and off in the [Cloudflare dashboard](https://dash.clo
1313

1414
This guide explains how to set up Logpush for AI Gateway, generate an RSA key pair for encryption, and decrypt the logs once they are received.
1515

16-
You can store up to 10 million logs per gateway. If your limit is reached, new logs will stop being saved and will not be exported through Logpush. To continue saving and exporting logs, you must delete older logs to free up space for new logs. Logpush has a limit of 4 jobs.
16+
You can store up to 10 million logs per gateway. If your limit is reached, new logs will stop being saved and will not be exported through Logpush. To continue saving and exporting logs, you must delete older logs to free up space for new logs. Logpush has a limit of 4 jobs.
1717

1818
:::note[Note]
1919

2020
To export logs using Logpush, you must have logs turned on for the gateway.
2121

2222
:::
2323

24+
## How logs are encrypted
25+
26+
We employ a hybrid encryption model efficiency and security. Initially, an AES key is generated for each log. This AES key is what actually encrypts the bulk of your data, chosen for its speed and security in handling large datasets efficiently.
27+
28+
Now, for securely sharing this AES key, we use RSA encryption. Here's what happens: the AES key, although lightweight, needs to be transmitted securely to the recipient. We encrypt this key with the recipient's RSA public key. This step leverages RSA's strength in secure key distribution, ensuring that only someone with the corresponding RSA private key can decrypt and use the AES key.
29+
30+
Once encrypted, both the AES-encrypted data and the RSA-encrypted AES key are sent together. Upon arrival, the recipient's system uses the RSA private key to decrypt the AES key. With the AES key now accessible, it's straightforward to decrypt the main data payload.
31+
32+
This method combines the best of both worlds: the efficiency of AES for data encryption with the secure key exchange capabilities of RSA, ensuring data integrity, confidentiality, and performance are all optimally maintained throughout the data lifecycle.
33+
2434
## Setting up Logpush
2535

2636
To configure Logpush for AI Gateway, follow these steps:
@@ -57,7 +67,7 @@ node {file name}
5767

5868
## 2. Upload public key to gateway settings
5969

60-
Once you have generated the key pair, upload the public key to your AI Gateway settings. This key will be used to encrypt your logs. In order to enable Logpush, you will need logs enabled for that gateway.
70+
Once you have generated the key pair, upload the public key to your AI Gateway settings. This key will be used to encrypt your logs. In order to enable Logpush, you will need logs enabled for that gateway.
6171

6272
## 3. Set up Logpush
6373

@@ -69,49 +79,98 @@ After configuring Logpush, logs will be sent encrypted using the public key you
6979

7080
## 5. Decrypt logs
7181

72-
To decrypt the encrypted log bodies and metadata from AI Gateway, you can use the following Node.js script:
82+
To decrypt the encrypted log bodies and metadata from AI Gateway, download the logs to a folder, in this case its named `my_log.log.gz`.
83+
84+
Then copy this javascript file into the same folder and place your private key in the top variable.
7385

7486
```js title="JavaScript"
75-
const privateKey = `-----BEGIN RSA PRIVATE KEY-----
87+
const privateKeyStr = `-----BEGIN RSA PRIVATE KEY-----
7688
....
7789
-----END RSA PRIVATE KEY-----`;
7890

7991
const crypto = require("crypto");
80-
const key = crypto.createPrivateKey(privateKey);
92+
const privateKey = crypto.createPrivateKey(privateKeyStr);
8193

8294
const fs = require("fs");
8395
const zlib = require("zlib");
8496
const readline = require("readline");
8597

86-
function decryptBase64(key, string) {
87-
const decryptedData = crypto.privateDecrypt(
88-
{
89-
key: key,
90-
oaepHash: "SHA256",
91-
},
92-
Buffer.from(string, "base64"),
93-
);
98+
async function importAESGCMKey(keyBuffer) {
99+
try {
100+
// Ensure the key length is valid for AES
101+
if ([128, 192, 256].includes(256)) {
102+
return await crypto.webcrypto.subtle.importKey(
103+
'raw',
104+
keyBuffer,
105+
{
106+
name: 'AES-GCM',
107+
length: 256
108+
},
109+
true, // Whether the key is extractable (true in this case to allow for export later if needed)
110+
['encrypt', 'decrypt'] // Use for encryption and decryption
111+
);
112+
} else {
113+
throw new Error('Invalid AES key length. Must be 128, 12, or 256 bits.');
114+
}
115+
} catch (error) {
116+
console.error('Failed to import key:', error);
117+
throw error;
118+
}
119+
}
94120

95-
return decryptedData.toString();
121+
async function decryptData(encryptedData, aesKey, iv) {
122+
const decryptedData = await crypto.subtle.decrypt(
123+
{name: "AES-GCM", iv: iv},
124+
aesKey,
125+
encryptedData
126+
);
127+
return new TextDecoder().decode(decryptedData);
96128
}
97129

98-
let lineReader = readline.createInterface({
99-
input: fs.createReadStream("my_log.log.gz").pipe(zlib.createGunzip()),
100-
});
130+
async function decryptBase64(privateKey, data) {
131+
if (data.key === undefined) {
132+
return data
133+
}
134+
135+
const aesKeyBuf = crypto.privateDecrypt(
136+
{
137+
key: privateKey,
138+
oaepHash: "SHA256",
139+
},
140+
Buffer.from(data.key, "base64"),
141+
);
142+
const aesKey = await importAESGCMKey(aesKeyBuf)
143+
144+
const decryptedData = await decryptData(
145+
Buffer.from(data.data, "base64"),
146+
aesKey,
147+
Buffer.from(data.iv, "base64")
148+
)
149+
150+
return decryptedData.toString();
151+
}
101152

102-
lineReader.on("line", (line) => {
103-
line = JSON.parse(line);
153+
async function run() {
154+
let lineReader = readline.createInterface({
155+
input: fs.createReadStream("my_log.log.gz").pipe(zlib.createGunzip()),
156+
});
104157

105-
const { Metadata, RequestBody, ResponseBody, ...remaining } = line;
158+
lineReader.on("line", async (line) => {
159+
line = JSON.parse(line);
106160

107-
console.log({
108-
...remaining,
109-
Metadata: decryptBase64(key, Metadata.data),
110-
RequestBody: decryptBase64(key, RequestBody.data),
111-
ResponseBody: decryptBase64(key, ResponseBody.data),
112-
});
113-
console.log("--");
114-
});
161+
const {Metadata, RequestBody, ResponseBody, ...remaining} = line;
162+
163+
console.log({
164+
...remaining,
165+
Metadata: await decryptBase64(privateKey, Metadata),
166+
RequestBody: await decryptBase64(privateKey, RequestBody),
167+
ResponseBody: await decryptBase64(privateKey, ResponseBody),
168+
});
169+
console.log("--");
170+
});
171+
}
172+
173+
run()
115174
```
116175

117176
Run the script by executing the below code on your terminal. Replace `file name` with the name of your JavaScript file.

0 commit comments

Comments
 (0)