-
Star
(115)
You must be signed in to star a gist -
Fork
(22)
You must be signed in to fork a gist
-
-
Save dankrause/5585907 to your computer and use it in GitHub Desktop.
| import requests | |
| class HoverException(Exception): | |
| pass | |
| class HoverAPI(object): | |
| def __init__(self, username, password): | |
| params = {"username": username, "password": password} | |
| r = requests.post("https://www.hover.com/api/login", params=params) | |
| if not r.ok or "hoverauth" not in r.cookies: | |
| raise HoverException(r) | |
| self.cookies = {"hoverauth": r.cookies["hoverauth"]} | |
| def call(self, method, resource, data=None): | |
| url = "https://www.hover.com/api/{0}".format(resource) | |
| r = requests.request(method, url, data=data, cookies=self.cookies) | |
| if not r.ok: | |
| raise HoverException(r) | |
| if r.content: | |
| body = r.json() | |
| if "succeeded" not in body or body["succeeded"] is not True: | |
| raise HoverException(body) | |
| return body | |
| # connect to the API using your account | |
| client = HoverAPI("myusername", "mypassword") | |
| # get details of a domains without DNS records | |
| client.call("get", "domains") | |
| # get all domains and DNS records | |
| client.call("get", "dns") | |
| # notice the "id" field of domains in response to the above calls - that's needed | |
| # to address the domains individually, like so: | |
| # get details of a specific domain without DNS records | |
| client.call("get", "domains/dom123456") | |
| # get DNS records of a specific domain: | |
| client.call("get", "domains/dom123456/dns") | |
| # create a new A record: | |
| record = {"name": "mysubdomain", "type": "A", "content": "127.0.0.1"} | |
| client.call("post", "domains/dom123456/dns", record) | |
| # create a new SRV record | |
| # note that content is "{priority} {weight} {port} {target}" | |
| record = {"name": "mysubdomain", "type": "SRV", "content": "10 10 123 __service"} | |
| client.call("post", "domains/dom123456/dns", record) | |
| # create a new MX record | |
| # note that content is "{priority} {host}" | |
| record = {"name": "mysubdomain", "type": "MX", "content": "10 mail"} | |
| client.call("post", "domains/dom123456/dns", record) | |
| # notice the "id" field of DNS records in the above calls - that's | |
| # needed to address the DNS records individually, like so: | |
| # update an existing DNS record | |
| client.call("put", "dns/dns1234567", {"content": "127.0.0.1"}) | |
| # delete a DNS record: | |
| client.call("delete", "dns/dns1234567") |
| #!/usr/bin/python | |
| """ | |
| bulkhover.py 1.1 | |
| This is a command-line script to import and export DNS records for a single | |
| domain into or out of a hover account. | |
| Usage: | |
| bulkhover.py [options] (import|export) <domain> <dnsfile> | |
| bulkhover.py (-h | --help) | |
| bulkhover.py --version | |
| Options: | |
| -h --help Show this screen | |
| --version Show version | |
| -c --conf=<conf> Path to conf | |
| -u --username=<user> Your hover username | |
| -p --password=<pass> Your hover password | |
| -f --flush Delete all existing records before importing | |
| Examples: | |
| The DNS file should have one record per line, in the format: | |
| {name} {type} {content} | |
| For example: | |
| www A 127.0.0.1 | |
| @ MX 10 example.com | |
| Since the script output is in the same format as its input, you can use shell | |
| pipelines to do complex operations. | |
| Copy all DNS records from one domain to another: | |
| bulkhover.py -c my.conf export example.com - | ./bulkhover.py -c my.conf -f import other.com - | |
| Copy only MX records from one domain to another: | |
| ./bulkhover.py -c my.conf export foo.com - | awk '$2 == "MX" {print $0}' | ./bulkhover.py -c my.conf import bar.com - | |
| To avoid passing your username and password in the command-line, you can use | |
| a conf file that contains them instead: | |
| [hover] | |
| username=YOUR_USERNAME | |
| password=YOUR_PASSWORD | |
| """ | |
| import ConfigParser | |
| import docopt | |
| import requests | |
| import sys | |
| class HoverException(Exception): | |
| pass | |
| class HoverAPI(object): | |
| def __init__(self, username, password): | |
| params = {"username": username, "password": password} | |
| r = requests.post("https://www.hover.com/api/login", params=params) | |
| if not r.ok or "hoverauth" not in r.cookies: | |
| raise HoverException(r) | |
| self.cookies = {"hoverauth": r.cookies["hoverauth"]} | |
| def call(self, method, resource, data=None): | |
| url = "https://www.hover.com/api/{0}".format(resource) | |
| r = requests.request(method, url, data=data, cookies=self.cookies) | |
| if not r.ok: | |
| raise HoverException(r) | |
| if r.content: | |
| body = r.json() | |
| if "succeeded" not in body or body["succeeded"] is not True: | |
| raise HoverException(body) | |
| return body | |
| def import_dns(username, password, domain, filename, flush=False): | |
| try: | |
| client = HoverAPI(username, password) | |
| except HoverException as e: | |
| raise HoverException("Authentication failed") | |
| if flush: | |
| records = client.call("get", "domains/{0}/dns".format(domain))["domains"][0]["entries"] | |
| for record in records: | |
| client.call("delete", "dns/{0}".format(record["id"])) | |
| print "Deleted {name} {type} {content}".format(**record) | |
| domain_id = client.call("get", "domains/{0}".format(domain))["domain"]["id"] | |
| if filename == "-": filename = "/dev/stdin" | |
| with open(filename, "r") as f: | |
| for line in f: | |
| parts = line.strip().split(" ", 2) | |
| record = {"name": parts[0], "type": parts[1], "content": parts[2]} | |
| client.call("post", "domains/{0}/dns".format(domain), record) | |
| print "Created {name} {type} {content}".format(**record) | |
| def export_dns(username, password, domain, filename): | |
| try: | |
| client = HoverAPI(username, password) | |
| except HoverException as e: | |
| raise HoverException("Authentication failed") | |
| records = client.call("get", "domains/{0}/dns".format(domain))["domains"][0]["entries"] | |
| if filename == "-": filename = "/dev/stdout" | |
| with open(filename, "w") as f: | |
| for record in records: | |
| f.write("{name} {type} {content}\n".format(**record)) | |
| def main(args): | |
| def get_conf(filename): | |
| config = ConfigParser.ConfigParser() | |
| config.read(filename) | |
| items = dict(config.items("hover")) | |
| return items["username"], items["password"] | |
| if args["--conf"] is None: | |
| if not all((args["--username"], args["--password"])): | |
| print("You must specifiy either a conf file, or a username and password") | |
| return 1 | |
| else: | |
| username, password = args["--username"], args["--password"] | |
| else: | |
| username, password = get_conf(args["--conf"]) | |
| try: | |
| if args["import"]: | |
| import_dns(username, password, args["<domain>"], args["<dnsfile>"], args["--flush"]) | |
| elif args["export"]: | |
| export_dns(username, password, args["<domain>"], args["<dnsfile>"]) | |
| except HoverException as e: | |
| print "Unable to update DNS: {0}".format(e) | |
| return 1 | |
| if __name__ == "__main__": | |
| version = __doc__.strip().split("\n")[0] | |
| args = docopt.docopt(__doc__, version=version) | |
| status = main(args) | |
| sys.exit(status) |
| #!/usr/bin/env python | |
| """ | |
| dynhover.py 1.2 | |
| This tool will update an A record for given (sub)domain in your hover.com | |
| with your IP, or an IP that you specify | |
| Usage: | |
| dynhover.py (-c <conf> | -u <user> -p <password>) <domain> | |
| dynhover.py (-h | --help) | |
| dynhover.py --version | |
| Options: | |
| -h --help Show this screen | |
| --version Show version | |
| -c --conf=<conf> Path to conf | |
| -u --username=<user> Your hover username | |
| -p --password=<pass> Your hover password | |
| -i --ip=<ip> An IP to set (auto-detected by default) | |
| """ | |
| import ConfigParser | |
| import docopt | |
| import requests | |
| import sys | |
| class HoverException(Exception): | |
| pass | |
| class HoverAPI(object): | |
| def __init__(self, username, password): | |
| params = {"username": username, "password": password} | |
| r = requests.post("https://www.hover.com/api/login", params=params) | |
| if not r.ok or "hoverauth" not in r.cookies: | |
| raise HoverException(r) | |
| self.cookies = {"hoverauth": r.cookies["hoverauth"]} | |
| def call(self, method, resource, data=None): | |
| url = "https://www.hover.com/api/{0}".format(resource) | |
| r = requests.request(method, url, data=data, cookies=self.cookies) | |
| if not r.ok: | |
| raise HoverException(r) | |
| if r.content: | |
| body = r.json() | |
| if "succeeded" not in body or body["succeeded"] is not True: | |
| raise HoverException(body) | |
| return body | |
| def get_public_ip(): | |
| return requests.get("http://ifconfig.me/ip").content | |
| def update_dns(username, password, fqdn, ip): | |
| try: | |
| client = HoverAPI(username, password) | |
| except HoverException as e: | |
| raise HoverException("Authentication failed") | |
| dns = client.call("get", "dns") | |
| dns_id = None | |
| for domain in dns["domains"]: | |
| if fqdn == domain["domain_name"]: | |
| fqdn = "@.{domain_name}".format(**domain) | |
| for entry in domain["entries"]: | |
| if entry["type"] != "A": continue | |
| if "{0}.{1}".format(entry["name"], domain["domain_name"]) == fqdn: | |
| dns_id = entry["id"] | |
| break | |
| if dns_id is None: | |
| raise HoverException("No DNS record found for {0}".format(fqdn)) | |
| response = client.call("put", "dns/{0}".format(dns_id), {"content": my_ip}) | |
| if "succeeded" not in response or response["succeeded"] is not True: | |
| raise HoverException(response) | |
| def main(args): | |
| if args["--username"]: | |
| username, password = args["--username"], args["--password"] | |
| else: | |
| config = ConfigParser.ConfigParser() | |
| config.read(args["--conf"]) | |
| items = dict(config.items("hover")) | |
| username, password = items["username"], items["password"] | |
| domain = args["<domain>"] | |
| ip = args["--ip"] or get_public_ip() | |
| try: | |
| update_dns(username, password, domain, ip) | |
| except HoverException as e: | |
| print "Unable to update DNS: {0}".format(e) | |
| return 1 | |
| return 0 | |
| if __name__ == "__main__": | |
| version = __doc__.strip().split("\n")[0] | |
| args = docopt.docopt(__doc__, version=version) | |
| status = main(args) | |
| sys.exit(status) |
| #!/bin/bash | |
| [[ $# -lt 3 ]] && echo "Usage: $0 USERNAME PASSWORD DNS_ID" | |
| USERNAME=${1} | |
| PASSWORD=${2} | |
| DNS_ID=${3} | |
| # find your DNS ID here: https://www.hover.com/api/domains/yourdomain.com/dns/ | |
| # (replace "yourdomain.com" with your actual domain, and look for the record | |
| # you want to change. The ID looks like: dns1234567) | |
| IP=$(curl "http://ifconfig.me/ip" -s) | |
| curl "https://www.hover.com/api/dns/${DNS_ID}" \ | |
| -X PUT \ | |
| -d "content=${IP}" \ | |
| -s \ | |
| -b <(curl "https://www.hover.com/api/login" \ | |
| -X POST \ | |
| -G \ | |
| -d "username=${USERNAME}" \ | |
| -d "password=${PASSWORD}" \ | |
| -s \ | |
| -o /dev/null \ | |
| -c -) | |
| echo | |
Sorry for the delay in getting back to you... slipped through the cracks!
a 404 error is normal to that URL when doing a GET request... you have use the POST method. Make sure you are doing that by using the "-X POST" command line parameter with curl.
Sorry for the delay in getting back to you... slipped through the cracks!
a 404 error is normal to that URL when doing a GET request... you have use the POST method. Make sure you are doing that by using the "-X POST" command line parameter with curl.
Thank you - curl "https://www.hover.com/api/login" -H "Content-type: application/json" -X POST -d "{"password": "$PASSWORD", "username": "$USERNAME"}" -s -S -c $COOKIEJAR -o /dev/null
I was using -X POST.
I contacted hover.com and it's definately been disabled even with 2FA
Lei (Hover Help Center)
Jul 6, 2023, 12:05 EDT
Hello!
Thanks for your reply - API is no longer usable to log in to Hover.
If using an API is critical to your setup, I may suggest our sister company, [OpenSRS.com](http://opensrs.com/).
Best,
Just FYI - there's an example that does use 2FA. I've copied code from it. dns-lexicon seems to have support for 2FA now as well. https://github.com/pjslauta/hover-dyn-dns
Thank you very much. I'll try this later today. I do note that the login is different so that's a positive thing.
https://www.hover.com/signin/auth.json. I emailed hover yesterday asking how to do it also. No idea if they will reply in a helpful manner.UPDATE: I just tried with username password as you described and it gets a 404. A direct curl to that endpoint shows a 404 also. Could you please confirm on your end?