diff --git a/readme.md b/readme.md index a2daa9426d1287e3b3a39d412679244d9a861edb..692fe58a0f39fc4d77676d1cba102b8da17119bb 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ --- -title : Sécurité des applications - Création d'une API REST avec GO Lang +title : Sécurité des applications - Création d'une API REST avec Golang author : - "Birner Scott" date : "2021 - 2022" @@ -27,18 +27,18 @@ Mais aussi de : - Ajouter un enseignant - Supprimer un enseignant -Ces actions seront régies en fonction de plusieurs règles que nous aurons défini dans notre API pour les différents utilisateurs de celle-ci. En effet, certain de nos endpoints seront limité en accès via des logins locaux ou via un JWT *Json Web Token* via une service en ligne nommé Okta. +Ces actions seront régies en fonction de plusieurs règles que nous aurons défini dans notre API pour les différents utilisateurs de celle-ci. En effet, certain de nos endpoints seront limités en accès via des utilisateurs locaux ou par un JWT *Json Web Token* délivré par un service en ligne: Okta. ## Endpoints Un endpoint est une url qui permet d'aboutir aux données de notre API. -Par exemple, si nous souhaitons obtenir la liste de nos étudiants, il nous faut taper dans un navigateur (par exemple) l'url suivante: +Par exemple, si nous souhaitons obtenir la liste de nos étudiants, il faut taper dans un navigateur (par exemple) l'url suivante: ``` https://localhost:8080/students ``` -et le lot de données retourné par notre API est : +Le lot de données retourné par notre API (au format json) est : ```json [ @@ -63,7 +63,7 @@ et le lot de données retourné par notre API est : ] ``` ## Types de requêtes -Il existe plusieurs type de requête http(s) en fonction de l'action souhaitée. En effet, pour récupérer des données de notre API, nous allons réaliser une requête "GET" sur un de nos endpoints sensé produire des données. Chacun de ces types on un code d'erreur permettant de savoir si notre requête a aboutit correctement ou non. Dans notre cas, nous avons implémenté trois type de requêtes: GET, POST, DELETE. +Il existe plusieurs types de requête http(s) en fonction de l'action souhaitée. En effet, pour récupérer des données de notre API, nous allons réaliser une requête "GET" sur un de nos endpoints censé produire des données. Chacun de ces types ont un code d'erreur permettant de savoir si notre requête a aboutit correctement ou non. Dans notre cas, nous avons implémenté trois types de requêtes: GET, POST, DELETE. ### Requêtes HTTP(S) | Requête | Description | Code de succès | Code d'erreur | @@ -99,13 +99,12 @@ Request type: DELETE https://localhost:8080/student/delete/1 ``` -Pour tester les endpoints de notre API, nous avons utilisé l'application postman qui permet de créer des requêtes de différents type et de passer des lots de données. +Pour tester les endpoints de notre API, nous avons utilisé l'application postman qui permet de créer des requêtes de différents types et de passer des lots de données pour la création d'un ressource par exemple. \newpage # Docker Docker est aujourd’hui une application extrêmement prisée dans le monde du développement. Elle permet d’empaqueter une application ainsi que ses dépendances dans un conteneur isolé afin d’être exécutée sur n’importe quel serveur supportant docker. Il ne s’agit pas de virtualisation, mais de conteneurisation, une forme plus légère s’appuyant sur certaines parties de la machine hôte pour son fonctionnement. Le but est de pouvoir reproduire les conditions exactes dans lesquelles l’application va être déployée. - ## Infrastructure Notre objectif premier est de créer une infrastructure simple nous permettant d'exposer un port de notre application que nous pouvons appeler via des requêtes http. @@ -113,30 +112,39 @@ Voici la représentation de cette première infrastructure :  -La partie de gauche représente l'API avec notre application go. +La partie de gauche représente l'API qui est donc notre application go. -Nous accédons à nos données via le port 8080 (celui exposé par notre application). Les données retournées sont au format JSON. +Nous accédons en premier temps à nos données via le port 8080 (celui exposé par notre application). Les données retournées sont au format JSON. ### appsec -Pour créer un container docker avec notre application, il faut ajouter un fichier "Dockerfile" avec le comportement à adopter quand notre conainer sera créé: +Pour conteneuriser notre application go, il faut ajouter un fichier "Dockerfile" avec le comportement à adopter quand notre container sera créé: ```Dockerfile +# Image utilisée pour faire tourner notre application FROM golang:1.16-alpine WORKDIR /app +# Copie du code COPY go.mod ./ COPY go.sum ./ RUN go mod download COPY *.go ./ +# Commande à exécuter lors de la création du container RUN go build -o /appsec +# Commande à exécuter CMD ["/appsec"] ``` ### docker-compose -Le fichier docker-compose.yml est un fichier de configuration qui englobe différents containers docker. Il a pour but, en un fichier de lancer toute une infrastructure. Dans le fichier suivant, il permet de lancer le container de notre application go, mais aussi de lancer un serveur nginx. +Le fichier docker-compose.yml est un fichier de configuration qui englobe différents containers docker. Il a pour but, en un fichier et une commande de lancer toute une infrastructure. Le fichier suivant permet de lancer le container de notre application go, mais aussi de lancer un serveur nginx. + +Pour construire notre container go si celui a été modifier (mise à jour du code), il faut reconstruire l'application. Pour se faire, via docker-compose, utiliser la commande: +``` +docker-compose build +``` Pour démarrer le serveur, il suffit de taper la commande suivante: ``` @@ -148,6 +156,10 @@ ou docker-compose up -d ``` +Ainsi les container décrits par notre docker-compose seront démarré. + +Le fichier docker-compose est composé des différents container à démarrer, les fichiers à mapper, les ports à exposer et bien d'autres éléments... + ```yml version: '3' services: #container @@ -170,14 +182,14 @@ services: #container \newpage ## Reverse proxy avec Nginx -En second temps, nous souhaitons chiffrer les données envoyée à ou, retournée par notre API. Pour se faire, nous allons ajouter un serveur nginx qui permet d'appliquer un certificat sur les communications. L'infrastructure de notre application devient donc: +En second temps, nous souhaitons chiffrer les données envoyées à ou, retournée par notre API. Pour ce faire, nous allons ajouter un serveur nginx qui permet d'appliquer un certificat sur les communications. L'infrastructure de notre application devient donc:  -Le but sera de rediriger toutes les connexions sur le port 80 vers le port 443 et de chiffrer les communications. De sorte les informations transmises ne pourrons être lue si les paquets sont interceptés. +Le but sera de rediriger toutes les connexions sur le port 80 vers le port 443 et de chiffrer les communications. De cette façon, les informations transmises ne pourrons pas être lue si les paquets sont interceptés. ### Génération des clés et configuration -Comme on peut le constater sur la deuxième architecture, deux fichier sont utilisé pour le container de Nginx. Ce sont les fichier qui vont nous service pour chiffrer la connexion: le certificat et la clés de chiffrement. +Comme on peut le constater sur la deuxième architecture, deux fichiers sont utilisés pour le container de Nginx. Ce sont les fichiers qui vont nous servir pour chiffrer la connexion: le certificat et la clés de chiffrement. Pour générer ces fichiers, il faut utiliser la commande suivante : ``` @@ -274,7 +286,8 @@ authorized.GET("/students", studentAuthorization, getStudents) Par exemple, la ligne ci-dessus permet de créer l'endpoint https://localhost:8080/students. Comme on peut le voir, deux actions sont effectuée dans cette méthode: - 1. Appel de la méthode qui permet le contrôle d'accès: studentAuthorization + + 1. Appel de la méthode qui permet le contrôle d'accès (middleware): studentAuthorization 2. Récupération des données via la fonction: getStudents ## Contrôle d'accès via un routeur interne @@ -291,6 +304,7 @@ authorized := router.Group("/", gin.BasicAuth(gin.Accounts{ En deuxième temps, nous allons donner des droits précis pour chaque utilisateur. Dans la fonction "studentAuthorization" nous allons créer un hash-map contenant quel utilisateur a quels droits, puis quand on appel cette fonction, on va vérifier si l'utilisateur existe et ensuite s'il a le droit d'effectuer la requête demandée. + ```go func studentAuthorization(c *gin.Context) { user := c.MustGet(gin.AuthUserKey).(string) @@ -313,7 +327,7 @@ func studentAuthorization(c *gin.Context) { } ``` -Et pour finir, nous allons utiliser la fonction "getStudents" lors de l'appel du endpoint. C'est elle qui va donner l'accès à la récupération des données. +Et pour finir, nous allons utiliser la fonction "getStudents" lors de l'appel du endpoint. C'est elle qui va retourner les données au format JSON. ```go var students = []Student{ @@ -330,10 +344,10 @@ func getStudents(c *gin.Context) { ``` ## Contrôle d'accès via Okta -Okta est une plateforme de gestion des utilisateurs pour des applications. Il peut délivrer des **token** pour permettre l'accès aux ressources d'une application. C'est exactement ce qu'on va faire dans le cas de notre API. +Okta est une plateforme de gestion des utilisateurs pour des applications. Il peut délivrer des **token** permettant le contrôle d'identité et donc l'accès aux ressources d'une application. C'est exactement ce qu'on va faire dans le cas de notre API. ### Récupération d'un token -En premier lieu, nous allons créer les différents utilisateur dans la plateforme d'okta qui auront accès à notre API. +En premier lieu, nous allons créer différents utilisateur dans la plateforme d'okta qui auront accès à notre API. Par la suite, le but va être de récupérer un **token** certifiant que l'utilisateur en question est autorisé à accéder à notre API. @@ -366,7 +380,7 @@ Dans le body de la réponse, on peut retrouver l'**access_token** qui est donc l \newpage -Et enfin, voici l'utilisation du token pour récupérer des données de notre API +Et enfin, voici l'utilisation du token pour récupérer des données de notre API en utilisant norte token.  @@ -392,7 +406,7 @@ Enfin, dans notre code, faire appel à ces variables aux endroits où nous souha os.Getenv("MA VARIABLE D'ENVIRONNEMENT") ``` -Par exemple : +Exemples : ```go # Exemple 1 var toValidate = map[string]string{ diff --git a/readme.pdf b/readme.pdf index 2e1e73c0908af5bf993895d5dad1559e2a89c5da..c37f128dbd92fe2c1c1f64f2df23f185df56ac8e 100644 Binary files a/readme.pdf and b/readme.pdf differ