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

Commit 2e0bd95

Browse files
author
Dan Vittegleo
committed
playing around with connection management
1 parent 84b9bee commit 2e0bd95

File tree

8 files changed

+105
-43
lines changed

8 files changed

+105
-43
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ The easiest way is to download a pre-built binary from the [GitHub Releases](htt
4444

4545
1. Copy `awslambdaproxy` binary to a publicly accessible linux host (e.g. EC2 instance). You will need to open the following ports on this host:
4646

47-
* Port 8080 - this port listens for user proxy connections and needs to only be opened to whatever your external IP address is where you plan to browse the web.
48-
* Port 8081 - this port listens for tunnel connections from executing Lambda functions and needs to be opened to the world. This is a security concern and will be locked down in the future.
47+
* Port 8080 - this port listens for user proxy connections and needs to only be opened to whatever your external IP address is where you plan to browse the web. Alternatively on OSX, I use [Secure Pipes](https://www.opoet.com/pyro/) to setup a SSH tunnel with port 8080 forwarded to localhost. This allows port 8080 to remain unexposed and instead relies on SSH being exposed.
48+
* Port 8081 - this port listens for tunnel connections from executing Lambda functions and needs to be opened to the world. <b>This is a security concern and will be locked down in the future.</b>
4949

5050
2. On publicly accessible host, run `awslambdaproxy`. You'll need to ensure AWS access key and secret key environment variables are defined. For now, this access key should have AdministratorAccess.
5151

@@ -59,7 +59,7 @@ The easiest way is to download a pre-built binary from the [GitHub Releases](htt
5959

6060
## FAQ
6161
1. <b>Should I use awslambdaproxy?</b> That's up to you. Use at your own risk.
62-
2. <b>Will this make me completely anonymous?</b> No, absolutely not. The goal of this project is just to obfuscate your web traffic by rotating your IP address. All of your traffic is going through AWS which could be traced back to your account. You can also be tracked still with [browser fingerprinting](https://panopticlick.eff.org/), etc.
62+
2. <b>Will this make me completely anonymous?</b> No, absolutely not. The goal of this project is just to obfuscate your web traffic by rotating your IP address. All of your traffic is going through AWS which could be traced back to your account. You can also be tracked still with [browser fingerprinting](https://panopticlick.eff.org/), etc. Your [IP address may still leak](https://ipleak.net/) due to WebRTC, Flash, etc.
6363
3. <b>How often will my external IP address change?</b> For each region specified, the IP address will change roughly every 4 hours. This of course is subject to change at any moment as this is not something that is documented by AWS Lambda.
6464
4. <b>How big is the pool of IP addresses?</b> This I don't know, but I do know I did not have a duplicate IP while running the proxy for a week.
6565
5. <b>How much does this cost?</b> awslambdaproxy should be able to run mostly on the [AWS free tier](https://aws.amazon.com/free/) minus bandwidth costs. It can run on a t2.micro instance and the default 128MB Lambda function that is constantly running should also fall in the free tier usage. The bandwidth is what will cost you money; you will pay for bandwidth usage for both EC2 and Lambda.

cmd/awslambdaproxy/awslambdaproxy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const (
1515

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)")
18-
frequencyPtr := flag.Int( "frequency", 10, "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")
18+
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")
1919
proxyPortPtr := flag.String("proxy-port", "8080", "Port to listen for proxy connections")
2020
tunnelPortPtr := flag.String("tunnel-port", "8081", "Port to listen for reverse connection from Lambda")
2121
flag.Parse()

datacopy.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,20 @@ func (d *DataCopyManager) run() {
1818
}
1919

2020
func (d *DataCopyManager) handleClient(userConn net.Conn) {
21-
d.tunnelConnectionManager.mutex.RLock()
22-
tunnelStream, err := d.tunnelConnectionManager.lastTunnel.sess.Open()
23-
d.tunnelConnectionManager.mutex.RUnlock()
21+
tunnelStream, err := d.tunnelConnectionManager.getActiveSession()
2422
if err != nil {
2523
log.Println("Failed to open stream to remote")
2624
return
2725
}
2826

29-
log.Println("Opened stream to remote " + tunnelStream.RemoteAddr().String())
3027
bidirectionalCopy(tunnelStream, userConn)
3128
}
3229

3330
func newDataCopyManager(user *UserConnectionManager, tunnel *TunnelConnectionManager) *DataCopyManager {
34-
return &DataCopyManager{
31+
copyManager := &DataCopyManager{
3532
userConnectionManager: user,
3633
tunnelConnectionManager: tunnel,
3734
}
35+
go copyManager.run()
36+
return copyManager
3837
}

init.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import (
44
"time"
55
"os"
66
"log"
7+
"runtime"
78
)
89

910
func ServerInit(proxyPort string, tunnelPort string, regions []string, lambdaExecutionFrequency time.Duration) {
10-
lambdaExecutionTimeout := int64(lambdaExecutionFrequency.Seconds()) + int64(5)
11+
lambdaExecutionTimeout := int64(lambdaExecutionFrequency.Seconds()) + int64(10)
1112

1213
log.Println("Setting up Lambda infrastructure")
1314
err := setupLambdaInfrastructure(regions, lambdaExecutionTimeout)
@@ -17,20 +18,27 @@ func ServerInit(proxyPort string, tunnelPort string, regions []string, lambdaExe
1718
}
1819

1920
log.Println("Starting TunnelConnectionManager")
20-
tunnelConnectionManager, err := newTunnelConnectionManager(tunnelPort)
21+
tunnelConnectionManager, err := newTunnelConnectionManager(tunnelPort, lambdaExecutionFrequency)
2122
if err != nil {
2223
log.Println("Failed to setup TunnelConnectionManager", err.Error())
2324
os.Exit(1)
2425
}
25-
go tunnelConnectionManager.run()
2626

2727
log.Println("Starting LambdaExecutionManager")
2828
lambdaExecutionManager, err := newLambdaExecutionManager(tunnelPort, regions, lambdaExecutionFrequency)
2929
if err != nil {
3030
log.Println("Failed to setup LambdaExecutionManager", err.Error())
3131
os.Exit(1)
3232
}
33-
go lambdaExecutionManager.run()
33+
34+
go func(){
35+
for {
36+
<-tunnelConnectionManager.emergencyTunnel
37+
log.Println("EMERGENCY TUNNEL STARTED")
38+
lambdaExecutionManager.executeFunction(0)
39+
time.Sleep(time.Second * 5)
40+
}
41+
}()
3442

3543
tunnelConnectionManager.waitUntilReady()
3644

@@ -40,9 +48,9 @@ func ServerInit(proxyPort string, tunnelPort string, regions []string, lambdaExe
4048
log.Println("Failed to setup UserConnectionManager", err.Error())
4149
os.Exit(1)
4250
}
43-
go userConnectionManager.run()
4451

4552
log.Println("Starting DataCopyManager")
46-
dataCopyManager := newDataCopyManager(userConnectionManager, tunnelConnectionManager)
47-
dataCopyManager.run()
53+
newDataCopyManager(userConnectionManager, tunnelConnectionManager)
54+
55+
runtime.Goexit()
4856
}

lambdaexecution.go

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,34 +22,41 @@ func (l *LambdaExecutionManager) run() {
2222
log.Println("Using public IP", l.publicIp)
2323
log.Println("Lambda execution frequency", l.frequency)
2424
for {
25-
sess := session.New(&aws.Config{})
2625
for region := range l.regions {
27-
svc := lambda.New(sess, &aws.Config{Region: aws.String(l.regions[region])})
28-
payload, _ := json.Marshal(l.publicIp + ":" + l.port)
29-
params := &lambda.InvokeInput{
30-
FunctionName: aws.String(lambdaFunctionName),
31-
InvocationType: aws.String(lambda.InvocationTypeEvent),
32-
Payload: payload,
33-
}
34-
log.Println("Executing Lambda function in region", l.regions[region])
35-
_, err := svc.Invoke(params)
36-
if err != nil {
37-
log.Println("Failed to execute Lambda function.", err.Error())
38-
}
26+
l.executeFunction(region)
3927
time.Sleep(l.frequency)
4028
}
4129
}
4230
}
4331

32+
func (l *LambdaExecutionManager) executeFunction(region int) error {
33+
log.Println("Executing Lambda function in region", l.regions[region])
34+
sess := session.New(&aws.Config{})
35+
svc := lambda.New(sess, &aws.Config{Region: aws.String(l.regions[region])})
36+
payload, _ := json.Marshal(l.publicIp + ":" + l.port)
37+
params := &lambda.InvokeInput{
38+
FunctionName: aws.String(lambdaFunctionName),
39+
InvocationType: aws.String(lambda.InvocationTypeEvent),
40+
Payload: payload,
41+
}
42+
_, err := svc.Invoke(params)
43+
if err != nil {
44+
return errors.Wrap(err, "Failed to execute Lambda function")
45+
}
46+
return nil
47+
}
48+
4449
func newLambdaExecutionManager(port string, regions []string, frequency time.Duration) (*LambdaExecutionManager, error) {
4550
publicIp, err := getPublicIp()
4651
if err != nil {
47-
return nil, errors.Wrap(err, "Error getting IP address")
52+
return nil, errors.Wrap(err, "Error getting public IP address")
4853
}
49-
return &LambdaExecutionManager{
54+
executionManager := &LambdaExecutionManager{
5055
port: port,
5156
regions: regions,
5257
frequency: frequency,
5358
publicIp: publicIp,
54-
}, nil
59+
}
60+
go executionManager.run()
61+
return executionManager, nil
5562
}

tunnelconnection.go

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@ import (
1313
type TunnelConnection struct {
1414
conn net.Conn
1515
sess *yamux.Session
16+
time time.Time
1617
}
1718

1819
type TunnelConnectionManager struct {
19-
listener net.Listener
20-
lastTunnel TunnelConnection
21-
mutex sync.RWMutex
20+
listener net.Listener
21+
currentTunnel string
22+
tunnels map[string]TunnelConnection
23+
mutex sync.RWMutex
24+
expectedTunnelSeconds float64
25+
emergencyTunnel chan bool
2226
}
2327

2428
func (t *TunnelConnectionManager) run() {
@@ -38,8 +42,44 @@ func (t *TunnelConnectionManager) run() {
3842
log.Println("Established session to", tunnelSession.RemoteAddr())
3943

4044
t.mutex.Lock()
41-
t.lastTunnel = TunnelConnection{c, tunnelSession}
45+
t.currentTunnel = c.RemoteAddr().String()
46+
t.tunnels[t.currentTunnel] = TunnelConnection{c, tunnelSession, time.Now()}
4247
t.mutex.Unlock()
48+
go t.monitorConnectionHealth(c.RemoteAddr().String())
49+
log.Println("Active tunnel count: ", len(t.tunnels))
50+
}
51+
}
52+
53+
func (t *TunnelConnectionManager) monitorConnectionHealth(connectionId string) {
54+
for {
55+
_, err := t.tunnels[connectionId].sess.Ping()
56+
if err != nil {
57+
if time.Since(t.tunnels[connectionId].time).Seconds() < t.expectedTunnelSeconds {
58+
log.Println("Signaling for emergency tunnel due to tunnel ending early: ", time.Since(t.tunnels[connectionId].time).Seconds())
59+
t.emergencyTunnel <- true
60+
}
61+
t.tunnels[connectionId].sess.Close()
62+
t.tunnels[connectionId].conn.Close()
63+
t.mutex.Lock()
64+
delete(t.tunnels, connectionId)
65+
t.mutex.Unlock()
66+
break
67+
}
68+
time.Sleep(time.Millisecond * 250)
69+
}
70+
}
71+
72+
func (t *TunnelConnectionManager) getActiveSession() (net.Conn, error) {
73+
for {
74+
t.mutex.RLock()
75+
tunnel, ok := t.tunnels[t.currentTunnel]
76+
t.mutex.RUnlock()
77+
if ok {
78+
sess, err := tunnel.sess.Open()
79+
return sess, err
80+
}
81+
log.Println("TunnelConnectionManager.getActiveSession failed. Retrying..")
82+
time.Sleep(time.Second * 1)
4383
}
4484
}
4585

@@ -55,19 +95,26 @@ func (t *TunnelConnectionManager) waitUntilReady() {
5595
}
5696

5797
func (t *TunnelConnectionManager) isReady() bool {
58-
if t.lastTunnel == (TunnelConnection{}) {
98+
if t.currentTunnel == "" {
5999
return false
60100
} else {
61101
return true
62102
}
63103
}
64104

65-
func newTunnelConnectionManager(port string) (*TunnelConnectionManager, error) {
105+
func newTunnelConnectionManager(port string, lambdaExecutionFrequency time.Duration) (*TunnelConnectionManager, error) {
66106
listener, err := startTunnelListener(port)
67107
if err != nil {
68108
return nil, errors.Wrap(err, "Failed to start TunnelConnectionManager")
69109
}
70-
return &TunnelConnectionManager{listener: listener}, nil
110+
connectionManager := &TunnelConnectionManager{
111+
listener: listener,
112+
tunnels: make(map[string]TunnelConnection),
113+
emergencyTunnel: make(chan bool),
114+
expectedTunnelSeconds: lambdaExecutionFrequency.Seconds(),
115+
}
116+
go connectionManager.run()
117+
return connectionManager, nil
71118
}
72119

73120
func startTunnelListener(tunnelPort string) (net.Listener, error) {

userconnection.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ type UserConnectionManager struct {
1515
func (u *UserConnectionManager) run() {
1616
for {
1717
c, err := u.listener.Accept()
18-
log.Println("Accepted user connection..")
1918
if err != nil {
2019
log.Println("Failed to accept user connection")
2120
return
@@ -33,10 +32,12 @@ func newUserConnectionManager(port string) (*UserConnectionManager, error) {
3332
if err != nil {
3433
return nil, errors.Wrap(err, "Failed to start UserConnectionManager")
3534
}
36-
return &UserConnectionManager{
35+
connectionManager := &UserConnectionManager{
3736
listener: listener,
3837
connections: make(chan net.Conn),
39-
}, nil
38+
}
39+
go connectionManager.run()
40+
return connectionManager, nil
4041
}
4142

4243
func startUserListener(proxyPort string) (net.Listener, error) {

util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
)
1313

1414
const (
15-
getIPUrl = "http://myexternalip.com/raw"
15+
getIPUrl = "http://checkip.amazonaws.com/"
1616
)
1717

1818
func bidirectionalCopy(dst io.ReadWriteCloser, src io.ReadWriteCloser) {

0 commit comments

Comments
 (0)