Ticket #28271: tor-check-onion-service-descriptor

File tor-check-onion-service-descriptor, 3.7 KB (added by irl, 9 months ago)
Line 
1#!/usr/bin/python3
2#
3# Copyright 2018 The Tor Project
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10#   notice, this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above
13#   copyright notice, this list of conditions and the following disclaimer
14#   in the documentation and/or other materials provided with the
15#   distribution.
16#
17# * Neither the names of the copyright owners nor the names of its
18#   contributors may be used to endorse or promote products derived from
19#   this software without specific prior written permission.
20#
21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32"""Nagios plugin to check that the age of the latest published descriptor for a
33v2 Onion service.
34
35Do not run this check more than once every 30 minutes as you will hit
36rate-limiting, which is indistinguishable from the descriptor not being
37retrievable from the HSDirs. (See https://bugs.torproject.org/28269)"""
38
39import argparse
40import datetime
41import stem
42import sys
43from stem.control import Controller
44
45OK, WARNING, CRITICAL, UNKNOWN = range(4)
46
47DEFAULT_WARNING_AGE = 18
48DEFAULT_CRITICAL_AGE = 23
49
50
51def hidden_service_descriptor_age(onion, warn_age, crit_age):
52    try:
53        with Controller.from_port(port=9051) as controller:
54            controller.authenticate()
55            desc = controller.get_hidden_service_descriptor(onion)
56            age = datetime.datetime.utcnow() - desc.published
57            if age > crit_age:
58                return CRITICAL, "Hidden service descriptor is %s old" % (
59                    str(age), )
60            elif age > warn_age:
61                return CRITICAL, "Hidden service descriptor is %s old" % (
62                    str(age), )
63            else:
64                return OK, "Hidden service descriptor is %s old" % (str(age), )
65            print(repr(age))
66    except ValueError as e:
67        return CRITICAL, "%s is not a valid onion address" % (onion, )
68    except stem.ControllerError as e:
69        return CRITICAL, "Controller error"
70
71
72if __name__ == "__main__":
73    parser = argparse.ArgumentParser(
74        description="Check age of Tor rend-spec v2 service descriptor")
75    parser.add_argument(
76        '-w',
77        metavar='HOURS',
78        type=int,
79        nargs='?',
80        help='warning when older than this many hours')
81    parser.add_argument(
82        '-c',
83        metavar='HOURS',
84        type=int,
85        nargs='?',
86        help='critical when older than this many hours')
87    parser.add_argument(
88        'onion', metavar='onion', type=str, help='onion address')
89    args = parser.parse_args()
90
91    warn_age = datetime.timedelta(hours=args.w or DEFAULT_WARNING_AGE)
92    crit_age = datetime.timedelta(hours=args.c or DEFAULT_CRITICAL_AGE)
93
94    status, message = hidden_service_descriptor_age(args.onion, warn_age,
95                                                    crit_age)
96    print(message)
97    sys.exit(status)