Skip to content

Commit fd9dc41

Browse files
Fricounetimeoer
authored andcommitted
[remote] Skip HTTP fallback unless skip_verify is set
When skip_verify is false (the default), RetryWithPlainHTTP now returns false immediately, preventing silent HTTPS-to-HTTP downgrades. HTTP fallback is only permitted when the registry is explicitly configured as insecure, matching Docker's --insecure-registry semantics.
1 parent 03e051a commit fd9dc41

2 files changed

Lines changed: 83 additions & 0 deletions

File tree

pkg/remote/remote.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ type Remote struct {
4747
// withPlainHTTP attempts to request the remote registry using http instead
4848
// of https.
4949
withPlainHTTP bool
50+
// insecure indicates that the registry is explicitly configured as insecure.
51+
// HTTP fallback is only allowed when insecure is true,
52+
// matching Docker's --insecure-registry semantics.
53+
insecure bool
5054
}
5155

5256
func New(keyChain *auth.PassKeyChain, insecure bool) *Remote {
@@ -90,10 +94,15 @@ func New(keyChain *auth.PassKeyChain, insecure bool) *Remote {
9094
return &Remote{
9195
resolverFunc: resolverFunc,
9296
withPlainHTTP: false,
97+
insecure: insecure,
9398
}
9499
}
95100

96101
func (remote *Remote) RetryWithPlainHTTP(ref string, err error) bool {
102+
if !remote.insecure {
103+
return false
104+
}
105+
97106
retry := err != nil && (isErrHTTPResponseToHTTPSClient(err) || isErrConnectionRefused(err))
98107
if !retry {
99108
return false

pkg/remote/remote_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2026. Nydus Developers. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package remote
8+
9+
import (
10+
"fmt"
11+
"testing"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestRetryWithPlainHTTP(t *testing.T) {
17+
const host = "myregistry.example.com"
18+
ref := fmt.Sprintf("%s/repo/image:latest", host)
19+
httpResponseErr := fmt.Errorf("Get https://%s/v2/: server gave HTTP response to HTTPS client", host)
20+
connRefusedErr := fmt.Errorf("Get https://%s/v2/: connect: connection refused", host)
21+
otherErr := fmt.Errorf("some unrelated error")
22+
23+
tests := []struct {
24+
name string
25+
insecure bool
26+
err error
27+
want bool
28+
}{
29+
{
30+
name: "insecure allows HTTP fallback on HTTP response error",
31+
insecure: true,
32+
err: httpResponseErr,
33+
want: true,
34+
},
35+
{
36+
name: "insecure allows HTTP fallback on connection refused",
37+
insecure: true,
38+
err: connRefusedErr,
39+
want: true,
40+
},
41+
{
42+
name: "insecure does not fallback on unrelated error",
43+
insecure: true,
44+
err: otherErr,
45+
want: false,
46+
},
47+
{
48+
name: "secure blocks HTTP fallback on HTTP response error",
49+
insecure: false,
50+
err: httpResponseErr,
51+
want: false,
52+
},
53+
{
54+
name: "secure blocks HTTP fallback on connection refused",
55+
insecure: false,
56+
err: connRefusedErr,
57+
want: false,
58+
},
59+
{
60+
name: "nil error returns false",
61+
insecure: true,
62+
err: nil,
63+
want: false,
64+
},
65+
}
66+
67+
for _, tt := range tests {
68+
t.Run(tt.name, func(t *testing.T) {
69+
r := &Remote{insecure: tt.insecure}
70+
got := r.RetryWithPlainHTTP(ref, tt.err)
71+
assert.Equal(t, tt.want, got)
72+
})
73+
}
74+
}

0 commit comments

Comments
 (0)