Compare commits
44 Commits
5fbf645dd9
...
5d99bf51da
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d99bf51da | |||
| 5ff9ca4687 | |||
| d364f82c9f | |||
| ce62e4afa6 | |||
| f2be3f4899 | |||
| fd5f6f5bca | |||
| eb7bb02e86 | |||
| 7f3bb699f9 | |||
| 7d73885162 | |||
| 3be40169b2 | |||
| 08053e6c39 | |||
| 5073008506 | |||
| 0ab26cae67 | |||
| 4920837641 | |||
| 8764bede85 | |||
| c751ced793 | |||
| 03a1a5879e | |||
| 1ecff67cd9 | |||
| 8e9b993f14 | |||
| 659feb3322 | |||
| 00486fbc8d | |||
| f7ba34ec69 | |||
| 5440fd3acb | |||
| c1af7193f7 | |||
| f39bb9c8a3 | |||
| 40fa1312c4 | |||
| 8ba6a236f8 | |||
| 4f9ecc84d3 | |||
| 217ace503f | |||
| a614f4461f | |||
| 4a658857b3 | |||
| 9f85033aff | |||
| 9aac725e88 | |||
| c45946739e | |||
| 4c9a4d480e | |||
| 020fcf2c51 | |||
| 6a2179d7a0 | |||
| 6eaeeb0322 | |||
| f01f1b5431 | |||
| 9c770faa23 | |||
| 7dae1fc086 | |||
| 43cfc07c11 | |||
| 3a558c5bad | |||
| b6b4aad798 |
129
.ansible-lint.yml
Normal file
129
.ansible-lint.yml
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
# .ansible-lint
|
||||
|
||||
profile: null # min, basic, moderate,safety, shared, production
|
||||
|
||||
# Allows dumping of results in SARIF format
|
||||
# sarif_file: result.sarif
|
||||
|
||||
# exclude_paths included in this file are parsed relative to this file's location
|
||||
# and not relative to the CWD of execution. CLI arguments passed to the --exclude
|
||||
# option are parsed relative to the CWD of execution.
|
||||
exclude_paths:
|
||||
- .cache/ # implicit unless exclude_paths is defined in config
|
||||
- test/fixtures/formatting-before/
|
||||
- test/fixtures/formatting-prettier/
|
||||
# quiet: true
|
||||
# strict: true
|
||||
# verbosity: 1
|
||||
|
||||
# Mock modules or roles in order to pass ansible-playbook --syntax-check
|
||||
mock_modules:
|
||||
- zuul_return
|
||||
# note the foo.bar is invalid as being neither a module or a collection
|
||||
- fake_namespace.fake_collection.fake_module
|
||||
- fake_namespace.fake_collection.fake_module.fake_submodule
|
||||
mock_roles:
|
||||
- mocked_role
|
||||
- author.role_name # old standalone galaxy role
|
||||
- fake_namespace.fake_collection.fake_role # role within a collection
|
||||
|
||||
# Enable checking of loop variable prefixes in roles
|
||||
loop_var_prefix: "^(__|{role}_)"
|
||||
|
||||
# Enforce variable names to follow pattern below, in addition to Ansible own
|
||||
# requirements, like avoiding python identifiers. To disable add `var-naming`
|
||||
# to skip_list.
|
||||
var_naming_pattern: "^[a-z_][a-z0-9_]*$"
|
||||
|
||||
use_default_rules: true
|
||||
# Load custom rules from this specific folder
|
||||
# rulesdir:
|
||||
# - ./rule/directory/
|
||||
|
||||
# Ansible-lint is able to recognize and load skip rules stored inside
|
||||
# `.ansible-lint-ignore` (or `.config/ansible-lint-ignore.txt`) files.
|
||||
# To skip a rule just enter filename and tag, like "playbook.yml package-latest"
|
||||
# on a new line.
|
||||
# Optionally you can add comments after the tag, prefixed by "#". We discourage
|
||||
# the use of skip_list below because that will hide violations from the output.
|
||||
# When putting ignores inside the ignore file, they are marked as ignored, but
|
||||
# still visible, making it easier to address later.
|
||||
skip_list:
|
||||
- skip_this_tag
|
||||
|
||||
# Ansible-lint does not automatically load rules that have the 'opt-in' tag.
|
||||
# You must enable opt-in rules by listing each rule 'id' below.
|
||||
enable_list:
|
||||
- args
|
||||
- empty-string-compare # opt-in
|
||||
- no-log-password # opt-in
|
||||
- no-same-owner # opt-in
|
||||
- name[prefix] # opt-in
|
||||
- galaxy-version-incorrect # opt-in
|
||||
# add yaml here if you want to avoid ignoring yaml checks when yamllint
|
||||
# library is missing. Normally its absence just skips using that rule.
|
||||
- yaml
|
||||
# Report only a subset of tags and fully ignore any others
|
||||
# tags:
|
||||
# - jinja[spacing]
|
||||
|
||||
# Ansible-lint does not fail on warnings from the rules or tags listed below
|
||||
warn_list:
|
||||
- skip_this_tag
|
||||
- experimental # experimental is included in the implicit list
|
||||
# - role-name
|
||||
# - yaml[document-start] # you can also use sub-rule matches
|
||||
|
||||
# Some rules can transform files to fix (or make it easier to fix) identified
|
||||
# errors. `ansible-lint --fix` will reformat YAML files and run these transforms.
|
||||
# By default it will run all transforms (effectively `write_list: ["all"]`).
|
||||
# You can disable running transforms by setting `write_list: ["none"]`.
|
||||
# Or only enable a subset of rule transforms by listing rules/tags here.
|
||||
# write_list:
|
||||
# - all
|
||||
|
||||
# Offline mode disables installation of requirements.yml and schema refreshing
|
||||
offline: true
|
||||
|
||||
# Define required Ansible's variables to satisfy syntax check
|
||||
extra_vars:
|
||||
foo: bar
|
||||
multiline_string_variable: |
|
||||
line1
|
||||
line2
|
||||
complex_variable: ":{;\t$()"
|
||||
|
||||
# Uncomment to enforce action validation with tasks, usually is not
|
||||
# needed as Ansible syntax check also covers it.
|
||||
# skip_action_validation: false
|
||||
|
||||
# List of additional kind:pattern to be added at the top of the default
|
||||
# match list, first match determines the file kind.
|
||||
kinds:
|
||||
# - playbook: "**/examples/*.{yml,yaml}"
|
||||
# - galaxy: "**/folder/galaxy.yml"
|
||||
# - tasks: "**/tasks/*.yml"
|
||||
# - vars: "**/vars/*.yml"
|
||||
# - meta: "**/meta/main.yml"
|
||||
- yaml: "**/*.yaml-too"
|
||||
|
||||
# List of additional collections to allow in only-builtins rule.
|
||||
# only_builtins_allow_collections:
|
||||
# - example_ns.example_collection
|
||||
|
||||
# List of additions modules to allow in only-builtins rule.
|
||||
# only_builtins_allow_modules:
|
||||
# - example_module
|
||||
|
||||
# Allow setting custom prefix for name[prefix] rule
|
||||
task_name_prefix: "{stem} | "
|
||||
|
||||
# Complexity related settings
|
||||
|
||||
# Limit the depth of the nested blocks:
|
||||
# max_block_depth: 20
|
||||
|
||||
# Also recognize these versions of Ansible as supported:
|
||||
# supported_ansible_also:
|
||||
# - "2.18"
|
||||
@@ -0,0 +1,32 @@
|
||||
# This is a mocked Ansible module generated by ansible-lint
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: fake_namespace.fake_collection.fake_module
|
||||
|
||||
short_description: Mocked
|
||||
version_added: "1.0.0"
|
||||
description: Mocked
|
||||
|
||||
author:
|
||||
- ansible-lint (@nobody)
|
||||
'''
|
||||
EXAMPLES = '''mocked'''
|
||||
RETURN = '''mocked'''
|
||||
|
||||
|
||||
def main():
|
||||
result = dict(
|
||||
changed=False,
|
||||
original_message='',
|
||||
message='')
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,32 @@
|
||||
# This is a mocked Ansible module generated by ansible-lint
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: fake_namespace.fake_collection.fake_module.fake_submodule
|
||||
|
||||
short_description: Mocked
|
||||
version_added: "1.0.0"
|
||||
description: Mocked
|
||||
|
||||
author:
|
||||
- ansible-lint (@nobody)
|
||||
'''
|
||||
EXAMPLES = '''mocked'''
|
||||
RETURN = '''mocked'''
|
||||
|
||||
|
||||
def main():
|
||||
result = dict(
|
||||
changed=False,
|
||||
original_message='',
|
||||
message='')
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
32
.ansible/modules/zuul_return.py
Normal file
32
.ansible/modules/zuul_return.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# This is a mocked Ansible module generated by ansible-lint
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: zuul_return
|
||||
|
||||
short_description: Mocked
|
||||
version_added: "1.0.0"
|
||||
description: Mocked
|
||||
|
||||
author:
|
||||
- ansible-lint (@nobody)
|
||||
'''
|
||||
EXAMPLES = '''mocked'''
|
||||
RETURN = '''mocked'''
|
||||
|
||||
|
||||
def main():
|
||||
result = dict(
|
||||
changed=False,
|
||||
original_message='',
|
||||
message='')
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,4 +1,11 @@
|
||||
# Ansible Tower ignore list
|
||||
/hosts
|
||||
/hosts.test.yml
|
||||
/hosts.yml
|
||||
/hosts.yaml
|
||||
/hosts.json
|
||||
*.bak
|
||||
/init@homeserver.yml
|
||||
|
||||
# Ansible runtime and backups
|
||||
*.original
|
||||
@@ -11,6 +18,8 @@
|
||||
.galcache/
|
||||
/collections/ansible_collections/
|
||||
/.devcontainer/
|
||||
.lock
|
||||
/.cache/
|
||||
|
||||
# Try tyo avoid any plain-text passwords
|
||||
*pwd*
|
||||
|
||||
10
ansible.cfg
10
ansible.cfg
@@ -96,6 +96,7 @@ action_plugins=plugins/action:~/.ansible/plugins/action:/usr/share/ansible/plugi
|
||||
# (pathspec) Colon-separated paths in which Ansible will search for Cache Plugins.
|
||||
cache_plugins=plugins/cache:~/.ansible/plugins/cache:/usr/share/ansible/plugins/cache
|
||||
|
||||
callback_result_format=yaml
|
||||
# (pathspec) Colon-separated paths in which Ansible will search for Callback Plugins.
|
||||
callback_plugins=plugins/callback:~/.ansible/plugins/callback:/usr/share/ansible/plugins/callback
|
||||
|
||||
@@ -138,7 +139,7 @@ gathering=smart
|
||||
;hash_behaviour=replace
|
||||
|
||||
# (pathlist) Comma-separated list of Ansible inventory sources
|
||||
inventory=['.','inv','/etc/ansible/hosts']
|
||||
inventory=['hosts','hosts.yml','hosts.yaml','hosts.json','/etc/ansible/hosts','/etc/ansible/hosts.yml','/etc/ansible/hosts.yaml','/etc/ansible/hosts.json']
|
||||
|
||||
# (pathspec) Colon-separated paths in which Ansible will search for HttpApi Plugins.
|
||||
httpapi_plugins=plugins/httpapi:~/.ansible/plugins/httpapi:/usr/share/ansible/plugins/httpapi
|
||||
@@ -233,7 +234,7 @@ roles_path=roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
|
||||
# (string) Set the main callback used to display Ansible output. You can only have one at a time.
|
||||
# You can have many other callbacks, but just one can be in charge of stdout.
|
||||
# See :ref:`callback_plugins` for a list of available options.
|
||||
stdout_callback=yaml
|
||||
;stdout_callback=yaml
|
||||
|
||||
# (string) Set the default strategy used for plays.
|
||||
;strategy=linear
|
||||
@@ -318,7 +319,7 @@ doc_fragment_plugins=plugins/doc_fragments:~/.ansible/plugins/doc_fragments:/usr
|
||||
# (boolean) Whether or not to enable the task debugger, this previously was done as a strategy plugin.
|
||||
# Now all strategy plugins can inherit this behavior. The debugger defaults to activating when
|
||||
# a task is failed on unreachable. Use the debugger keyword for more flexibility.
|
||||
;enable_task_debugger=False
|
||||
enable_task_debugger=True
|
||||
|
||||
# (boolean) Toggle to allow missing handlers to become a warning instead of an error when notifying.
|
||||
;error_on_missing_handler=True
|
||||
@@ -331,6 +332,7 @@ doc_fragment_plugins=plugins/doc_fragments:~/.ansible/plugins/doc_fragments:/usr
|
||||
# (boolean) Set this to "False" if you want to avoid host key checking by the underlying connection plugin Ansible uses to connect to the host.
|
||||
# Please read the documentation of the specific connection plugin used for details.
|
||||
;host_key_checking=True
|
||||
host_key_checking=False
|
||||
|
||||
# (boolean) Facts are available inside the `ansible_facts` variable, this setting also pushes them as their own vars in the main namespace.
|
||||
# Unlike inside the `ansible_facts` dictionary where the prefix `ansible_` is removed from fact names, these will have the exact names that are returned by the module.
|
||||
@@ -663,7 +665,7 @@ token_path=.gal_token
|
||||
;any_unparsed_is_failed=False
|
||||
|
||||
# (list) List of enabled inventory plugins, it also determines the order in which they are used.
|
||||
;enable_plugins=host_list, script, auto, yaml, ini, toml
|
||||
enable_plugins=host_list, script, auto, yaml, ini, toml
|
||||
|
||||
# (bool) Controls if ansible-inventory will accurately reflect Ansible's view into inventory or its optimized for exporting.
|
||||
;export=False
|
||||
|
||||
3
dry_run.sh
Executable file
3
dry_run.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
ansible-playbook --ask-pass --ask-become-pass -i hosts.yml init@homeserver.yml --check
|
||||
@@ -13,26 +13,26 @@ instance: armitage
|
||||
# operating_system: "tftp://hikiki.local:69/debian.iso"
|
||||
operating_system: ~
|
||||
# <list[<str>]> of control node or local SSH key basenames
|
||||
keys:
|
||||
ssh_keys:
|
||||
- id_ed25519_localhost
|
||||
# <list<dict>> list of administrative users (in Linux, users that can use "sudo")
|
||||
admins:
|
||||
- username: admin # <str> arbitrary valid user name
|
||||
services: ~ # <list[<str>]> if linux system user, assocated servce
|
||||
# <list[<str>]> list of control node or local SSH key basenames for this user
|
||||
keys: "{{ keys }}"
|
||||
ssh_keys: "{{ keys }}"
|
||||
# <str<vault?>> hashed (and maybe salted) password
|
||||
password: !vault |
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
34396235306630656138303939346638343135623430353666326462663131613130643061366435
|
||||
6563616331656566626263633966633764386564383961640a656466323835616263653531323861
|
||||
65376663363934653163313666303166376262623334343034626535356431636662366261333061
|
||||
3866656638623631660a386666383136396238633365333465333766383766303631663336326264
|
||||
35663339663062333162643039663430363265393163303839356664343633373630303462393735
|
||||
37316262383335323837646265336139373238623735383134623361363136663436393162666336
|
||||
62353462323534316531313533636461353139326466646662356233373130616633633262616539
|
||||
37306332666338363231383537343832396432666134663462633336646330646332306634356636
|
||||
36626166386634653537613334616538313266323866303738316430666131646333
|
||||
33663131343861303735643439393165356231366338346538333537643464343761373139303364
|
||||
6630303563346437373161626662313432306138353132350a353334356139376662333562353834
|
||||
36326461613664616565373835303636636533616462303732633461343130346134366662373566
|
||||
6431623034653363310a303665636366353535313436666532623737373930356364616339313633
|
||||
34663839656637373031393031656332393761623161643730326563323863363461333864353338
|
||||
30633964353339323465643064636538346464343035626461333366303835333039653661383030
|
||||
62656663336536373262623062633563646434646431303137306438633937323764633334396539
|
||||
64353734613662663063343966356562326661626436663430623430663766343030646333306634
|
||||
32353839313235313339353431323837356537336231366564313431313462613333
|
||||
pkgs:
|
||||
# <dict[<str>:<dict>]> representing package groups installed by package manager via repositories
|
||||
mngr:
|
||||
@@ -207,6 +207,18 @@ pkgs:
|
||||
key_path: ~
|
||||
src_entry: ~
|
||||
src_path: ~
|
||||
- name: python3-venv
|
||||
uri: ~
|
||||
key: ~
|
||||
key_path: ~
|
||||
src_entry: ~
|
||||
src_path: ~
|
||||
- name: python3-pip
|
||||
uri: ~
|
||||
key: ~
|
||||
key_path: ~
|
||||
src_entry: ~
|
||||
src_path: ~
|
||||
- name: golang
|
||||
uri: ~
|
||||
key: ~
|
||||
@@ -238,6 +250,18 @@ pkgs:
|
||||
src_entry: ~
|
||||
src_path: ~
|
||||
handler: ~
|
||||
- name: avahi-daemon
|
||||
uri: ~
|
||||
key: ~
|
||||
key_path: ~
|
||||
src_entry: ~
|
||||
src_path: ~
|
||||
- name: avahi-utils
|
||||
uri: ~
|
||||
key: ~
|
||||
key_path: ~
|
||||
src_entry: ~
|
||||
src_path: ~
|
||||
# <dict[<str>:<dict>]> representing package groups installed by shell scripts
|
||||
script:
|
||||
# <list[<dict>]> representing user-level or supplemental shell script installations
|
||||
@@ -254,6 +278,10 @@ pkgs:
|
||||
src: "https://install.julialang.org"
|
||||
pre: ~
|
||||
post: ~
|
||||
- name: uv
|
||||
src: "https://astral.sh/uv/install.sh"
|
||||
pre: ~
|
||||
post: ~
|
||||
# <dict[<str>:<dict>]> representing package groups installed from source archives
|
||||
archive:
|
||||
# <list[<dict>]> representing user-level or supplemental source archives
|
||||
|
||||
@@ -8,7 +8,7 @@ instance: ""
|
||||
# Example-- operating_system: "tftp://hikiki.local:69/debian.iso"
|
||||
operating_system: ~
|
||||
# <list[<str>]> of control node or local SSH key basenames
|
||||
keys: []
|
||||
ssh_keys: []
|
||||
# <dict[<str>:<dict>]> package groups
|
||||
pkgs:
|
||||
# <dict[<str>:<dict>]> representing package groups installed by package manager via repositories
|
||||
|
||||
@@ -24,7 +24,7 @@ origin: us-east
|
||||
# <str<enum>> representing Linux distro or OS image available in VPS service to be used for VPS
|
||||
operating_system: linode/debian13
|
||||
# <list[<str>]> list of control node or local SSH key basenames for root user
|
||||
keys:
|
||||
ssh_keys:
|
||||
- id_ecdsa-sha2_sukaato_miniyubikey
|
||||
- id_ecdsa-sha2_sukaato_yubikey
|
||||
# <list<dict>> list of administrative users (in Linux, users that can use "sudo")
|
||||
@@ -32,7 +32,7 @@ admins:
|
||||
- username: senpai # <str> arbitrary valid user name
|
||||
services: ~ # <list[<str>]> if linux system user, assocated servce
|
||||
# <list[<str>]> list of control node or local SSH key basenames for this user
|
||||
keys:
|
||||
ssh_keys:
|
||||
- id_ed25519_sukaato_yubikey
|
||||
- id_ed25519_sukaato_miniyubikey
|
||||
# <str<vault?>> hashed (and maybe salted) password
|
||||
@@ -222,6 +222,18 @@ pkgs:
|
||||
key_path: ~
|
||||
src_entry: ~
|
||||
src_path: ~
|
||||
- name: python3-venv
|
||||
uri: ~
|
||||
key: ~
|
||||
key_path: ~
|
||||
src_entry: ~
|
||||
src_path: ~
|
||||
- name: python3-pip
|
||||
uri: ~
|
||||
key: ~
|
||||
key_path: ~
|
||||
src_entry: ~
|
||||
src_path: ~
|
||||
- name: golang
|
||||
uri: ~
|
||||
key: ~
|
||||
@@ -269,6 +281,10 @@ pkgs:
|
||||
src: "https://install.julialang.org"
|
||||
pre: ~
|
||||
post: ~
|
||||
- name: uv
|
||||
src: "https://astral.sh/uv/install.sh"
|
||||
pre: ~
|
||||
post: ~
|
||||
# <dict[<str>:<dict>]> representing package groups installed from source archives
|
||||
archive:
|
||||
# <list[<dict>]> representing user-level or supplemental source archives
|
||||
|
||||
@@ -9,7 +9,7 @@ origin: ""
|
||||
# <str<enum>> representing Linux distro or OS image available in VPS service to be used for VPS
|
||||
operating_system: ~
|
||||
# <list[<str>]> of control node or local SSH key basenames
|
||||
keys: []
|
||||
ssh_keys: []
|
||||
# <dict[<str>:<dict>]> package groups
|
||||
pkgs:
|
||||
# <dict[<str>:<dict>]> representing package groups installed by package manager via repositories
|
||||
|
||||
11
hosts.yml.example
Normal file
11
hosts.yml.example
Normal file
@@ -0,0 +1,11 @@
|
||||
# @TODO use hosts and host groupings that refer or point to VM or containerized servers for testing
|
||||
ungrouped:
|
||||
hosts: ~
|
||||
sukaato:
|
||||
hosts: ~
|
||||
armitage:
|
||||
hosts: ~
|
||||
vps:
|
||||
children: ~
|
||||
homeserver:
|
||||
children: ~
|
||||
28
init@homeserver.yml.example
Normal file
28
init@homeserver.yml.example
Normal file
@@ -0,0 +1,28 @@
|
||||
# @NOTE run 'ansible-playbook' command on this using 'sudo'
|
||||
- name: Initialize homeserver
|
||||
hosts: armitage
|
||||
remote_user: root
|
||||
vars:
|
||||
harden: true
|
||||
local_facts:
|
||||
user_dir: ~
|
||||
user_id: ~
|
||||
tasks:
|
||||
- name: Hardening SSH server
|
||||
ansible.builtin.include_role:
|
||||
name: init-server # required. The name of the role to be executed.
|
||||
# apply: # not required. Accepts a hash of task keywords (e.g. C(tags), C(become)) that will be applied to all tasks within the included role.
|
||||
tasks_from: harden # not required. File to load from a role's C(tasks/) directory.
|
||||
# vars_from: main # not required. File to load from a role's C(vars/) directory.
|
||||
# defaults_from: main # not required. File to load from a role's C(defaults/) directory.
|
||||
# allow_duplicates: True # not required. Overrides the role's metadata setting to allow using a role more than once with the same parameters.
|
||||
# handlers_from: main # not required. File to load from a role's C(handlers/) directory.
|
||||
- name: Initializing groups and users
|
||||
ansible.builtin.include_role:
|
||||
name: init-server # required. The name of the role to be executed.
|
||||
# apply: # not required. Accepts a hash of task keywords (e.g. C(tags), C(become)) that will be applied to all tasks within the included role.
|
||||
tasks_from: ssh-users # not required. File to load from a role's C(tasks/) directory.
|
||||
vars_from: main # not required. File to load from a role's C(vars/) directory.
|
||||
defaults_from: main # not required. File to load from a role's C(defaults/) directory.
|
||||
# allow_duplicates: True # not required. Overrides the role's metadata setting to allow using a role more than once with the same parameters.
|
||||
# handlers_from: main # not required. File to load from a role's C(handlers/) directory.
|
||||
@@ -1,11 +1,15 @@
|
||||
ansible==13.7.0
|
||||
ansible-builder==3.1.1
|
||||
ansible-compat==26.3.0
|
||||
ansible-core==2.20.6
|
||||
ansible-lint==26.4.0
|
||||
ansible-navigator==26.4.0
|
||||
ansible-runner==2.4.3
|
||||
ansible-specdoc==0.0.20
|
||||
appdirs==1.4.4
|
||||
attrs==26.1.0
|
||||
baron==0.10.1
|
||||
bindep==2.14.0
|
||||
black==26.5.1
|
||||
bracex==2.6
|
||||
certifi==2026.5.20
|
||||
@@ -13,29 +17,36 @@ cffi==2.0.0
|
||||
charset-normalizer==3.4.7
|
||||
click==8.4.1
|
||||
cryptography==48.0.0
|
||||
Deprecated==1.3.1
|
||||
deprecated==1.3.1
|
||||
distro==1.9.0
|
||||
enrich==1.2.7
|
||||
filelock==3.29.0
|
||||
idna==3.16
|
||||
Jinja2==3.1.6
|
||||
jinja2==3.1.6
|
||||
jsonschema==4.26.0
|
||||
jsonschema-specifications==2025.9.1
|
||||
linode_api4==5.44.0
|
||||
linode-api4==5.44.0
|
||||
lockfile==0.12.2
|
||||
markdown-it-py==4.2.0
|
||||
MarkupSafe==3.0.3
|
||||
markupsafe==3.0.3
|
||||
mdurl==0.1.2
|
||||
molecule==26.4.0
|
||||
mypy_extensions==1.1.0
|
||||
mypy-extensions==1.1.0
|
||||
onigurumacffi==1.5.0
|
||||
packaging==26.2
|
||||
parsley==1.3
|
||||
pathspec==1.0.4
|
||||
pbr==7.0.3
|
||||
pexpect==4.9.0
|
||||
platformdirs==4.9.6
|
||||
pluggy==1.6.0
|
||||
polling==0.3.2
|
||||
ptyprocess==0.7.0
|
||||
pycparser==3.0
|
||||
Pygments==2.20.0
|
||||
pygments==2.20.0
|
||||
python-daemon==3.1.2
|
||||
pytokens==0.4.1
|
||||
PyYAML==6.0.3
|
||||
pyyaml==6.0.3
|
||||
redbaron==0.9.2
|
||||
referencing==0.37.0
|
||||
requests==2.34.2
|
||||
@@ -43,9 +54,11 @@ resolvelib==1.2.1
|
||||
rich==15.0.0
|
||||
rpds-py==0.30.0
|
||||
rply==0.7.8
|
||||
ruamel.yaml==0.19.1
|
||||
ruamel.yaml.clib==0.2.15
|
||||
ruamel-yaml==0.19.1
|
||||
ruamel-yaml-clib==0.2.15
|
||||
setuptools==82.0.1
|
||||
subprocess-tee==0.4.2
|
||||
tzdata==2026.2
|
||||
urllib3==2.7.0
|
||||
wcmatch==10.1
|
||||
wrapt==2.2.1
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# defaults file for roles/init-vps
|
||||
# <str<bool>> whether to harden managed node's SSH service
|
||||
harden: true
|
||||
@@ -1,54 +1,7 @@
|
||||
# SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# handlers file for roles/init-vps
|
||||
- name: Executing relevant files for software installation from git repository
|
||||
block:
|
||||
- name: Finalizing quartz installation
|
||||
listen: quartz
|
||||
block:
|
||||
- name: Installing NodeJS dependencies of quartz software
|
||||
community.general.npm:
|
||||
executable: "{{ ansible_facts['user_dir'] }}/.nvm/versions/node/v24.11.1/lib/node_modules/npm"
|
||||
path: "{{ ansible_facts['user_dir'] }}/repos/.foreign/quartz"
|
||||
state: latest
|
||||
- name: Configuring quartz software
|
||||
block:
|
||||
- name: Initializing quartz website
|
||||
ansible.builtin.command:
|
||||
chdir: "{{ ansible_facts['user_dir'] }}/repos/.foreign/quartz"
|
||||
cmd: npx quartz create
|
||||
register: stdout
|
||||
changed_when: stdout.rc == 0
|
||||
- name: Installing quartz plugins referenced in website template
|
||||
ansible.builtin.command:
|
||||
chdir: "{{ ansible_facts['user_dir'] }}/repos/.foreign/quartz"
|
||||
cmd: npx quartz plugin install --from-config
|
||||
register: stdout
|
||||
changed_when: stdout.rc == 0
|
||||
# - name: Starting quartz site web server
|
||||
# ansible.builtin.command:
|
||||
# chdir: "{{ ansible_facts['user_dir'] }}/repos/.foreign/quartz"
|
||||
# cmd: npx quartz build --serve
|
||||
# register: stdout
|
||||
# changed_when: stdout
|
||||
- name: Committing requisite actions for building software from source archives
|
||||
block:
|
||||
- name: Finalizing building of Surge
|
||||
listen: surge
|
||||
block:
|
||||
- name: Hardlinking Surge executable
|
||||
ansible.builtin.file:
|
||||
src: "{{ ansible_facts['user_dir'] }}/downloads/archives/released/surge/surge"
|
||||
dest: "{{ ansible_facts['user_dir'] }}/.local/bin/surge"
|
||||
state: hard
|
||||
mode: "755"
|
||||
- name: Copying Surge executable
|
||||
become: true
|
||||
ansible.builtin.copy:
|
||||
src: "{{ ansible_facts['user_dir'] }}/downloads/archives/released/surge/surge"
|
||||
dest: /usr/bin/surge
|
||||
owner: root
|
||||
group: root
|
||||
mode: "755"
|
||||
force: true
|
||||
backup: false
|
||||
- name: Setting up Quartz
|
||||
ansible.builtin.include_tasks:
|
||||
file: tasks/contingent/pkg/quartz.yml
|
||||
listen: quartz
|
||||
|
||||
26
roles/init-server/tasks/contingent/pkg/quartz.yml
Normal file
26
roles/init-server/tasks/contingent/pkg/quartz.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
- name: Installing NodeJS dependencies of quartz software
|
||||
community.general.npm:
|
||||
executable: "{{ ansible_facts['user_dir'] }}/.nvm/versions/node/v24.11.1/lib/node_modules/npm"
|
||||
path: "{{ ansible_facts['user_dir'] }}/repos/.foreign/quartz"
|
||||
state: latest
|
||||
- name: Configuring quartz software
|
||||
block:
|
||||
- name: Initializing quartz website
|
||||
ansible.builtin.command:
|
||||
chdir: "{{ ansible_facts['user_dir'] }}/repos/.foreign/quartz"
|
||||
cmd: npx quartz create
|
||||
register: stdout
|
||||
changed_when: stdout.rc == 0
|
||||
- name: Installing quartz plugins referenced in website template
|
||||
ansible.builtin.command:
|
||||
chdir: "{{ ansible_facts['user_dir'] }}/repos/.foreign/quartz"
|
||||
cmd: npx quartz plugin install --from-config
|
||||
register: stdout
|
||||
changed_when: stdout.rc == 0
|
||||
# - name: Starting quartz site web server
|
||||
# ansible.builtin.command:
|
||||
# chdir: "{{ ansible_facts['user_dir'] }}/repos/.foreign/quartz"
|
||||
# cmd: npx quartz build --serve
|
||||
# register: stdout
|
||||
# changed_when: stdout
|
||||
18
roles/init-server/tasks/harden.yml
Normal file
18
roles/init-server/tasks/harden.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# tasks file for roles/init-vps
|
||||
- name: Checking whether administrative login used
|
||||
when: ansible_user not in (admins | map(attribute="username") | list) and ansible_user != "root"
|
||||
ansible.builtin.fail:
|
||||
msg: Must use administrative user for subsequent tasks
|
||||
- name: Hardening SSH service for the Linode VPS
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/harden.conf
|
||||
dest: /etc/ssh/sshd_config.d/harden.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: 'sshd -t -f %s'
|
||||
register: ssh_hardened
|
||||
@@ -1,16 +1,25 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# tasks file for roles/init-vps
|
||||
- name: Creating prerequisite directory tree
|
||||
- name: Checking whether administrative login used
|
||||
when: ansible_user not in (admins | map(attribute="username") | list)
|
||||
ansible.builtin.fail:
|
||||
msg: Must use administrative user for subsequent tasks
|
||||
- name: Creating prerequisite directory tree for installation scripts
|
||||
ansible.builtin.file:
|
||||
path: "{{ ansible_facts['user_dir'] }}/.local/bin"
|
||||
recurse: true
|
||||
state: directory
|
||||
- name: Creating prerequisite directory tree
|
||||
- name: Creating prerequisite directory tree for unarchived archives
|
||||
ansible.builtin.file:
|
||||
path: "{{ ansible_facts['user_dir'] }}/downloads/archives/released"
|
||||
recurse: true
|
||||
state: directory
|
||||
- name: Creating prerequisite directory tree for package installation executables
|
||||
ansible.builtin.file:
|
||||
path: "{{ ansible_facts['user_dir'] }}/.local_pkgs"
|
||||
recurse: true
|
||||
state: directory
|
||||
- name: Installing Linux software
|
||||
when: ansible_facts["system"] == "Linux"
|
||||
block:
|
||||
@@ -19,7 +28,7 @@
|
||||
become: true
|
||||
block:
|
||||
- name: Registering a package signing key
|
||||
when: item.key is defined and item.key_path is defined
|
||||
when: item.key != None and item.key_path != None
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ item.key }}"
|
||||
dest: "{{ item.key_path | default('/etc/apt/keyrings/') }}"
|
||||
@@ -29,14 +38,16 @@
|
||||
force: true
|
||||
backup: true
|
||||
loop: "{{ pkgs.mngr.core + pkgs.mngr.userspace | rejectattr('key', 'search', '\\.deb$') }}"
|
||||
- name: Premature stop
|
||||
ansible.builtin.meta: end_play
|
||||
- name: Installing a package signing key
|
||||
when: item.key is defined
|
||||
when: item.key != None
|
||||
ansible.builtin.apt:
|
||||
deb: "{{ item.key }}"
|
||||
state: present
|
||||
loop: "{{ pkgs.mngr.core + pkgs.mngr.userspace | selectattr('key', 'search', '\\.deb$') }}"
|
||||
- name: Registering a package source
|
||||
when: item.src_entry is defined and item.src_path is defined
|
||||
when: item.src_entry != None and item.src_path != None
|
||||
ansible.builtin.copy:
|
||||
content: "{{ item.src_entry }}"
|
||||
dest: "{{ item.src_path }}"
|
||||
@@ -47,7 +58,7 @@
|
||||
backup: true
|
||||
loop: "{{ pkgs.mngr.core + pkgs.mngr.userspace }}"
|
||||
- name: Installing a local package in managed node
|
||||
when: item.uri is defined
|
||||
when: item.uri != None
|
||||
ansible.builtin.apt:
|
||||
deb: "{{ item.uri }}"
|
||||
update_cache: true
|
||||
@@ -55,7 +66,7 @@
|
||||
notify: "{{ item.name }}"
|
||||
loop: "{{ pkgs.mngr.core + pkgs.mngr.userspace | selectattr('uri', 'search', '\\.deb$') }}"
|
||||
- name: Installing a package
|
||||
when: item.name is defined and item.uri is undefined
|
||||
when: item.name != None and item.uri == None
|
||||
ansible.builtin.package:
|
||||
name: "{{ item.name }}"
|
||||
update_cache: true
|
||||
@@ -65,7 +76,7 @@
|
||||
tags:
|
||||
- get_mngr_pkgs
|
||||
- name: Installing software by executing installation shell scripts
|
||||
when: item.src is defined
|
||||
when: item.src != None
|
||||
block:
|
||||
- name: Acquiring installation shell script
|
||||
ansible.builtin.get_url:
|
||||
|
||||
@@ -3,11 +3,26 @@
|
||||
# tasks file for roles/init-vps
|
||||
# @TODO complete below tasks
|
||||
- name: Checking whether administrative login used
|
||||
when: ansible_facts["user_id"] not in (admins | map(attribute="username") | list)
|
||||
when: ansible_user not in (admins | map(attribute="username") | list)
|
||||
ansible.builtin.fail:
|
||||
msg: Administrative user does not exist on managed node
|
||||
msg: Must use administrative user for subsequent tasks
|
||||
- name: Setting approved SSH authentication procedures
|
||||
when: harden and ansible_facts["system"] == "Linux"
|
||||
become: true
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/auth.conf
|
||||
dest: /etc/ssh/sshd_config.d/auth.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t %s"
|
||||
register: ssh_authenticator
|
||||
tags:
|
||||
- ssh_secure_auth
|
||||
- name: Prohibiting SSH root login
|
||||
when: harden
|
||||
when: harden and ansible_facts["system"] == "Linux"
|
||||
become: true
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/denyroot.conf
|
||||
@@ -19,7 +34,7 @@
|
||||
backup: true
|
||||
validate: "sshd -t %s"
|
||||
- name: Create groups for FTP services
|
||||
when: "'internal-sftp' in item.service or 'proftpd' in item.service or 'vsftpd' in item.service"
|
||||
when: "'sftp-server' in item.service or 'proftpd' in item.service or 'vsftpd' in item.service"
|
||||
become: true
|
||||
ansible.builtin.group:
|
||||
name: "{{ item.username }}"
|
||||
@@ -28,14 +43,27 @@
|
||||
loop: "{{ sys_users }}"
|
||||
register: ftp_groups
|
||||
- name: Configuring SFTP for FTP group
|
||||
when: ansible_facts["system"] == "Linux"
|
||||
become: true
|
||||
ansible.builtin.template:
|
||||
src: sshd_config.d/sftp.conf.j2
|
||||
dest: /etc/ssh/sshd_config.d/sftp.conf
|
||||
ansible.builtin.blockinfile:
|
||||
# src: sshd_config.d/sftp.conf.j2
|
||||
# dest: /etc/ssh/sshd_config.d/sftp.conf
|
||||
path: /etc/ssh/sshd_config.d/sftp.conf
|
||||
block: |
|
||||
Match Group {{ item.name }}
|
||||
ForceCommand internal-sftp -d /%u
|
||||
ChrootDirectory /srv/{{ item.name}}
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
X11Forwarding no
|
||||
append_newline: true
|
||||
marker_begin: "BEGIN FTP GROUP SSH MATCH BLOCK"
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t %s"
|
||||
state: present
|
||||
loop: "{{ ftp_groups.results }}"
|
||||
register: configured_sftp
|
||||
@@ -1,443 +0,0 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# tasks file for roles/init-vps
|
||||
# @NOTE server deployment method is based on task tags compiled herein
|
||||
# @TODO review 'loop' task attribute return values and make compliant changes
|
||||
- name: Finding SSH public keys for root
|
||||
ansible.builtin.find:
|
||||
paths: "{{ cnode_homedir | default('/home/' ~ ansible_user ~ '/.ssh') }}" # @TODO define 'cnode_homedir' in playbook
|
||||
patterns: "{{ ['^'] | product(keys) | map('join') | list }}"
|
||||
file_type: file
|
||||
use_regex: true
|
||||
register: ssh_keypairs
|
||||
- name: Bootstrapping VPS
|
||||
block:
|
||||
- name: Creating VPS via Linode VPS service API
|
||||
block:
|
||||
- name: Creating the VPS
|
||||
linode.cloud.instance:
|
||||
api_token: "{{ token }}"
|
||||
label: "{{ instance }}"
|
||||
type: g6-standard-2
|
||||
image: "{{ operating_system }}"
|
||||
disk_encryption: enabled
|
||||
region: "{{ origin }}"
|
||||
private_ip: true
|
||||
root_pass: "{{ password }}"
|
||||
authorized_keys: "{{ ssh_keypairs.files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | map('lookup', 'file') | list }}"
|
||||
state: present
|
||||
register: new_instance
|
||||
- name: Waiting for that VPS to come online
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
delegate_facts: true
|
||||
ansible.builtin.wait_for_connection:
|
||||
delay: 20
|
||||
timeout: 300
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
- name: Checking if that VPS has required operating system
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
delegate_facts: true
|
||||
when: ansible_facts["system"] != "Linux"
|
||||
ansible.builtin.fail:
|
||||
msg: Unsupported operating system found
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
- name: Checking if that VPS has required Linux distro
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
delegate_facts: true
|
||||
when: ansible_facts["system"] == "Linux" and ansible_facts["os_family"] != "Debian"
|
||||
ansible.builtin.fail:
|
||||
msg: Unsupported Linux distro found
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
tags:
|
||||
- linode
|
||||
tags:
|
||||
- vps
|
||||
- name: Bootstrapping homeserver
|
||||
block:
|
||||
- name: Installing operating system or distro in server
|
||||
when: operating_system is defined
|
||||
block:
|
||||
- name: Creating a server
|
||||
block: []
|
||||
tags:
|
||||
- unimplemented
|
||||
- name: Waiting for that VPS to come online
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
delegate_facts: true
|
||||
ansible.builtin.wait_for_connection:
|
||||
delay: 20
|
||||
timeout: 300
|
||||
vars:
|
||||
ansible_user: root
|
||||
- name: Checking if that server has required operating system
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
delegate_facts: true
|
||||
when: ansible_facts["system"] != "Linux"
|
||||
ansible.builtin.fail:
|
||||
msg: Unsupported operating system found
|
||||
vars:
|
||||
ansible_user: root
|
||||
- name: Checking if that server has required Linux distro
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
delegate_facts: true
|
||||
when: ansible_facts["system"] == "Linux" and ansible_facts["os_family"] != "Debian"
|
||||
ansible.builtin.fail:
|
||||
msg: Unsupported Linux distro found
|
||||
vars:
|
||||
ansible_user: root
|
||||
- name: Providing authorized keys for server root account
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
delegate_facts: true
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ ansible_facts['user_id'] }}"
|
||||
key: "{{ lookup('file', item) }}"
|
||||
state: present
|
||||
vars:
|
||||
ansible_root: root
|
||||
loop: "{{ ssh_keypairs.files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | list }}"
|
||||
tags:
|
||||
- lan
|
||||
- name: Starting SSH hardening
|
||||
when: harden
|
||||
delegate_facts: true
|
||||
block:
|
||||
- name: Hardening SSH service for the Linode VPS
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/harden.conf
|
||||
dest: /etc/ssh/sshd_config.d/harden.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t %s"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
register: ssh_hardened
|
||||
tags:
|
||||
- linode
|
||||
- name: Hardening SSH service for the server
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/harden.conf
|
||||
dest: /etc/ssh/sshd_config.d/harden.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t %s"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
register: ssh_hardened
|
||||
tags:
|
||||
- lan
|
||||
- name: Starting user and group creation for SSH access
|
||||
block:
|
||||
- name: Creating group remote for managing SSH access
|
||||
delegate_facts: true
|
||||
block:
|
||||
- name: In the Linode VPS
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
ansible.builtin.group:
|
||||
name: remote
|
||||
system: true
|
||||
state: present
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
register: remote_group
|
||||
tags:
|
||||
- linode
|
||||
- name: In the server
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
ansible.builtin.group:
|
||||
name: remote
|
||||
system: true
|
||||
state: present
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
register: remote_group
|
||||
tags:
|
||||
- lan
|
||||
- name: Creating an administrative user
|
||||
delegate_facts: true
|
||||
when: ansible_facts["system"] == "Linux"
|
||||
block:
|
||||
- name: In the Linode VPS
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
ansible.builtin.user:
|
||||
name: "{{ item.username }}"
|
||||
comment: administrator
|
||||
group: "{{ item.username }}"
|
||||
groups:
|
||||
- "{{ remote_group.name }}"
|
||||
- sudo # @NOTE used by Debian
|
||||
append: true
|
||||
generate_ssh_key: true
|
||||
create_home: true
|
||||
password: "{{ item.password }}"
|
||||
shell: "/bin/bash"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
loop: "{{ admins }}"
|
||||
register: admin_users
|
||||
tags:
|
||||
- linode
|
||||
- name: In the server
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
ansible.builtin.user:
|
||||
name: "{{ item.username }}"
|
||||
comment: administrator
|
||||
group: "{{ item.username }}"
|
||||
groups:
|
||||
- "{{ remote_group.name }}"
|
||||
- sudo # @NOTE used by Debian
|
||||
append: true
|
||||
generate_ssh_key: true
|
||||
create_home: true
|
||||
password: "{{ item.password }}"
|
||||
shell: "/bin/bash"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
loop: "{{ admins }}"
|
||||
register: admin_users
|
||||
tags:
|
||||
- lan
|
||||
- name: Finding SSH public keys for an administrative user
|
||||
when: item.username in (admin_users.results | map(attribute="name") | list)
|
||||
ansible.builtin.find:
|
||||
paths: "{{ cnode_homedir | default('/home/' ~ ansible_user ~ '/.ssh') }}" # @TODO define 'cnode_homedir' in playbook
|
||||
patterns: "{{ ['^'] | product(item.keys) | map('join') | list }}"
|
||||
file_type: file
|
||||
use_regex: true
|
||||
loop: "{{ admins }}"
|
||||
register: admin_ssh_keypairs
|
||||
- name: Authorizing SSH public key for an administrative user
|
||||
delegate_facts: true
|
||||
block:
|
||||
- name: In the Linode VPS
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ admin_users.results[idx] }}"
|
||||
key: "{{ admin_ssh_keypairs.results[idx].files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | map('lookup', 'file') | list | map('join','\n') }}"
|
||||
state: present
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
loop: "{{ admin_users.results }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
register: ssh_authorizations
|
||||
tags:
|
||||
- linode
|
||||
- name: In the server
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ admin_users.results[idx] }}"
|
||||
key: "{{ admin_ssh_keypairs.results[idx].files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | map('lookup', 'file') | list | map('join','\n') }}"
|
||||
state: present
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
loop: "{{ admin_users.results }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
register: ssh_authorizations
|
||||
tags:
|
||||
- lan
|
||||
- name: Allowing sole SSH access to users in group remote
|
||||
delegate_facts: true
|
||||
block:
|
||||
- name: In Linode VPS
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
ansible.builtin.template:
|
||||
src: sshd_config.d/allowance.conf.j2 # @TODO create corresponding role template file
|
||||
dest: /etc/ssh/sshd_config.d/allowance.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t %s"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
register: ssh_gatekept
|
||||
tags:
|
||||
- linode
|
||||
- name: In Linode VPS
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
ansible.builtin.template:
|
||||
src: sshd_config.d/allowance.conf.j2 # @TODO create corresponding role template file
|
||||
dest: /etc/ssh/sshd_config.d/allowance.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t %s"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
register: ssh_gatekept
|
||||
tags:
|
||||
- lan
|
||||
- name: Setting approved SSH authentication procedures
|
||||
when: harden
|
||||
delegate_facts: true
|
||||
block:
|
||||
- name: In the Linode VPS
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/auth.conf
|
||||
dest: /etc/ssh/sshd_config.d/auth.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t %s"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
register: ssh_authenticator
|
||||
tags:
|
||||
- linode
|
||||
- name: In the server
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/auth.conf
|
||||
dest: /etc/ssh/sshd_config.d/auth.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t %s"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
register: ssh_authenticator
|
||||
tags:
|
||||
- lan
|
||||
- name: Installing core packages
|
||||
delegate_facts: true
|
||||
block:
|
||||
- name: In the Linode VPS
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
block:
|
||||
- name: Registering a package signing key
|
||||
when: ansible_facts["os_family"] == "Debian" and item.key is defined and item.key_path is defined
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ item.key }}"
|
||||
dest: "{{ item.key_path | default('/etc/apt/keyrings/') }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
loop: "{{ pkgs.mngr.core | rejectattr('key', 'search', '\\.deb$') }}"
|
||||
- name: Installing a package signing key
|
||||
when: ansible_facts["os_family"] == "Debian" and item.key is defined
|
||||
ansible.builtin.apt:
|
||||
deb: "{{ item.key }}"
|
||||
state: latest
|
||||
loop: "{{ pkgs.mngr.core | selectattr('key', 'search', '\\.deb$') }}"
|
||||
- name: Registering a package source
|
||||
when: ansible_facts["os_family"] == "Debian" and item.src_entry is defined and item.src_path is defined
|
||||
ansible.builtin.copy:
|
||||
content: "{{ item.src_entry }}"
|
||||
dest: "{{ item.src_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
loop: "{{ pkgs.mngr.core }}"
|
||||
- name: Installing a local package in managed node
|
||||
when: ansible_facts["os_family"] == "Debian" and item.uri is defined
|
||||
ansible.builtin.apt:
|
||||
deb: "{{ item.uri }}"
|
||||
update_cache: true
|
||||
state: latest
|
||||
loop: "{{ pkgs.mngr.core | selectattr('uri', 'search', '\\.deb$') }}"
|
||||
- name: Installing a package
|
||||
when: item.name is defined and item.uri is undefined
|
||||
ansible.builtin.package:
|
||||
name: "{{ item.name }}"
|
||||
update_cache: true
|
||||
state: latest
|
||||
notify: "{{ item.name }}" # @TODO create corresponding roles/init-vps handlers
|
||||
loop: "{{ pkgs.mngr.core| rejectattr('uri', 'search', '\\.deb$') }}"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
tags:
|
||||
- linode
|
||||
- name: In the server
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
block:
|
||||
- name: Registering a package signing key
|
||||
when: ansible_facts["os_family"] == "Debian" and item.key is defined and item.key_path is defined
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ item.key }}"
|
||||
dest: "{{ item.key_path | default('/etc/apt/keyrings/') }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
loop: "{{ pkgs.mngr.core | rejectattr('key', 'search', '\\.deb$') }}"
|
||||
- name: Installing a package signing key
|
||||
when: ansible_facts["os_family"] == "Debian" and item.key is defined
|
||||
ansible.builtin.apt:
|
||||
deb: "{{ item.key }}"
|
||||
state: latest
|
||||
loop: "{{ pkgs.mngr.core | selectattr('key', 'search', '\\.deb$') }}"
|
||||
- name: Registering a package source
|
||||
when: ansible_facts["os_family"] == "Debian" and item.src_entry is defined and item.src_path is defined
|
||||
ansible.builtin.copy:
|
||||
content: "{{ item.src_entry }}"
|
||||
dest: "{{ item.src_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
loop: "{{ pkgs.mngr.core }}"
|
||||
- name: Installing a local package in managed node
|
||||
when: ansible_facts["os_family"] == "Debian" and item.uri is defined
|
||||
ansible.builtin.apt:
|
||||
deb: "{{ item.uri }}"
|
||||
update_cache: true
|
||||
state: latest
|
||||
notify: "{{ item.name }}"
|
||||
loop: "{{ pkgs.mngr.core | selectattr('uri', 'search', '\\.deb$') }}"
|
||||
- name: Installing a package
|
||||
when: item.name is defined and item.uri is undefined
|
||||
ansible.builtin.package:
|
||||
name: "{{ item.name }}"
|
||||
update_cache: true
|
||||
state: latest
|
||||
notify: "{{ item.name }}" # @TODO create corresponding roles/init-vps handlers
|
||||
loop: "{{ pkgs.mngr.core| rejectattr('uri', 'search', '\\.deb$') }}"
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
tags:
|
||||
- lan
|
||||
tags:
|
||||
- get_pkgs
|
||||
91
roles/init-server/tasks/spawn.yml
Normal file
91
roles/init-server/tasks/spawn.yml
Normal file
@@ -0,0 +1,91 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# tasks file for roles/init-vps
|
||||
# @NOTE server deployment method is based on task tags compiled herein
|
||||
# @TODO review 'loop' task attribute return values and make compliant changes
|
||||
- name: Finding SSH public keys for root
|
||||
ansible.builtin.find:
|
||||
paths: "{{ cnode_homedir | default('/home/' ~ ansible_user ~ '/.ssh') }}" # @TODO define 'cnode_homedir' in playbook
|
||||
patterns: "{{ ['^'] | product(keys) | map('join') | list }}"
|
||||
file_type: file
|
||||
use_regex: true
|
||||
register: ssh_keypairs
|
||||
- name: Bootstrapping VPS
|
||||
block:
|
||||
- name: Creating VPS via Linode VPS service API
|
||||
block:
|
||||
- name: Creating the VPS
|
||||
linode.cloud.instance:
|
||||
api_token: "{{ token }}"
|
||||
label: "{{ instance }}"
|
||||
type: g6-standard-2
|
||||
image: "{{ operating_system }}"
|
||||
disk_encryption: enabled
|
||||
region: "{{ origin }}"
|
||||
private_ip: true
|
||||
root_pass: "{{ password }}"
|
||||
authorized_keys: "{{ ssh_keypairs.files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | map('lookup', 'file') | list }}"
|
||||
state: present
|
||||
register: new_instance
|
||||
- name: Waiting for that VPS to come online
|
||||
delegate_to: "{{ new_instance.instance[ip_pref][0] }}"
|
||||
delegate_facts: true
|
||||
ansible.builtin.wait_for_connection:
|
||||
delay: 20
|
||||
timeout: 300
|
||||
vars:
|
||||
ansible_ssh_private_key_file: "{{ chosen_privkey | default(ssh_keypairs.files | rejectattr('path', 'search', '\\.pub$') | map(attribute='path') | list | random) }}" # @TODO define 'chosen_privkey'in playbook
|
||||
ansible_user: root
|
||||
tags:
|
||||
- linode
|
||||
tags:
|
||||
- vps
|
||||
- name: Bootstrapping homeserver
|
||||
block:
|
||||
- name: Installing operating system or distro in server
|
||||
when: operating_system != None
|
||||
block:
|
||||
- name: Creating a server
|
||||
block: []
|
||||
tags:
|
||||
- unimplemented
|
||||
- name: Waiting for that server to come online
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
delegate_facts: true
|
||||
remote_user: root
|
||||
ansible.builtin.wait_for_connection:
|
||||
delay: 20
|
||||
timeout: 300
|
||||
vars:
|
||||
ansible_user: root
|
||||
- name: Checking if that server has required operating system
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
delegate_facts: true
|
||||
remote_user: root
|
||||
when: ansible_facts["system"] != "Linux"
|
||||
ansible.builtin.fail:
|
||||
msg: Unsupported operating system found
|
||||
vars:
|
||||
ansible_user: root
|
||||
- name: Checking if that server has required Linux distro
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
delegate_facts: true
|
||||
remote_user: root
|
||||
when: ansible_facts["system"] == "Linux" and ansible_facts["os_family"] != "Debian"
|
||||
ansible.builtin.fail:
|
||||
msg: Unsupported Linux distro found
|
||||
vars:
|
||||
ansible_user: root
|
||||
- name: Providing authorized keys for server root account
|
||||
delegate_to: "{{ hostvars[instance]['ansible_default_' ~ ip_pref].address }}"
|
||||
delegate_facts: true
|
||||
remote_user: root
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ ansible_user }}"
|
||||
key: "{{ lookup('file', item) }}"
|
||||
state: present
|
||||
vars:
|
||||
ansible_root: root
|
||||
loop: "{{ ssh_keypairs.files | selectattr('path', 'search', '\\.pub$') | map(attribute='path') | list }}"
|
||||
tags:
|
||||
- lan
|
||||
93
roles/init-server/tasks/ssh-users.yml
Normal file
93
roles/init-server/tasks/ssh-users.yml
Normal file
@@ -0,0 +1,93 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# tasks file for roles/init-vps
|
||||
- name: Checking whether administrative or root login used
|
||||
when: ansible_user not in (admins | map(attribute="username") | list) and ansible_user != "root"
|
||||
ansible.builtin.fail:
|
||||
msg: Must use administrative or root user for subsequent tasks
|
||||
- name: Starting user and group creation for SSH access
|
||||
block:
|
||||
- name: Creating group remote for managing SSH access
|
||||
become: true
|
||||
ansible.builtin.group:
|
||||
name: remote
|
||||
system: true
|
||||
state: present
|
||||
register: remote_group
|
||||
tags:
|
||||
- lan
|
||||
- name: Creating an administrative user
|
||||
become: true
|
||||
ansible.builtin.user:
|
||||
name: "{{ item.username }}"
|
||||
comment: administrator
|
||||
groups:
|
||||
- "{{ remote_group.name | default('remote') }}"
|
||||
- sudo # @NOTE used by Debian
|
||||
append: true
|
||||
generate_ssh_key: true
|
||||
create_home: true
|
||||
password: "{{ item.password }}"
|
||||
shell: "/bin/bash"
|
||||
loop: "{{ admins }}"
|
||||
register: admin_users
|
||||
tags:
|
||||
- lan
|
||||
- name: Finding SSH public keys for an administrative user
|
||||
delegate_facts: true
|
||||
delegate_to: localhost
|
||||
when: item.username in (admin_users.results | map(attribute="name") | list)
|
||||
ansible.builtin.find:
|
||||
paths: "{{ local_facts['user_dir'] }}/.ssh" # @TODO define 'cnode_homedir' in playbook
|
||||
patterns: "{{ ['^'] | product(item.ssh_keys) | map('join') | list }}"
|
||||
file_type: file
|
||||
use_regex: true
|
||||
loop: "{{ admins }}"
|
||||
register: admin_ssh_keypairs
|
||||
- name: Creating list wherein each SSH public key is associated with a user
|
||||
ansible.builtin.set_fact:
|
||||
pubkey_users: "{{ [admin_users.results[idx].name] | product(admin_ssh_keypairs.results[idx].files | selectattr('path', 'search', '\\.pub$') | map(attribute='path')) }}"
|
||||
loop: "{{ admins }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
- name: Authorizing SSH public key for an administrative user
|
||||
become: true
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ item[0] }}"
|
||||
key: "{{ lookup('file', item[1]) }}"
|
||||
state: present
|
||||
loop: "{{ pubkey_users }}"
|
||||
register: ssh_authorizations
|
||||
tags:
|
||||
- lan
|
||||
- name: Setting approved SSH authentication procedures
|
||||
when: harden and ansible_facts["system"] == "Linux"
|
||||
become: true
|
||||
ansible.builtin.copy:
|
||||
src: sshd_config.d/auth.conf
|
||||
dest: /etc/ssh/sshd_config.d/auth.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t -f %s"
|
||||
register: ssh_authenticator
|
||||
tags:
|
||||
- lan
|
||||
- ssh_secure_auth
|
||||
- name: Allowing sole SSH access to users in group remote
|
||||
when: ansible_facts["system"] == "Linux"
|
||||
become: true
|
||||
ansible.builtin.template:
|
||||
src: sshd_config.d/allowance.conf.j2 # @TODO create corresponding role template file
|
||||
dest: /etc/ssh/sshd_config.d/allowance.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "644"
|
||||
force: true
|
||||
backup: true
|
||||
validate: "sshd -t -f %s"
|
||||
register: ssh_gatekept
|
||||
tags:
|
||||
- lan
|
||||
@@ -1,9 +0,0 @@
|
||||
{% for item in ftp_groups.results %}
|
||||
Match Group {{ item.name }}
|
||||
ForceCommand internal-sftp -d /%u
|
||||
ChrootDirectory /srv/{{ item.name}}
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
X11Forwarding no
|
||||
|
||||
{% endfor %}
|
||||
Reference in New Issue
Block a user