Compare commits

..

1 Commits

Author SHA1 Message Date
dff95c7af9 chore: add jenkins pipeline
Some checks failed
arcad/emissary/pipeline/head There was a failure building this commit
2023-03-31 15:46:40 +02:00
24 changed files with 55 additions and 167 deletions

2
.gitignore vendored
View File

@ -4,7 +4,7 @@ dist/
/tools
/tmp
/state.json
/emissary.sqlite*
/emissary.sqlite
/.gitea-release
/agent-key.json
/apps

3
Jenkinsfile vendored
View File

@ -23,7 +23,6 @@ pipeline {
sh '''
git config --global credential.https://forge.cadoles.com.username "$GIT_USERNAME"
git config --global credential.https://forge.cadoles.com.helper '!f() { test "$1" = get && echo "password=$GIT_PASSWORD"; }; f'
export GOPRIVATE=forge.cadoles.com/arcad/edge
make test
'''
@ -54,7 +53,7 @@ pipeline {
build(
job: "../emissary-firmware/${env.GIT_BRANCH}",
parameters: [
[$class: 'StringParameterValue', name: 'emissaryRelease', value: currentVersion]
[$class: 'StringParameterValue', name: 'emissaryVersion', value: currentVersion]
]
)
}

View File

@ -135,7 +135,7 @@ gitea-release: tools/gitea-release/bin/gitea-release.sh goreleaser
GITEA_RELEASE_COMMITISH_TARGET="$(GIT_VERSION)" \
GITEA_RELEASE_IS_DRAFT="false" \
GITEA_RELEASE_BODY="" \
GITEA_RELEASE_ATTACHMENTS="$$(find .gitea-release/* -type f)" \
GITEA_RELEASE_ATTACHMENTS="$(shell find .gitea-release/* -type f)" \
tools/gitea-release/bin/gitea-release.sh
tools/gitea-release/bin/gitea-release.sh:

View File

@ -7,7 +7,6 @@ import (
"forge.cadoles.com/Cadoles/emissary/internal/command/agent"
"forge.cadoles.com/Cadoles/emissary/internal/command/api"
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/format"
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/spec"
)

View File

@ -28,8 +28,6 @@ import (
"github.com/pkg/errors"
)
const defaultSQLiteParams = "?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_txlock=immediate"
func (c *Controller) getHandlerOptions(ctx context.Context, appKey string, specs *spec.Spec) ([]edgeHTTP.HandlerOptionFunc, error) {
dataDir, err := c.ensureAppDataDir(ctx, appKey)
if err != nil {
@ -37,7 +35,7 @@ func (c *Controller) getHandlerOptions(ctx context.Context, appKey string, specs
}
dbFile := filepath.Join(dataDir, appKey+".sqlite")
db, err := sqlite.Open(dbFile + defaultSQLiteParams)
db, err := sqlite.Open(dbFile)
if err != nil {
return nil, errors.Wrapf(err, "could not open database file '%s'", dbFile)
}

View File

@ -35,15 +35,12 @@ type Server struct {
}
func (s *Server) Start(ctx context.Context, addr string) (err error) {
if s.Running() {
if s.server != nil {
if err := s.Stop(); err != nil {
return errors.WithStack(err)
}
}
s.serverMutex.Lock()
defer s.serverMutex.Unlock()
router := chi.NewRouter()
router.Use(middleware.Logger)
@ -88,7 +85,9 @@ func (s *Server) Start(ctx context.Context, addr string) (err error) {
}
}()
s.serverMutex.Lock()
s.server = server
s.serverMutex.Unlock()
return nil
}
@ -101,25 +100,20 @@ func (s *Server) Running() bool {
}
func (s *Server) Stop() error {
if !s.Running() {
return nil
}
s.serverMutex.Lock()
defer s.serverMutex.Unlock()
if s.server == nil {
return nil
}
if err := s.server.Close(); err != nil {
defer func() {
s.serverMutex.Lock()
s.server = nil
s.serverMutex.Unlock()
}()
if err := s.server.Close(); err != nil {
return errors.WithStack(err)
}
s.server = nil
return nil
}

View File

@ -4,7 +4,6 @@ import (
"context"
"net/http"
"strings"
"time"
"forge.cadoles.com/Cadoles/emissary/internal/auth"
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
@ -14,11 +13,8 @@ import (
"gitlab.com/wpetit/goweb/logger"
)
const DefaultAcceptableSkew = 5 * time.Minute
type Authenticator struct {
repo datastore.AgentRepository
acceptableSkew time.Duration
repo datastore.AgentRepository
}
// Authenticate implements auth.Authenticator.
@ -75,19 +71,11 @@ func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth
[]byte(rawToken),
jwt.WithKeySet(agent.KeySet.Set, jws.WithRequireKid(false)),
jwt.WithValidate(true),
jwt.WithAcceptableSkew(a.acceptableSkew),
)
if err != nil {
return nil, errors.WithStack(err)
}
contactedAt := time.Now()
agent, err = a.repo.Update(ctx, agent.ID, datastore.WithAgentUpdateContactedAt(contactedAt))
if err != nil {
return nil, errors.WithStack(err)
}
user := &User{
agent: agent,
}
@ -95,10 +83,9 @@ func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth
return user, nil
}
func NewAuthenticator(repo datastore.AgentRepository, acceptableSkew time.Duration) *Authenticator {
func NewAuthenticator(repo datastore.AgentRepository) *Authenticator {
return &Authenticator{
repo: repo,
acceptableSkew: acceptableSkew,
repo: repo,
}
}

View File

@ -4,7 +4,6 @@ import (
"context"
"net/http"
"strings"
"time"
"forge.cadoles.com/Cadoles/emissary/internal/auth"
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
@ -12,12 +11,9 @@ import (
"gitlab.com/wpetit/goweb/logger"
)
const DefaultAcceptableSkew = 5 * time.Minute
type Authenticator struct {
keys jwk.Set
issuer string
acceptableSkew time.Duration
keys jwk.Set
issuer string
}
// Authenticate implements auth.Authenticator.
@ -34,7 +30,7 @@ func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth
return nil, errors.WithStack(auth.ErrUnauthenticated)
}
token, err := parseToken(ctx, a.keys, a.issuer, rawToken, a.acceptableSkew)
token, err := parseToken(ctx, a.keys, a.issuer, rawToken)
if err != nil {
return nil, errors.WithStack(err)
}
@ -61,11 +57,10 @@ func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth
return user, nil
}
func NewAuthenticator(keys jwk.Set, issuer string, acceptableSkew time.Duration) *Authenticator {
func NewAuthenticator(keys jwk.Set, issuer string) *Authenticator {
return &Authenticator{
keys: keys,
issuer: issuer,
acceptableSkew: acceptableSkew,
keys: keys,
issuer: issuer,
}
}

View File

@ -13,13 +13,12 @@ import (
const keyRole = "role"
func parseToken(ctx context.Context, keys jwk.Set, issuer string, rawToken string, acceptableSkew time.Duration) (jwt.Token, error) {
func parseToken(ctx context.Context, keys jwk.Set, issuer string, rawToken string) (jwt.Token, error) {
token, err := jwt.Parse(
[]byte(rawToken),
jwt.WithKeySet(keys, jws.WithRequireKid(false)),
jwt.WithIssuer(issuer),
jwt.WithValidate(true),
jwt.WithAcceptableSkew(acceptableSkew),
)
if err != nil {
return nil, errors.WithStack(err)

View File

@ -10,7 +10,6 @@ import (
type UpdateAgentOptions struct {
Status *int
Label *string
Options []OptionFunc
}
@ -22,12 +21,6 @@ func WithAgentStatus(status int) UpdateAgentOptionFunc {
}
}
func WithAgentLabel(label string) UpdateAgentOptionFunc {
return func(opts *UpdateAgentOptions) {
opts.Label = &label
}
}
func WithUpdateAgentsOptions(funcs ...OptionFunc) UpdateAgentOptionFunc {
return func(opts *UpdateAgentOptions) {
opts.Options = funcs
@ -46,10 +39,6 @@ func (c *Client) UpdateAgent(ctx context.Context, agentID datastore.AgentID, fun
payload["status"] = *opts.Status
}
if opts.Label != nil {
payload["label"] = *opts.Label
}
response := withResponse[struct {
Agent *datastore.Agent `json:"agent"`
}]()

View File

@ -49,6 +49,10 @@ func RunCommand() *cli.Command {
controllers = append(controllers, spec.NewController())
}
if ctrlConf.Proxy.Enabled {
controllers = append(controllers, proxy.NewController())
}
if ctrlConf.UCI.Enabled {
controllers = append(controllers, openwrt.NewUCIController(
string(ctrlConf.UCI.BinPath),
@ -62,10 +66,6 @@ func RunCommand() *cli.Command {
))
}
if ctrlConf.Proxy.Enabled {
controllers = append(controllers, proxy.NewController())
}
if ctrlConf.SysUpgrade.Enabled {
sysUpgradeArgs := make([]string, 0)
if len(ctrlConf.SysUpgrade.SysUpgradeCommand) > 1 {

View File

@ -22,11 +22,6 @@ func UpdateCommand() *cli.Command {
Usage: "Set `STATUS` to selected agent",
Value: -1,
},
&cli.StringFlag{
Name: "label",
Usage: "Set `LABEL` to selected agent",
Value: "",
},
),
Action: func(ctx *cli.Context) error {
baseFlags := clientFlag.GetBaseFlags(ctx)
@ -48,11 +43,6 @@ func UpdateCommand() *cli.Command {
options = append(options, client.WithAgentStatus(status))
}
label := ctx.String("label")
if label != "" {
options = append(options, client.WithAgentLabel(label))
}
client := client.New(baseFlags.ServerURL, client.WithToken(token))
agent, err := client.UpdateAgent(ctx.Context, agentID, options...)

View File

@ -7,10 +7,9 @@ func agentHints(outputMode format.OutputMode) format.Hints {
OutputMode: outputMode,
Props: []format.Prop{
format.NewProp("ID", "ID"),
format.NewProp("Label", "Label"),
format.NewProp("Thumbprint", "Thumbprint"),
format.NewProp("Status", "Status"),
format.NewProp("ContactedAt", "ContactedAt"),
format.NewProp("CreatedAt", "CreatedAt"),
format.NewProp("UpdatedAt", "UpdatedAt"),
},
}

View File

@ -15,6 +15,6 @@ type DatabaseConfig struct {
func NewDefaultDatabaseConfig() DatabaseConfig {
return DatabaseConfig{
Driver: "sqlite",
DSN: "sqlite://emissary.sqlite?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_txlock=immediate",
DSN: "sqlite://emissary.sqlite?_fk=true&_journal=WAL",
}
}

View File

@ -20,15 +20,13 @@ const (
)
type Agent struct {
ID AgentID `json:"id"`
Label string `json:"label"`
Thumbprint string `json:"thumbprint"`
KeySet *SerializableKeySet `json:"keyset,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
Status AgentStatus `json:"status"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ContactedAt *time.Time `json:"contactedAt,omitempty"`
ID AgentID `json:"id"`
Thumbprint string `json:"thumbprint"`
KeySet *SerializableKeySet `json:"keyset,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
Status AgentStatus `json:"status"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type SerializableKeySet struct {

View File

@ -2,7 +2,6 @@ package datastore
import (
"context"
"time"
"github.com/lestrrat-go/jwx/v2/jwk"
)
@ -69,12 +68,10 @@ func WithAgentQueryThumbprints(thumbprints ...string) AgentQueryOptionFunc {
type AgentUpdateOptionFunc func(*AgentUpdateOptions)
type AgentUpdateOptions struct {
Label *string
Status *AgentStatus
ContactedAt *time.Time
Metadata *map[string]any
KeySet *jwk.Set
Thumbprint *string
Status *AgentStatus
Metadata *map[string]any
KeySet *jwk.Set
Thumbprint *string
}
func WithAgentUpdateStatus(status AgentStatus) AgentUpdateOptionFunc {
@ -100,15 +97,3 @@ func WithAgentUpdateThumbprint(thumbprint string) AgentUpdateOptionFunc {
opts.Thumbprint = &thumbprint
}
}
func WithAgentUpdateLabel(label string) AgentUpdateOptionFunc {
return func(opts *AgentUpdateOptions) {
opts.Label = &label
}
}
func WithAgentUpdateContactedAt(contactedAt time.Time) AgentUpdateOptionFunc {
return func(opts *AgentUpdateOptions) {
opts.ContactedAt = &contactedAt
}
}

View File

@ -127,7 +127,7 @@ func (r *AgentRepository) Query(ctx context.Context, opts ...datastore.AgentQuer
count := 0
err := r.withTx(ctx, func(tx *sql.Tx) error {
query := `SELECT id, label, thumbprint, status, contacted_at, created_at, updated_at FROM agents`
query := `SELECT id, thumbprint, status, created_at, updated_at FROM agents`
limit := 10
if options.Limit != nil {
@ -194,16 +194,12 @@ func (r *AgentRepository) Query(ctx context.Context, opts ...datastore.AgentQuer
agent := &datastore.Agent{}
metadata := JSONMap{}
contactedAt := sql.NullTime{}
if err := rows.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &agent.Status, &contactedAt, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
if err := rows.Scan(&agent.ID, &agent.Thumbprint, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
return errors.WithStack(err)
}
agent.Metadata = metadata
if contactedAt.Valid {
agent.ContactedAt = &contactedAt.Time
}
agents = append(agents, agent)
}
@ -319,7 +315,7 @@ func (r *AgentRepository) Get(ctx context.Context, id datastore.AgentID) (*datas
err := r.withTx(ctx, func(tx *sql.Tx) error {
query := `
SELECT "id", "label", "thumbprint", "keyset", "metadata", "status", "contacted_at", "created_at", "updated_at"
SELECT "id", "thumbprint", "keyset", "metadata", "status", "created_at", "updated_at"
FROM agents
WHERE id = $1
`
@ -327,10 +323,9 @@ func (r *AgentRepository) Get(ctx context.Context, id datastore.AgentID) (*datas
row := r.db.QueryRowContext(ctx, query, id)
metadata := JSONMap{}
contactedAt := sql.NullTime{}
var rawKeySet []byte
if err := row.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &contactedAt, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
if err := row.Scan(&agent.ID, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return datastore.ErrNotFound
}
@ -339,9 +334,6 @@ func (r *AgentRepository) Get(ctx context.Context, id datastore.AgentID) (*datas
}
agent.Metadata = metadata
if contactedAt.Valid {
agent.ContactedAt = &contactedAt.Time
}
keySet := jwk.NewSet()
if err := json.Unmarshal(rawKeySet, &keySet); err != nil {
@ -370,11 +362,15 @@ func (r *AgentRepository) Update(ctx context.Context, id datastore.AgentID, opts
err := r.withTx(ctx, func(tx *sql.Tx) error {
query := `
UPDATE agents SET id = $1
UPDATE agents SET updated_at = $2
`
args := []any{id}
index := 2
now := time.Now().UTC()
args := []any{
id, now,
}
index := 3
if options.Status != nil {
query += fmt.Sprintf(`, status = $%d`, index)
@ -399,51 +395,23 @@ func (r *AgentRepository) Update(ctx context.Context, id datastore.AgentID, opts
index++
}
if options.Label != nil {
query += fmt.Sprintf(`, label = $%d`, index)
args = append(args, *options.Label)
index++
}
if options.ContactedAt != nil {
query += fmt.Sprintf(`, contacted_at = $%d`, index)
utc := options.ContactedAt.UTC()
args = append(args, utc)
index++
}
if options.Metadata != nil {
query += fmt.Sprintf(`, metadata = $%d`, index)
args = append(args, JSONMap(*options.Metadata))
index++
}
updated := options.Metadata != nil ||
options.Status != nil ||
options.Label != nil ||
options.KeySet != nil ||
options.Thumbprint != nil
if updated {
now := time.Now().UTC()
query += fmt.Sprintf(`, updated_at = $%d`, index)
args = append(args, now)
index++
}
query += `
WHERE id = $1
RETURNING "id", "label", "thumbprint", "keyset", "metadata", "status", "contacted_at", "created_at", "updated_at"
RETURNING "id", "thumbprint", "keyset", "metadata", "status", "created_at", "updated_at"
`
logger.Debug(ctx, "executing query", logger.F("query", query), logger.F("args", args))
row := tx.QueryRowContext(ctx, query, args...)
metadata := JSONMap{}
contactedAt := sql.NullTime{}
var rawKeySet []byte
if err := row.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &contactedAt, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
if err := row.Scan(&agent.ID, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return datastore.ErrNotFound
}
@ -452,9 +420,6 @@ func (r *AgentRepository) Update(ctx context.Context, id datastore.AgentID, opts
}
agent.Metadata = metadata
if contactedAt.Valid {
agent.ContactedAt = &contactedAt.Time
}
keySet := jwk.NewSet()
if err := json.Unmarshal(rawKeySet, &keySet); err != nil {

View File

@ -145,7 +145,6 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) {
type updateAgentRequest struct {
Status *datastore.AgentStatus `json:"status" validate:"omitempty,oneof=0 1 2 3"`
Label *string `json:"label" validate:"omitempty"`
}
func (s *Server) updateAgent(w http.ResponseWriter, r *http.Request) {
@ -167,10 +166,6 @@ func (s *Server) updateAgent(w http.ResponseWriter, r *http.Request) {
options = append(options, datastore.WithAgentUpdateStatus(*updateAgentReq.Status))
}
if updateAgentReq.Label != nil {
options = append(options, datastore.WithAgentUpdateLabel(*updateAgentReq.Label))
}
agent, err := s.agentRepo.Update(
ctx,
datastore.AgentID(agentID),

View File

@ -105,8 +105,8 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
r.Group(func(r chi.Router) {
r.Use(auth.Middleware(
thirdparty.NewAuthenticator(keys, string(s.conf.Issuer), thirdparty.DefaultAcceptableSkew),
agent.NewAuthenticator(s.agentRepo, agent.DefaultAcceptableSkew),
thirdparty.NewAuthenticator(keys, string(s.conf.Issuer)),
agent.NewAuthenticator(s.agentRepo),
))
r.Route("/agents", func(r chi.Router) {

View File

@ -1 +0,0 @@
ALTER TABLE agents DROP COLUMN label;

View File

@ -1 +0,0 @@
ALTER TABLE agents ADD COLUMN label TEXT DEFAULT "";

View File

@ -1 +0,0 @@
ALTER TABLE agents DROP COLUMN contacted_at;

View File

@ -1 +0,0 @@
ALTER TABLE agents ADD COLUMN contacted_at datetime;

View File

@ -9,7 +9,7 @@ server:
port: 3000
database:
driver: sqlite
dsn: sqlite:///var/lib/emissary/data.sqlite?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_txlock=immediate
dsn: sqlite:///var/lib/emissary/data.sqlite?_fk=true&_journal=WAL
cors:
allowedOrigins: []
allowCredentials: true