Spokes v1.2.1
Published at June 3, 2021 · 6 min read
Share on:Release v1.2.1 introduces a built-in SOCKSv5 server. SOCKS is a simple protocol for relaying a TCP or UDP request to a destination server. It’s typically used to navigate traffic through a firewall.
To summarize the protocol: a SOCKS server will receive a connection from a client and a request to connect to a host. The request includes the protocol (TCP or UDP), a destination, either a hostname or IP address, and the port for that host. The SOCKS server will make the connection to the destination and relay the traffic between it and the original client.
SOCKS is supported in many programming languages and network utilities. curl
is a utility on most systems that supports SOCKS for making HTTP requests.
With SOCKS built into Spokes, an admin can now provide a port-range to support generic TCP relaying but no longer need to open the entire range of ports on their firewall. This provide less surface area for bots or would-be attackers to probe.
We think this new mechanism is going to open up many new configuration possibilities. Please reach out to us for any questions, support or requests for strategies on how to use this new feature.
TLS-SOCKS
The standard SOCKSv5 protocol does not support TLS for incoming client connection. However, our implementation of SOCKSv5 supports does. It reuses the same certificate and server name as the Spokes server. A TLS session can be established over a TCP connection to the port configured as a the TLS-SOCKS server port.
We thought providing this non-standard feature could add another layer of security and privacy to the communications for clients. In addition, this is extremely useful if the underlying protocol is does not use transport encryption by default. For legacy software and hardware systems, this is often the case.
Setting up the SOCKS server
Let’s walk through enabling and setting up the SOCKS server. Visit the Configuration section of the Spokes server via the navigation menu on the left-hand side of the dashboard. You will need to scroll down to the buttom of this page to find the SOCKS server configuration panel.
SOCKS can be enabled and disabled from here. You will need to specify ports for the standard SOCKS server and separate one for the TLS server. Once the inputs are saved the server will be started.
Requests to any TCP or HTTP/S service hosted behind a tunnel connected to the Spokes server can now be made using the SOCKS protocol.
Firewall Rules
A firewall can be implemented using table IP tables in Linux or through utility like firewalld
.
firewalld
is used by default on Centos 7 and 8 so we’ll provide some examples commands to open up the ports for the SOCKS servers. Let’s say we used port 9001
for the standard SOCKS server and 9002
for TLS-SOCKS server.
[user@host ] sudo firewall-cmd --add-port 9001/tcp --permanent --zone=public
[user@host ] sudo firewall-cmd --add-port 9002/tcp --permanent --zone=public
[user@host ] sudo firewall-cmd --reload
Port filtering can also be implemented by a cloud firewall that is available a cloud provider. An example is the Security Group used by AWS EC2 for filtering inbound and outband traffic to EC2 instances.
In case you’re using SOCKS to limit the ports that you’re opening on your server, remember to close any previous port ranges opened earlier to support TCP relaying.
cURL Examples
Below is an example using the curl
command to request an HTTPS resource that is hosted behind one of our tunnels. curl
provides many different options for using SOCKS proxies but the option chosen here is very important.
The --socks5-hostname
option will instruct curl
to not resolve the hostname of the HTTPS resource we are trying to connect to. It will provide the hostname <subdomain>.spokes.example.com
as the request destination, not the IP address. By default, curl
will resolve the IP address of website and send that in its request to the SOCKS server.
Note, we are connecting to the plain-text, standard, SOCKSv5 server listening on port 9001
.
[user@host ] curl -vvv --socks5-hostname spokes.example.com:9001 https://<subdomain>.spokes.example.com
Other network clients that can use SOCKS for proxying request may have their own nuances. It’s very important to not obscure the destination for HTTPS requests to the SOCKS server in Spokes. It will use the hostname to help route requests to the correct the tunnel.
TLS-SOCKS Code Examples
Since TLS-enabled SOCKS is not standard you will need to write some custom code to work with it. Below is a snippet of code written in Go that implements the logic to connect to the TLS SOCKS server listening on port 9002 on our example Spokes server.
package main
import (
"bufio"
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"os"
"golang.org/x/net/proxy"
)
// This custom Go net.Dialer makes a TLS connection to spokes.example.com
// and specifies the intended server name it wants to connect to. This is important
// for TLS connections in Go since now the TLS clientHello handshake on port 9002
// will include the server name.
//
/ If this was not specified in the tls.Config the TLS connection would be able
// to be established.
type tlsDialer struct{}
func (t *tlsDialer) Dial(network, addr string) (c net.Conn, err error) {
return tls.Dial(network, addr, &tls.Config{ServerName: "spokes.example.com"})
}
func main() {
// Creates a SOCKS dialer.
dialer, err := proxy.SOCKS5("tcp", "spokes.example.com:9002", nil, &tlsDialer{})
if err != nil {
fmt.Println(err.Error())
return
}
// Create a connection TCP connection to this resource using the SOCKS dialer.
conn, err := dialer.Dial("tcp", "tun-1.spokes.example.com:443")
if err != nil {
fmt.Println(err.Error())
return
}
// Use the tls.Client to wrap the 'plain-text' connection with a TLS session.
// Note, this is a second layer of TLS.
tlsConn := tls.Client(conn, &tls.Config{ServerName: "tun-1.spokes.example.com"})
// Create an HTTPS request.
request, err := http.NewRequest("GET", "https://tun-1.spokes.example.com", nil)
if err != nil {
fmt.Println(err.Error())
return
}
// Write to the TLS client connection to tun-1.spokes.example.com.
if err := request.Write(tlsConn); err != nil {
fmt.Println(err.Error())
return
}
resp, err := http.ReadResponse(bufio.NewReader(tlsConn), request)
if err != nil {
fmt.Println(err.Error())
return
}
defer resp.Body.Close()
// Print out the HTTP response code.
fmt.Printf("Status: %d\n", resp.StatusCode)
// Dump the HTTP response body to standard out.
io.Copy(os.Stdout, resp.Body)
}
Without much custom code we’ve added an initial TLS connection to the TLS-SOCKS server running on Spokes. On top of that we established another TLS session for the request to https://tun-1.spokes.example.com
. This necessary but artifact of using the TLS-SOCKS server.
For applications that perform communications over plain-text, using this mechanism will add transport layer security to those requests enhancing security and privacy of any communications with the upstream service.
Coming soon
We’re working on additions to the pktriot
client program to check Packetriot.com for new releases. Updates can be downloaded and unpacked for use alongside the version install on the system (via RPM, Debian).
This adds convenience when upgrading in cases when you did not install using a package manager.
The updates binaries are stored alongside the configuration file. For example, if you’re configuration is stored in your home directory in ~/.pktriot/config.json
, then updates would be stored in ~/.pktriot/updates/<version>/pktriot.exe
.
Spokes v1.2.2 will introduce new features to check on client program updates and mirror them. This will enable a Spokes server to manage what versions of the client program a fleet of tunnels is using and make remote management of client software possible.
As always, thanks for your suggestions and feedback.
Cheers!