feat: crl support, metrics
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.idea
|
||||||
10
go.mod
10
go.mod
@@ -5,14 +5,24 @@ go 1.23.4
|
|||||||
require (
|
require (
|
||||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||||
github.com/cloudflare/cfssl v1.6.5
|
github.com/cloudflare/cfssl v1.6.5
|
||||||
|
github.com/prometheus/client_golang v1.20.5
|
||||||
golang.org/x/crypto v0.32.0
|
golang.org/x/crypto v0.32.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/google/certificate-transparency-go v1.1.7 // indirect
|
github.com/google/certificate-transparency-go v1.1.7 // indirect
|
||||||
github.com/jmhodges/clock v1.2.0 // indirect
|
github.com/jmhodges/clock v1.2.0 // indirect
|
||||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 // indirect
|
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
|
github.com/prometheus/common v0.55.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
28
go.sum
28
go.sum
@@ -2,6 +2,10 @@ github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjH
|
|||||||
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cloudflare/cfssl v1.6.5 h1:46zpNkm6dlNkMZH/wMW22ejih6gIaJbzL2du6vD7ZeI=
|
github.com/cloudflare/cfssl v1.6.5 h1:46zpNkm6dlNkMZH/wMW22ejih6gIaJbzL2du6vD7ZeI=
|
||||||
github.com/cloudflare/cfssl v1.6.5/go.mod h1:Bk1si7sq8h2+yVEDrFJiz3d7Aw+pfjjJSZVaD+Taky4=
|
github.com/cloudflare/cfssl v1.6.5/go.mod h1:Bk1si7sq8h2+yVEDrFJiz3d7Aw+pfjjJSZVaD+Taky4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -12,28 +16,48 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt
|
|||||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/google/certificate-transparency-go v1.1.7 h1:IASD+NtgSTJLPdzkthwvAG1ZVbF2WtFg4IvoA68XGSw=
|
github.com/google/certificate-transparency-go v1.1.7 h1:IASD+NtgSTJLPdzkthwvAG1ZVbF2WtFg4IvoA68XGSw=
|
||||||
github.com/google/certificate-transparency-go v1.1.7/go.mod h1:FSSBo8fyMVgqptbfF6j5p/XNdgQftAhSmXcIxV9iphE=
|
github.com/google/certificate-transparency-go v1.1.7/go.mod h1:FSSBo8fyMVgqptbfF6j5p/XNdgQftAhSmXcIxV9iphE=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
|
github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
|
||||||
github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=
|
github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=
|
||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 h1:veS9QfglfvqAw2e+eeNT/SbGySq8ajECXJ9e4fPoLhY=
|
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 h1:veS9QfglfvqAw2e+eeNT/SbGySq8ajECXJ9e4fPoLhY=
|
||||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||||
|
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||||
|
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||||
|
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||||
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
|
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||||
|
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||||
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
34
internal/metrics/metrics.go
Normal file
34
internal/metrics/metrics.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
labelPath = "path"
|
||||||
|
labelStatus = "status"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
totalRequests = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||||
|
Name: "http_requests_total",
|
||||||
|
Help: "Number of get requests.",
|
||||||
|
}, []string{labelPath})
|
||||||
|
|
||||||
|
responseStatus = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||||
|
Name: "response_status",
|
||||||
|
Help: "Status of HTTP response",
|
||||||
|
}, []string{labelPath, labelStatus})
|
||||||
|
|
||||||
|
httpDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||||
|
Name: "http_response_time_seconds",
|
||||||
|
Help: "Duration of HTTP requests.",
|
||||||
|
Buckets: prometheus.DefBuckets,
|
||||||
|
}, []string{labelPath})
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
prometheus.MustRegister(totalRequests)
|
||||||
|
prometheus.MustRegister(responseStatus)
|
||||||
|
prometheus.MustRegister(httpDuration)
|
||||||
|
}
|
||||||
35
internal/metrics/middleware.go
Normal file
35
internal/metrics/middleware.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Middleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
path := r.URL.Path
|
||||||
|
|
||||||
|
timer := prometheus.NewTimer(httpDuration.With(prometheus.Labels{
|
||||||
|
labelPath: path,
|
||||||
|
}))
|
||||||
|
rw := newResponseWriter(w)
|
||||||
|
next.ServeHTTP(rw, r)
|
||||||
|
if rw.statusCode == 0 {
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
statusCode := rw.statusCode
|
||||||
|
|
||||||
|
responseStatus.With(prometheus.Labels{
|
||||||
|
labelPath: path,
|
||||||
|
labelStatus: strconv.Itoa(statusCode),
|
||||||
|
}).Inc()
|
||||||
|
totalRequests.With(prometheus.Labels{
|
||||||
|
labelPath: path,
|
||||||
|
}).Inc()
|
||||||
|
|
||||||
|
log.Printf("%s %s %s %d %s", r.RemoteAddr, r.Method, r.URL.Path, statusCode, timer.ObserveDuration())
|
||||||
|
})
|
||||||
|
}
|
||||||
19
internal/metrics/response_writer.go
Normal file
19
internal/metrics/response_writer.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func newResponseWriter(w http.ResponseWriter) *responseWriter {
|
||||||
|
return &responseWriter{w, http.StatusOK}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) WriteHeader(code int) {
|
||||||
|
rw.statusCode = code
|
||||||
|
if code != http.StatusOK {
|
||||||
|
rw.ResponseWriter.WriteHeader(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
statusCode int
|
||||||
|
}
|
||||||
@@ -4,11 +4,8 @@ import (
|
|||||||
"crypto"
|
"crypto"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/ocsp"
|
"golang.org/x/crypto/ocsp"
|
||||||
@@ -29,21 +26,8 @@ func NewCrlSource(caCertificate *x509.Certificate, responderKeyPair tls.Certific
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (source *CrlSource) LoadCrlFromFile(path string) error {
|
func (source *CrlSource) UseCrl(crl *x509.RevocationList) {
|
||||||
crlContent, openCrlError := os.ReadFile(path)
|
|
||||||
if openCrlError != nil {
|
|
||||||
return openCrlError
|
|
||||||
}
|
|
||||||
block, rest := pem.Decode(crlContent)
|
|
||||||
if len(rest) > 0 {
|
|
||||||
return fmt.Errorf("failed to decode crl")
|
|
||||||
}
|
|
||||||
crl, parseCrlError := x509.ParseRevocationList(block.Bytes)
|
|
||||||
if parseCrlError != nil {
|
|
||||||
return parseCrlError
|
|
||||||
}
|
|
||||||
source.crl = crl
|
source.crl = crl
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (source *CrlSource) Response(request *ocsp.Request) ([]byte, http.Header, error) {
|
func (source *CrlSource) Response(request *ocsp.Request) ([]byte, http.Header, error) {
|
||||||
|
|||||||
84
main.go
84
main.go
@@ -4,17 +4,36 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"ocspcrl/internal/metrics"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/alecthomas/kingpin/v2"
|
"github.com/alecthomas/kingpin/v2"
|
||||||
cfocsp "github.com/cloudflare/cfssl/ocsp"
|
cfocsp "github.com/cloudflare/cfssl/ocsp"
|
||||||
|
|
||||||
"ocspcrl/internal/ocsp_source"
|
"ocspcrl/internal/ocsp_source"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func loadCrlFromFile(path string) (*x509.RevocationList, error) {
|
||||||
|
crlContent, openCrlError := os.ReadFile(path)
|
||||||
|
if openCrlError != nil {
|
||||||
|
return nil, openCrlError
|
||||||
|
}
|
||||||
|
block, rest := pem.Decode(crlContent)
|
||||||
|
if len(rest) > 0 {
|
||||||
|
return nil, fmt.Errorf("failed to decode crl")
|
||||||
|
}
|
||||||
|
crl, parseCrlError := x509.ParseRevocationList(block.Bytes)
|
||||||
|
if parseCrlError != nil {
|
||||||
|
return nil, parseCrlError
|
||||||
|
}
|
||||||
|
return crl, nil
|
||||||
|
}
|
||||||
|
|
||||||
type responder struct {
|
type responder struct {
|
||||||
certificatePath string
|
certificatePath string
|
||||||
keyPath string
|
keyPath string
|
||||||
@@ -24,24 +43,19 @@ type crlSourceFile struct {
|
|||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
type addresses struct {
|
|
||||||
ocsp string
|
|
||||||
crl string
|
|
||||||
}
|
|
||||||
|
|
||||||
type configuration struct {
|
type configuration struct {
|
||||||
responder *responder
|
responder *responder
|
||||||
caCrtPath string
|
caCrtPath string
|
||||||
crlSourceType string
|
crlSourceType string
|
||||||
crlSourceFile *crlSourceFile
|
crlSourceFile *crlSourceFile
|
||||||
addresses *addresses
|
applicationListenAddress string
|
||||||
|
metricsListenAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config := &configuration{
|
config := &configuration{
|
||||||
responder: &responder{},
|
responder: &responder{},
|
||||||
crlSourceFile: &crlSourceFile{},
|
crlSourceFile: &crlSourceFile{},
|
||||||
addresses: &addresses{},
|
|
||||||
}
|
}
|
||||||
app := kingpin.New("ocspcrl", "OCSP responder / CRL server")
|
app := kingpin.New("ocspcrl", "OCSP responder / CRL server")
|
||||||
app.HelpFlag.Short('h')
|
app.HelpFlag.Short('h')
|
||||||
@@ -50,42 +64,68 @@ func main() {
|
|||||||
app.Flag("ca-crt-path", "Path to the CA certificate").Envar("CA_CRL_PATH").Required().ExistingFileVar(&config.caCrtPath)
|
app.Flag("ca-crt-path", "Path to the CA certificate").Envar("CA_CRL_PATH").Required().ExistingFileVar(&config.caCrtPath)
|
||||||
app.Flag("crl-source-type", "Type of CRL source").Envar("CRL_SOURCE").Default("file").EnumVar(&config.crlSourceType, "file")
|
app.Flag("crl-source-type", "Type of CRL source").Envar("CRL_SOURCE").Default("file").EnumVar(&config.crlSourceType, "file")
|
||||||
app.Flag("source.file.path", "Path to the CRL file").Envar("SOURCE_FILE_PATH").ExistingFileVar(&config.crlSourceFile.path)
|
app.Flag("source.file.path", "Path to the CRL file").Envar("SOURCE_FILE_PATH").ExistingFileVar(&config.crlSourceFile.path)
|
||||||
app.Flag("ocsp.listen-address", "Address for ocsp endpoint").Envar("OCSP_LISTEN_ADDRESS").Default(":8080").StringVar(&config.addresses.ocsp)
|
app.Flag("web.listen-address", "Address for application endpoint").Envar("WEB_LISTEN_ADDRESS").Default(":8080").StringVar(&config.applicationListenAddress)
|
||||||
app.Flag("crl.listen-address", "Address for crl endpoint").Envar("CRL_LISTEN_ADDRESS").Default(":8081").StringVar(&config.addresses.crl)
|
app.Flag("metrics.listen-address", "Address for metrics endpoint").Envar("METRICS_LISTEN_ADDRESS").Default("[::1]:8081").StringVar(&config.metricsListenAddress)
|
||||||
kingpin.MustParse(app.Parse(os.Args[1:]))
|
kingpin.MustParse(app.Parse(os.Args[1:]))
|
||||||
|
|
||||||
responderKeyPair, loadResponderKeyPairError := tls.LoadX509KeyPair(config.responder.certificatePath, config.responder.keyPath)
|
responderKeyPair, loadResponderKeyPairError := tls.LoadX509KeyPair(config.responder.certificatePath, config.responder.keyPath)
|
||||||
if loadResponderKeyPairError != nil {
|
if loadResponderKeyPairError != nil {
|
||||||
panic(loadResponderKeyPairError)
|
log.Fatalf("failed to load responder key pair: %v", loadResponderKeyPairError)
|
||||||
}
|
}
|
||||||
|
|
||||||
caCrtContent, openCaCrtError := os.ReadFile(config.caCrtPath)
|
caCrtContent, openCaCrtError := os.ReadFile(config.caCrtPath)
|
||||||
if openCaCrtError != nil {
|
if openCaCrtError != nil {
|
||||||
panic(openCaCrtError)
|
log.Fatalf("failed to open ca certificate: %v", openCaCrtError)
|
||||||
}
|
}
|
||||||
block, rest := pem.Decode(caCrtContent)
|
block, rest := pem.Decode(caCrtContent)
|
||||||
if len(rest) > 0 {
|
if len(rest) > 0 {
|
||||||
panic("failed to decode ca certificate")
|
log.Fatalln("failed to decode ca certificate")
|
||||||
}
|
}
|
||||||
caCertificate, loadCaCertificateError := x509.ParseCertificate(block.Bytes)
|
caCertificate, loadCaCertificateError := x509.ParseCertificate(block.Bytes)
|
||||||
if loadCaCertificateError != nil {
|
if loadCaCertificateError != nil {
|
||||||
panic(loadCaCertificateError)
|
log.Fatalf("failed to parse ca certificate: %v", loadCaCertificateError)
|
||||||
}
|
}
|
||||||
|
|
||||||
source := ocsp_source.NewCrlSource(caCertificate, responderKeyPair)
|
source := ocsp_source.NewCrlSource(caCertificate, responderKeyPair)
|
||||||
loadCrlError := source.LoadCrlFromFile(config.crlSourceFile.path)
|
crl, loadCrlError := loadCrlFromFile(config.crlSourceFile.path)
|
||||||
if loadCrlError != nil {
|
if loadCrlError != nil {
|
||||||
panic(loadCrlError)
|
log.Fatalf("failed to load crl: %v", loadCrlError)
|
||||||
}
|
}
|
||||||
|
source.UseCrl(crl)
|
||||||
|
|
||||||
signalChan := make(chan os.Signal, 1)
|
signalChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
responder := cfocsp.NewResponder(source, nil)
|
applicationRouter := http.NewServeMux()
|
||||||
listenError := http.ListenAndServe(config.addresses.ocsp, responder)
|
applicationRouter.Handle("/ocsp", cfocsp.NewResponder(source, nil))
|
||||||
if listenError != nil {
|
applicationRouter.HandleFunc("/crl", func(w http.ResponseWriter, r *http.Request) {
|
||||||
panic(listenError)
|
w.Header().Set("Content-Type", "application/pkix-crl")
|
||||||
}
|
w.Write(crl.Raw)
|
||||||
|
})
|
||||||
|
|
||||||
// TODO: Implement CRL server
|
applicationServer := &http.Server{Addr: config.applicationListenAddress, Handler: metrics.Middleware(applicationRouter)}
|
||||||
|
metricsSever := &http.Server{Addr: config.metricsListenAddress, Handler: promhttp.Handler()}
|
||||||
|
|
||||||
|
applicationServerClosed := make(chan any)
|
||||||
|
metricsServerClosed := make(chan any)
|
||||||
|
go func() {
|
||||||
|
log.Printf("starting application server on %s", config.applicationListenAddress)
|
||||||
|
if listenError := applicationServer.ListenAndServe(); listenError != nil {
|
||||||
|
log.Printf("application error: %v", listenError)
|
||||||
|
}
|
||||||
|
close(applicationServerClosed)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
log.Printf("starting metrics server on %s", config.metricsListenAddress)
|
||||||
|
if listenError := metricsSever.ListenAndServe(); listenError != nil {
|
||||||
|
log.Printf("metrics error: %v", listenError)
|
||||||
|
}
|
||||||
|
close(metricsServerClosed)
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-signalChan
|
||||||
|
applicationServer.Shutdown(nil)
|
||||||
|
metricsSever.Shutdown(nil)
|
||||||
|
<-applicationServerClosed
|
||||||
|
<-metricsServerClosed
|
||||||
}
|
}
|
||||||
|
|||||||
16
test.sh
16
test.sh
@@ -1 +1,15 @@
|
|||||||
openssl ocsp -CAfile ../../ca/ca.crt -url http://127.0.0.1:8080 -issuer ../../ca/ca.crt -resp_text -cert ../../test.crt
|
#!/usr/bin/env bash
|
||||||
|
set -xeou pipefail
|
||||||
|
|
||||||
|
# go run main.go --responder.certificate-path ../tinypki/ca/ca.crt --responder.key-path ../tinypki/ca/ca.key --ca-crt-path ../tinypki/ca/ca.crt --source.file.path ../tinypki/root.crl
|
||||||
|
|
||||||
|
ca_dir="$(dirname $(readlink -f $0))/../tinypki"
|
||||||
|
ocsp_url="$(openssl x509 -noout -ocsp_uri -in $ca_dir/dev-server.crt)"
|
||||||
|
openssl ocsp \
|
||||||
|
-CAfile $ca_dir/ca/ca.crt \
|
||||||
|
-url "$ocsp_url" \
|
||||||
|
-issuer $ca_dir/ca/ca.crt \
|
||||||
|
-resp_text \
|
||||||
|
-cert $ca_dir/dev-server.crt
|
||||||
|
|
||||||
|
openssl verify -crl_check -crl_download -CAfile $ca_dir/ca/ca.crt $ca_dir/dev-server.crt
|
||||||
Reference in New Issue
Block a user