Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,25 @@ func handleContainer(container container.Summary, piholeClient *pihole.Client, n
msg += ". In dry run mode, not doing anything."
log.Println(msg)
} else {
msg += ", adding entries to pi-hole and nginx proxy mananger."
msg += ", adding entries to Pi-Hole and Nginx Proxy Manager."
log.Println(msg)

piholeClient.AddDNSHostEntry(url, ip)
err := piholeClient.AddDNSHostEntry(url, ip)
if err != nil {
log.Printf("ERROR failed to add entry to Pi-Hole: %v", err)
}

npmClient.AddProxyHost(npm.ProxyHost{
err = npmClient.AddProxyHost(npm.ProxyHost{
DomainNames: []string{url},
ForwardScheme: "http",
ForwardHost: ip,
ForwardPort: port,
Locations: []npm.Location{},
Meta: map[string]any{},
})
if err != nil {
log.Printf("ERROR failed to add entry to Nginx Proxy Manager: %v", err)
}
}
}

Expand Down Expand Up @@ -88,7 +94,7 @@ func main() {
piHolePassword := envVars["PIHOLE_PASSWORD"]
err = piholeClient.Login(piHolePassword)
if err != nil {
log.Fatal(err)
log.Fatalf("ERROR failed to login to Pi-Hole: %v", err)
}

npmHost := envVars["NGINX_PROXY_MANAGER_HOST"]
Expand All @@ -97,7 +103,7 @@ func main() {
npmClient := npm.NewClient(npmHost, npmUser, npmPassword)
err = npmClient.Login()
if err != nil {
log.Fatal(err)
log.Fatalf("ERROR failed to login to Nginx Proxy Manager: %v", err)
}

dockerClient, err := docker.NewClient()
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type f struct {
var flags f = f{}

func ParseFlags() f {
flag.BoolVarP(&flags.DryRun, "dry-run", "d", false, "Simulates the process of adding DNS records and proxy hosts without making any actual changes to Pi-hole or Nginx Proxy Manager.")
flag.BoolVarP(&flags.DryRun, "dry-run", "d", false, "Simulates the process of adding DNS records and proxy hosts without making any actual changes to Pi-Hole or Nginx Proxy Manager.")
flag.Parse()
return flags
}
37 changes: 15 additions & 22 deletions pkg/clients/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package clients

import (
"io"
"log"
"net/http"
"strings"
)
Expand All @@ -13,84 +12,78 @@ func setHeaders(req *http.Request, headers map[string]string) {
}
}

func Post(client *http.Client, path string, headers map[string]string, data *string) (string, int) {
func Post(client *http.Client, path string, headers map[string]string, data *string) (string, int, error) {
req, err := http.NewRequest(
http.MethodPost,
path,
strings.NewReader(*data),
)
if err != nil {
log.Fatal(err)
return "", 0, err
}

setHeaders(req, headers)

resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
return "", 0, err
}

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
return "", 0, err
}

return string(body), resp.StatusCode
return string(body), resp.StatusCode, nil
}

func Get(client *http.Client, path string, headers map[string]string) (string, int) {
func Get(client *http.Client, path string, headers map[string]string) (string, int, error) {
req, err := http.NewRequest(
http.MethodGet,
path,
nil,
)
if err != nil {
log.Fatal(err)
return "", 0, err
}

setHeaders(req, headers)

resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode >= 400 {
log.Fatal(resp.StatusCode)
return "", 0, err
}

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
return "", 0, err
}
return string(body), resp.StatusCode
return string(body), resp.StatusCode, nil
}

func Patch(client *http.Client, path string, headers map[string]string, data string) (string, int) {
func Patch(client *http.Client, path string, headers map[string]string, data string) (string, int, error) {
req, err := http.NewRequest(
http.MethodPatch,
path,
strings.NewReader(data),
)
if err != nil {
log.Fatal(err)
return "", 0, nil
}

setHeaders(req, headers)

resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode >= 400 {
log.Fatal(resp.StatusCode)
return "", 0, err
}

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
return "", 0, err
}
return string(body), resp.StatusCode
return string(body), resp.StatusCode, nil
}
65 changes: 43 additions & 22 deletions pkg/clients/npm/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package npm

import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
Expand Down Expand Up @@ -37,24 +38,33 @@ func (n *Client) Login() error {
}
payloadBytes, err := json.Marshal(loginPayload)
if err != nil {
log.Fatal(err)
return err
}
payloadString := string(payloadBytes)
loginResponseString, statusCode := clients.Post(&n.Client, n.baseURL+"/tokens", headers, &payloadString)
loginResponseString, statusCode, err := clients.Post(&n.Client, n.baseURL+"/tokens", headers, &payloadString)
if err != nil {
return err
}

var resp Token
json.Unmarshal([]byte(loginResponseString), &resp)
if statusCode >= 400 || resp.Token == "" {
return fmt.Errorf("ERROR loging in to Nginx Proxy Manager: %v", loginResponseString)
err = json.Unmarshal([]byte(loginResponseString), &resp)
if statusCode >= 400 || err != nil || resp.Token == "" {
var loginError ErrorResponse
json.Unmarshal([]byte(loginResponseString), &loginError)
return errors.New(loginError.Error.Message)
}
n.token = resp.Token
headers["authorization"] = "Bearer " + n.token
return nil
}

func (n *Client) getProxyHosts() (map[string]string, error) {
proxyHostsString, statusCode := clients.Get(&n.Client, n.baseURL+"/nginx/proxy-hosts", headers)
if statusCode >= 400 {
return nil, fmt.Errorf("ERROR getting proxy hosts from nginx proxy manager")
proxyHostsString, statusCode, err := clients.Get(&n.Client, n.baseURL+"/nginx/proxy-hosts", headers)
if statusCode == 401 {
n.refreshToken()
return n.getProxyHosts()
} else if statusCode >= 400 {
return nil, err
}
var proxyHosts []ProxyHost
existingProxyHostsMap := map[string]string{}
Expand All @@ -69,34 +79,45 @@ func (n *Client) getProxyHosts() (map[string]string, error) {
return existingProxyHostsMap, nil
}

func (n *Client) refreshToken() {
log.Println("Refreshing nginx proxy manager token")
if err := n.Login(); err != nil {
log.Fatal(err)
}
func (n *Client) refreshToken() error {
log.Println("Refreshing Nginx Proxy Manager token")
return n.Login()
}

func (n *Client) AddProxyHost(host ProxyHost) {
func (n *Client) AddProxyHost(host ProxyHost) error {
existingProxyHosts, err := n.getProxyHosts()
if err != nil {
n.refreshToken()
existingProxyHosts, _ = n.getProxyHosts()
return err
}

for _, domainName := range host.DomainNames {
if _, exists := existingProxyHosts[domainName]; exists {
return
return nil
}
}

payloadBytes, err := json.Marshal(host)
if err != nil {
log.Fatal(err)
return err
}

payloadString := string(payloadBytes)
_, statusCode := clients.Post(&n.Client, n.baseURL+"/nginx/proxy-hosts", headers, &payloadString)
resp, statusCode, err := clients.Post(&n.Client, n.baseURL+"/nginx/proxy-hosts", headers, &payloadString)
if err != nil {
return err
}
if statusCode == 401 {
n.refreshToken()
clients.Post(&n.Client, n.baseURL+"/nginx/proxy-hosts", headers, &payloadString)
err := n.refreshToken()
if err != nil {
return err
}
_, _, err = clients.Post(&n.Client, n.baseURL+"/nginx/proxy-hosts", headers, &payloadString)
if err != nil {
return err
}
} else {
var errorResponse ErrorResponse
json.Unmarshal([]byte(resp), &errorResponse)
return errors.New(errorResponse.Error.Message)
}
return nil
}
7 changes: 7 additions & 0 deletions pkg/clients/npm/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ type LoginRequest struct {
Secret string `json:"secret"`
}

type ErrorResponse struct {
Error struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"error"`
}

type Token struct {
Token string `json:"token"`
}
Expand Down
43 changes: 28 additions & 15 deletions pkg/clients/pihole/pihole.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pihole

import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
Expand Down Expand Up @@ -31,13 +32,15 @@ func NewClient(baseURL string) *Client {

func (p *Client) Login(password string) error {
loginPayload := fmt.Sprintf(`{"password": "%v"}`, password)
loginResponseString, statusCode := clients.Post(&p.Client, p.baseURL+"/auth", headers, &loginPayload)

loginResponseString, statusCode, err := clients.Post(&p.Client, p.baseURL+"/auth", headers, &loginPayload)
if err != nil {
return err
}
var resp loginResponse
json.Unmarshal([]byte(loginResponseString), &resp)

if statusCode >= 400 || resp.Session.Sid == "" {
return fmt.Errorf("ERROR loging in to Pi-Hole: %v", loginResponseString)
return fmt.Errorf(resp.Session.Message)
}

p.sid = resp.Session.Sid
Expand All @@ -59,35 +62,40 @@ func dnsHostEntryToRawEntry(domain DomainName, ip IP) string {
return fmt.Sprintf("%v %v", ip, domain)
}

func (p *Client) getDNSHosts() DNSHostEntries {
func (p *Client) getDNSHosts() (DNSHostEntries, error) {
if p.sid == "" {
// TODO:
log.Fatal("no SID")
}
headers["X-FTL-SID"] = p.sid
configResponseString, _ := clients.Get(&p.Client, p.baseURL+"/config", headers)

configResponseString, _, err := clients.Get(&p.Client, p.baseURL+"/config", headers)
if err != nil {
return nil, err
}
var resp configResponse
json.Unmarshal([]byte(configResponseString), &resp)

dnsHostEntries := DNSHostEntries{}
for _, rawDNSHostEntry := range resp.Config.DNS.Hosts {
domain, ip, err := rawDNSHostRawEntryToEntry(rawDNSHostEntry)
if err != nil {
log.Fatal(err)
return nil, err
}
dnsHostEntries[domain] = ip
}
return dnsHostEntries
return dnsHostEntries, nil
}

func (p *Client) AddDNSHostEntry(domain, ip string) {
existingEntries := p.getDNSHosts()
func (p *Client) AddDNSHostEntry(domain, ip string) error {
existingEntries, err := p.getDNSHosts()
if err != nil {
return err
}
d := DomainName(domain)
_, exists := existingEntries[d]

if exists {
return
return nil
}

existingEntries[d] = IP(ip)
Expand All @@ -102,16 +110,21 @@ func (p *Client) AddDNSHostEntry(domain, ip string) {

payloadString, err := json.Marshal(payload)
if err != nil {
log.Fatal(err)
return err
}

if p.sid == "" {
// TODO:
log.Fatal("no SID")
}
headers["X-FTL-SID"] = p.sid
resp, _ := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
resp, statusCode, err := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
if err != nil {
return err
}
if statusCode >= 400 {
return errors.New(resp)
}

var r configResponse
json.Unmarshal([]byte(resp), &r)
return nil
}