mirror of https://github.com/status-im/consul.git
222 lines
7.9 KiB
Markdown
222 lines
7.9 KiB
Markdown
|
---
|
||
|
layout: "docs"
|
||
|
page_title: "Connect - Native Application Integration - Go"
|
||
|
sidebar_current: "docs-connect-native-go"
|
||
|
description: |-
|
||
|
We provide a library that makes it drop-in simple to integrate Connect with most Go applications. For most Go applications, Connect can be natively integrated in just a single line of code excluding imports and struct initialization.
|
||
|
---
|
||
|
|
||
|
# Connect-Native Integration with Go
|
||
|
|
||
|
We provide a library that makes it drop-in simple to integrate Connect
|
||
|
with most [Go](https://golang.org/) applications. This page shows examples
|
||
|
of integrating this library for accepting or establishing Connect-based
|
||
|
connections. For most Go applications, Connect can be natively integrated
|
||
|
in just a single line of code excluding imports and struct initialization.
|
||
|
|
||
|
In addition to this, please read and understand the
|
||
|
[overview of Connect-Native integrations](/docs/connect/native.html).
|
||
|
In particular, after integrating applications with Connect, they must declare
|
||
|
that they accept Connect-based connections via their service definitions.
|
||
|
|
||
|
## Accepting Connections
|
||
|
|
||
|
Any server that supports TLS (HTTP, gRPC, net/rpc, etc.) can begin
|
||
|
accepting Connect-based connections in just a few lines of code. For most
|
||
|
existing applications, converting the server to accept Connect-based
|
||
|
connections will require only a one-line change excluding imports and
|
||
|
structure initialization.
|
||
|
|
||
|
The
|
||
|
Go library exposes a `*tls.Config` that _automatically_ communicates with
|
||
|
Consul to load certificates and authorize inbound connections during the
|
||
|
TLS handshake. This also automatically starts goroutines to update any
|
||
|
changing certs.
|
||
|
|
||
|
Example, followed by more details:
|
||
|
|
||
|
```go
|
||
|
import(
|
||
|
"net/http"
|
||
|
|
||
|
"github.com/hashicorp/consul/api"
|
||
|
"github.com/hashicorp/consul/connect"
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
// Create a Consul API client
|
||
|
client, _ := api.NewClient(api.DefaultConfig())
|
||
|
|
||
|
// Create an instance representing this service. "my-service" is the
|
||
|
// name of _this_ service. The service should be cleaned up via Close.
|
||
|
svc, _ := connect.NewService("my-service", client)
|
||
|
defer svc.Close()
|
||
|
|
||
|
// Creating an HTTP server that serves via Connect
|
||
|
server := &http.Server{
|
||
|
Addr: ":8080",
|
||
|
TLSConfig: svc.ServerTLSConfig(),
|
||
|
// ... other standard fields
|
||
|
}
|
||
|
|
||
|
// Serve!
|
||
|
server.ListenAndServerTLS("", "")
|
||
|
}
|
||
|
```
|
||
|
|
||
|
The first step is to create a Consul API client. This is almost always the
|
||
|
default configuration with an ACL token set, since you want to communicate
|
||
|
to the local agent. The Go library will use this client to request certificates,
|
||
|
authorize connections, and more.
|
||
|
|
||
|
Next, `connect.NewService` is called to create a service structure representing
|
||
|
the _currently running service_. This structure maintains all the state
|
||
|
for accepting and establishing connections. An application should generally
|
||
|
create one service and reuse that one service for all servers and clients.
|
||
|
|
||
|
Finally, a standard `*http.Server` is created. The magic line is the `TLSConfig`
|
||
|
value. This is set to a TLS configuration returned by the service structure.
|
||
|
This TLS configuration is configured to automatically load certificates
|
||
|
in the background, cache them, and authorize inbound connections. This
|
||
|
also automatically handles maintaining blocking queries to update certificates
|
||
|
in the background if they change.
|
||
|
|
||
|
Since the service returns a standard `*tls.Config`, _any_ server that supports
|
||
|
TLS can be configured. This includes gRPC, net/rpc, basic TCP, and more.
|
||
|
Another example is shown below with just a plain TLS listener:
|
||
|
|
||
|
```go
|
||
|
import(
|
||
|
"crypto/tls"
|
||
|
|
||
|
"github.com/hashicorp/consul/api"
|
||
|
"github.com/hashicorp/consul/connect"
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
// Create a Consul API client
|
||
|
client, _ := api.NewClient(api.DefaultConfig())
|
||
|
|
||
|
// Create an instance representing this service. "my-service" is the
|
||
|
// name of _this_ service. The service should be cleaned up via Close.
|
||
|
svc, _ := connect.NewService("my-service", client)
|
||
|
defer svc.Close()
|
||
|
|
||
|
// Creating an HTTP server that serves via Connect
|
||
|
listener, _ := tls.Listen("tcp", ":8080", svc.ServeTLSConfig())
|
||
|
defer listener.Close()
|
||
|
|
||
|
// Accept
|
||
|
go acceptLoop(listener)
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## HTTP Clients
|
||
|
|
||
|
For Go applications that need to Connect to HTTP-based upstream dependencies,
|
||
|
the Go library can construct an `*http.Client` that automatically establishes
|
||
|
Connect-based connections as long as Consul-based service discovery is used.
|
||
|
|
||
|
Example, followed by more details:
|
||
|
|
||
|
```go
|
||
|
import(
|
||
|
"github.com/hashicorp/consul/api"
|
||
|
"github.com/hashicorp/consul/connect"
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
// Create a Consul API client
|
||
|
client, _ := api.NewClient(api.DefaultConfig())
|
||
|
|
||
|
// Create an instance representing this service. "my-service" is the
|
||
|
// name of _this_ service. The service should be cleaned up via Close.
|
||
|
svc, _ := connect.NewService("my-service", client)
|
||
|
defer svc.Close()
|
||
|
|
||
|
// Get an HTTP client
|
||
|
httpClient := svc.HTTPClient()
|
||
|
|
||
|
// Perform a request, then use the standard response
|
||
|
resp, _ := httpClient.Get("https://userinfo.service.consul/user/mitchellh")
|
||
|
}
|
||
|
```
|
||
|
|
||
|
The first step is to create a Consul API client and service. These are the
|
||
|
same steps as accepting connections and are explained in detail in the
|
||
|
section above. If your application is both a client and server, both the
|
||
|
API client and service structure can be shared and reused.
|
||
|
|
||
|
Next, we call `svc.HTTPClient()` to return a specially configured
|
||
|
`*http.Client`. This client will automatically established Connect-based
|
||
|
connections using Consul service discovery.
|
||
|
|
||
|
Finally, we perform an HTTP `GET` request to a hypothetical user service.
|
||
|
The HTTP client configuration automatically sends the correct client
|
||
|
certificate, verifies the server certificate, and manages background
|
||
|
goroutines for updating our certificates as necessary.
|
||
|
|
||
|
-> **Important:** The HTTP client _requires_ the hostname is a Consul
|
||
|
DNS name. Static IP addresses and external DNS cannot be used with the
|
||
|
HTTP client. For these values, please use `svc.Dial` directly.
|
||
|
|
||
|
If the application already uses a manually constructed `*http.Client`,
|
||
|
the `svc.HTTPDialTLS` function can be used to configure the
|
||
|
`http.Transport.DialTLS` field to achieve equivalent behavior.
|
||
|
|
||
|
## Raw TLS Connection
|
||
|
|
||
|
For a raw `net.Conn` TLS connection, the `svc.Dial` function can be used.
|
||
|
This will establish a connection to the desired service via Connect and
|
||
|
return the `net.Conn`. This connection can then be used as desired.
|
||
|
|
||
|
Example:
|
||
|
|
||
|
````go
|
||
|
import(
|
||
|
"context"
|
||
|
|
||
|
"github.com/hashicorp/consul/api"
|
||
|
"github.com/hashicorp/consul/connect"
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
// Create a Consul API client
|
||
|
client, _ := api.NewClient(api.DefaultConfig())
|
||
|
|
||
|
// Create an instance representing this service. "my-service" is the
|
||
|
// name of _this_ service. The service should be cleaned up via Close.
|
||
|
svc, _ := connect.NewService("my-service", client)
|
||
|
defer svc.Close()
|
||
|
|
||
|
// Connect to the "userinfo" Consul service.
|
||
|
conn, _ := svc.Dial(context.Background(), &connect.ConsulResolver{
|
||
|
Client: client,
|
||
|
Name: "userinfo",
|
||
|
})
|
||
|
}
|
||
|
```
|
||
|
|
||
|
This uses a familiar `Dial`-like function to establish raw `net.Conn` values.
|
||
|
The second parameter to dial is an implementation of the `connect.Resolver`
|
||
|
interface. The example above uses the `*connect.ConsulResolver` implementation
|
||
|
to perform Consul-based service discovery. This also automatically determines
|
||
|
the correct certificate metadata we expect the remote service to serve.
|
||
|
|
||
|
## Static Addresses, Custom Resolvers
|
||
|
|
||
|
In the raw TLS connection example, you see the use of a `connect.Resolver`
|
||
|
implementation. This interface can be implemented to perform address
|
||
|
resolution. This must return the address and also the URI SAN expected
|
||
|
in the TLS certificate served by the remote service.
|
||
|
|
||
|
The Go library provides two built-in resolvers:
|
||
|
|
||
|
* `*connect.StaticResolver` can be used for static addresses where no
|
||
|
service discovery is required. The expected cert URI SAN must be
|
||
|
manually specified.
|
||
|
|
||
|
* `*connect.ConsulResolver` which resolves services and prepared queries
|
||
|
via the Consul API. This also automatically determines the expected
|
||
|
cert URI SAN.
|