feat: allow using a local RSA key for machine keys (#7671)

* Allow using a local RSA key for machine keys

* Add check for key validity

* Fix naming error

* docs: provide translations of invalid key

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Ari 2024-04-23 11:38:07 +02:00 committed by GitHub
parent df50c3835b
commit e46dd121cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 80 additions and 13 deletions

View File

@ -774,13 +774,22 @@ func (s *Server) ListMachineKeys(ctx context.Context, req *mgmt_pb.ListMachineKe
func (s *Server) AddMachineKey(ctx context.Context, req *mgmt_pb.AddMachineKeyRequest) (*mgmt_pb.AddMachineKeyResponse, error) {
machineKey := AddMachineKeyRequestToCommand(req, authz.GetCtxData(ctx).OrgID)
// If there is no pubkey supplied, then AddUserMachineKey will generate a new one
pubkeySupplied := len(machineKey.PublicKey) > 0
details, err := s.command.AddUserMachineKey(ctx, machineKey)
if err != nil {
return nil, err
}
keyDetails, err := machineKey.Detail()
if err != nil {
return nil, err
// Return key details only if the pubkey wasn't supplied, otherwise the user already has
// private key locally
var keyDetails []byte
if !pubkeySupplied {
var err error
keyDetails, err = machineKey.Detail()
if err != nil {
return nil, err
}
}
return &mgmt_pb.AddMachineKeyResponse{
KeyId: machineKey.KeyID,

View File

@ -237,6 +237,7 @@ func AddMachineKeyRequestToCommand(req *mgmt_pb.AddMachineKeyRequest, resourceOw
},
ExpirationDate: expDate,
Type: authn.KeyTypeToDomain(req.Type),
PublicKey: req.PublicKey,
}
}

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/zitadel/zitadel/internal/command/preparation"
"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"
@ -78,6 +79,12 @@ func (key *MachineKey) valid() (err error) {
if err := key.content(); err != nil {
return err
}
// If a key is supplied, it should be a valid public key
if len(key.PublicKey) > 0 {
if _, err := crypto.BytesToPublicKey(key.PublicKey); err != nil {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-5F3h1", "Errors.User.Machine.Key.Invalid")
}
}
key.ExpirationDate, err = domain.ValidateExpirationDate(key.ExpirationDate)
return err
}

View File

@ -18,6 +18,16 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
const fakePubkey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp4qNBuUu/HekF2E5bOtA
oEL76zS0NQdZL3ByEJ3hZplJhE30ITPIOLW3+uaMMM+obl/LLapwG2vdhvutQtx/
FOLJmXysbG3RL9zjXDBT5IE+nGFC7ctsi5FGbHQbAm45E3HHCSk7gfmTy9hxyk1K
GsyU8BDeOWasJO6aeXqpOnRM8vw/fY+6mHVC9CxcIroSfrIabFGe/mP6qpBGeFSn
APymBc/8lca4JaPv2/u/rBhnaAHZiUuCS1+MonWelOb+MSfq48VgtpiaYIVY9szI
esorA6EJ9pO17ROEUpX5wP5Oir+yGJU27jSvLCjvK6fOFX+OwUM9L8047JKoo+Nf
PwIDAQAB
-----END PUBLIC KEY-----`
func TestCommands_AddMachineKey(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
@ -145,7 +155,7 @@ func TestCommands_AddMachineKey(t *testing.T) {
"key1",
domain.AuthNKeyTypeJSON,
time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
[]byte("public"),
[]byte(fakePubkey),
),
),
),
@ -161,14 +171,14 @@ func TestCommands_AddMachineKey(t *testing.T) {
},
Type: domain.AuthNKeyTypeJSON,
ExpirationDate: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
PublicKey: []byte("public"),
PublicKey: []byte(fakePubkey),
},
},
res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
key: true,
key: false,
},
},
{
@ -194,7 +204,7 @@ func TestCommands_AddMachineKey(t *testing.T) {
"key1",
domain.AuthNKeyTypeJSON,
time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
[]byte("public"),
[]byte(fakePubkey),
),
),
),
@ -210,14 +220,35 @@ func TestCommands_AddMachineKey(t *testing.T) {
KeyID: "key1",
Type: domain.AuthNKeyTypeJSON,
ExpirationDate: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC),
PublicKey: []byte("public"),
PublicKey: []byte(fakePubkey),
},
},
res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
key: true,
key: false,
},
},
{
"key added with invalid public key",
fields{
eventstore: eventstoreExpect(t),
},
args{
ctx: context.Background(),
key: &MachineKey{
ObjectRoot: models.ObjectRoot{
AggregateID: "user1",
ResourceOwner: "org1",
},
KeyID: "key1",
Type: domain.AuthNKeyTypeJSON,
PublicKey: []byte("incorrect"),
},
},
res{
err: zerrors.IsErrorInvalidArgument,
},
},
}
@ -237,9 +268,8 @@ func TestCommands_AddMachineKey(t *testing.T) {
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
if tt.res.key {
assert.NotEqual(t, "", tt.args.key.PrivateKey)
}
receivedKey := len(tt.args.key.PrivateKey) > 0
assert.Equal(t, tt.res.key, receivedKey)
}
})
}

View File

@ -111,6 +111,7 @@ Errors:
Key:
NotFound: Машинният ключ не е намерен
AlreadyExisting: Машинният ключ вече съществува
Invalid: Публичният ключ не е валиден RSA публичен ключ във формат PKIX с PEM кодиране
Secret:
NotExisting: Тайната не съществува
Invalid: Тайната е невалидна

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: Klíč stroje nenalezen
AlreadyExisting: Klíč stroje již existuje
Invalid: Veřejný klíč není platný veřejný klíč RSA ve formátu PKIX s kódováním PEM
Secret:
NotExisting: Tajemství neexistuje
Invalid: Tajemství je neplatné

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: Maschinen Schlüssel nicht gefunden
AlreadyExisting: Machine Schlüssel exisiert bereits
Invalid: Der öffentliche Schlüssel ist kein gültiger öffentlicher RSA-Schlüssel im PKIX-Format mit PEM-Kodierung
Secret:
NotExisting: Secret existiert nicht
Invalid: Secret ist ungültig

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: Machine key not found
AlreadyExisting: Machine key already existing
Invalid: Public key is not a valid RSA public key in PKIX format with PEM encoding
Secret:
NotExisting: Secret doesn't exist
Invalid: Secret is invalid

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: Clave de máquina no encontrada
AlreadyExisting: La clave de máquina ya existe
Invalid: La clave pública no es una clave pública RSA válida en formato PKIX con codificación PEM
Secret:
NotExisting: El secreto no existe
Invalid: El secret no es válido

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: Clé de la machine non trouvée
AlreadyExisting: Clé de la machine déjà existante
Invalid: La clé publique n'est pas une clé publique RSA valide au format PKIX avec encodage PEM
Secret:
NotExisting: Secret n'existe pas
Invalid: Secret n'est pas valide

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: Chiave macchina non trovato
AlreadyExisting: Chiave macchina già esistente
Invalid: La chiave pubblica non è una chiave pubblica RSA valida in formato PKIX con codifica PEM
Secret:
NotExisting: Secret non esiste
Invalid: Secret non è valido

View File

@ -102,6 +102,7 @@ Errors:
Key:
NotFound: マシーンキーが見つかりません
AlreadyExisting: すでに存在しているマシーンキーです
Invalid: 公開キーは、PEM エンコードを使用した PKIX 形式の有効な RSA 公開キーではありません
Secret:
NotExisting: シークレットは存在しません
Invalid: 無効なシークレットです

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: Machine key не е пронајден
AlreadyExisting: Machine key веќе постои
Invalid: Јавниот клуч не е валиден јавен клуч RSA во формат PKIX со PEM кодирање
Secret:
NotExisting: Тајната не постои
Invalid: Тајната е невалидна

View File

@ -108,6 +108,7 @@ Errors:
Key:
NotFound: Machine sleutel niet gevonden
AlreadyExisting: Machine sleutel al bestaand
Invalid: De openbare sleutel is geen geldige openbare RSA-sleutel in PKIX-indeling met PEM-codering
Secret:
NotExisting: Geheim bestaat niet
Invalid: Geheim is ongeldig

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: Klucz maszyny nie znaleziony
AlreadyExisting: Klucz maszyny już istnieje
Invalid: Klucz publiczny nie jest prawidłowym kluczem publicznym RSA w formacie PKIX z kodowaniem PEM
Secret:
NotExisting: Sekret nie istnieje
Invalid: Sekret jest nieprawidłowy

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: Chave de máquina não encontrada
AlreadyExisting: Chave de máquina já existe
Invalid: A chave pública não é uma chave pública RSA válida no formato PKIX com codificação PEM
Secret:
NotExisting: Segredo não existe
Invalid: Segredo é inválido

View File

@ -110,6 +110,7 @@ Errors:
Key:
NotFound: Машинный ключ не найден
AlreadyExisting: Машинный ключ уже существует
Invalid: Открытый ключ не является допустимым открытым ключом RSA в формате PKIX с кодировкой PEM
Secret:
NotExisting: Ключ не существует
Invalid: Ключ недействителен

View File

@ -109,6 +109,7 @@ Errors:
Key:
NotFound: 未找到机器密钥
AlreadyExisting: 已有的机器钥匙
Invalid: 公钥不是采用 PEM 编码的 PKIX 格式的有效 RSA 公钥
Secret:
NotExisting: 秘密并不存在
Invalid: 秘密是无效的

View File

@ -1734,7 +1734,7 @@ service ManagementService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Create Key for machine user";
description: "A new key is generated and will be returned in the response. Make sure to store the returned key. Machine keys are used to authenticate with jwt profile."
description: "If a public key is not supplied, a new key is generated and will be returned in the response. Make sure to store the returned key. If an RSA public key is supplied, the private key is omitted from the response. Machine keys are used to authenticate with jwt profile."
tags: "Users";
tags: "User Machine";
responses: {
@ -8504,6 +8504,12 @@ message AddMachineKeyRequest {
description: "The date the key will expire and no logins will be possible";
}
];
bytes public_key = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1...\"";
description: "Optionally provide a public key of your own generated RSA private key.";
}
];
}
message AddMachineKeyResponse {