Compare commits
20 Commits
v2023.6.23
...
feature/te
Author | SHA1 | Date | |
---|---|---|---|
c55c723868 | |||
3d7a094cb8 | |||
077964c7b9 | |||
3af6324121 | |||
b31900ae2f | |||
777648ff44 | |||
d9919c888f | |||
1eb3de4f16 | |||
9326bac792 | |||
3c1f5042c8 | |||
14eecbf01e | |||
c51ac0adc7 | |||
3e168dadf6 | |||
61ac5e8ae0 | |||
929394c479 | |||
a1ec5b87c8 | |||
5c36955c13 | |||
6cf01adb61 | |||
8e88b5a7f1 | |||
42d49eb090 |
44
.chglog/CHANGELOG.tpl.md
Normal file
44
.chglog/CHANGELOG.tpl.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{{ if .Versions -}}
|
||||||
|
{{ if .Unreleased.CommitGroups -}}
|
||||||
|
<a name="unreleased"></a>
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
{{ range .Unreleased.CommitGroups -}}
|
||||||
|
### {{ .Title }}
|
||||||
|
{{ range .Commits -}}
|
||||||
|
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{ range .Versions }}
|
||||||
|
<a name="{{ .Tag.Name }}"></a>
|
||||||
|
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
|
||||||
|
{{ range .CommitGroups -}}
|
||||||
|
### {{ .Title }}
|
||||||
|
{{ range .Commits -}}
|
||||||
|
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
_Nothing functionally significant._
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{- if .NoteGroups -}}
|
||||||
|
{{ range .NoteGroups -}}
|
||||||
|
### {{ .Title }}
|
||||||
|
{{ range .Notes }}
|
||||||
|
{{ .Body }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{- if .Versions }}
|
||||||
|
[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
|
||||||
|
{{ range .Versions -}}
|
||||||
|
{{ if .Tag.Previous -}}
|
||||||
|
[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
33
.chglog/config.yml
Normal file
33
.chglog/config.yml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
style: github
|
||||||
|
template: CHANGELOG.tpl.md
|
||||||
|
info:
|
||||||
|
title: CHANGELOG
|
||||||
|
repository_url: https://forge.cadoles.com//
|
||||||
|
options:
|
||||||
|
commits:
|
||||||
|
filters:
|
||||||
|
Type:
|
||||||
|
- feat
|
||||||
|
- fix
|
||||||
|
- perf
|
||||||
|
- refactor
|
||||||
|
- docs
|
||||||
|
commit_groups:
|
||||||
|
title_maps:
|
||||||
|
feat: Features
|
||||||
|
fix: Bug Fixes
|
||||||
|
perf: Performance Improvements
|
||||||
|
refactor: Code Refactoring
|
||||||
|
docs: Documentation
|
||||||
|
header:
|
||||||
|
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
|
||||||
|
pattern_maps:
|
||||||
|
- Type
|
||||||
|
- Scope
|
||||||
|
- Subject
|
||||||
|
notes:
|
||||||
|
keywords:
|
||||||
|
- BREAKING CHANGE
|
||||||
|
issues:
|
||||||
|
prefix:
|
||||||
|
- '#'
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,3 +11,5 @@ dist/
|
|||||||
/server-key.json
|
/server-key.json
|
||||||
/.emissary-token
|
/.emissary-token
|
||||||
/out
|
/out
|
||||||
|
.mktools/
|
||||||
|
/CHANGELOG.md
|
||||||
|
14
Jenkinsfile
vendored
14
Jenkinsfile
vendored
@ -58,12 +58,15 @@ pipeline {
|
|||||||
passwordVariable: 'GITEA_RELEASE_PASSWORD'
|
passwordVariable: 'GITEA_RELEASE_PASSWORD'
|
||||||
])
|
])
|
||||||
]) {
|
]) {
|
||||||
sh 'make gitea-release'
|
sh """
|
||||||
|
export MKT_PROJECT_VERSION_BRANCH_NAME=${env.BRANCH_NAME}
|
||||||
|
make mktools
|
||||||
|
make gitea-release
|
||||||
|
"""
|
||||||
}
|
}
|
||||||
def currentVersion = sh(returnStdout: true, script: 'make full-version').trim()
|
|
||||||
if (currentVersion.endsWith('-dirty')) {
|
String currentVersion = sh(script: "MKT_PROJECT_VERSION_BRANCH_NAME=${env.BRANCH_NAME} make version", returnStdout: true).trim()
|
||||||
unstable('Could not trigger emissary-firmware build, dirty version !')
|
|
||||||
} else {
|
|
||||||
build(
|
build(
|
||||||
job: "../emissary-firmware/${env.GIT_BRANCH}",
|
job: "../emissary-firmware/${env.GIT_BRANCH}",
|
||||||
parameters: [
|
parameters: [
|
||||||
@ -75,7 +78,6 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
post {
|
post {
|
||||||
always {
|
always {
|
||||||
|
64
Makefile
64
Makefile
@ -5,12 +5,9 @@ GITCHLOG_ARGS ?=
|
|||||||
SHELL := /bin/bash
|
SHELL := /bin/bash
|
||||||
|
|
||||||
EMISSARY_VERSION ?=
|
EMISSARY_VERSION ?=
|
||||||
GIT_VERSION := $(shell git describe --always)
|
|
||||||
DATE_VERSION := $(shell date +%Y.%-m.%-d)
|
|
||||||
FULL_VERSION := v$(DATE_VERSION)-$(GIT_VERSION)$(if $(shell git diff --stat),-dirty,)
|
|
||||||
|
|
||||||
DOCKER_IMAGE_NAME ?= bornholm/emissary
|
DOCKER_IMAGE_NAME ?= bornholm/emissary
|
||||||
DOCKER_IMAGE_TAG ?= $(FULL_VERSION)
|
DOCKER_IMAGE_TAG ?= $(MKT_PROJECT_VERSION)
|
||||||
|
|
||||||
GOTEST_ARGS ?= -short
|
GOTEST_ARGS ?= -short
|
||||||
|
|
||||||
@ -45,7 +42,7 @@ build-emissary-%: deps ## Build executable
|
|||||||
-v \
|
-v \
|
||||||
-ldflags "\
|
-ldflags "\
|
||||||
-X 'main.GitRef=$(shell git rev-parse --short HEAD)' \
|
-X 'main.GitRef=$(shell git rev-parse --short HEAD)' \
|
||||||
-X 'main.ProjectVersion=$(FULL_VERSION)' \
|
-X 'main.ProjectVersion=$(MKT_PROJECT_VERSION)' \
|
||||||
-X 'main.BuildDate=$(shell date --utc --rfc-3339=seconds)' \
|
-X 'main.BuildDate=$(shell date --utc --rfc-3339=seconds)' \
|
||||||
" \
|
" \
|
||||||
-o ./bin/$* \
|
-o ./bin/$* \
|
||||||
@ -66,7 +63,7 @@ run-emissary-%: .env
|
|||||||
( set -o allexport && source .env && set +o allexport && bin/$* $(EMISSARY_CMD))
|
( set -o allexport && source .env && set +o allexport && bin/$* $(EMISSARY_CMD))
|
||||||
|
|
||||||
.PHONY: deps
|
.PHONY: deps
|
||||||
deps: .env
|
deps: .env .mktools
|
||||||
|
|
||||||
.PHONY: dump-config
|
.PHONY: dump-config
|
||||||
dump-config: build-emissary
|
dump-config: build-emissary
|
||||||
@ -74,27 +71,8 @@ dump-config: build-emissary
|
|||||||
./bin/emissary config dump > tmp/config.yml
|
./bin/emissary config dump > tmp/config.yml
|
||||||
|
|
||||||
.PHONY: goreleaser
|
.PHONY: goreleaser
|
||||||
goreleaser: deps
|
goreleaser: .mktools
|
||||||
( set -o allexport && source .env && set +o allexport && VERSION=$(GORELEASER_VERSION) curl -sfL https://goreleaser.com/static/run | GORELEASER_CURRENT_TAG="$(FULL_VERSION)" bash /dev/stdin $(GORELEASER_ARGS) )
|
( set -o allexport && source .env && set +o allexport && VERSION=$(GORELEASER_VERSION) curl -sfL https://goreleaser.com/static/run | GORELEASER_CURRENT_TAG="$(MKT_PROJECT_VERSION)" bash /dev/stdin $(GORELEASER_ARGS) )
|
||||||
|
|
||||||
.PHONY: start-release
|
|
||||||
start-release:
|
|
||||||
if [ -z "$(EMISSARY_VERSION)" ]; then echo "You must define environment variable FAQD_VERSION"; exit 1; fi
|
|
||||||
|
|
||||||
git flow release start $(EMISSARY_VERSION)
|
|
||||||
|
|
||||||
# Update package.json version
|
|
||||||
jq '.version = "$(EMISSARY_VERSION)"' package.json | sponge package.json
|
|
||||||
git add package.json
|
|
||||||
git commit -m "chore: bump to version $(EMISSARY_VERSION)" --allow-empty
|
|
||||||
|
|
||||||
echo "Commit you additional modifications then execute 'make finish-release'"
|
|
||||||
|
|
||||||
.PHONY: finish-release
|
|
||||||
finish-release:
|
|
||||||
git flow release finish -m "v$(EMISSARY_VERSION)"
|
|
||||||
git push --all
|
|
||||||
git push --tags
|
|
||||||
|
|
||||||
install-git-hooks:
|
install-git-hooks:
|
||||||
git config core.hooksPath .githooks
|
git config core.hooksPath .githooks
|
||||||
@ -119,32 +97,28 @@ deploy-openwrt-agent:
|
|||||||
scp dist/emissary-agent_linux_arm_6/emissary root@$(OPENWRT_DEVICE):/usr/bin/emissary
|
scp dist/emissary-agent_linux_arm_6/emissary root@$(OPENWRT_DEVICE):/usr/bin/emissary
|
||||||
ssh root@$(OPENWRT_DEVICE) /etc/init.d/emissary-agent start
|
ssh root@$(OPENWRT_DEVICE) /etc/init.d/emissary-agent start
|
||||||
|
|
||||||
gitea-release: tools/gitea-release/bin/gitea-release.sh goreleaser
|
gitea-release: .mktools tools/gitea-release/bin/gitea-release.sh goreleaser changelog
|
||||||
mkdir -p .gitea-release
|
mkdir -p .gitea-release
|
||||||
rm -rf .gitea-release/*
|
rm -rf .gitea-release/*
|
||||||
|
|
||||||
cp dist/*.tar.gz .gitea-release/
|
cp dist/*.tar.gz .gitea-release/
|
||||||
cp dist/*.apk .gitea-release/
|
cp dist/*.apk .gitea-release/
|
||||||
cp dist/*.deb .gitea-release/
|
cp dist/*.deb .gitea-release/
|
||||||
|
cp CHANGELOG.md .gitea-release/
|
||||||
|
|
||||||
GITEA_RELEASE_PROJECT="emissary" \
|
GITEA_RELEASE_PROJECT="emissary" \
|
||||||
GITEA_RELEASE_ORG="arcad" \
|
GITEA_RELEASE_ORG="arcad" \
|
||||||
GITEA_RELEASE_BASE_URL="https://forge.cadoles.com" \
|
GITEA_RELEASE_BASE_URL="https://forge.cadoles.com" \
|
||||||
GITEA_RELEASE_VERSION="$(FULL_VERSION)" \
|
GITEA_RELEASE_VERSION="$(MKT_PROJECT_VERSION)" \
|
||||||
GITEA_RELEASE_NAME="$(FULL_VERSION)" \
|
GITEA_RELEASE_NAME="$(MKT_PROJECT_VERSION)" \
|
||||||
GITEA_RELEASE_COMMITISH_TARGET="$(GIT_VERSION)" \
|
GITEA_RELEASE_COMMITISH_TARGET="$(GIT_VERSION)" \
|
||||||
GITEA_RELEASE_IS_DRAFT="false" \
|
GITEA_RELEASE_IS_DRAFT="false" \
|
||||||
GITEA_RELEASE_BODY="" \
|
GITEA_RELEASE_BODY="" \
|
||||||
GITEA_RELEASE_ATTACHMENTS="$$(find .gitea-release/* -type f)" \
|
GITEA_RELEASE_ATTACHMENTS="$$(find .gitea-release/* -type f)" \
|
||||||
tools/gitea-release/bin/gitea-release.sh
|
tools/gitea-release/bin/gitea-release.sh
|
||||||
|
|
||||||
tools/gitea-release/bin/gitea-release.sh:
|
|
||||||
mkdir -p tools/gitea-release/bin
|
|
||||||
curl --output tools/gitea-release/bin/gitea-release.sh https://forge.cadoles.com/Cadoles/Jenkins/raw/branch/master/resources/com/cadoles/gitea/gitea-release.sh
|
|
||||||
chmod +x tools/gitea-release/bin/gitea-release.sh
|
|
||||||
|
|
||||||
.emissary-token:
|
.emissary-token:
|
||||||
$(MAKE) run-emissary-server EMISSARY_CMD="--debug --config tmp/server.yml server auth create-token --role writer > .emissary-token"
|
$(MAKE) run-emissary-server EMISSARY_CMD="--debug --config tmp/server.yml server auth create-token --role writer --output .emissary-token"
|
||||||
|
|
||||||
AGENT_ID ?= 1
|
AGENT_ID ?= 1
|
||||||
|
|
||||||
@ -153,8 +127,8 @@ load-sample-specs:
|
|||||||
cat misc/spec-samples/proxy.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name proxy.emissary.cadoles.com
|
cat misc/spec-samples/proxy.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name proxy.emissary.cadoles.com
|
||||||
cat misc/spec-samples/mdns.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name mdns.emissary.cadoles.com
|
cat misc/spec-samples/mdns.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name mdns.emissary.cadoles.com
|
||||||
|
|
||||||
full-version:
|
version: .mktools
|
||||||
@echo $(FULL_VERSION)
|
@echo $(MKT_PROJECT_VERSION)
|
||||||
|
|
||||||
update-edge-lib:
|
update-edge-lib:
|
||||||
git pull --rebase
|
git pull --rebase
|
||||||
@ -164,3 +138,17 @@ update-edge-lib:
|
|||||||
git add go.mod go.sum
|
git add go.mod go.sum
|
||||||
git commit -m "feat: update arcad/edge dependency"
|
git commit -m "feat: update arcad/edge dependency"
|
||||||
git push
|
git push
|
||||||
|
|
||||||
|
.PHONY: changelog
|
||||||
|
changelog: .mktools
|
||||||
|
$(MAKE) MKT_GIT_CHGLOG_ARGS='--next-tag $(MKT_PROJECT_VERSION) --tag-filter-pattern $(MKT_PROJECT_VERSION_CHANNEL) --output CHANGELOG.md' mkt-changelog
|
||||||
|
|
||||||
|
.PHONY: mktools
|
||||||
|
mktools:
|
||||||
|
rm -rf .mktools
|
||||||
|
curl -q https://forge.cadoles.com/Cadoles/mktools/raw/branch/master/install.sh | $(SHELL)
|
||||||
|
|
||||||
|
.mktools:
|
||||||
|
$(MAKE) mktools
|
||||||
|
|
||||||
|
-include .mktools/*.mk
|
@ -24,3 +24,7 @@ See:
|
|||||||
|
|
||||||
- [`misc/packaging/common/config-agent.yml`](../misc/packaging/common/config-agent.yml)
|
- [`misc/packaging/common/config-agent.yml`](../misc/packaging/common/config-agent.yml)
|
||||||
- [`misc/packaging/common/config-server.yml`](../misc/packaging/common/config-server.yml)
|
- [`misc/packaging/common/config-server.yml`](../misc/packaging/common/config-server.yml)
|
||||||
|
|
||||||
|
### Other projects
|
||||||
|
|
||||||
|
- [`emissary-firmware`](https://forge.cadoles.com/arcad/emissary-firmware) - Preconfigured OpenWRT firmwares with an agent
|
@ -80,9 +80,11 @@
|
|||||||
5. Créer un jeton d'administration:
|
5. Créer un jeton d'administration:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo emissary --workdir /usr/share/emissary --config /etc/emissary/server.yml server auth create-token --role writer --subject $(whoami) > .emissary-token
|
sudo emissary --workdir /usr/share/emissary --config /etc/emissary/server.yml server auth create-token --role writer --subject $(whoami)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Note** Le jeton sera stocké dans le répertoire `$HOME/.config/emissary`.
|
||||||
|
|
||||||
6. Vérifier l'authentification sur l'API:
|
6. Vérifier l'authentification sur l'API:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
1
go.mod
1
go.mod
@ -7,6 +7,7 @@ require (
|
|||||||
github.com/Masterminds/sprig/v3 v3.2.3
|
github.com/Masterminds/sprig/v3 v3.2.3
|
||||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5
|
github.com/alecthomas/participle/v2 v2.0.0-beta.5
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
|
||||||
|
github.com/antonmedv/expr v1.12.7
|
||||||
github.com/brutella/dnssd v1.2.6
|
github.com/brutella/dnssd v1.2.6
|
||||||
github.com/btcsuite/btcd/btcutil v1.1.3
|
github.com/btcsuite/btcd/btcutil v1.1.3
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
2
go.sum
2
go.sum
@ -149,6 +149,8 @@ github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY
|
|||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
|
github.com/antonmedv/expr v1.12.7 h1:jfV/l/+dHWAadLwAtESXNxXdfbK9bE4+FNMHYCMntwk=
|
||||||
|
github.com/antonmedv/expr v1.12.7/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU=
|
||||||
github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY=
|
github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY=
|
||||||
github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs=
|
github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth/agent"
|
"forge.cadoles.com/Cadoles/emissary/internal/auth/agent"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/api"
|
"gitlab.com/wpetit/goweb/api"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
@ -3,7 +3,7 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/lestrrat-go/jwx/v2/jws"
|
"github.com/lestrrat-go/jwx/v2/jws"
|
||||||
"github.com/lestrrat-go/jwx/v2/jwt"
|
"github.com/lestrrat-go/jwx/v2/jwt"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultAcceptableSkew = 5 * time.Minute
|
const DefaultAcceptableSkew = 5 * time.Minute
|
||||||
@ -23,8 +22,6 @@ type Authenticator struct {
|
|||||||
|
|
||||||
// Authenticate implements auth.Authenticator.
|
// Authenticate implements auth.Authenticator.
|
||||||
func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth.User, error) {
|
func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth.User, error) {
|
||||||
ctx = logger.With(r.Context(), logger.F("remoteAddr", r.RemoteAddr))
|
|
||||||
|
|
||||||
authorization := r.Header.Get("Authorization")
|
authorization := r.Header.Get("Authorization")
|
||||||
if authorization == "" {
|
if authorization == "" {
|
||||||
return nil, errors.WithStack(auth.ErrUnauthenticated)
|
return nil, errors.WithStack(auth.ErrUnauthenticated)
|
||||||
|
39
internal/auth/thirdparty/authenticator.go
vendored
39
internal/auth/thirdparty/authenticator.go
vendored
@ -8,22 +8,25 @@ import (
|
|||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth"
|
"forge.cadoles.com/Cadoles/emissary/internal/auth"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
||||||
|
"github.com/lestrrat-go/jwx/v2/jwt"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultAcceptableSkew = 5 * time.Minute
|
const DefaultAcceptableSkew = 5 * time.Minute
|
||||||
|
|
||||||
|
type (
|
||||||
|
GetKeySet func(context.Context) (jwk.Set, error)
|
||||||
|
GetTokenRole func(context.Context, jwt.Token) (string, error)
|
||||||
|
)
|
||||||
|
|
||||||
type Authenticator struct {
|
type Authenticator struct {
|
||||||
keys jwk.Set
|
getKeySet GetKeySet
|
||||||
issuer string
|
getTokenRole GetTokenRole
|
||||||
acceptableSkew time.Duration
|
acceptableSkew time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate implements auth.Authenticator.
|
// Authenticate implements auth.Authenticator.
|
||||||
func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth.User, error) {
|
func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth.User, error) {
|
||||||
ctx = logger.With(r.Context(), logger.F("remoteAddr", r.RemoteAddr))
|
|
||||||
|
|
||||||
authorization := r.Header.Get("Authorization")
|
authorization := r.Header.Get("Authorization")
|
||||||
if authorization == "" {
|
if authorization == "" {
|
||||||
return nil, errors.WithStack(auth.ErrUnauthenticated)
|
return nil, errors.WithStack(auth.ErrUnauthenticated)
|
||||||
@ -34,37 +37,37 @@ func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth
|
|||||||
return nil, errors.WithStack(auth.ErrUnauthenticated)
|
return nil, errors.WithStack(auth.ErrUnauthenticated)
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := parseToken(ctx, a.keys, a.issuer, rawToken, a.acceptableSkew)
|
keys, err := a.getKeySet(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawRole, exists := token.Get(keyRole)
|
token, err := parseToken(ctx, keys, rawToken, a.acceptableSkew)
|
||||||
if !exists {
|
if err != nil {
|
||||||
return nil, errors.New("could not find 'thumbprint' claim")
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
role, ok := rawRole.(string)
|
rawRole, err := a.getTokenRole(ctx, token)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, errors.Errorf("unexpected '%s' claim value: '%v'", keyRole, rawRole)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isValidRole(role) {
|
if !isValidRole(rawRole) {
|
||||||
return nil, errors.Errorf("invalid role '%s'", role)
|
return nil, errors.Errorf("invalid role '%s'", rawRole)
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &User{
|
user := &User{
|
||||||
subject: token.Subject(),
|
subject: token.Subject(),
|
||||||
role: Role(role),
|
role: Role(rawRole),
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthenticator(keys jwk.Set, issuer string, acceptableSkew time.Duration) *Authenticator {
|
func NewAuthenticator(getKeySet GetKeySet, getTokenRole GetTokenRole, acceptableSkew time.Duration) *Authenticator {
|
||||||
return &Authenticator{
|
return &Authenticator{
|
||||||
keys: keys,
|
getTokenRole: getTokenRole,
|
||||||
issuer: issuer,
|
getKeySet: getKeySet,
|
||||||
acceptableSkew: acceptableSkew,
|
acceptableSkew: acceptableSkew,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
internal/auth/thirdparty/jwt.go
vendored
16
internal/auth/thirdparty/jwt.go
vendored
@ -11,15 +11,13 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const keyRole = "role"
|
func parseToken(ctx context.Context, keys jwk.Set, rawToken string, acceptableSkew time.Duration) (jwt.Token, error) {
|
||||||
|
|
||||||
func parseToken(ctx context.Context, keys jwk.Set, issuer string, rawToken string, acceptableSkew time.Duration) (jwt.Token, error) {
|
|
||||||
token, err := jwt.Parse(
|
token, err := jwt.Parse(
|
||||||
[]byte(rawToken),
|
[]byte(rawToken),
|
||||||
jwt.WithKeySet(keys, jws.WithRequireKid(false)),
|
jwt.WithKeySet(keys, jws.WithRequireKid(false)),
|
||||||
jwt.WithIssuer(issuer),
|
|
||||||
jwt.WithValidate(true),
|
jwt.WithValidate(true),
|
||||||
jwt.WithAcceptableSkew(acceptableSkew),
|
jwt.WithAcceptableSkew(acceptableSkew),
|
||||||
|
jwt.WithContext(ctx),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
@ -28,18 +26,16 @@ func parseToken(ctx context.Context, keys jwk.Set, issuer string, rawToken strin
|
|||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateToken(ctx context.Context, key jwk.Key, issuer, subject string, role Role) (string, error) {
|
const DefaultRoleKey string = "role"
|
||||||
|
|
||||||
|
func GenerateToken(ctx context.Context, key jwk.Key, subject string, role Role) (string, error) {
|
||||||
token := jwt.New()
|
token := jwt.New()
|
||||||
|
|
||||||
if err := token.Set(jwt.SubjectKey, subject); err != nil {
|
if err := token.Set(jwt.SubjectKey, subject); err != nil {
|
||||||
return "", errors.WithStack(err)
|
return "", errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := token.Set(jwt.IssuerKey, issuer); err != nil {
|
if err := token.Set(DefaultRoleKey, role); err != nil {
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := token.Set(keyRole, role); err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
return "", errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -3,12 +3,12 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -3,11 +3,11 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -3,11 +3,11 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -3,12 +3,12 @@ package spec
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -3,11 +3,11 @@ package spec
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -4,12 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
@ -3,11 +3,11 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/pkg/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,6 @@ package flag
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -12,6 +11,11 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AuthTokenDefaultHomePath = "$HOME/.config/emissary/auth-token"
|
||||||
|
AuthTokenDefaultLocalPath = ".emissary-token"
|
||||||
|
)
|
||||||
|
|
||||||
func ComposeFlags(flags ...cli.Flag) []cli.Flag {
|
func ComposeFlags(flags ...cli.Flag) []cli.Flag {
|
||||||
baseFlags := []cli.Flag{
|
baseFlags := []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
@ -37,10 +41,10 @@ func ComposeFlags(flags ...cli.Flag) []cli.Flag {
|
|||||||
Aliases: []string{"t"},
|
Aliases: []string{"t"},
|
||||||
Usage: "use `TOKEN` as authentication token",
|
Usage: "use `TOKEN` as authentication token",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringSliceFlag{
|
||||||
Name: "token-file",
|
Name: "token-file",
|
||||||
Usage: "use `TOKEN_FILE` as file containing the authentication token",
|
Usage: "use `TOKEN_FILE` as file containing the authentication token",
|
||||||
Value: ".emissary-token",
|
Value: cli.NewStringSlice(AuthTokenDefaultLocalPath, AuthTokenDefaultHomePath),
|
||||||
TakesFile: true,
|
TakesFile: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -55,14 +59,14 @@ type BaseFlags struct {
|
|||||||
Format format.Format
|
Format format.Format
|
||||||
OutputMode format.OutputMode
|
OutputMode format.OutputMode
|
||||||
Token string
|
Token string
|
||||||
TokenFile string
|
TokenFiles []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBaseFlags(ctx *cli.Context) *BaseFlags {
|
func GetBaseFlags(ctx *cli.Context) *BaseFlags {
|
||||||
serverURL := ctx.String("server")
|
serverURL := ctx.String("server")
|
||||||
rawFormat := ctx.String("format")
|
rawFormat := ctx.String("format")
|
||||||
rawOutputMode := ctx.String("output-mode")
|
rawOutputMode := ctx.String("output-mode")
|
||||||
tokenFile := ctx.String("token-file")
|
tokenFiles := ctx.StringSlice("token-file")
|
||||||
token := ctx.String("token")
|
token := ctx.String("token")
|
||||||
|
|
||||||
return &BaseFlags{
|
return &BaseFlags{
|
||||||
@ -70,7 +74,7 @@ func GetBaseFlags(ctx *cli.Context) *BaseFlags {
|
|||||||
Format: format.Format(rawFormat),
|
Format: format.Format(rawFormat),
|
||||||
OutputMode: format.OutputMode(rawOutputMode),
|
OutputMode: format.OutputMode(rawOutputMode),
|
||||||
Token: token,
|
Token: token,
|
||||||
TokenFile: tokenFile,
|
TokenFiles: tokenFiles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,18 +83,20 @@ func GetToken(flags *BaseFlags) (string, error) {
|
|||||||
return flags.Token, nil
|
return flags.Token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.TokenFile == "" {
|
for _, tokenFile := range flags.TokenFiles {
|
||||||
return "", nil
|
tokenFile = os.ExpandEnv(tokenFile)
|
||||||
}
|
|
||||||
|
|
||||||
rawToken, err := ioutil.ReadFile(flags.TokenFile)
|
rawToken, err := os.ReadFile(tokenFile)
|
||||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
return "", errors.WithStack(err)
|
return "", errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rawToken == nil {
|
if rawToken == nil {
|
||||||
return "", nil
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.TrimSpace(string(rawToken)), nil
|
return strings.TrimSpace(string(rawToken)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,11 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth/thirdparty"
|
"forge.cadoles.com/Cadoles/emissary/internal/auth/thirdparty"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
||||||
"github.com/lithammer/shortuuid/v4"
|
"github.com/lithammer/shortuuid/v4"
|
||||||
@ -26,6 +29,13 @@ func CreateTokenCommand() *cli.Command {
|
|||||||
Usage: "associate `SUBJECT` to the token",
|
Usage: "associate `SUBJECT` to the token",
|
||||||
Value: fmt.Sprintf("user-%s", shortuuid.New()),
|
Value: fmt.Sprintf("user-%s", shortuuid.New()),
|
||||||
},
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "output",
|
||||||
|
Aliases: []string{"o"},
|
||||||
|
TakesFile: true,
|
||||||
|
Usage: "output token to `OUTPUT` (or '-' to print to stdout)",
|
||||||
|
Value: flag.AuthTokenDefaultHomePath,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: func(ctx *cli.Context) error {
|
Action: func(ctx *cli.Context) error {
|
||||||
conf, err := common.LoadConfig(ctx)
|
conf, err := common.LoadConfig(ctx)
|
||||||
@ -35,18 +45,40 @@ func CreateTokenCommand() *cli.Command {
|
|||||||
|
|
||||||
subject := ctx.String("subject")
|
subject := ctx.String("subject")
|
||||||
role := ctx.String("role")
|
role := ctx.String("role")
|
||||||
|
output := ctx.String("output")
|
||||||
|
|
||||||
key, err := jwk.LoadOrGenerate(string(conf.Server.PrivateKeyPath), jwk.DefaultKeySize)
|
localAuth := conf.Server.Auth.Local
|
||||||
|
if localAuth == nil {
|
||||||
|
return errors.New("local auth is disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := jwk.LoadOrGenerate(string(localAuth.PrivateKeyPath), jwk.DefaultKeySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := thirdparty.GenerateToken(ctx.Context, key, string(conf.Server.Issuer), subject, thirdparty.Role(role))
|
token, err := thirdparty.GenerateToken(ctx.Context, key, subject, thirdparty.Role(role))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output = os.ExpandEnv(output)
|
||||||
|
|
||||||
|
if output == "-" {
|
||||||
fmt.Println(token)
|
fmt.Println(token)
|
||||||
|
} else {
|
||||||
|
outputDirectory := filepath.Dir(output)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(outputDirectory, os.FileMode(0o700)); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(output, []byte(token), os.FileMode(0o600)); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Token written to '%s'.\n", output)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@ -123,3 +124,37 @@ func (iss *InterpolatedStringSlice) UnmarshalYAML(value *yaml.Node) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InterpolatedDuration time.Duration
|
||||||
|
|
||||||
|
func (id *InterpolatedDuration) UnmarshalYAML(value *yaml.Node) error {
|
||||||
|
var str string
|
||||||
|
|
||||||
|
if err := value.Decode(&str); err != nil {
|
||||||
|
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
|
||||||
|
str = os.Getenv(match[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
duration, err := time.ParseDuration(str)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not parse duration '%v', line '%d'", str, value.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
*id = InterpolatedDuration(duration)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id *InterpolatedDuration) MarshalYAML() (interface{}, error) {
|
||||||
|
duration := time.Duration(*id)
|
||||||
|
|
||||||
|
return duration.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInterpolatedDuration(d time.Duration) *InterpolatedDuration {
|
||||||
|
id := InterpolatedDuration(d)
|
||||||
|
return &id
|
||||||
|
}
|
||||||
|
@ -1,19 +1,50 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/auth/thirdparty"
|
||||||
|
)
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
PrivateKeyPath InterpolatedString `yaml:"privateKeyPath"`
|
|
||||||
Issuer InterpolatedString `yaml:"issuer"`
|
|
||||||
HTTP HTTPConfig `yaml:"http"`
|
HTTP HTTPConfig `yaml:"http"`
|
||||||
Database DatabaseConfig `yaml:"database"`
|
Database DatabaseConfig `yaml:"database"`
|
||||||
CORS CORSConfig `yaml:"cors"`
|
CORS CORSConfig `yaml:"cors"`
|
||||||
|
Auth AuthConfig `yaml:"auth"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultServerConfig() ServerConfig {
|
func NewDefaultServerConfig() ServerConfig {
|
||||||
return ServerConfig{
|
return ServerConfig{
|
||||||
PrivateKeyPath: "server-key.json",
|
|
||||||
Issuer: "http://127.0.0.1:3000",
|
|
||||||
HTTP: NewDefaultHTTPConfig(),
|
HTTP: NewDefaultHTTPConfig(),
|
||||||
Database: NewDefaultDatabaseConfig(),
|
Database: NewDefaultDatabaseConfig(),
|
||||||
CORS: NewDefaultCORSConfig(),
|
CORS: NewDefaultCORSConfig(),
|
||||||
|
Auth: NewDefaultAuthConfig(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AuthConfig struct {
|
||||||
|
Local *LocalAuthConfig `yaml:"local"`
|
||||||
|
Remote *RemoteAuthConfig `yaml:"remote"`
|
||||||
|
RoleExtractionRules []string `yaml:"roleExtractionRules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultAuthConfig() AuthConfig {
|
||||||
|
return AuthConfig{
|
||||||
|
Local: &LocalAuthConfig{
|
||||||
|
PrivateKeyPath: "server-key.json",
|
||||||
|
},
|
||||||
|
Remote: nil,
|
||||||
|
RoleExtractionRules: []string{
|
||||||
|
fmt.Sprintf("jwt.%s != nil ? str(jwt.%s) : ''", thirdparty.DefaultRoleKey, thirdparty.DefaultRoleKey),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalAuthConfig struct {
|
||||||
|
PrivateKeyPath InterpolatedString `yaml:"privateKeyPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoteAuthConfig struct {
|
||||||
|
JsonWebKeySetURL InterpolatedString `yaml:"jwksUrl"`
|
||||||
|
RefreshInterval *InterpolatedDuration `yaml:"refreshInterval"`
|
||||||
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package jwk
|
package jwk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil/base58"
|
"github.com/btcsuite/btcd/btcutil/base58"
|
||||||
"github.com/lestrrat-go/jwx/v2/jwa"
|
"github.com/lestrrat-go/jwx/v2/jwa"
|
||||||
@ -34,7 +36,7 @@ func Parse(src []byte, options ...jwk.ParseOption) (Set, error) {
|
|||||||
return jwk.Parse(src, options...)
|
return jwk.Parse(src, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PublicKeySet(keys ...jwk.Key) (jwk.Set, error) {
|
func RS256PublicKeySet(keys ...jwk.Key) (jwk.Set, error) {
|
||||||
set := jwk.NewSet()
|
set := jwk.NewSet()
|
||||||
|
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
@ -85,6 +87,27 @@ func LoadOrGenerate(path string, size int) (jwk.Key, error) {
|
|||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateCachedRemoteKeySet(ctx context.Context, url string, refreshInterval time.Duration) (func(context.Context) (jwk.Set, error), error) {
|
||||||
|
cache := jwk.NewCache(ctx)
|
||||||
|
|
||||||
|
if err := cache.Register(url, jwk.WithMinRefreshInterval(refreshInterval)); err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := cache.Refresh(ctx, url); err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(ctx context.Context) (jwk.Set, error) {
|
||||||
|
keySet, err := cache.Get(ctx, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keySet, nil
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func Generate(size int) (jwk.Key, error) {
|
func Generate(size int) (jwk.Key, error) {
|
||||||
privKey, err := rsa.GenerateKey(rand.Reader, size)
|
privKey, err := rsa.GenerateKey(rand.Reader, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -12,7 +12,7 @@ func TestJWK(t *testing.T) {
|
|||||||
t.Fatalf("%+v", errors.WithStack(err))
|
t.Fatalf("%+v", errors.WithStack(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
keySet, err := PublicKeySet(privateKey)
|
keySet, err := RS256PublicKeySet(privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%+v", errors.WithStack(err))
|
t.Fatalf("%+v", errors.WithStack(err))
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,9 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth"
|
"forge.cadoles.com/Cadoles/emissary/internal/auth"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth/agent"
|
"forge.cadoles.com/Cadoles/emissary/internal/auth/agent"
|
||||||
@ -13,9 +16,13 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/config"
|
"forge.cadoles.com/Cadoles/emissary/internal/config"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
||||||
|
"github.com/antonmedv/expr"
|
||||||
|
"github.com/antonmedv/expr/vm"
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/go-chi/chi/middleware"
|
"github.com/go-chi/chi/middleware"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
|
"github.com/lestrrat-go/jwx/v2/jwa"
|
||||||
|
"github.com/lestrrat-go/jwx/v2/jwt"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
@ -72,20 +79,6 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
key, err := jwk.LoadOrGenerate(string(s.conf.PrivateKeyPath), jwk.DefaultKeySize)
|
|
||||||
if err != nil {
|
|
||||||
errs <- errors.WithStack(err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
keys, err := jwk.PublicKeySet(key)
|
|
||||||
if err != nil {
|
|
||||||
errs <- errors.WithStack(err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
router := chi.NewRouter()
|
router := chi.NewRouter()
|
||||||
|
|
||||||
router.Use(middleware.Logger)
|
router.Use(middleware.Logger)
|
||||||
@ -100,12 +93,19 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
|
|||||||
|
|
||||||
router.Use(corsMiddleware.Handler)
|
router.Use(corsMiddleware.Handler)
|
||||||
|
|
||||||
|
thirdPartyAuth, err := s.getThirdPartyAuthenticator()
|
||||||
|
if err != nil {
|
||||||
|
errs <- errors.WithStack(err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
router.Route("/api/v1", func(r chi.Router) {
|
router.Route("/api/v1", func(r chi.Router) {
|
||||||
r.Post("/register", s.registerAgent)
|
r.Post("/register", s.registerAgent)
|
||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(auth.Middleware(
|
r.Use(auth.Middleware(
|
||||||
thirdparty.NewAuthenticator(keys, string(s.conf.Issuer), thirdparty.DefaultAcceptableSkew),
|
thirdPartyAuth,
|
||||||
agent.NewAuthenticator(s.agentRepo, agent.DefaultAcceptableSkew),
|
agent.NewAuthenticator(s.agentRepo, agent.DefaultAcceptableSkew),
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -131,6 +131,151 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
|
|||||||
logger.Info(ctx, "http server exiting")
|
logger.Info(ctx, "http server exiting")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) getThirdPartyAuthenticator() (*thirdparty.Authenticator, error) {
|
||||||
|
var localPublicKey jwk.Key
|
||||||
|
|
||||||
|
localAuth := s.conf.Auth.Local
|
||||||
|
if localAuth != nil {
|
||||||
|
key, err := jwk.LoadOrGenerate(string(localAuth.PrivateKeyPath), jwk.DefaultKeySize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey, err := key.PublicKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := publicKey.Set(jwk.AlgorithmKey, jwa.RS256); err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
localPublicKey = publicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
var getRemoteKeySet thirdparty.GetKeySet
|
||||||
|
|
||||||
|
remoteAuth := s.conf.Auth.Remote
|
||||||
|
if remoteAuth != nil {
|
||||||
|
refreshInterval := time.Minute * 15
|
||||||
|
if remoteAuth.RefreshInterval != nil {
|
||||||
|
refreshInterval = time.Duration(*remoteAuth.RefreshInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn, err := jwk.CreateCachedRemoteKeySet(context.Background(), string(remoteAuth.JsonWebKeySetURL), refreshInterval)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
getRemoteKeySet = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
getKeySet := func(ctx context.Context) (jwk.Set, error) {
|
||||||
|
keySet := jwk.NewSet()
|
||||||
|
|
||||||
|
if localPublicKey != nil {
|
||||||
|
if err := keySet.AddKey(localPublicKey); err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if getRemoteKeySet != nil {
|
||||||
|
remoteKeySet, err := getRemoteKeySet(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx := 0; idx < remoteKeySet.Len(); idx++ {
|
||||||
|
key, ok := remoteKeySet.Key(idx)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := keySet.AddKey(key); err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keySet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
getTokenRole, err := s.createGetTokenRoleFunc()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return thirdparty.NewAuthenticator(getKeySet, getTokenRole, thirdparty.DefaultAcceptableSkew), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) createGetTokenRoleFunc() (func(ctx context.Context, token jwt.Token) (string, error), error) {
|
||||||
|
rawRules := s.conf.Auth.RoleExtractionRules
|
||||||
|
rules := make([]*vm.Program, 0, len(rawRules))
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
JWT map[string]any `expr:"jwt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
strFunc := expr.Function(
|
||||||
|
"str",
|
||||||
|
func(params ...any) (any, error) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
for _, p := range params {
|
||||||
|
if _, err := builder.WriteString(fmt.Sprintf("%v", p)); err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String(), nil
|
||||||
|
},
|
||||||
|
new(func(any) string),
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, rr := range rawRules {
|
||||||
|
r, err := expr.Compile(rr,
|
||||||
|
expr.Env(Env{}),
|
||||||
|
expr.AsKind(reflect.String),
|
||||||
|
strFunc,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "could not compile role extraction rule '%s'", rr)
|
||||||
|
}
|
||||||
|
|
||||||
|
rules = append(rules, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(ctx context.Context, token jwt.Token) (string, error) {
|
||||||
|
jwt, err := token.AsMap(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vm := vm.VM{}
|
||||||
|
|
||||||
|
for _, r := range rules {
|
||||||
|
result, err := vm.Run(r, Env{
|
||||||
|
JWT: jwt,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
role, ok := result.(string)
|
||||||
|
if !ok {
|
||||||
|
logger.Debug(ctx, "ignoring unexpected role extraction result", logger.F("result", result))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if role != "" {
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("could not extract role from token")
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func New(funcs ...OptionFunc) *Server {
|
func New(funcs ...OptionFunc) *Server {
|
||||||
opt := defaultOption()
|
opt := defaultOption()
|
||||||
for _, fn := range funcs {
|
for _, fn := range funcs {
|
||||||
|
@ -1,26 +1,55 @@
|
|||||||
|
# Emissary agent configuration
|
||||||
|
|
||||||
|
# Logger configuration
|
||||||
logger:
|
logger:
|
||||||
|
# Logging verbosity
|
||||||
|
# DEBUG: 0
|
||||||
|
# INFO: 1
|
||||||
|
# WARN: 2
|
||||||
|
# ERROR: 3
|
||||||
|
# CRITICAL: 4
|
||||||
level: 1
|
level: 1
|
||||||
|
# Logging format
|
||||||
|
# Possible values: human, json
|
||||||
format: human
|
format: human
|
||||||
|
|
||||||
|
# Agent configuration
|
||||||
agent:
|
agent:
|
||||||
|
# Emissary server URL
|
||||||
serverUrl: http://127.0.0.1:3000
|
serverUrl: http://127.0.0.1:3000
|
||||||
|
# Agent private key path
|
||||||
privateKeyPath: /var/lib/emissary/agent-key.json
|
privateKeyPath: /var/lib/emissary/agent-key.json
|
||||||
reconciliationInterval: 5
|
# Agent reconciliation interval (in seconds)
|
||||||
|
reconciliationInterval: 30
|
||||||
|
|
||||||
|
# Controllers configuration
|
||||||
controllers:
|
controllers:
|
||||||
|
# Persistence controller configuration
|
||||||
persistence:
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
stateFile: /var/lib/emissary/state.json
|
stateFile: /var/lib/emissary/state.json
|
||||||
|
|
||||||
|
# Spec controller configuration
|
||||||
spec:
|
spec:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
# Proxy controller configuration
|
||||||
proxy:
|
proxy:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
# UCI controller configuration
|
||||||
uci:
|
uci:
|
||||||
enabled: true
|
enabled: true
|
||||||
binPath: uci
|
binPath: uci
|
||||||
configBackupFile: /var/lib/emissary/uci-backup.conf
|
configBackupFile: /var/lib/emissary/uci-backup.conf
|
||||||
|
|
||||||
|
# App controller configuration
|
||||||
app:
|
app:
|
||||||
enabled: true
|
enabled: true
|
||||||
dataDir: /var/lib/emissary/apps/data
|
dataDir: /var/lib/emissary/apps/data
|
||||||
downloadDir: /var/lib/emissary/apps/bundles
|
downloadDir: /var/lib/emissary/apps/bundles
|
||||||
|
|
||||||
|
# Sysupgrade controller configuration
|
||||||
sysupgrade:
|
sysupgrade:
|
||||||
enabled: true
|
enabled: true
|
||||||
sysupgradeCommand:
|
sysupgradeCommand:
|
||||||
@ -33,6 +62,8 @@ agent:
|
|||||||
- sh
|
- sh
|
||||||
- -c
|
- -c
|
||||||
- source /etc/openwrt_release && echo "$DISTRIB_ID-$DISTRIB_RELEASE-$DISTRIB_REVISION"
|
- source /etc/openwrt_release && echo "$DISTRIB_ID-$DISTRIB_RELEASE-$DISTRIB_REVISION"
|
||||||
|
|
||||||
|
# Collectors configuration
|
||||||
collectors:
|
collectors:
|
||||||
- name: uname
|
- name: uname
|
||||||
command: uname
|
command: uname
|
||||||
|
@ -1,15 +1,38 @@
|
|||||||
|
# Emissary server configuration
|
||||||
|
|
||||||
|
# Logger configuration
|
||||||
logger:
|
logger:
|
||||||
|
# Logging verbosity
|
||||||
|
# DEBUG: 0
|
||||||
|
# INFO: 1
|
||||||
|
# WARN: 2
|
||||||
|
# ERROR: 3
|
||||||
|
# CRITICAL: 4
|
||||||
level: 1
|
level: 1
|
||||||
|
# Logging format
|
||||||
|
# Possible values: human, json
|
||||||
format: human
|
format: human
|
||||||
|
|
||||||
|
# Server configuration
|
||||||
server:
|
server:
|
||||||
privateKeyPath: /var/lib/emissary/server-key.json
|
# HTTP server configuration
|
||||||
issuer: http://127.0.0.1:3000
|
|
||||||
http:
|
http:
|
||||||
|
# Listening address (0.0.0.0 to listen on all interfaces)
|
||||||
host: 0.0.0.0
|
host: 0.0.0.0
|
||||||
|
# Listening port
|
||||||
port: 3000
|
port: 3000
|
||||||
|
|
||||||
|
# Database configuration
|
||||||
database:
|
database:
|
||||||
|
# Database driver
|
||||||
|
# Possible values: sqlite
|
||||||
driver: sqlite
|
driver: sqlite
|
||||||
|
# Database DSN
|
||||||
|
# sqlite: see https://github.com/mattn/go-sqlite3#connection-string
|
||||||
dsn: sqlite:///var/lib/emissary/data.sqlite?_pragma=foreign_keys(1)&_pragma=busy_timeout=60000
|
dsn: sqlite:///var/lib/emissary/data.sqlite?_pragma=foreign_keys(1)&_pragma=busy_timeout=60000
|
||||||
|
|
||||||
|
# CORS configuration
|
||||||
|
# See https://developer.mozilla.org/en/docs/Glossary/CORS
|
||||||
cors:
|
cors:
|
||||||
allowedOrigins: []
|
allowedOrigins: []
|
||||||
allowCredentials: true
|
allowCredentials: true
|
||||||
@ -24,4 +47,25 @@ server:
|
|||||||
- Content-Type
|
- Content-Type
|
||||||
- Authorization
|
- Authorization
|
||||||
- Sentry-Trace
|
- Sentry-Trace
|
||||||
debug: false
|
|
||||||
|
# Auth configuration
|
||||||
|
auth:
|
||||||
|
# Local authentication configuration
|
||||||
|
local:
|
||||||
|
privateKeyPath: /var/lib/emissary/server-key.json
|
||||||
|
|
||||||
|
# Remote authentication configuration
|
||||||
|
# Disabled by default
|
||||||
|
remote: ~
|
||||||
|
# jwksUrl: https://my-server/.well-known/jwks.json
|
||||||
|
|
||||||
|
# Role extraction rules
|
||||||
|
# Permit to derivate user's role
|
||||||
|
# from the received JWT.
|
||||||
|
#
|
||||||
|
# The first rule returning a non empty
|
||||||
|
# string will define the role of the user.
|
||||||
|
#
|
||||||
|
# The role should be 'reader' or 'writer'.
|
||||||
|
roleExtractionRules:
|
||||||
|
- "jwt.role != nil ? str(jwt.role) : ''"
|
||||||
|
23
pkg/client/alias.go
Normal file
23
pkg/client/alias.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Spec = spec.Spec
|
||||||
|
SpecName = spec.Name
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
AgentID = datastore.AgentID
|
||||||
|
Agent = datastore.Agent
|
||||||
|
AgentStatus = datastore.AgentStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
type MetadataTuple = metadata.Tuple
|
||||||
|
|
||||||
|
type Key = jwk.Key
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) DeleteAgent(ctx context.Context, agentID datastore.AgentID, funcs ...OptionFunc) (datastore.AgentID, error) {
|
func (c *Client) DeleteAgent(ctx context.Context, agentID AgentID, funcs ...OptionFunc) (AgentID, error) {
|
||||||
response := withResponse[struct {
|
response := withResponse[struct {
|
||||||
AgentID int64 `json:"agentId"`
|
AgentID int64 `json:"agentId"`
|
||||||
}]()
|
}]()
|
@ -4,12 +4,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) DeleteAgentSpec(ctx context.Context, agentID datastore.AgentID, name spec.Name, funcs ...OptionFunc) (spec.Name, error) {
|
func (c *Client) DeleteAgentSpec(ctx context.Context, agentID AgentID, name SpecName, funcs ...OptionFunc) (SpecName, error) {
|
||||||
payload := struct {
|
payload := struct {
|
||||||
Name spec.Name `json:"name"`
|
Name spec.Name `json:"name"`
|
||||||
}{
|
}{
|
@ -4,13 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) GetAgent(ctx context.Context, agentID datastore.AgentID, funcs ...OptionFunc) (*datastore.Agent, error) {
|
func (c *Client) GetAgent(ctx context.Context, agentID AgentID, funcs ...OptionFunc) (*Agent, error) {
|
||||||
response := withResponse[struct {
|
response := withResponse[struct {
|
||||||
Agent *datastore.Agent `json:"agent"`
|
Agent *Agent `json:"agent"`
|
||||||
}]()
|
}]()
|
||||||
|
|
||||||
path := fmt.Sprintf("/api/v1/agents/%d", agentID)
|
path := fmt.Sprintf("/api/v1/agents/%d", agentID)
|
@ -4,12 +4,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) GetAgentSpecs(ctx context.Context, agentID datastore.AgentID, funcs ...OptionFunc) ([]spec.Spec, error) {
|
func (c *Client) GetAgentSpecs(ctx context.Context, agentID AgentID, funcs ...OptionFunc) ([]Spec, error) {
|
||||||
response := withResponse[struct {
|
response := withResponse[struct {
|
||||||
Specs []*spec.RawSpec `json:"specs"`
|
Specs []*spec.RawSpec `json:"specs"`
|
||||||
}]()
|
}]()
|
@ -16,8 +16,8 @@ type QueryAgentsOptions struct {
|
|||||||
Limit *int
|
Limit *int
|
||||||
Offset *int
|
Offset *int
|
||||||
Thumbprints []string
|
Thumbprints []string
|
||||||
IDs []datastore.AgentID
|
IDs []AgentID
|
||||||
Statuses []datastore.AgentStatus
|
Statuses []AgentStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithQueryAgentsOptions(funcs ...OptionFunc) QueryAgentsOptionFunc {
|
func WithQueryAgentsOptions(funcs ...OptionFunc) QueryAgentsOptionFunc {
|
||||||
@ -56,7 +56,7 @@ func WithQueryAgentsStatus(statuses ...datastore.AgentStatus) QueryAgentsOptionF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) QueryAgents(ctx context.Context, funcs ...QueryAgentsOptionFunc) ([]*datastore.Agent, int, error) {
|
func (c *Client) QueryAgents(ctx context.Context, funcs ...QueryAgentsOptionFunc) ([]*Agent, int, error) {
|
||||||
options := &QueryAgentsOptions{}
|
options := &QueryAgentsOptions{}
|
||||||
for _, fn := range funcs {
|
for _, fn := range funcs {
|
||||||
fn(options)
|
fn(options)
|
@ -9,8 +9,8 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) RegisterAgent(ctx context.Context, key jwk.Key, thumbprint string, meta []metadata.Tuple, funcs ...OptionFunc) (*datastore.Agent, error) {
|
func (c *Client) RegisterAgent(ctx context.Context, key Key, thumbprint string, meta []MetadataTuple, funcs ...OptionFunc) (*Agent, error) {
|
||||||
keySet, err := jwk.PublicKeySet(key)
|
keySet, err := jwk.RS256PublicKeySet(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Client) UpdateAgentSpec(ctx context.Context, agentID datastore.AgentID, spc spec.Spec, funcs ...OptionFunc) (*datastore.Spec, error) {
|
func (c *Client) UpdateAgentSpec(ctx context.Context, agentID AgentID, spc Spec, funcs ...OptionFunc) (Spec, error) {
|
||||||
payload := struct {
|
payload := struct {
|
||||||
Name spec.Name `json:"name"`
|
Name spec.Name `json:"name"`
|
||||||
Revision int `json:"revision"`
|
Revision int `json:"revision"`
|
Reference in New Issue
Block a user