Nwjs applications needing browser-like security

I’m trying to snap a web based game - actually a framework which will enable lots of games - and it uses nwjs as the runtime. I have this working in devmode, but when confined I get the following errors in dmesg:

[867432.367253] audit: type=1400 audit(1508764403.925:6231): apparmor="DENIED" operation="open" profile="snap.c3game.c3game" name="/etc/xdg/user-dirs.conf" pid=19219 comm="xdg-user-dirs-u" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0
[867437.923567] audit: type=1107 audit(1508764409.481:6232): pid=2768 uid=106 auid=4294967295 ses=4294967295 msg='apparmor="DENIED" operation="dbus_method_call"  bus="system" path="/" interface="org.freedesktop.DBus.ObjectManager" member="GetManagedObjects" mask="send" name="org.bluez" pid=19141 label="snap.c3game.c3game" peer_pid=3753 peer_label="unconfined"
[867438.209149] audit: type=1400 audit(1508764409.765:6233): apparmor="DENIED" operation="open" profile="snap.c3game.c3game" name="/home/alan/.config/dconf/user" pid=19141 comm="nw" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000
[867438.650879] audit: type=1400 audit(1508764410.209:6234): apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.p4j5xA" pid=19141 comm="Chrome_IOThread" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
[867438.650945] audit: type=1400 audit(1508764410.209:6235): apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.TWFd0o" pid=19141 comm="Chrome_IOThread" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
[867438.650996] audit: type=1400 audit(1508764410.209:6236): apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.DY8msd" pid=19141 comm="Chrome_IOThread" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
[867438.700206] audit: type=1326 audit(1508764410.257:6237): auid=4294967295 uid=1000 gid=1000 ses=4294967295 pid=19382 comm="Chrome_IOThread" exe="/snap/c3game/x1/nw" sig=31 arch=c000003e syscall=101 compat=0 ip=0x7fa820f92216 code=0x0

… then the game dies …

[22312:22411:1023/143744.489745:FATAL:memory.cc(22)] Out of memory. size=131072
#0 0x7f0d31ad6327 <unknown>

Failed to generate minidump.Received signal 6
#0 0x7f0d31ad6327 <unknown>
  r8: 00007f0d2ebd3771  r9: 00007f0cc2ffd700 r10: 0000000000000008 r11: 0000000000000206
 r12: 00007f0cc2ffbde0 r13: 000000000000004f r14: 00007f0cc2ffbdd8 r15: 00007f0cc2ffbdd0
  di: 0000000000005728  si: 000000000000578b  bp: 00007f0cc2ffb980  bx: 00007f0cc2ffb980
  dx: 0000000000000006  ax: 0000000000000000  cx: 00007f0d2e842428  sp: 00007f0cc2ffb7d8
  ip: 00007f0d2e842428 efl: 0000000000000206 cgf: 002b000000000033 erf: 0000000000000000
 trp: 0000000000000000 msk: 0000000000000000 cr2: 0000000000000000
[end of stack trace]
Calling _exit(1). Core file will not be generated.

I have tried using browser-support and browser-sandbox but this doesn’t resolve the problem.

Is there something else I can try, or does this need changes in the apparmor profiles?

syscall 101 is sys_ptrace. Maybe process-control will help? (although that isn’t desirable, because it is a privileged plug)

I had a separate thread about process-control. However I think I have worked past that by replacing the nwjs runtime shipped with the application with the latest stable one from nwjs.io directly. In devmode the game launches fine, which it never did previously. But strictly I get those /dev/shm browser style failures which I didn’t get before.

I suggest using snappy-debug for pastes (the recent upload has a lot of nice improvements).

For dconf, you need to plugs the gsettings interface.

For the ‘ptrace’ syscall, this is not covered by process-control. ptrace can be used to break out of the seccomp sandbox in kernels < 4.8. However, I suspect this was triggered due to cascading failures (ie, if the shm accesses were allowed, we could allow it).

The shm denials are covered here: Shared memory in /dev/shm rewriting. Does using the snapcraft preload part resolve this? If it does, that is the ideal solution. If it doesn’t, we can consider updating the browser-support interface (it looks like nwjs uses the chromium content api under the hood).

1 Like

This should have been: However, I suspect this was triggered due to cascading failures (ie, if the shm accesses were allowed, nwjs wouldn’t try to ptrace it).

snappy-debug 0.31.6-snapd2.28.5?


= AppArmor =
Time: Oct 27 09:44:31
Log: apparmor="DENIED" operation="open" profile="snap.c3game.c3game" name="/etc/xdg/user-dirs.conf" pid=19989 comm="xdg-user-dirs-u" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0
File: /etc/xdg/user-dirs.conf (read)
Suggestion:
* adjust program to read necessary files from $SNAP, $SNAP_DATA, $SNAP_COMMON, $SNAP_USER_DATA or $SNAP_USER_COMMON

= AppArmor =
Time: Oct 27 09:44:31
Log: apparmor="DENIED" operation="dbus_method_call"  bus="system" path="/" interface="org.freedesktop.DBus.ObjectManager" member="GetManagedObjects" mask="send" name="org.bluez" pid=19918 label="snap.c3game.c3game" peer_pid=3663 peer_label="unconfined"
DBus access

= AppArmor =
Time: Oct 27 09:44:31
Log: apparmor="DENIED" operation="open" profile="snap.c3game.c3game" name="/home/alan/.config/dconf/user" pid=19918 comm="nw" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000
File: /home/alan/.config/dconf/user (read)
Suggestion:
* adjust program to read necessary files from $SNAP, $SNAP_DATA, $SNAP_COMMON, $SNAP_USER_DATA or $SNAP_USER_COMMON

= AppArmor =
Time: Oct 27 09:44:31
Log: apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.BfZpTA" pid=19918 comm="nw" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
File: /dev/shm/.io.nwjs.BfZpTA (write)
Suggestion:
* adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*

= AppArmor =
Time: Oct 27 09:44:31
Log: apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.AzUOg3" pid=19918 comm="Chrome_IOThread" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
File: /dev/shm/.io.nwjs.AzUOg3 (write)
Suggestion:
* adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*

= AppArmor =
Time: Oct 27 09:44:31
Log: apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.ssefEv" pid=19918 comm="Chrome_IOThread" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
File: /dev/shm/.io.nwjs.ssefEv (write)
Suggestion:
* adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*

= AppArmor =
Time: Oct 27 09:44:31
Log: apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.PENI1X" pid=19918 comm="Chrome_IOThread" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
File: /dev/shm/.io.nwjs.PENI1X (write)
Suggestion:
* adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*

= Seccomp =
Time: Oct 27 09:44:31
Log: auid=4294967295 uid=1000 gid=1000 ses=4294967295 pid=20024 comm="Chrome_IOThread" exe="/snap/c3game/x1/nw" sig=31 arch=c000003e 101(ptrace) compat=0 ip=0x7f442748d216 code=0x0
Syscall: ptrace

That doesn’t even build.

Scanning dependencies of target snapcraft-preload32
Scanning dependencies of target snapcraft-preload
[ 25%] Building CXX object CMakeFiles/snapcraft-preload32.dir/preload.cpp.o
[ 50%] Building CXX object CMakeFiles/snapcraft-preload.dir/preload.cpp.o
In file included from /usr/include/dirent.h:25:0,
                 from /root/build_c3game/parts/snapcraft-preload/src/preload.cpp:24:
/usr/include/features.h:367:25: fatal error: sys/cdefs.h: No such file or directory
compilation terminated.
CMakeFiles/snapcraft-preload32.dir/build.make:62: recipe for target 'CMakeFiles/snapcraft-preload32.dir/preload.cpp.o' failed
make[2]: *** [CMakeFiles/snapcraft-preload32.dir/preload.cpp.o] Error 1
CMakeFiles/Makefile2:104: recipe for target 'CMakeFiles/snapcraft-preload32.dir/all' failed
make[1]: *** [CMakeFiles/snapcraft-preload32.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
[ 75%] Linking CXX shared library libsnapcraft-preload.so
[ 75%] Built target snapcraft-preload
Makefile:127: recipe for target 'all' failed
make: *** [all] Error 2

The preload part compilation could be solved by adding gcc-multilib and g+±multilib as build-packages.
see: https://github.com/sergiusens/snapcraft-preload/issues/21

@sergiusens - can you update the README.md file for this?

Thanks @thooxx! Got it building. However it still craps out in similar ways.

Here’s my exec line, is this appropriate?

command: desktop-launch snapcraft-preload $SNAP/nw
$ c3game 
Gtk-Message: Failed to load module "unity-gtk-module"
Gtk-Message: Failed to load module "canberra-gtk-module"
Gtk-Message: Failed to load module "canberra-gtk-module"
shm_open() failed: No such file or directory
[30446:30544:1027/135713.992212:FATAL:memory.cc(22)] Out of memory. size=131072
#0 0x7fdb1c54f327 <unknown>

Failed to generate minidump.Received signal 6
#0 0x7fdb1c54f327 <unknown>
  r8: 00007fdb1964c771  r9: 00007fdab5ffb700 r10: 0000000000000008 r11: 0000000000000206
 r12: 00007fdab5ff9de0 r13: 000000000000004f r14: 00007fdab5ff9dd8 r15: 00007fdab5ff9dd0
  di: 00000000000076ee  si: 0000000000007750  bp: 00007fdab5ff9980  bx: 00007fdab5ff9980
  dx: 0000000000000006  ax: 0000000000000000  cx: 00007fdb192bb428  sp: 00007fdab5ff97d8
  ip: 00007fdb192bb428 efl: 0000000000000206 cgf: 002b000000000033 erf: 0000000000000000
 trp: 0000000000000000 msk: 0000000000000000 cr2: 0000000000000000
[end of stack trace]
Calling _exit(1). Core file will not be generated.

= AppArmor =
Time: Oct 27 13:57:13
Log: apparmor="DENIED" operation="open" profile="snap.c3game.c3game" name="/etc/xdg/user-dirs.conf" pid=30518 comm="xdg-user-dirs-u" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0
File: /etc/xdg/user-dirs.conf (read)
Suggestion:
* adjust program to read necessary files from $SNAP, $SNAP_DATA, $SNAP_COMMON, $SNAP_USER_DATA or $SNAP_USER_COMMON

= AppArmor =
Time: Oct 27 13:57:13
Log: apparmor="DENIED" operation="dbus_method_call"  bus="system" path="/" interface="org.freedesktop.DBus.ObjectManager" member="GetManagedObjects" mask="send" name="org.bluez" pid=30446 label="snap.c3game.c3game" peer_pid=3663 peer_label="unconfined"
DBus access

= AppArmor =
Time: Oct 27 13:57:13
Log: apparmor="DENIED" operation="open" profile="snap.c3game.c3game" name="/home/alan/.config/dconf/user" pid=30446 comm="nw" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000
File: /home/alan/.config/dconf/user (read)
Suggestion:
* adjust program to read necessary files from $SNAP, $SNAP_DATA, $SNAP_COMMON, $SNAP_USER_DATA or $SNAP_USER_COMMON

= AppArmor =
Time: Oct 27 13:57:13
Log: apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.BH65Jf" pid=30446 comm="nw" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
File: /dev/shm/.io.nwjs.BH65Jf (write)
Suggestion:
* adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*

= AppArmor =
Time: Oct 27 13:57:13
Log: apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.RtkF0F" pid=30446 comm="Chrome_IOThread" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
File: /dev/shm/.io.nwjs.RtkF0F (write)
Suggestion:
* adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*

= AppArmor =
Time: Oct 27 13:57:13
Log: apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.5Rwfh6" pid=30446 comm="Chrome_IOThread" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
File: /dev/shm/.io.nwjs.5Rwfh6 (write)
Suggestion:
* adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*

= AppArmor =
Time: Oct 27 13:57:13
Log: apparmor="DENIED" operation="mknod" profile="snap.c3game.c3game" name="/dev/shm/.io.nwjs.oHQQxw" pid=30446 comm="Chrome_IOThread" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
File: /dev/shm/.io.nwjs.oHQQxw (write)
Suggestion:
* adjust program to create files and directories in /dev/shm/snap.$SNAP_NAME.*

= Seccomp =
Time: Oct 27 13:57:13
Log: auid=4294967295 uid=1000 gid=1000 ses=4294967295 pid=30558 comm="Chrome_IOThread" exe="/snap/c3game/x2/nw" sig=31 arch=c000003e 101(ptrace) compat=0 ip=0x7fdb1e731216 code=0x0
Syscall: ptrace

= AppArmor =
Time: Oct 27 13:57:15
Log: apparmor="DENIED" operation="open" profile="snap.steamscript.steamscript" name="/proc/30563/mounts" pid=30563 comm="python3" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000
File: /proc/30563/mounts (read)
Suggestions:
* adjust program to not access '@{PROC}/@{pid}/mounts'
* add one of 'mount-observe, network-control' to 'plugs'

This seems to be very similar to what is being discussed in interProcess - Permission denied when creating named_mutex. There is also: https://bugs.launchpad.net/snapcraft/+bug/1577514/comments/2. Perhaps LD_PRELOAD is getting cleared along the way (Shared memory in /dev/shm rewriting).

As mentioned earlier, nwjs uses the chromium content api. Oxide was adjusted to account for this: https://git.launchpad.net/oxide/commit/?id=9bbf87e80b49e6abcc9419f142655a4ce2fc0638. Ideally nwjs would be updated to include a similar patch. Would they be amenable to that? (FTR, this would also be a nice improvement for electron apps).

As mentioned, we can hack up browser-support for this, but we should only do that as a last resort.

While I certainly could contact nwjs upstream, the problem for now is that most projects use old snapshots of nwjs rather than bleeding edge. It’s a fundamental component used on multiple platforms, so revving it requires a significant effort on their part, for little gain.

I’m also not exactly qualified to patch nwjs, would need to hunt down someone who could help with this.

Sure, but the ultimate goal is to get rid of transitional policy like browser-support and that can only happen if we approach upstreams and have them code for application isolation. Can you approach them?

Alternatively we could have a snapcraft part for nwjs that uses the snapcraft preload techniques for this one access. Did you try the snapcraft preload part?

In the meantime since you said most people use old releases, I’ve created a PR to adjust the browser-support interface.

1 Like

Finally got around to re-testing this. Works a treat now with browser-support.This is a sample construct 3 game running as a strictly confined snap! Thanks @jdstrand!

Screenshot from 2017-12-27 16-57-43

2 Likes

can i see the snapcraft file ? Am also building something in nwjs :slight_smile:

@popey ??? :frowning:

Here you go. The ./game folder contains the usual game assets…

$ ls ./game
assets  index.html  js
name: gamename # short name for game, all lower case
version: '1.0' # arbitrary version number, can be any text
summary: Game Name # Short human readable name of the application
description: |
  This is a game made with Phaser. # Multi-line 79 column wide description of game

grade: stable
confinement: strict # strictly confined application

# The apps section exposes a command to the running system
# The plugs allow access to the network, display, sound and input
# devices
apps:
  gamename:
    command: desktop-launch $SNAP/nw
    plugs:
      - network
      - network-bind
      - x11
      - wayland
      - desktop
      - opengl
      - pulseaudio
      - browser-support
      - screen-inhibit-control
      - joystick

# 3 parts are needed
# metadata: directory containing a package.json used by nwjs to describe the application
# and set the initial screen resolution of the window
# nwjs: dumps the build of nwjs into the snap as-is. This was just downloaded from the nwjs
# website and is unchanged. Will get unpacked into the snap
# game: the actual unpacked game itself. Will also get dumped into the root of the snap
# along with nwjs.
parts:
  metadata:
    plugin: dump
    source: metadata/
  nwjs:
    plugin: dump
    source: https://dl.nwjs.io/v0.31.0/nwjs-v0.31.0-linux-x64.tar.gz
  game:
    plugin: dump
    source: ./game
    after:
      - nwjs-support
      - desktop-gtk3
1 Like

thanks man :slight_smile:
god bless you :kissing_heart: I used desktop-qt5 instead of desktop-gtk3 and it worked fine used that because my app need some qt5 stuff too.

1 Like