Skip to content

Instantly share code, notes, and snippets.

@free-pmx
Last active April 12, 2025 18:15
Show Gist options
  • Save free-pmx/286871284450c87f6116baf66068d03b to your computer and use it in GitHub Desktop.
Save free-pmx/286871284450c87f6116baf66068d03b to your computer and use it in GitHub Desktop.

A neater Proxmox no subscription setup

TL;DR Be aware of security implications that come with third-party setup tools. Compare Debian-backed approach with innocuously looking scripts and learn what actually makes the difference.


ORIGINAL POST A neater Proxmox no subscription setup


Lots of users run Proxmox suite of products with no support license and as long as they understand the caveats of freely available packages, there is nothing wrong with that. A major “post-install” chore no to forget is setting up the no-subscription repositories - otherwise users will be left with no updates whatsoever. Many also opt to look for a solution to get rid of the infamous “No valid subscription” notice popup, also dubbed as a nag - for a reason.

This post will explore a robust solution to all of this - now conveniently packaged into a no-subscription tool - that is meant to do one thing and do it well - and more importantly, will have its innards explained below.

Security risk

Having to deal with chores like this is somewhat inexplicable given that Proxmox distribute their suite under a FREE license - which grants everyone freedom to modify it as they please. This simply means users WILL take advantage of third-party solutions and are then often left in the dark what those actually do to their otherwise stock install. In turn, Proxmox open up their unpaid user base to third-party associated security risks over something that could have been resolved with a simple acknowledgment checkbox.

What not to do

This post exists to encourage better understanding of the system and what a user runs on it. The last thing one wants to end up with is having stealth third-party recurrent scripts on their system that they do not even comprehend.

WARNING Never download unknown code without having (someone trusted) had a good look at it. No exceptions.

Notably, whatever a user ends up running on a Proxmox host is running under the root privileges due to the architecture of Proxmox suite of products.

Issues with standalone scripts

There is a major caveat to using a simple script for a problem at hand here: effects of it will NOT persist, changes would get overwritten during upgrades from official repositories. Any “one-off” script that does NOT have this drawback uses its “single run” to install something onto the system that then runs recurrently. There goes the popular perception of a mere script being less intrusive to the system then a more complex solution.

The popular post-install script

Merely due to number of inquiries - NOT because it does something particularly worse than others - we will have a look at the now community maintained post-install script found under the misc category and delve right into its source on GitHub. ^ We will ONLY focus on one of its parts, namely: removing the ‘no-subscription’ popup. It can be found below a line: msg_info "Disabling subscription nag"

There’s the code of our interest - which we will dissect:

echo "DPkg::Post-Invoke { \"...\"; };" >/etc/apt/apt.conf.d/no-nag-script
apt --reinstall install proxmox-widget-toolkit &>/dev/null

And the inner part (...) was (reformatted - all from that single line, escaping NOT retained):

dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'

if [ $? -eq 1 ]; then {
    echo 'Removing subscription nag from UI...';

    sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/}' \
        /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js;
}; fi

APT hook

The first part adds a DPkg::Post-Invoke like into a file under /etc/apt/apt.conf.d/ - this belongs to APT hooks that are then run (as per the rule) virtually every time APT runs on the system, for whichever reason, over all packages. Such scripts (the content) have to be scrutinised as they are basically living on on the system and run under root privileges, regularly.

NOTE There also does not appear to be any built-in way to remove this script, while its presence after having run the script (if only once) is concealed from the user.

Following addition of this inner (recurrent) script onto the system, ir proceeds to (once) reinstall original a Proxmox package - this might look counter-intuitive, but it actually relies on the (now installed) recurrent script to do its work already on the APT run (i.e. hence the hook designation), i.e. the reinstall will be subject to the inner script already.

sed command

The inner script - this will be run regularly, e.g. after updates - checks result of dpkg -V, i.e. integrity of the same Proxmox package, more specifically it will find out whether a file named proxmoxlib.js has been tampered with (compared with the stock version that comes with the package).

The conditional if statement only executes the sed inside if the package is intact (stock). This is a wide assumption - that nothing else is e.g. patching the same file - as otherwise it would not do anything. The idea was to only execute the patching when the target file has changed, but unfortunately the APT hooks were absolutely not designed for this kind of problem and so there’s no knowing what exactly APT was altering on the system - hence the conditional check. The script runs every time APT runs, but only shows the announcement when it is about to patch anything.

JavaScript patched

The actual sed run is beyond the scope of this post, but it is a method of replacing strings through the use of so-called regular expressions, i.e. the file proxmoxlib.js will have its content altered on one specific line, changing it (< out, > in):

<           .data.status.toLowerCase() !== 'active') {
---
>           .data.status.toLowerCase() == 'NoMoreNagging') {

This affects the JavaScript component of Proxmox - a condition within - more of the surrounding JavaScript code can be seen in a separate post here regarding the no subscription notice removal - by a different method. A mere reformatting of the underlying target file might deem this regular expression ineffective, corrupt the JavaScript code or worse - change the logic without bringing in any obviously corrupt syntax and in an unintended piece of code.

NOTE To reiterate, this is not a comparison of the sole script and the free-pmx package - which is about to be described - but whole range of approaches making use of APT hooks and sed substitutions on source files. If anything, this digression was meant to shed more light on what “just a script” can bring onto the system. Other approaches might e.g. install cron jobs or install systemd services.

A better way

Meanwhile, Debian already provides a neat mechanism for handling a situation that was to be tackled above - by the packaging system itself. A Debian package can bring in its executables - which can be scripts and configuration, but also - importantly - explicitly declare its interest to be notified when other packages are altering specific files on the system. It is the system that decides when it will trigger actions implemented by the interested package and under no other than declared rules. When the package is removed, so are its components responsible for handling these triggers.

Myths around Debian packages

First, we will dispel some myths that might surround the seemingly complicated matters:

  • No dubious APT repository. A package can be installed manually - from a single downloaded file - without having to trust an unknown repository. This one-off approach will NOT keep it updated, but this is the safer way to run code from strangers. Installing a package simply means files will be copied into well-defined locations. The system will take care of removing those same files during uninstall.
  • Transparency. Debian package is essentially an archive, the lack of transparency might come if it contains compiled binaries, but packages that contain only cleartext payload are as transparent to the user as a compressed script.
  • Root access. It is true that a e.g. a postinst hook (not to be confused with APT hooks mentioned above) runs under root privileges when package is installed, but this is the case for any script run from through CLI in Proxmox stock system. Furthermore, these scripts have well-known place in the package archive and are easy to find - in fact, they should be the first ones of interest to audit.

Further, maintainers of Debian packages typically follow certain standards when creating them - if they want it to pass a check.

free-pmx-no-subscription

Checking thoroughly any scripts is vital. Debian packages that contain scripts are no different. For the sake of convenience, GitHub repository of free-pmx-no-subscription is available, but users should always be ready to dissect a package downloaded as already packaged .deb file first-hand - or package it themselves.

TIP More on the structure of a Debian package, how to look into the archive and more are in a separate post specifically examining free-pmx-no-subscription v0.1 as an example.

The locations to examine:

  • DEBIAN/postinst - contains the script that runs with the package installation, it will only directly run the two user commands (below)
  • DEBIAN/triggers - list of files that when altered, e.g. by Proxmox upgrades, will launch the postinst script automatically
  • bin/ - user commands - actual scripts of no-subscription and no-nag
  • usr/lib/free-pmx - sub-tool scripts that are run or sourced from the user command scripts only (more below)
  • usr/share/free-pmx/no-nag-nag-patchdefs/ - patch definitions containing templates for search&replace-like substitutions
  • etc/free-pmx/no-subscription.conf - default configuration file

Conceptually, these are all BASH scripts relying on limited use of external tools, namely:

  • sha512 to calculate checksums;
  • gnupg to display repository key details;
  • wget to download Proxmox release key from their site;
  • perl with a single one-liner taking advantage of its capabilities when it comes to regular expressions.

User commands

No direct filesystem actions are performed in the user command scripts per se, i.e. they process options and arguments, display output and make use of the sub-tools as necessary. The actual declarations of any and all essential strings (e.g. APT repositories) can be found in the shared usr/lib/free-pmx/no-subscription-common that is a single source for both user commands.

The sub-tools are purposely small and take all the inputs from their arguments, not environment. Sub-tools do not directly source the shared script. The arguments come from user command scripts which always source them from the above-mentioned shared script.

no-subscription

User-level documentation can be found in the manual page.

There are two internal sub-tools that are called: - repo-list-replace to perform the actual repository list replacement; and - repo-key-check to ensure that Proxmox release keys are present on the system.

no-nag

User-level documentation can be found in the manual page.

This script relies only on patchdef-apply internal sub-tool, which implements the individual patching of front-end related (JavaScript) file. The user command script determines which files are to be patched, the sub-tool runs the well-known perl one-liner with single regular expression - see standalone post on patching the “No valid subscription” notice. All patches are applied in the same manner.

The difference within this implementation lies in the fact that there are more target files to patch and potentially more patches (per target) to be applied. Patches are defined in a series of modular .patchdef files held in usr/share/free-pmx/no-nag-nag-patchdefs/ - these are nothing more than rudimentary BASH scripts with variable declarations and can be examined for the actual JavaScript code snippets - before and after patching state.

Patching

The reason for the “more elaborate” patching method lies in its robustness - either whole blocks of code are identified and replaced with both syntactically and semantically correct substitutes or the whole patch fails gracefully, i.e. the target file remains untouched. The worst case scenario allows for safe failing of e.g. some of the patches - a good reason to e.g. consciously look for updated definitions if need be, but by no means an operational necessity. This is why this tool aims to be reliable in the long run - even if left outdated.

The format of patch definitions also aims to provide educational background on Proxmox implementation details and encourage users to tinker with it some more - if they desire. Hopefully it will become playground for JavaScript aficionados down the road - as no more is required.

And before breaking off - a little example: a single patch definition targeting the annoying highlight of the ‘no-subscription’ repository line - reveals that the original code is completely oblivious to the fact that Ceph is not highlighted.

NOTE Arguably, this is a testament how actually this feature is - and has been, for years - focused on marketing outcomes rather than system integrity.

Before pwt-32-repos-list-line:

if (components[0].match(/\w+(-no-subscription|test)\s*$/i)) {
metaData.tdCls = 'proxmox-warning-row';
err = '<i class="fa fa-fw warning fa-exclamation-circle"></i> ';

let qtip = components[0].match(/no-subscription/)
    ? gettext('The no-subscription repository is NOT production-ready')
    : gettext('The test repository may contain unstable updates')
    ;

After pwt-32-repos-list-line:

if (components[0].match(/\w+(test)\s*$/i)) {
metaData.tdCls = 'proxmox-warning-row';
err = '<i class="fa fa-fw warning fa-exclamation-circle"></i> ';

let qtip = gettext('The test repository may contain unstable updates');

There’s currently 2 target files only (for each product) - the Proxmox Widget Toolkit (hence ‘pwt’ in the patch name above) is common and then a specific one for each PVE / PBS / PMG - targeting dashboard.

No other than JavaScript files are getting patched and therefore only GUI is targeted - of a single host that the tool is being used on. Essentially, it influences the view of the otherwise unchanged situation - the other parts of stack, especially API are completely original. In other words, it is completely possible to run just one node patched like this with no influence on the rest.

Also, if the user decides to activate a license, this is still possible in the GUI with absolutely no ill-effects and will behave as per original implementation.

More to come

Expect this post to be further updated with more details depending on your feedback, which is very welcome in the GitHub Gist.

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