unioslo/mreg

Looks like MREG allows name collisions between host objects (A records) and CNAMEs

Closed this issue · 3 comments

Fra Mattermost:

mikaeld
virker som vi har funnet en bug: vortex-sync01 er et host-object som har et cname knyttet til seg: vortex-sync. i går fikk
virtprov lov å lage et fullverdig host-object med navn vortex-sync

resultatet er at vi har både "vortex-sync cname vortex-sync01" og "vortex-sync A 129.240.113.119"

det går ikke

odberg
Jøss! Betyr det at denne sjekken gjøres i mreg-cli, men ikke i API'et?

mreg> host cname_add  fangorn floffa                                                      
OK: : Added cname alias floffa.uio.no for fangorn.uio.no
mreg> host add -ip 129.240.202.0/ floffa                                                  
WARNING: : the name is already in use by a cname
mreg> 

(Forresten, dette burde vært en ERROR, ikke WARNING. Ting som ikke blir utført bør gi ERROR).

mikaeld
enig, warning gir en antydning om at det ble utført selv om det ikke er optimalt å gjøre det

amedov
Må ærlig innrømme at det aldri falt meg inn å gjøre en slik sjekk i virtprov, da jeg trodde man alltid kunne stole på at det der ikke kunne skje. Men det gir mening med tanke på interne logikken

amedov
For det gir jo mening at mreg tillater noe sånt i APIet, felt i A-records-tabellen er jo helt separat fra C-records-tabellen
Men tydeligvis burde de to ha kobling i navnfelta så det aldri kan eksistere to felt som er like

odberg
Hm, nei, jeg synes ikke at det gir mening at mreg tillater at man gjør operasjoner via API'et som vil gi ugyldig sonefil.

amedov
Jeg forstår jo at det ikke gir mening for DNS, og tydeligvis er dette enten en bug eller feil implementasjon

amedov
Ser jo ut som sjekken blir gjort:

elif Host.objects.filter(name=data['name']).exists():

Here is a test that reproduces the issue:

diff --git a/mreg/api/v1/tests/tests.py b/mreg/api/v1/tests/tests.py
index 8e277b7..0a2f9d2 100644
--- a/mreg/api/v1/tests/tests.py
+++ b/mreg/api/v1/tests/tests.py
@@ -453,6 +453,14 @@ class APIHostsTestCase(MregAPITestCase):
         """"Posting a new host with a name already in use should return 409"""
         self.assert_post_and_409('/hosts/', self.post_data_name)
 
+    def test_hosts_post_409_conflict_cname(self):
+        """"Posting a new host with a CNAME already in use should return 409"""
+        post_cname = {'name': 'new-name2.example.org',
+                      'host': self.host_two.id,
+                      'ttl': 5000}
+        self.assert_post('/cnames/', post_cname)
+        self.assert_post_and_409('/hosts/', self.post_data)
+
     def test_hosts_patch_204_no_content(self):
         """Patching an existing and valid entry should return 204 and Location"""
         response = self.assert_patch_and_204('/hosts/%s' % self.host_one.name, self.patch_data)

I tried naively patching HostSerializer to check for a CNAME conflict:

    def validate(self, data):
        data = super().validate(data)
        name = data.get('name')
        if name and Cname.objects.filter(name=name).exists():
            raise serializers.ValidationError(
                "CNAME record already exists with this name")
        return data

...which kind of works, but gives 400 Bad Request instead of 409 Conflict as demanded by API_hosts.md.

Not sure how to entice a 409 response here. Thoughts?

Embarrassing bug. Though API_hosts.md status 409 for Conflicts, it is severly outdated...

DRF returns 400 when validation fails, as there can be multiple errors and therefore the generic 400 is chosen.

A simple un-tested fix for the 400 -> 409 error code is:

err = serializers.ValidationError("blah")
err.status_code = status.HTTP_409_CONFLICT
raise err

Thanks for the hint. It looks like it is not supported to attach status_code to a ValidationError (though it can probably be implemented).

I sent a potential fix in #418 which does this check in the view instead of the serializer.