1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
summary: Verify that snap-interfaces-requests-control grants read-access to relevant endpoints
details: |
The snap-interfaces-requests-control interface grants access to prompting clients
(e.g. prompting-client) to a subset of snapd's API endpoints needed
to view and manage request prompts and request rules.
Specifically:
- /v2/notices: to read interfaces-requests-prompt and interfaces-requests-rule-update notices
- /v2/interfaces/requests/prompts: to receive and reply to request prompts
- /v2/interfaces/requests/rules: to view and manage request rules
- /v2/system-info: to check whether prompting is supported/enabled
- /v2/snaps/{name}: to get details about installed snaps
systems: [ubuntu-2*]
environment:
# not all terminals support UTF-8, but Python tries to be smart and attempts
# to guess the encoding as if the output would go to the terminal, but in
# fact all the test does is pipe the output to (go)jq
PYTHONIOENCODING: utf-8
skip:
- reason: Ubuntu 20.04 kernel doesn't support prompting
if: os.query is-ubuntu 20.04
- reason: Ubuntu 22.04 kernel <6.7 doesn't support prompting
if: os.query is-ubuntu 22.04 && os.query is-kernel-lt 6.7
- reason: Ubuntu 22.04 AppArmor parser doesn't support prompting
if: os.query is-ubuntu 22.04 && not tests.info is-reexec-in-use
prepare: |
tests.session prepare -u test
snap remove --purge api-client || true
# prerequisite for having a prompts handler service
snap set system experimental.user-daemons=true
restore: |
tests.session restore -u test
debug: |
echo "Check kernel version"
uname -a
echo "Check kernel notification socket presence"
if ls /sys/kernel/security/apparmor/.notify ; then
echo "kernel notification socket exists"
else
echo "kernel notification socket does not exist"
fi
echo "Check system info"
snap debug api /v2/system-info
execute: |
"$TESTSTOOLS"/snaps-state install-local api-client
echo "The snap-interfaces-requests-control plug on the api-client snap is initially disconnected"
snap connections api-client | MATCH "snap-interfaces-requests-control +api-client:snap-interfaces-requests-control +- +-"
echo "Connect the snap-interfaces-requests-control plug"
snap connect api-client:snap-interfaces-requests-control
echo "Check snap can access interfaces-requests-prompt and interfaces-requests-rule-update notices under /v2/notices"
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/notices?types=interfaces-requests-prompt" | MATCH '"status-code":200'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/notices?types=interfaces-requests-rule-update" | MATCH '"status-code":200'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/notices" | MATCH '"status-code":200'
echo "But not other notice types"
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/notices?types=change-update,warning" | MATCH '"status-code":403'
echo "Check snap can access system info via /v2/system-info"
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/system-info" | MATCH '"status-code":200'
SNAP_NAME="snapd"
if os.query is-core16; then
SNAP_NAME="core"
fi
echo "Check snap can access snap info via /v2/snaps/{name}"
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/snaps/$SNAP_NAME" | MATCH '"status-code":200'
echo "Enable AppArmor prompting experimental feature"
snap set system experimental.apparmor-prompting=true
echo 'Check "apparmor-prompting" is shown as enabled in /v2/system-info'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/system-info" | \
gojq '."result"."features"."apparmor-prompting"."enabled"' | MATCH '^true$'
echo "Check snap can access prompts via /v2/interfaces/requests/prompts"
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/prompts" | MATCH '"status-code":200'
echo "Check snap can access a single prompt via /v2/interfaces/requests/prompts/<ID>"
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/prompts/1234123412341234" | MATCH '"status-code":404'
# TODO: include the "home" interface and create a request prompt by attempting to list contents of $HOME
# PROMPT_ID=FIXME
# tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/prompts/$PROMPT_ID" | MATCH '"status-code":200'
# echo "Check snap can reply to a prompt via /v2/interfaces/requests/prompts/<ID>
# TODO: split this line more
# tests.session -u test exec api-client --socket /run/snapd-snap.socket --method=POST '{"action":"allow","lifespan":"forever","constraints":{"path-pattern":"/**","permissions":["read"]}}' "/v2/interfaces/requests/prompts/$PROMPT_ID" | MATCH '"status-code":200'
# TODO: check that thread which triggered request completed successfully
echo "Check snap can access rules via /v2/interfaces/requests/rules"
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/rules" | MATCH '"status-code":200'
echo "Check snap can create rule via /v2/interfaces/requests/rules"
tests.session -u test exec sh -c 'api-client --socket /run/snapd-snap.socket --method=POST "/v2/interfaces/requests/rules" '\''{"action":"add","rule":{"snap":"api-client","interface":"home","constraints":{"path-pattern":"/path/to/file","permissions":{"read":{"outcome":"allow","lifespan":"forever"},"write":{"outcome":"deny","lifespan":"session"},"execute":{"outcome":"allow","lifespan":"timespan","duration":"1h"}}}}}'\' | tee result.json
MATCH '"status-code":200' result.json
RULE_ID=$(gojq '."result"."id"' < result.json | tr -d '"')
echo "Check snap can view a single rule via /v2/interfaces/requests/rules/<ID>"
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/rules/$RULE_ID" | MATCH '"status-code":200'
echo "Check that another user cannot view that rule via /v2/interfaces/requests/rules/<ID>"
api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/rules/$RULE_ID" | MATCH '"status-code":404,.*"message":"user not allowed to request the rule with the given ID"'
echo "Check snap can modify/remove a single rule via /v2/interfaces/requests/rules/<ID>"
tests.session -u test exec api-client --socket /run/snapd-snap.socket --method=POST "/v2/interfaces/requests/rules/$RULE_ID" '{"action":"remove"}' | MATCH '"status-code":200'
echo "Check that snap can access icons info via /v2/icons and gets 404 if icon is not found"
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/icons/api-client/icon" | MATCH '"status-code":404'
echo "Without snap-interfaces-requests-control the snap cannot access those API endpoints"
snap disconnect api-client:snap-interfaces-requests-control
# XXX: this relies on the fact that prompting remains enabled, even though
# the prerequisite of there being a snap with snap-interfaces-requests-control
# connected and a handler service running is no longer true. Otherwise, the error
# code would be 500 instead of 403.
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/notices?types=interfaces-requests-prompt" | MATCH '"status-code":403'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/notices?types=interfaces-requests-rule-update" | MATCH '"status-code":403'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/system-info" | MATCH '"status-code":403'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/snaps/$SNAP_NAME" | MATCH '"status-code":403'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/prompts" | MATCH '"status-code":403'
# Try to access an arbitrary prompt ID, should fail with 403 rather than 404
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/prompts/1234123412341234" | MATCH '"status-code":403'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/rules" | MATCH '"status-code":403'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/interfaces/requests/rules/$RULE_ID" | MATCH '"status-code":403'
tests.session -u test exec api-client --socket /run/snapd-snap.socket "/v2/icons/api-client/icon" | MATCH '"status-code":403'
|