vdirsyncer icon indicating copy to clipboard operation
vdirsyncer copied to clipboard

Ocassionally, google backend fail

Open WhyNotHugo opened this issue 7 years ago • 14 comments

I've a cron on two machines that syncs all my calendars, including a single google calendar one:

 0	*	*	*	*	chronic vdirsyncer -v DEBUG sync

For some reason, I get a few emails a day for 401 errors. The odd thing is:

  • Which machine fails varies.
  • They fail only from time to time.

The full debug output is here.

My configuration:

[general]
status_path = "~/.local/share/vdirsyncer/status/"

##### Contacts ######

[pair contacts]
a = "contacts_local"
b = "contacts_fastmail"
collections = null
metadata = []
conflict_resolution = ["command", "vimdiff"]

[storage contacts_local]
type = "filesystem"
path = "~/.local/share/contacts/cards/"
fileext = ".vcf"

[storage contacts_fastmail]
type = "carddav"
url = "https://carddav.messagingengine.com/dav/addressbooks/user/[email protected]/Default/"
username = "[email protected]"
password.fetch = ["command", "~/.local/bin/vdirsyncer-pass", "fastmail:carddav"]

##### Calendars ######

[pair calendars]
a = "calendars_local"
b = "calendars_fastmail"
collections = ["from b"]
metadata = ["color", "displayname"]
conflict_resolution = ["command", "vimdiff"]

[storage calendars_local]
type = "filesystem"
path = "~/.local/share/calendars/"
fileext = ".ics"

[storage calendars_fastmail]
type = "caldav"
url = "https://caldav.messagingengine.com/"
username = "[email protected]"
password.fetch = ["command", "~/.local/bin/vdirsyncer-pass", "fastmail:caldav"]

##### Work Calendars #####

[pair work_calendars]
a = "calendars_local"
b = "calendars_google"
collections = ["[email protected]"]
metadata = ["color", "displayname"]
conflict_resolution = ["command", "vimdiff"]

[storage calendars_google]
type = "google_calendar"
token_file = "~/.cache/vdirsyncer/google.token"
client_id = "1066339489224-5v10lce8q2i7d4ivo441bjus5d3178uu.apps.googleusercontent.com"
client_secret = "REDACTED"

WhyNotHugo avatar Mar 02 '17 19:03 WhyNotHugo

I can't do anything against that though. Best I can recommend is to wrap vdirsyncer with a retry-script

untitaker avatar Mar 02 '17 20:03 untitaker

So, it's just a server-side thing?

WhyNotHugo avatar Mar 02 '17 20:03 WhyNotHugo

I do think so, I can't imagine why we would send invalid auth headers sometimes. Also we let oauthlib handle all that, and that library is pretty popular.

untitaker avatar Mar 02 '17 20:03 untitaker

If you manage to dump raw traffic (particularly auth headers), perhaps this will be easier. I think mitmproxy has a logging mode as well?

untitaker avatar Mar 02 '17 20:03 untitaker

That's really hard, because of how timed apart the failures are. I just ran this:

for i in $(seq 1 50); do vdirsyncer sync work_calendars; done;

And all 50 tried worked fine. :(

WhyNotHugo avatar Mar 02 '17 21:03 WhyNotHugo

I'll try and leave that on both computers at some point if I'm out for the weekend or something.

WhyNotHugo avatar Mar 02 '17 21:03 WhyNotHugo

@hobarrera any update on this?

untitaker avatar Mar 07 '17 21:03 untitaker

Nope. I'll try to leave a mitm tomorrow if I get the time. ^_^

WhyNotHugo avatar Mar 07 '17 22:03 WhyNotHugo

Okay, so I finally had the time to leave mitm running, and vdirsyncer debugging:

Debug output:

debug: Using 1 maximal workers.
Syncing work_calendars/[email protected]
debug: PROPFIND https://apidata.googleusercontent.com/caldav/v2/hbarrera%40shiftgig.com/events/
debug: {'User-Agent': '1066339489224-5v10lce8q2i7d4ivo441bjus5d3178uu.apps.googleusercontent.com', 'Content-Type': 'application/xml; charset=UTF-8', 'Depth': '1'}
debug: <?xml version="1.0" encoding="utf-8" ?>
debug:             <D:propfind xmlns:D="DAV:">
debug:                 <D:prop>
debug:                     <D:resourcetype/>
debug:                     <D:getcontenttype/>
debug:                     <D:getetag/>
debug:                 </D:prop>
debug:             </D:propfind>
debug:             
debug: Sending request...
debug: 401
debug: {'Vary': 'Origin, X-Origin', 'WWW-Authenticate': 'Bearer realm="https://accounts.google.com/", error=invalid_token', 'Content-Type': 'application/vnd.google.gdata.error+xml; charset=UTF-8', 'Content-Encoding': 'gzip', 'Date': 'Sun, 02 Apr 2017 18:00:03 GMT', 'Expires': 'Sun, 02 Apr 2017 18:00:03 GMT', 'Cache-Control': 'private, max-age=0', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'Server': 'GSE', 'Alt-Svc': 'quic=":443"; ma=2592000; v="37,36,35"', 'Transfer-Encoding': 'chunked'}
debug: <?xml version="1.0" encoding="UTF-8"?>
debug: <errors xmlns="http://schemas.google.com/g/2005">
debug:  <error>
debug:   <domain>GData</domain>
debug:   <code>authError</code>
debug:   <location type="header">Authorization</location>
debug:   <internalReason>Invalid Credentials</internalReason>
debug:  </error>
debug: </errors>
/usr/lib/python3.6/site-packages/urllib3/connectionpool.py:852: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
error: Unknown error occured for work_calendars/[email protected]: 401 Client Error: Unauthorized for url: https://apidata.googleusercontent.com/caldav/v2/hbarrera%40shiftgig.com/events/
error: Use `-vdebug` to see the full traceback.
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/cli/tasks.py", line 69, in sync_collection
debug:     partial_sync=pair.partial_sync
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/sync.py", line 561, in sync
debug:     b_nonempty = b_info.prepare_new_status()
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/sync.py", line 472, in prepare_new_status
debug:     for href, etag in self.storage.list():
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/storage/dav.py", line 801, in list
debug:     for x in DAVStorage.list(self):
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/storage/dav.py", line 609, in list
debug:     headers=headers)
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/storage/dav.py", line 358, in request
debug:     return http.request(method, url, session=self._session, **more)
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/http.py", line 183, in request
debug:     r.raise_for_status()
debug:   File "/usr/lib/python3.6/site-packages/requests/models.py", line 909, in raise_for_status
debug:     raise HTTPError(http_error_msg, response=self)
error: 1 out of 2 tasks failed.

Request:

PROPFIND /caldav/v2/hbarrera%40shiftgig.com/events/ HTTP/1.1
Host: apidata.googleusercontent.com
User-Agent: 1066339489224-5v10lce8q2i7d4ivo441bjus5d3178uu.apps.googleusercontent.com
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Type: application/xml; charset=UTF-8
Depth: 1
Authorization: Bearer ya29.G[REDACTED]-A
Content-Length: 280

<?xml version="1.0" encoding="utf-8" ?>
            <D:propfind xmlns:D="DAV:">
                <D:prop>
                    <D:resourcetype/>
                    <D:getcontenttype/>
                    <D:getetag/>
                </D:prop>
            </D:propfind>
            

Response:

HTTP/1.1 401 Unauthorized
Vary: Origin
Vary: X-Origin
WWW-Authenticate: Bearer realm="https://accounts.google.com/", error=invalid_token
Content-Type: application/vnd.google.gdata.error+xml; charset=UTF-8
Date: Sun, 02 Apr 2017 18:00:03 GMT
Expires: Sun, 02 Apr 2017 18:00:03 GMT
Cache-Control: private, max-age=0
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE
Alt-Svc: quic=":443"; ma=2592000; v="37,36,35"
Transfer-Encoding: chunked
content-length: 209

111
<?xml version="1.0" encoding="UTF-8"?>
<errors xmlns="http://schemas.google.com/g/2005">
 <error>
  <domain>GData</domain>
  <code>authError</code>
  <location type="header">Authorization</location>
  <internalReason>Invalid Credentials</internalReason>
 </error>
</errors>
0

Running vdirsyncer sync again a moment later works fine.

My cron runs hourly; is it possible that when running, it doesn't see the token as expires, uses it, but it expires while on-the-wire (it lasts EXACTLY an hour)?

Oh, BTW, I applied this diff to use mitmproxy (in case you see the above warning):

--- a/vdirsyncer/storage/google.py
+++ b/vdirsyncer/storage/google.py
@@ -35,7 +35,9 @@ class GoogleSession(dav.DAVSession):
             self.url = url

         self.useragent = client_id
-        self._settings = {}
+        self._settings = {
+            'verify': False,
+        }

         if not have_oauth2:
             raise exceptions.UserError('requests-oauthlib not installed')

WhyNotHugo avatar Apr 02 '17 18:04 WhyNotHugo

Please try changing your cron interval config to something else (every 40 mins?) to see if your suspicion is true

On Sun, Apr 02, 2017 at 11:17:08AM -0700, Hugo Osvaldo Barrera wrote:

Okay, so I finally had the time to leave mitm running, and vdirsyncer debugging:

Debug output:

debug: Using 1 maximal workers.
Syncing work_calendars/[email protected]
debug: PROPFIND https://apidata.googleusercontent.com/caldav/v2/hbarrera%40shiftgig.com/events/
debug: {'User-Agent': '1066339489224-5v10lce8q2i7d4ivo441bjus5d3178uu.apps.googleusercontent.com', 'Content-Type': 'application/xml; charset=UTF-8', 'Depth': '1'}
debug: <?xml version="1.0" encoding="utf-8" ?>
debug:             <D:propfind xmlns:D="DAV:">
debug:                 <D:prop>
debug:                     <D:resourcetype/>
debug:                     <D:getcontenttype/>
debug:                     <D:getetag/>
debug:                 </D:prop>
debug:             </D:propfind>
debug:             
debug: Sending request...
debug: 401
debug: {'Vary': 'Origin, X-Origin', 'WWW-Authenticate': 'Bearer realm="https://accounts.google.com/", error=invalid_token', 'Content-Type': 'application/vnd.google.gdata.error+xml; charset=UTF-8', 'Content-Encoding': 'gzip', 'Date': 'Sun, 02 Apr 2017 18:00:03 GMT', 'Expires': 'Sun, 02 Apr 2017 18:00:03 GMT', 'Cache-Control': 'private, max-age=0', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'Server': 'GSE', 'Alt-Svc': 'quic=":443"; ma=2592000; v="37,36,35"', 'Transfer-Encoding': 'chunked'}
debug: <?xml version="1.0" encoding="UTF-8"?>
debug: <errors xmlns="http://schemas.google.com/g/2005">
debug:  <error>
debug:   <domain>GData</domain>
debug:   <code>authError</code>
debug:   <location type="header">Authorization</location>
debug:   <internalReason>Invalid Credentials</internalReason>
debug:  </error>
debug: </errors>
/usr/lib/python3.6/site-packages/urllib3/connectionpool.py:852: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
error: Unknown error occured for work_calendars/[email protected]: 401 Client Error: Unauthorized for url: https://apidata.googleusercontent.com/caldav/v2/hbarrera%40shiftgig.com/events/
error: Use `-vdebug` to see the full traceback.
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/cli/tasks.py", line 69, in sync_collection
debug:     partial_sync=pair.partial_sync
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/sync.py", line 561, in sync
debug:     b_nonempty = b_info.prepare_new_status()
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/sync.py", line 472, in prepare_new_status
debug:     for href, etag in self.storage.list():
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/storage/dav.py", line 801, in list
debug:     for x in DAVStorage.list(self):
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/storage/dav.py", line 609, in list
debug:     headers=headers)
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/storage/dav.py", line 358, in request
debug:     return http.request(method, url, session=self._session, **more)
debug:   File "/usr/lib/python3.6/site-packages/vdirsyncer/http.py", line 183, in request
debug:     r.raise_for_status()
debug:   File "/usr/lib/python3.6/site-packages/requests/models.py", line 909, in raise_for_status
debug:     raise HTTPError(http_error_msg, response=self)
error: 1 out of 2 tasks failed.

Request:

PROPFIND /caldav/v2/hbarrera%40shiftgig.com/events/ HTTP/1.1
Host: apidata.googleusercontent.com
User-Agent: 1066339489224-5v10lce8q2i7d4ivo441bjus5d3178uu.apps.googleusercontent.com
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Type: application/xml; charset=UTF-8
Depth: 1
Authorization: Bearer ya29.G[REDACTED]-A
Content-Length: 280

<?xml version="1.0" encoding="utf-8" ?>
            <D:propfind xmlns:D="DAV:">
                <D:prop>
                    <D:resourcetype/>
                    <D:getcontenttype/>
                    <D:getetag/>
                </D:prop>
            </D:propfind>
            

Response:

HTTP/1.1 401 Unauthorized
Vary: Origin
Vary: X-Origin
WWW-Authenticate: Bearer realm="https://accounts.google.com/", error=invalid_token
Content-Type: application/vnd.google.gdata.error+xml; charset=UTF-8
Date: Sun, 02 Apr 2017 18:00:03 GMT
Expires: Sun, 02 Apr 2017 18:00:03 GMT
Cache-Control: private, max-age=0
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE
Alt-Svc: quic=":443"; ma=2592000; v="37,36,35"
Transfer-Encoding: chunked
content-length: 209

111
<?xml version="1.0" encoding="UTF-8"?>
<errors xmlns="http://schemas.google.com/g/2005">
 <error>
  <domain>GData</domain>
  <code>authError</code>
  <location type="header">Authorization</location>
  <internalReason>Invalid Credentials</internalReason>
 </error>
</errors>
0

Running vdirsyncer sync again a moment later works fine.

My cron runs hourly; is it possible that when running, it doesn't see the token as expires, uses it, but it expires while on-the-wire (it lasts EXACTLY an hour)?

Oh, BTW, I applied this diff to use mitmproxy (in case you see the above warning):

--- a/vdirsyncer/storage/google.py
+++ b/vdirsyncer/storage/google.py
@@ -35,7 +35,9 @@ class GoogleSession(dav.DAVSession):
             self.url = url

         self.useragent = client_id
-        self._settings = {}
+        self._settings = {
+            'verify': False,
+        }

         if not have_oauth2:
             raise exceptions.UserError('requests-oauthlib not installed')

-- You are receiving this because you commented. Reply to this email directly or view it on GitHub: https://github.com/pimutils/vdirsyncer/issues/575#issuecomment-291004121

untitaker avatar Apr 04 '17 08:04 untitaker

I did exactly that on one machine (well, 20 minutes), and left it at an hour in another.

The hourly one failed almost every time, the other didn't fail once.

It looks like my suspicion is correct, and running exactly at the time of expiration might make the token expire after being read but before being received by google.

We should probably renew it slight before it's expiration to avoid this.

WhyNotHugo avatar Apr 04 '17 12:04 WhyNotHugo

Could you try patching oauthlib (or changing expiration times in your token file before executing vdirsyncer) to see if this fixes things? Another possible problem is a skewed clock on your system.

Another thing is that oauthlib appears to be using "one hour" as some sort of default value. Perhaps this applies here. Could you do the auth dance (deleting the token file before) and capture it with mitmproxy?

Otherwise this might very well be a conceptual problem in oauthlib, i.e. oauthlib unrealistically assumes a perfect system clock.

On Tue, Apr 04, 2017 at 05:32:15AM -0700, Hugo Osvaldo Barrera wrote:

I did exactly that on one machine (well, 20 minutes), and left it at an hour in another.

The hourly one failed almost every time, the other didn't fail once.

It looks like my suspicion is correct, and running exactly at the time of expiration might make the token expire after being read but before being received by google.

We should probably renew it slight before it's expiration to avoid this.

-- You are receiving this because you commented. Reply to this email directly or view it on GitHub: https://github.com/pimutils/vdirsyncer/issues/575#issuecomment-291485192

untitaker avatar Apr 04 '17 17:04 untitaker

So I haven't forgotten about this: quite the contrary, I'm still suffering it. After I set my laptop and desktop to sync every 20 minutes, the errors started again. I now set one to do an hourly cron and the other to do it every 20-minutes. It's working. EXTREMELY odd. It looks like two clients can't connect for the same user at the same time, but I'll keep experimenting with this extreme quirkyness.

It's also possible that they're all very frequent failures that are driving me mad and I'm just seeing patterns where there aren't any.

WhyNotHugo avatar Apr 12 '17 03:04 WhyNotHugo

I have the same issue, a crontab running every 1 hour but it failed approximately 1 time every day.

cdemoulins avatar Jul 16 '18 16:07 cdemoulins