Publish API: "json: cannot unmarshal array into Go struct field .Signing of type api.SigningOptions"
When using an encrypted PGP key and delivering the passphrase, publishing fails with HTTP status code 400 and the response body {"error":"json: cannot unmarshal array into Go struct field .Signing of type api.SigningOptions"}
Detailed Description
When using a GPG key that was generated with a passphrase, publishing a repo via API fails (cropped output):
$ curl -X POST -H 'Content-Type: application/json' --data '{"SourceKind": "local", "Sources": [{"Name": "test-repo"}], "Architectures": ["i386", "amd64"], "Signing": [{"Skip": false, "Passphrase": "123456"}], "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: 176
>
< HTTP/1.1 400 Bad Request
< Date: Thu, 11 Apr 2024 14:08:36 GMT
< Content-Length: 97
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host localhost left intact
{"error":"json: cannot unmarshal array into Go struct field .Signing of type api.SigningOptions"}
This seems to be related to: json - cannot unmarshal array into Go struct when decoding array| StackOverflow
Context
Storing private keys in an encrypted manner is a security feature, I'd like to keep. As the aptly API can be made accessible TLS-encrypted behind a reverse proxy, delivering the passphrase in cleartext to the aptly API endpoint isn't a security risk either.
Possible Implementation
In api/publish.go:102, changing Signing SigningOptions to Signing []SigningOptions could solve it, if it's related to above mentioned StackOverflow issue (I haven't tested it)
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"}
what is the reason for adding the array ?
in our api calls we just pass a dict:
$ curl -X POST -H 'Content-Type: application/json' --data '{"SourceKind": "local", "Sources": [{"Name": "test-repo"}], "Architectures": ["i386", "amd64"], "Signing": {"Skip": false, "Passphrase": "123456"}, "Distribution": "bookworm"}' http://localhost:8080/api/publish/:. -v
could you try this ?
That one didn't work for me either unfortunately. Up until now, I have succeeded with unencrypted PGP keys. It's also unusual that it returns 202 Accepted for almost any input. So I'd have to list items through a GET request to see if it was actually successful or not
$ curl http://localhost:8080/api/repos
[{"Name":"test-repo","Comment":"","DefaultDistribution":"","DefaultComponent":""}]
$ curl -X POST -H 'Content-Type: application/json' --data '{"SourceKind": "local", "Sources": [{"Name": "test-repo"}], "Architectures": ["i386", "amd64"], "Signing": {"Skip": false, "Passphrase": "hello"}, "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: 174
>
< HTTP/1.1 202 Accepted
< Content-Type: application/json; charset=utf-8
< Date: Thu, 18 Apr 2024 07:28:52 GMT
< Content-Length: 52
<
* Connection #0 to host localhost left intact
{"Name":"Publish local: test-repo","ID":1,"State":0}
$ curl http://localhost:8080/api/publish
[]
In the Logs of aptly:
[GIN] 2024/04/18 - 07:28:52 | 202 | 9.624038ms | 172.17.0.1 | POST "/api/publish/:."
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
looks like the keyring is not found. where is it stored ?
we are using something similar to this:
curl -fsS -X PUT -H 'Content-Type: application/json' --data \
'{"AcquireByHash": true, "Snapshots": [{"Component": "main", "Name": "snapshot1"}],
"Signing": {"Batch": true, "Keyring": "trustedkeys.gpg", "Passphrase": "hello"}}' \
http://localhost:8080/api/publish/pub1
this uses ~/.gnupg/trustedkeys.gpg, also the batch argument is important for running gpg.
Thanks for the response. Adding "Batch":true was the solution apparently. It just worked
Could you also add the Batch option to the list describing the Signing Options in the Publish API documentation?
Thanks for your inputs !
Documentation updated: https://www.aptly.info/doc/api/publish/ :-)
@neolynx one small issue in the documentation: the type should be set to bool instead of string for completeness
also, thanks for your help with this!