Merge pull request #8035 from hashicorp/feature/auto-config/server-rpc
This commit is contained in:
commit
8c601ad8db
|
@ -2,6 +2,7 @@ package acl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,3 +71,8 @@ func (e PermissionDeniedError) Error() string {
|
||||||
}
|
}
|
||||||
return errPermissionDenied
|
return errPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PermissionDenied(msg string, args ...interface{}) PermissionDeniedError {
|
||||||
|
cause := fmt.Sprintf(msg, args...)
|
||||||
|
return PermissionDeniedError{Cause: cause}
|
||||||
|
}
|
||||||
|
|
|
@ -1402,6 +1402,18 @@ func (a *Agent) consulConfig() (*consul.Config, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy over auto config settings
|
||||||
|
base.AutoConfigEnabled = a.config.AutoConfig.Enabled
|
||||||
|
base.AutoConfigIntroToken = a.config.AutoConfig.IntroToken
|
||||||
|
base.AutoConfigIntroTokenFile = a.config.AutoConfig.IntroTokenFile
|
||||||
|
base.AutoConfigServerAddresses = a.config.AutoConfig.ServerAddresses
|
||||||
|
base.AutoConfigDNSSANs = a.config.AutoConfig.DNSSANs
|
||||||
|
base.AutoConfigIPSANs = a.config.AutoConfig.IPSANs
|
||||||
|
base.AutoConfigAuthzEnabled = a.config.AutoConfig.Authorizer.Enabled
|
||||||
|
base.AutoConfigAuthzAuthMethod = a.config.AutoConfig.Authorizer.AuthMethod
|
||||||
|
base.AutoConfigAuthzClaimAssertions = a.config.AutoConfig.Authorizer.ClaimAssertions
|
||||||
|
base.AutoConfigAuthzAllowReuse = a.config.AutoConfig.Authorizer.AllowReuse
|
||||||
|
|
||||||
// Setup the user event callback
|
// Setup the user event callback
|
||||||
base.UserEventHandler = func(e serf.UserEvent) {
|
base.UserEventHandler = func(e serf.UserEvent) {
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package agentpb
|
||||||
|
|
||||||
|
func (req *AutoConfigRequest) RequestDatacenter() string {
|
||||||
|
return req.Datacenter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *AutoConfigRequest) IsRead() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *AutoConfigRequest) AllowStaleRead() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *AutoConfigRequest) TokenSecret() string {
|
||||||
|
return req.ConsulToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *AutoConfigRequest) SetTokenSecret(token string) {
|
||||||
|
req.ConsulToken = token
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by protoc-gen-go-binary. DO NOT EDIT.
|
||||||
|
// source: agent/agentpb/auto_config.proto
|
||||||
|
|
||||||
|
package agentpb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *AutoConfigRequest) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *AutoConfigRequest) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *AutoConfigResponse) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *AutoConfigResponse) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
|
@ -0,0 +1,757 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: agent/agentpb/auto_config.proto
|
||||||
|
|
||||||
|
package agentpb
|
||||||
|
|
||||||
|
import (
|
||||||
|
fmt "fmt"
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
config "github.com/hashicorp/consul/agent/agentpb/config"
|
||||||
|
io "io"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
// AutoConfigRequest is the data structure to be sent along with the
|
||||||
|
// Cluster.AutoConfig RPC
|
||||||
|
type AutoConfigRequest struct {
|
||||||
|
// Datacenter is the local datacenter name. This wont actually be set by clients
|
||||||
|
// but rather will be set by the servers to allow for forwarding to
|
||||||
|
// the leader. If it ever happens to be set and differs from the local datacenters
|
||||||
|
// name then an error should be returned.
|
||||||
|
Datacenter string `protobuf:"bytes,1,opt,name=Datacenter,proto3" json:"Datacenter,omitempty"`
|
||||||
|
// Node is the node name that the requester would like to assume
|
||||||
|
// the identity of.
|
||||||
|
Node string `protobuf:"bytes,2,opt,name=Node,proto3" json:"Node,omitempty"`
|
||||||
|
// Segment is the network segment that the requester would like to join
|
||||||
|
Segment string `protobuf:"bytes,4,opt,name=Segment,proto3" json:"Segment,omitempty"`
|
||||||
|
// JWT is a signed JSON Web Token used to authorize the request
|
||||||
|
JWT string `protobuf:"bytes,5,opt,name=JWT,proto3" json:"JWT,omitempty"`
|
||||||
|
// ConsulToken is a Consul ACL token that the agent requesting the
|
||||||
|
// configuration already has.
|
||||||
|
ConsulToken string `protobuf:"bytes,6,opt,name=ConsulToken,proto3" json:"ConsulToken,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigRequest) Reset() { *m = AutoConfigRequest{} }
|
||||||
|
func (m *AutoConfigRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*AutoConfigRequest) ProtoMessage() {}
|
||||||
|
func (*AutoConfigRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_c842365210d144b0, []int{0}
|
||||||
|
}
|
||||||
|
func (m *AutoConfigRequest) XXX_Unmarshal(b []byte) error {
|
||||||
|
return m.Unmarshal(b)
|
||||||
|
}
|
||||||
|
func (m *AutoConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
if deterministic {
|
||||||
|
return xxx_messageInfo_AutoConfigRequest.Marshal(b, m, deterministic)
|
||||||
|
} else {
|
||||||
|
b = b[:cap(b)]
|
||||||
|
n, err := m.MarshalTo(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b[:n], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (m *AutoConfigRequest) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_AutoConfigRequest.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *AutoConfigRequest) XXX_Size() int {
|
||||||
|
return m.Size()
|
||||||
|
}
|
||||||
|
func (m *AutoConfigRequest) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_AutoConfigRequest.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_AutoConfigRequest proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *AutoConfigRequest) GetDatacenter() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Datacenter
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigRequest) GetNode() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Node
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigRequest) GetSegment() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Segment
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigRequest) GetJWT() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.JWT
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigRequest) GetConsulToken() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ConsulToken
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoConfigResponse is the data structure sent in response to a Cluster.AutoConfig request
|
||||||
|
type AutoConfigResponse struct {
|
||||||
|
Config *config.Config `protobuf:"bytes,1,opt,name=Config,proto3" json:"Config,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigResponse) Reset() { *m = AutoConfigResponse{} }
|
||||||
|
func (m *AutoConfigResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*AutoConfigResponse) ProtoMessage() {}
|
||||||
|
func (*AutoConfigResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_c842365210d144b0, []int{1}
|
||||||
|
}
|
||||||
|
func (m *AutoConfigResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return m.Unmarshal(b)
|
||||||
|
}
|
||||||
|
func (m *AutoConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
if deterministic {
|
||||||
|
return xxx_messageInfo_AutoConfigResponse.Marshal(b, m, deterministic)
|
||||||
|
} else {
|
||||||
|
b = b[:cap(b)]
|
||||||
|
n, err := m.MarshalTo(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b[:n], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (m *AutoConfigResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_AutoConfigResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *AutoConfigResponse) XXX_Size() int {
|
||||||
|
return m.Size()
|
||||||
|
}
|
||||||
|
func (m *AutoConfigResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_AutoConfigResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_AutoConfigResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *AutoConfigResponse) GetConfig() *config.Config {
|
||||||
|
if m != nil {
|
||||||
|
return m.Config
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*AutoConfigRequest)(nil), "agentpb.AutoConfigRequest")
|
||||||
|
proto.RegisterType((*AutoConfigResponse)(nil), "agentpb.AutoConfigResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("agent/agentpb/auto_config.proto", fileDescriptor_c842365210d144b0) }
|
||||||
|
|
||||||
|
var fileDescriptor_c842365210d144b0 = []byte{
|
||||||
|
// 258 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0x4c, 0x4f, 0xcd,
|
||||||
|
0x2b, 0xd1, 0x07, 0x93, 0x05, 0x49, 0xfa, 0x89, 0xa5, 0x25, 0xf9, 0xf1, 0xc9, 0xf9, 0x79, 0x69,
|
||||||
|
0x99, 0xe9, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xec, 0x50, 0x29, 0x29, 0x45, 0x54, 0x95,
|
||||||
|
0x10, 0x45, 0xfa, 0xc8, 0x6a, 0x95, 0xa6, 0x32, 0x72, 0x09, 0x3a, 0x96, 0x96, 0xe4, 0x3b, 0x83,
|
||||||
|
0x05, 0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0xe4, 0xb8, 0xb8, 0x5c, 0x12, 0x4b, 0x12,
|
||||||
|
0x93, 0x53, 0xf3, 0x4a, 0x52, 0x8b, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0x90, 0x44, 0x84,
|
||||||
|
0x84, 0xb8, 0x58, 0xfc, 0xf2, 0x53, 0x52, 0x25, 0x98, 0xc0, 0x32, 0x60, 0xb6, 0x90, 0x04, 0x17,
|
||||||
|
0x7b, 0x70, 0x6a, 0x7a, 0x6e, 0x6a, 0x5e, 0x89, 0x04, 0x0b, 0x58, 0x18, 0xc6, 0x15, 0x12, 0xe0,
|
||||||
|
0x62, 0xf6, 0x0a, 0x0f, 0x91, 0x60, 0x05, 0x8b, 0x82, 0x98, 0x42, 0x0a, 0x5c, 0xdc, 0xce, 0xf9,
|
||||||
|
0x79, 0xc5, 0xa5, 0x39, 0x21, 0xf9, 0xd9, 0xa9, 0x79, 0x12, 0x6c, 0x60, 0x19, 0x64, 0x21, 0x25,
|
||||||
|
0x1b, 0x2e, 0x21, 0x64, 0x67, 0x15, 0x17, 0xe4, 0xe7, 0x15, 0xa7, 0x0a, 0xa9, 0x71, 0xb1, 0x41,
|
||||||
|
0x44, 0xc0, 0x6e, 0xe2, 0x36, 0xe2, 0xd3, 0x83, 0x7a, 0x06, 0xaa, 0x0e, 0x2a, 0xeb, 0x64, 0x7d,
|
||||||
|
0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0xce, 0x78, 0x2c, 0xc7,
|
||||||
|
0x10, 0xa5, 0x99, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x9f, 0x91, 0x58,
|
||||||
|
0x9c, 0x91, 0x99, 0x9c, 0x5f, 0x54, 0x00, 0x0a, 0x8a, 0xe2, 0xd2, 0x1c, 0x7d, 0x94, 0x60, 0x4a,
|
||||||
|
0x62, 0x03, 0x87, 0x8c, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x3b, 0x8c, 0xf1, 0x75, 0x68, 0x01,
|
||||||
|
0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigRequest) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.Datacenter) > 0 {
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintAutoConfig(dAtA, i, uint64(len(m.Datacenter)))
|
||||||
|
i += copy(dAtA[i:], m.Datacenter)
|
||||||
|
}
|
||||||
|
if len(m.Node) > 0 {
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintAutoConfig(dAtA, i, uint64(len(m.Node)))
|
||||||
|
i += copy(dAtA[i:], m.Node)
|
||||||
|
}
|
||||||
|
if len(m.Segment) > 0 {
|
||||||
|
dAtA[i] = 0x22
|
||||||
|
i++
|
||||||
|
i = encodeVarintAutoConfig(dAtA, i, uint64(len(m.Segment)))
|
||||||
|
i += copy(dAtA[i:], m.Segment)
|
||||||
|
}
|
||||||
|
if len(m.JWT) > 0 {
|
||||||
|
dAtA[i] = 0x2a
|
||||||
|
i++
|
||||||
|
i = encodeVarintAutoConfig(dAtA, i, uint64(len(m.JWT)))
|
||||||
|
i += copy(dAtA[i:], m.JWT)
|
||||||
|
}
|
||||||
|
if len(m.ConsulToken) > 0 {
|
||||||
|
dAtA[i] = 0x32
|
||||||
|
i++
|
||||||
|
i = encodeVarintAutoConfig(dAtA, i, uint64(len(m.ConsulToken)))
|
||||||
|
i += copy(dAtA[i:], m.ConsulToken)
|
||||||
|
}
|
||||||
|
if m.XXX_unrecognized != nil {
|
||||||
|
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigResponse) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigResponse) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.Config != nil {
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintAutoConfig(dAtA, i, uint64(m.Config.Size()))
|
||||||
|
n1, err := m.Config.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n1
|
||||||
|
}
|
||||||
|
if m.XXX_unrecognized != nil {
|
||||||
|
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeVarintAutoConfig(dAtA []byte, offset int, v uint64) int {
|
||||||
|
for v >= 1<<7 {
|
||||||
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
dAtA[offset] = uint8(v)
|
||||||
|
return offset + 1
|
||||||
|
}
|
||||||
|
func (m *AutoConfigRequest) Size() (n int) {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.Datacenter)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAutoConfig(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Node)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAutoConfig(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Segment)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAutoConfig(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.JWT)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAutoConfig(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.ConsulToken)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAutoConfig(uint64(l))
|
||||||
|
}
|
||||||
|
if m.XXX_unrecognized != nil {
|
||||||
|
n += len(m.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AutoConfigResponse) Size() (n int) {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.Config != nil {
|
||||||
|
l = m.Config.Size()
|
||||||
|
n += 1 + l + sovAutoConfig(uint64(l))
|
||||||
|
}
|
||||||
|
if m.XXX_unrecognized != nil {
|
||||||
|
n += len(m.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovAutoConfig(x uint64) (n int) {
|
||||||
|
for {
|
||||||
|
n++
|
||||||
|
x >>= 7
|
||||||
|
if x == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func sozAutoConfig(x uint64) (n int) {
|
||||||
|
return sovAutoConfig(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (m *AutoConfigRequest) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: AutoConfigRequest: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: AutoConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Datacenter", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Datacenter = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Node", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Node = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 4:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Segment", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Segment = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 5:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field JWT", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.JWT = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 6:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field ConsulToken", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.ConsulToken = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipAutoConfig(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *AutoConfigResponse) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: AutoConfigResponse: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: AutoConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= int(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if m.Config == nil {
|
||||||
|
m.Config = &config.Config{}
|
||||||
|
}
|
||||||
|
if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipAutoConfig(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) < 0 {
|
||||||
|
return ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipAutoConfig(dAtA []byte) (n int, err error) {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if dAtA[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
return iNdEx, nil
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
if iNdEx < 0 {
|
||||||
|
return 0, ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 3:
|
||||||
|
for {
|
||||||
|
var innerWire uint64
|
||||||
|
var start int = iNdEx
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowAutoConfig
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
innerWire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerWireType := int(innerWire & 0x7)
|
||||||
|
if innerWireType == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, err := skipAutoConfig(dAtA[start:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iNdEx = start + next
|
||||||
|
if iNdEx < 0 {
|
||||||
|
return 0, ErrInvalidLengthAutoConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 4:
|
||||||
|
return iNdEx, nil
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
return iNdEx, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthAutoConfig = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowAutoConfig = fmt.Errorf("proto: integer overflow")
|
||||||
|
)
|
|
@ -0,0 +1,36 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package agentpb;
|
||||||
|
|
||||||
|
option go_package = "github.com/hashicorp/consul/agent/agentpb";
|
||||||
|
|
||||||
|
import "agent/agentpb/config/config.proto";
|
||||||
|
|
||||||
|
// AutoConfigRequest is the data structure to be sent along with the
|
||||||
|
// Cluster.AutoConfig RPC
|
||||||
|
message AutoConfigRequest {
|
||||||
|
// Datacenter is the local datacenter name. This wont actually be set by clients
|
||||||
|
// but rather will be set by the servers to allow for forwarding to
|
||||||
|
// the leader. If it ever happens to be set and differs from the local datacenters
|
||||||
|
// name then an error should be returned.
|
||||||
|
string Datacenter = 1;
|
||||||
|
|
||||||
|
// Node is the node name that the requester would like to assume
|
||||||
|
// the identity of.
|
||||||
|
string Node = 2;
|
||||||
|
|
||||||
|
// Segment is the network segment that the requester would like to join
|
||||||
|
string Segment = 4;
|
||||||
|
|
||||||
|
// JWT is a signed JSON Web Token used to authorize the request
|
||||||
|
string JWT = 5;
|
||||||
|
|
||||||
|
// ConsulToken is a Consul ACL token that the agent requesting the
|
||||||
|
// configuration already has.
|
||||||
|
string ConsulToken = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoConfigResponse is the data structure sent in response to a Cluster.AutoConfig request
|
||||||
|
message AutoConfigResponse {
|
||||||
|
config.Config Config = 1;
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Code generated by protoc-gen-go-binary. DO NOT EDIT.
|
||||||
|
// source: agent/agentpb/config/config.proto
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *Config) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *Config) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *Gossip) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *Gossip) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *GossipEncryption) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *GossipEncryption) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *TLS) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *TLS) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *ACL) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *ACL) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *ACLTokens) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *ACLTokens) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *ACLServiceProviderToken) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *ACLServiceProviderToken) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *AutoEncrypt) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *AutoEncrypt) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,70 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package config;
|
||||||
|
|
||||||
|
option go_package = "github.com/hashicorp/consul/agent/agentpb/config";
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
string Datacenter = 1;
|
||||||
|
string PrimaryDatacenter = 2;
|
||||||
|
string NodeName = 3;
|
||||||
|
string SegmentName = 4;
|
||||||
|
ACL ACL = 5;
|
||||||
|
AutoEncrypt AutoEncrypt = 6;
|
||||||
|
Gossip Gossip = 7;
|
||||||
|
TLS TLS = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Gossip {
|
||||||
|
GossipEncryption Encryption = 1;
|
||||||
|
repeated string RetryJoinLAN = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GossipEncryption {
|
||||||
|
string Key = 1;
|
||||||
|
bool VerifyIncoming = 2;
|
||||||
|
bool VerifyOutgoing = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TLS {
|
||||||
|
bool VerifyOutgoing = 1;
|
||||||
|
bool VerifyServerHostname = 2;
|
||||||
|
string CipherSuites = 3;
|
||||||
|
string MinVersion = 4;
|
||||||
|
bool PreferServerCipherSuites = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ACL {
|
||||||
|
bool Enabled = 1;
|
||||||
|
string PolicyTTL = 2;
|
||||||
|
string RoleTTL = 3;
|
||||||
|
string TokenTTL = 4;
|
||||||
|
string DownPolicy = 5;
|
||||||
|
string DefaultPolicy = 6;
|
||||||
|
bool EnableKeyListPolicy = 7;
|
||||||
|
ACLTokens Tokens = 8;
|
||||||
|
string DisabledTTL = 9;
|
||||||
|
bool EnableTokenPersistence = 10;
|
||||||
|
bool MSPDisableBootstrap = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ACLTokens {
|
||||||
|
string Master = 1;
|
||||||
|
string Replication = 2;
|
||||||
|
string AgentMaster = 3;
|
||||||
|
string Default = 4;
|
||||||
|
string Agent = 5;
|
||||||
|
repeated ACLServiceProviderToken ManagedServiceProvider = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ACLServiceProviderToken {
|
||||||
|
string AccessorID = 1;
|
||||||
|
string SecretID = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AutoEncrypt {
|
||||||
|
bool TLS = 1;
|
||||||
|
repeated string DNSSAN = 2;
|
||||||
|
repeated string IPSAN = 3;
|
||||||
|
bool AllowTLS = 4;
|
||||||
|
}
|
|
@ -0,0 +1,312 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
|
"github.com/hashicorp/consul/agent/agentpb"
|
||||||
|
"github.com/hashicorp/consul/agent/agentpb/config"
|
||||||
|
"github.com/hashicorp/consul/agent/consul/authmethod/ssoauth"
|
||||||
|
"github.com/hashicorp/consul/agent/metadata"
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/lib"
|
||||||
|
"github.com/hashicorp/consul/lib/template"
|
||||||
|
"github.com/hashicorp/consul/tlsutil"
|
||||||
|
bexpr "github.com/hashicorp/go-bexpr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AutoConfigOptions struct {
|
||||||
|
NodeName string
|
||||||
|
SegmentName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AutoConfigAuthorizer interface {
|
||||||
|
// Authorizes the request and returns a struct containing the various
|
||||||
|
// options for how to generate the configuration.
|
||||||
|
Authorize(*agentpb.AutoConfigRequest) (AutoConfigOptions, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type disabledAuthorizer struct{}
|
||||||
|
|
||||||
|
func (_ *disabledAuthorizer) Authorize(_ *agentpb.AutoConfigRequest) (AutoConfigOptions, error) {
|
||||||
|
return AutoConfigOptions{}, fmt.Errorf("Auto Config is disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
type jwtAuthorizer struct {
|
||||||
|
validator *ssoauth.Validator
|
||||||
|
allowReuse bool
|
||||||
|
claimAssertions []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *jwtAuthorizer) Authorize(req *agentpb.AutoConfigRequest) (AutoConfigOptions, error) {
|
||||||
|
// perform basic JWT Authorization
|
||||||
|
identity, err := a.validator.ValidateLogin(context.Background(), req.JWT)
|
||||||
|
if err != nil {
|
||||||
|
// TODO (autoconf) maybe we should add a more generic permission denied error not tied to the ACL package?
|
||||||
|
return AutoConfigOptions{}, acl.PermissionDenied("Failed JWT authorization: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
varMap := map[string]string{
|
||||||
|
"node": req.Node,
|
||||||
|
"segment": req.Segment,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO (autoconf) check for JWT reuse if configured to do so.
|
||||||
|
for _, raw := range a.claimAssertions {
|
||||||
|
// validate and fill any HIL
|
||||||
|
filled, err := template.InterpolateHIL(raw, varMap, true)
|
||||||
|
if err != nil {
|
||||||
|
return AutoConfigOptions{}, fmt.Errorf("Failed to render claim assertion template %q: %w", raw, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluator, err := bexpr.CreateEvaluatorForType(filled, nil, identity.SelectableFields)
|
||||||
|
if err != nil {
|
||||||
|
return AutoConfigOptions{}, fmt.Errorf("Failed to create evaluator for claim assertion %q: %w", filled, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := evaluator.Evaluate(identity.SelectableFields)
|
||||||
|
if err != nil {
|
||||||
|
return AutoConfigOptions{}, fmt.Errorf("Failed to execute claim assertion %q: %w", filled, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return AutoConfigOptions{}, acl.PermissionDenied("Failed JWT claim assertion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AutoConfigOptions{
|
||||||
|
NodeName: req.Node,
|
||||||
|
SegmentName: req.Segment,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cluster endpoint is used for cluster configuration operations
|
||||||
|
type Cluster struct {
|
||||||
|
srv *Server
|
||||||
|
|
||||||
|
authorizer AutoConfigAuthorizer
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateTLSCertificatesInConfig will ensure that the TLS settings regarding how an agent is
|
||||||
|
// made aware of its certificates are populated. This will only work if connect is enabled and
|
||||||
|
// in some cases only if auto_encrypt is enabled on the servers. This endpoint has the option
|
||||||
|
// to configure auto_encrypt or potentially in the future to generate the certificates inline.
|
||||||
|
func (c *Cluster) updateTLSCertificatesInConfig(opts AutoConfigOptions, conf *config.Config) error {
|
||||||
|
if c.srv.config.AutoEncryptAllowTLS {
|
||||||
|
conf.AutoEncrypt = &config.AutoEncrypt{TLS: true}
|
||||||
|
} else {
|
||||||
|
conf.AutoEncrypt = &config.AutoEncrypt{TLS: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateACLtokensInConfig will configure all of the agents ACL settings and will populate
|
||||||
|
// the configuration with an agent token usable for all default agent operations.
|
||||||
|
func (c *Cluster) updateACLsInConfig(opts AutoConfigOptions, conf *config.Config) error {
|
||||||
|
acl := &config.ACL{
|
||||||
|
Enabled: c.srv.config.ACLsEnabled,
|
||||||
|
PolicyTTL: c.srv.config.ACLPolicyTTL.String(),
|
||||||
|
RoleTTL: c.srv.config.ACLRoleTTL.String(),
|
||||||
|
TokenTTL: c.srv.config.ACLTokenTTL.String(),
|
||||||
|
DisabledTTL: c.srv.config.ACLDisabledTTL.String(),
|
||||||
|
DownPolicy: c.srv.config.ACLDownPolicy,
|
||||||
|
DefaultPolicy: c.srv.config.ACLDefaultPolicy,
|
||||||
|
EnableKeyListPolicy: c.srv.config.ACLEnableKeyListPolicy,
|
||||||
|
}
|
||||||
|
|
||||||
|
// when ACLs are enabled we want to create a local token with a node identity
|
||||||
|
if c.srv.config.ACLsEnabled {
|
||||||
|
// we have to require local tokens or else it would require having these servers use a token with acl:write to make a
|
||||||
|
// token create RPC to the servers in the primary DC.
|
||||||
|
if !c.srv.LocalTokensEnabled() {
|
||||||
|
return fmt.Errorf("Agent Auto Configuration requires local token usage to be enabled in this datacenter: %s", c.srv.config.Datacenter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate the accessor id
|
||||||
|
accessor, err := lib.GenerateUUID(c.srv.checkTokenUUID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// generate the secret id
|
||||||
|
secret, err := lib.GenerateUUID(c.srv.checkTokenUUID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up the token
|
||||||
|
token := structs.ACLToken{
|
||||||
|
AccessorID: accessor,
|
||||||
|
SecretID: secret,
|
||||||
|
Description: fmt.Sprintf("Auto Config Token for Node %q", opts.NodeName),
|
||||||
|
CreateTime: time.Now(),
|
||||||
|
Local: true,
|
||||||
|
NodeIdentities: []*structs.ACLNodeIdentity{
|
||||||
|
{
|
||||||
|
NodeName: opts.NodeName,
|
||||||
|
Datacenter: c.srv.config.Datacenter,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
|
}
|
||||||
|
|
||||||
|
req := structs.ACLTokenBatchSetRequest{
|
||||||
|
Tokens: structs.ACLTokens{&token},
|
||||||
|
CAS: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform the request to mint the new token
|
||||||
|
if _, err := c.srv.raftApplyMsgpack(structs.ACLTokenSetRequestType, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
acl.Tokens = &config.ACLTokens{Agent: secret}
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.ACL = acl
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateJoinAddressesInConfig determines the correct gossip endpoints that clients should
|
||||||
|
// be connecting to for joining the cluster based on the segment given in the opts parameter.
|
||||||
|
func (c *Cluster) updateJoinAddressesInConfig(opts AutoConfigOptions, conf *config.Config) error {
|
||||||
|
members, err := c.srv.LANSegmentMembers(opts.SegmentName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var joinAddrs []string
|
||||||
|
for _, m := range members {
|
||||||
|
if ok, _ := metadata.IsConsulServer(m); ok {
|
||||||
|
serfAddr := net.TCPAddr{IP: m.Addr, Port: int(m.Port)}
|
||||||
|
joinAddrs = append(joinAddrs, serfAddr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Gossip == nil {
|
||||||
|
conf.Gossip = &config.Gossip{}
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.Gossip.RetryJoinLAN = joinAddrs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateGossipEncryptionInConfig will populate the gossip encryption configuration settings
|
||||||
|
func (c *Cluster) updateGossipEncryptionInConfig(_ AutoConfigOptions, conf *config.Config) error {
|
||||||
|
// Add gossip encryption settings if there is any key loaded
|
||||||
|
memberlistConfig := c.srv.config.SerfLANConfig.MemberlistConfig
|
||||||
|
if lanKeyring := memberlistConfig.Keyring; lanKeyring != nil {
|
||||||
|
if conf.Gossip == nil {
|
||||||
|
conf.Gossip = &config.Gossip{}
|
||||||
|
}
|
||||||
|
if conf.Gossip.Encryption == nil {
|
||||||
|
conf.Gossip.Encryption = &config.GossipEncryption{}
|
||||||
|
}
|
||||||
|
|
||||||
|
pk := lanKeyring.GetPrimaryKey()
|
||||||
|
if len(pk) > 0 {
|
||||||
|
conf.Gossip.Encryption.Key = base64.StdEncoding.EncodeToString(pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.Gossip.Encryption.VerifyIncoming = memberlistConfig.GossipVerifyIncoming
|
||||||
|
conf.Gossip.Encryption.VerifyOutgoing = memberlistConfig.GossipVerifyOutgoing
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateTLSSettingsInConfig will populate the TLS configuration settings but will not
|
||||||
|
// populate leaf or ca certficiates.
|
||||||
|
func (c *Cluster) updateTLSSettingsInConfig(_ AutoConfigOptions, conf *config.Config) error {
|
||||||
|
// add in TLS configuration
|
||||||
|
if conf.TLS == nil {
|
||||||
|
conf.TLS = &config.TLS{}
|
||||||
|
}
|
||||||
|
conf.TLS.VerifyServerHostname = c.srv.tlsConfigurator.VerifyServerHostname()
|
||||||
|
base := c.srv.tlsConfigurator.Base()
|
||||||
|
conf.TLS.VerifyOutgoing = base.VerifyOutgoing
|
||||||
|
conf.TLS.MinVersion = base.TLSMinVersion
|
||||||
|
conf.TLS.PreferServerCipherSuites = base.PreferServerCipherSuites
|
||||||
|
|
||||||
|
var err error
|
||||||
|
conf.TLS.CipherSuites, err = tlsutil.CipherString(base.CipherSuites)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// baseConfig will populate the configuration with some base settings such as the
|
||||||
|
// datacenter names, node name etc.
|
||||||
|
func (c *Cluster) baseConfig(opts AutoConfigOptions, conf *config.Config) error {
|
||||||
|
if opts.NodeName == "" {
|
||||||
|
return fmt.Errorf("Cannot generate auto config response without a node name")
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.Datacenter = c.srv.config.Datacenter
|
||||||
|
conf.PrimaryDatacenter = c.srv.config.PrimaryDatacenter
|
||||||
|
conf.NodeName = opts.NodeName
|
||||||
|
conf.SegmentName = opts.SegmentName
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type autoConfigUpdater func(c *Cluster, opts AutoConfigOptions, conf *config.Config) error
|
||||||
|
|
||||||
|
var (
|
||||||
|
// variable holding the list of config updating functions to execute when generating
|
||||||
|
// the auto config response. This will allow for more easily adding extra self-contained
|
||||||
|
// configurators here in the future.
|
||||||
|
autoConfigUpdaters []autoConfigUpdater = []autoConfigUpdater{
|
||||||
|
(*Cluster).baseConfig,
|
||||||
|
(*Cluster).updateJoinAddressesInConfig,
|
||||||
|
(*Cluster).updateGossipEncryptionInConfig,
|
||||||
|
(*Cluster).updateTLSSettingsInConfig,
|
||||||
|
(*Cluster).updateACLsInConfig,
|
||||||
|
(*Cluster).updateTLSCertificatesInConfig,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// AgentAutoConfig will authorize the incoming request and then generate the configuration
|
||||||
|
// to push down to the client
|
||||||
|
func (c *Cluster) AutoConfig(req *agentpb.AutoConfigRequest, resp *agentpb.AutoConfigResponse) error {
|
||||||
|
// default the datacenter to our datacenter - agents do not have to specify this as they may not
|
||||||
|
// yet know the datacenter name they are going to be in.
|
||||||
|
if req.Datacenter == "" {
|
||||||
|
req.Datacenter = c.srv.config.Datacenter
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO (autoconf) Is performing auto configuration over the WAN really a bad idea?
|
||||||
|
if req.Datacenter != c.srv.config.Datacenter {
|
||||||
|
return fmt.Errorf("invalid datacenter %q - agent auto configuration cannot target a remote datacenter", req.Datacenter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// forward to the leader
|
||||||
|
if done, err := c.srv.forward("Cluster.AutoConfig", req, req, resp); done {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO (autoconf) maybe panic instead?
|
||||||
|
if c.authorizer == nil {
|
||||||
|
return fmt.Errorf("No Auto Config authorizer is configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
// authorize the request with the configured authorizer
|
||||||
|
opts, err := c.authorizer.Authorize(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := &config.Config{}
|
||||||
|
|
||||||
|
// update all the configurations
|
||||||
|
for _, configFn := range autoConfigUpdaters {
|
||||||
|
if err := configFn(c, opts, conf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Config = conf
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,617 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/agentpb"
|
||||||
|
"github.com/hashicorp/consul/agent/agentpb/config"
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/internal/go-sso/oidcauth/oidcauthtest"
|
||||||
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
|
"github.com/hashicorp/consul/tlsutil"
|
||||||
|
"github.com/hashicorp/memberlist"
|
||||||
|
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"gopkg.in/square/go-jose.v2/jwt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testJWTStandardClaims() jwt.Claims {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
return jwt.Claims{
|
||||||
|
Subject: "consul",
|
||||||
|
Issuer: "consul",
|
||||||
|
Audience: jwt.Audience{"consul"},
|
||||||
|
NotBefore: jwt.NewNumericDate(now.Add(-1 * time.Second)),
|
||||||
|
Expiry: jwt.NewNumericDate(now.Add(10 * time.Minute)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func signJWT(t *testing.T, privKey string, claims jwt.Claims, privateClaims interface{}) string {
|
||||||
|
t.Helper()
|
||||||
|
token, err := oidcauthtest.SignJWT(privKey, claims, privateClaims)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
func signJWTWithStandardClaims(t *testing.T, privKey string, claims interface{}) string {
|
||||||
|
t.Helper()
|
||||||
|
return signJWT(t, privKey, testJWTStandardClaims(), claims)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestClusterAutoConfig is really an integration test of all the moving parts of the Cluster.AutoConfig RPC.
|
||||||
|
// Full testing of the individual parts will not be done in this test:
|
||||||
|
//
|
||||||
|
// * Any implementations of the AutoConfigAuthorizer interface (although these test do use the jwtAuthorizer)
|
||||||
|
// * Each of the individual config generation functions. These can be unit tested separately and many wont
|
||||||
|
// require a running test server.
|
||||||
|
func TestClusterAutoConfig(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
request agentpb.AutoConfigRequest
|
||||||
|
expected agentpb.AutoConfigResponse
|
||||||
|
patchResponse func(t *testing.T, srv *Server, resp *agentpb.AutoConfigResponse)
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
gossipKey := make([]byte, 32)
|
||||||
|
// this is not cryptographic randomness and is not secure but for the sake of this test its all we need.
|
||||||
|
n, err := rand.Read(gossipKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 32, n)
|
||||||
|
|
||||||
|
gossipKeyEncoded := base64.StdEncoding.EncodeToString(gossipKey)
|
||||||
|
|
||||||
|
// generate a test certificate for the server serving out the insecure RPC
|
||||||
|
cert, key, cacert, err := testTLSCertificates("server.dc1.consul")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// generate a JWT signer
|
||||||
|
pub, priv, err := oidcauthtest.GenerateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, altpriv, err := oidcauthtest.GenerateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cases := map[string]testCase{
|
||||||
|
"wrong-datacenter": {
|
||||||
|
request: agentpb.AutoConfigRequest{
|
||||||
|
Datacenter: "no-such-dc",
|
||||||
|
},
|
||||||
|
err: `invalid datacenter "no-such-dc" - agent auto configuration cannot target a remote datacenter`,
|
||||||
|
},
|
||||||
|
"unverifiable": {
|
||||||
|
request: agentpb.AutoConfigRequest{
|
||||||
|
Node: "test-node",
|
||||||
|
// this is signed using an incorrect private key
|
||||||
|
JWT: signJWTWithStandardClaims(t, altpriv, map[string]interface{}{"consul_node_name": "test-node"}),
|
||||||
|
},
|
||||||
|
err: "Permission denied: Failed JWT authorization: no known key successfully validated the token signature",
|
||||||
|
},
|
||||||
|
"claim-assertion-failed": {
|
||||||
|
request: agentpb.AutoConfigRequest{
|
||||||
|
Node: "test-node",
|
||||||
|
JWT: signJWTWithStandardClaims(t, priv, map[string]interface{}{"wrong_claim": "test-node"}),
|
||||||
|
},
|
||||||
|
err: "Permission denied: Failed JWT claim assertion",
|
||||||
|
},
|
||||||
|
"good": {
|
||||||
|
request: agentpb.AutoConfigRequest{
|
||||||
|
Node: "test-node",
|
||||||
|
JWT: signJWTWithStandardClaims(t, priv, map[string]interface{}{"consul_node_name": "test-node"}),
|
||||||
|
},
|
||||||
|
expected: agentpb.AutoConfigResponse{
|
||||||
|
Config: &config.Config{
|
||||||
|
Datacenter: "dc1",
|
||||||
|
PrimaryDatacenter: "dc1",
|
||||||
|
NodeName: "test-node",
|
||||||
|
AutoEncrypt: &config.AutoEncrypt{
|
||||||
|
TLS: true,
|
||||||
|
},
|
||||||
|
ACL: &config.ACL{
|
||||||
|
Enabled: true,
|
||||||
|
PolicyTTL: "30s",
|
||||||
|
TokenTTL: "30s",
|
||||||
|
RoleTTL: "30s",
|
||||||
|
DisabledTTL: "0s",
|
||||||
|
DownPolicy: "extend-cache",
|
||||||
|
DefaultPolicy: "deny",
|
||||||
|
Tokens: &config.ACLTokens{
|
||||||
|
Agent: "patched-secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Gossip: &config.Gossip{
|
||||||
|
Encryption: &config.GossipEncryption{
|
||||||
|
Key: gossipKeyEncoded,
|
||||||
|
VerifyIncoming: true,
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &config.TLS{
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
VerifyServerHostname: true,
|
||||||
|
MinVersion: "tls12",
|
||||||
|
PreferServerCipherSuites: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
patchResponse: func(t *testing.T, srv *Server, resp *agentpb.AutoConfigResponse) {
|
||||||
|
// we are expecting an ACL token but cannot check anything for equality
|
||||||
|
// so here we check that it was set and overwrite it
|
||||||
|
require.NotNil(t, resp.Config)
|
||||||
|
require.NotNil(t, resp.Config.ACL)
|
||||||
|
require.NotNil(t, resp.Config.ACL.Tokens)
|
||||||
|
require.NotEmpty(t, resp.Config.ACL.Tokens.Agent)
|
||||||
|
resp.Config.ACL.Tokens.Agent = "patched-secret"
|
||||||
|
|
||||||
|
// we don't know the expected join address until we start up the test server
|
||||||
|
joinAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: srv.config.SerfLANConfig.MemberlistConfig.AdvertisePort}
|
||||||
|
require.NotNil(t, resp.Config.Gossip)
|
||||||
|
require.Equal(t, []string{joinAddr.String()}, resp.Config.Gossip.RetryJoinLAN)
|
||||||
|
resp.Config.Gossip.RetryJoinLAN = nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, s, _ := testACLServerWithConfig(t, func(c *Config) {
|
||||||
|
c.Domain = "consul"
|
||||||
|
c.AutoConfigAuthzEnabled = true
|
||||||
|
c.AutoConfigAuthzAuthMethod = structs.ACLAuthMethod{
|
||||||
|
Name: "Auth Config Authorizer",
|
||||||
|
Type: "jwt",
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"BoundAudiences": []string{"consul"},
|
||||||
|
"BoundIssuer": "consul",
|
||||||
|
"JWTValidationPubKeys": []string{pub},
|
||||||
|
"ClaimMappings": map[string]string{
|
||||||
|
"consul_node_name": "node",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.AutoConfigAuthzClaimAssertions = []string{
|
||||||
|
`value.node == "${node}"`,
|
||||||
|
}
|
||||||
|
c.AutoConfigAuthzAllowReuse = true
|
||||||
|
|
||||||
|
cafile := path.Join(c.DataDir, "cacert.pem")
|
||||||
|
err := ioutil.WriteFile(cafile, []byte(cacert), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
certfile := path.Join(c.DataDir, "cert.pem")
|
||||||
|
err = ioutil.WriteFile(certfile, []byte(cert), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
keyfile := path.Join(c.DataDir, "key.pem")
|
||||||
|
err = ioutil.WriteFile(keyfile, []byte(key), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
c.CAFile = cafile
|
||||||
|
c.CertFile = certfile
|
||||||
|
c.KeyFile = keyfile
|
||||||
|
c.VerifyOutgoing = true
|
||||||
|
c.VerifyIncoming = true
|
||||||
|
c.VerifyServerHostname = true
|
||||||
|
c.TLSMinVersion = "tls12"
|
||||||
|
c.TLSPreferServerCipherSuites = true
|
||||||
|
|
||||||
|
c.ConnectEnabled = true
|
||||||
|
c.AutoEncryptAllowTLS = true
|
||||||
|
c.SerfLANConfig.MemberlistConfig.GossipVerifyIncoming = true
|
||||||
|
c.SerfLANConfig.MemberlistConfig.GossipVerifyOutgoing = true
|
||||||
|
|
||||||
|
keyring, err := memberlist.NewKeyring(nil, gossipKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
c.SerfLANConfig.MemberlistConfig.Keyring = keyring
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
conf := tlsutil.Config{
|
||||||
|
CAFile: s.config.CAFile,
|
||||||
|
VerifyServerHostname: s.config.VerifyServerHostname,
|
||||||
|
VerifyOutgoing: s.config.VerifyOutgoing,
|
||||||
|
Domain: s.config.Domain,
|
||||||
|
}
|
||||||
|
codec, err := insecureRPCClient(s, conf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
waitForLeaderEstablishment(t, s)
|
||||||
|
|
||||||
|
for testName, tcase := range cases {
|
||||||
|
t.Run(testName, func(t *testing.T) {
|
||||||
|
var reply agentpb.AutoConfigResponse
|
||||||
|
err := msgpackrpc.CallWithCodec(codec, "Cluster.AutoConfig", &tcase.request, &reply)
|
||||||
|
if tcase.err != "" {
|
||||||
|
testutil.RequireErrorContains(t, err, tcase.err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
if tcase.patchResponse != nil {
|
||||||
|
tcase.patchResponse(t, s, &reply)
|
||||||
|
}
|
||||||
|
require.Equal(t, tcase.expected, reply)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClusterAutoConfig_baseConfig(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
serverConfig Config
|
||||||
|
opts AutoConfigOptions
|
||||||
|
expected config.Config
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]testCase{
|
||||||
|
"ok": {
|
||||||
|
serverConfig: Config{
|
||||||
|
Datacenter: "oSWzfhnU",
|
||||||
|
PrimaryDatacenter: "53XO9mx4",
|
||||||
|
},
|
||||||
|
opts: AutoConfigOptions{
|
||||||
|
NodeName: "lBdc0lsH",
|
||||||
|
SegmentName: "HZiwlWpi",
|
||||||
|
},
|
||||||
|
expected: config.Config{
|
||||||
|
Datacenter: "oSWzfhnU",
|
||||||
|
PrimaryDatacenter: "53XO9mx4",
|
||||||
|
NodeName: "lBdc0lsH",
|
||||||
|
SegmentName: "HZiwlWpi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"no-node-name": {
|
||||||
|
serverConfig: Config{
|
||||||
|
Datacenter: "oSWzfhnU",
|
||||||
|
PrimaryDatacenter: "53XO9mx4",
|
||||||
|
},
|
||||||
|
err: "Cannot generate auto config response without a node name",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tcase := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
cluster := Cluster{
|
||||||
|
srv: &Server{
|
||||||
|
config: &tcase.serverConfig,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual config.Config
|
||||||
|
err := cluster.baseConfig(tcase.opts, &actual)
|
||||||
|
if tcase.err == "" {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tcase.expected, actual)
|
||||||
|
} else {
|
||||||
|
testutil.RequireErrorContains(t, err, tcase.err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClusterAutoConfig_updateTLSSettingsInConfig(t *testing.T) {
|
||||||
|
_, _, cacert, err := testTLSCertificates("server.dc1.consul")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dir := testutil.TempDir(t, "auto-config-tls-settings")
|
||||||
|
t.Cleanup(func() { os.RemoveAll(dir) })
|
||||||
|
|
||||||
|
cafile := path.Join(dir, "cacert.pem")
|
||||||
|
err = ioutil.WriteFile(cafile, []byte(cacert), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
parseCiphers := func(t *testing.T, cipherStr string) []uint16 {
|
||||||
|
t.Helper()
|
||||||
|
ciphers, err := tlsutil.ParseCiphers(cipherStr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return ciphers
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
tlsConfig tlsutil.Config
|
||||||
|
expected config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]testCase{
|
||||||
|
"secure": {
|
||||||
|
tlsConfig: tlsutil.Config{
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
VerifyServerHostname: true,
|
||||||
|
TLSMinVersion: "tls12",
|
||||||
|
PreferServerCipherSuites: true,
|
||||||
|
CAFile: cafile,
|
||||||
|
CipherSuites: parseCiphers(t, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"),
|
||||||
|
},
|
||||||
|
expected: config.Config{
|
||||||
|
TLS: &config.TLS{
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
VerifyServerHostname: true,
|
||||||
|
MinVersion: "tls12",
|
||||||
|
PreferServerCipherSuites: true,
|
||||||
|
CipherSuites: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"less-secure": {
|
||||||
|
tlsConfig: tlsutil.Config{
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
VerifyServerHostname: false,
|
||||||
|
TLSMinVersion: "tls10",
|
||||||
|
PreferServerCipherSuites: false,
|
||||||
|
CAFile: cafile,
|
||||||
|
CipherSuites: parseCiphers(t, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"),
|
||||||
|
},
|
||||||
|
expected: config.Config{
|
||||||
|
TLS: &config.TLS{
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
VerifyServerHostname: false,
|
||||||
|
MinVersion: "tls10",
|
||||||
|
PreferServerCipherSuites: false,
|
||||||
|
CipherSuites: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tcase := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
logger := testutil.Logger(t)
|
||||||
|
configurator, err := tlsutil.NewConfigurator(tcase.tlsConfig, logger)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cluster := &Cluster{
|
||||||
|
srv: &Server{
|
||||||
|
tlsConfigurator: configurator,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual config.Config
|
||||||
|
err = cluster.updateTLSSettingsInConfig(AutoConfigOptions{}, &actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tcase.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAutoConfig_updateGossipEncryptionInConfig(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
conf memberlist.Config
|
||||||
|
expected config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
gossipKey := make([]byte, 32)
|
||||||
|
// this is not cryptographic randomness and is not secure but for the sake of this test its all we need.
|
||||||
|
n, err := rand.Read(gossipKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 32, n)
|
||||||
|
gossipKeyEncoded := base64.StdEncoding.EncodeToString(gossipKey)
|
||||||
|
|
||||||
|
keyring, err := memberlist.NewKeyring(nil, gossipKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cases := map[string]testCase{
|
||||||
|
"encryption-required": {
|
||||||
|
conf: memberlist.Config{
|
||||||
|
Keyring: keyring,
|
||||||
|
GossipVerifyIncoming: true,
|
||||||
|
GossipVerifyOutgoing: true,
|
||||||
|
},
|
||||||
|
expected: config.Config{
|
||||||
|
Gossip: &config.Gossip{
|
||||||
|
Encryption: &config.GossipEncryption{
|
||||||
|
Key: gossipKeyEncoded,
|
||||||
|
VerifyIncoming: true,
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"encryption-allowed": {
|
||||||
|
conf: memberlist.Config{
|
||||||
|
Keyring: keyring,
|
||||||
|
GossipVerifyIncoming: false,
|
||||||
|
GossipVerifyOutgoing: false,
|
||||||
|
},
|
||||||
|
expected: config.Config{
|
||||||
|
Gossip: &config.Gossip{
|
||||||
|
Encryption: &config.GossipEncryption{
|
||||||
|
Key: gossipKeyEncoded,
|
||||||
|
VerifyIncoming: false,
|
||||||
|
VerifyOutgoing: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"encryption-disabled": {
|
||||||
|
// zero values all around - if no keyring is configured then the gossip
|
||||||
|
// encryption settings should not be set.
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tcase := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
cluster := Cluster{
|
||||||
|
srv: &Server{
|
||||||
|
config: DefaultConfig(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster.srv.config.SerfLANConfig.MemberlistConfig = &tcase.conf
|
||||||
|
|
||||||
|
var actual config.Config
|
||||||
|
err := cluster.updateGossipEncryptionInConfig(AutoConfigOptions{}, &actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tcase.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAutoConfig_updateTLSCertificatesInConfig(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
serverConfig Config
|
||||||
|
expected config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]testCase{
|
||||||
|
"auto_encrypt-enabled": {
|
||||||
|
serverConfig: Config{
|
||||||
|
ConnectEnabled: true,
|
||||||
|
AutoEncryptAllowTLS: true,
|
||||||
|
},
|
||||||
|
expected: config.Config{
|
||||||
|
AutoEncrypt: &config.AutoEncrypt{TLS: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"auto_encrypt-disabled": {
|
||||||
|
serverConfig: Config{
|
||||||
|
ConnectEnabled: true,
|
||||||
|
AutoEncryptAllowTLS: false,
|
||||||
|
},
|
||||||
|
expected: config.Config{
|
||||||
|
AutoEncrypt: &config.AutoEncrypt{TLS: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tcase := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
cluster := Cluster{
|
||||||
|
srv: &Server{
|
||||||
|
config: &tcase.serverConfig,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual config.Config
|
||||||
|
err := cluster.updateTLSCertificatesInConfig(AutoConfigOptions{}, &actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tcase.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAutoConfig_updateACLsInConfig(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
patch func(c *Config)
|
||||||
|
expected config.Config
|
||||||
|
verify func(t *testing.T, c *config.Config)
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]testCase{
|
||||||
|
"enabled": {
|
||||||
|
patch: func(c *Config) {
|
||||||
|
c.ACLsEnabled = true
|
||||||
|
c.ACLPolicyTTL = 7 * time.Second
|
||||||
|
c.ACLRoleTTL = 10 * time.Second
|
||||||
|
c.ACLTokenTTL = 12 * time.Second
|
||||||
|
c.ACLDisabledTTL = 31 * time.Second
|
||||||
|
c.ACLDefaultPolicy = "allow"
|
||||||
|
c.ACLDownPolicy = "deny"
|
||||||
|
c.ACLEnableKeyListPolicy = true
|
||||||
|
},
|
||||||
|
expected: config.Config{
|
||||||
|
ACL: &config.ACL{
|
||||||
|
Enabled: true,
|
||||||
|
PolicyTTL: "7s",
|
||||||
|
RoleTTL: "10s",
|
||||||
|
TokenTTL: "12s",
|
||||||
|
DisabledTTL: "31s",
|
||||||
|
DownPolicy: "deny",
|
||||||
|
DefaultPolicy: "allow",
|
||||||
|
EnableKeyListPolicy: true,
|
||||||
|
Tokens: &config.ACLTokens{Agent: "verified"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
verify: func(t *testing.T, c *config.Config) {
|
||||||
|
t.Helper()
|
||||||
|
// the agent token secret is non-deterministically generated
|
||||||
|
// So we want to validate that one was set and overwrite with
|
||||||
|
// a value that the expected configurate wants.
|
||||||
|
require.NotNil(t, c)
|
||||||
|
require.NotNil(t, c.ACL)
|
||||||
|
require.NotNil(t, c.ACL.Tokens)
|
||||||
|
require.NotEmpty(t, c.ACL.Tokens.Agent)
|
||||||
|
c.ACL.Tokens.Agent = "verified"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
patch: func(c *Config) {
|
||||||
|
c.ACLsEnabled = false
|
||||||
|
c.ACLPolicyTTL = 7 * time.Second
|
||||||
|
c.ACLRoleTTL = 10 * time.Second
|
||||||
|
c.ACLTokenTTL = 12 * time.Second
|
||||||
|
c.ACLDisabledTTL = 31 * time.Second
|
||||||
|
c.ACLDefaultPolicy = "allow"
|
||||||
|
c.ACLDownPolicy = "deny"
|
||||||
|
c.ACLEnableKeyListPolicy = true
|
||||||
|
},
|
||||||
|
expected: config.Config{
|
||||||
|
ACL: &config.ACL{
|
||||||
|
Enabled: false,
|
||||||
|
PolicyTTL: "7s",
|
||||||
|
RoleTTL: "10s",
|
||||||
|
TokenTTL: "12s",
|
||||||
|
DisabledTTL: "31s",
|
||||||
|
DownPolicy: "deny",
|
||||||
|
DefaultPolicy: "allow",
|
||||||
|
EnableKeyListPolicy: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"local-tokens-disabled": {
|
||||||
|
patch: func(c *Config) {
|
||||||
|
c.PrimaryDatacenter = "somewhere else"
|
||||||
|
},
|
||||||
|
err: "Agent Auto Configuration requires local token usage to be enabled in this datacenter",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tcase := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
_, s, _ := testACLServerWithConfig(t, tcase.patch, false)
|
||||||
|
|
||||||
|
waitForLeaderEstablishment(t, s)
|
||||||
|
|
||||||
|
cluster := Cluster{srv: s}
|
||||||
|
|
||||||
|
var actual config.Config
|
||||||
|
err := cluster.updateACLsInConfig(AutoConfigOptions{NodeName: "something"}, &actual)
|
||||||
|
if tcase.err != "" {
|
||||||
|
testutil.RequireErrorContains(t, err, tcase.err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
if tcase.verify != nil {
|
||||||
|
tcase.verify(t, &actual)
|
||||||
|
}
|
||||||
|
require.Equal(t, tcase.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAutoConfig_updateJoinAddressesInConfig(t *testing.T) {
|
||||||
|
conf := testClusterConfig{
|
||||||
|
Datacenter: "primary",
|
||||||
|
Servers: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes := newTestCluster(t, &conf)
|
||||||
|
|
||||||
|
cluster := Cluster{srv: nodes.Servers[0]}
|
||||||
|
|
||||||
|
var actual config.Config
|
||||||
|
err := cluster.updateJoinAddressesInConfig(AutoConfigOptions{}, &actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var expected []string
|
||||||
|
for _, srv := range nodes.Servers {
|
||||||
|
expected = append(expected, fmt.Sprintf("127.0.0.1:%d", srv.config.SerfLANConfig.MemberlistConfig.BindPort))
|
||||||
|
}
|
||||||
|
require.NotNil(t, actual.Gossip)
|
||||||
|
require.ElementsMatch(t, expected, actual.Gossip.RetryJoinLAN)
|
||||||
|
}
|
|
@ -305,6 +305,17 @@ type Config struct {
|
||||||
// by default in Consul 1.0 and later.
|
// by default in Consul 1.0 and later.
|
||||||
ACLEnableKeyListPolicy bool
|
ACLEnableKeyListPolicy bool
|
||||||
|
|
||||||
|
AutoConfigEnabled bool
|
||||||
|
AutoConfigIntroToken string
|
||||||
|
AutoConfigIntroTokenFile string
|
||||||
|
AutoConfigServerAddresses []string
|
||||||
|
AutoConfigDNSSANs []string
|
||||||
|
AutoConfigIPSANs []net.IP
|
||||||
|
AutoConfigAuthzEnabled bool
|
||||||
|
AutoConfigAuthzAuthMethod structs.ACLAuthMethod
|
||||||
|
AutoConfigAuthzClaimAssertions []string
|
||||||
|
AutoConfigAuthzAllowReuse bool
|
||||||
|
|
||||||
// TombstoneTTL is used to control how long KV tombstones are retained.
|
// TombstoneTTL is used to control how long KV tombstones are retained.
|
||||||
// This provides a window of time where the X-Consul-Index is monotonic.
|
// This provides a window of time where the X-Consul-Index is monotonic.
|
||||||
// Outside this window, the index may not be monotonic. This is a result
|
// Outside this window, the index may not be monotonic. This is a result
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
ca "github.com/hashicorp/consul/agent/connect/ca"
|
ca "github.com/hashicorp/consul/agent/connect/ca"
|
||||||
"github.com/hashicorp/consul/agent/consul/authmethod"
|
"github.com/hashicorp/consul/agent/consul/authmethod"
|
||||||
|
"github.com/hashicorp/consul/agent/consul/authmethod/ssoauth"
|
||||||
"github.com/hashicorp/consul/agent/consul/autopilot"
|
"github.com/hashicorp/consul/agent/consul/autopilot"
|
||||||
"github.com/hashicorp/consul/agent/consul/fsm"
|
"github.com/hashicorp/consul/agent/consul/fsm"
|
||||||
"github.com/hashicorp/consul/agent/consul/state"
|
"github.com/hashicorp/consul/agent/consul/state"
|
||||||
|
@ -834,6 +835,27 @@ func (s *Server) setupRPC() error {
|
||||||
// been configured.
|
// been configured.
|
||||||
s.insecureRPCServer.Register(&AutoEncrypt{srv: s})
|
s.insecureRPCServer.Register(&AutoEncrypt{srv: s})
|
||||||
|
|
||||||
|
// Setup the AutoConfig JWT Authorizer
|
||||||
|
var authz AutoConfigAuthorizer
|
||||||
|
if s.config.AutoConfigAuthzEnabled {
|
||||||
|
// create the auto config authorizer from the JWT authmethod
|
||||||
|
validator, err := ssoauth.NewValidator(s.logger, &s.config.AutoConfigAuthzAuthMethod)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to initialize JWT Auto Config Authorizer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authz = &jwtAuthorizer{
|
||||||
|
validator: validator,
|
||||||
|
allowReuse: s.config.AutoConfigAuthzAllowReuse,
|
||||||
|
claimAssertions: s.config.AutoConfigAuthzClaimAssertions,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This authorizer always returns that the endpoint is disabled
|
||||||
|
authz = &disabledAuthorizer{}
|
||||||
|
}
|
||||||
|
// now register with the insecure RPC server
|
||||||
|
s.insecureRPCServer.Register(&Cluster{srv: s, authorizer: authz})
|
||||||
|
|
||||||
ln, err := net.ListenTCP("tcp", s.config.RPCAddr)
|
ln, err := net.ListenTCP("tcp", s.config.RPCAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -2,6 +2,10 @@ package consul
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
|
@ -38,6 +42,46 @@ const (
|
||||||
TestDefaultMasterToken = "d9f05e83-a7ae-47ce-839e-c0d53a68c00a"
|
TestDefaultMasterToken = "d9f05e83-a7ae-47ce-839e-c0d53a68c00a"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// testTLSCertificates Generates a TLS CA and server key/cert and returns them
|
||||||
|
// in PEM encoded form.
|
||||||
|
func testTLSCertificates(serverName string) (cert string, key string, cacert string, err error) {
|
||||||
|
// generate CA
|
||||||
|
serial, err := tlsutil.GenerateSerialNumber()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", err
|
||||||
|
}
|
||||||
|
signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", err
|
||||||
|
}
|
||||||
|
ca, err := tlsutil.GenerateCA(signer, serial, 365, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate leaf
|
||||||
|
serial, err = tlsutil.GenerateSerialNumber()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, privateKey, err := tlsutil.GenerateCert(
|
||||||
|
signer,
|
||||||
|
ca,
|
||||||
|
serial,
|
||||||
|
"Test Cert Name",
|
||||||
|
365,
|
||||||
|
[]string{serverName},
|
||||||
|
nil,
|
||||||
|
[]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert, privateKey, ca, nil
|
||||||
|
}
|
||||||
|
|
||||||
// testServerACLConfig wraps another arbitrary Config altering callback
|
// testServerACLConfig wraps another arbitrary Config altering callback
|
||||||
// to setup some common ACL configurations. A new callback func will
|
// to setup some common ACL configurations. A new callback func will
|
||||||
// be returned that has the original callback invoked after setting
|
// be returned that has the original callback invoked after setting
|
||||||
|
|
|
@ -80,17 +80,15 @@ function main {
|
||||||
local proto_go_path=${proto_path%%.proto}.pb.go
|
local proto_go_path=${proto_path%%.proto}.pb.go
|
||||||
local proto_go_bin_path=${proto_path%%.proto}.pb.binary.go
|
local proto_go_bin_path=${proto_path%%.proto}.pb.binary.go
|
||||||
|
|
||||||
local go_proto_out=""
|
local go_proto_out="paths=source_relative"
|
||||||
local sep=""
|
|
||||||
if is_set "${grpc}"
|
if is_set "${grpc}"
|
||||||
then
|
then
|
||||||
go_proto_out="plugins=grpc"
|
go_proto_out="${go_proto_out},plugins=grpc"
|
||||||
sep=","
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if is_set "${imp_replace}"
|
if is_set "${imp_replace}"
|
||||||
then
|
then
|
||||||
go_proto_out="${go_proto_out}${sep}${gogo_proto_imp_replace}"
|
go_proto_out="${go_proto_out},${gogo_proto_imp_replace}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -n "${go_proto_out}"
|
if test -n "${go_proto_out}"
|
||||||
|
@ -98,15 +96,19 @@ function main {
|
||||||
go_proto_out="${go_proto_out}:"
|
go_proto_out="${go_proto_out}:"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# How we run protoc probably needs some documentation.
|
||||||
|
#
|
||||||
|
# This is the path to where
|
||||||
|
# -I="${gogo_proto_path}/protobuf" \
|
||||||
local -i ret=0
|
local -i ret=0
|
||||||
status_stage "Generating ${proto_path} into ${proto_go_path} and ${proto_go_bin_path}"
|
status_stage "Generating ${proto_path} into ${proto_go_path} and ${proto_go_bin_path}"
|
||||||
debug_run protoc \
|
debug_run protoc \
|
||||||
-I="$(dirname ${proto_path})" \
|
|
||||||
-I="${gogo_proto_path}/protobuf" \
|
-I="${gogo_proto_path}/protobuf" \
|
||||||
-I="${gogo_proto_path}" \
|
-I="${gogo_proto_path}" \
|
||||||
-I="${gogo_proto_mod_path}" \
|
-I="${gogo_proto_mod_path}" \
|
||||||
--gofast_out="${go_proto_out}$(dirname ${proto_path})" \
|
-I="${SOURCE_DIR}" \
|
||||||
--go-binary_out="$(dirname ${proto_path})" \
|
--gofast_out="${go_proto_out}${SOURCE_DIR}" \
|
||||||
|
--go-binary_out="${SOURCE_DIR}" \
|
||||||
"${proto_path}"
|
"${proto_path}"
|
||||||
if test $? -ne 0
|
if test $? -ne 0
|
||||||
then
|
then
|
||||||
|
|
|
@ -490,13 +490,13 @@ func init() {
|
||||||
// test. These are cached between runs but do not persist between restarts
|
// test. These are cached between runs but do not persist between restarts
|
||||||
// of the test binary.
|
// of the test binary.
|
||||||
var err error
|
var err error
|
||||||
ecdsaPublicKey, ecdsaPrivateKey, err = generateKey()
|
ecdsaPublicKey, ecdsaPrivateKey, err = GenerateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateKey() (pub, priv string, err error) {
|
func GenerateKey() (pub, priv string, err error) {
|
||||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("error generating private key: %v", err)
|
return "", "", fmt.Errorf("error generating private key: %v", err)
|
||||||
|
|
|
@ -936,6 +936,7 @@ func ParseCiphers(cipherStr string) ([]uint16, error) {
|
||||||
}
|
}
|
||||||
ciphers := strings.Split(cipherStr, ",")
|
ciphers := strings.Split(cipherStr, ",")
|
||||||
|
|
||||||
|
// Note: this needs to be kept up to date with the cipherMap in CipherString
|
||||||
cipherMap := map[string]uint16{
|
cipherMap := map[string]uint16{
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
@ -958,3 +959,31 @@ func ParseCiphers(cipherStr string) ([]uint16, error) {
|
||||||
|
|
||||||
return suites, nil
|
return suites, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CipherString performs the inverse operation of ParseCiphers
|
||||||
|
func CipherString(ciphers []uint16) (string, error) {
|
||||||
|
// Note: this needs to be kept up to date with the cipherMap in ParseCiphers
|
||||||
|
cipherMap := map[uint16]string{
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
}
|
||||||
|
|
||||||
|
cipherStrings := make([]string, len(ciphers))
|
||||||
|
for i, cipher := range ciphers {
|
||||||
|
if v, ok := cipherMap[cipher]; ok {
|
||||||
|
cipherStrings[i] = v
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("unsupported cipher %d", cipher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(cipherStrings, ","), nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue