k3s/vendor/github.com/flannel-io/flannel/backend/ipsec/handle_charon.go

259 lines
6.3 KiB
Go

// Copyright 2017 flannel authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !windows
package ipsec
import (
"fmt"
"net"
"os"
"os/exec"
"strings"
"sync"
"syscall"
"time"
"github.com/bronze1man/goStrongswanVici"
"github.com/flannel-io/flannel/subnet"
"golang.org/x/net/context"
log "k8s.io/klog"
)
type Uri struct {
network, address string
}
type CharonIKEDaemon struct {
viciUri Uri
espProposal string
ctx context.Context
}
func NewCharonIKEDaemon(ctx context.Context, wg *sync.WaitGroup, espProposal string) (*CharonIKEDaemon, error) {
charon := &CharonIKEDaemon{ctx: ctx, espProposal: espProposal}
addr := strings.Split("unix:///var/run/charon.vici", "://")
charon.viciUri = Uri{addr[0], addr[1]}
execPath, err := findExecPath()
if err != nil {
log.Errorf("Charon daemon not found: %v", err)
return nil, err
}
cmd, err := charon.run(execPath)
if err != nil {
log.Errorf("Error starting charon daemon: %v", err)
return nil, err
} else {
log.Info("Charon daemon started")
}
wg.Add(1)
go func() {
select {
case <-ctx.Done():
cmd.Process.Signal(syscall.SIGTERM)
cmd.Wait()
log.Infof("Stopped charon daemon")
wg.Done()
}
}()
return charon, nil
}
func (charon *CharonIKEDaemon) getClient(wait bool) (client *goStrongswanVici.ClientConn, err error) {
for {
socket_conn, err := net.Dial(charon.viciUri.network, charon.viciUri.address)
if err == nil {
return goStrongswanVici.NewClientConn(socket_conn), nil
} else {
if wait {
select {
case <-charon.ctx.Done():
log.Error("Cancel waiting for charon")
return nil, err
default:
log.Errorf("ClientConnection failed: %v", err)
}
log.Info("Retrying in a second ...")
time.Sleep(time.Second)
} else {
return nil, err
}
}
}
}
func (charon *CharonIKEDaemon) run(execPath string) (cmd *exec.Cmd, err error) {
cmd = &exec.Cmd{
Path: execPath,
SysProcAttr: &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
},
Stdout: os.Stdout,
Stderr: os.Stderr,
}
err = cmd.Start()
return
}
func (charon *CharonIKEDaemon) LoadSharedKey(remotePublicIP, password string) error {
var err error
var client *goStrongswanVici.ClientConn
client, err = charon.getClient(true)
if err != nil {
log.Errorf("Failed to acquire Vici client: %v", err)
return err
}
defer client.Close()
sharedKey := &goStrongswanVici.Key{
Typ: "IKE",
Data: password,
Owners: []string{remotePublicIP},
}
for {
err = client.LoadShared(sharedKey)
if err != nil {
log.Errorf("Failed to load my key. Retrying. %v", err)
time.Sleep(time.Second)
} else {
break
}
}
log.Infof("Loaded shared key for: %v", remotePublicIP)
return nil
}
func (charon *CharonIKEDaemon) LoadConnection(localLease, remoteLease *subnet.Lease,
reqID, encap string) error {
var err error
var client *goStrongswanVici.ClientConn
client, err = charon.getClient(true)
if err != nil {
log.Errorf("Failed to acquire Vici client: %s", err)
return err
}
defer client.Close()
childConfMap := make(map[string]goStrongswanVici.ChildSAConf)
childSAConf := goStrongswanVici.ChildSAConf{
Local_ts: []string{localLease.Subnet.String()},
Remote_ts: []string{remoteLease.Subnet.String()},
ESPProposals: []string{charon.espProposal},
StartAction: "start",
CloseAction: "trap",
Mode: "tunnel",
ReqID: reqID,
// RekeyTime: rekeyTime,
InstallPolicy: "no",
}
childSAConfName := formatChildSAConfName(localLease, remoteLease)
childConfMap[childSAConfName] = childSAConf
localAuthConf := goStrongswanVici.AuthConf{
AuthMethod: "psk",
}
remoteAuthConf := goStrongswanVici.AuthConf{
AuthMethod: "psk",
}
ikeConf := goStrongswanVici.IKEConf{
LocalAddrs: []string{localLease.Attrs.PublicIP.String()},
RemoteAddrs: []string{remoteLease.Attrs.PublicIP.String()},
Proposals: []string{"aes256-sha256-modp4096"},
Version: "2",
KeyingTries: "0", //continues to retry
LocalAuth: localAuthConf,
RemoteAuth: remoteAuthConf,
Children: childConfMap,
Encap: encap,
}
ikeConfMap := make(map[string]goStrongswanVici.IKEConf)
connectionName := formatConnectionName(localLease, remoteLease)
ikeConfMap[connectionName] = ikeConf
err = client.LoadConn(&ikeConfMap)
if err != nil {
return err
}
log.Infof("Loaded connection: %v", connectionName)
return nil
}
func (charon *CharonIKEDaemon) UnloadCharonConnection(localLease,
remoteLease *subnet.Lease) error {
client, err := charon.getClient(false)
if err != nil {
log.Errorf("Failed to acquire Vici client: %s", err)
return err
}
defer client.Close()
connectionName := formatConnectionName(localLease, remoteLease)
unloadConnRequest := &goStrongswanVici.UnloadConnRequest{
Name: connectionName,
}
err = client.UnloadConn(unloadConnRequest)
if err != nil {
return err
}
log.Infof("Unloaded connection: %v", connectionName)
return nil
}
func formatConnectionName(localLease, remoteLease *subnet.Lease) string {
return fmt.Sprintf("%s-%s-%s-%s", localLease.Attrs.PublicIP,
localLease.Subnet, remoteLease.Subnet, remoteLease.Attrs.PublicIP)
}
func formatChildSAConfName(localLease, remoteLease *subnet.Lease) string {
return fmt.Sprintf("%s-%s", localLease.Subnet, remoteLease.Subnet)
}
func findExecPath() (string, error) {
// try well known charon paths
paths := []string{
"charon", // PATH
"/usr/lib/strongswan/charon", // alpine, arch, flannel container
"/usr/lib/ipsec/charon", // debian/ubuntu
"/usr/libexec/strongswan/charon", // centos/rhel
"/usr/libexec/ipsec/charon", // opensuse/sles
}
for _, path := range paths {
path, err := exec.LookPath(path)
if err != nil {
log.Warningf("No valid charon executable found at path %s: %v", path, err)
continue
}
return path, nil
}
err := fmt.Errorf("No valid charon executable found at paths %v", paths)
return "", err
}