Skip to content
This repository was archived by the owner on Nov 1, 2025. It is now read-only.

Commit 7a49f61

Browse files
author
Dan Vittegleo
committed
Add SOCKS5 proxy support
1 parent 77c725f commit 7a49f61

File tree

10 files changed

+91
-27
lines changed

10 files changed

+91
-27
lines changed

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
<b>awslambdaproxy</b> is an [AWS Lambda](https://aws.amazon.com/lambda/) powered HTTP(s) web proxy. It provides a constantly rotating IP address for your web traffic from all regions where AWS Lambda is available. The goal is to obfuscate your web traffic and make it harder to track you as a user.
1+
<b>awslambdaproxy</b> is an [AWS Lambda](https://aws.amazon.com/lambda/) powered HTTP(s)/SOCKS web proxy. It provides a constantly rotating IP address for your web traffic from all regions where AWS Lambda is available. The goal is to obfuscate your web traffic and make it harder to track you as a user.
22

33
![](/images/overview.gif?raw=true)
44

55
## Features
6-
* HTTP & HTTPS support.
7-
* No special software required. Just configure your system to use an HTTP proxy.
6+
* HTTP(s) and SOCKS5 proxy support.
7+
* No special software required. Just configure your system to use an HTTP or SOCKS proxy.
88
* Each AWS Lambda region provides 1 outgoing IP address that gets rotated roughly every 4 hours. That means if you use 10 AWS regions, you'll get 60 unique IPs per day.
99
* Configurable IP rotation frequency between multiple regions. By default IP will rotate to new region every 3 minutes.
1010
* Personal proxy server not shared with anyone else.
@@ -14,7 +14,7 @@
1414
Current code status: <b>proof of concept</b>. This is the first Go application that I've ever written. It has no tests. Listening endpoints have no security yet. It may not work. It may blow up. Use at your own risk.
1515

1616
## How it works
17-
At a high level, awslambdaproxy proxies HTTP(s) traffic through AWS Lambda regional endpoints. To do this, awslambdaproxy is setup on a publicly accessible host (e.g. EC2 instance) and it handles creating Lambda resources that run a simple HTTP proxy ([elazarl/goproxy](https://github.com/elazarl/goproxy)). Since Lambda does not allow you to bind ports in your executing functions, the HTTP proxy is bound to a unix socket and a reverse tunnel is established from the Lambda function to port 8081 on the host running awslambdaproxy. Once a tunnel connection is established, all user web traffic is forwarded from port 8080 through this HTTP proxy. Lambda functions have a max execution time of 5 minutes, so there is a goroutine that continuously executes Lambda functions to ensure there is always a live tunnel in place. If multiple regions are specified, user HTTP traffic will be routed in a round robin fashion across these regions.
17+
At a high level, awslambdaproxy proxies HTTP(s) traffic through AWS Lambda regional endpoints. To do this, awslambdaproxy is setup on a publicly accessible host (e.g. EC2 instance) and it handles creating Lambda resources that run a simple HTTP ([elazarl/goproxy](https://github.com/elazarl/goproxy)) or SOCKS ([armon/go-socks5](https://github.com/armon/go-socks5)) proxy. Since Lambda does not allow you to bind ports in your executing functions, the proxy server is bound to a unix socket and a reverse tunnel is established from the Lambda function to port 8081 on the host running awslambdaproxy. Once a tunnel connection is established, all user web traffic is forwarded from port 8080 through this proxy. Lambda functions have a max execution time of 5 minutes, so there is a goroutine that continuously executes Lambda functions to ensure there is always a live tunnel in place. If multiple regions are specified, user traffic will be routed in a round robin fashion across these regions.
1818

1919
![](/images/how-it-works.png?raw=true)
2020

@@ -55,10 +55,10 @@ The easiest way is to download a pre-built binary from the [GitHub Releases](htt
5555
```sh
5656
export AWS_ACCESS_KEY_ID=XXXXXXXXXX
5757
export AWS_SECRET_ACCESS_KEY=YYYYYYYYYYYYYYYYYYYYYY
58-
./awslambdaproxy -regions us-west-2,us-west-1,us-east-1,us-east-2
58+
./awslambdaproxy -regions us-west-2,us-west-1,us-east-1,us-east-2 -proxy-type socks
5959
```
6060

61-
3. Configure your web browser (or OS) to use an HTTP and HTTPS proxy at the publicly accessible host running `awslambdaproxy` on port 8080.
61+
3. Configure your web browser (or OS) to use the HTTP/HTTPS or SOCKS5 proxy (depending on -proxy-type selection) on the publicly accessible host running `awslambdaproxy` on port 8080.
6262

6363
## FAQ
6464
1. <b>Should I use awslambdaproxy?</b> That's up to you. Use at your own risk.
@@ -69,9 +69,10 @@ The easiest way is to download a pre-built binary from the [GitHub Releases](htt
6969
6. <b>Why does my connection drop periodically?</b> AWS Lambda functions can currently only execute for a maximum of 5 minutes. In order to maintain an ongoing HTTP proxy a new function is executed and all new traffic is cut over to it. Any ongoing connections to previous Lambda function will hard stop after a timeout period.
7070

7171
# Powered by
72-
* [Goproxy](https://github.com/elazarl/goproxy) - An HTTP proxy written in Go.
73-
* [Yamux](https://github.com/hashicorp/yamux) - Golang connection multiplexing library.
74-
* [Goad](https://github.com/goadapp/goad) - Code was borrowed from this project to handle AWS Lambda zip creation and function upload.
72+
* [goproxy](https://github.com/elazarl/goproxy) - An HTTP proxy server written in Go.
73+
* [go-socks5](https://github.com/armon/go-socks5) - A SOCKS5 proxy written in Go.
74+
* [yamux](https://github.com/hashicorp/yamux) - Golang connection multiplexing library.
75+
* [goad](https://github.com/goadapp/goad) - Code was borrowed from this project to handle AWS Lambda zip creation and function upload.
7576

7677
# Future work
7778
* Add security to proxy and tunnel connections

cmd/awslambdaproxy/awslambdaproxy.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const (
1616
func main() {
1717
regionsPtr := flag.String("regions", "us-west-2", "Regions to run proxy (e.g. us-west-2) (can be comma separated list)")
1818
frequencyPtr := flag.Int( "frequency", 180, "Frequency in seconds to execute Lambda function. If multiple regions are specified, this will cause traffic to rotate round robin at the interval specified here")
19+
proxyTypePtr := flag.String("proxy-type", "http", "Proxy type to setup: 'http' or 'socks'")
1920
proxyPortPtr := flag.String("proxy-port", "8080", "Port to listen for proxy connections")
2021
tunnelPortPtr := flag.String("tunnel-port", "8081", "Port to listen for reverse connection from Lambda")
2122
flag.Parse()
@@ -32,6 +33,10 @@ func main() {
3233
flag.PrintDefaults()
3334
os.Exit(1)
3435
}
36+
if *proxyTypePtr != "http" && *proxyTypePtr != "socks" {
37+
flag.PrintDefaults()
38+
os.Exit(1)
39+
}
3540

3641
// handle frequency
3742
if *frequencyPtr > 255 {
@@ -53,6 +58,6 @@ func main() {
5358
}
5459

5560
regions := strings.Split(*regionsPtr, ",")
56-
awslambdaproxy.ServerInit(*proxyPortPtr, *tunnelPortPtr, regions, frequencySeconds)
61+
awslambdaproxy.ServerInit(*proxyPortPtr, *tunnelPortPtr, regions, frequencySeconds, *proxyTypePtr)
5762
}
5863

data/lambda/main.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
def handler(event, context):
99
logger.info("Event: {}".format(event))
1010
logger.info("Context: {}".format(context))
11+
address = event['ConnectBackAddress']
12+
proxy_type = event['ProxyType']
1113

12-
command = "./awslambdaproxy-lambda -address {}".format(event)
14+
command = "./awslambdaproxy-lambda -address {} -proxy-type {}".format(address, proxy_type)
1315
logger.info("Running: {}".format(command))
1416
try:
1517
proc = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)

glide.lock

Lines changed: 9 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

glide.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ import:
1313
- package: github.com/hashicorp/yamux
1414
- package: github.com/pkg/errors
1515
version: ^0.8.0
16+
- package: github.com/armon/go-socks5

init.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"runtime"
88
)
99

10-
func ServerInit(proxyPort string, tunnelPort string, regions []string, lambdaExecutionFrequency time.Duration) {
10+
func ServerInit(proxyPort string, tunnelPort string, regions []string, lambdaExecutionFrequency time.Duration, proxyType string) {
1111
lambdaExecutionTimeout := int64(lambdaExecutionFrequency.Seconds()) + int64(10)
1212

1313
log.Println("Setting up Lambda infrastructure")
@@ -25,7 +25,7 @@ func ServerInit(proxyPort string, tunnelPort string, regions []string, lambdaExe
2525
}
2626

2727
log.Println("Starting LambdaExecutionManager")
28-
lambdaExecutionManager, err := newLambdaExecutionManager(tunnelPort, regions, lambdaExecutionFrequency)
28+
lambdaExecutionManager, err := newLambdaExecutionManager(tunnelPort, regions, lambdaExecutionFrequency, proxyType)
2929
if err != nil {
3030
log.Println("Failed to setup LambdaExecutionManager", err.Error())
3131
os.Exit(1)

lambda/lambda.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ const (
1010
proxyUnixSocket = "/tmp/lambda-proxy.socket"
1111
)
1212

13-
func LambdaInit(tunnelHost string) {
13+
func LambdaInit(tunnelHost string, proxyType string) {
1414
log.Println("Starting LambdaProxyServer")
15-
lambdaProxyServer := startLambdaProxyServer()
15+
lambdaProxyServer := startLambdaProxyServer(proxyType)
1616

1717
log.Println("Establishing tunnel connection to", tunnelHost)
1818
lambdaTunnelConnection := setupLambdaTunnelConnection(tunnelHost)
@@ -24,6 +24,7 @@ func LambdaInit(tunnelHost string) {
2424

2525
func main() {
2626
addressPtr := flag.String("address", "localhost:8081", "IP and port of server to connect to")
27+
proxyTypePtr := flag.String("proxy-type", "http", "Proxy type to setup: 'http' or 'socks'")
2728

2829
flag.Parse()
2930

@@ -32,7 +33,12 @@ func main() {
3233
os.Exit(1)
3334
}
3435

35-
LambdaInit(*addressPtr)
36+
if *proxyTypePtr != "http" && *proxyTypePtr != "socks" {
37+
flag.PrintDefaults()
38+
os.Exit(1)
39+
}
40+
41+
LambdaInit(*addressPtr, *proxyTypePtr)
3642

3743
}
3844

lambda/lambdaproxyserver.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@ package main
33
import (
44
"os"
55
"net"
6-
"net/http"
76
"log"
7+
"net/http"
88
"time"
99

10+
"github.com/armon/go-socks5"
1011
"github.com/elazarl/goproxy"
1112
)
1213

1314
type LambdaProxyServer struct {
1415
unixSocket string
16+
proxyType string
1517
listener net.Listener
1618
}
1719

18-
func (l *LambdaProxyServer) run() {
19-
log.Println("Starting proxy server on socket", l.unixSocket)
20+
func (l *LambdaProxyServer) runHttp() {
21+
log.Println("Starting HTTP proxy server on socket", l.unixSocket)
2022
proxy := goproxy.NewProxyHttpServer()
2123
proxy.Verbose = true
2224
for {
@@ -37,6 +39,31 @@ func (l *LambdaProxyServer) run() {
3739
}
3840
}
3941

42+
func (l *LambdaProxyServer) runSocks() {
43+
log.Println("Starting Socks proxy server on socket", l.unixSocket)
44+
conf := &socks5.Config{}
45+
server, err := socks5.New(conf)
46+
if err != nil {
47+
panic(err)
48+
}
49+
for {
50+
os.Remove(l.unixSocket)
51+
socketAddress, err := net.ResolveUnixAddr("unix", l.unixSocket)
52+
if err != nil {
53+
log.Println("Failed to resolve unix socket " + l.unixSocket)
54+
os.Exit(1)
55+
}
56+
unixListener, err := net.ListenUnix("unix", socketAddress)
57+
if err != nil {
58+
log.Println("Created listener on unix socket " + l.unixSocket)
59+
os.Exit(1)
60+
}
61+
l.listener = unixListener
62+
os.Chmod(l.unixSocket, 0777)
63+
log.Fatal(server.Serve(unixListener))
64+
}
65+
}
66+
4067
func (l *LambdaProxyServer) isReady() bool {
4168
if l.listener != nil {
4269
return true
@@ -45,11 +72,16 @@ func (l *LambdaProxyServer) isReady() bool {
4572
}
4673
}
4774

48-
func startLambdaProxyServer() *LambdaProxyServer {
75+
func startLambdaProxyServer(proxyType string) *LambdaProxyServer {
4976
ret := &LambdaProxyServer{
5077
unixSocket: proxyUnixSocket,
78+
proxyType: proxyType,
79+
}
80+
if ret.proxyType == "http" {
81+
go ret.runHttp()
82+
} else {
83+
go ret.runSocks()
5184
}
52-
go ret.run()
5385
for {
5486
if ret.isReady() == true {
5587
break

lambdaexecution.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ type LambdaExecutionManager struct {
1616
regions []string
1717
frequency time.Duration
1818
publicIp string
19+
proxyType string
20+
}
21+
22+
type LambdaPayload struct {
23+
ConnectBackAddress string
24+
ProxyType string
1925
}
2026

2127
func (l *LambdaExecutionManager) run() {
@@ -33,7 +39,11 @@ func (l *LambdaExecutionManager) executeFunction(region int) error {
3339
log.Println("Executing Lambda function in region", l.regions[region])
3440
sess := session.New(&aws.Config{})
3541
svc := lambda.New(sess, &aws.Config{Region: aws.String(l.regions[region])})
36-
payload, _ := json.Marshal(l.publicIp + ":" + l.port)
42+
lambdaPayload := LambdaPayload{
43+
ConnectBackAddress: l.publicIp + ":" + l.port,
44+
ProxyType: l.proxyType,
45+
}
46+
payload, _ := json.Marshal(lambdaPayload)
3747
params := &lambda.InvokeInput{
3848
FunctionName: aws.String(lambdaFunctionName),
3949
InvocationType: aws.String(lambda.InvocationTypeEvent),
@@ -46,7 +56,7 @@ func (l *LambdaExecutionManager) executeFunction(region int) error {
4656
return nil
4757
}
4858

49-
func newLambdaExecutionManager(port string, regions []string, frequency time.Duration) (*LambdaExecutionManager, error) {
59+
func newLambdaExecutionManager(port string, regions []string, frequency time.Duration, proxyType string) (*LambdaExecutionManager, error) {
5060
publicIp, err := getPublicIp()
5161
if err != nil {
5262
return nil, errors.Wrap(err, "Error getting public IP address")
@@ -56,6 +66,7 @@ func newLambdaExecutionManager(port string, regions []string, frequency time.Dur
5666
regions: regions,
5767
frequency: frequency,
5868
publicIp: publicIp,
69+
proxyType: proxyType,
5970
}
6071
go executionManager.run()
6172
return executionManager, nil

version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package awslambdaproxy
22

33
import "strings"
44

5-
const Version = "0.0.1"
5+
const Version = "0.0.2"
66
func LambdaVersion() string {
77
return "v" + strings.Replace(Version, ".", "-", -1)
88
}

0 commit comments

Comments
 (0)