Support for claiming existing certificate + Store certificate in Volume

* Adds a new required "certificate name" parameter (backward
compatible): When there is an existing certificate by that name it will
be overwritten with a renewed certificate
* fullchain.pem and privatekey.pem are now stored in a Volume under /etc/letsencrypt/production/certificates/. This is until we find a better way to share the private key with other services.
This commit is contained in:
Jan Broer
2016-03-31 03:47:33 +02:00
parent 46f12b3f90
commit 2a91bcf147
12 changed files with 252 additions and 164 deletions
+2 -1
View File
@@ -3,9 +3,10 @@ MAINTAINER Jan Broer <jan@festplatte.eu.org>
RUN apk add --no-cache ca-certificates
ENV LETSENCRYPT_RELEASE v0.2.5
ENV LETSENCRYPT_RELEASE v0.2.6
ADD https://github.com/janeczku/rancher-letsencrypt/releases/download/${LETSENCRYPT_RELEASE}/rancher-letsencrypt-linux-amd64.tar.gz /tmp/rancher-letsencrypt.tar.gz
RUN tar -zxvf /tmp/rancher-letsencrypt.tar.gz -C /usr/bin \
&& chmod +x /usr/bin/rancher-letsencrypt
+3
View File
@@ -4,8 +4,11 @@ MAINTAINER Jan Broer <jan@festplatte.eu.org>
RUN apk add --no-cache ca-certificates
ADD build/rancher-letsencrypt-linux-amd64 /usr/bin/rancher-letsencrypt
RUN chmod +x /usr/bin/rancher-letsencrypt
VOLUME /etc/letsencrypt
ENV DEBUG=true
ENTRYPOINT ["/usr/bin/rancher-letsencrypt"]
+1 -1
View File
@@ -52,7 +52,7 @@ dockerhub: image
docker push $(DOCKER_IMAGE):$(VERSION)
image:
docker build -t $(DOCKER_IMAGE):$(VERSION) -f Dockerfile.dev .
docker build -t $(DOCKER_IMAGE):dev-$(SHA) -f Dockerfile.dev .
version:
@echo $(VERSION) $(SHA)
+1 -1
View File
@@ -1 +1 @@
v0.2.5
v0.2.6
+19 -11
View File
@@ -12,7 +12,7 @@ import (
)
const (
DESCRIPTION = "Created by Let's Encrypt Certificate Manager"
CERT_DESCRIPTION = "Created by Let's Encrypt Certificate Manager"
ISSUER_PRODUCTION = "Let's Encrypt"
ISSUER_STAGING = "fake CA"
PREFIX_PROD = "[LE] "
@@ -38,12 +38,13 @@ func (c *Context) InitContext() {
cattleUrl := getEnvOption("CATTLE_URL", true)
cattleApiKey := getEnvOption("CATTLE_ACCESS_KEY", true)
cattleSecretKey := getEnvOption("CATTLE_SECRET_KEY", true)
debug := getEnvOption("DEBUG", false)
debugParam := getEnvOption("DEBUG", false)
eulaParam := getEnvOption("EULA", false)
apiVerParam := getEnvOption("API_VERSION", true)
emailParam := getEnvOption("EMAIL", true)
domainParam := getEnvOption("DOMAINS", true)
keyTypeParam := getEnvOption("PUBLIC_KEY_TYPE", true)
certNameParam := getEnvOption("CERT_NAME", false)
timeParam := getEnvOption("RENEWAL_TIME", true)
providerParam := getEnvOption("PROVIDER", true)
@@ -63,18 +64,26 @@ func (c *Context) InitContext() {
logrus.Fatalf("Invalid value for RENEWAL_TIME: %s", timeParam)
}
var serverURI string
var certAutoName string
switch apiVerParam {
case "Production":
serverURI = letsencrypt.PRODUCTION_URI
c.RancherCertName = PREFIX_PROD + c.Domains[0]
certAutoName = PREFIX_PROD + c.Domains[0]
case "Sandbox":
serverURI = letsencrypt.STAGING_URI
c.RancherCertName = PREFIX_STAGING + c.Domains[0]
certAutoName = PREFIX_STAGING + c.Domains[0]
default:
logrus.Fatalf("Invalid value for API_VERSION: %s", apiVerParam)
}
apiVersion := letsencrypt.ApiVersion(apiVerParam)
keyType := letsencrypt.KeyType(keyTypeParam)
if len(certNameParam) != 0 {
c.RancherCertName = certNameParam
} else {
c.RancherCertName = certAutoName
}
c.Rancher, err = rancher.NewClient(cattleUrl, cattleApiKey, cattleSecretKey)
if err != nil {
logrus.Fatalf("Rancher client: %v", err)
@@ -92,19 +101,18 @@ func (c *Context) InitContext() {
DynCustomerName: os.Getenv("DYN_CUSTOMER_NAME"),
DynUserName: os.Getenv("DYN_USER_NAME"),
DynPassword: os.Getenv("DYN_PASSWORD"),
AwsRegionName: "us-east-1",
}
c.Acme, err = letsencrypt.NewClient(emailParam, keyTypeParam, serverURI, providerOpts)
c.Acme, err = letsencrypt.NewClient(emailParam, keyType, apiVersion, providerOpts)
if err != nil {
logrus.Fatalf("LetsEncrypt client: %v", err)
}
// Enable debug/test mode
if strings.EqualFold(debug, "true") {
if strings.EqualFold(debugParam, "true") {
logrus.SetLevel(logrus.DebugLevel)
c.Debug = true
c.Acme.EnableDebugLogging()
c.Acme.EnableDebug()
}
}
+18 -7
View File
@@ -7,21 +7,25 @@ import (
"io/ioutil"
"os"
"path"
"strings"
"github.com/Sirupsen/logrus"
lego "github.com/xenolf/lego/acme"
)
type Account struct {
Email string `json:"email"`
key crypto.PrivateKey
Email string `json:"email"`
Registration *lego.RegistrationResource `json:"registrations"`
key crypto.PrivateKey
path string
}
// NewAccount creates a new or gets a stored LE account for the given email
func NewAccount(email string, keyType lego.KeyType) (*Account, error) {
keyFile := path.Join(configPath(), email+".key")
accountFile := path.Join(configPath(), email+".json")
func NewAccount(email string, apiVer ApiVersion, keyType lego.KeyType) (*Account, error) {
accPath := accountPath(email, apiVer)
keyFile := path.Join(accPath, "account.key")
accountFile := path.Join(accPath, "account.json")
var privKey crypto.PrivateKey
if _, err := os.Stat(keyFile); os.IsNotExist(err) {
@@ -39,7 +43,7 @@ func NewAccount(email string, keyType lego.KeyType) (*Account, error) {
}
if _, err := os.Stat(accountFile); os.IsNotExist(err) {
return &Account{Email: email, key: privKey}, nil
return &Account{Email: email, key: privKey, path: accPath}, nil
}
fileBytes, err := ioutil.ReadFile(accountFile)
@@ -54,6 +58,7 @@ func NewAccount(email string, keyType lego.KeyType) (*Account, error) {
}
acc.key = privKey
acc.path = accPath
return &acc, nil
}
@@ -63,7 +68,7 @@ func (a *Account) Save() error {
if err != nil {
return err
}
accountFile := path.Join(configPath(), a.Email+".json")
accountFile := path.Join(a.path, "account.json")
return ioutil.WriteFile(accountFile, jsonBytes, 0700)
}
@@ -83,3 +88,9 @@ func (a *Account) GetPrivateKey() crypto.PrivateKey {
func (a *Account) GetRegistration() *lego.RegistrationResource {
return a.Registration
}
func accountPath(email string, apiVer ApiVersion) string {
path := path.Join(ConfigDir, strings.ToLower(string(apiVer)), "accounts", email)
maybeCreatePath(path)
return path
}
+138 -82
View File
@@ -15,54 +15,81 @@ import (
)
const (
PRODUCTION_URI = "https://acme-v01.api.letsencrypt.org/directory"
STAGING_URI = "https://acme-staging.api.letsencrypt.org/directory"
DATA_DIR = "/etc/letsencrypt"
ConfigDir = "/etc/letsencrypt"
ProductionApiUri = "https://acme-v01.api.letsencrypt.org/directory"
StagingApiUri = "https://acme-staging.api.letsencrypt.org/directory"
)
type KeyType string
const (
RSA2048 KeyType = "RSA-2048"
RSA4096 KeyType = "RSA-4096"
RSA8192 KeyType = "RSA-8192"
EC256 KeyType = "ECDSA-256"
EC384 KeyType = "ECDSA-384"
)
type ApiVersion string
const (
Production ApiVersion = "Production"
Sandbox ApiVersion = "Sandbox"
)
// AcmeCertificate represents a CA issued certificate,
// PrivateKey and Certificate are both PEM encoded.
//
// Anonymous fields:
// PrivateKey []byte
// Certificate []byte
// Domain string
type AcmeCertificate struct {
PrivateKey []byte `json:"privateKey"`
Certificate []byte `json:"certificate"`
lego.CertificateResource
ExpiryDate time.Time `json:"expiryDate"`
SerialNumber string `json:"serialnumber"`
}
type acmeCertificateStore struct {
CertRes lego.CertificateResource `json:"certRes"`
AcmeCert AcmeCertificate `json:"acmeCert"`
}
// Client represents a Lets Encrypt client
type Client struct {
client *lego.Client
client *lego.Client
apiVersion ApiVersion
}
// NewClient returns a new Lets Encrypt client
func NewClient(email, keyTypeStr, uri string, provider ProviderOpts) (*Client, error) {
func NewClient(email string, kt KeyType, apiVer ApiVersion, provider ProviderOpts) (*Client, error) {
var keyType lego.KeyType
switch strings.ToUpper(keyTypeStr) {
case "RSA-2048":
switch kt {
case RSA2048:
keyType = lego.RSA2048
case "RSA-4096":
case RSA4096:
keyType = lego.RSA4096
case "RSA-8192":
case RSA8192:
keyType = lego.RSA8192
case "ECDSA-256":
case EC256:
keyType = lego.EC256
case "ECDSA-384":
case EC384:
keyType = lego.EC384
default:
return nil, fmt.Errorf("Invalid private key type: %s", keyTypeStr)
return nil, fmt.Errorf("Invalid private key type: %s", string(kt))
}
acc, err := NewAccount(email, keyType)
var serverUri string
switch apiVer {
case Production:
serverUri = ProductionApiUri
case Sandbox:
serverUri = StagingApiUri
default:
return nil, fmt.Errorf("Invalid LE API version: %s", string(apiVer))
}
acc, err := NewAccount(email, apiVer, keyType)
if err != nil {
return nil, fmt.Errorf("Could not initialize account store for %s: %v", email, err)
}
client, err := lego.NewClient(uri, acc, keyType)
client, err := lego.NewClient(serverUri, acc, keyType)
if err != nil {
return nil, fmt.Errorf("Could not create client: %v", err)
}
@@ -105,10 +132,16 @@ func NewClient(email, keyTypeStr, uri string, provider ProviderOpts) (*Client, e
client.ExcludeChallenges([]lego.Challenge{lego.HTTP01, lego.TLSSNI01})
return &Client{
client: client,
client: client,
apiVersion: apiVer,
}, nil
}
// EnableDebugLogging enables logging in the upstream lego library
func (c *Client) EnableDebug() {
lego.Logger = log.New(os.Stdout, "", 0)
}
// Issue obtains a new SAN certificate from the Lets Encrypt CA
func (c *Client) Issue(domains []string) (*AcmeCertificate, map[string]error) {
cert, failures := c.client.ObtainCertificate(domains, true, nil)
@@ -117,30 +150,26 @@ func (c *Client) Issue(domains []string) (*AcmeCertificate, map[string]error) {
}
expirydate, _ := lego.GetPEMCertExpiration(cert.Certificate)
serial, _ := getPEMCertSerialNo(cert.Certificate)
serialnum, _ := getPEMCertSerialNo(cert.Certificate)
acmeCert := AcmeCertificate{
cert.PrivateKey,
cert.Certificate,
expirydate,
serial,
CertificateResource: cert,
ExpiryDate: expirydate,
SerialNumber: serialnum,
}
saveCertStore(cert, acmeCert)
c.saveCertificate(acmeCert)
return &acmeCert, nil
}
// Renew obtains a renewed SAN certificate from the Lets Encrypt CA
func (c *Client) Renew(domains []string) (*AcmeCertificate, error) {
domain := domains[0]
certStore, err := loadCertStore(domain)
currentCert, err := c.loadCertificate(domain)
if err != nil {
return nil, err
logrus.Fatalf(err.Error())
}
certRes := certStore.CertRes
certRes.PrivateKey = certStore.AcmeCert.PrivateKey
certRes.Certificate = certStore.AcmeCert.Certificate
certRes := currentCert.CertificateResource
newCert, err := c.client.RenewCertificate(certRes, true)
if err != nil {
@@ -148,98 +177,125 @@ func (c *Client) Renew(domains []string) (*AcmeCertificate, error) {
}
expirydate, _ := lego.GetPEMCertExpiration(newCert.Certificate)
serial, _ := getPEMCertSerialNo(newCert.Certificate)
serialnum, _ := getPEMCertSerialNo(newCert.Certificate)
acmeCert := AcmeCertificate{
newCert.PrivateKey,
newCert.Certificate,
expirydate,
serial,
CertificateResource: newCert,
ExpiryDate: expirydate,
SerialNumber: serialnum,
}
saveCertStore(newCert, acmeCert)
c.saveCertificate(acmeCert)
return &acmeCert, nil
}
// GetStoredCert returns a locally stored certificate for the given domains
func (c *Client) GetStoredCert(domains []string) (bool, *AcmeCertificate) {
// GetStoredCertificate returns a locally stored certificate for the given domains
func (c *Client) GetStoredCertificate(domains []string) (bool, *AcmeCertificate) {
domain := domains[0]
if !haveCertStore(domain) {
if !c.haveCertificate(domain) {
return false, nil
}
certStore, err := loadCertStore(domain)
acmeCert, err := c.loadCertificate(domain)
if err != nil {
logrus.Error(err)
// Don't fail here. Try to create a new certificate instead.
logrus.Error(err.Error())
return false, nil
}
return true, &certStore.AcmeCert
return true, &acmeCert
}
// EnableDebugLogging enables logging in the upstream lego library
func (c *Client) EnableDebugLogging() {
lego.Logger = log.New(os.Stdout, "", 0)
}
func haveCertStore(domain string) bool {
path := certStorePath(domain)
if _, err := os.Stat(path); os.IsNotExist(err) {
func (c *Client) haveCertificate(domain string) bool {
certPath := c.CertPath(domain)
if _, err := os.Stat(path.Join(certPath, "metadata.json")); err != nil {
logrus.Debugf("No existing acme certificate resource found for '%s'", domain)
return false
}
return true
}
func loadCertStore(domain string) (*acmeCertificateStore, error) {
file := certStorePath(domain)
certStoreBytes, err := ioutil.ReadFile(file)
func (c *Client) loadCertificate(domain string) (AcmeCertificate, error) {
var acmeCert AcmeCertificate
certPath := c.CertPath(domain)
logrus.Debugf("Loading acme certificate resource for '%s' from '%s'", domain, certPath)
certIn := path.Join(certPath, "fullchain.pem")
privIn := path.Join(certPath, "privkey.pem")
metaIn := path.Join(certPath, "metadata.json")
certBytes, err := ioutil.ReadFile(certIn)
if err != nil {
return nil, fmt.Errorf("Error loading certificate store for %s: %v", domain, err)
return acmeCert, fmt.Errorf("Failed to load certificate for domain '%s': %s", domain, err.Error())
}
var certStore acmeCertificateStore
err = json.Unmarshal(certStoreBytes, &certStore)
metaBytes, err := ioutil.ReadFile(metaIn)
if err != nil {
return nil, fmt.Errorf("Error parsing certificate store for %s: %v", domain, err)
return acmeCert, fmt.Errorf("Failed to load meta data for domain '%s': %s", domain, err.Error())
}
return &certStore, nil
keyBytes, err := ioutil.ReadFile(privIn)
if err != nil {
return acmeCert, fmt.Errorf("Failed to load private key for domain '%s': %s", domain, err.Error())
}
err = json.Unmarshal(metaBytes, &acmeCert)
if err != nil {
return acmeCert, fmt.Errorf("Failed to unmarshal meta data for domain '%s': %s", domain, err.Error())
}
acmeCert.PrivateKey = keyBytes
acmeCert.Certificate = certBytes
return acmeCert, nil
}
func saveCertStore(certRes lego.CertificateResource, acmeCert AcmeCertificate) {
out := certStorePath(certRes.Domain)
func (c *Client) saveCertificate(acmeCert AcmeCertificate) {
certPath := c.CertPath(acmeCert.Domain)
maybeCreatePath(certPath)
certStore := acmeCertificateStore{
CertRes: certRes,
AcmeCert: acmeCert,
logrus.Debugf("Saving acme certificate resource for '%s' to '%s'", acmeCert.Domain, certPath)
certOut := path.Join(certPath, "fullchain.pem")
privOut := path.Join(certPath, "privkey.pem")
metaOut := path.Join(certPath, "metadata.json")
err := ioutil.WriteFile(certOut, acmeCert.Certificate, 0600)
if err != nil {
logrus.Fatalf("Failed to save certificate for domain %s\n\t%s", acmeCert.Domain, err.Error())
}
jsonBytes, err := json.MarshalIndent(certStore, "", "\t")
err = ioutil.WriteFile(privOut, acmeCert.PrivateKey, 0600)
if err != nil {
logrus.Fatalf("Unable to marshal certificate store for domain %s: %s", certRes.Domain, err.Error())
logrus.Fatalf("Failed to save private key for domain %s\n\t%s", acmeCert.Domain, err.Error())
}
err = ioutil.WriteFile(out, jsonBytes, 0600)
jsonBytes, err := json.MarshalIndent(acmeCert, "", "\t")
if err != nil {
logrus.Fatalf("Unable to save certificate store for domain %s: %s", certRes.Domain, err.Error())
logrus.Fatalf("Failed to marshal meta data for domain %s\n\t%s", acmeCert.Domain, err.Error())
}
err = ioutil.WriteFile(metaOut, jsonBytes, 0600)
if err != nil {
logrus.Fatalf("Failed to save meta data for domain %s\n\t%s", acmeCert.Domain, err.Error())
}
}
func configPath() string {
path := path.Join(DATA_DIR, ".acme")
if err := checkFolder(path); err != nil {
logrus.Fatalf("Could not check/create config directory: %v", err)
}
func (c *Client) ConfigPath() string {
path := path.Join(ConfigDir, strings.ToLower(string(c.apiVersion)))
maybeCreatePath(path)
return path
}
func certStorePath(domain string) string {
path := path.Join(configPath(), domain+".json")
return path
func (c *Client) CertPath(domain string) string {
return path.Join(c.ConfigPath(), "certificates", domain)
}
func checkFolder(path string) error {
func maybeCreatePath(path string) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return os.MkdirAll(path, 0700)
err = os.MkdirAll(path, 0700)
if err != nil {
logrus.Fatalf("Error creating path: %v", err)
}
}
return nil
}
+3 -7
View File
@@ -25,9 +25,8 @@ type ProviderOpts struct {
DoAccessToken string
// AWS Route 53 credentials
AwsAccessKey string
AwsSecretKey string
AwsRegionName string
AwsAccessKey string
AwsSecretKey string
// DNSimple credentials
DNSimpleEmail string
@@ -105,11 +104,8 @@ func makeRoute53Provider(opts ProviderOpts) (lego.ChallengeProvider, error) {
if len(opts.AwsSecretKey) == 0 {
return nil, fmt.Errorf("AWS secret key is not set")
}
if len(opts.AwsRegionName) == 0 {
return nil, fmt.Errorf("AWS region name is not set")
}
os.Setenv("AWS_REGION", opts.AwsRegionName)
os.Setenv("AWS_REGION", "us-east-1")
os.Setenv("AWS_ACCESS_KEY_ID", opts.AwsAccessKey)
os.Setenv("AWS_SECRET_ACCESS_KEY", opts.AwsSecretKey)
+44 -36
View File
@@ -21,40 +21,38 @@ func (c *Context) Run() {
}
func (c *Context) startup() {
var haveLocal bool
var haveRemote bool
ok, acmeCert := c.Acme.GetStoredCert(c.Domains)
var haveLocal, haveRemote bool
ok, acmeCert := c.Acme.GetStoredCertificate(c.Domains)
if ok {
haveLocal = true
c.ExpiryDate = acmeCert.ExpiryDate
logrus.Info("Found local store for certificate")
logrus.Infof("Found locally stored certificate for '%s'", strings.Join(c.Domains, " | "))
}
rancherCert, err := c.Rancher.FindCertByName(c.RancherCertName)
if err != nil {
logrus.Fatalf("Failed to lookup Rancher certificates: %v", err)
logrus.Fatalf("Error looking up Rancher certificates: %v", err)
}
if rancherCert != nil {
haveRemote = true
c.RancherCertId = rancherCert.Id
logrus.Infof("Found existing Rancher certificate '%s'", rancherCert.Name)
}
if haveLocal && haveRemote {
if rancherCert.SerialNumber != acmeCert.SerialNumber {
logrus.Fatalf("Cannot manage existing Rancher certificate %s: Serial number not matching local store", rancherCert.Name)
if rancherCert.SerialNumber == acmeCert.SerialNumber {
logrus.Infof("Managing renewal of Rancher certificate '%s'", rancherCert.Name)
return
}
logrus.Infof("Managing existing Rancher certificate: %s", rancherCert.Name)
logrus.Infof("Certificate serial number mismatch. Overwriting Rancher certificate '%s'", rancherCert.Name)
c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
return
}
if !haveLocal && haveRemote {
logrus.Fatalf("Cannot manage existing Rancher certificate %s: Not in local store", rancherCert.Name)
}
if haveLocal && !haveRemote {
c.addCertToRancher(acmeCert.PrivateKey, acmeCert.Certificate)
logrus.Infof("Adding Rancher certificate '%s'", rancherCert.Name)
c.addRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
return
}
@@ -63,29 +61,48 @@ func (c *Context) startup() {
acmeCert, failures := c.Acme.Issue(c.Domains)
if len(failures) > 0 {
for k, v := range failures {
logrus.Errorf("[%s] Failed to obtain certificate: %s", k, v.Error())
logrus.Errorf("[%s] Error obtaining certificate: %s", k, v.Error())
}
os.Exit(1)
}
logrus.Infof("Successfully obtained certificate")
logrus.Infof("Successfully obtained SSL certificate")
c.ExpiryDate = acmeCert.ExpiryDate
c.addCertToRancher(acmeCert.PrivateKey, acmeCert.Certificate)
if haveRemote {
logrus.Infof("Overwriting Rancher certificate '%s'", rancherCert.Name)
c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
return
}
c.addRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
}
func (c *Context) addCertToRancher(privateKey, cert []byte) {
rancherCert, err := c.Rancher.AddCertificate(c.RancherCertName, DESCRIPTION, privateKey, cert)
func (c *Context) addRancherCert(privateKey, cert []byte) {
rancherCert, err := c.Rancher.AddCertificate(c.RancherCertName, CERT_DESCRIPTION, privateKey, cert)
if err != nil {
logrus.Fatalf("Failed to add Rancher certificate resource: %v", err)
logrus.Fatalf("Failed to add Rancher certificate '%s': %v", c.RancherCertName, err)
}
c.RancherCertId = rancherCert.Id
logrus.Infof("Added Rancher certificate resource: %s", c.RancherCertName)
logrus.Infof("Added Rancher certificate '%s'", c.RancherCertName)
}
func (c *Context) updateRancherCert(privateKey, cert []byte) {
err := c.Rancher.UpdateCertificate(c.RancherCertId, CERT_DESCRIPTION, privateKey, cert)
if err != nil {
logrus.Fatalf("Failed to update Rancher certificate '%s': %v", c.RancherCertName, err)
}
logrus.Infof("Updated Rancher certificate '%s'", c.RancherCertName)
err = c.Rancher.UpgradeLoadBalancers(c.RancherCertId)
if err != nil {
logrus.Fatalf("Error upgrading load balancers: %v", err)
}
}
func (c *Context) renew() {
logrus.Infof("Trying to renew certificate for %s", strings.Join(c.Domains, " | "))
logrus.Infof("Trying to renew certificate for '%s'", strings.Join(c.Domains, " | "))
acmeCert, err := c.Acme.Renew(c.Domains)
if err != nil {
@@ -95,17 +112,7 @@ func (c *Context) renew() {
logrus.Infof("Successfully renewed certificate")
c.ExpiryDate = acmeCert.ExpiryDate
err = c.Rancher.UpdateCertificate(c.RancherCertId, acmeCert.Certificate)
if err != nil {
logrus.Fatalf("Failed to update Rancher certificate resource: %v", err)
}
logrus.Infof("Updated Rancher certificate resource %s", c.RancherCertName)
err = c.Rancher.UpgradeLoadBalancers(c.RancherCertId)
if err != nil {
logrus.Fatalf("Error upgrading load balancers: %v", err)
}
c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
}
func (c *Context) timer() <-chan time.Time {
@@ -116,13 +123,14 @@ func (c *Context) timer() <-chan time.Time {
left = 10 * time.Second
}
// Test mode
logrus.Infof("Next certificate renewal scheduled for %s", next.Format("2006/01/02 15:04 MST"))
// Debug option set to true enables test mode
if c.Debug {
logrus.Debug("Test mode enabled: Certificate renewal in 120 seconds")
logrus.Debug("Test mode enabled: certificate will be renewed in 120 seconds")
left = 120 * time.Second
}
logrus.Infof("Next certificate renewal scheduled for %s", next.Format("2006/01/02 15:04 MST"))
return time.After(left)
}
+11 -8
View File
@@ -24,7 +24,7 @@ func (r *Client) AddCertificate(name, descr string, privateKey, cert []byte) (*r
return nil, err
}
logrus.Debugf("Added certificate: %s", rancherCert.Name)
logrus.Debugf("Waiting for added certificate '%s' to become active", rancherCert.Name)
if err := r.WaitCertificate(rancherCert); err != nil {
return nil, err
@@ -34,28 +34,31 @@ func (r *Client) AddCertificate(name, descr string, privateKey, cert []byte) (*r
}
// UpdateCertificate updates an existing certificate resource using the given PEM encoded certificate
func (r *Client) UpdateCertificate(certId string, cert []byte) error {
func (r *Client) UpdateCertificate(certId, descr string, privateKey, cert []byte) error {
certString := string(cert[:])
keyString := string(privateKey[:])
rancherCert, err := r.client.Certificate.ById(certId)
if err != nil {
return err
}
rancherCert, err = r.client.Certificate.Update(rancherCert, &rancherClient.Certificate{
Cert: certString,
Description: descr,
Cert: certString,
Key: keyString,
})
if err != nil {
return err
}
logrus.Debugf("Updated certificate %s", rancherCert.Name)
logrus.Debugf("Waiting for updated certificate '%s' to become active", rancherCert.Name)
return r.WaitCertificate(rancherCert)
}
// FindCertByName retrieves an existing certificate
func (r *Client) FindCertByName(name string) (*rancherClient.Certificate, error) {
logrus.Debugf("Looking up certificate by name %s", name)
logrus.Debugf("Looking up Rancher certificate by name: '%s'", name)
certificates, err := r.client.Certificate.List(&rancherClient.ListOpts{
Filters: map[string]interface{}{
@@ -72,7 +75,7 @@ func (r *Client) FindCertByName(name string) (*rancherClient.Certificate, error)
return nil, nil
}
logrus.Debugf("Found existing certificate %s", name)
logrus.Debugf("Found existing Rancher certificate by name: '%s'", name)
return &certificates.Data[0], nil
}
@@ -84,9 +87,9 @@ func (r *Client) GetCertById(certId string) (*rancherClient.Certificate, error)
}
if rancherCert == nil {
return nil, fmt.Errorf("Certificate with Id %s does not exist.", certId)
return nil, fmt.Errorf("Rancher certificate with Id '%s' does not exist", certId)
}
logrus.Debugf("Found certificate %s by Id %s", rancherCert.Name, certId)
logrus.Debugf("Got Rancher certificate '%s' by Id '%s'", rancherCert.Name, certId)
return rancherCert, nil
}
+11 -9
View File
@@ -13,22 +13,22 @@ func (r *Client) UpgradeLoadBalancers(certId string) error {
}
if len(balancers) == 0 {
logrus.Info("Certificate is not being used by any load balancers")
logrus.Info("Certificate is not being used by any load balancer")
return nil
}
for _, id := range balancers {
lb, err := r.client.LoadBalancerService.ById(id)
if err != nil {
logrus.Errorf("Failed to acquire load balancer by id %s: %v", id, err)
logrus.Errorf("Failed to get load balancer by id '%s': %v", id, err)
continue
}
logrus.Infof("Upgrading load balancer %s...", lb.Name)
err = r.upgrade(lb)
if err != nil {
logrus.Errorf("Failed to upgrade load balancer %s: %v", lb.Name, err)
logrus.Errorf("Failed to upgrade load balancer '%s': %v", lb.Name, err)
} else {
logrus.Infof("Successfully upgraded load balancer %s with renewed certificate", lb.Name)
logrus.Infof("Upgraded load balancer '%s' with renewed certificate", lb.Name)
}
}
@@ -43,6 +43,8 @@ func (r *Client) upgrade(lb *rancherClient.LoadBalancerService) error {
}
upgrade.ToServiceStrategy = &rancherClient.ToServiceUpgradeStrategy{}
logrus.Debugf("Upgrading load balancer '%s'", lb.Name)
service, err := r.client.LoadBalancerService.ActionUpgrade(lb, upgrade)
if err != nil {
return err
@@ -50,11 +52,11 @@ func (r *Client) upgrade(lb *rancherClient.LoadBalancerService) error {
err = r.WaitService(service)
if err != nil {
logrus.Warnf("Upgrade check: %v", err)
logrus.Warnf(err.Error())
}
if service.State == "upgraded" {
logrus.Debugf("Finishing upgrade of load balancer: %s", lb.Name)
logrus.Debugf("Finishing upgrade for load balancer '%s'", lb.Name)
service, err = r.client.Service.ActionFinishupgrade(service)
if err != nil {
@@ -62,7 +64,7 @@ func (r *Client) upgrade(lb *rancherClient.LoadBalancerService) error {
}
err = r.WaitService(service)
if err != nil {
logrus.Warnf("Upgrade check: %v", err)
logrus.Warnf(err.Error())
}
}
@@ -72,7 +74,7 @@ func (r *Client) upgrade(lb *rancherClient.LoadBalancerService) error {
func (r *Client) findLoadBalancerServicesByCert(certId string) ([]string, error) {
var results []string
logrus.Debugf("Looking up load balancers matching certificate id %s", certId)
logrus.Debugf("Looking up load balancers matching certificate id '%s'", certId)
balancers, err := r.client.LoadBalancerService.List(&rancherClient.ListOpts{
Filters: map[string]interface{}{
+1 -1
View File
@@ -34,7 +34,7 @@ func backoff(maxDuration time.Duration, timeoutMessage string, f func() (bool, e
// WaitFor waits for a resource to reach a certain state.
func (r *Client) WaitFor(resource *rancherClient.Resource, output interface{}, transitioning func() string) error {
return backoff(2*time.Minute, fmt.Sprintf("Time out waiting for %s:%s to transition", resource.Type, resource.Id), func() (bool, error) {
return backoff(2*time.Minute, fmt.Sprintf("Time out waiting for %s:%s to become active", resource.Type, resource.Id), func() (bool, error) {
err := r.client.Reload(resource, output)
if err != nil {
return false, err