TUN-7245: Add bastion flag to origin service check
This commit is contained in:
parent
bf3136debb
commit
93f8f6b55c
|
@ -199,7 +199,7 @@ func TunnelCommand(c *cli.Context) error {
|
||||||
// Run a quick tunnel
|
// Run a quick tunnel
|
||||||
// A unauthenticated named tunnel hosted on <random>.<quick-tunnels-service>.com
|
// A unauthenticated named tunnel hosted on <random>.<quick-tunnels-service>.com
|
||||||
// We don't support running proxy-dns and a quick tunnel at the same time as the same process
|
// We don't support running proxy-dns and a quick tunnel at the same time as the same process
|
||||||
shouldRunQuickTunnel := c.IsSet("url") || c.IsSet("hello-world")
|
shouldRunQuickTunnel := c.IsSet("url") || c.IsSet(ingress.HelloWorldFlag)
|
||||||
if !c.IsSet("proxy-dns") && c.String("quick-service") != "" && shouldRunQuickTunnel {
|
if !c.IsSet("proxy-dns") && c.String("quick-service") != "" && shouldRunQuickTunnel {
|
||||||
return RunQuickTunnel(sc)
|
return RunQuickTunnel(sc)
|
||||||
}
|
}
|
||||||
|
@ -215,6 +215,9 @@ func TunnelCommand(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.IsSet("proxy-dns") {
|
if c.IsSet("proxy-dns") {
|
||||||
|
if shouldRunQuickTunnel {
|
||||||
|
return fmt.Errorf("running a quick tunnel with `proxy-dns` is not supported")
|
||||||
|
}
|
||||||
// NamedTunnelProperties are nil since proxy dns server does not need it.
|
// NamedTunnelProperties are nil since proxy dns server does not need it.
|
||||||
// This is supported for legacy reasons: dns proxy server is not a tunnel and ideally should
|
// This is supported for legacy reasons: dns proxy server is not a tunnel and ideally should
|
||||||
// not run as part of cloudflared tunnel.
|
// not run as part of cloudflared tunnel.
|
||||||
|
@ -786,7 +789,7 @@ func configureProxyFlags(shouldHide bool) []cli.Flag {
|
||||||
Hidden: shouldHide,
|
Hidden: shouldHide,
|
||||||
}),
|
}),
|
||||||
altsrc.NewBoolFlag(&cli.BoolFlag{
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
Name: "hello-world",
|
Name: ingress.HelloWorldFlag,
|
||||||
Value: false,
|
Value: false,
|
||||||
Usage: "Run Hello World Server",
|
Usage: "Run Hello World Server",
|
||||||
EnvVars: []string{"TUNNEL_HELLO_WORLD"},
|
EnvVars: []string{"TUNNEL_HELLO_WORLD"},
|
||||||
|
|
|
@ -124,7 +124,7 @@ func isSecretEnvVar(key string) bool {
|
||||||
func dnsProxyStandAlone(c *cli.Context, namedTunnel *connection.NamedTunnelProperties) bool {
|
func dnsProxyStandAlone(c *cli.Context, namedTunnel *connection.NamedTunnelProperties) bool {
|
||||||
return c.IsSet("proxy-dns") &&
|
return c.IsSet("proxy-dns") &&
|
||||||
!(c.IsSet("name") || // adhoc-named tunnel
|
!(c.IsSet("name") || // adhoc-named tunnel
|
||||||
c.IsSet("hello-world") || // quick or named tunnel
|
c.IsSet(ingress.HelloWorldFlag) || // quick or named tunnel
|
||||||
namedTunnel != nil) // named tunnel
|
namedTunnel != nil) // named tunnel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,17 +231,16 @@ func prepareTunnelConfig(
|
||||||
if err != nil && err != ingress.ErrNoIngressRules {
|
if err != nil && err != ingress.ErrNoIngressRules {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if c.IsSet("url") {
|
if c.IsSet("url") || c.IsSet(ingress.HelloWorldFlag) || c.IsSet(config.BastionFlag) {
|
||||||
// Ingress rules cannot be provided with --url flag
|
// Ingress rules cannot be provided with --url, --hello-world or --bastion flag
|
||||||
if !ingressRules.IsEmpty() {
|
if !ingressRules.IsEmpty() {
|
||||||
return nil, nil, ingress.ErrURLIncompatibleWithIngress
|
return nil, nil, ingress.ErrURLIncompatibleWithIngress
|
||||||
} else {
|
}
|
||||||
// Only for quick or adhoc tunnels will we attempt to parse:
|
// Only for quick or adhoc tunnels will we attempt to parse:
|
||||||
// --url, --hello-world, or --unix-socket flag for a tunnel ingress rule
|
// --url, --hello-world, --bastion, or --unix-socket flag for a tunnel ingress rule
|
||||||
ingressRules, err = ingress.NewSingleOrigin(c, false)
|
ingressRules, err = ingress.NewSingleOrigin(c, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,10 @@ class NamedTunnelBaseConfig(BaseConfig):
|
||||||
|
|
||||||
def merge_config(self, additional):
|
def merge_config(self, additional):
|
||||||
config = super(NamedTunnelBaseConfig, self).merge_config(additional)
|
config = super(NamedTunnelBaseConfig, self).merge_config(additional)
|
||||||
config['tunnel'] = self.tunnel
|
if 'tunnel' not in config:
|
||||||
config['credentials-file'] = self.credentials_file
|
config['tunnel'] = self.tunnel
|
||||||
|
if 'credentials-file' not in config:
|
||||||
|
config['credentials-file'] = self.credentials_file
|
||||||
# In some cases we want to override default ingress, such as in config tests
|
# In some cases we want to override default ingress, such as in config tests
|
||||||
if 'ingress' not in config:
|
if 'ingress' not in config:
|
||||||
config['ingress'] = self.ingress
|
config['ingress'] = self.ingress
|
||||||
|
@ -84,28 +86,9 @@ class NamedTunnelConfig(NamedTunnelBaseConfig):
|
||||||
def get_credentials_json(self):
|
def get_credentials_json(self):
|
||||||
with open(self.credentials_file) as json_file:
|
with open(self.credentials_file) as json_file:
|
||||||
return json.load(json_file)
|
return json.load(json_file)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ClassicTunnelBaseConfig(BaseConfig):
|
class QuickTunnelConfig(BaseConfig):
|
||||||
hostname: str = None
|
|
||||||
origincert: str = None
|
|
||||||
|
|
||||||
def __post_init__(self):
|
|
||||||
if self.hostname is None:
|
|
||||||
raise TypeError("Field tunnel is not set")
|
|
||||||
if self.origincert is None:
|
|
||||||
raise TypeError("Field credentials_file is not set")
|
|
||||||
|
|
||||||
def merge_config(self, additional):
|
|
||||||
config = super(ClassicTunnelBaseConfig, self).merge_config(additional)
|
|
||||||
config['hostname'] = self.hostname
|
|
||||||
config['origincert'] = self.origincert
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class ClassicTunnelConfig(ClassicTunnelBaseConfig):
|
|
||||||
full_config: dict = None
|
full_config: dict = None
|
||||||
additional_config: InitVar[dict] = {}
|
additional_config: InitVar[dict] = {}
|
||||||
|
|
||||||
|
@ -115,10 +98,6 @@ class ClassicTunnelConfig(ClassicTunnelBaseConfig):
|
||||||
object.__setattr__(self, 'full_config',
|
object.__setattr__(self, 'full_config',
|
||||||
self.merge_config(additional_config))
|
self.merge_config(additional_config))
|
||||||
|
|
||||||
def get_url(self):
|
|
||||||
return "https://" + self.hostname
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ProxyDnsConfig(BaseConfig):
|
class ProxyDnsConfig(BaseConfig):
|
||||||
full_config = {
|
full_config = {
|
||||||
|
|
|
@ -5,14 +5,14 @@ from time import sleep
|
||||||
import pytest
|
import pytest
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from config import NamedTunnelConfig, ClassicTunnelConfig, ProxyDnsConfig
|
from config import NamedTunnelConfig, ProxyDnsConfig, QuickTunnelConfig
|
||||||
from constants import BACKOFF_SECS, PROXY_DNS_PORT
|
from constants import BACKOFF_SECS, PROXY_DNS_PORT
|
||||||
from util import LOGGER
|
from util import LOGGER
|
||||||
|
|
||||||
|
|
||||||
class CfdModes(Enum):
|
class CfdModes(Enum):
|
||||||
NAMED = auto()
|
NAMED = auto()
|
||||||
CLASSIC = auto()
|
QUICK = auto()
|
||||||
PROXY_DNS = auto()
|
PROXY_DNS = auto()
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,12 +42,10 @@ def component_tests_config():
|
||||||
tunnel=config['tunnel'],
|
tunnel=config['tunnel'],
|
||||||
credentials_file=config['credentials_file'],
|
credentials_file=config['credentials_file'],
|
||||||
ingress=config['ingress'])
|
ingress=config['ingress'])
|
||||||
elif cfd_mode is CfdModes.CLASSIC:
|
|
||||||
return ClassicTunnelConfig(
|
|
||||||
additional_config=additional_config, cloudflared_binary=config['cloudflared_binary'],
|
|
||||||
hostname=config['classic_hostname'], origincert=config['origincert'])
|
|
||||||
elif cfd_mode is CfdModes.PROXY_DNS:
|
elif cfd_mode is CfdModes.PROXY_DNS:
|
||||||
return ProxyDnsConfig(cloudflared_binary=config['cloudflared_binary'])
|
return ProxyDnsConfig(cloudflared_binary=config['cloudflared_binary'])
|
||||||
|
elif cfd_mode is CfdModes.QUICK:
|
||||||
|
return QuickTunnelConfig(additional_config=additional_config, cloudflared_binary=config['cloudflared_binary'])
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Unknown cloudflared mode {cfd_mode}")
|
raise Exception(f"Unknown cloudflared mode {cfd_mode}")
|
||||||
|
|
||||||
|
|
|
@ -7,4 +7,4 @@ PROXY_DNS_PORT = 9053
|
||||||
|
|
||||||
|
|
||||||
def protocols():
|
def protocols():
|
||||||
return ["h2mux", "http2", "quic"]
|
return ["http2", "quic"]
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import requests
|
||||||
|
from conftest import CfdModes
|
||||||
|
from constants import METRICS_PORT
|
||||||
|
from util import LOGGER, start_cloudflared, wait_tunnel_ready, get_quicktunnel_url, send_requests
|
||||||
|
|
||||||
|
class TestCLI:
|
||||||
|
def test_quick_tunnel(self, tmp_path, component_tests_config):
|
||||||
|
config = component_tests_config(cfd_mode=CfdModes.QUICK, run_proxy_dns=False)
|
||||||
|
LOGGER.debug(config)
|
||||||
|
with start_cloudflared(tmp_path, config, cfd_args=["--hello-world"], new_process=True):
|
||||||
|
wait_tunnel_ready(require_min_connections=4)
|
||||||
|
url = get_quicktunnel_url()
|
||||||
|
send_requests(url, 3, True)
|
||||||
|
|
||||||
|
def test_quick_tunnel_url(self, tmp_path, component_tests_config):
|
||||||
|
config = component_tests_config(cfd_mode=CfdModes.QUICK, run_proxy_dns=False)
|
||||||
|
LOGGER.debug(config)
|
||||||
|
with start_cloudflared(tmp_path, config, cfd_args=["--url", f"http://localhost:{METRICS_PORT}/"], new_process=True):
|
||||||
|
wait_tunnel_ready()
|
||||||
|
url = get_quicktunnel_url()
|
||||||
|
send_requests(url+"/ready", 3, True)
|
||||||
|
|
||||||
|
def test_quick_tunnel_proxy_dns_url(self, tmp_path, component_tests_config):
|
||||||
|
config = component_tests_config(cfd_mode=CfdModes.QUICK, run_proxy_dns=True)
|
||||||
|
LOGGER.debug(config)
|
||||||
|
failed_start = start_cloudflared(tmp_path, config, cfd_args=["--url", f"http://localhost:{METRICS_PORT}/"], expect_success=False)
|
||||||
|
assert failed_start.returncode == 1, "Expected cloudflared to fail to run with `proxy-dns` and `hello-world`"
|
||||||
|
|
||||||
|
def test_quick_tunnel_proxy_dns_hello_world(self, tmp_path, component_tests_config):
|
||||||
|
config = component_tests_config(cfd_mode=CfdModes.QUICK, run_proxy_dns=True)
|
||||||
|
LOGGER.debug(config)
|
||||||
|
failed_start = start_cloudflared(tmp_path, config, cfd_args=["--hello-world"], expect_success=False)
|
||||||
|
assert failed_start.returncode == 1, "Expected cloudflared to fail to run with `proxy-dns` and `url`"
|
|
@ -35,7 +35,7 @@ def write_config(directory, config):
|
||||||
|
|
||||||
|
|
||||||
def start_cloudflared(directory, config, cfd_args=["run"], cfd_pre_args=["tunnel"], new_process=False,
|
def start_cloudflared(directory, config, cfd_args=["run"], cfd_pre_args=["tunnel"], new_process=False,
|
||||||
allow_input=False, capture_output=True, root=False, skip_config_flag=False):
|
allow_input=False, capture_output=True, root=False, skip_config_flag=False, expect_success=True):
|
||||||
|
|
||||||
config_path = None
|
config_path = None
|
||||||
if not skip_config_flag:
|
if not skip_config_flag:
|
||||||
|
@ -46,7 +46,7 @@ def start_cloudflared(directory, config, cfd_args=["run"], cfd_pre_args=["tunnel
|
||||||
if new_process:
|
if new_process:
|
||||||
return run_cloudflared_background(cmd, allow_input, capture_output)
|
return run_cloudflared_background(cmd, allow_input, capture_output)
|
||||||
# By setting check=True, it will raise an exception if the process exits with non-zero exit code
|
# By setting check=True, it will raise an exception if the process exits with non-zero exit code
|
||||||
return subprocess.run(cmd, check=True, capture_output=capture_output)
|
return subprocess.run(cmd, check=expect_success, capture_output=capture_output)
|
||||||
|
|
||||||
|
|
||||||
def cloudflared_cmd(config, config_path, cfd_args, cfd_pre_args, root):
|
def cloudflared_cmd(config, config_path, cfd_args, cfd_pre_args, root):
|
||||||
|
@ -77,7 +77,18 @@ def run_cloudflared_background(cmd, allow_input, capture_output):
|
||||||
cfd.terminate()
|
cfd.terminate()
|
||||||
if capture_output:
|
if capture_output:
|
||||||
LOGGER.info(f"cloudflared log: {cfd.stderr.read()}")
|
LOGGER.info(f"cloudflared log: {cfd.stderr.read()}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_quicktunnel_url():
|
||||||
|
quicktunnel_url = f'http://localhost:{METRICS_PORT}/quicktunnel'
|
||||||
|
with requests.Session() as s:
|
||||||
|
resp = send_request(s, quicktunnel_url, True)
|
||||||
|
|
||||||
|
hostname = resp.json()["hostname"]
|
||||||
|
assert hostname, \
|
||||||
|
f"Quicktunnel endpoint returned {hostname} but we expected a url"
|
||||||
|
|
||||||
|
return f"https://{hostname}"
|
||||||
|
|
||||||
def wait_tunnel_ready(tunnel_url=None, require_min_connections=1, cfd_logs=None):
|
def wait_tunnel_ready(tunnel_url=None, require_min_connections=1, cfd_logs=None):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -112,7 +112,7 @@ func NewWarpRoutingService(config WarpRoutingConfig) *WarpRoutingService {
|
||||||
|
|
||||||
// Get a single origin service from the CLI/config.
|
// Get a single origin service from the CLI/config.
|
||||||
func parseSingleOriginService(c *cli.Context, allowURLFromArgs bool) (OriginService, error) {
|
func parseSingleOriginService(c *cli.Context, allowURLFromArgs bool) (OriginService, error) {
|
||||||
if c.IsSet("hello-world") {
|
if c.IsSet(HelloWorldFlag) {
|
||||||
return new(helloWorld), nil
|
return new(helloWorld), nil
|
||||||
}
|
}
|
||||||
if c.IsSet(config.BastionFlag) {
|
if c.IsSet(config.BastionFlag) {
|
||||||
|
@ -206,7 +206,7 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq
|
||||||
}
|
}
|
||||||
srv := newStatusCode(statusCode)
|
srv := newStatusCode(statusCode)
|
||||||
service = &srv
|
service = &srv
|
||||||
} else if r.Service == HelloWorldService || r.Service == "hello-world" || r.Service == "helloworld" {
|
} else if r.Service == HelloWorldFlag || r.Service == HelloWorldService {
|
||||||
service = new(helloWorld)
|
service = new(helloWorld)
|
||||||
} else if r.Service == ServiceSocksProxy {
|
} else if r.Service == ServiceSocksProxy {
|
||||||
rules := make([]ipaccess.Rule, len(r.OriginRequest.IPRules))
|
rules := make([]ipaccess.Rule, len(r.OriginRequest.IPRules))
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HelloWorldService = "hello_world"
|
HelloWorldService = "hello_world"
|
||||||
|
HelloWorldFlag = "hello-world"
|
||||||
HttpStatusService = "http_status"
|
HttpStatusService = "http_status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue