From 7d7094fad28993cec8ea9e2b736cb25c83b3d4f5 Mon Sep 17 00:00:00 2001 From: Sergey G Date: Mon, 7 Aug 2023 18:41:06 +0200 Subject: [PATCH 01/10] docs: add link to mdns-subdomain --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d5241fc..6c8840c 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ It is a simple and lightweight project that allows you to publish CNAME records Here are some of the benefits of using go-avahi-cname: -- **✅ No dependencies** +- **✅ No dependencies** You only need the Avahi daemon running on your host, no other libraries or packages are required. -- **✅ Small footprint** +- **✅ Small footprint** The binary size is less than 3MB, and it consumes minimal resources while running. -- **✅ Support x86_64 and ARM** -- **✅ Release binaries and containers** +- **✅ Support x86_64 and ARM** +- **✅ Release binaries and containers** ### How does it work? @@ -96,6 +96,7 @@ Ansible task to run the container: ## Source of inspiration +- https://github.com/nfam/mdns-subdomain/tree/master - I used this approach for version 2. - https://web.archive.org/web/20151016190620/http://www.avahi.org/wiki/Examples/PythonPublishAlias - https://pypi.org/project/mdns-publisher/ From dfba1b2bd329669b5e31ec44c7de6c8ef28b955d Mon Sep 17 00:00:00 2001 From: Sergey G Date: Mon, 7 Aug 2023 19:43:26 +0200 Subject: [PATCH 02/10] refactor: split on subcommands --- Dockerfile | 1 + cmd/cname.go | 101 +++++++++++++++++++++++++++++++++++++++++++++++ cmd/subdomain.go | 38 ++++++++++++++++++ go.mod | 10 +++-- go.sum | 20 +++++++--- main.go | 68 ++++++------------------------- 6 files changed, 173 insertions(+), 65 deletions(-) create mode 100644 cmd/cname.go create mode 100644 cmd/subdomain.go diff --git a/Dockerfile b/Dockerfile index d1e373d..1daf075 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,4 +15,5 @@ RUN go build -ldflags="-w -s" -o /go-avahi-cname # STAGE 2: build the container to run FROM scratch COPY --from=builder /go-avahi-cname /go-avahi-cname +EXPOSE 5353/udp ENTRYPOINT ["/go-avahi-cname"] diff --git a/cmd/cname.go b/cmd/cname.go new file mode 100644 index 0000000..18ef257 --- /dev/null +++ b/cmd/cname.go @@ -0,0 +1,101 @@ +package cmd + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "github.com/grishy/go-avahi-cname/publisher" + "github.com/miekg/dns" + "github.com/urfave/cli/v2" +) + +func formatCname(hostnameFqdn string, cnames []string) []string { + log.Println("Formating CNAMEs:") + + for i, cname := range cnames { + if !dns.IsFqdn(cname) { + cnames[i] = dns.Fqdn(cname + "." + hostnameFqdn) + + log.Printf(" > '%s' (added current FQDN)", cnames[i]) + continue + } + + log.Printf(" > '%s'", cname) + } + + return cnames +} + +func publishing(ctx context.Context, publisher *publisher.Publisher, ttl, interval uint32, cnames []string) { + resendDuration := time.Duration(interval) * time.Second + log.Printf("Publishing every %v and CNAME TTL=%ds.", resendDuration, ttl) + + // To start publishing immediately + // https://github.com/golang/go/issues/17601 + if err := publisher.PublishCNAMES(cnames, ttl); err != nil { + log.Fatalf("can't publish CNAMEs: %v", err) + } + + for { + select { + case <-time.Tick(resendDuration): + if err := publisher.PublishCNAMES(cnames, ttl); err != nil { + log.Fatalf("can't publish CNAMEs: %v", err) + } + case <-ctx.Done(): + fmt.Println() + log.Println("Closing publisher...") + if err := publisher.Close(); err != nil { + log.Fatalf("Can't close publisher: %v", err) + } + os.Exit(0) + } + } +} + +func cnameCmd(ctx context.Context, ttl, interval uint32, cnames []string) { + +} + +func CmdCname(ctx context.Context) *cli.Command { + return &cli.Command{ + Name: "cname", + Usage: "anonse CNAME via Avahi", + Flags: []cli.Flag{ + &cli.UintFlag{ + Name: "ttl", + Value: 600, + EnvVars: []string{"CNAME_TTL"}, + Usage: "TTL of CNAME record in seconds", + }, + &cli.UintFlag{ + Name: "interval", + Value: 300, + EnvVars: []string{"CNAME_INTERVAL"}, + Usage: "Interval of sending CNAME record in seconds", + }, + }, + Action: func(cCtx *cli.Context) error { + ttl := uint32(cCtx.Uint("ttl")) + interval := uint32(cCtx.Uint("interval")) + cnames := cCtx.Args().Slice() + + if len(cnames) == 0 { + log.Fatal("CNAMEs are not specified") + } + + log.Println("Creating publisher") + publisher, err := publisher.NewPublisher() + if err != nil { + log.Fatalf("Can't create publisher: %v", err) + } + + formattedCname := formatCname(publisher.Fqdn(), cnames) + publishing(ctx, publisher, ttl, interval, formattedCname) + return nil + }, + } +} diff --git a/cmd/subdomain.go b/cmd/subdomain.go new file mode 100644 index 0000000..9abe5c1 --- /dev/null +++ b/cmd/subdomain.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "context" + + "github.com/urfave/cli/v2" +) + +func CmdSubdomain(ctx context.Context) *cli.Command { + return &cli.Command{ + Name: "subdomain", + Usage: "reply on all subdomains queries", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "domain", + Value: "", + EnvVars: []string{"SUBDOMAIN_DOMAIN"}, + Usage: "Domain name to publish", + }, + &cli.StringSliceFlag{ + Name: "ifaces", + Value: nil, + EnvVars: []string{"SUBDOMAIN_IFACES"}, + Usage: "Interface for listening and publishing", + }, + &cli.BoolFlag{ + Name: "use-avahi", + Value: true, + EnvVars: []string{"SUBDOMAIN_USE_AVAHI"}, + Usage: "Use avahi for sending CNAMEs or plain DNS", + }, + }, + Action: func(cCtx *cli.Context) error { + + return nil + }, + } +} diff --git a/go.mod b/go.mod index 91d9a26..e811d4e 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,15 @@ require ( github.com/godbus/dbus/v5 v5.1.0 github.com/holoplot/go-avahi v1.0.1 github.com/miekg/dns v1.1.55 + github.com/urfave/cli/v2 v2.25.7 ) require ( + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/tools v0.11.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/tools v0.12.0 // indirect ) diff --git a/go.sum b/go.sum index f299854..141e9e2 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -5,12 +7,18 @@ github.com/holoplot/go-avahi v1.0.1 h1:XcqR2keL4qWRnlxHD5CAOdWpLFZJ+EOUK0vEuylfv github.com/holoplot/go-avahi v1.0.1/go.mod h1:qH5psEKb0DK+BRplMfc+RY4VMOlbf6mqfxgpMy6aP0M= github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= -golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= diff --git a/main.go b/main.go index 0316344..8123cfa 100644 --- a/main.go +++ b/main.go @@ -2,72 +2,28 @@ package main import ( "context" - "fmt" "log" "os" "os/signal" - "time" - "github.com/grishy/go-avahi-cname/publisher" - "github.com/miekg/dns" + "github.com/grishy/go-avahi-cname/cmd" + "github.com/urfave/cli/v2" ) -const TTL = uint32(10 * 60) // in seconds - -func formatCname(hostnameFqdn string, cnames []string) []string { - log.Println("Formating CNAMEs:") - - for i, cname := range cnames { - if !dns.IsFqdn(cname) { - cnames[i] = dns.Fqdn(cname + "." + hostnameFqdn) - - log.Printf(" > '%s' (added current FQDN)", cnames[i]) - continue - } - - log.Printf(" > '%s'", cname) - } - - return cnames -} - -func publishing(ctx context.Context, publisher *publisher.Publisher, cnames []string) { - resendDuration := time.Duration(TTL/2) * time.Second - log.Printf("Publishing every %v and CNAME TTL=%ds.", resendDuration, TTL) - - // To start publishing immediately - // https://github.com/golang/go/issues/17601 - if err := publisher.PublishCNAMES(cnames, TTL); err != nil { - log.Fatalf("can't publish CNAMEs: %v", err) - } - - for { - select { - case <-time.Tick(resendDuration): - if err := publisher.PublishCNAMES(cnames, TTL); err != nil { - log.Fatalf("can't publish CNAMEs: %v", err) - } - case <-ctx.Done(): - fmt.Println() - log.Println("Closing publisher...") - if err := publisher.Close(); err != nil { - log.Fatalf("Can't close publisher: %v", err) - } - os.Exit(0) - } - } -} - func main() { ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() - log.Println("Creating publisher") - publisher, err := publisher.NewPublisher() - if err != nil { - log.Fatalf("Can't create publisher: %v", err) + app := &cli.App{ + Name: "go-avahi-cname", + Usage: "make an explosive entrance", + Commands: []*cli.Command{ + cmd.CmdCname(ctx), + cmd.CmdSubdomain(ctx), + }, } - cnames := formatCname(publisher.Fqdn(), os.Args[1:]) - publishing(ctx, publisher, cnames) + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } From 5ff3e5c947c0e9141dd00e6796ab46004af092ca Mon Sep 17 00:00:00 2001 From: Sergey G Date: Mon, 7 Aug 2023 23:13:28 +0200 Subject: [PATCH 03/10] feat: reading UDP and parse DNS --- cmd/cname.go | 61 ++++++++++++++-------- cmd/subdomain.go | 133 ++++++++++++++++++++++++++++++++++++++++------- go.mod | 1 + go.sum | 2 + main.go | 13 +++-- 5 files changed, 164 insertions(+), 46 deletions(-) diff --git a/cmd/cname.go b/cmd/cname.go index 18ef257..9575f51 100644 --- a/cmd/cname.go +++ b/cmd/cname.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log" - "os" "time" "github.com/grishy/go-avahi-cname/publisher" @@ -19,7 +18,7 @@ func formatCname(hostnameFqdn string, cnames []string) []string { if !dns.IsFqdn(cname) { cnames[i] = dns.Fqdn(cname + "." + hostnameFqdn) - log.Printf(" > '%s' (added current FQDN)", cnames[i]) + log.Printf(" > '%s' (added FQDN)", cnames[i]) continue } @@ -29,41 +28,58 @@ func formatCname(hostnameFqdn string, cnames []string) []string { return cnames } -func publishing(ctx context.Context, publisher *publisher.Publisher, ttl, interval uint32, cnames []string) { +func publishing(ctx context.Context, publisher *publisher.Publisher, cnames []string, ttl, interval uint32) error { + log.Printf("Publishing every %ds and CNAME TTL %ds", interval, ttl) + resendDuration := time.Duration(interval) * time.Second - log.Printf("Publishing every %v and CNAME TTL=%ds.", resendDuration, ttl) + ticker := time.NewTicker(resendDuration) + defer ticker.Stop() // To start publishing immediately // https://github.com/golang/go/issues/17601 if err := publisher.PublishCNAMES(cnames, ttl); err != nil { - log.Fatalf("can't publish CNAMEs: %v", err) + return fmt.Errorf("can't publish CNAMEs: %w", err) } for { select { - case <-time.Tick(resendDuration): + case <-ticker.C: if err := publisher.PublishCNAMES(cnames, ttl); err != nil { - log.Fatalf("can't publish CNAMEs: %v", err) + return fmt.Errorf("can't publish CNAMEs: %w", err) } case <-ctx.Done(): - fmt.Println() + fmt.Println() // Add new line after ^C log.Println("Closing publisher...") if err := publisher.Close(); err != nil { - log.Fatalf("Can't close publisher: %v", err) + return fmt.Errorf("can't close publisher: %w", err) } - os.Exit(0) + return nil } } } -func cnameCmd(ctx context.Context, ttl, interval uint32, cnames []string) { +func runCname(ctx context.Context, cnames []string, fqdn string, ttl, interval uint32) error { + log.Println("Creating publisher") + publisher, err := publisher.NewPublisher() + if err != nil { + return fmt.Errorf("can't create publisher: %w", err) + } + + if fqdn == "" { + log.Println("Getting FQDN from Avahi") + fqdn = publisher.Fqdn() + } + + log.Printf("FQDN: %s", fqdn) + formattedCname := formatCname(fqdn, cnames) + return publishing(ctx, publisher, formattedCname, ttl, interval) } func CmdCname(ctx context.Context) *cli.Command { return &cli.Command{ Name: "cname", - Usage: "anonse CNAME via Avahi", + Usage: "Anounce CNAME records for current host via Avahi. Require DBus connection to Avahi daemon.", Flags: []cli.Flag{ &cli.UintFlag{ Name: "ttl", @@ -75,27 +91,26 @@ func CmdCname(ctx context.Context) *cli.Command { Name: "interval", Value: 300, EnvVars: []string{"CNAME_INTERVAL"}, - Usage: "Interval of sending CNAME record in seconds", + Usage: "Interval of publishing CNAME records in seconds", + }, + &cli.StringFlag{ + Name: "fqdn", + EnvVars: []string{"SUBDOMAIN_FQDN"}, + Usage: "FQDN which will be used for CNAME. If empty, will be used current FQDN", + DefaultText: "hostname.local.", }, }, Action: func(cCtx *cli.Context) error { ttl := uint32(cCtx.Uint("ttl")) interval := uint32(cCtx.Uint("interval")) + fqdn := cCtx.String("fqdn") cnames := cCtx.Args().Slice() if len(cnames) == 0 { - log.Fatal("CNAMEs are not specified") + return fmt.Errorf("at least one CNAME should be provided") } - log.Println("Creating publisher") - publisher, err := publisher.NewPublisher() - if err != nil { - log.Fatalf("Can't create publisher: %v", err) - } - - formattedCname := formatCname(publisher.Fqdn(), cnames) - publishing(ctx, publisher, ttl, interval, formattedCname) - return nil + return runCname(ctx, cnames, fqdn, ttl, interval) }, } } diff --git a/cmd/subdomain.go b/cmd/subdomain.go index 9abe5c1..feed078 100644 --- a/cmd/subdomain.go +++ b/cmd/subdomain.go @@ -2,37 +2,134 @@ package cmd import ( "context" + "fmt" + "log" + "net" + "github.com/godbus/dbus/v5" + "github.com/holoplot/go-avahi" + "github.com/miekg/dns" "github.com/urfave/cli/v2" ) +func getFqdn() (string, error) { + conn, err := dbus.SystemBus() + if err != nil { + return "nil", fmt.Errorf("can't connect to system bus: %v", err) + } + + server, err := avahi.ServerNew(conn) + if err != nil { + return "nil", fmt.Errorf("can't create Avahi server: %v", err) + } + + avahiFqdn, err := server.GetHostNameFqdn() + if err != nil { + return "nil", fmt.Errorf("can't get FQDN from Avahi: %v", err) + } + + return avahiFqdn, nil +} + +func listen() (*net.UDPConn, error) { + addr := &net.UDPAddr{ + IP: net.ParseIP("224.0.0.251"), + Port: 5353, + } + + conn, err := net.ListenMulticastUDP("udp4", nil, addr) + if err != nil { + return nil, err + } + + return conn, nil +} + +func readMessage(ctx context.Context, conn *net.UDPConn) (chan *dns.Msg, chan error) { + buf := make([]byte, 1500) + + msgCh := make(chan *dns.Msg) + errCh := make(chan error) + + go func() { + <-ctx.Done() + fmt.Println() // Add new line after ^C + log.Println("Closing reader") + + conn.Close() + close(msgCh) + close(errCh) + }() + + go func() { + for { + read, addr, err := conn.ReadFromUDP(buf) + if err != nil { + errCh <- fmt.Errorf("can't read from UDP from %s: %w", addr, err) + continue + } + + msg := new(dns.Msg) + if err := msg.Unpack(buf[:read]); err != nil { + errCh <- fmt.Errorf("can't unpack message from %s: %w", addr, err) + continue + } + + msgCh <- msg + } + }() + + return msgCh, errCh +} + +func runSubdomain(ctx context.Context, fqdn string) error { + log.Println("Starting subdomain...") + + l, err := listen() + if err != nil { + return fmt.Errorf("can't listen: %w", err) + } + defer l.Close() + + msgCh, errCh := readMessage(ctx, l) + // TODO: Pack to one struct, msg and error + _ = errCh + + for msg := range msgCh { + if len(msg.Question) > 0 { + log.Printf("Received question: %s", msg.Question[0].Name) + } + } + + return nil +} + func CmdSubdomain(ctx context.Context) *cli.Command { return &cli.Command{ Name: "subdomain", - Usage: "reply on all subdomains queries", + Usage: "Listen for all queries and publish CNAMEs for subdomains", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "domain", - Value: "", - EnvVars: []string{"SUBDOMAIN_DOMAIN"}, - Usage: "Domain name to publish", - }, - &cli.StringSliceFlag{ - Name: "ifaces", - Value: nil, - EnvVars: []string{"SUBDOMAIN_IFACES"}, - Usage: "Interface for listening and publishing", - }, - &cli.BoolFlag{ - Name: "use-avahi", - Value: true, - EnvVars: []string{"SUBDOMAIN_USE_AVAHI"}, - Usage: "Use avahi for sending CNAMEs or plain DNS", + Name: "fqdn", + EnvVars: []string{"SUBDOMAIN_FQDN"}, + Usage: "FQDN which will be used for CNAME. If empty, will be used current FQDN", + DefaultText: "hostname.local.", }, }, Action: func(cCtx *cli.Context) error { + fqdn := cCtx.String("fqdn") + + if fqdn == "" { + var err error + fqdn, err = getFqdn() + if err != nil { + return fmt.Errorf("can't get FQDN: %w", err) + } + } + + log.Printf("FQDN: %s", fqdn) - return nil + return runSubdomain(ctx, fqdn) }, } } diff --git a/go.mod b/go.mod index e811d4e..2cab6f4 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/grishy/go-avahi-cname go 1.20 require ( + github.com/carlmjohnson/versioninfo v0.22.5 github.com/godbus/dbus/v5 v5.1.0 github.com/holoplot/go-avahi v1.0.1 github.com/miekg/dns v1.1.55 diff --git a/go.sum b/go.sum index 141e9e2..4a95877 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc= +github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/main.go b/main.go index 8123cfa..dec0c59 100644 --- a/main.go +++ b/main.go @@ -2,21 +2,23 @@ package main import ( "context" - "log" + "fmt" "os" "os/signal" + "github.com/carlmjohnson/versioninfo" "github.com/grishy/go-avahi-cname/cmd" "github.com/urfave/cli/v2" ) func main() { - ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill) defer stop() app := &cli.App{ - Name: "go-avahi-cname", - Usage: "make an explosive entrance", + Name: "go-avahi-cname", + Usage: "Additional functionality for Avahi's mDNS responder", + Version: versioninfo.Short(), Commands: []*cli.Command{ cmd.CmdCname(ctx), cmd.CmdSubdomain(ctx), @@ -24,6 +26,7 @@ func main() { } if err := app.Run(os.Args); err != nil { - log.Fatal(err) + fmt.Println("Error:") + fmt.Printf(" > %+v\n", err) } } From b2888ad01ed58f1907f6c67a202de8a5645aa4cd Mon Sep 17 00:00:00 2001 From: Sergey G Date: Tue, 8 Aug 2023 14:21:11 +0200 Subject: [PATCH 04/10] feat: add subdomain --- {publisher => avahi}/publisher.go | 7 +- cmd/cname.go | 44 +++++---- cmd/subdomain.go | 147 +++++++++++++++++------------- 3 files changed, 110 insertions(+), 88 deletions(-) rename {publisher => avahi}/publisher.go (95%) diff --git a/publisher/publisher.go b/avahi/publisher.go similarity index 95% rename from publisher/publisher.go rename to avahi/publisher.go index 693bd22..33d3a92 100644 --- a/publisher/publisher.go +++ b/avahi/publisher.go @@ -1,4 +1,4 @@ -package publisher +package avahi import ( "fmt" @@ -89,7 +89,6 @@ func (p *Publisher) PublishCNAMES(cnames []string, ttl uint32) error { return nil } -func (p *Publisher) Close() error { - p.avahiServer.Close() - return p.dbusConn.Close() +func (p *Publisher) Close() { + p.avahiServer.Close() // It also close the DBus connection } diff --git a/cmd/cname.go b/cmd/cname.go index 9575f51..bf6e5d5 100644 --- a/cmd/cname.go +++ b/cmd/cname.go @@ -6,7 +6,7 @@ import ( "log" "time" - "github.com/grishy/go-avahi-cname/publisher" + "github.com/grishy/go-avahi-cname/avahi" "github.com/miekg/dns" "github.com/urfave/cli/v2" ) @@ -28,7 +28,7 @@ func formatCname(hostnameFqdn string, cnames []string) []string { return cnames } -func publishing(ctx context.Context, publisher *publisher.Publisher, cnames []string, ttl, interval uint32) error { +func publishing(ctx context.Context, publisher *avahi.Publisher, cnames []string, ttl, interval uint32) error { log.Printf("Publishing every %ds and CNAME TTL %ds", interval, ttl) resendDuration := time.Duration(interval) * time.Second @@ -49,27 +49,14 @@ func publishing(ctx context.Context, publisher *publisher.Publisher, cnames []st } case <-ctx.Done(): fmt.Println() // Add new line after ^C - log.Println("Closing publisher...") - if err := publisher.Close(); err != nil { - return fmt.Errorf("can't close publisher: %w", err) - } + log.Println("Closing publisher") + publisher.Close() return nil } } } -func runCname(ctx context.Context, cnames []string, fqdn string, ttl, interval uint32) error { - log.Println("Creating publisher") - publisher, err := publisher.NewPublisher() - if err != nil { - return fmt.Errorf("can't create publisher: %w", err) - } - - if fqdn == "" { - log.Println("Getting FQDN from Avahi") - fqdn = publisher.Fqdn() - } - +func runCname(ctx context.Context, publisher *avahi.Publisher, cnames []string, fqdn string, ttl, interval uint32) error { log.Printf("FQDN: %s", fqdn) formattedCname := formatCname(fqdn, cnames) @@ -79,24 +66,24 @@ func runCname(ctx context.Context, cnames []string, fqdn string, ttl, interval u func CmdCname(ctx context.Context) *cli.Command { return &cli.Command{ Name: "cname", - Usage: "Anounce CNAME records for current host via Avahi. Require DBus connection to Avahi daemon.", + Usage: "Anounce CNAME records for host via avahi-daemon", Flags: []cli.Flag{ &cli.UintFlag{ Name: "ttl", Value: 600, EnvVars: []string{"CNAME_TTL"}, - Usage: "TTL of CNAME record in seconds", + Usage: "TTL of CNAME record in seconds. How long will they be valid.", }, &cli.UintFlag{ Name: "interval", Value: 300, EnvVars: []string{"CNAME_INTERVAL"}, - Usage: "Interval of publishing CNAME records in seconds", + Usage: "Interval of publishing CNAME records in seconds. How often to send records to other machines.", }, &cli.StringFlag{ Name: "fqdn", EnvVars: []string{"SUBDOMAIN_FQDN"}, - Usage: "FQDN which will be used for CNAME. If empty, will be used current FQDN", + Usage: "Where to redirect. If empty, will be used avahi FQDN (current machine).", DefaultText: "hostname.local.", }, }, @@ -110,7 +97,18 @@ func CmdCname(ctx context.Context) *cli.Command { return fmt.Errorf("at least one CNAME should be provided") } - return runCname(ctx, cnames, fqdn, ttl, interval) + log.Println("Creating publisher") + publisher, err := avahi.NewPublisher() + if err != nil { + return fmt.Errorf("can't create publisher: %w", err) + } + + if fqdn == "" { + log.Println("Getting FQDN from Avahi") + fqdn = publisher.Fqdn() + } + + return runCname(ctx, publisher, cnames, fqdn, ttl, interval) }, } } diff --git a/cmd/subdomain.go b/cmd/subdomain.go index feed078..94dd137 100644 --- a/cmd/subdomain.go +++ b/cmd/subdomain.go @@ -2,35 +2,17 @@ package cmd import ( "context" + "errors" "fmt" "log" "net" + "strings" - "github.com/godbus/dbus/v5" - "github.com/holoplot/go-avahi" + "github.com/grishy/go-avahi-cname/avahi" "github.com/miekg/dns" "github.com/urfave/cli/v2" ) -func getFqdn() (string, error) { - conn, err := dbus.SystemBus() - if err != nil { - return "nil", fmt.Errorf("can't connect to system bus: %v", err) - } - - server, err := avahi.ServerNew(conn) - if err != nil { - return "nil", fmt.Errorf("can't create Avahi server: %v", err) - } - - avahiFqdn, err := server.GetHostNameFqdn() - if err != nil { - return "nil", fmt.Errorf("can't get FQDN from Avahi: %v", err) - } - - return avahiFqdn, nil -} - func listen() (*net.UDPConn, error) { addr := &net.UDPAddr{ IP: net.ParseIP("224.0.0.251"), @@ -45,59 +27,93 @@ func listen() (*net.UDPConn, error) { return conn, nil } -func readMessage(ctx context.Context, conn *net.UDPConn) (chan *dns.Msg, chan error) { - buf := make([]byte, 1500) +type dnsMsg struct { + msg dns.Msg + err error +} - msgCh := make(chan *dns.Msg) - errCh := make(chan error) +func reader(ctx context.Context, conn *net.UDPConn) chan *dnsMsg { + buf := make([]byte, 1500) - go func() { - <-ctx.Done() - fmt.Println() // Add new line after ^C - log.Println("Closing reader") - - conn.Close() - close(msgCh) - close(errCh) - }() + msgCh := make(chan *dnsMsg) go func() { for { - read, addr, err := conn.ReadFromUDP(buf) + dnsMsg := &dnsMsg{ + msg: dns.Msg{}, + } + + bytesRead, remoteAddress, err := conn.ReadFromUDP(buf) if err != nil { - errCh <- fmt.Errorf("can't read from UDP from %s: %w", addr, err) - continue + if ctx.Err() != nil { + log.Println("Closing reader") + close(msgCh) + return + } + + dnsMsg.err = errors.Join(dnsMsg.err, fmt.Errorf("can't read from UDP from %s: %w", remoteAddress, err)) + msgCh <- dnsMsg + return } - msg := new(dns.Msg) - if err := msg.Unpack(buf[:read]); err != nil { - errCh <- fmt.Errorf("can't unpack message from %s: %w", addr, err) + if err := dnsMsg.msg.Unpack(buf[:bytesRead]); err != nil { + dnsMsg.err = errors.Join(dnsMsg.err, fmt.Errorf("can't unpack message: %w", err)) + msgCh <- dnsMsg continue } - msgCh <- msg + msgCh <- dnsMsg } }() - return msgCh, errCh + return msgCh +} + +func selectQuestion(fqdn string, qs []dns.Question) (res []string) { + for _, q := range qs { + if strings.HasSuffix(q.Name, fqdn) { + res = append(res, q.Name) + } + } + + return res } -func runSubdomain(ctx context.Context, fqdn string) error { - log.Println("Starting subdomain...") +func runSubdomain(ctx context.Context, publisher *avahi.Publisher, fqdn string, ttl uint32) error { + log.Printf("FQDN: %s", fqdn) - l, err := listen() + log.Println("Create connection to multicast") + conn, err := listen() if err != nil { - return fmt.Errorf("can't listen: %w", err) + return fmt.Errorf("can't create connection: %w", err) } - defer l.Close() - msgCh, errCh := readMessage(ctx, l) - // TODO: Pack to one struct, msg and error - _ = errCh + msgCh := reader(ctx, conn) + + go func() { + <-ctx.Done() + fmt.Println() // Add new line after ^C + log.Println("Closing connection") + if err := conn.Close(); err != nil { + log.Printf("Can't close connection: %v", err) + } + }() - for msg := range msgCh { - if len(msg.Question) > 0 { - log.Printf("Received question: %s", msg.Question[0].Name) + log.Println("Start listening") + for m := range msgCh { + msg := m.msg + if m.err != nil { + log.Printf("Error: %v", m.err) + continue + } + + found := selectQuestion(fqdn, msg.Question) + + if len(found) > 0 { + if err := publisher.PublishCNAMES(found, ttl); err != nil { + log.Printf("Can't publish CNAMEs: %v", err) + continue + } } } @@ -109,6 +125,12 @@ func CmdSubdomain(ctx context.Context) *cli.Command { Name: "subdomain", Usage: "Listen for all queries and publish CNAMEs for subdomains", Flags: []cli.Flag{ + &cli.UintFlag{ + Name: "ttl", + Value: 600, + EnvVars: []string{"CNAME_TTL"}, + Usage: "TTL of CNAME record in seconds", + }, &cli.StringFlag{ Name: "fqdn", EnvVars: []string{"SUBDOMAIN_FQDN"}, @@ -117,19 +139,22 @@ func CmdSubdomain(ctx context.Context) *cli.Command { }, }, Action: func(cCtx *cli.Context) error { + ttl := uint32(cCtx.Uint("ttl")) fqdn := cCtx.String("fqdn") - if fqdn == "" { - var err error - fqdn, err = getFqdn() - if err != nil { - return fmt.Errorf("can't get FQDN: %w", err) - } + log.Println("Creating publisher") + publisher, err := avahi.NewPublisher() + if err != nil { + return fmt.Errorf("can't create publisher: %w", err) } + defer publisher.Close() - log.Printf("FQDN: %s", fqdn) + if fqdn == "" { + log.Println("Getting FQDN from Avahi") + fqdn = publisher.Fqdn() + } - return runSubdomain(ctx, fqdn) + return runSubdomain(ctx, publisher, fqdn, ttl) }, } } From c57807a79ff7fddac812b7eee58237b90f5cb170 Mon Sep 17 00:00:00 2001 From: Sergey G Date: Tue, 8 Aug 2023 15:15:45 +0200 Subject: [PATCH 05/10] refactor: add comments --- avahi/publisher.go | 18 +++++++++++------- cmd/cname.go | 39 +++++++++++++++++++++------------------ cmd/subdomain.go | 30 +++++++++++++++++------------- main.go | 2 +- 4 files changed, 50 insertions(+), 39 deletions(-) diff --git a/avahi/publisher.go b/avahi/publisher.go index 33d3a92..5d76317 100644 --- a/avahi/publisher.go +++ b/avahi/publisher.go @@ -22,20 +22,21 @@ type Publisher struct { rdataField []byte } +// NewPublisher creates a new service for Publisher. func NewPublisher() (*Publisher, error) { conn, err := dbus.SystemBus() if err != nil { - return nil, fmt.Errorf("can't connect to system bus: %v", err) + return nil, fmt.Errorf("failed to connect to system bus: %v", err) } server, err := avahi.ServerNew(conn) if err != nil { - return nil, fmt.Errorf("can't create Avahi server: %v", err) + return nil, fmt.Errorf("failed to create Avahi server: %v", err) } avahiFqdn, err := server.GetHostNameFqdn() if err != nil { - return nil, fmt.Errorf("can't get FQDN from Avahi: %v", err) + return nil, fmt.Errorf("failed to get FQDN from Avahi: %v", err) } fqdn := dns.Fqdn(avahiFqdn) @@ -45,7 +46,7 @@ func NewPublisher() (*Publisher, error) { rdataField := make([]byte, len(fqdn)+1) _, err = dns.PackDomainName(fqdn, rdataField, 0, nil, false) if err != nil { - return nil, fmt.Errorf("can't pack FQDN into RDATA: %v", err) + return nil, fmt.Errorf("failed to pack FQDN into RDATA: %v", err) } return &Publisher{ @@ -56,14 +57,16 @@ func NewPublisher() (*Publisher, error) { }, nil } +// Fqdn returns the fully qualified domain name from Avahi. func (p *Publisher) Fqdn() string { return p.fqdn } +// PublishCNAMES send via Avahi-daemon CNAME records with the provided TTL. func (p *Publisher) PublishCNAMES(cnames []string, ttl uint32) error { group, err := p.avahiServer.EntryGroupNew() if err != nil { - return fmt.Errorf("can't create entry group: %v", err) + return fmt.Errorf("failed to create entry group: %v", err) } for _, cname := range cnames { @@ -78,17 +81,18 @@ func (p *Publisher) PublishCNAMES(cnames []string, ttl uint32) error { p.rdataField, ) if err != nil { - return fmt.Errorf("can't add record to entry group: %v", err) + return fmt.Errorf("failed to add record to entry group: %v", err) } } if err := group.Commit(); err != nil { - return fmt.Errorf("can't commit entry group: %v", err) + return fmt.Errorf("failed to commit entry group: %v", err) } return nil } +// Close associated resources. func (p *Publisher) Close() { p.avahiServer.Close() // It also close the DBus connection } diff --git a/cmd/cname.go b/cmd/cname.go index bf6e5d5..aa08b5d 100644 --- a/cmd/cname.go +++ b/cmd/cname.go @@ -11,23 +11,25 @@ import ( "github.com/urfave/cli/v2" ) +// formatCname formats CNAMEs by ensuring they are fully qualified domain names (FQDNs). func formatCname(hostnameFqdn string, cnames []string) []string { - log.Println("Formating CNAMEs:") + log.Println("Formatting CNAMEs:") + formattedCnames := make([]string, len(cnames)) for i, cname := range cnames { if !dns.IsFqdn(cname) { - cnames[i] = dns.Fqdn(cname + "." + hostnameFqdn) - - log.Printf(" > '%s' (added FQDN)", cnames[i]) - continue + formattedCnames[i] = dns.Fqdn(cname + "." + hostnameFqdn) + log.Printf(" > '%s' (added FQDN)", formattedCnames[i]) + } else { + formattedCnames[i] = cname + log.Printf(" > '%s'", cname) } - - log.Printf(" > '%s'", cname) } - return cnames + return formattedCnames } +// publishLoop handles the continuous publishing of CNAME records. func publishing(ctx context.Context, publisher *avahi.Publisher, cnames []string, ttl, interval uint32) error { log.Printf("Publishing every %ds and CNAME TTL %ds", interval, ttl) @@ -35,17 +37,17 @@ func publishing(ctx context.Context, publisher *avahi.Publisher, cnames []string ticker := time.NewTicker(resendDuration) defer ticker.Stop() - // To start publishing immediately + // Publish immediately // https://github.com/golang/go/issues/17601 if err := publisher.PublishCNAMES(cnames, ttl); err != nil { - return fmt.Errorf("can't publish CNAMEs: %w", err) + return fmt.Errorf("failed to publish CNAMEs: %w", err) } for { select { case <-ticker.C: if err := publisher.PublishCNAMES(cnames, ttl); err != nil { - return fmt.Errorf("can't publish CNAMEs: %w", err) + return fmt.Errorf("failed to publish CNAMEs: %w", err) } case <-ctx.Done(): fmt.Println() // Add new line after ^C @@ -56,6 +58,7 @@ func publishing(ctx context.Context, publisher *avahi.Publisher, cnames []string } } +// runCname sets up and starts the CNAME publishing process. func runCname(ctx context.Context, publisher *avahi.Publisher, cnames []string, fqdn string, ttl, interval uint32) error { log.Printf("FQDN: %s", fqdn) @@ -66,24 +69,24 @@ func runCname(ctx context.Context, publisher *avahi.Publisher, cnames []string, func CmdCname(ctx context.Context) *cli.Command { return &cli.Command{ Name: "cname", - Usage: "Anounce CNAME records for host via avahi-daemon", + Usage: "Announce CNAME records for host via avahi-daemon", Flags: []cli.Flag{ &cli.UintFlag{ Name: "ttl", Value: 600, - EnvVars: []string{"CNAME_TTL"}, - Usage: "TTL of CNAME record in seconds. How long will they be valid.", + EnvVars: []string{"TTL"}, + Usage: "TTL of CNAME record in seconds. How long they will be valid.", }, &cli.UintFlag{ Name: "interval", Value: 300, - EnvVars: []string{"CNAME_INTERVAL"}, + EnvVars: []string{"INTERVAL"}, Usage: "Interval of publishing CNAME records in seconds. How often to send records to other machines.", }, &cli.StringFlag{ Name: "fqdn", - EnvVars: []string{"SUBDOMAIN_FQDN"}, - Usage: "Where to redirect. If empty, will be used avahi FQDN (current machine).", + EnvVars: []string{"FQDN"}, + Usage: "Where to redirect. If empty, the Avahi FQDN (current machine) will be used", DefaultText: "hostname.local.", }, }, @@ -100,7 +103,7 @@ func CmdCname(ctx context.Context) *cli.Command { log.Println("Creating publisher") publisher, err := avahi.NewPublisher() if err != nil { - return fmt.Errorf("can't create publisher: %w", err) + return fmt.Errorf("failed to create publisher: %w", err) } if fqdn == "" { diff --git a/cmd/subdomain.go b/cmd/subdomain.go index 94dd137..b54949c 100644 --- a/cmd/subdomain.go +++ b/cmd/subdomain.go @@ -13,6 +13,12 @@ import ( "github.com/urfave/cli/v2" ) +type dnsMsg struct { + msg dns.Msg + err error +} + +// listen creates a UDP connection to multicast for listening to DNS messages. func listen() (*net.UDPConn, error) { addr := &net.UDPAddr{ IP: net.ParseIP("224.0.0.251"), @@ -27,11 +33,7 @@ func listen() (*net.UDPConn, error) { return conn, nil } -type dnsMsg struct { - msg dns.Msg - err error -} - +// reader reads DNS messages from the UDP connection. Assume that context is canceled before closing the connection. func reader(ctx context.Context, conn *net.UDPConn) chan *dnsMsg { buf := make([]byte, 1500) @@ -51,13 +53,13 @@ func reader(ctx context.Context, conn *net.UDPConn) chan *dnsMsg { return } - dnsMsg.err = errors.Join(dnsMsg.err, fmt.Errorf("can't read from UDP from %s: %w", remoteAddress, err)) + dnsMsg.err = errors.Join(dnsMsg.err, fmt.Errorf("failed to read from UDP from %s: %w", remoteAddress, err)) msgCh <- dnsMsg return } if err := dnsMsg.msg.Unpack(buf[:bytesRead]); err != nil { - dnsMsg.err = errors.Join(dnsMsg.err, fmt.Errorf("can't unpack message: %w", err)) + dnsMsg.err = errors.Join(dnsMsg.err, fmt.Errorf("failed to unpack message: %w", err)) msgCh <- dnsMsg continue } @@ -69,6 +71,7 @@ func reader(ctx context.Context, conn *net.UDPConn) chan *dnsMsg { return msgCh } +// selectQuestion filters and selects questions with the given FQDN suffix. func selectQuestion(fqdn string, qs []dns.Question) (res []string) { for _, q := range qs { if strings.HasSuffix(q.Name, fqdn) { @@ -79,13 +82,14 @@ func selectQuestion(fqdn string, qs []dns.Question) (res []string) { return res } +// runSubdomain starts listening for DNS messages, filters relevant questions, and publishes corresponding CNAMEs. func runSubdomain(ctx context.Context, publisher *avahi.Publisher, fqdn string, ttl uint32) error { log.Printf("FQDN: %s", fqdn) log.Println("Create connection to multicast") conn, err := listen() if err != nil { - return fmt.Errorf("can't create connection: %w", err) + return fmt.Errorf("failed to create connection: %w", err) } msgCh := reader(ctx, conn) @@ -95,7 +99,7 @@ func runSubdomain(ctx context.Context, publisher *avahi.Publisher, fqdn string, fmt.Println() // Add new line after ^C log.Println("Closing connection") if err := conn.Close(); err != nil { - log.Printf("Can't close connection: %v", err) + log.Printf("Failed to close connection: %v", err) } }() @@ -111,7 +115,7 @@ func runSubdomain(ctx context.Context, publisher *avahi.Publisher, fqdn string, if len(found) > 0 { if err := publisher.PublishCNAMES(found, ttl); err != nil { - log.Printf("Can't publish CNAMEs: %v", err) + log.Printf("Failed to publish CNAMEs: %v", err) continue } } @@ -128,12 +132,12 @@ func CmdSubdomain(ctx context.Context) *cli.Command { &cli.UintFlag{ Name: "ttl", Value: 600, - EnvVars: []string{"CNAME_TTL"}, + EnvVars: []string{"TTL"}, Usage: "TTL of CNAME record in seconds", }, &cli.StringFlag{ Name: "fqdn", - EnvVars: []string{"SUBDOMAIN_FQDN"}, + EnvVars: []string{"FQDN"}, Usage: "FQDN which will be used for CNAME. If empty, will be used current FQDN", DefaultText: "hostname.local.", }, @@ -145,7 +149,7 @@ func CmdSubdomain(ctx context.Context) *cli.Command { log.Println("Creating publisher") publisher, err := avahi.NewPublisher() if err != nil { - return fmt.Errorf("can't create publisher: %w", err) + return fmt.Errorf("failed to create publisher: %w", err) } defer publisher.Close() diff --git a/main.go b/main.go index dec0c59..8bbc64b 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ func main() { app := &cli.App{ Name: "go-avahi-cname", - Usage: "Additional functionality for Avahi's mDNS responder", + Usage: "A tool for publishing CNAME records with Avahi", Version: versioninfo.Short(), Commands: []*cli.Command{ cmd.CmdCname(ctx), From a517b0218c1aad7154ebac25c517568d04d6640f Mon Sep 17 00:00:00 2001 From: Sergey G Date: Tue, 8 Aug 2023 17:02:43 +0200 Subject: [PATCH 06/10] docs: split on 2 modes --- README.md | 93 +++++++++++++++++++++++++++--- docs/arch.excalidraw.svg | 17 ------ docs/arch_cname.excalidraw.svg | 17 ++++++ docs/arch_subdomain.excalidraw.svg | 17 ++++++ 4 files changed, 118 insertions(+), 26 deletions(-) delete mode 100644 docs/arch.excalidraw.svg create mode 100644 docs/arch_cname.excalidraw.svg create mode 100644 docs/arch_subdomain.excalidraw.svg diff --git a/README.md b/README.md index 6c8840c..adab7f0 100644 --- a/README.md +++ b/README.md @@ -4,28 +4,103 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/grishy/go-avahi-cname)](https://goreportcard.com/report/github.com/grishy/go-avahi-cname) ![Build Status](https://github.com/grishy/go-avahi-cname/actions/workflows/release.yml/badge.svg) -## What is go-avahi-cname? +# What is go-avahi-cname? It is a simple and lightweight project that allows you to publish CNAME records pointing to the local host over multicast DNS using the **Avahi** daemon, which is widely available in most Linux distributions. This means that you can access your local host using different names from any device on the same network, as long as they support Apple’s Bonjour protocol, which is compatible with Avahi. -### Goals +## TLDR + +Forward all subdomains from machine to machine( `*.hostname.local` -> `hostname.local`) + +1. _Binary_ `./go-avahi-cname subdomain` +2. _Docker_ `docker run -d -v "/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket" ghcr.io/grishy/go-avahi-cname:v2.0.0` + +## Modes + +- **Subdomain reply** - _I think you want this._ We listen to the traffic and if someone asks `*.hostname.local` (example: `name1.hostname.local`), we "redirect" to `hostname.local`. +- **Interval publishing** - With some frequency, we send out information about all `name1.hostname.local`, `name2.hostname.local`... + +## Goals Here are some of the benefits of using go-avahi-cname: - **✅ No dependencies** You only need the Avahi daemon running on your host, no other libraries or packages are required. -- **✅ Small footprint** - The binary size is less than 3MB, and it consumes minimal resources while running. +- **✅ Small footprint** - **✅ Support x86_64 and ARM** -- **✅ Release binaries and containers** +- **✅ Install as binaries or containers** + +--- + +# How does it work? + +_go-avahi-cname_ communicates with the Avahi daemon via DBus, and publishes the CNAME records. Different modes are only the way of how we select records to publish. The Avahi daemon then broadcasts these records over multicast DNS, so that other devices on the same network can resolve them. + +## Subdomain CNAME reply + +![Architecture](./docs/arch_subdomain.excalidraw.svg) + +```plain +> ./go-avahi-cname subdomain -h +NAME: + go-avahi-cname subdomain - Listen for all queries and publish CNAMEs for subdomains + +USAGE: + go-avahi-cname subdomain [command options] [arguments...] + +OPTIONS: + --ttl value TTL of CNAME record in seconds (default: 600) [$TTL] + --fqdn value FQDN which will be used for CNAME. If empty, will be used current FQDN (default: hostname.local.) [$FQDN] + --help, -h show help +``` + +In this variant, we listen to the traffic with avahi-daemon for all questions with names and if they match ours, we send a command to avahi to answer it (send CNAME). The standard can be run without parameters, then we will resolve all requests that contain our hostname. For example, `git.lab.local` will be redirected to `lab.local` + +## Interval publishing of CNAME records -### How does it work? +![Architecture](./docs/arch_cname.excalidraw.svg) -The following diagram shows the basic architecture of go-avahi-cname: +As you can see, _go-avahi-cname_ communicates with the Avahi daemon via DBus, and publishes the CNAME records that you specify as arguments. -![Architecture](./docs/arch.excalidraw.svg) +```plain +> ./go-avahi-cname cname -h +NAME: + go-avahi-cname cname - Announce CNAME records for host via avahi-daemon + +USAGE: + go-avahi-cname cname [command options] [arguments...] + +OPTIONS: + --ttl value TTL of CNAME record in seconds. How long they will be valid. (default: 600) [$TTL] + --interval value Interval of publishing CNAME records in seconds. How often to send records to other machines. (default: 300) [$INTERVAL] + --fqdn value Where to redirect. If empty, the Avahi FQDN (current machine) will be used (default: hostname.local.) [$FQDN] + --help, -h show help +``` + +You can specify any number of CNAMEs as arguments when running go-avahi-cname, with no length limit. +You can use either just the name (`name1`), which will create a record as a subdomain for the current machine, or you can write the full FQDN (`name1.hostname.local.` domain with a dot on the end) format. -As you can see, _go-avahi-cname_ communicates with the Avahi daemon via DBus, and publishes the CNAME records that you specify as arguments. The Avahi daemon then broadcasts these records over multicast DNS, so that other devices on the same network can resolve them. +For example, if your machine’s hostname is lab, you can run: + +```plain +> ./go-avahi-cname cname git photo.local. example.lab.local. +2023/08/08 14:51:21 Creating publisher +2023/08/08 14:51:21 Getting FQDN from Avahi +2023/08/08 14:51:21 FQDN: lab.local. +2023/08/08 14:51:21 Formatting CNAMEs: +2023/08/08 14:51:21 > 'git.lab.local.' (added FQDN) +2023/08/08 14:51:21 > 'photo.local.' +2023/08/08 14:51:21 > 'example.lab.local.' +2023/08/08 14:51:21 Publishing every 300s and CNAME TTL 600s +``` + +This will create three CNAME records pointing to your local host: + +- `git.lab.local.` +- `photo.local.` +- `example.lab.local.` + +You can then access your local host using any of these names from other devices on the same network. ## How to use and install? diff --git a/docs/arch.excalidraw.svg b/docs/arch.excalidraw.svg deleted file mode 100644 index 329f22d..0000000 --- a/docs/arch.excalidraw.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daVPi2tb+3r/C8v16yN3zcL8549SKtlwivnXLilx1MDAxMDVcclx1MDAwMkJU9Fb/97t2VFx1MDAxMjKRMIndzanqOiaQ7Oy9nmettdeQ/35bWVn1XrrO6r9XVp1B3W65jZ79vPqPOf7k9Ppup1xyp4j/d7/z2Kv737zzvG7/3//6V/BcdTAwMGKr3rl/+5XTcu6dtteH7/0//L2y8l//39B9ek7ds9u3Lcf/gX8quFx1MDAxNSY4evR7p+3fVlItmdCSXHUwMDBlv+D2N+F2ntOAszd2q+9cdTAwMDRnzKHVW6V71Z1KpdzbOT/ePl1bf3HuL4O73rit1qn30vJH1e/Ak1x1MDAwNOf6Xq/TdKpuw7szo4pcdTAwMWNP+1Wv83h713b65unR8Gina9dd78VcXFx1MDAwN1x1MDAwNUffpiD8vVx1MDAwMfxVokohS2NcIrRWiElOxfC8uVx1MDAwMoHTzNKaXHUwMDBiKSVDVNPI0DY6rU7PXGbt/26QqiNcdTAwMTRcZu7arjdvYYTtRug7NzfBXHUwMDE3nt+fViBtUVx1MDAwMVx1MDAxN6dYXHUwMDEyzmUwgjvHvb3z4CtUSUtcIqFcdTAwMTGSXGIxjEgwXG7HX1x1MDAwZSyZ1lRSpIdnzL27u1xyXzL+XHUwMDEzLELPvnd2zU/aj61WeCbbjfeZXHUwMDFjOXFtTmyFhCy41GO3Yb9cdFx1MDAwM4aRMcY5TCNVw/Mtt92MXq7VqTdcdTAwMDP58Y/++mdcdTAwMDK5hblKk1tOqYKZYDi33H5fq1Xv3Pblz2shS73X/o/blieXXW6lpFx1MDAxNueaa3haqkMwflx1MDAxN1uMLIawZppcYoajXHUwMDAzXHUwMDBiJFx1MDAxMmuJ6yRbalx1MDAxZHnDk1x1MDAwNJdqZEktsFZcdTAwMTJTzVWC4GLELURcdTAwMDWmhFx0JCRGXCIquZpcdTAwMTNcIjBcXOQzXHUwMDA1l1x1MDAxMlx1MDAxYYLdhIL7JkzlnbXaNvZIvfX8UqP9ffWonlx1MDAxZoK5y5Buf11cdTAwMTnDxFwiXHUwMDEyw3RiymF18tLRx6IwXHUwMDA1M1x1MDAwZb9cdTAwMDNcdCCSIFGITeLCNjXLfS56ZiNLXHUwMDFmJEtcdTAwMDFVsCg0mPWAkWTs4Fx1MDAwNyFhwlx1MDAxNcIg+8FapjNSVGxHxjEnxn1cdTAwMTPcwWGnV1vr1WxR92p7a81cdTAwMWLUYDdxwYV7xGRcdTAwMTZzZVx1MDAxOeWjXHUwMDA0ZoQyNiqzXHUwMDFhcWppRFx00qDcKIrLLIrLKFbYXHUwMDAyyVx1MDAxNFx1MDAxMoRcdTAwMTj+VbiAjFwi/1OM0/KLKVsmMf1Yw2CByPuRX1HpXHUwMDA1vailhlx1MDAwNUhcdTAwMTBeplKFlzIluOQojzqds/BcdTAwMGVcdTAwMGZ3O27Y5DWf4P9Wgtn2/1x1MDAxOP7/f/5cdTAwMTn/7VJM7oKff4tcXGa1Zfe9jc79vevBc1x1MDAxZJsxxYjDs3veuttuuO3b6Dmn3Ug54/9qrdfrPN85dmz54XfRc1x1MDAxOajeLcunw3KFt0j15qB9P9jWT9VcdTAwMWZcdTAwMDXUXHUwMDEx1sJCXHUwMDFjJEBrUFx1MDAxNipqXHUwMDFkw1FLcYS4Uf88kJFcdTAwMGZkg3EtXHUwMDEwJ4hKXHUwMDA1LKBYIHyBNmJcdTAwMTZmnHCwX1x1MDAxOeisXCLKKFx1MDAwZtB/XHUwMDA3ZVx1MDAxNEc5TUO5hJlUVPHAnFxuoTx2cOjrabA7wuu3jFxuqn6wzq9cdTAwMWO2f+g81l5Kg8GV6lc6XHUwMDA1RFx1MDAxOUxQkDSQQVx1MDAwMlxuXHUwMDBiqDCmpVx1MDAxOMgq2FYgqFxmsbiWwklcdTAwMDYuZzOXV6eu9Z8kskAxXHUwMDA0XHUwMDEx8L9jLt2q79imXHSt4GC1g53LJ5DaQFx1MDAwMVx1MDAwNFx1MDAwM1xyyZDnXGa84YSFRLDN1p+/46P6y2vp5uTkrNZvlm+OV4ff+/VP8mXffnyy0zmmVd3XqH5zurVV/+k0Wpejd/m4v21cYj7vdU/X6+yKeXZZ4s5O80Bcclx1MDAxZWvO6Vxmrntev9ztdWVdv/60jytcdTAwMGZHdr/1ejaD665vbG3udfqe222ellv18+79QWWQ77oxXHUwMDE1nEg4IexORTjJq1x1MDAxZCOcXHUwMDExYfngXHUwMDFhXHUwMDAy3rnhXHUwMDFhqlx1MDAxOVY8YMB3rlx1MDAxMXpcZtcwZClDXHUwMDE2XHUwMDE0cSy5XGYuMGRcdTAwMWXyl3nye25KXG5cZv5woudcdTAwMDbH0yiGUGNcdHKRZ1x1MDAwZrS4Yiwkp4HYXHUwMDE5cTPYeLLv3FLDdu7hKYJcdOq0vVP31Vx1MDAxZjxcdTAwMWE5um3fuy0zy3TkUmst97btq1dcdTAwMTi301x1MDAxYtls89y63Vx1MDAxYX7h3m00wlr12u47vjdoKHx4tFx1MDAwZbey4WhvN4/S7vTcW7dtt35kPpS5S3noXHUwMDE5WiRcdTAwMDOzXHUwMDE3V0fkeNfZcdCG3nJcdTAwMWNWesH8ez7MYiQsgbmBpTZ7J1x1MDAxMcxcdTAwMWEnV1x1MDAxMcbNVi0nOGHnRViawodRgDVYYFx1MDAwMT7/Ynai3Vx1MDAxNo6ZXHUwMDBlq/dQhCTVkiXGl9BcdTAwMTSrSWzZXHUwMDE0q2BqdThcdTAwMDe1XHUwMDE1o4PnVlx1MDAwN82CXHUwMDA2Ws6Nl0FcdTAwMDJep1uMXHUwMDAxRp4hXG730UHngnkyglx1MDAwM9HQqWSOwVx1MDAxMzFcdTAwMTLF8ke02m7t9OHR6za9LS4rzZ3umX1yuCxoS4lcZjAq8EQ8Nlx1MDAwYv6ZjFx1MDAwYodaXHUwMDFhkCCYXHUwMDE0XHUwMDAx5OdcdTAwMTRcdTAwMGKYk5k+T5xcdTAwMTdGtFx1MDAxOaDj3YVcdTAwMTZwYpxcdTAwMTdA9OhcdTAwMWSjiE5mjolwjnGqXHUwMDBlYEqBKEmdZ7/9bc3JRvX18nnQuz98rVxcP5Sfz1x1MDAxYps/+LLjnLCvinOkMShoXHUwMDEyilx1MDAwNHxcdTAwMDLOp3Cbl1x1MDAxMed44TjHi8E5T1xyrCmKiFx1MDAxMEzl1+furtc9fTh/YD8q9qB2+eBcdTAwMWXU93vLjnOEtCX9fUugNMai+5ZcdTAwMGLBOeVcdTAwMTbxfy5cdTAwMDVY15pcdTAwMDVcdTAwMWE6XHUwMDEz54BcdTAwMDKlSCg54Vx1MDAxM3C+XFzbTdPh3LKsxcJ85IZToDyPaVx1MDAxNVmGXGJcbrDZnudKSEQ5VyRcdTAwMWFjxphaRq1wTVx1MDAwNSCBxyTYXHUwMDBm7Fx1MDAxOVx1MDAxMHGNMCgsXHUwMDFkl2CqLLOjjClcdTAwMTOwsEjyXGZo/sHOeoGYM9xcdTAwMGYpKoExXHUwMDEymJ2T6MGhp1x1MDAwNmzGXHUwMDA11ng+XHUwMDE5XHUwMDEzk1x1MDAwMXamQedSqjiaT0xcdTAwMTCDy8UoqXBcdTAwMTA6tHzvyaN5dup8bqo/mucvIUtcYmlypoiiRlx1MDAwNzNcdTAwMTn61q3dNXahZWK5XGLIX1x1MDAwYkQwjlx0yEj0O21I2e53eEgwmaD6sJRKamaCSVwiNiRpMc2FoFxcXG7EJSimuNBcdTAwMTaJu4eVRZpNXHUwMDEzIbKQUZOevlxiPIdcdTAwMDRjKL9Rk23ILqlRXHUwMDAzZp2fg4axwErzOJ1cdTAwMTNYLk1cdTAwMTDIPyY0mnMzXHUwMDFio0Ygy+RzUaHAeVx1MDAwMVMyIchCTaZcdTAwMWOcU0KB8sGBRvggOKKIscvQXGZMnDy8WjRcdTAwMWR3MqqbXHUwMDAzY1x1MDAwMJ+bbFx1MDAxYS2FhqFcdTAwMDXxrCE8qaWUJpxp4D0gfjZcdTAwMTljZDvyUVx1MDAxMtMmdVx1MDAxNmFcck5cdTAwMDTDLDYkZVx08FFcdTAwMTEhSFItlI5ccikn91x1MDAxNqSUOWmcVGk3n6icx1x1MDAxNc6ERKdSd2kwoJ+w8Fx1MDAxN8ZcdTAwMTFd9lx1MDAwZfxcdTAwMTJcdTAwMTOd5pJiXHUwMDAy+pxKXHUwMDFhUEjIcEVcZnhcdTAwMDQrcK9UZGgzYjplXHUwMDEyMME9ZEC3cJ+EbVx1MDAxYYZN2jDodY4kmNFcIjRHXHUwMDFmVFx1MDAwN2epYvwv1WVQXHUwMDFkMJ1cdTAwMTJYXCKGpE8rivDQtz6MI/CgKcWCXHRcdTAwMDAkIzz27Lm4LjvUOTImuFx1MDAwZiFcdTAwMTJcYlx1MDAwZmDPVaK9XHUwMDA2wlx1MDAwN7QsQTiwRlp8aa5LXHUwMDE1d/OJXHT67LhORI9+cJ2mYOmPeJTjuC5712KJuVx1MDAwZVx1MDAxNFxmMllcdTAwMTNcXLPYjrShOlhcdTAwMTakwFxuXHUwMDE389mowqDmlOIg55pzRXkotyOgOmJcdTAwMTFBhFBgaCuktYpcdTAwMTWlUEGNXHUwMDE3IWawQf07U53ARoGDboOp5ihIrlx0u4HGhFdcdTAwMThhXHUwMDBlqzKhUZe9azsyXCIgONCxylx1MDAxODFcdTAwMWGr+ICMfGLKNVx1MDAxMDTYmSCpX5rn0oXdfOJiXpDpsivwZCrbYYpcdTAwMTQ3XHUwMDE0mz9cdTAwMDC33qm4+mJtt2QzpKve0UH1xCHLTndgLVuKMlhcdTAwMDEttNbxXHUwMDEyPDCqKVHaOFx1MDAwZiS0JTlcdN/ZvKGSavBcYk+w5Xh8K15LRmCIfFx1MDAwNqH1PKm9M8q33dj09m9cdTAwMGUu8MOx7m2fXHR60XloXX5CPui4IFx1MDAxNU1PIcRSMFwiKdP561GTn3rZwVx1MDAwMHaVkXNsttZcdTAwMTTWUTBQMjswpCt/XHUwMDA2lFx1MDAwN1x1MDAxZlx1MDAwMlxmL7ggOcNUXHUwMDAyaYZlYFx1MDAxNn9S7fRcdTAwMDJcdTAwMDNJt53SW6plvVxyz5RcdTAwMTJTKpxcdTAwMTFcdTAwMWHJ/Vx1MDAxY0Pq0VhT2pimXHUwMDBlO3VbrZ/XP3+cnW9Rfbh1tXu3v3FcdTAwMTaSnsycUFx1MDAwNc5cdTAwMWHCgjNj6MTzuFx1MDAxMbFAqM3aXHRKQsXNXHUwMDFmXHUwMDEyyZVl5FFITc1GYcgknl9O6G9S2piUXHUwMDEzXG42XHUwMDBlXHUwMDEzXGazXHUwMDE4265mJv5xLqTItaO+6CTuzdI6WK6TY/lz0jYjoy6Qnv3zQTzvPWwyl1f2L3bP+M6PkvuaN1x1MDAwMixBwyihzUalVFHvUmnELaa0pohcdTAwMTghXHSkIaPKmGnLpIpphsBnZVKHXHUwMDEw/NlY/KohXyk1XHUwMDE34OInpm3j1GIuXGbGqZaAp/nUIC5ByHfk23HBXHUwMDBiflx1MDAxZTNqZ1x1MDAxNuJtlVx1MDAxYvrn6f1cdTAwMTXvo9Lm0428vq/uXaVER2AxuKD+RlxyXHUwMDAwTsVcdTAwMDM2woLRS4UkXHUwMDE4d0orKWNcdTAwMTIxo1xu56xIq5vjubJcdTAwMGK1uLSQRGC9gpJHMtZuXHUwMDAz9D+YrIpKjjFcdTAwMGbVXCJcdTAwMGVcdTAwMGKchUUxSJmiXHUwMDE0PPCk/EdMTalcdTAwMTfFXHUwMDA0YbAhKOPBPf5yzDh9j2HSwI9OSlx1MDAxZiFcdTAwMTRFj1x1MDAwZVx1MDAxM1x1MDAwM01cdTAwMDBcdTAwMWRxOvNcdTAwMWGQbPU1XCJsn1FcdTAwMDOy1mqtbHxfO9xaue/fJlpcdTAwMTVcdTAwMThblGFcdTAwMTBHkEPMiVxuZVxyz9PKwFx1MDAxNlxuf4J1KWB0pDxcXFx1MDAwMePjuVJcdTAwMWLwtVb3pG/v3V90+y+3qJuzNlxmmM5cdTAwMTJUIVxmXHUwMDFmpmQkiOd3WzLTXHTGiWZcXMVtXHUwMDBmlWD4UyCGXHUwMDAyVPAnd94xXHUwMDE1+0xilFQhTmKWxrBcdTAwMTZcZmFwuphGeULMi7b8XHUwMDBm3PbjINnyVyNH54TJ0I5VXHUwMDAxXHUwMDEwRkZdqGJrtHNQyHFL30hcdTAwMTZcdTAwMDRcdTAwMGKEwrtJ4/bOWqdosHnaaWK+o/u8hu36qT1RxdY8fOG0Xm6cgFx1MDAxMYKpXCJSYWzaco3Si99AyWyqK7BQiCaRkU1nLUzUg+nD1mdCIKHYXFw2lZckTJazZ8+Mw0Yj5z633VEmpjNDROk7Mlx1MDAxY0xhWFx1MDAxZZZcdTAwMWbXXHUwMDE3d3vlo1t5vC56Tezeetu18tb+sui6VFxcXHUwMDBiZplcdTAwMGWqwGJUh8UxaJ5cdTAwMDQuh6TgLtNwXHUwMDE3gqn7XHUwMDFjzaL90jvGXHUwMDE5RVRxPp9cdTAwMDTHXHUwMDFjgaOFxXYyRZmQ1GR1XHT2nkSiQFXx0zWvnmzust7JNd5cdTAwMTl0eL98q1x1MDAxZZZdllx1MDAwNWaWVCYzXHUwMDEzgTjQJFx1MDAxNVx1MDAwNV6yIFx1MDAwMtFw08mZhnfGNGBcdTAwMWGWXHUwMDExw1xiZ9JcdHeBkc7udfPIRu758eXm/tOPrdfDjqiU8nbUqVVtedJ9fN1y7dp6+bDy0K72avnc0szrXv/cVJePndetavmlop5cdTAwMGY3Op5em8F1d1jzfKfhPXtej21cXKz1t6+r+1x1MDAxYjO47ln9nG+ePFW0fnTly/WRWP9JXHUwMDFi+a77oe1cdTAwMTbFNqnljjLVncFEMaSxwPmZJlmqlpxpuCZcdTAwMTY2TMNcdTAwMDU2dkKUaYSaP9NcdTAwMTRqv/ThpFJKXHT4nHOval6eMHJyu56ZXHUwMDA2kccoy8lcdTAwMWFcYoWEYtr6ZJlcdTAwMTF2XHUwMDE0XFxiXHUwMDA1Zl5uvD5cZnbdas0kS3TAYKvt1neer86XXHUwMDFkr0xyi3CuXHUwMDE4yFx1MDAxZVJcdTAwMTEj1y9PXHUwMDA2XHUwMDEzlFx1MDAxOMdcdTAwMTYrMp/89lx0241wzU1GyrzxOidVOFx1MDAwN5U1XHUwMDFkXHUwMDFijHbzWUR5cnb/oFx1MDAxOaJcdTAwMWOz9F1GKZng+WpV39b8svx6yG33TrrX3eNLvnVUX6ufLDvKJUVfXHUwMDEz5VKAe1x1MDAwNj723LO75mShL1x1MDAxZMx/56ZCXCJdmyumXHTIncyvzdsv9n6p11x1MDAxYdw8VUtb7kvPtWW5s+w4XHUwMDE3+Gvi3DhcZlR9bquRKfzlZUT579pSiMTjiMPNPFx1MDAwMoJNmM7vYouDu9et9VbttYlcdTAwMWb22NHRXHUwMDAz15flZVx1MDAwNzmVyqL+Zlx1MDAxZVx1MDAxMzJcdTAwMWVvWlxiyidqKWTSOClcdFx1MDAxN1xyf1x1MDAwMsyXa5tpOph/2Y5CY8sxM7bswSk3KcHhaphxMM824JZcdTAwMTTmnFCLaFxyqlx1MDAxY1x1MDAxMU5VvFx1MDAxZZNYXHUwMDE05sK84lx1MDAwNZF5XHUwMDE1ZE7Td2low1NcdTAwMDC+wOxPLD3P3lx1MDAwMFuJtLQweYgmkYBgpufVlifbf1xyjyjSloeyeDH8SFtcdTAwMWUqvnZB5tRtnSajOppu0GBjxytcdTAwMWU2jMdRXbZcdTAwMTW7tFRcdTAwMDdcXGZaXHUwMDA3U1x1MDAwMuSO41SHrbfCNNNjg80najCDfkImSZiBXHUwMDBiNlx1MDAwYj/mt2Y64Fx1MDAwYj9cdTAwMWFm3i1JQ1x1MDAxZKNXkvpcdCmYU1x1MDAxNn/4fD3IMp34KP1cdTAwMDZcdTAwMWSFuCY6NqhQRyGToEziPci+XHUwMDE0231KSyFcdTAwMWF/t9Uwrci0Q1x1MDAxNkVcdTAwMDIu2dvvS8x1XFwrxFx1MDAxOWh6TXFS8zRCsXnboun5Mm1e0fxaXG6Z3r1kXqlcdTAwMWG/XHUwMDBi2ZmOQuCjXHUwMDBiXHRcdTAwMGWNXHR+y3ijslx1MDAxOTVcdTAwMTTKjj6OXGYp3FCI0YTeaSNccoU441+b6T6noVx1MDAxMOXpmdFcdTAwMTiB+kOkgFmXvWuxxFTnR3BcdTAwMTj4XHUwMDBl8dZpwHPc1NFhkMY5XHUwMDE5dbNpKGTkXHUwMDAyXHUwMDE2i8xcIjnkd6Y6gWFZpYalXHUwMDA121x1MDAwMbN41eFIRyGm0YRWXfau7ciQwi2FXHUwMDEwXHUwMDE3car721Po7Xq5yC4zy5aR9PpgkFxuNvqe+LHJb45bOrZrgp7VjkRlsHnIaFx1MDAxNy874Vx0zi0mgSykoJozXHUwMDE1K0jFwtJcdTAwMDLkXHUwMDEwZoMpPlx1MDAxZOdN3VTIJMeBdv9aqbbkcU82XHUwMDBm2nc/9tq7u1xc3D3vXHUwMDFkempcdFNBaUYqKFx1MDAwMFx1MDAxNPSeKKD+k5962dGAjalcdEykNFU0Xp1N8ezAMNumQuDfS4oo/YOSQVx1MDAxN9JTaFxmqS+gp9BcdTAwMTSpYr53IIyPllx1MDAxZrZrL6f2dvVg6/xwjWxekrVB63SiXFyxhdYzcmJpRihg02A0ZrgjhC1cdTAwMGXWXHUwMDA1uE1cXCs83Vx1MDAwNkVaQWOx1ktcdTAwMWb7r5gqXHUwMDA0xt4sSlx1MDAxYb9cbm4j3XxcdTAwMTZcdTAwMTFcdTAwMDRcdTAwMWXTQGhcdTAwMDI0pjvRJPX9UUZl0JwtZ97QWN7a7js/q09cdTAwMWLlq13lXHUwMDBlOicnjVpl2ZUoZ6BEXHS4Jlx1MDAwNEtJQ6/GXHUwMDE4Nk5ilsT+WzGM5C2svDij+dI7XHUwMDFhOVx1MDAwMNF0rvxcdTAwMTODIf3qUVx1MDAxM5Bmnz7tbFfOK+Te7tlPKXGHkV49XCLh3ScjvXqIQGNcdTAwMDK/86xsnqvzWrjH0kRcdTAwMWGehOqBYq9cdTAwMWYm/o5cdTAwMTTOzynZXHUwMDBivaScXHUwMDAyVjBwXHUwMDA20oooykD0ooa5Ylx1MDAxNlx1MDAxNkAn1O/eMF1mSVx1MDAxYaVM1XrpI2tUgG3AXHUwMDEx+tQ3zmZrlVx1MDAxMZH8pHyyydpcdTAwMDaZXHUwMDAxp3TtWYSRkbNhUKKxgSdcIob0V1VyzogwRdK5acHeOjmWZ5WX1/O9mz19vF/bRvpl2WlBSWFcdTAwMDE7Slx1MDAwZf8wRmkkMqlcdTAwMTTSXHUwMDE2NYFLTjRONzRcbrc6yt1mKXgvnalcdTAwMWZFYu7p4nM383M1XHUwMDBiMjNcdTAwMTZp3bNcYlx1MDAwNI7pXHUwMDE2lIQ8XCJcdTAwMTOR94aJzdvX1nmzdnJRum9p0q5eNPvrzdAjpTfwoppgy69cdTAwMDKiWiFcdTAwMTW2Nd+NYKktzkxUgSOusYqJlulfrc2HXHUwMDE4XHUwMDFiXHUwMDBi8YRdoIJccr2wY/4rtkP7XHUwMDFi9fTCzLQ+MdlOSSZWeuRTSmSCQ7nqcFx1MDAxNt3U6yi8NfM1OnqFh1xcoJfe5ZW8e7luujvt5np7bXdflrxcdTAwMWI3XHUwMDFmXHUwMDE0OdZcdTAwMTbzUz9cYtZcdTAwMWPpqJLQXHUwMDAwNSFcdTAwMTWYjiZhL1x1MDAwNkT/1YB+XHUwMDE3QVx1MDAwZaa2VFx0XHTFcIOAXFz+XHUwMDAycVxmXHUwMDEwhSnBoGFcdTAwMWI4ZNGkXHUwMDA35Ew6pXm91Vx1MDAxY9pqXHUwMDFio12GpbIoXHUwMDBlXHUwMDBm4X5wyZXGdUp37VBXiXn2vVxmprRcdTAwMDBcIpNcdTAwMDdfXHUwMDAwm+5Zt7/xeqSvNpq37cbzcU21Nk/yYVOaTFx1MDAxN8JcdTAwMDVcdTAwMTPKXHUwMDBmacfDj+DYfVx1MDAxOHA4odGlROPAWajfvelcdF0nfyw0YbpcdTAwMTmmmCX1v2Wp7W9hgTRlJNywb2bQlFxchpy/STrem9O9ZDW5zC3vI8NOhuO3d1d81e52Tz2YutWPXHK+1SfXeV5P9mOMK/PtXHUwMDFkzEZ6XHUwMDFkf8Py17df/1x1MDAwM/hcdTAwMDbWViJ9 - - - - - avahi-daemonwlo0eth0eth1...go-avahi-cnameD-BusAll CNAME msgLinuxavahi-daemonwlo0eth0eth1...go-avahi-cnameD-BusAll CNAME msgLinuxORMounted dbusDocker \ No newline at end of file diff --git a/docs/arch_cname.excalidraw.svg b/docs/arch_cname.excalidraw.svg new file mode 100644 index 0000000..37a72d2 --- /dev/null +++ b/docs/arch_cname.excalidraw.svg @@ -0,0 +1,17 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1da1PizNb9Pr/CmvfrQ07vvvf55lx1MDAxZG+j6CjiW6esXGJBMyAgREVPzX8/u5nRhIRE7kZnmCpcdTAwMWRcdTAwMDNJOslaa1969+a/X1ZWvlx1MDAwNk9cdTAwMWTv679Xvnr9qtv0a1338es/dvuD1+357Vx1MDAxNr5FXHUwMDA3f/fa993q4JM3QdDp/ftf/1xu93Cq7dtfe3lN79ZrXHUwMDA1Pfzc/+PfKyv/XHUwMDFk/Iycp+tVXHUwMDAzt3Xd9Fx1MDAwNjtcZt5cbk9cdTAwMDWUxbd+a7dcdTAwMDanXHUwMDA1QiUxQOTrXHUwMDA3/N5cdTAwMDaeLvBq+G7dbfa88Fx1MDAxZLvp67U23fJ2qVTsbp9cdTAwMWRtnayuPXm3XHUwMDE34VnrfrN5XHUwMDEyPDVcdTAwMDej6rXxSsL3ekG33fDKfi24saeObU/bq9u+v75peT179eR1a7vjVv3gaXBcdOHWX7cg+rk+/lVgWlx1MDAxOcdcdTAwMDCVxmjClWDh5dojUI1bXHUwMDFkY4RUSnHCXGaLXHJtvd1sd+3Q/q9OdJWQcHBXbrVxjSNs1Vwin6nXw1x1MDAwZjz+vlq8y1x1MDAwZZN4cFx1MDAwNopcbqHCXHUwMDEx3Hj+9U2AXHUwMDFmwVE6ikhDiFwihOOTXHRH4VxyXHUwMDFlXHUwMDA3KG5cZlOMmNd37Lk7O7VcdTAwMDEy/lx1MDAxMz6Ernvr7dhdWvfNZvROtmq/7+TQXHUwMDFiV/aNzVxiyMJD3Xdq7i8wgDQgiDBEXHUwMDAwXHUwMDBiXHUwMDA30PRbjfjhmu1qI8TPYOvPf6bALWOpuDVcXGrJONNj4/bbaqV847cuflxcSVXoPve+XzdcdTAwMDOVd9wqRVx1MDAxZCGMMEwzZijEYVx1MDAwYsThXHUwMDA0XGY3VHKIXHUwMDBmLEQkXHUwMDE4XHUwMDA1VZqNWk/VxSjgMkNcdTAwMWNlJFx1MDAxOK3wuVx1MDAwYj1cdTAwMDK4QIRDmFx1MDAwNEa5JFJF1eQ3co2gVFx1MDAwMlx1MDAxZeTPXHUwMDAwrlx1MDAxMql6a0fCXHUwMDA0XHUwMDAzPjZwi9urlS1cYmi1+fhUYb09fa9cdTAwMWbvclx1MDAwZVxcjmh0qFx1MDAwMsRcdTAwMGIwgfBbut5yjZjEXHUwMDEzI0eookROpbdcZlx0hsOPyNBHhy3uPlx1MDAxMrGCpFwiloNcdTAwMTBcdTAwMTRAju9cIvRcdTAwMGba3cpqt+LKalDZXW3USY3Xp0EsX1wiYkEox9pWLYFTxvkwYvEpMMdcdTAwMTBGiUHbzUg6YsngNZ3WkiRIQYNjnTSFKMafXHUwMDFh4iBcdTAwMTVoXHUwMDE4lMExzY7RXHUwMDE3tIR4ob+3/FxcXHUwMDBldCM31e1cdTAwMDZrfqvmt67ju3itWso7TbdcdTAwMTest29v/Vx1MDAwMIdx1PZbQfxcdTAwMTOD4652u+3HXHUwMDFiz03cXHUwMDBiPHLqe1x1MDAxZHu40Fx1MDAwMbev8H8r4ZNcdTAwMWL88fr///zz9qdcdTAwMGKJR1x1MDAxY+7+Jfp7KlMkIL7xhdhcdTAwMTRcdTAwMTg3XG5d0Vx1MDAxMHVvXHUwMDExe6eoXHUwMDFlXHUwMDBliiXRpOX6fuu2v2VcdTAwMWXK3/NuitBcYjlESKGMQZ3XcddcdTAwMWa3OlpcdTAwMTBEK/o2XCLdh1x1MDAxYYfXIy2RJo4kglx1MDAxMqY0iojmIVFDS8RcdTAwMWTgglxu9O452quEIVL4nmZazMF9SnKcLZfj00NcdTAwMTlcIkFTXHUwMDFjy+h2XHUwMDEyKShcdTAwMWJcdTAwMWbL1f01cenxvVx1MDAwM+++8lTo9y91r9TOO5YpXHUwMDE3iFx1MDAxNMRcdTAwMTBFe4WynzBSXHUwMDFjsYaOXHUwMDE1XHUwMDAyjVx1MDAxMz6bkarXvaoxSTzDqFx1MDAxMEDwOGbBSEooXHUwMDA23PBOoH3dJ9w7grTA61x1MDAwN6/XXHUwMDE2gUWLrz1+g8Pq03Ohfnx8Wuk1ivWjr6+f+/nP6MP+2vl4u33EyqZnSLV+srlZ/eHVmlx1MDAxN8NneTm/a1xyzbjHPVmr8ktcdTAwMWW4RVx1MDAwNe3txr7u31e8kzlcdTAwMWP3rHqx0+2oqnn+4Vx1MDAxZZXuXHUwMDBl3V7z+XRcdTAwMGXHXVvf3Nht91x1MDAwMr/TOCk2q2ed2/1Sf7zjvli8bMXhIahmVJwhXHUwMDFjXGblzEx864vYYFBuNGFcdTAwMTPkzEaDKu9aQ6kjrNYww0GLMNPyW2ukWYLWcOJouzvDh66EXG5cdTAwMDfxqjw0qTxaScCo793DtolQXHUwMDFhjqrdXG5O/OdfZm1o65Z76zft3Vx1MDAwZi9sXHUwMDAwX0ujXHUwMDA398Yv1Fxc71x1MDAxNtE79O5q079uXHJcZlx1MDAxZY7a61x1MDAwZWE98Ktu8/VcdTAwMDO3fq1cdTAwMTa1vFU8pYvxYndnXHUwMDFjg9nu+td+y21+z1x1MDAxY5GNP4uvsY1DI6joeYPg1FqOqVx1MDAxOMtofONcdTAwMGJhmZHS0PHpen55SI92vG2PrJtNz+OFJ1x1MDAxMN/yTlebhJOIPGSksSmPXHUwMDE4XUFoR6P7YHPQXHUwMDE4zi+GrlxcOobhizNkXHUwMDAwuqshNbPoypApRuswZ7Ygti7IXHUwMDEyLsBizaZcdTAwMDWPzTZJ0YCmV1x1MDAwZjJcdTAwMTQgaHfS6D802jjXh8+4QI5HiVx1MDAxMCe5XHUwMDA0zlx0VeMnVlt+5eTuPug0gk2hSo3tzql7fJB3mjNJPijNNedCciXfleczeOi547lcdTAwMTfcLJnnw2dcXCTPaaoxR0POXHUwMDEw/ZFs/Vs8p+vl54vHfvf24Ll0dVd8PKttfFx1MDAxN3nnOeVcdTAwMWaV58RcdTAwMDBooGTh3ndcdTAwMTbPZ4iY88hzWDrPYTk8XHUwMDE3qSk9XHUwMDAwjlx1MDAwMOecjW/Q/Z2gc3J3dse/l9x+5eLO36/udfNOdEK0o1x1MDAwNik9ZVx1MDAxNOfxlN5SiM6EQ1x1MDAwN7srSVxyhvuhic4kOlCpNaVzSPBNT/R8pZpmI7rjOMvl+dBcdOdA8193eFx1MDAwNM8j2aO42y44mlx1MDAwYjVcdTAwMDHLs124nLJcdTAwMWNcdTAwMDRzpNBSXHUwMDExJoSm8dllXHUwMDAw5li7KVxmk8h0XHUwMDExXHUwMDFi2bxcdTAwMTL34IBcdTAwMTVcdTAwMWHENaBVXHUwMDBmpSZSXHUwMDEy4VCmXHUwMDE0MC6FXHUwMDAwXHUwMDEyqWN5JT3qXHUwMDE1U4YtZCpq9unm6dhcdTAwMWGbbo5cZux35eU4XHUwMDE5sFx1MDAwMb+r93aUXHUwMDA19N2kZkwwja5cdTAwMWLwsP7F3i+3Y30rx870XHUwMDEx1E8jXHRcdTAwMDVIXFz50DR32oiyQ9joiPDRo/VcdTAwMDCltDKcROzM64iUw42Qklx0JYlQKO2JIc15fj0uXHUwMDE3c51iL6Si3b5cdTAwMTI4XHUwMDBmXHUwMDBm9yX6e2KhXHUwMDAzpuJbX1x1MDAxZFx1MDAxYVx1MDAxY1xmXHUwMDExXHUwMDFjvZqxpS7bi82t1NFBWVx1MDAxNYBcdTAwMDRtRFLqKFx1MDAwMs1QgjdcdTAwMWUoi5f4zEfqJHFs5Vx1MDAxNpNcdTAwMWF1gSCWR0idLf7C97TUKMyQVDqqqfXJyELmLz+R0qFgaI7Gy1x1MDAxNoByXHRJqWOO1oZcblRBXHUwMDA1XHUwMDA2XHUwMDE0n07qsqP4uPhcdTAwMWFbL0vASDSm4dN/XHUwMDFkknYkXHUwMDA2qIRSopiR2nxsqUtFu33FcT43pTPpRa7MlrlTNX5xdnb6PcdCZ4SyV4pcdTAwMDZGsfB+RJw6wlFHQGNopWNDm5PSaVuWiKEhR7nF84zI0XCwlbBSXHRBXHUwMDE0upgyUUJcYuiDKKa5+Ct1XHUwMDE5UodKp1x1MDAxNSFSUWpnv3U401x1MDAxMHHqMHhG6EsukY+cisSljyV12fOPQ0PC81CqXHUwMDA205taqNCKRYaE2EOFVohccjDEyFx1MDAwZi11qWi3r1x1MDAwNM7nJ3Wp2WiwhVhcdTAwMTjc8fFnl7MzXHUwMDE2OdY6NDCINry/hifS0Vbq8LlcdTAwMTCN8YNcXFAtXGKaOa1cdTAwMDVcdTAwMDLdYFxizVx1MDAwNFxyPe1Q6qhDJcXYi0iuiTE6sVx1MDAxMlx1MDAwNeNrTlDvclounVx1MDAxN6mTaFx1MDAxM0BcdTAwMTl8sMqm/kfpXG63LrxcdTAwMDZcdTAwMDJcdTAwMDKfypROXXbGdmhEdt5cdTAwMDeNlHVi8EbJpPgyXHUwMDA0KDBhXHUwMDE04ehoXCJUP7TSpaPdvpI4n1DrstfdqdR0XHUwMDFk4lQjXHUwMDFl5Fx1MDAwNL7dWrvkm/PVnYKLUUI5ONwvXHUwMDFmezTveke1cDTj+Fx1MDAwNIw0SINhvdNcdTAwMWHQq2ZUXHUwMDFiXHUwMDFiPdBcdTAwMTnzda6o6VGF41SMcOZGXHUwMDE12ipOcYhiXHUwMDBlXHUwMDEz60sstF3fXGL26vvncHdkulunkp2375pcdTAwMTc5LFx1MDAwNGXphaBcdTAwMTjNSsqpmqBcdTAwMTJ09FXnnVxmXHUwMDFj5Vx1MDAxN3FcdTAwMGU2K6jBxMnA6PzIkG79OUpcdTAwMWW+KCq8XHUwMDE0+CvJjVFzVJJcdTAwMThcdTAwMGWKq9m58VFKQa/bhV+1l9VcdTAwMTZe04KKQd9cdTAwMTD1+ERT2pjeqVRMa26A0PFcdTAwMTOxnWbzx9WP76dnm8xcdTAwMWNsXu7c7K2fmmlou9RcdTAwMTWNmjmMgFx1MDAxNFx1MDAxY/+rklx1MDAwNdyEOshaoFx1MDAxOK0yXHUwMDFhXbM9x1x1MDAxNY1CO5a0Ulx1MDAxOWbTqZHAIYO16O5wyeFPXCLtRmFccv3c0VxcXdDkb+yUi5z+pZDuUFx1MDAxMptN0XKC8uxcdTAwMWZ38nH3boP7orR3vnMqtr9cdTAwMTf857zbUIyjXHUwMDFjdNONzcUqXHUwMDFkXHUwMDBmoG1I43BtXGYj1GJfxUa2sOXF3DhcdTAwMWFQXHI5wdCeK5Ns3aCUXHUwMDEx0nr9i/Auc1x1MDAxZTA3izXz4+T2UvRIYeOhrq5uy7uXKXNcdTAwMGWG205cdTAwMWOGcC64IGGhzspLfCrxI/joiUKPSVx1MDAxYq1U4tqXtrJ5oZHr0KeT+Fxud/9cdTAwMTL9Pal5p+m9YdDYaYqInWAxaPaDzqmkUCFcdTAwMWSiXGLlgDaeqESPXHLNXHUwMDFkdMk1U1x1MDAwMkCo2SZa0yRFK0fYXHUwMDEyUYqOv8LBjFjcXGbMrlx1MDAxM2NAXHRYl4TzhMTYJFx1MDAxMseQeuHdYV6xN6KQLNuqXGZh8p1cbslcdTAwMDBcdTAwMWPGXHUwMDAxbyXjXHUwMDAyXHUwMDA01ZGi4De8i9Vmc2X92+rB5spt73q5XkbKqcfyNmA6ZUj1NThjUlx1MDAxM1x1MDAwMuPnrlx1MDAxZUuVvlhtdo577u7teaf3dE06uV9cdMaJdCTTXHUwMDA0wNbQqti05KBplIVcdTAwMGb6XCKGXHUwMDBine5qTN17R4/w8lx1MDAxOapAnPd27oQrmEeFxTv7+Xpoa1x1MDAwNlx1MDAxM/f91n1/uVxmjJ1yXHUwMDFj5lGVyby0JkImnXlGXHSr8ONXeTZPSH/jpN1cdTAwMDCxbXqiXHUwMDAybvXEnWpx1lx1MDAxMiNuJihaZLBFf1x1MDAxYcD2lVx1MDAxYabeoIlcdTAwMTAwojWaa1x1MDAxYZlUfO9cdTAwMWVCiktJpOaf2cf/20Mo8lx1MDAxMCabXHUwMDBmMulcdTAwMTlwbcFcdTAwMGWT9Fx1MDAxMDq/2S1cdTAwMWVeq6M12W2Af1x1MDAxZGxVipt7OTepdk7bQXvJKEhmZKTZQdhDXGJcdTAwMWRwxWzUXHUwMDAxdLbofVFNhDgjTFx1MDAwYrGYesbZm1xizW8mJ1x1MDAxM8s0km5MrDjSVGKUXHUwMDAy44P54UqUjzd2ePf4XG62+23RK17rvPdmtHWajtK2XHUwMDEykyBcdTAwMWXYKCOFQaOk0vY4mS0tPGtcdTAwMGYhjSOcS7vbJU5sdq5cdTAwMWGHLvHPji429lx1MDAxZb5vPlx1MDAxZrRlqTBu55xK2VXHnfvnTd+trFx1MDAxNVx1MDAwZkp3rXK3Ml5cdTAwMTSaedyrXHUwMDFmXHUwMDFi+uK+/bxZLj6V9OPBejswq3M47jZvnG3Xgscg6PL189Xe1lV5b31cdTAwMGXHPa2eiY3jh5Ix9756ujqUaz9Ybbzjvti7ZclN6lx1MDAwNJTKWlxuwG3Rxlx1MDAwNP7waFTlXFxphLFrMFBphMRQXHUwMDE0Yq33bFx1MDAxMeXilWaaXHUwMDBlQtRmtGgkifthXHUwMDAz0zw1XHUwMDEwesNYvntcdTAwMDMhnd5cdTAwMDNXMoko5Vx1MDAxM1R53vV3/HLF1ka00WOr7FS3XHUwMDFmL8/yzleuuEOF0Fx1MDAxY5CSOublXHUwMDBlllwio1x1MDAwZkptaIuu0mLq2afsLVwijLBcdTAwMDUoi+brgkzhXHUwMDAyTNZsavCZW1xi8dQsXHUwMDE1XCKbU7D1pWPT/KL4fCBcXP9G+Vedo1x1MDAwYrF5WF2tXHUwMDFl553mipqPSXMllVwiXHUwMDE4ZS+8MGRBLnrueP6ZW1xiqVRzjs63ppTqXHRcdTAwMWF4Prl7hW6zX38oXHUwMDE3Nv2nru+qYt6bXHUwMDA1M/uVM1x1MDAxZpHmNmBg+n3bisxcdTAwMTAv55Hkn7V/XHUwMDEwJenZPGnbXHUwMDAxilx0XHUwMDE2Ksj9m+fNtWbluVx1MDAwMXe7/PDwTpiLYt5JzpRy2CCZx6VKzjgtheVTtVx1MDAwZlJcdTAwMTj0M1xu+l2LPvKVZpqN5p+3e1x1MDAxME0v6TCUK8kmWWqe7b7llOXIYIdcdTAwMWGDllx1MDAxYyWN6eTyS+ow0HbBkiR0Uesv59E+SDHkvYzS4vPNNKdVk2bnv1beoX1QdvRcdTAwMWFcdTAwMWTRcPsgynhyRehQ/yAmP/byy3dqXHUwMDFmxEj6nIFcdTAwMTSMaEEmmJ3MdmJzK3W2zFHh1VKtXHUwMDA1JKVcdTAwMGWcX8vQbEtccr6YSYM5tFx1MDAwZrLVrlxcXHUwMDAymUdcdTAwMTjzqZUu2j6IRbpDr4xqXHUwMDFmpPGe8uTFj9crLTOGj8tv2EBIXHUwMDE4mmyWXHUwMDE2aSDEXHQ62Fx1MDAxZrxZ2rt0XHUwMDEwYjK1g1x1MDAxMLJHSqLNXHUwMDA0XHUwMDEzpNnp91x1MDAxY4udMJpcYo6m3kS/XHUwMDE1NHTsKFx1MDAwM/udg7bJy6yFRYtrISS5RlO5oFKNz6J2y+sglD37ODSkaFx1MDAwNyFcdTAwMWVB4MrIXHUwMDBlQoKLjy1179NBiMnUXHUwMDBlQpzj2WxcdTAwMTHE2EqXnbTIsdJccuZvOMZcdTAwMGXJTmkoc8IuXHUwMDE0XHUwMDAzXHUwMDA044Kcuvn0XHUwMDBmsrCgXHUwMDE06DxqQz6z0lxyNVx1MDAxMIrMua6MbCDEXHKZ0qvLTtpcdTAwMGVcculvXHUwMDA3oajULbSDXHUwMDEwp+l9/Ym2Xf3Hj2I7nl84ciuSnVZcdTAwMGVlqb9xwFlcdTAwMDfyLndSIL5cdTAwMTVKhZLMXGKuXHUwMDEzizNBOkZcIlxuXHJcdTAwMDY0WsymeDM3XHUwMDEwspVxaNo/Vp0tvd9Vjf3Wzffd1s6OkDePu1x1MDAwN4HOYVx1MDAxZCjLqFx1MDAwM5WMXCI6yFx1MDAwNN+tPvqq885cdTAwMDZivytcdTAwMTJ1SFx1MDAxYqZZcqUyg/mRYb5ccoQwule2m+1cdTAwMWZUXHS6lP5Bb4h6XHUwMDFl+lx1MDAwN6XXiSGI0XchXHUwMDEzVIPS1adcdTAwMTN3q7y/eXawSjcu6Gq/eTJVmdhSVzNcbqQlp1xmqWkpmvDaXHRcdTAwMDFHoGuBIZMwXHUwMDFhXHUwMDE207Nkqlx1MDAwNkLonWtiuzwt/Lvm8kPbz91BiNFcZiNKgFx1MDAwYkYn+Ea44uZWz/tRflgvXu5ov98+Pq5VSnk3ooKhXHUwMDExpVx1MDAxOJhQUIpcdTAwMTlcdTAwMTNbTYExXHJ3XHUwMDE0XGa+vENcdTAwMDCVS1td/HZcdTAwMGIhgUy0XSr/xKmQXvmwgVRzT1x1MDAxZba3Smcleut23YeUWYehXHUwMDE2QjRcXMG5MrKFXHUwMDEwRdwnrv1vXHUwMDBioUlcdTAwMWGFQHxr+Fx1MDAxNe/aLvmYoEI0+znnVFIktZJBjKaacbzguF+uuVx1MDAwM1x1MDAxMtWEXHUwMDExosHMVlay2Fx1MDAwZUJcdTAwMTKdXHUwMDAzQci7frdstlVcdTAwMTmC5N9cdTAwMGVCee4glP6llIhBu1x1MDAxMGyCXHUwMDFhXGZ38/hInZaens9267vmaK+yRcxT3nVBK+FQhT/xXHUwMDA354zFpiW1xnie2VlLQVxypDtcdTAwMWFcdTAwMGJvIITXYlx1MDAxN49cdTAwMTK58Frxv1x1MDAxZIQmY95cdTAwMWJcdTAwMWSEUk1yaqdcdTAwMTF0evF2T5Ao27h+bp41Ksfnhdumoa3yeaO31sg78VxmJc5ggVx1MDAxMzOa6Kgr/dvHV8ZcdTAwMTHcTplY4EF6OTd49t90aWPbztvYXHUwMDE3ZYpgODFcIlE2kozcdkCxRU9/XHUwMDBlXHUwMDE5XHUwMDBmj5fLxOj5XHUwMDE2SMNoS7ZcdTAwMThcdTAwMGbttyGgM1xi41x1MDAxN+ZcXFxcqpunq4a/3WqstVZ39lQhqPt5J6Lk0pGc2XlJbks24lx1MDAxNlx1MDAxMKNZR6DbLG1cdTAwMDc7km5cdTAwMDJn4eHgm1x1MDAxMq1jja6a0EqPKLhcdTAwMDbjJPr8SLtcdTAwMGWDLcFcdTAwMTdeuLcqh7Zm0PBcdTAwMDCHhudfqV0tO1x1MDAwNTb6zGP5pmIqavL0trd4z/VgSn1savqnnd7686G5XFxvXFy3ao9HXHUwMDE13dzI/+plqVx1MDAxY0NcdTAwMDUyVFx1MDAwZibrk1OrXHUwMDE4tb44p5DR31x1MDAxMoyCKp2Om4q8Rc1cdTAwMTHrXHUwMDFhgXNgMI9p1lx1MDAwZpOUtsfqLpeS8XNOkpb+8jtcdTAwMDb/6nY6J1x1MDAwMd66ry+Zva9cdTAwMGa+97g2OoCxMcyX31S2pPFcdTAwMDaZyp9ffv5cdTAwMGb5g8dcdTAwMWQifQ== + + + + + avahi-daemonwlo0eth0eth1...go-avahi-cnameD-BusAll CNAME msgLinuxavahi-daemonwlo0eth0eth1...go-avahi-cnameD-BusAll CNAME msgLinuxORMounted dbusDocker \ No newline at end of file diff --git a/docs/arch_subdomain.excalidraw.svg b/docs/arch_subdomain.excalidraw.svg new file mode 100644 index 0000000..594a952 --- /dev/null +++ b/docs/arch_subdomain.excalidraw.svg @@ -0,0 +1,17 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1dWVfbyrJ+z69g5b5u63T1WH3eXHUwMDAwM4Z5XGbce1x1MDAxNsvYwnY84oHprP3fb7WTYFmyhGwsXHUwMDEw7DgrIWhsS/V9NXb1f78sLX1cdTAwMWQ8dv2v/1766j+US816pVe6//qX237n9/r1Tpt28dHv/c6wV1x1MDAxZVx1MDAxZFlcdTAwMWJcZrr9f//rX+MzvHKn9fMsv+m3/PagT8f9L/2+tPTf0b+B+/T88qDUrjb90Vx0o12BW1x0XGJv3eu0R7dcdTAwMDXNwUjLXHUwMDE5fz6i3i/S/Vx1MDAwNn6Fdt+Umn1/vMdt+rq/cyo2e9tcdTAwMDP+sL7MXHUwMDFletVcdTAwMDZcdTAwMDOzPb7tTb3ZPFx1MDAxZTw2R8Pqd+irjPf1XHUwMDA3vU7DP69XXHUwMDA2NXfv0Pa4s3qdYbXW9vvu67PnrZ1uqVxcXHUwMDFmPLrrsPHWn88geNxcdTAwMDP9Vlx1MDAxMIjM41x1MDAwNjhIxkHp8a3dXHUwMDA1uOVoPKmNYZpcdTAwMDG3XHUwMDE2QyNb7TQ7PTey/7lhWGZsPLbrUrlRpVx1MDAwMbYrgWNubsZcdTAwMDfc//qymllPaMmEXHUwMDAww5Uy+vmIml+v1lx1MDAwNnSIoEHQXGIsY4YxXHSBV9L3R29DaaVcdTAwMTDUeHTuzt2tykgu/jN+XHUwMDAzvVLL33IntIfNZvAxtiu/XHUwMDFl48SOa7djLSBi40tccruV0k9JXHUwMDAwbUExZZlcdTAwMDI5fnrNertcdTAwMTG+XFyzU26MhWe09e+/5pBaqUWs1HIhXHUwMDA1s1x1MDAxMHhRL0ntnjrR90cr51fVu9Vqdf1cdTAwMWPl5TbkXWqN4Z5cdTAwMDVcdTAwMDSGTDHDMSy1lntohDVGXHRtQMZKLVhcdTAwMDNlniy1vrlR01x1MDAwNFeA8pTiJJdcXElNN4pcbi4w5TGhQXCpXHUwMDE5XHKE6bDkguSoiIZcdTAwMTT7Z8guWFx1MDAxZCu79DCt1vQsU8vu8KBo73uVh4ejnfpcbjs9uyhcdTAwMWVcdTAwMGZPcy67Ulx1MDAwMnhaMGm4IOlhMiy7mTOuRJJKknxE4Ibkd1x1MDAxZcZFMNZwK/mnXHUwMDExWzp9usRcdTAwMDbUSlhiXHTb9Dq5YKkltm8uturtWrld2sPN4sr+nVxck3OxbZjTspRYUCRcclx1MDAwMjRjQtGTXHUwMDBm21xiRH2eMMRiXHUwMDA2JVpcdTAwMTYvsWz0mY9tWVRIif89XHUwMDEyTW1IiulfhLCQXG7kklx1MDAwZbJcdTAwMGLg1t/iMlx1MDAxNlx1MDAxOP5ry99vI7uBh1rqXHJW6u1KvV1ccp/itysxe5ql/mC102rVXHUwMDA3NIyDTr09XGJcdTAwMWYxuu5yr9e5r/mlyLOgK8fu67rLjU1w91x1MDAxOf9vafzmRr88//8/f718dCHyisenf1x0/pxPXHUwMDE3KVx1MDAxYm9HIdNcbs1cdTAwMThlL1x1MDAwMVx1MDAxYr9dWHt6qtllv+eflYaFXHUwMDAyirxcdTAwMWL/XHUwMDEyrPJcYt3MXHUwMDEyTDTasCqS5Fx1MDAxYlxizlx1MDAxOVx1MDAxYSTcg1x1MDAwZY1sNmBPVUV0XHUwMDAzzUhcdTAwMGJcdTAwMTJ1MCVQjlxy27Eqklx1MDAxZUjFlbVCksKKaFwiXHUwMDBlZOJJXHUwMDE0mYBcXLwtyOeXZY4yVpYls6ilhfRa6mrnpN/Y2vkurm/Nelx1MDAxZPhO73BL5l2YuSR7nGxcdTAwMTapteBcdTAwMDZ1SJiVMFx1MDAxZcOR2UMmj3qlLPtla6PiXGY4xZZSMiyyZOmiXCI2eyeJfT5nfHZAzFx1MDAwNv7D4PmbXHUwMDA1ZOLHmir3Li+HVyfL5/Wjp43Kxmqx//X5uL//mn7Znyffk4tcdOud04MufD/ePblcdTAwMWFWardcdTAwMWKTd/l9/5JTM2mve944bmFp5bI1KNTPdleh9MCuXHUwMDFlXHUwMDE3cF053D7sdm52O1x1MDAwZlx1MDAxN/5T/XH13jxu7y/iuteFleW188Odp1x1MDAwMtbxsL1cdGvbLN11f+u7t6KbXHQ5mIiZmVimQdJcdTAwMTbkwNn0TDNdqvLONJx71jGNizlcdTAwMDW55Fx1MDAxN9Monj3TSOahO13QSzcqwCXPvMMjvEP2k5GkbD9UrGE8qk57cFxcf1x1MDAxYdnebGLreqlVb7pnPzZcdTAwMWZG0utQdFeq1Vx1MDAwYpWS31wi4Z3Yu9ysV508fy3TqP3ehKhcdTAwMGbq5VLz+YBWvVJcdGrdMt2yRN5ibyuNsuz06tV6u9Q8SVx1MDAxY5HzPjefXHUwMDFkXHUwMDFij1x1MDAwN4Si749cXFNcdTAwMTc7mVx1MDAwYrAkKrGAXHUwMDA1I0hvzmJcdTAwMWGUyn5zV5vtvVxy9I3Re+apy1bzXHUwMDBlWFx1MDAxN4ezxlx1MDAwNeKQITNjs/8nYLWkJ2610i7ugvA6XHUwMDA3Nlx1MDAwZbFSe1bQR1xuQlx1MDAwMdnTY3QmIFYwo6SAzFx1MDAwM9tcdTAwMTnpwlxmdNbr6OC+2WExNND0b1x1MDAwNlx0JDDodONcdTAwMThgYrRhuE/eMUOYc1x1MDAxNlx1MDAxYqdcdTAwMTJagWBWp09l7fJTdu/j1fWgutXQlepcdTAwMTHULOZcdTAwMWTlQrOPiXLOjLCMXFzYzKOpXHUwMDE52ei5w7k/qL0xzifvmCXORaw6R4FCXHUwMDFipdMnUFpPx8enl0Z+W3lcdTAwMTT1++Ph7vZcdTAwMTWovOOcy1x1MDAwZopz4mEl6Fx1MDAxZL2rOn+Fy5xHmMObw1x1MDAxY95cdTAwMDbmOj6gp1x1MDAxNdLTljNEp8/aW43iMWzuNzbXLn5091d6ULR5xzlj1lx1MDAwM+dmXHUwMDFiXHUwMDA2kitcdTAwMTWO6L1cdNCF8vjodKO5XHUwMDE1Vo7d+Vx1MDAwNKBcdTAwMTPOXHUwMDE1XHUwMDFknn1BSqLdnqtY0+uA7nne2+J84oZcdTAwMGKA+c8nPFx1MDAwNedSx5rtnFx1MDAxNJwyiDOkl5NtuJzinKjMY2BcdTAwMDClMlxci0h62SpP0WY0WmiuRGhki4rcg0dcdTAwMDNcdTAwMTCcJFx1MDAxYkitj42KQE2Ex4UxICTBXHUwMDFiyDFcdTAwMGajXHUwMDFlwHJcdNJ+6nxzYGC/ii/TRMFGXGIvXHUwMDBm3ShcdTAwMGLM01x1MDAxYYVQXHUwMDAyueH0uEzgoGqp6zDhuUxcdTAwMWZDJHZnXHUwMDFjIPLNJ/LccSNK9mGDI6JXT1hcdTAwMDNSXCLGSsbFmOSfh2Q8acmwXHUwMDE2ymimhI5mWVx1MDAxNpxgXHUwMDBmXHUwMDEzxkJz7IVYaXefiJyPL/cl+HNmqiOLJdakMdZcdTAwMWGypU16qku2Y3NLdcJjWpE1g06UolQnybNcdTAwMDEuhHG+XFw2qVx1MDAwM83Iqlwi+GkkXmBcXE9JXHUwMDFkXGJX/UX7UKN2hbVhpiPRcbyyiHjFpyY6escomVx1MDAwNeerXG6jo7RcIjxEVz5qjVN/Usvol0/Fdcl+fJh9LdEvXHUwMDAx31x1MDAxNVtabiODQk+7JFx1MDAxMefMkFxyrjh+bLKLlXf3XHRL+qK4jrRELNdcdTAwMDGgIJCb9DXaySH4XHUwMDFjc51cdTAwMDCFXFxbwSBcXKLtrDpJXHUwMDE2XHUwMDFkMKnonby2tiiG6tCzLkNKZpmmp86mRGnIh3RIMEoxXHUwMDAzXHUwMDEzXHUwMDEzIH4nTelVIdHKXHUwMDFmrkvgOqI6NIxpwzm3TFx1MDAwN1xu51x1MDAwMkZcdTAwMWR5z4L0htSERtKA8zFdcv5xYkh0XHUwMDFmzlxy0Vx1MDAxZMlcdTAwMTcprGlDQuI3svpINMhcdFFcdTAwMWab6GKF3X1cImK+OKLj4a3jwjOlmVx1MDAxM9X0carkmEVuiY57aKVcIlfeciPD8eiR/8rIcdWWIVx1MDAxN9kwXHUwMDFkkJZDdExqlbMt+djUXHUwMDFlU1x1MDAxZPe45uR8MS2RWYvR2ShcYpZsXHUwMDE1s4iy/s9Mdlx1MDAxYbh1Llx1MDAwYoBxo1xuWFx1MDAxNGNcdTAwMGaWMzcnjXhcdTAwMDUlKY/5yC45bDsxJFwiOWFcdTAwMDQ6K4aelI6SnfCUXHUwMDAwoaxhklRcdTAwMWFx44cmu3h5d5+opM9Id8nz7zDWjyVcdTAwMTdXc2FVeje2US+dXHUwMDE1XHUwMDFm2/1cdTAwMWSEp/rF/drqt9uV3GfayVxc9ogpyDtcdTAwMDDUxoZtO8GYx5m0ZFxmSIbydYxXUlx1MDAxNZxWOs7VXHUwMDE0a25KrS1acCFUnlx0pWVWbdvYMrvtu6ftw/7+0ZDvfCv6V0dPaatM29/Wj69cdTAwMGXuV1x1MDAwYo2O2Li8XHUwMDFiXGZcdTAwMWLLXHUwMDFi9Y9VZSrjq0ytsZJcZk1I7z5Nf5h5x1x1MDAxOJlsgmtDhisqXCKzMMZALVx1MDAwZWPxVoUkXCKlXHUwMDBmJ72haVx1MDAxNKmyX0BWkFx1MDAxM5WAffHp60yrncLPws5ym75TRpWmL+iKcFx1MDAwZStuTO9ThIaadiozQ6lpXHUwMDFiloV/PFx1MDAxY55fi71S6+50vXjz/WpcdTAwMWXYvulcXElcdTAwMTSedm5cdTAwMGZqoVx1MDAwMnbY85SqkdPpsvdCITOhkS1mrqRCz4GWNLMgh4NcdTAwMDes+STUgiBcdTAwMGKS41wipqV8XHUwMDE01Fx1MDAxNlx1MDAwYivDflxmWDNKLIdumWVqWcn4mcuKWecopMZi96DW983xStcv7T9cZta6/vfbJ513XHUwMDE1XG5cdTAwMDY88onIT3NTXHUwMDBiXHUwMDAzXTN+qVA0ntVAXHUwMDBmgtxcdTAwMDeueGhkXHUwMDBimrfsXHQrlJtcdTAwMDdiXHKJNunyKTpUWlx1MDAwZoH8RDKoXHSuxkb7RCBcdTAwMDfOdU6nMqNcbrD64lx1MDAxZHNbNjeNYu1+5/DMv93d78jz7v1UL7hAbrCUXGasZVIqSa5fNOOi6Vx1MDAxMGbQNVxyQeRE1JHv/maTqDN1kFx1MDAxM+TOfaJcdTAwMTI3vuCX4M9ZLVx1MDAwMGFiXHUwMDFifEhERlx1MDAxYSZ9dWryi88p6XC0LqXk5odcdTAwMTEuTMRu18aTQG6zS1xykrGQXHTpXHUwMDAwXHUwMDA38s/p4+aokf6HKeUsQHZcbiM3gjNw45AyXHUwMDFhXHI0WnIlrM7cjn+WvSnObLLmmZDJeZzZebkrYG9cdTAwMDA5alx1MDAxMuhZXG6pQJFcdTAwMDXFJ45KsD+O/G7zcanTXlrdW95dW2r1q29rjCTdP5VlXHUwMDAyc3FcdTAwMDSI+CZAnJPRLEX6lMHQfvMtylxy8f1+25xufj9cXN9cdTAwMTetvJOEZKSHrFZcdTAwMDQvQa5cdTAwMDJcdTAwMGJPIXVccqw0l2jRtY3CXGaaXHUwMDAw4Vx1MDAxNKdAXHUwMDEwJURIQDNjLGr28Z15nNiaXHUwMDAwy516e/jwtkhcZt0yXHL4uElcdTAwMDRfXFw/I85iwcdBXHUwMDE45CZcdTAwMTgrfVx0feunUmxsblfvlGg3TsqH14PCVt59dKG4a1jEheSM2JpFSlx1MDAxM4RcItXIrauMU4pno6Ln6WdE74bMWtSfOTv3p59R4CXM2Fx1MDAwM4bFxszJyHOdyvRcZkHzzcZdbXBnXHUwMDBiq5dypdnf56W9x/uHnOtVQbaDx0ZcdEGk/6pIcz3ydDxXbaeFUMq8tpQ8m4ZGXHUwMDAwzmaHbHLwXHUwMDFmpqGRXHUwMDEwsXUlVmmynXCG+rmd5rdcdTAwMWaPa1x1MDAxYnpcdTAwMGJcdTAwMWZs66hwUto72sx7PyOhwVx0itGj7CX5Zzwky6O2e4yMSCnIh9dcdTAwMTllgNJ1NHLFXHUwMDBlnEZcZpnEpzLLsj5U7lx1MDAwZbd630FcdTAwMWavXHUwMDFm3l2tXHUwMDE0Kqz67TxtlvV42NvaPC+ul4t+jbd2n/ZPqrXe5F3m6lx1MDAxMbRtWlx1MDAwN1x1MDAxZHpwV/3T9vp27dtW0+5sLeC6N4XKQVnXzlx1MDAxZn+0d6qr9uKgdn17sIDr7vnFQlx1MDAwN3eLrfrT8IHz8vXtVevHx8o2c4yt1nUlc9JcdTAwMTXypCab6UKVc7JRXHUwMDE2PFe8Qlx1MDAxZSdHzsOhcqXgXHK4Zp6eRuTOoFO48uP7p3lqavSCxnz/pkZcdTAwMTif20JcdTAwMTJcIlx1MDAxNLPU1++dysteZXB0bJonrLP8uD3UV3nPblx0aaRHNlx1MDAwMlx1MDAxObLSkEE7tv1/z45mnuKuWVx1MDAwMlx1MDAxOClFNtMm52qDgNxVJ+vsy0My0oZcdTAwMTlordfxwWfuaqRio1VuZpU0qNLDvCTPn7p7rd3SrZWntZqGi2Iz73VgwnD7MWFcdTAwMGWa0EB+Ln9XnL/CSs9cdTAwMWTOP3NXI1x1MDAxM6/P3cQ8YWeJSm9cdTAwMTdub0+webeMPXm0cvOw2/tRXHUwMDFl5Fx1MDAxZOhuIZyPXHR08lxiXHUwMDAwXHUwMDA14Lu2L3uF25xHoH/WvkZcdTAwMDLi54tcdTAwMTHSR2WT6ZO/ot1uqFx1MDAxZqei8tg/vK3BZUtcdTAwMTSHd3lcdTAwMDe6MMYzo8CeRoFcdTAwMTCOUb9cdNDna2uk3WpH6n1xnq9w0+tw/nnbXHUwMDFhaVx1MDAxMavPpbFSWibS2+3JNlxcTlGuOPe0MpZrV4CF0Vmh0jOWXHUwMDEx3ZH6xNfVgWfa1Egp60rITCaR/ZwkneMqT5OjYEvv0NQo2YVcco7oT1Oj4OmZNTUyXHUwMDEwP1x1MDAxOVRwt7zmXGZuS6JcdJtbnlx1MDAwM7dUXHUwMDFjaEOIXHUwMDE0MI3nwKJlliOTue1oxLWbryZcdTAwMDPG2Fx1MDAxZpZ7Pvo9Olx1MDAxYSW78GHq/dPR6PmTVUcjY+I7XHUwMDFhSWuNU3/pXHUwMDFkt+Twe46pjlx1MDAxNCY5qYq+sIguMeVsOiasXHUwMDBinNhs/LZFdDRcImNUK6m4/UN2XHRk93YtjZKzj1x1MDAxM0P609JovDOzlkbGJLQ0XCKQ24nV5V7MLSfGK3LMdIr+Rdd6QLBwLHpEdKRPgUsmMaNcdTAwMTWuXHUwMDE20tGItiNZI9l0//g8VPdWXHKNkuO1XHUwMDEzQ/rT0CjIdZk2NFJcdEv6XHUwMDE55K7p9ixZdnO5VVjb3Vx1MDAxNcerbGX3bOXie/sx7415XHUwMDA1XHUwMDE5RFx1MDAxZVxu6aZCKi1cIktccnBrvVFkwVx1MDAxOGHet6GRsUZpZk0mxltmlbY3+nT38ODmZHX4+L3KLp6at+fr5bRcdTAwMTWmP+5h5bRbkPypZFcufvS7xfra+61Aloiz2H5G8Vx1MDAxNaZuXHJJoaVg6W2K6U8z71x1MDAxMGPWI3NNXHUwMDFhXHUwMDAxSlx1MDAwMYbTXmTNLlxmYlx1MDAwYu5nXHUwMDA01nCJ/Fx1MDAxM0yBzFc/o1x1MDAxNzRFXHUwMDFl+lx1MDAxOcWXn0kgJ18qmT68u1laOzq6LF9e99dcdTAwMWVuaz+wti9cdTAwMWXmmlH1pnMlXHUwMDE191x1MDAwNGNcdTAwMTZGVdmch11cdTAwMDHX0IiRQ+TmqPCgnf3+XHKNXFzvglElyz9cdTAwMDe2n7uhkVx1MDAwZbzkcDEouapuXHUwMDFhQHo0kue6vHJcdTAwMDernaJcdTAwMWTu+Vx1MDAxNX38sFHKe3NcdTAwMTGh3JJXalx1MDAxNGOUXHUwMDE2eaRIzLU0cuE94TJ+qLNpL0ZDMFxmwU2OdiWeMM0xf7GhkXTTapRaRG+RhTvmQFx1MDAwNlx1MDAwMlx1MDAwZkr0glx1MDAxZPNOo8iKrSux378s3jdcYs7lXm8tJrdcdTAwMTFsaKT4eFhL/7SGRrFS5z6ZtTOS8VxyXHLdOlx1MDAwZVxuXX1Has5JfvE55Vx1MDAxY62sp4xcdTAwMDWhjFx1MDAwMlBcdTAwMTG7XWtPXHUwMDBiS8zE3WKcr1uFL9uGRpLgjWpcdTAwMTFcdTAwMTOrX7EqX6LamZDJOXxZV0H5p53RW7czSliqk6xV66pw0k9cdTAwMWY94D9cdTAwMWVKdvh9i1x1MDAwZvXFiej1hFx1MDAwZlx1MDAxN3nnXGI0ynPdQ7VhzCVcdTAwMDWi8TPwUJHlXCKUXHUwMDE2yOJzo5n3M5JcXGolgzz1UZ2Cf1Y7o/heYrGxa+PmTk3MXGZ+sVx1MDAwMGv/4Xq4vNbaWL7bP2nvXHUwMDE3XHUwMDFiT1e313nHXHUwMDFlfUVcdTAwMTc4Y4hgjbbRlsOae1xmXHUwMDA0k8JcdTAwMThM6HJcbr77M1/sWnDPOPVsySVhmk3rcTpccoyajFtcdTAwMTf9/Fx1MDAwN4Fx/+htkVx1MDAxOLxfhjAkcytWXHUwMDA3XHUwMDFh1+XOzNJC4bG2cbTB7E59sHm1/8jw8LBw1c07XHUwMDBltVx1MDAxNJ7L3ymyQa2K2MlSXHUwMDExXHUwMDAwXGaQblx1MDAxY5VLxavA1+BcdTAwMTCAe9KZ2WS4KTQ4zUy23pSWQ5Z8K/P+kbK5vO6g7aontibgcJeGRvdfqlxcv3XAbPqdU9mnaj5cdTAwMTWpILz1WUVcdTAwMTL9WjPDQvL7ZzfN5f7+TqF5ebWMp4fH9bXb3EfNjEaPo6shYqRcdTAwMDYjTcG4JVx1MDAwZva3cWpsaGBcdTAwMDFgWlx1MDAwM2U+XHUwMDFmMFxye1x0l1Nam7iVa1xcXHUwMDA30I+Oylx1MDAxOeLX7lq9t8Vj+J6viGD/6ol5vFtcXGVPjcY27Ozu2+FyXHUwMDE1XHUwMDBmXHUwMDAzXHKAXHUwMDEySjJ+NodlbsE4MEZzYzgqXHUwMDFkSbkw7XFcdTAwMDBXe6hYXHUwMDAwu+Ms51x1MDAxNF8oUFx1MDAxNFx1MDAxZoXJPCromt/w67mM47BcdLpcYuhnWlx1MDAwM/FbS0prlFKWR8pcdTAwMDNHg7HhreNcdTAwMDQ/t1x1MDAwZUypOp8vqN7ibHNlp8TtXavxbVx1MDAxN9ZbR+WtobzJz/pRszBKXHUwMDAy0qZ/y1xi0iZcdTAwMTXjL5Ch9aSit+aW25Yymtfkrrw0XHUwMDAxZC7k7HKSaOiwiVx1MDAxZc3ByXVywahcdTAwMGLrl1x1MDAwZoq6yVx1MDAxY8PzslFcdTAwMDJcdTAwMTlcdTAwMTPRmaMj4Yl3851vizaVXHUwMDExXHUwMDEzRtfEOFx1MDAxNqj8xqGX/sBvL7WKe8f/1+ZcXHqM/nBcdTAwMDX/VkKJwFuc1YKdv1x1MDAwNCOgxFx1MDAwMuvLh1xuM17QYNFoT6pvXHUwMDE5VqyJxmz8qjZcdTAwMTBPtDha7kLOUKyYTHVcdTAwMTnDXHUwMDBiXoBXXFz3eLJi6fFyQ6ylnVFcdTAwMWFZY4Kjxyzj5GqC1TKbmShgPKtcdTAwMDCkXHUwMDE02uVcdTAwMWXttKkoRnhgiV8tukJcdTAwMTlcdTAwMWSM+/7uXHUwMDExJMkuz+96s1lmgZNXc1uazFx1MDAwMmshRv6DNlJcdTAwMWJcdTAwMDOBo35mgcFlgbWiJymRK4tTJn6kKtBORn5gUMxcdTAwMDO3clx1MDAwMXBmaGjur45cZkp6rnBWonBGrJuRXHUwMDE2XHUwMDE501x1MDAxY1x06K+VTkCZhzPQXHUwMDEzO1x1MDAxN5t/jpV394lI+vhyX4I/56rPXHUwMDA2mUB50lwizMJ4xbPL29OGumqsbfbE3tXx7enT1lleXGaK2F6HyqX/XHUwMDAxXFy1v3VcdTAwMTPsw9ZcdTAwMWGQrFx0hq5cdTAwMDE4UUp89eg8nk1av+o3p7mmXHUwMDBl5CR8sF7IhT1ZXHUwMDFm1E66a932xb3tXHUwMDE0XHUwMDBmq8PD1D2As6/QfrXH8IqlK7klXHUwMDE3YIbJrdNcdTAwMWZl3iEmR1x1MDAwYsq6XHUwMDFhXHUwMDFhoi9mwi2AJbduztdiIFx1MDAxNlx1MDAxYr+e1at6XqpcbsRPtbdg3yTHXHUwMDEx7JRW90Irtl9QXHUwMDFlXHUwMDBiclxmpjsrc7pcdTAwMGI2VndcdTAwMWHGrGYwQ344meby6S1cYmGtp7RwXHUwMDFkXHUwMDE5lJXRklHuKlxirVFslI3NxlngxkNcdTAwMDAmgWDKjFx1MDAxMlPi3+iCnm5cdTAwMDVcdTAwMDF0b8VEc1SISDawUXmdtZ6lr5A8U1wi5CtIMpeIwzWQdVx1MDAwZVNadIwqKd3LtjR6S69kznnryWQw6StcdTAwMDAn3cGQnFVm3PorkUFxj7RcbvnuYLmrYiUv5iM7XHUwMDBisfLuPmFJj3NcdTAwMTW+/Lr811K3ezwgiXt+XHUwMDE3X+/q/v3K9NIoV1x1MDAxZPXl12NzjOSPXHUwMDA0/u8vf/8/U63ZXHUwMDA0In0= + + + + + avahi-daemonwlo0eth0eth1...go-avahi-cnameD-BusReply on CNAME msgLinuxavahi-daemonwlo0eth0eth1...go-avahi-cnameD-BusReply on CNAME msgLinuxORMounted dbusDockerListen mDNS224.0.0.251:5353Listen mDNS224.0.0.251:5353 \ No newline at end of file From 7d1867814fa9515c0e50c0004eac4fb6c3af77a4 Mon Sep 17 00:00:00 2001 From: Sergey G Date: Tue, 8 Aug 2023 17:03:09 +0200 Subject: [PATCH 07/10] docker: use subdomain as default in docker --- Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1daf075..bfa165d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # STAGE 1: building the executable -FROM docker.io/golang:1.20.6-alpine3.18 as builder +FROM docker.io/golang:1.20.7-alpine3.18 as builder WORKDIR /build COPY go.mod go.sum ./ @@ -15,5 +15,8 @@ RUN go build -ldflags="-w -s" -o /go-avahi-cname # STAGE 2: build the container to run FROM scratch COPY --from=builder /go-avahi-cname /go-avahi-cname + EXPOSE 5353/udp + ENTRYPOINT ["/go-avahi-cname"] +CMD [ "subdomain" ] From 310838252c86317071472fc00b95414e40fc2cac Mon Sep 17 00:00:00 2001 From: Sergey G Date: Tue, 8 Aug 2023 17:03:20 +0200 Subject: [PATCH 08/10] ci: bump to go 1.20.7 --- .github/workflows/commit.yml | 6 +++--- .github/workflows/release.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/commit.yml b/.github/workflows/commit.yml index 0d3a6cb..8ab608a 100644 --- a/.github/workflows/commit.yml +++ b/.github/workflows/commit.yml @@ -15,20 +15,20 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: "1.20" + go-version: "1.20.7" cache: false - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.53 + version: v1.53.3 build_test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: "1.20" + go-version: "1.20.7" cache: false - name: Verify dependencies diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5458dda..66d7e4e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/setup-go@v4 with: - go-version: "1.20.6" + go-version: "1.20.7" - name: Set up QEMU uses: docker/setup-qemu-action@v2 From 0873a49a2a0b393baac665ef2ddf2e74218b0673 Mon Sep 17 00:00:00 2001 From: Sergey G Date: Tue, 8 Aug 2023 17:13:12 +0200 Subject: [PATCH 09/10] docs: remove repeats --- README.md | 72 ++++++++++++++++--------------------------------------- 1 file changed, 21 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index adab7f0..167c22e 100644 --- a/README.md +++ b/README.md @@ -8,17 +8,12 @@ It is a simple and lightweight project that allows you to publish CNAME records pointing to the local host over multicast DNS using the **Avahi** daemon, which is widely available in most Linux distributions. This means that you can access your local host using different names from any device on the same network, as long as they support Apple’s Bonjour protocol, which is compatible with Avahi. -## TLDR +## TL;DR -Forward all subdomains from machine to machine( `*.hostname.local` -> `hostname.local`) +Forward all subdomains current to machine( `*.hostname.local` -> `hostname.local`) 1. _Binary_ `./go-avahi-cname subdomain` -2. _Docker_ `docker run -d -v "/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket" ghcr.io/grishy/go-avahi-cname:v2.0.0` - -## Modes - -- **Subdomain reply** - _I think you want this._ We listen to the traffic and if someone asks `*.hostname.local` (example: `name1.hostname.local`), we "redirect" to `hostname.local`. -- **Interval publishing** - With some frequency, we send out information about all `name1.hostname.local`, `name2.hostname.local`... +2. _Docker_ `docker run -d --network host -v "/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket" ghcr.io/grishy/go-avahi-cname:v2.0.0` ## Goals @@ -30,6 +25,11 @@ Here are some of the benefits of using go-avahi-cname: - **✅ Support x86_64 and ARM** - **✅ Install as binaries or containers** +## Modes + +- **Subdomain reply** - _I think you want this._ We listen to the traffic and if someone asks `*.hostname.local` (example: `name1.hostname.local`), we "redirect" to `hostname.local`. +- **Interval publishing** - With some frequency, we send out information about all `name1.hostname.local`, `name2.hostname.local`... + --- # How does it work? @@ -102,58 +102,29 @@ This will create three CNAME records pointing to your local host: You can then access your local host using any of these names from other devices on the same network. -## How to use and install? - -You can specify any number of CNAMEs as arguments when running go-avahi-cname, with no length limit. -You can use either just the name (`name1`), which will create a record as a subdomain for the current machine, or you can write the full FQDN (`name1.hostname.local.` domain with a dot on the end) format. - -For example, if your machine’s hostname is lab, you can run: - -```plain -> ./go-avahi-cname git photo.local. example.lab.local. -2023/07/27 08:37:14 Creating publisher -2023/07/27 08:37:14 Formating CNAMEs: -2023/07/27 08:37:14 > 'git.lab.local.' (added current FQDN) -2023/07/27 08:37:14 > 'photo.local.' -2023/07/27 08:37:14 > 'example.lab.local.' -2023/07/27 08:37:14 Publishing every 5m0s and CNAME TTL=600s. -^C -2023/07/27 08:37:16 Closing publisher... -``` - -This will create three CNAME records pointing to your local host: - -- `git.lab.local.` -- `photo.local.` -- `example.lab.local.` - -You can then access your local host using any of these names from other devices on the same network. - -### Installation options +## Installation options There are two ways to install and run go-avahi-cname: -#### Binary +### Binary Binary files can be taken as artifacts for [the Release](https://github.com/grishy/go-avahi-cname/releases). In this case, it would be better to create a systemd service. -#### Container +### Container The images for each version are in [the Packages section](https://github.com/grishy/go-avahi-cname/pkgs/container/go-avahi-cname). You need to provide the `/var/run/dbus/system_bus_socket` file to the container to be able to communicate with the host's Avahi daemon. -One-liner to run the container `v0.3.1`: +Docker Composer example: -```bash -> docker run --restart=unless-stopped -d -v /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket ghcr.io/grishy/go-avahi-cname:v0.3.1 name1 name2.lab.local. -5a19790e06cca93016af6651d7af4046c24095a6909ace2fe26c3451fb98ceee - -> docker logs 5a19790e06cca93016af6651d7af4046c24095a6909ace2fe26c3451fb98ceee -2023/07/27 08:49:02 Creating publisher -2023/07/27 08:49:02 Formating CNAMEs: -2023/07/27 08:49:02 > 'name1.lab.local.' (added current FQDN) -2023/07/27 08:49:02 > 'name2.lab.local.' -2023/07/27 08:49:02 Publishing every 5m0s ans CNAME TTL=600s. +```yaml +version: "3.3" +services: + go-avahi-cname: + network_mode: host + volumes: + - "/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket" + image: "ghcr.io/grishy/go-avahi-cname:v2.0.0" ``` Ansible task to run the container: @@ -162,11 +133,10 @@ Ansible task to run the container: - name: go-avahi-cname | Start container community.docker.docker_container: name: "go-avahi-cname" - image: "ghcr.io/grishy/go-avahi-cname:v0.3.1" + image: "ghcr.io/grishy/go-avahi-cname:v2.0.0" restart_policy: unless-stopped volumes: - "/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket" # access to avahi-daemon - command: "name1 name2 git" ``` ## Source of inspiration From 98253876170ce19c39d78f50fa6e02433ef3136a Mon Sep 17 00:00:00 2001 From: Sergey G Date: Tue, 8 Aug 2023 17:30:46 +0200 Subject: [PATCH 10/10] docs: logo in center and simplify text --- README.md | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 167c22e..891ac95 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ - +

+ +

-![GitHub tag (with filter)](https://img.shields.io/github/v/tag/grishy/go-avahi-cname) -[![Go Report Card](https://goreportcard.com/badge/github.com/grishy/go-avahi-cname)](https://goreportcard.com/report/github.com/grishy/go-avahi-cname) -![Build Status](https://github.com/grishy/go-avahi-cname/actions/workflows/release.yml/badge.svg) - -# What is go-avahi-cname? - -It is a simple and lightweight project that allows you to publish CNAME records pointing to the local host over multicast DNS using the **Avahi** daemon, which is widely available in most Linux distributions. This means that you can access your local host using different names from any device on the same network, as long as they support Apple’s Bonjour protocol, which is compatible with Avahi. +

+ GitHub tag (with filter) + Go Report Card + Build Status +

## TL;DR @@ -15,26 +15,29 @@ Forward all subdomains current to machine( `*.hostname.local` -> `hostname.local 1. _Binary_ `./go-avahi-cname subdomain` 2. _Docker_ `docker run -d --network host -v "/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket" ghcr.io/grishy/go-avahi-cname:v2.0.0` +# What is go-avahi-cname? + +It is a simple and lightweight project that allows you to publish CNAME records pointing to the local host over multicast DNS using the **Avahi** daemon, which is widely available in most Linux distributions. This means that you can access your local host using different names from any device on the same network, as long as they support Apple’s Bonjour protocol, which is compatible with Avahi. + ## Goals Here are some of the benefits of using go-avahi-cname: -- **✅ No dependencies** - You only need the Avahi daemon running on your host, no other libraries or packages are required. +- **✅ No dependencies** - Requires only the Avahi daemon. - **✅ Small footprint** - **✅ Support x86_64 and ARM** - **✅ Install as binaries or containers** -## Modes +## Modes of Operation -- **Subdomain reply** - _I think you want this._ We listen to the traffic and if someone asks `*.hostname.local` (example: `name1.hostname.local`), we "redirect" to `hostname.local`. -- **Interval publishing** - With some frequency, we send out information about all `name1.hostname.local`, `name2.hostname.local`... +- **Subdomain reply** - _I think you want this._ Listen to the traffic and if someone asks `*.hostname.local` (example: `name1.hostname.local`), we "redirect" to `hostname.local`. +- **Interval publishing** - Periodically broadcasts CNAME records for various `name1.hostname.local`, `git.any.local`... --- # How does it work? -_go-avahi-cname_ communicates with the Avahi daemon via DBus, and publishes the CNAME records. Different modes are only the way of how we select records to publish. The Avahi daemon then broadcasts these records over multicast DNS, so that other devices on the same network can resolve them. +The tool communicates with the Avahi daemon via DBus to publish CNAME records. ## Subdomain CNAME reply @@ -141,9 +144,13 @@ Ansible task to run the container: ## Source of inspiration -- https://github.com/nfam/mdns-subdomain/tree/master - I used this approach for version 2. -- https://web.archive.org/web/20151016190620/http://www.avahi.org/wiki/Examples/PythonPublishAlias -- https://pypi.org/project/mdns-publisher/ +- [PythonPublishAlias](https://web.archive.org/web/20151016190620/http://www.avahi.org/wiki/Examples/PythonPublishAlias) +- [mdns-publisher](https://pypi.org/project/mdns-publisher/) +- [nfam/mdns-subdomain](https://github.com/nfam/mdns-subdomain) + +## Logo + +Generated by mix of Go Gopher and Avahi logo. ## License