Fixie Socks
Last updated 24 June 2020
Table of Contents
Fixie Socks is a Heroku add-on that enables users to make TCP connections from a known IP address via a SOCKS proxy. Common use cases are database connections, FTP, SCP, and SSH. By connecting via Fixie Socks, users can integrate with external services and databases that do IP allowlisting. Fixie Socks is language- and framework-agnostic.
Fixie Socks acts as a SOCKS V5 proxy for outbound traffic, tunneling your requests through a known IP address. Each Fixie subscriber is assigned a set of static IP addresses. Through this proxy, subscribers can connect to any database or service that requires a fixed IP range.
Fixie Socks provides customers with a standard SOCKS V5 proxy URL that can be used to establish connections from any server-side language, including Node, Java, and Go. Fixie Socks can also be used by SSH, SCP, cURL, and other command-line tools.
The difference between Fixie Socks and Fixie
Fixie Socks provides a fixed set of IP addresses via a SOCKS V5 proxy. As such, Fixie Socks is capable of proxying any TCP connection. You can make HTTP and HTTPS requests via Fixie Socks, but Fixie (an HTTP proxy) is generally a better fit for that use case. Fixie Socks is perfect for making requests to databases, FTP servers, and other lower-level connections because it allows you to tunnel arbitrary TCP connections.
Provisioning the add-on
Fixie Socks can be attached to a Heroku application via the CLI:
A list of all plans available can be found here.
$ heroku addons:create fixie-socks
-----> Adding fixie-socks to sharp-mountain-4005... done, v18 (free)
After you provision Fixie Socks, a FIXIE_SOCKS_HOST
config var is available in your app’s configuration. This contains the SOCKSV5 proxy URL through which your application makes outbound requests. This can be confirmed using the heroku config:get
command:
$ heroku config:get FIXIE_SOCKS_HOST
user:pass@criterium.usefixie.com:1080
After installing Fixie Socks, the application should be configured to fully integrate with the add-on.
Local setup
Environment setup
For local development, it is necessary to replicate the FIXIE_SOCKS_HOST
environment variable.
FIXIE_SOCKS_HOST
can be retrieved either from the Fixie Socks dashboard or via the Heroku CLI:
$ heroku config:get FIXIE_SOCKS_HOST -s >> .env
Credentials and other sensitive configuration values should not be committed to source-control. In Git, exclude the .env
file with: echo .env >> .gitignore
.
For more information, see the Heroku Local article.
Using with Node.js
With Node.js, the general pattern is to establish a SOCKS connection using a client library like socksjs and then provide this stream to the database driver, request library, or other library of your choosing.
Connecting to MySQL via Fixie Socks with Node.js
MySql2 connects via a SOCKS connection provided as the stream
property. Because Fixie Socks terminates idle connections after 60 seconds of inactivity, we suggest using connection pooling (this is a best practice regardless) or manual error handling. The example below uses connection pooling:
'use strict';
const SocksConnection = require('socksjs');
const mysql = require('mysql2');
const fixieUrl = process.env.FIXIE_SOCKS_HOST;
const fixieValues = fixieUrl.split(new RegExp('[/(:\\/@)/]+'));
const mysqlServer = {
host: 'your-host.example.com',
port: 3306
};
const dbUser = 'user';
const dbPassword = 'password';
const db = 'database';
const fixieConnection = new SocksConnection(mysqlServer, {
user: fixieValues[0],
pass: fixieValues[1],
host: fixieValues[2],
port: fixieValues[3],
});
const mysqlConnPool = mysql.createPool({
user: dbUser,
password: dbPassword,
database: db,
stream: fixieConnection
});
mysqlConnPool.getConnection(function gotConnection(err, connection) {
if (err) throw err;
queryVersion(connection);
});
function queryVersion(connection) {
connection.query('SELECT version();', function (err, rows, fields) {
if (err) throw err;
console.log('MySQL/MariaDB version: ', rows);
connection.release();
process.exit();
});
}
Connecting to Postgres or Amazon Redshift via Fixie Socks with Node.js
As with mysql, you can pass a custom stream to the node-postgres library. This will work both for connections to Postgres databases as well as to Amazon Redshift:
'use strict';
const pg = require('pg');
const SocksConnection = require('socksjs');
const fixieUrl = process.env.FIXIE_SOCKS_HOST;
const fixieValues = fixieUrl.split(new RegExp('[/(:\\/@)/]+'));
const pgServer = {
host: 'YOUR-HOST',
port: 5432
};
const fixieConnection = new SocksConnection(pgServer, {
user: fixieValues[0],
pass: fixieValues[1],
host: fixieValues[2],
port: fixieValues[3],
});
const connectionConfig = {
user: 'YOUR-DB-USERNAME',
password: 'YOUR-DB-PASSWORD',
database: 'YOUR-DATABASE',
stream: fixieConnection,
ssl: true // Optional, depending on db config
};
var client = new pg.Client(connectionConfig);
client.connect(function (err) {
if (err) throw err;
client.query('SELECT 1+1 as test1', function (err, result) {
if (err) throw err;
console.log(result.rows[0]);
client.end(function (err) {
if (err) throw err;
});
});
});
Establishing an SSH connection over Fixie Socks with Node.js
The SSH2 library can be used to establish an SSH connection and execute commands over Fixie Socks.
var socks = require('socksv5'),
SSHClient = require('ssh2').Client;
const fixieUrl = process.env.FIXIE_SOCKS_HOST;
const fixieValues = fixieUrl.split(new RegExp('[/(:\\/@)/]+'));
socks.connect({
host: 'ssh.example.org',
port: 22,
proxyHost: fixieValues[2],
proxyPort: fixieValues[3],
auths: [socks.auth.UserPassword(fixieValues[0], fixieValues[1])]
}, function(socket) {
var conn = new SSHClient();
conn.on('ready', function() {
conn.exec('uptime', function(err, stream) {
if (err) throw err;
stream.on('close', function(code, signal) {
conn.end();
}).on('data', function(data) {
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data) {
console.log('STDERR: ' + data);
});
});
}).connect({
sock: socket,
username: 'ssh-user',
privateKey: require('fs').readFileSync('/here/is/my/key')
});
});
Making HTTP or HTTPS requests over Fixie Socks with Node.js
The socks package provides an agent that can be used by the Node’s http and https libraries, as well as by popular higher-level libraries like request.
'use strict';
const http = require('http');
const Socks = require('socks');
const fixieUrl = process.env.FIXIE_SOCKS_HOST;
const fixieValues = fixieUrl.split(new RegExp('[/(:\\/@)/]+'));
const socksAgent = new Socks.Agent({
proxy: {
ipaddress: fixieValues[2],
port: fixieValues[3],
type: 5,
authentication: {
username: fixieValues[0],
password: fixieValues[1]
}
}},
true, // true HTTPS server, false for HTTP server
false // rejectUnauthorized option passed to tls.connect()
);
function resHandler(res) {
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('error', () => {
process.exit(1);
});
res.on('end', () => {
console.log(responseBody);
socksAgent.encryptedSocket.end();
});
}
http.get({ hostname: 'example.com', port: '443', agent: socksAgent}, resHandler);
Using with Go
Golang’s net/proxy
package provides a Socks5 proxy dialer that can be used
with Fixie Socks. This dialer can be used for any TCP connection. The example
below uses this to make an HTTP request:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"golang.org/x/net/proxy"
)
const URL = "http://www.example.com"
func main() {
fixie_data := strings.Split(os.Getenv("FIXIE_SOCKS_HOST"), "@")
fixie_addr := fixie_data[1]
auth_data := strings.Split(fixie_data[0], ":")
auth := proxy.Auth{
User: auth_data[0],
Password: auth_data[1],
}
dialer, err := proxy.SOCKS5("tcp", fixie_addr, &auth, proxy.Direct)
if err != nil {
fmt.Fprintln(os.Stderr, "can't connect to the proxy:", err)
os.Exit(1)
}
httpTransport := &http.Transport{}
httpClient := &http.Client{Transport: httpTransport}
httpTransport.Dial = dialer.Dial
req, err := http.NewRequest("GET", URL, nil)
if err != nil {
fmt.Fprintln(os.Stderr, "can't create request:", err)
os.Exit(2)
}
resp, err := httpClient.Do(req)
if err != nil {
fmt.Fprintln(os.Stderr, "can't GET page:", err)
os.Exit(3)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Fprintln(os.Stderr, "error reading body:", err)
os.Exit(4)
}
fmt.Println(string(b))
}
Using with Java
Java supports a system property socksProxyHost
, which can be used to route
every outbound request through Fixie Socks. Additionally, Java 7 introduced
ProxySelector,
which can be used to conditionally proxy requests depending on the hostname.
A simple example that proxies all outbound requests through Fixie Socks:
URL fixie = new URL(System.getenv("FIXIE_SOCKS_HOST"));
String[] fixieUserInfo = fixie.getUserInfo().split(':');
String fixieUser = fixieUserInfo[0];
String fixiePassword = fixieUserInfo[1];
System.setProperty("socksProxyHost", fixie.getHost());
Authenticator.setDefault(new ProxyAuthenticator(fixieUser, fixiePassword));
//...
private class ProxyAuthenticator extends Authenticator {
private final PasswordAuthentication passwordAuthentication;
private ProxyAuthenticator(String user, String password) {
passwordAuthentication = new PasswordAuthentication(user, password.toCharArray());
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return passwordAuthentication;
}
}
Curl via Fixie Socks
Curl accepts a socks5
argument:
curl --socks5 $FIXIE_SOCKS_HOST http://example.com
SSH and SCP via Fixie Socks
SSH and SCP accept a ProxyCommand
option. Using this option, you can pass in an ncat command that establishes a connection via the Fixie Socks proxy. This requires ncat v7 or newer.
Ncat does not accept proxy authentication via the standard proxy url schema, so instead you must provide it as a separate argument:
ssh -o ProxyCommand='ncat --proxy-type socks5 --proxy YOUR-FIXIE-DOMAIN.usefixie.com:1080 --proxy-auth fixie:YOUR-FIXIE-TOKEN %h %p' serveruser@server.ip.address
FTP via Fixie Socks
Making FTP requests via Fixie Socks is straight forward, but depending on the configuration of the FTP server, you may need to specify a specific Fixie IP address instead of using your load-balanced Fixie Socks proxy URL.
FTP uses multiple TCP connections (a control connection and a transfer connection). If your FTP server supports “passive promiscuous” connections, connecting through the standard Fixie Socks URL works without issue. If, however, the server does not support “passive promiscuous” connections, both the control and transfer connection must go through the same IP. For this use case, you can proxy directly through a specific Fixie Socks IP instead of through a load-balanced Fixie Socks proxy group. You can find the IP address and credentials in the Fixie Socks dashboard.
An example using cURL:
curl -v 'ftp://ftp2.census.gov/robots.txt' --socks5-hostname $FIXIE_SOCKS_IP_SPECIFIC_URL
cURL versions prior 7.48 had a bug which left the SOCKS connection open after the FTP transfer completed. We recommend using cURL 7.48 or later.
Using with languages that don’t support SOCKS proxies
Some languages do not provide native support for SOCKS proxies. Where this is the case, there are other options for using Fixie Socks.
- There are several utilities that transparently proxy requests over SOCKS5 proxies like Fixie Socks. Popular options include Dante Socksify client, tsocks, and proxychains. Each of these utilities require some caveats: Dante only works on linux, so is not a good option if you develop locally on Mac or Windows. tsocks is not threadsafe, so is not a good choice for some applications. proxychains requires a numeric IPV4 address rather than a host name - you can provide one from your Fixie Dashboard, but in doing so you lose the availability benefits of Fixie’s load-balanced SOCKS proxy cluster.
- Alternately, socat can be used to establish port-forwarding through Fixie Socks. Socat will establish a tunnel through Fixie Socks and bind to a local port through which your service can make requests.
Dashboard
The Fixie Socks dashboard allows you to view your connection logs and account settings.
The dashboard can be accessed via the CLI:
$ heroku addons:open fixie-socks
Opening fixie-socks for sharp-mountain-4005
or by visiting the Heroku Dashboard and selecting the application in question. Select Fixie Socks from the add-ons menu.
Removing the add-on
Fixie Socks can be removed via the CLI.
This will destroy all associated data and cannot be undone! If your application is configured to make requests through Fixie Socks, those requests will fail after removing the add-on.
$ heroku addons:destroy fixie-socks
-----> Removing fixie-socks from sharp-mountain-4005... done, v20 (free)
Support
All Fixie Socks support and runtime issues should be submitted via one of the Heroku Support channels. Any non-support related issues or product feedback is welcome via email at team@usefixie.com.