[{"data":1,"prerenderedAt":810},["ShallowReactive",2],{"nav_featured_podcast":3,"nav_featured_press_release":21,"navigation_content":30,"engineering_blog_post:secrets-build-pipelines":377},{"uid":4,"title":5,"episodeLabel":6,"dateLabel":7,"episodeArt":8,"href":20},"from-payment-rails-to-connectivity","From Payment Rails to Connectivity, with The Clearing House's David Watson","EPISODE 5","August 18, 2026",{"dimensions":9,"alt":12,"copyright":13,"url":14,"id":15,"edit":16},{"width":10,"height":11},1064,652,"Payments Cannot Fail — Episode 05 with David Watson",null,"\u002F_prismic-media\u002Fd80df2e47c3ecb6f-cFcyPAgzKCMQEaND_Episode-05-David.jpg","cFcyPAgzKCMQEaND",{"x":17,"y":17,"zoom":18,"background":19},0,1,"transparent","\u002Fresources\u002Fpodcasts\u002Ffrom-payment-rails-to-connectivity",{"uid":22,"title":23,"tag":24,"dateLabel":25,"imageUrl":26,"imageAlt":27,"href":28,"imageFit":29},"form3-expands-strategic-partnership-with-sumup","Form3 expands strategic partnership with SumUp to bring real-time SEPA payments to millions of European small businesses","EU","February 25, 2026","\u002F_prismic-media\u002F3e91177f91478610-QA44Gzev0_tdg3ZI_SumUp.jpg","sumup","\u002Fresources\u002Fpress-releases\u002Fform3-expands-strategic-partnership-with-sumup","cover",{"header":31,"platform":59,"solutions":91,"developers":133,"company":155,"resources":184,"partnerships":208,"footer":220,"mobile":324},{"ctaLabel":32,"ctaHref":33,"topNav":34},"TALK TO OUR TEAM","\u002Fcontact",[35,39,43,47,51,55],{"label":36,"href":37,"key":38},"Platform","\u002Fplatform","platform",{"label":40,"href":41,"key":42},"Solutions","\u002Fsolutions","solutions",{"label":44,"href":45,"key":46},"Developers","\u002Fdevelopers","developers",{"label":48,"href":49,"key":50},"Company","\u002Fcompany","company",{"label":52,"href":53,"key":54},"Resources","\u002Fresources","resources",{"label":56,"href":57,"key":58},"Partnerships","\u002Fpartnerships","partnerships",{"sectionEyebrow":60,"primary":61,"productLinks":65,"enterprise":81,"trustFabric":86},"Explore the Platform",{"heading":62,"body":63,"ctaLabel":64,"ctaHref":37},"Our Payments Platform","A resilient, cloud-native platform powering real-time account-to-account payments at global scale.","See How It Works",[66,69,72,75,78],{"label":67,"href":68},"Payments Processing & Gateway","\u002Fpayments-processing-gateway",{"label":70,"href":71},"Orchestration & Intelligent Routing","\u002Forchestration-intelligent-routing",{"label":73,"href":74},"Payments Simulator","\u002Fpayments-simulator",{"label":76,"href":77},"Sponsor Bank Enablement","\u002Fsponsor-bank-enablement",{"label":79,"href":80},"Inform3d UI","\u002Finform3d-ui",{"heading":82,"body":83,"ctaLabel":84,"ctaHref":85},"Form3 Enterprise","Deploy and manage proven cloud-native payments technology in the environment that works for your institution.","Deploy Enterprise","\u002Fplatform\u002Fenterprise",{"heading":87,"body":88,"ctaLabel":89,"ctaHref":90},"Form3 Trust Fabric","Manage TLS certificates, cryptographic vulnerabilities and quantum readiness at scale, with continuous visibility.","Deploy Trust Fabric","\u002Fplatform\u002Ftrust-fabric",{"regionsIntro":92,"regions":98,"industries":118},{"eyebrow":93,"heading":94,"headingLine2":95,"body":96,"ctaLabel":64,"ctaHref":97},"Regions","Global Reach.","Regional Expertise.","A proven payments platform serving organizations worldwide, with specialized experience across North America, United Kingdom, and Europe.","\u002Fglobal",[99,108,113],{"label":100,"description":101,"ctaLabel":102,"href":103,"extraLinks":104},"North America","Real-time infrastructure, through a resilient, multi-cloud platform built for scale and regulatory confidence.","US HOME","\u002Fsolutions\u002Funited-states",[105],{"label":106,"href":107},"CANADA HOME","\u002Fsolutions\u002Fcanada",{"label":109,"description":110,"ctaLabel":111,"href":112},"United Kingdom","Powering account-to-account payments at the heart of the UK financial ecosystem, with proven resilience.","UK HOME","\u002Fsolutions\u002Funited-kingdom",{"label":114,"description":115,"ctaLabel":116,"href":117},"Europe","Enabling secure, real-time and SEPA payments across European markets through a unified, cloud-native platform.","EUROPE HOME","\u002Fsolutions\u002Feurope",{"eyebrow":119,"links":120},"Industries",[121,125,129],{"label":122,"href":123,"iconSrc":124},"Financial Services","\u002Fsolutions\u002Findustries\u002Ffinancial-services","\u002F_prismic-media\u002F29c09c45caa89db9-kbUHZmgTmuIMbo1t_FinancialServices_menu.svg",{"label":126,"href":127,"iconSrc":128},"Fintech","\u002Fsolutions\u002Findustries\u002Ffintech","\u002F_prismic-media\u002F6de3e7cb4dd8d874-SjwNe6Nh-bbuTi0P_Fintech_menu.svg",{"label":130,"href":131,"iconSrc":132},"Government","\u002Fsolutions\u002Findustries\u002Fgovernment","\u002F_prismic-media\u002Fe71bc3a74a1d2142-kaGgdituzKDnVTbm_Government_menu.svg",{"engineering":134,"api":142,"pressEyebrow":154},{"eyebrow":135,"links":136},"Engineering",[137,139],{"label":135,"href":138},"\u002Fengineering",{"label":140,"href":141},"Accreditations","\u002Faccreditations",{"eyebrow":143,"links":144},"API",[145,148,151],{"label":146,"href":147},"API Docs","https:\u002F\u002Fwww.api-docs.form3.tech\u002F",{"label":149,"href":150},"API Tutorials","https:\u002F\u002Fwww.api-docs.form3.tech\u002Fapi\u002Ftutorials\u002Fgetting-started\u002Fintroduction",{"label":152,"href":153},"API Status","https:\u002F\u002Fstatus.form3.tech\u002F","Latest Press Release",{"about":156,"people":171,"podcastEyebrow":183},{"eyebrow":157,"links":158},"About",[159,162,165,168],{"label":160,"href":161},"Our Story","\u002Fcompany\u002Four-story",{"label":163,"href":164},"Our Team","\u002Fcompany\u002Fteam",{"label":166,"href":167},"Our Success & Strategic Approach","\u002Fcompany\u002Four-success",{"label":169,"href":170},"Customer Stories","\u002Fcompany\u002Fcustomer-stories",{"eyebrow":172,"links":173},"People",[174,177,180],{"label":175,"href":176},"Careers","\u002Fcompany\u002Fcareers",{"label":178,"href":179},"Vacancies","\u002Fcompany\u002Fvacancies",{"label":181,"href":182},"Culture","\u002Fcompany\u002Fculture","Latest Podcast",{"intro":185,"links":189,"pressEyebrow":154,"podcastEyebrow":183},{"eyebrow":52,"heading":186,"body":187,"ctaLabel":188,"ctaHref":53},"Form3 Resources","Our Resources bring together thought leadership, engineering insights, payments expertise, webinars, podcasts, whitepapers, company news and stories from our teams—all in one place.","LEARN MORE",[190,193,196,199,202,205],{"label":191,"href":192},"Payments Cannot Fail Podcast","\u002Fresources\u002Fpodcasts",{"label":194,"href":195},"Press Releases","\u002Fresources\u002Fpress-releases",{"label":197,"href":198},"Payment Insights","\u002Fresources\u002Fpayment-insights",{"label":200,"href":201},"Culture Blog","\u002Fresources\u002Fculture-blog",{"label":203,"href":204},"Engineering Blog","\u002Fresources\u002Fengineering-blog",{"label":206,"href":207},"Events","\u002Fevents",{"intro":209,"links":213,"pressEyebrow":154},{"eyebrow":56,"heading":210,"body":211,"ctaLabel":212,"ctaHref":57},"Partner with Form3","Explore how Form3 works with banks, fintechs, cloud providers, technology platforms and system integrators to accelerate payment modernization, strengthen customer offerings and unlock new opportunities for growth.","Let's Partner Together",[214,217],{"label":215,"href":216},"Technical & Ecosystem Partners","\u002Fpartnerships\u002Ftechnical-ecosystem",{"label":218,"href":219},"Channel & Distribution Partners","\u002Fpartnerships\u002Fchannel-distribution",{"platform":221,"region":228,"development":239,"company":247,"resources":258,"partnerships":266,"stayConnected":271,"legalLinks":291,"certifications":313},{"title":36,"links":222},[223,224,225,226,227],{"label":62,"href":37},{"label":67,"href":68},{"label":70,"href":71},{"label":73,"href":74},{"label":76,"href":77},{"title":229,"links":230},"Region",[231,233,235,236,237],{"label":232,"href":97},"Global",{"label":234,"href":103},"United States",{"label":109,"href":112},{"label":114,"href":117},{"label":238,"href":107},"Canada",{"title":240,"links":241},"Development",[242,243,244,245,246],{"label":135,"href":138},{"label":140,"href":141},{"label":146,"href":147},{"label":149,"href":150},{"label":152,"href":153},{"title":48,"links":248},[249,250,251,252,253,254,255],{"label":160,"href":161},{"label":163,"href":164},{"label":169,"href":170},{"label":175,"href":176},{"label":178,"href":179},{"label":181,"href":182},{"label":256,"href":257},"Net Zero","\u002Fcompany\u002Fnet-zero",{"title":52,"links":259},[260,261,262,263,264,265],{"label":186,"href":53},{"label":191,"href":192},{"label":194,"href":195},{"label":200,"href":201},{"label":203,"href":204},{"label":206,"href":207},{"title":56,"links":267},[268,269,270],{"label":210,"href":57},{"label":215,"href":216},{"label":218,"href":219},{"title":272,"body":273,"ctaLabel":32,"ctaHref":33,"social":274},"Stay Connected","Form3 is a global account-to-account financial fabric delivering a proven payments platform trusted by banks and fintechs to deliver resilient, real-time payment experiences for their customers at scale.",[275,279,283,287],{"label":276,"href":277,"icon":278},"LinkedIn","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fform3-financial-cloud\u002F","linkedin",{"label":280,"href":281,"icon":282},"X","https:\u002F\u002Fwww.x.com\u002FForm3Cloud","x",{"label":284,"href":285,"icon":286},"YouTube","https:\u002F\u002Fwww.youtube.com\u002F@Form3Cloud","youtube",{"label":288,"href":289,"icon":290},"Instagram","https:\u002F\u002Fwww.instagram.com\u002Fform3.tech","instagram",[292,295,298,301,304,307,310],{"label":293,"href":294},"Terms & Conditions","\u002Flegal\u002Fterms-and-conditions",{"label":296,"href":297},"Privacy Policy","\u002Flegal\u002Fdata-privacy-statement",{"label":299,"href":300},"Recruitment Data Policy","\u002Flegal\u002Frecruitment-data-policy",{"label":302,"href":303},"Modern Slavery Statement","\u002Flegal\u002Fmodern-slavery",{"label":305,"href":306},"License","\u002Flegal\u002Flicense",{"label":308,"href":309},"Cookie Policy","\u002Flegal\u002Fcookie-policy",{"label":311,"href":312},"Manage Cookies","#cookies",[314,316,318,320,322],{"label":315},"ISO 27001",{"label":317},"ISO 27017",{"label":319},"ISO 27018",{"label":321},"ISAE 3000 SOC 2 Type II",{"label":323},"ISO 22301",{"platform":325,"solutions":340,"developers":354,"company":364,"resources":365,"partnerships":373},[326,328,338,339],{"kind":327,"label":62,"href":37},"link",{"kind":329,"label":60,"children":330},"group",[331,332,333,334,336],{"label":67,"href":68},{"label":70,"href":71},{"label":73,"href":74},{"label":76,"href":335},"\u002Fplatform\u002Fsponsor-bank-enablement",{"label":79,"href":337},"\u002Fplatform\u002Finform3d-ui",{"kind":327,"label":82,"href":85},{"kind":327,"label":87,"href":90},[341,343,349],{"kind":327,"label":232,"href":342},"\u002Fsolutions\u002Fglobal",{"kind":329,"label":93,"children":344},[345,346,347,348],{"label":234,"href":103},{"label":238,"href":107},{"label":109,"href":112},{"label":114,"href":117},{"kind":329,"label":119,"children":350},[351,352,353],{"label":122,"href":123},{"label":126,"href":127},{"label":130,"href":131},[355,359],{"kind":329,"label":135,"children":356},[357,358],{"label":135,"href":138},{"label":140,"href":141},{"kind":329,"label":143,"children":360},[361,362,363],{"label":146,"href":147},{"label":149,"href":150},{"label":152,"href":153},[],[366,367,368,369,370,371,372],{"kind":327,"label":186,"href":53},{"kind":327,"label":191,"href":192},{"kind":327,"label":194,"href":195},{"kind":327,"label":197,"href":198},{"kind":327,"label":200,"href":201},{"kind":327,"label":203,"href":204},{"kind":327,"label":206,"href":207},[374,375,376],{"kind":327,"label":210,"href":57},{"kind":327,"label":215,"href":216},{"kind":327,"label":218,"href":219},{"id":378,"uid":379,"url":380,"type":381,"href":382,"tags":383,"first_publication_date":384,"last_publication_date":385,"slugs":386,"linked_documents":388,"lang":389,"alternate_languages":390,"data":391},"alz0vxEAACcAUWZu","secrets-build-pipelines","\u002Fresources\u002Fengineering-blog\u002Fsecrets-build-pipelines","engineering_blog_post","https:\u002F\u002Fform3-website.cdn.prismic.io\u002Fapi\u002Fv2\u002Fdocuments\u002Fsearch?ref=apjJ1BEAAC0AJxzl&q=%5B%5B%3Ad+%3D+at%28document.id%2C+%22alz0vxEAACcAUWZu%22%29+%5D%5D",[],"2026-07-19T16:22:00+0000","2026-08-27T02:07:01+0000",[387],"first-an-example",[],"en-us",[],{"title":392,"excerpt":393,"card_image":394,"published_date":400,"reading_time":401,"tag":402,"dek":393,"featured_image":403,"about_form3":410,"client_about_heading":13,"client_about_body":411,"author_name":412,"author_title":413,"author_photo":414,"author_bio":422,"author_linkedin":435,"slices":438,"meta_title":392,"meta_description":393},"Secrets management in build and deployment pipelines","In his previous post about bootstrapping engineering organisations, Andy Kuszyk identified secrets management as one of the key challenges that need to be tackled early on in growing organisations. In this post, he dives deeper into this topic and discusses two secrets management patterns: injecting secrets with Terraform and issuing them with a central secrets manager.",{"dimensions":395,"alt":392,"copyright":13,"url":397,"id":398,"edit":399},{"width":396,"height":396},800,"\u002F_prismic-media\u002F14824fa16d53fa00-rHlkIsi_eWPq8x9x_secrets-build-pipelines.png","rHlkIsi_eWPq8x9x",{"x":17,"y":17,"zoom":18,"background":19},"2023-05-04",6,"Blogs",{"dimensions":404,"alt":13,"copyright":13,"url":407,"id":408,"edit":409},{"width":405,"height":406},814,548,"\u002F_prismic-media\u002F8925ff353b8b8dd7-3YUTqNHKynr5ubBP_8ea81d8f-5f79-4d44-97fa-a0838df.png","3YUTqNHKynr5ubBP",{"x":17,"y":17,"zoom":18,"background":19},[],[],"Andy Kuszyk","Staff Engineer",{"dimensions":415,"alt":412,"copyright":13,"url":418,"id":419,"edit":420},{"width":416,"height":417},317,473,"\u002F_prismic-media\u002Fc371d17a59432918-GFOBoL2Tovd3zPoj_27d09fad-dd24-483c-92fc-1ac2a07.jpeg","GFOBoL2Tovd3zPoj",{"x":17,"y":17,"zoom":18,"background":421},"#ffffff",[423],{"type":424,"text":425,"spans":426},"paragraph","Andy Kuszyk is a Staff Engineer at Form3, based in Southampton. He's been working as a software engineer for 8 years with a variety of technologies, including .NET, Python and most recently Go. Check out more of his tech articles on his blog.",[427],{"start":428,"end":429,"type":430,"data":431},233,241,"hyperlink",{"link_type":432,"url":433,"target":434},"Web","http:\u002F\u002Fandykuszyk.github.io\u002F","_blank",{"link_type":432,"key":436,"url":437,"target":434},"ef9d5df4-1c7c-45d5-81c7-a86cb9a4defd","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fandy-kuszyk",[439,478,497,508,516,524,532,540,553,579,593,601,615,649,668,676,684,692,700,708,716,752,770,781],{"variation":440,"version":441,"items":442,"primary":443,"id":476,"slice_type":477,"slice_label":13},"default","initial",[],{"body":444},[445,458,461,464,467],{"type":424,"text":446,"spans":447},"I recently wrote about what I thought were the key ingredients to success when bootstrapping a new engineering organisation. One of these ingredients is secrets management in build and deployment pipelines. In this post, I'm going to describe why I think this is so important, and provide some practical examples of how to get it right (and how to get it wrong!).",[448],{"start":449,"end":450,"type":430,"data":451},2,16,{"id":452,"type":453,"tags":454,"lang":13,"slug":455,"first_publication_date":13,"last_publication_date":13,"link_type":456,"isBroken":457},"ZCF3lBEAAC0AflWB","broken_type",[],"-","Document",true,{"type":424,"text":459,"spans":460},"Secrets are required to build and deploy software. Normally when you start out, there are some simple, secure ways to inject secrets into your build and deployment pipelines. For example, with Travis you can use the CLI to commit an encrypted file to a repo, or add an encrypted environment variable. In GitHub Actions, you can add a secret to a repo, which you can safely use later in your workflows.",[],{"type":424,"text":462,"spans":463},"However, it doesn't take long before you've got lots of secrets in lots of different places. When this happens, the secrets themselves become hard to manage, and hard to maintain. You can't easily revoke or rotate them, or roll out new secrets automatically.",[],{"type":424,"text":465,"spans":466},"Ideally, when bootstrapping an engineering organisation, you want to establish a good way of injecting secrets into your entire build and deployment pipeline estate from day one.",[],{"type":424,"text":468,"spans":469},"If you're interested in this topic, you might also be interested in our recent podcast about secrets management.",[470],{"start":471,"end":472,"type":430,"data":473},79,111,{"id":474,"type":453,"tags":475,"lang":13,"slug":455,"first_publication_date":13,"last_publication_date":13,"link_type":456,"isBroken":457},"ZDWFRBIAACoALkGf",[],"rich_text$9c46e2ae-2bd4-4662-91f3-50b1f3544977","rich_text",{"variation":440,"version":441,"items":479,"primary":480,"id":496,"slice_type":477,"slice_label":13},[],{"body":481},[482,486,489],{"type":483,"text":484,"spans":485},"heading2","First, an example",[],{"type":424,"text":487,"spans":488},"What exactly am I talking about when I say \"secrets management in build and deployment pipelines\"? Let's start with a simple example to set the scene.",[],{"type":424,"text":490,"spans":491},"Say you have a project called Hello World, which has a simple Dockerfile:",[492],{"start":493,"end":494,"type":495},30,41,"em","rich_text$ebcb615b-4825-4781-8762-b96219b7dace",{"variation":440,"version":441,"items":498,"primary":499,"id":506,"slice_type":507,"slice_label":13},[],{"language_label":500,"code":501},"yaml",[502],{"type":503,"text":504,"spans":505},"preformatted","FROM alpine\nCMD echo 'hello world'",[],"code_block$6f0cca15-2c52-471e-aaac-417f936b49b5","code_block",{"variation":440,"version":441,"items":509,"primary":510,"id":515,"slice_type":477,"slice_label":13},[],{"body":511},[512],{"type":424,"text":513,"spans":514},"In your build and deployment pipeline, you want to build this Dockerfile, and publish it to a public Docker registry (for example Docker Hub). Let's assume you're using GitHub Actions for this. Your pipeline might look something like this:",[],"rich_text$2df1aa8d-3f56-474d-b19c-3008e4c141b3",{"variation":440,"version":441,"items":517,"primary":518,"id":523,"slice_type":507,"slice_label":13},[],{"language_label":500,"code":519},[520],{"type":503,"text":521,"spans":522},"on: [push]\njobs:\n  docker-publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v3\n      - run: docker build -t andykuszyk\u002Fhello-world:latest .\n      - run: docker push andykuszyk\u002Fhello-world:latest",[],"code_block$bca32e6e-e963-4f72-a2fd-a513b9bf0de9",{"variation":440,"version":441,"items":525,"primary":526,"id":531,"slice_type":477,"slice_label":13},[],{"body":527},[528],{"type":424,"text":529,"spans":530},"Great; nice and simple. However, before you publish to the Docker registry, you're going to need to authenticate with it, which means you're going to need some credentials. You need something like this before the docker build step:",[],"rich_text$aa0fc86e-f803-4e94-b4f7-2b9d6a7746db",{"variation":440,"version":441,"items":533,"primary":534,"id":539,"slice_type":507,"slice_label":13},[],{"language_label":500,"code":535},[536],{"type":503,"text":537,"spans":538},"- run: docker login -u \"andykuszyk\" -p \"n0tmyr3alpa22w0rd\"",[],"code_block$01684a8f-3233-42a3-ae35-db6db116f7c8",{"variation":440,"version":441,"items":541,"primary":542,"id":552,"slice_type":477,"slice_label":13},[],{"body":543},[544],{"type":424,"text":545,"spans":546},"So, how are you going to do that without committing your credentials to source control? Well, there's normally a variety of ways you can do this with the CI system of your choice. In the case of GitHub Actions, you can store encrypted secrets alongside the repo itself via the repo settings:",[547],{"start":548,"end":549,"type":430,"data":550},225,242,{"link_type":432,"url":551,"target":434},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Factions\u002Fsecurity-guides\u002Fencrypted-secrets","rich_text$3ad12715-5175-4a6a-91e3-fbf3d0a09d8e",{"variation":440,"version":554,"items":555,"primary":556,"id":577,"slice_type":578,"slice_label":13},"",[],{"eyebrow":13,"heading":557,"body":558,"cta_label":13,"cta_link":559,"aside_type":561,"aside_image":562,"aside_video":565,"aside_video_poster":567,"aside_video_reduced_motion":568,"aside_video_url":13,"aside_embed":569,"pardot_form_url":13,"form_submit_label":13,"form_variant":570,"form_heading":13,"redirect_on_success":571,"theme":572,"overlay_pattern":573,"background_continuation":574,"media_position":575,"aside_vertical_align":576},[],[],{"link_type":560},"Any","Image",{"dimensions":563,"alt":13,"copyright":13,"url":407,"id":408,"edit":564},{"width":405,"height":406},{"x":17,"y":17,"zoom":18,"background":19},{"link_type":566},"Media",{},{},{},"Contact (default)",{"link_type":456},"Light","None","Solid color (no gradient)","Right","Top of section","content_block$b80e551f-e819-40b1-9340-203dbff86c09","content_block",{"variation":440,"version":441,"items":580,"primary":581,"id":592,"slice_type":477,"slice_label":13},[],{"body":582},[583],{"type":424,"text":584,"spans":585},"If you add docker_username and docker_password secrets to your repo, then you can easily use them in your CI pipeline:",[586,589],{"start":587,"end":588,"type":495},11,26,{"start":590,"end":591,"type":495},31,46,"rich_text$d0c1a412-676b-4da8-8250-66d69a1405a4",{"variation":440,"version":441,"items":594,"primary":595,"id":600,"slice_type":507,"slice_label":13},[],{"language_label":500,"code":596},[597],{"type":503,"text":598,"spans":599},"on: [push]\njobs:\n  docker-publish:\n    runs-on: ubuntu-latest\n    env:\n      DOCKER_USERNAME: ${{ secrets.docker_username }}\n      DOCKER_PASSWORD: ${{ secrets.docker_password }}\n    steps:\n      - uses: actions\u002Fcheckout@v3\n      - run: docker login -u \"$DOCKER_USERNAME\" -p \"$DOCKER_PASSWORD\"\n      - run: docker build -t andykuszyk\u002Fhello-world:latest .\n      - run: docker push andykuszyk\u002Fhello-world:latest",[],"code_block$0a6438ec-9168-411a-bc7f-c8df2447a10d",{"variation":440,"version":441,"items":602,"primary":603,"id":614,"slice_type":477,"slice_label":13},[],{"body":604},[605,608,611],{"type":424,"text":606,"spans":607},"This has solved the problem for a single repo. When you're starting a new engineering organisation, you might start off with one (or a small number) of repos, which makes this approach effective. However, when the number of repos in your estate begins to grow, this approach becomes problematic. It's easy to lose track of what secrets are in use in which repos, and you're relying on people manually adding secrets in order to inject them into your pipelines. This means you need to grant everyone in your organisation access to your secrets, or rely on a small number of people to manage the secret injection.",[],{"type":424,"text":609,"spans":610},"Most problematic of all is that, one day, you'll need to revoke or rotate the secrets. If you've been manually adding them here, there, and everywhere, then this becomes a real problem. You have to go through every repo, and rotate them one by one.",[],{"type":424,"text":612,"spans":613},"What you really need is a nice, convenient way to manage the secrets centrally in a way that lets you deploy a single set of secrets to your estate of repos. That way you can see what is being used where, and rotate secret values easily.",[],"rich_text$570af281-0279-4aa7-882a-af065211888d",{"variation":440,"version":441,"items":616,"primary":617,"id":648,"slice_type":477,"slice_label":13},[],{"body":618},[619,622,625,629,632,635,639,642,645],{"type":483,"text":620,"spans":621},"Two patterns for pragmatic secret management",[],{"type":424,"text":623,"spans":624},"In order to make managing secrets in CI easier in the long-run when building an engineering organisation, I think you need to achieve two things:",[],{"type":626,"text":627,"spans":628},"list-item","Manage secrets centrally.",[],{"type":626,"text":630,"spans":631},"Be able to rotate them easily.",[],{"type":424,"text":633,"spans":634},"Below are two patterns for secret management that will help you do this.",[],{"type":636,"text":637,"spans":638},"heading3","Inject secrets using Terraform",[],{"type":424,"text":640,"spans":641},"Rather than creating and updating secrets manually, you can manage them centrally by managing your estate of repos using Terraform. This is a good practice anyway, because it means that you can administer your repos en-masse in Terraform, and can restrict admin permissions to the user that Terraform uses, rather than granting it to your ordinary users.",[],{"type":424,"text":643,"spans":644},"In my description below, I will be focusing on GitHub and GitHub Actions secrets, although this pattern can just as easily be applied to other CI providers that expose some sort of secret storage functionality via an API.",[],{"type":424,"text":646,"spans":647},"The general idea with this pattern is that you manage your GitHub repos centrally in Terraform, and use Terraform to create secrets in each of your repos. CI pipelines in each repo can then access the secrets provisioned via Terraform:",[],"rich_text$9e5ddccb-ccd1-41b2-b571-4f2c01cedc2a",{"variation":440,"version":554,"items":650,"primary":651,"id":667,"slice_type":578,"slice_label":13},[],{"eyebrow":13,"heading":652,"body":653,"cta_label":13,"cta_link":654,"aside_type":561,"aside_image":655,"aside_video":662,"aside_video_poster":663,"aside_video_reduced_motion":664,"aside_video_url":13,"aside_embed":665,"pardot_form_url":13,"form_submit_label":13,"form_variant":570,"form_heading":13,"redirect_on_success":666,"theme":572,"overlay_pattern":573,"background_continuation":574,"media_position":575,"aside_vertical_align":576},[],[],{"link_type":560},{"dimensions":656,"alt":13,"copyright":13,"url":659,"id":660,"edit":661},{"width":657,"height":658},620,287,"\u002F_prismic-media\u002F36101b39acb4742f-6G2djj_rtx_3zB2d_d7751132-c9d3-41e4-831a-ff3014f.png","6G2djj_rtx_3zB2d",{"x":17,"y":17,"zoom":18,"background":19},{"link_type":566},{},{},{},{"link_type":456},"content_block$108af09c-e6e3-46f1-82a5-3f5ed5cd1e1a",{"variation":440,"version":441,"items":669,"primary":670,"id":675,"slice_type":477,"slice_label":13},[],{"body":671},[672],{"type":424,"text":673,"spans":674},"What this looks like in reality is fairly straightforward. GitHub repos are easy to provision using Terraform:",[],"rich_text$af21f00d-3e1d-4e00-9ad3-14f739c823e0",{"variation":440,"version":441,"items":677,"primary":678,"id":683,"slice_type":507,"slice_label":13},[],{"language_label":500,"code":679},[680],{"type":503,"text":681,"spans":682},"resource \"github_repository\" \"hello_world_api\" {\n  name                 = \"hello-world-api\"\n  description          = \"\"\n  has_downloads        = true\n  has_issues           = true\n  has_wiki             = true\n  has_projects         = true\n  homepage_url         = \"\"\n  vulnerability_alerts = true\n  visibility           = \"public\"\n  lifecycle {\n    ignore_changes = [etag]\n  }\n}",[],"code_block$565555d1-7023-4376-97a6-6c26c899e415",{"variation":440,"version":441,"items":685,"primary":686,"id":691,"slice_type":477,"slice_label":13},[],{"body":687},[688],{"type":424,"text":689,"spans":690},"Similarly, secrets can also easily be provisioned:",[],"rich_text$28985161-77f9-4ffd-abbd-5e79fe43a934",{"variation":440,"version":441,"items":693,"primary":694,"id":699,"slice_type":507,"slice_label":13},[],{"language_label":500,"code":695},[696],{"type":503,"text":697,"spans":698},"resource \"github_actions_secret\" \"hello_world_api_docker_username\" {\n  repository      = \"hello-world-api\"\n  secret_name     = \"DOCKER_USERNAME\"\n  plaintext_value = var.docker_username\n}",[],"code_block$35180418-916a-4ce9-8f48-ead2c1f16c83",{"variation":440,"version":441,"items":701,"primary":702,"id":707,"slice_type":477,"slice_label":13},[],{"body":703},[704],{"type":424,"text":705,"spans":706},"Then, you just need a variable for injecting the value:",[],"rich_text$baa5ba55-60ae-4ebf-a8da-de558b725a20",{"variation":440,"version":441,"items":709,"primary":710,"id":715,"slice_type":507,"slice_label":13},[],{"language_label":500,"code":711},[712],{"type":503,"text":713,"spans":714},"variable \"docker_username\" {}",[],"code_block$8a4ac8ca-9f57-4c38-8264-95fe905175d3",{"variation":440,"version":441,"items":717,"primary":718,"id":751,"slice_type":477,"slice_label":13},[],{"body":719},[720,728,731,734,737,740,743],{"type":424,"text":721,"spans":722},"You obviously need a good way to actually set values for this secret, and systems like Terraform Cloud allow you to set sensitive values for a Terraform project, such that they can be injected using a Terraform variable. For this to work, you need to lock-down access to your Terraform Cloud account (especially to the state files, which will contain the secret values in plain text), however you do end up with a simple way of injecting secrets from a single place into your entire repo estate.",[723],{"start":724,"end":725,"type":430,"data":726},87,102,{"link_type":432,"url":727,"target":434},"https:\u002F\u002Fwww.hashicorp.com\u002Fproducts\u002Fterraform\u002Fpricing",{"type":424,"text":729,"spans":730},"Furthermore, if you need to rotate a secret, the process is simple:",[],{"type":626,"text":732,"spans":733},"Update the value used by the Terraform variable.",[],{"type":626,"text":735,"spans":736},"Run a terraform plan and terraform apply.",[],{"type":626,"text":738,"spans":739},"Profit! All your repos are updated in one go!",[],{"type":636,"text":741,"spans":742},"Issue secrets from a secret manager",[],{"type":424,"text":744,"spans":745},"An alternative pattern to the approach described above, is to issue secrets to CI pipelines, rather than inject them in. A secret manager (e.g. Hashicorp Vault) can be used as the central location of secrets, and can control issuing them to authorised clients. Provided your CI pipelines have a suitable mechanism of authenticating with the secret manager, they can then reach out on demand to fetch the secrets they need:",[746],{"start":747,"end":748,"type":430,"data":749},144,159,{"link_type":432,"url":750},"https:\u002F\u002Fwww.vaultproject.io\u002F","rich_text$076db320-c97c-49b4-8e38-757fd9c4c223",{"variation":440,"version":554,"items":753,"primary":754,"id":769,"slice_type":578,"slice_label":13},[],{"eyebrow":13,"heading":755,"body":756,"cta_label":13,"cta_link":757,"aside_type":561,"aside_image":758,"aside_video":764,"aside_video_poster":765,"aside_video_reduced_motion":766,"aside_video_url":13,"aside_embed":767,"pardot_form_url":13,"form_submit_label":13,"form_variant":570,"form_heading":13,"redirect_on_success":768,"theme":572,"overlay_pattern":573,"background_continuation":574,"media_position":575,"aside_vertical_align":576},[],[],{"link_type":560},{"dimensions":759,"alt":13,"copyright":13,"url":761,"id":762,"edit":763},{"width":760,"height":658},601,"\u002F_prismic-media\u002F193a8011143871df-AFVo9gc8-tx6wyTs_40f5289a-503a-4792-b246-cc8904c.png","AFVo9gc8-tx6wyTs",{"x":17,"y":17,"zoom":18,"background":19},{"link_type":566},{},{},{},{"link_type":456},"content_block$d98d60a4-de2e-411d-ac8c-33ef3b29f3e2",{"variation":440,"version":441,"items":771,"primary":772,"id":780,"slice_type":477,"slice_label":13},[],{"body":773},[774,777],{"type":424,"text":775,"spans":776},"This approach has the advantage of avoiding the need to restrict access to the same Terraform workspace that manages your GitHub repos (although you still need to inject your secrets into the secret manager somehow), but does require additional infrastructure to run and manage the secret manager in the first place.",[],{"type":424,"text":778,"spans":779},"More importantly, it means that the secrets issued by your secret manager could be dynamically provisioned, and issued with short time-to-live. Rather than having static credentials that are valid for a long period (which could cause a lot of damage if they were stolen), your CI pipelines can use ephemeral secrets with such short lifespans that they wouldn't be very useful to an attacker.",[],"rich_text$e4b6b4b2-b567-491a-938d-d2644b46e75c",{"variation":440,"version":441,"items":782,"primary":783,"id":809,"slice_type":477,"slice_label":13},[],{"body":784},[785,788,791,794,797,800,803,806],{"type":483,"text":786,"spans":787},"Summary",[],{"type":424,"text":789,"spans":790},"Managing secrets in your build and deployment pipelines is something that I think you need to get right early on when building an engineering organisation, otherwise it can become a logistical and security problem later.",[],{"type":424,"text":792,"spans":793},"In this post I've described two patterns for managing your pipeline secrets at scale:",[],{"type":626,"text":795,"spans":796},"Injecting them using infrastructure as code.",[],{"type":626,"text":798,"spans":799},"Issuing them using a central secrets manager.",[],{"type":424,"text":801,"spans":802},"In my opinion, the first pattern is easier to get up and running, and scales very well. However, it does require careful configuration to ensure the secrets are securely stored at the point of injection (e.g. in Terraform Cloud). It also has the disadvantage that all the secrets are static values with long expiry lifetimes.",[],{"type":424,"text":804,"spans":805},"The second approach separates the concepts of repo management and secret management, by introducing a dedicated secrets manager. Whilst this approach has advantages--notably by allowing the use of ephemeral secrets--it comes with the additional overhead of having to run and manage a separate secret manager (e.g. Vault).",[],{"type":424,"text":807,"spans":808},"However, I think both approaches are preferable to manually managing secrets on a repo-by-repo basis. It's all too easy to fall into that trap when you start out, which is why I think getting this right from day one is so important.",[],"rich_text$eb1533eb-eea4-4528-b8fa-386f42730fd6",1788399681642]