Skip to content

Commit 2267e9e

Browse files
authored
fix: add localnet proxy to add Access-Control-Allow-Private-Network header (#523)
* fix: add localnet proxy to add Access-Control-Allow-Private-Network header * chore: fix audit
1 parent 872f6b1 commit 2267e9e

15 files changed

+326
-32
lines changed

poetry.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/algokit/core/sandbox.py

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def __init__(self, name: str = SANDBOX_BASE_NAME) -> None:
5656
self._latest_yaml = get_docker_compose_yml(name=f"algokit_{self.name}")
5757
self._latest_config_json = get_config_json()
5858
self._latest_algod_network_template = get_algod_network_template()
59+
self._latest_proxy_config = get_proxy_config()
5960

6061
@property
6162
def compose_file_path(self) -> Path:
@@ -73,6 +74,10 @@ def algod_config_file_path(self) -> Path:
7374
def algod_network_template_file_path(self) -> Path:
7475
return self.directory / "algod_network_template.json"
7576

77+
@property
78+
def proxy_config_file_path(self) -> Path:
79+
return self.directory / "nginx.conf"
80+
7681
@classmethod
7782
def from_environment(cls) -> ComposeSandbox | None:
7883
try:
@@ -128,6 +133,8 @@ def compose_file_status(self) -> ComposeFileStatus:
128133
compose_content = self.compose_file_path.read_text()
129134
config_content = self.algod_config_file_path.read_text()
130135
algod_network_template_content = self.algod_network_template_file_path.read_text()
136+
proxy_config_content = self.proxy_config_file_path.read_text()
137+
131138
except FileNotFoundError:
132139
# treat as out of date if compose file exists but algod config doesn't
133140
# so that existing setups aren't suddenly reset
@@ -139,6 +146,7 @@ def compose_file_status(self) -> ComposeFileStatus:
139146
compose_content == self._latest_yaml
140147
and config_content == self._latest_config_json
141148
and algod_network_template_content == self._latest_algod_network_template
149+
and proxy_config_content == self._latest_proxy_config
142150
):
143151
return ComposeFileStatus.UP_TO_DATE
144152
else:
@@ -149,6 +157,7 @@ def write_compose_file(self) -> None:
149157
self.compose_file_path.write_text(self._latest_yaml)
150158
self.algod_config_file_path.write_text(self._latest_config_json)
151159
self.algod_network_template_file_path.write_text(self._latest_algod_network_template)
160+
self.proxy_config_file_path.write_text(self._latest_proxy_config)
152161

153162
def _run_compose_command(
154163
self,
@@ -424,6 +433,74 @@ def get_conduit_yaml() -> str:
424433
"""
425434

426435

436+
def get_proxy_config(algod_port: int = DEFAULT_ALGOD_PORT, kmd_port: int = 4002) -> str:
437+
return f"""worker_processes 1;
438+
439+
events {{
440+
worker_connections 1024;
441+
}}
442+
443+
http {{
444+
access_log off;
445+
446+
map $request_method$http_access_control_request_private_network $cors_allow_private_network {{
447+
"OPTIONStrue" "true";
448+
default "";
449+
}}
450+
451+
add_header Access-Control-Allow-Private-Network $cors_allow_private_network;
452+
453+
upstream algod_api {{
454+
zone upstreams 64K;
455+
server algod:8080 max_fails=1 fail_timeout=2s;
456+
keepalive 2;
457+
}}
458+
459+
upstream kmd_api {{
460+
zone upstreams 64K;
461+
server algod:7833 max_fails=1 fail_timeout=2s;
462+
keepalive 2;
463+
}}
464+
465+
upstream indexer_api {{
466+
zone upstreams 64K;
467+
server indexer:8980 max_fails=1 fail_timeout=2s;
468+
keepalive 2;
469+
}}
470+
471+
server {{
472+
listen {algod_port};
473+
474+
location / {{
475+
proxy_set_header Host $host;
476+
proxy_pass http://algod_api;
477+
proxy_pass_header Server;
478+
}}
479+
}}
480+
481+
server {{
482+
listen {kmd_port};
483+
484+
location / {{
485+
proxy_set_header Host $host;
486+
proxy_pass http://kmd_api;
487+
proxy_pass_header Server;
488+
}}
489+
}}
490+
491+
server {{
492+
listen 8980;
493+
494+
location / {{
495+
proxy_set_header Host $host;
496+
proxy_pass http://indexer_api;
497+
proxy_pass_header Server;
498+
}}
499+
}}
500+
}}
501+
"""
502+
503+
427504
def get_docker_compose_yml(
428505
name: str = "algokit_sandbox",
429506
algod_port: int = DEFAULT_ALGOD_PORT,
@@ -437,8 +514,8 @@ def get_docker_compose_yml(
437514
container_name: "{name}_algod"
438515
image: {ALGORAND_IMAGE}
439516
ports:
440-
- {algod_port}:8080
441-
- {kmd_port}:7833
517+
- 8080
518+
- 7833
442519
- {tealdbg_port}:9392
443520
environment:
444521
START_KMD: 1
@@ -483,13 +560,29 @@ def get_docker_compose_yml(
483560
container_name: "{name}_indexer"
484561
image: {INDEXER_IMAGE}
485562
ports:
486-
- "8980:8980"
563+
- 8980
487564
restart: unless-stopped
488565
command: daemon --enable-all-parameters
489566
environment:
490567
INDEXER_POSTGRES_CONNECTION_STRING: "host=indexer-db port=5432 user=algorand password=algorand dbname=indexerdb sslmode=disable"
491568
depends_on:
492569
- conduit
570+
571+
proxy:
572+
container_name: "{name}_proxy"
573+
image: nginx:1.27.0-alpine
574+
restart: unless-stopped
575+
ports:
576+
- {algod_port}:{algod_port}
577+
- {kmd_port}:{kmd_port}
578+
- 8980:8980
579+
volumes:
580+
- type: bind
581+
source: ./nginx.conf
582+
target: /etc/nginx/nginx.conf
583+
depends_on:
584+
- algod
585+
- indexer
493586
""" # noqa: E501
494587

495588

tests/goal/test_goal.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
from subprocess import CompletedProcess
44

55
import pytest
6-
from algokit.core.sandbox import ALGOD_HEALTH_URL, get_algod_network_template, get_config_json, get_docker_compose_yml
6+
from algokit.core.sandbox import (
7+
ALGOD_HEALTH_URL,
8+
get_algod_network_template,
9+
get_config_json,
10+
get_docker_compose_yml,
11+
get_proxy_config,
12+
)
713
from pytest_httpx import HTTPXMock
814
from pytest_mock import MockerFixture
915

@@ -43,6 +49,7 @@ def _setup_latest_dummy_compose(app_dir_mock: AppDirs) -> None:
4349
(app_dir_mock.app_config_dir / "sandbox" / "docker-compose.yml").write_text(get_docker_compose_yml())
4450
(app_dir_mock.app_config_dir / "sandbox" / "algod_config.json").write_text(get_config_json())
4551
(app_dir_mock.app_config_dir / "sandbox" / "algod_network_template.json").write_text(get_algod_network_template())
52+
(app_dir_mock.app_config_dir / "sandbox" / "nginx.conf").write_text(get_proxy_config())
4653

4754

4855
@pytest.fixture()

tests/localnet/test_localnet_console.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from subprocess import CompletedProcess
33

44
import pytest
5-
from algokit.core.sandbox import get_algod_network_template, get_config_json, get_docker_compose_yml
5+
from algokit.core.sandbox import get_algod_network_template, get_config_json, get_docker_compose_yml, get_proxy_config
66
from pytest_mock import MockerFixture
77

88
from tests.goal.test_goal import _normalize_output
@@ -25,6 +25,7 @@ def test_goal_console(
2525
(app_dir_mock.app_config_dir / "sandbox" / "docker-compose.yml").write_text(get_docker_compose_yml())
2626
(app_dir_mock.app_config_dir / "sandbox" / "algod_config.json").write_text(get_config_json())
2727
(app_dir_mock.app_config_dir / "sandbox" / "algod_network_template.json").write_text(get_algod_network_template())
28+
(app_dir_mock.app_config_dir / "sandbox" / "nginx.conf").write_text(get_proxy_config())
2829

2930
mocker.patch("algokit.core.proc.subprocess_run").return_value = CompletedProcess(
3031
["docker", "exec"], 0, "STDOUT+STDERR"

tests/localnet/test_localnet_reset.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json
22

33
import pytest
4-
from algokit.core.sandbox import get_algod_network_template, get_config_json, get_docker_compose_yml
4+
from algokit.core.sandbox import get_algod_network_template, get_config_json, get_docker_compose_yml, get_proxy_config
55

66
from tests import get_combined_verify_output
77
from tests.utils.app_dir_mock import AppDirs
@@ -52,6 +52,7 @@ def test_localnet_reset_with_existing_sandbox_with_up_to_date_config(app_dir_moc
5252
(app_dir_mock.app_config_dir / "sandbox" / "docker-compose.yml").write_text(get_docker_compose_yml())
5353
(app_dir_mock.app_config_dir / "sandbox" / "algod_config.json").write_text(get_config_json())
5454
(app_dir_mock.app_config_dir / "sandbox" / "algod_network_template.json").write_text(get_algod_network_template())
55+
(app_dir_mock.app_config_dir / "sandbox" / "nginx.conf").write_text(get_proxy_config())
5556

5657
result = invoke("localnet reset")
5758

@@ -83,6 +84,7 @@ def test_localnet_reset_with_named_sandbox_config(app_dir_mock: AppDirs, proc_mo
8384
(app_dir_mock.app_config_dir / "sandbox_test" / "algod_network_template.json").write_text(
8485
get_algod_network_template()
8586
)
87+
(app_dir_mock.app_config_dir / "sandbox_test" / "nginx.conf").write_text(get_proxy_config())
8688

8789
result = invoke("localnet reset")
8890

@@ -98,6 +100,7 @@ def test_localnet_reset_with_existing_sandbox_with_up_to_date_config_with_pull(a
98100
(app_dir_mock.app_config_dir / "sandbox" / "docker-compose.yml").write_text(get_docker_compose_yml())
99101
(app_dir_mock.app_config_dir / "sandbox" / "algod_config.json").write_text(get_config_json())
100102
(app_dir_mock.app_config_dir / "sandbox" / "algod_network_template.json").write_text(get_algod_network_template())
103+
(app_dir_mock.app_config_dir / "sandbox" / "nginx.conf").write_text(get_proxy_config())
101104

102105
result = invoke("localnet reset --update")
103106

tests/localnet/test_localnet_reset.test_localnet_reset_with_existing_sandbox_with_out_of_date_config.approved.txt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ services:
3333
container_name: "algokit_sandbox_algod"
3434
image: algorand/algod:latest
3535
ports:
36-
- 4001:8080
37-
- 4002:7833
36+
- 8080
37+
- 7833
3838
- 9392:9392
3939
environment:
4040
START_KMD: 1
@@ -79,13 +79,29 @@ services:
7979
container_name: "algokit_sandbox_indexer"
8080
image: algorand/indexer:latest
8181
ports:
82-
- "8980:8980"
82+
- 8980
8383
restart: unless-stopped
8484
command: daemon --enable-all-parameters
8585
environment:
8686
INDEXER_POSTGRES_CONNECTION_STRING: "host=indexer-db port=5432 user=algorand password=algorand dbname=indexerdb sslmode=disable"
8787
depends_on:
8888
- conduit
8989

90+
proxy:
91+
container_name: "algokit_sandbox_proxy"
92+
image: nginx:1.27.0-alpine
93+
restart: unless-stopped
94+
ports:
95+
- 4001:4001
96+
- 4002:4002
97+
- 8980:8980
98+
volumes:
99+
- type: bind
100+
source: ./nginx.conf
101+
target: /etc/nginx/nginx.conf
102+
depends_on:
103+
- algod
104+
- indexer
105+
90106
{app_config}/sandbox/algod_config.json
91107
{ "Version": 12, "GossipFanout": 1, "EndpointAddress": "0.0.0.0:8080", "DNSBootstrapID": "", "IncomingConnectionsLimit": 0, "Archival":true, "isIndexerActive":false, "EnableDeveloperAPI":true}

tests/localnet/test_localnet_reset.test_localnet_reset_without_existing_sandbox.approved.txt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ services:
2525
container_name: "algokit_sandbox_algod"
2626
image: algorand/algod:latest
2727
ports:
28-
- 4001:8080
29-
- 4002:7833
28+
- 8080
29+
- 7833
3030
- 9392:9392
3131
environment:
3232
START_KMD: 1
@@ -71,10 +71,26 @@ services:
7171
container_name: "algokit_sandbox_indexer"
7272
image: algorand/indexer:latest
7373
ports:
74-
- "8980:8980"
74+
- 8980
7575
restart: unless-stopped
7676
command: daemon --enable-all-parameters
7777
environment:
7878
INDEXER_POSTGRES_CONNECTION_STRING: "host=indexer-db port=5432 user=algorand password=algorand dbname=indexerdb sslmode=disable"
7979
depends_on:
8080
- conduit
81+
82+
proxy:
83+
container_name: "algokit_sandbox_proxy"
84+
image: nginx:1.27.0-alpine
85+
restart: unless-stopped
86+
ports:
87+
- 4001:4001
88+
- 4002:4002
89+
- 8980:8980
90+
volumes:
91+
- type: bind
92+
source: ./nginx.conf
93+
target: /etc/nginx/nginx.conf
94+
depends_on:
95+
- algod
96+
- indexer

tests/localnet/test_localnet_start.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
get_algod_network_template,
1010
get_config_json,
1111
get_docker_compose_yml,
12+
get_proxy_config,
1213
)
1314
from pytest_httpx import HTTPXMock
1415

@@ -142,6 +143,7 @@ def test_localnet_start_up_to_date_definition(app_dir_mock: AppDirs) -> None:
142143
(app_dir_mock.app_config_dir / "sandbox" / "docker-compose.yml").write_text(get_docker_compose_yml())
143144
(app_dir_mock.app_config_dir / "sandbox" / "algod_config.json").write_text(get_config_json())
144145
(app_dir_mock.app_config_dir / "sandbox" / "algod_network_template.json").write_text(get_algod_network_template())
146+
(app_dir_mock.app_config_dir / "sandbox" / "nginx.conf").write_text(get_proxy_config())
145147

146148
result = invoke("localnet start")
147149

0 commit comments

Comments
 (0)