Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions ctf/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ def deploy(
help="Skip build container. (Use this only if you already have the necessary locally for the deploy.yaml to work!)",
),
] = False,
skip_pre_common: Annotated[
bool,
typer.Option(
"--skip-pre-common",
help="Skip pre-common deployment Ansible script. Useful for Windows VM that crashes all the time. (Use this only if you already ran the pre-common once)",
),
] = False,
skip_post_common: Annotated[
bool,
typer.Option(
"--skip-post-common",
help="Skip post-common deployment Ansible script. Useful for Windows VM. DO NOT USE IN PRODUCTION.",
),
] = False,
exclude_tracks: Annotated[
list[str],
typer.Option(
Expand Down Expand Up @@ -240,6 +254,8 @@ def deploy(
path=path,
vm_remote=vm_remote,
vm_project=vm_project,
skip_pre_common=skip_pre_common,
skip_post_common=skip_post_common,
)

track.already_deployed = True
Expand Down Expand Up @@ -467,6 +483,8 @@ def run_ansible_playbook(
vm_remote: str | None = None,
vm_project: str | None = None,
execute_common: bool = True,
skip_pre_common: bool = False,
skip_post_common: bool = False,
) -> None:
extra_args = []
if STATE["verbose"]:
Expand All @@ -484,11 +502,11 @@ def run_ansible_playbook(
if production:
extra_args += ["-e", "nsec_production=true"]

if execute_common:
LOG.info(msg=f"Running common yaml with ansible for track {track}...")
if not skip_pre_common and execute_common:
LOG.info(msg=f"Running pre-common.yaml with ansible for track {track}...")
ansible_args = [
"ansible-playbook",
os.path.join("..", "..", "..", ".deploy", "common.yaml"),
os.path.join("..", "..", "..", ".deploy", "ansible", "common.yaml"),
"-i",
"inventory",
] + extra_args
Expand All @@ -507,6 +525,24 @@ def run_ansible_playbook(
] + extra_args
subprocess.run(args=ansible_args, cwd=path, check=True)

if not skip_post_common and execute_common:
LOG.info(msg=f"Running post-common.yaml with ansible for track {track}...")
ansible_args = (
[
"ansible-playbook",
os.path.join("..", "..", "..", ".deploy", "ansible", "common.yaml"),
"-i",
"inventory",
]
+ extra_args
+ ["-e", "nsec_post_deployment=true"]
)
subprocess.run(
args=ansible_args,
cwd=path,
check=True,
)

artifacts_path = os.path.join(path, "artifacts")
if os.path.exists(path=artifacts_path):
shutil.rmtree(artifacts_path)
16 changes: 16 additions & 0 deletions ctf/redeploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ def redeploy(
help="Skip build container. (Use this only if you already have the necessary locally for the deploy.yaml to work!)",
),
] = False,
skip_pre_common: Annotated[
bool,
typer.Option(
"--skip-pre-common",
help="Skip pre-common deployment Ansible script. Useful for Windows VM that crashes all the time. (Use this only if you already ran the pre-common once)",
),
] = False,
skip_post_common: Annotated[
bool,
typer.Option(
"--skip-post-common",
help="Skip post-common deployment Ansible script. Useful for Windows VM. DO NOT USE IN PRODUCTION.",
),
] = False,
exclude_tracks: Annotated[
list[str],
typer.Option(
Expand All @@ -90,5 +104,7 @@ def redeploy(
keep_already_deployed=True,
force=force,
skip_build=skip_build,
skip_pre_common=skip_pre_common,
skip_post_common=skip_post_common,
exclude_tracks=exclude_tracks,
)
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
- name: Pre-deployment Common
- name: Pre-deployment - install Python on Linux OS
hosts: all,!build,!windows
order: shuffle
gather_facts: false
any_errors_fatal: true

tasks:
- name: Distro update and Python3 install
ansible.builtin.raw: |
apt update && apt upgrade -y && apt install -y python3
changed_when: true
when: not (nsec_post_deployment | bool)

- name: Importing cleanup.yaml Playbook
ansible.builtin.import_playbook: cleanup.yaml
- name: Running common deployment roles for Linux and Windows
hosts: all,!build
order: shuffle
any_errors_fatal: true
roles:
- nsec
3 changes: 3 additions & 0 deletions ctf/templates/init/.deploy/ansible/group_vars/all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
nsec_post_deployment: false
nsec_production: false
3 changes: 3 additions & 0 deletions ctf/templates/init/.deploy/ansible/group_vars/windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
allow_coercion: false
local_admin_password: CHANGE_ME
131 changes: 131 additions & 0 deletions ctf/templates/init/.deploy/ansible/roles/cleanup/tasks/linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
- name: Make network configuration static
ansible.builtin.shell: |
[ ! -e /run/systemd/resolve/resolv.conf ] && exit 0
rm -f /etc/resolv.conf || true
cat /run/systemd/resolve/resolv.conf > /etc/resolv.conf
when: (nsec_production | bool) and not (nsec_post_deployment | bool)
changed_when: true

- name: Mask most systemd units
ansible.builtin.shell: |
for i in \
apt-daily-upgrade.service \
apt-daily-upgrade.timer \
apt-daily.service \
apt-daily.timer \
console-getty.service \
console-setup.service \
dmesg.service \
dpkg-db-backup.service \
dpkg-db-backup.timer \
e2scrub_all.service \
e2scrub_all.timer \
e2scrub_reap.service \
emergency.service \
fstrim.service \
fstrim.timer \
getty-static.service \
getty@tty1.service \
initrd-cleanup.service \
initrd-parse-etc.service \
initrd-switch-root.service \
initrd-udevadm-cleanup-db.service \
keyboard-setup.service \
kmod-static-nodes.service \
ldconfig.service \
logrotate.service \
logrotate.timer \
modprobe@configfs.service \
modprobe@dm_mod.service \
modprobe@drm.service \
modprobe@fuse.service \
modprobe@loop.service \
motd-news.service \
motd-news.timer \
netplan-ovs-cleanup.service \
rescue.service \
rsyslog.service \
setvtrgb.service \
syslog.socket \
systemd-ask-password-console.service \
systemd-ask-password-wall.service \
systemd-battery-check.service \
systemd-bsod.service \
systemd-confext.service \
systemd-fsck-root.service \
systemd-fsckd.service \
systemd-fsckd.socket \
systemd-hibernate-resume.service \
systemd-initctl.service \
systemd-initctl.socket \
systemd-pcrextend.socket \
systemd-pcrlock-file-system.service \
systemd-pcrlock-firmware-code.service \
systemd-pcrlock-firmware-config.service \
systemd-pcrlock-machine-id.service \
systemd-pcrlock-make-policy.service \
systemd-pcrlock-secureboot-authority.service \
systemd-pcrlock-secureboot-policy.service \
systemd-pcrmachine.service \
systemd-pcrphase-initrd.service \
systemd-pcrphase-sysinit.service \
systemd-pcrphase.service \
systemd-random-seed.service \
systemd-repart.service \
systemd-soft-reboot.service \
systemd-sysctl.service \
systemd-sysext.service \
systemd-sysext.socket \
systemd-sysupdate-reboot.service \
systemd-sysupdate-reboot.timer \
systemd-sysupdate.service \
systemd-sysupdate.timer \
systemd-sysusers.service \
systemd-timesyncd.service \
systemd-tpm2-setup-early.service \
systemd-tpm2-setup.service \
systemd-update-done.service \
systemd-update-utmp-runlevel.service \
systemd-update-utmp.service \
ua-reboot-cmds.service \
ua-timer.service \
ua-timer.timer \
ubuntu-advantage.service; do
ln -s /dev/null /etc/systemd/system/${i} || true
done
changed_when: true
when: not (nsec_post_deployment | bool)

- name: Mask network systemd units
ansible.builtin.shell: |
for i in \
systemd-journal-catalog-update.service \
systemd-journal-flush.service \
systemd-journald-dev-log.socket \
systemd-journald.service \
systemd-journald.socket \
networkd-dispatcher.service \
systemd-network-generator.service \
systemd-networkd-wait-online.service \
systemd-networkd.service \
systemd-networkd.socket \
systemd-resolved.service \
systemd-udev-settle.service \
systemd-udev-trigger.service \
systemd-udevd-control.socket \
systemd-udevd-kernel.socket \
systemd-udevd.service; do
ln -s /dev/null /etc/systemd/system/${i} || true
done
when: (nsec_production | bool) and not (nsec_post_deployment | bool)
changed_when: true

- name: Remove all cron jobs
ansible.builtin.shell: |
rm -f /etc/cron.*/* || true
changed_when: true
when: not (nsec_post_deployment | bool)

- name: Reboot the instance
ansible.builtin.reboot:
12 changes: 12 additions & 0 deletions ctf/templates/init/.deploy/ansible/roles/cleanup/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
- name: Include cleanup Linux role
ansible.builtin.include_role:
name: cleanup
tasks_from: linux.yml
when: '"windows" not in group_names and "linux-incus-vm" not in group_names'

- name: Include cleanup Windows role
ansible.builtin.include_role:
name: cleanup
tasks_from: windows.yml
when: '"windows" in group_names'
37 changes: 37 additions & 0 deletions ctf/templates/init/.deploy/ansible/roles/cleanup/tasks/windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
- name: Delete the unattend file
ansible.windows.win_file:
name: C:\windows\system32\sysprep\unattend.xml
state: absent
when: not (nsec_post_deployment | bool)

- name: Delete the unattend panther file
ansible.windows.win_file:
name: C:\windows\panther\unattend.xml
state: absent
when: not (nsec_post_deployment | bool)

- name: Disable windows defender sending sample
ansible.windows.win_shell: Set-MpPreference -SubmitSamplesConsent 2
when: not (nsec_post_deployment | bool)

- name: Disable Defender via registry
ansible.windows.win_regedit:
path: HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender
name: DisableAntiSpyware
data: 1
type: dword
when: not (nsec_post_deployment | bool)

- name: Change the local Administrator password
ansible.windows.win_command: |
powershell -Command
net user Administrator {{ local_admin_password }};
Set-LocalUser -Name 'Administrator' -PasswordNeverExpires $true;
when: nsec_post_deployment | bool

# If using incus-windows, there is a default `admin` user that is created.
- name: Remove the local admin user
ansible.windows.win_command: powershell -Command "net user /delete admin"
ignore_errors: true
when: nsec_post_deployment | bool
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
nsec_post_deployment: false
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
---
16 changes: 16 additions & 0 deletions ctf/templates/init/.deploy/ansible/roles/nsec/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
- name: Include NSEC Linux role
ansible.builtin.include_role:
name: nsec
tasks_from: linux.yml
when: '"windows" not in group_names'

- name: Include NSEC Windows role
ansible.builtin.include_role:
name: nsec
tasks_from: windows.yml
when: '"windows" in group_names'

- name: Importing cleanup role
ansible.builtin.include_role:
name: cleanup
48 changes: 48 additions & 0 deletions ctf/templates/init/.deploy/ansible/roles/nsec/tasks/windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
# Incus exec/WebSocket to Windows VMs can flake; retry transient API errors.
- name: Disable IPv6 temporary addresses
ansible.windows.win_powershell:
script: Set-NetIPv6Protocol -UseTemporaryAddresses Disabled
register: nsec_ipv6_temp
until: nsec_ipv6_temp is success
retries: 8
delay: 15
when: not (nsec_post_deployment | bool)

- name: Disable IPv6 randomize identifiers
ansible.windows.win_powershell:
script: Set-NetIPv6Protocol -RandomizeIdentifiers Disabled
register: nsec_ipv6_rand
until: nsec_ipv6_rand is success
retries: 8
delay: 15
when: not (nsec_post_deployment | bool)

- name: Disable IPv4 protocol binding
ansible.windows.win_powershell:
script: |
$adapters = @(Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | Sort-Object InterfaceIndex)
if ($adapters.Count -eq 0) { throw 'No Up network adapter found for IPv4 binding.' }
$alias = ($adapters | Where-Object { $_.HardwareInterface } | Select-Object -First 1).Name
if (-not $alias) { $alias = $adapters[0].Name }
Disable-NetAdapterBinding -Name $alias -ComponentID ms_tcpip
register: ipv4_disabled
until: ipv4_disabled is success
retries: 8
delay: 15
when: not (nsec_post_deployment | bool)

- name: Display result
ansible.builtin.debug:
var: ipv4_disabled.output
when: not (nsec_post_deployment | bool)

- name: Include Windows optimization role
ansible.builtin.include_role:
name: windows_optimize
when: not (nsec_post_deployment | bool)

- name: Include Windows RPC hardening role
ansible.builtin.include_role:
name: windows_rpc
when: not (nsec_post_deployment | bool) and not allow_coercion
Loading
Loading