Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rust-lang build not reliably reproducible (relying on pre-compiled binaries, insufficient checksum checks) #3

Open
autobakterie opened this issue May 20, 2022 · 16 comments · May be fixed by #11

Comments

@autobakterie
Copy link
Contributor

I see some problems in the way the Rust toolchain is obtained by the rust-lang package. Description of the problems in form of a continued #2 conversation follows:

Believe me, you don't want to build Rust compiler from scratch. It takes ages :) I think that downloading Rust binaries is fine in this case.

Even if I might not want to (I think I believe you), it is the way how other parts of the OpenWrt toolchain (GCC, ...) are made, it is transparent and would be really nice to have.

It would not be so bad if the build time was the only issue. We are already building whole package feeds at each release, so the time added for Rust toolchain build probably wouldn't hurt that much and/or would be worth the possibility of running Rust software easily on Turris OS and OpenWrt routers.

Nevertheless, downloading the binaries might be an acceptable temporary solution as long as their integrity is reliably checked.

Adding a thread related to the current state of an effort to add Rust support to the OpenWrt build system for reference: openwrt/packages#13916

We fetch a specific version of Rust and rustup is responsible for checking integrity of the downloaded binaries. It should be 100% deterministic.

It would be nice if rustup of a particular version guaranteed concrete checksums of consecutively downloaded binaries, but I cannot see this being the case.

According to https://rust-lang.github.io/rustup/security.html , the referenced issues ( rust-lang/rustup#2028 , ... ) and the rustup-init.sh itself even the rustup binary downloaded by rustup-init.sh is not checked to have any static checksum (the only check is done by TLS and just makes sure the binary comes from the desired server).

By determinism, I mean that the packaged software will be based on exactly the same source files each time it is built.

This can be accomplished either by the source archive (that has its SHA256 checksum fixed in an OpenWrt Makefile variable) being sufficient for the build without additional downloads or by all the downloads being reliably checked against static checksums specified in the archive.

The first option is preferred by me and my colleagues in TurrisTech because it allows for rebuilding the package at any time once its download phase was completed no matter the availability of original sources at build time. It is then possible to download sources of all the packages at one time and build them all at any other (any build can then be reproduced at any future time if the downloaded sources are archived).

The second option would at least maintain the transparency and some degree of reproducibility of the build process. Unfortunately, rustup does not provide this kind of determinism either.

I know packaging Rust properly for OpenWrt is far from an easy task, but that, unfortunately, does not make improperly packaged one and the dependent packages easy to maintain.

@operutka
Copy link
Member

I understand your motivation to have it the same way as other OpenWrt packages. I just think that you're comparing apples and oranges. Let me elaborate.

You're comparing it with GCC but the thing is that there are no official builds of GCC with all possible combinations of C libraries and other environment choices (like kernel headers etc.), so it really is necessary to build it from scratch. On the other hand, Rust, as a compiler, is always the same (regardless of the target environment) and there are official builds available + official builds of the standard library for a lot of platforms (including arm/aarch64 + MUSL).

If we compare it with other languages/runtimes that you use, for example Python and Node.js, then it also makes sense to build those from scratch because you need a runtime for the target architecture. Rust has no runtime. The only target-specific part of the Rust toolchain is the standard library and there are official builds available for every version of Rust and for many target architectures. The standard library itself (parts of it) is then statically linked with the resulting binary along with all the application dependencies downloaded by cargo.

...the time added for Rust toolchain build probably wouldn't hurt that much and/or would be worth the possibility of running Rust software easily on Turris OS and OpenWrt routers.

The Rust package from this repository can be easily used to build also other Rust applications. You don't need to build the Rust compiler from its source in order to build other apps.

It would be nice if rustup of a particular version guaranteed concrete checksums of consecutively downloaded binaries, but I cannot see this being the case.

According to https://rust-lang.github.io/rustup/security.html , the referenced issues ( rust-lang/rustup#2028 , ... ) and the rustup-init.sh itself even the rustup binary downloaded by rustup-init.sh is not checked to have any static checksum (the only check is done by TLS and just makes sure the binary comes from the desired server).

TLS does more than that. TLS also guarantees integrity of the transferred data, so as long as you trust that the remote server will give you the binaries that you asked for (which it does in this case) and as long as you trust that the server hasn't been compromised, you'll get the same binaries every time. This is pretty much the same guarantee that you have for all those dependencies downloaded via Python's pip, Node.js' npm or Rust's cargo. I guess that you don't have static checksums for all those dependencies in your Makefiles, or do you?

The first option is preferred by me and my colleagues in TurrisTech because it allows for rebuilding the package at any time once its download phase was completed no matter the availability of original sources at build time. It is then possible to download sources of all the packages at one time and build them all at any other (any build can then be reproduced at any future time if the downloaded sources are archived).

And are you really sure that downloading sources at one time and building them at another time works in your case? How about all the dependencies downloaded via language specific dependency managers (pip, npm, cargo) during the package build?

The second option would at least maintain the transparency and some degree of reproducibility of the build process. Unfortunately, rustup does not provide this kind of determinism either.

Yes, it does. As I mentioned, if you trust that the server used by rustup hasn't been compromised and that the server will give you the binaries that you ask for (which is the server's purpose), you will get the same set of binaries every time, so the only difference between downloading sources and binaries is that the first is human-readable and the second is not. :) If you don't have this trust then you're screwed because you already face the same problem with dependencies downloaded via pip and npm.

TL;DR

If you insist on building the Rust toolchain from sources, I can update the package once I find some extra time. But given the fact that there really aren't many benefits in doing it, I'd prefer investing my time into something that actually makes sense.

Please note that building Rust from sources can easily take more than 60 minutes (this was the case the last time I had to build it), so it can really be painful.

If you decide to build Rust from sources, you should also know that Rust is a bootstrapping compiler (you need a Rust compiler to build a Rust compiler), so there will always be some binaries that need to be downloaded. It works the same way as GCC (you need GCC to build GCC).

@operutka
Copy link
Member

Here is a small follow up regarding the Rust bootstrapping procedure I mentioned before: https://rustc-dev-guide.rust-lang.org/building/bootstrapping.html#stage-0

Theoretically, it should be possible to download Rust installation package from here, verify its checksum and use it for bootstrapping the build from source. But I haven't tried this before, so I'm not sure if that would be feasible. I also cannot guarantee that the build script won't try to download anything else during the build. So as you can see, you cannot avoid downloading binaries.

Anyway, this still does not solve the problem with downloading dependencies via cargo during the build of the angelcam-connector package. But if the main concern here is allowing offline builds, we could run cargo check within the Build/Prepare phase. This would force cargo to download the dependencies that would be reused during the build phase.

@Grommish
Copy link

Grommish commented Jun 7, 2022

Hi,

Some issues I ran into with rustup and OpenWrt.

By default, rust-lang uses statically linked libraries for linux_musl targets (https://github.com/rust-lang/rust/blob/master/compiler/rustc_target/src/spec/linux_musl_base.rs#L13). I turn this off in the Openwrt tuples (https://github.com/rust-lang/rust/blob/master/compiler/rustc_target/src/spec/mips64_openwrt_linux_musl.rs#L11`). I've never used Turris, but I call GNU_REAL_TARGET_NAME for the target rather than trying to play the tuple-match game. Since I'm generating the tuples anyway, it was just easier.

The arguments about using pre-compiled binaries. I've got no opinion either way how anyone can/should handle that issue. If you're interested, these are some of the arguments I tangled with when I was looking at the project at the beginning.

If you are source building everything (including the LLVM), which is how I'm doing it, it does download a pre-compiled rustc archive of the previous version for the bootstrap. The only way around this would be to have the build-bot/end-user start at the beginning of the dev tree and sequentially build out each and every version, using the last to build the next. The arguments I saw and decided on when faced with this question was that the size/storage/processor resources would, in theory, be only used once per HOST toolchain (ie: x86_64-unknown-linux-gcc) during the long and drawn out boot-strap process, but you'd have a secure toolchain for the HOST tuples for anyone to download, and going forward, that chain of custody remains intact as you use that toolchain to create the TARGET toolchains (ie: aarch64-openwrt-linux-musl or armv7-openwrt-linux-muslgnueabihf). Of course, this only really matters if you've also audited the rust-lang toolchain through every version to ensure nothing hinky is buried in deep. Since the chance of doing an audit was none (in my case), the whole building everything from the ground up also became less important (as it didn't have a solution I could implement).

The compromise was building from source (since I had to anyway for the custom tuples) and using the pre-compiled to bootstrap If a security issue was discovered, the entire thing gets rebuilt on the update for it anyway under the way I do it. Since I will be changing the PKG_SOURCE to the new HEAD for the update, it'll rebuild. Best compromise I could justify and get away with. This, of course, would trigger an automatic rebuild of any rust dependent packages.

Looking quickly at the Turris github, in theory, rustup should work for some targets, as Turris uses the REAL_GNU_TARGET_NAME of -openwrt-linux- (https://github.com/CZ-NIC/turris-os/blob/test/rules.mk#L91). As I continue to upstream my tuples, those will become available in rustup (mips64-openwrt-linux-musl already is, for example).

Turris probably also suffers from the same arm arch reporting nightmare. OpenWrt has targets that are arm, armv5tej, armv6k, and armv7 that all report as arm in the tuple. even though they are separate LLVM targets on the back-end. However you solve it, it won't be fun (or wasn't for me, at least). But, once you figure out the way for Turris to address that issue, rustup could use the upstreamed tuples.

Feel free to ping me if you need another set of eyes on an issue. I might be able to help (or at least tell you how I previously failed at it to save you time).

@operutka
Copy link
Member

operutka commented Jun 7, 2022

Hi @Grommish! Thanks for the insights.

By default, rust-lang uses statically linked libraries for linux_musl targets...

Yes, that's a known issue. It can be easily dealt with using the -C target-feature=-crt-static compiler flag (see https://doc.rust-lang.org/reference/linkage.html#static-and-dynamic-c-runtimes). There is no need for adding OpenWrt specific targets. I'm also dealing with this issue here:

"-C", "target-feature=-crt-static",

If you are source building everything (including the LLVM), which is how I'm doing it, it does download a pre-compiled rustc archive of the previous version for the bootstrap. The only way around this would be to have the build-bot/end-user start at the beginning of the dev tree and sequentially build out each and every version, using the last to build the next.

Building every version of Rust just to have a "clean" bootstrapping chain would be insane. There have been 61 versions of Rust so far + bugfix releases. But guess what! It does not end there. The very first version of Rust was written in OCaml, so you'd also have to build OCaml. OCaml is implemented in C + OCaml. I'm not sure if it's also a bootstrapping compiler but that isn't important. We can use GCC to build/bootstrap OCaml but can we trust the GCC on the build host? Why should we?! Let's bootstrap also GCC all the way from the very first version implemented probably in assembler 😄

I hope you can see how ridiculous this is. In my opinion, we can trust the official Rust releases as much as we trust the GCC on the build host. Sure, we can be paranoid and download a standalone binary release from here and verify its checksum instead of using rustup. Then we could use this binary release to bootstrap our own build of Rust. But bootstrapping Rust all the way from the very first version? That's insanity :)

The compromise was building from source (since I had to anyway for the custom tuples)...

JFYI you don't have to build Rust just because you use custom targets. You can use the build-std feature together with a custom target specification in JSON. Even though it's still nightly, it works reasonably well. I'm using it in several Buildroot-based hobby projects targeting quite obscure architectures and everything works fine so far. Of course, we could argue that using Rust nightly channel isn't a good idea for production releases, so building Rust from source would still make sense for architectures that are not available using rustup.

Looking quickly at the Turris github, in theory, rustup should work for some targets...

I'm quite sure that rustup currently covers all targets used by Turris.

Turris probably also suffers from the same arm arch reporting nightmare. OpenWrt has targets that are arm, armv5tej, armv6k, and armv7 that all report as arm in the tuple. even though they are separate LLVM targets on the back-end. However you solve it, it won't be fun (or wasn't for me, at least). But, once you figure out the way for Turris to address that issue, rustup could use the upstreamed tuples.

Yes, I'm aware of that. Unfortunately, there are more problems then just the ARM ISA versioning. OpenWrt also messes up the target ABI which is quite unfortunate. For example, the GNU target for Turris Omnia is arm-openwrt-linux-musleabi even though it's ARMv7 with hard float ABI, so the corresponding Rust target is armv7-unknown-linux-musleabihf. I'm dealing with it here:

ifeq ($(CONFIG_TARGET_PROFILE),"DEVICE_cznic_turris-omnia")

There can be exceptions for various CONFIG_TARGET_PROFILEs and there is also a fallback to $(CONFIG_ARCH)-unknown-linux-$(CONFIG_TARGET_SUFFIX). The vendor part really isn't that important since I use the -C target-feature=-crt-static option here. Of course, we could make the logic smarter in the future but I guess the current implementation should be fine for now.

@Grommish
Copy link

Grommish commented Jun 7, 2022

The way I ended up dealing with it was via https://github.com/openwrt/packages/pull/13916/files#diff-6b866d23e2208710b2de3c36ab1ec8111be9a84c88aef41ff2bc1ead81031773

This outlines the logic I use to split out target logic, it also allows me to call CodeGen -C flags for most TargetOptions when the target code is compiled. For arm in particular, I removed all the TargetOptions defaults and then call them depending on the target.

I use -Z build-std but I still ran into issues.. I still actually invoke it (https://github.com/openwrt/packages/pull/13916/files#diff-5254eff797fc41c5ae1984a03deb7c18c13b12dbae015b51fa5179330d1c46a4R29) but it doesn't do much since I'm building the target toolchains anyway. But, I did try that at one point. One of the issues that I deal with that a rustup install won't is the juggling of the HOST vs TARGET toolchains that I have to deal with.

I hope you can see how ridiculous this is. In my opinion, we can trust the official Rust releases as much as we trust the GCC on the build host. Sure, we can be paranoid and download a standalone binary release from here and verify its checksum instead of using rustup. Then we could use this binary release to bootstrap our own build of Rust. But bootstrapping Rust all the way from the very first version? That's insanity :)

Very much so! I just wanted to point out the push-back I received about pre-compiles. I didn't find it in any way feasible to build up from scratch, either, but I'm sure if someone hasn't already brought it up here, it would be sooner or later.

@operutka
Copy link
Member

operutka commented Jun 7, 2022

Cool. That's some serious work you've done. I'd be happy to switch our angelcam-connector package to the official OpenWrt Rust package once it's in the upstream. And we can also drop our rust-lang package then. I believe the current solution will work just fine until then. cc @autobakterie

@Grommish
Copy link

Grommish commented Jun 7, 2022

Cool. That's some serious work you've done. I'd be happy to switch our angelcam-connector package to the official OpenWrt Rust package once it's in the upstream. And we can also drop our rust-lang package then. I believe the current solution will work just fine until then. cc @autobakterie

Thanks.. It does work, integrates well, and has the ability to build the distribution archives, which is how I was doing it prior to just installing it to the build system directly. My biggest issue is the fact my testing device is a mips64, so while it works, I can't personally vouch for other archs.

I've been working through issues to my test packages to figure out if it's the build system, the rust install, or the package itself. Rust/Cargo applications that use ring for crypto should be marked as @!BIG_ENDIAN in the package DEPENDS, because ring using BoringSSL, which doesn't support BigEndian. In other situations, it's issues with the package itself interacting with non-x86_64 hardware. The libc crate needed a few symbols added (like O_LARGEFILE) for mips64 because it was simply a matter of no one had bothered to use mips64 target in that way prior.

As I said before, I'm not trying to influence anything being done. There's no assurances OpenWrt will ever merge rust-lang into the official build system, but it'll be there if they decide to. I would have loved to use rustup, but I couldn't get it to work with the knowledge I had at the time, so I went in the direction I could get to work. I guess it was necessary to make custom tuples, so it wasn't a total loss 😆

My goal was to have $(call RustPacakge/Cargo/Update) and $(call RustPacakge/Cargo/Compile) to "just handle it". Similarly, I handle cargo packages that use Makefile wrappers. (like suricata6)

$(call RustPackage/Cargo/Update, -p libc)
$(call RustPackage/Cargo/Compile, --features 'pcre2')

I'm not sure what angelcam-connector is, but if it's a Rust package, then I'd like to test it in the build system :) I'll test it and if I run into issues, I'll report them.

@Grommish
Copy link

Grommish commented Jun 7, 2022

488587 Jun 7 22:24 bin/packages/mips64_octeonplus/packages/angelcam-connector_0.10.1-1_mips64_octeonplus.ipk

Clarification: Builds on OpenWrt master branch using my rust-lang toolchain.

So, it builds, and helped me with an issue I hadn't been able to previously diagnose regarding duplicate definitions. I'm still not quite sure what the arrow package is for, but at least builds.

@operutka
Copy link
Member

operutka commented Jun 8, 2022

I'm still not quite sure what the arrow package is for, but at least builds.

It's a service for connecting IP cameras from your local network to Angelcam Cloud. See https://github.com/angelcam/arrow-client and https://angelcam.com if you're interested.

@Grommish
Copy link

Grommish commented Jun 8, 2022

Ah, I've got no way to test it then. Anyone willing to test the ipk output and ensures it actually works with my rust implementation, since I can't? I'm more than happy to generate a target-specific arch ipk.

Mostly I"m interested in testing the package to ensure I still don't have issues with xxxx arch.

@operutka
Copy link
Member

operutka commented Jun 8, 2022

I can test it on Turris Omnia and Turris MOX. Those are armv7-unknown-linux-musleabihf and aarch64-unknown-linux-musl.

@Grommish
Copy link

Grommish commented Jun 8, 2022

angelcam-connector_0.10.1-1_aarch64_cortex-a53.ipk.gz

aarch_cortex-a53 mvebu was the target I used since I don't have a Turris repo. Without actually cloning Turris and trying my rust PR on that codebase, it would be a chore to port over specific targets.

The above ipk was gzipd so I could send it to here. If you need a aarch64_cortex-a72 arch, I can do one of those.

@Grommish
Copy link

Grommish commented Jun 8, 2022

I went on the assumption that the original Makefile for generating the package was correctly setup and changed formatting to meet the needs of how I call things. Below is what I used.

include $(TOPDIR)/rules.mk

PKG_NAME:=angelcam-connector
PKG_LICENSE:=Apache-2.0
PKG_VERSION:=0.10.1
PKG_RELEASE:=1

PKG_BUILD_DEPENDS:=rust/host

PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/angelcam/arrow-client.git
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
PKG_MIRROR_HASH:=8651cb53d51642cac468948a0d40d2b04cec28e9ff046df254641b5a46cc8d87

include $(INCLUDE_DIR)/package.mk
include $(TOPDIR)/package/feeds/packages/rust/rustc_environment.mk

CARGO_BUILD_FLAGS += \
  TARGET_CC=$(TOOLCHAIN_DIR)/bin/$(TARGET_CC_NOCACHE) \
  TARGET_AR=$(TOOLCHAIN_DIR)/bin/$(TARGET_AR)

define Build/Compile
	$(call RustPackage/Cargo/Compile,--features 'discovery threads')
endef

define Package/angelcam-connector
  TITLE:=Angelcam Connector
  CATEGORY:=Rust Packages
  SECTION:=net
  DEPENDS:=+libpcap +libopenssl
endef

define Package/angelcam-connector/description
  Angelcam Connector
endef

define Package/angelcam-connector/postinst
#!/bin/sh
/etc/init.d/angelcam-connector enable
/etc/init.d/angelcam-connector start
endef

define Package/angelcam-connector/prerm
#!/bin/sh
/etc/init.d/angelcam-connector stop
/etc/init.d/angelcam-connector disable
endef

define Package/angelcam-connector/install
	$(INSTALL_DIR) $(1)/usr/bin
	$(INSTALL_DIR) $(1)/etc/angelcam-connector
	$(INSTALL_DIR) $(1)/usr/local/angelcam-connector
	$(INSTALL_DIR) $(1)/etc/init.d
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/target/$(RUSTC_TARGET_ARCH)/release/arrow-client \
		$(1)/usr/bin/angelcam-connector
	$(INSTALL_BIN) ./files/init-script $(1)/etc/init.d/angelcam-connector
	$(INSTALL_DATA) $(PKG_BUILD_DIR)/ca.pem $(1)/usr/local/angelcam-connector/
	$(INSTALL_DATA) $(PKG_BUILD_DIR)/rtsp-paths $(1)/etc/angelcam-connector/
	$(INSTALL_DATA) $(PKG_BUILD_DIR)/mjpeg-paths $(1)/etc/angelcam-connector/
	touch $(1)/etc/angelcam-connector/config.json
	touch $(1)/etc/angelcam-connector/config-skel.json
	chmod 600 $(1)/etc/angelcam-connector/config.json
	chmod 600 $(1)/etc/angelcam-connector/config-skel.json
endef

$(eval $(call BuildPackage,angelcam-connector))

@operutka
Copy link
Member

operutka commented Jun 9, 2022

@Grommish I can't install the package because Turris probably uses different names for some dependencies (the package depends on libpcap1 and libopenssl1.1 but these packages are available as libopenssl and libpcap in the Turris OS). But that's not the main problem here.

I was able to extract the binary from the package and test it anyway. Unfortunately, it does not work because the binary depends on incorrect dynamic linker lib. It depends on /lib/ld-linux-aarch64.so.1 but the dynamic linker lib in the Turris OS is at /lib/ld-musl-aarch64.so.1.

This is the output of ldd usr/bin/angelcam-connector:

root@turris:~/tmp# ldd usr/bin/angelcam-connector 
	/lib/ld-linux-aarch64.so.1 (0x7fb60ee000)
	libpcap.so.1 => /usr/lib/libpcap.so.1 (0x7fb60a7000)
	libssl.so.1.1 => /usr/lib/libssl.so.1.1 (0x7fb6022000)
	libcrypto.so.1.1 => /usr/lib/libcrypto.so.1.1 (0x7fb5df7000)
	libc.so => /lib/ld-linux-aarch64.so.1 (0x7fb60ee000)
	libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x7fb5dd5000)

And this is an ldd output for a different binary:

root@turris:~/tmp# ldd /usr/bin/openssl
	/lib/ld-musl-aarch64.so.1 (0x7fbe0c2000)
	libssl.so.1.1 => /usr/lib/libssl.so.1.1 (0x7fbdf8e000)
	libcrypto.so.1.1 => /usr/lib/libcrypto.so.1.1 (0x7fbdd63000)
	libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x7fbdd41000)
	libc.so => /lib/ld-musl-aarch64.so.1 (0x7fbe0c2000)

Of course, if I create a symbolic link from /lib/ld-linux-aarch64.so.1 to /lib/ld-musl-aarch64.so.1, then the binary works fine. But that's just a hack.

I had a similar issue with our rust-lang package. The problem was that when MUSL was being linked statically, the resulting binary contained path to an invalid dynamic linker lib. Adding -C target-feature=-crt-static to rust flags solved the issue.

@autobakterie
Copy link
Contributor Author

Sorry for the close and reopen ping. That was a misclick.

First to the original reply:

I understand your motivation to have it the same way as other OpenWrt packages. I just think that you're comparing apples and oranges. Let me elaborate.

You're comparing it with GCC but the thing is that there are no official builds of GCC with all possible combinations of C libraries and other environment choices (like kernel headers etc.), so it really is necessary to build it from scratch. On the other hand, Rust, as a compiler, is always the same (regardless of the target environment) and there are official builds available + official builds of the standard library for a lot of platforms (including arm/aarch64 + MUSL).

If we compare it with other languages/runtimes that you use, for example Python and Node.js, then it also makes sense to build those from scratch because you need a runtime for the target architecture. Rust has no runtime. The only target-specific part of the Rust toolchain is the standard library and there are official builds available for every version of Rust and for many target architectures. The standard library itself (parts of it) is then statically linked with the resulting binary along with all the application dependencies downloaded by cargo.

I acknowledge there are additional reasons to build other languages' toolchains from source which are not relevant to the Rust toolchain, but that doesn't mean they are the only reasons.

The Rust package from this repository can be easily used to build also other Rust applications. You don't need to build the Rust compiler from its source in order to build other apps.

Well, I certainly can build other apps using precompiled Rust toolchain. What I wanted to imply by 'easily' wasn't just the possibility of building. It was also the reliability which, as I see it, isn't really provided by the current rust-lang package 'compilation' process. It's based on binaries and not checked against static checksums at the same time. Neither is considered good practice for any OpenWrt package. I would expect a compiler toolchain package other packages should rely on to be more robust.

TLS does more than that. TLS also guarantees integrity of the transferred data, so as long as you trust that the remote server will give you the binaries that you asked for (which it does in this case) and as long as you trust that the server hasn't been compromised, you'll get the same binaries every time.

What you describe is just what I meant - relying on TLS == trusting the download server - which extends the attack surface unnecessarily.
It's standard nowadays that OpenWrt packages have concrete SHA-256 source checksums specified inside their manifests. That mostly eliminates the need to trust any download mirror. (combining this with checking GPG signatures of tarballs would be even better indeed, but we are not there yet)

This is pretty much the same guarantee that you have for all those dependencies downloaded via Python's pip, Node.js' npm or Rust's cargo. I guess that you don't have static checksums for all those dependencies in your Makefiles, or do you?

We actually do, I think, for most of them, at least. See all those *_HASH definitions in Python or Node related packages Makefiles. If you find a Makefile downloading the source without checking the hash (should be done automatically by the build system if the corresponding *_HASH variable is specified), don't hesitate to create an issue. It should be checked.

For reference:

And are you really sure that downloading sources at one time and building them at another time works in your case? How about all the dependencies downloaded via language specific dependency managers (pip, npm, cargo) during the package build?

Yes, those pip packages are problematic in this regard.

Yes, it does. As I mentioned, if you trust that the server used by rustup hasn't been compromised and that the server will give you the binaries that you ask for (which is the server's purpose), you will get the same set of binaries every time, so the only difference between downloading sources and binaries is that the first is human-readable and the second is not. :) If you don't have this trust then you're screwed because you already face the same problem with dependencies downloaded via pip and npm.

As I said - I do not trust the server; those pip and npm packages are being checked against static checksums as well.

I consider the preference of sources over binaries to be an unrelated issue - having integrity checked is important in both cases. Sources then make the build process more transparent and as we are developing a security-focused Linux distribution based on free software, maintaining maximal reasonable transparency is quite important to us.

Regarding the followup too: Building everything from source using a precompiled bootstrapping binary seems to be the way many other Linux distributions (e.g. Debian, Fedora) choose.

@operutka
Copy link
Member

Ok. Thank you for your response @autobakterie.

Let me summarize it as I see it and feel free to correct me if you see it differently.

  1. We'll download a standalone Rust installer (i.e. a binary package) from https://forge.rust-lang.org/infra/other-installation-methods.html and check hash of the downloaded archive.
  2. We'll download Rust sources from the official Git repository and check hash of the sources.
  3. We'll build our own Rust toolchain from the sources using the binaries from step 1 for bootstrap.

Regarding the dependencies downloaded by cargo - there is a checksum for each dependency in the lockfile, for example:

[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"

I assume that cargo uses it to check downloaded dependencies. So I guess that this should be enough for you, is that correct?

Since Grommish is working on the official Rust package for OpenWrt, I don't see much sense in duplicating his effort here. I'd be happy to switch the angelcam-connector package from the rust-lang package in this feed to the official OpenWrt package once it's available. So my question is if you're OK with leaving the rust-lang package in this feed to use rustup or if you want me to change it according to the description above.

@operutka operutka linked a pull request Aug 17, 2022 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants