1 /* 2 * Copyright 2020 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.xds.internal.security.certprovider; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import com.google.common.annotations.VisibleForTesting; 22 import io.grpc.Status; 23 import io.grpc.xds.internal.security.Closeable; 24 import java.security.PrivateKey; 25 import java.security.cert.X509Certificate; 26 import java.util.Collections; 27 import java.util.HashSet; 28 import java.util.List; 29 import java.util.Set; 30 31 /** 32 * A plug-in that provides certificates required by the xDS security component and created 33 * using the certificate-provider config from the xDS server. 34 * 35 * <p>We may move this out of the internal package and make this an official API in the future. 36 * 37 * <p>The plugin fetches certificates - root and optionally identity cert - required by xDS 38 * security. 39 */ 40 public abstract class CertificateProvider implements Closeable { 41 42 /** A watcher is registered to receive certificate updates. */ 43 public interface Watcher { updateCertificate(PrivateKey key, List<X509Certificate> certChain)44 void updateCertificate(PrivateKey key, List<X509Certificate> certChain); 45 updateTrustedRoots(List<X509Certificate> trustedRoots)46 void updateTrustedRoots(List<X509Certificate> trustedRoots); 47 onError(Status errorStatus)48 void onError(Status errorStatus); 49 } 50 51 @VisibleForTesting 52 public static final class DistributorWatcher implements Watcher { 53 private PrivateKey privateKey; 54 private List<X509Certificate> certChain; 55 private List<X509Certificate> trustedRoots; 56 57 @VisibleForTesting 58 final Set<Watcher> downstreamWatchers = new HashSet<>(); 59 addWatcher(Watcher watcher)60 synchronized void addWatcher(Watcher watcher) { 61 downstreamWatchers.add(watcher); 62 if (privateKey != null && certChain != null) { 63 sendLastCertificateUpdate(watcher); 64 } 65 if (trustedRoots != null) { 66 sendLastTrustedRootsUpdate(watcher); 67 } 68 } 69 removeWatcher(Watcher watcher)70 synchronized void removeWatcher(Watcher watcher) { 71 downstreamWatchers.remove(watcher); 72 } 73 getDownstreamWatchers()74 @VisibleForTesting public Set<Watcher> getDownstreamWatchers() { 75 return Collections.unmodifiableSet(downstreamWatchers); 76 } 77 sendLastCertificateUpdate(Watcher watcher)78 private void sendLastCertificateUpdate(Watcher watcher) { 79 watcher.updateCertificate(privateKey, certChain); 80 } 81 sendLastTrustedRootsUpdate(Watcher watcher)82 private void sendLastTrustedRootsUpdate(Watcher watcher) { 83 watcher.updateTrustedRoots(trustedRoots); 84 } 85 86 @Override updateCertificate(PrivateKey key, List<X509Certificate> certChain)87 public synchronized void updateCertificate(PrivateKey key, List<X509Certificate> certChain) { 88 checkNotNull(key, "key"); 89 checkNotNull(certChain, "certChain"); 90 privateKey = key; 91 this.certChain = certChain; 92 for (Watcher watcher : downstreamWatchers) { 93 sendLastCertificateUpdate(watcher); 94 } 95 } 96 97 @Override updateTrustedRoots(List<X509Certificate> trustedRoots)98 public synchronized void updateTrustedRoots(List<X509Certificate> trustedRoots) { 99 checkNotNull(trustedRoots, "trustedRoots"); 100 this.trustedRoots = trustedRoots; 101 for (Watcher watcher : downstreamWatchers) { 102 sendLastTrustedRootsUpdate(watcher); 103 } 104 } 105 106 @Override onError(Status errorStatus)107 public synchronized void onError(Status errorStatus) { 108 for (Watcher watcher : downstreamWatchers) { 109 watcher.onError(errorStatus); 110 } 111 } 112 getLastIdentityCert()113 X509Certificate getLastIdentityCert() { 114 if (certChain != null && !certChain.isEmpty()) { 115 return certChain.get(0); 116 } 117 return null; 118 } 119 close()120 void close() { 121 downstreamWatchers.clear(); 122 clearValues(); 123 } 124 clearValues()125 void clearValues() { 126 privateKey = null; 127 certChain = null; 128 trustedRoots = null; 129 } 130 } 131 132 /** 133 * Concrete subclasses will call this to register the {@link Watcher}. 134 * 135 * @param watcher to register 136 * @param notifyCertUpdates if true, the provider is required to call the watcher’s 137 * updateCertificate method. Implies the Provider is capable of minting certificates. 138 * Used by server-side and mTLS client-side. Note the Provider is always required 139 * to call updateTrustedRoots to provide trusted-root updates. 140 */ CertificateProvider(DistributorWatcher watcher, boolean notifyCertUpdates)141 protected CertificateProvider(DistributorWatcher watcher, boolean notifyCertUpdates) { 142 this.watcher = watcher; 143 this.notifyCertUpdates = notifyCertUpdates; 144 } 145 146 /** Releases all resources and stop cert refreshes and watcher updates. */ 147 @Override close()148 public abstract void close(); 149 150 /** Starts the cert refresh and watcher update cycle. */ start()151 public abstract void start(); 152 153 private final DistributorWatcher watcher; 154 private final boolean notifyCertUpdates; 155 getWatcher()156 public DistributorWatcher getWatcher() { 157 return watcher; 158 } 159 isNotifyCertUpdates()160 public boolean isNotifyCertUpdates() { 161 return notifyCertUpdates; 162 } 163 164 165 } 166