Skip to content

Commit 8462e1b

Browse files
committed
Add support for injecting additional GIDs
This change adds support for injecting additional GIDs using the internal CDI representations. (Applicable for Tegra-based systems and /dev/dri devices) This is disabled by default, but can be opted in to by setting the features.allow-additional-gids feature flag. This can also be done by running sudo nvidia-ctk config --in-place --set features.allow-additional-gids Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent 50278cb commit 8462e1b

File tree

13 files changed

+118
-24
lines changed

13 files changed

+118
-24
lines changed

internal/config/features.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ package config
1919
type featureName string
2020

2121
const (
22-
FeatureGDS = featureName("gds")
23-
FeatureMOFED = featureName("mofed")
24-
FeatureNVSWITCH = featureName("nvswitch")
25-
FeatureGDRCopy = featureName("gdrcopy")
22+
FeatureGDS = featureName("gds")
23+
FeatureMOFED = featureName("mofed")
24+
FeatureNVSWITCH = featureName("nvswitch")
25+
FeatureGDRCopy = featureName("gdrcopy")
26+
FeatureAllowAdditionalGIDs = featureName("allow-additional-gids")
2627
)
2728

2829
// features specifies a set of named features.
@@ -31,6 +32,11 @@ type features struct {
3132
MOFED *feature `toml:"mofed,omitempty"`
3233
NVSWITCH *feature `toml:"nvswitch,omitempty"`
3334
GDRCopy *feature `toml:"gdrcopy,omitempty"`
35+
// AllowAdditionalGIDs triggers the additionalGIDs field in internal CDI
36+
// specifications to be populated if required. This can be useful when
37+
// running the container as a user id that may not have access to device
38+
// nodes.
39+
AllowAdditionalGIDs *feature `toml:"allow-additional-gids,omitempty"`
3440
}
3541

3642
type feature bool
@@ -40,10 +46,11 @@ type feature bool
4046
// variables can also be supplied.
4147
func (fs features) IsEnabled(n featureName, in ...getenver) bool {
4248
featureEnvvars := map[featureName]string{
43-
FeatureGDS: "NVIDIA_GDS",
44-
FeatureMOFED: "NVIDIA_MOFED",
45-
FeatureNVSWITCH: "NVIDIA_NVSWITCH",
46-
FeatureGDRCopy: "NVIDIA_GDRCOPY",
49+
FeatureGDS: "NVIDIA_GDS",
50+
FeatureMOFED: "NVIDIA_MOFED",
51+
FeatureNVSWITCH: "NVIDIA_NVSWITCH",
52+
FeatureGDRCopy: "NVIDIA_GDRCOPY",
53+
FeatureAllowAdditionalGIDs: "NVIDIA_ALLOW_ADDITIONAL_GIDS",
4754
}
4855

4956
envvar := featureEnvvars[n]
@@ -56,6 +63,8 @@ func (fs features) IsEnabled(n featureName, in ...getenver) bool {
5663
return fs.NVSWITCH.isEnabled(envvar, in...)
5764
case FeatureGDRCopy:
5865
return fs.GDRCopy.isEnabled(envvar, in...)
66+
case FeatureAllowAdditionalGIDs:
67+
return fs.AllowAdditionalGIDs.isEnabled(envvar, in...)
5968
default:
6069
return false
6170
}

internal/edits/device.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
package edits
1818

1919
import (
20+
"os"
21+
22+
"golang.org/x/sys/unix"
2023
"tags.cncf.io/container-device-interface/pkg/cdi"
2124
"tags.cncf.io/container-device-interface/specs-go"
2225

@@ -26,15 +29,23 @@ import (
2629
type device discover.Device
2730

2831
// toEdits converts a discovered device to CDI Container Edits.
29-
func (d device) toEdits() (*cdi.ContainerEdits, error) {
32+
func (d device) toEdits(allowAdditionalGIDs bool) (*cdi.ContainerEdits, error) {
3033
deviceNode, err := d.toSpec()
3134
if err != nil {
3235
return nil, err
3336
}
3437

38+
var additionalGIDs []uint32
39+
if allowAdditionalGIDs {
40+
if requiredGID := d.getRequiredGID(); requiredGID != 0 {
41+
additionalGIDs = append(additionalGIDs, requiredGID)
42+
}
43+
}
44+
3545
e := cdi.ContainerEdits{
3646
ContainerEdits: &specs.ContainerEdits{
37-
DeviceNodes: []*specs.DeviceNode{deviceNode},
47+
DeviceNodes: []*specs.DeviceNode{deviceNode},
48+
AdditionalGIDs: additionalGIDs,
3849
},
3950
}
4051
return &e, nil
@@ -52,10 +63,37 @@ func (d device) toSpec() (*specs.DeviceNode, error) {
5263
if hostPath == d.Path {
5364
hostPath = ""
5465
}
66+
5567
s := specs.DeviceNode{
5668
HostPath: hostPath,
5769
Path: d.Path,
5870
}
5971

6072
return &s, nil
6173
}
74+
75+
// getRequiredGID returns the group id of the device if the device is not world read/writable.
76+
// If the information cannot be extracted or an error occurs, 0 is returned.
77+
func (d device) getRequiredGID() uint32 {
78+
path := d.HostPath
79+
if path == "" {
80+
path = d.Path
81+
}
82+
if path == "" {
83+
return 0
84+
}
85+
86+
var stat unix.Stat_t
87+
if err := unix.Lstat(path, &stat); err != nil {
88+
return 0
89+
}
90+
// This is only supported for char devices
91+
if stat.Mode&unix.S_IFMT != unix.S_IFCHR {
92+
return 0
93+
}
94+
95+
if permissionsForOther := os.FileMode(stat.Mode).Perm(); permissionsForOther&06 == 0 {
96+
return stat.Gid
97+
}
98+
return 0
99+
}

internal/edits/lib.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (o *options) EditsFromDiscoverer(d discover.Discover) (*cdi.ContainerEdits,
5858

5959
c := NewContainerEdits()
6060
for _, d := range devices {
61-
edits, err := device(d).toEdits()
61+
edits, err := device(d).toEdits(o.allowAdditionalGIDs)
6262
if err != nil {
6363
return nil, fmt.Errorf("failed to create container edits for device: %w", err)
6464
}

internal/edits/options.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ package edits
1919
import "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
2020

2121
type options struct {
22-
logger logger.Interface
22+
logger logger.Interface
23+
allowAdditionalGIDs bool
2324
}
2425

2526
type Option func(*options)
@@ -29,3 +30,9 @@ func WithLogger(logger logger.Interface) Option {
2930
o.logger = logger
3031
}
3132
}
33+
34+
func WithAllowAdditionalGIDs(allowAdditionalGIDs bool) Option {
35+
return func(o *options) {
36+
o.allowAdditionalGIDs = allowAdditionalGIDs
37+
}
38+
}

internal/modifier/discover.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,18 @@ import (
2828
)
2929

3030
type discoverModifier struct {
31-
logger logger.Interface
32-
discoverer discover.Discover
31+
logger logger.Interface
32+
discoverer discover.Discover
33+
allowAdditionalGIDs bool
3334
}
3435

3536
// NewModifierFromDiscoverer creates a modifier that applies the discovered
3637
// modifications to an OCI spec if required by the runtime wrapper.
37-
func NewModifierFromDiscoverer(logger logger.Interface, d discover.Discover) (oci.SpecModifier, error) {
38+
func NewModifierFromDiscoverer(logger logger.Interface, d discover.Discover, allowAdditionalGIDs bool) (oci.SpecModifier, error) {
3839
m := discoverModifier{
39-
logger: logger,
40-
discoverer: d,
40+
logger: logger,
41+
discoverer: d,
42+
allowAdditionalGIDs: allowAdditionalGIDs,
4143
}
4244
return &m, nil
4345
}
@@ -47,6 +49,7 @@ func NewModifierFromDiscoverer(logger logger.Interface, d discover.Discover) (oc
4749
func (m discoverModifier) Modify(spec *specs.Spec) error {
4850
e := edits.New(
4951
edits.WithLogger(m.logger),
52+
edits.WithAllowAdditionalGIDs(m.allowAdditionalGIDs),
5053
)
5154

5255
specEdits, err := e.SpecModifierFromDiscoverer(m.discoverer)

internal/modifier/discover_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func TestDiscoverModifier(t *testing.T) {
132132

133133
for _, tc := range testCases {
134134
t.Run(tc.description, func(t *testing.T) {
135-
m, err := NewModifierFromDiscoverer(logger, tc.discover)
135+
m, err := NewModifierFromDiscoverer(logger, tc.discover, true)
136136
require.NoError(t, err)
137137

138138
err = m.Modify(tc.spec)

internal/modifier/gated.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
7878
discoverers = append(discoverers, d)
7979
}
8080

81-
return NewModifierFromDiscoverer(logger, discover.Merge(discoverers...))
81+
allowAdditionalGIDs := !cfg.Features.IsEnabled(config.FeatureAllowAdditionalGIDs, image)
82+
83+
return NewModifierFromDiscoverer(logger, discover.Merge(discoverers...), allowAdditionalGIDs)
8284
}

internal/modifier/graphics.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, image imag
6262
drmNodes,
6363
mounts,
6464
)
65-
return NewModifierFromDiscoverer(logger, d)
65+
66+
allowAdditionalGIDs := cfg.Features.IsEnabled(config.FeatureAllowAdditionalGIDs, image)
67+
return NewModifierFromDiscoverer(logger, d, allowAdditionalGIDs)
6668
}
6769

6870
// requiresGraphicsModifier determines whether a graphics modifier is required.

internal/oci/spec_mock.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/nvcdi/lib.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ type nvcdilib struct {
6565
infolib info.Interface
6666

6767
mergedDeviceOptions []transform.MergedDeviceOption
68+
69+
allowAdditionalGIDs bool
6870
}
6971

7072
// New creates a new nvcdi library
@@ -196,6 +198,7 @@ func (m *wrapper) GetCommonEdits() (*cdi.ContainerEdits, error) {
196198
func (l *nvcdilib) editsFromDiscoverer(d discover.Discover) (*cdi.ContainerEdits, error) {
197199
e := edits.New(
198200
edits.WithLogger(l.logger),
201+
edits.WithAllowAdditionalGIDs(l.allowAdditionalGIDs),
199202
)
200203
return e.EditsFromDiscoverer(d)
201204
}

0 commit comments

Comments
 (0)