-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement sgr cloud tunnel (without provisioning)
The `sgr cloud tunnel` command now works with the following caveats: - The rathole client binary must be copied into SG_CONFIG_DIR manually. - When the provisioning endpoint works, the server's TLS cert hostname and public management address will be provided via GQL call, currently its hardcoded.
- Loading branch information
Showing
4 changed files
with
136 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import os | ||
import subprocess | ||
import sys | ||
from os import path | ||
from typing import IO, Optional, cast | ||
|
||
from splitgraph.cloud.project.models import Repository | ||
|
||
RATHOLE_CLIENT_FILENAME = "rathole-client.toml" | ||
|
||
RATHOLE_CLIENT_TEMPLATE = """ | ||
[client] | ||
remote_addr = "{rathole_server_management_address}" | ||
[client.transport] | ||
type = "tls" | ||
[client.transport.tls] | ||
{trusted_root_line} | ||
hostname = "{hostname}" | ||
[client.services.{servicename}] | ||
local_addr = "{local_address}" | ||
# token is provisioner JWT token | ||
token = "{provisioning_token}" | ||
""" | ||
|
||
|
||
def get_rathole_client_config( | ||
rathole_server_management_address: str, | ||
hostname: str, | ||
local_address: str, | ||
provisioning_token: str, | ||
namespace: str, | ||
repository: str, | ||
trusted_root: Optional[str], | ||
) -> str: | ||
trusted_root_line = f'trusted_root = "{trusted_root}"' if trusted_root else "" | ||
# 'servicename' is the suffix of a section header in the rathol TOML config. | ||
# It must match in the rathole client and server configs. | ||
# The rathole server config has sections for _all_ users' tunnels. If | ||
# namespace: 'a' repository: 'bc' and | ||
# namespace: 'ab' repository 'c' result in the same section config then | ||
# users could query each others' tunnels, so we need to disambiguate these | ||
# scenarios. Unfortuantely, TOML section header characters are as | ||
# restrictive as valid namespace / repo names. As a workaround, the length | ||
# of each field is prefixed with the field padded to 4 chars, | ||
# avoiding the collision. | ||
servicename = f"{len(namespace):04}{namespace}{len(repository):04}{repository}" | ||
return RATHOLE_CLIENT_TEMPLATE.format( | ||
rathole_server_management_address=rathole_server_management_address, | ||
hostname=hostname, | ||
local_address=local_address, | ||
provisioning_token=provisioning_token, | ||
servicename=servicename, | ||
trusted_root_line=trusted_root_line, | ||
) | ||
|
||
|
||
def write_rathole_client_config( | ||
provisioning_token: str, repository: Repository, config_dir: str | ||
) -> str: | ||
# verify repository is external | ||
if not repository.external or not repository.external.tunnel: | ||
raise Exception("Repository %s not a tunneled external repository" % (repository)) | ||
# TODO: instead of printing token, make gql call to provision tunnel | ||
print("Got provisioning token %s" % provisioning_token) | ||
# in production, this will be None, but for dev instances, we need to | ||
# specify rootCA.pem | ||
trusted_root = os.environ["REQUESTS_CA_BUNDLE"] or os.environ["SSL_CERT_FILE"] | ||
rathole_client_config = get_rathole_client_config( | ||
# TODO: replace these stub values with response of provisioning call | ||
rathole_server_management_address="34.70.46.40:2333", | ||
hostname="www.splitgraph.test", | ||
local_address=f"{repository.external.params['host']}:{repository.external.params['port']}", | ||
provisioning_token=provisioning_token, | ||
namespace=repository.namespace, | ||
repository=repository.repository, | ||
trusted_root=trusted_root, | ||
) | ||
config_filename = path.join(config_dir, RATHOLE_CLIENT_FILENAME) | ||
with open(config_filename, "w") as f: | ||
f.write(rathole_client_config) | ||
return config_filename | ||
|
||
|
||
# inspired by https://stackoverflow.com/questions/18421757/live-output-from-subprocess-command | ||
def launch_rathole_client(rathole_client_binary_path, rathole_client_config_path): | ||
process = subprocess.Popen( | ||
[rathole_client_binary_path, "--client", rathole_client_config_path], | ||
stdout=subprocess.PIPE, | ||
stderr=subprocess.STDOUT, | ||
) | ||
# pipe rathole process output to stdout | ||
for c in iter(lambda: cast(IO[bytes], process.stdout).read(1), b""): # nomypy | ||
sys.stdout.buffer.write(c) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters