| 
				
			 | 
			
			
				
				@@ -0,0 +1,159 @@ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				1
			 | 
			
			
				
				+#! /usr/bin/env python3 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				2
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				3
			 | 
			
			
				
				+import json 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				4
			 | 
			
			
				
				+import argparse 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				5
			 | 
			
			
				
				+import os 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				6
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				7
			 | 
			
			
				
				+from os import path 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				8
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				9
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				10
			 | 
			
			
				
				+class SiteGenException(Exception): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				11
			 | 
			
			
				
				+    error = None 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				12
			 | 
			
			
				
				+    code = None 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				13
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				14
			 | 
			
			
				
				+    def __init__(self, error, code): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				15
			 | 
			
			
				
				+        self.error = error 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				16
			 | 
			
			
				
				+        self.code = code 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				17
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				18
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				19
			 | 
			
			
				
				+class SiteGen: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				20
			 | 
			
			
				
				+    siteConfDir = "" 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				21
			 | 
			
			
				
				+    siteDir = "" 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				22
			 | 
			
			
				
				+    confDir = "" 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				23
			 | 
			
			
				
				+    hooksEnabledDir = "" 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				24
			 | 
			
			
				
				+    hooksAvailableDir = "" 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				25
			 | 
			
			
				
				+    templatesDir = "" 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				26
			 | 
			
			
				
				+    certRenewTime = "" 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				27
			 | 
			
			
				
				+    letsencryptCommand = "" 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				28
			 | 
			
			
				
				+    certDir = "" 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				29
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				30
			 | 
			
			
				
				+    def __init__(self, config): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				31
			 | 
			
			
				
				+        self.siteConfDir = config["siteConfDir"] 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				32
			 | 
			
			
				
				+        self.siteDir = config["siteDir"] 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				33
			 | 
			
			
				
				+        self.confDir = config["confDir"] 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				34
			 | 
			
			
				
				+        self.hooksEnabledDir = path.join(self.confDir, "hooks-enabled") 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				35
			 | 
			
			
				
				+        self.hooksAvailableDir = path.join(self.confDir, "hooks-available") 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				36
			 | 
			
			
				
				+        self.templatesDir = path.join(self.confDir, "templates") 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				37
			 | 
			
			
				
				+        self.certRenewTime = config["certRenewTime"] 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				38
			 | 
			
			
				
				+        self.letsencryptCommand = config["letsencryptCommand"] 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				39
			 | 
			
			
				
				+        self.certDir = config["certDir"] 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				40
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				41
			 | 
			
			
				
				+    def get_hook_dir(self, hook_type, is_enabled): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				42
			 | 
			
			
				
				+        return path.join(self.hooksEnabledDir if is_enabled else self.hooksAvailableDir, hook_type) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				43
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				44
			 | 
			
			
				
				+    def get_hook_file(self, hook_type, hook_name, is_enabled): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				45
			 | 
			
			
				
				+        return path.join(self.get_hook_dir(hook_type, is_enabled), hook_name) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				46
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				47
			 | 
			
			
				
				+    def get_hook_files(self, hook_type, is_enabled): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				48
			 | 
			
			
				
				+        hook_dir = self.get_hook_dir(hook_type, is_enabled) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				49
			 | 
			
			
				
				+        files = os.listdir(hook_dir) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				50
			 | 
			
			
				
				+        files.sort() 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				51
			 | 
			
			
				
				+        return files 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				52
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				53
			 | 
			
			
				
				+    def is_hook_present(self, hook_type, hook_name, is_enabled): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				54
			 | 
			
			
				
				+        return path.isfile(self.get_hook_file(hook_type, hook_name, is_enabled)) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				55
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				56
			 | 
			
			
				
				+    def is_hook_enabled(self, hook_type, hook_name): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				57
			 | 
			
			
				
				+        return self.is_hook_present(hook_type, hook_name, True) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				58
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				59
			 | 
			
			
				
				+    def cert_request(self, domain): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				60
			 | 
			
			
				
				+        pass 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				61
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				62
			 | 
			
			
				
				+    def cert_check(self, domain): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				63
			 | 
			
			
				
				+        pass 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				64
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				65
			 | 
			
			
				
				+    def cert_renew(self, domain): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				66
			 | 
			
			
				
				+        pass 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				67
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				68
			 | 
			
			
				
				+    def site_create(self, domain): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				69
			 | 
			
			
				
				+        pass 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				70
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				71
			 | 
			
			
				
				+    def site_remove(self, domain): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				72
			 | 
			
			
				
				+        pass 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				73
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				74
			 | 
			
			
				
				+    def hook_enable(self, hook_type, hook_name): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				75
			 | 
			
			
				
				+        if not self.is_hook_present(hook_type, hook_name, False): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				76
			 | 
			
			
				
				+            raise SiteGenException("Hook is not present", 1) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				77
			 | 
			
			
				
				+        if self.is_hook_enabled(hook_type, hook_name): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				78
			 | 
			
			
				
				+            raise SiteGenException("Hook is already enabled", 0) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				79
			 | 
			
			
				
				+        hook_dir = self.get_hook_dir(hook_type, hook_name) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				80
			 | 
			
			
				
				+        if not path.isdir(hook_dir): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				81
			 | 
			
			
				
				+            os.makedirs(hook_dir) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				82
			 | 
			
			
				
				+        hook_file_available = self.get_hook_file(hook_type, hook_name, False) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				83
			 | 
			
			
				
				+        hook_file_enabled = self.get_hook_file(hook_type, hook_name, True) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				84
			 | 
			
			
				
				+        hook_relative_file = path.relpath(hook_file_available, self.get_hook_dir(hook_type, True)) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				85
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				86
			 | 
			
			
				
				+        os.symlink(hook_relative_file, hook_file_enabled) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				87
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				88
			 | 
			
			
				
				+    def hook_disable(self, hook_type, hook_name): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				89
			 | 
			
			
				
				+        if not self.is_hook_present(hook_type, hook_name, False): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				90
			 | 
			
			
				
				+            raise SiteGenException("Hook is not present", 1) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				91
			 | 
			
			
				
				+        if not self.is_hook_enabled(hook_type, hook_name): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				92
			 | 
			
			
				
				+            raise SiteGenException("Hook is not enabled", 0) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				93
			 | 
			
			
				
				+        os.remove(self.get_hook_file(hook_type, hook_name, True)) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				94
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				95
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				96
			 | 
			
			
				
				+def main(): 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				97
			 | 
			
			
				
				+    parser = argparse.ArgumentParser(description='Manage apache websites and SSL certificates') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				98
			 | 
			
			
				
				+    parser.add_argument('--config', dest='config', default='/etc/sitegen/sitegen.json', help='Configuration file path') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				99
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				100
			 | 
			
			
				
				+    parser.add_argument('--cert-request', metavar='cert_request', const='', nargs='?', 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				101
			 | 
			
			
				
				+                        help='Request/renew a certificate. Request/renew all certificates if no domain is specified') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				102
			 | 
			
			
				
				+    parser.add_argument('--cert-check', metavar='cert_check', const='', nargs='?', 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				103
			 | 
			
			
				
				+                        help='Check if certificate needs to be renewed. Check all if no domain is specified') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				104
			 | 
			
			
				
				+    parser.add_argument('--cert-renew', metavar='cert_renew', const='', nargs='?', 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				105
			 | 
			
			
				
				+                        help='Renew certificate if it needs to be. Renew all that needs to be if no domain is specified') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				106
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				107
			 | 
			
			
				
				+    parser.add_argument('--site-create', help='Create a site configuration', metavar='site_create') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				108
			 | 
			
			
				
				+    parser.add_argument('--site-remove', help='Remove a site configuration', metavar='site_remove') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				109
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				110
			 | 
			
			
				
				+    parser.add_argument('--hook-site-enable', help='Enable a site hook', dest='site_hook_enable', metavar='hook') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				111
			 | 
			
			
				
				+    parser.add_argument('--hook-site-disable', help='Disable a site hook', dest='site_hook_disable', metavar='hook') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				112
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				113
			 | 
			
			
				
				+    parser.add_argument('--hook-cert-enable', help='Enable a certificate hook', dest='hook_cert_enable', metavar='hook') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				114
			 | 
			
			
				
				+    parser.add_argument('--hook-cert-disable', help='Disable a certificate hook', dest='hook_cert_disable', metavar='hook') 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				115
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				116
			 | 
			
			
				
				+    args = parser.parse_args() 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				117
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				118
			 | 
			
			
				
				+    with open(args.config, "r") as f: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				119
			 | 
			
			
				
				+        config = json.load(f) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				120
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				121
			 | 
			
			
				
				+    site_gen = SiteGen(config) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				122
			 | 
			
			
				
				+    print(site_gen.get_hook_files("site", True)) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				123
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				124
			 | 
			
			
				
				+    try: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				125
			 | 
			
			
				
				+        if args.cert_request is not None: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				126
			 | 
			
			
				
				+            site_gen.cert_request(args.cert_request) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				127
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				128
			 | 
			
			
				
				+        elif args.cert_check is not None: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				129
			 | 
			
			
				
				+            site_gen.cert_check(args.cert_check) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				130
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				131
			 | 
			
			
				
				+        elif args.cert_renew is not None: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				132
			 | 
			
			
				
				+            site_gen.cert_renew(args.cert_renew) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				133
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				134
			 | 
			
			
				
				+        elif args.site_create is not None: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				135
			 | 
			
			
				
				+            site_gen.site_create(args.site_create) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				136
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				137
			 | 
			
			
				
				+        elif args.site_remove is not None: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				138
			 | 
			
			
				
				+            site_gen.site_remove(args.site_remove) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				139
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				140
			 | 
			
			
				
				+        elif args.site_hook_enable is not None: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				141
			 | 
			
			
				
				+            site_gen.hook_enable("site", args.site_hook_enable) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				142
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				143
			 | 
			
			
				
				+        elif args.site_hook_disable is not None: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				144
			 | 
			
			
				
				+            site_gen.hook_disable("site", args.site_hook_disable) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				145
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				146
			 | 
			
			
				
				+        elif args.hook_cert_enable is not None: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				147
			 | 
			
			
				
				+            site_gen.hook_enable("cert", args.hook_cert_enable) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				148
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				149
			 | 
			
			
				
				+        elif args.hook_cert_disable is not None: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				150
			 | 
			
			
				
				+            site_gen.hook_disable("cert", args.hook_cert_disable) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				151
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				152
			 | 
			
			
				
				+        else: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				153
			 | 
			
			
				
				+            parser.print_help() 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				154
			 | 
			
			
				
				+    except SiteGenException as e: 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				155
			 | 
			
			
				
				+        print(e.error) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				156
			 | 
			
			
				
				+        exit(e.code) 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				157
			 | 
			
			
				
				+ 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				158
			 | 
			
			
				
				+if __name__ == "__main__": 
			 | 
		
		
	
		
			
			| 
				
			 | 
			
				159
			 | 
			
			
				
				+    main() 
			 |