Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
caddy
Commits
38885e43
Commit
38885e43
authored
Oct 20, 2015
by
Matthew Holt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Simplify timing mechanism for checking renewals
parent
c626774d
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
100 additions
and
145 deletions
+100
-145
config/letsencrypt/letsencrypt.go
config/letsencrypt/letsencrypt.go
+6
-140
config/letsencrypt/renew.go
config/letsencrypt/renew.go
+94
-0
config/letsencrypt/storage.go
config/letsencrypt/storage.go
+0
-5
No files found.
config/letsencrypt/letsencrypt.go
View file @
38885e43
...
...
@@ -7,7 +7,6 @@ import (
"encoding/json"
"errors"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
...
...
@@ -40,7 +39,8 @@ func Activate(configs []server.Config) ([]server.Config, error) {
configs
=
autoConfigure
(
&
configs
[
i
],
configs
)
}
}
// Handle cert renewal on Startup
// First renew any existing certificates that need it
processCertificateRenewal
(
configs
)
// Group configs by LE email address; this will help us
...
...
@@ -77,7 +77,7 @@ func Activate(configs []server.Config) ([]server.Config, error) {
}
}
go
renewalFunc
(
configs
)
go
keepCertificatesRenewed
(
configs
)
return
configs
,
nil
}
...
...
@@ -284,141 +284,6 @@ func redirPlaintextHost(cfg server.Config) server.Config {
}
}
func
renewalFunc
(
configs
[]
server
.
Config
)
{
nextRun
,
err
:=
processCertificateRenewal
(
configs
)
if
err
!=
nil
{
log
.
Printf
(
"[ERROR] Could not start renewal routine. %v"
,
err
)
return
}
for
{
timer
:=
time
.
NewTimer
(
time
.
Duration
(
nextRun
)
*
time
.
Hour
)
<-
timer
.
C
nextRun
,
err
=
processCertificateRenewal
(
configs
)
if
err
!=
nil
{
log
.
Printf
(
"[ERROR] Renewal routing stopped. %v"
,
err
)
return
}
}
}
// checkCertificateRenewal loops through all configured
// sites and looks for certificates to renew. Nothing is mutated
// through this function. The changes happen directly on disk.
func
processCertificateRenewal
(
configs
[]
server
.
Config
)
(
int
,
error
)
{
log
.
Print
(
"[INFO] Processing certificate renewals..."
)
// Check if we should run. If not, get out of here.
next
,
err
:=
getNextRenewalShedule
()
if
err
!=
nil
{
return
0
,
err
}
if
next
>
0
{
return
next
,
nil
}
// We are executing. Write the current timestamp into the file.
err
=
ioutil
.
WriteFile
(
storage
.
RenewTimerFile
(),
[]
byte
(
time
.
Now
()
.
UTC
()
.
Format
(
time
.
RFC3339
)),
0600
)
if
err
!=
nil
{
return
0
,
err
}
next
=
renewTimer
for
_
,
cfg
:=
range
configs
{
// Check if this entry is TLS enabled and managed by LE
if
!
cfg
.
TLS
.
Enabled
||
!
existingCertAndKey
(
cfg
.
Host
)
{
continue
}
// Read the certificate and get the NotAfter time.
certBytes
,
err
:=
ioutil
.
ReadFile
(
storage
.
SiteCertFile
(
cfg
.
Host
))
if
err
!=
nil
{
return
0
,
err
}
expTime
,
err
:=
acme
.
GetPEMCertExpiration
(
certBytes
)
if
err
!=
nil
{
return
0
,
err
}
// The time returned from the certificate is always in UTC.
// So calculate the time left with local time as UTC.
// Directly convert it to days for the following checks.
daysLeft
:=
int
(
expTime
.
Sub
(
time
.
Now
()
.
UTC
())
.
Hours
()
/
24
)
// Renew on two or less days remaining.
if
daysLeft
<=
2
{
log
.
Printf
(
"[WARN] There are %d days left on the certificate of %s. Trying to renew now."
,
daysLeft
,
cfg
.
Host
)
client
,
err
:=
newClient
(
getEmail
(
cfg
))
if
err
!=
nil
{
return
0
,
err
}
// Read metadata
metaBytes
,
err
:=
ioutil
.
ReadFile
(
storage
.
SiteMetaFile
(
cfg
.
Host
))
if
err
!=
nil
{
return
0
,
err
}
privBytes
,
err
:=
ioutil
.
ReadFile
(
storage
.
SiteKeyFile
(
cfg
.
Host
))
if
err
!=
nil
{
return
0
,
err
}
var
certMeta
acme
.
CertificateResource
err
=
json
.
Unmarshal
(
metaBytes
,
&
certMeta
)
certMeta
.
Certificate
=
certBytes
certMeta
.
PrivateKey
=
privBytes
// Renew certificate.
// TODO: revokeOld should be an option in the caddyfile
newCertMeta
,
err
:=
client
.
RenewCertificate
(
certMeta
,
true
)
if
err
!=
nil
{
return
0
,
err
}
saveCertsAndKeys
([]
acme
.
CertificateResource
{
newCertMeta
})
}
// Warn on 14 days remaining
if
daysLeft
<=
14
{
log
.
Printf
(
"[WARN] There are %d days left on the certificate of %s. Will renew on two days left.
\n
"
,
daysLeft
,
cfg
.
Host
)
}
}
return
next
,
nil
}
// getNextRenewalShedule calculates the offset in hours the renew process should
// run from the current time. If the file the time is in does not exists, the
// function returns zero to trigger a renew asap.
func
getNextRenewalShedule
()
(
int
,
error
)
{
// Check if the file exists. If it does not, return 0 to indicate immediate processing.
if
_
,
err
:=
os
.
Stat
(
storage
.
RenewTimerFile
());
os
.
IsNotExist
(
err
)
{
return
0
,
nil
}
renewTimeBytes
,
err
:=
ioutil
.
ReadFile
(
storage
.
RenewTimerFile
())
if
err
!=
nil
{
return
0
,
err
}
renewalTime
,
err
:=
time
.
Parse
(
time
.
RFC3339
,
string
(
renewTimeBytes
))
if
err
!=
nil
{
return
0
,
err
}
// The time read from the file was equal or more then 24 hours in the past,
// write the current time to the file and return true.
hoursSinceRenew
:=
int
(
time
.
Now
()
.
UTC
()
.
Sub
(
renewalTime
)
.
Hours
())
if
hoursSinceRenew
>=
renewTimer
{
return
0
,
nil
}
return
hoursSinceRenew
,
nil
}
var
(
// Let's Encrypt account email to use if none provided
DefaultEmail
string
...
...
@@ -431,13 +296,14 @@ var (
const
(
// The base URL to the Let's Encrypt CA
// TODO: Staging API URL is: https://acme-staging.api.letsencrypt.org
// TODO: Production endpoint is: https://acme-v01.api.letsencrypt.org
caURL
=
"http://192.168.99.100:4000"
// The port to expose to the CA server for Simple HTTP Challenge
exposePort
=
"5001"
//
Renewal Timer - Check renewals every x hours.
renew
Timer
=
24
//
How often to check certificates for renewal
renew
Interval
=
24
*
time
.
Hour
)
// KeySize represents the length of a key in bits.
...
...
config/letsencrypt/renew.go
0 → 100644
View file @
38885e43
package
letsencrypt
import
(
"encoding/json"
"io/ioutil"
"log"
"time"
"github.com/mholt/caddy/server"
"github.com/xenolf/lego/acme"
)
// keepCertificatesRenewed is a permanently-blocking function
// that loops indefinitely and, on a regular schedule, checks
// certificates for expiration and initiates a renewal of certs
// that are expiring soon.
func
keepCertificatesRenewed
(
configs
[]
server
.
Config
)
{
ticker
:=
time
.
Tick
(
renewInterval
)
for
range
ticker
{
if
err
:=
processCertificateRenewal
(
configs
);
err
!=
nil
{
log
.
Printf
(
"[ERROR] cert renewal: %v"
,
err
)
}
}
}
// checkCertificateRenewal loops through all configured
// sites and looks for certificates to renew. Nothing is mutated
// through this function. The changes happen directly on disk.
func
processCertificateRenewal
(
configs
[]
server
.
Config
)
error
{
log
.
Print
(
"[INFO] Processing certificate renewals..."
)
for
_
,
cfg
:=
range
configs
{
// Check if this entry is TLS enabled and managed by LE
if
!
cfg
.
TLS
.
Enabled
||
!
existingCertAndKey
(
cfg
.
Host
)
{
continue
}
// Read the certificate and get the NotAfter time.
certBytes
,
err
:=
ioutil
.
ReadFile
(
storage
.
SiteCertFile
(
cfg
.
Host
))
if
err
!=
nil
{
return
err
}
expTime
,
err
:=
acme
.
GetPEMCertExpiration
(
certBytes
)
if
err
!=
nil
{
return
err
}
// The time returned from the certificate is always in UTC.
// So calculate the time left with local time as UTC.
// Directly convert it to days for the following checks.
daysLeft
:=
int
(
expTime
.
Sub
(
time
.
Now
()
.
UTC
())
.
Hours
()
/
24
)
// Renew on two or less days remaining.
if
daysLeft
<=
2
{
log
.
Printf
(
"[WARN] There are %d days left on the certificate of %s. Trying to renew now."
,
daysLeft
,
cfg
.
Host
)
client
,
err
:=
newClient
(
getEmail
(
cfg
))
if
err
!=
nil
{
return
err
}
// Read metadata
metaBytes
,
err
:=
ioutil
.
ReadFile
(
storage
.
SiteMetaFile
(
cfg
.
Host
))
if
err
!=
nil
{
return
err
}
privBytes
,
err
:=
ioutil
.
ReadFile
(
storage
.
SiteKeyFile
(
cfg
.
Host
))
if
err
!=
nil
{
return
err
}
var
certMeta
acme
.
CertificateResource
err
=
json
.
Unmarshal
(
metaBytes
,
&
certMeta
)
certMeta
.
Certificate
=
certBytes
certMeta
.
PrivateKey
=
privBytes
// Renew certificate.
// TODO: revokeOld should be an option in the caddyfile
newCertMeta
,
err
:=
client
.
RenewCertificate
(
certMeta
,
true
)
if
err
!=
nil
{
return
err
}
saveCertsAndKeys
([]
acme
.
CertificateResource
{
newCertMeta
})
}
// Warn on 14 days remaining
if
daysLeft
<=
14
{
log
.
Printf
(
"[WARN] There are %d days left on the certificate of %s. Will renew on two days left.
\n
"
,
daysLeft
,
cfg
.
Host
)
}
}
return
nil
}
config/letsencrypt/storage.go
View file @
38885e43
...
...
@@ -16,11 +16,6 @@ var storage = Storage(filepath.Join(app.DataFolder(), "letsencrypt"))
// forming file paths derived from it.
type
Storage
string
// RenewTimerFile returns the path to the file used for renewal timing.
func
(
s
Storage
)
RenewTimerFile
()
string
{
return
filepath
.
Join
(
string
(
s
),
"lastrenew"
)
}
// Sites gets the directory that stores site certificate and keys.
func
(
s
Storage
)
Sites
()
string
{
return
filepath
.
Join
(
string
(
s
),
"sites"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment