Skip to content

Commit

Permalink
fork tomsteele/cloud-proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
freb committed Aug 9, 2019
0 parents commit 3af3fc3
Show file tree
Hide file tree
Showing 6 changed files with 449 additions and 0 deletions.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2016 Tom Steele

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
91 changes: 91 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Fork

Forked from tomsteele/cloud-proxy and repurposed to support scanning only, and maybe eventuall additional commands.

# cloud-proxy
cloud-proxy creates multiple DO droplets and then starts local socks proxies using SSH. After exiting, the droplets are deleted.

### Warning
This tool will deploy as many droplets as you desire, and will make a best effort to delete them after use. However, you are ultimately going to pay the bill for these droplets, and it is up to you, and you alone to ensure they actually get deleted.

### Install
Download a compiled release [here](https://github.com/tomsteele/cloud-proxy/releases/latest). You can now execute without any dependencies. Currently the only supported and tested OS is Linux:
```
$ ./cloud-proxy
```
### Usage
```
Usage of ./cloud-proxy:
-count int
Amount of droplets to deploy (default 5)
-force
Bypass built-in protections that prevent you from deploying more than 50 droplets
-key string
SSH key fingerprint
-key-location string
SSH key location (default "~/.ssh/id_rsa")
-name string
Droplet name prefix (default "cloud-proxy")
-regions string
Comma separated list of regions to deploy droplets to, defaults to all. (default "*")
-start-tcp int
TCP port to start first proxy on and increment from (default 55555)
-token string
DO API key
-v Print version and exit
```

### Getting Started
To use cloud-proxy you will need to have a DO API token, you can get one [here](https://cloud.digitalocean.com/settings/api/tokens). Next, ensure you have an SSH key saved on DO. This is the key that SSH will authentication with. The DO API and cloud-proxy require you to provide the fingerprint of the key you would like to use. You can obtain the fingerprint using `ssh-keygen`:
```
$ ssh-keygen -lf ~/.ssh/id_rsa.pub
```

If your key requires a passphrase, you will need to use ssh-agent:
```
$ eval `ssh-agent -s`
$ ssh-add ~/.ssh/id_rsa
```

Now you may create some proxies:
```
$ cloud-proxy -count 2 -token <api-token> -key <fingerprint>
```

When you are finished using your proxies, use CTRL-C to interrupt the program, cloud-proxy will catch the interrupt and delete the droplets.

cloud-proxy will output a proxy list for proxychains and [socksd](https://github.com/eahydra/socks/tree/master/cmd/socksd). proxychains can be configured to iterate over a random proxy for each connection by uncommenting `random_chain`, you should also comment out `string-chain`, which is the default. You will also need to uncomment `chain_len` and set it to `1`.

socksd can be helpful for programs that can accept a socks proxy, but may not work nicely with proxychains. socksd will listen as a socks proxy, and can be configured to use a set of upstream proxies, which it will iterate through in a round-robin manner. Follow the instructions in the README linked above, as it is self explanitory.

### Example Output
```
$ ./cloud-proxy -token <my_token> -key <my_fingerprint>
==> Info: Droplets deployed. Waiting 100 seconds...
==> Info: SSH proxy started on port 55555 on droplet name: cloud-proxy-1 IP: <IP>
==> Info: SSH proxy started on port 55556 on droplet name: cloud-proxy-2 IP: <IP>
==> Info: SSH proxy started on port 55557 on droplet name: cloud-proxy-3 IP: <IP>
==> Info: SSH proxy started on port 55558 on droplet name: cloud-proxy-4 IP: <IP>
==> Info: SSH proxy started on port 55559 on droplet name: cloud-proxy-5 IP: <IP>
==> Info: proxychains config
socks5 127.0.0.1 55555
socks5 127.0.0.1 55556
socks5 127.0.0.1 55557
socks5 127.0.0.1 55558
socks5 127.0.0.1 55559
==> Info: socksd config
"upstreams": [
{"type": "socks5", "address": "127.0.0.1:55555"},
{"type": "socks5", "address": "127.0.0.1:55556"},
{"type": "socks5", "address": "127.0.0.1:55557"},
{"type": "socks5", "address": "127.0.0.1:55558"},
{"type": "socks5", "address": "127.0.0.1:55559"}
]
==> Info: Please CTRL-C to destroy droplets
^C==> Info: Deleted droplet name: cloud-proxy-1
==> Info: Deleted droplet name: cloud-proxy-2
==> Info: Deleted droplet name: cloud-proxy-3
==> Info: Deleted droplet name: cloud-proxy-4
==> Info: Deleted droplet name: cloud-proxy-5
```

46 changes: 46 additions & 0 deletions do.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"fmt"

"github.com/digitalocean/godo"
"github.com/jmcvetta/randutil"
)

func doRegions(client *godo.Client) ([]string, error) {
slugs := []string{}
regions, _, err := client.Regions.List(&godo.ListOptions{})
if err != nil {
return slugs, err
}
for _, r := range regions {
slugs = append(slugs, r.Slug)
}
return slugs, nil
}

func newDropLetMultiCreateRequest(prefix, region, keyID string, count int) *godo.DropletMultiCreateRequest {

names := []string{}
for i := 0; i < count; i++ {
name, _ := randutil.AlphaString(8)
names = append(names, fmt.Sprintf("%s-%s", prefix, name))
}

return &godo.DropletMultiCreateRequest{
Names: names,
Region: region,
Size: "512mb",
Image: godo.DropletCreateImage{
Slug: "ubuntu-14-04-x64",
},
SSHKeys: []godo.DropletCreateSSHKey{
godo.DropletCreateSSHKey{
Fingerprint: keyID,
},
},
Backups: false,
IPv6: false,
PrivateNetworking: false,
}
}
105 changes: 105 additions & 0 deletions machine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package main

import (
"bufio"
"fmt"
"os/exec"

"github.com/digitalocean/godo"
)

// Machine is just a wrapper around a created droplet.
type Machine struct {
ID int
Name string
IPv4 string
SSHActive bool
Stderr *bufio.Reader
Listener string
CMD *exec.Cmd
}

// IsReady ensures that the machine has an IP address.
func (m *Machine) IsReady() bool {
return m.IPv4 != ""
}

// GetIPs populates the IPv4 address of the machine.
func (m *Machine) GetIPs(client *godo.Client) error {
droplet, _, err := client.Droplets.Get(m.ID)
if err != nil {
return err
}
m.IPv4, err = droplet.PublicIPv4()
if err != nil {
return err
}
return nil
}

// StartSSHProxy starts a socks proxy on 127.0.0.1 or the desired port.
func (m *Machine) StartSSHProxy(port, sshKeyLocation string) error {
m.Listener = port
m.CMD = exec.Command("ssh", "-N", "-D", port, "-o", "StrictHostKeyChecking=no", "-i", sshKeyLocation, fmt.Sprintf("root@%s", m.GetIP()))
stderr, err := m.CMD.StderrPipe()
if err != nil {
return err
}
m.Stderr = bufio.NewReader(stderr)
if err := m.CMD.Start(); err != nil {
return err
}
m.SSHActive = true
return nil
}

// Destroy deletes the droplet.
func (m *Machine) Destroy(client *godo.Client) error {
_, err := client.Droplets.Delete(m.ID)
return err
}

// GetIP returns the IPv4 address.
func (m *Machine) GetIP() string {
return m.IPv4
}

func dropletsToMachines(droplets []godo.Droplet) []Machine {
m := []Machine{}
for _, d := range droplets {
m = append(m, Machine{
ID: d.ID,
Name: d.Name,
})
}
return m
}

// PrintStdError reads from stderr from ssh and prints it to stdout.
func (m *Machine) PrintStdError() {
for {
str, err := m.Stderr.ReadString('\n')
if err != nil && str != "" {
fmt.Printf("From %s SSH stderr\n", m.Name)
fmt.Println(str)
}
}

}

func printProxyChains(machines []Machine) {
for _, m := range machines {
fmt.Printf("socks5 127.0.0.1 %s\n", m.Listener)
}
}

func printSocksd(machines []Machine) {
fmt.Printf("\"upstreams\": [\n")
for i, m := range machines {
fmt.Printf("{\"type\": \"socks5\", \"address\": \"127.0.0.1:%s\"}", m.Listener)
if i < len(machines)-1 {
fmt.Printf(",\n")
}
}
fmt.Printf("\n]\n")
}
Loading

0 comments on commit 3af3fc3

Please sign in to comment.