Add the build tags we were using, `integration` and `tools`, to be included in the linting/formatting of golangci-lint.
Rename the build tag `tools` to `sidero.tools` to avoid colliding with the same named build tag in `github.com/johannesboyne/gofakes3` package - otherwise the dependency was failing to compile due to having multiple package names in the same package.
Fix all the linting errors surfaced by this enablement.
Also, temporarily re-enabled `nolintlint` to find the nolint directives which were no longer necessary and removed them.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Omni now rejects legacy installation media download requests with a message asking users to upgrade omnictl instead of proxying them to the Talos image factory.
Current omnictl versions continue to download installation media directly from the Talos image factory.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Add creation timestamps and per-key last-active tracking to service account key listings. The `omnictl serviceaccount list` command now shows KEY CREATED and KEY LAST ACTIVE columns for each public key, alongside the existing SA-level LAST ACTIVE.
A new PublicKeyLastActive resource tracks per-key usage. The activity interceptor now extracts the signing key fingerprint from the auth context and records last-used timestamps per key, with independent debouncing. The ServiceAccountStatusController aggregates this data into the service account status for display.
A cleanup controller removes PublicKeyLastActive resources when their corresponding public key is torn down.
Closes: siderolabs/omni#2661
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Implement a guard for Omni to prevent usage until users accept an EULA through the UI or a startup flag.
Signed-off-by: Edward Sammut Alessi <edward.sammutalessi@siderolabs.com>
Add a cleanup controller that removes the identity last active resource when its corresponding identity is destroyed. This covers all identity types uniformly: users, service accounts, and infra providers.
Previously, users and service accounts had manual cleanup in their destroy paths, but infra providers did not, leaving orphaned resources. Remove the manual cleanup from user and service account destruction, as the controller now handles it.
Add a migration to remove any already-orphaned identity last active resources that have no matching identity.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
* Add `IdentityLastActive` resource to record the last time each identity(`User`/`ServiceAccount`) made a gRPC call.
* Add `IdentityStatusController` to aggregate identity, user role, and last-active data into an ephemeral `IdentityStatus` resource.
* Expose last_active in ListUsers/ListServiceAccounts gRPC responses, omnictl CLI output, and the frontend Users/ServiceAccounts views.
* Add `UserMetricsController` exposing `omni_users` (total) and `omni_active_users` (7d/30d windows) Prometheus gauges.
Signed-off-by: Oguz Kilcan <oguz.kilcan@siderolabs.com>
Add state validation that rejects identity creation when the configured maximum number of users or service accounts is reached. The gRPC resource and management servers now use the validated state so these limits are enforced for all creation paths (CLI, UI, API). Identity is created before the user resource so the validation fires before any side effects.
Also adds create validation for join token name, e2e Playwright tests covering UI and AccountLimits integration test covering API and CLI for limit enforcement.
Signed-off-by: Oguz Kilcan <oguz.kilcan@siderolabs.com>
Migrate user create, list, update, and destroy operations from direct resource manipulation to dedicated ManagementService gRPC endpoints, matching the existing service account pattern.
Direct Identity/User resource mutations are now restricted, and the CLI, frontend, and client library are updated to use the new endpoints.
Signed-off-by: Oguz Kilcan <oguz.kilcan@siderolabs.com>
In the backend enfore auth_time checks to validate that a re-authentication has taken place, and that the token meets our minimum age policy.
Signed-off-by: Edward Sammut Alessi <edward.sammutalessi@siderolabs.com>
Make all leaf fields nillable, so that we can distinguish unset from explicit empty, and merging of CLI args and YAML configs work correctly.
Generate nil-safe accessors (getter/setters) for these nillable fields and use them in the code.
Wrap the cobra command line parser to support nillable flags.
Move all validations into the JSON schema and drop go-validator usage and its annotations.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Simplify the code and make it less error prone.
Signed-off-by: Pranav Patil <pranavppatil767@gmail.com>
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
The encodeRFC4754 test helper failed to pad ECDSA integers to the curve size, causing signatures to be shorter than 64 bytes when r or s had leading zeros. This resulted in intermittent verification failures. This fix ensures both values are correctly zero-padded to 32 bytes.
The actual signing code in the frontend was verified to not have the issue, as it hands over signing to the Web Crypto API.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Use SQLite storage to store audit logs.
Use the same SQLite database used for the metrics state.
Refactor audit log storage to define a common interface for file and sqlite based logs.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Added new `update_on_each_login` field to the `SAMLLabelRule` spec.
Also renamed `assign_role_on_registration` to `assign_role` as it's no
longer reflecting the actual meaning.
The old field is kept there for the backward compatibility.
Fixes: https://github.com/siderolabs/omni/issues/1201
Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
Omni now supports ECDSA P-256 keys for signing the requests.
The plain key should be encoded as PEM when it is submitted to
`RegisterPublicKey` method.
Signature should be encoded using RFC4754 method (`r||s`).
Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
Rekres, fix linter issues, bump go to 1.25.2
See groups.google.com/g/golang-nuts/c/Gxn25BP4MXk/m/3KrM-XBOBAAJ
Signed-off-by: Oguz Kilcan <oguz.kilcan@siderolabs.com>
Check if the actor is internal to make sure the local resource server remains accessible without authentication.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Change the interceptor order to move the Prometheus metrics collector earlier, so that we can get metrics for the calls that fail early. Related to siderolabs/omni#1606.
Additionally, ensure that `get` access to the `AuthConfig` resource does not require a GRPC signature.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Fixes: https://github.com/siderolabs/omni/issues/92
Now Omni can be configured to use OIDC provider (tested against Google
provider).
New flags introduced:
```
--auth-oidc-enabled true
--auth-oidc-provider-url https://accounts.google.com
--auth-oidc-client-id REDACTED
--auth-oidc-client-secret REDACTED
--auth-oidc-scopes openid
--auth-oidc-scopes profile
--auth-oidc-scopes email
```
Initial users are created the same way as for Auth0 provider, same flag
should be used.
Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
default / e2e-cluster-import (push) Has been cancelled
default / e2e-forced-removal (push) Has been cancelled
default / e2e-omni-upgrade (push) Has been cancelled
default / e2e-scaling (push) Has been cancelled
default / e2e-short (push) Has been cancelled
default / e2e-short-secureboot (push) Has been cancelled
default / e2e-templates (push) Has been cancelled
default / e2e-upgrades (push) Has been cancelled
default / e2e-workload-proxy (push) Has been cancelled
Several changes:
- if identity doesn't look like a valid email address ignore the
attribute.
- if identity was already detected in an attribute ignore other
attributes.
Allow setting extra attribute mappings in the command line flags.
Fixes: https://github.com/siderolabs/omni/issues/1376
Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
default / e2e-forced-removal (push) Has been cancelled
default / e2e-omni-upgrade (push) Has been cancelled
default / e2e-scaling (push) Has been cancelled
default / e2e-short (push) Has been cancelled
default / e2e-short-secureboot (push) Has been cancelled
default / e2e-templates (push) Has been cancelled
default / e2e-upgrades (push) Has been cancelled
default / e2e-workload-proxy (push) Has been cancelled
- Bump some deps, namely cosi-runtime and Talos machinery.
- Update `auditState` to implement the new methods in COSI's `state.State`.
- Bump default Talos and Kubernetes versions to their latest.
- Rekres, which brings Go 1.24.5. Also update it in go.mod files.
- Fix linter errors coming from new linters.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
default / e2e-backups (push) Blocked by required conditions
default / e2e-forced-removal (push) Blocked by required conditions
default / e2e-scaling (push) Blocked by required conditions
default / e2e-short (push) Blocked by required conditions
default / e2e-short-secureboot (push) Blocked by required conditions
default / e2e-templates (push) Blocked by required conditions
default / e2e-upgrades (push) Blocked by required conditions
default / e2e-workload-proxy (push) Blocked by required conditions
This enables test coverage, builds Omni with race detector.
Also redone the COSI state creation flow: no more callbacks.
The state is now an Object, which has `Stop` method, that should be
called when the app stops.
All defers were moved into the `Stop` method basically.
Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
default / e2e-backups (push) Blocked by required conditions
default / e2e-forced-removal (push) Blocked by required conditions
default / e2e-scaling (push) Blocked by required conditions
default / e2e-short (push) Blocked by required conditions
default / e2e-short-secureboot (push) Blocked by required conditions
default / e2e-templates (push) Blocked by required conditions
default / e2e-upgrades (push) Blocked by required conditions
default / e2e-workload-proxy (push) Blocked by required conditions
Omni can now be configured via a config file instead of the command line
flags.
The flags `--config-path` will now read the config provided in the YAML
format.
The config structure was completely changed. It was not public before,
so it's fine to ignore backward compatibility.
The command line flags were not changed.
Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
default / e2e-forced-removal (push) Has been cancelled
default / e2e-scaling (push) Has been cancelled
default / e2e-short (push) Has been cancelled
default / e2e-short-secureboot (push) Has been cancelled
default / e2e-templates (push) Has been cancelled
default / e2e-upgrades (push) Has been cancelled
default / e2e-workload-proxy (push) Has been cancelled
- The license headers in the generated test sources via `mockgen` were getting commented-out after `make generate` was run.
Fix this by replacing repeated double-slashes `// //` via a single double-slash `//`.
- Rekres, `make generate` and `make generate-frontend`.
- Bump Go deps.
- Fix linting errors to satisfy new rules in golangci-lint `v2.1.1`.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
default / e2e-workload-proxy (push) Has been cancelled
Also:
- support generating the initial service account and dumping it's key
somewhere.
- support running omni integration tests against the production build of
Omni using a service account.
- enable `omni-integration-test` image.
Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
This commit implements session tracking and log audit for those types:
- [x] auth.PublicKey
- [x] auth.AccessPolicy
- [x] auth.User
- [x] auth.Identity
- [x] omni.Machine
- [x] omni.MachineLabels
- [x] omni.Cluster
- [x] omni.MachineSet (only empty owners for update, log create and delete in all cases)
- [x] omni.MachineSetNode (only empty owners for update, log create and delete in all cases)
- [x] omni.ConfigPatch
- [x] Talos API Access
- [x] Kubernetes API access
Output example:
```
{"event_type":"update","resource_type":"Machines.omni.sidero.dev","event_ts":1723137771180,"event_data":{"session":{"user_agent":"Omni-Internal-Agent"},"machine":{"id":"18cec051-d975-483d-8d43-10ac6421648a","is_connected":true,"management_address":"fdae:41e4:649b:9303:da9b:1ed:a725:c3dd","labels":{"omni.sidero.dev/address":"fdae:41e4:649b:9303:da9b:1ed:a725:c3dd"}}}}
{"event_type":"update","resource_type":"Machines.omni.sidero.dev","event_ts":1723137771180,"event_data":{"session":{"user_agent":"Omni-Internal-Agent"},"machine":{"id":"18cec051-d975-483d-8d43-10ac6421648a","is_connected":true,"management_address":"fdae:41e4:649b:9303:da9b:1ed:a725:c3dd","labels":{"omni.sidero.dev/address":"fdae:41e4:649b:9303:da9b:1ed:a725:c3dd"}}}}
{"event_type":"update","resource_type":"Machines.omni.sidero.dev","event_ts":1723137771181,"event_data":{"session":{"user_agent":"Omni-Internal-Agent"},"machine":{"id":"18cec051-d975-483d-8d43-10ac6421648a","is_connected":true,"management_address":"fdae:41e4:649b:9303:da9b:1ed:a725:c3dd","labels":{"omni.sidero.dev/address":"fdae:41e4:649b:9303:da9b:1ed:a725:c3dd"}}}}
{"event_type":"create","resource_type":"MachineLabels.omni.sidero.dev","event_ts":1723137787549,"event_data":{"session":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36","ip_address":"<snip>","user_id":"ea002172-b9da-423f-bd1d-b443b8a7b43c","role":"Admin","email":"dmitry.matrenichev@siderolabs.com","fingerprint":"da7b997eb68449a12bebc6a3bf4f59beaf167209"},"machine_labels":{"id":"18cec051-d975-483d-8d43-10ac6421648a","labels":{"222":""}}}}
{"event_type":"update","resource_type":"MachineLabels.omni.sidero.dev","event_ts":1723137787553,"event_data":{"session":{"user_agent":"Omni-Internal-Agent"},"machine_labels":{"id":"18cec051-d975-483d-8d43-10ac6421648a","labels":{"222":""}}}}
{"event_type":"update","resource_type":"MachineLabels.omni.sidero.dev","event_ts":1723137811532,"event_data":{"session":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36","ip_address":"<snip>","user_id":"ea002172-b9da-423f-bd1d-b443b8a7b43c","role":"Admin","email":"dmitry.matrenichev@siderolabs.com","fingerprint":"da7b997eb68449a12bebc6a3bf4f59beaf167209"},"machine_labels":{"id":"18cec051-d975-483d-8d43-10ac6421648a","labels":{"222":"","333":""}}}}
{"event_type":"update","resource_type":"MachineLabels.omni.sidero.dev","event_ts":1723137811610,"event_data":{"session":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36","ip_address":"<snip>","user_id":"ea002172-b9da-423f-bd1d-b443b8a7b43c","role":"Admin","email":"dmitry.matrenichev@siderolabs.com","fingerprint":"da7b997eb68449a12bebc6a3bf4f59beaf167209"},"machine_labels":{"id":"18cec051-d975-483d-8d43-10ac6421648a","labels":{"222":"","333":""}}}}
{"event_type":"update","resource_type":"MachineLabels.omni.sidero.dev","event_ts":1723137811611,"event_data":{"session":{"user_agent":"Omni-Internal-Agent"},"machine_labels":{"id":"18cec051-d975-483d-8d43-10ac6421648a","labels":{"222":"","333":""}}}}
{"event_type":"destroy","resource_type":"MachineLabels.omni.sidero.dev","event_ts":1723137811621,"event_data":{"session":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36","ip_address":"<snip>","user_id":"ea002172-b9da-423f-bd1d-b443b8a7b43c","role":"Admin","email":"dmitry.matrenichev@siderolabs.com","fingerprint":"da7b997eb68449a12bebc6a3bf4f59beaf167209"},"machine_labels":{"id":"18cec051-d975-483d-8d43-10ac6421648a","labels":{"222":"","333":""}}}}
{"event_type":"create","resource_type":"Users.omni.sidero.dev","event_ts":1723141793888,"event_data":{"new_user":{"role":"Admin","id":"7903a72c-87af-43b8-94dc-82bd961ab768"},"session":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36","ip_address":"<snip>","user_id":"ea002172-b9da-423f-bd1d-b443b8a7b43c","role":"Admin","email":"dmitry.matrenichev@siderolabs.com","fingerprint":"da7b997eb68449a12bebc6a3bf4f59beaf167209"}}}
{"event_type":"create","resource_type":"Identities.omni.sidero.dev","event_ts":1723141793981,"event_data":{"new_user":{"id":"7903a72c-87af-43b8-94dc-82bd961ab768","email":"some-user-email@email.com"},"session":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36","ip_address":"<snip>","user_id":"ea002172-b9da-423f-bd1d-b443b8a7b43c","role":"Admin","email":"dmitry.matrenichev@siderolabs.com","fingerprint":"da7b997eb68449a12bebc6a3bf4f59beaf167209"}}}
```
Closes#37
Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
This PR implements audit logs. To enable it you have to set the `--audit-log-dir` flag
to a directory where the audit logs will be stored. The audit logs are stored in a JSON format.
Example:
```json
{"event_type":"update","resource_type":"PublicKeys.omni.sidero.dev","event_ts":1722537710182,"event_data":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36","ip_address":"<snip>","user_id":"a19a7a38-1793-4262-a9ef-97bc00c7a155","role":"Admin","email":"useremail@userdomain.com","confirmation_type":"auth0","fingerprint":"15acb974f769bdccd38a4b28f282b78736b80bc7","public_key_expiration":1722565909}}
```
Keep in mind that `event_ts` are in milliseconds instead of seconds.
Field `event_data` contains all relevant information about the event.
To enabled it in the development environment you will have to add the
`--audit-log-dir /tmp/omni-data/audit-logs` line to `docker-compose.override.yml`
or run `generate-certs` again.
For #37
Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
Add the new resources for the cloud provider feature: `CloudConfigs`, `MachineRequests` and `MachineRequestStatuses`.
Add a new role, `CloudProvider` with access to the resources a cloud provider plugin requires.
Introduce the concept of "cloud provider service accounts" which are a special type of service accounts in the format `cloud-provider:<id>`. They must have the `CloudProvider` role and their id is matched against the label `omni.sidero.dev/cloud-provider-id` label on the `MachineRequest*` type resources.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Using so-called phantom types we can use the types themselves as keys directly without loosing performance.
You no longer need to remember which type was attached to the thing you passed in context and can look up
all fields access directly.
Part of #37
Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
Make the controller run tasks that can collect machine status from each
machine.
Instead of changing the `MachineStatusSnapshot` directly in the
siderolink events handler pass these events to the controller through
the channel, so that all events are handled in the same place.
If either event comes from siderolink or if task runner gets the machine
status it updates the `MachineStatusSnapshot` resource.
Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
- run rekres and fix nolint directives
- bump deps (keep gen to 0.4.8 for now) for server, client and tests
Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
If the user has their email not verified, instead of failing with a generic error message of "invalid JWT", print an error message asking user to verify their email and try again.
In Auth0 mode, if the JWT validation has failed on the backend at the moment of clicking "Login", get a new ID token from Auth0 on the next click. This way, the user will not have to reload the page after validating their email - they can simply click "Login" again to get in.
Part of siderolabs/omni#114.
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Omni doesn't use a generic OAuth2/OIDC client/SDK instead it uses the Auth0 SDK for Vue and it's using a pretty old version, v1.0.2 as can be seen by inspecting the Omni package.json [here](7fb5d2b20a/frontend/package.json (L13)). This SDK in turn uses [auth0-spa-js](https://github.com/auth0/auth0-spa-js) v1.22.1 which can be seen by inspecting its package.json [here](bb3bc817d1/package.json (L80)).
**This has significant implications as the v1 of the SDK is not compliant with OAuth2 in 1 critical area.**
OAuth2 mandates the use of the `application/x-www-form-urlencoded` content type for grant messages sent to the token endpoint and that sending JSON request bodies will result in a 400 error.
Unfortunately the v1 of the SDK sends the request payload as JSON which means that IdPs such as Authentik rightfully returns a 400 error and this results in an infinite loop of requests from Omni to Authentik.
The behavior can be confirmed by looking at the comment in the Auth0 SDK code [here](371e5a82a6/src/global.ts (L251)). Interestingly the default for the `useFormData` was changed to `true` in v1.22.6 of the SDK.
This PR introduces a new Omni flag called `--auth-auth0-use-form-data`. By default the flag is set to `false` to maintain backwards compatibility. If the flag is set to `true` then the Auth0 client is created with the `useFormData` set to `true`
Signed-off-by: Sherif Fanous <sherif.fanous+github@gmail.com>
Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
Omni is source-available under BUSL.
Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
Co-Authored-By: Artem Chernyshev <artem.chernyshev@talos-systems.com>
Co-Authored-By: Utku Ozdemir <utku.ozdemir@siderolabs.com>
Co-Authored-By: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
Co-Authored-By: Philipp Sauter <philipp.sauter@siderolabs.com>
Co-Authored-By: Noel Georgi <git@frezbo.dev>
Co-Authored-By: evgeniybryzh <evgeniybryzh@gmail.com>
Co-Authored-By: Tim Jones <tim.jones@siderolabs.com>
Co-Authored-By: Andrew Rynhard <andrew@rynhard.io>
Co-Authored-By: Spencer Smith <spencer.smith@talos-systems.com>
Co-Authored-By: Christian Rolland <christian.rolland@siderolabs.com>
Co-Authored-By: Gerard de Leeuw <gdeleeuw@leeuwit.nl>
Co-Authored-By: Steve Francis <67986293+steverfrancis@users.noreply.github.com>
Co-Authored-By: Volodymyr Mazurets <volodymyrmazureets@gmail.com>