Setting up a SOCKS Proxy Server with Dante on AWS
Tags: aws, cloud, proxy, devops
Published: 2024-01-11 23:23:10

A SOCKS proxy server serves various purposes. In this guide, we'll explore how to set up one using Dante on Amazon Web Services (AWS), particularly useful in scenarios such as running applications on a serverless solution where external services request your service's static public IP for whitelisting. As serverless solutions limit direct IP provision, employing a SOCKS proxy offers a versatile solution by routing all requests to the external service through the SOCKS server. It's advisable to assign a public static IP for the SOCKS server in this context. Additionally, we'll address another common use case: enhancing privacy and security by configuring our SOCKS server to route all requests through the dynamic public IP address assigned to the server instance during creation. For a guide on how to host this on Google Cloud, you can click [here](https://dev.to/idrisakintobi/setting-up-a-socks-proxy-server-with-dante-on-google-cloud-2hgc).


#### Requirements

_To set up a SOCKS proxy server on AWS, you'll need:_

- _An AWS account_
- _A security group rule_
- _An EC2 instance (virtual machine)_

---

### Security Group Rule Setup

- Go to the AWS Management Console and navigate to the EC2 Dashboard > Security Groups.
![Create Security Group](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tiondgmx906d6pkov2gp.png)

- Click on "Create Security Group." Name the security group (e.g., 'allow-sock-1047') and create a security group description.

- Create security group Inbound Rules: Add a rule to allow incoming traffic on port 22 for ssh and your desired SOCKS port (e.g., port 1047). Apply additional security rules, such as IP restrictions, as needed.
![Create security group Inbound Rules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/obs17yp3kd84sgmzokpu.png)


### EC2 Instance Setup

- Navigate to the EC2 Dashboard > Instances.

- Click on "Launch Instance."

- Name your instance (e.g., 'sock-server') and choose an Amazon Linux 2023 (AMI). If you opt for a different image, make sure to update the commands in the script to be compatible with the selected machine image.
![Launch instance](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g73v9s2dsvh5naitrubo.png)

- Choose an appropriate instance type (e.g., t2.micro, which is free-tier eligible).

- Select an existing key pair or create a new one to access your instance securely.

- Click 'Edit' in network settings and enable 'Auto-assign public IP.'

- Click on 'Select existing security group' and choose the 'allow-sock-1047' rule.
![Network setting](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mnjqoexczbz8v6ig337w.png)

- Click 'Advanced options' and paste the script into the 'User Data' text area field.
![Paste user data script](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ogoo9jzt6fjln9s1tlxv.png)

- Review your configurations and click on "Launch."

**_script_**

```bash
#!/bin/bash

# Set terminal type
export TERM=xterm-256color

# Variables
SOCK_USERNAME="socksproxy"
SOKSPROXY_PASS="socksproxy"
SOCK_PORT=1047

# Function to print stage
print_stage() {
    local TEXT="$1"
    echo -e "\n\e[32m\e[1m $TEXT \e[0m\e[32m"
}

# Update system and install dante-server dependencies
print_stage "Updating System and install development tools"
# use yum to install if you've selected machine image earlier than 2023
sudo dnf update -y
sudo dnf groupinstall "Development Tools" -y
sudo dnf install libevent-devel -y

# Download and install dante-server
print_stage "Downloading and installing dante-server"
curl -O https://www.inet.no/dante/files/dante-1.4.3.tar.gz
tar xvzf dante-1.4.3.tar.gz
cd dante-1.4.3
./configure
make && make install

# Create dante configuration file
print_stage "Setting up dante-server"
cat <<EOF | tee /etc/sockd.conf
logoutput: /var/log/sockd.log
internal: 0.0.0.0 port = ${SOCK_PORT}
external: enX0
clientmethod: none
socksmethod: username
user.privileged: root
user.notprivileged: nobody
client pass {
    from: 0.0.0.0/0 to: 0.0.0.0/0
    log: error connect disconnect
}
socks pass {
    from: 0.0.0.0/0 to: 0.0.0.0/0
    log: error connect disconnect
}
EOF

# Add socksproxy user
print_stage "Adding ${SOCK_USERNAME} user"
sudo useradd -r -s /bin/false ${SOCK_USERNAME}
echo "${SOCK_USERNAME}:${SOKSPROXY_PASS}" | sudo chpasswd

# Start sockd
sudo /usr/local/sbin/sockd -D

print_stage "Installation Completed"

# Get public IP address (metadata version 1)
# PUBLIC_IPV4=$(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address 2>/dev/null || echo "N/A")

# Get public IP address (metadata version 2 - token required)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null)
PUBLIC_IPV4=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-ipv4 2>/dev/null || echo "N/A")

# Display configuration details and IP addresses
print_stage "Configuration Details:"
echo -e "\033[1mIP:\033[0m ${PUBLIC_IPV4}"
echo -e "\033[1mUsername:\033[0m ${SOCK_USERNAME}"
echo -e "\033[1mPassword:\033[0m ${SOKSPROXY_PASS}"
echo -e "\033[1mPort:\033[0m ${SOCK_PORT}"
```

**_Script Explanation_**

For brevity, we'll focus on the Dante configuration in the script. For more configuration options you can visit [here](https://www.inet.no/dante/doc/latest/config/index.html). The script uses Dante version 1.4.3 which is the latest at the time of writing this article. Version 1.5 is currently in testing phase and it is set to roll out first quarter of this year.

> **logoutput: /var/log/sockd.log:** _Specifies the path for the log file. In this case, it will be stored at /var/log/sockd.log_.
**internal: 0.0.0.0 port = ${SOCK_PORT}:** _Configures the internal IP address and port on which the SOCKS server will listen for incoming connections. The variable ${SOCK_PORT} is used to set the specified port (in this case, 1047)_.
**external: ens4:** _Defines the external network interface to bind to. In this script, it uses the interface ens4_.
**clientmethod: none:** _Specifies that clients connecting to the SOCKS server do not need to authenticate_.
**socksmethod: username:** _Specifies that the SOCKS server will use username/password authentication for connections_.
**user.privileged: root:** _Sets the privileged user for the SOCKS server to root_.
**user.notprivileged: nobody:** _Sets the non-privileged user for the SOCKS server to nobody_.
**client pass { … }:** _Defines a rule allowing connections from any IP (0.0.0.0/0) to any destination. It logs errors related to connection and disconnection_.
**socks pass { … }:** _Similar to the client pass rule, this allows SOCKS connections from any IP to any destination and logs related errors_.

Once the instance is running, SSH into it to view the script log using `cat /var/log/cloud-init-output.log`. Please note that the script might take a while to complete due to the compilation of Dante and the use of a small machine type.
You can also rerun the startup script from the SSH terminal by using `sudo bash /var/lib/cloud/instance/user-data.txt`. This could be useful when you edit the instance startup script and need to rerun it manually.

### Testing the SOCKS Proxy

- Check if the configured port is open using:
```bash
nc -zv ${IP} ${PORT}
```

- Verify the SOCKS server functionality by curling Google's home page with the command:

```bash
socks5://${USERNAME}:${PASS}@${IP}:${PORT} https://www.google.com
```

### SOCKS Usage

- **In browser**
Install the Proxy Toggle Firefox extension and configure it with the SOCKS settings to route browser traffic through the SOCKS server.
![Proxy Toggle Firefox extension configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tjbtkaxp6390ro77npq2.png)


- **In code**
Use Axios in JavaScript with the socks-proxy-agent module for making requests through the SOCKS server.
```typescript
import axios from 'axios';
import { SocksProxyAgent } from 'socks-proxy-agent';

// Get socks config from the environment. - Assuming the variables are available
const { SOCKS_HOST, SOCKS_PORT, SOCKS_USERNAME, SOCKS_PASSWORD } = process.env;

// Setup proxy agent
const socksV5Agent = new SocksProxyAgent(`socks5://${SOCKS_USERNAME}:${SOCKS_PASSWORD}@${SOCKS_HOST}:${SOCKS_PORT}`);

// Create socks axios instance
const socksClient = axios.create({
    httpsAgent: socksV5Agent,
    httpAgent: socksV5Agent,
});

// Use the instance 
(async () => {
    try {
        const { data } = await socksClient.get('https://api.ipify.org');
        console.log(`Our socks IP is ${data}`);
    } catch (error) {
        console.error('Error fetching IP:', error.message);
        // Handle the error as needed
    }
})();
```

## Conclusion

In conclusion, this guide has equipped you with the knowledge and step-by-step instructions to seamlessly set up a SOCKS proxy server on AWS using Dante. By following the outlined procedures, you can establish a robust SOCKS proxy server ready to meet your specific needs.