-
Notifications
You must be signed in to change notification settings - Fork 33
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
Import gojail pkg into runj repo #13
base: main
Are you sure you want to change the base?
Conversation
import gojail from https://github.com/gizahNL/gojail @ 4a1b6b5aa507094cec670d2fcf723b12fd6022b8
This is failing the CI because jail/conf.go no longer exists
sendmsg = false | ||
) | ||
|
||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 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.
Is it possible to use pdfork here and expose process descriptors over the interface? This would make it feasible to apply Capsicum sandboxing to runj
in the future (I believe Ben Laurie added Capsicum support to the go core library some time ago). It's always possible to convert from a process descriptor to a PID, but operations on PIDs are not permitted in sandboxed mode, whereas operations on process descriptors are enable everywhere.
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 would be to apply capsicum on the jailed process correct?
In that case here would be the wrong place to do so, as this function exists solely to create a jail as a child from another jail, with the intention being that the parent jail functions as a namespace of sorts.
This is useful for k8s where pods expect a network namespace to be shared between its containers and I use it in my Docker port as a network namespace. The biggest advantage there is that this allows running Linux containers with networking support (as they would lack the FreeBSD networking configuration tools).
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.
To add: the forked child dies immediately after creating the jail, or failing to do so.
The fork is needed because a child jail can only be created from within the parent, and (rightly so) we cannot enter and leave a jail at will.
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 would be to apply capsicum on the jailed process correct?
It would apply capsicum to any child process, though it's up to the consumer of this API when they enter capability mode. If the parent exits immediately after this, then there is no benefit from doing so. If the parent continues executing then it may wish to drop privileges at that point. If the parent wishes to maintain a handle for monitoring the child, then it may wish to do so.
Either way, the APIs using process descriptors are more general and do not have the problems with data races that the PID-based APIs have and so should be preferred where there are not legacy API compatibility concerns.
Did you validate that this functionality works with the security.bsd.see_jail_proc
sysctl set to 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.
Ok, I understand the scope now :) I haven't tested that yet. It works fine from the root system.
How does one wait on a procdesc FD? The documentation of pdfork & wait do not seem to mention a way to do so.
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.
Use kqueue
. Here's an example of setting up the kqueue, which you then wait on later.
There's no pdwait6
currently, but if it would be helpful to this project I can add one fairly easily.
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.
My apologies for the super, super delayed review here. I really appreciate the work you've put in. I've left some comments and questions and I recognize that I'm asking for a somewhat large design change, but let me know whether this is work that you'd like to do or if you'd be comfortable with me making further changes on top of your contribution.
@@ -59,9 +62,17 @@ func _main() (int, error) { | |||
if len(os.Args) < 4 { | |||
return 1, errUsage | |||
} | |||
jid := os.Args[1] | |||
id := os.Args[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.
Why pass the container ID and not the JID? id
appears to only be used to load the state (line 70), and then the resulting state is only used to find the JID (line 75).
I'd prefer to avoid having runj-entrypoint
read the state file if possible.
func (j *jail) RunIn() error { | ||
return errors.New("Not implemented") | ||
} |
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.
Since this isn't implemented yet, let's remove both this function and the interface method.
//JailCreate creates a Jail with the given parameters, no validation is done atm | ||
//accepted types for interface{}: int32/*int32/uint32/*uint32/string/bool/[]byte | ||
//byte slices MUST be null terminated if the OS expects a string var. | ||
func JailCreate(jailParameters map[string]interface{}) (Jail, error) { |
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'm definitely not wild about taking a map[string]interface{}
here. Since we're using a small set of existing parameters, I'd prefer to make a struct to hold the ones that we'll use.
For example:
type JailCreateParams struct {
Name string
Path string
Persist bool
}
} | ||
s.JID = int(j.ID()) | ||
s.Save() |
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.
Saving should be handled by the defer
on line 93. If we remove the need for runj-entrypoint
to read the file, we shouldn't need to save here.
} | ||
j.Destroy() |
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.
j.Destroy() | |
err = j.Destroy() | |
if err != nil { | |
return err | |
} |
//not truly mandatory | ||
if _, ok := parameters["name"]; !ok { | ||
return "", errors.New("Name param mandatory for jail creation") | ||
} |
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 don't understand the comment. It's not mandatory but we're returning an error if it's not here?
(This becomes less relevant if we move to a struct instead of a map[string]interface
, but we'll probably still need to check that it's not ""
if it is mandatory.)
) | ||
|
||
var ( | ||
iovErrmsg = []byte("errmsg\000") |
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.
Where does this come from? I don't see it in the jail(2)
man page.
func JailGetByName(jailName string) (Jail, error) { | ||
jid, err := JailGetID(jailName) |
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 the API we want to expose from this package is related to the Jail
struct, we probably shouldn't make the non-struct functions like JailGetID
public.
//JailGet gets values for the parameters in the []syscall.Iovec | ||
func JailGet(iovecs []syscall.Iovec, flags int) (JailID, error) { |
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.
As discussed above, let's make this function and the others in this file private.
return prefix, param | ||
} | ||
|
||
func paramToIOVec(key string, value interface{}) ([]syscall.Iovec, error) { |
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.
Similar to my comment about map[string]interface{}
above, my preference would be to avoid reflection-based logic for constructing the syscall.Iovec
structs. It is probably a bit more tedious, but I'd prefer to see structs like JailCreateParams
that are able to serialize themselves into the appropriate []syscall.Iovec
.
For example:
type JailCreateParams struct {
Name string
Path string
Persist bool
}
This struct could implement an interface like this:
type JailIovecSerializer interface {
SerializeIovec() ([]syscall.Iovec, error)
}
func (j *JailCreateParams) SerializeIovec() ([]syscall.Iovec, error) {
iovec := make([]syscall.Iovec, 0)
nameKey, err := syscall.ByteSliceFromString("name")
if err != nil {
return nil, err
}
nameVal, err := syscall.BytePtrFromString(j.Name)
if err != nil {
return nil, err
}
iovec = append(iovec, makeJailIovec(nameKey, nameVal, len(j.Name)+1)...)
// repeat for other fields
return iovec, nil
}
Thanks again for opening this and showing me how to properly invoke the jail syscalls. I've adapted some of the work you did here into 398df9a and pushed that; as a result, this PR now has merge conflicts. I took the approach that I had described (eliminating the I'm still not sure of some of the things that I had asked you (including the "errmsg" iovec) so I didn't integrate that into my commit. You'll note that I credited you in the commit with |
Issue number:
#12
Description of changes:
This imports the gojail package originally developed at https://github.com/gizahNL/gojail into the runj repository to allow for a simpler co-evolution.
Also it modifies the start, delete and entrypoint commands to use the gojail library instead of relying on the jail and jexec os utilities.
Testing done:
Jail creation & deletion are working.
Terms of contribution:
By submitting this pull request, I agree that this contribution is licensed under the terms found in the LICENSE file.