2022-01-26 05:26:45 +07:00
|
|
|
//go:build !test
|
|
|
|
// +build !test
|
2016-11-28 18:20:19 +07:00
|
|
|
|
2016-11-11 21:48:00 +07:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2022-01-26 05:26:45 +07:00
|
|
|
"context"
|
2017-11-15 04:54:29 +07:00
|
|
|
"crypto/tls"
|
2018-09-21 17:38:23 +07:00
|
|
|
"flag"
|
2017-11-15 04:54:29 +07:00
|
|
|
stdlog "log"
|
|
|
|
"net/http"
|
2016-11-11 21:48:00 +07:00
|
|
|
"os"
|
2018-12-13 17:19:10 +07:00
|
|
|
"strings"
|
2018-08-13 00:06:54 +07:00
|
|
|
"syscall"
|
2017-01-30 17:19:22 +07:00
|
|
|
|
2022-01-26 05:26:45 +07:00
|
|
|
"github.com/caddyserver/certmagic"
|
2019-10-21 02:52:19 +07:00
|
|
|
legolog "github.com/go-acme/lego/v3/log"
|
2017-11-15 04:54:29 +07:00
|
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
"github.com/rs/cors"
|
2017-08-02 23:25:27 +07:00
|
|
|
log "github.com/sirupsen/logrus"
|
2016-11-11 21:48:00 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2018-08-13 00:06:54 +07:00
|
|
|
// Created files are not world writable
|
|
|
|
syscall.Umask(0077)
|
2018-09-21 17:38:23 +07:00
|
|
|
configPtr := flag.String("c", "/etc/acme-dns/config.cfg", "config file location")
|
|
|
|
flag.Parse()
|
2016-11-13 19:50:44 +07:00
|
|
|
// Read global config
|
2018-08-12 22:49:17 +07:00
|
|
|
var err error
|
2018-09-21 17:38:23 +07:00
|
|
|
if fileIsAccessible(*configPtr) {
|
|
|
|
log.WithFields(log.Fields{"file": *configPtr}).Info("Using config file")
|
|
|
|
Config, err = readConfig(*configPtr)
|
2018-08-12 22:49:17 +07:00
|
|
|
} else if fileIsAccessible("./config.cfg") {
|
2017-11-15 04:54:29 +07:00
|
|
|
log.WithFields(log.Fields{"file": "./config.cfg"}).Info("Using config file")
|
2018-08-12 22:49:17 +07:00
|
|
|
Config, err = readConfig("./config.cfg")
|
|
|
|
} else {
|
|
|
|
log.Errorf("Configuration file not found.")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Encountered an error while trying to read configuration file: %s", err)
|
|
|
|
os.Exit(1)
|
2017-11-14 05:42:30 +07:00
|
|
|
}
|
2016-11-23 22:11:31 +07:00
|
|
|
|
2017-11-14 05:42:30 +07:00
|
|
|
setupLogging(Config.Logconfig.Format, Config.Logconfig.Level)
|
2016-11-13 19:50:44 +07:00
|
|
|
|
2016-11-11 21:48:00 +07:00
|
|
|
// Open database
|
2016-11-28 04:21:46 +07:00
|
|
|
newDB := new(acmedb)
|
2018-08-12 22:49:17 +07:00
|
|
|
err = newDB.Init(Config.Database.Engine, Config.Database.Connection)
|
2016-11-11 21:48:00 +07:00
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Could not open database [%v]", err)
|
|
|
|
os.Exit(1)
|
2018-01-22 14:53:07 +07:00
|
|
|
} else {
|
|
|
|
log.Info("Connected to database")
|
2016-11-11 21:48:00 +07:00
|
|
|
}
|
2016-11-28 04:21:46 +07:00
|
|
|
DB = newDB
|
|
|
|
defer DB.Close()
|
2016-11-11 21:48:00 +07:00
|
|
|
|
2018-10-31 05:54:51 +07:00
|
|
|
// Error channel for servers
|
|
|
|
errChan := make(chan error, 1)
|
|
|
|
|
2016-11-28 02:21:38 +07:00
|
|
|
// DNS server
|
2019-10-21 02:52:19 +07:00
|
|
|
dnsservers := make([]*DNSServer, 0)
|
2018-12-13 17:19:10 +07:00
|
|
|
if strings.HasPrefix(Config.General.Proto, "both") {
|
|
|
|
// Handle the case where DNS server should be started for both udp and tcp
|
|
|
|
udpProto := "udp"
|
|
|
|
tcpProto := "tcp"
|
|
|
|
if strings.HasSuffix(Config.General.Proto, "4") {
|
|
|
|
udpProto += "4"
|
|
|
|
tcpProto += "4"
|
|
|
|
} else if strings.HasSuffix(Config.General.Proto, "6") {
|
|
|
|
udpProto += "6"
|
|
|
|
tcpProto += "6"
|
|
|
|
}
|
2019-10-21 02:52:19 +07:00
|
|
|
dnsServerUDP := NewDNSServer(DB, Config.General.Listen, udpProto, Config.General.Domain)
|
|
|
|
dnsservers = append(dnsservers, dnsServerUDP)
|
2019-02-03 22:23:04 +07:00
|
|
|
dnsServerUDP.ParseRecords(Config)
|
2019-10-21 02:52:19 +07:00
|
|
|
dnsServerTCP := NewDNSServer(DB, Config.General.Listen, tcpProto, Config.General.Domain)
|
|
|
|
dnsservers = append(dnsservers, dnsServerTCP)
|
2019-02-03 22:23:04 +07:00
|
|
|
// No need to parse records from config again
|
|
|
|
dnsServerTCP.Domains = dnsServerUDP.Domains
|
2019-02-06 17:09:01 +07:00
|
|
|
dnsServerTCP.SOA = dnsServerUDP.SOA
|
2019-02-03 22:23:04 +07:00
|
|
|
go dnsServerUDP.Start(errChan)
|
|
|
|
go dnsServerTCP.Start(errChan)
|
2018-12-13 17:19:10 +07:00
|
|
|
} else {
|
2019-10-21 02:52:19 +07:00
|
|
|
dnsServer := NewDNSServer(DB, Config.General.Listen, Config.General.Proto, Config.General.Domain)
|
|
|
|
dnsservers = append(dnsservers, dnsServer)
|
2019-02-03 22:23:04 +07:00
|
|
|
dnsServer.ParseRecords(Config)
|
|
|
|
go dnsServer.Start(errChan)
|
2018-12-13 17:19:10 +07:00
|
|
|
}
|
2016-11-11 21:48:00 +07:00
|
|
|
|
2016-11-28 03:09:13 +07:00
|
|
|
// HTTP API
|
2019-10-21 02:52:19 +07:00
|
|
|
go startHTTPAPI(errChan, Config, dnsservers)
|
2016-11-28 03:09:13 +07:00
|
|
|
|
2018-10-31 05:54:51 +07:00
|
|
|
// block waiting for error
|
2021-01-11 22:31:09 +07:00
|
|
|
for {
|
|
|
|
err = <-errChan
|
2018-10-31 05:54:51 +07:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-21 02:52:19 +07:00
|
|
|
func startHTTPAPI(errChan chan error, config DNSConfig, dnsservers []*DNSServer) {
|
2017-11-15 04:54:29 +07:00
|
|
|
// Setup http logger
|
|
|
|
logger := log.New()
|
|
|
|
logwriter := logger.Writer()
|
|
|
|
defer logwriter.Close()
|
2019-10-21 02:52:19 +07:00
|
|
|
// Setup logging for different dependencies to log with logrus
|
|
|
|
// Certmagic
|
|
|
|
stdlog.SetOutput(logwriter)
|
|
|
|
// Lego
|
|
|
|
legolog.Logger = logger
|
|
|
|
|
2017-11-15 04:54:29 +07:00
|
|
|
api := httprouter.New()
|
|
|
|
c := cors.New(cors.Options{
|
2017-11-14 05:42:30 +07:00
|
|
|
AllowedOrigins: Config.API.CorsOrigins,
|
2016-11-28 18:09:10 +07:00
|
|
|
AllowedMethods: []string{"GET", "POST"},
|
|
|
|
OptionsPassthrough: false,
|
2017-11-14 05:42:30 +07:00
|
|
|
Debug: Config.General.Debug,
|
2017-11-15 04:54:29 +07:00
|
|
|
})
|
2017-11-16 02:35:35 +07:00
|
|
|
if Config.General.Debug {
|
|
|
|
// Logwriter for saner log output
|
|
|
|
c.Log = stdlog.New(logwriter, "", 0)
|
|
|
|
}
|
2018-03-15 04:35:39 +07:00
|
|
|
if !Config.API.DisableRegistration {
|
|
|
|
api.POST("/register", webRegisterPost)
|
|
|
|
}
|
2017-11-15 04:54:29 +07:00
|
|
|
api.POST("/update", Auth(webUpdatePost))
|
2019-01-26 00:22:53 +07:00
|
|
|
api.GET("/health", healthCheck)
|
2017-11-15 04:54:29 +07:00
|
|
|
|
|
|
|
host := Config.API.IP + ":" + Config.API.Port
|
|
|
|
|
2019-10-21 02:52:19 +07:00
|
|
|
// TLS specific general settings
|
2017-11-15 04:54:29 +07:00
|
|
|
cfg := &tls.Config{
|
|
|
|
MinVersion: tls.VersionTLS12,
|
|
|
|
}
|
2019-10-21 02:52:19 +07:00
|
|
|
provider := NewChallengeProvider(dnsservers)
|
|
|
|
storage := certmagic.FileStorage{Path: Config.API.ACMECacheDir}
|
|
|
|
|
2022-01-26 05:26:45 +07:00
|
|
|
// Set up certmagic for getting certificate for acme-dns api
|
|
|
|
certmagic.DefaultACME.DNS01Solver = &provider
|
|
|
|
certmagic.DefaultACME.Agreed = true
|
|
|
|
if Config.API.TLS == "letsencrypt" {
|
|
|
|
certmagic.DefaultACME.CA = certmagic.LetsEncryptProductionCA
|
|
|
|
} else {
|
|
|
|
certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA
|
|
|
|
}
|
|
|
|
certmagic.DefaultACME.Email = Config.API.NotificationEmail
|
|
|
|
magicConf := certmagic.NewDefault()
|
|
|
|
magicConf.Storage = &storage
|
|
|
|
magicConf.DefaultServerName = Config.General.Domain
|
|
|
|
|
|
|
|
magicCache := certmagic.NewCache(certmagic.CacheOptions{
|
|
|
|
GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) {
|
|
|
|
return magicConf, nil
|
2019-10-21 02:52:19 +07:00
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2022-01-26 05:26:45 +07:00
|
|
|
magic := certmagic.New(magicCache, *magicConf)
|
2018-10-31 05:54:51 +07:00
|
|
|
var err error
|
2017-11-14 05:42:30 +07:00
|
|
|
switch Config.API.TLS {
|
2019-10-21 02:52:19 +07:00
|
|
|
case "letsencryptstaging":
|
2022-01-26 05:26:45 +07:00
|
|
|
err = magic.ManageAsync(context.Background(), []string{Config.General.Domain})
|
2019-10-21 02:52:19 +07:00
|
|
|
if err != nil {
|
|
|
|
errChan <- err
|
|
|
|
return
|
|
|
|
}
|
2022-01-26 05:26:45 +07:00
|
|
|
cfg.GetCertificate = magic.GetCertificate
|
|
|
|
|
2019-10-21 02:52:19 +07:00
|
|
|
srv := &http.Server{
|
|
|
|
Addr: host,
|
|
|
|
Handler: c.Handler(api),
|
|
|
|
TLSConfig: cfg,
|
|
|
|
ErrorLog: stdlog.New(logwriter, "", 0),
|
|
|
|
}
|
|
|
|
log.WithFields(log.Fields{"host": host, "domain": Config.General.Domain}).Info("Listening HTTPS")
|
|
|
|
err = srv.ListenAndServeTLS("", "")
|
2016-11-28 18:09:10 +07:00
|
|
|
case "letsencrypt":
|
2022-01-26 05:26:45 +07:00
|
|
|
err = magic.ManageAsync(context.Background(), []string{Config.General.Domain})
|
2019-10-21 02:52:19 +07:00
|
|
|
if err != nil {
|
|
|
|
errChan <- err
|
|
|
|
return
|
2017-11-15 04:54:29 +07:00
|
|
|
}
|
2022-01-26 05:26:45 +07:00
|
|
|
cfg.GetCertificate = magic.GetCertificate
|
2017-11-15 04:54:29 +07:00
|
|
|
srv := &http.Server{
|
|
|
|
Addr: host,
|
|
|
|
Handler: c.Handler(api),
|
|
|
|
TLSConfig: cfg,
|
|
|
|
ErrorLog: stdlog.New(logwriter, "", 0),
|
|
|
|
}
|
2019-10-21 02:52:19 +07:00
|
|
|
log.WithFields(log.Fields{"host": host, "domain": Config.General.Domain}).Info("Listening HTTPS")
|
2018-10-31 05:54:51 +07:00
|
|
|
err = srv.ListenAndServeTLS("", "")
|
2016-11-28 18:09:10 +07:00
|
|
|
case "cert":
|
2017-11-15 04:54:29 +07:00
|
|
|
srv := &http.Server{
|
|
|
|
Addr: host,
|
|
|
|
Handler: c.Handler(api),
|
|
|
|
TLSConfig: cfg,
|
|
|
|
ErrorLog: stdlog.New(logwriter, "", 0),
|
|
|
|
}
|
|
|
|
log.WithFields(log.Fields{"host": host}).Info("Listening HTTPS")
|
2018-10-31 05:54:51 +07:00
|
|
|
err = srv.ListenAndServeTLS(Config.API.TLSCertFullchain, Config.API.TLSCertPrivkey)
|
2016-11-28 18:09:10 +07:00
|
|
|
default:
|
2017-11-15 04:54:29 +07:00
|
|
|
log.WithFields(log.Fields{"host": host}).Info("Listening HTTP")
|
2018-10-31 05:54:51 +07:00
|
|
|
err = http.ListenAndServe(host, c.Handler(api))
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
errChan <- err
|
2016-11-28 18:09:10 +07:00
|
|
|
}
|
|
|
|
}
|