Files
tinypilot-kvm/app/update/launcher.py
Michael Lynch 49a888f404 Transfer privileged scripts to core repo (#1148)
# Overview

This change moves all the files that end up in /opt/tinypilot-privileged
from the Ansible role repo to this repo. Instead of installing these
files with Ansible, we'll install them with the TinyPilot Debian
package.

# Background

Previously, TinyPilot scripts that required root privileges lived in the
ansible-role-tinypilot repo. This was due to security considerations for
installing TinyPilot via Ansible. Our security model assumed that the
tinypilot user had write access to everything in the TinyPilot git repo,
so we couldn't keep scripts with sudo privileges in a place where
tinypilot could modify them, as that would effectively give tinypilot
root privileges (if tinypilot can modify a script and run it as root, it
effectively has arbitrary root permisssions).

When we switched to Debian-based installs, we no longer had to worry
about the tinypilot user being able to access files in the TinyPilot git
repo, as the Debian package manager now provisions the device with the
proper files and puts them in the right place.

# Benefits

* If we need to call a privileged script from the TinyPilot web app, we
no longer have to coordinate changes across two repos
* Installing privilged scripts with Debian instead of Ansible is
significantly faster
* The Debian package manager takes responsibility for cleaning up
TinyPilot-owned files on upgrade/uninstall

# Changes

## `privileged-scripts` organization

One significant change I'm applying here is to the file layout for
scripts that require the tinypilot system user to execute with sudoers.
Previously, all privileged scripts were in the same
/opt/tinypilot-privileged directory.

In this change, I'm placing files in different subdirectories depending
on whether they require tinypilot to be able to execute them with
sudoers.

The layout I propose is:

* /opt/tinypilot-privileged/scripts: For files that require root and the
tinypilot user must execute them via sudo
* /opt/tinypilot-privileged/services: For files that require root but
the tinypilot user never executes them (like
[init-usb-gadget](b6c905c4cf/files/init-usb-gadget))

Update: We're not populating the `services` subdirectory in this PR, but
we will in a subsequent Pro PR.

## sudoers.d

I learned from our hardware partners that it's better practice to have a
TinyPilot-owned file in the `/etc/sudoers.d/` directory than to directly
modify the root `/etc/sudoers` file.


https://www.digitalocean.com/community/tutorials/how-to-edit-the-sudoers-file

This change places TinyPilot's sudoers rules under
`/etc/sudoers.d/tinypilot`. We don't attempt to clean up old entries in
`/etc/sudoers` on legacy systems, but they shouldn't cause any harm.

# Other considerations

## Debug log symlink

Because we sometimes tell users to collect debug logs from the
command-line via `sudo /opt/tinypilot-privileged/collect-debug-logs`, I
left a symlink there so that we can still give that as an instruction
without worrying about which version of TinyPilot the user is running.

## Ansible coordination

These changes go along with corresponding Ansible changes, but we don't
need to do complex coordination. These changes should go in before the
Ansible changes, but it's okay there's a brief period where both the
Ansible role and the Debian package are placing the same files, but that
shouldn't cause a problem and should only be in that state for a few
minutes.

Ansible changes:
https://github.com/tiny-pilot/ansible-role-tinypilot/pull/233

## Testing

I've tested these changes on [a full image install of TinyPilot
Pro](https://github.com/tiny-pilot/tinypilot-pro/pull/669) and verified
that all the functionality that depends on `tinypilot-privileged` still
works:

* Changed video settings
* Mounted/unmounted virtual media
* Changed hostname
* Enabled SSH
* Performed an update (fails at the end because it can't downgrade
Debian packages, but that's unrelated to these changes)

As reviewer, you don't need to duplicate these tests.

Co-authored-by: Bilal Wasim <bilalwasim676@gmail.com>
2022-11-29 08:33:49 -05:00

40 lines
970 B
Python

import logging
import subprocess
import update.result_store
import update.status
logger = logging.getLogger(__name__)
class Error(Exception):
pass
class AlreadyInProgressError(Error):
pass
UPDATE_SCRIPT_PATH = '/opt/tinypilot-privileged/scripts/update'
def start_async():
"""Launches the update service asynchronously.
Launches the tinypilot-update systemd service in the background. If the
service is already running, raises an exception.
Raises:
AlreadyInProgressError if the update process is already running.
"""
current_state, _ = update.status.get()
if current_state == update.status.Status.IN_PROGRESS:
raise AlreadyInProgressError('An update is already in progress')
update.result_store.clear()
# Ignore pylint since we're not managing the child process.
# pylint: disable=consider-using-with
subprocess.Popen(
('sudo', '/usr/sbin/service', 'tinypilot-updater', 'start'))