1package vault 2 3import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/hashicorp/vault/helper/namespace" 9 10 multierror "github.com/hashicorp/go-multierror" 11 "github.com/hashicorp/vault/sdk/helper/strutil" 12 "github.com/hashicorp/vault/sdk/logical" 13) 14 15// reloadPluginMounts reloads provided mounts, regardless of 16// plugin name, as long as the backend type is plugin. 17func (c *Core) reloadMatchingPluginMounts(ctx context.Context, mounts []string) error { 18 c.mountsLock.RLock() 19 defer c.mountsLock.RUnlock() 20 c.authLock.RLock() 21 defer c.authLock.RUnlock() 22 23 ns, err := namespace.FromContext(ctx) 24 if err != nil { 25 return err 26 } 27 28 var errors error 29 for _, mount := range mounts { 30 entry := c.router.MatchingMountEntry(ctx, mount) 31 if entry == nil { 32 errors = multierror.Append(errors, fmt.Errorf("cannot fetch mount entry on %q", mount)) 33 continue 34 } 35 36 var isAuth bool 37 fullPath := c.router.MatchingMount(ctx, mount) 38 if strings.HasPrefix(fullPath, credentialRoutePrefix) { 39 isAuth = true 40 } 41 42 // We dont reload mounts that are not in the same namespace 43 if ns.ID != entry.Namespace().ID { 44 continue 45 } 46 47 err := c.reloadBackendCommon(ctx, entry, isAuth) 48 if err != nil { 49 errors = multierror.Append(errors, fmt.Errorf("cannot reload plugin on %q: %w", mount, err)) 50 continue 51 } 52 c.logger.Info("successfully reloaded plugin", "plugin", entry.Accessor, "path", entry.Path) 53 } 54 return errors 55} 56 57// reloadPlugin reloads all mounted backends that are of 58// plugin pluginName (name of the plugin as registered in 59// the plugin catalog). 60func (c *Core) reloadMatchingPlugin(ctx context.Context, pluginName string) error { 61 c.mountsLock.RLock() 62 defer c.mountsLock.RUnlock() 63 c.authLock.RLock() 64 defer c.authLock.RUnlock() 65 66 ns, err := namespace.FromContext(ctx) 67 if err != nil { 68 return err 69 } 70 71 // Filter mount entries that only matches the plugin name 72 for _, entry := range c.mounts.Entries { 73 // We dont reload mounts that are not in the same namespace 74 if ns.ID != entry.Namespace().ID { 75 continue 76 } 77 if entry.Type == pluginName || (entry.Type == "plugin" && entry.Config.PluginName == pluginName) { 78 err := c.reloadBackendCommon(ctx, entry, false) 79 if err != nil { 80 return err 81 } 82 c.logger.Info("successfully reloaded plugin", "plugin", pluginName, "path", entry.Path) 83 } 84 } 85 86 // Filter auth mount entries that ony matches the plugin name 87 for _, entry := range c.auth.Entries { 88 // We dont reload mounts that are not in the same namespace 89 if ns.ID != entry.Namespace().ID { 90 continue 91 } 92 93 if entry.Type == pluginName || (entry.Type == "plugin" && entry.Config.PluginName == pluginName) { 94 err := c.reloadBackendCommon(ctx, entry, true) 95 if err != nil { 96 return err 97 } 98 c.logger.Info("successfully reloaded plugin", "plugin", entry.Accessor, "path", entry.Path) 99 } 100 } 101 102 return nil 103} 104 105// reloadBackendCommon is a generic method to reload a backend provided a 106// MountEntry. 107func (c *Core) reloadBackendCommon(ctx context.Context, entry *MountEntry, isAuth bool) error { 108 // Make sure our cache is up-to-date. Since some singleton mounts can be 109 // tuned, we do this before the below check. 110 entry.SyncCache() 111 112 // We don't want to reload the singleton mounts. They often have specific 113 // inmemory elements and we don't want to touch them here. 114 if strutil.StrListContains(singletonMounts, entry.Type) { 115 c.logger.Debug("skipping reload of singleton mount", "type", entry.Type) 116 return nil 117 } 118 119 path := entry.Path 120 121 if isAuth { 122 path = credentialRoutePrefix + path 123 } 124 125 // Fast-path out if the backend doesn't exist 126 raw, ok := c.router.root.Get(entry.Namespace().Path + path) 127 if !ok { 128 return nil 129 } 130 131 re := raw.(*routeEntry) 132 133 // Grab the lock, this allows requests to drain before we cleanup the 134 // client. 135 re.l.Lock() 136 defer re.l.Unlock() 137 138 // Only call Cleanup if backend is initialized 139 if re.backend != nil { 140 // Call backend's Cleanup routine 141 re.backend.Cleanup(ctx) 142 } 143 144 view := re.storageView 145 viewPath := entry.UUID + "/" 146 switch entry.Table { 147 case mountTableType: 148 viewPath = backendBarrierPrefix + viewPath 149 case credentialTableType: 150 viewPath = credentialBarrierPrefix + viewPath 151 } 152 153 removePathCheckers(c, entry, viewPath) 154 155 sysView := c.mountEntrySysView(entry) 156 157 nilMount, err := preprocessMount(c, entry, view.(*BarrierView)) 158 if err != nil { 159 return err 160 } 161 162 var backend logical.Backend 163 if !isAuth { 164 // Dispense a new backend 165 backend, err = c.newLogicalBackend(ctx, entry, sysView, view) 166 } else { 167 backend, err = c.newCredentialBackend(ctx, entry, sysView, view) 168 } 169 if err != nil { 170 return err 171 } 172 if backend == nil { 173 return fmt.Errorf("nil backend of type %q returned from creation function", entry.Type) 174 } 175 176 addPathCheckers(c, entry, backend, viewPath) 177 178 if nilMount { 179 backend.Cleanup(ctx) 180 backend = nil 181 } 182 183 // Set the backend back 184 re.backend = backend 185 186 if backend != nil { 187 // Set paths as well 188 paths := backend.SpecialPaths() 189 if paths != nil { 190 re.rootPaths.Store(pathsToRadix(paths.Root)) 191 re.loginPaths.Store(pathsToRadix(paths.Unauthenticated)) 192 } 193 } 194 195 return nil 196} 197 198func (c *Core) setupPluginReload() error { 199 return handleSetupPluginReload(c) 200} 201