| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 5 | "crypto/rand" |
| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 6 | "crypto/tls" |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 7 | "encoding/base64" |
| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 8 | "encoding/json" |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 9 | "io" |
| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 10 | "net/http" |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 11 | |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 12 | "github.com/slackhq/nebula/cert" |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 13 | "golang.org/x/crypto/curve25519" |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 14 | "sigs.k8s.io/yaml" |
| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 15 | ) |
| 16 | |
| 17 | type VPNClient interface { |
| giolekva | 8d6a0ca | 2021-12-19 17:42:25 +0400 | [diff] [blame] | 18 | Sign(apiAddr string, message []byte) ([]byte, error) |
| giolekva | 1026d2d | 2021-12-19 19:09:15 +0400 | [diff] [blame^] | 19 | Join(apiAddr, hostname string, message, signature []byte) ([]byte, error) |
| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 20 | } |
| 21 | |
| 22 | type directVPNClient struct { |
| 23 | addr string |
| 24 | } |
| 25 | |
| 26 | func NewDirectVPNClient(addr string) VPNClient { |
| 27 | return &directVPNClient{addr} |
| 28 | } |
| 29 | |
| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 30 | type signReq struct { |
| 31 | Message []byte `json:"message"` |
| 32 | } |
| 33 | |
| 34 | type signResp struct { |
| 35 | Signature []byte `json:"signature"` |
| 36 | } |
| 37 | |
| giolekva | 8d6a0ca | 2021-12-19 17:42:25 +0400 | [diff] [blame] | 38 | func (c *directVPNClient) Sign(apiAddr string, message []byte) ([]byte, error) { |
| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 39 | var data bytes.Buffer |
| 40 | if err := json.NewEncoder(&data).Encode(signReq{message}); err != nil { |
| 41 | return nil, err |
| 42 | } |
| 43 | client := &http.Client{ |
| 44 | // TODO(giolekva): remove, for some reason valid certificates are not accepted on gioui android. |
| 45 | Transport: &http.Transport{ |
| 46 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
| 47 | }, |
| 48 | } |
| giolekva | 8d6a0ca | 2021-12-19 17:42:25 +0400 | [diff] [blame] | 49 | r, err := client.Post(apiAddr+"/api/sign", "application/json", &data) |
| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 50 | if err != nil { |
| giolekva | 313ee2b | 2021-12-15 15:17:29 +0400 | [diff] [blame] | 51 | return nil, err |
| 52 | } |
| 53 | resp := &signResp{} |
| 54 | if err := json.NewDecoder(r.Body).Decode(resp); err != nil { |
| 55 | return nil, err |
| 56 | } |
| 57 | return resp.Signature, nil |
| 58 | } |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 59 | |
| 60 | type joinReq struct { |
| 61 | Message []byte `json:"message"` |
| 62 | Signature []byte `json:"signature"` |
| 63 | Name string `json:"name"` |
| 64 | PublicKey []byte `json:"public_key"` |
| 65 | IPCidr string `json:"ip_cidr"` |
| 66 | } |
| 67 | |
| 68 | type joinResp struct { |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 69 | cfgYamlB64 string |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 70 | } |
| 71 | |
| giolekva | 1026d2d | 2021-12-19 19:09:15 +0400 | [diff] [blame^] | 72 | func (c *directVPNClient) Join(apiAddr, hostname string, message, signature []byte) ([]byte, error) { |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 73 | pubKey, privKey, err := x25519Keypair() |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 74 | if err != nil { |
| 75 | return nil, err |
| 76 | } |
| 77 | req := joinReq{ |
| 78 | message, |
| 79 | signature, |
| giolekva | 1026d2d | 2021-12-19 19:09:15 +0400 | [diff] [blame^] | 80 | hostname, |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 81 | cert.MarshalX25519PublicKey(pubKey), |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 82 | "111.0.0.13/24", |
| 83 | } |
| 84 | var data bytes.Buffer |
| 85 | if err := json.NewEncoder(&data).Encode(req); err != nil { |
| 86 | return nil, err |
| 87 | } |
| 88 | client := &http.Client{ |
| 89 | // TODO(giolekva): remove, for some reason valid certificates are not accepted on gioui android. |
| 90 | Transport: &http.Transport{ |
| 91 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
| 92 | }, |
| 93 | } |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 94 | r, err := client.Post(apiAddr+"/api/join", "application/json", &data) |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 95 | if err != nil { |
| 96 | return nil, err |
| 97 | } |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 98 | var cfgYamlB bytes.Buffer |
| 99 | _, err = io.Copy(&cfgYamlB, |
| 100 | base64.NewDecoder(base64.StdEncoding, r.Body)) |
| 101 | if err != nil { |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 102 | return nil, err |
| 103 | } |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 104 | cfgYaml := cfgYamlB.Bytes() |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 105 | var cfgMap map[string]interface{} |
| 106 | if err := yaml.Unmarshal(cfgYaml, &cfgMap); err != nil { |
| 107 | return nil, err |
| 108 | } |
| 109 | var pki map[string]interface{} |
| 110 | var ok bool |
| 111 | if pki, ok = cfgMap["pki"].(map[string]interface{}); !ok { |
| 112 | panic("Must not reach") |
| 113 | } |
| 114 | pki["key"] = string(cert.MarshalX25519PrivateKey(privKey)) |
| giolekva | cc3ebcb | 2021-12-17 10:52:17 +0400 | [diff] [blame] | 115 | return yaml.Marshal(cfgMap) |
| giolekva | f58a769 | 2021-12-15 18:05:39 +0400 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | func x25519Keypair() ([]byte, []byte, error) { |
| 119 | var pubkey, privkey [32]byte |
| 120 | if _, err := io.ReadFull(rand.Reader, privkey[:]); err != nil { |
| 121 | return nil, nil, err |
| 122 | } |
| 123 | curve25519.ScalarBaseMult(&pubkey, &privkey) |
| 124 | return pubkey[:], privkey[:], nil |
| 125 | } |