Skip to content
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

security entitlement support #570

Merged
merged 3 commits into from
Mar 28, 2019

Conversation

kunalkushwaha
Copy link
Collaborator

@kunalkushwaha kunalkushwaha commented Aug 14, 2018

This is just a rough draft for review of approach.
[edit] rebased with #560

What this PR includes:

@kunalkushwaha
Copy link
Collaborator Author

//cc @AkihiroSuda @tonistiigi

Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a reason for copying code from libentitlement. It will be impossible to maintain it like this. It is unclear to me if there are any changes in that code. We should either use it directly(at least a fork if there are changes) or implement the unconfined mode manually without the help of these packages.

fyi @justincormack

@@ -257,6 +258,13 @@ func (s State) Network(n pb.NetMode) State {
func (s State) GetNetwork() pb.NetMode {
return getNetwork(s)
}
func (s State) Security(n pb.SecMode) State {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add RunOption version as well.

@@ -40,6 +40,7 @@ const (
keyImageResolveMode = "image-resolve-mode"
keyGlobalAddHosts = "add-hosts"
keyForceNetwork = "force-network-mode"
keyForceSecurity = "force-security-mode"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets not add this yet. Can be a follow-up.

@@ -269,13 +269,17 @@ func notifyCompleted(ctx context.Context, v *client.Vertex, err error, cached bo
pw.Write(v.Digest.String(), *v)
}

var AllowNetworkHostUnstable = false // TODO: enable in constructor
var AllowNetworkHostUnstable = false // TODO: enable in constructor
var AllowSecurityUnconfinedUnstable = true // TODO: enable in constructor
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave this to false or add the constructor initialization in this PR.


func supportedEntitlements() []entitlements.Entitlement {
out := []entitlements.Entitlement{} // nil means no filter
if AllowNetworkHostUnstable {
out = append(out, entitlements.EntitlementNetworkHost)
}
if AllowSecurityUnconfinedUnstable {
out = append(out, entitlements.EntitlementSecurityUnconfined)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this validated? I think it is missing the condition in ValidateEntitlements()


s.Mounts = GetMounts(ctx,
ociProfile := entitlements.NewOCIProfile(s, meta.SecMode.String())
ociProfile, err = ociProfile.SecurityUnconfined()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be always called (and why the double call) atm.

@kunalkushwaha
Copy link
Collaborator Author

other simple alternative could be, build unconfined secomp profile as on lines defaultProfile() defined in containerd and remove whole libentitlement code.

But at some point if more entitlements are required a sub library to define all entitlements will be easy to manage.

@kunalkushwaha kunalkushwaha force-pushed the entirlement-revised branch 3 times, most recently from 9f12f29 to fb002f5 Compare August 28, 2018 08:24
@kunalkushwaha
Copy link
Collaborator Author

kunalkushwaha commented Aug 28, 2018

The entitlements package from libentitlement is removed and replaced by a simple function based on defaultSecompProfile of containerd.
fb002f5#diff-20b4a13522a5dac1bba70cce7b8e4e44R14

This function is required to reviewed, if its fit for security.unconfined profile.

@kunalkushwaha kunalkushwaha force-pushed the entirlement-revised branch 2 times, most recently from dee930c to e08ef67 Compare August 28, 2018 08:58
opts = append(opts, seccomp.WithDefaultProfile())
}
if meta.SecMode == pb.SecMode_UNCONFINED {
opts = append(opts, entitlements.WithDefaultAdminProfile())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should add all caps, disable seccomp/apparmor completely (no seccompsupported check), remove maskpaths and readonly overrides and change sys mounts to rw.

s.Process.Capabilities.Inheritable = append(s.Process.Capabilities.Inheritable, cap)
s.Process.Capabilities.Permitted = append(s.Process.Capabilities.Permitted, cap)
}
s.Linux.Seccomp = DefaultAdminProfile(s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the unconfied, we can just skip it and not set seccomp at all. For security.admin (future) we would use this profile.

@@ -269,13 +269,17 @@ func notifyCompleted(ctx context.Context, v *client.Vertex, err error, cached bo
pw.Write(v.Digest.String(), *v)
}

var AllowNetworkHostUnstable = false // TODO: enable in constructor
var AllowNetworkHostUnstable = false // TODO: enable in constructor
var AllowSecurityUnconfinedUnstable = false // TODO: enable in constructor
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kunalkushwaha Could you implement this as well so we can have an integration test or would you prefer that to be a follow-up? Something like buildkitd --allow-insecure-entitlement=foo.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am working on this, shall update the PR on Monday.

Facing issue while buildctl build --allow=security.unconfined is passed and security.confined is also set by default.

@kunalkushwaha kunalkushwaha force-pushed the entirlement-revised branch 2 times, most recently from f01c803 to 50c12a4 Compare September 5, 2018 01:25
@kunalkushwaha
Copy link
Collaborator Author

Have updated the code with test case and buildkitd flags to enable unsecured entitlements like network.host and security.unconfined.

There is missing something for both network.host & security.unconfined, even from client or from code llb.Security(llb.Security(llb.SecurityModeUnconfined)) is enabled, the Executor always receive default values i.e. security.confined & network.host.

Also, why dispatchRun() is never invoked.
Please help on this.

Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kunalkushwaha The issue seems to be that you are not calling WithDefaultAdminProfile anywhere (should be WithUnconfinedSecurityProfile)

for _, e := range ents {
switch e {
case "security.unconfined":
llbsolver.AllowSecurityUnconfinedUnstable = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use these variables anymore. Instead, add them to a config array and pass to the solver constructor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -207,6 +212,18 @@ func main() {

controller.Register(server)

ents := c.GlobalStringSlice("allow-insecure-entitlement")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this to config and override with the flag.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -18,6 +18,7 @@ type Meta struct {
ReadonlyRootFS bool
ExtraHosts []HostIP
NetMode pb.NetMode
SecMode pb.SecMode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SecurityMode

// Note: must follow the setting of process capabilities
func WithDefaultAdminProfile() oci.SpecOpts {
return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {
addCaps := []string{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still a limited list. For unconfined use all caps(different from security.admin).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping @kunalkushwaha

And no seccomp for unconfined.

@tonistiigi
Copy link
Member

@kunalkushwaha Ping. Did my answer clear things up or did I miss anything?

@kunalkushwaha
Copy link
Collaborator Author

@tonistiigi Sorry for delay.
Yes its clear, was working on some other stuff so just got delayed. Will update the PR soon.

PS: I will be on leave for 2 weeks from tomorrow.

@AkihiroSuda
Copy link
Member

@kunalkushwaha What's current status?

@kunalkushwaha
Copy link
Collaborator Author

@AkihiroSuda Sorry have not been able to resume work on this.. Shall start from Monday.

@kunalkushwaha
Copy link
Collaborator Author

Hi,
Sorry for delay in resuming the work.

I need some help in understanding how the entitlements can passed from client (buildctl build) workers of buildkitd

Current approach in this PR.

  1. The entitlement information in passed with --allow flag e.g buildctl build --allow security.unconfined
  2. store entitlements information in SolveOpt
  3. This is further passed ControleClient SoleveRequest in function client.solve()
  4. similar to step 3, the entitlements is passed to solver with frontend.SolveRequest
  5. Problem arises here, when llbbridge.Solve() passes only frontendOpt of SolveRequest
  • When control comes to frontend/builder/build.Build() via GatewayForwarder.Solve the options passed from client can be retrieved through BuildOpts() i.e. Opts and LLBCaps and rest of information is lost.

so currently defaultSecurityMode have only default security.confined & network.none entitlements.

I want to use the LLBCaps as it will be defined for both network and security entitlements.

The point where I am stuck is , where to set these LLBCaps, i.e. CapExecMetaSecurity, So this can be access in builder/build.Buid() function?

@tonistiigi @AkihiroSuda

@tonistiigi
Copy link
Member

@kunalkushwaha Why do you need to pass them with frontend.Solve() at all. They are only validated on vertex load and the bridge that is doing the loading already has all of this information initialized to the job in

j.SetValue(keyEntitlements, set)

@kunalkushwaha
Copy link
Collaborator Author

kunalkushwaha commented Dec 3, 2018

@tonistiigi I want to retrieve the value of entitlements in executor.Exec() functions. I am not getting what should be ideal way of retrieving.

If possible, please give me a overview of control flow how it goes once control comes to buildkitd for any build command.

@tonistiigi
Copy link
Member

The value you need in exec is not entitlements that is passed with the build and enabled from daemon. What you need there is only the SecMode that is defined in the Meta and available to you. The validation that this secmode value is allowed to be used with current entitlement settings has already happened before that.

There are 2 sets of values here. One is entitlements that define the allowed actions a current build can perform. The other is the actual action that will be done for specific vertexes. The entitlements only affects the validation of the actual actions. Eg. if you just pass entitlements but don't change your vertexes, nothing would change in your build.

@tonistiigi
Copy link
Member

@kunalkushwaha Haven't reviewed yet but the CI seems to be failing on vendor check.

secMode := sb.Value("secmode")

if secMode != securityUnconfined {
t.Skip()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check that caps are properly restricted in securityConfined mode.

@kunalkushwaha kunalkushwaha force-pushed the entirlement-revised branch 2 times, most recently from 1982a30 to e9b1f19 Compare March 4, 2019 03:29
@kunalkushwaha
Copy link
Collaborator Author

Updated the testcase for SecurityConfined check and fixed CI issue.

@kunalkushwaha
Copy link
Collaborator Author

@tonistiigi PTAL. Have fixed the testcase with check for Confined & Unconfined both. Also CI issue is resolved.

@AkihiroSuda AkihiroSuda added this to the v0.5.0 milestone Mar 13, 2019
}
s.Linux.ReadonlyPaths = []string{}
s.Linux.MaskedPaths = []string{}
s.Process.ApparmorProfile = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove seccomp profile as well?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it was discussed here #570 (comment).
I am updating function comments too.

@AkihiroSuda
Copy link
Member

sorry needs rebase

@kunalkushwaha kunalkushwaha force-pushed the entirlement-revised branch 3 times, most recently from 2445d51 to d40781c Compare March 19, 2019 07:28
@kunalkushwaha
Copy link
Collaborator Author

Its rebased and CI error gone.


var SecurityMode_name = map[int32]string{
0: "CONFINED",
1: "UNCONFINED",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justincormack Would you be okay calling unconfined insecure instead? I know all security profiles call it unconfined, but the user intent feels clearer with insecure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think that makes sense, although confined vs insecure is kind of weird, and I don't think we should call it secure vs insecure...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justincormack sandbox/sandboxed ? I'm not too bothered by having both confined and insecure, because those who will see confined will probably have read about its technical meaning, whereas insecure should mean something strong to those who won't read much about it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justincormack told me offline he prefers confined and insecure.

@kunalkushwaha would you mind updating UNCONFINED to INSECURE everywhere in this PR? We can keep the word CONFINED.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, I like sandbox/insecure pairing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok with sandbox/insecure too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect let’s roll with sandbox/insecure then. Do you mind updating @kunalkushwaha

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update with sandbox/insecure.

Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Small comments

require.NoError(t, err)

_, err = c.Solve(context.TODO(), def, SolveOpt{
AllowedEntitlements: []entitlements.Entitlement{entitlement},
Copy link
Member

@tonistiigi tonistiigi Mar 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a case where you don't pass the entitlement and get an error on securityUnconfined mode and where you try to pass entitlement on securityConfined and get an error as well.

@@ -159,6 +159,10 @@ func main() {
Usage: "ca certificate to verify clients",
Value: defaultConf.GRPC.TLS.CA,
},
cli.StringSliceFlag{
Name: "allow-insecure-entitlement",
Usage: "allows insecure entitlements like network.host & security.unconfined",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like network.host & security.unconfined -> (eg. network.host, security.unconfined)


for _, m := range s.Mounts {
if m.Type == "sysfs" {
m.Options = []string{"nosuid", "noexec", "nodev", "rw"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being reset by the oci.GetMounts call. Probably move it to that function instead for a fix.

@tonistiigi
Copy link
Member

tonistiigi commented Mar 25, 2019

Now that I think of it, I think it was a mistake to add "default entitlements"

var defaults = map[Entitlement]struct{}{
. I think you should just remove EntitlementSecurityConfined cause defining that doesn't make sense in any case. You still need Security(SecurityModeConfined) though, just allowing the entitlement is pointless because it is allowed always anyway. All the functionality remains, just remove the constant. Same goes for network-none but if you want I can clean that up myself after this PR is merged.

edit: I guess I took it from https://github.com/moby/libentitlement but doesn't really fit the design. These should more be like the configuration templates for the executor, not capabilities user needs to give access to.

@kunalkushwaha kunalkushwaha force-pushed the entirlement-revised branch 2 times, most recently from 8fc7880 to 01a8de7 Compare March 27, 2019 04:56
kunalkushwaha and others added 2 commits March 27, 2019 13:57
Signed-off-by: Kunal Kushwaha <[email protected]>
Signed-off-by: Tonis Tiigi <[email protected]>
Signed-off-by: Kunal Kushwaha <[email protected]>
@kunalkushwaha
Copy link
Collaborator Author

kunalkushwaha commented Mar 27, 2019

Updated the PR with changes including.

Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kunalkushwaha , LGTM

PTAL @AkihiroSuda

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants