Il s'agit du troisième blog d'une série de blogs qui couvrent divers aspects de ce qu'il nous a fallu pour créer et exploiter notre service SaaS :
Mon blog initial a fourni quelques informations sur les besoins qui nous ont conduit à créer une nouvelle plateforme pour les clouds distribués. Grâce à cette plateforme, nos clients créent un ensemble complexe et diversifié d’ applications , telles que la fabrication intelligente, la criminalistique vidéo pour la sécurité publique, le trading algorithmique et les réseaux de télécommunications 5G.
Étant donné que bon nombre de ces applications sont critiques, nos clients s’attendent à ce que nous fournissions non seulement une sécurité multicouche, mais également la capacité d’apporter des améliorations continues pour assurer la sécurité de leurs clusters distribués. Ce blog spécifique abordera les défis de la sécurisation de l’infrastructure, des applications et des données sur plusieurs clusters.
Comme indiqué ci-dessus, le problème de la sécurisation de l’accès des utilisateurs aux applications (par exemple, l’accès à nos comptes bancaires ou à nos e-mails) est bien compris. Cependant, il n’est pas aussi simple de procéder de la même manière pour l’accès d’une application à une autre ou d’une application à des données, car aucun humain n’est impliqué dans le processus de vérification.
Pour qu'une application puisse accéder à une autre ressource de manière sécurisée (par exemple, des données stockées dans un magasin d'objets ou effectuer un appel d'API vers une autre application, etc.), les conditions suivantes doivent se produire :
Dans le cadre du processus d'authentification, l' application requérante peut présenter son identité sous la forme d'un certificat PKI ou d'un secret (par exemple un mot de passe) ou d'une clé. Il y a deux problèmes qui doivent être résolus lors de l'utilisation de secrets ou d'une clé pour l'identité :
Réaliser ces opérations de sécurité (pour l’interaction entre applications) de manière fiable et répétable est un problème non trivial. Il y a de nombreuses raisons pour lesquelles cela n’est pas anodin :
Par conséquent, sécuriser l’infrastructure, les applications et les données dans un environnement dynamique constitue un problème très complexe. Bien que les fournisseurs de cloud aient relevé ce défi et fourni de nombreux outils pour faire face à ces problèmes, leur intégration et leur maintenance ne sont pas faciles pour les raisons suivantes :
Alors que de nombreuses entreprises sont un fournisseur de cloud unique et qu’il peut être suffisant pour elles d’investir des ressources dans la gestion et l’amélioration des primitives de sécurité de ce fournisseur, nous opérons dans un environnement hétérogène (cloud hybride et edge) et avons dû créer une nouvelle solution pour résoudre ces problèmes.
Notre équipe a été chargée d’assurer la sécurité des applications, de l’infrastructure et des données qui peuvent résider dans une infrastructure distribuée, comme le montre la figure 1.
En conséquence, notre équipe de plateforme a décidé de créer de nouveaux services logiciels intégrant les quatre aspects suivants pour assurer la sécurité de la plateforme à travers la périphérie, n'importe quel cloud et nos PoP réseau :
L’identité est une question fondamentale car de nombreux défis de sécurité peuvent être résolus plus facilement une fois le problème d’identité résolu. Cependant, pour résoudre le problème de l’identité, nous devons définir ce que nous entendons par là et comment émettre une identité de manière fiable. Les geeks de la cryptographie aiment avoir leur propre point de vue sur tout, et la définition de l'identité ne fait pas exception :
L'ensemble unique et complet de caractéristiques infalsifiables et cryptographiquement vérifiables , certifiées cryptographiquement via un protocole non délégué et sécurisé par une autorité de confiance, qui constituent ce qu'une personne ou une chose est connue ou considérée comme étant.
Essentiellement, ce qu’il faut, c’est une identité infalsifiable et vérifiable par cryptographie, délivrée de manière sécurisée. Délivrer une telle identité est un défi pour deux raisons : 1) amorçage de l'identité et 2) racine de confiance
Il existe quelques approches qui sont souvent discutées en relation avec l’identité : SPIFFE et Hashicorp Vault. Nous tenons à préciser que SPIFFE est un schéma de dénomination qui pourrait être utilisé dans notre système comme document d’identité (certificat X.509) — cependant, le format n’est pas adapté à certains attributs d’identité contenant des données binaires. Bien que Vault puisse être utilisé pour émettre un document d'identité, il ne résout pas les problèmes d'amorçage de l'identité et de racine de confiance :
Par exemple, lorsqu’une machine virtuelle est lancée dans AWS, elle est dotée d’une identité d’amorçage et le service de métadonnées d’AWS agit comme racine de confiance. Le document d'identité (signé par AWS à l'aide de sa propre clé cryptographique) ressemble à ceci :
Bien que l' instanceId puisse désigner l'identité unique de l'instance application qui a été lancée, elle doit être liée à un nom bien connu (myserver.acmecorp.com) que d'autres applications utiliseront pour communiquer avec cette instance particulière. Par conséquent, même ce document d’identité d’amorçage AWS est insuffisant, mais peut être utilisé pour émettre une autre identité qui peut être utilisée par les applications pour communiquer avec une autre application.
Comme indiqué précédemment, il était essentiel pour nous de fournir une identité permettant aux applications de communiquer et de partager des informations entre les fournisseurs de cloud et/ou les emplacements périphériques (Figure 2). Cela signifiait que nous devions créer un système d’amorçage d’identité et de racine de confiance qui fonctionne dans tous ces environnements.
Étant donné que nous utilisons Kubernetes pour gérer et orchestrer des applications (à la fois des microservices et des machines virtuelles), cela signifiait que l’amorçage d’une identité unique pour chaque pod lancé devait être intégré au processus de création de pod de Kubernetes. La figure 3 montre comment nous nous connectons au processus de création de pod à l’aide du mécanisme webhook K8s. Ce webhook, appelé Voucher, injecte un side-car de sécurité, appelé Wingman , dans tous les pods créés dans le cluster et fournit également les informations signées cryptographiquement nécessaires qui peuvent être utilisées comme racine de confiance . Ce webhook fournit un jeton signé de courte durée qui est utilisé par Wingman pour demander un certificat X.509 à Identity Authority dans le backend SaaS de Volterra.
L'Autorité d'identité applique les règles de création d'identité de manière à minimiser le « rayon d'explosion » au cas où l'un des clusters K8 serait compromis. De nombreuses autres solutions qui s’appuient sur une autorité de certification racine commune ou sur une fédération de clusters K8 ne peuvent pas limiter le rayon d’explosion, ce qui a été un élément majeur dans notre décision de conception.
Pour un Pod donné dans K8s, les attributs peuvent changer après la création du Pod. Par exemple, un Pod peut être attaché à un nouveau service après sa création. Cela signifiait que les certificats d’identité devaient être mis à jour avec le nouveau service. Wingman surveille en permanence le bon qui suit le serveur API K8s pour détecter de telles mises à jour.
Ce mécanisme fournit une identité globale unique et universelle à chaque instance application exécutée sur notre plateforme, qu’il s’agisse de notre propre charge de travail ou de la charge de travail du client. Cette identité unique et son side-car (Wingman) sont ensuite utilisés pour sécuriser toutes les communications, tous les accès et toutes les clés/secrets dans un système distribué.
Avoir une identité unique par Pod est un excellent début car cela facilite la tâche de mise en œuvre de l’authentification mutuelle entre les services communicants. Notre infrastructure sous-jacente est composée de nombreux services différents qui fonctionnent sur différents protocoles tels que gRPC, REST, IPSec, BGP, etc. Dès le début, l’équipe s’est fixé comme objectif d’assurer une authentification mutuelle et une sécurité des communications (canal crypté) pour toutes les parties communicantes, quel que soit le protocole. Cela signifiait également que nous ne pouvions pas lier notre identité à une solution (par exemple Istio) qui se limite à un ensemble particulier de protocoles (par exemple HTTP/TCP vs. (basé sur IP).
Étant donné que notre plateforme permet également aux clients d’exécuter les charges de travail de leur choix, nous nous attendons à ce que ces charges de travail puissent exécuter une variété de protocoles et la plateforme ne devrait pas limiter leur capacité en fournissant une identité limitée à un ensemble particulier de protocoles. Au lieu de cela, l'authentification est découplée de l'identité (via Wingman) et cela permet de se connecter à diverses technologies de maillage de services. Notre service mesh sidecar/dataplane (abordé dans un blog précédent) utilise cette identité pour fournir mTLS aux charges de travail des clients.
Étant donné que la plupart de nos propres services d'infrastructure ont été écrits à l'aide de Volterra Golang Service Framework (qui sera abordé dans un prochain blog), nous avons décidé d'intégrer la logique de consommation d'identité (à partir de Wingman) directement dans le framework. Cela a aidé nos développeurs à sécuriser leurs communications de service à service prêtes à l'emploi sans avoir à s'appuyer sur un sidecar de maillage de service pour mTLS.
L’étape logique suivante après avoir obtenu un canal sécurisé mutuellement authentifié est l’autorisation, un processus permettant au récepteur de la demande (serveur) de déterminer s’il faut ou non autoriser la demande provenant de l’appelant identifié (client). Les raisons de ne pas autoriser la demande peuvent être nombreuses : limitations de quota, autorisations, etc. Étant donné que ces raisons et leurs seuils évoluent de manière dynamique, un ensemble de règles codées en dur (politique) pour l’autorisation n’était pas envisageable pour notre plateforme.
Nous avons décidé d’utiliser le moteur d’Open Policy Agent comme point de départ et avons créé un wrapper pour l’autorisation dans le side-car Wingman. Ce code wrapper récupère les politiques pertinentes de manière dynamique et les maintient à chaud pour une évaluation rapide. Similairement à l’authentification, le découplage du moteur d’autorisation de l’identité (et de l’authentification) nous a permis d’appliquer des politiques d’autorisation à plusieurs étapes du traitement des demandes, y compris au plus profond de la logique métier et pas seulement immédiatement après l’authentification.
Étant donné que Wingman est inséré dans toutes les charges de travail, y compris celles du client, notre plateforme fournit un moteur d’autorisation en tant que fonctionnalité intégrée. Même si Open Policy Agent (OPA) est construit sur un langage puissant appelé Rego, nous voulions cacher sa complexité à nos développeurs et clients. Toutes les politiques de notre plateforme peuvent également être définies à l'aide d'une structure de politique beaucoup plus facile à comprendre et intuitive qui ne nécessite pas que les utilisateurs (DevOps) apprennent Rego et évitent donc les erreurs. Similairement à la configuration de l’authentification, notre framework de service Golang a été connecté au moteur d’autorisation de Wingman en appelant automatiquement Wingman pour l’autorisation et en masquant la complexité de l’autorisation aux développeurs.
En utilisant une identité unique (émise via Wingman) pour l'authentification et un moteur de politique programmable (dans Wingman) pour l'autorisation, nous sommes en mesure de sécuriser la communication à l'aide de mTLS et de contrôler chaque accès à l'aide d'une politique robuste et programmable.
Chaque jour, les ingénieurs commettent des erreurs par inadvertance en stockant des clés et des mots de passe dans leur code et ces données finissent d'une manière ou d'une autre par se retrouver dans des référentiels de code ou d'artefacts publics. La gestion des secrets est difficile et sans une boîte à outils facile à utiliser et un processus bien défini, les développeurs sont censés suivre le chemin le plus court. Par conséquent, dès le début de l'entreprise, la mission de notre équipe de sécurité de la plateforme (différente de la sécurité du réseau et des applications) était de garantir que les développeurs n'aient pas à se poser des questions telles que « Où est-ce que je stocke les secrets - le code source ou les artefacts ou … ? »
Nous avons évalué deux approches courantes qui étaient à notre disposition pour la gestion des secrets lorsque nous avons commencé à construire notre plateforme, et toutes deux présentaient certaines lacunes :
Dans notre cas, étant un service SaaS, nous avons dû trouver une méthode plus robuste pour sécuriser les secrets de nos clients, car aucun compromis ne doit révéler leurs secrets.
En conséquence, nous avons décidé de mettre en œuvre une nouvelle technique que nous appelons Volterra Blindfold (marque déposée), qui fonctionne en conjonction avec notre side-car de sécurité, Wingman, comme le montre la figure 4. Cette approche permet au propriétaire du secret de verrouiller (crypter) le secret de telle manière que le secret ne soit jamais révélé en clair à une partie indésirable (y compris le serveur de décryptage). Le secret n’est même pas stocké dans un serveur de décryptage central et cette conception, à certains égards, simplifie considérablement la conception du serveur.
Nous fournissons aux utilisateurs un outil en mode sans fil qui peut être utilisé dans un environnement totalement hors ligne pour crypter le secret (S) qui peut ensuite être distribué. Par exemple, le secret peut lui-même être stocké avec la charge de travail et téléchargé dans le registre. Une fois ceci réalisé, les étapes suivantes doivent être réalisées :
Cela garantit que le plan de contrôle centralisé n'a jamais accès au secret en clair (S) et que le secret n'est disponible que dans la mémoire d'exécution du Pod pendant la durée de l'accès. En outre, une politique d’accès peut être appliquée pour définir qui a accès à un secret. La politique peut être définie en fonction des attributs d'identité tels que le nom de application , l'emplacement, le niveau de conformité, etc. De cette façon, tout sous-ensemble complexe de charges de travail peut être découpé et un contrôle d'accès précis peut être obtenu. La politique est cryptographiquement intégrée dans le processus de chiffrement, d’aveuglement, de déchiffrement et de déverrouillage : il est informatiquement impossible de contrecarrer l’intention de la politique.
En utilisant notre technique unique Blindfold pour verrouiller chaque secret et Wingman pour desceller chaque secret en fonction de la politique et de l'identité infalsifiable, nous sommes en mesure de surmonter les problèmes des solutions existantes et de fournir une gestion des secrets dans un environnement distribué sans jamais nous soucier de la compromission de la mine d'or centrale.
Bien que les secrets et la gestion des clés puissent sembler être deux noms différents pour le même problème, il existe des différences subtiles (mais importantes dans la pratique) entre les deux, selon la personne à qui vous posez la question et la manière dont vous souhaitez mettre en œuvre les solutions.
Le terme secret fait référence à toute information censée être secrète et non accessible aux parties non autorisées. D’une certaine manière, les clés cryptographiques sont un cas particulier de secrets. La gestion des clés, en revanche, fait généralement référence à un système qui stocke en toute sécurité le matériel de clé cryptographique sensible et contrôle l'utilisation de ce matériel. Dans certains cas, le système de gestion des clés peut distribuer des octets bruts de la clé aux parties autorisées et peut donc être confondu avec un système de gestion secret. Cependant, dans la plupart des cas, le système de gestion des clés ne distribue pas réellement les octets bruts du matériel de clé et effectue plutôt des opérations pour les demandeurs autorisés et envoie uniquement le résultat de l'opération. De nombreux systèmes de gestion de clés s'appuient également sur un stockage matériel (par exemple, HSM) pour le matériel clé, de sorte que la clé n'est jamais exposée en clair au logiciel.
Dans les environnements distribués, même pour un seul fournisseur de cloud, le problème de la gestion, de la synchronisation et de la rotation des clés cryptographiques est très difficile et les solutions actuelles sont sujettes aux erreurs, inefficaces et même peu sûres. Par exemple, si l’on utilise aujourd’hui 3 régions AWS et que l’on souhaite utiliser la même clé de chiffrement dans les 3 régions, il faudra synchroniser et faire tourner manuellement les clés. Le problème devient encore pire lorsque l’environnement s’étend sur plusieurs fournisseurs de cloud (publics et/ou privés). Même après tout cela, les propriétaires application doivent encore écrire du code complexe pour utiliser ces fonctionnalités KMS.
Notre plateforme masque toutes les complexités de la gestion des clés de l' application en laissant le side-car Wingman effectuer tout le gros du travail et en fournissant des interfaces simples à l' application pour effectuer les demandes de gestion des clés, y compris le cryptage, le décryptage, le HMAC, la vérification HMAC, la signature numérique, la vérification de la signature, la récupération de la clé (lorsque cela est autorisé), etc. Cela rend la gestion des clés pas du tout intimidante pour nos propres services d’infrastructure ainsi que pour les charges de travail des clients.
Le diagramme suivant (Figure 5) montre comment le KMS de Volterra fonctionne dans différents environnements et aide les charges de travail à décharger leur gestion des clés et leurs opérations de cryptographie sur le side-car Wingman. Selon la configuration, Wingman est capable de mettre en cache des clés et d'actualiser le cache sans même que l' application le sache. Le facteur déterminant ici est l’identité universelle et infalsifiable que nous avons introduite plus tôt. Étant donné que chaque Pod, quel que soit son emplacement, obtient une identité unique à l'échelle mondiale, il est facile pour le système Volterra KMS d'appliquer des politiques d'accès aux clés cryptographiques et à des opérations spécifiques telles que le cryptage, le décryptage, le HMAC, la vérification HMAC, la signature numérique et la vérification de signature de manière très précise.
Étant donné que toutes les clés sont gérées via le backend SaaS de Volterra, les charges de travail exécutées dans des environnements hétérogènes n’ont pas à gérer la synchronisation, la rotation, la révocation des clés, etc. — elles doivent simplement connaître les API Wingman simples pour tous leurs besoins de sécurité des données au repos.
Grâce à une sécurité de plateforme multicouche, nous avons pu fournir une solution complète à trois problèmes critiques d’une manière entièrement nouvelle ! Notre système est capable de démarrer en toute sécurité une identité universelle qui ne souffre pas du problème des « tortues jusqu'en bas », de gérer des secrets qui peuvent être stockés et distribués sans jamais se soucier du problème de la mine d'or, et de fournir une gestion des clés pour faciliter la sécurité des données au repos dans un environnement distribué. Cela a conduit aux gains suivants pour nos équipes internes ainsi que pour nos clients :
Cette série de blogs couvrira divers aspects de ce qu'il nous a fallu pour créer et exploiter notre service SaaS distribué à l'échelle mondiale avec de nombreux clusters application dans le cloud public, nos PoP de réseau privé et nos sites périphériques. Ensuite, ce sera la sécurité des application et des réseaux …
Nous recherchons quelques développeurs et architectes de solutions bénévoles pour nous aider à apporter cette capacité à une communauté plus large en tant que projet Open Source, veuillez contacter directement asingla@volterra.io si vous souhaitez participer à quelque chose d'amusant !