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
4df8028b
Commit
4df8028b
authored
Mar 21, 2018
by
Matthew Holt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
diagnostics: Add/remove metrics
parent
385ea533
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
177 additions
and
58 deletions
+177
-58
caddy.go
caddy.go
+3
-2
caddyhttp/httpserver/mitm.go
caddyhttp/httpserver/mitm.go
+6
-3
caddyhttp/httpserver/plugin.go
caddyhttp/httpserver/plugin.go
+40
-2
caddyhttp/httpserver/server.go
caddyhttp/httpserver/server.go
+3
-1
caddytls/certificates.go
caddytls/certificates.go
+4
-0
caddytls/client.go
caddytls/client.go
+11
-3
caddytls/handshake.go
caddytls/handshake.go
+24
-17
caddytls/setup.go
caddytls/setup.go
+6
-0
diagnostics/collection.go
diagnostics/collection.go
+22
-18
diagnostics/diagnostics.go
diagnostics/diagnostics.go
+44
-12
sigtrap.go
sigtrap.go
+5
-0
sigtrap_posix.go
sigtrap_posix.go
+9
-0
No files found.
caddy.go
View file @
4df8028b
...
@@ -123,6 +123,7 @@ type Instance struct {
...
@@ -123,6 +123,7 @@ type Instance struct {
StorageMu
sync
.
RWMutex
StorageMu
sync
.
RWMutex
}
}
// Instances returns the list of instances.
func
Instances
()
[]
*
Instance
{
func
Instances
()
[]
*
Instance
{
return
instances
return
instances
}
}
...
@@ -616,7 +617,7 @@ func ValidateAndExecuteDirectives(cdyfile Input, inst *Instance, justValidate bo
...
@@ -616,7 +617,7 @@ func ValidateAndExecuteDirectives(cdyfile Input, inst *Instance, justValidate bo
return
fmt
.
Errorf
(
"error inspecting server blocks: %v"
,
err
)
return
fmt
.
Errorf
(
"error inspecting server blocks: %v"
,
err
)
}
}
diagnostics
.
Set
(
"num_server_blocks"
,
len
(
sblocks
))
diagnostics
.
Set
(
"
http_
num_server_blocks"
,
len
(
sblocks
))
return
executeDirectives
(
inst
,
cdyfile
.
Path
(),
stype
.
Directives
(),
sblocks
,
justValidate
)
return
executeDirectives
(
inst
,
cdyfile
.
Path
(),
stype
.
Directives
(),
sblocks
,
justValidate
)
}
}
...
@@ -872,7 +873,7 @@ func Stop() error {
...
@@ -872,7 +873,7 @@ func Stop() error {
// explicitly like a common local hostname. addr must only
// explicitly like a common local hostname. addr must only
// be a host or a host:port combination.
// be a host or a host:port combination.
func
IsLoopback
(
addr
string
)
bool
{
func
IsLoopback
(
addr
string
)
bool
{
host
,
_
,
err
:=
net
.
SplitHostPort
(
addr
)
host
,
_
,
err
:=
net
.
SplitHostPort
(
strings
.
ToLower
(
addr
)
)
if
err
!=
nil
{
if
err
!=
nil
{
host
=
addr
// happens if the addr is just a hostname
host
=
addr
// happens if the addr is just a hostname
}
}
...
...
caddyhttp/httpserver/mitm.go
View file @
4df8028b
...
@@ -51,6 +51,9 @@ type tlsHandler struct {
...
@@ -51,6 +51,9 @@ type tlsHandler struct {
// Halderman, et. al. in "The Security Impact of HTTPS Interception" (NDSS '17):
// Halderman, et. al. in "The Security Impact of HTTPS Interception" (NDSS '17):
// https://jhalderm.com/pub/papers/interception-ndss17.pdf
// https://jhalderm.com/pub/papers/interception-ndss17.pdf
func
(
h
*
tlsHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
func
(
h
*
tlsHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
// TODO: one request per connection, we should report UA in connection with
// handshake (reported in caddytls package) and our MITM assessment
if
h
.
listener
==
nil
{
if
h
.
listener
==
nil
{
h
.
next
.
ServeHTTP
(
w
,
r
)
h
.
next
.
ServeHTTP
(
w
,
r
)
return
return
...
@@ -100,12 +103,12 @@ func (h *tlsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
...
@@ -100,12 +103,12 @@ func (h *tlsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if
checked
{
if
checked
{
r
=
r
.
WithContext
(
context
.
WithValue
(
r
.
Context
(),
MitmCtxKey
,
mitm
))
r
=
r
.
WithContext
(
context
.
WithValue
(
r
.
Context
(),
MitmCtxKey
,
mitm
))
if
mitm
{
if
mitm
{
go
diagnostics
.
AppendUnique
(
"mitm"
,
"likely"
)
go
diagnostics
.
AppendUnique
(
"
http_
mitm"
,
"likely"
)
}
else
{
}
else
{
go
diagnostics
.
AppendUnique
(
"mitm"
,
"unlikely"
)
go
diagnostics
.
AppendUnique
(
"
http_
mitm"
,
"unlikely"
)
}
}
}
else
{
}
else
{
go
diagnostics
.
AppendUnique
(
"mitm"
,
"unknown"
)
go
diagnostics
.
AppendUnique
(
"
http_
mitm"
,
"unknown"
)
}
}
if
mitm
&&
h
.
closeOnMITM
{
if
mitm
&&
h
.
closeOnMITM
{
...
...
caddyhttp/httpserver/plugin.go
View file @
4df8028b
...
@@ -29,6 +29,7 @@ import (
...
@@ -29,6 +29,7 @@ import (
"github.com/mholt/caddy/caddyfile"
"github.com/mholt/caddy/caddyfile"
"github.com/mholt/caddy/caddyhttp/staticfiles"
"github.com/mholt/caddy/caddyhttp/staticfiles"
"github.com/mholt/caddy/caddytls"
"github.com/mholt/caddy/caddytls"
"github.com/mholt/caddy/diagnostics"
)
)
const
serverType
=
"http"
const
serverType
=
"http"
...
@@ -205,9 +206,34 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd
...
@@ -205,9 +206,34 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd
// MakeServers uses the newly-created siteConfigs to
// MakeServers uses the newly-created siteConfigs to
// create and return a list of server instances.
// create and return a list of server instances.
func
(
h
*
httpContext
)
MakeServers
()
([]
caddy
.
Server
,
error
)
{
func
(
h
*
httpContext
)
MakeServers
()
([]
caddy
.
Server
,
error
)
{
// make sure TLS is disabled for explicitly-HTTP sites
// make a rough estimate as to whether we're in a "production
// (necessary when HTTP address shares a block containing tls)
// environment/system" - start by assuming that most production
// servers will set their default CA endpoint to a public,
// trusted CA (obviously not a perfect hueristic)
var
looksLikeProductionCA
bool
for
_
,
publicCAEndpoint
:=
range
caddytls
.
KnownACMECAs
{
if
strings
.
Contains
(
caddytls
.
DefaultCAUrl
,
publicCAEndpoint
)
{
looksLikeProductionCA
=
true
break
}
}
var
atLeastOneSiteLooksLikeProduction
bool
for
_
,
cfg
:=
range
h
.
siteConfigs
{
for
_
,
cfg
:=
range
h
.
siteConfigs
{
// if we aren't sure yet whether it's a "production" server,
// continue to see if all the addresses (both sites and
// listeners) are loopback
if
!
atLeastOneSiteLooksLikeProduction
{
if
!
caddy
.
IsLoopback
(
cfg
.
Addr
.
Host
)
&&
!
caddy
.
IsLoopback
(
cfg
.
ListenHost
)
&&
(
caddytls
.
QualifiesForManagedTLS
(
cfg
)
||
caddytls
.
HostQualifies
(
cfg
.
Addr
.
Host
))
{
atLeastOneSiteLooksLikeProduction
=
true
}
}
// make sure TLS is disabled for explicitly-HTTP sites
// (necessary when HTTP address shares a block containing tls)
if
!
cfg
.
TLS
.
Enabled
{
if
!
cfg
.
TLS
.
Enabled
{
continue
continue
}
}
...
@@ -246,6 +272,18 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) {
...
@@ -246,6 +272,18 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) {
servers
=
append
(
servers
,
s
)
servers
=
append
(
servers
,
s
)
}
}
// NOTE: This value is only a "good" guess. Quite often, development
// environments will use internal DNS or a local hosts file to serve
// real-looking domains in local development. We can't easily tell
// which without doing a DNS lookup, so this guess is definitely naive,
// and if we ever want a better guess, we will have to do DNS lookups.
deploymentGuess
:=
"dev"
if
looksLikeProductionCA
&&
atLeastOneSiteLooksLikeProduction
{
deploymentGuess
=
"production"
}
diagnostics
.
Set
(
"http_deployment_guess"
,
deploymentGuess
)
diagnostics
.
Set
(
"http_num_sites"
,
len
(
h
.
siteConfigs
))
return
servers
,
nil
return
servers
,
nil
}
}
...
...
caddyhttp/httpserver/server.go
View file @
4df8028b
...
@@ -346,7 +346,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
...
@@ -346,7 +346,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
}()
}()
go
diagnostics
.
AppendUnique
(
"user_agent"
,
r
.
Header
.
Get
(
"User-Agent"
))
// TODO: Somehow report UA string in conjunction with TLS handshake, if any (and just once per connection)
go
diagnostics
.
AppendUnique
(
"http_user_agent"
,
r
.
Header
.
Get
(
"User-Agent"
))
go
diagnostics
.
Increment
(
"http_request_count"
)
// copy the original, unchanged URL into the context
// copy the original, unchanged URL into the context
// so it can be referenced by middlewares
// so it can be referenced by middlewares
...
...
caddytls/certificates.go
View file @
4df8028b
...
@@ -26,6 +26,7 @@ import (
...
@@ -26,6 +26,7 @@ import (
"sync"
"sync"
"time"
"time"
"github.com/mholt/caddy/diagnostics"
"golang.org/x/crypto/ocsp"
"golang.org/x/crypto/ocsp"
)
)
...
@@ -165,6 +166,7 @@ func (cfg *Config) CacheManagedCertificate(domain string) (Certificate, error) {
...
@@ -165,6 +166,7 @@ func (cfg *Config) CacheManagedCertificate(domain string) (Certificate, error) {
if
err
!=
nil
{
if
err
!=
nil
{
return
cert
,
err
return
cert
,
err
}
}
diagnostics
.
Increment
(
"tls_managed_cert_count"
)
return
cfg
.
cacheCertificate
(
cert
),
nil
return
cfg
.
cacheCertificate
(
cert
),
nil
}
}
...
@@ -179,6 +181,7 @@ func (cfg *Config) cacheUnmanagedCertificatePEMFile(certFile, keyFile string) er
...
@@ -179,6 +181,7 @@ func (cfg *Config) cacheUnmanagedCertificatePEMFile(certFile, keyFile string) er
return
err
return
err
}
}
cfg
.
cacheCertificate
(
cert
)
cfg
.
cacheCertificate
(
cert
)
diagnostics
.
Increment
(
"tls_manual_cert_count"
)
return
nil
return
nil
}
}
...
@@ -192,6 +195,7 @@ func (cfg *Config) cacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte)
...
@@ -192,6 +195,7 @@ func (cfg *Config) cacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte)
return
err
return
err
}
}
cfg
.
cacheCertificate
(
cert
)
cfg
.
cacheCertificate
(
cert
)
diagnostics
.
Increment
(
"tls_manual_cert_count"
)
return
nil
return
nil
}
}
...
...
caddytls/client.go
View file @
4df8028b
...
@@ -268,7 +268,7 @@ Attempts:
...
@@ -268,7 +268,7 @@ Attempts:
break
break
}
}
go
diagnostics
.
Increment
(
"
acme_certificate
s_obtained"
)
go
diagnostics
.
Increment
(
"
tls_acme_cert
s_obtained"
)
return
nil
return
nil
}
}
...
@@ -340,8 +340,7 @@ func (c *ACMEClient) Renew(name string) error {
...
@@ -340,8 +340,7 @@ func (c *ACMEClient) Renew(name string) error {
}
}
caddy
.
EmitEvent
(
caddy
.
CertRenewEvent
,
name
)
caddy
.
EmitEvent
(
caddy
.
CertRenewEvent
,
name
)
go
diagnostics
.
Increment
(
"acme_certificates_obtained"
)
go
diagnostics
.
Increment
(
"tls_acme_certs_renewed"
)
go
diagnostics
.
Increment
(
"acme_certificates_renewed"
)
return
saveCertResource
(
c
.
storage
,
newCertMeta
)
return
saveCertResource
(
c
.
storage
,
newCertMeta
)
}
}
...
@@ -368,6 +367,8 @@ func (c *ACMEClient) Revoke(name string) error {
...
@@ -368,6 +367,8 @@ func (c *ACMEClient) Revoke(name string) error {
return
err
return
err
}
}
go
diagnostics
.
Increment
(
"tls_acme_certs_revoked"
)
err
=
c
.
storage
.
DeleteSite
(
name
)
err
=
c
.
storage
.
DeleteSite
(
name
)
if
err
!=
nil
{
if
err
!=
nil
{
return
errors
.
New
(
"certificate revoked, but unable to delete certificate file: "
+
err
.
Error
())
return
errors
.
New
(
"certificate revoked, but unable to delete certificate file: "
+
err
.
Error
())
...
@@ -419,3 +420,10 @@ func (c *nameCoordinator) Has(name string) bool {
...
@@ -419,3 +420,10 @@ func (c *nameCoordinator) Has(name string) bool {
c
.
mu
.
RUnlock
()
c
.
mu
.
RUnlock
()
return
ok
return
ok
}
}
// KnownACMECAs is a list of ACME directory endpoints of
// known, public, and trusted ACME-compatible certificate
// authorities.
var
KnownACMECAs
=
[]
string
{
"https://acme-v02.api.letsencrypt.org/directory"
,
}
caddytls/handshake.go
View file @
4df8028b
...
@@ -100,24 +100,31 @@ func (cg configGroup) GetConfigForClient(clientHello *tls.ClientHelloInfo) (*tls
...
@@ -100,24 +100,31 @@ func (cg configGroup) GetConfigForClient(clientHello *tls.ClientHelloInfo) (*tls
//
//
// This method is safe for use as a tls.Config.GetCertificate callback.
// This method is safe for use as a tls.Config.GetCertificate callback.
func
(
cfg
*
Config
)
GetCertificate
(
clientHello
*
tls
.
ClientHelloInfo
)
(
*
tls
.
Certificate
,
error
)
{
func
(
cfg
*
Config
)
GetCertificate
(
clientHello
*
tls
.
ClientHelloInfo
)
(
*
tls
.
Certificate
,
error
)
{
go
diagnostics
.
Append
(
"client_hello"
,
struct
{
// TODO: We need to collect this in a heavily de-duplicating way
NoSNI
bool
`json:"no_sni,omitempty"`
// It would also be nice to associate a handshake with the UA string (but that is only for HTTP server type)
CipherSuites
[]
uint16
`json:"cipher_suites,omitempty"`
// go diagnostics.Append("tls_client_hello", struct {
SupportedCurves
[]
tls
.
CurveID
`json:"curves,omitempty"`
// NoSNI bool `json:"no_sni,omitempty"`
SupportedPoints
[]
uint8
`json:"points,omitempty"`
// CipherSuites []uint16 `json:"cipher_suites,omitempty"`
SignatureSchemes
[]
tls
.
SignatureScheme
`json:"sig_scheme,omitempty"`
// SupportedCurves []tls.CurveID `json:"curves,omitempty"`
ALPN
[]
string
`json:"alpn,omitempty"`
// SupportedPoints []uint8 `json:"points,omitempty"`
SupportedVersions
[]
uint16
`json:"versions,omitempty"`
// SignatureSchemes []tls.SignatureScheme `json:"sig_scheme,omitempty"`
}{
// ALPN []string `json:"alpn,omitempty"`
NoSNI
:
clientHello
.
ServerName
==
""
,
// SupportedVersions []uint16 `json:"versions,omitempty"`
CipherSuites
:
clientHello
.
CipherSuites
,
// }{
SupportedCurves
:
clientHello
.
SupportedCurves
,
// NoSNI: clientHello.ServerName == "",
SupportedPoints
:
clientHello
.
SupportedPoints
,
// CipherSuites: clientHello.CipherSuites,
SignatureSchemes
:
clientHello
.
SignatureSchemes
,
// SupportedCurves: clientHello.SupportedCurves,
ALPN
:
clientHello
.
SupportedProtos
,
// SupportedPoints: clientHello.SupportedPoints,
SupportedVersions
:
clientHello
.
SupportedVersions
,
// SignatureSchemes: clientHello.SignatureSchemes,
})
// ALPN: clientHello.SupportedProtos,
// SupportedVersions: clientHello.SupportedVersions,
// })
cert
,
err
:=
cfg
.
getCertDuringHandshake
(
strings
.
ToLower
(
clientHello
.
ServerName
),
true
,
true
)
cert
,
err
:=
cfg
.
getCertDuringHandshake
(
strings
.
ToLower
(
clientHello
.
ServerName
),
true
,
true
)
if
err
==
nil
{
go
diagnostics
.
Increment
(
"tls_handshake_count"
)
}
else
{
go
diagnostics
.
Append
(
"tls_handshake_error"
,
err
.
Error
())
}
return
&
cert
.
Certificate
,
err
return
&
cert
.
Certificate
,
err
}
}
...
...
caddytls/setup.go
View file @
4df8028b
...
@@ -28,6 +28,7 @@ import (
...
@@ -28,6 +28,7 @@ import (
"strings"
"strings"
"github.com/mholt/caddy"
"github.com/mholt/caddy"
"github.com/mholt/caddy/diagnostics"
)
)
func
init
()
{
func
init
()
{
...
@@ -174,9 +175,11 @@ func setupTLS(c *caddy.Controller) error {
...
@@ -174,9 +175,11 @@ func setupTLS(c *caddy.Controller) error {
case
"max_certs"
:
case
"max_certs"
:
c
.
Args
(
&
maxCerts
)
c
.
Args
(
&
maxCerts
)
config
.
OnDemand
=
true
config
.
OnDemand
=
true
diagnostics
.
Increment
(
"tls_on_demand_count"
)
case
"ask"
:
case
"ask"
:
c
.
Args
(
&
askURL
)
c
.
Args
(
&
askURL
)
config
.
OnDemand
=
true
config
.
OnDemand
=
true
diagnostics
.
Increment
(
"tls_on_demand_count"
)
case
"dns"
:
case
"dns"
:
args
:=
c
.
RemainingArgs
()
args
:=
c
.
RemainingArgs
()
if
len
(
args
)
!=
1
{
if
len
(
args
)
!=
1
{
...
@@ -251,6 +254,7 @@ func setupTLS(c *caddy.Controller) error {
...
@@ -251,6 +254,7 @@ func setupTLS(c *caddy.Controller) error {
return
c
.
Errf
(
"Unable to load certificate and key files for '%s': %v"
,
c
.
Key
,
err
)
return
c
.
Errf
(
"Unable to load certificate and key files for '%s': %v"
,
c
.
Key
,
err
)
}
}
log
.
Printf
(
"[INFO] Successfully loaded TLS assets from %s and %s"
,
certificateFile
,
keyFile
)
log
.
Printf
(
"[INFO] Successfully loaded TLS assets from %s and %s"
,
certificateFile
,
keyFile
)
diagnostics
.
Increment
(
"tls_manual_cert_count"
)
}
}
// load a directory of certificates, if specified
// load a directory of certificates, if specified
...
@@ -270,6 +274,7 @@ func setupTLS(c *caddy.Controller) error {
...
@@ -270,6 +274,7 @@ func setupTLS(c *caddy.Controller) error {
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"self-signed: %v"
,
err
)
return
fmt
.
Errorf
(
"self-signed: %v"
,
err
)
}
}
diagnostics
.
Increment
(
"tls_self_signed_count"
)
}
}
return
nil
return
nil
...
@@ -350,6 +355,7 @@ func loadCertsInDir(cfg *Config, c *caddy.Controller, dir string) error {
...
@@ -350,6 +355,7 @@ func loadCertsInDir(cfg *Config, c *caddy.Controller, dir string) error {
return
c
.
Errf
(
"%s: failed to load cert and key for '%s': %v"
,
path
,
c
.
Key
,
err
)
return
c
.
Errf
(
"%s: failed to load cert and key for '%s': %v"
,
path
,
c
.
Key
,
err
)
}
}
log
.
Printf
(
"[INFO] Successfully loaded TLS assets from %s"
,
path
)
log
.
Printf
(
"[INFO] Successfully loaded TLS assets from %s"
,
path
)
diagnostics
.
Increment
(
"tls_manual_cert_count"
)
}
}
return
nil
return
nil
})
})
...
...
diagnostics/collection.go
View file @
4df8028b
...
@@ -33,7 +33,7 @@ func Init(instanceID uuid.UUID) {
...
@@ -33,7 +33,7 @@ func Init(instanceID uuid.UUID) {
panic
(
"already initialized"
)
panic
(
"already initialized"
)
}
}
if
str
:=
instanceID
.
String
();
str
==
""
||
if
str
:=
instanceID
.
String
();
str
==
""
||
instanceID
.
String
()
==
"00000000-0000-0000-0000-000000000000"
{
str
==
"00000000-0000-0000-0000-000000000000"
{
panic
(
"empty UUID"
)
panic
(
"empty UUID"
)
}
}
instanceUUID
=
instanceID
instanceUUID
=
instanceID
...
@@ -73,6 +73,10 @@ func StartEmitting() {
...
@@ -73,6 +73,10 @@ func StartEmitting() {
//
//
// It is a no-op if the package was never initialized
// It is a no-op if the package was never initialized
// or if emitting was never started.
// or if emitting was never started.
//
// NOTE: This function is blocking. Run in a goroutine if
// you want to guarantee no blocking at critical times
// like exiting the program.
func
StopEmitting
()
{
func
StopEmitting
()
{
if
!
enabled
{
if
!
enabled
{
return
return
...
@@ -83,7 +87,12 @@ func StopEmitting() {
...
@@ -83,7 +87,12 @@ func StopEmitting() {
return
return
}
}
updateTimerMu
.
Unlock
()
updateTimerMu
.
Unlock
()
logEmit
(
true
)
logEmit
(
true
)
// likely too early; may take minutes to return
}
// Reset empties the current payload buffer.
func
Reset
()
{
resetBuffer
()
}
}
// Set puts a value in the buffer to be included
// Set puts a value in the buffer to be included
...
@@ -142,7 +151,7 @@ func Append(key string, value interface{}) {
...
@@ -142,7 +151,7 @@ func Append(key string, value interface{}) {
bufferMu
.
Unlock
()
bufferMu
.
Unlock
()
}
}
// AppendUnique adds value to a set
named
key.
// AppendUnique adds value to a set
named
key.
// Set items are unordered. Values in the set
// Set items are unordered. Values in the set
// are unique, but how many times they are
// are unique, but how many times they are
// appended is counted.
// appended is counted.
...
@@ -178,24 +187,23 @@ func AppendUnique(key string, value interface{}) {
...
@@ -178,24 +187,23 @@ func AppendUnique(key string, value interface{}) {
bufferMu
.
Unlock
()
bufferMu
.
Unlock
()
}
}
//
Increment adds 1
to a value named key.
//
Add adds amount
to a value named key.
// If it does not exist, it is created with
// If it does not exist, it is created with
// a value of 1. If key maps to a type that
// a value of 1. If key maps to a type that
// is not an integer, a panic is logged,
// is not an integer, a panic is logged,
// and this is a no-op.
// and this is a no-op.
func
Increment
(
key
string
)
{
func
Add
(
key
string
,
amount
int
)
{
incrementOrDecrement
(
key
,
true
)
atomicAdd
(
key
,
amount
)
}
}
// Decrement is the same as increment except
// Increment is a shortcut for Add(key, 1)
// it subtracts 1.
func
Increment
(
key
string
)
{
func
Decrement
(
key
string
)
{
atomicAdd
(
key
,
1
)
incrementOrDecrement
(
key
,
false
)
}
}
//
inc == true: increment
//
atomicAdd adds amount (negative to subtract)
//
inc == false: decrement
//
to key.
func
incrementOrDecrement
(
key
string
,
inc
bool
)
{
func
atomicAdd
(
key
string
,
amount
int
)
{
if
!
enabled
{
if
!
enabled
{
return
return
}
}
...
@@ -214,10 +222,6 @@ func incrementOrDecrement(key string, inc bool) {
...
@@ -214,10 +222,6 @@ func incrementOrDecrement(key string, inc bool) {
}
}
bufferItemCount
++
bufferItemCount
++
}
}
if
inc
{
buffer
[
key
]
=
intVal
+
amount
buffer
[
key
]
=
intVal
+
1
}
else
{
buffer
[
key
]
=
intVal
-
1
}
bufferMu
.
Unlock
()
bufferMu
.
Unlock
()
}
}
diagnostics/diagnostics.go
View file @
4df8028b
...
@@ -48,14 +48,16 @@ import (
...
@@ -48,14 +48,16 @@ import (
)
)
// logEmit calls emit and then logs the error, if any.
// logEmit calls emit and then logs the error, if any.
// See docs for emit.
func
logEmit
(
final
bool
)
{
func
logEmit
(
final
bool
)
{
err
:=
emit
(
final
)
err
:=
emit
(
final
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Printf
(
"[ERROR] Sending di
ga
nostics: %v"
,
err
)
log
.
Printf
(
"[ERROR] Sending di
ag
nostics: %v"
,
err
)
}
}
}
}
// emit sends an update to the diagnostics server.
// emit sends an update to the diagnostics server.
// Set final to true if this is the last call to emit.
// If final is true, no future updates will be scheduled.
// If final is true, no future updates will be scheduled.
// Otherwise, the next update will be scheduled.
// Otherwise, the next update will be scheduled.
func
emit
(
final
bool
)
error
{
func
emit
(
final
bool
)
error
{
...
@@ -136,9 +138,11 @@ func emit(final bool) error {
...
@@ -136,9 +138,11 @@ func emit(final bool) error {
reply
.
NextUpdate
=
time
.
Duration
(
ra
)
*
time
.
Second
reply
.
NextUpdate
=
time
.
Duration
(
ra
)
*
time
.
Second
}
}
}
}
log
.
Printf
(
"[NOTICE] Sending diagnostics: we were too early; waiting %s before trying again"
,
reply
.
NextUpdate
)
if
!
final
{
time
.
Sleep
(
reply
.
NextUpdate
)
log
.
Printf
(
"[NOTICE] Sending diagnostics: we were too early; waiting %s before trying again"
,
reply
.
NextUpdate
)
continue
time
.
Sleep
(
reply
.
NextUpdate
)
continue
}
}
else
if
resp
.
StatusCode
>=
400
{
}
else
if
resp
.
StatusCode
>=
400
{
err
=
fmt
.
Errorf
(
"diagnostics server returned status code %d"
,
resp
.
StatusCode
)
err
=
fmt
.
Errorf
(
"diagnostics server returned status code %d"
,
resp
.
StatusCode
)
continue
continue
...
@@ -146,7 +150,7 @@ func emit(final bool) error {
...
@@ -146,7 +150,7 @@ func emit(final bool) error {
break
break
}
}
if
err
==
nil
{
if
err
==
nil
&&
!
final
{
// (remember, if there was an error, we return it
// (remember, if there was an error, we return it
// below, so it WILL get logged if it's supposed to)
// below, so it WILL get logged if it's supposed to)
log
.
Println
(
"[INFO] Sending diagnostics: success"
)
log
.
Println
(
"[INFO] Sending diagnostics: success"
)
...
@@ -181,13 +185,7 @@ func emit(final bool) error {
...
@@ -181,13 +185,7 @@ func emit(final bool) error {
// resulting byte slice is lost, the payload is
// resulting byte slice is lost, the payload is
// gone with it.
// gone with it.
func
makePayloadAndResetBuffer
()
([]
byte
,
error
)
{
func
makePayloadAndResetBuffer
()
([]
byte
,
error
)
{
// make a local pointer to the buffer, then reset
bufCopy
:=
resetBuffer
()
// the buffer to an empty map to clear it out
bufferMu
.
Lock
()
bufCopy
:=
buffer
buffer
=
make
(
map
[
string
]
interface
{})
bufferItemCount
=
0
bufferMu
.
Unlock
()
// encode payload in preparation for transmission
// encode payload in preparation for transmission
payload
:=
Payload
{
payload
:=
Payload
{
...
@@ -198,6 +196,21 @@ func makePayloadAndResetBuffer() ([]byte, error) {
...
@@ -198,6 +196,21 @@ func makePayloadAndResetBuffer() ([]byte, error) {
return
json
.
Marshal
(
payload
)
return
json
.
Marshal
(
payload
)
}
}
// resetBuffer makes a local pointer to the buffer,
// then resets the buffer by assigning to be a newly-
// made value to clear it out, then sets the buffer
// item count to 0. It returns the copied pointer to
// the original map so the old buffer value can be
// used locally.
func
resetBuffer
()
map
[
string
]
interface
{}
{
bufferMu
.
Lock
()
bufCopy
:=
buffer
buffer
=
make
(
map
[
string
]
interface
{})
bufferItemCount
=
0
bufferMu
.
Unlock
()
return
bufCopy
}
// Response contains the body of a response from the
// Response contains the body of a response from the
// diagnostics server.
// diagnostics server.
type
Response
struct
{
type
Response
struct
{
...
@@ -222,10 +235,28 @@ type Payload struct {
...
@@ -222,10 +235,28 @@ type Payload struct {
// The UTC timestamp of the transmission
// The UTC timestamp of the transmission
Timestamp
time
.
Time
`json:"timestamp"`
Timestamp
time
.
Time
`json:"timestamp"`
// The timestamp before which the next update is expected
// (NOT populated by client - the server fills this in
// before it stores the data)
ExpectNext
time
.
Time
`json:"expect_next,omitempty"`
// The metrics
// The metrics
Data
map
[
string
]
interface
{}
`json:"data,omitempty"`
Data
map
[
string
]
interface
{}
`json:"data,omitempty"`
}
}
// Int returns the value of the data keyed by key
// if it is an integer; otherwise it returns 0.
func
(
p
Payload
)
Int
(
key
string
)
int
{
val
,
_
:=
p
.
Data
[
key
]
switch
p
.
Data
[
key
]
.
(
type
)
{
case
int
:
return
val
.
(
int
)
case
float64
:
// after JSON-decoding, int becomes float64...
return
int
(
val
.
(
float64
))
}
return
0
}
// countingSet implements a set that counts how many
// countingSet implements a set that counts how many
// times a key is inserted. It marshals to JSON in a
// times a key is inserted. It marshals to JSON in a
// way such that keys are converted to values next
// way such that keys are converted to values next
...
@@ -272,6 +303,7 @@ var (
...
@@ -272,6 +303,7 @@ var (
// instanceUUID is the ID of the current instance.
// instanceUUID is the ID of the current instance.
// This MUST be set to emit diagnostics.
// This MUST be set to emit diagnostics.
// This MUST NOT be openly exposed to clients, for privacy.
instanceUUID
uuid
.
UUID
instanceUUID
uuid
.
UUID
// enabled indicates whether the package has
// enabled indicates whether the package has
...
...
sigtrap.go
View file @
4df8028b
...
@@ -19,6 +19,8 @@ import (
...
@@ -19,6 +19,8 @@ import (
"os"
"os"
"os/signal"
"os/signal"
"sync"
"sync"
"github.com/mholt/caddy/diagnostics"
)
)
// TrapSignals create signal handlers for all applicable signals for this
// TrapSignals create signal handlers for all applicable signals for this
...
@@ -52,6 +54,9 @@ func trapSignalsCrossPlatform() {
...
@@ -52,6 +54,9 @@ func trapSignalsCrossPlatform() {
log
.
Println
(
"[INFO] SIGINT: Shutting down"
)
log
.
Println
(
"[INFO] SIGINT: Shutting down"
)
diagnostics
.
AppendUnique
(
"sigtrap"
,
"SIGINT"
)
go
diagnostics
.
StopEmitting
()
// not guaranteed to finish in time; that's OK (just don't block!)
// important cleanup actions before shutdown callbacks
// important cleanup actions before shutdown callbacks
for
_
,
f
:=
range
OnProcessExit
{
for
_
,
f
:=
range
OnProcessExit
{
f
()
f
()
...
...
sigtrap_posix.go
View file @
4df8028b
...
@@ -21,6 +21,8 @@ import (
...
@@ -21,6 +21,8 @@ import (
"os"
"os"
"os/signal"
"os/signal"
"syscall"
"syscall"
"github.com/mholt/caddy/diagnostics"
)
)
// trapSignalsPosix captures POSIX-only signals.
// trapSignalsPosix captures POSIX-only signals.
...
@@ -49,10 +51,15 @@ func trapSignalsPosix() {
...
@@ -49,10 +51,15 @@ func trapSignalsPosix() {
log
.
Printf
(
"[ERROR] SIGTERM stop: %v"
,
err
)
log
.
Printf
(
"[ERROR] SIGTERM stop: %v"
,
err
)
exitCode
=
3
exitCode
=
3
}
}
diagnostics
.
AppendUnique
(
"sigtrap"
,
"SIGTERM"
)
go
diagnostics
.
StopEmitting
()
// won't finish in time, but that's OK - just don't block
os
.
Exit
(
exitCode
)
os
.
Exit
(
exitCode
)
case
syscall
.
SIGUSR1
:
case
syscall
.
SIGUSR1
:
log
.
Println
(
"[INFO] SIGUSR1: Reloading"
)
log
.
Println
(
"[INFO] SIGUSR1: Reloading"
)
go
diagnostics
.
AppendUnique
(
"sigtrap"
,
"SIGUSR1"
)
// Start with the existing Caddyfile
// Start with the existing Caddyfile
caddyfileToUse
,
inst
,
err
:=
getCurrentCaddyfile
()
caddyfileToUse
,
inst
,
err
:=
getCurrentCaddyfile
()
...
@@ -84,12 +91,14 @@ func trapSignalsPosix() {
...
@@ -84,12 +91,14 @@ func trapSignalsPosix() {
case
syscall
.
SIGUSR2
:
case
syscall
.
SIGUSR2
:
log
.
Println
(
"[INFO] SIGUSR2: Upgrading"
)
log
.
Println
(
"[INFO] SIGUSR2: Upgrading"
)
go
diagnostics
.
AppendUnique
(
"sigtrap"
,
"SIGUSR2"
)
if
err
:=
Upgrade
();
err
!=
nil
{
if
err
:=
Upgrade
();
err
!=
nil
{
log
.
Printf
(
"[ERROR] SIGUSR2: upgrading: %v"
,
err
)
log
.
Printf
(
"[ERROR] SIGUSR2: upgrading: %v"
,
err
)
}
}
case
syscall
.
SIGHUP
:
case
syscall
.
SIGHUP
:
// ignore; this signal is sometimes sent outside of the user's control
// ignore; this signal is sometimes sent outside of the user's control
go
diagnostics
.
AppendUnique
(
"sigtrap"
,
"SIGHUP"
)
}
}
}
}
}()
}()
...
...
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