From c9636be52f96ce146c73ba079279a024b2132de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 5 Dec 2024 16:58:27 -0500 Subject: [PATCH 1/4] incusd/instance/qemu: Set CLOEXEC for TPM sockets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- internal/server/instance/drivers/driver_qemu.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/server/instance/drivers/driver_qemu.go b/internal/server/instance/drivers/driver_qemu.go index 4041284d72b..1c773213468 100644 --- a/internal/server/instance/drivers/driver_qemu.go +++ b/internal/server/instance/drivers/driver_qemu.go @@ -4870,7 +4870,7 @@ func (d *qemu) addTPMDeviceConfig(cfg *[]cfgSection, tpmConfig []deviceConfig.Ru } } - fd, err := unix.Open(socketPath, unix.O_PATH, 0) + fd, err := unix.Open(socketPath, unix.O_PATH|unix.O_CLOEXEC, 0) if err != nil { return err } From bed7a89e1b6eacd86c8ea044281291b0dff10755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 5 Dec 2024 17:20:44 -0500 Subject: [PATCH 2/4] incusd/patches: Run auth patches on all servers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- cmd/incusd/patches.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/cmd/incusd/patches.go b/cmd/incusd/patches.go index 4e84cd5c34d..4eb268ef8e1 100644 --- a/cmd/incusd/patches.go +++ b/cmd/incusd/patches.go @@ -686,25 +686,6 @@ func patchMoveBackupsInstances(name string, d *Daemon) error { } func patchGenericAuthorization(name string, d *Daemon) error { - // Only run authorization patches on the leader. - isLeader := false - - leaderAddress, err := d.gateway.LeaderAddress() - if err != nil { - if !errors.Is(err, cluster.ErrNodeIsNotClustered) { - return err - } - - isLeader = true - } else if leaderAddress == d.localConfig.ClusterAddress() { - isLeader = true - } - - // If clustered and not running on a leader, skip the resource update. - if !isLeader { - return nil - } - return d.authorizer.ApplyPatch(d.shutdownCtx, name) } From 873000ca08e0ddf1309748c943bad471a72ee43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 5 Dec 2024 17:23:57 -0500 Subject: [PATCH 3/4] incusd/auth/openfga: Get rid of applyPatches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have a proper patch mechanism, move the patch logic into that and initialization logic into the initialization code path. Signed-off-by: Stéphane Graber --- internal/server/auth/driver_openfga.go | 73 ++++++++++++++------------ 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/internal/server/auth/driver_openfga.go b/internal/server/auth/driver_openfga.go index 7cda8c580fa..b8ab3040fc6 100644 --- a/internal/server/auth/driver_openfga.go +++ b/internal/server/auth/driver_openfga.go @@ -146,7 +146,33 @@ func (f *fga) StopService(ctx context.Context) error { // ApplyPatch is called when an applicable server patch is run, this triggers a model re-upload. func (f *fga) ApplyPatch(ctx context.Context, name string) error { - // Upload a new model. + if name == "auth_openfga_viewer" { + // Add the public access permission if not set. + resp, err := f.client.Check(ctx).Body(client.ClientCheckRequest{ + User: "user:*", + Relation: "authenticated", + Object: ObjectServer().String(), + }).Execute() + if err != nil { + return err + } + + if !resp.GetAllowed() { + err = f.updateTuples(ctx, []client.ClientTupleKey{ + {User: "user:*", Relation: "authenticated", Object: ObjectServer().String()}, + }, nil) + if err != nil { + return err + } + + // Attempt to clear the former version of this permission. + _ = f.updateTuples(ctx, nil, []client.ClientTupleKeyWithoutCondition{ + {User: "user:*", Relation: "viewer", Object: ObjectServer().String()}, + }) + } + } + + // Always refresh the model. logger.Info("Refreshing the OpenFGA model") return f.refreshModel(ctx) } @@ -176,10 +202,20 @@ func (f *fga) connect(ctx context.Context, certificateCache *certificate.Cache, // Check if we need to upload an initial model. if readModelResponse.AuthorizationModel == nil { logger.Info("Upload initial OpenFGA model") + + // Upload the model itself. err := f.refreshModel(ctx) if err != nil { return fmt.Errorf("Failed to load initial model: %w", err) } + + // Allow basic authenticated access. + err = f.updateTuples(ctx, []client.ClientTupleKey{ + {User: "user:*", Relation: "authenticated", Object: ObjectServer().String()}, + }, nil) + if err != nil { + return err + } } if opts.resourcesFunc != nil { @@ -916,43 +952,10 @@ func (f *fga) projectObjects(ctx context.Context, projectName string) ([]string, return allObjects, nil } -func (f *fga) applyPatches(ctx context.Context) ([]client.ClientTupleKey, []client.ClientTupleKeyWithoutCondition, error) { +func (f *fga) syncResources(ctx context.Context, resources Resources) error { var writes []client.ClientTupleKey var deletions []client.ClientTupleKeyWithoutCondition - // Add the public access permission if not set. - resp, err := f.client.Check(ctx).Body(client.ClientCheckRequest{ - User: "user:*", - Relation: "authenticated", - Object: ObjectServer().String(), - }).Execute() - if err != nil { - return nil, nil, err - } - - if !resp.GetAllowed() { - writes = append(writes, client.ClientTupleKey{ - User: "user:*", - Relation: "authenticated", - Object: ObjectServer().String(), - }) - - // Attempt to clear the former version of this permission. - _ = f.updateTuples(ctx, nil, []client.ClientTupleKeyWithoutCondition{ - {User: "user:*", Relation: "viewer", Object: ObjectServer().String()}, - }) - } - - return writes, deletions, nil -} - -func (f *fga) syncResources(ctx context.Context, resources Resources) error { - // Apply model patches. - writes, deletions, err := f.applyPatches(ctx) - if err != nil { - return err - } - // Helper function for diffing local objects with those in OpenFGA. These are appended to the writes and deletions // slices as appropriate. If the given relation is relationProject, we need to construct a project object for the // "user" field. The project is calculated from the object we are inspecting. From 66baacfe6e3d0148ea0289520019d1ff37a7fee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 5 Dec 2024 17:58:27 -0500 Subject: [PATCH 4/4] incusd/auth/openfga: Force OpenFGA update on initial config and patching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- internal/server/auth/driver_openfga.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/server/auth/driver_openfga.go b/internal/server/auth/driver_openfga.go index b8ab3040fc6..48281abc37c 100644 --- a/internal/server/auth/driver_openfga.go +++ b/internal/server/auth/driver_openfga.go @@ -158,7 +158,7 @@ func (f *fga) ApplyPatch(ctx context.Context, name string) error { } if !resp.GetAllowed() { - err = f.updateTuples(ctx, []client.ClientTupleKey{ + err = f.sendTuples(ctx, []client.ClientTupleKey{ {User: "user:*", Relation: "authenticated", Object: ObjectServer().String()}, }, nil) if err != nil { @@ -166,7 +166,7 @@ func (f *fga) ApplyPatch(ctx context.Context, name string) error { } // Attempt to clear the former version of this permission. - _ = f.updateTuples(ctx, nil, []client.ClientTupleKeyWithoutCondition{ + _ = f.sendTuples(ctx, nil, []client.ClientTupleKeyWithoutCondition{ {User: "user:*", Relation: "viewer", Object: ObjectServer().String()}, }) } @@ -210,7 +210,7 @@ func (f *fga) connect(ctx context.Context, certificateCache *certificate.Cache, } // Allow basic authenticated access. - err = f.updateTuples(ctx, []client.ClientTupleKey{ + err = f.sendTuples(ctx, []client.ClientTupleKey{ {User: "user:*", Relation: "authenticated", Object: ObjectServer().String()}, }, nil) if err != nil { @@ -866,6 +866,7 @@ func (f *fga) DeleteStorageBucket(ctx context.Context, projectName string, stora return f.updateTuples(ctx, nil, deletions) } +// updateTuples sends an object update to OpenFGA if it's currently online. func (f *fga) updateTuples(ctx context.Context, writes []client.ClientTupleKey, deletions []client.ClientTupleKeyWithoutCondition) error { // If offline, skip updating as a full sync will happen after connection. if !f.online { @@ -876,6 +877,11 @@ func (f *fga) updateTuples(ctx context.Context, writes []client.ClientTupleKey, return nil } + return f.sendTuples(ctx, writes, deletions) +} + +// sendTuples directly sends the write/deletion tuples to OpenFGA. +func (f *fga) sendTuples(ctx context.Context, writes []client.ClientTupleKey, deletions []client.ClientTupleKeyWithoutCondition) error { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel()