Go 1.18 will embed source version information into binaries
utcc.utoronto.ca
utcc.utoronto.ca
const compile_version = gorge "git describe --tags --always --dirty"
const compile_time = gorge "date --rfc-3339=seconds"Often this arbitrary code is to do things like run "pkg-config --libs" or such to find dependencies to link against, or generate some files that shouldn't be checked into source code, but rarely does it have sandboxing or other restrictions.
Languages, like Go, which don't let a package execute arbitrary code on installation are the exception.
Go does stand in contrast to this. `go get` and `go build` cannot execute arbitrary code, and if you use those two commands to build untrusted code, in theory your machine should still remain uncompromised. They release CVEs for any issues here (such as https://github.com/golang/go/issues/29231).
Of course, if you run the code you compiled, that is unsafe, but just compiling it is supposed to be fine.
-buildinfo=false
I especially found out this is really useful for extracting information form non-stripped firmware binaries. They lay around on some S3 buckets, where cheap IoT manufacturers think it's safe to expose them online for updating purposes...
> the currently checked-out revision and a flag indicating whether edited or untracked files are present
Basically, it's including a git commit ID, which is a hash, and flags that indicate if there are local changes.
It's pretty common to include build hashes in shipping code, logs, ect.
BUILD_TIME=$(date +"%Y%m%d.%H%M%S")
CommitHash=$(git describe | cut -d- -f3)
GoVersion=$(go version | cut -c 14- | cut -d' ' -f1)
GitTag=$(git describe | cut -d- -f1)
FLAG="-X $TRG_PKG.BuildTime=$BUILD_TIME"
FLAG="$FLAG -X $TRG_PKG.CommitHash=$CommitHash"
FLAG="$FLAG -X $TRG_PKG.GoVersion=$GoVersion"
FLAG="$FLAG -X $TRG_PKG.GitTag=$GitTag"
go build -v -ldflags "$FLAG"
Part of the reason why "reproducibility" has taken so long to appear is precisely that its use cases take a long time and a lot of scale to appear. I approve of distros pursuing this goal, but haven't got a lot of use of it for the stuff I build, which is why I have a very similar build script.
Please don't reply with a simple recitation of the putative benefits of reproducibility (I'm well aware) or some wild "what if" scenario relating to my own binaries that will occur once every few centuries on average. I have had multiple real problems in my life where not knowing the exact source checkout and time something was built has been a problem. I've only really once had a problem where I would have felt better if I could have reproduced something exactly, to be sure that the source and random unattributed binary were in fact the same, and even that ended up only as a "I would have felt better." (And now that I think about it, if said unattributed binary had just said what commit it was built out of that would have been fine too....) I am much more interested in solving the problems that actually manifest than problems that someone somewhere hypothesizes I may someday have.
Once you know the exact source, what does it matter when the binary was built?
I needed this in pathological cases in the first place; think "this stuff dates from when this mature company was just a startup" kinda things. Knowing when it was built wouldn't have changed anything about the executable itself but would have helped with "which strata of old employees should I be hitting up", what other bugs were present, whether we were using a broken compiler at the time, etc.
In a perfect world, sure, none of that would matter, but.... the sibling reply from mentat is right in principle, but in practice sometimes the pathological does happen.
However, if I had to choose between knowing the exact commit and knowing the exact time, I'd certainly choose exact commit, no question. But you don't; you might as well just stick all the data in.
You don't have to choose between embedding the build time or commit information, but you do have to choose between embedding the build time or having reproducible builds. I know what I prefer, as a maintainer and consumer.
It's really nerve-wrecking (to me) when you pull down a binary from the website releases, pass it to sha256sum and gets something different than when you built the same commit from source. Usually that's because dates and times are embedded in the build, but sometimes stuff is just different depending on who built it, and that's scary.
To be fair, I'm aware that a poorly archived/copied file can lose them (especially over a network) and it's not part of `POSIX.1`. At the end of the day though I've found it's a reasonable "belt" to wear with your suspenders.
This of course could be fixed with a Nix type system (or even Nix itself), where the inputs of everything are hashed. But that’s a much more radical change than what OP is doing.
Build pipelines should be versioned as well. Correlate the application sha with the pipeline sha and you have both sources.
> And if you pull in a non-reproducible library, time will matter a lot.
I'd argue that if your application is pulling in a non-reproducible library and using it, then your application won't be reproducible until you've made the libraries you're using also reproducible. Otherwise the entire idea falls apart.
Git solves this problem by tracking all of the ingredients for you with plenty of metadata to ensure you're never baking a bad cake. Couple it with a changelog and tagging and you'll worry a lot less about bad or expired cakes.
When I see people depend on things like dates or build id's it's typically my first indication that something is wrong in the cake making process.
Reproducible releases should probably _not_ have these commit metadata built in (e.g. imagine reproducible building this 5 years in the future where you've moved off git). Though this is quite useful in debug builds where you might be have installed a number of different builds in the wild and want to be able to quickly figure out the exact version of the code a given binary was generated from.
When converting VCSs most converters will preserve the old hash in some form, e.g. by appending it to the commit message.
Github downloads however remain a problem, as they just include the branch name, not the commit hash.
buildtime="${BUILD_TIME:-$( date -u +%FT%TZ%z )}"
# Use $buildtime in your go build command.
This will only set the variable buildtime if BUILD_TIME
isn't set already. Making reproducing a build easy: just
define BUILD_TIME to be equal to the one of the binary
you want to reproduce.So it's accurate to say that "Reproducible Builds has standardized" that since they only talk about their own organization, not that it has been accepted to IETF or something like that.
Something being “standardised” requires more than just a specification and just because the specification throws the term “standard” around it doesn’t mean it has been formally standardised.
This doesn’t mean people shouldn’t still follow the specification. Plenty of specifications exist that have not been standardised. For example, there is an RFC specification for CSVs but not formal standard.
So by all means push this specification as a de facto standard but please don’t mislead people saying it has been standardised when it has not.
There’s nothing wrong with a specification or even a reference implementation but they are not themselves standards. And that’s exactly what this is: a widely respected specification.
You shouldn’t have to lie about this being standardised for it still to be relevant.
I hate arguing semantics because it’s usually a worthless argument. But the problem here is people are specifically arguing that Reproducible Builds have standardised something when they have not. If you still want to use their specification then that’s grand. You don’t need a specification standardised for it to be relevant.
However on the very specific point of whether this is a standard, I’m yet to see any proof that anyone has actually standardised the specification. All I’m reading is “but they say it is” and frankly that’s not enough to make a standard.
Of course not, and no one has claimed that this is a globally recognized standard. The only thing that has been claimed is that they have standarized it for _themselves_.
It's important to get semantics right, and it's equally important to really read and understand what's being said, and not assume something "more" is being said than what is really said.
> > Reproducible Builds has standardized "SOURCE_DATE_EPOCH" for exactly this usecase.
> It's important to get semantics right, and it's equally important to really read and understand what's being said
I’d say either you’re wrong or the GP was unclear in his statement. Either way I don’t buy your attempt to shift the responsibility of error into me.
However, a couple hundred projects heed this standard along with most distributions. Does your nitpicking really matter?
If your comment wasn’t so rude from the outset then I wouldn’t have nitpicked your statement. I think there’s some “law” people often quite about people who correct other people that applies to your post.
Yes there is
> Who would be the root standards authority to appoint other standards authorities?
You’re thinking of signing authorities.
> And why can't the Reproducible Builds project be one of them?
You could also flip The argument on its head and say why should every committee that throws a specification up be considered a standards authority when half the time specifications are incomplete, contradict other specifications or flat out don’t work.
Anyway, I’ll make this my last post on the subject. I have no interest to waste any more time explaining the difference between a standard and a specification to people who are unwilling to consider there might be a difference.
> You’re thinking of signing authorities.
No, I don't. I was asking for a list of authorities that you would consider authoritative (which invariably will be subjective, but would be interesting nonetheless).
> You could also flip The argument on its head and say why should every committee that throws a specification up be considered a standards authority when half the time specifications are incomplete, contradict other specifications or flat out don’t work.
Oh sure, specifications always suck™. But that is unfortunately quite independent from the organisation, happens all the time with ISO standards, IETF RFCs, or whatever you would consider "recognised" bodies.
> I have no interest to waste any more time explaining the difference between a standard and a specification to people who are unwilling to consider there might be a difference.
You haven't explained anything. All you have been saying in this thread is "I don't recognise the Reproducible Builds project as a standards authority, because I only believe in my personal list of authorities that I won't reveal".
The point is, a standard does not become a standard because it is published by a "competent authority". A standard becomes a standard when people in the respective field converge on using it.
And yes, there are of course certain fields where this convergence can be enforced e.g. through local law, professional associations etc. However, software development is generally not one of these fields: who will reprimand you if you write a C compiler that does not follow ISO/IEC 9899, or in this case one that does not respect SOURCE_DATE_EPOCH?
TZ=UTC git log -n1 --format='%cd' --date='format-local:%Y-%m-%dT%H:%M:%S'
There's probably a better way to do that, but the basic gist is to derive the most recent commit date.The solarwinds and other recent supply chain attack involved injecting additional code into a build environment. Reproducible builds could have been done in multiple places as an auditing mechanism to catch this.
Another place where reproducible builds is useful in industry is being able to reproduce debug symbols for something after it has shipped. Sure you can rely on always saving debug symbols in an artifact repo, but if those age out and you get a customer case... what ya gonna do?
Because that where reproducible builds started.
Projects not interested in reproducibility can/should content themselves to live in the non-reproducible set instead of expecting someone else to monkey patch for them!
The reason for this is the change in syntax for build conditionals. Go 1.17 being compatible with the old and new, but Go 1.18 will only be compatible with the new. By which point Go 1.18 will only use the new and Go 1.4 would no longer be able to build later versions of Go.
https://github.com/golang/go/issues/44505#issuecomment-94259...
This reminds me of the mcs(1) command (Manipulate Comment Section) used for some Solaris 10 binaries to display the patch level information:
# /usr/ccs/bin/mcs -p /kernel/crypto/sparcv9/aes
/kernel/crypto/sparcv9/aes:
@(#)SunOS 5.10 Generic 150400-64 Dec 2018
> People who distribute binaries widely and build them doing funny things may now have more of those funny things be somewhat visible, though. Well, until they turn these features off with those documented flags.
Yeah only if they know to check! This is pretty much the unsafe-by-default anti-pattern. You know, mysql_real_escape(), yaml.load(disable_gaping_security_holes=True) and so on.
That said, the actual information looks fairly innocuous and file paths get accidentally embedded in C++ binaries all the time with no issues.
Some people used to strip or blank out all sort of identifiers for various reasons (IIRC, primarily to complicate analysis with automated tools).
1. Go binaries are bloated already. Do they plan to compress this (and other textual info, like full type names) in the binary?
2. I saw enough of build pipelines that do not clone repositories, but do a shallow copy for a tag, and have no .git folder. How much info will be added in this case?
3. Moreover, pretty much every serious build has source generation steps (protobuf, jooq, etc - you name it). If resulting executable will have a very particular info about these, the sheer amount of information added will probably make executables considerable larger.2): It will not be added (based on information provided in the article [0]).
3): Is it valid question? By my basic understanding of article the case of adding more bytes to binary will require having the main package [0]. Also, generation (e.g. gogo-proto, go:generate) will create only business logic code in new package, no additional metadata will be added.
[0] Quote from article:
> Version control information is embedded if the go command is invoked in a directory within a Git or Mercurial repository, and the main package and its containing main module are in the same repository.
only way for this to be 'incorrect' would be if someone intentionally fucked it up or disabled it.
If you consider as a continuum then it's strictly more trustworthy than the manifest file.