paint-brush
Mise à l'échelle d'Ory Hydra à deux milliards de flux OAuth2 mensuels sur une seule base de données PostgreSQLpar@orydev
493 lectures
493 lectures

Mise à l'échelle d'Ory Hydra à deux milliards de flux OAuth2 mensuels sur une seule base de données PostgreSQL

par Ory6m2023/08/04
Read on Terminal Reader

Trop long; Pour lire

Ory Hydra est un serveur open-source OAuth2 et OpenID Connect populaire qui fournit une authentification et une autorisation sécurisées pour les applications. L'un des principaux défis dans la création d'un serveur OAuth2 évolutif et performant est la gestion de la couche de persistance, qui implique le stockage et la récupération de données à partir d'une base de données.
featured image - Mise à l'échelle d'Ory Hydra à deux milliards de flux OAuth2 mensuels sur une seule base de données PostgreSQL
Ory HackerNoon profile picture
0-item

Ory Hydra est un serveur open-source OAuth2 et OpenID Connect populaire qui fournit une authentification et une autorisation sécurisées pour les applications. L'un des principaux défis dans la création d'un serveur OAuth2 évolutif et performant est la gestion de la couche de persistance, qui implique le stockage et la récupération de données à partir d'une base de données.

Motivation

Ory a été approché par un fournisseur de services populaire pour optimiser les performances de son système d'authentification à charge élevée. Ils ont souvent eu du mal à faire face à l'énorme afflux d'autorisations pendant les heures de pointe (plus de 600 connexions/sec). À la recherche d'une autre solution, ils ont commencé à évaluer Ory Hydra, en cherchant s'il pouvait gérer ce montant de subventions. Après avoir contacté l'équipe, nous avons commencé à rechercher des moyens d'améliorer les performances globales pour rendre Ory Hydra plus rapide et plus évolutif que jamais. La clé du succès a été de repenser certaines parties de la couche de persistance d'Hydra pour réduire le trafic d'écriture vers la base de données, en passant à un flux OAuth2 transitoire.

Passage à un flux OAuth2 transitoire

L'une des parties essentielles de ce travail consistait à déplacer une grande partie de l'état de flux OAuth2 transitoire, qui est échangé entre les trois parties impliquées dans un flux OAuth2, du serveur vers le client. Au lieu de conserver l'état transitoire dans la base de données, l'état est désormais transmis entre les parties sous forme de cookies codés AEAD ou de paramètres de requête codés AEAD dans les URL de redirection. AEAD signifie chiffrement authentifié avec données associées, ce qui signifie que les données sont confidentielles et ne peuvent pas non plus être altérées sans connaître une clé secrète (symétrique).


Le flux n'est alors persisté dans la base de données qu'une seule fois lorsque le consentement final est donné.


Ce changement présente plusieurs avantages. Tout d'abord, cela réduit la quantité de données qui doit être stockée dans la base de données, ce qui réduit à son tour le trafic d'écriture. Deuxièmement, cela élimine le besoin de plusieurs index sur la table de flux qui étaient auparavant utilisés lors de l'échange.

Le flux d'octroi de code d'autorisation OAuth2 en détail

La partie pertinente du flux OAuth2 que nous voulions optimiser est un échange entre le client (agissant au nom d'un utilisateur), Hydra (le serveur d'autorisation OAuth2 d'Ory) et les écrans de connexion et de consentement. Lorsqu'un client demande un code d'autorisation via Authorization Code Grant , l'utilisateur sera d'abord redirigé vers l'interface utilisateur de connexion pour s'authentifier, puis vers l'interface utilisateur de consentement pour accorder l'accès aux données de l'utilisateur (telles que l'adresse e-mail ou les informations de profil).


Vous trouverez ci-dessous un diagramme de séquence de l'échange. Observez que chaque interface utilisateur reçoit un CHALLENGE dans le cadre des paramètres d'URL (étapes 3 et 12), puis utilise ce CHALLENGE comme paramètre pour récupérer plus d'informations (étapes 4 et 13). Enfin, les deux interfaces utilisateur acceptent ou rejettent la demande de l'utilisateur, généralement en fonction de l'interaction de l'utilisateur avec l'interface utilisateur (étapes 6 à 8 et 15 à 17). Ce contrat d'API maintient Ory Hydra sans tête et découplé des interfaces utilisateur personnalisées.

Diagramme de séquençage

Optimisation : Passage du flux encodé en AEAD dans les paramètres d'URL

Pour réduire l'accès à la base de données, nous passons maintenant comme LOGIN_CHALLENGE , LOGIN_VERIFIER , CONSENT_CHALLENGE et CONSENT_VERIFIER un flux codé AEAD. De cette façon, nous comptons sur les parties impliquées dans le flux OAuth2 pour transmettre l'état pertinent.

Avant

Après

Les défis et vérificateurs de connexion et de consentement sont des UUID aléatoires stockés dans la base de données.

Les défis et vérificateurs de connexion et de consentement sont le flux codé AEAD.

Accepter ou rejeter une demande de l'interface utilisateur implique une recherche dans la base de données pour le défi spécifique.

L'acceptation ou le rejet d'une demande de l'interface utilisateur implique le décryptage du flux dans le défi et la génération d'un flux mis à jour dans le cadre du vérificateur.

Mise en œuvre

Étant donné qu'Ory Hydra est open source, vous pouvez consulter les modifications de code dans les référentiels Ory GitHub. Il s'agit du commit pertinent.


C'est ici que nous encodons le flux dans les défis spécifiques et les vérificateurs :

 // ToLoginChallenge converts the flow into a login challenge. func (f *Flow) ToLoginChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) { return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsLoginChallenge) } // ToLoginVerifier converts the flow into a login verifier. func (f *Flow) ToLoginVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) { return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsLoginVerifier) } // ToConsentChallenge converts the flow into a consent challenge. func (f *Flow) ToConsentChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) { return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsConsentChallenge) } // ToConsentVerifier converts the flow into a consent verifier. func (f *Flow) ToConsentVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) { return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsConsentVerifier) }


Dans le persister (notre référentiel de base de données), nous décodons ensuite le flux contenu dans le défi. Par exemple, voici le code pour gérer une contestation de consentement :

 func (p *Persister) GetFlowByConsentChallenge(ctx context.Context, challenge string) (*flow.Flow, error) { ctx, span := prTracer(ctx).Tracer().Start(ctx, "persistence.sql.GetFlowByConsentChallenge") defer span.End() // challenge contains the flow. f, err := flowctx.Decode[flow.Flow](ctx, prFlowCipher(), challenge, flowctx.AsConsentChallenge) if err != nil { return nil, errorsx.WithStack(x.ErrNotFound) } if f.NID != p.NetworkID(ctx) { return nil, errorsx.WithStack(x.ErrNotFound) } if f.RequestedAt.Add(p.config.ConsentRequestMaxAge(ctx)).Before(time.Now()) { return nil, errorsx.WithStack(fosite.ErrRequestUnauthorized.WithHint("The consent request has expired, please try again.")) } return f, nil }


Regardons l'impact des modifications par rapport au code sans optimisations :

Barchart comparing baseline with improved performance

Les flux sont maintenant beaucoup plus rapides et parlent moins à la base de données.

Des indices améliorés entraînent de nouvelles améliorations des performances

En introduisant un nouvel index sur la table hydra_oauth2_flow , nous avons pu augmenter le débit et réduire l'utilisation du processeur sur PostgreSQL. La capture d'écran ci-dessous montre l'exécution des tests de performance sans les indices améliorés où l'utilisation du processeur atteint 100 %, et avec des indices améliorés, où l'utilisation du processeur reste inférieure à 10 %.

CPU and memory with and without index usage

Avec les indices nouvellement ajoutés, l'utilisation du processeur (barres vertes) est supprimée, ce qui réduit la probabilité de BufferLocks et des problèmes associés :

bufferlock events with and without index usage

Résultats

Les modifications du code et de la base de données ont réduit le nombre total d'allers-retours vers la base de données de 4 à 5 fois (selon la quantité de mise en cache effectuée) et les écritures de base de données réduites d'environ 50 %.

Repères

Analyse comparative de la nouvelle implémentation sur Microsoft Azure avec les spécifications suivantes :

Prestations de service

Configuration

Connexions SQL maximales totales

Remarques

Ory Hydra Consent App OAuth2 Client App rakyll/hey (outil de référence http)

3x Standard_D32as_v4 ; Centre-sud des États-Unis 5x Standard_D8s_v3 ; Centre-sud des États-Unis

512

Chaque machine virtuelle a exécuté tous les processus mentionnés.

PostgreSQL 14 en configuration haute disponibilité

Mémoire optimisée, E64ds_v4, 64 vCores, 432 Gio de RAM, 32767 Gio de stockage ; Centre-sud des États-Unis


La RAM bat le CPU.

Ory peut effectuer jusqu'à 1090 connexions par seconde en pointe et 800 connexions/seconde de façon constante dans la configuration ci-dessus. Cela est possible en rendant le flux sans état et en optimisant les index dans les requêtes fréquemment utilisées.

Conclusion

Le travail d'optimisation des performances effectué par l'équipe d'Ory a entraîné une amélioration significative des performances et de l'évolutivité d'Hydra. En réduisant le trafic d'écriture vers la base de données et en améliorant la base de code et les dépendances, Hydra est désormais plus rapide et plus réactif que jamais. En améliorant les indices, Hydra s'adapte désormais beaucoup plus efficacement au nombre d'instances.


À l'avenir, nous continuerons d'optimiser le logiciel d'Ory pour gérer encore plus de trafic. Nous pensons qu'il est possible d'obtenir 5 fois plus de débit sur un seul nœud PostgreSQL avec des optimisations du modèle de données.


Si vous construisez un serveur OAuth2, nous vous recommandons fortement d'essayer les implémentations OpenID Connect et OAuth2 entièrement certifiées d'Ory : Ory OAuth2 - notre service entièrement géré fonctionnant sur le réseau mondial Ory, basé sur l'open source Ory Hydra - utilise déjà les optimisations décrites dans cet article et sa configuration ne prend que quelques minutes !


Également publié ici .