Skip to content

Commit

Permalink
fix: add localnet proxy to add Access-Control-Allow-Private-Network h…
Browse files Browse the repository at this point in the history
…eader (#523)

* fix: add localnet proxy to add Access-Control-Allow-Private-Network header
* chore: fix audit
  • Loading branch information
neilcampbell authored Jun 19, 2024
1 parent 872f6b1 commit 2267e9e
Show file tree
Hide file tree
Showing 15 changed files with 326 additions and 32 deletions.
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

99 changes: 96 additions & 3 deletions src/algokit/core/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def __init__(self, name: str = SANDBOX_BASE_NAME) -> None:
self._latest_yaml = get_docker_compose_yml(name=f"algokit_{self.name}")
self._latest_config_json = get_config_json()
self._latest_algod_network_template = get_algod_network_template()
self._latest_proxy_config = get_proxy_config()

@property
def compose_file_path(self) -> Path:
Expand All @@ -73,6 +74,10 @@ def algod_config_file_path(self) -> Path:
def algod_network_template_file_path(self) -> Path:
return self.directory / "algod_network_template.json"

@property
def proxy_config_file_path(self) -> Path:
return self.directory / "nginx.conf"

@classmethod
def from_environment(cls) -> ComposeSandbox | None:
try:
Expand Down Expand Up @@ -128,6 +133,8 @@ def compose_file_status(self) -> ComposeFileStatus:
compose_content = self.compose_file_path.read_text()
config_content = self.algod_config_file_path.read_text()
algod_network_template_content = self.algod_network_template_file_path.read_text()
proxy_config_content = self.proxy_config_file_path.read_text()

except FileNotFoundError:
# treat as out of date if compose file exists but algod config doesn't
# so that existing setups aren't suddenly reset
Expand All @@ -139,6 +146,7 @@ def compose_file_status(self) -> ComposeFileStatus:
compose_content == self._latest_yaml
and config_content == self._latest_config_json
and algod_network_template_content == self._latest_algod_network_template
and proxy_config_content == self._latest_proxy_config
):
return ComposeFileStatus.UP_TO_DATE
else:
Expand All @@ -149,6 +157,7 @@ def write_compose_file(self) -> None:
self.compose_file_path.write_text(self._latest_yaml)
self.algod_config_file_path.write_text(self._latest_config_json)
self.algod_network_template_file_path.write_text(self._latest_algod_network_template)
self.proxy_config_file_path.write_text(self._latest_proxy_config)

def _run_compose_command(
self,
Expand Down Expand Up @@ -424,6 +433,74 @@ def get_conduit_yaml() -> str:
"""


def get_proxy_config(algod_port: int = DEFAULT_ALGOD_PORT, kmd_port: int = 4002) -> str:
return f"""worker_processes 1;
events {{
worker_connections 1024;
}}
http {{
access_log off;
map $request_method$http_access_control_request_private_network $cors_allow_private_network {{
"OPTIONStrue" "true";
default "";
}}
add_header Access-Control-Allow-Private-Network $cors_allow_private_network;
upstream algod_api {{
zone upstreams 64K;
server algod:8080 max_fails=1 fail_timeout=2s;
keepalive 2;
}}
upstream kmd_api {{
zone upstreams 64K;
server algod:7833 max_fails=1 fail_timeout=2s;
keepalive 2;
}}
upstream indexer_api {{
zone upstreams 64K;
server indexer:8980 max_fails=1 fail_timeout=2s;
keepalive 2;
}}
server {{
listen {algod_port};
location / {{
proxy_set_header Host $host;
proxy_pass http://algod_api;
proxy_pass_header Server;
}}
}}
server {{
listen {kmd_port};
location / {{
proxy_set_header Host $host;
proxy_pass http://kmd_api;
proxy_pass_header Server;
}}
}}
server {{
listen 8980;
location / {{
proxy_set_header Host $host;
proxy_pass http://indexer_api;
proxy_pass_header Server;
}}
}}
}}
"""


def get_docker_compose_yml(
name: str = "algokit_sandbox",
algod_port: int = DEFAULT_ALGOD_PORT,
Expand All @@ -437,8 +514,8 @@ def get_docker_compose_yml(
container_name: "{name}_algod"
image: {ALGORAND_IMAGE}
ports:
- {algod_port}:8080
- {kmd_port}:7833
- 8080
- 7833
- {tealdbg_port}:9392
environment:
START_KMD: 1
Expand Down Expand Up @@ -483,13 +560,29 @@ def get_docker_compose_yml(
container_name: "{name}_indexer"
image: {INDEXER_IMAGE}
ports:
- "8980:8980"
- 8980
restart: unless-stopped
command: daemon --enable-all-parameters
environment:
INDEXER_POSTGRES_CONNECTION_STRING: "host=indexer-db port=5432 user=algorand password=algorand dbname=indexerdb sslmode=disable"
depends_on:
- conduit
proxy:
container_name: "{name}_proxy"
image: nginx:1.27.0-alpine
restart: unless-stopped
ports:
- {algod_port}:{algod_port}
- {kmd_port}:{kmd_port}
- 8980:8980
volumes:
- type: bind
source: ./nginx.conf
target: /etc/nginx/nginx.conf
depends_on:
- algod
- indexer
""" # noqa: E501


Expand Down
9 changes: 8 additions & 1 deletion tests/goal/test_goal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
from subprocess import CompletedProcess

import pytest
from algokit.core.sandbox import ALGOD_HEALTH_URL, get_algod_network_template, get_config_json, get_docker_compose_yml
from algokit.core.sandbox import (
ALGOD_HEALTH_URL,
get_algod_network_template,
get_config_json,
get_docker_compose_yml,
get_proxy_config,
)
from pytest_httpx import HTTPXMock
from pytest_mock import MockerFixture

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


@pytest.fixture()
Expand Down
3 changes: 2 additions & 1 deletion tests/localnet/test_localnet_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from subprocess import CompletedProcess

import pytest
from algokit.core.sandbox import get_algod_network_template, get_config_json, get_docker_compose_yml
from algokit.core.sandbox import get_algod_network_template, get_config_json, get_docker_compose_yml, get_proxy_config
from pytest_mock import MockerFixture

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

mocker.patch("algokit.core.proc.subprocess_run").return_value = CompletedProcess(
["docker", "exec"], 0, "STDOUT+STDERR"
Expand Down
5 changes: 4 additions & 1 deletion tests/localnet/test_localnet_reset.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json

import pytest
from algokit.core.sandbox import get_algod_network_template, get_config_json, get_docker_compose_yml
from algokit.core.sandbox import get_algod_network_template, get_config_json, get_docker_compose_yml, get_proxy_config

from tests import get_combined_verify_output
from tests.utils.app_dir_mock import AppDirs
Expand Down Expand Up @@ -52,6 +52,7 @@ def test_localnet_reset_with_existing_sandbox_with_up_to_date_config(app_dir_moc
(app_dir_mock.app_config_dir / "sandbox" / "docker-compose.yml").write_text(get_docker_compose_yml())
(app_dir_mock.app_config_dir / "sandbox" / "algod_config.json").write_text(get_config_json())
(app_dir_mock.app_config_dir / "sandbox" / "algod_network_template.json").write_text(get_algod_network_template())
(app_dir_mock.app_config_dir / "sandbox" / "nginx.conf").write_text(get_proxy_config())

result = invoke("localnet reset")

Expand Down Expand Up @@ -83,6 +84,7 @@ def test_localnet_reset_with_named_sandbox_config(app_dir_mock: AppDirs, proc_mo
(app_dir_mock.app_config_dir / "sandbox_test" / "algod_network_template.json").write_text(
get_algod_network_template()
)
(app_dir_mock.app_config_dir / "sandbox_test" / "nginx.conf").write_text(get_proxy_config())

result = invoke("localnet reset")

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ services:
container_name: "algokit_sandbox_algod"
image: algorand/algod:latest
ports:
- 4001:8080
- 4002:7833
- 8080
- 7833
- 9392:9392
environment:
START_KMD: 1
Expand Down Expand Up @@ -79,13 +79,29 @@ services:
container_name: "algokit_sandbox_indexer"
image: algorand/indexer:latest
ports:
- "8980:8980"
- 8980
restart: unless-stopped
command: daemon --enable-all-parameters
environment:
INDEXER_POSTGRES_CONNECTION_STRING: "host=indexer-db port=5432 user=algorand password=algorand dbname=indexerdb sslmode=disable"
depends_on:
- conduit

proxy:
container_name: "algokit_sandbox_proxy"
image: nginx:1.27.0-alpine
restart: unless-stopped
ports:
- 4001:4001
- 4002:4002
- 8980:8980
volumes:
- type: bind
source: ./nginx.conf
target: /etc/nginx/nginx.conf
depends_on:
- algod
- indexer

{app_config}/sandbox/algod_config.json
{ "Version": 12, "GossipFanout": 1, "EndpointAddress": "0.0.0.0:8080", "DNSBootstrapID": "", "IncomingConnectionsLimit": 0, "Archival":true, "isIndexerActive":false, "EnableDeveloperAPI":true}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ services:
container_name: "algokit_sandbox_algod"
image: algorand/algod:latest
ports:
- 4001:8080
- 4002:7833
- 8080
- 7833
- 9392:9392
environment:
START_KMD: 1
Expand Down Expand Up @@ -71,10 +71,26 @@ services:
container_name: "algokit_sandbox_indexer"
image: algorand/indexer:latest
ports:
- "8980:8980"
- 8980
restart: unless-stopped
command: daemon --enable-all-parameters
environment:
INDEXER_POSTGRES_CONNECTION_STRING: "host=indexer-db port=5432 user=algorand password=algorand dbname=indexerdb sslmode=disable"
depends_on:
- conduit

proxy:
container_name: "algokit_sandbox_proxy"
image: nginx:1.27.0-alpine
restart: unless-stopped
ports:
- 4001:4001
- 4002:4002
- 8980:8980
volumes:
- type: bind
source: ./nginx.conf
target: /etc/nginx/nginx.conf
depends_on:
- algod
- indexer
2 changes: 2 additions & 0 deletions tests/localnet/test_localnet_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
get_algod_network_template,
get_config_json,
get_docker_compose_yml,
get_proxy_config,
)
from pytest_httpx import HTTPXMock

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

result = invoke("localnet start")

Expand Down
Loading

1 comment on commit 2267e9e

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/algokit
   __init__.py15753%6–13, 17–24, 32–34
   __main__.py440%1–7
src/algokit/cli
   __init__.py47394%31–34
   codespace.py50982%28, 114, 137, 150–155
   completions.py108992%63–64, 83, 93–99
   dispenser.py121199%77
   doctor.py53394%146–148
   explore.py501276%34–39, 41–46
   generate.py70396%76–77, 155
   goal.py47198%79
   init.py3092492%493–494, 499–500, 503, 524, 527–529, 540, 544, 602, 628, 657, 690, 699–701, 704–709, 722, 741, 753–754
   localnet.py1523279%65, 86–113, 133–137, 170, 182, 197–207, 220, 271, 292–293
   task.py34391%25–28
src/algokit/cli/common
   utils.py26292%120, 123
src/algokit/cli/project
   bootstrap.py32197%33
   deploy.py992080%47, 49, 101, 124, 146–148, 227, 234, 248–256, 259–268
   link.py891682%60, 65–66, 101–105, 115–120, 148–149, 218–219, 223
   list.py33585%21–23, 51–56
   run.py46393%38, 71, 160
src/algokit/cli/tasks
   analyze.py81199%81
   assets.py821384%65–66, 72, 74–75, 105, 119, 125–126, 132, 134, 136–137
   ipfs.py51884%52, 80, 92, 94–95, 105–107
   mint.py66494%48, 70, 91, 250
   send_transaction.py651085%52–53, 57, 89, 158, 170–174
   sign_transaction.py59886%21, 28–30, 71–72, 109, 123
   transfer.py39392%26, 90, 117
   utils.py994555%26–34, 40–43, 75–76, 100–101, 125–133, 152–162, 209, 258–259, 279–290, 297–299
   vanity_address.py561082%41, 45–48, 112, 114, 121–123
   wallet.py79495%21, 66, 136, 162
src/algokit/core
   codespace.py1756861%34–37, 41–44, 48–71, 111–112, 125–133, 191, 200–202, 210, 216–217, 229–236, 251–298, 311–313, 338–344, 348, 395
   conf.py57984%12, 24, 28, 36, 38, 73–75, 80
   dispenser.py2022687%91, 123–124, 141–149, 191–192, 198–200, 218–219, 259–260, 318, 332–334, 345–346, 356, 369, 384
   doctor.py65789%67–69, 92–94, 134
   generate.py48394%44, 81, 99
   goal.py65494%21, 36–37, 47
   init.py671085%53, 57–62, 70, 81, 88, 108–109
   log_handlers.py68790%50–51, 63, 112–116, 125
   proc.py45198%99
   sandbox.py2612391%32, 89–92, 97, 101–103, 153, 201–208, 219, 595, 611, 636, 644
   typed_client_generation.py1702088%62–64, 103–108, 132, 135–138, 156, 159–162, 229, 232–235
   utils.py1484073%50–51, 57–69, 125–131, 155, 158, 164–177, 198–200, 229–232, 254
src/algokit/core/compilers
   python.py28582%19–20, 25, 49–50
src/algokit/core/config_commands
   container_engine.py412149%24, 29–31, 47–76
   version_prompt.py921485%37–38, 68, 87–90, 108, 118–125, 148
src/algokit/core/project
   __init__.py53394%50, 86, 145
   bootstrap.py120893%47, 126–127, 149, 176, 207–209
   deploy.py54983%61–64, 73–75, 79, 84
   run.py1251588%83, 88, 97–98, 133–134, 138–139, 143, 147, 261–269, 284
src/algokit/core/tasks
   analyze.py93397%105–112, 187
   ipfs.py63789%58–64, 140, 144, 146, 152
   nfd.py491373%25, 31, 34–41, 70–72, 99–101
   vanity_address.py903462%49–50, 54, 59–75, 92–108, 128–131
   wallet.py71593%37, 129, 155–157
src/algokit/core/tasks/mint
   mint.py781087%123–133, 187
   models.py901188%50, 52, 57, 71–74, 85–88
TOTAL463264086% 

Tests Skipped Failures Errors Time
488 0 💤 0 ❌ 0 🔥 35.443s ⏱️

Please sign in to comment.