Config file support, more metrics
This commit is contained in:
parent
b175d73156
commit
eddafb699b
3 changed files with 165 additions and 127 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
prometheus-dnssec-exporter
|
prometheus-dnssec-exporter
|
||||||
|
config.yaml
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -9,6 +9,7 @@ require (
|
||||||
github.com/prometheus/common v0.29.0
|
github.com/prometheus/common v0.29.0
|
||||||
github.com/prometheus/exporter-toolkit v0.7.1
|
github.com/prometheus/exporter-toolkit v0.7.1
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||||
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -35,5 +36,4 @@ require (
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/appengine v1.6.6 // indirect
|
google.golang.org/appengine v1.6.6 // indirect
|
||||||
google.golang.org/protobuf v1.26.0-rc.1 // indirect
|
google.golang.org/protobuf v1.26.0-rc.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
245
main.go
245
main.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -17,28 +18,42 @@ import (
|
||||||
"github.com/prometheus/common/version"
|
"github.com/prometheus/common/version"
|
||||||
"github.com/prometheus/exporter-toolkit/web"
|
"github.com/prometheus/exporter-toolkit/web"
|
||||||
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const namespace = "dnssec"
|
const namespace = "dnssec"
|
||||||
|
|
||||||
var removalCds = DsData{
|
|
||||||
KeyTag: 0,
|
|
||||||
Algorithm: 0,
|
|
||||||
DigestType: 0,
|
|
||||||
Digest: "00",
|
|
||||||
}
|
|
||||||
|
|
||||||
type MetricsCollector struct {
|
type MetricsCollector struct {
|
||||||
Log log.Logger
|
Log log.Logger
|
||||||
signatureOkMetric *prom.GaugeVec
|
signatureOkMetric *prom.GaugeVec
|
||||||
dsPresentMetric *prom.GaugeVec
|
sokRcodeMetric *prom.GaugeVec
|
||||||
cdsPresentMetric *prom.GaugeVec
|
dsCountMetric *prom.GaugeVec
|
||||||
|
dsRcodeMetric *prom.GaugeVec
|
||||||
|
cdsCountMetric *prom.GaugeVec
|
||||||
|
cdsRcodeMetric *prom.GaugeVec
|
||||||
cdsDsMatchMetric *prom.GaugeVec
|
cdsDsMatchMetric *prom.GaugeVec
|
||||||
zones []string
|
zones []string
|
||||||
DNS dns.Client
|
DNS dns.Client
|
||||||
Resolver string
|
Resolver string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DnsConfig struct {
|
||||||
|
Resolver string `yaml:"resolver"`
|
||||||
|
Zones []string `yaml:"zones"`
|
||||||
|
RRTypes []string `yaml:"rrtypes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DnssecConfig struct {
|
||||||
|
DnsConfig DnsConfig `yaml:"dns"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func b2f64(b bool) float64 {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func NewMetricsCollector(zonelist []string, resolver string, logger log.Logger) *MetricsCollector {
|
func NewMetricsCollector(zonelist []string, resolver string, logger log.Logger) *MetricsCollector {
|
||||||
signatureOkMetric := prom.NewGaugeVec(
|
signatureOkMetric := prom.NewGaugeVec(
|
||||||
prom.GaugeOpts{
|
prom.GaugeOpts{
|
||||||
|
@ -48,19 +63,43 @@ func NewMetricsCollector(zonelist []string, resolver string, logger log.Logger)
|
||||||
},
|
},
|
||||||
[]string{"zone", "tld", "parent"},
|
[]string{"zone", "tld", "parent"},
|
||||||
)
|
)
|
||||||
dsPresentMetric := prom.NewGaugeVec(
|
sokRcodeMetric := prom.NewGaugeVec(
|
||||||
prom.GaugeOpts{
|
prom.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Name: "ds_present",
|
Name: "signature_rcode",
|
||||||
Help: "1 if CDS record is present in the parent zone, 0 otherwise",
|
Help: "RCode of the DNS query",
|
||||||
},
|
},
|
||||||
[]string{"zone", "tld", "parent"},
|
[]string{"zone", "tld", "parent"},
|
||||||
)
|
)
|
||||||
cdsPresentMetric := prom.NewGaugeVec(
|
dsCountMetric := prom.NewGaugeVec(
|
||||||
prom.GaugeOpts{
|
prom.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Name: "cds_present",
|
Name: "ds_count",
|
||||||
Help: "1 if a CDS record is present in the zone, 0 otherwise",
|
Help: "Number of DS record is present in the parent zone",
|
||||||
|
},
|
||||||
|
[]string{"zone", "tld", "parent"},
|
||||||
|
)
|
||||||
|
dsRcodeMetric := prom.NewGaugeVec(
|
||||||
|
prom.GaugeOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "ds_rcode",
|
||||||
|
Help: "RCode of the DS record answer",
|
||||||
|
},
|
||||||
|
[]string{"zone", "tld", "parent"},
|
||||||
|
)
|
||||||
|
cdsCountMetric := prom.NewGaugeVec(
|
||||||
|
prom.GaugeOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "cds_count",
|
||||||
|
Help: "Number of CDS records present in the zone",
|
||||||
|
},
|
||||||
|
[]string{"zone", "tld", "parent"},
|
||||||
|
)
|
||||||
|
cdsRcodeMetric := prom.NewGaugeVec(
|
||||||
|
prom.GaugeOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "cds_rcode",
|
||||||
|
Help: "RCode of the CDS record answer",
|
||||||
},
|
},
|
||||||
[]string{"zone", "tld", "parent"},
|
[]string{"zone", "tld", "parent"},
|
||||||
)
|
)
|
||||||
|
@ -74,8 +113,11 @@ func NewMetricsCollector(zonelist []string, resolver string, logger log.Logger)
|
||||||
)
|
)
|
||||||
collector := MetricsCollector{
|
collector := MetricsCollector{
|
||||||
signatureOkMetric: signatureOkMetric,
|
signatureOkMetric: signatureOkMetric,
|
||||||
dsPresentMetric: dsPresentMetric,
|
sokRcodeMetric: sokRcodeMetric,
|
||||||
cdsPresentMetric: cdsPresentMetric,
|
dsCountMetric: dsCountMetric,
|
||||||
|
dsRcodeMetric: dsRcodeMetric,
|
||||||
|
cdsCountMetric: cdsCountMetric,
|
||||||
|
cdsRcodeMetric: cdsRcodeMetric,
|
||||||
cdsDsMatchMetric: cdsDsMatchMetric,
|
cdsDsMatchMetric: cdsDsMatchMetric,
|
||||||
zones: zonelist,
|
zones: zonelist,
|
||||||
Resolver: resolver,
|
Resolver: resolver,
|
||||||
|
@ -86,29 +128,16 @@ func NewMetricsCollector(zonelist []string, resolver string, logger log.Logger)
|
||||||
|
|
||||||
func (c *MetricsCollector) Describe(ch chan<- *prom.Desc) {
|
func (c *MetricsCollector) Describe(ch chan<- *prom.Desc) {
|
||||||
c.signatureOkMetric.Describe(ch)
|
c.signatureOkMetric.Describe(ch)
|
||||||
c.dsPresentMetric.Describe(ch)
|
c.sokRcodeMetric.Describe(ch)
|
||||||
c.cdsPresentMetric.Describe(ch)
|
c.dsCountMetric.Describe(ch)
|
||||||
|
c.dsRcodeMetric.Describe(ch)
|
||||||
|
c.cdsCountMetric.Describe(ch)
|
||||||
|
c.cdsRcodeMetric.Describe(ch)
|
||||||
c.cdsDsMatchMetric.Describe(ch)
|
c.cdsDsMatchMetric.Describe(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MetricsCollector) Collect(ch chan<- prom.Metric) {
|
func (c *MetricsCollector) Collect(ch chan<- prom.Metric) {
|
||||||
err := c.reportMetrics(ch)
|
|
||||||
if err != nil {
|
|
||||||
// TODO
|
|
||||||
c.Log.Log(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *MetricsCollector) reportMetrics(ch chan<- prom.Metric) error {
|
|
||||||
for _, zone := range c.zones {
|
for _, zone := range c.zones {
|
||||||
sok, err1 := c.signatureOk(zone)
|
|
||||||
if err1 != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dsp, cdp, cdm, err2 := c.cdsDsMatches(zone)
|
|
||||||
if err2 != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
labels := strings.Split(zone, ".")
|
labels := strings.Split(zone, ".")
|
||||||
tld := strings.Join(labels[len(labels)-2:], ".")
|
tld := strings.Join(labels[len(labels)-2:], ".")
|
||||||
parent := strings.Join(labels[1:], ".")
|
parent := strings.Join(labels[1:], ".")
|
||||||
|
@ -117,37 +146,23 @@ func (c *MetricsCollector) reportMetrics(ch chan<- prom.Metric) error {
|
||||||
"tld": tld,
|
"tld": tld,
|
||||||
"parent": parent,
|
"parent": parent,
|
||||||
}
|
}
|
||||||
signatureOkMetric := c.signatureOkMetric.With(promlabels)
|
sok, sokrcode, _ := c.signatureOk(zone)
|
||||||
dsPresentMetric := c.dsPresentMetric.With(promlabels)
|
c.sokRcodeMetric.With(promlabels).Set(float64(sokrcode))
|
||||||
cdsPresentMetric := c.cdsPresentMetric.With(promlabels)
|
c.signatureOkMetric.With(promlabels).Set(b2f64(sok))
|
||||||
cdsDsMatchMetric := c.cdsDsMatchMetric.With(promlabels)
|
dscount, dsrcode, cdscount, cdsrcode, cdm, _ := c.cdsDsMatches(zone)
|
||||||
if sok {
|
c.dsCountMetric.With(promlabels).Set(float64(dscount))
|
||||||
signatureOkMetric.Set(1)
|
c.dsRcodeMetric.With(promlabels).Set(float64(dsrcode))
|
||||||
} else {
|
c.cdsCountMetric.With(promlabels).Set(float64(cdscount))
|
||||||
signatureOkMetric.Set(0)
|
c.cdsRcodeMetric.With(promlabels).Set(float64(cdsrcode))
|
||||||
}
|
c.cdsDsMatchMetric.With(promlabels).Set(b2f64(cdm))
|
||||||
if dsp {
|
|
||||||
dsPresentMetric.Set(1)
|
|
||||||
} else {
|
|
||||||
dsPresentMetric.Set(0)
|
|
||||||
}
|
|
||||||
if cdp {
|
|
||||||
cdsPresentMetric.Set(1)
|
|
||||||
} else {
|
|
||||||
cdsPresentMetric.Set(0)
|
|
||||||
}
|
|
||||||
if cdm {
|
|
||||||
cdsDsMatchMetric.Set(1)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
cdsDsMatchMetric.Set(0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
c.signatureOkMetric.Collect(ch)
|
c.signatureOkMetric.Collect(ch)
|
||||||
c.dsPresentMetric.Collect(ch)
|
c.sokRcodeMetric.Collect(ch)
|
||||||
c.cdsPresentMetric.Collect(ch)
|
c.dsCountMetric.Collect(ch)
|
||||||
|
c.dsRcodeMetric.Collect(ch)
|
||||||
|
c.cdsCountMetric.Collect(ch)
|
||||||
|
c.cdsRcodeMetric.Collect(ch)
|
||||||
c.cdsDsMatchMetric.Collect(ch)
|
c.cdsDsMatchMetric.Collect(ch)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DsData struct {
|
type DsData struct {
|
||||||
|
@ -169,31 +184,35 @@ func Equal(a, b []DsData) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MetricsCollector) signatureOk(zone string) (bool, error) {
|
func (c *MetricsCollector) signatureOk(zone string) (bool, int, error) {
|
||||||
qsoa := new(dns.Msg)
|
qsoa := new(dns.Msg)
|
||||||
// Request DNSSEC information
|
// Request DNSSEC information
|
||||||
qsoa.SetEdns0(4096, true)
|
qsoa.SetEdns0(4096, true)
|
||||||
qsoa.SetQuestion(dns.Fqdn(zone), dns.TypeSOA)
|
qsoa.SetQuestion(dns.Fqdn(zone), dns.TypeSOA)
|
||||||
rsoa, _, err := c.DNS.Exchange(qsoa, c.Resolver)
|
rsoa, _, err := c.DNS.Exchange(qsoa, c.Resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, rsoa.MsgHdr.Rcode, err
|
||||||
}
|
}
|
||||||
level.Debug(c.Log).Log(fmt.Sprintf("SOA header: %+v\n", rsoa.MsgHdr))
|
level.Debug(c.Log).Log(fmt.Sprintf("SOA header: %+v\n", rsoa.MsgHdr))
|
||||||
return rsoa.MsgHdr.AuthenticatedData && rsoa.MsgHdr.Rcode == dns.RcodeSuccess, nil
|
return rsoa.MsgHdr.AuthenticatedData && rsoa.MsgHdr.Rcode == dns.RcodeSuccess, rsoa.MsgHdr.Rcode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MetricsCollector) cdsDsMatches(zone string) (bool, bool, bool, error) {
|
func (c *MetricsCollector) cdsDsMatches(zone string) (int, int, int, int, bool, error) {
|
||||||
dsPresent := false
|
dsCount := 0
|
||||||
cdsPresent := false
|
dsRcode := -1
|
||||||
|
cdsCount := 0
|
||||||
|
cdsRcode := -1
|
||||||
|
match := false
|
||||||
|
var dsdata []DsData
|
||||||
|
var cdsdata []DsData
|
||||||
|
|
||||||
qcds := new(dns.Msg)
|
qcds := new(dns.Msg)
|
||||||
qcds.SetQuestion(dns.Fqdn(zone), dns.TypeCDS)
|
qcds.SetQuestion(dns.Fqdn(zone), dns.TypeCDS)
|
||||||
rcds, _, err := c.DNS.Exchange(qcds, c.Resolver)
|
rcds, _, err1 := c.DNS.Exchange(qcds, c.Resolver)
|
||||||
if err != nil {
|
cdsRcode = rcds.MsgHdr.Rcode
|
||||||
return dsPresent, cdsPresent, false, err
|
if err1 == nil {
|
||||||
}
|
cdsCount = len(rcds.Answer)
|
||||||
cdsPresent = len(rcds.Answer) > 0
|
cdsdata = make([]DsData, len(rcds.Answer))
|
||||||
cdsdata := make([]DsData, len(rcds.Answer))
|
|
||||||
level.Debug(c.Log).Log(fmt.Sprintf("CDS data: %+v\n", rcds.Answer))
|
level.Debug(c.Log).Log(fmt.Sprintf("CDS data: %+v\n", rcds.Answer))
|
||||||
for i, a := range rcds.Answer {
|
for i, a := range rcds.Answer {
|
||||||
ds := a.(*dns.CDS).DS
|
ds := a.(*dns.CDS).DS
|
||||||
|
@ -205,15 +224,15 @@ func (c *MetricsCollector) cdsDsMatches(zone string) (bool, bool, bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.SliceStable(cdsdata, func(a, b int) bool { return cdsdata[a].KeyTag < cdsdata[b].KeyTag })
|
sort.SliceStable(cdsdata, func(a, b int) bool { return cdsdata[a].KeyTag < cdsdata[b].KeyTag })
|
||||||
|
}
|
||||||
|
|
||||||
qds := new(dns.Msg)
|
qds := new(dns.Msg)
|
||||||
qds.SetQuestion(dns.Fqdn(zone), dns.TypeDS)
|
qds.SetQuestion(dns.Fqdn(zone), dns.TypeDS)
|
||||||
rds, _, err := c.DNS.Exchange(qds, c.Resolver)
|
rds, _, err2 := c.DNS.Exchange(qds, c.Resolver)
|
||||||
if err != nil {
|
dsRcode = rds.MsgHdr.Rcode
|
||||||
return dsPresent, cdsPresent, false, err
|
if err2 == nil {
|
||||||
}
|
dsCount = len(rds.Answer)
|
||||||
dsPresent = len(rds.Answer) > 0
|
dsdata = make([]DsData, len(rds.Answer))
|
||||||
dsdata := make([]DsData, len(rds.Answer))
|
|
||||||
level.Debug(c.Log).Log(fmt.Sprintf("DS data: %+v\n", rds.Answer))
|
level.Debug(c.Log).Log(fmt.Sprintf("DS data: %+v\n", rds.Answer))
|
||||||
for i, a := range rds.Answer {
|
for i, a := range rds.Answer {
|
||||||
ds := a.(*dns.DS)
|
ds := a.(*dns.DS)
|
||||||
|
@ -225,13 +244,16 @@ func (c *MetricsCollector) cdsDsMatches(zone string) (bool, bool, bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.SliceStable(dsdata, func(a, b int) bool { return dsdata[a].KeyTag < dsdata[b].KeyTag })
|
sort.SliceStable(dsdata, func(a, b int) bool { return dsdata[a].KeyTag < dsdata[b].KeyTag })
|
||||||
|
}
|
||||||
|
|
||||||
match := cdsPresent && Equal(dsdata, cdsdata)
|
if dsdata != nil && cdsdata != nil {
|
||||||
|
match = cdsCount > 0 && Equal(dsdata, cdsdata)
|
||||||
// special case: removal requested
|
// special case: removal requested
|
||||||
if len(cdsdata) == 1 && len(dsdata) == 0 && cdsdata[0] == removalCds {
|
if len(cdsdata) == 1 && len(dsdata) == 0 && cdsdata[0].Algorithm == 0 {
|
||||||
match = true
|
match = true
|
||||||
}
|
}
|
||||||
return dsPresent, cdsPresent, match, nil
|
}
|
||||||
|
return dsCount, dsRcode, cdsCount, cdsRcode, match, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -244,17 +266,13 @@ func main() {
|
||||||
"web.metrics-path",
|
"web.metrics-path",
|
||||||
"Path under which to expose metrics.",
|
"Path under which to expose metrics.",
|
||||||
).Default("/metrics").String()
|
).Default("/metrics").String()
|
||||||
resolver = kingpin.Flag(
|
webConfigFile = kingpin.Flag(
|
||||||
"dns.resolver",
|
|
||||||
"DNS Resolver to use.",
|
|
||||||
).Default("127.0.0.1:53").String()
|
|
||||||
zones = kingpin.Flag(
|
|
||||||
"dns.zones",
|
|
||||||
"DNS zones to check, comma separated.",
|
|
||||||
).Default("example.org").String()
|
|
||||||
configFile = kingpin.Flag(
|
|
||||||
"web.config",
|
"web.config",
|
||||||
"[EXPERIMENTAL] Path to config yaml file that can enable TLS or authentication.",
|
"Path to web config yaml file.",
|
||||||
|
).Default("").String()
|
||||||
|
configFile = kingpin.Flag(
|
||||||
|
"config",
|
||||||
|
"Path to config yaml file.",
|
||||||
).Default("").String()
|
).Default("").String()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -264,19 +282,38 @@ func main() {
|
||||||
kingpin.CommandLine.UsageWriter(os.Stdout)
|
kingpin.CommandLine.UsageWriter(os.Stdout)
|
||||||
kingpin.HelpFlag.Short('h')
|
kingpin.HelpFlag.Short('h')
|
||||||
kingpin.Parse()
|
kingpin.Parse()
|
||||||
zonelist := strings.Split(*zones, ",")
|
|
||||||
// Always add trailing dot
|
|
||||||
for i, z := range zonelist {
|
|
||||||
if z[len(z)-1] != byte('.') {
|
|
||||||
zonelist[i] = z + "."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger := promlog.New(promlogConfig)
|
logger := promlog.New(promlogConfig)
|
||||||
logger = level.NewFilter(logger, level.AllowDebug())
|
logger = level.NewFilter(logger, level.AllowDebug())
|
||||||
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
|
|
||||||
metricsCollector := NewMetricsCollector(zonelist, *resolver, logger)
|
config := DnssecConfig{
|
||||||
|
DnsConfig: DnsConfig{
|
||||||
|
Zones: []string{"example.org"},
|
||||||
|
Resolver: "1.1.1.1:53",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if configFile != nil {
|
||||||
|
content, err := ioutil.ReadFile(*configFile)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log(err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(content, &config)
|
||||||
|
if err != nil {
|
||||||
|
level.Error(logger).Log(err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Always add trailing dot
|
||||||
|
for i, z := range config.DnsConfig.Zones {
|
||||||
|
if z[len(z)-1] != byte('.') {
|
||||||
|
config.DnsConfig.Zones[i] = z + "."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsCollector := NewMetricsCollector(config.DnsConfig.Zones, config.DnsConfig.Resolver, logger)
|
||||||
prom.MustRegister(metricsCollector)
|
prom.MustRegister(metricsCollector)
|
||||||
http.Handle(*metricsPath, promhttp.Handler())
|
http.Handle(*metricsPath, promhttp.Handler())
|
||||||
server := &http.Server{Addr: *listenAddress}
|
server := &http.Server{Addr: *listenAddress}
|
||||||
logger.Log(web.ListenAndServe(server, *configFile, logger))
|
logger.Log(web.ListenAndServe(server, *webConfigFile, logger))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue