Skip to content

rootless: allow loading an existing AppArmor profile #508

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

Merged
merged 1 commit into from
Nov 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ FROM ubuntu:${UBUNTU_VERSION} AS base
# fuse3 is required by stargz snapshotter
RUN apt-get update && \
apt-get install -qq -y --no-install-recommends \
apparmor \
ca-certificates curl \
iproute2 iptables \
dbus systemd systemd-sysv \
Expand Down
6 changes: 6 additions & 0 deletions Dockerfile.d/test-integration-rootless.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@

set -eux -o pipefail
if [[ "$(id -u)" = "0" ]]; then
if [ -e /sys/kernel/security/apparmor/profiles ]; then
# Load the "nerdctl-default" profile for TestRunApparmor
nerdctl apparmor load
fi

# Switch to the rootless user via SSH
systemctl start sshd
exec ssh -o StrictHostKeyChecking=no rootless@localhost "$0" "$@"
else
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ Minor:
- Connecting a container to multiple networks at once: `nerdctl run --net foo --net bar`
- Running [FreeBSD jails](./docs/freebsd.md).
- Better multi-platform support, e.g., `nerdctl pull --all-platforms IMAGE`
- Applying an (existing) AppArmor profile to rootless containers: `nerdctl run --security-opt apparmor=<PROFILE>`.
Use `sudo nerdctl apparmor load` to load the `nerdctl-default` profile.

Trivial:
- Inspecting raw OCI config: `nerdctl container inspect --mode=native` .
Expand Down Expand Up @@ -253,6 +255,11 @@ It does not necessarily mean that the corresponding features are missing in cont
- [:whale: nerdctl volume rm](#whale-nerdctl-volume-rm)
- [Namespace management](#namespace-management)
- [:nerd_face: :blue_square: nerdctl namespace ls](#nerd_face-blue_square-nerdctl-namespace-ls)
- [AppArmor profile management](#apparmor-profile-management)
- [:nerd_face: nerdctl apparmor inspect](#nerd_face-nerdctl-apparmor-inspect)
- [:nerd_face: nerdctl apparmor load](#nerd_face-nerdctl-apparmor-load)
- [:nerd_face: nerdctl apparmor ls](#nerd_face-nerdctl-apparmor-ls)
- [:nerd_face: nerdctl apparmor unload](#nerd_face-nerdctl-apparmor-unload)
- [System](#system)
- [:whale: nerdctl events](#whale-nerdctl-events)
- [:whale: nerdctl info](#whale-nerdctl-info)
Expand Down Expand Up @@ -922,6 +929,31 @@ Usage: `nerdctl namespace ls [OPTIONS]`
Flags:
- `-q, --quiet`: Only display namespace names

## AppArmor profile management
### :nerd_face: nerdctl apparmor inspect
Display the default AppArmor profile "nerdctl-default". Other profiles cannot be displayed with this command.

Usage: `nerdctl apparmor inspect`

### :nerd_face: nerdctl apparmor load
Load the default AppArmor profile "nerdctl-default". Requires root.

Usage: `nerdctl apparmor load`

### :nerd_face: nerdctl apparmor ls
List the loaded AppArmor profile

Usage: `nerdctl apparmor ls [OPTIONS]`

Flags:
- `-q, --quiet`: Only display volume names
- `--format`: Format the output using the given Go template, e.g, `{{json .}}`

### :nerd_face: nerdctl apparmor unload
Unload an AppArmor profile. The target profile name defaults to "nerdctl-default". Requires root.

Usage: `nerdctl apparmor unload [PROFILE]`

## System
### :whale: nerdctl events
Get real time events from the server.
Expand Down
46 changes: 46 additions & 0 deletions cmd/nerdctl/apparmor_inspect_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Copyright The containerd Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"fmt"

"github.com/containerd/containerd/contrib/apparmor"
"github.com/containerd/nerdctl/pkg/defaults"
"github.com/spf13/cobra"
)

func newApparmorInspectCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "inspect",
Short: fmt.Sprintf("Display the default AppArmor profile %q. Other profiles cannot be displayed with this command.", defaults.AppArmorProfileName),
Args: cobra.NoArgs,
RunE: apparmorInspectAction,
SilenceUsage: true,
SilenceErrors: true,
}
return cmd
}

func apparmorInspectAction(cmd *cobra.Command, args []string) error {
b, err := apparmor.DumpDefaultProfile(defaults.AppArmorProfileName)
if err != nil {
return err
}
_, err = fmt.Fprintf(cmd.OutOrStdout(), b)
return err
}
39 changes: 39 additions & 0 deletions cmd/nerdctl/apparmor_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
Copyright The containerd Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"github.com/spf13/cobra"
)

func newApparmorCommand() *cobra.Command {
cmd := &cobra.Command{
Category: CategoryManagement,
Use: "apparmor",
Short: "Manage AppArmor profiles",
RunE: unknownSubcommandAction,
SilenceUsage: true,
SilenceErrors: true,
}
cmd.AddCommand(
newApparmorLsCommand(),
newApparmorInspectCommand(),
newApparmorLoadCommand(),
newApparmorUnloadCommand(),
)
return cmd
}
43 changes: 43 additions & 0 deletions cmd/nerdctl/apparmor_load_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
Copyright The containerd Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"fmt"

"github.com/containerd/containerd/contrib/apparmor"
"github.com/containerd/nerdctl/pkg/defaults"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

func newApparmorLoadCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "load",
Short: fmt.Sprintf("Load the default AppArmor profile %q. Requires root.", defaults.AppArmorProfileName),
Args: cobra.NoArgs,
RunE: apparmorLoadAction,
SilenceUsage: true,
SilenceErrors: true,
}
return cmd
}

func apparmorLoadAction(cmd *cobra.Command, args []string) error {
logrus.Infof("Loading profile %q", defaults.AppArmorProfileName)
return apparmor.LoadDefaultProfile(defaults.AppArmorProfileName)
}
103 changes: 103 additions & 0 deletions cmd/nerdctl/apparmor_ls_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
Copyright The containerd Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"bytes"
"errors"
"fmt"
"text/tabwriter"
"text/template"

"github.com/containerd/nerdctl/pkg/apparmorutil"
"github.com/spf13/cobra"
)

func newApparmorLsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "ls",
Aliases: []string{"list"},
Short: "List the loaded AppArmor profiles",
Args: cobra.NoArgs,
RunE: apparmorLsAction,
SilenceUsage: true,
SilenceErrors: true,
}
cmd.Flags().BoolP("quiet", "q", false, "Only display profile names")
// Alias "-f" is reserved for "--filter"
cmd.Flags().String("format", "", "Format the output using the given go template")
cmd.RegisterFlagCompletionFunc("format", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"json"}, cobra.ShellCompDirectiveNoFileComp
})
return cmd
}

func apparmorLsAction(cmd *cobra.Command, args []string) error {
quiet, err := cmd.Flags().GetBool("quiet")
if err != nil {
return err
}
w := cmd.OutOrStdout()
var tmpl *template.Template
format, err := cmd.Flags().GetString("format")
if err != nil {
return err
}
switch format {
case "", "table":
w = tabwriter.NewWriter(cmd.OutOrStdout(), 4, 8, 4, ' ', 0)
if !quiet {
fmt.Fprintln(w, "NAME\tMODE")
}
case "raw":
return errors.New("unsupported format: \"raw\"")
default:
if quiet {
return errors.New("format and quiet must not be specified together")
}
var err error
tmpl, err = parseTemplate(format)
if err != nil {
return err
}
}

profiles, err := apparmorutil.Profiles()
if err != nil {
return err
}

for _, f := range profiles {
if tmpl != nil {
var b bytes.Buffer
if err := tmpl.Execute(&b, f); err != nil {
return err
}
if _, err = fmt.Fprintf(w, b.String()+"\n"); err != nil {
return err
}
} else if quiet {
fmt.Fprintln(w, f.Name)
} else {
fmt.Fprintf(w, "%s\t%s\n", f.Name, f.Mode)
}
}
if f, ok := w.(Flusher); ok {
return f.Flush()
}
return nil
}
52 changes: 52 additions & 0 deletions cmd/nerdctl/apparmor_unload_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright The containerd Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"fmt"

"github.com/containerd/nerdctl/pkg/apparmorutil"
"github.com/containerd/nerdctl/pkg/defaults"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

func newApparmorUnloadCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "unload [PROFILE]",
Short: fmt.Sprintf("Unload an AppArmor profile. The target profile name defaults to %q. Requires root.", defaults.AppArmorProfileName),
Args: cobra.MaximumNArgs(1),
RunE: apparmorUnloadAction,
ValidArgsFunction: apparmorUnloadShellComplete,
SilenceUsage: true,
SilenceErrors: true,
}
return cmd
}

func apparmorUnloadAction(cmd *cobra.Command, args []string) error {
target := defaults.AppArmorProfileName
if len(args) > 0 {
target = args[0]
}
logrus.Infof("Unloading profile %q", target)
return apparmorutil.Unload(target)
}

func apparmorUnloadShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return shellCompleteApparmorProfiles(cmd)
}
34 changes: 34 additions & 0 deletions cmd/nerdctl/completion_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
Copyright The containerd Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"github.com/containerd/nerdctl/pkg/apparmorutil"
"github.com/spf13/cobra"
)

func shellCompleteApparmorProfiles(cmd *cobra.Command) ([]string, cobra.ShellCompDirective) {
profiles, err := apparmorutil.Profiles()
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var names []string // nolint: prealloc
for _, f := range profiles {
names = append(names, f.Name)
}
return names, cobra.ShellCompDirectiveNoFileComp
}
Loading