Skip to content

Commit 55ed026

Browse files
[AUTO-CHERRYPICK] [Medium] prometheus-adapter: Fix CVE-2022-3162 - branch main (#12533)
Co-authored-by: Sreenivasulu Malavathula (HCL Technologies Ltd) <v-smalavathu@microsoft.com>
1 parent b551da9 commit 55ed026

2 files changed

Lines changed: 348 additions & 1 deletion

File tree

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
From e78a604008da76c6483bd5a9e55a1fdd06b603b9 Mon Sep 17 00:00:00 2001
2+
From: Sreenivasulu Malavathula <v-smalavathu@microsoft.com>
3+
Date: Mon, 17 Feb 2025 17:17:57 -0600
4+
Subject: [PATCH] Address CVE-2022-3162
5+
6+
---
7+
.../apiserver/pkg/storage/etcd3/store.go | 139 ++++++++++++------
8+
1 file changed, 95 insertions(+), 44 deletions(-)
9+
10+
diff --git a/vendor/k8s.io/apiserver/pkg/storage/etcd3/store.go b/vendor/k8s.io/apiserver/pkg/storage/etcd3/store.go
11+
index f97bc38..0e2fb36 100644
12+
--- a/vendor/k8s.io/apiserver/pkg/storage/etcd3/store.go
13+
+++ b/vendor/k8s.io/apiserver/pkg/storage/etcd3/store.go
14+
@@ -99,16 +99,21 @@ func New(c *clientv3.Client, codec runtime.Codec, newFunc func() runtime.Object,
15+
16+
func newStore(c *clientv3.Client, codec runtime.Codec, newFunc func() runtime.Object, prefix string, groupResource schema.GroupResource, transformer value.Transformer, pagingEnabled bool, leaseManagerConfig LeaseManagerConfig) *store {
17+
versioner := APIObjectVersioner{}
18+
+ // for compatibility with etcd2 impl.
19+
+ // no-op for default prefix of '/registry'.
20+
+ // keeps compatibility with etcd2 impl for custom prefixes that don't start with '/'
21+
+ pathPrefix := path.Join("/", prefix)
22+
+ if !strings.HasSuffix(pathPrefix, "/") {
23+
+ // Ensure the pathPrefix ends in "/" here to simplify key concatenation later.
24+
+ pathPrefix += "/"
25+
+ }
26+
result := &store{
27+
- client: c,
28+
- codec: codec,
29+
- versioner: versioner,
30+
- transformer: transformer,
31+
- pagingEnabled: pagingEnabled,
32+
- // for compatibility with etcd2 impl.
33+
- // no-op for default prefix of '/registry'.
34+
- // keeps compatibility with etcd2 impl for custom prefixes that don't start with '/'
35+
- pathPrefix: path.Join("/", prefix),
36+
+ client: c,
37+
+ codec: codec,
38+
+ versioner: versioner,
39+
+ transformer: transformer,
40+
+ pagingEnabled: pagingEnabled,
41+
+ pathPrefix: pathPrefix,
42+
groupResource: groupResource,
43+
groupResourceString: groupResource.String(),
44+
watcher: newWatcher(c, codec, newFunc, versioner, transformer),
45+
@@ -124,9 +129,12 @@ func (s *store) Versioner() storage.Versioner {
46+
47+
// Get implements storage.Interface.Get.
48+
func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, out runtime.Object) error {
49+
- key = path.Join(s.pathPrefix, key)
50+
+ preparedKey, err := s.prepareKey(key)
51+
+ if err != nil {
52+
+ return err
53+
+ }
54+
startTime := time.Now()
55+
- getResp, err := s.client.KV.Get(ctx, key)
56+
+ getResp, err := s.client.KV.Get(ctx, preparedKey)
57+
metrics.RecordEtcdRequestLatency("get", getTypeName(out), startTime)
58+
if err != nil {
59+
return err
60+
@@ -139,11 +147,11 @@ func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, ou
61+
if opts.IgnoreNotFound {
62+
return runtime.SetZeroValue(out)
63+
}
64+
- return storage.NewKeyNotFoundError(key, 0)
65+
+ return storage.NewKeyNotFoundError(preparedKey, 0)
66+
}
67+
kv := getResp.Kvs[0]
68+
69+
- data, _, err := s.transformer.TransformFromStorage(ctx, kv.Value, authenticatedDataString(key))
70+
+ data, _, err := s.transformer.TransformFromStorage(ctx, kv.Value, authenticatedDataString(preparedKey))
71+
if err != nil {
72+
return storage.NewInternalError(err.Error())
73+
}
74+
@@ -153,6 +161,11 @@ func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, ou
75+
76+
// Create implements storage.Interface.Create.
77+
func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error {
78+
+ preparedKey, err := s.prepareKey(key)
79+
+ if err != nil {
80+
+ return err
81+
+ }
82+
+
83+
if version, err := s.versioner.ObjectResourceVersion(obj); err == nil && version != 0 {
84+
return errors.New("resourceVersion should not be set on objects to be created")
85+
}
86+
@@ -163,30 +176,29 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object,
87+
if err != nil {
88+
return err
89+
}
90+
- key = path.Join(s.pathPrefix, key)
91+
92+
opts, err := s.ttlOpts(ctx, int64(ttl))
93+
if err != nil {
94+
return err
95+
}
96+
97+
- newData, err := s.transformer.TransformToStorage(ctx, data, authenticatedDataString(key))
98+
+ newData, err := s.transformer.TransformToStorage(ctx, data, authenticatedDataString(preparedKey))
99+
if err != nil {
100+
return storage.NewInternalError(err.Error())
101+
}
102+
103+
startTime := time.Now()
104+
txnResp, err := s.client.KV.Txn(ctx).If(
105+
- notFound(key),
106+
+ notFound(preparedKey),
107+
).Then(
108+
- clientv3.OpPut(key, string(newData), opts...),
109+
+ clientv3.OpPut(preparedKey, string(newData), opts...),
110+
).Commit()
111+
metrics.RecordEtcdRequestLatency("create", getTypeName(obj), startTime)
112+
if err != nil {
113+
return err
114+
}
115+
if !txnResp.Succeeded {
116+
- return storage.NewKeyExistsError(key, 0)
117+
+ return storage.NewKeyExistsError(preparedKey, 0)
118+
}
119+
120+
if out != nil {
121+
@@ -200,12 +212,15 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object,
122+
func (s *store) Delete(
123+
ctx context.Context, key string, out runtime.Object, preconditions *storage.Preconditions,
124+
validateDeletion storage.ValidateObjectFunc, cachedExistingObject runtime.Object) error {
125+
+ preparedKey, err := s.prepareKey(key)
126+
+ if err != nil {
127+
+ return err
128+
+ }
129+
v, err := conversion.EnforcePtr(out)
130+
if err != nil {
131+
return fmt.Errorf("unable to convert output object to pointer: %v", err)
132+
}
133+
- key = path.Join(s.pathPrefix, key)
134+
- return s.conditionalDelete(ctx, key, out, v, preconditions, validateDeletion, cachedExistingObject)
135+
+ return s.conditionalDelete(ctx, preparedKey, out, v, preconditions, validateDeletion, cachedExistingObject)
136+
}
137+
138+
func (s *store) conditionalDelete(
139+
@@ -318,6 +333,10 @@ func (s *store) conditionalDelete(
140+
func (s *store) GuaranteedUpdate(
141+
ctx context.Context, key string, out runtime.Object, ignoreNotFound bool,
142+
preconditions *storage.Preconditions, tryUpdate storage.UpdateFunc, cachedExistingObject runtime.Object) error {
143+
+ preparedKey, err := s.prepareKey(key)
144+
+ if err != nil {
145+
+ return err
146+
+ }
147+
trace := utiltrace.New("GuaranteedUpdate etcd3", utiltrace.Field{"type", getTypeName(out)})
148+
defer trace.LogIfLong(500 * time.Millisecond)
149+
150+
@@ -325,16 +344,15 @@ func (s *store) GuaranteedUpdate(
151+
if err != nil {
152+
return fmt.Errorf("unable to convert output object to pointer: %v", err)
153+
}
154+
- key = path.Join(s.pathPrefix, key)
155+
156+
getCurrentState := func() (*objState, error) {
157+
startTime := time.Now()
158+
- getResp, err := s.client.KV.Get(ctx, key)
159+
+ getResp, err := s.client.KV.Get(ctx, preparedKey)
160+
metrics.RecordEtcdRequestLatency("get", getTypeName(out), startTime)
161+
if err != nil {
162+
return nil, err
163+
}
164+
- return s.getState(ctx, getResp, key, v, ignoreNotFound)
165+
+ return s.getState(ctx, getResp, preparedKey, v, ignoreNotFound)
166+
}
167+
168+
var origState *objState
169+
@@ -350,9 +368,9 @@ func (s *store) GuaranteedUpdate(
170+
}
171+
trace.Step("initial value restored")
172+
173+
- transformContext := authenticatedDataString(key)
174+
+ transformContext := authenticatedDataString(preparedKey)
175+
for {
176+
- if err := preconditions.Check(key, origState.obj); err != nil {
177+
+ if err := preconditions.Check(preparedKey, origState.obj); err != nil {
178+
// If our data is already up to date, return the error
179+
if origStateIsCurrent {
180+
return err
181+
@@ -435,11 +453,11 @@ func (s *store) GuaranteedUpdate(
182+
183+
startTime := time.Now()
184+
txnResp, err := s.client.KV.Txn(ctx).If(
185+
- clientv3.Compare(clientv3.ModRevision(key), "=", origState.rev),
186+
+ clientv3.Compare(clientv3.ModRevision(preparedKey), "=", origState.rev),
187+
).Then(
188+
- clientv3.OpPut(key, string(newData), opts...),
189+
+ clientv3.OpPut(preparedKey, string(newData), opts...),
190+
).Else(
191+
- clientv3.OpGet(key),
192+
+ clientv3.OpGet(preparedKey),
193+
).Commit()
194+
metrics.RecordEtcdRequestLatency("update", getTypeName(out), startTime)
195+
if err != nil {
196+
@@ -448,8 +466,8 @@ func (s *store) GuaranteedUpdate(
197+
trace.Step("Transaction committed")
198+
if !txnResp.Succeeded {
199+
getResp := (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange())
200+
- klog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", key)
201+
- origState, err = s.getState(ctx, getResp, key, v, ignoreNotFound)
202+
+ klog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", preparedKey)
203+
+ origState, err = s.getState(ctx, getResp, preparedKey, v, ignoreNotFound)
204+
if err != nil {
205+
return err
206+
}
207+
@@ -481,18 +499,21 @@ func getNewItemFunc(listObj runtime.Object, v reflect.Value) func() runtime.Obje
208+
}
209+
210+
func (s *store) Count(key string) (int64, error) {
211+
- key = path.Join(s.pathPrefix, key)
212+
+ preparedKey, err := s.prepareKey(key)
213+
+ if err != nil {
214+
+ return 0, err
215+
+ }
216+
217+
// We need to make sure the key ended with "/" so that we only get children "directories".
218+
// e.g. if we have key "/a", "/a/b", "/ab", getting keys with prefix "/a" will return all three,
219+
// while with prefix "/a/" will return only "/a/b" which is the correct answer.
220+
- if !strings.HasSuffix(key, "/") {
221+
- key += "/"
222+
+ if !strings.HasSuffix(preparedKey, "/") {
223+
+ preparedKey += "/"
224+
}
225+
226+
startTime := time.Now()
227+
- getResp, err := s.client.KV.Get(context.Background(), key, clientv3.WithRange(clientv3.GetPrefixRangeEnd(key)), clientv3.WithCountOnly())
228+
- metrics.RecordEtcdRequestLatency("listWithCount", key, startTime)
229+
+ getResp, err := s.client.KV.Get(context.Background(), preparedKey, clientv3.WithRange(clientv3.GetPrefixRangeEnd(preparedKey)), clientv3.WithCountOnly())
230+
+ metrics.RecordEtcdRequestLatency("listWithCount", preparedKey, startTime)
231+
if err != nil {
232+
return 0, err
233+
}
234+
@@ -561,6 +582,10 @@ func encodeContinue(key, keyPrefix string, resourceVersion int64) (string, error
235+
236+
// GetList implements storage.Interface.
237+
func (s *store) GetList(ctx context.Context, key string, opts storage.ListOptions, listObj runtime.Object) error {
238+
+ preparedKey, err := s.prepareKey(key)
239+
+ if err != nil {
240+
+ return err
241+
+ }
242+
recursive := opts.Recursive
243+
resourceVersion := opts.ResourceVersion
244+
match := opts.ResourceVersionMatch
245+
@@ -580,16 +605,15 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption
246+
if err != nil || v.Kind() != reflect.Slice {
247+
return fmt.Errorf("need ptr to slice: %v", err)
248+
}
249+
- key = path.Join(s.pathPrefix, key)
250+
251+
// For recursive lists, we need to make sure the key ended with "/" so that we only
252+
// get children "directories". e.g. if we have key "/a", "/a/b", "/ab", getting keys
253+
// with prefix "/a" will return all three, while with prefix "/a/" will return only
254+
// "/a/b" which is the correct answer.
255+
- if recursive && !strings.HasSuffix(key, "/") {
256+
- key += "/"
257+
+ if recursive && !strings.HasSuffix(preparedKey, "/") {
258+
+ preparedKey += "/"
259+
}
260+
- keyPrefix := key
261+
+ keyPrefix := preparedKey
262+
263+
// set the appropriate clientv3 options to filter the returned data set
264+
var limitOption *clientv3.OpOption
265+
@@ -626,7 +650,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption
266+
267+
rangeEnd := clientv3.GetPrefixRangeEnd(keyPrefix)
268+
options = append(options, clientv3.WithRange(rangeEnd))
269+
- key = continueKey
270+
+ preparedKey = continueKey
271+
272+
// If continueRV > 0, the LIST request needs a specific resource version.
273+
// continueRV==0 is invalid.
274+
@@ -693,7 +717,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption
275+
}()
276+
for {
277+
startTime := time.Now()
278+
- getResp, err = s.client.KV.Get(ctx, key, options...)
279+
+ getResp, err = s.client.KV.Get(ctx, preparedKey, options...)
280+
if recursive {
281+
metrics.RecordEtcdRequestLatency("list", getTypeName(listPtr), startTime)
282+
} else {
283+
@@ -765,7 +789,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption
284+
}
285+
*limitOption = clientv3.WithLimit(limit)
286+
}
287+
- key = string(lastKey) + "\x00"
288+
+ preparedKey = string(lastKey) + "\x00"
289+
if withRev == 0 {
290+
withRev = returnedRV
291+
options = append(options, clientv3.WithRev(withRev))
292+
@@ -830,12 +854,15 @@ func growSlice(v reflect.Value, maxCapacity int, sizes ...int) {
293+
294+
// Watch implements storage.Interface.Watch.
295+
func (s *store) Watch(ctx context.Context, key string, opts storage.ListOptions) (watch.Interface, error) {
296+
+ preparedKey, err := s.prepareKey(key)
297+
+ if err != nil {
298+
+ return nil, err
299+
+ }
300+
rev, err := s.versioner.ParseResourceVersion(opts.ResourceVersion)
301+
if err != nil {
302+
return nil, err
303+
}
304+
- key = path.Join(s.pathPrefix, key)
305+
- return s.watcher.Watch(ctx, key, int64(rev), opts.Recursive, opts.ProgressNotify, opts.Predicate)
306+
+ return s.watcher.Watch(ctx, preparedKey, int64(rev), opts.Recursive, opts.ProgressNotify, opts.Predicate)
307+
}
308+
309+
func (s *store) getState(ctx context.Context, getResp *clientv3.GetResponse, key string, v reflect.Value, ignoreNotFound bool) (*objState, error) {
310+
@@ -947,6 +974,30 @@ func (s *store) validateMinimumResourceVersion(minimumResourceVersion string, ac
311+
return nil
312+
}
313+
314+
+func (s *store) prepareKey(key string) (string, error) {
315+
+ if key == ".." ||
316+
+ strings.HasPrefix(key, "../") ||
317+
+ strings.HasSuffix(key, "/..") ||
318+
+ strings.Contains(key, "/../") {
319+
+ return "", fmt.Errorf("invalid key: %q", key)
320+
+ }
321+
+ if key == "." ||
322+
+ strings.HasPrefix(key, "./") ||
323+
+ strings.HasSuffix(key, "/.") ||
324+
+ strings.Contains(key, "/./") {
325+
+ return "", fmt.Errorf("invalid key: %q", key)
326+
+ }
327+
+ if key == "" || key == "/" {
328+
+ return "", fmt.Errorf("empty key: %q", key)
329+
+ }
330+
+ // We ensured that pathPrefix ends in '/' in construction, so skip any leading '/' in the key now.
331+
+ startIndex := 0
332+
+ if key[0] == '/' {
333+
+ startIndex = 1
334+
+ }
335+
+ return s.pathPrefix + key[startIndex:], nil
336+
+}
337+
+
338+
// decode decodes value of bytes into object. It will also set the object resource version to rev.
339+
// On success, objPtr would be set to the object.
340+
func decode(codec runtime.Codec, versioner storage.Versioner, value []byte, objPtr runtime.Object, rev int64) error {
341+
--
342+
2.45.2
343+

SPECS/prometheus-adapter/prometheus-adapter.spec

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Summary: Kubernetes Custom, Resource, and External Metric APIs implemented to work with Prometheus.
22
Name: prometheus-adapter
33
Version: 0.10.0
4-
Release: 16%{?dist}
4+
Release: 17%{?dist}
55
License: Apache-2.0
66
Vendor: Microsoft Corporation
77
Distribution: Mariner
@@ -10,6 +10,7 @@ Source0: https://github.com/kubernetes-sigs/%{name}/archive/refs/tags/v%{
1010
Patch0: CVE-2024-24786.patch
1111
Patch1: CVE-2022-32149.patch
1212
Patch2: CVE-2024-45338.patch
13+
Patch3: CVE-2022-3162.patch
1314
BuildRequires: golang
1415

1516
%description
@@ -44,6 +45,9 @@ make test
4445
%doc README.md RELEASE.md
4546

4647
%changelog
48+
* Tue Feb 18 2025 Sreeniavsulu Malavathula <v-smalavathu@microsoft.com> - 0.10.0-17
49+
- Patch to fix CVE-2022-3162
50+
4751
* Thu Jan 02 2025 Sumedh Sharma <sumsharma@microsoft.com> - 0.10.0-16
4852
- Add patch for CVE-2024-45338.
4953

0 commit comments

Comments
 (0)