Skip to content

Commit

Permalink
Revert "Merge pull request #37 from helium/feat/session-key-updates"
Browse files Browse the repository at this point in the history
This reverts commit 52a9ac2, reversing
changes made to 56712ed.
  • Loading branch information
deasydoesit committed Mar 18, 2024
1 parent 52a9ac2 commit 46a53c7
Show file tree
Hide file tree
Showing 13 changed files with 56 additions and 68 deletions.
48 changes: 44 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,47 @@
# RPC Proxy for Helius
# Helius RPC Proxy

The RPC Proxy is handled by a Cloudflare Worker.
[![RPC Proxy](docs/rpc_proxy.png)](https://helius.xyz)

Within Cloudflare, the `rpc-proxy-staging` worker denotes the worker corresponding to the Solana Devnet environment. Deployments to the staging RPC Proxy occur directly via GitHub Actions when PRs are merged to Main and on successful completion of the test suite.
This repo hosts a one-click-deploy Cloudflare worker that proxies RPC requests to Helius. The proxy will allow you to keep your API key
hidden from public requests made by clients. You will need both a [Helius](https://helius.xyz) account and a [Cloudflare](https://cloudflare.com) account to deploy this. Helius offers 100k credits for free each month, and Cloudflare workers can execute 100k invocations each day for free. Most projects can easily get started within these free tiers.

Similarly, the `rpc-proxy` worker denotes the worker corresponding to the Solana Mainnet environment. Deployments to the production RPC Proxy occur manually via the GitHub Actions control plane and can only be performed by those with requisite permissions and only after successful completion of the test suite.
Both standard JSON RPC and Websockets are supported!

[Video Walkthrough](https://www.loom.com/share/a7add579f1c349d2a4bcab96ee04c47e)

# Setup
### Step 1

Press the button below to deploy this to your own Cloudflare account:

[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/helius-labs/helius-rpc-proxy)

### Step 2

Navigate to your newly deployed worker, and click "Settings" and then "Variables":

![Variables](docs/add_variable.png)

### Step 3
Add a new variable with the key name `HELIUS_API_KEY` and your Helius API key as the value:

![Add Secret](docs/add_secret.png)

> NOTE: We recommend selecting "Encrypt". This will hide your key from the UI and API responses, and redact them from logs.
![Encrypt](docs/encrypt.png)

### Step 4
Refresh the page and confirm that your key is now saved and encrypted:

![Confirm](docs/confirm.png)

You can now use your worker URL as an the RPC endpoint in all SDK and client side configurations without your API key leaking!
# Additional Security Steps
This implementation is intentionally left in a less-than-ideal security state to facilitate easy deployment by anyone. If you would like to
lock down your RPC proxy further, consider the following steps after you have successfully deployed the worker:


* Update the `Access-Control-Allow-Origin` header by adding a new variable with the key name `CORS_ALLOW_ORIGIN` to contain the host that your requests are coming from (usually your client application). For example, if you wanted to allow requests from `https://example.com`, you would change the header to `https://example.com`.
* [Cloudflare Web Application Firewall (WAF)](https://www.cloudflare.com/lp/ppc/waf-x/) - You can configure the WAF to inspect requests and allow/deny based on your own business logic.
* Modify the IP address allow list in Helius for your API key to only accept connections from the Cloudflare ranges (https://cloudflare.com/ips-v4).
Binary file added docs/add_secret.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/add_variable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/confirm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/encrypt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/rpc_proxy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions jest-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ jest.setMock('node-fetch', fetch);

process.env.CORS_ALLOW_ORIGIN = 'CORS_ALLOW_ORIGIN';
process.env.HELIUS_API_KEY = 'HELIUS_API_KEY';
process.env.SESSION_KEY_PREV = 'SESSION_KEY_PREV';
process.env.SESSION_KEY_CURR = 'SESSION_KEY_CURR';
process.env.SESSION_KEY = 'SESSION_KEY';
process.env.AWS_REGION = 'AWS_REGION';
process.env.AWS_ACCESS_KEY_ID = 'AWS_ACCESS_KEY_ID';
process.env.AWS_SECRET_ACCESS_KEY = 'AWS_SECRET_ACCESS_KEY';
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ export default {
// other query string params
const { searchParams, pathname } = new URL(request.url);
const sessionKey = searchParams.get('session-key');
if (!sessionKey || ![env.SESSION_KEY_CURR, env.SESSION_KEY_PREV].includes(sessionKey)) {
if (sessionKey != env.SESSION_KEY) {
return new Response(null, {
status: 404,
statusText: 'Invalid session key',
statusText: 'Unexpected path',
});
}
searchParams.delete('session-key');
Expand Down
3 changes: 1 addition & 2 deletions src/tests/createLogStreamCommandBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ describe('createLogStreamCommandBuilder', () => {
const originalEnv = {
CORS_ALLOW_ORIGIN: process.env.CORS_ALLOW_ORIGIN,
HELIUS_API_KEY: process.env.HELIUS_API_KEY,
SESSION_KEY_PREV: process.env.SESSION_KEY_PREV,
SESSION_KEY_CURR: process.env.SESSION_KEY_CURR,
SESSION_KEY: process.env.SESSION_KEY,
AWS_REGION: process.env.AWS_REGION,
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
Expand Down
3 changes: 1 addition & 2 deletions src/tests/errorHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ describe('errorHandler', () => {
const originalEnv = {
CORS_ALLOW_ORIGIN: process.env.CORS_ALLOW_ORIGIN,
HELIUS_API_KEY: process.env.HELIUS_API_KEY,
SESSION_KEY_PREV: process.env.SESSION_KEY_PREV,
SESSION_KEY_CURR: process.env.SESSION_KEY_CURR,
SESSION_KEY: process.env.SESSION_KEY,
AWS_REGION: process.env.AWS_REGION,
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
Expand Down
57 changes: 5 additions & 52 deletions src/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ describe('index', () => {
const originalEnv = {
CORS_ALLOW_ORIGIN: process.env.CORS_ALLOW_ORIGIN as string,
HELIUS_API_KEY: process.env.HELIUS_API_KEY as string,
SESSION_KEY_PREV: process.env.SESSION_KEY_PREV as string,
SESSION_KEY_CURR: process.env.SESSION_KEY_CURR as string,
SESSION_KEY: process.env.SESSION_KEY as string,
AWS_REGION: process.env.AWS_REGION as string,
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID as string,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY as string,
Expand All @@ -24,7 +23,7 @@ describe('index', () => {
(fetch as jest.MockedFunction<typeof fetch>).mockImplementation();

const request = new Request(
`https://solana-rpc.web.helium.io/?session-key=${originalEnv.SESSION_KEY_CURR}`,
`https://solana-rpc.web.helium.io/?session-key=${originalEnv.SESSION_KEY}`,
{
method: 'POST',
headers: {
Expand All @@ -47,7 +46,7 @@ describe('index', () => {
(fetch as jest.MockedFunction<typeof fetch>).mockImplementation();

const request = new Request(
`https://solana-rpc.web.helium.io/?session-key=${originalEnv.SESSION_KEY_CURR}`,
`https://solana-rpc.web.helium.io/?session-key=${originalEnv.SESSION_KEY}`,
{
method: 'POST',
headers: {
Expand All @@ -65,60 +64,14 @@ describe('index', () => {
expect(fetch).not.toBeCalled();
});

test('handles invalid session-key',async () => {
(errorHandler as jest.Mock).mockImplementation();
(fetch as jest.MockedFunction<typeof fetch>).mockImplementation();

const request = new Request(
`https://solana-rpc.web.helium.io/?session-key=fail-me`,
{
method: 'POST',
headers: {
Host: 'solana-rpc.web.helium.io',
},
body: '{"jsonrpc": "2.0", }',
}
) as unknown as Parameters<typeof worker.fetch>[0];

const resp = await worker.fetch(request, originalEnv);

expect(resp.status).toEqual(404);

expect(errorHandler).not.toBeCalled();
expect(fetch).not.toBeCalled();
});

test('handles SESSION_KEY_PREV',async () => {
(errorHandler as jest.Mock).mockImplementation();
(fetch as jest.MockedFunction<typeof fetch>).mockImplementation();

const request = new Request(
`https://solana-rpc.web.helium.io/?session-key=${originalEnv.SESSION_KEY_PREV}}`,
{
method: 'POST',
headers: {
Host: 'solana-rpc.web.helium.io',
},
body: '{"jsonrpc": "2.0", }',
}
) as unknown as Parameters<typeof worker.fetch>[0];

const resp = await worker.fetch(request, originalEnv);

expect(resp.status).toEqual(404);

expect(errorHandler).not.toBeCalled();
expect(fetch).not.toBeCalled();
});

test('does not invoke errorHandler', async () => {
(errorHandler as jest.Mock).mockImplementation();
(fetch as jest.MockedFunction<typeof fetch>).mockResolvedValue(
new Response(undefined, { status: 200 })
);

const request = new Request(
`https://solana-rpc.web.helium.io/?session-key=${originalEnv.SESSION_KEY_CURR}`,
`https://solana-rpc.web.helium.io/?session-key=${originalEnv.SESSION_KEY}`,
{
method: 'POST',
headers: {
Expand All @@ -140,7 +93,7 @@ describe('index', () => {
);

const request = new Request(
`https://solana-rpc.web.helium.io/?session-key=${originalEnv.SESSION_KEY_CURR}`,
`https://solana-rpc.web.helium.io/?session-key=${originalEnv.SESSION_KEY}`,
{
method: 'POST',
headers: {
Expand Down
3 changes: 1 addition & 2 deletions src/tests/putLogEventsCommandBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ describe('putLogEventsCommandBuilder', () => {
const originalEnv = {
CORS_ALLOW_ORIGIN: process.env.CORS_ALLOW_ORIGIN,
HELIUS_API_KEY: process.env.HELIUS_API_KEY,
SESSION_KEY_PREV: process.env.SESSION_KEY_PREV,
SESSION_KEY_CURR: process.env.SESSION_KEY_CURR,
SESSION_KEY: process.env.SESSION_KEY,
AWS_REGION: process.env.AWS_REGION,
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
Expand Down
3 changes: 1 addition & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
export interface Env {
CORS_ALLOW_ORIGIN: string;
HELIUS_API_KEY: string;
SESSION_KEY_PREV: string;
SESSION_KEY_CURR: string;
SESSION_KEY: string;
AWS_REGION: string;
AWS_ACCESS_KEY_ID: string;
AWS_SECRET_ACCESS_KEY: string;
Expand Down

0 comments on commit 46a53c7

Please sign in to comment.