Skip to content

Commit 5a879c0

Browse files
committed
Fix #36897: Resolve NO_PROXY recognition issue in oss remote backend
1 parent 865a0eb commit 5a879c0

File tree

3 files changed

+116
-16
lines changed

3 files changed

+116
-16
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: BUG FIXES
2+
body: Fix OSS backend proxy support by adding a proxy layer for OSS backend operations. Resolves hashicorp/terraform#36897.
3+
time: 2025-08-28T18:27:46.867411+05:30
4+
custom:
5+
Issue: "36897"

internal/backend/remote-state/oss/backend.go

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"net/http"
1313
"net/url"
1414
"os"
15-
"regexp"
1615
"runtime"
1716
"strconv"
1817
"strings"
@@ -35,6 +34,7 @@ import (
3534
"github.com/hashicorp/terraform/internal/backend"
3635
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
3736
"github.com/hashicorp/terraform/version"
37+
"golang.org/x/net/http/httpproxy"
3838
)
3939

4040
// Deprecated in favor of flattening assume_role_* options
@@ -414,7 +414,10 @@ func (b *Backend) configure(ctx context.Context) error {
414414
}
415415
options = append(options, oss.UserAgent(fmt.Sprintf("%s/%s", TerraformUA, TerraformVersion)))
416416

417-
proxyUrl := getHttpProxyUrl()
417+
proxyUrl, err := getHttpProxyUrl(endpoint)
418+
if err != nil {
419+
return err
420+
}
418421
if proxyUrl != nil {
419422
options = append(options, oss.Proxy(proxyUrl.String()))
420423
}
@@ -706,19 +709,11 @@ func getAuthCredentialByEcsRoleName(ecsRoleName string) (accessKey, secretKey, t
706709
return accessKeyId.(string), accessKeySecret.(string), securityToken.(string), nil
707710
}
708711

709-
func getHttpProxyUrl() *url.URL {
710-
for _, v := range []string{"HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"} {
711-
value := strings.Trim(os.Getenv(v), " ")
712-
if value != "" {
713-
if !regexp.MustCompile(`^http(s)?://`).MatchString(value) {
714-
value = fmt.Sprintf("https://%s", value)
715-
}
716-
proxyUrl, err := url.Parse(value)
717-
if err == nil {
718-
return proxyUrl
719-
}
720-
break
721-
}
712+
func getHttpProxyUrl(rawUrl string) (*url.URL, error) {
713+
pc := httpproxy.FromEnvironment()
714+
u, err := url.Parse(rawUrl)
715+
if err != nil {
716+
return nil, err
722717
}
723-
return nil
718+
return pc.ProxyFunc()(u)
724719
}

internal/backend/remote-state/oss/backend_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,103 @@ func deleteTablestoreTable(t *testing.T, otsClient *tablestore.TableStoreClient,
251251
t.Logf("WARNING: Failed to delete the test TableStore table %q. It has been left in your Alibaba Cloud account and may incur charges. (error was %s)", tableName, err)
252252
}
253253
}
254+
255+
func TestGetHttpProxyUrl(t *testing.T) {
256+
tests := []struct {
257+
name string
258+
rawUrl string
259+
httpProxy string
260+
httpsProxy string
261+
noProxy string
262+
expectedProxyURL string
263+
}{
264+
{
265+
name: "should set proxy using http_proxy environment variable",
266+
rawUrl: "http://example.com",
267+
httpProxy: "http://foo.bar:3128",
268+
httpsProxy: "https://secure.example.com",
269+
noProxy: "",
270+
expectedProxyURL: "http://foo.bar:3128",
271+
},
272+
{
273+
name: "should set proxy using http_proxy environment variable",
274+
rawUrl: "http://example.com",
275+
httpProxy: "http://foo.barr",
276+
httpsProxy: "https://secure.example.com",
277+
noProxy: "",
278+
expectedProxyURL: "http://foo.barr",
279+
},
280+
{
281+
name: "should set proxy using https_proxy environment variable",
282+
rawUrl: "https://secure.example.com",
283+
httpProxy: "http://foo.bar",
284+
httpsProxy: "https://foo.bar.com:3128",
285+
noProxy: "",
286+
expectedProxyURL: "https://foo.bar.com:3128",
287+
},
288+
{
289+
name: "should set proxy using https_proxy environment variable",
290+
rawUrl: "https://secure.example.com",
291+
httpProxy: "",
292+
httpsProxy: "http://foo.baz",
293+
noProxy: "",
294+
expectedProxyURL: "http://foo.baz",
295+
},
296+
{
297+
name: "should not set http proxy if NO_PROXY contains the host",
298+
rawUrl: "http://example.internal",
299+
httpProxy: "http://foo.bar:3128",
300+
httpsProxy: "",
301+
noProxy: "example.internal",
302+
expectedProxyURL: "",
303+
},
304+
{
305+
name: "should not set HTTP proxy when NO_PROXY matches the domain with suffix",
306+
rawUrl: "http://qqu.example.internal",
307+
httpProxy: "http://foo.bar:3128",
308+
httpsProxy: "",
309+
noProxy: ".example.internal",
310+
expectedProxyURL: "",
311+
},
312+
{
313+
name: "should not set https proxy if NO_PROXY contains the host",
314+
rawUrl: "https://secure.internal",
315+
httpProxy: "",
316+
httpsProxy: "https://foo.baz:3128",
317+
noProxy: "secure.internal",
318+
expectedProxyURL: "",
319+
},
320+
{
321+
name: "should not set https proxy if NO_PROXY matches the domain with suffix",
322+
rawUrl: "https://ss.qcsc.secure.internal",
323+
httpProxy: "",
324+
httpsProxy: "https://foo.baz:3128",
325+
noProxy: ".secure.internal",
326+
expectedProxyURL: "",
327+
},
328+
}
329+
330+
for _, tt := range tests {
331+
t.Run(tt.name, func(t *testing.T) {
332+
// Set environment variables
333+
t.Setenv("HTTP_PROXY", tt.httpProxy)
334+
t.Setenv("HTTPS_PROXY", tt.httpsProxy)
335+
t.Setenv("NO_PROXY", tt.noProxy)
336+
337+
proxyUrl, err := getHttpProxyUrl(tt.rawUrl)
338+
if err != nil {
339+
t.Fatalf("unexpected error: %s", err)
340+
}
341+
342+
if tt.expectedProxyURL == "" {
343+
if proxyUrl != nil {
344+
t.Fatalf("unexpected proxy URL, want nil, got: %s", proxyUrl)
345+
}
346+
} else {
347+
if tt.expectedProxyURL != proxyUrl.String() {
348+
t.Fatalf("unexpected proxy URL, want: %s, got: %s", tt.expectedProxyURL, proxyUrl.String())
349+
}
350+
}
351+
})
352+
}
353+
}

0 commit comments

Comments
 (0)