package main import ( "crypto/md5" "encoding/hex" "fmt" "io" "io/ioutil" "log" "net" "net/http" "os" "os/exec" "strings" ) func usage() { fmt.Printf("Usage: %s \n", os.Args[0]) fmt.Printf("Example: %s https://keyserver.example.com /path/to/image.img \n", os.Args[0]) fmt.Printf("Example: %s https://keyserver.example.com /path/to/image.img /dev/mapper/crypt1\n", os.Args[0]) os.Exit(1) } func fileExists(path string) bool { _, err := os.Stat(path) return err == nil } func deviceExists(device string) bool { _, err := os.Stat("/dev/mapper/" + device) return err == nil } func keyserverReachable(url string) bool { resp, err := http.Head(url) if err != nil { return false } resp.Body.Close() return resp.StatusCode < 400 } func getUUID() string { // Get CPU model name cpuinfo, _ := ioutil.ReadFile("/proc/cpuinfo") var model string for _, line := range strings.Split(string(cpuinfo), "\n") { if strings.HasPrefix(line, "model name") { model = line break } } // Get eth0 MAC address iface, err := net.InterfaceByName("eth0") mac := "" if err == nil { mac = iface.HardwareAddr.String() } // Get system UUID out, _ := exec.Command("sudo", "dmidecode", "-s", "system-uuid").Output() sysuuid := strings.TrimSpace(string(out)) uuid := model + mac + sysuuid return uuid } func main() { log.SetFlags(0) if len(os.Args) != 4 { usage() } keyserver := os.Args[1] image := os.Args[2] device := os.Args[3] if !fileExists(image) { log.Printf("open_volume: (%s) Image file not found", device) os.Exit(1) } if !keyserverReachable(keyserver) { log.Printf("open_volume: (%s) Key server not reachable, exiting.", device) os.Exit(1) } if deviceExists(device) { fmt.Printf("open_volume: (%s) Device already open, exiting.\n", device) os.Exit(1) } uuid := getUUID() h := md5.Sum([]byte(uuid)) keyid := hex.EncodeToString(h[:]) log.Printf("open_volume: (%s) Downloading key from key server,", device) keyurl := fmt.Sprintf("%s/%s", keyserver, keyid) resp, err := http.Get(keyurl) if err != nil || resp.StatusCode != 200 { log.Printf("open_volume: (%s) Failed to download key from key server", device) os.Exit(1) } defer resp.Body.Close() tmpfile, err := ioutil.TempFile("", "keyfile") if err != nil { log.Printf("open_volume: (%s) Failed to create temp file", device) os.Exit(1) } defer os.Remove(tmpfile.Name()) _, err = io.Copy(tmpfile, resp.Body) tmpfile.Close() if err != nil { log.Printf("open_volume: (%s) Failed to save key file", device) os.Exit(1) } log.Printf("open_volume:'%s' Key downloaded successfully, opening volume", device) cmd := exec.Command("/usr/sbin/cryptsetup", "luksOpen", image, device, "-d", tmpfile.Name()) out, err := cmd.CombinedOutput() if err != nil { log.Printf("open_volume: (%s) Failed to open volume: %s", device, string(out)) os.Exit(1) } log.Printf("open_volume: (%s) Volume opened successfully, removing key file", device) os.Exit(0) }