Skip to content

Commit a9ab81b

Browse files
Implements HTTPS graceful shutdown (zalando#1866)
Fixes zalando#1865 Signed-off-by: Alexander Yastrebov <[email protected]>
1 parent 12ff2de commit a9ab81b

File tree

5 files changed

+209
-164
lines changed

5 files changed

+209
-164
lines changed

fixtures/gencert.sh

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
if [[ $# -eq 0 ]] ; then
4+
echo 'Certificate name required'
5+
exit 1
6+
fi
7+
8+
openssl req -x509 -sha256 -nodes -newkey rsa:2048 -keyout "$1.key" -out "$1.crt" \
9+
-days 3650 \
10+
-subj "/C=DE/ST=Berlin/O=Zalando SE/OU=Technology/CN=do-not-trust-test-data"

fixtures/test2.crt

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDszCCApugAwIBAgIUfXes2jmw6GNLTEObvIsMrlx4NX0wDQYJKoZIhvcNAQEL
3+
BQAwaTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJlcmxpbjETMBEGA1UECgwKWmFs
4+
YW5kbyBTRTETMBEGA1UECwwKVGVjaG5vbG9neTEfMB0GA1UEAwwWZG8tbm90LXRy
5+
dXN0LXRlc3QtZGF0YTAeFw0yMTA5MjQxMTQ1MjBaFw0zMTA5MjIxMTQ1MjBaMGkx
6+
CzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xEzARBgNVBAoMClphbGFuZG8g
7+
U0UxEzARBgNVBAsMClRlY2hub2xvZ3kxHzAdBgNVBAMMFmRvLW5vdC10cnVzdC10
8+
ZXN0LWRhdGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLvAr/DUEe
9+
2T+TzPjoT/Q32892rNXz3p/M4RopdqJWLEZwzASh8Z7FiLBiGOIsWrGhWluwqQCy
10+
nrpulZ/sQOqi8KYjyhsvlRV68L9wczX7GZScxnMCfvsEAJJD0E9GqwtOal+yCmvx
11+
59/5V7sLD6JzrPZWoUL4qEEXk1CoGCjSHGjHUPvVqX1oZmyzYKh73oiUKQ//DqYR
12+
9zhUaGO01R1QvNISMqFMsdYLW9SsatuRiyriAfTsslbIg3jQ+l/n7Y7YztJvbrbe
13+
gB6cMqrignXskNtlqZnEGbJgiX8Ko7m4Y7ttmOMcawLqhwWxzvKj9iBERL6irxqr
14+
Q15mpV915O4vAgMBAAGjUzBRMB0GA1UdDgQWBBSJv18vO9LQKJsCkTiIZ0vhI2xb
15+
KjAfBgNVHSMEGDAWgBSJv18vO9LQKJsCkTiIZ0vhI2xbKjAPBgNVHRMBAf8EBTAD
16+
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQC0o9QFpmTe0YC5sfRhJcjmejGggS4IZB5U
17+
KQ6/S+Xk/W4UitQCo7iQot+ZF5HOmaGX6FtpUCSx1JeSWxAkzbl8LK1dCO6YNXJ6
18+
2EzkPthcf5EJkJ9rpCRL5dAoPSXr1EJut646jlR/hwlxOEWCOr7zYQK136hc+HFA
19+
6No4tshp2l4mT+OfR3wn8Ae1hH+tyuWzXGlBdUlm/2OtsUwHjHKeiv/ZnQ63K+gg
20+
nxdkUvQL07O0Avz9ttz9yZ9jYAi00zjotG7e+buSbarGBTuXXxnlG4+ISclQikJ4
21+
m/ApwEDTocYpOJgsdn+bysY/F2nntrAQ1Sof2E6N5ww9sR8taGe3
22+
-----END CERTIFICATE-----

fixtures/test2.key

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLvAr/DUEe2T+T
3+
zPjoT/Q32892rNXz3p/M4RopdqJWLEZwzASh8Z7FiLBiGOIsWrGhWluwqQCynrpu
4+
lZ/sQOqi8KYjyhsvlRV68L9wczX7GZScxnMCfvsEAJJD0E9GqwtOal+yCmvx59/5
5+
V7sLD6JzrPZWoUL4qEEXk1CoGCjSHGjHUPvVqX1oZmyzYKh73oiUKQ//DqYR9zhU
6+
aGO01R1QvNISMqFMsdYLW9SsatuRiyriAfTsslbIg3jQ+l/n7Y7YztJvbrbegB6c
7+
MqrignXskNtlqZnEGbJgiX8Ko7m4Y7ttmOMcawLqhwWxzvKj9iBERL6irxqrQ15m
8+
pV915O4vAgMBAAECggEATQPKjFuwUD8Dn5WOShNfWHZJWK1BO6zeb45wW1gzSav2
9+
/NDCt40k3bssIgkSBn5KQ5pqqr9YOi1ygDcjeyWXDP03cLQHztbmhdDYLWP/9enX
10+
meQSudDShtLId8YZEbe60Gu5vQ3ffFSRACq/1BCW8m9ht6HCNUk1Qfo4NTLcy3+x
11+
F8gI65jGTPiZzJ7eHz7576KBcgeArv14DSm+jPjkBiKWzrUb9sV8elaps5j6yIjL
12+
glMimWhlTuajt/9d8QD/qN+uR67OYcWtflHnRI4FSRrwyPfN80tK3a9WjpuBKlFZ
13+
MwLaNU3gCw2x9VW4xVOKyFhuQzj7uHUn+vUCWjr5UQKBgQDm5XXUzRu5dRx6n5sR
14+
P3TrIs8UocKOuaNRgp3+aYjhF578szGP9lLG/he58w7irTYEZxvdwCRSE7HADwQv
15+
GUZmyyVtwSuO4yE0xVOVtf02SRJnn7akAWy/CL4V87RbpFGYDXdNtN/0600VaYxO
16+
UxKjw/K9yeCoAZv9Q+fPXKlO7QKBgQDh4paieu1ZvwNcRX1FFYwtq5YHTelL4AD/
17+
CNQXZLlIp2i4Z0rcrfaIovb5sh5sJ+WXH5YJ0VIzgNe6tuTKMU6Cc45UF4Hb0ZTx
18+
sVjAqZp+6KnajOHBdkc0w5c0Hv6UpAKPdZUzsKlb5r0kT4nRPB1/Ov3CUAqJi/JQ
19+
FZtYgz1yCwKBgAxXcX/pYrT8BIStaU13tdknqCfzKYIVfBxMPgOuQmm9qHrbXSfT
20+
w8LtK/l9e2s0VPHRTRUCQy677MFWTCP0VuYBr8N5Esn1a/31Gi2jZ6ByMXCmgc2s
21+
YdKoNfjYaOiJFO9qsNjPdTUTKrCdTqmVGSb1v1DTrJVuWJcl/QsBae9VAoGAbLHM
22+
KoNck0MHKu+FSCkGOzPGDd2/1XMFB7QH2vns7rkf+xw5Ode8OiOxFJZRbVoFcKMS
23+
X8cJ9x6YsJAxp9nyHXPdmTl2k4BWW7crLgpu/YKXuULxn1Z7DTjRGZOQjZYeZUn/
24+
cdAgrshpW3+qobR7vS11znsVlvpwr3i2N/FvL+ECgYEAwcvBK5wtd70YweHZ4Rm8
25+
P8iUsjeVV9LuBT0/0jL46Q48rGxMnHK+hUxXPCp/wl4BrML/lVsTvPZh+KlYQz4C
26+
yk/f2FB2aC8dCFXcsGswVXC0WQGStogkXf56nr05b8Av0LoQ4/REG0NDSoYaApVF
27+
U1EucR/02DUrc+LE0j44D9o=
28+
-----END PRIVATE KEY-----

skipper.go

+52-40
Original file line numberDiff line numberDiff line change
@@ -944,8 +944,35 @@ func initLog(o Options) error {
944944
return nil
945945
}
946946

947-
func (o *Options) isHTTPS() bool {
948-
return (o.ProxyTLS != nil) || (o.CertPathTLS != "" && o.KeyPathTLS != "")
947+
func (o *Options) tlsConfig() (*tls.Config, error) {
948+
if o.ProxyTLS != nil {
949+
return o.ProxyTLS, nil
950+
}
951+
952+
if o.CertPathTLS == "" && o.KeyPathTLS == "" {
953+
return nil, nil
954+
}
955+
956+
crts := strings.Split(o.CertPathTLS, ",")
957+
keys := strings.Split(o.KeyPathTLS, ",")
958+
959+
if len(crts) != len(keys) {
960+
return nil, fmt.Errorf("number of certificates does not match number of keys")
961+
}
962+
963+
config := &tls.Config{
964+
MinVersion: o.TLSMinVersion,
965+
}
966+
967+
for i := 0; i < len(crts); i++ {
968+
crt, key := crts[i], keys[i]
969+
keypair, err := tls.LoadX509KeyPair(crt, key)
970+
if err != nil {
971+
return nil, fmt.Errorf("failed to load X509 keypair from %s and %s: %w", crt, key, err)
972+
}
973+
config.Certificates = append(config.Certificates, keypair)
974+
}
975+
return config, nil
949976
}
950977

951978
func listen(o *Options, mtr metrics.Metrics) (net.Listener, error) {
@@ -1005,11 +1032,14 @@ func listenAndServeQuit(
10051032
idleConnsCH chan struct{},
10061033
mtr metrics.Metrics,
10071034
) error {
1008-
// create the access log handler
1009-
log.Infof("proxy listener on %v", o.Address)
1035+
tlsConfig, err := o.tlsConfig()
1036+
if err != nil {
1037+
return err
1038+
}
10101039

10111040
srv := &http.Server{
10121041
Addr: o.Address,
1042+
TLSConfig: tlsConfig,
10131043
Handler: proxy,
10141044
ReadTimeout: o.ReadTimeoutServer,
10151045
ReadHeaderTimeout: o.ReadHeaderTimeoutServer,
@@ -1025,35 +1055,6 @@ func listenAndServeQuit(
10251055
}
10261056
}
10271057

1028-
if o.isHTTPS() {
1029-
if o.ProxyTLS != nil {
1030-
srv.TLSConfig = o.ProxyTLS
1031-
o.CertPathTLS = ""
1032-
o.KeyPathTLS = ""
1033-
} else if strings.Index(o.CertPathTLS, ",") > 0 && strings.Index(o.KeyPathTLS, ",") > 0 {
1034-
tlsCfg := &tls.Config{
1035-
MinVersion: o.TLSMinVersion,
1036-
}
1037-
crts := strings.Split(o.CertPathTLS, ",")
1038-
keys := strings.Split(o.KeyPathTLS, ",")
1039-
if len(crts) != len(keys) {
1040-
log.Fatalf("number of certs does not match number of keys")
1041-
}
1042-
for i, crt := range crts {
1043-
kp, err := tls.LoadX509KeyPair(crt, keys[i])
1044-
if err != nil {
1045-
log.Fatalf("Failed to load X509 keypair from %s/%s: %v", crt, keys[i], err)
1046-
}
1047-
tlsCfg.Certificates = append(tlsCfg.Certificates, kp)
1048-
}
1049-
o.CertPathTLS = ""
1050-
o.KeyPathTLS = ""
1051-
srv.TLSConfig = tlsCfg
1052-
}
1053-
return srv.ListenAndServeTLS(o.CertPathTLS, o.KeyPathTLS)
1054-
}
1055-
log.Infof("TLS settings not found, defaulting to HTTP")
1056-
10571058
// making idleConnsCH and sigs optional parameters is required to be able to tear down a server
10581059
// from the tests
10591060
if idleConnsCH == nil {
@@ -1079,14 +1080,25 @@ func listenAndServeQuit(
10791080
close(idleConnsCH)
10801081
}()
10811082

1082-
l, err := listen(o, mtr)
1083-
if err != nil {
1084-
return err
1085-
}
1083+
log.Infof("proxy listener on %v", o.Address)
10861084

1087-
if err := srv.Serve(l); err != nil && err != http.ErrServerClosed {
1088-
log.Errorf("Failed to start to ListenAndServe: %v", err)
1089-
return err
1085+
if srv.TLSConfig != nil {
1086+
if err := srv.ListenAndServeTLS("", ""); err != http.ErrServerClosed {
1087+
log.Errorf("ListenAndServeTLS failed: %v", err)
1088+
return err
1089+
}
1090+
} else {
1091+
log.Infof("TLS settings not found, defaulting to HTTP")
1092+
1093+
l, err := listen(o, mtr)
1094+
if err != nil {
1095+
return err
1096+
}
1097+
1098+
if err := srv.Serve(l); err != http.ErrServerClosed {
1099+
log.Errorf("Serve failed: %v", err)
1100+
return err
1101+
}
10901102
}
10911103

10921104
<-idleConnsCH

0 commit comments

Comments
 (0)