Skip to content

[do not merge] PoC for remote installation support#1274

Draft
bruno-fs wants to merge 2 commits into
rhinstaller:mainfrom
bruno-fs:poc-remote
Draft

[do not merge] PoC for remote installation support#1274
bruno-fs wants to merge 2 commits into
rhinstaller:mainfrom
bruno-fs:poc-remote

Conversation

@bruno-fs

@bruno-fs bruno-fs commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Context

Proof-of-concept for PIN-based authentication as part of the remote installation feature (INSTALLER-4395 / INSTALLER-3723).

Do not merge — keeping this open while the broader design is worked out. It was the basis for the team demo on April 8 and drove the architectural decisions we've aligned on since.

What This Demonstrates

Cockpit (the framework Anaconda's Web UI is built on) lets you replace its default login page and authentication handler with custom implementations. This PoC exercises both:

Custom login page

src/components/login/LoginPage.jsx, built into login.html/login.js. A minimal PIN entry form — shows/hides the PIN, submits it as a Basic auth header (PIN as username). Cockpit routes that to the custom auth handler.

Wired up via [WebService] CustomLoginPage in cockpit.conf.

Custom auth handler

src/scripts/cockpit-pin-auth — a Python script that speaks Cockpit's auth protocol over a Unix socket. Receives the Basic auth credentials from Cockpit, validates the PIN, and if correct, exec's cockpit.bridge to start the actual installer session.

Deployed as a socket-activated systemd unit (cockpit-pin-auth.socket + cockpit-pin-auth@.service), separate from the main cockpit-ws process. Each connection spawns a fresh instance. After the PIN is accepted, Cockpit's session cookie takes over.

Cockpit config

src/config/cockpit.conf sets CustomLoginPage and overrides the [Basic] auth command. Currently installs to /etc/cockpit/cockpit.conf.

Known issue: config path should move to an anaconda-owned path (e.g., /etc/anaconda/cockpit/) so it doesn't persist to the installed system. Tracked as a follow-up.

How to Try It

Build an updates image and run it against a Fedora Rawhide boot ISO:

make create-updates.img

Then boot a Rawhide installer with inst.updates pointing to the generated updates.img, for example with virt-install:

virt-install \
  --location <rawhide-boot-iso> \
  --extra-args "inst.updates=<path-or-url-to-updates.img> inst.webui.remote" \
  ...

The PIN is hardcoded to 1234 in src/scripts/cockpit-pin-auth. Once the installer is running, point a browser at the VM's IP on port 80 — you'll get the custom login page. Enter 1234 to authenticate.

Session behavior observed in the demo: tab close preserves the session (cookie survives), browser close requires re-login.

What's Deliberately Incomplete

  • PIN mechanism: Hardcoded to 1234. Real implementation would read from a boot parameter and display on the local console.
  • Config path: Uses /etc/cockpit/ — should be anaconda-owned.
  • Single-connection enforcement: Not implemented. Multiple sessions can connect simultaneously (this was live-demoed as a problem — one session can start installation while another changes storage config).
  • TLS: Not configured. Connection is unencrypted in this PoC.
  • Login page pre-auth info: IP address / Fedora edition can't easily be shown before login because Cockpit APIs require authentication. Still being figured out.

What We're Looking For

If you're looking at this from the Cockpit side:

  • Is there a simpler way to get OS/network info on the login page without requiring auth?
  • What's the recommended mechanism for enforcing a single concurrent session?
  • How does Cockpit distinguish a reconnection (same user, dropped connection) from a new session?

bruno-fs and others added 2 commits April 8, 2026 10:20
Integrates Cockpit custom authentication handler that enables PIN-based
authentication for the installer web UI. Includes systemd socket
activation units and authentication script.

Co-Authored-By: Jelle van der Waa <jvanderwaa@redhat.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a custom PIN-based authentication mechanism for the Anaconda Web UI, integrating with Cockpit. It adds a new React-based login page, a Python authentication handler script, and the necessary systemd units and configuration files. Several critical issues were identified in the review: the authentication script incorrectly attempts to read from stdout instead of stdin, a missing function reference in the login entry point will cause a runtime error, and there are security concerns regarding hardcoded credentials and insecure logging to /tmp. Additionally, the use of the deprecated unescape function in the login page should be updated to a modern alternative.



def read_auth_reply():
data = read_frame(1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The script is attempting to read from file descriptor 1 (stdout). It should read from file descriptor 0 (stdin) to receive the authentication reply from the parent process (cockpit-ws).

Suggested change
data = read_frame(1)
data = read_frame(0)

Comment thread src/login.js
const root = createRoot(document.getElementById("app"));
root.render(<LoginPage />);
document.documentElement.setAttribute("dir", cockpit.language_direction);
document.documentElement.setAttribute("lang", convertToCockpitLang({ lang: cockpit.language }));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The function convertToCockpitLang is not defined or imported in this file. This will cause a ReferenceError when the login page is loaded. Ensure the necessary utility is imported or defined.



def main(args):
logging.basicConfig(filename='/tmp/pin.log', encoding='utf-8', level=logging.DEBUG)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Logging to a fixed path in /tmp (e.g., /tmp/pin.log) is a security risk as it is vulnerable to symlink attacks. It is recommended to log to the system journal by writing to stderr (the default for basicConfig when no filename is provided) or to use a secure temporary file creation method.

Suggested change
logging.basicConfig(filename='/tmp/pin.log', encoding='utf-8', level=logging.DEBUG)
logging.basicConfig(level=logging.DEBUG)

setIsLoading(true);

const headers = {
Authorization: "Basic " + window.btoa(unescape(encodeURIComponent(pin))),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The unescape function is deprecated and should be avoided. For a modern way to handle UTF-8 strings with btoa, consider using TextEncoder or a more robust encoding utility.

send_problem_init("internal-error", str(e), {})
raise

if password != "1234":

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The authentication PIN is hardcoded as "1234". While this is a Proof of Concept, hardcoding credentials is a significant security risk. This should be replaced with a dynamic mechanism or a secure configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant