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

workloadattestor systemd: dbus use of closed network connection #4360

Merged
merged 5 commits into from
Aug 23, 2023

Conversation

rkaippully
Copy link
Contributor

@rkaippully rkaippully commented Jul 26, 2023

The connection to system bus is shared and should not be closed after use in order to avoid errors on concurrent usage.

It is typical to share the connection in the same process as per this explanation:
godbus/dbus#179 (comment)

Pull Request check list

  • Commit conforms to CONTRIBUTING.md?
  • Proper tests/regressions included?
  • Documentation updated?

Affected functionality
Bug fix on systemd workload attestor

Description of change
Uses a plugin level dbus connection shared by all attestation requests that is not closed after every attestation. This will prevent errors when a concurrent request closes a connection being used by another request.

Which issue this PR fixes
Fixes #4315

Fixes spiffe#4315

The connection to system bus is shared and should not be closed after
use in order to avoid errors on concurrent usage.

It is typical to share the connection in the same process as per this
explanation:
godbus/dbus#179 (comment)

In case of errors, this shared connection will detect that and attempt
to reconnect: https://github.com/godbus/dbus/blob/v5.1.0/conn.go#L124.

Signed-off-by: Raghu Kaippully <rkaippully@gmail.com>
Copy link
Member

@amartinezfayo amartinezfayo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this, @rkaippully.

conn, err := dbus.SystemBus()
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to open dbus connection: %v", err)
}
defer conn.Close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is no error, when would be closed the connection?
I see that the connection is closed when the context is done, but the context used would be a new context and not the one from this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it is a background context which is never cancelled, it looks like the connection is never closed. Or did I get that wrong?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I don't think that the connection will be closed, which is a problem.
We will need to avoid using the shared dbus connection, since it doesn't seem that there is a clear way to properly handle the closure of connections when there are concurrent requests.
I think that the right solution here will involve maintaining a connection at the plugin level.

Copy link
Contributor Author

@rkaippully rkaippully Aug 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean a shared connection at the plugin level that's never closed (unless there is an error)? It's slightly better because it'll protect the plugin from other code inadvertently closing the connection.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I mean having a single connection at the plugin level that's closed when the plugin closes. The same connection can be used by the different attestation calls.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @amartinezfayo! I have changed this to use a plugin level shared connection. I could not find the lifecycle mechanism to close the connection on plugin close (probably because I am fairly new to spire code base and go). What am I missing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When plugins implement the io.Closer interface, the Close function is invoked before the plugin is unloaded. Since this is documented in the SPIRE Plugin SDK, it may be difficult to find it :)
You just need to add a function func (p *Plugin) Close() error {...} that closes the connection, and it will be automatically called.

@amartinezfayo amartinezfayo self-assigned this Aug 3, 2023
...instead of a global shared one. This ensures that the connection is
never closed by any other library while this plugin is using it.

Signed-off-by: Raghu Kaippully <rkaippully@gmail.com>
Signed-off-by: Raghu Kaippully <rkaippully@gmail.com>
Copy link
Member

@amartinezfayo amartinezfayo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @rkaippully for the changes.

When plugins implement the io.Closer interface, the Close function is invoked before the plugin is unloaded. You just need to add a function func (p *Plugin) Close() error {...} that closes the connection, and it will be automatically called.

@@ -67,12 +71,29 @@ func (p *Plugin) Attest(ctx context.Context, req *workloadattestorv1.AttestReque
}, nil
}

func getSystemdUnitInfo(ctx context.Context, pid uint) (*DBusUnitInfo, error) {
conn, err := dbus.SystemBus()
func (p *Plugin) DBusConn() (*dbus.Conn, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does not really need to be exported. We may call it getDBusConn.

}

func getSystemdUnitInfo(ctx context.Context, p *Plugin, pid uint) (*DBusUnitInfo, error) {
// Do not close this connection because it is shared and will autoclose on errors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this comment is accurate. I would probably mention that the connection is closed when the plugin is unloaded in SPIRE. Something along the lines of:

Suggested change
// Do not close this connection because it is shared and will autoclose on errors
// We are not closing the connection here because it's closed when the Close() function is called as part of unloading the plugin.

- Implement `Close()` on the plugin to close the DBus connection
- Do not export `DBusConn` method
- Fix a comment

Signed-off-by: Raghu Kaippully <rkaippully@gmail.com>
Copy link
Member

@amartinezfayo amartinezfayo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @rkaippully!

@amartinezfayo amartinezfayo merged commit dd57ddf into spiffe:main Aug 23, 2023
22 checks passed
@rkaippully rkaippully deleted the raghu/fix-4315 branch August 23, 2023 17:07
@azdagron azdagron added this to the 1.8.0 milestone Aug 29, 2023
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 this pull request may close these issues.

workloadattestor systemd: failed to collect all selectors - dbus use of closed network connection
3 participants