aptly-dev / aptly

aptly - Debian repository management tool
https://www.aptly.info/
MIT License
2.58k stars 376 forks source link

Publish API returns `202 Accepted` even though GPG Signing fails #1268

Open mkasimd opened 7 months ago

mkasimd commented 7 months ago

When calling the Publish API without the Signing Options even though aptly uses an encrypted GPG key, signing RELEASE file and publishing obviously fails, but the API returns 202 Accepted

Detailed Description

When using a GPG key that was generated with a passphrase, calling the Publish API without Signing Options unexpectedly return 202 Accepted instead of 4xx:

$ curl -X POST -H 'Content-Type: application/json' --data '{"SourceKind": "local", "Sources": [{"Name": "test-repo"}], "Architectures": ["i386", "amd64"], "Distribution": "bookworm"}' http://localhost:8080/api/publish/:. -v
* Connected to localhost (127.0.0.1) port 8080
> POST /api/publish/:. HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.6.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 123
> 
< HTTP/1.1 202 Accepted
< Content-Type: application/json; charset=utf-8
< Date: Thu, 11 Apr 2024 14:07:59 GMT
< Content-Length: 52
< 
* Connection #0 to host localhost left intact
{"Name":"Publish local: test-repo","ID":5,"State":0}

Meanwhile the logs on the server side look like this:

[GIN] 2024/04/11 - 13:28:12 | 202 |   11.431911ms |      172.17.0.1 | POST     "/api/publish/:."
Signing file 'Release' with gpg, please enter your passphrase when prompted:
gpg: signing failed: Inappropriate ioctl for device
gpg: signing failed: Inappropriate ioctl for device

and a simple GET request to list published repos shows that the repo was not published.

Context

When utilizing the REST API to build own applications around it, it's common to assume a 2xx response code usually means that the API call was fine. There is no clear error message visible to the caller. Instead, it's only visible within the server only. It's best practice to return a status code indicating an issue.

Possible Implementation

Modify returned HTTP status code to e.g. 400 Bad Request

Your Environment

Docker with Dockerfile (excerpt):

FROM debian:bookworm

RUN apt-get update && \
    apt-get install -y\
      ca-certificates \
      gnupg2 curl procps \
      graphviz && \
    curl -fSSL https://www.aptly.info/pubkey.txt | gpg --dearmor -o /usr/share/keyrings/aptly.gpg && \
    echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/aptly.gpg] http://repo.aptly.info/ squeeze main" | tee /etc/apt/sources.list.d/aptly.list && \ 
    apt-get update && apt-get install -y aptly
$ curl http://localhost:8080/api/version                                                                                                                                 ✔ 
{"Version":"1.5.0+ds1-1+b4"}
neolynx commented 5 months ago

this might be fixed with https://github.com/aptly-dev/aptly/pull/1271

are you using async mode for the API calls (_async=true) ?

could you check with latest ?

deb http://repo.aptly.info/nightly-DIST DIST main

Where DIST needs to be changed to the distribution: buster / bullseye / bookworm (Debian) or focal / jammy (Ubuntu).

mkasimd commented 4 months ago

Sorry for late reply. Just checked it, but doesn't seem to have fixed it. I'll dump some details below for context in hopes that it helps:

Details **aptly version** ``` $ cat /etc/apt/sources.list.d/aptly.list deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/aptly.gpg] http://repo.aptly.info/nightly-bookworm bookworm main $ aptly version aptly version: 1.5.0+ds1-1+b4 ``` **aptly publish** ``` $ curl -X POST -H 'Content-Type: application/json' --data '{"SourceKind": "local", "Sources": [{"Name": "test-repo"}], "Architectures": ["i386", "amd64"], "Distribution": "bookworm"}' http://localhost:8080/api/publish/:. -v * Connected to localhost (127.0.0.1) port 8080 > POST /api/publish/:. HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/8.8.0 > Accept: */* > Content-Type: application/json > Content-Length: 123 > * upload completely sent off: 123 bytes < HTTP/1.1 202 Accepted < Content-Type: application/json; charset=utf-8 < Date: Mon, 15 Jul 2024 12:16:54 GMT < Content-Length: 52 < * Connection #0 to host localhost left intact {"Name":"Publish local: test-repo","ID":4,"State":0} ``` **aptly logs** ``` [GIN] 2024/07/15 - 12:16:54 | 202 | 7.281485ms | 172.17.0.1 | POST "/api/publish/:." 2024/07/15 12:16:54 Executing task asynchronously Signing file 'Release' with gpg, please enter your passphrase when prompted: gpg: signing failed: No such file or directory gpg: signing failed: No such file or directory ```
neolynx commented 4 months ago

Could you try adding the Signing parameters (Batch, GpgKey and PassphraseFile) ? that usually works:

curl -X POST -H 'Content-Type: application/json' --data '{"SourceKind": "local", "Sources": [{"Name": "test-repo"}], "Architectures": ["i386", "amd64"], "Distribution": "bookworm", "Signing":{"Batch":true,"GpgKey":"signing@key", "PassphraseFile": "/path/to/gpg-passphrase-file"}}' http://localhost:8080/api/publish/:. -v
mkasimd commented 4 months ago

What I meant with this issue is: the HTTP 202 Accepted is kinda misleading. Is it possible to return a different status code (e.g. 400 Bad Request) if the request is faulty? That way, one can work on the returned status code to determine whether the action was successful or not.

As seen in issue #1266, it works with the signing options set just fine now. But the returned status code is kind of not helpful. I'd technically have to check the list of published repos to ensure whether it was successful or not.

In the best case, it'd be great to be able to distinguish between various errors using the HTTP status codes (missing Signing options, bad/wrong passphrase, etc.), but just being able to distinguish between "successful" and "didn't work" would be a great improvement as well

neolynx commented 3 months ago

Thanks for the clarifications !

I tried to reproduce with latest CI build (from master):

$ curl -X POST -H 'Content-Type: application/json' --data '{"SourceKind": "local", "Sources": [{"Name": "copied"}], "Architectures": ["i386", "amd64"], "Distribution": "bookworm"}' http://localhost:3142/api/publish/pub-test -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1:3142...
* Connected to localhost (127.0.0.1) port 3142 (#0)
> POST /api/publish/pub-test HTTP/1.1
> Host: localhost:3142
> User-Agent: curl/7.88.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 120
> 
< HTTP/1.1 500 Internal Server Error
< Content-Type: application/json; charset=utf-8
< Date: Mon, 12 Aug 2024 10:51:12 GMT
< Content-Length: 74
< 
* Connection #0 to host localhost left intact
{"error":"unable to publish: unable to detached sign file: exit status 2"}

while the aptly logs show:

molior-aptly | 2024-08-12T10:52:13Z DBG Executing task synchronously
molior-aptly | Signing file 'Release' with gpg, please enter your passphrase when prompted:
molior-aptly | gpg: Sorry, we are in batchmode - can't get input
molior-aptly | [GIN] 2024/08/12 - 10:52:13 | 500 |  112.632145ms |      172.19.0.1 | POST     "/api/publish/pub-test"
molior-aptly | Error #01: unable to publish: unable to detached sign file: exit status 2

it returns a 500 Internal Server Error, which is not a good choice, but I could not reproduce the 202 you are seeing.

Could you maybe try again with latest aptly ?