apache/scripts/manage_apache.py
2023-11-28 00:15:10 +01:00

727 lines
28 KiB
Python

#!/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
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':
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
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
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
# Get php version and install
"""
if args.type_cgi!=None:
if 'php' in args.type_cgi:
check_php_version(args.type_cgi, args.user)
#Add php support to virtualhost
# ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/path/to/your/documentroot/$1
# ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/path/to/socket.sock|fcgi://localhost/path/to/your/documentroot/
# ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/run/php/php-8.2-hosting.sock|fcgi://localhost/var/www/sites/coesinfo/prueba.cuchulu.com/htdocs/$1
number_version=php_version[args.type_cgi].replace('php', '')
# /run\/php\/php-"+number_version.replace('.', '\.')+"-{}.sock
php_line="# PHP Options\nProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/run/php/php-{}-{}.sock|fcgi://localhost{}/$1".format(number_version, args.user, real_root_dir)
#print(php_line)
vhost=vhost.replace('# PHP Options', php_line)
print('Adding php configuration to virtualhost...')
"""
#time.sleep(1)
# Add user
#err, txt=add_user(args.user)
"""
if err:
json_return['error']=1
json_return['status']=1
json_return['progress']=100
json_return['message']='Error: user exists'
print(json.dumps(json_return))
sys.exit(1)
"""
"""
json_return['progress']=30
json_return['message']='Created user for this site'
print(json.dumps(json_return))
time.sleep(1)
"""
if args.operation=='add':
# Add site directory
"""
yes_chown=False
if not os.path.isdir(args.root_dir):
yes_chown=True
p=Path(real_root_dir)
try:
p.mkdir(mode=0o755, parents=True, exist_ok=False)
#Change owner
#if yes_chown:
#shutil.chown('../'+args.root_dir, args.user, args.user)
if call(("sudo chown -R {}:{} "+args.root_dir).format(args.user, args.user), shell=True, stdout=DEVNULL) > 0:
json_return['error']=1
json_return['status']=1
json_return['progress']=100
json_return['message']='Error: cannot set the directory permissions'
else:
shutil.chown(real_root_dir, args.user, args.user)
except FileNotFoundError:
json_return['error']=1
json_return['status']=1
json_return['progress']=100
json_return['message']='Error: cannot create the site directory'
print(json.dumps(json_return))
sys.exit(1)
"""
# 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)
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'
"""
p=Path(virtualhost_base)
try:
p.mkdir(mode=0o755, parents=True, exist_ok=True)
except FileNotFoundError:
json_return['error']=1
json_return['status']=1
json_return['progress']=100
json_return['message']='Error: cannot create the apache vhost directory'
print(json.dumps(json_return))
sys.exit(1)
"""
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 '+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.unlink(virtualhost_enabled_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)
"""
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)
#os.seteuid(0)
# Delete user of virtualhost
#err, txt=del_user(args.user)
"""
if err:
json_return['error']=1
json_return['status']=1
json_return['progress']=100
json_return['message']='Error: I cannot remove the user'
print(json.dumps(json_return))
exit(1)
"""
# 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)
# Function check php version and install if necessary
def check_php_version(version, user):
linux_distro=distro.id()
if linux_distro=='debian' or linux_distro=='ubuntu':
php_packages={'php74': 'php7.4 php7.4-curl php7.4-dom php7.4-gd php7.4-xml php7.4-mbstring php7.4-zip php7.4-fileinfo php7.4-ctype php7.4-simplexml php7.4-xmlreader php7.4-xmlwriter php7.4-mysql php7.4-bz2 php7.4-intl php7.4-ldap php7.4-imap php7.4-bcmath php7.4-gmp php7.4-exif php7.4-opcache php7.4-redis php7.4-memcached php7.4-fpm', 'php80': 'php8.0 php8.0-curl php8.0-dom php8.0-gd php8.0-xml php8.0-mbstring php8.0-zip php8.0-fileinfo php8.0-ctype php8.0-simplexml php8.0-xmlreader php8.0-xmlwriter php8.0-mysql php8.0-bz2 php8.0-intl php8.0-ldap php8.0-imap php8.0-bcmath php8.0-gmp php8.0-exif php8.0-opcache php8.0-redis php8.0-memcached php8.0-fpm', 'php81': 'php8.1 php8.1-curl php8.1-dom php8.1-gd php8.1-xml php8.1-mbstring php8.1-zip php8.1 php8.1-fileinfo php8.1-ctype php8.1-simplexml php8.1-xmlreader php8.1-xmlwriter php8.1-mysql php8.1-bz2 php8.1-intl php8.1-ldap php8.1-imap php8.1-bcmath php8.1-gmp php8.1-exif php8.1-opcache php8.1-redis php8.1-memcached php8.1-fpm', 'php82': 'php8.2 php8.2-curl php8.2-dom php8.2-gd php8.2-xml php8.2-mbstring php8.2-zip php8.2 php8.2-fileinfo php8.2-ctype php8.2-simplexml php8.2-xmlreader php8.2-xmlwriter php8.2-mysql php8.2-bz2 php8.2-intl php8.2-ldap php8.2-imap php8.2-bcmath php8.2-gmp php8.2-exif php8.2-opcache php8.2-redis php8.2-memcached php8.2-fpm'}
if version in php_version:
#apt-get install php php-curl php-dom php-gd php-xml php-mbstring php-zip php-json php-fileinfo php-ctype php-simplexml php-xmlreader php-xmlwriter php-mysql php-bz2 php-intl php-ldap php-imap php-bcmath php-gmp php-exif php-opcache php-redis php-memcached
print('Checking install of php %s' % php_version[version])
number_version=php_version[version].replace('php', '')
try:
output=subprocess.check_output(['dpkg', '-l', 'php-'+number_version+'*'])
output=output.decode('utf-8')
except:
if call("sudo apt-get -y install %s && a2dismod mpm_prefork && a2enmod mpm_event proxy proxy_fcgi rewrite" % (php_packages[version]), shell=True) > 0:
print('Error, cannot install PHP...')
exit(1)
# Install php-fpm
# /etc/php/8.2/fpm/pool.d/www.conf
# sed -i 's/old-text/new-text/g' input.txt
#listen = /run/php/php8.2-fpm.sock
cmd="sudo cp /etc/php/"+number_version+"/fpm/pool.d/www.conf /etc/php/"+number_version+"/fpm/pool.d/{}.conf && sudo sed -i 's/^user = www-data/user = {}/g' /etc/php/"+number_version+"/fpm/pool.d/{}.conf && sudo sed -i 's/^group = www-data/group = {}/g' /etc/php/"+number_version+"/fpm/pool.d/{}.conf && sudo sed -i 's/\[www\]/[{}]/g' /etc/php/"+number_version+"/fpm/pool.d/{}.conf \
&& sudo sed -i 's/^listen = \/run\/php\/php"+number_version.replace('.', '\.')+"\-fpm\.sock/listen = \/run\/php\/php-"+number_version.replace('.', '\.')+"-{}.sock/g' /etc/php/"+number_version+"/fpm/pool.d/{}.conf"
cmd=cmd.format(user, user, user, user, user, user, user, user, user)
if call(cmd, shell=True) > 0:
print('Error, cannot update PHP-FPM...')
exit(1)
if call('systemctl restart php'+number_version+'-fpm.service', shell=True) > 0:
print('Error, cannot restart PHP-FPM...')
exit(1)
pass
# Reinstall php-fpm
if __name__=='__main__':
manage()