mirror of
https://github.com/joohoi/acme-dns.git
synced 2025-02-02 12:23:50 +07:00
Added config option to check for a header value for clinet IP
This commit is contained in:
parent
8c99346b01
commit
bf9eaf2f32
11
acmetxt.go
11
acmetxt.go
@ -57,6 +57,17 @@ func (a ACMETxt) allowedFrom(ip string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go through list (most likely from headers) to check for the IP.
|
||||||
|
// Reason for this is that some setups use reverse proxy in front of acme-dns
|
||||||
|
func (a ACMETxt) allowedFromList(ips []string) bool {
|
||||||
|
for _, v := range ips {
|
||||||
|
if a.allowedFrom(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func newACMETxt() ACMETxt {
|
func newACMETxt() ACMETxt {
|
||||||
var a = ACMETxt{}
|
var a = ACMETxt{}
|
||||||
password := generatePassword(40)
|
password := generatePassword(40)
|
||||||
|
12
api.go
12
api.go
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
// Serve is an authentication middlware function used to authenticate update requests
|
// Serve is an authentication middlware function used to authenticate update requests
|
||||||
func (a authMiddleware) Serve(ctx *iris.Context) {
|
func (a authMiddleware) Serve(ctx *iris.Context) {
|
||||||
|
allowUpdate := false
|
||||||
usernameStr := ctx.RequestHeader("X-Api-User")
|
usernameStr := ctx.RequestHeader("X-Api-User")
|
||||||
password := ctx.RequestHeader("X-Api-Key")
|
password := ctx.RequestHeader("X-Api-Key")
|
||||||
postData := ACMETxt{}
|
postData := ACMETxt{}
|
||||||
@ -23,7 +24,16 @@ func (a authMiddleware) Serve(ctx *iris.Context) {
|
|||||||
} else {
|
} else {
|
||||||
if correctPassword(password, au.Password) {
|
if correctPassword(password, au.Password) {
|
||||||
// Password ok
|
// Password ok
|
||||||
if au.allowedFrom(ctx.RequestIP()) {
|
|
||||||
|
// Now test for the possibly limited ranges
|
||||||
|
if DNSConf.API.UseHeader {
|
||||||
|
ips := getIPListFromHeader(ctx.RequestHeader(DNSConf.API.HeaderName))
|
||||||
|
allowUpdate = au.allowedFromList(ips)
|
||||||
|
} else {
|
||||||
|
allowUpdate = au.allowedFrom(ctx.RequestIP())
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowUpdate {
|
||||||
// Update is allowed from remote addr
|
// Update is allowed from remote addr
|
||||||
if err := ctx.ReadJSON(&postData); err == nil {
|
if err := ctx.ReadJSON(&postData); err == nil {
|
||||||
if au.Subdomain == postData.Subdomain {
|
if au.Subdomain == postData.Subdomain {
|
||||||
|
47
api_test.go
47
api_test.go
@ -19,6 +19,8 @@ func setupIris(t *testing.T, debug bool, noauth bool) *httpexpect.Expect {
|
|||||||
Port: "8080",
|
Port: "8080",
|
||||||
TLS: "none",
|
TLS: "none",
|
||||||
CorsOrigins: []string{"*"},
|
CorsOrigins: []string{"*"},
|
||||||
|
UseHeader: false,
|
||||||
|
HeaderName: "X-Forwarded-For",
|
||||||
}
|
}
|
||||||
var dnscfg = DNSConfig{
|
var dnscfg = DNSConfig{
|
||||||
API: httpapicfg,
|
API: httpapicfg,
|
||||||
@ -206,3 +208,48 @@ func TestApiManyUpdateWithCredentials(t *testing.T) {
|
|||||||
Status(test.status)
|
Status(test.status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApiManyUpdateWithIpCheckHeaders(t *testing.T) {
|
||||||
|
|
||||||
|
updateJSON := map[string]interface{}{
|
||||||
|
"subdomain": "",
|
||||||
|
"txt": ""}
|
||||||
|
|
||||||
|
e := setupIris(t, false, false)
|
||||||
|
// Use header checks from default header (X-Forwarded-For)
|
||||||
|
DNSConf.API.UseHeader = true
|
||||||
|
// User without defined CIDR masks
|
||||||
|
newUser, err := DB.Register(cidrslice{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not create new user, got error [%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newUserWithCIDR, err := DB.Register(cidrslice{"192.168.1.2/32", "invalid"})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not create new user with CIDR, got error [%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
user ACMETxt
|
||||||
|
headerValue string
|
||||||
|
status int
|
||||||
|
}{
|
||||||
|
{newUser, "whatever goes", 200},
|
||||||
|
{newUser, "10.0.0.1, 1.2.3.4 ,3.4.5.6", 200},
|
||||||
|
{newUserWithCIDR, "127.0.0.1", 401},
|
||||||
|
{newUserWithCIDR, "10.0.0.1, 10.0.0.2, 192.168.1.3", 401},
|
||||||
|
{newUserWithCIDR, "10.1.1.1 ,192.168.1.2, 8.8.8.8", 200},
|
||||||
|
} {
|
||||||
|
updateJSON = map[string]interface{}{
|
||||||
|
"subdomain": test.user.Subdomain,
|
||||||
|
"txt": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
|
||||||
|
e.POST("/update").
|
||||||
|
WithJSON(updateJSON).
|
||||||
|
WithHeader("X-Api-User", test.user.Username.String()).
|
||||||
|
WithHeader("X-Api-Key", test.user.Password).
|
||||||
|
WithHeader("X-Forwarded-For", test.headerValue).
|
||||||
|
Expect().
|
||||||
|
Status(test.status)
|
||||||
|
}
|
||||||
|
DNSConf.API.UseHeader = false
|
||||||
|
}
|
||||||
|
@ -44,6 +44,10 @@ tls_cert_fullchain = "/etc/tls/example.org/fullchain.pem"
|
|||||||
corsorigins = [
|
corsorigins = [
|
||||||
"*"
|
"*"
|
||||||
]
|
]
|
||||||
|
# use HTTP header to get the client ip
|
||||||
|
use_header = false
|
||||||
|
# header name to pull the ip address / list of ip addresses from
|
||||||
|
header_name = "X-Forwarded-For"
|
||||||
|
|
||||||
[logconfig]
|
[logconfig]
|
||||||
# logging level: "error", "warning", "info" or "debug"
|
# logging level: "error", "warning", "info" or "debug"
|
||||||
|
@ -68,6 +68,8 @@ func setupConfig() {
|
|||||||
Port: "8080",
|
Port: "8080",
|
||||||
TLS: "none",
|
TLS: "none",
|
||||||
CorsOrigins: []string{"*"},
|
CorsOrigins: []string{"*"},
|
||||||
|
UseHeader: false,
|
||||||
|
HeaderName: "X-Forwarded-For",
|
||||||
}
|
}
|
||||||
|
|
||||||
var dnscfg = DNSConfig{
|
var dnscfg = DNSConfig{
|
||||||
|
2
types.go
2
types.go
@ -56,6 +56,8 @@ type httpapi struct {
|
|||||||
TLSCertPrivkey string `toml:"tls_cert_privkey"`
|
TLSCertPrivkey string `toml:"tls_cert_privkey"`
|
||||||
TLSCertFullchain string `toml:"tls_cert_fullchain"`
|
TLSCertFullchain string `toml:"tls_cert_fullchain"`
|
||||||
CorsOrigins []string
|
CorsOrigins []string
|
||||||
|
UseHeader bool `toml:"use_header"`
|
||||||
|
HeaderName string `toml:"header_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging config
|
// Logging config
|
||||||
|
18
util.go
18
util.go
@ -2,12 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func readConfig(fname string) DNSConfig {
|
func readConfig(fname string) DNSConfig {
|
||||||
@ -68,3 +69,14 @@ func startDNS(listen string, proto string) *dns.Server {
|
|||||||
go server.ListenAndServe()
|
go server.ListenAndServe()
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getIPListFromHeader(header string) []string {
|
||||||
|
iplist := []string{}
|
||||||
|
for _, v := range strings.Split(header, ",") {
|
||||||
|
if len(v) > 0 {
|
||||||
|
// Ignore empty values
|
||||||
|
iplist = append(iplist, strings.TrimSpace(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iplist
|
||||||
|
}
|
||||||
|
24
util_test.go
24
util_test.go
@ -71,3 +71,27 @@ func TestReadConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetIPListFromHeader(t *testing.T) {
|
||||||
|
for i, test := range []struct {
|
||||||
|
input string
|
||||||
|
output []string
|
||||||
|
}{
|
||||||
|
{"1.1.1.1, 2.2.2.2", []string{"1.1.1.1", "2.2.2.2"}},
|
||||||
|
{" 1.1.1.1 , 2.2.2.2", []string{"1.1.1.1", "2.2.2.2"}},
|
||||||
|
{",1.1.1.1 ,2.2.2.2", []string{"1.1.1.1", "2.2.2.2"}},
|
||||||
|
} {
|
||||||
|
res := getIPListFromHeader(test.input)
|
||||||
|
if len(res) != len(test.output) {
|
||||||
|
t.Errorf("Test %d: Expected [%d] items in return list, but got [%d]", i, len(test.output), len(res))
|
||||||
|
} else {
|
||||||
|
|
||||||
|
for j, vv := range test.output {
|
||||||
|
if res[j] != vv {
|
||||||
|
t.Errorf("Test %d: Expected return value [%v] but got [%v]", j, test.output, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user