Skip to content

Commit

Permalink
fix: correctly compare compressed/expanded ipv6 in targets
Browse files Browse the repository at this point in the history
When syncing a target with a compressed IPv6 (for example
`[2001:db8:fd73::e]:1326`) decK can successfully create it
but it fails to detect its existence when pulling the config
from the API because the CP expands the address (for example to
`[2001:0db8:fd73:0000:0000:0000:0000:000e]:1326`) causing
decK to attempt to recreate the target and failing because the
target already exists.

This commit fixes this by using some helpers to expand the
ipv6 before trying to sync/dump.
  • Loading branch information
GGabriele committed Jul 1, 2024
1 parent 1047bbb commit 6681a64
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 0 deletions.
70 changes: 70 additions & 0 deletions pkg/file/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package file

import (
"context"
"encoding/hex"
"errors"
"fmt"
"net"
"reflect"
"regexp"
"sort"
"strings"

"github.com/blang/semver/v4"
"github.com/kong/go-database-reconciler/pkg/konnect"
Expand Down Expand Up @@ -1033,6 +1037,65 @@ func (b *stateBuilder) rbacRoles() {
}
}

var (
IPv6HasPortPattern = regexp.MustCompile(`\]\:\d+$`)
IPv6HasBracketPattern = regexp.MustCompile(`\[\S+\]$`)
)

func isIPv6(hostname string) bool {
parts := strings.Split(hostname, ":")
return len(parts) > 2
}

// expandIPv6 decompress an ipv6 address into its 'long' format.
// for example:
//
// from ::1 to 0000:0000:0000:0000:0000:0000:0000:0001.
func expandIPv6(address string) string {
ip := net.ParseIP(address).To16()
dst := make([]byte, hex.EncodedLen(len(ip)))
hex.Encode(dst, ip)
var final string
for i := 0; i < len(dst); i += 4 {
final += fmt.Sprintf("%s:", dst[i:i+4])
}
if len(final) == 0 {
return ""
}
// remove last colon
return final[:len(final)-1]
}

func normalizeIPv6(target string) (string, error) {
ip := target
var port = "8000"

Check failure on line 1071 in pkg/file/builder.go

View workflow job for this annotation

GitHub Actions / test

File is not `gofumpt`-ed (gofumpt)
match := IPv6HasPortPattern.FindStringSubmatch(target)
if len(match) > 0 {
// has [address]:port pattern
port = strings.ReplaceAll(match[0], "]:", "")
ip = strings.ReplaceAll(target, match[0], "")
ip = removeBrackets(ip)
} else {
match = IPv6HasBracketPattern.FindStringSubmatch(target)
if len(match) > 0 {
ip = removeBrackets(match[0])
}
if net.ParseIP(ip).To16() == nil {
return "", fmt.Errorf("invalid ipv6 address %s", target)
}
}
expandedIPv6 := expandIPv6(ip)
if expandedIPv6 == "" {
return "", fmt.Errorf("invalid ipv6 address %s", target)
}
return fmt.Sprintf("[%s]:%s", expandedIPv6, port), nil
}

func removeBrackets(ip string) string {
ip = strings.ReplaceAll(ip, "[", "")
return strings.ReplaceAll(ip, "]", "")
}

func (b *stateBuilder) upstreams() {
if b.err != nil {
return
Expand Down Expand Up @@ -1087,6 +1150,13 @@ func (b *stateBuilder) ingestTargets(targets []kong.Target) error {
}
utils.MustMergeTags(&t, b.selectTags)
b.defaulter.MustSet(&t)
if t.Target != nil && isIPv6(*t.Target) {
normalizedTarget, err := normalizeIPv6(*t.Target)
if err != nil {
return err
}
t.Target = kong.String(normalizedTarget)
}
b.rawState.Targets = append(b.rawState.Targets, &t)
}
return nil
Expand Down
48 changes: 48 additions & 0 deletions pkg/file/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,54 @@ func Test_stateBuilder_ingestTargets(t *testing.T) {
},
},
},
{
name: "expands IPv6 address correctly",
fields: fields{
currentState: emptyState(),
},
args: args{
targets: []kong.Target{
{
ID: kong.String("d6e7f8a9-bcde-1234-5678-9abcdef01234"),
Target: kong.String("[2001:db8:fd73::e]:1326"),
Upstream: &kong.Upstream{
ID: kong.String("a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
},
},
},
},
wantErr: false,
wantState: &utils.KongRawState{
Targets: []*kong.Target{
{
ID: kong.String("d6e7f8a9-bcde-1234-5678-9abcdef01234"),
Target: kong.String("[2001:0db8:fd73:0000:0000:0000:0000:000e]:1326"),
Weight: kong.Int(100),
Upstream: &kong.Upstream{
ID: kong.String("a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
},
},
},
},
},
{
name: "handles invalid IPv6 address correctly",
fields: fields{
currentState: emptyState(),
},
args: args{
targets: []kong.Target{
{
Target: kong.String("[invalid:ipv6::address]:1326"),
Upstream: &kong.Upstream{
ID: kong.String("b1c2d3e4-f5a6-7890-abcd-ef1234567890"),
},
},
},
},
wantErr: true,
wantState: &utils.KongRawState{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit 6681a64

Please sign in to comment.