Skip to content
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

Creating a Sudo() Client copy leads to nil pointer dereference #195

Closed
kaplan-michael opened this issue May 5, 2024 · 11 comments · Fixed by #199
Closed

Creating a Sudo() Client copy leads to nil pointer dereference #195

kaplan-michael opened this issue May 5, 2024 · 11 comments · Fixed by #199

Comments

@kaplan-michael
Copy link

I create the connections like this. yet when I check with a debugger, the SudoClient seems to have nil as it's connection. I'm guessing it should either have a pointer to the original, or a copy of the original one? Is it a bug or am I doing something wrong?

// setupHost configures and connects to a single host
func setupHost(hostConfig *HostConfig) (*Host, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	conn, err := createConnection(hostConfig)
	if err != nil {
		return nil, err
	}
	client, err := rig.NewClient(rig.WithConnection(conn), rig.WithLogger(logger.Slog))
	if err != nil {
		return nil, fmt.Errorf("failed to create rig client: %w", err)
	}

	err = client.Connect(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to connect: %w", err)
	}

	// Initialize sudo client at the same time
	sudoClient := client.Sudo()
	if sudoClient == nil {
		return nil, fmt.Errorf("failed to initialize sudo client")
	}

	return &Host{Client: client, Sudo: sudoClient, Vars: hostConfig.Vars, Fs: remotefs.NewFS(client)}, nil
}
@kaplan-michael
Copy link
Author

kaplan-michael commented May 5, 2024

From looking at it, it probably doesn't clone all required bits(doesn't init them) https://github.com/k0sproject/rig/blob/main/client.go#L252

so some internal Client bits end up being nil.

edit: seems the WithConnection, etc. don't properly propagate it, no clue why yet.

@kaplan-michael
Copy link
Author

kaplan-michael commented May 5, 2024

I think it is missing a call to the setup() method on the cloned client, hence the options don't get propagated.
not sure what is the best place for it though.

func (c *Client) Clone(opts ...ClientOption) *Client {
	options := c.options.Clone()
	options.Apply(opts...)
	client := &Client{options: options}
	if err := client.setup(); err != nil {
		return nil
	}
	return client
}

@kke
Copy link
Contributor

kke commented May 6, 2024

Hmm, setup does c.Runner = c.options.GetRunner(c.connection) which will overwrite the sudo Runner 🤔

Doesn't the nil panic backtrace show what is nil?

The Client already memoizes the sudoClone when you do h.Sudo(), so keeping your own copy of it shouldn't be necessary. But it probably doesn't solve this nil problem.

@kaplan-michael
Copy link
Author

I have tried to not keeping my copy and it behaved the same, so I started doing that so it's easier to debug.

The backtrace wasn't very helpful(I'll try to share later), but from what I found I guess either the connection/the runner is nil. It doesn't get applied from the options to the Sudo client itself(and I assume something is trying to use that)

@kke
Copy link
Contributor

kke commented May 7, 2024

I think @james-nesbitt solved it, PR incoming

@james-nesbitt
Copy link
Contributor

#199 makes sure that any client.Clone() has .setup() run before returning, which includes .Sudo()

@james-nesbitt
Copy link
Contributor

james-nesbitt commented May 7, 2024

oh. I didn't check the error as @kaplan-michael suggested. Let me update that.

Edit: actually, I think that the error catch is superfluous and maybe a lint ignore is better.

@kke kke linked a pull request May 7, 2024 that will close this issue
@kke kke closed this as completed in #199 May 7, 2024
@kaplan-michael
Copy link
Author

Thanks @james-nesbitt. works good.
I do have a question in terms of sudo, do you plan/want to support sudo with a password? probably injected through stdin?

@james-nesbitt
Copy link
Contributor

for encrypted private keys I use ssh-agent and pre-load the key.

@kke
Copy link
Contributor

kke commented May 15, 2024

I do have a question in terms of sudo, do you plan/want to support sudo with a password? probably injected through stdin?

I'm undecided. I suppose it wouldn't be that hard to do, the biggest hurdle is when the sudo times out (could be after each command) and password is needed again, so the password would have to be kept in memory. Something like https://github.com/awnumar/memguard could help in doing it somewhat securely.

@kaplan-michael
Copy link
Author

yeah, I do agree it would be nice to use memguard, but I feel like any password that are passed in will be through a flag or some kind of config, so I'm not sure if it justifies the effort to then try to keep it safe in memory? + It would be in memory on the system rig is running, which I guess in most cases is a single user personal system, so that mitigates it a bit more?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants