<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ascp &#8211; Blog of Kliment Andreev &#8211; A place so I won&#039;t forget things</title>
	<atom:link href="https://blog.andreev.it/tag/ascp/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.andreev.it</link>
	<description></description>
	<lastBuildDate>Wed, 03 Jan 2024 21:02:53 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>AWS: Get secrets from Secrets Manager using CSI Driver and ASCP</title>
		<link>https://blog.andreev.it/2024/01/aws-get-secrets-from-secrets-manager-using-csi-driver-and-ascp/</link>
					<comments>https://blog.andreev.it/2024/01/aws-get-secrets-from-secrets-manager-using-csi-driver-and-ascp/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Wed, 03 Jan 2024 21:02:53 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[ascp]]></category>
		<category><![CDATA[secrets manager]]></category>
		<category><![CDATA[secrets store csi driver]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=9718</guid>

					<description><![CDATA[In one of my previous posts, I wrote about accessing DynamoDB and AWS Gateway/Lambda&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>In one of my previous <a href="https://blog.andreev.it/2023/09/aws-access-dynamodb-and-secrets-manager-from-node-js-on-ec2-and-eks/" rel="noopener" target="_blank">posts</a>, I wrote about accessing DynamoDB and AWS Gateway/Lambda using IAM roles and secrets from EC2/EKS Node.js application. The idea is to reference passwords in your code and never store and secrets/passwords in the code. By using roles, we can access the secrets from the AWS Secrets Manager and use them dynamically in the code. However, that solution has its own pros and cons. The pros are that you don&#8217;t have to do anything extra except creating an IAM role and secrets. The cons are that you need AWS SDK as part of your application. You will use AWS SDK for Node.js to get the credentials from the Secrets Manager. This means you have to maintain up-to-date AWS SDK and add extra code. Another way is to use the k8s CSI driver and ASCP (provider) that pretty much gets the secret and mounts it as a file. You still need IAM roles to access the Secrets Manager but you don&#8217;t need AWS SDK. You can read the secrets as you read a file. </p>
<h1>Create an EKS cluster</h1>
<p>We&#8217;ll create an EKS cluster using the <strong>eksctl</strong> tool.</p>
<pre class="brush: bash; title: ; notranslate">
CLUSTER_NAME=&quot;eksDemoCluster&quot;
REGION=&quot;us-east-2&quot;
NAMESPACE=&quot;default&quot;

eksctl create cluster --name $CLUSTER_NAME \
  --region $REGION --version 1.28 \
  --node-type t3.small --nodes 2
</pre>
<p>We&#8217;ll use helm to install the CSI driver and the specific ASCP provider for AWS.</p>
<pre class="brush: bash; title: ; notranslate">
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm repo add aws-secrets-manager https://aws.github.io/secrets-store-csi-driver-provider-aws
helm repo update
helm install -n kube-system csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver
helm install -n kube-system secrets-provider-aws aws-secrets-manager/secrets-store-csi-driver-provider-aws
</pre>
<p>Check if everything is OK.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl --namespace=kube-system get pods -l &quot;app=secrets-store-csi-driver&quot;
NAME                                               READY   STATUS    RESTARTS   AGE
csi-secrets-store-secrets-store-csi-driver-nhq7b   3/3     Running   0          19s
csi-secrets-store-secrets-store-csi-driver-tft6q   3/3     Running   0          19s
</pre>
<h1>Create a secret</h1>
<p>Let&#8217;s create a secret that we&#8217;ll be using in a mock-up app.</p>
<pre class="brush: bash; highlight: [1,2,3,4,5]; title: ; notranslate">
SECRET_NAME=&quot;demoapp/mysecret&quot;
SECRET_ARN=$(aws secretsmanager create-secret  --name $SECRET_NAME \
  --secret-string &#039;{&quot;username&quot;:&quot;Administrator&quot;, &quot;password&quot;:&quot;SuperSecretPassword$&quot;}&#039; \
  --output text --query &#039;ARN&#039; --region $REGION)
echo $SECRET_ARN
arn:aws:secretsmanager:us-east-2:123456789012:secret:demoapp/mysecret-lboyfd
</pre>
<h1>Create an IAM policy</h1>
<p>We also need to define an IAM policy that will allow our EKS pods to access only this particular secret and nothing else.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; EOF &gt; polDemoSecret.json
{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: &#x5B; {
        &quot;Effect&quot;: &quot;Allow&quot;,
        &quot;Action&quot;: &#x5B;&quot;secretsmanager:GetSecretValue&quot;, &quot;secretsmanager:DescribeSecret&quot;],
        &quot;Resource&quot;: &#x5B;&quot;$SECRET_ARN&quot;]
    } ]
}
EOF
</pre>
<p>Create the policy.</p>
<pre class="brush: bash; highlight: [1,2,3,4]; title: ; notranslate">
POLICY_ARN=$(aws iam create-policy --policy-name polDemoSecret \
  --policy-document file://polDemoSecret.json \
  --output text --query &#039;Policy.Arn&#039;)
echo $POLICY_ARN
arn:aws:iam::123456789012:policy/polDemoSecret
</pre>
<h1>Create an IAM OIDC provider</h1>
<p>Let’s determine whether we have an existing IAM OIDC provider for our cluster.</p>
<pre class="brush: bash; highlight: [1,2]; title: ; notranslate">
aws eks describe-cluster --name $CLUSTER_NAME --region $REGION \
  --query &quot;cluster.identity.oidc.issuer&quot; --output text
https://oidc.eks.us-east-2.amazonaws.com/id/D7FAD6F2BEF430FC1CB673777A9E4FED
</pre>
<p>When we provisioned the cluster with eksctl, the OIDC was created automatically. We need the ID of the OIDC which is the hex value at the end.</p>
<pre class="brush: bash; highlight: [1,2,3]; title: ; notranslate">
OIDCID=$(aws eks describe-cluster --name $CLUSTER_NAME --region $REGION \
  --query &quot;cluster.identity.oidc.issuer&quot; --output text | cut -d &#039;/&#039; -f 5)
echo $OIDCID
E9B5E80C37A71FA3932B94EFF5BA3437
</pre>
<p>Check if the IAM OIDC provider is already configured. It shouldn’t if you provisioned a brand new cluster.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam list-open-id-connect-providers | grep $OIDCID 
</pre>
<p>If for whatever reason, you had an output from the command above, skip this <em>associate-iam-oidc-provider</em> step below.<br />
If you don&#8217;t have any output, associate the OIDC provider with the cluster.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
eksctl utils associate-iam-oidc-provider --cluster $CLUSTER_NAME --region $REGION --approve
2023-12-22 12:27:47 &#x5B;ℹ]  will create IAM Open ID Connect provider for cluster &quot;eksDemoCluster&quot; in &quot;us-east-2&quot;
2023-12-22 12:27:47 &#x5B;✔]  created IAM Open ID Connect provider for cluster &quot;eksDemoCluster&quot; in &quot;us-east-2&quot;
</pre>
<h1>Cluster service account</h1>
<p>We also need a service account for the cluster and we need to attache the policy for the secrets that we created before.</p>
<pre class="brush: bash; title: ; notranslate">
SA_NAME=&quot;sademosecret&quot;
eksctl create iamserviceaccount --name $SA_NAME --namespace $NAMESPACE \
--cluster $CLUSTER_NAME  --region $REGION \
--attach-policy-arn $POLICY_ARN --approve --override-existing-serviceaccounts
kubectl get sa
</pre>
<h1>Service Provider Class</h1>
<p>&#8230;and finally we need a service provider class that we&#8217;ll deploy on the EKS cluster.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; EOF &gt; spc.yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: spc-demosecret
  namespace: $NAMESPACE
spec:
  provider: aws
  parameters:
    objects: |
        - objectName: &quot;$SECRET_NAME&quot;
          objectType: &quot;secretsmanager&quot;
EOF
kubectl apply -f spc.yaml
</pre>
<h1>Test with busybox pod</h1>
<p>Create this YAML file that will deploy a pod. Pay attention to highlighted lines. We have to provide the namespace, the service account and the service provider class that specifies the secret.Check this <a href="https://docs.aws.amazon.com/secretsmanager/latest/userguide/integrating_csi_driver.html" rel="noopener" target="_blank">link</a> on how to mount and specify different secrets.</p>
<pre class="brush: bash; highlight: [6,8,15]; title: ; notranslate">
cat &lt;&lt; EOF &gt; app.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: $NAMESPACE
spec:
  serviceAccountName: $SA_NAME
  volumes:
  - name: secretsvolume
    csi:
       driver: secrets-store.csi.k8s.io
       readOnly: true
       volumeAttributes:
         secretProviderClass: &quot;spc-demosecret&quot;
  containers:
  - image: public.ecr.aws/docker/library/busybox:1.36
    command:
      - sleep
      - &quot;3600&quot;
    imagePullPolicy: IfNotPresent
    name: busybox
    volumeMounts:
    - name: secretsvolume
      mountPath: &quot;/mnt/secrets-store&quot;
      readOnly: true
  restartPolicy: Always
EOF
kubectl apply -f app.yaml
</pre>
<p>Test the secrets retrieval. As you can see, the secret that we created was <strong>demoapp/mysecret</strong>, but in this case the forward slash would mean a directory, so the actual secret is <strong>demoapp_mysecret</strong>&#8230; and it&#8217;s simple as that. The secret is just a file under the <em>/mnt/secrets-store</em> directory.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl exec -it $(kubectl get pods | awk &#039;/busybox/{print $1}&#039; | head -1) -- cat /mnt/secrets-store/demoapp_mysecret; echo
{&quot;username&quot;:&quot;Administrator&quot;, &quot;password&quot;:&quot;SuperSecretPassword$&quot;}
</pre>
<h1>Node.js app</h1>
<p>Create a folder and add this file. Save it as <strong>server.js</strong>.</p>
<pre class="brush: bash; highlight: [8]; title: ; notranslate">
&#039;use strict&#039;;
const express = require(&#039;express&#039;)
const app = express()
const port = 3000
 
var fs = require(&#039;fs&#039;);
try {  
    var data = fs.readFileSync(&#039;/mnt/secrets-store/demoapp_mysecret&#039;, &#039;utf8&#039;);
    console.log(data.toString());    
} catch(e) {
    console.log(&#039;Error:&#039;, e.stack);
}

app.get(&#039;/&#039;, (req, res) =&gt; {
    res.send(JSON.stringify(data));
})
 
app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`)
})
</pre>
<p>Let&#8217;s create the <strong>Dockerfile</strong> so we can build the image.</p>
<pre class="brush: bash; title: ; notranslate">
FROM node:20
  
# Create app directory
WORKDIR /usr/src/app
  
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
  
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
  
# Bundle app source
COPY . .
  
EXPOSE 3000
CMD &#x5B; &quot;node&quot;, &quot;server.js&quot; ]
</pre>
<p>Create a <strong>.dockerignore</strong> file.</p>
<pre class="brush: bash; title: ; notranslate">
node_modules
npm-debug.log
</pre>
<p>Now, we can create the image and push to Dockerhub or some other image repo. Use your own repo here at line 1.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
sudo docker build . -t klimenta/secretsdemo
sudo docker images
sudo docker push klimenta/secretsdemo
</pre>
<p>Create a file to deploy 2 replicas and a load balancer. Save it as something.yaml and deploy it. Analyze the file and see how we use the service account, secret provider class and the image.</p>
<pre class="brush: bash; title: ; notranslate">
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demosecret
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      run: demosecret
  template:
    metadata:
      labels:
        run: demosecret
    spec:
      serviceAccountName: sademosecret
      volumes:
      - name: secretsvolume
        csi:
          driver: secrets-store.csi.k8s.io
          readOnly: true
          volumeAttributes:
            secretProviderClass: &quot;spc-demosecret&quot;
      containers:
      - name: demosecret
        image: klimenta/secretsdemo
        volumeMounts:
        - name: secretsvolume
          mountPath: &quot;/mnt/secrets-store&quot;
          readOnly: true
        ports:
        - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: loadbalancer
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: nlb
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
  ports:
    - port: 80
      targetPort: 3000
      protocol: TCP
  type: LoadBalancer
  selector:
    run: demosecret
</pre>
<p>Once you deploy with <em>kubectl apply -f something.yaml</em>, type <em>kubectl get svc</em> and you&#8217;ll see a public URL.<br />
If you go to that URL, you&#8217;ll see our secret.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/01/P169-01.png"><img fetchpriority="high" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-1024x123.png" alt="" width="1024" height="123" class="aligncenter size-large wp-image-9746" srcset="https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-1024x123.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-300x36.png 300w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-768x92.png 768w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-1536x185.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-1170x141.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-585x70.png 585w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01.png 1812w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2024/01/aws-get-secrets-from-secrets-manager-using-csi-driver-and-ascp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
