Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ The proxy never uploads artifact bytes to a scanner. Each scanner is notified wi
| RPM | RHEL/Fedora | | ✓ |
| Alpine | Alpine Linux | | ✓ |
| Arch | Arch Linux | | ✓ |
| FreeBSD | FreeBSD (ports and pkgbase) | | ✓ |
| Chef Supermarket | Chef | | ✓ |
| Generic | Any | | ✓ |
| Git | Any | | ✓ |
Expand Down Expand Up @@ -777,6 +778,62 @@ Avoid `mirror.archlinuxarm.org` as the upstream. It redirects each request to a
changing regional mirror over plain HTTP, so the database and the packages can come
from mirrors with a different sync state.

### FreeBSD / pkg

Override the repositories in `/usr/local/etc/pkg/repos/FreeBSD.conf`. Settings
there are merged with `/etc/pkg/FreeBSD.conf`, so only the URL and mirror type
change and the signature settings stay. The default repository name `freebsd`
proxies the official mirror (`https://pkg.freebsd.org`). On FreeBSD 15:

```
FreeBSD-ports: {
url: "http://localhost:8080/freebsd/freebsd/${ABI}/quarterly",
mirror_type: "none"
}
FreeBSD-ports-kmods: {
url: "http://localhost:8080/freebsd/freebsd/${ABI}/kmods_quarterly_${VERSION_MINOR}",
mirror_type: "none"
}
FreeBSD-base: {
url: "http://localhost:8080/freebsd/freebsd/${ABI}/base_release_${VERSION_MINOR}",
mirror_type: "none"
}
```

FreeBSD 14 has a single repository called `FreeBSD`:

```
FreeBSD: {
url: "http://localhost:8080/freebsd/freebsd/${ABI}/quarterly",
mirror_type: "none"
}
```

Then:

```bash
pkg update
```

Keep the path after `${ABI}` that `pkg -vv` shows for each repository
(`quarterly`, `latest`, `base_latest`, ...) and replace only the host part.

The repository catalogues (`meta.conf`, `packagesite.pkg`, `data.pkg`) carry
their signature inside the archive and are served byte-for-byte unchanged
through the metadata cache (`metadata_ttl`, stale fallback). Enable
`cache_metadata` so `pkg update` keeps working while the upstream is down.
Packages under `Hashed/` have their content hash in the file name, so they are
stored in the shared artifact cache as `pkg:bsd/freebsd/{name}@{version}` and
remain available when the upstream is unreachable.

Other files are passed through uncached. That includes the bootstrap package
under `Latest/`, whose signature file is rewritten with every repository build,
and package files without a hash in their name, which can be rebuilt under the
same name. The denylist still applies to unhashed package files. Since only
cached artifacts can be scanned, both kinds of package file are refused while
a scanner covers `freebsd`; use repositories built with `pkg repo --hash` and
bootstrap pkg without the proxy.

### GitHub Releases / mise (aqua backend)

Configure named generic upstreams:
Expand Down Expand Up @@ -1171,6 +1228,7 @@ Recently cached:
| `GET /v2/homebrew/core/*` | Homebrew core bottle manifests and blobs from GHCR |
| `GET /apk/{repository}/*` | Alpine APK repository protocol |
| `GET /arch/{repository}/*` | Arch Linux pacman repository protocol |
| `GET /freebsd/{repository}/*` | FreeBSD pkg repository protocol |
| `GET /generic/{name}/*` | Generic HTTP download proxy (GitHub release assets, mise/aqua) |
| `GET /git/{name}/{repo}/info/refs`, `POST /git/{name}/{repo}/git-upload-pack` | Git smart HTTP protocol, read-only |
| `GET /debian/*` | Debian/APT repository protocol (main archive) |
Expand Down
8 changes: 8 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,14 @@ upstream:
# archlinux: "https://geo.mirror.pkgbuild.com"
# archlinuxarm: "https://<region>.mirror.archlinuxarm.org"

# Named FreeBSD pkg mirrors (used by /freebsd/{name}/), for ports and
# pkgbase repositories alike.
# Defaults to {"freebsd": "https://pkg.freebsd.org"} when empty;
# configuring any entry replaces that default.
# freebsd:
# freebsd: "https://pkg.freebsd.org"
# poudriere: "https://packages.example.com"

# Named generic HTTP upstreams (used by /generic/{name}/). The remaining
# request path and query are appended to the upstream URL. GitHub release
# assets ({owner}/{repo}/releases/download/{tag}/{asset}) are cached
Expand Down
27 changes: 27 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ The package rule covers every release and asset in that repository, but each cac
| `conda` | yes | `pkg:conda/conda-forge/numpy`, with the channel as the namespace; the package name's case doesn't matter. A key without a channel is refused. |
| `cran` | yes | `pkg:cran/ggplot2`, the package name as published, case included (`pkg:cran/Rcpp`). A rule covers source and binary builds alike. |
| `deb` | yes | `pkg:deb/curl`; case doesn't matter. A vendor namespace such as `pkg:deb/debian/curl` is accepted and dropped, so a rule covers the package from every configured archive. Debian and Ubuntu archives only keep the current version per suite, so an evicted older one can't be fetched again. |
| `freebsd` | yes | `pkg:bsd/freebsd/zsh`, the package name as published, case included (`pkg:bsd/freebsd/FreeBSD-acct`). A rule covers the package in every configured repository and ABI. Only packages under `Hashed/` are cached, and mirrors keep only the current build, so an evicted older one can't be fetched again. |
| `gem` | yes | `pkg:gem/rails`, the gem name as published, case included. |
| `generic` | yes, explicit rules only | `pkg:generic/owner%2Frepo` for a GitHub Release repository such as `jqlang/jq`. The ecosystem rule covers all cached version-pinned generic downloads, including GitHub Release assets and configured `generic_artifacts`. The global `default` never applies; metadata/API responses are not artifact retention candidates. |
| `golang` | yes | `pkg:golang/github.com/stretchr/testify`, the module path as in `go.mod`; case doesn't matter. A major version like `/v2` (or `.v3` on gopkg.in) is a module of its own and needs its own rule. The `!`-escaped form from proxy URLs and the module cache (`github.com/!azure/...`) is refused. |
Expand Down Expand Up @@ -537,6 +538,32 @@ When `upstream.arch` is empty, a single repository named `archlinux` pointing at
that default. Arch Linux ARM has no default: pick a regional HTTPS mirror, not
the `mirror.archlinuxarm.org` redirector.

```yaml
upstream:

# Named FreeBSD pkg mirrors, served at /freebsd/{name}/.
freebsd:
freebsd: "https://pkg.freebsd.org"
```

FreeBSD pkg repositories are read-only. Requests to `/freebsd/{name}/…` are
passed to the mirror unchanged, e.g.
`/freebsd/freebsd/FreeBSD:14:amd64/quarterly/packagesite.pkg`, so ports and
pkgbase repositories and repositories built with poudriere work. Catalogues
(`meta`, `meta.conf`, `packagesite` and `data` as `.pkg`, `.txz` or `.tzst`)
are cached with the metadata TTL and served byte-for-byte unchanged, which
keeps their embedded signatures valid. Packages under `Hashed/` use the shared
artifact cache. Other package files (`.pkg`, `.txz`, `.tzst`), unhashed ones
and the bootstrap package under `Latest/`, can be rebuilt under the same name,
so they are passed through uncached after the [denylist](#version-denylist)
check on their `{name}-{version}`. Only cached artifacts are scanned, so while
a scanner covers the `freebsd` ecosystem these files are refused with
`403 Forbidden`: serve repositories built with `pkg repo --hash` (as the
official mirror is) and bootstrap pkg without the proxy. Everything else, such
as signatures and keys, is passed through uncached. When `upstream.freebsd` is empty, a single
repository named `freebsd` pointing at `https://pkg.freebsd.org` is available;
configuring any entry replaces that default.

### Go checksum database

```yaml
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/git-pkgs/gcs v0.1.0
github.com/git-pkgs/integrity v0.2.0
github.com/git-pkgs/magic v0.5.0
github.com/git-pkgs/purl v0.1.22
github.com/git-pkgs/purl v0.1.23
github.com/git-pkgs/registries v0.11.0
github.com/git-pkgs/sbom v0.1.7
github.com/git-pkgs/spdx v0.3.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ github.com/git-pkgs/magic v0.5.0 h1:Pm+4fkHR+K6kg9pwJM4hnwWUuBlkYdLmF1yIAI8lU9Q=
github.com/git-pkgs/magic v0.5.0/go.mod h1:SXOqcsNmbmpZjJZHEEWnwxprbsFvpwWgTAZrgXp4Jm4=
github.com/git-pkgs/pom v0.1.8 h1:mXCuWw6XkrKKZb6IurRZyIznYLPhV5AYg5ZO38Uj/l8=
github.com/git-pkgs/pom v0.1.8/go.mod h1:sQBeYRzjCUn77fcABPn6I/a1aBHFxm55jTmkNxYtGtw=
github.com/git-pkgs/purl v0.1.22 h1:qT9FNXT/3/RbWnQCxMHn1CFeuruMsq0RBH154oLt1Xs=
github.com/git-pkgs/purl v0.1.22/go.mod h1:l8rCfvt5iXVrFIcoQ8+vCLOaHhrtEW2id0LENP+IzMw=
github.com/git-pkgs/purl v0.1.23 h1:RG7F73S4ZnbAnB/posWf2WS4OhdG0xUbyX8UIkEUhPw=
github.com/git-pkgs/purl v0.1.23/go.mod h1:l8rCfvt5iXVrFIcoQ8+vCLOaHhrtEW2id0LENP+IzMw=
github.com/git-pkgs/registries v0.11.0 h1:kkB80NjO9UncyyxGfKmdPXWqUMNE/CkNw5Z/6gqW7Rs=
github.com/git-pkgs/registries v0.11.0/go.mod h1:bKxgdy/PwnzQXL2FnOktmSQHTKk6B85MA4VtvvYItPc=
github.com/git-pkgs/sbom v0.1.7 h1:4eboy/Yed/SC9ZcC/xT50iywHPzWbmCU7azDb5T1RbA=
Expand Down
9 changes: 9 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,12 @@ type UpstreamConfig struct {
// Default when empty: {"archlinux": "https://geo.mirror.pkgbuild.com"}.
Arch map[string]string `json:"arch" yaml:"arch"`

// FreeBSD maps repository names to FreeBSD pkg mirror base URLs, served
// at /freebsd/{name}/. The remaining request path mirrors the upstream
// layout, e.g. /freebsd/freebsd/FreeBSD:14:amd64/latest/packagesite.pkg.
// Default when empty: {"freebsd": "https://pkg.freebsd.org"}.
FreeBSD map[string]string `json:"freebsd" yaml:"freebsd"`

// OCI maps names to OCI registry URLs. Requests to a named registry use
// the repository prefix upstream/{name}/, for example
// oci://proxy.example.com/upstream/ghcr/owner/chart.
Expand Down Expand Up @@ -839,6 +845,9 @@ func (u *UpstreamConfig) Validate() error {
if err := validateNamedUpstreams("upstream.arch", u.Arch); err != nil {
return err
}
if err := validateNamedUpstreams("upstream.freebsd", u.FreeBSD); err != nil {
return err
}
if err := validateNamedUpstreams("upstream.go_sumdbs", u.GoSumDBs); err != nil {
return err
}
Expand Down
21 changes: 21 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1901,3 +1901,24 @@ func TestGenericUpstreamNamedHosted(t *testing.T) {
t.Errorf("upstream.generic named hosted with hosting on: %v, want it refused", err)
}
}

func TestValidateFreeBSDUpstreams(t *testing.T) {
tests := []struct {
name string
freebsd map[string]string
wantErr bool
}{
{"valid", map[string]string{"freebsd": "https://pkg.freebsd.org", "poudriere": "https://packages.example.com"}, false},
{"name contains path separator", map[string]string{"freebsd/ports": "https://pkg.freebsd.org"}, true},
{"URL is not absolute", map[string]string{"freebsd": "pkg.freebsd.org"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := Default()
cfg.Upstream.FreeBSD = tt.freebsd
if err := cfg.Validate(); (err != nil) != tt.wantErr {
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
Loading
Loading