openvibers/openvibe.link Commits
Refuse net jobs on hosts without egress and probe veth only when set
Changes
@@@ -93,9 +93,11 @@ the server. The worker is **off by default**; off, the Node refuses every job `c`openvibe-only`: `public`, further restricted to `egress_allow`. A job runs under the **stricter** of the `net` thecontract's job names and this. `net` absent means `deny` (`platform.job@1`'s declared default), so **a job that namesno `net` runs with no network at all** however open this is set: the Node never widens the contract's default. A job-- naming `deny` or `none` gets the same; one naming `public` or `openvibe-only` runs under `egress` when that allows-- that much (`public` under `openvibe-only`, say, runs as `public`) and is **refused** with `net policy not supported`-- when `egress` is `none`: it is never run with less than was asked. A policy the host cannot enforce fails the++ naming `deny` or `none` gets the same. One naming `public` runs under `egress` when that is `public` or++ `openvibe-only` (so under `openvibe-only` it is restricted to `egress_allow`); one naming `openvibe-only` needs++ `egress` `openvibe-only` (a `public` host has no `egress_allow` to hold it to). Otherwise, `egress` `none` (or unset)++ included, the job is **refused** with `net policy not supported`: it is never run with no network, nor with a wider++ one, in place of what it asked. A policy the host cannot enforce fails theprobe and keeps the worker off: never a silent `none` nor an open network.- `egress_allow` (with `openvibe-only` only, and required by it): the IPv4 CIDRs a job may reach, the OpenVibenetwork's own ranges. They are configured, not guessed nor resolved from names: the Node trusts no DNS answer for@@@ -150,24 +150,6 @@ const (EgressOpenVibeOnly = "openvibe-only")--// StrictestEgress returns the stricter of two worker.egress policies: openvibe-only is stricter than public, public--// than none. An unknown value ranks as none, the strictest that is still enforceable.--func StrictestEgress(a, b string) string {-- rank := func(p string) int {-- switch p {-- case EgressPublic:-- return 1-- case EgressOpenVibeOnly:-- return 2-- }-- return 0-- }-- if rank(a) <= rank(b) {-- return a-- }-- return b--}--// FunctionConfig is one artifact a job may run: Command is started with the job's args as JSON on stdin.type FunctionConfig struct {Name string `json:"name"`@@@ -63,23 +63,25 @@ type plan struct {egress string // the job's network policy (egressFor); "" is none}--// egressFor is the network policy a job runs under: the stricter of the net the job names and the host's worker.egress.--// A net of deny (or none, the same policy as the Node spells it), which is also what an absent net means in--// platform.job@1, runs under no network whatever the host allows: the contract's default is deny and the Node never--// widens it. A named public or openvibe-only still needs the host to allow that much, so a host under none refuses--// the job at Admit rather than running it with less than was asked.++// egressFor is the network policy a job runs under: the stricter of the net the job names and the host's worker.egress,++// which must be the host's own, the only ruleset its veth is written with. A net of deny (or none, the same policy as++// the Node spells it), which is also what an absent net means in platform.job@1, runs under no network whatever the++// host allows: the contract's default is deny and the Node never widens it. A named public runs under a public or++// openvibe-only host's policy; a named openvibe-only needs an openvibe-only host (a public one has no egress_allow to++// restrict it to). Anything else, a host under none (or no worker.egress, the same) included, refuses the job at++// Admit rather than running it with less, or more, than was asked.func egressFor(host, net string) (string, bool) {switch net {case "", protocol.NetDeny, protocol.NetNone:return "", true-- case protocol.NetPublic, protocol.NetOpenVibeOnly:-- if host == config.EgressNone {-- return "", false++ case protocol.NetPublic:++ if host == config.EgressPublic || host == config.EgressOpenVibeOnly {++ return host, true}-- if host == net {++ case protocol.NetOpenVibeOnly:++ if host == config.EgressOpenVibeOnly {return host, true}-- return config.StrictestEgress(host, net), true}return "", false}@@@ -272,7 +272,7 @@ func (s *sandbox) close() {// filter; nothing but its /tmp, /proc and device nodes is mounted writable; it is in its job cgroup; its rlimits// (RLIMIT_AS included: max_vm_bytes must be set) are set; every block device has its io.max; its root holds none of// the Node's directories nor hiddenPaths (/var, the host's private keys); its CPU limit can be set and its usage read.--// It does so for every policy a job may run under: none (a job naming net "deny") and worker.egress, whose veth must++// It does so for every policy a job may run under: none (a job whose net is absent, deny or none) and worker.egress, whose veth must// then stand as the only interface up with the default route through it, the Node's nftables table written. It also// checks that no declared function's artifact directory is refused. Any failure keeps the class off: there is no// weaker mode, never none in place of public nor an open network.@@@ -311,7 +311,7 @@ func probe(cfg config.WorkerConfig, isolate isolateFunc) error {if err := probeSandbox(cfg, isolate, ""); err != nil {return err}-- if egress != "" {++ if egress != config.EgressNone {return probeSandbox(cfg, isolate, egress)}return nil@@@ -290,8 +290,9 @@ func TestUsageHelperProcess(t *testing.T) {}// TestEgressFor: an absent net (the contract's declared deny) is no network whatever the host allows; net deny and--// none are that too; a named public or openvibe-only runs under the stricter of itself and the host's, and is refused--// when the host is none; a net this Node does not know is refused, never run weaker.++// none are that too; a named public or openvibe-only runs under the host's policy when that is at least as strict and++// still a network, and is refused when the host is none (or unset) or wider than asked; a net this Node does not know++// is refused, never run weaker.func TestEgressFor(t *testing.T) {for _, c := range []struct {host, net string@@@ -302,11 +303,12 @@ func TestEgressFor(t *testing.T) {{config.EgressOpenVibeOnly, "", "", true},{config.EgressPublic, protocol.NetDeny, "", true}, {config.EgressOpenVibeOnly, protocol.NetNone, "", true},{config.EgressPublic, protocol.NetPublic, config.EgressPublic, true},-- {config.EgressPublic, protocol.NetOpenVibeOnly, config.EgressPublic, true},-- {config.EgressOpenVibeOnly, protocol.NetPublic, config.EgressPublic, true},++ {config.EgressPublic, protocol.NetOpenVibeOnly, "", false},++ {config.EgressOpenVibeOnly, protocol.NetPublic, config.EgressOpenVibeOnly, true},{config.EgressOpenVibeOnly, protocol.NetOpenVibeOnly, config.EgressOpenVibeOnly, true},{config.EgressNone, protocol.NetPublic, "", false},{config.EgressNone, protocol.NetOpenVibeOnly, "", false},++ {"", protocol.NetPublic, "", false}, {"", protocol.NetOpenVibeOnly, "", false},{config.EgressPublic, "allow", "", false}, {config.EgressPublic, "", "", true},} {got, ok := egressFor(c.host, c.net)
@@ -93,9 +93,11 @@ the server. The worker is **off by default**; off, the Node refuses every job `c
@@ -93,9 +93,11 @@ the server. The worker is **off by default**; off, the Node refuses every job `c
`openvibe-only`: `public`, further restricted to `egress_allow`. A job runs under the **stricter** of the `net` the
`openvibe-only`: `public`, further restricted to `egress_allow`. A job runs under the **stricter** of the `net` the
contract's job names and this. `net` absent means `deny` (`platform.job@1`'s declared default), so **a job that names
contract's job names and this. `net` absent means `deny` (`platform.job@1`'s declared default), so **a job that names
no `net` runs with no network at all** however open this is set: the Node never widens the contract's default. A job
no `net` runs with no network at all** however open this is set: the Node never widens the contract's default. A job
- naming `deny` or `none` gets the same; one naming `public` or `openvibe-only` runs under `egress` when that allows
- that much (`public` under `openvibe-only`, say, runs as `public`) and is **refused** with `net policy not supported`
- when `egress` is `none`: it is never run with less than was asked. A policy the host cannot enforce fails the
+ naming `deny` or `none` gets the same. One naming `public` runs under `egress` when that is `public` or
+ `openvibe-only` (so under `openvibe-only` it is restricted to `egress_allow`); one naming `openvibe-only` needs
+ `egress` `openvibe-only` (a `public` host has no `egress_allow` to hold it to). Otherwise, `egress` `none` (or unset)
+ included, the job is **refused** with `net policy not supported`: it is never run with no network, nor with a wider
+ one, in place of what it asked. A policy the host cannot enforce fails the
probe and keeps the worker off: never a silent `none` nor an open network.
probe and keeps the worker off: never a silent `none` nor an open network.
- `egress_allow` (with `openvibe-only` only, and required by it): the IPv4 CIDRs a job may reach, the OpenVibe
- `egress_allow` (with `openvibe-only` only, and required by it): the IPv4 CIDRs a job may reach, the OpenVibe
network's own ranges. They are configured, not guessed nor resolved from names: the Node trusts no DNS answer for
network's own ranges. They are configured, not guessed nor resolved from names: the Node trusts no DNS answer for
@@ -150,24 +150,6 @@ const (
@@ -150,24 +150,6 @@ const (
EgressOpenVibeOnly = "openvibe-only"
EgressOpenVibeOnly = "openvibe-only"
)
)
-// StrictestEgress returns the stricter of two worker.egress policies: openvibe-only is stricter than public, public
-// than none. An unknown value ranks as none, the strictest that is still enforceable.
-func StrictestEgress(a, b string) string {
- rank := func(p string) int {
- switch p {
- case EgressPublic:
- return 1
- case EgressOpenVibeOnly:
- return 2
- }
- return 0
- }
- if rank(a) <= rank(b) {
- return a
- }
- return b
-}
-
// FunctionConfig is one artifact a job may run: Command is started with the job's args as JSON on stdin.
// FunctionConfig is one artifact a job may run: Command is started with the job's args as JSON on stdin.
type FunctionConfig struct {
type FunctionConfig struct {
Name string `json:"name"`
Name string `json:"name"`
@@ -63,23 +63,25 @@ type plan struct {
@@ -63,23 +63,25 @@ type plan struct {
egress string // the job's network policy (egressFor); "" is none
egress string // the job's network policy (egressFor); "" is none
}
}
-// egressFor is the network policy a job runs under: the stricter of the net the job names and the host's worker.egress.
-// A net of deny (or none, the same policy as the Node spells it), which is also what an absent net means in
-// platform.job@1, runs under no network whatever the host allows: the contract's default is deny and the Node never
-// widens it. A named public or openvibe-only still needs the host to allow that much, so a host under none refuses
-// the job at Admit rather than running it with less than was asked.
+// egressFor is the network policy a job runs under: the stricter of the net the job names and the host's worker.egress,
+// which must be the host's own, the only ruleset its veth is written with. A net of deny (or none, the same policy as
+// the Node spells it), which is also what an absent net means in platform.job@1, runs under no network whatever the
+// host allows: the contract's default is deny and the Node never widens it. A named public runs under a public or
+// openvibe-only host's policy; a named openvibe-only needs an openvibe-only host (a public one has no egress_allow to
+// restrict it to). Anything else, a host under none (or no worker.egress, the same) included, refuses the job at
+// Admit rather than running it with less, or more, than was asked.
func egressFor(host, net string) (string, bool) {
func egressFor(host, net string) (string, bool) {
switch net {
switch net {
case "", protocol.NetDeny, protocol.NetNone:
case "", protocol.NetDeny, protocol.NetNone:
return "", true
return "", true
- case protocol.NetPublic, protocol.NetOpenVibeOnly:
- if host == config.EgressNone {
- return "", false
+ case protocol.NetPublic:
+ if host == config.EgressPublic || host == config.EgressOpenVibeOnly {
+ return host, true
}
}
- if host == net {
+ case protocol.NetOpenVibeOnly:
+ if host == config.EgressOpenVibeOnly {
return host, true
return host, true
}
}
- return config.StrictestEgress(host, net), true
}
}
return "", false
return "", false
}
}
@@ -272,7 +272,7 @@ func (s *sandbox) close() {
@@ -272,7 +272,7 @@ func (s *sandbox) close() {
// filter; nothing but its /tmp, /proc and device nodes is mounted writable; it is in its job cgroup; its rlimits
// filter; nothing but its /tmp, /proc and device nodes is mounted writable; it is in its job cgroup; its rlimits
// (RLIMIT_AS included: max_vm_bytes must be set) are set; every block device has its io.max; its root holds none of
// (RLIMIT_AS included: max_vm_bytes must be set) are set; every block device has its io.max; its root holds none of
// the Node's directories nor hiddenPaths (/var, the host's private keys); its CPU limit can be set and its usage read.
// the Node's directories nor hiddenPaths (/var, the host's private keys); its CPU limit can be set and its usage read.
-// It does so for every policy a job may run under: none (a job naming net "deny") and worker.egress, whose veth must
+// It does so for every policy a job may run under: none (a job whose net is absent, deny or none) and worker.egress, whose veth must
// then stand as the only interface up with the default route through it, the Node's nftables table written. It also
// then stand as the only interface up with the default route through it, the Node's nftables table written. It also
// checks that no declared function's artifact directory is refused. Any failure keeps the class off: there is no
// checks that no declared function's artifact directory is refused. Any failure keeps the class off: there is no
// weaker mode, never none in place of public nor an open network.
// weaker mode, never none in place of public nor an open network.
@@ -311,7 +311,7 @@ func probe(cfg config.WorkerConfig, isolate isolateFunc) error {
@@ -311,7 +311,7 @@ func probe(cfg config.WorkerConfig, isolate isolateFunc) error {
if err := probeSandbox(cfg, isolate, ""); err != nil {
if err := probeSandbox(cfg, isolate, ""); err != nil {
return err
return err
}
}
- if egress != "" {
+ if egress != config.EgressNone {
return probeSandbox(cfg, isolate, egress)
return probeSandbox(cfg, isolate, egress)
}
}
return nil
return nil
@@ -290,8 +290,9 @@ func TestUsageHelperProcess(t *testing.T) {
@@ -290,8 +290,9 @@ func TestUsageHelperProcess(t *testing.T) {
}
}
// TestEgressFor: an absent net (the contract's declared deny) is no network whatever the host allows; net deny and
// TestEgressFor: an absent net (the contract's declared deny) is no network whatever the host allows; net deny and
-// none are that too; a named public or openvibe-only runs under the stricter of itself and the host's, and is refused
-// when the host is none; a net this Node does not know is refused, never run weaker.
+// none are that too; a named public or openvibe-only runs under the host's policy when that is at least as strict and
+// still a network, and is refused when the host is none (or unset) or wider than asked; a net this Node does not know
+// is refused, never run weaker.
func TestEgressFor(t *testing.T) {
func TestEgressFor(t *testing.T) {
for _, c := range []struct {
for _, c := range []struct {
host, net string
host, net string
@@ -302,11 +303,12 @@ func TestEgressFor(t *testing.T) {
@@ -302,11 +303,12 @@ func TestEgressFor(t *testing.T) {
{config.EgressOpenVibeOnly, "", "", true},
{config.EgressOpenVibeOnly, "", "", true},
{config.EgressPublic, protocol.NetDeny, "", true}, {config.EgressOpenVibeOnly, protocol.NetNone, "", true},
{config.EgressPublic, protocol.NetDeny, "", true}, {config.EgressOpenVibeOnly, protocol.NetNone, "", true},
{config.EgressPublic, protocol.NetPublic, config.EgressPublic, true},
{config.EgressPublic, protocol.NetPublic, config.EgressPublic, true},
- {config.EgressPublic, protocol.NetOpenVibeOnly, config.EgressPublic, true},
- {config.EgressOpenVibeOnly, protocol.NetPublic, config.EgressPublic, true},
+ {config.EgressPublic, protocol.NetOpenVibeOnly, "", false},
+ {config.EgressOpenVibeOnly, protocol.NetPublic, config.EgressOpenVibeOnly, true},
{config.EgressOpenVibeOnly, protocol.NetOpenVibeOnly, config.EgressOpenVibeOnly, true},
{config.EgressOpenVibeOnly, protocol.NetOpenVibeOnly, config.EgressOpenVibeOnly, true},
{config.EgressNone, protocol.NetPublic, "", false},
{config.EgressNone, protocol.NetPublic, "", false},
{config.EgressNone, protocol.NetOpenVibeOnly, "", false},
{config.EgressNone, protocol.NetOpenVibeOnly, "", false},
+ {"", protocol.NetPublic, "", false}, {"", protocol.NetOpenVibeOnly, "", false},
{config.EgressPublic, "allow", "", false}, {config.EgressPublic, "", "", true},
{config.EgressPublic, "allow", "", false}, {config.EgressPublic, "", "", true},
} {
} {
got, ok := egressFor(c.host, c.net)
got, ok := egressFor(c.host, c.net)