[{"data":1,"prerenderedAt":722},["ShallowReactive",2],{"/en-us/blog/cicd-tunnel-impersonation/":3,"navigation-en-us":38,"banner-en-us":467,"footer-en-us":484,"Cesar Saavedra":694,"next-steps-en-us":707},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":28,"_id":31,"_type":32,"title":33,"_source":34,"_file":35,"_stem":36,"_extension":37},"/en-us/blog/cicd-tunnel-impersonation","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"Fine-grained permissions with impersonation in CI/CD tunnel","Learn how to use use fine-grained permissions via generic impersonation in CI/CD Tunnel","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667435/Blog/Hero%20Images/tunnel.jpg","https://about.gitlab.com/blog/cicd-tunnel-impersonation","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How to use fine-grained permissions via generic impersonation in CI/CD Tunnel\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Cesar Saavedra\"}],\n        \"datePublished\": \"2022-02-01\",\n      }",{"title":17,"description":10,"authors":18,"heroImage":11,"date":20,"body":21,"category":22,"tags":23},"How to use fine-grained permissions via generic impersonation in CI/CD Tunnel",[19],"Cesar Saavedra","2022-02-01","\nThe [CI/CD Tunnel](https://docs.gitlab.com/ee/user/clusters/agent/ci_cd_workflow.html), which leverages the [GitLab Agent for Kubernetes](https://docs.gitlab.com/ee/user/clusters/agent/), enables users to access Kubernetes clusters from GitLab CI/CD jobs. In this blog post, we review how you can securely access your clusters from your CI/CD pipelines by using generic impersonation. In addition, we will briefly cover the activity list of the GitLab Agent for Kubernetes, a capability recently introduced by GitLab, that can help you detect and troubleshoot faulty events.\n\n## Using impersonation with your CI/CD tunnel\n\nThe CI/CD Tunnel leverages the GitLab Agent for Kubernetes, which permits the secure connectivity between GitLab and your Kubernetes cluster without the need to expose your cluster to the internet and outside your firewall. The CI/CD Tunnel allows you to connect to your Kubernetes cluster from your CI/CD jobs/pipelines.\n\nBy default, the CI/CD Tunnel inherits all the permissions from the service account used to install the Agent in the cluster. However, fine-grained permissions can be used in conjunction with the CI/CD Tunnel to restrict and manage access to your cluster resources.\n\nFine-grained permissions control with the CI/CD tunnel via impersonation:\n\n- Allows you to leverage your K8s authorization capabilities to limit the permissions of what can be done with the CI/CD tunnel on your running cluster\n\n- Lowers the risk of providing unlimited access to your K8s cluster with the CI/CD tunnel\n\n- Segments fine-grained permissions with the CI/CD tunnel at the project or group level\n\n- Controls permissions with the CI/CD tunnel at the username or service account\n\nTo restrict access to your cluster, you can use impersonation. To specify impersonations, use the access_as attribute in your Agent's configuration file and use Kubernetes RBAC rules to manage impersonated account permissions.\n\nYou can impersonate:\n- The Agent itself (default)\n= The CI job that accesses the cluster\n- A specific user or system account defined within the cluster\n\n## Steps to exercise impersonation with the CI/CD Tunnel\n\nLet's go through the steps on how you can exercise impersonation with the CI/CD Tunnel.\n\n### Creating your Kubernetes cluster\n\nIn order to exercise the capabilities described above, we need a Kubernetes cluster. Although, you can use any Kubernetes distribution, for this example, we create a GKE Standard Kubernetes cluster and name it \"csaavedra-ga4k-cluster\". We select the zone and version 1.21 of Kubernetes and ensure that our cluster will have three nodes. We leave the security and metadata screens with their defaulted values and click on the create button:\n\n![Creating a GKE cluster](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/0-gke-creation.png){: .shadow.medium.center.wrap-text}\nCreating a GKE cluster\n{: .note.text-center}\n\n### Sample projects to be used\n\nLet's proceed now to this [top-level group](https://gitlab.com/tech-marketing/sandbox/gl-14-5-cs-demos), which contains three projects, which we will use to show impersonation with the CI/CD tunnel. You can do this at the project or group level. In this example, we will show setting impersonation at the project level:\n\n![Project structure in GitLab](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/1-project-struct.png){: .shadow.medium.center.wrap-text}\nProject structure in GitLab\n{: .note.text-center}\n\nProject \"ga4k\" will configure the GitLab Agent for Kubernetes and also set impersonations with the CI/CD tunnel. Project \"sample-application\" will use the CI/CD tunnel, managed by the agent, to connect to the Kubernetes cluster and execute a pipeline using different impersonations. Project \"cluster-management\" will also use the CI/CD tunnel to connect to the cluster and install the Ingress application on it.\n\nNot only does the CI/CD tunnel streamline the deployment, management, and monitoring of Kubernetes-native applications, but it also does it securely and safely by using impersonations that leverage your Kubernetes cluster's RBAC rules.\n\nProject \"ga4k\" contains and manages the configuration for the GitLab Agent for K8s called \"csaavedra-agentk\". Looking at its \"config.yaml\" file, we see that the agent points to itself for manifest projects, but most importantly, it provides CI/CD tunnel access to two projects: \"sample-application\" and \"cluster-management\". This means that these two projects' CI/CD pipelines will have access to the K8s cluster that the agent is securely connected to:\n\n![The GitLab Agent for K8s configuration](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/2-agent-config.png){: .shadow.medium.center.wrap-text}\nThe GitLab Agent for K8s configuration\n{: .note.text-center}\n\nProject \"sample-application\" has a pipeline, which we will later execute under different impersonations. And project \"cluster-management\" has a pipeline that will install only the Ingress application on the Kubernetes cluster, as configured in its helmfile.yaml file:\n\n![Deployable applications in cluster-management project](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/3-cluster-mgmt-helmfile.png){: .shadow.medium.center.wrap-text}\nDeployable applications in cluster-management project\n{: .note.text-center}\n\n### Connecting the Agent to your Kubernetes cluster\n\nLet's head back to project \"ga4k\" and connect to the Kubernetes cluster via the agent. We select agent \"csaavedra-agentk\" to register with GitLab:\n\n![List of defined agents](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/4-agents-popdown.png){: .shadow.medium.center.wrap-text}\nList of defined agents\n{: .note.text-center}\n\nThis step generates a token that we can use to install the agent on the cluster. We copy the Docker command to our local desktop for later use. Notice that the command includes the generated token, which you can also copy:\n\n![Docker command to deploy agent to your K8s cluster](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/5-docker-cmd.png){: .shadow.medium.center.wrap-text}\nDocker command to deploy agent to your K8s cluster\n{: .note.text-center}\n\nFrom a local command window, we ensure that our connectivity parameters to GCP are correct:\n\n![Checking your GCP connectivity parameters](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/6-gcp-connectivity.png){: .shadow.medium.center.wrap-text}\nChecking your GCP connectivity parameters\n{: .note.text-center}\n\nWe then add the credentials to our kubeconfig file to connect to our newly created Kubernetes cluster \"csaavedra-ga4k-cluster\" and verify that our context is set to it:\n\n![Adding your cluster credentials to your kubeconfig](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/7-adding-creds.png){: .shadow.medium.center.wrap-text}\nAdding the credentials of your cluster to your kubeconfig\n{: .note.text-center}\n\nOnce this is done, we can list all the pods that are up and running on the cluster by entering `kubectl get pods –all-namespaces`:\n\n![Listing the pods in your running cluster](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/8-listing-pods.png){: .shadow.medium.center.wrap-text}\nListing the pods in your running cluster\n{: .note.text-center}\n\nFinally, we paste the docker command that will install the GitLab Agent for Kubernetes to this cluster making sure that its namespace is \"ga4k-agent\":\n\n![Deploying the agent to your K8s cluster](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/9-pasted-docker-cmd.png){: .shadow.medium.center.wrap-text}\nDeploying the agent to your K8s cluster\n{: .note.text-center}\n\nWe list the pods one more time to check that the agent pod is up and running on the cluster:\n\n![Agent up and running on your K8s cluster](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/10-agent-up.png){: .shadow.medium.center.wrap-text}\nAgent up and running on your K8s cluster\n{: .note.text-center}\n\nThe screen will refresh and show our Kubernetes cluster connected via the agent:\n\n![Agent connected to your K8s cluster](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/11-agent-connected.png){: .shadow.large.center.wrap-text}\nAgent connected to your K8s cluster\n{: .note.text-center}\n\n### The Agent's Activity Information page\n\nClicking on the agent name takes us to the Agent's Activity Information page, which lists agent events in real time. This information can help monitor your cluster's activity and detect and troubleshoot faulty events from your cluster. Connection and token information is currently listed with more events coming in future releases:\n\n![Agent activity information page](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/12-agent-activity.png){: .shadow.small.center.wrap-text}\nAgent activity information page\n{: .note.text-center}\n\n### Deploying Ingress to your Kubernetes cluster using default impersonation\n\nBy default, the CI/CD Tunnel inherits all the permissions from the service account used to install the agent in the cluster. Per the agent's configuration, the CI/CD pipelines of the \"cluster-management\" project will have access to the K8s cluster that the agent is securely connected to. Let's leverage this connectivity to deploy the Ingress application to the Kubernetes cluster from project \"cluster-management\". Let's make a small update to the project pipeline to launch it. Once the pipeline launches, we navigate to its detail view to track its completion:\n\n![Project \"cluster-management\" pipeline completed](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/13-cluster-mgmt-pipeline.png){: .shadow.small.center.wrap-text}\nProject \"cluster-management\" pipeline completed\n{: .note.text-center}\n\nand check the log of its **apply** job to verify that it was able to switch to the agent's context and successfully ran all the installation steps:\n\n![Ingress deployed to your cluster via CI/CD Tunnel using default impersonation](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/14-apply-job-log.png){: .shadow.medium.center.wrap-text}\nIngress deployed to your cluster via CI/CD Tunnel using default impersonation\n{: .note.text-center}\n\nFor further verification, we list the pods in the cluster and check that the ingress pods are up and running:\n\n![Ingress pods up and running](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/15-ingress-pods-up.png){: .shadow.medium.center.wrap-text}\nIngress pods up and running on your cluster\n{: .note.text-center}\n\n### Start trailing the agent's log file to watch updates\n\nBefore we start the impersonation use cases, let's start trailing the agent's log file from a command window:\n\n![Trailing agent log from the command line](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/16-trail-agent-log.png){: .shadow.medium.center.wrap-text}\nTrailing agent log from the command line\n{: .note.text-center}\n\nAnd also let's increase its logging to debug:\n\n![Increasing the agent log level to debug](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/17-agent-logging-level.png){: .shadow.medium.center.wrap-text}\nIncreasing the agent log level to debug\n{: .note.text-center}\n\n### Running impersonation using access_as:ci_job\n\nLet's now impersonate the CI job that accesses the cluster. For this, we modify the agent's configuration and add the \"access_as\" attribute with the \"ci_job\" tag under it:\n\n![Impersonating the CI job](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/18-ci-job-impersonation.png){: .shadow.medium.center.wrap-text}\nImpersonating the CI job\n{: .note.text-center}\n\nAs we save the updated configuration, we verify in the log output that the update has taken place in the running agent:\n\n![Agent updated with CI job impersonation](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/19-agent-conf-updated.png){: .shadow.large.center.wrap-text}\nAgent updated with CI job impersonation\n{: .note.text-center}\n\nNotice that the pipeline of the \"sample-application\" project has a test stage and a test job. It sets the variable KUBE_CONTEXT first, loads an image with the version of kubectl that matches the version of the K8s cluster, and executes two kubectl commands that access the remote cluster via the agent:\n\n![Project \"sample-application\" pipeline](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/20-sample-application-pipeline.png){: .shadow.medium.center.wrap-text}\nProject \"sample-application\" pipeline\n{: .note.text-center}\n\nWe manually execute the pipeline of the \"sample-application\" project and verify in the job log output that the context switch was successful and that the kubectl commands executed correctly:\n\n![Job log output with CI impersonation](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/21-ci-impersonation-job-log.png){: .shadow.medium.center.wrap-text}\nJob log output with CI impersonation\n{: .note.text-center}\n\n### Running impersonation using access_as:impersonate:username\n\nThe last use case is the impersonation of a specific user or system account defined within the cluster. I have pre-created a service account called \"jane\" on the Kubernetes cluster under the \"default\" namespace. And \"jane\" has been given the permission to do a \"get\", \"list\", and \"watch\" on the cluster pods as you can see by the output in the command window:\n\n![Jane user with permission to list pods](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/22-jane-and-perms.png){: .shadow.medium.center.wrap-text}\nJane user with permission to list pods\n{: .note.text-center}\n\nRemember that the service account \"gitlab-agent\" under namespace \"ga4k-agent\" was created earlier when we installed the agent by running the Docker command. In order for the agent to be able to impersonate another service account or user, it needs to have the permissions to do so. We do this by creating a clusterrole \"impersonate\" for impersonating users, groups, and service accounts, and then create a clusterrolebinding \"allowimpersonator\" to give these permissions for the \"default\" namespace to the agent \"gitlab-agent\" in the \"ga4k-agent\" namespace:\n\n![Giving impersonation permission to agent](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/23-clusterrole-perm-to-agent.png){: .shadow.large.center.wrap-text}\nGiving impersonation permission to agent\n{: .note.text-center}\n\nWe then edit the agent's configuration and add the \"impersonate\" attribute and provide the service account for \"jane\" as the parameter for the \"username\" tag:\n\n![Impersonating a specific user](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/24-user-impersonation.png){: .shadow.medium.center.wrap-text}\nImpersonating a specific user called jane\n{: .note.text-center}\n\nAs we commit the changes, we check the log output to verify that the update has taken place in the running agent:\n\n![Agent updated with user impersonation](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/25-agent-conf-updated.png){: .shadow.large.center.wrap-text}\nAgent updated with user impersonation\n{: .note.text-center}\n\nSince we know that \"jane\" has the permission to list the running pods in the cluster, let's head to the project \"sample-application\" pipeline and add the command \"kubectl get pods –all-namespaces\" to it:\n\n![Adding get pods command that jane is allowed to run](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/26-adding-get-pods-cmd.png){: .shadow.medium.center.wrap-text}\nAdding get pods command that jane is allowed to run\n{: .note.text-center}\n\nWe commit the update and head over to the running pipeline and drill into the \"test\" job log output to see that the context switch was successful and that the kubectl commands executed correctly, including the listing of the running pods in the cluster:\n\n![Job output for pipeline impersonation jane](https://about.gitlab.com/images/blogimages/cicd-tunnel-impersonate/27-user-impersonation-job-log.png){: .shadow.medium.center.wrap-text}\nJob output for pipeline impersonation jane\n{: .note.text-center}\n\n## Conclusion\n\nIn this blog post, we reviewed how you can securely access your Kubernetes clusters from your CI/CD pipelines by using generic impersonation.  In addition, we showed the activity list of the GitLab Agent for Kubernetes, which can help you detect and troubleshoot faulty events from your cluster.\n\nTo see these capabilities in action, check out the following video:\n\n\u003C!-- blank line -->\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube.com/embed/j8SJuHd7Zsw\" frameborder=\"0\" allowfullscreen=\"true\"> \u003C/iframe>\n\u003C/figure>\n\u003C!-- blank line -->\n\nCover image by Jakob Søby on [Unsplash](https://www.unsplash.com)\n{: .note}\n","engineering",[24,25,26,27],"releases","CI","CD","kubernetes",{"slug":29,"featured":6,"template":30},"cicd-tunnel-impersonation","BlogPost","content:en-us:blog:cicd-tunnel-impersonation.yml","yaml","Cicd Tunnel Impersonation","content","en-us/blog/cicd-tunnel-impersonation.yml","en-us/blog/cicd-tunnel-impersonation","yml",{"_path":39,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"data":41,"_id":463,"_type":32,"title":464,"_source":34,"_file":465,"_stem":466,"_extension":37},"/shared/en-us/main-navigation","en-us",{"logo":42,"freeTrial":47,"sales":52,"login":57,"items":62,"search":394,"minimal":425,"duo":444,"pricingDeployment":453},{"config":43},{"href":44,"dataGaName":45,"dataGaLocation":46},"/","gitlab logo","header",{"text":48,"config":49},"Get free trial",{"href":50,"dataGaName":51,"dataGaLocation":46},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":53,"config":54},"Talk to sales",{"href":55,"dataGaName":56,"dataGaLocation":46},"/sales/","sales",{"text":58,"config":59},"Sign in",{"href":60,"dataGaName":61,"dataGaLocation":46},"https://gitlab.com/users/sign_in/","sign in",[63,107,205,210,315,375],{"text":64,"config":65,"cards":67,"footer":90},"Platform",{"dataNavLevelOne":66},"platform",[68,74,82],{"title":64,"description":69,"link":70},"The most comprehensive AI-powered DevSecOps Platform",{"text":71,"config":72},"Explore our Platform",{"href":73,"dataGaName":66,"dataGaLocation":46},"/platform/",{"title":75,"description":76,"link":77},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":78,"config":79},"Meet GitLab Duo",{"href":80,"dataGaName":81,"dataGaLocation":46},"/gitlab-duo/","gitlab duo ai",{"title":83,"description":84,"link":85},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":86,"config":87},"Learn more",{"href":88,"dataGaName":89,"dataGaLocation":46},"/why-gitlab/","why gitlab",{"title":91,"items":92},"Get started with",[93,98,103],{"text":94,"config":95},"Platform Engineering",{"href":96,"dataGaName":97,"dataGaLocation":46},"/solutions/platform-engineering/","platform engineering",{"text":99,"config":100},"Developer Experience",{"href":101,"dataGaName":102,"dataGaLocation":46},"/developer-experience/","Developer experience",{"text":104,"config":105},"MLOps",{"href":106,"dataGaName":104,"dataGaLocation":46},"/topics/devops/the-role-of-ai-in-devops/",{"text":108,"left":109,"config":110,"link":112,"lists":116,"footer":187},"Product",true,{"dataNavLevelOne":111},"solutions",{"text":113,"config":114},"View all Solutions",{"href":115,"dataGaName":111,"dataGaLocation":46},"/solutions/",[117,142,166],{"title":118,"description":119,"link":120,"items":125},"Automation","CI/CD and automation to accelerate deployment",{"config":121},{"icon":122,"href":123,"dataGaName":124,"dataGaLocation":46},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[126,130,134,138],{"text":127,"config":128},"CI/CD",{"href":129,"dataGaLocation":46,"dataGaName":127},"/solutions/continuous-integration/",{"text":131,"config":132},"AI-Assisted Development",{"href":80,"dataGaLocation":46,"dataGaName":133},"AI assisted development",{"text":135,"config":136},"Source Code Management",{"href":137,"dataGaLocation":46,"dataGaName":135},"/solutions/source-code-management/",{"text":139,"config":140},"Automated Software Delivery",{"href":123,"dataGaLocation":46,"dataGaName":141},"Automated software delivery",{"title":143,"description":144,"link":145,"items":150},"Security","Deliver code faster without compromising security",{"config":146},{"href":147,"dataGaName":148,"dataGaLocation":46,"icon":149},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[151,156,161],{"text":152,"config":153},"Application Security Testing",{"href":154,"dataGaName":155,"dataGaLocation":46},"/solutions/application-security-testing/","Application security testing",{"text":157,"config":158},"Software Supply Chain Security",{"href":159,"dataGaLocation":46,"dataGaName":160},"/solutions/supply-chain/","Software supply chain security",{"text":162,"config":163},"Software Compliance",{"href":164,"dataGaName":165,"dataGaLocation":46},"/solutions/software-compliance/","software compliance",{"title":167,"link":168,"items":173},"Measurement",{"config":169},{"icon":170,"href":171,"dataGaName":172,"dataGaLocation":46},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[174,178,182],{"text":175,"config":176},"Visibility & Measurement",{"href":171,"dataGaLocation":46,"dataGaName":177},"Visibility and Measurement",{"text":179,"config":180},"Value Stream Management",{"href":181,"dataGaLocation":46,"dataGaName":179},"/solutions/value-stream-management/",{"text":183,"config":184},"Analytics & Insights",{"href":185,"dataGaLocation":46,"dataGaName":186},"/solutions/analytics-and-insights/","Analytics and insights",{"title":188,"items":189},"GitLab for",[190,195,200],{"text":191,"config":192},"Enterprise",{"href":193,"dataGaLocation":46,"dataGaName":194},"/enterprise/","enterprise",{"text":196,"config":197},"Small Business",{"href":198,"dataGaLocation":46,"dataGaName":199},"/small-business/","small business",{"text":201,"config":202},"Public Sector",{"href":203,"dataGaLocation":46,"dataGaName":204},"/solutions/public-sector/","public sector",{"text":206,"config":207},"Pricing",{"href":208,"dataGaName":209,"dataGaLocation":46,"dataNavLevelOne":209},"/pricing/","pricing",{"text":211,"config":212,"link":214,"lists":218,"feature":302},"Resources",{"dataNavLevelOne":213},"resources",{"text":215,"config":216},"View all resources",{"href":217,"dataGaName":213,"dataGaLocation":46},"/resources/",[219,252,274],{"title":220,"items":221},"Getting started",[222,227,232,237,242,247],{"text":223,"config":224},"Install",{"href":225,"dataGaName":226,"dataGaLocation":46},"/install/","install",{"text":228,"config":229},"Quick start guides",{"href":230,"dataGaName":231,"dataGaLocation":46},"/get-started/","quick setup checklists",{"text":233,"config":234},"Learn",{"href":235,"dataGaLocation":46,"dataGaName":236},"https://university.gitlab.com/","learn",{"text":238,"config":239},"Product documentation",{"href":240,"dataGaName":241,"dataGaLocation":46},"https://docs.gitlab.com/","product documentation",{"text":243,"config":244},"Best practice videos",{"href":245,"dataGaName":246,"dataGaLocation":46},"/getting-started-videos/","best practice videos",{"text":248,"config":249},"Integrations",{"href":250,"dataGaName":251,"dataGaLocation":46},"/integrations/","integrations",{"title":253,"items":254},"Discover",[255,260,264,269],{"text":256,"config":257},"Customer success stories",{"href":258,"dataGaName":259,"dataGaLocation":46},"/customers/","customer success stories",{"text":261,"config":262},"Blog",{"href":263,"dataGaName":5,"dataGaLocation":46},"/blog/",{"text":265,"config":266},"Remote",{"href":267,"dataGaName":268,"dataGaLocation":46},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":270,"config":271},"TeamOps",{"href":272,"dataGaName":273,"dataGaLocation":46},"/teamops/","teamops",{"title":275,"items":276},"Connect",[277,282,287,292,297],{"text":278,"config":279},"GitLab Services",{"href":280,"dataGaName":281,"dataGaLocation":46},"/services/","services",{"text":283,"config":284},"Community",{"href":285,"dataGaName":286,"dataGaLocation":46},"/community/","community",{"text":288,"config":289},"Forum",{"href":290,"dataGaName":291,"dataGaLocation":46},"https://forum.gitlab.com/","forum",{"text":293,"config":294},"Events",{"href":295,"dataGaName":296,"dataGaLocation":46},"/events/","events",{"text":298,"config":299},"Partners",{"href":300,"dataGaName":301,"dataGaLocation":46},"/partners/","partners",{"backgroundColor":303,"textColor":304,"text":305,"image":306,"link":310},"#2f2a6b","#fff","Insights for the future of software development",{"altText":307,"config":308},"the source promo card",{"src":309},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":311,"config":312},"Read the latest",{"href":313,"dataGaName":314,"dataGaLocation":46},"/the-source/","the source",{"text":316,"config":317,"lists":319},"Company",{"dataNavLevelOne":318},"company",[320],{"items":321},[322,327,333,335,340,345,350,355,360,365,370],{"text":323,"config":324},"About",{"href":325,"dataGaName":326,"dataGaLocation":46},"/company/","about",{"text":328,"config":329,"footerGa":332},"Jobs",{"href":330,"dataGaName":331,"dataGaLocation":46},"/jobs/","jobs",{"dataGaName":331},{"text":293,"config":334},{"href":295,"dataGaName":296,"dataGaLocation":46},{"text":336,"config":337},"Leadership",{"href":338,"dataGaName":339,"dataGaLocation":46},"/company/team/e-group/","leadership",{"text":341,"config":342},"Team",{"href":343,"dataGaName":344,"dataGaLocation":46},"/company/team/","team",{"text":346,"config":347},"Handbook",{"href":348,"dataGaName":349,"dataGaLocation":46},"https://handbook.gitlab.com/","handbook",{"text":351,"config":352},"Investor relations",{"href":353,"dataGaName":354,"dataGaLocation":46},"https://ir.gitlab.com/","investor relations",{"text":356,"config":357},"Trust Center",{"href":358,"dataGaName":359,"dataGaLocation":46},"/security/","trust center",{"text":361,"config":362},"AI Transparency Center",{"href":363,"dataGaName":364,"dataGaLocation":46},"/ai-transparency-center/","ai transparency center",{"text":366,"config":367},"Newsletter",{"href":368,"dataGaName":369,"dataGaLocation":46},"/company/contact/","newsletter",{"text":371,"config":372},"Press",{"href":373,"dataGaName":374,"dataGaLocation":46},"/press/","press",{"text":376,"config":377,"lists":378},"Contact us",{"dataNavLevelOne":318},[379],{"items":380},[381,384,389],{"text":53,"config":382},{"href":55,"dataGaName":383,"dataGaLocation":46},"talk to sales",{"text":385,"config":386},"Get help",{"href":387,"dataGaName":388,"dataGaLocation":46},"/support/","get help",{"text":390,"config":391},"Customer portal",{"href":392,"dataGaName":393,"dataGaLocation":46},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":395,"login":396,"suggestions":403},"Close",{"text":397,"link":398},"To search repositories and projects, login to",{"text":399,"config":400},"gitlab.com",{"href":60,"dataGaName":401,"dataGaLocation":402},"search login","search",{"text":404,"default":405},"Suggestions",[406,408,412,414,418,422],{"text":75,"config":407},{"href":80,"dataGaName":75,"dataGaLocation":402},{"text":409,"config":410},"Code Suggestions (AI)",{"href":411,"dataGaName":409,"dataGaLocation":402},"/solutions/code-suggestions/",{"text":127,"config":413},{"href":129,"dataGaName":127,"dataGaLocation":402},{"text":415,"config":416},"GitLab on AWS",{"href":417,"dataGaName":415,"dataGaLocation":402},"/partners/technology-partners/aws/",{"text":419,"config":420},"GitLab on Google Cloud",{"href":421,"dataGaName":419,"dataGaLocation":402},"/partners/technology-partners/google-cloud-platform/",{"text":423,"config":424},"Why GitLab?",{"href":88,"dataGaName":423,"dataGaLocation":402},{"freeTrial":426,"mobileIcon":431,"desktopIcon":436,"secondaryButton":439},{"text":427,"config":428},"Start free trial",{"href":429,"dataGaName":51,"dataGaLocation":430},"https://gitlab.com/-/trials/new/","nav",{"altText":432,"config":433},"Gitlab Icon",{"src":434,"dataGaName":435,"dataGaLocation":430},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":432,"config":437},{"src":438,"dataGaName":435,"dataGaLocation":430},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":440,"config":441},"Get Started",{"href":442,"dataGaName":443,"dataGaLocation":430},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":445,"mobileIcon":449,"desktopIcon":451},{"text":446,"config":447},"Learn more about GitLab Duo",{"href":80,"dataGaName":448,"dataGaLocation":430},"gitlab duo",{"altText":432,"config":450},{"src":434,"dataGaName":435,"dataGaLocation":430},{"altText":432,"config":452},{"src":438,"dataGaName":435,"dataGaLocation":430},{"freeTrial":454,"mobileIcon":459,"desktopIcon":461},{"text":455,"config":456},"Back to pricing",{"href":208,"dataGaName":457,"dataGaLocation":430,"icon":458},"back to pricing","GoBack",{"altText":432,"config":460},{"src":434,"dataGaName":435,"dataGaLocation":430},{"altText":432,"config":462},{"src":438,"dataGaName":435,"dataGaLocation":430},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":468,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"title":469,"button":470,"image":475,"config":479,"_id":481,"_type":32,"_source":34,"_file":482,"_stem":483,"_extension":37},"/shared/en-us/banner","is now in public beta!",{"text":471,"config":472},"Try the Beta",{"href":473,"dataGaName":474,"dataGaLocation":46},"/gitlab-duo/agent-platform/","duo banner",{"altText":476,"config":477},"GitLab Duo Agent Platform",{"src":478},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":480},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":485,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"data":486,"_id":690,"_type":32,"title":691,"_source":34,"_file":692,"_stem":693,"_extension":37},"/shared/en-us/main-footer",{"text":487,"source":488,"edit":494,"contribute":499,"config":504,"items":509,"minimal":682},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":489,"config":490},"View page source",{"href":491,"dataGaName":492,"dataGaLocation":493},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":495,"config":496},"Edit this page",{"href":497,"dataGaName":498,"dataGaLocation":493},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":500,"config":501},"Please contribute",{"href":502,"dataGaName":503,"dataGaLocation":493},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":505,"facebook":506,"youtube":507,"linkedin":508},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[510,533,589,618,652],{"title":64,"links":511,"subMenu":516},[512],{"text":513,"config":514},"DevSecOps platform",{"href":73,"dataGaName":515,"dataGaLocation":493},"devsecops platform",[517],{"title":206,"links":518},[519,523,528],{"text":520,"config":521},"View plans",{"href":208,"dataGaName":522,"dataGaLocation":493},"view plans",{"text":524,"config":525},"Why Premium?",{"href":526,"dataGaName":527,"dataGaLocation":493},"/pricing/premium/","why premium",{"text":529,"config":530},"Why Ultimate?",{"href":531,"dataGaName":532,"dataGaLocation":493},"/pricing/ultimate/","why ultimate",{"title":534,"links":535},"Solutions",[536,541,543,545,550,555,559,562,566,571,573,576,579,584],{"text":537,"config":538},"Digital transformation",{"href":539,"dataGaName":540,"dataGaLocation":493},"/topics/digital-transformation/","digital transformation",{"text":152,"config":542},{"href":154,"dataGaName":152,"dataGaLocation":493},{"text":141,"config":544},{"href":123,"dataGaName":124,"dataGaLocation":493},{"text":546,"config":547},"Agile development",{"href":548,"dataGaName":549,"dataGaLocation":493},"/solutions/agile-delivery/","agile delivery",{"text":551,"config":552},"Cloud transformation",{"href":553,"dataGaName":554,"dataGaLocation":493},"/topics/cloud-native/","cloud transformation",{"text":556,"config":557},"SCM",{"href":137,"dataGaName":558,"dataGaLocation":493},"source code management",{"text":127,"config":560},{"href":129,"dataGaName":561,"dataGaLocation":493},"continuous integration & delivery",{"text":563,"config":564},"Value stream management",{"href":181,"dataGaName":565,"dataGaLocation":493},"value stream management",{"text":567,"config":568},"GitOps",{"href":569,"dataGaName":570,"dataGaLocation":493},"/solutions/gitops/","gitops",{"text":191,"config":572},{"href":193,"dataGaName":194,"dataGaLocation":493},{"text":574,"config":575},"Small business",{"href":198,"dataGaName":199,"dataGaLocation":493},{"text":577,"config":578},"Public sector",{"href":203,"dataGaName":204,"dataGaLocation":493},{"text":580,"config":581},"Education",{"href":582,"dataGaName":583,"dataGaLocation":493},"/solutions/education/","education",{"text":585,"config":586},"Financial services",{"href":587,"dataGaName":588,"dataGaLocation":493},"/solutions/finance/","financial services",{"title":211,"links":590},[591,593,595,597,600,602,604,606,608,610,612,614,616],{"text":223,"config":592},{"href":225,"dataGaName":226,"dataGaLocation":493},{"text":228,"config":594},{"href":230,"dataGaName":231,"dataGaLocation":493},{"text":233,"config":596},{"href":235,"dataGaName":236,"dataGaLocation":493},{"text":238,"config":598},{"href":240,"dataGaName":599,"dataGaLocation":493},"docs",{"text":261,"config":601},{"href":263,"dataGaName":5,"dataGaLocation":493},{"text":256,"config":603},{"href":258,"dataGaName":259,"dataGaLocation":493},{"text":265,"config":605},{"href":267,"dataGaName":268,"dataGaLocation":493},{"text":278,"config":607},{"href":280,"dataGaName":281,"dataGaLocation":493},{"text":270,"config":609},{"href":272,"dataGaName":273,"dataGaLocation":493},{"text":283,"config":611},{"href":285,"dataGaName":286,"dataGaLocation":493},{"text":288,"config":613},{"href":290,"dataGaName":291,"dataGaLocation":493},{"text":293,"config":615},{"href":295,"dataGaName":296,"dataGaLocation":493},{"text":298,"config":617},{"href":300,"dataGaName":301,"dataGaLocation":493},{"title":316,"links":619},[620,622,624,626,628,630,632,636,641,643,645,647],{"text":323,"config":621},{"href":325,"dataGaName":318,"dataGaLocation":493},{"text":328,"config":623},{"href":330,"dataGaName":331,"dataGaLocation":493},{"text":336,"config":625},{"href":338,"dataGaName":339,"dataGaLocation":493},{"text":341,"config":627},{"href":343,"dataGaName":344,"dataGaLocation":493},{"text":346,"config":629},{"href":348,"dataGaName":349,"dataGaLocation":493},{"text":351,"config":631},{"href":353,"dataGaName":354,"dataGaLocation":493},{"text":633,"config":634},"Sustainability",{"href":635,"dataGaName":633,"dataGaLocation":493},"/sustainability/",{"text":637,"config":638},"Diversity, inclusion and belonging (DIB)",{"href":639,"dataGaName":640,"dataGaLocation":493},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":356,"config":642},{"href":358,"dataGaName":359,"dataGaLocation":493},{"text":366,"config":644},{"href":368,"dataGaName":369,"dataGaLocation":493},{"text":371,"config":646},{"href":373,"dataGaName":374,"dataGaLocation":493},{"text":648,"config":649},"Modern Slavery Transparency Statement",{"href":650,"dataGaName":651,"dataGaLocation":493},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":653,"links":654},"Contact Us",[655,658,660,662,667,672,677],{"text":656,"config":657},"Contact an expert",{"href":55,"dataGaName":56,"dataGaLocation":493},{"text":385,"config":659},{"href":387,"dataGaName":388,"dataGaLocation":493},{"text":390,"config":661},{"href":392,"dataGaName":393,"dataGaLocation":493},{"text":663,"config":664},"Status",{"href":665,"dataGaName":666,"dataGaLocation":493},"https://status.gitlab.com/","status",{"text":668,"config":669},"Terms of use",{"href":670,"dataGaName":671,"dataGaLocation":493},"/terms/","terms of use",{"text":673,"config":674},"Privacy statement",{"href":675,"dataGaName":676,"dataGaLocation":493},"/privacy/","privacy statement",{"text":678,"config":679},"Cookie preferences",{"dataGaName":680,"dataGaLocation":493,"id":681,"isOneTrustButton":109},"cookie preferences","ot-sdk-btn",{"items":683},[684,686,688],{"text":668,"config":685},{"href":670,"dataGaName":671,"dataGaLocation":493},{"text":673,"config":687},{"href":675,"dataGaName":676,"dataGaLocation":493},{"text":678,"config":689},{"dataGaName":680,"dataGaLocation":493,"id":681,"isOneTrustButton":109},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[695],{"_path":696,"_dir":697,"_draft":6,"_partial":6,"_locale":7,"content":698,"config":702,"_id":704,"_type":32,"title":19,"_source":34,"_file":705,"_stem":706,"_extension":37},"/en-us/blog/authors/cesar-saavedra","authors",{"name":19,"config":699},{"headshot":700,"ctfId":701},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659600/Blog/Author%20Headshots/csaavedra1-headshot.jpg","csaavedra1",{"template":703},"BlogAuthor","content:en-us:blog:authors:cesar-saavedra.yml","en-us/blog/authors/cesar-saavedra.yml","en-us/blog/authors/cesar-saavedra",{"_path":708,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"header":709,"eyebrow":710,"blurb":711,"button":712,"secondaryButton":716,"_id":718,"_type":32,"title":719,"_source":34,"_file":720,"_stem":721,"_extension":37},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":48,"config":713},{"href":714,"dataGaName":51,"dataGaLocation":715},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":53,"config":717},{"href":55,"dataGaName":56,"dataGaLocation":715},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1759347837231]