#!/usr/bin/python3 -u import sys import argparse import os #from pastafariutils.unix import add_user, del_user, change_password from pathlib import Path from subprocess import call, DEVNULL import json import time import shutil import pwd import distro import subprocess import pwd import grp php_version={'php74': 'php7.4', 'php80': 'php8.0', 'php81': 'php8.1', 'php82': 'php8.2'} def manage(): parser=argparse.ArgumentParser(prog='manage_apache.py', description='A tool for admin an apache server') parser.add_argument('--operation', help='The operation', required=True) parser.add_argument('--domain', help='The domain for this operation', required=True) parser.add_argument('--aliases', help='The Domain aliases for the domain') parser.add_argument('--port', help='The http port where petitions are listened') parser.add_argument('--ssl_port', help='The https port where petitions are listened') parser.add_argument('--ip', help='The IP where http petitions are listened') parser.add_argument('--email', help='If add an domain, you can use an email for apache errors') parser.add_argument('--user', help='You need an user for the virtualhost') parser.add_argument('--root_dir', help='If add an domain, you need define where the virtualhost is installed') parser.add_argument("--indexes", action='store_true') parser.add_argument("--allow_override", action='store_true') parser.add_argument("--ssl", help='SSL Type: 0, no SSL') parser.add_argument("--redirect_ssl", action='store_true') parser.add_argument("--debug", action='store_true') parser.add_argument('--type_cgi', help='The CGI type for this virtualhost') parser.add_argument('--password', help='The password of the unix user') #parser.add_argument('--ftp_user', help='The CGI type for this virtualhost', required=True) args=parser.parse_args() apache_cmd='apache2' apachectl='apache2ctl' apache_group='www-data' linux_distro=distro.id() if linux_distro!='debian' and linux_distro!='ubuntu': apache_cmd='httpd' apachectl='apachectl' if linux_distro=='rocky' or linux_distro=='fedora' or linux_distro=='almalinux': apache_group='apache' if linux_distro=='arch': apache_group='http' port=80 ssl_port=443 ip='*' yes_ssl=False debug=False if args.debug: debug=True json_return={'error':0, 'status': 0, 'progress': 0, 'no_progress':0, 'message': ''} if args.operation=='add' or args.operation=='edit': if not args.email: parser.error('You need --email option if you add a new virtual host.') if not args.root_dir: parser.error('You need --root_dir option if you add a new virtual host.') if not args.user: parser.error('You need --user option if you add a new virtual host.') if args.port: port=args.port if args.ip: ip=args.ip if ip.find(':')!=-1: ip='['+ip+']' real_root_dir=args.root_dir+'/htdocs' #aliases='ServerAlias www.'+args.domain aliases='' if args.aliases: aliases='ServerAlias '+args.aliases.replace(',', ' ') indexes='' if args.indexes: indexes='Indexes' allow_override='' if args.allow_override: allow_override='AllowOverride all' type_ssl=0 if args.ssl: try: type_ssl=int(args.ssl) except: json_return['error']=1 json_return['status']=1 json_return['progress']=100 json_return['message']='Error: bad SSL configuration type' print(json.dumps(json_return)) exit(1) #print( os.path.basename(__file__)+'/files/vhost.tpl') vhost_file=os.path.dirname(os.path.abspath(__file__))+'/files/vhost.tpl' with open(vhost_file) as f_vhost: vhost=f_vhost.read() vhost_ssl=vhost vhost=vhost.replace('$port', args.port) vhost=vhost.replace('$ip', args.ip) vhost=vhost.replace('$email', args.email) vhost=vhost.replace('$domain', args.domain) #ServerAlias www.$domain vhost=vhost.replace('$ServerAlias', aliases) vhost=vhost.replace('$rootDir', real_root_dir) vhost=vhost.replace('$Indexes', indexes) vhost=vhost.replace('$AllowOverride', allow_override) vhost=vhost.replace('$apache_cmd', apache_cmd) if type_ssl>0: if type_ssl==1: json_return['error']=0 json_return['status']=0 json_return['progress']=0 json_return['message']='Preparing SSL virtualhost' #print(json.dumps(json_return)) #Make letsencrypt """ MDContactEmail antonio.delarosa@salirdelhoyo.com MDCertificateAgreement accepted MDomain foro.desiertoslejanos.org SSLEngine on """ vhost_ssl=vhost_ssl.replace('$port', args.ssl_port) vhost_ssl=vhost_ssl.replace('$ip', args.ip) vhost_ssl=vhost_ssl.replace('$email', args.email) vhost_ssl=vhost_ssl.replace('$domain', args.domain) #ServerAlias www.$domain vhost_ssl=vhost_ssl.replace('$ServerAlias', aliases) vhost_ssl=vhost_ssl.replace('$rootDir', real_root_dir) vhost_ssl=vhost_ssl.replace('$Indexes', indexes) vhost_ssl=vhost_ssl.replace('$AllowOverride', allow_override) vhost_ssl=vhost_ssl.replace('$apache_cmd', apache_cmd) vhost_ssl_md=[] vhost_ssl_md.append("MDContactEmail {}".format(args.email)) vhost_ssl_md.append("MDCertificateAgreement accepted") vhost_ssl_md.append("MDomain {}".format(args.domain)) if debug: #vhost_ssl_md.append("LE Test Setup") vhost_ssl_md.append("MDCertificateAuthority https://acme-staging-v02.api.letsencrypt.org/directory") vhost_ssl="\n".join(vhost_ssl_md)+"\n"+vhost_ssl vhost_ssl=vhost_ssl.replace('# SSL Options', "SSLEngine on") if args.redirect_ssl: print("Adding redirect http to https...") ssl_options_http=("RewriteEngine On", "RewriteCond %{HTTPS} !=on", "RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]") vhost=vhost.replace('# SSL Options', "\n".join(ssl_options_http)) vhost=vhost_ssl+"\n\n"+vhost json_return={'error':0, 'status': 0, 'progress': 100, 'no_progress':0, 'message': 'Creating SSL Cert with Letsencrypt using mod_md...'} print(json.dumps(json_return)) """ ssl_debug='' if debug: ssl_debug='--test-cert' #certbot certonly --test-cert --webroot -w /var/www/sites/prueba.cuchulu.com/htdocs/ -d prueba.cuchulu.com final_aliases='' if args.aliases: #arr_aliases=[alias.strip() for args.aliases.split(',')] final_aliases=" ".join(['-d '+alias.strip() for alias in args.aliases.split(',')]) expand='' if os.path.isfile('/etc/letsencrypt/live/{}/fullchain.pem'.format(args.domain)): expand='--expand' certbot_cmd='certbot certonly --noninteractive --agree-tos -m {} {} --webroot -w /home/{}/sites/{}/htdocs/ -d {} {} {}'.format(args.email, ssl_debug, args.user, args.domain, args.domain, final_aliases, expand) json_return={'error':0, 'status': 0, 'progress': 0, 'no_progress':0, 'message': 'Creating SSL Cert with Letsencrypt...'} print(json.dumps(json_return)) if call("sudo "+certbot_cmd, shell=True) > 0: #If error, not stop the virtualhost, simply not creater ssl. json_return['error']=0 json_return['status']=0 json_return['progress']=100 json_return['message']='Error: the ssl command is wrong '+certbot_cmd print(json.dumps(json_return)) else: json_return={'error':0, 'status': 0, 'progress': 100, 'no_progress':0, 'message': 'Created SSL Cert with Letsencrypt...'} print(json.dumps(json_return)) # /etc/letsencrypt/live/prueba.cuchulu.com/fullchain.pem # /etc/letsencrypt/live/prueba.cuchulu.com/privkey.pem vhost_ssl=vhost_ssl.replace('$port', args.ssl_port) vhost_ssl=vhost_ssl.replace('$ip', args.ip) vhost_ssl=vhost_ssl.replace('$email', args.email) vhost_ssl=vhost_ssl.replace('$domain', args.domain) #ServerAlias www.$domain vhost_ssl=vhost_ssl.replace('$ServerAlias', aliases) vhost_ssl=vhost_ssl.replace('$rootDir', real_root_dir) vhost_ssl=vhost_ssl.replace('$Indexes', indexes) vhost_ssl=vhost_ssl.replace('$AllowOverride', allow_override) vhost_ssl=vhost_ssl.replace('$apache_cmd', apache_cmd) ssl_cert='/etc/letsencrypt/live/{}/fullchain.pem'.format(args.domain) ssl_key='/etc/letsencrypt/live/{}/privkey.pem'.format(args.domain) ssl_options=("SSLEngine on", "SSLCertificateFile %s" % ssl_cert, "SSLCertificateKeyFile %s" % ssl_key) vhost_ssl=vhost_ssl.replace('# SSL Options', "\n".join(ssl_options)) if args.redirect_ssl: print("Adding redirect http to https...") ssl_options_http=("RewriteEngine On", "RewriteCond %{HTTPS} !=on", "RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]") vhost=vhost.replace('# SSL Options', "\n".join(ssl_options_http)) vhost+="\n\n"+vhost_ssl pass """ if type_ssl==2: vhost_ssl=vhost_ssl.replace('$port', args.ssl_port) vhost_ssl=vhost_ssl.replace('$ip', args.ip) vhost_ssl=vhost_ssl.replace('$email', args.email) vhost_ssl=vhost_ssl.replace('$domain', args.domain) #ServerAlias www.$domain vhost_ssl=vhost_ssl.replace('$ServerAlias', aliases) vhost_ssl=vhost_ssl.replace('$rootDir', real_root_dir) vhost_ssl=vhost_ssl.replace('$Indexes', indexes) vhost_ssl=vhost_ssl.replace('$AllowOverride', allow_override) vhost_ssl=vhost_ssl.replace('$apache_cmd', apache_cmd) #Create dir for ssl if call("sudo mkdir -p /etc/"+apache_cmd+"/ssl/", shell=True) > 0: print('Error, cannot create SSL Cert directory if not exists...') exit(1) ssl_cert='/etc/'+apache_cmd+'/ssl/'+args.domain+'-ssl.crt' ssl_key='/etc/'+apache_cmd+'/ssl/'+args.domain+'-ssl.key' ssl_orig_cert=os.path.dirname(os.path.abspath(__file__))+'/files/'+args.domain+'-ssl.crt' ssl_orig_key=os.path.dirname(os.path.abspath(__file__))+'/files/'+args.domain+'-ssl.key' if os.path.isfile(ssl_orig_cert): shutil.copy(ssl_orig_cert, ssl_cert) shutil.copy(ssl_orig_key, ssl_key) if not os.path.isfile(ssl_cert): print('Error, you have not uploaded crt files for ssl vhost...') exit(1) ssl_options=("SSLEngine on", "SSLCertificateFile %s" % ssl_cert, "SSLCertificateKeyFile %s" % ssl_key) vhost_ssl=vhost_ssl.replace('# SSL Options', "\n".join(ssl_options)) if args.redirect_ssl: print("Adding redirect http to https...") ssl_options_http=("RewriteEngine On", "RewriteCond %{HTTPS} !=on", "RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]") vhost=vhost.replace('# SSL Options', "\n".join(ssl_options_http)) vhost+="\n\n"+vhost_ssl pass if args.operation=='add': # Add site directory # Add user if args.user=='root': json_return['error']=1 json_return['status']=1 json_return['progress']=100 json_return['message']='Error: you cannot use user root for sites' print(json.dumps(json_return)) sys.exit(1) #ret_user=add_user(args.user, '', '', user_directory='', shell='/bin/bash') func_user="sudo useradd -m -s %s %s" % ('/bin/bash', args.user) ret_user=True if call(func_user, shell=True, stdout=DEVNULL) > 0: ret_user=False if not ret_user: print("User %s exists, used for the new site" % args.user) #Need add to group apache if not. Need for red hat and derivatives. g=grp.getgrnam(apache_group) #grp.struct_group(gr_name='apache', gr_passwd='x', gr_gid=48, gr_mem=['absurdo']) if args.user not in g.gr_mem: if call("sudo usermod -a -G %s %s" % (apache_group, args.user), shell=True) > 0: print('Error, cannot add group to user') exit(1) else: print('Added user to apache group') else: if call("sudo usermod -a -G %s %s" % (apache_group, args.user), shell=True) > 0: print('Error, cannot add group to user') exit(1) # Add ssh key for diverses uses print("Adding ssh key for user...") if call('sudo su - %s -s /bin/bash -c \'ssh-keygen -f /home/%s/.ssh/id_rsa.pub -N \\"\\"\'' % (args.user, args.user), shell=True) > 0: print('Error, cannot add ssh key for user...') exit(1) if call("sudo chmod +x %s" % '/home/'+args.user, shell=True) > 0: print('Error, cannot set permissions for folder %s...' % real_root_dir) exit(1) if call("sudo su - %s -s /bin/bash -c 'mkdir -p %s'" % (args.user, real_root_dir), shell=True) > 0: print('Error, cannot create Virtualhost folder %s...' % real_root_dir) exit(1) password_user='' if args.password: password_user=args.password ret_pass=change_password(args.user, password_user) if not ret_pass[0]: print('Error, cannot change password for %s' % args.user ) exit(1) if call("sudo getenforce", shell=True)==0: # If selinux enabled, set permissions to /home/hosting/sites for sites. #if call("sudo setsebool -P httpd_enable_homedirs true && sudo chcon -R -t httpd_sys_content_t /var/www/sites && sudo setsebool -P httpd_can_network_connect 1 && sudo semanage fcontext -a -t httpd_sys_rw_content_t \"/var/www/sites(/.*)?\"", shell=True) > 0: print('Setting selinux permissions for the http folder %s...' % real_root_dir) if call("sudo setsebool -P httpd_enable_homedirs true && sudo chcon -R -t httpd_sys_content_t %s && sudo setsebool -P httpd_can_network_connect 1 && chcon -R -t httpd_sys_rw_content_t %s" % (real_root_dir, real_root_dir), shell=True) > 0: print('Error, cannot set selinux permissions...') exit(1) # Save virtualhost virtualhost_base='/etc/'+apache_cmd+'/vhosts.d' virtualhost_path=virtualhost_base+'/%s.conf' % (args.domain) #virtualhost_enabled_path='/etc/'+apache_cmd+'/vhosts.d/%s.conf' % args.domain with open(virtualhost_path, 'w') as f: f.write(vhost) json_return['progress']=60 json_return['message']='Created or edited virtualhost' print(json.dumps(json_return)) #time.sleep(1) # Activate virtualhost #os.symlink(virtualhost_path, virtualhost_enabled_path) # Check apache configuration, if not delete user and virtualhost. if call("sudo "+apachectl+" configtest", shell=True) > 0: json_return['error']=1 json_return['status']=1 json_return['progress']=100 json_return['message']='Error: the config is wrong \n'+vhost # Delete user #os.unlink(virtualhost_enabled_path) os.remove(virtualhost_path) #err, txt=del_user(args.user) print(json.dumps(json_return)) exit(1) json_return['progress']=75 json_return['message']='Finished virtualhost configuration' print(json.dumps(json_return)) #time.sleep(1) # Restart config if call("sudo systemctl restart "+apache_cmd, shell=True) > 0: json_return['error']=1 json_return['status']=1 json_return['progress']=100 json_return['message']='Error: I cannot reload the httpd server' # Delete user #os.unlink(virtualhost_enabled_path) os.remove(virtualhost_path) #err, txt=del_user(args.user) print(json.dumps(json_return)) exit(1) json_return['progress']=85 json_return['message']='Apache2 restarted...' print(json.dumps(json_return)) #time.sleep(1) # Finish json_return['progress']=100 json_return['message']='Virtualhost done' json_return['status']=0 print(json.dumps(json_return)) #time.sleep(1) elif args.operation=='remove': # Change uid to user id of the if args.root_dir=='/': json_return['error']=1 json_return['status']=1 json_return['progress']=100 json_return['message']='Error: I cannot delete this place...' #if not args.user: # parser.error('You need --user option if you want to remove a virtual host.') virtualhost_path='/etc/'+apache_cmd+'/vhosts.d/%s.conf' % (args.domain) virtualhost_php_path='/etc/'+apache_cmd+'/vhosts.d/php/%s-php.conf' % (args.domain) #virtualhost_enabled_path='/etc/'+apache_cmd+'/vhosts.d/%s.conf' % args.domain # Delete first apache configuration try: os.remove(virtualhost_path) except: json_return['error']=1 json_return['status']=1 json_return['progress']=100 json_return['message']='Error: I cannot remove symbolic link of apache file config' print(json.dumps(json_return)) exit(1) if os.path.isfile(virtualhost_php_path): try: os.remove(virtualhost_php_path) except: json_return['error']=1 json_return['status']=1 json_return['progress']=100 json_return['message']='Error: I cannot remove symbolic link of apache file config' print(json.dumps(json_return)) exit(1) json_return['progress']=25 json_return['message']='Deleted virtualhost in Apache configuration' print(json.dumps(json_return)) #time.sleep(1) # Test apache config if call("sudo "+apachectl+" configtest", shell=True) > 0: json_return['error']=1 json_return['status']=1 json_return['progress']=100 json_return['message']='Error: the config is wrong' exit(1) json_return['progress']=50 json_return['message']='Apache configuration checked correctly...' print(json.dumps(json_return)) #time.sleep(1) # Restart apache if call("sudo systemctl restart "+apache_cmd, shell=True) > 0: json_return['error']=1 json_return['status']=1 json_return['progress']=100 json_return['message']='Error: I cannot reload the httpd server' print(json.dumps(json_return)) exit(1) json_return['progress']=75 json_return['message']='Apache restarted successfully...' print(json.dumps(json_return)) #time.sleep(1) # Delete root_dir if exists # Change effective uid pw=pwd.getpwnam(args.user) os.setuid(pw[2]) if args.root_dir: shutil.rmtree(args.root_dir) # Delete user of virtualhost # Done json_return['progress']=100 json_return['message']='VHost removed successfully. Done all.' json_return['status']=0 print(json.dumps(json_return)) #time.sleep(1) if __name__=='__main__': manage()