diff --git a/common/auth/testdata/ca.crt b/common/auth/testdata/ca.crt new file mode 100644 index 00000000000..3e8cc5a9b3c --- /dev/null +++ b/common/auth/testdata/ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUKDtHndnyZJWPWmN6pDbf6i6YaYgwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzExMzAwNTA0MDlaFw0yMzEy +MzAwNTA0MDlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDVdCQkheGyNKVsbolQ1Ae+LN6bxxssB3/qcDZgCrUe +L1lAzXrX7QIOi81A96NKQd396N7uQDMcigCjMeKjV54CC3pz0WiZfYWGK0b1u74u +GXaz7QkMC1qrEjRZ8hbiw3Z+lluLleAy3vJMRkw8GjpqxyWk4VMwisGS3UvAWJjJ +73EQkE2/OHkNmmq0MiEqNIUgTdSBGwjIhuRkpT9TBHV53op7nN2shx31FVjR2pGl +F4oNV8itESRURs8tChrMZW2CBK7YRG4JmPJ/cXCx+1mZNbHwbC9grIWQB+9mCTd/ +auzUo9chWw/q64NWPNBvlvuuHXMQY2fHdiNiTmWlwK0zAgMBAAGjUzBRMB0GA1Ud +DgQWBBQ8VDnJd6HequH6Mq+3GWt1Xo1UKjAfBgNVHSMEGDAWgBQ8VDnJd6HequH6 +Mq+3GWt1Xo1UKjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCl +wCCE+mRZxycA+JH87n7Ngj+RB05jNVNZBoe3ACYFl+/sElDLsu3Fdwf4G9ou/Lvm +CUTShsEBd8bFiZUJ7GKJihLwDJ4XKiArCGKJxwwhTKYP9sh02QsXWI0sZ+7W8nYy +RK3d44ZrIg3ArChPWpGjRkVW/XY7SjlsYLQHSEGkL/9RymTYR2pd9s8Feqkzadlr +JmcEIEdSmmscp3P4I71n7lqWE22T9nRALs9J+Xd2oHnHKk4dq55AzlSp8837iOEO +v/nQg1MR2O8Ocpp2SRlbilmYfxdIk+KesYJeCwSbiLnrcIxwmubAgA7N5V7ZEkZZ +uEcg7LezqBLiETRmFkMN +-----END CERTIFICATE----- diff --git a/common/auth/testdata/ca.key b/common/auth/testdata/ca.key new file mode 100644 index 00000000000..868c452fa3c --- /dev/null +++ b/common/auth/testdata/ca.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDVdCQkheGyNKVs +bolQ1Ae+LN6bxxssB3/qcDZgCrUeL1lAzXrX7QIOi81A96NKQd396N7uQDMcigCj +MeKjV54CC3pz0WiZfYWGK0b1u74uGXaz7QkMC1qrEjRZ8hbiw3Z+lluLleAy3vJM +Rkw8GjpqxyWk4VMwisGS3UvAWJjJ73EQkE2/OHkNmmq0MiEqNIUgTdSBGwjIhuRk +pT9TBHV53op7nN2shx31FVjR2pGlF4oNV8itESRURs8tChrMZW2CBK7YRG4JmPJ/ +cXCx+1mZNbHwbC9grIWQB+9mCTd/auzUo9chWw/q64NWPNBvlvuuHXMQY2fHdiNi +TmWlwK0zAgMBAAECggEAAUnDpBLIw2YM74QVQQ8egtfLeh4v/EqJS5VkxpyZ7jYz +8C+tQ5tTDVproS49ZofSOqOKoN5zUINGsLVu3DR9qB6AnS1Q18hJo38kXPxGkAmo +TTqreM6iu/COr1JGMBUk5Gus3sFHSpdM2Af+kshHvPg5sm6HrX3Q4D2EpQMD3QgZ +kCkkpvnX6ISkbE1ZrqIKLv86mmFyCsJIqiCIGnI4snNRPtq0nSqMr5Y4iluBX1rP +Wq+haLvSe+16rmLEIELONMK+q2aL33q6SZj3tgbFzJPqPzG+c5QASZgE1xW42L6G +6NF21yX/regwLDe/FKwpFo2rNgKTkd5XStM0u1Gg8QKBgQDzvREgx5PVp0YDtN7K +0If8CF75iyMVyvBwj2Jbb+J8pau9hZFeftez9M0FH2JJCcWcY+CgeyT6QzNnuRLT +ZDzTnR7jrnKVzk+0GwxdMRA5sLR5ZnL9ntG5z/wx34hfN+iCZbpuVneokAGOwzU3 +RKR/JPuVax2iJNX+JGixs3bzgwKBgQDgMQ6rbxHHvLWIXkyNDnITQ65i0bB5fi+V +jKOCfFQLKkcQKtR/lcqyFbQEWrvGl/csc21i3Ke8MND8vSSUvVIMa5BlxQcRf9U1 +HqWayhJl6VZKRLhfLqDzfx6N4g2sSTWdBXyQNCz7UfANjjo3ew3Tx8CaNma7UHp/ +wPkVq4BAkQKBgQCtAy/9TK2roykXyHO9E3jzdh20lQc6mOtDewQeU7U5NYBK71zY +7sC+hK5jTHu/zKfw8xSn1cHRw5HhtoWeeDCVJZqfk1zS6be56NlF7WXPDZeN9SL6 +1JmahABIhpuOu2jX5s4HNGmLxWzVoctMh/UcK2xKC9blGbCszbtjKDjvcQKBgG4g +iwU5dTF5iG7pa48q1mySJZqTSK4Vh0heIn/ZlOs7JFdTwri9mykpshklfPIL+jYS +qVwT6i1uiWptewe5jBFf0Tm8tEErW09Rs6W6t5jqKyImaa8P165k9lxZ+79y4uw0 +IsEvcB0wXcw63mwcfWlXyUu3h3ViDhqtb23upfyRAoGAEe/uw3ZO1PWKCvSBdiZ8 +ocBEjWGS1WO5rooZQT/wvOCCJS+Dr9jag0j+jqmiTgX87beBanNu6x6kKazTBPbN +iqsHC/XuwfYQ0Q7vfsLb5+X652GrT1R4xTYKxKgO6iTRdHF6stMuMJHcTbf+pmGr +LbIRficccAExEnzju0VjK5g= +-----END PRIVATE KEY----- diff --git a/common/auth/testdata/invalid_ca.crt b/common/auth/testdata/invalid_ca.crt new file mode 100644 index 00000000000..92a95aa18ad --- /dev/null +++ b/common/auth/testdata/invalid_ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIUL1JuLt+GQqcn3JCdcbiLbn0fJ8AwDQYJKoZIhvcNAQEL +BQAwaDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT +B1NlYXR0bGUxDTALBgNVBAoTBFVuaXQxDTALBgNVBAsTBFRlc3QxFDASBgNVBAMT +C1VuaXRUZXN0IENBMB4XDTIwMDkxNzE3MzUwMFoXDTI1MDkxNjE3MzUwMFowaDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0 +bGUxDTALBgNVBAoTBFVuaXQxDTALBgNVBAsTBFRlc3QxFDASBgNVBAMTC1VuaXRU +ZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtg59He605eb2 +q8FaJrpphESOfrbTGHBXWFN5gCuAIY2eMMGJJqpXDkR3FZJ6LVZaqdVokZi3yTH9 +kYnnLHAD2I+7y3Ags0tYfnrlt0hmZ3eyYQHi4cWtVGwiZ2qm2BvLo2U0CdytRJ4Q +3eAT2yTkNvxZoWyHG+Obr6xPPr28vmj7CKqVsKCAHVyjuyrmtIpwdmeiU9Em1SMH +IPKGJICoMxix5sCtujfdRMbSShHDYnRgf2Lvzr1VNffGZKMXzBZzI2gpIfoXhfUS +dvceQ5hYz8zgDchC8mehC7mMv3+zCwz9kFnbibpoIWFq+Fo63xsgsnytYPMv4rim +X0IdpfP6UQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUe3cC/2YkMdfEFTmIb1o83U4Uh1cwDQYJKoZIhvcNAQELBQAD +ggEBACvM5TDoA3AEDYkpYnyl0VZfD7JEXVHByY12xocPs8Lbs4KMKSmPeewGHSnV +++AWEto/ZQcRuUroRGfED4SSy2OKr4hx3BtRcFFFktX8Se0rNkJ+ZHuhTPVud9/M +QtAzyvQedp0WBW2t0oZHCqSNZc/IaXXcqy7hpzK8pKe3NQv2RGGVA2mdCGZ1PNk2 +EMxLVxhQDdm3JEbxHBO4+UZn90uCwPFsnkTQf6nwY1+23LsyaxY1YqdyvGO8ctG4 +z3RdSm5I3nWi3DDWwNxngYi0+A/MUCAAb0Nz9nIr7w0yRRiXrakXTb9Z8/FXMItq +tnprBs+xac8AUlspL7p+TZdT0WM= +-----END CERTIFICATE----- diff --git a/common/auth/testdata/invalid_localhost.crt b/common/auth/testdata/invalid_localhost.crt new file mode 100644 index 00000000000..505338479b6 --- /dev/null +++ b/common/auth/testdata/invalid_localhost.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDOTCCAiGgAwIBAgIUfqTjOyjzNUgjAVuBnuTUa6HLXy0wDQYJKoZIhvcNAQEL +BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l +cmF0ZWQgQ0EwHhcNMjMxMTE4MDc1NjE0WhcNMjYxMTE3MDc1NjE0WjAUMRIwEAYD +VQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCD +CskLZV02cYzXgmhF8ONylTFZNGbFeAHm0zt2TrDHIMIH8nO2ZnqbE5LGkdrzTmRS +JLEJMCgWjCAlL2KKw4Y5m5vw7bZmYE5T8DyvCdew/XRnVKumGPXfwG9IA1IXjlu+ +OUiTuzefdDzwy/Nd+uYQnAr2BwhT8orQfySFSF+fNGYeqhxN70iePKzTQMf/JpgS +aBoWatY7JLTLX/XMmuukrk+CMI6AMRYae5A8RcVfpnow9sn2q8oek9psbLrlYDgx +4o/X2nXbHOHkkLLsXwRG3Kub3gdL7PPqH7EXaIoR9Ftds7c1u5hbuZWEydQavUul +oc2iFX0V86hiYaP3u2KJAgMBAAGjYzBhMB0GA1UdDgQWBBSRVLi3tsILeNVwSc3H +DttvrGYR1DAfBgNVHSMEGDAWgBRR/1/f5vyVpI6WVXYMOADpFzFbfTAUBgNVHREE +DTALgglsb2NhbGhvc3QwCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEAa6sC +6y0MfxWkF0DR8YV9RvdbgK/s1azp8dw2vbL/JeLLZ4RtzAVMo34yqj6fmbIcF2OX +L+FPCCavo3J1P6ZvJZeKMvd84KHS2PUV/DF1EuVBHI0O9Eff3PCKESVfTKoPnylI +c+69/CzocJL1H6ZiCTG1ePOmZfSX5Gg7x9rc9JxWfiVuHcpRZu34iHiOlB194caR +L1/TwdbxrcoyAMParADE/mvt4jMt8dY7jz0Ga46NyFE2JVkzqK2ZFlFxQ61t6oYc +ei777qLYpQPulDGVEo3gtwHvNjXCCGq2MC6G3Bc0leVQPFAuTOavB8sQQ6txzWJ8 ++7BE/QZfkrIFO8aJgA== +-----END CERTIFICATE----- diff --git a/common/auth/testdata/invalid_localhost.key b/common/auth/testdata/invalid_localhost.key new file mode 100644 index 00000000000..1c5ba07c5db --- /dev/null +++ b/common/auth/testdata/invalid_localhost.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAgwrJC2VdNnGM14JoRfDjcpUxWTRmxXgB5tM7dk6wxyDCB/Jz +tmZ6mxOSxpHa805kUiSxCTAoFowgJS9iisOGOZub8O22ZmBOU/A8rwnXsP10Z1Sr +phj138BvSANSF45bvjlIk7s3n3Q88MvzXfrmEJwK9gcIU/KK0H8khUhfnzRmHqoc +Te9Injys00DH/yaYEmgaFmrWOyS0y1/1zJrrpK5PgjCOgDEWGnuQPEXFX6Z6MPbJ +9qvKHpPabGy65WA4MeKP19p12xzh5JCy7F8ERtyrm94HS+zz6h+xF2iKEfRbXbO3 +NbuYW7mVhMnUGr1LpaHNohV9FfOoYmGj97tiiQIDAQABAoIBAH50B/HRTRPem3TT +yN/FRxcpVUepw4rG9b7TE8xkvz5JJDQbCQJ4/dNsHfU0hr7HZRPHiHc/W0/2xIZd +pZAWgg5RVTg3jA5a3PsvFsAqlVOcIZodIM7Vw166CijJ24wTugBkst6siU58Etqj +VSPZm+1nR0SHIMgxgzcTmiBr6KptumYxe/6OS4A5Fac54hVQaHr3LpEi2EofZpOH +EPBp6fMqMGc6w5YbI6n+3md0xjLtuZtLPz6EgWC0Dm8EgnsJHif3hd4AAflAfEtL +hsma041FuP9Ty/lhHnDxGVD/Q2Jp9xnDf+GkDDJlDPogl0ezXthg0wWryWLTW/xP +MfGrPKECgYEAvkEA52sQc3as6Iw8F4S9dvnmhBqQXlg7q7CZg3/+wcMqH+NM7gw9 +IL8hqgHm0kR0WpBHLWewha6SbvS9DQ0zinOSJjnxpel6toEOshGNn8eBzcHj3ajg +CQhbb14LJlhdUkE2hzH56YPk6SrkCTOx0gRWfNs5SF6VZ6ieE6QqdP0CgYEAsFON +wK+QzePzUoNFEANLPXdb5LDStvtWOuP5v4tROgL780CCvXDXWTcJ+aHtvwr0rHGB +/0XJocMSVel0M10WGCz2PcNrDQfECVJ1Z+LyoSz9hy5GxMxDHsSn2vZuAkSbaMog +jDr0YUyFEaZ0Pfnr+tuLobWYsrnsZ7ujK83uP30CgYAZ3OJPkzlYSOC2eCHPHKdY +3h3dBXNsr9fkvgwQWTz7gBlg3lhd3+ILapAb+Ug0e0PJ7+UNIhRYB+Q2GyT7+jhw +cMeEVMopwLSCzMZ/pG05NDjMDLh6MHPMJouvdvxTCR4eiWjtNfkAKoLaw47eRV29 +7AMJ1A9UbC8pOT3L87wlzQKBgFN7q9DADwo8U/cMKcW0GYqHmZ2ETq/N/fyx9YxH +9PRHd2yrbXsuEvethG6JyVu7Xy7KKydmrlmFV4gPmTG8b/qQRyHlBSlNNPbI8Md3 +/zJqb7ryIRWKNHk72NFl/Z3bR81sbc6XFx9+MD60FbNGQgEqs0ikByEPwCs4/r99 +p8CtAoGBAJ72FQFEZxFNdNOCt1KUtU6RP0nH+13LiYdkghcB/xTjjeZMzFitXzVQ +wd/F9Es63pKHZ0RC682wYY998P3osUYy6WxHpCT+sweAPEFBOQl4OELnG5eJnA4P +OOaZlUJYZq6k+sORTMFtngSFP9cKIw5XvIkWZ38EvHlbxTkYCpRj +-----END RSA PRIVATE KEY----- diff --git a/common/auth/testdata/localhost.crt b/common/auth/testdata/localhost.crt new file mode 100644 index 00000000000..a9bb7ac36d4 --- /dev/null +++ b/common/auth/testdata/localhost.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDRTCCAi2gAwIBAgIUJEH/lvfvYxNKFBI0M9IQ5UxjAHwwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzExMzAwODM4MDRaFw0yNjAy +MDcwODM4MDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAOw8p3Tctq5ZfWfROoe2iOToaOwVrZQnH0sCekCQ2UsH +iGy8sNxCo+Zvkffn9BzfRV5RcRNwGYzTEUNmMNxTv7evQAVYtM2QcIGXinH0uk+e +P/LZ9nLmQgculsfxGZXv/QGHoK9Rzn4ZmGRgvwC7R9UPOI5alrFNkNbkdvqNndVz +WqWpAaIsSfCn3A4fPKzg5HZSbfMIw4b2lknOvMWMv9/84b+csddHW/MvMruWAFa/ +1WIJVZ3NYcNoyZNBp8vTLY4ByUVLWiBEvaY8v9j4vEexiWCK9SJUrwhJVy9b9raa +TcmyzEBKosSQh1zbC8pnA+pQaDEpJbggpGsf3yhRnYkCAwEAAaNeMFwwGgYDVR0R +BBMwEYIJbG9jYWxob3N0hwR/AAABMB0GA1UdDgQWBBSOaKT91dwYEWYu/227BYcq +/I0YhTAfBgNVHSMEGDAWgBQ8VDnJd6HequH6Mq+3GWt1Xo1UKjANBgkqhkiG9w0B +AQsFAAOCAQEAIHOkBrzW/7cnLu/BOCNhcKvbPU4aWG6aNHVsgprZcVMmhXi4Ss35 +8OdjTcLeIeznYRE5fPvzXpyLIzpSqMO925tSTuv89uwpGRyVyIVqRAzM5rT/lg99 +tR03kT3EYJyS7JWVsuC2H3uHDC15EpfgiIKCVrACQz1KPEeVrNyugCeXSDYxhF4S +xpEDsCZ2AhJ64RA6znh61LJSS7muPwNnbf3O5HzLw1Oxj1DpUduO+5Q0S53wn5gI +riHGoaVqMPXD3bINnVJ3qzQLbnMaVQqJFaJl8asRuoznO7lNX7fcU/7ArQP2h8VU +z7FQc8hQyUwmTr9OjtF3jNd/gPQXq1e/AA== +-----END CERTIFICATE----- diff --git a/common/auth/testdata/localhost.key b/common/auth/testdata/localhost.key new file mode 100644 index 00000000000..9faa9e477fe --- /dev/null +++ b/common/auth/testdata/localhost.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDsPKd03LauWX1n +0TqHtojk6GjsFa2UJx9LAnpAkNlLB4hsvLDcQqPmb5H35/Qc30VeUXETcBmM0xFD +ZjDcU7+3r0AFWLTNkHCBl4px9LpPnj/y2fZy5kIHLpbH8RmV7/0Bh6CvUc5+GZhk +YL8Au0fVDziOWpaxTZDW5Hb6jZ3Vc1qlqQGiLEnwp9wOHzys4OR2Um3zCMOG9pZJ +zrzFjL/f/OG/nLHXR1vzLzK7lgBWv9ViCVWdzWHDaMmTQafL0y2OAclFS1ogRL2m +PL/Y+LxHsYlgivUiVK8ISVcvW/a2mk3JssxASqLEkIdc2wvKZwPqUGgxKSW4IKRr +H98oUZ2JAgMBAAECggEACmgzI9iv0w818TeETbN+xR/QdHsRgCX3kNNBfbvr2Kxy +qACphXCJniaS3oIZTNbQrGKn2bh95ahsOaUEOMaXOvE1X67sWyR2cl0R0Cqc9RI2 +hL0xqFbouH6GzbTbQNHXWmSGUYbn8wHvZtekvnRhqhsPLaVDv3iYbYEX1TqdJgim +2mqpfmHxVih5iaTlcgvTyCK7fSg7QjQd+wuZOHg6oxLiSrxXTYNi7QIS+nh5i4v7 +1gxpAjkbLxVm4OSeQZc5ZDE4aMqS8zJ9vUsQDgnPz+m+R8hVlZD+QHVexNa6+FiI +GlGAjq/1uxaNAbE/PCnw79OM8h5dhYesFSqeFsd0wQKBgQD/F9uiAoktkKrouEla +Xur/QMkrANJXq3JrBgJATLT5UlRidsmDyTI1tTmqNUVSHP74nQaU/D8MWvtauA32 +SzD8BsNcFJPvF0hZdQ/mOWWtzU8hTPGvZsm5zN8bGXulKqApHod/JqPaTrndFOIR +nZG7JtefUK7wzpF6v3B7VipOyQKBgQDtE6LuUneQ1VvjG/wGr7HKTRwkglB0CwKE +kBSvGKbow5buLWGJEKX5avhRBlBTEkgjW9aDK9kcyWQdcUws0sEeWY7c6bGT6O0O +olAZPu+9lJeoWT0LtQp4QbX14o8PDIlktmBkWRql61V8yH+sNZLZysbvDmcbrx6y +mg4tWo54wQKBgD1L+lbfzR7J2Ie7YMT6cfWMF8rhk6nFZOUauIfC5unSQry2Vo34 +VrxZI9YmntWwagRLlz1Nr8jERAn0mEZKoyasaV1DBJk7OwNkAcIMoMY2w4D5AEpp +pKek9yeH5BI4P+whjeWb31uh9z+MuRYKiu4x0zZRKPhsasDYJ6s7dUchAoGBAJhG +x52KjNATq0yqGex1h54of04YAfM3ayuYnCCHlhXmI5jiv0ZZ0jHteogYpRlmob1M +bPGeBXuPA+ZBcqtLx81luvS8ilo1/6YprYc5vKWpvuvcPfC6HXp2wpioKDmDVPDC +kBGEhjBSg3t+ETucwbFwpOzB9Ip8phwUBc0t4FtBAoGAGcUGBMNXnTfy98GjO+Dx +vy4cMtauq6G2cPK7CQWrh+n6UqH+gTcwcq9BXkCHU81+CT9APE5siHDfVlhrvnnK +2Wl8wGZ7due+yiVqIlVwqK0rcmFgNNWpo/3sOz2qRocVk47Ot5n8UeW41GIWk0AW +55n1QieOvXAN2pPotqwEd54= +-----END PRIVATE KEY----- diff --git a/common/auth/tls_config_helper.go b/common/auth/tls_config_helper.go index 0a5d576f878..40abbd1c400 100644 --- a/common/auth/tls_config_helper.go +++ b/common/auth/tls_config_helper.go @@ -27,11 +27,18 @@ package auth import ( "crypto/tls" "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" + "fmt" + "os" "go.temporal.io/server/common/log" "go.temporal.io/server/common/log/tag" ) +var ErrTLSConfig = errors.New("unable to config TLS") + // Helper methods for creating tls.Config structs to ensure MinVersion is 1.3 func NewEmptyTLSConfig() *tls.Config { @@ -95,3 +102,148 @@ func tlsCN(state tls.ConnectionState) string { } return state.PeerCertificates[0].Subject.CommonName } + +func NewTLSConfig(temporalTls *TLS) (*tls.Config, error) { + if temporalTls == nil || !temporalTls.Enabled { + return nil, nil + } + err := validateTemporalTls(temporalTls) + if err != nil { + return nil, err + } + + tlsConfig := &tls.Config{ + InsecureSkipVerify: !temporalTls.EnableHostVerification, + } + if temporalTls.ServerName != "" { + tlsConfig.ServerName = temporalTls.ServerName + } + + // Load CA cert + caCertPool, err := parseCAs(temporalTls) + if err != nil { + return nil, err + } + if caCertPool != nil { + tlsConfig.RootCAs = caCertPool + } + + // Load client cert + clientCert, err := parseClientCert(temporalTls) + if err != nil { + return nil, err + } + if clientCert != nil { + tlsConfig.Certificates = []tls.Certificate{*clientCert} + } + + return tlsConfig, nil +} + +func validateTemporalTls(temporalTls *TLS) error { + if temporalTls.CertData != "" && temporalTls.CertFile != "" { + return fmt.Errorf("%w: %s", ErrTLSConfig, "only one of certData or certFile properties should be specified") + } + + if temporalTls.KeyData != "" && temporalTls.KeyFile != "" { + return fmt.Errorf("%w: %s", ErrTLSConfig, "only one of keyData or keyFile properties should be specified") + } + + certProvided := temporalTls.CertData != "" || temporalTls.CertFile != "" + keyProvided := temporalTls.KeyData != "" || temporalTls.KeyFile != "" + if certProvided != keyProvided { + return fmt.Errorf("%w: %s", ErrTLSConfig, "cert or key is missing") + } + + if temporalTls.CaData != "" && temporalTls.CaFile != "" { + return fmt.Errorf("%w: %s", ErrTLSConfig, "only one of caData or caFile properties should be specified") + } + return nil +} + +func parseCAs(temporalTls *TLS) (*x509.CertPool, error) { + var caBytes []byte + var err error + if temporalTls.CaFile != "" { + caBytes, err = os.ReadFile(temporalTls.CaFile) + if err != nil { + return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to read client ca file", err) + } + } else if temporalTls.CaData != "" { + caBytes, err = base64.StdEncoding.DecodeString(temporalTls.CaData) + if err != nil { + return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to decode client ca data", err) + } + } + if len(caBytes) > 0 { + caCertPool := x509.NewCertPool() + caCerts, err := parseCertsFromPEM(caBytes) + if len(caCerts) == 0 { + return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to parse certs as PEM", err) + } + for _, cert := range caCerts { + caCertPool.AddCert(cert) + } + if err != nil { + return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to load decoded CA Cert as PEM", err) + } + return caCertPool, nil + } + return nil, nil +} + +func parseCertsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) { + for len(pemCerts) > 0 { + var block *pem.Block + block, pemCerts = pem.Decode(pemCerts) + if block == nil { + break + } + if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { + continue + } + + certBytes := block.Bytes + return x509.ParseCertificates(certBytes) + } + return nil, nil +} + +func parseClientCert(temporalTls *TLS) (*tls.Certificate, error) { + var certBytes []byte + var keyBytes []byte + var err error + if temporalTls.CertFile != "" { + certBytes, err = os.ReadFile(temporalTls.CertFile) + if err != nil { + return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to read client certificate file", err) + } + } else if temporalTls.CertData != "" { + certBytes, err = base64.StdEncoding.DecodeString(temporalTls.CertData) + if err != nil { + return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to decode client certificate", err) + } + } + + if temporalTls.KeyFile != "" { + keyBytes, err = os.ReadFile(temporalTls.KeyFile) + if err != nil { + return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to read client certificate private key file", err) + } + } else if temporalTls.KeyData != "" { + keyBytes, err = base64.StdEncoding.DecodeString(temporalTls.KeyData) + if err != nil { + return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to decode client certificate private key", err) + } + } + + if len(certBytes) > 0 { + clientCert, err := tls.X509KeyPair(certBytes, keyBytes) + if err != nil { + return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to generate x509 key pair", err) + } + + return &clientCert, nil + } + return nil, nil +} diff --git a/common/auth/tls_config_helper_test.go b/common/auth/tls_config_helper_test.go new file mode 100644 index 00000000000..3ed8d36fcb2 --- /dev/null +++ b/common/auth/tls_config_helper_test.go @@ -0,0 +1,334 @@ +// The MIT License +// +// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. +// +// Copyright (c) 2020 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package auth + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const validBase64CaData = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURhekNDQWxPZ0F3SUJBZ0lVS0R0SG5kbnlaSldQV21ONnBEYmY2aTZZYVlnd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1JURUxNQWtHQTFVRUJoTUNRVlV4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQpHRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpEQWVGdzB5TXpFeE16QXdOVEEwTURsYUZ3MHlNekV5Ck16QXdOVEEwTURsYU1FVXhDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1TRXcKSHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdnZ0VpTUEwR0NTcUdTSWIzRFFFQgpBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRFZkQ1FraGVHeU5LVnNib2xRMUFlK0xONmJ4eHNzQjMvcWNEWmdDclVlCkwxbEF6WHJYN1FJT2k4MUE5Nk5LUWQzOTZON3VRRE1jaWdDak1lS2pWNTRDQzNwejBXaVpmWVdHSzBiMXU3NHUKR1hhejdRa01DMXFyRWpSWjhoYml3M1orbGx1TGxlQXkzdkpNUmt3OEdqcHF4eVdrNFZNd2lzR1MzVXZBV0pqSgo3M0VRa0UyL09Ia05tbXEwTWlFcU5JVWdUZFNCR3dqSWh1UmtwVDlUQkhWNTNvcDduTjJzaHgzMUZWalIycEdsCkY0b05WOGl0RVNSVVJzOHRDaHJNWlcyQ0JLN1lSRzRKbVBKL2NYQ3grMW1aTmJId2JDOWdySVdRQis5bUNUZC8KYXV6VW85Y2hXdy9xNjROV1BOQnZsdnV1SFhNUVkyZkhkaU5pVG1XbHdLMHpBZ01CQUFHalV6QlJNQjBHQTFVZApEZ1FXQkJROFZEbkpkNkhlcXVINk1xKzNHV3QxWG8xVUtqQWZCZ05WSFNNRUdEQVdnQlE4VkRuSmQ2SGVxdUg2Ck1xKzNHV3QxWG8xVUtqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ2wKd0NDRSttUlp4eWNBK0pIODduN05naitSQjA1ak5WTlpCb2UzQUNZRmwrL3NFbERMc3UzRmR3ZjRHOW91L0x2bQpDVVRTaHNFQmQ4YkZpWlVKN0dLSmloTHdESjRYS2lBckNHS0p4d3doVEtZUDlzaDAyUXNYV0kwc1orN1c4bll5ClJLM2Q0NFpySWczQXJDaFBXcEdqUmtWVy9YWTdTamxzWUxRSFNFR2tMLzlSeW1UWVIycGQ5czhGZXFremFkbHIKSm1jRUlFZFNtbXNjcDNQNEk3MW43bHFXRTIyVDluUkFMczlKK1hkMm9IbkhLazRkcTU1QXpsU3A4ODM3aU9FTwp2L25RZzFNUjJPOE9jcHAyU1JsYmlsbVlmeGRJaytLZXNZSmVDd1NiaUxucmNJeHdtdWJBZ0E3TjVWN1pFa1paCnVFY2c3TGV6cUJMaUVUUm1Ga01OCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=" +const invalidBase64CaData = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURvRENDQW9pZ0F3SUJBZ0lVTDFKdUx0K0dRcWNuM0pDZGNiaUxibjBmSjhBd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2FERUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdUQ2xkaGMyaHBibWQwYjI0eEVEQU9CZ05WQkFjVApCMU5sWVhSMGJHVXhEVEFMQmdOVkJBb1RCRlZ1YVhReERUQUxCZ05WQkFzVEJGUmxjM1F4RkRBU0JnTlZCQU1UCkMxVnVhWFJVWlhOMElFTkJNQjRYRFRJd01Ea3hOekUzTXpVd01Gb1hEVEkxTURreE5qRTNNelV3TUZvd2FERUwKTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnVENsZGhjMmhwYm1kMGIyNHhFREFPQmdOVkJBY1RCMU5sWVhSMApiR1V4RFRBTEJnTlZCQW9UQkZWdWFYUXhEVEFMQmdOVkJBc1RCRlJsYzNReEZEQVNCZ05WQkFNVEMxVnVhWFJVClpYTjBJRU5CTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF0ZzU5SGU2MDVlYjIKcThGYUpycHBoRVNPZnJiVEdIQlhXRk41Z0N1QUlZMmVNTUdKSnFwWERrUjNGWko2TFZaYXFkVm9rWmkzeVRIOQprWW5uTEhBRDJJKzd5M0FnczB0WWZucmx0MGhtWjNleVlRSGk0Y1d0Vkd3aVoycW0yQnZMbzJVMENkeXRSSjRRCjNlQVQyeVRrTnZ4Wm9XeUhHK09icjZ4UFByMjh2bWo3Q0txVnNLQ0FIVnlqdXlybXRJcHdkbWVpVTlFbTFTTUgKSVBLR0pJQ29NeGl4NXNDdHVqZmRSTWJTU2hIRFluUmdmMkx2enIxVk5mZkdaS01YekJaekkyZ3BJZm9YaGZVUwpkdmNlUTVoWXo4emdEY2hDOG1laEM3bU12Myt6Q3d6OWtGbmJpYnBvSVdGcStGbzYzeHNnc255dFlQTXY0cmltClgwSWRwZlA2VVFJREFRQUJvMEl3UURBT0JnTlZIUThCQWY4RUJBTUNBUVl3RHdZRFZSMFRBUUgvQkFVd0F3RUIKL3pBZEJnTlZIUTRFRmdRVWUzY0MvMllrTWRmRUZUbUliMW84M1U0VWgxY3dEUVlKS29aSWh2Y05BUUVMQlFBRApnZ0VCQUN2TTVURG9BM0FFRFlrcFlueWwwVlpmRDdKRVhWSEJ5WTEyeG9jUHM4TGJzNEtNS1NtUGVld0dIU25WCisrQVdFdG8vWlFjUnVVcm9SR2ZFRDRTU3kyT0tyNGh4M0J0UmNGRkZrdFg4U2Uwck5rSitaSHVoVFBWdWQ5L00KUXRBenl2UWVkcDBXQlcydDBvWkhDcVNOWmMvSWFYWGNxeTdocHpLOHBLZTNOUXYyUkdHVkEybWRDR1oxUE5rMgpFTXhMVnhoUURkbTNKRWJ4SEJPNCtVWm45MHVDd1BGc25rVFFmNm53WTErMjNMc3lheFkxWXFkeXZHTzhjdEc0CnozUmRTbTVJM25XaTNERFd3TnhuZ1lpMCtBL01VQ0FBYjBOejluSXI3dzB5UlJpWHJha1hUYjlaOC9GWE1JdHEKdG5wckJzK3hhYzhBVWxzcEw3cCtUWmRUMFdNPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" +const validBase64Certificate = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURSVENDQWkyZ0F3SUJBZ0lVSkVIL2x2ZnZZeE5LRkJJME05SVE1VXhqQUh3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1JURUxNQWtHQTFVRUJoTUNRVlV4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQpHRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpEQWVGdzB5TXpFeE16QXdPRE00TURSYUZ3MHlOakF5Ck1EY3dPRE00TURSYU1CUXhFakFRQmdOVkJBTU1DV3h2WTJGc2FHOXpkRENDQVNJd0RRWUpLb1pJaHZjTkFRRUIKQlFBRGdnRVBBRENDQVFvQ2dnRUJBT3c4cDNUY3RxNVpmV2ZST29lMmlPVG9hT3dWclpRbkgwc0Nla0NRMlVzSAppR3k4c054Q28rWnZrZmZuOUJ6ZlJWNVJjUk53R1l6VEVVTm1NTnhUdjdldlFBVll0TTJRY0lHWGluSDB1aytlClAvTFo5bkxtUWdjdWxzZnhHWlh2L1FHSG9LOVJ6bjRabUdSZ3Z3QzdSOVVQT0k1YWxyRk5rTmJrZHZxTm5kVnoKV3FXcEFhSXNTZkNuM0E0ZlBLemc1SFpTYmZNSXc0YjJsa25Pdk1XTXY5Lzg0Yitjc2RkSFcvTXZNcnVXQUZhLwoxV0lKVlozTlljTm95Wk5CcDh2VExZNEJ5VVZMV2lCRXZhWTh2OWo0dkVleGlXQ0s5U0pVcndoSlZ5OWI5cmFhClRjbXl6RUJLb3NTUWgxemJDOHBuQStwUWFERXBKYmdncEdzZjN5aFJuWWtDQXdFQUFhTmVNRnd3R2dZRFZSMFIKQkJNd0VZSUpiRzlqWVd4b2IzTjBod1IvQUFBQk1CMEdBMVVkRGdRV0JCU09hS1Q5MWR3WUVXWXUvMjI3QlljcQovSTBZaFRBZkJnTlZIU01FR0RBV2dCUThWRG5KZDZIZXF1SDZNcSszR1d0MVhvMVVLakFOQmdrcWhraUc5dzBCCkFRc0ZBQU9DQVFFQUlIT2tCcnpXLzdjbkx1L0JPQ05oY0t2YlBVNGFXRzZhTkhWc2dwclpjVk1taFhpNFNzMzUKOE9kalRjTGVJZXpuWVJFNWZQdnpYcHlMSXpwU3FNTzkyNXRTVHV2ODl1d3BHUnlWeUlWcVJBek01clQvbGc5OQp0UjAza1QzRVlKeVM3SldWc3VDMkgzdUhEQzE1RXBmZ2lJS0NWckFDUXoxS1BFZVZyTnl1Z0NlWFNEWXhoRjRTCnhwRURzQ1oyQWhKNjRSQTZ6bmg2MUxKU1M3bXVQd05uYmYzTzVIekx3MU94ajFEcFVkdU8rNVEwUzUzd241Z0kKcmlIR29hVnFNUFhEM2JJTm5WSjNxelFMYm5NYVZRcUpGYUpsOGFzUnVvem5PN2xOWDdmY1UvN0FyUVAyaDhWVQp6N0ZRYzhoUXlVd21UcjlPanRGM2pOZC9nUFFYcTFlL0FBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" +const invalidBase64Certificate = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURPVENDQWlHZ0F3SUJBZ0lVZnFUak95anpOVWdqQVZ1Qm51VFVhNkhMWHkwd0RRWUpLb1pJaHZjTkFRRUwKQlFBd05ERXlNREFHQTFVRUF4TXBSV3hoYzNScFl5QkRaWEowYVdacFkyRjBaU0JVYjI5c0lFRjFkRzluWlc1bApjbUYwWldRZ1EwRXdIaGNOTWpNeE1URTRNRGMxTmpFMFdoY05Nall4TVRFM01EYzFOakUwV2pBVU1SSXdFQVlEClZRUURFd2xzYjJOaGJHaHZjM1F3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQ0QKQ3NrTFpWMDJjWXpYZ21oRjhPTnlsVEZaTkdiRmVBSG0wenQyVHJESElNSUg4bk8yWm5xYkU1TEdrZHJ6VG1SUwpKTEVKTUNnV2pDQWxMMktLdzRZNW01dnc3YlptWUU1VDhEeXZDZGV3L1hSblZLdW1HUFhmd0c5SUExSVhqbHUrCk9VaVR1emVmZER6d3kvTmQrdVlRbkFyMkJ3aFQ4b3JRZnlTRlNGK2ZOR1llcWh4TjcwaWVQS3pUUU1mL0pwZ1MKYUJvV2F0WTdKTFRMWC9YTW11dWtyaytDTUk2QU1SWWFlNUE4UmNWZnBub3c5c24ycThvZWs5cHNiTHJsWURneAo0by9YMm5YYkhPSGtrTExzWHdSRzNLdWIzZ2RMN1BQcUg3RVhhSW9SOUZ0ZHM3YzF1NWhidVpXRXlkUWF2VXVsCm9jMmlGWDBWODZoaVlhUDN1MktKQWdNQkFBR2pZekJoTUIwR0ExVWREZ1FXQkJTUlZMaTN0c0lMZU5Wd1NjM0gKRHR0dnJHWVIxREFmQmdOVkhTTUVHREFXZ0JSUi8xL2Y1dnlWcEk2V1ZYWU1PQURwRnpGYmZUQVVCZ05WSFJFRQpEVEFMZ2dsc2IyTmhiR2h2YzNRd0NRWURWUjBUQkFJd0FEQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFhNnNDCjZ5ME1meFdrRjBEUjhZVjlSdmRiZ0svczFhenA4ZHcydmJML0plTExaNFJ0ekFWTW8zNHlxajZmbWJJY0YyT1gKTCtGUENDYXZvM0oxUDZadkpaZUtNdmQ4NEtIUzJQVVYvREYxRXVWQkhJME85RWZmM1BDS0VTVmZUS29QbnlsSQpjKzY5L0N6b2NKTDFINlppQ1RHMWVQT21aZlNYNUdnN3g5cmM5SnhXZmlWdUhjcFJadTM0aUhpT2xCMTk0Y2FSCkwxL1R3ZGJ4cmNveUFNUGFyQURFL212dDRqTXQ4ZFk3anowR2E0Nk55RkUySlZrenFLMlpGbEZ4UTYxdDZvWWMKZWk3NzdxTFlwUVB1bERHVkVvM2d0d0h2TmpYQ0NHcTJNQzZHM0JjMGxlVlFQRkF1VE9hdkI4c1FRNnR4eldKOAorN0JFL1FaZmtySUZPOGFKZ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" +const validBase64Key = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRHNQS2QwM0xhdVdYMW4KMFRxSHRvams2R2pzRmEyVUp4OUxBbnBBa05sTEI0aHN2TERjUXFQbWI1SDM1L1FjMzBWZVVYRVRjQm1NMHhGRApaakRjVTcrM3IwQUZXTFROa0hDQmw0cHg5THBQbmoveTJmWnk1a0lITHBiSDhSbVY3LzBCaDZDdlVjNStHWmhrCllMOEF1MGZWRHppT1dwYXhUWkRXNUhiNmpaM1ZjMXFscVFHaUxFbndwOXdPSHp5czRPUjJVbTN6Q01PRzlwWkoKenJ6RmpML2YvT0cvbkxIWFIxdnpMeks3bGdCV3Y5VmlDVldkeldIRGFNbVRRYWZMMHkyT0FjbEZTMW9nUkwybQpQTC9ZK0x4SHNZbGdpdlVpVks4SVNWY3ZXL2EybWszSnNzeEFTcUxFa0lkYzJ3dktad1BxVUdneEtTVzRJS1JyCkg5OG9VWjJKQWdNQkFBRUNnZ0VBQ21nekk5aXYwdzgxOFRlRVRiTit4Ui9RZEhzUmdDWDNrTk5CZmJ2cjJLeHkKcUFDcGhYQ0puaWFTM29JWlROYlFyR0tuMmJoOTVhaHNPYVVFT01hWE92RTFYNjdzV3lSMmNsMFIwQ3FjOVJJMgpoTDB4cUZib3VINkd6YlRiUU5IWFdtU0dVWWJuOHdIdlp0ZWt2blJocWhzUExhVkR2M2lZYllFWDFUcWRKZ2ltCjJtcXBmbUh4VmloNWlhVGxjZ3ZUeUNLN2ZTZzdRalFkK3d1Wk9IZzZveExpU3J4WFRZTmk3UUlTK25oNWk0djcKMWd4cEFqa2JMeFZtNE9TZVFaYzVaREU0YU1xUzh6Sjl2VXNRRGduUHorbStSOGhWbFpEK1FIVmV4TmE2K0ZpSQpHbEdBanEvMXV4YU5BYkUvUENudzc5T004aDVkaFllc0ZTcWVGc2Qwd1FLQmdRRC9GOXVpQW9rdGtLcm91RWxhClh1ci9RTWtyQU5KWHEzSnJCZ0pBVExUNVVsUmlkc21EeVRJMXRUbXFOVVZTSFA3NG5RYVUvRDhNV3Z0YXVBMzIKU3pEOEJzTmNGSlB2RjBoWmRRL21PV1d0elU4aFRQR3Zac201ek44YkdYdWxLcUFwSG9kL0pxUGFUcm5kRk9JUgpuWkc3SnRlZlVLN3d6cEY2djNCN1ZpcE95UUtCZ1FEdEU2THVVbmVRMVZ2akcvd0dyN0hLVFJ3a2dsQjBDd0tFCmtCU3ZHS2JvdzVidUxXR0pFS1g1YXZoUkJsQlRFa2dqVzlhREs5a2N5V1FkY1V3czBzRWVXWTdjNmJHVDZPME8Kb2xBWlB1KzlsSmVvV1QwTHRRcDRRYlgxNG84UERJbGt0bUJrV1JxbDYxVjh5SCtzTlpMWnlzYnZEbWNicng2eQptZzR0V281NHdRS0JnRDFMK2xiZnpSN0oySWU3WU1UNmNmV01GOHJoazZuRlpPVWF1SWZDNXVuU1FyeTJWbzM0ClZyeFpJOVltbnRXd2FnUkxsejFOcjhqRVJBbjBtRVpLb3lhc2FWMURCSms3T3dOa0FjSU1vTVkydzRENUFFcHAKcEtlazl5ZUg1Qkk0UCt3aGplV2IzMXVoOXorTXVSWUtpdTR4MHpaUktQaHNhc0RZSjZzN2RVY2hBb0dCQUpoRwp4NTJLak5BVHEweXFHZXgxaDU0b2YwNFlBZk0zYXl1WW5DQ0hsaFhtSTVqaXYwWlowakh0ZW9nWXBSbG1vYjFNCmJQR2VCWHVQQStaQmNxdEx4ODFsdXZTOGlsbzEvNllwclljNXZLV3B2dXZjUGZDNkhYcDJ3cGlvS0RtRFZQREMKa0JHRWhqQlNnM3QrRVR1Y3diRndwT3pCOUlwOHBod1VCYzB0NEZ0QkFvR0FHY1VHQk1OWG5UZnk5OEdqTytEeAp2eTRjTXRhdXE2RzJjUEs3Q1FXcmgrbjZVcUgrZ1Rjd2NxOUJYa0NIVTgxK0NUOUFQRTVzaUhEZlZsaHJ2bm5LCjJXbDh3R1o3ZHVlK3lpVnFJbFZ3cUswcmNtRmdOTldwby8zc096MnFSb2NWazQ3T3Q1bjhVZVc0MUdJV2swQVcKNTVuMVFpZU92WEFOMnBQb3Rxd0VkNTQ9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K" +const invalidBase64Key = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBZ3dySkMyVmRObkdNMTRKb1JmRGpjcFV4V1RSbXhYZ0I1dE03ZGs2d3h5RENCL0p6CnRtWjZteE9TeHBIYTgwNWtVaVN4Q1RBb0Zvd2dKUzlpaXNPR09adWI4TzIyWm1CT1UvQThyd25Yc1AxMFoxU3IKcGhqMTM4QnZTQU5TRjQ1YnZqbElrN3MzbjNRODhNdnpYZnJtRUp3SzlnY0lVL0tLMEg4a2hVaGZuelJtSHFvYwpUZTlJbmp5czAwREgveWFZRW1nYUZtcldPeVMweTEvMXpKcnJwSzVQZ2pDT2dERVdHbnVRUEVYRlg2WjZNUGJKCjlxdktIcFBhYkd5NjVXQTRNZUtQMTlwMTJ4emg1SkN5N0Y4RVJ0eXJtOTRIUyt6ejZoK3hGMmlLRWZSYlhiTzMKTmJ1WVc3bVZoTW5VR3IxTHBhSE5vaFY5RmZPb1ltR2o5N3RpaVFJREFRQUJBb0lCQUg1MEIvSFJUUlBlbTNUVAp5Ti9GUnhjcFZVZXB3NHJHOWI3VEU4eGt2ejVKSkRRYkNRSjQvZE5zSGZVMGhyN0haUlBIaUhjL1cwLzJ4SVpkCnBaQVdnZzVSVlRnM2pBNWEzUHN2RnNBcWxWT2NJWm9kSU03VncxNjZDaWpKMjR3VHVnQmtzdDZzaVU1OEV0cWoKVlNQWm0rMW5SMFNISU1neGd6Y1RtaUJyNktwdHVtWXhlLzZPUzRBNUZhYzU0aFZRYUhyM0xwRWkyRW9mWnBPSApFUEJwNmZNcU1HYzZ3NVliSTZuKzNtZDB4akx0dVp0TFB6NkVnV0MwRG04RWduc0pIaWYzaGQ0QUFmbEFmRXRMCmhzbWEwNDFGdVA5VHkvbGhIbkR4R1ZEL1EySnA5eG5EZitHa0RESmxEUG9nbDBlelh0aGcwd1dyeVdMVFcveFAKTWZHclBLRUNnWUVBdmtFQTUyc1FjM2FzNkl3OEY0Uzlkdm5taEJxUVhsZzdxN0NaZzMvK3djTXFIK05NN2d3OQpJTDhocWdIbTBrUjBXcEJITFdld2hhNlNidlM5RFEwemluT1NKam54cGVsNnRvRU9zaEdObjhlQnpjSGozYWpnCkNRaGJiMTRMSmxoZFVrRTJoekg1NllQazZTcmtDVE94MGdSV2ZOczVTRjZWWjZpZUU2UXFkUDBDZ1lFQXNGT04Kd0srUXplUHpVb05GRUFOTFBYZGI1TERTdHZ0V091UDV2NHRST2dMNzgwQ0N2WERYV1RjSithSHR2d3IwckhHQgovMFhKb2NNU1ZlbDBNMTBXR0N6MlBjTnJEUWZFQ1ZKMVorTHlvU3o5aHk1R3hNeERIc1NuMnZadUFrU2JhTW9nCmpEcjBZVXlGRWFaMFBmbnIrdHVMb2JXWXNybnNaN3VqSzgzdVAzMENnWUFaM09KUGt6bFlTT0MyZUNIUEhLZFkKM2gzZEJYTnNyOWZrdmd3UVdUejdnQmxnM2xoZDMrSUxhcEFiK1VnMGUwUEo3K1VOSWhSWUIrUTJHeVQ3K2podwpjTWVFVk1vcHdMU0N6TVovcEcwNU5Eak1ETGg2TUhQTUpvdXZkdnhUQ1I0ZWlXanROZmtBS29MYXc0N2VSVjI5CjdBTUoxQTlVYkM4cE9UM0w4N3dselFLQmdGTjdxOURBRHdvOFUvY01LY1cwR1lxSG1aMkVUcS9OL2Z5eDlZeEgKOVBSSGQyeXJiWHN1RXZldGhHNkp5VnU3WHk3S0t5ZG1ybG1GVjRnUG1URzhiL3FRUnlIbEJTbE5OUGJJOE1kMwovekpxYjdyeUlSV0tOSGs3Mk5GbC9aM2JSODFzYmM2WEZ4OStNRDYwRmJOR1FnRXFzMGlrQnlFUHdDczQvcjk5CnA4Q3RBb0dCQUo3MkZRRkVaeEZOZE5PQ3QxS1V0VTZSUDBuSCsxM0xpWWRrZ2hjQi94VGpqZVpNekZpdFh6VlEKd2QvRjlFczYzcEtIWjBSQzY4MndZWTk5OFAzb3NVWXk2V3hIcENUK3N3ZUFQRUZCT1FsNE9FTG5HNWVKbkE0UApPT2FabFVKWVpxNmsrc09SVE1GdG5nU0ZQOWNLSXc1WHZJa1daMzhFdkhsYnhUa1lDcFJqCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==" + +// test if the input is valid +func Test_NewTLSConfig(t *testing.T) { + tests := map[string]struct { + cfg *TLS + cfgErr string + }{ + "emptyConfig": { + cfg: &TLS{}, + }, + "caData_good": { + cfg: &TLS{ + Enabled: true, + CaData: validBase64CaData, + }, + }, + "caData_badBase64": { + cfg: &TLS{Enabled: true, CaData: "this isn't base64"}, + cfgErr: "illegal base64 data at input byte", + }, + "caData_badPEM": { + cfg: &TLS{Enabled: true, CaData: "dGhpcyBpc24ndCBhIFBFTSBjZXJ0"}, + cfgErr: "unable to parse certs as PEM", + }, + "clientCert_badbase64cert": { + cfg: &TLS{ + Enabled: true, + CertData: "this ain't base64", + KeyData: validBase64Key, + }, + cfgErr: "illegal base64 data at input byte", + }, + "clientCert_badbase64key": { + cfg: &TLS{ + Enabled: true, + CertData: validBase64Certificate, + KeyData: "this ain't base64", + }, + cfgErr: "illegal base64 data at input byte", + }, + "clientCert_missingprivatekey": { + cfg: &TLS{ + Enabled: true, + CertData: validBase64Certificate, + KeyData: "", + }, + cfgErr: "unable to config TLS: cert or key is missing", + }, + "clientCert_duplicate_cert": { + cfg: &TLS{ + Enabled: true, + CertData: validBase64Certificate, + CertFile: "/a/b/c", + }, + cfgErr: "only one of certData or certFile properties should be specified", + }, + "clientCert_duplicate_key": { + cfg: &TLS{ + Enabled: true, + KeyData: validBase64Key, + KeyFile: "/a/b/c", + }, + cfgErr: "only one of keyData or keyFile properties should be specified", + }, + "clientCert_duplicate_ca": { + cfg: &TLS{ + Enabled: true, + CaData: validBase64CaData, + CaFile: "/a/b/c", + }, + cfgErr: "only one of caData or caFile properties should be specified", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + ctrl := gomock.NewController(t) + _, err := NewTLSConfig(tc.cfg) + if tc.cfgErr != "" { + assert.ErrorContains(t, err, tc.cfgErr) + } else { + assert.NoError(t, err) + } + + ctrl.Finish() + }) + } +} + +func Test_ConnectToTLSServerWithCA(t *testing.T) { + // setup server + h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello World") + }) + ts := httptest.NewUnstartedServer(h) + certBytes, err := os.ReadFile("./testdata/localhost.crt") + if err != nil { + panic(fmt.Errorf("unable to decode certificate %w", err)) + } + keyBytes, err := os.ReadFile("./testdata/localhost.key") + if err != nil { + panic(fmt.Errorf("unable to decode key %w", err)) + } + cert, err := tls.X509KeyPair(certBytes, keyBytes) + if err != nil { + panic(fmt.Errorf("unable to load certificate %w", err)) + } + ts.TLS = &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + ts.StartTLS() + + tests := map[string]struct { + cfg *TLS + connectionErr string + }{ + "caData_good": { + cfg: &TLS{ + Enabled: true, + CaData: validBase64CaData, + }, + }, + "caData_signedByWrongCA": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: true, + CaData: invalidBase64CaData, + }, + connectionErr: "x509: certificate signed by unknown authority", + }, + "caData_signedByWrongCAButNotEnableHostVerification": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: false, + CaData: invalidBase64CaData, + }, + }, + "caFile_good": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: true, + CaFile: "testdata/ca.crt", + }, + }, + "caFile_signedByWrongCA": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: true, + CaFile: "testdata/invalid_ca.crt", + }, + connectionErr: "x509: certificate signed by unknown authority", + }, + "caFile_signedByWrongCANotEnableHostVerification": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: false, + CaFile: "testdata/invalid_ca.crt", + }, + }, + "certData_good": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: true, + CaData: validBase64Certificate, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + ctrl := gomock.NewController(t) + tlsConfig, err := NewTLSConfig(tc.cfg) + if err != nil { + panic(err) + } + cl := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}} + resp, err := cl.Get(ts.URL) + if tc.connectionErr != "" { + assert.ErrorContains(t, err, tc.connectionErr) + } else { + assert.NoError(t, err) + assert.Equal(t, 200, resp.StatusCode) + } + + ctrl.Finish() + }) + } +} + +func Test_ConnectToTLSServerWithClientCertificate(t *testing.T) { + // setup server + h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello World") + }) + ts := httptest.NewUnstartedServer(h) + certBytes, err := os.ReadFile("./testdata/localhost.crt") + if err != nil { + panic(fmt.Errorf("unable to decode certificate %w", err)) + } + keyBytes, err := os.ReadFile("./testdata/localhost.key") + if err != nil { + panic(fmt.Errorf("unable to decode key %w", err)) + } + cert, err := tls.X509KeyPair(certBytes, keyBytes) + if err != nil { + panic(fmt.Errorf("unable to load certificate %w", err)) + } + caBytes, _ := os.ReadFile("testdata/ca.crt") + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caBytes) + ts.TLS = &tls.Config{ + ClientCAs: caCertPool, + Certificates: []tls.Certificate{cert}, + ClientAuth: tls.RequireAndVerifyClientCert, + } + ts.StartTLS() + + tests := map[string]struct { + cfg *TLS + connectionErr string + }{ + "clientData_good": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: true, + CaData: validBase64CaData, + CertData: validBase64Certificate, + KeyData: validBase64Key, + }, + }, + "clientData_certNotProvided": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: true, + CaData: validBase64CaData, + }, + connectionErr: "certificate required", + }, + "clientData_certInvalid": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: true, + CaData: validBase64CaData, + CertData: invalidBase64Certificate, + KeyData: invalidBase64Key, + }, + connectionErr: "certificate required", + }, + "certFile_good": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: true, + CaData: validBase64CaData, + CertFile: "testdata/localhost.crt", + KeyFile: "testdata/localhost.key", + }, + }, + "clientFile_certInvalid": { + cfg: &TLS{ + Enabled: true, + EnableHostVerification: true, + CaData: validBase64CaData, + CertFile: "testdata/invalid_localhost.crt", + KeyFile: "testdata/invalid_localhost.key", + }, + connectionErr: "certificate required", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + ctrl := gomock.NewController(t) + tlsConfig, err := NewTLSConfig(tc.cfg) + if err != nil { + panic(err) + } + cl := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}} + resp, err := cl.Get(ts.URL) + if tc.connectionErr != "" { + assert.ErrorContains(t, err, tc.connectionErr) + } else { + assert.NoError(t, err) + assert.Equal(t, 200, resp.StatusCode) + } + + ctrl.Finish() + }) + } +} diff --git a/common/persistence/visibility/store/elasticsearch/client/client_v7.go b/common/persistence/visibility/store/elasticsearch/client/client_v7.go index 68bdbd60721..03c20e5fe08 100644 --- a/common/persistence/visibility/store/elasticsearch/client/client_v7.go +++ b/common/persistence/visibility/store/elasticsearch/client/client_v7.go @@ -27,6 +27,7 @@ package client import ( "context" "encoding/json" + "fmt" "net/http" "net/url" "strings" @@ -38,6 +39,7 @@ import ( "github.com/olivere/elastic/v7/uritemplates" enumspb "go.temporal.io/api/enums/v1" + "go.temporal.io/server/common/auth" "go.temporal.io/server/common/log" ) @@ -88,7 +90,15 @@ func newClient(cfg *Config, httpClient *http.Client, logger log.Logger) (*client options = append(options, getLoggerOptions(cfg.LogLevel, logger)...) if httpClient == nil { - httpClient = http.DefaultClient + if cfg.TLS != nil && cfg.TLS.Enabled { + tlsHttpClient, err := buildTLSHTTPClient(cfg.TLS) + if err != nil { + return nil, fmt.Errorf("unable to create TLS HTTP client: %w", err) + } + httpClient = tlsHttpClient + } else { + httpClient = http.DefaultClient + } } // TODO (alex): Remove this when https://github.com/olivere/elastic/pull/1507 is merged. @@ -130,6 +140,19 @@ func newClient(cfg *Config, httpClient *http.Client, logger log.Logger) (*client }, nil } +// Build Http Client with TLS +func buildTLSHTTPClient(config *auth.TLS) (*http.Client, error) { + tlsConfig, err := auth.NewTLSConfig(config) + if err != nil { + return nil, err + } + + transport := &http.Transport{TLSClientConfig: tlsConfig} + tlsClient := &http.Client{Transport: transport} + + return tlsClient, nil +} + func (c *clientImpl) Get(ctx context.Context, index string, docID string) (*elastic.GetResult, error) { return c.esClient.Get().Index(index).Id(docID).Do(ctx) } diff --git a/common/persistence/visibility/store/elasticsearch/client/config.go b/common/persistence/visibility/store/elasticsearch/client/config.go index 92f2c8ce3ca..41758c615f0 100644 --- a/common/persistence/visibility/store/elasticsearch/client/config.go +++ b/common/persistence/visibility/store/elasticsearch/client/config.go @@ -29,6 +29,8 @@ import ( "fmt" "net/url" "time" + + "go.temporal.io/server/common/auth" ) const ( @@ -51,6 +53,7 @@ type ( CloseIdleConnectionsInterval time.Duration `yaml:"closeIdleConnectionsInterval"` EnableSniff bool `yaml:"enableSniff"` EnableHealthcheck bool `yaml:"enableHealthcheck"` + TLS *auth.TLS `yaml:"tls"` } // ESAWSRequestSigningConfig represents configuration for signing ES requests to AWS