Skip to content

Commit 340b25c

Browse files
authored
Merge branch 'dev' into stakers_model
2 parents 33b7dd9 + ab20b7d commit 340b25c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+2029
-1944
lines changed

.github/workflows/test.upgrade.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ jobs:
2626
- name: Build the avalanchego binary
2727
shell: bash
2828
run: ./scripts/build.sh
29-
# TODO: re-activate this test after there is a compatible tag to use
30-
# - name: Run upgrade tests
31-
# shell: bash
32-
# run: scripts/tests.upgrade.sh 1.9.0 ./build/avalanchego
29+
- name: Run upgrade tests
30+
shell: bash
31+
run: scripts/tests.upgrade.sh 1.10.1 ./build/avalanchego

api/health/health_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ func TestDeadlockRegression(t *testing.T) {
245245
h.Start(context.Background(), time.Nanosecond)
246246
defer h.Stop()
247247

248-
for i := 0; i < 1000; i++ {
248+
for i := 0; i < 100; i++ {
249249
lock.Lock()
250250
require.NoError(h.RegisterHealthCheck(fmt.Sprintf("check-%d", i), check))
251251
lock.Unlock()

api/server/allowed_hosts.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package server
5+
6+
import (
7+
"net"
8+
"net/http"
9+
"strings"
10+
11+
"github.com/ava-labs/avalanchego/utils/set"
12+
)
13+
14+
const wildcard = "*"
15+
16+
var _ http.Handler = (*allowedHostsHandler)(nil)
17+
18+
func filterInvalidHosts(
19+
handler http.Handler,
20+
allowed []string,
21+
) http.Handler {
22+
s := set.Set[string]{}
23+
24+
for _, host := range allowed {
25+
if host == wildcard {
26+
// wildcards match all hostnames, so just return the base handler
27+
return handler
28+
}
29+
s.Add(strings.ToLower(host))
30+
}
31+
32+
return &allowedHostsHandler{
33+
handler: handler,
34+
hosts: s,
35+
}
36+
}
37+
38+
// allowedHostsHandler is an implementation of http.Handler that validates the
39+
// http host header of incoming requests. This can prevent DNS rebinding attacks
40+
// which do not utilize CORS-headers. Http request host headers are validated
41+
// against a whitelist to determine whether the request should be dropped or
42+
// not.
43+
type allowedHostsHandler struct {
44+
handler http.Handler
45+
hosts set.Set[string]
46+
}
47+
48+
func (a *allowedHostsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
49+
// if the host header is missing we can serve this request because dns
50+
// rebinding attacks rely on this header
51+
if r.Host == "" {
52+
a.handler.ServeHTTP(w, r)
53+
return
54+
}
55+
56+
host, _, err := net.SplitHostPort(r.Host)
57+
if err != nil {
58+
// either invalid (too many colons) or no port specified
59+
host = r.Host
60+
}
61+
62+
if ipAddr := net.ParseIP(host); ipAddr != nil {
63+
// accept requests from ips
64+
a.handler.ServeHTTP(w, r)
65+
return
66+
}
67+
68+
// a specific hostname - we need to check the whitelist to see if we should
69+
// accept this r
70+
if a.hosts.Contains(strings.ToLower(host)) {
71+
a.handler.ServeHTTP(w, r)
72+
return
73+
}
74+
75+
http.Error(w, "invalid host specified", http.StatusForbidden)
76+
}

api/server/allowed_hosts_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package server
5+
6+
import (
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestAllowedHostsHandler_ServeHTTP(t *testing.T) {
15+
tests := []struct {
16+
name string
17+
allowed []string
18+
host string
19+
serve bool
20+
}{
21+
{
22+
name: "no host header",
23+
allowed: []string{"www.foobar.com"},
24+
host: "",
25+
serve: true,
26+
},
27+
{
28+
name: "ip",
29+
allowed: []string{"www.foobar.com"},
30+
host: "192.168.1.1",
31+
serve: true,
32+
},
33+
{
34+
name: "hostname not allowed",
35+
allowed: []string{"www.foobar.com"},
36+
host: "www.evil.com",
37+
},
38+
{
39+
name: "hostname allowed",
40+
allowed: []string{"www.foobar.com"},
41+
host: "www.foobar.com",
42+
serve: true,
43+
},
44+
{
45+
name: "wildcard",
46+
allowed: []string{"*"},
47+
host: "www.foobar.com",
48+
serve: true,
49+
},
50+
}
51+
52+
for _, test := range tests {
53+
t.Run(test.name, func(t *testing.T) {
54+
require := require.New(t)
55+
56+
baseHandler := &testHandler{}
57+
58+
httpAllowedHostsHandler := filterInvalidHosts(
59+
baseHandler,
60+
test.allowed,
61+
)
62+
63+
w := &httptest.ResponseRecorder{}
64+
r := httptest.NewRequest("", "/", nil)
65+
r.Host = test.host
66+
67+
httpAllowedHostsHandler.ServeHTTP(w, r)
68+
69+
if test.serve {
70+
require.True(baseHandler.called)
71+
return
72+
}
73+
74+
require.Equal(http.StatusForbidden, w.Code)
75+
})
76+
}
77+
}

api/server/server.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ func New(
119119
namespace string,
120120
registerer prometheus.Registerer,
121121
httpConfig HTTPConfig,
122+
allowedHosts []string,
122123
wrappers ...Wrapper,
123124
) (Server, error) {
124125
m, err := newMetrics(namespace, registerer)
@@ -127,10 +128,11 @@ func New(
127128
}
128129

129130
router := newRouter()
131+
allowedHostsHandler := filterInvalidHosts(router, allowedHosts)
130132
corsHandler := cors.New(cors.Options{
131133
AllowedOrigins: allowedOrigins,
132134
AllowCredentials: true,
133-
}).Handler(router)
135+
}).Handler(allowedHostsHandler)
134136
gzipHandler := gziphandler.GzipHandler(corsHandler)
135137
var handler http.Handler = http.HandlerFunc(
136138
func(w http.ResponseWriter, r *http.Request) {

codec/manager.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ const (
2323
)
2424

2525
var (
26-
ErrUnknownVersion = errors.New("unknown codec version")
27-
28-
errMarshalNil = errors.New("can't marshal nil pointer or interface")
29-
errUnmarshalNil = errors.New("can't unmarshal nil")
30-
errUnmarshalTooBig = errors.New("byte array exceeds maximum length")
31-
errCantPackVersion = errors.New("couldn't pack codec version")
32-
errCantUnpackVersion = errors.New("couldn't unpack codec version")
33-
errDuplicatedVersion = errors.New("duplicated codec version")
26+
ErrUnknownVersion = errors.New("unknown codec version")
27+
ErrMarshalNil = errors.New("can't marshal nil pointer or interface")
28+
ErrUnmarshalNil = errors.New("can't unmarshal nil")
29+
ErrUnmarshalTooBig = errors.New("byte array exceeds maximum length")
30+
ErrCantPackVersion = errors.New("couldn't pack codec version")
31+
ErrCantUnpackVersion = errors.New("couldn't unpack codec version")
32+
ErrDuplicatedVersion = errors.New("duplicated codec version")
3433
)
3534

3635
var _ Manager = (*manager)(nil)
@@ -43,7 +42,7 @@ type Manager interface {
4342
// Size returns the size, in bytes, of [value] when it's marshaled
4443
// using the codec with the given version.
4544
// RegisterCodec must have been called with that version.
46-
// If [value] is nil, returns [errMarshalNil]
45+
// If [value] is nil, returns [ErrMarshalNil]
4746
Size(version uint16, value interface{}) (int, error)
4847

4948
// Marshal the given value using the codec with the given version.
@@ -82,15 +81,15 @@ func (m *manager) RegisterCodec(version uint16, codec Codec) error {
8281
defer m.lock.Unlock()
8382

8483
if _, exists := m.codecs[version]; exists {
85-
return errDuplicatedVersion
84+
return ErrDuplicatedVersion
8685
}
8786
m.codecs[version] = codec
8887
return nil
8988
}
9089

9190
func (m *manager) Size(version uint16, value interface{}) (int, error) {
9291
if value == nil {
93-
return 0, errMarshalNil // can't marshal nil
92+
return 0, ErrMarshalNil // can't marshal nil
9493
}
9594

9695
m.lock.RLock()
@@ -110,7 +109,7 @@ func (m *manager) Size(version uint16, value interface{}) (int, error) {
110109
// To marshal an interface, [value] must be a pointer to the interface.
111110
func (m *manager) Marshal(version uint16, value interface{}) ([]byte, error) {
112111
if value == nil {
113-
return nil, errMarshalNil // can't marshal nil
112+
return nil, ErrMarshalNil // can't marshal nil
114113
}
115114

116115
m.lock.RLock()
@@ -126,7 +125,7 @@ func (m *manager) Marshal(version uint16, value interface{}) ([]byte, error) {
126125
}
127126
p.PackShort(version)
128127
if p.Errored() {
129-
return nil, errCantPackVersion // Should never happen
128+
return nil, ErrCantPackVersion // Should never happen
130129
}
131130
return p.Bytes, c.MarshalInto(value, &p)
132131
}
@@ -135,19 +134,19 @@ func (m *manager) Marshal(version uint16, value interface{}) ([]byte, error) {
135134
// interface.
136135
func (m *manager) Unmarshal(bytes []byte, dest interface{}) (uint16, error) {
137136
if dest == nil {
138-
return 0, errUnmarshalNil
137+
return 0, ErrUnmarshalNil
139138
}
140139

141140
if byteLen := len(bytes); byteLen > m.maxSize {
142-
return 0, fmt.Errorf("%w: %d > %d", errUnmarshalTooBig, byteLen, m.maxSize)
141+
return 0, fmt.Errorf("%w: %d > %d", ErrUnmarshalTooBig, byteLen, m.maxSize)
143142
}
144143

145144
p := wrappers.Packer{
146145
Bytes: bytes,
147146
}
148147
version := p.UnpackShort()
149148
if p.Errored() { // Make sure the codec version is correct
150-
return 0, errCantUnpackVersion
149+
return 0, ErrCantUnpackVersion
151150
}
152151

153152
m.lock.RLock()

codec/test_codec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ func TestTooLargeUnmarshal(codec GeneralCodec, t testing.TB) {
746746

747747
s := inner{}
748748
_, err := manager.Unmarshal(bytes, &s)
749-
require.ErrorIs(err, errUnmarshalTooBig)
749+
require.ErrorIs(err, ErrUnmarshalTooBig)
750750
}
751751

752752
type outerInterface interface {

config/config.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,12 @@ func getConsensusConfig(v *viper.Viper) snowball.Parameters {
108108
//
109109
// TODO: After the X-chain linearization use the
110110
// SnowVirtuousCommitThresholdKey as before.
111-
BetaVirtuous: v.GetInt(SnowRogueCommitThresholdKey),
112-
BetaRogue: v.GetInt(SnowRogueCommitThresholdKey),
113-
ConcurrentRepolls: v.GetInt(SnowConcurrentRepollsKey),
114-
OptimalProcessing: v.GetInt(SnowOptimalProcessingKey),
115-
MaxOutstandingItems: v.GetInt(SnowMaxProcessingKey),
116-
MaxItemProcessingTime: v.GetDuration(SnowMaxTimeProcessingKey),
117-
MixedQueryNumPushVdr: int(v.GetUint(SnowMixedQueryNumPushVdrKey)),
118-
MixedQueryNumPushNonVdr: int(v.GetUint(SnowMixedQueryNumPushNonVdrKey)),
111+
BetaVirtuous: v.GetInt(SnowRogueCommitThresholdKey),
112+
BetaRogue: v.GetInt(SnowRogueCommitThresholdKey),
113+
ConcurrentRepolls: v.GetInt(SnowConcurrentRepollsKey),
114+
OptimalProcessing: v.GetInt(SnowOptimalProcessingKey),
115+
MaxOutstandingItems: v.GetInt(SnowMaxProcessingKey),
116+
MaxItemProcessingTime: v.GetDuration(SnowMaxTimeProcessingKey),
119117
}
120118
}
121119

@@ -238,14 +236,15 @@ func getHTTPConfig(v *viper.Viper) (node.HTTPConfig, error) {
238236
MetricsAPIEnabled: v.GetBool(MetricsAPIEnabledKey),
239237
HealthAPIEnabled: v.GetBool(HealthAPIEnabledKey),
240238
},
241-
HTTPHost: v.GetString(HTTPHostKey),
242-
HTTPPort: uint16(v.GetUint(HTTPPortKey)),
243-
HTTPSEnabled: v.GetBool(HTTPSEnabledKey),
244-
HTTPSKey: httpsKey,
245-
HTTPSCert: httpsCert,
246-
APIAllowedOrigins: v.GetStringSlice(HTTPAllowedOrigins),
247-
ShutdownTimeout: v.GetDuration(HTTPShutdownTimeoutKey),
248-
ShutdownWait: v.GetDuration(HTTPShutdownWaitKey),
239+
HTTPHost: v.GetString(HTTPHostKey),
240+
HTTPPort: uint16(v.GetUint(HTTPPortKey)),
241+
HTTPSEnabled: v.GetBool(HTTPSEnabledKey),
242+
HTTPSKey: httpsKey,
243+
HTTPSCert: httpsCert,
244+
HTTPAllowedOrigins: v.GetStringSlice(HTTPAllowedOrigins),
245+
HTTPAllowedHosts: v.GetStringSlice(HTTPAllowedHostsKey),
246+
ShutdownTimeout: v.GetDuration(HTTPShutdownTimeoutKey),
247+
ShutdownWait: v.GetDuration(HTTPShutdownWaitKey),
249248
}
250249

251250
config.APIAuthConfig, err = getAPIAuthConfig(v)

config/flags.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ func addNodeFlags(fs *pflag.FlagSet) {
221221
fs.String(HTTPSCertFileKey, "", fmt.Sprintf("TLS certificate file for the HTTPs server. Ignored if %s is specified", HTTPSCertContentKey))
222222
fs.String(HTTPSCertContentKey, "", "Specifies base64 encoded TLS certificate for the HTTPs server")
223223
fs.String(HTTPAllowedOrigins, "*", "Origins to allow on the HTTP port. Defaults to * which allows all origins. Example: https://*.avax.network https://*.avax-test.network")
224+
fs.StringSlice(HTTPAllowedHostsKey, []string{"localhost"}, "List of acceptable host names in API requests. Provide the wildcard ('*') to accept requests from all hosts. API requests where the Host field is empty or an IP address will always be accepted. An API call whose HTTP Host field isn't acceptable will receive a 403 error code")
224225
fs.Duration(HTTPShutdownWaitKey, 0, "Duration to wait after receiving SIGTERM or SIGINT before initiating shutdown. The /health endpoint will return unhealthy during this duration")
225226
fs.Duration(HTTPShutdownTimeoutKey, 10*time.Second, "Maximum duration to wait for existing connections to complete during node shutdown")
226227
fs.Duration(HTTPReadTimeoutKey, 30*time.Second, "Maximum duration for reading the entire request, including the body. A zero or negative value means there will be no timeout")
@@ -318,8 +319,6 @@ func addNodeFlags(fs *pflag.FlagSet) {
318319
fs.Int(SnowOptimalProcessingKey, 10, "Optimal number of processing containers in consensus")
319320
fs.Int(SnowMaxProcessingKey, 256, "Maximum number of processing items to be considered healthy")
320321
fs.Duration(SnowMaxTimeProcessingKey, 30*time.Second, "Maximum amount of time an item should be processing and still be healthy")
321-
fs.Uint(SnowMixedQueryNumPushVdrKey, 10, fmt.Sprintf("If this node is a validator, when a container is inserted into consensus, send a Push Query to %s validators and a Pull Query to the others. Must be <= k.", SnowMixedQueryNumPushVdrKey))
322-
fs.Uint(SnowMixedQueryNumPushNonVdrKey, 0, fmt.Sprintf("If this node is not a validator, when a container is inserted into consensus, send a Push Query to %s validators and a Pull Query to the others. Must be <= k.", SnowMixedQueryNumPushNonVdrKey))
323322

324323
// ProposerVM
325324
fs.Bool(ProposerVMUseCurrentHeightKey, false, "Have the ProposerVM always report the last accepted P-chain block height")

config/keys.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const (
5454
HTTPSCertFileKey = "http-tls-cert-file"
5555
HTTPSCertContentKey = "http-tls-cert-file-content"
5656
HTTPAllowedOrigins = "http-allowed-origins"
57+
HTTPAllowedHostsKey = "http-allowed-hosts"
5758
HTTPShutdownTimeoutKey = "http-shutdown-timeout"
5859
HTTPShutdownWaitKey = "http-shutdown-wait"
5960
HTTPReadTimeoutKey = "http-read-timeout"
@@ -136,8 +137,6 @@ const (
136137
SnowOptimalProcessingKey = "snow-optimal-processing"
137138
SnowMaxProcessingKey = "snow-max-processing"
138139
SnowMaxTimeProcessingKey = "snow-max-time-processing"
139-
SnowMixedQueryNumPushVdrKey = "snow-mixed-query-num-push-vdr"
140-
SnowMixedQueryNumPushNonVdrKey = "snow-mixed-query-num-push-non-vdr"
141140
TrackSubnetsKey = "track-subnets"
142141
AdminAPIEnabledKey = "api-admin-enabled"
143142
InfoAPIEnabledKey = "api-info-enabled"

0 commit comments

Comments
 (0)