Skip to content

Commit

Permalink
clients now can use a user-specific default configuration file; easie…
Browse files Browse the repository at this point in the history
…r specification of proxy on the command-line
  • Loading branch information
obiltschnig committed Oct 11, 2023
1 parent 83daf63 commit 99ad706
Show file tree
Hide file tree
Showing 16 changed files with 514 additions and 63 deletions.
22 changes: 20 additions & 2 deletions WebTunnel/WebTunnelAgent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,24 @@ The domain name or IP address of the HTTP proxy server to use.

The port number of the HTTP proxy server to use.

#### http.proxy.url

An alternative form to specify a HTTP proxy, taking a URL, e.g.:

```
http.proxy.url = http://proxy.nowhere.com:8080
```

Note that `http.proxy.host` and `http.proxy.port` will take precedence,
if specified.

This can be used if the proxy address is provided via an environment variable,
like `https_proxy` on Linux:

```
http.proxy.url = ${system.env.https_proxy}
```

#### http.proxy.username

The username for authenticating against the HTTP proxy server. Can be left empty
Expand Down Expand Up @@ -462,7 +480,7 @@ This setting is used to specify the minimum TLS version required by `WebTunnelAg
when connecting to the macchina.io REMOTE server. The following values can be
specified:

* `tlsv1` (TLS version 1.0)
* `tlsv1` or `tlsv1_0` (TLS version 1.0)
* `tlsv1_1` (TLS version 1.1)
* `tlsv1_2` (TLS version 1.2, default)
* `tlsv1_3` (TLS version 1.3)
Expand Down Expand Up @@ -515,7 +533,7 @@ This setting is used to specify the minimum TLS version required by `WebTunnelAg
when connecting to the device web server via HTTPS. The following values can be
specified:

* `tlsv1` (TLS version 1.0)
* `tlsv1` or `tlsv1_0` (TLS version 1.0)
* `tlsv1_1` (TLS version 1.1)
* `tlsv1_2` (TLS version 1.2, default)
* `tlsv1_3` (TLS version 1.3)
Expand Down
4 changes: 4 additions & 0 deletions WebTunnel/WebTunnelAgent/WebTunnelAgent.properties
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ http.proxy.host = proxy.nowhere.com
# The port of the proxy server.
http.proxy.port = 80

# An alternative form to specify a proxy.
# Note: http.proxy.host and http.proxy.port will take precedence if specified.
# http.proxy.url = http://proxy.nowhere.com:8080

# The username for the proxy server, if required.
http.proxy.username =

Expand Down
36 changes: 25 additions & 11 deletions WebTunnel/WebTunnelAgent/src/WebTunnelAgent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ class WebTunnelAgent: public Poco::Util::ServerApplication
_pHTTPClientSession->setTimeout(_httpTimeout);
if (_useProxy && !_proxyHost.empty())
{
logger().debug("Connecting via proxy %s:%hu"s, _proxyHost, _proxyPort);
_pHTTPClientSession->setProxy(_proxyHost, _proxyPort);
if (!_proxyUsername.empty())
{
Expand Down Expand Up @@ -388,7 +389,7 @@ class WebTunnelAgent: public Poco::Util::ServerApplication

logger().debug("Creating WebSocket..."s);
Poco::SharedPtr<Poco::Net::WebSocket> pWebSocket = new Poco::Net::WebSocket(*_pHTTPClientSession, request, response);
if (response.get(SEC_WEBSOCKET_PROTOCOL, "") == WEBTUNNEL_PROTOCOL)
if (response.get(SEC_WEBSOCKET_PROTOCOL, ""s) == WEBTUNNEL_PROTOCOL)
{
logger().debug("WebSocket established. Creating RemotePortForwarder..."s);
pWebSocket->setNoDelay(true);
Expand Down Expand Up @@ -489,7 +490,7 @@ class WebTunnelAgent: public Poco::Util::ServerApplication
}
}
statusChanged(STATUS_DISCONNECTED);
logger().debug("Disconnected.");
logger().debug("Disconnected."s);
}

void onClose(const int& reason)
Expand Down Expand Up @@ -742,7 +743,7 @@ class WebTunnelAgent: public Poco::Util::ServerApplication
throw Poco::InvalidArgumentException(prefix + ".verification", vModeStr);

Poco::Net::Context::Protocols minProto = Poco::Net::Context::PROTO_TLSV1_2;
if (tlsMinVersion == "tlsv1")
if (tlsMinVersion == "tlsv1" || tlsMinVersion == "tlsv1_0")
minProto = Poco::Net::Context::PROTO_TLSV1;
else if (tlsMinVersion == "tlsv1_1")
minProto = Poco::Net::Context::PROTO_TLSV1_1;
Expand Down Expand Up @@ -788,7 +789,7 @@ class WebTunnelAgent: public Poco::Util::ServerApplication
_host = Poco::Net::DNS::resolveOne(host);
}
std::string ports = config().getString("webtunnel.ports"s, ""s);
Poco::StringTokenizer tok(ports, ";,", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY);
Poco::StringTokenizer tok(ports, ";,"s, Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY);
for (Poco::StringTokenizer::Iterator it = tok.begin(); it != tok.end(); ++it)
{
int port = Poco::NumberParser::parse(*it);
Expand All @@ -814,19 +815,32 @@ class WebTunnelAgent: public Poco::Util::ServerApplication
_remoteTimeout = Poco::Timespan(config().getInt("webtunnel.remoteTimeout"s, 300), 0);
_threads = config().getInt("webtunnel.threads"s, 8);
_httpPath = config().getString("webtunnel.httpPath"s, ""s);
_httpPort = static_cast<Poco::UInt16>(config().getInt("webtunnel.httpPort"s, 0));
_httpPort = config().getUInt16("webtunnel.httpPort"s, 0);
_httpsRequired = config().getBool("webtunnel.https.enable"s, false);
_sshPort = static_cast<Poco::UInt16>(config().getInt("webtunnel.sshPort"s, 0));
_vncPort = static_cast<Poco::UInt16>(config().getInt("webtunnel.vncPort"s, 0));
_rdpPort = static_cast<Poco::UInt16>(config().getInt("webtunnel.rdpPort"s, 0));
_appPort = static_cast<Poco::UInt16>(config().getInt("webtunnel.appPort"s, 0));
_sshPort = config().getUInt16("webtunnel.sshPort"s, 0);
_vncPort = config().getUInt16("webtunnel.vncPort"s, 0);
_rdpPort = config().getUInt16("webtunnel.rdpPort"s, 0);
_appPort = config().getUInt16("webtunnel.appPort"s, 0);
_userAgent = config().getString("webtunnel.userAgent"s, ""s);
_httpTimeout = Poco::Timespan(config().getInt("http.timeout"s, 30), 0);
_propertiesUpdateInterval = Poco::Timespan(config().getInt("webtunnel.propertiesUpdateInterval"s, 0), 0);

_useProxy = config().getBool("http.proxy.enable"s, false);
_proxyHost = config().getString("http.proxy.host"s, ""s);
_proxyPort = static_cast<Poco::UInt16>(config().getInt("http.proxy.port"s, 80));
_proxyPort = config().getUInt16("http.proxy.port"s, 80);

std::string proxyURL = config().getString("http.proxy.url"s, ""s);
if (!proxyURL.empty() && _proxyHost.empty())
{
Poco::URI proxyURI(proxyURL);
if (proxyURI.getScheme() != "http")
{
logger().warning("Proxy URL specified, but scheme is not \"http\"."s);
}
_proxyHost = proxyURI.getHost();
_proxyPort = proxyURI.getPort();
}

_proxyUsername = config().getString("http.proxy.username"s, ""s);
_proxyPassword = config().getString("http.proxy.password"s, ""s);

Expand Down Expand Up @@ -964,7 +978,7 @@ class WebTunnelAgent: public Poco::Util::ServerApplication

const std::string WebTunnelAgent::SEC_WEBSOCKET_PROTOCOL("Sec-WebSocket-Protocol");
const std::string WebTunnelAgent::WEBTUNNEL_PROTOCOL("com.appinf.webtunnel.server/1.0");
const std::string WebTunnelAgent::WEBTUNNEL_AGENT("WebTunnelAgent/1.16.0");
const std::string WebTunnelAgent::WEBTUNNEL_AGENT("WebTunnelAgent/1.17.0");


POCO_SERVER_MAIN(WebTunnelAgent)
15 changes: 13 additions & 2 deletions WebTunnel/WebTunnelAgentLib/src/Tunnel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,18 @@ void Tunnel::init()
_httpTimeout = Poco::Timespan(_pConfig->getInt("http.timeout"s, 30), 0);
_useProxy = _pConfig->getBool("http.proxy.enable"s, false);
_proxyHost = _pConfig->getString("http.proxy.host"s, ""s);
_proxyPort = static_cast<Poco::UInt16>(_pConfig->getInt("http.proxy.port"s, 80));
_proxyPort = _pConfig->getUInt16("http.proxy.port"s, 80);
std::string proxyURL = _pConfig->getString("http.proxy.url"s, ""s);
if (!proxyURL.empty() && _proxyHost.empty())
{
Poco::URI proxyURI(proxyURL);
if (proxyURI.getScheme() != "http")
{
_logger.warning("Proxy URL specified, but scheme is not \"http\"."s);
}
_proxyHost = proxyURI.getHost();
_proxyPort = proxyURI.getPort();
}
_proxyUsername = _pConfig->getString("http.proxy.username"s, ""s);
_proxyPassword = _pConfig->getString("http.proxy.password"s, ""s);

Expand Down Expand Up @@ -531,7 +542,7 @@ Poco::UInt16 Tunnel::loadPort(const std::string& proto) const
{
if (_pConfig->getBool(Poco::format("webtunnel.%sPort.enable"s, proto), true))
{
return static_cast<Poco::UInt16>(_pConfig->getUInt(Poco::format("webtunnel.%sPort"s, proto), 0));
return _pConfig->getUInt16(Poco::format("webtunnel.%sPort"s, proto), 0);
}
else return 0;
}
Expand Down
64 changes: 60 additions & 4 deletions WebTunnel/WebTunnelClient/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ programs for specific protocols:

## Running remote-client

`remote-client` does not need a configuration file, all parameters can be passed
via command-line arguments. Some settings can also be set using a configuration file
`remote-client` usually does not need a configuration file, most parameters can be passed
via command-line arguments. Some settings can be set using a configuration file
(see the `WebTunnelAgent` [documentation](../WebTunnelAgent/README.md) for more
information on configuration files), but in most cases no configuration file is needed.
information on configuration files). Also, see the Configuration section below.

At startup, `remote-client` will look for a configuration file `.remote-client.properties`
in the current user's home directory, and read it if it's present.

To run `remote-client`, you'll need to specify the URL of the remote device to connect
to (e.g. https://8ba57423-ec1a-4f31-992f-a66c240cbfa0.remote.macchina.io), as well as
Expand All @@ -60,7 +63,7 @@ remote-client https://8ba57423-ec1a-4f31-992f-a66c240cbfa0.remote.macchina.io /r
When no longer needed, the tunnel can be terminated by typing `CTRL-C`.

The macchina.io REMOTE username and password can also be supplied via environment
variables `REMOTE_USERNAME` and `REMOTE_PASSWORD`.
variables `REMOTE_USERNAME` and `REMOTE_PASSWORD`, or via the configuration file.

You can now start your SSH client and connect it to port 2222 on your local machine
in order to open an SSH session to your device:
Expand Down Expand Up @@ -93,3 +96,56 @@ Please see the [`WebTunnelAgent` documentation](../WebTunnelAgent/README.md) for

You can also run `remote-client` without command-line options (or with `--help`
or `/help` on Windows) to see a help screen with available command-line options.

## Connecting Trough a HTTP Proxy

In some environments it may be required to connect to the macchina.io REMOTE server
via a HTTP proxy. This can be done by providing the address of the proxy server
on the command-line (`--proxy`, `-P` for short, or `/proxy` on Windows), or by providing the
proxy server and optionally credentials for the proxy server in a configuration file
(see below).

Below is an example for specifying a proxy server on the command-line:

```
remote-client https://8ba57423-ec1a-4f31-992f-a66c240cbfa0.remote.macchina.io -R 22 -L 2222 -P http://proxy.nowhere.com:8080
```

## Configuration

`remote-client` can optionally read settings from a configuration file. A configuration file
can be specified on the command-line with the `--config-file` (or `/config-file` on Windows) option.
Also, specific configuration options can also be set with the `--define` (or `/define`) option.

At startup, `remote-client` will also look for a configuration file named `.remote-client.properties`
in the user's home directory and read it if it is present. If that file is not present,
`remote-client` will attempt to read a configuration file named `remote-client.properties` located
in the same directory as the `remote-client` executable, or a parent directory.

Please refer to the [`WebTunnelAgent`](../WebTunnelAgent/README.md#configuration-file-format)
documentation for the configuration file format.

The following settings can be provided via a configuration file:

### Credentials

- `remote.username`: The username for the macchina.io REMOTE server.
- `remote.password`: The password for the macchina.io REMOTE server.
- `remote.token`: A token (JSON Web Token) for authenticating against the macchina.io REMOTE server.
If a token is given, username and password are not required. NOTE: A token is supported
by `remote-client` only, not any of the other client programs like `remote-ssh`.

### SSL/TLS Configuration

Please refer to the [`WebTunnelAgent`](../WebTunnelAgent/README.md#ssltls-configuration)
documentation for SSL/TLS configuration settings.

### HTTP Proxy Configuration

Please refer to the [`WebTunnelAgent`](../WebTunnelAgent/README.md#http-configuration)
documentation for configuring a HTTP proxy, including proxy credentials.

### Logging

Please refer to the [`WebTunnelAgent`](../WebTunnelAgent/README.md#ssltls-configuration)
documentation for configuring logging.
64 changes: 60 additions & 4 deletions WebTunnel/WebTunnelClient/src/WebTunnelClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
#include "Poco/NumberParser.h"
#include "Poco/Process.h"
#include "Poco/Environment.h"
#include "Poco/Path.h"
#include "Poco/File.h"
#include "Poco/Format.h"
#include <iostream>
#if defined(POCO_OS_FAMILY_WINDOWS)
#include <windows.h>
Expand Down Expand Up @@ -109,7 +112,10 @@ class WebTunnelClient: public Poco::Util::ServerApplication
protected:
void initialize(Poco::Util::Application& self)
{
loadConfiguration(); // load default configuration files, if present
if (!loadUserConfiguration("remote-client"s))
{
loadConfiguration(); // load default configuration files, if present
}
Poco::Util::ServerApplication::initialize(self);
Poco::Net::HTTPSessionInstantiator::registerInstantiator();
#if defined(WEBTUNNEL_ENABLE_TLS)
Expand All @@ -126,6 +132,19 @@ class WebTunnelClient: public Poco::Util::ServerApplication
Poco::Util::ServerApplication::uninitialize();
}

bool loadUserConfiguration(const std::string& baseName)
{
Poco::Path p(Poco::Path::home());
p.setFileName(Poco::format(".%s.properties"s, baseName));
Poco::File f(p.toString());
if (f.exists())
{
loadConfiguration(f.path());
return true;
}
else return false;
}

void defineOptions(OptionSet& options)
{
Poco::Util::ServerApplication::defineOptions(options);
Expand Down Expand Up @@ -187,6 +206,13 @@ class WebTunnelClient: public Poco::Util::ServerApplication
.argument("token"s)
.callback(OptionCallback<WebTunnelClient>(this, &WebTunnelClient::handleToken)));

options.addOption(
Option("proxy"s, "P"s, "Specify a HTTP proxy server to connect through, e.g. \"http://proxy.nowhere.com:8080\"."s)
.required(false)
.repeatable(false)
.argument("url"s)
.callback(OptionCallback<WebTunnelClient>(this, &WebTunnelClient::handleProxy)));

options.addOption(
Option("command"s, "C"s, "Specify a command to run (instead of waiting)."s)
.required(false)
Expand Down Expand Up @@ -242,6 +268,13 @@ class WebTunnelClient: public Poco::Util::ServerApplication
_token = value;
}

void handleProxy(const std::string& name, const std::string& value)
{
config().setBool("http.proxy.enable"s, true);
config().setString("http.proxy.url"s, value);
config().setString("http.proxy.host"s, ""s);
}

void handleCommand(const std::string& name, const std::string& value)
{
_command = value;
Expand Down Expand Up @@ -342,6 +375,19 @@ class WebTunnelClient: public Poco::Util::ServerApplication
Poco::Timespan remoteTimeout = Poco::Timespan(config().getInt("webtunnel.remoteTimeout"s, 300), 0);
Poco::Timespan localTimeout = Poco::Timespan(config().getInt("webtunnel.localTimeout"s, 7200), 0);

if (_username.empty())
{
_username = config().getString("remote.username"s, ""s);
}
if (_password.empty())
{
_password = config().getString("remote.password"s, ""s);
}
if (_token.empty())
{
_token = config().getString("remote.token"s, ""s);
}

#if defined(WEBTUNNEL_ENABLE_TLS)
bool acceptUnknownCert = config().getBool("tls.acceptUnknownCertificate"s, true);
std::string cipherList = config().getString("tls.ciphers"s, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"s);
Expand All @@ -361,7 +407,7 @@ class WebTunnelClient: public Poco::Util::ServerApplication
throw Poco::InvalidArgumentException("tls.verification", vModeStr);

Poco::Net::Context::Protocols minProto = Poco::Net::Context::PROTO_TLSV1_2;
if (tlsMinVersion == "tlsv1")
if (tlsMinVersion == "tlsv1" || tlsMinVersion == "tlsv1_0")
minProto = Poco::Net::Context::PROTO_TLSV1;
else if (tlsMinVersion == "tlsv1_1")
minProto = Poco::Net::Context::PROTO_TLSV1_1;
Expand Down Expand Up @@ -390,10 +436,20 @@ class WebTunnelClient: public Poco::Util::ServerApplication

if (config().getBool("http.proxy.enable"s, false))
{
logger().information("Proxy enable"s);
Poco::Net::HTTPClientSession::ProxyConfig proxyConfig;
proxyConfig.host = config().getString("http.proxy.host"s, ""s);
proxyConfig.port = static_cast<Poco::UInt16>(config().getInt("http.proxy.port"s, 80));
proxyConfig.port = config().getUInt16("http.proxy.port"s, 80);
std::string proxyURL = config().getString("http.proxy.url"s, ""s);
if (!proxyURL.empty() && proxyConfig.host.empty())
{
Poco::URI proxyURI(proxyURL);
if (proxyURI.getScheme() != "http")
{
logger().warning("Proxy URL specified, but scheme is not \"http\"."s);
}
proxyConfig.host = proxyURI.getHost();
proxyConfig.port = proxyURI.getPort();
}
proxyConfig.username = config().getString("http.proxy.username"s, ""s);
proxyConfig.password = config().getString("http.proxy.password"s, ""s);
Poco::Net::HTTPClientSession::setGlobalProxyConfig(proxyConfig);
Expand Down
Loading

0 comments on commit 99ad706

Please sign in to comment.