mirror of
https://github.com/google/nomulus.git
synced 2025-07-23 19:20:44 +02:00
Fix in-baliwick nameserver check bug in CloudDnsWriter
In publishDomain, we load the subordinate hosts of the domain from datastore and compare its nameservers to them. For any nameserver that is in-baliwick, we call publishSubordinateHost on it and stage the A/AAAA records of the host for publication. This is superior to the old approach where we use hostName.endsWith(domainName) to check if a nameserver is in-baliwick because it will mistake ns.another-example.tld as a subordinate host of example.tld. It is also better than checking hostName.endsWith("." + domainName), which will catch false positives as above, but falls short in a corner case where the nameserver has been deleted before its superordinate domain's record is updated. In that case, subordinateHosts.cotains(hostName) will be false but hostName.endsWith("." + domainName) will still be true. Note that we still use the suffix check in filterGlueRecords because it is filtering on existing records from Cloud DNS. It is even advantageous to do so because if there were (and there shouldn't be if everything is consistent) any orphaned glue records (suffix matches to the domain, but not actually in its subordinate host list), they would be retained by the filter and therefore be deleted when the staged changes are committed. Also fixed a few tests that should have failed had we checked subrodinate hosts.... ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=184732005
This commit is contained in:
parent
55dcf8e062
commit
1227046bcb
2 changed files with 56 additions and 34 deletions
|
@ -67,8 +67,8 @@ import org.joda.time.Duration;
|
||||||
public class CloudDnsWriter extends BaseDnsWriter {
|
public class CloudDnsWriter extends BaseDnsWriter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the dns writer, as used in {@code Registry.dnsWriter}. Remember to change
|
* The name of the dns writer, as used in {@code Registry.dnsWriter}. Remember to change the value
|
||||||
* the value on affected Registry objects to prevent runtime failures.
|
* on affected Registry objects to prevent runtime failures.
|
||||||
*/
|
*/
|
||||||
public static final String NAME = "CloudDnsWriter";
|
public static final String NAME = "CloudDnsWriter";
|
||||||
|
|
||||||
|
@ -153,16 +153,16 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Construct NS records (if any).
|
// Construct NS records (if any).
|
||||||
Set<String> nameserverData = domainResource.get().loadNameserverFullyQualifiedHostNames();
|
Set<String> nameserverData = domainResource.get().loadNameserverFullyQualifiedHostNames();
|
||||||
|
Set<String> subordinateHosts = domainResource.get().getSubordinateHosts();
|
||||||
if (!nameserverData.isEmpty()) {
|
if (!nameserverData.isEmpty()) {
|
||||||
HashSet<String> nsRrData = new HashSet<>();
|
HashSet<String> nsRrData = new HashSet<>();
|
||||||
for (String hostName : nameserverData) {
|
for (String hostName : nameserverData) {
|
||||||
nsRrData.add(getAbsoluteHostName(hostName));
|
nsRrData.add(getAbsoluteHostName(hostName));
|
||||||
|
|
||||||
// Construct glue records for subordinate NS hostnames (if any)
|
// Construct glue records for subordinate NS hostnames (if any)
|
||||||
if (hostName.endsWith(domainName)) {
|
if (subordinateHosts.contains(hostName)) {
|
||||||
publishSubordinateHost(hostName);
|
publishSubordinateHost(hostName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,19 +285,15 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||||
logger.info("Wrote to Cloud DNS");
|
logger.info("Wrote to Cloud DNS");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the glue records for in-bailiwick nameservers for the given domain+records. */
|
||||||
* Returns the glue records for in-bailiwick nameservers for the given domain+records.
|
|
||||||
*/
|
|
||||||
private Stream<String> filterGlueRecords(String domainName, Stream<ResourceRecordSet> records) {
|
private Stream<String> filterGlueRecords(String domainName, Stream<ResourceRecordSet> records) {
|
||||||
return records
|
return records
|
||||||
.filter(record -> record.getType().equals("NS"))
|
.filter(record -> record.getType().equals("NS"))
|
||||||
.flatMap(record -> record.getRrdatas().stream())
|
.flatMap(record -> record.getRrdatas().stream())
|
||||||
.filter(hostName -> hostName.endsWith(domainName) && !hostName.equals(domainName));
|
.filter(hostName -> hostName.endsWith("." + domainName) && !hostName.equals(domainName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Mutate the zone with the provided {@code desiredRecords}. */
|
||||||
* Mutate the zone with the provided {@code desiredRecords}.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void mutateZone(ImmutableMap<String, ImmutableSet<ResourceRecordSet>> desiredRecords) {
|
void mutateZone(ImmutableMap<String, ImmutableSet<ResourceRecordSet>> desiredRecords) {
|
||||||
// Fetch all existing records for names that this writer is trying to modify
|
// Fetch all existing records for names that this writer is trying to modify
|
||||||
|
|
|
@ -110,7 +110,6 @@ public class CloudDnsWriterTest {
|
||||||
return listResourceRecordSetsRequest;
|
return listResourceRecordSetsRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
createTld("tld");
|
createTld("tld");
|
||||||
|
@ -133,8 +132,7 @@ public class CloudDnsWriterTest {
|
||||||
when(dnsConnection.changes()).thenReturn(changes);
|
when(dnsConnection.changes()).thenReturn(changes);
|
||||||
when(dnsConnection.resourceRecordSets()).thenReturn(resourceRecordSets);
|
when(dnsConnection.resourceRecordSets()).thenReturn(resourceRecordSets);
|
||||||
when(resourceRecordSets.list(anyString(), anyString()))
|
when(resourceRecordSets.list(anyString(), anyString()))
|
||||||
.thenAnswer(
|
.thenAnswer(invocationOnMock -> newListResourceRecordSetsRequestMock());
|
||||||
invocationOnMock -> newListResourceRecordSetsRequestMock());
|
|
||||||
when(changes.create(anyString(), zoneNameCaptor.capture(), changeCaptor.capture()))
|
when(changes.create(anyString(), zoneNameCaptor.capture(), changeCaptor.capture()))
|
||||||
.thenReturn(createChangeRequest);
|
.thenReturn(createChangeRequest);
|
||||||
// Change our stub zone when a request to change the records is executed
|
// Change our stub zone when a request to change the records is executed
|
||||||
|
@ -165,6 +163,22 @@ public class CloudDnsWriterTest {
|
||||||
assertThat(stubZone).containsExactlyElementsIn(expectedRecords);
|
assertThat(stubZone).containsExactlyElementsIn(expectedRecords);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a a zone cut with records for a domain and given nameservers, with no glue records. */
|
||||||
|
private static ImmutableSet<ResourceRecordSet> fakeDomainRecords(
|
||||||
|
String domainName, String... nameservers) {
|
||||||
|
ImmutableSet.Builder<ResourceRecordSet> recordSetBuilder = new ImmutableSet.Builder<>();
|
||||||
|
if (nameservers.length > 0) {
|
||||||
|
recordSetBuilder.add(
|
||||||
|
new ResourceRecordSet()
|
||||||
|
.setKind("dns#resourceRecordSet")
|
||||||
|
.setType("NS")
|
||||||
|
.setName(domainName + ".")
|
||||||
|
.setTtl(222)
|
||||||
|
.setRrdatas(ImmutableList.copyOf(nameservers)));
|
||||||
|
}
|
||||||
|
return recordSetBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns a a zone cut with records for a domain */
|
/** Returns a a zone cut with records for a domain */
|
||||||
private static ImmutableSet<ResourceRecordSet> fakeDomainRecords(
|
private static ImmutableSet<ResourceRecordSet> fakeDomainRecords(
|
||||||
String domainName,
|
String domainName,
|
||||||
|
@ -337,13 +351,13 @@ public class CloudDnsWriterTest {
|
||||||
@Test
|
@Test
|
||||||
public void testLoadDomain_withInBailiwickNs_IPv4() throws Exception {
|
public void testLoadDomain_withInBailiwickNs_IPv4() throws Exception {
|
||||||
persistResource(
|
persistResource(
|
||||||
fakeDomain(
|
fakeDomain(
|
||||||
"example.tld",
|
"example.tld",
|
||||||
ImmutableSet.of(persistResource(fakeHost("0.ip4.example.tld", IPv4))),
|
ImmutableSet.of(persistResource(fakeHost("0.ip4.example.tld", IPv4))),
|
||||||
0))
|
0)
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.addSubordinateHost("0.ip4.example.tld")
|
.addSubordinateHost("0.ip4.example.tld")
|
||||||
.build();
|
.build());
|
||||||
writer.publishDomain("example.tld");
|
writer.publishDomain("example.tld");
|
||||||
|
|
||||||
verifyZone(fakeDomainRecords("example.tld", 1, 0, 0, 0));
|
verifyZone(fakeDomainRecords("example.tld", 1, 0, 0, 0));
|
||||||
|
@ -352,18 +366,30 @@ public class CloudDnsWriterTest {
|
||||||
@Test
|
@Test
|
||||||
public void testLoadDomain_withInBailiwickNs_IPv6() throws Exception {
|
public void testLoadDomain_withInBailiwickNs_IPv6() throws Exception {
|
||||||
persistResource(
|
persistResource(
|
||||||
fakeDomain(
|
fakeDomain(
|
||||||
"example.tld",
|
"example.tld",
|
||||||
ImmutableSet.of(persistResource(fakeHost("0.ip6.example.tld", IPv6))),
|
ImmutableSet.of(persistResource(fakeHost("0.ip6.example.tld", IPv6))),
|
||||||
0))
|
0)
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.addSubordinateHost("0.ip6.example.tld")
|
.addSubordinateHost("0.ip6.example.tld")
|
||||||
.build();
|
.build());
|
||||||
writer.publishDomain("example.tld");
|
writer.publishDomain("example.tld");
|
||||||
|
|
||||||
verifyZone(fakeDomainRecords("example.tld", 0, 1, 0, 0));
|
verifyZone(fakeDomainRecords("example.tld", 0, 1, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoadDomain_withNameserveThatEndsWithDomainName() throws Exception {
|
||||||
|
persistResource(
|
||||||
|
fakeDomain(
|
||||||
|
"example.tld",
|
||||||
|
ImmutableSet.of(persistResource(fakeHost("ns.another-example.tld", IPv4))),
|
||||||
|
0));
|
||||||
|
writer.publishDomain("example.tld");
|
||||||
|
|
||||||
|
verifyZone(fakeDomainRecords("example.tld", "ns.another-example.tld."));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoadHost_externalHost() throws Exception {
|
public void testLoadHost_externalHost() throws Exception {
|
||||||
writer.publishHost("ns1.example.com");
|
writer.publishHost("ns1.example.com");
|
||||||
|
@ -380,13 +406,13 @@ public class CloudDnsWriterTest {
|
||||||
// Model the domain with only one NS record -- this is equivalent to creating it
|
// Model the domain with only one NS record -- this is equivalent to creating it
|
||||||
// with two NS records and then deleting one
|
// with two NS records and then deleting one
|
||||||
persistResource(
|
persistResource(
|
||||||
fakeDomain(
|
fakeDomain(
|
||||||
"example.tld",
|
"example.tld",
|
||||||
ImmutableSet.of(persistResource(fakeHost("0.ip4.example.tld", IPv4))),
|
ImmutableSet.of(persistResource(fakeHost("0.ip4.example.tld", IPv4))),
|
||||||
0))
|
0)
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.addSubordinateHost("0.ip4.example.tld")
|
.addSubordinateHost("0.ip4.example.tld")
|
||||||
.build();
|
.build());
|
||||||
|
|
||||||
// Ask the writer to delete the deleted NS record and glue
|
// Ask the writer to delete the deleted NS record and glue
|
||||||
writer.publishHost("1.ip4.example.tld");
|
writer.publishHost("1.ip4.example.tld");
|
||||||
|
@ -456,13 +482,13 @@ public class CloudDnsWriterTest {
|
||||||
// In publishing DNS records, we can end up publishing information on the same host twice
|
// In publishing DNS records, we can end up publishing information on the same host twice
|
||||||
// (through a domain change and a host change), so this scenario needs to work.
|
// (through a domain change and a host change), so this scenario needs to work.
|
||||||
persistResource(
|
persistResource(
|
||||||
fakeDomain(
|
fakeDomain(
|
||||||
"example.tld",
|
"example.tld",
|
||||||
ImmutableSet.of(persistResource(fakeHost("0.ip4.example.tld", IPv4))),
|
ImmutableSet.of(persistResource(fakeHost("0.ip4.example.tld", IPv4))),
|
||||||
0))
|
0)
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.addSubordinateHost("ns1.example.tld")
|
.addSubordinateHost("0.ip4.example.tld")
|
||||||
.build();
|
.build());
|
||||||
writer.publishDomain("example.tld");
|
writer.publishDomain("example.tld");
|
||||||
writer.publishHost("0.ip4.example.tld");
|
writer.publishHost("0.ip4.example.tld");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue