Merge pull request #109024 from stlaz/sha1_sig_deprecation

webhooks,aggregation: add metrics to count certs with SHA1 signatures
This commit is contained in:
Kubernetes Prow Robot 2022-03-28 09:53:24 -07:00 committed by GitHub
commit e0ca5cfd73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1229 additions and 559 deletions

View File

@ -20,184 +20,266 @@ limitations under the License.
package webhook
var caKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----`)
var caCert = []byte(`-----BEGIN CERTIFICATE-----
MIIDGTCCAgGgAwIBAgIUOS2MkobR2t4rguefcC78gLuXkc0wDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMDEwMDcxMjMxNDFa
GA8yMjk0MDcyMzEyMzE0MVowGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMy8o1sRSe8viuSxh8a7Ou8n
vyDJxeJA9son+clZdSwa1YpbhSsYPr2svTGLT4ZxX3JkvHZGDaB+G6QrZNIWOssJ
4+fjvEsVSnk9q7Yr8wX8QdShksnSP0qdB1xFKlqwTFcZyQHqjlyctV93/LEosM9D
RZzgPpXJuC6wD7qdLRSPxQwuegZYcMGT2Y4/8CfBgiEUkZNrGYJiDFrblI0N9jX2
jUeP/g8xWJIKLTBedVbAzj02Y66WdNecYcyTMROdclPB1IbF4ABbCGynnP0fbiaf
0+4v0pLedqwCYSWD/ujajyDtYuhI8U+OkENwkZ/h5jw/6aWq7esMZDbEAT6UPgEC
AwEAAaNTMFEwHQYDVR0OBBYEFBgvyZWkRJCRjxclYA0mMlnzc/GmMB8GA1UdIwQY
MBaAFBgvyZWkRJCRjxclYA0mMlnzc/GmMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAHigaFJ8DqYFMGXNUT5lYM8oLLWYcbivGxR7ofDm8rXWGLdQ
tKsWeusSlgpzeO1PKCzwSrYQhlFIZI6AH+ch7EAWt84MfAaMz/1DiXF6Q8fAcR0h
QoyFIAKTiVkcgOQjSQOIM2SS5cyGeDRaHGVWfaJOwdIYo6ctFzI2asPJ4yU0QsA7
0WTD2+sBG6AXGhfafGUHEmou8sGQ+QT8rgi4hs1bfyHuT5dPgC4TbwknD1G8pMqm
ID3CIiCF8hhF5C2hjrW0LTJ6zTlg1c1K0NmmUL1ucsfzEk//c7GsU8dk+FYmtW9A
VzryJj1NmnSqA3zd3jBMuK37Ei3pRvVbO7Uxf14=
MIIDGTCCAgGgAwIBAgIUealQGELTHLUVcpsNNwz8XexiWvswDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKeW0Jkq6ViZkyhaLCUgbqsN
7+6HLwfZK/ljy/KnZ7W7QlJ65Q2tkptL0fY4DPumT7JgVTGnXyTXJ35Ss5A4yhm9
yyMH8pNVR19udK1fU74YVmbXJkc0nP7n+AXX9lD2Yy9pDvtaq1E+erN2nR1XaCS9
n0ph4C/fB1Rh7mIv/u7WW7/aRz/rJjBBZIbg7hgZPwFsukEifGi0U4uitVYR6MWp
jHj++e1G38+4JrZR9vhBoHtBJ1DBpmjAQaAtkSZAxXJnma4awE0Bv0Q4lxkUeY+D
th8OxPXxgTbwTKDaguHlWLTapppygA8FnKqmcUkwHZO5OVldi/ZKOUm2YCuJlfEC
AwEAAaNTMFEwHQYDVR0OBBYEFJGX6zVDm0Ur4gJJR+PKlgGdwhPfMB8GA1UdIwQY
MBaAFJGX6zVDm0Ur4gJJR+PKlgGdwhPfMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAJaRryxp0iYWGfLiZ0uIdOiYRVZUtmpqUSqT9/y29ffDAnCS
5labJS8FjaiQdlyaH+E9gzo0+nkO9NyfemJRLTEsU4Mz9AAvxs/NuWucqiyF0Y6d
JSYt7+2liGK5WvJMbHfW3jloWlv3oX+qL4iGFkJN+L9G+vf0GnKZCxgNIOqM4otv
cviCA9ouPwnSfnCeTsBoaUJqhLMizvUx7avvUyZTuV+t9+aN/qH4V//hTBqz9CNq
koodzngbUuyaGFI8QISUAhU+pbt6npb69LlhJHC0icDXCc4uTsd+SDsBOorZDuUL
HsNKnE0CSPZ65ENVNfJjB1fVEYbbpjr8kizOCDE=
-----END CERTIFICATE-----`)
var badCACert = []byte(`-----BEGIN CERTIFICATE-----
MIIDGTCCAgGgAwIBAgIUINBaI0NGgSo4UqKHuPd/HSBO38EwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMDEwMDcxMjMxNDFa
GA8yMjk0MDcyMzEyMzE0MVowGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJhgUJ5gSvPA3R3s0V3ndKV
9L7W8TD+lhMGsVtXese4zmn59h238bsQvwdXaXsKSrMAZUE4uP9C6o1Itv2CSMlS
WVxnRUWkec6mFSuC2DR0gb2PMvcMrqVDihoj+n8fWzAFzs2n3StC0ewvD+avfs8U
XI1PBubCvZp3fqDSyjGSdYGjFNyjGIt4J8YuXmzccT9CwGj6mPsn6Y0ptjmZEv63
DFjF7KuNhET723JX9K2OaPrmrl12jYpZIyYVFKBJNGzpan5bIdqJfllcIUt1yDvT
DkcLKT+fHa5+e+CXMITBZTO2lJE+P4VaKp0mjpknS0dzqdzpkEqvp/ifXGyYlX0C
AwEAAaNTMFEwHQYDVR0OBBYEFLD1yOtonJPAufGLqZ3QSRZDAeoiMB8GA1UdIwQY
MBaAFLD1yOtonJPAufGLqZ3QSRZDAeoiMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBACpuDaS4SPpiA3cU6/tadt8F1vqDs0T4sHbPfiCLdb4P+3N/
2R8q3SvGzpxJzPflkR8QFwjK0fQuIb0FLyXbBCAXz1KyDb7m5OAs50Cn8OVqsjci
LWUGR/QPhAlcNba5jLhbbq7P4qh28aNx8u6XT/l/PSxgcfWm5vOc/2dN4I948R3P
U1iyLSqHTDFzsawHgXIyTUq9TOZXlBjWyWHBFsDI1AnQdeT3ELdeDzGyD6paGSB5
IJ4LFyvJkslvKFGWgOc6EhB9zvxrKWBEMTDUTOfRe7gNQ1esr6XcsAqtJ7/QRTUF
36VxjlPiERA2m5tmNNy4hZ4zUXKlsJgeY+bAdVM=
MIIDGTCCAgGgAwIBAgIUDr7CjUg6evYtl4uqdgyu94ACVOAwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzgv+bNXgI07CBjqHRie/2R
3Etpu7eVC9/LuBoe+KpUuns3r2Ix0bKg1VIjDLdidcFmAqx/qSi0SWdEtAfPWqWo
LI6GVYmBUZzcJHAxMsJJLIpVCIZsQt+k4Dz0ECGwB/Q501Es9hEiTHZSXnPN8XSO
0f9m2rINT3yWCa+UyMlxolIJAkYlajU/BU8xIOcSKACaE2iNKv3rs+6qIO7dpP+j
tUWI/Vtjdt5PSY7+WUl5yh4WqTMkMKfAonSyvCxzyMb1HIe3TAN0dTulN/PdAe9x
7qLJx08wju2v5eBhwwQn0ZO9FPPNE/440nwanUmJtQBh46htgImPmz3j2VQvRLcC
AwEAAaNTMFEwHQYDVR0OBBYEFKp7SXYko8r9qgYDbOaDOEt2aNUhMB8GA1UdIwQY
MBaAFKp7SXYko8r9qgYDbOaDOEt2aNUhMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAADzhaAVNIIWykvV4+RZ6FtSS3oJwerFUY2/PECLTcEyjtuu
eTu9uiZMu4R/hmvcGXxa40uS96SOwseIo8FJFSVEiTLqPJB+ZhiFE/wAaChRlc78
4/Q9Hj46IS67aZoOT+nCIv3PcOrxFMZqtLyBLIrtFxkv2WfoaqxTm7GbBdvwIvQO
DvOHHudXGeC+RL8sz8YIv1ghd8LxLdWZmyZhvCWHB0YHnoVryN6oqhKY0zv9YUGJ
r97Qb+RQu9ijmNHWkwuTy5Zxj/GMIqC/vYu7x5DH/m0oCnSGqASLh1gAsjC0Aa+D
YqXbFLxSCtUNhKuW5ejbvHAYp/QgtjAtu81HOkQ=
-----END CERTIFICATE-----`)
var caCertInter = []byte(`-----BEGIN CERTIFICATE-----
MIIDMzCCAhugAwIBAgIUTJoqwFusJcupNCs/u39LBFrkZEIwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowKDEmMCQGA1UEAwwdd2ViaG9va190ZXN0c19pbnRl
cm1lZGlhdGVfY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDT9DIt
uNXhesrh8XtPXK4pR7xGReEsIlgLpMYf11PtFln9eV0HXvUO2CG/YvMxwgyd6Yoq
EfzD4rjmXvl5bQPMygmxf5GN1PM7ef7gVYuHfDgsQ4a82u1JFhKvuOrXn3QRfRg4
M4uYND7J4+Bg6J8oaA0yXIiMCpBi+XwEufo0RvgxM6mT+CeJ82hmlTKVhQJZZ9ZT
al1C4dTR2XeH5TLiIAvm+egBmSZhtCVn14rGk/PcHOWV7hdCxaFhSm7dSC+dR4zK
SxNleJ4Y+tZgoMfvgP/xHZEjbBzxnxyasES/Nc4nTgylcr6aqEX/fbcF0QzHpL9Z
ibkt1cBExU9zHuFJAgMBAAGjYDBeMB0GA1UdDgQWBBTfgUwjHsTOey7WqL4f3oFD
bmY77TAfBgNVHSMEGDAWgBSRl+s1Q5tFK+ICSUfjypYBncIT3zAPBgNVHRMBAf8E
BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEARYbIpIidgAVb
5ra9zd7F902+xC13/nmlrKL/dRMrRdZxk1kVVww3FbXSp7k7oHsih42KUCVDBevw
0ZZiolZlLneU57dEKKiTMkuPdVbNbIBPXIQpHLrXpVIR5BRRdRZ5OJZY24hYCvce
50XV8ITIU0R/U4sQ6NFHv8NJ5BB+2u1M3HF2LSKZFLnfP5FBcTCg84Jd6gEmTU2j
wZELnHy1AVdQnKMP9VrdAr9Wn6omWxAfO/PSb9YeKhGH5vtX+Bpb9bSPQIpXeBdB
LLCkme0M+1UsF7xua0KVi4DSuJc+RBl4aOH0ZvKmrIWzLzZhRS0vaO/fPArVCvvI
VrUba0E3WQ==
-----END CERTIFICATE-----`)
var caCertInterSHA1 = []byte(`-----BEGIN CERTIFICATE-----
MIIDMzCCAhugAwIBAgIUTJoqwFusJcupNCs/u39LBFrkZEMwDQYJKoZIhvcNAQEF
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowKDEmMCQGA1UEAwwdd2ViaG9va190ZXN0c19pbnRl
cm1lZGlhdGVfY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDT9DIt
uNXhesrh8XtPXK4pR7xGReEsIlgLpMYf11PtFln9eV0HXvUO2CG/YvMxwgyd6Yoq
EfzD4rjmXvl5bQPMygmxf5GN1PM7ef7gVYuHfDgsQ4a82u1JFhKvuOrXn3QRfRg4
M4uYND7J4+Bg6J8oaA0yXIiMCpBi+XwEufo0RvgxM6mT+CeJ82hmlTKVhQJZZ9ZT
al1C4dTR2XeH5TLiIAvm+egBmSZhtCVn14rGk/PcHOWV7hdCxaFhSm7dSC+dR4zK
SxNleJ4Y+tZgoMfvgP/xHZEjbBzxnxyasES/Nc4nTgylcr6aqEX/fbcF0QzHpL9Z
ibkt1cBExU9zHuFJAgMBAAGjYDBeMB0GA1UdDgQWBBTfgUwjHsTOey7WqL4f3oFD
bmY77TAfBgNVHSMEGDAWgBSRl+s1Q5tFK+ICSUfjypYBncIT3zAPBgNVHRMBAf8E
BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAAhMQTwrpAeIQ
nShHfTERiwg/tx3dL971d3pFS5wi4kEIbbYCUGpzkmK/FTw4hfUnLpwcjjAbOWkk
45glOmrLJXM4RvH5PQF3GZmZvxv8Dl4zuhH1QvWbJHUiC+gyrBWI0moyLSmNiutZ
d3TZGEehZGwivMdHHuhgiyFM4i33EQTW1vdMdOvdu8yNpAeXM2h1PcJwbEML9PO3
LONzVKhz/RsyEwv7PkX1gdmi6eyAE61BWJGwzxE4K0xIYmcr6iOjsJhEf/5Qc93P
IGSHsG/HjWwZ47gbobtv7L+8uKP/0ky+k1cE4nIB1gKYey+SYwvkQTsj24oG9xcL
XhgnIl+qDw==
-----END CERTIFICATE-----`)
var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----`)
var serverCert = []byte(`-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----`)
var serverCertNoSAN = []byte(`-----BEGIN CERTIFICATE-----
MIIC+DCCAeCgAwIBAgIUZIt+6zrmR41Be/CrcPHLsj3pCAUwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMDEwMDcxMjMxNDFa
GA8yMjk0MDcyMzEyMzE0MVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Gi/3oXPU0oRP38xNAQ1js4Py1fVqy5O
W+3rcaZONdUiiHJ46K1mIMKuEH2nrFAq2cFiXIm9prrSN8hyLY9lUguyAlecdHe6
he/7/PAoX7NM8NX1LzOfUotcO+iZtrnMVCut2vlqjsGsnxppLKnFnp17ycOy7yKC
uQHowPln5xp6JkhRF0bxDXIFFmve4uHHlRsWcEoNQRJZTaOMrfrFYfqbc5iBitI7
/5xgMPZNq282TQDcGeJmbFHEu/L1qO6uTg1LkbNbZKNfXeAU46K+eecGCwPyTMlD
YuREdoVpH9GYW7CxOIRTYgCLWVok96yk2jeVCq21BbxZ8+NBJ2rhqQIDAQABozkw
MIIC+DCCAeCgAwIBAgIUTJoqwFusJcupNCs/u39LBFrkZEUwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA52b4byIJpUDyTKo5FiCa5Ekiy7CCd8Uo
leSomQjh5zZGsIbgz9RqjaTMLF0jqbzh9ix2DQSnY+w32LqPM2sOK1+/atmeCa8m
5bvZrRoDxP2T3pQHTye0C9WI7jqosmMquRFakaY1ODxDRPWF9CRghFF62NcHfnzt
W2rMiEYtuDRkZGspJL/B5OAzEgLr4iP8TKMlqSuhaQhi52dEZo0pJ1Ie4up8xxXK
eoqfO3WSVDeRpj/n0tYSALhOCdGn0RMav5wfZmxdfZpKUhBrcxFfeiDB5c7xdlnz
iHrY5lqSQPCHxAQbS1jMaV4adhxDzF56t5RU6/5eWPZ4IvlTRtYmNwIDAQABozkw
NzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
KwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAGgrpuQ4n0W/TaaXhdbfFELziXoN
eT89eFYOqgtx/o97sj8B/y+n8tm+lBopfmdnifta3e8iNVULAd6JKjBhkFL1NY3Q
tR+VqT8uEZthDM69cyuGTv1GybnUCY9mtW9dSgHHkcJNsZGn9oMTCYDcBrgoA4s3
vZc4wuPj8wyiSJBDYMNZfLWHCvLTOa0XUfh0Pf0/d26KuAUTNQkJZLZ5cbNXZuu3
fVN5brtOw+Md5nUa60z+Xp0ESaGvOLUjnmd2SWUfAzcbFbbV4fAyYZF/93zCVTJ1
ig9gRWmPqLcDDLf9LPyDR5yHRTSF4USH2ykun4PiPfstjfv0xwddWgG2+S8=
KwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAGfCa0eCws/7+NYLJwVsdd7C/QHT
qbPw6w8oGnlXELMPwC701VFOcadhhengYCY1Kwa/KVu1ucFODDgp1ncvRoMVVWvD
/q6V07zu+aV/aW64zU27f+TzxTVXyCgfCSFUELJYBsBFWLw0K57ZDZdN2KJD+zD5
BAU0ghmy1DB+WSFMTqQ2iQaNX8oZh5jTZV3JtRNncqEqqIh4Nv7YYYZ02rgE7P2o
btVFYLBXHW7VYqnWpWM1pBfZpfGzMpGdR+1feST/88gUZh7ze15Ib4BlyU13v+0l
/BjuUsSWiITKWb2fqTiAkrqVbkOrC7Orz8yvgjuih4lEinQV1+KJUtcMmng=
-----END CERTIFICATE-----`)
var clientKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----`)
var clientCert = []byte(`-----BEGIN CERTIFICATE-----
MIIDFDCCAfygAwIBAgIUZIt+6zrmR41Be/CrcPHLsj3pCAYwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMDEwMDcxMjMxNDFa
GA8yMjk0MDcyMzEyMzE0MVowHzEdMBsGA1UEAwwUd2ViaG9va190ZXN0c19jbGll
bnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDgmmhC2CHBZZRBYyuM
B1vCjG9eThZVUFH9Ik95HG8MOHJDNSHc8kOZqtJyTVltZ2FprzWnaF7O3tuhbI57
1jxf7IBJg48n574lgSKgO21XUNbkrPV/hWNFeAaAQ9kzOOblW21cYZ13AhFXuYfs
zaCPJV+cpZi1+vCp/nl69mb69i/J1n5vPTni8W6xaIBRZFThbdwdyuYrDhML9/8j
QGlZsLTYTTIV/NWQXmoM21fU0FVUbpKxGih6CRTosiiTT5dxnuDK4d9vQNPUCFQX
PdrB4yolExxeL3NCgHaT3P4YGKWIlT9DpQUagOmLvTyiZjpkk5UOhJLW7buT+usk
u2MHAgMBAAGjSjBIMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdJQQWMBQG
MIIDFDCCAfygAwIBAgIUTJoqwFusJcupNCs/u39LBFrkZEYwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowHzEdMBsGA1UEAwwUd2ViaG9va190ZXN0c19jbGll
bnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDH0HrVqiQ/6Cqv59gd
/drQnyKUpHYZ5Cd2mD0yfswf6dd3kMaxirFDyUhImVjqkaPAc/KTv+K9EOJfX2Pv
EPZhDS+ropDzkPd+yogv/S0m2/RIFrhP/vdvZliIb9I45j5+EIrlG4W2TGLb/OMT
W2q6hEPRCVPQXm47pz7uZAAfV5gR6IIABkDJ/joBpCsrJbV2UVmLfwvJVAVe1kPS
4GJBvzUDv2VjspKHy7vRuQpo7goYEQPNSgZFLgC9qPlLMl5d1crPsk4ZAtoyuWQC
WAlaEPTrBzTbQwz0qKW8COx4itp5X7ZQZQlDHDyXfJTRZ+ljVDNfy3qEGlXKrtGL
RawZAgMBAAGjSjBIMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdJQQWMBQG
CCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEB
CwUAA4IBAQBUockuDBViT8F/1plpUT5fccMkePO8h/wsk/K8IxpXAv8XerBL0H/j
k235/cGvksiYH1nKSS8VOZfEYY+b0lUDRJFf6kppU7TFiEaH3idGAcmlQ1dSBu1f
iVP63tF0KOfdPlCzdXY4jzyXSnUsxZejALmecCZt1LCOMai2w5zzqy0isjWnDPFF
+Ssmf5HxtfrPvsKDJGD+79HGtTJ3dXmifuvRuuXsYSv2uQPtJsbtXhQWaQHs/6Bg
OtuFbTvFOyxYFJmAl/YM6hRbvKB8Itq38aayi47TA/b11QdAXCB9DMvivI6a89F7
uL8+zNieoQkyj84eXfkyiSs03TrqOsj9
CwUAA4IBAQA67Zgf2H84cDOlEbBFznivpp9jRQzwv21e4O0o0HtUJ/rgwbfry1D7
LkYjtqMKQcxMREfp4d5rmkC0f7W3Skf3eoaCXTyW7Zh8ghHuqMllReSrOTAjS60m
1kzOAE07/Br1O8JtpPKFX7CTSBvLZYNNTRa/dPyQT8ZAMWSyHLc3kW9peIkQ5SZZ
kZ4EJ0/ztNuTLBbjcaxtFDYJpKuv14NJNy/ddjWvKSyBOtqae3HdEvmnfE9Ar9wt
J20OuNSKUAOZEbk9n8UrTV/C/oQ4yDpw0iHSNWhti+KvARQSTj115NkEg+7HrM4d
vhrAcrxy49UrY03GqZwmaf79vF+c2oKk
-----END CERTIFICATE-----`)
var sha1ServerCertInter = []byte(`-----BEGIN CERTIFICATE-----
MIIDITCCAgmgAwIBAgIUaVjtC++/JGFoZRtkpo/j5q1nQ/4wDQYJKoZIhvcNAQEF
BQAwKDEmMCQGA1UEAwwdd2ViaG9va190ZXN0c19pbnRlcm1lZGlhdGVfY2EwIBcN
MjIwMzI1MTUzMzI5WhgPMjI5NjAxMDgxNTMzMjlaMBQxEjAQBgNVBAMMCWxvY2Fs
aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOdm+G8iCaVA8kyq
ORYgmuRJIsuwgnfFKJXkqJkI4ec2RrCG4M/Uao2kzCxdI6m84fYsdg0Ep2PsN9i6
jzNrDitfv2rZngmvJuW72a0aA8T9k96UB08ntAvViO46qLJjKrkRWpGmNTg8Q0T1
hfQkYIRRetjXB3587VtqzIhGLbg0ZGRrKSS/weTgMxIC6+Ij/EyjJakroWkIYudn
RGaNKSdSHuLqfMcVynqKnzt1klQ3kaY/59LWEgC4TgnRp9ETGr+cH2ZsXX2aSlIQ
a3MRX3ogweXO8XZZ84h62OZakkDwh8QEG0tYzGleGnYcQ8xeereUVOv+Xlj2eCL5
U0bWJjcCAwEAAaNVMFMwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYw
FAYIKwYBBQUHAwIGCCsGAQUFBwMBMBoGA1UdEQQTMBGHBH8AAAGCCWxvY2FsaG9z
dDANBgkqhkiG9w0BAQUFAAOCAQEATpiJFBwcRFIfZ9ffvS1WDzHqNElEnvocv/ul
3KVtoX4gmKRoOy344s3oJ5APPHYWUFuZVc3uofjW265r2uOW1Cb4P9yAtNc4htBS
+hYsdS3MQlzZCS9rItaT25R6Ieq5TbHGRCof387jzvo1NNhcAQ5akQlQKI87km77
VzoEBdAw68Q0ZE+X34Q9eAA44oCcLAgCpGvs6hQuUSInribSR3vtsjuaLjdJ5F1f
GCu2QGM4cVLaezmoa1J54ETZggT2xFw2IyWJ2g/kXFpo+HnoyaDrPthud3Pe5xEt
JMzX0s3jPSjfeAv34Pr37s0Or18r1bS1hrgxE0SV2vk31fsImg==
-----END CERTIFICATE-----`)
var serverCertInterSHA1 = []byte(`-----BEGIN CERTIFICATE-----
MIIDITCCAgmgAwIBAgIUfSzzygvth1xlpa9DtyGpHuY2V+swDQYJKoZIhvcNAQEL
BQAwKDEmMCQGA1UEAwwdd2ViaG9va190ZXN0c19pbnRlcm1lZGlhdGVfY2EwIBcN
MjIwMzI1MTUzMzI5WhgPMjI5NjAxMDgxNTMzMjlaMBQxEjAQBgNVBAMMCWxvY2Fs
aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOdm+G8iCaVA8kyq
ORYgmuRJIsuwgnfFKJXkqJkI4ec2RrCG4M/Uao2kzCxdI6m84fYsdg0Ep2PsN9i6
jzNrDitfv2rZngmvJuW72a0aA8T9k96UB08ntAvViO46qLJjKrkRWpGmNTg8Q0T1
hfQkYIRRetjXB3587VtqzIhGLbg0ZGRrKSS/weTgMxIC6+Ij/EyjJakroWkIYudn
RGaNKSdSHuLqfMcVynqKnzt1klQ3kaY/59LWEgC4TgnRp9ETGr+cH2ZsXX2aSlIQ
a3MRX3ogweXO8XZZ84h62OZakkDwh8QEG0tYzGleGnYcQ8xeereUVOv+Xlj2eCL5
U0bWJjcCAwEAAaNVMFMwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYw
FAYIKwYBBQUHAwIGCCsGAQUFBwMBMBoGA1UdEQQTMBGHBH8AAAGCCWxvY2FsaG9z
dDANBgkqhkiG9w0BAQsFAAOCAQEAwe/JUeIiJ5ugiO4tM0ZtvgHuFC3hK+ZWndRE
z4JfVXTW9soxpa/cOU9QdJhZzouIu9yqZasY4zSEerC1e6grBYP95vMbN6xUAown
wNzrQzyJ6yP526txiIdOkKf+yVNdz0OWNHMPtwTWIr8kKGK23ABF94aUa0VlkErp
Qrd8NQ3guIPI+/upuxirJCFdhE+U3U0pLHpGaGvhkOytfnLYiINwR9norVCDGbQG
ITH0tOz8gVWWWwxa9s5CmbqTnasgUMDh1jHa5xOo+riX8H5lwQUaItKU1JM+QMIR
6Z+M0Isdw647A6tmX7DqNcmHlBKxPN1GDcVXalwYJUoXwTb9Hw==
-----END CERTIFICATE-----`)

View File

@ -148,8 +148,11 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {
cfg.ContentConfig.ContentType = runtime.ContentTypeJSON
// Add a transport wrapper that allows detection of TLS connections to
// servers without SAN extension in their serving certificates
cfg.Wrap(x509metrics.NewMissingSANRoundTripperWrapperConstructor(x509MissingSANCounter))
// servers with serving certificates with deprecated characteristics
cfg.Wrap(x509metrics.NewDeprecatedCertificateRoundTripperWrapperConstructor(
x509MissingSANCounter,
x509InsecureSHA1Counter,
))
client, err := rest.UnversionedRESTClientFor(cfg)
if err == nil {

View File

@ -23,6 +23,14 @@ set -e
CN_BASE="webhook_tests"
cat > intermediate_ca.conf << EOF
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
keyUsage = cRLSign, keyCertSign
EOF
cat > server.conf << EOF
[req]
req_extensions = v3_req
@ -71,6 +79,15 @@ openssl req -x509 -new -nodes -key caKey.pem -days 100000 -out caCert.pem -subj
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 an intermediate certificate authority
openssl genrsa -out caKeyInter.pem 2048
openssl req -new -nodes -key caKeyInter.pem -days 100000 -out caCertInter.csr -subj "/CN=${CN_BASE}_intermediate_ca"
openssl x509 -req -in caCertInter.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out caCertInter.pem -days 100000 -extensions v3_ca -extfile intermediate_ca.conf
# Create an intermediate certificate authority with sha1 signature
openssl req -new -nodes -key caKeyInter.pem -days 100000 -out caCertInterSHA1.csr -subj "/CN=${CN_BASE}_intermediate_ca"
openssl x509 -sha1 -req -in caCertInterSHA1.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out caCertInterSHA1.pem -days 100000 -extensions v3_ca -extfile intermediate_ca.conf
# 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
@ -80,6 +97,14 @@ openssl x509 -req -in server.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial
openssl req -new -key serverKey.pem -out serverNoSAN.csr -subj "/CN=localhost" -config server_no_san.conf
openssl x509 -req -in serverNoSAN.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out serverCertNoSAN.pem -days 100000 -extensions v3_req -extfile server_no_san.conf
# Create a server certiticate with SHA1 signature signed by OK intermediate CA
openssl req -new -key serverKey.pem -out serverSHA1.csr -subj "/CN=localhost" -config server.conf
openssl x509 -sha1 -req -in serverSHA1.csr -CA caCertInter.pem -CAkey caKeyInter.pem -CAcreateserial -out sha1ServerCertInter.pem -days 100000 -extensions v3_req -extfile server.conf
# Create a server certiticate signed by SHA1-signed intermediate CA
openssl req -new -key serverKey.pem -out serverInterSHA1.csr -subj "/CN=localhost" -config server.conf
openssl x509 -req -in serverInterSHA1.csr -CA caCertInterSHA1.pem -CAkey caKeyInter.pem -CAcreateserial -out serverCertInterSHA1.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
@ -110,7 +135,7 @@ limitations under the License.
package webhook
EOF
for file in caKey caCert badCAKey badCACert serverKey serverCert serverCertNoSAN clientKey clientCert; do
for file in caKey caCert badCAKey badCACert caCertInter caCertInterSHA1 serverKey serverCert serverCertNoSAN clientKey clientCert sha1ServerCertInter serverCertInterSHA1; do
data=$(cat ${file}.pem)
echo "" >> $outfile
echo "var $file = []byte(\`$data\`)" >> $outfile

View File

@ -34,6 +34,19 @@ var x509MissingSANCounter = metrics.NewCounter(
},
)
var x509InsecureSHA1Counter = metrics.NewCounter(
&metrics.CounterOpts{
Subsystem: "webhooks",
Namespace: "apiserver",
Name: "x509_insecure_sha1_total",
Help: "Counts the number of requests to servers with insecure SHA1 signatures " +
"in their serving certificate OR the number of connection failures " +
"due to the insecure SHA1 signatures (either/or, based on the runtime environment)",
StabilityLevel: metrics.ALPHA,
},
)
func init() {
legacyregistry.MustRegister(x509MissingSANCounter)
legacyregistry.MustRegister(x509InsecureSHA1Counter)
}

View File

@ -85,7 +85,10 @@ func NewGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFact
codec := codecFactory.LegacyCodec(groupVersions...)
clientConfig.ContentConfig.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
clientConfig.Wrap(x509metrics.NewMissingSANRoundTripperWrapperConstructor(x509MissingSANCounter))
clientConfig.Wrap(x509metrics.NewDeprecatedCertificateRoundTripperWrapperConstructor(
x509MissingSANCounter,
x509InsecureSHA1Counter,
))
restClient, err := rest.UnversionedRESTClientFor(clientConfig)
if err != nil {

View File

@ -334,11 +334,12 @@ func TestMissingKubeConfigFile(t *testing.T) {
func TestTLSConfig(t *testing.T) {
invalidCert := []byte("invalid")
tests := []struct {
test string
clientCert, clientKey, clientCA []byte
serverCert, serverKey, serverCA []byte
errRegex string
increaseSANWarnCounter bool
test string
clientCert, clientKey, clientCA []byte
serverCert, serverKey, serverCA []byte
errRegex string
increaseSANWarnCounter bool
increaseSHA1SignatureWarnCounter bool
}{
{
test: "invalid server CA",
@ -402,8 +403,23 @@ func TestTLSConfig(t *testing.T) {
errRegex: "x509: certificate relies on legacy Common Name field",
increaseSANWarnCounter: true,
},
{
test: "server cert with SHA1 signature",
clientCA: caCert,
serverCert: append(append(sha1ServerCertInter, byte('\n')), caCertInter...), serverKey: serverKey,
errRegex: "x509: cannot verify signature: insecure algorithm SHA1-RSA \\(temporarily override with GODEBUG=x509sha1=1\\)",
increaseSHA1SignatureWarnCounter: true,
},
{
test: "server cert signed by an intermediate CA with SHA1 signature",
clientCA: caCert,
serverCert: append(append(serverCertInterSHA1, byte('\n')), caCertInterSHA1...), serverKey: serverKey,
errRegex: "x509: cannot verify signature: insecure algorithm SHA1-RSA \\(temporarily override with GODEBUG=x509sha1=1\\)",
increaseSHA1SignatureWarnCounter: true,
},
}
lastSHA1SigCounter := 0
for _, tt := range tests {
// Use a closure so defer statements trigger between loop iterations.
func() {
@ -483,6 +499,20 @@ func TestTLSConfig(t *testing.T) {
t.Errorf("expected the x509_common_name_error_count to be 1, but it's %d", errorCounter)
}
}
if tt.increaseSHA1SignatureWarnCounter {
errorCounter := getSingleCounterValueFromRegistry(t, legacyregistry.DefaultGatherer, "apiserver_webhooks_x509_insecure_sha1_total")
if errorCounter == -1 {
t.Errorf("failed to get the apiserver_webhooks_x509_insecure_sha1_total metrics: %v", err)
}
if int(errorCounter) != lastSHA1SigCounter+1 {
t.Errorf("expected the apiserver_webhooks_x509_insecure_sha1_total counter to be 1, but it's %d", errorCounter)
}
lastSHA1SigCounter++
}
}()
}
}

View File

@ -1,92 +0,0 @@
/*
Copyright 2020 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 x509metrics
import (
"crypto/x509"
"errors"
"net/http"
"strings"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/component-base/metrics"
)
var _ utilnet.RoundTripperWrapper = &x509MissingSANErrorMetricsRTWrapper{}
type x509MissingSANErrorMetricsRTWrapper struct {
rt http.RoundTripper
counter *metrics.Counter
}
// NewMissingSANRoundTripperWrapperConstructor returns a RoundTripper wrapper that's usable
// within ClientConfig.Wrap that increases the `metricCounter` whenever:
// 1. we get a x509.HostnameError with string `x509: certificate relies on legacy Common Name field`
// which indicates an error caused by the deprecation of Common Name field when veryfing remote
// hostname
// 2. the server certificate in response contains no SAN. This indicates that this binary run
// with the GODEBUG=x509ignoreCN=0 in env
func NewMissingSANRoundTripperWrapperConstructor(metricCounter *metrics.Counter) func(rt http.RoundTripper) http.RoundTripper {
return func(rt http.RoundTripper) http.RoundTripper {
return &x509MissingSANErrorMetricsRTWrapper{
rt: rt,
counter: metricCounter,
}
}
}
func (w *x509MissingSANErrorMetricsRTWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := w.rt.RoundTrip(req)
checkForHostnameError(err, w.counter)
checkRespForNoSAN(resp, w.counter)
return resp, err
}
func (w *x509MissingSANErrorMetricsRTWrapper) WrappedRoundTripper() http.RoundTripper {
return w.rt
}
// checkForHostnameError increases the metricCounter when we're running w/o GODEBUG=x509ignoreCN=0
// and the client reports a HostnameError about the legacy CN fields
func checkForHostnameError(err error, metricCounter *metrics.Counter) {
if err != nil && errors.As(err, &x509.HostnameError{}) && strings.Contains(err.Error(), "x509: certificate relies on legacy Common Name field") {
// increase the count of registered failures due to Go 1.15 x509 cert Common Name deprecation
metricCounter.Inc()
}
}
// checkRespForNoSAN increases the metricCounter when the server response contains
// a leaf certificate w/o the SAN extension
func checkRespForNoSAN(resp *http.Response, metricCounter *metrics.Counter) {
if resp != nil && resp.TLS != nil && len(resp.TLS.PeerCertificates) > 0 {
if serverCert := resp.TLS.PeerCertificates[0]; !hasSAN(serverCert) {
metricCounter.Inc()
}
}
}
func hasSAN(c *x509.Certificate) bool {
sanOID := []int{2, 5, 29, 17}
for _, e := range c.Extensions {
if e.Id.Equal(sanOID) {
return true
}
}
return false
}

View File

@ -1,301 +0,0 @@
/*
Copyright 2020 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 x509metrics
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"net"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"k8s.io/component-base/metrics"
"k8s.io/component-base/metrics/testutil"
)
// taken from pkg/util/webhook/certs_test.go
var caCert = []byte(`-----BEGIN CERTIFICATE-----
MIIDGTCCAgGgAwIBAgIUOS2MkobR2t4rguefcC78gLuXkc0wDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMDEwMDcxMjMxNDFa
GA8yMjk0MDcyMzEyMzE0MVowGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMy8o1sRSe8viuSxh8a7Ou8n
vyDJxeJA9son+clZdSwa1YpbhSsYPr2svTGLT4ZxX3JkvHZGDaB+G6QrZNIWOssJ
4+fjvEsVSnk9q7Yr8wX8QdShksnSP0qdB1xFKlqwTFcZyQHqjlyctV93/LEosM9D
RZzgPpXJuC6wD7qdLRSPxQwuegZYcMGT2Y4/8CfBgiEUkZNrGYJiDFrblI0N9jX2
jUeP/g8xWJIKLTBedVbAzj02Y66WdNecYcyTMROdclPB1IbF4ABbCGynnP0fbiaf
0+4v0pLedqwCYSWD/ujajyDtYuhI8U+OkENwkZ/h5jw/6aWq7esMZDbEAT6UPgEC
AwEAAaNTMFEwHQYDVR0OBBYEFBgvyZWkRJCRjxclYA0mMlnzc/GmMB8GA1UdIwQY
MBaAFBgvyZWkRJCRjxclYA0mMlnzc/GmMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAHigaFJ8DqYFMGXNUT5lYM8oLLWYcbivGxR7ofDm8rXWGLdQ
tKsWeusSlgpzeO1PKCzwSrYQhlFIZI6AH+ch7EAWt84MfAaMz/1DiXF6Q8fAcR0h
QoyFIAKTiVkcgOQjSQOIM2SS5cyGeDRaHGVWfaJOwdIYo6ctFzI2asPJ4yU0QsA7
0WTD2+sBG6AXGhfafGUHEmou8sGQ+QT8rgi4hs1bfyHuT5dPgC4TbwknD1G8pMqm
ID3CIiCF8hhF5C2hjrW0LTJ6zTlg1c1K0NmmUL1ucsfzEk//c7GsU8dk+FYmtW9A
VzryJj1NmnSqA3zd3jBMuK37Ei3pRvVbO7Uxf14=
-----END CERTIFICATE-----`)
var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0Gi/3oXPU0oRP38xNAQ1js4Py1fVqy5OW+3rcaZONdUiiHJ4
6K1mIMKuEH2nrFAq2cFiXIm9prrSN8hyLY9lUguyAlecdHe6he/7/PAoX7NM8NX1
LzOfUotcO+iZtrnMVCut2vlqjsGsnxppLKnFnp17ycOy7yKCuQHowPln5xp6JkhR
F0bxDXIFFmve4uHHlRsWcEoNQRJZTaOMrfrFYfqbc5iBitI7/5xgMPZNq282TQDc
GeJmbFHEu/L1qO6uTg1LkbNbZKNfXeAU46K+eecGCwPyTMlDYuREdoVpH9GYW7Cx
OIRTYgCLWVok96yk2jeVCq21BbxZ8+NBJ2rhqQIDAQABAoIBAB8bY3gdVOLDrWti
2r8+2ZelHiplw9i3Iq8KBhiCkC3s0Ci5nV5tc070f/KqLrrDhIHYIYxaatpWDEaT
PqeaPa9PW5SJ6ypfLJINTfllB0Gxi4xvAxe2htNVRcETaM4jUWJG2r5SeBsywUdG
M+ictoiETRPCiBS1e/mNVWZoU5/kyMApP+5U9+THKhV4V2Pi2OHon/AeTP1Yc/1A
lTB9giQIysDK11j9zbpL2ICW9HSbbeLJlsw8/wCOdnPHFn5kun6EuesOF6MingvM
vL9ZHsh6N7oOHupqRvDPImM3QTYSuBe1hTJSvhi7hmnl9dolsBKKxJz0wjCO1N+V
wdPzrwECgYEA9PH+5RSWJtUb2riDpM0QB/Cci6UEFeTJbaCSluog/EH1/E+mOo2J
VbLCMhWwF8kJYToG0sTFGbJ1J1SiaYan7pxH2YIZkgXePC8Vj5WJnLhv05kKRanq
kOE1hVnaaCeFJFiLjDW2WeEaNLo6Ap1Qnb6ObzwV+0QvWCMUnVQwfjECgYEA2dCh
JKDXdsuLUklTc39VKCXXhqO/5FFOzNrZhPEo6KV2mY0BZnNjau4Ff2F6UJb6NMza
fFSLScEZTdbCv5lElGAxJueqC+p2LR3dS/1JfdWJ0zrP1BZa+MWr8O510Go/NOC2
/s5cR2aVBdJ2WK4d/3XShOr6W8T6hPisr5wFZPkCgYBptUIWpNLEAXZa5wRRC/pe
ItW8YkOoGytet0xr+rCvjNvWvpzzaf+Zz2KFcNyk9yqoHf2x2h9hnqV2iszok6dH
j4RmdwIIBaZJ/NvmMlfIHcSM4eAP/mtviPGrEgLyrhOEgv3+TXPbyAyiMrg0RqXy
3bjkgl7OKDfyZnlQCHRBEQKBgCfmLK6N/AoZzQKcxfmhOJMrI2jZdBw5vKqP6EqO
9oRvUuNbzgbbWjnLMhycWZCLp3emkts1jXJMOftlPLVmOQbI/Bf5Vc/q+gzXrKLv
2deAF0gnPMzH75AkfZObyt8Lp1pjU4IngQXfR6sSW3VxJ7OU/KQ2evf2hEF5YACn
HuHZAoGBAI+i6KI0WiWadenDctkuzLgxEPXaQ3oLBJjhE9CjpcdF6mRMWaU8lPgj
D1bo9L8wxvqIW5qIrX9dKnGgYAxnomhBNQn3C+5XDgtq6UiANalilqs3AoaWlQiF
WKaPuWf2T2ypFVzdzPl/0fsFUo8Rw5D4VO4nHeglOrkGQx+OdXA6
-----END RSA PRIVATE KEY-----`)
var serverCert = []byte(`-----BEGIN CERTIFICATE-----
MIIDHzCCAgegAwIBAgIUZIt+6zrmR41Be/CrcPHLsj3pCAQwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMDEwMDcxMjMxNDFa
GA8yMjk0MDcyMzEyMzE0MVowHzEdMBsGA1UEAwwUd2ViaG9va190ZXN0c19zZXJ2
ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQaL/ehc9TShE/fzE0
BDWOzg/LV9WrLk5b7etxpk411SKIcnjorWYgwq4QfaesUCrZwWJcib2mutI3yHIt
j2VSC7ICV5x0d7qF7/v88Chfs0zw1fUvM59Si1w76Jm2ucxUK63a+WqOwayfGmks
qcWenXvJw7LvIoK5AejA+WfnGnomSFEXRvENcgUWa97i4ceVGxZwSg1BEllNo4yt
+sVh+ptzmIGK0jv/nGAw9k2rbzZNANwZ4mZsUcS78vWo7q5ODUuRs1tko19d4BTj
or555wYLA/JMyUNi5ER2hWkf0ZhbsLE4hFNiAItZWiT3rKTaN5UKrbUFvFnz40En
auGpAgMBAAGjVTBTMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdJQQWMBQG
CCsGAQUFBwMCBggrBgEFBQcDATAaBgNVHREEEzARhwR/AAABgglsb2NhbGhvc3Qw
DQYJKoZIhvcNAQELBQADggEBAFXNoW48IHJAcO84Smb+/k8DvJBwOorOPspJ/6DY
pYITCANq9kQ54bv3LgPuel5s2PytVL1dVQmAya1cT9kG3nIjNaulR2j5Sgt0Ilyd
Dk/HOE/zBi6KyifV3dgQSbzua8AI9VboR3o3FhmA9C9jDDxAS+q9+NQjB40/aG8m
TBx+oKgeYHee5llKNTsY1Jqh6TT47om70+sjvmgZ4blAV7ft+WG/h3ZVtAZJuFee
tchgUEpGR8ZGyK0r/vWBIKHNSqtG5gdOS9swQLdUFG90OivhddKUU8Zt52uUXbc/
/ggEd4dM4X6B21xKJQY6vCnTvHFXcVJezV3g1xaNN0yR0DA=
-----END CERTIFICATE-----`)
var serverCertNoSAN = []byte(`-----BEGIN CERTIFICATE-----
MIIC+DCCAeCgAwIBAgIUZIt+6zrmR41Be/CrcPHLsj3pCAUwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMDEwMDcxMjMxNDFa
GA8yMjk0MDcyMzEyMzE0MVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Gi/3oXPU0oRP38xNAQ1js4Py1fVqy5O
W+3rcaZONdUiiHJ46K1mIMKuEH2nrFAq2cFiXIm9prrSN8hyLY9lUguyAlecdHe6
he/7/PAoX7NM8NX1LzOfUotcO+iZtrnMVCut2vlqjsGsnxppLKnFnp17ycOy7yKC
uQHowPln5xp6JkhRF0bxDXIFFmve4uHHlRsWcEoNQRJZTaOMrfrFYfqbc5iBitI7
/5xgMPZNq282TQDcGeJmbFHEu/L1qO6uTg1LkbNbZKNfXeAU46K+eecGCwPyTMlD
YuREdoVpH9GYW7CxOIRTYgCLWVok96yk2jeVCq21BbxZ8+NBJ2rhqQIDAQABozkw
NzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
KwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAGgrpuQ4n0W/TaaXhdbfFELziXoN
eT89eFYOqgtx/o97sj8B/y+n8tm+lBopfmdnifta3e8iNVULAd6JKjBhkFL1NY3Q
tR+VqT8uEZthDM69cyuGTv1GybnUCY9mtW9dSgHHkcJNsZGn9oMTCYDcBrgoA4s3
vZc4wuPj8wyiSJBDYMNZfLWHCvLTOa0XUfh0Pf0/d26KuAUTNQkJZLZ5cbNXZuu3
fVN5brtOw+Md5nUa60z+Xp0ESaGvOLUjnmd2SWUfAzcbFbbV4fAyYZF/93zCVTJ1
ig9gRWmPqLcDDLf9LPyDR5yHRTSF4USH2ykun4PiPfstjfv0xwddWgG2+S8=
-----END CERTIFICATE-----`)
// Test_checkForHostnameError tests that the upstream message for remote server
// certificate's hostname hasn't changed when no SAN extension is present and that
// the metrics counter increases properly when such an error is encountered
//
// Requires GODEBUG=x509ignoreCN=0 to not be set in the environment
func TestCheckForHostnameError(t *testing.T) {
tests := []struct {
name string
serverCert []byte
counterIncrease bool
}{
{
name: "no SAN",
serverCert: serverCertNoSAN,
counterIncrease: true,
},
{
name: "with SAN",
serverCert: serverCert,
},
}
// register the test metrics
x509MissingSANCounter := metrics.NewCounter(&metrics.CounterOpts{Name: "Test_checkForHostnameError"})
registry := testutil.NewFakeKubeRegistry("0.0.0")
registry.MustRegister(x509MissingSANCounter)
var lastCounterVal int
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tlsServer, serverURL := testServer(t, tt.serverCert)
defer tlsServer.Close()
client := tlsServer.Client()
req, err := http.NewRequest(http.MethodGet, serverURL.String(), nil)
if err != nil {
t.Fatalf("failed to create an http request: %v", err)
}
_, err = client.Transport.RoundTrip(req)
checkForHostnameError(err, x509MissingSANCounter)
errorCounterVal := getSingleCounterValueFromRegistry(t, registry, "Test_checkForHostnameError")
if errorCounterVal == -1 {
t.Fatalf("failed to get the error counter from the registry")
}
if tt.counterIncrease && errorCounterVal != lastCounterVal+1 {
t.Errorf("expected the Test_checkForHostnameError metrics to increase by 1 from %d, but it is %d", lastCounterVal, errorCounterVal)
}
if !tt.counterIncrease && errorCounterVal != lastCounterVal {
t.Errorf("expected the Test_checkForHostnameError metrics to stay the same (%d), but it is %d", lastCounterVal, errorCounterVal)
}
lastCounterVal = errorCounterVal
})
}
}
func TestCheckRespForNoSAN(t *testing.T) {
tests := []struct {
name string
serverCert []byte
counterIncrease bool
}{
{
name: "no certs",
},
{
name: "no SAN",
serverCert: serverCertNoSAN,
counterIncrease: true,
},
{
name: "with SAN",
serverCert: serverCert,
},
}
// register the test metrics
x509MissingSANCounter := metrics.NewCounter(&metrics.CounterOpts{Name: "Test_checkRespForNoSAN"})
registry := testutil.NewFakeKubeRegistry("0.0.0")
registry.MustRegister(x509MissingSANCounter)
var lastCounterVal int
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var tlsConnectionState *tls.ConnectionState
if tt.serverCert != nil {
block, _ := pem.Decode([]byte(tt.serverCert))
if block == nil {
t.Fatal("failed to parse certificate PEM")
}
serverCert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("failed to parse certificate: %v", err)
}
tlsConnectionState = &tls.ConnectionState{
PeerCertificates: []*x509.Certificate{serverCert},
}
}
resp := &http.Response{
TLS: tlsConnectionState,
}
checkRespForNoSAN(resp, x509MissingSANCounter)
errorCounterVal := getSingleCounterValueFromRegistry(t, registry, "Test_checkRespForNoSAN")
if errorCounterVal == -1 {
t.Fatalf("failed to get the error counter from the registry")
}
if tt.counterIncrease && errorCounterVal != lastCounterVal+1 {
t.Errorf("expected the Test_checkRespForNoSAN metrics to increase by 1 from %d, but it is %d", lastCounterVal, errorCounterVal)
}
if !tt.counterIncrease && errorCounterVal != lastCounterVal {
t.Errorf("expected the Test_checkRespForNoSAN metrics to stay the same (%d), but it is %d", lastCounterVal, errorCounterVal)
}
lastCounterVal = errorCounterVal
})
}
}
func testServer(t *testing.T, serverCert []byte) (*httptest.Server, *url.URL) {
rootCAs := x509.NewCertPool()
rootCAs.AppendCertsFromPEM(caCert)
cert, err := tls.X509KeyPair(serverCert, serverKey)
if err != nil {
t.Fatalf("failed to init x509 cert/key pair: %v", err)
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: rootCAs,
}
tlsServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(200)
w.Write([]byte("ok"))
}))
tlsServer.TLS = tlsConfig
tlsServer.StartTLS()
serverURL, err := url.Parse(tlsServer.URL)
if err != nil {
tlsServer.Close()
t.Fatalf("failed to parse the testserver URL: %v", err)
}
serverURL.Host = net.JoinHostPort("localhost", serverURL.Port())
return tlsServer, serverURL
}
func getSingleCounterValueFromRegistry(t *testing.T, r metrics.Gatherer, name string) int {
mfs, err := r.Gather()
if err != nil {
t.Logf("failed to gather local registry metrics: %v", err)
return -1
}
for _, mf := range mfs {
if mf.Name != nil && *mf.Name == name {
mfMetric := mf.GetMetric()
for _, m := range mfMetric {
if m.GetCounter() != nil {
return int(m.GetCounter().GetValue())
}
}
}
}
return -1
}

View File

@ -0,0 +1,216 @@
/*
Copyright 2020 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 x509metrics
import (
"crypto/x509"
"errors"
"net/http"
"reflect"
"strings"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/component-base/metrics"
"k8s.io/klog/v2"
)
var _ utilnet.RoundTripperWrapper = &x509DeprecatedCertificateMetricsRTWrapper{}
type x509DeprecatedCertificateMetricsRTWrapper struct {
rt http.RoundTripper
checkers []deprecatedCertificateAttributeChecker
}
type deprecatedCertificateAttributeChecker interface {
// CheckRoundTripError returns true if the err is an error specific
// to this deprecated certificate attribute
CheckRoundTripError(err error) bool
// CheckPeerCertificates returns true if the deprecated attribute/value pair
// was found in a given certificate in the http.Response.TLS.PeerCertificates bundle
CheckPeerCertificates(certs []*x509.Certificate) bool
// IncreaseCounter increases the counter internal to this interface
// Use the req to derive and log information useful for troubleshooting the certificate issue
IncreaseMetricsCounter(req *http.Request)
}
// counterRaiser is a helper structure to include in certificate deprecation checkers.
// It implements the IncreaseMetricsCounter() method so that, when included in the checker,
// it does not have to be reimplemented.
type counterRaiser struct {
counter *metrics.Counter
reason string
}
func (c *counterRaiser) IncreaseMetricsCounter(req *http.Request) {
if req != nil && req.URL != nil {
if hostname := req.URL.Hostname(); len(hostname) > 0 {
klog.Infof("invalid certificate detected while connecting to %q: %s", req.URL.Hostname(), c.reason)
}
}
c.counter.Inc()
}
// NewDeprecatedCertificateRoundTripperWrapperConstructor returns a RoundTripper wrapper that's usable within ClientConfig.Wrap.
//
// It increases the `missingSAN` counter whenever:
// 1. we get a x509.HostnameError with string `x509: certificate relies on legacy Common Name field`
// which indicates an error caused by the deprecation of Common Name field when veryfing remote
// hostname
// 2. the server certificate in response contains no SAN. This indicates that this binary run
// with the GODEBUG=x509ignoreCN=0 in env
//
// It increases the `sha1` counter whenever:
// 1. we get a x509.InsecureAlgorithmError with string `SHA1`
// which indicates an error caused by an insecure SHA1 signature
// 2. the server certificate in response contains a SHA1WithRSA or ECDSAWithSHA1 signature.
// This indicates that this binary run with the GODEBUG=x509sha1=1 in env
func NewDeprecatedCertificateRoundTripperWrapperConstructor(missingSAN, sha1 *metrics.Counter) func(rt http.RoundTripper) http.RoundTripper {
return func(rt http.RoundTripper) http.RoundTripper {
return &x509DeprecatedCertificateMetricsRTWrapper{
rt: rt,
checkers: []deprecatedCertificateAttributeChecker{
NewSANDeprecatedChecker(missingSAN),
NewSHA1SignatureDeprecatedChecker(sha1),
},
}
}
}
func (w *x509DeprecatedCertificateMetricsRTWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := w.rt.RoundTrip(req)
if err != nil {
for _, checker := range w.checkers {
if checker.CheckRoundTripError(err) {
checker.IncreaseMetricsCounter(req)
}
}
} else if resp != nil {
if resp.TLS != nil && len(resp.TLS.PeerCertificates) > 0 {
for _, checker := range w.checkers {
if checker.CheckPeerCertificates(resp.TLS.PeerCertificates) {
checker.IncreaseMetricsCounter(req)
}
}
}
}
return resp, err
}
func (w *x509DeprecatedCertificateMetricsRTWrapper) WrappedRoundTripper() http.RoundTripper {
return w.rt
}
var _ deprecatedCertificateAttributeChecker = &missingSANChecker{}
type missingSANChecker struct {
counterRaiser
}
func NewSANDeprecatedChecker(counter *metrics.Counter) *missingSANChecker {
return &missingSANChecker{
counterRaiser: counterRaiser{
counter: counter,
reason: "relies on a legacy Common Name field instead of the SAN extension for subject validation",
},
}
}
// CheckRoundTripError returns true when we're running w/o GODEBUG=x509ignoreCN=0
// and the client reports a HostnameError about the legacy CN fields
func (c *missingSANChecker) CheckRoundTripError(err error) bool {
if err != nil && errors.As(err, &x509.HostnameError{}) && strings.Contains(err.Error(), "x509: certificate relies on legacy Common Name field") {
// increase the count of registered failures due to Go 1.15 x509 cert Common Name deprecation
return true
}
return false
}
// CheckPeerCertificates returns true when the server response contains
// a leaf certificate w/o the SAN extension
func (c *missingSANChecker) CheckPeerCertificates(peerCertificates []*x509.Certificate) bool {
if len(peerCertificates) > 0 {
if serverCert := peerCertificates[0]; !hasSAN(serverCert) {
return true
}
}
return false
}
func hasSAN(c *x509.Certificate) bool {
sanOID := []int{2, 5, 29, 17}
for _, e := range c.Extensions {
if e.Id.Equal(sanOID) {
return true
}
}
return false
}
type sha1SignatureChecker struct {
*counterRaiser
}
func NewSHA1SignatureDeprecatedChecker(counter *metrics.Counter) *sha1SignatureChecker {
return &sha1SignatureChecker{
counterRaiser: &counterRaiser{
counter: counter,
reason: "uses an insecure SHA-1 signature",
},
}
}
// CheckRoundTripError returns true when we're running w/o GODEBUG=x509sha1=1
// and the client reports an InsecureAlgorithmError about a SHA1 signature
func (c *sha1SignatureChecker) CheckRoundTripError(err error) bool {
var unknownAuthorityError x509.UnknownAuthorityError
if err == nil {
return false
}
if !errors.As(err, &unknownAuthorityError) {
return false
}
errMsg := err.Error()
if strIdx := strings.Index(errMsg, "x509: cannot verify signature: insecure algorithm"); strIdx != -1 && strings.Contains(errMsg[strIdx:], "SHA1") {
// increase the count of registered failures due to Go 1.18 x509 sha1 signature deprecation
return true
}
return false
}
// CheckPeerCertificates returns true when the server response contains
// a non-root non-self-signed certificate with a deprecated SHA1 signature
func (c *sha1SignatureChecker) CheckPeerCertificates(peerCertificates []*x509.Certificate) bool {
// check all received non-self-signed certificates for deprecated signing algorithms
for _, cert := range peerCertificates {
if cert.SignatureAlgorithm == x509.SHA1WithRSA || cert.SignatureAlgorithm == x509.ECDSAWithSHA1 {
// the SHA-1 deprecation does not involve self-signed root certificates
if !reflect.DeepEqual(cert.Issuer, cert.Subject) {
return true
}
}
}
return false
}

View File

@ -0,0 +1,675 @@
/*
Copyright 2020 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 x509metrics
import (
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"net"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"testing"
"github.com/stretchr/testify/require"
"k8s.io/component-base/metrics"
"k8s.io/component-base/metrics/testutil"
)
// taken from pkg/util/webhook/certs_test.go
var caCert = []byte(`-----BEGIN CERTIFICATE-----
MIIDGTCCAgGgAwIBAgIUealQGELTHLUVcpsNNwz8XexiWvswDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKeW0Jkq6ViZkyhaLCUgbqsN
7+6HLwfZK/ljy/KnZ7W7QlJ65Q2tkptL0fY4DPumT7JgVTGnXyTXJ35Ss5A4yhm9
yyMH8pNVR19udK1fU74YVmbXJkc0nP7n+AXX9lD2Yy9pDvtaq1E+erN2nR1XaCS9
n0ph4C/fB1Rh7mIv/u7WW7/aRz/rJjBBZIbg7hgZPwFsukEifGi0U4uitVYR6MWp
jHj++e1G38+4JrZR9vhBoHtBJ1DBpmjAQaAtkSZAxXJnma4awE0Bv0Q4lxkUeY+D
th8OxPXxgTbwTKDaguHlWLTapppygA8FnKqmcUkwHZO5OVldi/ZKOUm2YCuJlfEC
AwEAAaNTMFEwHQYDVR0OBBYEFJGX6zVDm0Ur4gJJR+PKlgGdwhPfMB8GA1UdIwQY
MBaAFJGX6zVDm0Ur4gJJR+PKlgGdwhPfMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAJaRryxp0iYWGfLiZ0uIdOiYRVZUtmpqUSqT9/y29ffDAnCS
5labJS8FjaiQdlyaH+E9gzo0+nkO9NyfemJRLTEsU4Mz9AAvxs/NuWucqiyF0Y6d
JSYt7+2liGK5WvJMbHfW3jloWlv3oX+qL4iGFkJN+L9G+vf0GnKZCxgNIOqM4otv
cviCA9ouPwnSfnCeTsBoaUJqhLMizvUx7avvUyZTuV+t9+aN/qH4V//hTBqz9CNq
koodzngbUuyaGFI8QISUAhU+pbt6npb69LlhJHC0icDXCc4uTsd+SDsBOorZDuUL
HsNKnE0CSPZ65ENVNfJjB1fVEYbbpjr8kizOCDE=
-----END CERTIFICATE-----`)
var caCertInter = []byte(`-----BEGIN CERTIFICATE-----
MIIDMzCCAhugAwIBAgIUTJoqwFusJcupNCs/u39LBFrkZEIwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowKDEmMCQGA1UEAwwdd2ViaG9va190ZXN0c19pbnRl
cm1lZGlhdGVfY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDT9DIt
uNXhesrh8XtPXK4pR7xGReEsIlgLpMYf11PtFln9eV0HXvUO2CG/YvMxwgyd6Yoq
EfzD4rjmXvl5bQPMygmxf5GN1PM7ef7gVYuHfDgsQ4a82u1JFhKvuOrXn3QRfRg4
M4uYND7J4+Bg6J8oaA0yXIiMCpBi+XwEufo0RvgxM6mT+CeJ82hmlTKVhQJZZ9ZT
al1C4dTR2XeH5TLiIAvm+egBmSZhtCVn14rGk/PcHOWV7hdCxaFhSm7dSC+dR4zK
SxNleJ4Y+tZgoMfvgP/xHZEjbBzxnxyasES/Nc4nTgylcr6aqEX/fbcF0QzHpL9Z
ibkt1cBExU9zHuFJAgMBAAGjYDBeMB0GA1UdDgQWBBTfgUwjHsTOey7WqL4f3oFD
bmY77TAfBgNVHSMEGDAWgBSRl+s1Q5tFK+ICSUfjypYBncIT3zAPBgNVHRMBAf8E
BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEARYbIpIidgAVb
5ra9zd7F902+xC13/nmlrKL/dRMrRdZxk1kVVww3FbXSp7k7oHsih42KUCVDBevw
0ZZiolZlLneU57dEKKiTMkuPdVbNbIBPXIQpHLrXpVIR5BRRdRZ5OJZY24hYCvce
50XV8ITIU0R/U4sQ6NFHv8NJ5BB+2u1M3HF2LSKZFLnfP5FBcTCg84Jd6gEmTU2j
wZELnHy1AVdQnKMP9VrdAr9Wn6omWxAfO/PSb9YeKhGH5vtX+Bpb9bSPQIpXeBdB
LLCkme0M+1UsF7xua0KVi4DSuJc+RBl4aOH0ZvKmrIWzLzZhRS0vaO/fPArVCvvI
VrUba0E3WQ==
-----END CERTIFICATE-----`)
var caCertInterSHA1 = []byte(`-----BEGIN CERTIFICATE-----
MIIDMzCCAhugAwIBAgIUTJoqwFusJcupNCs/u39LBFrkZEMwDQYJKoZIhvcNAQEF
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowKDEmMCQGA1UEAwwdd2ViaG9va190ZXN0c19pbnRl
cm1lZGlhdGVfY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDT9DIt
uNXhesrh8XtPXK4pR7xGReEsIlgLpMYf11PtFln9eV0HXvUO2CG/YvMxwgyd6Yoq
EfzD4rjmXvl5bQPMygmxf5GN1PM7ef7gVYuHfDgsQ4a82u1JFhKvuOrXn3QRfRg4
M4uYND7J4+Bg6J8oaA0yXIiMCpBi+XwEufo0RvgxM6mT+CeJ82hmlTKVhQJZZ9ZT
al1C4dTR2XeH5TLiIAvm+egBmSZhtCVn14rGk/PcHOWV7hdCxaFhSm7dSC+dR4zK
SxNleJ4Y+tZgoMfvgP/xHZEjbBzxnxyasES/Nc4nTgylcr6aqEX/fbcF0QzHpL9Z
ibkt1cBExU9zHuFJAgMBAAGjYDBeMB0GA1UdDgQWBBTfgUwjHsTOey7WqL4f3oFD
bmY77TAfBgNVHSMEGDAWgBSRl+s1Q5tFK+ICSUfjypYBncIT3zAPBgNVHRMBAf8E
BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAAhMQTwrpAeIQ
nShHfTERiwg/tx3dL971d3pFS5wi4kEIbbYCUGpzkmK/FTw4hfUnLpwcjjAbOWkk
45glOmrLJXM4RvH5PQF3GZmZvxv8Dl4zuhH1QvWbJHUiC+gyrBWI0moyLSmNiutZ
d3TZGEehZGwivMdHHuhgiyFM4i33EQTW1vdMdOvdu8yNpAeXM2h1PcJwbEML9PO3
LONzVKhz/RsyEwv7PkX1gdmi6eyAE61BWJGwzxE4K0xIYmcr6iOjsJhEf/5Qc93P
IGSHsG/HjWwZ47gbobtv7L+8uKP/0ky+k1cE4nIB1gKYey+SYwvkQTsj24oG9xcL
XhgnIl+qDw==
-----END CERTIFICATE-----`)
var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA52b4byIJpUDyTKo5FiCa5Ekiy7CCd8UoleSomQjh5zZGsIbg
z9RqjaTMLF0jqbzh9ix2DQSnY+w32LqPM2sOK1+/atmeCa8m5bvZrRoDxP2T3pQH
Tye0C9WI7jqosmMquRFakaY1ODxDRPWF9CRghFF62NcHfnztW2rMiEYtuDRkZGsp
JL/B5OAzEgLr4iP8TKMlqSuhaQhi52dEZo0pJ1Ie4up8xxXKeoqfO3WSVDeRpj/n
0tYSALhOCdGn0RMav5wfZmxdfZpKUhBrcxFfeiDB5c7xdlnziHrY5lqSQPCHxAQb
S1jMaV4adhxDzF56t5RU6/5eWPZ4IvlTRtYmNwIDAQABAoIBAAb0r28XxNZ011O6
ojCqFj3afPNGgQV8pbWrw+2luLSsiv9vbn6Q0gsj8wc6XYISrXAq8fl+NFHqndsj
8H4JL8nZ/PUHSZrc6vxo4ygy6f4X6UP9iyKz/NOGPbF7jeqe1H/vp5tNNbhVB2ih
QL+QAF653El8XTtOIgxnb3KBOYqZ6e0rWvC5XlZrfT4EGqpokW4zQ6ROQUbnWyCk
LC4CtQpcLLd7fdGfA2cB2xDdGJ3Er8gAnU/X+tAtcghWanoNARKGU6opyGpwhipe
+31CivIUhtASWdbS73ay5QaDQSlgNM1/2hk5Beal7D9bGfKtwT/VGDSpKc4EKP8j
ktQSE0ECgYEA/jHMLQyvJ2VuqBdMI5hbaw5jzUAQwaJof5iQNuvFrbtWDeotAf+6
HomwoqzZ9+juiil4PHLQJzkArHwMWXbc+3FAznN1foS+YlOmIgJrjKa+EP+sz/X2
GxuyH3RD9+TH4EGd4TbeDr0eZOnIbKVybj4ueE+um7jtdLzYW2Y8iCcCgYEA6Qu6
x5WOQaPaEOQwEP5AqVBZnZl1ogeEVanlPYl6amPFFnlc41+M61p3ebwRqikaC9Dv
hePiOcTTJyt4h7qzgd5rJTjy5bNYDx9F61NGagF0xJLQiMnXM/TsoFABVWetLepG
DTzgvCf7wmB9QTgdLct7KyG4suDBJlEAvr70q3ECgYEAxx4pC0z5U4oAMYn2aZeq
XOUrxpcdySC4bOMMbQkpk1rBIStEUGGK4OsIw5VVNP5xBSdQ+UESzva3EWYmoloa
5pgjpNUKv62qGQnfhJqStt3S2yv8qfbI7xk14a/IokHDVGbyDn5VWgRI79G1322G
gtcQvcvlQjSNRbm8XXRrjFcCgYA66x1Awl3h2IQUSyyfzzgX1lmhz59+5HmfksGD
SlOpvCmi4fILBihBhHC6VUL+C0ArhppX9mJGiq17tLDXV+t0RQA/u+MlEa+MuzJZ
KYee21ljLV8NhkIjP6Pnb/K2XezZs+YcCK0kxNMQtIZWS9KMtmogYHkquEn83vPa
Rbrj8QKBgHifm6Z9F4qTz2b2RsYPoMHOdsX0DrZ8xQOH8jioTAy/Xi2hrL5Klp7h
zaLifWtOdtckFxIk+6D/zLLn1icC7cc8n4TMwQ1ikY+9IPnkTXVx4b/r/NSbAVxZ
J821mkhGdqKJGAzk6uh/Sn4rNGubH+I1x2Xa9hWbARCLsj8tp6TX
-----END RSA PRIVATE KEY-----`)
var serverCert = []byte(`-----BEGIN CERTIFICATE-----
MIIDHzCCAgegAwIBAgIUTJoqwFusJcupNCs/u39LBFrkZEQwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowHzEdMBsGA1UEAwwUd2ViaG9va190ZXN0c19zZXJ2
ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDnZvhvIgmlQPJMqjkW
IJrkSSLLsIJ3xSiV5KiZCOHnNkawhuDP1GqNpMwsXSOpvOH2LHYNBKdj7DfYuo8z
aw4rX79q2Z4Jryblu9mtGgPE/ZPelAdPJ7QL1YjuOqiyYyq5EVqRpjU4PENE9YX0
JGCEUXrY1wd+fO1basyIRi24NGRkaykkv8Hk4DMSAuviI/xMoyWpK6FpCGLnZ0Rm
jSknUh7i6nzHFcp6ip87dZJUN5GmP+fS1hIAuE4J0afRExq/nB9mbF19mkpSEGtz
EV96IMHlzvF2WfOIetjmWpJA8IfEBBtLWMxpXhp2HEPMXnq3lFTr/l5Y9ngi+VNG
1iY3AgMBAAGjVTBTMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdJQQWMBQG
CCsGAQUFBwMCBggrBgEFBQcDATAaBgNVHREEEzARhwR/AAABgglsb2NhbGhvc3Qw
DQYJKoZIhvcNAQELBQADggEBAAeUHlNJiGfvhi8ts96javP8tO5gPkN7uErIMpzA
N1rf5Kdy7/LsxM6Uvwn0ns+p1vxANAjR/c0nfu0eIO1t5fKVDD0s9+ohKA/6phrm
xChTyl21mDZlFKjq0sjSwzBcUHPJjzUW9+AMDvS7pOjR5h4nD21LlMIkBzinl5KT
uo2Pm/OZqepPdM5XH9DaW0T0tjXKvRFe4FklJSKGD7f+T1whtmyziyA84YjYVa/6
gF+gpIOmPruJI9UoFqEncNpLfh5vKu2Vxv+maztFRhb+9gOg+nVBq1pxmMZV0PuM
L+tz0avIZEO2+KhgVGF3AF8HSZQHYcaskGFSGc8FxDKcDjM=
-----END CERTIFICATE-----`)
var serverCertNoSAN = []byte(`-----BEGIN CERTIFICATE-----
MIIC+DCCAeCgAwIBAgIUTJoqwFusJcupNCs/u39LBFrkZEUwDQYJKoZIhvcNAQEL
BQAwGzEZMBcGA1UEAwwQd2ViaG9va190ZXN0c19jYTAgFw0yMjAzMjUxNTMzMjla
GA8yMjk2MDEwODE1MzMyOVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA52b4byIJpUDyTKo5FiCa5Ekiy7CCd8Uo
leSomQjh5zZGsIbgz9RqjaTMLF0jqbzh9ix2DQSnY+w32LqPM2sOK1+/atmeCa8m
5bvZrRoDxP2T3pQHTye0C9WI7jqosmMquRFakaY1ODxDRPWF9CRghFF62NcHfnzt
W2rMiEYtuDRkZGspJL/B5OAzEgLr4iP8TKMlqSuhaQhi52dEZo0pJ1Ie4up8xxXK
eoqfO3WSVDeRpj/n0tYSALhOCdGn0RMav5wfZmxdfZpKUhBrcxFfeiDB5c7xdlnz
iHrY5lqSQPCHxAQbS1jMaV4adhxDzF56t5RU6/5eWPZ4IvlTRtYmNwIDAQABozkw
NzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
KwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAGfCa0eCws/7+NYLJwVsdd7C/QHT
qbPw6w8oGnlXELMPwC701VFOcadhhengYCY1Kwa/KVu1ucFODDgp1ncvRoMVVWvD
/q6V07zu+aV/aW64zU27f+TzxTVXyCgfCSFUELJYBsBFWLw0K57ZDZdN2KJD+zD5
BAU0ghmy1DB+WSFMTqQ2iQaNX8oZh5jTZV3JtRNncqEqqIh4Nv7YYYZ02rgE7P2o
btVFYLBXHW7VYqnWpWM1pBfZpfGzMpGdR+1feST/88gUZh7ze15Ib4BlyU13v+0l
/BjuUsSWiITKWb2fqTiAkrqVbkOrC7Orz8yvgjuih4lEinQV1+KJUtcMmng=
-----END CERTIFICATE-----`)
var sha1ServerCertInter = []byte(`-----BEGIN CERTIFICATE-----
MIIDITCCAgmgAwIBAgIUaVjtC++/JGFoZRtkpo/j5q1nQ/4wDQYJKoZIhvcNAQEF
BQAwKDEmMCQGA1UEAwwdd2ViaG9va190ZXN0c19pbnRlcm1lZGlhdGVfY2EwIBcN
MjIwMzI1MTUzMzI5WhgPMjI5NjAxMDgxNTMzMjlaMBQxEjAQBgNVBAMMCWxvY2Fs
aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOdm+G8iCaVA8kyq
ORYgmuRJIsuwgnfFKJXkqJkI4ec2RrCG4M/Uao2kzCxdI6m84fYsdg0Ep2PsN9i6
jzNrDitfv2rZngmvJuW72a0aA8T9k96UB08ntAvViO46qLJjKrkRWpGmNTg8Q0T1
hfQkYIRRetjXB3587VtqzIhGLbg0ZGRrKSS/weTgMxIC6+Ij/EyjJakroWkIYudn
RGaNKSdSHuLqfMcVynqKnzt1klQ3kaY/59LWEgC4TgnRp9ETGr+cH2ZsXX2aSlIQ
a3MRX3ogweXO8XZZ84h62OZakkDwh8QEG0tYzGleGnYcQ8xeereUVOv+Xlj2eCL5
U0bWJjcCAwEAAaNVMFMwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYw
FAYIKwYBBQUHAwIGCCsGAQUFBwMBMBoGA1UdEQQTMBGHBH8AAAGCCWxvY2FsaG9z
dDANBgkqhkiG9w0BAQUFAAOCAQEATpiJFBwcRFIfZ9ffvS1WDzHqNElEnvocv/ul
3KVtoX4gmKRoOy344s3oJ5APPHYWUFuZVc3uofjW265r2uOW1Cb4P9yAtNc4htBS
+hYsdS3MQlzZCS9rItaT25R6Ieq5TbHGRCof387jzvo1NNhcAQ5akQlQKI87km77
VzoEBdAw68Q0ZE+X34Q9eAA44oCcLAgCpGvs6hQuUSInribSR3vtsjuaLjdJ5F1f
GCu2QGM4cVLaezmoa1J54ETZggT2xFw2IyWJ2g/kXFpo+HnoyaDrPthud3Pe5xEt
JMzX0s3jPSjfeAv34Pr37s0Or18r1bS1hrgxE0SV2vk31fsImg==
-----END CERTIFICATE-----`)
var serverCertInterSHA1 = []byte(`-----BEGIN CERTIFICATE-----
MIIDITCCAgmgAwIBAgIUfSzzygvth1xlpa9DtyGpHuY2V+swDQYJKoZIhvcNAQEL
BQAwKDEmMCQGA1UEAwwdd2ViaG9va190ZXN0c19pbnRlcm1lZGlhdGVfY2EwIBcN
MjIwMzI1MTUzMzI5WhgPMjI5NjAxMDgxNTMzMjlaMBQxEjAQBgNVBAMMCWxvY2Fs
aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOdm+G8iCaVA8kyq
ORYgmuRJIsuwgnfFKJXkqJkI4ec2RrCG4M/Uao2kzCxdI6m84fYsdg0Ep2PsN9i6
jzNrDitfv2rZngmvJuW72a0aA8T9k96UB08ntAvViO46qLJjKrkRWpGmNTg8Q0T1
hfQkYIRRetjXB3587VtqzIhGLbg0ZGRrKSS/weTgMxIC6+Ij/EyjJakroWkIYudn
RGaNKSdSHuLqfMcVynqKnzt1klQ3kaY/59LWEgC4TgnRp9ETGr+cH2ZsXX2aSlIQ
a3MRX3ogweXO8XZZ84h62OZakkDwh8QEG0tYzGleGnYcQ8xeereUVOv+Xlj2eCL5
U0bWJjcCAwEAAaNVMFMwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYw
FAYIKwYBBQUHAwIGCCsGAQUFBwMBMBoGA1UdEQQTMBGHBH8AAAGCCWxvY2FsaG9z
dDANBgkqhkiG9w0BAQsFAAOCAQEAwe/JUeIiJ5ugiO4tM0ZtvgHuFC3hK+ZWndRE
z4JfVXTW9soxpa/cOU9QdJhZzouIu9yqZasY4zSEerC1e6grBYP95vMbN6xUAown
wNzrQzyJ6yP526txiIdOkKf+yVNdz0OWNHMPtwTWIr8kKGK23ABF94aUa0VlkErp
Qrd8NQ3guIPI+/upuxirJCFdhE+U3U0pLHpGaGvhkOytfnLYiINwR9norVCDGbQG
ITH0tOz8gVWWWwxa9s5CmbqTnasgUMDh1jHa5xOo+riX8H5lwQUaItKU1JM+QMIR
6Z+M0Isdw647A6tmX7DqNcmHlBKxPN1GDcVXalwYJUoXwTb9Hw==
-----END CERTIFICATE-----`)
// Test_checkForHostnameError tests that the upstream message for remote server
// certificate's hostname hasn't changed when no SAN extension is present and that
// the metrics counter increases properly when such an error is encountered
//
// Requires GODEBUG=x509ignoreCN=0 to not be set in the environment
func TestCheckForHostnameError(t *testing.T) {
tests := []struct {
name string
serverCert []byte
counterIncrease bool
}{
{
name: "no SAN",
serverCert: serverCertNoSAN,
counterIncrease: true,
},
{
name: "with SAN",
serverCert: serverCert,
},
}
// register the test metrics
x509MissingSANCounter := metrics.NewCounter(&metrics.CounterOpts{Name: "Test_checkForHostnameError"})
registry := testutil.NewFakeKubeRegistry("0.0.0")
registry.MustRegister(x509MissingSANCounter)
sanChecker := NewSANDeprecatedChecker(x509MissingSANCounter)
var lastCounterVal int
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tlsServer, serverURL := testServer(t, tt.serverCert)
defer tlsServer.Close()
client := tlsServer.Client()
req, err := http.NewRequest(http.MethodGet, serverURL.String(), nil)
if err != nil {
t.Fatalf("failed to create an http request: %v", err)
}
_, err = client.Transport.RoundTrip(req)
if sanChecker.CheckRoundTripError(err) {
sanChecker.IncreaseMetricsCounter(req)
}
errorCounterVal := getSingleCounterValueFromRegistry(t, registry, "Test_checkForHostnameError")
if errorCounterVal == -1 {
t.Fatalf("failed to get the error counter from the registry")
}
if tt.counterIncrease && errorCounterVal != lastCounterVal+1 {
t.Errorf("expected the Test_checkForHostnameError metrics to increase by 1 from %d, but it is %d", lastCounterVal, errorCounterVal)
}
if !tt.counterIncrease && errorCounterVal != lastCounterVal {
t.Errorf("expected the Test_checkForHostnameError metrics to stay the same (%d), but it is %d", lastCounterVal, errorCounterVal)
}
lastCounterVal = errorCounterVal
})
}
}
func TestCheckRespForNoSAN(t *testing.T) {
tests := []struct {
name string
serverCert []byte
counterIncrease bool
}{
{
name: "no SAN",
serverCert: serverCertNoSAN,
counterIncrease: true,
},
{
name: "with SAN",
serverCert: serverCert,
},
}
// register the test metrics
x509MissingSANCounter := metrics.NewCounter(&metrics.CounterOpts{Name: "Test_checkRespForNoSAN"})
registry := testutil.NewFakeKubeRegistry("0.0.0")
registry.MustRegister(x509MissingSANCounter)
sanChecker := NewSANDeprecatedChecker(x509MissingSANCounter)
var lastCounterVal int
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var tlsConnectionState *tls.ConnectionState
if tt.serverCert != nil {
block, _ := pem.Decode([]byte(tt.serverCert))
if block == nil {
t.Fatal("failed to parse certificate PEM")
}
serverCert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("failed to parse certificate: %v", err)
}
tlsConnectionState = &tls.ConnectionState{
PeerCertificates: []*x509.Certificate{serverCert},
}
}
resp := &http.Response{
TLS: tlsConnectionState,
}
if sanChecker.CheckPeerCertificates(resp.TLS.PeerCertificates) {
sanChecker.IncreaseMetricsCounter(nil)
}
errorCounterVal := getSingleCounterValueFromRegistry(t, registry, "Test_checkRespForNoSAN")
if errorCounterVal == -1 {
t.Fatalf("failed to get the error counter from the registry")
}
if tt.counterIncrease && errorCounterVal != lastCounterVal+1 {
t.Errorf("expected the Test_checkRespForNoSAN metrics to increase by 1 from %d, but it is %d", lastCounterVal, errorCounterVal)
}
if !tt.counterIncrease && errorCounterVal != lastCounterVal {
t.Errorf("expected the Test_checkRespForNoSAN metrics to stay the same (%d), but it is %d", lastCounterVal, errorCounterVal)
}
lastCounterVal = errorCounterVal
})
}
}
func TestCheckForInsecureAlgorithmError(t *testing.T) {
tests := []struct {
name string
serverCert []byte
counterIncrease bool
}{
{
name: "server cert sha1-signed",
serverCert: append(append(sha1ServerCertInter, byte('\n')), caCertInter...),
counterIncrease: true,
},
{
name: "intermediate CA cert sha1-signed",
serverCert: append(append(serverCertInterSHA1, byte('\n')), caCertInterSHA1...),
counterIncrease: true,
},
{
name: "different error - cert untrusted, intermediate not in returned chain",
serverCert: serverCertInterSHA1,
},
{
name: "properly signed",
serverCert: serverCert,
},
}
// register the test metrics
x509SHA1SignatureCounter := metrics.NewCounter(&metrics.CounterOpts{Name: "Test_checkForInsecureAlgorithmError"})
registry := testutil.NewFakeKubeRegistry("0.0.0")
registry.MustRegister(x509SHA1SignatureCounter)
sha1checker := NewSHA1SignatureDeprecatedChecker(x509SHA1SignatureCounter)
var lastCounterVal int
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tlsServer, serverURL := testServer(t, tt.serverCert)
defer tlsServer.Close()
req, err := http.NewRequest(http.MethodGet, serverURL.String(), nil)
if err != nil {
t.Fatalf("failed to create an http request: %v", err)
}
// can't use tlsServer.Client() as it contains the server certificate
// in tls.Config.Certificates. The signatures are, however, only checked
// during building a candidate verification certificate chain and
// if the content of tls.Config.Certificates matches the certificate
// returned by the server, this short-circuits and the signature verification is
// never performed.
caPool := x509.NewCertPool()
require.True(t, caPool.AppendCertsFromPEM(caCert))
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
RootCAs: caPool,
},
},
}
_, err = client.Transport.RoundTrip(req)
if sha1checker.CheckRoundTripError(err) {
sha1checker.IncreaseMetricsCounter(req)
}
errorCounterVal := getSingleCounterValueFromRegistry(t, registry, "Test_checkForInsecureAlgorithmError")
if errorCounterVal == -1 {
t.Fatalf("failed to get the error counter from the registry")
}
if tt.counterIncrease && errorCounterVal != lastCounterVal+1 {
t.Errorf("expected the Test_checkForInsecureAlgorithmError metrics to increase by 1 from %d, but it is %d", lastCounterVal, errorCounterVal)
}
if !tt.counterIncrease && errorCounterVal != lastCounterVal {
t.Errorf("expected the Test_checkForInsecureAlgorithmError metrics to stay the same (%d), but it is %d", lastCounterVal, errorCounterVal)
}
lastCounterVal = errorCounterVal
})
}
}
func TestCheckRespSHA1SignedCert(t *testing.T) {
tests := []struct {
name string
serverCert []byte
counterIncrease bool
}{
{
name: "server cert sha1-signed",
serverCert: append(append(sha1ServerCertInter, byte('\n')), caCertInter...),
counterIncrease: true,
},
{
name: "intermediate CA cert sha1-signed",
serverCert: append(append(serverCertInterSHA1, byte('\n')), caCertInterSHA1...),
counterIncrease: true,
},
{
name: "properly signed",
serverCert: serverCert,
},
}
// register the test metrics
x509SHA1SignatureCounter := metrics.NewCounter(&metrics.CounterOpts{Name: "Test_checkRespSHA1SignedCert"})
registry := testutil.NewFakeKubeRegistry("0.0.0")
registry.MustRegister(x509SHA1SignatureCounter)
sha1checker := NewSHA1SignatureDeprecatedChecker(x509SHA1SignatureCounter)
var lastCounterVal int
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var tlsConnectionState *tls.ConnectionState
if tt.serverCert != nil {
peerCerts := []*x509.Certificate{}
for block, rest := pem.Decode([]byte(tt.serverCert)); block != nil; block, rest = pem.Decode(rest) {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("failed to parse certificate: %v", err)
}
peerCerts = append(peerCerts, cert)
}
tlsConnectionState = &tls.ConnectionState{
PeerCertificates: peerCerts,
}
}
resp := &http.Response{
TLS: tlsConnectionState,
}
if sha1checker.CheckPeerCertificates(resp.TLS.PeerCertificates) {
sha1checker.IncreaseMetricsCounter(nil)
}
errorCounterVal := getSingleCounterValueFromRegistry(t, registry, "Test_checkRespSHA1SignedCert")
if errorCounterVal == -1 {
t.Fatalf("failed to get the error counter from the registry")
}
if tt.counterIncrease && errorCounterVal != lastCounterVal+1 {
t.Errorf("expected the Test_checkRespSHA1SignedCert metrics to increase by 1 from %d, but it is %d", lastCounterVal, errorCounterVal)
}
if !tt.counterIncrease && errorCounterVal != lastCounterVal {
t.Errorf("expected the Test_checkRespSHA1SignedCert metrics to stay the same (%d), but it is %d", lastCounterVal, errorCounterVal)
}
lastCounterVal = errorCounterVal
})
}
}
func Test_x509DeprecatedCertificateMetricsRTWrapper_RoundTrip(t *testing.T) {
// register the test metrics
testCounter := metrics.NewCounter(&metrics.CounterOpts{Name: "testCounter"})
registry := testutil.NewFakeKubeRegistry("0.0.0")
registry.MustRegister(testCounter)
tests := []struct {
name string
checkers []deprecatedCertificateAttributeChecker
resp *http.Response
err error
counterIncrease bool
}{
{
name: "no error, resp w/ cert, no counter increase",
checkers: []deprecatedCertificateAttributeChecker{&testNegativeChecker{counterRaiser{testCounter, ""}}},
resp: httpResponseWithCert(),
},
{
name: "no error, resp w/o cert, no counter increase",
checkers: []deprecatedCertificateAttributeChecker{&testPositiveChecker{counterRaiser{testCounter, ""}}},
resp: httpResponseNoCert(),
},
{
name: "no error, resp w/ cert, counter increase",
checkers: []deprecatedCertificateAttributeChecker{&testPositiveChecker{counterRaiser{testCounter, ""}}},
resp: httpResponseWithCert(),
counterIncrease: true,
},
{
name: "unrelated error, no resp, no counter increase",
checkers: []deprecatedCertificateAttributeChecker{&testNegativeChecker{counterRaiser{testCounter, ""}}},
err: fmt.Errorf("error"),
},
{
name: "related error, no resp, counter increase",
checkers: []deprecatedCertificateAttributeChecker{&testPositiveChecker{counterRaiser{testCounter, ""}}},
err: fmt.Errorf("error"),
counterIncrease: true,
},
}
var lastCounterVal int
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &x509DeprecatedCertificateMetricsRTWrapper{
rt: newTestRoundTripper(tt.resp, tt.err),
checkers: tt.checkers,
}
got, err := w.RoundTrip(&http.Request{})
if err != tt.err {
t.Errorf("x509DeprecatedCertificateMetricsRTWrapper.RoundTrip() should not mutate the error. Got %v, want %v", err, tt.err)
return
}
if !reflect.DeepEqual(got, tt.resp) {
t.Errorf("x509DeprecatedCertificateMetricsRTWrapper.RoundTrip() = should not mutate the response. Got %v, want %v", got, tt.resp)
}
errorCounterVal := getSingleCounterValueFromRegistry(t, registry, "testCounter")
if errorCounterVal == -1 {
t.Fatalf("failed to get the error counter from the registry")
}
if tt.counterIncrease && errorCounterVal != lastCounterVal+1 {
t.Errorf("expected the testCounter metrics to increase by 1 from %d, but it is %d", lastCounterVal, errorCounterVal)
}
if !tt.counterIncrease && errorCounterVal != lastCounterVal {
t.Errorf("expected the testCounter metrics to stay the same (%d), but it is %d", lastCounterVal, errorCounterVal)
}
lastCounterVal = errorCounterVal
})
}
}
type testRoundTripper func(req *http.Request) (*http.Response, error)
func (rt testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return rt(req)
}
func newTestRoundTripper(resp *http.Response, err error) testRoundTripper {
return func(_ *http.Request) (*http.Response, error) {
return resp, err
}
}
type testPositiveChecker struct {
counterRaiser
}
func (c *testPositiveChecker) CheckRoundTripError(_ error) bool {
return true
}
func (c *testPositiveChecker) CheckPeerCertificates(_ []*x509.Certificate) bool {
return true
}
type testNegativeChecker struct {
counterRaiser
}
func (c *testNegativeChecker) CheckRoundTripError(_ error) bool {
return false
}
func (c *testNegativeChecker) CheckPeerCertificates(_ []*x509.Certificate) bool {
return false
}
func httpResponseWithCert() *http.Response {
return &http.Response{
TLS: &tls.ConnectionState{
PeerCertificates: []*x509.Certificate{
{Issuer: pkix.Name{CommonName: "a name"}},
},
},
}
}
func httpResponseNoCert() *http.Response {
return &http.Response{}
}
func testServer(t *testing.T, serverCert []byte) (*httptest.Server, *url.URL) {
rootCAs := x509.NewCertPool()
rootCAs.AppendCertsFromPEM(caCert)
cert, err := tls.X509KeyPair(serverCert, serverKey)
if err != nil {
t.Fatalf("failed to init x509 cert/key pair: %v", err)
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: rootCAs,
}
tlsServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(200)
w.Write([]byte("ok"))
}))
tlsServer.TLS = tlsConfig
tlsServer.StartTLS()
serverURL, err := url.Parse(tlsServer.URL)
if err != nil {
tlsServer.Close()
t.Fatalf("failed to parse the testserver URL: %v", err)
}
serverURL.Host = net.JoinHostPort("localhost", serverURL.Port())
return tlsServer, serverURL
}
func getSingleCounterValueFromRegistry(t *testing.T, r metrics.Gatherer, name string) int {
mfs, err := r.Gather()
if err != nil {
t.Logf("failed to gather local registry metrics: %v", err)
return -1
}
for _, mf := range mfs {
if mf.Name != nil && *mf.Name == name {
mfMetric := mf.GetMetric()
for _, m := range mfMetric {
if m.GetCounter() != nil {
return int(m.GetCounter().GetValue())
}
}
}
}
return -1
}

View File

@ -241,7 +241,10 @@ func (r *proxyHandler) updateAPIService(apiService *apiregistrationv1api.APIServ
CAData: apiService.Spec.CABundle,
},
}
clientConfig.Wrap(x509metrics.NewMissingSANRoundTripperWrapperConstructor(x509MissingSANCounter))
clientConfig.Wrap(x509metrics.NewDeprecatedCertificateRoundTripperWrapperConstructor(
x509MissingSANCounter,
x509InsecureSHA1Counter,
))
newInfo := proxyHandlingInfo{
name: apiService.Name,

View File

@ -34,6 +34,19 @@ var x509MissingSANCounter = metrics.NewCounter(
},
)
var x509InsecureSHA1Counter = metrics.NewCounter(
&metrics.CounterOpts{
Subsystem: "kube_aggregator",
Namespace: "apiserver",
Name: "x509_insecure_sha1_total",
Help: "Counts the number of requests to servers with insecure SHA1 signatures " +
"in their serving certificate OR the number of connection failures " +
"due to the insecure SHA1 signatures (either/or, based on the runtime environment)",
StabilityLevel: metrics.ALPHA,
},
)
func init() {
legacyregistry.MustRegister(x509MissingSANCounter)
legacyregistry.MustRegister(x509InsecureSHA1Counter)
}