Skip to content

Commit

Permalink
Add auto generate for user on first run, add auto magic config -> env
Browse files Browse the repository at this point in the history
  • Loading branch information
NHAS committed Nov 7, 2024
1 parent 3169299 commit 77f54b7
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .env.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DOMAIN=localhost:8081
GOHUNT_USERNAME=test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
gohunt
.env
28 changes: 28 additions & 0 deletions Caddyfile.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
on_demand_tls {
ask http://127.0.0.1:8081/check_domain
}

}

{$DOMAIN} {
log {
output stdout
}

reverse_proxy 127.0.0.1:8081
}

*.{$DOMAIN} {

# Log all requests
log {
output stdout
}

tls {
on_demand
}

reverse_proxy 127.0.0.1:8081
}
44 changes: 44 additions & 0 deletions application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"
"net/http"
"net/url"
"os"
"path"
"time"

Expand All @@ -15,6 +16,7 @@ import (
"github.com/NHAS/gohunt/config"
"github.com/gorilla/mux"
"github.com/zitadel/oidc/v3/pkg/client/rp"
"golang.org/x/crypto/bcrypt"

"github.com/NHAS/session"
"github.com/google/uuid"
Expand Down Expand Up @@ -61,6 +63,48 @@ func New(c config.Config) (*Application, error) {

newApplication.db = db

if os.Getenv("GOHUNT_USERNAME") != "" {

var count int64
if err := db.Model(&models.User{}).Count(&count).Error; err != nil {
log.Println("failed to count users", err)
return nil, fmt.Errorf("failed to count users: %s", err)
}

// No users, time to make at least one admin
if count == 0 {
firstUserName := os.Getenv("GOHUNT_USERNAME")

potentialPassword := os.Getenv("GOHUNT_PASSWORD")
if potentialPassword == "" {
potentialPassword = newApplication.generateRandom(16)
log.Println("GOHUNT_PASSWORD: ", potentialPassword)
}

// Create new user
user := models.User{
UserDTO: models.UserDTO{
Username: firstUserName,
Email: firstUserName + "@" + firstUserName,
Domain: firstUserName,
IsAdmin: true,
},
}

b, err := bcrypt.GenerateFromPassword([]byte(potentialPassword), 10)
if err != nil {
return nil, fmt.Errorf("failed to generate password hash: %s", err)
}

user.Password = string(b)

if err := db.Create(&user).Error; err != nil {
return nil, fmt.Errorf("failed to save first user in database: %s", err)
}
}

}

if c.Features.Oidc.Enabled {

hashkey := make([]byte, 32)
Expand Down
83 changes: 79 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package config

import (
"fmt"
"log"
"os"
"reflect"
"strconv"
"strings"

"gopkg.in/yaml.v2"
)
Expand Down Expand Up @@ -60,13 +64,88 @@ type Config struct {
}
}

func listFields(v interface{}) []string {
var fields []string
t := reflect.TypeOf(v).Elem()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fieldName := field.Name

if field.Type.Kind() == reflect.Struct {
subFields := listFields(reflect.New(field.Type).Interface())
for _, subField := range subFields {
fields = append(fields, fmt.Sprintf("%s.%s", fieldName, subField))
}
} else {
fields = append(fields, fieldName)
}
}
return fields
}

func setField(v interface{}, fieldPath string, value string) {
r := reflect.ValueOf(v).Elem()
parts := strings.Split(fieldPath, ".")

for i, part := range parts {
if i == len(parts)-1 {
f := r.FieldByName(part)
if f.IsValid() {
switch f.Kind() {
case reflect.Bool:
f.SetBool(value == "true")
case reflect.Slice:
f.Set(reflect.ValueOf(strings.Split(value, ",")))
case reflect.String:
f.SetString(value)
case reflect.Int:

reflectedVal, err := strconv.Atoi(value)
if err != nil {
log.Println(fieldPath, " should be int, but couldnt be parsed as one: ", err)
continue
}
f.SetInt(int64(reflectedVal))

default:
log.Printf("Unsupported field type for field: %s", fieldPath)
}
} else {
log.Printf("Field not found: %s", fieldPath)
}
} else {
r = r.FieldByName(part).Elem()
}
}
}

func LoadConfig(path string) (c Config, err error) {
c.Notification.Webhooks.SafeDomains = []string{"discord.com", "slack.com"}

// Load configuration
configFile, err := os.Open(path)
if err != nil {

fields := listFields(&c)
setSomething := false
for _, field := range fields {
envVariable := os.Getenv(field)
fmt.Printf("%s=%s\n", field, envVariable)

if envVariable != "" {
setSomething = true
setField(&c, field, envVariable)
}
}

if setSomething {
err = nil
return
}

err = fmt.Errorf("error reading config.yaml, have you created one? Error: %s", err)
return

}
defer configFile.Close()

Expand All @@ -78,9 +157,5 @@ func LoadConfig(path string) (c Config, err error) {
return
}

if len(c.Notification.Webhooks.SafeDomains) == 0 {
c.Notification.Webhooks.SafeDomains = []string{"discord.com", "slack.com"}
}

return
}
33 changes: 33 additions & 0 deletions docker-compose.dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
services:
postgres:
image: postgres:13
environment:
- POSTGRES_USER=gohunt
- POSTGRES_PASSWORD=gohunt
- POSTGRES_DB=gohunt
volumes:
- db-data:/var/lib/postgresql/data

gohunt:
image: ghcr.io/nhas/gohunt:main
environment:
- GOHUNT_USERNAME=${GOHUNT_USERNAME}
- GOHUNT_PASSWORD=${GOHUNT_PASSWORD}
# Settings that can be configured either from the docker env, or config file
- Domain=${DOMAIN}
- ListenAddress=:8081
- Features.Signup.Enabled=true
- Notification.Webhooks.Enabled=true
- Database.Host=postgres
- Database.Port=5432
- Database.User=gohunt
- Database.DBname=gohunt
- Database.SSLmode=disabled
- Database.Password=gohunt
ports:
- 8081:8081

volumes:
db-data:
caddy-data:
caddy_config:
36 changes: 33 additions & 3 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,40 @@ services:
- POSTGRES_USER=gohunt
- POSTGRES_PASSWORD=gohunt
- POSTGRES_DB=gohunt
volumes:
- db-data:/var/lib/postgresql/data

gohunt:
image: ghcr.io/nhas/gohunt:main
environment:
- GOHUNT_USERNAME=${GOHUNT_USERNAME}
- GOHUNT_PASSWORD=${GOHUNT_PASSWORD}
# Settings that can be configured either from the docker env, or config file
- Domain=${DOMAIN}
- ListenAddress=:8081
- NumberProxies=1
- Features.Signup.Enabled=true
- Notification.Webhooks.Enabled=true
- Database.Host=postgres
- Database.Port=5432
- Database.User=gohunt
- Database.DBname=gohunt
- Database.SSLmode=disabled
- Database.Password=gohunt

caddy:
image: caddy:2
ports:
- 127.0.0.1:5432:5432
- 80:80
- 443:443
environment:
- DOMAIN=${DOMAIN}
volumes:
- gohunt-db-data:/var/lib/postgresql/data
- caddy-data:/data
- caddy_config:/config
- ./Caddyfile.example:/etc/caddy/Caddyfile

volumes:
gohunt-db-data:
db-data:
caddy-data:
caddy_config:

0 comments on commit 77f54b7

Please sign in to comment.