elego

package module
v0.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 26, 2025 License: Apache-2.0 Imports: 32 Imported by: 0

README

Simple ACME Certificate Library (Built on Lego)

A lightweight Go library for automated SSL/TLS certificate management, built as a simplified wrapper around lego. Designed to streamline ACME operations like certificate issuance, renewal, and deployment for Let’s Encrypt and other ACME-compatible CAs.

Installation

go get github.com/pkg6/elego
Install Command-Line Tool (Optional)

You can install the standalone CLI tool for convenience:

# Official GitHub source
curl -sSL https://raw.githubusercontent.com/pkg6/elego/main/install.sh | bash

# Domestic CDN mirror for faster access in China
# https://cnb.cool/zhiqiangwang/tlsctl/-/releases
curl -sSL https://cnb.cool/zhiqiangwang/tlsctl/-/git/raw/main/install.sh | bash

Design Philosophy

  • Simplified API: Abstracts complex ACME interactions into easy-to-use functions.
  • Flexible Challenge Support: Supports HTTP-01, DNS-01, and other ACME challenge methods.
  • Customizable Deployment: Allows hooking custom deployment commands (e.g., reload Nginx).
  • Embed-Friendly: Meant to be embedded in Go projects or automation pipelines.

Core Features

  • Certificate Issuance Automatically registers an account and obtains certificates for specified domains.
  • Automatic Renewal Provides mechanisms to check certificate validity and renew before expiration.
  • Certificate Deployment Supports running post-issuance commands for deployment automation.

Basic Usage Example

package main

import (
	"flag"
	"fmt"
	"github.com/pkg6/elego"
	"github.com/pkg6/elego/deploy"
	"github.com/pkg6/elego/pkg/log"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
)

type Flags struct {
	Email     string
	Domain    string
	CADirURL  string
	CachePath string

	InFewDayReset float64

	HttpWebroot elego.HTTPWebrootChallenge
	DNS         elego.DNSChallenge

	Deploy string
	Path   string
}

var (
	args         Flags
	homePath     string
	cachePath    string
	defaultEmail string
)

func init() {
	homePath, _ = os.UserHomeDir()
	cachePath = path.Join(homePath, ".tlsctl")
	defaultEmail = "elego@" + runtime.GOOS + ".com"

	flag.StringVar(&args.CADirURL, "cadirurl", elego.CADirURLLetsencrypt, "Which supplier will we issue the certificate from")

	flag.StringVar(&args.Email, "email", defaultEmail, "email")
	flag.StringVar(&args.Domain, "domain", "", "Need to generate SSL domain names")

	flag.StringVar(&args.CachePath, "cache", cachePath, "When creating, a cache file will be generated and the path needs to be saved")

	flag.Float64Var(&args.InFewDayReset, "in-few-day", 0, "Recharge will be done within a few days")

	flag.StringVar(&args.HttpWebroot.WebRoot, "webroot", "", "Directory for domain deployment")

	flag.StringVar(&args.DNS.DNS, "dns", "", "Enter your DNS name, please refer to https://go-acme.github.io/lego/dns/index.html")

	flag.StringVar(&args.Deploy, "deploy", "local", "Where do you want to deploy your certificate")

	flag.StringVar(&args.Path, "path", "/etc/nginx/ssl/", "Path for saving certificates")
}
func main() {
	flag.Parse()
	if args.Path == "" {
		log.Fatal("Need to set --path")
	}
	if args.DNS.DNS == "" && args.HttpWebroot.WebRoot == "" {
		log.Fatal("Need to set either --dns or --webroot")
	}
	log.Printf("Accepted data : %#v", args)
	if strings.HasPrefix(args.Domain, "*") {
		//https://github.com/go-acme/lego/issues/1867
		_ = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", strconv.FormatBool(true))
	}
	sanitizedDomain, err := elego.SanitizedDomain(args.Domain)
	if err != nil {
		log.Fatal(fmt.Errorf("sanitizing domain: %v", err))
	}
	cStorage, err := elego.NewCertificatesStorage(args.CachePath, "RC2")
	if err != nil {
		log.Fatal(fmt.Errorf("error creating certificates storage: %v", err))
	}
	//
	//A reset will take place in a few days
	//We can create a scheduled task. When in-few-day=1, it means that the certificate will be regenerated when it expires in less than or equal to 1 day.
	var forceReset bool
	day, err := cStorage.CheckExpire(args.Domain)
	if err != nil {
		log.Printf("failed to check expire for %s: %v", args.Domain, err)
		forceReset = true
	} else if day <= args.InFewDayReset {
		log.Printf("%s is not expiring soon (%.0f days left), forcing reset", args.Domain, day)
		forceReset = true
	}
	if day > 0 && forceReset == false {
		log.Printf("%s will expire in %.0f days", args.Domain, day)
		savePath, _ := cStorage.GetSavePath(args.Domain)
		log.Printf("we will back up the data in the %s directory", savePath)
		return
	}
	aStorage, err := elego.NewAccountsStorage(args.CachePath, args.Email, args.CADirURL)
	if err != nil {
		log.Fatal(fmt.Errorf("error creating accounts storage: %v", err))
	}
	var register elego.IRegister
	kid := os.Getenv("ELEGO_REGISTER_KID")
	hmacEncoded := os.Getenv("ELEGO_REGISTER_HMAC")
	if kid != "" && hmacEncoded != "" {
		register = &elego.EABRegister{
			TermsOfServiceAgreed: true,
			Kid:                  kid,
			HmacEncoded:          hmacEncoded,
		}
	} else {
		register = &elego.Register{}
	}
	_, client, err := elego.NewLegoClient(aStorage, register)
	if err != nil {
		log.Fatal(fmt.Errorf("error creating lego client: %w", err))
	}
	if args.DNS.DNS != "" {
		err = elego.SetChallenge(client, &args.DNS)
	} else if args.HttpWebroot.WebRoot != "" {
		err = elego.SetChallenge(client, &args.HttpWebroot)
	} else {
		err = fmt.Errorf("we are working hard to support other ways")
	}
	if err != nil {
		log.Fatal(fmt.Errorf("elego SetChallenge: %w", err))
	}
	certificate, err := elego.ObtainCertificate(client, []string{args.Domain})
	if err != nil {
		log.Fatal(fmt.Errorf("error obtaining certificate: %w", err))
	}
	if err := cStorage.SaveResource(certificate); err != nil {
		log.Fatal(fmt.Errorf("error saving certificate: %w", err))
	}
	resource, err := cStorage.ReadResource(args.Domain)
	if err != nil {
		log.Fatal(err)
	}
	switch args.Deploy {
	case "local":
		keyPath := path.Join(args.Path, fmt.Sprintf("%s%s", sanitizedDomain, elego.PemExt))
		_ = os.Setenv("LOCAL_CERT_PATH", keyPath)
		cerPath := path.Join(args.Path, fmt.Sprintf("%s%s", sanitizedDomain, elego.KeyExt))
		_ = os.Setenv("LOCAL_KEY_PATH", cerPath)
		break
	}
	if err := deploy.Run(args.Deploy, resource); err != nil {
		log.Fatal(fmt.Errorf("error deploying certificate: %w", err))
	}
	log.Printf("You successfully deployed your number through %s deployment method", args.Deploy)
}

Advanced Configuration

  • Supports DNS providers via environment variables for DNS-01 challenge.
  • External Account Binding (EAB) support.
  • Custom certificate and key storage locations.

Suitable For

  • Automated HTTPS provisioning for self-hosted services.
  • Integration in Go applications requiring on-demand certificate management.
  • CI/CD pipelines automating SSL certificate issuance and deployment.

References

Documentation

Index

Constants

View Source
const (
	IssuerExt   = ".issuer.crt"
	CertExt     = ".crt"
	KeyExt      = ".key"
	PemExt      = ".pem"
	PfxExt      = ".pfx"
	ResourceExt = ".json"
)
View Source
const (
	CADirURLLetsencrypt        = "letsencrypt"
	CADirURLLetsencryptStaging = "letsencrypt_staging"
	CADirURLZerossl            = "zerossl"
	CADirURLGts                = "gts"
)

Variables

Functions

func GetCADirURL added in v0.1.3

func GetCADirURL(name string) string

func ObtainCertificate

func ObtainCertificate(client *lego.Client, domains []string) (*certificate.Resource, error)

func ParsePEMBundle added in v0.2.0

func ParsePEMBundle(content []byte, inputDomain string) (*x509.Certificate, error)

func SanitizedDomain added in v0.1.3

func SanitizedDomain(domain string) (string, error)

func SetChallenge

func SetChallenge(client *lego.Client, challenge IChallenge) error

Types

type Account

type Account struct {
	Email        string                 `json:"email"`
	Registration *registration.Resource `json:"registration"`
	Key          crypto.PrivateKey      `json:"-"`
}

Account represents a users local saved credentials.

func NewLegoClient

func NewLegoClient(accountStorage *AccountsStorage, register IRegister) (account *Account, client *lego.Client, err error)

func (*Account) GetEmail

func (a *Account) GetEmail() string

GetEmail returns the email address for the account.

func (*Account) GetPrivateKey

func (a *Account) GetPrivateKey() crypto.PrivateKey

GetPrivateKey returns the private RSA account key.

func (*Account) GetRegistration

func (a *Account) GetRegistration() *registration.Resource

GetRegistration returns the server registration.

type AccountsStorage

type AccountsStorage struct {
	Email    string
	CADirURL string
	// contains filtered or unexported fields
}

func NewAccountsStorage

func NewAccountsStorage(savePath, email, CADirURLOrName string) (*AccountsStorage, error)

NewAccountsStorage Creates a new AccountsStorage.

func (*AccountsStorage) GetCADirURL added in v0.1.3

func (s *AccountsStorage) GetCADirURL() string

func (*AccountsStorage) GetEmail

func (s *AccountsStorage) GetEmail() string

func (*AccountsStorage) LoadAccount

func (s *AccountsStorage) LoadAccount() (*Account, error)

func (*AccountsStorage) Remove

func (s *AccountsStorage) Remove()

func (*AccountsStorage) Save

func (s *AccountsStorage) Save(account *Account) error

type CertificatesStorage added in v0.1.3

type CertificatesStorage struct {
	// contains filtered or unexported fields
}

func NewCertificatesStorage added in v0.1.3

func NewCertificatesStorage(savePath, pfxFormat string) (s *CertificatesStorage, err error)

NewCertificatesStorage pfxFormat RC2

func (*CertificatesStorage) CheckExpire added in v0.1.3

func (s *CertificatesStorage) CheckExpire(inputDomain string) (day float64, err error)

func (*CertificatesStorage) GetAllResource added in v0.2.0

func (s *CertificatesStorage) GetAllResource() ([]*certificate.Resource, error)

func (*CertificatesStorage) GetSanitizedDomainSavePath added in v0.2.2

func (s *CertificatesStorage) GetSanitizedDomainSavePath(sanitizedDomain string) string

func (*CertificatesStorage) GetSavePath added in v0.1.4

func (s *CertificatesStorage) GetSavePath(domain string) (string, error)

func (*CertificatesStorage) ParseResourceFindCertificate added in v0.2.2

func (s *CertificatesStorage) ParseResourceFindCertificate(resource *certificate.Resource) (*x509.Certificate, error)

func (*CertificatesStorage) ReadCertificate added in v0.1.3

func (s *CertificatesStorage) ReadCertificate(domain string) ([]*x509.Certificate, error)

func (*CertificatesStorage) ReadResource added in v0.1.3

func (s *CertificatesStorage) ReadResource(domain string) (*certificate.Resource, error)

func (*CertificatesStorage) SaveResource added in v0.1.3

func (s *CertificatesStorage) SaveResource(certRes *certificate.Resource) error

type DNSChallenge

type DNSChallenge struct {
	DNS                         string
	Servers                     []string
	PropagationWait             int
	Timeout                     int
	AuthoritativeNssPropagation bool
	RecursiveNssPropagation     bool
}

func (*DNSChallenge) Set

func (w *DNSChallenge) Set(client *lego.Client) error

type EABRegister

type EABRegister struct {
	TermsOfServiceAgreed bool
	Kid                  string
	HmacEncoded          string
}

func (*EABRegister) Register

func (r *EABRegister) Register(lego *lego.Client) (*registration.Resource, error)

type HTTPChallenge

type HTTPChallenge struct {
	HeaderName string
}

func (*HTTPChallenge) Set

func (w *HTTPChallenge) Set(client *lego.Client) error

type HTTPMemcachedHostChallenge

type HTTPMemcachedHostChallenge struct {
	Hosts []string
}

func (*HTTPMemcachedHostChallenge) Set

func (w *HTTPMemcachedHostChallenge) Set(client *lego.Client) error

type HTTPPortChallenge

type HTTPPortChallenge struct {
	HostPort   string
	HeaderName string
}

func (*HTTPPortChallenge) Set

func (w *HTTPPortChallenge) Set(client *lego.Client) error

type HTTPS3BucketChallenge

type HTTPS3BucketChallenge struct {
	Bucket string
}

func (*HTTPS3BucketChallenge) Set

func (w *HTTPS3BucketChallenge) Set(client *lego.Client) error

type HTTPWebrootChallenge

type HTTPWebrootChallenge struct {
	WebRoot string
}

func (*HTTPWebrootChallenge) Set

func (w *HTTPWebrootChallenge) Set(client *lego.Client) error

type IChallenge

type IChallenge interface {
	Set(client *lego.Client) error
}

type IRegister

type IRegister interface {
	Register(lego *lego.Client) (*registration.Resource, error)
}

type Register

type Register struct {
}

func (*Register) Register

func (r *Register) Register(lego *lego.Client) (*registration.Resource, error)

type TLSChallenge

type TLSChallenge struct {
}

func (*TLSChallenge) Set

func (w *TLSChallenge) Set(client *lego.Client) error

type TLSPortChallenge

type TLSPortChallenge struct {
	HostPort string
}

func (*TLSPortChallenge) Set

func (w *TLSPortChallenge) Set(client *lego.Client) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL