@@ -26,6 +26,7 @@ import (
2626
2727 "github.com/containerd/containerd/log"
2828 "github.com/containerd/containerd/mount"
29+ "github.com/containerd/containerd/sys"
2930 "github.com/containerd/continuity/fs"
3031 "github.com/pkg/errors"
3132)
@@ -86,3 +87,84 @@ func Supported(root string) error {
8687 }
8788 return supportsMultipleLowerDir (root )
8889}
90+
91+ // NeedsUserXAttr returns whether overlayfs should be mounted with the "userxattr" mount option.
92+ //
93+ // The "userxattr" option is needed for mounting overlayfs inside a user namespace with kernel >= 5.11.
94+ //
95+ // The "userxattr" option is NOT needed for the initial user namespace (aka "the host").
96+ //
97+ // Also, Ubuntu (since circa 2015) and Debian (since 10) with kernel < 5.11 can mount
98+ // the overlayfs in a user namespace without the "userxattr" option.
99+ //
100+ // The corresponding kernel commit: https://github.com/torvalds/linux/commit/2d2f2d7322ff43e0fe92bf8cccdc0b09449bf2e1
101+ // > ovl: user xattr
102+ // >
103+ // > Optionally allow using "user.overlay." namespace instead of "trusted.overlay."
104+ // > ...
105+ // > Disable redirect_dir and metacopy options, because these would allow privilege escalation through direct manipulation of the
106+ // > "user.overlay.redirect" or "user.overlay.metacopy" xattrs.
107+ // > ...
108+ //
109+ // The "userxattr" support is not exposed in "/sys/module/overlay/parameters".
110+ func NeedsUserXAttr (d string ) (bool , error ) {
111+ if ! sys .RunningInUserNS () {
112+ // we are the real root (i.e., the root in the initial user NS),
113+ // so we do never need "userxattr" opt.
114+ return false , nil
115+ }
116+
117+ // TODO: add fast path for kernel >= 5.11 .
118+ //
119+ // Keep in mind that distro vendors might be going to backport the patch to older kernels.
120+ // So we can't completely remove the check.
121+
122+ tdRoot := filepath .Join (d , "userxattr-check" )
123+ if err := os .RemoveAll (tdRoot ); err != nil {
124+ log .L .WithError (err ).Warnf ("Failed to remove check directory %v" , tdRoot )
125+ }
126+
127+ if err := os .MkdirAll (tdRoot , 0700 ); err != nil {
128+ return false , err
129+ }
130+
131+ defer func () {
132+ if err := os .RemoveAll (tdRoot ); err != nil {
133+ log .L .WithError (err ).Warnf ("Failed to remove check directory %v" , tdRoot )
134+ }
135+ }()
136+
137+ td , err := ioutil .TempDir (tdRoot , "" )
138+ if err != nil {
139+ return false , err
140+ }
141+
142+ for _ , dir := range []string {"lower1" , "lower2" , "upper" , "work" , "merged" } {
143+ if err := os .Mkdir (filepath .Join (td , dir ), 0755 ); err != nil {
144+ return false , err
145+ }
146+ }
147+
148+ opts := []string {
149+ fmt .Sprintf ("lowerdir=%s:%s,upperdir=%s,workdir=%s" , filepath .Join (td , "lower2" ), filepath .Join (td , "lower1" ), filepath .Join (td , "upper" ), filepath .Join (td , "work" )),
150+ "userxattr" ,
151+ }
152+
153+ m := mount.Mount {
154+ Type : "overlay" ,
155+ Source : "overlay" ,
156+ Options : opts ,
157+ }
158+
159+ dest := filepath .Join (td , "merged" )
160+ if err := m .Mount (dest ); err != nil {
161+ // Probably the host is running Ubuntu/Debian kernel (< 5.11) with the userns patch but without the userxattr patch.
162+ // Return false without error.
163+ log .L .WithError (err ).Debugf ("cannot mount overlay with \" userxattr\" , probably the kernel does not support userxattr" )
164+ return false , nil
165+ }
166+ if err := mount .UnmountAll (dest , 0 ); err != nil {
167+ log .L .WithError (err ).Warnf ("Failed to unmount check directory %v" , dest )
168+ }
169+ return true , nil
170+ }
0 commit comments