diff --git a/.gitignore b/.gitignore index 2a1c43e..0d42487 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ *.iws out/ dist/ +data/ ssh.key .testCoverage.txt wg_portal.db diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8dffc61 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,48 @@ +# Dockerfile References: https://docs.docker.com/engine/reference/builder/ +# This dockerfile uses a multi-stage build system to reduce the image footprint. + +######- +# Start from the latest golang base image as builder image (only used to compile the code) +######- +FROM golang:1.15 as builder + +RUN mkdir /build + +# Copy the source from the current directory to the Working Directory inside the container +ADD . /build/ + +# Set the Current Working Directory inside the container +WORKDIR /build + +# Build the Go app +RUN make build + +######- +# Here starts the main image +######- +FROM debian:buster + +# Setup timezone +ENV TZ=Europe/Vienna + +# GOSS for container health checks +ENV GOSS_VERSION v0.3.14 +RUN apt-get update && apt-get upgrade -y && \ + apt-get install --no-install-recommends -y moreutils ca-certificates curl && \ + rm -rf /var/cache/apt /var/lib/apt/lists/*; \ + curl -L https://github.com/aelsabbahy/goss/releases/download/$GOSS_VERSION/goss-linux-amd64 -o /usr/local/bin/goss && \ + chmod +rx /usr/local/bin/goss && \ + goss --version + +COPY --from=builder /build/dist/wg-portal /app/ +COPY --from=builder /build/dist/assets /app/assets +COPY --from=builder /build/scripts /app/ + +# Set the Current Working Directory inside the container +WORKDIR /app + +# Command to run the executable +CMD [ "/app/wg-portal" ] + +HEALTHCHECK --interval=1m --timeout=10s \ + CMD /app/docker-healthcheck.sh diff --git a/internal/common/configuration.go b/internal/common/configuration.go index 6d581e1..7e649f8 100644 --- a/internal/common/configuration.go +++ b/internal/common/configuration.go @@ -61,6 +61,7 @@ type Config struct { MailFrom string `yaml:"mailfrom" envconfig:"MAIL_FROM"` AdminUser string `yaml:"adminUser" envconfig:"ADMIN_USER"` // optional, non LDAP admin user AdminPassword string `yaml:"adminPass" envconfig:"ADMIN_PASS"` + DatabasePath string `yaml:"database" envconfig:"DATABASE_PATH"` } `yaml:"core"` Email MailConfig `yaml:"email"` LDAP ldap.Config `yaml:"ldap"` @@ -79,12 +80,14 @@ func NewConfig() *Config { cfg.Core.MailFrom = "WireGuard VPN " cfg.Core.AdminUser = "" // non-ldap admin access is disabled by default cfg.Core.AdminPassword = "" + cfg.Core.DatabasePath = "data/wg_portal.db" cfg.LDAP.URL = "ldap://srv-ad01.company.local:389" cfg.LDAP.BaseDN = "DC=COMPANY,DC=LOCAL" cfg.LDAP.StartTLS = true cfg.LDAP.BindUser = "company\\\\ldap_wireguard" cfg.LDAP.BindPass = "SuperSecret" cfg.WG.DeviceName = "wg0" + cfg.WG.WireGuardConfig = "/etc/wireguard/wg0.conf" cfg.AdminLdapGroup = "CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL" cfg.Email.Host = "127.0.0.1" cfg.Email.Port = 25 diff --git a/internal/server/core.go b/internal/server/core.go index d3d2151..0038469 100644 --- a/internal/server/core.go +++ b/internal/server/core.go @@ -79,6 +79,11 @@ type Server struct { } func (s *Server) Setup() error { + dir := s.getExecutableDirectory() + rDir, _ := filepath.Abs(filepath.Dir(os.Args[0])) + log.Infof("Real working directory: %s", rDir) + log.Infof("Current working directory: %s", dir) + // Init rand rand.Seed(time.Now().UnixNano()) @@ -102,7 +107,7 @@ func (s *Server) Setup() error { } // Setup user manager - if s.users = NewUserManager(s.wg, s.ldapUsers); s.users == nil { + if s.users = NewUserManager(filepath.Join(dir, s.config.Core.DatabasePath), s.wg, s.ldapUsers); s.users == nil { return errors.New("unable to setup user manager") } if err := s.users.InitFromCurrentInterface(); err != nil { @@ -112,10 +117,7 @@ func (s *Server) Setup() error { return errors.New("unable to restore wirguard state") } - dir := s.getExecutableDirectory() - rDir, _ := filepath.Abs(filepath.Dir(os.Args[0])) - log.Infof("Real working directory: %s", rDir) - log.Infof("Current working directory: %s", dir) + // Setup mail template var err error s.mailTpl, err = template.New("email.html").ParseFiles(filepath.Join(dir, "/assets/tpl/email.html")) if err != nil { diff --git a/internal/server/usermanager.go b/internal/server/usermanager.go index a11cbb9..c4db8b9 100644 --- a/internal/server/usermanager.go +++ b/internal/server/usermanager.go @@ -6,6 +6,8 @@ import ( "errors" "fmt" "net" + "os" + "path/filepath" "reflect" "regexp" "sort" @@ -281,12 +283,19 @@ type UserManager struct { ldapUsers *ldap.SynchronizedUserCacheHolder } -func NewUserManager(wg *wireguard.Manager, ldapUsers *ldap.SynchronizedUserCacheHolder) *UserManager { +func NewUserManager(dbPath string, wg *wireguard.Manager, ldapUsers *ldap.SynchronizedUserCacheHolder) *UserManager { + um := &UserManager{wg: wg, ldapUsers: ldapUsers} var err error - um.db, err = gorm.Open(sqlite.Open("wg_portal.db"), &gorm.Config{}) + if _, err = os.Stat(filepath.Dir(dbPath)); os.IsNotExist(err) { + if err = os.MkdirAll(filepath.Dir(dbPath), 0700); err != nil { + log.Errorf("failed to create database directory (%s): %v", filepath.Dir(dbPath), err) + return nil + } + } + um.db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) if err != nil { - log.Errorf("failed to open sqlite database: %v", err) + log.Errorf("failed to open sqlite database (%s): %v", dbPath, err) return nil }