Exemple #1
0
func main() {
	flag.Parse()
	hkjnweb.Register(os.Getenv("PROD") != "")
	registerStatic(os.Getenv("STATIC_DIR"))

	addr := os.Getenv("BIND_ADDR")
	if addr == "" {
		addr = ":12345"
	}
	if os.Getenv("SERVE_HTTP") != "" {
		log.Printf("webserver serving HTTP on %s..\n", addr)
		err := http.ListenAndServe(addr, nil)
		if err != nil {
			log.Fatalf("FATAL: http server exited: %v\n", err)
		}
	} else {
		log.Println("Since SERVE_HTTP isn't set, we should serve https")
		m := autocert.Manager{
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(hosts...),
		}
		s := &http.Server{
			Addr:      addr,
			TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
		}
		log.Printf("webserver serving HTTPS on %s..\n", addr)
		err := s.ListenAndServeTLS("", "")
		if err != nil {
			log.Fatalf("FATAL: https server exited: %v\n", err)
		}
	}
}
Exemple #2
0
// Run the application, start http and scp server.
func Run(appConfig Config) {
	config = appConfig

	// Connect to DB
	db, err := lib.Connect()
	if err != nil {
		fmt.Printf("Failed to connect to db")
		return
	}

	// Logging
	log := logrus.New()
	log.Level = logrus.DebugLevel
	log.Out = os.Stdout
	log.Formatter = &logrus.TextFormatter{}

	// Websockets
	ws := ws.NewServer()

	// Shared dependencies between all controller
	deps := dependencies.Dependencies{
		Fs:     afero.NewOsFs(),
		Logger: log,
		DB:     db,
		WS:     ws,
	}

	ws.Dependencies = &deps
	go ws.Start()

	// // Start SCP
	// scp := scp.Server{}
	// scp.DB = deps.DB
	// scp.Logger = deps.Logger
	// scp.CertPath = "certs/scp.rsa"
	// scp.BindAddr = config.SCPBindAddr
	// go scp.ListenAndServe()

	if config.Secure {
		c := autocert.DirCache("certs")
		m := autocert.Manager{
			Cache:      c,
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist("x.zqz.ca"),
		}

		s := &http.Server{
			Addr:      config.HTTPBindAddr,
			TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
		}

		deps.Info("Listening for HTTP1.1 Connections", "addr", ":3001")
		deps.Info("Listening for HTTP2 Connections", "addr", config.HTTPBindAddr)
		go http.ListenAndServe(":3001", secureRedirect())
		s.ListenAndServeTLS("", "")
	} else {
		deps.Info("Listening for HTTP1.1 Connections", "addr", config.HTTPBindAddr)
		http.ListenAndServe(config.HTTPBindAddr, Routes(deps))
	}
}
Exemple #3
0
func setupTLS(ws *webserver.Server, conf *config) error {
	if conf.HTTPSCert != "" && conf.HTTPSKey != "" {
		ws.SetTLS(webserver.TLSSetup{
			CertFile: conf.HTTPSCert,
			KeyFile:  conf.HTTPSKey,
		})
		return nil
	}
	if !conf.CertManager {
		return nil
	}

	// As all requests to the publisher are proxied through Camlistore's app
	// handler, it makes sense to assume that both Camlistore and the publisher
	// are behind the same domain name. Therefore, it follows that
	// camlistored's autocert is the one actually getting a cert (and answering
	// the challenge) for the both of them. Plus, if they run on the same host
	// (default setup), they can't both listen on 443 to answer the TLS-SNI
	// challenge.
	// TODO(mpl): however, camlistored and publisher could be running on
	// different hosts, in which case we need to find a way for camlistored to
	// share its autocert cache with publisher. But I think that can wait a
	// later CL.
	hostname := os.Getenv("CAMLI_API_HOST")
	hostname = strings.TrimPrefix(hostname, "https://")
	hostname = strings.SplitN(hostname, "/", 2)[0]
	hostname = strings.SplitN(hostname, ":", 2)[0]
	if !netutil.IsFQDN(hostname) {
		return fmt.Errorf("cannot ask Let's Encrypt for a cert because %v is not a fully qualified domain name", hostname)
	}
	logger.Print("TLS enabled, with Let's Encrypt")

	// TODO(mpl): we only want publisher to use the same cache as
	// camlistored, and we don't actually need an autocert.Manager.
	// So we could just instantiate an autocert.DirCache, and generate
	// from there a *tls.Certificate, but it looks like it would mean
	// extracting quite a bit of code from the autocert pkg to do it properly.
	// Instead, I've opted for using an autocert.Manager (even though it's
	// never going to reply to any challenge), but with NOOP for Put and
	// Delete, just to be on the safe side. It's simple enough, but there's
	// still a catch: there's no ServerName in the clientHello we get, so we
	// reinject one so we can simply use the autocert.Manager.GetCertificate
	// method as the way to get the certificate from the cache. Is that OK?
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(hostname),
		Cache:      roCertCacheDir(osutil.DefaultLetsEncryptCache()),
	}
	ws.SetTLS(webserver.TLSSetup{
		CertManager: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
			if clientHello.ServerName == "" {
				clientHello.ServerName = hostname
			}
			return m.GetCertificate(clientHello)
		},
	})
	return nil
}
Exemple #4
0
func main() {
	flag.Parse()
	log.Println("Starting")

	// http.ListenAndServe(":443", nil)
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist("www.homedroids.io"),
	}
	s := &http.Server{
		Addr:      *httpsAddr,
		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
	}
	log.Fatal(s.ListenAndServeTLS("", ""))
}
Exemple #5
0
func serveHTTPS(httpServer *http.Server) error {
	log.Printf("Starting TLS server on %s", *httpsAddr)
	httpsServer := new(http.Server)
	*httpsServer = *httpServer
	httpsServer.Addr = *httpsAddr
	cacheDir := autocert.DirCache("letsencrypt.cache")
	var domain string
	if !inProd {
		if *tlsCertFile != "" && *tlsKeyFile != "" {
			return httpsServer.ListenAndServeTLS(*tlsCertFile, *tlsKeyFile)
		}
		// Otherwise use Let's Encrypt, i.e. same use case as in prod
		if strings.HasPrefix(*httpsAddr, ":") {
			return errors.New("for Let's Encrypt, -https needs to start with a host name")
		}
		host, _, err := net.SplitHostPort(*httpsAddr)
		if err != nil {
			return err
		}
		domain = host
	} else {
		domain = "camlistore.org"
		cacheDir = autocert.DirCache(prodLECacheDir)
	}
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(domain),
		Cache:      cacheDir,
	}
	if *adminEmail != "" {
		m.Email = *adminEmail
	}

	httpsServer.TLSConfig = &tls.Config{
		GetCertificate: m.GetCertificate,
	}
	log.Printf("Listening for HTTPS on %v", *httpsAddr)
	ln, err := net.Listen("tcp", *httpsAddr)
	if err != nil {
		return err
	}
	return httpsServer.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, httpsServer.TLSConfig))
}
Exemple #6
0
func main() {
	flag.Parse()
	log.Printf("Starting (prod=%t)", *prod)

	client := &acme.Client{}
	if !*prod {
		client = &acme.Client{
			DirectoryURL: "https://acme-staging.api.letsencrypt.org/directory",
		}
	}
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist("www.homedroids.io"),
		Client:     client,
	}
	s := &http.Server{
		Addr:      *httpsAddr,
		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
	}
	log.Fatal(s.ListenAndServeTLS("", ""))
}
Exemple #7
0
func serveProdTLS() error {
	const cacheDir = "/var/cache/autocert"
	if err := os.MkdirAll(cacheDir, 0700); err != nil {
		return err
	}
	m := autocert.Manager{
		Cache:      autocert.DirCache(cacheDir),
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist("http2.golang.org"),
	}
	srv := &http.Server{
		TLSConfig: &tls.Config{
			GetCertificate: m.GetCertificate,
		},
	}
	http2.ConfigureServer(srv, &http2.Server{})
	ln, err := net.Listen("tcp", ":443")
	if err != nil {
		return err
	}
	return srv.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig))
}
Exemple #8
0
func (srv *Server) newTLSConfig(cfg ServerConfig) *tls.Config {
	tlsConfig := utils.SecureTLSConfig()
	if cfg.AutocertDNSName == "" {
		// No official DNS name, no certificate.
		tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
			cert, _ := srv.localCertificate(clientHello.ServerName)
			return cert, nil
		}
		return tlsConfig
	}
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		Cache:      srv.state.AutocertCache(),
		HostPolicy: autocert.HostWhitelist(cfg.AutocertDNSName),
	}
	if cfg.AutocertURL != "" {
		m.Client = &acme.Client{
			DirectoryURL: cfg.AutocertURL,
		}
	}
	tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
		logger.Infof("getting certificate for server name %q", clientHello.ServerName)
		// Get the locally created certificate and whether it's appropriate
		// for the SNI name. If not, we'll try to get an acme cert and
		// fall back to the local certificate if that fails.
		cert, shouldUse := srv.localCertificate(clientHello.ServerName)
		if shouldUse {
			return cert, nil
		}
		acmeCert, err := m.GetCertificate(clientHello)
		if err == nil {
			return acmeCert, nil
		}
		logger.Errorf("cannot get autocert certificate for %q: %v", clientHello.ServerName, err)
		return cert, nil
	}
	return tlsConfig
}
Exemple #9
0
func main() {
	flag.Parse()
	log.Printf("Starting (prod=%t)", *prod)

	client := &acme.Client{}
	if !*prod {
		client = &acme.Client{
			DirectoryURL: "https://acme-staging.api.letsencrypt.org/directory",
		}
	}
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(reg.Hosts()...),
		Cache:      autocert.DirCache("certs"),
		Client:     client,
	}
	hs := newHostSwitch(reg)
	s := &http.Server{
		Addr:      *httpsAddr,
		Handler:   hs,
		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
	}
	log.Fatal(s.ListenAndServeTLS("", ""))
}
Exemple #10
0
func main() {
	log.SetLevel(log.DebugLevel)
	log.SetFlags(0)
	// выводим информацию о версии сборки
	log.WithFields(log.Fields{
		"version": version,
		"date":    date,
		"build":   build,
		"name":    appName,
	}).Info("starting service")
	// разбираем параметры запуска приложения
	config := flag.String("config", appName+".json", "configuration `filename`")
	address := flag.String("address", host, "server address and `port`")
	flag.Parse()

	// загружаем конфигурацию сервиса
	log.WithField("file", *config).Info("loading service config")
	service, err := LoadConfig(*config)
	if err != nil {
		log.WithError(err).Error("loading config error")
		os.Exit(1)
	}

	// выводим список поддерживаемых MX-серверов
	mxnames := service.Auth.Names()
	if len(mxnames) == 0 {
		log.Error("no MX servers configured")
		os.Exit(1)
	}
	log.WithField("names", strings.Join(mxnames, ", ")).
		Info("supported MX servers")

	// выводим информацию о кеше авторизации
	log.WithField("lifeTime", mxlogin.CacheLifetime).
		Info("authorization cache")

	// удаляем устаревшие файлы раз в сутки
	cleanInterval := time.Hour * 24
	// выводим информацию о хранилище файлов
	log.WithFields(log.Fields{
		"path":          service.FileStore.Store.Path(),
		"lifeTime":      service.FileStore.Lifetime,
		"cleanInterval": cleanInterval,
	}).Info("file store")
	// запускаем процесс автоматического удаления старых файлов
	go func() {
		for {
			time.Sleep(cleanInterval)
			err = service.FileStore.Clean()
			if err != nil {
				log.WithField("service", "filestore").
					WithError(err).Debug("file store clean error")
			}
		}
	}()

	// инициализируем мультиплексор HTTP-запросов
	mux := &rest.ServeMux{
		Headers: map[string]string{
			"Server":            "MXStore/2.0",
			"X-API-Version":     "1.1",
			"X-Service-Version": version,
		},
		Logger:  log.WithField("service", "http"),
		Encoder: Encoder,
	}

	// обработчики файлов хранилища и аватарок
	mux.Handles(rest.Paths{
		"/mx/:mx-name/store": {
			// переходим на основное имя MX сервера, если используется синоним
			// возвращает пустой ответ, если имя сервера и так основное
			"GET": nil,
			// сохраняем в хранилище новый файл
			"POST": service.FileStore.Save,
		},
		// отдаем файл по запросу
		"/mx/:mx-name/store/*filename": {
			"GET": service.FileStore.Get,
		},
		"/mx/:mx-name/avatar": {
			// переходим на основное имя MX сервера, если используется синоним
			// возвращаем список идентификаторов пользователей и аватарок
			"GET": service.Avatars.List,
			// сохраняем в хранилище новый файл
			"POST": service.Avatars.Save,
		},
		// отдаем файл с аватаркой по запросу
		"/mx/:mx-name/avatar/*filename": {
			"GET": service.Avatars.Get,
		},
	},
		// для всех запросов требуется авторизация
		service.Auth.Authorize,
	)

	// добавляем обработчик отдачи документации
	mux.Handle("GET", "/",
		rest.Redirect("https://www.connector73.com/en#softphone"))
	// запускаем сервис с ответом об информации о ссылке
	mux.Handle("GET", "/urlinfo/*url", URLInfo)
	// генератор аватарок
	mux.Handle("GET", "/avatar/:id", AvatarGenerator)

	// инициализируем HTTP-сервер
	server := &http.Server{
		Addr:         *address,
		Handler:      mux,
		ReadTimeout:  time.Second * 60,
		WriteTimeout: time.Second * 120,
	}
	// для защищенного соединения проделываем дополнительные настройки
	host, port, err := net.SplitHostPort(*address)
	if err != nil {
		log.WithError(err).Error("bad server address")
		os.Exit(2)
	}

	if port == "https" || port == "443" {
		if host != "localhost" && host != "127.0.0.1" {
			manager := autocert.Manager{
				Prompt:     autocert.AcceptTOS,
				HostPolicy: autocert.HostWhitelist(host),
				Email:      "*****@*****.**",
				Cache:      autocert.DirCache("letsEncript.cache"),
			}
			server.TLSConfig = &tls.Config{
				GetCertificate: manager.GetCertificate,
			}
		} else {
			// исключительно для отладки
			cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)
			if err != nil {
				panic(fmt.Sprintf("local certificates error: %v", err))
			}
			server.TLSConfig = &tls.Config{
				Certificates: []tls.Certificate{cert},
			}
		}
		// запускаем автоматический переход для HTTP на HTTPS
		go func() {
			log.Info("starting http to https redirect server")
			err := http.ListenAndServe(":http", http.HandlerFunc(
				func(w http.ResponseWriter, r *http.Request) {
					http.Redirect(w, r,
						"https://"+r.Host+r.URL.String(),
						http.StatusMovedPermanently)
				}))
			if err != nil {
				log.WithError(err).Warning("http redirect server error")
			}
		}()
		// запускаем основной сервер
		go func() {
			log.WithFields(log.Fields{
				"address": server.Addr,
				"host":    host,
			}).Info("starting https server")
			err = server.ListenAndServeTLS("", "")
			// // корректно закрываем сервисы по окончании работы
			// service.Close()
			log.WithError(err).Warning("https server stoped")
			os.Exit(3)
		}()
	} else {
		// не защищенный HTTP сервер
		go func() {
			log.WithField("address", *address).Info("starting service")
			err = server.ListenAndServe()
			// service.Close()
			log.WithError(err).Warning("http server stoped")
			os.Exit(3)
		}()
	}

	// инициализируем поддержку системных сигналов и ждем, когда он случится
	monitorSignals(os.Interrupt, os.Kill)
	// service.Close()
	log.Info("service stoped")
}
Exemple #11
0
// If cert/key files are specified, and found, use them.
// If cert/key files are specified, not found, and the default values, generate
// them (self-signed CA used as a cert), and use them.
// If cert/key files are not specified, use Let's Encrypt.
func setupTLS(ws *webserver.Server, config *serverinit.Config, hostname string) {
	cert, key := config.OptionalString("httpsCert", ""), config.OptionalString("httpsKey", "")
	if !config.OptionalBool("https", true) {
		return
	}
	if (cert != "") != (key != "") {
		exitf("httpsCert and httpsKey must both be either present or absent")
	}

	defCert := osutil.DefaultTLSCert()
	defKey := osutil.DefaultTLSKey()
	const hint = "You must add this certificate's fingerprint to your client's trusted certs list to use it. Like so:\n\"trustedCerts\": [\"%s\"],"
	if cert == defCert && key == defKey {
		_, err1 := wkfs.Stat(cert)
		_, err2 := wkfs.Stat(key)
		if err1 != nil || err2 != nil {
			if os.IsNotExist(err1) || os.IsNotExist(err2) {
				sig, err := httputil.GenSelfTLSFiles(hostname, defCert, defKey)
				if err != nil {
					exitf("Could not generate self-signed TLS cert: %q", err)
				}
				log.Printf(hint, sig)
			} else {
				exitf("Could not stat cert or key: %q, %q", err1, err2)
			}
		}
	}
	if cert == "" && key == "" {
		// Use Let's Encrypt if no files are specified, and we have a usable hostname.
		if netutil.IsFQDN(hostname) {
			m := autocert.Manager{
				Prompt:     autocert.AcceptTOS,
				HostPolicy: autocert.HostWhitelist(hostname),
				Cache:      autocert.DirCache(osutil.DefaultLetsEncryptCache()),
			}
			log.Print("TLS enabled, with Let's Encrypt")
			ws.SetTLS(webserver.TLSSetup{
				CertManager: m.GetCertificate,
			})
			return
		}
		// Otherwise generate new certificates
		sig, err := httputil.GenSelfTLSFiles(hostname, defCert, defKey)
		if err != nil {
			exitf("Could not generate self signed creds: %q", err)
		}
		log.Printf(hint, sig)
		cert = defCert
		key = defKey
	}
	data, err := wkfs.ReadFile(cert)
	if err != nil {
		exitf("Failed to read pem certificate: %s", err)
	}
	sig, err := httputil.CertFingerprint(data)
	if err != nil {
		exitf("certificate error: %v", err)
	}
	log.Printf("TLS enabled, with SHA-256 certificate fingerprint: %v", sig)
	ws.SetTLS(webserver.TLSSetup{
		CertFile: cert,
		KeyFile:  key,
	})
}
Exemple #12
0
func (r *Runner) start() error {
	r.authKey = os.Getenv("AUTH_KEY")
	if r.authKey == "" {
		return errors.New("AUTH_KEY not set")
	}
	r.runEnv["TEST_RUNNER_AUTH_KEY"] = r.authKey

	for _, s := range []string{"S3", "GCS", "AZURE"} {
		name := fmt.Sprintf("BLOBSTORE_%s_CONFIG", s)
		if c := os.Getenv(name); c != "" {
			r.runEnv[name] = c
		} else {
			return fmt.Errorf("%s not set", name)
		}
	}

	r.githubToken = os.Getenv("GITHUB_TOKEN")
	if r.githubToken == "" {
		return errors.New("GITHUB_TOKEN not set")
	}

	am := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		Cache:      autocert.DirCache(args.TLSDir),
		HostPolicy: autocert.HostWhitelist(args.Domain),
	}

	awsAuth, err := aws.EnvCreds()
	if err != nil {
		return err
	}
	r.s3 = s3.New(awsAuth, "us-east-1", nil)

	_, listenPort, err = net.SplitHostPort(args.ListenAddr)
	if err != nil {
		return err
	}

	bc := r.bc
	bc.Network = r.allocateNet()
	if r.rootFS, err = cluster.BuildFlynn(bc, args.RootFS, "origin/master", false, os.Stdout); err != nil {
		return fmt.Errorf("could not build flynn: %s", err)
	}
	r.releaseNet(bc.Network)
	shutdown.BeforeExit(func() { removeRootFS(r.rootFS) })

	db, err := bolt.Open(args.DBPath, 0600, &bolt.Options{Timeout: 5 * time.Second})
	if err != nil {
		return fmt.Errorf("could not open db: %s", err)
	}
	r.db = db
	shutdown.BeforeExit(func() { r.db.Close() })

	if err := r.db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists(dbBucket)
		return err
	}); err != nil {
		return fmt.Errorf("could not create builds bucket: %s", err)
	}

	for i := 0; i < args.ConcurrentBuilds; i++ {
		r.buildCh <- struct{}{}
	}

	if err := r.buildPending(); err != nil {
		log.Printf("could not build pending builds: %s", err)
	}

	go r.connectIRC()
	go r.watchEvents()

	router := httprouter.New()
	router.RedirectTrailingSlash = true
	router.Handler("GET", "/", http.RedirectHandler("/builds", 302))
	router.POST("/", r.handleEvent)
	router.GET("/builds/:build", r.getBuildLog)
	router.GET("/builds/:build/download", r.downloadBuildLog)
	router.POST("/builds/:build/restart", r.restartBuild)
	router.POST("/builds/:build/explain", r.explainBuild)
	router.GET("/builds", r.getBuilds)
	router.ServeFiles("/assets/*filepath", http.Dir(args.AssetsDir))
	router.GET("/cluster/:cluster", r.clusterAPI(r.getCluster))
	router.POST("/cluster/:cluster", r.clusterAPI(r.addHost))
	router.POST("/cluster/:cluster/release", r.clusterAPI(r.addReleaseHosts))
	router.DELETE("/cluster/:cluster/:host", r.clusterAPI(r.removeHost))

	srv := &http.Server{
		Addr:    args.ListenAddr,
		Handler: router,
		TLSConfig: tlsconfig.SecureCiphers(&tls.Config{
			GetCertificate: am.GetCertificate,
		}),
	}
	log.Println("Listening on", args.ListenAddr, "...")
	if err := srv.ListenAndServeTLS("", ""); err != nil {
		return fmt.Errorf("ListenAndServeTLS: %s", err)
	}

	return nil
}
Exemple #13
0
func main() {
	launchConfig.MaybeDeploy()
	flag.Parse()

	var kv keyValue
	var httpsListenAddr string
	if metadata.OnGCE() {
		httpsListenAddr = ":443"
		dsClient, err := datastore.NewClient(context.Background(), GCEProjectID)
		if err != nil {
			log.Fatalf("Error creating datastore client for records: %v", err)
		}
		kv = cachedStore{
			dsClient: dsClient,
			cache:    lru.New(cacheSize),
		}
	} else {
		httpsListenAddr = ":4430"
		kv = memkv{skv: sorted.NewMemoryKeyValue()}
	}
	if err := kv.Set("6401800c.camlistore.net.", "159.203.246.79"); err != nil {
		log.Fatalf("Error adding %v:%v record: %v", "6401800c.camlistore.net.", "159.203.246.79", err)
	}
	if err := kv.Set(domain, *flagServerIP); err != nil {
		log.Fatalf("Error adding %v:%v record: %v", domain, *flagServerIP, err)
	}
	if err := kv.Set("www.camlistore.net.", *flagServerIP); err != nil {
		log.Fatalf("Error adding %v:%v record: %v", "www.camlistore.net.", *flagServerIP, err)
	}

	ds := newDNSServer(kv)
	cs := &gpgchallenge.Server{
		OnSuccess: func(identity string, address string) error {
			log.Printf("Adding %v.camlistore.net. as %v", identity, address)
			return ds.dataSource.Set(strings.ToLower(identity+".camlistore.net."), address)
		},
	}

	tcperr := make(chan error, 1)
	udperr := make(chan error, 1)
	httperr := make(chan error, 1)
	log.Printf("serving DNS on %s\n", *addr)
	go func() {
		tcperr <- dns.ListenAndServe(*addr, "tcp", ds)
	}()
	go func() {
		udperr <- dns.ListenAndServe(*addr, "udp", ds)
	}()
	if metadata.OnGCE() {
		// TODO(mpl): if we want to get a cert for anything
		// *.camlistore.net, it's a bit of a chicken and egg problem, since
		// we need camnetdns itself to be already running and answering DNS
		// queries. It's probably doable, but easier for now to just ask
		// one for camnetdns.camlistore.org, since that name is not
		// resolved by camnetdns.
		hostname := strings.TrimSuffix(authorityNS, ".")
		m := autocert.Manager{
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(hostname),
			Cache:      autocert.DirCache(osutil.DefaultLetsEncryptCache()),
		}
		ln, err := tls.Listen("tcp", httpsListenAddr, &tls.Config{
			Rand:           rand.Reader,
			Time:           time.Now,
			NextProtos:     []string{http2.NextProtoTLS, "http/1.1"},
			MinVersion:     tls.VersionTLS12,
			GetCertificate: m.GetCertificate,
		})
		if err != nil {
			log.Fatalf("Error listening on %v: %v", httpsListenAddr, err)
		}
		go func() {
			httperr <- http.Serve(ln, cs)
		}()
	}
	select {
	case err := <-tcperr:
		log.Fatalf("DNS over TCP error: %v", err)
	case err := <-udperr:
		log.Fatalf("DNS error: %v", err)
	case err := <-httperr:
		log.Fatalf("HTTP server error: %v", err)
	}
}