118 lines
3.8 KiB
Go
118 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/oracle/oci-go-sdk/v65/common"
|
|
"github.com/oracle/oci-go-sdk/v65/core"
|
|
)
|
|
|
|
type networkSecurityGroupClient interface {
|
|
UpdateNetworkSecurityGroupSecurityRules(context.Context, core.UpdateNetworkSecurityGroupSecurityRulesRequest) (core.UpdateNetworkSecurityGroupSecurityRulesResponse, error)
|
|
}
|
|
|
|
type ociReconciler struct {
|
|
client networkSecurityGroupClient
|
|
networkSecurityGroup string
|
|
securityRule string
|
|
wireguardDestinationPort int
|
|
wireguardURL string
|
|
recoveryTimeout time.Duration
|
|
probeWireGuard func(context.Context, string, time.Duration) error
|
|
}
|
|
|
|
func newOCIReconciler(cfg config) (*ociReconciler, error) {
|
|
provider := common.NewRawConfigurationProvider(
|
|
cfg.ociTenancy,
|
|
cfg.ociUser,
|
|
cfg.ociRegion,
|
|
cfg.ociFingerprint,
|
|
cfg.ociPrivateKey,
|
|
nil,
|
|
)
|
|
client, err := core.NewVirtualNetworkClientWithConfigurationProvider(provider)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create OCI virtual network client: %w", err)
|
|
}
|
|
return &ociReconciler{
|
|
client: &client,
|
|
networkSecurityGroup: cfg.networkSecurityGroup,
|
|
securityRule: cfg.securityRule,
|
|
wireguardDestinationPort: cfg.wireguardPort,
|
|
wireguardURL: cfg.wireguardURL,
|
|
recoveryTimeout: cfg.recoveryTimeout,
|
|
probeWireGuard: waitForWireGuard,
|
|
}, nil
|
|
}
|
|
|
|
func (r *ociReconciler) Reconcile(ctx context.Context, source net.IP) error {
|
|
if !validPublicIPv4(source) {
|
|
return fmt.Errorf("refuse invalid WAN source %q", source)
|
|
}
|
|
port := r.wireguardDestinationPort
|
|
protocol := "17"
|
|
description := "Home WireGuard peer"
|
|
sourceCIDR := source.String() + "/32"
|
|
response, err := r.client.UpdateNetworkSecurityGroupSecurityRules(ctx, core.UpdateNetworkSecurityGroupSecurityRulesRequest{
|
|
NetworkSecurityGroupId: common.String(r.networkSecurityGroup),
|
|
UpdateNetworkSecurityGroupSecurityRulesDetails: core.UpdateNetworkSecurityGroupSecurityRulesDetails{
|
|
SecurityRules: []core.UpdateSecurityRuleDetails{{
|
|
Direction: core.UpdateSecurityRuleDetailsDirectionIngress,
|
|
Id: common.String(r.securityRule),
|
|
Protocol: common.String(protocol),
|
|
Description: common.String(description),
|
|
Source: common.String(sourceCIDR),
|
|
SourceType: core.UpdateSecurityRuleDetailsSourceTypeCidrBlock,
|
|
IsStateless: common.Bool(false),
|
|
UdpOptions: &core.UdpOptions{DestinationPortRange: &core.PortRange{Min: common.Int(port), Max: common.Int(port)}},
|
|
}},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("update WireGuard NSG source: %w", err)
|
|
}
|
|
if err := verifyUpdatedRule(response.SecurityRules, r.securityRule, sourceCIDR, port); err != nil {
|
|
return err
|
|
}
|
|
if err := r.probeWireGuard(ctx, r.wireguardURL, r.recoveryTimeout); err != nil {
|
|
return fmt.Errorf("verify WireGuard connectivity: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func verifyUpdatedRule(rules []core.SecurityRule, id, source string, port int) error {
|
|
if len(rules) != 1 {
|
|
return fmt.Errorf("OCI returned %d updated rules, want 1", len(rules))
|
|
}
|
|
rule := rules[0]
|
|
if stringValue(rule.Id) != id || stringValue(rule.Source) != source ||
|
|
stringValue(rule.Protocol) != "17" || rule.Direction != core.SecurityRuleDirectionIngress ||
|
|
rule.SourceType != core.SecurityRuleSourceTypeCidrBlock || boolValue(rule.IsStateless) ||
|
|
rule.UdpOptions == nil || rule.UdpOptions.DestinationPortRange == nil ||
|
|
intValue(rule.UdpOptions.DestinationPortRange.Min) != port ||
|
|
intValue(rule.UdpOptions.DestinationPortRange.Max) != port {
|
|
return fmt.Errorf("OCI returned an unexpected WireGuard rule")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func stringValue(value *string) string {
|
|
if value == nil {
|
|
return ""
|
|
}
|
|
return *value
|
|
}
|
|
|
|
func boolValue(value *bool) bool {
|
|
return value != nil && *value
|
|
}
|
|
|
|
func intValue(value *int) int {
|
|
if value == nil {
|
|
return 0
|
|
}
|
|
return *value
|
|
}
|