Skip to content

Commit

Permalink
feat: use hujson as data format for ACL and IAM policy
Browse files Browse the repository at this point in the history
  • Loading branch information
jsiebens committed Mar 15, 2024
1 parent a1debdf commit 6173621
Show file tree
Hide file tree
Showing 36 changed files with 751 additions and 1,414 deletions.
34 changes: 4 additions & 30 deletions internal/cmd/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"bytes"
"encoding/json"
"fmt"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/go-edit/editor"
Expand All @@ -25,12 +24,7 @@ func getACLConfigCommand() *cobra.Command {
return err
}

marshal, err := json.MarshalIndent(resp.Msg.Policy, "", " ")
if err != nil {
return err
}

fmt.Println(string(marshal))
fmt.Println(resp.Msg.Policy)

return nil
}
Expand All @@ -53,12 +47,7 @@ func editACLConfigCommand() *cobra.Command {
return err
}

previous, err := json.MarshalIndent(resp.Msg.Policy, "", " ")
if err != nil {
return err
}

next, s, err := edit.LaunchTempFile("ionscale", ".json", bytes.NewReader(previous))
next, s, err := edit.LaunchTempFile("ionscale", ".json", bytes.NewReader([]byte(resp.Msg.Policy)))
if err != nil {
return err
}
Expand All @@ -70,12 +59,7 @@ func editACLConfigCommand() *cobra.Command {
return err
}

var policy = &api.ACLPolicy{}
if err := json.Unmarshal(next, policy); err != nil {
return err
}

_, err = tc.Client().SetACLPolicy(cmd.Context(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy}))
_, err = tc.Client().SetACLPolicy(cmd.Context(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tc.TailnetID(), Policy: string(next)}))
if err != nil {
return err
}
Expand Down Expand Up @@ -105,17 +89,7 @@ func setACLConfigCommand() *cobra.Command {
return err
}

rawJson, err := hujson.Standardize(content)
if err != nil {
return err
}

var policy = &api.ACLPolicy{}
if err := json.Unmarshal(rawJson, policy); err != nil {
return err
}

_, err = tc.Client().SetACLPolicy(cmd.Context(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy}))
_, err = tc.Client().SetACLPolicy(cmd.Context(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tc.TailnetID(), Policy: string(content)}))
if err != nil {
return err
}
Expand Down
40 changes: 4 additions & 36 deletions internal/cmd/iam.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ package cmd

import (
"bytes"
"encoding/json"
"fmt"
"github.com/bufbuild/connect-go"
"github.com/jsiebens/go-edit/editor"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/spf13/cobra"
"github.com/tailscale/hujson"
"os"
)

Expand All @@ -25,12 +23,7 @@ func getIAMPolicyCommand() *cobra.Command {
return err
}

marshal, err := json.MarshalIndent(resp.Msg.Policy, "", " ")
if err != nil {
return err
}

fmt.Println(string(marshal))
fmt.Println(resp.Msg.Policy)

return nil
}
Expand All @@ -53,29 +46,14 @@ func editIAMPolicyCommand() *cobra.Command {
return err
}

previous, err := json.MarshalIndent(resp.Msg.Policy, "", " ")
if err != nil {
return err
}

next, s, err := edit.LaunchTempFile("ionscale", ".json", bytes.NewReader(previous))
if err != nil {
return err
}

next, err = hujson.Standardize(next)
next, s, err := edit.LaunchTempFile("ionscale", ".json", bytes.NewReader([]byte(resp.Msg.Policy)))
if err != nil {
return err
}

defer os.Remove(s)

var policy = &api.IAMPolicy{}
if err := json.Unmarshal(next, policy); err != nil {
return err
}

_, err = tc.Client().SetIAMPolicy(cmd.Context(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy}))
_, err = tc.Client().SetIAMPolicy(cmd.Context(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tc.TailnetID(), Policy: string(next)}))
if err != nil {
return err
}
Expand Down Expand Up @@ -105,17 +83,7 @@ func setIAMPolicyCommand() *cobra.Command {
return err
}

rawJson, err := hujson.Standardize(content)
if err != nil {
return err
}

var policy = &api.IAMPolicy{}
if err := json.Unmarshal(rawJson, policy); err != nil {
return err
}

_, err = tc.Client().SetIAMPolicy(cmd.Context(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy}))
_, err = tc.Client().SetIAMPolicy(cmd.Context(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tc.TailnetID(), Policy: string(content)}))
if err != nil {
return err
}
Expand Down
17 changes: 13 additions & 4 deletions internal/cmd/tailnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"github.com/bufbuild/connect-go"
idomain "github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/pkg/client/ionscale"
"github.com/jsiebens/ionscale/pkg/defaults"
api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1"
"github.com/rodaine/table"
Expand Down Expand Up @@ -102,24 +103,32 @@ func createTailnetsCommand() *cobra.Command {
command.RunE = func(cmd *cobra.Command, args []string) error {

dnsConfig := defaults.DefaultDNSConfig()
aclPolicy := defaults.DefaultACLPolicy()
iamPolicy := &api.IAMPolicy{}
aclPolicy := defaults.DefaultACLPolicy().Marshal()
iamPolicy := "{}"

if len(domain) != 0 {
domainToLower := strings.ToLower(domain)
iamPolicy = &api.IAMPolicy{
m, err := json.MarshalIndent(&ionscale.IAMPolicy{
Filters: []string{fmt.Sprintf("domain == %s", domainToLower)},
}, "", " ")
if err != nil {
return err
}
iamPolicy = string(m)
}

if len(email) != 0 {
emailToLower := strings.ToLower(email)
iamPolicy = &api.IAMPolicy{
m, err := json.MarshalIndent(&ionscale.IAMPolicy{
Emails: []string{emailToLower},
Roles: map[string]string{
emailToLower: string(idomain.UserRoleAdmin),
},
}, "", " ")
if err != nil {
return err
}
iamPolicy = string(m)
}

resp, err := tc.Client().CreateTailnet(cmd.Context(), connect.NewRequest(&api.CreateTailnetRequest{
Expand Down
29 changes: 29 additions & 0 deletions internal/database/migration/m202403130830_json_to_text.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package migration

import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

func m202403130830_json_to_text() *gormigrate.Migration {
return &gormigrate.Migration{
ID: "202403130830",
Migrate: func(db *gorm.DB) error {
type Tailnet struct {
IAMPolicy string
ACLPolicy string
}

if err := db.Migrator().AlterColumn(&Tailnet{}, "IAMPolicy"); err != nil {
return err
}

if err := db.Migrator().AlterColumn(&Tailnet{}, "ACLPolicy"); err != nil {
return err
}

return nil
},
Rollback: nil,
}
}
1 change: 1 addition & 0 deletions internal/database/migration/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func Migrations() []*gormigrate.Migration {
m202312290900_machine_indeces(),
m202401061400_machine_indeces(),
m202402120800_user_last_authenticated(),
m202403130830_json_to_text(),
}
return migrations
}
37 changes: 2 additions & 35 deletions internal/domain/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"github.com/hashicorp/go-multierror"
"github.com/jsiebens/ionscale/pkg/client/ionscale"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"net/netip"
Expand All @@ -30,41 +31,7 @@ type AutoApprovers struct {
}

type ACLPolicy struct {
Groups map[string][]string `json:"groups,omitempty"`
Hosts map[string]string `json:"hosts,omitempty"`
ACLs []ACL `json:"acls,omitempty"`
TagOwners map[string][]string `json:"tagowners,omitempty"`
AutoApprovers *AutoApprovers `json:"autoApprovers,omitempty"`
SSHRules []SSHRule `json:"ssh,omitempty"`
NodeAttrs []NodeAttr `json:"nodeAttrs,omitempty"`
Grants []Grant `json:"grants,omitempty"`
}

type ACL struct {
Action string `json:"action"`
Proto string `json:"proto"`
Src []string `json:"src"`
Dst []string `json:"dst"`
}

type SSHRule struct {
Action string `json:"action"`
Src []string `json:"src"`
Dst []string `json:"dst"`
Users []string `json:"users"`
CheckPeriod string `json:"checkPeriod,omitempty"`
}

type NodeAttr struct {
Target []string `json:"target"`
Attr []string `json:"attr"`
}

type Grant struct {
Src []string `json:"src"`
Dst []string `json:"dst"`
IP []tailcfg.ProtoPortRange `json:"ip"`
App tailcfg.PeerCapMap `json:"app"`
ionscale.ACLPolicy
}

func (a *ACLPolicy) Equal(x *ACLPolicy) bool {
Expand Down
31 changes: 16 additions & 15 deletions internal/domain/acl_filter_rules.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package domain

import (
"github.com/jsiebens/ionscale/pkg/client/ionscale"
"net/netip"
"strings"
"tailscale.com/tailcfg"
Expand All @@ -12,16 +13,16 @@ func (a ACLPolicy) IsValidPeer(src *Machine, dest *Machine) bool {
}

for _, acl := range a.ACLs {
selfDestPorts, allDestPorts := a.translateDestinationAliasesToMachineNetPortRanges(acl.Dst, dest)
selfDestPorts, allDestPorts := a.translateDestinationAliasesToMachineNetPortRanges(acl.Destination, dest)
if len(selfDestPorts) != 0 {
for _, alias := range acl.Src {
for _, alias := range acl.Source {
if len(a.translateSourceAliasToMachineIPs(alias, src, &dest.User)) != 0 {
return true
}
}
}
if len(allDestPorts) != 0 {
for _, alias := range acl.Src {
for _, alias := range acl.Source {
if len(a.translateSourceAliasToMachineIPs(alias, src, nil)) != 0 {
return true
}
Expand All @@ -30,16 +31,16 @@ func (a ACLPolicy) IsValidPeer(src *Machine, dest *Machine) bool {
}

for _, grant := range a.Grants {
selfIps, otherIps := a.translateDestinationAliasesToMachineIPs(grant.Dst, dest)
selfIps, otherIps := a.translateDestinationAliasesToMachineIPs(grant.Destination, dest)
if len(selfIps) != 0 {
for _, alias := range grant.Src {
for _, alias := range grant.Source {
if len(a.translateSourceAliasToMachineIPs(alias, src, &dest.User)) != 0 {
return true
}
}
}
if len(otherIps) != 0 {
for _, alias := range grant.Src {
for _, alias := range grant.Source {
if len(a.translateSourceAliasToMachineIPs(alias, src, nil)) != 0 {
return true
}
Expand Down Expand Up @@ -89,23 +90,23 @@ func (a ACLPolicy) BuildFilterRules(peers []Machine, dst *Machine) []tailcfg.Fil

for _, acl := range a.ACLs {
self, other := a.prepareFilterRulesFromACL(dst, acl)
rules = matchSourceAndAppendRule(rules, acl.Src, self, &dst.User)
rules = matchSourceAndAppendRule(rules, acl.Src, other, nil)
rules = matchSourceAndAppendRule(rules, acl.Source, self, &dst.User)
rules = matchSourceAndAppendRule(rules, acl.Source, other, nil)
}

for _, acl := range a.Grants {
self, other := a.prepareFilterRulesFromGrant(dst, acl)
rules = matchSourceAndAppendRule(rules, acl.Src, self, &dst.User)
rules = matchSourceAndAppendRule(rules, acl.Src, other, nil)
rules = matchSourceAndAppendRule(rules, acl.Source, self, &dst.User)
rules = matchSourceAndAppendRule(rules, acl.Source, other, nil)
}

return rules
}

func (a ACLPolicy) prepareFilterRulesFromACL(candidate *Machine, acl ACL) ([]tailcfg.FilterRule, []tailcfg.FilterRule) {
proto := parseProtocol(acl.Proto)
func (a ACLPolicy) prepareFilterRulesFromACL(candidate *Machine, acl ionscale.ACLEntry) ([]tailcfg.FilterRule, []tailcfg.FilterRule) {
proto := parseProtocol(acl.Protocol)

selfDstPorts, otherDstPorts := a.translateDestinationAliasesToMachineNetPortRanges(acl.Dst, candidate)
selfDstPorts, otherDstPorts := a.translateDestinationAliasesToMachineNetPortRanges(acl.Destination, candidate)

var selfFilterRules []tailcfg.FilterRule
var otherFilterRules []tailcfg.FilterRule
Expand All @@ -121,8 +122,8 @@ func (a ACLPolicy) prepareFilterRulesFromACL(candidate *Machine, acl ACL) ([]tai
return selfFilterRules, otherFilterRules
}

func (a ACLPolicy) prepareFilterRulesFromGrant(candidate *Machine, grant Grant) ([]tailcfg.FilterRule, []tailcfg.FilterRule) {
selfIPs, otherIPs := a.translateDestinationAliasesToMachineIPs(grant.Dst, candidate)
func (a ACLPolicy) prepareFilterRulesFromGrant(candidate *Machine, grant ionscale.ACLGrant) ([]tailcfg.FilterRule, []tailcfg.FilterRule) {
selfIPs, otherIPs := a.translateDestinationAliasesToMachineIPs(grant.Destination, candidate)

var selfFilterRules []tailcfg.FilterRule
var otherFilterRules []tailcfg.FilterRule
Expand Down
Loading

0 comments on commit 6173621

Please sign in to comment.