Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto encrypt k8s #6944

Merged
merged 6 commits into from
Jan 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,8 @@ func (a *Agent) setupClientAutoEncryptCache(reply *structs.SignedResponse) (*str
Datacenter: a.config.Datacenter,
Token: a.tokens.AgentToken(),
Agent: a.config.NodeName,
DNSSAN: a.config.AutoEncryptDNSSAN,
IPSAN: a.config.AutoEncryptIPSAN,
}

// prepolutate leaf cache
Expand Down
9 changes: 8 additions & 1 deletion agent/cache-types/connect_ca_leaf.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net"
"sync"
"sync/atomic"
"time"
Expand Down Expand Up @@ -508,6 +509,8 @@ func (c *ConnectCALeaf) generateNewLeaf(req *ConnectCALeafRequest,
// Build the cert uri
var id connect.CertURI
var commonName string
var dnsNames []string
var ipAddresses []net.IP
if req.Service != "" {
id = &connect.SpiffeIDService{
Host: roots.TrustDomain,
Expand All @@ -523,6 +526,8 @@ func (c *ConnectCALeaf) generateNewLeaf(req *ConnectCALeafRequest,
Agent: req.Agent,
}
commonName = connect.ServiceCN(req.Agent, roots.TrustDomain)
dnsNames = append([]string{"localhost"}, req.DNSSAN...)
ipAddresses = append([]net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::")}, req.IPSAN...)
} else {
return result, errors.New("URI must be either service or agent")
}
Expand All @@ -545,7 +550,7 @@ func (c *ConnectCALeaf) generateNewLeaf(req *ConnectCALeafRequest,
}

// Create a CSR.
csr, err := connect.CreateCSR(id, commonName, pk)
csr, err := connect.CreateCSR(id, commonName, pk, dnsNames, ipAddresses)
if err != nil {
return result, err
}
Expand Down Expand Up @@ -636,6 +641,8 @@ type ConnectCALeafRequest struct {
Datacenter string
Service string // Service name, not ID
Agent string // Agent name, not ID
DNSSAN []string
IPSAN []net.IP
MinQueryIndex uint64
MaxQueryTime time.Duration
}
Expand Down
16 changes: 16 additions & 0 deletions agent/config/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,20 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
}

autoEncryptTLS := b.boolVal(c.AutoEncrypt.TLS)
autoEncryptDNSSAN := []string{}
for _, d := range c.AutoEncrypt.DNSSAN {
autoEncryptDNSSAN = append(autoEncryptDNSSAN, d)
}
autoEncryptIPSAN := []net.IP{}
for _, i := range c.AutoEncrypt.IPSAN {
ip := net.ParseIP(i)
if ip == nil {
b.warn(fmt.Sprintf("Cannot parse ip %q from AutoEncrypt.IPSAN", i))
continue
}
autoEncryptIPSAN = append(autoEncryptIPSAN, ip)

}
autoEncryptAllowTLS := b.boolVal(c.AutoEncrypt.AllowTLS)

if autoEncryptAllowTLS {
Expand Down Expand Up @@ -809,6 +823,8 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
ClientAddrs: clientAddrs,
ConfigEntryBootstrap: configEntries,
AutoEncryptTLS: autoEncryptTLS,
AutoEncryptDNSSAN: autoEncryptDNSSAN,
AutoEncryptIPSAN: autoEncryptIPSAN,
AutoEncryptAllowTLS: autoEncryptAllowTLS,
ConnectEnabled: connectEnabled,
ConnectCAProvider: connectCAProvider,
Expand Down
6 changes: 6 additions & 0 deletions agent/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,12 @@ type AutoEncrypt struct {
// TLS enables receiving certificates for clients from servers
TLS *bool `json:"tls,omitempty" hcl:"tls" mapstructure:"tls"`

// Additional DNS SAN entries that clients request for their certificates.
DNSSAN []string `json:"dns_san,omitempty" hcl:"dns_san" mapstructure:"dns_san"`

// Additional IP SAN entries that clients request for their certificates.
IPSAN []string `json:"ip_san,omitempty" hcl:"ip_san" mapstructure:"ip_san"`

// AllowTLS enables the RPC endpoint on the server to answer
// AutoEncrypt.Sign requests.
AllowTLS *bool `json:"allow_tls,omitempty" hcl:"allow_tls" mapstructure:"allow_tls"`
Expand Down
8 changes: 8 additions & 0 deletions agent/config/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,14 @@ type RuntimeConfig struct {
// servers.
AutoEncryptTLS bool

// Additional DNS SAN entries that clients request during auto_encrypt
// flow for their certificates.
AutoEncryptDNSSAN []string

// Additional IP SAN entries that clients request during auto_encrypt
// flow for their certificates.
AutoEncryptIPSAN []net.IP

// AutoEncryptAllowTLS enables the server to respond to
// AutoEncrypt.Sign requests.
AutoEncryptAllowTLS bool
Expand Down
8 changes: 8 additions & 0 deletions agent/config/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3727,6 +3727,8 @@ func TestFullConfig(t *testing.T) {
},
"auto_encrypt": {
"tls": true,
"dns_san": ["a.com", "b.com"],
"ip_san": ["192.168.4.139", "192.168.4.140"],
"allow_tls": true
},
"connect": {
Expand Down Expand Up @@ -4324,6 +4326,8 @@ func TestFullConfig(t *testing.T) {
}
auto_encrypt = {
tls = true
dns_san = ["a.com", "b.com"]
ip_san = ["192.168.4.139", "192.168.4.140"]
allow_tls = true
}
connect {
Expand Down Expand Up @@ -5030,6 +5034,8 @@ func TestFullConfig(t *testing.T) {
},
},
AutoEncryptTLS: true,
AutoEncryptDNSSAN: []string{"a.com", "b.com"},
AutoEncryptIPSAN: []net.IP{net.ParseIP("192.168.4.139"), net.ParseIP("192.168.4.140")},
AutoEncryptAllowTLS: true,
ConnectEnabled: true,
ConnectSidecarMinPort: 8888,
Expand Down Expand Up @@ -5868,6 +5874,8 @@ func TestSanitize(t *testing.T) {
"ClientAddrs": [],
"ConfigEntryBootstrap": [],
"AutoEncryptTLS": false,
"AutoEncryptDNSSAN": [],
"AutoEncryptIPSAN": [],
"AutoEncryptAllowTLS": false,
"ConnectCAConfig": {},
"ConnectCAProvider": "",
Expand Down
2 changes: 2 additions & 0 deletions agent/connect/ca/provider_consul.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,8 @@ func (c *ConsulProvider) Sign(csr *x509.CertificateRequest) (string, error) {
NotBefore: effectiveNow,
AuthorityKeyId: keyId,
SubjectKeyId: keyId,
DNSNames: csr.DNSNames,
IPAddresses: csr.IPAddresses,
}

// Create the certificate, PEM encode it and return that value.
Expand Down
11 changes: 7 additions & 4 deletions agent/connect/csr.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"net"
"net/url"
)

Expand Down Expand Up @@ -41,15 +42,17 @@ func SigAlgoForKeyType(keyType string) x509.SignatureAlgorithm {
}
}

// CreateCSR returns a CSR to sign the given service along with the PEM-encoded
// private key for this certificate.
// CreateCSR returns a CSR to sign the given service with SAN entries
// along with the PEM-encoded private key for this certificate.
func CreateCSR(uri CertURI, commonName string, privateKey crypto.Signer,
extensions ...pkix.Extension) (string, error) {
dnsNames []string, ipAddresses []net.IP, extensions ...pkix.Extension) (string, error) {
template := &x509.CertificateRequest{
URIs: []*url.URL{uri.URI()},
SignatureAlgorithm: SigAlgoForKey(privateKey),
ExtraExtensions: extensions,
Subject: pkix.Name{CommonName: commonName},
DNSNames: dnsNames,
IPAddresses: ipAddresses,
}

// Create the CSR itself
Expand All @@ -75,7 +78,7 @@ func CreateCACSR(uri CertURI, commonName string, privateKey crypto.Signer) (stri
return "", err
}

return CreateCSR(uri, commonName, privateKey, ext)
return CreateCSR(uri, commonName, privateKey, nil, nil, ext)
}

// CreateCAExtension creates a pkix.Extension for the x509 Basic Constraints
Expand Down
5 changes: 4 additions & 1 deletion agent/consul/auto_encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,15 @@ func (c *Client) RequestAutoEncryptCerts(servers []string, port int, token strin
return errFn(err)
}

dnsNames := []string{"localhost"}
ipAddresses := []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::")}

// Create a CSR.
//
// The Common Name includes the dummy trust domain for now but Server will
// override this when it is signed anyway so it's OK.
cn := connect.AgentCN(string(c.config.NodeName), dummyTrustDomain)
csr, err := connect.CreateCSR(id, cn, pk)
csr, err := connect.CreateCSR(id, cn, pk, dnsNames, ipAddresses)
if err != nil {
return errFn(err)
}
Expand Down
10 changes: 9 additions & 1 deletion agent/consul/auto_encrypt_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package consul
import (
"crypto/x509"
"fmt"
"net"
"os"
"strings"
"testing"
Expand Down Expand Up @@ -77,7 +78,9 @@ func TestAutoEncryptSign(t *testing.T) {
// Create a CSR.
cn, err := connect.CNForCertURI(id)
require.NoError(t, err)
csr, err := connect.CreateCSR(id, cn, pk)
dnsNames := []string{"localhost"}
ipAddresses := []net.IP{net.ParseIP("127.0.0.1")}
csr, err := connect.CreateCSR(id, cn, pk, dnsNames, ipAddresses)
require.NoError(t, err, info)
require.NotEmpty(t, csr, info)
args := &structs.CASignRequest{
Expand Down Expand Up @@ -119,6 +122,11 @@ func TestAutoEncryptSign(t *testing.T) {
})
require.NoError(t, err, info)

// Verify SANs
require.Equal(t, dnsNames, leaf.DNSNames)
require.Len(t, leaf.IPAddresses, 1)
require.True(t, ipAddresses[0].Equal(leaf.IPAddresses[0]))

// Verify other fields
require.Equal(t, "uuid", reply.IssuedCert.Agent, info)
require.Len(t, reply.ManualCARoots, 1, info)
Expand Down
4 changes: 4 additions & 0 deletions website/source/docs/agent/options.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,10 @@ default will automatically work with some tooling.

* <a name="tls"></a><a href="#tls">`tls`</a> (Defaults to `false`) Allows the client to request the Connect CA and certificates from the servers, for encrypting RPC communication. The client will make the request to any servers listed in the `-join` or `-retry-join` option. This requires that every server to have `auto_encrypt.allow_tls` enabled. When both `auto_encrypt` options are used, it allows clients to receive certificates that are generated on the servers. If the `-server-port` is not the default one, it has to be provided to the client as well. Usually this is discovered through LAN gossip, but `auto_encrypt` provision happens before the information can be distributed through gossip. The most secure `auto_encrypt` setup is when the client is provided with the built-in CA, `verify_server_hostname` is turned on, and when an ACL token with `node.write` permissions is setup. It is also possible to use `auto_encrypt` with a CA and ACL, but without `verify_server_hostname`, or only with a ACL enabled, or only with CA and `verify_server_hostname`, or only with a CA, or finally without a CA and without ACL enabled. In any case, the communication to the `auto_encrypt` endpoint is always TLS encrypted.

* <a name="dns_san"></a><a href="#dns_san">`dns_san`</a> (Defaults to `[]`) When this option is being used, the certificates requested by `auto_encrypt` from the server have these `dns_san` set as DNS SAN.

* <a name="ip_san"></a><a href="#ip_san">`ip_san`</a> (Defaults to `[]`) When this option is being used, the certificates requested by `auto_encrypt` from the server have these `ip_san` set as IP SAN.

* <a name="bootstrap"></a><a href="#bootstrap">`bootstrap`</a> Equivalent to the
[`-bootstrap` command-line flag](#_bootstrap).

Expand Down