Go client for HAProxy configuration and runtime API
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
5.2 KiB

package runtime
import (
"fmt"
"regexp"
"strconv"
"strings"
native_errors "github.com/haproxytech/client-native/v6/errors"
)
type CrtLists []*CrtList
type CrtList struct {
File string
}
type CrtListEntries []*CrtListEntry
type CrtListEntry struct {
LineNumber int
File string
SSLBindConfig string
SNIFilter []string
}
// ShowCrtLists returns CrtList files description from runtime
func (s *SingleRuntime) ShowCrtLists() (CrtLists, error) {
response, err := s.ExecuteWithResponse("show ssl crt-list")
if err != nil {
return nil, fmt.Errorf("%s %w", err.Error(), native_errors.ErrNotFound)
}
return s.parseCrtLists(response), nil
}
// parseCrtLists parses output from `show crt-list` command and return array of crt-list files
// First line in output represents format and is ignored
// Sample output format:
// /etc/ssl/crt-list
// /etc/ssl/...
func (s *SingleRuntime) parseCrtLists(output string) CrtLists {
output = strings.TrimSpace(output)
if output == "" {
return nil
}
crtLists := CrtLists{}
lines := strings.Split(output, "\n")
for _, line := range lines {
c := s.parseCrtList(line)
if c != nil {
crtLists = append(crtLists, c)
}
}
return crtLists
}
// parseCrtList parses one line from CrtList files array and return it structured
func (s *SingleRuntime) parseCrtList(line string) *CrtList {
if line == "" {
return nil
}
crtList := &CrtList{
File: line,
}
return crtList
}
// GetCrtList returns one structured runtime CrtList file
func (s *SingleRuntime) GetCrtList(file string) (*CrtList, error) {
crtLists, err := s.ShowCrtLists()
if err != nil {
return nil, err
}
for _, m := range crtLists {
if m.File == file {
return m, nil
}
}
return nil, fmt.Errorf("%s %w", file, native_errors.ErrNotFound)
}
// ShowCrtListEntries returns one CrtList runtime entries
func (s *SingleRuntime) ShowCrtListEntries(file string) (CrtListEntries, error) {
cmd := fmt.Sprintf("show ssl crt-list -n %s", file)
response, err := s.ExecuteWithResponse(cmd)
if err != nil {
return nil, fmt.Errorf("%s %w", err.Error(), native_errors.ErrNotFound)
}
return ParseCrtListEntries(response)
}
// ParseCrtListEntries parses array of entries in one CrtList file
// One line sample entry:
// /etc/ssl/cert-0.pem !*.crt-test.platform.domain.com !connectivitynotification.platform.domain.com !connectivitytunnel.platform.domain.com !authentication.cert.another.domain.com !*.authentication.cert.another.domain.com
// /etc/ssl/cert-1.pem [verify optional ca-file /etc/ssl/ca-file-1.pem] *.crt-test.platform.domain.com !connectivitynotification.platform.domain.com !connectivitytunnel.platform.domain.com !authentication.cert.another.domain.com !*.authentication.cert.another.domain.com
// /etc/ssl/cert-2.pem [verify required ca-file /etc/ssl/ca-file-2.pem]
func ParseCrtListEntries(output string) (CrtListEntries, error) {
output = strings.TrimSpace(output)
if output == "" || strings.HasPrefix(output, "didn't find the specified filename") {
return nil, native_errors.ErrNotFound
}
ce := CrtListEntries{}
lines := strings.Split(strings.TrimSpace(output), "\n")
for _, line := range lines {
entry := parseCrtListEntry(line)
if entry != nil {
ce = append(ce, entry)
}
}
return ce, nil
}
// parseCrtListEntry parses one entry in one CrtList file/runtime and returns it structured
// example:
// cert1.pem
// cert2.pem [alpn h2,http/1.1]
// certW.pem *.domain.tld !secure.domain.tld
// certS.pem [curves X25519:P-256 ciphers ECDHE-ECDSA-AES256-GCM-SHA384] secure.domain.tld
func parseCrtListEntry(line string) *CrtListEntry {
if line == "" || strings.HasPrefix(strings.TrimSpace(line), "#") {
return nil
}
c := &CrtListEntry{}
re := regexp.MustCompile(`(\S+)(?:\s\[(.*)\])?(?:\s(.*))?`)
matches := re.FindStringSubmatch(line)
if matches != nil {
split := strings.Split(matches[1], ":")
linenumber, _ := strconv.ParseInt(split[1], 0, 32)
c.LineNumber = int(linenumber)
c.File = split[0]
c.SSLBindConfig = matches[2]
c.SNIFilter = strings.Fields(matches[3])
}
return c
}
// AddCrtListEntry adds an entry into the CrtList file
func (s *SingleRuntime) AddCrtListEntry(crtList string, entry CrtListEntry) error {
cmd := fmt.Sprintf("add ssl crt-list %s <<\n%s", crtList, entry.File)
if entry.SSLBindConfig != "" {
cmd = fmt.Sprintf("%s [%s]", cmd, entry.SSLBindConfig)
}
for _, sni := range entry.SNIFilter {
cmd = fmt.Sprintf("%s %s", cmd, sni)
}
cmd += "\n"
response, err := s.ExecuteWithResponse(cmd)
if err != nil {
return fmt.Errorf("%s %w", err.Error(), native_errors.ErrGeneral)
}
if !strings.Contains(response, "Success") {
return fmt.Errorf("%s %w", response, native_errors.ErrGeneral)
}
return nil
}
// DeleteCrtListEntry deletes all the CrtList entries from the CrtList by its id
func (s *SingleRuntime) DeleteCrtListEntry(crtList, certFile string, lineNumber int) error {
cmd := fmt.Sprintf("del ssl crt-list %s %s:%v", crtList, certFile, lineNumber)
response, err := s.ExecuteWithResponse(cmd)
if err != nil {
return fmt.Errorf("%s %w", err.Error(), native_errors.ErrNotFound)
}
if !strings.Contains(response, "deleted in crtlist") {
return fmt.Errorf("%s %w", response, native_errors.ErrGeneral)
}
return nil
}