103 lines
2.1 KiB
Go
103 lines
2.1 KiB
Go
|
|
package config
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"net/netip"
|
||
|
|
"os"
|
||
|
|
)
|
||
|
|
|
||
|
|
type Peer struct {
|
||
|
|
ID string `json:"id"`
|
||
|
|
Address string `json:"address"`
|
||
|
|
Secret string `json:"secret"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type Bundle struct {
|
||
|
|
WAN string `json:"wan"`
|
||
|
|
Peers []Peer `json:"peers"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type Runtime struct {
|
||
|
|
Bundle
|
||
|
|
Path Path `json:"path"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type Cloud struct {
|
||
|
|
Tenancy string `json:"tenancy"`
|
||
|
|
User string `json:"user"`
|
||
|
|
Fingerprint string `json:"fingerprint"`
|
||
|
|
Region string `json:"region"`
|
||
|
|
Compartment string `json:"compartment"`
|
||
|
|
DRG string `json:"drg"`
|
||
|
|
Address string `json:"address"`
|
||
|
|
Network string `json:"network"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type Path struct {
|
||
|
|
Address string `json:"address"`
|
||
|
|
Network string `json:"network"`
|
||
|
|
Gateway string `json:"gateway"`
|
||
|
|
Ports []uint16 `json:"ports"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func Read[T any](path string) (T, error) {
|
||
|
|
var value T
|
||
|
|
data, err := os.ReadFile(path)
|
||
|
|
if err != nil {
|
||
|
|
return value, err
|
||
|
|
}
|
||
|
|
err = json.Unmarshal(data, &value)
|
||
|
|
return value, err
|
||
|
|
}
|
||
|
|
|
||
|
|
func (b Bundle) Validate() error {
|
||
|
|
if err := IPv4(b.WAN); err != nil {
|
||
|
|
return fmt.Errorf("WAN: %w", err)
|
||
|
|
}
|
||
|
|
if len(b.Peers) != 2 {
|
||
|
|
return fmt.Errorf("expected two OCI headends")
|
||
|
|
}
|
||
|
|
for _, p := range b.Peers {
|
||
|
|
if p.ID == "" || p.Secret == "" {
|
||
|
|
return fmt.Errorf("incomplete headend")
|
||
|
|
}
|
||
|
|
if err := IPv4(p.Address); err != nil {
|
||
|
|
return fmt.Errorf("headend: %w", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if b.Peers[0].ID == b.Peers[1].ID || b.Peers[0].Address == b.Peers[1].Address {
|
||
|
|
return fmt.Errorf("duplicate headend")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (p Path) Validate() error {
|
||
|
|
if err := IPv4(p.Address); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if err := IPv4(p.Gateway); err != nil {
|
||
|
|
return fmt.Errorf("Gateway: %w", err)
|
||
|
|
}
|
||
|
|
n, err := netip.ParsePrefix(p.Network)
|
||
|
|
if err != nil || !n.Addr().Is4() {
|
||
|
|
return fmt.Errorf("invalid OCI network")
|
||
|
|
}
|
||
|
|
if len(p.Ports) == 0 {
|
||
|
|
return fmt.Errorf("empty service allowlist")
|
||
|
|
}
|
||
|
|
for _, port := range p.Ports {
|
||
|
|
if port == 0 {
|
||
|
|
return fmt.Errorf("invalid service port")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func IPv4(value string) error {
|
||
|
|
ip, err := netip.ParseAddr(value)
|
||
|
|
if err != nil || !ip.Is4() || !ip.IsGlobalUnicast() {
|
||
|
|
return fmt.Errorf("invalid unicast IPv4")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|