package autoconf import ( "fmt" "github.com/hashicorp/consul/agent/config" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/proto" "github.com/hashicorp/consul/proto/pbautoconf" "github.com/hashicorp/consul/proto/pbconfig" "github.com/hashicorp/consul/proto/pbconnect" "github.com/mitchellh/mapstructure" ) // translateAgentConfig is meant to take in a proto/pbconfig.Config type // and craft the corresponding agent/config.Config type. The need for this function // should eventually be removed with the protobuf and normal version converging. // In the meantime, its not desirable to have the flatter Config struct in protobufs // as in the long term we want a configuration with more nested groupings. // // Why is this function not in the proto/pbconfig package? The answer, that // package cannot import the agent/config package without running into import cycles. func translateConfig(c *pbconfig.Config) config.Config { result := config.Config{ Datacenter: stringPtrOrNil(c.Datacenter), PrimaryDatacenter: stringPtrOrNil(c.PrimaryDatacenter), NodeName: stringPtrOrNil(c.NodeName), // only output the SegmentName in the configuration if its non-empty // this will avoid a warning later when parsing the persisted configuration SegmentName: stringPtrOrNil(c.SegmentName), } if a := c.AutoEncrypt; a != nil { result.AutoEncrypt = config.AutoEncrypt{ TLS: &a.TLS, DNSSAN: a.DNSSAN, IPSAN: a.IPSAN, AllowTLS: &a.AllowTLS, } } if a := c.ACL; a != nil { result.ACL = config.ACL{ Enabled: &a.Enabled, PolicyTTL: stringPtrOrNil(a.PolicyTTL), RoleTTL: stringPtrOrNil(a.RoleTTL), TokenTTL: stringPtrOrNil(a.TokenTTL), DownPolicy: stringPtrOrNil(a.DownPolicy), DefaultPolicy: stringPtrOrNil(a.DefaultPolicy), EnableKeyListPolicy: &a.EnableKeyListPolicy, DisabledTTL: stringPtrOrNil(a.DisabledTTL), EnableTokenPersistence: &a.EnableTokenPersistence, } if t := c.ACL.Tokens; t != nil { tokens := make([]config.ServiceProviderToken, 0, len(t.ManagedServiceProvider)) for _, mspToken := range t.ManagedServiceProvider { tokens = append(tokens, config.ServiceProviderToken{ AccessorID: &mspToken.AccessorID, SecretID: &mspToken.SecretID, }) } result.ACL.Tokens = config.Tokens{ Master: stringPtrOrNil(t.Master), Replication: stringPtrOrNil(t.Replication), AgentMaster: stringPtrOrNil(t.AgentMaster), Default: stringPtrOrNil(t.Default), Agent: stringPtrOrNil(t.Agent), ManagedServiceProvider: tokens, } } } if g := c.Gossip; g != nil { result.RetryJoinLAN = g.RetryJoinLAN if e := c.Gossip.Encryption; e != nil { result.EncryptKey = stringPtrOrNil(e.Key) result.EncryptVerifyIncoming = &e.VerifyIncoming result.EncryptVerifyOutgoing = &e.VerifyOutgoing } } if t := c.TLS; t != nil { result.VerifyOutgoing = &t.VerifyOutgoing result.VerifyServerHostname = &t.VerifyServerHostname result.TLSMinVersion = stringPtrOrNil(t.MinVersion) result.TLSCipherSuites = stringPtrOrNil(t.CipherSuites) result.TLSPreferServerCipherSuites = &t.PreferServerCipherSuites } return result } func stringPtrOrNil(v string) *string { if v == "" { return nil } return &v } func extractSignedResponse(resp *pbautoconf.AutoConfigResponse) (*structs.SignedResponse, error) { roots, err := translateCARootsToStructs(resp.CARoots) if err != nil { return nil, err } cert, err := translateIssuedCertToStructs(resp.Certificate) if err != nil { return nil, err } out := &structs.SignedResponse{ IssuedCert: *cert, ConnectCARoots: *roots, ManualCARoots: resp.ExtraCACertificates, } if resp.Config != nil && resp.Config.TLS != nil { out.VerifyServerHostname = resp.Config.TLS.VerifyServerHostname } return out, err } // translateCARootsToStructs will create a structs.IndexedCARoots object from the corresponding // protobuf struct. Those structs are intended to be identical so the conversion just uses // mapstructure to go from one to the other. func translateCARootsToStructs(in *pbconnect.CARoots) (*structs.IndexedCARoots, error) { var out structs.IndexedCARoots if err := mapstructureTranslateToStructs(in, &out); err != nil { return nil, fmt.Errorf("Failed to re-encode CA Roots: %w", err) } return &out, nil } // translateIssuedCertToStructs will create a structs.IssuedCert object from the corresponding // protobuf struct. Those structs are intended to be identical so the conversion just uses // mapstructure to go from one to the other. func translateIssuedCertToStructs(in *pbconnect.IssuedCert) (*structs.IssuedCert, error) { var out structs.IssuedCert if err := mapstructureTranslateToStructs(in, &out); err != nil { return nil, fmt.Errorf("Failed to re-encode CA Roots: %w", err) } return &out, nil } func mapstructureTranslateToStructs(in interface{}, out interface{}) error { decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: proto.HookPBTimestampToTime, Result: out, }) if err != nil { return err } return decoder.Decode(in) } func translateCARootsToProtobuf(in *structs.IndexedCARoots) (*pbconnect.CARoots, error) { var out pbconnect.CARoots if err := mapstructureTranslateToProtobuf(in, &out); err != nil { return nil, fmt.Errorf("Failed to re-encode CA Roots: %w", err) } return &out, nil } func translateIssuedCertToProtobuf(in *structs.IssuedCert) (*pbconnect.IssuedCert, error) { var out pbconnect.IssuedCert if err := mapstructureTranslateToProtobuf(in, &out); err != nil { return nil, fmt.Errorf("Failed to re-encode CA Roots: %w", err) } return &out, nil } func mapstructureTranslateToProtobuf(in interface{}, out interface{}) error { decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: proto.HookTimeToPBTimestamp, Result: out, }) if err != nil { return err } return decoder.Decode(in) }