123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- package wg
- import (
- "bytes"
- _ "embed"
- "fmt"
- "log"
- "os/exec"
- "text/template"
- )
- const (
- ExitSetupSuccess = 0
- ExitSetupFailed = 1
- )
- const (
- ENV_WG_TUN_FD = "WG_TUN_FD"
- ENV_WG_UAPI_FD = "WG_UAPI_FD"
- ENV_WG_PROCESS_FOREGROUND = "WG_PROCESS_FOREGROUND"
- )
- //go:embed wireguard.conf.templ
- var confTmpl string
- type WireguardTemplateSeed struct {
- VpnClientPrivateKey string
- VpnClientAddress string
- Peers []WireguardTemplatePeer
- }
- type WireguardTemplatePeer struct {
- Pubkey string
- Address string
- Port int
- }
- /*
- Render out a client configuration file, utilizing the data provided from Semaphore and the daemon keyring
- :param tmpl: a template.Template that will be populated with the VPN data
- :param wgData: a WireGuardTemplateSeed struct that contains all the info needed to populate a wireguard config file
- */
- func RenderClientConfiguration(wgData WireguardTemplateSeed) ([]byte, error) {
- buff := bytes.NewBuffer([]byte{})
- tmpl, err := template.New("wireguard.conf.templ").Parse(confTmpl)
- if err != nil {
- log.Fatal(err)
- }
- err = tmpl.Execute(buff, wgData)
- if err != nil {
- return buff.Bytes(), &TemplatingError{TemplateData: wgData, Msg: err.Error()}
- }
- return buff.Bytes(), nil
- }
- /*
- Start a wireguard interface
- */
- func ChangeWgInterfaceState(intfName string, state string) ([]byte, error) {
- wgCmd := exec.Command("wg-quick", state, intfName)
- return wgCmd.Output()
- /*
- var foreground bool
- if !foreground {
- foreground = os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1"
- }
- // get log level (default: info)
- logLevel := func() int {
- switch os.Getenv("LOG_LEVEL") {
- case "verbose", "debug":
- return device.LogLevelVerbose
- case "error":
- return device.LogLevelError
- case "silent":
- return device.LogLevelSilent
- }
- return device.LogLevelError
- }()
- // open TUN device (or use supplied fd)
- tdev, err := func() (tun.Device, error) {
- tunFdStr := os.Getenv(ENV_WG_TUN_FD)
- if tunFdStr == "" {
- return tun.CreateTUN(intfName, device.DefaultMTU)
- }
- // construct tun device from supplied fd
- fd, err := strconv.ParseUint(tunFdStr, 10, 32)
- if err != nil {
- return nil, err
- }
- err = unix.SetNonblock(int(fd), true)
- if err != nil {
- return nil, err
- }
- file := os.NewFile(uintptr(fd), "")
- return tun.CreateTUNFromFile(file, device.DefaultMTU)
- }()
- if err == nil {
- realInterfaceName, err2 := tdev.Name()
- if err2 == nil {
- intfName = realInterfaceName
- }
- }
- logger := device.NewLogger(
- logLevel,
- fmt.Sprintf("(%s) ", intfName),
- )
- if err != nil {
- logger.Errorf("Failed to create TUN device: %v", err)
- os.Exit(ExitSetupFailed)
- }
- // open UAPI file (or use supplied fd)
- fileUAPI, err := func() (*os.File, error) {
- uapiFdStr := os.Getenv(ENV_WG_UAPI_FD)
- if uapiFdStr == "" {
- return ipc.UAPIOpen(intfName)
- }
- // use supplied fd
- fd, err := strconv.ParseUint(uapiFdStr, 10, 32)
- if err != nil {
- return nil, err
- }
- return os.NewFile(uintptr(fd), ""), nil
- }()
- if err != nil {
- logger.Errorf("UAPI listen error: %v", err)
- os.Exit(ExitSetupFailed)
- return
- }
- // daemonize the process
- if !foreground {
- env := os.Environ()
- env = append(env, fmt.Sprintf("%s=3", ENV_WG_TUN_FD))
- env = append(env, fmt.Sprintf("%s=4", ENV_WG_UAPI_FD))
- env = append(env, fmt.Sprintf("%s=1", ENV_WG_PROCESS_FOREGROUND))
- files := [3]*os.File{}
- if os.Getenv("LOG_LEVEL") != "" && logLevel != device.LogLevelSilent {
- files[0], _ = os.Open(os.DevNull)
- files[1] = os.Stdout
- files[2] = os.Stderr
- } else {
- files[0], _ = os.Open(os.DevNull)
- files[1], _ = os.Open(os.DevNull)
- files[2], _ = os.Open(os.DevNull)
- }
- attr := &os.ProcAttr{
- Files: []*os.File{
- files[0], // stdin
- files[1], // stdout
- files[2], // stderr
- tdev.File(),
- fileUAPI,
- },
- Dir: ".",
- Env: env,
- }
- path, err := os.Executable()
- if err != nil {
- logger.Errorf("Failed to determine executable: %v", err)
- os.Exit(ExitSetupFailed)
- }
- process, err := os.StartProcess(
- path,
- os.Args,
- attr,
- )
- if err != nil {
- logger.Errorf("Failed to daemonize: %v", err)
- os.Exit(ExitSetupFailed)
- }
- process.Release()
- return
- }
- device := device.NewDevice(tdev, conn.NewDefaultBind(), logger)
- logger.Verbosef("Device started")
- errs := make(chan error)
- term := make(chan os.Signal, 1)
- uapi, err := ipc.UAPIListen(intfName, fileUAPI)
- if err != nil {
- logger.Errorf("Failed to listen on uapi socket: %v", err)
- os.Exit(ExitSetupFailed)
- }
- go func() {
- for {
- conn, err := uapi.Accept()
- if err != nil {
- errs <- err
- return
- }
- go device.IpcHandle(conn)
- }
- }()
- logger.Verbosef("UAPI listener started")
- // wait for program to terminate
- signal.Notify(term, unix.SIGTERM)
- signal.Notify(term, os.Interrupt)
- select {
- case <-term:
- case <-errs:
- case <-device.Wait():
- }
- // clean up
- uapi.Close()
- device.Close()
- logger.Verbosef("Shutting down")
- */
- }
- type TemplatingError struct {
- TemplateData WireguardTemplateSeed
- Msg string
- }
- func (t *TemplatingError) Error() string {
- return fmt.Sprintf("There was an error executing the template file: %s, Seed data: %+v\n", t.Msg, t.TemplateData)
- }
|