Skip to content

Commit dbfb668

Browse files
committed
LOG-8068: Vector can't access log stores outside the cluster when restrict network policy is enabled and the cluster has cluster-wide proxy.
1 parent 28c8b02 commit dbfb668

File tree

3 files changed

+240
-36
lines changed

3 files changed

+240
-36
lines changed

internal/network/network_policy.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,26 @@ func ReconcileClusterLogForwarderNetworkPolicy(k8Client client.Client, namespace
2323

2424
// For RestrictIngressEgress, determine the ports to use based on URLs in outputs and defaults
2525
if policyRuleSet == obsv1.NetworkPolicyRuleSetTypeRestrictIngressEgress {
26-
// Parse ports with protocols from outputs
27-
if len(outputs) > 0 {
28-
egressPorts = GetOutputPortsWithProtocols(outputs)
29-
}
3026
// Parse ports from inputs (receiver inputs use TCP)
3127
if len(inputs) > 0 {
3228
ingressPorts = GetInputPorts(inputs)
3329
}
30+
31+
// Parse ports for egress from outputs and proxy configuration if any
32+
egressPortMap := map[factory.PortProtocol]bool{}
33+
// Parse ports with protocols from outputs
34+
if len(outputs) > 0 {
35+
GetOutputPortsWithProtocols(outputs, egressPortMap)
36+
37+
}
38+
// Add proxy ports if any for cluster-wide proxy configuration
39+
GetProxyPorts(egressPortMap)
40+
41+
// Convert map to slice
42+
egressPorts = make([]factory.PortProtocol, 0, len(egressPortMap))
43+
for pp := range egressPortMap {
44+
egressPorts = append(egressPorts, pp)
45+
}
3446
}
3547

3648
desired := factory.NewNetworkPolicyWithProtocolPorts(namespace, policyName, instanceName, component, string(policyRuleSet), egressPorts, ingressPorts, visitor)

internal/network/ports.go

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import (
77

88
obs "github.com/openshift/cluster-logging-operator/api/observability/v1"
99
"github.com/openshift/cluster-logging-operator/internal/factory"
10+
"github.com/openshift/cluster-logging-operator/internal/utils"
1011
corev1 "k8s.io/api/core/v1"
12+
"k8s.io/apimachinery/pkg/util/sets"
1113
"k8s.io/utils/set"
1214
)
1315

1416
// GetOutputPortsWithProtocols extracts all unique ports with their protocols from the given outputs.
1517
// It parses URLs to extract ports and protocols, or uses default values based on the output type.
16-
func GetOutputPortsWithProtocols(outputs []obs.OutputSpec) []factory.PortProtocol {
17-
portProtocolMap := map[factory.PortProtocol]bool{}
18-
18+
func GetOutputPortsWithProtocols(outputs []obs.OutputSpec, portProtocolMap map[factory.PortProtocol]bool) {
1919
for _, output := range outputs {
2020
portProtocols := getPortProtocolFromOutputURL(output)
2121
for _, pp := range portProtocols {
@@ -24,13 +24,6 @@ func GetOutputPortsWithProtocols(outputs []obs.OutputSpec) []factory.PortProtoco
2424
}
2525
}
2626
}
27-
28-
result := make([]factory.PortProtocol, 0, len(portProtocolMap))
29-
for pp := range portProtocolMap {
30-
result = append(result, pp)
31-
}
32-
33-
return result
3427
}
3528

3629
// GetInputPorts extracts all unique ports from the given input receiver specs.
@@ -200,3 +193,37 @@ func getDefaultPort(outputType obs.OutputType, urlStr string) int32 {
200193
}
201194
panic(fmt.Sprintf("unknown output type: %s", outputType))
202195
}
196+
197+
// GetProxyPorts extracts unique ports from cluster-wide proxy environment variables.
198+
// It parses HTTP_PROXY and HTTPS_PROXY URLs to determine explicit proxy ports,
199+
// or adds default ports (80 for HTTP, 443 for HTTPS) when no port is specified.
200+
func GetProxyPorts(portProtocolMap map[factory.PortProtocol]bool) {
201+
proxyEnvNames := sets.NewString("http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY")
202+
203+
// Get proxy environment variables and parse them for additional explicit ports
204+
proxyEnvVars := utils.GetProxyEnvVars()
205+
206+
for _, envVar := range proxyEnvVars {
207+
// Process proxy environment variables
208+
if proxyEnvNames.Has(envVar.Name) {
209+
if port := parsePortProtocolFromURL(envVar.Value); port != nil {
210+
// Add explicitly configured port from proxy URL
211+
portProtocolMap[*port] = true
212+
} else if envVar.Value != "" {
213+
// If no explicit port, add default port based on scheme
214+
if parsedURL, err := url.Parse(envVar.Value); err == nil {
215+
var defaultPort int32
216+
switch parsedURL.Scheme {
217+
case "http":
218+
defaultPort = 80
219+
case "https":
220+
defaultPort = 443
221+
}
222+
if defaultPort > 0 {
223+
portProtocolMap[factory.PortProtocol{Port: defaultPort, Protocol: corev1.ProtocolTCP}] = true
224+
}
225+
}
226+
}
227+
}
228+
}
229+
}

internal/network/ports_test.go

Lines changed: 187 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package network
22

33
import (
4+
"os"
5+
46
. "github.com/onsi/ginkgo/v2"
57
. "github.com/onsi/gomega"
68

@@ -323,12 +325,13 @@ var _ = Describe("Network Ports", func() {
323325
},
324326
},
325327
}
326-
ports := GetOutputPortsWithProtocols(outputs)
327-
Expect(ports).To(ConsistOf(
328-
factory.PortProtocol{Port: 9200, Protocol: corev1.ProtocolTCP},
329-
factory.PortProtocol{Port: 8088, Protocol: corev1.ProtocolTCP},
330-
factory.PortProtocol{Port: 3100, Protocol: corev1.ProtocolTCP},
331-
))
328+
portMap := map[factory.PortProtocol]bool{}
329+
GetOutputPortsWithProtocols(outputs, portMap)
330+
Expect(portMap).To(Equal(map[factory.PortProtocol]bool{
331+
{Port: 9200, Protocol: corev1.ProtocolTCP}: true,
332+
{Port: 8088, Protocol: corev1.ProtocolTCP}: true,
333+
{Port: 3100, Protocol: corev1.ProtocolTCP}: true,
334+
}))
332335
})
333336

334337
It("should deduplicate ports from multiple outputs", func() {
@@ -352,11 +355,12 @@ var _ = Describe("Network Ports", func() {
352355
},
353356
},
354357
}
355-
ports := GetOutputPortsWithProtocols(outputs)
356-
Expect(ports).To(ConsistOf(
357-
factory.PortProtocol{Port: 9200, Protocol: corev1.ProtocolTCP},
358-
factory.PortProtocol{Port: 8088, Protocol: corev1.ProtocolTCP},
359-
))
358+
portMap := map[factory.PortProtocol]bool{}
359+
GetOutputPortsWithProtocols(outputs, portMap)
360+
Expect(portMap).To(Equal(map[factory.PortProtocol]bool{
361+
{Port: 9200, Protocol: corev1.ProtocolTCP}: true,
362+
{Port: 8088, Protocol: corev1.ProtocolTCP}: true,
363+
}))
360364
})
361365

362366
It("should handle outputs with default ports", func() {
@@ -374,11 +378,12 @@ var _ = Describe("Network Ports", func() {
374378
},
375379
},
376380
}
377-
ports := GetOutputPortsWithProtocols(outputs)
378-
Expect(ports).To(ConsistOf(
379-
factory.PortProtocol{Port: 9200, Protocol: corev1.ProtocolTCP},
380-
factory.PortProtocol{Port: 80, Protocol: corev1.ProtocolTCP},
381-
))
381+
portMap := map[factory.PortProtocol]bool{}
382+
GetOutputPortsWithProtocols(outputs, portMap)
383+
Expect(portMap).To(Equal(map[factory.PortProtocol]bool{
384+
{Port: 9200, Protocol: corev1.ProtocolTCP}: true,
385+
{Port: 80, Protocol: corev1.ProtocolTCP}: true,
386+
}))
382387
})
383388

384389
It("should handle complex Kafka output with multiple brokers", func() {
@@ -401,12 +406,13 @@ var _ = Describe("Network Ports", func() {
401406
},
402407
},
403408
}
404-
ports := GetOutputPortsWithProtocols(outputs)
405-
Expect(ports).To(ConsistOf(
406-
factory.PortProtocol{Port: 9092, Protocol: corev1.ProtocolTCP},
407-
factory.PortProtocol{Port: 9093, Protocol: corev1.ProtocolTCP},
408-
factory.PortProtocol{Port: 9200, Protocol: corev1.ProtocolTCP},
409-
))
409+
portMap := map[factory.PortProtocol]bool{}
410+
GetOutputPortsWithProtocols(outputs, portMap)
411+
Expect(portMap).To(Equal(map[factory.PortProtocol]bool{
412+
{Port: 9092, Protocol: corev1.ProtocolTCP}: true,
413+
{Port: 9093, Protocol: corev1.ProtocolTCP}: true,
414+
{Port: 9200, Protocol: corev1.ProtocolTCP}: true,
415+
}))
410416
})
411417
})
412418
})
@@ -557,4 +563,163 @@ var _ = Describe("Network Ports", func() {
557563
})
558564
})
559565
})
566+
567+
Describe("GetProxyPorts", func() {
568+
var originalEnvVars map[string]string
569+
570+
BeforeEach(func() {
571+
// Save original environment variables
572+
originalEnvVars = make(map[string]string)
573+
for _, envVar := range []string{"HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy", "NO_PROXY", "no_proxy"} {
574+
originalEnvVars[envVar] = os.Getenv(envVar)
575+
os.Unsetenv(envVar) // Clear all proxy env vars for clean test state
576+
}
577+
})
578+
579+
AfterEach(func() {
580+
// Restore original environment variables
581+
for envVar, value := range originalEnvVars {
582+
if value != "" {
583+
os.Setenv(envVar, value)
584+
} else {
585+
os.Unsetenv(envVar)
586+
}
587+
}
588+
})
589+
590+
DescribeTable("when proxy environment variables are set",
591+
func(envVars map[string]string, expectedPorts []factory.PortProtocol) {
592+
for key, value := range envVars {
593+
os.Setenv(key, value)
594+
}
595+
596+
portMap := map[factory.PortProtocol]bool{}
597+
GetProxyPorts(portMap)
598+
ports := make([]factory.PortProtocol, 0, len(portMap))
599+
for pp := range portMap {
600+
ports = append(ports, pp)
601+
}
602+
if len(expectedPorts) == 0 {
603+
Expect(ports).To(BeEmpty())
604+
} else {
605+
Expect(ports).To(ConsistOf(expectedPorts))
606+
}
607+
},
608+
Entry("should extract ports from HTTP proxy URLs with explicit ports",
609+
map[string]string{
610+
"http_proxy": "http://proxy.example.com:8080",
611+
"https_proxy": "https://proxy.example.com:8443",
612+
},
613+
[]factory.PortProtocol{
614+
{Port: 8080, Protocol: corev1.ProtocolTCP},
615+
{Port: 8443, Protocol: corev1.ProtocolTCP},
616+
},
617+
),
618+
Entry("should use default ports when proxy URLs don't specify ports",
619+
map[string]string{
620+
"http_proxy": "http://proxy.example.com",
621+
"https_proxy": "https://proxy.example.com",
622+
},
623+
[]factory.PortProtocol{
624+
{Port: 80, Protocol: corev1.ProtocolTCP},
625+
{Port: 443, Protocol: corev1.ProtocolTCP},
626+
},
627+
),
628+
Entry("should return empty when proxy URLs have unknown schemes without ports",
629+
map[string]string{
630+
"http_proxy": "proxy://proxy.example.com",
631+
},
632+
[]factory.PortProtocol{},
633+
),
634+
Entry("should deduplicate identical proxy ports",
635+
map[string]string{
636+
"http_proxy": "http://proxy.example.com:8080",
637+
"https_proxy": "https://proxy.example.com:8080",
638+
},
639+
[]factory.PortProtocol{
640+
{Port: 8080, Protocol: corev1.ProtocolTCP},
641+
},
642+
),
643+
Entry("should handle malformed proxy URLs gracefully",
644+
map[string]string{
645+
"http_proxy": "not-a-valid-url",
646+
"https_proxy": "http://proxy.example.com:8080",
647+
},
648+
[]factory.PortProtocol{
649+
{Port: 8080, Protocol: corev1.ProtocolTCP},
650+
},
651+
),
652+
Entry("should handle uppercase proxy environment variables",
653+
map[string]string{
654+
"HTTP_PROXY": "http://proxy.example.com:3128",
655+
"HTTPS_PROXY": "https://proxy.example.com:3129",
656+
},
657+
[]factory.PortProtocol{
658+
{Port: 3128, Protocol: corev1.ProtocolTCP},
659+
{Port: 3129, Protocol: corev1.ProtocolTCP},
660+
},
661+
),
662+
Entry("should extract explicitly specified standard ports from proxy URLs",
663+
map[string]string{
664+
"http_proxy": "http://proxy.example.com:80",
665+
"https_proxy": "https://proxy.example.com:443",
666+
},
667+
[]factory.PortProtocol{
668+
{Port: 80, Protocol: corev1.ProtocolTCP},
669+
{Port: 443, Protocol: corev1.ProtocolTCP},
670+
},
671+
),
672+
Entry("should extract explicit ports when both proxy and no-proxy environment variables are set",
673+
map[string]string{
674+
"http_proxy": "http://proxy.example.com:8080",
675+
"https_proxy": "https://proxy.example.com:8080",
676+
"no_proxy": "localhost,127.0.0.1",
677+
},
678+
[]factory.PortProtocol{
679+
{Port: 8080, Protocol: corev1.ProtocolTCP},
680+
},
681+
),
682+
Entry("should handle proxy URLs with authentication",
683+
map[string]string{
684+
"http_proxy": "http://user:[email protected]:8080",
685+
},
686+
[]factory.PortProtocol{
687+
{Port: 8080, Protocol: corev1.ProtocolTCP},
688+
},
689+
),
690+
Entry("should handle IPv6 proxy URLs",
691+
map[string]string{
692+
"http_proxy": "http://[::1]:8080",
693+
},
694+
[]factory.PortProtocol{
695+
{Port: 8080, Protocol: corev1.ProtocolTCP},
696+
},
697+
),
698+
Entry("should handle empty string proxy URLs gracefully",
699+
map[string]string{
700+
"http_proxy": "",
701+
"https_proxy": "https://proxy.example.com:8443",
702+
},
703+
[]factory.PortProtocol{
704+
{Port: 8443, Protocol: corev1.ProtocolTCP},
705+
},
706+
),
707+
Entry("should handle mix of uppercase and lowercase proxy environment variables",
708+
map[string]string{
709+
"HTTP_PROXY": "http://proxy.example.com:8080",
710+
"https_proxy": "https://proxy.example.com:8443",
711+
},
712+
[]factory.PortProtocol{
713+
{Port: 8080, Protocol: corev1.ProtocolTCP},
714+
{Port: 8443, Protocol: corev1.ProtocolTCP},
715+
},
716+
),
717+
)
718+
719+
It("should be empty portMap when no proxy environment variables are set", func() {
720+
portMap := map[factory.PortProtocol]bool{}
721+
GetProxyPorts(portMap)
722+
Expect(portMap).To(BeEmpty())
723+
})
724+
})
560725
})

0 commit comments

Comments
 (0)