dirmngr fails with keyservers specified by IP without rDNS; reported as dead host or uses wrong Host header
Open, NormalPublic

Description

TL;DR: dirmgr should use the string as specified by the user as-is for connecting to keyservers; if it's an IP, connect directly and send Host: IP as-is.

  1. if the IP does not have rDNS, the lookup fails entirely.
  2. if the IP does have rDNS, it sends a Host header based on the rDNS; this should NOT happen.

The user should be responsible for specifying the correct entry to trigger SNI & vhost-based routing in reverse proxies/load-balancers.

$ killall dirmngr # clear the hosttable
$ h1=kookaburra.gentoo.org
$ h2=$(dig +short $h1 IN A) # no rDNS
$ h3=$(dig +short trogan.gentoo.org IN A) # has rDNS
$ gpg --keyserver hkp://$h1 --recv E3F69979BB4B8928DA78E3D17CBF44EF5C350883
gpg: key 0x7CBF44EF5C350883: 34 signatures not checked due to missing keys
gpg: key 0x7CBF44EF5C350883: "Patrick McLean (Chutzpah) <chutzpah@gentoo.org>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1

$ gpg --keyserver hkp://$h2 --recv E3F69979BB4B8928DA78E3D17CBF44EF5C350883
gpg: keyserver receive failed: No keyserver available


$ gpg --keyserver hkp://$h3 --recv E3F69979BB4B8928DA78E3D17CBF44EF5C350883
gpg: key 0x7CBF44EF5C350883: 34 signatures not checked due to missing keys
gpg: key 0x7CBF44EF5C350883: "Patrick McLean (Chutzpah) <chutzpah@gentoo.org>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1


$ gpg-connect-agent --dirmngr 'keyserver --hosttable' /bye
S # hosttable (idx, ipv6, ipv4, dead, name, time):
S #   0     d 208.116.51.2  (45s)
S #   1   4   kookaburra.gentoo.org (208.116.51.2)
S #   2   4   89.238.71.4 (trogan.gentoo.org)
OK

dirmngr.log:

----- h1 testcase
2019-03-31 13:21:06 dirmngr[5381.6] handler for fd 6 started
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 -> # Home: /home/robbat2/.gnupg
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 -> # Config: /home/robbat2/.gnupg/dirmngr.conf
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 -> OK Dirmngr 2.2.12 at your service
2019-03-31 13:21:06 dirmngr[5381.6] connection from process 27596 (10000:10000)
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 <- GETINFO version
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 -> D 2.2.12
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 -> OK
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 <- KEYSERVER --clear hkp://kookaburra.gentoo.org
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 -> OK
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 <- KS_GET -- 0xE3F69979BB4B8928DA78E3D17CBF44EF5C350883
2019-03-31 13:21:06 dirmngr[5381.6] DBG: Using TLS library: GNUTLS 3.6.5
2019-03-31 13:21:06 dirmngr[5381.6] DBG: http.c:connect_server: trying name='kookaburra.gentoo.org' port=11371
2019-03-31 13:21:06 dirmngr[5381.6] DBG: dns: resolve_dns_name(kookaburra.gentoo.org): Success
2019-03-31 13:21:06 dirmngr[5381.6] DBG: http.c:1899:socket_new: object 0x00007f40500be210 for fd 7 created
2019-03-31 13:21:06 dirmngr[5381.6] DBG: http.c:request:
2019-03-31 13:21:06 dirmngr[5381.6] DBG: >> GET /pks/lookup?op=get&options=mr&search=0xE3F69979BB4B8928DA78E3D17CBF44EF5C350883 HTTP/1.0\r\n
2019-03-31 13:21:06 dirmngr[5381.6] DBG: >> Host: kookaburra.gentoo.org:11371\r\n
2019-03-31 13:21:06 dirmngr[5381.6] DBG: http.c:request-header:
2019-03-31 13:21:06 dirmngr[5381.6] DBG: >> \r\n
2019-03-31 13:21:06 dirmngr[5381.6] DBG: chan_6 -> S PROGRESS tick ? 0 0
2019-03-31 13:21:07 dirmngr[5381.6] DBG: http.c:response:
2019-03-31 13:21:07 dirmngr[5381.6] DBG: >> HTTP/1.0 200 OK\r\n
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: 'Server: sks_www/1.1.6'
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: 'Cache-Control: no-cache'
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: 'Pragma: no-cache'
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: 'Expires: 0'
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: 'Content-length: 30886'
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: 'X-HKP-Results-Count: 1'
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: 'Content-type: application/pgp-keys; charset=UTF-8'
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: 'Content-disposition: attachment; filename=gpgkey.asc'
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: 'Access-Control-Allow-Origin: *'
2019-03-31 13:21:07 dirmngr[5381.6] http.c:RESP: ''
2019-03-31 13:21:07 dirmngr[5381.6] DBG: chan_6 -> S SOURCE http://kookaburra.gentoo.org:11371
2019-03-31 13:21:07 dirmngr[5381.6] DBG: (30886 bytes sent via D lines not shown)
2019-03-31 13:21:07 dirmngr[5381.6] DBG: chan_6 -> OK
2019-03-31 13:21:07 dirmngr[5381.6] DBG: chan_6 <- BYE
2019-03-31 13:21:07 dirmngr[5381.6] DBG: chan_6 -> OK closing connection
2019-03-31 13:21:07 dirmngr[5381.6] handler for fd 6 terminated


----- h2 testcase
2019-03-31 13:21:32 dirmngr[5381.6] handler for fd 6 started
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 -> # Home: /home/robbat2/.gnupg
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 -> # Config: /home/robbat2/.gnupg/dirmngr.conf
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 -> OK Dirmngr 2.2.12 at your service
2019-03-31 13:21:32 dirmngr[5381.6] connection from process 27666 (10000:10000)
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 <- GETINFO version
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 -> D 2.2.12
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 -> OK
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 <- KEYSERVER --clear hkp://208.116.51.2
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 -> OK
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 <- KS_GET -- 0xE3F69979BB4B8928DA78E3D17CBF44EF5C350883
2019-03-31 13:21:32 dirmngr[5381.6] DBG: dns: resolve_dns_name(208.116.51.2): Success
2019-03-31 13:21:32 dirmngr[5381.6] DBG: dns: resolve_dns_addr(): No name
2019-03-31 13:21:32 dirmngr[5381.6] host '208.116.51.2' marked as dead
2019-03-31 13:21:32 dirmngr[5381.6] command 'KS_GET' failed: No keyserver available
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 -> ERR 167772346 No keyserver available <Dirmngr>
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 <- BYE
2019-03-31 13:21:32 dirmngr[5381.6] DBG: chan_6 -> OK closing connection
2019-03-31 13:21:32 dirmngr[5381.6] handler for fd 6 terminated

----- h3 testcase
2019-03-31 13:30:05 dirmngr[5381.6] handler for fd 6 started
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 -> # Home: /home/robbat2/.gnupg
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 -> # Config: /home/robbat2/.gnupg/dirmngr.conf
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 -> OK Dirmngr 2.2.12 at your service
2019-03-31 13:30:05 dirmngr[5381.6] connection from process 29501 (10000:10000)
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 <- GETINFO version
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 -> D 2.2.12
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 -> OK
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 <- KEYSERVER --clear hkp://89.238.71.4
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 -> OK
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 <- KS_GET -- 0xE3F69979BB4B8928DA78E3D17CBF44EF5C350883
2019-03-31 13:30:05 dirmngr[5381.6] DBG: dns: resolve_dns_name(89.238.71.4): Success
2019-03-31 13:30:05 dirmngr[5381.6] DBG: dns: resolve_dns_addr(): Success
2019-03-31 13:30:05 dirmngr[5381.6] DBG: Using TLS library: GNUTLS 3.6.5
2019-03-31 13:30:05 dirmngr[5381.6] DBG: http.c:connect_server: trying name='89.238.71.4' port=11371
2019-03-31 13:30:05 dirmngr[5381.6] DBG: dns: resolve_dns_name(89.238.71.4): Success
2019-03-31 13:30:05 dirmngr[5381.6] DBG: http.c:1899:socket_new: object 0x00007f40500bd6a0 for fd 8 created
2019-03-31 13:30:05 dirmngr[5381.6] DBG: http.c:request:
2019-03-31 13:30:05 dirmngr[5381.6] DBG: >> GET /pks/lookup?op=get&options=mr&search=0xE3F69979BB4B8928DA78E3D17CBF44EF5C350883 HTTP/1.0\r\n
2019-03-31 13:30:05 dirmngr[5381.6] DBG: >> Host: trogan.gentoo.org:11371\r\n
2019-03-31 13:30:05 dirmngr[5381.6] DBG: http.c:request-header:
2019-03-31 13:30:05 dirmngr[5381.6] DBG: >> \r\n
2019-03-31 13:30:05 dirmngr[5381.6] DBG: chan_6 -> S PROGRESS tick ? 0 0
2019-03-31 13:30:06 dirmngr[5381.6] DBG: http.c:response:
2019-03-31 13:30:06 dirmngr[5381.6] DBG: >> HTTP/1.0 200 OK\r\n
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: 'Server: sks_www/1.1.6'
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: 'Cache-Control: no-cache'
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: 'Pragma: no-cache'
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: 'Expires: 0'
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: 'Content-length: 30882'
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: 'X-HKP-Results-Count: 1'
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: 'Content-type: application/pgp-keys; charset=UTF-8'
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: 'Content-disposition: attachment; filename=gpgkey.asc'
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: 'Access-Control-Allow-Origin: *'
2019-03-31 13:30:06 dirmngr[5381.6] http.c:RESP: ''
2019-03-31 13:30:06 dirmngr[5381.6] DBG: chan_6 -> S SOURCE http://89.238.71.4:11371
2019-03-31 13:30:06 dirmngr[5381.6] DBG: (30882 bytes sent via D lines not shown)
2019-03-31 13:30:06 dirmngr[5381.6] DBG: chan_6 -> OK
2019-03-31 13:30:06 dirmngr[5381.6] DBG: chan_6 <- BYE
2019-03-31 13:30:06 dirmngr[5381.6] DBG: chan_6 -> OK closing connection
2019-03-31 13:30:06 dirmngr[5381.6] handler for fd 6 terminated

Details

Version
2.2.12
robbat2 created this task.Mar 31 2019, 10:35 PM
werner added a subscriber: werner.Apr 1 2019, 12:55 PM

So in short you want:

  1. Allow to specify a keyserver by IP without any DNS lookups.
  2. When connecting via IP use the IP address for Host:.

Implement the the first might be possible.

I do not understand the second, because the IP address is already known to the server. In case your server setup needs to know the original IP the client used, that is imho up to your frontend server and not a matter of the client.

It's up to GPG to send the Host header that shows the user's intent.

If I do curl -v http://1.1.1.1/, CURL doesn't try to do a rDNS lookup to figure out the hostname, opens a TCP connection to 1.1.1.1:80, and sends GET / HTTP/1.1\r\nHost: 1.1.1.1\r\n (plus other optional HTTP headers like User-Agent etc).

The user's intent to connect to that specific IP is preserved in that.

GPG's present behavior, restated:

  1. User runs: gpg --keyserver hkp://89.238.71.4 --recv E3F69979BB4B8928DA78E3D17CBF44EF5C350883
  2. GPG does rDNS lookup for 89.238.71.4, and gets the name trogan.gentoo.org.
  3. GPG connects to 89.238.71.4:11371
  4. GPG Sends GET /pks/lookup?op=get&options=mr&search=0xE3F69979BB4B8928DA78E3D17CBF44EF5C350883 HTTP/1.0\r\nHost: trogan.gentoo.org\r\n

GPG here has used the value in rDNS to create the Host header.

My expected behavior for GPG if provided a straight IP, is that of pure HTTP like curl, so it should have done:

  1. User runs gpg --keyserver hkp://89.238.71.4 --recv E3F69979BB4B8928DA78E3D17CBF44EF5C350883
  2. GPG connects to 89.238.71.4:11371
  3. GPG sends GET /pks/lookup?op=get&options=mr&search=0xE3F69979BB4B8928DA78E3D17CBF44EF5C350883 HTTP/1.0\r\nHost: 89.238.71.4\r\n

No DNS lookups needed at all.

werner added a comment.Apr 1 2019, 8:01 PM

Please be so kind and point me to the specs stating that you should put the IP address into Host:

HTTP/1.1 spec, RFC 7230, Section 5.4, paragraph 2:
https://tools.ietf.org/html/rfc7230#section-5.4

A client MUST send a Host header field in all HTTP/1.1 request
messages. If the target URI includes an authority component, then a
client MUST send a field-value for Host that is identical to that
authority component, excluding any userinfo subcomponent and its "@"
delimiter (Section 2.7.1). If the authority component is missing or
undefined for the target URI, then a client MUST send a Host header
field with an empty field-value.

Emphasis: If the target URI includes an authority component, then a client MUST send a field-value for Host that is identical to that authority component

The authority component in this case is the IP being used as a hostname.

werner triaged this task as Normal priority.May 17 2019, 6:47 PM