1+ - name : Create dedicated API service user
2+ become : true
3+ ansible.builtin.user :
4+ name : attackmate-api
5+ system : yes
6+ shell : /usr/sbin/nologin
7+ create_home : no
8+
9+ # --- Git checkout ---
10+
111- name : Git checkout attackmate-api-server
212 ansible.builtin.git :
313 repo : " {{ attackmate_api_server_url }}"
414 dest : " {{ attackmate_api_server_dest }}"
515 version : " {{ attackmate_api_server_version }}"
16+ register : attackmate_api_git
617 tags :
718 - molecule-idempotence-notest
819
20+ # --- Installation ---
21+
922- name : Install attackmate-api-server with uv
1023 become : true
1124 ansible.builtin.shell :
1225 chdir : " {{ attackmate_api_server_dest }}"
1326 cmd : " uv pip install --python {{ attackmate_dest }}/.venv/bin/python ."
27+ when : attackmate_api_git.changed
28+ changed_when : true
1429 tags :
1530 - molecule-idempotence-notest
16-
17- - name : Create dedicated API service user
18- become : true
19- ansible.builtin.user :
20- name : attackmate-api
21- system : yes
22- shell : /usr/sbin/nologin
23- create_home : no
31+ notify : Restart attackmate-api
2432
2533- name : Create symlink for attackmate-api-server executable
2634 become : true
2735 ansible.builtin.file :
2836 src : " {{ attackmate_dest }}/.venv/bin/attackmate-api"
29- dest : " /usr/local/bin/attackmate-api "
37+ dest : " {{ attackmate_api_bin_path }} "
3038 state : link
3139 force : true
3240
33- - name : Get site-packages path from the venv
34- become : true
35- ansible.builtin.shell :
36- cmd : " {{ attackmate_dest }}/.venv/bin/python -c 'import site; print(site.getsitepackages()[0])'"
37- register : venv_site_packages
38- changed_when : false
39-
40- - name : Set API package destination path
41- ansible.builtin.set_fact :
42- api_package_path : " {{ venv_site_packages.stdout }}/attackmate_api_server"
4341
4442- name : Create API log directory
4543 become : true
4644 ansible.builtin.file :
4745 path : " {{ attackmate_api_log_dir }}"
4846 state : directory
47+ owner : attackmate-api
48+ group : attackmate-api
4949 mode : ' 0755'
5050
5151- name : Ensure SSL directories exist with correct permissions
6060 - { path: '/etc/ssl/private', mode: '0711' }
6161 - { path: '/etc/ssl/certs', mode: '0755' }
6262
63+ # --- SSL certificate generation ---
64+
6365- name : Ensure OpenSSL is installed
6466 ansible.builtin.package :
6567 name : openssl
6668 state : present
6769
6870- name : Generate Private Key
6971 community.crypto.openssl_privatekey :
70- path : " /etc/ssl/private/attackmate.key "
72+ path : " {{ attackmate_ssl_key_path }} "
7173 type : RSA
7274 size : 4096
7375 owner : root
74- group : attackmate-api
76+ group : attackmate-api
7577 mode : ' 0640'
76-
77- - name : Generate Certificate Signing Request (CSR)
78- community.crypto.openssl_csr :
79- path : " /etc/ssl/private/attackmate.csr"
80- privatekey_path : " /etc/ssl/private/attackmate.key"
81- common_name : " {{ ansible_host }}"
82- subject_alt_name : " IP:{{ ansible_host }},DNS:localhost"
78+ # Only regenerate if the key is absent
79+ regenerate : never
80+ notify : Restart attackmate-api
8381
8482- name : Generate Self-Signed Certificate
8583 community.crypto.x509_certificate :
86- path : " /etc/ssl/certs/attackmate.pem"
87- privatekey_path : " /etc/ssl/private/attackmate.key"
88- csr_path : " /etc/ssl/private/attackmate.csr"
84+ path : " {{ attackmate_ssl_cert_path }}"
85+ privatekey_path : " {{ attackmate_ssl_key_path }}"
8986 provider : selfsigned
87+ selfsigned_not_after : " +825d"
88+ selfsigned_digest : sha256
89+ subject :
90+ CN : " {{ ansible_host }}"
91+ subject_alt_name :
92+ - " IP:{{ ansible_host }}"
93+ - " DNS:localhost"
94+ regenerate : never
95+ notify : Restart attackmate-api
9096
91- - name : Create .env file in project root
97+ - name : Fix permissions on SSL keys for the service user
9298 become : true
93- ansible.builtin.copy :
94- dest : " {{ attackmate_api_server_dest }}/.env"
95- content : |
96- SSL_KEY_PATH="/etc/ssl/private/attackmate.key"
97- SSL_CERT_PATH="/etc/ssl/certs/attackmate.pem"
98- ATTACKMATE_CONFIG_PATH="/etc/attackmate.yml"
99- WRITE_PLAYBOOK_LOGS_TO_DISK="{{ attackmate_api_logs_to_disk }}"
100- LOG_DIR="{{ attackmate_api_log_dir }}"
101- # USERS='{"username": "<argon2_hash>", ...}' # You need to enter credentials here, hashed with argon2
102- owner : attackmate-api
99+ ansible.builtin.file :
100+ path : " {{ item.path }}"
101+ owner : root
103102 group : attackmate-api
104- mode : ' 0600'
103+ mode : " {{ item.mode }}"
104+ loop :
105+ - { path: '/etc/ssl/private', mode: '0710' }
106+ - { path: "{{ attackmate_ssl_key_path }}", mode: '0640' }
107+ - { path: "{{ attackmate_ssl_cert_path }}", mode: '0644' }
108+
105109
110+ # --- Env file ---
106111
107- - name : Ensure API log directory is owned by the service user
112+ - name : Create .env file in project root
108113 become : true
109- ansible.builtin.file :
110- path : " {{ attackmate_api_log_dir }} "
111- state : directory
114+ ansible.builtin.template :
115+ src : api-env.j2
116+ dest : " {{ attackmate_api_server_dest }}/.env "
112117 owner : attackmate-api
113118 group : attackmate-api
114- mode : ' 0750'
119+ mode : ' 0600'
120+ notify : Restart attackmate-api
115121
116- - name : Fix permissions on SSL keys for the service user
117- become : true
118- ansible.builtin.file :
119- path : " {{ item.path }}"
120- owner : root
121- group : attackmate-api # Allow the API user to read the key
122- mode : " {{ item.mode }}"
123- loop :
124- - { path: '/etc/ssl/private', mode: '0710' }
125- - { path: '/etc/ssl/private/attackmate.key', mode: '0640' }
126- - { path: '/etc/ssl/certs/attackmate.pem', mode: '0644' }
122+ # --- Systemd service ---
127123
128124- name : Install AttackMate API Systemd service
129125 become : true
130126 ansible.builtin.copy :
131- dest : /etc/systemd/system/attackmate-api.service
127+ dest : " {{ attackmate_api_service_path }} "
132128 content : |
133129 [Unit]
134130 Description=AttackMate API Server
138134 Type=simple
139135 User=attackmate-api
140136 Group=attackmate-api
141- # This tells Pydantic where to look for the .env file
142137 WorkingDirectory={{ attackmate_api_server_dest }}
143- ExecStart=/usr/local/bin/attackmate-api
144- Restart=always
138+ ExecStart={{ attackmate_api_bin_path }}
139+ Restart=on-failure
145140
146141 NoNewPrivileges=yes
147142 PrivateTmp=yes
150145
151146 [Install]
152147 WantedBy=multi-user.target
148+ notify : Restart attackmate-api
153149
154150- name : Start and enable AttackMate API
155151 become : true
156152 ansible.builtin.systemd :
157153 name : attackmate-api
158- state : restarted
154+ state : started
159155 enabled : yes
160156 daemon_reload : yes
0 commit comments