mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-05 14:57:49 +00:00
cmd/tsconnect: make logtail uploading work
Initialize logtail and provide an uploader that works in the browser (we make a no-cors cross-origin request to avoid having to open up the logcatcher servers to CORS). Fixes #5147 Signed-off-by: Mihai Parparita <mihai@tailscale.com>
This commit is contained in:
parent
4950fe60bd
commit
f371a1afd9
@ -31,11 +31,12 @@
|
||||
"tailscale.com/ipn/ipnlocal"
|
||||
"tailscale.com/ipn/ipnserver"
|
||||
"tailscale.com/ipn/store/mem"
|
||||
"tailscale.com/logpolicy"
|
||||
"tailscale.com/logtail"
|
||||
"tailscale.com/net/netns"
|
||||
"tailscale.com/net/tsdial"
|
||||
"tailscale.com/safesocket"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/wgengine"
|
||||
"tailscale.com/wgengine/netstack"
|
||||
"tailscale.com/words"
|
||||
@ -56,7 +57,25 @@ func main() {
|
||||
|
||||
func newIPN(jsConfig js.Value) map[string]any {
|
||||
netns.SetEnabled(false)
|
||||
var logf logger.Logf = log.Printf
|
||||
|
||||
jsStateStorage := jsConfig.Get("stateStorage")
|
||||
var store ipn.StateStore
|
||||
if jsStateStorage.IsUndefined() {
|
||||
store = new(mem.Store)
|
||||
} else {
|
||||
store = &jsStateStore{jsStateStorage}
|
||||
}
|
||||
|
||||
lpc := getOrCreateLogPolicyConfig(store)
|
||||
c := logtail.Config{
|
||||
Collection: lpc.Collection,
|
||||
PrivateID: lpc.PrivateID,
|
||||
// NewZstdEncoder is intentionally not passed in, compressed requests
|
||||
// set HTTP headers that are not supported by the no-cors fetching mode.
|
||||
HTTPC: &http.Client{Transport: &noCORSTransport{http.DefaultTransport}},
|
||||
}
|
||||
logtail := logtail.NewLogger(c, log.Printf)
|
||||
logf := logtail.Logf
|
||||
|
||||
dialer := new(tsdial.Dialer)
|
||||
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
|
||||
@ -86,14 +105,7 @@ func newIPN(jsConfig js.Value) map[string]any {
|
||||
return ns.DialContextTCP(ctx, dst)
|
||||
}
|
||||
|
||||
jsStateStorage := jsConfig.Get("stateStorage")
|
||||
var store ipn.StateStore
|
||||
if jsStateStorage.IsUndefined() {
|
||||
store = new(mem.Store)
|
||||
} else {
|
||||
store = &jsStateStore{jsStateStorage}
|
||||
}
|
||||
srv, err := ipnserver.New(log.Printf, "some-logid", store, eng, dialer, nil, ipnserver.Options{
|
||||
srv, err := ipnserver.New(logf, lpc.PublicID.String(), store, eng, dialer, nil, ipnserver.Options{
|
||||
SurviveDisconnects: true,
|
||||
LoginFlags: controlclient.LoginEphemeral,
|
||||
})
|
||||
@ -527,3 +539,40 @@ func makePromise(f func() (any, error)) js.Value {
|
||||
promiseConstructor := js.Global().Get("Promise")
|
||||
return promiseConstructor.New(handler)
|
||||
}
|
||||
|
||||
const logPolicyStateKey = "log-policy"
|
||||
|
||||
func getOrCreateLogPolicyConfig(state ipn.StateStore) *logpolicy.Config {
|
||||
if configBytes, err := state.ReadState(logPolicyStateKey); err == nil {
|
||||
if config, err := logpolicy.ConfigFromBytes(configBytes); err == nil {
|
||||
return config
|
||||
} else {
|
||||
log.Printf("Could not parse log policy config: %v", err)
|
||||
}
|
||||
} else if err != ipn.ErrStateNotExist {
|
||||
log.Printf("Could not get log policy config from state store: %v", err)
|
||||
}
|
||||
config := logpolicy.NewConfig(logtail.CollectionNode)
|
||||
if err := state.WriteState(logPolicyStateKey, config.ToBytes()); err != nil {
|
||||
log.Printf("Could not save log policy config to state store: %v", err)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// noCORSTransport wraps a RoundTripper and forces the no-cors mode on requests,
|
||||
// so that we can use it with non-CORS-aware servers.
|
||||
type noCORSTransport struct {
|
||||
http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *noCORSTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req.Header.Set("js.fetch:mode", "no-cors")
|
||||
resp, err := t.RoundTripper.RoundTrip(req)
|
||||
if err == nil {
|
||||
// In no-cors mode no response properties are returned. Populate just
|
||||
// the status so that callers do not think this was an error.
|
||||
resp.StatusCode = http.StatusOK
|
||||
resp.Status = http.StatusText(http.StatusOK)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
22
logtail/filch/filch_js.go
Normal file
22
logtail/filch/filch_js.go
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package filch
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func saveStderr() (*os.File, error) {
|
||||
return os.Stderr, nil
|
||||
}
|
||||
|
||||
func unsaveStderr(f *os.File) error {
|
||||
os.Stderr = f
|
||||
return nil
|
||||
}
|
||||
|
||||
func dup2Stderr(f *os.File) error {
|
||||
return nil
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
//go:build !windows && !js
|
||||
// +build !windows,!js
|
||||
|
||||
package filch
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user