* It has been improved a little by the https and tls 1.2 parameters compared to earlier versions that didn't have these params
* You are not required to install rustup that way. There are many rustup packages available in distros today. Arch has one, nix OS has one. Debian is dragging their feet [0] but they aren't entirely opposed to the idea, so maybe something will change about this in the future.
* Even if the download process is secured, rustup doesn't really verify any signatures, it just downloads what's inside the manifest. So you can download potentially malicious code if the S3 bucket is hacked that rustup downloads the compiler from.
* Even if the entire process of obtaining rustc is secured, the standard way of using rust + cargo basically gives maintainers access to your local computer through build.rs and other methods. You can sandbox the cargo invocation, but who does that?
[0]: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=955208
The article writes this is obviously bad, but I think it needs to be expanded upon, it's not obvious.
Put it like this: if an attacker compromised Rust's website and put in their own malicious Rust installer, macOS and Windows would both show a very scary warning that's like "This is unsigned! Don't run this!" (macOS would even refuse to run it unless you did the right-click to open trick). A Linux package manager would refuse to install such a package outright, unless you --forced it. Not so with curl-to-bash, it would just silently compromise your computer.
Basically, if you believe that code signing is a good thing for security (and I think we all do), curl-to-bash is awful security practice, and you should manually review the script.
Perhaps modify the installation script, something like `curl | some-verification-tool | bash`
(PowerShell script signing is based on standard Windows codesigning certificates, but of course this hypothetical bash script signature verifier can use GPG keys instead.)
- Store the install script on www.xyz.com/install.sh
- Store the hash of the install script on www.abc.com/hash.dat
Then in the installation command:
- curl the install script from www.xyz.com/install.sh
- hash it
- curl the hash from www.abc.com/hash.dat
- compare the computed hash with the "curl'd hash"
- only if they match, pipe www.xyz.com/install.sh to bash
In that case, a malicious actor would need to hack at least two locations to be successful.
But signing is largely a moot point as people will follow the instructions on the website where a clever attacker would simply create their own cryptographic keys sign it and provide instructions for adding it to your keychain to the compromised website. Public key crypto does not solve establishing trust for an actor. It can only do so through delegation which as we have seen with the deprecation of EV certificates (CAs no longer being trusted to establish the identity of legal persons) is not something that is easy to get right.
Certainly it is better to trust the maintainers of your operating system/package manager than a random website. However they only sign things where they have done due-diligence on the project, which is a slow process, too slow for projects with faster release cycle such as Docker or Rust.
Public crypto can help carry the established trust. Say you track some developer's work and the dev behaves consistently in a trustworthy manner, his signing key can be used to help carry this trust forward, so that after a while you don't need to keep checking and just rely on the assumption that dev is probably honest going forward + crypto to verify you're really using this developer's output.
No need for delegation. Delegation doesn't solve trust anyway, only identity verification.
There is no reason in principle why a shell could not implement digital signatures. bash could have a "--require-signature" option where it looks for a GPG signature appended to the end of the script, and refuses to run the script if the signature is missing or the signer is untrusted.
(No idea if the Bash maintainers would be willing to accept a patch adding such a feature if someone were to write one; but, signed scripts is something supported by PowerShell, although most people's experience with it is limited to turning off the default setting which requires all scripts to be signed.)
A related question – what's the difference between downloading an unsigned script over HTTPS versus downloading a signed script (whether over HTTPS or just plain HTTP)? Ideally, the code signing is done offline, so an attacker that compromises the HTTPS server gets the HTTPS private key but not the code signing private key. However, I suspect that ideal often isn't actually obtained; offline code signing can be a pain for CI. If someone has a CI pipeline which signs the code and then deploys it to the HTTPS server, then all an attacker has to do is compromise access to that CI pipeline and neither HTTPS nor code signing will stop them.
They currently don't, though, which is the point :)
The reason in principle this might not work is that different distros and os's trust different keys. Fedora trusts different keys than Ubuntu, than Arch, than macOS, etc.
> Ideally, the code signing is done offline, so an attacker that compromises the HTTPS server gets the HTTPS private key but not the code signing private key.
I don't think this is necessarily the case. Even if you gain access to the server, HTTPS keys are usually stored in such a way that only root can read them, right? So if you only compromised a non-root account, you wouldn't necessarily be able to read them (I might be wrong about this, I don't do this kind of thing professionally).
Also, you could imagine that the script is not stored as a file, but in a database or something, and then you could compromise it with SQL injection or a similar technique. That would allow you to change the script without any access to HTTPS keys.
> However, I suspect that ideal often isn't actually obtained; offline code signing can be a pain for CI.
You're probably right about this, but this is a great example of why you shouldn't use the same key for both code signing and HTTPS.
Every distro has a keystore / certificate store to which root can add whatever keys/certificates they like. The only difference is in which keys/certificates are present OOTB.
> I don't think this is necessarily the case. Even if you gain access to the server, HTTPS keys are usually stored in such a way that only root can read them, right? So if you only compromised a non-root account, you wouldn't necessarily be able to read them (I might be wrong about this, I don't do this kind of thing professionally).
The purpose of the key is to vouch for the integrity of the data. If I can change the data the key vouches at will, I've effectively compromised the key, even if I never gain access to the bytes of the key itself.
> You're probably right about this, but this is a great example of why you shouldn't use the same key for both code signing and HTTPS.
If the CI pipeline has privilege both to (1) sign stuff with the code signing key (2) upload stuff to the HTTPS server, then even if the code signing key and the HTTPS key are different keys (which is the norm), compromising the CI pipeline is enough to effectively compromise both of them.
If something goes wrong, you can’t examine the script afterwards to help figure out what happened and how to recover.
That's why in order to write Rust, you'll likely need rustup, which is packaged on way less distros than the rust compiler is. Debian for example doesn't have an official rustup package.
It seems that way, which is a big disincentive to use Rust at all.
* doesn't have subslice patterns * has `Error::description`, which has since been deprecated * doesn't have x86 CPU feature detection * lacks fix for unsound typecasts between integers and floats * lacks fix for incomplete constant propagation * doesn't let you use conditionals, loops, match, $$, or || in constant fns, or cast arrays to slices in constant fns * has less-helpful error messages and backtraces * doesn't let you implement traits on arrays with lengths >32 * has worse type inference, this (valid since 1.43.0) code doesn't compile because 0.0 and &0.0 are f64s: let n: f32 = 0.0 + &0.0; * requires you to import standard libraries to use predefined numeric constants (like MAXINT or NaN) * doesn't support Control Flow Guard on Windows * doesn't generate documentation links from classpaths (you have to write relative file paths instead)
and numerous libraries being pre-stable.
For code that you're writing yourself, not having these features is mostly just a quality-of-life issue: you'll have to do things that you wouldn't have to do if you had the latest compiler. For code that you're collaborating on, having an 10-months-older compiler can be a showstopper: the code might just not compile at all, and very likely the only fix will be to uninstall your system-packaged rustc and install the version from rustup.
Contrast this with C/C++ development, where your system's pre-packaged compilers are much more likely to be sufficiently up-to-date. These languages are more mature than Rust: they change slower, and new features are adopted by programmers more gradually. For example, the Linux kernel only requires GCC 4.9 [1], which was released in 2013. Debian stable supplies GCC 8.3, which will probably be able to compile unmodified kernel source code for many years without being updated.
[1] https://www.kernel.org/doc/html/latest/process/changes.html
I just don't really see this as a problem - Rust really isn't that much of a moving target, and the benefit is there is one solution to the problem: use Rustup to manage your Rust installation.
That is significantly more robust than "use your package manager."