handle multidomains, modified schema structure
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing

This commit is contained in:
Paul 2021-01-24 18:42:12 +01:00
parent 90650aacfa
commit 7c7a6a2e35
5 changed files with 50 additions and 33 deletions

View File

@ -5,7 +5,7 @@ import "time"
// Entry is the main struct for stored certificates // Entry is the main struct for stored certificates
type Entry struct { type Entry struct {
ID int `xorm:"pk autoincr"` ID int `xorm:"pk autoincr"`
Domain string `xorm:"notnull"` Domains string `xorm:"notnull"`
Certificate string `xorm:"text notnull"` Certificate string `xorm:"text notnull"`
PrivateKey string `xorm:"text notnull"` PrivateKey string `xorm:"text notnull"`
AuthURL string AuthURL string

View File

@ -9,6 +9,7 @@ import (
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"log" "log"
"strings"
"time" "time"
"git.paulbsd.com/paulbsd/pki/src/cert" "git.paulbsd.com/paulbsd/pki/src/cert"
@ -29,14 +30,15 @@ func (u *User) Init(cfg *config.Config) (err error) {
} }
// GetEntry returns requested acme ressource in database relative to domain // GetEntry returns requested acme ressource in database relative to domain
func (u *User) GetEntry(cfg *config.Config, domain string) (Entry cert.Entry, err error) { func (u *User) GetEntry(cfg *config.Config, domains []string) (Entry cert.Entry, err error) {
todaydate := time.Now().Format("2006-01-02")
requireddate := time.Now().AddDate(0, 0, -cfg.ACME.MaxDaysBefore).Format("2006-01-02") requireddate := time.Now().AddDate(0, 0, -cfg.ACME.MaxDaysBefore).Format("2006-01-02")
has, err := cfg.Db.Where("domain = ?", domain).Where( has, err := cfg.Db.Where("domains = ?", strings.Join(domains, ",")).Where(
"validity_begin < ?::date", todaydate).Where( "validity_begin <= now()").Where(
"validity_end > ?::date", requireddate).Where( "validity_end >= ?::timestamp", requireddate).Where(
"auth_url = ?", cfg.ACME.AuthURL).Get(&Entry) "auth_url = ?", cfg.ACME.AuthURL).Desc(
"id").Get(&Entry)
if !has { if !has {
err = fmt.Errorf("Entry doesn't exists") err = fmt.Errorf("Entry doesn't exists")
} }
@ -67,7 +69,7 @@ func (u *User) HandleRegistration(cfg *config.Config, client *lego.Client) (err
} }
// RequestNewCert returns a newly requested certificate to letsencrypt // RequestNewCert returns a newly requested certificate to letsencrypt
func (u *User) RequestNewCert(cfg *config.Config, domain string) (certificates *certificate.Resource, err error) { func (u *User) RequestNewCert(cfg *config.Config, domains []string) (certificates *certificate.Resource, err error) {
legoconfig := lego.NewConfig(u) legoconfig := lego.NewConfig(u)
legoconfig.CADirURL = cfg.ACME.AuthURL legoconfig.CADirURL = cfg.ACME.AuthURL
legoconfig.Certificate.KeyType = certcrypto.RSA2048 legoconfig.Certificate.KeyType = certcrypto.RSA2048
@ -93,7 +95,7 @@ func (u *User) RequestNewCert(cfg *config.Config, domain string) (certificates *
} }
request := certificate.ObtainRequest{ request := certificate.ObtainRequest{
Domains: []string{domain}, Domains: domains,
Bundle: true, Bundle: true,
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"strings"
"git.paulbsd.com/paulbsd/pki/src/config" "git.paulbsd.com/paulbsd/pki/src/config"
"git.paulbsd.com/paulbsd/pki/src/pki" "git.paulbsd.com/paulbsd/pki/src/pki"
@ -27,18 +28,25 @@ func RunServer(cfg *config.Config) (err error) {
e.HideBanner = cfg.Options.HideBanner e.HideBanner = cfg.Options.HideBanner
e.GET("/", func(c echo.Context) error { e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Welcome to PKI software") return c.String(http.StatusOK, "Welcome to PKI software (https://git.paulbsd.com/paulbsd/pki)")
}) })
e.GET("/domain/:domain", func(c echo.Context) (err error) { e.GET("/domain/:domains", func(c echo.Context) (err error) {
var result EntryResponse var result EntryResponse
log.Println(fmt.Sprintf("Providing %s to user %s at %s", c.Param("domain"), c.Get("username"), c.RealIP())) var domains = strings.Split(c.Param("domains"), ",")
result, err = GetCertificate(cfg, c.Get("user").(*pki.User), c.Param("domain"))
log.Println(fmt.Sprintf("Providing %s to user %s at %s", domains, c.Get("username"), c.RealIP()))
result, err = GetCertificate(cfg, c.Get("user").(*pki.User), domains)
if err != nil { if err != nil {
return c.String(http.StatusInternalServerError, fmt.Sprintf("%s %s", result, err)) return c.String(http.StatusInternalServerError, fmt.Sprintf("%s", err))
} }
return c.JSON(http.StatusOK, result) return c.JSON(http.StatusOK, result)
}) })
e.Logger.Fatal(e.Start(fmt.Sprintf(":%d", cfg.Switchs.Port))) e.Logger.Fatal(
e.Start(
fmt.Sprintf(":%d",
cfg.Switchs.Port)))
return return
} }

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"log" "log"
"regexp" "regexp"
"strings"
"time" "time"
"git.paulbsd.com/paulbsd/pki/src/cert" "git.paulbsd.com/paulbsd/pki/src/cert"
@ -14,15 +15,15 @@ import (
) )
// GetCertificate get certificate from database if exists, of request it from ACME // GetCertificate get certificate from database if exists, of request it from ACME
func GetCertificate(cfg *config.Config, user *pki.User, domain string) (result EntryResponse, err error) { func GetCertificate(cfg *config.Config, user *pki.User, domains []string) (result EntryResponse, err error) {
err = CheckDomain(domain) err = CheckDomains(domains)
if err != nil { if err != nil {
return result, err return result, err
} }
entry, err := user.GetEntry(cfg, domain) entry, err := user.GetEntry(cfg, domains)
if err != nil { if err != nil {
certs, err := user.RequestNewCert(cfg, domain) certs, err := user.RequestNewCert(cfg, domains)
if err != nil { if err != nil {
log.Println(fmt.Sprintf("Error fetching new certificate %s", err)) log.Println(fmt.Sprintf("Error fetching new certificate %s", err))
return result, err return result, err
@ -32,7 +33,7 @@ func GetCertificate(cfg *config.Config, user *pki.User, domain string) (result E
log.Println("Error where parsing dates") log.Println("Error where parsing dates")
return result, err return result, err
} }
entry := cert.Entry{Domain: domain, entry := cert.Entry{Domains: strings.Join(domains, ","),
Certificate: string(certs.Certificate), Certificate: string(certs.Certificate),
PrivateKey: string(certs.PrivateKey), PrivateKey: string(certs.PrivateKey),
ValidityBegin: NotBefore, ValidityBegin: NotBefore,
@ -46,11 +47,14 @@ func GetCertificate(cfg *config.Config, user *pki.User, domain string) (result E
return return
} }
// CheckDomain check if requested domain is valid // CheckDomains check if requested domains are valid
func CheckDomain(domain string) (err error) { func CheckDomains(domains []string) (err error) {
res, err := regexp.Match(`^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$`, []byte(domain)) for _, d := range domains {
if !res { res, err := regexp.Match(`^[a-z0-9\*]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$`, []byte(d))
return fmt.Errorf("Domain has not a valid syntax") if !res {
fmt.Println(res, err)
return fmt.Errorf(fmt.Sprintf("Domain has not a valid syntax %s, please verify", err))
}
} }
return return
} }
@ -71,20 +75,22 @@ func GetDates(cert []byte) (NotBefore time.Time, NotAfter time.Time, err error)
// convertEntryToResponse converts database ACME entry to JSON ACME entry // convertEntryToResponse converts database ACME entry to JSON ACME entry
func convertEntryToResponse(in cert.Entry) (out EntryResponse) { func convertEntryToResponse(in cert.Entry) (out EntryResponse) {
out.Domain = in.Domain timeformatstring := "2006-01-02 15:04:05"
out.Domains = in.Domains
out.Certificate = in.Certificate out.Certificate = in.Certificate
out.PrivateKey = in.PrivateKey out.PrivateKey = in.PrivateKey
out.ValidityBegin = in.ValidityBegin out.ValidityBegin = in.ValidityBegin.Format(timeformatstring)
out.ValidityEnd = in.ValidityEnd out.ValidityEnd = in.ValidityEnd.Format(timeformatstring)
return return
} }
// EntryResponse is the struct defining JSON response from webservice // EntryResponse is the struct defining JSON response from webservice
type EntryResponse struct { type EntryResponse struct {
Domain string `json:"domain"` Domains string `json:"domains"`
Certificate string `json:"certificate"` Certificate string `json:"certificate"`
PrivateKey string `json:"privatekey"` PrivateKey string `json:"privatekey"`
ValidityBegin time.Time `json:"validitybegin"` ValidityBegin string `json:"validitybegin"`
ValidityEnd time.Time `json:"validityend"` ValidityEnd string `json:"validityend"`
} }

View File

@ -13,6 +13,7 @@ import (
// Auth make authentication to webservice // Auth make authentication to webservice
func Auth(cfg *config.Config, username string, password string, c echo.Context) (res bool, user *pki.User, err error) { func Auth(cfg *config.Config, username string, password string, c echo.Context) (res bool, user *pki.User, err error) {
user = &pki.User{Username: username} user = &pki.User{Username: username}
_, err = cfg.Db.Get(user) _, err = cfg.Db.Get(user)
if err != nil { if err != nil {
res = false res = false