<?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: Chanuth Abeynayake</title>
    <description>The latest articles on DEV Community by Chanuth Abeynayake (@chanuth_412).</description>
    <link>https://dev.to/chanuth_412</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3909085%2Fda3d2ebd-d8e7-4983-83b7-b516df41a69f.jpeg</url>
      <title>DEV Community: Chanuth Abeynayake</title>
      <link>https://dev.to/chanuth_412</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chanuth_412"/>
    <language>en</language>
    <item>
      <title>Agentic Pipeline to convert AWS infrastructure to Terraform</title>
      <dc:creator>Chanuth Abeynayake</dc:creator>
      <pubDate>Mon, 13 Jul 2026 15:58:35 +0000</pubDate>
      <link>https://dev.to/chanuth_412/agentic-pipeline-to-convert-aws-infrastructure-to-terraform-29k4</link>
      <guid>https://dev.to/chanuth_412/agentic-pipeline-to-convert-aws-infrastructure-to-terraform-29k4</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwukb23fi2p7u7hf3p1w2.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwukb23fi2p7u7hf3p1w2.png" alt=" " width="800" height="415"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;AWS architecture of the TF-Agentic-Engine&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Technical Analysis of the TF-Agentic-Engine
&lt;/h1&gt;

&lt;p&gt;The &lt;strong&gt;TF-Agentic-Engine&lt;/strong&gt; converts an existing AWS environment into Terraform code. Its main purpose is to support two tasks: bringing existing AWS resources under Terraform management and creating a reusable copy of an existing environment.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Import Mode&lt;/strong&gt;, the system creates Terraform resource blocks and import definitions using the real IDs collected from AWS. It should only generate resources that were found during the scan.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg9sevliz9t68amww0tkf.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg9sevliz9t68amww0tkf.png" alt=" " width="800" height="682"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Import Mode output with fixed AWS resource values from the scanned environment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Clone Mode&lt;/strong&gt;, the system converts the same infrastructure into reusable Terraform by replacing account-specific values, names, CIDR ranges and other fixed settings with variables. This allows the environment to be recreated in another AWS account, region or project. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvvvhaqbbxt46y6n32ls5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvvvhaqbbxt46y6n32ls5.png" alt=" " width="800" height="677"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Clone Mode output using Terraform variables to create a reusable infrastructure configuration.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resource Discovery
&lt;/h2&gt;

&lt;p&gt;The process starts in &lt;code&gt;main.py&lt;/code&gt;, which controls the full execution. It starts the AWS scan, creates the initial system state and runs the LangGraph workflow.&lt;/p&gt;

&lt;p&gt;The AWS discovery logic is mainly located in &lt;code&gt;src/aws_client.py&lt;/code&gt;. It uses &lt;code&gt;boto3&lt;/code&gt; to collect details about resources such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPCs and subnets&lt;/li&gt;
&lt;li&gt;Route tables and gateways&lt;/li&gt;
&lt;li&gt;EC2 instances&lt;/li&gt;
&lt;li&gt;Security groups&lt;/li&gt;
&lt;li&gt;RDS databases and DB subnet groups&lt;/li&gt;
&lt;li&gt;S3 buckets and DynamoDB tables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For local testing, the same discovery layer can use &lt;code&gt;moto&lt;/code&gt; instead of connecting to a real AWS account. This makes it possible to test the pipeline without creating real cloud resources. &lt;/p&gt;

&lt;p&gt;AWS API responses contain many values that are not required in Terraform. The system removes runtime details and keeps configuration values that are needed to describe the infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure Graph
&lt;/h2&gt;

&lt;p&gt;After discovery, the system converts the resource data into a graph.&lt;/p&gt;

&lt;p&gt;Each AWS resource is stored as a node. The connection between two resources is stored as an edge. For example, an edge can show that a subnet belongs to a VPC, an EC2 instance uses a subnet or an RDS database uses a DB subnet group.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;compile_infrastructure_graph()&lt;/code&gt; function creates this graph from the scanned AWS data. This is important because the resource relationships come from AWS IDs rather than being guessed by the language model.&lt;/p&gt;

&lt;p&gt;The graph also helps with incomplete AWS data. For example, when an RDS instance exists but its subnet-group information is missing, the system can create a DB subnet-group entry and connect it to the discovered private subnets. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzsyiby6h07c7ylnse8d3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzsyiby6h07c7ylnse8d3.png" alt=" " width="799" height="462"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Generated architecture diagram showing the discovered AWS resources and their dependency relationships.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  LangGraph Workflow
&lt;/h2&gt;

&lt;p&gt;The Terraform generation process is controlled by &lt;code&gt;src/agent.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;LangGraph runs the system in a fixed order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Network generation&lt;/li&gt;
&lt;li&gt;Security generation&lt;/li&gt;
&lt;li&gt;Compute generation&lt;/li&gt;
&lt;li&gt;Data generation&lt;/li&gt;
&lt;li&gt;Terraform validation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each stage is implemented inside &lt;code&gt;src/nodes.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Network Node&lt;/strong&gt; creates Terraform for VPCs, subnets, gateways and route tables.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Security Node&lt;/strong&gt; creates security groups, rules, IAM roles and policies.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Compute Node&lt;/strong&gt; creates EC2 and related compute resources.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Data Node&lt;/strong&gt; creates RDS, S3 and DynamoDB resources.&lt;/p&gt;

&lt;p&gt;Separating the resources this way keeps each generation request smaller and reduces the chance of networking, security and database code being mixed together. The output is normally separated into files such as &lt;code&gt;network.tf&lt;/code&gt;, &lt;code&gt;security.tf&lt;/code&gt;, &lt;code&gt;compute.tf&lt;/code&gt; and &lt;code&gt;data.tf&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  GraphState
&lt;/h2&gt;

&lt;p&gt;The nodes share information through a structure called &lt;code&gt;GraphState&lt;/code&gt;, defined in &lt;code&gt;src/state.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;GraphState stores the selected mode, scanned AWS data, infrastructure graph, generated Terraform sections, current phase, validation errors, retry count and final validation result.&lt;/p&gt;

&lt;p&gt;This allows every node to work with the latest version of the infrastructure and generated code. It also allows the validation stage to send errors back into the generation process. &lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform Validation and Repair
&lt;/h2&gt;

&lt;p&gt;After all Terraform files are generated, the Validation Node runs the real Terraform CLI.&lt;/p&gt;

&lt;p&gt;It checks the files for invalid syntax, unsupported provider arguments, missing references and incorrect resource structures. A temporary provider file can be added so that the Terraform configuration can be checked locally without using real AWS credentials.&lt;/p&gt;

&lt;p&gt;When validation fails, the error is saved in GraphState and the generation workflow runs again. The retry count prevents the system from entering an endless loop.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F938xas1e00ootlx1mw4c.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F938xas1e00ootlx1mw4c.png" alt=" " width="799" height="372"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Execution log showing a failed Terraform validation, automatic retry, and successful validation on the second run (As an example).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The project also includes fixed Python-based corrections for common language-model errors.&lt;/p&gt;

&lt;p&gt;The S3 correction logic removes arguments that are no longer valid inside the main S3 bucket resource.&lt;/p&gt;

&lt;p&gt;The RDS correction logic removes incorrectly placed subnet IDs, creates a proper DB subnet group and links it to the database.&lt;/p&gt;

&lt;p&gt;The security-group correction logic fixes invalid ingress and egress block formats.&lt;/p&gt;

&lt;p&gt;The template-protection logic escapes braces found in Terraform and JSON error messages so that LangChain does not treat them as prompt variables. &lt;/p&gt;

&lt;p&gt;These corrections are handled by normal code because common Terraform mistakes are easier and safer to fix with fixed rules than by repeatedly asking the model to rewrite everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Outputs
&lt;/h2&gt;

&lt;p&gt;When Terraform validation succeeds, the system produces the final Terraform files and architecture documents.&lt;/p&gt;

&lt;p&gt;The same infrastructure graph is used to generate a PNG image and an editable Draw.io file. This keeps the Terraform output and architecture diagram based on the same resource relationships.&lt;/p&gt;

&lt;p&gt;The final output includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform files&lt;/li&gt;
&lt;li&gt;Infrastructure graph data&lt;/li&gt;
&lt;li&gt;Validation results&lt;/li&gt;
&lt;li&gt;PNG architecture image&lt;/li&gt;
&lt;li&gt;Editable Draw.io file&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AWS Infrastructure Used by the System
&lt;/h2&gt;

&lt;p&gt;In the AWS architecture, a Discovery Lambda can start an on-demand scan. AWS Config and EventBridge can also detect infrastructure changes and start the process automatically.&lt;/p&gt;

&lt;p&gt;Amazon S3 stores the scanned graph, generated Terraform files, diagrams and logs.&lt;/p&gt;

&lt;p&gt;Amazon Neptune Serverless is used temporarily to store and query the infrastructure graph. It can check multi-level relationships, such as the connection from an EC2 instance to its subnet, VPC and security group. Neptune does not create the relationships; the Python graph compiler creates them first.&lt;/p&gt;

&lt;p&gt;Amazon Bedrock provides the language model used for Terraform generation and repair in the AWS version of the system. LangGraph still controls the order of the generation and validation steps.&lt;/p&gt;

&lt;p&gt;After validation finishes and the outputs are stored, the temporary Neptune environment is removed. This prevents the graph database from remaining active when the system is not running.&lt;/p&gt;

&lt;p&gt;The system therefore separates the work clearly: Python reads and organizes the AWS environment, LangGraph controls the stages, the language model writes Terraform, and Terraform checks whether the generated code is valid.&lt;/p&gt;

&lt;p&gt;(The testing was done in an HPC in the university with the local LLM models gemma3:27b and llama3:70b instead of testing using AWS Bedrock for cost efficiency).&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>aws</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>Running FreeIPA on Ubuntu Using Podman – Part 2: Step-by-Step Deployment</title>
      <dc:creator>Chanuth Abeynayake</dc:creator>
      <pubDate>Thu, 28 May 2026 15:56:37 +0000</pubDate>
      <link>https://dev.to/chanuth_412/running-freeipa-on-ubuntu-using-podman-part-2-step-by-step-deployment-3ngc</link>
      <guid>https://dev.to/chanuth_412/running-freeipa-on-ubuntu-using-podman-part-2-step-by-step-deployment-3ngc</guid>
      <description>&lt;p&gt;This is the full guide of step by step implementation to launch, prepare and configure the FreeIPA server inside a Podman container.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Accessing the FreeIPA web dashboard with HTTPS.&lt;/em&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%2Fg1nvbgr6g2slgrpzwxop.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%2Fg1nvbgr6g2slgrpzwxop.png" alt="Accessing the FreeIPA administrative web interface securely via HTTPS" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Allow Web Ports&lt;/strong&gt;&lt;br&gt;
FreeIPA uses normal web ports such as 80 and 443. Sometimes, Linux does not allow normal container processes to use these ports directly, so allow the system to use ports starting from 80.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.ip_unprivileged_port_start&lt;span class="o"&gt;=&lt;/span&gt;80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Start the Container&lt;/strong&gt;&lt;br&gt;
FreeIPA needs some system services like LDAP and Kerberos, which systemd manages, so we start the container with systemd enabled.&lt;br&gt;
So when we start the container, we must enable systemd inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;podman run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; freeipa-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--systemd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SYS_ADMIN &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 443:443 &lt;span class="nt"&gt;-p&lt;/span&gt; 80:80 &lt;span class="nt"&gt;-p&lt;/span&gt; 389:389 &lt;span class="nt"&gt;-p&lt;/span&gt; 636:636 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 88:88 &lt;span class="nt"&gt;-p&lt;/span&gt; 464:464 &lt;span class="nt"&gt;-p&lt;/span&gt; 88:88/udp &lt;span class="nt"&gt;-p&lt;/span&gt; 464:464/udp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-h&lt;/span&gt; ipa.example.edu&lt;span class="se"&gt;\&lt;/span&gt;
  almalinux:9 /usr/sbin/init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In here,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;--systemd=always&lt;/em&gt; allows systemd to run inside the container.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;--restart always makes&lt;/em&gt; sure the container starts again after a reboot or crash.&lt;/li&gt;
&lt;li&gt;The -p values open the ports needed by FreeIPA.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Enter the Container&lt;/strong&gt;&lt;br&gt;
To install and configure FreeIPA it needs to go inside the running container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;podman &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; freeipa-server /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Prepare the Container&lt;/strong&gt;&lt;br&gt;
As said in the previous blog, the AlmaLinux image is very minimal. So for those missing folders and configuration files needed, it should be created those required folders, install packages, restore LDAP schema files, and set some security options&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Install foundational packages&lt;/span&gt;
dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ipa-server ipa-server-dns

&lt;span class="c"&gt;# 2. Reconstruct missing directory structures&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /etc/dirsrv /etc/sysconfig /etc/tmpfiles.d /etc/pkcs11
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/sysconfig /etc/tmpfiles.d /etc/pkcs11/modules /etc/dirsrv/config
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/lib/ipa/sysrestore /var/lib/ipa-client/sysrestore /var/log/dirsrv

&lt;span class="c"&gt;# 3. Restore missing LDAP schemas and configure cryptographic policies&lt;/span&gt;
dnf reinstall &lt;span class="nt"&gt;-y&lt;/span&gt; 389-ds-base &lt;span class="nt"&gt;--setopt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;tsflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;noscripts &lt;span class="nt"&gt;--setopt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;sslverify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false
echo&lt;/span&gt; &lt;span class="s2"&gt;"module: /usr/lib64/libsofthsm2.so"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/pkcs11/modules/softhsm2.module
update-crypto-policies &lt;span class="nt"&gt;--set&lt;/span&gt; LEGACY

&lt;span class="c"&gt;# 4. Generate necessary Kerberos configuration placeholders&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; /etc/sysconfig/krb5kdc /etc/sysconfig/kadmin
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 /etc/sysconfig/krb5kdc /etc/sysconfig/kadmin

&lt;span class="c"&gt;# 5. Map local networking and identity alignment&lt;/span&gt;
&lt;span class="nv"&gt;MY_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="nv"&gt;$MY_IP&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;ipa.example.edu ipa"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/hosts

&lt;span class="c"&gt;# 6. Set Java and Certificate Authority environment variables&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-Djava.security.egd=file:/dev/./urandom -Djava.net.preferIPv4Stack=true"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NSS_SDB_USE_CACHE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Install FreeIPA&lt;/strong&gt;&lt;br&gt;
Then the FreeIPA installer can be run in unattended mode as the container is now ready.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ipa-server-install &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--unattended&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;example.edu &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--realm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;EXAMPLE.EDU &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ds-password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;YOUR_DS_PASSWORD&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--admin-password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;YOUR_ADMIN_PASSWORD&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-ntp&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-host-dns&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-pkinit&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--skip-mem-check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace YOUR_DS_PASSWORD and YOUR_ADMIN_PASSWORD with your own secure passwords.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Access the Web Interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Local Access&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;If the container is on a local system, add this line to the hosts file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="m"&gt;127&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="n"&gt;ipa&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;edu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://ipa.example.edu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Username: admin&lt;/li&gt;
&lt;li&gt;Password: YOUR_ADMIN_PASSWORD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;u&gt;Remote Access via SSH Tunnel&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;If the container is on a remote server, use an SSH tunnel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-L&lt;/span&gt; 443:localhost:443 &lt;span class="nt"&gt;-L&lt;/span&gt; 80:localhost:80 user@remote-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the same hosts line locally and open the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7: Verify Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside the container, verify that FreeIPA and Kerberos are working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kinit admin
ipa user-show admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 8: Make Port Change Permanent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The earlier port change will reset after a reboot. To keep it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"net.ipv4.ip_unprivileged_port_start=80"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/sysctl.conf
&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Checking FreeIPA Service Status&lt;/em&gt;&lt;br&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%2F02y35ggd321797980cgq.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%2F02y35ggd321797980cgq.png" alt="Checking FreeIPA Service Status" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Testing Kerberos Authentication&lt;/em&gt;&lt;br&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%2Fesc8r7xzx3nmuzbklty6.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%2Fesc8r7xzx3nmuzbklty6.png" alt="Testing Kerberos Authentication" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Running FreeIPA on Ubuntu Using Podman – Part 1: Overview</title>
      <dc:creator>Chanuth Abeynayake</dc:creator>
      <pubDate>Sat, 16 May 2026 19:14:12 +0000</pubDate>
      <link>https://dev.to/chanuth_412/running-freeipa-on-ubuntu-using-podman-mdo</link>
      <guid>https://dev.to/chanuth_412/running-freeipa-on-ubuntu-using-podman-mdo</guid>
      <description>&lt;p&gt;There was this consideration to replace OpenLDAP with FreeIPA in the university authentication infrastructure. The main reason for this was to improve the security in the system and to give more identity management features which the OpenLDAP lacks.&lt;/p&gt;

&lt;p&gt;Some of these features are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ticket based authentication instead of sending the passwords over the network constantly.&lt;/li&gt;
&lt;li&gt;Centralized user and authentication management.&lt;/li&gt;
&lt;li&gt;Certificate for verifying systems and services     etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The issue was that the existing university infrastructure is mainly based on Ubuntu Linux systems, but FreeIPA is mainly designed for Red Hat-based systems like AlmaLinux , Centos etc. Therefore direct installation of FreeIPA was not a choice here. &lt;br&gt;
Therefore, this system needed to be deployed in a containerized manner.&lt;/p&gt;

&lt;p&gt;So this article mainly focuses on the decisions made, issues that came across with and what solutions were used to fix them.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introduction to FreeIPA&lt;/strong&gt;&lt;br&gt;
FreeIPA is an integrated identity management system used in Linux environments but was mainly developed in Red Hat based systems. It is integrated of set of services as,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;389 Directory Server (LDAP): Stores the identity data.&lt;/li&gt;
&lt;li&gt;MIT Kerberos (KDC):Handles the tickets for SSO authentication&lt;/li&gt;
&lt;li&gt;Apache (HTTPD): The administrative web dashboard and API.&lt;/li&gt;
&lt;li&gt;Dogtag Certificate System (PKI):The Certification Authority (CA) for the secure communication between systems.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Service Startup Order&lt;/strong&gt;&lt;br&gt;
These services need to be started in the specific order given above. As the LDAP has the initial configuration settings for other services on it, it needs to start first, then KDC as it needs LDAP data for authentication, next the dashboard and finally the PKI (Dogtag CA) service as it depends on other main services being ready. &lt;/p&gt;

&lt;p&gt;If any of the services did not start with this order the whole system crashes. For this order to be preserved it uses the systemd in here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform Compatibility Issues&lt;/strong&gt;&lt;br&gt;
As said above direct installation of FreeIPA on to the existing ubuntu systems was not a solution. &lt;/p&gt;

&lt;p&gt;The Centos Stream 9 container image lacked the required binaries, so it was unable to even start. &lt;/p&gt;

&lt;p&gt;But Almalinux was compatible with FreeIPA. However, changing all the existing systems to Almalinux was not a solution also. That is why it was thought to use a containerization appproach to run an Almalinux environment while still keeping the existing Ubuntu infrastructure unchanged.&lt;/p&gt;

&lt;p&gt;CentOS 9 image does not start properly&lt;br&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%2Fw583fluyxfcp8b11j3br.jpeg" 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%2Fw583fluyxfcp8b11j3br.jpeg" alt="CentOS 9 image does not start properly" width="661" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure 1: Docker and Cgroups v2 Incompatibility&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;systemd and cgroups&lt;/strong&gt; - When systemd runs as the PID1 inside a container it is responsible for starting and managing the other services there. It uses the cgroups to control resources like memory and CPU. To do that systemd needs read, write access to cgroup file system at &lt;em&gt;/sys/fs/cgroup&lt;/em&gt; .&lt;/p&gt;

&lt;p&gt;Modern Ubuntu use cgroups v2, which is stricter. This causes Docker container to not get proper access to system resources causing systemd to fail. &lt;/p&gt;

&lt;p&gt;So as the systemd is the PID1, failing it prevents the other services to start, causing the container to run but without running any FreeIPA services in it.&lt;/p&gt;

&lt;p&gt;Docker blocks system access needed for systemd to run properly&lt;br&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%2Flj79byu12817vk47jtpt.jpeg" 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%2Flj79byu12817vk47jtpt.jpeg" alt="Docker blocks system access needed for systemd to run properly" width="697" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There were many attempts to fix this issue by changing cgroup isolation settings using flags like &lt;em&gt;--cgroupns=host&lt;/em&gt; etc ,but nothing worked.&lt;br&gt;
Similar outcomes were also obtained by other attempts like these. &lt;a href="https://github.com/moby/moby/issues/16238" rel="noopener noreferrer"&gt;https://github.com/moby/moby/issues/16238&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transition to Podman&lt;/strong&gt;&lt;br&gt;
To solve these issues, Podman was thought to be used considering 2 main reasons. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;systemd support - As podman supports systemd-based containers directly, it automatically configures the container environment to run systemd correctly.&lt;/li&gt;
&lt;li&gt;Compatibility - As Podman fits better with the same ecosystem use by FreeIPA and AlmaLinux as it was also made by Red Hat ecosystem. This reduced the runtime issues.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Failure 2: CA Initialization in Pre-packaged Images&lt;/strong&gt;&lt;br&gt;
Then when tried to get an official container image of FreeIPA to be run in this, it failed during the Certificate Authority(CA) setup.&lt;/p&gt;

&lt;p&gt;This was because the official container images are built to be minimal. So some parts of the operating system environments were removed and simplified. &lt;/p&gt;

&lt;p&gt;Specially, the default directory folders and the services that auto-create them were missing. These couldn't be created manually during the installation as FreeIPA is a tightly coupled package.(To be installed as a complete system)&lt;/p&gt;

&lt;p&gt;Due to those missing directories, CA setup failed, causing no certificates to be created. As LDAP needs certificates to have secure connections, LDAP fails, failing the whole system.&lt;/p&gt;

&lt;p&gt;FreeIPA CA setup failed because the container was incomplete&lt;br&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%2Fgw6t1sjn5uqjjy0ntbqw.jpeg" 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%2Fgw6t1sjn5uqjjy0ntbqw.jpeg" alt="FreeIPA CA setup failed because the container was incomplete" width="700" height="734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Controlled Setup Process&lt;/strong&gt;&lt;br&gt;
Because important directories, security settings, and LDAP schema files needed for installation were missing, the setup failed as said above.&lt;br&gt;
As a solution then a clean AlmaLinux 9 base image was obtained and it was prepared for required conditions before running the FreeIPA installer inside it.&lt;/p&gt;

&lt;p&gt;Main preparations done was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating the necessary filesystems&lt;/li&gt;
&lt;li&gt;Temporarily allowing security policies for CA initialization&lt;/li&gt;
&lt;li&gt;Restoring the missing LDAP schema files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After doing these steps, the FreeIPA installation was completed successfully and the system ran successfully after that.&lt;/p&gt;

&lt;p&gt;All services in the FreeIPA are running properly now&lt;br&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%2Fz7t9tq5kaftv4w96wrb1.jpeg" 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%2Fz7t9tq5kaftv4w96wrb1.jpeg" alt="All services in the FreeIPA are running properly here" width="647" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As this is a series of articles, in the next part a full guide on starting FreeIPA in a Podman container, including all the preparation steps will be provided.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>ubuntu</category>
      <category>documentation</category>
    </item>
  </channel>
</rss>
