From 923bb66eba61490a862a5dcf73ae9f67fb73054a Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Mon, 25 Sep 2023 00:06:07 +0100 Subject: [PATCH] feat(aa-log): new structure for apparmor rules. --- pkg/aa/capability.go | 17 +++ pkg/aa/change_profile.go | 19 ++++ pkg/aa/dbus.go | 29 +++++ pkg/aa/file.go | 25 +++++ pkg/aa/include.go | 12 +++ pkg/aa/io_uring.go | 12 +++ pkg/aa/mount.go | 72 +++++++++++++ pkg/aa/mqueue.go | 21 ++++ pkg/aa/network.go | 34 ++++++ pkg/aa/pivot_root.go | 21 ++++ pkg/aa/profile.go | 25 +++++ pkg/aa/ptrace.go | 20 ++++ pkg/aa/rlimit.go | 11 ++ pkg/aa/rules.go | 221 ++++++--------------------------------- pkg/aa/signal.go | 22 ++++ pkg/aa/unix.go | 33 ++++++ pkg/aa/userns.go | 18 ++++ pkg/aa/variables.go | 8 +- 18 files changed, 428 insertions(+), 192 deletions(-) create mode 100644 pkg/aa/capability.go create mode 100644 pkg/aa/change_profile.go create mode 100644 pkg/aa/dbus.go create mode 100644 pkg/aa/file.go create mode 100644 pkg/aa/include.go create mode 100644 pkg/aa/io_uring.go create mode 100644 pkg/aa/mount.go create mode 100644 pkg/aa/mqueue.go create mode 100644 pkg/aa/network.go create mode 100644 pkg/aa/pivot_root.go create mode 100644 pkg/aa/ptrace.go create mode 100644 pkg/aa/rlimit.go create mode 100644 pkg/aa/signal.go create mode 100644 pkg/aa/unix.go create mode 100644 pkg/aa/userns.go diff --git a/pkg/aa/capability.go b/pkg/aa/capability.go new file mode 100644 index 000000000..47c60138d --- /dev/null +++ b/pkg/aa/capability.go @@ -0,0 +1,17 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type Capability struct { + Qualifier + Name string +} + +func CapabilityFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Capability{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + Name: log["capname"], + } +} diff --git a/pkg/aa/change_profile.go b/pkg/aa/change_profile.go new file mode 100644 index 000000000..feba6a8a6 --- /dev/null +++ b/pkg/aa/change_profile.go @@ -0,0 +1,19 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type ChangeProfile struct { + ExecMode string + Exec string + ProfileName string +} + +func ChangeProfileFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &ChangeProfile{ + ExecMode: log["mode"], + Exec: log["exec"], + ProfileName: log["name"], + } +} diff --git a/pkg/aa/dbus.go b/pkg/aa/dbus.go new file mode 100644 index 000000000..c1907e4cd --- /dev/null +++ b/pkg/aa/dbus.go @@ -0,0 +1,29 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type Dbus struct { + Qualifier + Access string + Bus string + Name string + Path string + Interface string + Member string + Label string +} + +func DbusFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Dbus{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + Access: log["mask"], + Bus: log["bus"], + Name: log["name"], + Path: log["path"], + Interface: log["interface"], + Member: log["member"], + Label: log["peer_label"], + } +} diff --git a/pkg/aa/file.go b/pkg/aa/file.go new file mode 100644 index 000000000..eff893bf0 --- /dev/null +++ b/pkg/aa/file.go @@ -0,0 +1,25 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type File struct { + Qualifier + Path string + Access string + Target string +} + +func FileFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + owner := false + if log["fsuid"] == log["ouid"] && log["OUID"] != "root" { + owner = true + } + return &File{ + Qualifier: NewQualifier(owner, noNewPrivs, fileInherit), + Path: log["name"], + Access: maskToAccess[log["requested_mask"]], + Target: log["target"], + } +} diff --git a/pkg/aa/include.go b/pkg/aa/include.go new file mode 100644 index 000000000..83c5bede4 --- /dev/null +++ b/pkg/aa/include.go @@ -0,0 +1,12 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type Include struct { + IfExists bool + Path string + IsMagic bool +} + diff --git a/pkg/aa/io_uring.go b/pkg/aa/io_uring.go new file mode 100644 index 000000000..061ca707d --- /dev/null +++ b/pkg/aa/io_uring.go @@ -0,0 +1,12 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type IOUring struct { + Qualifier + Access string + Label string +} + diff --git a/pkg/aa/mount.go b/pkg/aa/mount.go new file mode 100644 index 000000000..105fee43a --- /dev/null +++ b/pkg/aa/mount.go @@ -0,0 +1,72 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +import "golang.org/x/exp/slices" + +type MountConditions struct { + Fs string + Op string + FsType string + Options []string +} + +type Mount struct { + Qualifier + MountConditions + Source string + MountPoint string +} + +func MountFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Mount{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + MountConditions: MountConditions{ + Fs: "", + Op: "", + FsType: log["fstype"], + Options: []string{}, + }, + Source: log["srcname"], + MountPoint: log["name"], + } +} +type Umount struct { + Qualifier + MountConditions + MountPoint string +} + +func UmountFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Umount{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + MountConditions: MountConditions{ + Fs: "", + Op: "", + FsType: log["fstype"], + Options: []string{}, + }, + MountPoint: log["name"], + } +} + +type Remount struct { + Qualifier + MountConditions + MountPoint string +} + +func RemountFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Remount{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + MountConditions: MountConditions{ + Fs: "", + Op: "", + FsType: log["fstype"], + Options: []string{}, + }, + MountPoint: log["name"], + } +} diff --git a/pkg/aa/mqueue.go b/pkg/aa/mqueue.go new file mode 100644 index 000000000..d00b6bd34 --- /dev/null +++ b/pkg/aa/mqueue.go @@ -0,0 +1,21 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type Mqueue struct { + Qualifier + Access string + Type string + Label string +} + +func MqueueFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Mqueue{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + Access: maskToAccess[log["requested_mask"]], + Type: log["type"], + Label: log["label"], + } +} diff --git a/pkg/aa/network.go b/pkg/aa/network.go new file mode 100644 index 000000000..62a0b44f7 --- /dev/null +++ b/pkg/aa/network.go @@ -0,0 +1,34 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type AddressExpr struct { + Source string + Destination string + Port string +} + + +type Network struct { + Qualifier + Domain string + Type string + Protocol string + AddressExpr +} + +func NetworkFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Network{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + AddressExpr: AddressExpr{ + Source: log["laddr"], + Destination: log["faddr"], + Port: log["lport"], + }, + Domain: log["family"], + Type: log["sock_type"], + Protocol: log["protocol"], + } +} diff --git a/pkg/aa/pivot_root.go b/pkg/aa/pivot_root.go new file mode 100644 index 000000000..2e336ab1f --- /dev/null +++ b/pkg/aa/pivot_root.go @@ -0,0 +1,21 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type PivotRoot struct { + Qualifier + OldRoot string + NewRoot string + TargetProfile string +} + +func PivotRootFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &PivotRoot{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + OldRoot: log["oldroot"], + NewRoot: log["root"], + TargetProfile: log["name"], + } +} diff --git a/pkg/aa/profile.go b/pkg/aa/profile.go index d906cbfad..ee40d8ce2 100644 --- a/pkg/aa/profile.go +++ b/pkg/aa/profile.go @@ -23,6 +23,31 @@ type AppArmorProfile struct { Profile } +// Preamble section of a profile +type Preamble struct { + Abi []Abi + Includes []Include + Aliases []Alias + Variables []Variable +} + +// Profile section of a profile +type Profile struct { + Name string + Attachments []string + Attributes map[string]string + Flags []string + Rules Rules +} + +// ApparmorRule generic interface +type ApparmorRule interface { + Less(other any) bool + Equals(other any) bool +} + +type Rules []ApparmorRule + func NewAppArmorProfile() *AppArmorProfile { return &AppArmorProfile{} } diff --git a/pkg/aa/ptrace.go b/pkg/aa/ptrace.go new file mode 100644 index 000000000..a6bde6e36 --- /dev/null +++ b/pkg/aa/ptrace.go @@ -0,0 +1,20 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type Ptrace struct { + Qualifier + Access string + Peer string +} + +func PtraceFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Ptrace{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + Access: maskToAccess[log["requested_mask"]], + Peer: log["peer"], + } +} + diff --git a/pkg/aa/rlimit.go b/pkg/aa/rlimit.go new file mode 100644 index 000000000..a4fabec4c --- /dev/null +++ b/pkg/aa/rlimit.go @@ -0,0 +1,11 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type Rlimit struct { + Key string + Op string + Value string +} diff --git a/pkg/aa/rules.go b/pkg/aa/rules.go index 4913b3be7..287bfd97b 100644 --- a/pkg/aa/rules.go +++ b/pkg/aa/rules.go @@ -4,42 +4,6 @@ package aa -// Preamble section of a profile -type Preamble struct { - Abi []Abi - PreambleIncludes []Include - Aliases []Alias - Variables []Variable -} - -// Profile section of a profile -type Profile struct { - Name string - Attachments []string - Attributes []string - Flags []string - Rules -} - -type Rules struct { - Includes []Include - Rlimit []Rlimit - Userns Userns - Capability []Capability - Network []Network - Mount []Mount - Umount []Umount - Remount []Remount - PivotRoot []PivotRoot - ChangeProfile []ChangeProfile - Unix []Unix - Ptrace []Ptrace - Signal []Signal - Dbus []Dbus - File []File -} - - // Qualifier to apply extra settings to a rule type Qualifier struct { Audit bool @@ -49,159 +13,6 @@ type Qualifier struct { FileInherit bool } -// Preamble rules - -type Abi struct { - Path string - IsMagic bool -} - -type Alias struct { - Path string - RewrittenPath string -} - -type Include struct { - IfExists bool - Path string - IsMagic bool -} - -type Variable struct { - Name string - Values []string -} - -// Profile rules - -type Rlimit struct { - Key string - Op string - Value string -} - -type Userns struct { - Qualifier - Create bool -} - -type Capability struct { - Qualifier - Name string -} - -type AddressExpr struct { - Source string - Destination string - Port string -} - -type Network struct { - Qualifier - Domain string - Type string - Protocol string - AddressExpr -} - -type MountConditions struct { - Fs string - Op string - FsType string - Options []string -} - -type Mount struct { - Qualifier - MountConditions - Source string - MountPoint string -} - -type Umount struct { - Qualifier - MountConditions - MountPoint string -} - -type Remount struct { - Qualifier - MountConditions - MountPoint string -} - -type PivotRoot struct { - Qualifier - OldRoot string - NewRoot string - TargetProfile string -} - -type ChangeProfile struct { - ExecMode string - Exec string - ProfileName string -} - -type IOUring struct { - Qualifier - Access string - Label string -} - -type Signal struct { - Qualifier - Access string - Set string - Peer string -} - -type Ptrace struct { - Qualifier - Access string - Peer string -} - -type Unix struct { - Qualifier - Access string - Type string - Protocol string - Address string - Label string - Attr string - Opt string - Peer string - PeerAddr string -} - -type Mqueue struct { - Qualifier - Access string - Type string - Label string -} - -type Dbus struct { - Qualifier - Access string - Bus string - Name string - Path string - Interface string - Member string - Label string -} - -type File struct { - Qualifier - Path string - Access string - Target string -} - -// Rules constructors from logs - func NewQualifier(owner, noNewPrivs, fileInherit bool) Qualifier { return Qualifier{ Audit: false, @@ -303,4 +114,36 @@ func NewDbus(log map[string]string, noNewPrivs, fileInherit bool) Dbus { Member: log["member"], Label: log["peer_label"], } +// Preamble specific rules + +type Abi struct { + Path string + IsMagic bool +} + +func (r Abi) Less(other Abi) bool { + if r.Path == other.Path { + return r.IsMagic == other.IsMagic + } + return r.Path < other.Path +} + +func (r Abi) Equals(other Abi) bool { + return r.Path == other.Path && r.IsMagic == other.IsMagic +} + +type Alias struct { + Path string + RewrittenPath string +} + +func (r Alias) Less(other Alias) bool { + if r.Path == other.Path { + return r.RewrittenPath < other.RewrittenPath + } + return r.Path < other.Path +} + +func (r Alias) Equals(other Alias) bool { + return r.Path == other.Path && r.RewrittenPath == other.RewrittenPath } diff --git a/pkg/aa/signal.go b/pkg/aa/signal.go new file mode 100644 index 000000000..09a699f5b --- /dev/null +++ b/pkg/aa/signal.go @@ -0,0 +1,22 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type Signal struct { + Qualifier + Access string + Set string + Peer string +} + +func SignalFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Signal{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + Access: maskToAccess[log["requested_mask"]], + Set: log["signal"], + Peer: log["peer"], + } +} + diff --git a/pkg/aa/unix.go b/pkg/aa/unix.go new file mode 100644 index 000000000..c1fa6ea11 --- /dev/null +++ b/pkg/aa/unix.go @@ -0,0 +1,33 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type Unix struct { + Qualifier + Access string + Type string + Protocol string + Address string + Label string + Attr string + Opt string + Peer string + PeerAddr string +} + +func UnixFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Unix{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + Access: maskToAccess[log["requested_mask"]], + Type: log["sock_type"], + Protocol: log["protocol"], + Address: log["addr"], + Label: log["peer_label"], + Attr: log["attr"], + Opt: log["opt"], + Peer: log["peer"], + PeerAddr: log["peer_addr"], + } +} diff --git a/pkg/aa/userns.go b/pkg/aa/userns.go new file mode 100644 index 000000000..36238df80 --- /dev/null +++ b/pkg/aa/userns.go @@ -0,0 +1,18 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2023 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package aa + +type Userns struct { + Qualifier + Create bool +} + +func UsernsFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRule { + return &Userns{ + Qualifier: NewQualifier(false, noNewPrivs, fileInherit), + Create: true, + } +} + diff --git a/pkg/aa/variables.go b/pkg/aa/variables.go index 8e1bff278..03f7c58fd 100644 --- a/pkg/aa/variables.go +++ b/pkg/aa/variables.go @@ -17,10 +17,12 @@ import ( var ( regVariablesDef = regexp.MustCompile(`@{(.*)}\s*[+=]+\s*(.*)`) regVariablesRef = regexp.MustCompile(`@{([^{}]+)}`) -) -// Default Apparmor magic directory: /etc/apparmor.d/. -var MagicRoot = paths.New("/etc/apparmor.d") +type Variable struct { + Name string + Values []string +} + // DefaultTunables return a minimal working profile to build the profile // It should not be used when loading file from /etc/apparmor.d