From 29004e4342b7846abc2e4b7447fbd0d950e4eeb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 06:50:10 +0100 Subject: [PATCH 01/14] doc: remove some outdated documentation --- doc/locale_api.md | 295 ---------------------- doc/multi_process_backend.md | 56 ---- rust/agama-cli/doc/CLI_API.md | 248 ------------------ rust/agama-cli/doc/backend-for-testing.md | 40 --- 4 files changed, 639 deletions(-) delete mode 100644 doc/locale_api.md delete mode 100644 doc/multi_process_backend.md delete mode 100644 rust/agama-cli/doc/CLI_API.md delete mode 100644 rust/agama-cli/doc/backend-for-testing.md diff --git a/doc/locale_api.md b/doc/locale_api.md deleted file mode 100644 index 407d0686c8..0000000000 --- a/doc/locale_api.md +++ /dev/null @@ -1,295 +0,0 @@ -# Legacy-free Locale Service - -Problem statement: (2023-03) - -> Agama currently has a separate Language service, although it's rather -> simplistic. It just allows to set the language of the installed system using -> Yast::Language.Set. And it's quite memory demanding for such an unimpressive -> task. - -> That service would be a nice candidate to be rewritten from scratch with no -> dependencies on YaST or Ruby. It's small enough and could give us a good -> overview on how much can we save. - -Original Plan: - -1. take the systemd APIs as a sensible starting point. -2. deviate only where we add value - -The problem with the original plan is that -the installer runs in one system (inst-sys, `/`) -and operates on another (target, `/mnt`) and we cannot use the full systemd -API. We may use `systemd-firstboot` instead but its API is much more limited. - -## Localization - -This design includes localized labels in the API. In other contexts that would -be a responsibility of the frontend, but here the backend has the -information, provided by _langtable_. - -(Languages, Territories and Timezones have localized names. Keyboards do not.) - -(Possible alternative: still include localized labels, but in a supplemental -method while the main method only provides the IDs (and English labels)) - -## Proposal - -A Proposal is what the installer proposes to the user -as settings to be applied to the target system. - -For example, when selecting the "German (Germany)" locale, -the timezone will be proposed to "Europe/Berlin". - -Design decision: put the proposal logic to the antecedent object, that is, -the Locale object will know how to change the Timezone object, -not the other way around (Timezone reacting to Locale value). - -### Overriding the User's Choice? - -
- -If setting the locale proposes the keyboard, what do we do if the user first -changes the keyboard and _then_ the locale? - - -When Agama UI first shows up, it may show default choices like: - -> Locale: English (US), Keyboard: US - -Then we change the locale to Czech, and the keyboard is adjusted automatically: - -> Locale: Czech, Keyboard: Czech - -We tune the keyboard: - -> Locale: Czech, Keyboard: Czech (qwerty) - -When we then change the locale, the keyboard could stay the same, as we have -already touched it: - -> Locale: German, Keyboard: Czech (qwerty) -
- -### Simple Design: Always Repropose - -We can easily afford throwing away the user's choice of keyboard layout and -simply set what we consider a good default for a newly set locale, because: - -1. it is just one setting (as opposed to whole partitioning layout) -2. the change will be visible in the UI, I assume - -### Detailed Design: Prioritize - -But other cases may not be as simple, so here's a generic design (NOT YET USED): - -All settings are wrapped in a `Priority` generic type (an Enum in Rust), -meaning, what is the source and importance of the setting: -- `Machine(data)` means the system has proposed it -- `Human(data)` means the user has made the choice - -In D-Bus, it is represented by wrapping the data in a struct, with a leading -byte* tagging the priority. For ease of recognition when watching bus traffic, -special numbers are used: -- `23` means Human, for the number of chromosome pairs -- `42` means Machine, as the famous Answer was given by Deep Thought, a machine - -In the following dump, we see that the locale was set by the user and the -system has adjusted the keyboard. - -``` -node ...Agama1/Locale { - interface ...Agama1.Locale { - properties: - readwrite (yas) Locale = (23, ['cs_CZ.UTF-8', 'de_DE.UTF-8']); - readwrite (y(ss)) X11Keyboard = (42, ('cz','qwerty)); - }; -}; -``` - -You may know a [similar settings in libzypp][resstatus] where it has 4 levels. - -*: maybe this is a crazy optimization? I am not too opposed to use strings for -this on the bus. - -[resstatus]: https://github.com/openSUSE/libzypp/blob/d441746c59f063b5d54833bfdebc48829b07feb5/zypp/ResStatus.h#L106 - - -## Interfaces - -### Language and Keyboard - -- when setting the locale, adjust the proposed package selection and keyboard - accordingly. And timezone. - -The general design of the proposal layer is - -- declarative, using read-write properties -- setting some properties will make changes in the proposal layer of other - properties of other objects - -I don't know: should the proposal be adjusted automatically as part of the property setter, or should it be explicit? - -So here, setting `Locale` below will set also `VConsoleKeyboard` here and - - Agama...Software...todo(...) - - Agama...Timezone...todo(...) - -For the first version of the API, let's keep things simple: - -**LocaleType** is just one string, the value for the `LANG` variable, like -`"cs_CZ.UTF-8"`. - -**VConsoleKeyboardType** is a string, for example -`"cz-qwerty"` or `"us"`. - -`systemd-firstboot` only has an option for the console keymap, but we have a -way to propagate it to X11, see [bsc#1046436](https://bugzilla.suse.com/show_bug.cgi?id=1046436) - -We don't expose the X11 keyboard, instead letting systemd do it via the -_convert_ parameter. - -(The other systemd keyboard settings are X11Model and X11Options, we don't -have UI or data for that) - -NOTE: _langtable_ on the other hand only deals with the X11 keyboards, -linking them to languages and territories. - -``` -# this is gdbus syntax BTW -node /org/opensuse/Agama1/Locale { - interface org.opensuse.Agama1.Locale { - methods: - # In the same order as in SupportedLocales, pairs of - # (english_labels, native_labels), where foo_labels - # is a pair of (language, territory) - LabelsForLocales( - out a((ss)(ss)) id_english_native # [(("Spanish", "Spain"), ("Español", "España")), (('English', 'United States'), ('English', 'United States'))] - ) - ListVConsoleKeyboards( - out as ids # like ["cz", "cz-qwerty", "gb-intl", "us", "us-dvorak",…] - ) - - # ProposeKeyboard(); # not needed? adjusted automatically, same object - # Sets Agama/TimeDate1's Timezone (but not LocalRTC, that's for Storage to say?) - ProposeTimeDate(); # different object but same service - ProposeSoftware(); # different service - - Commit(); - properties: - - # The locale service DOES NOT KNOW which locales are - # available for the product currently selected for installation. - # When the user chooses a product, SupportedLocales should be set. - # It affects the output of LabelsForLocales - # and the valid inputs for Locales. - readwrite as SupportedLocales = ["es_ES.UTF-8", "en_US.UTF-8"]; - - # NOTE: "as" has different meaning to systemd, - # we have a list of LANG settings, 1st gets passed to systemd, - # others affect package selection - readwrite as Locales = ['cs_CZ.UTF-8', 'de_DE.UTF-8']; - - readwrite s VConsoleKeyboard = 'cz-qwerty'; - }; -}; -``` - -#### Systemd - -
- -For reference, the systemd API for Locale(Language) and Keyboard is this: - - -``` -$ gdbus introspect -y -d org.freedesktop.locale1 -o /org/freedesktop/locale1 -node /org/freedesktop/locale1 { - interface org.freedesktop.locale1 { - methods: - SetLocale(in as locale, - in b interactive); - SetVConsoleKeyboard(in s keymap, - in s keymap_toggle, - in b convert, - in b interactive); - SetX11Keyboard(in s layout, - in s model, - in s variant, - in s options, - in b convert, - in b interactive); -… -$ busctl --system introspect org.freedesktop.locale1 /org/freedesktop/locale1 -(all properties are read-only and emit PropertiesChanged) -.Locale property as 1 "LANG=en_US.UTF-8" -.VConsoleKeymap property s "cz-lat2-us" -.VConsoleKeymapToggle property s "" -.X11Layout property s "cz,us" -.X11Model property s "pc105" -.X11Options property s "terminate:ctrl_alt_bksp,grp:shift_togg… -.X11Variant property s "qwerty,basic" -``` - -
- -### Timezone - -``` -node /org/opensuse/Agama/TimeDate1 { - interface org.opensuse.Agama.TimeDate1 { - methods: - ListTimezones( - in s display_locale # "de_DE.UTF-8" - out a(ss) id_label_pairs # [('Europe/Prague', 'Europa/Prag')] - ) - # success? do we need a specific return value other than some Error? - Commit(); - properties: - readwrite s Timezone = 'Europe/Prague'; - readwrite b LocalRTC = false; - }; -}; -``` - -#### Systemd - -
- -For reference, the systemd API for Time and Timezone is this: - - -(I find `gdbus` verbose output better for methods and `busctl` terse output -better for properties) - -``` -$ gdbus introspect -y -d org.freedesktop.timedate1 -o /org/freedesktop/timedate1 -node /org/freedesktop/timedate1 { … - interface org.freedesktop.timedate1 { … - methods: - SetTime(in x usec_utc, - in b relative, - in b interactive); - SetTimezone(in s timezone, - in b interactive); - SetLocalRTC(in b local_rtc, - in b fix_system, - in b interactive); - SetNTP(in b use_ntp, - in b interactive); - ListTimezones(out as timezones); -… -$ busctl --system introspect org.freedesktop.timedate1 /org/freedesktop/timedate1 -NAME TYPE SIG RESULT/VALUE FLAGS -(properties are read only) -.CanNTP property b true - -.LocalRTC property b false emits-change -.NTP property b false emits-change -.NTPSynchronized property b false - -.RTCTimeUSec property t 1681214874000000 - -.TimeUSec property t 1681214874046139 - -.Timezone property s "Europe/Prague" emits-change -``` - -"LocalRTC" means "is the local time zone used for the real time clock", -so it's !hwclock_in_UTC - -
diff --git a/doc/multi_process_backend.md b/doc/multi_process_backend.md deleted file mode 100644 index 1d86126589..0000000000 --- a/doc/multi_process_backend.md +++ /dev/null @@ -1,56 +0,0 @@ -## Multi-Process Backend - -The idea is to have a backend consisting of several services that communicate -via D-Bus. The goals of such an approach are: - -* Parallelize as much work as possible, especially relevant during probing. -* Keep service responsiveness even when a long-running task is running. -* Improve reusability by building smaller D-Bus-based services. - -## Processes or threads - -We decided to go for processes instead of threads because the latter are known -to cause problems to YaST and it is easier to avoid race conditions. Moreover, -we could even reimplement any of those services in a different language in the -future. - -## Proof-of-concept - -Untangling the YaST code into different processes is quite a challenging task. -Take the *storage* API as an example: other components, like *bootloader*, use -its API extensively. Hence we decided to extract the *users handling* part in -the first place. Of course, in terms of speed and responsiveness, it does not -bring much benefit, but we thought it was a good starting point because: - -1. Users handling was (partially) refactored recently, so it is well covered - by unit tests. -2. No other Agama component relies on users handling. - -### Users - -We found these dependencies: - -- `MailAliases` (which depends on `MailTable`): no other Agama component - depends on it. -- `ShadowConfig` uses CFA to modify the `login.defs` file, which is also used - by `Security`. However, it looks like `Security` is not used in other parts - of the installer. -- `Autologin`, which uses many modules, including packages, can be tricky. It - basically checks which supported Display Managers are available. -- `ProductFeatures`, which should be replaced with Agama configuration - mechanism. Ignored by now. - -We reached these agreements: - -- This PoC uses a special directory (`service/lib/agama/dbus/y2dir`) - which contains a modified version of the dependencies. This directory is - added to `Y2DIR`, so these modules are used instead of the original ones. -- Having `Agama::DBus::Clients` for D-Bus clients that are needed to - communicate between different processes. - -## Future steps - -- We agreed on using a separate process for questions. The API should allow to - asking questions and replying to them. -- Software is the most time-consuming aspect of the installation, so we should - aim to move it to a separate process. diff --git a/rust/agama-cli/doc/CLI_API.md b/rust/agama-cli/doc/CLI_API.md deleted file mode 100644 index bc8e12f135..0000000000 --- a/rust/agama-cli/doc/CLI_API.md +++ /dev/null @@ -1,248 +0,0 @@ -# Agama CLI - -Agama already shipped an initial CLI prototype for managing and driving the installation process. Note that such a CLI was created as a proof of concept, and its current API needs some refactoring. This document is intended to discuss how the new CLI should look like, what patterns to follow, etc. - -## CLI Guidelines - -There already are guidelines for creating modern CLI applications. For example [clig.dev](https://clig.dev/) defines a guide that is agnostic about programming languages and tooling in general, and it can be perfectly used as reference for Agama CLI. - -## Command name - -Some naming recommendations from the guidelines: - -* Make it a simple, memorable word -* Use only lowercase letters, and dashes if you really need to -* Keep it short -* Make it easy to type - -Currently we have two executables: `agamactl` for managing the D-Bus services and `agama` for configuring and performing the installation. - -## Subcommands - -Let's list the recommendations from the guidelines: - -* Be consistent across subcommands. Use the same flag names for the same things, have similar output formatting, etc. -* Use consistent names for multiple levels of subcommand. If a complex piece of software has lots of objects and operations that can be performed on those objects, it is a common pattern to use two levels of subcommand for this, where one is a noun and one is a verb. For example, `docker container create`. Be consistent with the verbs you use across different types of objects. -* Don’t have ambiguous or similarly-named commands. For example, having two subcommands called “update” and “upgrade” is quite confusing. - -## New CLI - -The API of the current CLI is not consistent. It sometimes uses verbs for the subcommand action (e.g., `agama user clear`), and for other subcommands adjectives or nouns are used (e.g., `agama language selected `). Moreover, there is a subcommand per each area, for example `agama language`, `agama software`, `agama storage`, etc. Having a subcommand for each area is not bad per se, but for some areas like storage the subcommand could grow with too many actions and options. - -The new CLI could be designed with more generic subcommands and verbs, allowing to configure any installation setting in a standard way. Note that the installation process can be already configured by means of a YAML config file with `agama config load `. And the options currently supported by the config file are: - -~~~ ---- -product: "Tumbleweed" - -languages: - - "es_ES" - - "en_US" - -disks: - - /dev/vda - - /dev/vdb - -user: - name: "test" - fullname: "User Test" - password: "12345" - autologin: true - -root: - ssh_key: "1234abcd" - password: "12345" -~~~ - -We could extend the `config` subcommand for editing such a config without the need of a subcommand per area. In general, the `config` subcommand should have verbs for the following actions: - -* To load a YAML config file with the values for the installation. -* To edit any value of the config without loading a new complete file again. -* To show the current config for the installation. -* To validate the current config. - -Moreover, the CLI should also offer subcommands for these actions: - -* To ask for the possible values that can be used for some setting (e.g., list of available products). -* To start and abort the installation. -* To see the installation status. - -Let's assume we will use `agamactl` for managing D-Bus services and `agama` for driving the installation (the opposite as it is now). The CLI for Agama could look like something similar to this: - -~~~ -$ agama install -Starts the installation. - -$ agama abort -Aborts the installation. - -$ agama status -Prints the current status of the installation process and informs about pending actions (e.g., if there are questions waiting to be answered, if a product is not selected yet, etc). - -$ agama watch -Prints messages from the installation process (e.g., progress, questions, etc). - -$ agama config load -Loads installation config from a YAML file, keeping the rest of the config as it is. - -$ agama config show [] -Prints the current installation config in YAML format. If a is given, then it only prints the content for the given key. - -$ agama config set = ... -Sets a config value for the given key. - -$ agama config unset -Removes the current value for the given key. - -$ agama config reset [] -Sets the default value for the given . If no key is given, then the whole config is reset. - -$ agama config add [=] ... -Adds a new entry with all the given key-value pairs to a config list. The key is omitted for a list of scalar values (e.g., languages). - -$ agama config delete [=] ... -Deletes any entry matching all the given key-value pairs from a config list. The key is omitted for a list of scalar values. - -$ agama config check -Validates the config and prints errors - -$ agama info [] -Prints info about the given key. If no value is given, then it prints what values are admitted by the given key. If a value is given, then it shows extra info about such a value. - -$ agama summary [
] -Prints a summary with the actions to perform in the system. If a section is given (e.g., storage, software, ...), then it only shows the section summary. - -$ agama questions -Prints questions and allows to answer them. - -~~~ - -In those commands `` represents a YAML key from the config file (e.g., `root.ssh_key`) and `` is the value associated to the given key. Note that dots are used for nested keys. Let's see some examples: - -~~~ -# Set a product -$ agama config set product=Tumbleweed - -# Set user values -$ agama config set user.name=linux -$ agama config set user.fullname=linux -$ agama config set user.password=linux -$ agama config set user.name=linux user.fullname=linux user.password=12345 - -# Unset user -$ agama config unset user - -# Add and delete languages -$ agama config add languages en_US -$ agama config delete languages en_US - -# Set storage settings -$ agama config set storage.lvm=false -$ agama config set storage.encryption_password=12345 - -# Add and delete candidate devices -$ agama config add storage.candidate_devices /dev/sda -$ agama config delete storage.candidate_devices /dev/sdb - -# Add and delete storage volumes -$ agama config add storage.volumes mountpoint=/ minsize=10GiB -$ agama config delete storage.volumes mountpoint=/home - -# Reset storage config -$ agama config reset storage - -# Show some config values -$ agama config show storage.candidate_devices -$ agama config show user - -# Dump config into a file -$ agama config show > ~/config.yaml - -# Show info of a key -$ agama info storage.candidate_devices -$ agama info storage.candidate_devices /dev/sda -$ agama info languages -~~~ - -### Config file - -The current YAML config file needs to be extended in order to support the new storage proposal settings offered by the D-Bus API: - -~~~ -... - -storage: - candidate_devices: - - /dev/sda - lvm: true - encryption_password: 12345 - volumes: - - mountpoint: / - fstype: btrfs - - mountpoint: /home - fstype: ext4 - minsize: 10GiB -~~~ - -### Product Selection - -Agama can automatically infers all the config values, but at least one product must be selected. Selecting a product implies some actions in the D-Bus services (e.g., storage devices are probed). And the D-Bus services might emit some questions if needed (e.g., asking to provide a LUKS password). Because of that, the command for selecting a product could ask questions to the user: - -~~~ -$ agama config set product=ALP -> The device /dev/sda is encrypted. Provide an encryption password if you want to open it (enter to skip): -~~~ - -Another option would be to avoid asking questions directly, and to request the answer when another command is used (see *D-Bus Questions* section). - -If a product is not selected yet, then many commands cannot work. In that case, commands should inform about it: - -~~~ -$ agama config show -A product is not selected yet. Please, select a product first: agama config set product=. -~~~ - -### D-Bus Questions - -The CLI should offer a way of answering pending questions. For example, for single product live images the storage proposal is automatically done because the target product is already known. If some questions were emitted during the process, then they have to be answered before continuing using the CLI. Therefore, most of the commands would show a warning to inform about the situation and how to proceed: - -~~~ -$ agama config show -There are pending questions. Please, answer questions first: agama questions. -~~~ - -### Non Interactive Mode - -Commands should offer a `--non-interactive` option to make scripting possible. The non interactive mode should offer a way to answer questions automatically. Non interactive mode will be defined later in a following interation of the CLI definition. - -## Current CLI - -As reference, this was the old CLI: - -~~~ -dinstallerctl install # Perform the installation - -dinstallerctl config dump # Dump the current installation config to stdout -dinstallerctl config load # Load a config file and apply the configuration - -dinstallerctl language available # List available languages for the installation -dinstallerctl language selected [...] # Select the languages to install in the target system - -dinstallerctl rootuser clear # Clear root configuration -dinstallerctl rootuser password [] # Set the root password -dinstallerctl rootuser ssh_key [] # Set the SSH key for root - -dinstallerctl software available_products # List available products for the installation -dinstallerctl software selected_product [] # Select the product to install in the target system - -dinstallerctl storage actions # List the storage actions to perform -dinstallerctl storage available_devices # List available devices for the installation -dinstallerctl storage selected_devices [...] # Select devices for the installation - -dinstallerctl user clear # Clear the user configuration -dinstallerctl user set # Configure the user that will be created during the installation -dinstallerctl user show # Show the user configuration` -~~~ - - -Original post with discussion is at https://gist.github.com/joseivanlopez/808c2be0cf668b4b457fc5d9ec20dc73 diff --git a/rust/agama-cli/doc/backend-for-testing.md b/rust/agama-cli/doc/backend-for-testing.md deleted file mode 100644 index d23c993cf7..0000000000 --- a/rust/agama-cli/doc/backend-for-testing.md +++ /dev/null @@ -1,40 +0,0 @@ -# How to set up a backend for testing the CLI frontend - -I needed a testing instance of the Agama backend so that the -Rust command-line frontend has something to talk to. - -## Summary - -1. Take the container used for continuous integration (CI) testing of the - backend -2. Give it a git checkout of this repo -3. Install the backend within the container -4. Copy the frontend binary into the container - -## Considered Alternatives - -My first plan had a different finale, 4. Make the D-Bus service visible -ouside the container, but I hit an issue with D-Bus authentication, hopefully -solvable. (Update: `xdg-dbus-proxy` seems to work, ask mvidner about it) - -Josef wanted to test against a different container (`d-installer-backend`) but that one was a -bit old and the D-Bus API was mismatched between frontend and backend. - -## Details - -The container used is built in -[OBS systemsmanagement:Agama:Staging/agama-testing][agama-testing] and -downloaded from registry.o.o specified below. - -[agama-testing]: https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/agama-testing - -I basically picked the useful bits from the `integration-tests` part -of [.github/workflows/ci.yml][ci.yml]. - -[ci.yml]: https://github.com/openSUSE/agama/blob/25462f57ab695d6910beb59ff0b21a7afaeda47e/.github/workflows/ci.yml - -## Resulting Script - -The script, which used to be inlined here, is now at -[`/testing_using_container.sh`](../../../testing_using_container.sh). ->>>>>>> 8f2f0404 (copied the script part of rust/agama-cli/doc/backend-for-testing.md) From 042d88e9920594ea159a384451d1ae13bd19c1fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 06:58:21 +0100 Subject: [PATCH 02/14] doc: remove references to the --generate-token option --- doc/agama-security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/agama-security.md b/doc/agama-security.md index 2e33326e1d..ad8a36bb63 100644 --- a/doc/agama-security.md +++ b/doc/agama-security.md @@ -8,7 +8,7 @@ As frontend Agama offers a web based user interface (web UI) or a commandline in Authorization is done via password. To get authorized the frontend has to provide the root password (root on the backend's system). The password is validated through PAM [1]. Once the authorization succeeds, the backend generates an authorization token and passes it back to frontend. Agama uses [JSON Web Token (JWT)] [2] as authorization token [3]. All subsequent calls to the API has to be done together with the token. In case of the web UI, the token is stored in a HTTP-only cookie. -Agama supports special use case when Agama's UI or CLI is used in live installation media. In such case skipping autorization is supported to get feeling of using a desktop application. However, skipping authorization happens only for local access. When connecting remotely, authorization is still in place. Skipping of authorization is made possible thanks to option ```--generate-token```. When this option is used, Agama's web server service generates valid JWT automatically on start. The token is stored locally [4]. To make it usable for web UI, token is imported into web browser's internal database by Agama provided startup [5] script. The script prepares custom profile for Firefox with predefined homepage pointing to Agama's special login page with the generated token as part of a get request in the homepage url. As part of the response, the token is stored as `httpOnly` cookie. In case of CLI the situation is way easier as the token can be accessed and used directly as needed from well known location [4]. +Agama supports special use case when Agama's UI or CLI is used in live installation media. In such case skipping autorization is supported to get feeling of using a desktop application. However, skipping authorization happens only for local access. When connecting remotely, authorization is still in place. Agama's web server service generates valid JWT automatically on start. The token is stored locally [4]. To make it usable for web UI, token is imported into web browser's internal database by Agama provided startup [5] script. The script prepares custom profile for Firefox with predefined homepage pointing to Agama's special login page with the generated token as part of a get request in the homepage url. As part of the response, the token is stored as `httpOnly` cookie. In case of CLI the situation is way easier as the token can be accessed and used directly as needed from well known location [4]. ### JWT From 488f8c1a4ec0445917bf66513cdc70dade7970cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 11:13:47 +0100 Subject: [PATCH 03/14] doc: rename the security document --- doc/{agama-security.md => security.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/{agama-security.md => security.md} (100%) diff --git a/doc/agama-security.md b/doc/security.md similarity index 100% rename from doc/agama-security.md rename to doc/security.md From 18d0d0ed19ec0d3af2127a01de3db00075a97e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 09:23:44 +0100 Subject: [PATCH 04/14] doc: update the architecture documentation --- doc/architecture.md | 98 +++++++++++++++++++++++ doc/new_architecture.md | 173 ---------------------------------------- 2 files changed, 98 insertions(+), 173 deletions(-) create mode 100644 doc/architecture.md delete mode 100644 doc/new_architecture.md diff --git a/doc/architecture.md b/doc/architecture.md new file mode 100644 index 0000000000..d26ad1bc01 --- /dev/null +++ b/doc/architecture.md @@ -0,0 +1,98 @@ +# Agama's architecture + +On the surface, Agama implements a typical client-server architecture. The server offers an HTTP/ +JSON API with a WebSocket to send messages to the connected clients. The web and the command-line +interfaces, part of Agama, connect to that server. + +However, things are more complex, and the server comprises different pieces, as described in this +document. + +## Components + +On the server side, Agama is composed by: + +* **Agama server**: from a user's perspective, this is the core of Agama. It is responsible for: + * Implementing (part of) the installation logic. A good share of this logic is delegated to + **Agama YaST**. + * Offering an HTTP and WebSocket (HTTP/WS) interface. + * Making the **web-based user interface** available to the browsers. + +* **Agama YaST service**: it is written in Ruby and has direct access to YaST libraries. This + component implements complex parts, like storage and software handling. Communication with the + Agama web server happens over D-Bus. + +* **Agama D-Bus server**: implements a minimal API to allow **Agama YaST server** to talk to the web +server. It is expected to be replaced by direct communication in the future. + +On the client side, these are the main components: + +* **Web user interface (old `cockpit-agama`)**: Agama's graphical user interface. The **Agama web +server** makes this React application available to browsers. + +* **Command Line Interface (`agama-cli`)**: it allows interaction with Agama and drives the +auto-installation process. + +* **Auto-installation (`autoinstallation`)**: it is composed of a Systemd service (`agama-auto`) and +a script that relies on `agama-cli` binary. + +The following diagram could be better, but it represents the main components and their interactions. + +```mermaid +flowchart LR + subgraph Clients + Browser + CLI + auto + end + + subgraph Agama Server + subgraph Web["Web Server"] + direction TB + UI["Web UI"] + API["HTTP/WS API"] + WS["WebSocket"] + end + + Web <--"Channel" --> Rust + end + Rust <-- "D-Bus" --> YaST["Agama YaST"] + + Browser --> UI + Browser --> API + Browser <---> WS + CLI --> API + CLI <---> WS + auto --> CLI +``` + +## Encryption + +In the case of a remote installation, the communication between the clients and the server must be +encrypted. Connecting to port 80 (HTTP) redirects the client to port 443 (HTTPS). + +About the certificate, Agama uses a self-signed certificate unless the user injects its own. + +## Authentication + +The HTTP interface allows authentication specifying the root password that will be checked +against PAM. + +On successful authentication, the server generates a [JSON Web Token][jwt] that the client +will include in the subsequent requests. The web client stores the token in an HTTP-only +cookie[^http-only] and the CLI uses a file with restricted permissions. + +[^http-only]: HTTP-only cookies cannot be accessed by client-side JavaScript. + +## Skipping the authentication + +When using Agama locally in the installation media, it would be unpleasant to ask for a password. +For that reason, Agama implements a mechanism to skip the authentication step. This mechanism is +documented in the [security document](./agama-security.md). + +## Links + +* https://bugzilla.suse.com/show_bug.cgi?id=1219688 +* https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html + +[http-auth]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication +[jwt]: https://jwt.io diff --git a/doc/new_architecture.md b/doc/new_architecture.md deleted file mode 100644 index a0d9b087e6..0000000000 --- a/doc/new_architecture.md +++ /dev/null @@ -1,173 +0,0 @@ -# Agama's 2024 architecture - -This document describes the proposal for the new Agama architecture. The reasons for introducing -these changes are recorded in [a discussion in Agama's repository][drop-cockpit]. - -[drop-cockpit]: https://github.com/openSUSE/agama/discussions/1000 - -But before describing how the architecture should look, let's quickly look at the current status. - -## The current architecture - -At this point, Agama is composed of four high-level components: - -* **Agama service**: implements the logic to perform the installation. It is the core component of -Agama and it offers a D-Bus interface. Actually, it is composed of two services: `rubygem-agama` and -`agama-dbus-server`. - -* **Web user interface (`cockpit-agama`)**: a web-based interface that plays the role of a GUI when -using Agama live. - -* **Command Line Interface (`agama-cli`)**: it allows to interact with Agama core and drives the -auto-installation process. - -* **Auto-installation (`autoinstallation`)**: it is composed by a Systemd service (`agama-auto`) and -a script that relies on `agama-cli`. - -In addition to those components, we need to consider Cockpit, which plays a vital role: - -* It makes communication between the web UI and the D-Bus services possible. -* It makes the web UI code available to the browser. -* It takes care of authenticating the user when connecting remotely. Again, it is only relative to -the web UI. - -```mermaid -flowchart LR - subgraph Clients - Browser - CLI - auto - end - - subgraph Cockpit - UI["Web UI"] - end - - subgraph Agama Service - Rust[Agama Rust] - Ruby[Agama Ruby] - end - - Browser <---> UI - UI <--D-Bus--> Rust - UI <--D-Bus--> Ruby - CLI <--D-Bus--> Rust - CLI <--D-Bus--> Ruby - Rust <--D-Bus---> Ruby - auto --> CLI -``` - -## The new architecture - -The proposed architecture is not that different from the current one, but it tries to meet these -goals: - -* Drop our dependency on Cockpit. -* Implement a higher-level API to be consumed by the clients, replacing D-Bus for client-server -communication. Agama will still use D-Bus for IPC between the Rust and Ruby components. - -### Components - -With those goals in mind, we are considering the following components: - -* **Agama core (old `agama-dbus-server`)**: implements the installation logic. It relies heavily on -the Agama YaST service. - -* **Agama YaST service (old `rubygem-agama`)**: it is written in Ruby and has direct access to YaST -libraries. Complex parts, like storage and software handling, are implemented in this component. - -* **HTTP and WebSocket API**: implements the API the clients should use to communicate with Agama. -Under the hood, it still uses D-Bus for communication between Agama core and Agama YaST. - -* **Web user interface (old `cockpit-agama`)**: Agama's graphical user interface. The web server -makes this React application available to the browsers. - -* **Command Line Interface (`agama-cli`)**: it allows interaction with Agama and drives the -auto-installation process. With the new architecture, connecting through the network might be -possible without SSH. - -* **Auto-installation (`autoinstallation`)**: as in the current architecture, it is composed by -a Systemd service (`agama-auto`) and a script that relies on `agama-cli`. - -The following diagram could be better, but it represents the main components and their interactions. - -```mermaid -flowchart LR - subgraph Clients - Browser - CLI - auto - end - - subgraph Agama Core - subgraph Web["Web Server"] - direction TB - UI["Web UI"] - API["HTTP/WS API"] - WS["WebSocket"] - end - - Web <--"Channel" --> Rust - end - Rust <-- "D-Bus" --> YaST["Agama YaST"] - - Browser --> UI - Browser --> API - Browser <---> WS - CLI --> API - CLI <---> WS - auto --> CLI -``` - -### The web-based API - -The new web-based API is divided into two different parts: - -* **HTTP/JSON API**: allows clients to execute actions or query Agama. For instance, it could get -the list of available products, request a new storage proposal or start the installation. About the -approach, something REST-like is the most suitable. Switching to GraphQL or gRPC do not seem to be -needed in our case (todo: write why). - -* **WebSocket**: Agama will use WebSockets to notify clients about any relevant event: progress, -configuration changes, etc. The event's format is not defined yet, but a JSON event containing the -`type` and the `payload/details` should be enough[^topics]. - -[^topics]: Ideally, the clients should be able to subscribe to the topics they are interested in. - But that feature can wait. - -### Encryption - -In the case of a remote installation, the communication between the clients and the server must be -encrypted. Connecting to port 80 (HTTP) should redirect the client to port 443 (HTTPS). - -About the certificate, Agama will use a self-signed certificate unless the user injects its own -certificate (through a kernel command line option). - -NOTE: under discussion. - -### Authentication - -The HTTP interface should allow authentication specifying a user and password that will be checked -against PAM. It is not clear yet, but we might need to check whether the logged user has permissions -(making sure it is `root` or through Polkit). - -On successful authentication, the server generates a [JSON Web Token][jwt] that the client will -include in the subsequent requests. The web client stores the token in an HTTP-only -cookie[^http-only] and the CLI uses a file with restricted permissions. - -[^http-only] HTTP-only cookies cannot be accessed byt client-side JavaScript. - -#### Skipping the authentication - -When using Agama locally in the installation media, it would be unpleasant to ask for a -user/password. For that reason, there must be a mechanism to skip the authentication step. Agama -Live could run a special service that generates a valid token and injects such a token into the -server, the CLI and the web browser. - -## Links - -* https://bugzilla.suse.com/show_bug.cgi?id=1219688 -* https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html - -[http-auth]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication -[jwt]: https://jwt.io From 6232966b3bf69ca789d37ef1d020cf2d9ed8bc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 11:01:51 +0100 Subject: [PATCH 05/14] doc: update the i18n documentation --- doc/i18n.md | 76 ++++++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/doc/i18n.md b/doc/i18n.md index 1ef2b7c55b..07c61f522c 100644 --- a/doc/i18n.md +++ b/doc/i18n.md @@ -26,7 +26,7 @@ - [ESLint Plugin](#eslint-plugin) - [Testing Language](#testing-language) - [Building POT File](#building-pot-file) - - [Cockpit Details](#cockpit-details) + - [Loading Web UI Translations](#loading-web-ui-translations) - [Development Server](#development-server) - [D-Bus Backend](#d-bus-backend) - [Backend Translations](#backend-translations) @@ -96,10 +96,6 @@ The basic translation workflow looks like this: 7. The PO files are processed during build so the translations can be used later at runtime -*Note: The Agama workflow is pretty similar to the [Cockpit localization]( -https://github.com/cockpit-project/cockpit/blob/main/doc/i18n.md) workflow, -we decided to use a similar approach here.* - ## Staging Translation Repository The special [agama-weblate](https://github.com/openSUSE/agama-weblate) @@ -220,10 +216,9 @@ web/share/update-manifest.py web/src/manifest.json The texts are marked for translation using the usual functions like `_()`, `n_()` and others. It is similar to the GNU gettext style. -Currently Agama uses the Cockpit implementation for loading and displaying -translations. To make this process transparent for developers and to allow easy -change of the implementation there is a simple [i18n.js]( ../web/src/i18n.js) -wrapper which provides the translation functions: +The mechanism of loading and displaying the translation is heavily inspired +in Cockpit's approach. The [i18n module](../web/src/i18n.js) offers a set of translation functions: + ```js import { _, n_, N_, Nn_ } from "~/i18n"; @@ -468,20 +463,19 @@ which defines the needed parameters for the `xgettext` tool. To build the POT file locally just run the script, it will save the output to the `agama.pot` file. -### Cockpit Details +### Loading Web UI Translations The translations are loaded by the `` HTML code in the [index.html](../web/src/index.html) file. But there is no such file -in Agama (or in any Cockpit plugin in general), there are files `po..js` -like `po.cs.js`. +in Agama but one `po..js` file per language, like `po.cs.js`. -The trick is that the Cockpit server checks the `CockpitLang` cookie sent in the +The trick is that Agama's web server checks the `agamaLang` cookie sent in the HTTP request header and returns the content from the respective file. ### Development Server -Because Cockpit serves the `po.js` file a bit differently as described in the -[Cockpit Details](#cockpit-details) section above we need to implement this +Because Agama serves the `po.js` file a bit differently as described in the +[](#loading-web-ui-translations) section above we need to implement this logic also in the development server. The [webpack-po-handler.js](../web/src/lib/webpack-po-handler.js) file @@ -490,22 +484,13 @@ language file. It uses redirection because the built translation files are only available in the webpack memory. But the result is the same, the browser gets a different content according to the currently configured language. -### D-Bus Backend - -The D-Bus service backend implements a writable `UILocale` property which -defines the locale used by the service. +### Backend Locale -For debugging purposes you can get the current value or set a new one using -command line: - -```shell -# get the current locale -busctl --address=unix:path=/run/agama/bus get-property org.opensuse.Agama1 /org/opensuse/Agama1/Locale org.opensuse.Agama1.Locale UILocale -# set a new locale -busctl --address=unix:path=/run/agama/bus set-property org.opensuse.Agama1 /org/opensuse/Agama1/Locale org.opensuse.Agama1.Locale UILocale s cs_CZ -``` +The Agama server exposes a `uiLocale` configuration option via the +`/api/l10n/locales/config` endpoint which defines the locale used by all the + services. -The `UILocale` property is a single global value shared by all connected clients. +The `uiLocale` property is a single global value shared by all connected clients. That means it is possible to use only one language for all clients, if more users connect to the server and uses a different UI language then there will be race conditions and the other users might see the texts coming from the @@ -515,6 +500,15 @@ This is a known limitation, we expect that only one user at a time will access the Agama installer or if multiple users use the same server we expect that they will be from the same team or company using the same language. +To check or set the locale you can use the `/api/l10n/locales/config` endpoint. +Alternatively, you can check (but not change) the current locale via D-Bus: + + +```shell +sudo busctl --address unix:path=/run/agama/bus get-property org.opensuse.Agama1 \ + /org/opensuse/Agama1/Locale org.opensuse.Agama1.Locale UILocale +``` + #### Backend Translations The backend might return texts from external components like `libstorage-ng`. @@ -528,32 +522,32 @@ Here are some hints what to do when some untranslated text appears in the Agama UI. 1. Check that the text is marked for translation, for a quick verification - you might try setting the [testing language](#testing-language) + you might try setting the [testing language](#testing-language). 2. If the text comes from backend or the other parts check that the appropriate - [language package](#backend-translations) is installed + [language package](#backend-translations) is installed. 3. The text should be [extracted into the POT file](#building-pot-file) 4. The [agama.pot]( https://github.com/openSUSE/agama-weblate/blob/master/web/agama.pot) in the `agama-weblate` repository is up to date, if not then run the [Weblate Update POT](https://github.com/openSUSE/agama/actions/workflows/weblate-update-pot.yml) - Github Action manually + Github Action manually. 5. The text is translated in the [Weblate repository]( - https://l10n.opensuse.org/projects/agama/) + https://l10n.opensuse.org/projects/agama/). 6. The translation is included in the respective PO file in the [agama-weblate]( - https://github.com/openSUSE/agama-weblate) repository + https://github.com/openSUSE/agama-weblate) repository. 7. The PO file in the [agama]( https://github.com/openSUSE/agama/tree/master/web/po) repository is up to date, if not the check whether there is an [open pull request]( https://github.com/openSUSE/agama/pulls?q=is%3Aopen+is%3Apr+label%3Atranslations+label%3Abot) with the change, if yes then check it and merge, if not then run the [Weblate Merge PO](https://github.com/openSUSE/agama/actions/workflows/weblate-merge-po.yml) - GitHub Action manually, then check the created pull request + GitHub Action manually, then check the created pull request. 8. The translated string should be present in the built packages, run `npm build` command and check the `dist/po.*.js` files, check the built RPM - package + package. 9. The translations are loaded by the browser, check the content loaded for the - `po.js` file -10. Check the current language used in the browser, run `cockpit.language` - command in the development tools console, check the `CockpitLang` cookie - value (run `document.cookie` command in the console) -11. Check the language used by the [service backend](#d-bus-backend) + `po.js` file. +10. Check the current language used in the browser, run `agama.language` + command in the development tools console, check the `agamaLang` cookie + value (run `document.cookie` command in the console). +11. Check the language used by the [Backend Locale](#backend-locale). From 46d00b60e2a0deb27a5f01f2f44ae4bfc9052b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 11:08:51 +0100 Subject: [PATCH 06/14] doc: drop non-relevant information about networking --- doc/network_support_design.md | 92 ----------------------------------- doc/networking.md | 28 +++++++++++ 2 files changed, 28 insertions(+), 92 deletions(-) delete mode 100644 doc/network_support_design.md create mode 100644 doc/networking.md diff --git a/doc/network_support_design.md b/doc/network_support_design.md deleted file mode 100644 index aac39ecfab..0000000000 --- a/doc/network_support_design.md +++ /dev/null @@ -1,92 +0,0 @@ -# Network Support Planning - -This document summarizes the plan to add proper networking support to Agama. It is still under -discussion, so expect some things to change. - -## What scenarios/features do we want to support? - -In general, we are focused on scenarios that are important for the installation process. Defining -a set up to be used *after the installation* is out of scope. - -Here is a preliminary list of the scenarios/features we would like to support. In general, we should -focus on scenarios that are important for the installation. - -* Specify a static configuration, useful where no DHCP is available or the configuration needs some - extra change: - - IPv4 / IPv6 (optionally in addition to the DHCP settings) - - DNS configuration (needed to reach the repositories) - - Routing configuration (needed to work on some specific networks) -* Use the DHCP provided configuration, allowing the user to adapt it if needed. -* Connect to a wireless network, adding support for the most common authentication mechanisms. -* Define a proxy to access the network. -* High availability scenarios. This features are critical when working with remote storage, network - redundancy and so on. NTP configuration very important here. We should support: - - Bonding - - Bridge - - VLAN -* s390 deployment: devices activation (port number and layer 2/3 configuration). -* VPN (?). If needed, which one? - -Other interesting use cases: - -* Provide multiple WiFi networks (in the unattended installation) and select one available during - installation. You could reuse the same profile and deploy on different places. - -## Current situation - -Networking support in Agama is far from being finished. At this point, only the web UI allows -setting up simple scenarios: - -* DHCP and static configuration of Ethernet devices. -* Connection to wireless devices with limited authentication settings. - -Moreover, it connects directly to the NetworkManager D-Bus interface, so the Agama service is not -really involved. So if you want to set up the network using the CLI, you need to use `nmcli` and -rely on Agama to copy the configuration files at the end of the installation. - -## Considered options - -Based on the situation described above, we considered these approaches: - -1. Implement support to set up the network through Agama D-Bus service. -2. Keep the status quo, extend the web UI and rely on `nmcli` for the CLI-based installation. For - automation, we could rely on third-party tool. - -Although it might be harder, option 1 looks more consistent: you can install your system just using -Agama D-Bus interface. - -## Adding our own D-Bus interface - -Adding a D-Bus interface does not mean that we need to implement a full solution to set up the -network. The idea is to build a good enough API to support our use cases. - -Initially, we though about adding the D-Bus API on top of [nmstate](https://nmstate.io/), although -it covers a different use-case. After playing a bit with this idea, we decided to come up with a -solution more aligned with our needs. - -As an alternative, you might be wondering why not use YaST2 itself? Let's see some reasons: - -* It does not implement support for reading the NetworkManager configuration. -* It is not able to talk to NetworkManager D-Bus interface. It configures NetworkManager by writing - the connection files. -* It is Ruby-based, so we might consider a Rust-based solution. It is not a language problem, but we - would like to reduce the memory consumption. - -## The proposal - -Agama's network service is responsible for holding the network configuration for the installer. It -should be agnostic from the used network service, although in the short-term it will support -NetworkManager only. Therefore, the current solution is influenced by NetworkManager itself. - -In a first version, the API is composed of the following objects: - -* Network devices. Each one is available as an object under `/org/opensuse/Agama/Network1/devices/*` - exposing the current status[^1]. -* Connections (or configurations). They are exposed as `/org/opensuse/Agama/Network1/connections/*`. - Depending on the type of connection, those objects implements different interfaces like - `org.opensuse.Agama.Network1.IPv4`, `org.opensuse.Agama.Network1.Wireless`, etc. - -This API could be expanded in the future to provide a list of access points, emit signals when the -configuration changes, provide more information about the network devices, etc. - -[^1]: By now it only exposes some basic data, as the current status is only needed by the web UI. diff --git a/doc/networking.md b/doc/networking.md new file mode 100644 index 0000000000..13dba06836 --- /dev/null +++ b/doc/networking.md @@ -0,0 +1,28 @@ +# Networking Support + +In general, we are focused on scenarios that are important for the installation process. Defining +a set up to be used *after the installation* is out of scope. + +Here is a preliminary list of the scenarios/features we would like to support. In general, we should +focus on scenarios that are important for the installation. + +* Specify a static configuration, useful where no DHCP is available or the configuration needs some + extra change: + - IPv4 / IPv6 (optionally in addition to the DHCP settings) + - DNS configuration (needed to reach the repositories) + - Routing configuration (needed to work on some specific networks) +* Use the DHCP provided configuration, allowing the user to adapt it if needed. +* Connect to a wireless network, adding support for the most common authentication mechanisms. +* Define a proxy to access the network. +* High availability scenarios. This features are critical when working with remote storage, network + redundancy and so on. NTP configuration very important here. We should support: + - Bonding + - Bridge + - VLAN +* s390 deployment: devices activation (port number and layer 2/3 configuration). +* VPN (?). If needed, which one? + +Other interesting use cases: + +* Provide multiple WiFi networks (in the unattended installation) and select one available during + installation. You could reuse the same profile and deploy on different places. From 6c78af9db515fc083925907c9347f696d704b8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 11:15:26 +0100 Subject: [PATCH 07/14] doc: drop the debugging.md document * The content is not relevant anymore. * We need a new document. --- doc/debugging.md | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 doc/debugging.md diff --git a/doc/debugging.md b/doc/debugging.md deleted file mode 100644 index 36a6686b13..0000000000 --- a/doc/debugging.md +++ /dev/null @@ -1,40 +0,0 @@ -# Debugging - -This document describes some tips for debugging problems in Agama. - -## The Web Frontend - -Cockpit by default does not log any debugging messages, but you can enable it -manually. After enabling the debug mode you can see all DBus traffic, executed -commands and more. - -To enable it open the developer tools in the browser (usually the `F12` or -`Ctrl+Shift+I` key shortcut), switch to the console and run this command: - -```js -window.sessionStorage.debugging = "all" -``` - -This enables logging for all Cockpit parts. If the log is too verbose you can -enable just some specific parts: - -`"channel"` - log the websocket traffic, includes most of the Cockpit traffic -`"dbus"` - log the DBus traffic when using the [Cockpit DBus API]( -https://cockpit-project.org/guide/latest/cockpit-dbus.html) -`"http"` - log HTTP traffic for the [Cockpit HTTP client]( -https://cockpit-project.org/guide/latest/cockpit-http.html) -`"spawn"` - log executed commands and results when using [Cockpit process -spawning](https://cockpit-project.org/guide/latest/cockpit-spawn) - -To turn off the debugging set it to the `undefined` value: - -```js -window.sessionStorage.debugging = undefined -``` - -The setting is stored in the session storage which means the value is kept -between page reloads and each browser tab uses a different instance. - -:warning: *Warning: Cockpit logs all data transferred between the browser -and the server including sensitive data like passwords, registration codes or -similar! Be careful when sharing the Cockpit logs!* :warning: From e006795986c776c9a6c8ef357915731b5659c6bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 11:24:08 +0100 Subject: [PATCH 08/14] auto: start agama-auto after agama-web-server --- autoinstallation/systemd/agama-auto.service | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/autoinstallation/systemd/agama-auto.service b/autoinstallation/systemd/agama-auto.service index e1a5b3f861..e4ffb93e05 100644 --- a/autoinstallation/systemd/agama-auto.service +++ b/autoinstallation/systemd/agama-auto.service @@ -5,8 +5,7 @@ After=dbus.socket # it needs to NetworkManager, so it has access to it After=NetworkManager.service # it needs agama, of course -After=agama.service -Requires=agama.service +After=agama-web-server.service [Service] ExecStart=/usr/bin/auto.sh From c30784e312d98af40b33600f9f5d67ba4b8eda60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 11:24:25 +0100 Subject: [PATCH 09/14] doc: update the startup document --- doc/startup.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/doc/startup.md b/doc/startup.md index ba485977fe..9917f48b9a 100644 --- a/doc/startup.md +++ b/doc/startup.md @@ -4,10 +4,10 @@ This document summarizes Agama's startup process. ## Overview -As described in the [README](../README.md#architecture), Agama is composed of a set of D-Bus -services, a web client and a command-line interface. The startup process aims to get those D-Bus -services up and running and make the web UI available. Additionally, the auto-installation procedure -could be started if required by the user. +As described in the [architecture docment](./architecture.md), Agama is composed of a web server, +a set of D-Bus services, a web client and a command-line interface. The startup process aims +to get those D-Bus services up and running and make the web server available. Additionally, the +auto-installation procedure could be started if required by the user. The startup process is handled by systemd and D-Bus. The only exception is starting the local browser in the Agama Live image. @@ -23,20 +23,22 @@ required. The definitions of those services are located in `/usr/share/dbus-1/ag although you can find the sources in the [repository](../service/share) (`org.opensuse.Agama*.service` files). +## Starting the web server + +[agama-web-server](../rust/share/agama-web-server.service) is responsible for starting up +Agama's web server using the `agama-web-server` command. + ## Auto-installation If the `agama.auto` option is specified in the kernel command-line, the [agama-auto.service](../service/share/systemd/agama-auto.service) comes into play. It runs after the -`agama.service` so the D-Bus daemon is ready and the services can be activated as needed. +`agama-web-server.service` so the web server and the D-Bus daemon are ready. ## Web UI When discussing the web UI, we can distinguish two sides: the server process and the web browser. -Regarding the server, Agama's web UI is implemented as a Cockpit module, so the only requirement is -that the `cockpit.socket` is enabled. Then, you can connect to the UI using the -`https://$SERVER_IP:9090/cockpit/@localhost/agama/index.html`. +Regarding the server, Agama's web UI is implemented as a React application which is served +by the web server. You can connect to the UI using the http://$SERVER_IP` address. When using Agama Live, a local web browser is automatically started. In the default image, it is -launched using an Icewm startup script[^1]. - -[^1]: Check the `root.tar` file from the [agama-live](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/agama-live) sources. +launched using an [IceWM startup script](../live/root/root/.icewm/startup) From 5c98ddc685589d632fd9f5aec9007eb294009cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 11:30:26 +0100 Subject: [PATCH 10/14] doc: drop outdated Cockpit-related config options --- doc/yaml_config.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/doc/yaml_config.md b/doc/yaml_config.md index c2a8e0de9a..70210e1395 100644 --- a/doc/yaml_config.md +++ b/doc/yaml_config.md @@ -74,25 +74,6 @@ Default policy. Only applicable for selinux lsm. List of supported distros that can be offered in installer. Archs key is used for products that is not available for all hardware architectures. -### web - -Cockpit's web server related settings. - -#### ssl - -Whether enable or disable TLS/SSL for remote connections. If it is not set, it does not modify -Cockpit configuration in that regard. - -#### ssl\_cert - -Location of the certificate to use for remote connections. The certificate is retrieved and copied -to `/etc/cockpit/ws-certs.d`. - -#### ssl\_key - -Location of the certificate key to use for remote connections. The key is retrieved and copied to -`/etc/cockpit/ws-certs.d`. This option is ignored unless the `ssl_cert` is set. - ## storage Options related to management of storage devices. It is map with the following keys: From c689d9caa91eeef7d73964effdc343f189b1402b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 12:13:05 +0100 Subject: [PATCH 11/14] doc: reduce the main README.md file --- README.md | 230 ++++-------------------------------- STATUS.md | 46 ++++++++ doc/avahi.md | 90 ++++++++++++++ doc/images/architecture.png | Bin 122623 -> 0 bytes doc/running.md | 38 ++++++ 5 files changed, 199 insertions(+), 205 deletions(-) create mode 100644 STATUS.md create mode 100644 doc/avahi.md delete mode 100644 doc/images/architecture.png create mode 100644 doc/running.md diff --git a/README.md b/README.md index 6fd9ba799c..91f13228f0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -**Checks** +# Agama: A Service-based Linux Installer [![CI - Rust](https://github.com/openSUSE/agama/actions/workflows/ci-rust.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-rust.yml) [![CI - Service](https://github.com/openSUSE/agama/actions/workflows/ci-service.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-service.yml) @@ -7,41 +7,8 @@ [![CI - Documentation Check](https://github.com/openSUSE/agama/actions/workflows/ci-doc-check.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-doc-check.yml) [![CI - Integration Tests](https://github.com/openSUSE/agama/actions/workflows/ci-integration-tests.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-integration-tests.yml) [![Coverage Status](https://coveralls.io/repos/github/openSUSE/agama/badge.svg?branch=master)](https://coveralls.io/github/openSUSE/agama?branch=master) -[![GitHub Pages](https://github.com/openSUSE/agama/actions/workflows/github-pages.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/github-pages.yml) - -**Translations** - -[![Weblate Update POT](https://github.com/openSUSE/agama/actions/workflows/weblate-update-pot.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/weblate-update-pot.yml) -[![Weblate Merge PO](https://github.com/openSUSE/agama/actions/workflows/weblate-merge-po.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/weblate-merge-po.yml) -[![Weblate Merge Product PO](https://github.com/openSUSE/agama/actions/workflows/weblate-merge-products-po.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/weblate-merge-products-po.yml) [![Translation Status](https://l10n.opensuse.org/widgets/agama/-/agama-web/svg-badge.svg)](https://l10n.opensuse.org/engage/agama/) -**[OBS systemsmanagement:Agama:Staging](https://build.opensuse.org/project/show/systemsmanagement:Agama:Staging)** - -[![Submit agama](https://github.com/openSUSE/agama/actions/workflows/obs-staging-rust.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-staging-rust.yml) -[![Submit cockpit-agama](https://github.com/openSUSE/agama/actions/workflows/obs-staging-web.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-staging-web.yml) -[![Submit rubygem-agama-yast](https://github.com/openSUSE/agama/actions/workflows/obs-staging-service.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-staging-service.yml) -[![Submit cockpit-agama-playwright](https://github.com/openSUSE/agama/actions/workflows/obs-staging-playwright.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-staging-playwright.yml) - -[![OBS Staging/agama](https://img.shields.io/obs/systemsmanagement:Agama:Staging/agama/openSUSE_Tumbleweed/x86_64?label=Package%20agama)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/agama) -[![OBS Staging/cockpit-agama](https://img.shields.io/obs/systemsmanagement:Agama:Staging/cockpit-agama/openSUSE_Tumbleweed/x86_64?label=Package%20cockpit-agama)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/cockpit-agama) -[![OBS Staging/rubygem-agama-yast](https://img.shields.io/obs/systemsmanagement:Agama:Staging/rubygem-agama-yast/openSUSE_Tumbleweed/x86_64?label=Package%20rubygem-agama-yast)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/rubygem-agama-yast) -[![OBS Staging/agama-products-opensuse](https://img.shields.io/obs/systemsmanagement%3AAgama%3AStaging/agama-products-opensuse/openSUSE_Tumbleweed/x86_64?label=Package%20agama-products-opensuse)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/agama-products-opensuse) -[![OBS Staging/cockpit-agama-playwright](https://img.shields.io/obs/systemsmanagement:Agama:Staging/cockpit-agama-playwright/openSUSE_Tumbleweed/x86_64?label=Package%20cockpit-agama-playwright)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/cockpit-agama-playwright) -[![OBS Staging/agama-live](https://img.shields.io/obs/systemsmanagement:Agama:Staging/agama-live:openSUSE/images/x86_64?label=Live%20ISO)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/agama-live) - -**[OBS systemsmanagement:Agama:Devel](https://build.opensuse.org/project/show/systemsmanagement:Agama:Devel)** - -![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/openSUSE/agama?label=Version&sort=semver) -[![Release](https://github.com/openSUSE/agama/actions/workflows/obs-release.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-release.yml) - -[![OBS Devel/agama](https://img.shields.io/obs/systemsmanagement:Agama:Devel/agama/openSUSE_Tumbleweed/x86_64?label=Package%20agama)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/agama) -[![OBS Devel/cockpit-agama](https://img.shields.io/obs/systemsmanagement:Agama:Devel/cockpit-agama/openSUSE_Tumbleweed/x86_64?label=Package%20cockpit-agama)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/cockpit-agama) -[![OBS Devel/rubygem-agama-yast](https://img.shields.io/obs/systemsmanagement:Agama:Devel/rubygem-agama-yast/openSUSE_Tumbleweed/x86_64?label=Package%20rubygem-agama-yast)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/rubygem-agama-yast) -[![OBS Devel/agama-live](https://img.shields.io/obs/systemsmanagement:Agama:Devel/agama-live:openSUSE/images/x86_64?label=Live%20ISO)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/agama-live) - -# Agama: A Service-based Linux Installer - Agama is a new Linux installer born in the core of the YaST team. It is designed to offer re-usability, integration with third party tools and the possibility of building advanced user interfaces over it. ||| @@ -67,17 +34,6 @@ Agama is a new Linux installer born in the core of the YaST team. It is designed -## Table of Content - -* [Why a New Installer](#why-a-new-installer) -* [Architecture](#architecture) -* [How to Run](#how-to-run) - * [Live ISO Image](#live-iso-image) - * [Avahi/mDNS](#avahimdns) - * [Manual Configuration](#manual-configuration) -* [How to Contribute](#how-to-contribute) -* [Development Notes](#development-notes) - ## Why a New Installer This new project follows two main motivations: to overcome some of the limitations of YaST and to serve as installer for new projects like SUSE ALP (Adaptable Linux Platform). @@ -89,173 +45,44 @@ YaST is a mature installer and control center for SUSE and openSUSE operating sy * Sharing logic with other tools like Salt or Ansible is very difficult. * Some in-house solutions like libyui makes more difficult to contribute to the project. -SUSE is working on its next generation operating system called ALP (Adaptable Linux Platform). ALP is designed to be a lean core system, moving most of the software and workloads to containers and virtual machines. For some cases, for example cloud and virtual machines, ALP based systems will be deployed with auto-installable images. But still there are quite some situations in which ALP must be installed in a more traditional way. A clear example consists on installing over bare metal where some system analysis is required beforehand. Agama is also intended to cover such use cases for ALP, offering a minimal but powerful installer able to support a wide range of scenarios (e.g., RAID, encryption, LVM, network storage, etc). - -## Architecture - -This project is designed as a service-client system, using a dedicated D-Bus server for process -communication. - -![Architecture](./doc/images/architecture.png) - -Agama consists on a set of D-Bus services, a web client and a command-line interface. The services use YaST-based libraries under the hood, reusing a lot logic already provided by YaST. Currently Agama comes with six separate services, although the list can increase in the future: - -* Agama service: it is the main service which manages and controls the installation process. -* Software service: configures the product and software to install. -* Users service: manages first user creation and configuration for root. -* Localization service: allows to configure the language and keyboard settings. -* Storage service: analyzes and prepares the storage devices in order to perform the installation. -* Questions service: helper service used for requesting information from clients. - -Agama offers a web interface and its UI process uses the [Cockpit's infrastructure](https://cockpit-project.org/) to communicate with the D-Bus services. - -## How to run - -There are two ways of running this project: a) by using a Agama live ISO image or b) by cloning and configuring the project. - -### Live ISO Image +## Running Agama The easiest way to give Agama a try is to grab a live ISO image and boot it in a virtual machine. This is also the recommended way if you only want to play and see it in action. If you want to have a closer look, then clone and configure the project as explained in the next section. -There are two flavors of live ISO images: - -* openSUSE: it can be used to install different *openSUSE* distributions, like *Tumbleweed* or *Leap*. -* ALP: it allows to install the development version of *SUSE ALP Dolomite*. - -You can download them from the [openSUSE Build +You can download the ISO from the [openSUSE Build Service](https://download.opensuse.org/repositories/systemsmanagement:/Agama:/Devel/images/iso/). -* Make sure to download the correct ISO file according to your system architecture (eg. - you would need to choose a file including `x86_64` if you use an Intel or AMD 64-bit processor) - and according to the system you want to install (openSUSE vs ALP). - -#### Avahi/mDNS - -The Live ISO is configured to use mDNS (sometimes called Avahi, Zeroconf, -Bonjour) for hostname resolution. The reason is that it might be quite difficult -to find out which URL should be used for connecting to a running Agama -installer. - -##### :warning: Security Note :warning: -*Do not use the `.local` hostnames in untrusted networks (like public WiFi -networks, shared networks), it is a security risk. An attacker can easily send -malicious responses for the `.local` hostname resolutions and point you to a -wrong Agama instance which could for example steal your root password!* +> [!NOTE] +> Make sure to download the correct ISO file according to your system architecture (eg. you would +> need to choose a file including `x86_64` if you use an Intel or AMD 64-bit processor). -##### Firewall Configuration +## Remote access -If you cannot connect to a server using the `.local` domain then maybe the -firewall is blocking the traffic. Then you need to enable the mDNS traffic using -these commands: +The Live ISO automatically starts a graphical interface (using the local browser). However, you +might want to access remotely to the installer. If you know the IP address of the system, you just +need to point your browser to `https://$IP`. -```shell -# enable the mDNS traffic in the current run -firewall-cmd --zone=public --add-service=mdns -# make the change permanent -firewall-cmd --permanent --zone=public --add-service=mdns -``` +For the case you do not know the address, or just for convenience, the Live ISO is configured to use +mDNS (sometimes called Avahi, Zeroconf, Bonjour) for hostname resolution. Therefore, connecting to `https://agama.local` should do the trick. -##### Using mDNS +>[!WARNING] +> Do not use the `.local` hostnames in untrusted networks (like public WiFi networks, shared +> networks), it is a security risk. An attacker can easily send malicious responses for the `.local` +> hostname resolutions and point you to a wrong Agama instance which could for example steal your +> root password! -The Live ISO by default uses the `agama.local` hostname. To connect to the -running instance simply type `https://agama.local` in your browser. In most -browsers the HTTPS is the default protocol so usually it is enough to just type -`agama.local`. +If you have troubles or you want to know more about this feature, check our [Avahi/mDNS] (./doc/avahi.md) documentation. -If you run multiple Agama instances, each one will have a different name. The server -appends a number to make it unique. So the second Agama instance gets the -`agama-2.local` hostname. +## Other Resources -If you are not sure whether there are multiple Agama instances running you scan -the network, see the [service advertising](#service-advertising) below. - -Alternatively you can set a different hostname for each instance manually. Use -the `hostname=` boot option to set a different hostname. For example set -`hostname=foo`, `hostname=bar` and then use `https://foo.local`, -`https://bar.local` URLs in the web browser to connect to the respective -instance. - -It is possible to change the hostname later if needed: - -```shell -# set the new hostname -hostnamectl hostname -# restart the avahi daemon server -systemctl restart avahi-daemon -``` - -The mDNS resolution also works for other services like ping or SSH. So you can -use commands like: - -```shell -ping agama.local -ssh root@agama.local -``` - -##### Fallback - -The mDNS approach is just an addition, one more possibility how to connect to -the machine. If it does not work for you then you can always use the old good -classic IP address approach. - -##### Service Advertising - -The Agama Live ISO also uses Avahi service advertising. With this you can easily -search for all running Agama instances in the local network: - -```shell -avahi-browse -t -r _agama._sub._https._tcp -``` - -The command will print the found servers and their hostnames and IP addresses. - -##### Notes - -- mDNS works only in the same local network, it does not work over internet - or separate network segments. -- mDNS might not be supported in all systems or it might be blocked by firewall. -- On mobile phones with Android OS mDNS is supported since Android version 12. - (but this might be vendor dependent...). - -### Manual Configuration - -You can run Agama from its sources by cloning and configuring the project: - -```console -$ git clone https://github.com/openSUSE/agama -$ cd agama -$ ./setup.sh -``` - -Then point your browser to http://localhost:9090/cockpit/@localhost/agama/index.html and that's all. - -The [setup.sh](./setup.sh) script installs the required dependencies -to build and run the project and it also configures the Agama services -and Cockpit. It uses `sudo` to install packages and files to system locations. -The script is well commented so we refer you to it instead of repeating its -steps here. - -Regarding the web user interface, alternatively you can run a development -server which works as a proxy for the cockpit server. See more details [in the -documentation]( web/README.md#using-a-development-server). - -To start or stop Agama D-Bus services at any time, use the `agama` systemd service: - -```console -sudo systemctl start agama -``` - -If something goes wrong, you can use `journalctl` to get Agama logs: - -```console -sudo journalctl -u agama -``` - -Another alternative is to run source checkout inside container so system is not -affected by doing testing run beside real actions really done by installer. -See more details [in the documentation](doc/testing_using_container.md). +* If you want to know how Agama works, you should read about [Agama's architecture](/doc/architecture.md) +* If you would like to [contribute](#how-to-contribute), you might be interested in: + * [Running Agama from sources](./doc/running.md). + * [Working with Agama's web server](./rust/WEB-SERVER.md). + * [Working with Agama's web UI](./web/README.md). +* You can check the overall status of the project through the [status page](/STATUS.md). ## How to Contribute @@ -263,10 +90,3 @@ If you want to contribute to Agama, then please open a pull request or report an get involved in [our discussions](https://github.com/openSUSE/agama/discussions). For more details, please read the [contributing](CONTRIBUTING.md) guidelines. - -## Development Notes - -* [Packaging](PACKAGING.md) -* [Working with the web UI](./web/README.md) -* [D-Bus service API](https://opensuse.github.io/agama/dbus/) (generated) -* [Web frontend documentation](https://opensuse.github.io/agama/jsdoc/) (generated) diff --git a/STATUS.md b/STATUS.md new file mode 100644 index 0000000000..deebaaf9d5 --- /dev/null +++ b/STATUS.md @@ -0,0 +1,46 @@ +# Agama Status + +## CI and checks + +[![CI - Rust](https://github.com/openSUSE/agama/actions/workflows/ci-rust.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-rust.yml) +[![CI - Service](https://github.com/openSUSE/agama/actions/workflows/ci-service.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-service.yml) +[![CI - Web](https://github.com/openSUSE/agama/actions/workflows/ci-web.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-web.yml) +[![CI - Rubocop](https://github.com/openSUSE/agama/actions/workflows/ci-rubocop.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-rubocop.yml) +[![CI - Documentation Check](https://github.com/openSUSE/agama/actions/workflows/ci-doc-check.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-doc-check.yml) +[![CI - Integration Tests](https://github.com/openSUSE/agama/actions/workflows/ci-integration-tests.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/ci-integration-tests.yml) +[![Coverage Status](https://coveralls.io/repos/github/openSUSE/agama/badge.svg?branch=master)](https://coveralls.io/github/openSUSE/agama?branch=master) +[![GitHub Pages](https://github.com/openSUSE/agama/actions/workflows/github-pages.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/github-pages.yml) + +## Translations + +[![Weblate Update POT](https://github.com/openSUSE/agama/actions/workflows/weblate-update-pot.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/weblate-update-pot.yml) +[![Weblate Merge PO](https://github.com/openSUSE/agama/actions/workflows/weblate-merge-po.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/weblate-merge-po.yml) +[![Weblate Merge Product PO](https://github.com/openSUSE/agama/actions/workflows/weblate-merge-products-po.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/weblate-merge-products-po.yml) +[![Translation Status](https://l10n.opensuse.org/widgets/agama/-/agama-web/svg-badge.svg)](https://l10n.opensuse.org/engage/agama/) + +## Packages + +### [OBS systemsmanagement:Agama:Staging](https://build.opensuse.org/project/show/systemsmanagement:Agama:Staging) + +[![Submit agama](https://github.com/openSUSE/agama/actions/workflows/obs-staging-rust.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-staging-rust.yml) +[![Submit cockpit-agama](https://github.com/openSUSE/agama/actions/workflows/obs-staging-web.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-staging-web.yml) +[![Submit rubygem-agama-yast](https://github.com/openSUSE/agama/actions/workflows/obs-staging-service.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-staging-service.yml) +[![Submit cockpit-agama-playwright](https://github.com/openSUSE/agama/actions/workflows/obs-staging-playwright.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-staging-playwright.yml) + +[![OBS Staging/agama](https://img.shields.io/obs/systemsmanagement:Agama:Staging/agama/openSUSE_Tumbleweed/x86_64?label=Package%20agama)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/agama) +[![OBS Staging/cockpit-agama](https://img.shields.io/obs/systemsmanagement:Agama:Staging/cockpit-agama/openSUSE_Tumbleweed/x86_64?label=Package%20cockpit-agama)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/cockpit-agama) +[![OBS Staging/rubygem-agama-yast](https://img.shields.io/obs/systemsmanagement:Agama:Staging/rubygem-agama-yast/openSUSE_Tumbleweed/x86_64?label=Package%20rubygem-agama-yast)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/rubygem-agama-yast) +[![OBS Staging/agama-products-opensuse](https://img.shields.io/obs/systemsmanagement%3AAgama%3AStaging/agama-products-opensuse/openSUSE_Tumbleweed/x86_64?label=Package%20agama-products-opensuse)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/agama-products-opensuse) +[![OBS Staging/cockpit-agama-playwright](https://img.shields.io/obs/systemsmanagement:Agama:Staging/cockpit-agama-playwright/openSUSE_Tumbleweed/x86_64?label=Package%20cockpit-agama-playwright)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/cockpit-agama-playwright) +[![OBS Staging/agama-live](https://img.shields.io/obs/systemsmanagement:Agama:Staging/agama-live:openSUSE/images/x86_64?label=Live%20ISO)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Staging/agama-live) + +### [OBS systemsmanagement:Agama:Devel](https://build.opensuse.org/project/show/systemsmanagement:Agama:Devel) + +![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/openSUSE/agama?label=Version&sort=semver) +[![Release](https://github.com/openSUSE/agama/actions/workflows/obs-release.yml/badge.svg)](https://github.com/openSUSE/agama/actions/workflows/obs-release.yml) + +[![OBS Devel/agama](https://img.shields.io/obs/systemsmanagement:Agama:Devel/agama/openSUSE_Tumbleweed/x86_64?label=Package%20agama)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/agama) +[![OBS Devel/cockpit-agama](https://img.shields.io/obs/systemsmanagement:Agama:Devel/cockpit-agama/openSUSE_Tumbleweed/x86_64?label=Package%20cockpit-agama)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/cockpit-agama) +[![OBS Devel/rubygem-agama-yast](https://img.shields.io/obs/systemsmanagement:Agama:Devel/rubygem-agama-yast/openSUSE_Tumbleweed/x86_64?label=Package%20rubygem-agama-yast)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/rubygem-agama-yast) +[![OBS Devel/agama-live](https://img.shields.io/obs/systemsmanagement:Agama:Devel/agama-live:openSUSE/images/x86_64?label=Live%20ISO)](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/agama-live) + diff --git a/doc/avahi.md b/doc/avahi.md new file mode 100644 index 0000000000..659fcaa995 --- /dev/null +++ b/doc/avahi.md @@ -0,0 +1,90 @@ +#### Avahi/mDNS + +Agama's Live ISO is configured to use mDNS (sometimes called Avahi, Zeroconf, Bonjour) for hostname +resolution. The reason is that it might be quite difficult to find out which URL should be used for +connecting to a running Agama installer. + +This document explains how this feature works and offers a few hints for fix potential problems. + +>[!WARNING] +> Do not use the `.local` hostnames in untrusted networks (like public WiFi networks, shared +> networks), it is a security risk. An attacker can easily send malicious responses for the `.local` +> hostname resolutions and point you to a wrong Agama instance which could for example steal your +> root password! + +##### Firewall Configuration + +If you cannot connect to a server using the `.local` domain then maybe the +firewall is blocking the traffic. Then you need to enable the mDNS traffic using +these commands: + +```shell +# enable the mDNS traffic in the current run +firewall-cmd --zone=public --add-service=mdns +# make the change permanent +firewall-cmd --permanent --zone=public --add-service=mdns +``` + +##### Using mDNS + +The Live ISO by default uses the `agama.local` hostname. To connect to the +running instance simply type `https://agama.local` in your browser. In most +browsers the HTTPS is the default protocol so usually it is enough to just type +`agama.local`. + +If you run multiple Agama instances, each one will have a different name. The server +appends a number to make it unique. So the second Agama instance gets the +`agama-2.local` hostname. + +If you are not sure whether there are multiple Agama instances running you scan +the network, see the [service advertising](#service-advertising) below. + +Alternatively you can set a different hostname for each instance manually. Use +the `hostname=` boot option to set a different hostname. For example set +`hostname=foo`, `hostname=bar` and then use `https://foo.local`, +`https://bar.local` URLs in the web browser to connect to the respective +instance. + +It is possible to change the hostname later if needed: + +```shell +# set the new hostname +hostnamectl hostname +# restart the avahi daemon server +systemctl restart avahi-daemon +``` + +The mDNS resolution also works for other services like ping or SSH. So you can +use commands like: + +```shell +ping agama.local +ssh root@agama.local +``` + +##### Fallback + +The mDNS approach is just an addition, one more possibility how to connect to +the machine. If it does not work for you then you can always use the old good +classic IP address approach. + +##### Service Advertising + +The Agama Live ISO also uses Avahi service advertising. With this you can easily +search for all running Agama instances in the local network: + +```shell +avahi-browse -t -r _agama._sub._https._tcp +``` + +The command will print the found servers and their hostnames and IP addresses. + +##### Notes + +- mDNS works only in the same local network, it does not work over internet + or separate network segments. +- mDNS might not be supported in all systems or it might be blocked by firewall. +- On mobile phones with Android OS mDNS is supported since Android version 12. + (but this might be vendor dependent...). + + diff --git a/doc/images/architecture.png b/doc/images/architecture.png deleted file mode 100644 index e3f33fe20c29b954cd2b632072c346a6aa82d72d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122623 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 Date: Thu, 9 May 2024 12:58:36 +0100 Subject: [PATCH 12/14] doc: small documentation fixes --- rust/WEB-SERVER.md | 2 +- web/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/WEB-SERVER.md b/rust/WEB-SERVER.md index 6637309e57..24c22a25d7 100644 --- a/rust/WEB-SERVER.md +++ b/rust/WEB-SERVER.md @@ -1,6 +1,6 @@ # Web server development notes -This document includes some notes about the usage and development of the new Agama web server. +This document includes some notes about the usage and development of Agama's web server. ## Installing Rust and related tools diff --git a/web/README.md b/web/README.md index 24f3db9d1c..691c045eca 100644 --- a/web/README.md +++ b/web/README.md @@ -21,7 +21,7 @@ use this command: The extra `--open` option automatically opens the server page in your default web browser. In this case the server will use the `https://localhost:8080` URL -and expects a running `agama-web-server` at `https://localhost:9090`. +and expects a running `agama-web-server` at `https://localhost:3000`. This can work also remotely, with a Agama instance running in a different machine (a virtual machine as well). In that case run From b5fefc9ce9baf55b95abc7783e5d60d306f45f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 13:46:49 +0100 Subject: [PATCH 13/14] doc: small fixes to the architecture document --- doc/architecture.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/architecture.md b/doc/architecture.md index d26ad1bc01..9abe228e7d 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -21,7 +21,7 @@ On the server side, Agama is composed by: component implements complex parts, like storage and software handling. Communication with the Agama web server happens over D-Bus. -* **Agama D-Bus server**: implements a minimal API to allow **Agama YaST server** to talk to the web +* **Agama D-Bus service**: implements a minimal API to allow **Agama YaST server** to talk to the web server. It is expected to be replaced by direct communication in the future. On the client side, these are the main components: @@ -29,7 +29,7 @@ On the client side, these are the main components: * **Web user interface (old `cockpit-agama`)**: Agama's graphical user interface. The **Agama web server** makes this React application available to browsers. -* **Command Line Interface (`agama-cli`)**: it allows interaction with Agama and drives the +* **Command-Line Interface (`agama-cli`)**: it allows interaction with Agama and drives the auto-installation process. * **Auto-installation (`autoinstallation`)**: it is composed of a Systemd service (`agama-auto`) and From 7872bf1261de689756891b688c6b9a6ce109f640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 9 May 2024 13:47:21 +0100 Subject: [PATCH 14/14] doc: update (and reduce) the rust/README.md file --- rust/README.md | 118 +++++-------------------------------------------- 1 file changed, 12 insertions(+), 106 deletions(-) diff --git a/rust/README.md b/rust/README.md index fd585a9296..90fc314c3d 100644 --- a/rust/README.md +++ b/rust/README.md @@ -1,9 +1,10 @@ -# Agama Command Line and D-Bus Interface +# Agama Server -This project aims to build a command-line interface for -[Agama](https://github.com/yast/agama), a service-based Linux installer featuring a nice -web interface. The second aim is D-Bus service that does not depend heavily on YaST to -reduce memory consumption and also provide better performance. +According to [Agama's architecture](../doc/architecture.md) this project implements the following components: + +* The *Agama server*, excluding *Agama YaST* which lives in the [service](../service) directory. +* The *Agama D-Bus service*. +* The *Command Line Interface*. ## Code organization @@ -13,109 +14,14 @@ three packages: * [agama-lib](./agama-lib): code that can be reused to access the [Agama D-Bus API](https://github.com/yast/agama/blob/master/doc/dbus_api.md) and a model for the configuration settings. -* [agama-cli](./agama-cli): code specific to the command line interface. -* [agama-derive](./agama-derive): includes a [procedural +* [agama-cli](./agama-cli): code specific to the command-line interface. +* [agama-settings](./agama-settingS) and [agama-derive](./agama-derive): includes a [procedural macro](https://doc.rust-lang.org/reference/procedural-macros.html) to reduce the boilerplate code. * [agama-locale-data](./agama-locale-data): specific library to provide data for localization D-Bus API -* [agama-dbus-server](./agama-dbus-server): provides D-Bus API for services implemented in rust - -## Status - -Agama CLI is still a work in progress, although it is already capable of doing a few things: - -* Querying and setting the configuration for the users, storage and software services. -* Handling the auto-installation profiles. -* Triggering the *probing* and the *installation* processes. - -Agama D-Bus API is also a work in progress, but it is already used by Agama. - -## Installation - -You can grab the [RPM package](https://build.opensuse.org/package/show/systemsmanagement:Agama:Devel/agama-cli) from -the [systemsmanagement:Agama:Devel](https://build.opensuse.org/project/show/systemsmanagement:Agama:Devel) project. - -If you prefer, you can install it from sources with [Cargo](https://doc.rust-lang.org/cargo/): - -``` -git clone https://github.com/openSUSE/agama -cd rust -cargo install --path . -``` - -## Running - -For D-Bus API just run as root agama-dbus-server binary and it will properly attach to D-Bus. - -For CLI take into account that you need to run `agama-cli` as root when you want to query or change -the Agama configuration. Assuming that the Agama D-Bus service is running, the next command -prints the current settings using JSON (hint: you can use `jq` to make result look better): - -``` -$ sudo agama --format json config show -{"user":{"fullName":"","userName":"","password":"","autologin":false},"software":{"product":""}} -``` - -To set one or multiple parameters, just use the `config set` command: - -``` -$ sudo agama config set software.product=Tumbleweed user.fullName="Jane Doe" user.userName="jane.doe" user.password="12345" user.autologin=true -``` - -The following operation can take some time. Please, make sure to read the *Caveats* section for more -information. - -``` -$ sudo agama config show -{"user":{"fullName":"Jane Doe","userName":"jane.doe","password":"","autologin":true},"software":{"product":"Tumbleweed"}} -``` - -If, at some point you want to force a new probing, you can ask Agama to repeat the process again: - -``` -$ sudo agama probe -``` - -It is possible to handle auto-installation profiles too: - -``` -$ agama profile download http://192.168.122.1/profile.jsonnet -$ agama profile evaluate profile.jsonnet > profile.json -$ agama profile validate profile.json -``` - -Now that you have a ready to use profile, you can load it into Agama: - -``` -$ sudo agama config load profile.json -``` - -## Building and running - -You can build and run the project using the `cargo` command: - -``` -cargo build -sudo ./target/debug/agama --help -``` - -## A Testing Backend - -The previous section assumes that the Agama D-Bus services are running -on the same machine. - -For an alternative setup using a containerized backend, see -*[How to set up a backend for testing this -frontend](./agama-cli/doc/backend-for-testing.md)*. - -## Testing OBS Build - -To test if cargo packages build in OBS push your changes to remote branch. Then do osc checkout of Agama:Staging. -Modify `_service` file and point it to your branch in ``. Run `osc service manualrun obs_scm` and -then try `osc build` to build it. If it failed push fixes to your branch and repeat service and build step. - -## Caveats +* [agama-server](./agama-server): implements the HTTP/JSON (and WebSocket) API. Additionally, it + offers a minimal D-Bus API for internal communication. -* If no product is selected, the `probe` command fails. +## Other resources -[c_a_bug]: https://github.com/openSUSE/obs-service-cargo_audit/pull/6 +* [Web server development notes](/rust/WEB-SERVER.md).