mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
apiserver: add pkg/util/webhook tests
This commit adds tests for pkg/util/webhooks. The purpose of this was not only for better code coverage but also to alleviate the need for consumers to write their own tests for core functionality.
This commit is contained in:
parent
6236dfb594
commit
d15dba7e8b
@ -5,6 +5,7 @@ licenses(["notice"])
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
@ -22,3 +23,20 @@ go_library(
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"certs_test.go",
|
||||
"webhook_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/pkg/api:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
214
staging/src/k8s.io/apiserver/pkg/util/webhook/certs_test.go
Normal file
214
staging/src/k8s.io/apiserver/pkg/util/webhook/certs_test.go
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// This file was generated using openssl by the gencerts.sh script
|
||||
// and holds raw certificates for the webhook tests.
|
||||
|
||||
package webhook
|
||||
|
||||
var caKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwmdGuDsPPyirvNqlqoDmwf/bmF3zTJBFYQRsJK0vKBAdrfMY
|
||||
MVwoPEpM5ZZ+VCHEB6vSuGYbC0PyO97H/kIV22FsMwN7zifhJP2T2Hb+B7Nc6s8W
|
||||
tAQn1J4xUY31xOXOEcXe2nuGezrlKfX3DpA1FVXp6/Cf8vhyUQ0fJXwuZE/Pbhvp
|
||||
bBUciQLqfPSH6EnShZvzjJBP5Bs13UqzaRobhUf9A3pyk2Mb3PXkTetET7hLc4J2
|
||||
uIp5BxOoNZSvgQCvCjRyV5s1his7BNRALKG3qz/48i6JRyO7FvNoxDkWC09zIqD8
|
||||
1bw9I1d/+EwWdqAPZLa8iLpSsd0gD03gDSFbOwIDAQABAoIBAQC0JPO5oLDePBf4
|
||||
pzxBJbWwLCIXrWfZmQ9RecGksv8xxs1Z9hyDEP0P8WIUlkJ2P9vhp+1ahvOkmtAL
|
||||
fsQg7qhGZJ7ZHu9I+Fd/6aNpQcrg4+rEhCZrpjYqpnTZOA146eLtQUjjePgDlW3q
|
||||
Vk0cJ7GpFbXwt0fg5S05wkkMeWib9mKvme3vooNbog52164U1wo/p4WBTpbAMoYA
|
||||
XlJSqXeoxBpsLWBZHlRG+AYfYpk7BXk8MkIslcKh97RmLsZt52Fh3SbsFJeIEmD5
|
||||
2hQDvn/PJojAnM6SMkUqfvv87SdkryvqQYJ80b2D6qd+y8o7gUFr8WkEqVRCqVLh
|
||||
GaD2C06hAoGBAO9JOe+typoQUPi24aj5BoqWcpyrHQkCdjLlxS805oLsPfmb+EqF
|
||||
1HwnA8UHNMlrdiczJ8f2M7Y4cSUIEXv6LSE5r4teSiYWASidDLREi0q8scw21CGH
|
||||
BnCc7PUhUnBngXJ3B1MtCj+r3TFfpOEEi1J1HtMK1AxAaq7zEFzdOrtjAoGBAM/7
|
||||
fC89Awvd7yJsgTVumKVx/bA+Q54YJOFMkdba3JbcLsQyn4TBaFv0pwqqXqmkTLZz
|
||||
WHjkNscomRf9VY34D4q07nO4YGXCBNqm3MaV3mE0xhIyBsATRZnf03O2a/pnRPu/
|
||||
yTE1EyuIqK/l4+5iv2O5mWzxorC4qdV34Wf5WCRJAoGBANfmfjvf1zoDFswSVrGb
|
||||
X2eUL31kdyI18mgiITRiysm+VnztWa4D6qDKowAXbG2AZG8iHPazEh2L96quCPiP
|
||||
1kBwSA+717Ndj1YRvfC5F+UrNFFJ90T5C7p4HOVgV33MJmQdOaK2tNSWQVHXNnFB
|
||||
JGQWAOXykzkqthd8gHsJsYB5AoGAd7BfKAQxg5vFqYbN2MT7vYJbHxjF6u40Ex/w
|
||||
cbfj6EFv/GKxoEF5YCnsE1w2O+QcbYb1rCSRTY2UhNS6bogJ0aYL77Z0azr7diU+
|
||||
ul226zPmpMP7VIACtumzE00w2JqjfUlCbDoB/TSY9xkSUbasM6S0oZhxKsgqnHlv
|
||||
01kQG1kCgYBPZfZiqKwnnsOSRy8UBN4Oo5zg9QrbMki472/s4FhHaunXF0pFmIUG
|
||||
QU/9kYteJ8DlCppvvtU5C3qmEkW6c2u8KAfJXRmA51uS6v36kEx/8313ZJ5afwLU
|
||||
i2ZMmS8OabHjIhdnCSA2N7geqaAZa7BCLqt8735Doys1p0KB0y+ZNw==
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var caCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDNzCCAh+gAwIBAgIJAM1Z9H27fWCyMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjUzWhgPMjI5MDExMjcw
|
||||
MDIyNTNaMBsxGTAXBgNVBAMUEHdlYmhvb2tfdGVzdHNfY2EwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDCZ0a4Ow8/KKu82qWqgObB/9uYXfNMkEVhBGwk
|
||||
rS8oEB2t8xgxXCg8Skzlln5UIcQHq9K4ZhsLQ/I73sf+QhXbYWwzA3vOJ+Ek/ZPY
|
||||
dv4Hs1zqzxa0BCfUnjFRjfXE5c4Rxd7ae4Z7OuUp9fcOkDUVVenr8J/y+HJRDR8l
|
||||
fC5kT89uG+lsFRyJAup89IfoSdKFm/OMkE/kGzXdSrNpGhuFR/0DenKTYxvc9eRN
|
||||
60RPuEtzgna4inkHE6g1lK+BAK8KNHJXmzWGKzsE1EAsoberP/jyLolHI7sW82jE
|
||||
ORYLT3MioPzVvD0jV3/4TBZ2oA9ktryIulKx3SAPTeANIVs7AgMBAAGjfDB6MB0G
|
||||
A1UdDgQWBBS0/gwwXmdxIe8o+a7WKbdiTYPy+TBLBgNVHSMERDBCgBS0/gwwXmdx
|
||||
Ie8o+a7WKbdiTYPy+aEfpB0wGzEZMBcGA1UEAxQQd2ViaG9va190ZXN0c19jYYIJ
|
||||
AM1Z9H27fWCyMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHGUdTCN
|
||||
rm0Mx6V5SmSIGbOB/+3QMqE1ZBIWwPsVFKHhROLaouELZFO+QysfLufB8SS54aM6
|
||||
ewufjfSz4KL26DnoSwOFirPxpG0+Sdry55lCjmZ50KtENZDu6g288Qx9GBzqgVHz
|
||||
kGi/eciV4fZ4HYIhZY+oR29n3YYQOID4UqbQ86lSoN781dmsEQLL+TEK4mJJFcNg
|
||||
SKHM526WdwJ15zqpKNlcqXtTyx3UfBFlNwvrxHNFbth1vOfdTW8zAs9Xzcr5vSm2
|
||||
G8nJ3FF/UF4dYpjDzggO3ALZZqUJHnl/XusETo5kYY3Ozp0xQYg2beR8irElqP8f
|
||||
oNcE4Ycfe10Hmec=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var badCAKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAxrCZsZHIeKuYSUYw4qKB8P+HeU81VP7F0tVbLOffj587sfDu
|
||||
N2QsOsnmiWKS9HiNVUs5ap/Zg3rekbnZV5eBeS5qOPC95c8Oyac7A2HNYSRp08kQ
|
||||
dUUo3ueDqwmZ5dNiDp0SriQCWfPYxiWHdWSacqDxxzXPFWCs6z3vwFYV35VxDFhr
|
||||
M8gPWgSqjWdqj+p5cHrL88me7vWWsj6ceL/i2KWo8lmuHuhUzggn0ekU/aKsavHi
|
||||
X/MNVd5tPzGIR+B49aywQZ9KyrJ7V8SdFqKYEGfH6RaiDhaNNUOv9E9668PdupnG
|
||||
qei0260zB8SDY4j6VKPGOS90YBA66qOP6J11rwIDAQABAoIBAQCsgs0PNep/i01v
|
||||
4Xe0bzCvVM4Fb9Z4c7TDN+gv9ytOggzMlMngYiNc78wwYNwDU2AzPFsfzqaG1/nD
|
||||
QUAKI0uRMdGcmrnmfH70azR73UD7JSiVb6/QgjnYP988c9uhhoVO9uYvOKip/WSr
|
||||
tg4EyVKoUEFcm8WvY/7/SQmPT68yLf5VpbtuCysAkSLPUjcBer4A6eWwlZ9PWrtj
|
||||
rLjUCGXXDKRgMmQRAwL+tpBgMwb1+euriv4+M6ddZCkcyaohW076qA3aaq3+XtAB
|
||||
RTGQubWshuri3N1WRQcn1ZvGURLCAhI8q+9i/wXADKrAlL6imDuYzTW+LMQdZLuH
|
||||
bwHvq/yRAoGBAP3beuc2R/jjkDttFsSce2dMx6F8AKPpmdbzVw7/DhflzNRDM/Yo
|
||||
dfVOabRLqcAyfhNm2L6CdUaIJuHRKyRJT3X5wgxUapAjXFUE0kH+qnaq3BxZCCjU
|
||||
fwDUZ4SUVDAuyaMo5OfVbqkI/L3rvSSgklNOnSkXMPtftDkz8pVljLo9AoGBAMhd
|
||||
6uiddCt3Dpt75C1BDRX0xGKc4KwtPK0CnQeQmQNXUx192m6IhfPW7YUoKvIZibWB
|
||||
f9NNJ/KCxDGG+QP7X+0sWQZMfdp5f1l6EsM6HFPLAOgjQ4PyBVWqxknJyxy6GCnt
|
||||
vI3s6cwMxN7B7QJ/87ffO23elEu7bCdg0lrOAmpbAoGBALN6fI+B6irGwU+ylflV
|
||||
5U2olC/Q2ycIXtMBYpjgrRcqSsH77X3pJ1TTJprpL9AKIucWvMEcvUursUnQt97E
|
||||
0iBH//D1sg3MYlhdu0Ybhmu16z9Dlyg+7LgqdDHhKRCT082+ePCMDtwF1aN1S1nd
|
||||
CPdLSoQluGTRSjtzRdxoWrHFAoGAJqNlz2G9qzwURwuHHuryeQ9wZ4vVD57RmpNs
|
||||
cK8Dss8+KevBGZueKT2DJDBwx6sBEU1dtwOj9nIdH2fl0UzCXNw2dq59fon7cufF
|
||||
gnxMRiRZkmpqdKFRQgninwwY7Ps9+afsunm7RCwaMtK2v8qo1wZnUXKgqlIEMzvK
|
||||
lNQxRw0CgYEA0uT5TkrZ3S+GAbyQtwb6FtXiLoocL+8QyBw6d1+5Fy7D0pYYQDLw
|
||||
TMeR2NOEqQGLgsqCnbuKzKBIuIY8wm0VIzOqRGmk4fWiOGxrtEBrfPl+A21bexyC
|
||||
qv5UBMLcEinZEM3x1e/rloDwKi0IGfyKiRfVpxdVKebs7dJfFYkhmw0=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var badCACert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDNzCCAh+gAwIBAgIJAPbb5w6p8Cw8MA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjUzWhgPMjI5MDExMjcw
|
||||
MDIyNTNaMBsxGTAXBgNVBAMUEHdlYmhvb2tfdGVzdHNfY2EwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDGsJmxkch4q5hJRjDiooHw/4d5TzVU/sXS1Vss
|
||||
59+Pnzux8O43ZCw6yeaJYpL0eI1VSzlqn9mDet6RudlXl4F5Lmo48L3lzw7JpzsD
|
||||
Yc1hJGnTyRB1RSje54OrCZnl02IOnRKuJAJZ89jGJYd1ZJpyoPHHNc8VYKzrPe/A
|
||||
VhXflXEMWGszyA9aBKqNZ2qP6nlwesvzyZ7u9ZayPpx4v+LYpajyWa4e6FTOCCfR
|
||||
6RT9oqxq8eJf8w1V3m0/MYhH4Hj1rLBBn0rKsntXxJ0WopgQZ8fpFqIOFo01Q6/0
|
||||
T3rrw926mcap6LTbrTMHxINjiPpUo8Y5L3RgEDrqo4/onXWvAgMBAAGjfDB6MB0G
|
||||
A1UdDgQWBBTTHlbuK0loVSNNa+TCM0Bt7dLEcTBLBgNVHSMERDBCgBTTHlbuK0lo
|
||||
VSNNa+TCM0Bt7dLEcaEfpB0wGzEZMBcGA1UEAxQQd2ViaG9va190ZXN0c19jYYIJ
|
||||
APbb5w6p8Cw8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBACrsdqfQ
|
||||
7be0HI4+/cz/VwzvbQwjr9O6Ybxqs/eKOprh8RWRtjYeuagvTc6f5jH39W9kZdjs
|
||||
E3ktCG3RJJ/SyooeJlzNhgaAaATnMqEf7GyiQv3ch0B/Mc4TOPJeQ2E/pzFj3snq
|
||||
Edm8Xu9+WLwTTF4j/WlSY1sgVSnFk3Yzl5cn0ip00DVCOsL2sP3JlX9HRG4IrdiX
|
||||
jYXb+nGUPYFultSGSUw+5SiL2yM1ZyHfOBaO1RH8QIRA1/aFTfE+1QRuja5YtCwl
|
||||
ahpWVRhii7GVR3zKEgKFTxjELHm8x3vBC/HAhj5J3433nlRrgvwZXsZYplqp8422
|
||||
IpexMtsutA+y9aE=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA2lF1EXRYzsUIwM1vrMM/OIinPvOzqSQSQ0euiUYgcNTw2Y87
|
||||
2fNWnQXOP3JDHgzZw231kJbdRxeWgRdeTnvCFUaBzA63GXMMCljs1sgAVLXnAmWR
|
||||
sE/TzG/OwsZUnyYzQMdG49PAEpw8GlX9t0eojmXPy7C/M0GnyAMx/UyMrq40lht6
|
||||
gdUI2gYRUofRRtHFs+a0EX8/q+49ciwgQx5inj2BX3Jc2cvc35Y3bSY86CYsAZic
|
||||
PZP84wP5iWkYmvFhJUoS/JY2FMC6CvRFPcTi8Dnp28kHEaqrocmwajSfyiJe/1qJ
|
||||
dMlHAInvTxp9E53cOzfCP6nmHbSKQPxm5u8hSQIDAQABAoIBAQCDhfNbEpa16qn9
|
||||
TUZr9CxQKLNpD3Q6/8oo0jRp6t98WizHRK0v/pM9gdPhETsyDVfbjpEUDG8+dw1q
|
||||
s+NSsOgZ3SIxBuRz5oVobm4wbskUP4nuPbZpW44jaXBMkyNDxcW2ztb8RgM+svTa
|
||||
gNea5Qa80sU+1zo47OLhcltZWBag3KCU/JQT+3LThVZDHt3GRx4QCASTJx3v/vBB
|
||||
o9M5wCYZp6sP7wmFUZfwEpkTfJ5M7sG1h7ibD/8kjIvpnQj+OFpcoylDxTINvqsN
|
||||
ADAe1NPK00Rx6vE9GNQ8ZA/lg0pih+EpK4PpE5cDDkYs3VchUlYHBSrsc7+K6kUk
|
||||
mMTdmVvpAoGBAP7sHhKMEpmPIUqxb5M95l+PX5uOS0x08HM40Vr8mxgx4z849CpW
|
||||
1vcQlZwcXwkxUfJyXZCPx9CK0Sw877Afpac1OL4RiEKZ3qmwLeI9RnRTeKC5qXJ9
|
||||
u31l+dgoSbRZDUdcM1ZwFs9+V8+zId58SifDaBjm3466VCMnD7KQUz4jAoGBANs9
|
||||
udy4Os+SvCYVUaiVvtoYMdcyym+VQzf3ycVAk91dO8Qha/5uFYD/f7ykgEgg7QCd
|
||||
jQp+ZVYPD7Hbh8XNwAt/6T+bF1qe8TSM3K8uk2Wt/tlk1ZqRnNNYsIZ8BO8c4T+f
|
||||
pbu/mCDdmTKWQWVEwCj2kKNBHptmlLO5Ie2nebujAoGBAIqoZccS138dAi+9iYHe
|
||||
VnM96fQTltN0e+FAU2eZJMcpQ4D8+poY9/4U0DvEltDKOdeU612ZR0cgapwUXQ9A
|
||||
d3sWkNGZebM4PIux35NCXxMg3+kUc51p1FRl5lrztvtYwMdC2E241D9yalL4DYEV
|
||||
u8QbHoEE+y6IHQGt2nT22cBfAoGAWmZuT+uLHHIFsLJTtG7ifi1Bx9lCjZX/XIGI
|
||||
qhQBpGJANZQOYp/jsAgqFI/D8XnaH8nXET+i60RUlWLO7inziQpaFAcQLyagkKmQ
|
||||
iY9r6Z5AGkWwqgZmouLMDvfuVOYUntZmUS8kPFEDTU+VcXtSvNFGPHqqcytuH1kz
|
||||
+zl2QX8CgYB9nFMpqARHl0kJp1UXtq9prUei+Lyu2gvrl3a74w96gqg3yx9+fU/n
|
||||
FzGF2VXnC5n1KvCvQi3xjLNzCOXKM3igu7u50CiaA/HEYmyWyOJw2Nt2+ICvxcCH
|
||||
rnsA8P8I/R5Esl0rvv2BbA1Q1O6SLC+Dfnhf7KulWmNgqVXKllj+Ng==
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var serverCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIJAPJbY53f15/vMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjU0WhgPMjI5MDExMjcw
|
||||
MDIyNTRaMB8xHTAbBgNVBAMMFHdlYmhvb2tfdGVzdHNfc2VydmVyMIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lF1EXRYzsUIwM1vrMM/OIinPvOzqSQS
|
||||
Q0euiUYgcNTw2Y872fNWnQXOP3JDHgzZw231kJbdRxeWgRdeTnvCFUaBzA63GXMM
|
||||
Cljs1sgAVLXnAmWRsE/TzG/OwsZUnyYzQMdG49PAEpw8GlX9t0eojmXPy7C/M0Gn
|
||||
yAMx/UyMrq40lht6gdUI2gYRUofRRtHFs+a0EX8/q+49ciwgQx5inj2BX3Jc2cvc
|
||||
35Y3bSY86CYsAZicPZP84wP5iWkYmvFhJUoS/JY2FMC6CvRFPcTi8Dnp28kHEaqr
|
||||
ocmwajSfyiJe/1qJdMlHAInvTxp9E53cOzfCP6nmHbSKQPxm5u8hSQIDAQABo0ow
|
||||
SDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
|
||||
KwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQUFAAOCAQEAX/CG
|
||||
3C1nwCWabpBw6h0k7UlDI55nnTH6xHdSX9EFmHz49NmAym9gUwXK5xDPVDNYURjb
|
||||
TD3R2e76Cov7wXRzw99BMzKOhNrMgjiOrc0WT4Ck5MOaKgjzZEIXRSSBllsrF9ut
|
||||
hnnuSaaKwUVn4D/9vPMp/TuZoK7yZaW3Pyv0ScQfpkECDLKYIkXOlyhC/I5Tfbof
|
||||
+zReStbTsc0EWMVLLIAbP7uPf1VcH5HnElh1ignxRAPBsXwF8jQzjUBTWcZ5dEi9
|
||||
ofIrWo+AVKvcoRlyZZyLjOKPzhA5+pwG4yBkWJB5Cshq2trOYVf3+uUN8lz6i57M
|
||||
wqxS1Q1MmtLhyhy79Q==
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var clientKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAt6auF7X7jl94ilPpJid6k15/Tnt/hFWMRv2eGeuSBT1YMjDP
|
||||
X5pIaseeIVdXl0ePKzPeyYJma4t7EpGhUO7TX12Cr1e5S8IMExNDpYGti3ncLf1O
|
||||
HP+faJNAlbKMTgf2xr0HPg8Q2lsDKfam/OEn+7jqv3FpZ5guwQQy7beQhWV38YuR
|
||||
vf2ChNJVcalN0h+MRNkT+hsGKGM9XKgKFGknDuCP8N0H7HrP7LLf/tLOMNq/PeMz
|
||||
I6MXMlXmB4VRPMlf1zJGfvE6i0sSbNM0p2ZjArpjhjdveLuvBwan/Guk9vW970ON
|
||||
sNn9gdLiwCSLqzhRy0cTlIJsSnlkhbuOQZsqKwIDAQABAoIBAE2gCVQCWsrJ9dRa
|
||||
NWEoLLpfpeXRc4vG8R0MlCgWl0jZrg7A7NZXCyb/KwqitWY/G/fB2/hGwu3QLfwi
|
||||
TBI+cF+N0fA1Xx/zbFEfwmcRkf4zSuqxd7PwJDv6icD8kCtnWFqWiZokmhYBhCvX
|
||||
kquuq8zNU4QJ9uiPvateD/zEqzSGgMeL+j7RGJsRmh2TnSBgKXLwadRhYLeHiFu/
|
||||
AwoWljlhLNXrCzCLx2kJPIA9CNYYtShhQncfZfkfC0I02vPWX9hu8lMpKQp2MmD9
|
||||
b3DvVW3H6cjAtm/nsjGghYNCngep8uPX2twcrLOZfzJgsZJf+yn/KLWb/yhGBXjd
|
||||
TERHRCECgYEA2i5OfkIrBIPQcjhQCtkjYBgKUKVS54KTTPQQ0zoiGRPkXb6kpqrt
|
||||
kaCKGYXT4oqvQQapNZQykrLUQ/xzbdAAzdIwZ8hTWS5K5cxHOnkmOcPiKu2+jM4I
|
||||
zT7sdAYn0aSbrh1pNRQDV0tQZcI1Urp/OcEuniaEblWhq5/VRCmpCBECgYEA13wg
|
||||
jKRobq4QBoQM8pu1Ha7waeJZ26NcZwCxno0TwH2JZ6X9e4iXfhywUOgVW7hXzcs5
|
||||
2nBciVX5h31u1EDPJz6aPyzzQHi0YspDy/zuO0GWEJxLKm5QMyjh5vakhF5bVP6f
|
||||
Dh3rXts/ZYKk3+p4ezXs2b+uTelowuq8Kk55qnsCgYAy/tvN2v1fAsg3yj27K2F/
|
||||
Vl8i1mF4RybSt8Eu/cl2fxXDa4nkgtMgVJuyt3r82ll4I2xtX4Qqka3Xbiw0oIdv
|
||||
lA9IUqRYld9fss17N1Hd8pDsY8FD++xGvMxbmgy4jXbtzWYHx/O39ZyHDEuWWIzg
|
||||
HO0effY6K72r9aHNWsdtYQKBgQDNXUw8Hbg1u4gkXZdlZEYxevc/Qmz3KXK36+5b
|
||||
uAJaEopwkL7LC/utQjQ7d2RbnI154TRK3Ykjjh+ZJE8K1JVYxo4EpZdTG3Z3LGN+
|
||||
tphpOvGE9R+h2a5vg4gAMZHLYY3TrDL0JkmahoOd/+uYR4L5kgQf5lF9iXTBRyt7
|
||||
enzznwKBgBr9rHUsdd8whEo/UntKaGQ6sqevd/ESxDErUqkTkiTtDAT2vuLQwgA0
|
||||
JnzYUv1/wmoLCz6Nbe6Fg9shAgRY/tz7GI75NBi4HBjp286df6Uu6qEJ4LAELc0+
|
||||
Yh/uRF/H4WkpmBckEXobnRxoX/0HWFY5SETLoG/nwaxJG9YL/Acr
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var clientCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIJAPJbY53f15/wMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjU0WhgPMjI5MDExMjcw
|
||||
MDIyNTRaMB8xHTAbBgNVBAMMFHdlYmhvb2tfdGVzdHNfY2xpZW50MIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt6auF7X7jl94ilPpJid6k15/Tnt/hFWM
|
||||
Rv2eGeuSBT1YMjDPX5pIaseeIVdXl0ePKzPeyYJma4t7EpGhUO7TX12Cr1e5S8IM
|
||||
ExNDpYGti3ncLf1OHP+faJNAlbKMTgf2xr0HPg8Q2lsDKfam/OEn+7jqv3FpZ5gu
|
||||
wQQy7beQhWV38YuRvf2ChNJVcalN0h+MRNkT+hsGKGM9XKgKFGknDuCP8N0H7HrP
|
||||
7LLf/tLOMNq/PeMzI6MXMlXmB4VRPMlf1zJGfvE6i0sSbNM0p2ZjArpjhjdveLuv
|
||||
Bwan/Guk9vW970ONsNn9gdLiwCSLqzhRy0cTlIJsSnlkhbuOQZsqKwIDAQABo0ow
|
||||
SDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
|
||||
KwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQUFAAOCAQEAD8aZ
|
||||
E0nof9Rh7s+uwtF70KPgcz71ft0c1+vSmeLm4IkN0f+amcvgaT8xZLwNv1b77NZo
|
||||
uMWXvit24eIuiqzq7umKiHP/UrFv+Rl+9ue+lA3N0e3WikRoJsh3aoIn8BQUBbnX
|
||||
Nr9R69SeRYYRpMrs19N5Wn4gN7Nfie+1FKWsL3myJYDFsg+8GMEcOJ0YdOMALMy0
|
||||
tIJdYji28mTQ++lpGbekjhf7p9wazQ/6CVd8WNpIbGO84QbGCcpCaVM2XxOSiV/F
|
||||
hIGO1Z30SBq8rQw51XbhdRX+uvRM1ya4RuBMCSX/hpsMu9lVRqCzbkU4PvuUTqLA
|
||||
CebKCgjYbM0CWrP9kw==
|
||||
-----END CERTIFICATE-----`)
|
107
staging/src/k8s.io/apiserver/pkg/util/webhook/gencerts.sh
Executable file
107
staging/src/k8s.io/apiserver/pkg/util/webhook/gencerts.sh
Executable file
@ -0,0 +1,107 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2017 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e
|
||||
|
||||
# gencerts.sh generates the certificates for the webhook tests.
|
||||
#
|
||||
# It is not expected to be run often (there is no go generate rule), and mainly
|
||||
# exists for documentation purposes.
|
||||
|
||||
CN_BASE="webhook_tests"
|
||||
|
||||
cat > server.conf << EOF
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
[req_distinguished_name]
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth, serverAuth
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
cat > client.conf << EOF
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
[req_distinguished_name]
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth, serverAuth
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
# Create a certificate authority
|
||||
openssl genrsa -out caKey.pem 2048
|
||||
openssl req -x509 -new -nodes -key caKey.pem -days 100000 -out caCert.pem -subj "/CN=${CN_BASE}_ca"
|
||||
|
||||
# Create a second certificate authority
|
||||
openssl genrsa -out badCAKey.pem 2048
|
||||
openssl req -x509 -new -nodes -key badCAKey.pem -days 100000 -out badCACert.pem -subj "/CN=${CN_BASE}_ca"
|
||||
|
||||
# Create a server certiticate
|
||||
openssl genrsa -out serverKey.pem 2048
|
||||
openssl req -new -key serverKey.pem -out server.csr -subj "/CN=${CN_BASE}_server" -config server.conf
|
||||
openssl x509 -req -in server.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out serverCert.pem -days 100000 -extensions v3_req -extfile server.conf
|
||||
|
||||
# Create a client certiticate
|
||||
openssl genrsa -out clientKey.pem 2048
|
||||
openssl req -new -key clientKey.pem -out client.csr -subj "/CN=${CN_BASE}_client" -config client.conf
|
||||
openssl x509 -req -in client.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out clientCert.pem -days 100000 -extensions v3_req -extfile client.conf
|
||||
|
||||
outfile=certs_test.go
|
||||
|
||||
cat > $outfile << EOF
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
EOF
|
||||
|
||||
echo "// This file was generated using openssl by the gencerts.sh script" >> $outfile
|
||||
echo "// and holds raw certificates for the webhook tests." >> $outfile
|
||||
echo "" >> $outfile
|
||||
echo "package webhook" >> $outfile
|
||||
for file in caKey caCert badCAKey badCACert serverKey serverCert clientKey clientCert; do
|
||||
data=$(cat ${file}.pem)
|
||||
echo "" >> $outfile
|
||||
echo "var $file = []byte(\`$data\`)" >> $outfile
|
||||
done
|
||||
|
||||
# Clean up after we're done.
|
||||
rm *.pem
|
||||
rm *.csr
|
||||
rm *.srl
|
||||
rm *.conf
|
607
staging/src/k8s.io/apiserver/pkg/util/webhook/webhook_test.go
Normal file
607
staging/src/k8s.io/apiserver/pkg/util/webhook/webhook_test.go
Normal file
@ -0,0 +1,607 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/pkg/api"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd/api/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
errBadCertificate = "Get .*: remote error: tls: bad certificate"
|
||||
errNoConfiguration = "invalid configuration: no configuration has been provided"
|
||||
errMissingCertPath = "invalid configuration: unable to read %s %s for %s due to open %s: .*"
|
||||
errSignedByUnknownCA = "Get .*: x509: certificate signed by unknown authority"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultCluster = v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: "https://webhook.example.com",
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
}
|
||||
defaultUser = v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: clientCert,
|
||||
ClientKeyData: clientKey,
|
||||
},
|
||||
}
|
||||
namedCluster = v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: "https://webhook.example.com",
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
Name: "test-cluster",
|
||||
}
|
||||
groupVersions = []schema.GroupVersion{}
|
||||
retryBackoff = time.Duration(500) * time.Millisecond
|
||||
)
|
||||
|
||||
// TestDisabledGroupVersion ensures that requiring a group version works as expected
|
||||
func TestDisabledGroupVersion(t *testing.T) {
|
||||
gv := schema.GroupVersion{Group: "webhook.util.k8s.io", Version: "v1"}
|
||||
gvs := []schema.GroupVersion{gv}
|
||||
_, err := NewGenericWebhook(api.Registry, api.Codecs, "/some/path", gvs, retryBackoff)
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
} else {
|
||||
aErrMsg := err.Error()
|
||||
eErrMsg := fmt.Sprintf("webhook plugin requires enabling extension resource: %s", gv)
|
||||
|
||||
if aErrMsg != eErrMsg {
|
||||
t.Errorf("unexpected error message mismatch:\n Expected: %s\n Actual: %s", eErrMsg, aErrMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestKubeConfigFile ensures that a kube config file, regardless of validity, is handled properly
|
||||
func TestKubeConfigFile(t *testing.T) {
|
||||
badCAPath := "/tmp/missing/ca.pem"
|
||||
badClientCertPath := "/tmp/missing/client.pem"
|
||||
badClientKeyPath := "/tmp/missing/client-key.pem"
|
||||
dir := bootstrapTestDir(t)
|
||||
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// These tests check for all of the ways in which a Kubernetes config file could be malformed within the context of
|
||||
// configuring a webhook. Configuration issues that arise while using the webhook are tested elsewhere.
|
||||
tests := []struct {
|
||||
test string
|
||||
cluster *v1.NamedCluster
|
||||
context *v1.NamedContext
|
||||
currentContext string
|
||||
user *v1.NamedAuthInfo
|
||||
errRegex string
|
||||
}{
|
||||
{
|
||||
test: "missing context (no default, none specified)",
|
||||
cluster: &namedCluster,
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "missing context (specified context is missing)",
|
||||
cluster: &namedCluster,
|
||||
currentContext: "missing-context",
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "context without cluster",
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{},
|
||||
},
|
||||
currentContext: "testing-context",
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "context without user",
|
||||
cluster: &namedCluster,
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{
|
||||
Cluster: namedCluster.Name,
|
||||
},
|
||||
},
|
||||
currentContext: "testing-context",
|
||||
errRegex: "", // Not an error at parse time, only when using the webhook
|
||||
},
|
||||
{
|
||||
test: "context with missing cluster",
|
||||
cluster: &namedCluster,
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{
|
||||
Cluster: "missing-cluster",
|
||||
},
|
||||
},
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "context with missing user",
|
||||
cluster: &namedCluster,
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{
|
||||
Cluster: namedCluster.Name,
|
||||
AuthInfo: "missing-user",
|
||||
},
|
||||
},
|
||||
currentContext: "testing-context",
|
||||
errRegex: "", // Not an error at parse time, only when using the webhook
|
||||
},
|
||||
{
|
||||
test: "cluster with invalid CA certificate path",
|
||||
cluster: &v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: namedCluster.Cluster.Server,
|
||||
CertificateAuthority: badCAPath,
|
||||
},
|
||||
},
|
||||
user: &defaultUser,
|
||||
errRegex: fmt.Sprintf(errMissingCertPath, "certificate-authority", badCAPath, "", badCAPath),
|
||||
},
|
||||
{
|
||||
test: "cluster with invalid CA certificate ",
|
||||
cluster: &v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: namedCluster.Cluster.Server,
|
||||
CertificateAuthorityData: caKey,
|
||||
},
|
||||
},
|
||||
user: &defaultUser,
|
||||
errRegex: "", // Not an error at parse time, only when using the webhook
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate path",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificate: badClientCertPath,
|
||||
ClientKeyData: defaultUser.AuthInfo.ClientKeyData,
|
||||
},
|
||||
},
|
||||
errRegex: fmt.Sprintf(errMissingCertPath, "client-cert", badClientCertPath, "", badClientCertPath),
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: clientKey,
|
||||
ClientKeyData: defaultUser.AuthInfo.ClientKeyData,
|
||||
},
|
||||
},
|
||||
errRegex: "tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched",
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate path",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: defaultUser.AuthInfo.ClientCertificateData,
|
||||
ClientKey: badClientKeyPath,
|
||||
},
|
||||
},
|
||||
errRegex: fmt.Sprintf(errMissingCertPath, "client-key", badClientKeyPath, "", badClientKeyPath),
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: defaultUser.AuthInfo.ClientCertificateData,
|
||||
ClientKeyData: clientCert,
|
||||
},
|
||||
},
|
||||
errRegex: "tls: found a certificate rather than a key in the PEM for the private key",
|
||||
},
|
||||
{
|
||||
test: "valid configuration (certificate data embeded in config)",
|
||||
cluster: &defaultCluster,
|
||||
user: &defaultUser,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "valid configuration (certificate files referenced in config)",
|
||||
cluster: &v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: "https://webhook.example.com",
|
||||
CertificateAuthority: filepath.Join(dir, "ca.pem"),
|
||||
},
|
||||
},
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificate: filepath.Join(dir, "client.pem"),
|
||||
ClientKey: filepath.Join(dir, "client-key.pem"),
|
||||
},
|
||||
},
|
||||
errRegex: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
// Use a closure so defer statements trigger between loop iterations.
|
||||
err := func() error {
|
||||
kubeConfig := v1.Config{}
|
||||
|
||||
if tt.cluster != nil {
|
||||
kubeConfig.Clusters = []v1.NamedCluster{*tt.cluster}
|
||||
}
|
||||
|
||||
if tt.context != nil {
|
||||
kubeConfig.Contexts = []v1.NamedContext{*tt.context}
|
||||
}
|
||||
|
||||
if tt.user != nil {
|
||||
kubeConfig.AuthInfos = []v1.NamedAuthInfo{*tt.user}
|
||||
}
|
||||
|
||||
kubeConfigFile, err := newKubeConfigFile(kubeConfig)
|
||||
|
||||
if err == nil {
|
||||
defer os.Remove(kubeConfigFile)
|
||||
|
||||
_, err = NewGenericWebhook(api.Registry, api.Codecs, kubeConfigFile, groupVersions, retryBackoff)
|
||||
}
|
||||
|
||||
return err
|
||||
}()
|
||||
|
||||
if err == nil {
|
||||
if tt.errRegex != "" {
|
||||
t.Errorf("%s: expected an error", tt.test)
|
||||
}
|
||||
} else {
|
||||
if tt.errRegex == "" {
|
||||
t.Errorf("%s: unexpected error: %v", tt.test, err)
|
||||
} else if !regexp.MustCompile(tt.errRegex).MatchString(err.Error()) {
|
||||
t.Errorf("%s: unexpected error message to match:\n Expected: %s\n Actual: %s", tt.test, tt.errRegex, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMissingKubeConfigFile ensures that a kube config path to a missing file is handled properly
|
||||
func TestMissingKubeConfigFile(t *testing.T) {
|
||||
kubeConfigPath := "/some/missing/path"
|
||||
_, err := NewGenericWebhook(api.Registry, api.Codecs, kubeConfigPath, groupVersions, retryBackoff)
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("creating the webhook should had failed")
|
||||
} else if strings.Index(err.Error(), fmt.Sprintf("stat %s", kubeConfigPath)) != 0 {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTLSConfig ensures that the TLS-based communication between client and server works as expected
|
||||
func TestTLSConfig(t *testing.T) {
|
||||
invalidCert := []byte("invalid")
|
||||
tests := []struct {
|
||||
test string
|
||||
clientCert, clientKey, clientCA []byte
|
||||
serverCert, serverKey, serverCA []byte
|
||||
errRegex string
|
||||
}{
|
||||
{
|
||||
test: "invalid server CA",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: invalidCert,
|
||||
errRegex: errBadCertificate,
|
||||
},
|
||||
{
|
||||
test: "invalid client certificate",
|
||||
clientCert: invalidCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: "tls: failed to find any PEM data in certificate input",
|
||||
},
|
||||
{
|
||||
test: "invalid client key",
|
||||
clientCert: clientCert, clientKey: invalidCert, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: "tls: failed to find any PEM data in key input",
|
||||
},
|
||||
{
|
||||
test: "client does not trust server",
|
||||
clientCert: clientCert, clientKey: clientKey,
|
||||
serverCert: serverCert, serverKey: serverKey,
|
||||
errRegex: errSignedByUnknownCA,
|
||||
},
|
||||
{
|
||||
test: "server does not trust client",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: badCACert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: errSignedByUnknownCA + " .*",
|
||||
},
|
||||
{
|
||||
test: "server requires auth, client provides it",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "server does not require client auth",
|
||||
clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "server does not require client auth, client provides it",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "webhook does not support insecure servers",
|
||||
errRegex: errSignedByUnknownCA,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
// Use a closure so defer statements trigger between loop iterations.
|
||||
func() {
|
||||
// Create and start a simple HTTPS server
|
||||
server, err := newTestServer(tt.serverCert, tt.serverKey, tt.serverCA, nil)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("%s: failed to create server: %v", tt.test, err)
|
||||
return
|
||||
}
|
||||
|
||||
defer server.Close()
|
||||
|
||||
// Create a Kubernetes client configuration file
|
||||
configFile, err := newKubeConfigFile(v1.Config{
|
||||
Clusters: []v1.NamedCluster{
|
||||
{
|
||||
Cluster: v1.Cluster{
|
||||
Server: server.URL,
|
||||
CertificateAuthorityData: tt.clientCA,
|
||||
},
|
||||
},
|
||||
},
|
||||
AuthInfos: []v1.NamedAuthInfo{
|
||||
{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: tt.clientCert,
|
||||
ClientKeyData: tt.clientKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", tt.test, err)
|
||||
return
|
||||
}
|
||||
|
||||
defer os.Remove(configFile)
|
||||
|
||||
wh, err := NewGenericWebhook(api.Registry, api.Codecs, configFile, groupVersions, retryBackoff)
|
||||
|
||||
if err == nil {
|
||||
err = wh.RestClient.Get().Do().Error()
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if tt.errRegex != "" {
|
||||
t.Errorf("%s: expected an error", tt.test)
|
||||
}
|
||||
} else {
|
||||
if tt.errRegex == "" {
|
||||
t.Errorf("%s: unexpected error: %v", tt.test, err)
|
||||
} else if !regexp.MustCompile(tt.errRegex).MatchString(err.Error()) {
|
||||
t.Errorf("%s: unexpected error message mismatch:\n Expected: %s\n Actual: %s", tt.test, tt.errRegex, err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// TestWithExponentialBackoff ensures that the webhook's exponential backoff support works as expected
|
||||
func TestWithExponentialBackoff(t *testing.T) {
|
||||
count := 0 // To keep track of the requests
|
||||
gr := schema.GroupResource{
|
||||
Group: "webhook.util.k8s.io",
|
||||
Resource: "test",
|
||||
}
|
||||
|
||||
// Handler that will handle all backoff CONDITIONS
|
||||
ebHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
switch count++; count {
|
||||
case 1:
|
||||
// Timeout error with retry supplied
|
||||
w.WriteHeader(http.StatusGatewayTimeout)
|
||||
json.NewEncoder(w).Encode(apierrors.NewServerTimeout(gr, "get", 2))
|
||||
case 2:
|
||||
// Internal server error
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(apierrors.NewInternalError(fmt.Errorf("nope")))
|
||||
case 3:
|
||||
// HTTP error that is not retryable
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
json.NewEncoder(w).Encode(apierrors.NewGenericServerResponse(http.StatusNotAcceptable, "get", gr, "testing", "nope", 0, false))
|
||||
case 4:
|
||||
// Successful request
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"status": "OK",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Create and start a simple HTTPS server
|
||||
server, err := newTestServer(clientCert, clientKey, caCert, ebHandler)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("failed to create server: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer server.Close()
|
||||
|
||||
// Create a Kubernetes client configuration file
|
||||
configFile, err := newKubeConfigFile(v1.Config{
|
||||
Clusters: []v1.NamedCluster{
|
||||
{
|
||||
Cluster: v1.Cluster{
|
||||
Server: server.URL,
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
},
|
||||
},
|
||||
AuthInfos: []v1.NamedAuthInfo{
|
||||
{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: clientCert,
|
||||
ClientKeyData: clientKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("failed to create the client config file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer os.Remove(configFile)
|
||||
|
||||
wh, err := NewGenericWebhook(api.Registry, api.Codecs, configFile, groupVersions, retryBackoff)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create the webhook: %v", err)
|
||||
}
|
||||
|
||||
result := wh.WithExponentialBackoff(func() rest.Result {
|
||||
return wh.RestClient.Get().Do()
|
||||
})
|
||||
|
||||
var statusCode int
|
||||
|
||||
result.StatusCode(&statusCode)
|
||||
|
||||
if statusCode != http.StatusNotAcceptable {
|
||||
t.Errorf("unexpected status code: %d", statusCode)
|
||||
}
|
||||
|
||||
result = wh.WithExponentialBackoff(func() rest.Result {
|
||||
return wh.RestClient.Get().Do()
|
||||
})
|
||||
|
||||
result.StatusCode(&statusCode)
|
||||
|
||||
if statusCode != http.StatusOK {
|
||||
t.Errorf("unexpected status code: %d", statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func bootstrapTestDir(t *testing.T) string {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// The certificates needed on disk for the tests
|
||||
files := map[string][]byte{
|
||||
"ca.pem": caCert,
|
||||
"client.pem": clientCert,
|
||||
"client-key.pem": clientKey,
|
||||
}
|
||||
|
||||
// Write the certificate files to disk or fail
|
||||
for fileName, fileData := range files {
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, fileName), fileData, 0400); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
func newKubeConfigFile(config v1.Config) (string, error) {
|
||||
configFile, err := ioutil.TempFile("", "")
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to create the Kubernetes client config file: %v", err)
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(configFile).Encode(config); err != nil {
|
||||
return "", fmt.Errorf("unable to write the Kubernetes client configuration to disk: %v", err)
|
||||
}
|
||||
|
||||
return configFile.Name(), nil
|
||||
}
|
||||
|
||||
func newTestServer(clientCert, clientKey, caCert []byte, handler func(http.ResponseWriter, *http.Request)) (*httptest.Server, error) {
|
||||
var tlsConfig *tls.Config
|
||||
|
||||
if clientCert != nil {
|
||||
cert, err := tls.X509KeyPair(clientCert, clientKey)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
}
|
||||
|
||||
if caCert != nil {
|
||||
rootCAs := x509.NewCertPool()
|
||||
|
||||
rootCAs.AppendCertsFromPEM(caCert)
|
||||
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = &tls.Config{}
|
||||
}
|
||||
|
||||
tlsConfig.ClientCAs = rootCAs
|
||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
if handler == nil {
|
||||
handler = func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("OK"))
|
||||
}
|
||||
}
|
||||
|
||||
server := httptest.NewUnstartedServer(http.HandlerFunc(handler))
|
||||
|
||||
server.TLS = tlsConfig
|
||||
server.StartTLS()
|
||||
|
||||
return server, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user