moulinette/lib/yunohost/domain.py

298 lines
12 KiB
Python
Raw Normal View History

2012-10-29 17:38:05 +01:00
# -*- coding: utf-8 -*-
2013-07-06 09:42:26 +02:00
""" License
Copyright (C) 2013 YunoHost
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, see http://www.gnu.org/licenses
"""
""" yunohost_domain.py
2013-07-06 10:17:16 +02:00
Manage domains
2013-07-06 09:42:26 +02:00
"""
2012-10-29 17:38:05 +01:00
import os
import sys
2012-10-31 18:57:48 +01:00
import datetime
2012-11-06 09:45:28 +01:00
import re
2013-02-28 17:58:18 +01:00
import shutil
2013-10-17 13:37:27 +02:00
import json
import yaml
import requests
2012-11-06 09:45:28 +01:00
from urllib import urlopen
2014-02-05 02:01:03 +01:00
from dyndns import dyndns_subscribe
from moulinette.helpers import YunoHostError, YunoHostLDAP, win_msg, colorize, validate, get_required_args
2012-10-29 17:38:05 +01:00
def domain_list(filter=None, limit=None, offset=None):
2012-10-29 17:38:05 +01:00
"""
2013-07-06 10:17:16 +02:00
List domains
2012-10-29 17:38:05 +01:00
Keyword argument:
2013-07-06 10:17:16 +02:00
filter -- LDAP filter used to search
2013-10-31 11:21:59 +01:00
offset -- Starting number for domain fetching
limit -- Maximum number of domain fetched
2012-10-29 17:38:05 +01:00
"""
2012-11-09 18:04:15 +01:00
with YunoHostLDAP() as yldap:
2013-02-27 20:06:17 +01:00
result_list = []
if offset: offset = int(offset)
2012-11-09 18:04:15 +01:00
else: offset = 0
if limit: limit = int(limit)
2012-11-09 18:04:15 +01:00
else: limit = 1000
if not filter: filter = 'virtualdomain=*'
2012-11-09 18:04:15 +01:00
result = yldap.search('ou=domains,dc=yunohost,dc=org', filter, attrs=['virtualdomain'])
2013-02-27 20:06:17 +01:00
2012-11-09 18:04:15 +01:00
if result and len(result) > (0 + offset) and limit > 0:
i = 0 + offset
for domain in result[i:]:
2013-02-12 13:52:11 +01:00
if i <= limit:
2013-02-27 20:06:17 +01:00
result_list.append(domain['virtualdomain'][0])
2012-11-09 18:04:15 +01:00
i += 1
else:
raise YunoHostError(167, _("No domain found"))
2013-02-27 20:06:17 +01:00
return { 'Domains': result_list }
2012-11-09 18:04:15 +01:00
def domain_add(domains, main=False, dyndns=False):
2012-10-29 17:38:05 +01:00
"""
2013-07-06 10:17:16 +02:00
Create a custom domain
2012-10-29 17:38:05 +01:00
Keyword argument:
2013-07-06 10:17:16 +02:00
domains -- Domain name to add
main -- Is the main domain
dyndns -- Subscribe to DynDNS
2012-10-29 17:38:05 +01:00
"""
2012-11-09 18:04:15 +01:00
with YunoHostLDAP() as yldap:
attr_dict = { 'objectClass' : ['mailDomain', 'top'] }
ip = str(urlopen('http://ip.yunohost.org').read())
now = datetime.datetime.now()
2013-02-27 20:06:17 +01:00
timestamp = str(now.year) + str(now.month) + str(now.day)
2012-11-09 18:04:15 +01:00
result = []
if not isinstance(domains, list):
domains = [ domains ]
2013-02-27 20:06:17 +01:00
for domain in domains:
2013-06-09 22:16:27 +02:00
try:
if domain in domain_list()['Domains']: continue
except YunoHostError: pass
# DynDNS domain
if dyndns and len(domain.split('.')) >= 3:
r = requests.get('http://dyndns.yunohost.org/domains')
dyndomains = json.loads(r.text)
dyndomain = '.'.join(domain.split('.')[1:])
if dyndomain in dyndomains:
if os.path.exists('/etc/cron.d/yunohost-dyndns'):
raise YunoHostError(22, _("You already have a DynDNS domain"))
else:
dyndns_subscribe(domain=domain)
# Commands
2013-06-08 20:26:23 +02:00
ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA'
ssl_domain_path = '/etc/yunohost/certs/'+ domain
with open(ssl_dir +'/serial', 'r') as f:
serial = f.readline().rstrip()
try: os.listdir(ssl_domain_path)
except OSError: os.makedirs(ssl_domain_path)
command_list = [
'cp '+ ssl_dir +'/openssl.cnf '+ ssl_domain_path,
'sed -i "s/yunohost.org/' + domain + '/g" '+ ssl_domain_path +'/openssl.cnf',
'openssl req -new -config '+ ssl_domain_path +'/openssl.cnf -days 3650 -out '+ ssl_dir +'/certs/yunohost_csr.pem -keyout '+ ssl_dir +'/certs/yunohost_key.pem -nodes -batch',
'openssl ca -config '+ ssl_domain_path +'/openssl.cnf -days 3650 -in '+ ssl_dir +'/certs/yunohost_csr.pem -out '+ ssl_dir +'/certs/yunohost_crt.pem -batch',
'ln -s /etc/ssl/certs/ca-yunohost_crt.pem '+ ssl_domain_path +'/ca.pem',
'cp '+ ssl_dir +'/certs/yunohost_key.pem '+ ssl_domain_path +'/key.pem',
'cp '+ ssl_dir +'/newcerts/'+ serial +'.pem '+ ssl_domain_path +'/crt.pem',
2013-11-29 12:23:56 +01:00
'chmod 755 '+ ssl_domain_path,
2013-10-17 12:22:36 +02:00
'chmod 640 '+ ssl_domain_path +'/key.pem',
2013-11-29 12:23:56 +01:00
'chmod 640 '+ ssl_domain_path +'/crt.pem',
2013-11-29 12:27:23 +01:00
'chmod 600 '+ ssl_domain_path +'/openssl.cnf',
2013-11-29 12:23:56 +01:00
'chown root:metronome '+ ssl_domain_path +'/key.pem',
'chown root:metronome '+ ssl_domain_path +'/crt.pem'
2013-06-08 20:26:23 +02:00
]
for command in command_list:
if os.system(command) != 0:
raise YunoHostError(17, _("An error occurred during certificate generation"))
try:
yldap.validate_uniqueness({ 'virtualdomain' : domain })
except YunoHostError:
2013-10-17 11:45:02 +02:00
raise YunoHostError(17, _("Domain already created"))
2012-11-09 18:04:15 +01:00
attr_dict['virtualdomain'] = domain
try:
with open('/var/lib/bind/'+ domain +'.zone') as f: pass
except IOError as e:
zone_lines = [
'$TTL 38400',
domain +'. IN SOA ns.'+ domain +'. root.'+ domain +'. '+ timestamp +' 10800 3600 604800 38400',
domain +'. IN NS ns.'+ domain +'.',
domain +'. IN A '+ ip,
domain +'. IN MX 5 '+ domain +'.',
domain +'. IN TXT "v=spf1 mx a -all"',
2012-11-09 18:04:15 +01:00
'ns.'+ domain +'. IN A '+ ip,
2013-06-09 15:50:45 +02:00
'_xmpp-client._tcp.'+ domain +'. IN SRV 0 5 5222 '+ domain +'.',
'_xmpp-server._tcp.'+ domain +'. IN SRV 0 5 5269 '+ domain +'.',
'_jabber._tcp.'+ domain +'. IN SRV 0 5 5269 '+ domain +'.',
2012-11-09 18:04:15 +01:00
]
2013-06-10 13:05:31 +02:00
if main:
zone_lines.extend([
'pubsub.'+ domain +'. IN A '+ ip,
'muc.'+ domain +'. IN A '+ ip,
'vjud.'+ domain +'. IN A '+ ip
])
2012-11-09 18:04:15 +01:00
with open('/var/lib/bind/' + domain + '.zone', 'w') as zone:
for line in zone_lines:
zone.write(line + '\n')
2013-12-17 19:50:49 +01:00
os.system('chown bind /var/lib/bind/' + domain + '.zone')
2012-11-09 18:04:15 +01:00
else:
raise YunoHostError(17, _("Zone file already exists for ") + domain)
conf_lines = [
'zone "'+ domain +'" {',
' type master;',
' file "/var/lib/bind/'+ domain +'.zone";',
' allow-transfer {',
' 127.0.0.1;',
' localnets;',
' };',
'};'
2012-10-31 18:57:48 +01:00
]
2012-11-09 18:04:15 +01:00
with open('/etc/bind/named.conf.local', 'a') as conf:
for line in conf_lines:
2013-06-08 12:17:25 +02:00
conf.write(line + '\n')
os.system('service bind9 reload')
2013-06-09 15:12:21 +02:00
# XMPP
try:
with open('/etc/metronome/conf.d/'+ domain +'.cfg.lua') as f: pass
except IOError as e:
conf_lines = [
'VirtualHost "'+ domain +'"',
2013-10-17 12:22:36 +02:00
' ssl = {',
' key = "'+ ssl_domain_path +'/key.pem";',
' certificate = "'+ ssl_domain_path +'/crt.pem";',
' }',
2013-06-09 15:12:21 +02:00
' authentication = "ldap2"',
2013-06-22 12:36:11 +02:00
' ldap = {',
' hostname = "localhost",',
' user = {',
' basedn = "ou=users,dc=yunohost,dc=org",',
' filter = "(&(objectClass=posixAccount)(mail=*@'+ domain +'))",',
' usernamefield = "mail",',
' namefield = "cn",',
' },',
' }',
2013-06-09 15:12:21 +02:00
]
with open('/etc/metronome/conf.d/' + domain + '.cfg.lua', 'w') as conf:
for line in conf_lines:
conf.write(line + '\n')
os.system('mkdir -p /var/lib/metronome/'+ domain.replace('.', '%2e') +'/pep')
os.system('chown -R metronome: /var/lib/metronome/')
2013-06-14 09:42:35 +02:00
os.system('chown -R metronome: /etc/metronome/conf.d/')
2013-06-22 13:24:47 +02:00
os.system('service metronome restart')
2013-06-09 15:12:21 +02:00
2013-10-18 18:33:10 +02:00
# Nginx
os.system('cp /usr/share/yunohost/yunohost-config/nginx/template.conf /etc/nginx/conf.d/'+ domain +'.conf')
os.system('mkdir /etc/nginx/conf.d/'+ domain +'.d/')
os.system('sed -i s/yunohost.org/'+ domain +'/g /etc/nginx/conf.d/'+ domain +'.conf')
os.system('service nginx reload')
2012-11-09 18:04:15 +01:00
if yldap.add('virtualdomain=' + domain + ',ou=domains', attr_dict):
result.append(domain)
continue
else:
raise YunoHostError(169, _("An error occured during domain creation"))
2012-10-29 17:38:05 +01:00
2013-10-28 11:51:44 +01:00
os.system('yunohost app ssowatconf > /dev/null 2>&1')
2012-11-09 18:04:15 +01:00
win_msg(_("Domain(s) successfully created"))
2012-10-29 17:38:05 +01:00
2013-02-27 20:06:17 +01:00
return { 'Domains' : result }
2012-10-29 17:38:05 +01:00
def domain_remove(domains):
2012-10-29 17:47:37 +01:00
"""
2013-07-06 10:17:16 +02:00
Delete domains
2012-10-29 17:47:37 +01:00
Keyword argument:
2013-07-06 10:17:16 +02:00
domains -- Domain(s) to delete
2012-10-29 17:47:37 +01:00
"""
2012-11-09 18:04:15 +01:00
with YunoHostLDAP() as yldap:
result = []
if not isinstance(domains, list):
domains = [ domains ]
2012-11-09 18:04:15 +01:00
for domain in domains:
# Check if apps are installed on the domain
for app in os.listdir('/etc/yunohost/apps/'):
with open('/etc/yunohost/apps/' + app +'/settings.yml') as f:
if yaml.load(f)['domain'] == domain:
raise YunoHostError(1, _("One or more apps are installed on this domain, please uninstall them before proceed to domain removal"))
2012-11-09 18:04:15 +01:00
if yldap.remove('virtualdomain=' + domain + ',ou=domains'):
try:
2013-06-08 19:52:39 +02:00
shutil.rmtree('/etc/yunohost/certs/'+ domain)
2012-11-09 18:04:15 +01:00
os.remove('/var/lib/bind/'+ domain +'.zone')
2013-06-09 15:15:29 +02:00
shutil.rmtree('/var/lib/metronome/'+ domain.replace('.', '%2e'))
os.remove('/etc/metronome/conf.d/'+ domain +'.cfg.lua')
shutil.rmtree('/etc/nginx/conf.d/'+ domain +'.d')
os.remove('/etc/nginx/conf.d/'+ domain +'.conf')
2012-11-09 18:04:15 +01:00
except:
pass
with open('/etc/bind/named.conf.local', 'r') as conf:
conf_lines = conf.readlines()
with open('/etc/bind/named.conf.local', 'w') as conf:
in_block = False
for line in conf_lines:
if re.search(r'^zone "'+ domain, line):
in_block = True
if in_block:
if re.search(r'^};$', line):
in_block = False
else:
conf.write(line)
result.append(domain)
continue
else:
raise YunoHostError(169, _("An error occured during domain deletion"))
2013-10-28 11:51:44 +01:00
os.system('yunohost app ssowatconf > /dev/null 2>&1')
os.system('service nginx reload')
os.system('service bind9 reload')
os.system('service metronome restart')
2013-10-17 13:37:27 +02:00
2012-11-09 18:04:15 +01:00
win_msg(_("Domain(s) successfully deleted"))
return { 'Domains' : result }
2013-10-17 13:37:27 +02:00