Skip to content

Commit

Permalink
Merge pull request from GHSA-284c-x8m7-9w5h
Browse files Browse the repository at this point in the history
* api token fix

Signed-off-by: Elena Kolevska <elena@kolevska.com>

* Add released notes for 1.13.3

Signed-off-by: Artur Souza <asouza.pro@gmail.com>

---------

Signed-off-by: Elena Kolevska <elena@kolevska.com>
Signed-off-by: Artur Souza <asouza.pro@gmail.com>
Co-authored-by: Elena Kolevska <elena@kolevska.com>
  • Loading branch information
artursouza and elena-kolevska committed May 20, 2024
1 parent 3cc0fc0 commit e0591e4
Show file tree
Hide file tree
Showing 17 changed files with 1,114 additions and 7 deletions.
80 changes: 80 additions & 0 deletions docs/release_notes/v1.13.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Dapr 1.13.3

This update includes bug fixes:

- [App API token forwarded from caller to receiving app](#app-api-token-forwarded-from-caller-to-receiving-app)
- [Upgrade Go version to 1.21.9](#upgrade-go-version-to-1219)
- [Placement server fails to disseminate placement tables](#placement-server-fails-to-disseminate-placement-tables)
- [Restore dapr_http_server_response_count HTTP metric](#restore-dapr_http_server_response_count-http-metric)

## App API token forwarded from caller to receiving app

### Problem

The caller sidecar is appending the *local* app API token to the *egress* request, thereby leaking the API token protecting the local app to the foreign sidecar.

### Impact

Receiving app can have access to the calling app's API token and make unauthorized calls directly to the originating app - in case it is listening on 0.0.0.0 or an accessible IP address.

### Root cause

A pull request accidentally added this change.

### Solution

Fixed the issue and added integration tests to verify and avoid future regressions.

## Upgrade Go version to 1.21.9

### Problem

Go version 1.21.8 or older are impacted by CVE-2023-45288.

### Impact

See https://nvd.nist.gov/vuln/detail/CVE-2023-45288

### Root cause

See https://nvd.nist.gov/vuln/detail/CVE-2023-45288

### Solution

Update Go version used to build Dapr.

## Placement server fails to disseminate placement tables

### Problem

In case of an error during dissemination of placement table to a sidecar instance, the dissemination to the remaining instances do not complete. See https://github.com/dapr/dapr/issues/7031

### Impact

Sidecars can run with an old copy of the dissemination table and cannot invoke the correct Dapr sidecar for a given actor instance.

### Root cause

During shutdown, all publish calls to the application where being cancelled.

### Solution

Check the return value of performTableDissemination for errors.

## Restore `dapr_http_server_response_count` HTTP metric

### Problem

An existing metrics was removed without deprecation notice, affecting users that relied on it. See https://github.com/dapr/dapr/issues/7642

### Impact

Users did not have this specific metric available anymore, potentially impacting their alerts and monitoring.

### Root cause

Metric removed without deprecation notice.

### Solution

Added the metric back.
11 changes: 6 additions & 5 deletions pkg/messaging/grpc_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ func (p *proxy) intercept(ctx context.Context, fullName string) (context.Context
if err != nil {
return ctx, nil, nil, nopTeardown, err
}

appMetadataToken := security.GetAppToken()
if appMetadataToken != "" {
outCtx = metadata.AppendToOutgoingContext(outCtx, securityConsts.APITokenHeader, appMetadataToken)
}

return outCtx, appClient.(*grpc.ClientConn), nil, nopTeardown, nil
}

Expand All @@ -139,11 +145,6 @@ func (p *proxy) intercept(ctx context.Context, fullName string) (context.Context
outCtx = p.telemetryFn(outCtx)
outCtx = metadata.AppendToOutgoingContext(outCtx, invokev1.CallerIDHeader, p.appID, invokev1.CalleeIDHeader, target.id)

appMetadataToken := security.GetAppToken()
if appMetadataToken != "" {
outCtx = metadata.AppendToOutgoingContext(outCtx, securityConsts.APITokenHeader, appMetadataToken)
}

pt := &grpcProxy.ProxyTarget{
ID: target.id,
Namespace: target.namespace,
Expand Down
9 changes: 7 additions & 2 deletions pkg/messaging/grpc_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,19 @@ func TestIntercept(t *testing.T) {
}, nil
})

t.Setenv(securityConsts.AppAPITokenEnvVar, "token1")

ctx := metadata.NewIncomingContext(context.TODO(), metadata.MD{diagnostics.GRPCProxyAppIDKey: []string{"a"}})
proxy := p.(*proxy)
_, conn, _, teardown, err := proxy.intercept(ctx, "/test")
ctx, conn, _, teardown, err := proxy.intercept(ctx, "/test")
defer teardown(true)

require.NoError(t, err)
assert.NotNil(t, conn)
assert.Equal(t, "a", conn.Target())

md, _ := metadata.FromOutgoingContext(ctx)
assert.Equal(t, "token1", md[securityConsts.APITokenHeader][0])
})

t.Run("proxy to a remote app", func(t *testing.T) {
Expand Down Expand Up @@ -231,7 +236,7 @@ func TestIntercept(t *testing.T) {
assert.Equal(t, "b", md["a"][0])
assert.Equal(t, "a", md[invokev1.CallerIDHeader][0])
assert.Equal(t, "b", md[invokev1.CalleeIDHeader][0])
assert.Equal(t, "token1", md[securityConsts.APITokenHeader][0])
assert.NotContains(t, md, securityConsts.APITokenHeader)
})

t.Run("access policies applied", func(t *testing.T) {
Expand Down
6 changes: 6 additions & 0 deletions tests/integration/framework/process/daprd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,3 +287,9 @@ func WithSocket(t *testing.T, socket *socket.Socket) Option {
"DAPR_COMPONENTS_SOCKETS_FOLDER", socket.Directory(),
))
}

func WithAppAPIToken(t *testing.T, token string) Option {
return WithExecOptions(exec.WithEnvVars(t,
"APP_API_TOKEN", token,
))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
Copyright 2024 The Dapr Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implieh.
See the License for the specific language governing permissions and
limitations under the License.
*/

package appapitoken

import (
"context"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/anypb"

commonv1 "github.com/dapr/dapr/pkg/proto/common/v1"
runtimev1 "github.com/dapr/dapr/pkg/proto/runtime/v1"
"github.com/dapr/dapr/tests/integration/framework"
"github.com/dapr/dapr/tests/integration/framework/process/daprd"
"github.com/dapr/dapr/tests/integration/framework/process/grpc/app"
"github.com/dapr/dapr/tests/integration/suite"
testpb "github.com/dapr/dapr/tests/integration/suite/daprd/serviceinvocation/grpc/proto"
)

func init() {
suite.Register(new(remotebothtokens))
}

type remotebothtokens struct {
daprd1 *daprd.Daprd
daprd2 *daprd.Daprd
ch chan metadata.MD
}

func (b *remotebothtokens) Setup(t *testing.T) []framework.Option {
fn, ch := newServer()
b.ch = ch
app := app.New(t,
app.WithRegister(fn),
app.WithOnInvokeFn(func(ctx context.Context, _ *commonv1.InvokeRequest) (*commonv1.InvokeResponse, error) {
md, ok := metadata.FromIncomingContext(ctx)
require.True(t, ok)
b.ch <- md
return new(commonv1.InvokeResponse), nil
}),
)

b.daprd1 = daprd.New(t,
daprd.WithAppID("app1"),
daprd.WithAppProtocol("grpc"),
daprd.WithAppAPIToken(t, "abc"),
)

b.daprd2 = daprd.New(t,
daprd.WithAppProtocol("grpc"),
daprd.WithAppAPIToken(t, "def"),
daprd.WithAppPort(app.Port(t)),
)

return []framework.Option{
framework.WithProcesses(app, b.daprd1, b.daprd2),
}
}

func (b *remotebothtokens) Run(t *testing.T, ctx context.Context) {
b.daprd1.WaitUntilRunning(t, ctx)
b.daprd2.WaitUntilRunning(t, ctx)

client := testpb.NewTestServiceClient(b.daprd1.GRPCConn(t, ctx))
ctx = metadata.AppendToOutgoingContext(ctx, "dapr-app-id", b.daprd2.AppID())
_, err := client.Ping(ctx, new(testpb.PingRequest))
require.NoError(t, err)

select {
case md := <-b.ch:
require.Equal(t, []string{"def"}, md.Get("dapr-api-token"))
case <-time.After(10 * time.Second):
assert.Fail(t, "timed out waiting for metadata")
}

dclient := b.daprd1.GRPCClient(t, ctx)
_, err = dclient.InvokeService(ctx, &runtimev1.InvokeServiceRequest{
Id: b.daprd2.AppID(),
Message: &commonv1.InvokeRequest{
Method: "helloworld",
Data: new(anypb.Any),
HttpExtension: &commonv1.HTTPExtension{Verb: commonv1.HTTPExtension_GET},
},
})
require.NoError(t, err)

select {
case md := <-b.ch:
require.Equal(t, []string{"def"}, md.Get("dapr-api-token"))
case <-time.After(5 * time.Second):
assert.Fail(t, "timed out waiting for metadata")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
Copyright 2024 The Dapr Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implieh.
See the License for the specific language governing permissions and
limitations under the License.
*/

package appapitoken

import (
"context"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/anypb"

commonv1 "github.com/dapr/dapr/pkg/proto/common/v1"
runtimev1 "github.com/dapr/dapr/pkg/proto/runtime/v1"
"github.com/dapr/dapr/tests/integration/framework"
"github.com/dapr/dapr/tests/integration/framework/process/daprd"
"github.com/dapr/dapr/tests/integration/framework/process/grpc/app"
"github.com/dapr/dapr/tests/integration/suite"
testpb "github.com/dapr/dapr/tests/integration/suite/daprd/serviceinvocation/grpc/proto"
)

func init() {
suite.Register(new(remotereceiverhastoken))
}

type remotereceiverhastoken struct {
daprd1 *daprd.Daprd
daprd2 *daprd.Daprd
ch chan metadata.MD
}

func (r *remotereceiverhastoken) Setup(t *testing.T) []framework.Option {
fn, ch := newServer()
r.ch = ch
app := app.New(t,
app.WithRegister(fn),
app.WithOnInvokeFn(func(ctx context.Context, _ *commonv1.InvokeRequest) (*commonv1.InvokeResponse, error) {
md, ok := metadata.FromIncomingContext(ctx)
require.True(t, ok)
r.ch <- md
return new(commonv1.InvokeResponse), nil
}),
)

r.daprd1 = daprd.New(t)
r.daprd2 = daprd.New(t,
daprd.WithAppProtocol("grpc"),
daprd.WithAppAPIToken(t, "abc"),
daprd.WithAppPort(app.Port(t)),
)

return []framework.Option{
framework.WithProcesses(app, r.daprd1, r.daprd2),
}
}

func (r *remotereceiverhastoken) Run(t *testing.T, ctx context.Context) {
r.daprd1.WaitUntilRunning(t, ctx)
r.daprd2.WaitUntilRunning(t, ctx)

client := testpb.NewTestServiceClient(r.daprd1.GRPCConn(t, ctx))
ctx = metadata.AppendToOutgoingContext(ctx, "dapr-app-id", r.daprd2.AppID())
_, err := client.Ping(ctx, new(testpb.PingRequest))
require.NoError(t, err)

select {
case md := <-r.ch:
require.Equal(t, []string{"abc"}, md.Get("dapr-api-token"))
case <-time.After(5 * time.Second):
assert.Fail(t, "timed out waiting for metadata")
}

dclient := r.daprd1.GRPCClient(t, ctx)
_, err = dclient.InvokeService(ctx, &runtimev1.InvokeServiceRequest{
Id: r.daprd2.AppID(),
Message: &commonv1.InvokeRequest{
Method: "helloworld",
Data: new(anypb.Any),
HttpExtension: &commonv1.HTTPExtension{Verb: commonv1.HTTPExtension_GET},
},
})
require.NoError(t, err)

select {
case md := <-r.ch:
require.Equal(t, []string{"abc"}, md.Get("dapr-api-token"))
case <-time.After(5 * time.Second):
assert.Fail(t, "timed out waiting for metadata")
}
}
Loading

0 comments on commit e0591e4

Please sign in to comment.