Handle CNAME best effort

This commit is contained in:
nemunaire 2020-05-10 12:27:03 +02:00
parent e6edb443c7
commit 35c94bcd22
3 changed files with 217 additions and 41 deletions

View File

@ -34,34 +34,51 @@
<template>
<div v-if="!isLoading" class="pt-3">
<div v-for="(dn, index) in sortedDomains" :key="index">
<h2 :id="dn" :class="index !== 0 ? 'mt-4' : ''">
<b-icon v-if="hideDomain[index]" icon="chevron-right" @click="toogleHideDomain(index)" />
<b-icon v-if="!hideDomain[index]" icon="chevron-down" @click="toogleHideDomain(index)" />
<span class="text-monospace" @click="toogleHideDomain(index)">{{ dn }}</span>
<a :href="'#' + dn" class="float-right">
<b-icon icon="link45deg" />
</a>
<b-badge class="ml-2" v-if="myServices.aliases && myServices.aliases[dn]" v-b-popover.hover.focus="{ customClass: 'text-monospace', html: true, content: myServices.aliases[dn].map(function(alias) { return escapeHTML(alias) }).join('<br>') }">+ {{ myServices.aliases[dn].length }} aliases</b-badge>
<b-button type="button" variant="primary" size="sm" class="ml-2">
<b-icon icon="plus" />
Add a service
</b-button>
<b-button type="button" variant="outline-primary" size="sm" class="ml-2">
<div v-if="isCNAME(dn)">
<h2 :id="dn" :class="index !== 0 ? 'mt-4' : ''">
<b-icon icon="link" />
Add an alias
</b-button>
</h2>
<b-list-group v-show="!hideDomain[index]" v-for="(svc, idx) in myServices.services[dn]" :key="idx">
<b-list-group-item @click="toogleRR(index, idx)" button>
<strong>{{ services[svc._svctype].name }}</strong> <span v-if="svc._comment" class="text-muted">{{ svc._comment }}</span>
<span class="text-muted" v-if="services[svc._svctype].comment">{{ services[svc._svctype].comment }}</span>
<b-badge v-for="(categorie, idcat) in services[svc._svctype].categories" :key="idcat" variant="gray" class="float-right ml-1">{{ categorie }}</b-badge>
<b-badge v-if="svc._tmp_hint_nb && svc._tmp_hint_nb > 1" variant="danger" class="float-right ml-1">{{ svc._tmp_hint_nb }}</b-badge>
</b-list-group-item>
<b-list-group-item v-if="expandrrs['' + index + '.' + idx]">
{{ svc }}
</b-list-group-item>
</b-list-group>
<span class="text-monospace">{{ dn }}</span>
<a :href="'#' + dn" class="float-right">
<b-icon icon="link45deg" />
</a>
<b-icon icon="arrow-right" />
<span class="text-monospace">{{ myServices.services[dn][0].Service.Target }}</span>
<b-button type="button" variant="outline-danger" size="sm" class="ml-2">
<b-icon icon="x-circle" />
Drop alias
</b-button>
</h2>
</div>
<div v-else>
<h2 :id="dn" :class="index !== 0 ? 'mt-4' : ''">
<b-icon v-if="hideDomain[index]" icon="chevron-right" @click="toogleHideDomain(index)" />
<b-icon v-if="!hideDomain[index]" icon="chevron-down" @click="toogleHideDomain(index)" />
<span class="text-monospace" @click="toogleHideDomain(index)">{{ dn }}</span>
<a :href="'#' + dn" class="float-right">
<b-icon icon="link45deg" />
</a>
<b-badge class="ml-2" v-if="myServices.aliases && myServices.aliases[dn]" v-b-popover.hover.focus="{ customClass: 'text-monospace', html: true, content: myServices.aliases[dn].map(function(alias) { return escapeHTML(alias) }).join('<br>') }">+ {{ myServices.aliases[dn].length }} aliases</b-badge>
<b-button type="button" variant="primary" size="sm" class="ml-2">
<b-icon icon="plus" />
Add a service
</b-button>
<b-button type="button" variant="outline-primary" size="sm" class="ml-2">
<b-icon icon="link" />
Add an alias
</b-button>
</h2>
<b-list-group v-show="!hideDomain[index]" v-for="(svc, idx) in myServices.services[dn]" :key="idx">
<b-list-group-item @click="toogleRR(index, idx)" button>
<strong>{{ services[svc._svctype].name }}</strong> <span v-if="svc._comment" class="text-muted">{{ svc._comment }}</span>
<span class="text-muted" v-if="services[svc._svctype].comment">{{ services[svc._svctype].comment }}</span>
<b-badge v-for="(categorie, idcat) in services[svc._svctype].categories" :key="idcat" variant="gray" class="float-right ml-1">{{ categorie }}</b-badge>
<b-badge v-if="svc._tmp_hint_nb && svc._tmp_hint_nb > 1" variant="danger" class="float-right ml-1">{{ svc._tmp_hint_nb }}</b-badge>
</b-list-group-item>
<b-list-group-item v-if="expandrrs['' + index + '.' + idx]">
{{ svc }}
</b-list-group-item>
</b-list-group>
</div>
</div>
</div>
</template>
@ -162,6 +179,10 @@ export default {
}
},
isCNAME (dn) {
return this.myServices.services[dn].length === 1 && this.myServices.services[dn][0]._svctype === 'git.happydns.org/happydns/services/CNAME'
},
pullDomain () {
axios
.post('/api/domains/' + this.domain.domain + '/analyze')

164
services/alias.go Normal file
View File

@ -0,0 +1,164 @@
// Copyright or © or Copr. happyDNS (2020)
//
// contact@happydns.org
//
// This software is a computer program whose purpose is to provide a modern
// interface to interact with DNS systems.
//
// This software is governed by the CeCILL license under French law and abiding
// by the rules of distribution of free software. You can use, modify and/or
// redistribute the software under the terms of the CeCILL license as
// circulated by CEA, CNRS and INRIA at the following URL
// "http://www.cecill.info".
//
// As a counterpart to the access to the source code and rights to copy, modify
// and redistribute granted by the license, users are provided only with a
// limited warranty and the software's author, the holder of the economic
// rights, and the successive licensors have only limited liability.
//
// In this respect, the user's attention is drawn to the risks associated with
// loading, using, modifying and/or developing or reproducing the software by
// the user in light of its specific status of free software, that may mean
// that it is complicated to manipulate, and that also therefore means that it
// is reserved for developers and experienced professionals having in-depth
// computer knowledge. Users are therefore encouraged to load and test the
// software's suitability as regards their requirements in conditions enabling
// the security of their systems and/or data to be ensured and, more generally,
// to use and operate it in the same conditions as regards security.
//
// The fact that you are presently reading this means that you have had
// knowledge of the CeCILL license and that you accept its terms.
package svcs
import (
"fmt"
"strings"
"github.com/miekg/dns"
"git.happydns.org/happydns/model"
)
type CNAME struct {
Target string
}
func (s *CNAME) GetNbResources() int {
return 1
}
func (s *CNAME) GenComment(origin string) string {
return strings.TrimSuffix(s.Target, "."+origin)
}
func (s *CNAME) GenRRs(domain string, ttl uint32) (rrs []dns.RR) {
rrs = append(rrs, &dns.CNAME{
Hdr: dns.RR_Header{
Name: domain,
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
Ttl: ttl,
},
Target: s.Target,
})
return
}
type SpecialCNAME struct {
SubDomain string
Target string
}
func (s *SpecialCNAME) GetNbResources() int {
return 1
}
func (s *SpecialCNAME) GenComment(origin string) string {
return "(" + s.SubDomain + ") -> " + strings.TrimSuffix(s.Target, "."+origin)
}
func (s *SpecialCNAME) GenRRs(domain string, ttl uint32) (rrs []dns.RR) {
rrs = append(rrs, &dns.CNAME{
Hdr: dns.RR_Header{
Name: s.SubDomain + "." + domain,
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
Ttl: ttl,
},
Target: s.Target,
})
return
}
func specialalias_analyze(a *Analyzer) error {
// Try handle specials domains using CNAME
for _, record := range a.searchRR(AnalyzerRecordFilter{Type: dns.TypeCNAME, Prefix: "_"}) {
subdomains := SRV_DOMAIN.FindStringSubmatch(record.Header().Name)
if cname, ok := record.(*dns.CNAME); len(subdomains) == 4 && ok {
a.useRR(record, subdomains[3], &SpecialCNAME{
SubDomain: fmt.Sprintf("_%s._%s", subdomains[1], subdomains[2]),
Target: cname.Target,
})
}
}
return nil
}
func alias_analyze(a *Analyzer) error {
for _, record := range a.searchRR(AnalyzerRecordFilter{Type: dns.TypeCNAME}) {
if cname, ok := record.(*dns.CNAME); ok {
var newrr happydns.Service
if _, ok := a.services[cname.Target]; ok {
a.aliases[cname.Target] = append(a.aliases[cname.Target], record.Header().Name)
// Don't add extra domain in list if CNAME is the only one listed for it
if _, ok := a.services[record.Header().Name]; ok {
newrr = &CNAME{
Target: cname.Target,
}
}
} else {
newrr = &CNAME{
Target: cname.Target,
}
}
a.useRR(record, cname.Header().Name, newrr)
}
}
return nil
}
func init() {
RegisterService(
"git.happydns.org/happydns/services/SpecialCNAME",
func() happydns.Service {
return &CNAME{}
},
specialalias_analyze,
ServiceInfos{
Name: "SubAlias",
Description: "This is a service alias to another domain/service",
Categories: []string{
"internal",
},
},
99999997,
)
RegisterService(
"git.happydns.org/happydns/services/CNAME",
func() happydns.Service {
return &CNAME{}
},
alias_analyze,
ServiceInfos{
Name: "Alias",
Description: "This is an alias to another domain",
Categories: []string{
"internal",
},
},
99999998,
)
}

View File

@ -90,6 +90,11 @@ func (a *Analyzer) useRR(rr dns.RR, domain string, svc happydns.Service) error {
return errors.New("Record not found.")
}
// svc nil, just drop the record from the zone (probably handle another way)
if svc == nil {
return nil
}
for _, service := range a.services[domain] {
if service.Service == svc {
service.Comment = svc.GenComment(a.origin)
@ -140,20 +145,6 @@ func AnalyzeZone(origin string, zone []dns.RR) (svcs map[string][]*happydns.Serv
continue
}
// Handle CNAME as Aliases
if record.Header().Rrtype == dns.TypeCNAME {
// Ignore in zone CNAMEs
if cname, ok := record.(*dns.CNAME); ok {
if strings.HasSuffix(cname.Target, origin) {
a.aliases[cname.Target] = append(a.aliases[cname.Target], record.Header().Name)
if _, ok := a.services[cname.Target]; ok {
continue
}
}
}
}
orphan := &Orphan{record}
svcs[record.Header().Name] = append(svcs[record.Header().Name], &happydns.ServiceCombined{orphan, happydns.ServiceType{
Type: reflect.Indirect(reflect.ValueOf(orphan)).Type().PkgPath() + "/" + reflect.Indirect(reflect.ValueOf(orphan)).Type().Name(),