<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jonathan Wong</title>
    <description>The latest articles on DEV Community by Jonathan Wong (@jonathan78wong).</description>
    <link>https://dev.to/jonathan78wong</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3871202%2F343e4457-180b-4eab-af90-04c5935b3567.jpg</url>
      <title>DEV Community: Jonathan Wong</title>
      <link>https://dev.to/jonathan78wong</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jonathan78wong"/>
    <language>en</language>
    <item>
      <title>IEC BC x ISACA Vancouver Cybersecurity Networking Event</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Thu, 30 Apr 2026 23:04:57 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/iec-bc-x-isaca-vancouver-cybersecurity-networking-event-nml</link>
      <guid>https://dev.to/jonathan78wong/iec-bc-x-isaca-vancouver-cybersecurity-networking-event-nml</guid>
      <description>&lt;p&gt;At the IEC BC x ISACA Vancouver Cybersecurity Networking Event this week, newcomers, experienced professionals, and employers had the chance to connect meaningfully. Thank you to &lt;a href="https://www.linkedin.com/company/iecbc/" rel="noopener noreferrer"&gt;Immigrant Employment Council of BC&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/isaca-vancouver-chapter/" rel="noopener noreferrer"&gt;ISACA Vancouver Chapter&lt;/a&gt; and &lt;a href="https://www.linkedin.com/company/vancouver-community-college/" rel="noopener noreferrer"&gt;Vancouver Community College (VCC)&lt;/a&gt; for making this possible.  &lt;/p&gt;

&lt;p&gt;The speaker shared a clear breakdown of certification pathways in cybersecurity.  &lt;/p&gt;

&lt;p&gt;Foundational certifications include ISC2 Certified in Cybersecurity (CC).&lt;br&gt;&lt;br&gt;
Intermediate tracks include CISA, CCSP, and AWS Security.&lt;br&gt;&lt;br&gt;
Senior level designations include CISSP and CISM, which reflect broader responsibility across governance, architecture, and organizational risk.  &lt;/p&gt;

&lt;p&gt;The speaker also highlighted the challenges overseas trained professionals face in the Vancouver job market. Many arrive with strong technical backgrounds, yet still need to navigate local hiring expectations, credential recognition, and the persistent “Canadian experience” requirement. Hearing this acknowledged openly was valuable for many attendees.  &lt;/p&gt;

&lt;p&gt;Another insight from the talk focused on how companies are adopting AI inside their organizational structure. The risk is not only technical. For SMEs with fewer than 50 employees, restructuring teams too quickly around AI can create instability and unclear ownership.  &lt;/p&gt;

&lt;p&gt;One final note for anyone exploring cybersecurity.&lt;br&gt;&lt;br&gt;
The free ISC2 Certified in Cybersecurity (CC) enrollment ends on May 20, 2026.  &lt;/p&gt;

&lt;p&gt;If you are considering a first step into the field, this is a good opportunity before the window closes.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Jonathan Wong&lt;/em&gt; is an IT and AI consultant with 20+ years of experience leading engineering teams across Vancouver and Hong Kong. He specializes in modernizing legacy platforms, cloud security, and building AI-ready systems for startups and large enterprises while advising leadership on using strategic technology to drive business growth.&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://www.linkedin.com/in/jonanata/" rel="noopener noreferrer"&gt;Connect with me on LinkedIn&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/iec-bc-x-isaca-vancouver-cybersecurity-networking-event/" rel="noopener noreferrer"&gt;IEC BC x ISACA Vancouver Cybersecurity Networking Event&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>networking</category>
    </item>
    <item>
      <title>Sakura Migration   </title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Sun, 26 Apr 2026 05:03:15 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/sakura-migration-m</link>
      <guid>https://dev.to/jonathan78wong/sakura-migration-m</guid>
      <description>&lt;p&gt;Not long ago I visited UBC, where the Sakura blossoms were in full bloom again.&lt;/p&gt;

&lt;p&gt;It reminded me of the first time I saw them decades ago in Japan, when I was working inside a 200‑person IT department. That was the on‑prem era. Everything lived in racks and server rooms. Everything felt stable, predictable, and physical. I thought my career would stay that way.&lt;/p&gt;

&lt;p&gt;But life moves. Sometimes quietly. Sometimes without a plan.&lt;/p&gt;

&lt;p&gt;Later I met Notey, and that was the beginning of my startup journey. I moved from enterprise structure to startup speed. From on‑prem systems to the cloud. From a single role to wearing multiple hats. That was my first migration, not geographical but mental. A shift in how I saw technology, teams, and myself. &lt;/p&gt;

&lt;p&gt;I moved to Boxful next. Built an MVP that helped secure funding. Shifted from development to product. From proprietary stacks to open source. From execution to ownership. Another migration. Another environment. Another version of myself.&lt;/p&gt;

&lt;p&gt;After that chapter, I joined venture builders, Flatiron, helping capital create new startups. Moving from building one company to helping many. From operator to enabler. From solving problems to designing systems that solve problems. Another migration.&lt;/p&gt;

&lt;p&gt;Eventually, the Startup Visa program brought me to Vancouver. From East Asia to North America. From familiar ground to a new ecosystem. From the world I grew up into the world I chose. A migration across continents, but also across identity.&lt;/p&gt;

&lt;p&gt;Walking past the students on UBC’s campus takes me back to when I was an undergraduate, excited about my first Yahoo email. At the time, I believed I would be happy coding every day. But the world kept shifting. We moved from desktop to cloud, then to mobile, and now to AI, where skills can be downloaded and coding itself is being redefined. Another migration. Another environment. Another identity.&lt;/p&gt;

&lt;p&gt;Looking back, none of these migrations were planned. They happened one step at a time. Each one pulled me into a new environment and forced me to grow in ways I never anticipated.&lt;/p&gt;

&lt;p&gt;There were migrations in scale. Migrations in technology. Migrations in geography. Migrations in identity.&lt;/p&gt;

&lt;p&gt;Those who pause beneath the Sakura on UBC’s Memorial Road often say the trees were brought from Japan and replanted here. They blossom every year in a way you cannot find anywhere else. They did not choose the journey, yet they grew. And they became something unique because of the journey, not in spite of it.&lt;/p&gt;

&lt;p&gt;Are you someone shaped by migrations. &lt;/p&gt;

&lt;p&gt;What has been your latest migration.   &lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/sakura-migration/" rel="noopener noreferrer"&gt;Sakura Migration   &lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>lifestyle</category>
    </item>
    <item>
      <title>The Modernization Journey: How to Take a Legacy System From Zero Trust to AI Ready Without Rewrites or Downtime and Big Costs</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Fri, 24 Apr 2026 22:58:21 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/the-modernization-journey-how-to-take-a-legacy-system-from-zero-trust-to-ai-ready-without-rewrites-486j</link>
      <guid>https://dev.to/jonathan78wong/the-modernization-journey-how-to-take-a-legacy-system-from-zero-trust-to-ai-ready-without-rewrites-486j</guid>
      <description>&lt;p&gt;Modernization is often misunderstood. Many organizations believe it requires rewriting their legacy systems, replacing entire architectures, or enduring long periods of downtime. In reality, modernization is a sequence. It begins with security, continues with infrastructure uplift, and ends with AI powered capabilities that operate safely around the legacy core.&lt;/p&gt;

&lt;p&gt;This journey illustrates how we helped a client transform a fragile legacy PHP system into a secure, compliant, AI ready platform without rewriting the application and without interrupting production. The journey is documented across a series of articles that cover the business perspective, the technical deep dives, and the AI implementation. This final piece brings the entire story together.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Project Background: Why Modernization Became Mandatory&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The client wanted to elevate their product and expand into new regulated markets. To do that, they needed to meet compliance standards such as SOC 2 and PCI DSS. These frameworks require strict identity controls, auditability, and a security posture that legacy systems rarely provide. Compliance was not a technical preference. It was a business requirement for entering new industries.&lt;/p&gt;

&lt;p&gt;At the same time, the client wanted to introduce AI powered features into their product. They envisioned multilingual document understanding, intelligent automation, and natural language interfaces. But AI cannot be safely added to a system that lacks identity boundaries, secure data flows, or modern compute layers. Without the right foundation, AI becomes a risk multiplier.&lt;/p&gt;

&lt;p&gt;This created a clear sequence. Secure the current system. Achieve compliance readiness. Modernize the environment. Prepare the architecture for AI. Then introduce AI features safely. This is why the project began with Zero Trust rather than AI.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Business Case&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The business perspective behind this transformation is detailed in the article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.jonanata.com/how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-or-big-costs/" rel="noopener noreferrer"&gt;How I Delivered Zero Trust Security for a Client’s Legacy PHP System Without Rewrites, Downtime, or Big Costs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core challenge was simple. The legacy system was too critical to rewrite and too fragile to modify. It supported daily operations and revenue generating workflows. A rewrite would introduce risk, cost, and uncertainty. A multi-year migration could easily fail. Even a successful rewrite could disrupt the business.&lt;/p&gt;

&lt;p&gt;The constraints were clear: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No application revamp &lt;/li&gt;
&lt;li&gt;No downtime&lt;/li&gt;
&lt;li&gt;Free or low‑cost solutions only &lt;/li&gt;
&lt;li&gt;Compliance‑aligned security improvements&lt;/li&gt;
&lt;li&gt;Immediate business value &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The smarter approach was to modernize around the legacy system. Strengthen the environment. Improve the security posture. Introduce modern cloud capabilities. Build new features outside the legacy core. This approach delivered value quickly and reduced risk. It also created a path where AI could be added safely and incrementally.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Zero Trust Foundation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Zero Trust foundation became the anchor of the entire transformation. It provided the identity first model required for SOC 2 and PCI DSS. It removed public exposure. It enforced access boundaries. It created a secure perimeter around the legacy system without modifying the application code.&lt;/p&gt;

&lt;p&gt;The technical implementation is documented in two deep dive articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.jonanata.com/technical-deep-dive-how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-or-big-costs-part-1/" rel="noopener noreferrer"&gt;Technical Deep Dive: How I Delivered Zero Trust Security for a Client’s Legacy PHP System Without Rewrites, Downtime, or Big Costs Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.jonanata.com/technical-deep-dive-how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-or-big-costs-part-2/" rel="noopener noreferrer"&gt;Technical Deep Dive: How I Delivered Zero Trust Security for a Client’s Legacy PHP System Without Rewrites, Downtime, or Big Costs Part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The foundation included VPC only networking, IAM based access to RDS, S3, and CloudWatch, passwordless authentication, and a hardened runtime. Every component was isolated. Every request was authenticated. Every action was logged. The system became secure by design.&lt;/p&gt;

&lt;p&gt;This foundation made compliance achievable and created the conditions required for safe modernization.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Modernization Path&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the security perimeter was in place, we modernized the environment around the legacy system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application code remained untouched. Instead, we uplifted the infrastructure into modern cloud primitives. &lt;/li&gt;
&lt;li&gt;Introduced observability, identity enforcement, and automation. &lt;/li&gt;
&lt;li&gt;Replaced brittle components with managed services. &lt;/li&gt;
&lt;li&gt;Created a modernization perimeter that isolated risk and allowed new capabilities to be added without affecting production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach delivered immediate improvements. The system became more stable. Operations became more predictable. Compliance became measurable. And the environment became ready for the next stage.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The AI Ready Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI readiness is not about adding a model. It is about preparing the system to support AI safely. That requires an architecture that is event driven, API first, identity enforced, and capable of handling structured and unstructured data.&lt;/p&gt;

&lt;p&gt;However there was a major constraint: the client’s primary business entity is registered in an  &lt;strong&gt;unsupported region&lt;/strong&gt;  for these advanced AI models. The only viable path was to leverage their overseas entity to create a new AWS account in a supported region and integrate it with their existing environment.&lt;/p&gt;

&lt;p&gt;The constraints were non‑negotiable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multilingual inference&lt;/li&gt;
&lt;li&gt;multi‑modal document processing&lt;/li&gt;
&lt;li&gt;cross‑account AWS integration&lt;/li&gt;
&lt;li&gt;cross‑region invocation&lt;/li&gt;
&lt;li&gt;access to advanced LLMs such as Claude&lt;/li&gt;
&lt;li&gt;strong security controls for sensitive customer data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We introduced &lt;strong&gt;two‑sided trust‑granting model&lt;/strong&gt; and &lt;strong&gt;network isolation&lt;/strong&gt;  to secure APIs and data pipelines that could feed Bedrock or other models. We ensured that every AI workflow operated within the Zero Trust perimeter. The result was an architecture that could support AI features without exposing the legacy system to new risks.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Lambda and Bedrock Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The AI execution layer is documented in the article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.jonanata.com/how-i-delivered-a-cross-account-cross-region-multilingual-multi-modal-aws-bedrock-solution-in-a-zero-trust-environment/" rel="noopener noreferrer"&gt;How I Delivered a Cross Account Cross Region Multilingual Multi Modal AWS Bedrock Solution in a Zero Trust Environment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lambda act as a &lt;strong&gt;region proxy&lt;/strong&gt;. Bedrock provided secure enterprise grade AI capabilities. Together, they enabled new features without touching the legacy code.&lt;/p&gt;

&lt;p&gt;We implemented multilingual understanding, multimodal document analysis, workflow automation, and intelligent assistants. The legacy system remained stable. The AI layer delivered new value. The business moved forward without risk.&lt;/p&gt;

&lt;p&gt;The detailed implementation of the AI pipeline is documented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.jonanata.com/building-a-multilingual-multi-modal-document-analysis-pipeline-with-aws-lambda-and-claude-sonnet-4-6/" rel="noopener noreferrer"&gt;Building a Multilingual Multi Modal Document Analysis Pipeline with AWS Lambda and Claude Sonnet 4.6&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article explains how the multilingual and multimodal capabilities were orchestrated using Lambda, Bedrock, and secure data flows inside the Zero Trust perimeter.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Outcomes and Lessons Learned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The transformation delivered measurable results.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client achieved a compliance ready security posture &lt;/li&gt;
&lt;li&gt;The system became more stable and more observable &lt;/li&gt;
&lt;li&gt;The legacy system remained untouched &lt;/li&gt;
&lt;li&gt;No rewrite, no downtime, no big costs &lt;/li&gt;
&lt;li&gt;The business expanded into new regulated markets &lt;/li&gt;
&lt;li&gt;The product gained new AI capabilities &lt;/li&gt;
&lt;li&gt;The entire journey followed a repeatable sequence that can be applied to any legacy environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core lesson is simple. Modernization is not a single project. It is a sequence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure first &lt;/li&gt;
&lt;li&gt;Modernize the environment &lt;/li&gt;
&lt;li&gt;Prepare the architecture &lt;/li&gt;
&lt;li&gt;Add AI safely &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach reduces risk, accelerates delivery, and creates long term value.&lt;/p&gt;

&lt;p&gt;Modernization is not a rewrite. It is a journey. And when executed in the right order, it turns a legacy system into an AI ready platform without disruption.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Jonathan Wong&lt;/em&gt; is an IT and AI consultant with 20+ years of experience leading engineering teams across Vancouver and Hong Kong. He specializes in modernizing legacy platforms, cloud security, and building AI-ready systems for startups and large enterprises while advising leadership on using strategic technology to drive business growth.&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://www.linkedin.com/in/jonanata/" rel="noopener noreferrer"&gt;Connect with me on LinkedIn&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/the-modernization-journey-how-to-take-a-legacy-system-from-zero-trust-to-ai-ready-without-rewrites-or-downtime-and-big-costs/" rel="noopener noreferrer"&gt;The Modernization Journey: How to Take a Legacy System From Zero Trust to AI Ready Without Rewrites or Downtime and Big Costs&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>cybersecurity</category>
      <category>zerotrust</category>
    </item>
    <item>
      <title>Building a Multilingual, Multi‑Modal Document Analysis Pipeline with AWS Lambda and Claude Sonnet 4.6</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Wed, 22 Apr 2026 20:16:46 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/building-a-multilingual-multi-modal-document-analysis-pipeline-with-aws-lambda-and-claude-sonnet-4dp6</link>
      <guid>https://dev.to/jonathan78wong/building-a-multilingual-multi-modal-document-analysis-pipeline-with-aws-lambda-and-claude-sonnet-4dp6</guid>
      <description>&lt;p&gt;In my last article &lt;a href="https://blog.jonanata.com/how-i-delivered-a-cross-account-cross-region-multilingual-multi-modal-aws-bedrock-solution-in-a-zero-trust-environment/" rel="noopener noreferrer"&gt;&lt;strong&gt;How I Delivered a Cross‑Account, Cross‑Region, Multilingual, Multi‑Modal AWS Bedrock Solution in a Zero Trust Environment&lt;/strong&gt;&lt;/a&gt;, I walked through the architecture that makes secure, multilingual, multi‑modal AI processing possible across AWS accounts and regions. That foundation solved the infrastructure challenge, but it left one critical question unanswered: &lt;strong&gt;how does the system actually understand real documents?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;This article picks up exactly where the last one ended. Now that the pipeline is built, it’s time to open the black box and examine the Lambda Python function that turns raw files into structured, multilingual insights. This is where OCR, model selection, prompt engineering, and Claude Sonnet 4.6 all come together to form the intelligence layer of the entire solution.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Selecting the Right LLM Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core requirement was clear: the model must handle &lt;strong&gt;multilingual content (English + Chinese)&lt;/strong&gt; and &lt;strong&gt;multi‑modal inputs&lt;/strong&gt; including text files, PDFs, and images. Several AWS‑native and third‑party models were evaluated.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evaluation Results&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Textract + Titan&lt;/strong&gt; performs OCR well for English, but Titan consistently &lt;strong&gt;ignored Chinese content&lt;/strong&gt;, making it unsuitable for bilingual documents.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Amazon Nova&lt;/strong&gt; attempted to interpret Chinese text but produced &lt;strong&gt;random guesses&lt;/strong&gt; with a very low success rate.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Qwen3&lt;/strong&gt; handled Chinese better, with an acceptable success rate for mixed‑language documents.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Claude Sonnet 4.6&lt;/strong&gt; delivered &lt;strong&gt;5× higher accuracy than Qwen3&lt;/strong&gt; while also offering &lt;strong&gt;lower total cost&lt;/strong&gt;. It consistently extracted structured fields correctly across English‑only, Chinese‑only, and mixed‑language documents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final Decision&lt;/strong&gt;  &lt;br&gt;&lt;strong&gt;Claude Sonnet 4.6 was selected&lt;/strong&gt; due to its superior multilingual reasoning, stable multi‑modal performance, and cost efficiency.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Activating Claude Sonnet 4.6 in AWS Bedrock&lt;/strong&gt;  &lt;br&gt;Before the Lambda function can invoke Claude, the model must be activated in the AWS account.&lt;/p&gt;

&lt;p&gt;Step 1: Submit the Use Case &lt;br&gt;Submit the required use case form in the Bedrock console. Approval typically takes &lt;strong&gt;15–30 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Step 2: Test in the Model Playground &lt;br&gt;Run a first inference in the Claude Sonnet 4.6 playground to confirm access.&lt;/p&gt;

&lt;p&gt;Step 3: Resolve Marketplace Permission Errors  &lt;br&gt;Some accounts encounter the following error during the first invocation: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;em&gt;“Model access is denied due to IAM user or service role not authorized to perform the required AWS Marketplace actions (aws-marketplace:ViewSubscriptions, aws-marketplace:Subscribe)...”&lt;/em&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To resolve this, attach the following IAM policy to the IAM user or role:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "Effect": "Allow",
  "Action": [
    "aws-marketplace:Subscribe",
    "aws-marketplace:ViewSubscriptions"
  ],
  "Resource": "*"
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After applying the policy, retry the model activation. Once the subscription completes, the Lambda function can invoke Claude normally.  &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Lambda Function Architecture&lt;/strong&gt;  &lt;br&gt;The Lambda function is the operational heart of the pipeline. It performs ingestion, OCR, reasoning, and structured extraction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handler&lt;/strong&gt;  &lt;br&gt;The handler contains the main execution logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Receive request payload from the EC2 caller&lt;/li&gt;



&lt;li&gt;Download the uploaded file from the S3 bucket&lt;/li&gt;



&lt;li&gt;Perform OCR text extraction&lt;/li&gt;



&lt;li&gt;Invoke Claude Sonnet 4.6 for reasoning and field extraction&lt;/li&gt;



&lt;li&gt;Clean and normalize the output&lt;/li&gt;



&lt;li&gt;Return the structured JSON result back to the EC2 instance  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Claude handles PDFs and images differently during OCR and target‑field extraction, so the Lambda logic must normalize and clean the model’s output to ensure consistent, structured results:  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# s3_doc_in_bytes , the s3 document as bytes format  
# content_type , the content type matched by s3 document extension  

# covert the bytes to Claude supported base64 type  
doc_base64 = base64.standard_b64encode(s3_doc_in_bytes).decode("utf-8")

# Build the content block based on document type
if content_type == "application/pdf":
    doc_config = {
    "type": "document",
    "source": {
        "type": "base64",
        "media_type": content_type,
        "data": doc_base64,
    },
    }
else:
    doc_config = {
    "type": "image",
    "source": {
        "type": "base64",
        "media_type": content_type,
        "data": doc_base64,
    },
    }

request_body = {
    "anthropic_version": "bedrock-2023-05-31",
    "max_tokens": 4096,
    "temperature": 0,  # no creative answer  
    "system": OCR_PROMPT,
    "messages": [
    {
        "role": "user",
        "content": [
            doc_config,
            {"type": "text", "text": TARGET_FIELD_PROMPT},
        ],
    },
    ],
}

response = bedrock_client.invoke_model(
    modelId=BEDROCK_MODEL_ID,
    contentType="application/json",
    accept="application/json",
    body=json.dumps(request_body),
)

response_body = json.loads(response["body"].read())
assistant_text = response_body["content"][0]["text"]

# remove any markdown code fences from Claude answer, if present
cleaned = assistant_text.strip()
if cleaned.startswith("

```"):
    cleaned = cleaned.split("\n", 1)[1]
if cleaned.endswith("```

"):
    cleaned = cleaned.rsplit("

```

", 1)[0]
cleaned = cleaned.strip()

# other logic  
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Prompts&lt;/strong&gt;  &lt;br&gt;Two categories of prompts guide the LLM’s behavior.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OCR Prompt&lt;/strong&gt; Defines the system role, extraction rules, and reasoning instructions. It explains how the LLM should interpret different document scenarios.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Target Field Prompt&lt;/strong&gt; Defines how to handle different file types (PDF, image, text). Provides a list of target fields with descriptions and common pattern multilingual examples, like:   &lt;code&gt;| total_deposit | Total deposit collected in HKD (numeric) — include ALL deposits: deposit (按金), electricity deposit (電費按金), renovation deposit, etc. Use the grand total from the receipt/table if available. |&lt;/code&gt;. Specifies strict output format rules such as:
&lt;ul&gt;
&lt;li&gt;no markdown&lt;/li&gt;



&lt;li&gt;no explanation&lt;/li&gt;



&lt;li&gt;return &lt;strong&gt;only&lt;/strong&gt; a JSON object&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;/ul&gt;

&lt;p&gt;These prompts ensure deterministic, repeatable extraction across diverse document types.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Deploying the Lambda Function&lt;/strong&gt;  &lt;br&gt;Deployment is straightforward but requires attention to packaging: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zip &lt;strong&gt;only the Python files&lt;/strong&gt;, not the parent folder.&lt;/li&gt;



&lt;li&gt;The zip file must contain the &lt;code&gt;.py&lt;/code&gt; files at the root level.  &lt;/li&gt;



&lt;li&gt;Use the &lt;strong&gt;Upload ZIP&lt;/strong&gt; button in the Lambda console.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Configuring the Handler&lt;/strong&gt;  &lt;br&gt;In the Lambda configuration tab, set the handler to match your file and function name. For example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;document_handler.handler  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;document_handler.py&lt;/code&gt; is the file&lt;/li&gt;



&lt;li&gt;
&lt;code&gt;handler&lt;/code&gt; is the function inside that file&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Closing Thoughts  &lt;/h2&gt;

&lt;p&gt;This architecture demonstrates how to build a multilingual, multi‑modal document analysis pipeline using AWS Lambda and Claude Sonnet 4.6. The key is not just choosing the right model, but designing the prompts, IAM permissions, and Lambda workflow so the system behaves predictably under real production workloads.  &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;br&gt;&lt;em&gt;Jonathan Wong&lt;/em&gt; is an IT and AI consultant with 20+ years of experience leading engineering teams across Vancouver and Hong Kong. He specializes in modernizing legacy platforms, cloud security, and building AI-ready systems for startups and large enterprises while advising leadership on using strategic technology to drive business growth. &lt;br&gt;&lt;em&gt;&lt;a href="https://www.linkedin.com/in/jonanata/" rel="noopener noreferrer"&gt;Connect with me on LinkedIn&lt;/a&gt;&lt;/em&gt;  &lt;/p&gt;







&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/building-a-multilingual-multi-modal-document-analysis-pipeline-with-aws-lambda-and-claude-sonnet-4-6/" rel="noopener noreferrer"&gt;Building a Multilingual, Multi‑Modal Document Analysis Pipeline with AWS Lambda and Claude Sonnet 4.6&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
    </item>
    <item>
      <title>Inside TECHSPO Vancouver 2026 at Paradox Vancouver — Innovation, Community, and Real Conversations</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Tue, 21 Apr 2026 01:00:30 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/inside-techspo-vancouver-2026-at-paradox-vancouver-innovation-community-and-real-conversations-e9n</link>
      <guid>https://dev.to/jonathan78wong/inside-techspo-vancouver-2026-at-paradox-vancouver-innovation-community-and-real-conversations-e9n</guid>
      <description>&lt;p&gt;This year’s &lt;strong&gt;&lt;a href="https://techspovancouver.ca/" rel="noopener noreferrer"&gt;TECHSPO Vancouver 2026&lt;/a&gt;&lt;/strong&gt;, hosted at the Paradox Vancouver, brought together innovators, founders, engineers, and creators across the tech spectrum, with the venue’s modern, design‑driven environment providing the perfect backdrop for two days of meaningful conversations and emerging‑tech exploration.&lt;/p&gt;

&lt;p&gt;TECHSPO showcased a diverse range of companies across &lt;strong&gt;iCRM&lt;/strong&gt; , &lt;strong&gt;MarTech&lt;/strong&gt; , &lt;strong&gt;AI&lt;/strong&gt; , &lt;strong&gt;SaaS&lt;/strong&gt; , and even &lt;strong&gt;mental‑health technology&lt;/strong&gt; , creating an experience built for hands‑on demos, high‑value networking, and discovering the next wave of digital innovation.&lt;/p&gt;




&lt;p&gt;The hotel’s atmosphere, shaped by modern architecture, clean lines, and a calm yet energetic vibe, created a perfect setting for focused conversations and networking.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Meaningful Connections Throughout the Event&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reconnected with my friend Maksym&lt;/strong&gt; , a hardware professional I first met at the Vancouver Careerin Coffee Meetup. He’s one of the rare engineers who can move seamlessly from &lt;strong&gt;low‑level parts and protocol discussions&lt;/strong&gt; to &lt;strong&gt;market intelligence&lt;/strong&gt;. His insights always sharpen my thinking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Met Layth&lt;/strong&gt; , a cybersecurity professional with real‑world threat experience. Our conversation covered:

&lt;ul&gt;
&lt;li&gt;A real supply‑chain attack case&lt;/li&gt;
&lt;li&gt;How fake network infrastructures deceive internal teams&lt;/li&gt;
&lt;li&gt;The rising importance of &lt;strong&gt;AI model protection&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Connected with startup founders&lt;/strong&gt; , including &lt;strong&gt;Anil&lt;/strong&gt; , who is building an AI‑agent service. It’s energizing to see founders experimenting with automation, agentic workflows, and new business models.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Met new workforce talent&lt;/strong&gt; , like &lt;strong&gt;Betty&lt;/strong&gt; , an Intermediate Software Engineer open to work and passionate about &lt;strong&gt;accessibility‑first design&lt;/strong&gt;. Her enthusiasm for inclusive product development was refreshing and aligned with where modern product expectations are heading. &lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;Vancouver’s tech ecosystem is clearly accelerating, with momentum spanning AI agents, cybersecurity, and mental‑health technology, and what stood out most throughout TECHSPO was the depth of talent across the community, from experienced founders to early‑career engineers, all building with purpose and pushing the region’s innovation forward.&lt;/p&gt;

&lt;p&gt;If you attended TECHSPO Vancouver as well, I’d love to hear what conversations or trends stood out to you.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Jonathan Wong&lt;/em&gt; is an IT and AI consultant with 20+ years of experience leading engineering teams across Vancouver and Hong Kong. He specializes in modernizing legacy platforms, cloud security, and building AI-ready systems for startups and large enterprises while advising leadership on using strategic technology to drive business growth.&lt;br&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://www.linkedin.com/in/jonanata/" rel="noopener noreferrer"&gt;Connect with me on LinkedIn&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/inside-techspo-vancouver-2026-at-paradox-vancouver-innovation-community-and-real-conversations/" rel="noopener noreferrer"&gt;Inside TECHSPO Vancouver 2026 at Paradox Vancouver — Innovation, Community, and Real Conversations&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>networking</category>
    </item>
    <item>
      <title>How I Delivered a Cross‑Account, Cross‑Region, Multilingual, Multi‑Modal AWS Bedrock Solution in a Zero Trust Environment</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Mon, 20 Apr 2026 05:47:25 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/how-i-delivered-a-cross-account-cross-region-multilingual-multi-modal-aws-bedrock-solution-in-a-26a8</link>
      <guid>https://dev.to/jonathan78wong/how-i-delivered-a-cross-account-cross-region-multilingual-multi-modal-aws-bedrock-solution-in-a-26a8</guid>
      <description>&lt;p&gt;After I modernized my client’s platform security, the next strategic move was clear: elevate their product with generative AI. They wanted an LLM‑powered component capable of analyzing customer‑submitted documents during application intake and performing price forecasting. The goal was to dramatically improve user experience by streamlining the workflow, offering proactive suggestions, and providing real‑time assistance.&lt;/p&gt;

&lt;p&gt;To deliver this, the AI analyst feature needed &lt;strong&gt;multilingual support&lt;/strong&gt; , &lt;strong&gt;multi‑modal document understanding&lt;/strong&gt; (images, PDFs, text files, and more), and &lt;strong&gt;advanced reasoning&lt;/strong&gt; , which pointed directly to models like Claude. But there was a major constraint: the client’s primary business entity is registered in an &lt;strong&gt;unsupported region&lt;/strong&gt; for these advanced AI models. The only viable path was to leverage their overseas entity to create a new AWS account in a supported region and integrate it with their existing environment.&lt;/p&gt;

&lt;p&gt;The constraints were non‑negotiable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multilingual inference &lt;/li&gt;
&lt;li&gt;multi‑modal document processing &lt;/li&gt;
&lt;li&gt;cross‑account AWS integration&lt;/li&gt;
&lt;li&gt;cross‑region invocation &lt;/li&gt;
&lt;li&gt;access to advanced LLMs such as Claude &lt;/li&gt;
&lt;li&gt;strong security controls for sensitive customer data &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It reads like an AWS Solution Architect Professional exam scenario? It wasn’t a hypothetical. This was a real‑world architecture challenge with real compliance, security, and business constraints.&lt;/p&gt;

&lt;p&gt;To achieve the goal, I designed a &lt;strong&gt;two‑sided trust‑granting model&lt;/strong&gt; between the two AWS environments. AWS Account A (AC‑a) operates in Region A, and AWS Account B (AC‑b) operates in Region B.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1gt5g4yo6w5yxzdmzs0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1gt5g4yo6w5yxzdmzs0.webp" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why Lambda in AC‑b Is Mandatory&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Claude enforces &lt;strong&gt;region restrictions based on the source IP&lt;/strong&gt;. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all traffic to Claude &lt;strong&gt;must originate from Region B&lt;/strong&gt; , and&lt;/li&gt;
&lt;li&gt;any request coming directly from Region A will be &lt;strong&gt;blocked&lt;/strong&gt; , regardless of AWS account ownership.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this, the Lambda function in AC‑b becomes a &lt;strong&gt;non‑negotiable region proxy&lt;/strong&gt;. It is the only component allowed to make outbound requests to Claude, ensuring that Anthropic sees a valid Region‑B AWS IP.&lt;/p&gt;

&lt;p&gt;In other words: &lt;strong&gt;AC‑a (data + app) → AC‑b (Lambda proxy + Claude)&lt;/strong&gt; is the only viable architecture that satisfies both the business requirements and the regional restrictions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Resources and IAM Roles&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In AC‑a, an EC2 instance acts as the caller of the AI service hosted in AC‑b. The EC2 instance assumes the IAM role &lt;strong&gt;ec2‑production&lt;/strong&gt; , which must be explicitly granted permission to invoke the Lambda function in AC‑b.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:region-b:ac-b-id:function:lambda-name"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the same time, the Lambda function in AC‑b must enforce the reverse trust boundary: it should only accept invocations originating from AC‑a’s &lt;strong&gt;ec2‑production&lt;/strong&gt; role. It uses resource-based policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Statement ID : AllowCrossAccountPHPInvoke
Principal : arn:aws:iam::ac-a-id:user/ec2-production  
Effect : Allow
Action : lambda:InvokeFunction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the Lambda IAM role level, it must be explicitly granted permission to access &lt;strong&gt;Amazon Bedrock&lt;/strong&gt; and invoke &lt;strong&gt;Claude&lt;/strong&gt;. This role becomes the trusted execution identity for all outbound LLM requests from AC‑b, ensuring that only the Lambda function running in the supported region is authorized to call the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowBedrockInvoke"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bedrock:InvokeModel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:bedrock:*:ac-b-id:inference-profile/your-claude-model"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:bedrock:*::foundation-model/your-claude-model"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the region to “*” in the IAM policy, since Bedrock may internally route the invocation across multiple supported regions. This ensures the Lambda role can invoke the service regardless of the specific regional endpoint used.&lt;/p&gt;

&lt;p&gt;This ensures &lt;strong&gt;mutual, least‑privilege trust&lt;/strong&gt; across accounts and regions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Cross‑Account Data Access&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Because the system exchanges &lt;strong&gt;customer‑sensitive data across multiple AWS accounts and regions&lt;/strong&gt; , the communication path must meet strict security guarantees. The design enforces three core requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No traffic may traverse the public internet &lt;/li&gt;
&lt;li&gt;Data can only originate from controlled, authenticated AWS resources &lt;/li&gt;
&lt;li&gt;Data can only be delivered to controlled, authenticated AWS resources &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The PrivateLink endpoint is protected by a dedicated security group that permits inbound HTTPS traffic &lt;strong&gt;only&lt;/strong&gt; from the &lt;code&gt;ec2-production&lt;/code&gt; instance. This ensures that no other resource, inside or outside the VPC, can reach the endpoint at the network layer.&lt;/p&gt;

&lt;p&gt;In addition, the PrivateLink service is bound to a &lt;strong&gt;resource policy&lt;/strong&gt; that explicitly authorizes only the &lt;code&gt;ec2-production&lt;/code&gt; IAM role to invoke the controlled Lambda function. Even if another resource could reach the endpoint, the policy prevents it from calling the Lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RestrictToLambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"AWS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::ac-a-id:user/ec2-production"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:aws-region-b:ac-b-id:function:lambda-name"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Together, the security group and the endpoint policy enforce strict, dual‑layer Zero Trust: &lt;strong&gt;network access is restricted to a single controlled source, and service‑level access is restricted to a single controlled identity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Lambda function is responsible for analyzing the customer’s document as part of the AI prediction workflow. However, due to regulatory requirements, all customer data must remain stored in AC‑a’s &lt;strong&gt;private S3 bucket&lt;/strong&gt;. However, AC‑b still needs controlled access to this data for analysis. Therefore, the S3 bucket policy in AC‑a must selectively grant AC‑b permission to read only the required objects, while maintaining strict Zero Trust boundaries. This is the &lt;strong&gt;authoritative gatekeeper&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowACBReadSpecificObjects"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"AWS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::ac-b-id:role/lambda-role"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::your-bucket/path/to/allowed/*"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures AC‑b &lt;strong&gt;cannot&lt;/strong&gt; read anything outside the approved prefix.&lt;/p&gt;

&lt;p&gt;In the AC-b, the lambda iam role attaches a policy to ensure that only attempt to access the specific S3 objects it is supposed to. This is the &lt;strong&gt;identity‑side limiter&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowReadOnlySpecificObjects"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::your-bucket/path/to/allowed/*"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same dual‑layer pattern AWS recommends for &lt;strong&gt;cross‑account S3 access with Zero Trust&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After the Claude model is activated and the required use‑case approvals are completed, the EC2 instance in &lt;strong&gt;AC‑a&lt;/strong&gt; can securely invoke the AWS Bedrock service.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Bringing It All Together&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This project was not only about adding generative AI to an existing platform, but also delivering advanced LLM capabilities under strict regional, security, and compliance constraints. By designing a cross‑account, cross‑region architecture with a mandatory Lambda proxy, enforcing mutual least‑privilege trust, and routing all sensitive data through PrivateLink, the solution enables multilingual, multi‑modal AI analysis without ever exposing customer information to the public internet.&lt;/p&gt;

&lt;p&gt;The result is a fully compliant, Zero‑Trust, enterprise‑grade AI integration that unlocks Claude’s capabilities for a business operating in an otherwise unsupported region—transforming their application workflow while preserving the highest security standards.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/how-i-delivered-a-cross-account-cross-region-multilingual-multi-modal-aws-bedrock-solution-in-a-zero-trust-environment/" rel="noopener noreferrer"&gt;How I Delivered a Cross‑Account, Cross‑Region, Multilingual, Multi‑Modal AWS Bedrock Solution in a Zero Trust Environment&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>cybersecurity</category>
      <category>zerotrust</category>
    </item>
    <item>
      <title>Technical Deep Dive: How I Delivered Zero Trust Security for a Client’s Legacy PHP System — Without Rewrites, Downtime, or...</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Sun, 19 Apr 2026 02:37:09 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/technical-deep-dive-how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-35ed</link>
      <guid>https://dev.to/jonathan78wong/technical-deep-dive-how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-35ed</guid>
      <description>&lt;p&gt;This section expands on the technical architecture and implementation behind my recent Zero Trust case study: &lt;strong&gt;&lt;a href="https://blog.jonanata.com/how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-or-big-costs/" rel="noopener noreferrer"&gt;How I Delivered Zero Trust Security for a Client’s Legacy PHP System — Without Rewrites, Downtime, or Big Costs&lt;/a&gt;&lt;/strong&gt;, and extend the &lt;a href="https://blog.jonanata.com/technical-deep-dive-how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-or-big-costs-part-1/" id="140" rel="noopener noreferrer"&gt;part 1 story&lt;/a&gt; to the internet‑facing Zero Trust layer, covering how external traffic is authenticated, filtered, and isolated before it ever reaches the private network.&lt;/p&gt;

&lt;p&gt;Below is the high‑level architecture diagram:  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jonanata.com%2Fwp-content%2Fuploads%2F2026%2F04%2Farchitecture-1-1024x683.png" class="article-body-image-wrapper"&gt;&lt;img width="800" height="534" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jonanata.com%2Fwp-content%2Fuploads%2F2026%2F04%2Farchitecture-1-1024x683.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Server — Nginx Layer&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;The Nginx layer enforces strict request‑level security controls before traffic ever reaches PHP. It blocks unsafe request patterns, rejects malformed uploads, prevents host‑header spoofing, and adds essential security headers. Sensitive files, hidden paths, and directory traversal attempts are denied outright. The server also hides all version information and limits request size to reduce attack surface. Together, these rules create a hardened perimeter that filters out malicious traffic and ensures only clean, intentional API requests reach the application.  &lt;/p&gt;

&lt;p&gt;Nginx Configuration Snippet &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//  other settings  

autoindex off;  

# Block direct IP access
if ($host != "yourdomain.com") {
    return 444;
}

# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

# Allow large uploads
client_max_body_size 2M;  

location ~ ^/.*/$ {
    return 403;
}  

# Block hidden files
location ~ /\.(?!well-known).* {
    deny all;
}

# Main routing
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

# PHP handler
location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;  

    //  auth header   
    fastcgi_param HTTP_AUTHORIZATION $http_authorization;  

}

//  other settings  &lt;/code&gt;&lt;/pre&gt;




&lt;p&gt;&lt;strong&gt;Cloudflare Layer&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Cloudflare acts as the authenticated edge for the entire API surface in internet . Mutual TLS and Worker JWT validation creates a strong identity and authorization boundary before traffic ever touches your private API server.  &lt;/p&gt;

&lt;p&gt;1. Mutual TLS — It is configured directly in the Cloudflare dashboard. In the SSL/TLS &amp;gt; Client Certificates section, create a new Client Certificate and download the certificate and private key. Embed this certificate into your mobile app. Cloudflare automatically manages the Client CA and validates every incoming request against it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jonanata.com%2Fwp-content%2Fuploads%2F2026%2F04%2Fimage-6-1024x625.png" class="article-body-image-wrapper"&gt;&lt;img width="800" height="488" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jonanata.com%2Fwp-content%2Fuploads%2F2026%2F04%2Fimage-6-1024x625.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Than go to Security &amp;gt; Security rules &amp;gt; Custom rules and click Create rule to set up the logic to identify unverified traffic as follows: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jonanata.com%2Fwp-content%2Fuploads%2F2026%2F04%2Fimage-7-1024x362.png" class="article-body-image-wrapper"&gt;&lt;img width="800" height="283" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jonanata.com%2Fwp-content%2Fuploads%2F2026%2F04%2Fimage-7-1024x362.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2. Cloudflare Worker — It runs at the edge and validates the JWT before forwarding the request to your origin. The Worker checks the signature, issuer, audience, and expiration, and rejects any invalid or expired token. This ensures that only authenticated and authorized traffic reaches your private API server.  &lt;br&gt;The JWT public key should be added as a “Secret” to the Workers &amp;gt; Settings &amp;gt; Variables &amp;gt; Secrets.   &lt;/p&gt;

&lt;p&gt;Cloudflare Worker JWT Validation Code Snippet  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;export default {
  async fetch(request, env) {
    const auth = request.headers.get("Authorization");
    if (!auth || !auth.startsWith("Bearer ")) {
      return new Response("Unauthorized", { status: 401 });
    }

    const token = auth.replace("Bearer ", "");

    try {  
      // env.JWT_PUBLIC_KEY is the JWT public key   
      const { payload } = await env.JWT.verify(token, env.JWT_PUBLIC_KEY, {
        issuer: "yourdomain.com",
        audience: "cloudflare"
      });

      // Token is valid → forward to origin
      return fetch(request);
    } catch (err) {
      return new Response("Invalid or expired token", { status: 401 });
    }
  }
};&lt;/code&gt;&lt;/pre&gt;




&lt;p&gt;&lt;strong&gt;Support components &lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Robust logging is essential for real‑time production monitoring. Logrotate handles local retention by deleting old logs right after rotation, while Fluent Bit streams the latest logs to CloudWatch, ensuring centralized, reliable observability.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configure Logrotate &lt;/strong&gt;&lt;br&gt;We use Logrotate to clear logs daily, reducing retention risk and minimizing the security exposure of sensitive operational data. To create a config file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo nano /etc/logrotate.d/ec2-production  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;add the Logrotate configuration  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/var/log/yourdomain/*.log&lt;br&gt;/var/log/nginx/yourdomain.access.log&lt;br&gt;/var/log/nginx/yourdomain.error.log {&lt;br&gt;    daily&lt;br&gt;    missingok&lt;br&gt;    rotate 0        # This deletes the old log immediately after rotation&lt;br&gt;    notifempty&lt;br&gt;    copytruncate    # Clears the file content without changing the filename&lt;br&gt;    nocreate        # Do not create a new file; let the app handle it&lt;br&gt;}  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Install Fluent Bit   &lt;/strong&gt;&lt;br&gt;Run these commands on your Ubuntu instance to install the stable version:  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo wget -qO /etc/apt/keyrings/fluentbit.asc https://packages.fluentbit.io/fluentbit.key 
echo "deb [signed-by=/etc/apt/keyrings/fluentbit.asc] https://packages.fluentbit.io/ubuntu/noble noble main" \
| sudo tee /etc/apt/sources.list.d/fluentbit.list  

sudo apt update  
sudo apt install -y fluent-bit  
sudo systemctl start fluent-bit
sudo systemctl enable fluent-bit  
systemctl status fluent-bit &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure Fluent Bit to ship the logs to CloudWatch. Update /etc/fluent-bit/fluent-bit.conf to track these files.    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[SERVICE]
    Flush        5
    Daemon       Off
    Log_Level    info
    Parsers_File parsers.conf

# 1. PHP Application Logs
[INPUT]
    Name             tail
    Path             /var/log/yourdomain/*.log
    Tag              ec2-production.php
    Refresh_Interval 5

# 2. Nginx Access Logs
[INPUT]
    Name             tail
    Path             /var/log/nginx/yourdomain.access.log
    Tag              ec2-production.nginx_access
    Parser           nginx  # Standard built-in parser for Nginx access
    Refresh_Interval 5

# 3. Nginx Error Logs
[INPUT]
    Name             tail
    Path             /var/log/nginx/yourdomain.error.log
    Tag              ec2-production.nginx_error
    # Nginx error logs often need a custom regex parser or 'multiline'
    Refresh_Interval 5  

    Multiline        On
    Parser_Firstline nginx_error_firstline
    Parser_Nextline  nginx_error_nextline

[OUTPUT]
    Name            cloudwatch_logs
    Match           ec2-production.*
    region          aws-region
    log_group_name  ec2-production
    log_stream_prefix ec2-
    auto_create_group On&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Restart the Fluent Bit &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo systemctl restart fluent-bit  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Open the CloudWatch Console. Go to Logs &amp;gt; Log Groups. Look for the group name ec2-production. You should see log streams appearing within a few minutes of activity on your Nginx/PHP server. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Bastion Server  &lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To minimize the attack surface, the production EC2 server remains inside the AWS private network with no public IP and no inbound rules, except controlled SSH access from a bastion host used for production support.  &lt;/p&gt;

&lt;p&gt;The bastion server  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enabled only when production support is required &lt;/li&gt;



&lt;li&gt;Accessible only with an approved SSH key and trusted IP  &lt;/li&gt;



&lt;li&gt;Access to production is performed exclusively through a controlled login script  &lt;/li&gt;



&lt;li&gt;No production server private IPs or SSH keys are ever exposed  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To add the production server controlled login script  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# ensure the normal user can't read the production server key 
sudo mkdir -p /root/keys
sudo mv /home/ubuntu/.ssh/ec2-production.pem /root/keys/
sudo chmod 600 /root/keys/ec2-production.pem 

# login-ec2-production is the production server login script 
sudo nano /usr/local/bin/login-ec2-production  

# ensure normal can't update the login script 
sudo chmod 700 /usr/local/bin/login-ec2-production
sudo chown root:root /usr/local/bin/login-ec2-production  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;add the permission to the normal user to run the script by “sudo visudo” &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# add the permission   
ubuntu ALL=(root) NOPASSWD: /usr/local/bin/login-ec2-production  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can go to the production server by login-ec2-production for production support without direct access. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Security Outcome&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;This layered setup ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero public exposure — no public IPs, all traffic enters through Cloudflare Tunnel only.&lt;/li&gt;



&lt;li&gt;Authenticated edge — Cloudflare enforces mTLS and validates JWTs before forwarding any request.&lt;/li&gt;



&lt;li&gt;No direct origin access — only Cloudflare Tunnel can reach the private API server.&lt;/li&gt;



&lt;li&gt;Strict request filtering — Nginx blocks unsafe patterns, malformed requests, and disallowed methods.&lt;/li&gt;



&lt;li&gt;Hardened runtime — PHP runs with disabled dangerous functions, strict session rules, and no version leakage.&lt;/li&gt;



&lt;li&gt;No stored passwords — API connects to RDS using IAM authentication, not static credentials.&lt;/li&gt;



&lt;li&gt;RDS Proxy only — EC2 instances cannot connect directly to the database.&lt;/li&gt;



&lt;li&gt;Identity at every hop — Cloudflare mTLS → Worker JWT → IAM role → RDS Proxy → RDS.&lt;/li&gt;



&lt;li&gt;East‑west segmentation — security groups restrict lateral movement between internal components.&lt;/li&gt;



&lt;li&gt;Minimal attack surface — no directory browsing, no file uploads, no sensitive files exposed.&lt;/li&gt;



&lt;li&gt;Consistent Zero‑Trust posture — every request, device, and connection must prove identity before being allowed through.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s a Zero Trust architecture applied to a legacy PHP system — without rewriting the application, without downtime, and without blowing the budget.  &lt;/p&gt;





&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/technical-deep-dive-how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-or-big-costs-part-2/" rel="noopener noreferrer"&gt;Technical Deep Dive: How I Delivered Zero Trust Security for a Client’s Legacy PHP System — Without Rewrites, Downtime, or Big Costs (Part 2)&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cybersecurity</category>
      <category>zerotrust</category>
    </item>
    <item>
      <title>How to Create and Manage a Copilot Task That Automatically Scans Gmail for Bills and Adds Them to Google Calendar</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Wed, 15 Apr 2026 21:59:07 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/how-to-create-and-manage-a-copilot-task-that-automatically-scans-gmail-for-bills-and-adds-them-to-3e3h</link>
      <guid>https://dev.to/jonathan78wong/how-to-create-and-manage-a-copilot-task-that-automatically-scans-gmail-for-bills-and-adds-them-to-3e3h</guid>
      <description>&lt;p&gt;I recently discovered a new feature in Copilot called Tasks. It’s a cloud‑based automation capability that can take over routine, multi‑step processes and keep them running in the background — even when your computer is off. To understand what it can actually do, I tried a simple experiment: creating a task that scans Gmail for bill or invoice emails and automatically adds the due dates to my Payment Google Calendar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3n919eoy499w0wyye7lq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3n919eoy499w0wyye7lq.png" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let’s Create the Task&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Describe the automation in natural language&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You told Copilot:&lt;br&gt;&lt;br&gt;
&lt;em&gt;“Scan Gmail for bill/invoice emails from the past 7 days and create events on my Payment Google Calendar.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Copilot interprets the workflow&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It identifies required connectors, and permission prompts appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Allow Copilot to read your Gmail”: Gmail (to read emails)&lt;/li&gt;
&lt;li&gt;“Allow Copilot to create events in Google Calendar”: Google Calendar (to create events) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jmgmbft03ya73tb61eb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jmgmbft03ya73tb61eb.png" width="713" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uykdyjreq52jvyzox1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uykdyjreq52jvyzox1j.png" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Copilot generates a task card&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The card summarizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the task will do&lt;/li&gt;
&lt;li&gt;When it will run&lt;/li&gt;
&lt;li&gt;What services it needs access to &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6t5y2feq4yg9ubg5gh9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6t5y2feq4yg9ubg5gh9.png" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. You approve the permissions&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Copilot start to create the task and show the result. The task becomes active and begins running on its schedule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8fjrus2stnlbibnyzm4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8fjrus2stnlbibnyzm4.png" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The automation runs independently on Copilot’s servers, not your device. You don’t need to keep Copilot open. The task simply executes on schedule and reports back when it’s done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Are the Outcomes?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Daily Gmail scanning&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Looks back 7 days&lt;/li&gt;
&lt;li&gt;Detects bill/invoice emails&lt;/li&gt;
&lt;li&gt;Extracts due dates and vendor names&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Automatic calendar event creation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates events on your &lt;strong&gt;Payment&lt;/strong&gt; Google Calendar&lt;/li&gt;
&lt;li&gt;Time: &lt;strong&gt;8:00–8:30 AM&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Reminder: &lt;strong&gt;1 day before&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now find the new task card in your Copilot Tasks tab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfivetuhel245kmub8sm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfivetuhel245kmub8sm.png" width="800" height="712"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Manage Tasks Status&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To &lt;strong&gt;pause, resume,&lt;/strong&gt; and &lt;strong&gt;remove&lt;/strong&gt; tasks&lt;br&gt;&lt;br&gt;
In tasks tab you can ask Copilot to:&lt;br&gt;&lt;br&gt;
&lt;em&gt;“List my scheduled tasks” — see all active tasks&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;“Pause my bill check task” — temporarily stop it&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;“Delete my bill check task” — remove it entirely&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To &lt;strong&gt;modify&lt;/strong&gt; the task:&lt;br&gt;&lt;br&gt;
&lt;em&gt;“Modify my bill check task to scan 14 days instead of 7.”&lt;br&gt;&lt;br&gt;
“Update my bill task to add a 2‑day reminder.”&lt;br&gt;&lt;br&gt;
“Change the calendar to ‘Finance’ instead of ‘Payment’.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Copilot will regenerate the task card with updated logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpfcnewprxd82m2xyw5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpfcnewprxd82m2xyw5d.png" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Manage Task Permissions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can review and manage task permissions through the Sources and Connectors section in Copilot. This lets you see exactly which accounts and data the task relies on.&lt;/p&gt;

&lt;p&gt;Option 1: &lt;strong&gt;Copilot Dashboard&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Click the Sources and Connectors icon in the Copilot dashboard message bar. It shows a full list of all accounts and data sources your task is currently using.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furl6xqhg3dryh79tmeuf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furl6xqhg3dryh79tmeuf.png" width="799" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the “Connector settings” button to view all connector details associated with the task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhczv0rs5dnlx5lp1gy0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhczv0rs5dnlx5lp1gy0z.png" width="800" height="764"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Option 2: &lt;strong&gt;Ask Copilot&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In the message bar, type “Show me the connectors used in my bill check task.” Copilot will return a list of all connectors the task currently uses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lswq5k4xcp4xc3jom6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lswq5k4xcp4xc3jom6h.png" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to revoke Gmail or Google Calendar access, go to:&lt;br&gt;&lt;br&gt;
&lt;a href="https://copilot.microsoft.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;https://copilot.microsoft.com&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;→ Settings → Connected Accounts&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
There you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disconnect Gmail&lt;/li&gt;
&lt;li&gt;Disconnect Google Calendar&lt;/li&gt;
&lt;li&gt;Remove all scopes previously granted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once removed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The task will fail gracefully&lt;/li&gt;
&lt;li&gt;Copilot will notify you that access is required&lt;/li&gt;
&lt;li&gt;You can re‑grant permissions anytime&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Key Takeaway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My first hands‑on trial of &lt;strong&gt;Copilot Tasks&lt;/strong&gt; shows that Microsoft is quietly building a true cloud‑native automation engine — one that can run multi‑step workflows independently, without relying on your local machine. The experience is already surprisingly capable, but it’s important to note that &lt;strong&gt;Copilot Tasks is still in research preview, not general availability&lt;/strong&gt; , so features, reliability, and UI flows may continue to evolve as Microsoft expands the rollout.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/how-to-create-and-manage-a-copilot-task-that-automatically-scans-gmail-for-bills-and-adds-them-to-google-calendar/" rel="noopener noreferrer"&gt;How to Create and Manage a Copilot Task That Automatically Scans Gmail for Bills and Adds Them to Google Calendar&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>microsoft</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>Technical Deep Dive: How I Delivered Zero Trust Security for a Client’s Legacy PHP System — Without Rewrites, Downtime, or...</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Tue, 14 Apr 2026 19:26:40 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/technical-deep-dive-how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-hc</link>
      <guid>https://dev.to/jonathan78wong/technical-deep-dive-how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-hc</guid>
      <description>&lt;p&gt;This section expands on the technical architecture and implementation behind my recent Zero Trust case study:&lt;strong&gt; &lt;a href="https://blog.jonanata.com/how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-or-big-costs/" rel="noopener noreferrer"&gt;How I Delivered Zero Trust Security for a Client’s Legacy PHP System — Without Rewrites, Downtime, or Big Costs – Behind the Build&lt;/a&gt;&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Below is the high‑level architecture diagram:  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jonanata.com%2Fwp-content%2Fuploads%2F2026%2F04%2Farchitecture-1-1024x683.png" class="article-body-image-wrapper"&gt;&lt;img width="800" height="534" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jonanata.com%2Fwp-content%2Fuploads%2F2026%2F04%2Farchitecture-1-1024x683.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Layer — Role‑Based Access + Network Isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The AWS environment is built around least privilege, no public exposure, and segmented trust boundaries.  We enforce this through IAM roles, security groups, resource‑level permissions, and Cloudflare Tunnel. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Resources&lt;/em&gt;  &lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1. EC2 (api-production) — API Server with No Public IP&lt;/p&gt;

&lt;p&gt;This EC2 instance runs the PHP API and sits entirely inside a private subnet.  It has no public IP, no inbound rules, and no exposed ports. The only way it communicates with the internet is through Cloudflare Tunnel.&lt;/p&gt;

&lt;p&gt;Cloudflare Tunnel works because:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The EC2 instance initiates an outbound TLS connection to Cloudflare&lt;/li&gt;



&lt;li&gt;Cloudflare never needs to reach into AWS  &lt;/li&gt;



&lt;li&gt;No public IP, no inbound SG rules, and no NAT Gateway are required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This outbound‑only model eliminates the attack surface while still allowing global access.  &lt;br&gt;Cloudflare Tunnel setup: &lt;/p&gt;

&lt;p&gt;A. Install the cloudflared  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
| sudo tee /usr/share/keyrings/cloudflare.gpg &amp;gt;/dev/null 

echo "deb [signed-by=/usr/share/keyrings/cloudflare.gpg] https://pkg.cloudflare.com/cloudflared jammy main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list  

sudo apt update
sudo apt install cloudflared  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;B. Cloudflare Login and authentication. This command generates a one‑time login URL that authenticates your machine with Cloudflare and authorizes this cloudflared instance. During the process, Cloudflare downloads a “cert.pem” file, which the CLI uses later when creating and managing tunnels.  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Please log in to your Cloudflare dashboard before running the command below. Otherwise, you will be redirected to the Cloudflare login page and the authentication flow will not complete.  

cloudflared tunnel login &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;C. Create Cloudflare tunnel. This command create Cloudflare tunnel, and returns a tunnel ID in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx1234  &lt;br&gt;## Make sure to copy the tunnel ID for use in subsequent configuration steps  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cloudflared tunnel create api-production-cf-tunnel  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;E. Create tunnel DNS record. This command creates the DNS record for in Cloudflare. You should see a new ‘tunnel’ DNS record appear automatically in the Cloudflare dashboard. There is no need to manually add the record yourself.    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cloudflared tunnel route dns api-production-cf-tunnel yourdomain.com with value xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx1234.cfargotunnel.com &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;D. Create tunnel runtime configurations  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;nano ~/.cloudflared/config.yaml &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The settings  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;tunnel: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx1234        # The tunnel ID returned by Cloudflare
credentials-file: /etc/cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx1234.json  # Tunnel credentials

ingress:
  - hostname: yourdomain.com                        # Public hostname
    service: http://localhost:80                    # Local service to forward to
  - service: http_status:404                        # Default fallback  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;F. Start the Cloudflare tunnel  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;## save the configuration  
sudo mkdir -p /etc/cloudflared 
sudo mv ~/.cloudflared/config.yml /etc/cloudflared/
sudo mv ~/.cloudflared/*.json /etc/cloudflared/  
sudo chown root:root /etc/cloudflared/config.yml
sudo chown root:root /etc/cloudflared/&lt;em&gt;.json &lt;/em&gt;
&lt;em&gt;sudo chmod 600 /etc/cloudflared/&lt;/em&gt;.json
sudo chmod 600 /etc/cloudflared/config.yml  

## start the cloudflare tunnel in the api-production (EC2) 
cloudflared tunnel run api-production-cf-tunnel &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;G. Install cloudflared as service  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo cloudflared service install  
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sudo systemctl status cloudflared  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2. RDS (rds-production) — Private MySQL Database  &lt;br&gt;The production database is fully isolated:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No public access  &lt;/li&gt;



&lt;li&gt;No internet routing  &lt;/li&gt;



&lt;li&gt;IAM authentication only  &lt;/li&gt;



&lt;li&gt;Encryption enabled  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It can only be reached through the RDS Proxy. &lt;/p&gt;

&lt;p&gt;3. RDS Proxy (rds-proxy-production) — IAM‑Authenticated Database Access  &lt;br&gt;The proxy sits between the EC2 instance and the database. It enforces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM authentication  &lt;/li&gt;



&lt;li&gt;Connection pooling  &lt;/li&gt;



&lt;li&gt;No direct database exposure &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;&lt;em&gt;IAM Roles&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1. role-ec2-production — API Server Role  &lt;br&gt;Attached to the EC2 instance. It grants access only to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specific S3 buckets  &lt;/li&gt;



&lt;li&gt;The RDS Proxy  &lt;/li&gt;



&lt;li&gt;CloudWatch logs  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No IAM users, no access keys, no long‑lived credentials. The inline policy: &lt;/p&gt;

&lt;p&gt;role-ec2-production Inline Policy&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//  RDS  
{
  "Effect": "Allow",
  "Action": [
    "rds-db:connect"
  ],
  "Resource": [
    "arn:aws:rds-db:aws-region:aws-ac-id:dbuser:db-instance-arn/role-rds-db",
    "arn:aws:rds-db:aws-region:aws-ac-id:dbuser:prx-proxy-arn/role-rds-db"
  ]
}
//  S3  
{
  "Sid": "AllowListBucket",
  "Effect": "Allow",
  "Action": "s3:ListBucket",
  "Resource": "arn:aws:s3:::bucket-name",
  "Condition": {
    "StringEquals": {
      "aws:RequestedRegion": "aws-region"
    }
  }
},
{
  "Sid": "AllowObjectReadWrite",
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:PutObject"
  ],
  "Resource": "arn:aws:s3:::bucket-name/*",
  "Condition": {
    "StringEquals": {
      "aws:RequestedRegion": "aws-region"
    }
  }
}
//  CloudWatch  
{
  "Effect": "Allow",
  "Action": [
    "logs:DescribeLogGroups",
    "logs:DescribeLogStreams",
    "logs:CreateLogGroup",
    "logs:CreateLogStream",
    "logs:PutLogEvents"
  ],
  "Resource": "*"
}
//  JWT  
{
  "Sid": "AllowJWTPrivateKey",
  "Effect": "Allow",
  "Action": [
    "ssm:GetParameter",
    "ssm:GetParameters"
  ],
  "Resource": "arn:aws:ssm:aws-region:aws-ac-id:parameter/category-name/jwt/private-key"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2. role-rds-db — RDS Proxy Role  &lt;br&gt;Attached to the RDS Proxy. It allows the proxy to authenticate to the database using IAM auth. Tokens are generated and signed by AWS KMS.  &lt;/p&gt;

&lt;p&gt;role-rds-db Inline policy  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "Sid": "GetSecretValue",
  "Action": [
    "secretsmanager:GetSecretValue"
  ],
  "Effect": "Allow",
  "Resource": [
    "arn:aws:secretsmanager:aws-region:aws-ac-id:secret:rds-proxy-role-rds-db-name"
  ]
}
{
  "Sid": "DecryptSecretValue",
  "Action": [
    "kms:Decrypt"
  ],
  "Effect": "Allow",
  "Resource": [
    "arn:aws:kms:aws-region:aws-ac-id:key/decrypt-key"
  ],
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "secretsmanager.aws-region.amazonaws.com"
    }
  }
}
{
  "Effect": "Allow",
  "Action": "rds-db:connect",
  "Resource": "arn:aws:rds-db:aws-region:aws-ac-id:dbuser:db-name/role-rds-db"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3. role-rds-db — Database IAM Auth User  &lt;br&gt;The same IAM auth username must exist inside the MySQL database.  Creation SQL:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- 1. Create IAM-authenticated user (no password)
CREATE USER 'role-rds-db'@'%' IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';

-- 2. Grant only SELECT, INSERT, UPDATE, DELETE on the your_db database
GRANT SELECT, INSERT, UPDATE, DELETE ON your_db.* TO 'role-rds-db'@'%';

-- 3. Reload privileges (optional but safe)
FLUSH PRIVILEGES;  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This ensures the proxy can authenticate as a database user without passwords.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;em&gt;Security Groups&lt;/em&gt;&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;1. sg-ec2-production  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attached to the api-production  &lt;/li&gt;



&lt;li&gt;No inbound access except from the bastion host via private IP  &lt;/li&gt;



&lt;li&gt;Outbound allowed for Cloudflare Tunnel  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This removes the external attack surface entirely.&lt;/p&gt;

&lt;p&gt;2. sg-rds-proxy&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attached to the RDS Proxy  &lt;/li&gt;



&lt;li&gt;Only allows inbound 3306 from sg-ec2-production  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3. sg-rds-db  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attached to the RDS instance  &lt;/li&gt;



&lt;li&gt;Only allows inbound 3306 from sg-rds-proxy  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 cannot reach the database directly  &lt;/li&gt;



&lt;li&gt;Only the proxy can communicate with the database  &lt;/li&gt;



&lt;li&gt;Even inside the private network, lateral movement is blocked  &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;API Server — PHP Layer&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;1. JWT validation and authorization in the PHP API before processing any request  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The PHP API verifies the JWT signature &lt;/li&gt;



&lt;li&gt;enforces expiration &lt;/li&gt;



&lt;li&gt;checks user permissions directly from the claims &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Generate JWT Code Snippet  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// your function 

// -------------------------------
// 1. Load RSA private key from SSM
// -------------------------------

$ssm = new SsmClient([
    'region'  =&amp;gt; 'aws-region',
    'version' =&amp;gt; 'latest'
]);

$result = $ssm-&amp;gt;getParameter([
    'Name' =&amp;gt; '/category-name/jwt/private-key',   // &amp;lt;-- your parameter name
    'WithDecryption' =&amp;gt; true
]);

$privateKeyPem = $result['Parameter']['Value'];

$privateKey = openssl_pkey_get_private($privateKeyPem);

if (!$privateKey) {
    
    //  error handling  
}

// -------------------------------
// 2. Helper: Base64URL encoding
// -------------------------------

function base64UrlEncode($data) {
    return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

// -------------------------------
// 3. Build JWT header + payload
// -------------------------------

$header = [
    'alg' =&amp;gt; 'RS256',
    'typ' =&amp;gt; 'JWT'
];

$payload = [
    "sub" =&amp;gt; $sub,  //  variable for later checking  
    "iat" =&amp;gt; time(),
    "exp" =&amp;gt; time() + 360000,
    "iss" =&amp;gt; "yourdomain.com",
    "aud" =&amp;gt; "cloudflare"
];

$base64Header  = base64UrlEncode(json_encode($header, JSON_UNESCAPED_SLASHES));
$base64Payload = base64UrlEncode(json_encode($payload, JSON_UNESCAPED_SLASHES));

$dataToSign = "$base64Header.$base64Payload";

// -------------------------------
// 4. Sign with RSA private key
// -------------------------------

if (!openssl_sign($dataToSign, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {
    
    //  error handling  
}

$jwt = $dataToSign . "." . base64UrlEncode($signature);  

// other function logic  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Validate JWT Code Snippet  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//  your function  

list($h, $p, $s) = explode('.', $jwt);

$dataToVerify = "$h.$p";
$signature    = base64UrlDecode($s);

$publicKey = openssl_pkey_get_public(file_get_contents(your_jwt_public_key_path));

// validate signature  
$valid = openssl_verify($dataToVerify, $signature, $publicKey, OPENSSL_ALGO_SHA256);

if ($valid !== 1) { 

    // error handling  

}  

$payload = json_decode(base64UrlDecode($p), true);  

// validate payload 
if (!$payload || !isset($payload['exp'])) {

    // error handling  

}

// validate expiry 
if (time() &amp;lt;= $payload['exp']) { 

    // error handling  
    
}  

//  your function other logic  
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2. Secure database access  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All database operations use PDO’s native parameter binding, which inherently prevents SQL injection. &lt;/li&gt;



&lt;li&gt;The connection enforces TLS encryption with the RDS CA bundle, ensuring data in transit is protected and the server identity is verified. The RDS CA bundle can be downloaded &lt;a href="https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem" rel="noopener noreferrer"&gt;here&lt;/a&gt;   &lt;/li&gt;



&lt;li&gt;Authentication is fully passwordless using IAM role–based RDS authentication, so no credentials are stored in the application. &lt;/li&gt;



&lt;li&gt;The PHP layer is restricted to connect exclusively through the RDS Proxy, never directly to the RDS instance, enforcing a strict Zero Trust boundary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Database Connection Code Snippet  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//  your function 

$host = "proxy-xxxxxxxx"; // RDS Proxy endpoint
$port = 3306;
$username = "role-rds-db"; // MySQL user created with IDENTIFIED WITH AWSAuthenticationPlugin
$region = "aws-region";
$dbname = "your_db";

// AWS SDK
$sdk = new Sdk([
    'region'   =&amp;gt; $region,
    'version'  =&amp;gt; 'latest'
]);  

$provider = CredentialProvider::defaultProvider();  

$rdsAuthGenerator = new AuthTokenGenerator($provider);

// Generate IAM token (no password stored)
$token = $rdsAuthGenerator-&amp;gt;createToken("$host:$port", $region, $username);  

$dsn = "mysql:host=$host;port=$port;dbname=$dbname;charset=utf8mb4";  

// Enforces TLS encryption with the RDS CA bundle  
$ca_bundle_path = realpath($path_to_store_ . "/cert/global-bundle.pem");

$options = [
    PDO::ATTR_PERSISTENT         =&amp;gt; false,                  // Disable persistence in PDO  
    PDO::ATTR_ERRMODE            =&amp;gt; PDO::ERRMODE_EXCEPTION, // Throw exceptions on errors    
    PDO::MYSQL_ATTR_SSL_CA       =&amp;gt; $ca_bundle_path,       // Path to RDS CA bundle  
//  other option settings  
];

try {
    // 3. Establish the connection
    $pdo = new PDO($dsn, $username, $token, $options);
    
    return $pdo;  
    
} catch (PDOException $e) {
    //  error handling  
}  

//  other function logic  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3. No direct file handling on the server — The API never accepts file uploads. All file operations are performed through S3 pre‑signed URLs, ensuring the PHP layer remains stateless and free from file‑system exposure  &lt;/p&gt;

&lt;p&gt;S3 Upload / Read Code Snippet  &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//  your upload function  

$bucket = 'your-bucket';
$region = 'aws-region';
$subFolder = 'your-subFolder';  
//  $contentType as parameter  

$s3 = new S3Client([
    'region'  =&amp;gt; $region,
    'version' =&amp;gt; 'latest'
]);

// Generate unique filename
$filename = random_str() . '.' . $ext;
$key = $subFolder . $filename;

// Create command for PUT
$cmd = $s3-&amp;gt;getCommand('PutObject', [
    'Bucket' =&amp;gt; $bucket,
    'Key'    =&amp;gt; $key,
    'ContentType' =&amp;gt; $contentType  
]);

// Create pre-signed URL valid for 60 seconds
$request = $s3-&amp;gt;createPresignedRequest($cmd, '+600 seconds');
$presignedUrl = (string) $request-&amp;gt;getUri();

//  other function logic  

//  your read function  

    $bucket = 'your-bucket';
    $region = 'aws-region';  
    //  $filepath  as parameter  

    $s3 = new S3Client([
        'region'  =&amp;gt; $region,
        'version' =&amp;gt; 'latest'
    ]);

    // Create command for PUT
    $cmd = $s3-&amp;gt;getCommand('GetObject', [
        'Bucket' =&amp;gt; $bucket,
        'Key'    =&amp;gt; $filepath 
    ]);

    // Create pre-signed URL valid for 60 seconds
    $request = $s3-&amp;gt;createPresignedRequest($cmd, '+600 seconds');
    $presignedUrl = (string) $request-&amp;gt;getUri();  
    
//  other function logic  &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4. php.ini Security Hardening — These php.ini changes lock down the PHP runtime by removing risk. The result is a minimal‑surface, production‑safe environment aligned with Zero Trust principles.&lt;/p&gt;

&lt;p&gt;php.ini Setting Snippet    &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Disable Dangerous Functions
; Prevent command execution, file system abuse, and code injection
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_multi_exec,parse_ini_file,show_source

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Disable URL File Access
; Prevent Remote File Inclusion (RFI) attacks
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

allow_url_fopen = Off
allow_url_include = Off

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Hide PHP Version &amp;amp; Reduce Fingerprinting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

expose_php = Off

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Session Security
; Protect against hijacking, fixation, and CSRF
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

session.use_strict_mode = 1
session.use_only_cookies = 1
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = Strict
session.use_trans_sid = 0
session.sid_length = 48
session.sid_bits_per_character = 6

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Error Handling
; Never leak stack traces or internal paths to users
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = path_to_log_folder/php_errors.log

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Disable Legacy, Unsafe Features
; Prevent remote code injection via old PHP behaviors
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

register_globals = Off
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off&lt;/code&gt;&lt;/pre&gt;




&lt;p&gt;&lt;strong&gt;Security Outcome&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;This layered setup ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero public exposure — no public IPs, all traffic enters through Cloudflare Tunnel only.&lt;/li&gt;



&lt;li&gt;Authentication — JWTs are fully validated before any request is allowed to trigger the API.  &lt;/li&gt;



&lt;li&gt;No direct origin access — only Cloudflare Tunnel can reach the private API server.  &lt;/li&gt;



&lt;li&gt;No stored passwords — API connects to RDS using IAM authentication, not static credentials.&lt;/li&gt;



&lt;li&gt;RDS Proxy only — EC2 instances cannot connect directly to the database.&lt;/li&gt;



&lt;li&gt;Identity at every hop — IAM role → RDS Proxy → RDS.&lt;/li&gt;



&lt;li&gt;East‑west segmentation — security groups restrict lateral movement between internal components.  &lt;/li&gt;



&lt;li&gt;Hardened runtime — PHP runs with disabled dangerous functions, strict session rules, and no version leakage.  &lt;/li&gt;



&lt;li&gt;Minimal attack surface — no file uploads, no sensitive files exposed.&lt;/li&gt;



&lt;li&gt;Consistent Zero Trust posture — every request, device, and connection must prove identity before being allowed through.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What Next’s&lt;/strong&gt; &lt;br&gt;This article focused on the AWS private‑network Zero Trust architecture I applied to a legacy PHP system — the part that happens inside the VPC, with no public exposure and identity‑driven access to every internal component.&lt;br&gt;In my next article, I’ll extend the story to the internet‑facing Zero Trust layer, covering how external traffic is authenticated, filtered, and isolated before it ever reaches the private network. That next piece will dive into:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloudflare Edge and global identity enforcement  &lt;/li&gt;



&lt;li&gt;Nginx request filtering and origin isolation &lt;/li&gt;



&lt;li&gt;Supporting operational components like CloudWatch and the bastion host  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these complete the full end‑to‑end Zero Trust posture — from the public internet all the way down to the private AWS runtime.  &lt;/p&gt;





&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/technical-deep-dive-how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-or-big-costs-part-1/" rel="noopener noreferrer"&gt;Technical Deep Dive: How I Delivered Zero Trust Security for a Client’s Legacy PHP System — Without Rewrites, Downtime, or Big Costs (Part 1)&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cybersecurity</category>
      <category>zerotrust</category>
    </item>
    <item>
      <title>Joining the Google Cloud Get Certified Program Again — Day 1 Highlights</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Tue, 14 Apr 2026 03:01:08 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/joining-the-google-cloud-get-certified-program-again-day-1-highlights-59a3</link>
      <guid>https://dev.to/jonathan78wong/joining-the-google-cloud-get-certified-program-again-day-1-highlights-59a3</guid>
      <description>&lt;p&gt;I’m glad to have the chance to join the &lt;strong&gt;Google Cloud Get Certified&lt;/strong&gt; Program again. Today I attended the online, live, Instructor‑Led Training (ILT) session. It was a full one‑day workshop that delivered a solid refresh across key Google Cloud services.&lt;/p&gt;

&lt;p&gt;The training covered a wide range of topics, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Marketplace &lt;/li&gt;
&lt;li&gt;VPC Networking &lt;/li&gt;
&lt;li&gt;Google Compute Engine &lt;/li&gt;
&lt;li&gt;Cloud Storage &lt;/li&gt;
&lt;li&gt;Cloud SQL &lt;/li&gt;
&lt;li&gt;Cloud Run (Kubernetes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The session was well organized by Jellyfish Training, with clear explanations and strong demonstrations throughout the day. The structure made it easy to follow the progression from foundational services to more hands‑on deployment scenarios.&lt;/p&gt;

&lt;p&gt;We also completed several &lt;strong&gt;Google Skill Boost hands‑on labs&lt;/strong&gt; , which helped reinforce the concepts with practical exercises.&lt;/p&gt;

&lt;p&gt;With the first stage completed, I’m looking forward to the next phase of the program — the Certification Journey. This is where the deeper preparation begins, and I’m excited to continue building momentum.&lt;/p&gt;

&lt;p&gt;If you’re also working toward a Google Cloud certification, feel free to connect. Always open to sharing study approaches and learning together.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/joining-the-google-cloud-get-certified-program-again-day-1-highlights/" rel="noopener noreferrer"&gt;Joining the Google Cloud Get Certified Program Again — Day 1 Highlights&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>certification</category>
      <category>google</category>
      <category>learning</category>
    </item>
    <item>
      <title>Building Agentic AI Solutions with Azure AI Foundry — My Training Day Review &amp; Updated AI‑103 Study Plan</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Thu, 09 Apr 2026 17:16:00 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/building-agentic-ai-solutions-with-azure-ai-foundry-my-training-day-review-updated-ai-103-study-3k8k</link>
      <guid>https://dev.to/jonathan78wong/building-agentic-ai-solutions-with-azure-ai-foundry-my-training-day-review-updated-ai-103-study-3k8k</guid>
      <description>&lt;p&gt;On March 19, I attended the &lt;strong&gt;Microsoft Virtual Training Day: Build Agentic AI Solutions with Azure AI Foundry&lt;/strong&gt; , a deep‑dive session focused on the emerging world of &lt;strong&gt;agentic AI&lt;/strong&gt; , multi‑agent orchestration, and the evolving Azure ecosystem. With the new &lt;strong&gt;AI‑103 (beta)&lt;/strong&gt; exam approaching, this training arrived at the perfect time.&lt;/p&gt;

&lt;p&gt;Microsoft has now published the &lt;strong&gt;official AI‑103 syllabus and a few self‑paced modules&lt;/strong&gt; , which provide much‑needed structure for early learners:&lt;br&gt;&lt;br&gt;
&lt;a href="https://learn.microsoft.com/en-us/training/courses/ai-103t00#course-syllabus" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/training/courses/ai-103t00#course-syllabus&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below is my updated summary of the training, how it aligns with the exam, and my revised learning plan.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;1. Summary of the Training Day Content&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The training covered the full lifecycle of building AI agents on Azure:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Getting Started with AI Agent Development on Azure&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A walkthrough of Azure AI Foundry, including model catalog, prompt flow, and evaluation tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Developing an AI Agent with Azure AI Agent Service&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;How to configure, deploy, and scale agents using Microsoft’s new agent runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Integrating Custom Tools into Your Agent&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Connecting agents to APIs, enterprise systems, and custom tools for real‑world use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Developing Agents with the Semantic Kernel SDK&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Using planners, skills, connectors, and orchestration patterns to build intelligent workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Orchestrating Multi‑Agent Solutions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Designing collaborative agent systems that delegate tasks and coordinate actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Developing Multi‑Agent Solutions with Azure AI Foundry Agent Service&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Advanced multi‑agent patterns, routing strategies, and evaluation workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Integrating MCP Tools with Azure AI Agents&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A forward‑looking module on the Model Context Protocol (MCP) and standardized tool integration.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;2. How This Training Supports AI‑103 Exam Preparation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The training aligns closely with the expected AI‑103 domains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure AI Foundry fundamentals&lt;/li&gt;
&lt;li&gt;Agent Service configuration&lt;/li&gt;
&lt;li&gt;Semantic Kernel development&lt;/li&gt;
&lt;li&gt;Tool integration (including MCP)&lt;/li&gt;
&lt;li&gt;Multi‑agent orchestration&lt;/li&gt;
&lt;li&gt;Evaluation and responsible AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the syllabus now available, it’s clear that &lt;strong&gt;AI‑103 is centered on agentic AI&lt;/strong&gt; , not just traditional LLM operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;3. AI‑103 Beta Exam — Registration Still Pending&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Although the exam has been announced, &lt;strong&gt;beta registration is not yet open&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
This creates a unique situation: early learners must prepare using a mix of training content, documentation, and hands‑on practice.&lt;/p&gt;

&lt;p&gt;The newly published syllabus helps clarify the scope, but official learning paths are still limited.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;4. Updated Learning Plan (Now Including Official Syllabus)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;With the syllabus available, I’ve updated my study plan to align with Microsoft’s structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;A. Follow the Official AI‑103 Syllabus&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The syllabus outlines the core domains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure AI Foundry&lt;/li&gt;
&lt;li&gt;Agent development&lt;/li&gt;
&lt;li&gt;Semantic Kernel&lt;/li&gt;
&lt;li&gt;Tool integration&lt;/li&gt;
&lt;li&gt;Multi‑agent orchestration&lt;/li&gt;
&lt;li&gt;Evaluation and monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is now my primary roadmap.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;B. Complete the Available Self‑Paced Modules&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The course page includes a few early modules that reinforce foundational concepts.&lt;br&gt;&lt;br&gt;
These are short but useful for grounding terminology and workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;C. Deep Dive into Azure AI Foundry Documentation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model catalog&lt;/li&gt;
&lt;li&gt;Prompt flow&lt;/li&gt;
&lt;li&gt;Agent Service&lt;/li&gt;
&lt;li&gt;Evaluation tools&lt;/li&gt;
&lt;li&gt;Deployment patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;D. Semantic Kernel GitHub Samples&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Hands‑on practice with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Planners&lt;/li&gt;
&lt;li&gt;Skills&lt;/li&gt;
&lt;li&gt;Connectors&lt;/li&gt;
&lt;li&gt;Multi‑agent orchestration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;E. Build Practical Mini‑Projects&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To internalize the concepts, I’m building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A multi‑agent research assistant&lt;/li&gt;
&lt;li&gt;A tool‑calling enterprise agent&lt;/li&gt;
&lt;li&gt;A workflow‑orchestration agent using SK&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;F. Review Build &amp;amp; Ignite Sessions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;These sessions contain early previews of Microsoft’s agentic architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;5. Suggested Learning Plan for Other AI‑103 Candidates&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you’re also preparing for AI‑103, here’s a simple, effective path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with the &lt;strong&gt;official syllabus&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Complete the &lt;strong&gt;self‑paced modules&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Learn Azure AI Foundry basics&lt;/li&gt;
&lt;li&gt;Build a single agent with Azure AI Agent Service&lt;/li&gt;
&lt;li&gt;Deep‑dive into Semantic Kernel&lt;/li&gt;
&lt;li&gt;Create a multi‑agent solution&lt;/li&gt;
&lt;li&gt;Practice tool integration (including MCP)&lt;/li&gt;
&lt;li&gt;Use Azure’s evaluation tools&lt;/li&gt;
&lt;li&gt;Monitor Microsoft Learn for new content&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sequence mirrors both the training and the exam structure.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Closing Thoughts&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;The March 19 training was a strong introduction to Microsoft’s agentic AI stack and a helpful starting point for &lt;strong&gt;AI‑103 beta&lt;/strong&gt; preparation. With the syllabus now available, I’ve updated my study plan to align with Microsoft’s official direction.&lt;/p&gt;

&lt;p&gt;I’ll continue sharing updates as I progress through the learning materials and as Microsoft releases more content leading up to the exam.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/building-agentic-ai-solutions-with-azure-ai-foundry-my-training-day-review-updated-ai-103-study-plan/" rel="noopener noreferrer"&gt;Building Agentic AI Solutions with Azure AI Foundry — My Training Day Review &amp;amp; Updated AI‑103 Study Plan&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>certification</category>
      <category>learning</category>
    </item>
    <item>
      <title>How I Delivered Zero Trust Security for a Client’s Legacy PHP System — Without Rewrites, Downtime, or Big Costs</title>
      <dc:creator>Jonathan Wong</dc:creator>
      <pubDate>Sun, 05 Apr 2026 02:13:55 +0000</pubDate>
      <link>https://dev.to/jonathan78wong/how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-jj4</link>
      <guid>https://dev.to/jonathan78wong/how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-jj4</guid>
      <description>&lt;p&gt;A practical playbook to implementing Zero Trust architecture using AWS and Cloudflare. Covering edge security, identity controls, and data protection for modern cloud infrastructure.&lt;/p&gt;

&lt;p&gt;As the founder of &lt;a href="https://jonanata.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Jonanata&lt;/strong&gt;&lt;/a&gt;, I often support clients who are growing fast but whose infrastructure hasn’t kept up with modern security expectations. One recent project stands out because it reflects a challenge many founders face:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you bring an existing production system closer to SOC 2 and PCI‑DSS expectations , without rewriting the application, without downtime, and without blowing the budget?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My client had a public‑facing mobile app backed by a legacy PHP API server built on a proprietary framework. It worked, but it wasn’t compliant, and it wasn’t defensible. They were already using AWS and Cloudflare — but only Cloudflare’s free plan. &lt;/p&gt;

&lt;p&gt;The constraints were clear: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No application revamp &lt;/li&gt;
&lt;li&gt;No downtime&lt;/li&gt;
&lt;li&gt;Free or low‑cost solutions only &lt;/li&gt;
&lt;li&gt;Compliance‑aligned security improvements&lt;/li&gt;
&lt;li&gt;Immediate business value &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the story of how I delivered a  &lt;strong&gt;Zero Trust Architecture&lt;/strong&gt;  that strengthened every layer — AWS, Cloudflare, PHP, and Nginx — while keeping the system running and the budget under control. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Why Zero Trust, SOC 2, and PCI‑DSS Matter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before diving into the solution, it’s worth explaining these concepts in business terms. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero Trust Architecture (ZTA)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A modern security model built on one principle:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Never trust anything by default — verify everything.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It protects businesses from: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Credential theft&lt;/li&gt;
&lt;li&gt;Lateral movement inside servers&lt;/li&gt;
&lt;li&gt;Insider threats&lt;/li&gt;
&lt;li&gt;Misconfigurations&lt;/li&gt;
&lt;li&gt;Public exposure &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For founders, Zero Trust means  &lt;strong&gt;reduced risk&lt;/strong&gt; ,  &lt;strong&gt;better investor confidence&lt;/strong&gt; , and  &lt;strong&gt;stronger customer trust&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SOC 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A widely recognized security and operational standard.&lt;br&gt;&lt;br&gt;
It focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access control&lt;/li&gt;
&lt;li&gt;Logging and monitoring&lt;/li&gt;
&lt;li&gt;Network restrictions&lt;/li&gt;
&lt;li&gt;Data protection&lt;/li&gt;
&lt;li&gt;Operational discipline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if you’re not formally audited, aligning with SOC 2 makes your business  &lt;strong&gt;more trustworthy to partners and enterprise clients&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PCI‑DSS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A security standard for systems that handle  &lt;strong&gt;payment‑related data&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
It emphasizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network segmentation&lt;/li&gt;
&lt;li&gt;Least privilege&lt;/li&gt;
&lt;li&gt;Secure coding&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Encryption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if you don’t process payments directly, PCI‑DSS alignment reduces the risk of data breaches and strengthens your compliance posture. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Challenge: Secure a Legacy System Without Rewriting It&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The client’s PHP backend was built on a proprietary framework. Rewriting it would take months and introduce risk. Instead, I designed a solution that  &lt;strong&gt;wraps the existing system in Zero Trust&lt;/strong&gt; , hardens every layer, and enforces strict access control — all without touching core business logic.&lt;/p&gt;

&lt;p&gt;The only additional cost?&lt;br&gt;&lt;br&gt;
&lt;strong&gt;CloudWatch log storage.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything else used AWS native features and Cloudflare’s free plan. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;AWS Layer: Identity‑Based Access and Network Isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. IAM Roles Only — No Stored Keys&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The production EC2 instance uses a dedicated IAM role (role-ec2-production) with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access only to specific S3 buckets&lt;/li&gt;
&lt;li&gt;Access only to the RDS MySQL instance&lt;/li&gt;
&lt;li&gt;Access only to CloudWatch&lt;/li&gt;
&lt;li&gt;All permissions scoped to resource names&lt;/li&gt;
&lt;li&gt;No IAM users, no access keys stored on the server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business &amp;amp; compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No leaked keys, no credential rotation headaches, and full alignment with SOC 2 CC6.1 and PCI‑DSS 7.1. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Private EC2 — No Public IP&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The production EC2 sits behind a new security group (sg-ec2-production) with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No public IP&lt;/li&gt;
&lt;li&gt;No inbound access from the internet&lt;/li&gt;
&lt;li&gt;Only the bastion host can reach it via private IP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business &amp;amp; compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The production server is invisible to attackers.&lt;br&gt;&lt;br&gt;
This satisfies PCI‑DSS 1.2.1 and SOC 2 CC6.6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. RDS: Identity‑Based Database Access&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The MySQL database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts connections only from the production SG&lt;/li&gt;
&lt;li&gt;Uses IAM authentication (no password stored anywhere)&lt;/li&gt;
&lt;li&gt;Generates short‑lived tokens via AWS KMS&lt;/li&gt;
&lt;li&gt;Grants the role-ec2-production only SELECT/INSERT/UPDATE/DELETE &lt;/li&gt;
&lt;li&gt;No public IP and is accessible only inside the VPC &lt;/li&gt;
&lt;li&gt;Is fully encrypted &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business &amp;amp; compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No database passwords to leak.&lt;br&gt;&lt;br&gt;
No over‑privileged accounts.&lt;br&gt;&lt;br&gt;
Meets PCI‑DSS 3.4, 7.2 and SOC 2 CC6.1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. S3: Fully Private With Pre‑Signed URLs&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Block all public access&lt;/li&gt;
&lt;li&gt;Upload/download only via pre‑signed URLs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Keeps all PII and sensitive files private and off the public internet, reducing breach risk and supporting compliance with SOC 2 (CC6.6, CC6.7, CC9.1) and PCI‑DSS (3.4, 7.1, 10.2). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Logging: Fluent Bit + CloudWatch + Logrotate&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logs stored outside the web root&lt;/li&gt;
&lt;li&gt;Fluent Bit ships logs to CloudWatch&lt;/li&gt;
&lt;li&gt;Logrotate deletes rotated logs immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business &amp;amp; compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Centralized, tamper‑resistant logs that satisfy SOC 2 CC7.2 and PCI‑DSS 10.x.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Bastion Host: Controlled, Auditable Access&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only turned on when needed&lt;/li&gt;
&lt;li&gt;Only developer IPs allowed&lt;/li&gt;
&lt;li&gt;Developers authenticate with their own SSH keys&lt;/li&gt;
&lt;li&gt;Developers never see the production private key&lt;/li&gt;
&lt;li&gt;A controlled script handles access to the production EC2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business &amp;amp; compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No shared credentials.&lt;br&gt;&lt;br&gt;
Full accountability.&lt;br&gt;&lt;br&gt;
Meets SOC 2 CC6.3 and PCI‑DSS 8.x.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Cloudflare Layer: Strong Perimeter Security (Free Plan)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even on the free plan, Cloudflare provides powerful security controls when configured correctly. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Cloudflare Tunnel — What It Is and Why It Matters&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Cloudflare Tunnel creates an outbound‑only connection from the EC2 instance to Cloudflare.&lt;br&gt;&lt;br&gt;
This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server is  &lt;strong&gt;never exposed&lt;/strong&gt;  to the public internet&lt;/li&gt;
&lt;li&gt;No open ports&lt;/li&gt;
&lt;li&gt;No public IP&lt;/li&gt;
&lt;li&gt;All traffic passes through Cloudflare’s Zero Trust layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Supports SOC 2 CC6.6 (network segmentation) and PCI‑DSS 1.3 (no direct public access). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. mTLS With Client Certificates&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A security mechanism where both the client and server present certificates, proving their identities before any data is exchanged.&lt;br&gt;&lt;br&gt;
This means: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server to trust the client&lt;/li&gt;
&lt;li&gt;The client to trust the server &lt;/li&gt;
&lt;li&gt;Only devices with valid client certificates can reach the API &lt;/li&gt;
&lt;li&gt;Prevents unauthorized devices, bots, or compromised workloads from connecting to your API &lt;/li&gt;
&lt;li&gt;Eliminates blind trust inside the network and blocks lateral movement &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business &amp;amp; compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Even if someone discovers the tunnel URL, or credentials (JWT) leak they cannot bypass certificate‑based authentication to access the API.&lt;br&gt;&lt;br&gt;
This fulfills SOC 2 CC6.7 (strong authentication) and PCI‑DSS 8.x.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Cloudflare Worker: JWT Validation at the Edge&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Before requests reach the EC2 instance, a Worker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validates the JWT&lt;/li&gt;
&lt;li&gt;Rejects invalid or expired tokens&lt;/li&gt;
&lt;li&gt;Ensures only authenticated traffic reaches the backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business &amp;amp; compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Reduces load on the server and blocks attacks early.&lt;br&gt;&lt;br&gt;
Supports SOC 2 CC7.1 (input validation) and PCI‑DSS 6.5.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;PHP Layer: Hardening Without Rewriting Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even without modifying business logic, we strengthened the runtime environment. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Disable Dangerous Functions&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Functions like exec, system, popen, etc. are disabled. Prevents remote command execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Disable URL File Access&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Prevents remote file inclusion (RFI) attacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Disable legacy PHP features that automatically turn user input into variables&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Attackers can exploit old PHP behaviors such as register_globals and magic_quotes_gpc, which implicitly convert or modify user input. Prevents malicious input from becoming variables and reduces the risk of remote code injection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Hide PHP Version &amp;amp; Disable Error Display&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Prevents attackers from fingerprinting the system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Session Security Hardening&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Protects against session hijacking and fixation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. PDO Everywhere&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Prevents SQL injection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Hardens the PHP runtime without modifying business logic, eliminating high‑risk attack vectors (RCE, SQL injection, session hijacking) and strengthening compliance posture for SOC 2 and PCI‑DSS by enforcing safer defaults, strict input handling, and controlled execution paths. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Nginx Layer: API‑Focused Security Controls&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No directory browsing&lt;/li&gt;
&lt;li&gt;Block multipart uploads&lt;/li&gt;
&lt;li&gt;Enforce correct Host header&lt;/li&gt;
&lt;li&gt;Add HSTS and minimal CSP&lt;/li&gt;
&lt;li&gt;Limit request size&lt;/li&gt;
&lt;li&gt;Block directory traversal&lt;/li&gt;
&lt;li&gt;Block hidden files except .well-known&lt;/li&gt;
&lt;li&gt;Block sensitive files&lt;/li&gt;
&lt;li&gt;Remove version numbers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business &amp;amp; compliance value:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Reduces attack surface and prevents common web vulnerabilities.&lt;br&gt;&lt;br&gt;
Supports PCI‑DSS 6.6 and SOC 2 CC7.1.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Defense in Depth: How Each Layer Protects the Business&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-assets.cosmic.aws.dev%2Fa%2F3BmN7NIq6F0BOUtLBKefhy3RyqR%2FSecu.webp%3FimgSize%3D1000x592" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-assets.cosmic.aws.dev%2Fa%2F3BmN7NIq6F0BOUtLBKefhy3RyqR%2FSecu.webp%3FimgSize%3D1000x592" title="How Each Layer Protects the Business " alt="How Each Layer Protects the Business" width="1000" height="592"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;How Each Layer Protects the Business&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is  &lt;strong&gt;Zero Trust in practice&lt;/strong&gt; : every layer assumes nothing is safe and verifies everything. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Outcome: Compliance‑Aligned Security Without Rewrites or Cost Overruns&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By applying Zero Trust principles across AWS, Cloudflare, PHP, and Nginx, we delivered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A secure, compliant, modernized backend&lt;/li&gt;
&lt;li&gt;No application rewrite&lt;/li&gt;
&lt;li&gt;No downtime&lt;/li&gt;
&lt;li&gt;No expensive tools&lt;/li&gt;
&lt;li&gt;Only CloudWatch storage cost&lt;/li&gt;
&lt;li&gt;A defensible security posture aligned with SOC 2 and PCI‑DSS &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For the client, this meant:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stronger trust with users&lt;/li&gt;
&lt;li&gt;Better readiness for enterprise partnerships&lt;/li&gt;
&lt;li&gt;Reduced operational risk&lt;/li&gt;
&lt;li&gt;A future‑proof foundation for growth&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A compliance‑aligned architecture that enables expansion into regulated or restricted markets without major rework&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A security posture that meets the expectations of partners operating in highly controlled industries and jurisdictions&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the kind of security upgrade that delivers  &lt;strong&gt;real business value&lt;/strong&gt; , not just technical improvements. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What’s Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ll publish a deeper technical breakdown on my next AWS Builder Center article, including full configuration examples and source code in my GitHub repository. &lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://blog.jonanata.com/how-i-delivered-zero-trust-security-for-a-clients-legacy-php-system-without-rewrites-downtime-or-big-costs/" rel="noopener noreferrer"&gt;How I Delivered Zero Trust Security for a Client’s Legacy PHP System — Without Rewrites, Downtime, or Big Costs&lt;/a&gt; appeared first on &lt;a href="https://blog.jonanata.com" rel="noopener noreferrer"&gt;Behind the Build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cybersecurity</category>
      <category>zerotrust</category>
    </item>
  </channel>
</rss>
