mirror of
https://github.com/google/nomulus.git
synced 2025-08-05 17:28:25 +02:00
Add a new reservation type to support nameserver restrictions
A new field (allowedNameservers) is added to ReservedListEntry that stores the allow nameservers for the label. The field itself is a comma separated string, but the actual lines within a reserved list file (from which the field is parsed) uses colon to separate nameservers, to avoid conflicting with the commas used as primary separators in a CSV file. Combined with upcoming update(s) that enables locking down an entire TLD to only delegate domains with a nameserver restricted reservation type, this change will enable us to restrict domain delegation to nameservers specifically specified in the allowed nameservers list, in order to prevent malicious delegation in case the registrar for a brand TLD is compromised. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=149989330
This commit is contained in:
parent
5253f6fd6b
commit
c426a80563
7 changed files with 208 additions and 42 deletions
|
@ -22,9 +22,11 @@ import static google.registry.model.registry.label.DomainLabelMetrics.reservedLi
|
|||
import static google.registry.model.registry.label.ReservationType.ALLOWED_IN_SUNRISE;
|
||||
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
|
||||
import static google.registry.model.registry.label.ReservationType.MISTAKEN_PREMIUM;
|
||||
import static google.registry.model.registry.label.ReservationType.NAMESERVER_RESTRICTED;
|
||||
import static google.registry.model.registry.label.ReservationType.NAME_COLLISION;
|
||||
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT;
|
||||
import static google.registry.model.registry.label.ReservationType.UNRESERVED;
|
||||
import static google.registry.model.registry.label.ReservedList.getAllowedNameservers;
|
||||
import static google.registry.model.registry.label.ReservedList.getReservationTypes;
|
||||
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
|
||||
import static google.registry.monitoring.metrics.contrib.EventMetricSubject.assertThat;
|
||||
|
@ -163,22 +165,59 @@ public class ReservedListTest {
|
|||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllowedNameservers() throws Exception {
|
||||
ReservedList rl1 =
|
||||
persistReservedList(
|
||||
"reserved1",
|
||||
"lol,NAMESERVER_RESTRICTED,ns1.nameserver.com",
|
||||
"lol1,NAMESERVER_RESTRICTED,ns1.nameserver.com:ns2.domain.tld:ns3.domain.tld",
|
||||
"lol2,NAMESERVER_RESTRICTED,ns.name.tld # This is a comment");
|
||||
ReservedList rl2 =
|
||||
persistReservedList(
|
||||
"reserved2",
|
||||
"lol1,NAMESERVER_RESTRICTED,ns3.nameserver.com:ns2.domain.tld:ns3.domain.tld",
|
||||
"lol2,NAMESERVER_RESTRICTED,ns3.nameserver.com:ns4.domain.tld",
|
||||
"lol3,NAMESERVER_RESTRICTED,ns3.nameserver.com");
|
||||
ReservedList rl3 =
|
||||
persistReservedList(
|
||||
"reserved3", "lol1,NAMESERVER_RESTRICTED,ns3.domain.tld", "lol4,ALLOWED_IN_SUNRISE");
|
||||
persistResource(Registry.get("tld").asBuilder().setReservedLists(rl1, rl2, rl3).build());
|
||||
assertThat(getReservationTypes("lol", "tld")).containsExactly(NAMESERVER_RESTRICTED);
|
||||
assertThat(getReservationTypes("lol1", "tld")).containsExactly(NAMESERVER_RESTRICTED);
|
||||
assertThat(getReservationTypes("lol2", "tld")).containsExactly(NAMESERVER_RESTRICTED);
|
||||
assertThat(getReservationTypes("lol3", "tld")).containsExactly(NAMESERVER_RESTRICTED);
|
||||
assertThat(getAllowedNameservers(InternetDomainName.from("lol.tld")))
|
||||
.containsExactly("ns1.nameserver.com");
|
||||
assertThat(getAllowedNameservers(InternetDomainName.from("lol1.tld")))
|
||||
.containsExactly("ns3.domain.tld");
|
||||
assertThat(getAllowedNameservers(InternetDomainName.from("lol2.tld"))).isEmpty();
|
||||
assertThat(getAllowedNameservers(InternetDomainName.from("lol3.tld")))
|
||||
.containsExactly("ns3.nameserver.com");
|
||||
assertThat(getAllowedNameservers(InternetDomainName.from("lol4.tld"))).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchesAnchorTenantReservation_falseOnOtherReservationTypes() throws Exception {
|
||||
persistResource(Registry.get("tld").asBuilder()
|
||||
.setReservedLists(ImmutableSet.of(
|
||||
persistReservedList(
|
||||
"reserved2",
|
||||
"lol,FULLY_BLOCKED",
|
||||
"lol2,NAME_COLLISION",
|
||||
"lol3,MISTAKEN_PREMIUM",
|
||||
"lol4,ALLOWED_IN_SUNRISE")))
|
||||
.build());
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
.setReservedLists(
|
||||
ImmutableSet.of(
|
||||
persistReservedList(
|
||||
"reserved2",
|
||||
"lol,FULLY_BLOCKED",
|
||||
"lol2,NAME_COLLISION",
|
||||
"lol3,MISTAKEN_PREMIUM",
|
||||
"lol4,ALLOWED_IN_SUNRISE",
|
||||
"lol5,NAMESERVER_RESTRICTED,na1.domain.tld")))
|
||||
.build());
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol2.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol3.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol4.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol5.tld"), "")).isFalse();
|
||||
assertThat(matchesAnchorTenantReservation(InternetDomainName.from("lol6.tld"), "")).isFalse();
|
||||
assertThat(reservedListChecks)
|
||||
.hasValueForLabels(1, "tld", "1", "reserved2", FULLY_BLOCKED.toString())
|
||||
.and()
|
||||
|
@ -188,6 +227,8 @@ public class ReservedListTest {
|
|||
.and()
|
||||
.hasValueForLabels(1, "tld", "1", "reserved2", ALLOWED_IN_SUNRISE.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "1", "reserved2", NAMESERVER_RESTRICTED.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
|
@ -200,6 +241,8 @@ public class ReservedListTest {
|
|||
.and()
|
||||
.hasAnyValueForLabels("tld", "1", "reserved2", ALLOWED_IN_SUNRISE.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "1", "reserved2", NAMESERVER_RESTRICTED.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "0", "(none)", UNRESERVED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
|
@ -212,6 +255,8 @@ public class ReservedListTest {
|
|||
.and()
|
||||
.hasValueForLabels(1, "tld", "reserved2", ALLOWED_IN_SUNRISE.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "reserved2", NAMESERVER_RESTRICTED.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
|
@ -435,9 +480,11 @@ public class ReservedListTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSave_passwordWithNonAnchorTenantReservation() throws Exception {
|
||||
thrown.expect(IllegalArgumentException.class,
|
||||
"Only anchor tenant reservations should have an auth code configured");
|
||||
public void testSave_additionalRestrictionWithIncompatibleReservationType() throws Exception {
|
||||
thrown.expect(
|
||||
IllegalArgumentException.class,
|
||||
"Only anchor tenant and nameserver restricted reservations "
|
||||
+ "should have restrictions imposed");
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
|
@ -446,6 +493,24 @@ public class ReservedListTest {
|
|||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSave_badNameservers_invalidSyntax() throws Exception {
|
||||
thrown.expect(IllegalArgumentException.class, "Not a valid domain name: 'ns@.domain.tld'");
|
||||
persistReservedList(
|
||||
"reserved1",
|
||||
"lol,NAMESERVER_RESTRICTED,ns1.domain.tld:ns2.domain.tld",
|
||||
"lol1,NAMESERVER_RESTRICTED,ns1.domain.tld:ns@.domain.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSave_badNameservers_tooFewPartsForHostname() throws Exception {
|
||||
thrown.expect(IllegalArgumentException.class, "domain.tld is not a valid nameserver hostname");
|
||||
persistReservedList(
|
||||
"reserved1",
|
||||
"lol,NAMESERVER_RESTRICTED,ns1.domain.tld:ns2.domain.tld",
|
||||
"lol1,NAMESERVER_RESTRICTED,ns1.domain.tld:domain.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSave_noPasswordWithAnchorTenantReservation() throws Exception {
|
||||
thrown.expect(IllegalArgumentException.class,
|
||||
|
@ -458,6 +523,19 @@ public class ReservedListTest {
|
|||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSave_noNameserversWithNameserverRestrictedReservation() throws Exception {
|
||||
thrown.expect(
|
||||
IllegalArgumentException.class,
|
||||
"Nameserver restricted reservations must have at least one nameserver configured");
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
.setReservedLists(
|
||||
ImmutableSet.of(persistReservedList("reserved1", "lol,NAMESERVER_RESTRICTED")))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_cannotIncludeDuplicateLabels() {
|
||||
ReservedList rl = new ReservedList.Builder().setName("blah").build();
|
||||
|
|
|
@ -748,6 +748,7 @@ enum google.registry.model.registry.label.ReservationType {
|
|||
ALLOWED_IN_SUNRISE;
|
||||
FULLY_BLOCKED;
|
||||
MISTAKEN_PREMIUM;
|
||||
NAMESERVER_RESTRICTED;
|
||||
NAME_COLLISION;
|
||||
RESERVED_FOR_ANCHOR_TENANT;
|
||||
UNRESERVED;
|
||||
|
@ -764,6 +765,7 @@ class google.registry.model.registry.label.ReservedList {
|
|||
class google.registry.model.registry.label.ReservedList$ReservedListEntry {
|
||||
@Id java.lang.String label;
|
||||
google.registry.model.registry.label.ReservationType reservationType;
|
||||
java.lang.String allowedNameservers;
|
||||
java.lang.String authCode;
|
||||
java.lang.String comment;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue