mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-29 12:09:59 +00:00
Compare commits
1 Commits
dependabot
...
client-jso
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2fd1bb0a8 |
@@ -5,6 +5,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -22,15 +23,21 @@ var serviceCmd = &cobra.Command{
|
|||||||
Short: "Manage the NetBird daemon service",
|
Short: "Manage the NetBird daemon service",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultJSONSocket = "unix:///var/run/netbird-http.sock"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
serviceName string
|
serviceName string
|
||||||
serviceEnvVars []string
|
serviceEnvVars []string
|
||||||
|
jsonSocket string
|
||||||
|
jsonSocketDisabled bool
|
||||||
)
|
)
|
||||||
|
|
||||||
type program struct {
|
type program struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
serv *grpc.Server
|
serv *grpc.Server
|
||||||
|
jsonServ *http.Server
|
||||||
|
jsonServMu sync.Mutex
|
||||||
serverInstance *server.Server
|
serverInstance *server.Server
|
||||||
serverInstanceMu sync.Mutex
|
serverInstanceMu sync.Mutex
|
||||||
}
|
}
|
||||||
@@ -46,6 +53,8 @@ func init() {
|
|||||||
serviceCmd.PersistentFlags().BoolVar(&updateSettingsDisabled, "disable-update-settings", false, "Disables update settings feature. If enabled, the client will not be able to change or edit any settings. To persist this setting, use: netbird service install --disable-update-settings")
|
serviceCmd.PersistentFlags().BoolVar(&updateSettingsDisabled, "disable-update-settings", false, "Disables update settings feature. If enabled, the client will not be able to change or edit any settings. To persist this setting, use: netbird service install --disable-update-settings")
|
||||||
serviceCmd.PersistentFlags().BoolVar(&captureEnabled, "enable-capture", false, "Enables packet capture via 'netbird debug capture'. To persist, use: netbird service install --enable-capture")
|
serviceCmd.PersistentFlags().BoolVar(&captureEnabled, "enable-capture", false, "Enables packet capture via 'netbird debug capture'. To persist, use: netbird service install --enable-capture")
|
||||||
serviceCmd.PersistentFlags().BoolVar(&networksDisabled, "disable-networks", false, "Disables network selection. If enabled, the client will not allow listing, selecting, or deselecting networks. To persist, use: netbird service install --disable-networks")
|
serviceCmd.PersistentFlags().BoolVar(&networksDisabled, "disable-networks", false, "Disables network selection. If enabled, the client will not allow listing, selecting, or deselecting networks. To persist, use: netbird service install --disable-networks")
|
||||||
|
serviceCmd.PersistentFlags().StringVar(&jsonSocket, "json-socket", defaultJSONSocket, "HTTP/JSON API socket address served by grpc-gateway [unix|tcp]://[path|host:port]. To persist, use: netbird service install --json-socket")
|
||||||
|
serviceCmd.PersistentFlags().BoolVar(&jsonSocketDisabled, "disable-json-socket", false, "Disables the HTTP/JSON API socket. To persist, use: netbird service install --disable-json-socket")
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&serviceName, "service", "s", defaultServiceName, "Netbird system service name")
|
rootCmd.PersistentFlags().StringVarP(&serviceName, "service", "s", defaultServiceName, "Netbird system service name")
|
||||||
serviceEnvDesc := `Sets extra environment variables for the service. ` +
|
serviceEnvDesc := `Sets extra environment variables for the service. ` +
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
@@ -32,31 +29,35 @@ func (p *program) Start(svc service.Service) error {
|
|||||||
// in any case, even if configuration does not exists we run daemon to serve CLI gRPC API.
|
// in any case, even if configuration does not exists we run daemon to serve CLI gRPC API.
|
||||||
p.serv = grpc.NewServer()
|
p.serv = grpc.NewServer()
|
||||||
|
|
||||||
split := strings.Split(daemonAddr, "://")
|
daemonListener, err := listenOnAddress(daemonAddr)
|
||||||
switch split[0] {
|
|
||||||
case "unix":
|
|
||||||
// cleanup failed close
|
|
||||||
stat, err := os.Stat(split[1])
|
|
||||||
if err == nil && !stat.IsDir() {
|
|
||||||
if err := os.Remove(split[1]); err != nil {
|
|
||||||
log.Debugf("remove socket file: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "tcp":
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported daemon address protocol: %v", split[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
listen, err := net.Listen(split[0], split[1])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("listen daemon interface: %w", err)
|
return fmt.Errorf("listen daemon interface: %w", err)
|
||||||
}
|
}
|
||||||
go func() {
|
|
||||||
defer listen.Close()
|
|
||||||
|
|
||||||
if split[0] == "unix" {
|
var jsonListener *socketListener
|
||||||
if err := os.Chmod(split[1], 0666); err != nil {
|
if !jsonSocketDisabled {
|
||||||
log.Errorf("failed setting daemon permissions: %v", split[1])
|
jsonListener, err = listenOnAddress(jsonSocket)
|
||||||
|
if err != nil {
|
||||||
|
_ = daemonListener.Close()
|
||||||
|
return fmt.Errorf("listen daemon JSON interface: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
removeStaleUnixSocketForAddress(jsonSocket)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer daemonListener.Close()
|
||||||
|
if jsonListener != nil {
|
||||||
|
defer jsonListener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := daemonListener.chmodUnixSocket("daemon"); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if jsonListener != nil {
|
||||||
|
if err := jsonListener.chmodUnixSocket("daemon JSON"); err != nil {
|
||||||
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,8 +72,16 @@ func (p *program) Start(svc service.Service) error {
|
|||||||
p.serverInstance = serverInstance
|
p.serverInstance = serverInstance
|
||||||
p.serverInstanceMu.Unlock()
|
p.serverInstanceMu.Unlock()
|
||||||
|
|
||||||
log.Printf("started daemon server: %v", split[1])
|
if jsonListener != nil {
|
||||||
if err := p.serv.Serve(listen); err != nil {
|
if err := p.startJSONGateway(jsonListener, daemonAddr); err != nil {
|
||||||
|
log.Fatalf("failed to start daemon JSON server: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debug("daemon JSON socket disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("started daemon server: %v", daemonListener.address)
|
||||||
|
if err := p.serv.Serve(daemonListener.Listener); err != nil {
|
||||||
log.Errorf("failed to serve daemon requests: %v", err)
|
log.Errorf("failed to serve daemon requests: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -92,6 +101,20 @@ func (p *program) Stop(srv service.Service) error {
|
|||||||
|
|
||||||
p.cancel()
|
p.cancel()
|
||||||
|
|
||||||
|
p.jsonServMu.Lock()
|
||||||
|
jsonServ := p.jsonServ
|
||||||
|
p.jsonServMu.Unlock()
|
||||||
|
if jsonServ != nil {
|
||||||
|
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
if err := jsonServ.Shutdown(shutdownCtx); err != nil {
|
||||||
|
log.Errorf("failed to stop daemon JSON server gracefully: %v", err)
|
||||||
|
if err := jsonServ.Close(); err != nil {
|
||||||
|
log.Errorf("failed to close daemon JSON server: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shutdownCancel()
|
||||||
|
}
|
||||||
|
|
||||||
if p.serv != nil {
|
if p.serv != nil {
|
||||||
p.serv.Stop()
|
p.serv.Stop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ func buildServiceArguments() []string {
|
|||||||
args = append(args, "--disable-networks")
|
args = append(args, "--disable-networks")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args = append(args, "--json-socket", jsonSocket)
|
||||||
|
if jsonSocketDisabled {
|
||||||
|
args = append(args, "--disable-json-socket")
|
||||||
|
}
|
||||||
|
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
52
client/cmd/service_json_gateway.go
Normal file
52
client/cmd/service_json_gateway.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
//go:build !ios && !android
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func grpcGatewayEndpoint(addr string) string {
|
||||||
|
return strings.TrimPrefix(addr, "tcp://")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) startJSONGateway(jsonListener *socketListener, daemonEndpoint string) error {
|
||||||
|
mux := runtime.NewServeMux()
|
||||||
|
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
|
||||||
|
if err := proto.RegisterDaemonServiceHandlerFromEndpoint(p.ctx, mux, grpcGatewayEndpoint(daemonEndpoint), opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonServer := &http.Server{
|
||||||
|
Handler: mux,
|
||||||
|
ReadHeaderTimeout: 5 * time.Second,
|
||||||
|
BaseContext: func(net.Listener) context.Context {
|
||||||
|
return p.ctx
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
p.jsonServMu.Lock()
|
||||||
|
p.jsonServ = jsonServer
|
||||||
|
p.jsonServMu.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Printf("started daemon JSON server: %v", jsonListener.address)
|
||||||
|
if err := jsonServer.Serve(jsonListener.Listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Errorf("failed to serve daemon JSON requests: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ const serviceParamsFile = "service.json"
|
|||||||
type serviceParams struct {
|
type serviceParams struct {
|
||||||
LogLevel string `json:"log_level"`
|
LogLevel string `json:"log_level"`
|
||||||
DaemonAddr string `json:"daemon_addr"`
|
DaemonAddr string `json:"daemon_addr"`
|
||||||
|
JSONSocket string `json:"json_socket"`
|
||||||
ManagementURL string `json:"management_url,omitempty"`
|
ManagementURL string `json:"management_url,omitempty"`
|
||||||
ConfigPath string `json:"config_path,omitempty"`
|
ConfigPath string `json:"config_path,omitempty"`
|
||||||
LogFiles []string `json:"log_files,omitempty"`
|
LogFiles []string `json:"log_files,omitempty"`
|
||||||
@@ -30,6 +31,7 @@ type serviceParams struct {
|
|||||||
DisableUpdateSettings bool `json:"disable_update_settings,omitempty"`
|
DisableUpdateSettings bool `json:"disable_update_settings,omitempty"`
|
||||||
EnableCapture bool `json:"enable_capture,omitempty"`
|
EnableCapture bool `json:"enable_capture,omitempty"`
|
||||||
DisableNetworks bool `json:"disable_networks,omitempty"`
|
DisableNetworks bool `json:"disable_networks,omitempty"`
|
||||||
|
DisableJSONSocket bool `json:"disable_json_socket,omitempty"`
|
||||||
ServiceEnvVars map[string]string `json:"service_env_vars,omitempty"`
|
ServiceEnvVars map[string]string `json:"service_env_vars,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +77,7 @@ func currentServiceParams() *serviceParams {
|
|||||||
params := &serviceParams{
|
params := &serviceParams{
|
||||||
LogLevel: logLevel,
|
LogLevel: logLevel,
|
||||||
DaemonAddr: daemonAddr,
|
DaemonAddr: daemonAddr,
|
||||||
|
JSONSocket: jsonSocket,
|
||||||
ManagementURL: managementURL,
|
ManagementURL: managementURL,
|
||||||
ConfigPath: configPath,
|
ConfigPath: configPath,
|
||||||
LogFiles: logFiles,
|
LogFiles: logFiles,
|
||||||
@@ -82,6 +85,7 @@ func currentServiceParams() *serviceParams {
|
|||||||
DisableUpdateSettings: updateSettingsDisabled,
|
DisableUpdateSettings: updateSettingsDisabled,
|
||||||
EnableCapture: captureEnabled,
|
EnableCapture: captureEnabled,
|
||||||
DisableNetworks: networksDisabled,
|
DisableNetworks: networksDisabled,
|
||||||
|
DisableJSONSocket: jsonSocketDisabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(serviceEnvVars) > 0 {
|
if len(serviceEnvVars) > 0 {
|
||||||
@@ -113,9 +117,8 @@ func applyServiceParams(cmd *cobra.Command, params *serviceParams) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// For fields with non-empty defaults (log-level, daemon-addr), keep the
|
// For fields with non-empty defaults, keep the != "" guard so that an older
|
||||||
// != "" guard so that an older service.json missing the field doesn't
|
// service.json missing the field doesn't clobber the default with an empty string.
|
||||||
// clobber the default with an empty string.
|
|
||||||
if !rootCmd.PersistentFlags().Changed("log-level") && params.LogLevel != "" {
|
if !rootCmd.PersistentFlags().Changed("log-level") && params.LogLevel != "" {
|
||||||
logLevel = params.LogLevel
|
logLevel = params.LogLevel
|
||||||
}
|
}
|
||||||
@@ -124,6 +127,20 @@ func applyServiceParams(cmd *cobra.Command, params *serviceParams) {
|
|||||||
daemonAddr = params.DaemonAddr
|
daemonAddr = params.DaemonAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jsonSocketChanged := serviceCmd.PersistentFlags().Changed("json-socket")
|
||||||
|
if !jsonSocketChanged && params.JSONSocket != "" {
|
||||||
|
jsonSocket = params.JSONSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
if !serviceCmd.PersistentFlags().Changed("disable-json-socket") {
|
||||||
|
jsonSocketDisabled = params.DisableJSONSocket
|
||||||
|
// Passing --json-socket should re-enable the JSON gateway unless
|
||||||
|
// --disable-json-socket was explicitly provided too.
|
||||||
|
if jsonSocketChanged {
|
||||||
|
jsonSocketDisabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For optional fields where empty means "use default", always apply so
|
// For optional fields where empty means "use default", always apply so
|
||||||
// that an explicit clear (--management-url "") persists across reinstalls.
|
// that an explicit clear (--management-url "") persists across reinstalls.
|
||||||
if !rootCmd.PersistentFlags().Changed("management-url") {
|
if !rootCmd.PersistentFlags().Changed("management-url") {
|
||||||
|
|||||||
@@ -530,6 +530,7 @@ func fieldToGlobalVar(field string) string {
|
|||||||
m := map[string]string{
|
m := map[string]string{
|
||||||
"LogLevel": "logLevel",
|
"LogLevel": "logLevel",
|
||||||
"DaemonAddr": "daemonAddr",
|
"DaemonAddr": "daemonAddr",
|
||||||
|
"JSONSocket": "jsonSocket",
|
||||||
"ManagementURL": "managementURL",
|
"ManagementURL": "managementURL",
|
||||||
"ConfigPath": "configPath",
|
"ConfigPath": "configPath",
|
||||||
"LogFiles": "logFiles",
|
"LogFiles": "logFiles",
|
||||||
@@ -537,6 +538,7 @@ func fieldToGlobalVar(field string) string {
|
|||||||
"DisableUpdateSettings": "updateSettingsDisabled",
|
"DisableUpdateSettings": "updateSettingsDisabled",
|
||||||
"EnableCapture": "captureEnabled",
|
"EnableCapture": "captureEnabled",
|
||||||
"DisableNetworks": "networksDisabled",
|
"DisableNetworks": "networksDisabled",
|
||||||
|
"DisableJSONSocket": "jsonSocketDisabled",
|
||||||
"ServiceEnvVars": "serviceEnvVars",
|
"ServiceEnvVars": "serviceEnvVars",
|
||||||
}
|
}
|
||||||
if v, ok := m[field]; ok {
|
if v, ok := m[field]; ok {
|
||||||
|
|||||||
83
client/cmd/service_socket.go
Normal file
83
client/cmd/service_socket.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
//go:build !ios && !android
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type socketListener struct {
|
||||||
|
net.Listener
|
||||||
|
network string
|
||||||
|
address string
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenOnAddress(addr string) (*socketListener, error) {
|
||||||
|
network, address, err := parseListenAddress(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if network == "unix" {
|
||||||
|
removeStaleUnixSocket(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err := net.Listen(network, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &socketListener{Listener: listener, network: network, address: address}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseListenAddress(addr string) (string, string, error) {
|
||||||
|
network, address, ok := strings.Cut(addr, "://")
|
||||||
|
if !ok || network == "" || address == "" {
|
||||||
|
return "", "", fmt.Errorf("address must be in [unix|tcp]://[path|host:port] format: %q", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch network {
|
||||||
|
case "unix", "tcp":
|
||||||
|
return network, address, nil
|
||||||
|
default:
|
||||||
|
return "", "", fmt.Errorf("unsupported daemon address protocol: %v", network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeStaleUnixSocket(path string) {
|
||||||
|
stat, err := os.Stat(path)
|
||||||
|
if err == nil && !stat.IsDir() {
|
||||||
|
if err := os.Remove(path); err != nil {
|
||||||
|
log.Debugf("remove socket file: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
log.Debugf("stat socket file: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeStaleUnixSocketForAddress(addr string) {
|
||||||
|
network, address, err := parseListenAddress(addr)
|
||||||
|
if err != nil || network != "unix" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
removeStaleUnixSocket(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *socketListener) chmodUnixSocket(description string) error {
|
||||||
|
if l == nil || l.network != "unix" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Chmod(l.address, 0666); err != nil {
|
||||||
|
return fmt.Errorf("failed setting %s permissions for %s: %w", description, l.address, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
2497
client/proto/daemon.pb.gw.go
Normal file
2497
client/proto/daemon.pb.gw.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,5 +13,11 @@ script_path=$(dirname $(realpath "$0"))
|
|||||||
cd "$script_path"
|
cd "$script_path"
|
||||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.6
|
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.6
|
||||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
|
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
|
||||||
protoc -I ./ ./daemon.proto --go_out=../ --go-grpc_out=../ --experimental_allow_proto3_optional
|
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@v2.26.3
|
||||||
|
protoc -I ./ ./daemon.proto \
|
||||||
|
--go_out=../ \
|
||||||
|
--go-grpc_out=../ \
|
||||||
|
--grpc-gateway_out=../ \
|
||||||
|
--grpc-gateway_opt=generate_unbound_methods=true \
|
||||||
|
--experimental_allow_proto3_optional
|
||||||
cd "$old_pwd"
|
cd "$old_pwd"
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -62,6 +62,7 @@ require (
|
|||||||
github.com/google/nftables v0.3.0
|
github.com/google/nftables v0.3.0
|
||||||
github.com/gopacket/gopacket v1.1.1
|
github.com/gopacket/gopacket v1.1.1
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240212192251-757544f21357
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240212192251-757544f21357
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2
|
||||||
github.com/hashicorp/go-version v1.7.0
|
github.com/hashicorp/go-version v1.7.0
|
||||||
@@ -324,6 +325,7 @@ require (
|
|||||||
golang.org/x/text v0.36.0 // indirect
|
golang.org/x/text v0.36.0 // indirect
|
||||||
golang.org/x/tools v0.43.0 // indirect
|
golang.org/x/tools v0.43.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
|
|||||||
Reference in New Issue
Block a user