From eb3d3b13a06378901a55c57f10fac955845e1e09 Mon Sep 17 00:00:00 2001 From: Helene Durand Date: Mon, 24 Jun 2024 10:00:01 +0200 Subject: [PATCH] MEDIUM: add new server/bind options Added: server pool-conn-name +1 server hash-key +1 bind ssl default-crt +1 --- configuration/bind.go | 10 +++++ configuration/server.go | 10 +++++ go.mod | 2 +- go.sum | 4 +- models/bind_params.go | 24 +++++++++++ models/bind_params_compare.go | 30 ++++++++++++- models/bind_params_compare_test.go | 4 +- models/server_params.go | 42 +++++++++++++++++++ models/server_params_compare.go | 16 +++++++ models/server_params_compare_test.go | 4 +- specification/build/haproxy_spec.yaml | 12 ++++++ .../models/configuration/bind_params.yaml | 6 +++ .../models/configuration/server_params.yaml | 6 +++ test/bind_test.go | 1 + test/configuration_test.go | 4 +- test/expected/structured.json | 10 ++++- 16 files changed, 172 insertions(+), 13 deletions(-) diff --git a/configuration/bind.go b/configuration/bind.go index 745e0038..f14310da 100644 --- a/configuration/bind.go +++ b/configuration/bind.go @@ -324,6 +324,11 @@ func parseBindParams(bindOptions []params.BindOption) (b models.BindParams) { // b.CrtIgnoreErr = v.Value case "crt-list": b.CrtList = v.Value + case "default-crt": + if b.DefaultCrtList == nil { + b.DefaultCrtList = []string{} + } + b.DefaultCrtList = append(b.DefaultCrtList, v.Value) case "gid": gid, err := strconv.ParseInt(v.Value, 10, 64) if err == nil { @@ -485,6 +490,11 @@ func serializeBindParams(b models.BindParams, path string) (options []params.Bin if b.CrtList != "" { options = append(options, ¶ms.BindOptionValue{Name: "crt-list", Value: b.CrtList}) } + if b.DefaultCrtList != nil { + for _, dc := range b.DefaultCrtList { + options = append(options, ¶ms.BindOptionValue{Name: "default-crt", Value: dc}) + } + } if b.DeferAccept { options = append(options, ¶ms.BindOptionWord{Name: "defer-accept"}) } diff --git a/configuration/server.go b/configuration/server.go index 4d97d835..6dd31e3c 100644 --- a/configuration/server.go +++ b/configuration/server.go @@ -480,6 +480,10 @@ func parseServerParams(serverOptions []params.ServerOption, serverParams *models } case "ws": serverParams.Ws = v.Value + case "pool-conn-name": + serverParams.PoolConnName = v.Value + case "hash-key": + serverParams.HashKey = v.Value } case *params.ServerOptionIDValue: if v.Name == "set-proxy-v2-tlv-fmt" { @@ -837,6 +841,12 @@ func serializeServerParams(s models.ServerParams) (options []params.ServerOption if s.Ws != "" { options = append(options, ¶ms.ServerOptionValue{Name: "ws", Value: s.Ws}) } + if s.PoolConnName != "" { + options = append(options, ¶ms.ServerOptionValue{Name: "pool-conn-name", Value: s.PoolConnName}) + } + if s.HashKey != "" { + options = append(options, ¶ms.ServerOptionValue{Name: "hash-key", Value: s.HashKey}) + } return options } diff --git a/go.mod b/go.mod index 0bd3be17..04ca5dc6 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/renameio v1.0.1 github.com/google/uuid v1.6.0 - github.com/haproxytech/config-parser/v5 v5.1.1-0.20240619192607-9ef3696c6fec + github.com/haproxytech/config-parser/v5 v5.1.1-0.20240624075631-c75eaad880c2 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mitchellh/mapstructure v1.5.0 diff --git a/go.sum b/go.sum index 6bd1c61c..f4c1cd53 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/haproxytech/config-parser/v5 v5.1.1-0.20240619192607-9ef3696c6fec h1:3tOw7lSODKXfZa+kZA+lCpmoucV9qrme0cIOX6dvLtc= -github.com/haproxytech/config-parser/v5 v5.1.1-0.20240619192607-9ef3696c6fec/go.mod h1:uzi0JXWJYW31M1AzGsczaJtEaoG54qP0LX8B1A2iQRw= +github.com/haproxytech/config-parser/v5 v5.1.1-0.20240624075631-c75eaad880c2 h1:sdgelGYw2i16OL47z2bYEiwjuH7KDWC4P9WLQ0BFZd0= +github.com/haproxytech/config-parser/v5 v5.1.1-0.20240624075631-c75eaad880c2/go.mod h1:uzi0JXWJYW31M1AzGsczaJtEaoG54qP0LX8B1A2iQRw= github.com/haproxytech/go-logger v1.1.0 h1:HgGtYaI1ApkvbQdsm7f9AzQQoxTB7w37criTflh7IQE= github.com/haproxytech/go-logger v1.1.0/go.mod h1:OekUd8HCb7ubxMplzHUPBTHNxZmddOWfOjWclZsqIeM= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= diff --git a/models/bind_params.go b/models/bind_params.go index 593556cc..cbe270aa 100644 --- a/models/bind_params.go +++ b/models/bind_params.go @@ -23,6 +23,7 @@ package models import ( "context" "encoding/json" + "strconv" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" @@ -85,6 +86,9 @@ type BindParams struct { // curves Curves string `json:"curves,omitempty"` + // default crt list + DefaultCrtList []string `json:"default_crt_list,omitempty"` + // defer accept DeferAccept bool `json:"defer_accept,omitempty"` @@ -284,6 +288,10 @@ func (m *BindParams) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateDefaultCrtList(formats); err != nil { + res = append(res, err) + } + if err := m.validateGUIDPrefix(formats); err != nil { res = append(res, err) } @@ -346,6 +354,22 @@ func (m *BindParams) validateAlpn(formats strfmt.Registry) error { return nil } +func (m *BindParams) validateDefaultCrtList(formats strfmt.Registry) error { + if swag.IsZero(m.DefaultCrtList) { // not required + return nil + } + + for i := 0; i < len(m.DefaultCrtList); i++ { + + if err := validate.Pattern("default_crt_list"+"."+strconv.Itoa(i), "body", m.DefaultCrtList[i], `^[^\s]+$`); err != nil { + return err + } + + } + + return nil +} + func (m *BindParams) validateGUIDPrefix(formats strfmt.Registry) error { if swag.IsZero(m.GUIDPrefix) { // not required return nil diff --git a/models/bind_params_compare.go b/models/bind_params_compare.go index 53271756..13a0a5a7 100644 --- a/models/bind_params_compare.go +++ b/models/bind_params_compare.go @@ -19,11 +19,20 @@ package models // Equal checks if two structs of type BindParams are equal // +// By default empty maps and slices are equal to nil: +// // var a, b BindParams // equal := a.Equal(b) // -// opts ...Options are ignored in this method +// For more advanced use case you can configure these options (default values are shown): +// +// var a, b BindParams +// equal := a.Equal(b,Options{ +// NilSameAsEmpty: true, +// }) func (s BindParams) Equal(t BindParams, opts ...Options) bool { + opt := getOptions(opts...) + if s.AcceptNetscalerCip != t.AcceptNetscalerCip { return false } @@ -88,6 +97,10 @@ func (s BindParams) Equal(t BindParams, opts ...Options) bool { return false } + if !equalComparableSlice(s.DefaultCrtList, t.DefaultCrtList, opt) { + return false + } + if s.DeferAccept != t.DeferAccept { return false } @@ -309,11 +322,20 @@ func (s BindParams) Equal(t BindParams, opts ...Options) bool { // Diff checks if two structs of type BindParams are equal // +// By default empty maps and slices are equal to nil: +// // var a, b BindParams // diff := a.Diff(b) // -// opts ...Options are ignored in this method +// For more advanced use case you can configure these options (default values are shown): +// +// var a, b BindParams +// diff := a.Diff(b,Options{ +// NilSameAsEmpty: true, +// }) func (s BindParams) Diff(t BindParams, opts ...Options) map[string][]interface{} { + opt := getOptions(opts...) + diff := make(map[string][]interface{}) if s.AcceptNetscalerCip != t.AcceptNetscalerCip { diff["AcceptNetscalerCip"] = []interface{}{s.AcceptNetscalerCip, t.AcceptNetscalerCip} @@ -379,6 +401,10 @@ func (s BindParams) Diff(t BindParams, opts ...Options) map[string][]interface{} diff["Curves"] = []interface{}{s.Curves, t.Curves} } + if !equalComparableSlice(s.DefaultCrtList, t.DefaultCrtList, opt) { + diff["DefaultCrtList"] = []interface{}{s.DefaultCrtList, t.DefaultCrtList} + } + if s.DeferAccept != t.DeferAccept { diff["DeferAccept"] = []interface{}{s.DeferAccept, t.DeferAccept} } diff --git a/models/bind_params_compare_test.go b/models/bind_params_compare_test.go index 9f6949a6..cb2084b8 100644 --- a/models/bind_params_compare_test.go +++ b/models/bind_params_compare_test.go @@ -233,7 +233,7 @@ func TestBindParamsDiffFalse(t *testing.T) { for _, sample := range samples { result := sample.a.Diff(sample.b) - if len(result) != 70 { + if len(result) != 71 { json := jsoniter.ConfigCompatibleWithStandardLibrary a, err := json.Marshal(&sample.a) if err != nil { @@ -243,7 +243,7 @@ func TestBindParamsDiffFalse(t *testing.T) { if err != nil { t.Errorf(err.Error()) } - t.Errorf("Expected BindParams to be different in 70 cases, but it is not (%d) %s %s", len(result), a, b) + t.Errorf("Expected BindParams to be different in 71 cases, but it is not (%d) %s %s", len(result), a, b) } } } diff --git a/models/server_params.go b/models/server_params.go index aa7f884f..0cac86a2 100644 --- a/models/server_params.go +++ b/models/server_params.go @@ -168,6 +168,11 @@ type ServerParams struct { // +kubebuilder:validation:Pattern=`^[A-Za-z0-9-_.:]+$` GUID string `json:"guid,omitempty"` + // hash key + // Pattern: ^[^\s]+$ + // +kubebuilder:validation:Pattern=`^[^\s]+$` + HashKey string `json:"hash_key,omitempty"` + // health check address // Pattern: ^[^\s]+$ // +kubebuilder:validation:Pattern=`^[^\s]+$` @@ -269,6 +274,11 @@ type ServerParams struct { // +kubebuilder:validation:Enum=shutdown-backup-sessions; OnMarkedUp string `json:"on-marked-up,omitempty"` + // pool conn name + // Pattern: ^[^\s]+$ + // +kubebuilder:validation:Pattern=`^[^\s]+$` + PoolConnName string `json:"pool_conn_name,omitempty"` + // pool low conn PoolLowConn *int64 `json:"pool_low_conn,omitempty"` @@ -505,6 +515,10 @@ func (m *ServerParams) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateHashKey(formats); err != nil { + res = append(res, err) + } + if err := m.validateHealthCheckAddress(formats); err != nil { res = append(res, err) } @@ -565,6 +579,10 @@ func (m *ServerParams) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validatePoolConnName(formats); err != nil { + res = append(res, err) + } + if err := m.validateProto(formats); err != nil { res = append(res, err) } @@ -1229,6 +1247,18 @@ func (m *ServerParams) validateGUID(formats strfmt.Registry) error { return nil } +func (m *ServerParams) validateHashKey(formats strfmt.Registry) error { + if swag.IsZero(m.HashKey) { // not required + return nil + } + + if err := validate.Pattern("hash_key", "body", m.HashKey, `^[^\s]+$`); err != nil { + return err + } + + return nil +} + func (m *ServerParams) validateHealthCheckAddress(formats strfmt.Registry) error { if swag.IsZero(m.HealthCheckAddress) { // not required return nil @@ -1773,6 +1803,18 @@ func (m *ServerParams) validateOnMarkedUp(formats strfmt.Registry) error { return nil } +func (m *ServerParams) validatePoolConnName(formats strfmt.Registry) error { + if swag.IsZero(m.PoolConnName) { // not required + return nil + } + + if err := validate.Pattern("pool_conn_name", "body", m.PoolConnName, `^[^\s]+$`); err != nil { + return err + } + + return nil +} + func (m *ServerParams) validateProto(formats strfmt.Registry) error { if swag.IsZero(m.Proto) { // not required return nil diff --git a/models/server_params_compare.go b/models/server_params_compare.go index da1940d0..950c3ba4 100644 --- a/models/server_params_compare.go +++ b/models/server_params_compare.go @@ -157,6 +157,10 @@ func (s ServerParams) Equal(t ServerParams, opts ...Options) bool { return false } + if s.HashKey != t.HashKey { + return false + } + if s.HealthCheckAddress != t.HealthCheckAddress { return false } @@ -249,6 +253,10 @@ func (s ServerParams) Equal(t ServerParams, opts ...Options) bool { return false } + if s.PoolConnName != t.PoolConnName { + return false + } + if !equalPointers(s.PoolLowConn, t.PoolLowConn) { return false } @@ -559,6 +567,10 @@ func (s ServerParams) Diff(t ServerParams, opts ...Options) map[string][]interfa diff["GUID"] = []interface{}{s.GUID, t.GUID} } + if s.HashKey != t.HashKey { + diff["HashKey"] = []interface{}{s.HashKey, t.HashKey} + } + if s.HealthCheckAddress != t.HealthCheckAddress { diff["HealthCheckAddress"] = []interface{}{s.HealthCheckAddress, t.HealthCheckAddress} } @@ -651,6 +663,10 @@ func (s ServerParams) Diff(t ServerParams, opts ...Options) map[string][]interfa diff["OnMarkedUp"] = []interface{}{s.OnMarkedUp, t.OnMarkedUp} } + if s.PoolConnName != t.PoolConnName { + diff["PoolConnName"] = []interface{}{s.PoolConnName, t.PoolConnName} + } + if !equalPointers(s.PoolLowConn, t.PoolLowConn) { diff["PoolLowConn"] = []interface{}{ValueOrNil(s.PoolLowConn), ValueOrNil(t.PoolLowConn)} } diff --git a/models/server_params_compare_test.go b/models/server_params_compare_test.go index aef4f3d8..fc3bdaeb 100644 --- a/models/server_params_compare_test.go +++ b/models/server_params_compare_test.go @@ -213,7 +213,7 @@ func TestServerParamsDiffFalse(t *testing.T) { for _, sample := range samples { result := sample.a.Diff(sample.b) - if len(result) != 91 { + if len(result) != 93 { json := jsoniter.ConfigCompatibleWithStandardLibrary a, err := json.Marshal(&sample.a) if err != nil { @@ -223,7 +223,7 @@ func TestServerParamsDiffFalse(t *testing.T) { if err != nil { t.Errorf(err.Error()) } - t.Errorf("Expected ServerParams to be different in 91 cases, but it is not (%d) %s %s", len(result), a, b) + t.Errorf("Expected ServerParams to be different in 93 cases, but it is not (%d) %s %s", len(result), a, b) } } } diff --git a/specification/build/haproxy_spec.yaml b/specification/build/haproxy_spec.yaml index b3dec98d..4e515ba7 100644 --- a/specification/build/haproxy_spec.yaml +++ b/specification/build/haproxy_spec.yaml @@ -281,6 +281,12 @@ definitions: x-dependency: ssl: value: true + default_crt_list: + items: + pattern: ^[^\s]+$ + type: string + type: array + x-omitempty: true defer_accept: type: boolean ecdhe: @@ -618,6 +624,9 @@ definitions: guid: pattern: ^[A-Za-z0-9-_.:]+$ type: string + hash_key: + pattern: ^[^\s]+$ + type: string health_check_address: pattern: ^[^\s]+$ type: string @@ -720,6 +729,9 @@ definitions: enum: - shutdown-backup-sessions type: string + pool_conn_name: + pattern: ^[^\s]+$ + type: string pool_low_conn: type: integer x-nullable: true diff --git a/specification/models/configuration/bind_params.yaml b/specification/models/configuration/bind_params.yaml index 7fe6cb07..92fd7f74 100644 --- a/specification/models/configuration/bind_params.yaml +++ b/specification/models/configuration/bind_params.yaml @@ -80,6 +80,12 @@ bind_params: x-dependency: ssl: value: true + default_crt_list: + type: array + x-omitempty: true + items: + type: string + pattern: '^[^\s]+$' defer_accept: type: boolean expose_fd_listeners: diff --git a/specification/models/configuration/server_params.yaml b/specification/models/configuration/server_params.yaml index 7a5630a2..be4039dd 100644 --- a/specification/models/configuration/server_params.yaml +++ b/specification/models/configuration/server_params.yaml @@ -130,6 +130,9 @@ server_params: guid: type: string pattern: '^[A-Za-z0-9-_.:]+$' + hash_key: + type: string + pattern: '^[^\s]+$' init-addr: pattern: ^[^\s]+$ type: string @@ -203,6 +206,9 @@ server_params: on-marked-up: type: string enum: [shutdown-backup-sessions] + pool_conn_name: + type: string + pattern: '^[^\s]+$' pool_low_conn: type: integer x-nullable: true diff --git a/test/bind_test.go b/test/bind_test.go index 6630804e..53ca4691 100644 --- a/test/bind_test.go +++ b/test/bind_test.go @@ -128,6 +128,7 @@ func TestCreateEditDeleteBind(t *testing.T) { Nice: 123, QuicSocket: "listener", Nbconn: 12, + DefaultCrtList: []string{"foobar2.pem.rsa", "foobar2.pem.ecdsa"}, }, } diff --git a/test/configuration_test.go b/test/configuration_test.go index b01de4c3..06502b84 100644 --- a/test/configuration_test.go +++ b/test/configuration_test.go @@ -373,7 +373,7 @@ defaults unnamed_defaults_1 frontend test mode http backlog 2048 - bind 192.168.1.1:80 name webserv thread all sigalgs RSA+SHA256 client-sigalgs ECDSA+SHA256:RSA+SHA256 ca-verify-file ca.pem nice 789 guid-prefix guid-example + bind 192.168.1.1:80 name webserv thread all sigalgs RSA+SHA256 client-sigalgs ECDSA+SHA256:RSA+SHA256 ca-verify-file ca.pem nice 789 guid-prefix guid-example default-crt foobar.pem.rsa default-crt foobar.pem.ecdsa bind 192.168.1.1:8080 name webserv2 thread 1/all bind 192.168.1.2:8080 name webserv3 thread 1/1 bind [2a01:c9c0:a3:8::3]:80 name ipv6 thread 1/1-1 @@ -718,7 +718,7 @@ backend test use-server webserv if TRUE use-server webserv2 unless TRUE server webserv 192.168.1.1:9200 maxconn 1000 ssl weight 10 inter 2s cookie BLAH slowstart 6000 proxy-v2-options authority,crc32c ws h1 pool-low-conn 128 id 1234 pool-purge-delay 10s tcp-ut 2s curves secp384r1 client-sigalgs ECDSA+SHA256:RSA+SHA256 sigalgs ECDSA+SHA256 log-bufsize 10 set-proxy-v2-tlv-fmt(0x20) %[fc_pp_tlv(0x20)] - server webserv2 192.168.1.1:9300 maxconn 1000 ssl weight 10 inter 2s cookie BLAH slowstart 6000 proxy-v2-options authority,crc32c ws h1 pool-low-conn 128 + server webserv2 192.168.1.1:9300 maxconn 1000 ssl weight 10 inter 2s cookie BLAH slowstart 6000 proxy-v2-options authority,crc32c ws h1 pool-low-conn 128 hash-key akey pool-conn-name apoolconnname http-request set-dst hdr(x-dst) http-request set-dst-port int(4000) http-check connect diff --git a/test/expected/structured.json b/test/expected/structured.json index 4fc41e93..ba950ed9 100644 --- a/test/expected/structured.json +++ b/test/expected/structured.json @@ -935,7 +935,9 @@ "ws": "h1", "address": "192.168.1.1", "name": "webserv2", - "port": 9300 + "port": 9300, + "pool_conn_name": "apoolconnname", + "hash_key": "akey" } } }, @@ -2532,7 +2534,11 @@ "sigalgs": "RSA+SHA256", "thread": "all", "address": "192.168.1.1", - "port": 80 + "port": 80, + "default_crt_list": [ + "foobar.pem.rsa", + "foobar.pem.ecdsa" + ] }, "webserv2": { "name": "webserv2",