wave13: 514/239 — flink/nifi/artemis + K8s/cilium/linkerd2 + ES/OpenSearch/Solr + hadoop/hbase/spark
This commit is contained in:
parent
424a2a7787
commit
8f0bc73afa
32 changed files with 3977 additions and 5 deletions
|
|
@ -0,0 +1,114 @@
|
|||
# linkerd2-0002: CWE-407 — Quadratic opaque port lookup during pod injection
|
||||
|
||||
## Severity: MEDIUM
|
||||
|
||||
## Repository
|
||||
github.com/linkerd/linkerd2
|
||||
Commit: (depth-1 clone, branch main)
|
||||
|
||||
## File
|
||||
`pkg/inject/inject.go`
|
||||
|
||||
## Defective Lines
|
||||
```
|
||||
816: defaultPorts := strings.Split(conf.GetValues().Proxy.OpaquePorts, ",") // []string slice
|
||||
|
||||
855: func (conf *ResourceConfig) FilterPodOpaquePorts(defaultPorts []string) []string {
|
||||
856: var filteredPorts []string
|
||||
857: for _, c := range append(conf.pod.spec.InitContainers, conf.pod.spec.Containers...) { // O(C)
|
||||
858: for _, p := range c.Ports { // O(P)
|
||||
859: port := strconv.Itoa(int(p.ContainerPort))
|
||||
860: if util.ContainsString(port, defaultPorts) { // O(D)
|
||||
861: filteredPorts = append(filteredPorts, port)
|
||||
862: }
|
||||
863: }
|
||||
864: }
|
||||
865: return filteredPorts
|
||||
866: }
|
||||
|
||||
826: for _, p := range service.Spec.Ports { // O(SP) service ports
|
||||
834: if util.ContainsString(port, defaultPorts) { // O(D) scan
|
||||
837: } else if util.ContainsString(strconv.Itoa(int(p.TargetPort.IntVal)), defaultPorts) { // O(D)
|
||||
```
|
||||
|
||||
## ContainsString Implementation
|
||||
```go
|
||||
// pkg/util/parsing.go:77
|
||||
func ContainsString(str string, collection []string) bool {
|
||||
for _, s := range collection { // O(D) linear scan
|
||||
if s == str { return true }
|
||||
}
|
||||
return false
|
||||
}
|
||||
```
|
||||
|
||||
## Call Chain
|
||||
```
|
||||
Admission webhook per pod/service creation/update →
|
||||
CreateAnnotationPatch() [inject.go line ~815] →
|
||||
FilterPodOpaquePorts(defaultPorts) →
|
||||
for each container × port: ContainsString(port, defaultPorts) // O(C×P×D)
|
||||
|
||||
for each service port: ContainsString(port, defaultPorts) // O(SP×D)
|
||||
```
|
||||
|
||||
## Complexity
|
||||
O(C × P × D) where:
|
||||
- C = number of containers + init containers per pod
|
||||
- P = number of exposed ports per container
|
||||
- D = length of defaultPorts list (comma-separated string, user-configured)
|
||||
|
||||
`defaultPorts` defaults to ~25 well-known opaque ports (3306, 5432, 6379, etc.)
|
||||
but can be extended by operators. It is parsed as `[]string` and scanned linearly
|
||||
on every port check. The same `defaultPorts` slice is scanned multiple times per
|
||||
pod without pre-indexing.
|
||||
|
||||
## Impact
|
||||
Every pod and service injection via the linkerd-proxy-injector webhook calls this
|
||||
path. In clusters with high churn (rolling deployments, HPA scaling), this webhook
|
||||
is hot. Pods with many containers and ports (e.g., sidecar-heavy microservices)
|
||||
cause O(C×P×D) work per admission request.
|
||||
|
||||
## Fix
|
||||
Pre-build a `map[string]struct{}` from `defaultPorts` once before the loop.
|
||||
|
||||
```go
|
||||
// Before (defective):
|
||||
func (conf *ResourceConfig) FilterPodOpaquePorts(defaultPorts []string) []string {
|
||||
var filteredPorts []string
|
||||
for _, c := range ... {
|
||||
for _, p := range c.Ports {
|
||||
port := strconv.Itoa(int(p.ContainerPort))
|
||||
if util.ContainsString(port, defaultPorts) { // O(D) per port
|
||||
filteredPorts = append(filteredPorts, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredPorts
|
||||
}
|
||||
|
||||
// After (fixed):
|
||||
func (conf *ResourceConfig) FilterPodOpaquePorts(defaultPorts []string) []string {
|
||||
defaultSet := make(map[string]struct{}, len(defaultPorts))
|
||||
for _, p := range defaultPorts { defaultSet[p] = struct{}{} }
|
||||
|
||||
var filteredPorts []string
|
||||
for _, c := range ... {
|
||||
for _, p := range c.Ports {
|
||||
port := strconv.Itoa(int(p.ContainerPort))
|
||||
if _, ok := defaultSet[port]; ok { // O(1)
|
||||
filteredPorts = append(filteredPorts, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredPorts
|
||||
}
|
||||
```
|
||||
|
||||
The same fix applies to the service port checks at lines 834 and 837 —
|
||||
build the set once before the `for _, p := range service.Spec.Ports` loop.
|
||||
|
||||
## References
|
||||
- CWE-407: Inefficient Algorithmic Complexity
|
||||
- `pkg/inject/inject.go` lines 816-865
|
||||
- `pkg/util/parsing.go` lines 76-82 (ContainsString)
|
||||
Loading…
Add table
Add a link
Reference in a new issue