Compare commits
32 Commits
marwan/noc
...
raggi/tsde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e09e8d0701 | ||
|
|
12f8c98823 | ||
|
|
1c4a047ad0 | ||
|
|
f8f0b981ac | ||
|
|
a353ae079b | ||
|
|
43e230d4cd | ||
|
|
5dd0b02133 | ||
|
|
d3c8c3dd00 | ||
|
|
64f16f7f38 | ||
|
|
6554a0cbec | ||
|
|
d17312265e | ||
|
|
4321d1d6e9 | ||
|
|
2492ca2900 | ||
|
|
570cb018da | ||
|
|
dc1d8826a2 | ||
|
|
67882ad35d | ||
|
|
07eacdfe92 | ||
|
|
d06fac0ede | ||
|
|
9d09c821f7 | ||
|
|
2aa8299c37 | ||
|
|
88ee857bc8 | ||
|
|
1a691ec5b2 | ||
|
|
6a156f6243 | ||
|
|
525b9c806f | ||
|
|
fc5b137d25 | ||
|
|
32e0ba5e68 | ||
|
|
399a80785e | ||
|
|
c0b4a54146 | ||
|
|
c4fe9c536d | ||
|
|
370b2c37e0 | ||
|
|
cb94ddb7b8 | ||
|
|
66f97f4bea |
2
.github/workflows/go-licenses.yml
vendored
2
.github/workflows/go-licenses.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
private_key: ${{ secrets.LICENSING_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Send pull request
|
||||
uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 #v5.0.0
|
||||
uses: peter-evans/create-pull-request@284f54f989303d2699d373481a0cfa13ad5a6666 #v5.0.1
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
author: License Updater <noreply+license-updater@tailscale.com>
|
||||
|
||||
2
.github/workflows/update-flake.yml
vendored
2
.github/workflows/update-flake.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
private_key: ${{ secrets.LICENSING_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Send pull request
|
||||
uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 #v5.0.0
|
||||
uses: peter-evans/create-pull-request@284f54f989303d2699d373481a0cfa13ad5a6666 #v5.0.1
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
author: Flakes Updater <noreply+flakes-updater@tailscale.com>
|
||||
|
||||
5
Makefile
5
Makefile
@@ -48,11 +48,10 @@ staticcheck: ## Run staticcheck.io checks
|
||||
./tool/go run honnef.co/go/tools/cmd/staticcheck -- $$(./tool/go list ./... | grep -v tempfork)
|
||||
|
||||
spk: ## Build synology package for ${SYNO_ARCH} architecture and ${SYNO_DSM} DSM version
|
||||
PATH="${PWD}/tool:${PATH}" ./tool/go run github.com/tailscale/tailscale-synology@main -o tailscale.spk --source=. --goarch=${SYNO_ARCH} --dsm-version=${SYNO_DSM}
|
||||
./tool/go run ./cmd/dist build synology/dsm${SYNO_DSM}/${SYNO_ARCH}
|
||||
|
||||
spkall: ## Build synology packages for all architectures and DSM versions
|
||||
mkdir -p spks
|
||||
PATH="${PWD}/tool:${PATH}" ./tool/go run github.com/tailscale/tailscale-synology@main -o spks --source=. --goarch=all --dsm-version=all
|
||||
./tool/go run ./cmd/dist build synology
|
||||
|
||||
pushspk: spk ## Push and install synology package on ${SYNO_HOST} host
|
||||
echo "Pushing SPK to root@${SYNO_HOST} (env var SYNO_HOST) ..."
|
||||
|
||||
@@ -49,4 +49,4 @@ while [ "$#" -gt 1 ]; do
|
||||
esac
|
||||
done
|
||||
|
||||
exec ./tool/go build ${tags:+-tags=$tags} -ldflags "$ldflags" "$@"
|
||||
exec $go build ${tags:+-tags=$tags} -ldflags "$ldflags" "$@"
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/types/opt"
|
||||
)
|
||||
@@ -213,8 +212,20 @@ func (c *Client) DeleteDevice(ctx context.Context, deviceID string) (err error)
|
||||
|
||||
// AuthorizeDevice marks a device as authorized.
|
||||
func (c *Client) AuthorizeDevice(ctx context.Context, deviceID string) error {
|
||||
return c.SetAuthorized(ctx, deviceID, true)
|
||||
}
|
||||
|
||||
// SetAuthorized marks a device as authorized or not.
|
||||
func (c *Client) SetAuthorized(ctx context.Context, deviceID string, authorized bool) error {
|
||||
params := &struct {
|
||||
Authorized bool `json:"authorized"`
|
||||
}{Authorized: authorized}
|
||||
data, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := fmt.Sprintf("%s/api/v2/device/%s/authorized", c.baseURL(), url.PathEscape(deviceID))
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", path, strings.NewReader(`{"authorized":true}`))
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", path, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
25
cmd/dist/dist.go
vendored
25
cmd/dist/dist.go
vendored
@@ -13,15 +13,38 @@ import (
|
||||
|
||||
"tailscale.com/release/dist"
|
||||
"tailscale.com/release/dist/cli"
|
||||
"tailscale.com/release/dist/synology"
|
||||
"tailscale.com/release/dist/unixpkgs"
|
||||
)
|
||||
|
||||
var synologyPackageCenter bool
|
||||
|
||||
func getTargets() ([]dist.Target, error) {
|
||||
return unixpkgs.Targets(), nil
|
||||
var ret []dist.Target
|
||||
|
||||
ret = append(ret, unixpkgs.Targets()...)
|
||||
// Synology packages can be built either for sideloading, or for
|
||||
// distribution by Synology in their package center. When
|
||||
// distributed through the package center, apps can request
|
||||
// additional permissions to use a tuntap interface and control
|
||||
// the NAS's network stack, rather than be forced to run in
|
||||
// userspace mode.
|
||||
//
|
||||
// Since only we can provide packages to Synology for
|
||||
// distribution, we default to building the "sideload" variant of
|
||||
// packages that we distribute on pkgs.tailscale.com.
|
||||
ret = append(ret, synology.Targets(synologyPackageCenter)...)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
cmd := cli.CLI(getTargets)
|
||||
for _, subcmd := range cmd.Subcommands {
|
||||
if subcmd.Name == "build" {
|
||||
subcmd.FlagSet.BoolVar(&synologyPackageCenter, "synology-package-center", false, "build synology packages with extra metadata for the official package center")
|
||||
}
|
||||
}
|
||||
|
||||
if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil && !errors.Is(err, flag.ErrHelp) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/transport"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
@@ -38,7 +37,6 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
"sigs.k8s.io/yaml"
|
||||
"tailscale.com/client/tailscale"
|
||||
"tailscale.com/hostinfo"
|
||||
@@ -185,17 +183,17 @@ waitOnline:
|
||||
// the cache that sits a few layers below the builder stuff, which will
|
||||
// implicitly filter what parts of the world the builder code gets to see at
|
||||
// all.
|
||||
nsFilter := cache.ObjectSelector{
|
||||
Field: fields.SelectorFromSet(fields.Set{"metadata.namespace": tsNamespace}),
|
||||
nsFilter := cache.ByObject{
|
||||
Field: client.InNamespace(tsNamespace).AsSelector(),
|
||||
}
|
||||
restConfig := config.GetConfigOrDie()
|
||||
mgr, err := manager.New(restConfig, manager.Options{
|
||||
NewCache: cache.BuilderWithOptions(cache.Options{
|
||||
SelectorsByObject: map[client.Object]cache.ObjectSelector{
|
||||
Cache: cache.Options{
|
||||
ByObject: map[client.Object]cache.ByObject{
|
||||
&corev1.Secret{}: nsFilter,
|
||||
&appsv1.StatefulSet{}: nsFilter,
|
||||
},
|
||||
}),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
startlog.Fatalf("could not create manager: %v", err)
|
||||
@@ -211,7 +209,7 @@ waitOnline:
|
||||
logger: zlog.Named("service-reconciler"),
|
||||
}
|
||||
|
||||
reconcileFilter := handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request {
|
||||
reconcileFilter := handler.EnqueueRequestsFromMapFunc(func(_ context.Context, o client.Object) []reconcile.Request {
|
||||
ls := o.GetLabels()
|
||||
if ls[LabelManaged] != "true" {
|
||||
return nil
|
||||
@@ -231,8 +229,8 @@ waitOnline:
|
||||
err = builder.
|
||||
ControllerManagedBy(mgr).
|
||||
For(&corev1.Service{}).
|
||||
Watches(&source.Kind{Type: &appsv1.StatefulSet{}}, reconcileFilter).
|
||||
Watches(&source.Kind{Type: &corev1.Secret{}}, reconcileFilter).
|
||||
Watches(&appsv1.StatefulSet{}, reconcileFilter).
|
||||
Watches(&corev1.Secret{}, reconcileFilter).
|
||||
Complete(sr)
|
||||
if err != nil {
|
||||
startlog.Fatalf("could not create controller: %v", err)
|
||||
|
||||
@@ -110,6 +110,8 @@ func TestLoadBalancerClass(t *testing.T) {
|
||||
mustUpdate(t, fc, "default", "test", func(s *corev1.Service) {
|
||||
s.Spec.Type = corev1.ServiceTypeClusterIP
|
||||
s.Spec.LoadBalancerClass = nil
|
||||
})
|
||||
mustUpdateStatus(t, fc, "default", "test", func(s *corev1.Service) {
|
||||
// Fake client doesn't automatically delete the LoadBalancer status when
|
||||
// changing away from the LoadBalancer type, we have to do
|
||||
// controller-manager's work by hand.
|
||||
@@ -447,6 +449,8 @@ func TestLBIntoAnnotation(t *testing.T) {
|
||||
}
|
||||
s.Spec.Type = corev1.ServiceTypeClusterIP
|
||||
s.Spec.LoadBalancerClass = nil
|
||||
})
|
||||
mustUpdateStatus(t, fc, "default", "test", func(s *corev1.Service) {
|
||||
// Fake client doesn't automatically delete the LoadBalancer status when
|
||||
// changing away from the LoadBalancer type, we have to do
|
||||
// controller-manager's work by hand.
|
||||
@@ -777,6 +781,21 @@ func mustUpdate[T any, O ptrObject[T]](t *testing.T, client client.Client, ns, n
|
||||
}
|
||||
}
|
||||
|
||||
func mustUpdateStatus[T any, O ptrObject[T]](t *testing.T, client client.Client, ns, name string, update func(O)) {
|
||||
t.Helper()
|
||||
obj := O(new(T))
|
||||
if err := client.Get(context.Background(), types.NamespacedName{
|
||||
Name: name,
|
||||
Namespace: ns,
|
||||
}, obj); err != nil {
|
||||
t.Fatalf("getting %q: %v", name, err)
|
||||
}
|
||||
update(obj)
|
||||
if err := client.Status().Update(context.Background(), obj); err != nil {
|
||||
t.Fatalf("updating %q: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func expectEqual[T any, O ptrObject[T]](t *testing.T, client client.Client, want O) {
|
||||
t.Helper()
|
||||
got := O(new(T))
|
||||
|
||||
@@ -126,6 +126,10 @@ var syslogf logger.Logf = logger.Discard
|
||||
// At this point we're still the parent process that
|
||||
// Windows started.
|
||||
func runWindowsService(pol *logpolicy.Policy) error {
|
||||
go func() {
|
||||
winutil.LogSupportInfo(log.Printf)
|
||||
}()
|
||||
|
||||
if winutil.GetPolicyInteger("LogSCMInteractions", 0) != 0 {
|
||||
syslog, err := eventlog.Open(serviceName)
|
||||
if err == nil {
|
||||
|
||||
@@ -115,4 +115,4 @@
|
||||
in
|
||||
flake-utils.lib.eachDefaultSystem (system: flakeForSystem nixpkgs system);
|
||||
}
|
||||
# nix-direnv cache busting line: sha256-7L+dvS++UNfMVcPUCbK/xuBPwtrzW4RpZTtcl7VCwQs=
|
||||
# nix-direnv cache busting line: sha256-l2uIma2oEdSN0zVo9BOFJF2gC3S60vXwTLVadv8yQPo=
|
||||
|
||||
22
go.mod
22
go.mod
@@ -24,7 +24,7 @@ require (
|
||||
github.com/frankban/quicktest v1.14.5
|
||||
github.com/fxamacker/cbor/v2 v2.4.0
|
||||
github.com/go-json-experiment/json v0.0.0-20230321051131-ccbac49a6929
|
||||
github.com/go-logr/zapr v1.2.3
|
||||
github.com/go-logr/zapr v1.2.4
|
||||
github.com/go-ole/go-ole v1.2.6
|
||||
github.com/godbus/dbus/v5 v5.1.0
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
|
||||
@@ -76,13 +76,13 @@ require (
|
||||
golang.org/x/crypto v0.8.0
|
||||
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53
|
||||
golang.org/x/mod v0.10.0
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/net v0.10.0
|
||||
golang.org/x/oauth2 v0.7.0
|
||||
golang.org/x/sync v0.2.0
|
||||
golang.org/x/sys v0.8.0
|
||||
golang.org/x/term v0.7.0
|
||||
golang.org/x/term v0.8.0
|
||||
golang.org/x/time v0.3.0
|
||||
golang.org/x/tools v0.8.0
|
||||
golang.org/x/tools v0.9.1
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||
gvisor.dev/gvisor v0.0.0-20230504175454-7b0a1988a28f
|
||||
@@ -90,11 +90,11 @@ require (
|
||||
inet.af/peercred v0.0.0-20210906144145-0893ea02156a
|
||||
inet.af/tcpproxy v0.0.0-20221017015627-91f861402626
|
||||
inet.af/wf v0.0.0-20221017222439-36129f591884
|
||||
k8s.io/api v0.26.1
|
||||
k8s.io/apimachinery v0.26.1
|
||||
k8s.io/client-go v0.26.1
|
||||
k8s.io/api v0.27.2
|
||||
k8s.io/apimachinery v0.27.2
|
||||
k8s.io/client-go v0.27.2
|
||||
nhooyr.io/websocket v1.8.7
|
||||
sigs.k8s.io/controller-runtime v0.14.6
|
||||
sigs.k8s.io/controller-runtime v0.15.0
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.0
|
||||
)
|
||||
@@ -334,7 +334,7 @@ require (
|
||||
golang.org/x/exp/typeparams v0.0.0-20230425010034-47ecfdc1ba53 // indirect
|
||||
golang.org/x/image v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
@@ -343,8 +343,8 @@ require (
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.26.1 // indirect
|
||||
k8s.io/component-base v0.26.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.27.2 // indirect
|
||||
k8s.io/component-base v0.27.2 // indirect
|
||||
k8s.io/klog/v2 v2.100.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
|
||||
|
||||
@@ -1 +1 @@
|
||||
sha256-7L+dvS++UNfMVcPUCbK/xuBPwtrzW4RpZTtcl7VCwQs=
|
||||
sha256-l2uIma2oEdSN0zVo9BOFJF2gC3S60vXwTLVadv8yQPo=
|
||||
|
||||
57
go.sum
57
go.sum
@@ -274,7 +274,6 @@ github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStB
|
||||
github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0=
|
||||
github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw=
|
||||
github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY=
|
||||
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
|
||||
@@ -339,11 +338,10 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
|
||||
github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=
|
||||
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
|
||||
github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
@@ -362,6 +360,7 @@ github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
||||
github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8=
|
||||
github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU=
|
||||
@@ -514,6 +513,7 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/rpmpack v0.0.0-20201206194719-59e495f2b7e1/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk=
|
||||
github.com/google/rpmpack v0.0.0-20221120200012-98b63d62fd77 h1:+C0+foB1Bm0WYdbaDIuUGEVG1Eqx9WWcGUoJBSLdZo0=
|
||||
@@ -831,11 +831,11 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
|
||||
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
|
||||
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
@@ -1172,13 +1172,13 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
|
||||
@@ -1316,8 +1316,8 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1444,8 +1444,8 @@ golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1566,8 +1566,8 @@ golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
|
||||
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
|
||||
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1576,8 +1576,8 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc=
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -1709,7 +1709,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -1734,16 +1733,16 @@ inet.af/tcpproxy v0.0.0-20221017015627-91f861402626 h1:2dMP3Ox/Wh5BiItwOt4jxRsfz
|
||||
inet.af/tcpproxy v0.0.0-20221017015627-91f861402626/go.mod h1:Tojt5kmHpDIR2jMojxzZK2w2ZR7OILODmUo2gaSwjrk=
|
||||
inet.af/wf v0.0.0-20221017222439-36129f591884 h1:zg9snq3Cpy50lWuVqDYM7AIRVTtU50y5WXETMFohW/Q=
|
||||
inet.af/wf v0.0.0-20221017222439-36129f591884/go.mod h1:bSAQ38BYbY68uwpasXOTZo22dKGy9SNvI6PZFeKomZE=
|
||||
k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ=
|
||||
k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg=
|
||||
k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI=
|
||||
k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM=
|
||||
k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ=
|
||||
k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
|
||||
k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU=
|
||||
k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE=
|
||||
k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4=
|
||||
k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU=
|
||||
k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo=
|
||||
k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4=
|
||||
k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo=
|
||||
k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ=
|
||||
k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg=
|
||||
k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
|
||||
k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE=
|
||||
k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ=
|
||||
k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo=
|
||||
k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo=
|
||||
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
|
||||
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg=
|
||||
@@ -1767,8 +1766,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA=
|
||||
sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
|
||||
sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU=
|
||||
sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
|
||||
@@ -1 +1 @@
|
||||
ddff070c02790cb571006e820e58cce9627569cf
|
||||
480a0c381923c53e70ed5e72f9a9f79ce1884859
|
||||
|
||||
@@ -101,11 +101,13 @@ func (b *LocalBackend) GetCertPEM(ctx context.Context, domain string) (*TLSCertK
|
||||
}
|
||||
|
||||
if pair, err := getCertPEMCached(cs, domain, now); err == nil {
|
||||
future := now.AddDate(0, 0, 14)
|
||||
if b.shouldStartDomainRenewal(cs, domain, future) {
|
||||
shouldRenew, err := shouldStartDomainRenewal(domain, now, pair)
|
||||
if err != nil {
|
||||
logf("error checking for certificate renewal: %v", err)
|
||||
} else if shouldRenew {
|
||||
logf("starting async renewal")
|
||||
// Start renewal in the background.
|
||||
go b.getCertPEM(context.Background(), cs, logf, traceACME, domain, future)
|
||||
go b.getCertPEM(context.Background(), cs, logf, traceACME, domain, now)
|
||||
}
|
||||
return pair, nil
|
||||
}
|
||||
@@ -118,18 +120,41 @@ func (b *LocalBackend) GetCertPEM(ctx context.Context, domain string) (*TLSCertK
|
||||
return pair, nil
|
||||
}
|
||||
|
||||
func (b *LocalBackend) shouldStartDomainRenewal(cs certStore, domain string, future time.Time) bool {
|
||||
func shouldStartDomainRenewal(domain string, now time.Time, pair *TLSCertKeyPair) (bool, error) {
|
||||
renewMu.Lock()
|
||||
defer renewMu.Unlock()
|
||||
now := time.Now()
|
||||
if last, ok := lastRenewCheck[domain]; ok && now.Sub(last) < time.Minute {
|
||||
// We checked very recently. Don't bother reparsing &
|
||||
// validating the x509 cert.
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
lastRenewCheck[domain] = now
|
||||
_, err := getCertPEMCached(cs, domain, future)
|
||||
return errors.Is(err, errCertExpired)
|
||||
|
||||
block, _ := pem.Decode(pair.CertPEM)
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("parsing certificate PEM")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("parsing certificate: %w", err)
|
||||
}
|
||||
|
||||
certLifetime := cert.NotAfter.Sub(cert.NotBefore)
|
||||
if certLifetime < 0 {
|
||||
return false, fmt.Errorf("negative certificate lifetime %v", certLifetime)
|
||||
}
|
||||
|
||||
// Per https://github.com/tailscale/tailscale/issues/8204, check
|
||||
// whether we're more than 2/3 of the way through the certificate's
|
||||
// lifetime, which is the officially-recommended best practice by Let's
|
||||
// Encrypt.
|
||||
renewalDuration := certLifetime * 2 / 3
|
||||
renewAt := cert.NotBefore.Add(renewalDuration)
|
||||
|
||||
if now.After(renewAt) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// certStore provides a way to perist and retrieve TLS certificates.
|
||||
|
||||
@@ -6,12 +6,19 @@
|
||||
package ipnlocal
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"embed"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/exp/maps"
|
||||
"tailscale.com/ipn/store/mem"
|
||||
)
|
||||
|
||||
@@ -100,3 +107,94 @@ func TestCertStoreRoundTrip(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldStartDomainRenewal(t *testing.T) {
|
||||
reset := func() {
|
||||
renewMu.Lock()
|
||||
defer renewMu.Unlock()
|
||||
maps.Clear(lastRenewCheck)
|
||||
}
|
||||
|
||||
mustMakePair := func(template *x509.Certificate) *TLSCertKeyPair {
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
certPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: b,
|
||||
})
|
||||
|
||||
return &TLSCertKeyPair{
|
||||
Cached: false,
|
||||
CertPEM: certPEM,
|
||||
KeyPEM: []byte("unused"),
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Unix(1685714838, 0)
|
||||
subject := pkix.Name{
|
||||
Organization: []string{"Tailscale, Inc."},
|
||||
Country: []string{"CA"},
|
||||
Province: []string{"ON"},
|
||||
Locality: []string{"Toronto"},
|
||||
StreetAddress: []string{"290 Bremner Blvd"},
|
||||
PostalCode: []string{"M5V 3L9"},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
notBefore time.Time
|
||||
lifetime time.Duration
|
||||
want bool
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "should renew",
|
||||
notBefore: now.AddDate(0, 0, -89),
|
||||
lifetime: 90 * 24 * time.Hour,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "short-lived renewal",
|
||||
notBefore: now.AddDate(0, 0, -7),
|
||||
lifetime: 10 * 24 * time.Hour,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "no renew",
|
||||
notBefore: now.AddDate(0, 0, -59), // 59 days ago == not 2/3rds of the way through 90 days yet
|
||||
lifetime: 90 * 24 * time.Hour,
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reset()
|
||||
|
||||
ret, err := shouldStartDomainRenewal("example.com", now, mustMakePair(&x509.Certificate{
|
||||
SerialNumber: big.NewInt(2019),
|
||||
Subject: subject,
|
||||
NotBefore: tt.notBefore,
|
||||
NotAfter: tt.notBefore.Add(tt.lifetime),
|
||||
}))
|
||||
|
||||
if tt.wantErr != "" {
|
||||
if err == nil {
|
||||
t.Errorf("wanted error, got nil")
|
||||
} else if err.Error() != tt.wantErr {
|
||||
t.Errorf("got err=%q, want %q", err.Error(), tt.wantErr)
|
||||
}
|
||||
} else {
|
||||
if ret != tt.want {
|
||||
t.Errorf("got ret=%v, want %v", ret, tt.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,10 +292,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
||||
osshare.SetFileSharingEnabled(false, logf)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
portpoll, err := portlist.NewPoller()
|
||||
if err != nil {
|
||||
logf("skipping portlist: %s", err)
|
||||
}
|
||||
portpoll := new(portlist.Poller)
|
||||
|
||||
b := &LocalBackend{
|
||||
ctx: ctx,
|
||||
@@ -1377,7 +1374,6 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
||||
|
||||
if b.portpoll != nil {
|
||||
b.portpollOnce.Do(func() {
|
||||
go b.portpoll.Run(b.ctx)
|
||||
go b.readPoller()
|
||||
|
||||
// Give the poller a second to get results to
|
||||
@@ -1812,11 +1808,30 @@ func dnsMapsEqual(new, old *netmap.NetworkMap) bool {
|
||||
// readPoller is a goroutine that receives service lists from
|
||||
// b.portpoll and propagates them into the controlclient's HostInfo.
|
||||
func (b *LocalBackend) readPoller() {
|
||||
n := 0
|
||||
isFirst := true
|
||||
ticker := time.NewTicker(portlist.PollInterval())
|
||||
defer ticker.Stop()
|
||||
initChan := make(chan struct{})
|
||||
close(initChan)
|
||||
for {
|
||||
ports, ok := <-b.portpoll.Updates()
|
||||
if !ok {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
case <-b.ctx.Done():
|
||||
return
|
||||
case <-initChan:
|
||||
// Preserving old behavior: readPoller should
|
||||
// immediately poll the first time, then wait
|
||||
// for a tick after.
|
||||
initChan = nil
|
||||
}
|
||||
|
||||
ports, changed, err := b.portpoll.Poll()
|
||||
if err != nil {
|
||||
b.logf("error polling for open ports: %v", err)
|
||||
return
|
||||
}
|
||||
if !changed {
|
||||
continue
|
||||
}
|
||||
sl := []tailcfg.Service{}
|
||||
for _, p := range ports {
|
||||
@@ -1840,8 +1855,8 @@ func (b *LocalBackend) readPoller() {
|
||||
|
||||
b.doSetHostinfoFilterServices(hi)
|
||||
|
||||
n++
|
||||
if n == 1 {
|
||||
if isFirst {
|
||||
isFirst = false
|
||||
close(b.gotPortPollRes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,6 +449,8 @@ func (b *LocalBackend) NetworkLockStatus() *ipnstate.NetworkLockStatus {
|
||||
filtered[i] = b.tka.filtered[i].Clone()
|
||||
}
|
||||
|
||||
stateID1, _ := b.tka.authority.StateIDs()
|
||||
|
||||
return &ipnstate.NetworkLockStatus{
|
||||
Enabled: true,
|
||||
Head: &head,
|
||||
@@ -457,6 +459,7 @@ func (b *LocalBackend) NetworkLockStatus() *ipnstate.NetworkLockStatus {
|
||||
NodeKeySigned: selfAuthorized,
|
||||
TrustedKeys: outKeys,
|
||||
FilteredPeers: filtered,
|
||||
StateID: stateID1,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ import (
|
||||
|
||||
var errAlreadyMigrated = errors.New("profile migration already completed")
|
||||
|
||||
var debug = envknob.RegisterBool("TS_DEBUG_PROFILES")
|
||||
|
||||
// profileManager is a wrapper around a StateStore that manages
|
||||
// multiple profiles and the current profile.
|
||||
type profileManager struct {
|
||||
@@ -42,6 +44,13 @@ type profileManager struct {
|
||||
isNewProfile bool
|
||||
}
|
||||
|
||||
func (pm *profileManager) dlogf(format string, args ...any) {
|
||||
if !debug() {
|
||||
return
|
||||
}
|
||||
pm.logf(format, args...)
|
||||
}
|
||||
|
||||
// CurrentUserID returns the current user ID. It is only non-empty on
|
||||
// Windows where we have a multi-user system.
|
||||
func (pm *profileManager) CurrentUserID() ipn.WindowsUserID {
|
||||
@@ -66,8 +75,10 @@ func (pm *profileManager) SetCurrentUserID(uid ipn.WindowsUserID) error {
|
||||
// Read the CurrentProfileKey from the store which stores
|
||||
// the selected profile for the current user.
|
||||
b, err := pm.store.ReadState(ipn.CurrentProfileKey(string(uid)))
|
||||
pm.dlogf("SetCurrentUserID: ReadState(%q) = %v, %v", string(uid), len(b), err)
|
||||
if err == ipn.ErrStateNotExist || len(b) == 0 {
|
||||
if runtime.GOOS == "windows" {
|
||||
pm.dlogf("SetCurrentUserID: windows: migrating from legacy preferences")
|
||||
if err := pm.migrateFromLegacyPrefs(); err != nil && !errors.Is(err, errAlreadyMigrated) {
|
||||
return err
|
||||
}
|
||||
@@ -81,6 +92,7 @@ func (pm *profileManager) SetCurrentUserID(uid ipn.WindowsUserID) error {
|
||||
pk := ipn.StateKey(string(b))
|
||||
prof := pm.findProfileByKey(pk)
|
||||
if prof == nil {
|
||||
pm.dlogf("SetCurrentUserID: no profile found for key: %q", pk)
|
||||
pm.NewProfile()
|
||||
return nil
|
||||
}
|
||||
@@ -555,6 +567,7 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, goos stri
|
||||
// and runtime must be valid Windows security identifier structures.
|
||||
} else if len(knownProfiles) == 0 && goos != "windows" && runtime.GOOS != "windows" {
|
||||
// No known profiles, try a migration.
|
||||
pm.dlogf("no known profiles; trying to migrate from legacy prefs")
|
||||
if err := pm.migrateFromLegacyPrefs(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -573,11 +586,13 @@ func (pm *profileManager) migrateFromLegacyPrefs() error {
|
||||
metricMigrationError.Add(1)
|
||||
return fmt.Errorf("load legacy prefs: %w", err)
|
||||
}
|
||||
pm.dlogf("loaded legacy preferences; sentinel=%q", sentinel)
|
||||
if err := pm.SetPrefs(prefs); err != nil {
|
||||
metricMigrationError.Add(1)
|
||||
return fmt.Errorf("migrating _daemon profile: %w", err)
|
||||
}
|
||||
pm.completeMigration(sentinel)
|
||||
pm.dlogf("completed legacy preferences migration with sentinel=%q", sentinel)
|
||||
metricMigrationSuccess.Add(1)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ func legacyPrefsDir(uid ipn.WindowsUserID) (string, error) {
|
||||
func (pm *profileManager) loadLegacyPrefs() (string, ipn.PrefsView, error) {
|
||||
userLegacyPrefsDir, err := legacyPrefsDir(pm.currentUserID)
|
||||
if err != nil {
|
||||
pm.dlogf("no legacy preferences directory for %q: %v", pm.currentUserID, err)
|
||||
return "", ipn.PrefsView{}, err
|
||||
}
|
||||
|
||||
@@ -47,14 +48,17 @@ func (pm *profileManager) loadLegacyPrefs() (string, ipn.PrefsView, error) {
|
||||
// verify that migration sentinel is not present
|
||||
_, err = os.Stat(migrationSentinel)
|
||||
if err == nil {
|
||||
pm.dlogf("migration sentinel %q already exists", migrationSentinel)
|
||||
return "", ipn.PrefsView{}, errAlreadyMigrated
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
pm.dlogf("os.Stat(%q) = %v", migrationSentinel, err)
|
||||
return "", ipn.PrefsView{}, err
|
||||
}
|
||||
|
||||
prefsPath := filepath.Join(userLegacyPrefsDir, legacyPrefsFile+legacyPrefsExt)
|
||||
prefs, err := ipn.LoadPrefs(prefsPath)
|
||||
pm.dlogf("ipn.LoadPrefs(%q) = %v, %v", prefsPath, prefs, err)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return "", ipn.PrefsView{}, errAlreadyMigrated
|
||||
}
|
||||
|
||||
@@ -447,6 +447,8 @@ func (b *LocalBackend) proxyHandlerForBackend(backend string) (*httputil.Reverse
|
||||
Rewrite: func(r *httputil.ProxyRequest) {
|
||||
r.SetURL(u)
|
||||
r.Out.Host = r.In.Host
|
||||
r.Out.Header.Set("X-Forwarded-Host", r.In.Host)
|
||||
r.Out.Header.Set("X-Forwarded-Proto", "https")
|
||||
if c, ok := r.Out.Context().Value(serveHTTPContextKey{}).(*serveHTTPContext); ok {
|
||||
r.Out.Header.Set("X-Forwarded-For", c.SrcAddr.Addr().String())
|
||||
}
|
||||
|
||||
@@ -121,6 +121,11 @@ type NetworkLockStatus struct {
|
||||
// (i.e. no connectivity) because they failed tailnet lock
|
||||
// checks.
|
||||
FilteredPeers []*TKAFilteredPeer
|
||||
|
||||
// StateID is a nonce associated with the network lock authority,
|
||||
// generated upon enablement. This field is not populated if the
|
||||
// network lock is disabled.
|
||||
StateID uint64
|
||||
}
|
||||
|
||||
// NetworkLockUpdate describes a change to network-lock state.
|
||||
@@ -583,6 +588,8 @@ func osEmoji(os string) string {
|
||||
return "🖥️"
|
||||
case "iOS":
|
||||
return "📱"
|
||||
case "tvOS":
|
||||
return "🍎📺"
|
||||
case "android":
|
||||
return "🤖"
|
||||
case "freebsd":
|
||||
|
||||
@@ -930,8 +930,8 @@ func InUseOtherUserIPNStream(w http.ResponseWriter, r *http.Request, err error)
|
||||
}
|
||||
|
||||
func (h *Handler) serveWatchIPNBus(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.PermitWrite {
|
||||
http.Error(w, "denied", http.StatusForbidden)
|
||||
if !h.PermitRead {
|
||||
http.Error(w, "watch ipn bus access denied", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
f, ok := w.(http.Flusher)
|
||||
|
||||
@@ -60,10 +60,10 @@ and [iOS][]. See also the dependencies in the [Tailscale CLI][].
|
||||
- [go4.org/netipx](https://pkg.go.dev/go4.org/netipx) ([BSD-3-Clause](https://github.com/go4org/netipx/blob/f1b76eb4bb35/LICENSE))
|
||||
- [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto) ([BSD-3-Clause](https://cs.opensource.google/go/x/crypto/+/v0.8.0:LICENSE))
|
||||
- [golang.org/x/exp](https://pkg.go.dev/golang.org/x/exp) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/47ecfdc1:LICENSE))
|
||||
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.9.0:LICENSE))
|
||||
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.10.0:LICENSE))
|
||||
- [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.2.0:LICENSE))
|
||||
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.8.0:LICENSE))
|
||||
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.7.0:LICENSE))
|
||||
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.8.0:LICENSE))
|
||||
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.9.0:LICENSE))
|
||||
- [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/v0.3.0:LICENSE))
|
||||
- [gvisor.dev/gvisor/pkg](https://pkg.go.dev/gvisor.dev/gvisor/pkg) ([Apache-2.0](https://github.com/google/gvisor/blob/7b0a1988a28f/LICENSE))
|
||||
|
||||
@@ -81,11 +81,11 @@ Some packages may only be included on certain architectures or operating systems
|
||||
- [go4.org/netipx](https://pkg.go.dev/go4.org/netipx) ([BSD-3-Clause](https://github.com/go4org/netipx/blob/f1b76eb4bb35/LICENSE))
|
||||
- [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto) ([BSD-3-Clause](https://cs.opensource.google/go/x/crypto/+/v0.8.0:LICENSE))
|
||||
- [golang.org/x/exp](https://pkg.go.dev/golang.org/x/exp) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/47ecfdc1:LICENSE))
|
||||
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.9.0:LICENSE))
|
||||
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.10.0:LICENSE))
|
||||
- [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.7.0:LICENSE))
|
||||
- [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.2.0:LICENSE))
|
||||
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.8.0:LICENSE))
|
||||
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.7.0:LICENSE))
|
||||
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.8.0:LICENSE))
|
||||
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.9.0:LICENSE))
|
||||
- [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/v0.3.0:LICENSE))
|
||||
- [golang.zx2c4.com/wintun](https://pkg.go.dev/golang.zx2c4.com/wintun) ([MIT](https://git.zx2c4.com/wintun-go/tree/LICENSE?id=0fa3db229ce2))
|
||||
@@ -94,7 +94,7 @@ Some packages may only be included on certain architectures or operating systems
|
||||
- [gvisor.dev/gvisor/pkg](https://pkg.go.dev/gvisor.dev/gvisor/pkg) ([Apache-2.0](https://github.com/google/gvisor/blob/7b0a1988a28f/LICENSE))
|
||||
- [inet.af/peercred](https://pkg.go.dev/inet.af/peercred) ([BSD-3-Clause](https://github.com/inetaf/peercred/blob/0893ea02156a/LICENSE))
|
||||
- [inet.af/wf](https://pkg.go.dev/inet.af/wf) ([BSD-3-Clause](https://github.com/inetaf/wf/blob/36129f591884/LICENSE))
|
||||
- [k8s.io/client-go/util/homedir](https://pkg.go.dev/k8s.io/client-go/util/homedir) ([Apache-2.0](https://github.com/kubernetes/client-go/blob/v0.26.1/LICENSE))
|
||||
- [k8s.io/client-go/util/homedir](https://pkg.go.dev/k8s.io/client-go/util/homedir) ([Apache-2.0](https://github.com/kubernetes/client-go/blob/v0.27.2/LICENSE))
|
||||
- [nhooyr.io/websocket](https://pkg.go.dev/nhooyr.io/websocket) ([MIT](https://github.com/nhooyr/websocket/blob/v1.8.7/LICENSE.txt))
|
||||
- [sigs.k8s.io/yaml](https://pkg.go.dev/sigs.k8s.io/yaml) ([MIT](https://github.com/kubernetes-sigs/yaml/blob/v1.3.0/LICENSE))
|
||||
- [software.sslmate.com/src/go-pkcs12](https://pkg.go.dev/software.sslmate.com/src/go-pkcs12) ([BSD-3-Clause](https://github.com/SSLMate/go-pkcs12/blob/v0.2.0/LICENSE))
|
||||
|
||||
@@ -42,10 +42,10 @@ Windows][]. See also the dependencies in the [Tailscale CLI][].
|
||||
- [golang.org/x/exp](https://pkg.go.dev/golang.org/x/exp) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/47ecfdc1:LICENSE))
|
||||
- [golang.org/x/image/bmp](https://pkg.go.dev/golang.org/x/image/bmp) ([BSD-3-Clause](https://cs.opensource.google/go/x/image/+/v0.7.0:LICENSE))
|
||||
- [golang.org/x/mod](https://pkg.go.dev/golang.org/x/mod) ([BSD-3-Clause](https://cs.opensource.google/go/x/mod/+/v0.10.0:LICENSE))
|
||||
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.9.0:LICENSE))
|
||||
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.10.0:LICENSE))
|
||||
- [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.2.0:LICENSE))
|
||||
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.8.0:LICENSE))
|
||||
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.7.0:LICENSE))
|
||||
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.8.0:LICENSE))
|
||||
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.9.0:LICENSE))
|
||||
- [golang.zx2c4.com/wintun](https://pkg.go.dev/golang.zx2c4.com/wintun) ([MIT](https://git.zx2c4.com/wintun-go/tree/LICENSE?id=0fa3db229ce2))
|
||||
- [golang.zx2c4.com/wireguard/windows/tunnel/winipcfg](https://pkg.go.dev/golang.zx2c4.com/wireguard/windows/tunnel/winipcfg) ([MIT](https://git.zx2c4.com/wireguard-windows/tree/COPYING?h=v0.5.3))
|
||||
|
||||
@@ -143,10 +143,6 @@ func (r *Resolver) cloudHostResolver() (v *net.Resolver, ok bool) {
|
||||
switch runtime.GOOS {
|
||||
case "android", "ios", "darwin":
|
||||
return nil, false
|
||||
case "windows":
|
||||
// TODO(bradfitz): remove this restriction once we're using Go 1.19
|
||||
// which supports net.Resolver.PreferGo on Windows.
|
||||
return nil, false
|
||||
}
|
||||
ip := cloudenv.Get().ResolverIP()
|
||||
if ip == "" {
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
package portlist
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -17,9 +17,17 @@ import (
|
||||
"tailscale.com/envknob"
|
||||
)
|
||||
|
||||
var pollInterval = 5 * time.Second // default; changed by some OS-specific init funcs
|
||||
var (
|
||||
newOSImpl func(includeLocalhost bool) osImpl // if non-nil, constructs a new osImpl.
|
||||
pollInterval = 5 * time.Second // default; changed by some OS-specific init funcs
|
||||
debugDisablePortlist = envknob.RegisterBool("TS_DEBUG_DISABLE_PORTLIST")
|
||||
)
|
||||
|
||||
var debugDisablePortlist = envknob.RegisterBool("TS_DEBUG_DISABLE_PORTLIST")
|
||||
// PollInterval is the recommended OS-specific interval
|
||||
// to wait between *Poller.Poll method calls.
|
||||
func PollInterval() time.Duration {
|
||||
return pollInterval
|
||||
}
|
||||
|
||||
// Poller scans the systems for listening ports periodically and sends
|
||||
// the results to C.
|
||||
@@ -29,22 +37,15 @@ type Poller struct {
|
||||
// This field should only be changed before calling Run.
|
||||
IncludeLocalhost bool
|
||||
|
||||
c chan List // unbuffered
|
||||
|
||||
// os, if non-nil, is an OS-specific implementation of the portlist getting
|
||||
// code. When non-nil, it's responsible for getting the complete list of
|
||||
// cached ports complete with the process name. That is, when set,
|
||||
// addProcesses is not used.
|
||||
// A nil values means we don't have code for getting the list on the current
|
||||
// operating system.
|
||||
os osImpl
|
||||
osOnce sync.Once // guards init of os
|
||||
|
||||
// closeCtx is the context that's canceled on Close.
|
||||
closeCtx context.Context
|
||||
closeCtxCancel context.CancelFunc
|
||||
|
||||
runDone chan struct{} // closed when Run completes
|
||||
os osImpl
|
||||
initOnce sync.Once // guards init of os
|
||||
initErr error
|
||||
|
||||
// scatch is memory for Poller.getList to reuse between calls.
|
||||
scratch []Port
|
||||
@@ -66,123 +67,55 @@ type osImpl interface {
|
||||
AppendListeningPorts(base []Port) ([]Port, error)
|
||||
}
|
||||
|
||||
// newOSImpl, if non-nil, constructs a new osImpl.
|
||||
var newOSImpl func(includeLocalhost bool) osImpl
|
||||
|
||||
var errUnimplemented = errors.New("portlist poller not implemented on " + runtime.GOOS)
|
||||
|
||||
// NewPoller returns a new portlist Poller. It returns an error
|
||||
// if the portlist couldn't be obtained.
|
||||
func NewPoller() (*Poller, error) {
|
||||
if debugDisablePortlist() {
|
||||
return nil, errors.New("portlist disabled by envknob")
|
||||
}
|
||||
p := &Poller{
|
||||
c: make(chan List),
|
||||
runDone: make(chan struct{}),
|
||||
}
|
||||
p.closeCtx, p.closeCtxCancel = context.WithCancel(context.Background())
|
||||
p.osOnce.Do(p.initOSField)
|
||||
if p.os == nil {
|
||||
return nil, errUnimplemented
|
||||
}
|
||||
|
||||
// Do one initial poll synchronously so we can return an error
|
||||
// early.
|
||||
if pl, err := p.getList(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
p.setPrev(pl)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *Poller) setPrev(pl List) {
|
||||
// Make a copy, as the pass in pl slice aliases pl.scratch and we don't want
|
||||
// that to except to the caller.
|
||||
p.prev = slices.Clone(pl)
|
||||
}
|
||||
|
||||
func (p *Poller) initOSField() {
|
||||
if newOSImpl != nil {
|
||||
// init initializes the Poller by ensuring it has an underlying
|
||||
// OS implementation and is not turned off by envknob.
|
||||
func (p *Poller) init() {
|
||||
switch {
|
||||
case debugDisablePortlist():
|
||||
p.initErr = errors.New("portlist disabled by envknob")
|
||||
case newOSImpl == nil:
|
||||
p.initErr = errors.New("portlist poller not implemented on " + runtime.GOOS)
|
||||
default:
|
||||
p.os = newOSImpl(p.IncludeLocalhost)
|
||||
}
|
||||
}
|
||||
|
||||
// Updates return the channel that receives port list updates.
|
||||
//
|
||||
// The channel is closed when the Poller is closed.
|
||||
func (p *Poller) Updates() <-chan List { return p.c }
|
||||
|
||||
// Close closes the Poller.
|
||||
// Run will return with a nil error.
|
||||
func (p *Poller) Close() error {
|
||||
p.closeCtxCancel()
|
||||
<-p.runDone
|
||||
if p.os != nil {
|
||||
p.os.Close()
|
||||
if p.initErr != nil {
|
||||
return p.initErr
|
||||
}
|
||||
return nil
|
||||
if p.os == nil {
|
||||
return nil
|
||||
}
|
||||
return p.os.Close()
|
||||
}
|
||||
|
||||
// send sends pl to p.c and returns whether it was successfully sent.
|
||||
func (p *Poller) send(ctx context.Context, pl List) (sent bool, err error) {
|
||||
select {
|
||||
case p.c <- pl:
|
||||
return true, nil
|
||||
case <-ctx.Done():
|
||||
return false, ctx.Err()
|
||||
case <-p.closeCtx.Done():
|
||||
return false, nil
|
||||
// Poll returns the list of listening ports, if changed from
|
||||
// a previous call as indicated by the changed result.
|
||||
func (p *Poller) Poll() (ports []Port, changed bool, err error) {
|
||||
p.initOnce.Do(p.init)
|
||||
if p.initErr != nil {
|
||||
return nil, false, fmt.Errorf("error initializing poller: %w", p.initErr)
|
||||
}
|
||||
}
|
||||
|
||||
// Run runs the Poller periodically until either the context
|
||||
// is done, or the Close is called.
|
||||
//
|
||||
// Run may only be called once.
|
||||
func (p *Poller) Run(ctx context.Context) error {
|
||||
tick := time.NewTicker(pollInterval)
|
||||
defer tick.Stop()
|
||||
return p.runWithTickChan(ctx, tick.C)
|
||||
}
|
||||
|
||||
func (p *Poller) runWithTickChan(ctx context.Context, tickChan <-chan time.Time) error {
|
||||
defer close(p.runDone)
|
||||
defer close(p.c)
|
||||
|
||||
// Send out the pre-generated initial value.
|
||||
if sent, err := p.send(ctx, p.prev); !sent {
|
||||
return err
|
||||
pl, err := p.getList()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tickChan:
|
||||
pl, err := p.getList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pl.equal(p.prev) {
|
||||
continue
|
||||
}
|
||||
p.setPrev(pl)
|
||||
if sent, err := p.send(ctx, p.prev); !sent {
|
||||
return err
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-p.closeCtx.Done():
|
||||
return nil
|
||||
}
|
||||
if pl.equal(p.prev) {
|
||||
return nil, false, nil
|
||||
}
|
||||
p.setPrev(pl)
|
||||
return p.prev, true, nil
|
||||
}
|
||||
|
||||
func (p *Poller) getList() (List, error) {
|
||||
if debugDisablePortlist() {
|
||||
return nil, nil
|
||||
}
|
||||
p.osOnce.Do(p.initOSField)
|
||||
var err error
|
||||
p.scratch, err = p.os.AppendListeningPorts(p.scratch[:0])
|
||||
return p.scratch, err
|
||||
|
||||
@@ -4,11 +4,8 @@
|
||||
package portlist
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"tailscale.com/tstest"
|
||||
)
|
||||
@@ -17,14 +14,14 @@ func TestGetList(t *testing.T) {
|
||||
tstest.ResourceCheck(t)
|
||||
|
||||
var p Poller
|
||||
pl, err := p.getList()
|
||||
pl, _, err := p.Poll()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i, p := range pl {
|
||||
t.Logf("[%d] %+v", i, p)
|
||||
}
|
||||
t.Logf("As String: %v", pl.String())
|
||||
t.Logf("As String: %s", List(pl))
|
||||
}
|
||||
|
||||
func TestIgnoreLocallyBoundPorts(t *testing.T) {
|
||||
@@ -38,7 +35,7 @@ func TestIgnoreLocallyBoundPorts(t *testing.T) {
|
||||
ta := ln.Addr().(*net.TCPAddr)
|
||||
port := ta.Port
|
||||
var p Poller
|
||||
pl, err := p.getList()
|
||||
pl, _, err := p.Poll()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -49,16 +46,16 @@ func TestIgnoreLocallyBoundPorts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangesOverTime(t *testing.T) {
|
||||
func TestPoller(t *testing.T) {
|
||||
var p Poller
|
||||
p.IncludeLocalhost = true
|
||||
get := func(t *testing.T) []Port {
|
||||
t.Helper()
|
||||
s, err := p.getList()
|
||||
s, _, err := p.Poll()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return append([]Port(nil), s...)
|
||||
return s
|
||||
}
|
||||
|
||||
p1 := get(t)
|
||||
@@ -175,68 +172,21 @@ func TestEqualLessThan(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoller(t *testing.T) {
|
||||
p, err := NewPoller()
|
||||
func TestClose(t *testing.T) {
|
||||
var p Poller
|
||||
err := p.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p = Poller{}
|
||||
_, _, err = p.Poll()
|
||||
if err != nil {
|
||||
t.Skipf("skipping due to poll error: %v", err)
|
||||
}
|
||||
err = p.Close()
|
||||
if err != nil {
|
||||
t.Skipf("not running test: %v", err)
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
gotUpdate := make(chan bool, 16)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for pl := range p.Updates() {
|
||||
// Look at all the pl slice memory to maximize
|
||||
// chance of race detector seeing violations.
|
||||
for _, v := range pl {
|
||||
if v == (Port{}) {
|
||||
// Force use
|
||||
panic("empty port")
|
||||
}
|
||||
}
|
||||
select {
|
||||
case gotUpdate <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
tick := make(chan time.Time, 16)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := p.runWithTickChan(context.Background(), tick); err != nil {
|
||||
t.Error("runWithTickChan:", err)
|
||||
}
|
||||
}()
|
||||
for i := 0; i < 10; i++ {
|
||||
ln, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
tick <- time.Time{}
|
||||
|
||||
select {
|
||||
case <-gotUpdate:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timed out waiting for update")
|
||||
}
|
||||
}
|
||||
|
||||
// And a bunch of ticks without waiting for updates,
|
||||
// to make race tests more likely to fail, if any present.
|
||||
for i := 0; i < 10; i++ {
|
||||
tick <- time.Time{}
|
||||
}
|
||||
|
||||
if err := p.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkGetList(b *testing.B) {
|
||||
@@ -250,6 +200,11 @@ func BenchmarkGetListIncremental(b *testing.B) {
|
||||
func benchmarkGetList(b *testing.B, incremental bool) {
|
||||
b.ReportAllocs()
|
||||
var p Poller
|
||||
p.init()
|
||||
if p.initErr != nil {
|
||||
b.Skip(p.initErr)
|
||||
}
|
||||
b.Cleanup(func() { p.Close() })
|
||||
for i := 0; i < b.N; i++ {
|
||||
pl, err := p.getList()
|
||||
if err != nil {
|
||||
|
||||
7
release/dist/cli/cli.go
vendored
7
release/dist/cli/cli.go
vendored
@@ -124,10 +124,11 @@ func runBuild(ctx context.Context, filters []string, targets []dist.Target) erro
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting absolute path of manifest: %w", err)
|
||||
}
|
||||
fmt.Println(manifest)
|
||||
fmt.Println(filepath.Join(b.Out, out[0]))
|
||||
for i := range out {
|
||||
rel, err := filepath.Rel(filepath.Dir(manifest), filepath.Join(b.Out, out[i]))
|
||||
if !filepath.IsAbs(out[i]) {
|
||||
out[i] = filepath.Join(b.Out, out[i])
|
||||
}
|
||||
rel, err := filepath.Rel(filepath.Dir(manifest), out[i])
|
||||
if err != nil {
|
||||
return fmt.Errorf("making path relative: %w", err)
|
||||
}
|
||||
|
||||
7
release/dist/dist.go
vendored
7
release/dist/dist.go
vendored
@@ -17,6 +17,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"tailscale.com/util/multierr"
|
||||
"tailscale.com/version/mkversion"
|
||||
@@ -44,6 +45,8 @@ type Build struct {
|
||||
Go string
|
||||
// Version is the version info of the build.
|
||||
Version mkversion.VersionInfo
|
||||
// Time is the timestamp of the build.
|
||||
Time time.Time
|
||||
|
||||
// once is a cache of function invocations that should run once per process
|
||||
// (for example building a helper docker container)
|
||||
@@ -86,6 +89,7 @@ func NewBuild(repo, out string) (*Build, error) {
|
||||
Out: out,
|
||||
Go: goTool,
|
||||
Version: mkversion.Info(),
|
||||
Time: time.Now().UTC(),
|
||||
extra: map[any]any{},
|
||||
goBuildLimit: make(chan struct{}, runtime.NumCPU()),
|
||||
}
|
||||
@@ -114,6 +118,9 @@ func (b *Build) Build(targets []Target) (files []string, err error) {
|
||||
go func(i int, t Target) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%s: %w", t, err)
|
||||
}
|
||||
errs[i] = err
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
BIN
release/dist/synology/files/PACKAGE_ICON.PNG
vendored
Normal file
BIN
release/dist/synology/files/PACKAGE_ICON.PNG
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
BIN
release/dist/synology/files/PACKAGE_ICON_256.PNG
vendored
Normal file
BIN
release/dist/synology/files/PACKAGE_ICON_256.PNG
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
6
release/dist/synology/files/Tailscale.sc
vendored
Normal file
6
release/dist/synology/files/Tailscale.sc
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[Tailscale]
|
||||
title="Tailscale"
|
||||
desc="Tailscale VPN"
|
||||
port_forward="no"
|
||||
src.ports="41641/udp"
|
||||
dst.ports="41641/udp"
|
||||
12
release/dist/synology/files/config
vendored
Normal file
12
release/dist/synology/files/config
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
".url": {
|
||||
"SYNO.SDS.Tailscale": {
|
||||
"type": "url",
|
||||
"version": "1.8.3",
|
||||
"title": "Tailscale",
|
||||
"icon": "PACKAGE_ICON_256.PNG",
|
||||
"url": "webman/3rdparty/Tailscale/",
|
||||
"urlTarget": "_syno_tailscale"
|
||||
}
|
||||
}
|
||||
}
|
||||
2
release/dist/synology/files/index.cgi
vendored
Executable file
2
release/dist/synology/files/index.cgi
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
#! /bin/sh
|
||||
exec /var/packages/Tailscale/target/bin/tailscale web -cgi
|
||||
8
release/dist/synology/files/logrotate-dsm6
vendored
Normal file
8
release/dist/synology/files/logrotate-dsm6
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/var/packages/Tailscale/etc/tailscaled.stdout.log {
|
||||
size 10M
|
||||
rotate 3
|
||||
missingok
|
||||
copytruncate
|
||||
compress
|
||||
notifempty
|
||||
}
|
||||
8
release/dist/synology/files/logrotate-dsm7
vendored
Normal file
8
release/dist/synology/files/logrotate-dsm7
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/var/packages/Tailscale/var/tailscaled.stdout.log {
|
||||
size 10M
|
||||
rotate 3
|
||||
missingok
|
||||
copytruncate
|
||||
compress
|
||||
notifempty
|
||||
}
|
||||
7
release/dist/synology/files/privilege-dsm6
vendored
Normal file
7
release/dist/synology/files/privilege-dsm6
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"defaults":{
|
||||
"run-as": "root"
|
||||
},
|
||||
"username": "tailscale",
|
||||
"groupname": "tailscale"
|
||||
}
|
||||
7
release/dist/synology/files/privilege-dsm7
vendored
Normal file
7
release/dist/synology/files/privilege-dsm7
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"defaults":{
|
||||
"run-as": "package"
|
||||
},
|
||||
"username": "tailscale",
|
||||
"groupname": "tailscale"
|
||||
}
|
||||
13
release/dist/synology/files/privilege-dsm7.for-package-center
vendored
Normal file
13
release/dist/synology/files/privilege-dsm7.for-package-center
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"defaults":{
|
||||
"run-as": "package"
|
||||
},
|
||||
"username": "tailscale",
|
||||
"groupname": "tailscale",
|
||||
"tool": [{
|
||||
"relpath": "bin/tailscaled",
|
||||
"user": "package",
|
||||
"group": "package",
|
||||
"capabilities": "cap_net_admin,cap_chown,cap_net_raw"
|
||||
}]
|
||||
}
|
||||
11
release/dist/synology/files/resource
vendored
Normal file
11
release/dist/synology/files/resource
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"port-config": {
|
||||
"protocol-file": "conf/Tailscale.sc"
|
||||
},
|
||||
"usr-local-linker": {
|
||||
"bin": ["bin/tailscale"]
|
||||
},
|
||||
"syslog-config": {
|
||||
"logrotate-relpath": "conf/logrotate.conf"
|
||||
}
|
||||
}
|
||||
3
release/dist/synology/files/scripts/postupgrade
vendored
Normal file
3
release/dist/synology/files/scripts/postupgrade
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exit 0
|
||||
3
release/dist/synology/files/scripts/preupgrade
vendored
Normal file
3
release/dist/synology/files/scripts/preupgrade
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exit 0
|
||||
129
release/dist/synology/files/scripts/start-stop-status
vendored
Executable file
129
release/dist/synology/files/scripts/start-stop-status
vendored
Executable file
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
|
||||
SERVICE_NAME="tailscale"
|
||||
|
||||
if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "6" ]; then
|
||||
PKGVAR="/var/packages/Tailscale/etc"
|
||||
else
|
||||
PKGVAR="${SYNOPKG_PKGVAR}"
|
||||
fi
|
||||
|
||||
PID_FILE="${PKGVAR}/tailscaled.pid"
|
||||
LOG_FILE="${PKGVAR}/tailscaled.stdout.log"
|
||||
STATE_FILE="${PKGVAR}/tailscaled.state"
|
||||
SOCKET_FILE="${PKGVAR}/tailscaled.sock"
|
||||
PORT="41641"
|
||||
|
||||
SERVICE_COMMAND="${SYNOPKG_PKGDEST}/bin/tailscaled \
|
||||
--state=${STATE_FILE} \
|
||||
--socket=${SOCKET_FILE} \
|
||||
--port=$PORT"
|
||||
|
||||
if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "7" -a ! -e "/dev/net/tun" ]; then
|
||||
# TODO(maisem/crawshaw): Disable the tun device in DSM7 for now.
|
||||
SERVICE_COMMAND="${SERVICE_COMMAND} --tun=userspace-networking"
|
||||
fi
|
||||
|
||||
if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "6" ]; then
|
||||
chown -R tailscale:tailscale "${PKGVAR}/"
|
||||
fi
|
||||
|
||||
start_daemon() {
|
||||
local ts=$(date --iso-8601=second)
|
||||
echo "${ts} Starting ${SERVICE_NAME} with: ${SERVICE_COMMAND}" >${LOG_FILE}
|
||||
STATE_DIRECTORY=${PKGVAR} ${SERVICE_COMMAND} 2>&1 | sed -u '1,200p;201s,.*,[further tailscaled logs suppressed],p;d' >>${LOG_FILE} &
|
||||
# We pipe tailscaled's output to sed, so "$!" retrieves the PID of sed not tailscaled.
|
||||
# Use jobs -p to retrieve the PID of the most recent process group leader.
|
||||
jobs -p >"${PID_FILE}"
|
||||
}
|
||||
|
||||
stop_daemon() {
|
||||
if [ -r "${PID_FILE}" ]; then
|
||||
local PID=$(cat "${PID_FILE}")
|
||||
local ts=$(date --iso-8601=second)
|
||||
echo "${ts} Stopping ${SERVICE_NAME} service PID=${PID}" >>${LOG_FILE}
|
||||
kill -TERM $PID >>${LOG_FILE} 2>&1
|
||||
wait_for_status 1 || kill -KILL $PID >>${LOG_FILE} 2>&1
|
||||
rm -f "${PID_FILE}" >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
daemon_status() {
|
||||
if [ -r "${PID_FILE}" ]; then
|
||||
local PID=$(cat "${PID_FILE}")
|
||||
if ps -o pid -p ${PID} > /dev/null; then
|
||||
return
|
||||
fi
|
||||
rm -f "${PID_FILE}" >/dev/null
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_status() {
|
||||
# 20 tries
|
||||
# sleeps for 1 second after each try
|
||||
local counter=20
|
||||
while [ ${counter} -gt 0 ]; do
|
||||
daemon_status
|
||||
[ $? -eq $1 ] && return
|
||||
counter=$((counter - 1))
|
||||
sleep 1
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
ensure_tun_created() {
|
||||
if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "7" ]; then
|
||||
# TODO(maisem/crawshaw): Disable the tun device in DSM7 for now.
|
||||
return
|
||||
fi
|
||||
# Create the necessary file structure for /dev/net/tun
|
||||
if ([ ! -c /dev/net/tun ]); then
|
||||
if ([ ! -d /dev/net ]); then
|
||||
mkdir -m 755 /dev/net
|
||||
fi
|
||||
mknod /dev/net/tun c 10 200
|
||||
chmod 0755 /dev/net/tun
|
||||
fi
|
||||
|
||||
# Load the tun module if not already loaded
|
||||
if (!(lsmod | grep -q "^tun\s")); then
|
||||
insmod /lib/modules/tun.ko
|
||||
fi
|
||||
}
|
||||
|
||||
case $1 in
|
||||
start)
|
||||
if daemon_status; then
|
||||
exit 0
|
||||
else
|
||||
ensure_tun_created
|
||||
start_daemon
|
||||
exit $?
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
if daemon_status; then
|
||||
stop_daemon
|
||||
exit $?
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
if daemon_status; then
|
||||
echo "${SERVICE_NAME} is running"
|
||||
exit 0
|
||||
else
|
||||
echo "${SERVICE_NAME} is not running"
|
||||
exit 3
|
||||
fi
|
||||
;;
|
||||
log)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "command $1 is not implemented"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
306
release/dist/synology/pkgs.go
vendored
Normal file
306
release/dist/synology/pkgs.go
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package synology contains dist Targets for building Synology Tailscale packages.
|
||||
package synology
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"tailscale.com/release/dist"
|
||||
)
|
||||
|
||||
type target struct {
|
||||
filenameArch string
|
||||
dsmMajorVersion int
|
||||
goenv map[string]string
|
||||
packageCenter bool
|
||||
}
|
||||
|
||||
func (t *target) String() string {
|
||||
return fmt.Sprintf("synology/dsm%d/%s", t.dsmMajorVersion, t.filenameArch)
|
||||
}
|
||||
|
||||
func (t *target) Build(b *dist.Build) ([]string, error) {
|
||||
inner, err := getSynologyBuilds(b).buildInnerPackage(b, t.dsmMajorVersion, t.goenv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := t.buildSPK(b, inner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []string{out}, nil
|
||||
}
|
||||
|
||||
func (t *target) buildSPK(b *dist.Build, inner *innerPkg) (string, error) {
|
||||
filename := fmt.Sprintf("tailscale-%s-%s-%d-dsm%d.spk", t.filenameArch, b.Version.Short, b.Version.Synology[t.dsmMajorVersion], t.dsmMajorVersion)
|
||||
out := filepath.Join(b.Out, filename)
|
||||
log.Printf("Building %s", filename)
|
||||
|
||||
privFile := fmt.Sprintf("privilege-dsm%d", t.dsmMajorVersion)
|
||||
if t.packageCenter && t.dsmMajorVersion == 7 {
|
||||
privFile += ".for-package-center"
|
||||
}
|
||||
|
||||
f, err := os.Create(out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
tw := tar.NewWriter(f)
|
||||
defer tw.Close()
|
||||
|
||||
err = writeTar(tw, b.Time,
|
||||
memFile("INFO", t.mkInfo(b, inner.uncompressedSz), 0644),
|
||||
static("PACKAGE_ICON.PNG", "PACKAGE_ICON.PNG", 0644),
|
||||
static("PACKAGE_ICON_256.PNG", "PACKAGE_ICON_256.PNG", 0644),
|
||||
static("Tailscale.sc", "Tailscale.sc", 0644),
|
||||
dir("conf"),
|
||||
static("resource", "conf/resource", 0644),
|
||||
static(privFile, "conf/privilege", 0644),
|
||||
file(inner.path, "package.tgz", 0644),
|
||||
dir("scripts"),
|
||||
static("scripts/start-stop-status", "scripts/start-stop-status", 0644),
|
||||
static("scripts/postupgrade", "scripts/postupgrade", 0644),
|
||||
static("scripts/preupgrade", "scripts/preupgrade", 0644),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := tw.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (t *target) mkInfo(b *dist.Build, uncompressedSz int64) []byte {
|
||||
var ret bytes.Buffer
|
||||
f := func(k, v string) {
|
||||
fmt.Fprintf(&ret, "%s=%q\n", k, v)
|
||||
}
|
||||
f("package", "Tailscale")
|
||||
f("version", fmt.Sprintf("%s-%d", b.Version.Short, b.Version.Synology[t.dsmMajorVersion]))
|
||||
f("arch", t.filenameArch)
|
||||
f("description", "Connect all your devices using WireGuard, without the hassle.")
|
||||
f("displayname", "Tailscale")
|
||||
f("maintainer", "Tailscale, Inc.")
|
||||
f("maintainer_url", "https://github.com/tailscale/tailscale")
|
||||
f("create_time", b.Time.Format("20060102-15:04:05"))
|
||||
f("dsmuidir", "ui")
|
||||
f("dsmappname", "SYNO.SDS.Tailscale")
|
||||
f("startstop_restart_services", "nginx")
|
||||
switch t.dsmMajorVersion {
|
||||
case 6:
|
||||
f("os_min_ver", "6.0.1-7445")
|
||||
f("os_max_ver", "7.0-40000")
|
||||
case 7:
|
||||
f("os_min_ver", "7.0-40000")
|
||||
f("os_max_ver", "")
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported DSM major version %d", t.dsmMajorVersion))
|
||||
}
|
||||
f("extractsize", fmt.Sprintf("%v", uncompressedSz>>10)) // in KiB
|
||||
return ret.Bytes()
|
||||
}
|
||||
|
||||
type synologyBuildsMemoizeKey struct{}
|
||||
|
||||
type innerPkg struct {
|
||||
path string
|
||||
uncompressedSz int64
|
||||
}
|
||||
|
||||
// synologyBuilds is extra build context shared by all synology builds.
|
||||
type synologyBuilds struct {
|
||||
innerPkgs dist.Memoize[*innerPkg]
|
||||
}
|
||||
|
||||
// getSynologyBuilds returns the synologyBuilds for b, creating one if needed.
|
||||
func getSynologyBuilds(b *dist.Build) *synologyBuilds {
|
||||
return b.Extra(synologyBuildsMemoizeKey{}, func() any { return new(synologyBuilds) }).(*synologyBuilds)
|
||||
}
|
||||
|
||||
// buildInnerPackage builds the inner tarball for synology packages,
|
||||
// which contains the files to unpack to disk on installation (as
|
||||
// opposed to the outer tarball, which contains package metadata)
|
||||
func (m *synologyBuilds) buildInnerPackage(b *dist.Build, dsmVersion int, goenv map[string]string) (*innerPkg, error) {
|
||||
key := []any{dsmVersion, goenv}
|
||||
return m.innerPkgs.Do(key, func() (*innerPkg, error) {
|
||||
ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", goenv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tsd, err := b.BuildGoBinary("tailscale.com/cmd/tailscaled", goenv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tmp := b.TmpDir()
|
||||
out := filepath.Join(tmp, "package.tgz")
|
||||
|
||||
f, err := os.Create(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
gw := gzip.NewWriter(f)
|
||||
defer gw.Close()
|
||||
cw := &countingWriter{gw, 0}
|
||||
tw := tar.NewWriter(cw)
|
||||
defer tw.Close()
|
||||
|
||||
err = writeTar(tw, b.Time,
|
||||
dir("bin"),
|
||||
file(tsd, "bin/tailscaled", 0755),
|
||||
file(ts, "bin/tailscale", 0755),
|
||||
dir("conf"),
|
||||
static("Tailscale.sc", "conf/Tailscale.sc", 0644),
|
||||
static(fmt.Sprintf("logrotate-dsm%d", dsmVersion), "conf/logrotate.conf", 0644),
|
||||
dir("ui"),
|
||||
static("PACKAGE_ICON_256.PNG", "ui/PACKAGE_ICON_256.PNG", 0644),
|
||||
static("config", "ui/config", 0644), // TODO: this has "1.8.3" hard-coded in it; why? what is it? bug?
|
||||
static("index.cgi", "ui/index.cgi", 0755))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tw.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := gw.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &innerPkg{out, cw.n}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// writeTar writes ents to tw.
|
||||
func writeTar(tw *tar.Writer, modTime time.Time, ents ...tarEntry) error {
|
||||
for _, ent := range ents {
|
||||
if err := ent(tw, modTime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// tarEntry is a function that writes tar entries (files or
|
||||
// directories) to a tar.Writer.
|
||||
type tarEntry func(*tar.Writer, time.Time) error
|
||||
|
||||
// fsFile returns a tarEntry that writes src in fsys to dst in the tar
|
||||
// file, with mode.
|
||||
func fsFile(fsys fs.FS, src, dst string, mode int64) tarEntry {
|
||||
return func(tw *tar.Writer, modTime time.Time) error {
|
||||
f, err := fsys.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr := &tar.Header{
|
||||
Name: dst,
|
||||
Size: fi.Size(),
|
||||
Mode: mode,
|
||||
ModTime: modTime,
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = io.Copy(tw, f); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// file returns a tarEntry that writes src on disk into the tar file as
|
||||
// dst, with mode.
|
||||
func file(src, dst string, mode int64) tarEntry {
|
||||
return fsFile(os.DirFS(filepath.Dir(src)), filepath.Base(src), dst, mode)
|
||||
}
|
||||
|
||||
//go:embed files/*
|
||||
var files embed.FS
|
||||
|
||||
// static returns a tarEntry that writes src in files/ into the tar
|
||||
// file as dst, with mode.
|
||||
func static(src, dst string, mode int64) tarEntry {
|
||||
fsys, err := fs.Sub(files, "files")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fsFile(fsys, src, dst, mode)
|
||||
}
|
||||
|
||||
// memFile returns a tarEntry that writes bs to dst in the tar file,
|
||||
// with mode.
|
||||
func memFile(dst string, bs []byte, mode int64) tarEntry {
|
||||
return func(tw *tar.Writer, modTime time.Time) error {
|
||||
hdr := &tar.Header{
|
||||
Name: dst,
|
||||
Size: int64(len(bs)),
|
||||
Mode: mode,
|
||||
ModTime: modTime,
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tw.Write(bs); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// dir returns a tarEntry that creates a world-readable directory in
|
||||
// the tar file.
|
||||
func dir(name string) tarEntry {
|
||||
return func(tw *tar.Writer, modTime time.Time) error {
|
||||
return tw.WriteHeader(&tar.Header{
|
||||
Typeflag: tar.TypeDir,
|
||||
Name: name + "/",
|
||||
Mode: 0755,
|
||||
ModTime: modTime,
|
||||
// TODO: why tailscale? Files are being written as owned by root.
|
||||
Uname: "tailscale",
|
||||
Gname: "tailscale",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type countingWriter struct {
|
||||
w io.Writer
|
||||
n int64
|
||||
}
|
||||
|
||||
func (cw *countingWriter) Write(bs []byte) (int, error) {
|
||||
n, err := cw.w.Write(bs)
|
||||
cw.n += int64(n)
|
||||
return n, err
|
||||
}
|
||||
90
release/dist/synology/targets.go
vendored
Normal file
90
release/dist/synology/targets.go
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package synology
|
||||
|
||||
import "tailscale.com/release/dist"
|
||||
|
||||
var v5Models = []string{
|
||||
"armv5",
|
||||
"88f6281",
|
||||
"88f6282",
|
||||
// hi3535 is actually an armv7 under the hood, but with no
|
||||
// hardware floating point. To the Go compiler, that means it's an
|
||||
// armv5.
|
||||
"hi3535",
|
||||
}
|
||||
|
||||
var v7Models = []string{
|
||||
"armv7",
|
||||
"alpine",
|
||||
"armada370",
|
||||
"armada375",
|
||||
"armada38x",
|
||||
"armadaxp",
|
||||
"comcerto2k",
|
||||
"monaco",
|
||||
}
|
||||
|
||||
func Targets(forPackageCenter bool) []dist.Target {
|
||||
var ret []dist.Target
|
||||
for _, dsmVersion := range []int{6, 7} {
|
||||
ret = append(ret,
|
||||
&target{
|
||||
filenameArch: "x86_64",
|
||||
dsmMajorVersion: dsmVersion,
|
||||
goenv: map[string]string{
|
||||
"GOOS": "linux",
|
||||
"GOARCH": "amd64",
|
||||
},
|
||||
packageCenter: forPackageCenter,
|
||||
},
|
||||
&target{
|
||||
filenameArch: "i686",
|
||||
dsmMajorVersion: dsmVersion,
|
||||
goenv: map[string]string{
|
||||
"GOOS": "linux",
|
||||
"GOARCH": "386",
|
||||
},
|
||||
packageCenter: forPackageCenter,
|
||||
},
|
||||
&target{
|
||||
filenameArch: "armv8",
|
||||
dsmMajorVersion: dsmVersion,
|
||||
goenv: map[string]string{
|
||||
"GOOS": "linux",
|
||||
"GOARCH": "arm64",
|
||||
},
|
||||
packageCenter: forPackageCenter,
|
||||
})
|
||||
|
||||
// On older ARMv5 and ARMv7 platforms, synology used a whole
|
||||
// mess of SoC-specific target names, even though the packages
|
||||
// built for each are identical apart from metadata.
|
||||
for _, v5Arch := range v5Models {
|
||||
ret = append(ret, &target{
|
||||
filenameArch: v5Arch,
|
||||
dsmMajorVersion: dsmVersion,
|
||||
goenv: map[string]string{
|
||||
"GOOS": "linux",
|
||||
"GOARCH": "arm",
|
||||
"GOARM": "5",
|
||||
},
|
||||
packageCenter: forPackageCenter,
|
||||
})
|
||||
}
|
||||
for _, v7Arch := range v7Models {
|
||||
ret = append(ret, &target{
|
||||
filenameArch: v7Arch,
|
||||
dsmMajorVersion: dsmVersion,
|
||||
goenv: map[string]string{
|
||||
"GOOS": "linux",
|
||||
"GOARCH": "arm",
|
||||
"GOARM": "7",
|
||||
},
|
||||
packageCenter: forPackageCenter,
|
||||
})
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
6
release/dist/unixpkgs/pkgs.go
vendored
6
release/dist/unixpkgs/pkgs.go
vendored
@@ -14,7 +14,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goreleaser/nfpm"
|
||||
"tailscale.com/release/dist"
|
||||
@@ -71,7 +70,6 @@ func (t *tgzTarget) Build(b *dist.Build) ([]string, error) {
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
|
||||
buildTime := time.Now()
|
||||
addFile := func(src, dst string, mode int64) error {
|
||||
f, err := os.Open(src)
|
||||
if err != nil {
|
||||
@@ -86,7 +84,7 @@ func (t *tgzTarget) Build(b *dist.Build) ([]string, error) {
|
||||
Name: dst,
|
||||
Size: fi.Size(),
|
||||
Mode: mode,
|
||||
ModTime: buildTime,
|
||||
ModTime: b.Time,
|
||||
Uid: 0,
|
||||
Gid: 0,
|
||||
Uname: "root",
|
||||
@@ -104,7 +102,7 @@ func (t *tgzTarget) Build(b *dist.Build) ([]string, error) {
|
||||
hdr := &tar.Header{
|
||||
Name: name + "/",
|
||||
Mode: 0755,
|
||||
ModTime: buildTime,
|
||||
ModTime: b.Time,
|
||||
Uid: 0,
|
||||
Gid: 0,
|
||||
Uname: "root",
|
||||
|
||||
@@ -217,7 +217,12 @@ main() {
|
||||
VERSION="tumbleweed"
|
||||
PACKAGETYPE="zypper"
|
||||
;;
|
||||
arch|archarm|endeavouros)
|
||||
sle-micro-rancher)
|
||||
OS="opensuse"
|
||||
VERSION="leap/15.4"
|
||||
PACKAGETYPE="zypper"
|
||||
;;
|
||||
arch|archarm|endeavouros|blendos)
|
||||
OS="arch"
|
||||
VERSION="" # rolling release
|
||||
PACKAGETYPE="pacman"
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
) {
|
||||
src = ./.;
|
||||
}).shellNix
|
||||
# nix-direnv cache busting line: sha256-7L+dvS++UNfMVcPUCbK/xuBPwtrzW4RpZTtcl7VCwQs=
|
||||
# nix-direnv cache busting line: sha256-l2uIma2oEdSN0zVo9BOFJF2gC3S60vXwTLVadv8yQPo=
|
||||
|
||||
@@ -81,7 +81,11 @@ func userLookup(username string) (*userMeta, error) {
|
||||
}
|
||||
|
||||
func validUsername(uid string) bool {
|
||||
if len(uid) > 32 || len(uid) == 0 {
|
||||
maxUid := 32
|
||||
if runtime.GOOS == "linux" {
|
||||
maxUid = 256
|
||||
}
|
||||
if len(uid) > maxUid || len(uid) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, r := range uid {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -620,6 +621,14 @@ func (c *compactingChonkFake) PurgeAUMs(hashes []AUMHash) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Avoid go vet complaining about copying a lock value
|
||||
func cloneMem(src, dst *Mem) {
|
||||
dst.l = sync.RWMutex{}
|
||||
dst.aums = src.aums
|
||||
dst.parentIndex = src.parentIndex
|
||||
dst.lastActiveAncestor = src.lastActiveAncestor
|
||||
}
|
||||
|
||||
func TestCompact(t *testing.T) {
|
||||
fakeState := &State{
|
||||
Keys: []Key{{Kind: Key25519, Votes: 1}},
|
||||
@@ -661,12 +670,13 @@ func TestCompact(t *testing.T) {
|
||||
`, optTemplate("checkpoint", AUM{MessageKind: AUMCheckpoint, State: fakeState}))
|
||||
|
||||
storage := &compactingChonkFake{
|
||||
Mem: (*c.Chonk().(*Mem)),
|
||||
aumAge: map[AUMHash]time.Time{(c.AUMHashes["F1"]): time.Now()},
|
||||
t: t,
|
||||
wantDelete: []AUMHash{c.AUMHashes["A"], c.AUMHashes["B"], c.AUMHashes["OLD"]},
|
||||
}
|
||||
|
||||
cloneMem(c.Chonk().(*Mem), &storage.Mem)
|
||||
|
||||
lastActiveAncestor, err := Compact(storage, c.AUMHashes["H"], CompactionOptions{MinChain: 2, MinAge: time.Hour})
|
||||
if err != nil {
|
||||
t.Errorf("Compact() failed: %v", err)
|
||||
|
||||
@@ -114,6 +114,8 @@ func autoflagsForTest(argv []string, env *Environment, goroot, nativeGOOS, nativ
|
||||
xcodeFlags = append(xcodeFlags, "-miphoneos-version-min="+env.Get("IPHONEOS_DEPLOYMENT_TARGET", ""))
|
||||
case env.IsSet("MACOSX_DEPLOYMENT_TARGET"):
|
||||
xcodeFlags = append(xcodeFlags, "-mmacosx-version-min="+env.Get("MACOSX_DEPLOYMENT_TARGET", ""))
|
||||
case env.IsSet("TVOS_DEPLOYMENT_TARGET"):
|
||||
xcodeFlags = append(xcodeFlags, "-mtvos-version-min="+env.Get("TVOS_DEPLOYMENT_TARGET", ""))
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("invoked by Xcode but couldn't figure out deployment target. Did Xcode change its envvars again?")
|
||||
}
|
||||
@@ -153,7 +155,9 @@ func autoflagsForTest(argv []string, env *Environment, goroot, nativeGOOS, nativ
|
||||
|
||||
env.Set("GOOS", targetOS)
|
||||
env.Set("GOARCH", targetArch)
|
||||
env.Set("GOARM", "5") // TODO: fix, see go/internal-bug/3092
|
||||
if !env.IsSet("GOARM") {
|
||||
env.Set("GOARM", "5") // TODO: fix, see go/internal-bug/3092
|
||||
}
|
||||
env.Set("GOMIPS", "softfloat")
|
||||
env.Set("CGO_ENABLED", boolStr(cgo))
|
||||
env.Set("CGO_CFLAGS", strings.Join(cgoCflags, " "))
|
||||
|
||||
@@ -66,6 +66,12 @@ type Server struct {
|
||||
// state. If empty, a directory is selected automatically
|
||||
// under os.UserConfigDir (https://golang.org/pkg/os/#UserConfigDir).
|
||||
// based on the name of the binary.
|
||||
//
|
||||
// If you want to use multiple tsnet services in the same
|
||||
// binary, you will need to make sure that Dir is set uniquely
|
||||
// for each service. A good pattern for this is to have a
|
||||
// "base" directory (such as your mutable storage folder) and
|
||||
// then append the hostname on the end of it.
|
||||
Dir string
|
||||
|
||||
// Store specifies the state store to use.
|
||||
|
||||
22
util/cmpx/cmpx.go
Normal file
22
util/cmpx/cmpx.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package cmpx has code that will likely land in a future version of Go, but
|
||||
// we want sooner.
|
||||
package cmpx
|
||||
|
||||
// Or returns the first non-zero element of list, or else returns the zero T.
|
||||
//
|
||||
// This is the proposal from
|
||||
// https://github.com/golang/go/issues/60204#issuecomment-1581245334.
|
||||
func Or[T comparable](list ...T) T {
|
||||
// TODO(bradfitz): remove the comparable constraint so we can use this
|
||||
// with funcs too and use reflect to see whether they're non-zero? 🤷♂️
|
||||
var zero T
|
||||
for _, v := range list {
|
||||
if v != zero {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return zero
|
||||
}
|
||||
24
util/cmpx/cmpx_test.go
Normal file
24
util/cmpx/cmpx_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package cmpx
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestOr(t *testing.T) {
|
||||
if g, w := Or[string](), ""; g != w {
|
||||
t.Errorf("got %v; want %v", g, w)
|
||||
}
|
||||
if g, w := Or[int](), 0; g != w {
|
||||
t.Errorf("got %v; want %v", g, w)
|
||||
}
|
||||
if g, w := Or("", "foo", "bar"), "foo"; g != w {
|
||||
t.Errorf("got %v; want %v", g, w)
|
||||
}
|
||||
if g, w := Or("foo", "bar"), "foo"; g != w {
|
||||
t.Errorf("got %v; want %v", g, w)
|
||||
}
|
||||
if g, w := Or("", "", "bar"), "bar"; g != w {
|
||||
t.Errorf("got %v; want %v", g, w)
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,7 @@
|
||||
package winutil
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go mksyscall.go
|
||||
//go:generate go run golang.org/x/tools/cmd/goimports -w zsyscall_windows.go
|
||||
|
||||
//sys queryServiceConfig2(hService windows.Handle, infoLevel uint32, buf *byte, bufLen uint32, bytesNeeded *uint32) (err error) [failretval==0] = advapi32.QueryServiceConfig2W
|
||||
//sys regEnumValue(key registry.Key, index uint32, valueName *uint16, valueNameLen *uint32, reserved *uint32, valueType *uint32, pData *byte, cbData *uint32) (ret error) [failretval!=0] = advapi32.RegEnumValueW
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
package winutil
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
@@ -13,10 +16,12 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -551,3 +556,166 @@ func findHomeDirInRegistry(uid string) (dir string, err error) {
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
const (
|
||||
maxBinaryValueLen = 128 // we'll truncate any binary values longer than this
|
||||
maxRegValueNameLen = 16384 // maximum length supported by Windows + 1
|
||||
initialValueBufLen = 80 // large enough to contain a stringified GUID encoded as UTF-16
|
||||
)
|
||||
|
||||
const (
|
||||
supportInfoKeyRegistry = "Registry"
|
||||
)
|
||||
|
||||
// LogSupportInfo obtains information useful for troubleshooting and support,
|
||||
// and writes it to the log as a JSON-encoded object.
|
||||
func LogSupportInfo(logf logger.Logf) {
|
||||
var b strings.Builder
|
||||
if err := getSupportInfo(&b); err != nil {
|
||||
log.Printf("error encoding support info: %v", err)
|
||||
return
|
||||
}
|
||||
logf("Support Info: %s", b.String())
|
||||
}
|
||||
|
||||
func getSupportInfo(w io.Writer) error {
|
||||
output := make(map[string]any)
|
||||
|
||||
regInfo, err := getRegistrySupportInfo(registry.LOCAL_MACHINE, []string{regPolicyBase, regBase})
|
||||
if err == nil {
|
||||
output[supportInfoKeyRegistry] = regInfo
|
||||
} else {
|
||||
output[supportInfoKeyRegistry] = err
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
return enc.Encode(output)
|
||||
}
|
||||
|
||||
type getRegistrySupportInfoBufs struct {
|
||||
nameBuf []uint16
|
||||
valueBuf []byte
|
||||
}
|
||||
|
||||
func getRegistrySupportInfo(root registry.Key, subKeys []string) (map[string]any, error) {
|
||||
bufs := getRegistrySupportInfoBufs{
|
||||
nameBuf: make([]uint16, maxRegValueNameLen),
|
||||
valueBuf: make([]byte, initialValueBufLen),
|
||||
}
|
||||
|
||||
output := make(map[string]any)
|
||||
|
||||
for _, subKey := range subKeys {
|
||||
if err := getRegSubKey(root, subKey, 5, &bufs, output); err != nil && !errors.Is(err, registry.ErrNotExist) {
|
||||
return nil, fmt.Errorf("getRegistrySupportInfo: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func keyString(key registry.Key, subKey string) string {
|
||||
var keyStr string
|
||||
switch key {
|
||||
case registry.CLASSES_ROOT:
|
||||
keyStr = `HKCR\`
|
||||
case registry.CURRENT_USER:
|
||||
keyStr = `HKCU\`
|
||||
case registry.LOCAL_MACHINE:
|
||||
keyStr = `HKLM\`
|
||||
case registry.USERS:
|
||||
keyStr = `HKU\`
|
||||
case registry.CURRENT_CONFIG:
|
||||
keyStr = `HKCC\`
|
||||
case registry.PERFORMANCE_DATA:
|
||||
keyStr = `HKPD\`
|
||||
default:
|
||||
}
|
||||
|
||||
return keyStr + subKey
|
||||
}
|
||||
|
||||
func getRegSubKey(key registry.Key, subKey string, recursionLimit int, bufs *getRegistrySupportInfoBufs, output map[string]any) error {
|
||||
keyStr := keyString(key, subKey)
|
||||
k, err := registry.OpenKey(key, subKey, registry.READ)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening %q: %w", keyStr, err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
kv := make(map[string]any)
|
||||
index := uint32(0)
|
||||
|
||||
loopValues:
|
||||
for {
|
||||
nbuf := bufs.nameBuf
|
||||
nameLen := uint32(len(nbuf))
|
||||
valueType := uint32(0)
|
||||
vbuf := bufs.valueBuf
|
||||
valueLen := uint32(len(vbuf))
|
||||
|
||||
err := regEnumValue(k, index, &nbuf[0], &nameLen, nil, &valueType, &vbuf[0], &valueLen)
|
||||
switch err {
|
||||
case windows.ERROR_NO_MORE_ITEMS:
|
||||
break loopValues
|
||||
case windows.ERROR_MORE_DATA:
|
||||
bufs.valueBuf = make([]byte, valueLen)
|
||||
continue
|
||||
case nil:
|
||||
default:
|
||||
return fmt.Errorf("regEnumValue: %w", err)
|
||||
}
|
||||
|
||||
var value any
|
||||
|
||||
switch valueType {
|
||||
case registry.SZ, registry.EXPAND_SZ:
|
||||
value = windows.UTF16PtrToString((*uint16)(unsafe.Pointer(&vbuf[0])))
|
||||
case registry.BINARY:
|
||||
if valueLen > maxBinaryValueLen {
|
||||
valueLen = maxBinaryValueLen
|
||||
}
|
||||
value = append([]byte{}, vbuf[:valueLen]...)
|
||||
case registry.DWORD:
|
||||
value = binary.LittleEndian.Uint32(vbuf[:4])
|
||||
case registry.MULTI_SZ:
|
||||
// Adapted from x/sys/windows/registry/(Key).GetStringsValue
|
||||
p := (*[1 << 29]uint16)(unsafe.Pointer(&vbuf[0]))[: valueLen/2 : valueLen/2]
|
||||
var strs []string
|
||||
if len(p) > 0 {
|
||||
if p[len(p)-1] == 0 {
|
||||
p = p[:len(p)-1]
|
||||
}
|
||||
strs = make([]string, 0, 5)
|
||||
from := 0
|
||||
for i, c := range p {
|
||||
if c == 0 {
|
||||
strs = append(strs, string(utf16.Decode(p[from:i])))
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
value = strs
|
||||
case registry.QWORD:
|
||||
value = binary.LittleEndian.Uint64(vbuf[:8])
|
||||
default:
|
||||
value = fmt.Sprintf("<unsupported value type %d>", valueType)
|
||||
}
|
||||
|
||||
kv[windows.UTF16PtrToString(&nbuf[0])] = value
|
||||
index++
|
||||
}
|
||||
|
||||
if recursionLimit > 0 {
|
||||
if sks, err := k.ReadSubKeyNames(0); err == nil {
|
||||
for _, sk := range sks {
|
||||
if err := getRegSubKey(k, sk, recursionLimit-1, bufs, kv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output[keyStr] = kv
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,13 @@
|
||||
package winutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,3 +34,117 @@ func TestLookupPseudoUser(t *testing.T) {
|
||||
t.Errorf("LookupPseudoUser(%q) unexpectedly succeeded", networkSID)
|
||||
}
|
||||
}
|
||||
|
||||
func makeLongBinaryValue() []byte {
|
||||
buf := make([]byte, maxBinaryValueLen*2)
|
||||
for i, _ := range buf {
|
||||
buf[i] = byte(i % 0xFF)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
var testData = map[string]any{
|
||||
"": "I am the default",
|
||||
"StringEmpty": "",
|
||||
"StringShort": "Hello",
|
||||
"StringLong": strings.Repeat("7", initialValueBufLen+1),
|
||||
"MultiStringEmpty": []string{},
|
||||
"MultiStringSingle": []string{"Foo"},
|
||||
"MultiStringSingleEmpty": []string{""},
|
||||
"MultiString": []string{"Foo", "Bar", "Baz"},
|
||||
"MultiStringWithEmptyBeginning": []string{"", "Foo", "Bar"},
|
||||
"MultiStringWithEmptyMiddle": []string{"Foo", "", "Bar"},
|
||||
"MultiStringWithEmptyEnd": []string{"Foo", "Bar", ""},
|
||||
"DWord": uint32(0x12345678),
|
||||
"QWord": uint64(0x123456789abcdef0),
|
||||
"BinaryEmpty": []byte{},
|
||||
"BinaryShort": []byte{0x01, 0x02, 0x03, 0x04},
|
||||
"BinaryLong": makeLongBinaryValue(),
|
||||
}
|
||||
|
||||
const (
|
||||
keyNameTest = `SOFTWARE\Tailscale Test`
|
||||
subKeyNameTest = "SubKey"
|
||||
)
|
||||
|
||||
func setValues(t *testing.T, k registry.Key) {
|
||||
for vk, v := range testData {
|
||||
var err error
|
||||
switch tv := v.(type) {
|
||||
case string:
|
||||
err = k.SetStringValue(vk, tv)
|
||||
case []string:
|
||||
err = k.SetStringsValue(vk, tv)
|
||||
case uint32:
|
||||
err = k.SetDWordValue(vk, tv)
|
||||
case uint64:
|
||||
err = k.SetQWordValue(vk, tv)
|
||||
case []byte:
|
||||
err = k.SetBinaryValue(vk, tv)
|
||||
default:
|
||||
t.Fatalf("Unknown type")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting %q: %v", vk, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistrySupportInfo(t *testing.T) {
|
||||
// Make sure the key doesn't exist yet
|
||||
k, err := registry.OpenKey(registry.CURRENT_USER, keyNameTest, registry.READ)
|
||||
switch {
|
||||
case err == nil:
|
||||
k.Close()
|
||||
t.Fatalf("Test key already exists")
|
||||
case !errors.Is(err, registry.ErrNotExist):
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
func() {
|
||||
k, _, err := registry.CreateKey(registry.CURRENT_USER, keyNameTest, registry.WRITE)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating test key: %v", err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
setValues(t, k)
|
||||
|
||||
sk, _, err := registry.CreateKey(k, subKeyNameTest, registry.WRITE)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating test subkey: %v", err)
|
||||
}
|
||||
defer sk.Close()
|
||||
|
||||
setValues(t, sk)
|
||||
}()
|
||||
|
||||
t.Cleanup(func() {
|
||||
registry.DeleteKey(registry.CURRENT_USER, keyNameTest+"\\"+subKeyNameTest)
|
||||
registry.DeleteKey(registry.CURRENT_USER, keyNameTest)
|
||||
})
|
||||
|
||||
wantValuesData := maps.Clone(testData)
|
||||
wantValuesData["BinaryLong"] = (wantValuesData["BinaryLong"].([]byte))[:maxBinaryValueLen]
|
||||
|
||||
wantKeyData := make(map[string]any)
|
||||
maps.Copy(wantKeyData, wantValuesData)
|
||||
wantSubKeyData := make(map[string]any)
|
||||
maps.Copy(wantSubKeyData, wantValuesData)
|
||||
wantKeyData[subKeyNameTest] = wantSubKeyData
|
||||
|
||||
wantData := map[string]any{
|
||||
"HKCU\\" + keyNameTest: wantKeyData,
|
||||
}
|
||||
|
||||
gotData, err := getRegistrySupportInfo(registry.CURRENT_USER, []string{keyNameTest})
|
||||
if err != nil {
|
||||
t.Errorf("getRegistrySupportInfo error: %v", err)
|
||||
}
|
||||
|
||||
want, got := fmt.Sprintf("%#v", wantData), fmt.Sprintf("%#v", gotData)
|
||||
if want != got {
|
||||
t.Errorf("Compare error: want\n%s,\ngot %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
@@ -41,6 +42,7 @@ var (
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
|
||||
procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W")
|
||||
procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
|
||||
)
|
||||
|
||||
func queryServiceConfig2(hService windows.Handle, infoLevel uint32, buf *byte, bufLen uint32, bytesNeeded *uint32) (err error) {
|
||||
@@ -50,3 +52,11 @@ func queryServiceConfig2(hService windows.Handle, infoLevel uint32, buf *byte, b
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func regEnumValue(key registry.Key, index uint32, valueName *uint16, valueNameLen *uint32, reserved *uint32, valueType *uint32, pData *byte, cbData *uint32) (ret error) {
|
||||
r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(valueName)), uintptr(unsafe.Pointer(valueNameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valueType)), uintptr(unsafe.Pointer(pData)), uintptr(unsafe.Pointer(cbData)), 0)
|
||||
if r0 != 0 {
|
||||
ret = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ type VersionInfo struct {
|
||||
// Winres is the version string that gets embedded into Windows exe
|
||||
// metadata. It is of the form "x,y,z,0".
|
||||
Winres string
|
||||
// Synology is a map of Synology DSM major version to the
|
||||
// Tailscale numeric version that gets embedded in Synology spk
|
||||
// files.
|
||||
Synology map[int]int64
|
||||
// GitDate is the unix timestamp of GitHash's commit date.
|
||||
GitDate string
|
||||
// OtherDate is the unix timestamp of OtherHash's commit date, if any.
|
||||
@@ -239,6 +243,10 @@ func mkOutput(v verInfo) (VersionInfo, error) {
|
||||
GitHash: fmt.Sprintf("%s", v.hash),
|
||||
GitDate: fmt.Sprintf("%s", v.date),
|
||||
Track: track,
|
||||
Synology: map[int]int64{
|
||||
6: 6*1_000_000_000 + int64(v.major-1)*1_000_000 + int64(v.minor)*1_000 + int64(v.patch),
|
||||
7: 7*1_000_000_000 + int64(v.major-1)*1_000_000 + int64(v.minor)*1_000 + int64(v.patch),
|
||||
},
|
||||
}
|
||||
|
||||
if v.otherHash != "" {
|
||||
|
||||
@@ -28,6 +28,9 @@ func OS() string {
|
||||
// differentiate them. Then a later Go release added GOOS=ios as a separate
|
||||
// platform, but by then the "iOS" and "macOS" values we'd picked, with that
|
||||
// exact capitalization, were already baked into databases.
|
||||
if IsAppleTV() {
|
||||
return "tvOS"
|
||||
}
|
||||
if runtime.GOOS == "ios" {
|
||||
return "iOS"
|
||||
}
|
||||
@@ -76,6 +79,19 @@ func IsMacSysExt() bool {
|
||||
})
|
||||
}
|
||||
|
||||
var isAppleTV lazy.SyncValue[bool]
|
||||
|
||||
// IsAppleTV reports whether this binary is part of the Tailscale network extension for tvOS.
|
||||
// Needed because runtime.GOOS returns "ios" otherwise.
|
||||
func IsAppleTV() bool {
|
||||
if runtime.GOOS != "ios" {
|
||||
return false
|
||||
}
|
||||
return isAppleTV.Get(func() bool {
|
||||
return strings.EqualFold(os.Getenv("XPC_SERVICE_NAME"), "io.tailscale.ipn.tvos.network-extension")
|
||||
})
|
||||
}
|
||||
|
||||
var isWindowsGUI lazy.SyncValue[bool]
|
||||
|
||||
// IsWindowsGUI reports whether the current process is the Windows GUI.
|
||||
|
||||
@@ -622,11 +622,21 @@ func (ns *Impl) userPing(dstIP netip.Addr, pingResPkt []byte) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
err = exec.Command("ping", "-n", "1", "-w", "3000", dstIP.String()).Run()
|
||||
case "darwin":
|
||||
case "darwin", "freebsd":
|
||||
// Note: 2000 ms is actually 1 second + 2,000
|
||||
// milliseconds extra for 3 seconds total.
|
||||
// See https://github.com/tailscale/tailscale/pull/3753 for details.
|
||||
err = exec.Command("ping", "-c", "1", "-W", "2000", dstIP.String()).Run()
|
||||
ping := "ping"
|
||||
if dstIP.Is6() {
|
||||
ping = "ping6"
|
||||
}
|
||||
err = exec.Command(ping, "-c", "1", "-W", "2000", dstIP.String()).Run()
|
||||
case "openbsd":
|
||||
ping := "ping"
|
||||
if dstIP.Is6() {
|
||||
ping = "ping6"
|
||||
}
|
||||
err = exec.Command(ping, "-c", "1", "-w", "3", dstIP.String()).Run()
|
||||
case "android":
|
||||
ping := "/system/bin/ping"
|
||||
if dstIP.Is6() {
|
||||
|
||||
@@ -60,6 +60,11 @@ type watchdogEngine struct {
|
||||
inFlightCtr uint64
|
||||
}
|
||||
|
||||
// inDebugger returns true if the process has $TS_DEBUGGER set. This should be
|
||||
// used to avoid terminating the process when operations take an unusual amount
|
||||
// of time, as would be caused by a debugger breakpoint.
|
||||
var inDebugger = envknob.RegisterBool("TS_DEBUGGER")
|
||||
|
||||
func (e *watchdogEngine) watchdogErr(name string, fn func() error) error {
|
||||
// Track all in-flight operations so we can print more useful error
|
||||
// messages on watchdog failure
|
||||
@@ -88,6 +93,11 @@ func (e *watchdogEngine) watchdogErr(name string, fn func() error) error {
|
||||
t.Stop()
|
||||
return err
|
||||
case <-t.C:
|
||||
if inDebugger() {
|
||||
e.logf("wgengine: watchdog timeout on %s (in debugger, maybe process was frozen?)", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
buf := new(strings.Builder)
|
||||
pprof.Lookup("goroutine").WriteTo(buf, 1)
|
||||
e.logf("wgengine watchdog stacks:\n%s", buf.String())
|
||||
|
||||
@@ -390,7 +390,6 @@ minmi
|
||||
triceratops
|
||||
troodon
|
||||
trex
|
||||
tyrannosarus
|
||||
shetland
|
||||
pinto
|
||||
appaloosa
|
||||
|
||||
Reference in New Issue
Block a user