feat: action v2 signing (#8779)

# Which Problems Are Solved

The action v2 messages were didn't contain anything providing security
for the sent content.

# How the Problems Are Solved

Each Target now has a SigningKey, which can also be newly generated
through the API and returned at creation and through the Get-Endpoints.
There is now a HTTP header "Zitadel-Signature", which is generated with
the SigningKey and Payload, and also contains a timestamp to check with
a tolerance if the message took to long to sent.

# Additional Changes

The functionality to create and check the signature is provided in the
pkg/actions package, and can be reused in the SDK.

# Additional Context

Closes #7924

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Stefan Benz
2024-11-28 11:06:52 +01:00
committed by GitHub
parent 8537805ea5
commit 7caa43ab23
37 changed files with 745 additions and 122 deletions

View File

@@ -8,6 +8,7 @@ import (
"github.com/muhlemmer/gu"
"github.com/stretchr/testify/assert"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
@@ -19,8 +20,10 @@ import (
func TestCommands_AddTarget(t *testing.T) {
type fields struct {
eventstore func(t *testing.T) *eventstore.Eventstore
idGenerator id.Generator
eventstore func(t *testing.T) *eventstore.Eventstore
idGenerator id.Generator
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
defaultSecretGenerators *SecretGenerators
}
type args struct {
ctx context.Context
@@ -132,10 +135,18 @@ func TestCommands_AddTarget(t *testing.T) {
"https://example.com",
time.Second,
false,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("12345678"),
},
),
),
),
idGenerator: mock.ExpectID(t, "id1"),
idGenerator: mock.ExpectID(t, "id1"),
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("12345678", time.Hour),
defaultSecretGenerators: &SecretGenerators{},
},
args{
ctx: context.Background(),
@@ -186,7 +197,9 @@ func TestCommands_AddTarget(t *testing.T) {
targetAddEvent("id1", "instance"),
),
),
idGenerator: mock.ExpectID(t, "id1"),
idGenerator: mock.ExpectID(t, "id1"),
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("12345678", time.Hour),
defaultSecretGenerators: &SecretGenerators{},
},
args{
ctx: context.Background(),
@@ -219,7 +232,9 @@ func TestCommands_AddTarget(t *testing.T) {
}(),
),
),
idGenerator: mock.ExpectID(t, "id1"),
idGenerator: mock.ExpectID(t, "id1"),
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("12345678", time.Hour),
defaultSecretGenerators: &SecretGenerators{},
},
args{
ctx: context.Background(),
@@ -244,8 +259,10 @@ func TestCommands_AddTarget(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Commands{
eventstore: tt.fields.eventstore(t),
idGenerator: tt.fields.idGenerator,
eventstore: tt.fields.eventstore(t),
idGenerator: tt.fields.idGenerator,
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
}
details, err := c.AddTarget(tt.args.ctx, tt.args.add, tt.args.resourceOwner)
if tt.res.err == nil {
@@ -264,7 +281,9 @@ func TestCommands_AddTarget(t *testing.T) {
func TestCommands_ChangeTarget(t *testing.T) {
type fields struct {
eventstore func(t *testing.T) *eventstore.Eventstore
eventstore func(t *testing.T) *eventstore.Eventstore
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
defaultSecretGenerators *SecretGenerators
}
type args struct {
ctx context.Context
@@ -510,10 +529,18 @@ func TestCommands_ChangeTarget(t *testing.T) {
target.ChangeTargetType(domain.TargetTypeCall),
target.ChangeTimeout(10 * time.Second),
target.ChangeInterruptOnError(true),
target.ChangeSigningKey(&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("12345678"),
}),
},
),
),
),
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("12345678", time.Hour),
defaultSecretGenerators: &SecretGenerators{},
},
args{
ctx: context.Background(),
@@ -521,11 +548,12 @@ func TestCommands_ChangeTarget(t *testing.T) {
ObjectRoot: models.ObjectRoot{
AggregateID: "id1",
},
Name: gu.Ptr("name2"),
Endpoint: gu.Ptr("https://example2.com"),
TargetType: gu.Ptr(domain.TargetTypeCall),
Timeout: gu.Ptr(10 * time.Second),
InterruptOnError: gu.Ptr(true),
Name: gu.Ptr("name2"),
Endpoint: gu.Ptr("https://example2.com"),
TargetType: gu.Ptr(domain.TargetTypeCall),
Timeout: gu.Ptr(10 * time.Second),
InterruptOnError: gu.Ptr(true),
ExpirationSigningKey: true,
},
resourceOwner: "instance",
},
@@ -540,7 +568,9 @@ func TestCommands_ChangeTarget(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Commands{
eventstore: tt.fields.eventstore(t),
eventstore: tt.fields.eventstore(t),
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
}
details, err := c.ChangeTarget(tt.args.ctx, tt.args.change, tt.args.resourceOwner)
if tt.res.err == nil {