diff --git a/README.md b/README.md index 772d99d..9e32bf4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ # Ansible-SnipeIT -Ansible Playbook to deploy an instance of Snipe IT to a RHEL based Linux host \ No newline at end of file +Ansible Playbook to deploy an instance of Snipe IT to a RHEL based Linux host + +## Variables + +| Variable | Required | Default | Choices | Description | +| -------- | -------- | ------- | ------- | ----------- | +| | | | | | + +## Example diff --git a/collections/requirements.yml b/collections/requirements.yml new file mode 100644 index 0000000..08e2610 --- /dev/null +++ b/collections/requirements.yml @@ -0,0 +1,3 @@ +--- +collections: +- community.general diff --git a/hosts.yml b/hosts.yml new file mode 100644 index 0000000..7195db5 --- /dev/null +++ b/hosts.yml @@ -0,0 +1,11 @@ +--- +# file: hosts + +snipeit: + hosts: + server1: + ansible_host: 10.10.2.12 + + vars: + ansible_user: User + \ No newline at end of file diff --git a/roles/snipeit/defaults/main.yml b/roles/snipeit/defaults/main.yml new file mode 100644 index 0000000..0ab4d8d --- /dev/null +++ b/roles/snipeit/defaults/main.yml @@ -0,0 +1,57 @@ +--- +# file: roles/snipeit/defaults/main.yml + +snipeit_ssl_crt: "/etc/ssl/dc-assets01/live/fullchain.pem" +snipeit_ssl_key: "/etc/ssl/dc-assets01/live/privkey.pem" + +snipeit_user: "snipeit" + +snipeit_install_path: "/opt/snipeit" + +snipeit_config_app_env: "production" +snipeit_config_debug: "false" +snipeit_config_app_url: "https://{{ ansible_host }}" +snipeit_config_app_key: "ChangeMe" +snipeit_config_app_locale: "en-US" +snipeit_config_app_timezone: "UTC" +snipeit_config_app_max_results: "500" + +snipeit_config_private_filesystem_disk: "local" +snipeit_config_public_filesystem_disk: "local_public" + +snipeit_config_db_connection: "mysql" +snipeit_config_db_host: "127.0.0.1" +snipeit_config_db_port: "3306" +snipeit_config_db_database: "snipeit" +snipeit_config_db_username: "snipe_user" +snipeit_config_db_password: "changeme" +snipeit_config_db_prefix: "null" +snipeit_config_db_dump_path: "/usr/bin" +snipeit_config_db_charset: "utf8mb4" +snipeit_config_db_collation: "utf8mb4_unicode_ci" + +snipeit_config_db_ssl: "false" +snipeit_config_db_ssl_is_paas: "false" +snipeit_config_db_ssl_key_path: "null" +snipeit_config_db_ssl_cert_path: "null" +snipeit_config_db_ssl_ca_path: "null" +snipeit_config_db_ssl_cipher: "null" +snipeit_config_db_ssl_verify_server: "null" + +snipeit_config_mail_driver: "smtp" +snipeit_config_mail_host: "email-smtp.us-west-2.amazonaws.com" +snipeit_config_mail_port: "587" +snipeit_config_mail_username: "YOURUSERNAME" +snipeit_config_mail_password: "YOURPASSWORD" +snipeit_config_mail_encryption: "null" +snipeit_config_mail_from_addr: "you@example.com" +snipeit_config_mail_from_name: "Snipe-IT" +snipeit_config_mail_replyto_addr: "you@example.com" +snipeit_config_mail_replyto_name: "Snipe-IT" +snipeit_config_mail_auto_embed_method: "attachment" + +snipeit_config_image_lib: "gd" + +snipeit_nginx_config: "snipeit.conf.j2" +snipeit_nginx_servername: "{{ snipeit_config_app_url | regex_findall('(?<=https://).*') | first }}" +snipeit_nginx_config_output: "{{ snipeit_nginx_config.replace('.j2','') }}" diff --git a/roles/snipeit/handlers/main.yml b/roles/snipeit/handlers/main.yml new file mode 100644 index 0000000..8a57be8 --- /dev/null +++ b/roles/snipeit/handlers/main.yml @@ -0,0 +1,24 @@ +--- +# file: roles/snipeit/handlers/main.yml + +- name: Reload nginx + service: + name: nginx + state: reloaded + +- name: Reload firewalld + service: + name: firewalld + state: reloaded + +- name: Restorecon nginx config + command: "restorecon -irv /etc/nginx/conf.d/{{ snipeit_nginx_config_output }}" + +- name: Restorecon snipeit storage + command: "restorecon -irv {{ snipeit_install_path }}/storage" + +- name: Restorecon snipeit public + command: "restorecon -irv {{ snipeit_install_path }}/public" + +- name: Restorecon snipeit cache + command: "restorecon -irv {{ snipeit_install_path }}/bootstrap/cache" diff --git a/roles/snipeit/tasks/main.yml b/roles/snipeit/tasks/main.yml new file mode 100644 index 0000000..cb8132f --- /dev/null +++ b/roles/snipeit/tasks/main.yml @@ -0,0 +1,271 @@ +--- +# file: roles/snipeit/tasks/main.yml + +- name: Install EPEL RPM + package: + name: "https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm" + state: present + disable_gpg_check: True + +- name: Install packages + package: + name: + - nginx + - mariadb-server + - php + - php-mysqlnd + - php-json + - php-openssl + - php-pdo + - php-mbstring + - php-curl + - php-ldap + - php-fileinfo + - php-bcmath + - php-xml + - php-exif + - php-gd + - php-sodium + - php-zip + - git + state: latest + +- name: Install PyMySQL + pip: + name: pymysql + state: present + +- name: Start and enable php + service: + name: php-fpm + state: started + enabled: yes + +- name: Start and enable mariadb + service: + name: mariadb + state: started + enabled: yes + +- name: Delete anonymous MySQL server user + mysql_user: + user: "" + host_all: yes + state: "absent" + check_implicit_admin: true + login_unix_socket: /var/lib/mysql/mysql.sock + +- name: Remove the default MySQL test database + mysql_db: + db: test + state: absent + check_implicit_admin: true + login_unix_socket: /var/lib/mysql/mysql.sock + +- name: Creating DB + mysql_db: + name: "{{ snipeit_config_db_database }}" + state: present + encoding: "{{ snipeit_config_db_charset }}" + check_implicit_admin: true + login_unix_socket: /var/lib/mysql/mysql.sock + +- name: Creating DB User + mysql_user: + name: "{{ snipeit_config_db_username }}" + password: "{{ snipeit_config_db_password }}" + priv: "{{ snipeit_config_db_database + '.*:ALL' }}" + state: present + check_implicit_admin: true + login_unix_socket: /var/lib/mysql/mysql.sock + +- name: Create snipeit user + user: + name: "{{ snipeit_user }}" + shell: /sbin/nologin + comment: "nologin user" + groups: "apache,nginx" + state: present + system: yes + +- name: Deploy nginx configuration file + template: + src: "{{ snipeit_nginx_config }}" + dest: "/etc/nginx/conf.d/{{ snipeit_nginx_config_output }}" + notify: Reload nginx + +- name: Allow nginx to read config file + sefcontext: + target: "/etc/nginx/conf.d/{{ snipeit_nginx_config_output }}" + seuser: system_u + setype: httpd_config_t + state: present + notify: Restorecon nginx config + +- name: Set httpd_unified flag + seboolean: + name: httpd_unified + state: true + persistent: true + +- name: Set httpd_can_network_connect flag + seboolean: + name: httpd_can_network_connect + state: true + persistent: true + +- name: Set httpd_can_sendmail flag + seboolean: + name: httpd_can_sendmail + state: true + persistent: true + +- name: Ensure install directory exists + file: + path: "{{ snipeit_install_path }}" + state: directory + owner: "{{ snipeit_user }}" + group: "root" + +- name: Clone the upstream repo + git: + repo: "https://github.com/snipe/snipe-it" + dest: "{{ snipeit_install_path }}" + force: yes + version: master + become_user: "{{ snipeit_user }}" + +- name: Set owner to non-privileged user + file: + path: "{{ snipeit_install_path }}" + recurse: yes + owner: "{{ snipeit_user }}" + +- name: Update storage directory to allow webserver access + file: + path: "{{ snipeit_install_path }}/storage" + recurse: yes + owner: "{{ snipeit_user }}" + group: apache + mode: '775' + +- name: Set storage secontext definition + sefcontext: + target: "{{ snipeit_install_path }}/storage(/.*)?" + seuser: system_u + setype: httpd_sys_rw_content_t + state: present + notify: Restorecon snipeit storage + +- name: Update public directory to allow webserver access + file: + path: "{{ snipeit_install_path }}/public" + recurse: yes + owner: "{{ snipeit_user }}" + group: apache + mode: '775' + +- name: Set secontext definition + sefcontext: + target: "{{ snipeit_install_path }}/public(/.*)?" + seuser: system_u + setype: httpd_sys_content_t + state: present + notify: Restorecon snipeit public + +- name: Update cache directory to allow webserver access + file: + path: "{{ snipeit_install_path }}/bootstrap/cache" + state: directory + recurse: yes + owner: "{{ snipeit_user }}" + group: apache + mode: '775' + +- name: Set secontext definition + sefcontext: + target: "{{ snipeit_install_path }}/bootstrap/cache(/.*)?" + seuser: system_u + setype: httpd_sys_rw_content_t + state: present + notify: Restorecon snipeit cache + +- name: Download composer + shell: curl -sS https://getcomposer.org/installer | php + args: + chdir: "{{ snipeit_install_path }}" + creates: "{{ snipeit_install_path }}/composer.phar" + register: composer_installed + +- name: Install composer + shell: php composer.phar install --no-dev --prefer-source + args: + chdir: "{{ snipeit_install_path }}" + when: composer_installed.changed + +- name: Check that .env file exists + stat: + path: "{{ snipeit_install_path }}/.env" + register: stat_result + +- name: Capture app_key + block: + - name: Capture existing }/.env" file + slurp: + src: "{{ snipeit_install_path }}/.env" + register: envconfig + + - name: Set fact + set_fact: + snipeit_config_app_key: "{{ envconfig['content'] | b64decode | regex_findall('(?<=APP_KEY=).*') | first }}" + when: envconfig['content'] | b64decode | regex_findall('(?<=APP_KEY=).*') != snipeit_config_app_key + when: stat_result.stat.exists + +- name: Deploy env file + template: + src: "env.j2" + dest: "{{ snipeit_install_path }}/.env" + notify: Reload nginx + +- name: Generate app key for fresh install + shell: "php artisan key:generate --force" + args: + chdir: "{{ snipeit_install_path }}" + when: not stat_result.stat.exists or snipeit_config_app_key == 'ChangeMe' and stat_result.stat.exists + + +- name: Enable firewall rule for access 80 + firewalld: + port: "80/tcp" + permanent: yes + immediate: yes + state: enabled + notify: Reload firewalld + +- name: Enable firewall rule for access 443 + firewalld: + port: "443/tcp" + permanent: yes + immediate: yes + state: enabled + notify: Reload firewalld + +- name: Allow nginx to listen on port 80 + seport: + ports: "80" + proto: "tcp" + setype: http_port_t + state: present + +- name: Allow nginx to listen on port 443 + seport: + ports: "443" + proto: "tcp" + setype: http_port_t + state: present + +- name: Start and enable nginx services + service: + name: nginx + state: started + enabled: yes diff --git a/roles/snipeit/templates/env.j2 b/roles/snipeit/templates/env.j2 new file mode 100644 index 0000000..a26e77e --- /dev/null +++ b/roles/snipeit/templates/env.j2 @@ -0,0 +1,190 @@ +# -------------------------------------------- +# REQUIRED: BASIC APP SETTINGS +# -------------------------------------------- +APP_ENV={{ snipeit_config_app_env }} +APP_DEBUG={{ snipeit_config_debug }} +APP_KEY={{ snipeit_config_app_key }} +APP_URL={{ snipeit_config_app_url }} +APP_TIMEZONE='{{ snipeit_config_app_timezone }}' +APP_LOCALE='{{ snipeit_config_app_locale }}' +MAX_RESULTS={{ snipeit_config_app_max_results }} + + +# -------------------------------------------- +# REQUIRED: UPLOADED FILE STORAGE SETTINGS +# -------------------------------------------- +PRIVATE_FILESYSTEM_DISK={{ snipeit_config_private_filesystem_disk }} +PUBLIC_FILESYSTEM_DISK={{ snipeit_config_public_filesystem_disk }} + +# -------------------------------------------- +# REQUIRED: DATABASE SETTINGS +# -------------------------------------------- +DB_CONNECTION={{ snipeit_config_db_connection }} +DB_HOST={{ snipeit_config_db_host }} +DB_PORT={{ snipeit_config_db_port }} +DB_DATABASE={{ snipeit_config_db_database }} +DB_USERNAME={{ snipeit_config_db_username }} +DB_PASSWORD={{ snipeit_config_db_password }} +DB_PREFIX={{ snipeit_config_db_prefix }} +DB_DUMP_PATH='{{ snipeit_config_db_dump_path }}' +DB_CHARSET={{ snipeit_config_db_charset }} +DB_COLLATION={{ snipeit_config_db_collation }} + +# -------------------------------------------- +# OPTIONAL: SSL DATABASE SETTINGS +# -------------------------------------------- +DB_SSL={{ snipeit_config_db_ssl }} +DB_SSL_IS_PAAS={{ snipeit_config_db_ssl_is_paas }} +DB_SSL_KEY_PATH={{ snipeit_config_db_ssl_key_path }} +DB_SSL_CERT_PATH={{ snipeit_config_db_ssl_cert_path }} +DB_SSL_CA_PATH={{ snipeit_config_db_ssl_ca_path }} +DB_SSL_CIPHER={{ snipeit_config_db_ssl_cipher }} +DB_SSL_VERIFY_SERVER={{ snipeit_config_db_ssl_verify_server }} + +# -------------------------------------------- +# REQUIRED: OUTGOING MAIL SERVER SETTINGS +# -------------------------------------------- +MAIL_DRIVER={{ snipeit_config_mail_driver }} +MAIL_HOST={{ snipeit_config_mail_host }} +MAIL_PORT={{ snipeit_config_mail_port }} +MAIL_USERNAME={{ snipeit_config_mail_username }} +MAIL_PASSWORD={{ snipeit_config_mail_password }} +MAIL_ENCRYPTION={{ snipeit_config_mail_encryption }} +MAIL_FROM_ADDR={{ snipeit_config_mail_from_addr }} +MAIL_FROM_NAME='{{ snipeit_config_mail_from_name }}' +MAIL_REPLYTO_ADDR={{ snipeit_config_mail_replyto_addr }} +MAIL_REPLYTO_NAME='{{ snipeit_config_mail_replyto_name }}' +MAIL_AUTO_EMBED_METHOD='{{ snipeit_config_mail_auto_embed_method }}' + +# -------------------------------------------- +# REQUIRED: IMAGE LIBRARY +# This should be gd or imagick +# -------------------------------------------- +IMAGE_LIB={{ snipeit_config_image_lib }} + +# -------------------------------------------- +# OPTIONAL: BACKUP SETTINGS +# -------------------------------------------- +MAIL_BACKUP_NOTIFICATION_DRIVER=null +MAIL_BACKUP_NOTIFICATION_ADDRESS=null +BACKUP_ENV=true +ALLOW_BACKUP_DELETE=false +ALLOW_DATA_PURGE=false + +# -------------------------------------------- +# OPTIONAL: SESSION SETTINGS +# -------------------------------------------- +SESSION_DRIVER=file +SESSION_LIFETIME=12000 +EXPIRE_ON_CLOSE=false +ENCRYPT=false +COOKIE_NAME=snipeit_session +COOKIE_DOMAIN=null +SECURE_COOKIES=false +API_TOKEN_EXPIRATION_YEARS=15 +BS_TABLE_STORAGE=cookieStorage +BS_TABLE_DEEPLINK=true + +# -------------------------------------------- +# OPTIONAL: SECURITY HEADER SETTINGS +# -------------------------------------------- +APP_TRUSTED_PROXIES=192.168.1.1,10.0.0.1 +ALLOW_IFRAMING=false +REFERRER_POLICY=same-origin +ENABLE_CSP=false +CORS_ALLOWED_ORIGINS=null +ENABLE_HSTS=false + +# -------------------------------------------- +# OPTIONAL: CACHE SETTINGS +# -------------------------------------------- +CACHE_DRIVER=file +QUEUE_DRIVER=sync +CACHE_PREFIX=snipeit + +# -------------------------------------------- +# OPTIONAL: REDIS SETTINGS +# -------------------------------------------- +REDIS_HOST=null +REDIS_PASSWORD=null +REDIS_PORT=null + +# -------------------------------------------- +# OPTIONAL: MEMCACHED SETTINGS +# -------------------------------------------- +MEMCACHED_HOST=null +MEMCACHED_PORT=null + +# -------------------------------------------- +# OPTIONAL: PUBLIC S3 Settings +# -------------------------------------------- +PUBLIC_AWS_SECRET_ACCESS_KEY=null +PUBLIC_AWS_ACCESS_KEY_ID=null +PUBLIC_AWS_DEFAULT_REGION=null +PUBLIC_AWS_BUCKET=null +PUBLIC_AWS_URL=null +PUBLIC_AWS_BUCKET_ROOT=null + +# -------------------------------------------- +# OPTIONAL: PRIVATE S3 Settings +# -------------------------------------------- +PRIVATE_AWS_ACCESS_KEY_ID=null +PRIVATE_AWS_SECRET_ACCESS_KEY=null +PRIVATE_AWS_DEFAULT_REGION=null +PRIVATE_AWS_BUCKET=null +PRIVATE_AWS_URL=null +PRIVATE_AWS_BUCKET_ROOT=null + +# -------------------------------------------- +# OPTIONAL: AWS Settings +# -------------------------------------------- +AWS_ACCESS_KEY_ID=null +AWS_SECRET_ACCESS_KEY=null +AWS_DEFAULT_REGION=null + +# -------------------------------------------- +# OPTIONAL: LOGIN THROTTLING +# -------------------------------------------- +LOGIN_MAX_ATTEMPTS=5 +LOGIN_LOCKOUT_DURATION=60 +LOGIN_AUTOCOMPLETE=false + +# -------------------------------------------- +# OPTIONAL: FORGOTTEN PASSWORD SETTINGS +# -------------------------------------------- +RESET_PASSWORD_LINK_EXPIRES=15 +PASSWORD_CONFIRM_TIMEOUT=10800 +PASSWORD_RESET_MAX_ATTEMPTS_PER_MIN=50 + +# -------------------------------------------- +# OPTIONAL: MISC +# -------------------------------------------- +LOG_CHANNEL=single +LOG_MAX_DAYS=10 +APP_LOCKED=false +APP_CIPHER=AES-256-CBC +APP_FORCE_TLS=false +APP_ALLOW_INSECURE_HOSTS=false +GOOGLE_MAPS_API= +LDAP_MEM_LIM=500M +LDAP_TIME_LIM=600 +IMPORT_TIME_LIMIT=600 +IMPORT_MEMORY_LIMIT=500M +REPORT_TIME_LIMIT=12000 +REQUIRE_SAML=false +API_THROTTLE_PER_MINUTE=120 +CSV_ESCAPE_FORMULAS=true + +# -------------------------------------------- +# OPTIONAL: HASHING +# -------------------------------------------- +HASHING_DRIVER='bcrypt' +BCRYPT_ROUNDS=10 +ARGON_MEMORY=1024 +ARGON_THREADS=2 +ARGON_TIME=2 + +# -------------------------------------------- +# OPTIONAL: SCIM +# -------------------------------------------- +SCIM_TRACE=false diff --git a/roles/snipeit/templates/snipeit.conf.j2 b/roles/snipeit/templates/snipeit.conf.j2 new file mode 100644 index 0000000..be326de --- /dev/null +++ b/roles/snipeit/templates/snipeit.conf.j2 @@ -0,0 +1,31 @@ +server { + listen 80; + server_name {{ snipeit_nginx_servername }}; + + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl; + server_name {{ snipeit_nginx_servername }}; + + ssl_certificate {{ snipeit_ssl_crt }}; + ssl_certificate_key {{ snipeit_ssl_key }}; + ssl_session_timeout 5m; + ssl_session_cache builtin:1000 shared:SSL:10m; + + root {{ snipeit_install_path }}/public/; + index index.php index.html index.htm; + + location / { + try_files $uri $uri/ /index.php$is_args$args; + } + + location ~ \.php$ { + try_files $uri $uri/ =404; + fastcgi_pass unix:/var/run/php-fpm/www.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } +} diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..728b802 --- /dev/null +++ b/site.yml @@ -0,0 +1,4 @@ +--- +## This playbook deploys the whole application stack in this site. + +- import_playbook: snipeit.yml diff --git a/snipeit.yml b/snipeit.yml new file mode 100644 index 0000000..2ff8e3d --- /dev/null +++ b/snipeit.yml @@ -0,0 +1,7 @@ +--- +# file: snipeit.yml + +- hosts: snipeit + become: true + roles: + - snipeit