mirror of
https://github.com/prometheus/prometheus.git
synced 2026-06-10 09:00:08 -04:00
storage/remote: add OTLP request body read limit
Apply the same io.LimitReader guard (decodeReadLimit = 32 MiB) to the OTLP write decoder that remote read already use, so that a gzip-encoded request body cannot decompress to unbounded memory. Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
This commit is contained in:
parent
d6c18a4d62
commit
5d695516ba
2 changed files with 37 additions and 1 deletions
|
|
@ -1000,7 +1000,7 @@ func DecodeOTLPWriteRequest(r *http.Request) (pmetricotlp.ExportRequest, error)
|
|||
return pmetricotlp.NewExportRequest(), fmt.Errorf("unsupported compression: %s. Only \"gzip\" or no compression supported", r.Header.Get("Content-Encoding"))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(reader)
|
||||
body, err := io.ReadAll(io.LimitReader(reader, decodeReadLimit))
|
||||
if err != nil {
|
||||
r.Body.Close()
|
||||
return pmetricotlp.NewExportRequest(), err
|
||||
|
|
|
|||
|
|
@ -15,9 +15,12 @@ package remote
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
|
|
@ -25,6 +28,8 @@ import (
|
|||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/common/promslog"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/collector/pdata/pmetric"
|
||||
"go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp"
|
||||
|
||||
"github.com/prometheus/prometheus/config"
|
||||
"github.com/prometheus/prometheus/model/histogram"
|
||||
|
|
@ -729,6 +734,37 @@ func TestMergeLabels(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDecodeOTLPWriteRequestGzipSizeLimit(t *testing.T) {
|
||||
// Build a valid OTLP request whose serialized protobuf exceeds decodeReadLimit.
|
||||
// A metric description filled with repeated characters compresses very
|
||||
// efficiently, so the gzip payload is small while the decompressed form is
|
||||
// larger than the 32 MiB limit.
|
||||
d := pmetric.NewMetrics()
|
||||
m := d.ResourceMetrics().AppendEmpty().ScopeMetrics().AppendEmpty().Metrics().AppendEmpty()
|
||||
m.SetName("test_metric")
|
||||
m.SetDescription(strings.Repeat("a", decodeReadLimit+1))
|
||||
m.SetEmptyGauge()
|
||||
|
||||
proto, err := pmetricotlp.NewExportRequestFromMetrics(d).MarshalProto()
|
||||
require.NoError(t, err)
|
||||
|
||||
var buf bytes.Buffer
|
||||
gz := gzip.NewWriter(&buf)
|
||||
_, err = gz.Write(proto)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, gz.Close())
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, "/", &buf)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", pbContentType)
|
||||
req.Header.Set("Content-Encoding", "gzip")
|
||||
|
||||
// The decompressed payload exceeds decodeReadLimit and is truncated, so the
|
||||
// protobuf cannot be parsed into a valid ExportRequest.
|
||||
_, err = DecodeOTLPWriteRequest(req)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestDecodeWriteRequest(t *testing.T) {
|
||||
buf, _, _, err := buildWriteRequest(nil, writeRequestFixture.Timeseries, nil, nil, nil, nil, "snappy")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
Loading…
Reference in a new issue