-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[client, management] Add embedded VNC server #6170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
lixmal
wants to merge
164
commits into
main
Choose a base branch
from
embedded-vnc
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 150 commits
Commits
Show all changes
164 commits
Select commit
Hold shift + click to select a range
9f0aa1c
Add embedded VNC server with JWT auth and per-peer toggle
lixmal 62cf9e8
Track active VNC sessions in status and address CodeRabbit findings
lixmal 7123e6d
Fix Windows lint errcheck/unused and Linux nilerr in console VNC fall…
lixmal 9b5541d
Extract session-address anonymization helper to lower status complexity
lixmal 738c585
Guard VNC session negotiated encoding state with RWMutex
lixmal 94068d3
Drop -ac from Xvfb/Xorg invocations to keep xhost localuser grant aut…
lixmal a8541a1
Apply posture and validated-peers filtering on ResourceTypePeer polic…
lixmal 8bf13b0
Merge SSH wildcard authorized users across matching rules
lixmal fa90283
Extract wildcard user merge helper to satisfy case-clause length
lixmal d6d3fa9
Drop unused getPeerFromResource helper
lixmal 44ed0c1
Drop xclip-no-selection trace log that fires every 2s on Xvfb
lixmal cd005ef
Add CopyRect detection and emission for tile-aligned moves
lixmal 047cc95
Throttle capture-failure log to once per 5s while capturer is down
lixmal e759487
Prompt for macOS Accessibility and Screen Recording at VNC enable time
lixmal db5b6cf
Add DesktopSize, DesktopName, LastRect pseudo-encodings with resize d…
lixmal 6d937af
Drop dead Hextile and standalone Zlib encoding paths
lixmal b4f6962
Drop unused VNC DES auth path
lixmal 2bed8b6
Lock pixel format to 32bpp little-endian truecolour and reject other …
lixmal 4f884d9
Add QEMU Extended Key Event for layout-independent input
lixmal da37a28
Exclude VNC server from js, ios, and android builds
lixmal b135d46
Drop unused zlibState.scratch field
lixmal a11341f
Add ExtendedClipboard pseudo-encoding for UTF-8 bidirectional clipboard
lixmal 76add0b
Fix ExtendedClipboard auto-request by advertising all actions in Caps
lixmal 61ec8d6
Honor QualityLevel and CompressLevel pseudo-encodings
lixmal 2d0a54f
Fix golangci-lint and Sonar: drop newZlibState, extract applyEncoding…
lixmal 0b8fc5d
Split session.go: encoder pipeline and clipboard handling into separa…
lixmal ee393d0
Clamp Tight length to 22 bits and fall back to Raw on overflow
lixmal f5e1057
Latin-1 round-trip for legacy CutText and soft-fail ext clipboard errors
lixmal bfb6750
Reset encoding capability flags on each SetEncodings
lixmal 785f94d
Guard buildExtClipProvideText against oversized input
lixmal 7e7e056
Reset Tight zlib stream when deflater is recreated mid-session
lixmal 97d0a67
Release sticky modifiers and mouse buttons on client disconnect
lixmal b9f5264
Restore createRDPProxy wasm entry point for dashboard RDP
lixmal c2fdf62
Detect dead VNC peers on both ends and report session stats
lixmal 5543404
Cap honored VNC client JPEG quality at 50
lixmal bc40752
Register VNC netstack service only when netstack is active
lixmal 6bb66e0
[management] Avoid peer IP reallocation when account settings update …
lixmal d3f3e08
Avoid context cancellation in `cancelPeerRoutines` (#6175)
mlsmaycon d927ef4
Clean up legacy 32-bit and HKCU registry entries on Windows install (…
lixmal 32a5a06
[management] fix: device redirect uri wasn't registered (#6191)
jnfrati 97bc1ee
[management] Fence peer status updates with a session token (#6193)
mlsmaycon 8e2505b
[management] Add metrics for peer status updates and ephemeral cleanu…
mlsmaycon 9d189bb
Restore Hextile SolidFill and Zlib encoding paths
lixmal 24a5f22
Accept any RGB shift permutation as Tight-compatible per RFB 7.7.6
lixmal b41fbad
Surface DXGI fallback to GDI at warn level on Windows
lixmal 393c102
Throttle VNC encoder JPEG quality and skip frames under write backpre…
lixmal 5eec996
Honour client JPEG quality fully now that backpressure caps it dynami…
lixmal b3f0f53
Collapse dirty rects to their bounding box when the bbox is densely d…
lixmal 2285db2
Treat ExtendedClipboard messages with the Caps bit as Caps regardless…
lixmal fe15688
Emit Cursor pseudo-encoding on Linux, Windows, and macOS
lixmal b1b04f9
Composite remote cursor into the framebuffer when the dashboard toggl…
lixmal df9a6fb
Drop pbpaste trace log that fires whenever the macOS pasteboard is empty
lixmal 62b3611
Extract daemon-to-agent loopback proxy and token helpers into a platf…
lixmal 7d61975
Proxy macOS VNC connections from the LaunchDaemon to a per-user agent…
lixmal 5e200fa
Drop unreliable Sequoia preflight from macOS Screen Recording check
lixmal 02b9fe7
Use pixel-mode scroll on macOS for smoother wheel events
lixmal c28e41e
Track macOS click count and pixel-scale wheel scroll
lixmal 354fd00
Enable IdP JWKS refresh in VNC JWT validator
lixmal 896530f
Add ExtendedMouseButtons for back/forward mouse buttons
lixmal 517bea0
Collapse X11 DISPLAY/XAUTHORITY auto-detect logs into one line
lixmal 7e5846a
Resolve merge conflicts with main
lixmal 17359cd
Fix VNC lint, 386 atomic alignment, and Sonar code smells
lixmal 640a267
Address CodeRabbit feedback on VNC server
lixmal f37e228
Replace magic env-var and subcommand strings with named constants
lixmal b41d11b
Allow Cursor pseudo-encoding in session mode and cache last XFixes sp…
lixmal ef4ea2e
Set Fn flag on macOS navigation keycodes so the next key isn't treate…
lixmal 98d533c
Address CodeRabbit feedback on VNC server agent matching and session …
lixmal 2f4ddf0
Emit explicit Fn flagsChanged transitions around macOS navigation keys
lixmal 3d3055d
Replace VNC JWT auth with a Noise_IK handshake bound to ACL-pushed pu…
lixmal ee348ba
Abort VNC agent dial retry loop on server shutdown
lixmal 5e67feb
Address Sonar findings and move noise to direct dependency
lixmal 412193c
Address CodeRabbit VNC review feedback
lixmal 1cc5967
Address follow-up CodeRabbit VNC findings
lixmal 0f03c61
Lower CreateTemporaryAccess complexity and emit VncAuth for session p…
lixmal 030c571
Signal Zlib encode failure and fall back to Raw
lixmal 97b7b01
Fold init-only VNC and SSH setters into Config-struct constructors
lixmal c29ef63
Switch VNC daemon-to-agent IPC to Unix sockets and audit-log every co…
lixmal 8e72967
Add per-connection user-approval prompts for VNC
lixmal 8d329da
Evict orphaned packet captures and annotate VNC streams
lixmal 1f912be
Address codespell and Sonar findings on embedded-vnc
lixmal 7cb6388
Decline VNC approval early when no console user is logged in
lixmal fa57eed
Address CodeRabbit review and fix CI on embedded-vnc
lixmal f557e66
Return error from gateApproval and log at the caller
lixmal 5e2830b
Harden VNC server, IPC, and management plumbing
lixmal 4e3e3ce
Surface VNC initiator in status, clarify proxy logs, dampen capture n…
lixmal bf2fb2f
Address CodeRabbit review on embedded VNC PR
lixmal 2f67841
Reuse /var/run/netbird as VNC agent socket parent via configs.RuntimeDir
lixmal 65f302b
Authenticate virtual X11 sessions with per-session MIT-MAGIC-COOKIE-1
lixmal 3bcacff
Rename xauth_linux.go to xauth_x11.go so FreeBSD picks it up
lixmal 6cd5d60
Split prepareAgentSocketDir to reduce cognitive complexity
lixmal 6c9465d
Handle Lstat error in purgeStaleAgentSubdir
lixmal 144dfbc
Capture listener locally in accept loops to avoid nil deref on Stop
lixmal 2fdc3ae
Merge branch 'main' into embedded-vnc
lixmal f2c7920
Fix review findings for embedded VNC server
lixmal c1eecaa
Merge branch 'main' into embedded-vnc
lixmal d568084
Merge remote-tracking branch 'origin/main' into embedded-vnc
lixmal fd7bf98
Split CreateTemporaryAccess into smaller functions
lixmal dbc7b84
Merge origin/main into embedded-vnc
lixmal 02e7c0e
Port VNC settings and connection-approval prompt to the Wails UI
lixmal cec9ea8
Support IPv6 for the embedded VNC server and browser proxy
lixmal 125250c
Regenerate proto gateway and OpenAPI code with pinned tool versions
lixmal eb6e8dc
Reject empty approval request_id and verify gid after setgid
lixmal 152ba28
Add VNC allow and approval settings to MDM policy
lixmal 2b051ad
Merge main into embedded-vnc
lixmal abe8bc1
Require privilege for VNC server and approval config changes
lixmal 05a75d8
Carry netbird-vnc policies over the component network map
lixmal 0e2bd09
Build VNC auth for peers on the component network map path
lixmal 548cbfb
Drop the VNC port constant left unused by the shared types move
lixmal 1836616
Account for the VNC peer flag in the peer meta field-count guard
lixmal 8e919b4
Share one VNC session agent manager across all accept loops
lixmal abd5022
Flag active VNC sessions on the main screen
lixmal 90d2687
Push a status snapshot when VNC sessions start or end
lixmal 9c33470
Restrict the SAS event to SYSTEM and restore its registry snapshot pe…
lixmal 59c7512
Move the service agent accessor to the platforms that have one
lixmal a117c19
Treat an unreadable SoftwareSASGeneration as unknown rather than absent
lixmal 86d775f
Merge main into embedded-vnc
lixmal 3d4137a
Wait for the Windows VNC agent to respawn instead of refusing the con…
lixmal 4984e8e
Draw inverted monochrome cursor pixels so the text caret is visible
lixmal 78bc656
Fail fast when no console session exists and keep the SAS value type
lixmal fcfe643
Merge main into embedded-vnc
lixmal ef1d406
Ask for Screen Recording from the agent and Accessibility on first input
lixmal 12b18f5
Recycle the macOS VNC agent per connection so permission prompts work
lixmal a730672
Stamp modifiers on injected macOS key events so shifted input works
lixmal 4b72019
Merge branch 'main' into embedded-vnc
lixmal f7e186f
Merge branch 'main' into embedded-vnc
lixmal 90483da
Address review findings on the VNC server, session auth and capture d…
lixmal 64d808e
Split the VNC, routing and metrics flag copying out of the config req…
lixmal c804bee
Put the approval prompt's Deny and Allow buttons on one row
lixmal 8997670
Regenerate the daemon proto with the protoc version the tree was gene…
lixmal 4d7ca23
Split the status summary into per-section helpers and scope retrackCo…
lixmal 071bd94
Close view-only input, approval-responder and inbound-block gaps in t…
lixmal 5986929
Carry the VNC session key through the network map DB path
lixmal 913a1c6
Fix framebuffer layout handling, capturer lifecycle and macOS pointer…
lixmal f5d821c
Describe session auth as SSH and VNC, cover the conflicting key path,…
lixmal 432d249
Accept bare marker protocols, reject msb_right framebuffers, split th…
lixmal 4a0fe09
Merge branch 'main' into embedded-vnc
lixmal 95e86de
Resolve VNC authorized users on the components path and fix uinput, X…
lixmal fb9c0ef
Make session key authorization atomic, unblock the encoder on teardow…
lixmal 8b2db16
Route resource endpoints through the shared policy peer filter
lixmal 312b73f
Persist virtual session processes for crash recovery and identify the…
lixmal 1ecd1da
Collect auth requirements for bidirectional source peers and fix foll…
lixmal 827098c
Compare cursor serials by identity so returning to an earlier cursor …
lixmal d196b23
Stop narrowing the shared runtime dir, close the injector on stop, al…
lixmal d823600
Honour the negotiated pixel format for the cursor, enqueue key edges …
lixmal 9672e04
Scope crash recovery to Linux, drain connection handlers before teardown
lixmal 8d6b7b6
Leave the VNC approver nil when there is no broker to ask
lixmal ba104a1
Encode one framebuffer update at a single negotiated pixel format
lixmal 9ec27f9
Authenticate the daemon and its VNC agent to each other without sendi…
lixmal e104cef
Require a real first DXGI frame and read the FreeBSD framebuffer at i…
lixmal d0d8813
Fail an approval request fast when no subscriber received the prompt
lixmal 5f739ef
Reject an unsupported VNC session id and say why a cursor rect was sk…
lixmal 15d6e3b
Keep DXGI on an idle desktop, honour the FreeBSD pitch when swizzling…
lixmal fecd7cf
Attach to the X server on the active VT and keep a retryable DXGI fra…
lixmal 12040b1
Refuse an ambiguous X display and settle the approval timeout race un…
lixmal 3ce0957
Describe the approval no-match result and the cursor-skip key as they…
lixmal 932c87e
Stop retrying an Accept that will not recover
lixmal 3af7764
Give the user a minute to answer an approval prompt
lixmal bad63c1
Say whether an approval was refused, unanswered, or never shown
lixmal 336fc9a
Drop the VNC port helpers and approval kind nothing calls
lixmal be33e9e
Merge branch 'main' into embedded-vnc
lixmal cb8fbfa
Retry VNC accept on transient connection and resource errors
lixmal eb3dabb
Merge branch 'main' into embedded-vnc
lixmal 6bb1727
Add the VNC and approval strings to the Ukrainian bundle
lixmal 8d77231
Rebind the VNC listeners with the other overlay services
lixmal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| //go:build windows || (darwin && !ios) | ||
|
|
||
| package cmd | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "net" | ||
| "net/netip" | ||
| "os" | ||
|
|
||
| log "github.com/sirupsen/logrus" | ||
| "github.com/spf13/cobra" | ||
|
|
||
| vncserver "github.com/netbirdio/netbird/client/vnc/server" | ||
| ) | ||
|
|
||
| var ( | ||
| vncAgentSocket string | ||
| vncAgentTargetUID uint32 | ||
| ) | ||
|
|
||
| func init() { | ||
| vncAgentCmd.Flags().StringVar(&vncAgentSocket, "socket", "", "Unix-domain socket path the agent listens on (required)") | ||
| vncAgentCmd.Flags().Uint32Var(&vncAgentTargetUID, "target-uid", 0, "uid the agent drops privileges to before listening (darwin only; required there, and must not be 0)") | ||
| rootCmd.AddCommand(vncAgentCmd) | ||
| } | ||
|
|
||
| // vncAgentCmd runs a VNC server inside the user's interactive session, | ||
| // listening on a Unix-domain socket. The NetBird service spawns it: on | ||
| // Windows via CreateProcessAsUser into the console session, on macOS via | ||
| // launchctl asuser into the Aqua session. | ||
| var vncAgentCmd = &cobra.Command{ | ||
| Use: "vnc-agent", | ||
| Short: "Run VNC capture agent (internal, spawned by service)", | ||
| Hidden: true, | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| log.SetReportCaller(true) | ||
| log.SetFormatter(&log.JSONFormatter{}) | ||
| log.SetOutput(os.Stderr) | ||
|
|
||
| if vncAgentSocket == "" { | ||
| return fmt.Errorf("--socket is required") | ||
| } | ||
|
|
||
| token := os.Getenv("NB_VNC_AGENT_TOKEN") | ||
| if token == "" { | ||
| return fmt.Errorf("NB_VNC_AGENT_TOKEN not set; agent requires a token from the service") | ||
| } | ||
| // Purge the token from env so it doesn't leak via /proc/<pid>/environ. | ||
| if err := os.Unsetenv("NB_VNC_AGENT_TOKEN"); err != nil { | ||
| log.Debugf("unset NB_VNC_AGENT_TOKEN: %v", err) | ||
| } | ||
|
|
||
| // Drop root privileges to the target console user BEFORE creating | ||
| // the listening socket: keeps a post-auth bug in the encoder / | ||
| // input / capture paths confined to the user's own privileges | ||
| // rather than escalating to host root, and makes the daemon's | ||
| // LOCAL_PEERCRED check see the right uid. No-op on Windows, where | ||
| // both processes run as SYSTEM. | ||
| // | ||
| // Called unconditionally: a missing or zero --target-uid is exactly | ||
| // the case the Darwin implementation refuses, and skipping the call | ||
| // for it would leave the agent running as root instead. | ||
| if err := dropAgentPrivileges(vncAgentTargetUID); err != nil { | ||
| return fmt.Errorf("drop privileges to uid %d: %w", vncAgentTargetUID, err) | ||
| } | ||
|
|
||
| if err := os.Remove(vncAgentSocket); err != nil && !os.IsNotExist(err) { | ||
| log.Debugf("remove stale socket %s: %v", vncAgentSocket, err) | ||
| } | ||
| ln, err := net.Listen("unix", vncAgentSocket) | ||
| if err != nil { | ||
| return fmt.Errorf("listen on %s: %w", vncAgentSocket, err) | ||
| } | ||
| if err := os.Chmod(vncAgentSocket, 0o600); err != nil { | ||
| log.Debugf("chmod %s: %v", vncAgentSocket, err) | ||
| } | ||
|
|
||
| ctx := cmd.Context() | ||
|
|
||
| capturer, injector, err := newAgentResources() | ||
| if err != nil { | ||
| _ = ln.Close() | ||
| return err | ||
| } | ||
| srv := vncserver.New(vncserver.Config{ | ||
| Capturer: capturer, | ||
| Injector: injector, | ||
| DisableAuth: true, | ||
| AgentTokenHex: token, | ||
| Listener: ln, | ||
| }) | ||
|
|
||
| if err := srv.Start(ctx, netip.AddrPort{}, netip.Prefix{}); err != nil { | ||
| return fmt.Errorf("start vnc server: %w", err) | ||
| } | ||
| log.Infof("vnc-agent listening on %s, ready", vncAgentSocket) | ||
|
|
||
| <-ctx.Done() | ||
| log.Info("vnc-agent context cancelled, shutting down") | ||
| return srv.Stop() | ||
| }, | ||
| SilenceUsage: true, | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| //go:build darwin && !ios | ||
|
|
||
| package cmd | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| vncserver "github.com/netbirdio/netbird/client/vnc/server" | ||
| ) | ||
|
|
||
| func newAgentResources() (vncserver.ScreenCapturer, vncserver.InputInjector, error) { | ||
| // Ask for Screen Recording here and nowhere else. This process runs as the | ||
| // console user, which TCC requires for a user-scope service, and it is fresh | ||
| // per connection, which is what makes the dialog appear at all: TCC shows it | ||
| // once per process. The request blocks until the user answers, so it also | ||
| // keeps the Accessibility ask that follows the first input out of its way. | ||
| vncserver.RequestScreenRecording() | ||
|
|
||
| capturer := vncserver.NewMacPoller() | ||
| injector, err := vncserver.NewMacInputInjector() | ||
| if err != nil { | ||
| return nil, nil, fmt.Errorf("macOS input injector: %w", err) | ||
| } | ||
| return capturer, injector, nil | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| //go:build darwin && !ios | ||
|
|
||
| package cmd | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "os/user" | ||
| "strconv" | ||
| "syscall" | ||
| ) | ||
|
|
||
| // dropAgentPrivileges drops the vnc-agent process from root (its | ||
| // launchctl-asuser-inherited starting uid) to the target console user | ||
| // before any other initialisation runs. Without this the agent runs as | ||
| // root for the lifetime of the session; any post-auth memory-safety | ||
| // issue in the capture/input/encode paths would then be a root-level | ||
| // RCE on the host instead of a user-level one. Also makes the daemon's | ||
| // LOCAL_PEERCRED check correctly identify the agent as the console user, | ||
| // not as root. | ||
| // | ||
| // Returns an error when the agent is running as a non-root uid that | ||
| // differs from targetUID: non-root can only setuid to itself, so a | ||
| // mismatch here means the spawn went to the wrong session. | ||
| func dropAgentPrivileges(targetUID uint32) error { | ||
| if targetUID == 0 { | ||
| return fmt.Errorf("refusing to keep agent running as root (target uid 0)") | ||
| } | ||
| cur := uint32(os.Getuid()) | ||
| if cur == targetUID { | ||
| return nil | ||
| } | ||
| if cur != 0 { | ||
| return fmt.Errorf("agent uid %d does not match expected %d and we lack root to fix it", cur, targetUID) | ||
| } | ||
| // Resolve the target user's real primary group rather than reusing | ||
| // targetUID as the gid: a user's primary group on macOS is typically | ||
| // staff(20), not gid==uid. Fail closed if the lookup fails. | ||
| targetGID, err := primaryGroupID(targetUID) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Drop supplementary groups first: setgid alone doesn't touch the | ||
| // auxiliary group list, leaving root's groups attached would let the | ||
| // dropped process write to root-only group-writable files. | ||
| if err := syscall.Setgroups([]int{}); err != nil { | ||
| return fmt.Errorf("setgroups([]): %w", err) | ||
| } | ||
| if err := syscall.Setgid(targetGID); err != nil { | ||
| return fmt.Errorf("setgid(%d): %w", targetGID, err) | ||
| } | ||
| if os.Getgid() != targetGID || os.Getegid() != targetGID { | ||
| return fmt.Errorf("setgid verification: gid=%d egid=%d, expected %d", os.Getgid(), os.Getegid(), targetGID) | ||
| } | ||
| if err := syscall.Setuid(int(targetUID)); err != nil { | ||
| return fmt.Errorf("setuid(%d): %w", targetUID, err) | ||
| } | ||
| if uint32(os.Getuid()) != targetUID || uint32(os.Geteuid()) != targetUID { | ||
| return fmt.Errorf("setuid verification: uid=%d euid=%d, expected %d", os.Getuid(), os.Geteuid(), targetUID) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // primaryGroupID resolves the real primary group id of the user with the | ||
| // given uid. Fails closed: a lookup or parse error returns an error so the | ||
| // caller never falls back to using uid as the gid. | ||
| func primaryGroupID(targetUID uint32) (int, error) { | ||
| u, err := user.LookupId(strconv.Itoa(int(targetUID))) | ||
| if err != nil { | ||
| return 0, fmt.Errorf("look up uid %d: %w", targetUID, err) | ||
| } | ||
| gid, err := strconv.Atoi(u.Gid) | ||
| if err != nil { | ||
| return 0, fmt.Errorf("parse gid %q for uid %d: %w", u.Gid, targetUID, err) | ||
| } | ||
| return gid, nil | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.