[HN Gopher] Sudo-rs dependencies: when less is better
___________________________________________________________________
Sudo-rs dependencies: when less is better
Author : marbu
Score : 73 points
Date : 2024-03-26 22:06 UTC (1 days ago)
(HTM) web link (tweedegolf.nl)
(TXT) w3m dump (tweedegolf.nl)
| awoimbee wrote:
| > In the end, we chose the potential dangers of reimplementing
| command line parsing over the potential issues of including clap
|
| Have you considered using argh ? Seems like it has the upsides
| without the downsides.
| telotortium wrote:
| Don't think it's worth it. Looking at sudo's man page at
| https://linux.die.net/man/8/sudo, it looks like sudo only uses
| single-letter flags, some of which take arguments. Argh
| implements long options, built-in parsing, subcommands, and
| lots of other nice to have features that nevertheless add a lot
| of code. It's normal in traditional UNIX C programs to parse
| sudo-style flags in a handful of lines without any external
| dependencies.
| 0cf8612b2e1e wrote:
| I consider single letter flags only to be a mistake. There
| should almost always be a verbose double-dash option.
|
| I get it, most of the tooling which uses single letters is
| totally ossified due to backwards compatibility reasons.
| However, the sudors team is already breaking backwards
| compat. Now is the time to make a minor usability
| improvement.
| scbrg wrote:
| That's a bit dated. Both regular sudo (1.9.13p3) and sudo-rs
| (0.2.2) on my machine (Debian) support double dash style
| options.
| IshKebab wrote:
| I've used argh a fair bit. It has some weird ideas and
| restrictions and generally isn't nearly as good as clap. I
| would definitely recommend clap (unless you have extreme
| security concerns like this).
| Karellen wrote:
| Why not use `getopt()` which already exists in libc?
|
| (Or even `getopt_long()` if you're Linux/glibc-only? Author
| mentions not supporting Windows, but is unclear whether non-
| Linux Unices, e.g. *BSD, are intended target platforms.)
|
| https://manpages.debian.org/bookworm/manpages-dev/getopt.3.e...
| steveklabnik wrote:
| If you're trying to implement as much in Rust as possible,
| keeping an important part of the codebase in C code feels
| like the wrong decision, in my opinion.
| photonbucket wrote:
| Is there any tooling which can tell you exactly which parts of a
| crate that you actually use and produce a minimized version for
| vendoring/auditing?
| 0cf8612b2e1e wrote:
| I like this idea. Theoretically, the compiler already has the
| machinery to remove dead code. Next step could package up just
| the source you touch.
| Arnavion wrote:
| You can get that info from code coverage, via `cargo llvm-cov`
| etc, though that would require exercising all code paths into
| the deps or else you might underestimate how much of the deps
| you need to vendor. But at least if you underestimate in this
| way, you'll probably just get a compiler error rather than
| anything breaking at runtime.
| dathinab wrote:
| it's not trivial to do if you have multiple build targets and
| features
|
| i.e. you would need to vendor one version for each features x
| target tripple combination combined with cfg expansion and
| (proc) macro expansion inlining and then a static reachability
| analysis to prune all unused code (and dependencies). That
| would likely not be good enough so you probably need to have
| some runtime code coverage analysis to find "likely dead code"
| (but not statically provable dead code) and then manual choices
| to keep/remove combined with some bisecting/testing to make
| sure the choices are sane.
|
| Afik such tool doesn't exist.
|
| And it's non trivial.
|
| But it's also very viable to create it.
| jcgrillo wrote:
| I have been spitballing about this recently too [1]. The way
| I'd imagine it would work is the toolchain takes one pass over
| your crate, compiles everything, then takes another pass to
| trim all the dead code from your vendored deps. Then your git
| diff basically has your code + all the lines of all your deps
| that didn't get trimmed.
|
| There would probably need to be some more work to make it more
| user friendly, but I think it's really important that _all_ the
| code which ultimately ends up in your binary goes in the diff
| otherwise reviewers won 't actually look at it.
|
| Disclaimer: I don't know enough about compilers, or the Rust
| toolchain specifically, to know if this is even possible or
| whether it would actually help anyone in the real world. But it
| seems "naively reasonable" for some definition.
|
| [1] https://news.ycombinator.com/item?id=39828499
| sebazzz wrote:
| If they don't link libc statically it can become a problem if the
| system-installed libc is corrupt or incompatible. My Arch install
| broke once and I wasn't able to run pacman to correct it, because
| the libc installed was not compatible with pacman. If sudo
| wouldn't run, I would not even have a chance to repair the
| install without booting to live cd.
| Arnavion wrote:
| What distros are there that normally dynamically link
| everything but statically link sudo? OpenSUSE, Debian and
| Ubuntu (the distros I have on hand) do not, at least.
| paholg wrote:
| I just checked on NixOs, and ldd reports sudo is not dynamic.
| justinsaccount wrote:
| Did you check the real sudo binary, or the setuid wrapper?
|
| On my system sudo is `/run/wrappers/bin/sudo` but that is a
| setuid wrapper for `/nix/store/z008bzqrl2zc848gjhh04012jhxp
| l72q-sudo-1.9.15p5/bin/sudo` which is dynamically linked.
| dralley wrote:
| If the system-provided libc is corrupt, isn't sudo the least of
| your concern? What else is going to _work_?
| gkbrk wrote:
| > What else is going to work?
|
| Everything that was statically compiled.
| wizzwizz4 wrote:
| A statically-linked busybox, which is often enough.
| Quekid5 wrote:
| I'd invest $10 into a rescue USB stick regardless. I like
| putting Ventoy on it and having a large number of different
| distribution ISOs on there, just in case.
| jokethrowaway wrote:
| Funnily enough something similar happened to me after doing a
| partial update. Doing a partial update are not supported in
| arch linux for this very reason.
|
| sudo broke as well as many others command. ssh worked for a bit
| and then segfaulted. I edited my PATH to have a healthy version
| of libc but things kept breaking in different ways (version
| mismatches) In the end I had to use a live usb drive as I
| couldn't write to /usr/lib
| thevidel wrote:
| > including crates for platforms such as Windows, which we
| obviously would not require as a Unix utility.
|
| Probably a little less obvious now that Windows has their sudo?
|
| https://learn.microsoft.com/fr-fr/windows/sudo/
| pvg wrote:
| This also had a bigass HN discussion recently, for those
| interested https://news.ycombinator.com/item?id=39305452
| MuffinFlavored wrote:
| > We replaced it with our own argument parsing once we noticed
| that adopting clap was taking more code than doing it ourselves.
|
| I feel like it's obvious that there are two sides to this echoed
| throughout the "programming" community:
|
| 1. Don't pull a package in for what you can do yourself because
| it might have 500 dependenices for no good reason
|
| 2. Don't roll your own, use something off-the-shelf third-party
| that is actively maintained, open-source, well written/easily
| usable/fleshed out, etc.
|
| They conflict...
| cryptos wrote:
| Yeah, but that is what makes engineering interesting. You
| always have to find the right balance with your trade-offs.
| steveklabnik wrote:
| It is true that you cannot simply repeat maxims others have
| declared and expect that the job gets done well. Our profession
| (like many, many others, if not all!) requires judgement to do
| the best job. Different situations may call for different
| decisions.
| MuffinFlavored wrote:
| > Our profession (like many, many others, if not all!)
| requires judgement to do the best job.
|
| And is almost permanently open to retrospect + disagreement
| of "you shouldn't have done that this way and followed maxim
| A, you should've followed maxim B instead" and vice versa...
| :)
| steveklabnik wrote:
| Yeah, time is one of those factors that can change, and tip
| the scales one way or the other.
| jbverschoor wrote:
| They don't conflict:
|
| 1+2 -> Pull a package that is well maintained and doesn't use a
| ton of packages.
|
| The problem is the language platform / "culture", for example
| js
| MuffinFlavored wrote:
| But in this post we're seeing this "mindset" trickle to the
| Rust ecosystem, for something as "complicated" as command-
| line argument parsing
| ekidd wrote:
| Clap is a really fantastic command-line argument parser,
| especially using the "derive macro" they now include. Once
| you start dealing with git-like subcommands, and other
| complex cases, it Just Works. You get help, short and long
| options, defaults, repeated arguments, deserialization to
| custom types, etc. Essentially everything is accessible
| with a couple of lines of code.
|
| Life's too short to build all of this each time. I'm
| perfectly happy to ship 2-5 MB zip archives, which is where
| a lot of my more complicated Rust tools wind up.
| epage wrote:
| (maintainer of clap)
|
| In this situation, if they were truly concerned about
| clap, I think they should have gone down to lexopt
| (https://docs.rs/lexopt/) rather than roll their own
| mort96 wrote:
| I believe it actually has a lot more to do with the tools
| than some "mindset" in the community. You don't see this
| sort of thing in C++ really, because deep trees of
| transitive dependencies are painful. While Rust... has
| pretty much the exact same package management style as
| Node, so it doesn't surprise me that it has similar
| results.
| cmrdporcupine wrote:
| Worth pointing out that Java also has automatic
| transitive-dep package mgmt courtesy Maven & friends, or
| at least has had since the mid 00xs. And while it does
| have some amount of dependency explosion it's not this
| bad.
|
| Why? Because a) Maven central is moderated better b)
| Maven has <exclusions> to override crap if necessary. c)
| The JRE includes a much richer standard library that
| doesn't force you to rely on 3rd party deps for things
| like random number generation or HTTP calls.
| sunshowers wrote:
| Command-line parsing with good errors and help really is
| quite complicated. Clap doesn't have that many unnecessary
| features.
| cmrdporcupine wrote:
| 100% this, and I really hope we can turn the Rust culture
| ship around on this front.
|
| I've been ranting about this a lot, and getting about a 50%
| upvote vs downvote ratio :-)
| sunshowers wrote:
| This sets up some bad incentives.
|
| If someone decides part of a package is useful and extracts
| it out into another crate, then that will count as a demerit
| going by this rule, even though it should be rewarded.
| arp242 wrote:
| I don't think they necessarily conflict, and good programmers
| will pick what's appropriate for the context. The problems
| start when either path 1 or 2 gets followed dogmatically.
|
| I do have to say I'm closer to 2 than 1. Most code really isn't
| that hard to write, and just solving just your own problems
| instead of the general case can simplify things _a lot_. And
| the code is 100% under your control, which can also have its
| advantages.
|
| Some programmers seem afraid to write code. The amount of
| contortions I've seen folks pull just to re-use some package
| that wasn't really a good fit (or just wasn't a good package to
| start with) has at times been bewildering. In the most extreme
| case I replaced a badly working solution someone spent half a
| year on with something that worked well in just a week, just
| writing it from scratch (add another week or two for bugfixes,
| so that's 3 weeks).
|
| On the other hand I've also seen people NIH the silliest of
| things. In the most extreme case here they had done their own
| templating, i18n, database layer - the works. That would have
| been okay if it worked well, but all of it was ad-hoc junk.
|
| For example they did their own flag parsing for $reasons, and
| only "--flag=value" worked and not "--flag value". I spent
| quite some time being confused by this because it also didn't
| error on the wrong usage (it just did the wrong thing...) They
| gave me shit for nOt REadINg tHe dOCumENtAtiOn. Like, mate,
| I've never seen any tool where "--a=b" works but not "--a b"
| before or since, and I just used the space-variant out of habit
| without thinking. They didn't fix the flag parser, and I wasn't
| allowed to either. Didn't work long with these spanners.
|
| Nothing wrong with doing your own flag parsing necessarily; I
| did my own flag parsing for Go because I don't like the stdlib
| package and others I could find. Waste of time? Maybe. But it's
| my time to waste and at least it works well.
|
| The problem with NIH usually isn't the NIH part, but shitty
| programmers writing shitty code part.
| epage wrote:
| For some more detail on the choices that went into this, see
| https://www.reddit.com/r/rust/comments/1b92j0k/sudors_depend...
|
| For myself, I think people focus too much on "dependency count"
| and not what those dependencies represent. For example
|
| - If a subset of a package is pulled out, it is no longer a "zero
| dependency" package and some people look down on it.
|
| - Whether you use a dependency or write your own, the logic has
| to exist. The main question is if there is a difference in
| priorities.
|
| Applying those
|
| - I really wonder about their claim that using clap took more
| code than doing it themselves. I also wonder about "not using
| many features" as there are a lot of usability features in clap
| that aren't items you check off on a list. If dropping clap, it
| should have been replaced with https://docs.rs/lexopt/ rather
| than rolling their own
|
| - While rpassword had its problems, it would have been better to
| work upstream or create your own competition to upstream, rather
| than locking away the improvements within sudo-rs
|
| - I think its the right choice to keep glob. So long as it
| implements the spec of interest, bringing it in doesn't buy you
| much while keeping it external gives you the whole "many eyes"
| situation
|
| - I agree about dropping `thiserror`. It can be nice for
| prototyping or high churn code but if you write-and-forget your
| errors, you are carrying around that weight for nothing.
|
| - Its unclear why they merged all of the sudo-* packages into
| sudo-rs. I wonder if those would have been cases where they
| benefit everyone for being split out for reuse.
| nindalf wrote:
| Agreed with everything you've pointed out. There seems to be an
| implicit assumption that all dependencies are bad, even though
| it'd actually be better to refactor their own code to a crate
| under their maintenance. Almost as if they think the people
| evaluating the security of this will apply a simple heuristic
| like "if number of deps is more than x, this software is
| insecure".
| dathinab wrote:
| Agree the dependency count is mostly meaninglessly.
|
| What matters is how many vaguely defined "entities"
| (people/groups/companies) you trust and how trustable each of
| them is.
|
| Also there are not really zero dependency libraries, you always
| have some dependencies, e.g. the compiler implicitly is a
| dependency too. And so is your build system, and your languages
| standard library, and libc, etc. etc. So obsessing with "0" is
| like obsessing with "1.0" releases or abusing type systems,
| i.e. not helpful at all.
|
| Additionally you can have "crate" dependencies, but you pin (or
| even vendor) them and give them a though "supply chain risk"
| review and them keep them pinned or require a another review.
| Sure you still have to keep track of stuff like bug fixed
| yanked versions etc. But for a lot of smaller crates it's
| feasible. In difference to some other languages it's quite easy
| to do so in rust (for many crates, for larger ones which have a
| lot of functionality where you might need bug fixes, maybe even
| for security this isn't that viable, but then in most projects
| there is only a very small number of such dependencies if any
| (e.g. tokio, rustls).
| nindalf wrote:
| > how trustable each of them is
|
| I think this is the important point. They've removed clap
| (argument parsing library) as a dependency, but they continue
| to trust cargo (the rust build tool) that uses that library
| and is primarily maintained by the same developer?
|
| I feel like if they're willing to trust the developers of the
| standard library and the official compiler and build tool,
| then they might as well trust clap as well.
|
| This feels like removing dependencies just to say they did.
| But it may turn out well. Maybe there are "dependency
| skeptics" who will be won over when they see fewer
| dependencies.
| steveklabnik wrote:
| Clap ends up in your binary, Cargo does not.
| abigail95 wrote:
| if cargo was malicious it would affect the binary, which
| is the point
| nindalf wrote:
| Trusting trust Steve.
| steveklabnik wrote:
| I don't have any special insight to this decision, but
|
| > - Its unclear why they merged all of the sudo-* packages into
| sudo-rs. I wonder if those would have been cases where they
| benefit everyone for being split out for reuse.
|
| You play to your audience. If someone decides not to use sudo-
| rs because it's in multiple packages, that may be a bad reason,
| but they're still not choosing it, and that's a worse world
| than if they did.
|
| I would probably do the same thing, even though I am very much
| on the other side of this debate from the zero-dependency
| folks. The intended audience is probably much more full of
| folks who do believe that.
| ecliptik wrote:
| How does this compare to OpenBSD doas[1][2]?
|
| 1. https://man.openbsd.org/doas
|
| 2. https://cvsweb.openbsd.org/src/usr.bin/doas/
| steveklabnik wrote:
| > Our current target is to build a drop-in replacement for all
| common use cases of sudo.
|
| In my understanding, the same general comparison as doas to
| good old regular Classic (tm) sudo. They're going for
| "basically the same thing, but with some stuff removed" rather
| than a re-think of the tool.
|
| It's like harm reduction: the idea is to be able to replace
| sudo with a memory-safe version where sudo is already
| entrenched in a workflow, not to be a successor that's somehow
| better in a more abstract sense.
| dathinab wrote:
| There is also cargo vendor (which turns dependencies into path
| dependencies).
|
| Sometimes if you do security sensitive stuff it can be a good
| option to either:
|
| 1. pin dependencies and give each dependency a review for
| suspicious code
|
| 2. vendor them in some cases (e.g. applying patches, or if
| pinning seems to not be good enough for whatever reason likely
| related to offline building)
|
| If you are not a very security sensitive project but still worry
| about the supply chain then it may also be an option to
| pin/vendor some dependencies but e.g. trust `tokio`, `regex` or
| similar.
|
| E.g. not pin some more trusted dependencies but then pin some
| small utility crate from a random person which you don't want to
| write yourself and is trivial/self contained enough so that you
| likely might not care about any updates to it (still include it
| into security scans check why it was updated etc.).
___________________________________________________________________
(page generated 2024-03-27 23:01 UTC)