Abonnements
Dans Red Kite, les abonnements servent à lancer des tâches (jobs) à partir d'une expression cron (abonnements cron) ou d'un finding (abonnements sur événement). Ils permettent d'étendre le flux d'automatisation de Red Kite.
Un abonnement peut appartenir à un projet. Dans ce cas, il ne s'applique qu'à ce projet. Si aucun projet n'est précisé pour un abonnement, il s'applique à tous les projets.
Certains abonnements sont intégrés à Red Kite. Ils sont identifiés comme tels, mais les utilisateurs peuvent tout de même les modifier. Il est même possible de supprimer un abonnement intégré, mais assurez-vous de savoir ce que vous faites.
Un abonnement, cron ou sur événement, ne se déclenche jamais sur une ressource bloquée.
Dans l'interface, un abonnement s'écrit au format yaml. Vous pouvez aussi choisir le projet auquel l'abonnement s'applique à l'aide du
menu déroulant.
Un abonnement qui appartient à un projet est supprimé automatiquement lorsque ce projet est supprimé.
Abonnements cron
Les abonnements cron sont lancés selon une expression cron. Ce sont les abonnements les plus simples : ils ne demandent que l'information nécessaire pour lancer une tâche.
Les abonnements cron peuvent être déclenchés de façon fiable jusqu'à toutes les vingt (20) secondes.
Même si les abonnements cron n'exigent pas de projet, au moins un projet doit exister au moment du déclenchement pour qu'au moins une tâche soit lancée.
Syntaxe d'un abonnement cron
Un abonnement cron contient les éléments principaux suivants :
| Élément | Description | Requis |
|---|---|---|
| name | Le nom de l'abonnement, pour s'y retrouver plus tard | Oui |
| cronExpression | L'expression cron qui précise quand lancer la tâche | Oui |
| input | Une valeur qui représente toutes les ressources d'un type | Non |
| job | La tâche à lancer lorsque l'expression cron se déclenche | Oui |
| cooldown | Le délai minimal, en secondes, avant que l'abonnement puisse être redéclenché pour le même projet (par défaut : 0) | Non |
| batch | Un objet qui permet de regrouper les données pour lancer la tâche avec plusieurs ressources | Non |
Remarque : les sections suivantes donnent plus de détails sur ces éléments.
name
- Peut être n'importe quelle chaîne de caractères.
- Sert à distinguer l'abonnement des autres.
- Exemple :
name: my-first-cron-subscription
cronExpression
- Doit être une expression cron valide.
- Précise l'horaire de déclenchement de la tâche.
- Exemple (s'exécute tous les jours à minuit) :
cronExpression: "0 0 * * *"
input
- Élément facultatif qui définit le type de ressource à traiter.
- Valeurs acceptées :
- ALL_DOMAINS
- ALL_HOSTS
- ALL_IP_RANGES
- ALL_TCP_PORTS
- ALL_WEBSITES
- Exemple :
input: ALL_WEBSITES
job
-
L'élément
jobprécise la tâche à exécuter et ses paramètres.Sous-élément Description Requis nameLe nom de la tâche (doit correspondre à un type existant). Oui parametersUne liste des paramètres requis par la tâche. Non -
Exemple :
job:
name: resourceCheck
parameters:
- name: domain
value: example.com
cooldown
- Précise le délai minimal, en secondes, avant que la tâche puisse être redéclenchée pour le même projet.
- Valeur par défaut : 0 (aucun délai).
- Exemple (délai d'une heure) :
cooldown: 3600
batch
-
L'élément
batchpermet de regrouper des ressources pour l'exécution d'une tâche.Sous-élément Description Requis enabledValeur booléenne qui active ou désactive le regroupement. Oui sizeTaille maximale des tableaux d'entrée dans chaque lot. Facultatif, sans limite par défaut. Non -
Exemple (regroupement activé avec une taille de 50) :
batch:
enabled: true
size: 50
Exemple simple d'abonnement cron
Voici l'un des abonnements les plus courts possible. Il se déclenche tous les jours à midi pour lancer une tâche DomainNameResolvingJob,
avec example.com comme valeur du paramètre domainName.
Il résout donc le nom de domaine en adresse IP en lançant une tâche DomainNameResolvingJob. Si la résolution réussit, il émet un
HostnameIpFinding.
Il recommence le lendemain à midi.
name: my cron subscription
cronExpression: 0 0 12 * * ?
job:
name: DomainNameResolvingJob
parameters:
- name: domainName
value: example.com
Délai de réactivation d'un abonnement cron
Le délai de réactivation (cooldown) garantit qu'une tâche n'est lancée qu'aussi souvent que ce délai le permet. Par exemple, si une tâche
est lancée à 10 h pour un projet et que l'abonnement cron a un délai de 3600 secondes (1 heure), le même abonnement ne pourra pas la relancer
pour ce projet avant 11 h, même si l'expression cron se déclenche plus souvent.
Prenons aussi l'abonnement suivant. Son expression cron se déclenche toutes les heures, mais son délai est de 86400 secondes, soit 24 heures.
Pour un projet donné, la tâche DomainNameResolvingJob n'est donc lancée qu'une fois par jour. L'abonnement vérifie toutefois toutes les
heures s'il peut la lancer, à cause de l'expression cron.
name: my cron subscription
cronExpression: "0 */1 * * *"
input: ALL_DOMAINS
cooldown: 86400
job:
name: DomainNameResolvingJob
parameters:
- name: domainName
value: example.com
Variable input
La variable input précise une source d'entrée. Le tableau suivant décrit les sources d'entrée possibles. La variable input est
facultative. Les variables peuvent être injectées dans les paramètres de la tâche ainsi que dans les conditions.
Si un projet est précisé pour l'abonnement, seules les ressources de ce projet sont utilisées.
| Source d'entrée | Variables | Variables de regroupement |
|---|---|---|
| ALL_DOMAINS | ${domainName} | ${domainBatch} |
| ALL_HOSTS | ${ip} | ${ipBatch} |
| ALL_TCP_PORTS | ${ip}, ${port}, ${protocol} | ${ipBatch}, ${portBatch}, ${protocolBatch} |
| ALL_IP_RANGES | ${ip}, ${mask} | ${ipBatch}, ${maskBatch} |
| ALL_WEBSITES | ${domainName}, ${ip}, ${port}, ${protocol}, ${ssl}, ${path} | ${domainBatch}, ${ipBatch}, ${portBatch}, ${protocolBatch}, ${sslBatch}, ${pathBatch} |
Avec l'entrée ALL_DOMAINS, vous avez accès à la variable injectable ${domainName}. Red Kite applique l'abonnement à tous les domaines et
injecte la valeur de chaque domaine aux endroits indiqués.
name: Refreshing all domain names
input: ALL_DOMAINS
cronExpression: "0 0 * * *"
job:
name: DomainNameResolvingJob
parameters:
- name: domainName
value: ${domainName}
Avec l'entrée ALL_HOSTS, vous avez accès à la variable injectable ${ip}. Red Kite applique l'abonnement à tous les hôtes et injecte
l'adresse IP de chaque hôte aux endroits indiqués.
name: Rescanning all hosts
input: ALL_HOSTS
cronExpression: "0 0 * * *"
job:
name: TcpPortScanningJob
parameters:
- name: targetIp
value: ${ip}
- name: threads
value: 1000
- name: socketTimeoutSeconds
value: 0.7
- name: portMin
value: 1
- name: portMax
value: 65535
- name: ports
value: []
Avec l'entrée ALL_TCP_PORTS, vous avez accès aux variables injectables ${ip}, ${port} et ${protocol}. Red Kite applique
l'abonnement à tous les ports, et les valeurs de l'adresse IP, du port et du protocole peuvent être injectées aux endroits indiqués. Le
protocole est soit tcp, soit udp.
Seuls les ports TCP sont transmis avec cette entrée. La variable protocol vaut donc toujours 'tcp'.
name: All tcp ports with condition
input: ALL_TCP_PORTS
cronExpression: "0 0 * * *"
job:
name: HttpServerCheckJob
parameters:
- name: targetIp
value: ${ip}
- name: ports
value: ["${port}"]
conditions:
- lhs: "${protocol}"
operator: "equals"
rhs: "tcp"
Avec l'entrée ALL_IP_RANGES, vous avez accès aux variables injectables ${ip} et ${mask}. Red Kite applique l'abonnement à toutes les
plages d'adresses IP des projets.
name: Scanning IP ranges
cronExpression: 0 0 */7 * *
input: ALL_IP_RANGES
job:
name: TcpIpRangeScanningJob
parameters:
- name: targetIp
value: ${ip}
- name: targetMask
value: ${mask}
- name: rate
value: 100000
- name: portMin
value: 1
- name: portMax
value: 1000
- name: ports
value: []
Avec l'entrée ALL_WEBSITES, vous avez accès aux variables injectables ${domainName}, ${ip}, ${port}, ${path} et ${ssl}. Red
Kite applique l'abonnement à tous les sites web et injecte les différentes valeurs de chaque site aux endroits indiqués.
name: Recrawling websites
cronExpression: 0 0 */7 * *
input: ALL_WEBSITES
job:
name: WebsiteCrawlingJob
parameters:
- name: domainName
value: ${domainName}
- name: targetIp
value: ${ip}
- name: port
value: ${port}
- name: path
value: ${path}
- name: ssl
value: ${ssl}
- name: maxDepth
value: 3
- name: crawlDurationSeconds
value: 1800
- name: fetcherConcurrency
value: 10
- name: inputParallelism
value: 10
- name: extraOptions
value: -jc -kf all -duc -j -or -ob -silent
Regrouper les entrées d'une tâche
La syntaxe de regroupement (batching) d'un abonnement cron permet de lancer une tâche avec plusieurs valeurs en entrée. Au lieu de recevoir une simple chaîne de caractères, comme dans un abonnement cron standard, la tâche reçoit un tableau de chaînes, jusqu'à la taille indiquée.
Par exemple, l'abonnement suivant lance une tâche DomainNameResolvingJob en lui passant un tableau d'au plus 100 noms de domaine dans le
paramètre domainNames, grâce à la variable d'entrée domainBatch.
Toutes les variables d'entrée possibles sont présentées dans ce tableau.
name: Refreshing all domain names
input: ALL_DOMAINS
batch:
enabled: true
size: 100
cronExpression: "0 0 */7 * *"
job:
name: DomainNameResolvingJob
parameters:
- name: domainNames
value: ${domainBatch}
Certaines ressources, comme les ports, fournissent plusieurs valeurs en entrée à un abonnement cron. Avec la syntaxe de regroupement, chaque
valeur est transmise dans un tableau distinct, et toutes les valeurs situées à un même index i appartiennent à la même ressource. Tous les
tableaux ont donc la même longueur.
Par exemple, supposons que la base de données contient trois ports avec les caractéristiques suivantes :
| Index | Port | IP | Protocole |
|---|---|---|---|
| 0 | 22 | 1.1.1.1 | tcp |
| 1 | 80 | 2.2.2.2 | tcp |
| 2 | 443 | 3.3.3.3 | tcp |
Et un abonnement cron comme celui-ci :
name: Example cron subscription
input: ALL_TCP_PORTS
batch:
enabled: true # Le regroupement des entrées est activé
size: 100 # avec un maximum de 100 éléments par tableau, par tâche
cronExpression: "0 0 */7 * *"
job:
name: PortExampleJob
parameters:
- name: targetIps
value: ${ipBatch}
- name: ports
value: ${portBatch}
- name: protocols
value: ${protocolBatch}
Après l'injection des valeurs par le backend, vous obtiendriez les paramètres suivants :
parameters:
- name: targetIps
value: ["1.1.1.1", "2.2.2.2", "3.3.3.3"]
- name: ports
value: [22, 80, 443]
- name: protocols
value: ["tcp", "tcp", "tcp"]
Vous pourriez ensuite les utiliser de la façon suivante, en Python :
import os
import json
ips = json.loads(os.environ.get("targetIps")) # ['1.1.1.1', '2.2.2.2', '3.3.3.3']
ports = json.loads(os.environ.get("ports")) # [22, 80, 443]
protocols = json.loads(os.environ.get("protocols")) # ['tcp', 'tcp', 'tcp']
Avec une taille de lot de 100 et seulement trois ports dans la base de données, une seule tâche a été lancée. Par contre, avec 10 ports dans la base de données et une taille de lot de 3, 4 tâches seraient lancées, selon la logique suivante :
ceiling(10 / 3) => 4
Abonnements sur événement
Lorsque Red Kite trouve une information, que ce soit par la sortie d'une tâche ou par une saisie de l'utilisateur, un finding est émis. Ce finding contient l'information fournie par la tâche ou par l'utilisateur. À partir de cette information, de nouvelles tâches sont lancées et produisent à leur tour d'autres findings. C'est, en gros, le fonctionnement du flux d'automatisation de Red Kite.
Un abonnement sur événement permet de personnaliser ce flux d'automatisation en lançant n'importe quelle tâche à partir de n'importe quel finding. Ces tâches peuvent être lancées selon des conditions précises. De plus, la sortie du finding peut servir d'entrée à la tâche ainsi que de paramètre de condition.
Les abonnements sur événement ont aussi un délai de réactivation (cooldown), en secondes. Ce délai empêche qu'un abonnement soit déclenché
trop souvent pour la même ressource. Il évite par exemple les boucles infinies ou une nouvelle analyse immédiate du même hôte.
Syntaxe d'un abonnement sur événement
Un abonnement sur événement peut contenir les éléments principaux suivants :
| Élément | Description | Obligatoire |
|---|---|---|
| name | Le nom de l'abonnement, pour s'y retrouver plus tard | Oui |
| findings | Les findings auxquels réagir. Une liste de noms de findings. | Oui |
| cooldown | Le délai minimal, en secondes, avant que l'abonnement puisse être redéclenché pour la même ressource | Oui |
| job | La tâche à lancer lorsque le finding est reçu et que les conditions sont remplies | Oui |
| conditions | Les conditions préalables au lancement de la tâche | Non |
Remarque : les sections suivantes donnent plus de détails sur ces éléments.
- L'élément
namepeut contenir n'importe quoi. Il sert uniquement à distinguer l'abonnement des autres. - Les éléments
findingsdoivent être des types de findings existants. Consultez la documentation sur les findings pour en obtenir la liste. - L'élément
cooldownest un nombre de secondes à attendre avant de relancer la tâche pour la même ressource. - L'élément
jobcontient plusieurs valeurs :name: obligatoire, doit être un type de tâche existant. Consultez la section sur les tâches pour la liste des valeurs valides.parameters: facultatif, mais presque toujours nécessaire. Décrit, sous forme de liste, les valeurs d'entrée de la tâche par lenamedu paramètre et savalue.
- L'élément
conditionsest le seul élément principal facultatif. S'il est absent, la tâche est toujours lancée lorsque le finding est trouvé. Vous pouvez fournir plusieurs conditions dans une liste. Une condition contient plusieurs éléments :lhs: l'opérande de gauche.operator: l'opérateur qui compare les deux opérandes.rhs: l'opérande de droite.
Entrée dynamique d'un abonnement sur événement
Vous pouvez ajouter une entrée dynamique à un abonnement sur événement en faisant référence aux champs d'un finding ou en injectant un secret.
Vous pouvez faire référence à une variable de sortie d'un finding par son nom, dans la valeur d'un paramètre de tâche ou dans l'opérande
d'une condition, avec la syntaxe suivante : ${parameterName}. Le nom de la variable n'est pas sensible à la casse.
Un finding peut contenir des champs dynamiques dans le tableau fields. Les valeurs des champs
dynamiques de type texte peuvent être injectées comme un champ ordinaire, avec la syntaxe ${parameterName}. Utilisez simplement la partie
key du champ dynamique comme nom de variable, et son contenu data sera injecté.
Vous pouvez aussi injecter un secret comme valeur de paramètre avec la syntaxe ${secrets.secretName}.
Apprenez-en plus sur les secrets ici.
Exemple simple d'abonnement sur événement
Voici l'un des abonnements sur événement les plus courts possible. Il réagit à un HostnameFinding pour lancer une tâche
DomainNameResolvingJob, en utilisant la sortie du finding comme valeur du paramètre domainName.
Il résout donc le nom de domaine trouvé dans le HostnameFinding en adresse IP en lançant une tâche DomainNameResolvingJob. Si la
résolution réussit, il émet un HostnameIpFinding.
Le cooldown représente ici 23 heures. Par cet abonnement, un même nom d'hôte ne peut donc être résolu qu'une fois toutes les 23 heures au
plus.
Cet abonnement est déjà intégré à Red Kite.
name: Domain name resolution
findings:
- HostnameFinding
cooldown: 82800
job:
name: DomainNameResolvingJob
parameters:
- name: domainName
value: ${domainName}
Exemple complexe d'abonnement sur événement
Voici comment lancer une tâche TcpPortScanningJob lorsqu'un HostnameIpFinding est émis. La tâche ne démarre toutefois que si toutes les
conditions sont remplies : l'ip trouvée par le finding doit contenir la chaîne « 13.37 » (sans égard à la casse), et 5 doit être
supérieur ou égal à 3.
Si les conditions sont remplies lorsqu'un HostnameIpFinding est émis, la tâche démarre avec les paramètres indiqués. Remarquez que le
paramètre targetIp contiendra la valeur de la variable de sortie ip du finding.
Dans ce cas, la tâche analyse les 1000 premiers ports de ${ip} ([1-1000]) ainsi que les ports 1234, 3389 et 8080. Elle le fait avec 10
fils d'exécution en parallèle. Le socket de chaque fil a un délai d'expiration de 1 seconde s'il ne reçoit pas de réponse. On peut donc
s'attendre à ce que la tâche se termine en environ 100 secondes au maximum.
Un abonnement au comportement semblable, mis à part les conditions, est déjà intégré à Red Kite.
name: My complex subscription
findings:
- HostnameIpFinding
cooldown: 82800
job:
name: TcpPortScanningJob
parameters:
- name: targetIp
value: ${ip}
- name: threads
value: 10
- name: socketTimeoutSeconds
value: 1
- name: portMin
value: 1
- name: portMax
value: 1000
- name: ports
value: [1234, 3389, 8080]
conditions:
- lhs: ${ip}
operator: contains_i
rhs: "13.37"
- lhs: 5
operator: gte
rhs: 3
Plusieurs findings dans un abonnement
Un abonnement sur événement peut être déclenché par plusieurs types de findings, à condition que les valeurs utilisées dans l'abonnement soient disponibles dans chacun de ces types.
Par exemple, un abonnement peut être déclenché à la fois par un HostnameIpFinding et par un IpFinding, et utiliser leurs champs
communs.
Le HostnameIpFinding :
{
"type": "HostnameIpFinding",
"key": "HostnameIpFinding",
"domainName": "red-kite.io",
"ip": "0.0.0.0"
}
L'IpFinding :
{
"type": "IpFinding",
"key": "IpFinding",
"ip": "0.0.0.0"
}
Comme le HostnameIpFinding et l'IpFinding ont tous deux un champ ip, ce champ peut être utilisé dans l'abonnement.
name: My complex subscription
findings:
- HostnameIpFinding
- IpFinding
cooldown: 82800
job:
name: TcpPortScanningJob
parameters:
- name: targetIp
value: ${ip}
- name: threads
value: 10
- name: socketTimeoutSeconds
value: 1
- name: portMin
value: 1
- name: portMax
value: 1000
- name: ports
value: [1234, 3389, 8080]
conditions:
- lhs: ${ip}
operator: contains_i
rhs: "13.37"
- lhs: 5
operator: gte
rhs: 3
Findings
Red Kite propage un événement de finding chaque fois qu'une nouvelle information entre en jeu. Chaque type de finding contient des informations qui lui sont propres.
Il est possible d'utiliser un finding produit par une tâche comme entrée d'une nouvelle tâche, ou comme opérande d'une condition. Les références aux variables de sortie d'un finding ne sont jamais sensibles à la casse.
Pour en savoir plus sur les findings, cliquez ici.
Conditions
Par défaut, la tâche d'un abonnement sur événement est toujours lancée lorsque le finding indiqué est trouvé. Ce n'est toutefois pas toujours le comportement souhaité. Parfois, la tâche ne devrait démarrer que si la sortie du finding respecte certaines conditions.
Une condition se compose habituellement de trois éléments : un paramètre de gauche (lhs), un opérateur (operator) et un paramètre de
droite (rhs). Le paramètre lhs est comparé au paramètre rhs à l'aide de l'opérateur. Une variable de sortie d'un finding peut servir
de paramètre lhs ou rhs d'une condition lorsqu'elle est référencée avec la syntaxe ${paramName}.
Le type primitif des paramètres doit être le même des deux côtés et doit correspondre à l'opérateur pour que la condition retourne true.
Les types acceptés pour un opérande sont string, number, boolean, ou un array de ces types. Lorsque deux tableaux sont fournis,
chaque élément du tableau lhs est comparé à chaque élément du tableau rhs. Si toutes les conditions retournent vrai, la tâche indiquée
est lancée.
| Opérateur | Types d'opérandes acceptés | Description |
|---|---|---|
| gte | number | lhs >= rhs |
| gt | number | lhs > rhs |
| lte | number | lhs <= rhs |
| lt | number | lhs < rhs |
| equals | string, number, boolean | lhs == rhs |
| equals_i | string | Insensible à la casse, lhs == rhs |
| contains | string | Vérifie si la chaîne lhs contient la chaîne rhs. |
| contains_i | string | Vérifie si la chaîne lhs contient la chaîne rhs. Insensible à la casse. |
| startsWith | string | Vérifie si la chaîne lhs commence par la chaîne rhs. |
| startsWith_i | string | Vérifie si la chaîne lhs commence par la chaîne rhs. Insensible à la casse. |
| endsWith | string | Vérifie si la chaîne lhs se termine par la chaîne rhs. |
| endsWith_i | string | Vérifie si la chaîne lhs se termine par la chaîne rhs. Insensible à la casse. |
| not_ | string, number, boolean | Ajoutez le préfixe not_ à un autre opérateur pour inverser le résultat. Par exemple, not_equals est vrai lorsque deux opérandes ne sont pas égaux. |
| or_ | string, number, boolean | À utiliser avec des tableaux pour remplacer l'opérateur booléen entre les éléments du tableau, de and à or. |
Des tableaux du type correspondant peuvent être utilisés avec n'importe quel opérateur.
Par défaut, les conditions sont liées par un opérateur and. Toutes les conditions doivent donc être remplies pour lancer la tâche. Vous
pouvez toutefois utiliser les opérateurs and et or dans le yaml des conditions pour changer l'opérateur appliqué entre les conditions.
Dans l'exemple suivant, même si la condition 4 >= 5 est false, elle se trouve dans un opérateur or : l'ensemble de cet opérateur
retourne donc true. Comme l'opérateur or retourne true et que 1 <= 2 est aussi true, la tâche est lancée.
conditions:
- or:
- and:
- lhs: asdf
operator: endsWith
rhs: df
- lhs: qwerty
operator: startsWith
rhs: qwe
- lhs: 4
operator: gte
rhs: 5
- lhs: 1
operator: lte
rhs: 2
La condition précédente équivaut à peu près à l'instruction if Python suivante :
if (("qwerty".startswith("qwe") and "asdf".endswith("df")) or 4 >= 5) and 1 <= 2:
print("Job will start")