1
0
mirror of synced 2026-03-01 19:26:55 +00:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Stefan Prodan
eb2eb004ed Update RFC to reflect Flux v0.26 multi-tenant capabilities
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2022-02-03 11:04:58 +02:00
Stefan Prodan
c3e5b18b8c [RFC-0003] Flux Multi-Tenancy Mode
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2022-02-03 11:04:53 +02:00
38 changed files with 276 additions and 431 deletions

View File

@@ -254,7 +254,6 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithKubeconfig(kubeconfigArgs),
bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
}
if bootstrapArgs.sshHostname != "" {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))

View File

@@ -243,7 +243,6 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithKubeconfig(kubeconfigArgs),
bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
}
if bootstrapArgs.sshHostname != "" {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))

View File

@@ -257,7 +257,6 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithKubeconfig(kubeconfigArgs),
bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
}
if bootstrapArgs.sshHostname != "" {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))

View File

@@ -23,7 +23,7 @@ import (
var diffCmd = &cobra.Command{
Use: "diff",
Short: "Diff a flux resource",
Long: "The diff command is used to do a server-side dry-run on flux resources, then prints the diff.",
Long: "The diff command is used to do a server-side dry-run on flux resources, then output the diff.",
}
func init() {

View File

@@ -31,9 +31,8 @@ var diffKsCmd = &cobra.Command{
Use: "kustomization",
Aliases: []string{"ks"},
Short: "Diff Kustomization",
Long: `The diff command does a build, then it performs a server-side dry-run and prints the diff.
Exit status: 0 No differences were found. 1 Differences were found. >1 diff failed with an error.`,
Example: `# Preview local changes as they were applied on the cluster
Long: `The diff command does a build, then it performs a server-side dry-run and output the diff.`,
Example: `# Preview changes local changes as they were applied on the cluster
flux diff kustomization my-app --path ./path/to/local/manifests`,
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
RunE: diffKsCmdRun,
@@ -57,16 +56,16 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
name := args[0]
if diffKsArgs.path == "" {
return &RequestError{StatusCode: 2, Err: fmt.Errorf("invalid resource path %q", diffKsArgs.path)}
return fmt.Errorf("invalid resource path %q", diffKsArgs.path)
}
if fs, err := os.Stat(diffKsArgs.path); err != nil || !fs.IsDir() {
return &RequestError{StatusCode: 2, Err: fmt.Errorf("invalid resource path %q", diffKsArgs.path)}
return fmt.Errorf("invalid resource path %q", diffKsArgs.path)
}
builder, err := build.NewBuilder(kubeconfigArgs, name, diffKsArgs.path, build.WithTimeout(rootArgs.timeout))
if err != nil {
return &RequestError{StatusCode: 2, Err: err}
return err
}
// create a signal channel
@@ -75,18 +74,13 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
errChan := make(chan error)
go func() {
output, hasChanged, err := builder.Diff()
output, err := builder.Diff()
if err != nil {
errChan <- &RequestError{StatusCode: 2, Err: err}
errChan <- err
}
cmd.Print(output)
if hasChanged {
errChan <- &RequestError{StatusCode: 1, Err: fmt.Errorf("identified at least one change, exiting with non-zero exit code")}
} else {
errChan <- nil
}
errChan <- nil
}()
select {

View File

@@ -79,18 +79,6 @@ func TestDiffKustomization(t *testing.T) {
objectFile: "./testdata/diff-kustomization/value-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-value-sops-secret.golden"),
},
{
name: "diff with a sops dockerconfigjson secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo",
objectFile: "./testdata/diff-kustomization/dockerconfigjson-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-dockerconfigjson-sops-secret.golden"),
},
{
name: "diff with a sops stringdata secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo",
objectFile: "./testdata/diff-kustomization/stringdata-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-stringdata-sops-secret.golden"),
},
}
tmpl := map[string]string{

View File

@@ -105,16 +105,6 @@ type rootFlags struct {
defaults install.Options
}
// RequestError is a custom error type that wraps an error returned by the flux api.
type RequestError struct {
StatusCode int
Err error
}
func (r *RequestError) Error() string {
return r.Err.Error()
}
var rootArgs = NewRootFlags()
var kubeconfigArgs = genericclioptions.NewConfigFlags(false)
@@ -153,17 +143,6 @@ func NewRootFlags() rootFlags {
func main() {
log.SetFlags(0)
if err := rootCmd.Execute(); err != nil {
if err, ok := err.(*RequestError); ok {
if err.StatusCode == 1 {
logger.Warningf("%v", err)
} else {
logger.Failuref("%v", err)
}
os.Exit(err.StatusCode)
}
logger.Failuref("%v", err)
os.Exit(1)
}

View File

@@ -325,12 +325,6 @@ type cmdTestCase struct {
func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
actual, testErr := executeCommand(cmd.args)
// If the cmd error is a change, discard it
if isChangeError(testErr) {
testErr = nil
}
if assertErr := cmd.assert(actual, testErr); assertErr != nil {
t.Error(assertErr)
}
@@ -372,12 +366,3 @@ func resetCmdArgs() {
getArgs = GetFlags{}
secretGitArgs = NewSecretGitFlags()
}
func isChangeError(err error) bool {
if reqErr, ok := err.(*RequestError); ok {
if strings.Contains(err.Error(), "identified at least one change, exiting with non-zero exit code") && reqErr.StatusCode == 1 {
return true
}
}
return false
}

View File

@@ -48,7 +48,6 @@ func init() {
type resumable interface {
adapter
copyable
statusable
setUnsuspended()
successMessage() string
@@ -98,13 +97,10 @@ func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
for i := 0; i < resume.list.len(); i++ {
logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, resume.list.resumeItem(i).asClientObject().GetName(), *kubeconfigArgs.Namespace)
obj := resume.list.resumeItem(i)
patch := client.MergeFrom(obj.deepCopyClientObject())
obj.setUnsuspended()
if err := kubeClient.Patch(ctx, obj.asClientObject(), patch); err != nil {
resume.list.resumeItem(i).setUnsuspended()
if err := kubeClient.Update(ctx, resume.list.resumeItem(i).asClientObject()); err != nil {
return err
}
logger.Successf("%s resumed", resume.humanKind)
namespacedName := types.NamespacedName{

View File

@@ -46,7 +46,6 @@ func init() {
type suspendable interface {
adapter
copyable
isSuspended() bool
setSuspended()
}
@@ -95,11 +94,8 @@ func (suspend suspendCommand) run(cmd *cobra.Command, args []string) error {
for i := 0; i < suspend.list.len(); i++ {
logger.Actionf("suspending %s %s in %s namespace", suspend.humanKind, suspend.list.item(i).asClientObject().GetName(), *kubeconfigArgs.Namespace)
obj := suspend.list.item(i)
patch := client.MergeFrom(obj.deepCopyClientObject())
obj.setSuspended()
if err := kubeClient.Patch(ctx, obj.asClientObject(), patch); err != nil {
suspend.list.item(i).setSuspended()
if err := kubeClient.Update(ctx, suspend.list.item(i).asClientObject()); err != nil {
return err
}
logger.Successf("%s suspended", suspend.humanKind)

View File

@@ -123,31 +123,6 @@ spec:
type: ClusterIP
---
apiVersion: v1
data:
.dockerconfigjson: eyJtYXNrIjoiKipTT1BTKioifQ==
kind: Secret
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: podinfo
kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }}
name: docker-secret
namespace: default
type: kubernetes.io/dockerconfigjson
---
apiVersion: v1
kind: Secret
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: podinfo
kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }}
name: secret-basic-auth-stringdata
namespace: default
stringData:
password: KipTT1BTKio=
username: KipTT1BTKio=
type: kubernetes.io/basic-auth
---
apiVersion: v1
data:
token: KipTT1BTKio=
kind: Secret

View File

@@ -1,27 +0,0 @@
apiVersion: v1
data:
.dockerconfigjson: ENC[AES256_GCM,data:KHCFH3hNnc+PMfWLFEPjebf3W4z4WXbGFAANRZyZC+07z7wlrTALJM6rn8YslW4tMAWCoAYxblC5WRCszTy0h9rw0U/RGOv5H0qCgnNg/FILFUqhwo9pNfrUH+MEP4M9qxxbLKZwObpHUE7DUsKx1JYAxsI=,iv:q48lqUbUQD+0cbYcjNMZMJLRdGHi78ZmDhNAT2th9tg=,tag:QRI2SZZXQrAcdql3R5AH2g==,type:str]
kind: Secret
metadata:
name: docker-secret
type: kubernetes.io/dockerconfigjson
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age10la2ge0wtvx3qr7datqf7rs4yngxszdal927fs9rukamr8u2pshsvtz7ce
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3eU1CTEJhVXZ4eEVYYkVV
OU90TEcrR2pYckttN0pBanJoSUZWSW1RQXlRCkUydFJ3V1NZUTBuVFF0aC9GUEcw
bUdhNjJWTkoyL1FUVi9Dc1dxUDBkM0UKLS0tIE1sQXkwcWdGaEFuY0RHQTVXM0J6
dWpJcThEbW15V3dXYXpPZklBdW1Hd1kKoIAdmGNPrEctV8h1w8KuvQ5S+BGmgqN9
MgpNmUhJjWhgcQpb5BRYpQesBOgU5TBGK7j58A6DMDKlSiYZsdQchQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2022-02-03T16:03:17Z"
mac: ENC[AES256_GCM,data:AHdYSawajwgAFwlmDN1IPNmT9vWaYKzyVIra2d6sPcjTbZ8/p+VRSRpVm4XZFFsaNnW5AUJaouwXnKYDTmJDXKlr/rQcu9kXqsssQgdzcXaA6l5uJlgsnml8ba7J3OK+iEKMax23mwQEx2EUskCd9ENOwFDkunP02sxqDNOz20k=,iv:8F5OamHt3fAVorf6p+SoIrWoqkcATSGWVoM0EK87S4M=,tag:E1mxXnc7wWkEX5BxhpLtng==,type:str]
pgp: []
encrypted_regex: ^(data|stringData)$
version: 3.7.1

View File

@@ -4,8 +4,6 @@ resources:
- ./deployment.yaml
- ./hpa.yaml
- ./service.yaml
- ./dockerconfigjson-sops-secret.yaml
- ./stringdata-secret.yaml
secretGenerator:
- files:
- token=token.encrypted

View File

@@ -1,28 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-basic-auth-stringdata
type: kubernetes.io/basic-auth
stringData:
username: ENC[AES256_GCM,data:uKiQR48=,iv:jh2lgyAVu7igJAgoJsnOGhjxFyvUAa9lvT21u3hhqpU=,tag:zXM2JEpk3ZEH7WfkcWXXkw==,type:str]
password: ENC[AES256_GCM,data:PyhZmNhy929JGQ==,iv:PBqPaJmSw21+kn4gIlg5VdjLNZyf613z5RUTCesBoVw=,tag:Hjc7DsuUrtsz7PYPdNkL3g==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age10la2ge0wtvx3qr7datqf7rs4yngxszdal927fs9rukamr8u2pshsvtz7ce
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJd0xxbDZhYjVoZzY4YWhK
d2NvMVgrSGRVUGhHRGg3R1FpVURnbmh1TDBzCjcwby85M3JaK09QVk0yZFNMb2NL
c2NQZW5hS1FhYlBHU0VoUzBVYzZYUUUKLS0tIEdaNEw2Y0VjVHpZc3pyYUtLVmJk
NmN3K2VLU0NiZ1d0VHBYbGlCM1lrNmMKeWz3yfFbMNE+ly21oLfc1XnDSPRmnlPP
wIs8lk/qrzVZ45C9GdWnnPeGZZiia46Yop9TxseUS8gCjJ6KCxJCAg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2022-02-06T12:51:07Z"
mac: ENC[AES256_GCM,data:jtdzwj19uxdxvnmXg1HkAkDA6XlKMJOYFy7uLI5t/t11LwGop5Yeo7a4nQEEELehRx9J7B6U6NiySxAxBxWx5uW5vI5c8+069VV6dkiCIefnYSzuoIhQafjlFl1/KvH7VEjIWfHYuXF09v9PEKXkxEHUYDpS3QqQ3ymHRRI08pU=, iv:xX3E7F+AM29Pm8G5oqxRfYu9E7tEBGIaHeCJYgrtFmc=,tag:MJPGusNvu05z939jg8PAwQ==,type:str]
pgp: []
encrypted_regex: ^(data|stringData)$
version: 3.7.1

View File

@@ -1,6 +1,4 @@
► HorizontalPodAutoscaler/default/podinfo created
► Service/default/podinfo created
► Secret/default/docker-secret created
► Secret/default/secret-basic-auth-stringdata created
► Secret/default/podinfo-token-77t89m9b67 created
► Secret/default/db-user-pass-bkbd782d2c created

View File

@@ -1,6 +0,0 @@
► Deployment/default/podinfo created
► HorizontalPodAutoscaler/default/podinfo created
► Service/default/podinfo created
► Secret/default/secret-basic-auth-stringdata created
► Secret/default/podinfo-token-77t89m9b67 created
► Secret/default/db-user-pass-bkbd782d2c created

View File

@@ -1,8 +1,6 @@
► Deployment/default/podinfo created
► HorizontalPodAutoscaler/default/podinfo created
► Service/default/podinfo created
► Secret/default/docker-secret created
► Secret/default/secret-basic-auth-stringdata created
► Secret/default/podinfo-token-77t89m9b67 drifted
data

View File

@@ -1,8 +1,6 @@
► Deployment/default/podinfo created
► HorizontalPodAutoscaler/default/podinfo created
► Service/default/podinfo created
► Secret/default/docker-secret created
► Secret/default/secret-basic-auth-stringdata created
► Secret/default/podinfo-token-77t89m9b67 created
► Secret/default/db-user-pass-bkbd782d2c drifted

View File

@@ -7,7 +7,5 @@ spec.ports.http.port
- 9899
+ 9898
► Secret/default/docker-secret created
► Secret/default/secret-basic-auth-stringdata created
► Secret/default/podinfo-token-77t89m9b67 created
► Secret/default/db-user-pass-bkbd782d2c created

View File

@@ -1,6 +1,4 @@
► Deployment/default/podinfo created
► HorizontalPodAutoscaler/default/podinfo created
► Service/default/podinfo created
► Secret/default/docker-secret created
► Secret/default/secret-basic-auth-stringdata created
► Secret/default/db-user-pass-bkbd782d2c created

View File

@@ -1,6 +0,0 @@
► Deployment/default/podinfo created
► HorizontalPodAutoscaler/default/podinfo created
► Service/default/podinfo created
► Secret/default/docker-secret created
► Secret/default/podinfo-token-77t89m9b67 created
► Secret/default/db-user-pass-bkbd782d2c created

View File

@@ -1,11 +0,0 @@
apiVersion: v1
data:
.dockerconfigjson: eyJtYXNrIjoiKipTT1BTKioifQ==
kind: Secret
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: podinfo
kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }}
name: docker-secret
namespace: default
type: kubernetes.io/dockerconfigjson

View File

@@ -1,7 +1,5 @@
► Deployment/default/podinfo created
► HorizontalPodAutoscaler/default/podinfo created
► Service/default/podinfo created
► Secret/default/docker-secret created
► Secret/default/secret-basic-auth-stringdata created
► Secret/default/podinfo-token-77t89m9b67 created
► Secret/default/db-user-pass-bkbd782d2c created

View File

@@ -1,12 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
labels:
kustomize.toolkit.fluxcd.io/name: podinfo
kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }}
name: secret-basic-auth-stringdata
namespace: default
stringData:
password: KipTT1BTKio=
username: KipTT1BTKio=
type: kubernetes.io/basic-auth

View File

@@ -11,6 +11,6 @@ spec:
branch: main
secretRef:
name: flux-system
timeout: 1m0s
timeout: 20s
url: ssh://git@github.com/example/repo

8
go.mod
View File

@@ -10,23 +10,23 @@ require (
github.com/fluxcd/helm-controller/api v0.16.0
github.com/fluxcd/image-automation-controller/api v0.20.0
github.com/fluxcd/image-reflector-controller/api v0.16.0
github.com/fluxcd/kustomize-controller/api v0.20.1
github.com/fluxcd/kustomize-controller/api v0.20.0
github.com/fluxcd/notification-controller/api v0.21.0
github.com/fluxcd/pkg/apis/kustomize v0.3.1 // indirect
github.com/fluxcd/pkg/apis/meta v0.10.2
github.com/fluxcd/pkg/kustomize v0.0.2
github.com/fluxcd/pkg/runtime v0.12.4
github.com/fluxcd/pkg/ssa v0.13.0
github.com/fluxcd/pkg/ssa v0.12.0
github.com/fluxcd/pkg/ssh v0.3.1
github.com/fluxcd/pkg/untar v0.0.5
github.com/fluxcd/pkg/version v0.0.1
github.com/fluxcd/source-controller/api v0.21.2
github.com/fluxcd/source-controller/api v0.21.1
github.com/go-git/go-git/v5 v5.4.2
github.com/gonvenience/bunt v1.3.2
github.com/gonvenience/ytbx v1.4.2
github.com/google/go-cmp v0.5.6
github.com/google/go-containerregistry v0.2.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/homeport/dyff v1.4.6
github.com/lucasb-eyer/go-colorful v1.2.0

12
go.sum
View File

@@ -230,8 +230,8 @@ github.com/fluxcd/image-automation-controller/api v0.20.0 h1:Z+lxqif0KwccsuNOBZq
github.com/fluxcd/image-automation-controller/api v0.20.0/go.mod h1:XhLYccGUbmJvTTpJ1jAFKZHr2e1GNXy0T85ZBO50mik=
github.com/fluxcd/image-reflector-controller/api v0.16.0 h1:1O1YdoK7LsJgWLyvfZTSbvQcUQCBcgJ573HA0arlQQY=
github.com/fluxcd/image-reflector-controller/api v0.16.0/go.mod h1:OIe3mSXc3OwQiNbiQ9vNXWYtNif31hc7WAbZWlFUUnc=
github.com/fluxcd/kustomize-controller/api v0.20.1 h1:BMOUdKCb6bZ6kepcxU8vz9VcDbl4q6SH9j/mRPl2O1A=
github.com/fluxcd/kustomize-controller/api v0.20.1/go.mod h1:5MdpzJVx8+KiDIRv37zLme992BAOCgE0v1n+NOgs1lo=
github.com/fluxcd/kustomize-controller/api v0.20.0 h1:Vw+2qCxeHMv0y1mfiBgVrMfcfFevBMrRfLEdfPTrb40=
github.com/fluxcd/kustomize-controller/api v0.20.0/go.mod h1:5MdpzJVx8+KiDIRv37zLme992BAOCgE0v1n+NOgs1lo=
github.com/fluxcd/notification-controller/api v0.21.0 h1:D5B3TH5YtSww0SyvW1Ru5xWsh0MgHQanC/a1t3CvXq0=
github.com/fluxcd/notification-controller/api v0.21.0/go.mod h1:gA9/j0kjh7VDuUC2Cubr9twxOdzb/0+ojcE9Lzsc9ug=
github.com/fluxcd/pkg/apis/acl v0.0.3 h1:Lw0ZHdpnO4G7Zy9KjrzwwBmDZQuy4qEjaU/RvA6k1lc=
@@ -245,8 +245,8 @@ github.com/fluxcd/pkg/kustomize v0.0.2/go.mod h1:AFwnf3OqQmpTCuwCARTGpPRMBf0ZFJN
github.com/fluxcd/pkg/runtime v0.12.3/go.mod h1:imJ2xYy/d4PbSinX2IefmZk+iS2c1P5fY0js8mCE4SM=
github.com/fluxcd/pkg/runtime v0.12.4 h1:gA27RG/+adN2/7Qe03PB46Iwmye/MusPCpuS4zQ2fW0=
github.com/fluxcd/pkg/runtime v0.12.4/go.mod h1:gspNvhAqodZgSmK1ZhMtvARBf/NGAlxmaZaIOHkJYsc=
github.com/fluxcd/pkg/ssa v0.13.0 h1:LU4wf7dB9ksYdda0BEWNTBSTd68E5YwWxuPiPLAtw4Y=
github.com/fluxcd/pkg/ssa v0.13.0/go.mod h1:XGVGjUaG152HGN6sSUj+VFK/Th5i5rj2XsXSDdlIMNU=
github.com/fluxcd/pkg/ssa v0.12.0 h1:7nF4UigU9Zk/9P/nbzUP3ah8IRC+BpB64O9iu5VnvEo=
github.com/fluxcd/pkg/ssa v0.12.0/go.mod h1:S+qig7BTOxop0c134y8Yv8/iQST4Kt7S2xXiFkP4VMA=
github.com/fluxcd/pkg/ssh v0.3.1 h1:iQw07bkX2rScactX8WYv+uMDsufFOlg8M3fV2TNs244=
github.com/fluxcd/pkg/ssh v0.3.1/go.mod h1:Sebfv4Um51PvomuYdMvKRncQW5dtKhZ5J5TA+wiHNSQ=
github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk=
@@ -254,8 +254,8 @@ github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7
github.com/fluxcd/pkg/version v0.0.1 h1:/8asQoDXSThz3csiwi4Qo8Zb6blAxLXbtxNgeMJ9bCg=
github.com/fluxcd/pkg/version v0.0.1/go.mod h1:WAF4FEEA9xyhngF8TDxg3UPu5fA1qhEYV8Pmi2Il01Q=
github.com/fluxcd/source-controller/api v0.21.0/go.mod h1:Ab2qDmAUz6ZCp8UaHYLYzxyFrC1FQqEqjxiROb/Rdiw=
github.com/fluxcd/source-controller/api v0.21.2 h1:J0S5NN4V8FPLrkSMXIUoUvj1X/RuTpVJSjIRF414wmc=
github.com/fluxcd/source-controller/api v0.21.2/go.mod h1:Ab2qDmAUz6ZCp8UaHYLYzxyFrC1FQqEqjxiROb/Rdiw=
github.com/fluxcd/source-controller/api v0.21.1 h1:7X39YQHmB1vmIBrHxU+YAqxwtdC9Zh+tdtMKREW3xiQ=
github.com/fluxcd/source-controller/api v0.21.1/go.mod h1:Ab2qDmAUz6ZCp8UaHYLYzxyFrC1FQqEqjxiROb/Rdiw=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=

View File

@@ -275,7 +275,7 @@ func (b *GitProviderBootstrapper) reconcileOrgRepository(ctx context.Context) (g
subOrgs, repoName := splitSubOrganizationsFromRepositoryName(b.repositoryName)
orgRef, err := b.getOrganization(ctx, subOrgs)
if err != nil {
return nil, fmt.Errorf("failed to create new Git repository %q: %w", b.repositoryName, err)
return nil, fmt.Errorf("failed to create new Git repository for the organization %q: %w", orgRef.String(), err)
}
repoRef := newOrgRepositoryRef(*orgRef, repoName)
repoInfo := newRepositoryInfo(b.description, b.defaultBranch, b.visibility)

View File

@@ -20,7 +20,6 @@ import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"sync"
"time"
@@ -37,17 +36,12 @@ import (
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/kyaml/filesys"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
const (
controllerName = "kustomize-controller"
controllerGroup = "kustomize.toolkit.fluxcd.io"
mask = "**SOPS**"
dockercfgSecretType = "kubernetes.io/dockerconfigjson"
typeField = "type"
dataField = "data"
stringDataField = "stringData"
controllerName = "kustomize-controller"
controllerGroup = "kustomize.toolkit.fluxcd.io"
mask = "**SOPS**"
)
var defaultTimeout = 80 * time.Second
@@ -188,7 +182,7 @@ func (b *Builder) build() (m resmap.ResMap, err error) {
}
// make sure secrets are masked
err = maskSopsData(res)
err = trimSopsData(res)
if err != nil {
return
}
@@ -262,131 +256,26 @@ func (b *Builder) setOwnerLabels(res *resource.Resource) error {
return nil
}
func maskSopsData(res *resource.Resource) error {
func trimSopsData(res *resource.Resource) error {
// sopsMess is the base64 encoded mask
sopsMess := base64.StdEncoding.EncodeToString([]byte(mask))
if res.GetKind() == "Secret" {
// get both data and stringdata maps as a secret can have both
dataMap := res.GetDataMap()
stringDataMap := getStringDataMap(res)
asYaml, err := res.AsYAML()
if err != nil {
return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err)
}
// delete any sops data as we don't want to expose it
// assume that both data and stringdata are encrypted
if bytes.Contains(asYaml, []byte("sops:")) && bytes.Contains(asYaml, []byte("mac: ENC[")) {
// delete the sops object
res.PipeE(yaml.FieldClearer{Name: "sops"})
secretType, err := res.GetFieldValue(typeField)
for k, v := range dataMap {
data, err := base64.StdEncoding.DecodeString(v)
if err != nil {
return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err)
if _, ok := err.(base64.CorruptInputError); ok {
return fmt.Errorf("failed to decode secret data: %w", err)
}
}
if v, ok := secretType.(string); ok && v == dockercfgSecretType {
// if the secret is a json docker config secret, we need to mask the data with a json object
err := maskDockerconfigjsonSopsData(dataMap)
if err != nil {
return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err)
}
err = maskDockerconfigjsonSopsData(stringDataMap)
if err != nil {
return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err)
}
} else {
for k := range dataMap {
dataMap[k] = sopsMess
}
for k := range stringDataMap {
stringDataMap[k] = sopsMess
}
}
} else {
err := maskBase64EncryptedSopsData(dataMap, sopsMess)
if err != nil {
return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err)
}
err = maskSopsDataInStringDataSecret(stringDataMap, sopsMess)
if err != nil {
return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err)
if bytes.Contains(data, []byte("sops")) && bytes.Contains(data, []byte("ENC[")) {
dataMap[k] = sopsMess
}
}
// set the data and stringdata maps
res.SetDataMap(dataMap)
if len(stringDataMap) > 0 {
err = res.SetMapField(yaml.NewMapRNode(&stringDataMap), stringDataField)
if err != nil {
return fmt.Errorf("failed to mask secret %s sops data: %w", res.GetName(), err)
}
}
}
return nil
}
func getStringDataMap(rn *resource.Resource) map[string]string {
n, err := rn.Pipe(yaml.Lookup(stringDataField))
if err != nil {
return nil
}
result := map[string]string{}
_ = n.VisitFields(func(node *yaml.MapNode) error {
result[yaml.GetValue(node.Key)] = yaml.GetValue(node.Value)
return nil
})
return result
}
func maskDockerconfigjsonSopsData(dataMap map[string]string) error {
sopsMess := struct {
Mask string `json:"mask"`
}{
Mask: mask,
}
maskJson, err := json.Marshal(sopsMess)
if err != nil {
return err
}
for k := range dataMap {
dataMap[k] = base64.StdEncoding.EncodeToString(maskJson)
}
return nil
}
func maskBase64EncryptedSopsData(dataMap map[string]string, mask string) error {
for k, v := range dataMap {
data, err := base64.StdEncoding.DecodeString(v)
if err != nil {
if _, ok := err.(base64.CorruptInputError); ok {
return err
}
}
if bytes.Contains(data, []byte("sops")) && bytes.Contains(data, []byte("ENC[")) {
dataMap[k] = mask
}
}
return nil
}
func maskSopsDataInStringDataSecret(stringDataMap map[string]string, mask string) error {
for k, v := range stringDataMap {
if bytes.Contains([]byte(v), []byte("sops")) && bytes.Contains([]byte(v), []byte("ENC[")) {
stringDataMap[k] = mask
}
}
return nil

View File

@@ -91,45 +91,6 @@ kind: Secret
metadata:
name: secret-basic-auth
type: kubernetes.io/basic-auth
`,
},
{
name: "secret sops secret",
yamlStr: `apiVersion: v1
data:
.dockerconfigjson: ENC[AES256_GCM,data:KHCFH3hNnc+PMfWLFEPjebf3W4z4WXbGFAANRZyZC+07z7wlrTALJM6rn8YslW4tMAWCoAYxblC5WRCszTy0h9rw0U/RGOv5H0qCgnNg/FILFUqhwo9pNfrUH+MEP4M9qxxbLKZwObpHUE7DUsKx1JYAxsI=,iv:q48lqUbUQD+0cbYcjNMZMJLRdGHi78ZmDhNAT2th9tg=,tag:QRI2SZZXQrAcdql3R5AH2g==,type:str]
kind: Secret
metadata:
name: secret
type: kubernetes.io/dockerconfigjson
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age10la2ge0wtvx3qr7datqf7rs4yngxszdal927fs9rukamr8u2pshsvtz7ce
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3eU1CTEJhVXZ4eEVYYkVV
OU90TEcrR2pYckttN0pBanJoSUZWSW1RQXlRCkUydFJ3V1NZUTBuVFF0aC9GUEcw
bUdhNjJWTkoyL1FUVi9Dc1dxUDBkM0UKLS0tIE1sQXkwcWdGaEFuY0RHQTVXM0J6
dWpJcThEbW15V3dXYXpPZklBdW1Hd1kKoIAdmGNPrEctV8h1w8KuvQ5S+BGmgqN9
MgpNmUhJjWhgcQpb5BRYpQesBOgU5TBGK7j58A6DMDKlSiYZsdQchQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2022-02-03T16:03:17Z"
mac: ENC[AES256_GCM,data:AHdYSawajwgAFwlmDN1IPNmT9vWaYKzyVIra2d6sPcjTbZ8/p+VRSRpVm4XZFFsaNnW5AUJaouwXnKYDTmJDXKlr/rQcu9kXqsssQgdzcXaA6l5uJlgsnml8ba7J3OK+iEKMax23mwQEx2EUskCd9ENOwFDkunP02sxqDNOz20k=,iv:8F5OamHt3fAVorf6p+SoIrWoqkcATSGWVoM0EK87S4M=,tag:E1mxXnc7wWkEX5BxhpLtng==,type:str]
pgp: []
encrypted_regex: ^(data|stringData)$
version: 3.7.1
`,
expected: `apiVersion: v1
data:
.dockerconfigjson: eyJtYXNrIjoiKipTT1BTKioifQ==
kind: Secret
metadata:
name: secret
type: kubernetes.io/dockerconfigjson
`,
},
}
@@ -142,7 +103,7 @@ type: kubernetes.io/dockerconfigjson
}
resource := &resource.Resource{RNode: *r}
err = maskSopsData(resource)
err = trimSopsData(resource)
if err != nil {
t.Fatalf("unable to trim sops data: %v", err)
}

View File

@@ -32,7 +32,6 @@ import (
"github.com/gonvenience/bunt"
"github.com/gonvenience/ytbx"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/go-multierror"
"github.com/homeport/dyff/pkg/dyff"
"github.com/lucasb-eyer/go-colorful"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -52,32 +51,30 @@ func (b *Builder) Manager() (*ssa.ResourceManager, error) {
return ssa.NewResourceManager(b.client, statusPoller, owner), nil
}
func (b *Builder) Diff() (string, bool, error) {
func (b *Builder) Diff() (string, error) {
output := strings.Builder{}
createdOrDrifted := false
res, err := b.Build()
if err != nil {
return "", createdOrDrifted, err
return "", err
}
// convert the build result into Kubernetes unstructured objects
objects, err := ssa.ReadObjects(bytes.NewReader(res))
if err != nil {
return "", createdOrDrifted, err
return "", err
}
resourceManager, err := b.Manager()
if err != nil {
return "", createdOrDrifted, err
return "", err
}
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
if err := ssa.SetNativeKindsDefaults(objects); err != nil {
return "", createdOrDrifted, err
return "", err
}
var diffErrs error
// create an inventory of objects to be reconciled
newInventory := newInventory()
for _, obj := range objects {
@@ -88,8 +85,11 @@ func (b *Builder) Diff() (string, bool, error) {
}
change, liveObject, mergedObject, err := resourceManager.Diff(ctx, obj, diffOptions)
if err != nil {
// gather errors and continue, as we want to see all the diffs
diffErrs = multierror.Append(diffErrs, err)
if b.kustomization.Spec.Force && ssa.IsImmutableError(err) {
output.WriteString(writeString(fmt.Sprintf("► %s created\n", obj.GetName()), bunt.Green))
} else {
output.WriteString(writeString(fmt.Sprintf("✗ %v\n", err), bunt.Red))
}
continue
}
@@ -101,34 +101,31 @@ func (b *Builder) Diff() (string, bool, error) {
if change.Action == string(ssa.CreatedAction) {
output.WriteString(writeString(fmt.Sprintf("► %s created\n", change.Subject), bunt.Green))
createdOrDrifted = true
}
if change.Action == string(ssa.ConfiguredAction) {
output.WriteString(writeString(fmt.Sprintf("► %s drifted\n", change.Subject), bunt.WhiteSmoke))
liveFile, mergedFile, tmpDir, err := writeYamls(liveObject, mergedObject)
if err != nil {
return "", createdOrDrifted, err
return "", err
}
defer cleanupDir(tmpDir)
err = diff(liveFile, mergedFile, &output)
if err != nil {
return "", createdOrDrifted, err
return "", err
}
createdOrDrifted = true
}
addObjectsToInventory(newInventory, change)
}
if b.kustomization.Spec.Prune && diffErrs == nil {
if b.kustomization.Spec.Prune {
oldStatus := b.kustomization.Status.DeepCopy()
if oldStatus.Inventory != nil {
diffObjects, err := diffInventory(oldStatus.Inventory, newInventory)
if err != nil {
return "", createdOrDrifted, err
return "", err
}
for _, object := range diffObjects {
output.WriteString(writeString(fmt.Sprintf("► %s deleted\n", ssa.FmtUnstructured(object)), bunt.OrangeRed))
@@ -136,7 +133,7 @@ func (b *Builder) Diff() (string, bool, error) {
}
}
return output.String(), createdOrDrifted, diffErrs
return output.String(), nil
}
func writeYamls(liveObject, mergedObject *unstructured.Unstructured) (string, string, string, error) {
@@ -199,31 +196,17 @@ func diff(liveFile, mergedFile string, output io.Writer) error {
}
func diffSopsSecret(obj, liveObject, mergedObject *unstructured.Unstructured, change *ssa.ChangeSetEntry) {
// get both data and stringdata maps
data := obj.Object[dataField]
stringData := obj.Object[stringDataField]
if m, ok := data.(map[string]interface{}); ok && m != nil {
applySopsDiff(m, liveObject, mergedObject, change)
}
if m, ok := stringData.(map[string]interface{}); ok && m != nil {
applySopsDiff(m, liveObject, mergedObject, change)
}
}
func applySopsDiff(data map[string]interface{}, liveObject, mergedObject *unstructured.Unstructured, change *ssa.ChangeSetEntry) {
for _, v := range data {
data := obj.Object["data"]
for _, v := range data.(map[string]interface{}) {
v, err := base64.StdEncoding.DecodeString(v.(string))
if err != nil {
fmt.Println(err)
}
if bytes.Contains(v, []byte(mask)) {
if liveObject != nil && mergedObject != nil {
change.Action = string(ssa.UnchangedAction)
dataLive := liveObject.Object[dataField].(map[string]interface{})
dataMerged := mergedObject.Object[dataField].(map[string]interface{})
dataLive := liveObject.Object["data"].(map[string]interface{})
dataMerged := mergedObject.Object["data"].(map[string]interface{})
if cmp.Diff(keys(dataLive), keys(dataMerged)) != "" {
change.Action = string(ssa.ConfiguredAction)
}

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.20.1/kustomize-controller.crds.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.20.1/kustomize-controller.deployment.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.20.0/kustomize-controller.crds.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.20.0/kustomize-controller.deployment.yaml
- account.yaml
patchesJson6902:
- target:

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/source-controller/releases/download/v0.21.2/source-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v0.21.2/source-controller.deployment.yaml
- https://github.com/fluxcd/source-controller/releases/download/v0.21.1/source-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v0.21.1/source-controller.deployment.yaml
- account.yaml
patchesJson6902:
- target:

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/fluxcd/source-controller/releases/download/v0.21.2/source-controller.crds.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.20.1/kustomize-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v0.21.1/source-controller.crds.yaml
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.20.0/kustomize-controller.crds.yaml
- https://github.com/fluxcd/helm-controller/releases/download/v0.16.0/helm-controller.crds.yaml
- https://github.com/fluxcd/notification-controller/releases/download/v0.21.0/notification-controller.crds.yaml
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.16.0/image-reflector-controller.crds.yaml

View File

@@ -0,0 +1,206 @@
# RFC-0003 Flux Multi-Tenancy Mode
**Status:** provisional
**Creation date:** 2021-11-16
**Last update:** 2022-02-03
## Summary
For multi-tenant environments, we want to offer an easy way of configuring Flux to enforce tenant isolation
(as defined by the Soft Multi-Tenancy model from RFC-0001).
When running in the multi-tenant mode, Flux will lock down access to sources (as defined by RFC-0002),
and will use the tenant service account instead of defaulting to `cluster-admin`.
From an end-user perspective, the multi-tenancy mode means that:
- Platform admins have to create a Kubernetes service account and RBAC in each namespace where
Flux performs source-to-cluster reconciliation on behalf of tenants.
By default, Flux will have no permissions to reconcile the tenants sources onto clusters.
- Source owners have to specify with which tenants they wish to share their sources.
By default, nothing is shared between tenants.
## Motivation
As of [version 0.26](https://github.com/fluxcd/flux2/releases/tag/v0.26.0) (Feb 2022),
configuring Flux for soft multi-tenancy requires platform admins to:
- Deny cross-namespace access to Flux custom resources by setting the `--no-cross-namespace-refs` flag.
- Enforce impersonation by setting a default service account with the `--default-service-account` flag.
Instead of using a Kustomize patch to lock down Flux as descried in the
[multi-tenancy lockdown documentation](https://fluxcd.io/docs/installation/#multi-tenancy-lockdown),
we could extend `flux install` and `flux bootstrap` and offer a flag to configure Flux with multi-tenancy enforcements.
### Goals
- Enforce service account impersonation for source-to-cluster reconciliation.
- Enforce ACLs for cross-namespace access to sources.
### Non-Goals
- Enforce tenant's workload isolation with network policies and pod security standards as described
[here](https://kubernetes.io/blog/2021/04/15/three-tenancy-models-for-kubernetes/#security-considerations).
## Proposal
### User Stories
#### Story 1
> As a platform admin, I want to install Flux with lowest privilege/permission level possible.
#### Story 2
> As a platform admin, I want to give tenants full control over their assigned namespaces.
> So that tenants could use their own repositories and manager the app delivery with Flux.
#### Story 3
> As a platform admin, I want to prevent tenants from changing the cluster-wide configuration.
> If a tenant adds to their repository a cluster-scoped resource such as a namespace or cluster role,
> Flux should reject the change and notify the tenant that this operation is not allowed.
### Multi-tenant Bootstrap
When bootstrapping Flux, platform admins should have the option to lock down Flux for multi-tenant environments e.g.:
```shell
flux bootstrap --security-profile=multi-tenant
```
The security profile flag accepts two values: `single-tenant` and `multi-tenant`.
Platform admins may switch between the two modes at any time, either by rerunning bootstrap
or by patching the Flux manifests in Git.
The `multi-tenant` profile is just a shortcut to setting the following container args in the Flux deployment manifests:
```yaml
containers:
- name: manager
args:
- --default-service-account=flux
- --enable-source-acl=true
```
And for disabling cross-namespace references when using the notification API:
```yaml
kind: Deployment
metadata:
name: notification-controller
spec:
template:
spec:
containers:
- name: manager
args:
- --no-cross-namespace-refs=true
```
When running in the `multi-tenant` mode, Flux behaves differently:
- The source-to-cluster reconciliation no longer runs under the service account of
the Flux controllers. The controller service account, is only used to impersonate
the service account specified in the Flux custom resources (`Kustomizations`, `HelmReleases`).
- When no service account name is specified in a Flux custom resource,
a default will be used e.g. `system:serviceaccount:<tenant-namespace>:flux`.
- When a Flux custom resource (`Kustomizations`, `HelmReleases`, `ImagePolicies`, `ImageUpdateAutomations`)
refers to a source in a different namespace, access is granted based the source access control list.
If no ACL is defined for a source, cross-namespace access is denied.
- When a Flux notification (`Alerts`, `Receivers`)
refers to a resource in a different namespace, access is denied.
### Tenants Onboarding
When onboarding tenants, platform admins should have the option to assign namespaces, set
permissions and register the tenants repositories onto clusters in a declarative manner.
The Flux CLI offers an easy way of generating all the Kubernetes manifests needed to onboard tenants:
- `flux create tenant` command generates namespaces, service accounts and Kubernetes RBAC
with restricted access to the cluster resources, given tenants access only to their namespaces.
- `flux create secret git` command generates SSH keys used by Flux to clone the tenants repositories.
- `flux create source git` command generates the configuration that tells Flux which repositories belong to tenants.
- `flux create kustomization` command generates the configuration that tells Flux how to reconcile the manifests found in the tenants repositories.
All the above commands have an `--export` flag for generating the Kubernetes resources in YAML format.
The platform admins should place the generated manifests in the repository that defines the cluster(s) desired state.
Here is an example of the generated manifests:
```yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: tenant1
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: flux
namespace: tenant1
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: flux
namespace: tenant1
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: flux
namespace: tenant1
---
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
name: tenant1
namespace: tenant1
spec:
interval: 5m0s
ref:
branch: main
secretRef:
name: tenant1-git-auth
url: ssh://git@github.com/org/tenant1
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: tenant1
namespace: tenant1
spec:
interval: 10m0s
path: ./
prune: true
serviceAccountName: flux
sourceRef:
kind: GitRepository
name: tenant1
```
Note that the [cluster-admin](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles)
role is used in a `RoleBinding`, this only gives full control over every resource in the role binding's namespace.
Once the tenants repositories are registered on the cluster(s), the tenants can configure their app delivery
in Git using Kubernetes namespace-scoped resources such as `Deployments`, `Services`, Flagger `Canaries`,
Flux `Kustomizations`, `HelmReleases`, `ImageUpdateAutomations`, `Alerts`, `Receivers`, etc.
## Alternatives
Instead of introducing the security profile flag to `flux bootstrap`,
we could document how to patch each controller deployment with Kustomize as described in the
[multi-tenancy lockdown documentation](https://fluxcd.io/docs/installation/#multi-tenancy-lockdown).
Having an easy way of locking down Flux with a single flag, make users aware of the security implications
and improves the user experience.
## Implementation History
- Disabling cross-namespace access and providing a default service account was first released in flux2 **v0.26.0**.

View File

@@ -483,7 +483,7 @@ func TestImageRepositoryACR(t *testing.T) {
Interval: metav1.Duration{
Duration: 1 * time.Minute,
},
SourceRef: automationv1beta1.CrossNamespaceSourceReference{
SourceRef: automationv1beta1.SourceReference{
Kind: "GitRepository",
Name: name,
},

View File

@@ -7,11 +7,11 @@ require (
github.com/fluxcd/helm-controller/api v0.16.0
github.com/fluxcd/image-automation-controller/api v0.20.0
github.com/fluxcd/image-reflector-controller/api v0.16.0
github.com/fluxcd/kustomize-controller/api v0.20.1
github.com/fluxcd/kustomize-controller/api v0.20.0
github.com/fluxcd/notification-controller/api v0.21.0
github.com/fluxcd/pkg/apis/meta v0.10.2
github.com/fluxcd/pkg/runtime v0.12.4
github.com/fluxcd/source-controller/api v0.21.2
github.com/fluxcd/source-controller/api v0.21.1
github.com/hashicorp/terraform-exec v0.14.0
github.com/libgit2/git2go/v31 v31.6.1
github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5

View File

@@ -204,8 +204,8 @@ github.com/fluxcd/image-automation-controller/api v0.20.0 h1:Z+lxqif0KwccsuNOBZq
github.com/fluxcd/image-automation-controller/api v0.20.0/go.mod h1:XhLYccGUbmJvTTpJ1jAFKZHr2e1GNXy0T85ZBO50mik=
github.com/fluxcd/image-reflector-controller/api v0.16.0 h1:1O1YdoK7LsJgWLyvfZTSbvQcUQCBcgJ573HA0arlQQY=
github.com/fluxcd/image-reflector-controller/api v0.16.0/go.mod h1:OIe3mSXc3OwQiNbiQ9vNXWYtNif31hc7WAbZWlFUUnc=
github.com/fluxcd/kustomize-controller/api v0.20.1 h1:BMOUdKCb6bZ6kepcxU8vz9VcDbl4q6SH9j/mRPl2O1A=
github.com/fluxcd/kustomize-controller/api v0.20.1/go.mod h1:5MdpzJVx8+KiDIRv37zLme992BAOCgE0v1n+NOgs1lo=
github.com/fluxcd/kustomize-controller/api v0.20.0 h1:Vw+2qCxeHMv0y1mfiBgVrMfcfFevBMrRfLEdfPTrb40=
github.com/fluxcd/kustomize-controller/api v0.20.0/go.mod h1:5MdpzJVx8+KiDIRv37zLme992BAOCgE0v1n+NOgs1lo=
github.com/fluxcd/notification-controller/api v0.21.0 h1:D5B3TH5YtSww0SyvW1Ru5xWsh0MgHQanC/a1t3CvXq0=
github.com/fluxcd/notification-controller/api v0.21.0/go.mod h1:gA9/j0kjh7VDuUC2Cubr9twxOdzb/0+ojcE9Lzsc9ug=
github.com/fluxcd/pkg/apis/acl v0.0.3 h1:Lw0ZHdpnO4G7Zy9KjrzwwBmDZQuy4qEjaU/RvA6k1lc=
@@ -218,8 +218,8 @@ github.com/fluxcd/pkg/runtime v0.12.3/go.mod h1:imJ2xYy/d4PbSinX2IefmZk+iS2c1P5f
github.com/fluxcd/pkg/runtime v0.12.4 h1:gA27RG/+adN2/7Qe03PB46Iwmye/MusPCpuS4zQ2fW0=
github.com/fluxcd/pkg/runtime v0.12.4/go.mod h1:gspNvhAqodZgSmK1ZhMtvARBf/NGAlxmaZaIOHkJYsc=
github.com/fluxcd/source-controller/api v0.21.0/go.mod h1:Ab2qDmAUz6ZCp8UaHYLYzxyFrC1FQqEqjxiROb/Rdiw=
github.com/fluxcd/source-controller/api v0.21.2 h1:J0S5NN4V8FPLrkSMXIUoUvj1X/RuTpVJSjIRF414wmc=
github.com/fluxcd/source-controller/api v0.21.2/go.mod h1:Ab2qDmAUz6ZCp8UaHYLYzxyFrC1FQqEqjxiROb/Rdiw=
github.com/fluxcd/source-controller/api v0.21.1 h1:7X39YQHmB1vmIBrHxU+YAqxwtdC9Zh+tdtMKREW3xiQ=
github.com/fluxcd/source-controller/api v0.21.1/go.mod h1:Ab2qDmAUz6ZCp8UaHYLYzxyFrC1FQqEqjxiROb/Rdiw=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=