tangled
alpha
login
or
join now
tjh.dev
/
core
forked from
tangled.org/core
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
implement two-way signature verification
oppi.li
1 year ago
f9cea457
0d751a41
+62
-18
5 changed files
expand all
collapse all
unified
split
appview
db
db.go
state
state.go
cmd
knotserver
main.go
knotserver
middleware.go
routes.go
+4
-2
appview/db/db.go
···
108
108
109
109
var secret string
110
110
err := res.Scan(&secret)
111
111
-
if err != nil {
112
112
-
return "", nil
111
111
+
if err != nil || secret == "" {
112
112
+
return "", err
113
113
}
114
114
+
115
115
+
log.Println("domain, secret: ", domain, secret)
114
116
115
117
return secret, nil
116
118
}
+45
-12
appview/state/state.go
···
4
4
"crypto/hmac"
5
5
"crypto/sha256"
6
6
"encoding/hex"
7
7
+
"fmt"
7
8
"log"
8
9
"net/http"
9
9
-
"net/url"
10
10
"time"
11
11
12
12
"github.com/go-chi/chi/v5"
···
83
83
84
84
// check if domain is valid url, and strip extra bits down to just host
85
85
domain := r.FormValue("domain")
86
86
-
url, err := url.Parse(domain)
87
86
if domain == "" || err != nil {
87
87
+
log.Println(err)
88
88
http.Error(w, "Invalid form", http.StatusBadRequest)
89
89
return
90
90
}
91
91
92
92
-
key, err := s.Db.GenerateRegistrationKey(url.Host, did)
92
92
+
key, err := s.Db.GenerateRegistrationKey(domain, did)
93
93
94
94
if err != nil {
95
95
log.Println(err)
···
112
112
return
113
113
}
114
114
115
115
+
log.Println("checking ", domain)
116
116
+
115
117
secret, err := s.Db.GetRegistrationKey(domain)
116
118
if err != nil {
117
119
log.Printf("no key found for domain %s: %s\n", domain, err)
118
120
return
119
121
}
120
120
-
121
121
-
hmac := hmac.New(sha256.New, []byte(secret))
122
122
-
signature := hex.EncodeToString(hmac.Sum(nil))
122
122
+
log.Println("has secret ", secret)
123
123
124
124
// make a request do the knotserver with an empty body and above signature
125
125
-
url, _ := url.Parse(domain)
126
126
-
url = url.JoinPath("check")
127
127
-
pingRequest, err := http.NewRequest("GET", url.String(), nil)
125
125
+
url := fmt.Sprintf("http://%s/internal/health", domain)
126
126
+
127
127
+
pingRequest, err := buildPingRequest(url, secret)
128
128
if err != nil {
129
129
-
log.Println("failed to create ping request for ", url.String())
129
129
+
log.Println("failed to build ping request", err)
130
130
return
131
131
}
132
132
-
pingRequest.Header.Set("X-Signature", signature)
133
132
134
133
client := &http.Client{
135
134
Timeout: 5 * time.Second,
···
142
141
}
143
142
144
143
if resp.StatusCode != http.StatusOK {
145
145
-
log.Println("status nok")
144
144
+
log.Println("status nok", resp.StatusCode)
146
145
w.Write([]byte("no dice"))
147
146
return
148
147
}
148
148
+
149
149
+
// verify response mac
150
150
+
signature := resp.Header.Get("X-Signature")
151
151
+
signatureBytes, err := hex.DecodeString(signature)
152
152
+
if err != nil {
153
153
+
return
154
154
+
}
155
155
+
156
156
+
expectedMac := hmac.New(sha256.New, []byte(secret))
157
157
+
expectedMac.Write([]byte("ok"))
158
158
+
159
159
+
if !hmac.Equal(expectedMac.Sum(nil), signatureBytes) {
160
160
+
log.Printf("response body signature mismatch: %x\n", signatureBytes)
161
161
+
return
162
162
+
}
163
163
+
149
164
w.Write([]byte("check success"))
150
165
151
166
// mark as registered
152
167
s.Db.Register(domain)
153
168
154
169
return
170
170
+
}
171
171
+
172
172
+
func buildPingRequest(url, secret string) (*http.Request, error) {
173
173
+
pingRequest, err := http.NewRequest("GET", url, nil)
174
174
+
if err != nil {
175
175
+
return nil, err
176
176
+
}
177
177
+
178
178
+
timestamp := time.Now().Format(time.RFC3339)
179
179
+
mac := hmac.New(sha256.New, []byte(secret))
180
180
+
message := pingRequest.Method + pingRequest.URL.Path + timestamp
181
181
+
mac.Write([]byte(message))
182
182
+
signature := hex.EncodeToString(mac.Sum(nil))
183
183
+
184
184
+
pingRequest.Header.Set("X-Signature", signature)
185
185
+
pingRequest.Header.Set("X-Timestamp", timestamp)
186
186
+
187
187
+
return pingRequest, nil
155
188
}
156
189
157
190
func (s *State) Router() http.Handler {
+3
-4
cmd/knotserver/main.go
···
7
7
"log/slog"
8
8
"net/http"
9
9
"os"
10
10
-
"os/signal"
11
11
-
"syscall"
12
10
13
11
"github.com/icyphox/bild/knotserver"
14
12
"github.com/icyphox/bild/knotserver/config"
15
13
)
16
14
17
15
func main() {
18
18
-
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
19
19
-
defer stop()
16
16
+
ctx := context.Background()
17
17
+
// ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
18
18
+
// defer stop()
20
19
21
20
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil)))
22
21
+2
knotserver/middleware.go
···
4
4
"crypto/hmac"
5
5
"crypto/sha256"
6
6
"encoding/hex"
7
7
+
"log"
7
8
"net/http"
8
9
"time"
9
10
)
···
11
12
func (h *Handle) VerifySignature(next http.Handler) http.Handler {
12
13
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
13
14
signature := r.Header.Get("X-Signature")
15
15
+
log.Println(signature)
14
16
if signature == "" || !h.verifyHMAC(signature, r) {
15
17
writeError(w, "signature verification failed", http.StatusForbidden)
16
18
return
+8
knotserver/routes.go
···
2
2
3
3
import (
4
4
"compress/gzip"
5
5
+
"crypto/hmac"
6
6
+
"crypto/sha256"
7
7
+
"encoding/hex"
5
8
"encoding/json"
6
9
"errors"
7
10
"fmt"
···
404
407
// }
405
408
406
409
func (h *Handle) Health(w http.ResponseWriter, r *http.Request) {
410
410
+
log.Println("got health check")
411
411
+
mac := hmac.New(sha256.New, []byte(h.c.Secret))
412
412
+
mac.Write([]byte("ok"))
413
413
+
w.Header().Add("X-Signature", hex.EncodeToString(mac.Sum(nil)))
414
414
+
407
415
w.Write([]byte("ok"))
408
416
}