Merge pull request #37 from shaned24/master
feat(ApiTokenAuth): Add support for using PiHole's api token
This commit is contained in:
commit
a124a6be35
4 changed files with 64 additions and 22 deletions
27
README.md
27
README.md
|
@ -49,6 +49,18 @@ $ docker run \
|
||||||
ekofr/pihole-exporter:latest
|
ekofr/pihole-exporter:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or use PiHole's `WEBPASSWORD` as an API token instead of the password
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ API_TOKEN=$(awk -F= -v key="WEBPASSWORD" '$1==key {print $2}' /etc/pihole/setupVars.conf)
|
||||||
|
$ docker run \
|
||||||
|
-e 'PIHOLE_HOSTNAME=192.168.1.2' \
|
||||||
|
-e "PIHOLE_API_TOKEN=$API_TOKEN" \
|
||||||
|
-e 'INTERVAL=30s' \
|
||||||
|
-e 'PORT=9617' \
|
||||||
|
ekofr/pihole-exporter:latest
|
||||||
|
```
|
||||||
|
|
||||||
### From sources
|
### From sources
|
||||||
|
|
||||||
Optionally, you can download and build it from the sources. You have to retrieve the project sources by using one of the following way:
|
Optionally, you can download and build it from the sources. You have to retrieve the project sources by using one of the following way:
|
||||||
|
@ -73,9 +85,20 @@ $ GOOS=linux GOARCH=arm GOARM=7 go build -o pihole_exporter .
|
||||||
|
|
||||||
In order to run the exporter, type the following command (arguments are optional):
|
In order to run the exporter, type the following command (arguments are optional):
|
||||||
|
|
||||||
|
Using a password
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ ./pihole_exporter -pihole_hostname 192.168.1.10 -pihole_password azerty
|
$ ./pihole_exporter -pihole_hostname 192.168.1.10 -pihole_password azerty
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use PiHole's `WEBPASSWORD` as an API token instead of the password
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ API_TOKEN=$(awk -F= -v key="WEBPASSWORD" '$1==key {print $2}' /etc/pihole/setupVars.conf)
|
||||||
|
$ ./pihole_exporter -pihole_hostname 192.168.1.10 -pihole_api_token $API_TOKEN
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
2019/05/09 20:19:52 ------------------------------------
|
2019/05/09 20:19:52 ------------------------------------
|
||||||
2019/05/09 20:19:52 - PI-Hole exporter configuration -
|
2019/05/09 20:19:52 - PI-Hole exporter configuration -
|
||||||
2019/05/09 20:19:52 ------------------------------------
|
2019/05/09 20:19:52 ------------------------------------
|
||||||
|
@ -126,6 +149,10 @@ scrape_configs:
|
||||||
# Password defined on the PI-Hole interface
|
# Password defined on the PI-Hole interface
|
||||||
-pihole_password string (optional)
|
-pihole_password string (optional)
|
||||||
|
|
||||||
|
|
||||||
|
# WEBPASSWORD / api token defined on the PI-Hole interface at `/etc/pihole/setupVars.conf`
|
||||||
|
-pihole_api_token string (optional)
|
||||||
|
|
||||||
# Port to be used for the exporter
|
# Port to be used for the exporter
|
||||||
-port string (optional) (default "9617")
|
-port string (optional) (default "9617")
|
||||||
```
|
```
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
type Config struct {
|
type Config struct {
|
||||||
PIHoleHostname string `config:"pihole_hostname"`
|
PIHoleHostname string `config:"pihole_hostname"`
|
||||||
PIHolePassword string `config:"pihole_password"`
|
PIHolePassword string `config:"pihole_password"`
|
||||||
|
PIHoleApiToken string `config:"pihole_api_token"`
|
||||||
Port string `config:"port"`
|
Port string `config:"port"`
|
||||||
Interval time.Duration `config:"interval"`
|
Interval time.Duration `config:"interval"`
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ func getDefaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
PIHoleHostname: "127.0.0.1",
|
PIHoleHostname: "127.0.0.1",
|
||||||
PIHolePassword: "",
|
PIHolePassword: "",
|
||||||
|
PIHoleApiToken: "",
|
||||||
Port: "9617",
|
Port: "9617",
|
||||||
Interval: 10 * time.Second,
|
Interval: 10 * time.Second,
|
||||||
}
|
}
|
||||||
|
@ -61,10 +61,18 @@ func (c Config) show() {
|
||||||
valueField := val.Field(i)
|
valueField := val.Field(i)
|
||||||
typeField := val.Type().Field(i)
|
typeField := val.Type().Field(i)
|
||||||
|
|
||||||
// Do not print password
|
// Do not print password or api token but do print the authentication method
|
||||||
if typeField.Name != "PIHolePassword" {
|
if typeField.Name != "PIHolePassword" && typeField.Name != "PIHoleApiToken" {
|
||||||
log.Println(fmt.Sprintf("%s : %v", typeField.Name, valueField.Interface()))
|
log.Println(fmt.Sprintf("%s : %v", typeField.Name, valueField.Interface()))
|
||||||
|
} else {
|
||||||
|
showAuthenticationMethod(typeField.Name, valueField.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Println("------------------------------------")
|
log.Println("------------------------------------")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showAuthenticationMethod(name, value string) {
|
||||||
|
if len(value) > 0 {
|
||||||
|
log.Println(fmt.Sprintf("Pi-Hole Authentication Method : %s", name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,13 +26,15 @@ type Client struct {
|
||||||
hostname string
|
hostname string
|
||||||
password string
|
password string
|
||||||
sessionID string
|
sessionID string
|
||||||
|
apiToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient method initializes a new PI-Hole client.
|
// NewClient method initializes a new PI-Hole client.
|
||||||
func NewClient(hostname, password string, interval time.Duration) *Client {
|
func NewClient(hostname, password, apiToken string, interval time.Duration) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
password: password,
|
password: password,
|
||||||
|
apiToken: apiToken,
|
||||||
interval: interval,
|
interval: interval,
|
||||||
httpClient: http.Client{
|
httpClient: http.Client{
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
@ -42,15 +44,12 @@ func NewClient(hostname, password string, interval time.Duration) *Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scrape method logins and retrieves statistics from PI-Hole JSON API
|
// Scrape method authenticates and retrieves statistics from PI-Hole JSON API
|
||||||
// and then pass them as Prometheus metrics.
|
// and then pass them as Prometheus metrics.
|
||||||
func (c *Client) Scrape() {
|
func (c *Client) Scrape() {
|
||||||
for range time.Tick(c.interval) {
|
for range time.Tick(c.interval) {
|
||||||
if c.isAuthenticated() {
|
|
||||||
c.sessionID = c.getPHPSessionID()
|
|
||||||
}
|
|
||||||
|
|
||||||
stats := c.getStatistics()
|
stats := c.getStatistics()
|
||||||
|
|
||||||
c.setMetrics(stats)
|
c.setMetrics(stats)
|
||||||
|
|
||||||
log.Printf("New tick of statistics: %s", stats.ToString())
|
log.Printf("New tick of statistics: %s", stats.ToString())
|
||||||
|
@ -133,12 +132,16 @@ func (c *Client) getStatistics() *Stats {
|
||||||
|
|
||||||
statsURL := fmt.Sprintf(statsURLPattern, c.hostname)
|
statsURL := fmt.Sprintf(statsURLPattern, c.hostname)
|
||||||
|
|
||||||
|
if c.isUsingApiToken() {
|
||||||
|
statsURL = fmt.Sprintf("%s&auth=%s", statsURL, c.apiToken)
|
||||||
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", statsURL, nil)
|
req, err := http.NewRequest("GET", statsURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("An error has occured when creating HTTP statistics request", err)
|
log.Fatal("An error has occured when creating HTTP statistics request", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.isAuthenticated() {
|
if c.isUsingPassword() {
|
||||||
c.authenticateRequest(req)
|
c.authenticateRequest(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,11 +163,15 @@ func (c *Client) getStatistics() *Stats {
|
||||||
return &stats
|
return &stats
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) isAuthenticated() bool {
|
func (c *Client) isUsingPassword() bool {
|
||||||
return len(c.password) > 0
|
return len(c.password) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) isUsingApiToken() bool {
|
||||||
|
return len(c.apiToken) > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) authenticateRequest(req *http.Request) {
|
func (c *Client) authenticateRequest(req *http.Request) {
|
||||||
cookie := http.Cookie{Name: "PHPSESSID", Value: c.sessionID}
|
cookie := http.Cookie{Name: "PHPSESSID", Value: c.getPHPSessionID()}
|
||||||
req.AddCookie(&cookie)
|
req.AddCookie(&cookie)
|
||||||
}
|
}
|
||||||
|
|
6
main.go
6
main.go
|
@ -26,14 +26,14 @@ func main() {
|
||||||
|
|
||||||
metrics.Init()
|
metrics.Init()
|
||||||
|
|
||||||
initPiholeClient(conf.PIHoleHostname, conf.PIHolePassword, conf.Interval)
|
initPiHoleClient(conf.PIHoleHostname, conf.PIHolePassword, conf.PIHoleApiToken, conf.Interval)
|
||||||
initHttpServer(conf.Port)
|
initHttpServer(conf.Port)
|
||||||
|
|
||||||
handleExitSignal()
|
handleExitSignal()
|
||||||
}
|
}
|
||||||
|
|
||||||
func initPiholeClient(hostname, password string, interval time.Duration) {
|
func initPiHoleClient(hostname, password, apiToken string, interval time.Duration) {
|
||||||
client := pihole.NewClient(hostname, password, interval)
|
client := pihole.NewClient(hostname, password, apiToken, interval)
|
||||||
go client.Scrape()
|
go client.Scrape()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue