Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ helm-generate: manifests kubebuilder ## Regenerate Helm chart from kustomize
$(SED) -i 's/team-operator-controller-manager-metrics-service/{{ .Values.controllerManager.serviceAccountName }}-metrics-service/g' dist/chart/templates/metrics/metrics-service.yaml
# Fix RoleBinding namespace to use watchNamespace value
$(SED) -i '/kind: RoleBinding/,/roleRef:/{s/namespace: posit-team/namespace: {{ .Values.watchNamespace }}/}' dist/chart/templates/rbac/role_binding.yaml
# Remove duplicate metrics service that kubebuilder generates - we already have one in dist/chart/templates/metrics/
# This was causing "services 'team-operator-controller-manager-metrics-service' already exists" errors
# The correct metrics service is gated on .Values.metrics.enable, not .Values.rbac.enable
rm -f dist/chart/templates/rbac/auth_proxy_service.yaml
# Remove kubebuilder-generated test workflow - we use our own CI workflows
rm -f .github/workflows/test-chart.yml

Expand Down
7 changes: 7 additions & 0 deletions api/core/v1beta1/site_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,13 @@ type InternalWorkbenchSpec struct {
// Workbench Auth/Login Landing Page Customization HTML
AuthLoginPageHtml string `json:"authLoginPageHtml,omitempty"`

// AuditedJobs configures Workbench Audited Jobs for tracking execution details
// alongside job output, including digital signatures and environment data.
// Requires the Advanced product tier.
// See: https://docs.posit.co/ide/server-pro/admin/auditing_and_monitoring/audited_workbench_jobs.html
// +optional
AuditedJobs *AuditedJobsConfig `json:"auditedJobs,omitempty"`

// JupyterConfig contains Jupyter configuration for Workbench
JupyterConfig *WorkbenchJupyterConfig `json:"jupyterConfig,omitempty"`
}
Expand Down
68 changes: 68 additions & 0 deletions api/core/v1beta1/workbench_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,63 @@ type WorkbenchJupyterConfig struct {
DefaultSessionContainerImage string `json:"default-session-container-image,omitempty"`
}

// AuditedJobsConfig contains configuration for Workbench Audited Jobs.
// See: https://docs.posit.co/ide/server-pro/admin/auditing_and_monitoring/audited_workbench_jobs.html
type AuditedJobsConfig struct {
// Enabled enables audited jobs support (0=disabled, 1=enabled)
// Maps to rserver.conf: audited-jobs
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=1
// +optional
Enabled *int `json:"enabled,omitempty"`

// StoragePath sets the directory for audited job data
// Maps to rserver.conf: audited-jobs-storage-path
// +optional
StoragePath string `json:"storagePath,omitempty"`

// PrivateKeyPath sets the path to the RSA private key for digital signatures
// Maps to rserver.conf: audited-jobs-private-key-path
// +optional
PrivateKeyPath string `json:"privateKeyPath,omitempty"`

// PublicKeyPaths sets the path(s) to RSA public keys for signature verification
// Maps to rserver.conf: audited-jobs-public-key-paths
// +optional
PublicKeyPaths string `json:"publicKeyPaths,omitempty"`

// LogLimit sets the maximum number of audit log entries
// Maps to rserver.conf: audited-jobs-log-limit
// +optional
LogLimit *int `json:"logLimit,omitempty"`

// DeletionExpiry sets the number of days before completed audited jobs are deleted
// Maps to rserver.conf: audited-jobs-deletion-expiry
// +optional
DeletionExpiry *int `json:"deletionExpiry,omitempty"`

// VanillaRequired requires --vanilla flag for R jobs (0=disabled, 1=enabled)
// Maps to rserver.conf: audited-jobs-vanilla-required
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=1
// +optional
VanillaRequired *int `json:"vanillaRequired,omitempty"`

// DetailsEnvironment enables capturing environment information (0=disabled, 1=enabled)
// Maps to rserver.conf: audited-jobs-details-environment
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=1
// +optional
DetailsEnvironment *int `json:"detailsEnvironment,omitempty"`

// DetailsUserDefined enables capturing user-defined data (0=disabled, 1=enabled)
// Maps to rserver.conf: audited-jobs-details-user-defined
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=1
// +optional
DetailsUserDefined *int `json:"detailsUserDefined,omitempty"`
}

type WorkbenchLoggingConfig struct {
All *WorkbenchLoggingSection `json:"*,omitempty"`
}
Expand Down Expand Up @@ -974,6 +1031,17 @@ type WorkbenchRServerConfig struct {
WorkbenchApiAdminEnabled int `json:"workbench-api-admin-enabled,omitempty"`
WorkbenchApiSuperAdminEnabled int `json:"workbench-api-super-admin-enabled,omitempty"`
ForceAdminUiEnabled int `json:"force-admin-ui-enabled,omitempty"`
// Audited Jobs Configuration
// See: https://docs.posit.co/ide/server-pro/admin/auditing_and_monitoring/audited_workbench_jobs.html
AuditedJobs int `json:"audited-jobs,omitempty"`
AuditedJobsStoragePath string `json:"audited-jobs-storage-path,omitempty"`
AuditedJobsPrivateKeyPath string `json:"audited-jobs-private-key-path,omitempty"`
AuditedJobsPublicKeyPaths string `json:"audited-jobs-public-key-paths,omitempty"`
AuditedJobsLogLimit int `json:"audited-jobs-log-limit,omitempty"`
AuditedJobsDeletionExpiry int `json:"audited-jobs-deletion-expiry,omitempty"`
AuditedJobsVanillaRequired int `json:"audited-jobs-vanilla-required,omitempty"`
AuditedJobsDetailsEnvironment int `json:"audited-jobs-details-environment,omitempty"`
AuditedJobsDetailsUserDefined int `json:"audited-jobs-details-user-defined,omitempty"`
}

type WorkbenchLauncherConfig struct {
Expand Down
55 changes: 55 additions & 0 deletions api/core/v1beta1/workbench_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,61 @@ func TestWorkbenchConfig_GenerateConfigmap(t *testing.T) {
require.Contains(t, res["logging.conf"], "[*]\nlog-level")
}

func TestWorkbenchConfig_AuditedJobs(t *testing.T) {
wb := WorkbenchConfig{
WorkbenchIniConfig: WorkbenchIniConfig{
RServer: &WorkbenchRServerConfig{
AuditedJobs: 1,
AuditedJobsStoragePath: "/mnt/shared-storage/audited-jobs",
AuditedJobsPrivateKeyPath: "/etc/rstudio/audited-jobs-private-key.pem",
AuditedJobsPublicKeyPaths: "/etc/rstudio/audited-jobs-public-key.pem",
AuditedJobsLogLimit: 5000,
AuditedJobsDeletionExpiry: 60,
AuditedJobsVanillaRequired: 1,
AuditedJobsDetailsEnvironment: 1,
AuditedJobsDetailsUserDefined: 0,
},
},
}

res, err := wb.GenerateConfigmap()
require.Nil(t, err)
require.Contains(t, res["rserver.conf"], "audited-jobs=1\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-storage-path=/mnt/shared-storage/audited-jobs\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-private-key-path=/etc/rstudio/audited-jobs-private-key.pem\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-public-key-paths=/etc/rstudio/audited-jobs-public-key.pem\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-log-limit=5000\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-deletion-expiry=60\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-vanilla-required=1\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-details-environment=1\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-details-user-defined=0\n")
}

func TestWorkbenchConfig_AuditedJobsPartial(t *testing.T) {
wb := WorkbenchConfig{
WorkbenchIniConfig: WorkbenchIniConfig{
RServer: &WorkbenchRServerConfig{
AuditedJobs: 1,
AuditedJobsStoragePath: "/mnt/shared-storage/audited-jobs",
},
},
}

res, err := wb.GenerateConfigmap()
require.Nil(t, err)
require.Contains(t, res["rserver.conf"], "audited-jobs=1\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-storage-path=/mnt/shared-storage/audited-jobs\n")
// Unset int fields render as zero (consistent with all other WorkbenchRServerConfig int fields)
require.Contains(t, res["rserver.conf"], "audited-jobs-log-limit=0\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-deletion-expiry=0\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-vanilla-required=0\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-details-environment=0\n")
require.Contains(t, res["rserver.conf"], "audited-jobs-details-user-defined=0\n")
// Unset string fields should be omitted
require.NotContains(t, res["rserver.conf"], "audited-jobs-private-key-path=")
require.NotContains(t, res["rserver.conf"], "audited-jobs-public-key-paths=")
}

func TestWorkbenchConfig_GenerateSupervisorConfigmap(t *testing.T) {
wbc := WorkbenchConfig{
SupervisordIniConfig: SupervisordIniConfig{
Expand Down
50 changes: 50 additions & 0 deletions api/core/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 98 additions & 0 deletions client-go/applyconfiguration/core/v1beta1/auditedjobsconfig.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading