authelia/cmd/authelia-scripts/cmd_bootstrap.go

228 lines
5.6 KiB
Go

package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"strings"
"github.com/clems4ever/authelia/utils"
"github.com/spf13/cobra"
)
// HostEntry represents an entry in /etc/hosts
type HostEntry struct {
Domain string
IP string
}
var hostEntries = []HostEntry{
// 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")
}