Live data from Hacker News

The modern packager’s security nightmare

blogs.gentoo.org

121–130 of 282 posts

Re: The modern packager’s security nightmare

#121
> We try hard to unpin the dependencies and test packages with the newest versions of them. However, often we end up discovering that the newer versions of dependencies simply are not compatible with the packages in question. Sadly, upstreams often either ignore reports of these incompatibilities or even are actively hostile to us for not following their pins.

I don't get it. They unpin the versions and then are disappointed that the software does not work with untested versions? It would be nice if semantic versioning could always work but it does not. I know Elm, Rust and Nix have some solutions to propose for this problem.

Re: The modern packager’s security nightmare

#122

I feel like this confuses a lot of things by assuming an extremely sophisticated end user. Sure, if you're a double-threat dev/sysadmin using linux, then when some vulnerability gets discovered in some dynamically linked library on your system, you have the capacity to (a) receive information about that fact, and (b) update it. But now suppose you're an ordinary person. You use software. Maybe you even have a windows…

> (b) You have to either learn what a DLL is and learn how to update it ...

That's not at all what the process is for the ordinary person. The ordinary person sees a notification pop-up from "Ubuntu Software Center" that says 8 packages or whatever need to updated, with one button that says "update everything now" or whatever and one that says "remind me later" or whatever.

It's up to you to choose a distro that applies the appropriate amount of rigor with regards to testing dynamic library updates. For bleeding edge distros like Gentoo or Arch, it's not that much. Upstream publishes an upstream, and the Gentoo package maintainer chucks it into the testing branch. After 30 days, if no one complains, it gets marked stable. The user chooses a certain amount of risk. (although it's been several years at least since ABI breakage has been an issue for me on Gentoo testing) (note that security critical updates are fast tracked into stable) For other distros like RHEL and Debian stable, the package maintainer spends considerably more effort ensuring a random update of openssl-1.1.1i to openssl-1.1.1j doesn't break stuff. The user chooses a certain amount of stability at the expense of not having the latest version of whatever.

On the other hand, on my Windows computer at work, the process for updating is significantly more intrusive. Few OS updates can be applied without a reboot, Visual Studio updates do not permit me to continue working during an update, there are half a dozen auto-updaters, and some programs which don't get updated unless I manually go to their website and check for an update. I don't know when the last time I updated 7-zip is?

Within the past week, there was a bug report that Python has an RCE with untrusted floats or something. On my linux systems, the package manager had an update within hours, and because there is only one Python installation on each of my linux machines, I know that all applications leveraging Python are now protected from that RCE. On my Windows work machine, I have not been notified that any of the applications I use which embed Python need to be updated. Presumably, this means my Windows machine is vulnerable.

Re: The modern packager’s security nightmare

#123

This will be a somewhat intemperate response, because as a developer of a significant library I found this quite irritating. If you publish a Python library without pinned dependencies, your code is broken. It happens to work today, but there will come a day when the artifact you have published no longer works. It's only a matter of time. The command the user had run before, like "pip install spacy==2.3.5" will no lo…

It's maddening to hear people say things like, "Oh if everyone just used semantic versioning this wouldn't be a problem". Of course this cannot work. _Think about it_. There are innumerable ways two pieces of code can be incompatible. ... If you call these things "breaking" changes, you will constantly be increasing the major version. One of the things that prompted the OP was this breakage in Python's cryptography p…

It's hard because underneath is a battle of who bears the maintenance and testing costs that no one wants to bear.

Asking a publisher to qualify their library against a big range of versions just means that they need to do a lot more testing and support. Obviously they want to validate their code against one version, not 20, and certainly don't want an open ended > version which would force them to do a validation each time a minor dep is released.

Similarly when publishers say I will only work against version X, this puts a bigger burden on the user to configure their dependencies and figure out which version they can use. They would like to push that work onto vendors.

What's a bit depressing is that these economic concerns are not raised openly as the primary subject matter, but the discussion is always veiled in terms of engineering best practices. You're not gonna engineer your way out of paying some cost. Just agree on who bears the cost and how you will compensate them for the cost, then the engineering concerns became much easier.

Re: The modern packager’s security nightmare

#124
post #58

Earlier quoted context omitted.

> Two different communities create two very different kinds of software, which run on the same systems, but are created and distributed in very different ways. This is where the disconnect is coming from. The distro maintainers are coming from a world of multi-user systems where backwards compatibility and updating deps without disturbing a user's workload / forcing them to recompile is paramount. Go (and a fair amou…

> it's easier to reason about code that is running if you can look at the commit that a bin was built from and know exactly what's inside (including all deps). Believe me, it's usually the opposite. Lack of proper releases, testing, and versioning results in unending checkout-fu to figure out what commits for each of 20 libraries will work for each other. The idea is plainly stupid, without any redeeming qualities. T…

> Lack of proper releases, testing, and versioning results in unending checkout-fu to figure out what commits for each of 20 libraries will work for each other.

Admittedly I'm basing this on my experience in a very large monorepo environment, but there's no figuring out which commits will work with each other. Every commit with every library will work, otherwise it doesn't get committed. Yes, this involves massive CI infra and tooling to aid in refactoring.

You want to make a breaking change to a lib? Great, it's on you to update every piece of code that calls it.

It's great when you can control every piece of your infra, but I totally get how it's unfeasible (and maintainer hell) for the distro community.

Re: The modern packager’s security nightmare

#125
post #56

Earlier quoted context omitted.

The open source solutions are primarily for C/C++. A bit for Perl, a bit for Python. But they haven't really moved on. Java has been tacked on since forever. Same for .NET, JavaScript, whatever. And if you wanted your program propagated to all major distros you'd have to wait a decade. Nobody has time for that. Not corporations, not mom and pop stores, and I doubt many hobbyists.

Then you always have the option to package your software for the ones you use and not worry about the rest. If you want to make your program popular then packaging is part of the process needed.

That's not the problem. (*) The actual problem is that the distro maintainers want split packages (for security and so on), not vendored, and this requirement was already burdensome for many languages other than C/C++. If vendored packages were acceptable I believe people would have of course contributed them. Maintainers made this obstacle themselves (for a good cause, arguably) and it seems farfetched for them to then complain other languages are uncooperative.

* Was "isn't that a problem?", which was not I meant to say.

Re: The modern packager’s security nightmare

#126
post #58

Earlier quoted context omitted.

> Two different communities create two very different kinds of software, which run on the same systems, but are created and distributed in very different ways. This is where the disconnect is coming from. The distro maintainers are coming from a world of multi-user systems where backwards compatibility and updating deps without disturbing a user's workload / forcing them to recompile is paramount. Go (and a fair amou…

> it's easier to reason about code that is running if you can look at the commit that a bin was built from and know exactly what's inside (including all deps). Believe me, it's usually the opposite. Lack of proper releases, testing, and versioning results in unending checkout-fu to figure out what commits for each of 20 libraries will work for each other. The idea is plainly stupid, without any redeeming qualities. T…

Are you actually talking about static linking here, or just venting your spleen about sloppiness in software engineering practice more generally?

Because tracking dependencies in source control (specifically, checking in lock files) is tremendous for reproducibility. It means that when bisecting through the commit history to find when a problem began is not just bisecting through the local source code, but also the specific versions of every dependency.

So regardless of whether the issue you're investigating is in the package, a dependency, or an unexpected interaction between the two, you're able to find the first commit that introduced the issue in O(log(commits)) time, rather than needing O(commits * (num dependencies * dependency versions)) time.

Re: The modern packager’s security nightmare

#128
post #11

Earlier quoted context omitted.

I think it would help if distributions could snatch a piece of real estate in upstream software. Something like, "in every project, the debian/ root folder belongs to the debian project and follows their rules". The packaging could then verify this folder and put their patches, build scripts, etc. there. This would help upstream communication a lot, I guess.

The problem is there isn't just Debian, there is Debian and arch and Gentoo and nix and guix and freebsd and cygwin (at least). Then, how do I check who should have merge access to all these directories?

The problem is actually far worse than that. While the gp has what seems to be a reasonable solution, the issue is that you can't actually know what distribution is going to package your software, or what environment it is going to run in. There are code bases out there that still work decades later without any active maintenance, but if you need maintenance just to be able to build software in a new environment something is wrong.

The underlying issue is that there are no standards for how to package software in a multi language environment. If I want to go from a state where (require 'module-name) fails to a state where (require 'module-name) succeeds, there are a potentially infinite number of ways that that could be accomplished, a single software project cannot ever specify all the possible ways for building software. What they can try to do use uniform interfaces and standard patterns for building their software, in a way that delegates dependency management to an external system. It seems that it is hard for software developers to admit that other people know more about how and where their software will be running than they do.

Good engineering practice seems to dictate that dependency and environment management should be completely orthogonal to the development of an individual component or individual functionality. The op is 3 stories about what happens when the two are not kept orthogonal. The fundamental problem is that it is often easier for individual projects to make decisions that conflate the individual project with its dependencies (no longer orthogonal).

To my knowledge, there is not a universal or well understood set of requirements that could be used to specify what a stable interface between an individual software project and its dependencies looks like. There are a number of candidates, such as gentoo ebuilds, rpm spec files, etc. however I have not seen one that effectively accommodates all of them. Further, there are languages where the implementation (or even design) makes it impossible to keep dependencies and individual projects orthogonal.

The end result of non-orthognal systems is more work for everyone, more wasted cpu cycles, and worse security. Distros can't stop people from using languages that conflate the two, but they can tell them that they are on their own, and that the distros can't depend on components written in such languages in the core of the OS. To everyone pushing the rewrite it in Rust meme this should be a wakeup call. The current design decisions in the language and limitations of the implementation make it less secure than C or C++ because swapping out dependencies is bottlenecked by the centralized primary development team, and maintainers and users can't take orthogonal action to fix an issue.

Re: The modern packager’s security nightmare

#129

Earlier quoted context omitted.

The day I stop static linking is the day I can compile on one distro and ship to many without worrying about users reporting loader errors. That day is not today. Maybe do the trendy thing and link your whole distro together with your app (a.k.a containers)?

The fact that containers exist and are prevalent is a damning indictment on the Linux model imho. Producing software that can simply launch is so wildly complex that the solution is... to snapshot an entire operating system install to run the application. That’s INSANE. But it’s unfortunately required. https://xkcd.com/1987/ > Now, for the worst of all — one that combines all the aforementioned issues, and adds even…

You are comparing having containers versus having no containers and no kind of bundling at all, which is not a reasonable comparison. If you need an OS snapshot, the only option on other operating systems is often to use a VM. That's what containers are used as an alternative to. (The other alternative there is to never install patches or upgrades on the production machines, which probably creates as many problems as using containers)

Re: The modern packager’s security nightmare

#130
post #58

Earlier quoted context omitted.

> Two different communities create two very different kinds of software, which run on the same systems, but are created and distributed in very different ways. This is where the disconnect is coming from. The distro maintainers are coming from a world of multi-user systems where backwards compatibility and updating deps without disturbing a user's workload / forcing them to recompile is paramount. Go (and a fair amou…

This makes me wonder if there's a Linux base system suitable for servers that embraces the newer approach. That is, a minimal base system that's built for immutable container images, providing just what's needed to bootstrap the current generation of language-specific build and package systems. The Alpine Linux Docker images might be a good choice for now, but IIUC, Alpine Linux itself still embraces the older distro…

Maybe the Nix package manager / NixOS is what you're looking for? I think it takes the best features from both worlds.

Every package installed with Nix is isolated into content-addressable* directories, so for example, my install of Firefox is located at /nix/store/c7pmng2x05dkigpbhnjs8fdzd8kk31np-firefox-85.0.2/bin/firefox. This is pretty inconvenient to use directly, so Nix generates a profile that symlinks all your packages into one place (eg. /run/current/system/sw, ~/.nix-profile), and then environment variables like PATH can just include /bin.

With this approach, I can have multiple versions of the same package installed simultaneously, without them conflicting with each other. Like in a traditional distro, any dependencies that are shared between packages aren't duplicated, but if a package needs to explicitly depend on a different version, it can.

Also, because Nix is designed as a functional package manager for building packages from source (even though it has a binary cache), you can trace back exactly what sources were used to build your package and its dependencies, all the way back to the bootstrap binaries used to build any self-hosting compilers (gcc, rust, openjdk, ...)

* Most packages use a hash that's generated from the inputs used to build it, rather than the output that's generated.

Post reply on HN