Compare commits

...

74 Commits

Author SHA1 Message Date
Johan Siebens b265fc42c7 feat: implement tag owners 2022-09-16 16:30:51 +02:00
Johan Siebens 69dd1f6b95 fix: don't strip tag: prefix from tag values 2022-09-16 14:49:06 +02:00
Johan Siebens ebf0016096 fix: always show machines of the same user 2022-09-16 14:40:23 +02:00
Johan Siebens 3aa2d68ce2 fix: validate tags when creating auth keys 2022-09-16 14:20:50 +02:00
Johan Siebens 3d03f49138 feat: update on how to show dns config 2022-09-16 14:04:48 +02:00
Johan Siebens c4783f8165 chore: cleanup event listener tool 2022-09-16 13:15:31 +02:00
Johan Siebens 3b9ce04ec8 feat: add methods to enable and disable single routes 2022-09-16 11:33:14 +02:00
Johan Siebens f71ca49693 fix: send correct nameservers when using split dns 2022-09-16 10:20:54 +02:00
Johan Siebens 61d78fe121 chore: don't save default derp in db 2022-09-10 15:58:02 +02:00
Johan Siebens 5b51e29140 fix: disable logtail for now 2022-09-10 12:29:40 +02:00
Johan Siebens e5ed4713d8 feat: make keep alive interval configurable 2022-09-10 12:25:30 +02:00
Johan Siebens 9281deb549 feat: force http to https redirect even when tls is disabled 2022-09-10 09:18:32 +02:00
Johan Siebens 88509c826d feature: force https 2022-09-10 08:34:40 +02:00
Johan Siebens 405110867a fix: change port to match default server config 2022-09-10 08:12:44 +02:00
Johan Siebens 82c814aa2a fix: change metrics port to 9091, a more default port for prometheus clients 2022-09-10 08:12:14 +02:00
Johan Siebens 5a524d7357 chore: go mod tidy 2022-09-10 08:11:28 +02:00
Johan Siebens 0f0829ccba fix: use same name for admin key env variable in client and server 2022-09-10 07:28:04 +02:00
Johan Siebens 4c9ea463db feat: add number of connected machines metric 2022-09-09 22:17:06 +02:00
Johan Siebens 284ec18339 feat: add command to generate a simple config 2022-09-09 21:21:57 +02:00
Johan Siebens 5a77d2b35b feat: decouple db migrations from domain model 2022-09-07 10:36:36 +02:00
Johan Siebens c193a4bf71 fix: correct registration and cli authentication flows 2022-09-07 10:25:40 +02:00
Johan Siebens 550febc5ba fix: use abs path, giving a proper error when file does not exists 2022-09-06 16:25:21 +02:00
Johan Siebens f0d71c8a66 chore: bump alpine version 2022-09-06 16:25:21 +02:00
Johan Siebens 3c50d4869d Create CONTRIBUTING.md 2022-09-06 15:08:10 +02:00
Johan Siebens e8fe0e2467 chore(deps): upgrade dependencies 2022-09-06 14:55:22 +02:00
Johan Siebens 633f29003c chore(deps): upgrade tailscale dependency 2022-09-06 14:49:31 +02:00
Johan Siebens 145ae6ab1d chore: bump to go 1.19 2022-09-06 11:36:31 +02:00
Johan Siebens b60e332cbd docs: update readme 2022-09-06 09:48:59 +02:00
Johan Siebens f38939415d feat: lock database when migrating 2022-09-06 08:19:45 +02:00
Johan Siebens 49e5c7999f feat: make system admin key optional 2022-09-05 17:07:33 +02:00
Johan Siebens 82a28e32c0 feat: read control and legacy control key from environment 2022-09-05 16:58:53 +02:00
Johan Siebens 7976e7aa83 fix: add env variable for enabling acme 2022-09-05 16:58:17 +02:00
Johan Siebens 404b667aaf chore: rename cert magic config to acme 2022-09-05 16:16:09 +02:00
Johan Siebens 6700d0db01 feat: add support for postgres 2022-09-05 16:01:22 +02:00
Johan Siebens 25ee5a21a6 feat: save current tailnet for subsequent requests 2022-09-05 11:38:25 +02:00
Johan Siebens d735974406 fix: add csrf and remove need of a cache 2022-09-03 17:33:22 +02:00
Johan Siebens 41827dcdcd fix: increase poll frequency when waiting for authentication 2022-08-31 16:35:55 +02:00
Johan Siebens cd1854f510 chore(ci): bump checkout version 2022-08-31 14:23:40 +02:00
Johan Siebens 6a6049b76b chore(ci): pin go version 2022-08-31 13:59:39 +02:00
Johan Siebens 50d52ae481 chore(ci): add nightly job 2022-08-31 13:08:49 +02:00
Johan Siebens 402f98b688 chore(deps): upgrade cosign action 2022-08-31 13:02:25 +02:00
Johan Siebens 4234c5eed9 feat: login as system admin using oidc 2022-08-31 11:21:31 +02:00
Johan Siebens 3568764ec1 feat: get machine details 2022-08-26 09:34:15 +02:00
Johan Siebens df02644437 fix: expiration timestamp and disable flag 2022-08-25 15:59:52 +02:00
Johan Siebens 7db10b563d fix: update expiration date when authentication is succesful 2022-08-25 09:04:23 +02:00
Johan Siebens 496fd5f47c chore: configure auth provider using env variables 2022-08-22 13:22:17 +02:00
Johan Siebens 200b523ae0 chore: rename to AuthProvider 2022-07-15 07:57:56 +02:00
Johan Siebens f225f427ac fix: foreign key violation when deleting tailnet 2022-07-15 07:43:31 +02:00
Johan Siebens 70e84be8f4 feat: delete users 2022-07-15 07:39:19 +02:00
Johan Siebens 409dd3aa5f fix: exclude 'service' users in list 2022-07-14 07:54:07 +02:00
Johan Siebens 0d5ffa9c8b feat: read keys from config file 2022-07-06 07:55:02 +02:00
Johan Siebens 0756de5bfb fix: send dns update correctly 2022-07-02 08:42:13 +02:00
Johan Siebens 32cb12e286 chore: remove auth method and configure oidc via config file 2022-07-02 08:31:59 +02:00
Johan Siebens f6961cf2f7 feat: delete auth method 2022-06-28 09:38:31 +02:00
Johan Siebens ba379e1b65 feat: list users 2022-06-22 08:26:59 +02:00
Johan Siebens 12eb258e1e feat: user roles 2022-06-22 07:47:09 +02:00
Johan Siebens 32c396a972 fix: incorrect json tag 2022-06-16 11:16:58 +02:00
Johan Siebens d0e69cc2bf feat: add method to get auth method 2022-06-16 09:12:54 +02:00
Johan Siebens 5e132392b3 chore: remove println 2022-06-14 14:41:23 +02:00
Johan Siebens 58e1f38231 fix: type safe acl policy in api 2022-06-14 14:41:09 +02:00
Johan Siebens d5f71224f6 feat: disable and enable key expiry 2022-06-12 08:31:04 +02:00
Johan Siebens 090e5c3c88 chore: add dns config as field of tailnet 2022-06-10 15:55:52 +02:00
Johan Siebens 8e8646b757 chore: add acl policy as field of tailnet 2022-06-10 15:49:07 +02:00
Johan Siebens a94e0ce9b8 feat: remove auth-filter in favor of a new IAM Policy setup 2022-06-10 15:36:21 +02:00
Johan Siebens eefa150738 fix: check if an auth method is available when authenticating with the cli 2022-06-06 15:30:35 +02:00
Johan Siebens bbe9d16294 feat: user auth 2022-06-06 15:21:13 +02:00
Johan Siebens 5fdde45fdd fix: add some more information and checks on cli flags 2022-06-06 15:19:44 +02:00
Johan Siebens 1715eb681d fix: remove whitespace when printing new key 2022-06-06 12:34:41 +02:00
Johan Siebens 9d29644941 chore: remove unused code 2022-06-05 14:25:58 +02:00
Johan Siebens da71a43990 feat: replace grpc with buf connect 2022-06-03 14:25:31 +02:00
Johan Siebens 687fcd16d1 fix: only expire machines from tailnet of the auth filter 2022-06-01 16:16:09 +02:00
Johan Siebens b9b42d8342 chore: remove unused code 2022-06-01 10:13:44 +02:00
Johan Siebens 1654680cab feat: set default key expiry 2022-06-01 10:13:44 +02:00
Johan Siebens 9df514036e chore(deps): updates 2022-05-30 10:49:46 +00:00
135 changed files with 9497 additions and 7224 deletions
+9
View File
@@ -0,0 +1,9 @@
## Contribution Policy
ionscale is open to code contributions for bug fixes only.
Features carry a long-term maintenance burden so they will not be accepted at this time.
Please [submit an issue][new-issue] if you have a feature you'd like to
request.
[new-issue]: https://github.com/jsiebens/ionscale/issues/new
+2 -4
View File
@@ -10,14 +10,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ^1.18
go-version: 1.19
- name: Build
run: |
+42
View File
@@ -0,0 +1,42 @@
name: nightly
on: workflow_dispatch
permissions:
contents: write
packages: write
id-token: write
jobs:
release:
runs-on: ubuntu-latest
env:
DOCKER_CLI_EXPERIMENTAL: "enabled"
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Docker Login
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
- name: Install cosign
uses: sigstore/cosign-installer@v2.5.1
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
distribution: goreleaser-pro
version: latest
args: release --nightly --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
+3 -3
View File
@@ -17,7 +17,7 @@ jobs:
DOCKER_CLI_EXPERIMENTAL: "enabled"
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up QEMU
@@ -31,9 +31,9 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ^1.18
go-version: 1.19
- name: Install cosign
uses: sigstore/cosign-installer@v2.0.0
uses: sigstore/cosign-installer@v2.5.1
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
+2 -2
View File
@@ -1,3 +1,3 @@
FROM alpine:3.15.4
FROM alpine:3.16.2
COPY ionscale /usr/local/bin/ionscale
ENTRYPOINT ["ionscale"]
ENTRYPOINT ["/usr/local/bin/ionscale"]
+6
View File
@@ -1,2 +1,8 @@
init:
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:
buf generate proto
+3
View File
@@ -1 +1,4 @@
# ionscale
> **Note**:
> ionscale is currently alpha quality, actively being developed and so subject to changes
+3 -6
View File
@@ -1,11 +1,8 @@
version: v1beta1
version: v1
plugins:
- name: go
out: pkg/gen
opt: paths=source_relative
- name: go-grpc
- name: connect-go
out: pkg/gen
opt: paths=source_relative,require_unimplemented_servers=false
- name: grpc-gateway
out: pkg/gen
opt: paths=source_relative
opt: paths=source_relative
+60
View File
@@ -0,0 +1,60 @@
package main
import (
"database/sql"
"fmt"
"github.com/muesli/coral"
"os"
"time"
"github.com/lib/pq"
)
func main() {
cmd := rootCommand()
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
func rootCommand() *coral.Command {
command := &coral.Command{
Use: "pg-ionscale-events",
}
var url string
command.Flags().StringVar(&url, "url", "", "")
_ = command.MarkFlagRequired("url")
command.RunE = func(cmd *coral.Command, args []string) error {
_, err := sql.Open("postgres", url)
if err != nil {
return err
}
reportProblem := func(ev pq.ListenerEventType, err error) {
if err != nil {
fmt.Println(err.Error())
}
}
minReconn := 10 * time.Second
maxReconn := time.Minute
listener := pq.NewListener(url, minReconn, maxReconn, reportProblem)
err = listener.Listen("ionscale_events")
if err != nil {
return err
}
fmt.Println("listening for events ...")
fmt.Println("")
for {
select {
case n, _ := <-listener.Notify:
fmt.Println(n.Extra)
}
}
}
return command
}
+69 -62
View File
@@ -1,102 +1,109 @@
module github.com/jsiebens/ionscale
go 1.18
go 1.19
require (
github.com/apparentlymart/go-cidr v1.1.0
github.com/caddyserver/certmagic v0.16.1
github.com/coreos/go-oidc/v3 v3.2.0
github.com/glebarez/sqlite v1.4.3
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.2
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/bufbuild/connect-go v0.4.0
github.com/caddyserver/certmagic v0.17.1
github.com/coreos/go-oidc/v3 v3.3.0
github.com/glebarez/sqlite v1.4.6
github.com/go-gormigrate/gormigrate/v2 v2.0.2
github.com/google/uuid v1.3.0
github.com/hashicorp/go-bexpr v0.1.11
github.com/hashicorp/go-hclog v1.1.0
github.com/improbable-eng/grpc-web v0.15.0
github.com/klauspost/compress v1.15.3
github.com/labstack/echo-contrib v0.12.0
github.com/labstack/echo/v4 v4.6.3
github.com/hashicorp/go-hclog v1.3.0
github.com/hashicorp/go-multierror v1.1.1
github.com/klauspost/compress v1.15.9
github.com/labstack/echo-contrib v0.13.0
github.com/labstack/echo/v4 v4.9.0
github.com/lib/pq v1.10.6
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/pointerstructure v1.2.1
github.com/mr-tron/base58 v1.2.0
github.com/muesli/coral v1.0.0
github.com/nleeper/goment v1.4.4
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.13.0
github.com/rodaine/table v1.0.1
github.com/soheilhy/cmux v0.1.5
github.com/sony/sonyflake v1.0.0
github.com/sony/sonyflake v1.1.0
github.com/stretchr/testify v1.8.0
github.com/xhit/go-str2duration/v2 v2.0.0
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
google.golang.org/grpc v1.44.0
google.golang.org/protobuf v1.27.1
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.0
gorm.io/gorm v1.23.5
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6
tailscale.com v1.24.2
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/postgres v1.3.9
gorm.io/gorm v1.23.8
inet.af/netaddr v0.0.0-20220811202034-502d2d690317
tailscale.com v1.30.0
)
require (
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/glebarez/go-sqlite v1.16.0 // indirect
github.com/glebarez/go-sqlite v1.18.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.7 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.13.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.1 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.12.0 // indirect
github.com/jackc/pgx/v4 v4.17.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/native v1.0.0 // indirect
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b // indirect
github.com/klauspost/cpuid/v2 v2.0.11 // indirect
github.com/jsimonetti/rtnetlink v1.2.2 // indirect
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
github.com/labstack/gommon v0.3.1 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mdlayher/netlink v1.6.0 // indirect
github.com/mdlayher/socket v0.2.3 // indirect
github.com/mholt/acmez v1.0.2 // indirect
github.com/miekg/dns v1.1.46 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/pointerstructure v1.2.1 // indirect
github.com/prometheus/client_golang v1.11.0 // indirect
github.com/mholt/acmez v1.0.4 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rs/cors v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tkuchiki/go-timezone v0.2.0 // indirect
github.com/tkuchiki/go-timezone v0.2.2 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.21.0 // indirect
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
golang.org/x/tools v0.1.11-0.20220413170336-afc6aad76eb1 // indirect
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect
go4.org/intern v0.0.0-20220617035311-6925f38cc365 // indirect
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
golang.org/x/tools v0.1.12 // indirect
golang.zx2c4.com/wireguard/windows v0.4.10 // indirect
google.golang.org/appengine v1.6.6 // indirect
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
modernc.org/libc v1.14.12 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.7 // indirect
modernc.org/sqlite v1.16.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
modernc.org/libc v1.18.0 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.3.0 // indirect
modernc.org/sqlite v1.18.1 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)
+468 -441
View File
File diff suppressed because it is too large Load Diff
+28 -12
View File
@@ -3,18 +3,34 @@ package addr
import (
"github.com/apparentlymart/go-cidr/cidr"
"github.com/jsiebens/ionscale/internal/util"
"inet.af/netaddr"
"math/big"
"net"
"net/netip"
"tailscale.com/net/tsaddr"
)
var ipv4Range = tsaddr.CGNATRange().IPNet()
var (
ipv4Range *net.IPNet
ipv4Count uint64
)
type Predicate func(netaddr.IP) (bool, error)
func init() {
ipv4Range, ipv4Count = prepareIP4Range()
}
func SelectIP(predicate Predicate) (*netaddr.IP, *netaddr.IP, error) {
ip4, err := selectIP(ipv4Range, predicate)
func prepareIP4Range() (*net.IPNet, uint64) {
cgnatRange := tsaddr.CGNATRange()
_, ipNet, err := net.ParseCIDR(cgnatRange.String())
if err != nil {
panic(err)
}
return ipNet, cidr.AddressCount(ipNet)
}
type Predicate func(netip.Addr) (bool, error)
func SelectIP(predicate Predicate) (*netip.Addr, *netip.Addr, error) {
ip4, err := selectIP(predicate)
if err != nil {
return nil, nil, err
}
@@ -22,16 +38,16 @@ func SelectIP(predicate Predicate) (*netaddr.IP, *netaddr.IP, error) {
return ip4, &ip6, err
}
func selectIP(c *net.IPNet, predicate Predicate) (*netaddr.IP, error) {
count := cidr.AddressCount(c)
var n = util.RandUint64(count)
func selectIP(predicate Predicate) (*netip.Addr, error) {
var n = util.RandUint64(ipv4Count)
for {
stdIP, err := cidr.HostBig(c, big.NewInt(int64(n)))
stdIP, err := cidr.HostBig(ipv4Range, big.NewInt(int64(n)))
if err != nil {
return nil, err
}
ip, _ := netaddr.FromStdIP(stdIP)
ip, _ := netip.AddrFromSlice(stdIP)
ok, err := validateIP(ip, predicate)
if err != nil {
return nil, err
@@ -39,11 +55,11 @@ func selectIP(c *net.IPNet, predicate Predicate) (*netaddr.IP, error) {
if ok {
return &ip, nil
}
n = (n + 1) % count
n = (n + 1) % ipv4Count
}
}
func validateIP(ip netaddr.IP, p Predicate) (bool, error) {
func validateIP(ip netip.Addr, p Predicate) (bool, error) {
if tsaddr.IsTailscaleIP(ip) {
if p != nil {
return p(ip)
-133
View File
@@ -1,134 +1 @@
package broker
import (
"sync"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
type BrokerPool struct {
lock sync.Mutex
store map[uint64]Broker
}
type Signal struct {
PeerUpdated *uint64
PeersRemoved []uint64
ACLUpdated bool
DNSUpdated bool
derpMap *tailcfg.DERPMap
}
type Broker interface {
AddClient(*Client)
RemoveClient(uint64)
SignalUpdate()
SignalPeerUpdated(id uint64)
SignalPeersRemoved([]uint64)
SignalDNSUpdated()
SignalACLUpdated()
SignalDERPMapUpdated(c *tailcfg.DERPMap)
IsConnected(uint64) bool
}
func NewBrokerPool() *BrokerPool {
return &BrokerPool{
store: make(map[uint64]Broker),
}
}
func (m *BrokerPool) Get(tailnetID uint64) Broker {
m.lock.Lock()
defer m.lock.Unlock()
b, ok := m.store[tailnetID]
if !ok {
b = newBroker(tailnetID)
m.store[tailnetID] = b
}
return b
}
func (m *BrokerPool) SignalDERPMapUpdated(c *tailcfg.DERPMap) {
m.lock.Lock()
defer m.lock.Unlock()
for _, b := range m.store {
b.SignalDERPMapUpdated(c)
}
}
func newBroker(tailnetID uint64) Broker {
b := &broker{
tailnetID: tailnetID,
newClients: make(chan *Client),
closingClients: make(chan uint64),
clients: make(map[uint64]*Client),
signalChannel: make(chan *Signal),
}
go b.listen()
return b
}
type broker struct {
tailnetID uint64
privateKey *key.MachinePrivate
newClients chan *Client
closingClients chan uint64
signalChannel chan *Signal
clients map[uint64]*Client
}
func (h *broker) IsConnected(id uint64) (ok bool) {
_, ok = h.clients[id]
return
}
func (h *broker) AddClient(client *Client) {
h.newClients <- client
}
func (h *broker) RemoveClient(id uint64) {
h.closingClients <- id
}
func (h *broker) SignalUpdate() {
h.signalChannel <- &Signal{}
}
func (h *broker) SignalPeerUpdated(id uint64) {
h.signalChannel <- &Signal{PeerUpdated: &id}
}
func (h *broker) SignalPeersRemoved(ids []uint64) {
h.signalChannel <- &Signal{PeersRemoved: ids}
}
func (h *broker) SignalDNSUpdated() {
h.signalChannel <- &Signal{DNSUpdated: true}
}
func (h *broker) SignalACLUpdated() {
h.signalChannel <- &Signal{ACLUpdated: true}
}
func (h *broker) SignalDERPMapUpdated(c *tailcfg.DERPMap) {
h.signalChannel <- &Signal{derpMap: c}
}
func (h *broker) listen() {
for {
select {
case s := <-h.newClients:
h.clients[s.id] = s
case s := <-h.closingClients:
delete(h.clients, s)
case s := <-h.signalChannel:
for _, c := range h.clients {
c.SignalUpdate(s)
}
}
}
}
-26
View File
@@ -1,26 +0,0 @@
package broker
import (
"github.com/jsiebens/ionscale/internal/bind"
"tailscale.com/tailcfg"
)
func NewClient(id uint64, channel chan *Signal) Client {
return Client{
id: id,
channel: channel,
}
}
type Client struct {
id uint64
binder bind.Binder
node *tailcfg.Node
compress string
channel chan *Signal
}
func (c *Client) SignalUpdate(s *Signal) {
c.channel <- s
}
+16
View File
@@ -0,0 +1,16 @@
package broker
type Signal struct {
PeerUpdated *uint64
PeersRemoved []uint64
ACLUpdated bool
DNSUpdated bool
}
type Listener chan *Signal
type Pubsub interface {
Subscribe(tailnet uint64, listener Listener) (cancel func(), err error)
Publish(tailnet uint64, message *Signal) error
Close() error
}
+61
View File
@@ -0,0 +1,61 @@
package broker
import (
"github.com/google/uuid"
"sync"
)
type memoryPubsub struct {
mut sync.RWMutex
listeners map[uint64]map[uuid.UUID]Listener
}
func (m *memoryPubsub) Subscribe(tailnet uint64, listener Listener) (cancel func(), err error) {
m.mut.Lock()
defer m.mut.Unlock()
var listeners map[uuid.UUID]Listener
var ok bool
if listeners, ok = m.listeners[tailnet]; !ok {
listeners = map[uuid.UUID]Listener{}
m.listeners[tailnet] = listeners
}
var id uuid.UUID
for {
id = uuid.New()
if _, ok = listeners[id]; !ok {
break
}
}
listeners[id] = listener
return func() {
m.mut.Lock()
defer m.mut.Unlock()
listeners := m.listeners[tailnet]
delete(listeners, id)
}, nil
}
func (m *memoryPubsub) Publish(tailnet uint64, message *Signal) error {
m.mut.RLock()
defer m.mut.RUnlock()
listeners, ok := m.listeners[tailnet]
if !ok {
return nil
}
for _, listener := range listeners {
listener <- message
}
return nil
}
func (*memoryPubsub) Close() error {
return nil
}
func NewPubsubInMemory() Pubsub {
return &memoryPubsub{
listeners: make(map[uint64]map[uuid.UUID]Listener),
}
}
+127
View File
@@ -0,0 +1,127 @@
package broker
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"github.com/lib/pq"
"github.com/pkg/errors"
"time"
)
type pgPubsub struct {
pgListener *pq.Listener
db *sql.DB
target Pubsub
}
// NewPubsub creates a new Pubsub implementation using a PostgreSQL connection.
func NewPubsub(ctx context.Context, database *sql.DB, connectURL string) (Pubsub, error) {
// Creates a new listener using pq.
errCh := make(chan error)
listener := pq.NewListener(connectURL, time.Second, time.Minute, func(event pq.ListenerEventType, err error) {
// This callback gets events whenever the connection state changes.
// Don't send if the errChannel has already been closed.
select {
case <-errCh:
return
default:
errCh <- err
close(errCh)
}
})
select {
case err := <-errCh:
if err != nil {
return nil, errors.Errorf("create pq listener: %w", err)
}
case <-ctx.Done():
return nil, ctx.Err()
}
if err := listener.Listen("ionscale_events"); err != nil {
return nil, errors.Errorf("listen: %w", err)
}
pubsub := &pgPubsub{
db: database,
pgListener: listener,
target: NewPubsubInMemory(),
}
go pubsub.listen(ctx)
return pubsub, nil
}
// Close closes the pubsub instance.
func (p *pgPubsub) Close() error {
return p.pgListener.Close()
}
func (p *pgPubsub) Subscribe(tailnet uint64, listener Listener) (cancel func(), err error) {
return p.target.Subscribe(tailnet, listener)
}
func (p *pgPubsub) Publish(tailnet uint64, message *Signal) error {
event := &pgEvent{
TailnetID: tailnet,
Signal: message,
}
payload, err := json.Marshal(event)
if err != nil {
return err
}
// This is safe because we are calling pq.QuoteLiteral. pg_notify doesn't
// support the first parameter being a prepared statement.
//nolint:gosec
_, err = p.db.ExecContext(context.Background(), `select pg_notify(`+pq.QuoteLiteral("ionscale_events")+`, $1)`, payload)
if err != nil {
fmt.Println(err)
return errors.Errorf("exec pg_notify: %w", err)
}
return nil
}
// listen begins receiving messages on the pq listener.
func (p *pgPubsub) listen(ctx context.Context) {
var (
notif *pq.Notification
ok bool
)
defer p.pgListener.Close()
for {
select {
case <-ctx.Done():
return
case notif, ok = <-p.pgListener.Notify:
if !ok {
return
}
}
// A nil notification can be dispatched on reconnect.
if notif == nil {
continue
}
p.listenReceive(notif)
}
}
func (p *pgPubsub) listenReceive(notif *pq.Notification) {
extra := []byte(notif.Extra)
event := &pgEvent{}
if err := json.Unmarshal(extra, event); err == nil {
p.target.Publish(event.TailnetID, event.Signal)
} else {
fmt.Println(err)
}
}
type pgEvent struct {
TailnetID uint64
Signal *Signal
}
+28 -42
View File
@@ -4,16 +4,15 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/bufbuild/connect-go"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/muesli/coral"
"gopkg.in/yaml.v2"
"io/ioutil"
)
func getACLConfig() *coral.Command {
func getACLConfigCommand() *coral.Command {
command := &coral.Command{
Use: "get-acl",
Use: "get-acl-policy",
Short: "Get the ACL policy",
SilenceUsage: true,
}
@@ -24,50 +23,33 @@ func getACLConfig() *coral.Command {
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
command.Flags().BoolVar(&asJson, "json", false, "")
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.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
command.RunE = func(cmd *coral.Command, args []string) error {
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
return err
}
resp, err := client.GetACLPolicy(context.Background(), &api.GetACLPolicyRequest{TailnetId: tailnet.Id})
resp, err := client.GetACLPolicy(context.Background(), connect.NewRequest(&api.GetACLPolicyRequest{TailnetId: tailnet.Id}))
if err != nil {
return err
}
var p domain.ACLPolicy
if err := json.Unmarshal(resp.Value, &p); err != nil {
marshal, err := json.MarshalIndent(resp.Msg.Policy, "", " ")
if err != nil {
return err
}
if asJson {
marshal, err := json.MarshalIndent(&p, "", " ")
if err != nil {
return err
}
fmt.Println()
fmt.Println(string(marshal))
} else {
marshal, err := yaml.Marshal(&p)
if err != nil {
return err
}
fmt.Println()
fmt.Println(string(marshal))
}
fmt.Println(string(marshal))
return nil
}
@@ -75,9 +57,9 @@ func getACLConfig() *coral.Command {
return command
}
func setACLConfig() *coral.Command {
func setACLConfigCommand() *coral.Command {
command := &coral.Command{
Use: "set-acl",
Use: "set-acl-policy",
Short: "Set ACL policy",
SilenceUsage: true,
}
@@ -88,33 +70,37 @@ func setACLConfig() *coral.Command {
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
command.Flags().StringVar(&file, "file", "", "")
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.RunE = func(command *coral.Command, args []string) error {
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
command.RunE = func(cmd *coral.Command, args []string) error {
rawJson, err := ioutil.ReadFile(file)
if err != nil {
return err
}
client, c, err := target.createGRPCClient()
var policy = &api.ACLPolicy{}
if err := json.Unmarshal(rawJson, policy); err != nil {
return err
}
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
return err
}
_, err = client.SetACLPolicy(context.Background(), &api.SetACLPolicyRequest{TailnetId: tailnet.Id, Value: rawJson})
_, err = client.SetACLPolicy(context.Background(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tailnet.Id, Policy: policy}))
if err != nil {
return err
}
fmt.Println()
fmt.Println("ACL policy updated successfully")
return nil
+80
View File
@@ -0,0 +1,80 @@
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"
"github.com/muesli/coral"
)
func authCommand() *coral.Command {
command := &coral.Command{
Use: "auth",
}
command.AddCommand(authLoginCommand())
return command
}
func authLoginCommand() *coral.Command {
command := &coral.Command{
Use: "login",
SilenceUsage: true,
}
var target = Target{}
target.prepareCommand(command)
command.RunE = func(command *coral.Command, args []string) error {
client, err := target.createGRPCClient()
if err != nil {
return err
}
req := &api.AuthenticationRequest{}
stream, err := client.Authenticate(context.Background(), connect.NewRequest(req))
if err != nil {
return err
}
var started = false
for stream.Receive() {
resp := stream.Msg()
if len(resp.Token) != 0 {
fmt.Println()
fmt.Println("Success.")
if err := ionscale.SessionToFile(resp.Token, resp.TailnetId); err != nil {
fmt.Println()
fmt.Println("Your api token:")
fmt.Println()
fmt.Printf(" %s\n", resp.Token)
fmt.Println()
}
return nil
}
if len(resp.AuthUrl) != 0 && !started {
started = true
fmt.Println()
fmt.Println("To authenticate, visit:")
fmt.Println()
fmt.Printf(" %s\n", resp.AuthUrl)
fmt.Println()
}
}
if err := stream.Err(); err != nil {
return err
}
return nil
}
return command
}
-180
View File
@@ -1,180 +0,0 @@
package cmd
import (
"context"
"fmt"
"github.com/hashicorp/go-bexpr"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/muesli/coral"
"github.com/rodaine/table"
)
func authFilterCommand() *coral.Command {
command := &coral.Command{
Use: "auth-filters",
Short: "Manage ionscale auth filters",
Long: `This command allows operations on ionscale auth filter resources. Example:
$ ionscale auth-filter create`,
}
command.AddCommand(createAuthFilterCommand())
command.AddCommand(listAuthFilterCommand())
command.AddCommand(deleteAuthFilterCommand())
return command
}
func listAuthFilterCommand() *coral.Command {
command := &coral.Command{
Use: "list",
SilenceUsage: true,
}
var authMethodID uint64
var target = Target{}
target.prepareCommand(command)
command.Flags().Uint64Var(&authMethodID, "auth-method-id", 0, "")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := &api.ListAuthFiltersRequest{}
if authMethodID != 0 {
req.AuthMethodId = &authMethodID
}
resp, err := client.ListAuthFilters(context.Background(), req)
if err != nil {
return err
}
tbl := table.New("ID", "AUTH_METHOD", "TAILNET", "EXPR")
for _, filter := range resp.AuthFilters {
if filter.Tailnet != nil {
tbl.AddRow(filter.Id, filter.AuthMethod.Name, filter.Tailnet.Name, filter.Expr)
} else {
tbl.AddRow(filter.Id, filter.AuthMethod.Name, "", filter.Expr)
}
}
tbl.Print()
return nil
}
return command
}
func createAuthFilterCommand() *coral.Command {
command := &coral.Command{
Use: "create",
SilenceUsage: true,
}
var expr string
var tailnetID uint64
var tailnetName string
var authMethodID uint64
var authMethodName string
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&expr, "expr", "*", "")
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
command.Flags().StringVar(&authMethodName, "auth-method", "", "")
command.Flags().Uint64Var(&authMethodID, "auth-method-id", 0, "")
command.RunE = func(command *coral.Command, args []string) error {
if expr != "*" {
if _, err := bexpr.CreateEvaluator(expr); err != nil {
return fmt.Errorf("invalid expression: %v", err)
}
}
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
return err
}
authMethod, err := findAuthMethod(client, authMethodName, authMethodID)
if err != nil {
return err
}
req := &api.CreateAuthFilterRequest{
AuthMethodId: authMethod.Id,
TailnetId: tailnet.Id,
Expr: expr,
}
resp, err := client.CreateAuthFilter(context.Background(), req)
if err != nil {
return err
}
tbl := table.New("ID", "AUTH_METHOD", "TAILNET", "EXPR")
if resp.AuthFilter.Tailnet != nil {
tbl.AddRow(resp.AuthFilter.Id, resp.AuthFilter.AuthMethod.Name, resp.AuthFilter.Tailnet.Name, resp.AuthFilter.Expr)
} else {
tbl.AddRow(resp.AuthFilter.Id, resp.AuthFilter.AuthMethod.Name, "", resp.AuthFilter.Expr)
}
tbl.Print()
return nil
}
return command
}
func deleteAuthFilterCommand() *coral.Command {
command := &coral.Command{
Use: "delete",
SilenceUsage: true,
}
var authFilterID uint64
var target = Target{}
target.prepareCommand(command)
command.Flags().Uint64Var(&authFilterID, "auth-filter-id", 0, "")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := &api.DeleteAuthFilterRequest{
AuthFilterId: authFilterID,
}
_, err = client.DeleteAuthFilter(context.Background(), req)
if err != nil {
return err
}
return nil
}
return command
}
+25 -21
View File
@@ -3,7 +3,8 @@ package cmd
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/bufbuild/connect-go"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/muesli/coral"
"github.com/rodaine/table"
str2dur "github.com/xhit/go-str2duration/v2"
@@ -14,7 +15,8 @@ import (
func authkeysCommand() *coral.Command {
command := &coral.Command{
Use: "auth-keys",
Use: "auth-keys",
Short: "Manage ionscale auth keys",
}
command.AddCommand(createAuthkeysCommand())
@@ -27,6 +29,7 @@ func authkeysCommand() *coral.Command {
func createAuthkeysCommand() *coral.Command {
command := &coral.Command{
Use: "create",
Short: "Creates a new auth key in the specified tailnet",
SilenceUsage: true,
}
@@ -38,18 +41,18 @@ func createAuthkeysCommand() *coral.Command {
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
command.Flags().BoolVar(&ephemeral, "ephemeral", false, "")
command.Flags().StringSliceVar(&tags, "tag", []string{}, "")
command.Flags().StringVar(&expiry, "expiry", "180d", "")
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.PreRunE = checkRequiredTailnetAndTailnetIdFlags
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
@@ -72,7 +75,7 @@ func createAuthkeysCommand() *coral.Command {
Tags: tags,
Expiry: expiryDur,
}
resp, err := client.CreateAuthKey(context.Background(), req)
resp, err := client.CreateAuthKey(context.Background(), connect.NewRequest(req))
if err != nil {
return err
@@ -82,7 +85,7 @@ func createAuthkeysCommand() *coral.Command {
fmt.Println("Generated new auth key")
fmt.Println("Be sure to copy your new key below. It won't be shown in full again.")
fmt.Println("")
fmt.Printf(" %s\n", resp.Value)
fmt.Printf(" %s\n", resp.Msg.Value)
fmt.Println("")
return nil
@@ -94,23 +97,23 @@ func createAuthkeysCommand() *coral.Command {
func deleteAuthKeyCommand() *coral.Command {
command := &coral.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, "")
command.Flags().Uint64Var(&authKeyId, "id", 0, "Auth Key ID")
command.RunE = func(command *coral.Command, args []string) error {
grpcClient, c, err := target.createGRPCClient()
grpcClient, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := api.DeleteAuthKeyRequest{AuthKeyId: authKeyId}
if _, err := grpcClient.DeleteAuthKey(context.Background(), &req); err != nil {
if _, err := grpcClient.DeleteAuthKey(context.Background(), connect.NewRequest(&req)); err != nil {
return err
}
@@ -125,6 +128,7 @@ func deleteAuthKeyCommand() *coral.Command {
func listAuthkeysCommand() *coral.Command {
command := &coral.Command{
Use: "list",
Short: "List all auth keys for a given tailnet",
SilenceUsage: true,
}
@@ -133,15 +137,15 @@ func listAuthkeysCommand() *coral.Command {
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
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 *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
@@ -149,13 +153,13 @@ func listAuthkeysCommand() *coral.Command {
}
req := &api.ListAuthKeysRequest{TailnetId: tailnet.Id}
resp, err := client.ListAuthKeys(context.Background(), req)
resp, err := client.ListAuthKeys(context.Background(), connect.NewRequest(req))
if err != nil {
return err
}
printAuthKeyTable(resp.AuthKeys...)
printAuthKeyTable(resp.Msg.AuthKeys...)
return nil
}
-127
View File
@@ -1,127 +0,0 @@
package cmd
import (
"context"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/muesli/coral"
"github.com/rodaine/table"
)
func authMethodsCommand() *coral.Command {
command := &coral.Command{
Use: "auth-methods",
Short: "Manage ionscale auth methods",
}
command.AddCommand(listAuthMethods())
command.AddCommand(createAuthMethodCommand())
return command
}
func listAuthMethods() *coral.Command {
command := &coral.Command{
Use: "list",
Short: "List auth methods",
Long: `List auth methods in this ionscale instance. Example:
$ ionscale auth-methods list`,
}
var target = Target{}
target.prepareCommand(command)
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
resp, err := client.ListAuthMethods(context.Background(), &api.ListAuthMethodsRequest{})
if err != nil {
return err
}
tbl := table.New("ID", "NAME", "TYPE")
for _, m := range resp.AuthMethods {
tbl.AddRow(m.Id, m.Name, m.Type)
}
tbl.Print()
return nil
}
return command
}
func createAuthMethodCommand() *coral.Command {
command := &coral.Command{
Use: "create",
Short: "Create a new auth method",
}
command.AddCommand(createOIDCAuthMethodCommand())
return command
}
func createOIDCAuthMethodCommand() *coral.Command {
command := &coral.Command{
Use: "oidc",
Short: "Create a new auth method",
SilenceUsage: true,
}
var methodName string
var clientId string
var clientSecret string
var issuer string
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVarP(&methodName, "name", "n", "", "")
command.Flags().StringVar(&clientId, "client-id", "", "")
command.Flags().StringVar(&clientSecret, "client-secret", "", "")
command.Flags().StringVar(&issuer, "issuer", "", "")
_ = command.MarkFlagRequired("name")
_ = command.MarkFlagRequired("client-id")
_ = command.MarkFlagRequired("client-secret")
_ = command.MarkFlagRequired("issuer")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := &api.CreateAuthMethodRequest{
Type: "oidc",
Name: methodName,
Issuer: issuer,
ClientId: clientId,
ClientSecret: clientSecret,
}
resp, err := client.CreateAuthMethod(context.Background(), req)
if err != nil {
return err
}
tbl := table.New("ID", "NAME", "TYPE")
tbl.AddRow(resp.AuthMethod.Id, resp.AuthMethod.Name, resp.AuthMethod.Type)
tbl.Print()
return nil
}
return command
}
+92
View File
@@ -0,0 +1,92 @@
package cmd
import (
"errors"
"fmt"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/key"
"github.com/muesli/coral"
"gopkg.in/yaml.v2"
"path/filepath"
)
func configureCommand() *coral.Command {
command := &coral.Command{
Use: "configure",
Short: "Generate a simple config file to get started.",
SilenceUsage: true,
}
var domain string
var acme bool
var email string
var dataDir string
var certFile string
var keyFile string
command.Flags().StringVar(&domain, "domain", "", "Public domain name of your ionscale instance.")
command.Flags().StringVar(&dataDir, "data-dir", "/var/lib/ionscale", "")
command.Flags().BoolVar(&acme, "acme", false, "Get automatic certificate from Letsencrypt.org using ACME.")
command.Flags().StringVar(&email, "acme-email", "", "Email to receive updates from Letsencrypt.org.")
command.Flags().StringVar(&certFile, "cert-file", "", "Path to a TLS certificate file.")
command.Flags().StringVar(&keyFile, "key-file", "", "Path to a TLS key file.")
command.MarkFlagRequired("domain")
command.PreRunE = func(cmd *coral.Command, args []string) error {
if domain == "" {
return errors.New("required flag 'domain' is missing")
}
if acme && email == "" {
return errors.New("flag 'acme-email' is required when acme is enabled")
}
if !acme && (certFile == "" || keyFile == "") {
return errors.New("flags 'cert-file' and 'key-file' are required when acme is disabled")
}
return nil
}
command.RunE = func(command *coral.Command, args []string) error {
c := &config.Config{}
c.HttpListenAddr = "0.0.0.0:80"
c.HttpsListenAddr = "0.0.0.0:443"
c.MetricsListenAddr = "127.0.0.1:9090"
c.ServerUrl = fmt.Sprintf("https://%s", domain)
c.Keys = config.Keys{
ControlKey: key.NewServerKey().String(),
LegacyControlKey: key.NewServerKey().String(),
SystemAdminKey: key.NewServerKey().String(),
}
c.Tls = config.Tls{}
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
}
c.Database = config.Database{
Type: "sqlite",
Url: filepath.Join(dataDir, "ionscale.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(ON)"),
}
configAsYaml, err := yaml.Marshal(c)
if err != nil {
return err
}
fmt.Println(string(configAsYaml))
return nil
}
return command
}
+10 -11
View File
@@ -4,7 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/bufbuild/connect-go"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/muesli/coral"
"gopkg.in/yaml.v2"
"io/ioutil"
@@ -34,16 +35,15 @@ func getDERPMap() *coral.Command {
var target = Target{}
target.prepareCommand(command)
command.Flags().BoolVar(&asJson, "json", false, "")
command.Flags().BoolVar(&asJson, "json", false, "When enabled, render output as json otherwise yaml")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
resp, err := client.GetDERPMap(context.Background(), &api.GetDERPMapRequest{})
resp, err := client.GetDERPMap(context.Background(), connect.NewRequest(&api.GetDERPMapRequest{}))
if err != nil {
return err
@@ -53,7 +53,7 @@ func getDERPMap() *coral.Command {
Regions map[int]*tailcfg.DERPRegion
}
if err := json.Unmarshal(resp.Value, &derpMap); err != nil {
if err := json.Unmarshal(resp.Msg.Value, &derpMap); err != nil {
return err
}
@@ -91,27 +91,26 @@ func setDERPMap() *coral.Command {
var file string
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&file, "file", "", "")
command.Flags().StringVar(&file, "file", "", "Path to json file with the DERP Map configuration")
command.RunE = func(command *coral.Command, args []string) error {
grpcClient, c, err := target.createGRPCClient()
grpcClient, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
rawJson, err := ioutil.ReadFile(file)
if err != nil {
return err
}
resp, err := grpcClient.SetDERPMap(context.Background(), &api.SetDERPMapRequest{Value: rawJson})
resp, err := grpcClient.SetDERPMap(context.Background(), connect.NewRequest(&api.SetDERPMapRequest{Value: rawJson}))
if err != nil {
return err
}
var derpMap tailcfg.DERPMap
if err := json.Unmarshal(resp.Value, &derpMap); err != nil {
if err := json.Unmarshal(resp.Msg.Value, &derpMap); err != nil {
return err
}
+49 -22
View File
@@ -3,12 +3,15 @@ package cmd
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/bufbuild/connect-go"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/muesli/coral"
"os"
"strings"
"text/tabwriter"
)
func getDNSConfig() *coral.Command {
func getDNSConfigCommand() *coral.Command {
command := &coral.Command{
Use: "get-dns",
Short: "Get DNS configuration",
@@ -20,15 +23,15 @@ func getDNSConfig() *coral.Command {
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
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 *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
@@ -36,12 +39,12 @@ func getDNSConfig() *coral.Command {
}
req := api.GetDNSConfigRequest{TailnetId: tailnet.Id}
resp, err := client.GetDNSConfig(context.Background(), &req)
resp, err := client.GetDNSConfig(context.Background(), connect.NewRequest(&req))
if err != nil {
return err
}
config := resp.Config
config := resp.Msg.Config
var allNameservers = config.Nameservers
@@ -51,9 +54,33 @@ func getDNSConfig() *coral.Command {
}
}
fmt.Printf("%-*v%v\n", 25, "Magic DNS Enabled:", config.MagicDns)
fmt.Printf("%-*v%v\n", 25, "Override Local DNS:", config.OverrideLocalDns)
fmt.Printf("%-*v%v\n", 25, "Nameservers:", strings.Join(allNameservers, ","))
w := new(tabwriter.Writer)
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
defer w.Flush()
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)
}
}
return nil
}
@@ -61,7 +88,7 @@ func getDNSConfig() *coral.Command {
return command
}
func setDNSConfig() *coral.Command {
func setDNSConfigCommand() *coral.Command {
command := &coral.Command{
Use: "set-dns",
Short: "Set DNS config",
@@ -76,18 +103,18 @@ func setDNSConfig() *coral.Command {
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
command.Flags().StringSliceVarP(&nameservers, "nameserver", "", []string{}, "")
command.Flags().BoolVarP(&magicDNS, "magic-dns", "", false, "")
command.Flags().BoolVarP(&overrideLocalDNS, "override-local-dns", "", false, "")
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(&overrideLocalDNS, "override-local-dns", "", false, "When enabled, connected clients ignore local DNS settings and always use the nameservers specified for this Tailnet")
command.PreRunE = checkRequiredTailnetAndTailnetIdFlags
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
@@ -116,17 +143,17 @@ func setDNSConfig() *coral.Command {
Config: &api.DNSConfig{
MagicDns: magicDNS,
OverrideLocalDns: overrideLocalDNS,
Nameservers: nameservers,
Nameservers: globalNameservers,
Routes: routes,
},
}
resp, err := client.SetDNSConfig(context.Background(), &req)
resp, err := client.SetDNSConfig(context.Background(), connect.NewRequest(&req))
if err != nil {
return err
}
config := resp.Config
config := resp.Msg.Config
var allNameservers = config.Nameservers
+33 -33
View File
@@ -3,50 +3,50 @@ package cmd
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"io"
"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/muesli/coral"
)
func findTailnet(client api.IonscaleClient, tailnet string, tailnetID uint64) (*api.Tailnet, error) {
if tailnetID == 0 && tailnet == "" {
return nil, fmt.Errorf("requested tailnet not found or you are not authorized for this tailnet")
func checkRequiredTailnetAndTailnetIdFlags(cmd *coral.Command, args []string) error {
savedTailnetID, err := ionscale.TailnetFromFile()
if err != nil {
return err
}
tailnets, err := client.ListTailnets(context.Background(), &api.ListTailnetRequest{})
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
}
for _, t := range tailnets.Tailnet {
if t.Id == tailnetID || t.Name == tailnet {
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.ListTailnetRequest{}))
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")
}
func findAuthMethod(client api.IonscaleClient, authMethod string, authMethodID uint64) (*api.AuthMethod, error) {
if authMethodID == 0 && authMethod == "" {
return nil, fmt.Errorf("requested auth method not found or you are not authorized for this tailnet")
}
resp, err := client.ListAuthMethods(context.Background(), &api.ListAuthMethodsRequest{})
if err != nil {
return nil, err
}
for _, t := range resp.AuthMethods {
if t.Id == authMethodID || t.Name == authMethod {
return t, nil
}
}
return nil, fmt.Errorf("requested auth method not found or you are not authorized for this tailnet")
}
func safeClose(c io.Closer) {
if c != nil {
_ = c.Close()
}
}
+108
View File
@@ -0,0 +1,108 @@
package cmd
import (
"context"
"encoding/json"
"fmt"
"github.com/bufbuild/connect-go"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/muesli/coral"
"io/ioutil"
)
func getIAMPolicyCommand() *coral.Command {
command := &coral.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 *coral.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}))
if err != nil {
return err
}
marshal, err := json.MarshalIndent(resp.Msg.Policy, "", " ")
if err != nil {
return err
}
fmt.Println(string(marshal))
return nil
}
return command
}
func setIAMPolicyCommand() *coral.Command {
command := &coral.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 *coral.Command, args []string) error {
rawJson, err := ioutil.ReadFile(file)
if err != nil {
return err
}
var policy = &api.IAMPolicy{}
if err := json.Unmarshal(rawJson, policy); err != nil {
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}))
if err != nil {
return err
}
fmt.Println("IAM policy updated successfully")
return nil
}
return command
}
+1 -5
View File
@@ -14,11 +14,7 @@ func keyCommand() *coral.Command {
command.RunE = func(command *coral.Command, args []string) error {
serverKey := key.NewServerKey()
fmt.Println()
fmt.Printf(" %s\n", serverKey.String())
fmt.Println()
fmt.Println(serverKey.String())
return nil
}
+286 -44
View File
@@ -3,12 +3,15 @@ package cmd
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/bufbuild/connect-go"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/muesli/coral"
"github.com/nleeper/goment"
"github.com/rodaine/table"
"inet.af/netaddr"
"os"
"strings"
"text/tabwriter"
)
func machineCommands() *coral.Command {
@@ -18,11 +21,113 @@ func machineCommands() *coral.Command {
SilenceUsage: true,
}
command.AddCommand(getMachineCommand())
command.AddCommand(deleteMachineCommand())
command.AddCommand(expireMachineCommand())
command.AddCommand(listMachinesCommand())
command.AddCommand(getMachineRoutesCommand())
command.AddCommand(setMachineRoutesCommand())
command.AddCommand(enableMachineRoutesCommand())
command.AddCommand(disableMachineRoutesCommand())
command.AddCommand(enableMachineKeyExpiryCommand())
command.AddCommand(disableMachineKeyExpiryCommand())
return command
}
func getMachineCommand() *coral.Command {
command := &coral.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 *coral.Command, args []string) error {
client, err := target.createGRPCClient()
if err != nil {
return err
}
req := api.GetMachineRequest{MachineId: machineID}
resp, err := client.GetMachine(context.Background(), connect.NewRequest(&req))
if err != nil {
return err
}
m := resp.Msg.Machine
var lastSeen = "N/A"
var expiresAt = "No expiry"
if m.LastSeen != nil && !m.LastSeen.AsTime().IsZero() {
if mom, err := goment.New(m.LastSeen.AsTime()); err == nil {
lastSeen = mom.FromNow()
}
}
if !m.KeyExpiryDisabled && m.ExpiresAt != nil && !m.ExpiresAt.AsTime().IsZero() {
if mom, err := goment.New(m.ExpiresAt.AsTime()); !m.ExpiresAt.AsTime().IsZero() && err == nil {
expiresAt = mom.FromNow()
}
}
// initialize tabwriter
w := new(tabwriter.Writer)
// minwidth, tabwidth, padding, padchar, flags
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
defer w.Flush()
fmt.Fprintf(w, "%s\t%d\n", "ID", m.Id)
fmt.Fprintf(w, "%s\t%s\n", "Machine name", m.Name)
fmt.Fprintf(w, "%s\t%s\n", "Creator", m.User.Name)
fmt.Fprintf(w, "%s\t%s\n", "OS", m.Os)
fmt.Fprintf(w, "%s\t%s\n", "Tailscale version", m.ClientVersion)
fmt.Fprintf(w, "%s\t%s\n", "Tailscale IPv4", m.Ipv4)
fmt.Fprintf(w, "%s\t%s\n", "Tailscale IPv6", m.Ipv6)
fmt.Fprintf(w, "%s\t%s\n", "Last seen", lastSeen)
fmt.Fprintf(w, "%s\t%s\n", "Key expiry", expiresAt)
for i, t := range m.Tags {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "ACL tags", t)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", t)
}
}
for i, e := range m.ClientConnectivity.Endpoints {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "Endpoints", e)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", e)
}
}
for i, t := range m.AdvertisedRoutes {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "Advertised routes", t)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", t)
}
}
for i, t := range m.EnabledRoutes {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "Enabled routes", t)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", t)
}
}
return nil
}
return command
}
@@ -37,17 +142,18 @@ func deleteMachineCommand() *coral.Command {
var machineID uint64
var target = Target{}
target.prepareCommand(command)
command.Flags().Uint64Var(&machineID, "machine-id", 0, "")
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.")
_ = command.MarkFlagRequired("machine-id")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := api.DeleteMachineRequest{MachineId: machineID}
if _, err := client.DeleteMachine(context.Background(), &req); err != nil {
if _, err := client.DeleteMachine(context.Background(), connect.NewRequest(&req)); err != nil {
return err
}
@@ -69,17 +175,18 @@ func expireMachineCommand() *coral.Command {
var machineID uint64
var target = Target{}
target.prepareCommand(command)
command.Flags().Uint64Var(&machineID, "machine-id", 0, "")
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.")
_ = command.MarkFlagRequired("machine-id")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := api.ExpireMachineRequest{MachineId: machineID}
if _, err := client.ExpireMachine(context.Background(), &req); err != nil {
if _, err := client.ExpireMachine(context.Background(), connect.NewRequest(&req)); err != nil {
return err
}
@@ -103,15 +210,15 @@ func listMachinesCommand() *coral.Command {
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
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 *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
@@ -119,14 +226,14 @@ func listMachinesCommand() *coral.Command {
}
req := api.ListMachinesRequest{TailnetId: tailnet.Id}
resp, err := client.ListMachines(context.Background(), &req)
resp, err := client.ListMachines(context.Background(), connect.NewRequest(&req))
if err != nil {
return err
}
tbl := table.New("ID", "TAILNET", "NAME", "IPv4", "IPv6", "EPHEMERAL", "LAST_SEEN", "TAGS")
for _, m := range resp.Machines {
for _, m := range resp.Msg.Machines {
var lastSeen = "N/A"
if m.Connected {
lastSeen = "Connected"
@@ -149,33 +256,48 @@ func listMachinesCommand() *coral.Command {
func getMachineRoutesCommand() *coral.Command {
command := &coral.Command{
Use: "get-routes",
Short: "Show the routes of a machine",
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, "")
command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.")
_ = command.MarkFlagRequired("machine-id")
command.RunE = func(command *coral.Command, args []string) error {
grpcClient, c, err := target.createGRPCClient()
grpcClient, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := api.GetMachineRoutesRequest{MachineId: machineID}
resp, err := grpcClient.GetMachineRoutes(context.Background(), &req)
resp, err := grpcClient.GetMachineRoutes(context.Background(), connect.NewRequest(&req))
if err != nil {
return err
}
tbl := table.New("ROUTE", "ALLOWED")
for _, r := range resp.Routes {
tbl.AddRow(r.Advertised, r.Allowed)
w := new(tabwriter.Writer)
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
defer w.Flush()
for i, t := range resp.Msg.AdvertisedRoutes {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "Advertised routes", t)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", t)
}
}
for i, t := range resp.Msg.EnabledRoutes {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "Enabled routes", t)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", t)
}
}
tbl.Print()
return nil
}
@@ -183,47 +305,167 @@ func getMachineRoutesCommand() *coral.Command {
return command
}
func setMachineRoutesCommand() *coral.Command {
func enableMachineRoutesCommand() *coral.Command {
command := &coral.Command{
Use: "set-routes",
Short: "Enable routes of a machine",
Use: "enable-routes",
Short: "Enable routes for a given machine",
SilenceUsage: true,
}
var machineID uint64
var allowedIps []string
var routes []string
var replace bool
var target = Target{}
target.prepareCommand(command)
command.Flags().Uint64Var(&machineID, "machine-id", 0, "")
command.Flags().StringSliceVar(&allowedIps, "allowed-ips", []string{}, "")
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 *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
var prefixes []netaddr.IPPrefix
for _, r := range allowedIps {
p, err := netaddr.ParseIPPrefix(r)
if err != nil {
for _, r := range routes {
if _, err := netaddr.ParseIPPrefix(r); err != nil {
return err
}
prefixes = append(prefixes, p)
}
req := api.SetMachineRoutesRequest{MachineId: machineID, AllowedIps: allowedIps}
resp, err := client.SetMachineRoutes(context.Background(), &req)
req := api.EnableMachineRoutesRequest{MachineId: machineID, Routes: routes, Replace: replace}
resp, err := client.EnableMachineRoutes(context.Background(), connect.NewRequest(&req))
if err != nil {
return err
}
tbl := table.New("ROUTE", "ALLOWED")
for _, r := range resp.Routes {
tbl.AddRow(r.Advertised, r.Allowed)
w := new(tabwriter.Writer)
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
defer w.Flush()
for i, t := range resp.Msg.AdvertisedRoutes {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "Advertised routes", t)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", t)
}
}
for i, t := range resp.Msg.EnabledRoutes {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "Enabled routes", t)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", t)
}
}
return nil
}
return command
}
func disableMachineRoutesCommand() *coral.Command {
command := &coral.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 *coral.Command, args []string) error {
client, err := target.createGRPCClient()
if err != nil {
return err
}
for _, r := range routes {
if _, err := netaddr.ParseIPPrefix(r); err != nil {
return err
}
}
req := api.DisableMachineRoutesRequest{MachineId: machineID, Routes: routes}
resp, err := client.DisableMachineRoutes(context.Background(), connect.NewRequest(&req))
if err != nil {
return err
}
w := new(tabwriter.Writer)
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
defer w.Flush()
for i, t := range resp.Msg.AdvertisedRoutes {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "Advertised routes", t)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", t)
}
}
for i, t := range resp.Msg.EnabledRoutes {
if i == 0 {
fmt.Fprintf(w, "%s\t%s\n", "Enabled routes", t)
} else {
fmt.Fprintf(w, "%s\t%s\n", "", t)
}
}
return nil
}
return command
}
func enableMachineKeyExpiryCommand() *coral.Command {
command := &coral.Command{
Use: "enable-key-expiry",
Short: "Enable machine key expiry",
SilenceUsage: true,
}
return configureSetMachineKeyExpiryCommand(command, false)
}
func disableMachineKeyExpiryCommand() *coral.Command {
command := &coral.Command{
Use: "disable-key-expiry",
Short: "Disable machine key expiry",
SilenceUsage: true,
}
return configureSetMachineKeyExpiryCommand(command, true)
}
func configureSetMachineKeyExpiryCommand(command *coral.Command, v bool) *coral.Command {
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 *coral.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))
if err != nil {
return err
}
tbl.Print()
return nil
}
+3 -2
View File
@@ -6,15 +6,16 @@ import (
func Command() *coral.Command {
rootCmd := rootCommand()
rootCmd.AddCommand(configureCommand())
rootCmd.AddCommand(keyCommand())
rootCmd.AddCommand(authCommand())
rootCmd.AddCommand(derpMapCommand())
rootCmd.AddCommand(serverCommand())
rootCmd.AddCommand(versionCommand())
rootCmd.AddCommand(authMethodsCommand())
rootCmd.AddCommand(authFilterCommand())
rootCmd.AddCommand(tailnetCommand())
rootCmd.AddCommand(authkeysCommand())
rootCmd.AddCommand(machineCommands())
rootCmd.AddCommand(userCommands())
return rootCmd
}
+22 -23
View File
@@ -3,7 +3,8 @@ package cmd
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/bufbuild/connect-go"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/muesli/coral"
"github.com/rodaine/table"
)
@@ -12,16 +13,17 @@ func tailnetCommand() *coral.Command {
command := &coral.Command{
Use: "tailnets",
Short: "Manage ionscale tailnets",
Long: "This command allows operations on ionscale tailnet resources.",
}
command.AddCommand(listTailnetsCommand())
command.AddCommand(createTailnetsCommand())
command.AddCommand(deleteTailnetCommand())
command.AddCommand(getDNSConfig())
command.AddCommand(setDNSConfig())
command.AddCommand(getACLConfig())
command.AddCommand(setACLConfig())
command.AddCommand(getDNSConfigCommand())
command.AddCommand(setDNSConfigCommand())
command.AddCommand(getACLConfigCommand())
command.AddCommand(setACLConfigCommand())
command.AddCommand(getIAMPolicyCommand())
command.AddCommand(setIAMPolicyCommand())
return command
}
@@ -29,8 +31,7 @@ func tailnetCommand() *coral.Command {
func listTailnetsCommand() *coral.Command {
command := &coral.Command{
Use: "list",
Short: "List tailnets",
Long: `List tailnets in this ionscale instance.`,
Short: "List available Tailnets",
SilenceUsage: true,
}
@@ -39,20 +40,19 @@ func listTailnetsCommand() *coral.Command {
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
resp, err := client.ListTailnets(context.Background(), &api.ListTailnetRequest{})
resp, err := client.ListTailnets(context.Background(), connect.NewRequest(&api.ListTailnetRequest{}))
if err != nil {
return err
}
tbl := table.New("ID", "NAME")
for _, tailnet := range resp.Tailnet {
for _, tailnet := range resp.Msg.Tailnet {
tbl.AddRow(tailnet.Id, tailnet.Name)
}
tbl.Print()
@@ -66,7 +66,7 @@ func listTailnetsCommand() *coral.Command {
func createTailnetsCommand() *coral.Command {
command := &coral.Command{
Use: "create",
Short: "Create a new tailnet",
Short: "Create a new Tailnet",
SilenceUsage: true,
}
@@ -79,20 +79,19 @@ func createTailnetsCommand() *coral.Command {
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
resp, err := client.CreateTailnet(context.Background(), &api.CreateTailnetRequest{Name: name})
resp, err := client.CreateTailnet(context.Background(), connect.NewRequest(&api.CreateTailnetRequest{Name: name}))
if err != nil {
return err
}
tbl := table.New("ID", "NAME")
tbl.AddRow(resp.Tailnet.Id, resp.Tailnet.Name)
tbl.AddRow(resp.Msg.Tailnet.Id, resp.Msg.Tailnet.Name)
tbl.Print()
return nil
@@ -114,24 +113,24 @@ func deleteTailnetCommand() *coral.Command {
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
command.Flags().BoolVar(&force, "force", false, "")
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 *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
return err
}
_, err = client.DeleteTailnet(context.Background(), &api.DeleteTailnetRequest{TailnetId: tailnet.Id, Force: force})
_, err = client.DeleteTailnet(context.Background(), connect.NewRequest(&api.DeleteTailnetRequest{TailnetId: tailnet.Id, Force: force}))
if err != nil {
return err
+7 -19
View File
@@ -3,21 +3,18 @@ package cmd
import (
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/pkg/client/ionscale"
"github.com/jsiebens/ionscale/pkg/gen/api"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1/ionscalev1connect"
"github.com/muesli/coral"
"io"
)
const (
ionscaleSystemAdminKey = "IONSCALE_ADMIN_KEY"
ionscaleSystemAdminKey = "IONSCALE_SYSTEM_ADMIN_KEY"
ionscaleAddr = "IONSCALE_ADDR"
ionscaleInsecureSkipVerify = "IONSCALE_SKIP_VERIFY"
ionscaleUseGrpcWeb = "IONSCALE_GRPC_WEB"
)
type Target struct {
addr string
useGrpcWeb bool
insecureSkipVerify bool
systemAdminKey string
}
@@ -25,29 +22,27 @@ type Target struct {
func (t *Target) prepareCommand(cmd *coral.Command) {
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().BoolVar(&t.useGrpcWeb, "grpc-web", false, "Enables gRPC-web protocol. Useful if ionscale server is behind proxy which does not support GRPC")
cmd.Flags().StringVar(&t.systemAdminKey, "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.")
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.IonscaleClient, io.Closer, error) {
func (t *Target) createGRPCClient() (api.IonscaleServiceClient, error) {
addr := t.getAddr()
useGrpcWeb := t.getUseGrpcWeb()
skipVerify := t.getInsecureSkipVerify()
systemAdminKey := t.getSystemAdminKey()
auth, err := ionscale.LoadClientAuth(systemAdminKey)
if err != nil {
return nil, nil, err
return nil, err
}
return ionscale.NewClient(auth, addr, skipVerify, useGrpcWeb)
return ionscale.NewClient(auth, addr, skipVerify)
}
func (t *Target) getAddr() string {
if len(t.addr) != 0 {
return t.addr
}
return config.GetString(ionscaleAddr, "https://localhost:8000")
return config.GetString(ionscaleAddr, "https://localhost:8443")
}
func (t *Target) getInsecureSkipVerify() bool {
@@ -57,13 +52,6 @@ func (t *Target) getInsecureSkipVerify() bool {
return config.GetBool(ionscaleInsecureSkipVerify, false)
}
func (t *Target) getUseGrpcWeb() bool {
if t.useGrpcWeb {
return true
}
return config.GetBool(ionscaleUseGrpcWeb, false)
}
func (t *Target) getSystemAdminKey() string {
if len(t.systemAdminKey) != 0 {
return t.systemAdminKey
+102
View File
@@ -0,0 +1,102 @@
package cmd
import (
"context"
"fmt"
"github.com/bufbuild/connect-go"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/muesli/coral"
"github.com/rodaine/table"
)
func userCommands() *coral.Command {
command := &coral.Command{
Use: "users",
Short: "Manage ionscale users",
SilenceUsage: true,
}
command.AddCommand(listUsersCommand())
command.AddCommand(deleteUserCommand())
return command
}
func listUsersCommand() *coral.Command {
command := &coral.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 *coral.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))
if err != nil {
return err
}
tbl := table.New("ID", "USER", "ROLE")
for _, m := range resp.Msg.Users {
tbl.AddRow(m.Id, m.Name, m.Role)
}
tbl.Print()
return nil
}
return command
}
func deleteUserCommand() *coral.Command {
command := &coral.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 *coral.Command, args []string) error {
client, err := target.createGRPCClient()
if err != nil {
return err
}
req := api.DeleteUserRequest{UserId: userID}
if _, err := client.DeleteUser(context.Background(), connect.NewRequest(&req)); err != nil {
return err
}
fmt.Println("User deleted.")
return nil
}
return command
}
+5 -5
View File
@@ -3,8 +3,9 @@ package cmd
import (
"context"
"fmt"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/version"
"github.com/jsiebens/ionscale/pkg/gen/api"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/muesli/coral"
)
@@ -26,7 +27,7 @@ Client:
Git Revision: %s
`, clientVersion, clientRevision)
client, c, err := target.createGRPCClient()
client, err := target.createGRPCClient()
if err != nil {
fmt.Printf(`
Server:
@@ -34,9 +35,8 @@ Server:
`, err)
return
}
defer safeClose(c)
resp, err := client.GetVersion(context.Background(), &api.GetVersionRequest{})
resp, err := client.GetVersion(context.Background(), connect.NewRequest(&api.GetVersionRequest{}))
if err != nil {
fmt.Printf(`
Server:
@@ -50,7 +50,7 @@ Server:
Addr: %s
Version: %s
Git Revision: %s
`, target.getAddr(), resp.Version, resp.Revision)
`, target.getAddr(), resp.Msg.Version, resp.Msg.Revision)
}
+142 -57
View File
@@ -4,21 +4,39 @@ import (
"fmt"
"github.com/caddyserver/certmagic"
"github.com/jsiebens/ionscale/internal/key"
"github.com/jsiebens/ionscale/internal/util"
"github.com/mitchellh/go-homedir"
"gopkg.in/yaml.v3"
"io/ioutil"
"os"
"path/filepath"
"strings"
tkey "tailscale.com/types/key"
"time"
)
var (
KeepAliveInterval = 1 * time.Minute
)
func LoadConfig(path string) (*Config, error) {
config := defaultConfig()
config, err := defaultConfig()
if err != nil {
return nil, err
}
if len(path) != 0 {
expandedPath, err := homedir.Expand(path)
if err != nil {
return nil, err
}
b, err := ioutil.ReadFile(expandedPath)
absPath, err := filepath.Abs(expandedPath)
if err != nil {
return nil, err
}
b, err := os.ReadFile(absPath)
if err != nil {
return nil, err
}
@@ -28,50 +46,75 @@ func LoadConfig(path string) (*Config, error) {
}
}
KeepAliveInterval = config.PollNet.KeepAliveInterval
return config, nil
}
const (
httpListenAddrKey = "IONSCALE_HTTP_LISTEN_ADDR"
httpsListenAddrKey = "IONSCALE_HTTPS_LISTEN_ADDR"
serverUrlKey = "IONSCALE_SERVER_URL"
keysSystemAdminKeyKey = "IONSCALE_SYSTEM_ADMIN_KEY"
keysEncryptionKeyKey = "IONSCALE_ENCRYPTION_KEY"
databaseUrlKey = "IONSCALE_DB_URL"
tlsDisableKey = "IONSCALE_TLS_DISABLE"
tlsCertFileKey = "IONSCALE_TLS_CERT_FILE"
tlsKeyFileKey = "IONSCALE_TLS_KEY_FILE"
tlsCertMagicCAKey = "IONSCALE_TLS_CERT_MAGIC_CA"
tlsCertMagicDomainKey = "IONSCALE_TLS_CERT_MAGIC_DOMAIN"
tlsCertMagicEmailKey = "IONSCALE_TLS_CERT_MAGIC_EMAIL"
tlsCertMagicStoragePath = "IONSCALE_TLS_CERT_MAGIC_STORAGE_PATH"
metricsListenAddrKey = "IONSCALE_METRICS_LISTEN_ADDR"
loggingLevelKey = "IONSCALE_LOGGING_LEVEL"
loggingFormatKey = "IONSCALE_LOGGING_FORMAT"
loggingFileKey = "IONSCALE_LOGGING_FILE"
httpListenAddrKey = "IONSCALE_HTTP_LISTEN_ADDR"
httpsListenAddrKey = "IONSCALE_HTTPS_LISTEN_ADDR"
serverUrlKey = "IONSCALE_SERVER_URL"
keysSystemAdminKeyKey = "IONSCALE_SYSTEM_ADMIN_KEY"
keysControlKeyKey = "IONSCALE_CONTROL_KEY"
keysLegacyControlKeyKey = "IONSCALE_LEGACY_CONTROL_KEY"
databaseUrlKey = "IONSCALE_DB_URL"
tlsDisableKey = "IONSCALE_TLS_DISABLE"
tlsForceHttpsKey = "IONSCALE_TLS_FORCE_HTTPS"
tlsCertFileKey = "IONSCALE_TLS_CERT_FILE"
tlsKeyFileKey = "IONSCALE_TLS_KEY_FILE"
tlsAcmeKey = "IONSCALE_TLS_ACME"
tlsAcmeCAKey = "IONSCALE_TLS_ACME_CA"
tlsAcmeEmailKey = "IONSCALE_TLS_ACME_EMAIL"
tlsAcmePath = "IONSCALE_TLS_ACME_PATH"
pollNetKeepAliveInterval = "IONSCALE_POLL_NET_KEEP_ALIVE_INTERVAL"
metricsListenAddrKey = "IONSCALE_METRICS_LISTEN_ADDR"
loggingLevelKey = "IONSCALE_LOGGING_LEVEL"
loggingFormatKey = "IONSCALE_LOGGING_FORMAT"
loggingFileKey = "IONSCALE_LOGGING_FILE"
authProviderIssuerKey = "IONSCALE_AUTH_PROVIDER_ISSUER"
authProviderClientIdKey = "IONSCALE_AUTH_PROVIDER_CLIENT_ID"
authProviderClientSecretKey = "IONSCALE_AUTH_PROVIDER_CLIENT_SECRET"
authProviderScopesKey = "IONSCALE_AUTH_PROVIDER_SCOPES"
)
func defaultConfig() *Config {
return &Config{
func defaultConfig() (*Config, error) {
defaultKeepAliveInterval, err := GetDuration(pollNetKeepAliveInterval, 1*time.Minute)
if err != nil {
return nil, err
}
c := &Config{
HttpListenAddr: GetString(httpListenAddrKey, ":8080"),
HttpsListenAddr: GetString(httpsListenAddrKey, ":8443"),
MetricsListenAddr: GetString(metricsListenAddrKey, ":8081"),
MetricsListenAddr: GetString(metricsListenAddrKey, ":9091"),
ServerUrl: GetString(serverUrlKey, "https://localhost:8443"),
Keys: Keys{
SystemAdminKey: GetString(keysSystemAdminKeyKey, ""),
EncryptionKey: GetString(keysEncryptionKeyKey, ""),
SystemAdminKey: GetString(keysSystemAdminKeyKey, ""),
ControlKey: GetString(keysControlKeyKey, ""),
LegacyControlKey: GetString(keysLegacyControlKeyKey, ""),
},
Database: Database{
Url: GetString(databaseUrlKey, "ionscale.db"),
},
Tls: Tls{
Disable: GetBool(tlsDisableKey, false),
CertFile: GetString(tlsCertFileKey, ""),
KeyFile: GetString(tlsKeyFileKey, ""),
CertMagicCA: GetString(tlsCertMagicCAKey, certmagic.LetsEncryptProductionCA),
CertMagicDomain: GetString(tlsCertMagicDomainKey, ""),
CertMagicEmail: GetString(tlsCertMagicEmailKey, ""),
CertMagicStoragePath: GetString(tlsCertMagicStoragePath, ""),
Disable: GetBool(tlsDisableKey, false),
ForceHttps: GetBool(tlsForceHttpsKey, true),
CertFile: GetString(tlsCertFileKey, ""),
KeyFile: GetString(tlsKeyFileKey, ""),
AcmeEnabled: GetBool(tlsAcmeKey, false),
AcmeCA: GetString(tlsAcmeCAKey, certmagic.LetsEncryptProductionCA),
AcmeEmail: GetString(tlsAcmeEmailKey, ""),
AcmePath: GetString(tlsAcmePath, ""),
},
PollNet: PollNet{
KeepAliveInterval: defaultKeepAliveInterval,
},
AuthProvider: AuthProvider{
Issuer: GetString(authProviderIssuerKey, ""),
ClientID: GetString(authProviderClientIdKey, ""),
ClientSecret: GetString(authProviderClientSecretKey, ""),
Scopes: GetStrings(authProviderScopesKey, nil),
},
Logging: Logging{
Level: GetString(loggingLevelKey, "info"),
@@ -79,31 +122,41 @@ func defaultConfig() *Config {
File: GetString(loggingFileKey, ""),
},
}
return c, nil
}
type ServerKeys struct {
SystemAdminKey key.ServerPrivate
SystemAdminKey *key.ServerPrivate
ControlKey tkey.MachinePrivate
LegacyControlKey tkey.MachinePrivate
}
type Config struct {
HttpListenAddr string `yaml:"http_listen_addr,omitempty"`
HttpsListenAddr string `yaml:"https_listen_addr,omitempty"`
MetricsListenAddr string `yaml:"metrics_listen_addr,omitempty"`
ServerUrl string `yaml:"server_url,omitempty"`
Tls Tls `yaml:"tls,omitempty"`
Logging Logging `yaml:"logging,omitempty"`
Keys Keys `yaml:"keys,omitempty"`
Database Database `yaml:"database,omitempty"`
HttpListenAddr string `yaml:"http_listen_addr,omitempty"`
HttpsListenAddr string `yaml:"https_listen_addr,omitempty"`
MetricsListenAddr string `yaml:"metrics_listen_addr,omitempty"`
ServerUrl string `yaml:"server_url,omitempty"`
Tls Tls `yaml:"tls,omitempty"`
PollNet PollNet `yaml:"poll_net,omitempty"`
Keys Keys `yaml:"keys,omitempty"`
Database Database `yaml:"database,omitempty"`
AuthProvider AuthProvider `yaml:"auth_provider,omitempty"`
Logging Logging `yaml:"logging,omitempty"`
}
type Tls struct {
Disable bool `yaml:"disable"`
CertFile string `yaml:"cert_file,omitempty"`
KeyFile string `yaml:"key_file,omitempty"`
CertMagicDomain string `yaml:"cert_magic_domain,omitempty"`
CertMagicEmail string `yaml:"cert_magic_email,omitempty"`
CertMagicCA string `yaml:"cert_magic_ca,omitempty"`
CertMagicStoragePath string `yaml:"cert_magic_storage_path,omitempty"`
Disable bool `yaml:"disable"`
ForceHttps bool `yaml:"force_https"`
CertFile string `yaml:"cert_file,omitempty"`
KeyFile string `yaml:"key_file,omitempty"`
AcmeEnabled bool `yaml:"acme,omitempty"`
AcmeEmail string `yaml:"acme_email,omitempty"`
AcmeCA string `yaml:"acme_ca,omitempty"`
AcmePath string `yaml:"acme_path,omitempty"`
}
type PollNet struct {
KeepAliveInterval time.Duration `yaml:"keep_alive_interval"`
}
type Logging struct {
@@ -113,12 +166,28 @@ type Logging struct {
}
type Database struct {
Url string `yaml:"url,omitempty"`
Type string `yaml:"type,omitempty"`
Url string `yaml:"url,omitempty"`
}
type Keys struct {
SystemAdminKey string `yaml:"system_admin_key,omitempty"`
EncryptionKey string `yaml:"encryption_key,omitempty"`
ControlKey string `yaml:"control_key,omitempty"`
LegacyControlKey string `yaml:"legacy_control_key,omitempty"`
SystemAdminKey string `yaml:"system_admin_key,omitempty"`
}
type AuthProvider struct {
Issuer string `yaml:"issuer"`
ClientID string `yaml:"client_id"`
ClientSecret string `yaml:"client_secret"`
Scopes []string `yaml:"additional_scopes"`
SystemAdminPolicy SystemAdminPolicy `yaml:"system_admins"`
}
type SystemAdminPolicy struct {
Subs []string `json:"subs,omitempty"`
Emails []string `json:"emails,omitempty"`
Filters []string `json:"filters,omitempty"`
}
func (c *Config) CreateUrl(format string, a ...interface{}) string {
@@ -127,12 +196,28 @@ func (c *Config) CreateUrl(format string, a ...interface{}) string {
}
func (c *Config) ReadServerKeys() (*ServerKeys, error) {
systemAdminKey, err := key.ParsePrivateKey(c.Keys.SystemAdminKey)
if err != nil {
return nil, fmt.Errorf("error reading system admin key: %v", err)
keys := &ServerKeys{}
if len(c.Keys.SystemAdminKey) != 0 {
systemAdminKey, err := key.ParsePrivateKey(c.Keys.SystemAdminKey)
if err != nil {
return nil, fmt.Errorf("error reading system admin key: %v", err)
}
keys.SystemAdminKey = systemAdminKey
}
return &ServerKeys{
SystemAdminKey: *systemAdminKey,
}, nil
controlKey, err := util.ParseMachinePrivateKey(c.Keys.ControlKey)
if err != nil {
return nil, fmt.Errorf("error reading control key: %v", err)
}
keys.ControlKey = *controlKey
legacyControlKey, err := util.ParseMachinePrivateKey(c.Keys.LegacyControlKey)
if err != nil {
return nil, fmt.Errorf("error reading legacy control key: %v", err)
}
keys.LegacyControlKey = *legacyControlKey
return keys, nil
}
+21
View File
@@ -3,6 +3,7 @@ package config
import (
"os"
"strings"
"time"
)
func GetBool(key string, defaultValue bool) bool {
@@ -20,3 +21,23 @@ func GetString(key, defaultValue string) string {
}
return defaultValue
}
func GetStrings(key string, defaultValue []string) []string {
v := os.Getenv(key)
if v != "" {
return strings.Split(v, ",")
}
return defaultValue
}
func GetDuration(key string, defaultValue time.Duration) (time.Duration, error) {
v := os.Getenv(key)
if len(v) > 0 {
duration, err := time.ParseDuration(v)
if err != nil {
return defaultValue, err
}
return duration, nil
}
return defaultValue, nil
}
+43 -80
View File
@@ -2,13 +2,12 @@ package database
import (
"context"
"encoding/json"
"errors"
"github.com/glebarez/sqlite"
"fmt"
"github.com/go-gormigrate/gormigrate/v2"
"github.com/hashicorp/go-hclog"
"net/http"
"tailscale.com/tailcfg"
tskey "tailscale.com/types/key"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/database/migration"
"time"
"github.com/jsiebens/ionscale/internal/config"
@@ -17,106 +16,70 @@ import (
"gorm.io/gorm/logger"
)
func OpenDB(config *config.Database, logger hclog.Logger) (*gorm.DB, domain.Repository, error) {
gormDB, err := createDB(config, logger)
type db interface {
DB() *gorm.DB
Lock() error
Unlock() error
UnlockErr(error) error
}
func OpenDB(config *config.Database, logger hclog.Logger) (domain.Repository, broker.Pubsub, error) {
db, pubsub, err := createDB(config, logger)
if err != nil {
return nil, nil, err
}
repository := domain.NewRepository(gormDB)
repository := domain.NewRepository(db.DB())
if err := migrate(gormDB, repository); err != nil {
if err := db.Lock(); err != nil {
return nil, nil, err
}
return gormDB, repository, nil
if err := db.UnlockErr(migrate(db.DB())); err != nil {
return nil, nil, err
}
return repository, pubsub, nil
}
func createDB(config *config.Database, logger hclog.Logger) (*gorm.DB, error) {
func createDB(config *config.Database, logger hclog.Logger) (db, broker.Pubsub, error) {
gormConfig := &gorm.Config{
Logger: &GormLoggerAdapter{logger: logger.Named("db")},
}
return gorm.Open(sqlite.Open(config.Url), gormConfig)
}
func migrate(db *gorm.DB, repository domain.Repository) error {
err := db.AutoMigrate(
&domain.ServerConfig{},
&domain.Tailnet{},
&domain.TailnetConfig{},
&domain.AuthMethod{},
&domain.AuthFilter{},
&domain.Account{},
&domain.User{},
&domain.AuthKey{},
&domain.Machine{},
&domain.RegistrationRequest{},
)
if err != nil {
return err
}
if err := initializeControlKeys(repository); err != nil {
return err
}
if err := initializeDERPMap(repository); err != nil {
return err
}
return nil
}
func initializeDERPMap(repository domain.Repository) error {
ctx := context.Background()
derpMap, err := repository.GetDERPMap(ctx)
if err != nil {
return err
}
if derpMap != nil {
return nil
}
getJson := func(url string, target interface{}) error {
c := http.Client{Timeout: 5 * time.Second}
r, err := c.Get(url)
switch config.Type {
case "sqlite", "sqlite3":
db, err := newSqliteDB(config, gormConfig)
return db, broker.NewPubsubInMemory(), err
case "postgres", "postgresql":
db, err := newPostgresDB(config, gormConfig)
if err != nil {
return err
return nil, nil, err
}
defer r.Body.Close()
return json.NewDecoder(r.Body).Decode(target)
stdDB, err := db.DB().DB()
if err != nil {
return nil, nil, err
}
pubsub, err := broker.NewPubsub(context.TODO(), stdDB, config.Url)
if err != nil {
return nil, nil, err
}
return db, pubsub, err
}
m := &tailcfg.DERPMap{}
if err := getJson("https://controlplane.tailscale.com/derpmap/default", m); err != nil {
return err
}
return nil, nil, fmt.Errorf("invalid database type '%s'", config.Type)
}
if err := repository.SetDERPMap(ctx, m); err != nil {
func migrate(db *gorm.DB) error {
m := gormigrate.New(db, gormigrate.DefaultOptions, migration.Migrations())
if err := m.Migrate(); err != nil {
return err
}
return nil
}
func initializeControlKeys(repository domain.Repository) error {
ctx := context.Background()
keys, err := repository.GetControlKeys(ctx)
if keys != nil || err != nil {
return err
}
keys = &domain.ControlKeys{
ControlKey: tskey.NewMachine(),
LegacyControlKey: tskey.NewMachine(),
}
return repository.SetControlKeys(ctx, keys)
}
type GormLoggerAdapter struct {
logger hclog.Logger
}
@@ -0,0 +1,151 @@
package migration
import (
"github.com/go-gormigrate/gormigrate/v2"
"github.com/jsiebens/ionscale/internal/domain"
"gorm.io/gorm"
"time"
)
func m202209070900_initial_schema() *gormigrate.Migration {
return &gormigrate.Migration{
ID: "202209070900",
Migrate: func(db *gorm.DB) error {
// it's a good practice to copy the struct inside the function,
// so side effects are prevented if the original struct changes during the time
type ServerConfig struct {
Key string `gorm:"primary_key"`
Value []byte
}
type Tailnet struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string `gorm:"type:varchar(64);unique_index"`
DNSConfig domain.DNSConfig
IAMPolicy domain.IAMPolicy
ACLPolicy domain.ACLPolicy
}
type Account struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
ExternalID string
LoginName string
}
type User struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string
UserType domain.UserType
TailnetID uint64
Tailnet Tailnet
AccountID *uint64
Account *Account
}
type SystemApiKey struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Key string `gorm:"type:varchar(64);unique_index"`
Hash string
CreatedAt time.Time
ExpiresAt *time.Time
AccountID uint64
Account Account
}
type ApiKey struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Key string `gorm:"type:varchar(64);unique_index"`
Hash string
CreatedAt time.Time
ExpiresAt *time.Time
TailnetID uint64
Tailnet Tailnet
UserID uint64
User User
}
type AuthKey struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Key string `gorm:"type:varchar(64);unique_index"`
Hash string
Ephemeral bool
Tags domain.Tags
CreatedAt time.Time
ExpiresAt *time.Time
TailnetID uint64
Tailnet Tailnet
UserID uint64
User User
}
type Machine struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string
NameIdx uint64
MachineKey string
NodeKey string
DiscoKey string
Ephemeral bool
RegisteredTags domain.Tags
Tags domain.Tags
KeyExpiryDisabled bool
HostInfo domain.HostInfo
Endpoints domain.Endpoints
AllowIPs domain.AllowIPs
IPv4 domain.IP
IPv6 domain.IP
CreatedAt time.Time
ExpiresAt time.Time
LastSeen *time.Time
UserID uint64
User User
TailnetID uint64
Tailnet Tailnet
}
type RegistrationRequest struct {
MachineKey string `gorm:"primary_key;autoIncrement:false"`
Key string `gorm:"type:varchar(64);unique_index"`
Data domain.RegistrationRequestData
CreatedAt time.Time
Authenticated bool
Error string
}
type AuthenticationRequest struct {
Key string `gorm:"primary_key;autoIncrement:false"`
Token string
TailnetID *uint64
Error string
CreatedAt time.Time
}
return db.AutoMigrate(
&ServerConfig{},
&Tailnet{},
&Account{},
&User{},
&SystemApiKey{},
&ApiKey{},
&AuthKey{},
&Machine{},
&RegistrationRequest{},
&AuthenticationRequest{},
)
},
Rollback: nil,
}
}
+12
View File
@@ -0,0 +1,12 @@
package migration
import (
"github.com/go-gormigrate/gormigrate/v2"
)
func Migrations() []*gormigrate.Migration {
var migrations = []*gormigrate.Migration{
m202209070900_initial_schema(),
}
return migrations
}
+69
View File
@@ -0,0 +1,69 @@
package database
import (
"context"
"fmt"
"hash/crc32"
"github.com/hashicorp/go-multierror"
"github.com/jsiebens/ionscale/internal/config"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func newPostgresDB(config *config.Database, g *gorm.Config) (db, error) {
db, err := gorm.Open(postgres.Open(config.Url), g)
if err != nil {
return nil, err
}
return &Postgres{
db: db,
}, nil
}
type Postgres struct {
db *gorm.DB
}
func (s *Postgres) DB() *gorm.DB {
return s.db
}
func (s *Postgres) Lock() error {
d, _ := s.db.DB()
query := `SELECT pg_advisory_lock($1)`
id := s.generateAdvisoryLockId()
if _, err := d.ExecContext(context.Background(), query, id); err != nil {
return err
}
return nil
}
func (s *Postgres) Unlock() error {
d, _ := s.db.DB()
query := `SELECT pg_advisory_unlock($1)`
if _, err := d.ExecContext(context.Background(), query, s.generateAdvisoryLockId()); err != nil {
return err
}
return nil
}
func (s *Postgres) UnlockErr(prevErr error) error {
if err := s.Unlock(); err != nil {
return multierror.Append(prevErr, err)
}
return prevErr
}
const advisoryLockIDSalt uint = 1486364155
func (s *Postgres) generateAdvisoryLockId() string {
sum := crc32.ChecksumIEEE([]byte("ionscale_migration"))
sum = sum * uint32(advisoryLockIDSalt)
return fmt.Sprint(sum)
}
+38
View File
@@ -0,0 +1,38 @@
package database
import (
"github.com/glebarez/sqlite"
"github.com/jsiebens/ionscale/internal/config"
"gorm.io/gorm"
)
func newSqliteDB(config *config.Database, g *gorm.Config) (db, error) {
db, err := gorm.Open(sqlite.Open(config.Url), g)
if err != nil {
return nil, err
}
return &Sqlite{
db: db,
}, nil
}
type Sqlite struct {
db *gorm.DB
}
func (s *Sqlite) DB() *gorm.DB {
return s.db
}
func (s *Sqlite) Lock() error {
return nil
}
func (s *Sqlite) Unlock() error {
return nil
}
func (s *Sqlite) UnlockErr(prevErr error) error {
return prevErr
}
+5 -7
View File
@@ -8,19 +8,17 @@ import (
)
type Account struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
ExternalID string
LoginName string
AuthMethodID uint64
ID uint64 `gorm:"primary_key"`
ExternalID string
LoginName string
}
func (r *repository) GetOrCreateAccount(ctx context.Context, authMethodID uint64, externalID, loginName string) (*Account, bool, error) {
func (r *repository) GetOrCreateAccount(ctx context.Context, externalID, loginName string) (*Account, bool, error) {
account := &Account{}
id := util.NextID()
tx := r.withContext(ctx).
Where(Account{AuthMethodID: authMethodID, ExternalID: externalID}).
Where(Account{ExternalID: externalID}).
Attrs(Account{ID: id, LoginName: loginName}).
FirstOrCreate(account)
+103 -35
View File
@@ -1,17 +1,23 @@
package domain
import (
"database/sql/driver"
"encoding/json"
"fmt"
"inet.af/netaddr"
"github.com/hashicorp/go-multierror"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"net/netip"
"strconv"
"strings"
"tailscale.com/tailcfg"
)
type ACLPolicy struct {
Groups map[string][]string `json:"groups,omitempty"`
Hosts map[string]string `json:"hosts,omitempty"`
ACLs []ACL `json:"acls"`
Groups map[string][]string `json:"groups,omitempty"`
Hosts map[string]string `json:"hosts,omitempty"`
ACLs []ACL `json:"acls"`
TagOwners map[string][]string `json:"tag_owners"`
}
type ACL struct {
@@ -20,7 +26,7 @@ type ACL struct {
Dst []string `json:"dst"`
}
func defaultPolicy() ACLPolicy {
func DefaultPolicy() ACLPolicy {
return ACLPolicy{
ACLs: []ACL{
{
@@ -32,27 +38,61 @@ func defaultPolicy() ACLPolicy {
}
}
type aclEngine struct {
policy *ACLPolicy
expandedTags map[string][]string
}
func IsValidPeer(policy *ACLPolicy, src *Machine, dest *Machine) bool {
f := &aclEngine{
policy: policy,
func (a ACLPolicy) CheckTags(tags []string) error {
var result *multierror.Error
for _, t := range tags {
if _, ok := a.TagOwners[t]; !ok {
result = multierror.Append(result, fmt.Errorf("tag [%s] is invalid or not permitted", t))
}
}
return f.isValidPeer(src, dest)
return result.ErrorOrNil()
}
func BuildFilterRules(policy *ACLPolicy, dst *Machine, peers []Machine) []tailcfg.FilterRule {
f := &aclEngine{
policy: policy,
func (a ACLPolicy) CheckTagOwners(tags []string, p *User) error {
var result *multierror.Error
for _, t := range tags {
if ok := a.IsTagOwner(t, p); !ok {
result = multierror.Append(result, fmt.Errorf("tag [%s] is invalid or not permitted", t))
}
}
return f.build(dst, peers)
return result.ErrorOrNil()
}
func (a *aclEngine) isValidPeer(src *Machine, dest *Machine) bool {
for _, acl := range a.policy.ACLs {
func (a ACLPolicy) IsTagOwner(tag string, p *User) bool {
if tagOwners, ok := a.TagOwners[tag]; ok {
if p.UserType == UserTypeService {
return true
}
return a.validateTagOwners(tagOwners, p)
}
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 {
return true
}
}
}
} else {
if alias == p.Name {
return true
}
}
}
return false
}
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 {
allDestPorts := a.expandMachineToDstPorts(dest, acl.Dst)
if len(allDestPorts) == 0 {
continue
@@ -67,19 +107,19 @@ func (a *aclEngine) isValidPeer(src *Machine, dest *Machine) bool {
return false
}
func (a *aclEngine) build(dst *Machine, peers []Machine) []tailcfg.FilterRule {
func (a ACLPolicy) BuildFilterRules(srcs []Machine, dst *Machine) []tailcfg.FilterRule {
var rules []tailcfg.FilterRule
for _, acl := range a.policy.ACLs {
for _, acl := range a.ACLs {
allDestPorts := a.expandMachineToDstPorts(dst, acl.Dst)
if len(allDestPorts) == 0 {
continue
}
var allSrcIPsSet = &StringSet{}
for _, src := range acl.Src {
for _, peer := range peers {
srcIPs := a.expandMachineAlias(&peer, src, true)
for _, alias := range acl.Src {
for _, src := range srcs {
srcIPs := a.expandMachineAlias(&src, alias, true)
allSrcIPsSet.Add(srcIPs...)
}
}
@@ -105,7 +145,7 @@ func (a *aclEngine) build(dst *Machine, peers []Machine) []tailcfg.FilterRule {
return rules
}
func (a *aclEngine) expandMachineToDstPorts(m *Machine, ports []string) []tailcfg.NetPortRange {
func (a ACLPolicy) expandMachineToDstPorts(m *Machine, ports []string) []tailcfg.NetPortRange {
allDestRanges := []tailcfg.NetPortRange{}
for _, d := range ports {
ranges := a.expandMachineDestToNetPortRanges(m, d)
@@ -114,7 +154,7 @@ func (a *aclEngine) expandMachineToDstPorts(m *Machine, ports []string) []tailcf
return allDestRanges
}
func (a *aclEngine) expandMachineDestToNetPortRanges(m *Machine, dest string) []tailcfg.NetPortRange {
func (a ACLPolicy) expandMachineDestToNetPortRanges(m *Machine, dest string) []tailcfg.NetPortRange {
tokens := strings.Split(dest, ":")
if len(tokens) < 2 || len(tokens) > 3 {
return nil
@@ -151,7 +191,7 @@ func (a *aclEngine) expandMachineDestToNetPortRanges(m *Machine, dest string) []
return dests
}
func (a *aclEngine) expandMachineAlias(m *Machine, alias string, src bool) []string {
func (a ACLPolicy) expandMachineAlias(m *Machine, alias string, src bool) []string {
if alias == "*" {
if alias == "*" {
return []string{"*"}
@@ -163,7 +203,7 @@ func (a *aclEngine) expandMachineAlias(m *Machine, alias string, src bool) []str
}
if strings.HasPrefix(alias, "group:") && !m.HasTags() {
users, ok := a.policy.Groups[alias]
users, ok := a.Groups[alias]
if !ok {
return []string{}
@@ -178,26 +218,26 @@ func (a *aclEngine) expandMachineAlias(m *Machine, alias string, src bool) []str
return []string{}
}
if strings.HasPrefix(alias, "tag:") && m.HasTag(alias[4:]) {
if strings.HasPrefix(alias, "tag:") && m.HasTag(alias) {
return []string{m.IPv4.String(), m.IPv6.String()}
}
if h, ok := a.policy.Hosts[alias]; ok {
if h, ok := a.Hosts[alias]; ok {
alias = h
}
if src {
ip, err := netaddr.ParseIP(alias)
ip, err := netip.ParseAddr(alias)
if err == nil && m.HasIP(ip) {
return []string{ip.String()}
}
} else {
ip, err := netaddr.ParseIP(alias)
ip, err := netip.ParseAddr(alias)
if err == nil && m.IsAllowedIP(ip) {
return []string{ip.String()}
}
prefix, err := netaddr.ParseIPPrefix(alias)
prefix, err := netip.ParsePrefix(alias)
if err == nil && m.IsAllowedIPPrefix(prefix) {
return []string{prefix.String()}
}
@@ -206,7 +246,7 @@ func (a *aclEngine) expandMachineAlias(m *Machine, alias string, src bool) []str
return []string{}
}
func (a *aclEngine) expandValuePortToPortRange(s string) ([]tailcfg.PortRange, error) {
func (a ACLPolicy) expandValuePortToPortRange(s string) ([]tailcfg.PortRange, error) {
if s == "*" {
return []tailcfg.PortRange{{First: 0, Last: 65535}}, nil
}
@@ -243,6 +283,34 @@ func (a *aclEngine) expandValuePortToPortRange(s string) ([]tailcfg.PortRange, e
return ports, nil
}
func (i *ACLPolicy) Scan(destination interface{}) error {
switch value := destination.(type) {
case []byte:
return json.Unmarshal(value, i)
default:
return fmt.Errorf("unexpected data type %T", destination)
}
}
func (i ACLPolicy) Value() (driver.Value, error) {
bytes, err := json.Marshal(i)
return bytes, err
}
// GormDataType gorm common data type
func (ACLPolicy) GormDataType() string {
return "json"
}
// GormDBDataType gorm db data type
func (ACLPolicy) GormDBDataType(db *gorm.DB, field *schema.Field) string {
switch db.Dialector.Name() {
case "sqlite":
return "JSON"
}
return ""
}
type StringSet struct {
items map[string]bool
}
+71
View File
@@ -0,0 +1,71 @@
package domain
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestACLPolicy_IsTagOwner(t *testing.T) {
policy := ACLPolicy{
Groups: map[string][]string{
"group:engineers": {"jane@example.com"},
},
TagOwners: map[string][]string{
"tag:web": {"john@example.com", "group:engineers"},
}}
testCases := []struct {
name string
tag string
userName string
userType UserType
expectErr bool
}{
{
name: "system admin is always a valid owner",
tag: "tag:web",
userName: "system admin",
userType: UserTypeService,
expectErr: false,
},
{
name: "direct tag owner",
tag: "tag:web",
userName: "john@example.com",
userType: UserTypePerson,
expectErr: false,
},
{
name: "owner by group",
tag: "tag:web",
userName: "jane@example.com",
userType: UserTypePerson,
expectErr: false,
},
{
name: "unknown owner",
tag: "tag:web",
userName: "nick@example.com",
userType: UserTypePerson,
expectErr: true,
},
{
name: "unknown tag",
tag: "tag:unknown",
userName: "jane@example.com",
userType: UserTypePerson,
expectErr: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := policy.CheckTagOwners([]string{tc.tag}, &User{Name: tc.userName, UserType: tc.userType})
if tc.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
+103
View File
@@ -0,0 +1,103 @@
package domain
import (
"context"
"errors"
"fmt"
"github.com/jsiebens/ionscale/internal/util"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"strings"
"time"
)
func CreateApiKey(tailnet *Tailnet, user *User, expiresAt *time.Time) (string, *ApiKey) {
key := util.RandStringBytes(12)
pwd := util.RandStringBytes(22)
value := fmt.Sprintf("%s_%s", key, pwd)
hash, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
if err != nil {
panic(err)
}
return value, &ApiKey{
ID: util.NextID(),
Key: key,
Hash: string(hash),
CreatedAt: time.Now().UTC(),
ExpiresAt: expiresAt,
TailnetID: tailnet.ID,
UserID: user.ID,
}
}
type ApiKey struct {
ID uint64 `gorm:"primary_key"`
Key string
Hash string
CreatedAt time.Time
ExpiresAt *time.Time
TailnetID uint64
Tailnet Tailnet
UserID uint64
User User
}
func (r *repository) SaveApiKey(ctx context.Context, key *ApiKey) error {
tx := r.withContext(ctx).Save(key)
if tx.Error != nil {
return tx.Error
}
return nil
}
func (r *repository) LoadApiKey(ctx context.Context, key string) (*ApiKey, error) {
split := strings.Split(key, "_")
if len(split) != 2 {
return nil, nil
}
var m ApiKey
tx := r.withContext(ctx).Preload("User").Preload("Tailnet").First(&m, "key = ?", split[0])
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
if err := bcrypt.CompareHashAndPassword([]byte(m.Hash), []byte(split[1])); err != nil {
return nil, nil
}
if !m.ExpiresAt.IsZero() && m.ExpiresAt.Before(time.Now()) {
return nil, nil
}
return &m, nil
}
func (r *repository) DeleteApiKeysByTailnet(ctx context.Context, tailnetID uint64) error {
tx := r.withContext(ctx).
Where("tailnet_id = ?", tailnetID).
Delete(&ApiKey{TailnetID: tailnetID})
return tx.Error
}
func (r *repository) DeleteApiKeysByUser(ctx context.Context, userID uint64) error {
tx := r.withContext(ctx).
Where("user_id = ?", userID).
Delete(&ApiKey{UserID: userID})
return tx.Error
}
-123
View File
@@ -1,123 +0,0 @@
package domain
import (
"context"
"errors"
"github.com/hashicorp/go-bexpr"
"github.com/mitchellh/pointerstructure"
"gorm.io/gorm"
)
type AuthFilter struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Expr string
AuthMethodID uint64
AuthMethod AuthMethod
TailnetID *uint64
Tailnet *Tailnet
}
type AuthFilters []AuthFilter
func (f *AuthFilter) Evaluate(v interface{}) (bool, error) {
if f.Expr == "*" {
return true, nil
}
eval, err := bexpr.CreateEvaluator(f.Expr)
if err != nil {
return false, err
}
result, err := eval.Evaluate(v)
if err != nil && !errors.Is(err, pointerstructure.ErrNotFound) {
return false, err
}
return result, err
}
func (fs AuthFilters) Evaluate(v interface{}) []Tailnet {
var tailnetIDMap = make(map[uint64]bool)
var tailnets []Tailnet
for _, f := range fs {
approved, err := f.Evaluate(v)
if err == nil && approved {
if f.TailnetID != nil {
_, alreadyApproved := tailnetIDMap[*f.TailnetID]
if !alreadyApproved {
tailnetIDMap[*f.TailnetID] = true
tailnets = append(tailnets, *f.Tailnet)
}
}
}
}
return tailnets
}
func (r *repository) GetAuthFilter(ctx context.Context, id uint64) (*AuthFilter, error) {
var t AuthFilter
tx := r.withContext(ctx).Take(&t, "id = ?", id)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
return &t, nil
}
func (r *repository) SaveAuthFilter(ctx context.Context, m *AuthFilter) error {
tx := r.withContext(ctx).Save(m)
if tx.Error != nil {
return tx.Error
}
return nil
}
func (r *repository) ListAuthFilters(ctx context.Context) (AuthFilters, error) {
var filters = []AuthFilter{}
tx := r.withContext(ctx).
Preload("Tailnet").
Preload("AuthMethod").
Find(&filters)
if tx.Error != nil {
return nil, tx.Error
}
return filters, nil
}
func (r *repository) ListAuthFiltersByAuthMethod(ctx context.Context, authMethodID uint64) (AuthFilters, error) {
var filters = []AuthFilter{}
tx := r.withContext(ctx).
Preload("Tailnet").
Preload("AuthMethod").
Where("auth_method_id = ?", authMethodID).Find(&filters)
if tx.Error != nil {
return nil, tx.Error
}
return filters, nil
}
func (r *repository) DeleteAuthFilter(ctx context.Context, id uint64) error {
tx := r.withContext(ctx).Delete(&AuthFilter{ID: id})
return tx.Error
}
func (r *repository) DeleteAuthFiltersByTailnet(ctx context.Context, tailnetID uint64) error {
tx := r.withContext(ctx).Where("tailnet_id = ?", tailnetID).Delete(&AuthFilter{})
return tx.Error
}
+23 -2
View File
@@ -36,8 +36,8 @@ func CreateAuthKey(tailnet *Tailnet, user *User, ephemeral bool, tags Tags, expi
}
type AuthKey struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Key string `gorm:"type:varchar(64);unique_index"`
ID uint64 `gorm:"primary_key"`
Key string
Hash string
Ephemeral bool
Tags Tags
@@ -93,6 +93,14 @@ func (r *repository) DeleteAuthKeysByTailnet(ctx context.Context, tailnetID uint
return tx.Error
}
func (r *repository) DeleteAuthKeysByUser(ctx context.Context, userID uint64) error {
tx := r.withContext(ctx).
Where("user_id = ?", userID).
Delete(&AuthKey{UserID: userID})
return tx.Error
}
func (r *repository) ListAuthKeys(ctx context.Context, tailnetID uint64) ([]AuthKey, error) {
var authKeys = []AuthKey{}
tx := (r.withContext(ctx).
@@ -106,6 +114,19 @@ func (r *repository) ListAuthKeys(ctx context.Context, tailnetID uint64) ([]Auth
return authKeys, nil
}
func (r *repository) ListAuthKeysByTailnetAndUser(ctx context.Context, tailnetID, userID uint64) ([]AuthKey, error) {
var authKeys = []AuthKey{}
tx := (r.withContext(ctx).
Preload("User").
Preload("Tailnet")).
Where("tailnet_id = ? and user_id = ?", tailnetID, userID).
Find(&authKeys)
if tx.Error != nil {
return nil, tx.Error
}
return authKeys, nil
}
func (r *repository) LoadAuthKey(ctx context.Context, key string) (*AuthKey, error) {
split := strings.Split(key, "_")
if len(split) != 2 {
-50
View File
@@ -1,50 +0,0 @@
package domain
import (
"context"
"errors"
"gorm.io/gorm"
)
type AuthMethod struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string `gorm:"type:varchar(64);unique_index"`
Type string
Issuer string
ClientId string
ClientSecret string
}
func (r *repository) SaveAuthMethod(ctx context.Context, m *AuthMethod) error {
tx := r.withContext(ctx).Save(m)
if tx.Error != nil {
return tx.Error
}
return nil
}
func (r *repository) ListAuthMethods(ctx context.Context) ([]AuthMethod, error) {
var authMethods = []AuthMethod{}
tx := r.withContext(ctx).Find(&authMethods)
if tx.Error != nil {
return nil, tx.Error
}
return authMethods, nil
}
func (r *repository) GetAuthMethod(ctx context.Context, id uint64) (*AuthMethod, error) {
var m AuthMethod
tx := r.withContext(ctx).First(&m, "id = ?", id)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
return &m, nil
}
+46
View File
@@ -0,0 +1,46 @@
package domain
import (
"context"
"errors"
"gorm.io/gorm"
"time"
)
type AuthenticationRequest struct {
Key string `gorm:"primary_key"`
Token string
TailnetID *uint64
Error string
CreatedAt time.Time
}
func (r *repository) SaveAuthenticationRequest(ctx context.Context, session *AuthenticationRequest) error {
tx := r.withContext(ctx).Save(session)
if tx.Error != nil {
return tx.Error
}
return nil
}
func (r *repository) GetAuthenticationRequest(ctx context.Context, key string) (*AuthenticationRequest, error) {
var m AuthenticationRequest
tx := r.withContext(ctx).First(&m, "key = ?", key)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
return &m, nil
}
func (r *repository) DeleteAuthenticationRequest(ctx context.Context, key string) error {
tx := r.withContext(ctx).Delete(&AuthenticationRequest{Key: key})
return tx.Error
}
+44
View File
@@ -0,0 +1,44 @@
package domain
import (
"database/sql/driver"
"encoding/json"
"fmt"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
type DNSConfig struct {
MagicDNS bool `json:"magic_dns"`
OverrideLocalDNS bool `json:"override_local_dns"`
Nameservers []string `json:"nameservers"`
Routes map[string][]string `json:"routes"`
}
func (i *DNSConfig) Scan(destination interface{}) error {
switch value := destination.(type) {
case []byte:
return json.Unmarshal(value, i)
default:
return fmt.Errorf("unexpected data type %T", destination)
}
}
func (i DNSConfig) Value() (driver.Value, error) {
bytes, err := json.Marshal(i)
return bytes, err
}
// GormDataType gorm common data type
func (DNSConfig) GormDataType() string {
return "json"
}
// GormDBDataType gorm db data type
func (DNSConfig) GormDBDataType(db *gorm.DB, field *schema.Field) string {
switch db.Dialector.Name() {
case "sqlite":
return "JSON"
}
return ""
}
+97
View File
@@ -0,0 +1,97 @@
package domain
import (
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"github.com/hashicorp/go-bexpr"
"github.com/mitchellh/pointerstructure"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
type Identity struct {
UserID string
Username string
Email string
Attr map[string]interface{}
}
type IAMPolicy struct {
Subs []string `json:"subs,omitempty"`
Emails []string `json:"emails,omitempty"`
Filters []string `json:"filters,omitempty"`
Roles map[string]UserRole `json:"roles,omitempty"`
}
func (i *IAMPolicy) GetRole(user User) UserRole {
if val, ok := i.Roles[user.Name]; ok {
return val
}
return UserRoleMember
}
func (i *IAMPolicy) EvaluatePolicy(identity *Identity) (bool, error) {
for _, sub := range i.Subs {
if identity.UserID == sub {
return true, nil
}
}
for _, email := range i.Emails {
if identity.Email == email {
return true, nil
}
}
for _, f := range i.Filters {
if f == "*" {
return true, nil
}
evaluator, err := bexpr.CreateEvaluator(f)
if err != nil {
return false, err
}
result, err := evaluator.Evaluate(identity.Attr)
if err != nil && !errors.Is(err, pointerstructure.ErrNotFound) {
return false, err
}
if result {
return true, nil
}
}
return false, nil
}
func (i *IAMPolicy) Scan(destination interface{}) error {
switch value := destination.(type) {
case []byte:
return json.Unmarshal(value, i)
default:
return fmt.Errorf("unexpected data type %T", destination)
}
}
func (i IAMPolicy) Value() (driver.Value, error) {
bytes, err := json.Marshal(i)
return bytes, err
}
// GormDataType gorm common data type
func (IAMPolicy) GormDataType() string {
return "json"
}
// GormDBDataType gorm db data type
func (IAMPolicy) GormDBDataType(db *gorm.DB, field *schema.Field) string {
switch db.Dialector.Name() {
case "sqlite":
return "JSON"
}
return ""
}
+79 -42
View File
@@ -8,21 +8,22 @@ import (
"fmt"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"inet.af/netaddr"
"net/netip"
"tailscale.com/tailcfg"
"time"
)
type Machine struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string
NameIdx uint64
MachineKey string
NodeKey string
DiscoKey string
Ephemeral bool
RegisteredTags Tags
Tags Tags
ID uint64 `gorm:"primary_key"`
Name string
NameIdx uint64
MachineKey string
NodeKey string
DiscoKey string
Ephemeral bool
RegisteredTags Tags
Tags Tags
KeyExpiryDisabled bool
HostInfo HostInfo
Endpoints Endpoints
@@ -32,7 +33,7 @@ type Machine struct {
IPv6 IP
CreatedAt time.Time
ExpiresAt *time.Time
ExpiresAt time.Time
LastSeen *time.Time
UserID uint64
@@ -45,11 +46,11 @@ type Machine struct {
type Machines []Machine
func (m *Machine) IsExpired() bool {
return m.ExpiresAt != nil && !m.ExpiresAt.IsZero() && m.ExpiresAt.Before(time.Now())
return !m.KeyExpiryDisabled && !m.ExpiresAt.IsZero() && m.ExpiresAt.Before(time.Now())
}
func (m *Machine) HasIP(v netaddr.IP) bool {
return v.Compare(*m.IPv4.IP) == 0 || v.Compare(*m.IPv6.IP) == 0
func (m *Machine) HasIP(v netip.Addr) bool {
return v.Compare(*m.IPv4.Addr) == 0 || v.Compare(*m.IPv6.Addr) == 0
}
func (m *Machine) HasTag(tag string) bool {
@@ -69,7 +70,7 @@ func (m *Machine) HasTags() bool {
return len(m.Tags) != 0
}
func (m *Machine) IsAllowedIP(i netaddr.IP) bool {
func (m *Machine) IsAllowedIP(i netip.Addr) bool {
if m.HasIP(i) {
return true
}
@@ -81,7 +82,7 @@ func (m *Machine) IsAllowedIP(i netaddr.IP) bool {
return false
}
func (m *Machine) IsAllowedIPPrefix(i netaddr.IPPrefix) bool {
func (m *Machine) IsAllowedIPPrefix(i netip.Prefix) bool {
for _, t := range m.AllowIPs {
if t.Overlaps(i) {
return true
@@ -91,13 +92,13 @@ func (m *Machine) IsAllowedIPPrefix(i netaddr.IPPrefix) bool {
}
type IP struct {
*netaddr.IP
*netip.Addr
}
func (i *IP) Scan(destination interface{}) error {
switch value := destination.(type) {
case string:
ip, err := netaddr.ParseIP(value)
ip, err := netip.ParseAddr(value)
if err != nil {
return err
}
@@ -109,13 +110,62 @@ func (i *IP) Scan(destination interface{}) error {
}
func (i IP) Value() (driver.Value, error) {
if i.IP == nil {
if i.Addr == nil {
return nil, nil
}
return i.String(), nil
}
type AllowIPs []netaddr.IPPrefix
func (IP) GormDBDataType(db *gorm.DB, field *schema.Field) string {
switch db.Dialector.Name() {
case "postgres":
return "TEXT"
}
return ""
}
type AllowIPs []netip.Prefix
type AllowIPsSet struct {
items map[netip.Prefix]bool
}
func NewAllowIPsSet(t AllowIPs) *AllowIPsSet {
s := &AllowIPsSet{}
return s.Add(t...)
}
func (s *AllowIPsSet) Add(t ...netip.Prefix) *AllowIPsSet {
if s.items == nil {
s.items = make(map[netip.Prefix]bool)
}
for _, v := range t {
s.items[v] = true
}
return s
}
func (s *AllowIPsSet) Remove(t ...netip.Prefix) *AllowIPsSet {
if s.items == nil {
return s
}
for _, v := range t {
delete(s.items, v)
}
return s
}
func (s *AllowIPsSet) Items() []netip.Prefix {
items := []netip.Prefix{}
for i := range s.items {
items = append(items, i)
}
return items
}
func (hi *AllowIPs) Scan(destination interface{}) error {
switch value := destination.(type) {
@@ -313,6 +363,11 @@ func (r *repository) DeleteMachineByTailnet(ctx context.Context, tailnetID uint6
return tx.Error
}
func (r *repository) DeleteMachineByUser(ctx context.Context, userID uint64) error {
tx := r.withContext(ctx).Model(&Machine{}).Where("user_id = ?", userID).Delete(&Machine{})
return tx.Error
}
func (r *repository) ListMachineByTailnet(ctx context.Context, tailnetID uint64) (Machines, error) {
var machines = []Machine{}
@@ -363,7 +418,10 @@ func (r *repository) ListInactiveEphemeralMachines(ctx context.Context, t time.T
func (r *repository) SetMachineLastSeen(ctx context.Context, machineID uint64) error {
now := time.Now().UTC()
tx := r.withContext(ctx).Model(Machine{}).Where("id = ?", machineID).Updates(map[string]interface{}{"last_seen": &now})
tx := r.withContext(ctx).
Model(Machine{}).
Where("id = ?", machineID).
Updates(map[string]interface{}{"last_seen": &now})
if tx.Error != nil {
return tx.Error
@@ -371,24 +429,3 @@ func (r *repository) SetMachineLastSeen(ctx context.Context, machineID uint64) e
return nil
}
func (r *repository) ExpireMachineByAuthMethod(ctx context.Context, authMethodID uint64) (int64, error) {
now := time.Now().UTC()
subQuery := r.withContext(ctx).
Select("machines.id").
Table("machines").
Joins("JOIN users u on u.id = machines.user_id JOIN accounts a on a.id = u.account_id").
Where("a.auth_method_id = ?", authMethodID)
tx := r.withContext(ctx).
Table("machines").
Where("tags = '' AND (expires_at is null or expires_at > ?) AND id in (?)", &now, subQuery).
Updates(map[string]interface{}{"expires_at": &now})
if tx.Error != nil {
return 0, tx.Error
}
return tx.RowsAffected, nil
}
+23
View File
@@ -0,0 +1,23 @@
package domain
type Principal struct {
SystemRole SystemRole
User *User
UserRole UserRole
}
func (p Principal) IsSystemAdmin() bool {
return p.SystemRole.IsAdmin()
}
func (p Principal) IsTailnetAdmin(tailnetID uint64) bool {
return p.User.TailnetID == tailnetID && p.UserRole.IsAdmin()
}
func (p Principal) IsTailnetMember(tailnetID uint64) bool {
return p.User.TailnetID == tailnetID
}
func (p Principal) UserMatches(userID uint64) bool {
return p.User.ID == userID
}
+2 -2
View File
@@ -13,8 +13,8 @@ import (
)
type RegistrationRequest struct {
MachineKey string `gorm:"primary_key;autoIncrement:false"`
Key string `gorm:"type:varchar(64);unique_index"`
MachineKey string `gorm:"primary_key"`
Key string
Data RegistrationRequestData
CreatedAt time.Time
Authenticated bool
+64 -25
View File
@@ -2,54 +2,49 @@ package domain
import (
"context"
"encoding/json"
"gorm.io/gorm"
"net/http"
"sync"
"tailscale.com/tailcfg"
"time"
)
type Repository interface {
GetControlKeys(ctx context.Context) (*ControlKeys, error)
SetControlKeys(ctx context.Context, v *ControlKeys) error
GetDERPMap(ctx context.Context) (*tailcfg.DERPMap, error)
SetDERPMap(ctx context.Context, v *tailcfg.DERPMap) error
SaveAuthMethod(ctx context.Context, m *AuthMethod) error
ListAuthMethods(ctx context.Context) ([]AuthMethod, error)
GetAuthMethod(ctx context.Context, id uint64) (*AuthMethod, error)
GetAuthFilter(ctx context.Context, id uint64) (*AuthFilter, error)
SaveAuthFilter(ctx context.Context, m *AuthFilter) error
ListAuthFilters(ctx context.Context) (AuthFilters, error)
ListAuthFiltersByAuthMethod(ctx context.Context, authMethodID uint64) (AuthFilters, error)
DeleteAuthFilter(ctx context.Context, id uint64) error
DeleteAuthFiltersByTailnet(ctx context.Context, tailnetID uint64) error
GetAccount(ctx context.Context, accountID uint64) (*Account, error)
GetOrCreateAccount(ctx context.Context, authMethodID uint64, externalID, loginName string) (*Account, bool, error)
GetOrCreateAccount(ctx context.Context, externalID, loginName string) (*Account, bool, error)
SaveTailnet(ctx context.Context, tailnet *Tailnet) error
GetOrCreateTailnet(ctx context.Context, name string) (*Tailnet, bool, error)
GetTailnet(ctx context.Context, id uint64) (*Tailnet, error)
ListTailnets(ctx context.Context) ([]Tailnet, error)
DeleteTailnet(ctx context.Context, id uint64) error
GetDNSConfig(ctx context.Context, tailnetID uint64) (*DNSConfig, error)
SetDNSConfig(ctx context.Context, tailnetID uint64, config *DNSConfig) error
DeleteDNSConfig(ctx context.Context, tailnetID uint64) error
GetACLPolicy(ctx context.Context, tailnetID uint64) (*ACLPolicy, error)
SetACLPolicy(ctx context.Context, tailnetID uint64, policy *ACLPolicy) error
DeleteACLPolicy(ctx context.Context, tailnetID uint64) error
SaveSystemApiKey(ctx context.Context, key *SystemApiKey) error
LoadSystemApiKey(ctx context.Context, key string) (*SystemApiKey, error)
SaveApiKey(ctx context.Context, key *ApiKey) error
LoadApiKey(ctx context.Context, key string) (*ApiKey, error)
DeleteApiKeysByTailnet(ctx context.Context, tailnetID uint64) error
DeleteApiKeysByUser(ctx context.Context, userID uint64) error
GetAuthKey(ctx context.Context, id uint64) (*AuthKey, error)
SaveAuthKey(ctx context.Context, key *AuthKey) error
DeleteAuthKey(ctx context.Context, id uint64) (bool, error)
DeleteAuthKeysByTailnet(ctx context.Context, tailnetID uint64) error
DeleteAuthKeysByUser(ctx context.Context, userID uint64) error
ListAuthKeys(ctx context.Context, tailnetID uint64) ([]AuthKey, error)
ListAuthKeysByTailnetAndUser(ctx context.Context, tailnetID, userID uint64) ([]AuthKey, error)
LoadAuthKey(ctx context.Context, key string) (*AuthKey, error)
GetOrCreateServiceUser(ctx context.Context, tailnet *Tailnet) (*User, bool, error)
ListUsers(ctx context.Context, tailnetID uint64) (Users, error)
GetOrCreateUserWithAccount(ctx context.Context, tailnet *Tailnet, account *Account) (*User, bool, error)
GetUser(ctx context.Context, userID uint64) (*User, error)
DeleteUser(ctx context.Context, userID uint64) error
ListUsers(ctx context.Context, tailnetID uint64) (Users, error)
DeleteUsersByTailnet(ctx context.Context, tailnetID uint64) error
SaveMachine(ctx context.Context, m *Machine) error
@@ -62,26 +57,32 @@ type Repository interface {
ListMachineByTailnet(ctx context.Context, tailnetID uint64) (Machines, error)
CountMachineByTailnet(ctx context.Context, tailnetID uint64) (int64, error)
DeleteMachineByTailnet(ctx context.Context, tailnetID uint64) error
DeleteMachineByUser(ctx context.Context, userID uint64) error
ListMachinePeers(ctx context.Context, tailnetID uint64, key string) (Machines, error)
ListInactiveEphemeralMachines(ctx context.Context, checkpoint time.Time) (Machines, error)
SetMachineLastSeen(ctx context.Context, machineID uint64) error
ExpireMachineByAuthMethod(ctx context.Context, authMethodID uint64) (int64, error)
SaveRegistrationRequest(ctx context.Context, request *RegistrationRequest) error
GetRegistrationRequestByKey(ctx context.Context, key string) (*RegistrationRequest, error)
GetRegistrationRequestByMachineKey(ctx context.Context, key string) (*RegistrationRequest, error)
SaveAuthenticationRequest(ctx context.Context, session *AuthenticationRequest) error
GetAuthenticationRequest(ctx context.Context, key string) (*AuthenticationRequest, error)
DeleteAuthenticationRequest(ctx context.Context, key string) error
Transaction(func(rp Repository) error) error
}
func NewRepository(db *gorm.DB) Repository {
return &repository{
db: db,
db: db,
defaultDERPMap: &derpMapCache{},
}
}
type repository struct {
db *gorm.DB
db *gorm.DB
defaultDERPMap *derpMapCache
}
func (r *repository) withContext(ctx context.Context) *gorm.DB {
@@ -93,3 +94,41 @@ func (r *repository) Transaction(action func(Repository) error) error {
return action(NewRepository(tx))
})
}
type derpMapCache struct {
sync.RWMutex
value *tailcfg.DERPMap
}
func (d *derpMapCache) Get() (*tailcfg.DERPMap, error) {
d.RLock()
if d.value != nil {
d.RUnlock()
return d.value, nil
}
d.RUnlock()
d.Lock()
defer d.Unlock()
getJson := func(url string, target interface{}) error {
c := http.Client{Timeout: 5 * time.Second}
r, err := c.Get(url)
if err != nil {
return err
}
defer r.Body.Close()
return json.NewDecoder(r.Body).Decode(target)
}
m := &tailcfg.DERPMap{}
if err := getJson("https://controlplane.tailscale.com/derpmap/default", m); err != nil {
return nil, err
}
d.value = m
return d.value, nil
}
+2 -29
View File
@@ -6,53 +6,26 @@ import (
"errors"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
type configKey string
const (
controlKeysConfigKey configKey = "control_keys"
derpMapConfigKey configKey = "derp_map"
derpMapConfigKey configKey = "derp_map"
)
type ControlKeys struct {
ControlKey key.MachinePrivate
LegacyControlKey key.MachinePrivate
}
type ServerConfig struct {
Key configKey `gorm:"primary_key"`
Value []byte
}
func (r *repository) GetControlKeys(ctx context.Context) (*ControlKeys, error) {
var m ControlKeys
err := r.getServerConfig(ctx, controlKeysConfigKey, &m)
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
if err != nil {
return nil, err
}
return &m, nil
}
func (r *repository) SetControlKeys(ctx context.Context, v *ControlKeys) error {
return r.setServerConfig(ctx, controlKeysConfigKey, v)
}
func (r *repository) GetDERPMap(ctx context.Context) (*tailcfg.DERPMap, error) {
var m tailcfg.DERPMap
err := r.getServerConfig(ctx, derpMapConfigKey, &m)
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
return r.defaultDERPMap.Get()
}
if err != nil {
+91
View File
@@ -0,0 +1,91 @@
package domain
import (
"context"
"errors"
"fmt"
"github.com/jsiebens/ionscale/internal/util"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"strings"
"time"
)
func CreateSystemApiKey(account *Account, expiresAt *time.Time) (string, *SystemApiKey) {
key := util.RandStringBytes(12)
pwd := util.RandStringBytes(22)
value := fmt.Sprintf("sk_%s_%s", key, pwd)
hash, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
if err != nil {
panic(err)
}
return value, &SystemApiKey{
ID: util.NextID(),
Key: key,
Hash: string(hash),
CreatedAt: time.Now().UTC(),
ExpiresAt: expiresAt,
AccountID: account.ID,
}
}
type SystemApiKey struct {
ID uint64 `gorm:"primary_key"`
Key string
Hash string
CreatedAt time.Time
ExpiresAt *time.Time
AccountID uint64
Account Account
}
func (r *repository) SaveSystemApiKey(ctx context.Context, key *SystemApiKey) error {
tx := r.withContext(ctx).Save(key)
if tx.Error != nil {
return tx.Error
}
return nil
}
func (r *repository) LoadSystemApiKey(ctx context.Context, token string) (*SystemApiKey, error) {
split := strings.Split(token, "_")
if len(split) != 3 {
return nil, nil
}
prefix := split[0]
key := split[1]
value := split[2]
if prefix != "sk" {
return nil, nil
}
var m SystemApiKey
tx := r.withContext(ctx).Preload("Account").First(&m, "key = ?", key)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
if err := bcrypt.CompareHashAndPassword([]byte(m.Hash), []byte(value)); err != nil {
return nil, nil
}
if !m.ExpiresAt.IsZero() && m.ExpiresAt.Before(time.Now()) {
return nil, nil
}
return &m, nil
}
+16 -14
View File
@@ -3,7 +3,9 @@ package domain
import (
"database/sql/driver"
"fmt"
"github.com/hashicorp/go-multierror"
"strings"
"tailscale.com/tailcfg"
)
type Tags []string
@@ -31,21 +33,21 @@ func (i Tags) Value() (driver.Value, error) {
return v, nil
}
func SanitizeTags(input []string) Tags {
keys := make(map[string]bool)
var tags []string
for _, v := range input {
var entry string
if strings.HasPrefix(v, "tag:") {
entry = v[4:]
} else {
entry = v
}
func CheckTag(tag string) error {
return tailcfg.CheckTag(tag)
}
if _, value := keys[entry]; !value {
keys[entry] = true
tags = append(tags, entry)
func CheckTags(tags []string) error {
var result *multierror.Error
for _, t := range tags {
if err := CheckTag(t); err != nil {
result = multierror.Append(result, err)
}
}
return tags
return result.ErrorOrNil()
}
func SanitizeTags(input []string) Tags {
s := StringSet{}
return s.Add(input...).Items()
}
+19 -3
View File
@@ -8,15 +8,31 @@ import (
)
type Tailnet struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string `gorm:"type:varchar(64);unique_index"`
ID uint64 `gorm:"primary_key"`
Name string
DNSConfig DNSConfig
IAMPolicy IAMPolicy
ACLPolicy ACLPolicy
}
func (r *repository) SaveTailnet(ctx context.Context, tailnet *Tailnet) error {
tx := r.withContext(ctx).Save(tailnet)
if tx.Error != nil {
return tx.Error
}
return nil
}
func (r *repository) GetOrCreateTailnet(ctx context.Context, name string) (*Tailnet, bool, error) {
tailnet := &Tailnet{}
id := util.NextID()
tx := r.withContext(ctx).Where(Tailnet{Name: name}).Attrs(Tailnet{ID: id}).FirstOrCreate(tailnet)
tx := r.withContext(ctx).
Where(Tailnet{Name: name}).
Attrs(Tailnet{ID: id, ACLPolicy: DefaultPolicy()}).
FirstOrCreate(tailnet)
if tx.Error != nil {
return nil, false, tx.Error
-103
View File
@@ -1,103 +0,0 @@
package domain
import (
"context"
"encoding/json"
"errors"
"gorm.io/gorm"
)
type TailnetConfig struct {
Key string `gorm:"primary_key"`
TailnetID uint64 `gorm:"primary_key;autoIncrement:false"`
Value []byte
}
type DNSConfig struct {
MagicDNS bool
OverrideLocalDNS bool
Nameservers []string
Routes map[string][]string
}
func (r *repository) GetDNSConfig(ctx context.Context, tailnetID uint64) (*DNSConfig, error) {
var m DNSConfig
err := r.getConfig(ctx, "dns_config", tailnetID, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (r *repository) SetDNSConfig(ctx context.Context, tailnetID uint64, config *DNSConfig) error {
return r.setConfig(ctx, "dns_config", tailnetID, config)
}
func (r *repository) DeleteDNSConfig(ctx context.Context, tailnetID uint64) error {
return r.deleteConfig(ctx, "dns_config", tailnetID)
}
func (r *repository) SetACLPolicy(ctx context.Context, tailnetID uint64, policy *ACLPolicy) error {
if err := r.setConfig(ctx, "acl_policy", tailnetID, policy); err != nil {
return err
}
return nil
}
func (r *repository) GetACLPolicy(ctx context.Context, tailnetID uint64) (*ACLPolicy, error) {
var p = defaultPolicy()
err := r.getConfig(ctx, "acl_policy", tailnetID, &p)
if err != nil {
return nil, err
}
return &p, nil
}
func (r *repository) DeleteACLPolicy(ctx context.Context, tailnetID uint64) error {
return r.deleteConfig(ctx, "acl_policy", tailnetID)
}
func (r *repository) getConfig(ctx context.Context, s string, tailnetID uint64, v interface{}) error {
var m TailnetConfig
tx := r.withContext(ctx).Take(&m, "key = ? AND tailnet_id = ?", s, tailnetID)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil
}
if tx.Error != nil {
return tx.Error
}
err := json.Unmarshal(m.Value, v)
if err != nil {
return err
}
return nil
}
func (r *repository) setConfig(ctx context.Context, s string, tailnetID uint64, v interface{}) error {
marshal, err := json.Marshal(v)
if err != nil {
return err
}
c := &TailnetConfig{
Key: s,
Value: marshal,
TailnetID: tailnetID,
}
tx := r.withContext(ctx).Save(c)
return tx.Error
}
func (r *repository) deleteConfig(ctx context.Context, s string, tailnetID uint64) error {
c := &TailnetConfig{
Key: s,
TailnetID: tailnetID,
}
tx := r.withContext(ctx).Delete(c)
return tx.Error
}
+57 -14
View File
@@ -2,24 +2,47 @@ package domain
import (
"context"
"errors"
"github.com/jsiebens/ionscale/internal/util"
"gorm.io/gorm"
)
type TailnetRole string
type SystemRole string
const (
TailnetRoleService TailnetRole = "service"
TailnetRoleMember TailnetRole = "member"
SystemRoleNone SystemRole = ""
SystemRoleAdmin SystemRole = "admin"
)
func (s SystemRole) IsAdmin() bool {
return s == SystemRoleAdmin
}
type UserType string
const (
UserTypeService UserType = "service"
UserTypePerson UserType = "person"
)
type UserRole string
const (
UserRoleNone UserRole = ""
UserRoleMember UserRole = "member"
UserRoleAdmin UserRole = "admin"
)
func (s UserRole) IsAdmin() bool {
return s == UserRoleAdmin
}
type User struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string
TailnetRole TailnetRole
TailnetID uint64
Tailnet Tailnet
ID uint64 `gorm:"primary_key"`
Name string
UserType UserType
TailnetID uint64
Tailnet Tailnet
AccountID *uint64
Account *Account
}
@@ -30,8 +53,8 @@ func (r *repository) GetOrCreateServiceUser(ctx context.Context, tailnet *Tailne
user := &User{}
id := util.NextID()
query := User{Name: tailnet.Name, TailnetID: tailnet.ID, TailnetRole: TailnetRoleService}
attrs := User{ID: id, Name: tailnet.Name, TailnetID: tailnet.ID, TailnetRole: TailnetRoleService}
query := User{Name: tailnet.Name, TailnetID: tailnet.ID, UserType: UserTypeService}
attrs := User{ID: id, Name: tailnet.Name, TailnetID: tailnet.ID, UserType: UserTypeService}
tx := r.withContext(ctx).Where(query).Attrs(attrs).FirstOrCreate(user)
@@ -45,7 +68,7 @@ func (r *repository) GetOrCreateServiceUser(ctx context.Context, tailnet *Tailne
func (r *repository) ListUsers(ctx context.Context, tailnetID uint64) (Users, error) {
var users = []User{}
tx := r.withContext(ctx).Where("tailnet_id = ?", tailnetID).Find(&users)
tx := r.withContext(ctx).Where("tailnet_id = ? AND user_type = ?", tailnetID, UserTypePerson).Find(&users)
if tx.Error != nil {
return nil, tx.Error
@@ -64,7 +87,7 @@ func (r *repository) GetOrCreateUserWithAccount(ctx context.Context, tailnet *Ta
id := util.NextID()
query := User{AccountID: &account.ID, TailnetID: tailnet.ID}
attrs := User{ID: id, Name: account.LoginName, TailnetID: tailnet.ID, AccountID: &account.ID, TailnetRole: TailnetRoleMember}
attrs := User{ID: id, Name: account.LoginName, TailnetID: tailnet.ID, AccountID: &account.ID, UserType: UserTypePerson}
tx := r.withContext(ctx).Where(query).Attrs(attrs).FirstOrCreate(user)
@@ -74,3 +97,23 @@ func (r *repository) GetOrCreateUserWithAccount(ctx context.Context, tailnet *Ta
return user, user.ID == id, nil
}
func (r *repository) GetUser(ctx context.Context, userID uint64) (*User, error) {
var m User
tx := r.withContext(ctx).Preload("Tailnet").Preload("Account").First(&m, "id = ? and user_type = ?", userID, UserTypePerson)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
return &m, nil
}
func (r *repository) DeleteUser(ctx context.Context, userID uint64) error {
tx := r.withContext(ctx).Delete(&User{ID: userID})
return tx.Error
}
+240 -113
View File
@@ -5,9 +5,9 @@ import (
"encoding/json"
"github.com/jsiebens/ionscale/internal/addr"
"github.com/jsiebens/ionscale/internal/provider"
"github.com/labstack/echo/v4/middleware"
"github.com/mr-tron/base58"
"net/http"
"strconv"
"tailscale.com/tailcfg"
"time"
@@ -15,37 +15,67 @@ import (
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/util"
"github.com/labstack/echo/v4"
"github.com/patrickmn/go-cache"
"tailscale.com/util/dnsname"
)
func NewAuthenticationHandlers(
config *config.Config,
authProvider provider.AuthProvider,
systemIAMPolicy *domain.IAMPolicy,
repository domain.Repository) *AuthenticationHandlers {
return &AuthenticationHandlers{
config: config,
repository: repository,
pendingOAuthUsers: cache.New(5*time.Minute, 10*time.Minute),
config: config,
authProvider: authProvider,
repository: repository,
systemIAMPolicy: systemIAMPolicy,
}
}
type AuthenticationHandlers struct {
repository domain.Repository
config *config.Config
pendingOAuthUsers *cache.Cache
repository domain.Repository
authProvider provider.AuthProvider
config *config.Config
systemIAMPolicy *domain.IAMPolicy
}
type AuthFormData struct {
AuthMethods []domain.AuthMethod
ProviderAvailable bool
Csrf string
}
type TailnetSelectionData struct {
Tailnets []domain.Tailnet
AccountID uint64
Tailnets []domain.Tailnet
SystemAdmin bool
Csrf string
}
type oauthState struct {
Key string
AuthMethod uint64
Key string
Flow string
}
func (h *AuthenticationHandlers) StartCliAuth(c echo.Context) error {
ctx := c.Request().Context()
key := c.Param("key")
if s, err := h.repository.GetAuthenticationRequest(ctx, key); err != nil || s == nil {
return c.Redirect(http.StatusFound, "/a/error")
}
if h.authProvider == nil {
return c.Redirect(http.StatusFound, "/a/error")
}
state, err := h.createState("c", key)
if err != nil {
return err
}
redirectUrl := h.authProvider.GetLoginURL(h.config.CreateUrl("/a/callback"), state)
return c.Redirect(http.StatusFound, redirectUrl)
}
func (h *AuthenticationHandlers) StartAuth(c echo.Context) error {
@@ -56,12 +86,8 @@ func (h *AuthenticationHandlers) StartAuth(c echo.Context) error {
return c.Redirect(http.StatusFound, "/a/error")
}
methods, err := h.repository.ListAuthMethods(ctx)
if err != nil {
return err
}
return c.Render(http.StatusOK, "auth.html", &AuthFormData{AuthMethods: methods})
csrf := c.Get(middleware.DefaultCSRFConfig.ContextKey).(string)
return c.Render(http.StatusOK, "auth.html", &AuthFormData{ProviderAvailable: h.authProvider != nil, Csrf: csrf})
}
func (h *AuthenticationHandlers) ProcessAuth(c echo.Context) error {
@@ -69,7 +95,7 @@ func (h *AuthenticationHandlers) ProcessAuth(c echo.Context) error {
key := c.Param("key")
authKey := c.FormValue("ak")
authMethodId := c.FormValue("s")
interactive := c.FormValue("s")
req, err := h.repository.GetRegistrationRequestByKey(ctx, key)
if err != nil || req == nil {
@@ -80,28 +106,13 @@ func (h *AuthenticationHandlers) ProcessAuth(c echo.Context) error {
return h.endMachineRegistrationFlow(c, req, &oauthState{Key: key})
}
if authMethodId != "" {
id, err := strconv.ParseUint(authMethodId, 10, 64)
if interactive != "" {
state, err := h.createState("r", key)
if err != nil {
return err
}
method, err := h.repository.GetAuthMethod(ctx, id)
if err != nil {
return err
}
state, err := h.createState(key, method.ID)
if err != nil {
return err
}
authProvider, err := provider.NewProvider(method)
if err != nil {
return err
}
redirectUrl := authProvider.GetLoginURL(h.config.CreateUrl("/a/callback"), state)
redirectUrl := h.authProvider.GetLoginURL(h.config.CreateUrl("/a/callback"), state)
return c.Redirect(http.StatusFound, redirectUrl)
}
@@ -118,30 +129,85 @@ func (h *AuthenticationHandlers) Callback(c echo.Context) error {
return err
}
user, err := h.exchangeUser(ctx, code, state)
user, err := h.exchangeUser(code)
if err != nil {
return err
}
filters, err := h.repository.ListAuthFiltersByAuthMethod(ctx, state.AuthMethod)
tailnets, err := h.listAvailableTailnets(ctx, user)
if err != nil {
return err
}
tailnets := filters.Evaluate(user.Attr)
if len(tailnets) == 0 {
return c.Redirect(http.StatusFound, "/a/error?e=ua")
}
account, _, err := h.repository.GetOrCreateAccount(ctx, state.AuthMethod, user.ID, user.Name)
account, _, err := h.repository.GetOrCreateAccount(ctx, user.ID, user.Name)
if err != nil {
return err
}
h.pendingOAuthUsers.Set(state.Key, account, cache.DefaultExpiration)
csrf := c.Get(middleware.DefaultCSRFConfig.ContextKey).(string)
return c.Render(http.StatusOK, "tailnets.html", &TailnetSelectionData{Tailnets: tailnets})
if state.Flow == "r" {
if len(tailnets) == 0 {
registrationRequest, err := h.repository.GetRegistrationRequestByKey(ctx, state.Key)
if err == nil && registrationRequest != nil {
registrationRequest.Error = "unauthorized"
_ = h.repository.SaveRegistrationRequest(ctx, registrationRequest)
}
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 state.Flow == "c" {
isSystemAdmin, err := h.isSystemAdmin(ctx, user)
if err != nil {
return err
}
if !isSystemAdmin && len(tailnets) == 0 {
req, err := h.repository.GetAuthenticationRequest(ctx, state.Key)
if err == nil && req != nil {
req.Error = "unauthorized"
_ = h.repository.SaveAuthenticationRequest(ctx, req)
}
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.Redirect(http.StatusFound, "/a/error")
}
func (h *AuthenticationHandlers) isSystemAdmin(ctx context.Context, u *provider.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 *provider.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 {
@@ -152,12 +218,21 @@ func (h *AuthenticationHandlers) EndOAuth(c echo.Context) error {
return c.Redirect(http.StatusFound, "/a/error")
}
req, err := h.repository.GetRegistrationRequestByKey(ctx, state.Key)
if state.Flow == "r" {
req, err := h.repository.GetRegistrationRequestByKey(ctx, state.Key)
if err != nil || req == nil {
return c.Redirect(http.StatusFound, "/a/error")
}
return h.endMachineRegistrationFlow(c, req, state)
}
req, err := h.repository.GetAuthenticationRequest(ctx, state.Key)
if err != nil || req == nil {
return c.Redirect(http.StatusFound, "/a/error")
}
return h.endMachineRegistrationFlow(c, req, state)
return h.endCliAuthenticationFlow(c, req, state)
}
func (h *AuthenticationHandlers) Success(c echo.Context) error {
@@ -171,15 +246,91 @@ func (h *AuthenticationHandlers) Error(c echo.Context) error {
return c.Render(http.StatusForbidden, "invalidauthkey.html", nil)
case "ua":
return c.Render(http.StatusForbidden, "unauthorized.html", nil)
case "nto":
return c.Render(http.StatusForbidden, "notagowner.html", nil)
}
return c.Render(http.StatusOK, "error.html", nil)
}
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 {
ctx := c.Request().Context()
var form TailnetSelectionForm
if err := c.Bind(&form); err != nil {
return c.Redirect(http.StatusFound, "/a/error")
}
account, err := h.repository.GetAccount(ctx, form.AccountID)
if err != nil {
return c.Redirect(http.StatusFound, "/a/error")
}
// continue as system admin?
if form.AsSystemAdmin {
expiresAt := time.Now().Add(24 * time.Hour)
token, apiKey := domain.CreateSystemApiKey(account, &expiresAt)
req.Token = token
err := h.repository.Transaction(func(rp domain.Repository) error {
if err := rp.SaveSystemApiKey(ctx, apiKey); err != nil {
return err
}
if err := rp.SaveAuthenticationRequest(ctx, req); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return c.Redirect(http.StatusFound, "/a/success")
}
tailnet, err := h.repository.GetTailnet(ctx, form.TailnetID)
if err != nil {
return err
}
user, _, err := h.repository.GetOrCreateUserWithAccount(ctx, tailnet, account)
if err != nil {
return err
}
expiresAt := time.Now().Add(24 * time.Hour)
token, apiKey := domain.CreateApiKey(tailnet, user, &expiresAt)
req.Token = token
req.TailnetID = &tailnet.ID
err = h.repository.Transaction(func(rp domain.Repository) error {
if err := rp.SaveApiKey(ctx, apiKey); err != nil {
return err
}
if err := rp.SaveAuthenticationRequest(ctx, req); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return c.Redirect(http.StatusFound, "/a/success")
}
func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, registrationRequest *domain.RegistrationRequest, state *oauthState) error {
ctx := c.Request().Context()
authKeyParam := c.FormValue("ak")
tailnetIDParam := c.FormValue("s")
var form TailnetSelectionForm
if err := c.Bind(&form); err != nil {
return c.Redirect(http.StatusFound, "/a/error")
}
req := tailcfg.RegisterRequest(registrationRequest.Data)
machineKey := registrationRequest.MachineKey
@@ -190,8 +341,8 @@ func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, regi
var ephemeral bool
var tags = []string{}
if authKeyParam != "" {
authKey, err := h.repository.LoadAuthKey(ctx, authKeyParam)
if form.AuthKey != "" {
authKey, err := h.repository.LoadAuthKey(ctx, form.AuthKey)
if err != nil {
return err
}
@@ -213,30 +364,35 @@ func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, regi
tags = authKey.Tags
ephemeral = authKey.Ephemeral
} else {
parseUint, err := strconv.ParseUint(tailnetIDParam, 10, 64)
if err != nil {
return err
}
tailnet, err = h.repository.GetTailnet(ctx, parseUint)
selectedTailnet, err := h.repository.GetTailnet(ctx, form.TailnetID)
if err != nil {
return err
}
item, ok := h.pendingOAuthUsers.Get(state.Key)
if !ok {
account, err := h.repository.GetAccount(ctx, form.AccountID)
if err != nil {
return c.Redirect(http.StatusFound, "/a/error")
}
oa := item.(*domain.Account)
user, _, err = h.repository.GetOrCreateUserWithAccount(ctx, tailnet, oa)
selectedUser, _, err := h.repository.GetOrCreateUserWithAccount(ctx, selectedTailnet, account)
if err != nil {
return err
}
user = selectedUser
tailnet = selectedTailnet
ephemeral = false
}
if err := tailnet.ACLPolicy.CheckTagOwners(registrationRequest.Data.Hostinfo.RequestTags, user); err != nil {
registrationRequest.Authenticated = false
registrationRequest.Error = err.Error()
if err := h.repository.SaveRegistrationRequest(ctx, registrationRequest); err != nil {
return c.Redirect(http.StatusFound, "/a/error")
}
return c.Redirect(http.StatusFound, "/a/error?e=nto")
}
var m *domain.Machine
m, err := h.repository.GetMachineByKey(ctx, tailnet.ID, machineKey)
@@ -244,20 +400,13 @@ func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, regi
return err
}
if m == nil {
now := time.Now().UTC()
now := time.Now().UTC()
if m == nil {
registeredTags := tags
advertisedTags := domain.SanitizeTags(req.Hostinfo.RequestTags)
tags := append(registeredTags, advertisedTags...)
if len(tags) != 0 {
user, _, err = h.repository.GetOrCreateServiceUser(ctx, tailnet)
if err != nil {
return err
}
}
sanitizeHostname := dnsname.SanitizeHostname(req.Hostinfo.Hostname)
nameIdx, err := h.repository.GetNextMachineNameIndex(ctx, tailnet.ID, sanitizeHostname)
if err != nil {
@@ -265,42 +414,33 @@ func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, regi
}
m = &domain.Machine{
ID: util.NextID(),
Name: sanitizeHostname,
NameIdx: nameIdx,
MachineKey: machineKey,
NodeKey: nodeKey,
Ephemeral: ephemeral,
RegisteredTags: registeredTags,
Tags: domain.SanitizeTags(tags),
CreatedAt: now,
ID: util.NextID(),
Name: sanitizeHostname,
NameIdx: nameIdx,
MachineKey: machineKey,
NodeKey: nodeKey,
Ephemeral: ephemeral,
RegisteredTags: registeredTags,
Tags: domain.SanitizeTags(tags),
CreatedAt: now,
ExpiresAt: now.Add(180 * 24 * time.Hour).UTC(),
KeyExpiryDisabled: len(tags) != 0,
User: *user,
Tailnet: *tailnet,
}
if !req.Expiry.IsZero() {
m.ExpiresAt = &req.Expiry
}
ipv4, ipv6, err := addr.SelectIP(checkIP(ctx, h.repository.CountMachinesWithIPv4))
if err != nil {
return err
}
m.IPv4 = domain.IP{IP: ipv4}
m.IPv6 = domain.IP{IP: ipv6}
m.IPv4 = domain.IP{Addr: ipv4}
m.IPv6 = domain.IP{Addr: ipv6}
} else {
registeredTags := tags
advertisedTags := domain.SanitizeTags(req.Hostinfo.RequestTags)
tags := append(registeredTags, advertisedTags...)
if len(tags) != 0 {
user, _, err = h.repository.GetOrCreateServiceUser(ctx, tailnet)
if err != nil {
return err
}
}
sanitizeHostname := dnsname.SanitizeHostname(req.Hostinfo.Hostname)
if m.Name != sanitizeHostname {
nameIdx, err := h.repository.GetNextMachineNameIndex(ctx, tailnet.ID, sanitizeHostname)
@@ -318,7 +458,7 @@ func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, regi
m.User = *user
m.TailnetID = tailnet.ID
m.Tailnet = *tailnet
m.ExpiresAt = nil
m.ExpiresAt = now.Add(180 * 24 * time.Hour).UTC()
}
err = h.repository.Transaction(func(rp domain.Repository) error {
@@ -343,23 +483,10 @@ func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, regi
return c.Redirect(http.StatusFound, "/a/success")
}
func (h *AuthenticationHandlers) getAuthProvider(ctx context.Context, authMethodId uint64) (provider.AuthProvider, error) {
authMethod, err := h.repository.GetAuthMethod(ctx, authMethodId)
if err != nil {
return nil, err
}
return provider.NewProvider(authMethod)
}
func (h *AuthenticationHandlers) exchangeUser(ctx context.Context, code string, state *oauthState) (*provider.User, error) {
func (h *AuthenticationHandlers) exchangeUser(code string) (*provider.User, error) {
redirectUrl := h.config.CreateUrl("/a/callback")
authProvider, err := h.getAuthProvider(ctx, state.AuthMethod)
if err != nil {
return nil, err
}
user, err := authProvider.Exchange(redirectUrl, code)
user, err := h.authProvider.Exchange(redirectUrl, code)
if err != nil {
return nil, err
}
@@ -367,8 +494,8 @@ func (h *AuthenticationHandlers) exchangeUser(ctx context.Context, code string,
return user, nil
}
func (h *AuthenticationHandlers) createState(key string, authMethodId uint64) (string, error) {
stateMap := oauthState{Key: key, AuthMethod: authMethodId}
func (h *AuthenticationHandlers) createState(flow string, key string) (string, error) {
stateMap := oauthState{Key: key, Flow: flow}
marshal, err := json.Marshal(&stateMap)
if err != nil {
return "", err
+21 -5
View File
@@ -4,12 +4,32 @@ import (
"github.com/caddyserver/certmagic"
"github.com/jsiebens/ionscale/internal/config"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net"
"net/http"
)
func httpsRedirectSkipper(c config.Tls) func(ctx echo.Context) bool {
return func(ctx echo.Context) bool {
if ctx.Request().Method == "POST" && ctx.Request().RequestURI == "/ts2021" {
return true
}
return !c.ForceHttps
}
}
func HttpsRedirect(c config.Tls) echo.MiddlewareFunc {
return middleware.HTTPSRedirectWithConfig(middleware.RedirectConfig{
Skipper: httpsRedirectSkipper(c),
})
}
func HttpRedirectHandler(tls config.Tls) echo.HandlerFunc {
if tls.CertMagicDomain != "" {
if tls.Disable {
return IndexHandler(http.StatusNotFound)
}
if tls.AcmeEnabled {
cfg := certmagic.NewDefault()
if len(cfg.Issuers) > 0 {
if am, ok := cfg.Issuers[0].(*certmagic.ACMEIssuer); ok {
@@ -19,10 +39,6 @@ func HttpRedirectHandler(tls config.Tls) echo.HandlerFunc {
}
}
if tls.Disable {
return IndexHandler(http.StatusNotFound)
}
return echo.WrapHandler(http.HandlerFunc(httpRedirectHandler))
}
+2 -2
View File
@@ -1,7 +1,7 @@
package handlers
import (
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/config"
"github.com/labstack/echo/v4"
"net/http"
"strconv"
@@ -12,7 +12,7 @@ const (
NoiseCapabilityVersion = 28
)
func KeyHandler(keys *domain.ControlKeys) echo.HandlerFunc {
func KeyHandler(keys *config.ServerKeys) echo.HandlerFunc {
legacyPublicKey := keys.LegacyControlKey.Public()
publicKey := keys.ControlKey.Public()
+16
View File
@@ -0,0 +1,16 @@
package handlers
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
const prometheusNamespace = "ionscale"
var (
connectedDevices = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: prometheusNamespace,
Name: "connected_machines_total",
Help: "Total amount of connected machines",
}, []string{"tailnet"})
)
+33 -31
View File
@@ -4,6 +4,7 @@ import (
"context"
"github.com/jsiebens/ionscale/internal/bind"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/mapping"
"github.com/labstack/echo/v4"
@@ -13,19 +14,15 @@ import (
"time"
)
const (
keepAliveInterval = 1 * time.Minute
)
func NewPollNetMapHandler(
createBinder bind.Factory,
brokers *broker.BrokerPool,
brokers broker.Pubsub,
repository domain.Repository,
offlineTimers *OfflineTimers) *PollNetMapHandler {
handler := &PollNetMapHandler{
createBinder: createBinder,
brokers: brokers.Get,
brokers: brokers,
repository: repository,
offlineTimers: offlineTimers,
}
@@ -36,7 +33,7 @@ func NewPollNetMapHandler(
type PollNetMapHandler struct {
createBinder bind.Factory
repository domain.Repository
brokers func(uint64) broker.Broker
brokers broker.Pubsub
offlineTimers *OfflineTimers
}
@@ -89,8 +86,7 @@ func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *
tailnetID := m.TailnetID
machineID := m.ID
tailnetBroker := h.brokers(tailnetID)
tailnetBroker.SignalPeerUpdated(machineID)
h.brokers.Publish(tailnetID, &broker.Signal{PeerUpdated: &machineID})
if !mapRequest.Stream {
return c.String(http.StatusOK, "")
@@ -104,9 +100,11 @@ func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *
}
updateChan := make(chan *broker.Signal, 20)
client := broker.NewClient(machineID, updateChan)
tailnetBroker.AddClient(&client)
unsubscribe, err := h.brokers.Subscribe(tailnetID, updateChan)
if err != nil {
return err
}
h.cancelOfflineMessage(machineID)
// Listen to connection close
@@ -116,7 +114,7 @@ func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *
if err != nil {
return err
}
keepAliveTicker := time.NewTicker(keepAliveInterval)
keepAliveTicker := time.NewTicker(config.KeepAliveInterval)
syncTicker := time.NewTicker(5 * time.Second)
c.Response().WriteHeader(http.StatusOK)
@@ -126,8 +124,11 @@ func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *
}
c.Response().Flush()
connectedDevices.WithLabelValues(m.Tailnet.Name).Inc()
defer func() {
tailnetBroker.RemoveClient(machineID)
connectedDevices.WithLabelValues(m.Tailnet.Name).Dec()
unsubscribe()
keepAliveTicker.Stop()
syncTicker.Stop()
_ = h.repository.SetMachineLastSeen(ctx, machineID)
@@ -219,16 +220,17 @@ func (h *PollNetMapHandler) createKeepAliveResponse(binder bind.Binder, request
func (h *PollNetMapHandler) createMapResponse(m *domain.Machine, binder bind.Binder, request *tailcfg.MapRequest, delta bool, prevSyncedPeerIDs map[uint64]bool) ([]byte, map[uint64]bool, error) {
ctx := context.TODO()
node, user, err := mapping.ToNode(m, true)
node, user, err := mapping.ToNode(m)
if err != nil {
return nil, nil, err
}
policies, err := h.repository.GetACLPolicy(ctx, m.TailnetID)
tailnet, err := h.repository.GetTailnet(ctx, m.TailnetID)
if err != nil {
return nil, nil, err
}
policies := tailnet.ACLPolicy
var users = []tailcfg.UserProfile{*user}
var changedPeers []*tailcfg.Node
var removedPeers []tailcfg.NodeID
@@ -245,8 +247,8 @@ func (h *PollNetMapHandler) createMapResponse(m *domain.Machine, binder bind.Bin
if peer.IsExpired() {
continue
}
if domain.IsValidPeer(policies, m, &peer) || domain.IsValidPeer(policies, &peer, m) {
n, u, err := mapping.ToNode(&peer, h.brokers(peer.TailnetID).IsConnected(peer.ID))
if policies.IsValidPeer(m, &peer) || policies.IsValidPeer(&peer, m) {
n, u, err := mapping.ToNode(&peer)
if err != nil {
return nil, nil, err
}
@@ -265,17 +267,14 @@ func (h *PollNetMapHandler) createMapResponse(m *domain.Machine, binder bind.Bin
removedPeers = append(removedPeers, tailcfg.NodeID(p))
}
dnsConfig, err := h.repository.GetDNSConfig(ctx, m.TailnetID)
if err != nil {
return nil, nil, err
}
dnsConfig := tailnet.DNSConfig
derpMap, err := h.repository.GetDERPMap(ctx)
if err != nil {
return nil, nil, err
}
rules := domain.BuildFilterRules(policies, m, candidatePeers)
rules := policies.BuildFilterRules(candidatePeers, m)
controlTime := time.Now().UTC()
var mapResponse *tailcfg.MapResponse
@@ -284,19 +283,24 @@ func (h *PollNetMapHandler) createMapResponse(m *domain.Machine, binder bind.Bin
mapResponse = &tailcfg.MapResponse{
KeepAlive: false,
Node: node,
DNSConfig: mapping.ToDNSConfig(&m.Tailnet, dnsConfig),
DNSConfig: mapping.ToDNSConfig(&m.Tailnet, &dnsConfig),
PacketFilter: rules,
DERPMap: derpMap,
Domain: dnsname.SanitizeHostname(m.Tailnet.Name),
Peers: changedPeers,
UserProfiles: users,
ControlTime: &controlTime,
Debug: &tailcfg.Debug{
DisableLogTail: true,
},
}
} else {
mapResponse = &tailcfg.MapResponse{
Node: node,
DNSConfig: mapping.ToDNSConfig(&m.Tailnet, &dnsConfig),
PacketFilter: rules,
DERPMap: derpMap,
Domain: dnsname.SanitizeHostname(m.Tailnet.Name),
PeersChanged: changedPeers,
PeersRemoved: removedPeers,
UserProfiles: users,
@@ -315,10 +319,10 @@ func (h *PollNetMapHandler) createMapResponse(m *domain.Machine, binder bind.Bin
return payload, syncedPeerIDs, nil
}
func NewOfflineTimers(repository domain.Repository, brokers *broker.BrokerPool) *OfflineTimers {
func NewOfflineTimers(repository domain.Repository, pubsub broker.Pubsub) *OfflineTimers {
return &OfflineTimers{
repository: repository,
brokers: brokers.Get,
pubsub: pubsub,
data: make(map[uint64]*time.Timer),
startCh: make(chan [2]uint64),
stopCh: make(chan uint64),
@@ -327,7 +331,7 @@ func NewOfflineTimers(repository domain.Repository, brokers *broker.BrokerPool)
type OfflineTimers struct {
repository domain.Repository
brokers func(uint64) broker.Broker
pubsub broker.Pubsub
data map[uint64]*time.Timer
stopCh chan uint64
startCh chan [2]uint64
@@ -351,13 +355,11 @@ func (o *OfflineTimers) scheduleOfflineMessage(tailnetID, machineID uint64) {
delete(o.data, machineID)
}
timer := time.NewTimer(10 * time.Second)
timer := time.NewTimer(config.KeepAliveInterval)
go func() {
<-timer.C
if !o.brokers(tailnetID).IsConnected(machineID) {
o.brokers(tailnetID).SignalPeerUpdated(machineID)
o.stopCh <- machineID
}
o.pubsub.Publish(tailnetID, &broker.Signal{PeerUpdated: &machineID})
o.stopCh <- machineID
}()
o.data[machineID] = timer
+4 -4
View File
@@ -12,15 +12,15 @@ const (
inactivityTimeout = 30 * time.Minute
)
func NewReaper(brokers *broker.BrokerPool, repository domain.Repository) *Reaper {
func NewReaper(brokers broker.Pubsub, repository domain.Repository) *Reaper {
return &Reaper{
brokers: brokers,
pubsub: brokers,
repository: repository,
}
}
type Reaper struct {
brokers *broker.BrokerPool
pubsub broker.Pubsub
repository domain.Repository
}
@@ -55,7 +55,7 @@ func (r *Reaper) reapInactiveEphemeralNodes() {
if len(removedNodes) != 0 {
for i, p := range removedNodes {
r.brokers.Get(i).SignalPeersRemoved(p)
r.pubsub.Publish(i, &broker.Signal{PeersRemoved: p})
}
}
}
+30 -23
View File
@@ -9,8 +9,8 @@ import (
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/util"
"github.com/labstack/echo/v4"
"inet.af/netaddr"
"net/http"
"net/netip"
"tailscale.com/tailcfg"
"tailscale.com/util/dnsname"
"time"
@@ -19,11 +19,11 @@ import (
func NewRegistrationHandlers(
createBinder bind.Factory,
config *config.Config,
brokers *broker.BrokerPool,
brokers broker.Pubsub,
repository domain.Repository) *RegistrationHandlers {
return &RegistrationHandlers{
createBinder: createBinder,
brokers: brokers.Get,
pubsub: brokers,
repository: repository,
config: config,
}
@@ -32,7 +32,7 @@ func NewRegistrationHandlers(
type RegistrationHandlers struct {
createBinder bind.Factory
repository domain.Repository
brokers func(uint64) broker.Broker
pubsub broker.Pubsub
config *config.Config
}
@@ -60,19 +60,19 @@ func (h *RegistrationHandlers) Register(c echo.Context) error {
}
if m != nil {
if m.ExpiresAt != nil && !m.ExpiresAt.IsZero() && m.ExpiresAt.Before(time.Now()) {
if m.IsExpired() {
response := tailcfg.RegisterResponse{NodeKeyExpired: true}
return binder.WriteResponse(c, http.StatusOK, response)
}
if !req.Expiry.IsZero() && req.Expiry.Before(time.Now()) {
m.ExpiresAt = &req.Expiry
m.ExpiresAt = req.Expiry
if err := h.repository.SaveMachine(ctx, m); err != nil {
return err
}
h.brokers(m.TailnetID).SignalPeerUpdated(m.ID)
h.pubsub.Publish(m.TailnetID, &broker.Signal{PeerUpdated: &m.ID})
response := tailcfg.RegisterResponse{NodeKeyExpired: true}
return binder.WriteResponse(c, http.StatusOK, response)
@@ -151,6 +151,11 @@ func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, bi
tailnet := authKey.Tailnet
user := authKey.User
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)
}
var m *domain.Machine
m, err = h.repository.GetMachineByKey(ctx, tailnet.ID, machineKey)
@@ -158,9 +163,9 @@ func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, bi
return err
}
if m == nil {
now := time.Now().UTC()
now := time.Now().UTC()
if m == nil {
registeredTags := authKey.Tags
advertisedTags := domain.SanitizeTags(req.Hostinfo.RequestTags)
tags := append(registeredTags, advertisedTags...)
@@ -172,30 +177,32 @@ func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, bi
}
m = &domain.Machine{
ID: util.NextID(),
Name: sanitizeHostname,
NameIdx: nameIdx,
MachineKey: machineKey,
NodeKey: nodeKey,
Ephemeral: authKey.Ephemeral,
RegisteredTags: registeredTags,
Tags: domain.SanitizeTags(tags),
CreatedAt: now,
ID: util.NextID(),
Name: sanitizeHostname,
NameIdx: nameIdx,
MachineKey: machineKey,
NodeKey: nodeKey,
Ephemeral: authKey.Ephemeral,
RegisteredTags: registeredTags,
Tags: domain.SanitizeTags(tags),
CreatedAt: now,
ExpiresAt: now.Add(180 * 24 * time.Hour).UTC(),
KeyExpiryDisabled: len(tags) != 0,
User: user,
Tailnet: tailnet,
}
if !req.Expiry.IsZero() {
m.ExpiresAt = &req.Expiry
m.ExpiresAt = req.Expiry
}
ipv4, ipv6, err := addr.SelectIP(checkIP(ctx, h.repository.CountMachinesWithIPv4))
if err != nil {
return err
}
m.IPv4 = domain.IP{IP: ipv4}
m.IPv6 = domain.IP{IP: ipv6}
m.IPv4 = domain.IP{Addr: ipv4}
m.IPv6 = domain.IP{Addr: ipv6}
} else {
registeredTags := authKey.Tags
advertisedTags := domain.SanitizeTags(req.Hostinfo.RequestTags)
@@ -218,7 +225,7 @@ func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, bi
m.User = user
m.TailnetID = tailnet.ID
m.Tailnet = tailnet
m.ExpiresAt = nil
m.ExpiresAt = now.Add(180 * 24 * time.Hour).UTC()
}
if err := h.repository.SaveMachine(ctx, m); err != nil {
@@ -260,7 +267,7 @@ func (h *RegistrationHandlers) followup(c echo.Context, binder bind.Binder, req
}
func checkIP(cxt context.Context, s Selector) addr.Predicate {
return func(ip netaddr.IP) (bool, error) {
return func(ip netip.Addr) (bool, error) {
c, err := s(cxt, ip.String())
if err != nil {
return false, err
+21 -14
View File
@@ -3,14 +3,16 @@ package mapping
import (
"encoding/json"
"fmt"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/util"
"inet.af/netaddr"
"net/netip"
"strconv"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
"tailscale.com/types/key"
"tailscale.com/util/dnsname"
"time"
)
const NetworkMagicDNSSuffix = "ionscale.net"
@@ -30,9 +32,9 @@ func CopyViaJson[F any, T any](f F, t T) error {
func ToDNSConfig(tailnet *domain.Tailnet, c *domain.DNSConfig) *tailcfg.DNSConfig {
tailnetDomain := dnsname.SanitizeHostname(tailnet.Name)
resolvers := []dnstype.Resolver{}
resolvers := []*dnstype.Resolver{}
for _, r := range c.Nameservers {
resolver := dnstype.Resolver{
resolver := &dnstype.Resolver{
Addr: r,
}
resolvers = append(resolvers, resolver)
@@ -54,11 +56,11 @@ func ToDNSConfig(tailnet *domain.Tailnet, c *domain.DNSConfig) *tailcfg.DNSConfi
}
if len(c.Routes) != 0 {
routes := make(map[string][]dnstype.Resolver)
routes := make(map[string][]*dnstype.Resolver)
for r, s := range c.Routes {
routeResolver := []dnstype.Resolver{}
routeResolver := []*dnstype.Resolver{}
for _, addr := range s {
resolver := dnstype.Resolver{Addr: addr}
resolver := &dnstype.Resolver{Addr: addr}
routeResolver = append(routeResolver, resolver)
}
routes[r] = routeResolver
@@ -72,7 +74,7 @@ func ToDNSConfig(tailnet *domain.Tailnet, c *domain.DNSConfig) *tailcfg.DNSConfi
return config
}
func ToNode(m *domain.Machine, connected bool) (*tailcfg.Node, *tailcfg.UserProfile, error) {
func ToNode(m *domain.Machine) (*tailcfg.Node, *tailcfg.UserProfile, error) {
nKey, err := util.ParseNodePublicKey(m.NodeKey)
if err != nil {
return nil, nil, err
@@ -95,10 +97,10 @@ func ToNode(m *domain.Machine, connected bool) (*tailcfg.Node, *tailcfg.UserProf
endpoints := m.Endpoints
hostinfo := tailcfg.Hostinfo(m.HostInfo)
var addrs []netaddr.IPPrefix
var allowedIPs []netaddr.IPPrefix
var addrs []netip.Prefix
var allowedIPs []netip.Prefix
if !m.IPv4.IsZero() {
if m.IPv4.IsValid() {
ipv4, err := m.IPv4.Prefix(32)
if err != nil {
return nil, nil, err
@@ -107,7 +109,7 @@ func ToNode(m *domain.Machine, connected bool) (*tailcfg.Node, *tailcfg.UserProf
allowedIPs = append(allowedIPs, ipv4)
}
if !m.IPv6.IsZero() {
if m.IPv6.IsValid() {
ipv6, err := m.IPv6.Prefix(128)
if err != nil {
return nil, nil, err
@@ -158,15 +160,20 @@ func ToNode(m *domain.Machine, connected bool) (*tailcfg.Node, *tailcfg.UserProf
User: tailcfg.UserID(m.UserID),
}
if m.ExpiresAt != nil {
if !m.ExpiresAt.IsZero() {
e := m.ExpiresAt.UTC()
n.KeyExpiry = e
}
n.Online = &connected
if !connected && m.LastSeen != nil {
if m.KeyExpiryDisabled {
n.KeyExpiry = time.Time{}
}
if m.LastSeen != nil {
l := m.LastSeen.UTC()
online := m.LastSeen.After(time.Now().Add(-config.KeepAliveInterval))
n.LastSeen = &l
n.Online = &online
}
var user = ToUserProfile(m.User)
+7 -4
View File
@@ -3,31 +3,34 @@ package provider
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/internal/config"
"strings"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/jsiebens/ionscale/internal/domain"
"golang.org/x/oauth2"
)
type OIDCProvider struct {
clientID string
clientSecret string
scopes []string
provider *oidc.Provider
verifier *oidc.IDTokenVerifier
}
func NewOIDCProvider(c *domain.AuthMethod) (*OIDCProvider, error) {
func NewOIDCProvider(c *config.AuthProvider) (*OIDCProvider, error) {
defaultScopes := []string{oidc.ScopeOpenID, "email", "profile"}
provider, err := oidc.NewProvider(context.Background(), c.Issuer)
if err != nil {
return nil, err
}
verifier := provider.Verifier(&oidc.Config{ClientID: c.ClientId})
verifier := provider.Verifier(&oidc.Config{ClientID: c.ClientID, SkipClientIDCheck: c.ClientID == ""})
return &OIDCProvider{
clientID: c.ClientId,
clientID: c.ClientID,
clientSecret: c.ClientSecret,
scopes: append(defaultScopes, c.Scopes...),
provider: provider,
verifier: verifier,
}, nil
-15
View File
@@ -1,11 +1,5 @@
package provider
import (
"fmt"
"github.com/jsiebens/ionscale/internal/domain"
)
type AuthProvider interface {
GetLoginURL(redirectURI, state string) string
Exchange(redirectURI, code string) (*User, error)
@@ -16,12 +10,3 @@ type User struct {
Name string
Attr map[string]interface{}
}
func NewProvider(m *domain.AuthMethod) (AuthProvider, error) {
switch m.Type {
case "oidc":
return NewOIDCProvider(m)
default:
return nil, fmt.Errorf("unknown auth method type")
}
}
-54
View File
@@ -1,54 +0,0 @@
package server
import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
"github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/hashicorp/go-hclog"
"github.com/jsiebens/ionscale/internal/key"
"github.com/jsiebens/ionscale/internal/service"
"google.golang.org/grpc"
)
func init() {
grpc_prometheus.EnableHandlingTimeHistogram()
}
func NewGrpcServer(logger hclog.Logger, systemAdminKey key.ServerPrivate) *grpc.Server {
return grpc.NewServer(
middleware.WithUnaryServerChain(
logging.UnaryServerInterceptor(
&grpcLogger{logger.Named("grpc")},
logging.WithDurationField(logging.DurationToDurationField),
),
grpc_prometheus.UnaryServerInterceptor,
recovery.UnaryServerInterceptor(),
service.UnaryServerTokenAuth(systemAdminKey),
),
)
}
type grpcLogger struct {
log hclog.Logger
}
func (l *grpcLogger) Log(lvl logging.Level, msg string) {
switch lvl {
case logging.ERROR:
l.log.Error(msg)
default:
l.log.Debug(msg)
}
}
func (l *grpcLogger) With(fields ...string) logging.Logger {
if len(fields) == 0 {
return l
}
vals := make([]interface{}, 0, len(fields))
for i := 0; i < len(fields); i++ {
vals = append(vals, fields[i])
}
return &grpcLogger{log: l.log.With(vals...)}
}
+15
View File
@@ -0,0 +1,15 @@
package server
import (
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/key"
"github.com/jsiebens/ionscale/internal/service"
apiconnect "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1/ionscalev1connect"
"net/http"
)
func NewRpcHandler(systemAdminKey *key.ServerPrivate, repository domain.Repository, handler apiconnect.IonscaleServiceHandler) (string, http.Handler) {
interceptors := connect.WithInterceptors(service.AuthenticationInterceptor(systemAdminKey, repository))
return apiconnect.NewIonscaleServiceHandler(handler, interceptors)
}
+83 -66
View File
@@ -6,79 +6,78 @@ import (
"fmt"
"github.com/caddyserver/certmagic"
"github.com/hashicorp/go-hclog"
"github.com/improbable-eng/grpc-web/go/grpcweb"
"github.com/jsiebens/ionscale/internal/bind"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/database"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/handlers"
"github.com/jsiebens/ionscale/internal/provider"
"github.com/jsiebens/ionscale/internal/service"
"github.com/jsiebens/ionscale/internal/templates"
"github.com/jsiebens/ionscale/pkg/gen/api"
echo_prometheus "github.com/labstack/echo-contrib/prometheus"
"github.com/labstack/echo/v4"
"github.com/soheilhy/cmux"
"github.com/labstack/echo/v4/middleware"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"golang.org/x/sync/errgroup"
"log"
"net"
"net/http"
"net/url"
"os"
"strings"
"tailscale.com/types/key"
)
func Start(config *config.Config) error {
logger, err := setupLogging(config.Logging)
func Start(c *config.Config) error {
logger, err := setupLogging(c.Logging)
if err != nil {
return err
}
logger.Info("Starting ionscale server")
serverKey, err := config.ReadServerKeys()
serverKey, err := c.ReadServerKeys()
if err != nil {
return err
}
_, repository, err := database.OpenDB(&config.Database, logger)
repository, brokers, err := database.OpenDB(&c.Database, logger)
if err != nil {
return err
}
controlKeys, err := repository.GetControlKeys(context.Background())
if err != nil {
return err
}
brokers := broker.NewBrokerPool()
offlineTimers := handlers.NewOfflineTimers(repository, brokers)
reaper := handlers.NewReaper(brokers, repository)
go offlineTimers.Start()
go reaper.Start()
serverUrl, err := url.Parse(c.ServerUrl)
if err != nil {
return err
}
// prepare CertMagic
if config.Tls.CertMagicDomain != "" {
if c.Tls.AcmeEnabled {
certmagic.DefaultACME.Agreed = true
certmagic.DefaultACME.Email = config.Tls.CertMagicEmail
certmagic.DefaultACME.CA = config.Tls.CertMagicCA
if config.Tls.CertMagicStoragePath != "" {
certmagic.Default.Storage = &certmagic.FileStorage{Path: config.Tls.CertMagicStoragePath}
certmagic.DefaultACME.Email = c.Tls.AcmeEmail
certmagic.DefaultACME.CA = c.Tls.AcmeCA
if c.Tls.AcmePath != "" {
certmagic.Default.Storage = &certmagic.FileStorage{Path: c.Tls.AcmePath}
}
cfg := certmagic.NewDefault()
if err := cfg.ManageAsync(context.Background(), []string{config.Tls.CertMagicDomain}); err != nil {
if err := cfg.ManageAsync(context.Background(), []string{serverUrl.Host}); err != nil {
return err
}
config.HttpListenAddr = fmt.Sprintf(":%d", certmagic.HTTPPort)
config.HttpsListenAddr = fmt.Sprintf(":%d", certmagic.HTTPSPort)
c.HttpListenAddr = fmt.Sprintf(":%d", certmagic.HTTPPort)
c.HttpsListenAddr = fmt.Sprintf(":%d", certmagic.HTTPSPort)
}
createPeerHandler := func(p key.MachinePublic) http.Handler {
registrationHandlers := handlers.NewRegistrationHandlers(bind.DefaultBinder(p), config, brokers, repository)
registrationHandlers := handlers.NewRegistrationHandlers(bind.DefaultBinder(p), c, brokers, repository)
pollNetMapHandler := handlers.NewPollNetMapHandler(bind.DefaultBinder(p), brokers, repository, offlineTimers)
e := echo.New()
@@ -90,14 +89,24 @@ func Start(config *config.Config) error {
return e
}
noiseHandlers := handlers.NewNoiseHandlers(controlKeys.ControlKey, createPeerHandler)
registrationHandlers := handlers.NewRegistrationHandlers(bind.BoxBinder(controlKeys.LegacyControlKey), config, brokers, repository)
pollNetMapHandler := handlers.NewPollNetMapHandler(bind.BoxBinder(controlKeys.LegacyControlKey), brokers, repository, offlineTimers)
authProvider, systemIAMPolicy, err := setupAuthProvider(c.AuthProvider)
if err != nil {
return err
}
noiseHandlers := handlers.NewNoiseHandlers(serverKey.ControlKey, createPeerHandler)
registrationHandlers := handlers.NewRegistrationHandlers(bind.BoxBinder(serverKey.LegacyControlKey), c, brokers, repository)
pollNetMapHandler := handlers.NewPollNetMapHandler(bind.BoxBinder(serverKey.LegacyControlKey), brokers, repository, offlineTimers)
authenticationHandlers := handlers.NewAuthenticationHandlers(
config,
c,
authProvider,
systemIAMPolicy,
repository,
)
rpcService := service.NewService(c, authProvider, repository, brokers)
rpcPath, rpcHandler := NewRpcHandler(serverKey.SystemAdminKey, repository, rpcService)
p := echo_prometheus.NewPrometheus("http", nil)
metricsHandler := echo.New()
@@ -108,9 +117,10 @@ func Start(config *config.Config) error {
nonTlsAppHandler.Use(EchoLogger(logger))
nonTlsAppHandler.Use(p.HandlerFunc)
nonTlsAppHandler.POST("/ts2021", noiseHandlers.Upgrade)
nonTlsAppHandler.Any("/*", handlers.HttpRedirectHandler(config.Tls))
nonTlsAppHandler.Any("/*", handlers.HttpRedirectHandler(c.Tls))
tlsAppHandler := echo.New()
tlsAppHandler.Pre(handlers.HttpsRedirect(c.Tls))
tlsAppHandler.Renderer = templates.NewTemplates()
tlsAppHandler.Use(EchoRecover(logger))
tlsAppHandler.Use(EchoLogger(logger))
@@ -118,99 +128,106 @@ func Start(config *config.Config) error {
tlsAppHandler.Any("/*", handlers.IndexHandler(http.StatusNotFound))
tlsAppHandler.Any("/", handlers.IndexHandler(http.StatusOK))
tlsAppHandler.POST(rpcPath+"*", echo.WrapHandler(rpcHandler))
tlsAppHandler.GET("/version", handlers.Version)
tlsAppHandler.GET("/key", handlers.KeyHandler(controlKeys))
tlsAppHandler.GET("/key", handlers.KeyHandler(serverKey))
tlsAppHandler.POST("/ts2021", noiseHandlers.Upgrade)
tlsAppHandler.POST("/machine/:id", registrationHandlers.Register)
tlsAppHandler.POST("/machine/:id/map", pollNetMapHandler.PollNetMap)
auth := tlsAppHandler.Group("/a")
auth.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
TokenLookup: "form:_csrf",
}))
auth.GET("/:key", authenticationHandlers.StartAuth)
auth.POST("/:key", authenticationHandlers.ProcessAuth)
auth.GET("/c/:key", authenticationHandlers.StartCliAuth)
auth.GET("/callback", authenticationHandlers.Callback)
auth.POST("/callback", authenticationHandlers.EndOAuth)
auth.GET("/success", authenticationHandlers.Success)
auth.GET("/error", authenticationHandlers.Error)
grpcService := service.NewService(repository, brokers)
grpcServer := NewGrpcServer(logger, serverKey.SystemAdminKey)
api.RegisterIonscaleServer(grpcServer, grpcService)
tlsL, err := tlsListener(config)
tlsL, err := tlsListener(c)
if err != nil {
return err
}
nonTlsL, err := nonTlsListener(config)
nonTlsL, err := nonTlsListener(c)
if err != nil {
return err
}
metricsL, err := metricsListener(config)
metricsL, err := metricsListener(c)
if err != nil {
return err
}
mux := cmux.New(selectListener(tlsL, nonTlsL))
grpcL := mux.MatchWithWriters(
cmux.HTTP2MatchHeaderFieldPrefixSendSettings("content-type", "application/grpc"),
cmux.HTTP2MatchHeaderFieldPrefixSendSettings("content-type", "application/grpc+proto"),
)
grpcWebL := mux.Match(cmux.HTTP1HeaderFieldPrefix("content-type", "application/grpc-web"))
httpL := mux.Match(cmux.Any())
grpcWebHandler := grpcweb.WrapServer(grpcServer)
httpL := selectListener(tlsL, nonTlsL)
http2Server := &http2.Server{}
g := new(errgroup.Group)
g.Go(func() error { return grpcServer.Serve(grpcL) })
g.Go(func() error { return http.Serve(grpcWebL, h2c.NewHandler(grpcWebHandler, http2Server)) })
g.Go(func() error { return http.Serve(httpL, h2c.NewHandler(tlsAppHandler, http2Server)) })
g.Go(func() error { return http.Serve(metricsL, metricsHandler) })
g.Go(func() error { return mux.Serve() })
if tlsL != nil {
g.Go(func() error { return http.Serve(nonTlsL, nonTlsAppHandler) })
}
if config.Tls.CertMagicDomain != "" {
logger.Info("TLS is enabled with CertMagic", "domain", config.Tls.CertMagicDomain)
logger.Info("Server is running", "http_addr", config.HttpListenAddr, "https_addr", config.HttpsListenAddr, "metrics_addr", config.MetricsListenAddr)
} else if !config.Tls.Disable {
logger.Info("TLS is enabled", "cert", config.Tls.CertFile)
logger.Info("Server is running", "http_addr", config.HttpListenAddr, "https_addr", config.HttpsListenAddr, "metrics_addr", config.MetricsListenAddr)
if c.Tls.AcmeEnabled {
logger.Info("TLS is enabled with ACME", "domain", serverUrl.Host)
logger.Info("Server is running", "http_addr", c.HttpListenAddr, "https_addr", c.HttpsListenAddr, "metrics_addr", c.MetricsListenAddr)
} else if !c.Tls.Disable {
logger.Info("TLS is enabled", "cert", c.Tls.CertFile)
logger.Info("Server is running", "http_addr", c.HttpListenAddr, "https_addr", c.HttpsListenAddr, "metrics_addr", c.MetricsListenAddr)
} else {
logger.Warn("TLS is disabled")
logger.Info("Server is running", "http_addr", config.HttpListenAddr, "metrics_addr", config.MetricsListenAddr)
logger.Info("Server is running", "http_addr", c.HttpListenAddr, "metrics_addr", c.MetricsListenAddr)
}
return g.Wait()
}
func setupAuthProvider(config config.AuthProvider) (provider.AuthProvider, *domain.IAMPolicy, error) {
if len(config.Issuer) == 0 {
return nil, &domain.IAMPolicy{}, nil
}
authProvider, err := provider.NewOIDCProvider(&config)
if err != nil {
return nil, nil, err
}
return authProvider, &domain.IAMPolicy{
Subs: config.SystemAdminPolicy.Subs,
Emails: config.SystemAdminPolicy.Emails,
Filters: config.SystemAdminPolicy.Filters,
}, nil
}
func metricsListener(config *config.Config) (net.Listener, error) {
return net.Listen("tcp", config.MetricsListenAddr)
}
func tlsListener(config *config.Config) (net.Listener, error) {
if config.Tls.CertMagicDomain != "" {
if config.Tls.Disable {
return nil, nil
}
if config.Tls.AcmeEnabled {
cfg := certmagic.NewDefault()
tlsConfig := cfg.TLSConfig()
tlsConfig.NextProtos = append([]string{"h2", "http/1.1"}, tlsConfig.NextProtos...)
return tls.Listen("tcp", config.HttpsListenAddr, tlsConfig)
}
if !config.Tls.Disable {
cer, err := tls.LoadX509KeyPair(config.Tls.CertFile, config.Tls.KeyFile)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{Certificates: []tls.Certificate{cer}}
return tls.Listen("tcp", config.HttpsListenAddr, tlsConfig)
cer, err := tls.LoadX509KeyPair(config.Tls.CertFile, config.Tls.KeyFile)
if err != nil {
return nil, err
}
return nil, nil
tlsConfig := &tls.Config{Certificates: []tls.Certificate{cer}}
return tls.Listen("tcp", config.HttpsListenAddr, tlsConfig)
}
func nonTlsListener(config *config.Config) (net.Listener, error) {
+39 -23
View File
@@ -2,46 +2,62 @@ package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/jsiebens/ionscale/internal/mapping"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
)
func (s *Service) GetACLPolicy(ctx context.Context, req *api.GetACLPolicyRequest) (*api.GetACLPolicyResponse, error) {
policy, err := s.repository.GetACLPolicy(ctx, req.TailnetId)
if err != nil {
return nil, err
func (s *Service) GetACLPolicy(ctx context.Context, req *connect.Request[api.GetACLPolicyRequest]) (*connect.Response[api.GetACLPolicyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
marshal, err := json.Marshal(policy)
if err != nil {
return nil, err
}
return &api.GetACLPolicyResponse{Value: marshal}, nil
}
func (s *Service) SetACLPolicy(ctx context.Context, req *api.SetACLPolicyRequest) (*api.SetACLPolicyResponse, error) {
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "tailnet does not exist")
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("tailnet does not exist"))
}
var policy api.ACLPolicy
if err := mapping.CopyViaJson(&tailnet.ACLPolicy, &policy); err != nil {
return nil, err
}
return connect.NewResponse(&api.GetACLPolicyResponse{Policy: &policy}), nil
}
func (s *Service) SetACLPolicy(ctx context.Context, req *connect.Request[api.SetACLPolicyRequest]) (*connect.Response[api.SetACLPolicyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("tailnet does not exist"))
}
var policy domain.ACLPolicy
if err := json.Unmarshal(req.Value, &policy); err != nil {
if err := mapping.CopyViaJson(req.Msg.Policy, &policy); err != nil {
return nil, err
}
if err := s.repository.SetACLPolicy(ctx, tailnet.ID, &policy); err != nil {
tailnet.ACLPolicy = policy
if err := s.repository.SaveTailnet(ctx, tailnet); err != nil {
return nil, err
}
s.brokers(tailnet.ID).SignalACLUpdated()
s.pubsub.Publish(tailnet.ID, &broker.Signal{ACLUpdated: true})
return &api.SetACLPolicyResponse{}, nil
return connect.NewResponse(&api.SetACLPolicyResponse{}), nil
}
+70
View File
@@ -0,0 +1,70 @@
package service
import (
"context"
"errors"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/util"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"time"
)
func (s *Service) Authenticate(ctx context.Context, req *connect.Request[api.AuthenticationRequest], stream *connect.ServerStream[api.AuthenticationResponse]) error {
if s.authProvider == nil {
return connect.NewError(connect.CodeFailedPrecondition, errors.New("no authentication method available, contact your ionscale administrator for more information"))
}
key := util.RandStringBytes(8)
authUrl := s.config.CreateUrl("/a/c/%s", key)
session := &domain.AuthenticationRequest{
Key: key,
CreatedAt: time.Now().UTC(),
}
if err := s.repository.SaveAuthenticationRequest(ctx, session); err != nil {
return err
}
if err := stream.Send(&api.AuthenticationResponse{AuthUrl: authUrl}); err != nil {
return err
}
notify := ctx.Done()
tick := time.NewTicker(1 * time.Second)
defer func() {
tick.Stop()
_ = s.repository.DeleteAuthenticationRequest(context.Background(), key)
}()
for {
select {
case <-tick.C:
m, err := s.repository.GetAuthenticationRequest(ctx, key)
if err != nil || m == nil {
return connect.NewError(connect.CodeInternal, errors.New("something went wrong"))
}
if len(m.Token) != 0 {
if err := stream.Send(&api.AuthenticationResponse{Token: m.Token, TailnetId: m.TailnetID}); err != nil {
return err
}
return nil
}
if len(m.Error) != 0 {
return connect.NewError(connect.CodePermissionDenied, errors.New(m.Error))
}
if err := stream.Send(&api.AuthenticationResponse{AuthUrl: authUrl}); err != nil {
return err
}
case <-notify:
return nil
}
}
}
-145
View File
@@ -1,145 +0,0 @@
package service
import (
"context"
"fmt"
"github.com/hashicorp/go-bexpr"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/util"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func (s *Service) ListAuthFilters(ctx context.Context, req *api.ListAuthFiltersRequest) (*api.ListAuthFiltersResponse, error) {
response := &api.ListAuthFiltersResponse{AuthFilters: []*api.AuthFilter{}}
if req.AuthMethodId == nil {
filters, err := s.repository.ListAuthFilters(ctx)
if err != nil {
return nil, err
}
for _, filter := range filters {
response.AuthFilters = append(response.AuthFilters, s.mapToApi(&filter.AuthMethod, filter))
}
} else {
authMethod, err := s.repository.GetAuthMethod(ctx, *req.AuthMethodId)
if err != nil {
return nil, err
}
if authMethod == nil {
return nil, status.Error(codes.NotFound, "invalid auth method id")
}
filters, err := s.repository.ListAuthFiltersByAuthMethod(ctx, authMethod.ID)
if err != nil {
return nil, err
}
for _, filter := range filters {
response.AuthFilters = append(response.AuthFilters, s.mapToApi(&filter.AuthMethod, filter))
}
}
return response, nil
}
func (s *Service) CreateAuthFilter(ctx context.Context, req *api.CreateAuthFilterRequest) (*api.CreateAuthFilterResponse, error) {
authMethod, err := s.repository.GetAuthMethod(ctx, req.AuthMethodId)
if err != nil {
return nil, err
}
if authMethod == nil {
return nil, status.Error(codes.NotFound, "invalid auth method id")
}
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "invalid tailnet id")
}
if req.Expr != "*" {
if _, err := bexpr.CreateEvaluator(req.Expr); err != nil {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid expression: %v", err))
}
}
authFilter := &domain.AuthFilter{
ID: util.NextID(),
Expr: req.Expr,
AuthMethod: *authMethod,
Tailnet: tailnet,
}
if err := s.repository.SaveAuthFilter(ctx, authFilter); err != nil {
return nil, err
}
response := api.CreateAuthFilterResponse{AuthFilter: s.mapToApi(authMethod, *authFilter)}
return &response, nil
}
func (s *Service) DeleteAuthFilter(ctx context.Context, req *api.DeleteAuthFilterRequest) (*api.DeleteAuthFilterResponse, error) {
err := s.repository.Transaction(func(rp domain.Repository) error {
filter, err := rp.GetAuthFilter(ctx, req.AuthFilterId)
if err != nil {
return err
}
if filter == nil {
return status.Error(codes.NotFound, "auth filter not found")
}
c, err := rp.ExpireMachineByAuthMethod(ctx, filter.AuthMethodID)
if err != nil {
return err
}
if err := rp.DeleteAuthFilter(ctx, filter.ID); err != nil {
return err
}
if c != 0 {
s.brokers(*filter.TailnetID).SignalUpdate()
}
return nil
})
if err != nil {
return nil, err
}
response := api.DeleteAuthFilterResponse{}
return &response, nil
}
func (s *Service) mapToApi(authMethod *domain.AuthMethod, filter domain.AuthFilter) *api.AuthFilter {
result := api.AuthFilter{
Id: filter.ID,
Expr: filter.Expr,
AuthMethod: &api.Ref{
Id: authMethod.ID,
Name: authMethod.Name,
},
}
if filter.Tailnet != nil {
id := filter.Tailnet.ID
name := filter.Tailnet.Name
result.Tailnet = &api.Ref{
Id: id,
Name: name,
}
}
return &result
}
+114 -44
View File
@@ -2,22 +2,28 @@ package service
import (
"context"
"errors"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"google.golang.org/protobuf/types/known/timestamppb"
"time"
)
func (s *Service) GetAuthKey(ctx context.Context, req *api.GetAuthKeyRequest) (*api.GetAuthKeyResponse, error) {
key, err := s.repository.GetAuthKey(ctx, req.AuthKeyId)
func (s *Service) GetAuthKey(ctx context.Context, req *connect.Request[api.GetAuthKeyRequest]) (*connect.Response[api.GetAuthKeyResponse], error) {
principal := CurrentPrincipal(ctx)
key, err := s.repository.GetAuthKey(ctx, req.Msg.AuthKeyId)
if err != nil {
return nil, err
}
if key == nil {
return nil, status.Error(codes.NotFound, "")
return nil, connect.NewError(connect.CodeNotFound, errors.New("auth key not found"))
}
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(key.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
var expiresAt *timestamppb.Timestamp
@@ -25,7 +31,7 @@ func (s *Service) GetAuthKey(ctx context.Context, req *api.GetAuthKeyRequest) (*
expiresAt = timestamppb.New(*key.ExpiresAt)
}
return &api.GetAuthKeyResponse{AuthKey: &api.AuthKey{
return connect.NewResponse(&api.GetAuthKeyResponse{AuthKey: &api.AuthKey{
Id: key.ID,
Key: key.Key,
Ephemeral: key.Ephemeral,
@@ -36,25 +42,11 @@ func (s *Service) GetAuthKey(ctx context.Context, req *api.GetAuthKeyRequest) (*
Id: key.Tailnet.ID,
Name: key.Tailnet.Name,
},
}}, nil
}}), nil
}
func (s *Service) ListAuthKeys(ctx context.Context, req *api.ListAuthKeysRequest) (*api.ListAuthKeysResponse, error) {
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "")
}
authKeys, err := s.repository.ListAuthKeys(ctx, req.TailnetId)
if err != nil {
return nil, err
}
response := api.ListAuthKeysResponse{}
func mapAuthKeysToApi(authKeys []domain.AuthKey) []*api.AuthKey {
var result []*api.AuthKey
for _, key := range authKeys {
var expiresAt *timestamppb.Timestamp
@@ -62,7 +54,7 @@ func (s *Service) ListAuthKeys(ctx context.Context, req *api.ListAuthKeysRequest
expiresAt = timestamppb.New(*key.ExpiresAt)
}
response.AuthKeys = append(response.AuthKeys, &api.AuthKey{
result = append(result, &api.AuthKey{
Id: key.ID,
Key: key.Key,
Ephemeral: key.Ephemeral,
@@ -70,47 +62,110 @@ func (s *Service) ListAuthKeys(ctx context.Context, req *api.ListAuthKeysRequest
CreatedAt: timestamppb.New(key.CreatedAt),
ExpiresAt: expiresAt,
Tailnet: &api.Ref{
Id: tailnet.ID,
Name: tailnet.Name,
Id: key.Tailnet.ID,
Name: key.Tailnet.Name,
},
})
}
return &response, nil
return result
}
func (s *Service) CreateAuthKey(ctx context.Context, req *api.CreateAuthKeyRequest) (*api.CreateAuthKeyResponse, error) {
if len(req.Tags) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "at least one tag is required when creating an auth key")
func (s *Service) ListAuthKeys(ctx context.Context, req *connect.Request[api.ListAuthKeysRequest]) (*connect.Response[api.ListAuthKeysResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "")
return nil, connect.NewError(connect.CodeNotFound, errors.New("tailnet not found"))
}
response := api.ListAuthKeysResponse{}
if principal.IsSystemAdmin() {
authKeys, err := s.repository.ListAuthKeys(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
response.AuthKeys = mapAuthKeysToApi(authKeys)
return connect.NewResponse(&response), nil
}
if principal.User != nil {
authKeys, err := s.repository.ListAuthKeysByTailnetAndUser(ctx, req.Msg.TailnetId, principal.User.ID)
if err != nil {
return nil, err
}
response.AuthKeys = mapAuthKeysToApi(authKeys)
return connect.NewResponse(&response), nil
}
return connect.NewResponse(&response), nil
}
func (s *Service) CreateAuthKey(ctx context.Context, req *connect.Request[api.CreateAuthKeyRequest]) (*connect.Response[api.CreateAuthKeyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
if principal.User == nil && len(req.Msg.Tags) == 0 {
return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("at least one tag is required when creating an auth key"))
}
if err := domain.CheckTags(req.Msg.Tags); err != nil {
return nil, connect.NewError(connect.CodeInvalidArgument, err)
}
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.New("tailnet not found"))
}
if principal.IsSystemAdmin() {
if err := tailnet.ACLPolicy.CheckTags(req.Msg.Tags); err != nil {
return nil, connect.NewError(connect.CodeInvalidArgument, err)
}
} else {
if err := tailnet.ACLPolicy.CheckTagOwners(req.Msg.Tags, principal.User); err != nil {
return nil, connect.NewError(connect.CodeInvalidArgument, err)
}
}
var expiresAt *time.Time
var expiresAtPb *timestamppb.Timestamp
if req.Expiry != nil {
duration := req.Expiry.AsDuration()
if req.Msg.Expiry != nil {
duration := req.Msg.Expiry.AsDuration()
e := time.Now().UTC().Add(duration)
expiresAt = &e
expiresAtPb = timestamppb.New(*expiresAt)
}
user, _, err := s.repository.GetOrCreateServiceUser(ctx, tailnet)
if err != nil {
return nil, err
var user = principal.User
if user == nil {
u, _, err := s.repository.GetOrCreateServiceUser(ctx, tailnet)
if err != nil {
return nil, err
}
user = u
}
tags := domain.SanitizeTags(req.Tags)
tags := domain.SanitizeTags(req.Msg.Tags)
v, authKey := domain.CreateAuthKey(tailnet, user, req.Ephemeral, tags, expiresAt)
v, authKey := domain.CreateAuthKey(tailnet, user, req.Msg.Ephemeral, tags, expiresAt)
if err := s.repository.SaveAuthKey(ctx, authKey); err != nil {
return nil, err
@@ -131,12 +186,27 @@ func (s *Service) CreateAuthKey(ctx context.Context, req *api.CreateAuthKeyReque
},
}}
return &response, nil
return connect.NewResponse(&response), nil
}
func (s *Service) DeleteAuthKey(ctx context.Context, req *api.DeleteAuthKeyRequest) (*api.DeleteAuthKeyResponse, error) {
if _, err := s.repository.DeleteAuthKey(ctx, req.AuthKeyId); err != nil {
func (s *Service) DeleteAuthKey(ctx context.Context, req *connect.Request[api.DeleteAuthKeyRequest]) (*connect.Response[api.DeleteAuthKeyResponse], error) {
principal := CurrentPrincipal(ctx)
key, err := s.repository.GetAuthKey(ctx, req.Msg.AuthKeyId)
if err != nil {
return nil, err
}
return &api.DeleteAuthKeyResponse{}, nil
if key == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.New("auth key not found"))
}
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(key.UserID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
if _, err := s.repository.DeleteAuthKey(ctx, req.Msg.AuthKeyId); err != nil {
return nil, err
}
return connect.NewResponse(&api.DeleteAuthKeyResponse{}), nil
}
-51
View File
@@ -1,51 +0,0 @@
package service
import (
"context"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/util"
"github.com/jsiebens/ionscale/pkg/gen/api"
)
func (s *Service) CreateAuthMethod(ctx context.Context, req *api.CreateAuthMethodRequest) (*api.CreateAuthMethodResponse, error) {
authMethod := &domain.AuthMethod{
ID: util.NextID(),
Name: req.Name,
Type: req.Type,
Issuer: req.Issuer,
ClientId: req.ClientId,
ClientSecret: req.ClientSecret,
}
if err := s.repository.SaveAuthMethod(ctx, authMethod); err != nil {
return nil, err
}
return &api.CreateAuthMethodResponse{AuthMethod: &api.AuthMethod{
Id: authMethod.ID,
Type: authMethod.Type,
Name: authMethod.Name,
Issuer: authMethod.Issuer,
ClientId: authMethod.ClientId,
}}, nil
}
func (s *Service) ListAuthMethods(ctx context.Context, _ *api.ListAuthMethodsRequest) (*api.ListAuthMethodsResponse, error) {
methods, err := s.repository.ListAuthMethods(ctx)
if err != nil {
return nil, err
}
response := &api.ListAuthMethodsResponse{AuthMethods: []*api.AuthMethod{}}
for _, m := range methods {
response.AuthMethods = append(response.AuthMethods, &api.AuthMethod{
Id: m.ID,
Name: m.Name,
Type: m.Type,
})
}
return response, nil
}
+27 -7
View File
@@ -3,11 +3,19 @@ package service
import (
"context"
"encoding/json"
"github.com/jsiebens/ionscale/pkg/gen/api"
"errors"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/broker"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"tailscale.com/tailcfg"
)
func (s *Service) GetDERPMap(ctx context.Context, req *api.GetDERPMapRequest) (*api.GetDERPMapResponse, error) {
func (s *Service) GetDERPMap(ctx context.Context, _ *connect.Request[api.GetDERPMapRequest]) (*connect.Response[api.GetDERPMapResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
derpMap, err := s.repository.GetDERPMap(ctx)
if err != nil {
return nil, err
@@ -18,12 +26,22 @@ func (s *Service) GetDERPMap(ctx context.Context, req *api.GetDERPMapRequest) (*
return nil, err
}
return &api.GetDERPMapResponse{Value: raw}, nil
return connect.NewResponse(&api.GetDERPMapResponse{Value: raw}), nil
}
func (s *Service) SetDERPMap(ctx context.Context, req *api.SetDERPMapRequest) (*api.SetDERPMapResponse, error) {
func (s *Service) SetDERPMap(ctx context.Context, req *connect.Request[api.SetDERPMapRequest]) (*connect.Response[api.SetDERPMapResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
var derpMap tailcfg.DERPMap
err := json.Unmarshal(req.Value, &derpMap)
err := json.Unmarshal(req.Msg.Value, &derpMap)
if err != nil {
return nil, err
}
tailnets, err := s.repository.ListTailnets(ctx)
if err != nil {
return nil, err
}
@@ -32,7 +50,9 @@ func (s *Service) SetDERPMap(ctx context.Context, req *api.SetDERPMapRequest) (*
return nil, err
}
s.brokerPool.SignalDERPMapUpdated(&derpMap)
for _, t := range tailnets {
s.pubsub.Publish(t.ID, &broker.Signal{})
}
return &api.SetDERPMapResponse{Value: req.Value}, nil
return connect.NewResponse(&api.SetDERPMapResponse{Value: req.Msg.Value}), nil
}
+35 -22
View File
@@ -2,69 +2,82 @@ package service
import (
"context"
"errors"
"fmt"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/jsiebens/ionscale/internal/mapping"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"tailscale.com/util/dnsname"
)
func (s *Service) GetDNSConfig(ctx context.Context, req *api.GetDNSConfigRequest) (*api.GetDNSConfigResponse, error) {
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
func (s *Service) GetDNSConfig(ctx context.Context, req *connect.Request[api.GetDNSConfigRequest]) (*connect.Response[api.GetDNSConfigResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "tailnet does not exist")
return nil, connect.NewError(connect.CodeNotFound, errors.New("tailnet not found"))
}
config, err := s.repository.GetDNSConfig(ctx, tailnet.ID)
if err != nil {
return nil, err
}
config := tailnet.DNSConfig
tailnetDomain := dnsname.SanitizeHostname(tailnet.Name)
resp := &api.GetDNSConfigResponse{
Config: &api.DNSConfig{
MagicDns: config.MagicDNS,
MagicDnsSuffix: fmt.Sprintf("%s.%s", tailnetDomain, mapping.NetworkMagicDNSSuffix),
OverrideLocalDns: config.OverrideLocalDNS,
Nameservers: config.Nameservers,
Routes: domainRoutesToApiRoutes(config.Routes),
},
}
return resp, nil
return connect.NewResponse(resp), nil
}
func (s *Service) SetDNSConfig(ctx context.Context, req *api.SetDNSConfigRequest) (*api.SetDNSConfigResponse, error) {
dnsConfig := req.Config
if dnsConfig.MagicDns && len(dnsConfig.Nameservers) == 0 {
return nil, status.Error(codes.InvalidArgument, "at least one global nameserver is required when enabling magic dns")
func (s *Service) SetDNSConfig(ctx context.Context, req *connect.Request[api.SetDNSConfigRequest]) (*connect.Response[api.SetDNSConfigResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
dnsConfig := req.Msg.Config
if dnsConfig.MagicDns && len(dnsConfig.Nameservers) == 0 {
return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("at least one global nameserver is required when enabling magic dns"))
}
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "tailnet does not exist")
return nil, connect.NewError(connect.CodeNotFound, errors.New("tailnet not found"))
}
config := domain.DNSConfig{
tailnet.DNSConfig = domain.DNSConfig{
MagicDNS: dnsConfig.MagicDns,
OverrideLocalDNS: dnsConfig.OverrideLocalDns,
Nameservers: dnsConfig.Nameservers,
Routes: apiRoutesToDomainRoutes(dnsConfig.Routes),
}
if err := s.repository.SetDNSConfig(ctx, tailnet.ID, &config); err != nil {
if err := s.repository.SaveTailnet(ctx, tailnet); err != nil {
return nil, err
}
s.brokers(tailnet.ID).SignalDNSUpdated()
s.pubsub.Publish(tailnet.ID, &broker.Signal{DNSUpdated: true})
resp := &api.SetDNSConfigResponse{Config: dnsConfig}
return resp, nil
return connect.NewResponse(resp), nil
}
func domainRoutesToApiRoutes(routes map[string][]string) map[string]*api.Routes {
+78
View File
@@ -0,0 +1,78 @@
package service
import (
"context"
"errors"
"fmt"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/domain"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
)
func (s *Service) GetIAMPolicy(ctx context.Context, req *connect.Request[api.GetIAMPolicyRequest]) (*connect.Response[api.GetIAMPolicyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("tailnet does not exist"))
}
policy := &api.IAMPolicy{
Subs: tailnet.IAMPolicy.Subs,
Emails: tailnet.IAMPolicy.Emails,
Filters: tailnet.IAMPolicy.Filters,
Roles: domainRolesMapToApiRolesMap(tailnet.IAMPolicy.Roles),
}
return connect.NewResponse(&api.GetIAMPolicyResponse{Policy: policy}), nil
}
func (s *Service) SetIAMPolicy(ctx context.Context, req *connect.Request[api.SetIAMPolicyRequest]) (*connect.Response[api.SetIAMPolicyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("tailnet does not exist"))
}
tailnet.IAMPolicy = domain.IAMPolicy{
Subs: req.Msg.Policy.Subs,
Emails: req.Msg.Policy.Emails,
Filters: req.Msg.Policy.Filters,
Roles: apiRolesMapToDomainRolesMap(req.Msg.Policy.Roles),
}
if err := s.repository.SaveTailnet(ctx, tailnet); err != nil {
return nil, err
}
return connect.NewResponse(&api.SetIAMPolicyResponse{}), nil
}
func apiRolesMapToDomainRolesMap(values map[string]string) map[string]domain.UserRole {
var result = map[string]domain.UserRole{}
for k, v := range values {
result[k] = domain.UserRole(v)
}
return result
}
func domainRolesMapToApiRolesMap(values map[string]domain.UserRole) map[string]string {
var result = map[string]string{}
for k, v := range values {
result[k] = string(v)
}
return result
}
+77
View File
@@ -0,0 +1,77 @@
package service
import (
"context"
"fmt"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/key"
"github.com/jsiebens/ionscale/internal/token"
"strings"
)
var (
errInvalidToken = connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("invalid token"))
)
const (
principalKey = "principalKay"
)
func CurrentPrincipal(ctx context.Context) domain.Principal {
p := ctx.Value(principalKey)
if p == nil {
return domain.Principal{SystemRole: domain.SystemRoleNone, UserRole: domain.UserRoleNone}
}
return p.(domain.Principal)
}
func AuthenticationInterceptor(systemAdminKey *key.ServerPrivate, repository domain.Repository) connect.UnaryInterceptorFunc {
return func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
name := req.Spec().Procedure
if strings.HasSuffix(name, "/GetVersion") {
return next(ctx, req)
}
authorizationHeader := req.Header().Get("Authorization")
bearerToken := strings.TrimPrefix(authorizationHeader, "Bearer ")
if principal := exchangeToken(ctx, systemAdminKey, repository, bearerToken); principal != nil {
return next(context.WithValue(ctx, principalKey, *principal), req)
}
return nil, errInvalidToken
}
}
}
func exchangeToken(ctx context.Context, systemAdminKey *key.ServerPrivate, repository domain.Repository, value string) *domain.Principal {
if len(value) == 0 {
return nil
}
if systemAdminKey != nil && token.IsSystemAdminToken(value) {
_, err := token.ParseSystemAdminToken(*systemAdminKey, value)
if err == nil {
return &domain.Principal{SystemRole: domain.SystemRoleAdmin}
}
}
apiKey, err := repository.LoadApiKey(ctx, value)
if err == nil && apiKey != nil {
user := apiKey.User
tailnet := apiKey.Tailnet
role := tailnet.IAMPolicy.GetRole(user)
return &domain.Principal{User: &apiKey.User, SystemRole: domain.SystemRoleNone, UserRole: role}
}
systemApiKey, err := repository.LoadSystemApiKey(ctx, value)
if err == nil && systemApiKey != nil {
return &domain.Principal{SystemRole: domain.SystemRoleAdmin}
}
return nil
}
+232 -94
View File
@@ -2,22 +2,84 @@ package service
import (
"context"
"errors"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/domain"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"google.golang.org/protobuf/types/known/timestamppb"
"inet.af/netaddr"
"net/netip"
"time"
)
func (s *Service) ListMachines(ctx context.Context, req *api.ListMachinesRequest) (*api.ListMachinesResponse, error) {
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
func (s *Service) machineToApi(m *domain.Machine) *api.Machine {
var lastSeen *timestamppb.Timestamp
var name = m.Name
if m.NameIdx != 0 {
name = fmt.Sprintf("%s-%d", m.Name, m.NameIdx)
}
online := false
if m.LastSeen != nil {
lastSeen = timestamppb.New(*m.LastSeen)
online = m.LastSeen.After(time.Now().Add(-config.KeepAliveInterval))
}
var advertisedRoutes []string
for _, r := range m.HostInfo.RoutableIPs {
advertisedRoutes = append(advertisedRoutes, r.String())
}
var enabledRoutes []string
for _, r := range m.AllowIPs {
enabledRoutes = append(enabledRoutes, r.String())
}
return &api.Machine{
Id: m.ID,
Name: name,
Ipv4: m.IPv4.String(),
Ipv6: m.IPv6.String(),
Ephemeral: m.Ephemeral,
Tags: m.Tags,
LastSeen: lastSeen,
CreatedAt: timestamppb.New(m.CreatedAt),
ExpiresAt: timestamppb.New(m.ExpiresAt),
KeyExpiryDisabled: m.KeyExpiryDisabled,
Connected: online,
Os: m.HostInfo.OS,
ClientVersion: m.HostInfo.IPNVersion,
Tailnet: &api.Ref{
Id: m.Tailnet.ID,
Name: m.Tailnet.Name,
},
User: &api.Ref{
Id: m.User.ID,
Name: m.User.Name,
},
ClientConnectivity: &api.ClientConnectivity{
Endpoints: m.Endpoints,
},
AdvertisedRoutes: advertisedRoutes,
EnabledRoutes: enabledRoutes,
}
}
func (s *Service) ListMachines(ctx context.Context, req *connect.Request[api.ListMachinesRequest]) (*connect.Response[api.ListMachinesResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "tailnet does not exist")
return nil, connect.NewError(connect.CodeNotFound, errors.New("tailnet not found"))
}
machines, err := s.repository.ListMachineByTailnet(ctx, tailnet.ID)
@@ -27,150 +89,226 @@ func (s *Service) ListMachines(ctx context.Context, req *api.ListMachinesRequest
response := &api.ListMachinesResponse{}
for _, m := range machines {
var name = m.Name
if m.NameIdx != 0 {
name = fmt.Sprintf("%s-%d", m.Name, m.NameIdx)
}
online := s.brokers(m.TailnetID).IsConnected(m.ID)
var lastSeen *timestamppb.Timestamp
if m.LastSeen != nil {
lastSeen = timestamppb.New(*m.LastSeen)
}
response.Machines = append(response.Machines, &api.Machine{
Id: m.ID,
Name: name,
Ipv4: m.IPv4.String(),
Ipv6: m.IPv6.String(),
Ephemeral: m.Ephemeral,
Tags: m.Tags,
LastSeen: lastSeen,
Connected: online,
Tailnet: &api.Ref{
Id: m.Tailnet.ID,
Name: m.Tailnet.Name,
},
User: &api.Ref{
Id: m.User.ID,
Name: m.User.Name,
},
})
response.Machines = append(response.Machines, s.machineToApi(&m))
}
return response, nil
return connect.NewResponse(response), nil
}
func (s *Service) DeleteMachine(ctx context.Context, req *api.DeleteMachineRequest) (*api.DeleteMachineResponse, error) {
m, err := s.repository.GetMachine(ctx, req.MachineId)
func (s *Service) GetMachine(ctx context.Context, req *connect.Request[api.GetMachineRequest]) (*connect.Response[api.GetMachineResponse], error) {
principal := CurrentPrincipal(ctx)
m, err := s.repository.GetMachine(ctx, req.Msg.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, status.Error(codes.NotFound, "machine does not exist")
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if _, err := s.repository.DeleteMachine(ctx, req.MachineId); err != nil {
return nil, err
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
s.brokers(m.TailnetID).SignalPeersRemoved([]uint64{m.ID})
return &api.DeleteMachineResponse{}, nil
return connect.NewResponse(&api.GetMachineResponse{Machine: s.machineToApi(m)}), nil
}
func (s *Service) ExpireMachine(ctx context.Context, req *api.ExpireMachineRequest) (*api.ExpireMachineResponse, error) {
m, err := s.repository.GetMachine(ctx, req.MachineId)
func (s *Service) DeleteMachine(ctx context.Context, req *connect.Request[api.DeleteMachineRequest]) (*connect.Response[api.DeleteMachineResponse], error) {
principal := CurrentPrincipal(ctx)
m, err := s.repository.GetMachine(ctx, req.Msg.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, status.Error(codes.NotFound, "machine does not exist")
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
if _, err := s.repository.DeleteMachine(ctx, req.Msg.MachineId); err != nil {
return nil, err
}
s.pubsub.Publish(m.TailnetID, &broker.Signal{PeersRemoved: []uint64{m.ID}})
return connect.NewResponse(&api.DeleteMachineResponse{}), nil
}
func (s *Service) ExpireMachine(ctx context.Context, req *connect.Request[api.ExpireMachineRequest]) (*connect.Response[api.ExpireMachineResponse], error) {
principal := CurrentPrincipal(ctx)
m, err := s.repository.GetMachine(ctx, req.Msg.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
timestamp := time.Unix(123, 0)
m.ExpiresAt = &timestamp
m.ExpiresAt = timestamp
m.KeyExpiryDisabled = false
if err := s.repository.SaveMachine(ctx, m); err != nil {
return nil, err
}
s.brokers(m.TailnetID).SignalPeerUpdated(m.ID)
s.pubsub.Publish(m.TailnetID, &broker.Signal{PeerUpdated: &m.ID})
return &api.ExpireMachineResponse{}, nil
return connect.NewResponse(&api.ExpireMachineResponse{}), nil
}
func (s *Service) GetMachineRoutes(ctx context.Context, req *api.GetMachineRoutesRequest) (*api.GetMachineRoutesResponse, error) {
m, err := s.repository.GetMachine(ctx, req.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, status.Error(codes.NotFound, "machine does not exist")
}
var routes []*api.RoutableIP
func (s *Service) createMachineRoutesResponse(m *domain.Machine) (*connect.Response[api.GetMachineRoutesResponse], error) {
var advertisedRoutes []string
for _, r := range m.HostInfo.RoutableIPs {
routes = append(routes, &api.RoutableIP{
Advertised: r.String(),
Allowed: m.IsAllowedIPPrefix(r),
})
advertisedRoutes = append(advertisedRoutes, r.String())
}
var enabledRoutes []string
for _, r := range m.AllowIPs {
enabledRoutes = append(enabledRoutes, r.String())
}
response := api.GetMachineRoutesResponse{
Routes: routes,
AdvertisedRoutes: advertisedRoutes,
EnabledRoutes: enabledRoutes,
}
return &response, nil
return connect.NewResponse(&response), nil
}
func (s *Service) SetMachineRoutes(ctx context.Context, req *api.SetMachineRoutesRequest) (*api.GetMachineRoutesResponse, error) {
m, err := s.repository.GetMachine(ctx, req.MachineId)
func (s *Service) GetMachineRoutes(ctx context.Context, req *connect.Request[api.GetMachineRoutesRequest]) (*connect.Response[api.GetMachineRoutesResponse], error) {
principal := CurrentPrincipal(ctx)
m, err := s.repository.GetMachine(ctx, req.Msg.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, status.Error(codes.NotFound, "machine does not exist")
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
var allowedIps []netaddr.IPPrefix
for _, r := range req.AllowedIps {
prefix, err := netaddr.ParseIPPrefix(r)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
return s.createMachineRoutesResponse(m)
}
func (s *Service) EnableMachineRoutes(ctx context.Context, req *connect.Request[api.EnableMachineRoutesRequest]) (*connect.Response[api.GetMachineRoutesResponse], error) {
principal := CurrentPrincipal(ctx)
m, err := s.repository.GetMachine(ctx, req.Msg.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
var enabledRoutes = domain.NewAllowIPsSet(m.AllowIPs)
if req.Msg.Replace {
enabledRoutes = domain.NewAllowIPsSet([]netip.Prefix{})
}
var routesToBeRemoved []netip.Prefix
for _, r := range req.Msg.Routes {
prefix, err := netip.ParsePrefix(r)
if err != nil {
return nil, err
}
allowedIps = append(allowedIps, prefix)
enabledRoutes.Add(prefix)
routesToBeRemoved = append(routesToBeRemoved, prefix)
}
m.AllowIPs = allowedIps
m.AllowIPs = enabledRoutes.Items()
if err := s.repository.SaveMachine(ctx, m); err != nil {
return nil, err
}
s.brokers(m.TailnetID).SignalPeerUpdated(m.ID)
s.pubsub.Publish(m.TailnetID, &broker.Signal{PeerUpdated: &m.ID})
var routes []*api.RoutableIP
for _, r := range m.HostInfo.RoutableIPs {
routes = append(routes, &api.RoutableIP{
Advertised: r.String(),
Allowed: m.IsAllowedIPPrefix(r),
})
}
response := api.GetMachineRoutesResponse{
Routes: routes,
}
return &response, nil
return s.createMachineRoutesResponse(m)
}
func mapIp(ip []netaddr.IPPrefix) []string {
var x = []string{}
for _, i := range ip {
x = append(x, i.String())
func (s *Service) DisableMachineRoutes(ctx context.Context, req *connect.Request[api.DisableMachineRoutesRequest]) (*connect.Response[api.GetMachineRoutesResponse], error) {
principal := CurrentPrincipal(ctx)
m, err := s.repository.GetMachine(ctx, req.Msg.MachineId)
if err != nil {
return nil, err
}
return x
if m == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
enabledRoutes := domain.NewAllowIPsSet(m.AllowIPs)
var routesToBeRemoved []netip.Prefix
for _, r := range req.Msg.Routes {
prefix, err := netip.ParsePrefix(r)
if err != nil {
return nil, err
}
enabledRoutes.Remove(prefix)
routesToBeRemoved = append(routesToBeRemoved, prefix)
}
m.AllowIPs = enabledRoutes.Items()
if err := s.repository.SaveMachine(ctx, m); err != nil {
return nil, err
}
s.pubsub.Publish(m.TailnetID, &broker.Signal{PeerUpdated: &m.ID})
return s.createMachineRoutesResponse(m)
}
func (s *Service) SetMachineKeyExpiry(ctx context.Context, req *connect.Request[api.SetMachineKeyExpiryRequest]) (*connect.Response[api.SetMachineKeyExpiryResponse], error) {
principal := CurrentPrincipal(ctx)
m, err := s.repository.GetMachine(ctx, req.Msg.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
m.KeyExpiryDisabled = req.Msg.Disabled
if err := s.repository.SaveMachine(ctx, m); err != nil {
return nil, err
}
s.pubsub.Publish(m.TailnetID, &broker.Signal{PeerUpdated: &m.ID})
return connect.NewResponse(&api.SetMachineKeyExpiryResponse{}), nil
}
+16 -64
View File
@@ -2,83 +2,35 @@ package service
import (
"context"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/key"
"github.com/jsiebens/ionscale/internal/token"
"github.com/jsiebens/ionscale/internal/provider"
"github.com/jsiebens/ionscale/internal/version"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"strings"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
)
var (
errMissingMetadata = status.Error(codes.InvalidArgument, "missing metadata")
errInvalidToken = status.Error(codes.Unauthenticated, "invalid token")
)
func NewService(repository domain.Repository, brokerPool *broker.BrokerPool) *Service {
func NewService(config *config.Config, authProvider provider.AuthProvider, repository domain.Repository, pubsub broker.Pubsub) *Service {
return &Service{
repository: repository,
brokerPool: brokerPool,
config: config,
authProvider: authProvider,
repository: repository,
pubsub: pubsub,
}
}
type Service struct {
repository domain.Repository
brokerPool *broker.BrokerPool
config *config.Config
authProvider provider.AuthProvider
repository domain.Repository
pubsub broker.Pubsub
}
func (s *Service) brokers(tailnetID uint64) broker.Broker {
return s.brokerPool.Get(tailnetID)
}
func (s *Service) GetVersion(ctx context.Context, req *api.GetVersionRequest) (*api.GetVersionResponse, error) {
func (s *Service) GetVersion(_ context.Context, _ *connect.Request[api.GetVersionRequest]) (*connect.Response[api.GetVersionResponse], error) {
v, revision := version.GetReleaseInfo()
return &api.GetVersionResponse{
return connect.NewResponse(&api.GetVersionResponse{
Version: v,
Revision: revision,
}, nil
}
func UnaryServerTokenAuth(systemAdminKey key.ServerPrivate) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if strings.HasSuffix(info.FullMethod, "/GetVersion") {
return handler(ctx, req)
}
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, errMissingMetadata
}
// The keys within metadata.MD are normalized to lowercase.
// See: https://godoc.org/google.golang.org/grpc/metadata#New
valid := validateAuthorizationToken(systemAdminKey, md["authorization"])
if valid {
return handler(ctx, req)
}
return nil, errInvalidToken
}
}
func validateAuthorizationToken(systemAdminKey key.ServerPrivate, authorization []string) bool {
if len(authorization) != 1 {
return false
}
bearerToken := strings.TrimPrefix(authorization[0], "Bearer ")
if token.IsSystemAdminToken(bearerToken) {
_, err := token.ParseSystemAdminToken(systemAdminKey, bearerToken)
return err == nil
}
return false
}), nil
}
+59 -38
View File
@@ -2,21 +2,27 @@ package service
import (
"context"
"errors"
"fmt"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
)
func (s *Service) CreateTailnet(ctx context.Context, req *api.CreateTailnetRequest) (*api.CreateTailnetResponse, error) {
tailnet, created, err := s.repository.GetOrCreateTailnet(ctx, req.Name)
func (s *Service) CreateTailnet(ctx context.Context, req *connect.Request[api.CreateTailnetRequest]) (*connect.Response[api.CreateTailnetResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
tailnet, created, err := s.repository.GetOrCreateTailnet(ctx, req.Msg.Name)
if err != nil {
return nil, err
}
if !created {
return nil, fmt.Errorf("tailnet already exists")
return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("tailnet already exists"))
}
resp := &api.CreateTailnetResponse{Tailnet: &api.Tailnet{
@@ -24,76 +30,91 @@ func (s *Service) CreateTailnet(ctx context.Context, req *api.CreateTailnetReque
Name: tailnet.Name,
}}
return resp, nil
return connect.NewResponse(resp), nil
}
func (s *Service) GetTailnet(ctx context.Context, req *api.GetTailnetRequest) (*api.GetTailnetResponse, error) {
tailnet, err := s.repository.GetTailnet(ctx, req.Id)
func (s *Service) GetTailnet(ctx context.Context, req *connect.Request[api.GetTailnetRequest]) (*connect.Response[api.GetTailnetResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.Id) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.Id)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "")
return nil, connect.NewError(connect.CodeNotFound, errors.New("tailnet not found"))
}
return &api.GetTailnetResponse{Tailnet: &api.Tailnet{
return connect.NewResponse(&api.GetTailnetResponse{Tailnet: &api.Tailnet{
Id: tailnet.ID,
Name: tailnet.Name,
}}, nil
}}), nil
}
func (s *Service) ListTailnets(ctx context.Context, _ *api.ListTailnetRequest) (*api.ListTailnetResponse, error) {
func (s *Service) ListTailnets(ctx context.Context, req *connect.Request[api.ListTailnetRequest]) (*connect.Response[api.ListTailnetResponse], error) {
principal := CurrentPrincipal(ctx)
resp := &api.ListTailnetResponse{}
tailnets, err := s.repository.ListTailnets(ctx)
if err != nil {
return nil, err
if principal.IsSystemAdmin() {
tailnets, err := s.repository.ListTailnets(ctx)
if err != nil {
return nil, err
}
for _, t := range tailnets {
gt := api.Tailnet{Id: t.ID, Name: t.Name}
resp.Tailnet = append(resp.Tailnet, &gt)
}
}
for _, t := range tailnets {
gt := api.Tailnet{Id: t.ID, Name: t.Name}
if principal.User != nil {
tailnet, err := s.repository.GetTailnet(ctx, principal.User.TailnetID)
if err != nil {
return nil, err
}
gt := api.Tailnet{Id: tailnet.ID, Name: tailnet.Name}
resp.Tailnet = append(resp.Tailnet, &gt)
}
return resp, nil
return connect.NewResponse(resp), nil
}
func (s *Service) DeleteTailnet(ctx context.Context, req *api.DeleteTailnetRequest) (*api.DeleteTailnetResponse, error) {
func (s *Service) DeleteTailnet(ctx context.Context, req *connect.Request[api.DeleteTailnetRequest]) (*connect.Response[api.DeleteTailnetResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
count, err := s.repository.CountMachineByTailnet(ctx, req.TailnetId)
count, err := s.repository.CountMachineByTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if !req.Force && count > 0 {
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("tailnet is not empty, number of machines: %d", count))
if !req.Msg.Force && count > 0 {
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("tailnet is not empty, number of machines: %d", count))
}
err = s.repository.Transaction(func(tx domain.Repository) error {
if err := tx.DeleteMachineByTailnet(ctx, req.TailnetId); err != nil {
if err := tx.DeleteMachineByTailnet(ctx, req.Msg.TailnetId); err != nil {
return err
}
if err := tx.DeleteAuthKeysByTailnet(ctx, req.TailnetId); err != nil {
if err := tx.DeleteApiKeysByTailnet(ctx, req.Msg.TailnetId); err != nil {
return err
}
if err := tx.DeleteUsersByTailnet(ctx, req.TailnetId); err != nil {
if err := tx.DeleteAuthKeysByTailnet(ctx, req.Msg.TailnetId); err != nil {
return err
}
if err := tx.DeleteAuthFiltersByTailnet(ctx, req.TailnetId); err != nil {
if err := tx.DeleteUsersByTailnet(ctx, req.Msg.TailnetId); err != nil {
return err
}
if err := tx.DeleteACLPolicy(ctx, req.TailnetId); err != nil {
return err
}
if err := tx.DeleteDNSConfig(ctx, req.TailnetId); err != nil {
return err
}
if err := tx.DeleteTailnet(ctx, req.TailnetId); err != nil {
if err := tx.DeleteTailnet(ctx, req.Msg.TailnetId); err != nil {
return err
}
@@ -104,7 +125,7 @@ func (s *Service) DeleteTailnet(ctx context.Context, req *api.DeleteTailnetReque
return nil, err
}
s.brokers(req.TailnetId).SignalUpdate()
s.pubsub.Publish(req.Msg.TailnetId, &broker.Signal{})
return &api.DeleteTailnetResponse{}, nil
return connect.NewResponse(&api.DeleteTailnetResponse{}), nil
}
+92
View File
@@ -0,0 +1,92 @@
package service
import (
"context"
"errors"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/domain"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
)
func (s *Service) ListUsers(ctx context.Context, req *connect.Request[api.ListUsersRequest]) (*connect.Response[api.ListUsersResponse], error) {
principal := CurrentPrincipal(ctx)
tailnet, err := s.repository.GetTailnet(ctx, req.Msg.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.New("tailnet not found"))
}
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(tailnet.ID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
users, err := s.repository.ListUsers(ctx, tailnet.ID)
if err != nil {
return nil, err
}
resp := &api.ListUsersResponse{}
for _, u := range users {
resp.Users = append(resp.Users, &api.User{
Id: u.ID,
Name: u.Name,
Role: string(tailnet.IAMPolicy.GetRole(u)),
})
}
return connect.NewResponse(resp), nil
}
func (s *Service) DeleteUser(ctx context.Context, req *connect.Request[api.DeleteUserRequest]) (*connect.Response[api.DeleteUserResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && principal.UserMatches(req.Msg.UserId) {
return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("unable delete yourself"))
}
user, err := s.repository.GetUser(ctx, req.Msg.UserId)
if err != nil {
return nil, err
}
if user == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.New("user not found"))
}
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(user.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
err = s.repository.Transaction(func(tx domain.Repository) error {
if err := tx.DeleteMachineByUser(ctx, req.Msg.UserId); err != nil {
return err
}
if err := tx.DeleteApiKeysByUser(ctx, req.Msg.UserId); err != nil {
return err
}
if err := tx.DeleteAuthKeysByUser(ctx, req.Msg.UserId); err != nil {
return err
}
if err := tx.DeleteUser(ctx, req.Msg.UserId); err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
s.pubsub.Publish(user.TailnetID, &broker.Signal{})
return connect.NewResponse(&api.DeleteUserResponse{}), nil
}
+5 -5
View File
@@ -74,29 +74,29 @@
</head>
<body>
<div class="wrapper">
{{if .AuthMethods}}
{{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">
{{range .AuthMethods}}
<li><button type="submit" name="s" value="{{.ID}}">{{.Name}}</button></li>
{{end}}
<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 .AuthMethods}}
{{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>
+96
View File
@@ -0,0 +1,96 @@
<!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>
+63
View File
@@ -0,0 +1,63 @@
<!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>
+25 -1
View File
@@ -74,17 +74,41 @@
</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="s" value="{{.ID}}">{{.Name}}</button></li>
<li><button type="submit" name="tid" value="{{.ID}}">{{.Name}}</button></li>
{{end}}
</ul>
</form>
{{end}}
</div>
</body>
</html>
+20 -19
View File
@@ -1,11 +1,9 @@
package ionscale
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/internal/key"
"github.com/jsiebens/ionscale/internal/token"
"google.golang.org/grpc/credentials"
)
func LoadClientAuth(systemAdminKey string) (ClientAuth, error) {
@@ -17,38 +15,41 @@ func LoadClientAuth(systemAdminKey string) (ClientAuth, error) {
return &systemAdminTokenAuth{key: *k}, nil
}
apiToken, err := TokenFromFile()
if err != nil {
return nil, err
}
if len(apiToken) != 0 {
return &apiTokenAuth{token: apiToken}, nil
}
return &anonymous{}, nil
}
type ClientAuth interface {
credentials.PerRPCCredentials
GetToken() (string, error)
}
type anonymous struct {
}
func (m *anonymous) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return nil, nil
}
func (m *anonymous) RequireTransportSecurity() bool {
return false
func (m *anonymous) GetToken() (string, error) {
return "", nil
}
type systemAdminTokenAuth struct {
key key.ServerPrivate
}
func (m *systemAdminTokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
t, err := token.GenerateSystemAdminToken(m.key)
if err != nil {
return nil, err
}
return map[string]string{
"authorization": "Bearer " + t,
}, nil
func (m *systemAdminTokenAuth) GetToken() (string, error) {
return token.GenerateSystemAdminToken(m.key)
}
func (m *systemAdminTokenAuth) RequireTransportSecurity() bool {
return false
type apiTokenAuth struct {
token string
}
func (m *apiTokenAuth) GetToken() (string, error) {
return m.token, nil
}
+24 -38
View File
@@ -1,49 +1,35 @@
package ionscale
import (
"context"
"crypto/tls"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"io"
"net/url"
"fmt"
"github.com/bufbuild/connect-go"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1/ionscalev1connect"
"net/http"
)
func NewClient(clientAuth ClientAuth, serverURL string, insecureSkipVerify bool, useGrpcWebProxy bool) (api.IonscaleClient, io.Closer, error) {
parsedUrl, err := url.Parse(serverURL)
if err != nil {
return nil, nil, err
func NewClient(clientAuth ClientAuth, serverURL string, insecureSkipVerify bool) (api.IonscaleServiceClient, error) {
tlsConfig := &tls.Config{
InsecureSkipVerify: insecureSkipVerify,
}
if parsedUrl.Scheme == "" {
parsedUrl.Scheme = "https"
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
if useGrpcWebProxy {
conn, err := NewGrpcWebProxy(*parsedUrl, insecureSkipVerify).Dial(grpc.WithPerRPCCredentials(clientAuth))
if err != nil {
return nil, nil, err
}
return api.NewIonscaleClient(conn), conn, nil
}
var targetAddr = parsedUrl.Host
if parsedUrl.Port() == "" {
targetAddr = targetAddr + ":443"
}
var transportCreds = credentials.NewTLS(&tls.Config{InsecureSkipVerify: insecureSkipVerify})
if parsedUrl.Scheme != "https" {
transportCreds = insecure.NewCredentials()
}
conn, err := grpc.Dial(targetAddr, grpc.WithPerRPCCredentials(clientAuth), grpc.WithTransportCredentials(transportCreds))
if err != nil {
return nil, nil, err
}
return api.NewIonscaleClient(conn), conn, nil
interceptors := connect.WithInterceptors(NewAuthenticationInterceptor(clientAuth))
return api.NewIonscaleServiceClient(client, serverURL, interceptors), nil
}
func NewAuthenticationInterceptor(clientAuth ClientAuth) connect.UnaryInterceptorFunc {
return func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
token, _ := clientAuth.GetToken()
req.Header().Set("Authorization", fmt.Sprintf("Bearer %s", token))
return next(ctx, req)
}
}
}
+104
View File
@@ -0,0 +1,104 @@
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
}
-269
View File
@@ -1,269 +0,0 @@
package ionscale
import (
"bytes"
"context"
"crypto/tls"
"encoding/binary"
"fmt"
"github.com/jsiebens/ionscale/internal/util"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"io"
"net"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync"
"time"
)
type Conn struct {
*grpc.ClientConn
cls io.Closer
}
func (c *Conn) Close() error {
_ = c.ClientConn.Close()
_ = c.cls.Close()
return nil
}
func NewGrpcWebProxy(serverUrl url.URL, insecureSkipVerify bool) *grpcWebProxy {
tlsConfig := &tls.Config{
InsecureSkipVerify: insecureSkipVerify,
}
httpClient := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
return &grpcWebProxy{
serverUrl: serverUrl,
proxyMutex: &sync.Mutex{},
httpClient: httpClient,
}
}
type grpcWebProxy struct {
serverUrl url.URL
proxyMutex *sync.Mutex
proxyListener net.Listener
proxyServer *grpc.Server
proxyUsersCount int
httpClient *http.Client
}
const (
frameHeaderLength = 5
endOfStreamFlag = 128
)
type noopCodec struct{}
func (noopCodec) Marshal(v interface{}) ([]byte, error) {
return v.([]byte), nil
}
func (noopCodec) Unmarshal(data []byte, v interface{}) error {
pointer := v.(*[]byte)
*pointer = data
return nil
}
func (noopCodec) Name() string {
return "proto"
}
func toFrame(msg []byte) []byte {
frame := append([]byte{0, 0, 0, 0}, msg...)
binary.BigEndian.PutUint32(frame, uint32(len(msg)))
frame = append([]byte{0}, frame...)
return frame
}
func (c *grpcWebProxy) Dial(opts ...grpc.DialOption) (*Conn, error) {
addr, i, err := c.useGRPCProxy()
if err != nil {
return nil, err
}
dialer := func(ctx context.Context, address string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, addr.Network(), address)
}
opts = append(opts,
grpc.WithBlock(),
grpc.FailOnNonTempDialError(true),
grpc.WithContextDialer(dialer),
grpc.WithInsecure(), // we are handling TLS, so tell grpc not to
grpc.WithKeepaliveParams(keepalive.ClientParameters{Time: 10 * time.Second}),
)
conn, err := grpc.DialContext(context.Background(), addr.String(), opts...)
return &Conn{ClientConn: conn, cls: i}, err
}
func (c *grpcWebProxy) executeRequest(fullMethodName string, msg []byte, md metadata.MD) (*http.Response, error) {
requestURL := fmt.Sprintf("%s%s", c.serverUrl.String(), fullMethodName)
req, err := http.NewRequest(http.MethodPost, requestURL, bytes.NewReader(toFrame(msg)))
if err != nil {
return nil, err
}
for k, v := range md {
if strings.HasPrefix(k, ":") {
continue
}
for i := range v {
req.Header.Set(k, v[i])
}
}
req.Header.Set("content-type", "application/grpc-web+proto")
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s %s failed with status code %d", req.Method, req.URL, resp.StatusCode)
}
var code codes.Code
if statusStr := resp.Header.Get("Grpc-Status"); statusStr != "" {
statusInt, err := strconv.ParseUint(statusStr, 10, 32)
if err != nil {
code = codes.Unknown
} else {
code = codes.Code(statusInt)
}
if code != codes.OK {
return nil, status.Error(code, resp.Header.Get("Grpc-Message"))
}
}
return resp, nil
}
func (c *grpcWebProxy) startGRPCProxy() (*grpc.Server, net.Listener, error) {
serverAddr := fmt.Sprintf("%s/ionscale-%s.sock", os.TempDir(), util.RandStringBytes(8))
ln, err := net.Listen("unix", serverAddr)
if err != nil {
return nil, nil, err
}
proxySrv := grpc.NewServer(
grpc.ForceServerCodec(&noopCodec{}),
grpc.UnknownServiceHandler(func(srv interface{}, stream grpc.ServerStream) error {
fullMethodName, ok := grpc.MethodFromServerStream(stream)
if !ok {
return fmt.Errorf("Unable to get method name from stream context.")
}
msg := make([]byte, 0)
err := stream.RecvMsg(&msg)
if err != nil {
return err
}
md, _ := metadata.FromIncomingContext(stream.Context())
resp, err := c.executeRequest(fullMethodName, msg, md)
if err != nil {
return err
}
go func() {
<-stream.Context().Done()
safeClose(resp.Body)
}()
defer safeClose(resp.Body)
c.httpClient.CloseIdleConnections()
for {
header := make([]byte, frameHeaderLength)
if _, err := io.ReadAtLeast(resp.Body, header, frameHeaderLength); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
}
if header[0] == endOfStreamFlag {
return nil
}
length := int(binary.BigEndian.Uint32(header[1:frameHeaderLength]))
data := make([]byte, length)
if read, err := io.ReadAtLeast(resp.Body, data, length); err != nil {
if err != io.EOF {
return err
} else if read < length {
return io.ErrUnexpectedEOF
} else {
return nil
}
}
if err := stream.SendMsg(data); err != nil {
return err
}
}
}))
go func() {
_ = proxySrv.Serve(ln)
}()
return proxySrv, ln, nil
}
func (c *grpcWebProxy) useGRPCProxy() (net.Addr, io.Closer, error) {
c.proxyMutex.Lock()
defer c.proxyMutex.Unlock()
if c.proxyListener == nil {
var err error
c.proxyServer, c.proxyListener, err = c.startGRPCProxy()
if err != nil {
return nil, nil, err
}
}
c.proxyUsersCount = c.proxyUsersCount + 1
return c.proxyListener.Addr(), NewCloser(func() error {
c.proxyMutex.Lock()
defer c.proxyMutex.Unlock()
c.proxyUsersCount = c.proxyUsersCount - 1
if c.proxyUsersCount == 0 {
c.proxyServer.Stop()
c.proxyListener = nil
c.proxyServer = nil
return nil
}
return nil
}), nil
}
type Closer interface {
Close() error
}
type inlineCloser struct {
close func() error
}
func (c *inlineCloser) Close() error {
return c.close()
}
func NewCloser(close func() error) Closer {
return &inlineCloser{close: close}
}
func safeClose(c Closer) {
if c != nil {
_ = c.Close()
}
}
-340
View File
@@ -1,340 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/acl.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
_ "google.golang.org/protobuf/types/known/durationpb"
_ "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GetACLPolicyRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TailnetId uint64 `protobuf:"varint,1,opt,name=tailnet_id,json=tailnetId,proto3" json:"tailnet_id,omitempty"`
}
func (x *GetACLPolicyRequest) Reset() {
*x = GetACLPolicyRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_acl_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetACLPolicyRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetACLPolicyRequest) ProtoMessage() {}
func (x *GetACLPolicyRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_acl_proto_msgTypes[0]
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 GetACLPolicyRequest.ProtoReflect.Descriptor instead.
func (*GetACLPolicyRequest) Descriptor() ([]byte, []int) {
return file_api_acl_proto_rawDescGZIP(), []int{0}
}
func (x *GetACLPolicyRequest) GetTailnetId() uint64 {
if x != nil {
return x.TailnetId
}
return 0
}
type GetACLPolicyResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *GetACLPolicyResponse) Reset() {
*x = GetACLPolicyResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_acl_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetACLPolicyResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetACLPolicyResponse) ProtoMessage() {}
func (x *GetACLPolicyResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_acl_proto_msgTypes[1]
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 GetACLPolicyResponse.ProtoReflect.Descriptor instead.
func (*GetACLPolicyResponse) Descriptor() ([]byte, []int) {
return file_api_acl_proto_rawDescGZIP(), []int{1}
}
func (x *GetACLPolicyResponse) GetValue() []byte {
if x != nil {
return x.Value
}
return nil
}
type SetACLPolicyRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TailnetId uint64 `protobuf:"varint,1,opt,name=tailnet_id,json=tailnetId,proto3" json:"tailnet_id,omitempty"`
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *SetACLPolicyRequest) Reset() {
*x = SetACLPolicyRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_acl_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SetACLPolicyRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SetACLPolicyRequest) ProtoMessage() {}
func (x *SetACLPolicyRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_acl_proto_msgTypes[2]
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 SetACLPolicyRequest.ProtoReflect.Descriptor instead.
func (*SetACLPolicyRequest) Descriptor() ([]byte, []int) {
return file_api_acl_proto_rawDescGZIP(), []int{2}
}
func (x *SetACLPolicyRequest) GetTailnetId() uint64 {
if x != nil {
return x.TailnetId
}
return 0
}
func (x *SetACLPolicyRequest) GetValue() []byte {
if x != nil {
return x.Value
}
return nil
}
type SetACLPolicyResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *SetACLPolicyResponse) Reset() {
*x = SetACLPolicyResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_acl_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SetACLPolicyResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SetACLPolicyResponse) ProtoMessage() {}
func (x *SetACLPolicyResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_acl_proto_msgTypes[3]
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 SetACLPolicyResponse.ProtoReflect.Descriptor instead.
func (*SetACLPolicyResponse) Descriptor() ([]byte, []int) {
return file_api_acl_proto_rawDescGZIP(), []int{3}
}
var File_api_acl_proto protoreflect.FileDescriptor
var file_api_acl_proto_rawDesc = []byte{
0x0a, 0x0d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x63, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x03, 0x61, 0x70, 0x69, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x66, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x22, 0x34, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x43, 0x4c, 0x50, 0x6f,
0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
0x09, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x22, 0x2c, 0x0a, 0x14, 0x47, 0x65,
0x74, 0x41, 0x43, 0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4a, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41,
0x43, 0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x14,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x41, 0x43, 0x4c, 0x50, 0x6f,
0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28,
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, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_api_acl_proto_rawDescOnce sync.Once
file_api_acl_proto_rawDescData = file_api_acl_proto_rawDesc
)
func file_api_acl_proto_rawDescGZIP() []byte {
file_api_acl_proto_rawDescOnce.Do(func() {
file_api_acl_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_acl_proto_rawDescData)
})
return file_api_acl_proto_rawDescData
}
var file_api_acl_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_api_acl_proto_goTypes = []interface{}{
(*GetACLPolicyRequest)(nil), // 0: api.GetACLPolicyRequest
(*GetACLPolicyResponse)(nil), // 1: api.GetACLPolicyResponse
(*SetACLPolicyRequest)(nil), // 2: api.SetACLPolicyRequest
(*SetACLPolicyResponse)(nil), // 3: api.SetACLPolicyResponse
}
var file_api_acl_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_api_acl_proto_init() }
func file_api_acl_proto_init() {
if File_api_acl_proto != nil {
return
}
file_api_ref_proto_init()
if !protoimpl.UnsafeEnabled {
file_api_acl_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetACLPolicyRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_acl_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetACLPolicyResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_acl_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SetACLPolicyRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_acl_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SetACLPolicyResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_acl_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_acl_proto_goTypes,
DependencyIndexes: file_api_acl_proto_depIdxs,
MessageInfos: file_api_acl_proto_msgTypes,
}.Build()
File_api_acl_proto = out.File
file_api_acl_proto_rawDesc = nil
file_api_acl_proto_goTypes = nil
file_api_acl_proto_depIdxs = nil
}
-577
View File
@@ -1,577 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/auth_filter.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type AuthFilter struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
AuthMethod *Ref `protobuf:"bytes,2,opt,name=auth_method,json=authMethod,proto3" json:"auth_method,omitempty"`
Tailnet *Ref `protobuf:"bytes,3,opt,name=tailnet,proto3" json:"tailnet,omitempty"`
Expr string `protobuf:"bytes,4,opt,name=expr,proto3" json:"expr,omitempty"`
}
func (x *AuthFilter) Reset() {
*x = AuthFilter{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_filter_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AuthFilter) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthFilter) ProtoMessage() {}
func (x *AuthFilter) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_filter_proto_msgTypes[0]
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 AuthFilter.ProtoReflect.Descriptor instead.
func (*AuthFilter) Descriptor() ([]byte, []int) {
return file_api_auth_filter_proto_rawDescGZIP(), []int{0}
}
func (x *AuthFilter) GetId() uint64 {
if x != nil {
return x.Id
}
return 0
}
func (x *AuthFilter) GetAuthMethod() *Ref {
if x != nil {
return x.AuthMethod
}
return nil
}
func (x *AuthFilter) GetTailnet() *Ref {
if x != nil {
return x.Tailnet
}
return nil
}
func (x *AuthFilter) GetExpr() string {
if x != nil {
return x.Expr
}
return ""
}
type CreateAuthFilterRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthMethodId uint64 `protobuf:"varint,1,opt,name=auth_method_id,json=authMethodId,proto3" json:"auth_method_id,omitempty"`
TailnetId uint64 `protobuf:"varint,2,opt,name=tailnet_id,json=tailnetId,proto3" json:"tailnet_id,omitempty"`
Expr string `protobuf:"bytes,3,opt,name=expr,proto3" json:"expr,omitempty"`
}
func (x *CreateAuthFilterRequest) Reset() {
*x = CreateAuthFilterRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_filter_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateAuthFilterRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateAuthFilterRequest) ProtoMessage() {}
func (x *CreateAuthFilterRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_filter_proto_msgTypes[1]
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 CreateAuthFilterRequest.ProtoReflect.Descriptor instead.
func (*CreateAuthFilterRequest) Descriptor() ([]byte, []int) {
return file_api_auth_filter_proto_rawDescGZIP(), []int{1}
}
func (x *CreateAuthFilterRequest) GetAuthMethodId() uint64 {
if x != nil {
return x.AuthMethodId
}
return 0
}
func (x *CreateAuthFilterRequest) GetTailnetId() uint64 {
if x != nil {
return x.TailnetId
}
return 0
}
func (x *CreateAuthFilterRequest) GetExpr() string {
if x != nil {
return x.Expr
}
return ""
}
type CreateAuthFilterResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthFilter *AuthFilter `protobuf:"bytes,1,opt,name=auth_filter,json=authFilter,proto3" json:"auth_filter,omitempty"`
}
func (x *CreateAuthFilterResponse) Reset() {
*x = CreateAuthFilterResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_filter_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateAuthFilterResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateAuthFilterResponse) ProtoMessage() {}
func (x *CreateAuthFilterResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_filter_proto_msgTypes[2]
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 CreateAuthFilterResponse.ProtoReflect.Descriptor instead.
func (*CreateAuthFilterResponse) Descriptor() ([]byte, []int) {
return file_api_auth_filter_proto_rawDescGZIP(), []int{2}
}
func (x *CreateAuthFilterResponse) GetAuthFilter() *AuthFilter {
if x != nil {
return x.AuthFilter
}
return nil
}
type ListAuthFiltersRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthMethodId *uint64 `protobuf:"varint,1,opt,name=auth_method_id,json=authMethodId,proto3,oneof" json:"auth_method_id,omitempty"`
}
func (x *ListAuthFiltersRequest) Reset() {
*x = ListAuthFiltersRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_filter_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListAuthFiltersRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListAuthFiltersRequest) ProtoMessage() {}
func (x *ListAuthFiltersRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_filter_proto_msgTypes[3]
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 ListAuthFiltersRequest.ProtoReflect.Descriptor instead.
func (*ListAuthFiltersRequest) Descriptor() ([]byte, []int) {
return file_api_auth_filter_proto_rawDescGZIP(), []int{3}
}
func (x *ListAuthFiltersRequest) GetAuthMethodId() uint64 {
if x != nil && x.AuthMethodId != nil {
return *x.AuthMethodId
}
return 0
}
type ListAuthFiltersResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthFilters []*AuthFilter `protobuf:"bytes,1,rep,name=auth_filters,json=authFilters,proto3" json:"auth_filters,omitempty"`
}
func (x *ListAuthFiltersResponse) Reset() {
*x = ListAuthFiltersResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_filter_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListAuthFiltersResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListAuthFiltersResponse) ProtoMessage() {}
func (x *ListAuthFiltersResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_filter_proto_msgTypes[4]
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 ListAuthFiltersResponse.ProtoReflect.Descriptor instead.
func (*ListAuthFiltersResponse) Descriptor() ([]byte, []int) {
return file_api_auth_filter_proto_rawDescGZIP(), []int{4}
}
func (x *ListAuthFiltersResponse) GetAuthFilters() []*AuthFilter {
if x != nil {
return x.AuthFilters
}
return nil
}
type DeleteAuthFilterRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthFilterId uint64 `protobuf:"varint,1,opt,name=auth_filter_id,json=authFilterId,proto3" json:"auth_filter_id,omitempty"`
}
func (x *DeleteAuthFilterRequest) Reset() {
*x = DeleteAuthFilterRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_filter_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteAuthFilterRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteAuthFilterRequest) ProtoMessage() {}
func (x *DeleteAuthFilterRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_filter_proto_msgTypes[5]
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 DeleteAuthFilterRequest.ProtoReflect.Descriptor instead.
func (*DeleteAuthFilterRequest) Descriptor() ([]byte, []int) {
return file_api_auth_filter_proto_rawDescGZIP(), []int{5}
}
func (x *DeleteAuthFilterRequest) GetAuthFilterId() uint64 {
if x != nil {
return x.AuthFilterId
}
return 0
}
type DeleteAuthFilterResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *DeleteAuthFilterResponse) Reset() {
*x = DeleteAuthFilterResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_filter_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteAuthFilterResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteAuthFilterResponse) ProtoMessage() {}
func (x *DeleteAuthFilterResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_filter_proto_msgTypes[6]
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 DeleteAuthFilterResponse.ProtoReflect.Descriptor instead.
func (*DeleteAuthFilterResponse) Descriptor() ([]byte, []int) {
return file_api_auth_filter_proto_rawDescGZIP(), []int{6}
}
var File_api_auth_filter_proto protoreflect.FileDescriptor
var file_api_auth_filter_proto_rawDesc = []byte{
0x0a, 0x15, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65,
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x0d, 0x61, 0x70,
0x69, 0x2f, 0x72, 0x65, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7f, 0x0a, 0x0a, 0x41,
0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x0b, 0x61, 0x75, 0x74,
0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x66, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x12, 0x22, 0x0a, 0x07, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x66, 0x52,
0x07, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x78, 0x70, 0x72,
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x78, 0x70, 0x72, 0x22, 0x72, 0x0a, 0x17,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x5f,
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x1d, 0x0a,
0x0a, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x04, 0x52, 0x09, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04,
0x65, 0x78, 0x70, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x78, 0x70, 0x72,
0x22, 0x4c, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x46, 0x69,
0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0b,
0x61, 0x75, 0x74, 0x68, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74,
0x65, 0x72, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x56,
0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68,
0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
0x48, 0x00, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64,
0x88, 0x01, 0x01, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74,
0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x22, 0x4d, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75,
0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x32, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x75,
0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x46, 0x69,
0x6c, 0x74, 0x65, 0x72, 0x73, 0x22, 0x3f, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41,
0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x24, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x46, 0x69,
0x6c, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x1a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
0x41, 0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 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, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_api_auth_filter_proto_rawDescOnce sync.Once
file_api_auth_filter_proto_rawDescData = file_api_auth_filter_proto_rawDesc
)
func file_api_auth_filter_proto_rawDescGZIP() []byte {
file_api_auth_filter_proto_rawDescOnce.Do(func() {
file_api_auth_filter_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_auth_filter_proto_rawDescData)
})
return file_api_auth_filter_proto_rawDescData
}
var file_api_auth_filter_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_api_auth_filter_proto_goTypes = []interface{}{
(*AuthFilter)(nil), // 0: api.AuthFilter
(*CreateAuthFilterRequest)(nil), // 1: api.CreateAuthFilterRequest
(*CreateAuthFilterResponse)(nil), // 2: api.CreateAuthFilterResponse
(*ListAuthFiltersRequest)(nil), // 3: api.ListAuthFiltersRequest
(*ListAuthFiltersResponse)(nil), // 4: api.ListAuthFiltersResponse
(*DeleteAuthFilterRequest)(nil), // 5: api.DeleteAuthFilterRequest
(*DeleteAuthFilterResponse)(nil), // 6: api.DeleteAuthFilterResponse
(*Ref)(nil), // 7: api.Ref
}
var file_api_auth_filter_proto_depIdxs = []int32{
7, // 0: api.AuthFilter.auth_method:type_name -> api.Ref
7, // 1: api.AuthFilter.tailnet:type_name -> api.Ref
0, // 2: api.CreateAuthFilterResponse.auth_filter:type_name -> api.AuthFilter
0, // 3: api.ListAuthFiltersResponse.auth_filters:type_name -> api.AuthFilter
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_api_auth_filter_proto_init() }
func file_api_auth_filter_proto_init() {
if File_api_auth_filter_proto != nil {
return
}
file_api_ref_proto_init()
if !protoimpl.UnsafeEnabled {
file_api_auth_filter_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AuthFilter); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_filter_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateAuthFilterRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_filter_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateAuthFilterResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_filter_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListAuthFiltersRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_filter_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListAuthFiltersResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_filter_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteAuthFilterRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_filter_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteAuthFilterResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_api_auth_filter_proto_msgTypes[3].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_auth_filter_proto_rawDesc,
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_auth_filter_proto_goTypes,
DependencyIndexes: file_api_auth_filter_proto_depIdxs,
MessageInfos: file_api_auth_filter_proto_msgTypes,
}.Build()
File_api_auth_filter_proto = out.File
file_api_auth_filter_proto_rawDesc = nil
file_api_auth_filter_proto_goTypes = nil
file_api_auth_filter_proto_depIdxs = nil
}
-468
View File
@@ -1,468 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/auth_methods.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type AuthMethod struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
Issuer string `protobuf:"bytes,4,opt,name=issuer,proto3" json:"issuer,omitempty"`
ClientId string `protobuf:"bytes,5,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
}
func (x *AuthMethod) Reset() {
*x = AuthMethod{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_methods_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AuthMethod) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthMethod) ProtoMessage() {}
func (x *AuthMethod) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_methods_proto_msgTypes[0]
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 AuthMethod.ProtoReflect.Descriptor instead.
func (*AuthMethod) Descriptor() ([]byte, []int) {
return file_api_auth_methods_proto_rawDescGZIP(), []int{0}
}
func (x *AuthMethod) GetId() uint64 {
if x != nil {
return x.Id
}
return 0
}
func (x *AuthMethod) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *AuthMethod) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *AuthMethod) GetIssuer() string {
if x != nil {
return x.Issuer
}
return ""
}
func (x *AuthMethod) GetClientId() string {
if x != nil {
return x.ClientId
}
return ""
}
type CreateAuthMethodRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Issuer string `protobuf:"bytes,3,opt,name=issuer,proto3" json:"issuer,omitempty"`
ClientId string `protobuf:"bytes,4,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
ClientSecret string `protobuf:"bytes,5,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"`
}
func (x *CreateAuthMethodRequest) Reset() {
*x = CreateAuthMethodRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_methods_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateAuthMethodRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateAuthMethodRequest) ProtoMessage() {}
func (x *CreateAuthMethodRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_methods_proto_msgTypes[1]
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 CreateAuthMethodRequest.ProtoReflect.Descriptor instead.
func (*CreateAuthMethodRequest) Descriptor() ([]byte, []int) {
return file_api_auth_methods_proto_rawDescGZIP(), []int{1}
}
func (x *CreateAuthMethodRequest) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *CreateAuthMethodRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *CreateAuthMethodRequest) GetIssuer() string {
if x != nil {
return x.Issuer
}
return ""
}
func (x *CreateAuthMethodRequest) GetClientId() string {
if x != nil {
return x.ClientId
}
return ""
}
func (x *CreateAuthMethodRequest) GetClientSecret() string {
if x != nil {
return x.ClientSecret
}
return ""
}
type CreateAuthMethodResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthMethod *AuthMethod `protobuf:"bytes,1,opt,name=auth_method,json=authMethod,proto3" json:"auth_method,omitempty"`
}
func (x *CreateAuthMethodResponse) Reset() {
*x = CreateAuthMethodResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_methods_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateAuthMethodResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateAuthMethodResponse) ProtoMessage() {}
func (x *CreateAuthMethodResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_methods_proto_msgTypes[2]
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 CreateAuthMethodResponse.ProtoReflect.Descriptor instead.
func (*CreateAuthMethodResponse) Descriptor() ([]byte, []int) {
return file_api_auth_methods_proto_rawDescGZIP(), []int{2}
}
func (x *CreateAuthMethodResponse) GetAuthMethod() *AuthMethod {
if x != nil {
return x.AuthMethod
}
return nil
}
type ListAuthMethodsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ListAuthMethodsRequest) Reset() {
*x = ListAuthMethodsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_methods_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListAuthMethodsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListAuthMethodsRequest) ProtoMessage() {}
func (x *ListAuthMethodsRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_methods_proto_msgTypes[3]
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 ListAuthMethodsRequest.ProtoReflect.Descriptor instead.
func (*ListAuthMethodsRequest) Descriptor() ([]byte, []int) {
return file_api_auth_methods_proto_rawDescGZIP(), []int{3}
}
type ListAuthMethodsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthMethods []*AuthMethod `protobuf:"bytes,1,rep,name=auth_methods,json=authMethods,proto3" json:"auth_methods,omitempty"`
}
func (x *ListAuthMethodsResponse) Reset() {
*x = ListAuthMethodsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_methods_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListAuthMethodsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListAuthMethodsResponse) ProtoMessage() {}
func (x *ListAuthMethodsResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_methods_proto_msgTypes[4]
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 ListAuthMethodsResponse.ProtoReflect.Descriptor instead.
func (*ListAuthMethodsResponse) Descriptor() ([]byte, []int) {
return file_api_auth_methods_proto_rawDescGZIP(), []int{4}
}
func (x *ListAuthMethodsResponse) GetAuthMethods() []*AuthMethod {
if x != nil {
return x.AuthMethods
}
return nil
}
var File_api_auth_methods_proto protoreflect.FileDescriptor
var file_api_auth_methods_proto_rawDesc = []byte{
0x0a, 0x16, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f,
0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x22, 0x79, 0x0a,
0x0a, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74,
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x9b, 0x01, 0x0a, 0x17, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06,
0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73,
0x73, 0x75, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69,
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49,
0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72,
0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x4c, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x30, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x75,
0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68,
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d,
0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x0c, 0x61, 0x75, 0x74,
0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64,
0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x42, 0x2a, 0x5a,
0x28, 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, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_api_auth_methods_proto_rawDescOnce sync.Once
file_api_auth_methods_proto_rawDescData = file_api_auth_methods_proto_rawDesc
)
func file_api_auth_methods_proto_rawDescGZIP() []byte {
file_api_auth_methods_proto_rawDescOnce.Do(func() {
file_api_auth_methods_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_auth_methods_proto_rawDescData)
})
return file_api_auth_methods_proto_rawDescData
}
var file_api_auth_methods_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_api_auth_methods_proto_goTypes = []interface{}{
(*AuthMethod)(nil), // 0: api.AuthMethod
(*CreateAuthMethodRequest)(nil), // 1: api.CreateAuthMethodRequest
(*CreateAuthMethodResponse)(nil), // 2: api.CreateAuthMethodResponse
(*ListAuthMethodsRequest)(nil), // 3: api.ListAuthMethodsRequest
(*ListAuthMethodsResponse)(nil), // 4: api.ListAuthMethodsResponse
}
var file_api_auth_methods_proto_depIdxs = []int32{
0, // 0: api.CreateAuthMethodResponse.auth_method:type_name -> api.AuthMethod
0, // 1: api.ListAuthMethodsResponse.auth_methods:type_name -> api.AuthMethod
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_api_auth_methods_proto_init() }
func file_api_auth_methods_proto_init() {
if File_api_auth_methods_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_api_auth_methods_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AuthMethod); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_methods_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateAuthMethodRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_methods_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateAuthMethodResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_methods_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListAuthMethodsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_methods_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListAuthMethodsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_auth_methods_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_auth_methods_proto_goTypes,
DependencyIndexes: file_api_auth_methods_proto_depIdxs,
MessageInfos: file_api_auth_methods_proto_msgTypes,
}.Build()
File_api_auth_methods_proto = out.File
file_api_auth_methods_proto_rawDesc = nil
file_api_auth_methods_proto_goTypes = nil
file_api_auth_methods_proto_depIdxs = nil
}
-307
View File
@@ -1,307 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/ionscale.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
_ "google.golang.org/protobuf/types/known/durationpb"
_ "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
var File_api_ionscale_proto protoreflect.FileDescriptor
var file_api_ionscale_proto_rawDesc = []byte{
0x0a, 0x12, 0x61, 0x70, 0x69, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x61, 0x70, 0x69, 0x2f,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x12, 0x61,
0x70, 0x69, 0x2f, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x1a, 0x16, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x61, 0x70, 0x69, 0x2f, 0x61,
0x75, 0x74, 0x68, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x1a, 0x13, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x12, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69,
0x6e, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x10, 0x61, 0x70, 0x69, 0x2f, 0x72,
0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x61, 0x70, 0x69,
0x2f, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x61, 0x70, 0x69, 0x2f,
0x61, 0x63, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x61, 0x70, 0x69, 0x2f, 0x64,
0x65, 0x72, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xb9, 0x0e, 0x0a, 0x08, 0x49, 0x6f,
0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x44, 0x45,
0x52, 0x50, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44,
0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x44,
0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74,
0x44, 0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x10, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1c, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0f,
0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x12,
0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f,
0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x10,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72,
0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74,
0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x46,
0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x51, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x46, 0x69, 0x6c,
0x74, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
0x41, 0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x75,
0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x46, 0x69,
0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x41, 0x75, 0x74, 0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74,
0x68, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x69, 0x6c,
0x6e, 0x65, 0x74, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x6e,
0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0a,
0x47, 0x65, 0x74, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x69, 0x6c,
0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a,
0x0c, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x73, 0x12, 0x17, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73,
0x74, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x69, 0x6c,
0x6e, 0x65, 0x74, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x6e,
0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0c,
0x47, 0x65, 0x74, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74,
0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x4e, 0x53,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0c, 0x47, 0x65,
0x74, 0x41, 0x43, 0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x47, 0x65, 0x74, 0x41, 0x43, 0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x43,
0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x45, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x41, 0x43, 0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63,
0x79, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x43, 0x4c, 0x50, 0x6f,
0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x43, 0x4c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x41,
0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74,
0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x75, 0x74,
0x68, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68,
0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a,
0x0c, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x18, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69,
0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68,
0x69, 0x6e, 0x65, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d,
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x45,
0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x78,
0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d,
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61,
0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x51, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75,
0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63,
0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69,
0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x51, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74,
0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61,
0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2a, 0x5a, 0x28, 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, 0x3b, 0x61, 0x70,
0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var file_api_ionscale_proto_goTypes = []interface{}{
(*GetVersionRequest)(nil), // 0: api.GetVersionRequest
(*GetDERPMapRequest)(nil), // 1: api.GetDERPMapRequest
(*SetDERPMapRequest)(nil), // 2: api.SetDERPMapRequest
(*CreateAuthMethodRequest)(nil), // 3: api.CreateAuthMethodRequest
(*ListAuthMethodsRequest)(nil), // 4: api.ListAuthMethodsRequest
(*CreateAuthFilterRequest)(nil), // 5: api.CreateAuthFilterRequest
(*DeleteAuthFilterRequest)(nil), // 6: api.DeleteAuthFilterRequest
(*ListAuthFiltersRequest)(nil), // 7: api.ListAuthFiltersRequest
(*CreateTailnetRequest)(nil), // 8: api.CreateTailnetRequest
(*GetTailnetRequest)(nil), // 9: api.GetTailnetRequest
(*ListTailnetRequest)(nil), // 10: api.ListTailnetRequest
(*DeleteTailnetRequest)(nil), // 11: api.DeleteTailnetRequest
(*GetDNSConfigRequest)(nil), // 12: api.GetDNSConfigRequest
(*SetDNSConfigRequest)(nil), // 13: api.SetDNSConfigRequest
(*GetACLPolicyRequest)(nil), // 14: api.GetACLPolicyRequest
(*SetACLPolicyRequest)(nil), // 15: api.SetACLPolicyRequest
(*GetAuthKeyRequest)(nil), // 16: api.GetAuthKeyRequest
(*CreateAuthKeyRequest)(nil), // 17: api.CreateAuthKeyRequest
(*DeleteAuthKeyRequest)(nil), // 18: api.DeleteAuthKeyRequest
(*ListAuthKeysRequest)(nil), // 19: api.ListAuthKeysRequest
(*ListMachinesRequest)(nil), // 20: api.ListMachinesRequest
(*ExpireMachineRequest)(nil), // 21: api.ExpireMachineRequest
(*DeleteMachineRequest)(nil), // 22: api.DeleteMachineRequest
(*GetMachineRoutesRequest)(nil), // 23: api.GetMachineRoutesRequest
(*SetMachineRoutesRequest)(nil), // 24: api.SetMachineRoutesRequest
(*GetVersionResponse)(nil), // 25: api.GetVersionResponse
(*GetDERPMapResponse)(nil), // 26: api.GetDERPMapResponse
(*SetDERPMapResponse)(nil), // 27: api.SetDERPMapResponse
(*CreateAuthMethodResponse)(nil), // 28: api.CreateAuthMethodResponse
(*ListAuthMethodsResponse)(nil), // 29: api.ListAuthMethodsResponse
(*CreateAuthFilterResponse)(nil), // 30: api.CreateAuthFilterResponse
(*DeleteAuthFilterResponse)(nil), // 31: api.DeleteAuthFilterResponse
(*ListAuthFiltersResponse)(nil), // 32: api.ListAuthFiltersResponse
(*CreateTailnetResponse)(nil), // 33: api.CreateTailnetResponse
(*GetTailnetResponse)(nil), // 34: api.GetTailnetResponse
(*ListTailnetResponse)(nil), // 35: api.ListTailnetResponse
(*DeleteTailnetResponse)(nil), // 36: api.DeleteTailnetResponse
(*GetDNSConfigResponse)(nil), // 37: api.GetDNSConfigResponse
(*SetDNSConfigResponse)(nil), // 38: api.SetDNSConfigResponse
(*GetACLPolicyResponse)(nil), // 39: api.GetACLPolicyResponse
(*SetACLPolicyResponse)(nil), // 40: api.SetACLPolicyResponse
(*GetAuthKeyResponse)(nil), // 41: api.GetAuthKeyResponse
(*CreateAuthKeyResponse)(nil), // 42: api.CreateAuthKeyResponse
(*DeleteAuthKeyResponse)(nil), // 43: api.DeleteAuthKeyResponse
(*ListAuthKeysResponse)(nil), // 44: api.ListAuthKeysResponse
(*ListMachinesResponse)(nil), // 45: api.ListMachinesResponse
(*ExpireMachineResponse)(nil), // 46: api.ExpireMachineResponse
(*DeleteMachineResponse)(nil), // 47: api.DeleteMachineResponse
(*GetMachineRoutesResponse)(nil), // 48: api.GetMachineRoutesResponse
}
var file_api_ionscale_proto_depIdxs = []int32{
0, // 0: api.Ionscale.GetVersion:input_type -> api.GetVersionRequest
1, // 1: api.Ionscale.GetDERPMap:input_type -> api.GetDERPMapRequest
2, // 2: api.Ionscale.SetDERPMap:input_type -> api.SetDERPMapRequest
3, // 3: api.Ionscale.CreateAuthMethod:input_type -> api.CreateAuthMethodRequest
4, // 4: api.Ionscale.ListAuthMethods:input_type -> api.ListAuthMethodsRequest
5, // 5: api.Ionscale.CreateAuthFilter:input_type -> api.CreateAuthFilterRequest
6, // 6: api.Ionscale.DeleteAuthFilter:input_type -> api.DeleteAuthFilterRequest
7, // 7: api.Ionscale.ListAuthFilters:input_type -> api.ListAuthFiltersRequest
8, // 8: api.Ionscale.CreateTailnet:input_type -> api.CreateTailnetRequest
9, // 9: api.Ionscale.GetTailnet:input_type -> api.GetTailnetRequest
10, // 10: api.Ionscale.ListTailnets:input_type -> api.ListTailnetRequest
11, // 11: api.Ionscale.DeleteTailnet:input_type -> api.DeleteTailnetRequest
12, // 12: api.Ionscale.GetDNSConfig:input_type -> api.GetDNSConfigRequest
13, // 13: api.Ionscale.SetDNSConfig:input_type -> api.SetDNSConfigRequest
14, // 14: api.Ionscale.GetACLPolicy:input_type -> api.GetACLPolicyRequest
15, // 15: api.Ionscale.SetACLPolicy:input_type -> api.SetACLPolicyRequest
16, // 16: api.Ionscale.GetAuthKey:input_type -> api.GetAuthKeyRequest
17, // 17: api.Ionscale.CreateAuthKey:input_type -> api.CreateAuthKeyRequest
18, // 18: api.Ionscale.DeleteAuthKey:input_type -> api.DeleteAuthKeyRequest
19, // 19: api.Ionscale.ListAuthKeys:input_type -> api.ListAuthKeysRequest
20, // 20: api.Ionscale.ListMachines:input_type -> api.ListMachinesRequest
21, // 21: api.Ionscale.ExpireMachineByAuthMethod:input_type -> api.ExpireMachineRequest
22, // 22: api.Ionscale.DeleteMachine:input_type -> api.DeleteMachineRequest
23, // 23: api.Ionscale.GetMachineRoutes:input_type -> api.GetMachineRoutesRequest
24, // 24: api.Ionscale.SetMachineRoutes:input_type -> api.SetMachineRoutesRequest
25, // 25: api.Ionscale.GetVersion:output_type -> api.GetVersionResponse
26, // 26: api.Ionscale.GetDERPMap:output_type -> api.GetDERPMapResponse
27, // 27: api.Ionscale.SetDERPMap:output_type -> api.SetDERPMapResponse
28, // 28: api.Ionscale.CreateAuthMethod:output_type -> api.CreateAuthMethodResponse
29, // 29: api.Ionscale.ListAuthMethods:output_type -> api.ListAuthMethodsResponse
30, // 30: api.Ionscale.CreateAuthFilter:output_type -> api.CreateAuthFilterResponse
31, // 31: api.Ionscale.DeleteAuthFilter:output_type -> api.DeleteAuthFilterResponse
32, // 32: api.Ionscale.ListAuthFilters:output_type -> api.ListAuthFiltersResponse
33, // 33: api.Ionscale.CreateTailnet:output_type -> api.CreateTailnetResponse
34, // 34: api.Ionscale.GetTailnet:output_type -> api.GetTailnetResponse
35, // 35: api.Ionscale.ListTailnets:output_type -> api.ListTailnetResponse
36, // 36: api.Ionscale.DeleteTailnet:output_type -> api.DeleteTailnetResponse
37, // 37: api.Ionscale.GetDNSConfig:output_type -> api.GetDNSConfigResponse
38, // 38: api.Ionscale.SetDNSConfig:output_type -> api.SetDNSConfigResponse
39, // 39: api.Ionscale.GetACLPolicy:output_type -> api.GetACLPolicyResponse
40, // 40: api.Ionscale.SetACLPolicy:output_type -> api.SetACLPolicyResponse
41, // 41: api.Ionscale.GetAuthKey:output_type -> api.GetAuthKeyResponse
42, // 42: api.Ionscale.CreateAuthKey:output_type -> api.CreateAuthKeyResponse
43, // 43: api.Ionscale.DeleteAuthKey:output_type -> api.DeleteAuthKeyResponse
44, // 44: api.Ionscale.ListAuthKeys:output_type -> api.ListAuthKeysResponse
45, // 45: api.Ionscale.ListMachines:output_type -> api.ListMachinesResponse
46, // 46: api.Ionscale.ExpireMachineByAuthMethod:output_type -> api.ExpireMachineResponse
47, // 47: api.Ionscale.DeleteMachine:output_type -> api.DeleteMachineResponse
48, // 48: api.Ionscale.GetMachineRoutes:output_type -> api.GetMachineRoutesResponse
48, // 49: api.Ionscale.SetMachineRoutes:output_type -> api.GetMachineRoutesResponse
25, // [25:50] is the sub-list for method output_type
0, // [0:25] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_api_ionscale_proto_init() }
func file_api_ionscale_proto_init() {
if File_api_ionscale_proto != nil {
return
}
file_api_version_proto_init()
file_api_tailnets_proto_init()
file_api_auth_methods_proto_init()
file_api_auth_filter_proto_init()
file_api_auth_keys_proto_init()
file_api_machines_proto_init()
file_api_routes_proto_init()
file_api_dns_proto_init()
file_api_acl_proto_init()
file_api_derp_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_ionscale_proto_rawDesc,
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_api_ionscale_proto_goTypes,
DependencyIndexes: file_api_ionscale_proto_depIdxs,
}.Build()
File_api_ionscale_proto = out.File
file_api_ionscale_proto_rawDesc = nil
file_api_ionscale_proto_goTypes = nil
file_api_ionscale_proto_depIdxs = nil
}
-963
View File
@@ -1,963 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package api
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// IonscaleClient is the client API for Ionscale service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type IonscaleClient interface {
GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*GetVersionResponse, error)
GetDERPMap(ctx context.Context, in *GetDERPMapRequest, opts ...grpc.CallOption) (*GetDERPMapResponse, error)
SetDERPMap(ctx context.Context, in *SetDERPMapRequest, opts ...grpc.CallOption) (*SetDERPMapResponse, error)
CreateAuthMethod(ctx context.Context, in *CreateAuthMethodRequest, opts ...grpc.CallOption) (*CreateAuthMethodResponse, error)
ListAuthMethods(ctx context.Context, in *ListAuthMethodsRequest, opts ...grpc.CallOption) (*ListAuthMethodsResponse, error)
CreateAuthFilter(ctx context.Context, in *CreateAuthFilterRequest, opts ...grpc.CallOption) (*CreateAuthFilterResponse, error)
DeleteAuthFilter(ctx context.Context, in *DeleteAuthFilterRequest, opts ...grpc.CallOption) (*DeleteAuthFilterResponse, error)
ListAuthFilters(ctx context.Context, in *ListAuthFiltersRequest, opts ...grpc.CallOption) (*ListAuthFiltersResponse, error)
CreateTailnet(ctx context.Context, in *CreateTailnetRequest, opts ...grpc.CallOption) (*CreateTailnetResponse, error)
GetTailnet(ctx context.Context, in *GetTailnetRequest, opts ...grpc.CallOption) (*GetTailnetResponse, error)
ListTailnets(ctx context.Context, in *ListTailnetRequest, opts ...grpc.CallOption) (*ListTailnetResponse, error)
DeleteTailnet(ctx context.Context, in *DeleteTailnetRequest, opts ...grpc.CallOption) (*DeleteTailnetResponse, error)
GetDNSConfig(ctx context.Context, in *GetDNSConfigRequest, opts ...grpc.CallOption) (*GetDNSConfigResponse, error)
SetDNSConfig(ctx context.Context, in *SetDNSConfigRequest, opts ...grpc.CallOption) (*SetDNSConfigResponse, error)
GetACLPolicy(ctx context.Context, in *GetACLPolicyRequest, opts ...grpc.CallOption) (*GetACLPolicyResponse, error)
SetACLPolicy(ctx context.Context, in *SetACLPolicyRequest, opts ...grpc.CallOption) (*SetACLPolicyResponse, error)
GetAuthKey(ctx context.Context, in *GetAuthKeyRequest, opts ...grpc.CallOption) (*GetAuthKeyResponse, error)
CreateAuthKey(ctx context.Context, in *CreateAuthKeyRequest, opts ...grpc.CallOption) (*CreateAuthKeyResponse, error)
DeleteAuthKey(ctx context.Context, in *DeleteAuthKeyRequest, opts ...grpc.CallOption) (*DeleteAuthKeyResponse, error)
ListAuthKeys(ctx context.Context, in *ListAuthKeysRequest, opts ...grpc.CallOption) (*ListAuthKeysResponse, error)
ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error)
ExpireMachine(ctx context.Context, in *ExpireMachineRequest, opts ...grpc.CallOption) (*ExpireMachineResponse, error)
DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error)
GetMachineRoutes(ctx context.Context, in *GetMachineRoutesRequest, opts ...grpc.CallOption) (*GetMachineRoutesResponse, error)
SetMachineRoutes(ctx context.Context, in *SetMachineRoutesRequest, opts ...grpc.CallOption) (*GetMachineRoutesResponse, error)
}
type ionscaleClient struct {
cc grpc.ClientConnInterface
}
func NewIonscaleClient(cc grpc.ClientConnInterface) IonscaleClient {
return &ionscaleClient{cc}
}
func (c *ionscaleClient) GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*GetVersionResponse, error) {
out := new(GetVersionResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/GetVersion", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) GetDERPMap(ctx context.Context, in *GetDERPMapRequest, opts ...grpc.CallOption) (*GetDERPMapResponse, error) {
out := new(GetDERPMapResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/GetDERPMap", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) SetDERPMap(ctx context.Context, in *SetDERPMapRequest, opts ...grpc.CallOption) (*SetDERPMapResponse, error) {
out := new(SetDERPMapResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/SetDERPMap", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) CreateAuthMethod(ctx context.Context, in *CreateAuthMethodRequest, opts ...grpc.CallOption) (*CreateAuthMethodResponse, error) {
out := new(CreateAuthMethodResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/CreateAuthMethod", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) ListAuthMethods(ctx context.Context, in *ListAuthMethodsRequest, opts ...grpc.CallOption) (*ListAuthMethodsResponse, error) {
out := new(ListAuthMethodsResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/ListAuthMethods", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) CreateAuthFilter(ctx context.Context, in *CreateAuthFilterRequest, opts ...grpc.CallOption) (*CreateAuthFilterResponse, error) {
out := new(CreateAuthFilterResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/CreateAuthFilter", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) DeleteAuthFilter(ctx context.Context, in *DeleteAuthFilterRequest, opts ...grpc.CallOption) (*DeleteAuthFilterResponse, error) {
out := new(DeleteAuthFilterResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/DeleteAuthFilter", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) ListAuthFilters(ctx context.Context, in *ListAuthFiltersRequest, opts ...grpc.CallOption) (*ListAuthFiltersResponse, error) {
out := new(ListAuthFiltersResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/ListAuthFilters", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) CreateTailnet(ctx context.Context, in *CreateTailnetRequest, opts ...grpc.CallOption) (*CreateTailnetResponse, error) {
out := new(CreateTailnetResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/CreateTailnet", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) GetTailnet(ctx context.Context, in *GetTailnetRequest, opts ...grpc.CallOption) (*GetTailnetResponse, error) {
out := new(GetTailnetResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/GetTailnet", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) ListTailnets(ctx context.Context, in *ListTailnetRequest, opts ...grpc.CallOption) (*ListTailnetResponse, error) {
out := new(ListTailnetResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/ListTailnets", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) DeleteTailnet(ctx context.Context, in *DeleteTailnetRequest, opts ...grpc.CallOption) (*DeleteTailnetResponse, error) {
out := new(DeleteTailnetResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/DeleteTailnet", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) GetDNSConfig(ctx context.Context, in *GetDNSConfigRequest, opts ...grpc.CallOption) (*GetDNSConfigResponse, error) {
out := new(GetDNSConfigResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/GetDNSConfig", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) SetDNSConfig(ctx context.Context, in *SetDNSConfigRequest, opts ...grpc.CallOption) (*SetDNSConfigResponse, error) {
out := new(SetDNSConfigResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/SetDNSConfig", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) GetACLPolicy(ctx context.Context, in *GetACLPolicyRequest, opts ...grpc.CallOption) (*GetACLPolicyResponse, error) {
out := new(GetACLPolicyResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/GetACLPolicy", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) SetACLPolicy(ctx context.Context, in *SetACLPolicyRequest, opts ...grpc.CallOption) (*SetACLPolicyResponse, error) {
out := new(SetACLPolicyResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/SetACLPolicy", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) GetAuthKey(ctx context.Context, in *GetAuthKeyRequest, opts ...grpc.CallOption) (*GetAuthKeyResponse, error) {
out := new(GetAuthKeyResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/GetAuthKey", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) CreateAuthKey(ctx context.Context, in *CreateAuthKeyRequest, opts ...grpc.CallOption) (*CreateAuthKeyResponse, error) {
out := new(CreateAuthKeyResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/CreateAuthKey", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) DeleteAuthKey(ctx context.Context, in *DeleteAuthKeyRequest, opts ...grpc.CallOption) (*DeleteAuthKeyResponse, error) {
out := new(DeleteAuthKeyResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/DeleteAuthKey", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) ListAuthKeys(ctx context.Context, in *ListAuthKeysRequest, opts ...grpc.CallOption) (*ListAuthKeysResponse, error) {
out := new(ListAuthKeysResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/ListAuthKeys", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error) {
out := new(ListMachinesResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/ListMachines", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) ExpireMachine(ctx context.Context, in *ExpireMachineRequest, opts ...grpc.CallOption) (*ExpireMachineResponse, error) {
out := new(ExpireMachineResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/ExpireMachineByAuthMethod", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error) {
out := new(DeleteMachineResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/DeleteMachine", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) GetMachineRoutes(ctx context.Context, in *GetMachineRoutesRequest, opts ...grpc.CallOption) (*GetMachineRoutesResponse, error) {
out := new(GetMachineRoutesResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/GetMachineRoutes", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) SetMachineRoutes(ctx context.Context, in *SetMachineRoutesRequest, opts ...grpc.CallOption) (*GetMachineRoutesResponse, error) {
out := new(GetMachineRoutesResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/SetMachineRoutes", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// IonscaleServer is the server API for Ionscale service.
// All implementations should embed UnimplementedIonscaleServer
// for forward compatibility
type IonscaleServer interface {
GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error)
GetDERPMap(context.Context, *GetDERPMapRequest) (*GetDERPMapResponse, error)
SetDERPMap(context.Context, *SetDERPMapRequest) (*SetDERPMapResponse, error)
CreateAuthMethod(context.Context, *CreateAuthMethodRequest) (*CreateAuthMethodResponse, error)
ListAuthMethods(context.Context, *ListAuthMethodsRequest) (*ListAuthMethodsResponse, error)
CreateAuthFilter(context.Context, *CreateAuthFilterRequest) (*CreateAuthFilterResponse, error)
DeleteAuthFilter(context.Context, *DeleteAuthFilterRequest) (*DeleteAuthFilterResponse, error)
ListAuthFilters(context.Context, *ListAuthFiltersRequest) (*ListAuthFiltersResponse, error)
CreateTailnet(context.Context, *CreateTailnetRequest) (*CreateTailnetResponse, error)
GetTailnet(context.Context, *GetTailnetRequest) (*GetTailnetResponse, error)
ListTailnets(context.Context, *ListTailnetRequest) (*ListTailnetResponse, error)
DeleteTailnet(context.Context, *DeleteTailnetRequest) (*DeleteTailnetResponse, error)
GetDNSConfig(context.Context, *GetDNSConfigRequest) (*GetDNSConfigResponse, error)
SetDNSConfig(context.Context, *SetDNSConfigRequest) (*SetDNSConfigResponse, error)
GetACLPolicy(context.Context, *GetACLPolicyRequest) (*GetACLPolicyResponse, error)
SetACLPolicy(context.Context, *SetACLPolicyRequest) (*SetACLPolicyResponse, error)
GetAuthKey(context.Context, *GetAuthKeyRequest) (*GetAuthKeyResponse, error)
CreateAuthKey(context.Context, *CreateAuthKeyRequest) (*CreateAuthKeyResponse, error)
DeleteAuthKey(context.Context, *DeleteAuthKeyRequest) (*DeleteAuthKeyResponse, error)
ListAuthKeys(context.Context, *ListAuthKeysRequest) (*ListAuthKeysResponse, error)
ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error)
ExpireMachine(context.Context, *ExpireMachineRequest) (*ExpireMachineResponse, error)
DeleteMachine(context.Context, *DeleteMachineRequest) (*DeleteMachineResponse, error)
GetMachineRoutes(context.Context, *GetMachineRoutesRequest) (*GetMachineRoutesResponse, error)
SetMachineRoutes(context.Context, *SetMachineRoutesRequest) (*GetMachineRoutesResponse, error)
}
// UnimplementedIonscaleServer should be embedded to have forward compatible implementations.
type UnimplementedIonscaleServer struct {
}
func (UnimplementedIonscaleServer) GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented")
}
func (UnimplementedIonscaleServer) GetDERPMap(context.Context, *GetDERPMapRequest) (*GetDERPMapResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetDERPMap not implemented")
}
func (UnimplementedIonscaleServer) SetDERPMap(context.Context, *SetDERPMapRequest) (*SetDERPMapResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetDERPMap not implemented")
}
func (UnimplementedIonscaleServer) CreateAuthMethod(context.Context, *CreateAuthMethodRequest) (*CreateAuthMethodResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateAuthMethod not implemented")
}
func (UnimplementedIonscaleServer) ListAuthMethods(context.Context, *ListAuthMethodsRequest) (*ListAuthMethodsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAuthMethods not implemented")
}
func (UnimplementedIonscaleServer) CreateAuthFilter(context.Context, *CreateAuthFilterRequest) (*CreateAuthFilterResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateAuthFilter not implemented")
}
func (UnimplementedIonscaleServer) DeleteAuthFilter(context.Context, *DeleteAuthFilterRequest) (*DeleteAuthFilterResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteAuthFilter not implemented")
}
func (UnimplementedIonscaleServer) ListAuthFilters(context.Context, *ListAuthFiltersRequest) (*ListAuthFiltersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAuthFilters not implemented")
}
func (UnimplementedIonscaleServer) CreateTailnet(context.Context, *CreateTailnetRequest) (*CreateTailnetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateTailnet not implemented")
}
func (UnimplementedIonscaleServer) GetTailnet(context.Context, *GetTailnetRequest) (*GetTailnetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetTailnet not implemented")
}
func (UnimplementedIonscaleServer) ListTailnets(context.Context, *ListTailnetRequest) (*ListTailnetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListTailnets not implemented")
}
func (UnimplementedIonscaleServer) DeleteTailnet(context.Context, *DeleteTailnetRequest) (*DeleteTailnetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteTailnet not implemented")
}
func (UnimplementedIonscaleServer) GetDNSConfig(context.Context, *GetDNSConfigRequest) (*GetDNSConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetDNSConfig not implemented")
}
func (UnimplementedIonscaleServer) SetDNSConfig(context.Context, *SetDNSConfigRequest) (*SetDNSConfigResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetDNSConfig not implemented")
}
func (UnimplementedIonscaleServer) GetACLPolicy(context.Context, *GetACLPolicyRequest) (*GetACLPolicyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetACLPolicy not implemented")
}
func (UnimplementedIonscaleServer) SetACLPolicy(context.Context, *SetACLPolicyRequest) (*SetACLPolicyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetACLPolicy not implemented")
}
func (UnimplementedIonscaleServer) GetAuthKey(context.Context, *GetAuthKeyRequest) (*GetAuthKeyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAuthKey not implemented")
}
func (UnimplementedIonscaleServer) CreateAuthKey(context.Context, *CreateAuthKeyRequest) (*CreateAuthKeyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateAuthKey not implemented")
}
func (UnimplementedIonscaleServer) DeleteAuthKey(context.Context, *DeleteAuthKeyRequest) (*DeleteAuthKeyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteAuthKey not implemented")
}
func (UnimplementedIonscaleServer) ListAuthKeys(context.Context, *ListAuthKeysRequest) (*ListAuthKeysResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAuthKeys not implemented")
}
func (UnimplementedIonscaleServer) ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListMachines not implemented")
}
func (UnimplementedIonscaleServer) ExpireMachine(context.Context, *ExpireMachineRequest) (*ExpireMachineResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ExpireMachineByAuthMethod not implemented")
}
func (UnimplementedIonscaleServer) DeleteMachine(context.Context, *DeleteMachineRequest) (*DeleteMachineResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteMachine not implemented")
}
func (UnimplementedIonscaleServer) GetMachineRoutes(context.Context, *GetMachineRoutesRequest) (*GetMachineRoutesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMachineRoutes not implemented")
}
func (UnimplementedIonscaleServer) SetMachineRoutes(context.Context, *SetMachineRoutesRequest) (*GetMachineRoutesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetMachineRoutes not implemented")
}
// UnsafeIonscaleServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to IonscaleServer will
// result in compilation errors.
type UnsafeIonscaleServer interface {
mustEmbedUnimplementedIonscaleServer()
}
func RegisterIonscaleServer(s grpc.ServiceRegistrar, srv IonscaleServer) {
s.RegisterService(&Ionscale_ServiceDesc, srv)
}
func _Ionscale_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetVersionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).GetVersion(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/GetVersion",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).GetVersion(ctx, req.(*GetVersionRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_GetDERPMap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetDERPMapRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).GetDERPMap(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/GetDERPMap",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).GetDERPMap(ctx, req.(*GetDERPMapRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_SetDERPMap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetDERPMapRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).SetDERPMap(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/SetDERPMap",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).SetDERPMap(ctx, req.(*SetDERPMapRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_CreateAuthMethod_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateAuthMethodRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).CreateAuthMethod(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/CreateAuthMethod",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).CreateAuthMethod(ctx, req.(*CreateAuthMethodRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_ListAuthMethods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListAuthMethodsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).ListAuthMethods(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/ListAuthMethods",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).ListAuthMethods(ctx, req.(*ListAuthMethodsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_CreateAuthFilter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateAuthFilterRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).CreateAuthFilter(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/CreateAuthFilter",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).CreateAuthFilter(ctx, req.(*CreateAuthFilterRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_DeleteAuthFilter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteAuthFilterRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).DeleteAuthFilter(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/DeleteAuthFilter",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).DeleteAuthFilter(ctx, req.(*DeleteAuthFilterRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_ListAuthFilters_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListAuthFiltersRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).ListAuthFilters(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/ListAuthFilters",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).ListAuthFilters(ctx, req.(*ListAuthFiltersRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_CreateTailnet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateTailnetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).CreateTailnet(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/CreateTailnet",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).CreateTailnet(ctx, req.(*CreateTailnetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_GetTailnet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetTailnetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).GetTailnet(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/GetTailnet",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).GetTailnet(ctx, req.(*GetTailnetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_ListTailnets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListTailnetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).ListTailnets(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/ListTailnets",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).ListTailnets(ctx, req.(*ListTailnetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_DeleteTailnet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteTailnetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).DeleteTailnet(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/DeleteTailnet",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).DeleteTailnet(ctx, req.(*DeleteTailnetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_GetDNSConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetDNSConfigRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).GetDNSConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/GetDNSConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).GetDNSConfig(ctx, req.(*GetDNSConfigRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_SetDNSConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetDNSConfigRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).SetDNSConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/SetDNSConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).SetDNSConfig(ctx, req.(*SetDNSConfigRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_GetACLPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetACLPolicyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).GetACLPolicy(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/GetACLPolicy",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).GetACLPolicy(ctx, req.(*GetACLPolicyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_SetACLPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetACLPolicyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).SetACLPolicy(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/SetACLPolicy",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).SetACLPolicy(ctx, req.(*SetACLPolicyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_GetAuthKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetAuthKeyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).GetAuthKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/GetAuthKey",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).GetAuthKey(ctx, req.(*GetAuthKeyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_CreateAuthKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateAuthKeyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).CreateAuthKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/CreateAuthKey",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).CreateAuthKey(ctx, req.(*CreateAuthKeyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_DeleteAuthKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteAuthKeyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).DeleteAuthKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/DeleteAuthKey",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).DeleteAuthKey(ctx, req.(*DeleteAuthKeyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_ListAuthKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListAuthKeysRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).ListAuthKeys(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/ListAuthKeys",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).ListAuthKeys(ctx, req.(*ListAuthKeysRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_ListMachines_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListMachinesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).ListMachines(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/ListMachines",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).ListMachines(ctx, req.(*ListMachinesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_ExpireMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ExpireMachineRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).ExpireMachine(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/ExpireMachineByAuthMethod",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).ExpireMachine(ctx, req.(*ExpireMachineRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_DeleteMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteMachineRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).DeleteMachine(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/DeleteMachine",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).DeleteMachine(ctx, req.(*DeleteMachineRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_GetMachineRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetMachineRoutesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).GetMachineRoutes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/GetMachineRoutes",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).GetMachineRoutes(ctx, req.(*GetMachineRoutesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_SetMachineRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetMachineRoutesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).SetMachineRoutes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/SetMachineRoutes",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).SetMachineRoutes(ctx, req.(*SetMachineRoutesRequest))
}
return interceptor(ctx, in, info, handler)
}
// Ionscale_ServiceDesc is the grpc.ServiceDesc for Ionscale service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Ionscale_ServiceDesc = grpc.ServiceDesc{
ServiceName: "api.Ionscale",
HandlerType: (*IonscaleServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetVersion",
Handler: _Ionscale_GetVersion_Handler,
},
{
MethodName: "GetDERPMap",
Handler: _Ionscale_GetDERPMap_Handler,
},
{
MethodName: "SetDERPMap",
Handler: _Ionscale_SetDERPMap_Handler,
},
{
MethodName: "CreateAuthMethod",
Handler: _Ionscale_CreateAuthMethod_Handler,
},
{
MethodName: "ListAuthMethods",
Handler: _Ionscale_ListAuthMethods_Handler,
},
{
MethodName: "CreateAuthFilter",
Handler: _Ionscale_CreateAuthFilter_Handler,
},
{
MethodName: "DeleteAuthFilter",
Handler: _Ionscale_DeleteAuthFilter_Handler,
},
{
MethodName: "ListAuthFilters",
Handler: _Ionscale_ListAuthFilters_Handler,
},
{
MethodName: "CreateTailnet",
Handler: _Ionscale_CreateTailnet_Handler,
},
{
MethodName: "GetTailnet",
Handler: _Ionscale_GetTailnet_Handler,
},
{
MethodName: "ListTailnets",
Handler: _Ionscale_ListTailnets_Handler,
},
{
MethodName: "DeleteTailnet",
Handler: _Ionscale_DeleteTailnet_Handler,
},
{
MethodName: "GetDNSConfig",
Handler: _Ionscale_GetDNSConfig_Handler,
},
{
MethodName: "SetDNSConfig",
Handler: _Ionscale_SetDNSConfig_Handler,
},
{
MethodName: "GetACLPolicy",
Handler: _Ionscale_GetACLPolicy_Handler,
},
{
MethodName: "SetACLPolicy",
Handler: _Ionscale_SetACLPolicy_Handler,
},
{
MethodName: "GetAuthKey",
Handler: _Ionscale_GetAuthKey_Handler,
},
{
MethodName: "CreateAuthKey",
Handler: _Ionscale_CreateAuthKey_Handler,
},
{
MethodName: "DeleteAuthKey",
Handler: _Ionscale_DeleteAuthKey_Handler,
},
{
MethodName: "ListAuthKeys",
Handler: _Ionscale_ListAuthKeys_Handler,
},
{
MethodName: "ListMachines",
Handler: _Ionscale_ListMachines_Handler,
},
{
MethodName: "ExpireMachineByAuthMethod",
Handler: _Ionscale_ExpireMachine_Handler,
},
{
MethodName: "DeleteMachine",
Handler: _Ionscale_DeleteMachine_Handler,
},
{
MethodName: "GetMachineRoutes",
Handler: _Ionscale_GetMachineRoutes_Handler,
},
{
MethodName: "SetMachineRoutes",
Handler: _Ionscale_SetMachineRoutes_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/ionscale.proto",
}

Some files were not shown because too many files have changed in this diff Show More