blob: 78a31f13bcc2e0bc8aa4c2c753b5455ea769d3b9 [file] [log] [blame]
giolekva313ee2b2021-12-15 15:17:29 +04001package main
2
3import (
4 "bytes"
giolekvaf58a7692021-12-15 18:05:39 +04005 "crypto/rand"
giolekva313ee2b2021-12-15 15:17:29 +04006 "crypto/tls"
giolekvacc3ebcb2021-12-17 10:52:17 +04007 "encoding/base64"
giolekva313ee2b2021-12-15 15:17:29 +04008 "encoding/json"
giolekvaf58a7692021-12-15 18:05:39 +04009 "io"
giolekva313ee2b2021-12-15 15:17:29 +040010 "net/http"
giolekvaf58a7692021-12-15 18:05:39 +040011
giolekvacc3ebcb2021-12-17 10:52:17 +040012 "github.com/slackhq/nebula/cert"
giolekvaf58a7692021-12-15 18:05:39 +040013 "golang.org/x/crypto/curve25519"
giolekvacc3ebcb2021-12-17 10:52:17 +040014 "sigs.k8s.io/yaml"
giolekva313ee2b2021-12-15 15:17:29 +040015)
16
17type VPNClient interface {
giolekva8d6a0ca2021-12-19 17:42:25 +040018 Sign(apiAddr string, message []byte) ([]byte, error)
giolekva1026d2d2021-12-19 19:09:15 +040019 Join(apiAddr, hostname string, message, signature []byte) ([]byte, error)
giolekva313ee2b2021-12-15 15:17:29 +040020}
21
22type directVPNClient struct {
23 addr string
24}
25
26func NewDirectVPNClient(addr string) VPNClient {
27 return &directVPNClient{addr}
28}
29
giolekva313ee2b2021-12-15 15:17:29 +040030type signReq struct {
31 Message []byte `json:"message"`
32}
33
34type signResp struct {
35 Signature []byte `json:"signature"`
36}
37
giolekva8d6a0ca2021-12-19 17:42:25 +040038func (c *directVPNClient) Sign(apiAddr string, message []byte) ([]byte, error) {
giolekva313ee2b2021-12-15 15:17:29 +040039 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 }
giolekva8d6a0ca2021-12-19 17:42:25 +040049 r, err := client.Post(apiAddr+"/api/sign", "application/json", &data)
giolekva313ee2b2021-12-15 15:17:29 +040050 if err != nil {
giolekva313ee2b2021-12-15 15:17:29 +040051 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}
giolekvaf58a7692021-12-15 18:05:39 +040059
60type 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
68type joinResp struct {
giolekvacc3ebcb2021-12-17 10:52:17 +040069 cfgYamlB64 string
giolekvaf58a7692021-12-15 18:05:39 +040070}
71
giolekva1026d2d2021-12-19 19:09:15 +040072func (c *directVPNClient) Join(apiAddr, hostname string, message, signature []byte) ([]byte, error) {
giolekvacc3ebcb2021-12-17 10:52:17 +040073 pubKey, privKey, err := x25519Keypair()
giolekvaf58a7692021-12-15 18:05:39 +040074 if err != nil {
75 return nil, err
76 }
77 req := joinReq{
78 message,
79 signature,
giolekva1026d2d2021-12-19 19:09:15 +040080 hostname,
giolekvacc3ebcb2021-12-17 10:52:17 +040081 cert.MarshalX25519PublicKey(pubKey),
giolekvaf58a7692021-12-15 18:05:39 +040082 "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 }
giolekvacc3ebcb2021-12-17 10:52:17 +040094 r, err := client.Post(apiAddr+"/api/join", "application/json", &data)
giolekvaf58a7692021-12-15 18:05:39 +040095 if err != nil {
96 return nil, err
97 }
giolekvacc3ebcb2021-12-17 10:52:17 +040098 var cfgYamlB bytes.Buffer
99 _, err = io.Copy(&cfgYamlB,
100 base64.NewDecoder(base64.StdEncoding, r.Body))
101 if err != nil {
giolekvaf58a7692021-12-15 18:05:39 +0400102 return nil, err
103 }
giolekvacc3ebcb2021-12-17 10:52:17 +0400104 cfgYaml := cfgYamlB.Bytes()
giolekvacc3ebcb2021-12-17 10:52:17 +0400105 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))
giolekvacc3ebcb2021-12-17 10:52:17 +0400115 return yaml.Marshal(cfgMap)
giolekvaf58a7692021-12-15 18:05:39 +0400116}
117
118func 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}