Skip to content

Commit d87c6c4

Browse files
Decompress GZIP'd user data (#1762)
1 parent 25b6f34 commit d87c6c4

File tree

2 files changed

+322
-105
lines changed

2 files changed

+322
-105
lines changed

nodeadm/internal/configprovider/userdata.go

+70-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package configprovider
22

33
import (
44
"bytes"
5+
"compress/gzip"
6+
"encoding/base64"
57
"fmt"
68
"io"
79
"mime"
@@ -22,17 +24,39 @@ const (
2224
nodeConfigMediaType = "application/" + api.GroupName
2325
)
2426

25-
type userDataConfigProvider struct{}
27+
type userDataProvider interface {
28+
GetUserData() ([]byte, error)
29+
}
30+
31+
type imdsUserDataProvider struct{}
32+
33+
func (p *imdsUserDataProvider) GetUserData() ([]byte, error) {
34+
return imds.GetUserData()
35+
}
36+
37+
type userDataConfigProvider struct {
38+
userDataProvider userDataProvider
39+
}
2640

2741
func NewUserDataConfigProvider() ConfigProvider {
28-
return &userDataConfigProvider{}
42+
return &userDataConfigProvider{
43+
userDataProvider: &imdsUserDataProvider{},
44+
}
2945
}
3046

31-
func (ics *userDataConfigProvider) Provide() (*internalapi.NodeConfig, error) {
32-
userData, err := imds.GetUserData()
47+
func (p *userDataConfigProvider) Provide() (*internalapi.NodeConfig, error) {
48+
userData, err := p.userDataProvider.GetUserData()
3349
if err != nil {
3450
return nil, err
3551
}
52+
userData, err = decodeIfBase64(userData)
53+
if err != nil {
54+
return nil, fmt.Errorf("failed to decode user data: %v", err)
55+
}
56+
userData, err = decompressIfGZIP(userData)
57+
if err != nil {
58+
return nil, fmt.Errorf("failed to decompress user data: %v", err)
59+
}
3660
// if the MIME data fails to parse as a multipart document, then fall back
3761
// to parsing the entire userdata as the node config.
3862
if multipartReader, err := getMIMEMultipartReader(userData); err == nil {
@@ -85,6 +109,14 @@ func parseMultipart(userDataReader *multipart.Reader) (*internalapi.NodeConfig,
85109
if err != nil {
86110
return nil, err
87111
}
112+
nodeConfigPart, err = decodeIfBase64(nodeConfigPart)
113+
if err != nil {
114+
return nil, err
115+
}
116+
nodeConfigPart, err = decompressIfGZIP(nodeConfigPart)
117+
if err != nil {
118+
return nil, err
119+
}
88120
decodedConfig, err := apibridge.DecodeNodeConfig(nodeConfigPart)
89121
if err != nil {
90122
return nil, err
@@ -102,6 +134,39 @@ func parseMultipart(userDataReader *multipart.Reader) (*internalapi.NodeConfig,
102134
}
103135
return config, nil
104136
} else {
105-
return nil, fmt.Errorf("Could not find NodeConfig within UserData")
137+
return nil, fmt.Errorf("could not find NodeConfig within UserData")
138+
}
139+
}
140+
141+
func decodeIfBase64(data []byte) ([]byte, error) {
142+
e := base64.StdEncoding
143+
maxDecodedLen := e.DecodedLen(len(data))
144+
decodedData := make([]byte, maxDecodedLen)
145+
decodedLen, err := e.Decode(decodedData, data)
146+
if err != nil {
147+
return data, nil
148+
}
149+
return decodedData[:decodedLen], nil
150+
}
151+
152+
// https://en.wikipedia.org/wiki/Gzip
153+
const gzipMagicNumber = uint16(0x1f8b)
154+
155+
func decompressIfGZIP(data []byte) ([]byte, error) {
156+
if len(data) < 2 {
157+
return data, nil
158+
}
159+
preamble := uint16(data[0])<<8 | uint16(data[1])
160+
if preamble == gzipMagicNumber {
161+
reader, err := gzip.NewReader(bytes.NewReader(data))
162+
if err != nil {
163+
return nil, fmt.Errorf("failed to create GZIP reader: %v", err)
164+
}
165+
if decompressed, err := io.ReadAll(reader); err != nil {
166+
return nil, fmt.Errorf("failed to read from GZIP reader: %v", err)
167+
} else {
168+
return decompressed, nil
169+
}
106170
}
171+
return data, nil
107172
}

0 commit comments

Comments
 (0)