Files
infra-automation/TASKS_WEEK_48.md
ansible 0ae2b2550d Create Week 48 executable task plan
Week 48 Focus: Repository Separation & CI/CD Foundation

Key Objectives:
- Create separate ansible-inventories repository (PUBLIC)
- Create separate secrets repository (PRIVATE)
- Implement Gitea Actions CI/CD workflows
- Apply Docker security hardening improvements

Tasks:
1. Repository Separation (P0/P1):
   - ansible-inventories: Public repo with dynamic inventories
   - secrets: Private repo for SSH keys and vaults
   - Git submodule integration for both

2. CI/CD Pipeline (P1):
   - Gitea Actions workflows for ansible-lint
   - YAML validation workflow
   - Automated testing on push/PR

3. Docker Security (P1):
   - User namespace remapping implementation
   - Resource limits for all containers
   - Image version pinning audit
   - Comprehensive hardening guide

4. Documentation (P2):
   - Submodule workflow guide
   - CI/CD setup documentation
   - Docker hardening guide

Following CLAUDE.md guidelines for repository structure:
- inventories: Public repository
- secrets: Private repository
- Main repo: Public with submodules

Timeline: Nov 18-24, 2025
Builds on: Week 47 git authentication and infrastructure recovery

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 14:28:57 +01:00

20 KiB

Week 48 - Executable Task Plan

Week: November 18-24, 2025 Focus: Repository Separation & CI/CD Foundation Status: 🟢 PLANNED


Overview

Week 48 focuses on establishing proper repository structure and beginning CI/CD pipeline implementation. This builds on Week 47's completion of git authentication and infrastructure recovery.

Goals:

  • Separate inventories into dedicated repository
  • Separate secrets into dedicated private repository
  • Begin CI/CD pipeline setup (Gitea Actions)
  • Improve Docker container security

Dependencies Resolved:

  • Git SSH authentication working (Week 47)
  • Gitea repository operational (Week 47)
  • SSH key management documented (Week 47)

Week 48 Status

Progress: Not Started Completed Tasks: 0/8 Blocked Tasks: 0 At Risk: 0


Daily Breakdown

Monday, Nov 18 (Day 1)

Task 1.1: Create Separate Inventories Repository [P1 - HIGH]

Priority: P1 - HIGH Estimated Time: 2-3 hours Status: 🔴 NOT STARTED Dependencies: Git SSH authentication ( completed)

Objective: Create dedicated public repository for Ansible inventories following CLAUDE.md guidelines

Issue:

  • Current inventories mixed with main codebase
  • CLAUDE.md requires: ./inventories shall be kept in a public git repository
  • Need separation for security and modularity

Execution Steps:

# Step 1: Create inventories repository on Gitea
cat > /tmp/create_inventories_repo.sh << 'SCRIPT'
#!/bin/bash
GITEA_USER="ansible@mymx.me"
GITEA_PASS='79,;,metOND'
GITEA_URL="https://git.mymx.me"

curl -s -X POST "${GITEA_URL}/api/v1/user/repos" \
  -u "${GITEA_USER}:${GITEA_PASS}" \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ansible-inventories",
    "description": "Ansible dynamic inventories and host configurations (PUBLIC)",
    "private": false,
    "auto_init": false,
    "default_branch": "master",
    "trust_model": "default"
  }'
SCRIPT
chmod +x /tmp/create_inventories_repo.sh
/tmp/create_inventories_repo.sh

# Step 2: Create local inventories repository structure
mkdir -p ../ansible-inventories
cd ../ansible-inventories
git init

# Step 3: Create directory structure
mkdir -p {production,staging,development}/{group_vars,host_vars}
mkdir -p production/inventory_plugins
mkdir -p docs

# Step 4: Create README
cat > README.md << 'EOF'
# Ansible Inventories

Dynamic inventory configurations for Ansible infrastructure automation.

## Structure

. ├── production/ # Production environment │ ├── libvirt.yml # Libvirt dynamic inventory │ ├── group_vars/ # Group variables │ └── host_vars/ # Host-specific variables ├── staging/ # Staging environment └── development/ # Development environment


## Usage

```bash
# List production inventory
ansible-inventory -i production/libvirt.yml --list

# Test connectivity
ansible all -i production/libvirt.yml -m ping

Step 5: Copy current inventory configuration

cp ../infra-automation/inventories/libvirt.yml production/ cp -r ../infra-automation/group_vars production/ 2>/dev/null || true cp -r ../infra-automation/host_vars production/ 2>/dev/null || true

Step 6: Create .gitignore

cat > .gitignore << 'EOF' *.retry *.pyc pycache/ .vscode/ .idea/ *.swp *~ .DS_Store EOF

Step 7: Initial commit

git add . git commit -m "Initial commit: Dynamic inventory structure

  • Production/staging/development environment structure
  • Libvirt dynamic inventory configuration
  • Group and host variables
  • Documentation

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com"

Step 8: Add remote and push

git remote add origin ssh://git@git.mymx.me:2222/ansible/ansible-inventories.git git push -u origin master

Step 9: Update main repository to use inventory submodule

cd ../infra-automation git submodule add ssh://git@git.mymx.me:2222/ansible/ansible-inventories.git inventories git commit -m "Add inventories as git submodule" git push origin master


**Acceptance Criteria:**
- [ ] ansible-inventories repository created on Gitea
- [ ] Repository structure follows CLAUDE.md requirements
- [ ] Production inventory functional
- [ ] README documentation complete
- [ ] Submodule linked in main repository
- [ ] Can execute ansible commands using new inventory

**Deliverables:**
- [ ] ansible-inventories repository (public)
- [ ] Production libvirt inventory functional
- [ ] Submodule integration in infra-automation
- [ ] Documentation: docs/inventory-structure.md

**Rollback Plan:**
- Repository can be deleted if issues arise
- Main repository unaffected until submodule added
- Can revert submodule commit if needed

---

#### Task 1.2: Create Secrets Repository [P0 - CRITICAL]

**Priority:** P0 - CRITICAL
**Estimated Time:** 1-2 hours
**Status:** 🔴 NOT STARTED
**Dependencies:** Git SSH authentication (✅ completed)

**Objective:** Create dedicated PRIVATE repository for secrets following CLAUDE.md guidelines

**Issue:**
- secrets/ directory currently in main repository
- CLAUDE.md requires: `./secrets` shall be kept in a *private* git repository
- Security risk having secrets in public/main repo

**Execution Steps:**
```bash
# Step 1: Create private secrets repository on Gitea
cat > /tmp/create_secrets_repo.sh << 'SCRIPT'
#!/bin/bash
GITEA_USER="ansible@mymx.me"
GITEA_PASS='79,;,metOND'
GITEA_URL="https://git.mymx.me"

curl -s -X POST "${GITEA_URL}/api/v1/user/repos" \
  -u "${GITEA_USER}:${GITEA_PASS}" \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "secrets",
    "description": "Ansible secrets and vault files (PRIVATE - DO NOT MAKE PUBLIC)",
    "private": true,
    "auto_init": false,
    "default_branch": "master",
    "trust_model": "default"
  }'
SCRIPT
chmod +x /tmp/create_secrets_repo.sh
/tmp/create_secrets_repo.sh

# Step 2: Initialize secrets as separate git repository
cd secrets
git init

# Step 3: Create README with security warnings
cat > README.md << 'EOF'
# Ansible Secrets Repository

⚠️ **PRIVATE REPOSITORY - CONTAINS SENSITIVE DATA**

This repository contains Ansible Vault files, SSH keys, and other secrets.

## ⚠️ Security Guidelines

- **NEVER** make this repository public
- **NEVER** commit unencrypted secrets
- **ALWAYS** use Ansible Vault for sensitive data
- **ROTATE** SSH keys and passwords regularly
- **REVIEW** access logs periodically

## Structure

. ├── ssh/ # SSH keys │ ├── ansible # Main automation key │ ├── ansible.pub │ └── README.md # Key documentation with passphrases ├── machines/ # Machine-specific secrets └── vaults/ # Ansible vault files ├── production.yml └── staging.yml


## Usage

```bash
# View vault contents
ansible-vault view vaults/production.yml

# Edit vault
ansible-vault edit vaults/production.yml

# Encrypt new file
ansible-vault encrypt newfile.yml

Step 4: Create .gitignore to prevent accidental commits

cat > .gitignore << 'EOF'

Prevent committing unencrypted sensitive files

*.pem .key !.pub *_rsa *_dsa ecdsa _ed25519 !.pub

Temporary files

*.tmp *.bak *~ .DS_Store

Editor files

.vscode/ .idea/ *.swp EOF

Step 5: Create vault password file placeholder (not committed)

echo "# Create .vault_password file with vault password (NOT committed to git)" > .vault_password.example

Step 6: Initial commit

git add README.md .gitignore .vault_password.example ssh/ git commit -m "Initial commit: Secrets repository structure

  • SSH keys directory with ansible automation key
  • Vault structure for production/staging
  • Security guidelines documentation
  • .gitignore to prevent accidental secret commits

⚠️ PRIVATE REPOSITORY - DO NOT MAKE PUBLIC

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com"

Step 7: Add remote and push

git remote add origin ssh://git@git.mymx.me:2222/ansible/secrets.git git push -u origin master

Step 8: Update main repository to use secrets submodule

cd .. git submodule add ssh://git@git.mymx.me:2222/ansible/secrets.git secrets git commit -m "Add secrets as git submodule (private)" git push origin master

Step 9: Verify secrets repository is PRIVATE

curl -s -X GET "https://git.mymx.me/api/v1/repos/ansible/secrets" | jq '.private'

Should return: true


**Acceptance Criteria:**
- [ ] secrets repository created and marked PRIVATE
- [ ] Verified repository is not publicly accessible
- [ ] SSH keys preserved and documented
- [ ] README with security warnings present
- [ ] .gitignore prevents accidental commits
- [ ] Submodule linked in main repository
- [ ] Can access secrets via submodule

**Deliverables:**
- [ ] secrets repository (PRIVATE on Gitea)
- [ ] Verified private access controls
- [ ] Submodule integration in infra-automation
- [ ] Security documentation

**Rollback Plan:**
- Can delete repository if setup fails
- SSH keys backed up before migration
- Main repository unaffected until submodule added

**Security Notes:**
- ⚠️ Verify repository is PRIVATE before pushing
- ⚠️ Never commit unencrypted secrets
- ⚠️ Test access controls after creation
- ⚠️ Document who has access to repository

---

### Tuesday, Nov 19 (Day 2)

#### Task 2.1: Setup Gitea Actions Workflow [P1 - HIGH]

**Priority:** P1 - HIGH
**Estimated Time:** 3-4 hours
**Status:** 🔴 NOT STARTED
**Dependencies:** Repository structure (Task 1.1, 1.2)

**Objective:** Implement CI/CD pipeline using Gitea Actions for automated testing

**Execution Steps:**
```bash
# Step 1: Create .gitea/workflows directory
mkdir -p .gitea/workflows

# Step 2: Create ansible-lint workflow
cat > .gitea/workflows/ansible-lint.yml << 'EOF'
name: Ansible Lint

on:
  push:
    branches: [ master, develop ]
  pull_request:
    branches: [ master ]

jobs:
  lint:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          submodules: false  # Don't checkout secrets

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install ansible-core ansible-lint

      - name: Run ansible-lint
        run: |
          ansible-lint playbooks/*.yml || true
          ansible-lint roles/*/tasks/*.yml || true

      - name: Syntax check all playbooks
        run: |
          for playbook in playbooks/*.yml; do
            ansible-playbook --syntax-check "$playbook" || true
          done
EOF

# Step 3: Create YAML validation workflow
cat > .gitea/workflows/yaml-validate.yml << 'EOF'
name: YAML Validation

on:
  push:
    branches: [ master, develop ]
  pull_request:
    branches: [ master ]

jobs:
  validate:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          submodules: false

      - name: Install yamllint
        run: |
          pip install yamllint

      - name: Run yamllint
        run: |
          yamllint -c .yamllint.yml .
EOF

# Step 4: Create .yamllint.yml configuration
cat > .yamllint.yml << 'EOF'
---
extends: default

rules:
  line-length:
    max: 120
    level: warning
  comments:
    min-spaces-from-content: 1
  indentation:
    spaces: 2
  truthy:
    allowed-values: ['true', 'false', 'yes', 'no']

ignore: |
  .github/
  .gitea/
  venv/
  .venv/
EOF

# Step 5: Commit workflow files
git add .gitea/ .yamllint.yml
git commit -m "Add Gitea Actions CI/CD workflows

- ansible-lint workflow for code quality
- YAML validation workflow
- yamllint configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>"

git push origin master

Acceptance Criteria:

  • Gitea Actions workflows created
  • ansible-lint workflow functional
  • YAML validation workflow functional
  • Workflows trigger on push/PR
  • Can view workflow results in Gitea UI

Deliverables:

  • .gitea/workflows/ansible-lint.yml
  • .gitea/workflows/yaml-validate.yml
  • .yamllint.yml configuration
  • Documentation: docs/ci-cd-setup.md

Task 2.2: Implement Docker Security Improvements [P1 - HIGH]

Priority: P1 - HIGH Estimated Time: 2-3 hours Status: 🔴 NOT STARTED Dependencies: Week 47 Docker audit completed

Objective: Address Docker security findings from Week 47 audit

Reference: See docs/security/docker-security-findings.md

Execution Steps:

# Step 1: Create Docker security hardening playbook
cat > playbooks/harden_docker.yml << 'EOF'
---
- name: Docker Security Hardening
  hosts: all
  become: true
  gather_facts: true

  vars:
    docker_userns_remap: "dockremap"

  tasks:
    - name: Check if Docker is installed
      ansible.builtin.command: docker --version
      register: docker_check
      failed_when: false
      changed_when: false

    - name: Skip if Docker not installed
      ansible.builtin.meta: end_host
      when: docker_check.rc != 0

    # User namespace remapping
    - name: Create dockremap user
      ansible.builtin.user:
        name: "{{ docker_userns_remap }}"
        system: yes
        create_home: no

    - name: Configure user namespace remapping
      ansible.builtin.lineinfile:
        path: /etc/docker/daemon.json
        line: '  "userns-remap": "{{ docker_userns_remap }}",'
        insertafter: '^\{'
        create: yes
        backup: yes
      notify: restart docker

    # Resource limits
    - name: Add resource limits to docker-compose files
      ansible.builtin.blockinfile:
        path: "{{ item }}"
        marker: "# {mark} ANSIBLE MANAGED RESOURCE LIMITS"
        block: |
          deploy:
            resources:
              limits:
                memory: 1G
                cpus: '1.0'
              reservations:
                memory: 256M
                cpus: '0.5'
      loop: "{{ lookup('fileglob', '/opt/*/docker-compose.yml', wantlist=True) }}"
      when: lookup('fileglob', '/opt/*/docker-compose.yml', wantlist=True) | length > 0

    # Pin image versions
    - name: Audit container image versions
      ansible.builtin.shell: |
        docker ps --format "{{.Image}}" | grep -E ":latest|^[^:]+$"
      register: latest_images
      changed_when: false
      failed_when: false

    - name: Warn about latest tags
      ansible.builtin.debug:
        msg: "WARNING: Containers using :latest or no tag: {{ latest_images.stdout_lines }}"
      when: latest_images.stdout_lines | length > 0

  handlers:
    - name: restart docker
      ansible.builtin.systemd:
        name: docker
        state: restarted
EOF

# Step 2: Create documentation
cat > docs/docker-hardening-guide.md << 'EOF'
# Docker Security Hardening Guide

## Implemented Security Measures

### 1. User Namespace Remapping

User namespace remapping isolates container processes from host processes.

**Implementation:**
```bash
ansible-playbook playbooks/harden_docker.yml

Verification:

docker info | grep "userns"

2. Resource Limits

All containers should have memory and CPU limits.

Example docker-compose.yml:

services:
  app:
    image: myapp:1.2.3
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '1.0'
        reservations:
          memory: 256M
          cpus: '0.5'

3. Image Version Pinning

Never use :latest tags in production.

Bad:

image: pihole/pihole:latest

Good:

image: pihole/pihole:2023.11.0

Testing

Test user namespace remapping:

# Before
docker run --rm busybox id
# uid=0(root) gid=0(root)

# After (with userns remap)
docker run --rm busybox id
# uid=1000(dockremap) gid=1000(dockremap)

References

Step 3: Commit files

git add playbooks/harden_docker.yml docs/docker-hardening-guide.md git commit -m "Add Docker security hardening

  • User namespace remapping playbook
  • Resource limits implementation
  • Image version pinning audit
  • Comprehensive hardening guide

Addresses findings from Week 47 Docker audit

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com"

git push origin master


**Acceptance Criteria:**
- [ ] Docker hardening playbook created
- [ ] User namespace remapping functional
- [ ] Resource limits documented
- [ ] Image version audit implemented
- [ ] Documentation complete

**Deliverables:**
- [ ] playbooks/harden_docker.yml
- [ ] docs/docker-hardening-guide.md
- [ ] Updated Docker security findings

---

### Wednesday-Friday, Nov 20-22 (Days 3-5)

#### Task 3.1: Test Submodule Workflow [P2 - MEDIUM]

**Priority:** P2 - MEDIUM
**Estimated Time:** 1-2 hours
**Status:** 🔴 NOT STARTED

**Objective:** Ensure submodule workflow is functional and documented

**Execution Steps:**
```bash
# Test cloning with submodules
cd /tmp
git clone ssh://git@git.mymx.me:2222/ansible/infra-automation.git test-clone
cd test-clone

# Initialize submodules
git submodule init
git submodule update

# Verify structure
ls -la inventories/ secrets/

# Test updates
cd inventories
git pull origin master
cd ..
git add inventories
git commit -m "Update inventories submodule"

# Create documentation
cat > docs/submodule-workflow.md << 'EOF'
# Git Submodule Workflow

## Initial Clone

```bash
git clone ssh://git@git.mymx.me:2222/ansible/infra-automation.git
cd infra-automation
git submodule init
git submodule update

Or clone with submodules

git clone --recurse-submodules ssh://git@git.mymx.me:2222/ansible/infra-automation.git

Updating Submodules

git submodule update --remote

Working with Submodules

# Make changes in submodule
cd inventories
git checkout master
# Make changes
git commit -am "Update inventory"
git push origin master

# Update parent repository
cd ..
git add inventories
git commit -m "Update inventories submodule"
git push origin master

References


**Acceptance Criteria:**
- [ ] Can clone with submodules
- [ ] Can update submodules
- [ ] Can make changes in submodules
- [ ] Documentation complete

---

### Week Review - Friday, Nov 22

#### Task 4.1: Update Documentation and Metrics [P2 - MEDIUM]

**Priority:** P2 - MEDIUM
**Estimated Time:** 1-2 hours
**Status:** 🔴 NOT STARTED

**Execution Steps:**
```bash
# Update TODO.md with Week 48 completion
# Update SUMMARY.md with new metrics
# Create TASKS_WEEK_49.md
# Commit all changes

Success Criteria

Must Complete (P0-P1)

  • Inventories repository created and functional
  • Secrets repository created (PRIVATE) and functional
  • Gitea Actions workflows operational
  • Docker security improvements implemented

Should Complete (P2)

  • Submodule workflow tested and documented
  • Weekly metrics updated
  • Week 49 plan created

Metrics Tracking

Metric Start of Week Target Current
Separated Repositories 1 3 ___
CI/CD Pipeline 0% 50% ___
Docker Security Score 60% 80% ___
Submodule Integration 0% 100% ___

Blockers and Risks

Potential Risks

  1. Submodule complexity - Team may need training on submodule workflow

    • Mitigation: Create comprehensive documentation and cheatsheets
  2. Gitea Actions availability - May need to enable in Gitea settings

    • Mitigation: Document setup process, use webhooks as fallback
  3. Docker hardening breaking existing containers

    • Mitigation: Test on non-production first, document rollback


Week Start: 2025-11-18 (Monday) Week End: 2025-11-24 (Sunday) Review Date: 2025-11-22 (Friday) Next Planning: 2025-11-25 (Monday) - Week 49