Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Its fun and games until you find out one of the things you bundled into your static binary has a remote code execution CVE that is being actively exploited in the wild. Good luck tracking down all the artifacts and making sure they are rebuild with the patched version - till the time next CVE hits and you can do it all again for every static binary bundling the affected component.


Rolling out a new binary is a happy path process that gets used all the time; what's the problem here?


Yeah, I don't really understand the hate containers get for this. If the people who run your software aren't updating it you're screwed anyway. I promise you'll be way more disappointed if you assume that the system libs in the wild will be patched and up to date.

Like if building a pet app is your company/team's job then containers more than fine. If you're a distro maintainer that has thousands on thousands of packages then sharing libs starts to make a lot more sense.


> Like if building a pet app is your company/team's job then containers more than fine. If you're a distro maintainer that has thousands on thousands of packages then sharing libs starts to make a lot more sense.

The problem is that distributions get their software from upstream developers. And if upstream decides to go static or use embedded code copies, the maintainers have much more work trying to untangle everything.

In my experience, packaging Go software for Linux distributions is very frustrating and tedious because of that.

Packaging Python or C/C++ software is much easier.


This is ironic, because Go in much easier to build than C or C++, especially since the introduction of Go modules.

Perhaps this "untangling" isn't the best idea, at least not always?


Unbundling is often necessary due to these issues:

- the bundled things might have unclear licensing

- bundling stuff that is available in the repos is sub optimal, as the repo version will be updated for security issues while the bundled version will not

- you can use some mechanism to mark what stuff is bundled in a package, but then you need to make sure the bundled thing is patched and rebuild each thing bundling it, where with system wide dependency you just update that and you are done


It's pretty rare there are parts with "unclear" licensing. Does it happen? Sure. But "often"?

I'm fairly sure that almost all projects are receptive to updates for dependencies with security issues; I don't see why a distro needs to "untangle" anything for them.

> with system wide dependency you just update that and you are done

Unless the system-wide update won't work with your program.

The entire point of the Go tooling is that you can reliably and consistently build your software, and replacing random parts to create a weird unsupported (by the actual developers anyway) version is something the Go tooling was never designed to support: it goes exactly against what it was designed to do. Hence my previous comment: perhaps this isn't the best of ideas.


This talk (from 2011 but still very relevant today) mentions some if the licensing and other issues package maintainers have to face:

https://www.youtube.com/watch?v=fRk97h1FLow


is it really that bad to distribute statically-linked go binaries in a distro? Or rather, perhaps the better question - what's the dynamic linking benefit that a distro can't live without?

Wondering since the application developers I know seem to prefer static linking.


Security fixes, for one. It's more convenient to rebuild one shared library instead of having to rebuild all downstream consumers.


hm, fun. That seems understandable given the status quo.

I can't help but wonder if we'd all just benefit from better build systems, so that "rebuilding all downstream consumers" wouldn't be hard.


The just leave it out of your distribution's repo. If it is a static binary then it is already easy to distribute so no one needs the repo.


> If the people who run your software aren't updating it you're screwed anyway.

They're screwed. Hopefully you aren't.

Hopefully.


containers aren't for binaries/reproducible builds. Actually they're quite bad for it and make caching hard.


The problem is that you need to keep track of all the binaries on your system which have embedded code copies.


This is a solved problem, its why apt and yum and all the other package managers exist. And/or use something like Ansible.


Maybe for software that runs on computers that you manage. But what about end-user software?


Do you think it's easier for end-users to update Python packages on their own, or just install the latest version of the app that they know about?


Since either can be automated in the distributed software itself, I'd say they are both equally easy for the end-user, if the developer really cares.


For Linux, it's easier to just perform update. For half-developed OS'es, it's easier to download installer, then Next, Next, Agree, Next, No, Next, wait, Finish.


The irony being that in the fully developed OS I experience:

- Outdated packages

- Having to build from source when the package is not in the repos

- Failed builds for no good reason

- Unability to have many versions of that same packages

- having to install log(n) build tools if I want to build n packages

- etc

while in half-developed OS'es everything just works (application-wise, at least)


Isn't that still broken if code is dynamically linked? If you don't control the end-user platform, you're out of luck anyway; static builds neither help nor hurt.


You can pre-build the static bin for the target platforms, you know exactly what are the bits in the blob on the user's box, and you can ship a binary diff and do incremental evergreen auto-updates. Probably faster and less brittle than testing/shipping and keeping Python up to date on the user's box.


But this doesn't necessarily work in the "loose binding" world either - because people pin their dependencies to a specific version to avoid breakage.

There's a fundamental unresolvable tension in automatic update systems between "not updating breaks things" and "updating breaks things"; you cannot solve for both at once and either one will get users mad at you, although not upgrading at least gives a pushback argument. See how all this has played out with Windows and Mac updates, for example.


Pinning dependencies to specific version is also bad - that's lazy engineering & instant technical debt.

You really should check what APIs your dependencies provide and how stable that API is, then set minimal dependency version. If you set the dependency to a specific version, you are creating a headache for anyone trying to use your software later on (by potentially forcing them to use oudated and insecure software) and for distro maintainers trying to keep distro software up to date and secure.

What if the dependency is so unstable that you have to pin the version or even use custom patched version? Well, thenaybe its not somethong you should be depending on & rather use something with less features but more stable. Othervise you are really being unresponsible - using quick convenience at the cost of long term usability of your software.


I'm not sure exactly what form of dependency pinning you are hating on - but if you are suggesting we should ditch dep-pinning altogether, I have to totally disagree. It's never a good experience to wake up one day and see that your code that worked yesterday, no longer does; or worse, that it works for you but other people start saying otherwise. Failing to pin deps (e.g. with a lockfile) makes it rather easy for these bugs to slip in - it's just one accidental backwards-incompatible change in a transitive dependency of yours away, and good luck figuring out how to reproduce the problem reliably.


I should have mentioned that I'm seeing this from the point of view of a distro maintainer involved in Fedora.

The thing is that in general you don't want to have many version of libraries available in the distro in parallel as each has to be separately maintained, security patches applied, build issues fixed, etc., eating valuable maintainer time. Also in some cases libraries of different version can't coexist on a system cleanly.

Then imagine every piece of software just pins versions of their dependencies to a specific random version that happened to work at the time. To satisfy those arbitrary dependencies the distro would have to maintain all these versions at the same time, which is simply impossible resource vise, not to mention incredibly wasteful (both in maintainer time & system resources).

As for stuff breaking if you don't pin dependency version - well, distros have mechanisms to handle that. For example for Fedora, there is a stable release very 6 months & stable releases are not expected to get major changes in libraries, just bug fixes and smaller enhancements.

And at the same time there is a rolling version of Fedora called Rawhide, where all the latest package versions land and where integration issues are addressed. So any breakage would happen on Rawhide and be addressed by maintainers (of the library/software affected or both) long before a new stable release is cut from Rawhide and users will actually use it.

For an example I'm maintaining the PyOtherSide Qt 5/Python bindings on Fedora. A while ago the build failed due to Python being updated to 3.9. I reported the issue upstream, which quickly fixed it and I've built an updated version in Rawhide. All this long before a stable Fedora version will get Python 3.9, but I can be sure that when this happens, all will work fine.


Yes - although from the developer perspective, they want to work cross-distro without having to test a big compatibility matrix, and generally use the language's package manager (which hopefully behaves the same across all platforms) rather than the distro's one.

Especially with Python, and especially with the 2/3 split, people got used to assuming that the distro version was something broken to work around (e.g. Redhat, OSX), and that all "real" work happened in one's local language-specific package cache or venv.

I'm increasingly of the opinion that it's a mistake for distros to ship Python or Ruby packages in their distro-specific package format, but I can also see that's going to be a holy war.


the compatibility matrix is exactly what I'm wondering about too. It's far easier to test against a single version of a dependency and deploy against it too.

I can't help but wonder if there's a better way that could scale to open source. I personally like monorepo-based development a lot - where you have one version of every library for the whole repo, and a total ordering on changesets (and a strong test suite to catch regressions). But organizing open source into a monorepo or even a virtual-monorepo seems tricky. Might be the sort of practice that can work well for companies but not so much when decision power is more distributed.


As developer I use language specific packager (bundler/gems) and I know it well.

As Linux user who sometimes build packages (tinker around) I need all kind of dependencies - python, ruby, perl, haskell. It is much safer and faster to use distro packages.

Breaking changes on major version is awful for any consumer. Python 2/3 story is a shame. These can't be arguments against distro packages.


So if I understand you correctly, you're asking developers to change how they work in order to make package maintainers' lives easier while providing no benefit to either developer or end user. Is that correct?


The benefit for developer is maintainers report compatibility issues before the developer hits them and in general take over a lot of the user support as well as the menial but necessary packaging tasks. For the user the benefit is they can actzally deploy the thing in a real production system (that is not the developrs laptop) with potentially clashing libraries and be reasonably sure it continues to be secure in the future by using up to date maintainer libraries, not some unstable release no longer getting any security fixes the developper pinned it to 5 years ago and never bothered to change.


On the other hand, inserting a volunteer middleman between the developer and the user cause extra unnecessary friction, delays, and has even been the cause of bugs [0], not to mention sometimes they explicitly ignore the wishes of the original developer.

[0] Remember when Debian generated predictable random numbers because a maintainer wanted valgrind to shut up?


On the other hand, stuff like the NPM leftpad issue can't easily happen as the package maintainer "middle man" usually inspects any upstream updates for general sanity. And even if it got in to Fedora for example, it would most likely break only the integration rolling release called Rawhide, where it would be quickly discovered and fixed, never reaching the overwhelming majority of users that are on Fedora stable releases.

Now in comparison people using NPM just blindly pulled random stuff directly from upstream without anyone doing any sanity checking at all - no wonder one package vanishing made the whole thing fall over, often directly in production.


Are we talking about source-code packaging for developers or binary packaging for users? Because the left-pad scenario really doesn't apply in the latter case.


By that logic 90% of javascript ecosystem should never be used in production. Maybe that's even the correct conclusion?


I think that's the correct conclusion. ;-)


There's no reason whatever binary packaging format you use can't store metadata on all of its internal dependencies. "Tracking down" vulnerable dependencies is almost trivial in this case.


And this metadata could point on the region so it could be swapped easily. Oh wait..


What’s the scenario where dynamic libraries are regularly upgraded but static binaries are not? Just seems totally non-sequitur as a reason to prefer dynamic libraries.


You mean like any normal Linux distro ? Dynamic libraries installed from packages in the repositories will get updated automatically while random static binaries downloaded from the internet or built from source will just sit there perfectly stable and unchanging and progressively more vulnerable, unless the user takes an action.


That's not static vs. dynamic though, that's "in package manager system" vs not. If package managers managed static binaries, they'd all be updated too since the package manager knows all the dependencies.


But what is the point of static compilation if maintainers update dependencies? Either dependencies defined by author or maintainers. Dynamic libraries just help to automate roll out.

And users would find outdated versions (maybe unmaintained). Current system update dependencies automatically or allow collaboration on life support (patched application, dependencies, config). It would be good to have alternative like container or virtual machine with snapshot from the days long past. But it should be clear this is not safe.


Developer who missed CVE. Not anyone has fulltime job on his project. It could result on even more shaming of open source developers (if they freeze dependencies).


But in this scenario, how is the dynamic library going to be upgraded?


* Developer who missed CVE fixed in his dependencies (if was not clear)

Em, like all other packages?


Say we have program X with dependency Y. X+Y is either dynamic or static. X can either have responsive maintainers or unresponsive maintainers. Y can either change to fix a bug or change to add a bug. (With Heartbleed, I remember our server was fine because we were on some ancient version of OpenSSL.)

Here are the scenarios:

- dynamic responsive remove bug: Positive/neutral. Team X would have done it anyway.

- dynamic unresponsive remove bug: Positive.

- dynamic responsive add bug: Negative. Team X will see the bug but only be able to passively warn users not to use Y version whatever.

- dynamic unresponsive add bug: Negative. Users will be impacted and have to get Y to fix the error.

- static responsive remove bug: Positive/neutral: Team X will incorporate the change from Y, although possibly somewhat slower (but safer).

- static unresponsive remove bug: Negative. Users will have to fork X or goad them into incorportating the fix.

- static responsive add bug: Positive. Users will not get the bad version of Y.

- static unresponsive add bug: Positive. Users will not get the bad version of Y.

Overall, dynamic is positive 1, neutral 1, negative 2, and static is positive 2, neutral 1, negative 1. Unless you can rule out Y adding bugs, static makes more sense. Dynamic is best if "unresponsive remove bug" is likely, but if X is unresponsive, maybe you should just leave X anyway.


Sorry, this is offtopic. You have asked how is it possible - I've answered. Dynamic vs static discussed elsewhere in the thread, no silver bullet. I prefer to optimize against unresponsive author [0]. Chose your own distro

[0] https://news.ycombinator.com/item?id=23458020


This issue has different risk profiles based on the target user base.

If the install is on a consumer machine for regular usage then the right answer is shared libraries for the machine. It adds a lot of complexity for the packagers for that OS but you get a lot of safety for that consumer.

If the install is an inhouse app deploying to servers a company controls/rents whatever then the right answer is probably a static application. Consumers are more likely to want to chase the latest version of everything. Company developed software is far less likely to want that. The problem then becomes less about packaging and more about deploying fixes quickly. A static binary that is rebuilt, tested, and deployed is going to be a smoother path to fixing that error than figuring out how to deploy your new shared library and avoiding any issues in our production environment caused by clashes to that library. This is made more problematic by the likelihood that you will need multiple versions of that shared library with the fix to meet the needs of applications that can not yet be upgraded. Static binaries make things easier in this scenario and thus quicker to resolve.


I mean, containers are basically just that and people seem to like them just fine.


People seem to like them just fine != People are patching CVEs


That's a question of process and tooling. Plenty of software, OSS and proprietary, which scans containers for vulnerabilities and sends out alerts. There's also dependabot that automatically creates PRs when dependencies get new versions.

Sure people may not use the tools they have but then again people also don't upgrade their OS packages or maven packages. So for those people packaging everything doesn't hurt any more since they've got rampant CVEs already.


Exactly, container is a way to package all these disorganised, chaotic snowflake languages into something you can actually run in a standard way. It full fills the function of an executable, but at a higher level of abstraction. We've come full circle again.


Containers solve a different problem.


Some ecosystems allow you to do that, no?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: