Service de gestion des certificats gNOI
RÉSUMÉ Utilisez le service gNOI CertificateManagement
pour gérer les certificats sur l’élément réseau cible.
Aperçu
Le service gNOI CertificateManagement
du gnoi.certificate
package gère la gestion des certificats sur l’élément réseau cible. Le fichier de proto-définition se trouve à l’https://github.com/openconfig/gnoi/blob/master/cert/cert.proto.
Une infrastructure à clé publique (PKI) prend en charge la distribution et l’identification des clés de chiffrement publiques, ce qui permet aux utilisateurs d’échanger des données en toute sécurité sur des réseaux tels qu’Internet et de vérifier l’identité de l’autre partie. Junos PKI vous permet de gérer les certificats de clé publique sur les équipements Junos, y compris le téléchargement, la génération et la vérification des certificats. Le service gNOI définit les opérations de gestion des certificats, via la PKI CertificateManagement
Junos. Les deux principales opérations sont :
-
Install (Installer) : permet d’installer un nouveau certificat à l’aide d’un nouvel ID de certificat sur le périphérique réseau cible. Si l’ID de certificat existe déjà, l’opération renvoie une erreur.
-
Rotate (Rotate) : remplace un certificat existant, qui possède déjà un ID de certificat, sur le périphérique réseau cible. Si le flux est interrompu ou si l’une des étapes échoue au cours du processus, l’appareil restaure le certificat d’origine.
La figure 1 illustre le flux de travail pour les opérations et Install()
Rotate()
. Pour les deux opérations, le client peut générer lui-même la demande de signature de certificat (CSR) ou demander à la cible de générer la CSR. Dans les deux cas, le client transmet la CSR à une autorité de certification (CA) pour demander un certificat numérique. Le client charge ensuite le certificat sur la cible, soit avec un nouvel ID de certificat pour les opérations, soit avec un ID de certificat existant pour Install()
Rotate()
les opérations. Pour Rotate()
les opérations, le client doit également valider les certificats de remplacement éventuels et finaliser ou annuler l’opération en fonction de la réussite ou de l’échec Rotate()
de la validation. Si le client annule l’opération, le serveur restaure le certificat, la paire de clés et tout bundle d’autorités de certification, s’il est présent dans la demande.
À partir de Junos OS Evolved version 23.1R1, lors d’une opération , ou , Rotate()
le LoadCertificate()
serveur gNOI vérifie le nouveau certificat d’entité finale à l’aide du certificat d’autorité Install()
de certification correspondant. Ainsi, la PKI du serveur gNOI doit inclure le certificat de l’autorité de certification racine qui vérifie le nouveau certificat. Vous pouvez charger le certificat d’autorité de certification requis dans le cadre du bundle d’autorités de certification gNOI, ou vous pouvez le charger séparément. Si la vérification échoue, l’appareil n’installe pas le nouveau certificat.

Le serveur gNOI ne prend en charge qu’un seul bundle global de certificats d’autorité de certification pour les services gNOI. Lorsque vous utilisez le service gNOI CertificateManagement
pour charger le bundle CA, les instructions suivantes s’appliquent :
-
Le
CertificateManagagment
service charge toujours le bundle de certificats CA à l’aide de l’identificateurca-profile-group
gnoi-ca-bundle
réservé . -
Si vous utilisez le service pour charger le
CertificateManagagment
bundle de certificats d’autorité de certification, l’appareil utilise implicitement l’authentification mutuelle et adopte la configuration suivante, même si elle n’est pas explicitement configurée sur l’appareil.[edit system services extension-service request-response grpc ssl] mutual-authentication { certificate-authority gnoi-ca-bundle; client-certificate-request require-certificate-and-verify; }
-
Si le service envoie une demande de chargement d’un nouveau bundle de certificats CA, le
CertificateManagagment
serveur efface les certificats du bundle CA précédent de l’appareil et charge les nouveaux. -
Si vous utilisez le
CertificateManagagment
service pour charger un bundle de certificats d’autorité de certification et que vous configurez également la hiérarchie des[edit system services extension-service request-response grpc ssl mutual-authentication]
instructions, les instructions configurées sont prioritaires.
Ainsi, vous pouvez d’abord configurer l’authentification serveur uniquement sur le serveur gNOI, puis utiliser le RPC pour charger les certificats de l’autorité Install()
de certification. Lorsque vous utilisez gNOI pour charger le bundle initial de certificats d’autorité de certification, l’appareil effectue les opérations suivantes :
- Ajoute les certificats d’autorité de certification dans la PKI Junos.
- Configure automatiquement le bundle de certificats d’autorité de certification gNOI au niveau de la hiérarchie à l’aide de
[edit security pki]
l’identificateurca-profile-group
gnoi-ca-bundle
. - Passe de l’authentification serveur uniquement à l’authentification mutuelle.
[edit] security { pki { ca-profile gnoi-ca-bundle_1 { ca-identity gnoi-ca-bundle_1; } ca-profile gnoi-ca-bundle_2 { ca-identity gnoi-ca-bundle_2; } ca-profile-group gnoi-ca-bundle { cert-base-count 2; } } }
Le Rotate()
RPC ne prend pas en charge le passage d’un mode d’authentification à l’autre pendant l’opération de rotation. Par conséquent, ne prend pas en charge le chargement du bundle de certificats d’autorité de certification sur le serveur gNOI pour la première fois, Rotate()
car cela fait passer l’appareil de l’authentification serveur uniquement à l’authentification mutuelle pendant l’opération. Lorsque le mode d’authentification change, le périphérique réseau doit redémarrer la pile gRPC et la connexion est perdue. Si le flux est interrompu, le client ne peut pas finaliser la demande de rotation et l’appareil revient aux certificats qui étaient en place avant le lancement de la Rotate()
demande.
L’instruction hot-reloading
au niveau de la hiérarchie ne conserve la [edit system services extension-service request-response grpc ssl]
session gRPC lors d’une mise à jour de certificat que lorsque le mode d’authentification reste inchangé pendant l’opération. Si, par exemple, le mode d’authentification passe d’une authentification serveur uniquement à une authentification mutuelle ou vice versa, le client se déconnecte.
RPC pris en charge
Le Tableau 1 présente les RPC de service pris en charge sur les CertificateManagement
équipements Junos.
Description | du RPC | introduite dans la version |
---|---|---|
CanGenerateCSR() |
Interrogez l’équipement cible pour déterminer s’il peut générer une demande de signature de certificat (CSR) avec le type, la taille et le type de certificat spécifiés. Valeurs prises en charge :
Renvoie |
Junos OS Evolved 23.1R1 |
GenerateCSR() |
Générez et renvoyez une demande de signature de certificat (CSR). |
Junos OS Evolved 22.2R1 |
GetCertificates() |
Renvoie les certificats locaux chargés sur l’équipement cible. |
Junos OS Evolved 22.2R1 |
Install() |
Chargez un nouveau certificat sur l’équipement cible en créant une demande CSR, en générant un certificat basé sur la CSR et en chargeant le certificat à l’aide d’un nouvel ID de certificat. |
Junos OS Evolved 22.2R1 |
LoadCertificate() |
Chargez un certificat signé par une autorité de certification (CA) sur l’équipement cible. |
Junos OS Evolved 22.2R1 |
LoadCertificateAuthorityBundle() |
Chargez un bundle de certificats CA sur l’équipement cible. |
Junos OS Evolved 22.2R1 |
RevokeCertificates() |
Révoquez les certificats à l’aide des ID de certificat spécifiés sur l’équipement cible. |
Junos OS Evolved 23.1R1 |
Rotate() |
Remplacez un certificat existant sur l’équipement cible en créant une demande CSR, en générant un certificat basé sur la CSR et en chargeant le certificat à l’aide d’un ID de certificat existant. |
Junos OS Evolved 22.2R1 |
Configuration de l’équipement réseau
Pour utiliser les services de gestion des certificats gNOI sur les équipements Junos, vous devez configurer les instructions et hot-reloading
pour les use-pki
services d’extensions gRPC. Dans la plupart des cas, vous configurez l’instruction lorsque vous configurez les use-pki
services gRPC sur le périphérique réseau. L’instruction hot-reloading
est nécessaire pour maintenir la session gRPC lors de la mise à jour des certificats qui affectent la session.
Avant de commencer :
- Configurez les services gRPC sur l’équipement réseau comme décrit dans la section Configurer les services gRPC.
- Configurez le système de gestion réseau pour prendre en charge les opérations gNOI comme décrit dans Configurer les services gNOI.
Pour configurer l’équipement réseau pour CertificateManagement
les opérations de service :
Installer un certificat
Vous pouvez utiliser le CertificateManagement
service Install()
RPC pour charger un nouveau certificat sur l’équipement cible. Lorsque vous installez un nouveau certificat à l’aide de l’opération, vous devez spécifier un nouvel ID de certificat qui n’existe pas déjà sur l’équipement Install()
cible. Vous pouvez également charger un bundle de certificats d’autorité de certification dans le cadre de l’opération Install()
.
Dans le cadre de l’opération, l’appareil vérifie le Install()
nouveau certificat. Par conséquent, l’infrastructure PKI Junos doit disposer du certificat de l’autorité de certification racine qui vérifie le nouveau certificat. Vous pouvez charger le certificat d’autorité de certification requis dans le cadre de l’opération, ou vous pouvez le charger séparément, avant l’opération, s’il ne se trouve pas déjà dans l’infrastructure Install()
à clé publique.
Si vous installez un nouveau certificat local qui sera utilisé pour l’authentification de session gRPC, vous devez également mettre à jour la configuration du serveur gRPC sur l’appareil pour utiliser le nouvel ID de certificat.
Exemple : Installation d’un certificat
Dans cet exemple, le serveur gNOI a été initialement configuré avec un certificat local uniquement et n’a pas été configuré pour utiliser l’authentification mutuelle. Le client gNOI utilise le RPC pour charger un nouveau certificat local et un bundle de certificats d’autorité de certification sur le Install()
périphérique. Une fois que le bundle CA est chargé sur le serveur gNOI, le serveur utilise l’authentification mutuelle par défaut. Le bundle d’autorités de certification comprend le certificat d’autorité de certification racine pour le certificat client, ainsi que le certificat d’autorité de certification racine pour le nouveau certificat de serveur.
Le client exécute l’application gnoi_cert_install_certificate_csr.py
Python, qui effectue les opérations suivantes :
- Demande à la cible de générer un CSR.
- Obtient un certificat signé basé sur la CSR.
- Charge le nouveau certificat de serveur, le nouveau certificat d’autorité de certification racine du serveur et le certificat d’autorité de certification racine du client sur l’équipement réseau cible.
L’application utilise le InstallCertificateRequest
message avec les paramètres appropriés pour définir les demandes de génération de la CSR et de chargement des certificats. Pour chaque requête, l’application utilise le Install()
RPC pour envoyer les requêtes à l’équipement réseau.
L’application gnoi_cert_install_certificate_csr.py
importe le module pour établir le grpc_channel
canal. Le grpc_channel
module est décrit dans Configurer les services gNOI. Les arguments de l’application sont stockés dans le args_cert_install_csr.txt
fichier. Les fichiers d’application et d’argumentation sont présentés ici.
gnoi_cert_install_certificate_csr.py
"""gNOI Install Certificate utility.""" from __future__ import print_function from __future__ import unicode_literals import argparse import logging import re from getpass import getpass from subprocess import call import cert_pb2 import cert_pb2_grpc from grpc_channel import grpc_authenticate_channel_server_only def get_args(parser): parser.add_argument('--server', dest='server', type=str, default='localhost', help='Server IP or name. Default is localhost.') parser.add_argument('--port', dest='port', nargs='?', type=int, default=50051, help='Server port. Default is 50051') parser.add_argument('--client_key', dest='client_key', type=str, default='', help='Full path of the client private key. Default is "".') parser.add_argument('--client_cert', dest='client_cert', type=str, default='', help='Full path of the client certificate. Default is "".') parser.add_argument('--root_ca_cert', dest='root_ca_cert', required=True, type=str, help='Full path of the Root CA certificate.') parser.add_argument('--user_id', dest='user_id', required=True, type=str, help='User ID for RPC call credentials.') parser.add_argument('--type', dest='type', type=int, default='1', help='Certificate Type. Default is 1. Valid value is 1 (1 is CT_X509); Invalid value is 0 (0 is CT_UNKNOWN).') parser.add_argument('--min_key_size', dest='min_key_size', type=int, default='2048', help='Minimum key size. Default is 2048.') parser.add_argument('--key_type', dest='key_type', type=int, default='1', help='Key Type. Default is 1 (KT_RSA); 0 is KT_UNKNOWN.') parser.add_argument('--common_name', dest='common_name', type=str, default='', help='CN of the certificate') parser.add_argument('--country', dest='country', type=str, default='US', help='Country name') parser.add_argument('--state', dest='state', type=str, default='CA', help='State name') parser.add_argument('--city', dest='city', type=str, default='Sunnyvale', help='City name') parser.add_argument('--organization', dest='organization', type=str, default='Acme', help='Organization name') parser.add_argument('--organizational_unit', dest='organizational_unit', type=str, default='Test', help='Organization unit name') parser.add_argument('--ip_address', dest='ip_address', type=str, default='', help='IP address on the certificate') parser.add_argument('--email_id', dest='email_id', type=str, default='', help='Email id') parser.add_argument('--certificate_id', dest='certificate_id', required=True, type=str, help='Certificate id.') parser.add_argument('--server_cert_private_key', dest='server_cert_private_key', type=str, default='', help='Server certificate private key') parser.add_argument('--server_cert_public_key', dest='server_cert_public_key', type=str, default='', help='Server certificate public key') parser.add_argument('--server_cert', dest='server_cert', type=str, default='server_cert', help='Server certificate') parser.add_argument('--server_root_ca1', dest='server_root_ca1', type=str, default='server_root_ca1', help='Server Root CA') parser.add_argument('--server_root_ca2', dest='server_root_ca2', type=str, default='server_root_ca2', help='Server Root CA') parser.add_argument('--client_root_ca1', dest='client_root_ca1', type=str, default='client_root_ca1', help='Client Root CA') parser.add_argument('--client_root_ca2', dest='client_root_ca2', type=str, default='client_root_ca2', help='Client Root CA') parser.add_argument('--client_root_ca3', dest='client_root_ca3', type=str, default='client_root_ca3', help='Client Root CA') parser.add_argument('--client_root_ca4', dest='client_root_ca4', type=str, default='client_root_ca4', help='Client Root CA') args = parser.parse_args() return args def install_cert(channel, metadata, args): try: stub = cert_pb2_grpc.CertificateManagementStub(channel) print("Executing GNOI::CertificateManagement::Install") # Create request to generate certificate signing request (CSR) it = [] req = cert_pb2.InstallCertificateRequest() req.generate_csr.csr_params.type = args.type req.generate_csr.csr_params.min_key_size = args.min_key_size req.generate_csr.csr_params.key_type = args.key_type req.generate_csr.csr_params.common_name = args.common_name req.generate_csr.csr_params.country = args.country req.generate_csr.csr_params.state = args.state req.generate_csr.csr_params.city = args.city req.generate_csr.csr_params.organization = args.organization req.generate_csr.csr_params.organizational_unit = args.organizational_unit req.generate_csr.csr_params.ip_address = args.ip_address req.generate_csr.csr_params.email_id = args.email_id req.generate_csr.certificate_id = args.certificate_id it.append(req) # Send request to generate CSR for csr_rsp in stub.Install(iter(it), metadata=metadata, timeout=180): logging.info(csr_rsp) # Write CSR to a file with open('/home/lab/certs/server_temp.csr', "wb") as file: file.write(csr_rsp.generated_csr.csr.csr) # If client connects to server IP address # update openssl.cnf template to include subjectAltName IP extension with open('/etc/pki/certs/openssl.cnf', 'r') as fd: data = fd.read() data1 = re.sub(r'(subjectAltName=IP:).*', r'\g<1>'+args.ip_address, data) with open('/home/lab/certs/openssl_temp.cnf', 'w') as fd: fd.write(data1) # Generate certificate with v3 extensions cmd = "openssl x509 -req -days 365 -in /home/lab/certs/server_temp.csr -CA /etc/pki/certs/serverRootCA.crt -CAkey /etc/pki/certs/serverRootCA.key -CAcreateserial -out /home/lab/certs/server_temp.crt -extensions v3_sign -extfile /home/lab/certs/openssl_temp.cnf -sha384" decrypted = call(cmd, shell=True) # Create request to install node certificate and CA certificates print("\nExecuting GNOI::CertificateManagement::Install") it = [] req = cert_pb2.InstallCertificateRequest() # Import certificate and add to request cert_data = bytearray(b'') with open("/home/lab/certs/server_temp.crt", "rb") as file: cert_data = file.read() req.load_certificate.certificate.type = args.type req.load_certificate.certificate_id = args.certificate_id req.load_certificate.certificate.certificate = cert_data # Add client and server CA certificates to request ca1 = req.load_certificate.ca_certificates.add() ca1.type = args.type ca1.certificate = open(args.client_root_ca1, 'rb').read() ca2 = req.load_certificate.ca_certificates.add() ca2.type = args.type ca2.certificate = open(args.server_root_ca1, 'rb').read() it.append(req) # Send request to install node certificate and CA bundle for rsp in stub.Install(iter(it), metadata=metadata, timeout=180): logging.info("Installing certificates: %s", rsp) print("Install complete.") except Exception as e: logging.error('Certificate install error: %s', e) print(e) def main(): parser = argparse.ArgumentParser(fromfile_prefix_chars='@') args = get_args(parser) grpc_server_password = getpass("gRPC server password for executing RPCs: ") metadata = [('username', args.user_id), ('password', grpc_server_password)] try: # Establish grpc channel to network device channel = grpc_authenticate_channel_server_only( args.server, args.port, args.root_ca_cert) install_cert(channel, metadata, args) except Exception as e: logging.error('Received error: %s', e) print(e) if __name__ == '__main__': logging.basicConfig(filename='gnoi-testing.log', format='%(asctime)s %(levelname)-8s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') main()
args_cert_install_csr.txt
--server=10.53.52.169 --port=50051 --root_ca_cert=/etc/pki/certs/serverRootCA.crt --user_id=gnoi-user --type=1 --min_key_size=2048 --key_type=1 --common_name=gnoi-server.example.com --country=US --state=CA --city=Sunnyvale --organization=Acme --organizational_unit=testing --ip_address=10.53.52.169 --email_id=test@example.com --certificate_id=gnoi-server1 --client_root_ca1=/etc/pki/certs/clientRootCA.crt --server_root_ca1=/etc/pki/certs/serverRootCA1.crt
Exécuter l’application
Lorsque le client exécute l’application, celle-ci demande la CSR, obtient le certificat signé et charge le nouveau certificat de serveur et les certificats d’autorité de certification sur l’équipement réseau cible.
lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_cert_install_certificate_csr.py @args_cert_install_csr.txt gRPC server password for executing RPCs: Creating channel Executing GNOI::CertificateManagement::Install Signature ok subject=CN = gnoi-server.example.com, C = US, ST = CA, O = Acme, OU = testing Getting CA Private Key Executing GNOI::CertificateManagement::Install Install complete.
Une fois que vous avez installé le nouveau certificat de serveur, vous devez configurer le serveur pour qu’il utilise cet ID de certificat pour l’authentification de session gRPC, comme illustré ici. De plus, étant donné que l’opération a chargé de nouveaux certificats d’autorité de certification, l’appareil Install()
utilise implicitement l’authentification mutuelle. Par conséquent, toutes les sessions gRPC suivantes doivent inclure le certificat et la clé du client lors de l’établissement du canal.
user@gnoi-server> show configuration system services extension-service request-response grpc ssl port 50051; local-certificate gnoi-server1; hot-reloading; use-pki;
Si vous exécutez l’application et fournissez un ID de certificat qui existe déjà sur le serveur, l’application renvoie une ALREADY_EXISTS
erreur, car l’opération nécessite un nouvel ID de Install()
certificat.
lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_cert_install_certificate_csr.py @args_cert_install_csr.txt gRPC server password for executing RPCs: Creating channel Executing GNOI::CertificateManagement::Install Signature ok subject=CN = gnoi-server.example.com, C = US, ST = CA, O = Acme, OU = testing Getting CA Private Key Executing GNOI::CertificateManagement::Install <_MultiThreadedRendezvous of RPC that terminated with: status = StatusCode.ALREADY_EXISTS details = "" debug_error_string = "{"created":"@1652241881.676147097","description":"Error received from peer ipv4:10.53.52.169:50051","file":"src/core/lib/surface/call.cc","file_line":903,"grpc_message":"","grpc_status":6}"
Rotation d’un certificat
Vous pouvez utiliser le CertificateManagement
service Rotate()
RPC pour remplacer un certificat existant sur l’équipement cible. Lorsque vous remplacez un certificat existant à l’aide de l’opération, vous devez charger le certificat à l’aide de l’ID de certificat qui existe déjà sur l’équipement Rotate()
cible. Vous pouvez également remplacer le bundle de certificats d’autorité de certification gNOI existant dans le cadre de l’opération Rotate()
.
L’opération Rotate()
est similaire à l’opération, Install()
sauf qu’elle remplace un certificat existant au lieu d’installer Rotate()
un nouveau certificat. En outre, le client doit valider que le certificat mis à jour fonctionne, puis finaliser ou annuler la demande en fonction de la réussite ou de l’échec de la Rotate()
validation du certificat.
Dans le cadre de l’opération, l’appareil vérifie le Rotate()
nouveau certificat. Par conséquent, l’infrastructure PKI Junos doit disposer du certificat de l’autorité de certification racine qui vérifie le nouveau certificat. Vous pouvez charger le certificat d’autorité de certification requis dans le cadre de l’opération, ou vous pouvez le charger séparément, avant l’opération, s’il ne se trouve pas déjà dans l’infrastructure Rotate()
à clé publique.
Exemple : Rotation d’un certificat
Dans cet exemple, le client exécute l’application gnoi_cert_rotate_certificate_csr.py
Python, qui effectue les opérations suivantes :
- Demande à la cible de générer un CSR.
- Obtention d’un certificat signé basé sur la CSR
- Remplace le certificat de nœud et le bundle d’autorités de certification gNOI sur le périphérique réseau cible.
- Valide le nouveau certificat.
- Finalise l’opération
Rotate
.
L’application utilise le RotateCertificateRequest
message avec les paramètres appropriés pour définir les demandes de génération de la CSR et de chargement du bundle de certificats et d’autorités de certification. Pour chaque requête, l’application utilise le Rotate()
RPC pour envoyer la requête au périphérique réseau. Pour permettre à l’équipement cible de vérifier le nouveau certificat de noeud, l’application remplace le bundle d’autorités de certification existant par un nouveau bundle d’autorités de certification. Le bundle comprend à la fois le certificat d’autorité de certification client et le certificat d’autorité de certification requis pour vérifier le certificat de nœud.
L’application valide le bon fonctionnement du nouveau certificat en créant une session gRPC avec le périphérique réseau et en exécutant un RPC simple Time()
, bien que vous puissiez tester l’authentification de session avec n’importe quel RPC. L’application finalise la demande de rotation si la session est établie avec succès et annule la demande de rotation si l’authentification de session échoue.
L’application gnoi_cert_rotate_certificate_csr.py
importe le module pour établir le grpc_channel
canal. Le grpc_channel
module est décrit dans Configurer les services gNOI. Les arguments de l’application sont stockés dans le args_cert_rotate_csr.txt
fichier. Les fichiers d’application et d’argumentation sont présentés ici.
gnoi_cert_rotate_certificate_csr.py
"""gNOI Rotate Certificate utility.""" from __future__ import print_function from __future__ import unicode_literals import argparse import logging import time import re import grpc from getpass import getpass from subprocess import call import cert_pb2 import cert_pb2_grpc import system_pb2 import system_pb2_grpc from grpc_channel import grpc_authenticate_channel_mutual def get_args(parser): parser.add_argument('--server', dest='server', type=str, default='localhost', help='Server IP or name. Default is localhost.') parser.add_argument('--port', dest='port', nargs='?', type=int, default=50051, help='Server port. Default is 50051') parser.add_argument('--client_key', dest='client_key', type=str, default='', help='Full path of the client private key. Default is "".') parser.add_argument('--client_cert', dest='client_cert', type=str, default='', help='Full path of the client certificate. Default is "".') parser.add_argument('--root_ca_cert', dest='root_ca_cert', required=True, type=str, help='Full path of the Root CA certificate.') parser.add_argument('--user_id', dest='user_id', required=True, type=str, help='User ID for RPC call credentials.') parser.add_argument('--type', dest='type', type=int, default='1', help='Certificate Type. Default is 1. Valid value is 1 (1 is CT_X509); Invalid value is 0 (0 is CT_UNKNOWN).') parser.add_argument('--min_key_size', dest='min_key_size', type=int, default='2048', help='Minimum key size. Default is 2048.') parser.add_argument('--key_type', dest='key_type', type=int, default='1', help='Key Type. Default is 1 (KT_RSA); 0 is KT_UNKNOWN.') parser.add_argument('--common_name', dest='common_name', type=str, default='', help='CN of the certificate') parser.add_argument('--country', dest='country', type=str, default='US', help='Country name') parser.add_argument('--state', dest='state', type=str, default='CA', help='State name') parser.add_argument('--city', dest='city', type=str, default='Sunnyvale', help='City name') parser.add_argument('--organization', dest='organization', type=str, default='Acme', help='Organization name') parser.add_argument('--organizational_unit', dest='organizational_unit', type=str, default='Test', help='Organization unit name') parser.add_argument('--ip_address', dest='ip_address', type=str, default='', help='IP address on the certificate') parser.add_argument('--email_id', dest='email_id', type=str, default='', help='Email id') parser.add_argument('--certificate_id', dest='certificate_id', required=True, type=str, help='Certificate id.') parser.add_argument('--server_cert_private_key', dest='server_cert_private_key', type=str, default='', help='Server certificate private key') parser.add_argument('--server_cert_public_key', dest='server_cert_public_key', type=str, default='', help='Server certificate public key') parser.add_argument('--server_cert', dest='server_cert', type=str, default='server_cert', help='Server certificate') parser.add_argument('--server_root_ca1', dest='server_root_ca1', type=str, default='server_root_ca1', help='Server Root CA') parser.add_argument('--server_root_ca2', dest='server_root_ca2', type=str, default='server_root_ca2', help='Server Root CA') parser.add_argument('--client_root_ca1', dest='client_root_ca1', type=str, default='client_root_ca1', help='Client Root CA') parser.add_argument('--client_root_ca2', dest='client_root_ca2', type=str, default='client_root_ca2', help='Client Root CA') parser.add_argument('--client_root_ca3', dest='client_root_ca3', type=str, default='client_root_ca3', help='Client Root CA') parser.add_argument('--client_root_ca4', dest='client_root_ca4', type=str, default='client_root_ca4', help='Client Root CA') parser.add_argument("--client_key_test", dest='client_key_test', type=str, default='', help='Full path of the test client private key. Default ""') parser.add_argument("--client_cert_test", dest='client_cert_test', type=str, default='', help='Full path of the test client certificate. Default ""') args = parser.parse_args() return args def rotate_cert(channel, metadata, args): try: result = '' stub = cert_pb2_grpc.CertificateManagementStub(channel) print("Executing GNOI::CertificateManagement::Rotate") # Create request to generate certificate signing request (CSR) it = [] req = cert_pb2.RotateCertificateRequest() req.generate_csr.csr_params.type = args.type req.generate_csr.csr_params.min_key_size = args.min_key_size req.generate_csr.csr_params.key_type = args.key_type req.generate_csr.csr_params.common_name = args.common_name req.generate_csr.csr_params.country = args.country req.generate_csr.csr_params.state = args.state req.generate_csr.csr_params.city = args.city req.generate_csr.csr_params.organization = args.organization req.generate_csr.csr_params.organizational_unit = args.organizational_unit req.generate_csr.csr_params.ip_address = args.ip_address req.generate_csr.csr_params.email_id = args.email_id req.generate_csr.certificate_id = args.certificate_id it.append(req) # Send request to generate CSR print('Sending request for CSR') for csr_rsp in stub.Rotate(iter(it), metadata=metadata, timeout=30): logging.info(csr_rsp) # Write CSR to a file with open('/home/lab/certs/server_temp.csr', "wb") as file: file.write(csr_rsp.generated_csr.csr.csr) # If client connects to server IP address # update openssl.cnf template to include subjectAltName IP extension with open('/etc/pki/certs/openssl.cnf', 'r') as fd: data = fd.read() data1 = re.sub(r'(subjectAltName=IP:).*', r'\g<1>'+args.ip_address, data) with open('/home/lab/certs/openssl_temp.cnf', 'w') as fd: fd.write(data1) # Generate certificate with v3 extensions cmd = "openssl x509 -req -days 365 -in /home/lab/certs/server_temp.csr -CA /etc/pki/certs/serverRootCA.crt -CAkey /etc/pki/certs/serverRootCA.key -CAcreateserial -out /home/lab/certs/server_temp.crt -extensions v3_sign -extfile /home/lab/certs/openssl_temp.cnf -sha384" decrypted = call(cmd, shell=True) # Create request to rotate node certificate and CA certificates print("\nExecuting GNOI::CertificateManagement::Rotate") it = [] req = cert_pb2.RotateCertificateRequest() # Import certificate and add to request with open("/home/lab/certs/server_temp.crt", "rb") as file: cert_data = file.read() req.load_certificate.certificate.type = args.type req.load_certificate.certificate.certificate = cert_data req.load_certificate.certificate_id = args.certificate_id # Add client and server CA certificates to request ca1 = req.load_certificate.ca_certificates.add() ca1.type = args.type ca1.certificate = open(args.client_root_ca1, 'rb').read() ca2 = req.load_certificate.ca_certificates.add() ca2.type = args.type ca2.certificate = open(args.server_root_ca1, 'rb').read() it.append(req) # Send request to replace node certificate and CA bundle for rsp in stub.Rotate(iter(it), metadata=metadata, timeout=60): logging.info("Rotating certificates. %s", rsp) # Validate certificates print("Validating certificates") time.sleep(5) validate_rc = True try: validate_channel = grpc_authenticate_channel_mutual( args.server, args.port, args.server_root_ca1, args.client_key_test, args.client_cert_test) validate_stub = system_pb2_grpc.SystemStub(validate_channel) validate_rsp = validate_stub.Time( request=system_pb2.TimeRequest(), metadata=metadata, timeout=60) except grpc.RpcError as e: print("Validation failed with error:", e) validate_rc = False pass if validate_rc: print("Finalizing certificate rotation.") it = [] req = cert_pb2.RotateCertificateRequest() req.finalize_rotation.SetInParent() it.append(req) for rsp in stub.Rotate(iter(it), metadata=metadata, timeout=30): logging.info("Finalizing rotate. %s", rsp) logging.info( "Certificate validation succeeded. Certificate rotation finalized.") result = "Certificate rotation finalized." else: print("Rolling back certificates.") logging.info( "Certificate validation failed. Rolling back to original certificates.") rsp.cancel() except Exception as e: logging.error('Certificate rotate error: %s', e) print(e) else: return result def main(): parser = argparse.ArgumentParser(fromfile_prefix_chars='@') args = get_args(parser) grpc_server_password = getpass("gRPC server password for executing RPCs: ") metadata = [('username', args.user_id), ('password', grpc_server_password)] try: # Establish grpc channel to network device channel = grpc_authenticate_channel_mutual( args.server, args.port, args.root_ca_cert, args.client_key, args.client_cert) response = rotate_cert(channel, metadata, args) print(response) except Exception as e: logging.error('Error: %s', e) print(e) if __name__ == '__main__': logging.basicConfig(filename='gnoi-testing.log', format='%(asctime)s %(levelname)-8s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') main()
args_cert_rotate_csr.txt
--server=10.53.52.169 --port=50051 --root_ca_cert=/etc/pki/certs/serverRootCA.crt --client_key=/home/lab/certs/client.key --client_cert=/home/lab/certs/client.crt --user_id=gnoi-user --type=1 --min_key_size=2048 --key_type=1 --common_name=gnoi-server.example.com --country=US --state=CA --city=Sunnyvale --organization=Acme --organizational_unit=testing --ip_address=10.53.52.169 --email_id=test@example.com --certificate_id=gnoi-server --client_root_ca1=/etc/pki/certs/clientRootCA.crt --server_root_ca1=/etc/pki/certs/serverRootCA.crt --client_key_test=/home/lab/certs/client.key --client_cert_test=/home/lab/certs/client.crt
Il est important de noter que l’argument est le certificat de l’autorité root_ca_cert
de certification racine du serveur requis pour les informations d’identification initiales du canal. L’argument server_root_ca1
est le certificat de l’autorité de certification racine correspondant au nouveau certificat du serveur. La PKI Junos doit disposer du nouveau certificat d’autorité de certification racine pour pouvoir vérifier le nouveau certificat local pendant l’opération Rotate()
. En outre, les informations d’identification du canal pour la session gRPC qui valide le nouveau certificat utilisent ce certificat d’autorité de certification racine. Bien que cet exemple utilise le même certificat d’autorité de certification racine pour le nouveau et l’ancien certificat de serveur, ceux-ci peuvent différer dans un autre cas.
Exécuter l’application
Lorsque le client exécute l’application, celle-ci demande la CSR, obtient le certificat signé et charge le certificat de remplacement et le bundle d’autorités de certification sur l’équipement réseau cible. L’application valide ensuite le certificat de remplacement avec une nouvelle session gRPC qui exécute un RPC simple Time()
. Une fois la validation réussie, le client finalise la demande de rotation.
lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_cert_rotate_certificate_csr.py @args_cert_rotate_csr.txt gRPC server password for executing RPCs: Creating channel Executing GNOI::CertificateManagement::Rotate Sending request for CSR Signature ok subject=CN = gnoi-server.example.com, C = US, ST = CA, O = Acme, OU = testing Getting CA Private Key Executing GNOI::CertificateManagement::Rotate Validating certificates Creating channel Finalizing certificate rotation. Certificate rotation finalized.
Révoquer un certificat
Un client gNOI peut utiliser le RevokeCertificates()
RPC pour supprimer un ou plusieurs certificats de l’équipement cible. Le client inclut un RevokeCertificatesRequest
message avec la liste des ID de certificat à révoquer.
Lorsque le serveur gNOI reçoit la requête, il traite chaque ID de certificat de la RevokeCertificates()
liste comme suit :
-
Si le certificat est présent et que la révocation réussit, l’équipement supprime le certificat du système de fichiers et de la PKI Junos et ajoute l’ID de certificat à la liste des certificats révoqués avec succès.
-
Si le certificat est présent et que la révocation échoue, l’appareil inclut l’ID de certificat et la raison de l’échec dans la liste des erreurs de révocation de certificat.
-
Si le certificat n’est pas présent, l’appareil considère que l’opération de révocation a réussi et ajoute l’ID de certificat à la liste des certificats révoqués avec succès.
Si la demande révoque le certificat utilisé pour la session en cours, celle-ci n’est pas affectée.
Après avoir traité la requête, le serveur gNOI renvoie un RevokeCertificatesResponse
message qui comprend :
-
Liste des ID de certificat révoqués avec succès.
-
Liste des erreurs de révocation contenant l’ID de certificat et la raison de l’échec.
Exemple : Révoquer un certificat
Dans cet exemple, le client exécute l’application gnoi_cert_revoke_certificates.py
Python, qui révoque deux certificats sur le serveur. Le premier ID de certificat est un identifiant valide sur l’appareil. Le deuxième ID de certificat est un identifiant qui n’existe pas sur l’appareil.
L’application utilise le RevokeCertificatesRequest
message avec les paramètres appropriés pour définir la requête. L’application envoie le RevokeCertificates()
RPC à l’équipement réseau pour qu’il effectue l’opération.
L’application gnoi_cert_revoke_certificates.py
importe le module pour établir le grpc_channel
canal. Le grpc_channel
module est décrit dans Configurer les services gNOI. Les arguments de l’application sont stockés dans le args_cert_revoke_certificates.txt
fichier. Les fichiers d’application et d’argumentation sont présentés ici.
gnoi_cert_revoke_certificates.py
"""gNOI Revoke Certificates utility.""" from __future__ import print_function from __future__ import unicode_literals import argparse import logging from getpass import getpass import cert_pb2 import cert_pb2_grpc from grpc_channel import grpc_authenticate_channel_mutual def get_args(parser): parser.add_argument('--server', dest='server', type=str, default='localhost', help='Server IP or name. Default is localhost.') parser.add_argument('--port', dest='port', nargs='?', type=int, default=50051, help='Server port. Default is 50051') parser.add_argument('--client_key', dest='client_key', type=str, default='', help='Full path of the client private key. Default is "".') parser.add_argument('--client_cert', dest='client_cert', type=str, default='', help='Full path of the client certificate. Default is "".') parser.add_argument('--root_ca_cert', dest='root_ca_cert', required=True, type=str, help='Full path of the Root CA certificate.') parser.add_argument('--user_id', dest='user_id', required=True, type=str, help='User ID for RPC call credentials.') parser.add_argument('--certificate_id1', dest='certificate_id1', required=True, type=str, help='Certificate id.') parser.add_argument('--certificate_id2', dest='certificate_id2', type=str, help='Certificate id.') args = parser.parse_args() return args def revoke_cert(channel, metadata, args): try: stub = cert_pb2_grpc.CertificateManagementStub(channel) print("Executing GNOI::CertificateManagement::RevokeCertificates") # Create request to revoke certificates req = cert_pb2.RevokeCertificatesRequest() req.certificate_id.append(args.certificate_id1) req.certificate_id.append(args.certificate_id2) # Send request to revoke certificates logging.info("Sending RevokeCertificates request.") rsp = stub.RevokeCertificates(req, metadata=metadata, timeout=60) logging.info(rsp) print("rsp:\n%s" %rsp) except Exception as e: logging.error('Error: %s', e) print(e) def main(): parser = argparse.ArgumentParser(fromfile_prefix_chars='@') args = get_args(parser) grpc_server_password = getpass("gRPC server password for executing RPCs: ") metadata = [('username', args.user_id), ('password', grpc_server_password)] try: # Establish grpc channel to network device channel = grpc_authenticate_channel_mutual( args.server, args.port, args.root_ca_cert, args.client_key, args.client_cert) revoke_cert(channel, metadata, args) except Exception as e: logging.error('Received error: %s', e) print(e) if __name__ == '__main__': logging.basicConfig(filename='gnoi-testing.log', format='%(asctime)s %(levelname)-8s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') main()
args_cert_revoke_certificates.txt
--server=10.53.52.169 --port=50051 --root_ca_cert=/etc/pki/certs/serverRootCA.crt --client_key=/home/lab/certs/client.key --client_cert=/home/lab/certs/client.crt --user_id=gnoi-user --certificate_id1=gnoi-server --certificate_id2=id-does-not-exist
Exécuter l’application
Lorsque le client exécute l’application, celle-ci demande à l’équipement cible de révoquer les certificats spécifiés. L’appareil renvoie une liste des certificats révoqués avec succès et de toutes les erreurs. L’appareil considère que l’opération a réussi à la fois pour l’ID de certificat valide et pour l’ID de certificat qui n’existe pas actuellement sur l’appareil.
lab@gnoi-client:~/src/gnoi/proto$ python3 gnoi_cert_revoke_certificates.py @args_cert_revoke_certificates.txt gRPC server password for executing RPCs: Creating channel Executing GNOI::CertificateManagement::RevokeCertificates rsp: revoked_certificate_id: "gnoi-server" revoked_certificate_id: "id-does-not-exist"
Install()
opérations ,
Rotate()
et
LoadCertificate()
vérifient le nouveau certificat dans le cadre de l’opération.