aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2022-12-03 15:58:18 -0800
committerMike Crute <mike@crute.us>2022-12-03 15:58:18 -0800
commitd4efff4950b6105f1d62362f8944a24659af4ea7 (patch)
tree2253164c433a60af411667d9c830040115f3bec8
parent220a2f4dab2296a5d79febceb18584772d96ac0e (diff)
downloaddockerfiles-d4efff4950b6105f1d62362f8944a24659af4ea7.tar.bz2
dockerfiles-d4efff4950b6105f1d62362f8944a24659af4ea7.tar.xz
dockerfiles-d4efff4950b6105f1d62362f8944a24659af4ea7.zip
bind: extract bind builder
-rw-r--r--bind/builder/go.mod7
-rw-r--r--bind/builder/go.sum4
-rw-r--r--bind/builder/main.go586
-rw-r--r--bind/builder/zones.yaml256
4 files changed, 0 insertions, 853 deletions
diff --git a/bind/builder/go.mod b/bind/builder/go.mod
deleted file mode 100644
index a04a401..0000000
--- a/bind/builder/go.mod
+++ /dev/null
@@ -1,7 +0,0 @@
1module test
2
3go 1.13
4
5require (
6 gopkg.in/yaml.v2 v2.2.2
7)
diff --git a/bind/builder/go.sum b/bind/builder/go.sum
deleted file mode 100644
index e936db1..0000000
--- a/bind/builder/go.sum
+++ /dev/null
@@ -1,4 +0,0 @@
1gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
4gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/bind/builder/main.go b/bind/builder/main.go
deleted file mode 100644
index aef3754..0000000
--- a/bind/builder/main.go
+++ /dev/null
@@ -1,586 +0,0 @@
1package main
2
3import (
4 "bytes"
5 "fmt"
6 "io/ioutil"
7 "log"
8 "os"
9 "os/exec"
10 "os/user"
11 "path/filepath"
12 "strconv"
13 "strings"
14 "syscall"
15 "text/template"
16
17 "gopkg.in/yaml.v2"
18)
19
20var zoneTemplate = template.Must(template.New("bind").Parse(`$ORIGIN .
21$TTL 86400 ; 1 day
22{{ .Zone.Name }} IN SOA {{ .Zone.PrimaryNS }} {{ .Zone.HostmasterEmail }} (
23 1 ; serial
24 604800 ; refresh (1 week)
25 86400 ; retry (1 day)
26 2419200 ; expire (4 weeks)
27 86400 ; minimum (1 day)
28 )
29{{ range .Zone.Nameservers }}
30 NS {{ . }}
31{{ end }}
32`))
33
34var cfgTemplate = template.Must(template.New("bind").Parse(`// vim:ft=named
35{{ range .Keys -}}
36key "{{ .Name }}" {
37 algorithm {{ .Algorithm }};
38 secret "{{ .KeyData }}";
39};
40{{ end }}
41
42{{ range $name, $value := .Acls -}}
43acl {{ $name }} {
44 {{ range $value -}}
45 {{ . }};
46 {{ end }}
47};
48{{ end }}
49
50{{ range .Views -}}
51view {{ .Name }} {
52 match-clients {
53 {{ range .MatchClients -}}
54 {{ . }};
55 {{ end }}
56 };
57
58 {{ .RawInclude }}
59
60 {{- if .Servers }}
61 {{ range .Servers -}}
62 server {{ .Address }} {
63 keys {{ .Key }};
64 };
65 {{- end }}
66 {{ end -}}
67
68 {{ if .NotifySource -}}notify-source {{ .NotifySource }};{{- end }}
69 {{ if .AlsoNotify -}}
70 also-notify {
71 {{- range .AlsoNotify }}
72 {{ .Address }}{{ if .Key }} key {{ .Key }}{{ end }};
73 {{- end }}
74 };
75 {{- end }}
76
77 include "/etc/bind/named.conf.default-zones";
78
79 {{ range .Zones }}
80 zone "{{ .Name }}" {
81 {{ if .InView -}}
82 in-view {{ .InView }};
83 {{- else -}}
84 type {{ .Type }};
85 {{ if .ForwardOnly }}forward only;{{ end }}
86 {{- if .File }}file "{{ .File }}";{{ end }}
87 {{ if .UpdateGrants -}}
88 update-policy {
89 {{- range .UpdateGrants }}
90 grant {{ . }} zonesub ANY;
91 {{- end }}
92 };
93 {{- end }}
94 {{- if .AllowQuery -}}
95 allow-query {
96 {{- range .AllowQuery }}
97 {{ . }};
98 {{- end }}
99 };
100 {{- end -}}
101 {{ if .Forwarders -}}
102 forwarders {
103 {{- range .Forwarders }}
104 {{ . }};
105 {{- end }}
106 };
107 {{- end }}
108 {{ if .Primaries -}}
109 masters {
110 {{- range .Primaries }}
111 {{ . }};
112 {{- end }}
113 };
114 {{- end -}}
115 {{- end }}
116 };
117 {{ end }}
118};
119{{ end }}
120`))
121
122type Server struct {
123 Address string
124 IPs []string `yaml:"ips"`
125 Type string `yaml:"type"`
126 Key *string `yaml:"key"`
127 Forwarders map[string][]string `yaml:"forwarders"`
128}
129
130type ServerMap map[string]*Server
131
132func (l *ServerMap) UnmarshalYAML(unmarshal func(v interface{}) error) error {
133 var rawData map[string]*Server
134 if err := unmarshal(&rawData); err != nil {
135 return err
136 }
137
138 for k, v := range rawData {
139 v.Address = k
140 }
141
142 *l = ServerMap(rawData)
143
144 return nil
145}
146
147type Config struct {
148 Keys map[string]map[string]map[string]string `yaml:"keys"`
149 StaticAcls map[string][]string `yaml:"static-acls"`
150 DynamicAcls map[string]struct {
151 Generator string `yaml:"generator"`
152 Filter string `yaml:"filter"`
153 } `yaml:"dynamic-acls"`
154 Servers ServerMap `yaml:"servers"`
155 Views map[string]struct {
156 MatchClients []string `yaml:"match-clients"`
157 RawInclude string `yaml:"raw-include"`
158 } `yaml:"views"`
159 Zones []struct {
160 Name string `yaml:"name"`
161 Type string `yaml:"type"` // forward-only is only valid type
162 MasterViews []string `yaml:"master-views"`
163 InViews []string `yaml:"in-views"`
164 AllowUpdateKeys []string `yaml:"allow-update-keys"`
165 AllowQuery []string `yaml:"allow-query"`
166 } `yaml:"zones"`
167}
168
169type TemplateData struct {
170 Keys []MaterializedKey
171 Acls map[string][]string
172 Views []MaterializedView
173}
174
175type MaterializedKey struct {
176 Name string
177 Algorithm string
178 KeyData string
179}
180
181type MaterializedView struct {
182 Name string
183 MatchClients []string
184 RawInclude string
185 Zones []MaterializedZone
186 NotifySource *string
187 AlsoNotify []Server
188 Servers []Server
189}
190
191type MaterializedZone struct {
192 Name string
193 Type string
194 File string
195 InView *string
196 ForwardOnly bool
197 UpdateGrants []string
198 Forwarders []string
199 AllowQuery []string
200 Primaries []string
201}
202
203func inSlice(needle string, haystack []string) bool {
204 for _, v := range haystack {
205 if v == needle {
206 return true
207 }
208 }
209 return false
210}
211
212func materializeZones(cfg *Config, forView string, forServer Server) []MaterializedZone {
213 out := []MaterializedZone{}
214
215 for _, zone := range cfg.Zones {
216 var forwarders []string
217 forwardOnly := false
218 zoneType := forServer.Type
219 if zone.Type == "forward-only" {
220 zoneType = "forward"
221 forwardOnly = true
222
223 if fw, ok := forServer.Forwarders[zone.Name]; ok {
224 forwarders = fw
225 } else {
226 // Some forward-only zones don't have forwarders in a given
227 // region, so suppress them if there's nothing to forward to
228 continue
229 }
230 }
231
232 masteredHere := inSlice(forView, zone.MasterViews)
233 exclusiveZone := len(zone.InViews) > 0
234
235 // Zones mastered in a view are always materialized there.
236 //
237 // Zones that specify an in-views clause are only materialized in a
238 // view if that view is listed in the in-views list.
239 //
240 // Zones not mastered in a view and without an in-views list are always
241 // materialized.
242 if masteredHere || !exclusiveZone || (exclusiveZone && inSlice(forView, zone.InViews)) {
243 keys := []string{}
244 primaries := []string{}
245
246 if zoneType == "primary" {
247 // Only primary zones may be updated
248 for _, k := range zone.AllowUpdateKeys {
249 keys = append(keys, fmt.Sprintf("%s-%s", k, forView))
250 }
251 } else if len(forwarders) == 0 { // forward-only zones have forwarders, not primaries
252 // Secondary zones use all primary servers as their primaries
253 for _, v := range cfg.Servers {
254 if v.Type == "primary" {
255 primaries = append(primaries, v.Address)
256 }
257 }
258 }
259
260 // Zones not mastered here are considered to be mastered in their
261 // first master view. This may not be correct for views nested in
262 // views.
263 var masterView *string
264 if !masteredHere {
265 masterView = &zone.MasterViews[0]
266 }
267
268 // forward-only zones are memory resident and have no file
269 fileName := fmt.Sprintf("%s/db.%s", forView, zone.Name)
270 if zoneType == "forward" {
271 fileName = ""
272 }
273
274 out = append(out, MaterializedZone{
275 Name: zone.Name,
276 Type: zoneType,
277 File: fileName,
278 InView: masterView,
279 ForwardOnly: forwardOnly,
280 UpdateGrants: keys,
281 Forwarders: forwarders,
282 AllowQuery: zone.AllowQuery,
283 Primaries: primaries,
284 })
285 }
286 }
287
288 return out
289}
290
291func getSecondaryServers(cfg *Config, viewName string) []Server {
292 out := []Server{}
293 for _, v := range cfg.Servers {
294 if v.Type == "secondary" {
295 kv := fmt.Sprintf("%s-%s", *v.Key, viewName)
296 out = append(out, Server{
297 Address: v.Address,
298 Key: &kv,
299 })
300 }
301 }
302 return out
303}
304
305func materializeViews(cfg *Config, forServer Server) []MaterializedView {
306 out := []MaterializedView{}
307
308 for viewName, configView := range cfg.Views {
309 var notifySource *string
310 var servers []Server
311 var alsoNotify []Server
312
313 if forServer.Type == "primary" {
314 // Only primary zones do notifies
315 notifySource = &forServer.Address
316 alsoNotify = getSecondaryServers(cfg, viewName)
317 } else {
318 myKey := fmt.Sprintf("%s-%s", *forServer.Key, viewName)
319
320 for _, s := range cfg.Servers {
321 if s.Type == "primary" {
322 servers = append(servers, Server{
323 Address: s.Address,
324 Key: &myKey,
325 })
326 }
327 }
328 }
329
330 out = append(out, MaterializedView{
331 Name: viewName,
332 MatchClients: configView.MatchClients,
333 RawInclude: configView.RawInclude,
334 Zones: materializeZones(cfg, viewName, forServer),
335 AlsoNotify: alsoNotify,
336 NotifySource: notifySource,
337 Servers: servers,
338 })
339 }
340 return out
341}
342
343func materializeKeys(cfg *Config) []MaterializedKey {
344 out := []MaterializedKey{}
345 // Flatten the key YAML structure to <key_name>-<view>
346 for name, subKeys := range cfg.Keys {
347 for view, key := range subKeys {
348 for keyAlgo, keyData := range key {
349 out = append(out, MaterializedKey{
350 Name: fmt.Sprintf("%s-%s", name, view),
351 Algorithm: keyAlgo,
352 KeyData: keyData,
353 })
354 }
355 }
356 }
357 return out
358}
359
360func aclGenerateServers(cfg *Config) []string {
361 lines := []string{}
362
363 for _, server := range cfg.Servers {
364 lines = append(lines, server.Address)
365 lines = append(lines, server.IPs...)
366 }
367
368 return lines
369}
370
371// Filters use the https://golang.org/pkg/path/filepath/#Match syntax. If no
372// filter is specified then the default is "*".
373func aclGenerateKeys(cfg *Config, keys []MaterializedKey, filter string) []string {
374 lines := []string{}
375
376 if filter == "" {
377 filter = "*"
378 }
379
380 for _, k := range keys {
381 if ok, _ := filepath.Match(filter, k.Name); ok {
382 lines = append(lines, fmt.Sprintf("key %s", k.Name))
383 }
384 }
385
386 return lines
387}
388
389func materializeAcls(cfg *Config, keys []MaterializedKey) map[string][]string {
390 out := map[string][]string{}
391
392 keyIndex := map[string]bool{}
393 for _, k := range keys {
394 keyIndex[k.Name] = true
395 }
396
397 // Generate dynamic ACLs
398 for aclName, acl := range cfg.DynamicAcls {
399 switch acl.Generator {
400 case "servers":
401 out[aclName] = aclGenerateServers(cfg)
402 case "keys":
403 out[aclName] = aclGenerateKeys(cfg, keys, acl.Filter)
404 }
405 }
406
407 // For static key-based ACLs only emit key lines for keys we have
408 //
409 // TODO: This was for an old iteration of the config format. All key-based
410 // ACLs should be generated now. If that's the case then this loop can just
411 // be removed.
412 for aclName, acl := range cfg.StaticAcls {
413 validLines := []string{}
414 for _, line := range acl {
415 parts := strings.Split(line, " ")
416 if parts[0] == "key" {
417 if _, ok := keyIndex[parts[1]]; !ok {
418 continue
419 }
420 }
421
422 validLines = append(validLines, line)
423 }
424 out[aclName] = validLines
425 }
426
427 return out
428}
429
430func loadYaml(filename string, into interface{}) error {
431 rawData, err := ioutil.ReadFile(filename)
432 if err != nil {
433 return err
434 }
435
436 if err = yaml.Unmarshal(rawData, into); err != nil {
437 return err
438 }
439
440 return nil
441}
442
443func lookupUserGroup(un, gn string) (int, int, error) {
444 u, err := user.Lookup(un)
445 if err != nil {
446 return 0, 0, err
447 }
448
449 g, err := user.LookupGroup(gn)
450 if err != nil {
451 return 0, 0, nil
452 }
453
454 uid, err := strconv.Atoi(u.Uid)
455 if err != nil {
456 return 0, 0, err
457 }
458
459 gid, err := strconv.Atoi(g.Gid)
460 if err != nil {
461 return 0, 0, err
462 }
463
464 return uid, gid, nil
465}
466
467func generateNamedConf(forServer string, cfgFile, keysFile string) []byte {
468 var out bytes.Buffer
469
470 cfg := Config{}
471 if err := loadYaml(cfgFile, &cfg); err != nil {
472 log.Fatalf("error: %s", err)
473 }
474
475 if err := loadYaml(keysFile, &cfg); err != nil {
476 log.Fatalf("error: %s", err)
477 }
478
479 server, ok := cfg.Servers[forServer]
480 if !ok {
481 log.Fatalf("No server %s\n", forServer)
482 }
483
484 keys := materializeKeys(&cfg)
485
486 cfgTemplate.Execute(&out, &TemplateData{
487 Keys: keys,
488 Acls: materializeAcls(&cfg, keys),
489 Views: materializeViews(&cfg, *server),
490 })
491
492 return out.Bytes()
493}
494
495// This will replace the current process
496func execToProcess(args []string) {
497 log.Printf("running: %s %+v", args[1], args[1:])
498 if err := syscall.Exec(args[1], args[1:], []string{}); err != nil {
499 log.Fatalf("error: exec failed %s", err)
500 }
501}
502
503func writeFile(path string, contents []byte, mode os.FileMode, uid, gid int) error {
504 if err := ioutil.WriteFile(path, contents, mode); err != nil {
505 return err
506 }
507
508 if err := os.Chown(path, uid, gid); err != nil {
509 return err
510 }
511
512 return nil
513}
514
515// This is used for RNDC and only from the current host, so just generate
516// it fresh each container start.
517func generateRNDCKey(path string, uid, gid int) error {
518 var out bytes.Buffer
519 cmd := exec.Command("ddns-confgen", "-q", "-k", "rndc-key")
520 cmd.Stdout = &out
521 if err := cmd.Run(); err != nil {
522 return err
523 }
524
525 if err := writeFile(path, out.Bytes(), 0440, uid, gid); err != nil {
526 return err
527 }
528
529 return nil
530}
531
532func ensureDirExists(path string, mode os.FileMode, uid, gid int) {
533 _ = os.Mkdir(path, mode)
534 os.Chown(path, uid, gid)
535}
536
537func realMain() {
538 forServer := os.Getenv("SERVER_ADDRESS")
539 if forServer == "" {
540 log.Fatalf("error: SERVER_ADDRESS is not in environment")
541 }
542
543 namedUser, namedGroup, err := lookupUserGroup("named", "named")
544 if err != nil {
545 log.Fatalf("error: %s", err)
546 }
547
548 cfg := generateNamedConf(forServer, "/etc/bind/zones.yaml", "keys.yaml")
549 if err := writeFile("/etc/bind/named_local.conf", cfg, 0644, namedUser, namedGroup); err != nil {
550 log.Fatalf("error: %s", err)
551 }
552
553 // Ensure these directories exist
554 ensureDirExists("/etc/bind/local/cache", 0755, namedUser, namedGroup)
555 ensureDirExists("/etc/bind/local/cache/internal", 0755, namedUser, namedGroup)
556 ensureDirExists("/etc/bind/local/cache/external", 0755, namedUser, namedGroup)
557
558 // TODO: Write missing zone files for master
559
560 if err := generateRNDCKey("/etc/bind/rndc.key", namedUser, namedGroup); err != nil {
561 log.Fatalf("error: %s", err)
562 }
563
564 execToProcess(os.Args)
565}
566
567func testMain() {
568 forServer := os.Getenv("SERVER_ADDRESS")
569 if forServer == "" {
570 log.Fatalf("error: SERVER_ADDRESS is not in environment")
571 }
572
573 namedUser, namedGroup, err := lookupUserGroup("mcrute", "mcrute")
574 if err != nil {
575 log.Fatalf("error: %s", err)
576 }
577
578 cfg := generateNamedConf(forServer, "zones.yaml", "keys.yaml")
579 if err := writeFile("named_local.conf", cfg, 0644, namedUser, namedGroup); err != nil {
580 log.Fatalf("error: %s", err)
581 }
582}
583
584func main() {
585 testMain()
586}
diff --git a/bind/builder/zones.yaml b/bind/builder/zones.yaml
deleted file mode 100644
index 85fc863..0000000
--- a/bind/builder/zones.yaml
+++ /dev/null
@@ -1,256 +0,0 @@
1dynamic-acls:
2 all-masters:
3 generator: servers
4
5 internal-keys:
6 generator: keys
7 filter: "*-internal"
8
9 external-keys:
10 generator: keys
11 filter: "*-external"
12
13static-acls:
14 internal-nets:
15 - 172.16.0.0/16 # SEA1 (and AWS)
16 - 172.17.0.0/16 # SEA2
17 - 172.18.0.0/16 # FKL1
18 - 172.19.0.0/16 # SEA4
19 - 172.20.0.0/16 # ORD1
20 - 172.21.0.0/16 # Mobile Network
21 - 23.149.16.0/24 # Pomona ARIN Delegation
22 - 192.168.255.0/24 # Local Docker Bridge
23 - 2602:0803:4000::/40 # Pomona ARIN Delegation
24 - 2600:1f14:f39:e000::/56 # PDX1
25 - 2600:1f16:33:500::/56 # CMH1
26 - 2a05:d01c:7ba:b800::/56 # LHR1
27
28servers:
29 172.16.18.52: # PDX1 Legacy Primary
30 type: primary
31 ips:
32 - 50.112.45.116 # PDX1 Gateway External Legacy
33 - 54.148.70.70 # PDX1 Gateway External
34 - 172.16.18.73 # PDX1 Gateway Internal Legacy
35 - 2600:1f14:f39:e000:9fb5:8745:4eec:28b8 # PDX1 Gateway
36 forwarders:
37 amazonaws.com:
38 - 172.16.16.2
39 internal:
40 - 172.16.16.2
41
42 172.20.0.53: # ORD1 Secondary
43 type: secondary
44 key: ord1-transfer
45
46 172.16.35.10: # CMH1 Legacy Secondary
47 type: secondary
48 key: us-east-2-transfer
49 forwarders:
50 amazonaws.com:
51 - 172.16.32.2
52 internal:
53 - 172.16.32.2
54
55 172.16.66.181: # LHR1 Legacy Secondary
56 type: secondary
57 key: eu-west-2-transfer
58
59views:
60 external:
61 match-clients:
62 - external-keys
63 - "!internal-keys"
64 - "!internal-nets"
65 - any
66
67 raw-include: |
68 rate-limit {
69 responses-per-second 15;
70 exempt-clients {
71 internal-nets;
72 };
73 };
74
75 internal:
76 match-clients:
77 - "!external-keys"
78 - internal-nets
79 - internal-keys
80 - localhost
81
82 raw-include: |
83 response-policy {
84 zone "dns-policy.crute.me" log true;
85 };
86
87 # https://www.mail-archive.com/bind-users@lists.isc.org/msg25350.html
88 server 63.150.72.5 { send-cookie no; }; # sauthns1.qwest.net
89 server 208.44.130.121 { send-cookie no; }; # sauthns2.qwest.net.
90
91zones:
92 - name: amazonaws.com
93 type: forward-only
94 master-views:
95 - internal
96 in-views:
97 - internal
98
99 - name: internal
100 type: forward-only
101 master-views:
102 - internal
103 in-views:
104 - internal
105
106 # 2602:0803:4000::/40
107 - name: 0.4.3.0.8.0.2.0.6.2.ip6.arpa
108 master-views:
109 - external
110 allow-update-keys:
111 - as398223-net
112 - crute-me
113
114 # 24.149.16.0/24
115 - name: 16.149.23.in-addr.arpa
116 master-views:
117 - external
118 allow-update-keys:
119 - as398223-net
120
121 # Global IPv4 Reverse Zone
122 # 172.16.0.0/16
123 - name: 16.172.in-addr.arpa
124 master-views:
125 - internal
126 in-views:
127 - internal
128 allow-update-keys:
129 - crute-me
130 - sea1-dhcpd-key
131
132 # FKL1 IPv4 Reverse Zone
133 # 172.18.0.0/16
134 - name: 18.172.in-addr.arpa
135 master-views:
136 - internal
137 in-views:
138 - internal
139 allow-update-keys:
140 - fkl1-crute-me
141 - fkl1-dhcpd-key
142
143 # SEA4 IPv4 Reverse Zone
144 # 172.19.0.0/16
145 - name: 19.172.in-addr.arpa
146 master-views:
147 - internal
148 in-views:
149 - internal
150 allow-update-keys:
151 - crute-me
152
153 - name: dns-policy.crute.me
154 master-views:
155 - internal
156 in-views:
157 - internal
158
159 # This is an RPZ policy zone, nothing should be querying it
160 # except BIND internals. Also the zone most be manually
161 # updated and reloaded to allow leaving comments and
162 # preventing errors.
163 allow-query:
164 - none
165
166 - name: crute.us
167 master-views:
168 - external
169 allow-update-keys:
170 - crute-us
171
172 - name: crute.me
173 master-views:
174 - external
175 - internal
176 allow-update-keys:
177 - crute-me
178
179 - name: sea1.crute.me
180 master-views:
181 - internal
182 in-views:
183 - internal
184 allow-update-keys:
185 - sea1-crute-me
186 - crute-me
187 - sea1-dhcpd-key
188
189 - name: fkl1.crute.me
190 master-views:
191 - internal
192 in-views:
193 - internal
194 allow-update-keys:
195 - fkl1-crute-me
196 - fkl1-dhcpd-key
197
198 - name: crute.org
199 master-views:
200 - external
201 allow-update-keys:
202 - crute-org
203
204 - name: crute.dev
205 master-views:
206 - external
207 allow-update-keys:
208 - crute-dev
209
210 - name: softgroupcorp.com
211 master-views:
212 - external
213 allow-update-keys:
214 - softgroupcorp-com
215
216 - name: pomonaconsulting.com
217 master-views:
218 - external
219 allow-update-keys:
220 - pomonaconsulting-com
221
222 - name: pomonaconsulting.net
223 master-views:
224 - external
225 allow-update-keys:
226 - pomonaconsulting-net
227
228 - name: as398223.net
229 master-views:
230 - external
231 allow-update-keys:
232 - as398223-net
233
234 - name: 59erdiner.com
235 master-views:
236 - external
237 allow-update-keys:
238 - 59erdiner-com
239
240 - name: leavenworthsnowmobilerentals.com
241 master-views:
242 - external
243 allow-update-keys:
244 - leavenworthsnowmobilerentals-com
245
246 - name: lakewenatcheecabins.net
247 master-views:
248 - external
249 allow-update-keys:
250 - lakewenatcheecabins-net
251
252 - name: frompythonimportpodcast.com
253 master-views:
254 - external
255 allow-update-keys:
256 - frompythonimportpodcast-com