redirectord.go 3.05 KB
Newer Older
1
2
3
package main

import (
4
	"errors"
5
6
7
	"flag"
	"fmt"
	"log"
8
	"strings"
9

10
	"net"
ale's avatar
ale committed
11
12
	_ "net/http/pprof"

ale's avatar
ale committed
13
14
	"git.autistici.org/ale/autoradio"
	"git.autistici.org/ale/autoradio/fe"
ale's avatar
ale committed
15
	"git.autistici.org/ale/autoradio/instrumentation"
ale's avatar
ale committed
16
	"git.autistici.org/ale/autoradio/util"
17
18
19
)

var (
ale's avatar
ale committed
20
	domain      = flag.String("domain", "", "DNS domain to serve")
21
	publicIPs   = util.IPList("ip", "Public IP for this machine (may be specified more than once). If unset, the program will try to resolve the local hostname, or it will fall back to inspecting network devices.")
ale's avatar
ale committed
22
23
	dnsPort     = flag.Int("dns-port", 53, "DNS port")
	httpPort    = flag.Int("http-port", 80, "HTTP port")
ale's avatar
ale committed
24
	staticDir   = flag.String("static-dir", "/usr/share/autoradio/htdocs/static", "Static content directory")
ale's avatar
ale committed
25
	templateDir = flag.String("template-dir", "/usr/share/autoradio/htdocs/templates", "HTML templates directory")
ale's avatar
ale committed
26
	lbPolicy    = flag.String("lb-policy", "listeners_available,listeners_score,weighted", "Load balancing rules specification (see godoc documentation for details)")
27
	nameservers = flag.String("nameservers", "", "Comma-separated list of name servers (not IPs) for the zone specified in --domain")
28
	redirectMap = flag.String("redirect-map", "", "File containing a list of source path / target redirects, space-separated, one per line")
29

ale's avatar
ale committed
30
31
	// Default DNS TTL (seconds).
	dnsTtl = 5
32
33
)

34
35
36
37
38
39
40
41
42
43
44
45
46
func getFQDN(ips []net.IP) (string, error) {
	for _, ip := range ips {
		if names, err := net.LookupAddr(ip.String()); err == nil && len(names) > 0 {
			// This is a pretty weak criteria for qualification.
			name := strings.TrimSuffix(names[0], ".")
			if strings.Contains(name, ".") {
				return names[0], nil
			}
		}
	}
	return "", errors.New("reverse resolution failed")
}

47
func main() {
ale's avatar
ale committed
48
	log.SetFlags(0)
49
50
	flag.Parse()

ale's avatar
ale committed
51
52
53
54
	if *domain == "" {
		log.Fatal("Must specify --domain")
	}

55
56
57
58
	if err := util.DetectPublicNetworkParams(publicIPs, nil, nil); err != nil {
		log.Fatal(err)
	}

ale's avatar
ale committed
59
60
	instrumentation.NewCounter("redirectord.restarts").Incr()

61
	client := autoradio.NewClient(autoradio.NewEtcdClient(false))
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
	// If no nameservers are specified, use the fqdn of the local
	// host. It is not going to provide a lot of reliability for
	// clients that cache the authoritative NS records for long,
	// but at least it will work.
	var ns []string
	if *nameservers != "" {
		ns = strings.Split(*nameservers, ",")
	} else {
		fqdn, err := getFQDN(*publicIPs)
		if err != nil {
			log.Fatal("Could not determine fully-qualified name of local host, and --nameservers is not specified")
		}
		log.Printf("autodetected fqdn %s", fqdn)
		ns = []string{fqdn}
	}

	dnsRed := fe.NewDNSRedirector(client, *domain, *publicIPs, dnsTtl, ns)
ale's avatar
ale committed
80
	dnsRed.Start(fmt.Sprintf(":%d", *dnsPort))
ale's avatar
ale committed
81

82
	httpRed, err := fe.NewHTTPRedirector(client, *domain, *lbPolicy, *staticDir, *templateDir)
ale's avatar
ale committed
83
84
85
	if err != nil {
		log.Fatal(err)
	}
86
87
88
89
90
91
92
	if *redirectMap != "" {
		if err := httpRed.LoadStaticRedirects(*redirectMap); err != nil {
			// An error loading the redirect map should not be fatal.
			log.Printf("Warning: could not load static redirect map: %v", err)
		}
	}
	httpRed.Run(fmt.Sprintf(":%d", *httpPort))
ale's avatar
ale committed
93
}