16 KiB
Table of Contents
- Construire une liste d'options
- La configuration du proxy type Mozilla Firefox
- La famille proxy
- Le type de proxy
- Le mode manuel
- La configuration du proxy HTTP
- Dupliquer la configuration HTTP vers HTTPS
- La configuration du proxy HTTPS
- La configuration du proxy SOCKS
- Le mode détection automatique
- Les exceptions au proxy
- La demande d'authentification
- Le DNS du proxy SOCKS v5
- Le DNS à travers HTTPS
- La configuration du proxy type FoxyProxy
Construire une liste d'options
Rougail permet de construite des options Tiramisu a partir de dictionnaire écrit en YAML. Le principal avantage étant que l'écrire de option est beaucoup plus simple.
Une fois chargé, on retrouve toute la puissance de Tiramisu dans la gestion de la configuration.
Avant de commencer, il faut connaitre les spécificités du format de fichier YAML, des notions de Jinja et de Tiramisu.
La configuration du proxy type Mozilla Firefox
L'objectif de ce première exemple est de reproduire cette page de paramètres de Mozilla Firefox :
Les variables vont être créées dans plusieurs fichiers dans un but didactique. Bien évidement toutes les variables pourront être mise dans le même fichier.
La famille proxy
Nous allons classer toutes ces variables dans une famille. Cette famille s'appelera proxy. Créons le premier fichier dict/00-proxy.yml
---
version: '1.0'
proxy:
description: Configure Proxy Access to the Internet
type: family
Le type de la famille est ici précisé parce qu'on n'a pas de variable ou de famille à l'intérieur de cette famille. Le moteur pensera alors que c'est une simple variable.
Le type de proxy
Il est possible de définir plusieurs modes de configuration du proxy (de "pas de proxy" à "la configuration d'un fichier de configuration automatique").
Nous allons donc créer une première variable dans cette famille dont la description est "Proxy mode". Créons le deuxième fichier dict/01-proxy_mode.yml
---
version: '1.0'
proxy:
proxy_mode:
description: Proxy mode
type: choice
choices:
- No proxy
- Auto-detect proxy settings for this network
- Use system proxy settings
- Manual proxy configuration
- Automatic proxy configuration URL
default: No proxy
mandatory: true
Cette variable nécessite une valeur (la valeur None n'est pas acceptable), elle est donc obligatoire (mandatory).
Si l'utilisateur ne précise pas de valeurs, la valeur de cette variable sera "No proxy" (default).
La variable est à choix (type: choice) donc la liste des valeurs disponible est contrainte (choices), seul les valeurs suivantes sont autorisés :
- No proxy
- Auto-detect proxy settings for this network
- Use system proxy settings
- Manual proxy configuration
- Automatic proxy configuration URL
Testons nos deux premiers dictionnaires :
>>> from rougail import Rougail, RougailConfig
>>> from pprint import pprint
>>> RougailConfig['dictionaries_dir'] = ['dict']
>>> rougail = Rougail()
>>> config = rougail.get_config()
>>> config.property.read_only()
>>> pprint(config.value.dict(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'No proxy'}
Le mode manuel
Si l'utilisateur choisi le mode de proxy "Manual proxy configuration", on veut voir apparaitre (disabled) une nouvelle sous-famille appelé manual. Créons le fichier dict/02-proxy_manual.yml :
---
version: '1.0'
proxy:
manual:
description: Manual proxy configuration
type: family
disabled:
type: jinja
jinja: |
{% if rougail.proxy.proxy_mode == 'Manual proxy configuration' %}
false
{% else %}
true
{% endif %}
La configuration du proxy HTTP
Dans cette famille ajoutons une sous-famille http_proxy contenant les variables address et port. Créons le fichier dict/03-proxy_manual_http_proxy.yml :
---
version: '1.0'
proxy:
manual:
http_proxy:
description: HTTP Proxy
address:
description: HTTP address
type: domainname
mandatory: true
port:
description: HTTP Port
type: port
default: '8080'
Les deux variables ont des types particuliers (domainname ou port) pour valider les valeurs configurer par l'utilisateur.
Pas la peine de précisé le type de http_proxy parce qu'on a déclaré les sous-variables dans ce fichier.
Dupliquer la configuration HTTP vers HTTPS
On veux proposer à l'utilisateur la possiblité de renseigner le même proxy pour les requêtes HTTPS. Créons le fichier dict/04-proxy_manual_http_use_for_https.yml :
version: '1.0'
proxy:
manual:
use_for_https:
description: Also use this proxy for HTTPS
type: boolean
Cette variable est de type boolean. Sa valeur par défaut est True.
La configuration du proxy HTTPS
Ajoutons une nouvelle sous-famille ssl_proxy avec les variables address et port. Créons le fichier dict/05-proxy_manual_ssl_proxy.yml :
version: '1.0'
proxy:
manual:
ssl_proxy:
description: HTTPS Proxy
hidden:
type: variable
variable: rougail.proxy.manual.use_for_https
address:
description: HTTPS address
type: domainname
default:
type: jinja
jinja: |
{% if rougail.proxy.manual.use_for_https %}
{{ rougail.proxy.manual.http_proxy.address }}
{% endif %}
mandatory: true
port:
description: HTTPS Port
type: port
default:
type: jinja
jinja: |
{% if rougail.proxy.manual.use_for_https %}
{{ rougail.proxy.manual.http_proxy.port }}
{% endif %}
mandatory: true
On vient de recréer un variable avec le nom "address". Ce n'est pas un problème, puisqu'elle est dans une autre famille.
Suivant la valeur de la variable rougail.proxy.mandatory.use_for_https (sans passé par une fonction Jinja ce coup-ci) cette famille apparaitra ou disparaitra (hidden). Contrairement à tout à l'heure, la famille n'est pas désactivé (disabled) parce que les variables devront rester accessible en mode lecture seule.
Testons ce point précis :
>>> from rougail import Rougail, RougailConfig
>>> from pprint import pprint
>>> RougailConfig['dictionaries_dir'] = ['dict']
>>> rougail = Rougail()
>>> config = rougail.get_config()
>>> config.property.read_only()
>>> pprint(config.value.dict(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'No proxy'}
>>> config.property.read_write()
>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration')
>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example')
>>> pprint(config.value.dict(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': True}
>>> config.property.read_only()
>>> pprint(config.value.dict(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': True,
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
'rougail.proxy.manual.ssl_proxy.port': '8080'}
Alors que la famille rougail.proxy.manual n'était pas présente lors que rougail.proxy.proxy_mode est à No proxy, la famille rougail.proxy.manual.ssl_proxy devient visible en mode lecture seule (et uniquement en mode lecture seule) si rougail.proxy.manual.use_for_https est à 'True'.
On voit bien également que les valeurs des variables de rougail.proxy.manual.http_proxy on bien été copié dans rougail.proxy.manual.ssl_proxy. Ces valeurs ne seront copié que si use_for_https est à "True" comme précisé dans les valeurs par défaut des variables.
De plus, si l'utilisateur modifie la valeur de la variable rougail.proxy.manual.ssl_proxy.address mais qu'il cache par la suite cette variable, la valeur de cette variable reviens à la valeur par défaut :
>>> config.property.read_write()
>>> config.option('rougail.proxy.manual.use_for_https').value.set(False)
>>> config.option('rougail.proxy.manual.ssl_proxy.address').value.set('other.proxy.example')
>>> pprint(config.value.dict(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': False,
'rougail.proxy.manual.ssl_proxy.address': 'other.proxy.example',
'rougail.proxy.manual.ssl_proxy.port': '8080'}
>>> config.option('rougail.proxy.manual.use_for_https').value.set(False)
>>> config.property.read_only()
>>> pprint(config.value.dict(), sort_dicts=False)
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
'rougail.proxy.manual.http_proxy.port': '8080',
'rougail.proxy.manual.use_for_https': False,
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
'rougail.proxy.manual.ssl_proxy.port': '8080'}
La configuration du proxy SOCKS
Ajoutons une nouvelle sous-famille socks_proxy avec les variables address, port et version. Créons le fichier dict/06-proxy_manual_socks_proxy.yml :
version: '1.0'
proxy:
manual:
socks_proxy:
description: SOCKS Proxy
address:
description: SOCKS Address
type: domainname
port:
description: SOCKS Port
type: port
version:
description: SOCKS host version used by proxy
type: choice
choices:
- v4
- v5
default: v5
Le mode détection automatique
Ajoutons une nouvelle sous-variable auto. Créons le fichier dict/07-proxy_auto.yml :
version: '1.0'
proxy:
auto:
type: web_address
description: Automatic proxy configuration URL
mandatory: true
disabled:
type: jinja
jinja: |
{% if rougail.proxy.proxy_mode == 'Automatic proxy configuration URL' %}
false
{% else %}
true
{% endif %}
Le type web_address impose une valeur qui commence par http:// ou https://.
Les exceptions au proxy
Enfin ajoutons une variable contenant les exceptions au proxy. Pour cela créons le fichier dict/07-proxy_no_proxy.yml :
version: '1.0'
proxy:
no_proxy:
description: Address for which proxy will be desactivated
multi: true
disabled:
type: jinja
jinja: |
{% if rougail.proxy.proxy_mode == 'No proxy' %}
true
{% else %}
false
{% endif %}
validators:
- type: jinja
jinja: '{{ rougail.no_proxy | valid_no_proxy }}'
Il peut y avoir plusieurs exceptions au proxy au proxy, la variable est donc multi.
Cette varible n'est pas accessible uniquement si aucun proxy n'est défini.
Enfin, on veut valider le contenu de la variable. Il n'y a pas de type correspond dans Rougail, donc on fait la fonction de validation suivante dans le fichier functions.py :
from tiramisu import DomainnameOption
def valid_no_proxy(value: str):
try:
DomainnameOption('',
'',
value,
allow_ip=True,
allow_cidr_network=True,
allow_without_dot=True,
allow_startswith_dot=True,
)
except ValueError as err:
return err
Pour tester :
from rougail import Rougail, RougailConfig
RougailConfig['functions_file'] = 'functions.py'
from pprint import pprint
RougailConfig['dictionaries_dir'] = ['dict']
rougail = Rougail()
config = rougail.get_config()
config.property.read_only()
pprint(config.value.dict(), sort_dicts=False)
La demande d'authentification
Rien de particulier à la création de la demande d'authentification. Pour cela créons le fichier dict/08-proxy_prompt_authentication.yml :
version: '1.0'
proxy:
prompt_authentication:
description: Prompt for authentication if password is saved
type: boolean
default: true
disabled:
type: jinja
jinja: |
{% if rougail.proxy.proxy_mode == 'No proxy' %}
true
{% else %}
false
{% endif %}
Le DNS du proxy SOCKS v5
La question sur le DNS pour le proxy SOCKS v5 n'apparait que si le proxy est configuré et que la version du proxy SOCKS sélectionné est bien la "v5".
Créons le fichier dict.09-proxy_proxy_dns_socks5.yml :
version: '1.0'
proxy:
proxy_dns_socks5:
description: Use proxy DNS when using SOCKS v5
type: boolean
default: false
disabled:
type: jinja
params:
socks_version:
type: variable
variable: rougail.proxy.manual.socks_proxy.version
propertyerror: false
jinja: |
{% if rougail.proxy.proxy_mode == 'No proxy' %}
true
{% elif socks_version is undefined or socks_version == 'v4' %}
true
{% else %}
false
{% endif %}
La difficulté ici c'est que la variable rougail.proxy.manual.socks_proxy.version peut être désactivé (et donc non utilisable dans un calcul).
Dans ce cas, nous allons ajouter un paramètre (ici appelé socks_version) qui contiendra, s'il n'y a pas d'erreur de propriété, la valeur de la variable.
Sinon le paramètre ne sera pas passé au template Jinja. C'est pourquoi il faut tester dans le template Jinja si la variable socks_version existe bien.
Le DNS à travers HTTPS
Enfin nous allons configurer le DNS à travers HTTPS dans le fichier 10-proxy_dns_over_https.yml :
version: '1.0'
proxy:
dns_over_https:
description: DNS over HTTPS
enable_dns_over_https:
description: Enable DNS over HTTPS
type: boolean
default: false
provider:
description: Use Provider
type: choice
choices:
- Cloudflare
- NextDNS
- Custom
default: Cloudflare
disabled:
type: jinja
jinja: |
{% if rougail.proxy.dns_over_https.enable_dns_over_https %}
false
{% else %}
true
{% endif %}
custom_dns_url:
description: Custom DNS URL
type: web_address
mandatory: true
disabled:
type: jinja
params:
provider:
type: variable
variable: rougail.proxy.dns_over_https.provider
propertyerror: false
jinja: |
{% if provider is defined and provider == 'Custom' %}
false
{% else %}
true
{% endif %}
validators:
- type: jinja
jinja: |
{% if rougail.proxy.dns_over_https.custom_dns_url.startswith('http://') %}
only https is allowed
{% endif %}
La seule particularitée ici est qu'on a ajouté une validation supplémentaire à la variable custom_dns_url. Seul une adresse commençant par https:// est autorisé (pas http://).
La configuration du proxy type FoxyProxy
---
foxyproxy:
_type: leadership
title:
description: Title or Description
multi: true
color:
description: Color
mandatory: true
type:
type: choice
choices:
- HTTP
- HTTPS/SSL
- SOCKS5
- SOCKS4
- PAC URL
- WPAD
- System (use system settings)
- Direct (no proxy)
default: Direct (no proxy)
address:
description: IP address, DNS name, server name
multi: true
mandatory: true
port:
description: Port
type: port
mandatory: true
username:
description: Username
type: unix_user
mandatory:
type: jinja
jinja: |
{% if rougail.proxy.password %}
true
{% else %}
false
{% endif %}
password:
type: secret
---
foxyproxy:
username:
redefine: true
mandatory: false
password:
hidden:
type: jinja
jinja: |
{% if rougail.foxyproxy.username %}
false
{% else %}
true
{% endif %}
