Skip to content

Add RDP printer redirection - #284

Open
skolarianer wants to merge 6 commits into
sol1:mainfrom
skolarianer:feat/rdp-printing
Open

skolarianer wants to merge 6 commits into
sol1:mainfrom
skolarianer:feat/rdp-printing

Conversation

@skolarianer

Copy link
Copy Markdown
Contributor

Adds optional RDP printer redirection. Print jobs are converted to PDFs by Ghostscript on the guacd host and sent to the browser, which opens the local print dialog. The printer can be enabled and named per connection or ad-hoc session.

Security policy change for review: The automatic print flow loads the generated PDF from a blob: URL in a hidden iframe, then calls the browser’s print() function. This requires adding frame-src 'self' blob: to the CSP. Please review whether this scoped allowance for blob frames is acceptable for the project’s security policy.

RDP printer redirection requires drive redirection. Print-only sessions therefore use a private per-session drive directory with mode 0750, while keeping the browser file manager disabled and uploads and downloads disabled. When file transfer is separately enabled, its configured permissions apply.

Adds Ghostscript to install packages and includes a client-side test for PDF printing and regular file downloads.

Tested: git diff --check; node tests/client-print-file.mjs

cargo test --offline passed.

@davekempe

Copy link
Copy Markdown
Member

Thanks, printing has been asked for a few times. Some changes needed though:

  • Download policy: printing is a way around the download and copy restrictions. Every print job comes to the browser as a PDF, which can be saved, even with allow_download = false or copy disabled. Ad-hoc users can also turn it on themselves. Could printing follow the download setting (or an admin setting that defaults to off), with ad-hoc requests ignored unless that's on, and each print job logged (just the headline, not the content)?
  • Hidden drive: the hidden print drive isn't needed. At our guacd pin the printer is registered separately from the drive, and printing alone loads the device channel. As it stands it maps a writable "rustguac Redirected Drive" into Windows even when file transfer is off. Could you drop the print-only drive?
  • Query string: client_url now ends in ?file_manager=..., but the launcher, search and reconnect all append ?name=..., so display names get lost (including in the fullscreen frame). The client already gets file_manager_enabled from /api/sessions/:id, so client_url can stay as it was. Share-link viewers could get it from the banner response.
  • Showing the PDF: at the moment it's loaded into a frame on our page, which needs frame-src blob:. We've just written up our framing rules (docs/security.md, "Framing and clickjacking"), and one of the promises there is that files from a session are saved, never shown as pages on our origin. Could it open in a new tab, or download, instead?
  • Small stuff:
    • The commits are authored as root@ your dev box; could you re-author them with your GitHub identity?
    • There's one em-dash in installation.md (we avoid them in docs).
    • The editor saves printer_name even when printing is off.

The sendAck change to the client's file-stream handling looks like a real fix on its own. We think missing acks may be why uploads drop sessions (#123). Happy to take that as a separate small PR straight away if you split it out.

@skolarianer
skolarianer marked this pull request as draft October 20, 2026 03:24
@skolarianer

skolarianer commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the review. The printer changes are consolidated in d422edb, authored with my GitHub identity.

Printing now requires [rdp] allow_printing, which defaults to false. When disabled, the UI hides the printing controls and the server ignores saved-entry and ad-hoc print requests. When enabled, each job logs the session, user, and filename only; PDF contents are never logged. Printing remains separately gated from allow_download because the browser receives the PDF.

The print-only drive has been removed. Printer redirection uses its own RDP device and does not create a drive path. The file_manager query parameter is obsolete now and is also removed. The editor now saves printer_name only when printing is enabled.

The intended experience is that a user selects Print in the remote RDP application, and RustGuac sends the resulting PDF to the browser in memory. The browser then seamlessly opens its local print dialog without opening a visible PDF page, preview, tab, or download step. The user chooses a local printer in that dialog. Because the browser receives the PDF and it can be saved from the print dialog, this remains a data egress path.

The hidden blob: iframe is used only to invoke the browser's native print dialog for a seamless user experience. This requires frame-src 'self' blob: in the CSP. Could you review whether this scoped allowance for the automatic print flow is acceptable under the existing policy or an exception would be worth it?

I removed sendAck from this PR and isolated it on fix/client-file-stream-acks for separate review, as suggested: #317

@skolarianer

Copy link
Copy Markdown
Contributor Author

One clarification on print-job logging: the current implementation writes an INFO entry to the RustGuac service log with the session ID, user, and filename only. Did you mean a persistent, user-visible record in Reports or an audit log instead? The Reports page currently covers session history and usage, so print-job entries there would need separate storage and UI/API work.

@skolarianer

Copy link
Copy Markdown
Contributor Author

The CSP change is limited to print-enabled client pages now: only those responses allow blob: in frame-src. The print flow loads the generated PDF from a blob URL in a hidden iframe and invokes the browser’s local print dialog. Other pages rely on the same-origin default-src fallback. Could you review whether this scoped exception is acceptable under the project’s security policy?

@skolarianer

Copy link
Copy Markdown
Contributor Author

The new rdp.printing_method setting defaults to download, so the default print flow saves the PDF without rendering it in a RustGuac frame. Administrators can opt into trigger_print for the seamless browser print dialog; rdp.allow_printing remains off by default and must also be enabled.

With trigger_print, the blob: frame permission is still added only to client pages for sessions with printer redirection enabled. Could you review whether this opt-in exception is acceptable under the framing policy?

@skolarianer

Copy link
Copy Markdown
Contributor Author

oops... there still was a commit by root@. Sorry for the mess!

@skolarianer

Copy link
Copy Markdown
Contributor Author

Keeping this PR in draft until PR #317 is merged. RDP printing depends on the initial file-stream acknowledgement included there.

@skolarianer
skolarianer marked this pull request as ready for review October 21, 2026 23:05
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.

2 participants