unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Guix, Nix flakes, and object capabilities
@ 2023-03-01  3:13 Jonathan Frederickson
  2023-03-01  9:56 ` Josselin Poiret
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Jonathan Frederickson @ 2023-03-01  3:13 UTC (permalink / raw)
  To: guix-devel; +Cc: christine

Hello Guix,

I recently had a discussion in #spritely on Libera.Chat about Guix and
Nix, and in particular a (relatively) new feature of Nix called flakes
that Guix doesn't currently have an analogue for.

I've been a Guix user for a while, but I've only recently started
looking at using Guix for development via ~guix shell~ as in this blog
post by David Thompson[0]. The Guile port of Spritely has been using it,
so I've been trying it out for one of my own projects as a result. And
it seems pretty nice; you're using the same package definitions you
might use when contributing a package upstream to Guix, which feels
pretty natural as someone already pretty familiar with Guix as a user.

However, I noticed something about the resulting dependency graph that
feels somewhat unsatisfying. When you define a package, the
dependencies you provide in e.g. ~inputs~ are references to
packages. And the way you get those is, of course, by importing
modules containing those packages.

But the package you end up with each time you do that... depends on
which revision of Guix you're running when you run ~guix shell~! So if
I point someone to a project with a ~guix.scm~ file, they might not be
able to use it if their Guix revision is too old. (Or too new, if
packages have been renamed or removed.) More generally, it means that
they do not end up with the same dependency graph that I do. This
makes troubleshooting potentially tricky, because if something breaks
you have to check the resulting profile to see which versions of your
package's dependencies (and transitive dependencies) are actually
installed.

For those who haven't used Nix, it has a solution to this called
flakes. Flakes let you specify git repositories explicitly as inputs for
your project[1]. (It also maintains a flake.lock file so you can lock to
a specific revision automatically while still using a named branch in
your inputs directly, but I believe you could in theory refer to a
specific rev in your inputs.) Effectively, the channels you're using for
dependencies are specified by the project you're building, not whatever
happens to be configured on your local machine.

I think something like this would be useful for Guix for many of the
same reasons it's useful in Nix. But there's a bit of a security
conundrum here. Loading Guix package definitions involves code
execution, which as far as I can tell isn't currently sandboxed at all!
And that's a problem. When you load package definitions from a channel
that you've configured on your system, you've explicitly trusted that
channel's maintainers. But with a flake-like system... even if you might
be okay depending on someone else's code, that doesn't necessarily mean
you fully trust them. You might ultimately choose to sandbox the
resulting binary, but that's moot if you can't fetch its dependencies
without running arbitrary code with all of your user's authority.

I think there is a solution to this, though. Right now when you
evaluate Guix package definitions, you're basically running arbitrary
Guile code. This of course can do anything you can do. But it doesn't
have to! If you're familiar with Christine Lemmer-Webber's work on
Spritely, you'll probably know what I'm getting at here: I think using
object capabilities[2] would fix this. I recommend reading the linked
blog post for a good explainer on what object capabilities are, as I
won't do it justice here, but to perhaps oversimplify: code in a
capability system only has access to the things you give it, and no
more. It's like lexical scope, but taken very seriously.

If you think about what a typical package definition needs to be able to
do to your system directly, I think it's not actually that much? My
(admittedly basic, possibly flawed) understanding of how Guix works is
that most of the heavy lifting is done by ~guix-daemon~, which itself is
pretty heavily sandboxed, and that most of what the ~guix~ subcommands
are doing is building derivations which instruct ~guix-daemon~ to
perform build actions. So while you're building these derivations,
unless I'm misunderstanding:

- You don't need network access
- You don't need (much) filesystem access

I think object capabilities provide a good answer to this
problem. Rather than evaluating package definitions from a channel as
you would normally run Guile code, evaluate them in a restricted
environment that only has access to things you've passed in. In
JavaScript, this might look like this (taken from this blog post[3]
about the event-stream incident):

#+BEGIN_SRC javascript
  const addHeader = require('./addHeader', {fs, https});
#+END_SRC

This way, you could import modules including packages you'd like to
use as dependencies, and if you don't pass those modules access to the
rest of your filesystem they won't have it, and can't do things like
cryptolocker your home directory. (At least not until you run some
software installed from it, but that's a separate issue!)

Of course, easier said than done. Guile's import system doesn't work
like this. But I believe the Spritely project has a module system like
this planned for Guile, which could enable things like this. I'm sure
such a thing would be a lot of work, but I hope this plants a seed in
your minds as to what might be possible.

[0] https://dthompson.us/guix-for-development.html
[1] https://nixos.wiki/wiki/Flakes#Introduction
[2] http://habitatchronicles.com/2017/05/what-are-capabilities/
[3] https://medium.com/agoric/pola-would-have-prevented-the-event-stream-incident-45653ecbda99

-- 
Jonathan Frederickson


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Guix, Nix flakes, and object capabilities
  2023-03-01  3:13 Guix, Nix flakes, and object capabilities Jonathan Frederickson
@ 2023-03-01  9:56 ` Josselin Poiret
  2023-03-03  1:12 ` Simon Tournier
  2023-03-06 18:29 ` Tobias Platen
  2 siblings, 0 replies; 4+ messages in thread
From: Josselin Poiret @ 2023-03-01  9:56 UTC (permalink / raw)
  To: Jonathan Frederickson, guix-devel; +Cc: christine

[-- Attachment #1: Type: text/plain, Size: 2597 bytes --]

Hi Jonathan,

I'll only address the first part, because we already have a nice
solution to that.

Jonathan Frederickson <jonathan@terracrypt.net> writes:

> Hello Guix,
>
> I recently had a discussion in #spritely on Libera.Chat about Guix and
> Nix, and in particular a (relatively) new feature of Nix called flakes
> that Guix doesn't currently have an analogue for.
>
> I've been a Guix user for a while, but I've only recently started
> looking at using Guix for development via ~guix shell~ as in this blog
> post by David Thompson[0]. The Guile port of Spritely has been using it,
> so I've been trying it out for one of my own projects as a result. And
> it seems pretty nice; you're using the same package definitions you
> might use when contributing a package upstream to Guix, which feels
> pretty natural as someone already pretty familiar with Guix as a user.
>
> However, I noticed something about the resulting dependency graph that
> feels somewhat unsatisfying. When you define a package, the
> dependencies you provide in e.g. ~inputs~ are references to
> packages. And the way you get those is, of course, by importing
> modules containing those packages.
>
> But the package you end up with each time you do that... depends on
> which revision of Guix you're running when you run ~guix shell~! So if
> I point someone to a project with a ~guix.scm~ file, they might not be
> able to use it if their Guix revision is too old. (Or too new, if
> packages have been renamed or removed.) More generally, it means that
> they do not end up with the same dependency graph that I do. This
> makes troubleshooting potentially tricky, because if something breaks
> you have to check the resulting profile to see which versions of your
> package's dependencies (and transitive dependencies) are actually
> installed.

But we do have a nice solution for this problem: `guix time-machine`!
You can use a Guix from any commit you want alongside other channels, by
just knowing what commits people are using. If projects do rely on
specific versions of dependencies, they can distribute a channels.scm
file to be consumed by `guix time-machine`.

Now my personal opinion: you should note that it's a very bad idea in
general to rely on specific versions of dependencies. Downstream
consumers of your software should be able to use it with up-to-date
dependencies, because they can provide security benefits, bugfixes,
etc. Having version pinning like in go leads to dependency hell, and
should be frowned upon.

Best,
-- 
Josselin Poiret

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 682 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Guix, Nix flakes, and object capabilities
  2023-03-01  3:13 Guix, Nix flakes, and object capabilities Jonathan Frederickson
  2023-03-01  9:56 ` Josselin Poiret
@ 2023-03-03  1:12 ` Simon Tournier
  2023-03-06 18:29 ` Tobias Platen
  2 siblings, 0 replies; 4+ messages in thread
From: Simon Tournier @ 2023-03-03  1:12 UTC (permalink / raw)
  To: Jonathan Frederickson, guix-devel, Josselin Poiret; +Cc: christine

Hi,

On Tue, 28 Feb 2023 at 22:13, Jonathan Frederickson <jonathan@terracrypt.net> wrote:

> I recently had a discussion in #spritely on Libera.Chat about Guix and
> Nix, and in particular a (relatively) new feature of Nix called flakes
> that Guix doesn't currently have an analogue for.

Well, even after several readings of Nix documentation about Flakes, I
am still not sure to understand how the concept would apply for Guix. :-)


> But the package you end up with each time you do that... depends on
> which revision of Guix you're running when you run ~guix shell~! So if
> I point someone to a project with a ~guix.scm~ file, they might not be
> able to use it if their Guix revision is too old. (Or too new, if
> packages have been renamed or removed.) More generally, it means that
> they do not end up with the same dependency graph that I do. This
> makes troubleshooting potentially tricky, because if something breaks
> you have to check the resulting profile to see which versions of your
> package's dependencies (and transitive dependencies) are actually
> installed.

That’s why “guix time-machine” is really cool! :-)

Basically, I have my current Guix revision and I run “guix pull” every…
indecent duration.  However, I run many many “guix time-machine”, well
each time I am working on a project, so daily.

Even, I only run “guix pull” against the default Savannah Guix channel.
Then, depending on the projects, they can require the channels
guix-science, guix-cran or others.

The graph I need for a specific project is controlled by the file
channels.scm and I run, from the directory of that project:

    guix time-machine -C channels.scm -- shell

This channels.scm file can be part of the Git repository of that
project.  And sometimes, I have several files for testing revision
variants.

On the top of that, for some cases, I have specific packages for one
project.  Therefore, I have a folder, say guix/extra containing one or
more files which define new or variant packages.

Somehow, my typical line looks like:

  guix time-machine -C state-1.scm -- shell -L guix/extra -m manifest-A.scm
  guix time-machine -C state-2.scm -- shell -L guix/extra -f guix-B.scm

etc.

(Although, I should admit that I barely use the option -f. ;-))

> For those who haven't used Nix, it has a solution to this called
> flakes. Flakes let you specify git repositories explicitly as inputs for
> your project[1]. (It also maintains a flake.lock file so you can lock to
> a specific revision automatically while still using a named branch in
> your inputs directly, but I believe you could in theory refer to a
> specific rev in your inputs.) Effectively, the channels you're using for
> dependencies are specified by the project you're building, not whatever
> happens to be configured on your local machine.

From my understanding, what I describe above seems providing these
Nix flakes, no?

Or could you explain on a concrete example what you can do with Nix
flakes that you cannot do with Guix?

Well, UI and how easy to use can also be considered part of “can
do”. :-) I mean, maybe the current Guix way could be improved on the
light of the Nix flakes.


> I think something like this would be useful for Guix for many of the
> same reasons it's useful in Nix. But there's a bit of a security
> conundrum here. Loading Guix package definitions involves code
> execution, which as far as I can tell isn't currently sandboxed at all!
> And that's a problem. When you load package definitions from a channel
> that you've configured on your system, you've explicitly trusted that
> channel's maintainers. But with a flake-like system... even if you might
> be okay depending on someone else's code, that doesn't necessarily mean
> you fully trust them. You might ultimately choose to sandbox the
> resulting binary, but that's moot if you can't fetch its dependencies
> without running arbitrary code with all of your user's authority.

I am not sure to understand this sandbox part.  For instance,

    guix time-machine -C channels.scm -- shell --container

provides you an isolated environment where you can run untrusted
packages coming from untrusted channels.  Even, the option ’-F,
--emulate-fhs’ combined with the option ’-C, --container’ allow to run
untrusted binaries coming from elsewhere (not built by Guix); you need
some care with the options -E and --expose though.

Do you think something is missing?  What could we improve in this area?

Do you mean evaluate the Guile files provided by the untrusted channels
in a sandbox?


> I think there is a solution to this, though. Right now when you
> evaluate Guix package definitions, you're basically running arbitrary
> Guile code. This of course can do anything you can do. But it doesn't
> have to! If you're familiar with Christine Lemmer-Webber's work on
> Spritely, you'll probably know what I'm getting at here: I think using
> object capabilities[2] would fix this. I recommend reading the linked
> blog post for a good explainer on what object capabilities are, as I
> won't do it justice here, but to perhaps oversimplify: code in a
> capability system only has access to the things you give it, and no
> more. It's like lexical scope, but taken very seriously.

Hum, I should have missed some details – since I am not familiar with
“object capability”.  Thanks for the pointer.

Is it another way to view “contract” [a]?

a: <https://docs.racket-lang.org/reference/contracts.html>


> If you think about what a typical package definition needs to be able to
> do to your system directly, I think it's not actually that much? My
> (admittedly basic, possibly flawed) understanding of how Guix works is
> that most of the heavy lifting is done by ~guix-daemon~, which itself is
> pretty heavily sandboxed, and that most of what the ~guix~ subcommands
> are doing is building derivations which instruct ~guix-daemon~ to
> perform build actions. So while you're building these derivations,
> unless I'm misunderstanding:
>
> - You don't need network access
> - You don't need (much) filesystem access

From my understanding, Guix and guix-daemon already works that way, no?

Do you mean evaluate the user-side Guile part of “guix foo -f file.scm”
inside a sandboxed environment?


On Wed, 01 Mar 2023 at 10:56, Josselin Poiret <dev@jpoiret.xyz> wrote:

> Now my personal opinion: you should note that it's a very bad idea in
> general to rely on specific versions of dependencies. Downstream
> consumers of your software should be able to use it with up-to-date
> dependencies, because they can provide security benefits, bugfixes,
> etc. Having version pinning like in go leads to dependency hell, and
> should be frowned upon.

Well, it depends on what you want, IMHO. :-)

Sometimes, you want to reproduce the exact same computational
environment, including all the defects.

From my personal opinion, channels.scm and manifests.scm, probably
considering also transformations, these features allow – putting more
than less efforts – to achieve a fine control with flexibility.


Cheers,
simon


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Guix, Nix flakes, and object capabilities
  2023-03-01  3:13 Guix, Nix flakes, and object capabilities Jonathan Frederickson
  2023-03-01  9:56 ` Josselin Poiret
  2023-03-03  1:12 ` Simon Tournier
@ 2023-03-06 18:29 ` Tobias Platen
  2 siblings, 0 replies; 4+ messages in thread
From: Tobias Platen @ 2023-03-06 18:29 UTC (permalink / raw)
  To: guix-devel

I recently saw that the SlimeVR server[1] has nix flakes, is there a
way to convert this for guix? 

On Tue, 2023-02-28 at 22:13 -0500, Jonathan Frederickson wrote:
> Hello Guix,
> 
> I recently had a discussion in #spritely on Libera.Chat about Guix
> and
> Nix, and in particular a (relatively) new feature of Nix called
> flakes
> that Guix doesn't currently have an analogue for.
> 
> I've been a Guix user for a while, but I've only recently started
> looking at using Guix for development via ~guix shell~ as in this
> blog
> post by David Thompson[0]. The Guile port of Spritely has been using
> it,
> so I've been trying it out for one of my own projects as a result.
> And
> it seems pretty nice; you're using the same package definitions you
> might use when contributing a package upstream to Guix, which feels
> pretty natural as someone already pretty familiar with Guix as a
> user.
> 
> However, I noticed something about the resulting dependency graph
> that
> feels somewhat unsatisfying. When you define a package, the
> dependencies you provide in e.g. ~inputs~ are references to
> packages. And the way you get those is, of course, by importing
> modules containing those packages.
> 
> But the package you end up with each time you do that... depends on
> which revision of Guix you're running when you run ~guix shell~! So
> if
> I point someone to a project with a ~guix.scm~ file, they might not
> be
> able to use it if their Guix revision is too old. (Or too new, if
> packages have been renamed or removed.) More generally, it means that
> they do not end up with the same dependency graph that I do. This
> makes troubleshooting potentially tricky, because if something breaks
> you have to check the resulting profile to see which versions of your
> package's dependencies (and transitive dependencies) are actually
> installed.
> 
> For those who haven't used Nix, it has a solution to this called
> flakes. Flakes let you specify git repositories explicitly as inputs
> for
> your project[1]. (It also maintains a flake.lock file so you can lock
> to
> a specific revision automatically while still using a named branch in
> your inputs directly, but I believe you could in theory refer to a
> specific rev in your inputs.) Effectively, the channels you're using
> for
> dependencies are specified by the project you're building, not
> whatever
> happens to be configured on your local machine.
> 
> I think something like this would be useful for Guix for many of the
> same reasons it's useful in Nix. But there's a bit of a security
> conundrum here. Loading Guix package definitions involves code
> execution, which as far as I can tell isn't currently sandboxed at
> all!
> And that's a problem. When you load package definitions from a
> channel
> that you've configured on your system, you've explicitly trusted that
> channel's maintainers. But with a flake-like system... even if you
> might
> be okay depending on someone else's code, that doesn't necessarily
> mean
> you fully trust them. You might ultimately choose to sandbox the
> resulting binary, but that's moot if you can't fetch its dependencies
> without running arbitrary code with all of your user's authority.
> 
> I think there is a solution to this, though. Right now when you
> evaluate Guix package definitions, you're basically running arbitrary
> Guile code. This of course can do anything you can do. But it doesn't
> have to! If you're familiar with Christine Lemmer-Webber's work on
> Spritely, you'll probably know what I'm getting at here: I think
> using
> object capabilities[2] would fix this. I recommend reading the linked
> blog post for a good explainer on what object capabilities are, as I
> won't do it justice here, but to perhaps oversimplify: code in a
> capability system only has access to the things you give it, and no
> more. It's like lexical scope, but taken very seriously.
> 
> If you think about what a typical package definition needs to be able
> to
> do to your system directly, I think it's not actually that much? My
> (admittedly basic, possibly flawed) understanding of how Guix works
> is
> that most of the heavy lifting is done by ~guix-daemon~, which itself
> is
> pretty heavily sandboxed, and that most of what the ~guix~
> subcommands
> are doing is building derivations which instruct ~guix-daemon~ to
> perform build actions. So while you're building these derivations,
> unless I'm misunderstanding:
> 
> - You don't need network access
> - You don't need (much) filesystem access
> 
> I think object capabilities provide a good answer to this
> problem. Rather than evaluating package definitions from a channel as
> you would normally run Guile code, evaluate them in a restricted
> environment that only has access to things you've passed in. In
> JavaScript, this might look like this (taken from this blog post[3]
> about the event-stream incident):
> 
> #+BEGIN_SRC javascript
>   const addHeader = require('./addHeader', {fs, https});
> #+END_SRC
> 
> This way, you could import modules including packages you'd like to
> use as dependencies, and if you don't pass those modules access to
> the
> rest of your filesystem they won't have it, and can't do things like
> cryptolocker your home directory. (At least not until you run some
> software installed from it, but that's a separate issue!)
> 
> Of course, easier said than done. Guile's import system doesn't work
> like this. But I believe the Spritely project has a module system
> like
> this planned for Guile, which could enable things like this. I'm sure
> such a thing would be a lot of work, but I hope this plants a seed in
> your minds as to what might be possible.
> 
> [0] https://dthompson.us/guix-for-development.html
> [1] https://nixos.wiki/wiki/Flakes#Introduction
> [2] http://habitatchronicles.com/2017/05/what-are-capabilities/
> [3]
> https://medium.com/agoric/pola-would-have-prevented-the-event-stream-incident-45653ecbda99
> 



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2023-03-06 18:29 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-01  3:13 Guix, Nix flakes, and object capabilities Jonathan Frederickson
2023-03-01  9:56 ` Josselin Poiret
2023-03-03  1:12 ` Simon Tournier
2023-03-06 18:29 ` Tobias Platen

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).