TUN-10834: Bump Go toolchains to 1.26.8

Update CI and standard container builders to Go 1.26.8.

Add coverage for Go 1.26's stricter net/url host parsing, rejecting
unbracketed IPv6 addresses and repeated port separators in origin URLs.
Retain existing Access bare-IPv6 normalization behavior.

Closes TUN-10834
This commit is contained in:
Devin Carr
2026-09-03 15:24:38 -07:00
parent 79c7547d81
commit 2253eeeb25
8 changed files with 31 additions and 82 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ RUN apt-get update && \
apt-get install --no-install-recommends --allow-downgrades -y \
build-essential \
git \
go-boring=1.26.4-1 \
go-boring=1.26.8-1 \
libffi-dev \
procps \
python3-dev \
+1 -1
View File
@@ -1,5 +1,5 @@
variables:
GO_VERSION: "1.26.4"
GO_VERSION: "1.26.8"
MAC_GO_VERSION: "go@$GO_VERSION"
WIN_GO_VERSION: "go$GO_VERSION"
GIT_DEPTH: "0"
+1 -1
View File
@@ -1,7 +1,7 @@
# use a builder image for building cloudflare
ARG TARGET_GOOS
ARG TARGET_GOARCH
FROM golang:1.26.4 AS builder
FROM golang:1.26.8 AS builder
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOPROXY=https://athens.cfdata.org|https://proxy.golang.org|direct \
+1 -1
View File
@@ -1,5 +1,5 @@
# use a builder image for building cloudflare
FROM golang:1.26.4 AS builder
FROM golang:1.26.8 AS builder
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOPROXY=https://athens.cfdata.org|https://proxy.golang.org|direct \
+1 -1
View File
@@ -1,5 +1,5 @@
# use a builder image for building cloudflare
FROM golang:1.26.4 AS builder
FROM golang:1.26.8 AS builder
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOPROXY=https://athens.cfdata.org|https://proxy.golang.org|direct \
+1 -1
View File
@@ -1,6 +1,6 @@
# use a builder image for building cloudflared
ARG CLOUDFLARE_DOCKER_REGISTRY_HOST
FROM ${CLOUDFLARE_DOCKER_REGISTRY_HOST:-registry.cfdata.org}/stash/plat/dockerfiles/debian-trixie-golang-boring/master:1.26.4-1@sha256:6306b83f8fcec303db94ebb69ad2f1f0aea5c09115f845839757afca4699925c AS builder
FROM ${CLOUDFLARE_DOCKER_REGISTRY_HOST:-registry.cfdata.org}/stash/plat/dockerfiles/debian-trixie-golang-boring/master:1.26.8-1@sha256:2cff81b1547df7c6bfe0cdba17b76678054d5054bedd71809be0829e5f3fc652 AS builder
ENV GO111MODULE=on \
CGO_ENABLED=1 \
GOPROXY=https://athens.cfdata.org|https://proxy.golang.org|direct \
+1 -1
View File
@@ -1,6 +1,6 @@
# use a builder image for building cloudflared
ARG CLOUDFLARE_DOCKER_REGISTRY_HOST
FROM ${CLOUDFLARE_DOCKER_REGISTRY_HOST:-registry.cfdata.org}/stash/plat/dockerfiles/debian-trixie-golang-boring/master:1.26.4-1@sha256:6306b83f8fcec303db94ebb69ad2f1f0aea5c09115f845839757afca4699925c AS builder
FROM ${CLOUDFLARE_DOCKER_REGISTRY_HOST:-registry.cfdata.org}/stash/plat/dockerfiles/debian-trixie-golang-boring/master:1.26.8-1@sha256:2cff81b1547df7c6bfe0cdba17b76678054d5054bedd71809be0829e5f3fc652 AS builder
ENV GO111MODULE=on \
CGO_ENABLED=1 \
GOPROXY=https://athens.cfdata.org|https://proxy.golang.org|direct \
+24 -75
View File
@@ -1,54 +1,45 @@
package validation
import (
"bytes"
"context"
"fmt"
"io"
"net/http/httptest"
"testing"
"context"
"crypto/tls"
"crypto/x509"
"net"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestValidateHostname(t *testing.T) {
var inputHostname string
hostname, err := ValidateHostname(inputHostname)
assert.Equal(t, err, nil)
require.NoError(t, err)
assert.Empty(t, hostname)
inputHostname = "hello.example.com"
hostname, err = ValidateHostname(inputHostname)
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, "hello.example.com", hostname)
inputHostname = "http://hello.example.com"
hostname, err = ValidateHostname(inputHostname)
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, "hello.example.com", hostname)
inputHostname = "bücher.example.com"
hostname, err = ValidateHostname(inputHostname)
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, "xn--bcher-kva.example.com", hostname)
inputHostname = "http://bücher.example.com"
hostname, err = ValidateHostname(inputHostname)
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, "xn--bcher-kva.example.com", hostname)
inputHostname = "http%3A%2F%2Fhello.example.com"
hostname, err = ValidateHostname(inputHostname)
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, "hello.example.com", hostname)
}
func TestValidateUrl(t *testing.T) {
@@ -99,7 +90,7 @@ func TestValidateUrl(t *testing.T) {
}
for i, testCase := range testCases {
validUrl, err := ValidateUrl(testCase.input)
assert.NoError(t, err, "test case %v", i)
require.NoError(t, err, "test case %v", i)
assert.Equal(t, testCase.expectedOutput, validUrl.String(), "test case %v", i)
}
@@ -111,6 +102,16 @@ func TestValidateUrl(t *testing.T) {
assert.Equal(t, "Currently Cloudflare Tunnel does not support ftp protocol.", err.Error())
assert.Empty(t, validUrl)
for _, input := range []string{
"http://::1/",
"http://localhost:80:80/",
} {
t.Run("reject malformed host "+input, func(t *testing.T) {
validURL, err := ValidateUrl(input)
require.Error(t, err)
assert.Nil(t, validURL)
})
}
}
func TestNewAccessValidatorOk(t *testing.T) {
@@ -118,11 +119,11 @@ func TestNewAccessValidatorOk(t *testing.T) {
url := "test.cloudflareaccess.com"
access, err := NewAccessValidator(ctx, url, url, "")
assert.NoError(t, err)
require.NoError(t, err)
assert.NotNil(t, access)
assert.Error(t, access.Validate(ctx, ""))
assert.Error(t, access.Validate(ctx, "invalid"))
require.Error(t, access.Validate(ctx, ""))
require.Error(t, access.Validate(ctx, "invalid"))
req := httptest.NewRequest("GET", "https://test.cloudflareaccess.com", nil)
req.Header.Set(accessJwtHeader, "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c")
@@ -141,63 +142,11 @@ func TestNewAccessValidatorErr(t *testing.T) {
for _, url := range urls {
access, err := NewAccessValidator(ctx, url, url, "")
assert.Error(t, err, url)
require.Error(t, err, url)
assert.Nil(t, access)
}
}
type testRoundTripper func(req *http.Request) (*http.Response, error)
func (f testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}
func emptyResponse(statusCode int) *http.Response {
return &http.Response{
StatusCode: statusCode,
Body: io.NopCloser(bytes.NewReader(nil)),
Header: make(http.Header),
}
}
func createMockServerAndClient(handler http.Handler) (*httptest.Server, *http.Client, error) {
client := http.DefaultClient
server := httptest.NewServer(handler)
client.Transport = &http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) {
return url.Parse(server.URL)
},
}
return server, client, nil
}
func createSecureMockServerAndClient(handler http.Handler) (*httptest.Server, *http.Client, error) {
client := http.DefaultClient
server := httptest.NewTLSServer(handler)
cert, err := x509.ParseCertificate(server.TLS.Certificates[0].Certificate[0])
if err != nil {
server.Close()
return nil, nil, err
}
certpool := x509.NewCertPool()
certpool.AddCert(cert)
client.Transport = &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.Dial("tcp", server.URL[strings.LastIndex(server.URL, "/")+1:])
},
TLSClientConfig: &tls.Config{
RootCAs: certpool,
},
}
return server, client, nil
}
func FuzzNewAccessValidator(f *testing.F) {
f.Fuzz(func(t *testing.T, domain string, issuer string, applicationAUD string) {
ctx := context.Background()