mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-28 00:42:37 -04:00
Add elevation for dangerous ssh flags
This commit is contained in:
58
client/cmd/elevate.go
Normal file
58
client/cmd/elevate.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// SetSSHConfigCmdName is the hidden subcommand an elevated process runs to apply
|
||||
// the privileged SSH settings.
|
||||
const SetSSHConfigCmdName = "set-ssh-config"
|
||||
|
||||
// sshConfigElevated is set by runInDaemonMode once the dangerous SSH settings
|
||||
// have been applied by an elevated helper, so the (unprivileged) up flow does
|
||||
// not re-send them and re-trip the daemon gate.
|
||||
var sshConfigElevated bool
|
||||
|
||||
// wantsDangerousSSH reports whether this invocation is trying to ENABLE SSH root
|
||||
// login or DISABLE SSH authentication.
|
||||
func wantsDangerousSSH(cmd *cobra.Command) bool {
|
||||
return (cmd.Flags().Changed(enableSSHRootFlag) && enableSSHRoot) ||
|
||||
(cmd.Flags().Changed(disableSSHAuthFlag) && disableSSHAuth)
|
||||
}
|
||||
|
||||
// buildSetSSHConfigArgs builds the argument list for an elevated
|
||||
// `netbird set-ssh-config` invocation.
|
||||
func buildSetSSHConfigArgs(profileName, username string, enableSSHRoot, disableSSHAuth *bool, daemonAddr string) []string {
|
||||
args := []string{SetSSHConfigCmdName}
|
||||
if profileName != "" {
|
||||
args = append(args, "--profile", profileName)
|
||||
}
|
||||
if username != "" {
|
||||
args = append(args, "--username", username)
|
||||
}
|
||||
if enableSSHRoot != nil && *enableSSHRoot {
|
||||
args = append(args, "--"+enableSSHRootFlag)
|
||||
}
|
||||
if disableSSHAuth != nil && *disableSSHAuth {
|
||||
args = append(args, "--"+disableSSHAuthFlag)
|
||||
}
|
||||
if daemonAddr != "" {
|
||||
args = append(args, "--daemon-addr", daemonAddr)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// ElevateSSHConfig re-runs the current executable's `set-ssh-config` command with
|
||||
// root/administrator privileges via the platform's prompt (pkexec/UAC/osascript),
|
||||
// so the elevated process connects to the daemon with a privileged identity and
|
||||
// the daemon's requirePrivilegedForDangerousSSH gate passes.
|
||||
func ElevateSSHConfig(profileName, username string, enableSSHRoot, disableSSHAuth *bool) error {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return fmt.Errorf("locate executable for elevation: %w", err)
|
||||
}
|
||||
return runElevated(exe, buildSetSSHConfigArgs(profileName, username, enableSSHRoot, disableSSHAuth, daemonAddr))
|
||||
}
|
||||
16
client/cmd/elevate_darwin.go
Normal file
16
client/cmd/elevate_darwin.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build darwin
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// isProcessPrivileged reports whether the current process runs as root.
|
||||
func isProcessPrivileged() bool { return os.Geteuid() == 0 }
|
||||
|
||||
// TODO(ssh-elevation): implement the osascript admin prompt.
|
||||
func runElevated(_ string, _ []string) error {
|
||||
return fmt.Errorf("automatic privilege elevation is not yet implemented on macOS, re-run with sudo")
|
||||
}
|
||||
38
client/cmd/elevate_linux.go
Normal file
38
client/cmd/elevate_linux.go
Normal file
@@ -0,0 +1,38 @@
|
||||
//go:build linux
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// isProcessPrivileged reports whether the current process runs as root.
|
||||
func isProcessPrivileged() bool { return os.Geteuid() == 0 }
|
||||
|
||||
// runElevated re-runs exe with args as root. On a graphical session it uses
|
||||
// pkexec, which drives the desktop's polkit authentication agent (GUI prompt).
|
||||
func runElevated(exe string, args []string) error {
|
||||
if !hasGraphicalSession() {
|
||||
return fmt.Errorf("cannot request privilege elevation without a graphical session. re-run as root: sudo %s %s", exe, strings.Join(args, " "))
|
||||
}
|
||||
pkexec, err := exec.LookPath("pkexec")
|
||||
if err != nil {
|
||||
return fmt.Errorf("pkexec not found for privilege elevation, re-run as root: sudo %s %s", exe, strings.Join(args, " "))
|
||||
}
|
||||
|
||||
c := exec.Command(pkexec, append([]string{exe}, args...)...)
|
||||
c.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
if err := c.Run(); err != nil {
|
||||
return fmt.Errorf("elevated %s failed (elevation cancelled or denied?): %w", SetSSHConfigCmdName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasGraphicalSession heuristically reports whether a desktop session is present
|
||||
// that polkit can prompt in.
|
||||
func hasGraphicalSession() bool {
|
||||
return os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != ""
|
||||
}
|
||||
17
client/cmd/elevate_other.go
Normal file
17
client/cmd/elevate_other.go
Normal file
@@ -0,0 +1,17 @@
|
||||
//go:build !linux && !darwin && !windows
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// isProcessPrivileged reports whether the current process runs as root.
|
||||
func isProcessPrivileged() bool { return os.Geteuid() == 0 }
|
||||
|
||||
// runElevated has no automatic elevation mechanism on these platforms
|
||||
func runElevated(exe string, args []string) error {
|
||||
return fmt.Errorf("automatic privilege elevation is not supported on this platform, re-run as root: sudo %s %s", exe, strings.Join(args, " "))
|
||||
}
|
||||
99
client/cmd/elevate_test.go
Normal file
99
client/cmd/elevate_test.go
Normal file
@@ -0,0 +1,99 @@
|
||||
//go:build !ios && !android
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBuildSetSSHConfigArgs(t *testing.T) {
|
||||
tr, fa := true, false
|
||||
|
||||
t.Run("enable root only, with daemon-addr", func(t *testing.T) {
|
||||
got := buildSetSSHConfigArgs("prof", "alice", &tr, nil, "unix:///x.sock")
|
||||
assert.Equal(t, []string{
|
||||
SetSSHConfigCmdName, "--profile", "prof", "--username", "alice",
|
||||
"--" + enableSSHRootFlag, "--daemon-addr", "unix:///x.sock",
|
||||
}, got)
|
||||
})
|
||||
|
||||
t.Run("disable auth only, no daemon-addr", func(t *testing.T) {
|
||||
got := buildSetSSHConfigArgs("prof", "alice", nil, &tr, "")
|
||||
assert.Equal(t, []string{
|
||||
SetSSHConfigCmdName, "--profile", "prof", "--username", "alice",
|
||||
"--" + disableSSHAuthFlag,
|
||||
}, got)
|
||||
})
|
||||
|
||||
t.Run("false pointers omit the flags", func(t *testing.T) {
|
||||
got := buildSetSSHConfigArgs("", "", &fa, &fa, "")
|
||||
assert.Equal(t, []string{SetSSHConfigCmdName}, got)
|
||||
})
|
||||
|
||||
t.Run("both enabled", func(t *testing.T) {
|
||||
got := buildSetSSHConfigArgs("p", "u", &tr, &tr, "")
|
||||
assert.Equal(t, []string{
|
||||
SetSSHConfigCmdName, "--profile", "p", "--username", "u",
|
||||
"--" + enableSSHRootFlag, "--" + disableSSHAuthFlag,
|
||||
}, got)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildSetSSHConfigRequest(t *testing.T) {
|
||||
tr := true
|
||||
|
||||
req := buildSetSSHConfigRequest("p", "u", &tr, nil)
|
||||
assert.Equal(t, "p", req.ProfileName)
|
||||
assert.Equal(t, "u", req.Username)
|
||||
if assert.NotNil(t, req.EnableSSHRoot) {
|
||||
assert.True(t, *req.EnableSSHRoot)
|
||||
}
|
||||
assert.Nil(t, req.DisableSSHAuth, "an unset flag must leave the daemon value untouched")
|
||||
}
|
||||
|
||||
func TestWantsDangerousSSH(t *testing.T) {
|
||||
origRoot, origAuth := enableSSHRoot, disableSSHAuth
|
||||
t.Cleanup(func() { enableSSHRoot, disableSSHAuth = origRoot, origAuth })
|
||||
|
||||
newCmd := func() *cobra.Command {
|
||||
enableSSHRoot, disableSSHAuth = false, false
|
||||
c := &cobra.Command{Use: "x"}
|
||||
c.Flags().BoolVar(&enableSSHRoot, enableSSHRootFlag, false, "")
|
||||
c.Flags().BoolVar(&disableSSHAuth, disableSSHAuthFlag, false, "")
|
||||
return c
|
||||
}
|
||||
|
||||
// wantsDangerousSSH fires only in the privileged direction.
|
||||
t.Run("enable root true is dangerous", func(t *testing.T) {
|
||||
c := newCmd()
|
||||
require.NoError(t, c.Flags().Set(enableSSHRootFlag, "true"))
|
||||
assert.True(t, wantsDangerousSSH(c))
|
||||
})
|
||||
|
||||
t.Run("enable root false is not dangerous", func(t *testing.T) {
|
||||
c := newCmd()
|
||||
require.NoError(t, c.Flags().Set(enableSSHRootFlag, "false"))
|
||||
assert.False(t, wantsDangerousSSH(c))
|
||||
})
|
||||
|
||||
t.Run("disable auth true is dangerous", func(t *testing.T) {
|
||||
c := newCmd()
|
||||
require.NoError(t, c.Flags().Set(disableSSHAuthFlag, "true"))
|
||||
assert.True(t, wantsDangerousSSH(c))
|
||||
})
|
||||
|
||||
t.Run("disable auth false is not dangerous", func(t *testing.T) {
|
||||
c := newCmd()
|
||||
require.NoError(t, c.Flags().Set(disableSSHAuthFlag, "false"))
|
||||
assert.False(t, wantsDangerousSSH(c))
|
||||
})
|
||||
|
||||
t.Run("nothing changed is not dangerous", func(t *testing.T) {
|
||||
c := newCmd()
|
||||
assert.False(t, wantsDangerousSSH(c))
|
||||
})
|
||||
}
|
||||
23
client/cmd/elevate_windows.go
Normal file
23
client/cmd/elevate_windows.go
Normal file
@@ -0,0 +1,23 @@
|
||||
//go:build windows
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// isProcessPrivileged reports whether the current process token is elevated
|
||||
// (running as administrator / LocalSystem).
|
||||
func isProcessPrivileged() bool {
|
||||
return windows.GetCurrentProcessToken().IsElevated()
|
||||
}
|
||||
|
||||
// runElevated should re-run exe with args elevated via a UAC prompt
|
||||
// (ShellExecuteEx with the "runas" verb).
|
||||
//
|
||||
// TODO(ssh-elevation): implement ShellExecuteEx("runas") + wait for the child.
|
||||
func runElevated(_ string, _ []string) error {
|
||||
return fmt.Errorf("automatic privilege elevation is not yet implemented on Windows, re-run netbird as administrator")
|
||||
}
|
||||
@@ -176,6 +176,8 @@ func init() {
|
||||
rootCmd.AddCommand(ownerCmd)
|
||||
ownerCmd.AddCommand(ownerAddCmd, ownerResetCmd, ownerShareCmd, ownerUnshareCmd)
|
||||
|
||||
rootCmd.AddCommand(setSSHConfigCmd)
|
||||
|
||||
networksCMD.AddCommand(routesListCmd)
|
||||
networksCMD.AddCommand(routesSelectCmd, routesDeselectCmd)
|
||||
|
||||
|
||||
73
client/cmd/ssh_config_cmd.go
Normal file
73
client/cmd/ssh_config_cmd.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
setSSHConfigProfile string
|
||||
setSSHConfigUsername string
|
||||
setSSHConfigEnableRoot bool
|
||||
setSSHConfigDisableAuth bool
|
||||
)
|
||||
|
||||
// setSSHConfigCmd applies the privileged SSH settings (enable root login /
|
||||
// disable auth) to a profile over the daemon IPC. Hidden because users
|
||||
// interact via `netbird up` / the UI, not directly.
|
||||
var setSSHConfigCmd = &cobra.Command{
|
||||
Use: SetSSHConfigCmdName,
|
||||
Short: "Apply privileged SSH settings to a profile (internal elevation target)",
|
||||
Hidden: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
ctx := internal.CtxInitState(cmd.Context())
|
||||
|
||||
conn, err := DialClientGRPCServer(ctx, daemonAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("connect to daemon: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if cerr := conn.Close(); cerr != nil {
|
||||
log.Warnf("failed closing daemon gRPC client connection: %v", cerr)
|
||||
}
|
||||
}()
|
||||
|
||||
var enableRoot, disableAuth *bool
|
||||
if cmd.Flags().Changed(enableSSHRootFlag) {
|
||||
enableRoot = &setSSHConfigEnableRoot
|
||||
}
|
||||
if cmd.Flags().Changed(disableSSHAuthFlag) {
|
||||
disableAuth = &setSSHConfigDisableAuth
|
||||
}
|
||||
|
||||
req := buildSetSSHConfigRequest(setSSHConfigProfile, setSSHConfigUsername, enableRoot, disableAuth)
|
||||
if _, err := proto.NewDaemonServiceClient(conn).SetConfig(ctx, req); err != nil {
|
||||
return fmt.Errorf("apply SSH config: %w", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// buildSetSSHConfigRequest builds a minimal SetConfigRequest that touches only
|
||||
// the SSH fields that were provided (nil pointers leave the daemon's stored
|
||||
// value untouched, matching setupSetConfigReq's pointer semantics).
|
||||
func buildSetSSHConfigRequest(profileName, username string, enableSSHRoot, disableSSHAuth *bool) *proto.SetConfigRequest {
|
||||
return &proto.SetConfigRequest{
|
||||
ProfileName: profileName,
|
||||
Username: username,
|
||||
EnableSSHRoot: enableSSHRoot,
|
||||
DisableSSHAuth: disableSSHAuth,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
setSSHConfigCmd.Flags().StringVar(&setSSHConfigProfile, "profile", "", "profile name to apply the SSH settings to")
|
||||
setSSHConfigCmd.Flags().StringVar(&setSSHConfigUsername, "username", "", "owning username of the profile")
|
||||
setSSHConfigCmd.Flags().BoolVar(&setSSHConfigEnableRoot, enableSSHRootFlag, false, "enable root login for the SSH server")
|
||||
setSSHConfigCmd.Flags().BoolVar(&setSSHConfigDisableAuth, disableSSHAuthFlag, false, "disable SSH authentication")
|
||||
}
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/profilemanager"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
nbnet "github.com/netbirdio/netbird/client/net"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
"github.com/netbirdio/netbird/client/server"
|
||||
"github.com/netbirdio/netbird/client/system"
|
||||
"github.com/netbirdio/netbird/shared/management/domain"
|
||||
@@ -321,6 +321,24 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command, pm *profilemanager
|
||||
return fmt.Errorf("get current user: %v", err)
|
||||
}
|
||||
|
||||
// Enabling SSH root login / disabling SSH auth is a privileged change the
|
||||
// daemon rejects from a non-privileged caller. Offer to elevate (polkit/UAC/
|
||||
// osascript) so an elevated helper applies just those settings.
|
||||
if wantsDangerousSSH(cmd) && !isProcessPrivileged() {
|
||||
cmd.Println("Enabling SSH root login / disabling SSH authentication requires administrator privileges, requesting elevation...")
|
||||
var enableRoot, disableAuth *bool
|
||||
if cmd.Flag(enableSSHRootFlag).Changed {
|
||||
enableRoot = &enableSSHRoot
|
||||
}
|
||||
if cmd.Flag(disableSSHAuthFlag).Changed {
|
||||
disableAuth = &disableSSHAuth
|
||||
}
|
||||
if err := ElevateSSHConfig(activeProf.ID.String(), username.Username, enableRoot, disableAuth); err != nil {
|
||||
return fmt.Errorf("apply privileged SSH settings: %w", err)
|
||||
}
|
||||
sshConfigElevated = true
|
||||
}
|
||||
|
||||
// set the new config
|
||||
req := setupSetConfigReq(customDNSAddressConverted, cmd, activeProf.ID.String(), username.Username)
|
||||
if _, err := client.SetConfig(ctx, req); err != nil {
|
||||
@@ -424,7 +442,7 @@ func setupSetConfigReq(customDNSAddressConverted []byte, cmd *cobra.Command, pro
|
||||
if cmd.Flag(serverSSHAllowedFlag).Changed {
|
||||
req.ServerSSHAllowed = &serverSSHAllowed
|
||||
}
|
||||
if cmd.Flag(enableSSHRootFlag).Changed {
|
||||
if cmd.Flag(enableSSHRootFlag).Changed && !sshConfigElevated {
|
||||
req.EnableSSHRoot = &enableSSHRoot
|
||||
}
|
||||
if cmd.Flag(enableSSHSFTPFlag).Changed {
|
||||
@@ -436,7 +454,7 @@ func setupSetConfigReq(customDNSAddressConverted []byte, cmd *cobra.Command, pro
|
||||
if cmd.Flag(enableSSHRemotePortForwardFlag).Changed {
|
||||
req.EnableSSHRemotePortForwarding = &enableSSHRemotePortForward
|
||||
}
|
||||
if cmd.Flag(disableSSHAuthFlag).Changed {
|
||||
if cmd.Flag(disableSSHAuthFlag).Changed && !sshConfigElevated {
|
||||
req.DisableSSHAuth = &disableSSHAuth
|
||||
}
|
||||
if cmd.Flag(sshJWTCacheTTLFlag).Changed {
|
||||
@@ -652,7 +670,7 @@ func setupLoginRequest(providedSetupKey string, customDNSAddressConverted []byte
|
||||
loginRequest.ServerSSHAllowed = &serverSSHAllowed
|
||||
}
|
||||
|
||||
if cmd.Flag(enableSSHRootFlag).Changed {
|
||||
if cmd.Flag(enableSSHRootFlag).Changed && !sshConfigElevated {
|
||||
loginRequest.EnableSSHRoot = &enableSSHRoot
|
||||
}
|
||||
|
||||
@@ -668,7 +686,7 @@ func setupLoginRequest(providedSetupKey string, customDNSAddressConverted []byte
|
||||
loginRequest.EnableSSHRemotePortForwarding = &enableSSHRemotePortForward
|
||||
}
|
||||
|
||||
if cmd.Flag(disableSSHAuthFlag).Changed {
|
||||
if cmd.Flag(disableSSHAuthFlag).Changed && !sshConfigElevated {
|
||||
loginRequest.DisableSSHAuth = &disableSSHAuth
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"flag"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||
|
||||
"github.com/netbirdio/netbird/client/cmd"
|
||||
"github.com/netbirdio/netbird/client/ui/authsession"
|
||||
"github.com/netbirdio/netbird/client/ui/i18n"
|
||||
"github.com/netbirdio/netbird/client/ui/preferences"
|
||||
@@ -80,6 +82,16 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
// When re-launched under a privilege prompt (pkexec/UAC/osascript) to apply
|
||||
// the dangerous SSH settings, this binary is invoked as os.Executable().
|
||||
// Delegate to the shared CLI command and exit before starting the GUI.
|
||||
if len(os.Args) > 1 && os.Args[1] == cmd.SetSSHConfigCmdName {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
daemonAddr, userSetLogFile := parseFlagsAndInitLog()
|
||||
conn := NewConn(daemonAddr)
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/netbirdio/netbird/client/cmd"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
)
|
||||
|
||||
@@ -190,9 +194,27 @@ func (s *Settings) SetConfig(ctx context.Context, p SetConfigParams) error {
|
||||
SshJWTCacheTTL: p.SSHJWTCacheTTL,
|
||||
}
|
||||
_, err = cli.SetConfig(ctx, req)
|
||||
if err != nil && wantsDangerousSSHConfig(p) && status.Code(err) == codes.PermissionDenied {
|
||||
// The daemon restricts enabling SSH root login / disabling SSH auth to
|
||||
// root/administrator, and the UI runs as the unprivileged desktop user.
|
||||
// Elevate (polkit/UAC/osascript) so a privileged helper applies just
|
||||
// those flags.
|
||||
if elevErr := cmd.ElevateSSHConfig(p.ProfileName, p.Username, p.EnableSSHRoot, p.DisableSSHAuth); elevErr != nil {
|
||||
return elevErr
|
||||
}
|
||||
req.EnableSSHRoot = nil
|
||||
req.DisableSSHAuth = nil
|
||||
_, err = cli.SetConfig(ctx, req)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// wantsDangerousSSHConfig reports whether p tries to enable SSH root login or
|
||||
// disable SSH authentication.
|
||||
func wantsDangerousSSHConfig(p SetConfigParams) bool {
|
||||
return (p.EnableSSHRoot != nil && *p.EnableSSHRoot) || (p.DisableSSHAuth != nil && *p.DisableSSHAuth)
|
||||
}
|
||||
|
||||
func (s *Settings) GetRestrictions(ctx context.Context) (Restrictions, error) {
|
||||
cli, err := s.conn.Client()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user