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

@@ -9,22 +9,24 @@ import (
"testing"
"time"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/zerrors"
)
var (
prepareTargetsStmt = `SELECT projections.targets1.id,` +
` projections.targets1.creation_date,` +
` projections.targets1.change_date,` +
` projections.targets1.resource_owner,` +
` projections.targets1.name,` +
` projections.targets1.target_type,` +
` projections.targets1.timeout,` +
` projections.targets1.endpoint,` +
` projections.targets1.interrupt_on_error,` +
prepareTargetsStmt = `SELECT projections.targets2.id,` +
` projections.targets2.creation_date,` +
` projections.targets2.change_date,` +
` projections.targets2.resource_owner,` +
` projections.targets2.name,` +
` projections.targets2.target_type,` +
` projections.targets2.timeout,` +
` projections.targets2.endpoint,` +
` projections.targets2.interrupt_on_error,` +
` projections.targets2.signing_key,` +
` COUNT(*) OVER ()` +
` FROM projections.targets1`
` FROM projections.targets2`
prepareTargetsCols = []string{
"id",
"creation_date",
@@ -35,19 +37,21 @@ var (
"timeout",
"endpoint",
"interrupt_on_error",
"signing_key",
"count",
}
prepareTargetStmt = `SELECT projections.targets1.id,` +
` projections.targets1.creation_date,` +
` projections.targets1.change_date,` +
` projections.targets1.resource_owner,` +
` projections.targets1.name,` +
` projections.targets1.target_type,` +
` projections.targets1.timeout,` +
` projections.targets1.endpoint,` +
` projections.targets1.interrupt_on_error` +
` FROM projections.targets1`
prepareTargetStmt = `SELECT projections.targets2.id,` +
` projections.targets2.creation_date,` +
` projections.targets2.change_date,` +
` projections.targets2.resource_owner,` +
` projections.targets2.name,` +
` projections.targets2.target_type,` +
` projections.targets2.timeout,` +
` projections.targets2.endpoint,` +
` projections.targets2.interrupt_on_error,` +
` projections.targets2.signing_key` +
` FROM projections.targets2`
prepareTargetCols = []string{
"id",
"creation_date",
@@ -58,6 +62,7 @@ var (
"timeout",
"endpoint",
"interrupt_on_error",
"signing_key",
}
)
@@ -102,6 +107,12 @@ func Test_TargetPrepares(t *testing.T) {
1 * time.Second,
"https://example.com",
true,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
},
),
@@ -123,6 +134,12 @@ func Test_TargetPrepares(t *testing.T) {
Timeout: 1 * time.Second,
Endpoint: "https://example.com",
InterruptOnError: true,
signingKey: &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
},
},
@@ -145,6 +162,12 @@ func Test_TargetPrepares(t *testing.T) {
1 * time.Second,
"https://example.com",
true,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
{
"id-2",
@@ -156,6 +179,12 @@ func Test_TargetPrepares(t *testing.T) {
1 * time.Second,
"https://example.com",
false,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
{
"id-3",
@@ -167,6 +196,12 @@ func Test_TargetPrepares(t *testing.T) {
1 * time.Second,
"https://example.com",
false,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
},
),
@@ -188,6 +223,12 @@ func Test_TargetPrepares(t *testing.T) {
Timeout: 1 * time.Second,
Endpoint: "https://example.com",
InterruptOnError: true,
signingKey: &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
{
ObjectDetails: domain.ObjectDetails{
@@ -201,6 +242,12 @@ func Test_TargetPrepares(t *testing.T) {
Timeout: 1 * time.Second,
Endpoint: "https://example.com",
InterruptOnError: false,
signingKey: &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
{
ObjectDetails: domain.ObjectDetails{
@@ -214,6 +261,12 @@ func Test_TargetPrepares(t *testing.T) {
Timeout: 1 * time.Second,
Endpoint: "https://example.com",
InterruptOnError: false,
signingKey: &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
},
},
@@ -270,6 +323,12 @@ func Test_TargetPrepares(t *testing.T) {
1 * time.Second,
"https://example.com",
true,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
),
},
@@ -285,6 +344,12 @@ func Test_TargetPrepares(t *testing.T) {
Timeout: 1 * time.Second,
Endpoint: "https://example.com",
InterruptOnError: true,
signingKey: &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "encKey",
Crypted: []byte("crypted"),
},
},
},
{