mirror of https://github.com/grpc/grpc-java.git
xds: Spiffe Trust Bundle Support (#11627)
Adds verification of SPIFFE based identities using SPIFFE trust bundles. For in-progress gRFC A87.
This commit is contained in:
parent
76705c235c
commit
d6c80294a7
|
|
@ -67,6 +67,35 @@ ecdsa.key is used to test keys with algorithm other than RSA:
|
|||
$ openssl ecparam -name secp256k1 -genkey -noout -out ecdsa.pem
|
||||
$ openssl pkcs8 -topk8 -in ecdsa.pem -out ecdsa.key -nocrypt
|
||||
|
||||
SPIFFE test credentials:
|
||||
=======================
|
||||
|
||||
The SPIFFE related extensions are listed in spiffe-openssl.cnf config. Both
|
||||
client_spiffe.pem and server1_spiffe.pem are generated in the same way with
|
||||
original client.pem and server1.pem but with using that config. Here are the
|
||||
exact commands (we pass "-subj" as argument in this case):
|
||||
----------------------
|
||||
$ openssl req -new -key client.key -out spiffe-cert.csr \
|
||||
-subj /C=US/ST=CA/L=SVL/O=gRPC/CN=testclient/ \
|
||||
-config spiffe-openssl.cnf -reqexts spiffe_client_e2e
|
||||
$ openssl x509 -req -CA ca.pem -CAkey ca.key -CAcreateserial \
|
||||
-in spiffe-cert.csr -out client_spiffe.pem -extensions spiffe_client_e2e \
|
||||
-extfile spiffe-openssl.cnf -days 3650 -sha256
|
||||
$ openssl req -new -key server1.key -out spiffe-cert.csr \
|
||||
-subj /C=US/ST=CA/L=SVL/O=gRPC/CN=*.test.google.com/ \
|
||||
-config spiffe-openssl.cnf -reqexts spiffe_server_e2e
|
||||
$ openssl x509 -req -CA ca.pem -CAkey ca.key -CAcreateserial \
|
||||
-in spiffe-cert.csr -out server1_spiffe.pem -extensions spiffe_server_e2e \
|
||||
-extfile spiffe-openssl.cnf -days 3650 -sha256
|
||||
|
||||
Additionally, SPIFFE trust bundle map files spiffebundle.json and \
|
||||
spiffebundle1.json are manually created for end to end testing. The \
|
||||
spiffebundle.json contains "example.com" trust domain (only this entry is used \
|
||||
in e2e tests) matching URI SAN of server1_spiffe.pem, and the CA certificate \
|
||||
there is ca.pem. The spiffebundle.json file contains "foo.bar.com" trust \
|
||||
domain (only this entry is used in e2e tests) matching URI SAN of \
|
||||
client_spiffe.pem, and the CA certificate there is also ca.pem.
|
||||
|
||||
Clean up:
|
||||
---------
|
||||
$ rm *.rsa
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEMjCCAxqgAwIBAgIUVXGlXjNENtOZbI12epjgIhMaShUwDQYJKoZIhvcNAQEL
|
||||
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTI0
|
||||
MTAyNDE2NDAzN1oXDTM0MTAyMjE2NDAzN1owaDELMAkGA1UEBhMCQVUxEzARBgNV
|
||||
BAgMClNvbWUtU3RhdGUxDDAKBgNVBAcMA1NWTDEhMB8GA1UECgwYSW50ZXJuZXQg
|
||||
V2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDDAp0ZXN0Y2xpZW50MIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsqmEafg11ae9jRW0B/IXYU2S8nGVzpSYZjLK
|
||||
yZq459qe6SP/Jk2f9BQvkhlgRmVfhC4h65gl+c32iC6/SLsOxoa91c6Hn4vK+tqy
|
||||
7qVTzYv6naso1pNnRAhwvWd/gINysyk8nq11oynL8ilZjNGcRNEV4Q1v0aEG6mbF
|
||||
NhioNQdq4VFPCjdIFZip9KyRzsc0VUmHmC2KeWJ+yq7TyXCsqPWlbhK+3RgDc6ch
|
||||
epYP52AVnPvUhsJKC3RbyrwAWCTMq2zYR1EH79H82mdD/OnX0xDaw8cwC68xp6nM
|
||||
dyk68CY5Gf2kq9bcg9P7V77pERYj8VgSYYx0O9BqkxUGNfUW4QIDAQABo4HlMIHi
|
||||
MEQGA1UdEQQ9MDuGOXNwaWZmZTovL2Zvby5iYXIuY29tLzllZWJjY2QyLTEyYmYt
|
||||
NDBhNi1iMjYyLTY1ZmUwNDg3ZDQ1MzAdBgNVHQ4EFgQU28U8sUTGNEDyeCrvJDJd
|
||||
AALabSMwewYDVR0jBHQwcqFapFgwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNv
|
||||
bWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0G
|
||||
A1UEAwwGdGVzdGNhghRas/RW8dzL4s/pS5g22Iv2AGEPmjANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEAE3LLE8GR283q/aE646SgAfltqpESP38NmYdJMdZgWRxbOqdWabYDfibt
|
||||
9r8j+IRvVuuTWuH2eNS5wXJtS1BZ+z24wTLa+a2KjOV12gChP+3N7jhqId4eolSL
|
||||
1fjscPY6luZP4Pm3D73lBvIoBvXpDGyrxleiUCEEkKXmTOA8doFvbrcbwm+yUJOP
|
||||
VKUKvAzTNztb0BGDzKKU4E2yK5PSyv2n5m2NpzxYYfHoGeVcxvj7nCnSfoX/EWHb
|
||||
d8ztJYDg9X0iNcfQXt7PZ+j6VcxfDpGCDxe2rFQoYvlWjhr3xOi/1e5A1zx1Ly07
|
||||
m9MB4hntu4e2656ZDWbgOHLpO0q1iQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEZDCCA0ygAwIBAgIUVXGlXjNENtOZbI12epjgIhMaShMwDQYJKoZIhvcNAQEL
|
||||
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTI0
|
||||
MTAyMTAyMTQxNVoXDTM0MTAxOTAyMTQxNVowZTELMAkGA1UEBhMCVVMxETAPBgNV
|
||||
BAgMCElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRUwEwYDVQQKDAxFeGFtcGxl
|
||||
LCBDby4xGjAYBgNVBAMMESoudGVzdC5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEA5xOONxJJ8b8Qauvob5/7dPYZfIcd+uhAWL2ZlTPz
|
||||
Qvu4oF0QI4iYgP5iGgry9zEtCM+YQS8UhiAlPlqa6ANxgiBSEyMHH/xE8lo/+caY
|
||||
GeACqy640Jpl/JocFGo3xd1L8DCawjlaj6eu7T7T/tpAV2qq13b5710eNRbCAfFe
|
||||
8yALiGQemx0IYhlZXNbIGWLBNhBhvVjJh7UvOqpADk4xtl8o5j0xgMIRg6WJGK6c
|
||||
6ffSIg4eP1XmovNYZ9LLEJG68tF0Q/yIN43B4dt1oq4jzSdCbG4F1EiykT2TmwPV
|
||||
YDi8tml6DfOCDGnit8svnMEmBv/fcPd31GSbXjF8M+KGGQIDAQABo4IBGTCCARUw
|
||||
dwYDVR0RBHAwboIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6b29pLnRlc3QuZ29v
|
||||
Z2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQOGJnNwaWZmZTovL2V4YW1w
|
||||
bGUuY29tL3dvcmtsb2FkLzllZWJjY2QyMB0GA1UdDgQWBBRvRpAYHQYP6dFPf5V7
|
||||
/MyCftnNjTB7BgNVHSMEdDByoVqkWDBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwK
|
||||
U29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8w
|
||||
DQYDVQQDDAZ0ZXN0Y2GCFFqz9Fbx3Mviz+lLmDbYi/YAYQ+aMA0GCSqGSIb3DQEB
|
||||
CwUAA4IBAQBJ1bnbBHa1n15vvhpGIzokuiJ+9q/zim63UuVDnkhrQM2N+RQbStGT
|
||||
Tis2tNse1bh460dJFm6ArgHWogzx6fQZzgaDeCOAXvrAe4jM9IHr9K7lkq/33CZS
|
||||
BDV+jCmm2sRsqSMkKUcX6JhyqWGFHuTDAKJzsEV2MlcswleKlGHDkeelAaxlLzpz
|
||||
RHOSQd0N9xAs18lzx95SQEx90PtrBOmvIDDiI5o5z9Oz12Iy1toiksFl4jmknkDD
|
||||
5VF3AyCRgN8NPW0uNC8D2vo4L+tgj9U6NPlmMOrjRsEH257LJ1wopAGr+yezkIId
|
||||
QQodGSVm5cOuw/K7Ma4nBDjVJkjcdY3t
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -4,9 +4,25 @@ subjectAltName = @alt_names
|
|||
[spiffe_client_multi]
|
||||
subjectAltName = @alt_names_multi
|
||||
|
||||
[spiffe_server_e2e]
|
||||
subjectAltName = @alt_names_server_e2e
|
||||
|
||||
[spiffe_client_e2e]
|
||||
subjectAltName = @alt_names_client_e2e
|
||||
|
||||
[alt_names]
|
||||
URI = spiffe://foo.bar.com/client/workload/1
|
||||
|
||||
[alt_names_multi]
|
||||
URI.1 = spiffe://foo.bar.com/client/workload/1
|
||||
URI.2 = spiffe://foo.bar.com/client/workload/2
|
||||
|
||||
[alt_names_server_e2e]
|
||||
DNS.1 = *.test.google.fr
|
||||
DNS.2 = waterzooi.test.google.be
|
||||
DNS.3 = *.test.youtube.com
|
||||
IP.1 = "192.168.1.3"
|
||||
URI = spiffe://example.com/workload/9eebccd2
|
||||
|
||||
[alt_names_client_e2e]
|
||||
URI = spiffe://foo.bar.com/9eebccd2-12bf-40a6-b262-65fe0487d453
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"trust_domains": {
|
||||
"example.com": {
|
||||
"spiffe_sequence": 12035488,
|
||||
"keys": [
|
||||
{
|
||||
"kty": "RSA",
|
||||
"use": "x509-svid",
|
||||
"x5c": ["MIIDWjCCAkKgAwIBAgIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQEL
|
||||
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIw
|
||||
MDMxNzE4NTk1MVoXDTMwMDMxNTE4NTk1MVowVjELMAkGA1UEBhMCQVUxEzARBgNV
|
||||
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
|
||||
ZDEPMA0GA1UEAwwGdGVzdGNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||
AQEAsGL0oXflF0LzoM+Bh+qUU9yhqzw2w8OOX5mu/iNCyUOBrqaHi7mGHx73GD01
|
||||
diNzCzvlcQqdNIH6NQSL7DTpBjca66jYT9u73vZe2MDrr1nVbuLvfu9850cdxiUO
|
||||
Inv5xf8+sTHG0C+a+VAvMhsLiRjsq+lXKRJyk5zkbbsETybqpxoJ+K7CoSy3yc/k
|
||||
QIY3TipwEtwkKP4hzyo6KiGd/DPexie4nBUInN3bS1BUeNZ5zeaIC2eg3bkeeW7c
|
||||
qT55b+Yen6CxY0TEkzBK6AKt/WUialKMgT0wbTxRZO7kUCH3Sq6e/wXeFdJ+HvdV
|
||||
LPlAg5TnMaNpRdQih/8nRFpsdwIDAQABoyAwHjAMBgNVHRMEBTADAQH/MA4GA1Ud
|
||||
DwEB/wQEAwICBDANBgkqhkiG9w0BAQsFAAOCAQEAkTrKZjBrJXHps/HrjNCFPb5a
|
||||
THuGPCSsepe1wkKdSp1h4HGRpLoCgcLysCJ5hZhRpHkRihhef+rFHEe60UePQO3S
|
||||
CVTtdJB4CYWpcNyXOdqefrbJW5QNljxgi6Fhvs7JJkBqdXIkWXtFk2eRgOIP2Eo9
|
||||
/OHQHlYnwZFrk6sp4wPyR+A95S0toZBcyDVz7u+hOW0pGK3wviOe9lvRgj/H3Pwt
|
||||
bewb0l+MhRig0/DVHamyVxrDRbqInU1/GTNCwcZkXKYFWSf92U+kIcTth24Q1gcw
|
||||
eZiLl5FfrWokUNytFElXob0V0a5/kbhiLc3yWmvWqHTpqCALbVyF+rKJo2f5Kw=="],
|
||||
"n": "<base64urlUint-encoded value>",
|
||||
"e": "AQAB"
|
||||
}
|
||||
]
|
||||
},
|
||||
"test.example.com": {
|
||||
"keys": [
|
||||
{
|
||||
"kty": "RSA",
|
||||
"use": "x509-svid",
|
||||
"x5c": ["MIIFsjCCA5qgAwIBAgIURygVMMzdr+Q7rsUaz189JozyHMwwDQYJKoZIhvcNAQEL
|
||||
BQAwTjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxDTAL
|
||||
BgNVBAoMBGdSUEMxFTATBgNVBAMMDHRlc3QtY2xpZW50MTAeFw0yMTEyMjMxODQy
|
||||
NTJaFw0zMTEyMjExODQyNTJaME4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEM
|
||||
MAoGA1UEBwwDU1ZMMQ0wCwYDVQQKDARnUlBDMRUwEwYDVQQDDAx0ZXN0LWNsaWVu
|
||||
dDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ4AqpGetyVSqGUuBJ
|
||||
LVFla+7bEfca7UYzfVSSZLZ/X+JDmWIVN8UIPuFib5jhMEc3XaUnFXUmM7zEtz/Z
|
||||
G5hapwLwOb2C3ZxOP6PQjYCJxbkLie+b43UQrFu1xxd3vMhVJgcj/AIxEpmszuqO
|
||||
a6kUrkYifjJADQ+64kZgl66bsTdXMCzpxyFl9xUfff59L8OX+HUfAcoZz3emjg3Z
|
||||
JPYURQEmjdZTOau1EjFilwHgd989Jt7NKgx30NXoHmw7nusVBIY94fL2VKN3f1XV
|
||||
m0dHu5NI279Q6zr0ZBU7k5T3IeHnzsUesQS4NGlklDWoVTKk73Uv9Pna8yQsSW75
|
||||
7PEbHOGp9Knu4bnoGPOlsG81yIPipO6hTgGFK24pF97M9kpGbWqYX4+2vLlrCAfc
|
||||
msHqaUPmQlYeRVTT6vw7ctYo2kyUYGtnODXk76LqewRBVvkzx75QUhfjAyb740Yc
|
||||
DmIenc56Tq6gebJHjhEmVSehR6xIpXP7SVeurTyhPsEQnpJHtgs4dcwWOZp7BvPN
|
||||
zHXmJqfr7vsshie3vS5kQ0u1e1yqAqXgyDjqKXOkx+dpgUTehSJHhPNHvTc5LXRs
|
||||
vvXKYz6FrwR/DZ8t7BNEvPeLjFgxpH7QVJFLCvCbXs5K6yYbsnLfxFIBPRnrbJkI
|
||||
sK+sQwnRdnsiUdPsTkG5B2lQfQIDAQABo4GHMIGEMB0GA1UdDgQWBBQ2lBp0PiRH
|
||||
HvQ5IRURm8aHsj4RETAfBgNVHSMEGDAWgBQ2lBp0PiRHHvQ5IRURm8aHsj4RETAP
|
||||
BgNVHRMBAf8EBTADAQH/MDEGA1UdEQQqMCiGJnNwaWZmZTovL2Zvby5iYXIuY29t
|
||||
L2NsaWVudC93b3JrbG9hZC8xMA0GCSqGSIb3DQEBCwUAA4ICAQA1mSkgRclAl+E/
|
||||
aS9zJ7t8+Y4n3T24nOKKveSIjxXm/zjhWqVsLYBI6kglWtih2+PELvU8JdPqNZK3
|
||||
4Kl0Q6FWpVSGDdWN1i6NyORt2ocggL3ke3iXxRk3UpUKJmqwz81VhA2KUHnMlyE0
|
||||
IufFfZNwNWWHBv13uJfRbjeQpKPhU+yf4DeXrsWcvrZlGvAET+mcplafUzCp7Iv+
|
||||
PcISJtUerbxbVtuHVeZCLlgDXWkLAWJN8rf0dIG4x060LJ+j6j9uRVhb9sZn1HJV
|
||||
+j4XdIYm1VKilluhOtNwP2d3Ox/JuTBxf7hFHXZPfMagQE5k5PzmxRaCAEMJ1l2D
|
||||
vUbZw+shJfSNoWcBo2qadnUaWT3BmmJRBDh7ZReib/RQ1Rd4ygOyzP3E0vkV4/gq
|
||||
yjLdApXh5PZP8KLQZ+1JN/sdWt7VfIt9wYOpkIqujdll51ESHzwQeAK9WVCB4UvV
|
||||
z6zdhItB9CRbXPreWC+wCB1xDovIzFKOVsLs5+Gqs1m7VinG2LxbDqaKyo/FB0Hx
|
||||
x0acBNzezLWoDwXYQrN0T0S4pnqhKD1CYPpdArBkNezUYAjS725FkApuK+mnBX3U
|
||||
0msBffEaUEOkcyar1EW2m/33vpetD/k3eQQkmvQf4Hbiu9AF+9cNDm/hMuXEw5EX
|
||||
GA91fn0891b5eEW8BJHXX0jri0aN8g=="],
|
||||
"n": "<base64urlUint-encoded value>",
|
||||
"e": "AQAB"
|
||||
},
|
||||
{
|
||||
"kty": "RSA",
|
||||
"use": "x509-svid",
|
||||
"x5c": ["MIIELTCCAxWgAwIBAgIUVXGlXjNENtOZbI12epjgIhMaShEwDQYJKoZIhvcNAQEL
|
||||
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTI0
|
||||
MDkxNzE2MTk0NFoXDTM0MDkxNTE2MTk0NFowTjELMAkGA1UEBhMCVVMxCzAJBgNV
|
||||
BAgMAkNBMQwwCgYDVQQHDANTVkwxDTALBgNVBAoMBGdSUEMxFTATBgNVBAMMDHRl
|
||||
c3QtY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOcTjjcS
|
||||
SfG/EGrr6G+f+3T2GXyHHfroQFi9mZUz80L7uKBdECOImID+YhoK8vcxLQjPmEEv
|
||||
FIYgJT5amugDcYIgUhMjBx/8RPJaP/nGmBngAqsuuNCaZfyaHBRqN8XdS/AwmsI5
|
||||
Wo+nru0+0/7aQFdqqtd2+e9dHjUWwgHxXvMgC4hkHpsdCGIZWVzWyBliwTYQYb1Y
|
||||
yYe1LzqqQA5OMbZfKOY9MYDCEYOliRiunOn30iIOHj9V5qLzWGfSyxCRuvLRdEP8
|
||||
iDeNweHbdaKuI80nQmxuBdRIspE9k5sD1WA4vLZpeg3zggxp4rfLL5zBJgb/33D3
|
||||
d9Rkm14xfDPihhkCAwEAAaOB+jCB9zBZBgNVHREEUjBQhiZzcGlmZmU6Ly9mb28u
|
||||
YmFyLmNvbS9jbGllbnQvd29ya2xvYWQvMYYmc3BpZmZlOi8vZm9vLmJhci5jb20v
|
||||
Y2xpZW50L3dvcmtsb2FkLzIwHQYDVR0OBBYEFG9GkBgdBg/p0U9/lXv8zIJ+2c2N
|
||||
MHsGA1UdIwR0MHKhWqRYMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0
|
||||
YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMM
|
||||
BnRlc3RjYYIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQELBQADggEB
|
||||
AJ4Cbxv+02SpUgkEu4hP/1+8DtSBXUxNxI0VG4e3Ap2+Rhjm3YiFeS/UeaZhNrrw
|
||||
UEjkSTPFODyXR7wI7UO9OO1StyD6CMkp3SEvevU5JsZtGL6mTiTLTi3Qkywa91Bt
|
||||
GlyZdVMghA1bBJLBMwiD5VT5noqoJBD7hDy6v9yNmt1Sw2iYBJPqI3Gnf5bMjR3s
|
||||
UICaxmFyqaMCZsPkfJh0DmZpInGJys3m4QqGz6ZE2DWgcSr1r/ML7/5bSPjjr8j4
|
||||
WFFSqFR3dMu8CbGnfZTCTXa4GTX/rARXbAO67Z/oJbJBK7VKayskL+PzKuohb9ox
|
||||
jGL772hQMbwtFCOFXu5VP0s="]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"trust_domains": {
|
||||
"example.com": {
|
||||
"spiffe_sequence": 12035488,
|
||||
"keys": [
|
||||
{
|
||||
"kty": "RSA",
|
||||
"use": "x509-svid",
|
||||
"x5c": ["MIIDWjCCAkKgAwIBAgIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQEL
|
||||
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIw
|
||||
MDMxNzE4NTk1MVoXDTMwMDMxNTE4NTk1MVowVjELMAkGA1UEBhMCQVUxEzARBgNV
|
||||
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
|
||||
ZDEPMA0GA1UEAwwGdGVzdGNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||
AQEAsGL0oXflF0LzoM+Bh+qUU9yhqzw2w8OOX5mu/iNCyUOBrqaHi7mGHx73GD01
|
||||
diNzCzvlcQqdNIH6NQSL7DTpBjca66jYT9u73vZe2MDrr1nVbuLvfu9850cdxiUO
|
||||
Inv5xf8+sTHG0C+a+VAvMhsLiRjsq+lXKRJyk5zkbbsETybqpxoJ+K7CoSy3yc/k
|
||||
QIY3TipwEtwkKP4hzyo6KiGd/DPexie4nBUInN3bS1BUeNZ5zeaIC2eg3bkeeW7c
|
||||
qT55b+Yen6CxY0TEkzBK6AKt/WUialKMgT0wbTxRZO7kUCH3Sq6e/wXeFdJ+HvdV
|
||||
LPlAg5TnMaNpRdQih/8nRFpsdwIDAQABoyAwHjAMBgNVHRMEBTADAQH/MA4GA1Ud
|
||||
DwEB/wQEAwICBDANBgkqhkiG9w0BAQsFAAOCAQEAkTrKZjBrJXHps/HrjNCFPb5a
|
||||
THuGPCSsepe1wkKdSp1h4HGRpLoCgcLysCJ5hZhRpHkRihhef+rFHEe60UePQO3S
|
||||
CVTtdJB4CYWpcNyXOdqefrbJW5QNljxgi6Fhvs7JJkBqdXIkWXtFk2eRgOIP2Eo9
|
||||
/OHQHlYnwZFrk6sp4wPyR+A95S0toZBcyDVz7u+hOW0pGK3wviOe9lvRgj/H3Pwt
|
||||
bewb0l+MhRig0/DVHamyVxrDRbqInU1/GTNCwcZkXKYFWSf92U+kIcTth24Q1gcw
|
||||
eZiLl5FfrWokUNytFElXob0V0a5/kbhiLc3yWmvWqHTpqCALbVyF+rKJo2f5Kw=="],
|
||||
"n": "<base64urlUint-encoded value>",
|
||||
"e": "AQAB"
|
||||
}
|
||||
]
|
||||
},
|
||||
"foo.bar.com": {
|
||||
"keys": [
|
||||
{
|
||||
"kty": "RSA",
|
||||
"use": "x509-svid",
|
||||
"x5c": ["MIIDWjCCAkKgAwIBAgIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQEL
|
||||
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIw
|
||||
MDMxNzE4NTk1MVoXDTMwMDMxNTE4NTk1MVowVjELMAkGA1UEBhMCQVUxEzARBgNV
|
||||
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
|
||||
ZDEPMA0GA1UEAwwGdGVzdGNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||
AQEAsGL0oXflF0LzoM+Bh+qUU9yhqzw2w8OOX5mu/iNCyUOBrqaHi7mGHx73GD01
|
||||
diNzCzvlcQqdNIH6NQSL7DTpBjca66jYT9u73vZe2MDrr1nVbuLvfu9850cdxiUO
|
||||
Inv5xf8+sTHG0C+a+VAvMhsLiRjsq+lXKRJyk5zkbbsETybqpxoJ+K7CoSy3yc/k
|
||||
QIY3TipwEtwkKP4hzyo6KiGd/DPexie4nBUInN3bS1BUeNZ5zeaIC2eg3bkeeW7c
|
||||
qT55b+Yen6CxY0TEkzBK6AKt/WUialKMgT0wbTxRZO7kUCH3Sq6e/wXeFdJ+HvdV
|
||||
LPlAg5TnMaNpRdQih/8nRFpsdwIDAQABoyAwHjAMBgNVHRMEBTADAQH/MA4GA1Ud
|
||||
DwEB/wQEAwICBDANBgkqhkiG9w0BAQsFAAOCAQEAkTrKZjBrJXHps/HrjNCFPb5a
|
||||
THuGPCSsepe1wkKdSp1h4HGRpLoCgcLysCJ5hZhRpHkRihhef+rFHEe60UePQO3S
|
||||
CVTtdJB4CYWpcNyXOdqefrbJW5QNljxgi6Fhvs7JJkBqdXIkWXtFk2eRgOIP2Eo9
|
||||
/OHQHlYnwZFrk6sp4wPyR+A95S0toZBcyDVz7u+hOW0pGK3wviOe9lvRgj/H3Pwt
|
||||
bewb0l+MhRig0/DVHamyVxrDRbqInU1/GTNCwcZkXKYFWSf92U+kIcTth24Q1gcw
|
||||
eZiLl5FfrWokUNytFElXob0V0a5/kbhiLc3yWmvWqHTpqCALbVyF+rKJo2f5Kw=="]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -58,15 +58,21 @@ final class CertProviderClientSslContextProvider extends CertProviderSslContextP
|
|||
// Null rootCertInstance implies hasSystemRootCerts because of the check in
|
||||
// CertProviderClientSslContextProviderFactory.
|
||||
if (rootCertInstance != null) {
|
||||
sslContextBuilder.trustManager(
|
||||
if (savedSpiffeTrustMap != null) {
|
||||
sslContextBuilder = sslContextBuilder.trustManager(
|
||||
new XdsTrustManagerFactory(
|
||||
savedTrustedRoots.toArray(new X509Certificate[0]),
|
||||
savedSpiffeTrustMap,
|
||||
certificateValidationContextdationContext));
|
||||
} else {
|
||||
sslContextBuilder = sslContextBuilder.trustManager(
|
||||
new XdsTrustManagerFactory(
|
||||
savedTrustedRoots.toArray(new X509Certificate[0]),
|
||||
certificateValidationContextdationContext));
|
||||
}
|
||||
}
|
||||
if (isMtls()) {
|
||||
sslContextBuilder.keyManager(savedKey, savedCertChain);
|
||||
}
|
||||
return sslContextBuilder;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,13 +59,17 @@ final class CertProviderServerSslContextProvider extends CertProviderSslContextP
|
|||
CertificateValidationContext certificateValidationContextdationContext)
|
||||
throws CertStoreException, CertificateException, IOException {
|
||||
SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(savedKey, savedCertChain);
|
||||
setClientAuthValues(
|
||||
sslContextBuilder,
|
||||
isMtls()
|
||||
? new XdsTrustManagerFactory(
|
||||
savedTrustedRoots.toArray(new X509Certificate[0]),
|
||||
certificateValidationContextdationContext)
|
||||
: null);
|
||||
XdsTrustManagerFactory trustManagerFactory = null;
|
||||
if (isMtls() && savedSpiffeTrustMap != null) {
|
||||
trustManagerFactory = new XdsTrustManagerFactory(
|
||||
savedSpiffeTrustMap,
|
||||
certificateValidationContextdationContext);
|
||||
} else if (isMtls()) {
|
||||
trustManagerFactory = new XdsTrustManagerFactory(
|
||||
savedTrustedRoots.toArray(new X509Certificate[0]),
|
||||
certificateValidationContextdationContext);
|
||||
}
|
||||
setClientAuthValues(sslContextBuilder, trustManagerFactory);
|
||||
sslContextBuilder = GrpcSslContexts.configure(sslContextBuilder);
|
||||
return sslContextBuilder;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ abstract class CertProviderSslContextProvider extends DynamicSslContextProvider
|
|||
@Nullable protected PrivateKey savedKey;
|
||||
@Nullable protected List<X509Certificate> savedCertChain;
|
||||
@Nullable protected List<X509Certificate> savedTrustedRoots;
|
||||
@Nullable protected Map<String, List<X509Certificate>> savedSpiffeTrustMap;
|
||||
private final boolean isUsingSystemRootCerts;
|
||||
|
||||
protected CertProviderSslContextProvider(
|
||||
|
|
@ -152,14 +153,21 @@ abstract class CertProviderSslContextProvider extends DynamicSslContextProvider
|
|||
updateSslContextWhenReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void updateSpiffeTrustMap(Map<String, List<X509Certificate>> spiffeTrustMap) {
|
||||
savedSpiffeTrustMap = spiffeTrustMap;
|
||||
updateSslContextWhenReady();
|
||||
}
|
||||
|
||||
private void updateSslContextWhenReady() {
|
||||
if (isMtls()) {
|
||||
if (savedKey != null && (savedTrustedRoots != null || isUsingSystemRootCerts)) {
|
||||
if (savedKey != null
|
||||
&& (savedTrustedRoots != null || isUsingSystemRootCerts || savedSpiffeTrustMap != null)) {
|
||||
updateSslContext();
|
||||
clearKeysAndCerts();
|
||||
}
|
||||
} else if (isClientSideTls()) {
|
||||
if (savedTrustedRoots != null) {
|
||||
if (savedTrustedRoots != null || savedSpiffeTrustMap != null) {
|
||||
updateSslContext();
|
||||
clearKeysAndCerts();
|
||||
}
|
||||
|
|
@ -174,6 +182,7 @@ abstract class CertProviderSslContextProvider extends DynamicSslContextProvider
|
|||
private void clearKeysAndCerts() {
|
||||
savedKey = null;
|
||||
savedTrustedRoots = null;
|
||||
savedSpiffeTrustMap = null;
|
||||
savedCertChain = null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import java.security.cert.X509Certificate;
|
|||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
|
@ -45,6 +46,8 @@ public abstract class CertificateProvider implements Closeable {
|
|||
|
||||
void updateTrustedRoots(List<X509Certificate> trustedRoots);
|
||||
|
||||
void updateSpiffeTrustMap(Map<String, List<X509Certificate>> spiffeTrustMap);
|
||||
|
||||
void onError(Status errorStatus);
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +56,7 @@ public abstract class CertificateProvider implements Closeable {
|
|||
private PrivateKey privateKey;
|
||||
private List<X509Certificate> certChain;
|
||||
private List<X509Certificate> trustedRoots;
|
||||
private Map<String, List<X509Certificate>> spiffeTrustMap;
|
||||
|
||||
@VisibleForTesting
|
||||
final Set<Watcher> downstreamWatchers = new HashSet<>();
|
||||
|
|
@ -65,6 +69,9 @@ public abstract class CertificateProvider implements Closeable {
|
|||
if (trustedRoots != null) {
|
||||
sendLastTrustedRootsUpdate(watcher);
|
||||
}
|
||||
if (spiffeTrustMap != null) {
|
||||
sendLastSpiffeTrustMapUpdate(watcher);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void removeWatcher(Watcher watcher) {
|
||||
|
|
@ -83,6 +90,10 @@ public abstract class CertificateProvider implements Closeable {
|
|||
watcher.updateTrustedRoots(trustedRoots);
|
||||
}
|
||||
|
||||
private void sendLastSpiffeTrustMapUpdate(Watcher watcher) {
|
||||
watcher.updateSpiffeTrustMap(spiffeTrustMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void updateCertificate(PrivateKey key, List<X509Certificate> certChain) {
|
||||
checkNotNull(key, "key");
|
||||
|
|
@ -103,6 +114,14 @@ public abstract class CertificateProvider implements Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpiffeTrustMap(Map<String, List<X509Certificate>> spiffeTrustMap) {
|
||||
this.spiffeTrustMap = spiffeTrustMap;
|
||||
for (Watcher watcher : downstreamWatchers) {
|
||||
sendLastSpiffeTrustMapUpdate(watcher);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onError(Status errorStatus) {
|
||||
for (Watcher watcher : downstreamWatchers) {
|
||||
|
|
@ -147,7 +166,7 @@ public abstract class CertificateProvider implements Closeable {
|
|||
@Override
|
||||
public abstract void close();
|
||||
|
||||
/** Starts the cert refresh and watcher update cycle. */
|
||||
/** Starts the async cert refresh and watcher update cycle. */
|
||||
public abstract void start();
|
||||
|
||||
private final DistributorWatcher watcher;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package io.grpc.xds.internal.security.certprovider;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.internal.SpiffeUtil;
|
||||
import io.grpc.internal.TimeProvider;
|
||||
import io.grpc.xds.internal.security.trust.CertificateUtils;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
|
@ -30,6 +32,7 @@ import java.nio.file.attribute.FileTime;
|
|||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
|
@ -47,11 +50,13 @@ final class FileWatcherCertificateProvider extends CertificateProvider implement
|
|||
private final Path certFile;
|
||||
private final Path keyFile;
|
||||
private final Path trustFile;
|
||||
private final Path spiffeTrustMapFile;
|
||||
private final long refreshIntervalInSeconds;
|
||||
@VisibleForTesting ScheduledFuture<?> scheduledFuture;
|
||||
private FileTime lastModifiedTimeCert;
|
||||
private FileTime lastModifiedTimeKey;
|
||||
private FileTime lastModifiedTimeRoot;
|
||||
private FileTime lastModifiedTimespiffeTrustMap;
|
||||
private boolean shutdown;
|
||||
|
||||
FileWatcherCertificateProvider(
|
||||
|
|
@ -60,6 +65,7 @@ final class FileWatcherCertificateProvider extends CertificateProvider implement
|
|||
String certFile,
|
||||
String keyFile,
|
||||
String trustFile,
|
||||
String spiffeTrustMapFile,
|
||||
long refreshIntervalInSeconds,
|
||||
ScheduledExecutorService scheduledExecutorService,
|
||||
TimeProvider timeProvider) {
|
||||
|
|
@ -69,7 +75,15 @@ final class FileWatcherCertificateProvider extends CertificateProvider implement
|
|||
this.timeProvider = checkNotNull(timeProvider, "timeProvider");
|
||||
this.certFile = Paths.get(checkNotNull(certFile, "certFile"));
|
||||
this.keyFile = Paths.get(checkNotNull(keyFile, "keyFile"));
|
||||
this.trustFile = Paths.get(checkNotNull(trustFile, "trustFile"));
|
||||
checkArgument((trustFile != null || spiffeTrustMapFile != null),
|
||||
"either trustFile or spiffeTrustMapFile must be present");
|
||||
if (spiffeTrustMapFile != null) {
|
||||
this.spiffeTrustMapFile = Paths.get(spiffeTrustMapFile);
|
||||
this.trustFile = null;
|
||||
} else {
|
||||
this.spiffeTrustMapFile = null;
|
||||
this.trustFile = Paths.get(trustFile);
|
||||
}
|
||||
this.refreshIntervalInSeconds = refreshIntervalInSeconds;
|
||||
}
|
||||
|
||||
|
|
@ -107,39 +121,48 @@ final class FileWatcherCertificateProvider extends CertificateProvider implement
|
|||
byte[] keyFileContents = Files.readAllBytes(keyFile);
|
||||
FileTime currentCertTime2 = Files.getLastModifiedTime(certFile);
|
||||
FileTime currentKeyTime2 = Files.getLastModifiedTime(keyFile);
|
||||
if (!currentCertTime2.equals(currentCertTime)) {
|
||||
return;
|
||||
if (currentCertTime2.equals(currentCertTime) && currentKeyTime2.equals(currentKeyTime)) {
|
||||
try (ByteArrayInputStream certStream = new ByteArrayInputStream(certFileContents);
|
||||
ByteArrayInputStream keyStream = new ByteArrayInputStream(keyFileContents)) {
|
||||
PrivateKey privateKey = CertificateUtils.getPrivateKey(keyStream);
|
||||
X509Certificate[] certs = CertificateUtils.toX509Certificates(certStream);
|
||||
getWatcher().updateCertificate(privateKey, Arrays.asList(certs));
|
||||
}
|
||||
lastModifiedTimeCert = currentCertTime;
|
||||
lastModifiedTimeKey = currentKeyTime;
|
||||
}
|
||||
if (!currentKeyTime2.equals(currentKeyTime)) {
|
||||
return;
|
||||
}
|
||||
try (ByteArrayInputStream certStream = new ByteArrayInputStream(certFileContents);
|
||||
ByteArrayInputStream keyStream = new ByteArrayInputStream(keyFileContents)) {
|
||||
PrivateKey privateKey = CertificateUtils.getPrivateKey(keyStream);
|
||||
X509Certificate[] certs = CertificateUtils.toX509Certificates(certStream);
|
||||
getWatcher().updateCertificate(privateKey, Arrays.asList(certs));
|
||||
}
|
||||
lastModifiedTimeCert = currentCertTime;
|
||||
lastModifiedTimeKey = currentKeyTime;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
generateErrorIfCurrentCertExpired(t);
|
||||
}
|
||||
try {
|
||||
FileTime currentRootTime = Files.getLastModifiedTime(trustFile);
|
||||
if (currentRootTime.equals(lastModifiedTimeRoot)) {
|
||||
return;
|
||||
if (spiffeTrustMapFile != null) {
|
||||
FileTime currentSpiffeTime = Files.getLastModifiedTime(spiffeTrustMapFile);
|
||||
if (!currentSpiffeTime.equals(lastModifiedTimespiffeTrustMap)) {
|
||||
SpiffeUtil.SpiffeBundle trustBundle = SpiffeUtil
|
||||
.loadTrustBundleFromFile(spiffeTrustMapFile.toString());
|
||||
getWatcher().updateSpiffeTrustMap(new HashMap<>(trustBundle.getBundleMap()));
|
||||
lastModifiedTimespiffeTrustMap = currentSpiffeTime;
|
||||
}
|
||||
}
|
||||
byte[] rootFileContents = Files.readAllBytes(trustFile);
|
||||
FileTime currentRootTime2 = Files.getLastModifiedTime(trustFile);
|
||||
if (!currentRootTime2.equals(currentRootTime)) {
|
||||
return;
|
||||
} catch (Throwable t) {
|
||||
getWatcher().onError(Status.fromThrowable(t));
|
||||
}
|
||||
try {
|
||||
if (trustFile != null) {
|
||||
FileTime currentRootTime = Files.getLastModifiedTime(trustFile);
|
||||
if (!currentRootTime.equals(lastModifiedTimeRoot)) {
|
||||
byte[] rootFileContents = Files.readAllBytes(trustFile);
|
||||
FileTime currentRootTime2 = Files.getLastModifiedTime(trustFile);
|
||||
if (currentRootTime2.equals(currentRootTime)) {
|
||||
try (ByteArrayInputStream rootStream = new ByteArrayInputStream(rootFileContents)) {
|
||||
X509Certificate[] caCerts = CertificateUtils.toX509Certificates(rootStream);
|
||||
getWatcher().updateTrustedRoots(Arrays.asList(caCerts));
|
||||
}
|
||||
lastModifiedTimeRoot = currentRootTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
try (ByteArrayInputStream rootStream = new ByteArrayInputStream(rootFileContents)) {
|
||||
X509Certificate[] caCerts = CertificateUtils.toX509Certificates(rootStream);
|
||||
getWatcher().updateTrustedRoots(Arrays.asList(caCerts));
|
||||
}
|
||||
lastModifiedTimeRoot = currentRootTime;
|
||||
} catch (Throwable t) {
|
||||
getWatcher().onError(Status.fromThrowable(t));
|
||||
}
|
||||
|
|
@ -195,6 +218,7 @@ final class FileWatcherCertificateProvider extends CertificateProvider implement
|
|||
String certFile,
|
||||
String keyFile,
|
||||
String trustFile,
|
||||
String spiffeTrustMapFile,
|
||||
long refreshIntervalInSeconds,
|
||||
ScheduledExecutorService scheduledExecutorService,
|
||||
TimeProvider timeProvider) {
|
||||
|
|
@ -204,6 +228,7 @@ final class FileWatcherCertificateProvider extends CertificateProvider implement
|
|||
certFile,
|
||||
keyFile,
|
||||
trustFile,
|
||||
spiffeTrustMapFile,
|
||||
refreshIntervalInSeconds,
|
||||
scheduledExecutorService,
|
||||
timeProvider);
|
||||
|
|
@ -220,6 +245,7 @@ final class FileWatcherCertificateProvider extends CertificateProvider implement
|
|||
String certFile,
|
||||
String keyFile,
|
||||
String trustFile,
|
||||
String spiffeTrustMapFile,
|
||||
long refreshIntervalInSeconds,
|
||||
ScheduledExecutorService scheduledExecutorService,
|
||||
TimeProvider timeProvider);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.google.common.annotations.VisibleForTesting;
|
|||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.google.protobuf.Duration;
|
||||
import com.google.protobuf.util.Durations;
|
||||
import io.grpc.internal.GrpcUtil;
|
||||
import io.grpc.internal.JsonUtil;
|
||||
import io.grpc.internal.TimeProvider;
|
||||
import java.text.ParseException;
|
||||
|
|
@ -33,11 +34,15 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||
/**
|
||||
* Provider of {@link FileWatcherCertificateProvider}s.
|
||||
*/
|
||||
final class FileWatcherCertificateProviderProvider implements CertificateProviderProvider {
|
||||
public final class FileWatcherCertificateProviderProvider implements CertificateProviderProvider {
|
||||
|
||||
@VisibleForTesting
|
||||
public static boolean enableSpiffe = GrpcUtil.getFlag("GRPC_EXPERIMENTAL_SPIFFE_TRUST_BUNDLE_MAP",
|
||||
false);
|
||||
private static final String CERT_FILE_KEY = "certificate_file";
|
||||
private static final String KEY_FILE_KEY = "private_key_file";
|
||||
private static final String ROOT_FILE_KEY = "ca_certificate_file";
|
||||
private static final String SPIFFE_TRUST_MAP_FILE_KEY = "spiffe_trust_bundle_map_file";
|
||||
private static final String REFRESH_INTERVAL_KEY = "refresh_interval";
|
||||
|
||||
@VisibleForTesting static final long REFRESH_INTERVAL_DEFAULT = 600L;
|
||||
|
|
@ -82,6 +87,7 @@ final class FileWatcherCertificateProviderProvider implements CertificateProvide
|
|||
configObj.certFile,
|
||||
configObj.keyFile,
|
||||
configObj.rootFile,
|
||||
configObj.spiffeTrustMapFile,
|
||||
configObj.refrehInterval,
|
||||
scheduledExecutorServiceFactory.create(),
|
||||
timeProvider);
|
||||
|
|
@ -98,7 +104,20 @@ final class FileWatcherCertificateProviderProvider implements CertificateProvide
|
|||
Config configObj = new Config();
|
||||
configObj.certFile = checkForNullAndGet(map, CERT_FILE_KEY);
|
||||
configObj.keyFile = checkForNullAndGet(map, KEY_FILE_KEY);
|
||||
configObj.rootFile = checkForNullAndGet(map, ROOT_FILE_KEY);
|
||||
if (enableSpiffe) {
|
||||
if (!map.containsKey(ROOT_FILE_KEY) && !map.containsKey(SPIFFE_TRUST_MAP_FILE_KEY)) {
|
||||
throw new NullPointerException(
|
||||
String.format("either '%s' or '%s' is required in the config",
|
||||
ROOT_FILE_KEY, SPIFFE_TRUST_MAP_FILE_KEY));
|
||||
}
|
||||
if (map.containsKey(SPIFFE_TRUST_MAP_FILE_KEY)) {
|
||||
configObj.spiffeTrustMapFile = JsonUtil.getString(map, SPIFFE_TRUST_MAP_FILE_KEY);
|
||||
} else {
|
||||
configObj.rootFile = JsonUtil.getString(map, ROOT_FILE_KEY);
|
||||
}
|
||||
} else {
|
||||
configObj.rootFile = checkForNullAndGet(map, ROOT_FILE_KEY);
|
||||
}
|
||||
String refreshIntervalString = JsonUtil.getString(map, REFRESH_INTERVAL_KEY);
|
||||
if (refreshIntervalString != null) {
|
||||
try {
|
||||
|
|
@ -139,6 +158,7 @@ final class FileWatcherCertificateProviderProvider implements CertificateProvide
|
|||
String certFile;
|
||||
String keyFile;
|
||||
String rootFile;
|
||||
String spiffeTrustMapFile;
|
||||
Long refrehInterval;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package io.grpc.xds.internal.security.trust;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
|
@ -33,6 +34,9 @@ import java.security.NoSuchAlgorithmException;
|
|||
import java.security.cert.CertStoreException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.net.ssl.ManagerFactoryParameters;
|
||||
|
|
@ -63,6 +67,11 @@ public final class XdsTrustManagerFactory extends SimpleTrustManagerFactory {
|
|||
this(certs, staticCertificateValidationContext, true);
|
||||
}
|
||||
|
||||
public XdsTrustManagerFactory(Map<String, List<X509Certificate>> spiffeTrustMap,
|
||||
CertificateValidationContext staticCertificateValidationContext) throws CertStoreException {
|
||||
this(spiffeTrustMap, staticCertificateValidationContext, true);
|
||||
}
|
||||
|
||||
private XdsTrustManagerFactory(
|
||||
X509Certificate[] certs,
|
||||
CertificateValidationContext certificateValidationContext,
|
||||
|
|
@ -76,6 +85,19 @@ public final class XdsTrustManagerFactory extends SimpleTrustManagerFactory {
|
|||
xdsX509TrustManager = createX509TrustManager(certs, certificateValidationContext);
|
||||
}
|
||||
|
||||
private XdsTrustManagerFactory(
|
||||
Map<String, List<X509Certificate>> spiffeTrustMap,
|
||||
CertificateValidationContext certificateValidationContext,
|
||||
boolean validationContextIsStatic)
|
||||
throws CertStoreException {
|
||||
if (validationContextIsStatic) {
|
||||
checkArgument(
|
||||
certificateValidationContext == null || !certificateValidationContext.hasTrustedCa(),
|
||||
"only static certificateValidationContext expected");
|
||||
xdsX509TrustManager = createX509TrustManager(spiffeTrustMap, certificateValidationContext);
|
||||
}
|
||||
}
|
||||
|
||||
private static X509Certificate[] getTrustedCaFromCertContext(
|
||||
CertificateValidationContext certificateValidationContext)
|
||||
throws CertificateException, IOException {
|
||||
|
|
@ -100,6 +122,24 @@ public final class XdsTrustManagerFactory extends SimpleTrustManagerFactory {
|
|||
@VisibleForTesting
|
||||
static XdsX509TrustManager createX509TrustManager(
|
||||
X509Certificate[] certs, CertificateValidationContext certContext) throws CertStoreException {
|
||||
return new XdsX509TrustManager(certContext, createTrustManager(certs));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static XdsX509TrustManager createX509TrustManager(
|
||||
Map<String, List<X509Certificate>> spiffeTrustMapFile,
|
||||
CertificateValidationContext certContext) throws CertStoreException {
|
||||
checkNotNull(spiffeTrustMapFile, "spiffeTrustMapFile");
|
||||
Map<String, X509ExtendedTrustManager> delegates = new HashMap<>();
|
||||
for (Map.Entry<String, List<X509Certificate>> entry:spiffeTrustMapFile.entrySet()) {
|
||||
delegates.put(entry.getKey(), createTrustManager(
|
||||
entry.getValue().toArray(new X509Certificate[0])));
|
||||
}
|
||||
return new XdsX509TrustManager(certContext, delegates);
|
||||
}
|
||||
|
||||
private static X509ExtendedTrustManager createTrustManager(X509Certificate[] certs)
|
||||
throws CertStoreException {
|
||||
TrustManagerFactory tmf = null;
|
||||
try {
|
||||
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
|
|
@ -131,7 +171,7 @@ public final class XdsTrustManagerFactory extends SimpleTrustManagerFactory {
|
|||
if (myDelegate == null) {
|
||||
throw new CertStoreException("Native X509 TrustManager not found.");
|
||||
}
|
||||
return new XdsX509TrustManager(certContext, myDelegate);
|
||||
return myDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -19,18 +19,25 @@ package io.grpc.xds.internal.security.trust;
|
|||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.re2j.Pattern;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext;
|
||||
import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher;
|
||||
import io.envoyproxy.envoy.type.matcher.v3.StringMatcher;
|
||||
import io.grpc.internal.SpiffeUtil;
|
||||
import java.net.Socket;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
|
|
@ -51,6 +58,7 @@ final class XdsX509TrustManager extends X509ExtendedTrustManager implements X509
|
|||
private static final int ALT_IPA_NAME = 7;
|
||||
|
||||
private final X509ExtendedTrustManager delegate;
|
||||
private final Map<String, X509ExtendedTrustManager> spiffeTrustMapDelegates;
|
||||
private final CertificateValidationContext certContext;
|
||||
|
||||
XdsX509TrustManager(@Nullable CertificateValidationContext certContext,
|
||||
|
|
@ -58,6 +66,15 @@ final class XdsX509TrustManager extends X509ExtendedTrustManager implements X509
|
|||
checkNotNull(delegate, "delegate");
|
||||
this.certContext = certContext;
|
||||
this.delegate = delegate;
|
||||
this.spiffeTrustMapDelegates = null;
|
||||
}
|
||||
|
||||
XdsX509TrustManager(@Nullable CertificateValidationContext certContext,
|
||||
Map<String, X509ExtendedTrustManager> spiffeTrustMapDelegates) {
|
||||
checkNotNull(spiffeTrustMapDelegates, "spiffeTrustMapDelegates");
|
||||
this.spiffeTrustMapDelegates = ImmutableMap.copyOf(spiffeTrustMapDelegates);
|
||||
this.certContext = certContext;
|
||||
this.delegate = null;
|
||||
}
|
||||
|
||||
private static boolean verifyDnsNameInPattern(
|
||||
|
|
@ -204,21 +221,21 @@ final class XdsX509TrustManager extends X509ExtendedTrustManager implements X509
|
|||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
|
||||
throws CertificateException {
|
||||
delegate.checkClientTrusted(chain, authType, socket);
|
||||
chooseDelegate(chain).checkClientTrusted(chain, authType, socket);
|
||||
verifySubjectAltNameInChain(chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine)
|
||||
throws CertificateException {
|
||||
delegate.checkClientTrusted(chain, authType, sslEngine);
|
||||
chooseDelegate(chain).checkClientTrusted(chain, authType, sslEngine);
|
||||
verifySubjectAltNameInChain(chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
delegate.checkClientTrusted(chain, authType);
|
||||
chooseDelegate(chain).checkClientTrusted(chain, authType);
|
||||
verifySubjectAltNameInChain(chain);
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +250,7 @@ final class XdsX509TrustManager extends X509ExtendedTrustManager implements X509
|
|||
sslSocket.setSSLParameters(sslParams);
|
||||
}
|
||||
}
|
||||
delegate.checkServerTrusted(chain, authType, socket);
|
||||
chooseDelegate(chain).checkServerTrusted(chain, authType, socket);
|
||||
verifySubjectAltNameInChain(chain);
|
||||
}
|
||||
|
||||
|
|
@ -245,19 +262,44 @@ final class XdsX509TrustManager extends X509ExtendedTrustManager implements X509
|
|||
sslParams.setEndpointIdentificationAlgorithm("");
|
||||
sslEngine.setSSLParameters(sslParams);
|
||||
}
|
||||
delegate.checkServerTrusted(chain, authType, sslEngine);
|
||||
chooseDelegate(chain).checkServerTrusted(chain, authType, sslEngine);
|
||||
verifySubjectAltNameInChain(chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
delegate.checkServerTrusted(chain, authType);
|
||||
chooseDelegate(chain).checkServerTrusted(chain, authType);
|
||||
verifySubjectAltNameInChain(chain);
|
||||
}
|
||||
|
||||
private X509ExtendedTrustManager chooseDelegate(X509Certificate[] chain)
|
||||
throws CertificateException {
|
||||
if (spiffeTrustMapDelegates != null) {
|
||||
Optional<SpiffeUtil.SpiffeId> spiffeId = SpiffeUtil.extractSpiffeId(chain);
|
||||
if (!spiffeId.isPresent()) {
|
||||
throw new CertificateException("Failed to extract SPIFFE ID from peer leaf certificate");
|
||||
}
|
||||
String trustDomain = spiffeId.get().getTrustDomain();
|
||||
if (!spiffeTrustMapDelegates.containsKey(trustDomain)) {
|
||||
throw new CertificateException(String.format("Spiffe Trust Map doesn't contain trust"
|
||||
+ " domain '%s' from peer leaf certificate", trustDomain));
|
||||
}
|
||||
return spiffeTrustMapDelegates.get(trustDomain);
|
||||
} else {
|
||||
return delegate;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
if (spiffeTrustMapDelegates != null) {
|
||||
Set<X509Certificate> result = new HashSet<>();
|
||||
for (X509ExtendedTrustManager tm: spiffeTrustMapDelegates.values()) {
|
||||
result.addAll(Arrays.asList(tm.getAcceptedIssuers()));
|
||||
}
|
||||
return result.toArray(new X509Certificate[0]);
|
||||
}
|
||||
return delegate.getAcceptedIssuers();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ public class CommonBootstrapperTestUtils {
|
|||
String certInstanceName1, @Nullable String privateKey1,
|
||||
@Nullable String cert1,
|
||||
@Nullable String trustCa1, String certInstanceName2, String privateKey2, String cert2,
|
||||
String trustCa2) {
|
||||
String trustCa2, @Nullable String spiffeTrustMap) {
|
||||
// get temp file for each file
|
||||
try {
|
||||
if (privateKey1 != null) {
|
||||
|
|
@ -109,6 +109,9 @@ public class CommonBootstrapperTestUtils {
|
|||
if (trustCa2 != null) {
|
||||
trustCa2 = CommonTlsContextTestsUtil.getTempFileNameForResourcesFile(trustCa2);
|
||||
}
|
||||
if (spiffeTrustMap != null) {
|
||||
spiffeTrustMap = CommonTlsContextTestsUtil.getTempFileNameForResourcesFile(spiffeTrustMap);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
|
|
@ -116,6 +119,9 @@ public class CommonBootstrapperTestUtils {
|
|||
config.put("certificate_file", cert1);
|
||||
config.put("private_key_file", privateKey1);
|
||||
config.put("ca_certificate_file", trustCa1);
|
||||
if (spiffeTrustMap != null) {
|
||||
config.put("spiffe_trust_bundle_map_file", spiffeTrustMap);
|
||||
}
|
||||
Bootstrapper.CertificateProviderInfo certificateProviderInfo =
|
||||
Bootstrapper.CertificateProviderInfo.create("file_watcher", config);
|
||||
HashMap<String, Bootstrapper.CertificateProviderInfo> certProviders =
|
||||
|
|
@ -126,6 +132,9 @@ public class CommonBootstrapperTestUtils {
|
|||
config.put("certificate_file", cert2);
|
||||
config.put("private_key_file", privateKey2);
|
||||
config.put("ca_certificate_file", trustCa2);
|
||||
if (spiffeTrustMap != null) {
|
||||
config.put("spiffe_trust_bundle_map_file", spiffeTrustMap);
|
||||
}
|
||||
certificateProviderInfo =
|
||||
Bootstrapper.CertificateProviderInfo.create("file_watcher", config);
|
||||
certProviders.put(certInstanceName2, certificateProviderInfo);
|
||||
|
|
|
|||
|
|
@ -24,8 +24,12 @@ import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.BAD_SERVER
|
|||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CA_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CLIENT_KEY_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CLIENT_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CLIENT_SPIFFE_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SERVER_1_SPIFFE_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SPIFFE_TRUST_MAP_1_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SPIFFE_TRUST_MAP_FILE;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
|
@ -67,6 +71,7 @@ import io.grpc.xds.internal.Matchers.HeaderMatcher;
|
|||
import io.grpc.xds.internal.security.CommonTlsContextTestsUtil;
|
||||
import io.grpc.xds.internal.security.SslContextProviderSupplier;
|
||||
import io.grpc.xds.internal.security.TlsContextManagerImpl;
|
||||
import io.grpc.xds.internal.security.certprovider.FileWatcherCertificateProviderProvider;
|
||||
import io.netty.handler.ssl.NotSslRecordException;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
|
@ -85,6 +90,7 @@ import java.security.cert.CertificateException;
|
|||
import java.security.cert.CertificateFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
|
|
@ -92,18 +98,25 @@ import java.util.concurrent.TimeUnit;
|
|||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link XdsChannelCredentials} and {@link XdsServerBuilder} for plaintext/TLS/mTLS
|
||||
* modes.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
@RunWith(Parameterized.class)
|
||||
public class XdsSecurityClientServerTest {
|
||||
|
||||
@Parameter
|
||||
public Boolean enableSpiffe;
|
||||
private Boolean originalEnableSpiffe;
|
||||
|
||||
@Rule public final GrpcCleanupRule cleanupRule = new GrpcCleanupRule();
|
||||
private int port;
|
||||
private FakeNameResolverFactory fakeNameResolverFactory;
|
||||
|
|
@ -115,11 +128,27 @@ public class XdsSecurityClientServerTest {
|
|||
private FakeXdsClientPoolFactory fakePoolFactory = new FakeXdsClientPoolFactory(xdsClient);
|
||||
private static final String OVERRIDE_AUTHORITY = "foo.test.google.fr";
|
||||
|
||||
@Parameters(name = "enableSpiffe={0}")
|
||||
public static Collection<Boolean> data() {
|
||||
return ImmutableList.of(true, false);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
saveEnvironment();
|
||||
FileWatcherCertificateProviderProvider.enableSpiffe = enableSpiffe;
|
||||
}
|
||||
|
||||
private void saveEnvironment() {
|
||||
originalEnableSpiffe = FileWatcherCertificateProviderProvider.enableSpiffe;
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws IOException {
|
||||
if (fakeNameResolverFactory != null) {
|
||||
NameResolverRegistry.getDefaultRegistry().deregister(fakeNameResolverFactory);
|
||||
}
|
||||
FileWatcherCertificateProviderProvider.enableSpiffe = originalEnableSpiffe;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -146,13 +175,13 @@ public class XdsSecurityClientServerTest {
|
|||
@Test
|
||||
public void tlsClientServer_noClientAuthentication() throws Exception {
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(null, null, null, null, false, false);
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
|
||||
null, false, false);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
// for TLS, client only needs trustCa
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
CLIENT_KEY_FILE,
|
||||
CLIENT_PEM_FILE, false);
|
||||
CLIENT_KEY_FILE, CLIENT_PEM_FILE, null, false);
|
||||
|
||||
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
|
||||
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
|
||||
|
|
@ -169,7 +198,8 @@ public class XdsSecurityClientServerTest {
|
|||
try {
|
||||
setTrustStoreSystemProperties(trustStoreFilePath.toAbsolutePath().toString());
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(null, null, null, null, false, false);
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
|
||||
null, false, false);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
UpstreamTlsContext upstreamTlsContext =
|
||||
|
|
@ -195,7 +225,8 @@ public class XdsSecurityClientServerTest {
|
|||
try {
|
||||
setTrustStoreSystemProperties(trustStoreFilePath.toAbsolutePath().toString());
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(null, null, null, null, false, false);
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
|
||||
null, false, false);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
UpstreamTlsContext upstreamTlsContext =
|
||||
|
|
@ -221,7 +252,8 @@ public class XdsSecurityClientServerTest {
|
|||
try {
|
||||
setTrustStoreSystemProperties(trustStoreFilePath.toAbsolutePath().toString());
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(null, null, null, null, false, false);
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
|
||||
null, false, false);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
UpstreamTlsContext upstreamTlsContext =
|
||||
|
|
@ -252,17 +284,59 @@ public class XdsSecurityClientServerTest {
|
|||
return trustStoreFile.toPath();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tlsClientServer_Spiffe_noClientAuthentication() throws Exception {
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_SPIFFE_PEM_FILE, null, null, null,
|
||||
null, null, false, false);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
// for TLS, client only needs trustCa, so BAD certs don't matter
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
BAD_CLIENT_KEY_FILE, BAD_CLIENT_PEM_FILE, SPIFFE_TRUST_MAP_FILE, false);
|
||||
|
||||
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
|
||||
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
|
||||
assertThat(unaryRpc(/* requestMessage= */ "buddy", blockingStub)).isEqualTo("Hello buddy");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tlsClientServer_Spiffe_noClientAuthentication_wrongServerCert() throws Exception {
|
||||
if (!enableSpiffe) {
|
||||
return;
|
||||
}
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
|
||||
null, false, false);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
// for TLS, client only needs trustCa, so BAD certs don't matter
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
BAD_CLIENT_KEY_FILE, BAD_CLIENT_PEM_FILE, SPIFFE_TRUST_MAP_FILE, false);
|
||||
|
||||
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
|
||||
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
|
||||
try {
|
||||
unaryRpc("buddy", blockingStub);
|
||||
fail("exception expected");
|
||||
} catch (StatusRuntimeException sre) {
|
||||
assertThat(sre.getStatus().getCode()).isEqualTo(Status.UNAVAILABLE.getCode());
|
||||
assertThat(sre.getCause().getCause().getMessage())
|
||||
.contains("Failed to extract SPIFFE ID from peer leaf certificate");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requireClientAuth_noClientCert_expectException()
|
||||
throws Exception {
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(null, null, null, null, true, true);
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
|
||||
null, true, true);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
// for TLS, client only uses trustCa
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
CLIENT_KEY_FILE,
|
||||
CLIENT_PEM_FILE, false);
|
||||
CLIENT_KEY_FILE, CLIENT_PEM_FILE, null, false);
|
||||
|
||||
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
|
||||
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
|
||||
|
|
@ -284,12 +358,12 @@ public class XdsSecurityClientServerTest {
|
|||
@Test
|
||||
public void noClientAuth_sendBadClientCert_passes() throws Exception {
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(null, null, null, null, false, false);
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
|
||||
null, false, false);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
BAD_CLIENT_KEY_FILE,
|
||||
BAD_CLIENT_PEM_FILE, true);
|
||||
BAD_CLIENT_KEY_FILE, BAD_CLIENT_PEM_FILE, null, true);
|
||||
|
||||
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
|
||||
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
|
||||
|
|
@ -299,8 +373,7 @@ public class XdsSecurityClientServerTest {
|
|||
@Test
|
||||
public void mtls_badClientCert_expectException() throws Exception {
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
BAD_CLIENT_KEY_FILE,
|
||||
BAD_CLIENT_PEM_FILE, true);
|
||||
BAD_CLIENT_KEY_FILE, BAD_CLIENT_PEM_FILE, null, true);
|
||||
try {
|
||||
performMtlsTestAndGetListenerWatcher(upstreamTlsContext, null, null, null, null);
|
||||
fail("exception expected");
|
||||
|
|
@ -316,20 +389,58 @@ public class XdsSecurityClientServerTest {
|
|||
}
|
||||
}
|
||||
|
||||
/** mTLS - client auth enabled - using {@link XdsChannelCredentials} API. */
|
||||
/** mTLS with Spiffe Trust Bundle - client auth enabled - using {@link XdsChannelCredentials}
|
||||
* API. */
|
||||
@Test
|
||||
public void mtlsClientServer_Spiffe_withClientAuthentication_withXdsChannelCreds()
|
||||
throws Exception {
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_SPIFFE_PEM_FILE, null, null, null,
|
||||
null, SPIFFE_TRUST_MAP_1_FILE, true, true);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
CLIENT_KEY_FILE, CLIENT_SPIFFE_PEM_FILE, SPIFFE_TRUST_MAP_1_FILE, true);
|
||||
|
||||
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
|
||||
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
|
||||
assertThat(unaryRpc(/* requestMessage= */ "buddy", blockingStub)).isEqualTo("Hello buddy");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mtlsClientServer_Spiffe_badClientCert_expectException()
|
||||
throws Exception {
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_SPIFFE_PEM_FILE, null, null, null,
|
||||
null, SPIFFE_TRUST_MAP_1_FILE, true, true);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
CLIENT_KEY_FILE, BAD_CLIENT_PEM_FILE, SPIFFE_TRUST_MAP_1_FILE, true);
|
||||
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
|
||||
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
|
||||
try {
|
||||
assertThat(unaryRpc(/* requestMessage= */ "buddy", blockingStub)).isEqualTo("Hello buddy");
|
||||
fail("exception expected");
|
||||
} catch (StatusRuntimeException sre) {
|
||||
assertThat(sre.getStatus().getCode()).isEqualTo(Status.UNAVAILABLE.getCode());
|
||||
assertThat(sre.getMessage()).contains("ssl exception");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mtlsClientServer_withClientAuthentication_withXdsChannelCreds()
|
||||
throws Exception {
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
CLIENT_KEY_FILE,
|
||||
CLIENT_PEM_FILE, true);
|
||||
CLIENT_KEY_FILE, CLIENT_PEM_FILE, null, true);
|
||||
performMtlsTestAndGetListenerWatcher(upstreamTlsContext, null, null, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tlsServer_plaintextClient_expectException() throws Exception {
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(null, null, null, null, false, false);
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
|
||||
null, false, false);
|
||||
buildServerWithTlsContext(downstreamTlsContext);
|
||||
|
||||
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
|
||||
|
|
@ -349,8 +460,7 @@ public class XdsSecurityClientServerTest {
|
|||
|
||||
// for TLS, client only needs trustCa
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
CLIENT_KEY_FILE,
|
||||
CLIENT_PEM_FILE, false);
|
||||
CLIENT_KEY_FILE, CLIENT_PEM_FILE, null, false);
|
||||
|
||||
SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub =
|
||||
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
|
||||
|
|
@ -368,8 +478,7 @@ public class XdsSecurityClientServerTest {
|
|||
public void mtlsClientServer_changeServerContext_expectException()
|
||||
throws Exception {
|
||||
UpstreamTlsContext upstreamTlsContext = setBootstrapInfoAndBuildUpstreamTlsContext(
|
||||
CLIENT_KEY_FILE,
|
||||
CLIENT_PEM_FILE, true);
|
||||
CLIENT_KEY_FILE, CLIENT_PEM_FILE, null, true);
|
||||
|
||||
performMtlsTestAndGetListenerWatcher(upstreamTlsContext, "cert-instance-name2",
|
||||
BAD_SERVER_KEY_FILE, BAD_SERVER_PEM_FILE, CA_PEM_FILE);
|
||||
|
|
@ -396,8 +505,8 @@ public class XdsSecurityClientServerTest {
|
|||
String privateKey2, String cert2, String trustCa2)
|
||||
throws Exception {
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(certInstanceName2, privateKey2, cert2,
|
||||
trustCa2, true, true);
|
||||
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, certInstanceName2,
|
||||
privateKey2, cert2, trustCa2, null, true, false);
|
||||
|
||||
buildServerWithFallbackServerCredentials(
|
||||
InsecureServerCredentials.create(), downstreamTlsContext);
|
||||
|
|
@ -408,22 +517,21 @@ public class XdsSecurityClientServerTest {
|
|||
}
|
||||
|
||||
private DownstreamTlsContext setBootstrapInfoAndBuildDownstreamTlsContext(
|
||||
String certInstanceName2,
|
||||
String privateKey2,
|
||||
String cert2, String trustCa2, boolean hasRootCert, boolean requireClientCertificate) {
|
||||
String cert1, String certInstanceName2, String privateKey2,
|
||||
String cert2, String trustCa2, String spiffeFile,
|
||||
boolean hasRootCert, boolean requireClientCertificate) {
|
||||
bootstrapInfoForServer = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-server", SERVER_1_KEY_FILE,
|
||||
SERVER_1_PEM_FILE, CA_PEM_FILE, certInstanceName2, privateKey2, cert2, trustCa2);
|
||||
cert1, CA_PEM_FILE, certInstanceName2, privateKey2, cert2, trustCa2, spiffeFile);
|
||||
return CommonTlsContextTestsUtil.buildDownstreamTlsContext(
|
||||
"google_cloud_private_spiffe-server", hasRootCert, requireClientCertificate);
|
||||
}
|
||||
|
||||
private UpstreamTlsContext setBootstrapInfoAndBuildUpstreamTlsContext(String clientKeyFile,
|
||||
String clientPemFile,
|
||||
boolean hasIdentityCert) {
|
||||
String clientPemFile, String spiffeFile, boolean hasIdentityCert) {
|
||||
bootstrapInfoForClient = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-client", clientKeyFile, clientPemFile,
|
||||
CA_PEM_FILE, null, null, null, null);
|
||||
CA_PEM_FILE, null, null, null, null, spiffeFile);
|
||||
return CommonTlsContextTestsUtil
|
||||
.buildUpstreamTlsContext("google_cloud_private_spiffe-client", hasIdentityCert);
|
||||
}
|
||||
|
|
@ -434,7 +542,7 @@ public class XdsSecurityClientServerTest {
|
|||
boolean useCombinedValidationContext) {
|
||||
bootstrapInfoForClient = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-client", clientKeyFile, clientPemFile,
|
||||
CA_PEM_FILE, null, null, null, null);
|
||||
CA_PEM_FILE, null, null, null, null, null);
|
||||
if (useCombinedValidationContext) {
|
||||
return CommonTlsContextTestsUtil.buildUpstreamTlsContextForCertProviderInstance(
|
||||
"google_cloud_private_spiffe-client", "ROOT", null,
|
||||
|
|
|
|||
|
|
@ -48,10 +48,14 @@ public class CommonTlsContextTestsUtil {
|
|||
public static final String SERVER_0_PEM_FILE = "server0.pem";
|
||||
public static final String SERVER_0_KEY_FILE = "server0.key";
|
||||
public static final String SERVER_1_PEM_FILE = "server1.pem";
|
||||
public static final String SERVER_1_SPIFFE_PEM_FILE = "server1_spiffe.pem";
|
||||
public static final String SERVER_1_KEY_FILE = "server1.key";
|
||||
public static final String CLIENT_PEM_FILE = "client.pem";
|
||||
public static final String CLIENT_SPIFFE_PEM_FILE = "client_spiffe.pem";
|
||||
public static final String CLIENT_KEY_FILE = "client.key";
|
||||
public static final String CA_PEM_FILE = "ca.pem";
|
||||
public static final String SPIFFE_TRUST_MAP_FILE = "spiffebundle.json";
|
||||
public static final String SPIFFE_TRUST_MAP_1_FILE = "spiffebundle1.json";
|
||||
/** Bad/untrusted server certs. */
|
||||
public static final String BAD_SERVER_PEM_FILE = "badserver.pem";
|
||||
public static final String BAD_SERVER_KEY_FILE = "badserver.key";
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ public class SecurityProtocolNegotiatorsTest {
|
|||
CommonCertProviderTestUtils.register(executor);
|
||||
Bootstrapper.BootstrapInfo bootstrapInfoForClient = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-client", CLIENT_KEY_FILE, CLIENT_PEM_FILE,
|
||||
CA_PEM_FILE, null, null, null, null);
|
||||
CA_PEM_FILE, null, null, null, null, null);
|
||||
UpstreamTlsContext upstreamTlsContext =
|
||||
CommonTlsContextTestsUtil
|
||||
.buildUpstreamTlsContext("google_cloud_private_spiffe-client", true);
|
||||
|
|
@ -216,7 +216,7 @@ public class SecurityProtocolNegotiatorsTest {
|
|||
pipeline = channel.pipeline();
|
||||
Bootstrapper.BootstrapInfo bootstrapInfoForServer = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-server", SERVER_1_KEY_FILE,
|
||||
SERVER_1_PEM_FILE, CA_PEM_FILE, null, null, null, null);
|
||||
SERVER_1_PEM_FILE, CA_PEM_FILE, null, null, null, null, null);
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
CommonTlsContextTestsUtil.buildDownstreamTlsContext(
|
||||
"google_cloud_private_spiffe-server", true, true);
|
||||
|
|
@ -361,7 +361,7 @@ public class SecurityProtocolNegotiatorsTest {
|
|||
CommonCertProviderTestUtils.register(executor);
|
||||
Bootstrapper.BootstrapInfo bootstrapInfoForClient = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-client", CLIENT_KEY_FILE, CLIENT_PEM_FILE,
|
||||
CA_PEM_FILE, null, null, null, null);
|
||||
CA_PEM_FILE, null, null, null, null, null);
|
||||
UpstreamTlsContext upstreamTlsContext =
|
||||
CommonTlsContextTestsUtil
|
||||
.buildUpstreamTlsContext("google_cloud_private_spiffe-client", true);
|
||||
|
|
@ -412,7 +412,7 @@ public class SecurityProtocolNegotiatorsTest {
|
|||
CommonCertProviderTestUtils.register(executor);
|
||||
Bootstrapper.BootstrapInfo bootstrapInfoForClient = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-client", CLIENT_KEY_FILE, CLIENT_PEM_FILE,
|
||||
CA_PEM_FILE, null, null, null, null);
|
||||
CA_PEM_FILE, null, null, null, null, null);
|
||||
UpstreamTlsContext upstreamTlsContext =
|
||||
CommonTlsContextTestsUtil
|
||||
.buildUpstreamTlsContext("google_cloud_private_spiffe-client", true);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public class TlsContextManagerTest {
|
|||
public void createServerSslContextProvider() {
|
||||
Bootstrapper.BootstrapInfo bootstrapInfoForServer = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-server", SERVER_1_KEY_FILE,
|
||||
SERVER_1_PEM_FILE, CA_PEM_FILE, null, null, null, null);
|
||||
SERVER_1_PEM_FILE, CA_PEM_FILE, null, null, null, null, null);
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
CommonTlsContextTestsUtil.buildDownstreamTlsContext(
|
||||
"google_cloud_private_spiffe-server", false, false);
|
||||
|
|
@ -76,7 +76,7 @@ public class TlsContextManagerTest {
|
|||
public void createClientSslContextProvider() {
|
||||
Bootstrapper.BootstrapInfo bootstrapInfoForClient = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-client", CLIENT_KEY_FILE, CLIENT_PEM_FILE,
|
||||
CA_PEM_FILE, null, null, null, null);
|
||||
CA_PEM_FILE, null, null, null, null, null);
|
||||
UpstreamTlsContext upstreamTlsContext =
|
||||
CommonTlsContextTestsUtil
|
||||
.buildUpstreamTlsContext("google_cloud_private_spiffe-client", false);
|
||||
|
|
@ -96,7 +96,7 @@ public class TlsContextManagerTest {
|
|||
Bootstrapper.BootstrapInfo bootstrapInfoForServer = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-server", SERVER_1_KEY_FILE,
|
||||
SERVER_1_PEM_FILE, CA_PEM_FILE, "cert-instance2", SERVER_0_KEY_FILE, SERVER_0_PEM_FILE,
|
||||
CA_PEM_FILE);
|
||||
CA_PEM_FILE, null);
|
||||
DownstreamTlsContext downstreamTlsContext =
|
||||
CommonTlsContextTestsUtil.buildDownstreamTlsContext(
|
||||
"google_cloud_private_spiffe-server", false, false);
|
||||
|
|
@ -120,7 +120,7 @@ public class TlsContextManagerTest {
|
|||
public void createClientSslContextProvider_differentInstance() {
|
||||
Bootstrapper.BootstrapInfo bootstrapInfoForClient = CommonBootstrapperTestUtils
|
||||
.buildBootstrapInfo("google_cloud_private_spiffe-client", CLIENT_KEY_FILE, CLIENT_PEM_FILE,
|
||||
CA_PEM_FILE, "cert-instance-2", CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE);
|
||||
CA_PEM_FILE, "cert-instance-2", CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE, null);
|
||||
UpstreamTlsContext upstreamTlsContext =
|
||||
CommonTlsContextTestsUtil
|
||||
.buildUpstreamTlsContext("google_cloud_private_spiffe-client", false);
|
||||
|
|
|
|||
|
|
@ -24,22 +24,28 @@ import static org.mockito.Mockito.times;
|
|||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.grpc.internal.JsonParser;
|
||||
import io.grpc.internal.TimeProvider;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
/** Unit tests for {@link FileWatcherCertificateProviderProvider}. */
|
||||
@RunWith(JUnit4.class)
|
||||
@RunWith(Parameterized.class)
|
||||
public class FileWatcherCertificateProviderProviderTest {
|
||||
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
|
||||
|
||||
|
|
@ -48,13 +54,28 @@ public class FileWatcherCertificateProviderProviderTest {
|
|||
scheduledExecutorServiceFactory;
|
||||
@Mock private TimeProvider timeProvider;
|
||||
|
||||
@Parameter
|
||||
public boolean enableSpiffe;
|
||||
private boolean originalEnableSpiffe;
|
||||
private FileWatcherCertificateProviderProvider provider;
|
||||
|
||||
@Parameters(name = "enableSpiffe={0}")
|
||||
public static Collection<Boolean> data() {
|
||||
return ImmutableList.of(true, false);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
provider =
|
||||
new FileWatcherCertificateProviderProvider(
|
||||
fileWatcherCertificateProviderFactory, scheduledExecutorServiceFactory, timeProvider);
|
||||
originalEnableSpiffe = FileWatcherCertificateProviderProvider.enableSpiffe;
|
||||
FileWatcherCertificateProviderProvider.enableSpiffe = enableSpiffe;
|
||||
}
|
||||
|
||||
@After
|
||||
public void restoreEnvironment() {
|
||||
FileWatcherCertificateProviderProvider.enableSpiffe = originalEnableSpiffe;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -85,6 +106,30 @@ public class FileWatcherCertificateProviderProviderTest {
|
|||
eq("/var/run/gke-spiffe/certs/certificates.pem"),
|
||||
eq("/var/run/gke-spiffe/certs/private_key.pem"),
|
||||
eq("/var/run/gke-spiffe/certs/ca_certificates.pem"),
|
||||
eq(null),
|
||||
eq(600L),
|
||||
eq(mockService),
|
||||
eq(timeProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProvider_minimalSpiffeConfig() throws IOException {
|
||||
Assume.assumeTrue(enableSpiffe);
|
||||
CertificateProvider.DistributorWatcher distWatcher =
|
||||
new CertificateProvider.DistributorWatcher();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(MINIMAL_FILE_WATCHER_WITH_SPIFFE_CONFIG);
|
||||
ScheduledExecutorService mockService = mock(ScheduledExecutorService.class);
|
||||
when(scheduledExecutorServiceFactory.create()).thenReturn(mockService);
|
||||
provider.createCertificateProvider(map, distWatcher, true);
|
||||
verify(fileWatcherCertificateProviderFactory, times(1))
|
||||
.create(
|
||||
eq(distWatcher),
|
||||
eq(true),
|
||||
eq("/var/run/gke-spiffe/certs/certificates.pem"),
|
||||
eq("/var/run/gke-spiffe/certs/private_key.pem"),
|
||||
eq(null),
|
||||
eq("/var/run/gke-spiffe/certs/spiffe_bundle.json"),
|
||||
eq(600L),
|
||||
eq(mockService),
|
||||
eq(timeProvider));
|
||||
|
|
@ -106,6 +151,30 @@ public class FileWatcherCertificateProviderProviderTest {
|
|||
eq("/var/run/gke-spiffe/certs/certificates2.pem"),
|
||||
eq("/var/run/gke-spiffe/certs/private_key3.pem"),
|
||||
eq("/var/run/gke-spiffe/certs/ca_certificates4.pem"),
|
||||
eq(null),
|
||||
eq(7890L),
|
||||
eq(mockService),
|
||||
eq(timeProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createProvider_spiffeConfig() throws IOException {
|
||||
Assume.assumeTrue(enableSpiffe);
|
||||
CertificateProvider.DistributorWatcher distWatcher =
|
||||
new CertificateProvider.DistributorWatcher();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(FULL_FILE_WATCHER_WITH_SPIFFE_CONFIG);
|
||||
ScheduledExecutorService mockService = mock(ScheduledExecutorService.class);
|
||||
when(scheduledExecutorServiceFactory.create()).thenReturn(mockService);
|
||||
provider.createCertificateProvider(map, distWatcher, true);
|
||||
verify(fileWatcherCertificateProviderFactory, times(1))
|
||||
.create(
|
||||
eq(distWatcher),
|
||||
eq(true),
|
||||
eq("/var/run/gke-spiffe/certs/certificates2.pem"),
|
||||
eq("/var/run/gke-spiffe/certs/private_key3.pem"),
|
||||
eq(null),
|
||||
eq("/var/run/gke-spiffe/certs/spiffe_bundle.json"),
|
||||
eq(7890L),
|
||||
eq(mockService),
|
||||
eq(timeProvider));
|
||||
|
|
@ -157,15 +226,18 @@ public class FileWatcherCertificateProviderProviderTest {
|
|||
|
||||
@Test
|
||||
public void createProvider_missingRoot_expectException() throws IOException {
|
||||
String expectedMessage = enableSpiffe ? "either 'ca_certificate_file' or "
|
||||
+ "'spiffe_trust_bundle_map_file' is required in the config"
|
||||
: "'ca_certificate_file' is required in the config";
|
||||
CertificateProvider.DistributorWatcher distWatcher =
|
||||
new CertificateProvider.DistributorWatcher();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(MISSING_ROOT_CONFIG);
|
||||
Map<String, ?> map = (Map<String, ?>) JsonParser.parse(MISSING_ROOT_AND_SPIFFE_CONFIG);
|
||||
try {
|
||||
provider.createCertificateProvider(map, distWatcher, true);
|
||||
fail("exception expected");
|
||||
} catch (NullPointerException npe) {
|
||||
assertThat(npe).hasMessageThat().isEqualTo("'ca_certificate_file' is required in the config");
|
||||
assertThat(npe).hasMessageThat().isEqualTo(expectedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,6 +248,14 @@ public class FileWatcherCertificateProviderProviderTest {
|
|||
+ " \"ca_certificate_file\": \"/var/run/gke-spiffe/certs/ca_certificates.pem\""
|
||||
+ " }";
|
||||
|
||||
private static final String MINIMAL_FILE_WATCHER_WITH_SPIFFE_CONFIG =
|
||||
"{\n"
|
||||
+ " \"certificate_file\": \"/var/run/gke-spiffe/certs/certificates.pem\","
|
||||
+ " \"private_key_file\": \"/var/run/gke-spiffe/certs/private_key.pem\","
|
||||
+ " \"spiffe_trust_bundle_map_file\":"
|
||||
+ " \"/var/run/gke-spiffe/certs/spiffe_bundle.json\""
|
||||
+ " }";
|
||||
|
||||
private static final String FULL_FILE_WATCHER_CONFIG =
|
||||
"{\n"
|
||||
+ " \"certificate_file\": \"/var/run/gke-spiffe/certs/certificates2.pem\","
|
||||
|
|
@ -184,6 +264,16 @@ public class FileWatcherCertificateProviderProviderTest {
|
|||
+ " \"refresh_interval\": \"7890s\""
|
||||
+ " }";
|
||||
|
||||
private static final String FULL_FILE_WATCHER_WITH_SPIFFE_CONFIG =
|
||||
"{\n"
|
||||
+ " \"certificate_file\": \"/var/run/gke-spiffe/certs/certificates2.pem\","
|
||||
+ " \"private_key_file\": \"/var/run/gke-spiffe/certs/private_key3.pem\","
|
||||
+ " \"ca_certificate_file\": \"/var/run/gke-spiffe/certs/ca_certificates4.pem\","
|
||||
+ " \"spiffe_trust_bundle_map_file\":"
|
||||
+ " \"/var/run/gke-spiffe/certs/spiffe_bundle.json\","
|
||||
+ " \"refresh_interval\": \"7890s\""
|
||||
+ " }";
|
||||
|
||||
private static final String MISSING_CERT_CONFIG =
|
||||
"{\n"
|
||||
+ " \"private_key_file\": \"/var/run/gke-spiffe/certs/private_key.pem\","
|
||||
|
|
@ -196,7 +286,7 @@ public class FileWatcherCertificateProviderProviderTest {
|
|||
+ " \"ca_certificate_file\": \"/var/run/gke-spiffe/certs/ca_certificates.pem\""
|
||||
+ " }";
|
||||
|
||||
private static final String MISSING_ROOT_CONFIG =
|
||||
private static final String MISSING_ROOT_AND_SPIFFE_CONFIG =
|
||||
"{\n"
|
||||
+ " \"certificate_file\": \"/var/run/gke-spiffe/certs/certificates.pem\","
|
||||
+ " \"private_key_file\": \"/var/run/gke-spiffe/certs/private_key.pem\""
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CLIENT_PEM
|
|||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SERVER_0_KEY_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SERVER_0_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SPIFFE_TRUST_MAP_1_FILE;
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
|
|
@ -47,6 +48,7 @@ import java.security.cert.CertificateException;
|
|||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
|
@ -73,6 +75,7 @@ public class FileWatcherCertificateProviderTest {
|
|||
private static final String CERT_FILE = "cert.pem";
|
||||
private static final String KEY_FILE = "key.pem";
|
||||
private static final String ROOT_FILE = "root.pem";
|
||||
private static final String SPIFFE_TRUST_MAP_FILE = "spiffebundle.json";
|
||||
|
||||
@Mock private CertificateProvider.Watcher mockWatcher;
|
||||
@Mock private ScheduledExecutorService timeService;
|
||||
|
|
@ -84,28 +87,33 @@ public class FileWatcherCertificateProviderTest {
|
|||
private String certFile;
|
||||
private String keyFile;
|
||||
private String rootFile;
|
||||
private String spiffeTrustMapFile;
|
||||
|
||||
private FileWatcherCertificateProvider provider;
|
||||
private DistributorWatcher watcher;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
DistributorWatcher watcher = new DistributorWatcher();
|
||||
watcher = new DistributorWatcher();
|
||||
watcher.addWatcher(mockWatcher);
|
||||
|
||||
certFile = new File(tempFolder.getRoot(), CERT_FILE).getAbsolutePath();
|
||||
keyFile = new File(tempFolder.getRoot(), KEY_FILE).getAbsolutePath();
|
||||
rootFile = new File(tempFolder.getRoot(), ROOT_FILE).getAbsolutePath();
|
||||
spiffeTrustMapFile = new File(tempFolder.getRoot(), SPIFFE_TRUST_MAP_FILE).getAbsolutePath();
|
||||
provider =
|
||||
new FileWatcherCertificateProvider(
|
||||
watcher, true, certFile, keyFile, rootFile, 600L, timeService, timeProvider);
|
||||
new FileWatcherCertificateProvider(watcher, true, certFile, keyFile, rootFile, null, 600L,
|
||||
timeService, timeProvider);
|
||||
}
|
||||
|
||||
private void populateTarget(
|
||||
String certFileSource,
|
||||
String keyFileSource,
|
||||
String rootFileSource,
|
||||
String spiffeTrustMapFileSource,
|
||||
boolean deleteCurCert,
|
||||
boolean deleteCurKey,
|
||||
boolean deleteCurSpiffeTrustMap,
|
||||
boolean deleteCurRoot)
|
||||
throws IOException {
|
||||
if (deleteCurCert) {
|
||||
|
|
@ -135,6 +143,17 @@ public class FileWatcherCertificateProviderTest {
|
|||
Files.setLastModifiedTime(
|
||||
Paths.get(rootFile), FileTime.fromMillis(timeProvider.currentTimeMillis()));
|
||||
}
|
||||
if (deleteCurSpiffeTrustMap) {
|
||||
Files.delete(Paths.get(spiffeTrustMapFile));
|
||||
}
|
||||
if (spiffeTrustMapFileSource != null) {
|
||||
spiffeTrustMapFileSource = CommonTlsContextTestsUtil
|
||||
.getTempFileNameForResourcesFile(spiffeTrustMapFileSource);
|
||||
Files.copy(Paths.get(spiffeTrustMapFileSource),
|
||||
Paths.get(spiffeTrustMapFile), REPLACE_EXISTING);
|
||||
Files.setLastModifiedTime(
|
||||
Paths.get(spiffeTrustMapFile), FileTime.fromMillis(timeProvider.currentTimeMillis()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -144,9 +163,9 @@ public class FileWatcherCertificateProviderTest {
|
|||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, false, false, false);
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, null, false, false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
verifyWatcherUpdates(CLIENT_PEM_FILE, CA_PEM_FILE);
|
||||
verifyWatcherUpdates(CLIENT_PEM_FILE, CA_PEM_FILE, null);
|
||||
verifyTimeServiceAndScheduledFuture();
|
||||
|
||||
reset(mockWatcher, timeService);
|
||||
|
|
@ -165,7 +184,7 @@ public class FileWatcherCertificateProviderTest {
|
|||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, false, false, false);
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, null, false, false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
|
||||
reset(mockWatcher, timeService);
|
||||
|
|
@ -173,9 +192,10 @@ public class FileWatcherCertificateProviderTest {
|
|||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
timeProvider.forwardTime(1, TimeUnit.SECONDS);
|
||||
populateTarget(SERVER_0_PEM_FILE, SERVER_0_KEY_FILE, SERVER_1_PEM_FILE, false, false, false);
|
||||
populateTarget(SERVER_0_PEM_FILE, SERVER_0_KEY_FILE, SERVER_1_PEM_FILE, null, false, false,
|
||||
false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
verifyWatcherUpdates(SERVER_0_PEM_FILE, SERVER_1_PEM_FILE);
|
||||
verifyWatcherUpdates(SERVER_0_PEM_FILE, SERVER_1_PEM_FILE, null);
|
||||
verifyTimeServiceAndScheduledFuture();
|
||||
}
|
||||
|
||||
|
|
@ -186,12 +206,13 @@ public class FileWatcherCertificateProviderTest {
|
|||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, false, false, false);
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, null, false, false, false, false);
|
||||
provider.close();
|
||||
provider.checkAndReloadCertificates();
|
||||
verify(mockWatcher, never())
|
||||
.updateCertificate(any(PrivateKey.class), ArgumentMatchers.<X509Certificate>anyList());
|
||||
verify(mockWatcher, never()).updateTrustedRoots(ArgumentMatchers.<X509Certificate>anyList());
|
||||
verify(mockWatcher, never()).updateSpiffeTrustMap(ArgumentMatchers.anyMap());
|
||||
verify(timeService, never()).schedule(any(Runnable.class), any(Long.TYPE), any(TimeUnit.class));
|
||||
verify(timeService, times(1)).shutdownNow();
|
||||
}
|
||||
|
|
@ -204,7 +225,7 @@ public class FileWatcherCertificateProviderTest {
|
|||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, false, false, false);
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, null, false, false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
|
||||
reset(mockWatcher, timeService);
|
||||
|
|
@ -212,9 +233,9 @@ public class FileWatcherCertificateProviderTest {
|
|||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
timeProvider.forwardTime(1, TimeUnit.SECONDS);
|
||||
populateTarget(null, null, SERVER_1_PEM_FILE, false, false, false);
|
||||
populateTarget(null, null, SERVER_1_PEM_FILE, null, false, false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
verifyWatcherUpdates(null, SERVER_1_PEM_FILE);
|
||||
verifyWatcherUpdates(null, SERVER_1_PEM_FILE, null);
|
||||
verifyTimeServiceAndScheduledFuture();
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +247,7 @@ public class FileWatcherCertificateProviderTest {
|
|||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, false, false, false);
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, CA_PEM_FILE, null, false, false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
|
||||
reset(mockWatcher, timeService);
|
||||
|
|
@ -234,9 +255,44 @@ public class FileWatcherCertificateProviderTest {
|
|||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
timeProvider.forwardTime(1, TimeUnit.SECONDS);
|
||||
populateTarget(SERVER_0_PEM_FILE, SERVER_0_KEY_FILE, null, false, false, false);
|
||||
populateTarget(SERVER_0_PEM_FILE, SERVER_0_KEY_FILE, null, null, false, false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
verifyWatcherUpdates(SERVER_0_PEM_FILE, null);
|
||||
verifyWatcherUpdates(SERVER_0_PEM_FILE, null, null);
|
||||
verifyTimeServiceAndScheduledFuture();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spiffeTrustMapFileUpdateOnly() throws Exception {
|
||||
provider = new FileWatcherCertificateProvider(watcher, true, certFile, keyFile, null,
|
||||
spiffeTrustMapFile, 600L, timeService, timeProvider);
|
||||
TestScheduledFuture<?> scheduledFuture =
|
||||
new TestScheduledFuture<>();
|
||||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, null, null, false, false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
verify(mockWatcher, never()).updateSpiffeTrustMap(ArgumentMatchers.anyMap());
|
||||
|
||||
reset(timeService);
|
||||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
timeProvider.forwardTime(1, TimeUnit.SECONDS);
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, null, SPIFFE_TRUST_MAP_FILE, false,
|
||||
false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
verify(mockWatcher, times(1)).updateSpiffeTrustMap(ArgumentMatchers.anyMap());
|
||||
|
||||
reset(timeService);
|
||||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
timeProvider.forwardTime(1, TimeUnit.SECONDS);
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, null, SPIFFE_TRUST_MAP_1_FILE, false,
|
||||
false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
verify(mockWatcher, times(2)).updateSpiffeTrustMap(ArgumentMatchers.anyMap());
|
||||
verifyTimeServiceAndScheduledFuture();
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +303,7 @@ public class FileWatcherCertificateProviderTest {
|
|||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
populateTarget(null, CLIENT_KEY_FILE, CA_PEM_FILE, false, false, false);
|
||||
populateTarget(null, CLIENT_KEY_FILE, CA_PEM_FILE, null, false, false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
verifyWatcherErrorUpdates(Status.Code.UNKNOWN, NoSuchFileException.class, 0, 1, "cert.pem");
|
||||
}
|
||||
|
|
@ -255,13 +311,14 @@ public class FileWatcherCertificateProviderTest {
|
|||
@Test
|
||||
public void getCertificate_missingCertFile() throws IOException, InterruptedException {
|
||||
commonErrorTest(
|
||||
null, CLIENT_KEY_FILE, CA_PEM_FILE, NoSuchFileException.class, 0, 1, 0, 0, "cert.pem");
|
||||
null, CLIENT_KEY_FILE, CA_PEM_FILE, null, NoSuchFileException.class, 0, 1, 0, 0,
|
||||
"cert.pem");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCertificate_missingKeyFile() throws IOException, InterruptedException {
|
||||
commonErrorTest(
|
||||
CLIENT_PEM_FILE, null, CA_PEM_FILE, NoSuchFileException.class, 0, 1, 0, 0, "key.pem");
|
||||
CLIENT_PEM_FILE, null, CA_PEM_FILE, null, NoSuchFileException.class, 0, 1, 0, 0, "key.pem");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -270,6 +327,7 @@ public class FileWatcherCertificateProviderTest {
|
|||
CLIENT_PEM_FILE,
|
||||
SERVER_0_PEM_FILE,
|
||||
CA_PEM_FILE,
|
||||
null,
|
||||
java.security.spec.InvalidKeySpecException.class,
|
||||
0,
|
||||
1,
|
||||
|
|
@ -285,12 +343,13 @@ public class FileWatcherCertificateProviderTest {
|
|||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
populateTarget(SERVER_0_PEM_FILE, SERVER_0_KEY_FILE, SERVER_1_PEM_FILE, false, false, false);
|
||||
populateTarget(SERVER_0_PEM_FILE, SERVER_0_KEY_FILE, SERVER_1_PEM_FILE, null, false, false,
|
||||
false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
|
||||
reset(mockWatcher);
|
||||
timeProvider.forwardTime(1, TimeUnit.SECONDS);
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, null, false, false, true);
|
||||
populateTarget(CLIENT_PEM_FILE, CLIENT_KEY_FILE, null, null, false, false, false, true);
|
||||
timeProvider.forwardTime(
|
||||
CERT0_EXPIRY_TIME_MILLIS - 610_000L - timeProvider.currentTimeMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
|
@ -302,6 +361,7 @@ public class FileWatcherCertificateProviderTest {
|
|||
String certFile,
|
||||
String keyFile,
|
||||
String rootFile,
|
||||
String spiffeFile,
|
||||
Class<?> throwableType,
|
||||
int firstUpdateCertCount,
|
||||
int firstUpdateRootCount,
|
||||
|
|
@ -314,13 +374,15 @@ public class FileWatcherCertificateProviderTest {
|
|||
doReturn(scheduledFuture)
|
||||
.when(timeService)
|
||||
.schedule(any(Runnable.class), any(Long.TYPE), eq(TimeUnit.SECONDS));
|
||||
populateTarget(SERVER_0_PEM_FILE, SERVER_0_KEY_FILE, SERVER_1_PEM_FILE, false, false, false);
|
||||
populateTarget(SERVER_0_PEM_FILE, SERVER_0_KEY_FILE, SERVER_1_PEM_FILE,
|
||||
SPIFFE_TRUST_MAP_1_FILE, false, false, false, false);
|
||||
provider.checkAndReloadCertificates();
|
||||
|
||||
reset(mockWatcher);
|
||||
timeProvider.forwardTime(1, TimeUnit.SECONDS);
|
||||
populateTarget(
|
||||
certFile, keyFile, rootFile, certFile == null, keyFile == null, rootFile == null);
|
||||
certFile, keyFile, rootFile, spiffeFile, certFile == null, keyFile == null,
|
||||
rootFile == null, spiffeFile == null);
|
||||
timeProvider.forwardTime(
|
||||
CERT0_EXPIRY_TIME_MILLIS - 610_000L - timeProvider.currentTimeMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
|
@ -372,7 +434,7 @@ public class FileWatcherCertificateProviderTest {
|
|||
assertThat(provider.scheduledFuture.isCancelled()).isFalse();
|
||||
}
|
||||
|
||||
private void verifyWatcherUpdates(String certPemFile, String rootPemFile)
|
||||
private void verifyWatcherUpdates(String certPemFile, String rootPemFile, String spiffeFile)
|
||||
throws IOException, CertificateException {
|
||||
if (certPemFile != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -399,6 +461,17 @@ public class FileWatcherCertificateProviderTest {
|
|||
} else {
|
||||
verify(mockWatcher, never()).updateTrustedRoots(ArgumentMatchers.<X509Certificate>anyList());
|
||||
}
|
||||
if (spiffeFile != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ArgumentCaptor<Map<String, List<X509Certificate>>> spiffeCaptor =
|
||||
ArgumentCaptor.forClass(Map.class);
|
||||
verify(mockWatcher, times(1)).updateSpiffeTrustMap(spiffeCaptor.capture());
|
||||
Map<String, List<X509Certificate>> trustMap = spiffeCaptor.getValue();
|
||||
assertThat(trustMap).hasSize(2);
|
||||
verify(mockWatcher, never()).onError(any(Status.class));
|
||||
} else {
|
||||
verify(mockWatcher, never()).updateSpiffeTrustMap(ArgumentMatchers.anyMap());
|
||||
}
|
||||
}
|
||||
|
||||
static class TestScheduledFuture<V> implements ScheduledFuture<V> {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CA_PEM_FIL
|
|||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CLIENT_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.envoyproxy.envoy.config.core.v3.DataSource;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext;
|
||||
|
|
@ -105,6 +107,46 @@ public class XdsTrustManagerFactoryTest {
|
|||
.isEqualTo(CertificateUtils.toX509Certificates(TlsTesting.loadCert(CA_PEM_FILE))[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructor_fromSpiffeTrustMap()
|
||||
throws CertificateException, IOException, CertStoreException {
|
||||
X509Certificate x509Cert = TestUtils.loadX509Cert(CA_PEM_FILE);
|
||||
CertificateValidationContext staticValidationContext = buildStaticValidationContext("san1",
|
||||
"san2");
|
||||
// Single domain and single cert
|
||||
XdsTrustManagerFactory factory = new XdsTrustManagerFactory(ImmutableMap
|
||||
.of("example.com", ImmutableList.of(x509Cert)), staticValidationContext);
|
||||
assertThat(factory).isNotNull();
|
||||
TrustManager[] tms = factory.getTrustManagers();
|
||||
assertThat(tms).isNotNull();
|
||||
assertThat(tms).hasLength(1);
|
||||
TrustManager myTm = tms[0];
|
||||
assertThat(myTm).isInstanceOf(XdsX509TrustManager.class);
|
||||
XdsX509TrustManager xdsX509TrustManager = (XdsX509TrustManager) myTm;
|
||||
assertThat(xdsX509TrustManager.getAcceptedIssuers()).isNotNull();
|
||||
assertThat(xdsX509TrustManager.getAcceptedIssuers()).hasLength(1);
|
||||
assertThat(xdsX509TrustManager.getAcceptedIssuers()[0].getIssuerX500Principal().getName())
|
||||
.isEqualTo("CN=testca,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU");
|
||||
// Multiple domains and multiple certs for one of it
|
||||
X509Certificate anotherCert = TestUtils.loadX509Cert(CLIENT_PEM_FILE);
|
||||
factory = new XdsTrustManagerFactory(ImmutableMap
|
||||
.of("example.com", ImmutableList.of(x509Cert),
|
||||
"google.com", ImmutableList.of(x509Cert, anotherCert)), staticValidationContext);
|
||||
assertThat(factory).isNotNull();
|
||||
tms = factory.getTrustManagers();
|
||||
assertThat(tms).isNotNull();
|
||||
assertThat(tms).hasLength(1);
|
||||
myTm = tms[0];
|
||||
assertThat(myTm).isInstanceOf(XdsX509TrustManager.class);
|
||||
xdsX509TrustManager = (XdsX509TrustManager) myTm;
|
||||
assertThat(xdsX509TrustManager.getAcceptedIssuers()).isNotNull();
|
||||
assertThat(xdsX509TrustManager.getAcceptedIssuers()).hasLength(2);
|
||||
assertThat(xdsX509TrustManager.getAcceptedIssuers()[0].getIssuerX500Principal().getName())
|
||||
.isEqualTo("CN=testca,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU");
|
||||
assertThat(xdsX509TrustManager.getAcceptedIssuers()[1].getIssuerX500Principal().getName())
|
||||
.isEqualTo("CN=testca,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorRootCert_checkServerTrusted()
|
||||
throws CertificateException, IOException, CertStoreException {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ import static com.google.common.truth.Truth.assertThat;
|
|||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.BAD_SERVER_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CA_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CLIENT_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.CLIENT_SPIFFE_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE;
|
||||
import static io.grpc.xds.internal.security.CommonTlsContextTestsUtil.SERVER_1_SPIFFE_PEM_FILE;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
|
@ -30,6 +32,7 @@ import static org.mockito.Mockito.verify;
|
|||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext;
|
||||
import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher;
|
||||
import io.envoyproxy.envoy.type.matcher.v3.StringMatcher;
|
||||
|
|
@ -38,6 +41,7 @@ import java.io.IOException;
|
|||
import java.security.cert.CertStoreException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
|
@ -537,6 +541,71 @@ public class XdsX509TrustManagerTest {
|
|||
assertThat(sslEngine.getSSLParameters().getEndpointIdentificationAlgorithm()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkServerTrustedSslEngineSpiffeTrustMap()
|
||||
throws CertificateException, IOException, CertStoreException {
|
||||
TestSslEngine sslEngine = buildTrustManagerAndGetSslEngine();
|
||||
X509Certificate[] serverCerts =
|
||||
CertificateUtils.toX509Certificates(TlsTesting.loadCert(SERVER_1_SPIFFE_PEM_FILE));
|
||||
List<X509Certificate> caCerts = Arrays.asList(CertificateUtils
|
||||
.toX509Certificates(TlsTesting.loadCert(CA_PEM_FILE)));
|
||||
trustManager = XdsTrustManagerFactory.createX509TrustManager(
|
||||
ImmutableMap.of("example.com", caCerts), null);
|
||||
trustManager.checkServerTrusted(serverCerts, "ECDHE_ECDSA", sslEngine);
|
||||
verify(sslEngine, times(1)).getHandshakeSession();
|
||||
assertThat(sslEngine.getSSLParameters().getEndpointIdentificationAlgorithm()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkServerTrustedSslEngineSpiffeTrustMap_missing_spiffe_id()
|
||||
throws CertificateException, IOException, CertStoreException {
|
||||
TestSslEngine sslEngine = buildTrustManagerAndGetSslEngine();
|
||||
X509Certificate[] serverCerts =
|
||||
CertificateUtils.toX509Certificates(TlsTesting.loadCert(SERVER_1_PEM_FILE));
|
||||
List<X509Certificate> caCerts = Arrays.asList(CertificateUtils
|
||||
.toX509Certificates(TlsTesting.loadCert(CA_PEM_FILE)));
|
||||
trustManager = XdsTrustManagerFactory.createX509TrustManager(
|
||||
ImmutableMap.of("example.com", caCerts), null);
|
||||
try {
|
||||
trustManager.checkServerTrusted(serverCerts, "ECDHE_ECDSA", sslEngine);
|
||||
fail("exception expected");
|
||||
} catch (CertificateException expected) {
|
||||
assertThat(expected).hasMessageThat()
|
||||
.isEqualTo("Failed to extract SPIFFE ID from peer leaf certificate");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkServerTrustedSpiffeSslEngineTrustMap_missing_trust_domain()
|
||||
throws CertificateException, IOException, CertStoreException {
|
||||
TestSslEngine sslEngine = buildTrustManagerAndGetSslEngine();
|
||||
X509Certificate[] serverCerts =
|
||||
CertificateUtils.toX509Certificates(TlsTesting.loadCert(SERVER_1_SPIFFE_PEM_FILE));
|
||||
List<X509Certificate> caCerts = Arrays.asList(CertificateUtils
|
||||
.toX509Certificates(TlsTesting.loadCert(CA_PEM_FILE)));
|
||||
trustManager = XdsTrustManagerFactory.createX509TrustManager(
|
||||
ImmutableMap.of("unknown.com", caCerts), null);
|
||||
try {
|
||||
trustManager.checkServerTrusted(serverCerts, "ECDHE_ECDSA", sslEngine);
|
||||
fail("exception expected");
|
||||
} catch (CertificateException expected) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Spiffe Trust Map doesn't contain trust"
|
||||
+ " domain 'example.com' from peer leaf certificate");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkClientTrustedSpiffeTrustMap()
|
||||
throws CertificateException, IOException, CertStoreException {
|
||||
X509Certificate[] clientCerts =
|
||||
CertificateUtils.toX509Certificates(TlsTesting.loadCert(CLIENT_SPIFFE_PEM_FILE));
|
||||
List<X509Certificate> caCerts = Arrays.asList(CertificateUtils
|
||||
.toX509Certificates(TlsTesting.loadCert(CA_PEM_FILE)));
|
||||
trustManager = XdsTrustManagerFactory.createX509TrustManager(
|
||||
ImmutableMap.of("foo.bar.com", caCerts), null);
|
||||
trustManager.checkClientTrusted(clientCerts, "RSA");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkServerTrustedSslEngine_untrustedServer_expectException()
|
||||
throws CertificateException, IOException, CertStoreException {
|
||||
|
|
@ -565,6 +634,22 @@ public class XdsX509TrustManagerTest {
|
|||
assertThat(sslSocket.getSSLParameters().getEndpointIdentificationAlgorithm()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkServerTrustedSslSocketSpiffeTrustMap()
|
||||
throws CertificateException, IOException, CertStoreException {
|
||||
TestSslSocket sslSocket = buildTrustManagerAndGetSslSocket();
|
||||
X509Certificate[] serverCerts =
|
||||
CertificateUtils.toX509Certificates(TlsTesting.loadCert(SERVER_1_SPIFFE_PEM_FILE));
|
||||
List<X509Certificate> caCerts = Arrays.asList(CertificateUtils
|
||||
.toX509Certificates(TlsTesting.loadCert(CA_PEM_FILE)));
|
||||
trustManager = XdsTrustManagerFactory.createX509TrustManager(
|
||||
ImmutableMap.of("example.com", caCerts), null);
|
||||
trustManager.checkServerTrusted(serverCerts, "ECDHE_ECDSA", sslSocket);
|
||||
verify(sslSocket, times(1)).isConnected();
|
||||
verify(sslSocket, times(1)).getHandshakeSession();
|
||||
assertThat(sslSocket.getSSLParameters().getEndpointIdentificationAlgorithm()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkServerTrustedSslSocket_untrustedServer_expectException()
|
||||
throws CertificateException, IOException, CertStoreException {
|
||||
|
|
|
|||
Loading…
Reference in New Issue