import re
# Chemin vers le fichier ns.conf
NS_CONF_PATH = r"C:\tempmax\ns.conf"
# Dictionnaire des chemins dans l'interface ADC
object_paths = {
"authentication policy": "Security > AAA-Application Traffic > Policies > Authentication > Advanced Policies > Authentication Policies",
"authentication policylabel": "Security > AAA-Application Traffic > Policies > Authentication > Advanced Policies > Policy Labels",
"authentication authnprofile": "Security > AAA-Application Traffic > Authentication Profile",
"authentication vserver": "Security > AAA-Application Traffic > Authentication Virtual Servers",
"authentication loginschema": "Security > AAA-Application Traffic > Authentication > Login Schemas",
"vpn sessionpolicy": "Citrix Gateway > Policies > Session Policies",
"vpn trafficpolicy": "Citrix Gateway > Policies > Traffic Policies, Profiles and Form SSO Profiles",
"rewrite policy": "AppExpert > Rewrite > Rewrite Policy",
"responder policy": "AppExpert > Responder > Responder Policy",
"cache policy": "AppExpert > Cache > Cache Policy",
"lb policy": "Traffic Management > Load Balancing > Load Balancing Policies",
"cs policy": "AppExpert > Content Switching > Content Switching Policies",
"aaa policy": "Citrix Gateway > Policies > AAA > AAA Policies",
"dns policy": "Traffic Management > DNS > DNS Policies",
"appfw policy": "Security > Application Firewall > Policies",
"videooptimization policy": "Traffic Management > Video Optimization > Video Optimization Policies",
"contentinspection policy": "Security > Content Inspection > Policies",
"servicegroup": "Traffic Management > Load Balancing > Service Groups",
"ssl certkey": "Traffic Management > SSL > Certificates and Keys",
"server": "Traffic Management > Servers"
}
def load_ns_conf(file_path):
"""Charge le fichier ns.conf et retourne son contenu sous forme de liste de lignes."""
try:
with open(file_path, "r", encoding="utf-8") as f:
return f.readlines()
except FileNotFoundError:
print(f"Erreur : Le fichier {file_path} est introuvable.")
return []
def find_defined_objects(config_lines, object_type):
"""
Extrait les objets définis via la commande add pour un type donné.
Par exemple, pour un responder policy :
add responder policy RESPPOL_MONITOR_SF_for_F5 TRUE RESPACT_MONITOR_SF_for_F5
"""
pattern = re.compile(rf'add {object_type} ("[^"]+"|\S+)', re.IGNORECASE)
return set(match.group(1).strip('"') for match in pattern.finditer("\n".join(config_lines)))
def find_bound_objects(config_lines, object_type):
"""
Extrait les objets réellement bindés dans la configuration.
- Pour **authentication authnprofile** : seuls ceux utilisés dans une commande add vpn vserver avec -authnProfile.
- Pour **authentication vserver** : on construit un mapping à partir des définitions d'authnprofile (avec -authnVsName)
et on considère comme utilisés les AAA vservers associés aux authnprofiles utilisés dans une commande add vpn vserver.
- Pour **authentication policy** et **policylabel** : on recherche dans les bindings d'un authentication vserver via -policy et -nextFactor.
- Pour **responder policy** : on recherche dans les commandes bind vpn vserver l'option -policy (puisqu'elle n'est pas utilisée avec -policyName).
- Pour les VPN (sessionpolicy, trafficpolicy) et SSL, on utilise des regex spécifiques.
- Pour les autres types (rewrite, cache, cs, etc.), on recherche partout l'option -policyName.
"""
config_str = "\n".join(config_lines)
if object_type.lower() == "authentication authnprofile":
pattern = re.compile(
r'add vpn vserver\s+\S+.*?-authnProfile\s+("([^"]+)"|\S+)',
re.IGNORECASE | re.DOTALL
)
return { match.group(1).strip('"') for match in pattern.finditer(config_str) }
elif object_type.lower() == "authentication vserver":
mapping = {}
pattern_map = re.compile(
r'add authentication authnProfile\s+("([^"]+)"|\S+).*?-authnVsName\s+("([^"]+)"|\S+)',
re.IGNORECASE
)
for match in pattern_map.finditer(config_str):
profile = match.group(1).strip('"')
aaa_vserver = match.group(3).strip('"')
mapping[profile] = aaa_vserver
pattern_bound = re.compile(
r'add vpn vserver\s+\S+.*?-authnProfile\s+("([^"]+)"|\S+)',
re.IGNORECASE | re.DOTALL
)
bound_profiles = { match.group(1).strip('"') for match in pattern_bound.finditer(config_str) }
return { mapping[ap] for ap in bound_profiles if ap in mapping }
elif object_type.lower() == "authentication policy":
direct_pattern = re.compile(
r'bind authentication vserver\s+(\S+).*?-policy\s+("([^"]+)"|\S+)',
re.IGNORECASE | re.DOTALL
)
direct_bound = { match.group(2).strip('"') for match in direct_pattern.finditer(config_str)
if match.group(1).strip('"').upper().startswith("AAA_AUTHVSERVER") }
next_pattern = re.compile(
r'bind authentication vserver\s+\S+.*?-nextFactor\s+(\S+)',
re.IGNORECASE | re.DOTALL
)
labels_bound = { match.group(1).strip('"') for match in next_pattern.finditer(config_str) }
return direct_bound.union(labels_bound)
elif object_type.lower() == "authentication policylabel":
pattern = re.compile(
r'bind authentication vserver\s+\S+.*?-nextFactor\s+(\S+)',
re.IGNORECASE | re.DOTALL
)
return { match.group(1).strip('"') for match in pattern.finditer(config_str) }
elif object_type.lower() == "responder policy":
# Recherche dans les commandes bind vpn vserver l'option -policy pour extraire le nom du responder policy
pattern = re.compile(
r'bind vpn vserver\s+\S+.*?-policy\s+("([^"]+)"|\S+)',
re.IGNORECASE | re.DOTALL
)
return { match.group(1).strip('"') for match in pattern.finditer(config_str) }
elif object_type.lower() in ["vpn sessionpolicy", "vpn trafficpolicy"]:
pattern = re.compile(
r'bind vpn vserver\s+\S+.*?-policy\s+("([^"]+)"|\S+)',
re.IGNORECASE | re.DOTALL
)
return { match.group(1).strip('"') for match in pattern.finditer(config_str) }
elif object_type.lower() == "ssl certkey":
pattern = re.compile(
r'bind ssl (?:service|vserver)\s+\S+.*?-certkeyName\s+("([^"]+)"|\S+)',
re.IGNORECASE | re.DOTALL
)
return { match.group(1).strip('"') for match in pattern.finditer(config_str) }
elif object_type.lower() in {"rewrite policy", "cache policy", "cs policy",
"aaa policy", "dns policy", "appfw policy", "videooptimization policy",
"contentinspection policy"}:
pattern = re.compile(r'-policyName\s+("([^"]+)"|\S+)', re.IGNORECASE)
return { match.group(1).strip('"') for match in pattern.finditer(config_str) }
else:
pattern_direct = re.compile(rf'bind {object_type} ("[^"]+"|\S+)', re.IGNORECASE)
pattern_label = re.compile(rf'bind {object_type} policylabel [^\s]+ -policyName ("[^"]+"|\S+)', re.IGNORECASE)
bound = { match.group(1).strip('"') for match in pattern_direct.finditer(config_str) }
bound.update(match.group(1).strip('"') for match in pattern_label.finditer(config_str))
return bound
def find_servers_bound_to_service_groups(config_lines):
"""Extrait les serveurs liés à un serviceGroup ou à un vserver."""
config_str = "\n".join(config_lines)
pattern = re.compile(r'bind serviceGroup\s+("[^"]+"|\S+)\s+("[^"]+"|\S+)', re.IGNORECASE)
bound = { match.group(2).strip('"') for match in pattern.finditer(config_str) }
pattern_vserver = re.compile(r'bind lb vserver\s+("[^"]+"|\S+)\s+("[^"]+"|\S+)', re.IGNORECASE)
bound.update(match.group(2).strip('"') for match in pattern_vserver.finditer(config_str))
return bound
def find_unbound_objects(file_path, object_types):
"""Retourne, pour chaque type d'objet, l'ensemble des objets définis mais non bindés."""
config_lines = load_ns_conf(file_path)
if not config_lines:
return {}
unbound = {}
for obj_type in object_types:
defined = find_defined_objects(config_lines, obj_type)
bound = find_bound_objects(config_lines, obj_type)
unbound[obj_type] = defined - bound
if "server" in unbound:
servers_bound = find_servers_bound_to_service_groups(config_lines)
unbound["server"] -= servers_bound
return unbound
def main():
object_types = [
"authentication policy",
"authentication policylabel",
"authentication authnprofile",
"authentication vserver",
"authentication loginschema",
"vpn sessionpolicy",
"vpn trafficpolicy",
"rewrite policy",
"responder policy",
"cache policy",
"lb policy",
"cs policy",
"aaa policy",
"dns policy",
"appfw policy",
"videooptimization policy",
"contentinspection policy",
"servicegroup",
"ssl certkey",
"server"
]
unbound = find_unbound_objects(NS_CONF_PATH, object_types)
# Afficher uniquement les catégories ayant des objets non bindés
for obj_type in object_types:
objs = unbound.get(obj_type, set())
if not objs:
continue
path = object_paths.get(obj_type.lower(), "Chemin non défini")
print(f"Emplacement : {path}")
print(f"Type d'objet : {obj_type}")
print(" Objets non bindés :")
for o in sorted(objs, key=str.lower):
if obj_type.lower() == "authentication vserver":
print(f" - {o} (à vérifier car non bindé à un VPN Vserver)")
else:
print(f" - {o}")
print()
if __name__ == "__main__":
main()