mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
clientupdate: add linux tarball updates (#9144)
As a fallback to package managers, allow updating tailscale that was self-installed in some way. There are some tricky bits around updating the systemd unit (should we stick to local binary paths or to the ones in tailscaled.service?), so leaving that out for now. Updates #6995 Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
parent
ce1e02096a
commit
4e72992900
@ -7,13 +7,16 @@
|
||||
package clientupdate
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -839,7 +842,162 @@ func (up *Updater) updateFreeBSD() (err error) {
|
||||
}
|
||||
|
||||
func (up *Updater) updateLinuxBinary() error {
|
||||
return errors.New("Linux binary updates without a package manager are not supported yet")
|
||||
ver, err := requestedTailscaleVersion(up.Version, up.track)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !up.confirm(ver) {
|
||||
return nil
|
||||
}
|
||||
// Root is needed to overwrite binaries and restart systemd unit.
|
||||
if err := requireRoot(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dlPath, err := up.downloadLinuxTarball(ver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
up.Logf("Extracting %q", dlPath)
|
||||
if err := up.unpackLinuxTarball(dlPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Remove(dlPath); err != nil {
|
||||
up.Logf("failed to clean up %q: %w", dlPath, err)
|
||||
}
|
||||
if err := restartSystemdUnit(context.Background()); err != nil {
|
||||
if errors.Is(err, errors.ErrUnsupported) {
|
||||
up.Logf("Tailscale binaries updated successfully.\nPlease restart tailscaled to finish the update.")
|
||||
} else {
|
||||
up.Logf("Tailscale binaries updated successfully, but failed to restart tailscaled: %s.\nPlease restart tailscaled to finish the update.", err)
|
||||
}
|
||||
} else {
|
||||
up.Logf("Success")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (up *Updater) downloadLinuxTarball(ver string) (string, error) {
|
||||
dlDir, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dlDir = filepath.Join(dlDir, "tailscale-update")
|
||||
if err := os.MkdirAll(dlDir, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
pkgsPath := fmt.Sprintf("%s/tailscale_%s_%s.tgz", up.track, ver, runtime.GOARCH)
|
||||
dlPath := filepath.Join(dlDir, path.Base(pkgsPath))
|
||||
if err := up.downloadURLToFile(pkgsPath, dlPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dlPath, nil
|
||||
}
|
||||
|
||||
func (up *Updater) unpackLinuxTarball(path string) error {
|
||||
tailscale, tailscaled, err := binaryPaths()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
gr, err := gzip.NewReader(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gr.Close()
|
||||
tr := tar.NewReader(gr)
|
||||
files := make(map[string]int)
|
||||
wantFiles := map[string]int{
|
||||
"tailscale": 1,
|
||||
"tailscaled": 1,
|
||||
}
|
||||
for {
|
||||
th, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed extracting %q: %w", path, err)
|
||||
}
|
||||
// TODO(awly): try to also extract tailscaled.service. The tricky part
|
||||
// is fixing up binary paths in that file if they differ from where
|
||||
// local tailscale/tailscaled are installed. Also, this may not be a
|
||||
// systemd distro.
|
||||
switch filepath.Base(th.Name) {
|
||||
case "tailscale":
|
||||
files["tailscale"]++
|
||||
if err := writeFile(tr, tailscale+".new", 0755); err != nil {
|
||||
return fmt.Errorf("failed extracting the new tailscale binary from %q: %w", path, err)
|
||||
}
|
||||
case "tailscaled":
|
||||
files["tailscaled"]++
|
||||
if err := writeFile(tr, tailscaled+".new", 0755); err != nil {
|
||||
return fmt.Errorf("failed extracting the new tailscaled binary from %q: %w", path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !maps.Equal(files, wantFiles) {
|
||||
return fmt.Errorf("%q has missing or duplicate files: got %v, want %v", path, files, wantFiles)
|
||||
}
|
||||
|
||||
// Only place the files in final locations after everything extracted correctly.
|
||||
if err := os.Rename(tailscale+".new", tailscale); err != nil {
|
||||
return err
|
||||
}
|
||||
up.Logf("Updated %s", tailscale)
|
||||
if err := os.Rename(tailscaled+".new", tailscaled); err != nil {
|
||||
return err
|
||||
}
|
||||
up.Logf("Updated %s", tailscaled)
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeFile(r io.Reader, path string, perm os.FileMode) error {
|
||||
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove existing file at %q: %w", path, err)
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := io.Copy(f, r); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
// Var allows overriding this in tests.
|
||||
var binaryPaths = func() (tailscale, tailscaled string, err error) {
|
||||
// This can be either tailscale or tailscaled.
|
||||
this, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
otherName := "tailscaled"
|
||||
if filepath.Base(this) == "tailscaled" {
|
||||
otherName = "tailscale"
|
||||
}
|
||||
// Try to find the other binary in the same directory.
|
||||
other := filepath.Join(filepath.Dir(this), otherName)
|
||||
_, err = os.Stat(other)
|
||||
if os.IsNotExist(err) {
|
||||
// If it's not in the same directory, try to find it in $PATH.
|
||||
other, err = exec.LookPath(otherName)
|
||||
}
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("cannot find %q in neither %q nor $PATH: %w", otherName, filepath.Dir(this), err)
|
||||
}
|
||||
if otherName == "tailscaled" {
|
||||
return this, other, nil
|
||||
} else {
|
||||
return other, this, nil
|
||||
}
|
||||
}
|
||||
|
||||
func haveExecutable(name string) bool {
|
||||
|
@ -4,9 +4,14 @@
|
||||
package clientupdate
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -502,3 +507,257 @@ func TestParseSynoinfo(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnpackLinuxTarball(t *testing.T) {
|
||||
oldBinaryPaths := binaryPaths
|
||||
t.Cleanup(func() { binaryPaths = oldBinaryPaths })
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
tarball map[string]string
|
||||
before map[string]string
|
||||
after map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
before: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscaled": "v1",
|
||||
},
|
||||
tarball: map[string]string{
|
||||
"/usr/bin/tailscale": "v2",
|
||||
"/usr/bin/tailscaled": "v2",
|
||||
},
|
||||
after: map[string]string{
|
||||
"tailscale": "v2",
|
||||
"tailscaled": "v2",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "don't touch unrelated files",
|
||||
before: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscaled": "v1",
|
||||
"foo": "bar",
|
||||
},
|
||||
tarball: map[string]string{
|
||||
"/usr/bin/tailscale": "v2",
|
||||
"/usr/bin/tailscaled": "v2",
|
||||
},
|
||||
after: map[string]string{
|
||||
"tailscale": "v2",
|
||||
"tailscaled": "v2",
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "unmodified",
|
||||
before: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscaled": "v1",
|
||||
},
|
||||
tarball: map[string]string{
|
||||
"/usr/bin/tailscale": "v1",
|
||||
"/usr/bin/tailscaled": "v1",
|
||||
},
|
||||
after: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscaled": "v1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "ignore extra tarball files",
|
||||
before: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscaled": "v1",
|
||||
},
|
||||
tarball: map[string]string{
|
||||
"/usr/bin/tailscale": "v2",
|
||||
"/usr/bin/tailscaled": "v2",
|
||||
"/systemd/tailscaled.service": "v2",
|
||||
},
|
||||
after: map[string]string{
|
||||
"tailscale": "v2",
|
||||
"tailscaled": "v2",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tarball missing tailscaled",
|
||||
before: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscaled": "v1",
|
||||
},
|
||||
tarball: map[string]string{
|
||||
"/usr/bin/tailscale": "v2",
|
||||
},
|
||||
after: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscale.new": "v2",
|
||||
"tailscaled": "v1",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "duplicate tailscale binary",
|
||||
before: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscaled": "v1",
|
||||
},
|
||||
tarball: map[string]string{
|
||||
"/usr/bin/tailscale": "v2",
|
||||
"/usr/sbin/tailscale": "v2",
|
||||
"/usr/bin/tailscaled": "v2",
|
||||
},
|
||||
after: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscale.new": "v2",
|
||||
"tailscaled": "v1",
|
||||
"tailscaled.new": "v2",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "empty archive",
|
||||
before: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscaled": "v1",
|
||||
},
|
||||
tarball: map[string]string{},
|
||||
after: map[string]string{
|
||||
"tailscale": "v1",
|
||||
"tailscaled": "v1",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
// Swap out binaryPaths function to point at dummy file paths.
|
||||
tmp := t.TempDir()
|
||||
tailscalePath := filepath.Join(tmp, "tailscale")
|
||||
tailscaledPath := filepath.Join(tmp, "tailscaled")
|
||||
binaryPaths = func() (string, string, error) {
|
||||
return tailscalePath, tailscaledPath, nil
|
||||
}
|
||||
for name, content := range tt.before {
|
||||
if err := os.WriteFile(filepath.Join(tmp, name), []byte(content), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
tarPath := filepath.Join(tmp, "tailscale.tgz")
|
||||
genTarball(t, tarPath, tt.tarball)
|
||||
|
||||
up := &Updater{Arguments: Arguments{Logf: t.Logf}}
|
||||
err := up.unpackLinuxTarball(tarPath)
|
||||
if err != nil {
|
||||
if !tt.wantErr {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
} else if tt.wantErr {
|
||||
t.Fatalf("unpack succeeded, expected an error")
|
||||
}
|
||||
|
||||
gotAfter := make(map[string]string)
|
||||
err = filepath.WalkDir(tmp, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.Type().IsDir() {
|
||||
return nil
|
||||
}
|
||||
if path == tarPath {
|
||||
return nil
|
||||
}
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path = filepath.ToSlash(path)
|
||||
base := filepath.ToSlash(tmp)
|
||||
gotAfter[strings.TrimPrefix(path, base+"/")] = string(content)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !maps.Equal(gotAfter, tt.after) {
|
||||
t.Errorf("files after unpack: %+v, want %+v", gotAfter, tt.after)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func genTarball(t *testing.T, path string, files map[string]string) {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
gw := gzip.NewWriter(f)
|
||||
defer gw.Close()
|
||||
tw := tar.NewWriter(gw)
|
||||
defer tw.Close()
|
||||
for file, content := range files {
|
||||
if err := tw.WriteHeader(&tar.Header{
|
||||
Name: file,
|
||||
Size: int64(len(content)),
|
||||
Mode: 0755,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := tw.Write([]byte(content)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteFileOverwrite(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "test")
|
||||
for i := 0; i < 2; i++ {
|
||||
content := fmt.Sprintf("content %d", i)
|
||||
if err := writeFile(strings.NewReader(content), path, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(got) != content {
|
||||
t.Errorf("got content: %q, want: %q", got, content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteFileSymlink(t *testing.T) {
|
||||
// Test for a malicious symlink at the destination path.
|
||||
// f2 points to f1 and writeFile(f2) should not end up overwriting f1.
|
||||
tmp := t.TempDir()
|
||||
f1 := filepath.Join(tmp, "f1")
|
||||
if err := os.WriteFile(f1, []byte("old"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f2 := filepath.Join(tmp, "f2")
|
||||
if err := os.Symlink(f1, f2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := writeFile(strings.NewReader("new"), f2, 0600); err != nil {
|
||||
t.Errorf("writeFile(%q) failed: %v", f2, err)
|
||||
}
|
||||
want := map[string]string{
|
||||
f1: "old",
|
||||
f2: "new",
|
||||
}
|
||||
for f, content := range want {
|
||||
got, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(got) != content {
|
||||
t.Errorf("%q: got content %q, want %q", f, got, content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
37
clientupdate/systemd_linux.go
Normal file
37
clientupdate/systemd_linux.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package clientupdate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/go-systemd/v22/dbus"
|
||||
)
|
||||
|
||||
func restartSystemdUnit(ctx context.Context) error {
|
||||
c, err := dbus.NewWithContext(ctx)
|
||||
if err != nil {
|
||||
// Likely not a systemd-managed distro.
|
||||
return errors.ErrUnsupported
|
||||
}
|
||||
defer c.Close()
|
||||
if err := c.ReloadContext(ctx); err != nil {
|
||||
return fmt.Errorf("failed to reload tailsacled.service: %w", err)
|
||||
}
|
||||
ch := make(chan string, 1)
|
||||
if _, err := c.RestartUnitContext(ctx, "tailscaled.service", "replace", ch); err != nil {
|
||||
return fmt.Errorf("failed to restart tailsacled.service: %w", err)
|
||||
}
|
||||
select {
|
||||
case res := <-ch:
|
||||
if res != "done" {
|
||||
return fmt.Errorf("systemd service restart failed with result %q", res)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
return nil
|
||||
}
|
15
clientupdate/systemd_other.go
Normal file
15
clientupdate/systemd_other.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !linux
|
||||
|
||||
package clientupdate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func restartSystemdUnit(ctx context.Context) error {
|
||||
return errors.ErrUnsupported
|
||||
}
|
@ -11,9 +11,11 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
|
||||
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
|
||||
L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw
|
||||
L github.com/coreos/go-systemd/v22/dbus from tailscale.com/clientupdate
|
||||
W 💣 github.com/dblohm7/wingoes from tailscale.com/util/winutil/authenticode+
|
||||
W 💣 github.com/dblohm7/wingoes/pe from tailscale.com/util/winutil/authenticode
|
||||
github.com/fxamacker/cbor/v2 from tailscale.com/tka
|
||||
L 💣 github.com/godbus/dbus/v5 from github.com/coreos/go-systemd/v22/dbus
|
||||
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
||||
L github.com/google/nftables from tailscale.com/util/linuxfw
|
||||
L 💣 github.com/google/nftables/alignedbuff from github.com/google/nftables/xt
|
||||
@ -200,11 +202,12 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||
golang.org/x/text/unicode/bidi from golang.org/x/net/idna+
|
||||
golang.org/x/text/unicode/norm from golang.org/x/net/idna
|
||||
golang.org/x/time/rate from tailscale.com/cmd/tailscale/cli+
|
||||
archive/tar from tailscale.com/clientupdate
|
||||
bufio from compress/flate+
|
||||
bytes from bufio+
|
||||
cmp from slices
|
||||
compress/flate from compress/gzip+
|
||||
compress/gzip from net/http
|
||||
compress/gzip from net/http+
|
||||
compress/zlib from image/png+
|
||||
container/list from crypto/tls+
|
||||
context from crypto/tls+
|
||||
|
@ -76,6 +76,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
L github.com/aws/smithy-go/transport/http/internal/io from github.com/aws/smithy-go/transport/http
|
||||
L github.com/aws/smithy-go/waiter from github.com/aws/aws-sdk-go-v2/service/ssm
|
||||
L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw
|
||||
L github.com/coreos/go-systemd/v22/dbus from tailscale.com/clientupdate
|
||||
LD 💣 github.com/creack/pty from tailscale.com/ssh/tailssh
|
||||
W 💣 github.com/dblohm7/wingoes from github.com/dblohm7/wingoes/com+
|
||||
W 💣 github.com/dblohm7/wingoes/com from tailscale.com/cmd/tailscaled+
|
||||
@ -413,6 +414,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
golang.org/x/text/unicode/bidi from golang.org/x/net/idna+
|
||||
golang.org/x/text/unicode/norm from golang.org/x/net/idna
|
||||
golang.org/x/time/rate from gvisor.dev/gvisor/pkg/tcpip/stack+
|
||||
archive/tar from tailscale.com/clientupdate
|
||||
bufio from compress/flate+
|
||||
bytes from bufio+
|
||||
cmp from slices
|
||||
|
3
go.mod
3
go.mod
@ -16,6 +16,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.36.3
|
||||
github.com/coreos/go-iptables v0.6.0
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||
github.com/coreos/go-systemd/v22 v22.4.0
|
||||
github.com/creack/pty v1.1.18
|
||||
github.com/dave/jennifer v1.6.1
|
||||
github.com/dblohm7/wingoes v0.0.0-20230821191801-fc76608aecf0
|
||||
@ -26,7 +27,7 @@ require (
|
||||
github.com/go-json-experiment/json v0.0.0-20230321051131-ccbac49a6929
|
||||
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/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
|
||||
github.com/golangci/golangci-lint v1.52.2
|
||||
github.com/google/go-cmp v0.5.9
|
||||
|
8
go.sum
8
go.sum
@ -208,6 +208,8 @@ github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo
|
||||
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.4.0 h1:y9YHcjnjynCd/DVbg5j9L/33jQM3MxJlbj/zWskzfGU=
|
||||
github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
@ -356,8 +358,9 @@ github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg=
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@ -1178,6 +1181,7 @@ golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
Loading…
Reference in New Issue
Block a user