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

PRO security isolation: apt_news #2794

Merged
merged 22 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cf9fc44
d/jinja2_render: new tool to render the apparmor profile template
panlinux Nov 19, 2023
f9d61d6
d/rules: render and install the apparmor profile for apt_news
panlinux Oct 10, 2023
69fe8d0
d/apparmor: add apparmor profile for apt_news
panlinux Oct 10, 2023
9343f4e
d/control: build-depends for dh-apparmor and jinja2
panlinux Oct 10, 2023
bbb967a
systemd/apt-news.service: isolation
panlinux Oct 10, 2023
47d8a0c
Move temporary test files to /var/lib/ubuntu-advantage
panlinux Oct 13, 2023
9fa0d98
test: apt-news starts in the background, give it some time
panlinux Nov 27, 2023
7a07c40
Check the syntax of the rendered apparmor profile
panlinux Dec 15, 2023
2888c63
d/apparmor/ubuntu_pro_apt_news.jinja2: renamed from ubuntu_advantage_*
panlinux Dec 24, 2023
cab7b49
d/rules: rename apparmor profile: ubuntu_advantage_apt_news to ubuntu…
panlinux Dec 24, 2023
00a1610
systemd/apt-news.service: rename apparmor profile: ubuntu_advantage_a…
panlinux Dec 24, 2023
6469819
apparmor apt_news: allow /proc/pid/status for older ubuntu releases
panlinux Dec 25, 2023
dd24fd8
apparmor apt-news: updates for xenial
panlinux Dec 31, 2023
a6b0ce3
apparmor apt-news: distinguish xenial and bionic where applicable
panlinux Dec 31, 2023
3e800c0
actions.py: write apparmor logs to apparmor_logs.txt
panlinux Dec 24, 2023
e9a3343
test_actions: adjust test for new apparmor log
panlinux Dec 31, 2023
347c484
test_cli_collect_logs.py: adjust test for new apparmor files
panlinux Dec 31, 2023
4869695
apport: include apparmor logs and profiles
panlinux Jan 1, 2024
aef745c
actions.py: handle exceptions when gathering kernel logs
panlinux Jan 3, 2024
ab101e9
Move APPARMOR_PROFILES to defaults
panlinux Jan 4, 2024
b67f887
Local black run is happy, but not github's
panlinux Jan 4, 2024
77b4d75
actions.py: function to get apparmor logs
panlinux Jan 4, 2024
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
5 changes: 4 additions & 1 deletion apport/source_ubuntu-advantage-tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

from apport.hookutils import attach_file_if_exists
from uaclient import defaults
from uaclient.actions import collect_logs
from uaclient.actions import collect_logs, APPARMOR_PROFILES
from uaclient.config import UAConfig


def add_info(report, ui=None):
report["LaunchpadPrivate"] = "1"
report["LaunchpadSubscribe"] = "ua-client"
cfg = UAConfig()
apparmor_files = [os.path.basename(f) for f in APPARMOR_PROFILES]
with tempfile.TemporaryDirectory() as output_dir:
collect_logs(cfg, output_dir)
auto_include_log_files = {
Expand All @@ -21,6 +22,8 @@ def add_info(report, ui=None):
"livepatch-status.txt",
"livepatch-status.txt-error",
"pro-journal.txt",
"apparmor_logs.txt",
*apparmor_files,
os.path.basename(cfg.cfg_path),
os.path.basename(cfg.log_file),
os.path.basename(cfg.data_path("jobs-status")),
Expand Down
64 changes: 64 additions & 0 deletions debian/apparmor/ubuntu_pro_apt_news.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{% if ubuntu_codename not in ["xenial", "bionic", "focal"] %}
abi <abi/3.0>,
{% endif %}
include <tunables/global>

profile ubuntu_pro_apt_news flags=(attach_disconnected) {
include <abstractions/base>
include <abstractions/nameservice>
include <abstractions/openssl>
include <abstractions/python>

# Needed because apt-news calls apt_pkg.init() which tries to
# switch to the _apt system user/group.
capability setgid,
capability setuid,
capability dac_read_search,

/etc/apt/** r,
/etc/default/apport r,
/etc/ubuntu-advantage/* r,
/usr/bin/python3.{1,}[0-9] mrix,
{% if ubuntu_codename in ["focal"] %}
# "import uuid" in focal triggers an uname call
/usr/bin/uname mrix,
{% endif %}
/usr/lib/apt/methods/http mrix,
/usr/lib/apt/methods/https mrix,
/usr/lib/ubuntu-advantage/apt_news.py r,
/usr/share/dpkg/* r,
/var/log/ubuntu-advantage.log rw,
/var/lib/ubuntu-advantage/** r,
/var/lib/ubuntu-advantage/messages/ rw,
/var/lib/ubuntu-advantage/messages/* rw,
/run/ubuntu-advantage/ rw,
/run/ubuntu-advantage/* rw,

/tmp/** r,

owner @{PROC}/@{pid}/fd/ r,
@{PROC}/@{pid}/cgroup r,
{% if ubuntu_codename in ["bionic", "xenial"] %}
# see https://bugs.python.org/issue40501
/sbin/ldconfig rix,
/sbin/ldconfig.real rix,
@{PROC}/@{pid}/mounts r,
@{PROC}/@{pid}/status r,
/usr/bin/@{multiarch}-gcc-* rix,
/usr/bin/@{multiarch}-ld.bfd rix,
/usr/lib/gcc/@{multiarch}/*/collect2 rix,
/usr/bin/@{multiarch}-objdump rix,
{% endif %}
{% if ubuntu_codename in ["xenial"] %}
# for some reason, these were just needed in xenial
capability chown,
capability fowner,
capability dac_override,

/etc/apt/auth.conf.d/90ubuntu-advantage rw,
/var/lib/apt/lists/partial/ rw,
/var/lib/apt/lists/partial/* rw,
/var/cache/apt/archives/partial/ rw,
/var/cache/apt/archives/partial/* rw,
{% endif %}
}
3 changes: 3 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Build-Depends: bash-completion,
debhelper (>=9),
debianutils (>= 4.7),
dh-python,
dh-apparmor,
apparmor,
# After debhelper 13.3 we no longer need dh-systemd.
# On hirsute and later, dh-systemd doesn't even exist.
# On recent releases, the first alternative will be used.
Expand All @@ -21,6 +23,7 @@ Build-Depends: bash-completion,
po-debconf,
python3 (>= 3.4),
python3-flake8,
python3-jinja2,
python3-mock,
python3-pytest,
python3-setuptools,
Expand Down
20 changes: 20 additions & 0 deletions debian/jinja2_render
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/python3

import sys

from jinja2 import Template

input_file = sys.argv[1]
output_file = sys.argv[2]
kwargs = {}

# key=value pairs in the command-line
if len(sys.argv) > 3:
kwargs = {arg.split("=")[0]:arg.split("=")[1] for arg in sys.argv[3:]}

t = Template(open(input_file, "r").read())
output = t.render(**kwargs)

with open(output_file, "w") as f:
f.write(output)

7 changes: 7 additions & 0 deletions debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ override_dh_systemd_start:

override_dh_auto_install:
dh_auto_install --destdir=debian/ubuntu-advantage-tools
debian/jinja2_render debian/apparmor/ubuntu_pro_apt_news.jinja2 debian/apparmor/ubuntu_pro_apt_news ubuntu_codename=${UBUNTU_CODENAME}
# quick syntax check on the generated profile
apparmor_parser -K -T -Q debian/apparmor/ubuntu_pro_apt_news
install -D -m 644 $(CURDIR)/debian/apparmor/ubuntu_pro_apt_news $(CURDIR)/debian/ubuntu-advantage-tools/etc/apparmor.d/ubuntu_pro_apt_news
dh_apparmor -pubuntu-advantage-tools --profile-name=ubuntu_pro_apt_news

flist=$$(find $(CURDIR)/debian/ -type f -name version.py) && sed -i 's,@@PACKAGED_VERSION@@,$(DEB_VERSION),' $${flist:-did-not-find-version-py-for-replacement}

# We install the conf file even on non-LTS version to avoid issues on upgrade scenarios
Expand All @@ -86,3 +92,4 @@ endif
override_dh_auto_clean:
dh_auto_clean
make clean
rm -f debian/apparmor/ubuntu_pro_apt_news
6 changes: 5 additions & 1 deletion features/apt_messages.feature
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ Feature: APT Messages
When I run `rm -rf /var/lib/ubuntu-advantage/messages` with sudo
When I run `rm /var/lib/apt/periodic/update-success-stamp` with sudo
When I run `apt-get update` with sudo
# the apt-news.service unit runs in the background, give it some time to fetch the json file
When I wait `5` seconds
When I run `apt upgrade` with sudo
Then I will see the following on stdout
"""
Expand Down Expand Up @@ -567,6 +569,8 @@ Feature: APT Messages
# test that apt update will trigger hook to update apt_news for local override
When I run `rm -f /var/lib/apt/periodic/update-success-stamp` with sudo
When I run `apt-get update` with sudo
# the apt-news.service unit runs in the background, give it some time to fetch the json file
When I wait `5` seconds
When I run `apt upgrade` with sudo
Then I will see the following on stdout
"""
Expand Down Expand Up @@ -638,7 +642,7 @@ Feature: APT Messages
"""
"*Your Ubuntu Pro subscription has EXPIRED*\nRenew your subscription at https://ubuntu.com/pro/dashboard"
"""
When I create the file `/tmp/machine-token-overlay.json` with the following:
When I create the file `/var/lib/ubuntu-advantage/machine-token-overlay.json` with the following:
"""
{
"machineTokenInfo": {
Expand Down
16 changes: 8 additions & 8 deletions features/attached_commands.feature
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ Feature: Command behaviour when attached to an Ubuntu Pro subscription

Scenario Outline: Attached status in a ubuntu machine with feature overrides
Given a `<release>` `<machine_type>` machine with ubuntu-advantage-tools installed
When I create the file `/tmp/machine-token-overlay.json` with the following:
When I create the file `/var/lib/ubuntu-advantage/machine-token-overlay.json` with the following:
"""
{
"machineTokenInfo": {
Expand All @@ -316,7 +316,7 @@ Feature: Command behaviour when attached to an Ubuntu Pro subscription
And I append the following on uaclient config:
"""
features:
machine_token_overlay: "/tmp/machine-token-overlay.json"
machine_token_overlay: "/var/lib/ubuntu-advantage/machine-token-overlay.json"
disable_auto_attach: true
other: false
"""
Expand All @@ -332,7 +332,7 @@ Feature: Command behaviour when attached to an Ubuntu Pro subscription
"""
FEATURES
disable_auto_attach: True
machine_token_overlay: /tmp/machine-token-overlay.json
machine_token_overlay: /var/lib/ubuntu-advantage/machine-token-overlay.json
other: False
"""
When I run `pro status --all` as non-root
Expand All @@ -346,7 +346,7 @@ Feature: Command behaviour when attached to an Ubuntu Pro subscription
"""
FEATURES
disable_auto_attach: True
machine_token_overlay: /tmp/machine-token-overlay.json
machine_token_overlay: /var/lib/ubuntu-advantage/machine-token-overlay.json
other: False
"""
When I run `pro detach --assume-yes` with sudo
Expand Down Expand Up @@ -745,7 +745,7 @@ Feature: Command behaviour when attached to an Ubuntu Pro subscription
Then I verify that `activityInfo.activityToken` value has been updated on the contract
And I verify that `activityInfo.activityID` value has been updated on the contract
# We are keeping this test to guarantee that the activityPingInterval is also updated
When I create the file `/tmp/machine-token-overlay.json` with the following:
When I create the file `/var/lib/ubuntu-advantage/machine-token-overlay.json` with the following:
"""
{
"machineTokenInfo": {
Expand All @@ -756,7 +756,7 @@ Feature: Command behaviour when attached to an Ubuntu Pro subscription
}
}
"""
And I create the file `/tmp/response-overlay.json` with the following:
And I create the file `/var/lib/ubuntu-advantage/response-overlay.json` with the following:
"""
{
"https://contracts.canonical.com/v1/contracts/testCID/machine-activity/testMID": [
Expand All @@ -773,8 +773,8 @@ Feature: Command behaviour when attached to an Ubuntu Pro subscription
And I append the following on uaclient config:
"""
features:
machine_token_overlay: "/tmp/machine-token-overlay.json"
serviceclient_url_responses: "/tmp/response-overlay.json"
machine_token_overlay: "/var/lib/ubuntu-advantage/machine-token-overlay.json"
serviceclient_url_responses: "/var/lib/ubuntu-advantage/response-overlay.json"
"""
When I delete the file `/var/lib/ubuntu-advantage/jobs-status.json`
And I run `python3 /usr/lib/ubuntu-advantage/timer.py` with sudo
Expand Down
2 changes: 1 addition & 1 deletion features/attached_enable.feature
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Feature: Enable command behaviour when attached to an Ubuntu Pro subscription
One moment, checking your subscription first
Ubuntu Pro: ESM Infra is not available for Ubuntu .*
"""
When I create the file `/tmp/machine-token-overlay.json` with the following:
When I create the file `/var/lib/ubuntu-advantage/machine-token-overlay.json` with the following:
"""
{
"machineTokenInfo": {
Expand Down
4 changes: 2 additions & 2 deletions features/attached_status.feature
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Feature: Attached status
When I run `pro status --format yaml` as non-root
Then stdout is a yaml matching the `ua_status` schema

When I create the file `/tmp/machine-token-overlay.json` with the following:
When I create the file `/var/lib/ubuntu-advantage/machine-token-overlay.json` with the following:
"""
{
"machineTokenInfo": {
Expand All @@ -22,7 +22,7 @@ Feature: Attached status
And I append the following on uaclient config:
"""
features:
machine_token_overlay: "/tmp/machine-token-overlay.json"
machine_token_overlay: "/var/lib/ubuntu-advantage/machine-token-overlay.json"
"""
And I run `pro status` with sudo
Then stdout contains substring:
Expand Down
4 changes: 2 additions & 2 deletions features/i18n.feature
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Feature: Pro supports multiple languages
Then stdout is a json matching the `ua_status` schema
When I run `pro status --format yaml` as non-root
Then stdout is a yaml matching the `ua_status` schema
When I create the file `/tmp/machine-token-overlay.json` with the following:
When I create the file `/var/lib/ubuntu-advantage/machine-token-overlay.json` with the following:
"""
{
"machineTokenInfo": {
Expand All @@ -173,7 +173,7 @@ Feature: Pro supports multiple languages
And I append the following on uaclient config:
"""
features:
machine_token_overlay: "/tmp/machine-token-overlay.json"
machine_token_overlay: "/var/lib/ubuntu-advantage/machine-token-overlay.json"
"""
And I run `pro status` with sudo
Then stdout contains substring:
Expand Down
2 changes: 1 addition & 1 deletion features/motd_messages.feature
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Feature: MOTD Messages
Renew your subscription at https:\/\/ubuntu.com\/pro\/dashboard

"""
When I create the file `/tmp/machine-token-overlay.json` with the following:
When I create the file `/var/lib/ubuntu-advantage/machine-token-overlay.json` with the following:
"""
{
"machineTokenInfo": {
Expand Down
7 changes: 5 additions & 2 deletions features/steps/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,13 @@ def when_i_set_the_machine_token_overlay(context):
yaml.safe_load(context.text), cls=util.DatetimeAwareJSONEncoder
)
when_i_create_file_with_content(
context, "/tmp/machine-token-overlay.json", text=json_text
context,
"/var/lib/ubuntu-advantage/machine-token-overlay.json",
text=json_text,
)
change_config_key_to_use_value(
context,
"features",
"{ machine_token_overlay: /tmp/machine-token-overlay.json}",
"{ machine_token_overlay: "
"/var/lib/ubuntu-advantage/machine-token-overlay.json}",
)
26 changes: 26 additions & 0 deletions systemd/apt-news.service
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,29 @@ Description=Update APT News
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /usr/lib/ubuntu-advantage/apt_news.py
AppArmorProfile=ubuntu_pro_apt_news
CapabilityBoundingSet=~CAP_SYS_ADMIN
CapabilityBoundingSet=~CAP_NET_ADMIN
CapabilityBoundingSet=~CAP_NET_BIND_SERVICE
CapabilityBoundingSet=~CAP_SYS_PTRACE
CapabilityBoundingSet=~CAP_NET_RAW
PrivateTmp=true
RestrictAddressFamilies=~AF_NETLINK
RestrictAddressFamilies=~AF_PACKET
# These may break some tests, and should be enabled carefully
#NoNewPrivileges=true
#PrivateDevices=true
#ProtectControlGroups=true
# ProtectHome=true seems to reliably break the GH integration test with a lunar lxd on jammy host
#ProtectHome=true
#ProtectKernelModules=true
#ProtectKernelTunables=true
#ProtectSystem=full
#RestrictSUIDSGID=true
# Unsupported in bionic
# Suggestion from systemd.exec(5) manpage on SystemCallFilter
#SystemCallFilter=@system-service
#SystemCallFilter=~@mount
#SystemCallErrorNumber=EPERM
#ProtectClock=true
#ProtectKernelLogs=true
34 changes: 34 additions & 0 deletions uaclient/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import glob
import logging
import os
import re
import shutil
from typing import List, Optional # noqa: F401

from uaclient import (
Expand Down Expand Up @@ -44,6 +46,11 @@

USER_LOG_COLLECTED_LIMIT = 10

# XXX handle /etc/apparmor.d/local/<name>, which has the local admin overrides
APPARMOR_PROFILES = [
panlinux marked this conversation as resolved.
Show resolved Hide resolved
"/etc/apparmor.d/ubuntu_pro_apt_news",
]


def attach_with_token(
cfg: config.UAConfig, token: str, allow_enable: bool
Expand Down Expand Up @@ -285,3 +292,30 @@ def collect_logs(cfg: config.UAConfig, output_dir: str):
system.write_file(
os.path.join(output_dir, os.path.basename(f)), content
)

# get apparmor logs
# can't use journalctl's --grep, because xenial doesn't support it :/
kernel_logs, _ = system.subp(
panlinux marked this conversation as resolved.
Show resolved Hide resolved
["journalctl", "-b", "-k", "--since=1 day ago"]
)
apparmor_logs = []
if kernel_logs:
# filter out only what interests us
for kernel_line in kernel_logs.split("\n"):
if re.search(
r"apparmor=\".*(profile=\"ubuntu_pro_|name=\"ubuntu_pro_)",
kernel_line,
):
apparmor_logs.append(kernel_line)
system.write_file(
"{}/apparmor_logs.txt".format(output_dir), "\n".join(apparmor_logs)
)

# include apparmor profiles
for f in APPARMOR_PROFILES:
if os.path.isfile(f):
try:
shutil.copy(f, output_dir)
except Exception as e:
LOG.warning("Failed to copy file: %s\n%s", f, str(e))
continue
Loading
Loading