consul/agent/hcp/bootstrap/testing.go

182 lines
5.0 KiB
Go
Raw Normal View History

package bootstrap
import (
"crypto/rand"
"crypto/x509"
"encoding/base64"
"encoding/json"
"fmt"
"net"
"net/http"
"strings"
"github.com/hashicorp/consul/agent/hcp"
"github.com/hashicorp/consul/tlsutil"
"github.com/hashicorp/go-uuid"
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
gnmmod "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/models"
"github.com/hashicorp/hcp-sdk-go/resource"
)
// TestEndpoint returns an hcp.TestEndpoint to be used in an hcp.MockHCPServer.
func TestEndpoint() hcp.TestEndpoint {
// Memoize data so it's consistent for the life of the test server
data := make(map[string]gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse)
return hcp.TestEndpoint{
Methods: []string{"GET"},
PathSuffix: "agent/bootstrap_config",
Handler: func(r *http.Request, cluster resource.Resource) (interface{}, error) {
return handleBootstrap(data, cluster)
},
}
}
func handleBootstrap(data map[string]gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse, cluster resource.Resource) (interface{}, error) {
resp, ok := data[cluster.ID]
if !ok {
// Create new response
r, err := generateClusterData(cluster)
if err != nil {
return nil, err
}
data[cluster.ID] = r
resp = r
}
return resp, nil
}
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
const TestExistingClusterID = "133114e7-9745-41ce-b1c9-9644a20d2952"
func testLeaf(caCert, caKey string) (serverCert, serverKey string, err error) {
signer, err := tlsutil.ParseSigner(caKey)
if err != nil {
return "", "", err
}
serverCert, serverKey, err = tlsutil.GenerateCert(tlsutil.CertOpts{
Signer: signer,
CA: caCert,
Name: "server.dc1.consul",
Days: 30,
DNSNames: []string{"server.dc1.consul", "localhost"},
IPAddresses: append([]net.IP{}, net.ParseIP("127.0.0.1")),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
})
if err != nil {
return "", "", err
}
return serverCert, serverKey, nil
}
func generateClusterData(cluster resource.Resource) (gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse, error) {
resp := gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{
Cluster: &gnmmod.HashicorpCloudGlobalNetworkManager20220215Cluster{},
Bootstrap: &gnmmod.HashicorpCloudGlobalNetworkManager20220215ClusterBootstrap{
ServerTLS: &gnmmod.HashicorpCloudGlobalNetworkManager20220215ServerTLS{},
},
}
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
if cluster.ID == TestExistingClusterID {
token, err := uuid.GenerateUUID()
if err != nil {
return resp, err
}
resp.Bootstrap.ConsulConfig = "{}"
resp.Bootstrap.ManagementToken = token
return resp, nil
}
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
caCert, caKey, err := tlsutil.GenerateCA(tlsutil.CAOpts{})
if err != nil {
return resp, err
}
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
serverCert, serverKey, err := testLeaf(caCert, caKey)
if err != nil {
return resp, err
}
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
resp.Bootstrap.ServerTLS.CertificateAuthorities = append(resp.Bootstrap.ServerTLS.CertificateAuthorities, caCert)
resp.Bootstrap.ServerTLS.Cert = serverCert
resp.Bootstrap.ServerTLS.PrivateKey = serverKey
// Generate Config. We don't use the read config.Config struct because it
// doesn't have `omitempty` which makes the output gross. We only want a tiny
// subset, so we use a map that ends up with the same structure for now.
// Gossip key
gossipKeyBs := make([]byte, 32)
_, err = rand.Reader.Read(gossipKeyBs)
if err != nil {
return resp, err
}
retryJoinArgs := map[string]string{
"provider": "hcp",
"resource_id": cluster.String(),
"client_id": "test_id",
"client_secret": "test_secret",
}
cfg := map[string]interface{}{
"encrypt": base64.StdEncoding.EncodeToString(gossipKeyBs),
"encrypt_verify_incoming": true,
"encrypt_verify_outgoing": true,
// TLS settings (certs will be added by client since we can't put them inline)
"verify_incoming": true,
"verify_outgoing": true,
"verify_server_hostname": true,
"auto_encrypt": map[string]interface{}{
"allow_tls": true,
},
// Enable HTTPS port, disable HTTP
"ports": map[string]interface{}{
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
"https": 8501,
"http": -1,
"grpc_tls": 8503,
},
// RAFT Peers
"bootstrap_expect": 1,
"retry_join": []string{
mapArgsString(retryJoinArgs),
},
}
// ACLs
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
token, err := uuid.GenerateUUID()
if err != nil {
return resp, err
}
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
resp.Bootstrap.ManagementToken = token
cfg["acl"] = map[string]interface{}{
"tokens": map[string]interface{}{
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
// Also setup the server's own agent token to be the management token so it has
// permission to register itself.
Update HCP bootstrapping to support existing clusters 1.15.x version (#17305) * Persist HCP management token from server config We want to move away from injecting an initial management token into Consul clusters linked to HCP. The reasoning is that by using a separate class of token we can have more flexibility in terms of allowing HCP's token to co-exist with the user's management token. Down the line we can also more easily adjust the permissions attached to HCP's token to limit it's scope. With these changes, the cloud management token is like the initial management token in that iit has the same global management policy and if it is created it effectively bootstraps the ACL system. * Update SDK and mock HCP server The HCP management token will now be sent in a special field rather than as Consul's "initial management" token configuration. This commit also updates the mock HCP server to more accurately reflect the behavior of the CCM backend. * Refactor HCP bootstrapping logic and add tests We want to allow users to link Consul clusters that already exist to HCP. Existing clusters need care when bootstrapped by HCP, since we do not want to do things like change ACL/TLS settings for a running cluster. Additional changes: * Deconstruct MaybeBootstrap so that it can be tested. The HCP Go SDK requires HTTPS to fetch a token from the Auth URL, even if the backend server is mocked. By pulling the hcp.Client creation out we can modify its TLS configuration in tests while keeping the secure behavior in production code. * Add light validation for data received/loaded. * Sanitize initial_management token from received config, since HCP will only ever use the CloudConfig.MangementToken. * Add changelog entry --------- Co-authored-by: freddygv <freddy@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com>
2023-05-12 21:01:18 +00:00
"agent": token,
"initial_management": token,
},
"default_policy": "deny",
"enabled": true,
"enable_token_persistence": true,
}
// Encode and return a JSON string in the response
jsonBs, err := json.Marshal(cfg)
if err != nil {
return resp, err
}
resp.Bootstrap.ConsulConfig = string(jsonBs)
return resp, nil
}
func mapArgsString(m map[string]string) string {
args := make([]string, len(m))
for k, v := range m {
args = append(args, fmt.Sprintf("%s=%s", k, v))
}
return strings.Join(args, " ")
}