From 2eae26db69bbdc3fffc315c2783e914d0af885c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Mon, 9 Mar 2026 13:24:43 +0100 Subject: [PATCH 01/15] Better env test --- rust/zypp-agama/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/zypp-agama/src/lib.rs b/rust/zypp-agama/src/lib.rs index b4416a6aba..509a040d65 100644 --- a/rust/zypp-agama/src/lib.rs +++ b/rust/zypp-agama/src/lib.rs @@ -597,8 +597,7 @@ impl Zypp { // save the solver testcase if the solver run failed or if saving is forced via boot // parameter, skip when "ZYPP_FULLLOG=1", in that case libzypp creates the solver // testcase automatically in the /var/log/YaST2/autoTestcase/ directory - if (!r_res || save_testcase) && std::env::var("ZYPP_FULLLOG").unwrap_or_default() != "1" - { + if (!r_res || save_testcase) && !std::env::var("ZYPP_FULLLOG").is_ok_and(|v| v == "1") { self.create_solver_testcase(); } else { // delete the solver testcase directory, it contains the previous error which is From 161a6c918188e6ca7742eea5622759eaf936e58e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Mon, 9 Mar 2026 16:20:27 +0100 Subject: [PATCH 02/15] Handle the "inst.listen_on=" boot option --- rust/install.sh | 1 + rust/package/agama.spec | 1 + rust/share/agama-web-server.service | 2 +- rust/share/agama-web-server.sh | 65 +++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100755 rust/share/agama-web-server.sh diff --git a/rust/install.sh b/rust/install.sh index b59b975ce7..9df916d5f9 100755 --- a/rust/install.sh +++ b/rust/install.sh @@ -38,6 +38,7 @@ install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/target/${RUST_TARGET}/agama" install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/target/${RUST_TARGET}/agama-autoinstall" install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/target/${RUST_TARGET}/agama-proxy-setup" install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/target/${RUST_TARGET}/agama-web-server" +install -D -t "${DESTDIR}${bindir}" "${SRCDIR}/share/agama-web-server.sh" install6 -D -p "${SRCDIR}"/share/agama.pam "${DESTDIR}${pamvendordir}"/agama diff --git a/rust/package/agama.spec b/rust/package/agama.spec index 0d284c1484..8de4f91cf5 100644 --- a/rust/package/agama.spec +++ b/rust/package/agama.spec @@ -245,6 +245,7 @@ echo $PATH %doc README.md %license LICENSE %{_bindir}/agama-web-server +%{_bindir}/agama-web-server.sh %{_bindir}/agama-proxy-setup %{_pam_vendordir}/agama %{_unitdir}/agama-web-server.service diff --git a/rust/share/agama-web-server.service b/rust/share/agama-web-server.service index b17151987c..3f96c86326 100644 --- a/rust/share/agama-web-server.service +++ b/rust/share/agama-web-server.service @@ -9,7 +9,7 @@ BindsTo=agama.service EnvironmentFile=-/run/agama/environment.conf Environment="AGAMA_LOG=debug,zbus=info" Type=notify -ExecStart=/usr/bin/agama-web-server serve --address :::80 --address2 :::443 +ExecStart=/usr/bin/agama-web-server.sh PIDFile=/run/agama/web.pid User=root TimeoutStopSec=5 diff --git a/rust/share/agama-web-server.sh b/rust/share/agama-web-server.sh new file mode 100755 index 0000000000..5f812164aa --- /dev/null +++ b/rust/share/agama-web-server.sh @@ -0,0 +1,65 @@ +#!/usr/bin/bash +# +# Copyright (c) [2026] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +# This script is a wrapper for the Agama web server + +# the default options: listen on all interfaces for both HTTP and HTTPS ports +OPTIONS=(--address :::80 --address2 :::443) + +# check if the "inst.listen_on=" boot option was used +if grep -q "\binst.listen_on=" /run/agama/cmdline.d/agama.conf; then + LISTEN_ON=$(grep "\binst.listen_on=" /run/agama/cmdline.d/agama.conf | sed 's/.*\binst.listen_on=\([^[:space:]]\+\)/\1/') + + if [ "$LISTEN_ON" = "localhost" ]; then + echo "<5>Disabling remote access to the Agama web server" + # listen only on the local loopback interface, HTTP port only + OPTIONS=(--address ::1:80) + elif [ "$LISTEN_ON" = "any" ]; then + echo "<5>Listening on all network interfaces" + else + # check if the value an IP address (IPv6 or IPv4) + if echo "$LISTEN_ON" | grep -qE '^[0-9a-fA-F:.]+$|^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then + echo "<5>Listening on IP address ${LISTEN_ON}" + # listen on local loopback (HTTP) + specified IP (HTTPS) + OPTIONS=(--address ::1:80 --address2 "${LISTEN_ON}:443") + else + if ip addr show dev "${LISTEN_ON}" >/dev/null 2>&1; then + # find the IP address for the specified interface + IP_ADDR=$(ip -o -4 addr show dev "${LISTEN_ON}" | awk '{print $4}' | cut -d/ -f1 | head -n1) + + if [ -n "${IP_ADDR}" ]; then + echo "<5>Listening on interface ${LISTEN_ON} with IP address ${IP_ADDR}" + # listen on local loopback (HTTP) + specified interface (HTTPS) + OPTIONS=(--address ::1:80 --address2 "${IP_ADDR}:443") + else + echo "<3>IP address for interface ${LISTEN_ON} not found, using defaults" + fi + else + echo "<3>Interface ${LISTEN_ON} not found, using defaults" + fi + fi + fi +else + echo "<5>Listening on all network interfaces" +fi + +exec /usr/bin/agama-web-server serve "${OPTIONS[@]}" From 7f8fe4cb448da5aae2b9c1e3f1923abc7f7d671d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 10 Mar 2026 16:03:41 +0100 Subject: [PATCH 03/15] Allow multiple `--address` option --- rust/agama-server/src/agama-web-server.rs | 15 ++-------- rust/share/agama-web-server.sh | 35 ++++++++++++++--------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/rust/agama-server/src/agama-web-server.rs b/rust/agama-server/src/agama-web-server.rs index fd149ba0eb..c26617225f 100644 --- a/rust/agama-server/src/agama-web-server.rs +++ b/rust/agama-server/src/agama-web-server.rs @@ -81,11 +81,7 @@ struct ServeArgs { // connections unless manually disabled in /proc/sys/net/ipv6/bindv6only. /// Primary port to listen on #[arg(long, default_value = ":::80")] - address: String, - - /// Optional secondary address to listen on - #[arg(long, default_value = None)] - address2: Option, + address: Vec, #[arg(long, default_value = "/etc/agama.d/ssl/key.pem")] key: Option, @@ -341,13 +337,8 @@ async fn serve_command(args: ServeArgs) -> anyhow::Result<()> { return Err(anyhow::anyhow!("SSL initialization failed")); }; - let mut addresses = vec![args.address]; - - if let Some(a) = args.address2 { - addresses.push(a) - } - - let servers: Vec<_> = addresses + let servers: Vec<_> = args + .address .iter() .map(|a| { tokio::spawn(start_server( diff --git a/rust/share/agama-web-server.sh b/rust/share/agama-web-server.sh index 5f812164aa..0b359b25c4 100755 --- a/rust/share/agama-web-server.sh +++ b/rust/share/agama-web-server.sh @@ -23,7 +23,9 @@ # This script is a wrapper for the Agama web server # the default options: listen on all interfaces for both HTTP and HTTPS ports -OPTIONS=(--address :::80 --address2 :::443) +OPTIONS=(--address :::80 --address :::443) +# option for localhost access only +LOCAL_OPTIONS=(--address ::1:80 --address ::1:443) # check if the "inst.listen_on=" boot option was used if grep -q "\binst.listen_on=" /run/agama/cmdline.d/agama.conf; then @@ -31,30 +33,34 @@ if grep -q "\binst.listen_on=" /run/agama/cmdline.d/agama.conf; then if [ "$LISTEN_ON" = "localhost" ]; then echo "<5>Disabling remote access to the Agama web server" - # listen only on the local loopback interface, HTTP port only - OPTIONS=(--address ::1:80) - elif [ "$LISTEN_ON" = "any" ]; then + # listen only on the local loopback interface (HTTP + HTTPS) + OPTIONS=("${LOCAL_OPTIONS[@]}") + elif [ "$LISTEN_ON" = "all" ]; then echo "<5>Listening on all network interfaces" else # check if the value an IP address (IPv6 or IPv4) if echo "$LISTEN_ON" | grep -qE '^[0-9a-fA-F:.]+$|^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then + # run on localhost + OPTIONS=("${LOCAL_OPTIONS[@]}") echo "<5>Listening on IP address ${LISTEN_ON}" - # listen on local loopback (HTTP) + specified IP (HTTPS) - OPTIONS=(--address ::1:80 --address2 "${LISTEN_ON}:443") + # listen on local loopback (HTTP) + specified IP (HTTP + HTTPS) + OPTIONS=+(--address "${LISTEN_ON}:80" --address "${LISTEN_ON}:443") else + # run on localhost + OPTIONS=("${LOCAL_OPTIONS[@]}") if ip addr show dev "${LISTEN_ON}" >/dev/null 2>&1; then # find the IP address for the specified interface - IP_ADDR=$(ip -o -4 addr show dev "${LISTEN_ON}" | awk '{print $4}' | cut -d/ -f1 | head -n1) - - if [ -n "${IP_ADDR}" ]; then - echo "<5>Listening on interface ${LISTEN_ON} with IP address ${IP_ADDR}" - # listen on local loopback (HTTP) + specified interface (HTTPS) - OPTIONS=(--address ::1:80 --address2 "${IP_ADDR}:443") + IP_ADDRS=$(ip -o addr show dev "${LISTEN_ON}" | awk '{print $4}' | cut -d/ -f1) + if [ -n "${IP_ADDRS}" ]; then + for IP in $IP_ADDRS; do + echo "<5>Listening on interface ${LISTEN_ON} with IP address ${IP}" + OPTIONS+=(--address "${IP}:80" --address "${IP}:443") + done else - echo "<3>IP address for interface ${LISTEN_ON} not found, using defaults" + echo "<3>IP address for interface ${LISTEN_ON} not found, enabling local access only" fi else - echo "<3>Interface ${LISTEN_ON} not found, using defaults" + echo "<3>Network Interface ${LISTEN_ON} not found, enabling local access only" fi fi fi @@ -62,4 +68,5 @@ else echo "<5>Listening on all network interfaces" fi +echo "<5>Starting Agama web server with options: ${OPTIONS[*]}" exec /usr/bin/agama-web-server serve "${OPTIONS[@]}" From 3b5f9af5eb4ef9cf963fc2f510f9f2fdb68cce1d Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Fri, 30 Jan 2026 18:39:04 +0100 Subject: [PATCH 04/15] agama-web-server: keep working even if IPv6 is not available --- rust/agama-server/src/agama-web-server.rs | 28 +++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/rust/agama-server/src/agama-web-server.rs b/rust/agama-server/src/agama-web-server.rs index c26617225f..26d819b8ff 100644 --- a/rust/agama-server/src/agama-web-server.rs +++ b/rust/agama-server/src/agama-web-server.rs @@ -269,19 +269,29 @@ async fn handle_http_stream( } } +async fn find_listener(addresses: String) -> Option { + let addresses = addresses.split(',').collect::>(); + for addr in addresses { + // see https://github.com/tokio-rs/axum/blob/main/examples/low-level-openssl/src/main.rs + // how to use axum with openSSL + match tokio::net::TcpListener::bind(&addr).await { + Ok(listener) => { + return Some(listener); + } + Err(error) => { + eprintln!("Error: could not listen on {}: {}", &addr, error); + } + } + } + None +} + /// Starts the web server async fn start_server(address: String, service: Router, ssl_acceptor: SslAcceptor) { tracing::info!("Starting Agama web server at {}", address); - // see https://github.com/tokio-rs/axum/blob/main/examples/low-level-openssl/src/main.rs - // how to use axum with openSSL - let listener = tokio::net::TcpListener::bind(&address) - .await - .unwrap_or_else(|error| { - let msg = format!("Error: could not listen on {}: {}", &address, error); - tracing::error!(msg); - panic!("{}", msg) - }); + let opt_listener = find_listener(address).await; + let listener = opt_listener.expect("None of the alternative addresses worked"); pin_mut!(listener); From 5898811e5fa63ee0e9703bf9162d1b1669125ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 10 Mar 2026 16:42:08 +0100 Subject: [PATCH 05/15] Add IPv4 fallbacks --- rust/share/agama-web-server.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/share/agama-web-server.sh b/rust/share/agama-web-server.sh index 0b359b25c4..da0395372e 100755 --- a/rust/share/agama-web-server.sh +++ b/rust/share/agama-web-server.sh @@ -23,9 +23,9 @@ # This script is a wrapper for the Agama web server # the default options: listen on all interfaces for both HTTP and HTTPS ports -OPTIONS=(--address :::80 --address :::443) +OPTIONS=(--address :::80,0.0.0.0:80 --address :::443,0.0.0.0:443) # option for localhost access only -LOCAL_OPTIONS=(--address ::1:80 --address ::1:443) +LOCAL_OPTIONS=(--address ::1:80,127.0.0.1:80 --address ::1:443,127.0.0.1:443) # check if the "inst.listen_on=" boot option was used if grep -q "\binst.listen_on=" /run/agama/cmdline.d/agama.conf; then From ec92a10e3c2039e4b59889c92163d2385926de32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 10 Mar 2026 17:16:18 +0100 Subject: [PATCH 06/15] Fixes, docu update --- rust/WEB-SERVER.md | 4 ++-- rust/share/agama-web-server.sh | 6 +++--- web/README.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/WEB-SERVER.md b/rust/WEB-SERVER.md index e3a989d76c..e11a1b3c00 100644 --- a/rust/WEB-SERVER.md +++ b/rust/WEB-SERVER.md @@ -68,8 +68,8 @@ Some more examples: - IPv4, only specific interface: `--address 192.168.1.2:5678` (use the IP address of that interface) -The server can optionally listen on a secondary address, use the `--address2` -option for that. +The server can listen on several address, you can use the `--address` option +multiple times. ## Trying the server diff --git a/rust/share/agama-web-server.sh b/rust/share/agama-web-server.sh index da0395372e..e1310c22bc 100755 --- a/rust/share/agama-web-server.sh +++ b/rust/share/agama-web-server.sh @@ -23,9 +23,9 @@ # This script is a wrapper for the Agama web server # the default options: listen on all interfaces for both HTTP and HTTPS ports -OPTIONS=(--address :::80,0.0.0.0:80 --address :::443,0.0.0.0:443) +OPTIONS=(--address ":::80,0.0.0.0:80" --address ":::443,0.0.0.0:443") # option for localhost access only -LOCAL_OPTIONS=(--address ::1:80,127.0.0.1:80 --address ::1:443,127.0.0.1:443) +LOCAL_OPTIONS=(--address "::1:80,127.0.0.1:80" --address "::1:443,127.0.0.1:443") # check if the "inst.listen_on=" boot option was used if grep -q "\binst.listen_on=" /run/agama/cmdline.d/agama.conf; then @@ -44,7 +44,7 @@ if grep -q "\binst.listen_on=" /run/agama/cmdline.d/agama.conf; then OPTIONS=("${LOCAL_OPTIONS[@]}") echo "<5>Listening on IP address ${LISTEN_ON}" # listen on local loopback (HTTP) + specified IP (HTTP + HTTPS) - OPTIONS=+(--address "${LISTEN_ON}:80" --address "${LISTEN_ON}:443") + OPTIONS+=(--address "${LISTEN_ON}:80" --address "${LISTEN_ON}:443") else # run on localhost OPTIONS=("${LOCAL_OPTIONS[@]}") diff --git a/web/README.md b/web/README.md index aeb005e3ab..91d327f532 100644 --- a/web/README.md +++ b/web/README.md @@ -41,7 +41,7 @@ Example of running from different machine: # backend machine # using ip of machine instead of localhost is important to be network accessible # second address is needed for SSL which is mandatory for remote access - agama-web-server serve --address :::80 --address2 :::443 + agama-web-server serve --address :::80 --address :::443 # frontend machine # ESLINT=0 is useful to ignore linter problems during development From fb58f5c3b53645d3f1ac839d74454a86ec455ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 10 Mar 2026 18:52:56 +0100 Subject: [PATCH 07/15] Handle IPv6 link local addresses --- rust/agama-server/src/agama-web-server.rs | 7 +++---- rust/share/agama-web-server.sh | 10 +++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/rust/agama-server/src/agama-web-server.rs b/rust/agama-server/src/agama-web-server.rs index 26d819b8ff..160dc01194 100644 --- a/rust/agama-server/src/agama-web-server.rs +++ b/rust/agama-server/src/agama-web-server.rs @@ -272,6 +272,7 @@ async fn handle_http_stream( async fn find_listener(addresses: String) -> Option { let addresses = addresses.split(',').collect::>(); for addr in addresses { + tracing::info!("Starting Agama web server at {}", addr); // see https://github.com/tokio-rs/axum/blob/main/examples/low-level-openssl/src/main.rs // how to use axum with openSSL match tokio::net::TcpListener::bind(&addr).await { @@ -279,7 +280,7 @@ async fn find_listener(addresses: String) -> Option { return Some(listener); } Err(error) => { - eprintln!("Error: could not listen on {}: {}", &addr, error); + tracing::warn!("Error: could not listen on {}: {}", &addr, error); } } } @@ -288,8 +289,6 @@ async fn find_listener(addresses: String) -> Option { /// Starts the web server async fn start_server(address: String, service: Router, ssl_acceptor: SslAcceptor) { - tracing::info!("Starting Agama web server at {}", address); - let opt_listener = find_listener(address).await; let listener = opt_listener.expect("None of the alternative addresses worked"); @@ -306,7 +305,7 @@ async fn start_server(address: String, service: Router, ssl_acceptor: SslAccepto let (tcp_stream, addr) = listener .accept() .await - .expect("Failed to open port for listening"); + .expect("Failed to accept connection"); tokio::spawn(async move { if is_ssl_stream(&tcp_stream).await { diff --git a/rust/share/agama-web-server.sh b/rust/share/agama-web-server.sh index e1310c22bc..2a83e3c2be 100755 --- a/rust/share/agama-web-server.sh +++ b/rust/share/agama-web-server.sh @@ -38,14 +38,14 @@ if grep -q "\binst.listen_on=" /run/agama/cmdline.d/agama.conf; then elif [ "$LISTEN_ON" = "all" ]; then echo "<5>Listening on all network interfaces" else - # check if the value an IP address (IPv6 or IPv4) - if echo "$LISTEN_ON" | grep -qE '^[0-9a-fA-F:.]+$|^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then + # check if the value is an IP address (IPv6, IPv6 link local or IPv4) + if echo "$LISTEN_ON" | grep -qE '^[0-9a-fA-F:]+$|^[fF][eE]80|^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then # run on localhost OPTIONS=("${LOCAL_OPTIONS[@]}") echo "<5>Listening on IP address ${LISTEN_ON}" - # listen on local loopback (HTTP) + specified IP (HTTP + HTTPS) OPTIONS+=(--address "${LISTEN_ON}:80" --address "${LISTEN_ON}:443") else + # otherwise consider it as an interface name # run on localhost OPTIONS=("${LOCAL_OPTIONS[@]}") if ip addr show dev "${LISTEN_ON}" >/dev/null 2>&1; then @@ -53,6 +53,10 @@ if grep -q "\binst.listen_on=" /run/agama/cmdline.d/agama.conf; then IP_ADDRS=$(ip -o addr show dev "${LISTEN_ON}" | awk '{print $4}' | cut -d/ -f1) if [ -n "${IP_ADDRS}" ]; then for IP in $IP_ADDRS; do + # append the %device for link local IPv6 addresses + if [[ "$IP" == fe80* ]]; then + IP="${IP}%${LISTEN_ON}" + fi echo "<5>Listening on interface ${LISTEN_ON} with IP address ${IP}" OPTIONS+=(--address "${IP}:80" --address "${IP}:443") done From 5375f4530d917bae6de2ec2fcef004b07668fc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 10 Mar 2026 21:56:09 +0100 Subject: [PATCH 08/15] Allow multiple interfaces --- rust/share/agama-web-server.sh | 82 +++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/rust/share/agama-web-server.sh b/rust/share/agama-web-server.sh index 2a83e3c2be..afa41c9784 100755 --- a/rust/share/agama-web-server.sh +++ b/rust/share/agama-web-server.sh @@ -20,56 +20,68 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. -# This script is a wrapper for the Agama web server +# This script is a wrapper for the Agama web server, it evaluates to which +# addresses the server should listen to. -# the default options: listen on all interfaces for both HTTP and HTTPS ports -OPTIONS=(--address ":::80,0.0.0.0:80" --address ":::443,0.0.0.0:443") -# option for localhost access only +# the default options: listen on all interfaces for both HTTP and HTTPS ports, +# the IPv4 addresses are fallbacks when IPv6 is disabled with the +# "ipv6.disable=1" kernel boot option +DEFAULT_OPTIONS=(--address ":::80,0.0.0.0:80" --address ":::443,0.0.0.0:443") +# options for localhost access only LOCAL_OPTIONS=(--address "::1:80,127.0.0.1:80" --address "::1:443,127.0.0.1:443") # check if the "inst.listen_on=" boot option was used -if grep -q "\binst.listen_on=" /run/agama/cmdline.d/agama.conf; then - LISTEN_ON=$(grep "\binst.listen_on=" /run/agama/cmdline.d/agama.conf | sed 's/.*\binst.listen_on=\([^[:space:]]\+\)/\1/') +if grep -q "\binst.listen_on=.\+" /run/agama/cmdline.d/agama.conf; then + LISTEN_ON=$(grep "\binst.listen_on=.\+" /run/agama/cmdline.d/agama.conf | sed 's/.*\binst.listen_on=\([^[:space:]]\+\)/\1/') if [ "$LISTEN_ON" = "localhost" ]; then - echo "<5>Disabling remote access to the Agama web server" - # listen only on the local loopback interface (HTTP + HTTPS) OPTIONS=("${LOCAL_OPTIONS[@]}") elif [ "$LISTEN_ON" = "all" ]; then - echo "<5>Listening on all network interfaces" + OPTIONS=("${DEFAULT_OPTIONS[@]}") else - # check if the value is an IP address (IPv6, IPv6 link local or IPv4) - if echo "$LISTEN_ON" | grep -qE '^[0-9a-fA-F:]+$|^[fF][eE]80|^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then - # run on localhost - OPTIONS=("${LOCAL_OPTIONS[@]}") - echo "<5>Listening on IP address ${LISTEN_ON}" - OPTIONS+=(--address "${LISTEN_ON}:80" --address "${LISTEN_ON}:443") - else - # otherwise consider it as an interface name - # run on localhost - OPTIONS=("${LOCAL_OPTIONS[@]}") - if ip addr show dev "${LISTEN_ON}" >/dev/null 2>&1; then - # find the IP address for the specified interface - IP_ADDRS=$(ip -o addr show dev "${LISTEN_ON}" | awk '{print $4}' | cut -d/ -f1) - if [ -n "${IP_ADDRS}" ]; then - for IP in $IP_ADDRS; do - # append the %device for link local IPv6 addresses - if [[ "$IP" == fe80* ]]; then - IP="${IP}%${LISTEN_ON}" - fi - echo "<5>Listening on interface ${LISTEN_ON} with IP address ${IP}" - OPTIONS+=(--address "${IP}:80" --address "${IP}:443") - done + # always run on the localhost + OPTIONS=("${LOCAL_OPTIONS[@]}") + + # the string can contain multiple addresses separated by comma + # replace commas with spaces to iterate over them + ADDRESSES=${LISTEN_ON//,/ } + ADDRESSES=("${ADDRESSES[@]}") + for ADDRESS in "${ADDRESSES[@]}"; do + # check if the value is an IP address (IPv6, IPv6 link local or IPv4) + if echo "$ADDRESS" | grep -qE '^[0-9a-fA-F:]+$|^[fF][eE]80|^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then + echo "<5>Listening on IP address ${ADDRESS}" + OPTIONS+=(--address "${ADDRESS}:80" --address "${ADDRESS}:443") + else + # otherwise assume it is as an interface name + if ip addr show dev "${ADDRESS}" >/dev/null 2>&1; then + # find the IP address for the specified interface + IP_ADDRS=$(ip -o addr show dev "${ADDRESS}" | awk '{print $4}' | cut -d/ -f1) + if [ -n "${IP_ADDRS}" ]; then + for IP in $IP_ADDRS; do + # append the %device for link local IPv6 addresses + if [[ "$IP" == fe80* ]]; then + IP="${IP}%${ADDRESS}" + fi + echo "<5>Listening on interface ${ADDRESS} with IP address ${IP}" + OPTIONS+=(--address "${IP}:80" --address "${IP}:443") + done + else + echo "<3>IP address for interface ${ADDRESS} not found" + fi else - echo "<3>IP address for interface ${LISTEN_ON} not found, enabling local access only" + echo "<3>Network Interface ${ADDRESS} not found" fi - else - echo "<3>Network Interface ${LISTEN_ON} not found, enabling local access only" fi - fi + done fi else + OPTIONS=("${DEFAULT_OPTIONS[@]}") +fi + +if [ "${OPTIONS[*]}" = "${DEFAULT_OPTIONS[*]}" ]; then echo "<5>Listening on all network interfaces" +elif [ "${OPTIONS[*]}" = "${LOCAL_OPTIONS[*]}" ]; then + echo "<5>Disabling remote access to the Agama web server" fi echo "<5>Starting Agama web server with options: ${OPTIONS[*]}" From 73678fc69329a5c1e7f2221b13f5f58c4d628dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Wed, 11 Mar 2026 08:26:53 +0100 Subject: [PATCH 09/15] Fixed iteration --- rust/share/agama-web-server.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rust/share/agama-web-server.sh b/rust/share/agama-web-server.sh index afa41c9784..e5990436b8 100755 --- a/rust/share/agama-web-server.sh +++ b/rust/share/agama-web-server.sh @@ -42,11 +42,10 @@ if grep -q "\binst.listen_on=.\+" /run/agama/cmdline.d/agama.conf; then # always run on the localhost OPTIONS=("${LOCAL_OPTIONS[@]}") - # the string can contain multiple addresses separated by comma - # replace commas with spaces to iterate over them + # the string can contain multiple addresses separated by comma, + # replace commas with spaces and iterate over items ADDRESSES=${LISTEN_ON//,/ } - ADDRESSES=("${ADDRESSES[@]}") - for ADDRESS in "${ADDRESSES[@]}"; do + for ADDRESS in $ADDRESSES; do # check if the value is an IP address (IPv6, IPv6 link local or IPv4) if echo "$ADDRESS" | grep -qE '^[0-9a-fA-F:]+$|^[fF][eE]80|^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then echo "<5>Listening on IP address ${ADDRESS}" From d6a1cadc83547dace1614343895bf94bb2b4919d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Wed, 11 Mar 2026 17:01:55 +0100 Subject: [PATCH 10/15] Changes --- rust/package/agama.changes | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rust/package/agama.changes b/rust/package/agama.changes index 39fbe7ab83..37db1a7095 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Wed Mar 11 15:33:11 UTC 2026 - Ladislav Slezák + +- Added support for the "inst.listen_on" boot parameter + - The "inst.listen_on=localhost" option disables remote access + - It is possible to specify IP address (both IPv4 and IPv6) + or network interface, accepts comma separated list + (jsc#AGM-153) + ------------------------------------------------------------------- Mon Mar 9 11:37:06 UTC 2026 - Ladislav Slezák From db4907ac13d1a80f4620a6449c6bfaad8bcc79ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Thu, 12 Mar 2026 09:40:47 +0100 Subject: [PATCH 11/15] Update rust/agama-server/src/agama-web-server.rs Co-authored-by: Martin Vidner --- rust/agama-server/src/agama-web-server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/agama-server/src/agama-web-server.rs b/rust/agama-server/src/agama-web-server.rs index 160dc01194..f04f3bacfd 100644 --- a/rust/agama-server/src/agama-web-server.rs +++ b/rust/agama-server/src/agama-web-server.rs @@ -79,7 +79,7 @@ struct Cli { struct ServeArgs { // Address/port to listen on. ":::80" listens for both IPv6 and IPv4 // connections unless manually disabled in /proc/sys/net/ipv6/bindv6only. - /// Primary port to listen on + /// Address:port to listen to, with comma-separated fallback address:port; can be repeated #[arg(long, default_value = ":::80")] address: Vec, From 06cd059005cef9d492847d948a7a30559cfdb328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Thu, 12 Mar 2026 09:41:06 +0100 Subject: [PATCH 12/15] Update rust/WEB-SERVER.md Co-authored-by: Martin Vidner --- rust/WEB-SERVER.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/WEB-SERVER.md b/rust/WEB-SERVER.md index e11a1b3c00..b7c186b766 100644 --- a/rust/WEB-SERVER.md +++ b/rust/WEB-SERVER.md @@ -68,7 +68,7 @@ Some more examples: - IPv4, only specific interface: `--address 192.168.1.2:5678` (use the IP address of that interface) -The server can listen on several address, you can use the `--address` option +The server can listen on several addresses, you can use the `--address` option multiple times. ## Trying the server From e44a6a9a0ac245ff8f47cdd51420198a91170930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Thu, 12 Mar 2026 09:41:21 +0100 Subject: [PATCH 13/15] Update rust/package/agama.changes Co-authored-by: Martin Vidner --- rust/package/agama.changes | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/package/agama.changes b/rust/package/agama.changes index 76609fffc5..aa282fd83a 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -5,6 +5,7 @@ Wed Mar 11 15:33:11 UTC 2026 - Ladislav Slezák - The "inst.listen_on=localhost" option disables remote access - It is possible to specify IP address (both IPv4 and IPv6) or network interface, accepts comma separated list + - agama-web-server --address2 option removed, --address can now be repeated (jsc#AGM-153) ------------------------------------------------------------------- From fa80638fb5a5687212b1bd6387198627a68ed488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Thu, 12 Mar 2026 10:00:13 +0100 Subject: [PATCH 14/15] Add -h/--help option --- rust/share/agama-web-server.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rust/share/agama-web-server.sh b/rust/share/agama-web-server.sh index e5990436b8..55291fdf94 100755 --- a/rust/share/agama-web-server.sh +++ b/rust/share/agama-web-server.sh @@ -23,6 +23,16 @@ # This script is a wrapper for the Agama web server, it evaluates to which # addresses the server should listen to. +if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: $0" + echo + echo " This is a wrapper script for the Agama web server (agama-web-server)." + echo + echo " It configures the listening addresses for the web server based on" + echo " the \"inst.listen_on\" boot option." + exit 0 +fi + # the default options: listen on all interfaces for both HTTP and HTTPS ports, # the IPv4 addresses are fallbacks when IPv6 is disabled with the # "ipv6.disable=1" kernel boot option From 5cc4f0f0ae4e748b58fb05d237ad5d1d01e2baf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Thu, 12 Mar 2026 14:03:56 +0100 Subject: [PATCH 15/15] Fixed comment --- rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h | 2 +- rust/zypp-agama/zypp-agama-sys/src/bindings.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h b/rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h index 9d8f3d23a5..8059d919b6 100644 --- a/rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h +++ b/rust/zypp-agama/zypp-agama-sys/c-layer/include/lib.h @@ -231,7 +231,7 @@ bool is_package_selected(struct Zypp *zypp, const char *tag, bool run_solver(struct Zypp *zypp, bool only_required, struct Status *status) noexcept; -/// Create a solver testcase, dumps all all solver data (repositories, loaded +/// Create a solver testcase, dumps all solver data (repositories, loaded /// packages...) to disk /// @param zypp see \ref init_target /// @param dir directory path where the solver testcase is saved diff --git a/rust/zypp-agama/zypp-agama-sys/src/bindings.rs b/rust/zypp-agama/zypp-agama-sys/src/bindings.rs index a4b0750dc5..4aa288bc60 100644 --- a/rust/zypp-agama/zypp-agama-sys/src/bindings.rs +++ b/rust/zypp-agama/zypp-agama-sys/src/bindings.rs @@ -674,7 +674,7 @@ unsafe extern "C" { ) -> bool; #[doc = " Runs solver\n @param zypp see \\ref init_target\n @param only_required if true, only required packages are installed (ignoring\n recommended packages)\n @param[out] status (will overwrite existing contents)\n @return true if solver pass and false if it found some dependency issues"] pub fn run_solver(zypp: *mut Zypp, only_required: bool, status: *mut Status) -> bool; - #[doc = " Create a solver testcase, dumps all all solver data (repositories, loaded packages...) to disk\n @param zypp see \\ref init_target\n @param dir directory path where the solver testcase is saved\n @return true if the solver testcase was successfully created"] + #[doc = " Create a solver testcase, dumps all solver data (repositories, loaded\n packages...) to disk\n @param zypp see \\ref init_target\n @param dir directory path where the solver testcase is saved\n @return true if the solver testcase was successfully created"] pub fn create_solver_testcase(zypp: *mut Zypp, dir: *const ::std::os::raw::c_char) -> bool; #[doc = " the last call that will free all pointers to zypp holded by agama"] pub fn free_zypp(zypp: *mut Zypp);