Skip to content

JEP 411

Chapman Flack edited this page Apr 5, 2024 · 7 revisions

PL/Java and JEP 411: Java 17 and beyond

Changes in Java, over some number of releases starting with Java 17, will force important functional changes in PL/Java.

Permissions and enforcement in PL/Java

PL/Java supports at least two PostgreSQL CREATE LANGUAGE declarations, one with and one without the TRUSTED property, and enforces limits on what a function declared in the TRUSTED language can do. The PL/Java 1.6 series goes further and makes those restrictions configurable, and more than two PL/Java "languages" can be declared, with different configurable restrictions for each, as described in the documentation.

To provide and support these controls, PL/Java relies on many features of the groundbreaking Java 2 security architecture as specified in 2002.

Changes in Java 17 and beyond: JEP 411

In April 2021, the Java developers announced a JDK Enhancement Proposal, JEP 411, that will remove this functionality from Java over a series of releases, with the first changes already shipped in Java 17.

As originally described, the "major components of the security model are security policy, access permissions, protection domain, access control checking, privileged operation, and class loading and resolution." JEP 411 will eventually eliminate the first, fourth and fifth, with the second and third remaining in vestigial form. PL/Java relies on those.

Java 17

In Java 17, however, all of the functionality remains; only warnings and deprecation markings have been added. Java 17 is also positioned as a long-term support release, the successor in that role to Java 11. It will therefore be possible to run current PL/Java versions without difficulty for a number of years.

However, Java 17 will be designed to write an unconditional warning on its standard error channel (which will go into PostgreSQL's log file, if logging_collector is enabled), every time any backend loads PL/Java. The JEP 411 proponents rebuffed requests to allow a higher layer, like PL/Java, to suppress the low-level JVM warning and issue one that is layer-appropriate (such as an explanation about trusted functions or a link to this wiki page).

The JVM's boilerplate warning will look like this:

WARNING: A terminally deprecated method in java.lang.System has been called
WARNING: System::setSecurityManager has been called by org.postgresql.pljava.internal.Backend
WARNING: Please consider reporting this to the maintainers of org.postgresql.pljava.internal.Backend
WARNING: System::setSecurityManager will be removed in a future release

As much as the warnings will urge you, please do not consider reporting them to the maintainers of PL/Java.

The PL/Java 1.6 series, as described below, does attempt to suppress the boilerplate JVM warning, and issue a "migration advisory" ereport of a more useful nature, only upon certain administrative actions like CREATE EXTENSION or installing a jar, and no more than once in a session. That should cut down on the log spam.

Java 18 and later

Two things are expected in Java 18 or later:

  • a special JVM option, -Djava.security.manager=allow, will be needed at launch time, to allow PL/Java to use the necessary components in the JVM
  • the components in the JVM, even if allowed, may have been "degraded" or changed into no-ops.

While the JEP does not read quite clearly on this point, it is possible both changes will ship in the same Java version, in which case there will be no point in using the special option.

If the changes arrive separately, so that there is a version where the special option is needed but the components still work if allowed, then adding -Djava.security.manager=allow to pljava.vmoptions will allow PL/Java to use that Java version.

Update: as of Java 22, the functionality does not seem degraded yet, as long as -Djava.security.manager=allow is specified, as is needed for Java 18 and above.

For both current release series of PL/Java, 1.5 and 1.6, there will eventually be a Java version on which they will be unable to enforce the intended security policies. Where that enforcement is needed, they should be run on any earlier Java version, including 17 and 21, which are positioned for long-term support.

Plans for PL/Java release series

PL/Java 1.5 series

PL/Java 1.5 is the series minimally maintained for legacy support (PostgreSQL back to 8.2, Java back to 6). Its build process requires Java 14 or earlier, but it can be run on later Java versions. Minimal changes will be made in 1.5.

  • Because it already does not support building on newer Java versions, it will not need any source code changes to suppress the new deprecation warnings.
  • It will make no attempt to intercept the fixed warning message described above. When running on Java 17 or later, that warning will be written on the backend's standard error channel, every time a backend starts PL/Java.
  • Code will not be added to automatically supply the -Djava.security.manager=allow option for any Java version.
  • Code will be added (in 1.5.8) to attempt to detect when the needed components have become no-ops, and then throw an exception rather than executing any function declared as trusted.

Options if that exception is seen can include:

  • Continuing to use PL/Java 1.5, downgrading Java to the preceding version
  • Changing all trusted function declarations to untrusted and accepting that they will run without security enforcement
  • Upgrading to a newer PL/Java version if available.

Note that even functions declared as untrusted have normally had a few limits enforced in PL/Java 1.5, and even those limits will be unenforced if running on a version of Java where the needed components are no-ops.

PL/Java 1.6 series

PL/Java 1.6 is the current recommended series covering PostgreSQL 9.5 and later, Java 9 and later.

  • The source code will need warning-suppression annotations added (in 1.6.3) for clean builds with Java 17 and later. In fact it does not, because 1.6 is built with --release 9, for which the deprecations are not effective.
  • Code will be added (1.6.3) to attempt to suppress the boilerplate, low-level warning from the JVM, so that more meaningful notice in relevant PL/Java terms can be given. Suppression of the boilerplate will be best-effort, relying on JDK internals, given the rebuffed request for an exposed control serving that purpose.
  • Code will be added (1.6.3) to:
    • Issue warnings of the upcoming JEP 411 impacts, including a link to this wiki page
    • Issue the warning whenever the runtime Java major version is above 11, so as to warn not only sites that have moved from 11 LTS to 17 LTS, but also sites that update more frequently between LTS versions
    • Issue the warning when PL/Java is installed or upgraded, or when a cluster that has PL/Java installed undergoes a binary pg_upgrade (in the pg_upgrade case, the warning will be unconditional, because no JVM is started at that time to query its version)
    • Issue warning, at most one per session, at commit of a transaction that has declared or redeclared at least one PL/Java function
    • Refuse to execute, if the needed support is detectably stubbed out, unless a special configuration option is set
    • Add a new configuration option that can be set, by a superuser, to try to allow all functions to execute with no security enforcement regardless of their declarations. Whether this is possible, or PL/Java will just break, depends on exactly how the Java developers choose to "degrade" the functionality.
  • 1.6.3 will not be usable on whatever version of Java finally removes the deprecated APIs. It will fail with linkage errors.
  • Code may be added (in a release after 1.6.3), to automatically supply a -Djava.security.manager=allow option if there would be any point (that is, if there is expected to be a Java release where the option is needed but the needed functionality is not yet stubbed out).
  • A release after 1.6.3 may also be adapted to run without linkage errors on a Java version where the APIs have been finally removed.

Note that even functions declared as untrusted run with significant limits by default in PL/Java 1.6, and the limits on both trusted and untrusted functions are configurable in policy, and all such limits will be unenforced if running on a version of Java where the needed components are no-ops.

PL/Java 1.6 will continue to see active development for the foreseeable future. The existing enforcement mechanisms will continue to be supported at least through Java 17 LTS, and for some applications, its fine-grained security policy may remain more attractive than what post-JEP-411 versions will support.

PL/Java ?.?

A new major version of PL/Java will need to be developed that can support some notion of trusted-function enforcement without relying on the rescinded Java mechanisms.

This will be a big undertaking, and will have to scale back expectations to a much more basic model of what can be enforced: chiefly filesystem access, process manipulations, and IPC. The result will probably land somewhere more fine-grained and configurable than PL/Java 1.5, but much less so than 1.6.

All usual disclaimers about forward-looking statements must be applied.

During that development, both existing PL/Java series, as described above, can continue to be used, and with all functionality unimpaired, on Java versions up to and including 17 LTS.