add postgres db

This commit is contained in:
Mans Ziesel 2024-12-14 14:44:37 +01:00
parent 5a6ccbc588
commit 3835b8ba86
13 changed files with 226 additions and 53 deletions

View File

@ -1,12 +1,15 @@
GO_FILES := $(shell find . -name '*.go' -not -path './build/*') GO_FILES := $(shell find . -name '*.go' -not -path './build/*' -not -path './tmp/**')
GOOSE_DBSTRING := "user=zadmin_user password=s3cret! dbname=zadmin_dev sslmode=disable host=localhost"
GOOSE_MIGRATION_PATH := "./db/migrations/"
GOOSE_SEED_PATH := "./db/seed/"
build-server: $(GO_FILES) build-server: $(GO_FILES)
mkdir -p build mkdir -p build
go build -o build/zadmin-server ./cmd/server/main.go CGO_ENABLED=0 go build -o build/zadmin-server ./cmd/server/main.go
build-client: $(GO_FILES) build-client: $(GO_FILES)
mkdir -p build mkdir -p build
go build -o build/zadmin-client ./cmd/client/main.go CGO_ENABLED=0 go build -o build/zadmin-client ./cmd/client/main.go
docker-up: docker-up:
mkdir -p ./tmp mkdir -p ./tmp
@ -16,6 +19,7 @@ docker-down:
docker compose -f ./deployments/compose-dev.yaml down docker compose -f ./deployments/compose-dev.yaml down
docker-clean: docker-down docker-clean: docker-down
docker volume rm -f deployments_pg_data
rm -rf ./tmp rm -rf ./tmp
server: build-server server: build-server
@ -27,4 +31,25 @@ client: build-client
clean: clean:
rm -r build rm -r build
.PHONY: clean run-server run-client docker-up install-goose:
go install github.com/pressly/goose/v3/cmd/goose@latest
db-status:
@GOOSE_DRIVER=postgres GOOSE_DBSTRING=$(GOOSE_DBSTRING) goose -dir=$(GOOSE_MIGRATION_PATH) status
db-up:
@GOOSE_DRIVER=postgres GOOSE_DBSTRING=$(GOOSE_DBSTRING) goose -dir=$(GOOSE_MIGRATION_PATH) up
db-reset:
@GOOSE_DRIVER=postgres GOOSE_DBSTRING=$(GOOSE_DBSTRING) goose -dir=$(GOOSE_MIGRATION_PATH) reset
goose-custom:
@GOOSE_DRIVER=postgres GOOSE_DBSTRING=$(GOOSE_DBSTRING) goose -dir=$(GOOSE_MIGRATION_PATH) $(cmd)
goose-seed-custom:
@GOOSE_DRIVER=postgres GOOSE_DBSTRING=$(GOOSE_DBSTRING) goose -dir=$(GOOSE_SEED_PATH) $(cmd)
db-seed:
echo "TODO"
.PHONY: clean run-server run-client docker-up docker-down docker-clean server client install-goose

View File

@ -69,3 +69,7 @@ Ideas???
- The admin runs a installer with this credential - The admin runs a installer with this credential
- The installer enrolls the device in zadmin and generates a unique package for this machine - The installer enrolls the device in zadmin and generates a unique package for this machine
- The installer places this package on the machine - The installer places this package on the machine
## Docs
[goose](https://github.com/pressly/goose) is used for migrations

View File

@ -3,6 +3,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"log" "log"
"runtime"
"git.mziesel.nl/mans/zadmin/internal/models" "git.mziesel.nl/mans/zadmin/internal/models"
"github.com/nats-io/nats.go" "github.com/nats-io/nats.go"
@ -37,7 +38,9 @@ func main() {
} }
}) })
shouldExit := false // shouldExit := false
for !shouldExit { // for !shouldExit {
} // }
// keep running indefenitly
runtime.Goexit()
} }

22
database/machine.go Normal file
View File

@ -0,0 +1,22 @@
package database
import (
"database/sql"
"git.mziesel.nl/mans/zadmin/internal/models"
"github.com/oklog/ulid/v2"
)
func CreateMachine(db *sql.DB, m *models.Machine) error {
query := "INSERT INTO machine (machine_name, os_type, os_arch) VALUES ($1, $2, $3)"
_, err := db.Exec(query, m.MachineName, m.OsType, m.GoArch)
if err != nil {
return err
}
return nil
}
func GetMachine(db *sql.DB) *models.Machine {
ulid.Now()
return &models.Machine{}
}

View File

@ -0,0 +1,13 @@
-- +goose Up
-- +goose StatementBegin
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE OR REPLACE FUNCTION generate_ulid() RETURNS uuid
AS $$
SELECT (lpad(to_hex(floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint), 12, '0') || encode(gen_random_bytes(10), 'hex'))::uuid;
$$ LANGUAGE SQL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP FUNCTION generate_ulid();
-- +goose StatementEnd

View File

@ -0,0 +1,26 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE machine (
id UUID NOT NULL DEFAULT generate_ulid(),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
machine_name VARCHAR(255) NOT NULL,
description text,
os_type VARCHAR(255) NOT NULL,
os_arch VARCHAR(255) NOT NULL,
hostname VARCHAR(255),
public_ipv4_address INET,
public_ipv6_address INET,
agent_version VARCHAR(255),
host_uptime VARCHAR(255),
logged_on_users VARCHAR(255),
os_version VARCHAR(255),
PRIMARY KEY(id)
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE machine;
-- +goose StatementEnd

View File

@ -0,0 +1,17 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE agent (
id UUID NOT NULL DEFAULT generate_ulid(),
machine_id UUID NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(id),
FOREIGN KEY (machine_id) REFERENCES machine(id)
);
CREATE INDEX idx_agent_machine_id ON agent(machine_id);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE agent;
-- +goose StatementEnd

42
db/seed/seed.go Normal file
View File

@ -0,0 +1,42 @@
package main
import (
"database/sql"
"fmt"
"log"
"git.mziesel.nl/mans/zadmin/database"
"git.mziesel.nl/mans/zadmin/internal/models"
_ "github.com/lib/pq"
"github.com/oklog/ulid/v2"
)
func main() {
db, err := sql.Open("postgres", "user=zadmin_user password=s3cret! dbname=zadmin_dev sslmode=disable host=localhost")
if err != nil {
log.Fatalf("goose: failed to open DB: %v\n", err)
}
defer func() {
if err := db.Close(); err != nil {
log.Fatalf("failed to close DB: %v\n", err)
}
}()
createTestMachines(db)
}
func createTestMachines(db *sql.DB) {
machine := models.Machine{
ID: ulid.Make(),
MachineName: "test-machine",
OsType: "linux",
GoArch: "amd64",
}
fmt.Println(models.PrettyFormatData(machine))
err := database.CreateMachine(db, &machine)
if err != nil {
log.Fatal(err)
}
}

View File

@ -1,6 +1,33 @@
services: services:
nats-server: nats-server:
image: nats:latest image: nats:latest
restart: unless-stopped
ports: ports:
- 4222:4222 - 4222:4222
command: ["-DV"] command: ["-DV"]
postgres:
image: postgres:16-alpine
restart: unless-stopped
ports:
- 5432:5432
volumes:
- pg_data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=s3cret!
- POSTGRES_USER=zadmin_user
- POSTGRES_DB=zadmin_dev
pgadmin:
image: dpage/pgadmin4
restart: unless-stopped
ports:
- "8888:80"
environment:
PGADMIN_DEFAULT_EMAIL: mans@mziesel.com
PGADMIN_DEFAULT_PASSWORD: secret
PGADMIN_LISTEN_PORT: 80
volumes:
- pgadmin_data:/var/lib/pgadmin
volumes:
pg_data: {}
pgadmin_data: {}

9
go.mod
View File

@ -3,14 +3,17 @@ module git.mziesel.nl/mans/zadmin
go 1.23.0 go 1.23.0
require ( require (
github.com/google/uuid v1.6.0
github.com/lib/pq v1.10.9
github.com/mackerelio/go-osstat v0.2.5 github.com/mackerelio/go-osstat v0.2.5
github.com/nats-io/nats.go v1.37.0 github.com/nats-io/nats.go v1.37.0
golang.org/x/sys v0.20.0 github.com/oklog/ulid/v2 v2.1.0
) )
require ( require (
github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/compress v1.17.7 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect github.com/nats-io/nuid v1.0.1 // indirect
golang.org/x/crypto v0.18.0 // indirect golang.org/x/crypto v0.31.0 // indirect
golang.org/x/sys v0.28.0 // indirect
) )

19
go.sum
View File

@ -1,5 +1,9 @@
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o= github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o=
github.com/mackerelio/go-osstat v0.2.5/go.mod h1:atxwWF+POUZcdtR1wnsUcQxTytoHG4uhl2AKKzrOajY= github.com/mackerelio/go-osstat v0.2.5/go.mod h1:atxwWF+POUZcdtR1wnsUcQxTytoHG4uhl2AKKzrOajY=
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE= github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
@ -8,7 +12,10 @@ github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

View File

@ -80,7 +80,7 @@ func GetMachineData(ac models.MachineAgentConfig) (models.MachineData, error) {
data.PublicIPv6Address = ipResponseBody.Address data.PublicIPv6Address = ipResponseBody.Address
// get machine uptime // get machine uptime
uptime, err := uptime.GetUptime() uptime, err := uptime.Get()
if err != nil { if err != nil {
LogError("failed to get machine uptime", err) LogError("failed to get machine uptime", err)
return data, nil return data, nil

View File

@ -1,47 +1,42 @@
package models package models
import ( import (
"fmt"
"strings"
"time" "time"
"github.com/oklog/ulid/v2"
) )
// Model for the user accounts // Model for the user accounts
type AccountModel struct { type Account struct {
ID string `json:"id"` ID ulid.ULID `json:"id"`
FirstName string `json:"first_name"` FirstName string `json:"first_name"`
LastName string `json:"last_name"` LastName string `json:"last_name"`
PasswordHash string `json:"password_hash"` PasswordHash string `json:"password_hash"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
} }
type OrganizationModel struct { type Organization struct {
ID string `json:"id"` ID ulid.ULID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
} }
// Model for the hosts managed by zadmin // Model for the hosts managed by zadmin
type MachineModel struct { type Machine struct {
ID string `json:"id"` // machine ID ID ulid.ULID `json:"id"` // machine ID
Organization OrganizationModel `json:"organization"` // Organization this machine belongs to MachineName string `json:"machine_name"` // Name of machine
CreatedAt time.Time `json:"created_at"` // Time created in zadmin Organization Organization `json:"organization"` // Organization this machine belongs to
Description string `json:"description"` // Description of machine CreatedAt time.Time `json:"created_at"` // Time created in zadmin
AgentConfig MachineAgentConfig `json:"agent_config"` // Config used by this machine Description string `json:"description"` // Description of machine
OsType string `json:"os_type"` // OS Type TODO: make constants OsType string `json:"os_type"` // OS Type TODO: make constants
GoArch string `json:"go_arch"` // Go arch GoArch string `json:"go_arch"` // Go arch
}
// Data of the machine
type MachineData struct {
FirstSeen time.Time `json:"first_seen"` // Time of first contact with zadmin FirstSeen time.Time `json:"first_seen"` // Time of first contact with zadmin
LastSeen time.Time `json:"last_seen"` // Time of last contact with zadmin LastSeen time.Time `json:"last_seen"` // Time of last contact with zadmin
Hostname string `json:"hostname"` // Configured system hostname Hostname string `json:"hostname"` // Configured system hostname
PublicIPv4Address string `json:"public_ipv4_address"` // Public IPv4 address PublicIPv4Address string `json:"public_ipv4_address"` // Public IPv4 address
PublicIPv6Address string `json:"public_ipv6_address"` // Public IPv6 address PublicIPv6Address string `json:"public_ipv6_address"` // Public IPv6 address
NetworkInterfaces []MachineInterfaceDetails `json:"machine_interfaces"` // Interface details NetworkInterfaces []MachineInterfaceDetails `json:"machine_interfaces"` // Interface details
AgentVersion string `json:"agent_version"` // installed zadmin agent version
AntivirusInfo AntivirusInfo `json:"antivirus_info"` // Antivirus info AntivirusInfo AntivirusInfo `json:"antivirus_info"` // Antivirus info
UsageStatistics UsageStatistics `json:"usage_statistics"` // Usage statistics of machine UsageStatistics UsageStatistics `json:"usage_statistics"` // Usage statistics of machine
SoftwareCatalog []Software `json:"software_catalog"` // Software installed on machine SoftwareCatalog []Software `json:"software_catalog"` // Software installed on machine
@ -50,23 +45,6 @@ type MachineData struct {
OSVersion string `json:"os_version"` // OS version OSVersion string `json:"os_version"` // OS version
} }
// String method for MachineData
func (m MachineData) String() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("MachineData:\n"))
sb.WriteString(fmt.Sprintf(" FirstSeen: %s\n", m.FirstSeen))
sb.WriteString(fmt.Sprintf(" LastSeen: %s\n", m.LastSeen))
sb.WriteString(fmt.Sprintf(" Hostname: %s\n", m.Hostname))
sb.WriteString(fmt.Sprintf(" PublicIPv4Address: %s\n", m.PublicIPv4Address))
sb.WriteString(fmt.Sprintf(" PublicIPv6Address: %s\n", m.PublicIPv6Address))
sb.WriteString(fmt.Sprintf(" AgentVersion: %s\n", m.AgentVersion))
sb.WriteString(fmt.Sprintf(" UptimeSeconds: %d\n", m.UptimeSeconds))
sb.WriteString(fmt.Sprintf(" OSVersion: %s\n", m.OSVersion))
sb.WriteString(fmt.Sprintf(" LoggedOnUsers: %v\n", m.LoggedOnUsers))
return sb.String()
}
// Software installed on a Machine // Software installed on a Machine
type Software struct { type Software struct {
Name string Name string
@ -110,6 +88,12 @@ type MachineInterfaceDetails struct {
Addressess []string `json:"addressess"` // Interface IP addressess Addressess []string `json:"addressess"` // Interface IP addressess
} }
type Agent struct {
ID ulid.ULID `json:"id"`
Machine Machine `json:"machine"`
CreatedAt time.Time `json:"created_at"` // Time created in zadmin
}
// Configuration to be used by the agent on a machine // Configuration to be used by the agent on a machine
type MachineAgentConfig struct { type MachineAgentConfig struct {
RESTServerHostname string `json:"rest_server_hostname"` // Hostname used for REST requests RESTServerHostname string `json:"rest_server_hostname"` // Hostname used for REST requests