custom-dns.html 12.5 KB
Newer Older
1
<style>
2 3 4
#custom-dns-current td.long {
  word-break: break-all;
}
5 6
</style>

7
<h2>Custom DNS</h2>
8

9
<p class="text-warning">This is an advanced configuration page.</p>
10

11 12
<p>It is possible to set custom DNS records on domains hosted here.</p>

13
<h3>Set custom DNS records</h3>
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

<p>You can set additional DNS records, such as if you have a website running on another server, to add DKIM records for external mail providers, or for various confirmation-of-ownership tests.</p>

<form class="form-horizontal" role="form" onsubmit="do_set_custom_dns(); return false;">
  <div class="form-group">
    <label for="customdnsQname" class="col-sm-1 control-label">Name</label>
    <div class="col-sm-10">
      <table style="max-width: 400px">
      <tr><td>
        <input type="text" class="form-control" id="customdnsQname" placeholder="subdomain">
      </td><td style="padding: 0 1em; font-weight: bold;">.</td><td>
        <select id="customdnsZone" class="form-control"> </select>
      </td></tr></table>
      <div class="text-info" style="margin-top: .5em">Leave the left field blank to set a record on the chosen domain name, or enter a subdomain.</div>
    </div>
  </div>
  <div class="form-group">
    <label for="customdnsType" class="col-sm-1 control-label">Type</label>
    <div class="col-sm-10">
      <select id="customdnsType" class="form-control" style="max-width: 400px" onchange="show_customdns_rtype_hint()">
34 35
        <option value="A" data-hint="Enter an IPv4 address (i.e. a dotted quad, such as 123.456.789.012).  The 'local' alias sets the record to this box's public IPv4 address.">A (IPv4 address)</option>
        <option value="AAAA" data-hint="Enter an IPv6 address.  The 'local' alias sets the record to this box's public IPv6 address.">AAAA (IPv6 address)</option>
36
        <option value="CAA" data-hint="Enter a CA that can issue certificates for this domain in the form of FLAG TAG VALUE. (0 issuewild &quot;letsencrypt.org&quot;)">CAA (Certificate Authority Authorization)</option>
37 38
        <option value="CNAME" data-hint="Enter another domain name followed by a period at the end (e.g. mypage.github.io.).">CNAME (DNS forwarding)</option>
        <option value="TXT" data-hint="Enter arbitrary text.">TXT (text record)</option>
39 40
        <option value="MX" data-hint="Enter record in the form of PRIORITY DOMAIN., including trailing period (e.g. 20 mx.example.com.).">MX (mail exchanger)</option>
        <option value="SRV" data-hint="Enter record in the form of PRIORITY WEIGHT PORT TARGET., including trailing period (e.g. 10 10 5060 sip.example.com.).">SRV (service record)</option>
41
        <option value="SSHFP" data-hint="Enter record in the form of ALGORITHM TYPE FINGERPRINT.">SSHFP (SSH fingerprint record)</option>
42
        <option value="NS" data-hint="Enter a hostname to which this subdomain should be delegated to">NS (DNS subdomain delegation)</option>
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
      </select>
    </div>
  </div>
  <div class="form-group">
    <label for="customdnsValue" class="col-sm-1 control-label">Value</label>
    <div class="col-sm-10">
      <input type="text" class="form-control" id="customdnsValue" placeholder="">
      <div id="customdnsTypeHint" class="text-info" style="margin-top: .5em"></div>
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-offset-1 col-sm-11">
      <button type="submit" class="btn btn-primary">Set Record</button>
    </div>
  </div>
</form>

<table id="custom-dns-current" class="table" style="width: auto; display: none">
  <thead>
    <th>Domain Name</th>
    <th>Record Type</th>
    <th>Value</th>
    <th></th>
  </thead>
  <tbody>
    <tr><td colspan="4">Loading...</td></tr>
  </tbody>
</table>

72
<h3>Using a secondary nameserver</h3>
73

74
<p>If your TLD requires you to have two separate nameservers, you can either set up <a href="#" onclick="return show_panel('external_dns')">external DNS</a> and ignore the DNS server on this box entirely, or use the DNS server on this box but add a secondary (aka &ldquo;slave&rdquo;) nameserver.</p>
75
<p>If you choose to use a secondary nameserver, you must find a secondary nameserver service provider. Your domain name registrar or virtual cloud provider may provide this service for you. Once you set up the secondary nameserver service, enter the hostname (not the IP address) of <em>their</em> secondary nameserver in the box below.</p>
76 77 78 79 80 81 82 83 84 85 86 87 88

<form class="form-horizontal" role="form" onsubmit="do_set_secondary_dns(); return false;">
  <div class="form-group">
    <label for="secondarydnsHostname" class="col-sm-1 control-label">Hostname</label>
    <div class="col-sm-10">
      <input type="text" class="form-control" id="secondarydnsHostname" placeholder="ns1.cloudprovider.com">
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-offset-1 col-sm-11">
      <button type="submit" class="btn btn-primary">Update</button>
    </div>
  </div>
89
  <div class="form-group">
90
    <div class="col-sm-offset-1 col-sm-11">
91 92 93
      <p class="small">
        Multiple secondary servers can be separated with commas or spaces (i.e., <code>ns2.hostingcompany.com ns3.hostingcompany.com</code>). 
        To enable zone transfers to additional servers without listing them as secondary nameservers, add <code>xfr:IPADDRESS</code>.
94 95
      </p>
      <p id="secondarydns-clear-instructions" style="display: none" class="small">
96 97
        Clear the input field above and click Update to use this machine itself as secondary DNS, which is the default/normal setup.
      </p>
98 99 100 101
    </div>
  </div>
</form>

102

103 104 105
<h3>Custom DNS API</h3>

<p>Use your box&rsquo;s DNS API to set custom DNS records on domains hosted here. For instance, you can create your own dynamic DNS service.</p>
106

107
<p>Usage:</p>
108

109
<pre>curl -X <b>VERB</b> [-d "<b>value</b>"] --user {email}:{password} https://{{hostname}}/admin/dns/custom[/<b>qname</b>[/<b>rtype</b>]]</pre>
110

111 112 113 114 115 116 117 118 119 120 121 122 123
<p>(Brackets denote an optional argument.)</p>

<h4>Verbs</h4>

<table class="table">
<thead><th>Verb</th> <th>Usage</th></thead>
<tr><td>GET</td> <td>Returns matching custom DNS records as a JSON array of objects. Each object has the keys <code>qname</code>, <code>rtype</code>, and <code>value</code>. The optional <code>qname</code> and <code>rtype</code> parameters in the request URL filter the records returned in the response. The request body (<code>-d "..."</code>) must be omitted.</td></tr>
<tr><td>PUT</td> <td>Sets a custom DNS record replacing any existing records with the same <code>qname</code> and <code>rtype</code>. Use PUT (instead of POST) when you only have one value for a <code>qname</code> and <code>rtype</code>, such as typical <code>A</code> records (without round-robin).</td></tr>
<tr><td>POST</td> <td>Adds a new custom DNS record. Use POST when you have multiple <code>TXT</code> records or round-robin <code>A</code> records. (PUT would delete previously added records.)</td></tr>
<tr><td>DELETE</td> <td>Deletes custom DNS records. If the request body (<code>-d "..."</code>) is empty or omitted, deletes all records matching the <code>qname</code> and <code>rtype</code>. If the request body is present, deletes only the record matching the <code>qname</code>, <code>rtype</code> and value.</td></tr>
</table>

<h4>Parameters</h4>
124

125 126 127 128
<table class="table">
<thead><th>Parameter</th> <th>Value</th></thead>
<tr><td>email</td> <td>The email address of any administrative user here.</td></tr>
<tr><td>password</td> <td>That user&rsquo;s password.</td></tr>
129
<tr><td>qname</td> <td>The fully qualified domain name for the record you are trying to set. It must be one of the domain names or a subdomain of one of the domain names hosted on this box. (Add mail users or aliases to add new domains.)</td></tr>
130
<tr><td>rtype</td> <td>The resource type. Defaults to <code>A</code> if omitted. Possible values: <code>A</code> (an IPv4 address), <code>AAAA</code> (an IPv6 address), <code>TXT</code> (a text string), <code>CNAME</code> (an alias, which is a fully qualified domain name &mdash; don&rsquo;t forget the final period), <code>MX</code>, <code>SRV</code>, <code>SSHFP</code>, <code>CAA</code> or <code>NS</code>.</td></tr>
131
<tr><td>value</td> <td>For PUT, POST, and DELETE, the record&rsquo;s value. If the <code>rtype</code> is <code>A</code> or <code>AAAA</code> and <code>value</code> is empty or omitted, the IPv4 or IPv6 address of the remote host is used (be sure to use the <code>-4</code> or <code>-6</code> options to curl). This is handy for dynamic DNS!</td></tr>
132 133
</table>

134 135
<p>Strict <a href="http://tools.ietf.org/html/rfc4408">SPF</a> and <a href="https://datatracker.ietf.org/doc/draft-kucherawy-dmarc-base/?include_text=1">DMARC</a> records will be added to all custom domains unless you override them.</p>

136
<h4>Examples:</h4>
137

138 139
<p>Try these examples. For simplicity the examples omit the <code>--user me@mydomain.com:yourpassword</code> command line argument which you must fill in with your email address and password.</p>

140
<pre># sets laptop.mydomain.com to point to the IP address of the machine you are executing curl on
141
curl -X PUT https://{{hostname}}/admin/dns/custom/laptop.mydomain.com
142

143 144
# deletes that record and all A records for that domain name
curl -X DELETE https://{{hostname}}/admin/dns/custom/laptop.mydomain.com
145

146 147
# sets a CNAME alias
curl -X PUT -d "bar.mydomain.com." https://{{hostname}}/admin/dns/custom/foo.mydomain.com/cname
148

149 150
# deletes that CNAME and all CNAME records for that domain name
curl -X DELETE https://{{hostname}}/admin/dns/custom/foo.mydomain.com/cname
151

152 153
# adds a TXT record using POST to preserve any previous TXT records
curl -X POST -d "some text here" https://{{hostname}}/admin/dns/custom/foo.mydomain.com/txt
154

155 156
# deletes that one TXT record while preserving other TXT records
curl -X DELETE -d "some text here" https://{{hostname}}/admin/dns/custom/foo.mydomain.com/txt
157
</pre>
158

159
<script>
160
function show_custom_dns() {
161
  api(
162 163 164 165
    "/dns/secondary-nameserver",
    "GET",
    { },
    function(data) {
166 167
      $('#secondarydnsHostname').val(data.hostnames.join(' '));
      $('#secondarydns-clear-instructions').toggle(data.hostnames.length > 0);
168
    });
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

  api(
    "/dns/zones",
    "GET",
    { },
    function(data) {
      $('#customdnsZone').text('');
      for (var i = 0; i < data.length; i++) {
        $('#customdnsZone').append($('<option/>').text(data[i]));
      }
    });

  show_current_custom_dns();
  show_customdns_rtype_hint();
}

function show_current_custom_dns() {
  api(
187
    "/dns/custom",
188 189 190 191 192 193 194
    "GET",
    { },
    function(data) {
      if (data.length > 0)
        $('#custom-dns-current').fadeIn();
      else
        $('#custom-dns-current').fadeOut();
195

196 197 198 199 200 201
      $('#custom-dns-current').find("tbody").text('');
      for (var i = 0; i < data.length; i++) {
        var tr = $("<tr/>");
        $('#custom-dns-current').find("tbody").append(tr);
        tr.attr('data-qname', data[i].qname);
        tr.attr('data-rtype', data[i].rtype);
202
        tr.attr('data-value', data[i].value);
203 204 205 206 207 208 209 210 211 212 213
        tr.append($('<td class="long"/>').text(data[i].qname));
        tr.append($('<td/>').text(data[i].rtype));
        tr.append($('<td class="long"/>').text(data[i].value));
        tr.append($('<td>[<a href="#" onclick="return delete_custom_dns_record(this)">delete</a>]</td>'));
      }
    });
}

function delete_custom_dns_record(elem) {
  var qname = $(elem).parents('tr').attr('data-qname');
  var rtype = $(elem).parents('tr').attr('data-rtype');
214 215
  var value = $(elem).parents('tr').attr('data-value');
  do_set_custom_dns(qname, rtype, value, "DELETE");
216
  return false;
217 218 219 220 221 222 223
}

function do_set_secondary_dns() {
 api(
    "/dns/secondary-nameserver",
    "POST",
    {
224
      hostnames: $('#secondarydnsHostname').val()
225 226 227 228
    },
    function(data) {
      if (data == "") return; // nothing updated
      show_modal_error("Secondary DNS", $("<pre/>").text(data));
Joshua Tauberer's avatar
Joshua Tauberer committed
229
      $('#secondarydns-clear-instructions').slideDown();
230 231 232 233
    },
    function(err) {
      show_modal_error("Secondary DNS", $("<pre/>").text(err));
    });
234
}
235

236
function do_set_custom_dns(qname, rtype, value, method) {
237 238 239 240 241 242 243
  if (!qname) {
    if ($('#customdnsQname').val() != '')
      qname = $('#customdnsQname').val() + '.' + $('#customdnsZone').val();
    else
      qname = $('#customdnsZone').val();
    rtype = $('#customdnsType').val();
    value = $('#customdnsValue').val();
244
    method = 'POST';
245 246 247
  }

  api(
248 249 250
    "/dns/custom/" + qname + "/" + rtype,
    method,
    value,
251 252 253 254 255 256
    function(data) {
      if (data == "") return; // nothing updated
      show_modal_error("Custom DNS", $("<pre/>").text(data));
      show_current_custom_dns();
    },
    function(err) {
257
      show_modal_error("Custom DNS (Error)", $("<pre/>").text(err));
258 259 260 261 262 263
    });
}

function show_customdns_rtype_hint() {
  $('#customdnsTypeHint').text($("#customdnsType").find('option:selected').attr('data-hint'));
}
264
</script>