package docker import ( "context" "fmt" "goseg/config" "io/ioutil" "os" "path/filepath" "encoding/base64" "strings" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" "github.com/docker/docker/api/types" // "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) func LoadWireguard() error { logger.Info("Loading Startram Wireguard container") confPath := filepath.Join(config.BasePath, "settings", "wireguard.json") _, err := os.Open(confPath) if err != nil { // create a default if it doesn't exist err = config.CreateDefaultWGConf() if err != nil { // error if we can't create it errmsg := fmt.Sprintf("Unable to create WG config! %v", err) logger.Error(errmsg) panic(errmsg) } } logger.Info("Running Wireguard") info, err := StartContainer("wireguard", "wireguard") if err != nil { logger.Error(fmt.Sprintf("Error starting wireguard: %v", err)) return err } config.UpdateContainerState("wireguard", info) return nil } // wireguard container config builder func wgContainerConf() (container.Config, container.HostConfig, error) { var containerConfig container.Config var hostConfig container.HostConfig // construct the container metadata from version server info containerInfo, err := GetLatestContainerInfo("wireguard") if err != nil { return containerConfig, hostConfig, err } desiredTag := containerInfo["tag"] desiredHash := containerInfo["hash"] desiredRepo := containerInfo["repo"] desiredImage := fmt.Sprintf("%s:%s@sha256:%s", desiredRepo, desiredTag, desiredHash) // construct the container config struct containerConfig = container.Config{ Image: desiredImage, Entrypoint: []string{"/bin/bash"}, Tty: true, OpenStdin: true, } // always on wg nw hostConfig = container.HostConfig{ NetworkMode: "container:wireguard", } return containerConfig, hostConfig, nil } // wg client config builder func buildWgConf() (string, error) { confB64 := config.StartramConfig.Conf confBytes, err := base64.StdEncoding.DecodeString(confB64) if err != nil { return "", fmt.Errorf("Failed to decode remote WG base64: %v", err) } conf := string(confBytes) configData := config.Conf() res := strings.Replace(conf, "privkey", configData.Privkey, -1) return res, nil } // write wg config if it doesn't exist or doesn't match func writeWgConf() error { volumeExists := true // read existing and build current conf filePath := filepath.Join(config.DockerDir, "settings", "wireguard", "_data", "wg0.conf") existingConf, err := ioutil.ReadFile(filePath) if err != nil { volumeExists = false } newConf, err := buildWgConf() if err != nil { return err } ctx := context.Background() cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { return err } _, err = cli.VolumeInspect(ctx, "wireguard") if err != nil { volumeExists = false } // if theyre different, or if the volume doesnt exist, copy the new config to the volume if string(existingConf) != newConf || !volumeExists { err = ioutil.WriteFile("tmp/wg0.conf", []byte(newConf), 0644) if err != nil { return fmt.Errorf("Failed to write new WG config: %v", err) } // copy to volume err = copyFileToVolume(filepath.Join("tmp","wg0.conf"), "/etc/wireguard/", "wireguard") if err != nil { return fmt.Errorf("Failed to copy WG config file to volume: %v", err) } } return nil } // write wg conf to volume func copyFileToVolume(filePath string, targetPath string, volumeName string) error { ctx := context.Background() cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { return err } // temp container to mount resp, err := cli.ContainerCreate(ctx, &container.Config{ Image: "busybox", Cmd: []string{"tail", "-f", "/dev/null"}, }, &container.HostConfig{ Binds: []string{volumeName + ":" + targetPath}, }, nil, nil, "bb_temp") if err != nil { return err } if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil { return err } file, err := os.Open(filepath.Join(filePath)) if err != nil { return fmt.Errorf("failed to open wg0 file: %v", err) } defer file.Close() // Copy the file to the volume via the temporary container err = cli.CopyToContainer(ctx, resp.ID, targetPath, file, types.CopyToContainerOptions{}) if err != nil { return err } // remove temporary container if err := cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true}); err != nil { return err } return nil }