Add HTTP Basic authentication for /metrics endpoint
This commit is contained in:
parent
79ff381c4c
commit
877dbed5e8
4 changed files with 65 additions and 3 deletions
|
@ -72,6 +72,8 @@ const (
|
||||||
defaultMetricsCollector = false
|
defaultMetricsCollector = false
|
||||||
defaultMetricsRefreshInterval = 60
|
defaultMetricsRefreshInterval = 60
|
||||||
defaultMetricsAllowedNetworks = "127.0.0.1/8"
|
defaultMetricsAllowedNetworks = "127.0.0.1/8"
|
||||||
|
defaultMetricsUsername = ""
|
||||||
|
defaultMetricsPassword = ""
|
||||||
defaultWatchdog = true
|
defaultWatchdog = true
|
||||||
defaultInvidiousInstance = "yewtu.be"
|
defaultInvidiousInstance = "yewtu.be"
|
||||||
)
|
)
|
||||||
|
@ -144,6 +146,8 @@ type Options struct {
|
||||||
metricsCollector bool
|
metricsCollector bool
|
||||||
metricsRefreshInterval int
|
metricsRefreshInterval int
|
||||||
metricsAllowedNetworks []string
|
metricsAllowedNetworks []string
|
||||||
|
metricsUsername string
|
||||||
|
metricsPassword string
|
||||||
watchdog bool
|
watchdog bool
|
||||||
invidiousInstance string
|
invidiousInstance string
|
||||||
proxyPrivateKey []byte
|
proxyPrivateKey []byte
|
||||||
|
@ -211,6 +215,8 @@ func NewOptions() *Options {
|
||||||
metricsCollector: defaultMetricsCollector,
|
metricsCollector: defaultMetricsCollector,
|
||||||
metricsRefreshInterval: defaultMetricsRefreshInterval,
|
metricsRefreshInterval: defaultMetricsRefreshInterval,
|
||||||
metricsAllowedNetworks: []string{defaultMetricsAllowedNetworks},
|
metricsAllowedNetworks: []string{defaultMetricsAllowedNetworks},
|
||||||
|
metricsUsername: defaultMetricsUsername,
|
||||||
|
metricsPassword: defaultMetricsPassword,
|
||||||
watchdog: defaultWatchdog,
|
watchdog: defaultWatchdog,
|
||||||
invidiousInstance: defaultInvidiousInstance,
|
invidiousInstance: defaultInvidiousInstance,
|
||||||
proxyPrivateKey: randomKey,
|
proxyPrivateKey: randomKey,
|
||||||
|
@ -513,6 +519,14 @@ func (o *Options) MetricsAllowedNetworks() []string {
|
||||||
return o.metricsAllowedNetworks
|
return o.metricsAllowedNetworks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Options) MetricsUsername() string {
|
||||||
|
return o.metricsUsername
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) MetricsPassword() string {
|
||||||
|
return o.metricsPassword
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPClientUserAgent returns the global User-Agent header for miniflux.
|
// HTTPClientUserAgent returns the global User-Agent header for miniflux.
|
||||||
func (o *Options) HTTPClientUserAgent() string {
|
func (o *Options) HTTPClientUserAgent() string {
|
||||||
return o.httpClientUserAgent
|
return o.httpClientUserAgent
|
||||||
|
@ -576,6 +590,8 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
|
||||||
"METRICS_ALLOWED_NETWORKS": strings.Join(o.metricsAllowedNetworks, ","),
|
"METRICS_ALLOWED_NETWORKS": strings.Join(o.metricsAllowedNetworks, ","),
|
||||||
"METRICS_COLLECTOR": o.metricsCollector,
|
"METRICS_COLLECTOR": o.metricsCollector,
|
||||||
"METRICS_REFRESH_INTERVAL": o.metricsRefreshInterval,
|
"METRICS_REFRESH_INTERVAL": o.metricsRefreshInterval,
|
||||||
|
"METRICS_USERNAME": o.metricsUsername,
|
||||||
|
"METRICS_PASSWORD": redactSecretValue(o.metricsPassword, redactSecret),
|
||||||
"OAUTH2_CLIENT_ID": o.oauth2ClientID,
|
"OAUTH2_CLIENT_ID": o.oauth2ClientID,
|
||||||
"OAUTH2_CLIENT_SECRET": redactSecretValue(o.oauth2ClientSecret, redactSecret),
|
"OAUTH2_CLIENT_SECRET": redactSecretValue(o.oauth2ClientSecret, redactSecret),
|
||||||
"OAUTH2_OIDC_DISCOVERY_ENDPOINT": o.oauth2OidcDiscoveryEndpoint,
|
"OAUTH2_OIDC_DISCOVERY_ENDPOINT": o.oauth2OidcDiscoveryEndpoint,
|
||||||
|
@ -626,7 +642,7 @@ func (o *Options) String() string {
|
||||||
|
|
||||||
func redactSecretValue(value string, redactSecret bool) string {
|
func redactSecretValue(value string, redactSecret bool) string {
|
||||||
if redactSecret && value != "" {
|
if redactSecret && value != "" {
|
||||||
return "******"
|
return "<secret>"
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,6 +207,14 @@ func (p *Parser) parseLines(lines []string) (err error) {
|
||||||
p.opts.metricsRefreshInterval = parseInt(value, defaultMetricsRefreshInterval)
|
p.opts.metricsRefreshInterval = parseInt(value, defaultMetricsRefreshInterval)
|
||||||
case "METRICS_ALLOWED_NETWORKS":
|
case "METRICS_ALLOWED_NETWORKS":
|
||||||
p.opts.metricsAllowedNetworks = parseStringList(value, []string{defaultMetricsAllowedNetworks})
|
p.opts.metricsAllowedNetworks = parseStringList(value, []string{defaultMetricsAllowedNetworks})
|
||||||
|
case "METRICS_USERNAME":
|
||||||
|
p.opts.metricsUsername = parseString(value, defaultMetricsUsername)
|
||||||
|
case "METRICS_USERNAME_FILE":
|
||||||
|
p.opts.metricsUsername = readSecretFile(value, defaultMetricsUsername)
|
||||||
|
case "METRICS_PASSWORD":
|
||||||
|
p.opts.metricsPassword = parseString(value, defaultMetricsPassword)
|
||||||
|
case "METRICS_PASSWORD_FILE":
|
||||||
|
p.opts.metricsPassword = readSecretFile(value, defaultMetricsPassword)
|
||||||
case "FETCH_YOUTUBE_WATCH_TIME":
|
case "FETCH_YOUTUBE_WATCH_TIME":
|
||||||
p.opts.fetchYouTubeWatchTime = parseBool(value, defaultFetchYouTubeWatchTime)
|
p.opts.fetchYouTubeWatchTime = parseBool(value, defaultFetchYouTubeWatchTime)
|
||||||
case "WATCHDOG":
|
case "WATCHDOG":
|
||||||
|
|
20
miniflux.1
20
miniflux.1
|
@ -283,6 +283,26 @@ List of networks allowed to access the metrics endpoint (comma-separated values)
|
||||||
.br
|
.br
|
||||||
Default is 127.0.0.1/8\&.
|
Default is 127.0.0.1/8\&.
|
||||||
.TP
|
.TP
|
||||||
|
.B METRICS_USERNAME
|
||||||
|
Metrics endpoint username for basic HTTP authentication\&.
|
||||||
|
.br
|
||||||
|
Default is emtpty\&.
|
||||||
|
.TP
|
||||||
|
.B METRICS_USERNAME_FILE
|
||||||
|
Path to a file that contains the username for the metrics endpoint HTTP authentication\&.
|
||||||
|
.br
|
||||||
|
Default is emtpty\&.
|
||||||
|
.TP
|
||||||
|
.B METRICS_PASSWORD
|
||||||
|
Metrics endpoint password for basic HTTP authentication\&.
|
||||||
|
.br
|
||||||
|
Default is emtpty\&.
|
||||||
|
.TP
|
||||||
|
.B METRICS_PASSWORD_FILE
|
||||||
|
Path to a file that contains the password for the metrics endpoint HTTP authentication\&.
|
||||||
|
.br
|
||||||
|
Default is emtpty\&.
|
||||||
|
.TP
|
||||||
.B OAUTH2_PROVIDER
|
.B OAUTH2_PROVIDER
|
||||||
Possible values are "google" or "oidc"\&.
|
Possible values are "google" or "oidc"\&.
|
||||||
.br
|
.br
|
||||||
|
|
|
@ -222,7 +222,25 @@ func setupHandler(store *storage.Storage, pool *worker.Pool) *mux.Router {
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAllowedToAccessMetricsEndpoint(r *http.Request) bool {
|
func isAllowedToAccessMetricsEndpoint(r *http.Request) bool {
|
||||||
clientIP := net.ParseIP(request.ClientIP(r))
|
clientIP := request.ClientIP(r)
|
||||||
|
|
||||||
|
if config.Opts.MetricsUsername() != "" && config.Opts.MetricsPassword() != "" {
|
||||||
|
username, password, authOK := r.BasicAuth()
|
||||||
|
if !authOK {
|
||||||
|
logger.Info("[Metrics] [ClientIP=%s] No authentication header sent", clientIP)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if username == "" || password == "" {
|
||||||
|
logger.Info("[Metrics] [ClientIP=%s] Empty username or password", clientIP)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if username != config.Opts.MetricsUsername() || password != config.Opts.MetricsPassword() {
|
||||||
|
logger.Error("[Metrics] [ClientIP=%s] Invalid username or password", clientIP)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, cidr := range config.Opts.MetricsAllowedNetworks() {
|
for _, cidr := range config.Opts.MetricsAllowedNetworks() {
|
||||||
_, network, err := net.ParseCIDR(cidr)
|
_, network, err := net.ParseCIDR(cidr)
|
||||||
|
@ -230,7 +248,7 @@ func isAllowedToAccessMetricsEndpoint(r *http.Request) bool {
|
||||||
logger.Fatal(`[Metrics] Unable to parse CIDR %v`, err)
|
logger.Fatal(`[Metrics] Unable to parse CIDR %v`, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if network.Contains(clientIP) {
|
if network.Contains(net.ParseIP(clientIP)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue