Closed gitbls closed 1 year ago
Same problem encountered here.
I thought toggling it off in account settings would do the trick but it just remains on, reading further into it I noticed they state it's now mandatory. Frustrating!
I've contacted Hover, if no change (which I highly doubt unfortunatly...) I'll change provider.
Just to update... I moved my domain to Google and now using a service called ddclient.
Not sure what OPs use case is but for me I simply needed a way to update my domain to reference my external dynamic IP.
ddclient does the job beautifully and seems to be very popular and well maintained/documented. I wish I had discovered it sooner. It took me a long time to get the (now broken) Hover script working in this way.
Hey - I'm the original contributer of the Hover provider in lexicon. I got a nice surprise when my certbot and wildcard certificates failed!
I'm also going to contact Hover support, but I suspect this will also drive me to change providers :disappointed:
I contacted Hover support, and they were not immediately helpful. I switched to using Google Cloud for DNS becuause that's what I use in my day job. It costs me $0.01/day or $0.30/month so its almost free. I'm sure there are other free DNS providers to switch to.
It is actually possible to login with 2fa using dns-lexicon
I've monkey-patched my local implementation to do this:
# The provided _authenticate function does not provide authentication
# for 2fa-enabled accounts. This shim does that
def _new_authenticate(self: hover.Provider) -> None:
# Getting required cookies "hover_session" and "hoverauth"
response = requests.get("https://www.hover.com/signin")
self.cookies["hover_session"] = response.cookies["hover_session"]
# Part one, login credentials
payload = {
"username": self._get_provider_option("auth_username"),
"token": None,
"password": self._get_provider_option("auth_password"),
}
response = requests.post(
"https://www.hover.com/signin/auth.json", json=payload, cookies=self.cookies
)
response.raise_for_status()
# Part two, 2fa
payload = {
"code": self._get_provider_option("auth_token"),
}
response = requests.post(
"https://www.hover.com/signin/auth2.json", json=payload, cookies=self.cookies
)
response.raise_for_status()
if "hoverauth" not in response.cookies:
raise Exception("Unexpected auth response")
self.cookies["hoverauth"] = response.cookies["hoverauth"]
# Make sure domain exists
# domain is stored in self.domain from BaseProvider
domains = self._list_domains()
for domain in domains:
if domain["name"] == self.domain:
self.domain_id = domain["id"]
break
else:
raise AuthenticationError(f"Domain {self.domain} not found")
hover.Provider._authenticate = _new_authenticate
If this looks like a reasonable implementation I'd be happy to open a PR
Confirmed @bigfootjon 's code works. Less monkey patch, more direct hacking...
my certbot.hover.sh is looking like
TOKEN=$(oathtool -b --totp 'MY_TOTP_SECRET') PROVIDER_CREDENTIALS=("--auth-username=MY_USERNAME" "--auth-password=MY_PASSWORD" "--auth-token=${TOKEN}")
I think a pyotp integration might be appropriate? --auth-totp-secret instead of --auth-token
Ok guys, let's tackle this.
So basically it seems that we have two approaches:
1) either exposing an OTP generated outside of Lexicon through a new flag like --auth-token
2) or generate the OTP directly in Lexicon using a provided secret through a new flag like --auth-totp-secret
and the integration of a Python OTP library.
The issues review from Sudrien seems to indicate that OTP could be used in other providers, in place of dedicated static application credentials. Also pyotp
seems a very light library with no dependency and large Python compatibility and activate maintenance. I am usually reluctant to add a dependency either globally to avoid big dependency graphs, or for a specific provider given the added complexity to handle this user-side. But here it seems to be a reasonable move.
So I will create a PR for hover
with the approach proposed by @bigfootjon + the flag and pyotp
integration proposed by @Sudrien.
Then, since I have no active account for Hover, I would like that one of you test the updated provider and generate the new set of cassettes for the integration tests. Sounds good to you ?
Unfortunately I’m traveling for the next few weeks so I’ll be unable to test until then, but the code looks conceptually correct to me
Sorry, more of a rubygems person usually
pip install git+https://github.com/AnalogJ/lexicon.git@hover-otp
somthing worked
lexicon hover -h
New option shows
PROVIDER_CREDENTIALS=("--auth-username=MY_USERNAME" "--auth-password=MY_PASSWORD" "--auth-totp-secret=MY_TOTP_SECRET")
Note: MY_TOTP_SECRET should have no whitespace, according to quick pyotp test - what breaks when I have it in? Test this later
trying this with my test call for last night...
manual-auth-hook command "/root/certbot.hover.sh auth" returned error code 1
Error output from manual-auth-hook command certbot.hover.sh:
Traceback (most recent call last):
File "/usr/local/bin/lexicon", line 8, in <module>
sys.exit(main())
File "/usr/local/lib/python3.9/dist-packages/lexicon/_private/cli.py", line 135, in main
results = client.execute()
File "/usr/local/lib/python3.9/dist-packages/lexicon/client.py", line 194, in execute
executor = self.__enter__()
File "/usr/local/lib/python3.9/dist-packages/lexicon/client.py", line 151, in __enter__
raise e
File "/usr/local/lib/python3.9/dist-packages/lexicon/client.py", line 143, in __enter__
provider = self.provider_class(self.config)
TypeError: Can't instantiate abstract class Provider with abstract method authenticate
....
Which seems to be about
def _authenticate(self) -> None:
in hover.py but switching it back gets rid of the error with no actual dns change, and thus challenge failure.
Wait, switched to
def authenticate(self):
from
def _authenticate(self) -> None:
and removed
"token": None,
in the first call from hover.py and I'm getting success.
Doing the MY_TOTP_SECRET with whitspace test mentioned above...
manual-cleanup-hook command "/root/certbot.hover.sh cleanup" returned error code 1
Error output from manual-cleanup-hook command certbot.hover.sh:
Traceback (most recent call last):
File "/usr/local/bin/lexicon", line 8, in <module>
sys.exit(main())
File "/usr/local/lib/python3.9/dist-packages/lexicon/_private/cli.py", line 135, in main
results = client.execute()
File "/usr/local/lib/python3.9/dist-packages/lexicon/client.py", line 194, in execute
executor = self.__enter__()
File "/usr/local/lib/python3.9/dist-packages/lexicon/client.py", line 151, in __enter__
raise e
File "/usr/local/lib/python3.9/dist-packages/lexicon/client.py", line 144, in __enter__
provider.authenticate()
File "/usr/local/lib/python3.9/dist-packages/lexicon/_private/providers/hover.py", line 59, in authenticate
payload = {"code": self.totp.now()}
File "/usr/local/lib/python3.9/dist-packages/pyotp/totp.py", line 64, in now
return self.generate_otp(self.timecode(datetime.datetime.now()))
File "/usr/local/lib/python3.9/dist-packages/pyotp/otp.py", line 35, in generate_otp
hasher = hmac.new(self.byte_secret(), self.int_to_bytestring(input), self.digest)
File "/usr/local/lib/python3.9/dist-packages/pyotp/otp.py", line 52, in byte_secret
return base64.b32decode(secret, casefold=True)
File "/usr/lib/python3.9/base64.py", line 231, in b32decode
raise binascii.Error('Non-base32 digit found') from None
binascii.Error: Non-base32 digit found
So I'd suggest stripping all whitespace when feeding it into pyotp - as whitespace is often introduced to make human handling easier.
Hello @Sudrien, I fixed the issues that have been caught, you can test again.
My tests are all working as expected now, thank you.
Hello @Sudrien, are you willing to go ahead and update the cassettes stored in the repository for Hover provider, that allows offline integration tests? It is basically about:
hover
: https://dns-lexicon.readthedocs.io/en/latest/developer_guide.html#add-new-tests-recordingsIf not, my alternative is that you provide me some temporary credentials and I do it directly.
I can handle one dump, unfortunately what I'm seeing is a bit beyond my python knowledge at the moment.
@adferrand, Assuming your commit email is current, I've sent login information for my second account.
Yeah I can confirm that this PR works for me
hi, i install via pip install dns-lexicon how to update to have this modify ? like pip install will update the existing one with the fix for that ?
It is actually possible to login with 2fa using dns-lexicon
I've monkey-patched my local implementation to do this:
# The provided _authenticate function does not provide authentication # for 2fa-enabled accounts. This shim does that def _new_authenticate(self: hover.Provider) -> None: # Getting required cookies "hover_session" and "hoverauth" response = requests.get("https://www.hover.com/signin") self.cookies["hover_session"] = response.cookies["hover_session"] # Part one, login credentials payload = { "username": self._get_provider_option("auth_username"), "token": None, "password": self._get_provider_option("auth_password"), } response = requests.post( "https://www.hover.com/signin/auth.json", json=payload, cookies=self.cookies ) response.raise_for_status() # Part two, 2fa payload = { "code": self._get_provider_option("auth_token"), } response = requests.post( "https://www.hover.com/signin/auth2.json", json=payload, cookies=self.cookies ) response.raise_for_status() if "hoverauth" not in response.cookies: raise Exception("Unexpected auth response") self.cookies["hoverauth"] = response.cookies["hoverauth"] # Make sure domain exists # domain is stored in self.domain from BaseProvider domains = self._list_domains() for domain in domains: if domain["name"] == self.domain: self.domain_id = domain["id"] break else: raise AuthenticationError(f"Domain {self.domain} not found") hover.Provider._authenticate = _new_authenticate
If this looks like a reasonable implementation I'd be happy to open a PR
can i see all your code please ? i don't have "hover."
Hover is just the provider provided by lexicon. Its import path has moved around over time.
I deleted this code since a new release was made with similar code
Hover is just the provider provided by lexicon. Its import path has moved around over time.
I deleted this code since a new release was made with similar code
so why my [dns-lexicon] hover api isn't working ?
from flask import jsonify
from lexicon.config import ConfigResolver
from lexicon.client import Client
def dohoverapi(nome,ip):
lexicon_config = {
"provider_name" : "hover", # lexicon shortname for provider, see providers directory for available proviers
"action": "create", # create, list, update, delete
"domain": "x", # domain name
"name" : nome,
"content" : ip,
"type": "A", # specify a type for record filtering, case sensitive in some cases.
"hover": {
"auth_username": "y",
"auth_password": "z"
}
}
config = ConfigResolver()
config.with_env().with_dict(dict_object=lexicon_config)
client = Client(config)
results = client.execute()
dohoverapi("test","x.y.z.k")
response : 401 Client Error: Unauthorized
Please look at this PR: https://github.com/AnalogJ/lexicon/pull/1718
or this documentation: https://dns-lexicon.readthedocs.io/en/latest/configuration_reference.html#hover
you need to pass a new option
Please look at this PR: #1718
or this documentation: https://dns-lexicon.readthedocs.io/en/latest/configuration_reference.html#hover
you need to pass a new option
thanks for quick reply : 1) i don't have "auth_totp_secret" in my code i install via pip install maybe isn't officially relesed ? 2) where i can take this param ?
auth_totp_secret <- what i have to put here ?
idk guys i have to do in a different way : with auth_totp_secret => xyz=== 422 (Unprocessable Entity) i do :
in hover.py : in __init ()
otp_uri = 'otpauth://totp/Hover:name?secret=secret&issuer=Hover'
self.totp = pyotp.parse_uri( otp_uri )
in authenticate():
payload = {"code": self.totp.now()}
now it work but everytime hover.com send me a mail new access
Your auth_totp_secret is the value of “secret=“ in your URL
Hover has forced mandatory 2FA on all accounts, with no notification, breaking usage of lexicon-dns with Hover.
I spoke with Hover Support about this yesterday. They said that they are not hearing from enough people needing API interfaces to Hover for them to invest in making it work in this new security regime. I mentioned that both Microsoft and Google have app passwords which they could implement as well, but no positive response on this from them yet.
If this is important to you, PLEASE contact Hover support and let them know that you use the API, and that them disabling access to it is a serious problem for you.