bioc2u distributes compiled binaries that users install as root, and asks them to trust its repository ahead of Ubuntu's own.
Running apt_setup.sh does three security-relevant things:
- Installs a signing key into
/etc/apt/trusted.gpg.d/bioc2u_key.asc. Keys there are trusted for any repository apt is configured with, not only this one. - Adds a repository served over HTTPS from OSN.
- Pins
origin mghp.osn.xsede.orgto priority 800, above Ubuntu's default 500 and r2u's 700.
The pin decides what gets installed. Anything that host publishes wins over
the distribution's and r2u's package of the same name, and the preference
matches Package: *, so that holds for any package name, not only R
packages:
Package: *
Pin: origin mghp.osn.xsede.org
Pin-Priority: 800
A package named openssl or sudo appearing in that repository would take
precedence over Ubuntu's. The repository carries only R packages (r-bioc-*,
and a few r-bioconductor-* and r-other-* on jammy), but the pin does not
restrict itself to those. See Recommended changes.
r2u pins its own repository to 700 and carries some r-bioc-* packages, built
as dependencies of CRAN packages, with version strings that sort above
bioc2u's. Ubuntu's archive carries some as well. The pin makes apt install
bioc2u's build of every r-bioc-* package, whatever the version strings say.
The documented installation is:
curl https://raw.githubusercontent.com/Bioconductor/bioc2u/devel/apt_setup.sh | sudo bash -s 3.23The user executes, as root, whatever is on the devel branch at that moment.
apt_setup.sh then fetches and runs a second script from r2u's master
branch, also as root:
curl -O https://raw.githubusercontent.com/eddelbuettel/r2u/master/inst/scripts/add_cranapt_$UBUNTU_CODENAME.sh
bash add_cranapt_$UBUNTU_CODENAME.shSo a user running the one-liner trusts two repositories' current branch state, not a reviewed release. Neither fetch is pinned to a tag or a checksum. On a shared or long-lived machine, download the script, read it, then run it.
There is no set -e. A failed curl, a failed apt install, or a failed
BiocManager::install() does not stop the script: execution carries on to the
next line with the machine in whatever state that failure left it. The exit
status is that of the last command, so it reports on the final
BiocManager::install(c('devtools')) and nothing else: a run that failed to add
the repository can still exit 0.
The security-relevant case: if the key fetch fails but the sources line is still
written, the machine ends up with a configured repository whose signature cannot
be verified. apt refuses to install from it, which is the right outcome, but
the user sees NO_PUBKEY rather than a setup failure, and may reach for
[trusted=yes].
The public key is served from the same bucket as the packages:
https://mghp.osn.xsede.org/bir190004-bucket01/bioc2u/bioc2u_key.asc
Fetched over HTTPS, so the transport is authenticated by the OSN certificate. Anyone who can write to that bucket can replace both the key and the packages it signs, which means bucket write access is equivalent to signing authority. The signature protects against a compromised mirror or a network attacker; it does not protect against a compromised bucket.
The private key lives on the operator's machine. The upload services in
build/docker-compose.yml mount ~/.gnupg (or GNUPG_DIR) read-write and
~/.aws (or AWS_DIR) into a bioc2u-builder container. upload.sh selects
the key with GPG_SIGN_KEY, and gpg asks for the passphrase inside that
container.
No CI system holds the key or the bucket credentials, and publishing needs a
person at a terminal. The upload container is inside the signing trust
boundary: whatever the builder image contains runs with the whole mounted
keyring, the passphrase and the S3 credentials. That image is the
<codename>-r-<R> tag, which the container workflow rebuilds every two days
from unpinned upstream scripts, and which a run on any branch can publish. See
Recommended changes.
The key's availability depends on the operators who hold it: keep an offline, encrypted backup. Losing it means publishing under a new key that every configured machine has to fetch again.
- Published to GHCR by a workflow using
secrets.GITHUB_TOKEN. No long-lived credential is stored in the repository. - Images run as root:
user.DockerfilesetsUSER rootexplicitly, and the builder inherits root from the r2u base. Reasonable for a build container; relevant if one is used as a runtime base. - The builder fetches and
sed-patches a script frombioconductor_docker@develat build time. Same unpinned-upstream concern as above, in a place where the result is published for others to use.
A .deb runs maintainer scripts as root at install time, and an R package can
contain compiled code that runs in-process. Installing from bioc2u is as
trustworthy as Bioconductor's review plus this pipeline, as for any binary
distribution.
None of these is applied. The first four change behaviour on machines that are already configured; the others change how images are built and packages are published.
- Narrow the pin to R packages, which keeps bioc2u's builds ahead of r2u's
and Ubuntu's and removes the ability to shadow the base system:
Package: r-* Pin: origin mghp.osn.xsede.org Pin-Priority: 800 - Scope the key to the repository with
Signed-By, instead of/etc/apt/trusted.gpg.d/, which confers system-wide trust:Combined with the narrowed pin, that closes both broad permissions described above.# /etc/apt/sources.list.d/bioc2u.sources Types: deb URIs: https://mghp.osn.xsede.org/bir190004-bucket01/bioc2u/ Suites: jammy Components: release Signed-By: /usr/share/keyrings/bioc2u-archive-keyring.asc - Fail fast in
apt_setup.shwithset -euo pipefail. - Pin r2u's setup script to a commit rather than
master, or verify a checksum of the fetched script. - Give the upload a keyring of its own. Point
GNUPG_DIRat a directory that holds only the bioc2u signing key, or a signing subkey, rather than a personal~/.gnupg, andAWS_DIRat one that holds only the bucket's credentials:install -d -m 700 /srv/bioc2u/gnupg gpg --export-secret-keys --armor <key id> | gpg --homedir /srv/bioc2u/gnupg --import
- Upload from a pinned image. Run the upload services from the builder
image a verified build used, by digest, rather than from a freshly pulled
tag. Record the digest after the build
(
docker image inspect --format '{{index .RepoDigests 0}}' <image>), and do notdocker compose pullbetween that build and the upload. - Pin
deb-s3.ensure_deb_s3inbuild/scripts/lib.shinstalls the latest gem from RubyGems on every upload, in the container that holds the key. Install a fixed version (gem install deb-s3 -v <deb-s3 version>), or bake one into the image the uploads run from. - Narrow the workflow token.
build_ghcr.yamlhas nopermissions:block, so itsGITHUB_TOKENgets the repository's default permissions, and publishing needs that default to be "Read and write" for every scope. A block in the workflow grants only what it needs, and the default can stay read-only:permissions: contents: read packages: write
- Pin third-party actions to commit SHAs. The
docker/*actions run with a token that can write packages, and a major tag such asv6is moved by its owner. Pin each to a full commit SHA, with the version as a comment:uses: docker/build-push-action@<commit sha> # v6.x.y
Do not open a public issue. Email the Bioconductor core team at bioconductorcoreteam@gmail.com, the contact published on bioconductor.org/about.