From d86c8bcabdcb6d2f7e9e69a03b665255f3385618 Mon Sep 17 00:00:00 2001 From: Milan Lenco Date: Thu, 6 Aug 2026 09:24:31 +0200 Subject: [PATCH 1/2] zedUpload/sftp: accept a scheme-qualified host as well as bare host:port getSftpClient assumed host was always a bare "host:port" and extracted the hostname for its pre-flight DNS check via a naive strings.Split(host, ":"), taking the first element. Since https://github.com/lf-edge/eve/pull/5588, EVE's datastore Fqdn is always scheme-qualified for every datastore type, including SFTP (e.g. "sftp://host:port"), which broke that assumption: the first element became the literal scheme "sftp" instead of the real host, causing the SFTP download to fail with: sftpclient failed for sftp://244.244.244.244:22: lookup sftp on 127.0.0.1:53: no such host (i.e. trying to resolve "sftp") Detect which form was given via url.Parse: a real scheme-qualified authority-form URL parses with a non-empty Host, whereas a bare host:port also "succeeds" but only as an opaque URI (empty Host, with the part before the port's colon landing in Scheme/Opaque instead). Use u.Host when present, and fall back to the original bare host:port otherwise, so existing callers outside EVE are unaffected. Also replaces the naive colon-split used for the hostname-only DNS pre-check with net.SplitHostPort, which additionally fixes handling of a bracketed IPv6 literal (e.g. "[::1]:22") in both forms. Signed-off-by: Milan Lenco (cherry picked from commit 882c84170868b8d199b29ab35127b914c8ada099) --- zedUpload/sftputil/sftp.go | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/zedUpload/sftputil/sftp.go b/zedUpload/sftputil/sftp.go index e8e8d36..e90690c 100644 --- a/zedUpload/sftputil/sftp.go +++ b/zedUpload/sftputil/sftp.go @@ -8,6 +8,7 @@ import ( "io" "log" "net" + "net/url" "os" "strings" "time" @@ -46,14 +47,31 @@ func getSftpClient(host, user, pass string) (*sftp.Client, error) { Timeout: time.Duration(10) * time.Second, } + // host may be a bare "host:port" (the original contract, still used by + // older EVE versions and potentially callers outside EVE) or a scheme-qualified + // URL such as "sftp://host:port", which newer EVE versions now require + // (since https://github.com/lf-edge/eve/pull/5588). + // url.Parse "succeeds" on a bare host:port too, but only as an opaque URI + // (an empty Host, with the part before the port's colon landing in Scheme/Opaque + // instead), so u.Host is what actually distinguishes the two: non-empty only for a + // real scheme-qualified authority-form URL. + dialAddr := host + if u, err := url.Parse(host); err == nil && u.Host != "" { + dialAddr = u.Host + } + // We break this up into a DNS lookup and a Dial only to be able to detect - // the errors better - args := strings.Split(host, ":") - if _, err := net.LookupHost(args[0]); err != nil { + // the errors better. net.SplitHostPort (rather than a naive split on ":") + // also correctly handles a bracketed IPv6 literal. + hostname, _, err := net.SplitHostPort(dialAddr) + if err != nil { + hostname = dialAddr + } + if _, err := net.LookupHost(hostname); err != nil { log.Printf("LookupHost error: %s", err) return nil, err } - client, err := ssh.Dial("tcp", host, clientConfig) + client, err := ssh.Dial("tcp", dialAddr, clientConfig) if err != nil { return nil, err } From 0de9f1944b3d754d3d9c6f49f5db67d376063093 Mon Sep 17 00:00:00 2001 From: Milan Lenco Date: Mon, 10 Aug 2026 15:24:15 +0200 Subject: [PATCH 2/2] nettrace: don't pin Let's Encrypt intermediate CN in TestTLSCertErrors Let's Encrypt rotated badssl.com's wrong-host certificate onto a new intermediate (YR2) on 2026-07-28, breaking the hardcoded R\d{1,2} regex. Match only the stable, non-rotating parts of the issuer DN instead, matching the fix from main (commit 6b30666, "nettrace: don't pin Let's Encrypt intermediate"), adapted to this branch's older, non-subtest test structure. Co-Authored-By: Claude Sonnet 5 Signed-off-by: Milan Lenco --- nettrace/httpclient_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nettrace/httpclient_test.go b/nettrace/httpclient_test.go index ea68175..dc1f5eb 100644 --- a/nettrace/httpclient_test.go +++ b/nettrace/httpclient_test.go @@ -743,7 +743,10 @@ func TestTLSCertErrors(test *testing.T) { t.Expect(tlsTun.PeerCerts).To(HaveLen(1)) peerCert = tlsTun.PeerCerts[0] t.Expect(peerCert.IsCA).To(BeFalse()) - t.Expect(peerCert.Issuer).To(MatchRegexp(`^CN=R\d{1,2},O=Let's Encrypt,C=US$`)) + // Let's Encrypt may start issuing from a new intermediate at any time + // without warning, so match only the parts of the issuer DN that do not + // change across intermediate rotations. + t.Expect(peerCert.Issuer).To(MatchRegexp(`^CN=[^,]+,O=Let's Encrypt,C=US$`)) t.Expect(peerCert.Subject).To(Equal("CN=*.badssl.com")) t.Expect(peerCert.NotBefore.Abs.Before(time.Now())).To(BeTrue()) t.Expect(peerCert.NotAfter.Abs.After(time.Now())).To(BeTrue())