You've already forked ionscale
mirror of
https://github.com/jsiebens/ionscale.git
synced 2026-04-06 13:02:58 +01:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 46cce89e0e | |||
| 128ed22bde | |||
| 5d1ac326ea | |||
| 7eb808c71c | |||
| d8f0492940 | |||
| b8b1075389 | |||
| 9f3a6bbcec | |||
| cce0fd08b0 | |||
| 58634fc98e | |||
| 280ee7e1b6 | |||
| b8c752d04a | |||
| dfd2fe9fdd | |||
| 25203d3cca | |||
| 0f54539302 | |||
| dea60272b7 | |||
| 5e43014a09 | |||
| 9748955f18 | |||
| 44b6b20361 | |||
| cbde00c9f5 | |||
| 8f2c198bfe | |||
| 8f998b05f7 | |||
| 3fccde2932 | |||
| 7fa31bdf1f | |||
| 980ab1bc46 | |||
| 123ca99665 | |||
| 0c5e586cf9 | |||
| 79bc3bffb1 | |||
| 452c5ee516 | |||
| c1ea283e6d | |||
| 6a5d44882a | |||
| cbcbd61c3e | |||
| b083e2631a | |||
| 4587ed8eaa | |||
| 3118d2e573 | |||
| 7e1d90590d | |||
| 1b66b1e9be | |||
| 35e13a0698 | |||
| 951d0f299e | |||
| d10a022f29 | |||
| 9b5f045849 | |||
| 8a3f47490e | |||
| c76c2f16dd | |||
| dd2e783d8e | |||
| 473c3370ce | |||
| d6cc55cf5b |
@@ -1 +1,3 @@
|
||||
.git
|
||||
.idea
|
||||
tests
|
||||
@@ -1,6 +1,10 @@
|
||||
name: Integration Tests
|
||||
|
||||
on: workflow_dispatch
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
integration:
|
||||
@@ -9,15 +13,15 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ts_version:
|
||||
- "1.56.1"
|
||||
- "1.54.1"
|
||||
- "1.52.1"
|
||||
- "1.50.1"
|
||||
- "1.48.1"
|
||||
- "1.46.1"
|
||||
- "1.44.2"
|
||||
- "1.42.0"
|
||||
- "1.40.1"
|
||||
- "v1.56"
|
||||
- "v1.54"
|
||||
- "v1.52"
|
||||
- "v1.50"
|
||||
- "v1.48"
|
||||
- "v1.46"
|
||||
- "v1.44"
|
||||
- "v1.42"
|
||||
- "v1.40"
|
||||
env:
|
||||
IONSCALE_TESTS_TS_TARGET_VERSION: ${{ matrix.ts_version }}
|
||||
steps:
|
||||
|
||||
@@ -15,15 +15,15 @@ jobs:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: go
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
trivy:
|
||||
name: Trivy
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run Trivy vulnerability scanner
|
||||
@@ -43,6 +43,6 @@ jobs:
|
||||
format: 'sarif'
|
||||
output: 'trivy-results.sarif'
|
||||
- name: Upload Trivy scan results
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Close inactive issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
days-before-issue-stale: 90
|
||||
days-before-issue-close: 7
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 90 days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} alpine:3.19.0
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} alpine:3.19.1
|
||||
|
||||
COPY ionscale /usr/local/bin/ionscale
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
init:
|
||||
go install github.com/a-h/templ/cmd/templ@latest
|
||||
go install github.com/bufbuild/buf/cmd/buf@latest
|
||||
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
go install github.com/bufbuild/connect-go/cmd/protoc-gen-connect-go@latest
|
||||
|
||||
generate:
|
||||
templ generate
|
||||
buf generate proto
|
||||
|
||||
format:
|
||||
|
||||
@@ -3,19 +3,23 @@ module github.com/jsiebens/ionscale
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/99designs/keyring v1.2.2
|
||||
github.com/a-h/templ v0.2.543
|
||||
github.com/apparentlymart/go-cidr v1.1.0
|
||||
github.com/bufbuild/connect-go v1.10.0
|
||||
github.com/caarlos0/env/v6 v6.10.1
|
||||
github.com/caddyserver/certmagic v0.20.0
|
||||
github.com/coreos/go-oidc/v3 v3.9.0
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20231002161417-6a283f1aaaf2
|
||||
github.com/glebarez/sqlite v1.10.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.0.2
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
||||
github.com/go-jose/go-jose/v3 v3.0.1
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/hashicorp/go-bexpr v0.1.13
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/imdario/mergo v0.3.16
|
||||
github.com/jsiebens/go-edit v0.1.0
|
||||
github.com/jsiebens/mockoidc v0.1.0-rc2
|
||||
github.com/klauspost/compress v1.17.4
|
||||
github.com/labstack/echo-contrib v0.15.0
|
||||
github.com/labstack/echo/v4 v4.11.4
|
||||
@@ -29,18 +33,20 @@ require (
|
||||
github.com/mitchellh/pointerstructure v1.2.1
|
||||
github.com/mr-tron/base58 v1.2.0
|
||||
github.com/nleeper/goment v1.4.4
|
||||
github.com/ory/dockertest/v3 v3.9.1
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/ory/dockertest/v3 v3.10.0
|
||||
github.com/prometheus/client_golang v1.18.0
|
||||
github.com/rodaine/table v1.1.0
|
||||
github.com/sony/sonyflake v1.2.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a
|
||||
github.com/travisjeffery/certmagic-sqlstorage v1.1.1
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0
|
||||
go.uber.org/zap v1.26.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/oauth2 v0.15.0
|
||||
golang.org/x/sync v0.5.0
|
||||
golang.org/x/crypto v0.18.0
|
||||
golang.org/x/net v0.20.0
|
||||
golang.org/x/oauth2 v0.16.0
|
||||
golang.org/x/sync v0.6.0
|
||||
google.golang.org/protobuf v1.32.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -48,61 +54,71 @@ require (
|
||||
gorm.io/gorm v1.25.5
|
||||
gorm.io/plugin/prometheus v0.1.0
|
||||
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a
|
||||
tailscale.com v1.48.0
|
||||
tailscale.com v1.56.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.23.0 // indirect
|
||||
cloud.google.com/go/compute v1.23.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 // indirect
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.36.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.36.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
|
||||
github.com/aws/smithy-go v1.19.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/containerd/continuity v0.3.0 // indirect
|
||||
github.com/coreos/go-iptables v0.6.0 // indirect
|
||||
github.com/containerd/continuity v0.4.3 // indirect
|
||||
github.com/coreos/go-iptables v0.7.0 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dblohm7/wingoes v0.0.0-20240108175832-49174e152ce1 // indirect
|
||||
github.com/digitalocean/godo v1.107.0 // indirect
|
||||
github.com/docker/cli v23.0.5+incompatible // indirect
|
||||
github.com/docker/docker v23.0.5+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/cli v25.0.2+incompatible // indirect
|
||||
github.com/docker/docker v25.0.2+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
||||
@@ -115,7 +131,7 @@ require (
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.3.2 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
@@ -124,22 +140,23 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/mdlayher/socket v0.5.0 // indirect
|
||||
github.com/mholt/acmez v1.2.0 // indirect
|
||||
github.com/miekg/dns v1.1.57 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
|
||||
github.com/opencontainers/runc v1.1.4 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc6 // indirect
|
||||
github.com/opencontainers/runc v1.1.12 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect
|
||||
github.com/tkuchiki/go-timezone v0.2.2 // indirect
|
||||
@@ -152,26 +169,30 @@ require (
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
|
||||
go.opentelemetry.io/otel v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.21.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
|
||||
go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/term v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.16.1 // indirect
|
||||
golang.org/x/tools v0.17.0 // indirect
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||
google.golang.org/api v0.126.0 // indirect
|
||||
google.golang.org/api v0.155.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect
|
||||
google.golang.org/grpc v1.60.1 // indirect
|
||||
gotest.tools/v3 v3.4.0 // indirect
|
||||
modernc.org/libc v1.38.0 // indirect
|
||||
modernc.org/libc v1.40.1 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/sqlite v1.28.0 // indirect
|
||||
nhooyr.io/websocket v1.8.7 // indirect
|
||||
nhooyr.io/websocket v1.8.10 // indirect
|
||||
)
|
||||
|
||||
@@ -30,8 +30,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9
|
||||
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
|
||||
cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
|
||||
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
|
||||
cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA=
|
||||
cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw=
|
||||
cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME=
|
||||
cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk=
|
||||
cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
|
||||
cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
|
||||
cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
|
||||
@@ -68,8 +68,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz
|
||||
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
|
||||
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
|
||||
cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=
|
||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
|
||||
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
|
||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
@@ -179,11 +179,17 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX
|
||||
cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
|
||||
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
|
||||
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc=
|
||||
filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA=
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
|
||||
github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
|
||||
github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI=
|
||||
@@ -194,8 +200,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxw
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 h1:hVeq+yCyUi+MsoO/CU95yqCIcdzra5ovzk8Q2BBpV2M=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
@@ -203,6 +209,8 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk=
|
||||
github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA=
|
||||
github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
|
||||
github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -211,52 +219,53 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
|
||||
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.21/go.mod h1:+jPQiVPz1diRnjj6VGqWcLK6EzNmQ42l7J3OqGTLsSY=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.2 h1:+RWLEIWQIGgrz2pBPAUoGgNGs1TOyF4Hml7hCnYj2jc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.2/go.mod h1:l6xqvUxt0Oj7PI/SUXYLNyZ9T/yBPn3YTQcJLLOdtR8=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.3 h1:dKuc2jdp10y13dEEvPqWxqLoc0vF3Z9FC45MvuQSxOA=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.3/go.mod h1:Bxgi+DeeswYofcYO0XyGClwlrq3DZEXli0kLf4hkGA0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.20/go.mod h1:xtZnXErtbZ8YGXC3+8WfajpMBn5Ga/3ojZdxHq6iI8o=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.13 h1:WLABQ4Cp4vXtXfOWOS3MEZKr6AAYUpMczLhgKtAjQ/8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.13/go.mod h1:Qg6x82FXwW0sJHzYruxGiuApNo31UEtJvXVSZAXeWiw=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.14 h1:mMDTwwYO9A0/JbOCOG7EOZHtYM+o7OfGWfu0toa23VE=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.14/go.mod h1:cniAUh3ErQPHtCQGPT5ouvSAQ0od8caTO9OOuufZOAE=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2/go.mod h1:cDh1p6XkSGSwSRIArWRc6+UqAQ7x4alQ0QfpVR6f+co=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6JkVzjowOKeOJRHERyy1vh58=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32/go.mod h1:RudqOgadTWdcS3t/erPQo24pcVEoYyqj/kKW5Vya21I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 h1:v+HbZaCGmOwnTTVS86Fleq0vPzOd7tnJGbFhP0stNLs=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9/go.mod h1:Xjqy+Nyj7VDLBtCMkQYOw1QYfAEZCVLrfI0ezve8wd4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26/go.mod h1:vq86l7956VgFr0/FWQ2BWnK07QC3WYsepKzy33qqY5U=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 h1:N94sVhRACtXyVcjXxrwK1SKFIJrA9pOJ5yu2eSHnmls=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9/go.mod h1:hqamLz7g1/4EJP+GH5NBhcUMLjW+gKLQabgyz6/7WAU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.33/go.mod h1:zG2FcwjQarWaqXSCGpgcr3RSjZ6dHGguZSppUL0XR7Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26/go.mod h1:Bd4C/4PkVGubtNe5iMXu5BNnaBi/9t/UsFspPt4ram8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 h1:Nf2sHxjMJR8CSImIVCONRi4g0Su3J+TSTbS7G0pUeMU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9/go.mod h1:idky4TER38YIjr2cADF1/ugFMKvZV7p//pVeV5LZbF0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.27.7/go.mod h1:Jhu94omkrksnqX6Xs4Qo10eA1Fx+2NYKjZMU4GvZLp0=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.36.0 h1:7wh6KdJnej4T7sE/xfnZf5T+GQzp6GfoZi+5r6ZPlW8=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.36.0/go.mod h1:F9El48+5Tf+TkYJB/6M9H7oqXw9Mr9eVetwJ6SUql7g=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.36.1 h1:dc3cH08KcmVkeh762FrB7/10UJydwpGKJU/6lLJ/KxM=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.36.1/go.mod h1:8qqfpG4mug2JLlEyWPSFhEGvJiaZ9iPmMDDMYc5Xtas=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.8/go.mod h1:GNIveDnP+aE3jujyUSH5aZ/rktsTM5EvtKnCqBZawdw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 h1:ldSFWz9tEHAwHNmjx2Cvy1MjP5/L9kNoR0skc6wyOOM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 h1:dGrs+Q/WzhsiUKh82SfTVN66QzyulXuMDTV/G8ZxOac=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.6/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8/go.mod h1:44qFP1g7pfd+U+sQHLPalAPKnyfTZjJsYR4xIwsJy5o=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsYYwrwnd5fIvgEKkfZFNM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 h1:Yf2MIo9x+0tyv76GljxzqA3WtC5mw7NmazD2chwjxE4=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.9/go.mod h1:yyW88BEPXA2fGFyI2KCcZC3dNpiT0CZAHaF+i656/tQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.6 h1:HJeiuZ2fldpd0WqngyMR6KW7ofkXNLyOaHwEIGm39Cs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.6/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
|
||||
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -265,23 +274,22 @@ github.com/bufbuild/connect-go v1.10.0 h1:QAJ3G9A1OYQW2Jbk3DeoJbkCxuKArrvZgDt47m
|
||||
github.com/bufbuild/connect-go v1.10.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8=
|
||||
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
|
||||
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
|
||||
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
|
||||
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
||||
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
||||
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||
github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
|
||||
github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE=
|
||||
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
|
||||
github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@@ -292,41 +300,42 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
||||
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
|
||||
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
|
||||
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
|
||||
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
|
||||
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo=
|
||||
github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA=
|
||||
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
|
||||
github.com/dblohm7/wingoes v0.0.0-20240108175832-49174e152ce1 h1:j3iPNm77Q///owWMlj74CwhP2aU7JGfh6vXXNQ56xPI=
|
||||
github.com/dblohm7/wingoes v0.0.0-20240108175832-49174e152ce1/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
|
||||
github.com/digitalocean/godo v1.41.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
|
||||
github.com/digitalocean/godo v1.107.0 h1:P72IbmGFQvKOvyjVLyT59bmHxilA4E5hWi40rF4zNQc=
|
||||
github.com/digitalocean/godo v1.107.0/go.mod h1:R6EmmWI8CT1+fCtjWY9UCB+L5uufuZH13wk3YhxycCs=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE=
|
||||
github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v23.0.5+incompatible h1:DaxtlTJjFSnLOXVNUBU1+6kXGz2lpDoEAH6QoxaSg8k=
|
||||
github.com/docker/docker v23.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/cli v25.0.2+incompatible h1:6GEdvxwEA451/+Y3GtqIGn/MNjujQazUlxC6uGu8Tog=
|
||||
github.com/docker/cli v25.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v25.0.2+incompatible h1:/OaKeauroa10K4Nqavw4zlhcDq/WBcPMc5DbjOGgozY=
|
||||
github.com/docker/docker v25.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20231002161417-6a283f1aaaf2 h1:S6Dco8FtAhEI/qkg/00H6RdEGC+MCy5GPiQ+xweNRFE=
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20231002161417-6a283f1aaaf2/go.mod h1:8AuBTZBRSFqEYBPYULd+NN474/zZBLP+6WeT5S9xlAc=
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY=
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -338,25 +347,22 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
|
||||
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
|
||||
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc=
|
||||
github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.0.2 h1:YV4Lc5yMQX8ahVW0ENPq6sPhrhdkGukc6fPRYmZ1R6Y=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.0.2/go.mod h1:vld36QpBTfTzLealsHsmQQJK5lSwJt6wiORv+oFX8/I=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1 h1:eGS0WTFRV30r103lU8JNXY27KbviRnqqIDobW3EV3iY=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1/go.mod h1:L7nJ620PFDKei9QOhJzqA8kRCk+E3UbV2f5gv+1ndLc=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
@@ -368,24 +374,16 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
@@ -395,10 +393,6 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4=
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -450,8 +444,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
@@ -482,8 +477,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -493,8 +488,8 @@ github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
@@ -504,12 +499,12 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99
|
||||
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
|
||||
github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
|
||||
github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
|
||||
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
|
||||
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
|
||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -535,10 +530,13 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.0.2/go.mod h1:JBbvW3Hdw77jKl9uJrEDATUZIFM2VFPzRq4RWIhkF4o=
|
||||
github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI=
|
||||
github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
||||
github.com/jackc/puddle/v2 v2.0.0/go.mod h1:itE7ZJY8xnoo0JqJEpSMprN0f+NQkMCuEV/N9j8h0oc=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
@@ -549,20 +547,18 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jsiebens/go-edit v0.1.0 h1:62SSGW8Qc2zoBcJx7gV86ImPHmQzlU/DQhwCOR4uilE=
|
||||
github.com/jsiebens/go-edit v0.1.0/go.mod h1:m/wuWMv8sNhSl+M2qA35gP/K5jX2J7Aa+g16VwyfxrI=
|
||||
github.com/jsimonetti/rtnetlink v1.3.2 h1:dcn0uWkfxycEEyNy0IGfx3GrhQ38LH7odjxAghimsVI=
|
||||
github.com/jsimonetti/rtnetlink v1.3.2/go.mod h1:BBu4jZCpTjP6Gk0/wfrO8qcqymnN3g0hoFqObRmUo6U=
|
||||
github.com/jsiebens/mockoidc v0.1.0-rc2 h1:ifKYhKxfYQMmglcBKkRX9EUuTYmOMnrrOrB6r1zW/gs=
|
||||
github.com/jsiebens/mockoidc v0.1.0-rc2/go.mod h1:ym8B5bmE8RQAyxuC3lzqtNvxXyJomwxZZG5CIJGXCtk=
|
||||
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
|
||||
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
@@ -570,10 +566,11 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.1.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -581,6 +578,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -595,8 +593,6 @@ github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zG
|
||||
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
|
||||
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/libdns/azure v0.3.0 h1:LW04LPmAd25ieFrsd/sd3QCajzaTn1vD78l7hgkHaAw=
|
||||
@@ -614,25 +610,24 @@ github.com/libdns/route53 v1.3.3 h1:16sTxbbRGm0zODz0p0aVHHIyTqtHzEn3j0s4dGzQvNI=
|
||||
github.com/libdns/route53 v1.3.3/go.mod h1:n1Xy55lpfdxMIx4CVWAM16GQac+/OZcnm1xBjMyhZAo=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
|
||||
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
|
||||
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
|
||||
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@@ -642,35 +637,32 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw=
|
||||
github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
||||
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
|
||||
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nleeper/goment v1.4.4 h1:GlMTpxvhueljArSunzYjN9Ri4SOmpn0Vh2hg2z/IIl8=
|
||||
github.com/nleeper/goment v1.4.4/go.mod h1:zDl5bAyDhqxwQKAvkSXMRLOdCowrdZz53ofRJc4VhTo=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
|
||||
github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg=
|
||||
github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||
github.com/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY=
|
||||
github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss=
|
||||
github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8=
|
||||
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
|
||||
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -685,8 +677,9 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
||||
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@@ -723,19 +716,17 @@ github.com/rodaine/table v1.1.0 h1:/fUlCSdjamMY8VifdQRIu3VWZXYLY7QHFkVorS8NTr4=
|
||||
github.com/rodaine/table v1.1.0/go.mod h1:Qu3q5wi1jTQD6B6HsP6szie/S4w1QUQ8pq22pz9iL8g=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ=
|
||||
github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
@@ -746,6 +737,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -759,25 +751,21 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
|
||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk=
|
||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
||||
github.com/tkuchiki/go-timezone v0.2.0/go.mod h1:b1Ean9v2UXtxSq4TZF0i/TU9NuoWa9hOzOKoGCV2zqY=
|
||||
github.com/tkuchiki/go-timezone v0.2.2 h1:MdHR65KwgVTwWFQrota4SKzc4L5EfuH5SdZZGtk/P2Q=
|
||||
github.com/tkuchiki/go-timezone v0.2.2/go.mod h1:oFweWxYl35C/s7HMVZXiA19Jr9Y0qJHMaG/J2TES4LY=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/travisjeffery/certmagic-sqlstorage v1.1.1 h1:xJyKQUnX8yc3vDZJAavBU0j+0XvaLDsPFk06OLwnhPg=
|
||||
github.com/travisjeffery/certmagic-sqlstorage v1.1.1/go.mod h1:bCTLU/lPOaLBZuVdjB3CT+tSQ9DAz77bsR/xxMZUKD4=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
@@ -814,11 +802,26 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
|
||||
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
|
||||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
|
||||
@@ -826,8 +829,8 @@ go4.org/intern v0.0.0-20230525184215-6c62f75575cb h1:ae7kzL5Cfdmcecbh22ll7lYP3iu
|
||||
go4.org/intern v0.0.0-20230525184215-6c62f75575cb/go.mod h1:Ycrt6raEcnF5FTsLiLKkhBTO6DPX3RCUCUVnks3gFJU=
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
|
||||
go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 h1:X66ZEoMN2SuaoI/dfZVYobB6E5zjZyyHUMWlCA7MgGE=
|
||||
go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 h1:lGdhQUN/cnWdSH3291CUuxSEqc+AsGTiDxPP3r2J0l4=
|
||||
@@ -840,9 +843,11 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -853,8 +858,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -916,13 +921,13 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
@@ -932,15 +937,18 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -968,8 +976,8 @@ golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri
|
||||
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
|
||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
|
||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -986,8 +994,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -998,17 +1006,13 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1043,15 +1047,12 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1067,10 +1068,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1079,13 +1082,15 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1159,10 +1164,11 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1221,8 +1227,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ
|
||||
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=
|
||||
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
|
||||
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
|
||||
google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA=
|
||||
google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -1333,12 +1339,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw
|
||||
google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
|
||||
google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
|
||||
google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck=
|
||||
google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg=
|
||||
google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA=
|
||||
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
|
||||
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 h1:EWIeHfGuUf00zrVZGEgYFxok7plSAXBGcH7NNdMAWvA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -1400,6 +1406,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
@@ -1413,16 +1420,11 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8=
|
||||
gorm.io/driver/mysql v1.3.3/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
|
||||
gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
|
||||
gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
|
||||
gorm.io/driver/sqlite v1.3.2 h1:nWTy4cE52K6nnMhv23wLmur9Y3qWbZvOBz+V4PrGAxg=
|
||||
gorm.io/driver/sqlite v1.3.2/go.mod h1:B+8GyC9K7VgzJAcrcXMRPdnMcck+8FgJynEehEPM16U=
|
||||
gorm.io/driver/sqlserver v1.3.2 h1:yYt8f/xdAKLY7lCCyXxIUEgZ/WsURos3dHrx8MKFGAk=
|
||||
gorm.io/driver/sqlserver v1.3.2/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ=
|
||||
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
@@ -1441,20 +1443,20 @@ howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM=
|
||||
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo=
|
||||
modernc.org/libc v1.38.0 h1:o4Lpk0zNDSdsjfEXnF1FGXWQ9PDi1NOdWcLP5n13FGo=
|
||||
modernc.org/libc v1.38.0/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
|
||||
modernc.org/libc v1.40.1 h1:ZhRylEBcj3GyQbPVC8JxIg7SdrT4JOxIDJoUon0NfF8=
|
||||
modernc.org/libc v1.40.1/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
|
||||
modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
|
||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
|
||||
nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=
|
||||
tailscale.com v1.48.0 h1:jpc6Fu/dBddptXw1VJ9Euny8+xB00YV91dSwcfuoxw4=
|
||||
tailscale.com v1.48.0/go.mod h1:RWW4emjviEEAIqr6P6bbZZGXr19BdAdtwtUVfW9SBvU=
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.1 h1:tbT1jjaeFOF230tzOIRJ6U5S1jNqpsSyNjzDd58H3J8=
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.1/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
||||
tailscale.com v1.56.1 h1:V3HBDJai3u7xo22Xlv7ioqKNZQdxOJebLYCNqCXVwZg=
|
||||
tailscale.com v1.56.1/go.mod h1:XQk6fCN8oMJ+qbCmW+2WS/VM3jTA9nIHT6O19t0hZeQ=
|
||||
|
||||
@@ -42,7 +42,7 @@ func (p *OIDCProvider) GetLoginURL(redirectURI, state string) string {
|
||||
ClientSecret: p.clientSecret,
|
||||
RedirectURL: redirectURI,
|
||||
Endpoint: p.provider.Endpoint(),
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
Scopes: p.scopes,
|
||||
}
|
||||
|
||||
return oauth2Config.AuthCodeURL(state, oauth2.ApprovalForce)
|
||||
@@ -54,7 +54,7 @@ func (p *OIDCProvider) Exchange(redirectURI, code string) (*User, error) {
|
||||
ClientSecret: p.clientSecret,
|
||||
RedirectURL: redirectURI,
|
||||
Endpoint: p.provider.Endpoint(),
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
Scopes: p.scopes,
|
||||
}
|
||||
|
||||
oauth2Token, err := oauth2Config.Exchange(context.Background(), code)
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
package bind
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/jsiebens/ionscale/internal/util"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/labstack/echo/v4"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
"tailscale.com/smallzstd"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
type Factory func(c echo.Context) (Binder, error)
|
||||
|
||||
type Binder interface {
|
||||
BindRequest(c echo.Context, v interface{}) error
|
||||
WriteResponse(c echo.Context, code int, v interface{}) error
|
||||
Marshal(compress string, v interface{}) ([]byte, error)
|
||||
Peer() key.MachinePublic
|
||||
}
|
||||
|
||||
func DefaultBinder(machineKey key.MachinePublic) Factory {
|
||||
return func(c echo.Context) (Binder, error) {
|
||||
return &defaultBinder{machineKey: machineKey}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func BoxBinder(controlKey key.MachinePrivate) Factory {
|
||||
return func(c echo.Context) (Binder, error) {
|
||||
idParam := c.Param("id")
|
||||
|
||||
id, err := util.ParseMachinePublicKey(idParam)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &boxBinder{
|
||||
controlKey: controlKey,
|
||||
machineKey: *id,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type defaultBinder struct {
|
||||
machineKey key.MachinePublic
|
||||
}
|
||||
|
||||
func (d *defaultBinder) BindRequest(c echo.Context, v interface{}) error {
|
||||
body, err := ioutil.ReadAll(c.Request().Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(body, v)
|
||||
}
|
||||
|
||||
func (d *defaultBinder) WriteResponse(c echo.Context, code int, v interface{}) error {
|
||||
marshalled, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Response().WriteHeader(code)
|
||||
_, err = c.Response().Write(marshalled)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *defaultBinder) Marshal(compress string, v interface{}) ([]byte, error) {
|
||||
var payload []byte
|
||||
|
||||
marshalled, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if compress == "zstd" {
|
||||
payload = zstdEncode(marshalled)
|
||||
} else {
|
||||
payload = marshalled
|
||||
}
|
||||
|
||||
data := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(data, uint32(len(payload)))
|
||||
data = append(data, payload...)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (d *defaultBinder) Peer() key.MachinePublic {
|
||||
return d.machineKey
|
||||
}
|
||||
|
||||
type boxBinder struct {
|
||||
controlKey key.MachinePrivate
|
||||
machineKey key.MachinePublic
|
||||
}
|
||||
|
||||
func (b *boxBinder) BindRequest(c echo.Context, v interface{}) error {
|
||||
body, err := ioutil.ReadAll(c.Request().Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
decrypted, ok := b.controlKey.OpenFrom(b.machineKey, body)
|
||||
if !ok {
|
||||
return fmt.Errorf("unable to decrypt payload")
|
||||
}
|
||||
|
||||
return json.Unmarshal(decrypted, v)
|
||||
}
|
||||
|
||||
func (b *boxBinder) WriteResponse(c echo.Context, code int, v interface{}) error {
|
||||
marshalled, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encrypted := b.controlKey.SealTo(b.machineKey, marshalled)
|
||||
|
||||
c.Response().WriteHeader(code)
|
||||
_, err = c.Response().Write(encrypted)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *boxBinder) Marshal(compress string, v interface{}) ([]byte, error) {
|
||||
var payload []byte
|
||||
|
||||
marshalled, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if compress == "zstd" {
|
||||
encoded := zstdEncode(marshalled)
|
||||
payload = b.controlKey.SealTo(b.machineKey, encoded)
|
||||
} else {
|
||||
payload = b.controlKey.SealTo(b.machineKey, marshalled)
|
||||
}
|
||||
|
||||
data := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(data, uint32(len(payload)))
|
||||
data = append(data, payload...)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (b *boxBinder) Peer() key.MachinePublic {
|
||||
return b.machineKey
|
||||
}
|
||||
|
||||
func zstdEncode(in []byte) []byte {
|
||||
encoder := zstdEncoderPool.Get().(*zstd.Encoder)
|
||||
out := encoder.EncodeAll(in, nil)
|
||||
encoder.Close()
|
||||
zstdEncoderPool.Put(encoder)
|
||||
return out
|
||||
}
|
||||
|
||||
var zstdEncoderPool = &sync.Pool{
|
||||
New: func() any {
|
||||
encoder, err := smallzstd.NewEncoder(nil, zstd.WithEncoderLevel(zstd.SpeedFastest))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return encoder
|
||||
},
|
||||
}
|
||||
+22
-68
@@ -2,45 +2,25 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
"github.com/jsiebens/go-edit/editor"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
"github.com/spf13/cobra"
|
||||
"io/ioutil"
|
||||
"github.com/tailscale/hujson"
|
||||
"os"
|
||||
)
|
||||
|
||||
func getACLConfigCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "get-acl-policy",
|
||||
Short: "Get the ACL policy",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.GetACLPolicy(context.Background(), connect.NewRequest(&api.GetACLPolicyRequest{TailnetId: tailnet.Id}))
|
||||
resp, err := tc.Client().GetACLPolicy(cmd.Context(), connect.NewRequest(&api.GetACLPolicyRequest{TailnetId: tc.TailnetID()}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -59,35 +39,16 @@ func getACLConfigCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func editACLConfigCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "edit-acl-policy",
|
||||
Short: "Edit the ACL policy",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
edit := editor.NewDefaultEditor([]string{"IONSCALE_EDITOR", "EDITOR"})
|
||||
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.GetACLPolicy(context.Background(), connect.NewRequest(&api.GetACLPolicyRequest{TailnetId: tailnet.Id}))
|
||||
resp, err := tc.Client().GetACLPolicy(cmd.Context(), connect.NewRequest(&api.GetACLPolicyRequest{TailnetId: tc.TailnetID()}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -104,12 +65,17 @@ func editACLConfigCommand() *cobra.Command {
|
||||
|
||||
defer os.Remove(s)
|
||||
|
||||
next, err = hujson.Standardize(next)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var policy = &api.ACLPolicy{}
|
||||
if err := json.Unmarshal(next, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.SetACLPolicy(context.Background(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tailnet.Id, Policy: policy}))
|
||||
_, err = tc.Client().SetACLPolicy(cmd.Context(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -123,25 +89,23 @@ func editACLConfigCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func setACLConfigCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "set-acl-policy",
|
||||
Short: "Set ACL policy",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var file string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
command.Flags().StringVar(&file, "file", "", "Path to json file with the acl configuration")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
rawJson, err := ioutil.ReadFile(file)
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawJson, err := hujson.Standardize(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -151,17 +115,7 @@ func setACLConfigCommand() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.SetACLPolicy(context.Background(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tailnet.Id, Policy: policy}))
|
||||
_, err = tc.Client().SetACLPolicy(cmd.Context(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+11
-16
@@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
"github.com/jsiebens/ionscale/pkg/client/ionscale"
|
||||
@@ -20,24 +19,14 @@ func authCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func authLoginCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "login",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := &api.AuthenticateRequest{}
|
||||
stream, err := client.Authenticate(context.Background(), connect.NewRequest(req))
|
||||
stream, err := tc.Client().Authenticate(cmd.Context(), connect.NewRequest(req))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -48,7 +37,13 @@ func authLoginCommand() *cobra.Command {
|
||||
if len(resp.Token) != 0 {
|
||||
fmt.Println()
|
||||
fmt.Println("Success.")
|
||||
if err := ionscale.SessionToFile(resp.Token, resp.TailnetId); err != nil {
|
||||
|
||||
tailnetId := uint64(0)
|
||||
if resp.TailnetId != nil {
|
||||
tailnetId = *resp.TailnetId
|
||||
}
|
||||
|
||||
if err := ionscale.StoreAuthToken(tc.Addr(), resp.Token, tailnetId); err != nil {
|
||||
fmt.Println()
|
||||
fmt.Println("Your api token:")
|
||||
fmt.Println()
|
||||
|
||||
+15
-58
@@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
@@ -28,40 +27,23 @@ func authkeysCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func createAuthkeysCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Creates a new auth key in the specified tailnet",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var ephemeral bool
|
||||
var preAuthorized bool
|
||||
var tags []string
|
||||
var expiry string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
command.Flags().BoolVar(&ephemeral, "ephemeral", false, "When enabled, machines authenticated by this key will be automatically removed after going offline.")
|
||||
command.Flags().StringSliceVar(&tags, "tag", []string{}, "Machines authenticated by this key will be automatically tagged with these tags")
|
||||
command.Flags().StringVar(&expiry, "expiry", "180d", "Human-readable expiration of the key")
|
||||
command.Flags().BoolVar(&preAuthorized, "pre-authorized", false, "Generate an auth key which is pre-authorized.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
var expiryDur *durationpb.Duration
|
||||
|
||||
if expiry != "" && expiry != "none" {
|
||||
@@ -73,13 +55,13 @@ func createAuthkeysCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
req := &api.CreateAuthKeyRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
Ephemeral: ephemeral,
|
||||
PreAuthorized: preAuthorized,
|
||||
Tags: tags,
|
||||
Expiry: expiryDur,
|
||||
}
|
||||
resp, err := client.CreateAuthKey(context.Background(), connect.NewRequest(req))
|
||||
resp, err := tc.Client().CreateAuthKey(cmd.Context(), connect.NewRequest(req))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -99,25 +81,19 @@ func createAuthkeysCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func deleteAuthKeyCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete a specified auth key",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var authKeyId uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().Uint64Var(&authKeyId, "id", 0, "Auth Key ID")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
grpcClient, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.DeleteAuthKeyRequest{AuthKeyId: authKeyId}
|
||||
if _, err := grpcClient.DeleteAuthKey(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().DeleteAuthKey(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -130,34 +106,15 @@ func deleteAuthKeyCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func listAuthkeysCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List all auth keys for a given tailnet",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := &api.ListAuthKeysRequest{TailnetId: tailnet.Id}
|
||||
resp, err := client.ListAuthKeys(context.Background(), connect.NewRequest(req))
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := &api.ListAuthKeysRequest{TailnetId: tc.TailnetID()}
|
||||
resp, err := tc.Client().ListAuthKeys(cmd.Context(), connect.NewRequest(req))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -67,7 +67,6 @@ func configureCommand() *cobra.Command {
|
||||
if acme {
|
||||
c.Tls.AcmeEnabled = true
|
||||
c.Tls.AcmeEmail = email
|
||||
c.Tls.AcmePath = filepath.Join(dataDir, "acme")
|
||||
} else {
|
||||
c.Tls.CertFile = certFile
|
||||
c.Tls.KeyFile = keyFile
|
||||
|
||||
+16
-32
@@ -1,12 +1,12 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tailscale/hujson"
|
||||
"gopkg.in/yaml.v2"
|
||||
"os"
|
||||
"tailscale.com/tailcfg"
|
||||
@@ -26,25 +26,18 @@ func systemCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func getDefaultDERPMap() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "get-derp-map",
|
||||
Short: "Get the DERP Map configuration",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var asJson bool
|
||||
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
command.Flags().BoolVar(&asJson, "json", false, "When enabled, render output as json otherwise yaml")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.GetDefaultDERPMap(context.Background(), connect.NewRequest(&api.GetDefaultDERPMapRequest{}))
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
resp, err := tc.Client().GetDefaultDERPMap(cmd.Context(), connect.NewRequest(&api.GetDefaultDERPMapRequest{}))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -81,29 +74,28 @@ func getDefaultDERPMap() *cobra.Command {
|
||||
}
|
||||
|
||||
func setDefaultDERPMap() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "set-derp-map",
|
||||
Short: "Set the DERP Map configuration",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var file string
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().StringVar(&file, "file", "", "Path to json file with the DERP Map configuration")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
grpcClient, err := target.createGRPCClient()
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawJson, err := os.ReadFile(file)
|
||||
rawJson, err := hujson.Standardize(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := grpcClient.SetDefaultDERPMap(context.Background(), connect.NewRequest(&api.SetDefaultDERPMapRequest{Value: rawJson}))
|
||||
resp, err := tc.Client().SetDefaultDERPMap(cmd.Context(), connect.NewRequest(&api.SetDefaultDERPMapRequest{Value: rawJson}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -122,22 +114,14 @@ func setDefaultDERPMap() *cobra.Command {
|
||||
}
|
||||
|
||||
func resetDefaultDERPMap() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "reset-derp-map",
|
||||
Short: "Reset the DERP Map to the default configuration",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
grpcClient, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := grpcClient.ResetDefaultDERPMap(context.Background(), connect.NewRequest(&api.ResetDefaultDERPMapRequest{})); err != nil {
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
if _, err := tc.Client().ResetDefaultDERPMap(cmd.Context(), connect.NewRequest(&api.ResetDefaultDERPMapRequest{})); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+55
-105
@@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
@@ -12,69 +11,22 @@ import (
|
||||
)
|
||||
|
||||
func getDNSConfigCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "get-dns",
|
||||
Short: "Get DNS configuration",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := api.GetDNSConfigRequest{TailnetId: tailnet.Id}
|
||||
resp, err := client.GetDNSConfig(context.Background(), connect.NewRequest(&req))
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.GetDNSConfigRequest{TailnetId: tc.TailnetID()}
|
||||
resp, err := tc.Client().GetDNSConfig(cmd.Context(), connect.NewRequest(&req))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config := resp.Msg.Config
|
||||
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 8, 8, 1, '\t', 0)
|
||||
defer w.Flush()
|
||||
|
||||
fmt.Fprintf(w, "%s\t\t%v\n", "MagicDNS", config.MagicDns)
|
||||
fmt.Fprintf(w, "%s\t\t%v\n", "HTTPS Certs", config.HttpsCerts)
|
||||
fmt.Fprintf(w, "%s\t\t%v\n", "Override Local DNS", config.OverrideLocalDns)
|
||||
|
||||
if config.MagicDns {
|
||||
fmt.Fprintf(w, "MagicDNS\t%s\t%s\n", config.MagicDnsSuffix, "100.100.100.100")
|
||||
}
|
||||
|
||||
for k, r := range config.Routes {
|
||||
for i, t := range r.Routes {
|
||||
if i == 0 {
|
||||
fmt.Fprintf(w, "SplitDNS\t%s\t%s\n", k, t)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\n", "", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range config.Nameservers {
|
||||
if i == 0 {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", "Global", "", t)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", "", "", t)
|
||||
}
|
||||
}
|
||||
printDnsConfig(config)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -83,40 +35,25 @@ func getDNSConfigCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func setDNSConfigCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "set-dns",
|
||||
Short: "Set DNS config",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var nameservers []string
|
||||
var magicDNS bool
|
||||
var httpsCerts bool
|
||||
var overrideLocalDNS bool
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
var searchDomains []string
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
command.Flags().StringSliceVarP(&nameservers, "nameserver", "", []string{}, "Machines on your network will use these nameservers to resolve DNS queries.")
|
||||
command.Flags().BoolVarP(&magicDNS, "magic-dns", "", false, "Enable MagicDNS for the specified Tailnet")
|
||||
command.Flags().BoolVarP(&httpsCerts, "https-certs", "", false, "Enable HTTPS Certificates for the specified Tailnet")
|
||||
command.Flags().BoolVarP(&overrideLocalDNS, "override-local-dns", "", false, "When enabled, connected clients ignore local DNS settings and always use the nameservers specified for this Tailnet")
|
||||
command.Flags().StringSliceVarP(&searchDomains, "search-domain", "", []string{}, "Custom DNS search domains.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
var globalNameservers []string
|
||||
var routes = make(map[string]*api.Routes)
|
||||
|
||||
@@ -135,16 +72,17 @@ func setDNSConfigCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
req := api.SetDNSConfigRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
Config: &api.DNSConfig{
|
||||
MagicDns: magicDNS,
|
||||
OverrideLocalDns: overrideLocalDNS,
|
||||
Nameservers: globalNameservers,
|
||||
Routes: routes,
|
||||
HttpsCerts: httpsCerts,
|
||||
SearchDomains: searchDomains,
|
||||
},
|
||||
}
|
||||
resp, err := client.SetDNSConfig(context.Background(), connect.NewRequest(&req))
|
||||
resp, err := tc.Client().SetDNSConfig(cmd.Context(), connect.NewRequest(&req))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -157,38 +95,50 @@ func setDNSConfigCommand() *cobra.Command {
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 8, 8, 1, '\t', 0)
|
||||
defer w.Flush()
|
||||
|
||||
fmt.Fprintf(w, "%s\t\t%v\n", "MagicDNS", config.MagicDns)
|
||||
fmt.Fprintf(w, "%s\t\t%v\n", "HTTPS Certs", config.HttpsCerts)
|
||||
fmt.Fprintf(w, "%s\t\t%v\n", "Override Local DNS", config.OverrideLocalDns)
|
||||
|
||||
if config.MagicDns {
|
||||
fmt.Fprintf(w, "MagicDNS\t%s\t%s\n", config.MagicDnsSuffix, "100.100.100.100")
|
||||
}
|
||||
|
||||
for k, r := range config.Routes {
|
||||
for i, t := range r.Routes {
|
||||
if i == 0 {
|
||||
fmt.Fprintf(w, "SplitDNS\t%s\t%s\n", k, t)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\n", "", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range config.Nameservers {
|
||||
if i == 0 {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", "Global", "", t)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", "", "", t)
|
||||
}
|
||||
}
|
||||
printDnsConfig(config)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
func printDnsConfig(config *api.DNSConfig) {
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 8, 8, 1, '\t', 0)
|
||||
defer w.Flush()
|
||||
|
||||
fmt.Fprintf(w, "%s\t\t%v\n", "MagicDNS", config.MagicDns)
|
||||
fmt.Fprintf(w, "%s\t\t%v\n", "HTTPS Certs", config.HttpsCerts)
|
||||
fmt.Fprintf(w, "%s\t\t%v\n", "Override Local DNS", config.OverrideLocalDns)
|
||||
|
||||
if config.MagicDns {
|
||||
fmt.Fprintf(w, "MagicDNS\t%s\t%s\n", config.MagicDnsSuffix, "100.100.100.100")
|
||||
}
|
||||
|
||||
for k, r := range config.Routes {
|
||||
for i, t := range r.Routes {
|
||||
if i == 0 {
|
||||
fmt.Fprintf(w, "SplitDNS\t%s\t%s\n", k, t)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\n", "", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range config.Nameservers {
|
||||
if i == 0 {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", "Global", "", t)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", "", "", t)
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range config.SearchDomains {
|
||||
if i == 0 {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", "Search Domains", t, "")
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", "", t, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
"github.com/jsiebens/ionscale/pkg/client/ionscale"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
apiconnect "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1/ionscalev1connect"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func checkRequiredTailnetAndTailnetIdFlags(cmd *cobra.Command, args []string) error {
|
||||
savedTailnetID, err := ionscale.TailnetFromFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if savedTailnetID == 0 && !cmd.Flags().Changed("tailnet") && !cmd.Flags().Changed("tailnet-id") {
|
||||
return fmt.Errorf("flag --tailnet or --tailnet-id is required")
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("tailnet") && cmd.Flags().Changed("tailnet-id") {
|
||||
return fmt.Errorf("flags --tailnet and --tailnet-id are mutually exclusive")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findTailnet(client apiconnect.IonscaleServiceClient, tailnet string, tailnetID uint64) (*api.Tailnet, error) {
|
||||
savedTailnetID, err := ionscale.TailnetFromFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if savedTailnetID == 0 && tailnetID == 0 && tailnet == "" {
|
||||
return nil, fmt.Errorf("requested tailnet not found or you are not authorized for this tailnet")
|
||||
}
|
||||
|
||||
tailnets, err := client.ListTailnets(context.Background(), connect.NewRequest(&api.ListTailnetsRequest{}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, t := range tailnets.Msg.Tailnet {
|
||||
if t.Id == savedTailnetID || t.Id == tailnetID || t.Name == tailnet {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("requested tailnet not found or you are not authorized for this tailnet")
|
||||
}
|
||||
+22
-68
@@ -2,45 +2,25 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
"github.com/jsiebens/go-edit/editor"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
"github.com/spf13/cobra"
|
||||
"io/ioutil"
|
||||
"github.com/tailscale/hujson"
|
||||
"os"
|
||||
)
|
||||
|
||||
func getIAMPolicyCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "get-iam-policy",
|
||||
Short: "Get the IAM policy",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.GetIAMPolicy(context.Background(), connect.NewRequest(&api.GetIAMPolicyRequest{TailnetId: tailnet.Id}))
|
||||
resp, err := tc.Client().GetIAMPolicy(cmd.Context(), connect.NewRequest(&api.GetIAMPolicyRequest{TailnetId: tc.TailnetID()}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -59,35 +39,16 @@ func getIAMPolicyCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func editIAMPolicyCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "edit-iam-policy",
|
||||
Short: "Edit the IAM policy",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
edit := editor.NewDefaultEditor([]string{"IONSCALE_EDITOR", "EDITOR"})
|
||||
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.GetIAMPolicy(context.Background(), connect.NewRequest(&api.GetIAMPolicyRequest{TailnetId: tailnet.Id}))
|
||||
resp, err := tc.Client().GetIAMPolicy(cmd.Context(), connect.NewRequest(&api.GetIAMPolicyRequest{TailnetId: tc.TailnetID()}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -102,6 +63,11 @@ func editIAMPolicyCommand() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
next, err = hujson.Standardize(next)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer os.Remove(s)
|
||||
|
||||
var policy = &api.IAMPolicy{}
|
||||
@@ -109,7 +75,7 @@ func editIAMPolicyCommand() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.SetIAMPolicy(context.Background(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tailnet.Id, Policy: policy}))
|
||||
_, err = tc.Client().SetIAMPolicy(cmd.Context(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -123,25 +89,23 @@ func editIAMPolicyCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func setIAMPolicyCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "set-iam-policy",
|
||||
Short: "Set IAM policy",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var file string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
command.Flags().StringVar(&file, "file", "", "Path to json file with the acl configuration")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
rawJson, err := ioutil.ReadFile(file)
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawJson, err := hujson.Standardize(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -151,17 +115,7 @@ func setIAMPolicyCommand() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.SetIAMPolicy(context.Background(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tailnet.Id, Policy: policy}))
|
||||
_, err = tc.Client().SetIAMPolicy(cmd.Context(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+52
-136
@@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
@@ -17,7 +16,7 @@ import (
|
||||
func machineCommands() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: "machines",
|
||||
Aliases: []string{"machine"},
|
||||
Aliases: []string{"machine", "devices", "device"},
|
||||
Short: "Manage ionscale machines",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
@@ -39,27 +38,20 @@ func machineCommands() *cobra.Command {
|
||||
}
|
||||
|
||||
func getMachineCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "Retrieve detailed information for a machine",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var machineID uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.GetMachineRequest{MachineId: machineID}
|
||||
resp, err := client.GetMachine(context.Background(), connect.NewRequest(&req))
|
||||
resp, err := tc.Client().GetMachine(cmd.Context(), connect.NewRequest(&req))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -151,27 +143,20 @@ func getMachineCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func deleteMachineCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Deletes a machine",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var machineID uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.DeleteMachineRequest{MachineId: machineID}
|
||||
if _, err := client.DeleteMachine(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().DeleteMachine(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -184,27 +169,20 @@ func deleteMachineCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func expireMachineCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "expire",
|
||||
Short: "Expires a machine",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var machineID uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.ExpireMachineRequest{MachineId: machineID}
|
||||
if _, err := client.ExpireMachine(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().ExpireMachine(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -217,27 +195,20 @@ func expireMachineCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func authorizeMachineCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "authorize",
|
||||
Short: "Authorizes a machine",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var machineID uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.AuthorizeMachineRequest{MachineId: machineID}
|
||||
if _, err := client.AuthorizeMachine(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().AuthorizeMachine(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -250,34 +221,15 @@ func authorizeMachineCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func listMachinesCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List machines",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := api.ListMachinesRequest{TailnetId: tailnet.Id}
|
||||
resp, err := client.ListMachines(context.Background(), connect.NewRequest(&req))
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.ListMachinesRequest{TailnetId: tc.TailnetID()}
|
||||
resp, err := tc.Client().ListMachines(cmd.Context(), connect.NewRequest(&req))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -305,27 +257,20 @@ func listMachinesCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func getMachineRoutesCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "get-routes",
|
||||
Short: "Show routes advertised and enabled by a given machine",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var machineID uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
grpcClient, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.GetMachineRoutesRequest{MachineId: machineID}
|
||||
resp, err := grpcClient.GetMachineRoutes(context.Background(), connect.NewRequest(&req))
|
||||
resp, err := tc.Client().GetMachineRoutes(cmd.Context(), connect.NewRequest(&req))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -339,29 +284,23 @@ func getMachineRoutesCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func enableMachineRoutesCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "enable-routes",
|
||||
Short: "Enable routes for a given machine",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var machineID uint64
|
||||
var routes []string
|
||||
var replace bool
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID")
|
||||
command.Flags().StringSliceVar(&routes, "routes", []string{}, "List of routes to enable")
|
||||
command.Flags().BoolVar(&replace, "replace", false, "Replace current enabled routes with this new list")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
for _, r := range routes {
|
||||
if _, err := netaddr.ParseIPPrefix(r); err != nil {
|
||||
return err
|
||||
@@ -369,7 +308,7 @@ func enableMachineRoutesCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
req := api.EnableMachineRoutesRequest{MachineId: machineID, Routes: routes, Replace: replace}
|
||||
resp, err := client.EnableMachineRoutes(context.Background(), connect.NewRequest(&req))
|
||||
resp, err := tc.Client().EnableMachineRoutes(cmd.Context(), connect.NewRequest(&req))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -383,27 +322,21 @@ func enableMachineRoutesCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func disableMachineRoutesCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "disable-routes",
|
||||
Short: "Disable routes for a given machine",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var machineID uint64
|
||||
var routes []string
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID")
|
||||
command.Flags().StringSliceVar(&routes, "routes", []string{}, "List of routes to enable")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
for _, r := range routes {
|
||||
if _, err := netaddr.ParseIPPrefix(r); err != nil {
|
||||
return err
|
||||
@@ -411,7 +344,7 @@ func disableMachineRoutesCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
req := api.DisableMachineRoutesRequest{MachineId: machineID, Routes: routes}
|
||||
resp, err := client.DisableMachineRoutes(context.Background(), connect.NewRequest(&req))
|
||||
resp, err := tc.Client().DisableMachineRoutes(cmd.Context(), connect.NewRequest(&req))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -425,27 +358,20 @@ func disableMachineRoutesCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func enableExitNodeCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "enable-exit-node",
|
||||
Short: "Enable given machine as an exit node",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var machineID uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.EnableExitNodeRequest{MachineId: machineID}
|
||||
resp, err := client.EnableExitNode(context.Background(), connect.NewRequest(&req))
|
||||
resp, err := tc.Client().EnableExitNode(cmd.Context(), connect.NewRequest(&req))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -459,27 +385,21 @@ func enableExitNodeCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func disableExitNodeCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "disable-exit-node",
|
||||
Short: "Disable given machine as an exit node",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var machineID uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.DisableExitNodeRequest{MachineId: machineID}
|
||||
resp, err := client.DisableExitNode(context.Background(), connect.NewRequest(&req))
|
||||
resp, err := tc.Client().DisableExitNode(cmd.Context(), connect.NewRequest(&req))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -512,22 +432,18 @@ func disableMachineKeyExpiryCommand() *cobra.Command {
|
||||
return configureSetMachineKeyExpiryCommand(command, true)
|
||||
}
|
||||
|
||||
func configureSetMachineKeyExpiryCommand(command *cobra.Command, v bool) *cobra.Command {
|
||||
func configureSetMachineKeyExpiryCommand(cmdTmpl *cobra.Command, disable bool) *cobra.Command {
|
||||
command, tc := prepareCommand(false, cmdTmpl)
|
||||
|
||||
var machineID uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID")
|
||||
|
||||
_ = command.MarkFlagRequired("machine-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := api.SetMachineKeyExpiryRequest{MachineId: machineID, Disabled: v}
|
||||
_, err = client.SetMachineKeyExpiry(context.Background(), connect.NewRequest(&req))
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.SetMachineKeyExpiryRequest{MachineId: machineID, Disabled: disable}
|
||||
_, err := tc.Client().SetMachineKeyExpiry(cmd.Context(), connect.NewRequest(&req))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@ import (
|
||||
|
||||
func serverCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Start an ionscale server",
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
Use: "server",
|
||||
Short: "Start an ionscale server",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var configFile string
|
||||
@@ -25,7 +24,7 @@ func serverCommand() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
return server.Start(c)
|
||||
return server.Start(command.Context(), c)
|
||||
}
|
||||
|
||||
return command
|
||||
|
||||
+64
-306
@@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
@@ -50,23 +49,14 @@ func tailnetCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func listTailnetsCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List available Tailnets",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.ListTailnets(context.Background(), connect.NewRequest(&api.ListTailnetsRequest{}))
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
resp, err := tc.Client().ListTailnets(cmd.Context(), connect.NewRequest(&api.ListTailnetsRequest{}))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -85,17 +75,15 @@ func listTailnetsCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func createTailnetsCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create a new Tailnet",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var name string
|
||||
var domain string
|
||||
var email string
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().StringVarP(&name, "name", "n", "", "")
|
||||
command.Flags().StringVar(&domain, "domain", "", "")
|
||||
@@ -111,7 +99,7 @@ func createTailnetsCommand() *cobra.Command {
|
||||
return nil
|
||||
}
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
dnsConfig := defaults.DefaultDNSConfig()
|
||||
aclPolicy := defaults.DefaultACLPolicy()
|
||||
@@ -134,12 +122,7 @@ func createTailnetsCommand() *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.CreateTailnet(context.Background(), connect.NewRequest(&api.CreateTailnetRequest{
|
||||
resp, err := tc.Client().CreateTailnet(cmd.Context(), connect.NewRequest(&api.CreateTailnetRequest{
|
||||
Name: name,
|
||||
IamPolicy: iamPolicy,
|
||||
AclPolicy: aclPolicy,
|
||||
@@ -161,37 +144,18 @@ func createTailnetsCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func deleteTailnetCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete a tailnet",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var force bool
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
command.Flags().BoolVar(&force, "force", false, "When enabled, force delete the specified Tailnet even when machines are still available.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.DeleteTailnet(context.Background(), connect.NewRequest(&api.DeleteTailnetRequest{TailnetId: tailnet.Id, Force: force}))
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
_, err := tc.Client().DeleteTailnet(cmd.Context(), connect.NewRequest(&api.DeleteTailnetRequest{TailnetId: tc.TailnetID(), Force: force}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -205,36 +169,18 @@ func deleteTailnetCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func getDERPMap() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "get-derp-map",
|
||||
Short: "Get the DERP Map configuration",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var asJson bool
|
||||
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
command.Flags().BoolVar(&asJson, "json", false, "When enabled, render output as json otherwise yaml")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.GetDERPMap(context.Background(), connect.NewRequest(&api.GetDERPMapRequest{TailnetId: tailnet.Id}))
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
resp, err := tc.Client().GetDERPMap(cmd.Context(), connect.NewRequest(&api.GetDERPMapRequest{TailnetId: tc.TailnetID()}))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -271,40 +217,23 @@ func getDERPMap() *cobra.Command {
|
||||
}
|
||||
|
||||
func setDERPMap() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "set-derp-map",
|
||||
Short: "Set the DERP Map configuration",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var file string
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
command.Flags().StringVar(&file, "file", "", "Path to json file with the DERP Map configuration")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
rawJson, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.SetDERPMap(context.Background(), connect.NewRequest(&api.SetDERPMapRequest{TailnetId: tailnet.Id, Value: rawJson}))
|
||||
resp, err := tc.Client().SetDERPMap(cmd.Context(), connect.NewRequest(&api.SetDERPMapRequest{TailnetId: tc.TailnetID(), Value: rawJson}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -323,33 +252,14 @@ func setDERPMap() *cobra.Command {
|
||||
}
|
||||
|
||||
func resetDERPMap() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "reset-derp-map",
|
||||
Short: "Reset the DERP Map to the default configuration",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := client.ResetDERPMap(context.Background(), connect.NewRequest(&api.ResetDERPMapRequest{TailnetId: tailnet.Id})); err != nil {
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
if _, err := tc.Client().ResetDERPMap(cmd.Context(), connect.NewRequest(&api.ResetDERPMapRequest{TailnetId: tc.TailnetID()})); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -362,38 +272,19 @@ func resetDERPMap() *cobra.Command {
|
||||
}
|
||||
|
||||
func enableFileSharingCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "enable-file-sharing",
|
||||
Aliases: []string{"enable-taildrop"},
|
||||
Short: "Enable Taildrop, the file sharing feature",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.EnableFileSharingRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
}
|
||||
|
||||
if _, err := client.EnableFileSharing(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().EnableFileSharing(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -404,38 +295,19 @@ func enableFileSharingCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func disableFileSharingCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "disable-file-sharing",
|
||||
Aliases: []string{"disable-taildrop"},
|
||||
Short: "Disable Taildrop, the file sharing feature",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.DisableFileSharingRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
}
|
||||
|
||||
if _, err := client.DisableFileSharing(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().DisableFileSharing(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -446,37 +318,18 @@ func disableFileSharingCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func enableServiceCollectionCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "enable-service-collection",
|
||||
Short: "Enable monitoring live services running on your network’s machines.",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.EnableServiceCollectionRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
}
|
||||
|
||||
if _, err := client.EnableServiceCollection(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().EnableServiceCollection(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -487,37 +340,18 @@ func enableServiceCollectionCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func disableServiceCollectionCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "disable-service-collection",
|
||||
Short: "Disable monitoring live services running on your network’s machines.",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.DisableServiceCollectionRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
}
|
||||
|
||||
if _, err := client.DisableServiceCollection(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().DisableServiceCollection(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -528,37 +362,18 @@ func disableServiceCollectionCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func enableSSHCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "enable-ssh",
|
||||
Short: "Enable ssh access using tailnet and ACLs.",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.EnableSSHRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
}
|
||||
|
||||
if _, err := client.EnableSSH(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().EnableSSH(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -569,37 +384,18 @@ func enableSSHCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func disableSSHCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "disable-ssh",
|
||||
Short: "Disable ssh access using tailnet and ACLs.",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.DisableSSHRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
}
|
||||
|
||||
if _, err := client.DisableSSH(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().DisableSSH(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -610,37 +406,18 @@ func disableSSHCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func enableMachineAuthorizationCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "enable-machine-authorization",
|
||||
Short: "Enable machine authorization.",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.EnableMachineAuthorizationRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
}
|
||||
|
||||
if _, err := client.EnableMachineAuthorization(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().EnableMachineAuthorization(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -651,37 +428,18 @@ func enableMachineAuthorizationCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func disableMachineAuthorizationCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "disable-machine-authorization",
|
||||
Short: "Disable machine authorization.",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
var target = Target{}
|
||||
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.DisableMachineAuthorizationRequest{
|
||||
TailnetId: tailnet.Id,
|
||||
TailnetId: tc.TailnetID(),
|
||||
}
|
||||
|
||||
if _, err := client.DisableMachineAuthorization(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().DisableMachineAuthorization(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+91
-16
@@ -1,60 +1,135 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
"github.com/jsiebens/ionscale/internal/config"
|
||||
"github.com/jsiebens/ionscale/pkg/client/ionscale"
|
||||
ionscalev1 "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1/ionscalev1connect"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
ionscaleSystemAdminKey = "IONSCALE_SYSTEM_ADMIN_KEY"
|
||||
ionscaleKeysSystemAdminKey = "IONSCALE_KEYS_SYSTEM_ADMIN_KEY"
|
||||
ionscaleAddr = "IONSCALE_ADDR"
|
||||
ionscaleInsecureSkipVerify = "IONSCALE_SKIP_VERIFY"
|
||||
)
|
||||
|
||||
type Target struct {
|
||||
type TargetContext interface {
|
||||
Client() api.IonscaleServiceClient
|
||||
Addr() string
|
||||
TailnetID() uint64
|
||||
}
|
||||
|
||||
type target struct {
|
||||
addr string
|
||||
insecureSkipVerify bool
|
||||
systemAdminKey string
|
||||
|
||||
tailnetID uint64
|
||||
tailnetName string
|
||||
|
||||
client api.IonscaleServiceClient
|
||||
tailnet *ionscalev1.Tailnet
|
||||
}
|
||||
|
||||
func (t *Target) prepareCommand(cmd *cobra.Command) {
|
||||
func prepareCommand(enableTailnetSelector bool, cmd *cobra.Command) (*cobra.Command, TargetContext) {
|
||||
t := &target{}
|
||||
|
||||
cmd.Flags().StringVar(&t.addr, "addr", "", "Addr of the ionscale server, as a complete URL")
|
||||
cmd.Flags().BoolVar(&t.insecureSkipVerify, "tls-skip-verify", false, "Disable verification of TLS certificates")
|
||||
cmd.Flags().StringVar(&t.systemAdminKey, "system-admin-key", "", "If specified, the given value will be used as the key to generate a Bearer token for the call. This can also be specified via the IONSCALE_ADMIN_KEY environment variable.")
|
||||
}
|
||||
|
||||
func (t *Target) createGRPCClient() (api.IonscaleServiceClient, error) {
|
||||
addr := t.getAddr()
|
||||
skipVerify := t.getInsecureSkipVerify()
|
||||
systemAdminKey := t.getSystemAdminKey()
|
||||
|
||||
auth, err := ionscale.LoadClientAuth(systemAdminKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if enableTailnetSelector {
|
||||
cmd.Flags().StringVar(&t.tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
cmd.Flags().Uint64Var(&t.tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
}
|
||||
|
||||
return ionscale.NewClient(auth, addr, skipVerify)
|
||||
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
addr := t.getAddr()
|
||||
skipVerify := t.getInsecureSkipVerify()
|
||||
systemAdminKey := t.getSystemAdminKey()
|
||||
|
||||
auth, err := ionscale.LoadClientAuth(addr, systemAdminKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := ionscale.NewClient(auth, addr, skipVerify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.client = client
|
||||
|
||||
if enableTailnetSelector {
|
||||
savedTailnetID := auth.TailnetID()
|
||||
|
||||
if savedTailnetID == 0 && !cmd.Flags().Changed("tailnet") && !cmd.Flags().Changed("tailnet-id") {
|
||||
return fmt.Errorf("flag --tailnet or --tailnet-id is required")
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("tailnet") && cmd.Flags().Changed("tailnet-id") {
|
||||
return fmt.Errorf("flags --tailnet and --tailnet-id are mutually exclusive")
|
||||
}
|
||||
|
||||
tailnets, err := t.client.ListTailnets(cmd.Context(), connect.NewRequest(&ionscalev1.ListTailnetsRequest{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, tailnet := range tailnets.Msg.Tailnet {
|
||||
if tailnet.Id == savedTailnetID || tailnet.Id == t.tailnetID || tailnet.Name == t.tailnetName {
|
||||
t.tailnet = tailnet
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t.tailnet == nil {
|
||||
return fmt.Errorf("requested tailnet not found or you are not authorized for this tailnet")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd, t
|
||||
}
|
||||
|
||||
func (t *Target) getAddr() string {
|
||||
func (t *target) getAddr() string {
|
||||
if len(t.addr) != 0 {
|
||||
return t.addr
|
||||
}
|
||||
return config.GetString(ionscaleAddr, "https://localhost:8443")
|
||||
}
|
||||
|
||||
func (t *Target) getInsecureSkipVerify() bool {
|
||||
func (t *target) getInsecureSkipVerify() bool {
|
||||
if t.insecureSkipVerify {
|
||||
return true
|
||||
}
|
||||
return config.GetBool(ionscaleInsecureSkipVerify, false)
|
||||
}
|
||||
|
||||
func (t *Target) getSystemAdminKey() string {
|
||||
func (t *target) getSystemAdminKey() string {
|
||||
if len(t.systemAdminKey) != 0 {
|
||||
return t.systemAdminKey
|
||||
}
|
||||
return config.GetString(ionscaleSystemAdminKey, "")
|
||||
return config.GetString(ionscaleSystemAdminKey, config.GetString(ionscaleKeysSystemAdminKey, ""))
|
||||
}
|
||||
|
||||
func (t *target) Addr() string {
|
||||
return t.getAddr()
|
||||
}
|
||||
|
||||
func (t *target) Client() api.IonscaleServiceClient {
|
||||
return t.client
|
||||
}
|
||||
|
||||
func (t *target) TailnetID() uint64 {
|
||||
if t.tailnet == nil {
|
||||
return 0
|
||||
}
|
||||
return t.tailnet.Id
|
||||
}
|
||||
|
||||
+10
-36
@@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
@@ -24,34 +23,15 @@ func userCommands() *cobra.Command {
|
||||
}
|
||||
|
||||
func listUsersCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(true, &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List users",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var tailnetID uint64
|
||||
var tailnetName string
|
||||
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.")
|
||||
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.")
|
||||
|
||||
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tailnet, err := findTailnet(client, tailnetName, tailnetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := api.ListUsersRequest{TailnetId: tailnet.Id}
|
||||
resp, err := client.ListUsers(context.Background(), connect.NewRequest(&req))
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.ListUsersRequest{TailnetId: tc.TailnetID()}
|
||||
resp, err := tc.Client().ListUsers(cmd.Context(), connect.NewRequest(&req))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -70,27 +50,21 @@ func listUsersCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func deleteUserCommand() *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Deletes a user",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
})
|
||||
|
||||
var userID uint64
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
|
||||
command.Flags().Uint64Var(&userID, "user-id", 0, "User ID.")
|
||||
|
||||
_ = command.MarkFlagRequired("user-id")
|
||||
|
||||
command.RunE = func(command *cobra.Command, args []string) error {
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
req := api.DeleteUserRequest{UserId: userID}
|
||||
if _, err := client.DeleteUser(context.Background(), connect.NewRequest(&req)); err != nil {
|
||||
if _, err := tc.Client().DeleteUser(cmd.Context(), connect.NewRequest(&req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+4
-17
@@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
"github.com/jsiebens/ionscale/internal/version"
|
||||
@@ -10,14 +9,11 @@ import (
|
||||
)
|
||||
|
||||
func versionCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
command, tc := prepareCommand(false, &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display version information",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var target = Target{}
|
||||
target.prepareCommand(command)
|
||||
})
|
||||
|
||||
command.Run = func(cmd *cobra.Command, args []string) {
|
||||
clientVersion, clientRevision := version.GetReleaseInfo()
|
||||
@@ -27,16 +23,7 @@ Client:
|
||||
Git Revision: %s
|
||||
`, clientVersion, clientRevision)
|
||||
|
||||
client, err := target.createGRPCClient()
|
||||
if err != nil {
|
||||
fmt.Printf(`
|
||||
Server:
|
||||
Error: %s
|
||||
`, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.GetVersion(context.Background(), connect.NewRequest(&api.GetVersionRequest{}))
|
||||
resp, err := tc.Client().GetVersion(cmd.Context(), connect.NewRequest(&api.GetVersionRequest{}))
|
||||
if err != nil {
|
||||
fmt.Printf(`
|
||||
Server:
|
||||
@@ -50,7 +37,7 @@ Server:
|
||||
Addr: %s
|
||||
Version: %s
|
||||
Git Revision: %s
|
||||
`, target.getAddr(), resp.Msg.Version, resp.Msg.Revision)
|
||||
`, tc.Addr(), resp.Msg.Version, resp.Msg.Revision)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,6 @@ func defaultConfig() *Config {
|
||||
ForceHttps: true,
|
||||
AcmeEnabled: false,
|
||||
AcmeCA: certmagic.LetsEncryptProductionCA,
|
||||
AcmePath: "./acme",
|
||||
},
|
||||
PollNet: PollNet{
|
||||
KeepAliveInterval: defaultKeepAliveInterval,
|
||||
@@ -157,7 +156,6 @@ type Tls struct {
|
||||
AcmeEnabled bool `yaml:"acme,omitempty" env:"ACME_ENABLED"`
|
||||
AcmeEmail string `yaml:"acme_email,omitempty" env:"ACME_EMAIL"`
|
||||
AcmeCA string `yaml:"acme_ca,omitempty" env:"ACME_CA"`
|
||||
AcmePath string `yaml:"acme_path,omitempty" env:"ACME_PATH"`
|
||||
}
|
||||
|
||||
type PollNet struct {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -11,7 +12,7 @@ type PollMapSessionManager interface {
|
||||
Register(tailnetID uint64, machineID uint64, ch chan *Ping)
|
||||
Deregister(tailnetID uint64, machineID uint64)
|
||||
HasSession(tailnetID uint64, machineID uint64) bool
|
||||
NotifyAll(tailnetID uint64)
|
||||
NotifyAll(tailnetID uint64, ignoreMachineIDs ...uint64)
|
||||
}
|
||||
|
||||
func NewPollMapSessionManager() PollMapSessionManager {
|
||||
@@ -82,13 +83,15 @@ func (n *pollMapSessionManager) HasSession(tailnetID uint64, machineID uint64) b
|
||||
return false
|
||||
}
|
||||
|
||||
func (n *pollMapSessionManager) NotifyAll(tailnetID uint64) {
|
||||
func (n *pollMapSessionManager) NotifyAll(tailnetID uint64, ignoreMachineIDs ...uint64) {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
|
||||
if ss := n.data[tailnetID]; ss != nil {
|
||||
for _, p := range ss {
|
||||
p <- &Ping{}
|
||||
for i, p := range ss {
|
||||
if !slices.Contains(ignoreMachineIDs, i) {
|
||||
p <- &Ping{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ const (
|
||||
inactivityTimeout = 30 * time.Minute
|
||||
)
|
||||
|
||||
func StartReaper(repository domain.Repository, sessionManager PollMapSessionManager) {
|
||||
r := &reaper{
|
||||
func StartWorker(repository domain.Repository, sessionManager PollMapSessionManager) {
|
||||
r := &worker{
|
||||
sessionManager: sessionManager,
|
||||
repository: repository,
|
||||
}
|
||||
@@ -20,19 +20,20 @@ func StartReaper(repository domain.Repository, sessionManager PollMapSessionMana
|
||||
go r.start()
|
||||
}
|
||||
|
||||
type reaper struct {
|
||||
type worker struct {
|
||||
sessionManager PollMapSessionManager
|
||||
repository domain.Repository
|
||||
}
|
||||
|
||||
func (r *reaper) start() {
|
||||
func (r *worker) start() {
|
||||
r.deleteInactiveEphemeralNodes()
|
||||
t := time.NewTicker(ticker)
|
||||
for range t.C {
|
||||
r.reapInactiveEphemeralNodes()
|
||||
r.deleteInactiveEphemeralNodes()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reaper) reapInactiveEphemeralNodes() {
|
||||
func (r *worker) deleteInactiveEphemeralNodes() {
|
||||
ctx := context.Background()
|
||||
|
||||
now := time.Now().UTC()
|
||||
@@ -41,6 +42,7 @@ func (r *reaper) reapInactiveEphemeralNodes() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var removedNodes = make(map[uint64][]uint64)
|
||||
for _, m := range machines {
|
||||
if now.After(m.LastSeen.Add(inactivityTimeout)) {
|
||||
@@ -2,6 +2,7 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
@@ -23,17 +24,17 @@ type dbLock interface {
|
||||
UnlockErr(error) error
|
||||
}
|
||||
|
||||
func OpenDB(config *config.Database, logger *zap.Logger) (domain.Repository, error) {
|
||||
func OpenDB(config *config.Database, logger *zap.Logger) (*sql.DB, domain.Repository, error) {
|
||||
db, lock, err := createDB(config, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_ = db.Use(prometheus.New(prometheus.Config{StartServer: false}))
|
||||
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sqlDB.SetMaxOpenConns(config.MaxOpenConns)
|
||||
@@ -44,14 +45,14 @@ func OpenDB(config *config.Database, logger *zap.Logger) (domain.Repository, err
|
||||
repository := domain.NewRepository(db)
|
||||
|
||||
if err := lock.Lock(); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := lock.UnlockErr(migrate(db)); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return repository, nil
|
||||
return sqlDB, repository, nil
|
||||
}
|
||||
|
||||
func createDB(config *config.Database, logger *zap.Logger) (*gorm.DB, dbLock, error) {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package migration
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func m202401061400_machine_indeces() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "202401061400",
|
||||
Migrate: func(db *gorm.DB) error {
|
||||
type Machine struct {
|
||||
ID uint64 `gorm:"primaryKey;autoIncrement:false;index:idx_tailnet_id_id,priority:2"`
|
||||
Name string `gorm:"index:idx_tailnet_id_name,unique,priority:2"`
|
||||
NameIdx uint64 `gorm:"index:idx_tailnet_id_name,unique,sort:desc,priority:3"`
|
||||
TailnetID uint64 `gorm:"index:idx_tailnet_id_id,priority:1;index:idx_tailnet_id_name,priority:1"`
|
||||
}
|
||||
|
||||
db.Migrator().DropIndex(&Machine{}, "idx_tailnet_id_name")
|
||||
|
||||
return db.AutoMigrate(
|
||||
&Machine{},
|
||||
)
|
||||
},
|
||||
Rollback: nil,
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ func Migrations() []*gormigrate.Migration {
|
||||
m202212270800_machine_indeces(),
|
||||
m202312271200_account_last_authenticated(),
|
||||
m202312290900_machine_indeces(),
|
||||
m202401061400_machine_indeces(),
|
||||
}
|
||||
return migrations
|
||||
}
|
||||
|
||||
+121
-239
@@ -8,6 +8,7 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -34,10 +35,13 @@ type ACLPolicy struct {
|
||||
TagOwners map[string][]string `json:"tagowners,omitempty"`
|
||||
AutoApprovers *AutoApprovers `json:"autoApprovers,omitempty"`
|
||||
SSHRules []SSHRule `json:"ssh,omitempty"`
|
||||
NodeAttrs []NodeAttr `json:"nodeAttrs,omitempty"`
|
||||
Grants []Grant `json:"grants,omitempty"`
|
||||
}
|
||||
|
||||
type ACL struct {
|
||||
Action string `json:"action"`
|
||||
Proto string `json:"proto"`
|
||||
Src []string `json:"src"`
|
||||
Dst []string `json:"dst"`
|
||||
}
|
||||
@@ -50,16 +54,16 @@ type SSHRule struct {
|
||||
CheckPeriod string `json:"checkPeriod,omitempty"`
|
||||
}
|
||||
|
||||
func DefaultACLPolicy() ACLPolicy {
|
||||
return ACLPolicy{
|
||||
ACLs: []ACL{
|
||||
{
|
||||
Action: "accept",
|
||||
Src: []string{"*"},
|
||||
Dst: []string{"*:*"},
|
||||
},
|
||||
},
|
||||
}
|
||||
type NodeAttr struct {
|
||||
Target []string `json:"target"`
|
||||
Attr []string `json:"attr"`
|
||||
}
|
||||
|
||||
type Grant struct {
|
||||
Src []string `json:"src"`
|
||||
Dst []string `json:"dst"`
|
||||
IP []tailcfg.ProtoPortRange `json:"ip"`
|
||||
App tailcfg.PeerCapMap `json:"app"`
|
||||
}
|
||||
|
||||
func (a ACLPolicy) FindAutoApprovedIPs(routableIPs []netip.Prefix, tags []string, u *User) []netip.Prefix {
|
||||
@@ -102,7 +106,7 @@ func (a ACLPolicy) FindAutoApprovedIPs(routableIPs []netip.Prefix, tags []string
|
||||
return false
|
||||
}
|
||||
|
||||
autoApprovedIPs := []netip.Prefix{}
|
||||
var autoApprovedIPs []netip.Prefix
|
||||
for route, autoApprovers := range a.AutoApprovers.Routes {
|
||||
candidate, err := netip.ParsePrefix(route)
|
||||
if err != nil {
|
||||
@@ -114,7 +118,7 @@ func (a ACLPolicy) FindAutoApprovedIPs(routableIPs []netip.Prefix, tags []string
|
||||
}
|
||||
}
|
||||
|
||||
result := []netip.Prefix{}
|
||||
var result []netip.Prefix
|
||||
for _, c := range routableIPs {
|
||||
if c.Bits() == 0 && matches(a.AutoApprovers.ExitNode) {
|
||||
result = append(result, c)
|
||||
@@ -127,15 +131,6 @@ func (a ACLPolicy) FindAutoApprovedIPs(routableIPs []netip.Prefix, tags []string
|
||||
return result
|
||||
}
|
||||
|
||||
func (a ACLPolicy) IsTagOwner(tags []string, p *User) bool {
|
||||
for _, t := range tags {
|
||||
if a.isTagOwner(t, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a ACLPolicy) CheckTagOwners(tags []string, p *User) error {
|
||||
var result *multierror.Error
|
||||
for _, t := range tags {
|
||||
@@ -151,239 +146,71 @@ func (a ACLPolicy) isTagOwner(tag string, p *User) bool {
|
||||
return true
|
||||
}
|
||||
if tagOwners, ok := a.TagOwners[tag]; ok {
|
||||
return a.validateTagOwners(tagOwners, p)
|
||||
for _, alias := range tagOwners {
|
||||
if strings.HasPrefix(alias, "group:") {
|
||||
if group, ok := a.Groups[alias]; ok {
|
||||
return slices.Contains(group, p.Name)
|
||||
}
|
||||
} else {
|
||||
if alias == p.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a ACLPolicy) validateTagOwners(tagOwners []string, p *User) bool {
|
||||
for _, alias := range tagOwners {
|
||||
if strings.HasPrefix(alias, "group:") {
|
||||
if group, ok := a.Groups[alias]; ok {
|
||||
for _, groupMember := range group {
|
||||
if groupMember == p.Name {
|
||||
func (a ACLPolicy) NodeCapabilities(m *Machine) []tailcfg.NodeCapability {
|
||||
var result = &StringSet{}
|
||||
|
||||
matches := func(targets []string) bool {
|
||||
for _, alias := range targets {
|
||||
if alias == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Contains(alias, "@") && !m.HasTags() && m.HasUser(alias) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(alias, "tag:") && m.HasTag(alias) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(alias, "group:") && !m.HasTags() {
|
||||
for _, u := range a.Groups[alias] {
|
||||
if m.HasUser(u) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if alias == p.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
for _, nodeAddr := range a.NodeAttrs {
|
||||
if matches(nodeAddr.Target) {
|
||||
result.Add(nodeAddr.Attr...)
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
items := result.Items()
|
||||
caps := make([]tailcfg.NodeCapability, len(items))
|
||||
for i, c := range items {
|
||||
caps[i] = tailcfg.NodeCapability(c)
|
||||
}
|
||||
|
||||
return caps
|
||||
}
|
||||
|
||||
func (a ACLPolicy) IsValidPeer(src *Machine, dest *Machine) bool {
|
||||
if !src.HasTags() && !dest.HasTags() && dest.HasUser(src.User.Name) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, acl := range a.ACLs {
|
||||
selfDestPorts, allDestPorts := a.expandMachineToDstPorts(dest, acl.Dst)
|
||||
if len(selfDestPorts) != 0 {
|
||||
for _, alias := range acl.Src {
|
||||
if len(a.expandMachineAlias(src, alias, true, &dest.User)) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(allDestPorts) != 0 {
|
||||
for _, alias := range acl.Src {
|
||||
if len(a.expandMachineAlias(src, alias, true, nil)) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a ACLPolicy) BuildFilterRules(srcs []Machine, dst *Machine) []tailcfg.FilterRule {
|
||||
var rules []tailcfg.FilterRule
|
||||
|
||||
transform := func(src []string, destPorts []tailcfg.NetPortRange, u *User) tailcfg.FilterRule {
|
||||
var allSrcIPsSet = &StringSet{}
|
||||
for _, alias := range src {
|
||||
for _, src := range srcs {
|
||||
srcIPs := a.expandMachineAlias(&src, alias, true, u)
|
||||
allSrcIPsSet.Add(srcIPs...)
|
||||
}
|
||||
}
|
||||
|
||||
allSrcIPs := allSrcIPsSet.Items()
|
||||
|
||||
if len(allSrcIPs) == 0 {
|
||||
allSrcIPs = nil
|
||||
}
|
||||
|
||||
return tailcfg.FilterRule{
|
||||
SrcIPs: allSrcIPs,
|
||||
DstPorts: destPorts,
|
||||
}
|
||||
}
|
||||
|
||||
for _, acl := range a.ACLs {
|
||||
selfDestPorts, allDestPorts := a.expandMachineToDstPorts(dst, acl.Dst)
|
||||
if len(selfDestPorts) != 0 {
|
||||
rules = append(rules, transform(acl.Src, selfDestPorts, &dst.User))
|
||||
}
|
||||
if len(allDestPorts) != 0 {
|
||||
rules = append(rules, transform(acl.Src, allDestPorts, nil))
|
||||
}
|
||||
}
|
||||
|
||||
if len(rules) == 0 {
|
||||
return []tailcfg.FilterRule{{}}
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
func (a ACLPolicy) expandMachineToDstPorts(m *Machine, ports []string) ([]tailcfg.NetPortRange, []tailcfg.NetPortRange) {
|
||||
selfDestRanges := []tailcfg.NetPortRange{}
|
||||
otherDestRanges := []tailcfg.NetPortRange{}
|
||||
for _, d := range ports {
|
||||
self, ranges := a.expandMachineDestToNetPortRanges(m, d)
|
||||
if self {
|
||||
selfDestRanges = append(selfDestRanges, ranges...)
|
||||
} else {
|
||||
otherDestRanges = append(otherDestRanges, ranges...)
|
||||
}
|
||||
}
|
||||
return selfDestRanges, otherDestRanges
|
||||
}
|
||||
|
||||
func (a ACLPolicy) expandMachineDestToNetPortRanges(m *Machine, dest string) (bool, []tailcfg.NetPortRange) {
|
||||
tokens := strings.Split(dest, ":")
|
||||
if len(tokens) < 2 || len(tokens) > 3 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var alias string
|
||||
if len(tokens) == 2 {
|
||||
alias = tokens[0]
|
||||
} else {
|
||||
alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1])
|
||||
}
|
||||
|
||||
ports, err := a.expandValuePortToPortRange(tokens[len(tokens)-1])
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
ips := a.expandMachineAlias(m, alias, false, nil)
|
||||
if len(ips) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
dests := []tailcfg.NetPortRange{}
|
||||
for _, d := range ips {
|
||||
for _, p := range ports {
|
||||
pr := tailcfg.NetPortRange{
|
||||
IP: d,
|
||||
Ports: p,
|
||||
}
|
||||
dests = append(dests, pr)
|
||||
}
|
||||
}
|
||||
|
||||
return alias == AutoGroupSelf, dests
|
||||
}
|
||||
|
||||
func (a ACLPolicy) expandMachineAlias(m *Machine, alias string, src bool, u *User) []string {
|
||||
if u != nil && m.HasTags() {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
if u != nil && !m.HasUser(u.Name) {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
if alias == "*" && u != nil {
|
||||
return m.IPs()
|
||||
}
|
||||
|
||||
if alias == "*" {
|
||||
return []string{"*"}
|
||||
}
|
||||
|
||||
if alias == AutoGroupMember || alias == AutoGroupMembers || alias == AutoGroupSelf {
|
||||
if !m.HasTags() {
|
||||
return m.IPs()
|
||||
} else {
|
||||
return []string{}
|
||||
}
|
||||
}
|
||||
|
||||
if alias == AutoGroupTagged {
|
||||
if m.HasTags() {
|
||||
return m.IPs()
|
||||
} else {
|
||||
return []string{}
|
||||
}
|
||||
}
|
||||
|
||||
if alias == AutoGroupInternet && m.IsExitNode() {
|
||||
return autogroupInternetRanges()
|
||||
}
|
||||
|
||||
if strings.Contains(alias, "@") && !m.HasTags() && m.HasUser(alias) {
|
||||
return m.IPs()
|
||||
}
|
||||
|
||||
if strings.HasPrefix(alias, "group:") && !m.HasTags() {
|
||||
users, ok := a.Groups[alias]
|
||||
|
||||
if !ok {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
for _, u := range users {
|
||||
if m.HasUser(u) {
|
||||
return m.IPs()
|
||||
}
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(alias, "tag:") && m.HasTag(alias) {
|
||||
return m.IPs()
|
||||
}
|
||||
|
||||
if h, ok := a.Hosts[alias]; ok {
|
||||
alias = h
|
||||
}
|
||||
|
||||
if src {
|
||||
ip, err := netip.ParseAddr(alias)
|
||||
if err == nil && m.HasIP(ip) {
|
||||
return []string{ip.String()}
|
||||
}
|
||||
} else {
|
||||
ip, err := netip.ParseAddr(alias)
|
||||
if err == nil && m.IsAllowedIP(ip) {
|
||||
return []string{ip.String()}
|
||||
}
|
||||
|
||||
prefix, err := netip.ParsePrefix(alias)
|
||||
if err == nil && m.IsAllowedIPPrefix(prefix) {
|
||||
return []string{prefix.String()}
|
||||
}
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (a ACLPolicy) expandValuePortToPortRange(s string) ([]tailcfg.PortRange, error) {
|
||||
func (a ACLPolicy) parsePortRanges(s string) ([]tailcfg.PortRange, error) {
|
||||
if s == "*" {
|
||||
return []tailcfg.PortRange{{First: 0, Last: 65535}}, nil
|
||||
return []tailcfg.PortRange{tailcfg.PortRangeAny}, nil
|
||||
}
|
||||
|
||||
ports := []tailcfg.PortRange{}
|
||||
var ports []tailcfg.PortRange
|
||||
for _, p := range strings.Split(s, ",") {
|
||||
rang := strings.Split(p, "-")
|
||||
if len(rang) == 1 {
|
||||
@@ -462,6 +289,57 @@ func (ACLPolicy) GormDBDataType(db *gorm.DB, field *schema.Field) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
const (
|
||||
protocolICMP = 1 // Internet Control Message
|
||||
protocolIGMP = 2 // Internet Group Management
|
||||
protocolIPv4 = 4 // IPv4 encapsulation
|
||||
protocolTCP = 6 // Transmission Control
|
||||
protocolEGP = 8 // Exterior Gateway Protocol
|
||||
protocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP)
|
||||
protocolUDP = 17 // User Datagram
|
||||
protocolGRE = 47 // Generic Routing Encapsulation
|
||||
protocolESP = 50 // Encap Security Payload
|
||||
protocolAH = 51 // Authentication Header
|
||||
protocolIPv6ICMP = 58 // ICMP for IPv6
|
||||
protocolSCTP = 132 // Stream Control Transmission Protocol
|
||||
)
|
||||
|
||||
func parseProtocol(protocol string) []int {
|
||||
switch protocol {
|
||||
case "":
|
||||
return nil
|
||||
case "igmp":
|
||||
return []int{protocolIGMP}
|
||||
case "ipv4", "ip-in-ip":
|
||||
return []int{protocolIPv4}
|
||||
case "tcp":
|
||||
return []int{protocolTCP}
|
||||
case "egp":
|
||||
return []int{protocolEGP}
|
||||
case "igp":
|
||||
return []int{protocolIGP}
|
||||
case "udp":
|
||||
return []int{protocolUDP}
|
||||
case "gre":
|
||||
return []int{protocolGRE}
|
||||
case "esp":
|
||||
return []int{protocolESP}
|
||||
case "ah":
|
||||
return []int{protocolAH}
|
||||
case "sctp":
|
||||
return []int{protocolSCTP}
|
||||
case "icmp":
|
||||
return []int{protocolICMP, protocolIPv6ICMP}
|
||||
|
||||
default:
|
||||
n, err := strconv.Atoi(protocol)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return []int{n}
|
||||
}
|
||||
}
|
||||
|
||||
type StringSet struct {
|
||||
items map[string]bool
|
||||
}
|
||||
@@ -487,6 +365,10 @@ func (s *StringSet) Items() []string {
|
||||
return items
|
||||
}
|
||||
|
||||
func (s *StringSet) Empty() bool {
|
||||
return len(s.items) == 0
|
||||
}
|
||||
|
||||
func autogroupInternetRanges() []string {
|
||||
return []string{
|
||||
"0.0.0.0/5",
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
func (a ACLPolicy) IsValidPeer(src *Machine, dest *Machine) bool {
|
||||
if !src.HasTags() && !dest.HasTags() && dest.HasUser(src.User.Name) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, acl := range a.ACLs {
|
||||
selfDestPorts, allDestPorts := a.translateDestinationAliasesToMachineNetPortRanges(acl.Dst, dest)
|
||||
if len(selfDestPorts) != 0 {
|
||||
for _, alias := range acl.Src {
|
||||
if len(a.translateSourceAliasToMachineIPs(alias, src, &dest.User)) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(allDestPorts) != 0 {
|
||||
for _, alias := range acl.Src {
|
||||
if len(a.translateSourceAliasToMachineIPs(alias, src, nil)) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, grant := range a.Grants {
|
||||
selfIps, otherIps := a.translateDestinationAliasesToMachineIPs(grant.Dst, dest)
|
||||
if len(selfIps) != 0 {
|
||||
for _, alias := range grant.Src {
|
||||
if len(a.translateSourceAliasToMachineIPs(alias, src, &dest.User)) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(otherIps) != 0 {
|
||||
for _, alias := range grant.Src {
|
||||
if len(a.translateSourceAliasToMachineIPs(alias, src, nil)) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a ACLPolicy) BuildFilterRules(peers []Machine, dst *Machine) []tailcfg.FilterRule {
|
||||
var rules = make([]tailcfg.FilterRule, 0)
|
||||
|
||||
matchSourceAndAppendRule := func(rules []tailcfg.FilterRule, aliases []string, preparedRules []tailcfg.FilterRule, u *User) []tailcfg.FilterRule {
|
||||
if len(preparedRules) == 0 {
|
||||
return rules
|
||||
}
|
||||
|
||||
var allSrcIPsSet = &StringSet{}
|
||||
for _, alias := range aliases {
|
||||
for _, peer := range peers {
|
||||
allSrcIPsSet.Add(a.translateSourceAliasToMachineIPs(alias, &peer, u)...)
|
||||
}
|
||||
}
|
||||
|
||||
if allSrcIPsSet.Empty() {
|
||||
return rules
|
||||
}
|
||||
|
||||
allSrcIPs := allSrcIPsSet.Items()
|
||||
|
||||
if len(allSrcIPs) == 0 {
|
||||
return rules
|
||||
}
|
||||
|
||||
for _, pr := range preparedRules {
|
||||
rules = append(rules, tailcfg.FilterRule{
|
||||
SrcIPs: allSrcIPs,
|
||||
DstPorts: pr.DstPorts,
|
||||
IPProto: pr.IPProto,
|
||||
CapGrant: pr.CapGrant,
|
||||
})
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
for _, acl := range a.ACLs {
|
||||
self, other := a.prepareFilterRulesFromACL(dst, acl)
|
||||
rules = matchSourceAndAppendRule(rules, acl.Src, self, &dst.User)
|
||||
rules = matchSourceAndAppendRule(rules, acl.Src, other, nil)
|
||||
}
|
||||
|
||||
for _, acl := range a.Grants {
|
||||
self, other := a.prepareFilterRulesFromGrant(dst, acl)
|
||||
rules = matchSourceAndAppendRule(rules, acl.Src, self, &dst.User)
|
||||
rules = matchSourceAndAppendRule(rules, acl.Src, other, nil)
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
func (a ACLPolicy) prepareFilterRulesFromACL(candidate *Machine, acl ACL) ([]tailcfg.FilterRule, []tailcfg.FilterRule) {
|
||||
proto := parseProtocol(acl.Proto)
|
||||
|
||||
selfDstPorts, otherDstPorts := a.translateDestinationAliasesToMachineNetPortRanges(acl.Dst, candidate)
|
||||
|
||||
var selfFilterRules []tailcfg.FilterRule
|
||||
var otherFilterRules []tailcfg.FilterRule
|
||||
|
||||
if len(selfDstPorts) != 0 {
|
||||
selfFilterRules = append(selfFilterRules, tailcfg.FilterRule{IPProto: proto, DstPorts: selfDstPorts})
|
||||
}
|
||||
|
||||
if len(otherDstPorts) != 0 {
|
||||
otherFilterRules = append(otherFilterRules, tailcfg.FilterRule{IPProto: proto, DstPorts: otherDstPorts})
|
||||
}
|
||||
|
||||
return selfFilterRules, otherFilterRules
|
||||
}
|
||||
|
||||
func (a ACLPolicy) prepareFilterRulesFromGrant(candidate *Machine, grant Grant) ([]tailcfg.FilterRule, []tailcfg.FilterRule) {
|
||||
selfIPs, otherIPs := a.translateDestinationAliasesToMachineIPs(grant.Dst, candidate)
|
||||
|
||||
var selfFilterRules []tailcfg.FilterRule
|
||||
var otherFilterRules []tailcfg.FilterRule
|
||||
|
||||
for _, ip := range grant.IP {
|
||||
if len(selfIPs) != 0 {
|
||||
ranges := make([]tailcfg.NetPortRange, len(selfIPs))
|
||||
for i, s := range selfIPs {
|
||||
ranges[i] = tailcfg.NetPortRange{IP: s, Ports: ip.Ports}
|
||||
}
|
||||
|
||||
rule := tailcfg.FilterRule{DstPorts: ranges}
|
||||
if ip.Proto != 0 {
|
||||
rule.IPProto = []int{ip.Proto}
|
||||
}
|
||||
|
||||
selfFilterRules = append(selfFilterRules, rule)
|
||||
}
|
||||
|
||||
if len(otherIPs) != 0 {
|
||||
ranges := make([]tailcfg.NetPortRange, len(otherIPs))
|
||||
for i, s := range otherIPs {
|
||||
ranges[i] = tailcfg.NetPortRange{IP: s, Ports: ip.Ports}
|
||||
}
|
||||
|
||||
rule := tailcfg.FilterRule{DstPorts: ranges}
|
||||
if ip.Proto != 0 {
|
||||
rule.IPProto = []int{ip.Proto}
|
||||
}
|
||||
|
||||
otherFilterRules = append(otherFilterRules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
if len(grant.App) != 0 {
|
||||
selfPrefixes, otherPrefixes := appGrantDstIpsToPrefixes(candidate, selfIPs, otherIPs)
|
||||
if len(selfPrefixes) != 0 {
|
||||
rule := tailcfg.FilterRule{CapGrant: []tailcfg.CapGrant{{Dsts: selfPrefixes, CapMap: grant.App}}}
|
||||
selfFilterRules = append(selfFilterRules, rule)
|
||||
}
|
||||
|
||||
if len(otherPrefixes) != 0 {
|
||||
rule := tailcfg.FilterRule{CapGrant: []tailcfg.CapGrant{{Dsts: otherPrefixes, CapMap: grant.App}}}
|
||||
otherFilterRules = append(otherFilterRules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return selfFilterRules, otherFilterRules
|
||||
}
|
||||
|
||||
func appGrantDstIpsToPrefixes(m *Machine, self []string, other []string) ([]netip.Prefix, []netip.Prefix) {
|
||||
translate := func(ips []string) []netip.Prefix {
|
||||
var prefixes []netip.Prefix
|
||||
for _, ip := range ips {
|
||||
if ip == "*" {
|
||||
prefixes = append(prefixes, netip.PrefixFrom(*m.IPv4.Addr, 32))
|
||||
prefixes = append(prefixes, netip.PrefixFrom(*m.IPv6.Addr, 128))
|
||||
} else {
|
||||
addr, err := netip.ParseAddr(ip)
|
||||
if err == nil && m.HasIP(addr) {
|
||||
if addr.Is4() {
|
||||
prefixes = append(prefixes, netip.PrefixFrom(addr, 32))
|
||||
} else {
|
||||
prefixes = append(prefixes, netip.PrefixFrom(addr, 128))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return prefixes
|
||||
}
|
||||
|
||||
return translate(self), translate(other)
|
||||
}
|
||||
|
||||
func (a ACLPolicy) translateDestinationAliasesToMachineIPs(aliases []string, m *Machine) ([]string, []string) {
|
||||
var self = &StringSet{}
|
||||
var other = &StringSet{}
|
||||
for _, alias := range aliases {
|
||||
ips := a.translateDestinationAliasToMachineIPs(alias, m)
|
||||
if alias == AutoGroupSelf {
|
||||
self.Add(ips...)
|
||||
} else {
|
||||
other.Add(ips...)
|
||||
}
|
||||
}
|
||||
return self.Items(), other.Items()
|
||||
}
|
||||
|
||||
func (a ACLPolicy) translateDestinationAliasesToMachineNetPortRanges(aliases []string, m *Machine) ([]tailcfg.NetPortRange, []tailcfg.NetPortRange) {
|
||||
var self []tailcfg.NetPortRange
|
||||
var other []tailcfg.NetPortRange
|
||||
for _, alias := range aliases {
|
||||
ranges := a.translationDestinationAliasToMachineNetPortRanges(alias, m)
|
||||
if strings.HasPrefix(alias, AutoGroupSelf) {
|
||||
self = append(self, ranges...)
|
||||
} else {
|
||||
other = append(other, ranges...)
|
||||
}
|
||||
}
|
||||
return self, other
|
||||
}
|
||||
|
||||
func (a ACLPolicy) translationDestinationAliasToMachineNetPortRanges(alias string, m *Machine) []tailcfg.NetPortRange {
|
||||
lastInd := strings.LastIndex(alias, ":")
|
||||
if lastInd == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ports := alias[lastInd+1:]
|
||||
alias = alias[:lastInd]
|
||||
|
||||
portRanges, err := a.parsePortRanges(ports)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ips := a.translateDestinationAliasToMachineIPs(alias, m)
|
||||
if len(ips) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var netPortRanges []tailcfg.NetPortRange
|
||||
for _, d := range ips {
|
||||
for _, p := range portRanges {
|
||||
pr := tailcfg.NetPortRange{
|
||||
IP: d,
|
||||
Ports: p,
|
||||
}
|
||||
netPortRanges = append(netPortRanges, pr)
|
||||
}
|
||||
}
|
||||
|
||||
return netPortRanges
|
||||
}
|
||||
|
||||
func (a ACLPolicy) translateDestinationAliasToMachineIPs(alias string, m *Machine) []string {
|
||||
f := func(alias string, m *Machine) []string {
|
||||
ip, err := netip.ParseAddr(alias)
|
||||
if err == nil && m.IsAllowedIP(ip) {
|
||||
return []string{ip.String()}
|
||||
}
|
||||
|
||||
prefix, err := netip.ParsePrefix(alias)
|
||||
if err == nil && m.IsAllowedIPPrefix(prefix) {
|
||||
return []string{prefix.String()}
|
||||
}
|
||||
|
||||
return make([]string, 0)
|
||||
}
|
||||
|
||||
return a.translateAliasToMachineIPs(alias, m, nil, f)
|
||||
}
|
||||
|
||||
func (a ACLPolicy) translateSourceAliasToMachineIPs(alias string, m *Machine, u *User) []string {
|
||||
f := func(alias string, m *Machine) []string {
|
||||
ip, err := netip.ParseAddr(alias)
|
||||
if err == nil && m.HasIP(ip) {
|
||||
return []string{ip.String()}
|
||||
}
|
||||
|
||||
return make([]string, 0)
|
||||
}
|
||||
|
||||
return a.translateAliasToMachineIPs(alias, m, u, f)
|
||||
}
|
||||
|
||||
func (a ACLPolicy) translateAliasToMachineIPs(alias string, m *Machine, u *User, f func(string, *Machine) []string) []string {
|
||||
if u != nil && m.HasTags() {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
if u != nil && !m.HasUser(u.Name) {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
if alias == "*" && u != nil {
|
||||
return m.IPs()
|
||||
}
|
||||
|
||||
if alias == "*" {
|
||||
return []string{"*"}
|
||||
}
|
||||
|
||||
if alias == AutoGroupMember || alias == AutoGroupMembers || alias == AutoGroupSelf {
|
||||
if !m.HasTags() {
|
||||
return m.IPs()
|
||||
} else {
|
||||
return []string{}
|
||||
}
|
||||
}
|
||||
|
||||
if alias == AutoGroupTagged {
|
||||
if m.HasTags() {
|
||||
return m.IPs()
|
||||
} else {
|
||||
return []string{}
|
||||
}
|
||||
}
|
||||
|
||||
if alias == AutoGroupInternet && m.IsExitNode() {
|
||||
return autogroupInternetRanges()
|
||||
}
|
||||
|
||||
if strings.Contains(alias, "@") && !m.HasTags() && m.HasUser(alias) {
|
||||
return m.IPs()
|
||||
}
|
||||
|
||||
if strings.HasPrefix(alias, "group:") && !m.HasTags() && a.isGroupMember(alias, m) {
|
||||
return m.IPs()
|
||||
}
|
||||
|
||||
if strings.HasPrefix(alias, "tag:") && m.HasTag(alias) {
|
||||
return m.IPs()
|
||||
}
|
||||
|
||||
if h, ok := a.Hosts[alias]; ok {
|
||||
alias = h
|
||||
}
|
||||
|
||||
return f(alias, m)
|
||||
}
|
||||
+289
-2
@@ -1,14 +1,127 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/jsiebens/ionscale/internal/addr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"net/netip"
|
||||
"sort"
|
||||
"tailscale.com/tailcfg"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestACLPolicy_NodeAttributesWithWildcards(t *testing.T) {
|
||||
p1 := createMachine("john@example.com")
|
||||
|
||||
policy := ACLPolicy{
|
||||
NodeAttrs: []NodeAttr{
|
||||
{
|
||||
Target: []string{"*"},
|
||||
Attr: []string{
|
||||
"attr1",
|
||||
"attr2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Target: []string{"*"},
|
||||
Attr: []string{
|
||||
"attr3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
actualAttrs := policy.NodeCapabilities(p1)
|
||||
expectedAttrs := []tailcfg.NodeCapability{
|
||||
tailcfg.NodeCapability("attr1"),
|
||||
tailcfg.NodeCapability("attr2"),
|
||||
tailcfg.NodeCapability("attr3"),
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedAttrs, actualAttrs)
|
||||
}
|
||||
|
||||
func TestACLPolicy_NodeAttributesWithUserAndGroups(t *testing.T) {
|
||||
p1 := createMachine("john@example.com")
|
||||
|
||||
policy := ACLPolicy{
|
||||
Groups: map[string][]string{
|
||||
"group:admins": []string{"john@example.com"},
|
||||
},
|
||||
NodeAttrs: []NodeAttr{
|
||||
{
|
||||
Target: []string{"john@example.com"},
|
||||
Attr: []string{
|
||||
"attr1",
|
||||
"attr2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Target: []string{"jane@example.com", "group:analytics", "group:admins"},
|
||||
Attr: []string{
|
||||
"attr3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
actualAttrs := policy.NodeCapabilities(p1)
|
||||
expectedAttrs := []tailcfg.NodeCapability{
|
||||
tailcfg.NodeCapability("attr1"),
|
||||
tailcfg.NodeCapability("attr2"),
|
||||
tailcfg.NodeCapability("attr3"),
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedAttrs, actualAttrs)
|
||||
}
|
||||
|
||||
func TestACLPolicy_NodeAttributesWithUserAndTags(t *testing.T) {
|
||||
p1 := createMachine("john@example.com", "tag:web")
|
||||
|
||||
policy := ACLPolicy{
|
||||
Groups: map[string][]string{
|
||||
"group:admins": []string{"john@example.com"},
|
||||
},
|
||||
NodeAttrs: []NodeAttr{
|
||||
{
|
||||
Target: []string{"john@example.com"},
|
||||
Attr: []string{
|
||||
"attr1",
|
||||
"attr2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Target: []string{"jane@example.com", "tag:web"},
|
||||
Attr: []string{
|
||||
"attr3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
actualAttrs := policy.NodeCapabilities(p1)
|
||||
expectedAttrs := []tailcfg.NodeCapability{tailcfg.NodeCapability("attr3")}
|
||||
|
||||
assert.Equal(t, expectedAttrs, actualAttrs)
|
||||
}
|
||||
|
||||
func TestACLPolicy_BuildFilterRulesEmptyACL(t *testing.T) {
|
||||
p1 := createMachine("john@example.com")
|
||||
p2 := createMachine("jane@example.com")
|
||||
|
||||
policy := ACLPolicy{
|
||||
ACLs: []ACL{},
|
||||
}
|
||||
|
||||
dst := createMachine("john@example.com")
|
||||
|
||||
actualRules := policy.BuildFilterRules([]Machine{*p1, *p2}, dst)
|
||||
expectedRules := []tailcfg.FilterRule{}
|
||||
|
||||
assert.Equal(t, expectedRules, actualRules)
|
||||
}
|
||||
|
||||
func TestACLPolicy_BuildFilterRulesWildcards(t *testing.T) {
|
||||
p1 := createMachine("john@example.com")
|
||||
p2 := createMachine("jane@example.com")
|
||||
@@ -44,6 +157,60 @@ func TestACLPolicy_BuildFilterRulesWildcards(t *testing.T) {
|
||||
assert.Equal(t, expectedRules, actualRules)
|
||||
}
|
||||
|
||||
func TestACLPolicy_BuildFilterRulesProto(t *testing.T) {
|
||||
p1 := createMachine("john@example.com")
|
||||
p2 := createMachine("jane@example.com")
|
||||
|
||||
policy := ACLPolicy{
|
||||
ACLs: []ACL{
|
||||
{
|
||||
Action: "accept",
|
||||
Src: []string{"*"},
|
||||
Dst: []string{"*:22"},
|
||||
},
|
||||
{
|
||||
Action: "accept",
|
||||
Src: []string{"*"},
|
||||
Dst: []string{"*:*"},
|
||||
Proto: "igmp",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
dst := createMachine("john@example.com")
|
||||
|
||||
actualRules := policy.BuildFilterRules([]Machine{*p1, *p2}, dst)
|
||||
expectedRules := []tailcfg.FilterRule{
|
||||
{
|
||||
SrcIPs: []string{"*"},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "*",
|
||||
Ports: tailcfg.PortRange{
|
||||
First: 22,
|
||||
Last: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
SrcIPs: []string{"*"},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "*",
|
||||
Ports: tailcfg.PortRange{
|
||||
First: 0,
|
||||
Last: 65535,
|
||||
},
|
||||
},
|
||||
},
|
||||
IPProto: []int{protocolIGMP},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedRules, actualRules)
|
||||
}
|
||||
|
||||
func TestACLPolicy_BuildFilterRulesWithGroups(t *testing.T) {
|
||||
p1 := createMachine("jane@example.com")
|
||||
p2 := createMachine("nick@example.com")
|
||||
@@ -676,7 +843,7 @@ func TestACLPolicy_FindAutoApprovedIPs(t *testing.T) {
|
||||
name: "no match",
|
||||
userName: "nick@example.com",
|
||||
routableIPs: []netip.Prefix{route1, route2, route3},
|
||||
expected: []netip.Prefix{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "exit",
|
||||
@@ -688,7 +855,7 @@ func TestACLPolicy_FindAutoApprovedIPs(t *testing.T) {
|
||||
name: "exit no match",
|
||||
userName: "john@example.com",
|
||||
routableIPs: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")},
|
||||
expected: []netip.Prefix{},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -699,3 +866,123 @@ func TestACLPolicy_FindAutoApprovedIPs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestACLPolicy_BuildFilterRulesWithAdvertisedRoutes(t *testing.T) {
|
||||
route1 := netip.MustParsePrefix("fd7a:115c:a1e0:b1a:0:1:a3c:0/120")
|
||||
p1 := createMachine("john@example.com", "tag:trusted")
|
||||
|
||||
policy := ACLPolicy{
|
||||
ACLs: []ACL{
|
||||
{
|
||||
Action: "accept",
|
||||
Src: []string{"tag:trusted"},
|
||||
Dst: []string{"fd7a:115c:a1e0:b1a:0:1:a3c:0/120:*"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
dst := createMachine("john@example.com")
|
||||
dst.AllowIPs = []netip.Prefix{route1}
|
||||
|
||||
actualRules := policy.BuildFilterRules([]Machine{*p1}, dst)
|
||||
expectedRules := []tailcfg.FilterRule{
|
||||
{
|
||||
SrcIPs: p1.IPs(),
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: route1.String(),
|
||||
Ports: tailcfg.PortRange{
|
||||
First: 0,
|
||||
Last: 65535,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedRules, actualRules)
|
||||
}
|
||||
|
||||
func TestACLPolicy_BuildFilterRulesWildcardGrants(t *testing.T) {
|
||||
ranges, err := tailcfg.ParseProtoPortRanges([]string{"*"})
|
||||
require.NoError(t, err)
|
||||
|
||||
p1 := createMachine("john@example.com")
|
||||
p2 := createMachine("jane@example.com")
|
||||
|
||||
policy := ACLPolicy{
|
||||
Grants: []Grant{
|
||||
{
|
||||
Src: []string{"*"},
|
||||
Dst: []string{"*"},
|
||||
IP: ranges,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
dst := createMachine("john@example.com")
|
||||
|
||||
actualRules := policy.BuildFilterRules([]Machine{*p1, *p2}, dst)
|
||||
expectedRules := []tailcfg.FilterRule{
|
||||
{
|
||||
SrcIPs: []string{"*"},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "*",
|
||||
Ports: tailcfg.PortRange{
|
||||
First: 0,
|
||||
Last: 65535,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedRules, actualRules)
|
||||
}
|
||||
|
||||
func TestACLPolicy_BuildFilterRulesWithAppGrants(t *testing.T) {
|
||||
p1 := createMachine("john@example.com")
|
||||
p2 := createMachine("jane@example.com")
|
||||
|
||||
dst := createMachine("john@example.com")
|
||||
|
||||
mycap := map[string]interface{}{
|
||||
"channel": "alpha",
|
||||
"ids": []string{"1", "2", "3"},
|
||||
}
|
||||
|
||||
marshal, _ := json.Marshal(mycap)
|
||||
|
||||
policy := ACLPolicy{
|
||||
Grants: []Grant{
|
||||
{
|
||||
Src: []string{"*"},
|
||||
Dst: []string{"*"},
|
||||
App: map[tailcfg.PeerCapability][]tailcfg.RawMessage{
|
||||
tailcfg.PeerCapability("localtest.me/cap/test"): {tailcfg.RawMessage(marshal)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
actualRules := policy.BuildFilterRules([]Machine{*p1, *p2}, dst)
|
||||
expectedRules := []tailcfg.FilterRule{
|
||||
{
|
||||
SrcIPs: []string{"*"},
|
||||
CapGrant: []tailcfg.CapGrant{
|
||||
{
|
||||
Dsts: []netip.Prefix{
|
||||
netip.PrefixFrom(*dst.IPv4.Addr, 32),
|
||||
netip.PrefixFrom(*dst.IPv6.Addr, 128),
|
||||
},
|
||||
CapMap: map[tailcfg.PeerCapability][]tailcfg.RawMessage{
|
||||
tailcfg.PeerCapability("localtest.me/cap/test"): {tailcfg.RawMessage(marshal)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedRules, actualRules)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ type DNSConfig struct {
|
||||
OverrideLocalDNS bool `json:"override_local_dns"`
|
||||
Nameservers []string `json:"nameservers"`
|
||||
Routes map[string][]string `json:"routes"`
|
||||
SearchDomains []string `json:"search_domains"`
|
||||
}
|
||||
|
||||
func (i *DNSConfig) Scan(destination interface{}) error {
|
||||
|
||||
@@ -11,10 +11,6 @@ import (
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
func DefaultIAMPolicy() IAMPolicy {
|
||||
return IAMPolicy{}
|
||||
}
|
||||
|
||||
type Identity struct {
|
||||
UserID string
|
||||
Username string
|
||||
|
||||
@@ -310,7 +310,7 @@ func (HostInfo) GormDBDataType(db *gorm.DB, field *schema.Field) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type Endpoints []string
|
||||
type Endpoints []netip.AddrPort
|
||||
|
||||
func (hi *Endpoints) Scan(destination interface{}) error {
|
||||
switch value := destination.(type) {
|
||||
|
||||
@@ -27,7 +27,7 @@ type Repository interface {
|
||||
|
||||
SaveTailnet(ctx context.Context, tailnet *Tailnet) error
|
||||
GetTailnet(ctx context.Context, id uint64) (*Tailnet, error)
|
||||
GetTailnetByAlias(ctx context.Context, alias string) (*Tailnet, error)
|
||||
GetTailnetByName(ctx context.Context, name string) (*Tailnet, error)
|
||||
ListTailnets(ctx context.Context) ([]Tailnet, error)
|
||||
DeleteTailnet(ctx context.Context, id uint64) error
|
||||
|
||||
|
||||
@@ -72,9 +72,9 @@ func (r *repository) GetTailnet(ctx context.Context, id uint64) (*Tailnet, error
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *repository) GetTailnetByAlias(ctx context.Context, alias string) (*Tailnet, error) {
|
||||
func (r *repository) GetTailnetByName(ctx context.Context, name string) (*Tailnet, error) {
|
||||
var t Tailnet
|
||||
tx := r.withContext(ctx).Take(&t, "alias = ?", alias)
|
||||
tx := r.withContext(ctx).Take(&t, "name = ?", name)
|
||||
|
||||
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
|
||||
+123
-101
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/jsiebens/ionscale/internal/addr"
|
||||
"github.com/jsiebens/ionscale/internal/auth"
|
||||
tpl "github.com/jsiebens/ionscale/internal/templates"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/mr-tron/base58"
|
||||
"net/http"
|
||||
@@ -40,48 +41,71 @@ type AuthenticationHandlers struct {
|
||||
systemIAMPolicy *domain.IAMPolicy
|
||||
}
|
||||
|
||||
type AuthFormData struct {
|
||||
ProviderAvailable bool
|
||||
Csrf string
|
||||
type AuthInput struct {
|
||||
Key string `param:"key"`
|
||||
Flow AuthFlow `param:"flow"`
|
||||
AuthKey string `query:"ak" form:"ak"`
|
||||
Oidc bool `query:"oidc" form:"oidc"`
|
||||
}
|
||||
|
||||
type TailnetSelectionData struct {
|
||||
AccountID uint64
|
||||
Tailnets []domain.Tailnet
|
||||
SystemAdmin bool
|
||||
Csrf string
|
||||
type EndAuthForm struct {
|
||||
AccountID uint64 `form:"aid"`
|
||||
TailnetID uint64 `form:"tid"`
|
||||
AsSystemAdmin bool `form:"sad"`
|
||||
AuthKey string `form:"ak"`
|
||||
State string `form:"state"`
|
||||
}
|
||||
|
||||
type oauthState struct {
|
||||
Key string
|
||||
Flow string
|
||||
Flow AuthFlow
|
||||
}
|
||||
|
||||
type AuthFlow string
|
||||
|
||||
const (
|
||||
AuthFlowMachineRegistration = "r"
|
||||
AuthFlowClient = "c"
|
||||
AuthFlowSSHCheckFlow = "s"
|
||||
)
|
||||
|
||||
func (h *AuthenticationHandlers) StartAuth(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
flow := c.Param("flow")
|
||||
key := c.Param("key")
|
||||
|
||||
var input AuthInput
|
||||
if err := c.Bind(&input); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
// machine registration auth flow
|
||||
if flow == "r" || flow == "" {
|
||||
if req, err := h.repository.GetRegistrationRequestByKey(ctx, key); err != nil || req == nil {
|
||||
if input.Flow == AuthFlowMachineRegistration {
|
||||
req, err := h.repository.GetRegistrationRequestByKey(ctx, input.Key)
|
||||
if err != nil || req == nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
if input.Oidc && h.authProvider != nil {
|
||||
goto startOidc
|
||||
}
|
||||
|
||||
if input.AuthKey != "" {
|
||||
return h.endMachineRegistrationFlow(c, EndAuthForm{AuthKey: input.AuthKey}, req)
|
||||
}
|
||||
|
||||
csrf := c.Get(middleware.DefaultCSRFConfig.ContextKey).(string)
|
||||
return c.Render(http.StatusOK, "auth.html", &AuthFormData{ProviderAvailable: h.authProvider != nil, Csrf: csrf})
|
||||
return c.Render(http.StatusOK, "", tpl.Auth(h.authProvider != nil, csrf))
|
||||
}
|
||||
|
||||
// cli auth flow
|
||||
if flow == "c" {
|
||||
if s, err := h.repository.GetAuthenticationRequest(ctx, key); err != nil || s == nil {
|
||||
if input.Flow == AuthFlowClient {
|
||||
if s, err := h.repository.GetAuthenticationRequest(ctx, input.Key); err != nil || s == nil {
|
||||
return logError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ssh check auth flow
|
||||
if flow == "s" {
|
||||
if s, err := h.repository.GetSSHActionRequest(ctx, key); err != nil || s == nil {
|
||||
if input.Flow == AuthFlowSSHCheckFlow {
|
||||
if s, err := h.repository.GetSSHActionRequest(ctx, input.Key); err != nil || s == nil {
|
||||
return logError(err)
|
||||
}
|
||||
}
|
||||
@@ -90,7 +114,9 @@ func (h *AuthenticationHandlers) StartAuth(c echo.Context) error {
|
||||
return logError(fmt.Errorf("unable to start auth flow as no auth provider is configured"))
|
||||
}
|
||||
|
||||
state, err := h.createState(flow, key)
|
||||
startOidc:
|
||||
|
||||
state, err := h.createState(input.Flow, input.Key)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
@@ -103,21 +129,22 @@ func (h *AuthenticationHandlers) StartAuth(c echo.Context) error {
|
||||
func (h *AuthenticationHandlers) ProcessAuth(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
key := c.Param("key")
|
||||
authKey := c.FormValue("ak")
|
||||
interactive := c.FormValue("s")
|
||||
var input AuthInput
|
||||
if err := c.Bind(&input); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
req, err := h.repository.GetRegistrationRequestByKey(ctx, key)
|
||||
req, err := h.repository.GetRegistrationRequestByKey(ctx, input.Key)
|
||||
if err != nil || req == nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
if authKey != "" {
|
||||
return h.endMachineRegistrationFlow(c, req, &oauthState{Key: key})
|
||||
if input.AuthKey != "" {
|
||||
return h.endMachineRegistrationFlow(c, EndAuthForm{AuthKey: input.AuthKey}, req)
|
||||
}
|
||||
|
||||
if interactive != "" {
|
||||
state, err := h.createState("r", key)
|
||||
if input.Oidc {
|
||||
state, err := h.createState(input.Flow, input.Key)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
@@ -127,7 +154,7 @@ func (h *AuthenticationHandlers) ProcessAuth(c echo.Context) error {
|
||||
return c.Redirect(http.StatusFound, redirectUrl)
|
||||
}
|
||||
|
||||
return c.Redirect(http.StatusFound, "/a/"+key)
|
||||
return c.Redirect(http.StatusFound, fmt.Sprintf("/a/%s/%s", input.Flow, input.Key))
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) Callback(c echo.Context) error {
|
||||
@@ -153,7 +180,7 @@ func (h *AuthenticationHandlers) Callback(c echo.Context) error {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
if state.Flow == "s" {
|
||||
if state.Flow == AuthFlowSSHCheckFlow {
|
||||
sshActionReq, err := h.repository.GetSSHActionRequest(ctx, state.Key)
|
||||
if err != nil || sshActionReq == nil {
|
||||
return c.Redirect(http.StatusFound, "/a/error?e=ua")
|
||||
@@ -186,7 +213,7 @@ func (h *AuthenticationHandlers) Callback(c echo.Context) error {
|
||||
|
||||
csrf := c.Get(middleware.DefaultCSRFConfig.ContextKey).(string)
|
||||
|
||||
if state.Flow == "r" {
|
||||
if state.Flow == AuthFlowMachineRegistration {
|
||||
if len(tailnets) == 0 {
|
||||
registrationRequest, err := h.repository.GetRegistrationRequestByKey(ctx, state.Key)
|
||||
if err == nil && registrationRequest != nil {
|
||||
@@ -195,16 +222,23 @@ func (h *AuthenticationHandlers) Callback(c echo.Context) error {
|
||||
}
|
||||
return c.Redirect(http.StatusFound, "/a/error?e=ua")
|
||||
}
|
||||
return c.Render(http.StatusOK, "tailnets.html", &TailnetSelectionData{
|
||||
Csrf: csrf,
|
||||
Tailnets: tailnets,
|
||||
SystemAdmin: false,
|
||||
AccountID: account.ID,
|
||||
})
|
||||
|
||||
if len(tailnets) == 1 {
|
||||
req, err := h.repository.GetRegistrationRequestByKey(ctx, state.Key)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
if req == nil {
|
||||
return logError(fmt.Errorf("invalid registration key"))
|
||||
}
|
||||
return h.endMachineRegistrationFlow(c, EndAuthForm{AccountID: account.ID, TailnetID: tailnets[0].ID}, req)
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "", tpl.Tailnets(account.ID, false, tailnets, csrf))
|
||||
}
|
||||
|
||||
if state.Flow == "c" {
|
||||
isSystemAdmin, err := h.isSystemAdmin(ctx, user)
|
||||
if state.Flow == AuthFlowClient {
|
||||
isSystemAdmin, err := h.isSystemAdmin(user)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
@@ -217,103 +251,74 @@ func (h *AuthenticationHandlers) Callback(c echo.Context) error {
|
||||
}
|
||||
return c.Redirect(http.StatusFound, "/a/error?e=ua")
|
||||
}
|
||||
return c.Render(http.StatusOK, "tailnets.html", &TailnetSelectionData{
|
||||
Csrf: csrf,
|
||||
Tailnets: tailnets,
|
||||
SystemAdmin: isSystemAdmin,
|
||||
AccountID: account.ID,
|
||||
})
|
||||
|
||||
return c.Render(http.StatusOK, "", tpl.Tailnets(account.ID, isSystemAdmin, tailnets, csrf))
|
||||
}
|
||||
|
||||
return echo.NewHTTPError(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) isSystemAdmin(ctx context.Context, u *auth.User) (bool, error) {
|
||||
return h.systemIAMPolicy.EvaluatePolicy(&domain.Identity{UserID: u.ID, Email: u.Name, Attr: u.Attr})
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) listAvailableTailnets(ctx context.Context, u *auth.User) ([]domain.Tailnet, error) {
|
||||
var result = []domain.Tailnet{}
|
||||
tailnets, err := h.repository.ListTailnets(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, t := range tailnets {
|
||||
approved, err := t.IAMPolicy.EvaluatePolicy(&domain.Identity{UserID: u.ID, Email: u.Name, Attr: u.Attr})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if approved {
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) EndOAuth(c echo.Context) error {
|
||||
func (h *AuthenticationHandlers) EndAuth(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
state, err := h.readState(c.QueryParam("state"))
|
||||
var form EndAuthForm
|
||||
if err := c.Bind(&form); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
state, err := h.readState(form.State)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Invalid state parameter")
|
||||
}
|
||||
|
||||
if state.Flow == "r" {
|
||||
if state.Flow == AuthFlowMachineRegistration {
|
||||
req, err := h.repository.GetRegistrationRequestByKey(ctx, state.Key)
|
||||
if err != nil || req == nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
return h.endMachineRegistrationFlow(c, req, state)
|
||||
return h.endMachineRegistrationFlow(c, form, req)
|
||||
}
|
||||
|
||||
req, err := h.repository.GetAuthenticationRequest(ctx, state.Key)
|
||||
if err != nil || req == nil {
|
||||
return logError(err)
|
||||
if state.Flow == AuthFlowClient {
|
||||
req, err := h.repository.GetAuthenticationRequest(ctx, state.Key)
|
||||
if err != nil || req == nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
return h.endCliAuthenticationFlow(c, form, req)
|
||||
}
|
||||
|
||||
return h.endCliAuthenticationFlow(c, req, state)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Invalid state parameter")
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) Success(c echo.Context) error {
|
||||
s := c.QueryParam("s")
|
||||
switch s {
|
||||
case "nma":
|
||||
return c.Render(http.StatusOK, "newmachine.html", nil)
|
||||
return c.Render(http.StatusOK, "", tpl.NewMachine())
|
||||
}
|
||||
return c.Render(http.StatusOK, "success.html", nil)
|
||||
return c.Render(http.StatusOK, "", tpl.Success())
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) Error(c echo.Context) error {
|
||||
e := c.QueryParam("e")
|
||||
switch e {
|
||||
case "iak":
|
||||
return c.Render(http.StatusForbidden, "invalidauthkey.html", nil)
|
||||
return c.Render(http.StatusForbidden, "", tpl.InvalidAuthKey())
|
||||
case "ua":
|
||||
return c.Render(http.StatusForbidden, "unauthorized.html", nil)
|
||||
return c.Render(http.StatusForbidden, "", tpl.Unauthorized())
|
||||
case "nto":
|
||||
return c.Render(http.StatusForbidden, "notagowner.html", nil)
|
||||
return c.Render(http.StatusForbidden, "", tpl.NotTagOwner())
|
||||
case "nmo":
|
||||
return c.Render(http.StatusForbidden, "notmachineowner.html", nil)
|
||||
return c.Render(http.StatusForbidden, "", tpl.NotMachineOwner())
|
||||
}
|
||||
return c.Render(http.StatusOK, "error.html", nil)
|
||||
return c.Render(http.StatusOK, "", tpl.Error())
|
||||
}
|
||||
|
||||
type TailnetSelectionForm struct {
|
||||
AccountID uint64 `form:"aid"`
|
||||
TailnetID uint64 `form:"tid"`
|
||||
AsSystemAdmin bool `form:"sad"`
|
||||
AuthKey string `form:"ak"`
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) endCliAuthenticationFlow(c echo.Context, req *domain.AuthenticationRequest, state *oauthState) error {
|
||||
func (h *AuthenticationHandlers) endCliAuthenticationFlow(c echo.Context, form EndAuthForm, req *domain.AuthenticationRequest) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
var form TailnetSelectionForm
|
||||
if err := c.Bind(&form); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
account, err := h.repository.GetAccount(ctx, form.AccountID)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
@@ -371,14 +376,9 @@ func (h *AuthenticationHandlers) endCliAuthenticationFlow(c echo.Context, req *d
|
||||
return c.Redirect(http.StatusFound, "/a/success")
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, registrationRequest *domain.RegistrationRequest, state *oauthState) error {
|
||||
func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, form EndAuthForm, registrationRequest *domain.RegistrationRequest) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
var form TailnetSelectionForm
|
||||
if err := c.Bind(&form); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
req := tailcfg.RegisterRequest(registrationRequest.Data)
|
||||
machineKey := registrationRequest.MachineKey
|
||||
nodeKey := req.NodeKey.String()
|
||||
@@ -542,6 +542,28 @@ func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, regi
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) isSystemAdmin(u *auth.User) (bool, error) {
|
||||
return h.systemIAMPolicy.EvaluatePolicy(&domain.Identity{UserID: u.ID, Email: u.Name, Attr: u.Attr})
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) listAvailableTailnets(ctx context.Context, u *auth.User) ([]domain.Tailnet, error) {
|
||||
var result = []domain.Tailnet{}
|
||||
tailnets, err := h.repository.ListTailnets(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, t := range tailnets {
|
||||
approved, err := t.IAMPolicy.EvaluatePolicy(&domain.Identity{UserID: u.ID, Email: u.Name, Attr: u.Attr})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if approved {
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) exchangeUser(code string) (*auth.User, error) {
|
||||
redirectUrl := h.config.CreateUrl("/a/callback")
|
||||
|
||||
@@ -553,7 +575,7 @@ func (h *AuthenticationHandlers) exchangeUser(code string) (*auth.User, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (h *AuthenticationHandlers) createState(flow string, key string) (string, error) {
|
||||
func (h *AuthenticationHandlers) createState(flow AuthFlow, key string) (string, error) {
|
||||
stateMap := oauthState{Key: key, Flow: flow}
|
||||
marshal, err := json.Marshal(&stateMap)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,38 +1,31 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/jsiebens/ionscale/internal/bind"
|
||||
"github.com/jsiebens/ionscale/internal/dns"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewDNSHandlers(createBinder bind.Factory, provider dns.Provider) *DNSHandlers {
|
||||
func NewDNSHandlers(_ key.MachinePublic, provider dns.Provider) *DNSHandlers {
|
||||
return &DNSHandlers{
|
||||
createBinder: createBinder,
|
||||
provider: provider,
|
||||
provider: provider,
|
||||
}
|
||||
}
|
||||
|
||||
type DNSHandlers struct {
|
||||
createBinder bind.Factory
|
||||
provider dns.Provider
|
||||
provider dns.Provider
|
||||
}
|
||||
|
||||
func (h *DNSHandlers) SetDNS(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
binder, err := h.createBinder(c)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
req := &tailcfg.SetDNSRequest{}
|
||||
if err := binder.BindRequest(c, req); err != nil {
|
||||
if err := c.Bind(req); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
@@ -58,16 +51,16 @@ func (h *DNSHandlers) SetDNS(c echo.Context) error {
|
||||
txtrecords, _ := net.LookupTXT(req.Name)
|
||||
for _, txt := range txtrecords {
|
||||
if txt == req.Value {
|
||||
return binder.WriteResponse(c, http.StatusOK, tailcfg.SetDNSResponse{})
|
||||
return c.JSON(http.StatusOK, tailcfg.SetDNSResponse{})
|
||||
}
|
||||
}
|
||||
case <-timeout:
|
||||
return binder.WriteResponse(c, http.StatusOK, tailcfg.SetDNSResponse{})
|
||||
return c.JSON(http.StatusOK, tailcfg.SetDNSResponse{})
|
||||
case <-notify:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return binder.WriteResponse(c, http.StatusOK, tailcfg.SetDNSResponse{})
|
||||
return c.JSON(http.StatusOK, tailcfg.SetDNSResponse{})
|
||||
}
|
||||
|
||||
@@ -4,63 +4,36 @@ import (
|
||||
"fmt"
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/jsiebens/ionscale/internal/bind"
|
||||
"github.com/jsiebens/ionscale/internal/config"
|
||||
"github.com/jsiebens/ionscale/internal/domain"
|
||||
"github.com/jsiebens/ionscale/internal/util"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewIDTokenHandlers(createBinder bind.Factory, config *config.Config, repository domain.Repository) *IDTokenHandlers {
|
||||
func NewIDTokenHandlers(machineKey key.MachinePublic, config *config.Config, repository domain.Repository) *IDTokenHandlers {
|
||||
return &IDTokenHandlers{
|
||||
issuer: config.ServerUrl,
|
||||
jwksUri: config.CreateUrl("/.well-known/jwks"),
|
||||
createBinder: createBinder,
|
||||
repository: repository,
|
||||
machineKey: machineKey,
|
||||
issuer: config.ServerUrl,
|
||||
repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
func NewOIDCConfigHandlers(config *config.Config, repository domain.Repository) *OIDCConfigHandlers {
|
||||
return &OIDCConfigHandlers{
|
||||
issuer: config.ServerUrl,
|
||||
jwksUri: config.CreateUrl("/.well-known/jwks"),
|
||||
repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
type IDTokenHandlers struct {
|
||||
issuer string
|
||||
jwksUri string
|
||||
createBinder bind.Factory
|
||||
repository domain.Repository
|
||||
}
|
||||
|
||||
func (h *IDTokenHandlers) OpenIDConfig(c echo.Context) error {
|
||||
v := map[string]interface{}{}
|
||||
|
||||
v["issuer"] = h.issuer
|
||||
v["jwks_uri"] = h.jwksUri
|
||||
v["subject_types_supported"] = []string{"public"}
|
||||
v["response_types_supported"] = []string{"id_token"}
|
||||
v["scopes_supported"] = []string{"openid"}
|
||||
v["id_token_signing_alg_values_supported"] = []string{"RS256"}
|
||||
v["claims_supported"] = []string{
|
||||
"sub",
|
||||
"aud",
|
||||
"exp",
|
||||
"iat",
|
||||
"iss",
|
||||
"jti",
|
||||
"nbf",
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, v)
|
||||
}
|
||||
|
||||
func (h *IDTokenHandlers) Jwks(c echo.Context) error {
|
||||
keySet, err := h.repository.GetJSONWebKeySet(c.Request().Context())
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
pub := jose.JSONWebKey{Key: keySet.Key.Public(), KeyID: keySet.Key.Id, Algorithm: "RS256", Use: "sig"}
|
||||
set := jose.JSONWebKeySet{Keys: []jose.JSONWebKey{pub}}
|
||||
return c.JSON(http.StatusOK, set)
|
||||
machineKey key.MachinePublic
|
||||
issuer string
|
||||
repository domain.Repository
|
||||
}
|
||||
|
||||
func (h *IDTokenHandlers) FetchToken(c echo.Context) error {
|
||||
@@ -71,17 +44,12 @@ func (h *IDTokenHandlers) FetchToken(c echo.Context) error {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
binder, err := h.createBinder(c)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
req := &tailcfg.TokenRequest{}
|
||||
if err := binder.BindRequest(c, req); err != nil {
|
||||
if err := c.Bind(req); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
machineKey := binder.Peer().String()
|
||||
machineKey := h.machineKey.String()
|
||||
nodeKey := req.NodeKey.String()
|
||||
|
||||
var m *domain.Machine
|
||||
@@ -134,7 +102,46 @@ func (h *IDTokenHandlers) FetchToken(c echo.Context) error {
|
||||
}
|
||||
|
||||
resp := tailcfg.TokenResponse{IDToken: jwtB64}
|
||||
return binder.WriteResponse(c, http.StatusOK, resp)
|
||||
return c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
type OIDCConfigHandlers struct {
|
||||
issuer string
|
||||
jwksUri string
|
||||
repository domain.Repository
|
||||
}
|
||||
|
||||
func (h *OIDCConfigHandlers) OpenIDConfig(c echo.Context) error {
|
||||
v := map[string]interface{}{}
|
||||
|
||||
v["issuer"] = h.issuer
|
||||
v["jwks_uri"] = h.jwksUri
|
||||
v["subject_types_supported"] = []string{"public"}
|
||||
v["response_types_supported"] = []string{"id_token"}
|
||||
v["scopes_supported"] = []string{"openid"}
|
||||
v["id_token_signing_alg_values_supported"] = []string{"RS256"}
|
||||
v["claims_supported"] = []string{
|
||||
"sub",
|
||||
"aud",
|
||||
"exp",
|
||||
"iat",
|
||||
"iss",
|
||||
"jti",
|
||||
"nbf",
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, v)
|
||||
}
|
||||
|
||||
func (h *OIDCConfigHandlers) Jwks(c echo.Context) error {
|
||||
keySet, err := h.repository.GetJSONWebKeySet(c.Request().Context())
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
pub := jose.JSONWebKey{Key: keySet.Key.Public(), KeyID: keySet.Key.Id, Algorithm: "RS256", Use: "sig"}
|
||||
set := jose.JSONWebKeySet{Keys: []jose.JSONWebKey{pub}}
|
||||
return c.JSON(http.StatusOK, set)
|
||||
}
|
||||
|
||||
func (h *IDTokenHandlers) names(m *domain.Machine) (string, string, string) {
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
tpl "github.com/jsiebens/ionscale/internal/templates"
|
||||
"github.com/jsiebens/ionscale/internal/version"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func IndexHandler(code int) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
info, s := version.GetReleaseInfo()
|
||||
data := map[string]interface{}{
|
||||
"Version": info,
|
||||
"Revision": s,
|
||||
}
|
||||
return c.Render(code, "index.html", data)
|
||||
return c.Render(code, "", tpl.Index(version.GetReleaseInfo()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,3 +41,35 @@ func (h *NoiseHandlers) Upgrade(c echo.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type JsonBinder struct {
|
||||
echo.DefaultBinder
|
||||
}
|
||||
|
||||
func (b JsonBinder) Bind(i interface{}, c echo.Context) error {
|
||||
if err := b.BindPathParams(c, i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
method := c.Request().Method
|
||||
if method == http.MethodGet || method == http.MethodDelete || method == http.MethodHead {
|
||||
if err := b.BindQueryParams(c, i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.Request().ContentLength == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.Echo().JSONSerializer.Deserialize(c, i); err != nil {
|
||||
switch err.(type) {
|
||||
case *echo.HTTPError:
|
||||
return err
|
||||
default:
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,26 +2,29 @@ package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/jsiebens/ionscale/internal/bind"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"github.com/jsiebens/ionscale/internal/config"
|
||||
"github.com/jsiebens/ionscale/internal/core"
|
||||
"github.com/jsiebens/ionscale/internal/domain"
|
||||
"github.com/jsiebens/ionscale/internal/mapping"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"tailscale.com/smallzstd"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/opt"
|
||||
"tailscale.com/types/key"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewPollNetMapHandler(
|
||||
createBinder bind.Factory,
|
||||
machineKey key.MachinePublic,
|
||||
sessionManager core.PollMapSessionManager,
|
||||
repository domain.Repository) *PollNetMapHandler {
|
||||
|
||||
handler := &PollNetMapHandler{
|
||||
createBinder: createBinder,
|
||||
machineKey: machineKey,
|
||||
sessionManager: sessionManager,
|
||||
repository: repository,
|
||||
}
|
||||
@@ -30,28 +33,24 @@ func NewPollNetMapHandler(
|
||||
}
|
||||
|
||||
type PollNetMapHandler struct {
|
||||
createBinder bind.Factory
|
||||
machineKey key.MachinePublic
|
||||
repository domain.Repository
|
||||
sessionManager core.PollMapSessionManager
|
||||
}
|
||||
|
||||
func (h *PollNetMapHandler) PollNetMap(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
binder, err := h.createBinder(c)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
req := &tailcfg.MapRequest{}
|
||||
if err := binder.BindRequest(c, req); err != nil {
|
||||
if err := c.Bind(req); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
machineKey := binder.Peer().String()
|
||||
machineKey := h.machineKey.String()
|
||||
nodeKey := req.NodeKey.String()
|
||||
|
||||
var m *domain.Machine
|
||||
m, err = h.repository.GetMachineByKeys(ctx, machineKey, nodeKey)
|
||||
m, err := h.repository.GetMachineByKeys(ctx, machineKey, nodeKey)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
@@ -61,13 +60,13 @@ func (h *PollNetMapHandler) PollNetMap(c echo.Context) error {
|
||||
}
|
||||
|
||||
if req.ReadOnly {
|
||||
return h.handleReadOnly(c, binder, m, req)
|
||||
return h.handleReadOnly(c, m, req)
|
||||
} else {
|
||||
return h.handleUpdate(c, binder, m, req)
|
||||
return h.handleUpdate(c, m, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *domain.Machine, mapRequest *tailcfg.MapRequest) error {
|
||||
func (h *PollNetMapHandler) handleUpdate(c echo.Context, m *domain.Machine, mapRequest *tailcfg.MapRequest) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
now := time.Now().UTC()
|
||||
@@ -84,16 +83,15 @@ func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *
|
||||
tailnetID := m.TailnetID
|
||||
machineID := m.ID
|
||||
|
||||
h.sessionManager.NotifyAll(tailnetID)
|
||||
h.sessionManager.NotifyAll(tailnetID, m.ID)
|
||||
|
||||
if !mapRequest.Stream {
|
||||
return c.String(http.StatusOK, "")
|
||||
}
|
||||
|
||||
var syncedPeers = make(map[uint64]bool)
|
||||
var derpMapChecksum = ""
|
||||
mapper := mapping.NewPollNetMapper(mapRequest, m.ID, h.repository, h.sessionManager)
|
||||
|
||||
response, syncedPeers, derpMapChecksum, err := h.createMapResponse(m, binder, mapRequest, false, make(map[uint64]bool), derpMapChecksum)
|
||||
response, err := h.createMapResponse(mapper, false, mapRequest.Compress)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
@@ -104,7 +102,7 @@ func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *
|
||||
// Listen to connection close
|
||||
notify := c.Request().Context().Done()
|
||||
|
||||
keepAliveResponse, err := h.createKeepAliveResponse(binder, mapRequest)
|
||||
keepAliveResponse, err := h.createKeepAliveResponse(mapRequest)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
@@ -157,7 +155,7 @@ func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *
|
||||
var payload []byte
|
||||
var payloadErr error
|
||||
|
||||
payload, syncedPeers, derpMapChecksum, payloadErr = h.createMapResponse(machine, binder, mapRequest, true, syncedPeers, derpMapChecksum)
|
||||
payload, payloadErr = h.createMapResponse(mapper, true, mapRequest.Compress)
|
||||
|
||||
if payloadErr != nil {
|
||||
return payloadErr
|
||||
@@ -176,7 +174,7 @@ func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *
|
||||
}
|
||||
}
|
||||
|
||||
func (h *PollNetMapHandler) handleReadOnly(c echo.Context, binder bind.Binder, m *domain.Machine, request *tailcfg.MapRequest) error {
|
||||
func (h *PollNetMapHandler) handleReadOnly(c echo.Context, m *domain.Machine, request *tailcfg.MapRequest) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
m.HostInfo = domain.HostInfo(*request.Hostinfo)
|
||||
@@ -186,167 +184,67 @@ func (h *PollNetMapHandler) handleReadOnly(c echo.Context, binder bind.Binder, m
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
response, _, _, err := h.createMapResponse(m, binder, request, false, map[uint64]bool{}, "")
|
||||
mapper := mapping.NewPollNetMapper(request, m.ID, h.repository, h.sessionManager)
|
||||
payload, err := h.createMapResponse(mapper, false, request.Compress)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
_, err = c.Response().Write(response)
|
||||
_, err = c.Response().Write(payload)
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
func (h *PollNetMapHandler) createKeepAliveResponse(binder bind.Binder, request *tailcfg.MapRequest) ([]byte, error) {
|
||||
func (h *PollNetMapHandler) createKeepAliveResponse(request *tailcfg.MapRequest) ([]byte, error) {
|
||||
mapResponse := &tailcfg.MapResponse{
|
||||
KeepAlive: true,
|
||||
}
|
||||
|
||||
return binder.Marshal(request.Compress, mapResponse)
|
||||
return h.marshalResponse(request.Compress, mapResponse)
|
||||
}
|
||||
|
||||
func (h *PollNetMapHandler) createMapResponse(m *domain.Machine, binder bind.Binder, request *tailcfg.MapRequest, delta bool, prevSyncedPeerIDs map[uint64]bool, prevDerpMapChecksum string) ([]byte, map[uint64]bool, string, error) {
|
||||
ctx := context.TODO()
|
||||
|
||||
prc := &primaryRoutesCollector{flagged: map[netip.Prefix]bool{}}
|
||||
|
||||
tailnet, err := h.repository.GetTailnet(ctx, m.TailnetID)
|
||||
func (h *PollNetMapHandler) createMapResponse(m *mapping.PollNetMapper, delta bool, compress string) ([]byte, error) {
|
||||
response, err := m.CreateMapResponse(context.Background(), delta)
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
return h.marshalResponse(compress, response)
|
||||
}
|
||||
|
||||
serviceUser, _, err := h.repository.GetOrCreateServiceUser(ctx, tailnet)
|
||||
func (h *PollNetMapHandler) marshalResponse(compress string, v interface{}) ([]byte, error) {
|
||||
var payload []byte
|
||||
|
||||
marshalled, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostinfo := tailcfg.Hostinfo(m.HostInfo)
|
||||
node, user, err := mapping.ToNode(m, tailnet, serviceUser, false, true, prc.filter)
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
policies := tailnet.ACLPolicy
|
||||
var users = []tailcfg.UserProfile{*user}
|
||||
var changedPeers []*tailcfg.Node
|
||||
var removedPeers []tailcfg.NodeID
|
||||
|
||||
candidatePeers, err := h.repository.ListMachinePeers(ctx, m.TailnetID, m.MachineKey)
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
syncedPeerIDs := map[uint64]bool{}
|
||||
syncedUserIDs := map[tailcfg.UserID]bool{user.ID: true}
|
||||
|
||||
for _, peer := range candidatePeers {
|
||||
if peer.IsExpired() {
|
||||
continue
|
||||
}
|
||||
if policies.IsValidPeer(m, &peer) || policies.IsValidPeer(&peer, m) {
|
||||
isConnected := h.sessionManager.HasSession(peer.TailnetID, peer.ID)
|
||||
|
||||
n, u, err := mapping.ToNode(&peer, tailnet, serviceUser, true, isConnected, prc.filter)
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
}
|
||||
changedPeers = append(changedPeers, n)
|
||||
syncedPeerIDs[peer.ID] = true
|
||||
delete(prevSyncedPeerIDs, peer.ID)
|
||||
|
||||
if _, ok := syncedUserIDs[u.ID]; !ok {
|
||||
users = append(users, *u)
|
||||
syncedUserIDs[u.ID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for p, _ := range prevSyncedPeerIDs {
|
||||
removedPeers = append(removedPeers, tailcfg.NodeID(p))
|
||||
}
|
||||
|
||||
dnsConfig := tailnet.DNSConfig
|
||||
|
||||
derpMap, err := m.Tailnet.GetDERPMap(ctx, h.repository)
|
||||
if err != nil {
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
filterRules := policies.BuildFilterRules(candidatePeers, m)
|
||||
|
||||
controlTime := time.Now().UTC()
|
||||
var mapResponse *tailcfg.MapResponse
|
||||
|
||||
if !delta {
|
||||
mapResponse = &tailcfg.MapResponse{
|
||||
KeepAlive: false,
|
||||
Node: node,
|
||||
DNSConfig: mapping.ToDNSConfig(m, &m.Tailnet, &dnsConfig),
|
||||
PacketFilter: filterRules,
|
||||
DERPMap: &derpMap.DERPMap,
|
||||
Domain: domain.SanitizeTailnetName(m.Tailnet.Name),
|
||||
Peers: changedPeers,
|
||||
UserProfiles: users,
|
||||
ControlTime: &controlTime,
|
||||
CollectServices: optBool(tailnet.ServiceCollectionEnabled),
|
||||
Debug: &tailcfg.Debug{
|
||||
DisableLogTail: true,
|
||||
},
|
||||
}
|
||||
if compress == "zstd" {
|
||||
payload = zstdEncode(marshalled)
|
||||
} else {
|
||||
mapResponse = &tailcfg.MapResponse{
|
||||
Node: node,
|
||||
DNSConfig: mapping.ToDNSConfig(m, &m.Tailnet, &dnsConfig),
|
||||
PacketFilter: filterRules,
|
||||
Domain: domain.SanitizeTailnetName(m.Tailnet.Name),
|
||||
PeersChanged: changedPeers,
|
||||
PeersRemoved: removedPeers,
|
||||
UserProfiles: users,
|
||||
ControlTime: &controlTime,
|
||||
CollectServices: optBool(tailnet.ServiceCollectionEnabled),
|
||||
}
|
||||
|
||||
if prevDerpMapChecksum != derpMap.Checksum {
|
||||
mapResponse.DERPMap = &derpMap.DERPMap
|
||||
}
|
||||
payload = marshalled
|
||||
}
|
||||
|
||||
if tailnet.SSHEnabled && hostinfo.TailscaleSSHEnabled() {
|
||||
mapResponse.SSHPolicy = policies.BuildSSHPolicy(candidatePeers, m)
|
||||
}
|
||||
data := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(data, uint32(len(payload)))
|
||||
data = append(data, payload...)
|
||||
|
||||
if request.OmitPeers {
|
||||
mapResponse.PeersChanged = nil
|
||||
mapResponse.PeersRemoved = nil
|
||||
mapResponse.Peers = nil
|
||||
}
|
||||
|
||||
payload, err := binder.Marshal(request.Compress, mapResponse)
|
||||
|
||||
return payload, syncedPeerIDs, derpMap.Checksum, nil
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func optBool(v bool) opt.Bool {
|
||||
b := opt.Bool("")
|
||||
b.Set(v)
|
||||
return b
|
||||
func zstdEncode(in []byte) []byte {
|
||||
encoder := zstdEncoderPool.Get().(*zstd.Encoder)
|
||||
out := encoder.EncodeAll(in, nil)
|
||||
_ = encoder.Close()
|
||||
zstdEncoderPool.Put(encoder)
|
||||
return out
|
||||
}
|
||||
|
||||
type primaryRoutesCollector struct {
|
||||
flagged map[netip.Prefix]bool
|
||||
}
|
||||
|
||||
func (p *primaryRoutesCollector) filter(m *domain.Machine) []netip.Prefix {
|
||||
var result = []netip.Prefix{}
|
||||
for _, r := range m.AllowIPs {
|
||||
if _, ok := p.flagged[r]; r.Bits() != 0 && !ok {
|
||||
result = append(result, r)
|
||||
p.flagged[r] = true
|
||||
var zstdEncoderPool = &sync.Pool{
|
||||
New: func() any {
|
||||
encoder, err := smallzstd.NewEncoder(nil, zstd.WithEncoderLevel(zstd.SpeedFastest))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
for _, r := range m.AutoAllowIPs {
|
||||
if _, ok := p.flagged[r]; r.Bits() != 0 && !ok {
|
||||
result = append(result, r)
|
||||
p.flagged[r] = true
|
||||
}
|
||||
}
|
||||
return result
|
||||
return encoder
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,44 +2,40 @@ package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jsiebens/ionscale/internal/bind"
|
||||
"github.com/jsiebens/ionscale/internal/dns"
|
||||
"github.com/jsiebens/ionscale/internal/domain"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
func NewQueryFeatureHandlers(createBinder bind.Factory, dnsProvider dns.Provider, repository domain.Repository) *QueryFeatureHandlers {
|
||||
func NewQueryFeatureHandlers(machineKey key.MachinePublic, dnsProvider dns.Provider, repository domain.Repository) *QueryFeatureHandlers {
|
||||
return &QueryFeatureHandlers{
|
||||
createBinder: createBinder,
|
||||
repository: repository,
|
||||
machineKey: machineKey,
|
||||
dnsProvider: dnsProvider,
|
||||
repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
type QueryFeatureHandlers struct {
|
||||
createBinder bind.Factory
|
||||
dnsProvider dns.Provider
|
||||
repository domain.Repository
|
||||
machineKey key.MachinePublic
|
||||
dnsProvider dns.Provider
|
||||
repository domain.Repository
|
||||
}
|
||||
|
||||
func (h *QueryFeatureHandlers) QueryFeature(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
binder, err := h.createBinder(c)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
req := new(tailcfg.QueryFeatureRequest)
|
||||
if err := binder.BindRequest(c, req); err != nil {
|
||||
if err := c.Bind(req); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
machineKey := binder.Peer().String()
|
||||
machineKey := h.machineKey.String()
|
||||
nodeKey := req.NodeKey.String()
|
||||
|
||||
resp := tailcfg.QueryFeatureResponse{}
|
||||
resp := tailcfg.QueryFeatureResponse{Complete: true}
|
||||
|
||||
switch req.Feature {
|
||||
case "serve":
|
||||
@@ -52,16 +48,19 @@ func (h *QueryFeatureHandlers) QueryFeature(c echo.Context) error {
|
||||
return echo.NewHTTPError(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if h.dnsProvider == nil || machine.Tailnet.DNSConfig.HttpsCertsEnabled {
|
||||
if h.dnsProvider == nil || !machine.Tailnet.DNSConfig.HttpsCertsEnabled {
|
||||
resp.Text = fmt.Sprintf(serverMessage, machine.Tailnet.Name)
|
||||
resp.Complete = false
|
||||
}
|
||||
case "funnel":
|
||||
resp.Text = fmt.Sprintf("Sorry, ionscale has no support for feature '%s'\n", req.Feature)
|
||||
resp.Complete = false
|
||||
default:
|
||||
resp.Text = fmt.Sprintf("Unknown feature request '%s'\n", req.Feature)
|
||||
resp.Complete = false
|
||||
}
|
||||
|
||||
return binder.WriteResponse(c, http.StatusOK, resp)
|
||||
return c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
const serverMessage = `Enabling HTTPS is required to use Serve:
|
||||
|
||||
@@ -3,7 +3,6 @@ package handlers
|
||||
import (
|
||||
"context"
|
||||
"github.com/jsiebens/ionscale/internal/addr"
|
||||
"github.com/jsiebens/ionscale/internal/bind"
|
||||
"github.com/jsiebens/ionscale/internal/config"
|
||||
"github.com/jsiebens/ionscale/internal/core"
|
||||
"github.com/jsiebens/ionscale/internal/domain"
|
||||
@@ -13,17 +12,18 @@ import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/util/dnsname"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewRegistrationHandlers(
|
||||
createBinder bind.Factory,
|
||||
machineKey key.MachinePublic,
|
||||
config *config.Config,
|
||||
sessionManager core.PollMapSessionManager,
|
||||
repository domain.Repository) *RegistrationHandlers {
|
||||
return &RegistrationHandlers{
|
||||
createBinder: createBinder,
|
||||
machineKey: machineKey,
|
||||
sessionManager: sessionManager,
|
||||
repository: repository,
|
||||
config: config,
|
||||
@@ -31,7 +31,7 @@ func NewRegistrationHandlers(
|
||||
}
|
||||
|
||||
type RegistrationHandlers struct {
|
||||
createBinder bind.Factory
|
||||
machineKey key.MachinePublic
|
||||
repository domain.Repository
|
||||
sessionManager core.PollMapSessionManager
|
||||
config *config.Config
|
||||
@@ -40,21 +40,16 @@ type RegistrationHandlers struct {
|
||||
func (h *RegistrationHandlers) Register(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
binder, err := h.createBinder(c)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
req := &tailcfg.RegisterRequest{}
|
||||
if err := binder.BindRequest(c, req); err != nil {
|
||||
if err := c.Bind(req); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
machineKey := binder.Peer().String()
|
||||
machineKey := h.machineKey.String()
|
||||
nodeKey := req.NodeKey.String()
|
||||
|
||||
var m *domain.Machine
|
||||
m, err = h.repository.GetMachineByKeys(ctx, machineKey, nodeKey)
|
||||
m, err := h.repository.GetMachineByKeys(ctx, machineKey, nodeKey)
|
||||
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
@@ -63,7 +58,7 @@ func (h *RegistrationHandlers) Register(c echo.Context) error {
|
||||
if m != nil {
|
||||
if m.IsExpired() {
|
||||
response := tailcfg.RegisterResponse{NodeKeyExpired: true}
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
if !req.Expiry.IsZero() && req.Expiry.Before(time.Now()) {
|
||||
@@ -82,7 +77,7 @@ func (h *RegistrationHandlers) Register(c echo.Context) error {
|
||||
}
|
||||
|
||||
response := tailcfg.RegisterResponse{NodeKeyExpired: true}
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
sanitizeHostname := dnsname.SanitizeHostname(req.Hostinfo.Hostname)
|
||||
@@ -111,17 +106,17 @@ func (h *RegistrationHandlers) Register(c echo.Context) error {
|
||||
Login: tLogin,
|
||||
}
|
||||
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
return h.authenticateMachine(c, binder, machineKey, req)
|
||||
return h.authenticateMachine(c, machineKey, req)
|
||||
}
|
||||
|
||||
func (h *RegistrationHandlers) authenticateMachine(c echo.Context, binder bind.Binder, machineKey string, req *tailcfg.RegisterRequest) error {
|
||||
func (h *RegistrationHandlers) authenticateMachine(c echo.Context, machineKey string, req *tailcfg.RegisterRequest) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
if req.Followup != "" {
|
||||
return h.followup(c, binder, req)
|
||||
return h.followup(c, req)
|
||||
}
|
||||
|
||||
if req.Auth.AuthKey == "" {
|
||||
@@ -138,17 +133,17 @@ func (h *RegistrationHandlers) authenticateMachine(c echo.Context, binder bind.B
|
||||
err := h.repository.SaveRegistrationRequest(ctx, &request)
|
||||
if err != nil {
|
||||
response := tailcfg.RegisterResponse{MachineAuthorized: false, Error: "something went wrong"}
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
response := tailcfg.RegisterResponse{AuthURL: authUrl}
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
} else {
|
||||
return h.authenticateMachineWithAuthKey(c, binder, machineKey, req)
|
||||
return h.authenticateMachineWithAuthKey(c, machineKey, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, binder bind.Binder, machineKey string, req *tailcfg.RegisterRequest) error {
|
||||
func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, machineKey string, req *tailcfg.RegisterRequest) error {
|
||||
ctx := c.Request().Context()
|
||||
nodeKey := req.NodeKey.String()
|
||||
|
||||
@@ -159,7 +154,7 @@ func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, bi
|
||||
|
||||
if authKey == nil {
|
||||
response := tailcfg.RegisterResponse{MachineAuthorized: false, Error: "invalid auth key"}
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
tailnet := authKey.Tailnet
|
||||
@@ -167,7 +162,7 @@ func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, bi
|
||||
|
||||
if err := tailnet.ACLPolicy.CheckTagOwners(req.Hostinfo.RequestTags, &user); err != nil {
|
||||
response := tailcfg.RegisterResponse{MachineAuthorized: false, Error: err.Error()}
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
registeredTags := authKey.Tags
|
||||
@@ -254,10 +249,10 @@ func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, bi
|
||||
Login: tLogin,
|
||||
}
|
||||
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
func (h *RegistrationHandlers) followup(c echo.Context, binder bind.Binder, req *tailcfg.RegisterRequest) error {
|
||||
func (h *RegistrationHandlers) followup(c echo.Context, req *tailcfg.RegisterRequest) error {
|
||||
// Listen to connection close
|
||||
ctx := c.Request().Context()
|
||||
notify := ctx.Done()
|
||||
@@ -265,7 +260,7 @@ func (h *RegistrationHandlers) followup(c echo.Context, binder bind.Binder, req
|
||||
|
||||
defer func() { tick.Stop() }()
|
||||
|
||||
machineKey := binder.Peer().String()
|
||||
machineKey := h.machineKey.String()
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -274,7 +269,7 @@ func (h *RegistrationHandlers) followup(c echo.Context, binder bind.Binder, req
|
||||
|
||||
if err != nil || m == nil {
|
||||
response := tailcfg.RegisterResponse{MachineAuthorized: false, Error: "something went wrong"}
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
if m != nil && m.Authenticated {
|
||||
@@ -291,7 +286,7 @@ func (h *RegistrationHandlers) followup(c echo.Context, binder bind.Binder, req
|
||||
User: u,
|
||||
Login: l,
|
||||
}
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
if m != nil && len(m.Error) != 0 {
|
||||
@@ -299,7 +294,7 @@ func (h *RegistrationHandlers) followup(c echo.Context, binder bind.Binder, req
|
||||
MachineAuthorized: len(m.Error) != 0,
|
||||
Error: m.Error,
|
||||
}
|
||||
return binder.WriteResponse(c, http.StatusOK, response)
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
case <-notify:
|
||||
return nil
|
||||
|
||||
@@ -2,28 +2,28 @@ package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jsiebens/ionscale/internal/bind"
|
||||
"github.com/jsiebens/ionscale/internal/config"
|
||||
"github.com/jsiebens/ionscale/internal/domain"
|
||||
"github.com/jsiebens/ionscale/internal/util"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewSSHActionHandlers(createBinder bind.Factory, config *config.Config, repository domain.Repository) *SSHActionHandlers {
|
||||
func NewSSHActionHandlers(machineKey key.MachinePublic, config *config.Config, repository domain.Repository) *SSHActionHandlers {
|
||||
return &SSHActionHandlers{
|
||||
createBinder: createBinder,
|
||||
repository: repository,
|
||||
config: config,
|
||||
machineKey: machineKey,
|
||||
repository: repository,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
type SSHActionHandlers struct {
|
||||
createBinder bind.Factory
|
||||
repository domain.Repository
|
||||
config *config.Config
|
||||
machineKey key.MachinePublic
|
||||
repository domain.Repository
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
type sshActionRequestData struct {
|
||||
@@ -35,13 +35,8 @@ type sshActionRequestData struct {
|
||||
func (h *SSHActionHandlers) StartAuth(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
|
||||
binder, err := h.createBinder(c)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
data := new(sshActionRequestData)
|
||||
if err = c.Bind(data); err != nil {
|
||||
if err := c.Bind(data); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
@@ -67,7 +62,7 @@ func (h *SSHActionHandlers) StartAuth(c echo.Context) error {
|
||||
AllowLocalPortForwarding: true,
|
||||
}
|
||||
|
||||
return binder.WriteResponse(c, http.StatusOK, resp)
|
||||
return c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +87,7 @@ check:
|
||||
HoldAndDelegate: fmt.Sprintf("https://unused/machine/ssh/action/check/%s", key),
|
||||
}
|
||||
|
||||
return binder.WriteResponse(c, http.StatusOK, resp)
|
||||
return c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *SSHActionHandlers) CheckAuth(c echo.Context) error {
|
||||
@@ -100,11 +95,6 @@ func (h *SSHActionHandlers) CheckAuth(c echo.Context) error {
|
||||
ctx := c.Request().Context()
|
||||
notify := ctx.Done()
|
||||
|
||||
binder, err := h.createBinder(c)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
tick := time.NewTicker(2 * time.Second)
|
||||
|
||||
defer func() { tick.Stop() }()
|
||||
@@ -117,7 +107,7 @@ func (h *SSHActionHandlers) CheckAuth(c echo.Context) error {
|
||||
m, err := h.repository.GetSSHActionRequest(ctx, key)
|
||||
|
||||
if err != nil || m == nil {
|
||||
return binder.WriteResponse(c, http.StatusOK, &tailcfg.SSHAction{Reject: true})
|
||||
return c.JSON(http.StatusOK, &tailcfg.SSHAction{Reject: true})
|
||||
}
|
||||
|
||||
if m.Action == "accept" {
|
||||
@@ -127,13 +117,13 @@ func (h *SSHActionHandlers) CheckAuth(c echo.Context) error {
|
||||
AllowLocalPortForwarding: true,
|
||||
}
|
||||
_ = h.repository.DeleteSSHActionRequest(ctx, key)
|
||||
return binder.WriteResponse(c, http.StatusOK, action)
|
||||
return c.JSON(http.StatusOK, action)
|
||||
}
|
||||
|
||||
if m.Action == "reject" {
|
||||
action := &tailcfg.SSHAction{Reject: true}
|
||||
_ = h.repository.DeleteSSHActionRequest(ctx, key)
|
||||
return binder.WriteResponse(c, http.StatusOK, action)
|
||||
return c.JSON(http.StatusOK, action)
|
||||
}
|
||||
case <-notify:
|
||||
return nil
|
||||
|
||||
+59
-39
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/jsiebens/ionscale/internal/domain"
|
||||
"github.com/jsiebens/ionscale/internal/util"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strconv"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/dnstype"
|
||||
@@ -33,20 +34,20 @@ func ToDNSConfig(m *domain.Machine, tailnet *domain.Tailnet, c *domain.DNSConfig
|
||||
sanitizeTailnetName := domain.SanitizeTailnetName(tailnet.Name)
|
||||
tailnetDomain := fmt.Sprintf("%s.%s", sanitizeTailnetName, config.MagicDNSSuffix())
|
||||
|
||||
resolvers := []*dnstype.Resolver{}
|
||||
resolvers := make([]*dnstype.Resolver, 0)
|
||||
|
||||
for _, r := range c.Nameservers {
|
||||
resolver := &dnstype.Resolver{
|
||||
Addr: r,
|
||||
}
|
||||
resolvers = append(resolvers, resolver)
|
||||
resolvers = append(resolvers, &dnstype.Resolver{Addr: r})
|
||||
}
|
||||
|
||||
dnsConfig := &tailcfg.DNSConfig{}
|
||||
|
||||
var routes = make(map[string][]*dnstype.Resolver)
|
||||
var domains []string
|
||||
var certDomains []string
|
||||
|
||||
if c.MagicDNS {
|
||||
routes[tailnetDomain] = nil
|
||||
domains = append(domains, tailnetDomain)
|
||||
dnsConfig.Proxied = true
|
||||
|
||||
@@ -62,49 +63,30 @@ func ToDNSConfig(m *domain.Machine, tailnet *domain.Tailnet, c *domain.DNSConfig
|
||||
}
|
||||
|
||||
if len(c.Routes) != 0 || certsEnabled {
|
||||
routes := make(map[string][]*dnstype.Resolver)
|
||||
|
||||
for r, s := range c.Routes {
|
||||
routeResolver := []*dnstype.Resolver{}
|
||||
routeResolver := make([]*dnstype.Resolver, 0)
|
||||
for _, addr := range s {
|
||||
resolver := &dnstype.Resolver{Addr: addr}
|
||||
routeResolver = append(routeResolver, resolver)
|
||||
routeResolver = append(routeResolver, &dnstype.Resolver{Addr: addr})
|
||||
}
|
||||
routes[r] = routeResolver
|
||||
domains = append(domains, r)
|
||||
}
|
||||
|
||||
dnsConfig.Routes = routes
|
||||
}
|
||||
|
||||
dnsConfig.Domains = domains
|
||||
dnsConfig.Domains = append(domains, c.SearchDomains...)
|
||||
dnsConfig.CertDomains = certDomains
|
||||
|
||||
dnsConfig.ExitNodeFilteredSet = []string{
|
||||
fmt.Sprintf(".%s", config.MagicDNSSuffix()),
|
||||
}
|
||||
|
||||
return dnsConfig
|
||||
}
|
||||
|
||||
func ToNode(m *domain.Machine, tailnet *domain.Tailnet, taggedDevicesUser *domain.User, peer bool, connected bool, routeFilter func(m *domain.Machine) []netip.Prefix) (*tailcfg.Node, *tailcfg.UserProfile, error) {
|
||||
func ToNode(capVer tailcfg.CapabilityVersion, m *domain.Machine, tailnet *domain.Tailnet, taggedDevicesUser *domain.User, peer bool, connected bool, routeFilter func(m *domain.Machine) []netip.Prefix) (*tailcfg.Node, *tailcfg.UserProfile, error) {
|
||||
role := tailnet.IAMPolicy.GetRole(m.User)
|
||||
|
||||
var capabilities []string
|
||||
|
||||
if !peer {
|
||||
if !m.HasTags() && role == domain.UserRoleAdmin {
|
||||
capabilities = append(capabilities, tailcfg.CapabilityAdmin)
|
||||
}
|
||||
|
||||
if tailnet.FileSharingEnabled {
|
||||
capabilities = append(capabilities, tailcfg.CapabilityFileSharing)
|
||||
}
|
||||
|
||||
if tailnet.SSHEnabled {
|
||||
capabilities = append(capabilities, tailcfg.CapabilitySSH)
|
||||
}
|
||||
|
||||
if tailnet.DNSConfig.HttpsCertsEnabled {
|
||||
capabilities = append(capabilities, tailcfg.CapabilityHTTPS)
|
||||
}
|
||||
}
|
||||
|
||||
nKey, err := util.ParseNodePublicKey(m.NodeKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -186,15 +168,55 @@ func ToNode(m *domain.Machine, tailnet *domain.Tailnet, taggedDevicesUser *domai
|
||||
Endpoints: endpoints,
|
||||
DERP: derp,
|
||||
|
||||
Hostinfo: hostInfo.View(),
|
||||
Capabilities: capabilities,
|
||||
|
||||
Created: m.CreatedAt.UTC(),
|
||||
Hostinfo: hostInfo.View(),
|
||||
Created: m.CreatedAt.UTC(),
|
||||
|
||||
MachineAuthorized: m.Authorized,
|
||||
User: tailcfg.UserID(m.UserID),
|
||||
}
|
||||
|
||||
if !peer {
|
||||
var capabilities []tailcfg.NodeCapability
|
||||
capMap := make(tailcfg.NodeCapMap)
|
||||
|
||||
for _, c := range tailnet.ACLPolicy.NodeCapabilities(m) {
|
||||
capabilities = append(capabilities, c)
|
||||
capMap[c] = []tailcfg.RawMessage{}
|
||||
}
|
||||
|
||||
if !m.HasTags() && role == domain.UserRoleAdmin {
|
||||
capabilities = append(capabilities, tailcfg.CapabilityAdmin)
|
||||
capMap[tailcfg.CapabilityAdmin] = []tailcfg.RawMessage{}
|
||||
}
|
||||
|
||||
if tailnet.FileSharingEnabled {
|
||||
capabilities = append(capabilities, tailcfg.CapabilityFileSharing)
|
||||
capMap[tailcfg.CapabilityFileSharing] = []tailcfg.RawMessage{}
|
||||
}
|
||||
|
||||
if tailnet.SSHEnabled {
|
||||
capabilities = append(capabilities, tailcfg.CapabilitySSH)
|
||||
capMap[tailcfg.CapabilitySSH] = []tailcfg.RawMessage{}
|
||||
}
|
||||
|
||||
if tailnet.DNSConfig.HttpsCertsEnabled {
|
||||
capabilities = append(capabilities, tailcfg.CapabilityHTTPS)
|
||||
capMap[tailcfg.CapabilityHTTPS] = []tailcfg.RawMessage{}
|
||||
}
|
||||
|
||||
// ionscale has no support for Funnel yet, so remove Funnel attribute if set via ACL policy
|
||||
{
|
||||
slices.DeleteFunc(capabilities, func(c tailcfg.NodeCapability) bool { return c == tailcfg.NodeAttrFunnel })
|
||||
delete(capMap, tailcfg.NodeAttrFunnel)
|
||||
}
|
||||
|
||||
if capVer >= 74 {
|
||||
n.CapMap = capMap
|
||||
} else {
|
||||
n.Capabilities = capabilities
|
||||
}
|
||||
}
|
||||
|
||||
if !m.ExpiresAt.IsZero() {
|
||||
e := m.ExpiresAt.UTC()
|
||||
n.KeyExpiry = e
|
||||
@@ -238,13 +260,11 @@ func ToUser(u domain.User) (tailcfg.User, tailcfg.Login) {
|
||||
LoginName: u.Name,
|
||||
DisplayName: u.Name,
|
||||
Logins: []tailcfg.LoginID{tailcfg.LoginID(u.ID)},
|
||||
Domain: u.Tailnet.Name,
|
||||
}
|
||||
login := tailcfg.Login{
|
||||
ID: tailcfg.LoginID(u.ID),
|
||||
LoginName: u.Name,
|
||||
DisplayName: u.Name,
|
||||
Domain: u.Tailnet.Name,
|
||||
}
|
||||
return user, login
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
package mapping
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/jsiebens/ionscale/internal/core"
|
||||
"github.com/jsiebens/ionscale/internal/domain"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/opt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MapResponse is a custom tailcfg.MapResponse
|
||||
// for marshalling non-nil zero-length slices (meaning explicitly now empty)
|
||||
// see tailcfg.MapResponse documentation
|
||||
type MapResponse struct {
|
||||
tailcfg.MapResponse
|
||||
PacketFilter []tailcfg.FilterRule
|
||||
}
|
||||
|
||||
func NewPollNetMapper(req *tailcfg.MapRequest, machineID uint64, repository domain.Repository, sessionManager core.PollMapSessionManager) *PollNetMapper {
|
||||
return &PollNetMapper{
|
||||
req: req,
|
||||
machineID: machineID,
|
||||
prevSyncedPeerIDs: make(map[uint64]bool),
|
||||
prevDerpMapChecksum: "",
|
||||
repository: repository,
|
||||
sessionManager: sessionManager,
|
||||
}
|
||||
}
|
||||
|
||||
type PollNetMapper struct {
|
||||
sync.Mutex
|
||||
req *tailcfg.MapRequest
|
||||
machineID uint64
|
||||
|
||||
prevSyncedPeerIDs map[uint64]bool
|
||||
prevDerpMapChecksum string
|
||||
|
||||
repository domain.Repository
|
||||
sessionManager core.PollMapSessionManager
|
||||
}
|
||||
|
||||
func (h *PollNetMapper) CreateMapResponse(ctx context.Context, delta bool) (*MapResponse, error) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
m, err := h.repository.GetMachine(ctx, h.machineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostinfo := tailcfg.Hostinfo(m.HostInfo)
|
||||
tailnet := m.Tailnet
|
||||
policies := tailnet.ACLPolicy
|
||||
dnsConfig := tailnet.DNSConfig
|
||||
|
||||
serviceUser, _, err := h.repository.GetOrCreateServiceUser(ctx, &tailnet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
derpMap, err := m.Tailnet.GetDERPMap(ctx, h.repository)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prc := &primaryRoutesCollector{flagged: map[netip.Prefix]bool{}}
|
||||
|
||||
node, user, err := ToNode(h.req.Version, m, &tailnet, serviceUser, false, true, prc.filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var users = []tailcfg.UserProfile{*user}
|
||||
var changedPeers []*tailcfg.Node
|
||||
var removedPeers []tailcfg.NodeID
|
||||
var filterRules = make([]tailcfg.FilterRule, 0)
|
||||
var sshPolicy *tailcfg.SSHPolicy
|
||||
syncedPeerIDs := map[uint64]bool{}
|
||||
|
||||
if !h.req.OmitPeers {
|
||||
candidatePeers, err := h.repository.ListMachinePeers(ctx, m.TailnetID, m.MachineKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
syncedUserIDs := map[tailcfg.UserID]bool{user.ID: true}
|
||||
|
||||
for _, peer := range candidatePeers {
|
||||
if peer.IsExpired() {
|
||||
continue
|
||||
}
|
||||
if policies.IsValidPeer(m, &peer) || policies.IsValidPeer(&peer, m) {
|
||||
isConnected := h.sessionManager.HasSession(peer.TailnetID, peer.ID)
|
||||
|
||||
n, u, err := ToNode(h.req.Version, &peer, &tailnet, serviceUser, true, isConnected, prc.filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changedPeers = append(changedPeers, n)
|
||||
syncedPeerIDs[peer.ID] = true
|
||||
delete(h.prevSyncedPeerIDs, peer.ID)
|
||||
|
||||
if _, ok := syncedUserIDs[u.ID]; !ok {
|
||||
users = append(users, *u)
|
||||
syncedUserIDs[u.ID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for p, _ := range h.prevSyncedPeerIDs {
|
||||
removedPeers = append(removedPeers, tailcfg.NodeID(p))
|
||||
}
|
||||
|
||||
filterRules = policies.BuildFilterRules(candidatePeers, m)
|
||||
|
||||
if tailnet.SSHEnabled && hostinfo.TailscaleSSHEnabled() {
|
||||
sshPolicy = policies.BuildSSHPolicy(candidatePeers, m)
|
||||
}
|
||||
}
|
||||
|
||||
controlTime := time.Now().UTC()
|
||||
var mapResponse tailcfg.MapResponse
|
||||
|
||||
if !delta {
|
||||
mapResponse = tailcfg.MapResponse{
|
||||
KeepAlive: false,
|
||||
Node: node,
|
||||
DNSConfig: ToDNSConfig(m, &m.Tailnet, &dnsConfig),
|
||||
PacketFilter: filterRules,
|
||||
SSHPolicy: sshPolicy,
|
||||
DERPMap: &derpMap.DERPMap,
|
||||
Domain: domain.SanitizeTailnetName(m.Tailnet.Name),
|
||||
Peers: changedPeers,
|
||||
UserProfiles: users,
|
||||
ControlTime: &controlTime,
|
||||
CollectServices: optBool(tailnet.ServiceCollectionEnabled),
|
||||
Debug: &tailcfg.Debug{
|
||||
DisableLogTail: true,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
mapResponse = tailcfg.MapResponse{
|
||||
Node: node,
|
||||
DNSConfig: ToDNSConfig(m, &m.Tailnet, &dnsConfig),
|
||||
PacketFilter: filterRules,
|
||||
SSHPolicy: sshPolicy,
|
||||
Domain: domain.SanitizeTailnetName(m.Tailnet.Name),
|
||||
PeersChanged: changedPeers,
|
||||
PeersRemoved: removedPeers,
|
||||
UserProfiles: users,
|
||||
ControlTime: &controlTime,
|
||||
CollectServices: optBool(tailnet.ServiceCollectionEnabled),
|
||||
}
|
||||
|
||||
if h.prevDerpMapChecksum != derpMap.Checksum {
|
||||
mapResponse.DERPMap = &derpMap.DERPMap
|
||||
}
|
||||
}
|
||||
|
||||
if h.req.OmitPeers {
|
||||
mapResponse.PeersChanged = nil
|
||||
mapResponse.PeersRemoved = nil
|
||||
mapResponse.Peers = nil
|
||||
}
|
||||
|
||||
h.prevSyncedPeerIDs = syncedPeerIDs
|
||||
h.prevDerpMapChecksum = derpMap.Checksum
|
||||
|
||||
return &MapResponse{MapResponse: mapResponse, PacketFilter: filterRules}, nil
|
||||
}
|
||||
|
||||
type primaryRoutesCollector struct {
|
||||
flagged map[netip.Prefix]bool
|
||||
}
|
||||
|
||||
func (p *primaryRoutesCollector) filter(m *domain.Machine) []netip.Prefix {
|
||||
var result []netip.Prefix
|
||||
for _, r := range m.AllowIPs {
|
||||
if _, ok := p.flagged[r]; r.Bits() != 0 && !ok {
|
||||
result = append(result, r)
|
||||
p.flagged[r] = true
|
||||
}
|
||||
}
|
||||
for _, r := range m.AutoAllowIPs {
|
||||
if _, ok := p.flagged[r]; r.Bits() != 0 && !ok {
|
||||
result = append(result, r)
|
||||
p.flagged[r] = true
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func optBool(v bool) opt.Bool {
|
||||
b := opt.Bool("")
|
||||
b.Set(v)
|
||||
return b
|
||||
}
|
||||
+35
-40
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/jsiebens/ionscale/internal/auth"
|
||||
"github.com/jsiebens/ionscale/internal/bind"
|
||||
"github.com/jsiebens/ionscale/internal/config"
|
||||
"github.com/jsiebens/ionscale/internal/core"
|
||||
"github.com/jsiebens/ionscale/internal/database"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
echo_prometheus "github.com/labstack/echo-contrib/prometheus"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
certmagicsql "github.com/travisjeffery/certmagic-sqlstorage"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/net/http2"
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
func Start(c *config.Config) error {
|
||||
func Start(ctx context.Context, c *config.Config) error {
|
||||
logger, err := setupLogging(c.Logging)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -48,14 +48,14 @@ func Start(c *config.Config) error {
|
||||
httpLogger := logger.Named("http")
|
||||
dbLogger := logger.Named("db")
|
||||
|
||||
repository, err := database.OpenDB(&c.Database, dbLogger)
|
||||
db, repository, err := database.OpenDB(&c.Database, dbLogger)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
sessionManager := core.NewPollMapSessionManager()
|
||||
|
||||
defaultControlKeys, err := repository.GetControlKeys(context.Background())
|
||||
defaultControlKeys, err := repository.GetControlKeys(ctx)
|
||||
if err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func Start(c *config.Config) error {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
core.StartReaper(repository, sessionManager)
|
||||
core.StartWorker(repository, sessionManager)
|
||||
|
||||
serverUrl, err := url.Parse(c.ServerUrl)
|
||||
if err != nil {
|
||||
@@ -74,16 +74,19 @@ func Start(c *config.Config) error {
|
||||
|
||||
// prepare CertMagic
|
||||
if c.Tls.AcmeEnabled {
|
||||
storage, err := certmagicsql.NewStorage(ctx, db, certmagicsql.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certmagic.DefaultACME.Agreed = true
|
||||
certmagic.DefaultACME.Email = c.Tls.AcmeEmail
|
||||
certmagic.DefaultACME.CA = c.Tls.AcmeCA
|
||||
certmagic.Default.Logger = logger.Named("certmagic")
|
||||
if c.Tls.AcmePath != "" {
|
||||
certmagic.Default.Storage = &certmagic.FileStorage{Path: c.Tls.AcmePath}
|
||||
}
|
||||
certmagic.Default.Storage = storage
|
||||
|
||||
cfg := certmagic.NewDefault()
|
||||
if err := cfg.ManageAsync(context.Background(), []string{serverUrl.Host}); err != nil {
|
||||
if err := cfg.ManageAsync(ctx, []string{serverUrl.Host}); err != nil {
|
||||
return logError(err)
|
||||
}
|
||||
|
||||
@@ -107,16 +110,15 @@ func Start(c *config.Config) error {
|
||||
p.SetMetricsPath(metricsHandler)
|
||||
|
||||
createPeerHandler := func(machinePublicKey key.MachinePublic) http.Handler {
|
||||
binder := bind.DefaultBinder(machinePublicKey)
|
||||
|
||||
registrationHandlers := handlers.NewRegistrationHandlers(binder, c, sessionManager, repository)
|
||||
pollNetMapHandler := handlers.NewPollNetMapHandler(binder, sessionManager, repository)
|
||||
dnsHandlers := handlers.NewDNSHandlers(binder, dnsProvider)
|
||||
idTokenHandlers := handlers.NewIDTokenHandlers(binder, c, repository)
|
||||
sshActionHandlers := handlers.NewSSHActionHandlers(binder, c, repository)
|
||||
queryFeatureHandlers := handlers.NewQueryFeatureHandlers(binder, dnsProvider, repository)
|
||||
registrationHandlers := handlers.NewRegistrationHandlers(machinePublicKey, c, sessionManager, repository)
|
||||
pollNetMapHandler := handlers.NewPollNetMapHandler(machinePublicKey, sessionManager, repository)
|
||||
dnsHandlers := handlers.NewDNSHandlers(machinePublicKey, dnsProvider)
|
||||
idTokenHandlers := handlers.NewIDTokenHandlers(machinePublicKey, c, repository)
|
||||
sshActionHandlers := handlers.NewSSHActionHandlers(machinePublicKey, c, repository)
|
||||
queryFeatureHandlers := handlers.NewQueryFeatureHandlers(machinePublicKey, dnsProvider, repository)
|
||||
|
||||
e := echo.New()
|
||||
e.Binder = handlers.JsonBinder{}
|
||||
e.Use(EchoMetrics(p), EchoLogger(httpLogger), EchoErrorHandler(), EchoRecover())
|
||||
e.POST("/machine/register", registrationHandlers.Register)
|
||||
e.POST("/machine/map", pollNetMapHandler.PollNetMap)
|
||||
@@ -131,10 +133,8 @@ func Start(c *config.Config) error {
|
||||
}
|
||||
|
||||
noiseHandlers := handlers.NewNoiseHandlers(serverKey.ControlKey, createPeerHandler)
|
||||
registrationHandlers := handlers.NewRegistrationHandlers(bind.BoxBinder(serverKey.LegacyControlKey), c, sessionManager, repository)
|
||||
pollNetMapHandler := handlers.NewPollNetMapHandler(bind.BoxBinder(serverKey.LegacyControlKey), sessionManager, repository)
|
||||
dnsHandlers := handlers.NewDNSHandlers(bind.BoxBinder(serverKey.LegacyControlKey), dnsProvider)
|
||||
idTokenHandlers := handlers.NewIDTokenHandlers(bind.BoxBinder(serverKey.LegacyControlKey), c, repository)
|
||||
oidcConfigHandlers := handlers.NewOIDCConfigHandlers(c, repository)
|
||||
|
||||
authenticationHandlers := handlers.NewAuthenticationHandlers(
|
||||
c,
|
||||
authProvider,
|
||||
@@ -151,9 +151,9 @@ func Start(c *config.Config) error {
|
||||
nonTlsAppHandler.Any("/*", handlers.HttpRedirectHandler(c.Tls))
|
||||
|
||||
tlsAppHandler := echo.New()
|
||||
tlsAppHandler.Renderer = templates.NewTemplates()
|
||||
tlsAppHandler.Renderer = &templates.Renderer{}
|
||||
tlsAppHandler.Pre(handlers.HttpsRedirect(c.Tls))
|
||||
tlsAppHandler.Use(EchoMetrics(p), EchoLogger(logger), EchoErrorHandler(), EchoRecover())
|
||||
tlsAppHandler.Use(EchoMetrics(p), EchoLogger(httpLogger), EchoErrorHandler(), EchoRecover())
|
||||
|
||||
tlsAppHandler.Any("/*", handlers.IndexHandler(http.StatusNotFound))
|
||||
tlsAppHandler.Any("/", handlers.IndexHandler(http.StatusOK))
|
||||
@@ -161,22 +161,16 @@ func Start(c *config.Config) error {
|
||||
tlsAppHandler.GET("/version", handlers.Version)
|
||||
tlsAppHandler.GET("/key", handlers.KeyHandler(serverKey))
|
||||
tlsAppHandler.POST("/ts2021", noiseHandlers.Upgrade)
|
||||
tlsAppHandler.POST("/machine/:id", registrationHandlers.Register)
|
||||
tlsAppHandler.POST("/machine/:id/map", pollNetMapHandler.PollNetMap)
|
||||
tlsAppHandler.POST("/machine/:id/set-dns", dnsHandlers.SetDNS)
|
||||
tlsAppHandler.GET("/.well-known/jwks", idTokenHandlers.Jwks)
|
||||
tlsAppHandler.GET("/.well-known/openid-configuration", idTokenHandlers.OpenIDConfig)
|
||||
tlsAppHandler.GET("/.well-known/jwks", oidcConfigHandlers.Jwks)
|
||||
tlsAppHandler.GET("/.well-known/openid-configuration", oidcConfigHandlers.OpenIDConfig)
|
||||
|
||||
auth := tlsAppHandler.Group("/a")
|
||||
auth.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
|
||||
TokenLookup: "form:_csrf",
|
||||
}))
|
||||
auth.GET("/:flow/:key", authenticationHandlers.StartAuth)
|
||||
auth.POST("/:flow/:key", authenticationHandlers.ProcessAuth)
|
||||
auth.GET("/callback", authenticationHandlers.Callback)
|
||||
auth.POST("/callback", authenticationHandlers.EndOAuth)
|
||||
auth.GET("/success", authenticationHandlers.Success)
|
||||
auth.GET("/error", authenticationHandlers.Error)
|
||||
csrf := middleware.CSRFWithConfig(middleware.CSRFConfig{TokenLookup: "form:_csrf"})
|
||||
tlsAppHandler.GET("/a/:flow/:key", authenticationHandlers.StartAuth, csrf)
|
||||
tlsAppHandler.POST("/a/:flow/:key", authenticationHandlers.ProcessAuth, csrf)
|
||||
tlsAppHandler.GET("/a/callback", authenticationHandlers.Callback, csrf)
|
||||
tlsAppHandler.POST("/a/callback", authenticationHandlers.EndAuth, csrf)
|
||||
tlsAppHandler.GET("/a/success", authenticationHandlers.Success, csrf)
|
||||
tlsAppHandler.GET("/a/error", authenticationHandlers.Error, csrf)
|
||||
|
||||
tlsL, err := tlsListener(c)
|
||||
if err != nil {
|
||||
@@ -308,7 +302,8 @@ func setupLogging(config config.Logging) (*zap.Logger, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zap.ReplaceGlobals(logger)
|
||||
globalLogger := logger.Named("ionscale")
|
||||
zap.ReplaceGlobals(globalLogger)
|
||||
|
||||
return logger, nil
|
||||
return globalLogger, nil
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@ func (s *Service) SetDNSConfig(ctx context.Context, req *connect.Request[api.Set
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, fmt.Errorf("MagicDNS must be enabled when enabling HTTPS Certs"))
|
||||
}
|
||||
|
||||
if dnsConfig.HttpsCerts && s.dnsProvider != nil {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, fmt.Errorf("A DNS provider must be configured when enabling HTTPS Certs"))
|
||||
}
|
||||
|
||||
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
|
||||
if err != nil {
|
||||
return nil, logError(err)
|
||||
@@ -50,13 +54,7 @@ func (s *Service) SetDNSConfig(ctx context.Context, req *connect.Request[api.Set
|
||||
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("tailnet not found"))
|
||||
}
|
||||
|
||||
tailnet.DNSConfig = domain.DNSConfig{
|
||||
MagicDNS: dnsConfig.MagicDns,
|
||||
HttpsCertsEnabled: s.dnsProvider != nil && dnsConfig.HttpsCerts,
|
||||
OverrideLocalDNS: dnsConfig.OverrideLocalDns,
|
||||
Nameservers: dnsConfig.Nameservers,
|
||||
Routes: apiRoutesToDomainRoutes(dnsConfig.Routes),
|
||||
}
|
||||
tailnet.DNSConfig = apiDNSConfigToDomainDNSConfig(req.Msg.Config)
|
||||
|
||||
if err := s.repository.SaveTailnet(ctx, tailnet); err != nil {
|
||||
return nil, logError(err)
|
||||
@@ -102,6 +100,7 @@ func apiDNSConfigToDomainDNSConfig(dnsConfig *api.DNSConfig) domain.DNSConfig {
|
||||
OverrideLocalDNS: dnsConfig.OverrideLocalDns,
|
||||
Nameservers: dnsConfig.Nameservers,
|
||||
Routes: apiRoutesToDomainRoutes(dnsConfig.Routes),
|
||||
SearchDomains: dnsConfig.SearchDomains,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,5 +114,6 @@ func domainDNSConfigToApiDNSConfig(tailnet *domain.Tailnet) *api.DNSConfig {
|
||||
OverrideLocalDns: dnsConfig.OverrideLocalDNS,
|
||||
Nameservers: dnsConfig.Nameservers,
|
||||
Routes: domainRoutesToApiRoutes(dnsConfig.Routes),
|
||||
SearchDomains: dnsConfig.SearchDomains,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@ func (s *Service) SetIAMPolicy(ctx context.Context, req *connect.Request[api.Set
|
||||
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("tailnet does not exist"))
|
||||
}
|
||||
|
||||
if err := validateIamPolicy(req.Msg.Policy); err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invalid iam policy: %w", err))
|
||||
}
|
||||
|
||||
tailnet.IAMPolicy = domain.IAMPolicy{
|
||||
Subs: req.Msg.Policy.Subs,
|
||||
Emails: req.Msg.Policy.Emails,
|
||||
|
||||
@@ -24,6 +24,11 @@ func (s *Service) machineToApi(m *domain.Machine) *api.Machine {
|
||||
lastSeen = timestamppb.New(*m.LastSeen)
|
||||
}
|
||||
|
||||
var endpoints []string
|
||||
for _, e := range m.Endpoints {
|
||||
endpoints = append(endpoints, e.String())
|
||||
}
|
||||
|
||||
return &api.Machine{
|
||||
Id: m.ID,
|
||||
Name: name,
|
||||
@@ -47,7 +52,7 @@ func (s *Service) machineToApi(m *domain.Machine) *api.Machine {
|
||||
Name: m.User.Name,
|
||||
},
|
||||
ClientConnectivity: &api.ClientConnectivity{
|
||||
Endpoints: m.Endpoints,
|
||||
Endpoints: endpoints,
|
||||
},
|
||||
AdvertisedRoutes: m.AdvertisedPrefixes(),
|
||||
EnabledRoutes: m.AllowedPrefixes(),
|
||||
|
||||
@@ -2,7 +2,10 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
"github.com/hashicorp/go-bexpr/grammar"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/jsiebens/ionscale/internal/auth"
|
||||
"github.com/jsiebens/ionscale/internal/config"
|
||||
"github.com/jsiebens/ionscale/internal/core"
|
||||
@@ -37,3 +40,13 @@ func (s *Service) GetVersion(_ context.Context, _ *connect.Request[api.GetVersio
|
||||
Revision: revision,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func validateIamPolicy(p *api.IAMPolicy) error {
|
||||
var mErr *multierror.Error
|
||||
for i, exp := range p.Filters {
|
||||
if _, err := grammar.Parse(fmt.Sprintf("filter %d", i), []byte(exp)); err != nil {
|
||||
mErr = multierror.Append(mErr, err)
|
||||
}
|
||||
}
|
||||
return mErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
+33
-12
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/jsiebens/ionscale/internal/domain"
|
||||
"github.com/jsiebens/ionscale/internal/mapping"
|
||||
"github.com/jsiebens/ionscale/internal/util"
|
||||
"github.com/jsiebens/ionscale/pkg/defaults"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
@@ -42,6 +43,26 @@ func (s *Service) CreateTailnet(ctx context.Context, req *connect.Request[api.Cr
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, fmt.Errorf("permission denied"))
|
||||
}
|
||||
|
||||
check, err := s.repository.GetTailnetByName(ctx, req.Msg.Name)
|
||||
if err != nil {
|
||||
return nil, logError(err)
|
||||
}
|
||||
if check != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("tailnet with name '%s' already exists", req.Msg.Name))
|
||||
}
|
||||
|
||||
if req.Msg.IamPolicy == nil {
|
||||
req.Msg.IamPolicy = defaults.DefaultIAMPolicy()
|
||||
}
|
||||
|
||||
if req.Msg.AclPolicy == nil {
|
||||
req.Msg.AclPolicy = defaults.DefaultACLPolicy()
|
||||
}
|
||||
|
||||
if req.Msg.DnsConfig == nil {
|
||||
req.Msg.DnsConfig = defaults.DefaultDNSConfig()
|
||||
}
|
||||
|
||||
tailnet := &domain.Tailnet{
|
||||
ID: util.NextID(),
|
||||
Name: req.Msg.Name,
|
||||
@@ -54,20 +75,16 @@ func (s *Service) CreateTailnet(ctx context.Context, req *connect.Request[api.Cr
|
||||
MachineAuthorizationEnabled: req.Msg.MachineAuthorizationEnabled,
|
||||
}
|
||||
|
||||
if req.Msg.IamPolicy != nil {
|
||||
if err := mapping.CopyViaJson(req.Msg.IamPolicy, &tailnet.IAMPolicy); err != nil {
|
||||
return nil, logError(err)
|
||||
}
|
||||
} else {
|
||||
tailnet.IAMPolicy = domain.DefaultIAMPolicy()
|
||||
if err := validateIamPolicy(req.Msg.IamPolicy); err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invalid iam policy: %w", err))
|
||||
}
|
||||
|
||||
if req.Msg.AclPolicy != nil {
|
||||
if err := mapping.CopyViaJson(req.Msg.AclPolicy, &tailnet.ACLPolicy); err != nil {
|
||||
return nil, logError(err)
|
||||
}
|
||||
} else {
|
||||
tailnet.ACLPolicy = domain.DefaultACLPolicy()
|
||||
if err := mapping.CopyViaJson(req.Msg.IamPolicy, &tailnet.IAMPolicy); err != nil {
|
||||
return nil, logError(err)
|
||||
}
|
||||
|
||||
if err := mapping.CopyViaJson(req.Msg.AclPolicy, &tailnet.ACLPolicy); err != nil {
|
||||
return nil, logError(err)
|
||||
}
|
||||
|
||||
if err := s.repository.SaveTailnet(ctx, tailnet); err != nil {
|
||||
@@ -100,6 +117,10 @@ func (s *Service) UpdateTailnet(ctx context.Context, req *connect.Request[api.Up
|
||||
}
|
||||
|
||||
if req.Msg.IamPolicy != nil {
|
||||
if err := validateIamPolicy(req.Msg.IamPolicy); err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invalid iam policy: %w", err))
|
||||
}
|
||||
|
||||
tailnet.IAMPolicy = domain.IAMPolicy{}
|
||||
if err := mapping.CopyViaJson(req.Msg.IamPolicy, &tailnet.IAMPolicy); err != nil {
|
||||
return nil, logError(err)
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList {
|
||||
padding-top: 5px
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
height: 45px;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
{{if .ProviderAvailable}}
|
||||
<div style="text-align: left; padding-bottom: 10px">
|
||||
<p><b>Authentication required</b></p>
|
||||
<small>Login with:</small>
|
||||
</div>
|
||||
<form method="post">
|
||||
<input type="hidden" name="_csrf" value="{{.Csrf}}">
|
||||
<ul class="selectionList">
|
||||
<li><button type="submit" name="s" value="true">OpenID</button></li>
|
||||
</ul>
|
||||
</form>
|
||||
<div style="text-align: left; padding-bottom: 10px; padding-top: 20px">
|
||||
<small>Or enter an <label for="ak">auth key</label> here:</small>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if not .ProviderAvailable}}
|
||||
<div style="text-align: left; padding-bottom: 10px">
|
||||
<p><b>Authentication required</b></p>
|
||||
<small>Enter an <label for="ak">auth key</label> here:</small>
|
||||
</div>
|
||||
{{end}}
|
||||
<form method="post" style="text-align: right">
|
||||
<input type="hidden" name="_csrf" value="{{.Csrf}}">
|
||||
<p><input id="ak" name="ak" type="text"/></p>
|
||||
<div style="padding-top: 10px">
|
||||
<button type="submit">submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,32 @@
|
||||
package templates
|
||||
|
||||
templ Auth(oidc bool, csrf string) {
|
||||
if oidc {
|
||||
<div style="text-align: left; padding-bottom: 10px">
|
||||
<p><b>Authentication required</b></p>
|
||||
<small>Login with:</small>
|
||||
</div>
|
||||
<form method="post">
|
||||
<input type="hidden" name="_csrf" value={ csrf } />
|
||||
<ul class="selectionList">
|
||||
<li><button type="submit" name="oidc" value="true">OpenID</button></li>
|
||||
</ul>
|
||||
</form>
|
||||
<div style="text-align: left; padding-bottom: 10px; padding-top: 20px">
|
||||
<small>Or enter an <label for="ak">auth key</label> here:</small>
|
||||
</div>
|
||||
} else {
|
||||
<div style="text-align: left; padding-bottom: 10px">
|
||||
<p><b>Authentication required</b></p>
|
||||
<small>Enter an <label for="ak">auth key</label> here:</small>
|
||||
</div>
|
||||
}
|
||||
|
||||
<form method="post" style="text-align: right">
|
||||
<input type="hidden" name="_csrf" value={ csrf } />
|
||||
<p><input id="ak" name="ak" type="text"/></p>
|
||||
<div style="padding-top: 10px">
|
||||
<button type="submit">submit</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.543
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
func Auth(oidc bool, csrf string) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
if oidc {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: left; padding-bottom: 10px\"><p><b>Authentication required</b></p><small>Login with:</small></div><form method=\"post\"><input type=\"hidden\" name=\"_csrf\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(csrf))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><ul class=\"selectionList\"><li><button type=\"submit\" name=\"oidc\" value=\"true\">OpenID</button></li></ul></form><div style=\"text-align: left; padding-bottom: 10px; padding-top: 20px\"><small>Or enter an <label for=\"ak\">auth key</label> here:</small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: left; padding-bottom: 10px\"><p><b>Authentication required</b></p><small>Enter an <label for=\"ak\">auth key</label> here:</small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<form method=\"post\" style=\"text-align: right\"><input type=\"hidden\" name=\"_csrf\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(csrf))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><p><input id=\"ak\" name=\"ak\" type=\"text\"></p><div style=\"padding-top: 10px\"><button type=\"submit\">submit</button></div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList {
|
||||
padding-top: 5px
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
height: 45px;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
{{if .ProviderAvailable}}
|
||||
<div style="text-align: left; padding-bottom: 10px">
|
||||
<p><b>Authentication required</b></p>
|
||||
<small>Login with:</small>
|
||||
</div>
|
||||
<form method="post">
|
||||
<ul class="selectionList">
|
||||
<li><button type="submit" name="s" value="true">OpenID</button></li>
|
||||
</ul>
|
||||
</form>
|
||||
{{end}}
|
||||
{{if not .ProviderAvailable}}
|
||||
<div style="text-align: center">
|
||||
<p><b>No authentication method available.</b></p>
|
||||
<small>contact your ionscale administrator for more information</small>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,62 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div style="text-align: center">
|
||||
<p><b>An error occurred</b></p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,64 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div style="text-align: center">
|
||||
<p><b>ionscale</b></p>
|
||||
<p><small>{{.Version}}</small></p>
|
||||
<p><small>{{.Revision}}</small></p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,63 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div style="text-align: center">
|
||||
<p><b>Authorization failed</b></p>
|
||||
<small>the provided auth key is <b style="color: red">invalid</b></small>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,140 @@
|
||||
package templates
|
||||
|
||||
templ Index(version string, revision string) {
|
||||
<div style="text-align: center">
|
||||
<p><b>ionscale</b></p>
|
||||
<p><small>{ version }</small></p>
|
||||
<p><small>{ revision }</small></p>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ Success() {
|
||||
<div style="text-align: center">
|
||||
<p><b>Authorization successful</b></p>
|
||||
<small>You can now close this window</small>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ NewMachine() {
|
||||
<div style="text-align: center">
|
||||
<p><b>Authentication successful</b></p>
|
||||
<small>but this is a <b style="color: blue">new machine</b> and needs to be authorized by your Tailnet admin.</small>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ Error() {
|
||||
<div style="text-align: center">
|
||||
<p><b>An error occurred</b></p>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ Unauthorized() {
|
||||
<div style="text-align: center">
|
||||
<p><b>Authentication successful</b></p>
|
||||
<small>but you're <b style="color: red">not</b> authorized to use any network</small>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ InvalidAuthKey() {
|
||||
<div style="text-align: center">
|
||||
<p><b>Authorization failed</b></p>
|
||||
<small>the provided auth key is <b style="color: red">invalid</b></small>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ NotTagOwner() {
|
||||
<div style="text-align: center">
|
||||
<p><b>Authentication successful</b></p>
|
||||
<small>but you're <b style="color: red">not</b> a valid tag owner for the requested tags</small>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ NotMachineOwner() {
|
||||
<div style="text-align: center">
|
||||
<p><b>Authentication successful</b></p>
|
||||
<small>but you're <b style="color: red">not</b> a valid owner of the machine</small>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ layout(contents templ.Component) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@heading()
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
@contents
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
|
||||
templ heading() {
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
height: 45px;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.543
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
func Index(version string, revision string) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: center\"><p><b>ionscale</b></p><p><small>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/layout.templ`, Line: 5, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</small></p><p><small>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(revision)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/layout.templ`, Line: 6, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</small></p></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func Success() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var4 == nil {
|
||||
templ_7745c5c3_Var4 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: center\"><p><b>Authorization successful</b></p><small>You can now close this window</small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func NewMachine() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var5 == nil {
|
||||
templ_7745c5c3_Var5 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: center\"><p><b>Authentication successful</b></p><small>but this is a <b style=\"color: blue\">new machine</b> and needs to be authorized by your Tailnet admin.</small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func Error() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var6 == nil {
|
||||
templ_7745c5c3_Var6 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: center\"><p><b>An error occurred</b></p></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func Unauthorized() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var7 == nil {
|
||||
templ_7745c5c3_Var7 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: center\"><p><b>Authentication successful</b></p><small>but you're <b style=\"color: red\">not</b> authorized to use any network</small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func InvalidAuthKey() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var8 == nil {
|
||||
templ_7745c5c3_Var8 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: center\"><p><b>Authorization failed</b></p><small>the provided auth key is <b style=\"color: red\">invalid</b></small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func NotTagOwner() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var9 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var9 == nil {
|
||||
templ_7745c5c3_Var9 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: center\"><p><b>Authentication successful</b></p><small>but you're <b style=\"color: red\">not</b> a valid tag owner for the requested tags</small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func NotMachineOwner() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var10 == nil {
|
||||
templ_7745c5c3_Var10 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: center\"><p><b>Authentication successful</b></p><small>but you're <b style=\"color: red\">not</b> a valid owner of the machine</small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func layout(contents templ.Component) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var11 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var11 == nil {
|
||||
templ_7745c5c3_Var11 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"en\"><head>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = heading().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</head><body><div class=\"wrapper\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = contents.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func heading() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var12 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var12 == nil {
|
||||
templ_7745c5c3_Var12 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><style>\n @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');\n\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n font-family: 'Poppins', sans-serif;\n }\n\n body {\n width: 100%;\n height: 100vh;\n padding: 10px;\n background: #379683;\n }\n\n .wrapper {\n background: #fff;\n max-width: 400px;\n width: 100%;\n margin: 120px auto;\n padding: 25px;\n border-radius: 5px;\n box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);\n }\n\n .selectionList li {\n position: relative;\n list-style: none;\n height: 45px;\n line-height: 45px;\n margin-bottom: 8px;\n background: #f2f2f2;\n border-radius: 3px;\n overflow: hidden;\n box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);\n }\n\n .selectionList li button {\n margin: 0;\n display: block;\n width: 100%;\n height: 100%;\n border: none;\n }\n\n input {\n display: block;\n width: 100%;\n height: 100%;\n padding: 10px;\n }\n\n button {\n padding-top: 10px;\n padding-bottom: 10px;\n padding-left: 20px;\n padding-right: 20px;\n height: 45px;\n border: none;\n }\n </style><title>ionscale</title>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div style="text-align: center">
|
||||
<p><b>Authentication successful</b></p>
|
||||
<small>but this is a <b style="color: blue">new machine</b> and needs to be authorized by your Tailnet admin.</small>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,63 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div style="text-align: center">
|
||||
<p><b>Authentication successful</b></p>
|
||||
<small>but you're <b style="color: red">not</b> a valid tag owner for the requested tags</small>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,63 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div style="text-align: center">
|
||||
<p><b>Authentication successful</b></p>
|
||||
<small>but you're <b style="color: red">not</b> a valid owner of the machine</small>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,63 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div style="text-align: center">
|
||||
<p><b>Authorization successful</b></p>
|
||||
<small>You can now close this window</small>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,114 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList {
|
||||
padding-top: 5px
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
height: 45px;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
{{if .SystemAdmin}}
|
||||
<div style="text-align: left; padding-bottom: 10px">
|
||||
<p><b>System Admin</b></p>
|
||||
<small>You are a member of the System Admin group:</small>
|
||||
</div>
|
||||
<form method="post">
|
||||
<input type="hidden" name="aid" value="{{.AccountID}}">
|
||||
<input type="hidden" name="_csrf" value="{{.Csrf}}">
|
||||
<ul class="selectionList">
|
||||
<li><button type="submit" name="sad" value="true">OK, continue as System Admin</button></li>
|
||||
</ul>
|
||||
</form>
|
||||
{{end}}
|
||||
{{if .Tailnets}}
|
||||
{{if .SystemAdmin}}
|
||||
<div style="text-align: left; padding-bottom: 10px; padding-top: 20px">
|
||||
<small>Or select your <b>tailnet</b>:</small>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if not .SystemAdmin}}
|
||||
<div style="text-align: left; padding-bottom: 10px;">
|
||||
<p><b>Tailnets</b></p>
|
||||
<small>Select your tailnet:</small>
|
||||
</div>
|
||||
{{end}}
|
||||
<form method="post">
|
||||
<input type="hidden" name="aid" value="{{.AccountID}}">
|
||||
<input type="hidden" name="_csrf" value="{{.Csrf}}">
|
||||
<ul class="selectionList">
|
||||
{{range .Tailnets}}
|
||||
<li><button type="submit" name="tid" value="{{.ID}}">{{.Name}}</button></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</form>
|
||||
{{end}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
package templates
|
||||
|
||||
import "strconv"
|
||||
import "github.com/jsiebens/ionscale/internal/domain"
|
||||
|
||||
templ Tailnets(accountID uint64, isSystemAdmin bool, tailnets []domain.Tailnet, csrf string) {
|
||||
if isSystemAdmin {
|
||||
<div style="text-align: left; padding-bottom: 10px">
|
||||
<p><b>System Admin</b></p>
|
||||
<small>You are a member of the System Admin group:</small>
|
||||
</div>
|
||||
<form method="post">
|
||||
<input type="hidden" name="aid" value={ strconv.FormatUint(accountID, 10) } />
|
||||
<input type="hidden" name="_csrf" value={ csrf } />
|
||||
<ul class="selectionList">
|
||||
<li><button type="submit" name="sad" value="true">OK, continue as System Admin</button></li>
|
||||
</ul>
|
||||
</form>
|
||||
}
|
||||
|
||||
if len(tailnets) != 0 {
|
||||
if isSystemAdmin {
|
||||
<div style="text-align: left; padding-bottom: 10px; padding-top: 20px">
|
||||
<small>Or select your <b>tailnet</b>:</small>
|
||||
</div>
|
||||
} else {
|
||||
<div style="text-align: left; padding-bottom: 10px;">
|
||||
<p><b>Tailnets</b></p>
|
||||
<small>Select your tailnet:</small>
|
||||
</div>
|
||||
}
|
||||
|
||||
<form method="post">
|
||||
<input type="hidden" name="aid" value={ strconv.FormatUint(accountID, 10) }/>
|
||||
<input type="hidden" name="_csrf" value={ csrf }/>
|
||||
<ul class="selectionList">
|
||||
for _, t := range tailnets {
|
||||
<li><button type="submit" name="tid" value={ strconv.FormatUint(t.ID, 10) }>{ t.Name }</button></li>
|
||||
}
|
||||
</ul>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.543
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "strconv"
|
||||
import "github.com/jsiebens/ionscale/internal/domain"
|
||||
|
||||
func Tailnets(accountID uint64, isSystemAdmin bool, tailnets []domain.Tailnet, csrf string) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
if isSystemAdmin {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: left; padding-bottom: 10px\"><p><b>System Admin</b></p><small>You are a member of the System Admin group:</small></div><form method=\"post\"><input type=\"hidden\" name=\"aid\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(strconv.FormatUint(accountID, 10)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> <input type=\"hidden\" name=\"_csrf\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(csrf))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><ul class=\"selectionList\"><li><button type=\"submit\" name=\"sad\" value=\"true\">OK, continue as System Admin</button></li></ul></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if len(tailnets) != 0 {
|
||||
if isSystemAdmin {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: left; padding-bottom: 10px; padding-top: 20px\"><small>Or select your <b>tailnet</b>:</small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div style=\"text-align: left; padding-bottom: 10px;\"><p><b>Tailnets</b></p><small>Select your tailnet:</small></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <form method=\"post\"><input type=\"hidden\" name=\"aid\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(strconv.FormatUint(accountID, 10)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> <input type=\"hidden\" name=\"_csrf\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(csrf))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><ul class=\"selectionList\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, t := range tailnets {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><button type=\"submit\" name=\"tid\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(strconv.FormatUint(t.ID, 10)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(t.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/tailnets.templ`, Line: 37, Col: 100}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
@@ -1,23 +1,19 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"github.com/a-h/templ"
|
||||
"github.com/labstack/echo/v4"
|
||||
"html/template"
|
||||
"io"
|
||||
)
|
||||
|
||||
//go:embed *.html
|
||||
var fs embed.FS
|
||||
|
||||
func NewTemplates() *Template {
|
||||
return &Template{templates: template.Must(template.ParseFS(fs, "*.html"))}
|
||||
type Renderer struct {
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
templates *template.Template
|
||||
}
|
||||
func (t *Renderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||
if x, ok := data.(templ.Component); ok {
|
||||
return layout(x).Render(c.Request().Context(), w)
|
||||
}
|
||||
|
||||
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||
return t.templates.ExecuteTemplate(w, name, data)
|
||||
return fmt.Errorf("invalid data")
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px;
|
||||
background: #379683;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
background: #fff;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin: 120px auto;
|
||||
padding: 25px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
margin-bottom: 8px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.selectionList li button {
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<title>ionscale</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div style="text-align: center">
|
||||
<p><b>Authentication successful</b></p>
|
||||
<small>but you're <b style="color: red">not</b> authorized to use any network</small>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+88
-15
@@ -1,55 +1,128 @@
|
||||
package ionscale
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/99designs/keyring"
|
||||
"github.com/jsiebens/ionscale/internal/key"
|
||||
"github.com/jsiebens/ionscale/internal/token"
|
||||
)
|
||||
|
||||
func LoadClientAuth(systemAdminKey string) (ClientAuth, error) {
|
||||
const (
|
||||
defaultDir string = "~/.ionscale"
|
||||
)
|
||||
|
||||
func LoadClientAuth(addr string, systemAdminKey string) (ClientAuth, error) {
|
||||
if systemAdminKey != "" {
|
||||
k, err := key.ParsePrivateKey(systemAdminKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid system admin key")
|
||||
}
|
||||
return &systemAdminTokenAuth{key: *k}, nil
|
||||
return systemAdminTokenSession{key: *k}, nil
|
||||
}
|
||||
|
||||
apiToken, err := TokenFromFile()
|
||||
ring, err := openKeyring()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(apiToken) != 0 {
|
||||
return &apiTokenAuth{token: apiToken}, nil
|
||||
data, err := ring.Get(createKeyName(addr))
|
||||
if err != nil && !errors.Is(err, keyring.ErrKeyNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &anonymous{}, nil
|
||||
if errors.Is(err, keyring.ErrKeyNotFound) {
|
||||
return Anonymous{}, nil
|
||||
}
|
||||
|
||||
var ds defaultSession
|
||||
if err := json.Unmarshal(data.Data, &ds); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func StoreAuthToken(addr, token string, tailnetID uint64) error {
|
||||
ring, err := openKeyring()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ds := defaultSession{
|
||||
TK: token,
|
||||
TID: tailnetID,
|
||||
}
|
||||
|
||||
data, err := json.Marshal(&ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ring.Set(keyring.Item{
|
||||
Key: createKeyName(addr),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
type ClientAuth interface {
|
||||
GetToken() (string, error)
|
||||
TailnetID() uint64
|
||||
}
|
||||
|
||||
type anonymous struct {
|
||||
type defaultSession struct {
|
||||
TK string
|
||||
TID uint64
|
||||
}
|
||||
|
||||
func (m *anonymous) GetToken() (string, error) {
|
||||
return "", nil
|
||||
func (m defaultSession) GetToken() (string, error) {
|
||||
return m.TK, nil
|
||||
}
|
||||
|
||||
type systemAdminTokenAuth struct {
|
||||
func (m defaultSession) TailnetID() uint64 {
|
||||
return m.TID
|
||||
}
|
||||
|
||||
type systemAdminTokenSession struct {
|
||||
key key.ServerPrivate
|
||||
}
|
||||
|
||||
func (m *systemAdminTokenAuth) GetToken() (string, error) {
|
||||
func (m systemAdminTokenSession) GetToken() (string, error) {
|
||||
return token.GenerateSystemAdminToken(m.key)
|
||||
}
|
||||
|
||||
type apiTokenAuth struct {
|
||||
token string
|
||||
func (m systemAdminTokenSession) TailnetID() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *apiTokenAuth) GetToken() (string, error) {
|
||||
return m.token, nil
|
||||
type Anonymous struct {
|
||||
}
|
||||
|
||||
func (m Anonymous) GetToken() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (m Anonymous) TailnetID() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func openKeyring() (keyring.Keyring, error) {
|
||||
return keyring.Open(keyring.Config{
|
||||
LibSecretCollectionName: "login",
|
||||
PassPrefix: "ionscale",
|
||||
FileDir: defaultDir,
|
||||
FilePasswordFunc: keyring.FixedStringPrompt(""),
|
||||
AllowedBackends: []keyring.BackendType{
|
||||
keyring.FileBackend,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func createKeyName(addr string) string {
|
||||
sum := md5.Sum([]byte(addr))
|
||||
x := hex.EncodeToString(sum[:])
|
||||
return fmt.Sprintf("ionscale:%s", x)
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
package ionscale
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultDir string = "~/.ionscale"
|
||||
DefaultPermissions os.FileMode = 0700
|
||||
)
|
||||
|
||||
func TokenFromFile() (string, error) {
|
||||
return valueFromFile("token")
|
||||
}
|
||||
|
||||
func TailnetFromFile() (uint64, error) {
|
||||
v, err := valueFromFile("tailnet_id")
|
||||
if v == "" {
|
||||
return 0, nil
|
||||
}
|
||||
p, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
|
||||
func valueFromFile(name string) (string, error) {
|
||||
file, err := EnsureFile(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
token, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(token), nil
|
||||
}
|
||||
|
||||
func SessionToFile(token string, tailnetID *uint64) error {
|
||||
if err := TokenToFile(token); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := TailnetToFile(tailnetID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TokenToFile(token string) error {
|
||||
file, err := EnsureFile("token")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(file, []byte(token), 0600)
|
||||
}
|
||||
|
||||
func TailnetToFile(id *uint64) error {
|
||||
file, err := EnsureFile("tailnet_id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var v = ""
|
||||
|
||||
if id != nil {
|
||||
v = strconv.FormatUint(*id, 10)
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(file, []byte(v), 0600)
|
||||
}
|
||||
|
||||
func ConfigDir() string {
|
||||
return DefaultDir
|
||||
}
|
||||
|
||||
func EnsureFile(file string) (string, error) {
|
||||
permission := DefaultPermissions
|
||||
dir := ConfigDir()
|
||||
dirPath, err := homedir.Expand(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
filePath := path.Clean(filepath.Join(dirPath, file))
|
||||
if err := os.MkdirAll(filepath.Dir(filePath), permission); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
}
|
||||
|
||||
return filePath, nil
|
||||
}
|
||||
@@ -11,9 +11,21 @@ func DefaultACLPolicy() *ionscalev1.ACLPolicy {
|
||||
Dst: []string{"*:*"},
|
||||
},
|
||||
},
|
||||
Ssh: []*ionscalev1.SSHRule{
|
||||
{
|
||||
Action: "check",
|
||||
Src: []string{"autogroup:member"},
|
||||
Dst: []string{"autogroup:self"},
|
||||
Users: []string{"autogroup:nonroot", "root"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultIAMPolicy() *ionscalev1.IAMPolicy {
|
||||
return &ionscalev1.IAMPolicy{}
|
||||
}
|
||||
|
||||
func DefaultDNSConfig() *ionscalev1.DNSConfig {
|
||||
return &ionscalev1.DNSConfig{
|
||||
MagicDns: true,
|
||||
|
||||
+268
-64
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/acl.proto
|
||||
|
||||
@@ -219,6 +219,8 @@ type ACLPolicy struct {
|
||||
Tagowners map[string]*structpb.ListValue `protobuf:"bytes,4,rep,name=tagowners,proto3" json:"tagowners,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
Autoapprovers *AutoApprovers `protobuf:"bytes,5,opt,name=autoapprovers,proto3,oneof" json:"autoapprovers,omitempty"`
|
||||
Ssh []*SSHRule `protobuf:"bytes,6,rep,name=ssh,proto3" json:"ssh,omitempty"`
|
||||
Nodeattrs []*NodeAttr `protobuf:"bytes,7,rep,name=nodeattrs,proto3" json:"nodeattrs,omitempty"`
|
||||
Grants []*ACLGrant `protobuf:"bytes,8,rep,name=grants,proto3" json:"grants,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ACLPolicy) Reset() {
|
||||
@@ -295,6 +297,20 @@ func (x *ACLPolicy) GetSsh() []*SSHRule {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ACLPolicy) GetNodeattrs() []*NodeAttr {
|
||||
if x != nil {
|
||||
return x.Nodeattrs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ACLPolicy) GetGrants() []*ACLGrant {
|
||||
if x != nil {
|
||||
return x.Grants
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ACL struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -303,6 +319,7 @@ type ACL struct {
|
||||
Action string `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"`
|
||||
Src []string `protobuf:"bytes,2,rep,name=src,proto3" json:"src,omitempty"`
|
||||
Dst []string `protobuf:"bytes,3,rep,name=dst,proto3" json:"dst,omitempty"`
|
||||
Proto string `protobuf:"bytes,4,opt,name=proto,proto3" json:"proto,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ACL) Reset() {
|
||||
@@ -358,6 +375,13 @@ func (x *ACL) GetDst() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ACL) GetProto() string {
|
||||
if x != nil {
|
||||
return x.Proto
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type AutoApprovers struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -492,6 +516,132 @@ func (x *SSHRule) GetCheckperiod() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type NodeAttr struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Target []string `protobuf:"bytes,1,rep,name=target,proto3" json:"target,omitempty"`
|
||||
Attr []string `protobuf:"bytes,2,rep,name=attr,proto3" json:"attr,omitempty"`
|
||||
}
|
||||
|
||||
func (x *NodeAttr) Reset() {
|
||||
*x = NodeAttr{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ionscale_v1_acl_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *NodeAttr) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*NodeAttr) ProtoMessage() {}
|
||||
|
||||
func (x *NodeAttr) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ionscale_v1_acl_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use NodeAttr.ProtoReflect.Descriptor instead.
|
||||
func (*NodeAttr) Descriptor() ([]byte, []int) {
|
||||
return file_ionscale_v1_acl_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *NodeAttr) GetTarget() []string {
|
||||
if x != nil {
|
||||
return x.Target
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *NodeAttr) GetAttr() []string {
|
||||
if x != nil {
|
||||
return x.Attr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ACLGrant struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Src []string `protobuf:"bytes,1,rep,name=src,proto3" json:"src,omitempty"`
|
||||
Dst []string `protobuf:"bytes,2,rep,name=dst,proto3" json:"dst,omitempty"`
|
||||
Ip []string `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
|
||||
App map[string]*structpb.ListValue `protobuf:"bytes,4,rep,name=app,proto3" json:"app,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
}
|
||||
|
||||
func (x *ACLGrant) Reset() {
|
||||
*x = ACLGrant{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ionscale_v1_acl_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ACLGrant) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ACLGrant) ProtoMessage() {}
|
||||
|
||||
func (x *ACLGrant) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ionscale_v1_acl_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ACLGrant.ProtoReflect.Descriptor instead.
|
||||
func (*ACLGrant) Descriptor() ([]byte, []int) {
|
||||
return file_ionscale_v1_acl_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *ACLGrant) GetSrc() []string {
|
||||
if x != nil {
|
||||
return x.Src
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ACLGrant) GetDst() []string {
|
||||
if x != nil {
|
||||
return x.Dst
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ACLGrant) GetIp() []string {
|
||||
if x != nil {
|
||||
return x.Ip
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ACLGrant) GetApp() map[string]*structpb.ListValue {
|
||||
if x != nil {
|
||||
return x.App
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_ionscale_v1_acl_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_ionscale_v1_acl_proto_rawDesc = []byte{
|
||||
@@ -514,8 +664,8 @@ var file_ionscale_v1_acl_proto_rawDesc = []byte{
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c,
|
||||
0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x43, 0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06,
|
||||
0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x41, 0x43, 0x4c,
|
||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xd7,
|
||||
0x04, 0x0a, 0x09, 0x41, 0x43, 0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x05,
|
||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbb,
|
||||
0x05, 0x0a, 0x09, 0x41, 0x43, 0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x05,
|
||||
0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x43, 0x4c, 0x50, 0x6f, 0x6c,
|
||||
0x69, 0x63, 0x79, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05,
|
||||
@@ -536,52 +686,75 @@ var file_ionscale_v1_acl_proto_rawDesc = []byte{
|
||||
0x00, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x6f, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x73,
|
||||
0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x03, 0x73, 0x73, 0x68, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x14, 0x2e, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53,
|
||||
0x53, 0x48, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x03, 0x73, 0x73, 0x68, 0x1a, 0x38, 0x0a, 0x0a, 0x48,
|
||||
0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x55, 0x0a, 0x0b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x45,
|
||||
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x58, 0x0a, 0x0e,
|
||||
0x54, 0x61, 0x67, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x61,
|
||||
0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x22, 0x41, 0x0a, 0x03, 0x41, 0x43, 0x4c, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x02,
|
||||
0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x73, 0x74,
|
||||
0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x64, 0x73, 0x74, 0x22, 0xc2, 0x01, 0x0a, 0x0d,
|
||||
0x41, 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a,
|
||||
0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f,
|
||||
0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a,
|
||||
0x08, 0x65, 0x78, 0x69, 0x74, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||
0x08, 0x65, 0x78, 0x69, 0x74, 0x6e, 0x6f, 0x64, 0x65, 0x1a, 0x55, 0x0a, 0x0b, 0x52, 0x6f, 0x75,
|
||||
0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x53, 0x48, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x03, 0x73, 0x73, 0x68, 0x12, 0x33, 0x0a, 0x09, 0x6e,
|
||||
0x6f, 0x64, 0x65, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15,
|
||||
0x2e, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64,
|
||||
0x65, 0x41, 0x74, 0x74, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x61, 0x74, 0x74, 0x72, 0x73,
|
||||
0x12, 0x2d, 0x0a, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x15, 0x2e, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41,
|
||||
0x43, 0x4c, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x1a,
|
||||
0x38, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x55, 0x0a, 0x0b, 0x47, 0x72, 0x6f,
|
||||
0x75, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74,
|
||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
|
||||
0x22, 0x7d, 0x0a, 0x07, 0x53, 0x53, 0x48, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x03, 0x73, 0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03,
|
||||
0x28, 0x09, 0x52, 0x03, 0x64, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73,
|
||||
0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x20, 0x0a,
|
||||
0x0b, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x42,
|
||||
0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x73,
|
||||
0x69, 0x65, 0x62, 0x65, 0x6e, 0x73, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f,
|
||||
0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65,
|
||||
0x2f, 0x76, 0x31, 0x3b, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x76, 0x31, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x1a, 0x58, 0x0a, 0x0e, 0x54, 0x61, 0x67, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x61,
|
||||
0x75, 0x74, 0x6f, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x22, 0x57, 0x0a, 0x03,
|
||||
0x41, 0x43, 0x4c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x73,
|
||||
0x72, 0x63, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x64, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x64, 0x73, 0x74, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc2, 0x01, 0x0a, 0x0d, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x70,
|
||||
0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65,
|
||||
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61,
|
||||
0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x65, 0x72, 0x73, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
|
||||
0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x69, 0x74, 0x6e,
|
||||
0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x6e,
|
||||
0x6f, 0x64, 0x65, 0x1a, 0x55, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x7d, 0x0a, 0x07, 0x53, 0x53,
|
||||
0x48, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x73, 0x72, 0x63, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x64, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x64, 0x73,
|
||||
0x74, 0x12, 0x14, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x65, 0x63, 0x6b,
|
||||
0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68,
|
||||
0x65, 0x63, 0x6b, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0x36, 0x0a, 0x08, 0x4e, 0x6f, 0x64,
|
||||
0x65, 0x41, 0x74, 0x74, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18,
|
||||
0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x61, 0x74, 0x74, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x61, 0x74, 0x74,
|
||||
0x72, 0x22, 0xc4, 0x01, 0x0a, 0x08, 0x41, 0x43, 0x4c, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x73, 0x72, 0x63,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x64, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x64,
|
||||
0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02,
|
||||
0x69, 0x70, 0x12, 0x30, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x1e, 0x2e, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x43,
|
||||
0x4c, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
|
||||
0x03, 0x61, 0x70, 0x70, 0x1a, 0x52, 0x0a, 0x08, 0x41, 0x70, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x73, 0x69, 0x65, 0x62, 0x65, 0x6e, 0x73, 0x2f,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e,
|
||||
0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x63, 0x61, 0x6c, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -596,7 +769,7 @@ func file_ionscale_v1_acl_proto_rawDescGZIP() []byte {
|
||||
return file_ionscale_v1_acl_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_ionscale_v1_acl_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
|
||||
var file_ionscale_v1_acl_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_ionscale_v1_acl_proto_goTypes = []interface{}{
|
||||
(*GetACLPolicyRequest)(nil), // 0: ionscale.v1.GetACLPolicyRequest
|
||||
(*GetACLPolicyResponse)(nil), // 1: ionscale.v1.GetACLPolicyResponse
|
||||
@@ -606,30 +779,37 @@ var file_ionscale_v1_acl_proto_goTypes = []interface{}{
|
||||
(*ACL)(nil), // 5: ionscale.v1.ACL
|
||||
(*AutoApprovers)(nil), // 6: ionscale.v1.AutoApprovers
|
||||
(*SSHRule)(nil), // 7: ionscale.v1.SSHRule
|
||||
nil, // 8: ionscale.v1.ACLPolicy.HostsEntry
|
||||
nil, // 9: ionscale.v1.ACLPolicy.GroupsEntry
|
||||
nil, // 10: ionscale.v1.ACLPolicy.TagownersEntry
|
||||
nil, // 11: ionscale.v1.AutoApprovers.RoutesEntry
|
||||
(*structpb.ListValue)(nil), // 12: google.protobuf.ListValue
|
||||
(*NodeAttr)(nil), // 8: ionscale.v1.NodeAttr
|
||||
(*ACLGrant)(nil), // 9: ionscale.v1.ACLGrant
|
||||
nil, // 10: ionscale.v1.ACLPolicy.HostsEntry
|
||||
nil, // 11: ionscale.v1.ACLPolicy.GroupsEntry
|
||||
nil, // 12: ionscale.v1.ACLPolicy.TagownersEntry
|
||||
nil, // 13: ionscale.v1.AutoApprovers.RoutesEntry
|
||||
nil, // 14: ionscale.v1.ACLGrant.AppEntry
|
||||
(*structpb.ListValue)(nil), // 15: google.protobuf.ListValue
|
||||
}
|
||||
var file_ionscale_v1_acl_proto_depIdxs = []int32{
|
||||
4, // 0: ionscale.v1.GetACLPolicyResponse.policy:type_name -> ionscale.v1.ACLPolicy
|
||||
4, // 1: ionscale.v1.SetACLPolicyRequest.policy:type_name -> ionscale.v1.ACLPolicy
|
||||
8, // 2: ionscale.v1.ACLPolicy.hosts:type_name -> ionscale.v1.ACLPolicy.HostsEntry
|
||||
9, // 3: ionscale.v1.ACLPolicy.groups:type_name -> ionscale.v1.ACLPolicy.GroupsEntry
|
||||
10, // 2: ionscale.v1.ACLPolicy.hosts:type_name -> ionscale.v1.ACLPolicy.HostsEntry
|
||||
11, // 3: ionscale.v1.ACLPolicy.groups:type_name -> ionscale.v1.ACLPolicy.GroupsEntry
|
||||
5, // 4: ionscale.v1.ACLPolicy.acls:type_name -> ionscale.v1.ACL
|
||||
10, // 5: ionscale.v1.ACLPolicy.tagowners:type_name -> ionscale.v1.ACLPolicy.TagownersEntry
|
||||
12, // 5: ionscale.v1.ACLPolicy.tagowners:type_name -> ionscale.v1.ACLPolicy.TagownersEntry
|
||||
6, // 6: ionscale.v1.ACLPolicy.autoapprovers:type_name -> ionscale.v1.AutoApprovers
|
||||
7, // 7: ionscale.v1.ACLPolicy.ssh:type_name -> ionscale.v1.SSHRule
|
||||
11, // 8: ionscale.v1.AutoApprovers.routes:type_name -> ionscale.v1.AutoApprovers.RoutesEntry
|
||||
12, // 9: ionscale.v1.ACLPolicy.GroupsEntry.value:type_name -> google.protobuf.ListValue
|
||||
12, // 10: ionscale.v1.ACLPolicy.TagownersEntry.value:type_name -> google.protobuf.ListValue
|
||||
12, // 11: ionscale.v1.AutoApprovers.RoutesEntry.value:type_name -> google.protobuf.ListValue
|
||||
12, // [12:12] is the sub-list for method output_type
|
||||
12, // [12:12] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
8, // 8: ionscale.v1.ACLPolicy.nodeattrs:type_name -> ionscale.v1.NodeAttr
|
||||
9, // 9: ionscale.v1.ACLPolicy.grants:type_name -> ionscale.v1.ACLGrant
|
||||
13, // 10: ionscale.v1.AutoApprovers.routes:type_name -> ionscale.v1.AutoApprovers.RoutesEntry
|
||||
14, // 11: ionscale.v1.ACLGrant.app:type_name -> ionscale.v1.ACLGrant.AppEntry
|
||||
15, // 12: ionscale.v1.ACLPolicy.GroupsEntry.value:type_name -> google.protobuf.ListValue
|
||||
15, // 13: ionscale.v1.ACLPolicy.TagownersEntry.value:type_name -> google.protobuf.ListValue
|
||||
15, // 14: ionscale.v1.AutoApprovers.RoutesEntry.value:type_name -> google.protobuf.ListValue
|
||||
15, // 15: ionscale.v1.ACLGrant.AppEntry.value:type_name -> google.protobuf.ListValue
|
||||
16, // [16:16] is the sub-list for method output_type
|
||||
16, // [16:16] is the sub-list for method input_type
|
||||
16, // [16:16] is the sub-list for extension type_name
|
||||
16, // [16:16] is the sub-list for extension extendee
|
||||
0, // [0:16] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ionscale_v1_acl_proto_init() }
|
||||
@@ -734,6 +914,30 @@ func file_ionscale_v1_acl_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ionscale_v1_acl_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*NodeAttr); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ionscale_v1_acl_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ACLGrant); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_ionscale_v1_acl_proto_msgTypes[4].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
@@ -742,7 +946,7 @@ func file_ionscale_v1_acl_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_ionscale_v1_acl_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 12,
|
||||
NumMessages: 15,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/auth.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/auth_keys.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/derp.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/dns.proto
|
||||
|
||||
@@ -235,6 +235,7 @@ type DNSConfig struct {
|
||||
Routes map[string]*Routes `protobuf:"bytes,4,rep,name=routes,proto3" json:"routes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
MagicDnsSuffix string `protobuf:"bytes,5,opt,name=magic_dns_suffix,json=magicDnsSuffix,proto3" json:"magic_dns_suffix,omitempty"`
|
||||
HttpsCerts bool `protobuf:"varint,6,opt,name=https_certs,json=httpsCerts,proto3" json:"https_certs,omitempty"`
|
||||
SearchDomains []string `protobuf:"bytes,7,rep,name=search_domains,json=searchDomains,proto3" json:"search_domains,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DNSConfig) Reset() {
|
||||
@@ -311,6 +312,13 @@ func (x *DNSConfig) GetHttpsCerts() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *DNSConfig) GetSearchDomains() []string {
|
||||
if x != nil {
|
||||
return x.SearchDomains
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Routes struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -383,7 +391,7 @@ var file_ionscale_v1_dns_proto_rawDesc = []byte{
|
||||
0x32, 0x16, 0x2e, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44,
|
||||
0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xcf, 0x02, 0x0a, 0x09, 0x44,
|
||||
0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xf6, 0x02, 0x0a, 0x09, 0x44,
|
||||
0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x67, 0x69,
|
||||
0x63, 0x5f, 0x64, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x61, 0x67,
|
||||
0x69, 0x63, 0x44, 0x6e, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
|
||||
@@ -399,19 +407,21 @@ var file_ionscale_v1_dns_proto_rawDesc = []byte{
|
||||
0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x61, 0x67,
|
||||
0x69, 0x63, 0x44, 0x6e, 0x73, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x12, 0x1f, 0x0a, 0x0b, 0x68,
|
||||
0x74, 0x74, 0x70, 0x73, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x43, 0x65, 0x72, 0x74, 0x73, 0x1a, 0x4e, 0x0a, 0x0b,
|
||||
0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65,
|
||||
0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x20, 0x0a, 0x06,
|
||||
0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x42, 0x3d,
|
||||
0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x73, 0x69,
|
||||
0x65, 0x62, 0x65, 0x6e, 0x73, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x70,
|
||||
0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f,
|
||||
0x76, 0x31, 0x3b, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x43, 0x65, 0x72, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07,
|
||||
0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x73, 0x1a, 0x4e, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
||||
0x02, 0x38, 0x01, 0x22, 0x20, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a,
|
||||
0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72,
|
||||
0x6f, 0x75, 0x74, 0x65, 0x73, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x73, 0x69, 0x65, 0x62, 0x65, 0x6e, 0x73, 0x2f, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61,
|
||||
0x6c, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/iam.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/ionscale.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/machines.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/ref.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/routes.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/tailnets.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/users.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc-gen-go v1.32.0
|
||||
// protoc (unknown)
|
||||
// source: ionscale/v1/version.proto
|
||||
|
||||
|
||||
@@ -28,12 +28,15 @@ message ACLPolicy {
|
||||
map<string, google.protobuf.ListValue> tagowners = 4;
|
||||
optional AutoApprovers autoapprovers = 5;
|
||||
repeated SSHRule ssh = 6;
|
||||
repeated NodeAttr nodeattrs = 7;
|
||||
repeated ACLGrant grants = 8;
|
||||
}
|
||||
|
||||
message ACL {
|
||||
string action = 1;
|
||||
repeated string src = 2;
|
||||
repeated string dst = 3;
|
||||
string proto = 4;
|
||||
}
|
||||
|
||||
message AutoApprovers {
|
||||
@@ -48,3 +51,15 @@ message SSHRule {
|
||||
repeated string users = 4;
|
||||
string checkperiod = 5;
|
||||
}
|
||||
|
||||
message NodeAttr {
|
||||
repeated string target = 1;
|
||||
repeated string attr = 2;
|
||||
}
|
||||
|
||||
message ACLGrant {
|
||||
repeated string src = 1;
|
||||
repeated string dst = 2;
|
||||
repeated string ip = 3;
|
||||
map<string, google.protobuf.ListValue> app = 4;
|
||||
}
|
||||
@@ -29,6 +29,7 @@ message DNSConfig {
|
||||
map<string, Routes> routes = 4;
|
||||
string magic_dns_suffix = 5;
|
||||
bool https_certs = 6;
|
||||
repeated string search_domains = 7;
|
||||
}
|
||||
|
||||
message Routes {
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ setup_env() {
|
||||
fatal "env variable IONSCALE_ACME_EMAIL is undefined"
|
||||
fi
|
||||
|
||||
IONSCALE_VERSION=v0.9.1
|
||||
IONSCALE_VERSION=v0.12.0
|
||||
IONSCALE_DATA_DIR=/var/lib/ionscale
|
||||
IONSCALE_CONFIG_DIR=/etc/ionscale
|
||||
IONSCALE_SERVICE_FILE=/etc/systemd/system/ionscale.service
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"github.com/jsiebens/ionscale/pkg/defaults"
|
||||
ionscalev1 "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
"github.com/jsiebens/ionscale/tests/sc"
|
||||
"github.com/jsiebens/ionscale/tests/tsn"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestACL_PeersShouldBeRemovedWhenNoMatchingACLRuleIsAvailable(t *testing.T) {
|
||||
sc.Run(t, func(s *sc.Scenario) {
|
||||
tailnet := s.CreateTailnet()
|
||||
clientKey := s.CreateAuthKey(tailnet.Id, true, "tag:client")
|
||||
serverKey := s.CreateAuthKey(tailnet.Id, true, "tag:server")
|
||||
|
||||
client1 := s.NewTailscaleNode()
|
||||
client2 := s.NewTailscaleNode()
|
||||
server := s.NewTailscaleNode()
|
||||
|
||||
require.NoError(t, client1.Up(clientKey))
|
||||
require.NoError(t, client2.Up(clientKey))
|
||||
require.NoError(t, server.Up(serverKey))
|
||||
require.NoError(t, server.WaitFor(tsn.PeerCount(2)))
|
||||
|
||||
policy := defaults.DefaultACLPolicy()
|
||||
policy.Acls = []*ionscalev1.ACL{
|
||||
{
|
||||
Action: "accept",
|
||||
Src: []string{"tag:server"},
|
||||
Dst: []string{"tag:server:*"},
|
||||
},
|
||||
}
|
||||
|
||||
s.SetACLPolicy(tailnet.Id, policy)
|
||||
|
||||
require.NoError(t, server.WaitFor(tsn.PeerCount(0)))
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
http_listen_addr: ":8080"
|
||||
server_url: "http://localhost:8080"
|
||||
http_listen_addr: ":80"
|
||||
server_url: "http://ionscale"
|
||||
|
||||
tls:
|
||||
disable: true
|
||||
@@ -12,5 +12,11 @@ database:
|
||||
type: sqlite
|
||||
url: /opt/ionscale.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(ON)
|
||||
|
||||
auth:
|
||||
provider:
|
||||
issuer: http://mockoidc/oidc
|
||||
client_id: "foo"
|
||||
client_secret: "bar"
|
||||
|
||||
logging:
|
||||
level: debug
|
||||
@@ -1,11 +1,12 @@
|
||||
FROM alpine:3.14.0
|
||||
|
||||
ARG TAILSCALE_VERSION
|
||||
|
||||
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
|
||||
FROM tailscale/tailscale:${TAILSCALE_VERSION} as src
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk update && apk add ca-certificates openssh curl && rm -rf /var/cache/apk/*
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=src /usr/local/bin/tailscale .
|
||||
COPY --from=src /usr/local/bin/tailscaled .
|
||||
|
||||
ENV TSFILE=tailscale_${TAILSCALE_VERSION}_amd64.tgz
|
||||
RUN wget https://pkgs.tailscale.com/stable/${TSFILE} && tar xzf ${TSFILE} --strip-components=1
|
||||
RUN mkdir -p /var/run/tailscale /var/cache/tailscale /var/lib/tailscale /.cache
|
||||
|
||||
+30
-28
@@ -2,36 +2,20 @@ package tests
|
||||
|
||||
import (
|
||||
"github.com/jsiebens/ionscale/tests/sc"
|
||||
"github.com/jsiebens/ionscale/tests/tsn"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPing(t *testing.T) {
|
||||
sc.Run(t, func(s sc.Scenario) {
|
||||
tailnet := s.CreateTailnet("pingtest")
|
||||
key := s.CreateAuthKey(tailnet.Id, true)
|
||||
|
||||
nodeA := s.NewTailscaleNode("pingtest-a")
|
||||
nodeB := s.NewTailscaleNode("pingtest-b")
|
||||
|
||||
nodeA.Up(key)
|
||||
nodeB.Up(key)
|
||||
|
||||
nodeA.WaitForPeers(1)
|
||||
nodeA.Ping("pingtest-b")
|
||||
nodeA.Ping(nodeB.IPv4())
|
||||
nodeA.Ping(nodeB.IPv6())
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetIPs(t *testing.T) {
|
||||
sc.Run(t, func(s sc.Scenario) {
|
||||
tailnet := s.CreateTailnet("tailnet01")
|
||||
sc.Run(t, func(s *sc.Scenario) {
|
||||
tailnet := s.CreateTailnet()
|
||||
authKey := s.CreateAuthKey(tailnet.Id, false)
|
||||
|
||||
tsNode := s.NewTailscaleNode("testip")
|
||||
tsNode := s.NewTailscaleNode()
|
||||
|
||||
tsNode.Up(authKey)
|
||||
require.NoError(t, tsNode.Up(authKey))
|
||||
|
||||
ip4 := tsNode.IPv4()
|
||||
ip6 := tsNode.IPv6()
|
||||
@@ -49,18 +33,36 @@ func TestGetIPs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPing(t *testing.T) {
|
||||
sc.Run(t, func(s *sc.Scenario) {
|
||||
tailnet := s.CreateTailnet()
|
||||
key := s.CreateAuthKey(tailnet.Id, true)
|
||||
|
||||
nodeA := s.NewTailscaleNode()
|
||||
nodeB := s.NewTailscaleNode()
|
||||
|
||||
require.NoError(t, nodeA.Up(key))
|
||||
require.NoError(t, nodeB.Up(key))
|
||||
|
||||
require.NoError(t, nodeA.WaitFor(tsn.PeerCount(1)))
|
||||
require.NoError(t, nodeA.Ping(nodeB.Hostname()))
|
||||
require.NoError(t, nodeA.Ping(nodeB.IPv4()))
|
||||
require.NoError(t, nodeA.Ping(nodeB.IPv6()))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNodeWithSameHostname(t *testing.T) {
|
||||
sc.Run(t, func(s sc.Scenario) {
|
||||
tailnet := s.CreateTailnet("tailnet01")
|
||||
sc.Run(t, func(s *sc.Scenario) {
|
||||
tailnet := s.CreateTailnet()
|
||||
authKey := s.CreateAuthKey(tailnet.Id, false)
|
||||
|
||||
tsNode := s.NewTailscaleNode("test")
|
||||
tsNode := s.NewTailscaleNode(sc.WithName("test"))
|
||||
|
||||
_ = tsNode.Up(authKey)
|
||||
require.NoError(t, tsNode.Up(authKey))
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
tc := s.NewTailscaleNode("test")
|
||||
_ = tc.Up(authKey)
|
||||
tc := s.NewTailscaleNode(sc.WithName("test"))
|
||||
require.NoError(t, tc.Up(authKey))
|
||||
}
|
||||
|
||||
machines := make(map[string]bool)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"github.com/jsiebens/ionscale/pkg/defaults"
|
||||
ionscalev1 "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
"github.com/jsiebens/ionscale/tests/sc"
|
||||
"github.com/jsiebens/ionscale/tests/tsn"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/tailcfg"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNodeAttrs(t *testing.T) {
|
||||
sc.Run(t, func(s *sc.Scenario) {
|
||||
tailnet := s.CreateTailnet()
|
||||
key := s.CreateAuthKey(tailnet.Id, true)
|
||||
|
||||
nodeA := s.NewTailscaleNode()
|
||||
require.NoError(t, nodeA.Up(key))
|
||||
|
||||
policy := defaults.DefaultACLPolicy()
|
||||
policy.Nodeattrs = []*ionscalev1.NodeAttr{
|
||||
{
|
||||
Target: []string{"tag:test"},
|
||||
Attr: []string{"ionscale:test"},
|
||||
},
|
||||
}
|
||||
|
||||
s.SetACLPolicy(tailnet.Id, policy)
|
||||
|
||||
require.NoError(t, nodeA.WaitFor(tsn.HasCapability("ionscale:test")))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNodeAttrs_IgnoreFunnelAttr(t *testing.T) {
|
||||
sc.Run(t, func(s *sc.Scenario) {
|
||||
tailnet := s.CreateTailnet()
|
||||
key := s.CreateAuthKey(tailnet.Id, true)
|
||||
|
||||
nodeA := s.NewTailscaleNode()
|
||||
require.NoError(t, nodeA.Up(key))
|
||||
|
||||
policy := defaults.DefaultACLPolicy()
|
||||
policy.Nodeattrs = []*ionscalev1.NodeAttr{
|
||||
{
|
||||
Target: []string{"tag:test"},
|
||||
Attr: []string{"ionscale:test", string(tailcfg.NodeAttrFunnel)},
|
||||
},
|
||||
}
|
||||
|
||||
s.SetACLPolicy(tailnet.Id, policy)
|
||||
|
||||
require.NoError(t, nodeA.WaitFor(tsn.HasCapability("ionscale:test")))
|
||||
require.NoError(t, nodeA.WaitFor(tsn.IsMissingCapability(tailcfg.NodeAttrFunnel)))
|
||||
})
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package sc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ory/dockertest/v3"
|
||||
"strings"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TailscaleNode interface {
|
||||
Hostname() string
|
||||
Up(authkey string) ipnstate.Status
|
||||
IPv4() string
|
||||
IPv6() string
|
||||
WaitForPeers(expected int)
|
||||
Ping(target string)
|
||||
}
|
||||
|
||||
type tailscaleNode struct {
|
||||
t *testing.T
|
||||
loginServer string
|
||||
hostname string
|
||||
resource *dockertest.Resource
|
||||
}
|
||||
|
||||
func (t *tailscaleNode) Hostname() string {
|
||||
return t.hostname
|
||||
}
|
||||
|
||||
func (t *tailscaleNode) Up(authkey string) ipnstate.Status {
|
||||
t.mustExecTailscaleCmd("up", "--login-server", t.loginServer, "--authkey", authkey)
|
||||
return t.waitForReady()
|
||||
}
|
||||
|
||||
func (t *tailscaleNode) IPv4() string {
|
||||
return t.mustExecTailscaleCmd("ip", "-4")
|
||||
}
|
||||
|
||||
func (t *tailscaleNode) IPv6() string {
|
||||
return t.mustExecTailscaleCmd("ip", "-6")
|
||||
}
|
||||
|
||||
func (t *tailscaleNode) waitForReady() ipnstate.Status {
|
||||
var status ipnstate.Status
|
||||
err := pool.Retry(func() error {
|
||||
out, err := t.execTailscaleCmd("status", "--json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(out), &status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if status.CurrentTailnet != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("not connected")
|
||||
})
|
||||
if err != nil {
|
||||
t.t.Fatal(err)
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func (t *tailscaleNode) WaitForPeers(expected int) {
|
||||
err := pool.Retry(func() error {
|
||||
out, err := t.execTailscaleCmd("status", "--json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var status ipnstate.Status
|
||||
if err := json.Unmarshal([]byte(out), &status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(status.Peers()) != expected {
|
||||
return fmt.Errorf("incorrect peer count")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tailscaleNode) Ping(target string) {
|
||||
result, err := t.execTailscaleCmd("ping", "--timeout=1s", "--c=10", "--until-direct=true", target)
|
||||
if err != nil {
|
||||
t.t.Fatal(err)
|
||||
}
|
||||
|
||||
if !strings.Contains(result, "pong") && !strings.Contains(result, "is local") {
|
||||
t.t.Fatal("ping failed")
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tailscaleNode) execTailscaleCmd(cmd ...string) (string, error) {
|
||||
i := append([]string{"/app/tailscale", "--socket=/tmp/tailscaled.sock"}, cmd...)
|
||||
return execCmd(t.resource, i...)
|
||||
}
|
||||
|
||||
func (t *tailscaleNode) mustExecTailscaleCmd(cmd ...string) string {
|
||||
i := append([]string{"/app/tailscale", "--socket=/tmp/tailscaled.sock"}, cmd...)
|
||||
s, err := execCmd(t.resource, i...)
|
||||
if err != nil {
|
||||
t.t.Fatal(err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func execCmd(resource *dockertest.Resource, cmd ...string) (string, error) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
exitCode, err := resource.Exec(cmd, dockertest.ExecOptions{StdOut: &stdout, StdErr: &stderr})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return strings.TrimSpace(stdout.String()), err
|
||||
}
|
||||
|
||||
if exitCode != 0 {
|
||||
return strings.TrimSpace(stdout.String()), fmt.Errorf("command failed with: %s", stderr.String())
|
||||
}
|
||||
|
||||
return strings.TrimSpace(stdout.String()), nil
|
||||
}
|
||||
+118
-72
@@ -4,11 +4,17 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bufbuild/connect-go"
|
||||
petname "github.com/dustinkirkland/golang-petname"
|
||||
ionscaleclt "github.com/jsiebens/ionscale/pkg/client/ionscale"
|
||||
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
|
||||
ionscaleconnect "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1/ionscalev1connect"
|
||||
"github.com/jsiebens/ionscale/tests/tsn"
|
||||
"github.com/jsiebens/mockoidc"
|
||||
mockoidcv1 "github.com/jsiebens/mockoidc/pkg/gen/mockoidc/v1"
|
||||
"github.com/jsiebens/mockoidc/pkg/gen/mockoidc/v1/mockoidcv1connect"
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"io"
|
||||
"log"
|
||||
@@ -21,7 +27,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const DefaultTargetVersion = "1.56.0"
|
||||
const DefaultTargetVersion = "stable"
|
||||
|
||||
var (
|
||||
setupOnce sync.Once
|
||||
@@ -29,51 +35,88 @@ var (
|
||||
pool *dockertest.Pool
|
||||
)
|
||||
|
||||
type Scenario interface {
|
||||
NewTailscaleNode(hostname string) TailscaleNode
|
||||
|
||||
ListMachines(tailnetID uint64) []*api.Machine
|
||||
CreateAuthKey(tailnetID uint64, ephemeral bool) string
|
||||
CreateTailnet(name string) *api.Tailnet
|
||||
type Scenario struct {
|
||||
t *testing.T
|
||||
pool *dockertest.Pool
|
||||
network *dockertest.Network
|
||||
mockoidc *dockertest.Resource
|
||||
ionscale *dockertest.Resource
|
||||
resources []*dockertest.Resource
|
||||
ionscaleClient ionscaleconnect.IonscaleServiceClient
|
||||
mockoidcClient mockoidcv1connect.MockOIDCServiceClient
|
||||
}
|
||||
|
||||
type scenario struct {
|
||||
t *testing.T
|
||||
pool *dockertest.Pool
|
||||
network *dockertest.Network
|
||||
ionscale *dockertest.Resource
|
||||
resources []*dockertest.Resource
|
||||
client ionscaleconnect.IonscaleServiceClient
|
||||
}
|
||||
|
||||
func (s *scenario) CreateTailnet(name string) *api.Tailnet {
|
||||
createTailnetResponse, err := s.client.CreateTailnet(context.Background(), connect.NewRequest(&api.CreateTailnetRequest{Name: name}))
|
||||
if err != nil {
|
||||
s.t.Fatal(err)
|
||||
}
|
||||
func (s *Scenario) CreateTailnet() *api.Tailnet {
|
||||
name := petname.Generate(3, "-")
|
||||
createTailnetResponse, err := s.ionscaleClient.CreateTailnet(context.Background(), connect.NewRequest(&api.CreateTailnetRequest{Name: name}))
|
||||
require.NoError(s.t, err)
|
||||
return createTailnetResponse.Msg.GetTailnet()
|
||||
}
|
||||
|
||||
func (s *scenario) CreateAuthKey(tailnetID uint64, ephemeral bool) string {
|
||||
key, err := s.client.CreateAuthKey(context.Background(), connect.NewRequest(&api.CreateAuthKeyRequest{TailnetId: tailnetID, Ephemeral: ephemeral, Tags: []string{"tag:test"}, Expiry: durationpb.New(60 * time.Minute)}))
|
||||
if err != nil {
|
||||
s.t.Fatal(err)
|
||||
func (s *Scenario) CreateAuthKey(tailnetID uint64, ephemeral bool, tags ...string) string {
|
||||
if len(tags) == 0 {
|
||||
tags = []string{"tag:test"}
|
||||
}
|
||||
key, err := s.ionscaleClient.CreateAuthKey(context.Background(), connect.NewRequest(&api.CreateAuthKeyRequest{TailnetId: tailnetID, Ephemeral: ephemeral, Tags: tags, Expiry: durationpb.New(60 * time.Minute)}))
|
||||
require.NoError(s.t, err)
|
||||
return key.Msg.Value
|
||||
}
|
||||
|
||||
func (s *scenario) ListMachines(tailnetID uint64) []*api.Machine {
|
||||
machines, err := s.client.ListMachines(context.Background(), connect.NewRequest(&api.ListMachinesRequest{TailnetId: tailnetID}))
|
||||
if err != nil {
|
||||
s.t.Fatal(err)
|
||||
}
|
||||
func (s *Scenario) ListMachines(tailnetID uint64) []*api.Machine {
|
||||
machines, err := s.ionscaleClient.ListMachines(context.Background(), connect.NewRequest(&api.ListMachinesRequest{TailnetId: tailnetID}))
|
||||
require.NoError(s.t, err)
|
||||
return machines.Msg.Machines
|
||||
}
|
||||
|
||||
func (s *scenario) NewTailscaleNode(hostname string) TailscaleNode {
|
||||
tailscaleOptions := &dockertest.RunOptions{
|
||||
func (s *Scenario) AuthorizeMachines(tailnetID uint64) {
|
||||
machines := s.ListMachines(tailnetID)
|
||||
for _, m := range machines {
|
||||
_, err := s.ionscaleClient.AuthorizeMachine(context.Background(), connect.NewRequest(&api.AuthorizeMachineRequest{MachineId: m.Id}))
|
||||
require.NoError(s.t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scenario) SetACLPolicy(tailnetID uint64, policy *api.ACLPolicy) {
|
||||
_, err := s.ionscaleClient.SetACLPolicy(context.Background(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tailnetID, Policy: policy}))
|
||||
require.NoError(s.t, err)
|
||||
}
|
||||
|
||||
func (s *Scenario) SetIAMPolicy(tailnetID uint64, policy *api.IAMPolicy) {
|
||||
_, err := s.ionscaleClient.SetIAMPolicy(context.Background(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tailnetID, Policy: policy}))
|
||||
require.NoError(s.t, err)
|
||||
}
|
||||
|
||||
func (s *Scenario) EnableMachineAutorization(tailnetID uint64) {
|
||||
_, err := s.ionscaleClient.EnableMachineAuthorization(context.Background(), connect.NewRequest(&api.EnableMachineAuthorizationRequest{TailnetId: tailnetID}))
|
||||
require.NoError(s.t, err)
|
||||
}
|
||||
|
||||
func (s *Scenario) PushOIDCUser(sub, email, preferredUsername string) {
|
||||
_, err := s.mockoidcClient.PushUser(context.Background(), connect.NewRequest(&mockoidcv1.PushUserRequest{Subject: sub, Email: email, PreferredUsername: preferredUsername}))
|
||||
require.NoError(s.t, err)
|
||||
}
|
||||
|
||||
type TailscaleNodeConfig struct {
|
||||
Hostname string
|
||||
}
|
||||
|
||||
type TailscaleNodeOpt = func(*TailscaleNodeConfig)
|
||||
|
||||
func WithName(name string) TailscaleNodeOpt {
|
||||
return func(config *TailscaleNodeConfig) {
|
||||
config.Hostname = name
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scenario) NewTailscaleNode(opts ...TailscaleNodeOpt) *tsn.TailscaleNode {
|
||||
config := &TailscaleNodeConfig{Hostname: petname.Generate(3, "-")}
|
||||
for _, o := range opts {
|
||||
o(config)
|
||||
}
|
||||
|
||||
runOpts := &dockertest.RunOptions{
|
||||
Repository: fmt.Sprintf("ts-%s", strings.Replace(targetVersion, ".", "-", -1)),
|
||||
Hostname: hostname,
|
||||
Hostname: config.Hostname,
|
||||
Networks: []*dockertest.Network{s.network},
|
||||
ExposedPorts: []string{"1055"},
|
||||
Cmd: []string{
|
||||
@@ -82,29 +125,20 @@ func (s *scenario) NewTailscaleNode(hostname string) TailscaleNode {
|
||||
}
|
||||
|
||||
resource, err := s.pool.RunWithOptions(
|
||||
tailscaleOptions,
|
||||
runOpts,
|
||||
restartPolicy,
|
||||
)
|
||||
if err != nil {
|
||||
s.t.Fatal(err)
|
||||
}
|
||||
require.NoError(s.t, err)
|
||||
|
||||
err = s.pool.Retry(portCheck(resource.GetPort("1055/tcp")))
|
||||
if err != nil {
|
||||
s.t.Fatal(err)
|
||||
}
|
||||
require.NoError(s.t, err)
|
||||
|
||||
s.resources = append(s.resources, resource)
|
||||
|
||||
return &tailscaleNode{
|
||||
t: s.t,
|
||||
loginServer: "http://ionscale:8080",
|
||||
hostname: hostname,
|
||||
resource: resource,
|
||||
}
|
||||
return tsn.New(s.t, config.Hostname, "http://ionscale", resource, s.pool.Retry)
|
||||
}
|
||||
|
||||
func Run(t *testing.T, f func(s Scenario)) {
|
||||
func Run(t *testing.T, f func(s *Scenario)) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipped due to -short flag")
|
||||
}
|
||||
@@ -116,7 +150,7 @@ func Run(t *testing.T, f func(s Scenario)) {
|
||||
}
|
||||
|
||||
var err error
|
||||
s := &scenario{t: t}
|
||||
s := &Scenario{t: t}
|
||||
|
||||
defer func() {
|
||||
for _, r := range s.resources {
|
||||
@@ -127,6 +161,10 @@ func Run(t *testing.T, f func(s Scenario)) {
|
||||
_ = pool.Purge(s.ionscale)
|
||||
}
|
||||
|
||||
if s.mockoidc != nil {
|
||||
_ = pool.Purge(s.mockoidc)
|
||||
}
|
||||
|
||||
if s.network != nil {
|
||||
_ = s.network.Close()
|
||||
}
|
||||
@@ -135,18 +173,33 @@ func Run(t *testing.T, f func(s Scenario)) {
|
||||
s.network = nil
|
||||
}()
|
||||
|
||||
if s.pool, err = dockertest.NewPool(""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s.pool, err = dockertest.NewPool("")
|
||||
require.NoError(t, err)
|
||||
|
||||
s.network, err = pool.CreateNetwork("ionscale-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(s.t, err)
|
||||
|
||||
currentPath, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
require.NoError(s.t, err)
|
||||
|
||||
// run mockoidc container
|
||||
{
|
||||
mockoidcOpts := &dockertest.RunOptions{
|
||||
Hostname: "mockoidc",
|
||||
Repository: "ghcr.io/jsiebens/mockoidc",
|
||||
Networks: []*dockertest.Network{s.network},
|
||||
ExposedPorts: []string{"80"},
|
||||
Cmd: []string{"--listen-addr", ":80", "--server-url", "http://mockoidc"},
|
||||
}
|
||||
|
||||
s.mockoidc, err = pool.RunWithOptions(mockoidcOpts, restartPolicy)
|
||||
require.NoError(s.t, err)
|
||||
|
||||
port := s.mockoidc.GetPort("80/tcp")
|
||||
err = pool.Retry(httpCheck(port, "/oidc/.well-known/openid-configuration"))
|
||||
require.NoError(s.t, err)
|
||||
|
||||
s.mockoidcClient = mockoidc.NewClient(fmt.Sprintf("http://localhost:%s", port), true)
|
||||
}
|
||||
|
||||
ionscale := &dockertest.RunOptions{
|
||||
@@ -156,31 +209,24 @@ func Run(t *testing.T, f func(s Scenario)) {
|
||||
fmt.Sprintf("%s/config:/etc/ionscale", currentPath),
|
||||
},
|
||||
Networks: []*dockertest.Network{s.network},
|
||||
ExposedPorts: []string{"8080"},
|
||||
ExposedPorts: []string{"80"},
|
||||
Cmd: []string{"server", "--config", "/etc/ionscale/config.yaml"},
|
||||
}
|
||||
|
||||
s.ionscale, err = pool.RunWithOptions(ionscale, restartPolicy)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(s.t, err)
|
||||
|
||||
port := s.ionscale.GetPort("8080/tcp")
|
||||
port := s.ionscale.GetPort("80/tcp")
|
||||
|
||||
err = pool.Retry(httpCheck(port, "/key"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(s.t, err)
|
||||
|
||||
auth, err := ionscaleclt.LoadClientAuth("804ecd57365342254ce6647da5c249e85c10a0e51e74856bfdf292a2136b4249")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
addr := fmt.Sprintf("http://localhost:%s", port)
|
||||
auth, err := ionscaleclt.LoadClientAuth(addr, "804ecd57365342254ce6647da5c249e85c10a0e51e74856bfdf292a2136b4249")
|
||||
require.NoError(s.t, err)
|
||||
|
||||
s.client, err = ionscaleclt.NewClient(auth, fmt.Sprintf("http://localhost:%s", port), true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s.ionscaleClient, err = ionscaleclt.NewClient(auth, addr, true)
|
||||
require.NoError(s.t, err)
|
||||
|
||||
f(s)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package tsn
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/views"
|
||||
)
|
||||
|
||||
type Condition = func(*ipnstate.Status) bool
|
||||
|
||||
func Connected() Condition {
|
||||
return func(status *ipnstate.Status) bool {
|
||||
return status.CurrentTailnet != nil
|
||||
}
|
||||
}
|
||||
|
||||
func HasTailnet(tailnet string) Condition {
|
||||
return func(status *ipnstate.Status) bool {
|
||||
return status.CurrentTailnet != nil && status.CurrentTailnet.Name == tailnet
|
||||
}
|
||||
}
|
||||
|
||||
func HasTag(tag string) Condition {
|
||||
return func(status *ipnstate.Status) bool {
|
||||
return status.Self != nil && status.Self.Tags != nil && views.SliceContains[string](*status.Self.Tags, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func NeedsMachineAuth() Condition {
|
||||
return func(status *ipnstate.Status) bool {
|
||||
return status.BackendState == "NeedsMachineAuth"
|
||||
}
|
||||
}
|
||||
|
||||
func IsRunning() Condition {
|
||||
return func(status *ipnstate.Status) bool {
|
||||
return status.BackendState == "Running"
|
||||
}
|
||||
}
|
||||
|
||||
func HasUser(email string) Condition {
|
||||
return func(status *ipnstate.Status) bool {
|
||||
if status.Self == nil {
|
||||
return false
|
||||
}
|
||||
userID := status.Self.UserID
|
||||
if u, ok := status.User[userID]; ok {
|
||||
return u.LoginName == email
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func PeerCount(expected int) Condition {
|
||||
return func(status *ipnstate.Status) bool {
|
||||
return len(status.Peers()) == expected
|
||||
}
|
||||
}
|
||||
|
||||
func HasCapability(capability tailcfg.NodeCapability) Condition {
|
||||
return func(status *ipnstate.Status) bool {
|
||||
self := status.Self
|
||||
|
||||
if self == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if slices.Contains(self.Capabilities, capability) {
|
||||
return true
|
||||
}
|
||||
|
||||
if _, ok := self.CapMap[capability]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func IsMissingCapability(capability tailcfg.NodeCapability) Condition {
|
||||
return func(status *ipnstate.Status) bool {
|
||||
self := status.Self
|
||||
|
||||
if slices.Contains(self.Capabilities, capability) {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, ok := self.CapMap[capability]; ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user