The Gitea container registry serves anonymous pull tokens over the public
internet. No credentials are needed to enumerate and download any spikerj/* package — including the .NET service images and the ~40 GB
artpipe model-env images.
That token then reads manifests and blobs. Verified end-to-end on the LAN
path with no credentials present (~/.docker/config.json does not exist for
the account used) — manifest fetched, 29 layers enumerated, and a multi-GB
blob download started.
What is exposed
Every spikerj/* container image: the .NET service binaries
(spikersoft-api, spikersoft-artpipe-processor, every worker), which are
trivially decompilable.
The artpipe model-env images (~40 GB each).
Image history/layer metadata, which leaks build arguments and file layout.
Not yet assessed: whether any image layer contains baked credentials. Worth
checking, given ARTPIPE_S3_SECRET_KEY and friends are forwarded at deploy time
— those come from OpenBao at runtime and should not be in a layer, but the docker history of these images has not been audited.
Fix
Gitea package visibility, not Traefik: packages inherit the owning user/org
visibility. Make the spikerj packages private so /v2/token refuses to issue
an anonymous pull scope, and confirm CI still authenticates (it logs in with DOCKER_PASSWORD from OpenBao already, so it should be unaffected).
Then re-run the reproduction above and confirm the token request is rejected.
Notes
Found while diagnosing #856 — the anonymous token is what let the registry
transfer path be measured directly, which is how the Traefik/Jetson bottleneck
was identified. Filed separately because it is a security issue in its own
right and should not wait on that work.
## Summary
The Gitea container registry serves **anonymous pull tokens over the public
internet**. No credentials are needed to enumerate and download any
`spikerj/*` package — including the .NET service images and the ~40 GB
artpipe model-env images.
## Reproduction (from outside the LAN path)
`git.spikersoft.com` is publicly resolvable:
```
$ dig +short @1.1.1.1 git.spikersoft.com
spikersoft.com.
204.197.150.99
```
Forcing the public address and supplying no credentials:
```
$ curl --resolve git.spikersoft.com:443:204.197.150.99 https://git.spikersoft.com/v2/
HTTP 401 # registry API responding
$ curl --resolve git.spikersoft.com:443:204.197.150.99 \
"https://git.spikersoft.com/v2/token?scope=repository%3Aspikerj%2Fartpipe-model-env-prodstages%3Apull&service=container_registry"
{"token":"..."} # anonymous pull token ISSUED
```
That token then reads manifests and blobs. Verified end-to-end on the LAN
path with no credentials present (`~/.docker/config.json` does not exist for
the account used) — manifest fetched, 29 layers enumerated, and a multi-GB
blob download started.
## What is exposed
- Every `spikerj/*` container image: the .NET service binaries
(`spikersoft-api`, `spikersoft-artpipe-processor`, every worker), which are
trivially decompilable.
- The artpipe model-env images (~40 GB each).
- Image history/layer metadata, which leaks build arguments and file layout.
**Not yet assessed:** whether any image layer contains baked credentials. Worth
checking, given `ARTPIPE_S3_SECRET_KEY` and friends are forwarded at deploy time
— those come from OpenBao at runtime and should not be in a layer, but the
`docker history` of these images has not been audited.
## Fix
Gitea package visibility, not Traefik: packages inherit the owning user/org
visibility. Make the `spikerj` packages private so `/v2/token` refuses to issue
an anonymous pull scope, and confirm CI still authenticates (it logs in with
`DOCKER_PASSWORD` from OpenBao already, so it should be unaffected).
Then re-run the reproduction above and confirm the token request is rejected.
## Notes
Found while diagnosing #856 — the anonymous token is what let the registry
transfer path be measured directly, which is how the Traefik/Jetson bottleneck
was identified. Filed separately because it is a security issue in its own
right and should not wait on that work.
Re-ran this ticket's own reproduction just now (2026-07-29 ~16:12Z). Still vulnerable — no code change needed to confirm, the live check settles it.
Ran exactly the two steps from the body, forcing the public address with no credentials present:
$ curl --resolve git.spikersoft.com:443:204.197.150.99 https://git.spikersoft.com/v2/
HTTP 401 # registry API responding, as before
$ curl --resolve git.spikersoft.com:443:204.197.150.99 \
"https://git.spikersoft.com/v2/token?scope=repository%3Aspikerj%2Fartpipe-model-env-prodstages%3Apull&service=container_registry"
>>> ANONYMOUS PULL TOKEN ISSUED (188 chars)
(I did not print or retain the token value, and did not proceed to fetch manifests or blobs — the token issuance alone is the verdict.)
So the fix — making the spikerj packages private so /v2/token refuses an anonymous pull scope — has not been applied. Three days on from filing, every spikerj/* image is still anonymously pullable from the public internet: the .NET service binaries (trivially decompilable) and the ~40 GB artpipe model-env images.
I flagged in an earlier sweep that git can't prove this either way, since the fix is a Gitea instance-level visibility setting rather than repo content. That's still true — but the ticket helpfully supplies a definitive external check, and it now has an answer.
The unassessed item in the body is worth escalating, given what other audits turned up today. The ticket notes that whether any image layer contains baked credentials "has not been audited." That matters more than it did on 2026-07-26, because #633 now tracks six sets of committed plaintext credentials — four in backend appsettings*.json (Gitea account password, InfluxDB admin token, Keycloak client secret, RabbitMQ dockerUser/dockerPassword) plus five literals I found today in mailserver/docker-stack.yml. If any of those values were present in the build context when these images were made, they are now in publicly-pullable layers, and rotation becomes urgent rather than housekeeping.
Suggested order:
Flip spikerj package visibility to private; confirm CI still authenticates (it already logs in with DOCKER_PASSWORD from OpenBao, so it should be unaffected).
Re-run the two commands above — expect the token request to be rejected.
Then audit docker history on the service images for baked credentials, and treat anything found as compromised.
**Re-ran this ticket's own reproduction just now (2026-07-29 ~16:12Z). Still vulnerable — no code change needed to confirm, the live check settles it.**
Ran exactly the two steps from the body, forcing the public address with no credentials present:
```
$ curl --resolve git.spikersoft.com:443:204.197.150.99 https://git.spikersoft.com/v2/
HTTP 401 # registry API responding, as before
$ curl --resolve git.spikersoft.com:443:204.197.150.99 \
"https://git.spikersoft.com/v2/token?scope=repository%3Aspikerj%2Fartpipe-model-env-prodstages%3Apull&service=container_registry"
>>> ANONYMOUS PULL TOKEN ISSUED (188 chars)
```
(I did not print or retain the token value, and did not proceed to fetch manifests or blobs — the token issuance alone is the verdict.)
So the fix — making the `spikerj` packages private so `/v2/token` refuses an anonymous pull scope — **has not been applied**. Three days on from filing, every `spikerj/*` image is still anonymously pullable from the public internet: the .NET service binaries (trivially decompilable) and the ~40 GB artpipe model-env images.
I flagged in an earlier sweep that git can't prove this either way, since the fix is a Gitea instance-level visibility setting rather than repo content. That's still true — but the ticket helpfully supplies a definitive external check, and it now has an answer.
**The unassessed item in the body is worth escalating**, given what other audits turned up today. The ticket notes that whether any image layer contains baked credentials "has not been audited." That matters more than it did on 2026-07-26, because **#633** now tracks **six** sets of committed plaintext credentials — four in backend `appsettings*.json` (Gitea account password, InfluxDB admin token, Keycloak client secret, RabbitMQ `dockerUser`/`dockerPassword`) plus five literals I found today in `mailserver/docker-stack.yml`. If any of those values were present in the build context when these images were made, they are now in publicly-pullable layers, and rotation becomes urgent rather than housekeeping.
**Suggested order:**
1. Flip `spikerj` package visibility to private; confirm CI still authenticates (it already logs in with `DOCKER_PASSWORD` from OpenBao, so it should be unaffected).
2. Re-run the two commands above — expect the token request to be rejected.
3. Then audit `docker history` on the service images for baked credentials, and treat anything found as compromised.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
The Gitea container registry serves anonymous pull tokens over the public
internet. No credentials are needed to enumerate and download any
spikerj/*package — including the .NET service images and the ~40 GBartpipe model-env images.
Reproduction (from outside the LAN path)
git.spikersoft.comis publicly resolvable:Forcing the public address and supplying no credentials:
That token then reads manifests and blobs. Verified end-to-end on the LAN
path with no credentials present (
~/.docker/config.jsondoes not exist forthe account used) — manifest fetched, 29 layers enumerated, and a multi-GB
blob download started.
What is exposed
spikerj/*container image: the .NET service binaries(
spikersoft-api,spikersoft-artpipe-processor, every worker), which aretrivially decompilable.
Not yet assessed: whether any image layer contains baked credentials. Worth
checking, given
ARTPIPE_S3_SECRET_KEYand friends are forwarded at deploy time— those come from OpenBao at runtime and should not be in a layer, but the
docker historyof these images has not been audited.Fix
Gitea package visibility, not Traefik: packages inherit the owning user/org
visibility. Make the
spikerjpackages private so/v2/tokenrefuses to issuean anonymous pull scope, and confirm CI still authenticates (it logs in with
DOCKER_PASSWORDfrom OpenBao already, so it should be unaffected).Then re-run the reproduction above and confirm the token request is rejected.
Notes
Found while diagnosing #856 — the anonymous token is what let the registry
transfer path be measured directly, which is how the Traefik/Jetson bottleneck
was identified. Filed separately because it is a security issue in its own
right and should not wait on that work.
Re-ran this ticket's own reproduction just now (2026-07-29 ~16:12Z). Still vulnerable — no code change needed to confirm, the live check settles it.
Ran exactly the two steps from the body, forcing the public address with no credentials present:
(I did not print or retain the token value, and did not proceed to fetch manifests or blobs — the token issuance alone is the verdict.)
So the fix — making the
spikerjpackages private so/v2/tokenrefuses an anonymous pull scope — has not been applied. Three days on from filing, everyspikerj/*image is still anonymously pullable from the public internet: the .NET service binaries (trivially decompilable) and the ~40 GB artpipe model-env images.I flagged in an earlier sweep that git can't prove this either way, since the fix is a Gitea instance-level visibility setting rather than repo content. That's still true — but the ticket helpfully supplies a definitive external check, and it now has an answer.
The unassessed item in the body is worth escalating, given what other audits turned up today. The ticket notes that whether any image layer contains baked credentials "has not been audited." That matters more than it did on 2026-07-26, because #633 now tracks six sets of committed plaintext credentials — four in backend
appsettings*.json(Gitea account password, InfluxDB admin token, Keycloak client secret, RabbitMQdockerUser/dockerPassword) plus five literals I found today inmailserver/docker-stack.yml. If any of those values were present in the build context when these images were made, they are now in publicly-pullable layers, and rotation becomes urgent rather than housekeeping.Suggested order:
spikerjpackage visibility to private; confirm CI still authenticates (it already logs in withDOCKER_PASSWORDfrom OpenBao, so it should be unaffected).docker historyon the service images for baked credentials, and treat anything found as compromised.