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
wrappers: allow sockets under $XDG_RUNTIME_DIR #6327
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
snap/validate.go
Outdated
@@ -272,7 +272,7 @@ func validateSocketAddrPath(socket *SocketInfo, fieldName string, path string) e | |||
return fmt.Errorf("invalid %q: %q should be written as %q", fieldName, path, clean) | |||
} | |||
|
|||
if !(strings.HasPrefix(path, "$SNAP_DATA/") || strings.HasPrefix(path, "$SNAP_COMMON/")) { | |||
if !(strings.HasPrefix(path, "$SNAP_DATA/") || strings.HasPrefix(path, "$SNAP_COMMON/") || strings.HasPrefix(path, "/run/user/0/$SNAP_NAME/")) { | |||
return fmt.Errorf( | |||
"invalid %q: must have a prefix of $SNAP_DATA or $SNAP_COMMON", fieldName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going this way, the message needs to be updated to include /run/user/0/...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @bboozzoo on this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did updated message, though with XDG_RUNTIME_DIR as @jdstrand proposed
snap/validate.go
Outdated
@@ -272,7 +272,7 @@ func validateSocketAddrPath(socket *SocketInfo, fieldName string, path string) e | |||
return fmt.Errorf("invalid %q: %q should be written as %q", fieldName, path, clean) | |||
} | |||
|
|||
if !(strings.HasPrefix(path, "$SNAP_DATA/") || strings.HasPrefix(path, "$SNAP_COMMON/")) { | |||
if !(strings.HasPrefix(path, "$SNAP_DATA/") || strings.HasPrefix(path, "$SNAP_COMMON/") || strings.HasPrefix(path, "/run/user/0/$SNAP_NAME/")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should use $SNAP_INSTANCE_NAME
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I change it to XDG_RUNTIME_DIR, so there is no more need or SNAP_INSTANCE_NAME now
a55b558
to
96b8e1f
Compare
Updated to use $SNAP_INSTANCE_NAME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clearly there is a bug since we allow /run/user/[0-9]*/snap.@{SNAP_INSTANCE_NAME}/**
but it feels wrong to make the user have to specify /run/user/*0*/...
since in the future we might change that and this is exactly what XDG_RUNTIME_DIR is for. Eg:
$ sudo hello-world.env|grep XDG
XDG_RUNTIME_DIR=/run/user/0/snap.hello-world
Perhaps instead use || strings.HasPrefix(path, "$XDG_RUNTIME_DIR/")
? I'm not sure if you'll have to expand that in the systemd unit or not, but this seems much more user friendly and in line with the other two variables.
snap/validate.go
Outdated
@@ -272,7 +272,7 @@ func validateSocketAddrPath(socket *SocketInfo, fieldName string, path string) e | |||
return fmt.Errorf("invalid %q: %q should be written as %q", fieldName, path, clean) | |||
} | |||
|
|||
if !(strings.HasPrefix(path, "$SNAP_DATA/") || strings.HasPrefix(path, "$SNAP_COMMON/")) { | |||
if !(strings.HasPrefix(path, "$SNAP_DATA/") || strings.HasPrefix(path, "$SNAP_COMMON/") || strings.HasPrefix(path, "/run/user/0/$SNAP_NAME/")) { | |||
return fmt.Errorf( | |||
"invalid %q: must have a prefix of $SNAP_DATA or $SNAP_COMMON", fieldName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @bboozzoo on this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please adjust tests to update the validation routine.
3a7e59b
to
7b2f8d6
Compare
7b2f8d6
to
effeb09
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving, but please consider adding the suggested comment (see inline).
wrappers/services.go
Outdated
@@ -600,6 +601,7 @@ func generateSnapSocketFiles(app *snap.AppInfo) (*map[string][]byte, error) { | |||
func renderListenStream(socket *snap.SocketInfo) string { | |||
snap := socket.App.Snap | |||
listenStream := strings.Replace(socket.ListenStream, "$SNAP_DATA", snap.DataDir(), -1) | |||
listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", snap.UserXdgRuntimeDir(sys.Geteuid()), -1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was slightly uncomfortable with this change since sys.Geteuid() is going to be whatever snapd is running as, which works for both the testsuite and typical "daemons run as root" case but would not work whenever we introduce User= and Group= in the generated systemd units. In that case, we would want to adjust sys.Geteuid() to be a lookup of the specified user/group, which is ok.
As a future reminder, can you add the following comment:
// TODO: when we support User/Group in the generated systemd unit,
// adjust this accordingly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdstrand I was thinking exactly same thing. But I was not sure if we have anything ready for this usecase. As I assume socket options will then need to expand to SNAP_USER_COMMON and SNAP_USER_DATA. But then we need way to identify what is user service should run under. And actually forbid use of SNAP_DATA/SNAP_COMMON if not running as root.
And indeed I used sys.Geteuid() trick to get right value in test and in actual run time
I will add comment, so make sure we keep this updated when introducing serviced for users.
I will add comment to make sure we capture this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would prefer not to use sys.Geteuid() here. Its slightly misleading, when looking at this code it seems like the value in xdg-runtime-dir depends on the daemon uid. But in reality it depends on the setting of the snap.yaml for the given service. By default the service will run as root and once we have a way to express users and groups it will run as a user or group. So I think it would be slightly better to represent that here (especially since initially this will always be uid=0). Something like:
--- a/wrappers/services.go
+++ b/wrappers/services.go
@@ -603,7 +603,9 @@ func renderListenStream(socket *snap.SocketInfo) string {
listenStream := strings.Replace(socket.ListenStream, "$SNAP_DATA", snap.DataDir(), -1)
// TODO: when we support User/Group in the generated systemd unit,
// adjust this accordingly
- listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", snap.UserXdgRuntimeDir(sys.Geteuid()), -1)
+ serviceUserUid := sys.UserID(0)
+ runtimeDir := snap.UserXdgRuntimeDir(serviceUserUid)
+ listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", runtimeDir, -1)
return strings.Replace(listenStream, "$SNAP_COMMON", snap.CommonDataDir(), -1)
}
diff --git a/wrappers/services_test.go b/wrappers/services_test.go
index 87de9e55c5..29700bc577 100644
--- a/wrappers/services_test.go
+++ b/wrappers/services_test.go
@@ -25,7 +25,6 @@ import (
"path/filepath"
"regexp"
"sort"
- "strconv"
"strings"
"time"
@@ -33,7 +32,6 @@ import (
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
- "github.com/snapcore/snapd/osutil/sys"
"github.com/snapcore/snapd/progress"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/snaptest"
@@ -460,7 +458,7 @@ Service=snap.hello-snap.svc1.service
FileDescriptorName=sock3
ListenStream=%s
-`, filepath.Join(s.tempdir, "/run/user", strconv.FormatUint(uint64(sys.Geteuid()), 10), "snap.hello-snap/sock3.socket"))
+`, filepath.Join(s.tempdir, "/run/user/0/snap.hello-snap/sock3.socket"))
c.Check(sock3File, testutil.FileContains, expected)
}
543eb6b
to
87e4807
Compare
@zyga can you please review updated tests as it should be fixed now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this looks good! +0.9 with one small suggestion, otherwise good to go.
wrappers/services.go
Outdated
@@ -600,6 +601,7 @@ func generateSnapSocketFiles(app *snap.AppInfo) (*map[string][]byte, error) { | |||
func renderListenStream(socket *snap.SocketInfo) string { | |||
snap := socket.App.Snap | |||
listenStream := strings.Replace(socket.ListenStream, "$SNAP_DATA", snap.DataDir(), -1) | |||
listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", snap.UserXdgRuntimeDir(sys.Geteuid()), -1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would prefer not to use sys.Geteuid() here. Its slightly misleading, when looking at this code it seems like the value in xdg-runtime-dir depends on the daemon uid. But in reality it depends on the setting of the snap.yaml for the given service. By default the service will run as root and once we have a way to express users and groups it will run as a user or group. So I think it would be slightly better to represent that here (especially since initially this will always be uid=0). Something like:
--- a/wrappers/services.go
+++ b/wrappers/services.go
@@ -603,7 +603,9 @@ func renderListenStream(socket *snap.SocketInfo) string {
listenStream := strings.Replace(socket.ListenStream, "$SNAP_DATA", snap.DataDir(), -1)
// TODO: when we support User/Group in the generated systemd unit,
// adjust this accordingly
- listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", snap.UserXdgRuntimeDir(sys.Geteuid()), -1)
+ serviceUserUid := sys.UserID(0)
+ runtimeDir := snap.UserXdgRuntimeDir(serviceUserUid)
+ listenStream = strings.Replace(listenStream, "$XDG_RUNTIME_DIR", runtimeDir, -1)
return strings.Replace(listenStream, "$SNAP_COMMON", snap.CommonDataDir(), -1)
}
diff --git a/wrappers/services_test.go b/wrappers/services_test.go
index 87de9e55c5..29700bc577 100644
--- a/wrappers/services_test.go
+++ b/wrappers/services_test.go
@@ -25,7 +25,6 @@ import (
"path/filepath"
"regexp"
"sort"
- "strconv"
"strings"
"time"
@@ -33,7 +32,6 @@ import (
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
- "github.com/snapcore/snapd/osutil/sys"
"github.com/snapcore/snapd/progress"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/snaptest"
@@ -460,7 +458,7 @@ Service=snap.hello-snap.svc1.service
FileDescriptorName=sock3
ListenStream=%s
-`, filepath.Join(s.tempdir, "/run/user", strconv.FormatUint(uint64(sys.Geteuid()), 10), "snap.hello-snap/sock3.socket"))
+`, filepath.Join(s.tempdir, "/run/user/0/snap.hello-snap/sock3.socket"))
c.Check(sock3File, testutil.FileContains, expected)
}
wrappers/services_test.go
Outdated
FileDescriptorName=sock3 | ||
ListenStream=%s | ||
|
||
`, filepath.Join(s.tempdir, "/run/user", strconv.FormatUint(uint64(sys.Geteuid()), 10), "snap.hello-snap/sock3.socket")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is similar to above (and the diff will change this part too): when reading this its slightly misleading as it may make the reader think the value is dependent on the runtime uid. But its strictly dependent on the user configured in the service yaml. So I would prefer here to not use sys.Geteuid() as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mvo5 I updated code per proposal. Initially I was trying to avoid hardcoding uid but you are right that it actually made it worse and code was giving wrong impression.
And true, this will anyway need to be revisited when we land support for user services
XDG_RUNTIME_DIR (usually /run/user/<uid>/snap.$SNAP_INSTANCE_NAME/) is permited path for sockets to be created, this is at the moment blocked when socket is defined as part of daemon configuration Signed-off-by: Ondrej Kubik <ondrej.kubik@canonical.com>
…ation Expand XDG_RUNTIME_DIR if used for socket activated service Signed-off-by: Ondrej Kubik <ondrej.kubik@canonical.com>
…ME_DIR socket path Update socket-activation test snap with socket in XDG_RUNTIME_DIR and SNAP_DATA path Signed-off-by: Ondrej Kubik <ondrej.kubik@canonical.com>
87e4807
to
3e0aa03
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I re-read the patch and the discussion, looks good to me.
seems there are some actual SE Linux failures now, @bboozzoo maybe can help |
Yes, this is caused by snapd trying to remove a socket file under /run/. I'll look into adjusting the policy and will push a patch to this branch. |
Adjust SELinux policy to allow snapd to remove socket files under /run/user (aka $XDG_RUNTIME_DIR). Fixes the following denials: ---- type=AVC msg=audit(06/24/19 07:52:12.092:324) : avc: denied { write } for pid=25331 comm=snapd name=/ dev="tmpfs" ino=21794 scontext=system_u:system_r:snappy_t:s0 tcontext =system_u:object_r:user_tmp_t:s0 tclass=dir permissive=1 ---- type=AVC msg=audit(06/24/19 07:52:12.092:325) : avc: denied { remove_name } for pid=25331 comm=snapd name=snap.socket-activation dev="tmpfs" ino=69344 scontext=system_u:sy stem_r:snappy_t:s0 tcontext=system_u:object_r:user_tmp_t:s0 tclass=dir permissive=1 ---- type=AVC msg=audit(06/24/19 07:52:12.092:326) : avc: denied { rmdir } for pid=25331 comm=snapd name=snap.socket-activation dev="tmpfs" ino=69344 scontext=system_u:system_r :snappy_t:s0 tcontext=system_u:object_r:user_tmp_t:s0 tclass=dir permissive=1 ---- type=AVC msg=audit(06/24/19 07:52:12.093:327) : avc: denied { unlink } for pid=25331 comm=snapd name=socket-xdg dev="tmpfs" ino=69345 scontext=system_u:system_r:snappy_t:s 0 tcontext=system_u:object_r:user_tmp_t:s0 tclass=sock_file permissive=1 Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
Pushed a patch that extends the policy accordingly. |
/run/user/0/$SNAP_NAME/ is permited path for sockets to be created, this is at the moment blocked
when socket is defined as part of daemon configuration
Signed-off-by: Ondrej Kubik ondrej.kubik@canonical.com
Thanks for helping us make a better snapd!
Have you signed the license agreement and read the contribution guide?