kairos-io / kairos

The immutable Linux meta-distribution for edge Kubernetes.
https://kairos.io
Apache License 2.0
1.16k stars 96 forks source link

SSH Hardening #789

Open sdwilsh opened 1 year ago

sdwilsh commented 1 year ago

Is your feature request related to a problem? Please describe.

I run the ssh role from dev-sec/ansible-collection-hardening on any linux box I run to harden ssh to the DevSec SSH Baseline. The Kairos images all appear to use the default configuration for ssh, which isn't as secure.

Describe the solution you'd like

I would love to see Kairos images pass, and verify with CI, against the compliance profile for the DevSec SSH Baseline.

Describe alternatives you've considered

Supporting the cloud-init ansible module would allow folks to run any arbitrary ansible playbook during cloud-init, and the ansible role for this could be ran during that phase. It's a more generic solution, but I think meeting the baseline profile would be better for most users by default than requiring them to run this playbook.

mudler commented 1 year ago

Thanks for filing the issue, this is something indeed we should support, either ootb or explicitly opting-out from the cloud config.

bencorrado commented 1 month ago

I have been hacking on this a bit and have a working prototype for Karios:

###############################################################
####           Ansible Stage for SSH Hardening             ####
###############################################################

FROM ubuntu-24.04 as ansible_stage
ARG CACHEBUST=8

# Install additional dependencies if necessary
RUN apt-get update && apt-get install -y --no-install-recommends \
    ssh \
    git \
    ansible \
    wget \
    diffutils \
    patch \
    rsync \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

# Download the collection directly from GitHub
ENV COLLECTION_VERSION="master"

RUN rm -R /tmp

# Clone the collection repository and checkout the specified version
RUN git clone --branch ${COLLECTION_VERSION} --depth 1 https://github.com/dev-sec/ansible-collection-hardening.git /tmp/devsec-hardening

# Replace the disable-systemd-socket.yml since we are not actively running systemd
COPY ansible/disable-systemd-socket.yml /tmp/devsec-hardening/roles/ssh_hardening/tasks/disable-systemd-socket.yml
COPY ansible/main.yml /tmp/devsec-hardening/roles/ssh_hardening/handlers/main.yml

# Build the collection locally
RUN ansible-galaxy collection build /tmp/devsec-hardening --output-path /tmp

# Install the collection from the built tarball
RUN ansible-galaxy collection install /tmp/devsec-hardening-*.tar.gz --force && \
    ansible-galaxy collection install comm6.8.0-45unity.general

# Copy the filesystem from the 'common' stage to /mnt/rootfs
COPY --from=common / /mnt/rootfs

# Create copies of the filesystem
RUN rsync -a --exclude='/proc/*' --exclude='/sys/*' --exclude='/dev/*' \
    --exclude='/tmp/*' --exclude='/mnt/*' --exclude='/media/*' --exclude='/run/*' \
    /mnt/rootfs/ /tmp/original_fs/ && \
    mkdir /tmp/modified_fs

# Copy Ansible playbook and requirements
COPY ansible/ssh_hardening.yml /tmp/ssh_hardening.yml
COPY ansible/requirements.yml /tmp/requirements.yml

# Install required Ansible collections
RUN ansible-galaxy collection install -r /tmp/requirements.yml

RUN ansible-playbook -c chroot -i '/mnt/rootfs,' /tmp/ssh_hardening.yml

# Create a copy of the modified filesystem
RUN rsync -a --exclude='/proc/*' --exclude='/sys/*' --exclude='/dev/*' \
    --exclude='/tmp/*' --exclude='/mnt/*' --exclude='/media/*' --exclude='/run/*' \
    /mnt/rootfs/ /tmp/modified_fs/

# Generate diffs between original and modified
RUN diff -ruN /tmp/original_fs/ /tmp/modified_fs/ || true | sed 's#/tmp/original_fs##' | sed 's#/tmp/modified_fs##' | tee /tmp/fs_diff.patch

Then FROM common doing:

RUN apt-get update && apt-get install -y --no-install-recommends \
    patch \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

# Copy the diff patch from 'ansible_stage' & apply patch to the base filesystem
COPY --from=ansible_stage /tmp/fs_diff.patch /tmp/fs_diff1.patch
RUN patch -p1 -d / < /tmp/fs_diff1.patch && rm /tmp/fs_diff1.patch && \
   apt-get remove --purge -y --no-install-recommends \
    patch \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

I have a few other patches that make it all work, but those are the basics.

I need to clean it up and figure out how to make it work for more than just Ubuntu.

sdwilsh commented 1 month ago

If we have a system to just run a collection from ansible, that might also let us run the os hardening ones too! Very exciting!

Itxaka commented 1 month ago

the ansible plugin for yip is very interesting. In the case of kairos it would require 2 hard deps that we currently migth not ship (git for sure, python+pip no idea, depends on the distro) But I think its a thing worth exploring so I opened https://github.com/mudler/yip/issues/179 to track this under yip.

On the hardening, I would start by running a CI job to run the ssh baseline against at least 1 docker image and improve it over time. I wonder if there are more profiles liek that that we could keep adding.

bencorrado commented 1 month ago

What I am proposing here does not need yip (though I think it would be interesting to add yip support). It instead directly applies to the ansible script to the base image in docker by mounting it in chroot style before building it as an image.

Itxaka commented 1 month ago

What I am proposing here does not need yip (though I think it would be interesting to add yip support). It instead directly applies to the ansible script to the base image in docker by mounting it in chroot style before building it as an image.

oooh, during building? Thats interesting indeed, but we have been dropping more and more stuff during building of the image to make it easier and clearer to build Kairos. This is part of trying to get our Kairos factory thing ongoing with as less building pieces int eh middle as possible (so you put an image in, get a kairos out as simple as possible) But the idea still prevails, our default ssh config should be hardened and then users can override that config themselves if they want a more relaxed ssh config.