Terraform İle Hassas Verileri Yönetmek (Secrets)
32 min read

Terraform İle Hassas Verileri Yönetmek (Secrets)

Terraform ile hassas verilerinizi (secret) nasıl güvenle yönetebileceğimize ve kullanacağımıza bakıyoruz.
Terraform İle Hassas Verileri Yönetmek (Secrets)
Photo by Michael Dziedzic / Unsplash

Bir noktada, size ve yazılımınıza çeşitli hassas veriler emanet edilecektir. Örneğin, veritabanı parolaları, API anahtarları, TLS sertifikaları, SSH anahtarları, GPG anahtarları vb. Bunların hepsi, yanlış ellere geçerse şirketinize ve müşterilerine çok fazla zarar verebilecek hassas verilerdir. Yazılım oluşturursanız, bu sırları güvende tutmak sizin sorumluluğunuzdadır.

Örneğin, bir veritabanını dağıtmak için aşağıdaki Terraform kodunu göz önünde bulundurun:

resource "aws_db_instance" "example" {
  identifier_prefix   = "terraform-up-and-running"
  engine              = "mysql"
  allocated_storage   = 10
  instance_class      = "db.t2.micro"
  skip_final_snapshot = true
  db_name             = var.db_name

  # How to set these parameters securely?
  username = "???"
  password = "???"
}

Bu kod, veritabanının ana kullanıcısı için kimlik bilgileri olan kullanıcı adı ve parola olmak üzere iki secret belirlemenizi gerektirir. Yanlış kişi bunlara erişirse, bu kimlik bilgileri size o veritabanına ve içindeki tüm verilere süper kullanıcı erişimi sağladığı için felaket olabilir. Peki, bu hassas verileri nasıl güvende tutacaksınız?

Bu, daha geniş secret yönetimi konusunun bir parçasıdır. Bu bölümde, secret yönetiminin temellerini gözden geçireceğiz, daha popüler bazı gizli yönetim araçları türlerinden bahsedeceğiz ve ardından bu tür secret yönetim araçlarının her birini Terraform ile nasıl kullanacağınız konusunda size yol göstereceğiz.

Bakacağımız başlıklar şunlar olacak:

  • Secret yönetim temelleri
  • Secret yönetim araçları
  • Terraform ile secret yönetim araçlarını kullanma

1. Secret Yönetiminin Temelleri

Secret yönetiminin ilk kuralı şudur:

📍
Hassas verilerinizi asla plain text olarak saklamayınız!

Secret yönetiminin ikinci kuralı ise şudur:

📍
HASSAS VERİLERİNİZİ ASLA PLAIN TEXT OLARAK SAKLAMAYINIZ!

Ciddi olarak bu iki kuralı okuyunuz ve asla hassas verilerinizi plain text olarak tutmayınız. Örneğin, veritabanı kimlik bilgilerinizi doğrudan Terraform kodunuza SAKLAMAYIN ve sürüm kontrolünde kontrol edin:

resource "aws_db_instance" "example" {
  identifier_prefix   = "terraform-up-and-running"
  engine              = "mysql"
  allocated_storage   = 10
  instance_class      = "db.t2.micro"
  skip_final_snapshot = true
  db_name             = var.db_name

  # DO NOT DO THIS!!!
  username = "admin"
  password = "password"
  # DO NOT DO THIS!!!
}

Sürüm kontrolünde secretleri düz metin olarak saklamak KÖTÜ bir FİKİRDİR. İşte bunun nedenlerinden sadece birkaçı:

  • Sürüm kontrol sistemine erişimi olan herkes bu hassas verilere erişebilir.

➡ Yukarıdaki örnekte, şirketinizde bu Terraform koduna erişebilen her bir geliştirici, veritabanınız için ana kimlik bilgilerine erişebilecektir.

  • Sürüm kontrol sistemine erişimi olan her bilgisayar bu hassas verinin bir kopyasını tutar.

➡ Bu repoyu kontrol eden her bilgisayar, yerel sabit diskinde hala bu secretin bir kopyasına sahip olabilir. Buna ekibinizdeki her geliştiricinin bilgisayarı, CI'ye dahil olan her bilgisayar (ör. Jenkins, CircleCI, GitLab, vb.), sürüm kontrolüne dahil olan her bilgisayar (ör. tüm üretim öncesi ve üretim ortamlarınız), yedeklemeye dahil olan her bilgisayar (ör. CrashPlan, Time Machine, vb.) vb.

  • Çalıştırdığınız her yazılım parçasının bu secreta erişimi vardır.

➡ Secretlar pek çok sabit sürücüde düz metin olarak bulunduğundan, bu bilgisayarlardan herhangi birinde çalışan her bir yazılım parçası potansiyel olarak bu secreti okuyabilir.

  • Bu secreta erişimi denetlemenin veya iptal etmenin bir yolu yok

➡ Sırlar düz metin olarak yüzlerce sabit diskte otururken, onlara kimin eriştiğini (denetim günlüğü yoktur) ve erişimi iptal etmenin kolay bir yolu yoktur.


Kısacası, sırları düz metin olarak saklarsanız, kötü niyetli aktörlere (ör. bilgisayar korsanları, rakipler, huysuz eski çalışanlar) şirketinizin en hassas verilerine erişmeleri için sayısız yol vermiş olursunuz.

Bu nedenle, hassas verilerinizi saklamak için uygun bir secret yönetim aracı kullanmanız çok önemlidir.

2. Secret Yönetim Araçları

Secret yönetiminin tüm yönlerine kapsamlı bir genel bakış bu yazının kapsamı dışındadır, ancak Terraform ile secret yönetim araçlarını kullanabilmek için, en azından gelişen temel hususların ve farklı secret yönetim araçlarının bunlara nasıl yaklaştığının farkında olmanız gerekir. Öyleyse şu konularla başlayalım:

  • Secret yönetim araçlarıyla ilgili önemli hususlar
  • Secret yönetim araçlarının karşılaştırılması

2.1. Secret yönetim araçlarıyla ilgili önemli hususlar

Yüksek düzeyde, secret yönetimiyle ilgili üç önemli husus vardır:

  • Secret'ların Tipleri

Üç temel secret türü vardır: kişisel secretlar, müşteri secretları ve altyapı secretları.

Kişisel secretlar, bir kişiye ait olan sırlardır. Örnekler: ziyaret ettiğiniz web sitelerinin kullanıcı adları ve şifreleri; SSH anahtarlarınız; PGP anahtarlarınız.

Müşteri secretlar, müşterilerinize ait secretlardır. Şirketinizin diğer çalışanları için yazılım çalıştırıyorsanız, örneğin şirketinizin dahili Active Directory sunucusunu yönetiyorsanız, bu diğer çalışanların müşterileriniz olduğunu unutmayın. Örnekler: müşterilerinizin ürününüze giriş yapmak için kullandığı kullanıcı adları ve şifreler; müşterileriniz için kişisel olarak tanımlanabilir bilgiler (PII); müşterileriniz için kişisel sağlık bilgileri (PHI).

Altyapı secretlar, altyapınıza ait secretlardır. Örnekler: veritabanı parolaları; API anahtarları; TLS sertifikaları.

Secret yönetim araçlarının çoğu, tam olarak bu tür secretlardan birini depolamak için tasarlanmıştır ve onu diğer türleri depolamaya zorlamaya çalışabilirsiniz, ancak bu, güvenlik veya kullanılabilirlik açısından nadiren iyi bir fikirdir. Örneğin, altyapı secretları olan şifreleri saklama şekliniz, müşteri secretları olan şifreleri saklama şeklinizden tamamen farklıdır: İlki için, secretları çözebilmek ve orijinal şifreyi geri alabilmek için genellikle AES gibi bir şifreleme algoritması (belki de bir nonce ile) kullanırsınız. Öte yandan, ikincisi için, orijinal parolayı geri almanın bir yolu olmaması gerektiğinden, genellikle salt içeren bir hash algoritma (ör. bcrypt) kullanırsınız. Yanlış yaklaşımı kullanmak felaket olabilir, bu nedenle iş için doğru aracı kullanın!

  • Depolama

Secretları depolamak için en yaygın iki strateji, dosya tabanlı bir secret store veya merkezi bir secret store kullanmaktır.

Dosya tabanlı secret depoları, secretları genellikle sürüm kontrolünde kontrol edilen şifreli dosyalarda saklar. Dosyaları şifrelemek için bir yerde bir şifreleme anahtarı saklamanız gerekir. Tipik çözüm, bulut sağlayıcınızın Anahtar Yönetim Hizmetini (KMS) (ör. AWS KMS, Google KMS, Azure Key Vault) kullanmak veya ekibinizdeki bir veya daha fazla geliştiricinin PGP anahtarlarını kullanmaktır.

Merkezileştirilmiş secret depolar, genellikle ağ üzerinden konuştuğunuz ve sırlarınızı şifreleyen ve bunları MySQL, PostgreSQL, DynamoDB, vb. gibi bir veri deposunda saklayan web hizmetleridir. Bu secretları şifrelemek için, bu merkezi secret depoların bir şifreleme anahtarına ihtiyacı vardır. Tipik olarak, şifreleme anahtarı o hizmet tarafından yönetilir veya hizmet, bir bulut sağlayıcısının KMS'sine dayanır.

  • Arayüz

Çoğu gizli yönetim aracına bir API, CLI ve/veya UI aracılığıyla erişilebilir.

Hemen hemen tüm merkezi secret depolar, ağ istekleri aracılığıyla tüketebileceğiniz bir API sunar: örneğin, HTTP üzerinden eriştiğiniz bir REST API. API, kodunuzun secretları programlı olarak okuması gerektiğinde uygundur. Örneğin, bir uygulama açılırken, bir veritabanı parolası almak için merkezi secret deponuza bir API çağrısı yapabilir. Ayrıca, bu bölümün ilerleyen kısımlarında göreceğiniz gibi, başlığın altında, secretları almak için merkezi bir secret store'un API'sini kullanan Terraform kodu yazabilirsiniz.

Tüm dosya tabanlı secret depolar, bir komut satırı arabirimi (CLI) aracılığıyla çalışır. Merkezileştirilmiş secret depoların çoğu, kaputun altında servise API çağrıları yapan CLI araçları da sağlar. CLI araçları, geliştiricilerin secretlara erişmesi (örneğin, bir dosyayı şifrelemek için birkaç CLI komutu kullanma) ve komut dosyası oluşturma (örneğin, secretları şifrelemek için bir komut dosyası yazma) için uygun bir yoldur.

Merkezi secret depolardan bazıları ayrıca web, masaüstü veya mobil aracılığıyla bir kullanıcı arayüzünü (UI) ortaya çıkarır. Bu, ekibinizdeki herkesin secretlara erişmesi için potansiyel olarak daha da uygun bir yoldur.

2.2. Secret yönetim araçlarının karşılaştırılması

Tablo 1, popüler gizli yönetim araçlarının bir karşılaştırmasını, önceki bölümde tanımlanan üç hususa göre ayrılmış olarak göstermektedir.

Secret Tipi Secret Deposu Secret Arayüzü
HashiCorp Vault Infrastructure Centralized service UI, API, CLI
AWS Secrets Manager Infrastructure Centralized service UI, API, CLI
Google Secrets Manager Infrastructure Centralized service UI, API, CLI
Azure Key Vault Infrastructure Centralized service UI, API, CLI
Confidant Infrastructure Centralized service UI, API, CLI
Keywhiz Infrastructure Centralized service API, CLI
SOPS Infrastructure Files CLI
git-secret Infrastructure Files CLI
1Password Personal Centralized service UI, API, CLI
LastPass Personal Centralized service UI, API, CLI
KeePass Personal Files UI, CLI
Keychain (macOS) Personal Files UI, CLI
Credential Manager (Windows) Personal Files UI, CLI
pass Personal Files CLI
Active Directory Customer Centralized service UI, API, CLI
Auth0 Customer Centralized service UI, API, CLI
Okta Customer Centralized service UI, API, CLI
OneLogin Customer Centralized service UI, API, CLI
Ping Customer Centralized service UI, API, CLI
AWS Cognito Customer Centralized service UI, API, CLI

Bu, Terraform hakkında bir yazı olduğundan, bundan sonra çoğunlukla bir API veya CLI aracılığıyla erişilen altyapı secretları için tasarlanmış secret yönetim araçlarına odaklanacağız. Ayrıca, bunlar genellikle altyapı secret araçlarında kimlik doğrulaması yapmanız gereken sırları içerdiğinden zaman zaman kişisel secret yönetim araçlarından da bahsedeceğiz.

3. Terraform ile secret yönetim araçlarını kullanma

Şimdi bu secret yönetim araçlarını Terraform ile nasıl kullanacağınıza bakalım. Terraform kodunuzun secretlarla karşılacağı muhtemel üç yerin her birini inceleyelim:

  1. Providerlar
  2. Resource ve data sourcelar
  3. State ve plan dosyaları

3.1. Providerlar

Tipik olarak, Terraform ile çalışırken secretlara ilk maruz kalmanız, bir provider için kimlik doğrulaması yapmanız gerektiği zamandır. Örneğin, AWS sağlayıcısını kullanan kodda terraform apply çalıştırmak istiyorsanız, önce AWS'de kimlik doğrulaması yapmanız gerekir ve bu genellikle gizli olan erişim anahtarlarınızı kullanmak anlamına gelir. Bu secretları nasıl saklamalısınız? Ve onları Terraform'a nasıl sunmalısınız?

Bu soruları cevaplamanın birçok yolu var. Terraform belgelerinde ara sıra ortaya çıksa da, YAPMAMANIZ gereken bir yol, secretları doğrudan kodun içine düz metin olarak koymaktır:

provider "aws" {
  region = "us-east-2"

  # DO NOT DO THIS!!!
  access_key = "(ACCESS_KEY)"
  secret_key = "(SECRET_KEY)"
  # DO NOT DO THIS!!!
}

Bu bölümde daha önce tartışıldığı gibi, kimlik bilgilerini bu şekilde düz metin olarak saklamak güvenli DEĞİLDİR. Ayrıca, bu, bu modülün tüm kullanıcıları için tek bir kimlik bilgisi seti kullanmanız için sizi kodlayacağından pratik de değildir. Oysa çoğu durumda farklı bilgisayarlarda ve farklı ortamlarda (dev, sahne, prod) farklı kimlik bilgilerine ihtiyacınız olacaktır.

Kimlik bilgilerinizi saklamak ve bunları Terraform providerlar tarafından erişilebilir kılmak için çok daha güvenli olan birkaç teknik vardır. Terraform'u çalıştıran kullanıcıya göre gruplandırarak bu tekniklere bir göz atalım:

  • İnsan kullanıcılar: Terraform'u kendi bilgisayarlarında çalıştıran geliştiriciler.
  • Makine kullanıcıları: Hiçbir insan olmadan Terraform çalıştıran otomatik sistemler (örneğin bir CI sunucusu).

3.1.1. İnsan Kullanıcılar

Hemen hemen tüm Terraform sağlayıcıları, kimlik bilgilerinizi doğrudan kodun içine koymaktan başka bir şekilde belirtmenize izin verir. En yaygın seçenek, ortam değişkenlerini kullanmaktır. Örneğin, AWS'de kimlik doğrulaması yapmak için ortam değişkenlerini şu şekilde kullanabilirsiniz:

$  export AWS_ACCESS_KEY_ID=(YOUR_ACCESS_KEY_ID)
$  export AWS_SECRET_ACCESS_KEY=(YOUR_SECRET_ACCESS_KEY)

Kimlik bilgilerinizi ortam değişkenleri olarak ayarlamak, plain text secretları kodunuzdan uzak tutar. Ayrıca Terraform çalıştıran herkesin kendi kimlik bilgilerini sağlamasına imkan verir ve kimlik bilgilerinin diskte değil, yalnızca bellekte saklanmasını sağlar.

Sorabileceğiniz önemli bir soru, erişim anahtarı kimliğinin ve gizli erişim anahtarının ilk etapta nerede saklanacağıdır? Ezberlemek için çok uzun ve rastgeledirler, ancak bunları bilgisayarınızda düz metin olarak saklarsanız, bu sırları hala riske atmış olursunuz. Bu bölüm insan kullanıcılara odaklandığından çözüm, erişim anahtarlarınızı (ve diğer secretları) kişisel secretlar için tasarlanmış bir secret yöneticide saklamaktır. Örneğin, erişim anahtarlarınızı 1Password veya KeePass'te saklayabilir ve bunları terminalinizdeki dışa aktarma komutlarına kopyalayabilir/yapıştırabilirsiniz.

Bu kimlik bilgilerini CLI'de sık kullanıyorsanız, CLI arabirimini destekleyen bir secret yönetici kullanmak daha da uygun bir seçenektir. Örneğin, 1Password, op adında bir CLI aracı sunar. Mac ve Linux'ta, CLI'de 1Password kimlik doğrulaması yapmak için op'u aşağıdaki gibi kullanabilirsiniz:

$ eval $(op signin my)

Kimliğinizi doğruladıktan sonra, erişim anahtarlarınızı "id" ve "secret" alanları ile "aws-dev" adı altında depolamak için 1Password uygulamasını kullandığınızı varsayarsak, bu erişim anahtarlarını ortam değişkenleri olarak ayarlamak için op'u şu şekilde kullanabilirsiniz:

$ export AWS_ACCESS_KEY_ID=$(op get item 'aws-dev' --fields 'id')
$ export AWS_SECRET_ACCESS_KEY=$(op get item 'aws-dev' --fields 'secret')

1Password ve op gibi araçlar genel amaçlı secret yönetimi için harika olsa da, belirli sağlayıcılar için bunu daha da kolaylaştırmak adına özel CLI araçları vardır. Örneğin, AWS'de kimlik doğrulaması yapmak için aws-vault açık kaynak aracını kullanabilirsiniz. aws-vault add komutunu kullanarak erişim anahtarlarınızı dev adlı bir profil altında aşağıdaki gibi kaydedebilirsiniz:

$ aws-vault add dev
Enter Access Key Id: (YOUR_ACCESS_KEY_ID)
Enter Secret Key: (YOUR_SECRET_ACCESS_KEY)

Başlık altında, aws-vault bu kimlik bilgilerini işletim sisteminizin yerel parola yöneticisinde (örneğin, macOS'te Anahtar Zinciri, Windows'ta Kimlik Bilgileri Yöneticisi) güvenli bir şekilde saklar. Bu kimlik bilgilerini kaydettikten sonra, artık herhangi bir CLI komutu için AWS'de aşağıdaki şekilde kimlik doğrulaması yapabilirsiniz:

$ aws-vault exec <PROFILE> -- <COMMAND>

burada PROFILE, daha önce add komutu (ör. dev) aracılığıyla oluşturduğunuz bir profilin adıdır ve COMMAND, yürütülecek komuttur. Örneğin, terraform apply çalıştırmak için daha önce kaydettiğiniz geliştirici kimlik bilgilerini şu şekilde kullanabilirsiniz:

$ aws-vault exec dev -- terraform apply

exec komutu, geçici kimlik bilgilerini almak için otomatik olarak AWS STS'yi kullanır ve bunları yürütmekte olduğunuz komuta ortam değişkenleri olarak sunar (bu durumda, terraform apply). Bu şekilde, kalıcı kimlik bilgileriniz yalnızca güvenli bir şekilde (işletim sisteminizin yerel parola yöneticisinde) saklanmakla kalmaz, aynı zamanda çalıştırdığınız herhangi bir işlem için yalnızca geçici kimlik bilgilerini açığa çıkarırsınız. Böylece kimlik bilgilerinin sızdırılması riski en aza indirilir. aws-vault ayrıca IAM rollerini üstlenmek, çok faktörlü kimlik doğrulama (MFA) kullanmak, web konsolunda hesaplarda oturum açmak ve daha fazlası için yerel desteğe sahiptir.

3.1.2. Makine Kullanıcılar

Bir insan kullanıcı, ezberlenmiş bir parolaya güvenebilirken, ortada insan olmadığı durumlarda ne yaparsınız? Örneğin, Terraform kodunu otomatik olarak çalıştırmak için bir continuous integration/continuous delivery (CI/CD) düzeni kuruyorsanız, bu işlem hattının kimliğini nasıl güvenli bir şekilde doğrularsınız? Bu durumda, bir makine kullanıcısı için kimlik doğrulama ile uğraşıyor olursunuz. Soru şu ki, bir makinenin (örneğin, CI sunucunuzun) kendisini başka bir makinede (örneğin, AWS API sunucuları) herhangi bir secreti düz metin olarak saklamadan doğrulamasını nasıl sağlarsınız?

Buradaki çözüm, büyük ölçüde dahil olan makinelerin türüne bağlıdır: yani, kimlik doğrulaması yaptığınız makine ve kimlik doğrulaması yaptığınız makine. Üç örnek üzerinden gidelim:

  • Saklanan secretlara sahip bir CI Sunucusu olarak CircleCI
  • IAM rolleriyle Jenkins'i bir CI sunucusu olarak çalıştıran EC2 instance
  • OIDC ile bir CI sunucusu olarak GitHub Actions

3.1.2.1 Saklanan secretlara sahip CircleCI CI Sunucusu

Terraform kodunu çalıştırmak için popüler bir yönetilen CI/CD platformu olan CircleCI'yi kullanmak istediğinizi düşünelim. CircleCI ile, build adımlarınızı bir .circleci/config.yml dosyasında yapılandırırsınız. Burada terraform apply çalıştırmak için şuna benzer bir iş tanımlayabilirsiniz:

version: '2.1'
orbs:
  # Install Terraform using a CircleCi Orb
  terraform: circleci/[email protected]
jobs:
  # Define a job to run 'terraform apply'
  terraform_apply:
    executor: terraform/default
    steps:
      - checkout
      - terraform/init
      - terraform/apply
workflows:
  # Create a workflow to deploy changes
  deploy:
    # The workflow runs our 'terraform apply' job
    jobs:
      - terraform_apply
    # Only run this workflow on commits to the main branch
    filters:
      branches:
        only:
          - main          

CircleCI gibi bir araçla, bir provider kimliğini doğrulamanın yolu, o providerda bir makine kullanıcısı (yani, herhangi bir insan tarafından değil, yalnızca otomasyon için kullanılan bir kullanıcı) oluşturmaktır. buna CircleCI Context denir ve build çalıştığında, CircleCI bu Cotext içindeki kimlik bilgilerini ortam değişkenleri olarak iş akışlarınıza sunar. Örneğin, Terraform kodunuzun AWS'de kimlik doğrulaması yapması gerekiyorsa, AWS'de yeni bir IAM kullanıcısı oluşturur, bu IAM kullanıcısına Terraform değişikliklerinizi dağıtmak için ihtiyaç duyduğu izinleri verir ve bu IAM kullanıcısının erişim anahtarlarını bir CircleCI Context'e manuel olarak kopyalarsınız.

Şekil 1: AWS kimlik bilgilerine sahip bir CircleCI Context

Son olarak, context parametresi aracılığıyla CircleCI Context'inizi kullanmak için .circleci/config.yml dosyanızdaki iş akışlarını güncellersiniz:

workflows:
  # Create a workflow to deploy changes
  deploy:
    # The workflow runs our 'terraform apply' job
    jobs:
      - terraform_apply
    # Only run this workflow on commits to the main branch
    filters:
      branches:
        only:
          - main
    # Expose secrets in the CircleCI context as environment variables
    context:
      - example-context      

Build çalıştığında, CircleCI bu Contextteki secretlrı otomatik olarak ortam değişkenlerine ekler. Bu durumda AWS_ACCESS_KEY_ID ve AWS_SECRET_ACCESS_KEY ortam değişkenlerinesahip olmuş olursunuz ve terraform apply, providerın kimliğini doğrulamak için bu ortam değişkenlerini otomatik olarak kullanır.

Bu yaklaşımın en büyük dezavantajı, (1) kimlik bilgilerini manuel olarak yönetmeniz ve (2) sonuç olarak, bir kez CircleCI'ye kaydedildikten sonra nadiren (eğer varsa) değişen kalıcı kimlik bilgilerini kullanmanız gerektiğidir. Sonraki iki örnek, alternatif yaklaşımları göstermektedir.

3.1.2.2 IAM rolleriyle Jenkins'i bir CI sunucusu olarak çalıştıran EC2 instance

Terraform kodunu çalıştırmak için bir EC2 instance kullanıyorsanız (örneğin, Jenkins'i bir EC2 instance üzerinde CI sunucusu olarak çalıştırıyorsunuz), makine kullanıcı kimlik doğrulaması için önerdiğim çözüm, bu EC2 instance bir IAM rolü vermektir. IAM rolü, AWS'de IAM izinleri verilebilen bir varlık olması bakımından IAM kullanıcısına benzer. Ancak, IAM kullanıcılarının aksine, IAM rolleri herhangi bir kişiyle ilişkilendirilmez ve kalıcı kimlik bilgilerine (parola veya erişim anahtarları) sahip değildir. Bunun yerine, diğer IAM varlıkları tarafından bir rol üstlenilebilir: örneğin, bir IAM kullanıcısı, normalde sahip olduklarından farklı izinlere geçici olarak erişim sağlamak için bir rol üstlenebilir; EC2 instanceları gibi birçok AWS hizmeti, AWS hesabınızda bu hizmet izinlerini vermek için IAM rollerini üstlenebilir.

IAM kullanıcıları ve rolleri hakkında daha detaylı bilgi için aşağıdaki blog yazımızı okuyabilirsiniz:

AWS IAM User, Group, Role ve Policy Nedir? Arasındaki Farklar Nelerdir?
AWS Identify and Access Management (IAM), AWS hizmetlerine ve kaynaklarına ayrıntılı izinler sağlar.

Örneğin, bir EC2 instance dağıtmak için birçok kez gördüğünüz kod:

resource "aws_instance" "example" {
  ami           = "ami-0fb653ca2d3203ac1"
  instance_type = "t2.micro"
}

Bir IAM rolü oluşturmak için önce, kimin IAM rolünü üstlenmesine izin verildiğini tanımlayan bir IAM policy olan assume role policy tanımlamanız gerekir. IAM policy'i ham JSON'da yazabilirsiniz, ancak Terraform'un sizin için JSON oluşturabilecek uygun bir aws_iam_policy_document data source'u vardır. EC2 hizmetinin bir IAM rolü üstlenmesine olanak tanıyan bir rol üstlenme policy tanımlamak için aws_iam_policy_document'ı şu şekilde kullanabilirsiniz:

data "aws_iam_policy_document" "assume_role" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

Şimdi, bir IAM rolü oluşturmak için aws_iam_role kaynağını kullanabilir ve rol üstlenme policy olarak kullanmak için onu aws_iam_policy_document'inizden JSON'u iletebilirsiniz:

resource "aws_iam_role" "instance" {
  name_prefix        = var.name
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

Artık bir IAM rolünüz var, ancak varsayılan olarak IAM rolleri size herhangi bir izin vermiyor. Bu nedenle, bir sonraki adım, bir kez üstlendikten sonra rolle gerçekte neler yapabileceğinizi belirten IAM rolüne bir veya daha fazla IAM policy eklemektir. EC2 instance dağıtan Terraform kodunu çalıştırmak için Jenkins kullandığınızı düşünelim. aws_iam_policy_document data source'u, EC2 instanceları üzerinde yönetici izinleri veren bir IAM policy'i aşağıdaki gibi tanımlamak için kullanabilirsiniz:

data "aws_iam_policy_document" "ec2_admin_permissions" {
  statement {
    effect    = "Allow"
    actions   = ["ec2:*"]
    resources = ["*"]
  }
}

Ve aws_iam_role_policy kaynağını kullanarak bu policy'i IAM rolünüze ekleyebilirsiniz:

resource "aws_iam_role_policy" "example" {
  role   = aws_iam_role.instance.id
  policy = data.aws_iam_policy_document.ec2_admin_permissions.json
}

Son adım, bir instance profili oluşturarak EC2 instance'ınızın bu IAM rolünü otomatik olarak üstlenmesine izin vermektir:

resource "aws_iam_instance_profile" "instance" {
  role = aws_iam_role.instance.name
}

Ve EC2 örneğinize iam_instance_profile parametresi aracılığıyla bu instance profilini kullanmasını söyleyin:

resource "aws_instance" "example" {
  ami           = "ami-0fb653ca2d3203ac1"
  instance_type = "t2.micro"

  # Attach the instance profile
  iam_instance_profile = aws_iam_instance_profile.instance.name
}

AWS, kaputun altında, her EC2 instance'da bir instance meta veri uç noktası çalıştırır. Bu, yalnızca instance'ın kendisinde çalışan işlemler tarafından ulaşılabilen bir uç noktadır ve bu işlemler, instance ile ilgili meta verileri getirmek için bu uç noktayı kullanabilir. Instance'a (bir instance profili aracılığıyla) eklenmiş bir IAM rolü varsa, bu meta veriler, AWS'de kimlik doğrulaması yapmak ve bu IAM rolünü üstlenmek için kullanılabilecek AWS kimlik bilgilerini içerecektir. Terraform gibi AWS SDK'sını kullanan herhangi bir araç, bu instance meta verisi uç noktası kimlik bilgilerini otomatik olarak nasıl kullanacağını bilir. Bu nedenle, bu IAM rolüyle EC2 instance'da terraform apply çalıştırır çalıştırmaz, Terraform kodunuz bu IAM rolü olarak doğrulanır, ve kodunuza başarıyla çalışması için gereken EC2 yönetici izinleri verilir.

CI sunucusu gibi AWS'de çalışan herhangi bir otomatik işlem için IAM rolleri, (1) kimlik bilgilerini manuel olarak yönetmek zorunda kalmadan kimlik doğrulaması için bir yol sağlar ve (2) AWS'nin instance meta veri uç noktası aracılığıyla sağladığı kimlik bilgileri her zaman geçicidir ve dönüşümlüdür. Bunlar, AWS hesabınızın dışında çalışan CircleCI gibi bir araçla manuel olarak yönetilen kalıcı kimlik bilgilerine göre iki büyük avantajdır. Ancak, bir sonraki örnekte göreceğiniz gibi, bazı durumlarda aynı avantajlara harici araçlar için de sahip olmak mümkündür.

3.1.2.3 OIDC ile bir CI sunucusu olarak GitHub Actions

GitHub Actions, Terraform'u çalıştırmak için kullanmak isteyebileceğiniz bir başka popüler yönetilen CI / CD platformudur. Geçmişte GitHub Actionas, tıpkı CircleCI gibi, kimlik bilgilerini manuel olarak kopyalamanızı gerektiriyordu. Ancak 2021 itibariyle GitHub Actions daha iyi bir alternatif sunuyor: Open ID Connect (OIDC). OIDC'yi kullanarak, CI sistemi ile bulut sağlayıcınız arasında güvenilir bir bağlantı kurabilirsiniz (GitHub Actions, AWS, Azure ve Google Cloud'u destekler), böylece CI sisteminiz, herhangi bir kimlik bilgilerini manuel olarak yönetmek zorunda kalmadan bu sağlayıcıların kimliğini doğrulayabilir.

GitHub Actions, iş akışlarını, burada gösterilen terraform.yml dosyası gibi bir .github/workflows klasöründeki YAML dosyalarında tanımlarsınız:

name: Terraform Apply
# Only run this workflow on commits to the main branch
on:
  push:
    branches:
      - 'main'
jobs:
  TerraformApply:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      # Run Terraform using HashiCorp's setup-terraform Action
      - uses: hashicorp/setup-terraform@v1
          with:
            terraform_version: 1.1.0
            terraform_wrapper: false
        run: |
          terraform init
          terraform apply -auto-approve          

Terraform kodunuz AWS gibi bir sağlayıcıyla görüşüyorsa, bu iş akışının o sağlayıcının kimliğini doğrulaması için bir yol sağlamanız gerekir. Bunu OIDC3 kullanarak yapmak için ilk adım, AWS hesabınızda aws_iam_openid_connect_provider kaynağını kullanarak bir IAM OIDC kimlik sağlayıcısı oluşturmak ve bunu tls_certificate veri kaynağı aracılığıyla alınan GitHub Actions fingerprinte güvenecek şekilde yapılandırmaktır:

# Create an IAM OIDC identity provider that trusts GitHub
resource "aws_iam_openid_connect_provider" "github_actions" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = [
    data.tls_certificate.github.certificates[0].sha1_fingerprint
  ]
}

# Fetch GitHub's OIDC thumbprint
data "tls_certificate" "github" {
  url = "https://token.actions.githubusercontent.com"
}

Artık, tam olarak önceki bölümde olduğu gibi IAM rolleri oluşturabilirsiniz (ör. EC2 yönetici izinleri eklenmiş bir IAM rolü), ancak bu IAM rolleri için rol üstlenme politikası farklı görünecektir:

data "aws_iam_policy_document" "assume_role_policy" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]
    effect  = "Allow"

    principals {
      identifiers = [aws_iam_openid_connect_provider.github_actions.arn]
      type        = "Federated"
    }

    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:sub"
      # The repos and branches defined in var.allowed_repos_branches
      # will be able to assume this IAM role
      values = [
        for a in var.allowed_repos_branches :
        "repo:${a["org"]}/${a["repo"]}:ref:refs/heads/${a["branch"]}"
      ]
    }
  }
}

Bu policy, IAM OIDC kimlik sağlayıcısının birleşik kimlik doğrulama yoluyla IAM rolünü üstlenmesine olanak tanır. Yalnızca allow_repos_branches giriş değişkeni aracılığıyla belirttiğiniz belirli GitHub depolarının ve branchlerinin bu IAM rolünü üstlenmesini sağlayan koşul bloğuna dikkat edin:

variable "allowed_repos_branches" {
  description = "GitHub repos/branches allowed to assume the IAM role."
  type = list(object({
    org    = string
    repo   = string
    branch = string
  }))
  # Example:
  # allowed_repos_branches = [
  #   {
  #     org    = "kerteriz"
  #     repo   = "terraform-up-and-running-code"
  #     branch = "main"
  #   }
  # ]
}

Bu, tüm GitHub repolarının yanlışlıkla AWS hesabınızda kimlik doğrulaması yapmasına izin vermemenizi sağlamak için önemlidir! Artık yapılarınızı bu IAM rolünü üstlenecek şekilde GitHub Actions'da yapılandırabilirsiniz. İlk olarak, iş akışınızın en üstünde, yapınıza id-token: write iznini verin:

permissions:
  id-token: write

Ardından, configure-aws-credentials eylemini kullanarak AWS'de kimlik doğrulaması yapmak için Terraform'u çalıştırmadan hemen önce bir build adımı ekleyin:

	  # Authenticate to AWS using OIDC
      - uses: aws-actions/configure-aws-credentials@v1
        with:
          # Specify the IAM role to assume here
          role-to-assume: arn:aws:iam::1234567890:role/example-role
          aws-region: us-east-2

      # Run Terraform using HashiCorp's setup-terraform Action
      - uses: hashicorp/setup-terraform@v1
          with:
            terraform_version: 1.1.0
            terraform_wrapper: false
        run: |
          terraform init
          terraform apply -auto-approve          

Şimdi, bu yapıyı allow_repos_branches değişkenindeki repolardan ve branchlerden birinde çalıştırdığınızda, GitHub geçici kimlik bilgilerini kullanarak IAM rolünüzü otomatik olarak üstlenebilir ve Terraform, yönetmek zorunda kalmadan bu IAM rolünü kullanarak AWS'de kimlik doğrulaması yapacaktır.

3.2. Resource ve Data Source'lar

Terraform kodunuzda secretlarla karşılaşacağınız bir sonraki yer resource ve data sourcelardır. Örneğin, bölümün önceki bölümlerinde veritabanı kimlik bilgilerinin aws_db_instance kaynağına iletilmesi örneğini gördünüz:

resource "aws_db_instance" "example" {
  identifier_prefix   = "terraform-up-and-running"
  engine              = "mysql"
  allocated_storage   = 10
  instance_class      = "db.t2.micro"
  skip_final_snapshot = true
  db_name             = var.db_name

  # DO NOT DO THIS!!!
  username = "admin"
  password = "password"
  # DO NOT DO THIS!!!
}

Bunu bu bölümde zaten birçok kez söyledim, ancak bu o kadar önemli bir nokta ki, tekrar tekrar etmeye değer: bu kimlik bilgilerini düz metin olarak kodda saklamak kötü bir fikirdir. Peki, bunu yapmanın daha iyi bir yolu nedir?

Kullanabileceğiniz üç ana yaklaşım vardır:

  • Ortam değişkenleri
  • Şifreli dosyalar
  • Secret storelar

3.2.1. Ortam Değişkenleri

Terraform State Nedir? Nasıl Yönetilir? başlıklı yazımızda ve bu bölümün başlarında providerlar hakkında konuşurken gördüğünüz bu ilk teknik, Terraform'un ortam değişkenlerini okumak için yerel desteğinden yararlanarak düz metin secretları kodunuzdan uzak tutar.

Genel bakış

Bu tekniği kullanmak için, iletmek istediğiniz secretlar için değişkenler tanımlayın:

variable "db_username" {
  description = "The username for the database"
  type        = string
  sensitive   = true
}

variable "db_password" {
  description = "The password for the database"
  type        = string
  sensitive   = true
}

Bu değişkenlerin, secret içerdiklerini belirtmek için sensitive = true ile işaretlenmiştir (böylece Terraform, plan veya apply komutunda değerleri loga kaydetmez) ve bu değişkenlerin bir default değeri yoktur (plain text olaral depolamamak için). Ardından, değişkenleri, bu secretlara ihtiyaç duyan Terraform kaynaklarına iletin:

resource "aws_db_instance" "example" {
  identifier_prefix   = "terraform-up-and-running"
  engine              = "mysql"
  allocated_storage   = 10
  instance_class      = "db.t2.micro"
  skip_final_snapshot = true
  db_name             = var.db_name

  # Pass the secrets to the resource
  username = var.db_username
  password = var.db_password
}

Artık TF_VAR_xxx ortam değişkenini ayarlayarak her xxx değişkeni için bir değer iletebilirsiniz:

$  export TF_VAR_db_username=(DB_USERNAME)
$  export TF_VAR_db_password=(DB_PASSWORD)

Ortam değişkenleri aracılığıyla secretları iletmek, secretları kodunuzda düz metin olarak saklamaktan kaçınmanıza yardımcı olur, ancak önemli bir soruyu yanıtlamaz: Secretları nasıl güvenli bir şekilde saklarsınız? Ortam değişkenlerini kullanmanın güzel bir yanı, hemen hemen her tür secret yönetimi çözümüyle çalışabilmeleridir. Örneğin, bir seçenek olarak, secretları kişisel bir secret yöneticisinde (örneğin, 1Password) depolamak ve bu secretları terminalinizde ortam değişkenleri olarak manuel olarak ayarlamaktır. Diğer bir seçenek de, secretları merkezi bir secret depoda (örneğin, HashiCorp Kasası) depolamak ve bu secretları okumak ve bunları ortam değişkenleri olarak ayarlamak için bu secret deponun API'sini veya CLI'sini kullanan bir komut dosyası yazmaktır.

Bu tekniğin avantajları

  • Düz metin secretları kodunuzdan ve sürüm kontrol sisteminizden uzak tutar.
  • Hemen hemen tüm diğer secret yönetim çözümlerini kullanabileceğiniz için secretları saklamak kolaydır. Diğer bir deyişle, şirketinizin secretları yönetmek için zaten bir yolu varsa, genellikle bunu ortam değişkenleriyle çalıştırmanın bir yolunu bulabilirsiniz.
  • Ortam değişkenlerini ayarlamak basit olduğu için secretları almak kolaydır.
  • Ortam değişkenlerini sahte değerlere kolayca ayarlayabileceğiniz için otomatik testlerle entegrasyon kolaydır.
  • Ortam değişkenlerini kullanmak, daha sonra tartışılacak olan diğer bazı secret yönetim çözümlerinin aksine, herhangi bir maliyet gerektirmez.

Bu tekniğin dezavantajları

  • Her şey Terraform kodunun kendisinde tanımlanmamıştır. Bu, kodu anlamayı ve korumayı zorlaştırır. Kodunuzu kullanan herkesin, bu ortam değişkenlerini manuel olarak ayarlamak veya bir handler komut dosyası çalıştırmak için ek adımlar atması gerektiğini bilmesi gerekir.
  • Secret yönetim uygulamalarını standartlaştırmak daha zordur. Tüm secretların yönetimi Terraform'un dışında gerçekleştiğinden, kod herhangi bir güvenlik özelliğini zorlamaz ve birisinin hala secretları güvenli olmayan bir şekilde yönetmesi mümkündür (örneğin, bunları düz metin olarak saklamak).
  • Secretlar versiyonlanmadığından, paketlenmediğinden ve kodunuzla test edilmediğinden, bir ortama yeni bir secret ekleme (ör. staging) ancak bunu başka bir ortama eklemeyi unutma (ör. production) gibi yapılandırma hataları daha olasıdır.

3.2.2. Şifreli Dosyalar

İkinci teknik, secretları şifrelemeye, şifreli metni bir dosyada depolamaya ve bu dosyayı sürüm kontrolünde kontrol etmeye dayanır.

Genel bakış

Bir dosyadaki bazı secretlar gibi bazı verileri şifrelemek için bir şifreleme anahtarına ihtiyacınız vardır. Bu anahtarın kendisi de bir secrettır! Bu biraz muamma yaratır: Bu anahtarı nasıl güvenli bir şekilde saklarsınız? Sürüm kontrolüyle anahtarı düz metin olarak kontrol edemezsiniz, çünkü bu sefer şifrelemenin bir anlamı kalmaz. Anahtarı başka bir anahtarla şifreleyebilirsiniz, ancak daha sonra ikinci anahtarı nasıl güvenli bir şekilde saklayacağınızı bulmanız gerektiğinden, yaptığınız tek şey kutuyu yolda bırakmaktır.

Bu bilmecenin en yaygın çözümü, anahtarı bulut sağlayıcınız tarafından sağlanan AWS KMS, GCP KMS veya Azure Key Vault gibi bir anahtar yönetim hizmetinde (KMS) depolamaktır. Bu, secreti güvenli bir şekilde depolamak için bulut sağlayıcısına güvenerek yol sorununu çözer.

Başka bir seçenek de PGP anahtarlarını kullanmaktır. Her geliştirici, bir genel anahtar ve bir özel anahtardan oluşan kendi PGP anahtarına sahip olabilir. Bir secreti bir veya daha fazla ortak anahtarla şifrelerseniz, yalnızca ilgili özel anahtarlara sahip geliştiriciler bu secretların şifresini çözebilir. Özel anahtarlar ise geliştiricinin ezberlediği veya kişisel secret yöneticisinde sakladığı bir parola ile korunur.

AWS KMS kullanan bir örneğe bakalım. Öncelikle, AWS'nin sizin için yönettiği bir şifreleme anahtarı olan bir KMS Customer Managed Key (CMK) oluşturmanız gerekir. Bir CMK oluşturmak için önce, bu CMK'yı kimlerin kullanabileceğini tanımlayan bir IAM politikası olan bir key policy tanımlamanız gerekir. Bu örneği basit tutmak için, mevcut kullanıcıya CMK üzerinden yönetici izinleri veren bir anahtar politikası oluşturalım. aws_caller_identity veri kaynağını kullanarak mevcut kullanıcının bilgilerini (kullanıcı adı, ARN vb.) getirebilirsiniz:

data "aws_caller_identity" "self" {}

Ve şimdi aws_caller_identity veri kaynağının çıktılarını bir aws_iam_policy_document veri kaynağında kullanabilirsiniz; bu veri kaynağı, CMK üzerinde mevcut kullanıcı yönetici izinlerini veren bir key policy oluşturmak için kullanılır:

data "aws_iam_policy_document" "cmk_admin_policy" {
  statement {
    effect    = "Allow"
    resources = ["*"]
    actions   = ["kms:*"]
    principals {
      type        = "AWS"
      identifiers = [data.aws_caller_identity.self.arn]
    }
  }
}

Ardından, aws_kms_key kaynağını kullanarak CMK'yi oluşturabilirsiniz:

resource "aws_kms_key" "cmk" {
  policy = data.aws_iam_policy_document.cmk_admin_policy.json
}

Varsayılan olarak, KMS CMK'lerinin yalnızca uzun bir sayısal tanımlayıcıyla (ör., b7670b0e-ed67-28e4-9b15-0d61e1485be3) tanımlandığını unutmayın, bu nedenle aws_kms_alias kaynağını kullanarak CMK'niz için insan dostu bir takma ad oluşturmak da iyi bir uygulamadır. :

resource "aws_kms_alias" "cmk" {
  name          = "alias/kms-cmk-example"
  target_key_id = aws_kms_key.cmk.id
}

Yukarıdaki takma ad, AWS API ve CLI kullanırken b7670b0e-ed67-28e4-9b15-0d61e1485be3 gibi uzun bir tanımlayıcı yerine CMK'nize alias/kms-cmk-example olarak başvurmanıza olanak tanır. CMK'yi oluşturduktan sonra, verileri şifrelemek ve şifresini çözmek için kullanmaya başlayabilirsiniz. Tasarım gereği, temeldeki şifreleme anahtarını asla göremeyeceğinizi (ve dolayısıyla yanlışlıkla sızdırmayacağınızı) unutmayın. Bu şifreleme anahtarına yalnızca AWS erişebilir, ancak aşağıda açıklandığı gibi AWS API ve CLI'yi kullanarak bundan yararlanabilirsiniz.

İlk olarak, veritabanı kimlik bilgileri gibi bazı gizli diziler içeren db-creds.yml adlı bir dosya oluşturun:

username: admin
password: password

Not: Henüz şifrelemediğiniz için bu dosyayı sürüm kontrolünde KONTROL ETMEYİN! Bu verileri şifrelemek için aws kms encrypt komutunu kullanabilir ve ortaya çıkan şifre metnini yeni bir dosyaya yazabilirsiniz. AWS CLI kullanarak bu adımları gerçekleştiren encrypt.sh adlı küçük bir Bash betiği (Linux/Unix/macOS için):

CMK_ID="$1"
AWS_REGION="$2"
INPUT_FILE="$3"
OUTPUT_FILE="$4"

echo "Encrypting contents of $INPUT_FILE using CMK $CMK_ID..."
ciphertext=$(aws kms encrypt \
  --key-id "$CMK_ID" \
  --region "$AWS_REGION" \
  --plaintext "fileb://$INPUT_FILE" \
  --output text \
  --query CiphertextBlob)

echo "Writing result to $OUTPUT_FILE..."
echo "$ciphertext" > "$OUTPUT_FILE"

echo "Done!"

db-creds.yml dosyasını daha önce oluşturduğunuz KMS CMK ile şifrelemek ve elde edilen şifreli metni db-creds.yml.encrypted adlı yeni bir dosyada saklamak için encrypt.sh'yi şu şekilde kullanabilirsiniz:

$ ./encrypt.sh \
  alias/kms-cmk-example \
  us-east-2 \
  db-creds.yml \
  db-creds.yml.encrypted

Encrypting contents of db-creds.yml using CMK alias/kms-cmk-example...
Writing result to db-creds.yml.encrypted...
Done!

Artık db-creds.yml'yi (düz metin dosyası) silebilir ve db-creds.yml.encrypted'ı (şifreli dosya) sürüm kontrolünde güvenle kontrol edebilirsiniz. Bu noktada, içinde bazı secretlar bulunan şifreli bir dosyanız var, ancak o dosyayı Terraform kodunuzda nasıl kullanıyorsunuz?

İlk adım, aws_kms_secrets veri kaynağını kullanarak bu dosyadaki secretların şifresini çözmektir:

data "aws_kms_secrets" "creds" {
  secret {
    name    = "db"
    payload = file("${path.module}/db-creds.yml.encrypted")
  }
}

Yukarıdaki kod, file işlevini kullanarak diskten db-creds.yml.encrypted dosyasını okur ve KMS'de karşılık gelen anahtara erişim izniniz olduğunu varsayarak içeriğin şifresini çözer. Bu size orijinal db-creds.yml dosyasının içeriğini geri verir, bu nedenle sonraki adım YAML'yi aşağıdaki gibi ayrıştırmaktır:

locals {
  db_creds = yamldecode(data.aws_kms_secrets.creds.plaintext["db"])
}

Bu kod, aws_kms_secrets veri kaynağından veritabanı secretlarını çeker, YAML'yi ayrıştırır ve sonuçları db_creds adlı yerel bir değişkende saklar. Son olarak, db_creds'den kullanıcı adını ve parolayı okuyabilir ve bu kimlik bilgilerini aws_db_instance kaynağına iletebilirsiniz:

resource "aws_db_instance" "example" {
  identifier_prefix   = "terraform-up-and-running"
  engine              = "mysql"
  allocated_storage   = 10
  instance_class      = "db.t2.micro"
  skip_final_snapshot = true
  db_name             = var.db_name

  # Pass the secrets to the resource
  username = local.db_creds.username
  password = local.db_creds.password
}

Artık secretları şifreli bir dosyada saklamak için bir yolunuz var. Bu, sürüm kontrolü kontrol etmek için güvenlidir ve bu secretları Terraform kodunuzdaki dosyadan otomatik olarak okuyabilirsiniz. Bu yaklaşımda dikkat edilmesi gereken bir nokta, şifrelenmiş dosyalarla çalışmanın garip olmasıdır. Bir değişiklik yapmak için, dosyanın yerel olarak şifresini uzun bir aws kms decrypt komutuyla çözmeniz, bazı düzenlemeler yapmanız, dosyayı başka bir uzun aws kms encryption komutuyla yeniden şifrelemeniz ve tüm bu süre boyunca, yanlışlıkla kontrol etmemek için son derece dikkatli olmanız gerekir. Bu sıkıcı ve hataya açık bir süreçtir.

Bunu daha az garip hale getirmenin bir yolu, sops4 adlı açık kaynaklı bir araç kullanmaktır. Sops  <FILE> çalıştırdığınızda, sops otomatik olarak dosyanın şifresini çözecek ve düz metin içerikleriyle varsayılan metin düzenleyicinizi açacaktır. Düzenlemeyi tamamladığınızda ve metin düzenleyiciden çıktığınızda, sops içeriği otomatik olarak şifreleyecektir. Bu şekilde, şifreleme ve şifre çözme çoğunlukla şeffaftır, uzun aws kms komutları çalıştırmaya gerek yoktur ve düz metin secretları yanlışlıkla sürüm kontrolüne kontrol etme şansı daha düşüktür. 2022'den itibaren sops, AWS KMS, GCP KMS, Azure Key Vault veya PGP anahtarları aracılığıyla şifrelenmiş dosyalarla çalışabilir. Terraform'un sops tarafından şifrelenmiş dosyaların şifresini çözmek için henüz yerel desteğe sahip olmadığını unutmayın, bu nedenle bir 3. taraf sağlayıcı kullanmanız veya Terragrunt'da yerleşik sops desteğini kullanmanız gerekir.

Bu tekniğin avantajları

  • Düz metin secretları kodunuzdan ve sürüm kontrol sisteminizden uzak tutar.
  • Secretlarınız sürüm kontrolünde şifreli bir biçimde saklanır, böylece kodunuzun geri kalanıyla sürümlendirilir, paketlenir ve test edilir. Bu, bir ortama yeni bir secret eklemek (örn. staging) ancak bunu başka bir ortama eklemeyi unutmak (örn. production) gibi yapılandırma hatalarının azaltılmasına yardımcı olur.
  • Kullanmakta olduğunuz şifreleme biçiminin yerel olarak Terraform veya bir 3. taraf eklentisi tarafından desteklendiğini varsayarsak, secretları almak kolaydır.
  • Çeşitli farklı şifreleme seçenekleriyle çalışır: AWS KMS, GCP KMS, PGP vb.
  • Her şey kodda tanımlanmıştır. Ekstra manuel adımlar veya sarmalayıcı komut dosyaları gerekmez (ancak sops entegrasyonu bir 3. taraf eklentisi gerektirir).

Bu tekniğin dezavantajları

  • Secretları saklamak daha zordur. Ya çok sayıda komut çalıştırmanız (ör. aws kms encryption) ya da sops gibi harici bir araç kullanmanız gerekir. Bu araçları doğru ve güvenli bir şekilde kullanmak için bir öğrenme eğrisi vardır.
  • Otomatik testlerle entegrasyon daha zordur, çünkü şifreleme anahtarlarını ve şifreli test verilerini test ortamlarınız için kullanılabilir hale getirmek için fazladan çalışmanız gerekecek.
  • Secretlar artık şifrelenmiştir, ancak hala sürüm kontrolünde saklandıkları için, secretları döndürmek ve iptal etmek zordur. Herhangi biri şifreleme anahtarından ödün verirse, geri dönüp onunla şifrelenmiş tüm sırların şifresini çözebilir.
  • Secretlara kimin eriştiğini denetleme yeteneği minimum düzeydedir. Bir bulut anahtarı yönetim sistemi kullanıyorsanız (ör. AWS KMS), şifreleme anahtarını kimin kullandığına ilişkin bir denetim günlüğü tutacaktır, ancak anahtarın gerçekte ne için kullanıldığını bilemezsiniz (ör. hangi secretlara erişildi).
  • Yönetilen anahtar hizmetlerin çoğu küçük bir miktar paraya mal olur. Örneğin, AWS KMS'de sakladığınız her anahtarın maliyeti ayda 1 ABD dolarıdır.
  • Secret yönetim uygulamalarını standartlaştırmak daha zordur. Farklı geliştiriciler veya ekipler, şifreleme anahtarlarını depolamak veya şifrelenmiş dosyaları yönetmek için farklı yollar kullanabilir ve şifrelemeyi doğru kullanmamak veya bir düz metin dosyasını yanlışlıkla sürüm kontrolüne eklemek gibi hatalar nispeten yaygındır.

3.2.3. Secret Store'lar

Üçüncü teknik, secretlarınızı merkezi bir secret mağazada saklamaya dayanır.

Genel bakış

Popüler secret mağazalardan bazıları AWS Secrets Manager, Google Secrets Manager, Azure Key Vault ve HashiCorp Vault'dur. AWS Secrets Manager'ı kullanan bir örneğe bakalım. İlk adım, veritabanı kimlik bilgilerinizi Şekil 2'de gösterildiği gibi AWS web konsolunu kullanarak yapabileceğiniz AWS Secrets Manager'da depolamaktır.

Şekil 2: AWS Secrets Manager'da secretları JSON biçiminde depolama

Şekil 2'deki secretın, AWS Secrets Manager'da veri depolamak için önerilen format olan JSON formatında olduğuna dikkat edin.

Sonraki adıma geçin ve secretlara Şekil 3'te gösterildiği gibi db-creds gibi benzersiz bir ad verdiğinizden emin olun.

Şekil 3: AWS Secrets Manager'da secreta benzersiz bir ad verme

Secretı kaydetmek için İleri ve Sakla'yı tıklayın. Şimdi, Terraform kodunuzda, db-creds sırrını okumak için aws_secretsmanager_secret_version veri kaynağını kullanabilirsiniz:

data "aws_secretsmanager_secret_version" "creds" {
  secret_id = "db-creds"
}

Secret JSON olarak depolandığından, JSON'u db_creds yerel değişkenine ayrıştırmak için jsondecode işlevini kullanabilirsiniz:

locals {
  db_creds = jsondecode(
    data.aws_secretsmanager_secret_version.creds.secret_string
  )
}

Ve şimdi db_creds'den veritabanı kimlik bilgilerini okuyabilir ve bunları aws_db_instance kaynağına iletebilirsiniz:

resource "aws_db_instance" "example" {
  identifier_prefix   = "terraform-up-and-running"
  engine              = "mysql"
  allocated_storage   = 10
  instance_class      = "db.t2.micro"
  skip_final_snapshot = true
  db_name             = var.db_name

  # Pass the secrets to the resource
  username = local.db_creds.username
  password = local.db_creds.password
}

Bu tekniğin avantajları

  • Plain text secretlar kodunuzdan ve sürüm kontrol sisteminizden uzak tutun.
  • Her şey kodun kendisinde tanımlanmıştır. Ekstra manuel adımlar veya sarmalayıcı komut dosyaları gerekmez.
  • Genellikle bir web kullanıcı arayüzü kullanabileceğiniz için secretları saklamak kolaydır.
  • Secret depolar genellikle secretların iptal edilmesini destekler; bu, bir sırrın ele geçirilmesi durumunda yararlıdır. Hatta önleyici bir önlem olarak rotasyonu programlanmış bir temelde (örneğin her 30 günde bir) etkinleştirebilirsiniz.
  • Secret depolar genellikle tam olarak kimin hangi verilere eriştiğini gösteren ayrıntılı denetim günlüklerini destekler.
  • Secret mağazalar, belirli şifreleme, depolama, erişim kalıpları vb. türlerini uyguladıkları için tüm secret uygulamalarınızı standartlaştırmayı kolaylaştırır.

Bu tekniğin dezavantajları

  • Secretlar için sürüm oluşturulmadığından, paketlenmediğinden ve kodunuzla test edilmediğinden, bir ortama yeni bir secret ekleme (ör. staging) ancak bunu başka bir ortama eklemeyi unutma (ör. production) gibi yapılandırma hataları daha olasıdır.
  • Çoğu yönetilen secret store paraya mal olur. Örneğin, AWS Secrets Manager, depoladığınız her bir secret için aylık 0,40 ABD doları ve ayrıca veri depolamak veya almak için yaptığınız her 10.000 API çağrısı için 0,05 ABD doları ücret alır.
  • HashiCorp Vault gibi kendi kendini yöneten bir secret store kullanıyorsanız, mağazayı çalıştırmak için para harcarsınız (ör. ve ekibinizin mağazayı dağıtması, yapılandırması, yönetmesi, güncellemesi ve izlemesi için)
  • Birden çok makine arasında güvenli kimlik doğrulamayı nasıl yapacağınızı çözmeniz gerektiğinden (ör: bir uygulama açılıyor ve bir veritabanı parolasını okumaya çalışıyor), özellikle otomatik ortamlarda secretları almak daha zordur.
  • Otomatik testlerle entegrasyon daha zordur, çünkü şu anda test ettiğiniz kodun çoğu, taklit edilmesi veya içinde test verilerinin depolanması gereken çalışan, harici bir sisteme bağlıdır.

3.3. State ve Plan Dosyaları

Terraform'u kullanırken secretlarla karşılaşacağınız iki yer daha vardır:

  • State dosyaları
  • Plan dosyalar

3.3.1. State Dosyaları

Umarız bu bölüm sizi secretlarınızı düz metin olarak saklamamaya ikna etmiş ve size daha iyi alternatifler sunmuştur. Ancak, birçok Terraform kullanıcısını hazırlıksız yakalayan bir şey, hangi tekniği kullanırsanız kullanın, Terraform kaynaklarınıza ve veri kaynaklarına ilettiğiniz tüm secretların Terraform state dosyanızda düz metin olarak sona ermesidir!

Örneğin, veritabanı kimlik bilgilerini (ortam değişkenleri, şifrelenmiş dosyalar, merkezi bir gizli depo) nereden okursanız okuyun, bu kimlik bilgilerini aws_db_instance gibi bir kaynağa iletirseniz:

resource "aws_db_instance" "example" {
  identifier_prefix   = "terraform-up-and-running"
  engine              = "mysql"
  allocated_storage   = 10
  instance_class      = "db.t2.micro"
  skip_final_snapshot = true
  db_name             = var.db_name

  # Pass the secrets to the resource
  username = local.db_creds.username
  password = local.db_creds.password
}

Ardından Terraform, bu kimlik bilgilerini terraform.tfstate dosyanızda düz metin olarak saklayacaktır. Bu, 8 yıldan uzun süredir açık bir konudur ve birinci sınıf bir çözüm için net bir plan yoktur. State dosyalarınızdaki secretları temizleyebilecek bazı geçici çözümler var, ancak bunlar kırılgandır ve her yeni Terraform sürümünde kırılma olasılığı yüksektir, bu yüzden bunları önermiyorum.

Şimdilik, secretları yönetmek için tartışılan tekniklerden hangisini kullanırsanız kullanın, aşağıdakileri yapmanız gerekir:

Terraform state'i şifrelemeyi destekleyen bir endpointte saklayın

State'i yerel bir terraform.tfstate dosyasında depolamak ve sürüm kontrolünde kontrol etmek yerine, S3, GCS ve Azure Blob Storage gibi yerel olarak şifrelemeyi destekleyen Terraform'un desteklediği endpointlerden birini kullanmalısınız. Bu endpointler, state dosyalarınızı hem aktarım sırasında (ör. TLS aracılığıyla) hem de diskte (ör. AES-256 aracılığıyla) şifreler.

Terraform endpointinize kimlerin erişebileceğini kesinlikle kontrol edin

Terraform state dosyaları secretlar içerebileceğinden, en az secretlara erişimi kontrol ettiğiniz kadar dikkatli bir şekilde endpointe  kimlerin erişebileceğini kontrol etmek isteyeceksiniz. Örneğin, endpoint olarak S3 kullanıyorsanız, production için yalnızca küçük bir avuç güvenilir geliştiriciye veya belki de yalnızca kullandığınız CI sunucusuna üretim için S3 klasörüne erişim sağlayan bir IAM ilkesi yapılandırmak isteyeceksiniz.

3.3.2. Plan Dosyaları

terraform plan komutunu birçok kez gördünüz. Henüz görmemiş olabileceğiniz bir özellik, plan komutunun (“diff”) çıktısını bir dosyada saklayabilmenizdir:

$ terraform plan -out=example.plan

Yukarıdaki komut, planı example.plan adlı bir dosyada saklar. Ardından, Terraform'un orijinal olarak gördüğünüz değişiklikleri tam olarak uyguladığından emin olmak için bu kayıtlı plan dosyasında apply komutunu çalıştırabilirsiniz:

$ terraform apply example.plan

Bu, Terraform'un kullanışlı bir özelliğidir, ancak önemli bir uyarı geçerlidir: tıpkı Terraform state'de olduğu gibi, Terraform kaynaklarınıza ve veri kaynaklarına ilettiğiniz tüm secretlar, Terraform plan dosyalarınızda düz metin olarak saklanacaktır! Örneğin, aws_db_instance kodunda plan çalıştırırsanız ve bir plan dosyası kaydettiyseniz, plan dosyası düz metin olarak veritabanı kullanıcı adını ve parolasını içerir.

Bu nedenle, plan dosyalarını kullanacaksanız aşağıdakileri yapmanız gerekir:

Terraform plan dosyalarınızı şifreleyin

Plan dosyalarınızı kaydedecekseniz, bu dosyaları hem aktarım sırasında (ör. TLS aracılığıyla) hem de diskte (ör. AES-256 aracılığıyla) şifrelemenin bir yolunu bulmanız gerekir. Örneğin, plan dosyalarını her iki şifreleme türünü de destekleyen bir S3 klasöründe saklayabilirsiniz.

Plan dosyalarınıza kimlerin erişebileceğini kesinlikle kontrol edin

Terraform plan dosyaları secretlar içerebileceğinden, bunlara kimlerin erişimi olduğunu kontrol etmek, en az secretlara erişimi kontrol ettiğiniz kadar dikkatli olmak isteyeceksiniz. Örneğin, plan dosyalarınızı depolamak için S3 kullanıyorsanız, küçük bir avuç güvenilir geliştiriciye üretim için yalnızca S3 kovasına veya belki de yalnızca yalnızca CI sunucusuna erişim sağlayan bir IAM politikası yapılandırmak isteyeceksiniz.

4. Özet

İşte bu bölümden önemli çıkarımlarınız:

İlk olarak, bu bölümden başka hiçbir şey hatırlamıyorsanız, lütfen şunu unutmayın: Secretları düz metin olarak saklamamalısınız.

İkinci olarak, secretları providerlara iletmek için insan kullanıcılarda kişisel secret yöneticilerini kullanabilir ve ortam değişkenlerini ayarlayabilir ve makine kullanıcılarda saklanan kimlik bilgilerini, IAM rollerini veya OIDC'yi kullanabilirsiniz. Makine kullanıcı seçenekleri arasındaki seçenekler için aşağıdaki tabloya bakabilirsiniz.

Saklanan Kimlik Bilgileri IAM Roller OIDC
Örnek CircleCI Jenkins on an EC2 instance GitHub Actions
Kimlik bilgilerini manuel olarak yönetmekten kaçınma x
Kalıcı kimlik bilgileri kullanmaktan kaçınma x
Bulut sağlayıcının içinde çalışır x x
Bulut sağlayıcısının dışında çalışır x
2022'den itibaren yaygın olarak destekleniyor x

Üçüncüsü, secretları kaynaklara ve veri kaynaklarına iletmek, ortam değişkenlerini, şifreli dosyaları veya merkezi secret depoları kullanmak. Bu farklı seçenekler arasındaki seçenekler için aşağıdaki tabloya bakabilirsiniz.

Ortam Değişkenleri Şifreli Dosyalar Merkezi Secret Mağazaları
Düz metin secretları kodun dışında tutar
Tüm secretlar Kod olarak tanımlanır ve yönetilir x
Şifreleme anahtarlarına erişim için log günlüğü x
Bireysel secretlara erişim için log günlüğü x x
Secretları değiştirmek veya iptal etmek kolaydır x x
Secret yönetimini standartlaştırmak kolaydır x x
Secretlarkodla sürümlendirilir x x
Secretları saklamak kolaydır x
Secretları almak kolaydır x
Otomatik testlerle entegrasyon kolaydır x x
Maliyet 0 $ $$$

Ve son olarak, dördüncüsü, secretları kaynaklara ve veri depolarına nasıl aktarırsanız aktarın, Terraform'un bu secretları state dosyalarınızda saklayacağını ve dosyaları düz metin olarak planlayacağını unutmayın, bu nedenle bu dosyaları her zaman şifrelediğinizden emin olun (aktarma ve bekleme sırasında) ve bunlara erişimi sıkı bir şekilde kontrol edin.

Artık Terraform ile çalışırken, secretları Terraform sağlayıcılarına nasıl güvenli bir şekilde ileteceğiniz de dahil olmak üzere secretları nasıl yöneteceğinizi anladığınıza göre, birden fazla sağlayıcınız olduğunda Terraform'u nasıl kullanacağınızı öğreneceğiniz diğer yazıya geçelim.