From 69280498ddfe0d3136f76e597dcfaa22f2224810 Mon Sep 17 00:00:00 2001 From: cyli Date: Tue, 17 Jan 2017 17:04:16 -0800 Subject: [PATCH 1/6] Update NodeDescription object to store information about its current TLS state, namely what roots the node trusts and the signer info of its current TLS certificate. Also update the session message to include the desired root CA for the node. Signed-off-by: cyli --- api/dispatcher.pb.go | 171 ++++--- api/dispatcher.proto | 3 + api/types.pb.go | 1100 +++++++++++++++++++++++++++--------------- api/types.proto | 12 + 4 files changed, 831 insertions(+), 455 deletions(-) diff --git a/api/dispatcher.pb.go b/api/dispatcher.pb.go index ce8d7ddbe4..98be17ad9a 100644 --- a/api/dispatcher.pb.go +++ b/api/dispatcher.pb.go @@ -154,6 +154,8 @@ type SessionMessage struct { // Symmetric encryption key distributed by the lead manager. Used by agents // for securing network bootstrapping and communication. NetworkBootstrapKeys []*EncryptionKey `protobuf:"bytes,4,rep,name=network_bootstrap_keys,json=networkBootstrapKeys" json:"network_bootstrap_keys,omitempty"` + // Which root certificates to trust + RootCA []byte `protobuf:"bytes,5,opt,name=RootCA,proto3" json:"RootCA,omitempty"` } func (m *SessionMessage) Reset() { *m = SessionMessage{} } @@ -513,6 +515,10 @@ func (m *SessionMessage) CopyFrom(src interface{}) { } } + if o.RootCA != nil { + m.RootCA = make([]byte, len(o.RootCA)) + copy(m.RootCA, o.RootCA) + } } func (m *HeartbeatRequest) Copy() *HeartbeatRequest { @@ -1148,6 +1154,12 @@ func (m *SessionMessage) MarshalTo(dAtA []byte) (int, error) { i += n } } + if len(m.RootCA) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintDispatcher(dAtA, i, uint64(len(m.RootCA))) + i += copy(dAtA[i:], m.RootCA) + } return i, nil } @@ -1860,6 +1872,10 @@ func (m *SessionMessage) Size() (n int) { n += 1 + l + sovDispatcher(uint64(l)) } } + l = len(m.RootCA) + if l > 0 { + n += 1 + l + sovDispatcher(uint64(l)) + } return n } @@ -2045,6 +2061,7 @@ func (this *SessionMessage) String() string { `Node:` + strings.Replace(fmt.Sprintf("%v", this.Node), "Node", "Node", 1) + `,`, `Managers:` + strings.Replace(fmt.Sprintf("%v", this.Managers), "WeightedPeer", "WeightedPeer", 1) + `,`, `NetworkBootstrapKeys:` + strings.Replace(fmt.Sprintf("%v", this.NetworkBootstrapKeys), "EncryptionKey", "EncryptionKey", 1) + `,`, + `RootCA:` + fmt.Sprintf("%v", this.RootCA) + `,`, `}`, }, "") return s @@ -2457,6 +2474,37 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RootCA", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDispatcher + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDispatcher + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RootCA = append(m.RootCA[:0], dAtA[iNdEx:postIndex]...) + if m.RootCA == nil { + m.RootCA = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipDispatcher(dAtA[iNdEx:]) @@ -3630,65 +3678,66 @@ var ( func init() { proto.RegisterFile("dispatcher.proto", fileDescriptorDispatcher) } var fileDescriptorDispatcher = []byte{ - // 949 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x6e, 0xe3, 0x54, - 0x14, 0x8e, 0xd3, 0xd4, 0x6d, 0x4f, 0xda, 0x12, 0x2e, 0xa3, 0xc1, 0x58, 0x9a, 0x34, 0xb8, 0x4c, - 0x54, 0x69, 0x8a, 0x3b, 0x84, 0x9f, 0x0d, 0x55, 0xa1, 0x69, 0x22, 0x35, 0x9a, 0x49, 0xa7, 0xba, - 0xcd, 0xcc, 0x2c, 0x23, 0x27, 0x3e, 0xb8, 0x26, 0x8d, 0xaf, 0xf1, 0xbd, 0x99, 0x21, 0x48, 0x48, - 0x48, 0x30, 0x12, 0x62, 0x85, 0x58, 0x75, 0xc3, 0x2b, 0xf0, 0x1c, 0x15, 0x2b, 0x96, 0xac, 0x0a, - 0x93, 0x07, 0xe0, 0x01, 0x58, 0x21, 0xdb, 0xd7, 0x49, 0xc8, 0x24, 0x9d, 0xb4, 0xab, 0xc4, 0xe7, - 0x7c, 0xdf, 0x39, 0x9f, 0xbf, 0x7b, 0x7c, 0x2e, 0xe4, 0x6c, 0x97, 0xfb, 0x96, 0x68, 0x9f, 0x62, - 0x60, 0xfa, 0x01, 0x13, 0x8c, 0x10, 0x9b, 0xb5, 0x3b, 0x18, 0x98, 0xfc, 0xb9, 0x15, 0x74, 0x3b, - 0xae, 0x30, 0x9f, 0x7d, 0xa0, 0x67, 0x45, 0xdf, 0x47, 0x1e, 0x03, 0xf4, 0x35, 0xd6, 0xfa, 0x12, - 0xdb, 0x22, 0x79, 0xbc, 0xe5, 0x30, 0x87, 0x45, 0x7f, 0x77, 0xc2, 0x7f, 0x32, 0xfa, 0x96, 0x7f, - 0xd6, 0x73, 0x5c, 0x6f, 0x27, 0xfe, 0x91, 0xc1, 0xbc, 0xc3, 0x98, 0x73, 0x86, 0x3b, 0xd1, 0x53, - 0xab, 0xf7, 0xc5, 0x8e, 0xdd, 0x0b, 0x2c, 0xe1, 0x32, 0x99, 0x37, 0x5e, 0x28, 0xb0, 0x7e, 0x82, - 0x9c, 0xbb, 0xcc, 0xa3, 0xf8, 0x55, 0x0f, 0xb9, 0x20, 0x55, 0xc8, 0xda, 0xc8, 0xdb, 0x81, 0xeb, - 0x87, 0x38, 0x4d, 0x29, 0x28, 0x5b, 0xd9, 0xd2, 0xa6, 0xf9, 0xaa, 0x46, 0xf3, 0x88, 0xd9, 0x58, - 0x19, 0x41, 0xe9, 0x38, 0x8f, 0x6c, 0x03, 0xf0, 0xb8, 0x70, 0xd3, 0xb5, 0xb5, 0x74, 0x41, 0xd9, - 0x5a, 0x29, 0xaf, 0x0d, 0x2e, 0x37, 0x56, 0x64, 0xbb, 0x5a, 0x85, 0xae, 0x48, 0x40, 0xcd, 0x36, - 0xbe, 0x4f, 0x0f, 0x75, 0xd4, 0x91, 0x73, 0xcb, 0xc1, 0x89, 0x02, 0xca, 0xd5, 0x05, 0xc8, 0x36, - 0x64, 0x3c, 0x66, 0x63, 0xd4, 0x28, 0x5b, 0xd2, 0x66, 0xc9, 0xa5, 0x11, 0x8a, 0xec, 0xc2, 0x72, - 0xd7, 0xf2, 0x2c, 0x07, 0x03, 0xae, 0x2d, 0x14, 0x16, 0xb6, 0xb2, 0xa5, 0xc2, 0x34, 0xc6, 0x53, - 0x74, 0x9d, 0x53, 0x81, 0xf6, 0x31, 0x62, 0x40, 0x87, 0x0c, 0xf2, 0x14, 0x6e, 0x7b, 0x28, 0x9e, - 0xb3, 0xa0, 0xd3, 0x6c, 0x31, 0x26, 0xb8, 0x08, 0x2c, 0xbf, 0xd9, 0xc1, 0x3e, 0xd7, 0x32, 0x51, - 0xad, 0x77, 0xa7, 0xd5, 0xaa, 0x7a, 0xed, 0xa0, 0x1f, 0x59, 0xf3, 0x00, 0xfb, 0xf4, 0x96, 0x2c, - 0x50, 0x4e, 0xf8, 0x0f, 0xb0, 0xcf, 0x8d, 0xcf, 0x21, 0x77, 0x88, 0x56, 0x20, 0x5a, 0x68, 0x89, - 0xe4, 0x38, 0xae, 0x65, 0x83, 0x71, 0x0c, 0x6f, 0x8e, 0x55, 0xe0, 0x3e, 0xf3, 0x38, 0x92, 0x4f, - 0x41, 0xf5, 0x31, 0x70, 0x99, 0x2d, 0x0f, 0xf3, 0x1d, 0x33, 0x9e, 0x0a, 0x33, 0x99, 0x0a, 0xb3, - 0x22, 0xa7, 0xa2, 0xbc, 0x7c, 0x71, 0xb9, 0x91, 0x3a, 0xff, 0x6b, 0x43, 0xa1, 0x92, 0x62, 0xfc, - 0x9c, 0x86, 0xb7, 0x1f, 0xfb, 0xb6, 0x25, 0xb0, 0x61, 0xf1, 0xce, 0x89, 0xb0, 0x44, 0x8f, 0xdf, - 0x48, 0x1b, 0x79, 0x02, 0x4b, 0xbd, 0xa8, 0x50, 0xe2, 0xf9, 0xee, 0x34, 0x9f, 0x66, 0xf4, 0x32, - 0x47, 0x91, 0x18, 0x41, 0x93, 0x62, 0x3a, 0x83, 0xdc, 0x64, 0x92, 0x6c, 0xc2, 0x92, 0xb0, 0x78, - 0x67, 0x24, 0x0b, 0x06, 0x97, 0x1b, 0x6a, 0x08, 0xab, 0x55, 0xa8, 0x1a, 0xa6, 0x6a, 0x36, 0xf9, - 0x04, 0x54, 0x1e, 0x91, 0xe4, 0xd4, 0xe4, 0xa7, 0xe9, 0x19, 0x53, 0x22, 0xd1, 0x86, 0x0e, 0xda, - 0xab, 0x2a, 0x63, 0xaf, 0x8d, 0x5d, 0x58, 0x0d, 0xa3, 0x37, 0xb3, 0xc8, 0xd8, 0x93, 0xec, 0xe4, - 0x1b, 0x30, 0x61, 0x31, 0xd4, 0xca, 0x35, 0x25, 0x32, 0x4c, 0x9b, 0x25, 0x90, 0xc6, 0x30, 0xa3, - 0x0c, 0x64, 0x9f, 0x73, 0xd7, 0xf1, 0xba, 0xe8, 0x89, 0x1b, 0x6a, 0xf8, 0x06, 0x60, 0x54, 0x83, - 0x98, 0x90, 0x09, 0x4b, 0xcb, 0xc9, 0x99, 0x29, 0xe0, 0x30, 0x45, 0x23, 0x1c, 0xf9, 0x08, 0x54, - 0x8e, 0xed, 0x00, 0x85, 0xf4, 0x54, 0x9f, 0xc6, 0x38, 0x89, 0x10, 0x87, 0x29, 0x2a, 0xb1, 0x65, - 0x15, 0x32, 0xae, 0xc0, 0xae, 0xf1, 0x22, 0x0d, 0xb9, 0x51, 0xf3, 0x83, 0x53, 0xcb, 0x73, 0x90, - 0xec, 0x01, 0x58, 0xc3, 0x98, 0x14, 0x32, 0xf5, 0xa8, 0x46, 0x4c, 0x3a, 0xc6, 0x20, 0x75, 0x50, - 0xad, 0x76, 0xb4, 0xcb, 0x42, 0x49, 0xeb, 0xa5, 0x8f, 0xaf, 0xe6, 0xc6, 0x5d, 0xc7, 0x02, 0xfb, - 0x11, 0x99, 0xca, 0x22, 0x46, 0x6b, 0x5c, 0x62, 0x9c, 0x23, 0x45, 0x50, 0x1f, 0x1f, 0x57, 0xf6, - 0x1b, 0xd5, 0x5c, 0x4a, 0xd7, 0x7f, 0xfa, 0xb5, 0x70, 0x7b, 0x12, 0x21, 0xc7, 0xb2, 0x08, 0x2a, - 0xad, 0xd6, 0x1f, 0x3d, 0xa9, 0xe6, 0x94, 0xe9, 0x38, 0x8a, 0x5d, 0xf6, 0x0c, 0x8d, 0x7f, 0x95, - 0xff, 0x1d, 0x64, 0x32, 0x0e, 0x9f, 0x41, 0x26, 0xbc, 0x16, 0x22, 0x0f, 0xd6, 0x4b, 0xf7, 0xae, - 0x7e, 0x8f, 0x84, 0x65, 0x36, 0xfa, 0x3e, 0xd2, 0x88, 0x48, 0xee, 0x00, 0x58, 0xbe, 0x7f, 0xe6, - 0x22, 0x6f, 0x0a, 0x16, 0x2f, 0x65, 0xba, 0x22, 0x23, 0x0d, 0x16, 0xa6, 0x03, 0xe4, 0xbd, 0x33, - 0xc1, 0x9b, 0xae, 0xa7, 0x2d, 0xc4, 0x69, 0x19, 0xa9, 0x79, 0x64, 0x0f, 0x96, 0xda, 0x91, 0x39, - 0xc9, 0xa2, 0x7b, 0x6f, 0x1e, 0x27, 0x69, 0x42, 0x32, 0xee, 0x42, 0x26, 0xd4, 0x42, 0x56, 0x61, - 0xf9, 0xe0, 0x51, 0xfd, 0xf8, 0x61, 0x35, 0xf4, 0x8b, 0xbc, 0x01, 0xd9, 0xda, 0xd1, 0x01, 0xad, - 0xd6, 0xab, 0x47, 0x8d, 0xfd, 0x87, 0x39, 0xa5, 0x74, 0xbe, 0x08, 0x50, 0x19, 0xde, 0x91, 0xe4, - 0x6b, 0x58, 0x92, 0x73, 0x4a, 0x8c, 0xe9, 0xc3, 0x34, 0x7e, 0x7d, 0xe9, 0x57, 0x61, 0xa4, 0x23, - 0xc6, 0xe6, 0xef, 0xbf, 0xfd, 0x73, 0x9e, 0xbe, 0x03, 0xab, 0x11, 0xe6, 0xfd, 0x70, 0x11, 0x63, - 0x00, 0x6b, 0xf1, 0x93, 0x5c, 0xf3, 0xf7, 0x15, 0xf2, 0x2d, 0xac, 0x0c, 0x97, 0x29, 0x99, 0xfa, - 0xae, 0x93, 0xdb, 0x5a, 0xbf, 0xfb, 0x1a, 0x94, 0xdc, 0x12, 0xf3, 0x08, 0x20, 0xbf, 0x28, 0x90, - 0x9b, 0xdc, 0x33, 0xe4, 0xde, 0x35, 0x76, 0xa6, 0xbe, 0x3d, 0x1f, 0xf8, 0x3a, 0xa2, 0x7a, 0xb0, - 0x18, 0x6d, 0x28, 0x52, 0x98, 0xb5, 0x0a, 0x86, 0xdd, 0x67, 0x23, 0x92, 0x73, 0x28, 0xce, 0xd1, - 0xf1, 0xc7, 0xb4, 0x72, 0x5f, 0x21, 0x3f, 0x28, 0x90, 0x1d, 0x1b, 0x6d, 0x52, 0x7c, 0xcd, 0xec, - 0x27, 0x1a, 0x8a, 0xf3, 0x7d, 0x23, 0x73, 0x4e, 0x44, 0x59, 0xbb, 0x78, 0x99, 0x4f, 0xfd, 0xf9, - 0x32, 0x9f, 0xfa, 0x6e, 0x90, 0x57, 0x2e, 0x06, 0x79, 0xe5, 0x8f, 0x41, 0x5e, 0xf9, 0x7b, 0x90, - 0x57, 0x5a, 0x6a, 0x74, 0x97, 0x7e, 0xf8, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x70, 0xa6, 0xd5, - 0x2c, 0xde, 0x09, 0x00, 0x00, + // 964 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0x4f, 0x6f, 0xe3, 0x44, + 0x14, 0xcf, 0x64, 0x53, 0xb7, 0x7d, 0x69, 0x4b, 0x18, 0x56, 0xc5, 0x58, 0xda, 0x34, 0xb8, 0x6c, + 0x55, 0x69, 0x8b, 0xbb, 0x84, 0x3f, 0x17, 0xaa, 0x42, 0xd2, 0x44, 0x6a, 0xb4, 0x9b, 0x6e, 0x35, + 0xcd, 0xee, 0x1e, 0x23, 0x27, 0x1e, 0x5c, 0x93, 0xc6, 0x63, 0x3c, 0x93, 0x5d, 0x82, 0x84, 0xc4, + 0x81, 0x95, 0x10, 0x27, 0xc4, 0xa9, 0x12, 0xe2, 0x2b, 0xf0, 0x39, 0x2a, 0x4e, 0x1c, 0x39, 0x15, + 0x36, 0x1f, 0x80, 0x0f, 0xc0, 0x09, 0x79, 0x3c, 0x4e, 0x42, 0x36, 0xe9, 0xa6, 0x3d, 0xd9, 0xf3, + 0xe6, 0xf7, 0x7b, 0xef, 0xe7, 0x37, 0xcf, 0xbf, 0x81, 0x9c, 0xe3, 0xf1, 0xc0, 0x16, 0xed, 0x53, + 0x1a, 0x5a, 0x41, 0xc8, 0x04, 0xc3, 0xd8, 0x61, 0xed, 0x0e, 0x0d, 0x2d, 0xfe, 0xdc, 0x0e, 0xbb, + 0x1d, 0x4f, 0x58, 0xcf, 0x3e, 0x30, 0xb2, 0xa2, 0x1f, 0x50, 0x1e, 0x03, 0x8c, 0x55, 0xd6, 0xfa, + 0x92, 0xb6, 0x45, 0xb2, 0xbc, 0xed, 0x32, 0x97, 0xc9, 0xd7, 0xdd, 0xe8, 0x4d, 0x45, 0xdf, 0x0a, + 0xce, 0x7a, 0xae, 0xe7, 0xef, 0xc6, 0x0f, 0x15, 0xcc, 0xbb, 0x8c, 0xb9, 0x67, 0x74, 0x57, 0xae, + 0x5a, 0xbd, 0x2f, 0x76, 0x9d, 0x5e, 0x68, 0x0b, 0x8f, 0xa9, 0x7d, 0xf3, 0x05, 0x82, 0xb5, 0x13, + 0xca, 0xb9, 0xc7, 0x7c, 0x42, 0xbf, 0xea, 0x51, 0x2e, 0x70, 0x15, 0xb2, 0x0e, 0xe5, 0xed, 0xd0, + 0x0b, 0x22, 0x9c, 0x8e, 0x0a, 0x68, 0x3b, 0x5b, 0xdc, 0xb4, 0x5e, 0xd5, 0x68, 0x1d, 0x31, 0x87, + 0x56, 0x46, 0x50, 0x32, 0xce, 0xc3, 0x3b, 0x00, 0x3c, 0x4e, 0xdc, 0xf4, 0x1c, 0x3d, 0x5d, 0x40, + 0xdb, 0xcb, 0xe5, 0xd5, 0xc1, 0xe5, 0xc6, 0xb2, 0x2a, 0x57, 0xab, 0x90, 0x65, 0x05, 0xa8, 0x39, + 0xe6, 0x2f, 0xe9, 0xa1, 0x8e, 0x3a, 0xe5, 0xdc, 0x76, 0xe9, 0x44, 0x02, 0x74, 0x75, 0x02, 0xbc, + 0x03, 0x19, 0x9f, 0x39, 0x54, 0x16, 0xca, 0x16, 0xf5, 0x59, 0x72, 0x89, 0x44, 0xe1, 0x3d, 0x58, + 0xea, 0xda, 0xbe, 0xed, 0xd2, 0x90, 0xeb, 0xb7, 0x0a, 0xb7, 0xb6, 0xb3, 0xc5, 0xc2, 0x34, 0xc6, + 0x53, 0xea, 0xb9, 0xa7, 0x82, 0x3a, 0xc7, 0x94, 0x86, 0x64, 0xc8, 0xc0, 0x4f, 0x61, 0xdd, 0xa7, + 0xe2, 0x39, 0x0b, 0x3b, 0xcd, 0x16, 0x63, 0x82, 0x8b, 0xd0, 0x0e, 0x9a, 0x1d, 0xda, 0xe7, 0x7a, + 0x46, 0xe6, 0x7a, 0x77, 0x5a, 0xae, 0xaa, 0xdf, 0x0e, 0xfb, 0xb2, 0x35, 0x0f, 0x68, 0x9f, 0xdc, + 0x56, 0x09, 0xca, 0x09, 0xff, 0x01, 0xed, 0x73, 0xbc, 0x0e, 0x1a, 0x61, 0x4c, 0x1c, 0x94, 0xf4, + 0x85, 0x02, 0xda, 0x5e, 0x21, 0x6a, 0x65, 0x7e, 0x0e, 0xb9, 0x43, 0x6a, 0x87, 0xa2, 0x45, 0x6d, + 0x91, 0x1c, 0xd3, 0xb5, 0xda, 0x63, 0x1e, 0xc3, 0x9b, 0x63, 0x19, 0x78, 0xc0, 0x7c, 0x4e, 0xf1, + 0xa7, 0xa0, 0x05, 0x34, 0xf4, 0x98, 0xa3, 0x0e, 0xf9, 0x1d, 0x2b, 0x9e, 0x16, 0x2b, 0x99, 0x16, + 0xab, 0xa2, 0xa6, 0xa5, 0xbc, 0x74, 0x71, 0xb9, 0x91, 0x3a, 0xff, 0x6b, 0x03, 0x11, 0x45, 0x31, + 0x7f, 0x4a, 0xc3, 0xdb, 0x8f, 0x03, 0xc7, 0x16, 0xb4, 0x61, 0xf3, 0xce, 0x89, 0xb0, 0x45, 0x8f, + 0xdf, 0x48, 0x1b, 0x7e, 0x02, 0x8b, 0x3d, 0x99, 0x28, 0x39, 0x8b, 0xbd, 0x69, 0xfd, 0x9b, 0x51, + 0xcb, 0x1a, 0x45, 0x62, 0x04, 0x49, 0x92, 0x19, 0x0c, 0x72, 0x93, 0x9b, 0x78, 0x13, 0x16, 0x85, + 0xcd, 0x3b, 0x23, 0x59, 0x30, 0xb8, 0xdc, 0xd0, 0x22, 0x58, 0xad, 0x42, 0xb4, 0x68, 0xab, 0xe6, + 0xe0, 0x4f, 0x40, 0xe3, 0x92, 0xa4, 0xa6, 0x29, 0x3f, 0x4d, 0xcf, 0x98, 0x12, 0x85, 0x36, 0x0d, + 0xd0, 0x5f, 0x55, 0x19, 0xf7, 0xda, 0xdc, 0x83, 0x95, 0x28, 0x7a, 0xb3, 0x16, 0x99, 0xfb, 0x8a, + 0x9d, 0xfc, 0x1b, 0x16, 0x2c, 0x44, 0x5a, 0xb9, 0x8e, 0x64, 0xc3, 0xf4, 0x59, 0x02, 0x49, 0x0c, + 0x33, 0xcb, 0x80, 0x4b, 0x9c, 0x7b, 0xae, 0xdf, 0xa5, 0xbe, 0xb8, 0xa1, 0x86, 0x6f, 0x00, 0x46, + 0x39, 0xb0, 0x05, 0x99, 0x28, 0xb5, 0x9a, 0x9c, 0x99, 0x02, 0x0e, 0x53, 0x44, 0xe2, 0xf0, 0x47, + 0xa0, 0x71, 0xda, 0x0e, 0xa9, 0x50, 0x3d, 0x35, 0xa6, 0x31, 0x4e, 0x24, 0xe2, 0x30, 0x45, 0x14, + 0xb6, 0xac, 0x41, 0xc6, 0x13, 0xb4, 0x6b, 0xbe, 0x48, 0x43, 0x6e, 0x54, 0xfc, 0xe0, 0xd4, 0xf6, + 0x5d, 0x8a, 0xf7, 0x01, 0xec, 0x61, 0x4c, 0x09, 0x99, 0x7a, 0x54, 0x23, 0x26, 0x19, 0x63, 0xe0, + 0x3a, 0x68, 0x76, 0x5b, 0x7a, 0x5c, 0x24, 0x69, 0xad, 0xf8, 0xf1, 0xd5, 0xdc, 0xb8, 0xea, 0x58, + 0xa0, 0x24, 0xc9, 0x44, 0x25, 0x31, 0x5b, 0xe3, 0x12, 0xe3, 0x3d, 0xbc, 0x05, 0xda, 0xe3, 0xe3, + 0x4a, 0xa9, 0x51, 0xcd, 0xa5, 0x0c, 0xe3, 0xc7, 0x5f, 0x0b, 0xeb, 0x93, 0x08, 0x35, 0x96, 0x5b, + 0xa0, 0x91, 0x6a, 0xfd, 0xd1, 0x93, 0x6a, 0x0e, 0x4d, 0xc7, 0x11, 0xda, 0x65, 0xcf, 0xa8, 0xf9, + 0x2f, 0xfa, 0xdf, 0x41, 0x26, 0xe3, 0xf0, 0x19, 0x64, 0xa2, 0xeb, 0x42, 0xf6, 0x60, 0xad, 0x78, + 0xef, 0xea, 0xef, 0x48, 0x58, 0x56, 0xa3, 0x1f, 0x50, 0x22, 0x89, 0xf8, 0x0e, 0x80, 0x1d, 0x04, + 0x67, 0x1e, 0xe5, 0x4d, 0xc1, 0x62, 0xb3, 0x26, 0xcb, 0x2a, 0xd2, 0x60, 0xd1, 0x76, 0x48, 0x79, + 0xef, 0x4c, 0xf0, 0xa6, 0xe7, 0xeb, 0xb7, 0xe2, 0x6d, 0x15, 0xa9, 0xf9, 0x78, 0x1f, 0x16, 0xdb, + 0xb2, 0x39, 0x89, 0x01, 0xbe, 0x37, 0x4f, 0x27, 0x49, 0x42, 0x32, 0xef, 0x42, 0x26, 0xd2, 0x82, + 0x57, 0x60, 0xe9, 0xe0, 0x51, 0xfd, 0xf8, 0x61, 0x35, 0xea, 0x17, 0x7e, 0x03, 0xb2, 0xb5, 0xa3, + 0x03, 0x52, 0xad, 0x57, 0x8f, 0x1a, 0xa5, 0x87, 0x39, 0x54, 0x3c, 0x5f, 0x00, 0xa8, 0x0c, 0xef, + 0x4e, 0xfc, 0x35, 0x2c, 0xaa, 0x39, 0xc5, 0xe6, 0xf4, 0x61, 0x1a, 0xbf, 0xd6, 0x8c, 0xab, 0x30, + 0xaa, 0x23, 0xe6, 0xe6, 0xef, 0xbf, 0xfd, 0x73, 0x9e, 0xbe, 0x03, 0x2b, 0x12, 0xf3, 0x7e, 0x64, + 0xd0, 0x34, 0x84, 0xd5, 0x78, 0xa5, 0xec, 0xff, 0x3e, 0xc2, 0xdf, 0xc2, 0xf2, 0xd0, 0x4c, 0xf1, + 0xd4, 0x6f, 0x9d, 0x74, 0x6b, 0xe3, 0xee, 0x6b, 0x50, 0xca, 0x25, 0xe6, 0x11, 0x80, 0x7f, 0x46, + 0x90, 0x9b, 0xf4, 0x19, 0x7c, 0xef, 0x1a, 0x9e, 0x69, 0xec, 0xcc, 0x07, 0xbe, 0x8e, 0xa8, 0x1e, + 0x2c, 0x48, 0x87, 0xc2, 0x85, 0x59, 0x56, 0x30, 0xac, 0x3e, 0x1b, 0x91, 0x9c, 0xc3, 0xd6, 0x1c, + 0x15, 0x7f, 0x48, 0xa3, 0xfb, 0x08, 0x7f, 0x8f, 0x20, 0x3b, 0x36, 0xda, 0x78, 0xeb, 0x35, 0xb3, + 0x9f, 0x68, 0xd8, 0x9a, 0xef, 0x1f, 0x99, 0x73, 0x22, 0xca, 0xfa, 0xc5, 0xcb, 0x7c, 0xea, 0xcf, + 0x97, 0xf9, 0xd4, 0x77, 0x83, 0x3c, 0xba, 0x18, 0xe4, 0xd1, 0x1f, 0x83, 0x3c, 0xfa, 0x7b, 0x90, + 0x47, 0x2d, 0x4d, 0xde, 0xa5, 0x1f, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0xdb, 0x8f, 0x76, 0x01, + 0xf6, 0x09, 0x00, 0x00, } diff --git a/api/dispatcher.proto b/api/dispatcher.proto index ffa2464a48..3045ca6745 100644 --- a/api/dispatcher.proto +++ b/api/dispatcher.proto @@ -126,6 +126,9 @@ message SessionMessage { // Symmetric encryption key distributed by the lead manager. Used by agents // for securing network bootstrapping and communication. repeated EncryptionKey network_bootstrap_keys = 4; + + // Which root certificates to trust + bytes RootCA = 5; } // HeartbeatRequest provides identifying properties for a single heartbeat. diff --git a/api/types.pb.go b/api/types.pb.go index 271c1794a0..958d73f091 100644 --- a/api/types.pb.go +++ b/api/types.pb.go @@ -29,6 +29,7 @@ PluginDescription EngineDescription NodeDescription + NodeTLSInfo RaftMemberStatus NodeStatus Image @@ -336,7 +337,7 @@ func (x RaftMemberStatus_Reachability) String() string { return proto.EnumName(RaftMemberStatus_Reachability_name, int32(x)) } func (RaftMemberStatus_Reachability) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{9, 0} + return fileDescriptorTypes, []int{10, 0} } // TODO(aluzzardi) These should be using `gogoproto.enumvalue_customname`. @@ -369,7 +370,7 @@ var NodeStatus_State_value = map[string]int32{ func (x NodeStatus_State) String() string { return proto.EnumName(NodeStatus_State_name, int32(x)) } -func (NodeStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{10, 0} } +func (NodeStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{11, 0} } type Mount_MountType int32 @@ -393,7 +394,7 @@ var Mount_MountType_value = map[string]int32{ func (x Mount_MountType) String() string { return proto.EnumName(Mount_MountType_name, int32(x)) } -func (Mount_MountType) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12, 0} } +func (Mount_MountType) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13, 0} } type Mount_BindOptions_MountPropagation int32 @@ -427,7 +428,7 @@ func (x Mount_BindOptions_MountPropagation) String() string { return proto.EnumName(Mount_BindOptions_MountPropagation_name, int32(x)) } func (Mount_BindOptions_MountPropagation) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{12, 0, 0} + return fileDescriptorTypes, []int{13, 0, 0} } type RestartPolicy_RestartCondition int32 @@ -453,7 +454,7 @@ func (x RestartPolicy_RestartCondition) String() string { return proto.EnumName(RestartPolicy_RestartCondition_name, int32(x)) } func (RestartPolicy_RestartCondition) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{13, 0} + return fileDescriptorTypes, []int{14, 0} } type UpdateConfig_FailureAction int32 @@ -479,7 +480,7 @@ func (x UpdateConfig_FailureAction) String() string { return proto.EnumName(UpdateConfig_FailureAction_name, int32(x)) } func (UpdateConfig_FailureAction) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{14, 0} + return fileDescriptorTypes, []int{15, 0} } // UpdateOrder controls the order of operations when rolling out an @@ -506,7 +507,7 @@ func (x UpdateConfig_UpdateOrder) String() string { return proto.EnumName(UpdateConfig_UpdateOrder_name, int32(x)) } func (UpdateConfig_UpdateOrder) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{14, 1} + return fileDescriptorTypes, []int{15, 1} } type UpdateStatus_UpdateState int32 @@ -544,7 +545,7 @@ func (x UpdateStatus_UpdateState) String() string { return proto.EnumName(UpdateStatus_UpdateState_name, int32(x)) } func (UpdateStatus_UpdateState) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{15, 0} + return fileDescriptorTypes, []int{16, 0} } // AddressFamily specifies the network address family that @@ -572,7 +573,7 @@ func (x IPAMConfig_AddressFamily) String() string { return proto.EnumName(IPAMConfig_AddressFamily_name, int32(x)) } func (IPAMConfig_AddressFamily) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{20, 0} + return fileDescriptorTypes, []int{21, 0} } type PortConfig_Protocol int32 @@ -594,7 +595,7 @@ var PortConfig_Protocol_value = map[string]int32{ func (x PortConfig_Protocol) String() string { return proto.EnumName(PortConfig_Protocol_name, int32(x)) } -func (PortConfig_Protocol) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21, 0} } +func (PortConfig_Protocol) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22, 0} } // PublishMode controls how ports are published on the swarm. type PortConfig_PublishMode int32 @@ -622,7 +623,7 @@ func (x PortConfig_PublishMode) String() string { return proto.EnumName(PortConfig_PublishMode_name, int32(x)) } func (PortConfig_PublishMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{21, 1} + return fileDescriptorTypes, []int{22, 1} } type IssuanceStatus_State int32 @@ -662,7 +663,7 @@ var IssuanceStatus_State_value = map[string]int32{ func (x IssuanceStatus_State) String() string { return proto.EnumName(IssuanceStatus_State_name, int32(x)) } -func (IssuanceStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26, 0} } +func (IssuanceStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27, 0} } type ExternalCA_CAProtocol int32 @@ -681,7 +682,7 @@ func (x ExternalCA_CAProtocol) String() string { return proto.EnumName(ExternalCA_CAProtocol_name, int32(x)) } func (ExternalCA_CAProtocol) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{28, 0} + return fileDescriptorTypes, []int{29, 0} } // Encryption algorithm that can implemented using this key @@ -702,7 +703,7 @@ func (x EncryptionKey_Algorithm) String() string { return proto.EnumName(EncryptionKey_Algorithm_name, int32(x)) } func (EncryptionKey_Algorithm) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{41, 0} + return fileDescriptorTypes, []int{42, 0} } type MaybeEncryptedRecord_Algorithm int32 @@ -725,7 +726,7 @@ func (x MaybeEncryptedRecord_Algorithm) String() string { return proto.EnumName(MaybeEncryptedRecord_Algorithm_name, int32(x)) } func (MaybeEncryptedRecord_Algorithm) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{46, 0} + return fileDescriptorTypes, []int{47, 0} } // Version tracks the last time an object in the store was updated. @@ -828,12 +829,26 @@ type NodeDescription struct { Resources *Resources `protobuf:"bytes,3,opt,name=resources" json:"resources,omitempty"` // Information about the Docker Engine on the node. Engine *EngineDescription `protobuf:"bytes,4,opt,name=engine" json:"engine,omitempty"` + // Information on the node's TLS setup + TLSInfo *NodeTLSInfo `protobuf:"bytes,5,opt,name=tls_info,json=tlsInfo" json:"tls_info,omitempty"` } func (m *NodeDescription) Reset() { *m = NodeDescription{} } func (*NodeDescription) ProtoMessage() {} func (*NodeDescription) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{8} } +type NodeTLSInfo struct { + // Information about which root certs the node trusts + TrustRoot []byte `protobuf:"bytes,1,opt,name=trust_root,json=trustRoot,proto3" json:"trust_root,omitempty"` + // Information about the node's current TLS certificate + CertIssuerSubject []byte `protobuf:"bytes,2,opt,name=cert_issuer_subject,json=certIssuerSubject,proto3" json:"cert_issuer_subject,omitempty"` + CertIssuerPublicKey []byte `protobuf:"bytes,3,opt,name=cert_issuer_public_key,json=certIssuerPublicKey,proto3" json:"cert_issuer_public_key,omitempty"` +} + +func (m *NodeTLSInfo) Reset() { *m = NodeTLSInfo{} } +func (*NodeTLSInfo) ProtoMessage() {} +func (*NodeTLSInfo) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{9} } + type RaftMemberStatus struct { Leader bool `protobuf:"varint,1,opt,name=leader,proto3" json:"leader,omitempty"` Reachability RaftMemberStatus_Reachability `protobuf:"varint,2,opt,name=reachability,proto3,enum=docker.swarmkit.v1.RaftMemberStatus_Reachability" json:"reachability,omitempty"` @@ -842,7 +857,7 @@ type RaftMemberStatus struct { func (m *RaftMemberStatus) Reset() { *m = RaftMemberStatus{} } func (*RaftMemberStatus) ProtoMessage() {} -func (*RaftMemberStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{9} } +func (*RaftMemberStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{10} } type NodeStatus struct { State NodeStatus_State `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.NodeStatus_State" json:"state,omitempty"` @@ -853,7 +868,7 @@ type NodeStatus struct { func (m *NodeStatus) Reset() { *m = NodeStatus{} } func (*NodeStatus) ProtoMessage() {} -func (*NodeStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{10} } +func (*NodeStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{11} } type Image struct { // reference is a docker image reference. This can include a rpository, tag @@ -864,7 +879,7 @@ type Image struct { func (m *Image) Reset() { *m = Image{} } func (*Image) ProtoMessage() {} -func (*Image) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{11} } +func (*Image) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12} } // Mount describes volume mounts for a container. // @@ -899,7 +914,7 @@ type Mount struct { func (m *Mount) Reset() { *m = Mount{} } func (*Mount) ProtoMessage() {} -func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12} } +func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13} } // BindOptions specifies options that are specific to a bind mount. type Mount_BindOptions struct { @@ -909,7 +924,7 @@ type Mount_BindOptions struct { func (m *Mount_BindOptions) Reset() { *m = Mount_BindOptions{} } func (*Mount_BindOptions) ProtoMessage() {} -func (*Mount_BindOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12, 0} } +func (*Mount_BindOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13, 0} } // VolumeOptions contains parameters for mounting the volume. type Mount_VolumeOptions struct { @@ -926,7 +941,7 @@ type Mount_VolumeOptions struct { func (m *Mount_VolumeOptions) Reset() { *m = Mount_VolumeOptions{} } func (*Mount_VolumeOptions) ProtoMessage() {} -func (*Mount_VolumeOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12, 1} } +func (*Mount_VolumeOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13, 1} } type Mount_TmpfsOptions struct { // Size sets the size of the tmpfs, in bytes. @@ -944,7 +959,7 @@ type Mount_TmpfsOptions struct { func (m *Mount_TmpfsOptions) Reset() { *m = Mount_TmpfsOptions{} } func (*Mount_TmpfsOptions) ProtoMessage() {} -func (*Mount_TmpfsOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12, 2} } +func (*Mount_TmpfsOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13, 2} } type RestartPolicy struct { Condition RestartPolicy_RestartCondition `protobuf:"varint,1,opt,name=condition,proto3,enum=docker.swarmkit.v1.RestartPolicy_RestartCondition" json:"condition,omitempty"` @@ -962,7 +977,7 @@ type RestartPolicy struct { func (m *RestartPolicy) Reset() { *m = RestartPolicy{} } func (*RestartPolicy) ProtoMessage() {} -func (*RestartPolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13} } +func (*RestartPolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{14} } // UpdateConfig specifies the rate and policy of updates. // TODO(aluzzardi): Consider making this a oneof with RollingStrategy and LockstepStrategy. @@ -1002,7 +1017,7 @@ type UpdateConfig struct { func (m *UpdateConfig) Reset() { *m = UpdateConfig{} } func (*UpdateConfig) ProtoMessage() {} -func (*UpdateConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{14} } +func (*UpdateConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{15} } // UpdateStatus is the status of an update in progress. type UpdateStatus struct { @@ -1026,7 +1041,7 @@ type UpdateStatus struct { func (m *UpdateStatus) Reset() { *m = UpdateStatus{} } func (*UpdateStatus) ProtoMessage() {} -func (*UpdateStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{15} } +func (*UpdateStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16} } // Container specific status. type ContainerStatus struct { @@ -1037,7 +1052,7 @@ type ContainerStatus struct { func (m *ContainerStatus) Reset() { *m = ContainerStatus{} } func (*ContainerStatus) ProtoMessage() {} -func (*ContainerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16} } +func (*ContainerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{17} } // PortStatus specifies the actual allocated runtime state of a list // of port configs. @@ -1047,7 +1062,7 @@ type PortStatus struct { func (m *PortStatus) Reset() { *m = PortStatus{} } func (*PortStatus) ProtoMessage() {} -func (*PortStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{17} } +func (*PortStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{18} } type TaskStatus struct { // Note: can't use stdtime because this field is nullable. @@ -1084,7 +1099,7 @@ type TaskStatus struct { func (m *TaskStatus) Reset() { *m = TaskStatus{} } func (*TaskStatus) ProtoMessage() {} -func (*TaskStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{18} } +func (*TaskStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{19} } type isTaskStatus_RuntimeStatus interface { isTaskStatus_RuntimeStatus() @@ -1186,7 +1201,7 @@ type NetworkAttachmentConfig struct { func (m *NetworkAttachmentConfig) Reset() { *m = NetworkAttachmentConfig{} } func (*NetworkAttachmentConfig) ProtoMessage() {} -func (*NetworkAttachmentConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{19} } +func (*NetworkAttachmentConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{20} } // IPAMConfig specifies parameters for IP Address Management. type IPAMConfig struct { @@ -1207,7 +1222,7 @@ type IPAMConfig struct { func (m *IPAMConfig) Reset() { *m = IPAMConfig{} } func (*IPAMConfig) ProtoMessage() {} -func (*IPAMConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{20} } +func (*IPAMConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21} } // PortConfig specifies an exposed port which can be // addressed using the given name. This can be later queried @@ -1233,7 +1248,7 @@ type PortConfig struct { func (m *PortConfig) Reset() { *m = PortConfig{} } func (*PortConfig) ProtoMessage() {} -func (*PortConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21} } +func (*PortConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22} } // Driver is a generic driver type to be used throughout the API. For now, a // driver is simply a name and set of options. The field contents depend on the @@ -1246,7 +1261,7 @@ type Driver struct { func (m *Driver) Reset() { *m = Driver{} } func (*Driver) ProtoMessage() {} -func (*Driver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22} } +func (*Driver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{23} } type IPAMOptions struct { Driver *Driver `protobuf:"bytes,1,opt,name=driver" json:"driver,omitempty"` @@ -1255,7 +1270,7 @@ type IPAMOptions struct { func (m *IPAMOptions) Reset() { *m = IPAMOptions{} } func (*IPAMOptions) ProtoMessage() {} -func (*IPAMOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{23} } +func (*IPAMOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} } // Peer should be used anywhere where we are describing a remote peer. type Peer struct { @@ -1265,7 +1280,7 @@ type Peer struct { func (m *Peer) Reset() { *m = Peer{} } func (*Peer) ProtoMessage() {} -func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} } +func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} } // WeightedPeer should be used anywhere where we are describing a remote peer // with a weight. @@ -1276,7 +1291,7 @@ type WeightedPeer struct { func (m *WeightedPeer) Reset() { *m = WeightedPeer{} } func (*WeightedPeer) ProtoMessage() {} -func (*WeightedPeer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} } +func (*WeightedPeer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} } type IssuanceStatus struct { State IssuanceStatus_State `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.IssuanceStatus_State" json:"state,omitempty"` @@ -1288,7 +1303,7 @@ type IssuanceStatus struct { func (m *IssuanceStatus) Reset() { *m = IssuanceStatus{} } func (*IssuanceStatus) ProtoMessage() {} -func (*IssuanceStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} } +func (*IssuanceStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} } type AcceptancePolicy struct { Policies []*AcceptancePolicy_RoleAdmissionPolicy `protobuf:"bytes,1,rep,name=policies" json:"policies,omitempty"` @@ -1296,7 +1311,7 @@ type AcceptancePolicy struct { func (m *AcceptancePolicy) Reset() { *m = AcceptancePolicy{} } func (*AcceptancePolicy) ProtoMessage() {} -func (*AcceptancePolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} } +func (*AcceptancePolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} } type AcceptancePolicy_RoleAdmissionPolicy struct { Role NodeRole `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"` @@ -1311,7 +1326,7 @@ type AcceptancePolicy_RoleAdmissionPolicy struct { func (m *AcceptancePolicy_RoleAdmissionPolicy) Reset() { *m = AcceptancePolicy_RoleAdmissionPolicy{} } func (*AcceptancePolicy_RoleAdmissionPolicy) ProtoMessage() {} func (*AcceptancePolicy_RoleAdmissionPolicy) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{27, 0} + return fileDescriptorTypes, []int{28, 0} } type AcceptancePolicy_RoleAdmissionPolicy_Secret struct { @@ -1326,7 +1341,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Reset() { } func (*AcceptancePolicy_RoleAdmissionPolicy_Secret) ProtoMessage() {} func (*AcceptancePolicy_RoleAdmissionPolicy_Secret) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{27, 0, 0} + return fileDescriptorTypes, []int{28, 0, 0} } type ExternalCA struct { @@ -1343,7 +1358,7 @@ type ExternalCA struct { func (m *ExternalCA) Reset() { *m = ExternalCA{} } func (*ExternalCA) ProtoMessage() {} -func (*ExternalCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} } +func (*ExternalCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} } type CAConfig struct { // NodeCertExpiry is the duration certificates should be issued for @@ -1368,7 +1383,7 @@ type CAConfig struct { func (m *CAConfig) Reset() { *m = CAConfig{} } func (*CAConfig) ProtoMessage() {} -func (*CAConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} } +func (*CAConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} } // OrchestrationConfig defines cluster-level orchestration settings. type OrchestrationConfig struct { @@ -1379,7 +1394,7 @@ type OrchestrationConfig struct { func (m *OrchestrationConfig) Reset() { *m = OrchestrationConfig{} } func (*OrchestrationConfig) ProtoMessage() {} -func (*OrchestrationConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} } +func (*OrchestrationConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} } // TaskDefaults specifies default values for task creation. type TaskDefaults struct { @@ -1393,7 +1408,7 @@ type TaskDefaults struct { func (m *TaskDefaults) Reset() { *m = TaskDefaults{} } func (*TaskDefaults) ProtoMessage() {} -func (*TaskDefaults) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} } +func (*TaskDefaults) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} } // DispatcherConfig defines cluster-level dispatcher settings. type DispatcherConfig struct { @@ -1405,7 +1420,7 @@ type DispatcherConfig struct { func (m *DispatcherConfig) Reset() { *m = DispatcherConfig{} } func (*DispatcherConfig) ProtoMessage() {} -func (*DispatcherConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} } +func (*DispatcherConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{33} } // RaftConfig defines raft settings for the cluster. type RaftConfig struct { @@ -1427,7 +1442,7 @@ type RaftConfig struct { func (m *RaftConfig) Reset() { *m = RaftConfig{} } func (*RaftConfig) ProtoMessage() {} -func (*RaftConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{33} } +func (*RaftConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{34} } type EncryptionConfig struct { // AutoLockManagers specifies whether or not managers TLS keys and raft data @@ -1438,7 +1453,7 @@ type EncryptionConfig struct { func (m *EncryptionConfig) Reset() { *m = EncryptionConfig{} } func (*EncryptionConfig) ProtoMessage() {} -func (*EncryptionConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{34} } +func (*EncryptionConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{35} } type SpreadOver struct { SpreadDescriptor string `protobuf:"bytes,1,opt,name=spread_descriptor,json=spreadDescriptor,proto3" json:"spread_descriptor,omitempty"` @@ -1446,7 +1461,7 @@ type SpreadOver struct { func (m *SpreadOver) Reset() { *m = SpreadOver{} } func (*SpreadOver) ProtoMessage() {} -func (*SpreadOver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{35} } +func (*SpreadOver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{36} } type PlacementPreference struct { // Types that are valid to be assigned to Preference: @@ -1456,7 +1471,7 @@ type PlacementPreference struct { func (m *PlacementPreference) Reset() { *m = PlacementPreference{} } func (*PlacementPreference) ProtoMessage() {} -func (*PlacementPreference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{36} } +func (*PlacementPreference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{37} } type isPlacementPreference_Preference interface { isPlacementPreference_Preference() @@ -1551,7 +1566,7 @@ type Placement struct { func (m *Placement) Reset() { *m = Placement{} } func (*Placement) ProtoMessage() {} -func (*Placement) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{37} } +func (*Placement) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{38} } // JoinToken contains the join tokens for workers and managers. type JoinTokens struct { @@ -1563,7 +1578,7 @@ type JoinTokens struct { func (m *JoinTokens) Reset() { *m = JoinTokens{} } func (*JoinTokens) ProtoMessage() {} -func (*JoinTokens) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{38} } +func (*JoinTokens) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{39} } type RootCA struct { // CAKey is the root CA private key. @@ -1584,7 +1599,7 @@ type RootCA struct { func (m *RootCA) Reset() { *m = RootCA{} } func (*RootCA) ProtoMessage() {} -func (*RootCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{39} } +func (*RootCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{40} } type Certificate struct { Role NodeRole `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"` @@ -1597,7 +1612,7 @@ type Certificate struct { func (m *Certificate) Reset() { *m = Certificate{} } func (*Certificate) ProtoMessage() {} -func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{40} } +func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{41} } // Symmetric keys to encrypt inter-agent communication. type EncryptionKey struct { @@ -1613,7 +1628,7 @@ type EncryptionKey struct { func (m *EncryptionKey) Reset() { *m = EncryptionKey{} } func (*EncryptionKey) ProtoMessage() {} -func (*EncryptionKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{41} } +func (*EncryptionKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{42} } // ManagerStatus provides informations about the state of a manager in the cluster. type ManagerStatus struct { @@ -1630,7 +1645,7 @@ type ManagerStatus struct { func (m *ManagerStatus) Reset() { *m = ManagerStatus{} } func (*ManagerStatus) ProtoMessage() {} -func (*ManagerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{42} } +func (*ManagerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{43} } // SecretReference is the linkage between a service and a secret that it uses. type SecretReference struct { @@ -1650,7 +1665,7 @@ type SecretReference struct { func (m *SecretReference) Reset() { *m = SecretReference{} } func (*SecretReference) ProtoMessage() {} -func (*SecretReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{43} } +func (*SecretReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{44} } type isSecretReference_Target interface { isSecretReference_Target() @@ -1748,7 +1763,7 @@ type SecretReference_FileTarget struct { func (m *SecretReference_FileTarget) Reset() { *m = SecretReference_FileTarget{} } func (*SecretReference_FileTarget) ProtoMessage() {} func (*SecretReference_FileTarget) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{43, 0} + return fileDescriptorTypes, []int{44, 0} } // BlacklistedCertificate is a record for a blacklisted certificate. It does not @@ -1762,7 +1777,7 @@ type BlacklistedCertificate struct { func (m *BlacklistedCertificate) Reset() { *m = BlacklistedCertificate{} } func (*BlacklistedCertificate) ProtoMessage() {} -func (*BlacklistedCertificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{44} } +func (*BlacklistedCertificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{45} } // HealthConfig holds configuration settings for the HEALTHCHECK feature. type HealthConfig struct { @@ -1792,7 +1807,7 @@ type HealthConfig struct { func (m *HealthConfig) Reset() { *m = HealthConfig{} } func (*HealthConfig) ProtoMessage() {} -func (*HealthConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{45} } +func (*HealthConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{46} } type MaybeEncryptedRecord struct { Algorithm MaybeEncryptedRecord_Algorithm `protobuf:"varint,1,opt,name=algorithm,proto3,enum=docker.swarmkit.v1.MaybeEncryptedRecord_Algorithm" json:"algorithm,omitempty"` @@ -1802,7 +1817,7 @@ type MaybeEncryptedRecord struct { func (m *MaybeEncryptedRecord) Reset() { *m = MaybeEncryptedRecord{} } func (*MaybeEncryptedRecord) ProtoMessage() {} -func (*MaybeEncryptedRecord) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{46} } +func (*MaybeEncryptedRecord) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{47} } type RootRotation struct { CACert []byte `protobuf:"bytes,1,opt,name=ca_cert,json=caCert,proto3" json:"ca_cert,omitempty"` @@ -1813,7 +1828,7 @@ type RootRotation struct { func (m *RootRotation) Reset() { *m = RootRotation{} } func (*RootRotation) ProtoMessage() {} -func (*RootRotation) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{47} } +func (*RootRotation) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{48} } func init() { proto.RegisterType((*Version)(nil), "docker.swarmkit.v1.Version") @@ -1825,6 +1840,7 @@ func init() { proto.RegisterType((*PluginDescription)(nil), "docker.swarmkit.v1.PluginDescription") proto.RegisterType((*EngineDescription)(nil), "docker.swarmkit.v1.EngineDescription") proto.RegisterType((*NodeDescription)(nil), "docker.swarmkit.v1.NodeDescription") + proto.RegisterType((*NodeTLSInfo)(nil), "docker.swarmkit.v1.NodeTLSInfo") proto.RegisterType((*RaftMemberStatus)(nil), "docker.swarmkit.v1.RaftMemberStatus") proto.RegisterType((*NodeStatus)(nil), "docker.swarmkit.v1.NodeStatus") proto.RegisterType((*Image)(nil), "docker.swarmkit.v1.Image") @@ -2070,6 +2086,37 @@ func (m *NodeDescription) CopyFrom(src interface{}) { m.Engine = &EngineDescription{} github_com_docker_swarmkit_api_deepcopy.Copy(m.Engine, o.Engine) } + if o.TLSInfo != nil { + m.TLSInfo = &NodeTLSInfo{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.TLSInfo, o.TLSInfo) + } +} + +func (m *NodeTLSInfo) Copy() *NodeTLSInfo { + if m == nil { + return nil + } + o := &NodeTLSInfo{} + o.CopyFrom(m) + return o +} + +func (m *NodeTLSInfo) CopyFrom(src interface{}) { + + o := src.(*NodeTLSInfo) + *m = *o + if o.TrustRoot != nil { + m.TrustRoot = make([]byte, len(o.TrustRoot)) + copy(m.TrustRoot, o.TrustRoot) + } + if o.CertIssuerSubject != nil { + m.CertIssuerSubject = make([]byte, len(o.CertIssuerSubject)) + copy(m.CertIssuerSubject, o.CertIssuerSubject) + } + if o.CertIssuerPublicKey != nil { + m.CertIssuerPublicKey = make([]byte, len(o.CertIssuerPublicKey)) + copy(m.CertIssuerPublicKey, o.CertIssuerPublicKey) + } } func (m *RaftMemberStatus) Copy() *RaftMemberStatus { @@ -3351,6 +3398,52 @@ func (m *NodeDescription) MarshalTo(dAtA []byte) (int, error) { } i += n5 } + if m.TLSInfo != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintTypes(dAtA, i, uint64(m.TLSInfo.Size())) + n6, err := m.TLSInfo.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n6 + } + return i, nil +} + +func (m *NodeTLSInfo) 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 *NodeTLSInfo) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.TrustRoot) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.TrustRoot))) + i += copy(dAtA[i:], m.TrustRoot) + } + if len(m.CertIssuerSubject) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.CertIssuerSubject))) + i += copy(dAtA[i:], m.CertIssuerSubject) + } + if len(m.CertIssuerPublicKey) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.CertIssuerPublicKey))) + i += copy(dAtA[i:], m.CertIssuerPublicKey) + } return i, nil } @@ -3498,31 +3591,31 @@ func (m *Mount) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.BindOptions.Size())) - n6, err := m.BindOptions.MarshalTo(dAtA[i:]) + n7, err := m.BindOptions.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n6 + i += n7 } if m.VolumeOptions != nil { dAtA[i] = 0x32 i++ i = encodeVarintTypes(dAtA, i, uint64(m.VolumeOptions.Size())) - n7, err := m.VolumeOptions.MarshalTo(dAtA[i:]) + n8, err := m.VolumeOptions.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n7 + i += n8 } if m.TmpfsOptions != nil { dAtA[i] = 0x3a i++ i = encodeVarintTypes(dAtA, i, uint64(m.TmpfsOptions.Size())) - n8, err := m.TmpfsOptions.MarshalTo(dAtA[i:]) + n9, err := m.TmpfsOptions.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n8 + i += n9 } return i, nil } @@ -3596,11 +3689,11 @@ func (m *Mount_VolumeOptions) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.DriverConfig.Size())) - n9, err := m.DriverConfig.MarshalTo(dAtA[i:]) + n10, err := m.DriverConfig.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n9 + i += n10 } return i, nil } @@ -3657,11 +3750,11 @@ func (m *RestartPolicy) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Delay.Size())) - n10, err := m.Delay.MarshalTo(dAtA[i:]) + n11, err := m.Delay.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n10 + i += n11 } if m.MaxAttempts != 0 { dAtA[i] = 0x18 @@ -3672,11 +3765,11 @@ func (m *RestartPolicy) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x22 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Window.Size())) - n11, err := m.Window.MarshalTo(dAtA[i:]) + n12, err := m.Window.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n11 + i += n12 } return i, nil } @@ -3704,11 +3797,11 @@ func (m *UpdateConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdDuration(m.Delay))) - n12, err := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Delay, dAtA[i:]) + n13, err := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Delay, dAtA[i:]) if err != nil { return 0, err } - i += n12 + i += n13 if m.FailureAction != 0 { dAtA[i] = 0x18 i++ @@ -3718,11 +3811,11 @@ func (m *UpdateConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x22 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Monitor.Size())) - n13, err := m.Monitor.MarshalTo(dAtA[i:]) + n14, err := m.Monitor.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n13 + i += n14 } if m.MaxFailureRatio != 0 { dAtA[i] = 0x2d @@ -3761,21 +3854,21 @@ func (m *UpdateStatus) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.StartedAt.Size())) - n14, err := m.StartedAt.MarshalTo(dAtA[i:]) + n15, err := m.StartedAt.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n14 + i += n15 } if m.CompletedAt != nil { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.CompletedAt.Size())) - n15, err := m.CompletedAt.MarshalTo(dAtA[i:]) + n16, err := m.CompletedAt.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n15 + i += n16 } if len(m.Message) > 0 { dAtA[i] = 0x22 @@ -3869,11 +3962,11 @@ func (m *TaskStatus) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Timestamp.Size())) - n16, err := m.Timestamp.MarshalTo(dAtA[i:]) + n17, err := m.Timestamp.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n16 + i += n17 } if m.State != 0 { dAtA[i] = 0x10 @@ -3893,21 +3986,21 @@ func (m *TaskStatus) MarshalTo(dAtA []byte) (int, error) { i += copy(dAtA[i:], m.Err) } if m.RuntimeStatus != nil { - nn17, err := m.RuntimeStatus.MarshalTo(dAtA[i:]) + nn18, err := m.RuntimeStatus.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += nn17 + i += nn18 } if m.PortStatus != nil { dAtA[i] = 0x32 i++ i = encodeVarintTypes(dAtA, i, uint64(m.PortStatus.Size())) - n18, err := m.PortStatus.MarshalTo(dAtA[i:]) + n19, err := m.PortStatus.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n18 + i += n19 } return i, nil } @@ -3918,11 +4011,11 @@ func (m *TaskStatus_Container) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Container.Size())) - n19, err := m.Container.MarshalTo(dAtA[i:]) + n20, err := m.Container.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n19 + i += n20 } return i, nil } @@ -4142,11 +4235,11 @@ func (m *IPAMOptions) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Driver.Size())) - n20, err := m.Driver.MarshalTo(dAtA[i:]) + n21, err := m.Driver.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n20 + i += n21 } if len(m.Configs) > 0 { for _, msg := range m.Configs { @@ -4212,11 +4305,11 @@ func (m *WeightedPeer) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Peer.Size())) - n21, err := m.Peer.MarshalTo(dAtA[i:]) + n22, err := m.Peer.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n21 + i += n22 } if m.Weight != 0 { dAtA[i] = 0x10 @@ -4319,11 +4412,11 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) MarshalTo(dAtA []byte) (int, erro dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Secret.Size())) - n22, err := m.Secret.MarshalTo(dAtA[i:]) + n23, err := m.Secret.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n22 + i += n23 } return i, nil } @@ -4429,11 +4522,11 @@ func (m *CAConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.NodeCertExpiry.Size())) - n23, err := m.NodeCertExpiry.MarshalTo(dAtA[i:]) + n24, err := m.NodeCertExpiry.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n23 + i += n24 } if len(m.ExternalCAs) > 0 { for _, msg := range m.ExternalCAs { @@ -4509,11 +4602,11 @@ func (m *TaskDefaults) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.LogDriver.Size())) - n24, err := m.LogDriver.MarshalTo(dAtA[i:]) + n25, err := m.LogDriver.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n24 + i += n25 } return i, nil } @@ -4537,11 +4630,11 @@ func (m *DispatcherConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.HeartbeatPeriod.Size())) - n25, err := m.HeartbeatPeriod.MarshalTo(dAtA[i:]) + n26, err := m.HeartbeatPeriod.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n25 + i += n26 } return i, nil } @@ -4657,11 +4750,11 @@ func (m *PlacementPreference) MarshalTo(dAtA []byte) (int, error) { var l int _ = l if m.Preference != nil { - nn26, err := m.Preference.MarshalTo(dAtA[i:]) + nn27, err := m.Preference.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += nn26 + i += nn27 } return i, nil } @@ -4672,11 +4765,11 @@ func (m *PlacementPreference_Spread) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Spread.Size())) - n27, err := m.Spread.MarshalTo(dAtA[i:]) + n28, err := m.Spread.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n27 + i += n28 } return i, nil } @@ -4791,20 +4884,20 @@ func (m *RootCA) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x22 i++ i = encodeVarintTypes(dAtA, i, uint64(m.JoinTokens.Size())) - n28, err := m.JoinTokens.MarshalTo(dAtA[i:]) + n29, err := m.JoinTokens.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n28 + i += n29 if m.RootRotation != nil { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.RootRotation.Size())) - n29, err := m.RootRotation.MarshalTo(dAtA[i:]) + n30, err := m.RootRotation.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n29 + i += n30 } if m.LastForcedRotation != 0 { dAtA[i] = 0x30 @@ -4843,11 +4936,11 @@ func (m *Certificate) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Status.Size())) - n30, err := m.Status.MarshalTo(dAtA[i:]) + n31, err := m.Status.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n30 + i += n31 if len(m.Certificate) > 0 { dAtA[i] = 0x22 i++ @@ -4975,11 +5068,11 @@ func (m *SecretReference) MarshalTo(dAtA []byte) (int, error) { i += copy(dAtA[i:], m.SecretName) } if m.Target != nil { - nn31, err := m.Target.MarshalTo(dAtA[i:]) + nn32, err := m.Target.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += nn31 + i += nn32 } return i, nil } @@ -4990,11 +5083,11 @@ func (m *SecretReference_File) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.File.Size())) - n32, err := m.File.MarshalTo(dAtA[i:]) + n33, err := m.File.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n32 + i += n33 } return i, nil } @@ -5058,11 +5151,11 @@ func (m *BlacklistedCertificate) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Expiry.Size())) - n33, err := m.Expiry.MarshalTo(dAtA[i:]) + n34, err := m.Expiry.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n33 + i += n34 } return i, nil } @@ -5101,21 +5194,21 @@ func (m *HealthConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Interval.Size())) - n34, err := m.Interval.MarshalTo(dAtA[i:]) + n35, err := m.Interval.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n34 + i += n35 } if m.Timeout != nil { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Timeout.Size())) - n35, err := m.Timeout.MarshalTo(dAtA[i:]) + n36, err := m.Timeout.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n35 + i += n36 } if m.Retries != 0 { dAtA[i] = 0x20 @@ -5126,11 +5219,11 @@ func (m *HealthConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.StartPeriod.Size())) - n36, err := m.StartPeriod.MarshalTo(dAtA[i:]) + n37, err := m.StartPeriod.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n36 + i += n37 } return i, nil } @@ -5378,6 +5471,28 @@ func (m *NodeDescription) Size() (n int) { l = m.Engine.Size() n += 1 + l + sovTypes(uint64(l)) } + if m.TLSInfo != nil { + l = m.TLSInfo.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + +func (m *NodeTLSInfo) Size() (n int) { + var l int + _ = l + l = len(m.TrustRoot) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.CertIssuerSubject) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.CertIssuerPublicKey) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -6327,6 +6442,19 @@ func (this *NodeDescription) String() string { `Platform:` + strings.Replace(fmt.Sprintf("%v", this.Platform), "Platform", "Platform", 1) + `,`, `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "Resources", "Resources", 1) + `,`, `Engine:` + strings.Replace(fmt.Sprintf("%v", this.Engine), "EngineDescription", "EngineDescription", 1) + `,`, + `TLSInfo:` + strings.Replace(fmt.Sprintf("%v", this.TLSInfo), "NodeTLSInfo", "NodeTLSInfo", 1) + `,`, + `}`, + }, "") + return s +} +func (this *NodeTLSInfo) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&NodeTLSInfo{`, + `TrustRoot:` + fmt.Sprintf("%v", this.TrustRoot) + `,`, + `CertIssuerSubject:` + fmt.Sprintf("%v", this.CertIssuerSubject) + `,`, + `CertIssuerPublicKey:` + fmt.Sprintf("%v", this.CertIssuerPublicKey) + `,`, `}`, }, "") return s @@ -8155,6 +8283,182 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TLSInfo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TLSInfo == nil { + m.TLSInfo = &NodeTLSInfo{} + } + if err := m.TLSInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NodeTLSInfo) 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 ErrIntOverflowTypes + } + 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: NodeTLSInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NodeTLSInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TrustRoot", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TrustRoot = append(m.TrustRoot[:0], dAtA[iNdEx:postIndex]...) + if m.TrustRoot == nil { + m.TrustRoot = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CertIssuerSubject", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CertIssuerSubject = append(m.CertIssuerSubject[:0], dAtA[iNdEx:postIndex]...) + if m.CertIssuerSubject == nil { + m.CertIssuerSubject = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CertIssuerPublicKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CertIssuerPublicKey = append(m.CertIssuerPublicKey[:0], dAtA[iNdEx:postIndex]...) + if m.CertIssuerPublicKey == nil { + m.CertIssuerPublicKey = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -14466,276 +14770,284 @@ var ( func init() { proto.RegisterFile("types.proto", fileDescriptorTypes) } var fileDescriptorTypes = []byte{ - // 4333 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x5a, 0x4f, 0x6c, 0x24, 0xc7, - 0x5a, 0xf7, 0xfc, 0xf5, 0xcc, 0x37, 0x63, 0xbb, 0xb7, 0xd6, 0xd9, 0x78, 0x27, 0x1b, 0x7b, 0xd2, - 0xc9, 0xbe, 0xfc, 0x79, 0xd1, 0x64, 0xd7, 0xfb, 0x12, 0x6d, 0x12, 0x5e, 0x92, 0xf9, 0xe7, 0xf5, - 0xbc, 0xb5, 0x67, 0x46, 0x35, 0xe3, 0xdd, 0x97, 0x03, 0xb4, 0xca, 0xdd, 0xe5, 0x71, 0xc7, 0x3d, - 0x5d, 0x43, 0x77, 0x8f, 0xbd, 0x03, 0x42, 0xac, 0x38, 0x00, 0xf2, 0x89, 0x23, 0x12, 0xf2, 0x09, - 0x4e, 0x1c, 0xb8, 0x70, 0x40, 0x70, 0x21, 0x48, 0x1c, 0x72, 0xe3, 0x01, 0x12, 0x7a, 0x02, 0xc9, - 0x10, 0x23, 0x71, 0x43, 0x70, 0x79, 0xe2, 0x02, 0x12, 0xaa, 0x3f, 0xdd, 0xd3, 0xf6, 0x8e, 0xed, - 0x0d, 0xe1, 0x62, 0x77, 0x7d, 0xf5, 0xfb, 0xbe, 0xaa, 0xfa, 0xea, 0xab, 0xaa, 0xdf, 0x57, 0x35, - 0x50, 0x08, 0x26, 0x23, 0xea, 0x57, 0x46, 0x1e, 0x0b, 0x18, 0x42, 0x16, 0x33, 0x0f, 0xa8, 0x57, - 0xf1, 0x8f, 0x88, 0x37, 0x3c, 0xb0, 0x83, 0xca, 0xe1, 0xfd, 0xd2, 0xda, 0x80, 0xb1, 0x81, 0x43, - 0x3f, 0x10, 0x88, 0xdd, 0xf1, 0xde, 0x07, 0x81, 0x3d, 0xa4, 0x7e, 0x40, 0x86, 0x23, 0xa9, 0x54, - 0x5a, 0xbd, 0x08, 0xb0, 0xc6, 0x1e, 0x09, 0x6c, 0xe6, 0xaa, 0xfa, 0xe5, 0x01, 0x1b, 0x30, 0xf1, - 0xf9, 0x01, 0xff, 0x92, 0x52, 0x7d, 0x0d, 0xe6, 0x9f, 0x50, 0xcf, 0xb7, 0x99, 0x8b, 0x96, 0x21, - 0x63, 0xbb, 0x16, 0x7d, 0xb6, 0x92, 0x28, 0x27, 0xde, 0x49, 0x63, 0x59, 0xd0, 0xef, 0x01, 0xb4, - 0xf8, 0x47, 0xd3, 0x0d, 0xbc, 0x09, 0xd2, 0x20, 0x75, 0x40, 0x27, 0x02, 0x91, 0xc7, 0xfc, 0x93, - 0x4b, 0x0e, 0x89, 0xb3, 0x92, 0x94, 0x92, 0x43, 0xe2, 0xe8, 0xdf, 0x26, 0xa0, 0x50, 0x75, 0x5d, - 0x16, 0x88, 0xd6, 0x7d, 0x84, 0x20, 0xed, 0x92, 0x21, 0x55, 0x4a, 0xe2, 0x1b, 0xd5, 0x21, 0xeb, - 0x90, 0x5d, 0xea, 0xf8, 0x2b, 0xc9, 0x72, 0xea, 0x9d, 0xc2, 0xfa, 0x0f, 0x2b, 0x2f, 0x0e, 0xb9, - 0x12, 0x33, 0x52, 0xd9, 0x12, 0x68, 0xd1, 0x09, 0xac, 0x54, 0xd1, 0x67, 0x30, 0x6f, 0xbb, 0x96, - 0x6d, 0x52, 0x7f, 0x25, 0x2d, 0xac, 0xac, 0xce, 0xb2, 0x32, 0xed, 0x7d, 0x2d, 0xfd, 0xcd, 0xe9, - 0xda, 0x1c, 0x0e, 0x95, 0x4a, 0x1f, 0x43, 0x21, 0x66, 0x76, 0xc6, 0xd8, 0x96, 0x21, 0x73, 0x48, - 0x9c, 0x31, 0x55, 0xa3, 0x93, 0x85, 0x4f, 0x92, 0x0f, 0x13, 0xfa, 0x97, 0x90, 0xc7, 0xd4, 0x67, - 0x63, 0xcf, 0xa4, 0x3e, 0x7a, 0x17, 0xf2, 0x2e, 0x71, 0x99, 0x61, 0x8e, 0xc6, 0xbe, 0x50, 0x4f, - 0xd5, 0x8a, 0x67, 0xa7, 0x6b, 0xb9, 0x36, 0x71, 0x59, 0xbd, 0xbb, 0xe3, 0xe3, 0x1c, 0xaf, 0xae, - 0x8f, 0xc6, 0x3e, 0x7a, 0x03, 0x8a, 0x43, 0x3a, 0x64, 0xde, 0xc4, 0xd8, 0x9d, 0x04, 0xd4, 0x17, - 0x86, 0x53, 0xb8, 0x20, 0x65, 0x35, 0x2e, 0xd2, 0x7f, 0x2f, 0x01, 0xcb, 0xa1, 0x6d, 0x4c, 0x7f, - 0x75, 0x6c, 0x7b, 0x74, 0x48, 0xdd, 0xc0, 0x47, 0x1f, 0x42, 0xd6, 0xb1, 0x87, 0x76, 0x20, 0xdb, - 0x28, 0xac, 0xbf, 0x3e, 0x6b, 0xb4, 0x51, 0xaf, 0xb0, 0x02, 0xa3, 0x2a, 0x14, 0x3d, 0xea, 0x53, - 0xef, 0x50, 0x7a, 0x52, 0x34, 0x79, 0xad, 0xf2, 0x39, 0x15, 0x7d, 0x03, 0x72, 0x5d, 0x87, 0x04, - 0x7b, 0xcc, 0x1b, 0x22, 0x1d, 0x8a, 0xc4, 0x33, 0xf7, 0xed, 0x80, 0x9a, 0xc1, 0xd8, 0x0b, 0x67, - 0xf5, 0x9c, 0x0c, 0xdd, 0x82, 0x24, 0x93, 0x0d, 0xe5, 0x6b, 0xd9, 0xb3, 0xd3, 0xb5, 0x64, 0xa7, - 0x87, 0x93, 0xcc, 0xd7, 0x3f, 0x85, 0x1b, 0x5d, 0x67, 0x3c, 0xb0, 0xdd, 0x06, 0xf5, 0x4d, 0xcf, - 0x1e, 0x71, 0xeb, 0x3c, 0x3c, 0x78, 0xec, 0x87, 0xe1, 0xc1, 0xbf, 0xa3, 0x90, 0x49, 0x4e, 0x43, - 0x46, 0xff, 0x9d, 0x24, 0xdc, 0x68, 0xba, 0x03, 0xdb, 0xa5, 0x71, 0xed, 0xbb, 0xb0, 0x48, 0x85, - 0xd0, 0x38, 0x94, 0x61, 0xac, 0xec, 0x2c, 0x48, 0x69, 0x18, 0xdb, 0xad, 0x0b, 0xf1, 0x76, 0x7f, - 0xd6, 0xf0, 0x5f, 0xb0, 0x3e, 0x33, 0xea, 0x9a, 0x30, 0x3f, 0x12, 0x83, 0xf0, 0x57, 0x52, 0xc2, - 0xd6, 0xdd, 0x59, 0xb6, 0x5e, 0x18, 0x67, 0x18, 0x7c, 0x4a, 0xf7, 0xfb, 0x04, 0xdf, 0xbf, 0x26, - 0x60, 0xa9, 0xcd, 0xac, 0x73, 0x7e, 0x28, 0x41, 0x6e, 0x9f, 0xf9, 0x41, 0x6c, 0xa1, 0x45, 0x65, - 0xf4, 0x10, 0x72, 0x23, 0x35, 0x7d, 0x6a, 0xf6, 0xef, 0xcc, 0xee, 0xb2, 0xc4, 0xe0, 0x08, 0x8d, - 0x3e, 0x85, 0xbc, 0x17, 0xc6, 0xc4, 0x4a, 0xea, 0x65, 0x02, 0x67, 0x8a, 0x47, 0x3f, 0x86, 0xac, - 0x9c, 0x84, 0x95, 0xb4, 0xd0, 0xbc, 0xfb, 0x52, 0x3e, 0xc7, 0x4a, 0x49, 0xff, 0x79, 0x02, 0x34, - 0x4c, 0xf6, 0x82, 0x6d, 0x3a, 0xdc, 0xa5, 0x5e, 0x2f, 0x20, 0xc1, 0xd8, 0x47, 0xb7, 0x20, 0xeb, - 0x50, 0x62, 0x51, 0x4f, 0x0c, 0x32, 0x87, 0x55, 0x09, 0xed, 0xf0, 0x20, 0x27, 0xe6, 0x3e, 0xd9, - 0xb5, 0x1d, 0x3b, 0x98, 0x88, 0x61, 0x2e, 0xce, 0x9e, 0xe5, 0x8b, 0x36, 0x2b, 0x38, 0xa6, 0x88, - 0xcf, 0x99, 0x41, 0x2b, 0x30, 0x3f, 0xa4, 0xbe, 0x4f, 0x06, 0x54, 0x8c, 0x3e, 0x8f, 0xc3, 0xa2, - 0xfe, 0x29, 0x14, 0xe3, 0x7a, 0xa8, 0x00, 0xf3, 0x3b, 0xed, 0xc7, 0xed, 0xce, 0xd3, 0xb6, 0x36, - 0x87, 0x96, 0xa0, 0xb0, 0xd3, 0xc6, 0xcd, 0x6a, 0x7d, 0xb3, 0x5a, 0xdb, 0x6a, 0x6a, 0x09, 0xb4, - 0x00, 0xf9, 0x69, 0x31, 0xa9, 0xff, 0x69, 0x02, 0x80, 0x4f, 0xa0, 0x1a, 0xd4, 0x27, 0x90, 0xf1, - 0x03, 0x12, 0xc8, 0x89, 0x5b, 0x5c, 0x7f, 0x6b, 0x56, 0xaf, 0xa7, 0xf0, 0x0a, 0xff, 0x47, 0xb1, - 0x54, 0x89, 0xf7, 0x30, 0x79, 0xae, 0x87, 0x7c, 0x0d, 0x11, 0xcb, 0xf2, 0x54, 0xc7, 0xc5, 0xb7, - 0xfe, 0x29, 0x64, 0x84, 0xf6, 0xf9, 0xee, 0xe6, 0x20, 0xdd, 0xe0, 0x5f, 0x09, 0x94, 0x87, 0x0c, - 0x6e, 0x56, 0x1b, 0x5f, 0x6a, 0x49, 0xa4, 0x41, 0xb1, 0xd1, 0xea, 0xd5, 0x3b, 0xed, 0x76, 0xb3, - 0xde, 0x6f, 0x36, 0xb4, 0x94, 0x7e, 0x17, 0x32, 0xad, 0x21, 0xb7, 0x7c, 0x87, 0x47, 0xc5, 0x1e, - 0xf5, 0xa8, 0x6b, 0x86, 0xc1, 0x36, 0x15, 0xe8, 0x3f, 0xcb, 0x43, 0x66, 0x9b, 0x8d, 0xdd, 0x00, - 0xad, 0xc7, 0x56, 0xf6, 0xe2, 0xec, 0xcd, 0x59, 0x00, 0x2b, 0xfd, 0xc9, 0x88, 0xaa, 0x95, 0x7f, - 0x0b, 0xb2, 0x32, 0x7e, 0xd4, 0x70, 0x54, 0x89, 0xcb, 0x03, 0xe2, 0x0d, 0x68, 0xa0, 0xc6, 0xa3, - 0x4a, 0xe8, 0x1d, 0xc8, 0x79, 0x94, 0x58, 0xcc, 0x75, 0x26, 0x22, 0xcc, 0x72, 0x72, 0xeb, 0xc5, - 0x94, 0x58, 0x1d, 0xd7, 0x99, 0xe0, 0xa8, 0x16, 0x6d, 0x42, 0x71, 0xd7, 0x76, 0x2d, 0x83, 0x8d, - 0xe4, 0x3e, 0x98, 0xb9, 0x3c, 0x28, 0x65, 0xaf, 0x6a, 0xb6, 0x6b, 0x75, 0x24, 0x18, 0x17, 0x76, - 0xa7, 0x05, 0xd4, 0x86, 0xc5, 0x43, 0xe6, 0x8c, 0x87, 0x34, 0xb2, 0x95, 0x15, 0xb6, 0xde, 0xbe, - 0xdc, 0xd6, 0x13, 0x81, 0x0f, 0xad, 0x2d, 0x1c, 0xc6, 0x8b, 0xe8, 0x31, 0x2c, 0x04, 0xc3, 0xd1, - 0x9e, 0x1f, 0x99, 0x9b, 0x17, 0xe6, 0x7e, 0x70, 0x85, 0xc3, 0x38, 0x3c, 0xb4, 0x56, 0x0c, 0x62, - 0xa5, 0xd2, 0x6f, 0xa5, 0xa0, 0x10, 0xeb, 0x39, 0xea, 0x41, 0x61, 0xe4, 0xb1, 0x11, 0x19, 0x88, - 0xbd, 0x5c, 0xcd, 0xc5, 0xfd, 0x97, 0x1a, 0x75, 0xa5, 0x3b, 0x55, 0xc4, 0x71, 0x2b, 0xfa, 0x49, - 0x12, 0x0a, 0xb1, 0x4a, 0xf4, 0x1e, 0xe4, 0x70, 0x17, 0xb7, 0x9e, 0x54, 0xfb, 0x4d, 0x6d, 0xae, - 0x74, 0xe7, 0xf8, 0xa4, 0xbc, 0x22, 0xac, 0xc5, 0x0d, 0x74, 0x3d, 0xfb, 0x90, 0x87, 0xde, 0x3b, - 0x30, 0x1f, 0x42, 0x13, 0xa5, 0xd7, 0x8e, 0x4f, 0xca, 0xaf, 0x5e, 0x84, 0xc6, 0x90, 0xb8, 0xb7, - 0x59, 0xc5, 0xcd, 0x86, 0x96, 0x9c, 0x8d, 0xc4, 0xbd, 0x7d, 0xe2, 0x51, 0x0b, 0xfd, 0x00, 0xb2, - 0x0a, 0x98, 0x2a, 0x95, 0x8e, 0x4f, 0xca, 0xb7, 0x2e, 0x02, 0xa7, 0x38, 0xdc, 0xdb, 0xaa, 0x3e, - 0x69, 0x6a, 0xe9, 0xd9, 0x38, 0xdc, 0x73, 0xc8, 0x21, 0x45, 0x6f, 0x41, 0x46, 0xc2, 0x32, 0xa5, - 0xdb, 0xc7, 0x27, 0xe5, 0x57, 0x5e, 0x30, 0xc7, 0x51, 0xa5, 0x95, 0xdf, 0xfd, 0xc3, 0xd5, 0xb9, - 0xbf, 0xf8, 0xa3, 0x55, 0xed, 0x62, 0x75, 0xe9, 0xbf, 0x13, 0xb0, 0x70, 0x6e, 0xca, 0x91, 0x0e, - 0x59, 0x97, 0x99, 0x6c, 0x24, 0xb7, 0xf8, 0x5c, 0x0d, 0xce, 0x4e, 0xd7, 0xb2, 0x6d, 0x56, 0x67, - 0xa3, 0x09, 0x56, 0x35, 0xe8, 0xf1, 0x85, 0x43, 0xea, 0xc1, 0x4b, 0xc6, 0xd3, 0xcc, 0x63, 0xea, - 0x73, 0x58, 0xb0, 0x3c, 0xfb, 0x90, 0x7a, 0x86, 0xc9, 0xdc, 0x3d, 0x7b, 0xa0, 0xb6, 0xef, 0xd2, - 0x2c, 0x9b, 0x0d, 0x01, 0xc4, 0x45, 0xa9, 0x50, 0x17, 0xf8, 0xef, 0x71, 0x40, 0x95, 0x9e, 0x40, - 0x31, 0x1e, 0xa1, 0xe8, 0x75, 0x00, 0xdf, 0xfe, 0x35, 0xaa, 0x38, 0x8f, 0x60, 0x48, 0x38, 0xcf, - 0x25, 0x82, 0xf1, 0xa0, 0xb7, 0x21, 0x3d, 0x64, 0x96, 0xb4, 0xb3, 0x50, 0xbb, 0xc9, 0xcf, 0xc9, - 0x7f, 0x3c, 0x5d, 0x2b, 0x30, 0xbf, 0xb2, 0x61, 0x3b, 0x74, 0x9b, 0x59, 0x14, 0x0b, 0x80, 0x7e, - 0x08, 0x69, 0xbe, 0x55, 0xa0, 0xd7, 0x20, 0x5d, 0x6b, 0xb5, 0x1b, 0xda, 0x5c, 0xe9, 0xc6, 0xf1, - 0x49, 0x79, 0x41, 0xb8, 0x84, 0x57, 0xf0, 0xd8, 0x45, 0x6b, 0x90, 0x7d, 0xd2, 0xd9, 0xda, 0xd9, - 0xe6, 0xe1, 0x75, 0xf3, 0xf8, 0xa4, 0xbc, 0x14, 0x55, 0x4b, 0xa7, 0xa1, 0xd7, 0x21, 0xd3, 0xdf, - 0xee, 0x6e, 0xf4, 0xb4, 0x64, 0x09, 0x1d, 0x9f, 0x94, 0x17, 0xa3, 0x7a, 0xd1, 0xe7, 0xd2, 0x0d, - 0x35, 0xab, 0xf9, 0x48, 0xae, 0xff, 0x22, 0x09, 0x0b, 0x98, 0x93, 0x6d, 0x2f, 0xe8, 0x32, 0xc7, - 0x36, 0x27, 0xa8, 0x0b, 0x79, 0x93, 0xb9, 0x96, 0x1d, 0x5b, 0x53, 0xeb, 0x97, 0x1c, 0x8c, 0x53, - 0xad, 0xb0, 0x54, 0x0f, 0x35, 0xf1, 0xd4, 0x08, 0xfa, 0x00, 0x32, 0x16, 0x75, 0xc8, 0x44, 0x9d, - 0xd0, 0xb7, 0x2b, 0x92, 0xce, 0x57, 0x42, 0x3a, 0x5f, 0x69, 0x28, 0x3a, 0x8f, 0x25, 0x4e, 0x50, - 0x49, 0xf2, 0xcc, 0x20, 0x41, 0x40, 0x87, 0xa3, 0x40, 0x1e, 0xcf, 0x69, 0x5c, 0x18, 0x92, 0x67, - 0x55, 0x25, 0x42, 0xf7, 0x21, 0x7b, 0x64, 0xbb, 0x16, 0x3b, 0x52, 0x27, 0xf0, 0x15, 0x46, 0x15, - 0x50, 0x3f, 0xe6, 0xa7, 0xee, 0x85, 0x6e, 0x72, 0x7f, 0xb7, 0x3b, 0xed, 0x66, 0xe8, 0x6f, 0x55, - 0xdf, 0x71, 0xdb, 0xcc, 0xe5, 0x6b, 0x05, 0x3a, 0x6d, 0x63, 0xa3, 0xda, 0xda, 0xda, 0xc1, 0xdc, - 0xe7, 0xcb, 0xc7, 0x27, 0x65, 0x2d, 0x82, 0x6c, 0x10, 0xdb, 0xe1, 0x94, 0xf0, 0x36, 0xa4, 0xaa, - 0xed, 0x2f, 0xb5, 0x64, 0x49, 0x3b, 0x3e, 0x29, 0x17, 0xa3, 0xea, 0xaa, 0x3b, 0x99, 0x2e, 0xa3, - 0x8b, 0xed, 0xea, 0x7f, 0x93, 0x82, 0xe2, 0xce, 0xc8, 0x22, 0x01, 0x95, 0x31, 0x89, 0xca, 0x50, - 0x18, 0x11, 0x8f, 0x38, 0x0e, 0x75, 0x6c, 0x7f, 0xa8, 0x12, 0x95, 0xb8, 0x08, 0x7d, 0xfc, 0xb2, - 0x6e, 0xac, 0xe5, 0x78, 0x9c, 0xfd, 0xfe, 0x3f, 0xaf, 0x25, 0x42, 0x87, 0xee, 0xc0, 0xe2, 0x9e, - 0xec, 0xad, 0x41, 0x4c, 0x31, 0xb1, 0x29, 0x31, 0xb1, 0x95, 0x59, 0x13, 0x1b, 0xef, 0x56, 0x45, - 0x0d, 0xb2, 0x2a, 0xb4, 0xf0, 0xc2, 0x5e, 0xbc, 0x88, 0x1e, 0xc0, 0xfc, 0x90, 0xb9, 0x76, 0xc0, - 0xbc, 0xeb, 0x67, 0x21, 0x44, 0xa2, 0xf7, 0xe0, 0x06, 0x9f, 0xdc, 0xb0, 0x3f, 0xa2, 0x5a, 0x9c, - 0x58, 0x49, 0xbc, 0x34, 0x24, 0xcf, 0x54, 0x83, 0x98, 0x8b, 0x51, 0x0d, 0x32, 0xcc, 0xe3, 0x94, - 0x28, 0x2b, 0xba, 0xfb, 0xfe, 0xb5, 0xdd, 0x95, 0x85, 0x0e, 0xd7, 0xc1, 0x52, 0x55, 0xff, 0x08, - 0x16, 0xce, 0x0d, 0x82, 0x33, 0x81, 0x6e, 0x75, 0xa7, 0xd7, 0xd4, 0xe6, 0x50, 0x11, 0x72, 0xf5, - 0x4e, 0xbb, 0xdf, 0x6a, 0xef, 0x70, 0x2a, 0x53, 0x84, 0x1c, 0xee, 0x6c, 0x6d, 0xd5, 0xaa, 0xf5, - 0xc7, 0x5a, 0x52, 0xaf, 0x40, 0x21, 0x66, 0x0d, 0x2d, 0x02, 0xf4, 0xfa, 0x9d, 0xae, 0xb1, 0xd1, - 0xc2, 0xbd, 0xbe, 0x24, 0x42, 0xbd, 0x7e, 0x15, 0xf7, 0x95, 0x20, 0xa1, 0xff, 0x47, 0x32, 0x9c, - 0x51, 0xc5, 0x7d, 0x6a, 0xe7, 0xb9, 0xcf, 0x15, 0x9d, 0x57, 0xec, 0x67, 0x5a, 0x88, 0x38, 0xd0, - 0xc7, 0x00, 0x22, 0x70, 0xa8, 0x65, 0x90, 0x40, 0x4d, 0x7c, 0xe9, 0x05, 0x27, 0xf7, 0xc3, 0x7c, - 0x19, 0xe7, 0x15, 0xba, 0x1a, 0xa0, 0x1f, 0x43, 0xd1, 0x64, 0xc3, 0x91, 0x43, 0x95, 0x72, 0xea, - 0x5a, 0xe5, 0x42, 0x84, 0xaf, 0x06, 0x71, 0xf6, 0x95, 0x3e, 0xcf, 0x0f, 0x7f, 0x3b, 0x11, 0x7a, - 0x66, 0x06, 0xe1, 0x2a, 0x42, 0x6e, 0xa7, 0xdb, 0xa8, 0xf6, 0x5b, 0xed, 0x47, 0x5a, 0x02, 0x01, - 0x64, 0x85, 0xab, 0x1b, 0x5a, 0x92, 0x13, 0xc5, 0x7a, 0x67, 0xbb, 0xbb, 0xd5, 0x14, 0x94, 0x0b, - 0x2d, 0x83, 0x16, 0x3a, 0xdb, 0x10, 0x8e, 0x6c, 0x36, 0xb4, 0x34, 0xba, 0x09, 0x4b, 0x91, 0x54, - 0x69, 0x66, 0xd0, 0x2d, 0x40, 0x91, 0x70, 0x6a, 0x22, 0xab, 0xff, 0x06, 0x2c, 0xd5, 0x99, 0x1b, - 0x10, 0xdb, 0x8d, 0x48, 0xf4, 0x3a, 0x1f, 0xb4, 0x12, 0x19, 0xb6, 0x25, 0xf7, 0xf4, 0xda, 0xd2, - 0xd9, 0xe9, 0x5a, 0x21, 0x82, 0xb6, 0x1a, 0x7c, 0xa4, 0x61, 0xc1, 0xe2, 0xeb, 0x77, 0x64, 0x5b, - 0xc2, 0xb9, 0x99, 0xda, 0xfc, 0xd9, 0xe9, 0x5a, 0xaa, 0xdb, 0x6a, 0x60, 0x2e, 0x43, 0xaf, 0x41, - 0x9e, 0x3e, 0xb3, 0x03, 0xc3, 0xe4, 0x7b, 0x38, 0x77, 0x60, 0x06, 0xe7, 0xb8, 0xa0, 0xce, 0xb7, - 0xec, 0x1a, 0x40, 0x97, 0x79, 0x81, 0x6a, 0xf9, 0x47, 0x90, 0x19, 0x31, 0x4f, 0x64, 0xb0, 0x97, - 0xe6, 0xeb, 0x1c, 0x2e, 0x03, 0x15, 0x4b, 0xb0, 0xfe, 0x97, 0x49, 0x80, 0x3e, 0xf1, 0x0f, 0x94, - 0x91, 0x87, 0x90, 0x8f, 0xee, 0x3e, 0x54, 0x2a, 0x7c, 0xe5, 0x6c, 0x47, 0x60, 0xf4, 0x20, 0x0c, - 0x36, 0x99, 0x1e, 0xcc, 0x4c, 0x65, 0xc2, 0x86, 0x66, 0x31, 0xec, 0xf3, 0x39, 0x00, 0x3f, 0x12, - 0xa9, 0xe7, 0xa9, 0x99, 0xe7, 0x9f, 0xa8, 0x2e, 0x8e, 0x05, 0xe9, 0x34, 0x45, 0x30, 0xdf, 0x9c, - 0xd5, 0xc8, 0x85, 0x19, 0xd9, 0x9c, 0xc3, 0x53, 0x3d, 0xf4, 0x39, 0x14, 0xf8, 0xb8, 0x0d, 0x5f, - 0xd4, 0x29, 0x6e, 0x79, 0xa9, 0xab, 0xa4, 0x05, 0x0c, 0xa3, 0xe8, 0xbb, 0xa6, 0xc1, 0xa2, 0x37, - 0x76, 0xf9, 0xb0, 0x95, 0x0d, 0xdd, 0x86, 0x57, 0xdb, 0x34, 0x38, 0x62, 0xde, 0x41, 0x35, 0x08, - 0x88, 0xb9, 0x3f, 0xa4, 0xae, 0xf2, 0x71, 0x8c, 0x58, 0x27, 0xce, 0x11, 0xeb, 0x15, 0x98, 0x27, - 0x8e, 0x4d, 0x7c, 0x2a, 0xd9, 0x48, 0x1e, 0x87, 0x45, 0x4e, 0xff, 0x79, 0x32, 0x41, 0x7d, 0x9f, - 0xca, 0x14, 0x38, 0x8f, 0xa7, 0x02, 0xfd, 0xef, 0x93, 0x00, 0xad, 0x6e, 0x75, 0x5b, 0x99, 0x6f, - 0x40, 0x76, 0x8f, 0x0c, 0x6d, 0x67, 0x72, 0xd5, 0x02, 0x9f, 0xe2, 0x2b, 0x55, 0x69, 0x68, 0x43, - 0xe8, 0x60, 0xa5, 0x2b, 0xb2, 0x82, 0xf1, 0xae, 0x4b, 0x83, 0x28, 0x2b, 0x10, 0x25, 0x4e, 0x41, - 0x3c, 0xe2, 0x46, 0x33, 0x23, 0x0b, 0xbc, 0xeb, 0x03, 0x12, 0xd0, 0x23, 0x32, 0x09, 0x57, 0xa5, - 0x2a, 0xa2, 0x4d, 0x9e, 0x2d, 0xf8, 0xd4, 0x3b, 0xa4, 0xd6, 0x4a, 0x46, 0x84, 0xe0, 0x75, 0xfd, - 0xc1, 0x0a, 0x2e, 0xc9, 0x55, 0xa4, 0x5d, 0xfa, 0x54, 0x30, 0x82, 0x69, 0xd5, 0x77, 0x4a, 0xe0, - 0xef, 0xc1, 0xc2, 0xb9, 0x71, 0xbe, 0x90, 0x8e, 0xb5, 0xba, 0x4f, 0x7e, 0xa4, 0xa5, 0xd5, 0xd7, - 0x47, 0x5a, 0x56, 0xff, 0xe3, 0x94, 0x5c, 0x47, 0xca, 0xab, 0xb3, 0xaf, 0xd4, 0x72, 0x22, 0xfa, - 0x4d, 0xe6, 0xa8, 0xf8, 0x7e, 0xfb, 0xea, 0xe5, 0xc5, 0xe9, 0xbd, 0x80, 0xe3, 0x48, 0x11, 0xad, - 0x41, 0x41, 0xce, 0xbf, 0xc1, 0xe3, 0x49, 0xb8, 0x75, 0x01, 0x83, 0x14, 0x71, 0x4d, 0x74, 0x17, - 0x16, 0x47, 0xe3, 0x5d, 0xc7, 0xf6, 0xf7, 0xa9, 0x25, 0x31, 0x69, 0x81, 0x59, 0x88, 0xa4, 0x02, - 0xb6, 0x0d, 0x45, 0x25, 0x30, 0x04, 0xb5, 0xcb, 0x88, 0x0e, 0xbd, 0x77, 0x5d, 0x87, 0xa4, 0x8a, - 0x60, 0x7c, 0x85, 0xd1, 0xb4, 0xa0, 0x37, 0x20, 0x17, 0x76, 0x16, 0xad, 0x40, 0xaa, 0x5f, 0xef, - 0x6a, 0x73, 0xa5, 0xa5, 0xe3, 0x93, 0x72, 0x21, 0x14, 0xf7, 0xeb, 0x5d, 0x5e, 0xb3, 0xd3, 0xe8, - 0x6a, 0x89, 0xf3, 0x35, 0x3b, 0x8d, 0x6e, 0x29, 0xcd, 0x29, 0x86, 0xbe, 0x07, 0x85, 0x58, 0x0b, - 0xe8, 0x4d, 0x98, 0x6f, 0xb5, 0x1f, 0xe1, 0x66, 0xaf, 0xa7, 0xcd, 0x95, 0x6e, 0x1d, 0x9f, 0x94, - 0x51, 0xac, 0xb6, 0xe5, 0x0e, 0xf8, 0xfc, 0xa0, 0xd7, 0x21, 0xbd, 0xd9, 0xe1, 0x47, 0x97, 0xe4, - 0x92, 0x31, 0xc4, 0x26, 0xf3, 0x83, 0xd2, 0x4d, 0xc5, 0x5d, 0xe2, 0x86, 0xf5, 0x3f, 0x48, 0x40, - 0x56, 0x52, 0xea, 0x99, 0x13, 0x55, 0x85, 0xf9, 0x30, 0xd1, 0x93, 0x3c, 0xff, 0xed, 0xcb, 0x39, - 0x79, 0x45, 0x51, 0x68, 0x19, 0x7e, 0xa1, 0x5e, 0xe9, 0x13, 0x28, 0xc6, 0x2b, 0xbe, 0x53, 0xf0, - 0xfd, 0x3a, 0x14, 0x78, 0x7c, 0x87, 0xdc, 0x7c, 0x1d, 0xb2, 0x92, 0xf6, 0x47, 0x5b, 0xe9, 0xe5, - 0x09, 0x82, 0x42, 0xa2, 0x87, 0x30, 0x2f, 0x93, 0x8a, 0xf0, 0x0a, 0x6c, 0xf5, 0xea, 0x55, 0x84, - 0x43, 0xb8, 0xfe, 0x39, 0xa4, 0xbb, 0x94, 0x7a, 0xdc, 0xf7, 0x2e, 0xb3, 0xe8, 0xf4, 0xf4, 0x51, - 0xf9, 0x90, 0x45, 0x5b, 0x0d, 0x9e, 0x0f, 0x59, 0xb4, 0x65, 0x45, 0x37, 0x18, 0xc9, 0xd8, 0x0d, - 0x46, 0x1f, 0x8a, 0x4f, 0xa9, 0x3d, 0xd8, 0x0f, 0xa8, 0x25, 0x0c, 0xbd, 0x0f, 0xe9, 0x11, 0x8d, - 0x3a, 0xbf, 0x32, 0x33, 0xc0, 0x28, 0xf5, 0xb0, 0x40, 0xf1, 0x7d, 0xe4, 0x48, 0x68, 0xab, 0x8b, - 0x57, 0x55, 0xd2, 0xff, 0x2e, 0x09, 0x8b, 0x2d, 0xdf, 0x1f, 0x13, 0xd7, 0x0c, 0x89, 0xc9, 0x67, - 0xe7, 0x89, 0xc9, 0x3b, 0x33, 0x47, 0x78, 0x4e, 0xe5, 0xfc, 0xc5, 0x8c, 0x3a, 0x1c, 0x92, 0xd1, - 0xe1, 0xa0, 0xff, 0x7b, 0x22, 0xbc, 0x7d, 0xb9, 0x1b, 0x5b, 0xee, 0xa5, 0x95, 0xe3, 0x93, 0xf2, - 0x72, 0xdc, 0x12, 0xdd, 0x71, 0x0f, 0x5c, 0x76, 0xe4, 0xa2, 0x37, 0x20, 0x83, 0x9b, 0xed, 0xe6, - 0x53, 0x2d, 0x21, 0xc3, 0xf3, 0x1c, 0x08, 0x53, 0x97, 0x1e, 0x71, 0x4b, 0xdd, 0x66, 0xbb, 0xc1, - 0x89, 0x44, 0x72, 0x86, 0xa5, 0x2e, 0x75, 0x2d, 0xdb, 0x1d, 0xa0, 0x37, 0x21, 0xdb, 0xea, 0xf5, - 0x76, 0x44, 0x7e, 0xfc, 0xea, 0xf1, 0x49, 0xf9, 0xe6, 0x39, 0x14, 0x2f, 0x50, 0x8b, 0x83, 0x38, - 0x8b, 0xe7, 0x14, 0x63, 0x06, 0x88, 0xd3, 0x43, 0x09, 0xc2, 0x9d, 0x3e, 0x4f, 0xde, 0x33, 0x33, - 0x40, 0x98, 0xf1, 0xbf, 0x6a, 0xb9, 0xfd, 0x53, 0x12, 0xb4, 0xaa, 0x69, 0xd2, 0x51, 0xc0, 0xeb, - 0x55, 0xe2, 0xd4, 0x87, 0xdc, 0x88, 0x7f, 0xd9, 0x34, 0x24, 0x01, 0x0f, 0x67, 0x5e, 0xfd, 0x5f, - 0xd0, 0xab, 0x60, 0xe6, 0xd0, 0xaa, 0x35, 0xb4, 0x7d, 0xdf, 0x66, 0xae, 0x94, 0xe1, 0xc8, 0x52, - 0xe9, 0x3f, 0x13, 0x70, 0x73, 0x06, 0x02, 0xdd, 0x83, 0xb4, 0xc7, 0x9c, 0x70, 0x0e, 0xef, 0x5c, - 0x76, 0xb1, 0xc6, 0x55, 0xb1, 0x40, 0xa2, 0x55, 0x00, 0x32, 0x0e, 0x18, 0x11, 0xed, 0x8b, 0xd9, - 0xcb, 0xe1, 0x98, 0x04, 0x3d, 0x85, 0xac, 0x4f, 0x4d, 0x8f, 0x86, 0x54, 0xf1, 0xf3, 0xff, 0x6b, - 0xef, 0x2b, 0x3d, 0x61, 0x06, 0x2b, 0x73, 0xa5, 0x0a, 0x64, 0xa5, 0x84, 0x87, 0xbd, 0x45, 0x02, - 0x22, 0x3a, 0x5d, 0xc4, 0xe2, 0x9b, 0x47, 0x13, 0x71, 0x06, 0x61, 0x34, 0x11, 0x67, 0xa0, 0xff, - 0x75, 0x12, 0xa0, 0xf9, 0x2c, 0xa0, 0x9e, 0x4b, 0x9c, 0x7a, 0x15, 0x35, 0x63, 0xbb, 0xbf, 0x1c, - 0xed, 0xbb, 0x33, 0xaf, 0x5b, 0x23, 0x8d, 0x4a, 0xbd, 0x3a, 0x63, 0xff, 0xbf, 0x0d, 0xa9, 0xb1, - 0xa7, 0x5e, 0x73, 0x24, 0xcd, 0xdb, 0xc1, 0x5b, 0x98, 0xcb, 0x50, 0x73, 0xba, 0x6d, 0xa5, 0x2e, - 0x7f, 0xb3, 0x89, 0x35, 0x30, 0x73, 0xeb, 0xe2, 0x2b, 0xdf, 0x24, 0x86, 0x49, 0xd5, 0xc9, 0x51, - 0x94, 0x2b, 0xbf, 0x5e, 0xad, 0x53, 0x2f, 0xc0, 0x59, 0x93, 0xf0, 0xff, 0xdf, 0x6b, 0x7f, 0x7b, - 0x1f, 0x60, 0x3a, 0x34, 0xb4, 0x0a, 0x99, 0xfa, 0x46, 0xaf, 0xb7, 0xa5, 0xcd, 0xc9, 0x0d, 0x7c, - 0x5a, 0x25, 0xc4, 0xfa, 0x9f, 0x27, 0x21, 0x57, 0xaf, 0xaa, 0x63, 0xb5, 0x0e, 0x9a, 0xd8, 0x95, - 0x78, 0xef, 0x0c, 0xfa, 0x6c, 0x64, 0x7b, 0x13, 0xb5, 0xb1, 0x5c, 0x91, 0xb3, 0x2d, 0x72, 0x15, - 0xde, 0xeb, 0xa6, 0x50, 0x40, 0x18, 0x8a, 0x54, 0x39, 0xc1, 0x30, 0x49, 0xb8, 0xc7, 0xaf, 0x5e, - 0xed, 0x2c, 0xc9, 0xbe, 0xa7, 0x65, 0x1f, 0x17, 0x42, 0x23, 0x75, 0xe2, 0xa3, 0x8f, 0x61, 0xc9, - 0xb7, 0x07, 0xae, 0xed, 0x0e, 0x8c, 0xd0, 0x79, 0x29, 0xe1, 0xbc, 0x1b, 0x67, 0xa7, 0x6b, 0x0b, - 0x3d, 0x59, 0xa5, 0x7c, 0xb8, 0xa0, 0x90, 0x75, 0xe1, 0x4a, 0xf4, 0x11, 0x2c, 0xc6, 0x54, 0xb9, - 0x17, 0xa5, 0xdb, 0xb5, 0xb3, 0xd3, 0xb5, 0x62, 0xa4, 0xf9, 0x98, 0x4e, 0x70, 0x31, 0x52, 0x7c, - 0x4c, 0xc5, 0xf5, 0xc2, 0x1e, 0xf3, 0x4c, 0x6a, 0x78, 0x62, 0x4d, 0x8b, 0x13, 0x3c, 0x8d, 0x0b, - 0x42, 0x26, 0x97, 0xb9, 0xfe, 0x04, 0x6e, 0x76, 0x3c, 0x73, 0x9f, 0xfa, 0x81, 0x74, 0x85, 0xf2, - 0xe2, 0xe7, 0x70, 0x27, 0x20, 0xfe, 0x81, 0xb1, 0x6f, 0xfb, 0x01, 0xf3, 0x26, 0x86, 0x47, 0x03, - 0xea, 0xf2, 0x7a, 0x43, 0xbc, 0x48, 0xa9, 0xfb, 0x9f, 0xdb, 0x1c, 0xb3, 0x29, 0x21, 0x38, 0x44, - 0x6c, 0x71, 0x80, 0xde, 0x82, 0x22, 0x67, 0xe1, 0x0d, 0xba, 0x47, 0xc6, 0x4e, 0xc0, 0x47, 0x0f, - 0x0e, 0x1b, 0x18, 0x2f, 0x7d, 0x4c, 0xe5, 0x1d, 0x36, 0x90, 0x9f, 0xfa, 0x4f, 0x41, 0x6b, 0xd8, - 0xfe, 0x88, 0x04, 0xe6, 0x7e, 0x78, 0xb1, 0x85, 0x1a, 0xa0, 0xed, 0x53, 0xe2, 0x05, 0xbb, 0x94, - 0x04, 0xc6, 0x88, 0x7a, 0x36, 0xb3, 0xae, 0x9f, 0xe5, 0xa5, 0x48, 0xa5, 0x2b, 0x34, 0xf4, 0xff, - 0x4a, 0x00, 0x60, 0xb2, 0x17, 0x32, 0xb2, 0x1f, 0xc2, 0x0d, 0xdf, 0x25, 0x23, 0x7f, 0x9f, 0x05, - 0x86, 0xed, 0x06, 0xd4, 0x3b, 0x24, 0x8e, 0xba, 0x9f, 0xd0, 0xc2, 0x8a, 0x96, 0x92, 0xa3, 0xf7, - 0x01, 0x1d, 0x50, 0x3a, 0x32, 0x98, 0x63, 0x19, 0x61, 0xa5, 0x7c, 0x2f, 0x4b, 0x63, 0x8d, 0xd7, - 0x74, 0x1c, 0xab, 0x17, 0xca, 0x51, 0x0d, 0x56, 0xf9, 0xf0, 0xa9, 0x1b, 0x78, 0x36, 0xf5, 0x8d, - 0x3d, 0xe6, 0x19, 0xbe, 0xc3, 0x8e, 0x8c, 0x3d, 0xe6, 0x38, 0xec, 0x88, 0x7a, 0xe1, 0xd5, 0x4f, - 0xc9, 0x61, 0x83, 0xa6, 0x04, 0x6d, 0x30, 0xaf, 0xe7, 0xb0, 0xa3, 0x8d, 0x10, 0xc1, 0x69, 0xdb, - 0x74, 0xcc, 0x81, 0x6d, 0x1e, 0x84, 0xb4, 0x2d, 0x92, 0xf6, 0x6d, 0xf3, 0x00, 0xbd, 0x09, 0x0b, - 0xd4, 0xa1, 0xe2, 0x06, 0x40, 0xa2, 0x32, 0x02, 0x55, 0x0c, 0x85, 0x1c, 0xa4, 0x7f, 0x01, 0x5a, - 0xd3, 0x35, 0xbd, 0xc9, 0x28, 0x36, 0xe7, 0xef, 0x03, 0xe2, 0x9b, 0xa4, 0xe1, 0x30, 0xf3, 0xc0, - 0x18, 0x12, 0x97, 0x0c, 0x78, 0xbf, 0xe4, 0x1b, 0x8d, 0xc6, 0x6b, 0xb6, 0x98, 0x79, 0xb0, 0xad, - 0xe4, 0xfa, 0xc7, 0x00, 0xbd, 0x91, 0x47, 0x89, 0xd5, 0xe1, 0x6c, 0x82, 0xbb, 0x4e, 0x94, 0x0c, - 0x4b, 0x3d, 0x03, 0x31, 0x4f, 0x2d, 0x75, 0x4d, 0x56, 0x34, 0x22, 0xb9, 0xfe, 0xcb, 0x70, 0xb3, - 0xeb, 0x10, 0x53, 0x3c, 0x89, 0x76, 0xa3, 0x47, 0x07, 0xf4, 0x10, 0xb2, 0x12, 0xaa, 0x66, 0x72, - 0xe6, 0x72, 0x9b, 0xb6, 0xb9, 0x39, 0x87, 0x15, 0xbe, 0x56, 0x04, 0x98, 0xda, 0xd1, 0x9f, 0x41, - 0x3e, 0x32, 0x8f, 0xca, 0xc0, 0x53, 0x60, 0x1e, 0xdd, 0xb6, 0xab, 0x72, 0xd6, 0x3c, 0x8e, 0x8b, - 0x50, 0x0b, 0x0a, 0xa3, 0x48, 0xf9, 0x4a, 0x3a, 0x37, 0xa3, 0xd3, 0x38, 0xae, 0xab, 0x7f, 0x06, - 0xf0, 0x13, 0x66, 0xbb, 0x7d, 0x76, 0x40, 0x5d, 0xf1, 0xce, 0xc5, 0xb3, 0x35, 0x1a, 0x3a, 0x42, - 0x95, 0x44, 0x32, 0x2a, 0xbd, 0x18, 0x3d, 0xf7, 0xc8, 0xa2, 0xfe, 0x57, 0x49, 0xc8, 0x62, 0xc6, - 0x82, 0x7a, 0x15, 0x95, 0x21, 0xab, 0x96, 0xba, 0x38, 0x42, 0x6a, 0xf9, 0xb3, 0xd3, 0xb5, 0x8c, - 0x5c, 0xe3, 0x19, 0x53, 0x2c, 0xee, 0xd8, 0x26, 0x9c, 0xbc, 0x6c, 0x13, 0x46, 0xf7, 0xa0, 0xa8, - 0x40, 0xc6, 0x3e, 0xf1, 0xf7, 0x65, 0x8e, 0x55, 0x5b, 0x3c, 0x3b, 0x5d, 0x03, 0x89, 0xdc, 0x24, - 0xfe, 0x3e, 0x06, 0x89, 0xe6, 0xdf, 0xa8, 0x09, 0x85, 0xaf, 0x98, 0xed, 0x1a, 0x81, 0x18, 0x84, - 0xba, 0xee, 0x9a, 0x39, 0x15, 0xd3, 0xa1, 0xaa, 0x77, 0x51, 0xf8, 0x6a, 0x3a, 0xf8, 0x26, 0x2c, - 0x78, 0x8c, 0x05, 0x72, 0xe7, 0xb1, 0x99, 0xab, 0x32, 0xe9, 0xf2, 0xcc, 0x0b, 0x56, 0xc6, 0x02, - 0xac, 0x70, 0xb8, 0xe8, 0xc5, 0x4a, 0xe8, 0x1e, 0x2c, 0x3b, 0xc4, 0x0f, 0x0c, 0xb1, 0x65, 0x59, - 0x53, 0x6b, 0x59, 0xb1, 0x5a, 0x10, 0xaf, 0xdb, 0x10, 0x55, 0xa1, 0x86, 0xfe, 0x0f, 0x09, 0x28, - 0xf0, 0xc1, 0xd8, 0x7b, 0xb6, 0xc9, 0x79, 0xda, 0x77, 0xa7, 0x0f, 0xb7, 0x21, 0x65, 0xfa, 0x9e, - 0x72, 0xaa, 0x38, 0x3f, 0xeb, 0x3d, 0x8c, 0xb9, 0x0c, 0x7d, 0x01, 0x59, 0x95, 0xd1, 0x4b, 0xe6, - 0xa0, 0x5f, 0xcf, 0x28, 0x95, 0x6f, 0x94, 0x9e, 0x88, 0xc7, 0x69, 0xef, 0xe4, 0x3e, 0x8e, 0xe3, - 0x22, 0x74, 0x0b, 0x92, 0xa6, 0x74, 0x97, 0x7a, 0x78, 0xaf, 0xb7, 0x71, 0xd2, 0x74, 0xf5, 0xbf, - 0x4d, 0xc0, 0xc2, 0x74, 0xcd, 0xf2, 0x08, 0xb8, 0x03, 0x79, 0x7f, 0xbc, 0xeb, 0x4f, 0xfc, 0x80, - 0x0e, 0xc3, 0x37, 0xbc, 0x48, 0x80, 0x5a, 0x90, 0x27, 0xce, 0x80, 0x79, 0x76, 0xb0, 0x3f, 0x54, - 0xc9, 0xe4, 0xec, 0xd3, 0x3e, 0x6e, 0xb3, 0x52, 0x0d, 0x55, 0xf0, 0x54, 0x3b, 0x3c, 0xba, 0xc5, - 0x71, 0x25, 0x8f, 0xee, 0x37, 0xa0, 0xe8, 0x90, 0xa1, 0xb8, 0xe2, 0x08, 0xec, 0xa1, 0x1c, 0x47, - 0x1a, 0x17, 0x94, 0xac, 0x6f, 0x0f, 0xa9, 0xae, 0x43, 0x3e, 0x32, 0x86, 0x96, 0xa0, 0x50, 0x6d, - 0xf6, 0x8c, 0xfb, 0xeb, 0x0f, 0x8d, 0x47, 0xf5, 0x6d, 0x6d, 0x4e, 0xd1, 0xcb, 0x3f, 0x4b, 0xc0, - 0x82, 0xda, 0x51, 0x14, 0x65, 0x7f, 0x13, 0xe6, 0x3d, 0xb2, 0x17, 0x84, 0x49, 0x45, 0x5a, 0x46, - 0x35, 0xdf, 0xa4, 0x79, 0x52, 0xc1, 0xab, 0x66, 0x27, 0x15, 0xb1, 0x57, 0xe5, 0xd4, 0x95, 0xaf, - 0xca, 0xe9, 0xff, 0x97, 0x57, 0x65, 0xfd, 0x4f, 0x92, 0xb0, 0xa4, 0xd8, 0x5f, 0xb4, 0x81, 0xbd, - 0x0b, 0x79, 0x49, 0x04, 0xa7, 0x29, 0x91, 0x78, 0xc8, 0x94, 0xb8, 0x56, 0x03, 0xe7, 0x64, 0x75, - 0xcb, 0xe2, 0x39, 0xba, 0x82, 0xc6, 0x7e, 0x23, 0x01, 0x52, 0xd4, 0xe6, 0x09, 0x66, 0x03, 0xd2, - 0x7b, 0xb6, 0x43, 0x55, 0x9c, 0xcd, 0xbc, 0xbe, 0xbe, 0xd0, 0xbc, 0x78, 0x68, 0xe9, 0x8b, 0x2c, - 0x7f, 0x73, 0x0e, 0x0b, 0xed, 0xd2, 0x6f, 0x02, 0x4c, 0xa5, 0x33, 0x13, 0x59, 0x4e, 0x16, 0xd5, - 0x9d, 0x60, 0x48, 0x16, 0x5b, 0x0d, 0xcc, 0x65, 0xbc, 0x6a, 0x60, 0x5b, 0x6a, 0xcb, 0x10, 0x55, - 0x8f, 0x78, 0xd5, 0xc0, 0xb6, 0xa2, 0xd7, 0x9e, 0xf4, 0x35, 0xaf, 0x3d, 0xb5, 0x5c, 0x78, 0x33, - 0xa5, 0x6f, 0xc1, 0xad, 0x9a, 0x43, 0xcc, 0x03, 0xc7, 0xf6, 0x03, 0x6a, 0xc5, 0x57, 0xe8, 0x3a, - 0x64, 0xcf, 0xf1, 0xb4, 0xab, 0x2e, 0x02, 0x15, 0x52, 0xff, 0xb7, 0x04, 0x14, 0x37, 0x29, 0x71, - 0x82, 0xfd, 0xe9, 0x6d, 0x4a, 0x40, 0xfd, 0x40, 0x6d, 0xf0, 0xe2, 0x1b, 0x7d, 0x08, 0xb9, 0xe8, - 0x18, 0xbf, 0xf6, 0x45, 0x26, 0x82, 0xa2, 0x07, 0x30, 0xcf, 0x63, 0x9a, 0x8d, 0xc3, 0xfc, 0xe0, - 0xaa, 0xcb, 0x7e, 0x85, 0xe4, 0x9b, 0xba, 0x47, 0xc5, 0xb9, 0x2d, 0x9c, 0x92, 0xc1, 0x61, 0x11, - 0xfd, 0x12, 0x14, 0xc5, 0x5d, 0x75, 0x48, 0x53, 0x32, 0xd7, 0xd9, 0x2c, 0xc8, 0xe7, 0x26, 0x49, - 0x51, 0xfe, 0x27, 0x01, 0xcb, 0xdb, 0x64, 0xb2, 0x4b, 0xd5, 0x32, 0xa5, 0x16, 0xa6, 0x26, 0xf3, - 0x2c, 0xd4, 0x8d, 0x2f, 0xef, 0x2b, 0x5e, 0xaf, 0x66, 0x29, 0xcf, 0x5e, 0xe5, 0x61, 0xce, 0x92, - 0x8c, 0xe5, 0x2c, 0xcb, 0x90, 0x71, 0x99, 0x6b, 0x52, 0xb5, 0xf6, 0x65, 0x41, 0xb7, 0xe3, 0x4b, - 0xbb, 0x14, 0x3d, 0x2c, 0x89, 0x67, 0xa1, 0x36, 0x0b, 0xa2, 0xd6, 0xd0, 0x17, 0x50, 0xea, 0x35, - 0xeb, 0xb8, 0xd9, 0xaf, 0x75, 0x7e, 0x6a, 0xf4, 0xaa, 0x5b, 0xbd, 0xea, 0xfa, 0x3d, 0xa3, 0xdb, - 0xd9, 0xfa, 0xf2, 0xfe, 0x83, 0x7b, 0x1f, 0x6a, 0x89, 0x52, 0xf9, 0xf8, 0xa4, 0x7c, 0xa7, 0x5d, - 0xad, 0x6f, 0xc9, 0x58, 0xde, 0x65, 0xcf, 0x7a, 0xc4, 0xf1, 0xc9, 0xfa, 0xbd, 0x2e, 0x73, 0x26, - 0x1c, 0xa3, 0x9f, 0x24, 0xa0, 0x18, 0x3f, 0x1f, 0xe2, 0xc7, 0x5e, 0xe2, 0xd2, 0x63, 0x6f, 0x7a, - 0x7a, 0x26, 0x2f, 0x39, 0x3d, 0x37, 0x60, 0xd9, 0xf4, 0x98, 0xef, 0x1b, 0x9c, 0x30, 0x53, 0xeb, - 0x02, 0x25, 0x7f, 0xe5, 0xec, 0x74, 0xed, 0x46, 0x9d, 0xd7, 0xf7, 0x44, 0xb5, 0x32, 0x7f, 0xc3, - 0x8c, 0x89, 0x44, 0x4b, 0xef, 0xfd, 0x22, 0x05, 0xf9, 0xe8, 0xba, 0x99, 0x2f, 0x19, 0x9e, 0xeb, - 0x2b, 0x57, 0x44, 0xf2, 0x36, 0x3d, 0x42, 0x6f, 0x4c, 0xb3, 0xfc, 0x2f, 0xe4, 0xfb, 0x5a, 0x54, - 0x1d, 0x66, 0xf8, 0x6f, 0x41, 0xae, 0xda, 0xeb, 0xb5, 0x1e, 0xb5, 0x9b, 0x0d, 0xed, 0xeb, 0x44, - 0xe9, 0x95, 0xe3, 0x93, 0xf2, 0x8d, 0x08, 0x54, 0xf5, 0x65, 0x4f, 0x05, 0xaa, 0x5e, 0x6f, 0x76, - 0xfb, 0xcd, 0x86, 0xf6, 0x3c, 0x79, 0x11, 0x25, 0xb2, 0x56, 0xf1, 0x4a, 0x9e, 0xef, 0xe2, 0x66, - 0xb7, 0x8a, 0x79, 0x83, 0x5f, 0x27, 0xe5, 0xe5, 0xc3, 0xb4, 0x45, 0x8f, 0x8e, 0x88, 0xc7, 0xdb, - 0x5c, 0x0d, 0x7f, 0x2d, 0xf2, 0x3c, 0x25, 0x5f, 0x52, 0xa7, 0x77, 0xe7, 0x94, 0x58, 0x13, 0xde, - 0x9a, 0x78, 0xb4, 0x10, 0x66, 0x52, 0x17, 0x5a, 0xeb, 0xf1, 0x40, 0xe5, 0x56, 0x74, 0x98, 0xc7, - 0x3b, 0xed, 0x36, 0x07, 0x3d, 0x4f, 0x5f, 0x18, 0x1d, 0x1e, 0xbb, 0x3c, 0x23, 0x41, 0x77, 0x21, - 0x17, 0xbe, 0x69, 0x68, 0x5f, 0xa7, 0x2f, 0x74, 0xa8, 0x1e, 0x3e, 0xc8, 0x88, 0x06, 0x37, 0x77, - 0xfa, 0xe2, 0xc7, 0x2c, 0xcf, 0x33, 0x17, 0x1b, 0xdc, 0x1f, 0x07, 0x16, 0x3b, 0x72, 0xf9, 0x04, - 0xab, 0x7b, 0x8e, 0xaf, 0x33, 0x32, 0x29, 0x8c, 0x30, 0xea, 0x92, 0xe3, 0x2d, 0xc8, 0xe1, 0xe6, - 0x4f, 0xe4, 0xef, 0x5e, 0x9e, 0x67, 0x2f, 0xd8, 0xc1, 0xf4, 0x2b, 0x6a, 0xaa, 0xd6, 0x3a, 0xb8, - 0xbb, 0x59, 0x15, 0x2e, 0xbf, 0x88, 0xea, 0x78, 0xa3, 0x7d, 0xe2, 0x52, 0x6b, 0xfa, 0x9c, 0x1c, - 0x55, 0xbd, 0xf7, 0x2b, 0x90, 0x0b, 0x69, 0x03, 0x5a, 0x85, 0xec, 0xd3, 0x0e, 0x7e, 0xdc, 0xc4, - 0xda, 0x9c, 0xf4, 0x61, 0x58, 0xf3, 0x54, 0x12, 0xbe, 0x32, 0xcc, 0x6f, 0x57, 0xdb, 0xd5, 0x47, - 0x4d, 0x1c, 0x5e, 0x41, 0x86, 0x00, 0x75, 0xf6, 0x95, 0x34, 0xd5, 0x40, 0x64, 0xb3, 0xb6, 0xf2, - 0xcd, 0xb7, 0xab, 0x73, 0x3f, 0xff, 0x76, 0x75, 0xee, 0xf9, 0xd9, 0x6a, 0xe2, 0x9b, 0xb3, 0xd5, - 0xc4, 0xcf, 0xce, 0x56, 0x13, 0xff, 0x72, 0xb6, 0x9a, 0xd8, 0xcd, 0x8a, 0x1d, 0xe3, 0xc1, 0xff, - 0x06, 0x00, 0x00, 0xff, 0xff, 0x45, 0x07, 0xa3, 0xee, 0x6e, 0x2a, 0x00, 0x00, + // 4449 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x7a, 0x4d, 0x6c, 0x23, 0xc7, + 0x72, 0xbf, 0xf8, 0x29, 0xb2, 0x48, 0x49, 0xb3, 0xbd, 0xf2, 0x5a, 0x4b, 0xaf, 0x25, 0x7a, 0xec, + 0x7d, 0xfe, 0x78, 0x06, 0xbd, 0xab, 0x7d, 0x36, 0xd6, 0xf6, 0xff, 0xd9, 0xe6, 0x97, 0x56, 0x7c, + 0x2b, 0x91, 0x44, 0x93, 0xda, 0x7d, 0x3e, 0xfc, 0x33, 0x18, 0xcd, 0xb4, 0xa8, 0xb1, 0x86, 0xd3, + 0xcc, 0x4c, 0x53, 0x5a, 0x26, 0x08, 0xb2, 0xc8, 0x21, 0x09, 0x74, 0x4a, 0x6e, 0x01, 0x02, 0x9d, + 0x92, 0x53, 0x10, 0xe4, 0x92, 0x43, 0x90, 0x5c, 0xe2, 0x00, 0x39, 0xf8, 0x96, 0x97, 0x04, 0x08, + 0x1e, 0x12, 0x40, 0x89, 0x75, 0xc8, 0x2d, 0x48, 0x2e, 0x0f, 0xb9, 0x24, 0x40, 0xd0, 0x1f, 0x33, + 0x1c, 0x69, 0x29, 0x69, 0x1d, 0xe7, 0x22, 0x4d, 0x57, 0xfd, 0xaa, 0xba, 0xbb, 0xba, 0xba, 0xbb, + 0xaa, 0x9a, 0x50, 0x60, 0x93, 0x11, 0x09, 0x2a, 0x23, 0x9f, 0x32, 0x8a, 0x90, 0x4d, 0xad, 0x03, + 0xe2, 0x57, 0x82, 0x23, 0xd3, 0x1f, 0x1e, 0x38, 0xac, 0x72, 0x78, 0xbf, 0xb4, 0x36, 0xa0, 0x74, + 0xe0, 0x92, 0x0f, 0x04, 0x62, 0x77, 0xbc, 0xf7, 0x01, 0x73, 0x86, 0x24, 0x60, 0xe6, 0x70, 0x24, + 0x85, 0x4a, 0xab, 0x17, 0x01, 0xf6, 0xd8, 0x37, 0x99, 0x43, 0x3d, 0xc5, 0x5f, 0x1e, 0xd0, 0x01, + 0x15, 0x9f, 0x1f, 0xf0, 0x2f, 0x49, 0xd5, 0xd7, 0x60, 0xfe, 0x09, 0xf1, 0x03, 0x87, 0x7a, 0x68, + 0x19, 0x32, 0x8e, 0x67, 0x93, 0x67, 0x2b, 0x89, 0x72, 0xe2, 0x9d, 0x34, 0x96, 0x0d, 0xfd, 0x1e, + 0x40, 0x8b, 0x7f, 0x34, 0x3d, 0xe6, 0x4f, 0x90, 0x06, 0xa9, 0x03, 0x32, 0x11, 0x88, 0x3c, 0xe6, + 0x9f, 0x9c, 0x72, 0x68, 0xba, 0x2b, 0x49, 0x49, 0x39, 0x34, 0x5d, 0xfd, 0xdb, 0x04, 0x14, 0xaa, + 0x9e, 0x47, 0x99, 0xe8, 0x3d, 0x40, 0x08, 0xd2, 0x9e, 0x39, 0x24, 0x4a, 0x48, 0x7c, 0xa3, 0x3a, + 0x64, 0x5d, 0x73, 0x97, 0xb8, 0xc1, 0x4a, 0xb2, 0x9c, 0x7a, 0xa7, 0xb0, 0xfe, 0xc3, 0xca, 0x8b, + 0x53, 0xae, 0xc4, 0x94, 0x54, 0xb6, 0x04, 0x5a, 0x0c, 0x02, 0x2b, 0x51, 0xf4, 0x19, 0xcc, 0x3b, + 0x9e, 0xed, 0x58, 0x24, 0x58, 0x49, 0x0b, 0x2d, 0xab, 0xb3, 0xb4, 0x4c, 0x47, 0x5f, 0x4b, 0x7f, + 0x73, 0xba, 0x36, 0x87, 0x43, 0xa1, 0xd2, 0xc7, 0x50, 0x88, 0xa9, 0x9d, 0x31, 0xb7, 0x65, 0xc8, + 0x1c, 0x9a, 0xee, 0x98, 0xa8, 0xd9, 0xc9, 0xc6, 0x27, 0xc9, 0x87, 0x09, 0xfd, 0x4b, 0xc8, 0x63, + 0x12, 0xd0, 0xb1, 0x6f, 0x91, 0x00, 0xbd, 0x0b, 0x79, 0xcf, 0xf4, 0xa8, 0x61, 0x8d, 0xc6, 0x81, + 0x10, 0x4f, 0xd5, 0x8a, 0x67, 0xa7, 0x6b, 0xb9, 0xb6, 0xe9, 0xd1, 0x7a, 0x77, 0x27, 0xc0, 0x39, + 0xce, 0xae, 0x8f, 0xc6, 0x01, 0x7a, 0x03, 0x8a, 0x43, 0x32, 0xa4, 0xfe, 0xc4, 0xd8, 0x9d, 0x30, + 0x12, 0x08, 0xc5, 0x29, 0x5c, 0x90, 0xb4, 0x1a, 0x27, 0xe9, 0xbf, 0x93, 0x80, 0xe5, 0x50, 0x37, + 0x26, 0xbf, 0x3c, 0x76, 0x7c, 0x32, 0x24, 0x1e, 0x0b, 0xd0, 0x87, 0x90, 0x75, 0x9d, 0xa1, 0xc3, + 0x64, 0x1f, 0x85, 0xf5, 0xd7, 0x67, 0xcd, 0x36, 0x1a, 0x15, 0x56, 0x60, 0x54, 0x85, 0xa2, 0x4f, + 0x02, 0xe2, 0x1f, 0x4a, 0x4b, 0x8a, 0x2e, 0xaf, 0x15, 0x3e, 0x27, 0xa2, 0x6f, 0x40, 0xae, 0xeb, + 0x9a, 0x6c, 0x8f, 0xfa, 0x43, 0xa4, 0x43, 0xd1, 0xf4, 0xad, 0x7d, 0x87, 0x11, 0x8b, 0x8d, 0xfd, + 0x70, 0x55, 0xcf, 0xd1, 0xd0, 0x2d, 0x48, 0x52, 0xd9, 0x51, 0xbe, 0x96, 0x3d, 0x3b, 0x5d, 0x4b, + 0x76, 0x7a, 0x38, 0x49, 0x03, 0xfd, 0x53, 0xb8, 0xd1, 0x75, 0xc7, 0x03, 0xc7, 0x6b, 0x90, 0xc0, + 0xf2, 0x9d, 0x11, 0xd7, 0xce, 0xdd, 0x83, 0xfb, 0x7e, 0xe8, 0x1e, 0xfc, 0x3b, 0x72, 0x99, 0xe4, + 0xd4, 0x65, 0xf4, 0xdf, 0x4a, 0xc2, 0x8d, 0xa6, 0x37, 0x70, 0x3c, 0x12, 0x97, 0xbe, 0x0b, 0x8b, + 0x44, 0x10, 0x8d, 0x43, 0xe9, 0xc6, 0x4a, 0xcf, 0x82, 0xa4, 0x86, 0xbe, 0xdd, 0xba, 0xe0, 0x6f, + 0xf7, 0x67, 0x4d, 0xff, 0x05, 0xed, 0x33, 0xbd, 0xae, 0x09, 0xf3, 0x23, 0x31, 0x89, 0x60, 0x25, + 0x25, 0x74, 0xdd, 0x9d, 0xa5, 0xeb, 0x85, 0x79, 0x86, 0xce, 0xa7, 0x64, 0xbf, 0x8f, 0xf3, 0xfd, + 0x71, 0x12, 0x96, 0xda, 0xd4, 0x3e, 0x67, 0x87, 0x12, 0xe4, 0xf6, 0x69, 0xc0, 0x62, 0x1b, 0x2d, + 0x6a, 0xa3, 0x87, 0x90, 0x1b, 0xa9, 0xe5, 0x53, 0xab, 0x7f, 0x67, 0xf6, 0x90, 0x25, 0x06, 0x47, + 0x68, 0xf4, 0x29, 0xe4, 0xfd, 0xd0, 0x27, 0x56, 0x52, 0x2f, 0xe3, 0x38, 0x53, 0x3c, 0xfa, 0x31, + 0x64, 0xe5, 0x22, 0xac, 0xa4, 0x85, 0xe4, 0xdd, 0x97, 0xb2, 0x39, 0x56, 0x42, 0xe8, 0x11, 0xe4, + 0x98, 0x1b, 0x18, 0x8e, 0xb7, 0x47, 0x57, 0x32, 0x42, 0xc1, 0xda, 0x2c, 0x05, 0xdc, 0x10, 0xfd, + 0xad, 0x5e, 0xcb, 0xdb, 0xa3, 0xb5, 0xc2, 0xd9, 0xe9, 0xda, 0xbc, 0x6a, 0xe0, 0x79, 0xe6, 0x06, + 0xfc, 0x43, 0xff, 0xdd, 0x04, 0x14, 0x62, 0x28, 0xf4, 0x3a, 0x00, 0xf3, 0xc7, 0x01, 0x33, 0x7c, + 0x4a, 0x99, 0x30, 0x56, 0x11, 0xe7, 0x05, 0x05, 0x53, 0xca, 0x50, 0x05, 0x6e, 0x5a, 0xc4, 0x67, + 0x86, 0x13, 0x04, 0x63, 0xe2, 0x1b, 0xc1, 0x78, 0xf7, 0x2b, 0x62, 0x31, 0x61, 0xb8, 0x22, 0xbe, + 0xc1, 0x59, 0x2d, 0xc1, 0xe9, 0x49, 0x06, 0x7a, 0x00, 0xb7, 0xe2, 0xf8, 0xd1, 0x78, 0xd7, 0x75, + 0x2c, 0x83, 0x2f, 0x66, 0x4a, 0x88, 0xdc, 0x9c, 0x8a, 0x74, 0x05, 0xef, 0x31, 0x99, 0xe8, 0x3f, + 0x4f, 0x80, 0x86, 0xcd, 0x3d, 0xb6, 0x4d, 0x86, 0xbb, 0xc4, 0xef, 0x31, 0x93, 0x8d, 0x03, 0x74, + 0x0b, 0xb2, 0x2e, 0x31, 0x6d, 0xe2, 0x8b, 0x41, 0xe5, 0xb0, 0x6a, 0xa1, 0x1d, 0xbe, 0x83, 0x4d, + 0x6b, 0xdf, 0xdc, 0x75, 0x5c, 0x87, 0x4d, 0xc4, 0x50, 0x16, 0x67, 0xbb, 0xf0, 0x45, 0x9d, 0x15, + 0x1c, 0x13, 0xc4, 0xe7, 0xd4, 0xa0, 0x15, 0x98, 0x1f, 0x92, 0x20, 0x30, 0x07, 0x44, 0x8c, 0x34, + 0x8f, 0xc3, 0xa6, 0xfe, 0x29, 0x14, 0xe3, 0x72, 0xa8, 0x00, 0xf3, 0x3b, 0xed, 0xc7, 0xed, 0xce, + 0xd3, 0xb6, 0x36, 0x87, 0x96, 0xa0, 0xb0, 0xd3, 0xc6, 0xcd, 0x6a, 0x7d, 0xb3, 0x5a, 0xdb, 0x6a, + 0x6a, 0x09, 0xb4, 0x00, 0xf9, 0x69, 0x33, 0xa9, 0xff, 0x69, 0x02, 0x80, 0x9b, 0x5b, 0x4d, 0xea, + 0x13, 0xc8, 0x04, 0xcc, 0x64, 0xd2, 0x2b, 0x17, 0xd7, 0xdf, 0xba, 0x6c, 0x0d, 0xd5, 0x78, 0xf9, + 0x3f, 0x82, 0xa5, 0x48, 0x7c, 0x84, 0xc9, 0x73, 0x23, 0xe4, 0x07, 0x84, 0x69, 0xdb, 0xbe, 0x1a, + 0xb8, 0xf8, 0xd6, 0x3f, 0x85, 0x8c, 0x90, 0x3e, 0x3f, 0xdc, 0x1c, 0xa4, 0x1b, 0xfc, 0x2b, 0x81, + 0xf2, 0x90, 0xc1, 0xcd, 0x6a, 0xe3, 0x4b, 0x2d, 0x89, 0x34, 0x28, 0x36, 0x5a, 0xbd, 0x7a, 0xa7, + 0xdd, 0x6e, 0xd6, 0xfb, 0xcd, 0x86, 0x96, 0xd2, 0xef, 0x42, 0xa6, 0x35, 0xe4, 0x9a, 0xef, 0x70, + 0x97, 0xdf, 0x23, 0x3e, 0xf1, 0xac, 0x70, 0x27, 0x4d, 0x09, 0xfa, 0xcf, 0xf2, 0x90, 0xd9, 0xa6, + 0x63, 0x8f, 0xa1, 0xf5, 0xd8, 0xb1, 0xb5, 0x38, 0xfb, 0xe6, 0x11, 0xc0, 0x4a, 0x7f, 0x32, 0x22, + 0xea, 0x58, 0xbb, 0x05, 0x59, 0xb9, 0x39, 0xd4, 0x74, 0x54, 0x8b, 0xd3, 0x99, 0xe9, 0x0f, 0x08, + 0x53, 0xf3, 0x51, 0x2d, 0xf4, 0x0e, 0xe4, 0x7c, 0x62, 0xda, 0xd4, 0x73, 0x27, 0x62, 0x0f, 0xe5, + 0xe4, 0xbd, 0x82, 0x89, 0x69, 0x77, 0x3c, 0x77, 0x82, 0x23, 0x2e, 0xda, 0x84, 0xe2, 0xae, 0xe3, + 0xd9, 0x06, 0x1d, 0xc9, 0x43, 0x3e, 0x73, 0xf9, 0x8e, 0x93, 0xa3, 0xaa, 0x39, 0x9e, 0xdd, 0x91, + 0x60, 0x5c, 0xd8, 0x9d, 0x36, 0x50, 0x1b, 0x16, 0x0f, 0xa9, 0x3b, 0x1e, 0x92, 0x48, 0x57, 0x56, + 0xe8, 0x7a, 0xfb, 0x72, 0x5d, 0x4f, 0x04, 0x3e, 0xd4, 0xb6, 0x70, 0x18, 0x6f, 0xa2, 0xc7, 0xb0, + 0xc0, 0x86, 0xa3, 0xbd, 0x20, 0x52, 0x37, 0x2f, 0xd4, 0xfd, 0xe0, 0x0a, 0x83, 0x71, 0x78, 0xa8, + 0xad, 0xc8, 0x62, 0xad, 0xd2, 0x6f, 0xa4, 0xa0, 0x10, 0x1b, 0x39, 0xea, 0x41, 0x61, 0xe4, 0xd3, + 0x91, 0x39, 0x10, 0x17, 0x95, 0x5a, 0x8b, 0xfb, 0x2f, 0x35, 0xeb, 0x4a, 0x77, 0x2a, 0x88, 0xe3, + 0x5a, 0xf4, 0x93, 0x24, 0x14, 0x62, 0x4c, 0xf4, 0x1e, 0xe4, 0x70, 0x17, 0xb7, 0x9e, 0x54, 0xfb, + 0x4d, 0x6d, 0xae, 0x74, 0xe7, 0xf8, 0xa4, 0xbc, 0x22, 0xb4, 0xc5, 0x15, 0x74, 0x7d, 0xe7, 0x90, + 0xbb, 0xde, 0x3b, 0x30, 0x1f, 0x42, 0x13, 0xa5, 0xd7, 0x8e, 0x4f, 0xca, 0xaf, 0x5e, 0x84, 0xc6, + 0x90, 0xb8, 0xb7, 0x59, 0xc5, 0xcd, 0x86, 0x96, 0x9c, 0x8d, 0xc4, 0xbd, 0x7d, 0xd3, 0x27, 0x36, + 0xfa, 0x01, 0x64, 0x15, 0x30, 0x55, 0x2a, 0x1d, 0x9f, 0x94, 0x6f, 0x5d, 0x04, 0x4e, 0x71, 0xb8, + 0xb7, 0x55, 0x7d, 0xd2, 0xd4, 0xd2, 0xb3, 0x71, 0xb8, 0xe7, 0x9a, 0x87, 0x04, 0xbd, 0x05, 0x19, + 0x09, 0xcb, 0x94, 0x6e, 0x1f, 0x9f, 0x94, 0x5f, 0x79, 0x41, 0x1d, 0x47, 0x95, 0x56, 0x7e, 0xfb, + 0x0f, 0x56, 0xe7, 0xfe, 0xe2, 0x0f, 0x57, 0xb5, 0x8b, 0xec, 0xd2, 0x7f, 0x25, 0x60, 0xe1, 0xdc, + 0x92, 0x23, 0x1d, 0xb2, 0x1e, 0xb5, 0xe8, 0x48, 0xde, 0x5f, 0xb9, 0x1a, 0x9c, 0x9d, 0xae, 0x65, + 0xdb, 0xb4, 0x4e, 0x47, 0x13, 0xac, 0x38, 0xe8, 0xf1, 0x85, 0x1b, 0xf8, 0xc1, 0x4b, 0xfa, 0xd3, + 0xcc, 0x3b, 0xf8, 0x73, 0x58, 0xb0, 0x7d, 0xe7, 0x90, 0xf8, 0x86, 0x45, 0xbd, 0x3d, 0x67, 0xa0, + 0xee, 0xa6, 0xd2, 0x2c, 0x9d, 0x0d, 0x01, 0xc4, 0x45, 0x29, 0x50, 0x17, 0xf8, 0xef, 0x71, 0xfb, + 0x96, 0x9e, 0x40, 0x31, 0xee, 0xa1, 0xfc, 0x3a, 0x09, 0x9c, 0x5f, 0x21, 0x2a, 0xa0, 0x13, 0xe1, + 0x1f, 0xce, 0x73, 0x8a, 0x08, 0xe7, 0xd0, 0xdb, 0x90, 0x1e, 0x52, 0x5b, 0xea, 0x59, 0xa8, 0xdd, + 0xe4, 0x41, 0xc0, 0x3f, 0x9e, 0xae, 0x15, 0x68, 0x50, 0xd9, 0x70, 0x5c, 0xb2, 0x4d, 0x6d, 0x82, + 0x05, 0x40, 0x3f, 0x84, 0x34, 0x3f, 0x2a, 0xd0, 0x6b, 0x90, 0xae, 0xb5, 0xda, 0x0d, 0x6d, 0xae, + 0x74, 0xe3, 0xf8, 0xa4, 0xbc, 0x20, 0x4c, 0xc2, 0x19, 0xdc, 0x77, 0xd1, 0x1a, 0x64, 0x9f, 0x74, + 0xb6, 0x76, 0xb6, 0xb9, 0x7b, 0xdd, 0x3c, 0x3e, 0x29, 0x2f, 0x45, 0x6c, 0x69, 0x34, 0xf4, 0x3a, + 0x64, 0xfa, 0xdb, 0xdd, 0x8d, 0x9e, 0x96, 0x2c, 0xa1, 0xe3, 0x93, 0xf2, 0x62, 0xc4, 0x17, 0x63, + 0x2e, 0xdd, 0x50, 0xab, 0x9a, 0x8f, 0xe8, 0xfa, 0x2f, 0x92, 0xb0, 0x80, 0x79, 0x26, 0xe1, 0xb3, + 0x2e, 0x75, 0x1d, 0x6b, 0x82, 0xba, 0x90, 0xb7, 0xa8, 0x67, 0x3b, 0xb1, 0x3d, 0xb5, 0x7e, 0xc9, + 0xad, 0x3f, 0x95, 0x0a, 0x5b, 0xf5, 0x50, 0x12, 0x4f, 0x95, 0xa0, 0x0f, 0x20, 0x63, 0x13, 0xd7, + 0x9c, 0xa8, 0xf0, 0xe3, 0x76, 0x45, 0xe6, 0x2a, 0x95, 0x30, 0x57, 0xa9, 0x34, 0x54, 0xae, 0x82, + 0x25, 0x4e, 0xc4, 0xc9, 0xe6, 0x33, 0xc3, 0x64, 0x8c, 0x0c, 0x47, 0x4c, 0xc6, 0x1e, 0x69, 0x5c, + 0x18, 0x9a, 0xcf, 0xaa, 0x8a, 0x84, 0xee, 0x43, 0xf6, 0xc8, 0xf1, 0x6c, 0x7a, 0xa4, 0xc2, 0x8b, + 0x2b, 0x94, 0x2a, 0xa0, 0x7e, 0xcc, 0x6f, 0xdd, 0x0b, 0xc3, 0xe4, 0xf6, 0x6e, 0x77, 0xda, 0xcd, + 0xd0, 0xde, 0x8a, 0xdf, 0xf1, 0xda, 0xd4, 0xe3, 0x7b, 0x05, 0x3a, 0x6d, 0x63, 0xa3, 0xda, 0xda, + 0xda, 0xc1, 0xdc, 0xe6, 0xcb, 0xc7, 0x27, 0x65, 0x2d, 0x82, 0x6c, 0x98, 0x8e, 0xcb, 0xe3, 0xdd, + 0xdb, 0x90, 0xaa, 0xb6, 0xbf, 0xd4, 0x92, 0x25, 0xed, 0xf8, 0xa4, 0x5c, 0x8c, 0xd8, 0x55, 0x6f, + 0x32, 0xdd, 0x46, 0x17, 0xfb, 0xd5, 0xff, 0x26, 0x05, 0xc5, 0x9d, 0x91, 0x6d, 0x32, 0x22, 0x7d, + 0x12, 0x95, 0xa1, 0x30, 0x32, 0x7d, 0xd3, 0x75, 0x89, 0xeb, 0x04, 0x43, 0x95, 0x85, 0xc5, 0x49, + 0xe8, 0xe3, 0x97, 0x35, 0x63, 0x2d, 0xc7, 0xfd, 0xec, 0xf7, 0xfe, 0x79, 0x2d, 0x11, 0x1a, 0x74, + 0x07, 0x16, 0xf7, 0xe4, 0x68, 0x0d, 0xd3, 0x12, 0x0b, 0x9b, 0x12, 0x0b, 0x5b, 0x99, 0xb5, 0xb0, + 0xf1, 0x61, 0x55, 0xd4, 0x24, 0xab, 0x42, 0x0a, 0x2f, 0xec, 0xc5, 0x9b, 0xe8, 0x01, 0xcc, 0x0f, + 0xa9, 0xe7, 0x30, 0xea, 0x5f, 0xbf, 0x0a, 0x21, 0x12, 0xbd, 0x07, 0x37, 0xf8, 0xe2, 0x86, 0xe3, + 0x11, 0x6c, 0x71, 0x63, 0x25, 0xf1, 0xd2, 0xd0, 0x7c, 0xa6, 0x3a, 0xc4, 0x9c, 0x8c, 0x6a, 0x90, + 0xa1, 0x3e, 0x0f, 0x89, 0xb2, 0x62, 0xb8, 0xef, 0x5f, 0x3b, 0x5c, 0xd9, 0xe8, 0x70, 0x19, 0x2c, + 0x45, 0xf5, 0x8f, 0x60, 0xe1, 0xdc, 0x24, 0x78, 0x24, 0xd0, 0xad, 0xee, 0xf4, 0x9a, 0xda, 0x1c, + 0x2a, 0x42, 0xae, 0xde, 0x69, 0xf7, 0x5b, 0xed, 0x1d, 0x1e, 0xca, 0x14, 0x21, 0x87, 0x3b, 0x5b, + 0x5b, 0xb5, 0x6a, 0xfd, 0xb1, 0x96, 0xd4, 0x2b, 0x50, 0x88, 0x69, 0x43, 0x8b, 0x00, 0xbd, 0x7e, + 0xa7, 0x6b, 0x6c, 0xb4, 0x70, 0xaf, 0x2f, 0x03, 0xa1, 0x5e, 0xbf, 0x8a, 0xfb, 0x8a, 0x90, 0xd0, + 0xff, 0x3d, 0x19, 0xae, 0xa8, 0x8a, 0x7d, 0x6a, 0xe7, 0x63, 0x9f, 0x2b, 0x06, 0xaf, 0xa2, 0x9f, + 0x69, 0x23, 0x8a, 0x81, 0x3e, 0x06, 0x10, 0x8e, 0x43, 0x6c, 0xc3, 0x64, 0x6a, 0xe1, 0x4b, 0x2f, + 0x18, 0xb9, 0x1f, 0x16, 0x03, 0x70, 0x5e, 0xa1, 0xab, 0x0c, 0xfd, 0x18, 0x8a, 0x16, 0x1d, 0x8e, + 0x5c, 0xa2, 0x84, 0x53, 0xd7, 0x0a, 0x17, 0x22, 0x7c, 0x95, 0xc5, 0xa3, 0xaf, 0xf4, 0xf9, 0xf8, + 0xf0, 0x37, 0x13, 0xa1, 0x65, 0x66, 0x04, 0x5c, 0x45, 0xc8, 0xed, 0x74, 0x1b, 0xd5, 0x7e, 0xab, + 0xfd, 0x48, 0x4b, 0x20, 0x80, 0xac, 0x30, 0x75, 0x43, 0x4b, 0xf2, 0x40, 0xb1, 0xde, 0xd9, 0xee, + 0x6e, 0x35, 0x45, 0xc8, 0x85, 0x96, 0x41, 0x0b, 0x8d, 0x6d, 0x08, 0x43, 0x36, 0x1b, 0x5a, 0x1a, + 0xdd, 0x84, 0xa5, 0x88, 0xaa, 0x24, 0x33, 0xe8, 0x16, 0xa0, 0x88, 0x38, 0x55, 0x91, 0xd5, 0x7f, + 0x0d, 0x96, 0xea, 0xd4, 0x63, 0xa6, 0xe3, 0x45, 0x41, 0xf4, 0x3a, 0x9f, 0xb4, 0x22, 0x19, 0x8e, + 0x2d, 0xcf, 0xf4, 0xda, 0xd2, 0xd9, 0xe9, 0x5a, 0x21, 0x82, 0xb6, 0x1a, 0x7c, 0xa6, 0x61, 0xc3, + 0xe6, 0xfb, 0x77, 0xe4, 0xd8, 0xc2, 0xb8, 0x99, 0xda, 0xfc, 0xd9, 0xe9, 0x5a, 0xaa, 0xdb, 0x6a, + 0x60, 0x4e, 0x43, 0xaf, 0x41, 0x9e, 0x3c, 0x73, 0x98, 0x61, 0xf1, 0x33, 0x9c, 0x1b, 0x30, 0x83, + 0x73, 0x9c, 0x50, 0xe7, 0x47, 0x76, 0x0d, 0xa0, 0x4b, 0x7d, 0xa6, 0x7a, 0xfe, 0x11, 0x64, 0x46, + 0xd4, 0x17, 0xe9, 0xf9, 0xa5, 0xc5, 0x08, 0x0e, 0x97, 0x8e, 0x8a, 0x25, 0x58, 0xff, 0xcb, 0x24, + 0x40, 0xdf, 0x0c, 0x0e, 0x94, 0x92, 0x87, 0x90, 0x8f, 0x0a, 0x3b, 0x2a, 0xcf, 0xbf, 0x72, 0xb5, + 0x23, 0x30, 0x7a, 0x10, 0x3a, 0x9b, 0x4c, 0x0f, 0x66, 0xe6, 0x69, 0x61, 0x47, 0xb3, 0x22, 0xec, + 0xf3, 0x39, 0x00, 0xbf, 0x12, 0x89, 0xef, 0xab, 0x95, 0xe7, 0x9f, 0xa8, 0x2e, 0xae, 0x05, 0x69, + 0x34, 0x15, 0x60, 0xbe, 0x39, 0xab, 0x93, 0x0b, 0x2b, 0xb2, 0x39, 0x87, 0xa7, 0x72, 0xe8, 0x73, + 0x28, 0xf0, 0x79, 0x1b, 0x81, 0xe0, 0xa9, 0xd8, 0xf2, 0x52, 0x53, 0x49, 0x0d, 0x18, 0x46, 0xd1, + 0x77, 0x4d, 0x83, 0x45, 0x7f, 0xec, 0xf1, 0x69, 0x2b, 0x1d, 0xba, 0x03, 0xaf, 0xb6, 0x09, 0x3b, + 0xa2, 0xfe, 0x41, 0x95, 0x31, 0xd3, 0xda, 0x1f, 0x12, 0x4f, 0xd9, 0x38, 0x16, 0x58, 0x27, 0xce, + 0x05, 0xd6, 0x2b, 0x30, 0x6f, 0xba, 0x8e, 0x19, 0x10, 0x19, 0x8d, 0xe4, 0x71, 0xd8, 0xe4, 0xe1, + 0x3f, 0x4f, 0x26, 0x48, 0x10, 0x10, 0x99, 0xdf, 0xe7, 0xf1, 0x94, 0xa0, 0xff, 0x7d, 0x12, 0xa0, + 0xd5, 0xad, 0x6e, 0x2b, 0xf5, 0x0d, 0xc8, 0xee, 0x99, 0x43, 0xc7, 0x9d, 0x5c, 0xb5, 0xc1, 0xa7, + 0xf8, 0x4a, 0x55, 0x2a, 0xda, 0x10, 0x32, 0x58, 0xc9, 0x8a, 0xac, 0x60, 0xbc, 0xeb, 0x11, 0x16, + 0x65, 0x05, 0xa2, 0xc5, 0x43, 0x10, 0xdf, 0xf4, 0xa2, 0x95, 0x91, 0x0d, 0x3e, 0xf4, 0x81, 0xc9, + 0xc8, 0x91, 0x39, 0x09, 0x77, 0xa5, 0x6a, 0xa2, 0x4d, 0x9e, 0x2d, 0x04, 0xc4, 0x3f, 0x24, 0xf6, + 0x4a, 0x46, 0xb8, 0xe0, 0x75, 0xe3, 0xc1, 0x0a, 0x2e, 0x83, 0xab, 0x48, 0xba, 0xf4, 0xa9, 0x88, + 0x08, 0xa6, 0xac, 0xef, 0x54, 0x9d, 0xb8, 0x07, 0x0b, 0xe7, 0xe6, 0xf9, 0x42, 0x3a, 0xd6, 0xea, + 0x3e, 0xf9, 0x91, 0x96, 0x56, 0x5f, 0x1f, 0x69, 0x59, 0xfd, 0x8f, 0x52, 0x72, 0x1f, 0x29, 0xab, + 0xce, 0xae, 0x17, 0xe6, 0x84, 0xf7, 0x5b, 0xd4, 0x55, 0xfe, 0xfd, 0xf6, 0xd5, 0xdb, 0x8b, 0x87, + 0xf7, 0x02, 0x8e, 0x23, 0x41, 0xb4, 0x06, 0x05, 0xb9, 0xfe, 0x06, 0xf7, 0x27, 0x61, 0xd6, 0x05, + 0x0c, 0x92, 0xc4, 0x25, 0xd1, 0x5d, 0x58, 0x14, 0xe9, 0x7b, 0xb0, 0x4f, 0x6c, 0x89, 0x49, 0x0b, + 0xcc, 0x42, 0x44, 0x15, 0xb0, 0x6d, 0x28, 0x2a, 0x82, 0x21, 0x42, 0xbb, 0x8c, 0x18, 0xd0, 0x7b, + 0xd7, 0x0d, 0x48, 0x8a, 0x88, 0x88, 0xaf, 0x30, 0x9a, 0x36, 0xf4, 0x06, 0xe4, 0xc2, 0xc1, 0xa2, + 0x15, 0x48, 0xf5, 0xeb, 0x5d, 0x6d, 0xae, 0xb4, 0x74, 0x7c, 0x52, 0x2e, 0x84, 0xe4, 0x7e, 0xbd, + 0xcb, 0x39, 0x3b, 0x8d, 0xae, 0x96, 0x38, 0xcf, 0xd9, 0x69, 0x74, 0x4b, 0x69, 0x1e, 0x62, 0xe8, + 0x7b, 0x50, 0x88, 0xf5, 0x80, 0xde, 0x84, 0xf9, 0x56, 0xfb, 0x11, 0x6e, 0xf6, 0x7a, 0xda, 0x5c, + 0xe9, 0xd6, 0xf1, 0x49, 0x19, 0xc5, 0xb8, 0x2d, 0x6f, 0xc0, 0xd7, 0x07, 0xbd, 0x0e, 0xe9, 0xcd, + 0x0e, 0xbf, 0xba, 0x64, 0x2c, 0x19, 0x43, 0x6c, 0xd2, 0x80, 0x95, 0x6e, 0xaa, 0xd8, 0x25, 0xae, + 0x58, 0xff, 0xfd, 0x04, 0x64, 0x65, 0x48, 0x3d, 0x73, 0xa1, 0xaa, 0x30, 0x1f, 0x26, 0x7a, 0x32, + 0xce, 0x7f, 0xfb, 0xf2, 0x98, 0xbc, 0xa2, 0x42, 0x68, 0xe9, 0x7e, 0xa1, 0x5c, 0xe9, 0x13, 0x28, + 0xc6, 0x19, 0xdf, 0xc9, 0xf9, 0x7e, 0x15, 0x0a, 0xdc, 0xbf, 0xc3, 0xd8, 0x7c, 0x1d, 0xb2, 0x32, + 0xec, 0x8f, 0x8e, 0xd2, 0xcb, 0x13, 0x04, 0x85, 0x44, 0x0f, 0x61, 0x5e, 0x26, 0x15, 0x61, 0x7d, + 0x6f, 0xf5, 0xea, 0x5d, 0x84, 0x43, 0xb8, 0xfe, 0x39, 0xa4, 0xbb, 0x84, 0xf8, 0xdc, 0xf6, 0x1e, + 0xb5, 0xc9, 0xf4, 0xf6, 0x51, 0xf9, 0x90, 0x4d, 0x5a, 0x0d, 0x9e, 0x0f, 0xd9, 0xa4, 0x65, 0x47, + 0x15, 0x8c, 0x64, 0xac, 0x82, 0xd1, 0x87, 0xe2, 0x53, 0xe2, 0x0c, 0xf6, 0x19, 0xb1, 0x85, 0xa2, + 0xf7, 0x21, 0x3d, 0x22, 0xd1, 0xe0, 0x57, 0x66, 0x3a, 0x18, 0x21, 0x3e, 0x16, 0x28, 0x7e, 0x8e, + 0x1c, 0x09, 0x69, 0x55, 0x55, 0x56, 0x2d, 0xfd, 0xef, 0x92, 0xb0, 0xd8, 0x0a, 0x82, 0xb1, 0xe9, + 0x59, 0x61, 0x60, 0xf2, 0xd9, 0xf9, 0xc0, 0xe4, 0x9d, 0x99, 0x33, 0x3c, 0x27, 0x72, 0xbe, 0x30, + 0xa3, 0x2e, 0x87, 0x64, 0x74, 0x39, 0xe8, 0xff, 0x96, 0x08, 0xab, 0x2f, 0x77, 0x63, 0xdb, 0xbd, + 0xb4, 0x72, 0x7c, 0x52, 0x5e, 0x8e, 0x6b, 0x22, 0x3b, 0xde, 0x81, 0x47, 0x8f, 0x3c, 0xf4, 0x06, + 0x64, 0x70, 0xb3, 0xdd, 0x7c, 0xaa, 0x25, 0xa4, 0x7b, 0x9e, 0x03, 0x61, 0xe2, 0x91, 0x23, 0xae, + 0xa9, 0xdb, 0x6c, 0x37, 0x78, 0x20, 0x91, 0x9c, 0xa1, 0xa9, 0x4b, 0x3c, 0xdb, 0xf1, 0x06, 0xe8, + 0x4d, 0xc8, 0xb6, 0x7a, 0xbd, 0x1d, 0x91, 0x1f, 0xbf, 0x7a, 0x7c, 0x52, 0xbe, 0x79, 0x0e, 0x25, + 0x2a, 0x6f, 0x36, 0x07, 0xf1, 0x28, 0x9e, 0x87, 0x18, 0x33, 0x40, 0x3c, 0x3c, 0x94, 0x20, 0xdc, + 0xe9, 0xf3, 0xe4, 0x3d, 0x33, 0x03, 0x84, 0x29, 0xff, 0xab, 0xb6, 0xdb, 0x3f, 0x25, 0x41, 0xab, + 0x5a, 0x16, 0x19, 0x31, 0xce, 0x57, 0x89, 0x53, 0x1f, 0x72, 0x23, 0xfe, 0xe5, 0x90, 0x30, 0x08, + 0x78, 0x38, 0xf3, 0x5d, 0xe3, 0x82, 0x5c, 0x05, 0x53, 0x97, 0x54, 0xed, 0xa1, 0x13, 0x04, 0x0e, + 0xf5, 0x24, 0x0d, 0x47, 0x9a, 0x4a, 0xff, 0x91, 0x80, 0x9b, 0x33, 0x10, 0xe8, 0x1e, 0xa4, 0x7d, + 0xea, 0x86, 0x6b, 0x78, 0xe7, 0xb2, 0xc2, 0x1a, 0x17, 0xc5, 0x02, 0x89, 0x56, 0x01, 0xcc, 0x31, + 0xa3, 0xa6, 0xe8, 0x5f, 0xac, 0x5e, 0x0e, 0xc7, 0x28, 0xe8, 0x29, 0x64, 0x03, 0x62, 0xf9, 0x24, + 0x0c, 0x15, 0x3f, 0xff, 0xdf, 0x8e, 0xbe, 0xd2, 0x13, 0x6a, 0xb0, 0x52, 0x57, 0xaa, 0x40, 0x56, + 0x52, 0xb8, 0xdb, 0xdb, 0x26, 0x33, 0x55, 0xd9, 0x55, 0x7c, 0x73, 0x6f, 0x32, 0xdd, 0x41, 0xe8, + 0x4d, 0xa6, 0x3b, 0xd0, 0xff, 0x3a, 0x09, 0xd0, 0x7c, 0xc6, 0x88, 0xef, 0x99, 0x6e, 0xbd, 0x8a, + 0x9a, 0xb1, 0xd3, 0x5f, 0xce, 0xf6, 0xdd, 0x99, 0xb5, 0xe4, 0x48, 0xa2, 0x52, 0xaf, 0xce, 0x38, + 0xff, 0x6f, 0x43, 0x6a, 0xec, 0xab, 0xa7, 0x2a, 0x19, 0xe6, 0xed, 0xe0, 0x2d, 0xcc, 0x69, 0xa8, + 0x39, 0x3d, 0xb6, 0x52, 0x97, 0x3f, 0x48, 0xc5, 0x3a, 0x98, 0x79, 0x74, 0xf1, 0x9d, 0x6f, 0x99, + 0x86, 0x45, 0xd4, 0xcd, 0x51, 0x94, 0x3b, 0xbf, 0x5e, 0xad, 0x13, 0x9f, 0xe1, 0xac, 0x65, 0xf2, + 0xff, 0xdf, 0xeb, 0x7c, 0x7b, 0x1f, 0x60, 0x3a, 0x35, 0xb4, 0x0a, 0x99, 0xfa, 0x46, 0xaf, 0xb7, + 0xa5, 0xcd, 0xc9, 0x03, 0x7c, 0xca, 0x12, 0x64, 0xfd, 0xcf, 0x93, 0x90, 0xab, 0x57, 0xd5, 0xb5, + 0x5a, 0x07, 0x4d, 0x9c, 0x4a, 0xa2, 0x58, 0x4d, 0x9e, 0x8d, 0x1c, 0x7f, 0xa2, 0x0e, 0x96, 0x2b, + 0x72, 0xb6, 0x45, 0x2e, 0xc2, 0x47, 0xdd, 0x14, 0x02, 0x08, 0x43, 0x91, 0x28, 0x23, 0x18, 0x96, + 0x19, 0x9e, 0xf1, 0xab, 0x57, 0x1b, 0x4b, 0x46, 0xdf, 0xd3, 0x76, 0x80, 0x0b, 0xa1, 0x92, 0xba, + 0x19, 0xa0, 0x8f, 0x61, 0x29, 0x70, 0x06, 0x9e, 0xe3, 0x0d, 0x8c, 0xd0, 0x78, 0xa2, 0x72, 0x5e, + 0xbb, 0x71, 0x76, 0xba, 0xb6, 0xd0, 0x93, 0x2c, 0x65, 0xc3, 0x05, 0x85, 0xac, 0x0b, 0x53, 0xa2, + 0x8f, 0x60, 0x31, 0x26, 0xca, 0xad, 0x28, 0xcd, 0xae, 0x9d, 0x9d, 0xae, 0x15, 0x23, 0xc9, 0xc7, + 0x64, 0x82, 0x8b, 0x91, 0xe0, 0x63, 0x22, 0xca, 0x0b, 0x7b, 0xd4, 0xb7, 0x88, 0xe1, 0x8b, 0x3d, + 0x2d, 0x6e, 0xf0, 0x34, 0x2e, 0x08, 0x9a, 0xdc, 0xe6, 0xfa, 0x13, 0xb8, 0xd9, 0xf1, 0xad, 0x7d, + 0x12, 0x30, 0x69, 0x0a, 0x65, 0xc5, 0xcf, 0xe1, 0x0e, 0x33, 0x83, 0x03, 0x63, 0xdf, 0x09, 0x18, + 0xf5, 0x27, 0x86, 0x4f, 0x18, 0xf1, 0x38, 0xdf, 0x10, 0xcf, 0x6d, 0xaa, 0xfe, 0x73, 0x9b, 0x63, + 0x36, 0x25, 0x04, 0x87, 0x88, 0x2d, 0x0e, 0xd0, 0x5b, 0x50, 0xe4, 0x51, 0x78, 0x83, 0xec, 0x99, + 0x63, 0x97, 0xf1, 0xd9, 0x83, 0x4b, 0x07, 0xc6, 0x4b, 0x5f, 0x53, 0x79, 0x97, 0x0e, 0xe4, 0xa7, + 0xfe, 0x53, 0xd0, 0x1a, 0x4e, 0x30, 0x32, 0x99, 0xb5, 0x1f, 0x16, 0xb6, 0x50, 0x03, 0xb4, 0x7d, + 0x62, 0xfa, 0x6c, 0x97, 0x98, 0xcc, 0x18, 0x11, 0xdf, 0xa1, 0xf6, 0xf5, 0xab, 0xbc, 0x14, 0x89, + 0x74, 0x85, 0x84, 0xfe, 0x9f, 0x09, 0x00, 0x6c, 0xee, 0x85, 0x11, 0xd9, 0x0f, 0xe1, 0x46, 0xe0, + 0x99, 0xa3, 0x60, 0x9f, 0x32, 0xc3, 0xf1, 0x18, 0xf1, 0x0f, 0x4d, 0x57, 0xd5, 0x27, 0xb4, 0x90, + 0xd1, 0x52, 0x74, 0xf4, 0x3e, 0xa0, 0x03, 0x42, 0x46, 0x06, 0x75, 0x6d, 0x23, 0x64, 0xca, 0xc7, + 0xc0, 0x34, 0xd6, 0x38, 0xa7, 0xe3, 0xda, 0xbd, 0x90, 0x8e, 0x6a, 0xb0, 0xca, 0xa7, 0x4f, 0x3c, + 0xe6, 0x3b, 0x24, 0x30, 0xf6, 0xa8, 0x6f, 0x04, 0x2e, 0x3d, 0x32, 0xf6, 0xa8, 0xeb, 0xd2, 0x23, + 0xe2, 0x87, 0xa5, 0x9f, 0x92, 0x4b, 0x07, 0x4d, 0x09, 0xda, 0xa0, 0x7e, 0xcf, 0xa5, 0x47, 0x1b, + 0x21, 0x82, 0x87, 0x6d, 0xd3, 0x39, 0x33, 0xc7, 0x3a, 0x08, 0xc3, 0xb6, 0x88, 0xda, 0x77, 0xac, + 0x03, 0xf4, 0x26, 0x2c, 0x10, 0x97, 0x88, 0x0a, 0x80, 0x44, 0x65, 0x04, 0xaa, 0x18, 0x12, 0x39, + 0x48, 0xff, 0x02, 0xb4, 0xa6, 0x67, 0xf9, 0x93, 0x51, 0x6c, 0xcd, 0xdf, 0x07, 0xc4, 0x0f, 0x49, + 0xc3, 0xa5, 0xd6, 0x81, 0x31, 0x34, 0x3d, 0x73, 0xc0, 0xc7, 0x25, 0xdf, 0x68, 0x34, 0xce, 0xd9, + 0xa2, 0xd6, 0xc1, 0xb6, 0xa2, 0xeb, 0x1f, 0x03, 0xf4, 0x46, 0x3e, 0x31, 0xed, 0x0e, 0x8f, 0x26, + 0xb8, 0xe9, 0x44, 0xcb, 0xb0, 0xd5, 0x1b, 0x17, 0xf5, 0xd5, 0x56, 0xd7, 0x24, 0xa3, 0x11, 0xd1, + 0xf5, 0xff, 0x0f, 0x37, 0xbb, 0xae, 0x69, 0x89, 0xf7, 0xde, 0x6e, 0xf4, 0xe8, 0x80, 0x1e, 0x42, + 0x56, 0x42, 0xd5, 0x4a, 0xce, 0xdc, 0x6e, 0xd3, 0x3e, 0x37, 0xe7, 0xb0, 0xc2, 0xd7, 0x8a, 0x00, + 0x53, 0x3d, 0xfa, 0x33, 0xc8, 0x47, 0xea, 0x51, 0x19, 0x78, 0x0a, 0xcc, 0xbd, 0xdb, 0xf1, 0x54, + 0xce, 0x9a, 0xc7, 0x71, 0x12, 0x6a, 0x41, 0x61, 0x14, 0x09, 0x5f, 0x19, 0xce, 0xcd, 0x18, 0x34, + 0x8e, 0xcb, 0xea, 0x9f, 0x01, 0xfc, 0x84, 0x3a, 0x5e, 0x9f, 0x1e, 0x10, 0x4f, 0xbc, 0x73, 0xf1, + 0x6c, 0x8d, 0x84, 0x86, 0x50, 0x2d, 0x91, 0x8c, 0x4a, 0x2b, 0x46, 0xcf, 0x3d, 0xb2, 0xa9, 0xff, + 0x55, 0x12, 0xb2, 0x98, 0x52, 0x56, 0xaf, 0xa2, 0x32, 0x64, 0xd5, 0x56, 0x17, 0x57, 0x48, 0x2d, + 0x7f, 0x76, 0xba, 0x96, 0x91, 0x7b, 0x3c, 0x63, 0x89, 0xcd, 0x1d, 0x3b, 0x84, 0x93, 0x97, 0x1d, + 0xc2, 0xe8, 0x1e, 0x14, 0x15, 0xc8, 0xd8, 0x37, 0x83, 0x7d, 0x99, 0x63, 0xd5, 0x16, 0xcf, 0x4e, + 0xd7, 0x40, 0x22, 0x37, 0xcd, 0x60, 0x1f, 0x83, 0x44, 0xf3, 0x6f, 0xd4, 0x84, 0xc2, 0x57, 0xd4, + 0xf1, 0x0c, 0x26, 0x26, 0xa1, 0xca, 0x5d, 0x33, 0x97, 0x62, 0x3a, 0x55, 0xf5, 0xe8, 0x0b, 0x5f, + 0x4d, 0x27, 0xdf, 0x84, 0x05, 0x9f, 0x52, 0x26, 0x4f, 0x1e, 0x87, 0x7a, 0x2a, 0x93, 0x2e, 0xcf, + 0x2c, 0xb0, 0x52, 0xca, 0xb0, 0xc2, 0xe1, 0xa2, 0x1f, 0x6b, 0xa1, 0x7b, 0xb0, 0xec, 0x9a, 0x01, + 0x33, 0xc4, 0x91, 0x65, 0x4f, 0xb5, 0x65, 0xc5, 0x6e, 0x41, 0x9c, 0xb7, 0x21, 0x58, 0xa1, 0x84, + 0xfe, 0x0f, 0x09, 0x28, 0xf0, 0xc9, 0x38, 0x7b, 0x8e, 0xc5, 0xe3, 0xb4, 0xef, 0x1e, 0x3e, 0xdc, + 0x86, 0x94, 0x15, 0xf8, 0xca, 0xa8, 0xe2, 0xfe, 0xac, 0xf7, 0x30, 0xe6, 0x34, 0xf4, 0x05, 0x64, + 0x55, 0x46, 0x2f, 0x23, 0x07, 0xfd, 0xfa, 0x88, 0x52, 0xd9, 0x46, 0xc9, 0x09, 0x7f, 0x9c, 0x8e, + 0x4e, 0x9e, 0xe3, 0x38, 0x4e, 0x42, 0xb7, 0x20, 0x69, 0x49, 0x73, 0xa9, 0x5f, 0x15, 0xd4, 0xdb, + 0x38, 0x69, 0x79, 0xfa, 0xdf, 0x26, 0x60, 0x61, 0xba, 0x67, 0xb9, 0x07, 0xdc, 0x81, 0x7c, 0x30, + 0xde, 0x0d, 0x26, 0x01, 0x23, 0xc3, 0xf0, 0x0d, 0x2f, 0x22, 0xa0, 0x16, 0xe4, 0x4d, 0x77, 0x40, + 0x7d, 0x87, 0xed, 0x0f, 0x55, 0x32, 0x39, 0xfb, 0xb6, 0x8f, 0xeb, 0xac, 0x54, 0x43, 0x11, 0x3c, + 0x95, 0x0e, 0xaf, 0x6e, 0xf9, 0xd0, 0x2b, 0xae, 0xee, 0x37, 0xa0, 0xe8, 0x9a, 0x43, 0x51, 0xe2, + 0x60, 0xce, 0x50, 0xce, 0x23, 0x8d, 0x0b, 0x8a, 0xd6, 0x77, 0x86, 0x44, 0xd7, 0x21, 0x1f, 0x29, + 0x43, 0x4b, 0x50, 0xa8, 0x36, 0x7b, 0xc6, 0xfd, 0xf5, 0x87, 0xc6, 0xa3, 0xfa, 0xb6, 0x36, 0xa7, + 0xc2, 0xcb, 0x3f, 0x4b, 0xc0, 0x82, 0x3a, 0x51, 0x54, 0xc8, 0xfe, 0x26, 0xcc, 0xfb, 0xe6, 0x1e, + 0x0b, 0x93, 0x8a, 0xb4, 0xf4, 0x6a, 0x7e, 0x48, 0xf3, 0xa4, 0x82, 0xb3, 0x66, 0x27, 0x15, 0xb1, + 0x57, 0xe5, 0xd4, 0x95, 0xaf, 0xca, 0xe9, 0xff, 0x93, 0x57, 0x65, 0xfd, 0x4f, 0x92, 0xb0, 0xa4, + 0xa2, 0xbf, 0xe8, 0x00, 0x7b, 0x17, 0xf2, 0x32, 0x10, 0x9c, 0xa6, 0x44, 0xe2, 0x21, 0x53, 0xe2, + 0x5a, 0x0d, 0x9c, 0x93, 0xec, 0x96, 0xcd, 0x73, 0x74, 0x05, 0x8d, 0xfd, 0x00, 0x04, 0x24, 0xa9, + 0xcd, 0x13, 0xcc, 0x06, 0xa4, 0xf7, 0x1c, 0x97, 0x28, 0x3f, 0x9b, 0x59, 0xbe, 0xbe, 0xd0, 0xbd, + 0x78, 0x68, 0xe9, 0x8b, 0x2c, 0x7f, 0x73, 0x0e, 0x0b, 0xe9, 0xd2, 0xaf, 0x03, 0x4c, 0xa9, 0x33, + 0x13, 0x59, 0x1e, 0x2c, 0xaa, 0x9a, 0x60, 0x18, 0x2c, 0xb6, 0x1a, 0x98, 0xd3, 0x38, 0x6b, 0xe0, + 0xd8, 0xea, 0xc8, 0x10, 0xac, 0x47, 0x9c, 0x35, 0x70, 0xec, 0xe8, 0xb5, 0x27, 0x7d, 0xcd, 0x6b, + 0x4f, 0x2d, 0x17, 0x56, 0xa6, 0xf4, 0x2d, 0xb8, 0x55, 0x73, 0x4d, 0xeb, 0xc0, 0x75, 0x02, 0x46, + 0xec, 0xf8, 0x0e, 0x5d, 0x87, 0xec, 0xb9, 0x38, 0xed, 0xaa, 0x42, 0xa0, 0x42, 0xea, 0xff, 0x9a, + 0x80, 0xe2, 0x26, 0x31, 0x5d, 0xb6, 0x3f, 0xad, 0xa6, 0x30, 0x12, 0x30, 0x75, 0xc0, 0x8b, 0x6f, + 0xf4, 0x21, 0xe4, 0xa2, 0x6b, 0xfc, 0xda, 0x17, 0x99, 0x08, 0x8a, 0x1e, 0xc0, 0x3c, 0xf7, 0x69, + 0x3a, 0x0e, 0xf3, 0x83, 0xab, 0x8a, 0xfd, 0x0a, 0xc9, 0x0f, 0x75, 0x9f, 0x88, 0x7b, 0x5b, 0x18, + 0x25, 0x83, 0xc3, 0x26, 0xfa, 0x7f, 0x50, 0x14, 0xb5, 0xea, 0x30, 0x4c, 0xc9, 0x5c, 0xa7, 0xb3, + 0x20, 0x9f, 0x9b, 0x64, 0x88, 0xf2, 0xdf, 0x09, 0x58, 0xde, 0x36, 0x27, 0xbb, 0x44, 0x6d, 0x53, + 0x62, 0x63, 0x62, 0x51, 0xdf, 0x46, 0xdd, 0xf8, 0xf6, 0xbe, 0xe2, 0xf5, 0x6a, 0x96, 0xf0, 0xec, + 0x5d, 0x1e, 0xe6, 0x2c, 0xc9, 0x58, 0xce, 0xb2, 0x0c, 0x19, 0x8f, 0x7a, 0x16, 0x51, 0x7b, 0x5f, + 0x36, 0x74, 0x27, 0xbe, 0xb5, 0x4b, 0xd1, 0xc3, 0x92, 0x78, 0x16, 0x6a, 0x53, 0x16, 0xf5, 0x86, + 0xbe, 0x80, 0x52, 0xaf, 0x59, 0xc7, 0xcd, 0x7e, 0xad, 0xf3, 0x53, 0xa3, 0x57, 0xdd, 0xea, 0x55, + 0xd7, 0xef, 0x19, 0xdd, 0xce, 0xd6, 0x97, 0xf7, 0x1f, 0xdc, 0xfb, 0x50, 0x4b, 0x94, 0xca, 0xc7, + 0x27, 0xe5, 0x3b, 0xed, 0x6a, 0x7d, 0x4b, 0xfa, 0xf2, 0x2e, 0x7d, 0xd6, 0x33, 0xdd, 0xc0, 0x5c, + 0xbf, 0xd7, 0xa5, 0xee, 0x84, 0x63, 0xf4, 0x93, 0x04, 0x14, 0xe3, 0xf7, 0x43, 0xfc, 0xda, 0x4b, + 0x5c, 0x7a, 0xed, 0x4d, 0x6f, 0xcf, 0xe4, 0x25, 0xb7, 0xe7, 0x06, 0x2c, 0x5b, 0x3e, 0x0d, 0x02, + 0x83, 0x07, 0xcc, 0xc4, 0xbe, 0x10, 0x92, 0xbf, 0x72, 0x76, 0xba, 0x76, 0xa3, 0xce, 0xf9, 0x3d, + 0xc1, 0x56, 0xea, 0x6f, 0x58, 0x31, 0x92, 0xe8, 0xe9, 0xbd, 0x5f, 0xa4, 0x20, 0x1f, 0x95, 0x9b, + 0xf9, 0x96, 0xe1, 0xb9, 0xbe, 0x32, 0x45, 0x44, 0x6f, 0x93, 0x23, 0xf4, 0xc6, 0x34, 0xcb, 0xff, + 0x42, 0xbe, 0xaf, 0x45, 0xec, 0x30, 0xc3, 0x7f, 0x0b, 0x72, 0xd5, 0x5e, 0xaf, 0xf5, 0xa8, 0xdd, + 0x6c, 0x68, 0x5f, 0x27, 0x4a, 0xaf, 0x1c, 0x9f, 0x94, 0x6f, 0x44, 0xa0, 0x6a, 0x20, 0x47, 0x2a, + 0x50, 0xf5, 0x7a, 0xb3, 0xdb, 0x6f, 0x36, 0xb4, 0xe7, 0xc9, 0x8b, 0x28, 0x91, 0xb5, 0x8a, 0x57, + 0xf2, 0x7c, 0x17, 0x37, 0xbb, 0x55, 0xcc, 0x3b, 0xfc, 0x3a, 0x29, 0x8b, 0x0f, 0xd3, 0x1e, 0x7d, + 0x32, 0x32, 0x7d, 0xde, 0xe7, 0x6a, 0xf8, 0x6b, 0x91, 0xe7, 0x29, 0xf9, 0x92, 0x3a, 0xad, 0x9d, + 0x13, 0xd3, 0x9e, 0xf0, 0xde, 0xc4, 0xa3, 0x85, 0x50, 0x93, 0xba, 0xd0, 0x5b, 0x8f, 0x3b, 0x2a, + 0xd7, 0xa2, 0xc3, 0x3c, 0xde, 0x69, 0xb7, 0x39, 0xe8, 0x79, 0xfa, 0xc2, 0xec, 0xf0, 0xd8, 0xe3, + 0x19, 0x09, 0xba, 0x0b, 0xb9, 0xf0, 0x4d, 0x43, 0xfb, 0x3a, 0x7d, 0x61, 0x40, 0xf5, 0xf0, 0x41, + 0x46, 0x74, 0xb8, 0xb9, 0xd3, 0x17, 0x3f, 0x66, 0x79, 0x9e, 0xb9, 0xd8, 0xe1, 0xfe, 0x98, 0xd9, + 0xf4, 0xc8, 0xe3, 0x0b, 0xac, 0xea, 0x1c, 0x5f, 0x67, 0x64, 0x52, 0x18, 0x61, 0x54, 0x91, 0xe3, + 0x2d, 0xc8, 0xe1, 0xe6, 0x4f, 0xe4, 0xef, 0x5e, 0x9e, 0x67, 0x2f, 0xe8, 0xc1, 0xe4, 0x2b, 0x62, + 0xa9, 0xde, 0x3a, 0xb8, 0xbb, 0x59, 0x15, 0x26, 0xbf, 0x88, 0xea, 0xf8, 0xa3, 0x7d, 0xd3, 0x23, + 0xf6, 0xf4, 0x39, 0x39, 0x62, 0xbd, 0xf7, 0x4b, 0x90, 0x0b, 0xc3, 0x06, 0xb4, 0x0a, 0xd9, 0xa7, + 0x1d, 0xfc, 0xb8, 0x89, 0xb5, 0x39, 0x69, 0xc3, 0x90, 0xf3, 0x54, 0x06, 0x7c, 0x65, 0x98, 0xdf, + 0xae, 0xb6, 0xab, 0x8f, 0x9a, 0x38, 0x2c, 0x41, 0x86, 0x00, 0x75, 0xf7, 0x95, 0x34, 0xd5, 0x41, + 0xa4, 0xb3, 0xb6, 0xf2, 0xcd, 0xb7, 0xab, 0x73, 0x3f, 0xff, 0x76, 0x75, 0xee, 0xf9, 0xd9, 0x6a, + 0xe2, 0x9b, 0xb3, 0xd5, 0xc4, 0xcf, 0xce, 0x56, 0x13, 0xff, 0x72, 0xb6, 0x9a, 0xd8, 0xcd, 0x8a, + 0x13, 0xe3, 0xc1, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x71, 0x7c, 0x7a, 0x32, 0x4b, 0x2b, 0x00, + 0x00, } diff --git a/api/types.proto b/api/types.proto index 8420ad4839..367a7096a6 100644 --- a/api/types.proto +++ b/api/types.proto @@ -86,6 +86,18 @@ message NodeDescription { // Information about the Docker Engine on the node. EngineDescription engine = 4; + + // Information on the node's TLS setup + NodeTLSInfo tls_info = 5 [(gogoproto.customname) = "TLSInfo"]; +} + +message NodeTLSInfo { + // Information about which root certs the node trusts + bytes trust_root = 1; + + // Information about the node's current TLS certificate + bytes cert_issuer_subject = 2; + bytes cert_issuer_public_key = 3; } message RaftMemberStatus { From 23934090e18f3ee8a0e6ece233011e220af22044 Mon Sep 17 00:00:00 2001 From: cyli Date: Fri, 31 Mar 2017 14:00:04 -0700 Subject: [PATCH 2/6] On cluster updates, the dispatcher will send the current desired root certificate to all connected agents. Signed-off-by: cyli --- ca/testutils/cautils.go | 48 +++++---- manager/dispatcher/dispatcher.go | 63 ++++++++--- manager/dispatcher/dispatcher_test.go | 150 +++++++++++++++++++++++--- 3 files changed, 208 insertions(+), 53 deletions(-) diff --git a/ca/testutils/cautils.go b/ca/testutils/cautils.go index 36d1b24d39..8acd12e2b0 100644 --- a/ca/testutils/cautils.go +++ b/ca/testutils/cautils.go @@ -173,10 +173,8 @@ func NewTestCAFromRootCA(t *testing.T, tempBaseDir string, rootCA ca.RootCA, krw serverOpts := []grpc.ServerOption{grpc.Creds(managerConfig.ServerTLSCreds)} grpcServer := grpc.NewServer(serverOpts...) - managerToken := ca.GenerateJoinToken(&rootCA) - workerToken := ca.GenerateJoinToken(&rootCA) + clusterObj := createClusterObject(t, s, organization, &rootCA, externalCAs...) - createClusterObject(t, s, organization, workerToken, managerToken, externalCAs...) caServer := ca.NewServer(s, managerConfig) caServer.SetReconciliationRetryInterval(50 * time.Millisecond) api.RegisterCAServer(grpcServer, caServer) @@ -235,8 +233,8 @@ func NewTestCAFromRootCA(t *testing.T, tempBaseDir string, rootCA ca.RootCA, krw Server: grpcServer, ServingSecurityConfig: managerConfig, CAServer: caServer, - WorkerToken: workerToken, - ManagerToken: managerToken, + WorkerToken: clusterObj.RootCA.JoinTokens.Worker, + ManagerToken: clusterObj.RootCA.JoinTokens.Manager, ConnBroker: connectionbroker.New(remotes), KeyReadWriter: krw, watchCancel: clusterWatchCancel, @@ -335,27 +333,33 @@ func genSecurityConfig(s *store.MemoryStore, rootCA ca.RootCA, krw *ca.KeyReadWr }) } -func createClusterObject(t *testing.T, s *store.MemoryStore, clusterID, workerToken, managerToken string, externalCAs ...*api.ExternalCA) { - assert.NoError(t, s.Update(func(tx store.Tx) error { - store.CreateCluster(tx, &api.Cluster{ - ID: clusterID, - Spec: api.ClusterSpec{ - Annotations: api.Annotations{ - Name: store.DefaultClusterName, - }, - CAConfig: api.CAConfig{ - ExternalCAs: externalCAs, - }, +func createClusterObject(t *testing.T, s *store.MemoryStore, clusterID string, rootCA *ca.RootCA, externalCAs ...*api.ExternalCA) *api.Cluster { + cluster := &api.Cluster{ + ID: clusterID, + Spec: api.ClusterSpec{ + Annotations: api.Annotations{ + Name: store.DefaultClusterName, }, - RootCA: api.RootCA{ - JoinTokens: api.JoinTokens{ - Worker: workerToken, - Manager: managerToken, - }, + CAConfig: api.CAConfig{ + ExternalCAs: externalCAs, + }, + }, + RootCA: api.RootCA{ + CACert: rootCA.Certs, + JoinTokens: api.JoinTokens{ + Worker: ca.GenerateJoinToken(rootCA), + Manager: ca.GenerateJoinToken(rootCA), }, - }) + }, + } + if s, err := rootCA.Signer(); err == nil && !External { + cluster.RootCA.CAKey = s.Key + } + assert.NoError(t, s.Update(func(tx store.Tx) error { + store.CreateCluster(tx, cluster) return nil })) + return cluster } // CreateRootCertAndKey returns a generated certificate and key for a root CA diff --git a/manager/dispatcher/dispatcher.go b/manager/dispatcher/dispatcher.go index eeec4657b4..c3b4785db7 100644 --- a/manager/dispatcher/dispatcher.go +++ b/manager/dispatcher/dispatcher.go @@ -102,20 +102,29 @@ type nodeUpdate struct { description *api.NodeDescription } +// clusterUpdate is an object that stores an update to the cluster that should trigger +// a new session message. These are pointers to indicate the difference between +// "there is no update" and "update this to nil" +type clusterUpdate struct { + managerUpdate *[]*api.WeightedPeer + bootstrapKeyUpdate *[]*api.EncryptionKey + rootCAUpdate *[]byte +} + // Dispatcher is responsible for dispatching tasks and tracking agent health. type Dispatcher struct { mu sync.Mutex wg sync.WaitGroup nodes *nodeStore store *store.MemoryStore - mgrQueue *watch.Queue lastSeenManagers []*api.WeightedPeer networkBootstrapKeys []*api.EncryptionKey - keyMgrQueue *watch.Queue + lastSeenRootCert []byte config *Config cluster Cluster ctx context.Context cancel context.CancelFunc + clusterUpdateQueue *watch.Queue taskUpdates map[string]*api.TaskStatus // indexed by task ID taskUpdatesLock sync.Mutex @@ -196,6 +205,7 @@ func (d *Dispatcher) Run(ctx context.Context) error { if clusters[0].NetworkBootstrapKeys != nil { d.networkBootstrapKeys = clusters[0].NetworkBootstrapKeys } + d.lastSeenRootCert = clusters[0].RootCA.CACert } return nil }, @@ -205,9 +215,8 @@ func (d *Dispatcher) Run(ctx context.Context) error { d.mu.Unlock() return err } - // set queues here to guarantee that Close will close them - d.mgrQueue = watch.NewQueue() - d.keyMgrQueue = watch.NewQueue() + // set queue here to guarantee that Close will close it + d.clusterUpdateQueue = watch.NewQueue() peerWatcher, peerCancel := d.cluster.SubscribePeers() defer peerCancel() @@ -231,7 +240,7 @@ func (d *Dispatcher) Run(ctx context.Context) error { d.mu.Lock() d.lastSeenManagers = mgrs d.mu.Unlock() - d.mgrQueue.Publish(mgrs) + d.clusterUpdateQueue.Publish(clusterUpdate{managerUpdate: &mgrs}) } batchTimer := time.NewTimer(maxBatchInterval) @@ -259,9 +268,13 @@ func (d *Dispatcher) Run(ctx context.Context) error { d.nodes.updatePeriod(d.config.HeartbeatPeriod, d.config.HeartbeatEpsilon, d.config.GracePeriodMultiplier) } } + d.lastSeenRootCert = cluster.Cluster.RootCA.CACert d.networkBootstrapKeys = cluster.Cluster.NetworkBootstrapKeys d.mu.Unlock() - d.keyMgrQueue.Publish(cluster.Cluster.NetworkBootstrapKeys) + d.clusterUpdateQueue.Publish(clusterUpdate{ + bootstrapKeyUpdate: &cluster.Cluster.NetworkBootstrapKeys, + rootCAUpdate: &cluster.Cluster.RootCA.CACert, + }) case <-ctx.Done(): return nil } @@ -286,8 +299,7 @@ func (d *Dispatcher) Stop() error { d.processUpdatesCond.Broadcast() d.processUpdatesLock.Unlock() - d.mgrQueue.Close() - d.keyMgrQueue.Close() + d.clusterUpdateQueue.Close() d.wg.Wait() @@ -1284,6 +1296,12 @@ func (d *Dispatcher) getNetworkBootstrapKeys() []*api.EncryptionKey { return d.networkBootstrapKeys } +func (d *Dispatcher) getRootCACert() []byte { + d.mu.Lock() + defer d.mu.Unlock() + return d.lastSeenRootCert +} + // Session is a stream which controls agent connection. // Each message contains list of backup Managers with weights. Also there is // a special boolean field Disconnect which if true indicates that node should @@ -1355,14 +1373,13 @@ func (d *Dispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_Sessio Node: nodeObj, Managers: d.getManagers(), NetworkBootstrapKeys: d.getNetworkBootstrapKeys(), + RootCA: d.getRootCACert(), }); err != nil { return err } - managerUpdates, mgrCancel := d.mgrQueue.Watch() - defer mgrCancel() - keyMgrUpdates, keyMgrCancel := d.keyMgrQueue.Watch() - defer keyMgrCancel() + clusterUpdatesCh, clusterCancel := d.clusterUpdateQueue.Watch() + defer clusterCancel() // disconnectNode is a helper forcibly shutdown connection disconnectNode := func() error { @@ -1396,11 +1413,21 @@ func (d *Dispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_Sessio disconnect bool mgrs []*api.WeightedPeer netKeys []*api.EncryptionKey + rootCert []byte ) select { - case ev := <-managerUpdates: - mgrs = ev.([]*api.WeightedPeer) + case ev := <-clusterUpdatesCh: + update := ev.(clusterUpdate) + if update.managerUpdate != nil { + mgrs = *update.managerUpdate + } + if update.bootstrapKeyUpdate != nil { + netKeys = *update.bootstrapKeyUpdate + } + if update.rootCAUpdate != nil { + rootCert = *update.rootCAUpdate + } case ev := <-nodeUpdates: nodeObj = ev.(api.EventUpdateNode).Node case <-stream.Context().Done(): @@ -1409,8 +1436,6 @@ func (d *Dispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_Sessio disconnect = true case <-dctx.Done(): disconnect = true - case ev := <-keyMgrUpdates: - netKeys = ev.([]*api.EncryptionKey) } if mgrs == nil { mgrs = d.getManagers() @@ -1418,12 +1443,16 @@ func (d *Dispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_Sessio if netKeys == nil { netKeys = d.getNetworkBootstrapKeys() } + if rootCert == nil { + rootCert = d.getRootCACert() + } if err := stream.Send(&api.SessionMessage{ SessionID: sessionID, Node: nodeObj, Managers: mgrs, NetworkBootstrapKeys: netKeys, + RootCA: rootCert, }); err != nil { return err } diff --git a/manager/dispatcher/dispatcher_test.go b/manager/dispatcher/dispatcher_test.go index 588cedbce7..c4738e05ac 100644 --- a/manager/dispatcher/dispatcher_test.go +++ b/manager/dispatcher/dispatcher_test.go @@ -2,8 +2,10 @@ package dispatcher import ( "crypto/tls" + "errors" "fmt" "net" + "sync" "testing" "time" @@ -17,9 +19,12 @@ import ( "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" "github.com/docker/swarmkit/ca/testutils" + "github.com/docker/swarmkit/identity" raftutils "github.com/docker/swarmkit/manager/state/raft/testutils" "github.com/docker/swarmkit/manager/state/store" + digest "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type grpcDispatcher struct { @@ -30,6 +35,7 @@ type grpcDispatcher struct { dispatcherServer *Dispatcher conns []*grpc.ClientConn testCA *testutils.TestCA + testCluster *testCluster } func (gd *grpcDispatcher) Close() { @@ -43,32 +49,73 @@ func (gd *grpcDispatcher) Close() { } type testCluster struct { - addr string - store *store.MemoryStore + mu sync.Mutex + addr string + store *store.MemoryStore + subscriptions map[string]chan events.Event + peers []*api.Peer + members map[uint64]*api.RaftMember } -func (t *testCluster) GetMemberlist() map[uint64]*api.RaftMember { - return map[uint64]*api.RaftMember{ - 1: { - NodeID: "1", - Addr: t.addr, +func newTestCluster(addr string, s *store.MemoryStore) *testCluster { + return &testCluster{ + addr: addr, + store: s, + subscriptions: make(map[string]chan events.Event), + peers: []*api.Peer{ + { + Addr: addr, + NodeID: "1", + }, + }, + members: map[uint64]*api.RaftMember{ + 1: { + NodeID: "1", + Addr: addr, + }, }, } } +func (t *testCluster) GetMemberlist() map[uint64]*api.RaftMember { + t.mu.Lock() + defer t.mu.Unlock() + return t.members +} + func (t *testCluster) SubscribePeers() (chan events.Event, func()) { + t.mu.Lock() + defer t.mu.Unlock() ch := make(chan events.Event, 1) - ch <- []*api.Peer{ - { - Addr: t.addr, - NodeID: "1", - }, - } + id := identity.NewID() + t.subscriptions[id] = ch + ch <- t.peers return ch, func() { + t.mu.Lock() + defer t.mu.Unlock() + delete(t.subscriptions, id) close(ch) } } +func (t *testCluster) addMember(addr string) { + t.mu.Lock() + defer t.mu.Unlock() + id := uint64(len(t.members) + 1) + strID := fmt.Sprintf("%d", id) + t.members[id] = &api.RaftMember{ + NodeID: strID, + Addr: addr, + } + t.peers = append(t.peers, &api.Peer{ + Addr: addr, + NodeID: strID, + }) + for _, ch := range t.subscriptions { + ch <- t.peers + } +} + func (t *testCluster) MemoryStore() *store.MemoryStore { return t.store } @@ -96,7 +143,7 @@ func startDispatcher(c *Config) (*grpcDispatcher, error) { serverOpts := []grpc.ServerOption{grpc.Creds(managerSecurityConfig.ServerTLSCreds)} s := grpc.NewServer(serverOpts...) - tc := &testCluster{addr: l.Addr().String(), store: tca.MemoryStore} + tc := newTestCluster(l.Addr().String(), tca.MemoryStore) d := New(tc, c) authorize := func(ctx context.Context, roles []string) error { @@ -154,6 +201,7 @@ func startDispatcher(c *Config) (*grpcDispatcher, error) { conns: conns, grpcServer: s, testCA: tca, + testCluster: tc, }, nil } @@ -1339,3 +1387,77 @@ func TestOldTasksNoCert(t *testing.T) { assert.Nil(t, resp) assert.EqualError(t, err, "rpc error: code = 7 desc = Permission denied: unauthorized peer role: rpc error: code = 7 desc = no client certificates in request") } + +func TestClusterUpdatesSendMessages(t *testing.T) { + cfg := DefaultConfig() + cfg.RateLimitPeriod = 0 + gd, err := startDispatcher(cfg) + require.NoError(t, err) + defer gd.Close() + + stream, err := gd.Clients[0].Session(context.Background(), &api.SessionRequest{}) + require.NoError(t, err) + defer stream.CloseSend() + + var msg *api.SessionMessage + { + msg, err = stream.Recv() + require.NoError(t, err) + require.NotEmpty(t, msg.SessionID) + require.NotNil(t, msg.Node) + require.Len(t, msg.Managers, 1) + require.Empty(t, msg.NetworkBootstrapKeys) + require.Equal(t, gd.testCA.RootCA.Certs, msg.RootCA) + } + + // changing the network bootstrap keys results in a new message with updated keys + expected := msg.Copy() + expected.NetworkBootstrapKeys = []*api.EncryptionKey{ + {Key: []byte("network key1")}, + {Key: []byte("network key2")}, + } + require.NoError(t, gd.Store.Update(func(tx store.Tx) error { + cluster := store.GetCluster(tx, gd.testCA.Organization) + if cluster == nil { + return errors.New("no cluster") + } + cluster.NetworkBootstrapKeys = expected.NetworkBootstrapKeys + return store.UpdateCluster(tx, cluster) + })) + time.Sleep(100 * time.Millisecond) + { + msg, err = stream.Recv() + require.NoError(t, err) + require.Equal(t, expected, msg) + } + + // changing the peers results in a new message with updated managers + gd.testCluster.addMember("1.1.1.1") + time.Sleep(100 * time.Millisecond) + { + msg, err = stream.Recv() + require.NoError(t, err) + require.Len(t, msg.Managers, 2) + expected.Managers = msg.Managers + require.Equal(t, expected, msg) + } + + // changing the rootCA cert and has in the cluster results in a new message with an updated cert + expected = msg.Copy() + expected.RootCA = testutils.ECDSA256SHA256Cert + require.NoError(t, gd.Store.Update(func(tx store.Tx) error { + cluster := store.GetCluster(tx, gd.testCA.Organization) + if cluster == nil { + return errors.New("no cluster") + } + cluster.RootCA.CACert = testutils.ECDSA256SHA256Cert + cluster.RootCA.CACertHash = digest.FromBytes(testutils.ECDSA256SHA256Cert).String() + return store.UpdateCluster(tx, cluster) + })) + time.Sleep(100 * time.Millisecond) + { + msg, err = stream.Recv() + require.NoError(t, err) + require.Equal(t, expected, msg) + } +} From 4fdfee544004602faddedc3dd775dcf02545a449 Mon Sep 17 00:00:00 2001 From: cyli Date: Fri, 31 Mar 2017 21:32:25 -0700 Subject: [PATCH 3/6] Allow setting a watch object on the security config, so that watchers can be notified of any changes. Signed-off-by: cyli --- ca/config.go | 22 ++++++++++++++++++-- ca/config_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/ca/config.go b/ca/config.go index 6fcc5cb362..90db943082 100644 --- a/ca/config.go +++ b/ca/config.go @@ -19,6 +19,7 @@ import ( "github.com/docker/swarmkit/connectionbroker" "github.com/docker/swarmkit/identity" "github.com/docker/swarmkit/log" + "github.com/docker/swarmkit/watch" "github.com/opencontainers/go-digest" "github.com/pkg/errors" "google.golang.org/grpc/credentials" @@ -78,6 +79,9 @@ type SecurityConfig struct { ServerTLSCreds *MutableTLSCreds ClientTLSCreds *MutableTLSCreds + + // An optional queue for anyone interested in subscribing to SecurityConfig updates + queue *watch.Queue } // CertificateUpdate represents a change in the underlying TLS configuration being returned by @@ -158,6 +162,13 @@ func (s *SecurityConfig) UpdateRootCA(rootCA *RootCA, externalCARootPool *x509.C return s.updateTLSCredentials(s.certificate, s.issuerInfo) } +// SetWatch allows you to set a watch on the security config, in order to be notified of any changes +func (s *SecurityConfig) SetWatch(q *watch.Queue) { + s.mu.Lock() + defer s.mu.Unlock() + s.queue = q +} + // IssuerInfo returns the issuer subject and issuer public key func (s *SecurityConfig) IssuerInfo() *IssuerInfo { s.mu.Lock() @@ -165,8 +176,7 @@ func (s *SecurityConfig) IssuerInfo() *IssuerInfo { return s.issuerInfo } -// updateTLSCredentials updates the client, server, and TLS credentials on a security config. This function expects -// something else to have taken out a lock on the SecurityConfig. +// This function expects something else to have taken out a lock on the SecurityConfig. func (s *SecurityConfig) updateTLSCredentials(certificate *tls.Certificate, issuerInfo *IssuerInfo) error { certs := []tls.Certificate{*certificate} clientConfig, err := NewClientTLSConfig(certs, s.rootCA.Pool, ManagerRole) @@ -197,6 +207,13 @@ func (s *SecurityConfig) updateTLSCredentials(certificate *tls.Certificate, issu s.certificate = certificate s.issuerInfo = issuerInfo + if s.queue != nil { + s.queue.Publish(&api.NodeTLSInfo{ + TrustRoot: s.rootCA.Certs, + CertIssuerPublicKey: s.issuerInfo.PublicKey, + CertIssuerSubject: s.issuerInfo.Subject, + }) + } return nil } @@ -445,6 +462,7 @@ func RenewTLSConfigNow(ctx context.Context, s *SecurityConfig, connBroker *conne log.WithError(err).Errorf("failed to renew the certificate") return err } + s.mu.Lock() defer s.mu.Unlock() return s.updateTLSCredentials(tlsKeyPair, issuerInfo) diff --git a/ca/config_test.go b/ca/config_test.go index 485a7181cb..ceb2387307 100644 --- a/ca/config_test.go +++ b/ca/config_test.go @@ -17,9 +17,11 @@ import ( cfconfig "github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/helpers" + "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" "github.com/docker/swarmkit/ca/testutils" "github.com/docker/swarmkit/manager/state/store" + "github.com/docker/swarmkit/watch" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -396,6 +398,57 @@ func TestSecurityConfigUpdateRootCA(t *testing.T) { } } +func TestSecurityConfigSetWatch(t *testing.T) { + tc := testutils.NewTestCA(t) + defer tc.Stop() + + secConfig, err := tc.NewNodeConfig(ca.ManagerRole) + require.NoError(t, err) + issuer := secConfig.IssuerInfo() + + w := watch.NewQueue() + defer w.Close() + secConfig.SetWatch(w) + + configWatch, configCancel := w.Watch() + defer configCancel() + + require.NoError(t, ca.RenewTLSConfigNow(context.Background(), secConfig, tc.ConnBroker)) + select { + case ev := <-configWatch: + nodeTLSInfo, ok := ev.(*api.NodeTLSInfo) + require.True(t, ok) + require.Equal(t, &api.NodeTLSInfo{ + TrustRoot: tc.RootCA.Certs, + CertIssuerPublicKey: issuer.PublicKey, + CertIssuerSubject: issuer.Subject, + }, nodeTLSInfo) + case <-time.After(time.Second): + require.FailNow(t, "on TLS certificate update, we should have gotten a security config update") + } + + require.NoError(t, secConfig.UpdateRootCA(&tc.RootCA, tc.RootCA.Pool)) + select { + case ev := <-configWatch: + nodeTLSInfo, ok := ev.(*api.NodeTLSInfo) + require.True(t, ok) + require.Equal(t, &api.NodeTLSInfo{ + TrustRoot: tc.RootCA.Certs, + CertIssuerPublicKey: issuer.PublicKey, + CertIssuerSubject: issuer.Subject, + }, nodeTLSInfo) + case <-time.After(time.Second): + require.FailNow(t, "on TLS certificate update, we should have gotten a security config update") + } + + configCancel() + w.Close() + + // ensure that we can still update tls certs and roots without error even though the watch is closed + require.NoError(t, secConfig.UpdateRootCA(&tc.RootCA, tc.RootCA.Pool)) + require.NoError(t, ca.RenewTLSConfigNow(context.Background(), secConfig, tc.ConnBroker)) +} + // enforce that no matter what order updating the root CA and updating TLS credential happens, we // end up with a security config that has updated certs, and an updated root pool func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { From 46b0f29d3a7e706146d269a635eb8620676deabb Mon Sep 17 00:00:00 2001 From: cyli Date: Mon, 3 Apr 2017 13:15:39 -0700 Subject: [PATCH 4/6] Update agent to send the node TLS information along with the node description, and to restart the session if the information has changed. Signed-off-by: cyli --- agent/agent.go | 134 +++++++++++-------- agent/agent_test.go | 273 +++++++++++++++++++++++++++++++-------- agent/config.go | 20 ++- agent/testutils/fakes.go | 219 +++++++++++++++++++++++++++++++ codecov.yml | 2 + integration/exec.go | 94 -------------- integration/node.go | 3 +- 7 files changed, 545 insertions(+), 200 deletions(-) create mode 100644 agent/testutils/fakes.go delete mode 100644 integration/exec.go diff --git a/agent/agent.go b/agent/agent.go index 3721f2916a..295366b2e8 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -1,6 +1,7 @@ package agent import ( + "bytes" "fmt" "math/rand" "reflect" @@ -44,6 +45,8 @@ type Agent struct { stopOnce sync.Once // only allow stop to be called once closed chan struct{} // only closed in run err error // read only after closed is closed + + nodeUpdatePeriod time.Duration } // New returns a new agent, ready for task dispatch. @@ -53,14 +56,15 @@ func New(config *Config) (*Agent, error) { } a := &Agent{ - config: config, - sessionq: make(chan sessionOperation), - started: make(chan struct{}), - leaving: make(chan struct{}), - left: make(chan struct{}), - stopped: make(chan struct{}), - closed: make(chan struct{}), - ready: make(chan struct{}), + config: config, + sessionq: make(chan sessionOperation), + started: make(chan struct{}), + leaving: make(chan struct{}), + left: make(chan struct{}), + stopped: make(chan struct{}), + closed: make(chan struct{}), + ready: make(chan struct{}), + nodeUpdatePeriod: nodeUpdatePeriod, } a.worker = newWorker(config.DB, config.Executor, a) @@ -182,13 +186,15 @@ func (a *Agent) run(ctx context.Context) { log.G(ctx).Debug("(*Agent).run") defer log.G(ctx).Debug("(*Agent).run exited") + nodeTLSInfo := a.config.NodeTLSInfo + // get the node description - nodeDescription, err := a.nodeDescriptionWithHostname(ctx) + nodeDescription, err := a.nodeDescriptionWithHostname(ctx, nodeTLSInfo) if err != nil { log.G(ctx).WithError(err).WithField("agent", a.config.Executor).Error("agent: node description unavailable") } // nodeUpdateTicker is used to periodically check for updates to node description - nodeUpdateTicker := time.NewTicker(nodeUpdatePeriod) + nodeUpdateTicker := time.NewTicker(a.nodeUpdatePeriod) defer nodeUpdateTicker.Stop() var ( @@ -214,6 +220,35 @@ func (a *Agent) run(ctx context.Context) { a.worker.Listen(ctx, reporter) + updateNode := func() { + // skip updating if the registration isn't finished + if registered != nil { + return + } + // get the current node description + newNodeDescription, err := a.nodeDescriptionWithHostname(ctx, nodeTLSInfo) + if err != nil { + log.G(ctx).WithError(err).WithField("agent", a.config.Executor).Error("agent: updated node description unavailable") + } + + // if newNodeDescription is nil, it will cause a panic when + // trying to create a session. Typically this can happen + // if the engine goes down + if newNodeDescription == nil { + return + } + + // if the node description has changed, update it to the new one + // and close the session. The old session will be stopped and a + // new one will be created with the updated description + if !reflect.DeepEqual(nodeDescription, newNodeDescription) { + nodeDescription = newNodeDescription + // close the session + log.G(ctx).Info("agent: found node update") + session.sendError(nil) + } + } + for { select { case operation := <-sessionq: @@ -247,7 +282,7 @@ func (a *Agent) run(ctx context.Context) { } } case msg := <-session.messages: - if err := a.handleSessionMessage(ctx, msg); err != nil { + if err := a.handleSessionMessage(ctx, msg, nodeTLSInfo); err != nil { log.G(ctx).WithError(err).Error("session message handler failed") } case sub := <-session.subscriptions: @@ -305,33 +340,17 @@ func (a *Agent) run(ctx context.Context) { } session = newSession(ctx, a, delay, session.sessionID, nodeDescription) registered = session.registered - case <-nodeUpdateTicker.C: - // skip this case if the registration isn't finished - if registered != nil { - continue - } - // get the current node description - newNodeDescription, err := a.nodeDescriptionWithHostname(ctx) - if err != nil { - log.G(ctx).WithError(err).WithField("agent", a.config.Executor).Error("agent: updated node description unavailable") - } - - // if newNodeDescription is nil, it will cause a panic when - // trying to create a session. Typically this can happen - // if the engine goes down - if newNodeDescription == nil { - continue - } - - // if the node description has changed, update it to the new one - // and close the session. The old session will be stopped and a - // new one will be created with the updated description - if !reflect.DeepEqual(nodeDescription, newNodeDescription) { - nodeDescription = newNodeDescription - // close the session - log.G(ctx).Info("agent: found node update") - session.sendError(nil) + case ev := <-a.config.NotifyTLSChange: + // the TLS info has changed, so force a check to see if we need to restart the session + if tlsInfo, ok := ev.(*api.NodeTLSInfo); ok { + nodeTLSInfo = tlsInfo + updateNode() + nodeUpdateTicker.Stop() + nodeUpdateTicker = time.NewTicker(a.nodeUpdatePeriod) } + case <-nodeUpdateTicker.C: + // periodically check to see whether the node information has changed, and if so, restart the session + updateNode() case <-a.stopped: // TODO(stevvooe): Wait on shutdown and cleanup. May need to pump // this loop a few times. @@ -347,7 +366,7 @@ func (a *Agent) run(ctx context.Context) { } } -func (a *Agent) handleSessionMessage(ctx context.Context, message *api.SessionMessage) error { +func (a *Agent) handleSessionMessage(ctx context.Context, message *api.SessionMessage, nti *api.NodeTLSInfo) error { seen := map[api.Peer]struct{}{} for _, manager := range message.Managers { if manager.Peer.Addr == "" { @@ -358,18 +377,28 @@ func (a *Agent) handleSessionMessage(ctx context.Context, message *api.SessionMe seen[*manager.Peer] = struct{}{} } - if message.Node != nil { - if a.node == nil || !nodesEqual(a.node, message.Node) { - if a.config.NotifyNodeChange != nil { - a.config.NotifyNodeChange <- message.Node.Copy() - } - a.node = message.Node.Copy() - if err := a.config.Executor.Configure(ctx, a.node); err != nil { - log.G(ctx).WithError(err).Error("node configure failed") - } + var changes *NodeChanges + if message.Node != nil && (a.node == nil || !nodesEqual(a.node, message.Node)) { + if a.config.NotifyNodeChange != nil { + changes = &NodeChanges{Node: message.Node.Copy()} + } + a.node = message.Node.Copy() + if err := a.config.Executor.Configure(ctx, a.node); err != nil { + log.G(ctx).WithError(err).Error("node configure failed") + } + } + if len(message.RootCA) > 0 && !bytes.Equal(message.RootCA, nti.TrustRoot) { + if changes == nil { + changes = &NodeChanges{RootCert: message.RootCA} + } else { + changes.RootCert = message.RootCA } } + if changes != nil { + a.config.NotifyNodeChange <- changes + } + // prune managers not in list. for peer := range a.config.ConnBroker.Remotes().Weights() { if _, ok := seen[peer]; !ok { @@ -517,12 +546,15 @@ func (a *Agent) Publisher(ctx context.Context, subscriptionID string) (exec.LogP } // nodeDescriptionWithHostname retrieves node description, and overrides hostname if available -func (a *Agent) nodeDescriptionWithHostname(ctx context.Context) (*api.NodeDescription, error) { +func (a *Agent) nodeDescriptionWithHostname(ctx context.Context, tlsInfo *api.NodeTLSInfo) (*api.NodeDescription, error) { desc, err := a.config.Executor.Describe(ctx) - // Override hostname - if a.config.Hostname != "" && desc != nil { - desc.Hostname = a.config.Hostname + // Override hostname and TLS info + if desc != nil { + if a.config.Hostname != "" && desc != nil { + desc.Hostname = a.config.Hostname + } + desc.TLSInfo = tlsInfo } return desc, err } diff --git a/agent/agent_test.go b/agent/agent_test.go index 2e01a0b3c9..9ef53cbfbd 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1,40 +1,23 @@ package agent import ( + "errors" "testing" "time" - "github.com/docker/swarmkit/agent/exec" + events "github.com/docker/go-events" + agentutils "github.com/docker/swarmkit/agent/testutils" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" "github.com/docker/swarmkit/ca/testutils" "github.com/docker/swarmkit/connectionbroker" + raftutils "github.com/docker/swarmkit/manager/state/raft/testutils" "github.com/docker/swarmkit/remotes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/context" ) -// NoopExecutor is a dummy executor that implements enough to get the agent started. -type NoopExecutor struct { -} - -func (e *NoopExecutor) Describe(ctx context.Context) (*api.NodeDescription, error) { - return &api.NodeDescription{}, nil -} - -func (e *NoopExecutor) Configure(ctx context.Context, node *api.Node) error { - return nil -} - -func (e *NoopExecutor) Controller(t *api.Task) (exec.Controller, error) { - return nil, exec.ErrRuntimeUnsupported -} - -func (e *NoopExecutor) SetNetworkBootstrapKeys([]*api.EncryptionKey) error { - return nil -} - func TestAgent(t *testing.T) { // TODO(stevvooe): The current agent is fairly monolithic, making it hard // to test without implementing or mocking an entire master. We'd like to @@ -73,10 +56,11 @@ func TestAgentStartStop(t *testing.T) { defer cleanup() agent, err := New(&Config{ - Executor: &NoopExecutor{}, + Executor: &agentutils.TestExecutor{}, ConnBroker: connectionbroker.New(remotes), Credentials: agentSecurityConfig.ClientTLSCreds, DB: db, + NodeTLSInfo: &api.NodeTLSInfo{}, }) require.NoError(t, err) assert.NotNil(t, agent) @@ -93,69 +77,252 @@ func TestAgentStartStop(t *testing.T) { assert.NoError(t, agent.Stop(ctx)) } -func TestHandleSessionMessage(t *testing.T) { - // TODO(rajdeepd): The current agent is fairly monolithic, hence we - // have to test message handling this way. Needs to be refactored - ctx, _ := context.WithTimeout(context.Background(), 5000*time.Millisecond) - agent, cleanup := agentTestEnv(t) +func TestHandleSessionMessageNetworkManagerChanges(t *testing.T) { + nodeChangeCh := make(chan *NodeChanges, 1) + defer close(nodeChangeCh) + tester := agentTestEnv(t, nodeChangeCh, nil) + defer tester.cleanup() - defer cleanup() + currSession, closedSessions := tester.dispatcher.GetSessions() + require.NotNil(t, currSession) + require.NotNil(t, currSession.Description) + require.Empty(t, closedSessions) var messages = []*api.SessionMessage{ - {SessionID: "sm1", Node: &api.Node{}, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "node1", Addr: "10.0.0.1"}, 1.0}}, - NetworkBootstrapKeys: []*api.EncryptionKey{{}}}, - {SessionID: "sm1", Node: &api.Node{}, + NetworkBootstrapKeys: []*api.EncryptionKey{{}}, + }, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "node1", Addr: ""}, 1.0}}, - NetworkBootstrapKeys: []*api.EncryptionKey{{}}}, - {SessionID: "sm1", Node: &api.Node{}, + NetworkBootstrapKeys: []*api.EncryptionKey{{}}, + }, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "node1", Addr: "10.0.0.1"}, 1.0}}, - NetworkBootstrapKeys: nil}, - {SessionID: "sm1", Node: &api.Node{}, + NetworkBootstrapKeys: nil, + }, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "", Addr: "10.0.0.1"}, 1.0}}, - NetworkBootstrapKeys: []*api.EncryptionKey{{}}}, - {SessionID: "sm1", Node: &api.Node{}, + NetworkBootstrapKeys: []*api.EncryptionKey{{}}, + }, + { Managers: []*api.WeightedPeer{ {&api.Peer{NodeID: "node1", Addr: "10.0.0.1"}, 0.0}}, - NetworkBootstrapKeys: []*api.EncryptionKey{{}}}, + NetworkBootstrapKeys: []*api.EncryptionKey{{}}, + }, } for _, m := range messages { - err := agent.handleSessionMessage(ctx, m) - if err != nil { - t.Fatal("err should be nil") + m.SessionID = currSession.SessionID + tester.dispatcher.SessionMessageChannel() <- m + select { + case nodeChange := <-nodeChangeCh: + require.FailNow(t, "there should be no node changes with these messages: %v", nodeChange) + case <-time.After(100 * time.Millisecond): } } + + currSession, closedSessions = tester.dispatcher.GetSessions() + require.NotEmpty(t, currSession) + require.Empty(t, closedSessions) +} + +func TestHandleSessionMessageNodeChanges(t *testing.T) { + nodeChangeCh := make(chan *NodeChanges, 1) + defer close(nodeChangeCh) + tester := agentTestEnv(t, nodeChangeCh, nil) + defer tester.cleanup() + + currSession, closedSessions := tester.dispatcher.GetSessions() + require.NotNil(t, currSession) + require.NotNil(t, currSession.Description) + require.Empty(t, closedSessions) + + var testcases = []struct { + msg *api.SessionMessage + change *NodeChanges + errorMsg string + }{ + { + msg: &api.SessionMessage{ + Node: &api.Node{}, + }, + change: &NodeChanges{Node: &api.Node{}}, + errorMsg: "the node changed, but no notification of node change", + }, + { + msg: &api.SessionMessage{ + RootCA: []byte("new root CA"), + }, + change: &NodeChanges{RootCert: []byte("new root CA")}, + errorMsg: "the root cert changed, but no notification of node change", + }, + { + msg: &api.SessionMessage{ + Node: &api.Node{ID: "something"}, + RootCA: []byte("new root CA"), + }, + change: &NodeChanges{ + Node: &api.Node{ID: "something"}, + RootCert: []byte("new root CA"), + }, + errorMsg: "the root cert and node both changed, but no notification of node change", + }, + { + msg: &api.SessionMessage{ + Node: &api.Node{ID: "something"}, + RootCA: tester.testCA.RootCA.Certs, + }, + errorMsg: "while a node and root cert were provided, nothing has changed so no node changed", + }, + } + + for _, tc := range testcases { + tc.msg.SessionID = currSession.SessionID + tester.dispatcher.SessionMessageChannel() <- tc.msg + if tc.change != nil { + select { + case nodeChange := <-nodeChangeCh: + require.Equal(t, tc.change, nodeChange, tc.errorMsg) + case <-time.After(100 * time.Millisecond): + require.FailNow(t, tc.errorMsg) + } + } else { + select { + case nodeChange := <-nodeChangeCh: + require.FailNow(t, "%s: but got change: %v", tc.errorMsg, nodeChange) + case <-time.After(100 * time.Millisecond): + } + } + } + + currSession, closedSessions = tester.dispatcher.GetSessions() + require.NotEmpty(t, currSession) + require.Empty(t, closedSessions) } -func agentTestEnv(t *testing.T) (*Agent, func()) { +// when the node description changes, the session is restarted and propagated up to the dispatcher +func TestSessionRestartedOnNodeDescriptionChange(t *testing.T) { + tlsCh := make(chan events.Event, 1) + defer close(tlsCh) + tester := agentTestEnv(t, nil, tlsCh) + defer tester.cleanup() + + currSession, closedSessions := tester.dispatcher.GetSessions() + require.NotNil(t, currSession) + require.NotNil(t, currSession.Description) + require.Empty(t, closedSessions) + + tester.executor.UpdateNodeDescription(&api.NodeDescription{ + Hostname: "testAgent", + }) + var gotSession *api.SessionRequest + require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error { + gotSession, closedSessions = tester.dispatcher.GetSessions() + if gotSession == nil || len(closedSessions) != 1 { + return errors.New("session has not been restarted yet") + } + return nil + }, 1*time.Second)) + require.NotEqual(t, currSession, gotSession) + require.NotNil(t, gotSession.Description) + require.Equal(t, "testAgent", gotSession.Description.Hostname) + currSession = gotSession + + newTLSInfo := &api.NodeTLSInfo{ + TrustRoot: testutils.ECDSA256SHA256Cert, + CertIssuerPublicKey: []byte("public key"), + CertIssuerSubject: []byte("subject"), + } + tlsCh <- newTLSInfo + require.NoError(t, raftutils.PollFuncWithTimeout(nil, func() error { + gotSession, closedSessions = tester.dispatcher.GetSessions() + if gotSession == nil || len(closedSessions) != 2 { + return errors.New("session has not been restarted yet") + } + return nil + }, 1*time.Second)) + require.NotEqual(t, currSession, gotSession) + require.NotNil(t, gotSession.Description) + require.Equal(t, "testAgent", gotSession.Description.Hostname) + require.Equal(t, newTLSInfo, gotSession.Description.TLSInfo) +} + +type agentTester struct { + agent *Agent + dispatcher *agentutils.MockDispatcher + executor *agentutils.TestExecutor + cleanup func() + testCA *testutils.TestCA +} + +func agentTestEnv(t *testing.T, nodeChangeCh chan *NodeChanges, tlsChangeCh chan events.Event) *agentTester { var cleanup []func() tc := testutils.NewTestCA(t) - cleanup = append(cleanup, func() { tc.Stop() }) + cleanup = append(cleanup, tc.Stop) agentSecurityConfig, err := tc.NewNodeConfig(ca.WorkerRole) require.NoError(t, err) + managerSecurityConfig, err := tc.NewNodeConfig(ca.ManagerRole) + require.NoError(t, err) - addr := "localhost:4949" - remotes := remotes.NewRemotes(api.Peer{Addr: addr}) + mockDispatcher, mockDispatcherStop := agentutils.NewMockDispatcher(t, managerSecurityConfig) + cleanup = append(cleanup, mockDispatcherStop) + + remotes := remotes.NewRemotes(api.Peer{Addr: mockDispatcher.Addr}) db, cleanupStorage := storageTestEnv(t) cleanup = append(cleanup, func() { cleanupStorage() }) + executor := &agentutils.TestExecutor{} + agent, err := New(&Config{ - Executor: &NoopExecutor{}, - ConnBroker: connectionbroker.New(remotes), - Credentials: agentSecurityConfig.ClientTLSCreds, - DB: db, + Executor: executor, + ConnBroker: connectionbroker.New(remotes), + Credentials: agentSecurityConfig.ClientTLSCreds, + DB: db, + NotifyNodeChange: nodeChangeCh, + NotifyTLSChange: tlsChangeCh, + NodeTLSInfo: &api.NodeTLSInfo{ + TrustRoot: tc.RootCA.Certs, + CertIssuerPublicKey: agentSecurityConfig.IssuerInfo().PublicKey, + CertIssuerSubject: agentSecurityConfig.IssuerInfo().Subject, + }, }) require.NoError(t, err) - return agent, func() { - for i := len(cleanup) - 1; i >= 0; i-- { - cleanup[i]() - } + agent.nodeUpdatePeriod = 200 * time.Millisecond + + go agent.Start(context.Background()) + cleanup = append(cleanup, func() { + agent.Stop(context.Background()) + }) + + getErr := make(chan error) + go func() { + getErr <- agent.Err(context.Background()) + }() + select { + case err := <-getErr: + require.FailNow(t, "starting agent errored with: %v", err) + case <-agent.Ready(): + case <-time.After(5 * time.Second): + require.FailNow(t, "agent not ready within 5 seconds") + } + + return &agentTester{ + agent: agent, + dispatcher: mockDispatcher, + executor: executor, + testCA: tc, + cleanup: func() { + // go in reverse order + for i := len(cleanup) - 1; i >= 0; i-- { + cleanup[i]() + } + }, } } diff --git a/agent/config.go b/agent/config.go index de9359842e..70c94ecb65 100644 --- a/agent/config.go +++ b/agent/config.go @@ -2,6 +2,7 @@ package agent import ( "github.com/boltdb/bolt" + "github.com/docker/go-events" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/connectionbroker" @@ -9,6 +10,13 @@ import ( "google.golang.org/grpc/credentials" ) +// NodeChanges encapsulates changes that should be made to the node as per session messages +// from the dispatcher +type NodeChanges struct { + Node *api.Node + RootCert []byte +} + // Config provides values for an Agent. type Config struct { // Hostname the name of host for agent instance. @@ -25,10 +33,16 @@ type Config struct { DB *bolt.DB // NotifyNodeChange channel receives new node changes from session messages. - NotifyNodeChange chan<- *api.Node + NotifyNodeChange chan<- *NodeChanges + + // NotifyTLSChange channel sends new TLS information changes, which can cause a session to restart + NotifyTLSChange <-chan events.Event // Credentials is credentials for grpc connection to manager. Credentials credentials.TransportCredentials + + // NodeTLSInfo contains the starting node TLS info to bootstrap into the agent + NodeTLSInfo *api.NodeTLSInfo } func (c *Config) validate() error { @@ -44,5 +58,9 @@ func (c *Config) validate() error { return errors.New("agent: database required") } + if c.NodeTLSInfo == nil { + return errors.New("agent: Node TLS info is required") + } + return nil } diff --git a/agent/testutils/fakes.go b/agent/testutils/fakes.go new file mode 100644 index 0000000000..31625bbac8 --- /dev/null +++ b/agent/testutils/fakes.go @@ -0,0 +1,219 @@ +package testutils + +import ( + "net" + "sync" + "testing" + "time" + + "google.golang.org/grpc" + + "github.com/docker/swarmkit/agent/exec" + "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/ca" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +// TestExecutor is executor for integration tests +type TestExecutor struct { + mu sync.Mutex + desc *api.NodeDescription +} + +// Describe just returns empty NodeDescription. +func (e *TestExecutor) Describe(ctx context.Context) (*api.NodeDescription, error) { + e.mu.Lock() + defer e.mu.Unlock() + if e.desc == nil { + return &api.NodeDescription{}, nil + } + return e.desc.Copy(), nil +} + +// Configure does nothing. +func (e *TestExecutor) Configure(ctx context.Context, node *api.Node) error { + return nil +} + +// SetNetworkBootstrapKeys does nothing. +func (e *TestExecutor) SetNetworkBootstrapKeys([]*api.EncryptionKey) error { + return nil +} + +// Controller returns TestController. +func (e *TestExecutor) Controller(t *api.Task) (exec.Controller, error) { + return &TestController{ + ch: make(chan struct{}), + }, nil +} + +// UpdateNodeDescription sets the node description on the test executor +func (e *TestExecutor) UpdateNodeDescription(newDesc *api.NodeDescription) { + e.mu.Lock() + defer e.mu.Unlock() + e.desc = newDesc +} + +// TestController is dummy channel based controller for tests. +type TestController struct { + ch chan struct{} + closeOnce sync.Once +} + +// Update does nothing. +func (t *TestController) Update(ctx context.Context, task *api.Task) error { + return nil +} + +// Prepare does nothing. +func (t *TestController) Prepare(ctx context.Context) error { + return nil +} + +// Start does nothing. +func (t *TestController) Start(ctx context.Context) error { + return nil +} + +// Wait waits on internal channel. +func (t *TestController) Wait(ctx context.Context) error { + select { + case <-t.ch: + case <-ctx.Done(): + } + return nil +} + +// Shutdown closes internal channel +func (t *TestController) Shutdown(ctx context.Context) error { + t.closeOnce.Do(func() { + close(t.ch) + }) + return nil +} + +// Terminate closes internal channel if it wasn't closed before. +func (t *TestController) Terminate(ctx context.Context) error { + t.closeOnce.Do(func() { + close(t.ch) + }) + return nil +} + +// Remove does nothing. +func (t *TestController) Remove(ctx context.Context) error { + return nil +} + +// Close does nothing. +func (t *TestController) Close() error { + t.closeOnce.Do(func() { + close(t.ch) + }) + return nil +} + +// MockDispatcher is a fake dispatcher that one agent at a time can connect to +type MockDispatcher struct { + mu sync.Mutex + sessionCh chan *api.SessionMessage + openSession *api.SessionRequest + closedSessions []*api.SessionRequest + + Addr string +} + +// UpdateTaskStatus is not implemented +func (m *MockDispatcher) UpdateTaskStatus(context.Context, *api.UpdateTaskStatusRequest) (*api.UpdateTaskStatusResponse, error) { + panic("not implemented") +} + +// Tasks keeps an open stream until canceled +func (m *MockDispatcher) Tasks(_ *api.TasksRequest, stream api.Dispatcher_TasksServer) error { + select { + case <-stream.Context().Done(): + } + return nil +} + +// Assignments keeps an open stream until canceled +func (m *MockDispatcher) Assignments(_ *api.AssignmentsRequest, stream api.Dispatcher_AssignmentsServer) error { + select { + case <-stream.Context().Done(): + } + return nil +} + +// Heartbeat always successfully heartbeats +func (m *MockDispatcher) Heartbeat(context.Context, *api.HeartbeatRequest) (*api.HeartbeatResponse, error) { + return &api.HeartbeatResponse{Period: time.Second * 5}, nil +} + +// Session allows a session to be established, and sends the node info +func (m *MockDispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_SessionServer) error { + m.mu.Lock() + m.openSession = r + m.mu.Unlock() + defer func() { + m.mu.Lock() + defer m.mu.Unlock() + m.closedSessions = append(m.closedSessions, m.openSession) + m.openSession = nil + }() + + // send the initial message first + if err := stream.Send(&api.SessionMessage{ + SessionID: r.SessionID, + Managers: []*api.WeightedPeer{ + { + Peer: &api.Peer{Addr: m.Addr}, + }, + }, + }); err != nil { + return err + } + + ctx := stream.Context() + for { + select { + case msg := <-m.sessionCh: + msg.SessionID = r.SessionID + if err := stream.Send(msg); err != nil { + return err + } + case <-ctx.Done(): + return nil + } + } +} + +// GetSessions return all the established and closed sessions +func (m *MockDispatcher) GetSessions() (*api.SessionRequest, []*api.SessionRequest) { + m.mu.Lock() + defer m.mu.Unlock() + return m.openSession, m.closedSessions +} + +// SessionMessageChannel returns a writable channel to inject session messages +func (m *MockDispatcher) SessionMessageChannel() chan<- *api.SessionMessage { + return m.sessionCh +} + +// NewMockDispatcher starts and returns a mock dispatcher instance that can be connected to +func NewMockDispatcher(t *testing.T, secConfig *ca.SecurityConfig) (*MockDispatcher, func()) { + l, err := net.Listen("tcp", "127.0.0.1:0") + addr := l.Addr().String() + require.NoError(t, err) + + serverOpts := []grpc.ServerOption{grpc.Creds(secConfig.ServerTLSCreds)} + s := grpc.NewServer(serverOpts...) + + m := &MockDispatcher{ + Addr: addr, + sessionCh: make(chan *api.SessionMessage, 1), + } + api.RegisterDispatcherServer(s, m) + go s.Serve(l) + return m, s.Stop +} diff --git a/codecov.yml b/codecov.yml index b03ce3d879..aa409fa9e0 100644 --- a/codecov.yml +++ b/codecov.yml @@ -8,3 +8,5 @@ coverage: enabled: yes target: 0 changes: false +ignore: + -**/testutils diff --git a/integration/exec.go b/integration/exec.go deleted file mode 100644 index 43d1e0de10..0000000000 --- a/integration/exec.go +++ /dev/null @@ -1,94 +0,0 @@ -package integration - -import ( - "sync" - - "github.com/docker/swarmkit/agent/exec" - "github.com/docker/swarmkit/api" - "golang.org/x/net/context" -) - -// TestExecutor is executor for integration tests -type TestExecutor struct { -} - -// Describe just returns empty NodeDescription. -func (e *TestExecutor) Describe(ctx context.Context) (*api.NodeDescription, error) { - return &api.NodeDescription{}, nil -} - -// Configure does nothing. -func (e *TestExecutor) Configure(ctx context.Context, node *api.Node) error { - return nil -} - -// SetNetworkBootstrapKeys does nothing. -func (e *TestExecutor) SetNetworkBootstrapKeys([]*api.EncryptionKey) error { - return nil -} - -// Controller returns TestController. -func (e *TestExecutor) Controller(t *api.Task) (exec.Controller, error) { - return &TestController{ - ch: make(chan struct{}), - }, nil -} - -// TestController is dummy channel based controller for tests. -type TestController struct { - ch chan struct{} - closeOnce sync.Once -} - -// Update does nothing. -func (t *TestController) Update(ctx context.Context, task *api.Task) error { - return nil -} - -// Prepare does nothing. -func (t *TestController) Prepare(ctx context.Context) error { - return nil -} - -// Start does nothing. -func (t *TestController) Start(ctx context.Context) error { - return nil -} - -// Wait waits on internal channel. -func (t *TestController) Wait(ctx context.Context) error { - select { - case <-t.ch: - case <-ctx.Done(): - } - return nil -} - -// Shutdown closes internal channel -func (t *TestController) Shutdown(ctx context.Context) error { - t.closeOnce.Do(func() { - close(t.ch) - }) - return nil -} - -// Terminate closes internal channel if it wasn't closed before. -func (t *TestController) Terminate(ctx context.Context) error { - t.closeOnce.Do(func() { - close(t.ch) - }) - return nil -} - -// Remove does nothing. -func (t *TestController) Remove(ctx context.Context) error { - return nil -} - -// Close does nothing. -func (t *TestController) Close() error { - t.closeOnce.Do(func() { - close(t.ch) - }) - return nil -} diff --git a/integration/node.go b/integration/node.go index 64dad8409a..d751bf01f9 100644 --- a/integration/node.go +++ b/integration/node.go @@ -9,6 +9,7 @@ import ( "google.golang.org/grpc" + agentutils "github.com/docker/swarmkit/agent/testutils" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" "github.com/docker/swarmkit/identity" @@ -40,7 +41,7 @@ func newTestNode(joinAddr, joinToken string, lateBind bool, rootCA *ca.RootCA) ( ListenControlAPI: cAddr, JoinAddr: joinAddr, StateDir: tmpDir, - Executor: &TestExecutor{}, + Executor: &agentutils.TestExecutor{}, JoinToken: joinToken, } if !lateBind { From 984a839307850003b159d211b1f7eb45cbef5ac2 Mon Sep 17 00:00:00 2001 From: cyli Date: Mon, 3 Apr 2017 13:52:30 -0700 Subject: [PATCH 5/6] In the manager, before updating the root CA in the security config, save the root CA certs. Signed-off-by: cyli --- ca/server.go | 9 ++++++++- ca/server_test.go | 8 ++++++++ ca/testutils/cautils.go | 2 +- manager/controlapi/ca_rotation_test.go | 2 +- manager/controlapi/server_test.go | 2 +- manager/dirty_test.go | 1 + manager/manager.go | 5 ++++- manager/manager_test.go | 4 ++++ 8 files changed, 28 insertions(+), 5 deletions(-) diff --git a/ca/server.go b/ca/server.go index 63f0b7e38c..f3fa9cb6c8 100644 --- a/ca/server.go +++ b/ca/server.go @@ -60,6 +60,9 @@ type Server struct { lastSeenClusterRootCA *api.RootCA lastSeenExternalCAs []*api.ExternalCA secConfigMu sync.Mutex + + // before we update the security config with the new root CA, we need to be able to save the root certs + rootPaths CertPaths } // DefaultCAConfig returns the default CA Config, with a default expiration. @@ -70,13 +73,14 @@ func DefaultCAConfig() api.CAConfig { } // NewServer creates a CA API server. -func NewServer(store *store.MemoryStore, securityConfig *SecurityConfig) *Server { +func NewServer(store *store.MemoryStore, securityConfig *SecurityConfig, rootCAPaths CertPaths) *Server { return &Server{ store: store, securityConfig: securityConfig, pending: make(map[string]*api.Node), started: make(chan struct{}), reconciliationRetryInterval: defaultReconciliationRetryInterval, + rootPaths: rootCAPaths, } } @@ -582,6 +586,9 @@ func (s *Server) UpdateRootCA(ctx context.Context, cluster *api.Cluster) error { if err != nil { return errors.Wrap(err, "invalid Root CA object in cluster") } + if err := SaveRootCA(updatedRootCA, s.rootPaths); err != nil { + return errors.Wrap(err, "unable to save new root CA certificates") + } externalCARootPool := updatedRootCA.Pool if rCA.RootRotation != nil { diff --git a/ca/server_test.go b/ca/server_test.go index e3f2500c95..4bc50e6b7a 100644 --- a/ca/server_test.go +++ b/ca/server_test.go @@ -4,6 +4,8 @@ import ( "bytes" "crypto/x509" "fmt" + "io/ioutil" + "os" "testing" "time" @@ -542,4 +544,10 @@ func TestCAServerUpdateRootCA(t *testing.T) { require.Equal(t, ca.ErrNoExternalCAURLs, err) } } + + // If we can't save the root cert, we can't update the root CA even if it's completely valid + require.NoError(t, os.RemoveAll(tc.TempDir)) + require.NoError(t, ioutil.WriteFile(tc.TempDir, []byte("cant create directory if this is file"), 0700)) + tc.CAServer.UpdateRootCA(context.Background(), fakeClusterSpec(testutils.ECDSA256SHA256Cert, testutils.ECDSA256Key, nil, nil)) + require.Equal(t, tc.RootCA.Certs, tc.ServingSecurityConfig.RootCA().Certs) } diff --git a/ca/testutils/cautils.go b/ca/testutils/cautils.go index 8acd12e2b0..fe80d49242 100644 --- a/ca/testutils/cautils.go +++ b/ca/testutils/cautils.go @@ -175,7 +175,7 @@ func NewTestCAFromRootCA(t *testing.T, tempBaseDir string, rootCA ca.RootCA, krw clusterObj := createClusterObject(t, s, organization, &rootCA, externalCAs...) - caServer := ca.NewServer(s, managerConfig) + caServer := ca.NewServer(s, managerConfig, paths.RootCA) caServer.SetReconciliationRetryInterval(50 * time.Millisecond) api.RegisterCAServer(grpcServer, caServer) api.RegisterNodeCAServer(grpcServer, caServer) diff --git a/manager/controlapi/ca_rotation_test.go b/manager/controlapi/ca_rotation_test.go index 562f7bb844..911451b93e 100644 --- a/manager/controlapi/ca_rotation_test.go +++ b/manager/controlapi/ca_rotation_test.go @@ -66,7 +66,7 @@ func getSecurityConfig(t *testing.T, localRootCA *ca.RootCA, cluster *api.Cluste secConfig, err := localRootCA.CreateSecurityConfig(context.Background(), ca.NewKeyReadWriter(paths.Node, nil, nil), ca.CertificateRequestConfig{}) require.NoError(t, err) - require.NoError(t, ca.NewServer(store.NewMemoryStore(nil), secConfig).UpdateRootCA(context.Background(), cluster)) + require.NoError(t, ca.NewServer(store.NewMemoryStore(nil), secConfig, paths.RootCA).UpdateRootCA(context.Background(), cluster)) return secConfig } diff --git a/manager/controlapi/server_test.go b/manager/controlapi/server_test.go index 16844fb240..934bd6cb29 100644 --- a/manager/controlapi/server_test.go +++ b/manager/controlapi/server_test.go @@ -48,7 +48,7 @@ func newTestServer(t *testing.T) *testServer { ts.Store = store.NewMemoryStore(&stateutils.MockProposer{}) assert.NotNil(t, ts.Store) - ts.Server = NewServer(ts.Store, nil, securityConfig, ca.NewServer(ts.Store, securityConfig), nil) + ts.Server = NewServer(ts.Store, nil, securityConfig, ca.NewServer(ts.Store, securityConfig, tc.Paths.RootCA), nil) assert.NotNil(t, ts.Server) temp, err := ioutil.TempFile("", "test-socket") diff --git a/manager/dirty_test.go b/manager/dirty_test.go index 52814946f5..259cedcb2f 100644 --- a/manager/dirty_test.go +++ b/manager/dirty_test.go @@ -45,6 +45,7 @@ func TestIsStateDirty(t *testing.T) { SecurityConfig: managerSecurityConfig, AutoLockManagers: true, UnlockKey: []byte("kek"), + RootCAPaths: tc.Paths.RootCA, }) assert.NoError(t, err) assert.NotNil(t, m) diff --git a/manager/manager.go b/manager/manager.go index cb3cb43fdb..7b436b16f2 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -65,6 +65,9 @@ type RemoteAddrs struct { type Config struct { SecurityConfig *ca.SecurityConfig + // RootCAPaths is the path to which new root certs should be save + RootCAPaths ca.CertPaths + // ExternalCAs is a list of initial CAs to which a manager node // will make certificate signing requests for node certificates. ExternalCAs []*api.ExternalCA @@ -210,7 +213,7 @@ func New(config *Config) (*Manager, error) { m := &Manager{ config: *config, - caserver: ca.NewServer(raftNode.MemoryStore(), config.SecurityConfig), + caserver: ca.NewServer(raftNode.MemoryStore(), config.SecurityConfig, config.RootCAPaths), dispatcher: dispatcher.New(raftNode, dispatcher.DefaultConfig()), logbroker: logbroker.New(raftNode.MemoryStore()), server: grpc.NewServer(opts...), diff --git a/manager/manager_test.go b/manager/manager_test.go index 48e6b78fd7..8569de3101 100644 --- a/manager/manager_test.go +++ b/manager/manager_test.go @@ -62,6 +62,7 @@ func TestManager(t *testing.T) { SecurityConfig: managerSecurityConfig, AutoLockManagers: true, UnlockKey: []byte("kek"), + RootCAPaths: tc.Paths.RootCA, }) assert.NoError(t, err) assert.NotNil(t, m) @@ -222,6 +223,7 @@ func TestManagerLockUnlock(t *testing.T) { ControlAPI: temp.Name(), StateDir: stateDir, SecurityConfig: managerSecurityConfig, + RootCAPaths: tc.Paths.RootCA, // start off without any encryption }) require.NoError(t, err) @@ -431,6 +433,7 @@ func TestManagerUpdatesSecurityConfig(t *testing.T) { ControlAPI: temp.Name(), StateDir: stateDir, SecurityConfig: managerSecurityConfig, + RootCAPaths: tc.Paths.RootCA, }) require.NoError(t, err) require.NotNil(t, m) @@ -539,6 +542,7 @@ func TestManagerEncryptsDecryptsRootKeyMaterial(t *testing.T) { ControlAPI: temp.Name(), StateDir: stateDir, SecurityConfig: managerSecurityConfig, + RootCAPaths: tc.Paths.RootCA, } done := make(chan error) defer close(done) From d9aa5995ea23cb290aeb8b39a58a550cbacd6f40 Mon Sep 17 00:00:00 2001 From: cyli Date: Mon, 3 Apr 2017 15:03:02 -0700 Subject: [PATCH 6/6] Node now passes the root CA path to the manager and CA server, and also sets up a security config watch queue and passes the watch channel to the agent, so the agent can be notified of TLS updates. Also, when the agent notifies the node that the root CA has changed, node updates the TLS config if the node is a worker. Signed-off-by: cyli --- node/node.go | 92 ++++++++++++++++++++-------- node/node_test.go | 152 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 208 insertions(+), 36 deletions(-) diff --git a/node/node.go b/node/node.go index 0551d5b93f..efa75504e9 100644 --- a/node/node.go +++ b/node/node.go @@ -1,6 +1,7 @@ package node import ( + "bytes" "crypto/tls" "encoding/json" "io/ioutil" @@ -25,6 +26,7 @@ import ( "github.com/docker/swarmkit/manager" "github.com/docker/swarmkit/manager/encryption" "github.com/docker/swarmkit/remotes" + "github.com/docker/swarmkit/watch" "github.com/docker/swarmkit/xnet" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/pkg/errors" @@ -129,7 +131,7 @@ type Node struct { err error agent *agent.Agent manager *manager.Manager - notifyNodeChange chan *api.Node // used to send role updates from the dispatcher api on promotion/demotion + notifyNodeChange chan *agent.NodeChanges // used by the agent to relay node updates from the dispatcher Session stream to (*Node).run unlockKey []byte } @@ -172,7 +174,7 @@ func New(c *Config) (*Node, error) { stopped: make(chan struct{}), closed: make(chan struct{}), ready: make(chan struct{}), - notifyNodeChange: make(chan *api.Node, 1), + notifyNodeChange: make(chan *agent.NodeChanges, 1), unlockKey: c.UnlockKey, } @@ -235,7 +237,8 @@ func (n *Node) run(ctx context.Context) (err error) { } }(ctx) - securityConfig, err := n.loadSecurityConfig(ctx) + paths := ca.NewConfigPaths(filepath.Join(n.config.StateDir, certDirectory)) + securityConfig, err := n.loadSecurityConfig(ctx, paths) if err != nil { return err } @@ -274,24 +277,44 @@ func (n *Node) run(ctx context.Context) (err error) { select { case <-agentDone: return - case node := <-n.notifyNodeChange: - // If the server is sending us a ForceRenewal State, renew - if node.Certificate.Status.State == api.IssuanceStateRotate { - renewCert() - continue - } + case nodeChanges := <-n.notifyNodeChange: n.Lock() - // If we got a role change, renew - role := ca.WorkerRole - if node.Role == api.NodeRoleManager { - role = ca.ManagerRole + currentRole := n.role + n.Unlock() + + if nodeChanges.Node != nil { + role := ca.WorkerRole + if nodeChanges.Node.Role == api.NodeRoleManager { + role = ca.ManagerRole + } + + // If the server is sending us a ForceRenewal State, or if the new node role doesn't match our current role, renew + if currentRole != role || nodeChanges.Node.Certificate.Status.State == api.IssuanceStateRotate { + renewCert() + } } - if n.role == role { - n.Unlock() - continue + + if nodeChanges.RootCert != nil { + // We only want to update the root CA if this is a worker node. Manager nodes directly watch the raft + // store and update the root CA, with the necessary signer, from the raft store (since the managers + // need the CA key as well to potentially issue new TLS certificates). + if currentRole == ca.ManagerRole || bytes.Equal(nodeChanges.RootCert, securityConfig.RootCA().Certs) { + continue + } + newRootCA, err := ca.NewRootCA(nodeChanges.RootCert, nil, nil, ca.DefaultNodeCertExpiration, nil) + if err != nil { + log.G(ctx).WithError(err).Error("invalid new root certificate from the dispatcher") + continue + } + if err := ca.SaveRootCA(newRootCA, paths.RootCA); err != nil { + log.G(ctx).WithError(err).Error("could not save new root certificate from the dispatcher") + continue + } + if err := securityConfig.UpdateRootCA(&newRootCA, newRootCA.Pool); err != nil { + log.G(ctx).WithError(err).Error("could not use new root CA from dispatcher") + continue + } } - n.Unlock() - renewCert() } } }() @@ -322,12 +345,12 @@ func (n *Node) run(ctx context.Context) (err error) { var managerErr error var agentErr error go func() { - managerErr = n.superviseManager(ctx, securityConfig, managerReady, forceCertRenewal) // store err and loop + managerErr = n.superviseManager(ctx, securityConfig, paths.RootCA, managerReady, forceCertRenewal) // store err and loop wg.Done() cancel() }() go func() { - agentErr = n.runAgent(ctx, db, securityConfig.ClientTLSCreds, agentReady) + agentErr = n.runAgent(ctx, db, securityConfig, agentReady) wg.Done() cancel() close(agentDone) @@ -401,7 +424,7 @@ func (n *Node) Err(ctx context.Context) error { } } -func (n *Node) runAgent(ctx context.Context, db *bolt.DB, creds credentials.TransportCredentials, ready chan<- struct{}) error { +func (n *Node) runAgent(ctx context.Context, db *bolt.DB, securityConfig *ca.SecurityConfig, ready chan<- struct{}) error { waitCtx, waitCancel := context.WithCancel(ctx) remotesCh := n.remotes.WaitSelect(ctx) controlCh := n.ListenControlSocket(waitCtx) @@ -428,13 +451,28 @@ waitPeer: default: } + secChangeQueue := watch.NewQueue() + defer secChangeQueue.Close() + secChangesCh, secChangesCancel := secChangeQueue.Watch() + defer secChangesCancel() + securityConfig.SetWatch(secChangeQueue) + + rootCA := securityConfig.RootCA() + issuer := securityConfig.IssuerInfo() + a, err := agent.New(&agent.Config{ Hostname: n.config.Hostname, ConnBroker: n.connBroker, Executor: n.config.Executor, DB: db, NotifyNodeChange: n.notifyNodeChange, - Credentials: creds, + NotifyTLSChange: secChangesCh, + Credentials: securityConfig.ClientTLSCreds, + NodeTLSInfo: &api.NodeTLSInfo{ + TrustRoot: rootCA.Certs, + CertIssuerPublicKey: issuer.PublicKey, + CertIssuerSubject: issuer.Subject, + }, }) if err != nil { return err @@ -565,8 +603,7 @@ func (n *Node) Remotes() []api.Peer { return remotes } -func (n *Node) loadSecurityConfig(ctx context.Context) (*ca.SecurityConfig, error) { - paths := ca.NewConfigPaths(filepath.Join(n.config.StateDir, certDirectory)) +func (n *Node) loadSecurityConfig(ctx context.Context, paths *ca.SecurityConfigPaths) (*ca.SecurityConfig, error) { var securityConfig *ca.SecurityConfig krw := ca.NewKeyReadWriter(paths.Node, n.unlockKey, &manager.RaftDEKData{}) @@ -717,7 +754,7 @@ func (n *Node) waitRole(ctx context.Context, role string) error { return nil } -func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig, ready chan struct{}, workerRole <-chan struct{}) (bool, error) { +func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig, rootPaths ca.CertPaths, ready chan struct{}, workerRole <-chan struct{}) (bool, error) { var remoteAPI *manager.RemoteAddrs if n.config.ListenRemoteAPI != "" { remoteAPI = &manager.RemoteAddrs{ @@ -741,6 +778,7 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig UnlockKey: n.unlockKey, Availability: n.config.Availability, PluginGetter: n.config.PluginGetter, + RootCAPaths: rootPaths, }) if err != nil { return false, err @@ -789,7 +827,7 @@ func (n *Node) runManager(ctx context.Context, securityConfig *ca.SecurityConfig return clearData, nil } -func (n *Node) superviseManager(ctx context.Context, securityConfig *ca.SecurityConfig, ready chan struct{}, forceCertRenewal chan struct{}) error { +func (n *Node) superviseManager(ctx context.Context, securityConfig *ca.SecurityConfig, rootPaths ca.CertPaths, ready chan struct{}, forceCertRenewal chan struct{}) error { for { if err := n.waitRole(ctx, ca.ManagerRole); err != nil { return err @@ -803,7 +841,7 @@ func (n *Node) superviseManager(ctx context.Context, securityConfig *ca.Security } }() - wasRemoved, err := n.runManager(ctx, securityConfig, ready, workerRole) + wasRemoved, err := n.runManager(ctx, securityConfig, rootPaths, ready, workerRole) if err != nil { waitRoleCancel() return errors.Wrap(err, "manager stopped") diff --git a/node/node_test.go b/node/node_test.go index 125430e346..562ac0a475 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -1,17 +1,25 @@ package node import ( + "bytes" "crypto/x509" "encoding/pem" + "fmt" "io/ioutil" "os" "path/filepath" + "sync" "testing" + "time" + "github.com/cloudflare/cfssl/helpers" + "github.com/docker/swarmkit/agent" + agentutils "github.com/docker/swarmkit/agent/testutils" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" cautils "github.com/docker/swarmkit/ca/testutils" "github.com/docker/swarmkit/identity" + "github.com/docker/swarmkit/manager/state/raft/testutils" "github.com/docker/swarmkit/manager/state/store" "github.com/pkg/errors" "github.com/stretchr/testify/require" @@ -33,7 +41,7 @@ func TestLoadSecurityConfigNewNode(t *testing.T) { AutoLockManagers: autoLockManagers, }) require.NoError(t, err) - securityConfig, err := node.loadSecurityConfig(context.Background()) + securityConfig, err := node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) require.NotNil(t, securityConfig) @@ -63,7 +71,7 @@ func TestLoadSecurityConfigPartialCertsOnDisk(t *testing.T) { StateDir: tempdir, }) require.NoError(t, err) - securityConfig, err := node.loadSecurityConfig(context.Background()) + securityConfig, err := node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) require.NotNil(t, securityConfig) @@ -81,7 +89,7 @@ func TestLoadSecurityConfigPartialCertsOnDisk(t *testing.T) { StateDir: tempdir, }) require.NoError(t, err) - securityConfig, err = node.loadSecurityConfig(context.Background()) + securityConfig, err = node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) require.NotNil(t, securityConfig) @@ -122,7 +130,7 @@ func TestLoadSecurityConfigLoadFromDisk(t *testing.T) { UnlockKey: []byte("passphrase"), }) require.NoError(t, err) - securityConfig, err := node.loadSecurityConfig(context.Background()) + securityConfig, err := node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) require.NotNil(t, securityConfig) @@ -133,7 +141,7 @@ func TestLoadSecurityConfigLoadFromDisk(t *testing.T) { JoinToken: tc.ManagerToken, }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.Equal(t, ErrInvalidUnlockKey, err) // Invalid CA @@ -147,7 +155,7 @@ func TestLoadSecurityConfigLoadFromDisk(t *testing.T) { UnlockKey: []byte("passphrase"), }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.IsType(t, x509.UnknownAuthorityError{}, errors.Cause(err)) } @@ -167,7 +175,7 @@ func TestLoadSecurityConfigDownloadAllCerts(t *testing.T) { JoinAddr: "127.0.0.1:12", }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.Error(t, err) tc := cautils.NewTestCA(t) @@ -182,7 +190,7 @@ func TestLoadSecurityConfigDownloadAllCerts(t *testing.T) { JoinToken: tc.ManagerToken, }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) // the TLS key and cert were written to disk unencrypted @@ -226,7 +234,7 @@ func TestLoadSecurityConfigDownloadAllCerts(t *testing.T) { JoinToken: tc.ManagerToken, }) require.NoError(t, err) - _, err = node.loadSecurityConfig(context.Background()) + _, err = node.loadSecurityConfig(context.Background(), paths) require.NoError(t, err) // make sure the CA cert has not been replaced @@ -240,3 +248,129 @@ func TestLoadSecurityConfigDownloadAllCerts(t *testing.T) { _, _, err = ca.NewKeyReadWriter(paths.Node, []byte("passphrase"), nil).Read() require.NoError(t, err) } + +func TestManagerIgnoresDispatcherRootCAUpdate(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "manager-root-ca-update") + require.NoError(t, err) + + // don't bother with a listening socket + cAddr := filepath.Join(tmpDir, "control.sock") + cfg := &Config{ + ListenControlAPI: cAddr, + StateDir: tmpDir, + Executor: &agentutils.TestExecutor{}, + } + + node, err := New(cfg) + require.NoError(t, err) + + var nodeErr error + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + nodeErr = node.Start(context.Background()) + wg.Done() + }() + + select { + case <-node.Ready(): + case <-time.After(5 * time.Second): + require.FailNow(t, "node did not ready in time") + } + + certPath := filepath.Join(tmpDir, certDirectory, "swarm-root-ca.crt") + currentCACerts, err := ioutil.ReadFile(certPath) + require.NoError(t, err) + parsedCerts, err := helpers.ParseCertificatesPEM(currentCACerts) + require.NoError(t, err) + require.Len(t, parsedCerts, 1) + + // fake an update from the dispatcher, because the dispatcher is actually the local manager + node.notifyNodeChange <- &agent.NodeChanges{ + RootCert: append(currentCACerts, cautils.ECDSA256SHA256Cert...), + } + + // the node root CA certificates do not change + time.Sleep(250 * time.Millisecond) + caCerts, err := ioutil.ReadFile(certPath) + require.NoError(t, err) + require.Equal(t, currentCACerts, caCerts) + + require.NoError(t, node.Stop(context.Background())) + wg.Wait() + require.NoError(t, nodeErr) +} + +func TestAgentRespectsDispatcherRootCAUpdate(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "manager-root-ca-update") + require.NoError(t, err) + + // bootstrap worker TLS certificates + paths := ca.NewConfigPaths(filepath.Join(tmpDir, certDirectory)) + rootCA, err := ca.CreateRootCA("rootCN") + require.NoError(t, err) + require.NoError(t, ca.SaveRootCA(rootCA, paths.RootCA)) + managerSecConfig, err := rootCA.CreateSecurityConfig(context.Background(), + ca.NewKeyReadWriter(paths.Node, nil, nil), ca.CertificateRequestConfig{}) + require.NoError(t, err) + + _, _, err = rootCA.IssueAndSaveNewCertificates(ca.NewKeyReadWriter(paths.Node, nil, nil), "workerNode", + ca.WorkerRole, managerSecConfig.ServerTLSCreds.Organization()) + require.NoError(t, err) + + mockDispatcher, cleanup := agentutils.NewMockDispatcher(t, managerSecConfig) + defer cleanup() + + cfg := &Config{ + StateDir: tmpDir, + Executor: &agentutils.TestExecutor{}, + JoinAddr: mockDispatcher.Addr, + } + node, err := New(cfg) + require.NoError(t, err) + + var nodeErr error + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + nodeErr = node.Start(context.Background()) + wg.Done() + }() + + select { + case <-node.Ready(): + case <-time.After(5 * time.Second): + require.FailNow(t, "node did not ready in time") + } + + currentCACerts, err := ioutil.ReadFile(paths.RootCA.Cert) + require.NoError(t, err) + parsedCerts, err := helpers.ParseCertificatesPEM(currentCACerts) + require.NoError(t, err) + require.Len(t, parsedCerts, 1) + + // fake an update from the dispatcher + node.notifyNodeChange <- &agent.NodeChanges{ + RootCert: append(currentCACerts, cautils.ECDSA256SHA256Cert...), + } + + require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { + caCerts, err := ioutil.ReadFile(paths.RootCA.Cert) + require.NoError(t, err) + if bytes.Equal(currentCACerts, caCerts) { + return errors.New("new certificates have not been replaced yet") + } + parsedCerts, err := helpers.ParseCertificatesPEM(caCerts) + if err != nil { + return err + } + if len(parsedCerts) != 2 { + return fmt.Errorf("expecting 2 new certificates, got %d", len(parsedCerts)) + } + return nil + }, time.Second)) + + require.NoError(t, node.Stop(context.Background())) + wg.Wait() + require.NoError(t, nodeErr) +}