Skip to content

Commit e909963

Browse files
authored
Merge pull request #3 from NullNet-ai/dev
Implement client-side caching
2 parents 313a0ce + f8c7407 commit e909963

File tree

11 files changed

+140
-17
lines changed

11 files changed

+140
-17
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ app_id.txt
88
app_secret.txt
99
firewall_defaults.json
1010
uuid.txt
11+
cache.txt
1112
.env
1213
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
1314

client_common/proto/appguard.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ message AppGuardIpInfo {
5656
optional string region = 7;
5757
optional string postal = 8;
5858
optional string timezone = 9;
59-
bool blacklist = 100;
59+
// bool blacklist = 100;
6060
}
6161

6262
message AppGuardTcpInfo {

client_common/proto/commands.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ message ServerMessage {
4646
message FirewallDefaults {
4747
uint32 timeout = 1;
4848
FirewallPolicy policy = 2;
49+
bool cache = 3;
4950
}
5051

5152
enum FirewallPolicy {

client_common/src/appguard.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import {AppGuardResponse__Output} from './proto/appguard/AppGuardResponse'
77
import {AppGuardTcpConnection} from './proto/appguard/AppGuardTcpConnection'
88
import {AppGuardHttpResponse} from './proto/appguard/AppGuardHttpResponse'
99
import {AppGuardTcpResponse__Output} from "./proto/appguard/AppGuardTcpResponse";
10-
import {APP_ID_FILE, APP_SECRET_FILE, FIREWALL_DEFAULTS_FILE, TOKEN_FILE} from "./auth";
10+
import {APP_ID_FILE, APP_SECRET_FILE, CACHE_FILE, FIREWALL_DEFAULTS_FILE, TOKEN_FILE} from "./auth";
1111
import {AuthorizationRequest} from "./proto/appguard_commands/AuthorizationRequest";
1212
import {ClientMessage} from "./proto/appguard_commands/ClientMessage";
1313
import {ServerMessage__Output} from "./proto/appguard_commands/ServerMessage";
1414
import {FirewallDefaults, FirewallDefaults__Output} from "./proto/appguard_commands/FirewallDefaults";
15+
import {CacheKey} from "./cache";
16+
import {FirewallPolicy} from "./proto/appguard_commands/FirewallPolicy";
1517

1618
const opts = {includeDirs: [
1719
'node_modules/@nullnet/appguard-express/node_modules/appguard-client-common/proto/',
@@ -186,6 +188,9 @@ export class AppGuardService {
186188
let firewallDefaults: FirewallDefaults = server_msg.setFirewallDefaults;
187189
console.log("Received firewall defaults from server:", firewallDefaults);
188190
fs.writeFileSync(FIREWALL_DEFAULTS_FILE, JSON.stringify(firewallDefaults), {flag: 'w'});
191+
// empty and update cache
192+
let cache: Map<string, FirewallPolicy> = new Map([]);
193+
writeCache(cache);
189194
}
190195
if (server_msg.deviceDeauthorized) {
191196
// delete saved app secret and app id
@@ -206,9 +211,49 @@ export class AppGuardService {
206211
}, 10000);
207212
});
208213
}
214+
215+
getFromCache(key: CacheKey) : FirewallPolicy | undefined {
216+
let defaults = readFirewallDefaults();
217+
if (defaults.cache) {
218+
let cache = readCache();
219+
return cache.get(JSON.stringify(key));
220+
}
221+
}
222+
223+
insertToCache(key: CacheKey, policy: FirewallPolicy) {
224+
let defaults = readFirewallDefaults();
225+
if (defaults.cache) {
226+
let cache = readCache();
227+
cache.set(JSON.stringify(key), policy);
228+
writeCache(cache);
229+
}
230+
}
209231
}
210232

211233
function readFirewallDefaults(): FirewallDefaults {
212234
let text = fs.readFileSync(FIREWALL_DEFAULTS_FILE, 'utf8');
213-
return JSON.parse(text);
235+
if (text.trim() === '') {
236+
return {
237+
policy: FirewallPolicy.ALLOW,
238+
timeout: 1000,
239+
cache: true,
240+
};
241+
} else {
242+
return JSON.parse(text);
243+
}
244+
}
245+
246+
function readCache(): Map<string, FirewallPolicy> {
247+
let text = fs.readFileSync(CACHE_FILE, 'utf8');
248+
if (text.trim() === '') {
249+
return new Map([]);
250+
} else {
251+
let map: Map<string, FirewallPolicy> = new Map(JSON.parse(text))
252+
return map;
253+
}
254+
}
255+
256+
function writeCache(cache: Map<string, FirewallPolicy>) {
257+
let mySerialMap = JSON.stringify(Array.from(cache.entries()));
258+
fs.writeFileSync(CACHE_FILE, mySerialMap, {flag: 'w'});
214259
}

client_common/src/auth.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import {AuthorizationRequest} from "./proto/appguard_commands/AuthorizationReque
33
import {FirewallDefaults} from "./proto/appguard_commands/FirewallDefaults";
44
import { v4 as uuidv4 } from 'uuid';
55

6-
export const TOKEN_FILE = process.cwd() + '/../token.txt'
7-
export const APP_ID_FILE = process.cwd() + '/../app_id.txt'
8-
export const APP_SECRET_FILE = process.cwd() + '/../app_secret.txt'
9-
export const FIREWALL_DEFAULTS_FILE = process.cwd() + '/../firewall_defaults.json'
10-
export const UUID_FILE = process.cwd() + '/../uuid.txt'
6+
export const TOKEN_FILE = process.cwd() + '/token.txt'
7+
export const APP_ID_FILE = process.cwd() + '/app_id.txt'
8+
export const APP_SECRET_FILE = process.cwd() + '/app_secret.txt'
9+
export const FIREWALL_DEFAULTS_FILE = process.cwd() + '/firewall_defaults.json'
10+
export const UUID_FILE = process.cwd() + '/uuid.txt'
11+
export const CACHE_FILE = process.cwd() + '/cache.txt'
1112

1213
const fs = require('fs');
1314

@@ -32,6 +33,10 @@ export class AuthHandler {
3233

3334
// empty token file content
3435
fs.writeFileSync(TOKEN_FILE, '', {flag: 'w'});
36+
// empty cache file content
37+
fs.writeFileSync(CACHE_FILE, '', {flag: 'w'});
38+
// empty firewall defaults file content
39+
fs.writeFileSync(FIREWALL_DEFAULTS_FILE, '', {flag: 'w'});
3540
}
3641

3742
async init(type: string){

client_common/src/cache.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {FirewallPolicy} from "./proto/appguard_commands/FirewallPolicy";
2+
3+
export class CacheKey {
4+
originalUrl: string;
5+
method: string;
6+
body: string;
7+
sourceIp: string;
8+
userAgent: string;
9+
query: Record<string, string>;
10+
}

client_common/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export {FirewallPolicy} from "./proto/appguard_commands/FirewallPolicy";
22
export {AppGuardService} from './appguard';
33
export {AuthHandler} from './auth';
4-
export {AppGuardTcpInfo} from './proto/appguard/AppGuardTcpInfo';
4+
export {AppGuardTcpInfo} from './proto/appguard/AppGuardTcpInfo';
5+
export {CacheKey} from './cache';

client_common/src/proto/appguard/AppGuardIpInfo.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export interface AppGuardIpInfo {
1111
'region'?: (string);
1212
'postal'?: (string);
1313
'timezone'?: (string);
14-
'blacklist'?: (boolean);
1514
'_country'?: "country";
1615
'_asn'?: "asn";
1716
'_org'?: "org";
@@ -32,5 +31,4 @@ export interface AppGuardIpInfo__Output {
3231
'region'?: (string);
3332
'postal'?: (string);
3433
'timezone'?: (string);
35-
'blacklist'?: (boolean);
3634
}

client_common/src/proto/appguard_commands/FirewallDefaults.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import type { FirewallPolicy as _appguard_commands_FirewallPolicy } from '../app
55
export interface FirewallDefaults {
66
'timeout'?: (number);
77
'policy'?: (_appguard_commands_FirewallPolicy | keyof typeof _appguard_commands_FirewallPolicy);
8+
'cache'?: (boolean);
89
}
910

1011
export interface FirewallDefaults__Output {
1112
'timeout'?: (number);
1213
'policy'?: (_appguard_commands_FirewallPolicy);
14+
'cache'?: (boolean);
1315
}

clients/express/src/express-middleware.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { NextFunction, Request, Response, Send } from 'express';
2-
import { FirewallPolicy, AppGuardTcpInfo, AppGuardService, AuthHandler } from 'appguard-client-common';
2+
import { FirewallPolicy, AppGuardTcpInfo, AppGuardService, AuthHandler, CacheKey } from 'appguard-client-common';
33

44
type ExpressMiddleware = (
55
req: Request,
@@ -21,7 +21,8 @@ export const createAppGuardMiddleware = () => {
2121

2222
const attachResponseHandlers = (
2323
res: Response,
24-
tcp_info: AppGuardTcpInfo
24+
tcp_info: AppGuardTcpInfo,
25+
cacheKey: CacheKey,
2526
) => {
2627
// Storing the original send function
2728
// @ts-ignore
@@ -49,10 +50,12 @@ export const createAppGuardMiddleware = () => {
4950
));
5051

5152
if (handleHTTPResponseResponse.policy === FirewallPolicy.DENY) {
53+
appGuardService.insertToCache(cacheKey, FirewallPolicy.DENY);
5254
// Destroying the socket connection instead of sending the response
5355
// @ts-ignore
5456
res.socket?.destroy();
5557
} else {
58+
appGuardService.insertToCache(cacheKey, FirewallPolicy.ALLOW);
5659
// Intercepting the response.send() call
5760
// Calling the original send function
5861
//@ts-expect-error: This function is this context
@@ -72,6 +75,7 @@ export const createAppGuardMiddleware = () => {
7275
// @ts-ignore
7376
req.socket.remoteAddress;
7477

78+
7579
// console.log(
7680
// // @ts-ignore
7781
// `Appguard Debug XRI:${req.headers['x-real-ip']} - XFF:${req.headers['x-forwarded-for']} TCP/PROXY:${req.socket.remoteAddress} SRC=${sourceIp}`
@@ -82,6 +86,32 @@ export const createAppGuardMiddleware = () => {
8286
// `Appguard Debug From - ${sourceIp} - ${req.method} ${req.originalUrl}`
8387
// );
8488

89+
let cacheKey: CacheKey = {
90+
// @ts-ignore
91+
originalUrl: req.originalUrl,
92+
// @ts-ignore
93+
method: req.method,
94+
// @ts-ignore
95+
body: req.body,
96+
// @ts-ignore
97+
sourceIp: sourceIp,
98+
// @ts-ignore
99+
userAgent: req.headers["user-agent"],
100+
// @ts-ignore
101+
query: req.query as Record<string, string>,
102+
};
103+
104+
let cached = appGuardService.getFromCache(cacheKey);
105+
if (cached !== undefined) {
106+
if (cached === FirewallPolicy.DENY) {
107+
// @ts-ignore
108+
res.socket?.destroy();
109+
} else {
110+
next();
111+
}
112+
return;
113+
}
114+
85115
const handleTCPConnectionResponse = await appGuardService.connectionPromise(
86116
{
87117
// @ts-ignore
@@ -122,6 +152,7 @@ export const createAppGuardMiddleware = () => {
122152

123153
const policy = handleHTTPRequestResponse.policy;
124154
if (policy === FirewallPolicy.DENY) {
155+
appGuardService.insertToCache(cacheKey, FirewallPolicy.DENY);
125156
// Destroying the socket connection instead of sending the response
126157
// @ts-ignore
127158
res.socket?.destroy();
@@ -130,7 +161,8 @@ export const createAppGuardMiddleware = () => {
130161
// attach response handlers after we get the req.id
131162
attachResponseHandlers(
132163
res,
133-
handleTCPConnectionResponse.tcpInfo as AppGuardTcpInfo
164+
handleTCPConnectionResponse.tcpInfo as AppGuardTcpInfo,
165+
cacheKey
134166
);
135167
next();
136168
}

0 commit comments

Comments
 (0)