Refactor core
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package acmedns
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Check if IP belongs to an allowed net
|
||||
func (a ACMETxt) AllowedFrom(ip string) bool {
|
||||
remoteIP := net.ParseIP(ip)
|
||||
// Range not limited
|
||||
if len(a.AllowFrom.ValidEntries()) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, v := range a.AllowFrom.ValidEntries() {
|
||||
_, vnet, _ := net.ParseCIDR(v)
|
||||
if vnet.Contains(remoteIP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if len(ips) == 0 {
|
||||
// If no IP provided, check if no whitelist present (everyone has access)
|
||||
return a.AllowedFrom("")
|
||||
}
|
||||
for _, v := range ips {
|
||||
if a.AllowedFrom(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func NewACMETxt() ACMETxt {
|
||||
var a = ACMETxt{}
|
||||
password := generatePassword(40)
|
||||
a.Username = uuid.New()
|
||||
a.Password = password
|
||||
a.Subdomain = uuid.New().String()
|
||||
return a
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package acmedns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
)
|
||||
|
||||
// cidrslice is a list of allowed cidr ranges
|
||||
type Cidrslice []string
|
||||
|
||||
func (c *Cidrslice) JSON() string {
|
||||
ret, _ := json.Marshal(c.ValidEntries())
|
||||
return string(ret)
|
||||
}
|
||||
|
||||
func (c *Cidrslice) IsValid() error {
|
||||
for _, v := range *c {
|
||||
_, _, err := net.ParseCIDR(sanitizeIPv6addr(v))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cidrslice) ValidEntries() []string {
|
||||
valid := []string{}
|
||||
for _, v := range *c {
|
||||
_, _, err := net.ParseCIDR(sanitizeIPv6addr(v))
|
||||
if err == nil {
|
||||
valid = append(valid, sanitizeIPv6addr(v))
|
||||
}
|
||||
}
|
||||
return valid
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package acmedns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
func FileIsAccessible(fname string) bool {
|
||||
_, err := os.Stat(fname)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
f.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
func ReadConfig(fname string) (AcmeDnsConfig, error) {
|
||||
var conf AcmeDnsConfig
|
||||
_, err := toml.DecodeFile(fname, &conf)
|
||||
if err != nil {
|
||||
// Return with config file parsing errors from toml package
|
||||
return conf, err
|
||||
}
|
||||
return prepareConfig(conf)
|
||||
}
|
||||
|
||||
// prepareConfig checks that mandatory values exist, and can be used to set default values in the future
|
||||
func prepareConfig(conf AcmeDnsConfig) (AcmeDnsConfig, error) {
|
||||
if conf.Database.Engine == "" {
|
||||
return conf, errors.New("missing database configuration option \"engine\"")
|
||||
}
|
||||
if conf.Database.Connection == "" {
|
||||
return conf, errors.New("missing database configuration option \"connection\"")
|
||||
}
|
||||
|
||||
// Default values for options added to config to keep backwards compatibility with old config
|
||||
if conf.API.ACMECacheDir == "" {
|
||||
conf.API.ACMECacheDir = "api-certs"
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package acmedns
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type AcmednsDB interface {
|
||||
Register(cidrslice Cidrslice) (ACMETxt, error)
|
||||
GetByUsername(uuid.UUID) (ACMETxt, error)
|
||||
GetTXTForDomain(string) ([]string, error)
|
||||
Update(ACMETxtPost) error
|
||||
GetBackend() *sql.DB
|
||||
SetBackend(*sql.DB)
|
||||
Close()
|
||||
}
|
||||
|
||||
type AcmednsNS interface {
|
||||
Start(errorChannel chan error)
|
||||
SetOwnAuthKey(key string)
|
||||
ParseRecords()
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package acmedns
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
type Account struct {
|
||||
Username string
|
||||
Password string
|
||||
Subdomain string
|
||||
}
|
||||
|
||||
// DNSConfig holds the config structure
|
||||
type AcmeDnsConfig struct {
|
||||
General general
|
||||
Database dbsettings
|
||||
API httpapi
|
||||
Logconfig logconfig
|
||||
}
|
||||
|
||||
// Config file general section
|
||||
type general struct {
|
||||
Listen string
|
||||
Proto string `toml:"protocol"`
|
||||
Domain string
|
||||
Nsname string
|
||||
Nsadmin string
|
||||
Debug bool
|
||||
StaticRecords []string `toml:"records"`
|
||||
}
|
||||
|
||||
type dbsettings struct {
|
||||
Engine string
|
||||
Connection string
|
||||
}
|
||||
|
||||
// API config
|
||||
type httpapi struct {
|
||||
Domain string `toml:"api_domain"`
|
||||
IP string
|
||||
DisableRegistration bool `toml:"disable_registration"`
|
||||
AutocertPort string `toml:"autocert_port"`
|
||||
Port string `toml:"port"`
|
||||
TLS string
|
||||
TLSCertPrivkey string `toml:"tls_cert_privkey"`
|
||||
TLSCertFullchain string `toml:"tls_cert_fullchain"`
|
||||
ACMECacheDir string `toml:"acme_cache_dir"`
|
||||
NotificationEmail string `toml:"notification_email"`
|
||||
CorsOrigins []string
|
||||
UseHeader bool `toml:"use_header"`
|
||||
HeaderName string `toml:"header_name"`
|
||||
}
|
||||
|
||||
// Logging config
|
||||
type logconfig struct {
|
||||
Level string `toml:"loglevel"`
|
||||
Logtype string `toml:"logtype"`
|
||||
File string `toml:"logfile"`
|
||||
Format string `toml:"logformat"`
|
||||
}
|
||||
|
||||
// ACMETxt is the default structure for the user controlled record
|
||||
type ACMETxt struct {
|
||||
Username uuid.UUID
|
||||
Password string
|
||||
ACMETxtPost
|
||||
AllowFrom Cidrslice
|
||||
}
|
||||
|
||||
// ACMETxtPost holds the DNS part of the ACMETxt struct
|
||||
type ACMETxtPost struct {
|
||||
Subdomain string `json:"subdomain"`
|
||||
Value string `json:"txt"`
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package acmedns
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func sanitizeIPv6addr(s string) string {
|
||||
// Remove brackets from IPv6 addresses, net.ParseCIDR needs this
|
||||
re, _ := regexp.Compile(`[\[\]]+`)
|
||||
return re.ReplaceAllString(s, "")
|
||||
}
|
||||
|
||||
func SanitizeString(s string) string {
|
||||
// URL safe base64 alphabet without padding as defined in ACME
|
||||
re, _ := regexp.Compile(`[^A-Za-z\-\_0-9]+`)
|
||||
return re.ReplaceAllString(s, "")
|
||||
}
|
||||
|
||||
func generatePassword(length int) string {
|
||||
ret := make([]byte, length)
|
||||
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_"
|
||||
alphalen := big.NewInt(int64(len(alphabet)))
|
||||
for i := 0; i < length; i++ {
|
||||
c, _ := rand.Int(rand.Reader, alphalen)
|
||||
r := int(c.Int64())
|
||||
ret[i] = alphabet[r]
|
||||
}
|
||||
return string(ret)
|
||||
}
|
||||
Reference in New Issue
Block a user