package main import ( "fmt" "io/ioutil" "log" "os" "os/exec" "strings" "github.com/clems4ever/authelia/internal/utils" "github.com/spf13/cobra" ) // HostEntry represents an entry in /etc/hosts type HostEntry struct { Domain string IP string } var hostEntries = []HostEntry{ // For authelia backend HostEntry{Domain: "authelia.example.com", IP: "192.168.240.50"}, // For common tests HostEntry{Domain: "login.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "admin.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "singlefactor.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "dev.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "home.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "mx1.mail.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "mx2.mail.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "public.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "secure.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "mail.example.com", IP: "192.168.240.100"}, HostEntry{Domain: "duo.example.com", IP: "192.168.240.100"}, // For Traefik suite HostEntry{Domain: "traefik.example.com", IP: "192.168.240.100"}, // For testing network ACLs HostEntry{Domain: "proxy-client1.example.com", IP: "192.168.240.201"}, HostEntry{Domain: "proxy-client2.example.com", IP: "192.168.240.202"}, HostEntry{Domain: "proxy-client3.example.com", IP: "192.168.240.203"}, // Kubernetes dashboard HostEntry{Domain: "kubernetes.example.com", IP: "192.168.240.110"}, } func runCommand(cmd string, args ...string) { command := utils.CommandWithStdout(cmd, args...) err := command.Run() if err != nil { panic(err) } } func installNpmPackages() { runCommand("npm", "ci") } func checkCommandExist(cmd string) { fmt.Print("Checking if '" + cmd + "' command is installed...") command := exec.Command("bash", "-c", "command -v "+cmd) err := command.Run() if err != nil { log.Fatal("[ERROR] You must install " + cmd + " on your machine.") } fmt.Println(" OK") } func installClientNpmPackages() { command := utils.CommandWithStdout("npm", "ci") command.Dir = "client" err := command.Run() if err != nil { panic(err) } } func createTemporaryDirectory() { err := os.MkdirAll("/tmp/authelia", 0755) if err != nil { panic(err) } } func bootstrapPrintln(args ...interface{}) { a := make([]interface{}, 0) a = append(a, "[BOOTSTRAP]") a = append(a, args...) fmt.Println(a...) } func shell(cmd string) { runCommand("bash", "-c", cmd) } func buildHelperDockerImages() { shell("docker build -t authelia-example-backend example/compose/nginx/backend") shell("docker build -t authelia-duo-api example/compose/duo-api") shell("docker-compose -f docker-compose.yml -f example/compose/kind/docker-compose.yml build") shell("docker-compose -f docker-compose.yml -f example/compose/authelia/docker-compose.backend.yml build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g)") shell("docker-compose -f docker-compose.yml -f example/compose/authelia/docker-compose.frontend.yml build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g)") } func prepareHostsFile() { contentBytes, err := readHostsFile() if err != nil { panic(err) } lines := strings.Split(string(contentBytes), "\n") toBeAddedLine := make([]string, 0) modified := false for _, entry := range hostEntries { domainInHostFile := false for i, line := range lines { domainFound := strings.Contains(line, entry.Domain) ipFound := strings.Contains(line, entry.IP) if domainFound { domainInHostFile = true // The IP is not up to date. if ipFound { break } else { lines[i] = entry.IP + " " + entry.Domain modified = true break } } } if !domainInHostFile { toBeAddedLine = append(toBeAddedLine, entry.IP+" "+entry.Domain) } } if len(toBeAddedLine) > 0 { lines = append(lines, toBeAddedLine...) modified = true } err = ioutil.WriteFile("/tmp/authelia/hosts", []byte(strings.Join(lines, "\n")), 0644) if err != nil { panic(err) } if modified { bootstrapPrintln("/etc/hosts needs to be updated") shell("/usr/bin/sudo mv /tmp/authelia/hosts /etc/hosts") } } // ReadHostsFile reads the hosts file. func readHostsFile() ([]byte, error) { bs, err := ioutil.ReadFile("/etc/hosts") if err != nil { return nil, err } return bs, nil } func readVersion(cmd string, args ...string) { command := exec.Command(cmd, args...) b, err := command.Output() if err != nil { panic(err) } fmt.Print(cmd + " => " + string(b)) } func readVersions() { readVersion("go", "version") readVersion("node", "--version") readVersion("docker", "--version") readVersion("docker-compose", "--version") } // Bootstrap bootstrap authelia dev environment func Bootstrap(cobraCmd *cobra.Command, args []string) { bootstrapPrintln("Checking command installation...") checkCommandExist("node") checkCommandExist("docker") checkCommandExist("docker-compose") bootstrapPrintln("Getting versions of tools") readVersions() bootstrapPrintln("Checking if GOPATH is set") goPathFound := false for _, v := range os.Environ() { if strings.HasPrefix(v, "GOPATH=") { goPathFound = true break } } if !goPathFound { log.Fatal("GOPATH is not set") } bootstrapPrintln("Installing NPM packages for development...") installNpmPackages() bootstrapPrintln("Building development Docker images...") buildHelperDockerImages() createTemporaryDirectory() bootstrapPrintln("Preparing /etc/hosts to serve subdomains of example.com...") prepareHostsFile() bootstrapPrintln("Run 'authelia-scripts suites setup Standalone' to start Authelia and visit https://home.example.com:8080.") bootstrapPrintln("More details at https://github.com/clems4ever/authelia/blob/master/docs/getting-started.md") }