diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cd25498 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +default: all + +version=$(shell ver=$$(git log -n 1 --pretty=oneline --format=%D | awk -F, '{print $$1}' | awk '{print $$3}'); \ + if [ "$$ver" = "master" ] ; then \ + ver="master($$(git log -n 1 --pretty=oneline --format=%h))" ; \ + fi ; \ + echo $$ver) + +client: + go build -ldflags "-X main.version=${version}" -o ./build/ck-client ./cmd/ck-client + +server: + go build -ldflags "-X main.version=${version}" -o ./build/ck-server ./cmd/ck-server + +install: + mv build/ck-* /usr/local/bin + +all: client server + +clean: + rm -rf ./build/ck-* diff --git a/README.md b/README.md index 0daf3ce..d30746c 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ Besides, Cloak allows multiple users to use one server **on a single port**. QoS ## Setup Instructions for the administrator of the server 0. [Install and configure shadowsocks-libev on your server](https://github.com/shadowsocks/shadowsocks-libev#installation) 1. Clone this repo onto your server -2. Build and run cmd/keygen -k. The base64 string before the comma is the public key, the one after the comma is the private key -3. Run cmd/keygen -u. This will be used as the AdminUID +2. Build and run cmd/ck-server -k. The base64 string before the comma is the public key, the one after the comma is the private key +3. Run cmd/ck-server -u. This will be used as the AdminUID 4. Put the private key and the AdminUID you obtained previously into config/ckserver.json 5. Edit the configuration file of shadowsocks-libev (default location is /etc/shadowsocks-libev/config.json). Let `server_port` be `443`, `plugin` be the full path to the ck-server binary and `plugin_opts` be the full path to ckserver.json. If the fields `plugin` and `plugin_opts` were not present originally, add these fields to the config file. 6. Run ss-server as root (because we are binding to TCP port 443) ### If you want to add more users -1. Run cmd/keygen -u to generate a new UID +1. Run cmd/ck-server -u to generate a new UID 2. On your client, run `ck-client -a -c ` to enter admin mode 3. Input as prompted, that is your ip:port of the server and your AdminUID. Enter 4 to create a new user. 4. Enter the UID in your ckclient.json as the prompted UID, enter SessionsCap (maximum amount of concurrent sessions a user can have), UpRate and DownRate (in bytes/s), UpCredit and DownCredit (in bytes) and ExpiryTime (as a unix epoch) diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index b3c65fc..f68518d 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -204,17 +204,29 @@ func main() { flag.StringVar(&pluginOpts, "c", "server.json", "pluginOpts: path to server.json or options seperated by semicolons") askVersion := flag.Bool("v", false, "Print the version number") printUsage := flag.Bool("h", false, "Print this message") + + genUID := flag.Bool("u", false, "Generate a UID") + genKeyPair := flag.Bool("k", false, "Generate a pair of public and private key, output in the format of pubkey,pvkey") + flag.Parse() if *askVersion { fmt.Printf("ck-server %s\n", version) return } - if *printUsage { flag.Usage() return } + if *genUID { + fmt.Println(generateUID()) + return + } + if *genKeyPair { + pub, pv := generateKeyPair() + fmt.Printf("%v,%v", pub, pv) + return + } if *localAddr == "" { log.Fatal("Must specify localAddr") diff --git a/cmd/ck-server/keygen.go b/cmd/ck-server/keygen.go new file mode 100644 index 0000000..b0f7958 --- /dev/null +++ b/cmd/ck-server/keygen.go @@ -0,0 +1,23 @@ +package main + +import ( + "crypto/rand" + "encoding/base64" + ecdh "github.com/cbeuw/go-ecdh" +) + +var b64 = base64.StdEncoding.EncodeToString + +func generateUID() string { + UID := make([]byte, 32) + rand.Read(UID) + return b64(UID) +} + +func generateKeyPair() (string, string) { + ec := ecdh.NewCurve25519ECDH() + staticPv, staticPub, _ := ec.GenerateKey(rand.Reader) + marshPub := ec.Marshal(staticPub) + marshPv := staticPv.(*[32]byte)[:] + return b64(marshPub), b64(marshPv) +} diff --git a/cmd/keygen/keygen.go b/cmd/keygen/keygen.go deleted file mode 100644 index 2a15677..0000000 --- a/cmd/keygen/keygen.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "crypto/rand" - "encoding/base64" - "flag" - "fmt" - ecdh "github.com/cbeuw/go-ecdh" -) - -var b64 = base64.StdEncoding.EncodeToString - -func main() { - var isUID *bool - var isKeypair *bool - isUID = flag.Bool("u", false, "Generate UID") - isKeypair = flag.Bool("k", false, "Generate a key pair") - flag.Parse() - - if *isUID { - UID := make([]byte, 32) - rand.Read(UID) - fmt.Printf(b64(UID)) - } else if *isKeypair { - ec := ecdh.NewCurve25519ECDH() - staticPv, staticPub, _ := ec.GenerateKey(rand.Reader) - marshPub := ec.Marshal(staticPub) - marshPv := staticPv.(*[32]byte)[:] - - fmt.Printf("%v,%v", b64(marshPub), b64(marshPv)) - - } -} diff --git a/internal/client/state.go b/internal/client/state.go index 92939ad..29e6cc0 100644 --- a/internal/client/state.go +++ b/internal/client/state.go @@ -57,11 +57,10 @@ func InitState(localHost, localPort, remoteHost, remotePort string, nowFunc func // semi-colon separated value. This is for Android plugin options func ssvToJson(ssv string) (ret []byte) { - // FIXME: base64 encoded data has =. How to escape? unescape := func(s string) string { - r := strings.Replace(s, "\\\\", "\\", -1) - r = strings.Replace(r, "\\=", "=", -1) - r = strings.Replace(r, "\\;", ";", -1) + r := strings.Replace(s, `\\`, `\`, -1) + r = strings.Replace(r, `\=`, `=`, -1) + r = strings.Replace(r, `\;`, `;`, -1) return r } lines := strings.Split(unescape(ssv), ";") @@ -76,9 +75,9 @@ func ssvToJson(ssv string) (ret []byte) { // JSON doesn't like quotation marks around int // Yes this is extremely ugly but it's still better than writing a tokeniser if key == "TicketTimeHint" || key == "NumConn" { - ret = append(ret, []byte("\""+key+"\":"+value+",")...) + ret = append(ret, []byte(`"`+key+`":`+value+`,`)...) } else { - ret = append(ret, []byte("\""+key+"\":\""+value+"\",")...) + ret = append(ret, []byte(`"`+key+`":"`+value+`",`)...) } } ret = ret[:len(ret)-1] // remove the last comma diff --git a/internal/server/state.go b/internal/server/state.go index b0d1167..b6d4836 100644 --- a/internal/server/state.go +++ b/internal/server/state.go @@ -60,9 +60,9 @@ func InitState(localHost, localPort, remoteHost, remotePort string, nowFunc func // semi-colon separated value. func ssvToJson(ssv string) (ret []byte) { unescape := func(s string) string { - r := strings.Replace(s, "\\\\", "\\", -1) - r = strings.Replace(r, "\\=", "=", -1) - r = strings.Replace(r, "\\;", ";", -1) + r := strings.Replace(s, `\\`, `\`, -1) + r = strings.Replace(r, `\=`, `=`, -1) + r = strings.Replace(r, `\;`, `;`, -1) return r } lines := strings.Split(unescape(ssv), ";") @@ -74,7 +74,7 @@ func ssvToJson(ssv string) (ret []byte) { sp := strings.SplitN(ln, "=", 2) key := sp[0] value := sp[1] - ret = append(ret, []byte("\""+key+"\":\""+value+"\",")...) + ret = append(ret, []byte(`"`+key+`":"`+value+`",`)...) } ret = ret[:len(ret)-1] // remove the last comma