diff options
Diffstat (limited to 'vendor/github.com/hashicorp/go-plugin/server.go')
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/server.go | 165 |
1 files changed, 140 insertions, 25 deletions
diff --git a/vendor/github.com/hashicorp/go-plugin/server.go b/vendor/github.com/hashicorp/go-plugin/server.go index 1e808b9..fc9f05a 100644 --- a/vendor/github.com/hashicorp/go-plugin/server.go +++ b/vendor/github.com/hashicorp/go-plugin/server.go | |||
@@ -2,6 +2,7 @@ package plugin | |||
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "crypto/tls" | 4 | "crypto/tls" |
5 | "crypto/x509" | ||
5 | "encoding/base64" | 6 | "encoding/base64" |
6 | "errors" | 7 | "errors" |
7 | "fmt" | 8 | "fmt" |
@@ -11,7 +12,9 @@ import ( | |||
11 | "os" | 12 | "os" |
12 | "os/signal" | 13 | "os/signal" |
13 | "runtime" | 14 | "runtime" |
15 | "sort" | ||
14 | "strconv" | 16 | "strconv" |
17 | "strings" | ||
15 | "sync/atomic" | 18 | "sync/atomic" |
16 | 19 | ||
17 | "github.com/hashicorp/go-hclog" | 20 | "github.com/hashicorp/go-hclog" |
@@ -36,6 +39,8 @@ type HandshakeConfig struct { | |||
36 | // ProtocolVersion is the version that clients must match on to | 39 | // ProtocolVersion is the version that clients must match on to |
37 | // agree they can communicate. This should match the ProtocolVersion | 40 | // agree they can communicate. This should match the ProtocolVersion |
38 | // set on ClientConfig when using a plugin. | 41 | // set on ClientConfig when using a plugin. |
42 | // This field is not required if VersionedPlugins are being used in the | ||
43 | // Client or Server configurations. | ||
39 | ProtocolVersion uint | 44 | ProtocolVersion uint |
40 | 45 | ||
41 | // MagicCookieKey and value are used as a very basic verification | 46 | // MagicCookieKey and value are used as a very basic verification |
@@ -46,6 +51,10 @@ type HandshakeConfig struct { | |||
46 | MagicCookieValue string | 51 | MagicCookieValue string |
47 | } | 52 | } |
48 | 53 | ||
54 | // PluginSet is a set of plugins provided to be registered in the plugin | ||
55 | // server. | ||
56 | type PluginSet map[string]Plugin | ||
57 | |||
49 | // ServeConfig configures what sorts of plugins are served. | 58 | // ServeConfig configures what sorts of plugins are served. |
50 | type ServeConfig struct { | 59 | type ServeConfig struct { |
51 | // HandshakeConfig is the configuration that must match clients. | 60 | // HandshakeConfig is the configuration that must match clients. |
@@ -55,7 +64,13 @@ type ServeConfig struct { | |||
55 | TLSProvider func() (*tls.Config, error) | 64 | TLSProvider func() (*tls.Config, error) |
56 | 65 | ||
57 | // Plugins are the plugins that are served. | 66 | // Plugins are the plugins that are served. |
58 | Plugins map[string]Plugin | 67 | // The implied version of this PluginSet is the Handshake.ProtocolVersion. |
68 | Plugins PluginSet | ||
69 | |||
70 | // VersionedPlugins is a map of PluginSets for specific protocol versions. | ||
71 | // These can be used to negotiate a compatible version between client and | ||
72 | // server. If this is set, Handshake.ProtocolVersion is not required. | ||
73 | VersionedPlugins map[int]PluginSet | ||
59 | 74 | ||
60 | // GRPCServer should be non-nil to enable serving the plugins over | 75 | // GRPCServer should be non-nil to enable serving the plugins over |
61 | // gRPC. This is a function to create the server when needed with the | 76 | // gRPC. This is a function to create the server when needed with the |
@@ -72,14 +87,83 @@ type ServeConfig struct { | |||
72 | Logger hclog.Logger | 87 | Logger hclog.Logger |
73 | } | 88 | } |
74 | 89 | ||
75 | // Protocol returns the protocol that this server should speak. | 90 | // protocolVersion determines the protocol version and plugin set to be used by |
76 | func (c *ServeConfig) Protocol() Protocol { | 91 | // the server. In the event that there is no suitable version, the last version |
77 | result := ProtocolNetRPC | 92 | // in the config is returned leaving the client to report the incompatibility. |
78 | if c.GRPCServer != nil { | 93 | func protocolVersion(opts *ServeConfig) (int, Protocol, PluginSet) { |
79 | result = ProtocolGRPC | 94 | protoVersion := int(opts.ProtocolVersion) |
95 | pluginSet := opts.Plugins | ||
96 | protoType := ProtocolNetRPC | ||
97 | // Check if the client sent a list of acceptable versions | ||
98 | var clientVersions []int | ||
99 | if vs := os.Getenv("PLUGIN_PROTOCOL_VERSIONS"); vs != "" { | ||
100 | for _, s := range strings.Split(vs, ",") { | ||
101 | v, err := strconv.Atoi(s) | ||
102 | if err != nil { | ||
103 | fmt.Fprintf(os.Stderr, "server sent invalid plugin version %q", s) | ||
104 | continue | ||
105 | } | ||
106 | clientVersions = append(clientVersions, v) | ||
107 | } | ||
108 | } | ||
109 | |||
110 | // We want to iterate in reverse order, to ensure we match the newest | ||
111 | // compatible plugin version. | ||
112 | sort.Sort(sort.Reverse(sort.IntSlice(clientVersions))) | ||
113 | |||
114 | // set the old un-versioned fields as if they were versioned plugins | ||
115 | if opts.VersionedPlugins == nil { | ||
116 | opts.VersionedPlugins = make(map[int]PluginSet) | ||
117 | } | ||
118 | |||
119 | if pluginSet != nil { | ||
120 | opts.VersionedPlugins[protoVersion] = pluginSet | ||
80 | } | 121 | } |
81 | 122 | ||
82 | return result | 123 | // Sort the version to make sure we match the latest first |
124 | var versions []int | ||
125 | for v := range opts.VersionedPlugins { | ||
126 | versions = append(versions, v) | ||
127 | } | ||
128 | |||
129 | sort.Sort(sort.Reverse(sort.IntSlice(versions))) | ||
130 | |||
131 | // See if we have multiple versions of Plugins to choose from | ||
132 | for _, version := range versions { | ||
133 | // Record each version, since we guarantee that this returns valid | ||
134 | // values even if they are not a protocol match. | ||
135 | protoVersion = version | ||
136 | pluginSet = opts.VersionedPlugins[version] | ||
137 | |||
138 | // If we have a configured gRPC server we should select a protocol | ||
139 | if opts.GRPCServer != nil { | ||
140 | // All plugins in a set must use the same transport, so check the first | ||
141 | // for the protocol type | ||
142 | for _, p := range pluginSet { | ||
143 | switch p.(type) { | ||
144 | case GRPCPlugin: | ||
145 | protoType = ProtocolGRPC | ||
146 | default: | ||
147 | protoType = ProtocolNetRPC | ||
148 | } | ||
149 | break | ||
150 | } | ||
151 | } | ||
152 | |||
153 | for _, clientVersion := range clientVersions { | ||
154 | if clientVersion == protoVersion { | ||
155 | return protoVersion, protoType, pluginSet | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | // Return the lowest version as the fallback. | ||
161 | // Since we iterated over all the versions in reverse order above, these | ||
162 | // values are from the lowest version number plugins (which may be from | ||
163 | // a combination of the Handshake.ProtocolVersion and ServeConfig.Plugins | ||
164 | // fields). This allows serving the oldest version of our plugins to a | ||
165 | // legacy client that did not send a PLUGIN_PROTOCOL_VERSIONS list. | ||
166 | return protoVersion, protoType, pluginSet | ||
83 | } | 167 | } |
84 | 168 | ||
85 | // Serve serves the plugins given by ServeConfig. | 169 | // Serve serves the plugins given by ServeConfig. |
@@ -107,6 +191,10 @@ func Serve(opts *ServeConfig) { | |||
107 | os.Exit(1) | 191 | os.Exit(1) |
108 | } | 192 | } |
109 | 193 | ||
194 | // negotiate the version and plugins | ||
195 | // start with default version in the handshake config | ||
196 | protoVersion, protoType, pluginSet := protocolVersion(opts) | ||
197 | |||
110 | // Logging goes to the original stderr | 198 | // Logging goes to the original stderr |
111 | log.SetOutput(os.Stderr) | 199 | log.SetOutput(os.Stderr) |
112 | 200 | ||
@@ -155,12 +243,47 @@ func Serve(opts *ServeConfig) { | |||
155 | } | 243 | } |
156 | } | 244 | } |
157 | 245 | ||
246 | var serverCert string | ||
247 | clientCert := os.Getenv("PLUGIN_CLIENT_CERT") | ||
248 | // If the client is configured using AutoMTLS, the certificate will be here, | ||
249 | // and we need to generate our own in response. | ||
250 | if tlsConfig == nil && clientCert != "" { | ||
251 | logger.Info("configuring server automatic mTLS") | ||
252 | clientCertPool := x509.NewCertPool() | ||
253 | if !clientCertPool.AppendCertsFromPEM([]byte(clientCert)) { | ||
254 | logger.Error("client cert provided but failed to parse", "cert", clientCert) | ||
255 | } | ||
256 | |||
257 | certPEM, keyPEM, err := generateCert() | ||
258 | if err != nil { | ||
259 | logger.Error("failed to generate client certificate", "error", err) | ||
260 | panic(err) | ||
261 | } | ||
262 | |||
263 | cert, err := tls.X509KeyPair(certPEM, keyPEM) | ||
264 | if err != nil { | ||
265 | logger.Error("failed to parse client certificate", "error", err) | ||
266 | panic(err) | ||
267 | } | ||
268 | |||
269 | tlsConfig = &tls.Config{ | ||
270 | Certificates: []tls.Certificate{cert}, | ||
271 | ClientAuth: tls.RequireAndVerifyClientCert, | ||
272 | ClientCAs: clientCertPool, | ||
273 | MinVersion: tls.VersionTLS12, | ||
274 | } | ||
275 | |||
276 | // We send back the raw leaf cert data for the client rather than the | ||
277 | // PEM, since the protocol can't handle newlines. | ||
278 | serverCert = base64.RawStdEncoding.EncodeToString(cert.Certificate[0]) | ||
279 | } | ||
280 | |||
158 | // Create the channel to tell us when we're done | 281 | // Create the channel to tell us when we're done |
159 | doneCh := make(chan struct{}) | 282 | doneCh := make(chan struct{}) |
160 | 283 | ||
161 | // Build the server type | 284 | // Build the server type |
162 | var server ServerProtocol | 285 | var server ServerProtocol |
163 | switch opts.Protocol() { | 286 | switch protoType { |
164 | case ProtocolNetRPC: | 287 | case ProtocolNetRPC: |
165 | // If we have a TLS configuration then we wrap the listener | 288 | // If we have a TLS configuration then we wrap the listener |
166 | // ourselves and do it at that level. | 289 | // ourselves and do it at that level. |
@@ -170,7 +293,7 @@ func Serve(opts *ServeConfig) { | |||
170 | 293 | ||
171 | // Create the RPC server to dispense | 294 | // Create the RPC server to dispense |
172 | server = &RPCServer{ | 295 | server = &RPCServer{ |
173 | Plugins: opts.Plugins, | 296 | Plugins: pluginSet, |
174 | Stdout: stdout_r, | 297 | Stdout: stdout_r, |
175 | Stderr: stderr_r, | 298 | Stderr: stderr_r, |
176 | DoneCh: doneCh, | 299 | DoneCh: doneCh, |
@@ -179,16 +302,17 @@ func Serve(opts *ServeConfig) { | |||
179 | case ProtocolGRPC: | 302 | case ProtocolGRPC: |
180 | // Create the gRPC server | 303 | // Create the gRPC server |
181 | server = &GRPCServer{ | 304 | server = &GRPCServer{ |
182 | Plugins: opts.Plugins, | 305 | Plugins: pluginSet, |
183 | Server: opts.GRPCServer, | 306 | Server: opts.GRPCServer, |
184 | TLS: tlsConfig, | 307 | TLS: tlsConfig, |
185 | Stdout: stdout_r, | 308 | Stdout: stdout_r, |
186 | Stderr: stderr_r, | 309 | Stderr: stderr_r, |
187 | DoneCh: doneCh, | 310 | DoneCh: doneCh, |
311 | logger: logger, | ||
188 | } | 312 | } |
189 | 313 | ||
190 | default: | 314 | default: |
191 | panic("unknown server protocol: " + opts.Protocol()) | 315 | panic("unknown server protocol: " + protoType) |
192 | } | 316 | } |
193 | 317 | ||
194 | // Initialize the servers | 318 | // Initialize the servers |
@@ -197,25 +321,16 @@ func Serve(opts *ServeConfig) { | |||
197 | return | 321 | return |
198 | } | 322 | } |
199 | 323 | ||
200 | // Build the extra configuration | ||
201 | extra := "" | ||
202 | if v := server.Config(); v != "" { | ||
203 | extra = base64.StdEncoding.EncodeToString([]byte(v)) | ||
204 | } | ||
205 | if extra != "" { | ||
206 | extra = "|" + extra | ||
207 | } | ||
208 | |||
209 | logger.Debug("plugin address", "network", listener.Addr().Network(), "address", listener.Addr().String()) | 324 | logger.Debug("plugin address", "network", listener.Addr().Network(), "address", listener.Addr().String()) |
210 | 325 | ||
211 | // Output the address and service name to stdout so that core can bring it up. | 326 | // Output the address and service name to stdout so that the client can bring it up. |
212 | fmt.Printf("%d|%d|%s|%s|%s%s\n", | 327 | fmt.Printf("%d|%d|%s|%s|%s|%s\n", |
213 | CoreProtocolVersion, | 328 | CoreProtocolVersion, |
214 | opts.ProtocolVersion, | 329 | protoVersion, |
215 | listener.Addr().Network(), | 330 | listener.Addr().Network(), |
216 | listener.Addr().String(), | 331 | listener.Addr().String(), |
217 | opts.Protocol(), | 332 | protoType, |
218 | extra) | 333 | serverCert) |
219 | os.Stdout.Sync() | 334 | os.Stdout.Sync() |
220 | 335 | ||
221 | // Eat the interrupts | 336 | // Eat the interrupts |