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
type Entry struct {
ID int `xorm:"pk autoincr"`
Domain string `xorm:"notnull"`
Domains string `xorm:"notnull"`
Certificate string `xorm:"text notnull"`
PrivateKey string `xorm:"text notnull"`
AuthURL string

View File

@ -9,6 +9,7 @@ import (
"encoding/pem"
"fmt"
"log"
"strings"
"time"
"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
func (u *User) GetEntry(cfg *config.Config, domain string) (Entry cert.Entry, err error) {
todaydate := time.Now().Format("2006-01-02")
func (u *User) GetEntry(cfg *config.Config, domains []string) (Entry cert.Entry, err error) {
requireddate := time.Now().AddDate(0, 0, -cfg.ACME.MaxDaysBefore).Format("2006-01-02")
has, err := cfg.Db.Where("domain = ?", domain).Where(
"validity_begin < ?::date", todaydate).Where(
"validity_end > ?::date", requireddate).Where(
"auth_url = ?", cfg.ACME.AuthURL).Get(&Entry)
has, err := cfg.Db.Where("domains = ?", strings.Join(domains, ",")).Where(
"validity_begin <= now()").Where(
"validity_end >= ?::timestamp", requireddate).Where(
"auth_url = ?", cfg.ACME.AuthURL).Desc(
"id").Get(&Entry)
if !has {
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
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.CADirURL = cfg.ACME.AuthURL
legoconfig.Certificate.KeyType = certcrypto.RSA2048
@ -93,7 +95,7 @@ func (u *User) RequestNewCert(cfg *config.Config, domain string) (certificates *
}
request := certificate.ObtainRequest{
Domains: []string{domain},
Domains: domains,
Bundle: true,
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"log"
"net/http"
"strings"
"git.paulbsd.com/paulbsd/pki/src/config"
"git.paulbsd.com/paulbsd/pki/src/pki"
@ -27,18 +28,25 @@ func RunServer(cfg *config.Config) (err error) {
e.HideBanner = cfg.Options.HideBanner
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
log.Println(fmt.Sprintf("Providing %s to user %s at %s", c.Param("domain"), c.Get("username"), c.RealIP()))
result, err = GetCertificate(cfg, c.Get("user").(*pki.User), c.Param("domain"))
var domains = strings.Split(c.Param("domains"), ",")
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 {
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)
})
e.Logger.Fatal(e.Start(fmt.Sprintf(":%d", cfg.Switchs.Port)))
e.Logger.Fatal(
e.Start(
fmt.Sprintf(":%d",
cfg.Switchs.Port)))
return
}

View File

@ -6,6 +6,7 @@ import (
"fmt"
"log"
"regexp"
"strings"
"time"
"git.paulbsd.com/paulbsd/pki/src/cert"
@ -14,15 +15,15 @@ import (
)
// 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) {
err = CheckDomain(domain)
func GetCertificate(cfg *config.Config, user *pki.User, domains []string) (result EntryResponse, err error) {
err = CheckDomains(domains)
if err != nil {
return result, err
}
entry, err := user.GetEntry(cfg, domain)
entry, err := user.GetEntry(cfg, domains)
if err != nil {
certs, err := user.RequestNewCert(cfg, domain)
certs, err := user.RequestNewCert(cfg, domains)
if err != nil {
log.Println(fmt.Sprintf("Error fetching new certificate %s", 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")
return result, err
}
entry := cert.Entry{Domain: domain,
entry := cert.Entry{Domains: strings.Join(domains, ","),
Certificate: string(certs.Certificate),
PrivateKey: string(certs.PrivateKey),
ValidityBegin: NotBefore,
@ -46,11 +47,14 @@ func GetCertificate(cfg *config.Config, user *pki.User, domain string) (result E
return
}
// CheckDomain check if requested domain is valid
func CheckDomain(domain string) (err error) {
res, err := regexp.Match(`^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$`, []byte(domain))
if !res {
return fmt.Errorf("Domain has not a valid syntax")
// CheckDomains check if requested domains are valid
func CheckDomains(domains []string) (err error) {
for _, d := range domains {
res, err := regexp.Match(`^[a-z0-9\*]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$`, []byte(d))
if !res {
fmt.Println(res, err)
return fmt.Errorf(fmt.Sprintf("Domain has not a valid syntax %s, please verify", err))
}
}
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
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.PrivateKey = in.PrivateKey
out.ValidityBegin = in.ValidityBegin
out.ValidityEnd = in.ValidityEnd
out.ValidityBegin = in.ValidityBegin.Format(timeformatstring)
out.ValidityEnd = in.ValidityEnd.Format(timeformatstring)
return
}
// EntryResponse is the struct defining JSON response from webservice
type EntryResponse struct {
Domain string `json:"domain"`
Certificate string `json:"certificate"`
PrivateKey string `json:"privatekey"`
ValidityBegin time.Time `json:"validitybegin"`
ValidityEnd time.Time `json:"validityend"`
Domains string `json:"domains"`
Certificate string `json:"certificate"`
PrivateKey string `json:"privatekey"`
ValidityBegin string `json:"validitybegin"`
ValidityEnd string `json:"validityend"`
}

View File

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