mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-07 07:07:05 +02:00
This is a follow-up to our initial work[0] to address RFC-5952 §4 conformance for IPv6 addresses in Vault. The initial pass focused on the vault server configuration and start-up routines. This follow-up focuses on Agent and Proxy, with a few minor improvements for server. The approach generally mirrors the server implementation but also adds support for normalization with CLI configuration overrides. One aspect we do not normalize currently is Agent/Proxy client creation to the Vault server with credentials taken from environment variables, as it would require larger changes to the `api` module. In practice this ought to be fine for the majority of cases. [0]: https://github.com/hashicorp/vault/pull/29228
110 lines
2.8 KiB
Go
110 lines
2.8 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package configutil
|
|
|
|
import (
|
|
"net"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
// NormalizeAddr takes a string of a Host, Host:Port, URL, or Destination
|
|
// Address and returns a copy where any IP addresses have been normalized to be
|
|
// conformant with RFC 5942 §4. If the input string does not match any of the
|
|
// supported syntaxes, or the "host" section is not an IP address, the input
|
|
// will be returned unchanged. Supported syntaxes are:
|
|
//
|
|
// Host host or [host]
|
|
// Host:Port host:port or [host]:port
|
|
// URL scheme://user@host/path?query#frag or scheme://user@[host]/path?query#frag
|
|
// Destination Address user@host:port or user@[host]:port
|
|
//
|
|
// See:
|
|
//
|
|
// https://rfc-editor.org/rfc/rfc3986.html
|
|
// https://rfc-editor.org/rfc/rfc5942.html
|
|
// https://rfc-editor.org/rfc/rfc5952.html
|
|
func NormalizeAddr(addr string) string {
|
|
if addr == "" {
|
|
return ""
|
|
}
|
|
|
|
// Host
|
|
ip := net.ParseIP(addr)
|
|
if ip != nil {
|
|
// net.IP.String() is RFC 5942 §4 compliant
|
|
return ip.String()
|
|
}
|
|
|
|
// [Host]
|
|
if strings.HasPrefix(addr, "[") && strings.HasSuffix(addr, "]") {
|
|
if len(addr) < 3 {
|
|
return addr
|
|
}
|
|
|
|
// If we've been given a bracketed IP address, return the address
|
|
// normalized without brackets.
|
|
ip := net.ParseIP(addr[1 : len(addr)-1])
|
|
if ip != nil {
|
|
return ip.String()
|
|
}
|
|
|
|
// Our input is not a valid schema.
|
|
return addr
|
|
}
|
|
|
|
// Host:Port
|
|
host, port, err := net.SplitHostPort(addr)
|
|
if err == nil {
|
|
ip := net.ParseIP(host)
|
|
if ip == nil {
|
|
// Our host isn't an IP address so we can return it unchanged
|
|
return addr
|
|
}
|
|
|
|
// net.JoinHostPort handles bracketing for RFC 5952 §6
|
|
return net.JoinHostPort(ip.String(), port)
|
|
}
|
|
|
|
// URL
|
|
u, err := url.Parse(addr)
|
|
if err == nil {
|
|
uhost := u.Hostname()
|
|
ip := net.ParseIP(uhost)
|
|
if ip == nil {
|
|
// Our URL doesn't contain an IP address so we can return our input unchanged.
|
|
return addr
|
|
} else {
|
|
uhost = ip.String()
|
|
}
|
|
|
|
if uport := u.Port(); uport != "" {
|
|
uhost = net.JoinHostPort(uhost, uport)
|
|
} else {
|
|
if !strings.HasPrefix(uhost, "[") && !strings.HasSuffix(uhost, "]") {
|
|
// Ensure the IPv6 URL host is bracketed post-normalization.
|
|
// When*url.URL.String() reassembles the URL it will not consider
|
|
// whether or not the *url.URL.Host is RFC 5952 §6 and RFC 3986 §3.2.2
|
|
// conformant.
|
|
uhost = "[" + uhost + "]"
|
|
}
|
|
}
|
|
u.Host = uhost
|
|
|
|
return u.String()
|
|
}
|
|
|
|
// Destination Address
|
|
if idx := strings.LastIndex(addr, "@"); idx > 0 {
|
|
if idx+1 > len(addr) {
|
|
return addr
|
|
}
|
|
|
|
return addr[:idx+1] + NormalizeAddr(addr[idx+1:])
|
|
}
|
|
|
|
// Our input did not match our supported schemas. Return it unchanged.
|
|
return addr
|
|
}
|