chores.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import time
  2. from socket import gethostbyname
  3. from django.conf import settings
  4. from django.core.mail import get_connection, mail_admins
  5. from django.core.management import BaseCommand
  6. from django.utils import timezone
  7. import dns.message, dns.rdatatype, dns.query
  8. from desecapi import models
  9. from desecapi.pdns_change_tracker import PDNSChangeTracker
  10. class Command(BaseCommand):
  11. @staticmethod
  12. def delete_expired_captchas():
  13. models.Captcha.objects.filter(
  14. created__lt=timezone.now() - settings.CAPTCHA_VALIDITY_PERIOD
  15. ).delete()
  16. @staticmethod
  17. def delete_never_activated_users():
  18. # delete inactive users whose activation link expired and who never logged in
  19. # (this will not delete users who have used their account and were later disabled)
  20. models.User.objects.filter(
  21. is_active__isnull=True,
  22. last_login__isnull=True,
  23. created__lt=timezone.now()
  24. - settings.VALIDITY_PERIOD_VERIFICATION_SIGNATURE,
  25. ).delete()
  26. @staticmethod
  27. def update_healthcheck_timestamp():
  28. name = "internal-timestamp.desec.test"
  29. try:
  30. domain = models.Domain.objects.get(name=name)
  31. except models.Domain.DoesNotExist:
  32. # Fail silently. If external alerting is configured, it will catch the problem; otherwise, we don't need it.
  33. print(f"{name} zone is not configured; skipping TXT record update")
  34. return
  35. content = f'"{int(time.time())}"'
  36. with PDNSChangeTracker():
  37. rrset, _ = domain.rrset_set.update_or_create(
  38. subname="", type="TXT", defaults={"ttl": settings.MINIMUM_TTL_DEFAULT}
  39. )
  40. rrset.save_records([content])
  41. print(f"TXT {name} updated to {content}")
  42. @staticmethod
  43. def alerting_healthcheck():
  44. name = "external-timestamp.desec.test"
  45. try:
  46. models.Domain.objects.get(name=name)
  47. except models.Domain.DoesNotExist:
  48. print(f"{name} zone is not configured; skipping alerting health check")
  49. return
  50. timestamps = []
  51. qname = dns.name.from_text(name)
  52. query = dns.message.make_query(qname, dns.rdatatype.TXT)
  53. query.use_edns(
  54. payload=4096, options=[dns.edns.GenericOption(dns.edns.NSID, "")]
  55. )
  56. server = gethostbyname("ns1.desec.io")
  57. response = None
  58. response_opts = None
  59. try:
  60. response = dns.query.tcp(query, server, timeout=5)
  61. for content in response.find_rrset(
  62. dns.message.ANSWER, qname, dns.rdataclass.IN, dns.rdatatype.TXT
  63. ):
  64. timestamps.append(str(content)[1:-1])
  65. response_opts = [opt.__dict__ for opt in response.options]
  66. except Exception:
  67. pass
  68. now = time.time()
  69. if any(now - 600 <= int(timestamp) <= now for timestamp in timestamps):
  70. print(f"TXT {name} up to date.")
  71. return
  72. timestamps = ", ".join(timestamps)
  73. print(f"TXT {name} out of date! Timestamps: {timestamps}")
  74. subject = "ALERT Alerting system down?"
  75. message = f"TXT query for {name} on {server} gave the following response:\n"
  76. message += f"{str(response)}\n\n"
  77. message += f"{str(response_opts)}\n\n"
  78. message += f"Extracted timestamps in TXT RRset:\n{timestamps}"
  79. mail_admins(
  80. subject,
  81. message,
  82. connection=get_connection("django.core.mail.backends.smtp.EmailBackend"),
  83. )
  84. def handle(self, *args, **kwargs):
  85. try:
  86. self.alerting_healthcheck()
  87. self.update_healthcheck_timestamp()
  88. self.delete_expired_captchas()
  89. self.delete_never_activated_users()
  90. except Exception as e:
  91. subject = "chores Exception!"
  92. message = f"{type(e)}\n\n{str(e)}"
  93. print(f"Chores exception: {type(e)}, {str(e)}")
  94. mail_admins(
  95. subject,
  96. message,
  97. connection=get_connection(
  98. "django.core.mail.backends.smtp.EmailBackend"
  99. ),
  100. )