Collecter les journaux du proxy OAuth2
Ce document explique comment ingérer des journaux OAuth2 Proxy dans Google Security Operations à l'aide de Google Cloud Storage V2.
OAuth2 Proxy est un proxy inverse CNCF Sandbox qui permet l'authentification à l'aide de fournisseurs OAuth2/OIDC (Google, GitHub, Keycloak, Azure AD et autres) pour valider les comptes par e-mail, domaine ou groupe. Il génère des journaux d'authentification (connexion réussie/échouée), des journaux de requêtes (requêtes HTTP proxyfiées avec l'identité de l'utilisateur) et des journaux d'application standards. Comme OAuth2 Proxy s'exécute en tant que conteneur dans Kubernetes et écrit tous les journaux dans stdout, un collecteur de journaux natif de Kubernetes (Fluentd) est utilisé pour transférer les journaux vers un bucket GCS pour l'ingestion Google SecOps.
Avant de commencer
Assurez-vous de remplir les conditions suivantes :
- Une instance Google SecOps
- Un projet GCP avec l'API Cloud Storage activée
- Autorisations pour créer et gérer des buckets GCS
- Autorisations permettant de gérer les stratégies IAM sur les buckets GCS
- Un cluster Kubernetes en cours d'exécution avec OAuth2 Proxy déployé (via un chart Helm ou un déploiement manuel)
- Accès
kubectlau cluster Kubernetes avec les autorisations nécessaires pour créer des DaemonSets, des ConfigMaps, des secrets et des espaces de noms - Clé JSON d'un compte de service GCP avec l'autorisation
storage.objects.createsur le bucket GCS cible
Créer un bucket Google Cloud Storage
- Accédez à la console Google Cloud.
- Sélectionnez votre projet ou créez-en un.
- Dans le menu de navigation, accédez à Cloud Storage > Buckets.
- Cliquez sur Créer un bucket.
Fournissez les informations de configuration suivantes :
Paramètre Valeur Nommer votre bucket Saisissez un nom unique (par exemple, oauth2-proxy-logs-bucket).Type d'emplacement Choisissez en fonction de vos besoins (région, birégion ou multirégion). Emplacement Sélectionnez l'emplacement (par exemple, us-central1).Classe de stockage Standard (recommandé pour les journaux auxquels vous accédez fréquemment) Access control (Contrôle des accès) Uniforme (recommandé) Outils de protection Facultatif : Activez la gestion des versions des objets ou une règle de conservation. Cliquez sur Créer.
Configurer la journalisation du proxy OAuth2
OAuth2 Proxy écrit trois types de journaux dans stdout : les journaux standards, les journaux d'authentification et les journaux de requêtes. Les trois sont activés par défaut. Pour vous assurer que les journaux contiennent le maximum de détails liés à la sécurité, configurez OAuth2 Proxy avec les indicateurs de journalisation suivants.
Option 1 : Configurer via les valeurs du chart Helm
Si OAuth2 Proxy est déployé à l'aide du chart Helm officiel, ajoutez les éléments suivants à votre fichier
values.yaml:config: configFile: |- standard_logging = true auth_logging = true request_logging = true silence_ping_logging = true standard_logging_format = "[{{.Timestamp}}] [{{.File}}] {{.Message}}" auth_logging_format = "{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] [{{.Status}}] {{.Message}}" request_logging_format = "{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] {{.Host}} {{.RequestMethod}} {{.Upstream}} {{.RequestURI}} {{.Protocol}} {{.UserAgent}} {{.StatusCode}} {{.ResponseSize}} {{.RequestDuration}}"Appliquez les valeurs Helm mises à jour :
helm upgrade oauth2-proxy oauth2-proxy/oauth2-proxy -f values.yaml -n <your-namespace>
Option 2 : Configurer à l'aide d'options de ligne de commande
Si OAuth2 Proxy est déployé à l'aide d'un fichier manifeste de déploiement Kubernetes, ajoutez les arguments suivants à la spécification du conteneur :
args: - --standard-logging=true - --auth-logging=true - --request-logging=true - --silence-ping-logging=true
Option 3 : Configurer à l'aide de variables d'environnement
Définissez les variables d'environnement suivantes sur le conteneur OAuth2 Proxy :
env: - name: OAUTH2_PROXY_STANDARD_LOGGING value: "true" - name: OAUTH2_PROXY_AUTH_LOGGING value: "true" - name: OAUTH2_PROXY_REQUEST_LOGGING value: "true" - name: OAUTH2_PROXY_SILENCE_PING_LOGGING value: "true"
Vérifier la journalisation du proxy OAuth2
Après avoir appliqué la configuration, vérifiez qu'OAuth2 Proxy génère des journaux :
kubectl logs -l app=oauth2-proxy -n <your-namespace> --tail=20Le résultat inclut des lignes semblables à celles-ci :
10.0.0.1 - abc123 - user@example.com [2024/01/15 10:30:00] [AuthSuccess] Authenticated via OAuth2 10.0.0.1 - abc123 - user@example.com [2024/01/15 10:30:01] example.com GET 10.0.0.5:8080 "/dashboard" HTTP/1.1 "Mozilla/5.0" 200 1234 0.005
Créer un compte de service GCP pour Fluentd
Fluentd nécessite un compte de service GCP avec accès en écriture au bucket GCS.
- Dans la console GCP, accédez à IAM et administration > Comptes de service.
- Cliquez sur Créer un compte de service.
- Fournissez les informations de configuration suivantes :
- Nom du compte de service : saisissez
fluentd-gcs-writer. - Description du compte de service : saisissez
Service account for Fluentd to write OAuth2 Proxy logs to GCS.
- Nom du compte de service : saisissez
- Cliquez sur Créer et continuer.
- Dans la section Autoriser ce compte de service à accéder au projet :
- Cliquez sur Sélectionner un rôle.
- Recherchez et sélectionnez Administrateur des objets de l'espace de stockage.
- Cliquez sur Continuer.
- Cliquez sur OK.
Créer et télécharger une clé de compte de service
- Dans la liste Comptes de service, cliquez sur le compte de service
fluentd-gcs-writer. - Accédez à l'onglet Clés.
- Cliquez sur Ajouter une clé > Créer une clé.
- Sélectionnez JSON comme type de clé.
- Cliquez sur Créer.
- Enregistrez le fichier de clé JSON téléchargé de manière sécurisée. Ce fichier sera utilisé à l'étape suivante.
Créer un secret Kubernetes pour la clé du compte de service
Créez un secret Kubernetes contenant la clé du compte de service GCP dans l'espace de noms où Fluentd sera déployé :
kubectl create namespace logging kubectl create secret generic fluentd-gcs-key \ --from-file=service-account-key.json=<path-to-downloaded-key>.json \ -n logging
Déployer le DaemonSet Fluentd pour collecter les journaux du proxy OAuth2
Déployez Fluentd en tant que DaemonSet dans le cluster Kubernetes pour collecter les journaux de conteneurs à partir des pods OAuth2 Proxy et les transférer vers le bucket GCS.
Créer un ConfigMap Fluentd
Créez un fichier nommé
fluentd-configmap.yamlavec le contenu suivant :apiVersion: v1 kind: ConfigMap metadata: name: fluentd-gcs-config namespace: logging data: fluent.conf: | <source> @type tail read_from_head true tag kubernetes.* path /var/log/containers/*oauth2-proxy*.log pos_file /var/log/fluentd-oauth2-proxy.log.pos <parse> @type regexp expression /^(?<time>[^ ]+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/ time_format %Y-%m-%dT%H:%M:%S.%N%z </parse> </source> <filter kubernetes.**> @type kubernetes_metadata @id filter_kube_metadata </filter> <match kubernetes.**> @type gcs project YOUR_GCP_PROJECT_ID keyfile /etc/secrets/service-account-key.json bucket oauth2-proxy-logs-bucket path oauth2-proxy-logs/%Y/%m/%d/ object_key_format %{path}%{time_slice}_%{hostname}_%{index}.%{file_extension} <buffer tag,time> @type file path /var/log/fluentd/gcs timekey 300 timekey_wait 60 timekey_use_utc true chunk_limit_size 10MB </buffer> <format> @type json </format> </match>
Remplacez les valeurs suivantes :
YOUR_GCP_PROJECT_ID: ID de votre projet GCP (par exemple,my-project-123456)oauth2-proxy-logs-bucket: nom du bucket GCS créé précédemmentAppliquez le ConfigMap :
kubectl apply -f fluentd-configmap.yaml
Créer un DaemonSet Fluentd
Créez un fichier nommé
fluentd-daemonset.yamlavec le contenu suivant :apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-gcs namespace: logging labels: k8s-app: fluentd-gcs spec: selector: matchLabels: k8s-app: fluentd-gcs template: metadata: labels: k8s-app: fluentd-gcs spec: tolerations: - key: node-role.kubernetes.io/control-plane effect: NoSchedule - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd-gcs image: fluent/fluentd-kubernetes-daemonset:v1-debian-gcs resources: limits: memory: 512Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: fluentd-gcs-config-volume mountPath: /fluentd/etc/fluent.conf subPath: fluent.conf readOnly: true - name: fluentd-gcs-secrets-volume mountPath: /etc/secrets/service-account-key.json subPath: service-account-key.json readOnly: true - name: varlog mountPath: /var/log - name: dockercontainerlogdirectory mountPath: /var/log/pods readOnly: true - name: fluentd-buffer mountPath: /var/log/fluentd terminationGracePeriodSeconds: 30 volumes: - name: fluentd-gcs-config-volume configMap: name: fluentd-gcs-config - name: fluentd-gcs-secrets-volume secret: secretName: fluentd-gcs-key - name: varlog hostPath: path: /var/log - name: dockercontainerlogdirectory hostPath: path: /var/log/pods - name: fluentd-buffer emptyDir: {}Appliquez le DaemonSet :
kubectl apply -f fluentd-daemonset.yaml
Vérifier le déploiement de Fluentd
Vérifiez que les pods Fluentd sont en cours d'exécution sur chaque nœud :
kubectl get pods -n logging -l k8s-app=fluentd-gcsConsultez les journaux Fluentd pour vérifier que les écritures GCS ont réussi :
kubectl logs -l k8s-app=fluentd-gcs -n logging --tail=20Vérifiez que les fichiers journaux apparaissent dans le bucket GCS :
- Accédez à Cloud Storage > Buckets dans la console GCP.
- Cliquez sur le bucket (par exemple,
oauth2-proxy-logs-bucket). - Accédez au dossier
oauth2-proxy-logs/. - Vérifiez que les fichiers
.jsonsont présents et que leurs codes temporels sont récents.
Récupérer le compte de service Google SecOps
Google SecOps utilise un compte de service unique pour lire les données de votre bucket GCS. Vous devez accorder à ce compte de service l'accès à votre bucket.
Obtenir l'adresse e-mail du compte de service
- Accédez à Paramètres SIEM> Flux.
- Cliquez sur Add New Feed (Ajouter un flux).
- Cliquez sur Configurer un flux unique.
- Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple,
OAuth2 Proxy Logs). - Sélectionnez Google Cloud Storage V2 comme Type de source.
- Sélectionnez Proxy d'authentification Kubernetes comme Type de journal.
Cliquez sur Obtenir un compte de service. Une adresse e-mail unique pour le compte de service s'affiche, par exemple :
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.comCopiez cette adresse e-mail pour l'utiliser à l'étape suivante.
Cliquez sur Suivant.
Spécifiez les valeurs des paramètres d'entrée suivants :
URL du bucket de stockage : saisissez l'URI du bucket GCS avec le chemin d'accès au préfixe :
gs://oauth2-proxy-logs-bucket/oauth2-proxy-logs/Remplacez
oauth2-proxy-logs-bucketpar le nom de votre bucket GCS.
Option de suppression de la source : sélectionnez l'option de suppression de votre choix :
- Jamais : ne supprime jamais aucun fichier après les transferts (recommandé pour les tests).
- Supprimer les fichiers transférés : supprime les fichiers une fois le transfert réussi.
Supprimer les fichiers transférés et les répertoires vides : supprime les fichiers et les répertoires vides après un transfert réussi.
Âge maximal des fichiers : incluez les fichiers modifiés au cours des derniers jours (180 jours par défaut).
Espace de noms de l'élément : espace de noms de l'élément
Libellés d'ingestion : libellé à appliquer aux événements de ce flux
Cliquez sur Suivant.
Vérifiez la configuration de votre nouveau flux sur l'écran Finaliser, puis cliquez sur Envoyer.
Accorder des autorisations IAM au compte de service Google SecOps
Le compte de service Google SecOps a besoin du rôle Lecteur des objets Storage sur votre bucket GCS.
- Accédez à Cloud Storage > Buckets.
- Cliquez sur le nom de votre bucket (par exemple,
oauth2-proxy-logs-bucket). - Accédez à l'onglet Autorisations.
- Cliquez sur Accorder l'accès.
- Fournissez les informations de configuration suivantes :
- Ajouter des comptes principaux : collez l'adresse e-mail du compte de service Google SecOps (par exemple,
chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com). - Attribuer des rôles : sélectionnez Lecteur des objets de l'espace de stockage.
- Ajouter des comptes principaux : collez l'adresse e-mail du compte de service Google SecOps (par exemple,
- Cliquez sur Enregistrer.
Documentation de référence sur les journaux de proxy OAuth2
Le tableau suivant décrit les champs de journaux générés par OAuth2 Proxy et leur pertinence en termes de sécurité :
Champs du journal d'authentification
| Champ | Exemple | Description |
|---|---|---|
| Client | 10.0.0.1 |
Adresse IP du client/à distance (utilise l'en-tête X-Real-IP si --reverse-proxy=true) |
| RequestID | 00010203-0405-4607-8809-0a0b0c0d0e0f |
ID de la requête provenant de l'en-tête X-Request-Id (UUID aléatoire si vide) |
| Nom d'utilisateur | user@example.com |
Adresse e-mail ou nom d'utilisateur de la demande d'authentification |
| Horodatage | 2024/01/15 10:30:00 |
Date et heure de l'événement d'authentification |
| État | AuthSuccess |
Résultat de l'authentification : AuthSuccess, AuthFailure ou AuthError |
| Message | Authenticated via OAuth2 |
Détails de la tentative d'authentification |
Champs du journal des requêtes
| Champ | Exemple | Description |
|---|---|---|
| Client | 10.0.0.1 |
Adresse IP du client/de l'hôte distant |
| RequestID | 00010203-0405-4607-8809-0a0b0c0d0e0f |
Identifiant de la demande |
| Nom d'utilisateur | user@example.com |
Adresse e-mail de l'utilisateur authentifié |
| Horodatage | 2024/01/15 10:30:01 |
Date et heure de la demande |
| Hôte | app.example.com |
Valeur de l'en-tête d'hôte |
| RequestMethod | GET |
Méthode de requête HTTP |
| En amont | 10.0.0.5:8080 |
Serveur en amont qui a traité la requête |
| RequestURI | /dashboard |
Chemin d'URI de la requête |
| Protocole | HTTP/1.1 |
Protocole de requête |
| UserAgent | Mozilla/5.0 |
Chaîne user-agent complète |
| StatusCode | 200 |
Code d'état de réponse HTTP |
| ResponseSize | 1234 |
Taille de la réponse en octets |
| RequestDuration | 0.005 |
Temps de traitement des requêtes en secondes |
Table de mappage UDM
| Champ de journal | Mappage UDM | Logique |
|---|---|---|
| à propos de | à propos de | Informations sur l'événement |
| http_req_id_field | additional.fields | Champs supplémentaires non couverts par le schéma UDM standard |
| http_req_path_field | additional.fields | |
| k8s_pod_app_field | additional.fields | |
| k8s_pod_template_hash_field | additional.fields | |
| k8s_pod_tls_mode_field | additional.fields | |
| k8s_pod_canonical_revision_field | additional.fields | |
| k8s_pod_canonical_name_field | additional.fields | |
| pod_name | additional.fields | |
| cntnr_name | additional.fields | |
| destination_canonical_revision | additional.fields | |
| requested_server | additional.fields | |
| nodename_label | additional.fields | |
| componentName_label | additional.fields | |
| componentVersion_label | additional.fields | |
| azureResourceID_label | additional.fields | |
| producer_label | additional.fields | |
| first_label | additional.fields | |
| last_label | additional.fields | |
| meta_name | additional.fields | |
| resource_version_label | additional.fields | |
| request_apiVersion | additional.fields | |
| request_kind_label | additional.fields | |
| request_type_label | additional.fields | |
| response_apiVersion | additional.fields | |
| response_kind_label | additional.fields | |
| response_type_label | additional.fields | |
| jsonPayload.message | metadata.description | Description de l'événement |
| event_type | metadata.event_type | Type d'événement |
| labels.request_id | metadata.product_log_id | Identifiant de journal spécifique au produit |
| insertId | metadata.product_log_id | |
| jsonPayload.chartVersion | metadata.product_version | Version du produit |
| httpRequest.protocol | network.application_protocol | Protocole d'application utilisé dans la connexion réseau |
| network.direction | network.direction | Sens du trafic réseau |
| httpRequest.requestMethod | network.http.method | Méthode HTTP |
| http_method | network.http.method | |
| httpRequest.status | network.http.response_code | Code de réponse HTTP |
| httpRequest.userAgent | network.http.user_agent | User-agent HTTP |
| requestMetadata.callerSuppliedUserAgent | network.http.user_agent | |
| labels.protocol | network.ip_protocol | Protocole IP |
| httpRequest.responseSize | network.received_bytes | Nombre d'octets reçus |
| labels.total_received_bytes | network.received_bytes | |
| httpRequest.requestSize | network.sent_bytes | Nombre d'octets envoyés |
| labels.total_sent_bytes | network.sent_bytes | |
| jsonPayload.session | network.session_id | Identifiant de session |
| labels.service_authentication_policy | network.tls.cipher | Suite de chiffrement TLS |
| principal | principal | Entité principale impliquée dans l'événement |
| principal_hostname | principal.hostname | Nom d'hôte du principal |
| prin_userid | principal.user.userid | ID utilisateur du principal |
| security_result | security_result | Résultat de l'évaluation de sécurité |
| cible | cible | Entité cible impliquée dans l'événement |
| target_hostname | target.hostname | Nom d'hôte de la cible |
| resource_sub_type | target.resource.resource_subtype | Sous-type de la ressource cible |
| target_userid | target.user.userid | ID utilisateur de la cible |
| metadata.product_name | metadata.product_name | Nom du produit |
| metadata.vendor_name | metadata.vendor_name | Nom du fournisseur |
Journal des modifications
Afficher le journal des modifications pour ce parseur
Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.