Spinnaker holds deployment rights in every cloud account and Kubernetes cluster it manages, so whoever can run a pipeline or reach its internal services can change production. This guide covers the customer side of securing a self-managed Spinnaker installation: authentication at Gate, authorisation with the Fiat service, account and application permissions, Fiat service accounts and managed service accounts for triggered pipelines, the cloud and Kubernetes credentials held by Clouddriver, TLS and logging. It is written for platform and release engineering teams and the security reviewers who sign off continuous delivery. For controls common to every delivery platform, see the CI/CD Security Guide and the GitOps Security Guide.
This is an independent guide from NHI Mgmt Group, based on Spinnaker's public documentation as of November 2026. It is not sponsored or reviewed by the Spinnaker project. Product features change, so confirm settings against Spinnaker's current documentation for the version you run.
Key takeaways
- Turn on Fiat, then define permissions everywhere. Fiat is off by default and, once on, resources with no permissions block stay unrestricted, so add READ and WRITE roles to every account and READ, WRITE and EXECUTE roles to every application.
- Expose only Gate and Deck. Gate (port 8084) is the API entry point and Deck (port 9000) the UI; Orca, Clouddriver, Front50, Fiat and the other services belong on a private network.
- Use managed service accounts for triggers. Pipeline permissions create a Fiat service account per pipeline from the roles the author holds, and only users with all those roles can then edit the pipeline.
- Scope Clouddriver's cloud credentials. Prefer node or pod roles over IAM users, narrow the managed-account role beyond the documented starting point, and restrict Kubernetes accounts to named namespaces with Roles rather than cluster-admin.
- Keep the admin role small. Members of fiat.admin.roles bypass almost all READ and WRITE restrictions; the docs themselves advise caution.
How identity and access work in Spinnaker
Spinnaker is a set of microservices. Deck is the browser UI and Gate is the API gateway that the UI and API callers talk to. Behind Gate, Orca orchestrates pipelines, Clouddriver makes mutating calls to cloud providers, Front50 stores application and pipeline metadata, Igor and Echo handle CI triggers, events and webhooks, Rosco bakes images, and Fiat makes authorisation decisions. Spinnaker integrates with your existing identity systems rather than running its own login.
| Layer | Component | Options in the docs | Customer action |
|---|---|---|---|
| Authentication | Gate | OAuth 2.0 (including Azure, G Suite, GitHub Organizations and Oracle Cloud), SAML 2.0, LDAP, X.509 client certificates | Use your corporate IdP with MFA for people; X.509 or an IdP-issued identity for API automation |
| Role resolution | Fiat role providers | YAML file, GitHub Teams, Google Groups, LDAP, SAML or OIDC groups (EXTERNAL roles fixed at login) | Map roles to groups owned by your IdP, not a hand-edited file |
| Authorisation | Fiat | Account READ and WRITE; application READ, WRITE and EXECUTE | Enable Fiat and set permissions on every account and application |
| Automation identity | Fiat service accounts and managed service accounts | Run As User, or roles on the pipeline trigger | Managed service accounts per pipeline |
| Cloud identity | Clouddriver and Rosco | Assumed roles in managed AWS accounts, kubeconfig per Kubernetes account | Workload identity, narrow roles, per-account scoping |
Fiat grants permissions to roles, never directly to users. Its documented model is an approve list that is open by default, so a resource with no access definition is available to everyone who can log in. Restricted applications disappear from the applications list and direct navigation returns 403 Forbidden.
Admin roles and privileged access
Account and application permissions
The authorisation docs give concrete examples of what each permission allows: deleting a load balancer needs WRITE on the account; updating a pipeline needs WRITE on the application; running a pipeline needs only EXECUTE on the application; and running an application's pipeline that deploys to another account needs EXECUTE on the application and WRITE on the target account.
| Resource | Permission | Allows | Notes |
|---|---|---|---|
| Account | READ | View cloud resources in the account | WRITE does not imply READ; grant both together |
| Account | WRITE | Change resources, deploy into the account | Reserve production accounts for release roles |
| Application | READ | View the application and its pipelines | By default READ also grants EXECUTE |
| Application | EXECUTE | Run pipelines | Set fiat.executeFallback: 'WRITE' in fiat-local.yml so READ no longer grants EXECUTE |
| Application | WRITE | Edit pipelines and application settings | Pipeline editors control what runs with the pipeline's identity |
Our recommendation at NHI Mgmt Group: set fiat.executeFallback to WRITE so viewing an application never implies the right to deploy it, and give production account WRITE only to the roles behind managed service accounts and a small release group.
Administrators
The admin page describes a role listed under fiat.admin.roles in fiat-local.yml (devops-admin in the example) whose members are freed from virtually all READ and WRITE restrictions, with a warning to proceed with caution. Keep this role mapped to a small IdP group with MFA, review membership quarterly and log its use. Separately, anyone with write access to the Spinnaker configuration (for example the *-local.yml files, the Kubernetes namespace that runs Spinnaker, or the Front50 storage bucket) can change authorisation itself, so treat those as administrative too.
Internal services
The docs describe Gate as the point where the UI and API callers enter, and user authentication is configured there. Design on the basis that Orca, Clouddriver, Front50 and Fiat should only ever be called by other Spinnaker services. Run them on a private network, use Kubernetes NetworkPolicy so only expected services can reach each port (for example Fiat on 7003 and Clouddriver on 7002), and expose only Gate and Deck through your ingress. The Fiat and Front50 service account endpoints used below are administrative APIs: keep them off any public listener.
Integrations, API tokens and service accounts
Triggered pipelines
Automated triggers (CI jobs, webhooks, schedules) run without a human session, so they need an identity to pass Fiat checks. Spinnaker documents two models.
- Fiat service accounts: permission abstractions, not authenticated accounts. An administrator creates one with a POST to Front50's /serviceAccounts endpoint containing a name and memberOf roles, then runs a Fiat sync (POST to /roles/sync). In the trigger, Run As User lists the service accounts a user may use. A user can assign one only if they hold every role it has, and the service account needs EXECUTE on the application. At run time the normal account and application checks apply.
- Managed service accounts (pipeline permissions): enabled with tasks.useManagedServiceAccounts: true in orca-local.yml and window.spinnakerSettings.feature.managedServiceAccounts = true in Deck's settings-local.js. The author selects roles they hold in the trigger's permissions, and a service account is created for that pipeline. After that only users holding all the selected roles can edit it. tasks.useSharedManagedServiceAccounts: true creates one account per unique role set, and migrations.migrateToManagedServiceAccounts: true in front50-local.yml migrates existing Run As User triggers.
The docs recommend pipeline permissions for most users. Plan the migration: when several triggers use different Run As User values, the managed account receives the combined roles, which can stop some users editing the pipeline. Pilot on a few applications first and keep the previous trigger JSON for rollback.
AWS accounts
The AWS EC2 provider docs describe a managing account where Spinnaker runs, with a spinnaker role that holds ec2:DescribeAvailabilityZones, ec2:DescribeRegions and sts:AssumeRole, and a spinnakerManaged role in each target account that trusts it. In Clouddriver configuration each aws.accounts entry sets assumeRole: role/spinnakerManaged, accountId, regions and a permissions block with READ and WRITE groups. The docs recommend IAM roles over IAM users with access keys, and node or pod roles (with pod identity or IRSA for advanced setups) for Spinnaker itself.
- Narrow the assume scope: the documented starting policy allows sts:AssumeRole on any resource; list the specific spinnakerManaged role ARNs instead.
- Narrow the managed role: the documented example attaches PowerUserAccess plus an inline PassRole-and-Certificates policy granting iam:PassRole and iam:ListServerCertificates. Replace these with the actions your deployments use, and restrict iam:PassRole to the instance roles you deploy with.
- Separate baking: the docs suggest scoping the Packer bake permissions to Rosco through service accounts or IRSA, rather than adding them to the main role.
- Permissions on every account: add a permissions block to each account entry so Fiat restricts who can deploy there.
See the Workload Identity Federation Guide for removing static keys.
Kubernetes accounts
Each Kubernetes account maps to a kubeconfig that the docs describe as acting like a private key for the cluster. The example creates spinnaker-service-account in the spinnaker namespace and binds it to cluster-admin, noting that more restrictive roles can be applied, and shows a spinnaker-role ClusterRole as an alternative. Setting the namespaces option limits an account to named namespaces, in which case use a Role and RoleBinding in each managed namespace. The documented role includes read on secrets, so scope it carefully. Our recommendation: one Spinnaker account per cluster and environment, restricted to named namespaces, with its kubeconfig stored as a Kubernetes secret readable only by Clouddriver. See the Kubernetes RBAC Guide.
TLS
The SSL docs cover enabling TLS on Gate in gate-local.yml (server.ssl.enabled: true with a PKCS12 key store) and on Deck through an Apache virtual host on port 9000 with SSLEngine on, or terminating TLS at a load balancer with gateHost, deck.baseUrl and Tomcat proxy header settings. Use certificates from your internal CA or a public CA rather than the self-signed example in production.
Logging and detection
| Source | What to watch | Why |
|---|---|---|
| Gate and IdP logs | Logins, failed logins, admin role sessions, API calls from automation identities | Account takeover and misuse of automation |
| Front50 and Fiat | Creation or deletion of service accounts, changes to application permissions, pipeline edits that change triggers or roles | Privilege changes inside Spinnaker |
| Orca execution history | Pipelines run outside change windows, manual runs of production pipelines, pipelines that deploy to unexpected accounts | Unauthorised deployment |
| Cloud audit logs | AssumeRole into spinnakerManaged from anything other than the spinnaker role; IAM and PassRole use | Credential misuse outside Spinnaker (ATT&CK T1078.004) |
| Kubernetes audit logs | Requests from spinnaker-service-account outside managed namespaces; secret reads | Scope creep and credential harvesting |
| Network | Connections to internal service ports from outside the Spinnaker namespace | Traffic that did not pass through Gate |
Metrics that show it is working:
- Percentage of accounts and applications with an explicit permissions block (target 100 per cent).
- Percentage of triggered pipelines using managed service accounts.
- Number of members in fiat.admin.roles, reviewed quarterly.
- Number of long-lived cloud keys used by Spinnaker components (target zero).
- Kubernetes accounts bound to cluster-admin (target zero).
Hardening baseline
- Inventory: list every Spinnaker account, its credential type and target, every application and its permissions, and every trigger and its identity. Apply Spinnaker releases promptly and follow the project's release notes.
- Authenticate at Gate with SAML or OAuth 2.0 against your IdP, with MFA, and enable TLS on Gate and Deck.
- Enable Fiat with an IdP-backed role provider, in a staging installation first, then add permissions to every account and application before production cut-over.
- Set fiat.executeFallback to WRITE and review who holds EXECUTE on production applications.
- Move triggers to managed service accounts and retire broad manual service accounts.
- Narrow cloud credentials: pod or node roles, specific AssumeRole targets, least-privilege managed roles, namespace-scoped Kubernetes accounts.
- Isolate internal services with NetworkPolicy and private networking.
- Roll back safely: keep previous *-local.yml files and account definitions in version control so an authorisation change that blocks deployments can be reverted quickly.
Common misconfigurations
- Fiat enabled but permissions left empty. Because unrestricted is the default for undefined resources, every logged-in user can still deploy to accounts without a permissions block.
- READ implying EXECUTE. With the default fallback, anyone who can view an application can run its pipelines.
- Broad Run As User accounts. A single service account with production roles reused across many pipelines spreads deployment rights widely.
- Starter cloud roles kept in production. PowerUserAccess and AssumeRole on any resource were documented to get started; replace them with scoped policies.
- Cluster-admin kubeconfigs. The example binding is convenient but gives Spinnaker, and anyone who can deploy through it, full control of the cluster.
- Internal services reachable. Orca, Clouddriver and Front50 are meant to sit behind Gate; keep them off public listeners so every user request passes through Gate.
Framework mapping
| Control area | NIST SP 800-53 Rev 5 | NIST CSF 2.0 | ISO/IEC 27001:2022 Annex A |
|---|---|---|---|
| Fiat roles, admin role and least privilege | AC-6 | PR.AA-05 | A.8.2 Privileged access rights |
| Cloud and Kubernetes credentials | IA-5 | PR.AA-05 | A.5.17 Authentication information |
| Pipeline and platform configuration | CM-6 | PR.PS-01 | A.8.9 Configuration management |
| Deployment logging and review | AU-2, AU-6 | DE.CM-09 | A.8.15 Logging |
See the OWASP CI/CD Top 10 CICD-SEC-5 Insufficient PBAC Guide for pipeline-based access controls across platforms.
Practitioner checklist
- Authenticate at Gate with your IdP and MFA; enable TLS on Gate and Deck.
- Enable Fiat with an IdP-backed role provider.
- Add READ and WRITE to every account and READ, WRITE and EXECUTE to every application.
- Set fiat.executeFallback to WRITE.
- Keep fiat.admin.roles to a small, reviewed group.
- Use managed service accounts for triggered pipelines.
- Run Spinnaker on node or pod roles and scope AssumeRole and managed roles to what deployments use.
- Restrict Kubernetes accounts to named namespaces with Roles.
- Keep internal services on a private network behind NetworkPolicy.
- Alert on service account changes, permission changes and AssumeRole from unexpected principals.
Standards and references
- Spinnaker: Security
- Spinnaker: Authorization (RBAC)
- Spinnaker: Service Accounts
- Spinnaker: Pipeline Permissions
- Spinnaker: Administrator Functionality
- Spinnaker: AWS EC2 Provider Setup
- Spinnaker: Kubernetes Provider
- Spinnaker: Microservices Overview
Related NHI Mgmt Group resources: CI/CD Pipeline Identity Security Guide · Software Supply Chain Security Guide · Kubernetes NHI Security Guide · Secrets Management Guide