瀏覽代碼

feat(api): whitelist local IPv6 subnet from captcha

Peter Thomassen 8 年之前
父節點
當前提交
c28761bcf7
共有 5 個文件被更改,包括 26 次插入18 次删除
  1. 1 1
      .travis.yml
  2. 5 5
      api/desecapi/tests/testregistration.py
  3. 4 0
      api/desecapi/tests/utils.py
  4. 15 12
      api/desecapi/views.py
  5. 1 0
      docker-compose.yml

+ 1 - 1
.travis.yml

@@ -24,7 +24,7 @@ env:
    - DESECSTACK_NSLORD_APIKEY=9Fn33T5yGukjekwjew
    - DESECSTACK_NSMASTER_APIKEY=LLq1orOQuXCINUz4TV
    - DESECSTACK_IPV4_REAR_PREFIX16=172.19
-   - DESECSTACK_IPV6_SUBNET=fd80::/8
+   - DESECSTACK_IPV6_SUBNET=fd80::/16
    - DESECSTACK_IPV6_ADDRESS=fd80::1
    - DESECSTACK_WWW_CERTS=/dev/null
    - DESECSTACK_DBMASTER_CERTS=/dev/null

+ 5 - 5
api/desecapi/tests/testregistration.py

@@ -125,7 +125,7 @@ class RegistrationTest(APITestCase):
                 'password': utils.generateRandomString(size=12),
                 'dyn': True,
             }
-            response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomString())
+            response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomIPv4Address())
             self.assertEqual(response.status_code, status.HTTP_201_CREATED)
             user = models.User.objects.get(email=data['email'])
             self.assertEqual(user.email, data['email'])
@@ -139,7 +139,7 @@ class RegistrationTest(APITestCase):
             'password': utils.generateRandomString(size=12),
             'dyn': True,
         }
-        response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomString())
+        response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomIPv4Address())
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
         user = models.User.objects.get(email=data['email'])
         self.assertEqual(user.email, data['email'])
@@ -157,7 +157,7 @@ class RegistrationTest(APITestCase):
                 'password': utils.generateRandomString(size=12),
                 'dyn': True,
             }
-            response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomString())
+            response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomIPv4Address())
             self.assertEqual(response.status_code, status.HTTP_201_CREATED)
             user = models.User.objects.get(email=data['email'])
             self.assertEqual(user.email, data['email'])
@@ -176,7 +176,7 @@ class RegistrationTest(APITestCase):
             'password': utils.generateRandomString(size=12),
             'dyn': True,
         }
-        response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomString())
+        response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomIPv4Address())
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
         user = models.User.objects.get(email=data['email'])
         self.assertEqual(user.email, data['email'])
@@ -193,7 +193,7 @@ class RegistrationTest(APITestCase):
             'password': utils.generateRandomString(size=12),
             'dyn': False,
         }
-        response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomString())
+        response = self.client.post(url, data, REMOTE_ADDR=utils.generateRandomIPv4Address())
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
 
         self.assertEqual(len(mail.outbox), outboxlen + 1)

+ 4 - 0
api/desecapi/tests/utils.py

@@ -6,6 +6,10 @@ from desecapi.models import Domain, User
 
 
 class utils(object):
+    @classmethod
+    def generateRandomIPv4Address(cls):
+        return ".".join(map(str, (random.randint(0, 255) for _ in range(4))))
+
     @classmethod
     def generateRandomString(cls, size=6, chars=string.ascii_lowercase + string.digits):
         return ''.join(random.choice(chars) for _ in range(size))

+ 15 - 12
api/desecapi/views.py

@@ -32,6 +32,7 @@ from django.shortcuts import render
 from django.http import HttpResponseRedirect
 from desecapi.emails import send_account_lock_email, send_token_email
 import re
+import ipaddress, os
 
 # TODO Generalize?
 patternDyn = re.compile(r'^[A-Za-z-][A-Za-z0-9_-]*\.dedyn\.io$')
@@ -93,7 +94,7 @@ class DomainList(generics.ListCreateAPIView):
                                  [self.request.user.email])
             email.send()
 
-        if self.request.user.dyn:
+        if obj.name.endswith('.dedyn.io'):
             sendDynDnsEmail(obj)
 
 
@@ -428,17 +429,19 @@ class RegistrationView(views.RegistrationView):
         return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
 
     def perform_create(self, serializer, remote_ip):
-        captcha = \
-            (
-                User.objects.filter(
-                    created__gte=timezone.now()-timedelta(hours=settings.ABUSE_BY_REMOTE_IP_PERIOD_HRS),
-                    registration_remote_ip=remote_ip
-                ).count() >= settings.ABUSE_BY_REMOTE_IP_LIMIT
-                or
-                User.objects.filter(
-                    created__gte=timezone.now() - timedelta(hours=settings.ABUSE_BY_EMAIL_HOSTNAME_PERIOD_HRS),
-                    email__endswith=serializer.validated_data['email'].split('@')[-1]
-                ).count() >= settings.ABUSE_BY_EMAIL_HOSTNAME_LIMIT
+        captcha = (
+                ipaddress.ip_address(remote_ip) not in ipaddress.IPv6Network(os.environ['DESECSTACK_IPV6_SUBNET'])
+                and (
+                    User.objects.filter(
+                        created__gte=timezone.now()-timedelta(hours=settings.ABUSE_BY_REMOTE_IP_PERIOD_HRS),
+                        registration_remote_ip=remote_ip
+                    ).count() >= settings.ABUSE_BY_REMOTE_IP_LIMIT
+                    or
+                    User.objects.filter(
+                        created__gte=timezone.now() - timedelta(hours=settings.ABUSE_BY_EMAIL_HOSTNAME_PERIOD_HRS),
+                        email__endswith=serializer.validated_data['email'].split('@')[-1]
+                    ).count() >= settings.ABUSE_BY_EMAIL_HOSTNAME_LIMIT
+                )
             )
 
         user = serializer.save(registration_remote_ip=remote_ip, captcha_required=captcha)

+ 1 - 0
docker-compose.yml

@@ -111,6 +111,7 @@ services:
     - DESECSTACK_API_EMAIL_PORT
     - DESECSTACK_API_SECRETKEY
     - DESECSTACK_DBAPI_PASSWORD_desec
+    - DESECSTACK_IPV6_SUBNET
     - DESECSTACK_NSLORD_APIKEY
     - DESECSTACK_NSMASTER_APIKEY
     - DESECSTACK_NORECAPTCHA_SITE_KEY