def request_vhost(locker, hostname, path, user=None, desc=''): """Request hostname as a vhost for the given locker and path. Throws a UserError if the request is invalid, otherwise returns a human-readable status message and sends a zephyr.""" locker = locker.encode('utf-8') hostname, reqtype = validate_hostname(hostname, locker) path = path.encode('utf-8') path = validate_path(path) message = "The hostname '%s' is now configured." % hostname if user is None: user = current_user() if reqtype == 'moira': # actually_create_vhost does this check for other reqtypes check_if_already_exists(hostname, locker) t = queue.Ticket.create(locker, hostname, path, requestor=user, purpose=desc) short = hostname[:-len('.mit.edu')] mail.create_ticket(subject="scripts-vhosts CNAME request: %s" % short, body="""Heyas, %(user)s requested %(hostname)s for locker '%(locker)s' path %(path)s. Go to %(url)s to approve it. Purpose: %(desc)s Love, ~Scripts Pony """ % dict(short=short, user=user, locker=locker, hostname=hostname, desc=desc, path=path, url=tg.request.host_url + tg.url('/ticket/%s' % t.id)), id=t.id, requestor=user) message = "We will request the hostname %s; mit.edu hostnames generally take 2-3 business days to become active." % hostname else: # Actually create the vhost actually_create_vhost(locker, hostname, path) if is_sudoing(): sudobit = '+scripts-pony-acl' forbit = ' (for %s)' % user else: sudobit = '' forbit = '' logmessage = "%s%s requested %s for locker '%s' path '%s'%s (Purpose: %s)" % ( current_user(), sudobit, hostname, locker, path, forbit, desc) log.info(logmessage) return message
def validate_hostname(hostname, locker): hostname = hostname.lower().encode("utf-8") if not HOSTNAME_PATTERN.search(hostname) or hostname.endswith(".invalid"): if "." not in hostname: raise UserError( "'%s' is not an absolute hostname. Do you mean '%s.mit.edu'?" % (hostname, hostname) ) else: raise UserError("'%s' is not a valid hostname." % hostname) if hostname.endswith(".scripts.mit.edu"): reqtype = "subscripts" if not hostname.endswith("." + locker + ".scripts.mit.edu"): raise UserError( "'%s' is not a valid hostname for the '%s' locker." % (hostname, locker) ) elif hostname.endswith(".mit.edu"): reqtype = "moira" if hostname.count(".") != 2: raise UserError("'%s' has too many dots for a mit.edu hostname." % hostname) if not hostname[0].isalpha(): raise UserError(".mit.edu hostnames must start with a letter.") if hostname[-len(".mit.edu") - 1] == "-": raise UserError(".mit.edu hostnames cannot end with a dash.") if "_" in hostname: raise UserError(".mit.edu hostnames cannot contain an underscore.") exists = False try: dns.resolver.query(hostname + ".", 'A') exists = True except dns.resolver.NXDOMAIN: pass except dns.exception.Timeout: raise except dns.resolver.NoAnswer: # A record doesn't exist, but other records do exists = True if exists: if hosts.points_at_scripts(hostname) and is_sudoing(): # It was manually transfered to scripts; if it's not an # existing vhost, we good. reqtype = "manual" else: raise UserError( "'%s' already exists. Please choose another name or contact [email protected] if you wish to transfer the hostname to scripts." % hostname ) else: reqtype = "external" if not hosts.points_at_scripts(hostname): # Check if our magic file is there. check_file = generate_hostname_check_file(hostname, locker) try: # If this is a wildcard, pick some random domain to test. # We expect that *all* subdomains will match the same host if you're # using a wildcard. test_hostname = hostname if test_hostname[0:2] == "*.": test_hostname = generate_random_hostname() + test_hostname[1:] connection = httplib.HTTPConnection( test_hostname, timeout=5 ) # Shortish timeout - 5 seconds connection.request("HEAD", "/%s" % check_file) status = connection.getresponse() connection.close() if status.status != httplib.OK: raise UserError( auth.html( "'%s' does not point at scripts-vhosts. If you want to continue anyway, please create a file called '%s' in the root directory of the site. See <a href='https://scripts.mit.edu/faq/132/can-i-add-a-vhost-before-i-point-my-domain-at-scripts' target='_blank'>the FAQ</a> for more information." % (cgi.escape(hostname), cgi.escape(check_file)) ) ) except socket.gaierror: raise UserError("'%s' does not exist." % hostname) except (httplib.HTTPException, socket.error): raise UserError( auth.html( "'%s' does not point at scripts-vhosts, and appears to have no running webserver. Please see <a href='https://scripts.mit.edu/faq/132/can-i-add-a-vhost-before-i-point-my-domain-at-scripts' target='_blank'>the FAQ</a> for more information." % cgi.escape(hostname) ) ) return hostname, reqtype
def request_vhost(locker, hostname, path, user=None, desc=""): """Request hostname as a vhost for the given locker and path. Throws a UserError if the request is invalid, otherwise returns a human-readable status message and sends a zephyr.""" locker = locker.encode("utf-8") hostname, reqtype = validate_hostname(hostname, locker) path = path.encode("utf-8") path = validate_path(path) message = "The hostname '%s' is now configured." % hostname if user is None: user = current_user() if reqtype == "moira": # actually_create_vhost does this check for other reqtypes check_if_already_exists(hostname, locker) t = queue.Ticket.create(locker, hostname, path, requestor=user, purpose=desc) short = hostname[: -len(".mit.edu")] out = rt.call( "ticket/new", Queue="Scripts", Subject="scripts-vhosts CNAME request: %s" % short, Text="""Heyas, %(user)s requested %(hostname)s for locker '%(locker)s' path %(path)s. Go to %(url)s to approve it. Purpose: %(desc)s Love, ~Scripts Pony """ % dict( short=short, user=user, locker=locker, hostname=hostname, desc=desc, path=path, url=tg.request.host_url + tg.url("/ticket/%s" % t.id), ), Requestor=user, AdminCc=rt.ponyaddr(), ) for line in out.splitlines(): if line.startswith("# Ticket ") and line.endswith(" created."): t.rtid = int(line[len("# Ticket ") : -len(" created.")]) break else: raise RuntimeError("Could not open an RT ticket") message = ( "We will request the hostname %s; mit.edu hostnames generally take 2-3 business days to become active." % hostname ) else: # Actually create the vhost actually_create_vhost(locker, hostname, path) if is_sudoing(): sudobit = "+scripts-pony-acl" forbit = " (for %s)" % user else: sudobit = "" forbit = "" logmessage = "%s%s requested %s for locker '%s' path '%s'%s (Purpose: %s)" % ( current_user(), sudobit, hostname, locker, path, forbit, desc, ) log.info(logmessage) return message
def validate_hostname(hostname, locker): hostname = hostname.lower().encode('utf-8') if not HOSTNAME_PATTERN.search(hostname): if '.' not in hostname: raise UserError( "'%s' is not an absolute hostname. Do you mean '%s.mit.edu'?" % (hostname, hostname)) else: raise UserError("'%s' is not a valid hostname." % hostname) if hostname.endswith(".scripts.mit.edu"): reqtype = 'subscripts' if not hostname.endswith("." + locker + ".scripts.mit.edu"): raise UserError( "'%s' is not a valid hostname for the '%s' locker." % (hostname, locker)) elif hostname.endswith(".mit.edu"): reqtype = 'moira' if hostname.count('.') != 2: raise UserError("'%s' has too many dots for a mit.edu hostname." % hostname) if not hostname[0].isalpha(): raise UserError(".mit.edu hostnames must start with a letter.") if hostname[-len(".mit.edu") - 1] == '-': raise UserError(".mit.edu hostnames cannot end with a dash.") if '_' in hostname: raise UserError(".mit.edu hostnames cannot contain an underscore.") try: dns.resolver.query(hostname + '.', 0) except dns.resolver.NXDOMAIN: pass except dns.exception.Timeout: raise except dns.exception.DNSException: if hosts.points_at_scripts(hostname) and is_sudoing(): # It was manually transfered to scripts; if it's not an # existing vhost, we good. reqtype = 'manual' else: raise UserError( "'%s' already exists. Please choose another name or contact [email protected] if you wish to transfer the hostname to scripts." % hostname) else: raise RuntimeError("DNS query should never return successfully!") stella_cmd = subprocess.Popen( ["/usr/bin/stella", "-u", "-noauth", hostname], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = stella_cmd.communicate() if stella_cmd.returncode != 1: # Then its reserved, deleted, etc. status = "Unknown" for line in out.split("\n"): if "Status:" in line: status = line.split(" ")[-2] raise UserError( "'%s' is not available; it currently has status %s. Please choose another name or contact [email protected] if you wish to transfer the hostname to scripts." % (hostname, status)) else: reqtype = 'external' if not hosts.points_at_scripts(hostname): # Check if our magic file is there. check_file = generate_hostname_check_file(hostname, locker) try: # If this is a wildcard, pick some random domain to test. # We expect that *all* subdomains will match the same host if you're # using a wildcard. test_hostname = hostname if test_hostname[0:2] == '*.': test_hostname = generate_random_hostname( ) + test_hostname[1:] connection = httplib.HTTPConnection( test_hostname, timeout=5) # Shortish timeout - 5 seconds connection.request("HEAD", "/%s" % check_file) status = connection.getresponse() connection.close() if status.status != httplib.OK: raise UserError( auth.html( "'%s' does not point at scripts-vhosts. If you want to continue anyway, please create a file called '%s' in the root directory of the site. See <a href='http://scripts.mit.edu/faq/132/can-i-add-a-vhost-before-i-point-my-domain-at-scripts' target='_blank'>the FAQ</a> for more information." % (hostname, check_file))) except socket.gaierror: raise UserError("'%s' does not exist." % hostname) except (httplib.HTTPException, socket.error): raise UserError( auth.html( "'%s' does not point at scripts-vhosts, and appears to have no running webserver. Please see <a href='http://scripts.mit.edu/faq/132/can-i-add-a-vhost-before-i-point-my-domain-at-scripts' target='_blank'>the FAQ</a> for more information." % hostname)) return hostname, reqtype