Skip to content

Commit

Permalink
feat: adds certificate validation on provider proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
stalniy committed Jan 24, 2025
1 parent 47df89f commit 2f46f23
Show file tree
Hide file tree
Showing 27 changed files with 1,523 additions and 68 deletions.
42 changes: 39 additions & 3 deletions .github/workflows/docker-build-provider-proxy.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
name: Provider Proxy CI
name: Validate and Build Provider Proxy CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
Expand All @@ -21,6 +23,40 @@ jobs:
filters: |
provider-proxy:
- 'apps/provider-proxy/**'
- 'packages/net/**'
- name: Setup Node.js
if: steps.filter.outputs.provider-proxy == 'true'
uses: actions/setup-node@v4
with:
node-version: 20.14.0

- name: Restore root node_modules cache
if: steps.filter.outputs.provider-proxy == 'true'
uses: actions/cache@v4
id: cache
with:
path: |
node_modules
apps/api/node_modules
packages/*/node_modules
key: api-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

- name: Install dependencies
if: steps.filter.outputs.provider-proxy == 'true' && steps.cache.outputs.cache-hit != 'true'
run: npm ci

- name: Run static code analysis
if: steps.filter.outputs.provider-proxy == 'true'
run: npm run lint -w apps/provider-proxy

- name: Run unit tests
if: steps.filter.outputs.provider-proxy == 'true'
run: npm run test:unit --workspace=apps/provider-proxy

- name: Run functional tests
if: steps.filter.outputs.provider-proxy == 'true'
run: npm run test:functional --workspace=apps/provider-proxy

- name: Build the Docker image
if: steps.filter.outputs.provider-proxy == 'true'
Expand Down
6 changes: 5 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ if [[ "$CI" != "true" ]]; then
npm run update-apps-local-deps -w packages/ui
npm run update-apps-local-deps -w packages/network-store
npm run update-apps-local-deps -w packages/logging
npm run update-apps-local-deps -w packages/net
git add ./apps/*/mvm.lock

npm run generate -w packages/net
git add ./packages/net/src/generated

npx lint-staged
fi
fi
3 changes: 2 additions & 1 deletion apps/provider-proxy/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ module.exports = {
{
displayName: "unit",
...common,
testMatch: ["<rootDir>/src/**/*.spec.ts"]
testMatch: ["<rootDir>/test/**/*.spec.ts"],
testPathIgnorePatterns: ["/node_modules", "test/functional"]
},
{
displayName: "functional",
Expand Down
3 changes: 3 additions & 0 deletions apps/provider-proxy/mvm.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"dependencies": {
"@akashnetwork/net": "0.0.1"
},
"devDependencies": {
"@akashnetwork/dev-config": "1.0.0"
}
Expand Down
17 changes: 11 additions & 6 deletions apps/provider-proxy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,42 @@
"author": "Akash Network",
"main": "main.js",
"scripts": {
"build": "npx tsc",
"dev": "npm run start",
"build": "rm -rf dist/* && npx tsc && esbuild server.ts --bundle --sourcemap --platform=node --target=node20.14.0 --outdir=dist",
"dev": "nodemon server.ts",
"dev-nodc": "npm run start",
"format": "prettier --write ./*.{ts,js,json} **/*.{ts,js,json}",
"lint": "eslint .",
"prod": "node ./dist/server.js",
"start": "npm run build && node ./dist/server.js",
"test:functional": "jest --selectProjects functional",
"test:functional:cov": "jest --selectProjects functional --coverage",
"test:functional:watch": "jest --selectProjects functional --watch"
"test:functional:watch": "jest --selectProjects functional --watch",
"test:unit": "jest --selectProjects unit"
},
"dependencies": {
"axios": "^1.7.2",
"@akashnetwork/net": "*",
"bech32": "^2.0.0",
"cors": "^2.8.5",
"express": "^4.18.2",
"node-fetch": "^2.6.9",
"lru-cache": "^11.0.2",
"uuid": "^9.0.0",
"ws": "^7.5.9"
},
"devDependencies": {
"@akashnetwork/dev-config": "*",
"@types/cors": "^2.8.13",
"@types/express": "^4.17.16",
"@types/node-fetch": "^2.6.2",
"@types/node-forge": "^1.3.11",
"@types/uuid": "^9.0.0",
"@types/ws": "^8.5.4",
"@typescript-eslint/eslint-plugin": "^7.12.0",
"esbuild": "^0.24.2",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.3",
"eslint-plugin-simple-import-sort": "^12.1.0",
"jest": "^29.7.0",
"node-forge": "^1.3.1",
"nodemon": "^3.1.9",
"prettier": "^3.3.0",
"prettier-plugin-tailwindcss": "^0.6.1",
"typescript": "5.1.3"
Expand Down
1 change: 1 addition & 0 deletions apps/provider-proxy/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const whitelist = [
"https://console-beta.akash.network"
];

app.disable("x-powered-by");
app.use(
cors({
origin: function (origin, callback) {
Expand Down
10 changes: 9 additions & 1 deletion apps/provider-proxy/src/container.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { netConfig, SupportedChainNetworks } from "@akashnetwork/net";

import { ProviderProxy } from "./services/ProviderProxy";
import { WebsocketStats } from "./services/WebsocketStats";

export const container = {
wsStats: new WebsocketStats(),
providerProxy: new ProviderProxy()
providerProxy: new ProviderProxy(Date.now, (network: SupportedChainNetworks) => {
// TEST_CHAIN_NETWORK_URL is hack for functional tests
// there is no good way to mock external server in nodejs
// both nock and msw do not work well when I need to use low level API like X509 certificate validation
// for some reason when those libraries are used I receive MockSocket instead of TLSSocket
return process.env.TEST_CHAIN_NETWORK_URL || netConfig.getBaseAPIUrl(network);
})
};
52 changes: 27 additions & 25 deletions apps/provider-proxy/src/routes/proxyProviderRequest.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,39 @@
import { NextFunction, Request as ExpressRequest, Response as ExpressResponse } from "express";

import { container } from "../container";
import { httpRetry } from "../utils/retry";

export async function proxyProviderRequest(req: ExpressRequest, res: ExpressResponse, next: NextFunction): Promise<void> {
const { certPem, keyPem, method, body, url } = req.body;
const DEFAULT_TIMEOUT = 5_000;
export async function proxyProviderRequest(req: ExpressRequest, incommingResponse: ExpressResponse, next: NextFunction): Promise<void> {
const { certPem, keyPem, method, body, url, network, providerAddress, timeout } = req.body;

try {
const response = await container.providerProxy.fetch(url, {
headers: {
"Content-Type": "application/json"
},
method,
body,
cert: certPem,
key: keyPem
});

if (response.status >= 200 && response.status < 300) {
const responseText = await response.text();
const contentType = response.headers.get("content-type");
if (contentType && contentType.indexOf("application/json") !== -1) {
res.contentType("application/json");
} else {
res.contentType("application/text");
const proxyResult = await httpRetry(
() =>
container.providerProxy.connect(url, {
method,
body,
cert: certPem,
key: keyPem,
network,
providerAddress,
timeout: Number(timeout || DEFAULT_TIMEOUT) || DEFAULT_TIMEOUT
}),
{
retryIf: result => result.ok && (!result.response.statusCode || result.response.statusCode > 500)
}
res.send(responseText);
} else {
const _res = await response.text();
console.log("Status code was not success (" + response.status + ") : " + _res);
);

res.status(500);
res.send(_res);
if (proxyResult.ok === false) {
incommingResponse.status(495); // https://http.dev/495
incommingResponse.send(`Invalid certificate error: ${proxyResult.reason}`);
return;
}

Object.keys(proxyResult.response.headers).forEach(header => {
incommingResponse.setHeader(header, proxyResult.response.headers[header] || "");
});
proxyResult.response.pipe(incommingResponse).on("error", next);
} catch (error) {
next(error);
}
Expand Down
Loading

0 comments on commit 2f46f23

Please sign in to comment.