commit ba33c5b5d309166468707f3b8b6025beb92632ae Author: lhahn Date: Sun Aug 20 11:13:16 2023 +0200 Git initial commit diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..f0f6389 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# cloud-environments-example + +This repository contains an example environment structure to demonstrate, how the OpenDevChain Cloud repositories can be used. +For each relevant application there is one playbook provided; with + +$ ansible-playbook .yml + +you should be able to execute and example playbook. + +Please make shure, that at least URLs/DNS entries are correct, or remove the "letsencrypt: true" attribute from website configurations; otherwise runs will fail as letsencrypt is probably not correctly configured to resolve DNS to your server. + +The initial roles are kept in this repository; for newer versions of Ansible Roles, please have a look at: + +https://git.opendevchain.de/OpenDevChain + + +For any questions, please do not hesitate to reach out to me: + +info@opendevchain.de + +Regards diff --git a/cloud-authentik.yml b/cloud-authentik.yml new file mode 100755 index 0000000..1b1c99d --- /dev/null +++ b/cloud-authentik.yml @@ -0,0 +1,94 @@ +- hosts: local + connection: local + roles: + - basis + - backup + - docker + - nginx + - authentik + - checkmk + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + authentik_db: + type: pgsql + name: authentik + user: authentikuser + pass: FancyGiteaDbPasswordVeryLong + authentik_secret_key: LookUpDocumentationForSettingUpKey + authentik_admin_pass: FancyInitialAdminPasswordVeryLong! + + authentik_stmp_host: "{{ mail_domain }}" + authentik_stmp_port: "{{ mail_port }}" + authentik_smtp_from: "{{ noreply_mail_address }}" + authentik_smtp_user: "{{ auth_mail_address }}" + authentik_smtp_pass: "{{ auth_mail_pass }}" + authentik_smpt_usessl: "true" + + + authentik_https_port: 9443 + + authentik_website: + domain: "idp.{{ domain_external }}" + letsencrypt: true + state: present + owner: root + port: 80 + root_setup: false + index: noindex + root: noroot + options: + access_log: "/var/log/nginx/idp.{{ domain_external }}-access.log" + error_log: "/var/log/nginx/idp.{{ domain_external }}-error.log" + pre_options: | + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + locations: + - location: "/" + options: | + proxy_pass https://localhost:{{ authentik_https_port }}; + proxy_http_version 1.1; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # This needs to be set inside the location block, very important. + proxy_set_header Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + authentik_ldaps: + domain: "idp.{{ domain_external }}" + filetag: "ldaps.idp.{{ domain_external }}" + letsencrypt: true + state: present + stream: true + owner: root + port: 3636 + root_setup: false + index: noindex + root: noroot + options: + proxy_pass: localhost:389 + + # Websites + web_sites: + - "{{ authentik_website }}" + - "{{ authentik_ldaps }}" + + + # Backup + backup_targets: + file: + - "{{ gitea_data_location }}" + - "/etc/letsencrypt" + + + + vars_files: + - "group_vars/environment.yml" diff --git a/cloud-backup.yml b/cloud-backup.yml new file mode 100755 index 0000000..09a0fa4 --- /dev/null +++ b/cloud-backup.yml @@ -0,0 +1,27 @@ +- hosts: local + connection: local + roles: + - basis + - backup + - checkmk + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + + mount_points: + - path: /backup + dev: /dev/sdb + + + # backup + backup_client: false + backup_hosts: + - name: cloud-example-server + domain: "example-server.{{ domain_external }}" + + vars_files: + - "group_vars/environment.yml" diff --git a/cloud-gitea.yml b/cloud-gitea.yml new file mode 100755 index 0000000..348037e --- /dev/null +++ b/cloud-gitea.yml @@ -0,0 +1,79 @@ +- hosts: local + connection: local + roles: + - basis + - backup + - postgres + - nginx + - gitea + - checkmk + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + mount_points: + - path: "{{ gitea_data_location }}" + dev: /dev/sdb + + gitea_data_location: "{{ cloud_storage }}/gitea-data" + gitea_db: + type: pgsql + name: gitea + user: giteauser + pass: FancyGiteaDbPasswordVeryLong + gitea_ssh_domain: "git.{{ domain_external }}" + + gitea_internal_token: LookUpDocumentationForSettingUpToken + gitea_jwt_secret: LookUpDocumentationForSettingUpSecret + gitea_secret_key: LookUpDocumentationForSettingUpKey + + gitea_mail_domain: "{{ mail_domain }}" + gitea_mail_from: "{{ noreply_mail_address }}" + gitea_mail_user: "{{ development_mail_address }}" + gitea_mail_pass: "{{ development_mail_pass }}" + + gitea_admin_user: "admin" + gitea_admin_pass: InitialGiteaPasswordPleaseChangeLater + gitea_admin_mail: "{{ gitea_mail_from }}" + + gitea_website: + domain: "{{ gitea_ssh_domain }}" + letsencrypt: true + state: present + owner: gitea + port: 80 + root_setup: false + index: noindex + root: noroot + options: + access_log: "/var/log/nginx/git.{{ domain_external }}-access.log" + error_log: "/var/log/nginx/git.{{ domain_external }}-error.log" + locations: + - location: '/_/static/assets' + options: "alias /path/to/gitea/public;" + - location: '/' + options: "proxy_pass http://localhost:3000;" + + # Websites + web_sites: "{{ gitea_website }}" + + # Databases + db_configs: + - "{{ gitea_db }}" + + # Backup + backup_dbs: + - "{{ db_configs | json_query('[*].{type: type, name: name}') }}" + backup_targets: + db: "{{ backup_dbs | flatten }}" + file: + - "{{ gitea_data_location }}" + - "/etc/letsencrypt" + + vars_files: + - "group_vars/environment.yml" diff --git a/cloud-gocd-agent.yml b/cloud-gocd-agent.yml new file mode 100755 index 0000000..8a1664d --- /dev/null +++ b/cloud-gocd-agent.yml @@ -0,0 +1,20 @@ +- hosts: local + connection: local + roles: + - basis + - java + - gocd-agent + - checkmk + + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + + gocd_server: "https://build.{{ domain_external }}/go" + + vars_files: + - "group_vars/{{ ansible_local.preferences.ansible.environment }}.yml" diff --git a/cloud-gocd.yml b/cloud-gocd.yml new file mode 100755 index 0000000..1b2d190 --- /dev/null +++ b/cloud-gocd.yml @@ -0,0 +1,69 @@ +- hosts: local + connection: local + roles: + - basis + - backup + - java + - nginx + - gocd + - checkmk + + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + mount_points: + - path: "{{ gocd_artifact_location }}" + dev: /dev/sdb + + gocd_admin_user: gocd_admin + gocd_admin_pass: VeryCoolAdminPassword! + gocd_artifact_location: "{{ cloud_storage }}/gocd-artifacts" + gocd_website: + domain: "build.{{ domain_external }}" + letsencrypt: true + state: present + owner: jenkins + port: 80 + root_setup: false + index: noindex + root: noroot + options: + access_log: "/var/log/nginx/build.{{ domain_external }}-access.log" + error_log: "/var/log/nginx/build.{{ domain_external }}-error.log" + pre_options: | + # Required for GoCD websocket agents + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + locations: + - location: '/' + options: | + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_pass http://localhost:8153/; + client_max_body_size 10000m; + + # Websites + web_sites: + - "{{ gocd_website }}" + + # Backup + backup_targets: + file: + - "{{ gocd_artifact_location }}" + - "/etc/letsencrypt" + + vars_files: + - "group_vars/{{ ansible_local.preferences.ansible.environment }}.yml" diff --git a/cloud-jenkins.yml b/cloud-jenkins.yml new file mode 100755 index 0000000..4ff2668 --- /dev/null +++ b/cloud-jenkins.yml @@ -0,0 +1,100 @@ +- hosts: local + connection: local + roles: + - basis + - backup + - java + - nginx + - jenkins + - checkmk + + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + mount_points: + - path: "{{ jenkins_data_location }}" + dev: /dev/sdb + + jenkins_data_location: "{{ cloud_storage }}/jenkins-data" + jenkins_website: + domain: "build.{{ domain_external }}" + letsencrypt: true + state: present + owner: jenkins + port: 80 + root_setup: false + index: noindex + root: noroot + options: + access_log: "/var/log/nginx/build.{{ domain_external }}-access.log" + error_log: "/var/log/nginx/build.{{ domain_external }}-error.log" + ignore_invalid_headers: "off" + pre_options: | + upstream jenkins { + keepalive 32; # keepalive connections + server 127.0.0.1:8080; # jenkins ip and port + } + # Required for Jenkins websocket agents + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + locations: + - location: '~ "^/static/[0-9a-fA-F]{8}\/(.*)$"' + options: | + rewrite "^/static/[0-9a-fA-F]{8}\/(.*)" /$1 last; + - location: '/userContent' + options: | + root {{ cloud_apps }}/jenkins/web; + if (!-f $request_filename){ + # this file does not exist, might be a directory or a /**view** url + rewrite (.*) /$1 last; + break; + } + sendfile on; + - location: '/' + options: | + sendfile off; + proxy_pass http://jenkins; + proxy_redirect default; + proxy_http_version 1.1; + + # Required for Jenkins websocket agents + proxy_set_header Connection $connection_upgrade; + proxy_set_header Upgrade $http_upgrade; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_max_temp_file_size 0; + + #this is the maximum upload size + client_max_body_size 10m; + client_body_buffer_size 128k; + + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_buffering off; + proxy_request_buffering off; # Required for HTTP CLI commands + proxy_set_header Connection ""; # Clear for keepalive + + # Websites + web_sites: + - "{{ jenkins_website }}" + + # Backup + backup_targets: + file: + - "{{ jenkins_data_location }}" + - "/etc/letsencrypt" + + vars_files: + - "group_vars/environment.yml" diff --git a/cloud-mail.yml b/cloud-mail.yml new file mode 100755 index 0000000..e17d207 --- /dev/null +++ b/cloud-mail.yml @@ -0,0 +1,36 @@ +--- +- hosts: local + connection: local + roles: + - basis + - backup + - docker + - mailcow + - checkmk + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + + # Mailcow + mailcow_db_root_pass: FancyInitialRootPasswordVeryLong + mailcow_db_pass: FancyInitialDBPasswordVeryLong + mailcow_use_sogo: true + mailcow_use_solr: true + mailcow_use_clamav: true + + + # Backup + backup_client_script_envs: + - var: "MAILCOW_BACKUP_LOCATION" + value: "/tmp" + backup_client_scripts: + - /app/mailcow/helper-scripts/backup_and_restore.sh backup all + - "mv /tmp/mailcow-* {{ backup_folder }}/mail/" + + + vars_files: + - "group_vars/environment.yml" diff --git a/cloud-monitoring.yml b/cloud-monitoring.yml new file mode 100755 index 0000000..7089711 --- /dev/null +++ b/cloud-monitoring.yml @@ -0,0 +1,55 @@ +- hosts: local + connection: local + roles: + - basis + - nginx + - checkmk + + vars: + users_local: [] + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + checkmk_is_server: true + checkmk_admin_pass: FancyInitialAdminPasswordVeryLong! + + checkmk_website: + domain: "monitoring.{{ domain_external }}" + letsencrypt: true + filetag: "monitoring.{{ domain_external }}" + state: present + port: 80 + root: noroot + root_setup: false + index: noindex + options: + access_log: "/var/log/nginx/monitoring.{{ domain_external }}-access.log" + error_log: "/var/log/nginx/monitoring.{{ domain_external }}-error.log" + locations: + - location: '~* /\w+' + options: | + proxy_pass http://127.0.0.1:5000; + proxy_pass_header Authorization; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_buffering off; + proxy_request_buffering off; + client_max_body_size 0; + proxy_read_timeout 36000s; + proxy_ssl_session_reuse off; + proxy_hide_header x-frame-options; + - location: '= /' + options: | + return 444; + + web_sites: + - "{{ checkmk_website }}" + + vars_files: + - "group_vars/{{ ansible_local.preferences.ansible.environment }}.yml" diff --git a/cloud-nextcloud.yml b/cloud-nextcloud.yml new file mode 100755 index 0000000..7e3adaf --- /dev/null +++ b/cloud-nextcloud.yml @@ -0,0 +1,227 @@ +--- +- hosts: local + connection: local + roles: + - basis + - backup + - docker + - postgres + - php + - signaling + - nginx + - nextcloud + - checkmk + + vars: + users_local: [] + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + mount_points: + - path: "{{ ncloud_data_location }}" + dev: /dev/sdb + + # NextCloud + ncloud_db: + type: pgsql + name: nextcloud + user: nextclouduser + pass: FancyNextcloudDbPasswordVeryLong + ncloud_admin_user: nextcloudadmin + ncloud_admin_pass: FancyInitialAdminPasswordVeryLong! + ncloud_data_location: "{{ cloud_storage }}/nextcloud-data" + ncloud_npush_port: 7867 + + ncloud_website: + domain: "{{ ncloud_domain }}" + letsencrypt: true + filetag: "cloud.{{ domain_external }}" + state: present + owner: ncloud + port: 80 + port_options: " ipv6only=on" + root: "{{ cloud_apps }}/nextcloud/" + root_setup: false + index: + - index.php + - index.html + - /index.php$request_uri + options: + access_log: "/var/log/nginx/cloud.{{ domain_external }}-access.log" + error_log: "/var/log/nginx/cloud.{{ domain_external }}-error.log" + client_max_body_size: 512M + client_body_timeout: 300s + fastcgi_buffers: 64 4K + gzip: !unsafe on + gzip_vary: !unsafe on + gzip_min_length: 256 + gzip_proxied: expired no-cache no-store private no_last_modified no_etag auth + gzip_types: pplication/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy + fastcgi_hide_header: X-Powered-By + add_header: + - Referrer-Policy "no-referrer" always + - X-Content-Type-Options "nosniff" always + - X-Download-Options "noopen" always + - X-Frame-Options "SAMEORIGIN" always + - X-Permitted-Cross-Domain-Policies "none" always + - X-Robots-Tag "none" always + - X-XSS-Protection "1; mode=block" always + - Strict-Transport-Security "max-age=15552000; includeSubDomains" always + - X-Content-Type-Options "nosniff" + - X-XSS-Protection "1; mode=block" + - X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" + - X-Frame-Options "SAMEORIGIN" + - Referrer-Policy "no-referrer" + locations: + - location: '= /' + options: | + if ( $http_user_agent ~ ^DavClnt ) { + return 302 /remote.php/webdav/$is_args$args; + } + - location: '= /robots.txt' + options: | + allow all; + log_not_found off; + access_log off; + - location: '^~ /.well-known' + options: | + location = /.well-known/carddav { return 301 /remote.php/dav/; } + location = /.well-known/caldav { return 301 /remote.php/dav/; } + + location /.well-known/acme-challenge { try_files $uri $uri/ =404; } + location /.well-known/pki-validation { try_files $uri $uri/ =404; } + + return 301 /index.php$request_uri; + - location: '~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)' + options: return 404; + - location: '~ ^/(?:\.|autotest|occ|issue|indie|db_|console)' + options: return 404; + - location: '~ \.php(?:$|/)' + options: | + rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri; + + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + set $path_info $fastcgi_path_info; + + try_files $fastcgi_script_name =404; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $path_info; + fastcgi_param HTTPS on; + + fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice + fastcgi_param front_controller_active true; # Enable pretty urls + fastcgi_pass php; + + fastcgi_intercept_errors on; + fastcgi_request_buffering off; + + fastcgi_max_temp_file_size 0; + - location: '~ \.(?:css|js|svg|gif|png|jpg|ico|wasm|tflite)$' + options: | + try_files $uri /index.php$request_uri; + expires 6M; # Cache-Control policy borrowed from `.htaccess` + access_log off; # Optional: Don't log access to assets + + location ~ \.wasm$ { + default_type application/wasm; + } + - location: '~ \.woff2?$' + options: | + try_files $uri /index.php$request_uri; + expires 7d; # Cache-Control policy borrowed from `.htaccess` + access_log off; # Optional: Don't log access to assets + - location: '/remote' + options: return 301 /remote.php$request_uri; + - location: '^~ /push/' + options: | + proxy_pass http://127.0.0.1:{{ ncloud_npush_port }}/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + - location: '/' + options: try_files $uri $uri/ /index.php$request_uri; + + # Signaling Nextcloud + signaling_listen_host: 127.0.0.1 + signaling_listen_port: 8080 + + signaling_janus_api_key: LookUpDocumentationForSettingUpKey + signaling_hash_key: LookUpDocumentationForSettingUpHashKey + signaling_block_key: LookUpDocumentationForSettingUpBlockKey + signaling_ncloud_secret_key: LookUpDocumentationForSettingUpSecretKey + signaling_backend_name: examplecloud + + signaling_website: + domain: "signaling.{{ domain_external }}" + letsencrypt: true + filetag: "signaling.{{ domain_external }}" + state: present + owner: signaling + port: 80 + root: noroot + root_setup: false + index: noindex + pre_options: | + upstream signaling { + server {{ signaling_listen_host }}:{{ signaling_listen_port }}; + } + options: + access_log: "/var/log/nginx/signaling.{{ domain_external }}-access.log" + error_log: "/var/log/nginx/signaling.{{ domain_external }}-error.log" + locations: + - location: '/standalone-signaling/' + options: | + proxy_pass http://signaling/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + - location: '/standalone-signaling/spreed' + options: | + proxy_pass http://signaling/spreed; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + + # Databases + db_configs: + - "{{ ncloud_db }}" + + # Websites + web_sites: + - "{{ ncloud_website }}" + - "{{ signaling_website }}" + + + # Nginx + nginx_conf_http_local: + - | + upstream php { + server unix:/run/php/php{{ php_version }}-fpm.sock; + } + nginx_conf_http: "{{ nginx_conf_http_local }}" + + + # Backup + backup_dbs: + - "{{ db_configs | json_query('[*].{type: type, name: name}') }}" + backup_targets: + db: "{{ backup_dbs | flatten }}" + file: + - "{{ ncloud_data_location }}" + - "/etc/letsencrypt" + + + vars_files: + - "group_vars/environment.yml" diff --git a/cloud-nexus.yml b/cloud-nexus.yml new file mode 100755 index 0000000..85cfcd9 --- /dev/null +++ b/cloud-nexus.yml @@ -0,0 +1,60 @@ +- hosts: local + connection: local + roles: + - basis + - backup + - java + - nginx + - nexus + - checkmk + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + mount_points: + - path: "{{ nexus_data_location }}" + dev: /dev/sdb + + nexus_java_home: "{{ cloud_apps }}/java_jdk/jdk8u322-b06" + + nexus_http_port: 8081 + nexus_http_host: localhost + nexus_data_location: "{{ cloud_storage }}/sonatype-nexus-data" + nexus_website: + domain: "artifact.{{ domain_external }}" + letsencrypt: true + state: present + owner: sonanexus + port: 80 + root_setup: false + index: noindex + root: noroot + options: + access_log: "/var/log/nginx/artifact.{{ domain_external }}-access.log" + error_log: "/var/log/nginx/artifact.{{ domain_external }}-error.log" + client_max_body_size: 1G + locations: + - location: "/" + options: | + proxy_pass http://{{ nexus_http_host }}:{{ nexus_http_port }}/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Websites + web_sites: + - "{{ nexus_website }}" + + # Backup + backup_targets: + file: + - "{{ nexus_data_location }}" + - "/etc/letsencrypt" + + vars_files: + - "group_vars/environment.yml" diff --git a/cloud-openproject b/cloud-openproject new file mode 100755 index 0000000..aaf7e2a --- /dev/null +++ b/cloud-openproject @@ -0,0 +1,77 @@ +- hosts: local + connection: local + roles: + - basis + - backup + - postgres + - nginx + - openproject + - checkmk + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + openproject_db: + type: pgsql + name: openproject + user: openprojectuser + pass: FancyOpenProjectDbPasswordVeryLong + openproject_mail_smtp_host: "{{ mail_domain }}" + openproject_mail_smtp_port: "{{ mail_port }}" + openproject_mail_smtp_user: "{{ development_mail_address }}" + openproject_mail_smtp_pass: "{{ development_mail_pass }}" + openproject_mail_smtp_domain: "{{ openproject_mail_smtp_host }}" + openproject_mail_admin: "{{ noreply_mail_address }}" + openproject_domain: "project.{{ domain_external }}" + + openproject_http_port: 6000 + openproject_webste: + domain: "project.{{ domain_external }}" + letsencrypt: true + state: present + owner: openproject + port: 80 + root_setup: false + index: noindex + root: noroot + options: + access_log: "/var/log/nginx/project.{{ domain_external }}-access.log" + error_log: "/var/log/nginx/project.{{ domain_external }}-error.log" + pre_options: | + upstream openproject { + server 127.0.0.1:{{ openproject_http_port }}; + } + locations: + - location: "/" + options: | + proxy_pass_header Server; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto https; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + proxy_pass http://openproject/; + + # Websites + web_sites: + - "{{ openproject_webste }}" + + # Databases + db_configs: + - "{{ openproject_db }}" + + # Backup + backup_dbs: + - "{{ db_configs | json_query('[*].{type: type, name: name}') }}" + backup_targets: + db: "{{ backup_dbs | flatten }}" + file: + - "/etc/letsencrypt" + + vars_files: + - "group_vars/environment.yml" diff --git a/cloud-vpn.yml b/cloud-vpn.yml new file mode 100755 index 0000000..45c28ba --- /dev/null +++ b/cloud-vpn.yml @@ -0,0 +1,49 @@ +--- +- hosts: local + connection: local + roles: + - basis + - backup + - easy-rsa + - vpn + - checkmk + + + vars: + users_local: [] + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + # easy-rsa + easy_rsa_servers: + - name: "{{ vpn_server }}" + state: present + easy_rsa_vars_conf: + country: "GERMANY" + province: "Niedersachsen" + city: "Wendeburg" + company: "Hühner Cloud" + mail: "{{ admin_mail_address }}" + easy_rsa_clients: "{{ vpn_users + vpn_apps }}" + + + # OpenVPN + vpn_server: "example-cloud-vpn" + vpn_users: "{{ users | json_query('[?contains(groups, `vpn`)].{name: name, state: state}') }}" + vpn_apps: + - name: admin.exampleapp + state: present + vpn_clients: "{{ vpn_users + vpn_apps }}" + + + # Backup + backup_targets: + file: + - "/etc/openvpn" + + + vars_files: + - "group_vars/environment.yml" diff --git a/cloud-wordpress b/cloud-wordpress new file mode 100755 index 0000000..c7c3023 --- /dev/null +++ b/cloud-wordpress @@ -0,0 +1,48 @@ +--- +- hosts: local + connection: local + roles: + - basis + - backup + - mariadb + - php + - nginx + - wordpress + - checkmk + + + vars: + users_local: [] #some local users only specific to this node! + users: "{{ users_local + users_admin }}" + + fail2ban_activate_modules: + - sshd + - nginx + + + # Websites + web_sites: "{{ wp_web_sites }}" + + # Databases + db_configs: "{{ web_sites | json_query('[*].db') }}" + + # Nginx + nginx_conf_http_local: + - | + upstream php { + server unix:/run/php/php{{ php_version }}-fpm.sock; + } + nginx_conf_http: "{{ nginx_conf_http_local }}" + + # Backup + backup_dbs: + - "{{ db_configs | json_query('[*].{type: type, name: name}') }}" + backup_targets: + db: "{{ backup_dbs | flatten }}" + file: + - "/app/wordpress" + - "/etc/letsencrypt" + + vars_files: + - "group_vars/environments.yml" + - "group_vars/wordpress.yml" \ No newline at end of file diff --git a/group_vars/environment.yml b/group_vars/environment.yml new file mode 100755 index 0000000..c09c2f6 --- /dev/null +++ b/group_vars/environment.yml @@ -0,0 +1,86 @@ +--- +ansible_python_interpreter: /usr/bin/python3 +cloud_name: "example-cloud" +cloud_home: "/{{ cloud_name }}" +cloud_host_group: "example-playbook" +cloud_env: "example_env" +cloud_env_path: "{{ cloud_home }}/{{ cloud_env }}" +cloud_control_name: "cloud-control" +cloud_apps: "/app" +cloud_storage: "/storage" +cloud_stage: "production" + +backup_folder: "/backup" +backup_times_hour: + - 20 + +basis_apps: + - passwd + - python3-jmespath + - python3-psycopg2 + - vim + +domain_external: "my-domain.tld" +cloud_python_envs: "{{ cloud_apps }}/py-env" + +admin_mail_address: "admin@{{ domain_external }}" +noreply_mail_address: "noreply@{{ domain_external }}" +letsencrypt_mail_address: "{{ admin_mail_address }}" + +development_mail_address: "development@{{ domain_external }}" +development_mail_pass: MyVeryCoolPassword! + +auth_mail_address: "auth@{{ domain_external }}" +auth_mail_pass: MyVeryCoolPassword! + +mail_domain: "mail.{{ domain_external }}" +mail_port: 465 +mail_ssl: true + +shared_group: "cloud" + +backup_owner_ssh_key: "ssh-rsa SomeFancyRSAKeyThatIsPreDefined" + +users_admin: + - name: admin + displayname: Admin User + shell: /bin/bash + groups: + - admin + - adm + - systemd-journal + - staff + - sudo + - vpn + state: present + ssh_key: "ssh-rsa AdminUserSshKeyThatIsPreDefined" + +ssh_port: 22 +default_groups: + - ssh + - users + - systemd-journal + - "{{ shared_group }}" + +ssh_configs: + - Protocol 2 + - "Port {{ ssh_port }}" + - PermitRootLogin prohibit-password + - PubkeyAuthentication yes + - PasswordAuthentication no + - PermitEmptyPasswords no + - ClientAliveInterval 1200 + - ClientAliveCountMax 3 + +fail2ban_bantime: 1h +fail2ban_maxretry: 3 +fail2ban_nginx_filter: + - nginx-noscript + - nginx-nohome + - nginx-noproxy + +php_version: 8.0 +php_upload_max_filesize: 512M +php_post_max_size: 512M +php_memory_limit: 512M + diff --git a/group_vars/wordpress b/group_vars/wordpress new file mode 100755 index 0000000..f061831 --- /dev/null +++ b/group_vars/wordpress @@ -0,0 +1,44 @@ +--- +wp_default_locations: + - location: "/favicon.ico" + options: | + log_not_found off; + access_log off; + - location: "/robots.txt" + options: | + allow all; + log_not_found off; + access_log off; + - location: '~ \.php$' + options: | + include fastcgi.conf; + fastcgi_intercept_errors on; + fastcgi_pass php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + - location: '~* \.(js|css|png|jpg|jpeg|gif|ico)$' + options: | + expires max; + log_not_found off; + - location: "/" + options: | + try_files $uri $uri/ /index.php?$args; + +wp_web_sites: + - domain: "{{ domain_external }}" + state: present + owner: admin + root: "{{ cloud_apps }}/wordpress/{{ domain_external }}" + root_setup: false + wordpress: true + index: + - index.php + db: + type: mariadb + name: wordpress + user: wordpressuser + pass: FancyGiteaDbPasswordVeryLong + letsencrypt: true + locations: "{{ wp_default_locations }}" + options: + access_log: "/var/log/nginx/{{ domain_external }}-access.log" + error_log: "/var/log/nginx/{{ domain_external }}-error.log" diff --git a/requirements.yml b/requirements.yml new file mode 100755 index 0000000..daee6df --- /dev/null +++ b/requirements.yml @@ -0,0 +1,69 @@ +--- +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-authentik.git + name: authentik + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-backup.git + name: backup + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-basis.git + name: basis + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-checkmk.git + name: checkmk + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-docker.git + name: docker + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-easyrsa.git + name: easy-rsa + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-gitea.git + name: gitea + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-gocd.git + name: gocd + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-gocd-agent.git + name: gocd-agent + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-java.git + name: java + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-jenkins.git + name: jenkins + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-mailcow.git + name: mailcow + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-mariadb.git + name: mariadb + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-nextcloud.git + name: nextcloud + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-nexus.git + name: nexus + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-nginx.git + name: nginx + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-openproject.git + name: openproject + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-php.git + name: php + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-postgresql.git + name: postgres + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-nextcloud-signaling.git + name: signaling + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-vault.git + name: vault + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-vpn.git + name: vpn + +- src: git+https://git.opendevchain.de/OpenDevChain/cloud-wordpress.git + name: wordpress diff --git a/roles/authentik/LICENSE b/roles/authentik/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/authentik/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/authentik/README.md b/roles/authentik/README.md new file mode 100755 index 0000000..e7997cd --- /dev/null +++ b/roles/authentik/README.md @@ -0,0 +1,3 @@ +# cloud-authentik + +Ansible Role in order to setup go-authentik as a personal identity provider. \ No newline at end of file diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml new file mode 100755 index 0000000..368599f --- /dev/null +++ b/roles/authentik/defaults/main.yml @@ -0,0 +1,41 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false +domain_external: "my-domain.tld" + + +authentik_dir: "{{ cloud_apps }}/authentik" + +authentik_version: 2022.5 +authentik_source: https://goauthentik.io/version +authentik_target: docker-compose.yml + +authentik_db: + type: pgsql + name: authentik_db + user: authentik_dbu + pass: authentik_dbpw + port: 5432 + host: localhost + +authentik_http_port: 9080 +authentik_https_port: 9443 + +authentik_secret_key: MyVerySecretKey! +authentik_loglevel: info + +authentik_admin_pass: VeryNicePassword! +authentik_admin_token: + +authentik_stmp_host: "mail.{{ domain_external }}" +authentik_stmp_port: 25 +authentik_smtp_user: "admin@{{ domain_external }}" +authentik_smtp_pass: VeryNicePassword! +authentik_smtp_from: "{{ authentik_smtp_user }}" + + +authentik_footer: +# - name: Link Name +# href: https://goauthentik.io \ No newline at end of file diff --git a/roles/authentik/handlers/main.yml b/roles/authentik/handlers/main.yml new file mode 100755 index 0000000..07f56e3 --- /dev/null +++ b/roles/authentik/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: restart authentik + docker_compose: + project_src: "{{ authentik_link }}" + state: present + restarted: yes + remove_orphans: yes diff --git a/roles/authentik/meta/.galaxy_install_info b/roles/authentik/meta/.galaxy_install_info new file mode 100755 index 0000000..cb911b2 --- /dev/null +++ b/roles/authentik/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:08 ' +version: '' diff --git a/roles/authentik/meta/main.yml b/roles/authentik/meta/main.yml new file mode 100755 index 0000000..64282b5 --- /dev/null +++ b/roles/authentik/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: authentik + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup authentik Identity provider + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - authentik +dependencies: + - docker diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml new file mode 100755 index 0000000..27838ac --- /dev/null +++ b/roles/authentik/tasks/main.yml @@ -0,0 +1,32 @@ +--- +- name: setup authentik docker dir + file: + state: directory + path: "{{ item }}" + loop: + - "{{ authentik_dir }}" + - "{{ authentik_inst }}" + +- name: link installation folder + file: + state: link + src: "{{ authentik_inst }}" + dest: "{{ authentik_link }}" + +- name: download authentik compose file + get_url: + url: "{{ authentik_source }}/{{ authentik_version }}/{{ authentik_target }}" + dest: "{{ authentik_link }}/{{ authentik_target }}" + force: yes + owner: root + group: root + notify: restart authentik + +- name: template environment + template: + src: opt/authentik/.env.j2 + dest: "{{ authentik_link }}/.env" + mode: 0640 + owner: root + group: root + notify: restart authentik diff --git a/roles/authentik/templates/opt/authentik/.env.j2 b/roles/authentik/templates/opt/authentik/.env.j2 new file mode 100755 index 0000000..7ecffee --- /dev/null +++ b/roles/authentik/templates/opt/authentik/.env.j2 @@ -0,0 +1,67 @@ +### BASIC ### +AUTHENTIK_PORT_HTTP={{ authentik_http_port | default(9080) }} +AUTHENTIK_PORT_HTTPS={{ authentik_https_port | default(9443) }} + +AUTHENTIK_SECRET_KEY={{ authentik_secret_key }} +AUTHENTIK_LOG_LEVEL={{ authentik_loglevel }} +AUTHENTIK_OUTPOSTS__DISCOVER={{ authentik_autodiscover | default('true') }} + +AUTHENTIK_DEFAULT_USER_CHANGE_NAME={{ authentik_user_change_name | default('true') }} +AUTHENTIK_DEFAULT_USER_CHANGE_MAIL={{ authentik_user_change_mail | default('true') }} +AUTHENTIK_DEFAULT_USER_CHANGE_USERNAME={{ authentik_user_change_username | default('true') }} + +AUTHENTIK_GDPR_COMPLIANCE={{ authentik_gdpr_removeondelete | default('true') }} +AUTHENTIK_DISABLE_UPDATE_CHECK={{ authentik_check_update | default('false') }} +AUTHENTIK_ERROR_REPORTING__ENABLED={{ authentik_reporting_error | default('false') }} +AUTHENTIK_ERROR_REPORTING__SEND_PII={{ authentik_reporting_user | default('false') }} +AUTHENTIK_DISABLE_STARTUP_ANALYTICS={{ authentik_disable_analytics | default('true') }} +AUTHENTIK_AVATARS={{ authentik_avatars | default('none') }} + +{% if authentik_footer is defined and authentik_footer is not none %} +AUTHENTIK_FOOTER_LINKS='{{ authentik_footer | to_json }}' +{% endif %} + +{% if authentik_admin_pass is defined and authentik_admin_pass is not none %} +AK_ADMIN_PASS={{ authentik_admin_pass }} +{% endif %} +{% if authentik_admin_token and authentik_admin_token is not none %} +AK_ADMIN_TOKEN={{ authentik_admin_token }} +{% endif %} + + + + +### SMTP ### +# SMTP Host Emails are sent to +AUTHENTIK_EMAIL__HOST={{ authentik_stmp_host }} +AUTHENTIK_EMAIL__PORT={{ authentik_stmp_port }} + +# Optionally authentikate (don't add quotation marks to you password) +AUTHENTIK_EMAIL__USERNAME={{ authentik_smtp_user }} +AUTHENTIK_EMAIL__PASSWORD={{ authentik_smtp_pass }} +# Email address authentik will send from, should have a correct @domain +AUTHENTIK_EMAIL__FROM={{ authentik_smtp_from }} + +AUTHENTIK_EMAIL__TIMEOUT={{ authentik_smpt_timeout | default('10') }} +# Use StartTLS +AUTHENTIK_EMAIL__USE_TLS={{ authentik_smpt_usetls | default('false') }} +# Use SSL +AUTHENTIK_EMAIL__USE_SSL={{ authentik_smpt_usessl | default('false') }} + + + + +{% if authentik_db is not none %} +### POSTGRES ### +PG_PASS={{ authentik_db.pass }} +PG_USER={{ authentik_db.user }} +PG_DB={{ authentik_db.name }} +POSTGRES_PASSWORD={{ authentik_db.pass }} +POSTGRES_USER={{ authentik_db.user }} +POSTGRES_DB={{ authentik_db.name }} +AUTHENTIK_POSTGRESQL__HOST={{ authentik_db.host | default('localhost') }} +AUTHENTIK_POSTGRESQL__PORT={{ authentik_db.post | default(5432) }} +AUTHENTIK_POSTGRESQL__NAME={{ authentik_db.name }} +AUTHENTIK_POSTGRESQL__USER={{ authentik_db.user }} +AUTHENTIK_POSTGRESQL__PASSWORD={{ authentik_db.pass }} +{% endif %} \ No newline at end of file diff --git a/roles/authentik/vars/main.yml b/roles/authentik/vars/main.yml new file mode 100755 index 0000000..deeab41 --- /dev/null +++ b/roles/authentik/vars/main.yml @@ -0,0 +1,3 @@ +--- +authentik_inst: "{{ authentik_dir }}/authentik_{{ authentik_version }}" +authentik_link: "{{ authentik_dir }}/inst" diff --git a/roles/backup/LICENSE b/roles/backup/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/backup/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/backup/README.md b/roles/backup/README.md new file mode 100755 index 0000000..e628543 --- /dev/null +++ b/roles/backup/README.md @@ -0,0 +1,3 @@ +# cloud-backup + +Ansible role repository in order to setup backup clientside tasks (what should be backuped) and masterside tasks (where should be backuped from where). \ No newline at end of file diff --git a/roles/backup/defaults/main.yml b/roles/backup/defaults/main.yml new file mode 100755 index 0000000..9f52e6e --- /dev/null +++ b/roles/backup/defaults/main.yml @@ -0,0 +1,69 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false +ssh_port: 1802 + +# clients push only daily backup +# server (!= client) will then pull daily backups +backup_client: true + +# Server should pull 1 hour later the backups. +backup_hour_shift: 1 + +backup_times_weekday: "*" +backup_times_hour: + - 6 + - 10 + - 14 + - 18 + - 22 +backup_times_minute: 0 + +backup_keep_days: 7 + +backup_owner: backup +backup_owner_ssh_key: "ssh-rsa THEREISSUCHRSAKEYYET" +backup_group: "{{ backup_owner }}" +backup_permission: 0770 + +backup_hosts: + - name: server1-name + domain: host1.my-domain.tld + ip: 001.112.223.123 + - name: server2-name + domain: host2.my-domain.tld + +backup_folder: "{{ cloud_apps }}/backup" + +#backup_client_scripts: +# - /path/to/script -param +backup_client_scripts: [] +#backup_client_script_envs: +# - var: VAR_NAME +# path: /some/path +# - var: VAR_NAME +# value: some_value +backup_client_script_envs: [] + +backup_lock: "{{ backup_folder }}/backup.lock" + +#backup_targets: +# db: +# - type: mariadb +# name: db_name_mdb +# - type: pgsql +# name: db_name_pg +# mail: +# - type: file +# path: /path/to/some/file +# - type: mariadb +# name: db_name_mdb_mail +# file: +# - /path/to/some/file +# - /path/to/some/dir +backup_targets: + db: [] + mail: [] + file: [] \ No newline at end of file diff --git a/roles/backup/handlers/main.yml b/roles/backup/handlers/main.yml new file mode 100755 index 0000000..ed97d53 --- /dev/null +++ b/roles/backup/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/backup/meta/.galaxy_install_info b/roles/backup/meta/.galaxy_install_info new file mode 100755 index 0000000..323f84b --- /dev/null +++ b/roles/backup/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:09 ' +version: '' diff --git a/roles/backup/meta/main.yml b/roles/backup/meta/main.yml new file mode 100755 index 0000000..0e6dc1a --- /dev/null +++ b/roles/backup/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: backup + namespace: hahn-cloud + author: Lars Hahn + company: Data Learning + license: MIT + description: Role to setup a backup system + configuration. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - backup +dependencies: [] diff --git a/roles/backup/tasks/client.yml b/roles/backup/tasks/client.yml new file mode 100755 index 0000000..bf17a8b --- /dev/null +++ b/roles/backup/tasks/client.yml @@ -0,0 +1,38 @@ +--- +- name: generate backup ssh user + user: + name: "{{ backup_owner }}" + group: "{{ backup_group }}" + comment: Backup user + shell: /bin/sh + +- name: Setup User ssh_key + authorized_key: + user: "{{ backup_owner }}" + key: "{{ backup_owner_ssh_key }}" + +- name: generate config lists + template: + src: backup/conf/{{ conf }}.list.j2 + dest: "{{ backup_conf }}/{{ conf }}.list" + loop: + - filedir + - db + - mail + loop_control: + loop_var: conf + label: "{{ conf }}.list" + +- name: setup backup script + template: + src: backup/backup_on_client.sh.j2 + dest: "{{ backup_folder }}/backup.sh" + mode: 0740 + +- name: setup cronjob for client backup generation + cron: + name: "backup" + weekday: "{{ backup_times_weekday }}" + hour: "{{ backup_times_hour | join(',') }}" + minute: "{{ backup_times_minute }}" + job: "{{ backup_folder }}/backup.sh" diff --git a/roles/backup/tasks/main.yml b/roles/backup/tasks/main.yml new file mode 100755 index 0000000..53d8579 --- /dev/null +++ b/roles/backup/tasks/main.yml @@ -0,0 +1,34 @@ +--- +# Basics +- name: install requirements for backup system + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - rsync + +- name: setup storage folders for backup + file: + state: directory + path: "{{ dir.value.path }}" + owner: "{{ backup_owner }}" + group: "{{ backup_group }}" + mode: "{{ backup_permission }}" + with_dict: "{{ backup_storage }}" + loop_control: + loop_var: dir + label: "{{ dir.key }}" + +- name: setup conf folder + file: + state: directory + path: "{{ backup_folder }}/conf" + +- name: setup client backup structure + include_tasks: client.yml + when: backup_client + +- name: setup server backup structure + include_tasks: server.yml + when: not backup_client \ No newline at end of file diff --git a/roles/backup/tasks/server.yml b/roles/backup/tasks/server.yml new file mode 100755 index 0000000..cbd4976 --- /dev/null +++ b/roles/backup/tasks/server.yml @@ -0,0 +1,27 @@ +--- +- name: analyse backup hours for pulling server + set_fact: + backup_times_server_hour: "{{ backup_times_server_hour | default([]) + [item + backup_hour_shift] }}" + loop: "{{ backup_times_hour }}" + +- name: generate backup user ssh key + user: + name: "{{ backup_owner }}" + generate_ssh_key: yes + ssh_key_type: rsa + ssh_key_bits: 4096 + home: "{{ backup_folder }}" + +- name: template main server backup pull script + template: + src: backup/backup_from_clients.sh.j2 + dest: "{{ backup_folder }}/pull-backup.sh" + mode: 0740 + +- name: setup cronjob for server backup pull + cron: + name: "pull backups" + weekday: "{{ backup_times_weekday }}" + hour: "{{ backup_times_server_hour | join(',') }}" + minute: "{{ backup_times_minute }}" + job: "{{ backup_folder }}/pull-backup.sh" diff --git a/roles/backup/templates/backup/backup_from_clients.sh.j2 b/roles/backup/templates/backup/backup_from_clients.sh.j2 new file mode 100755 index 0000000..d52d5ad --- /dev/null +++ b/roles/backup/templates/backup/backup_from_clients.sh.j2 @@ -0,0 +1,68 @@ +#!/bin/bash + +set -e -o pipefail + +cd {{ backup_folder }} +if [ -f {{ backup_lock }} ]; then + echo "Backup lock! Cannot procceed as lock '{{ backup_lock }}' exist." + echo "Associated PID is $(cat {{ backup_lock }})" + exit 1 +fi + +echo "$$" > {{ backup_lock }} +BKPTIMESTAMP=$(date +"%y-%m-%d.%H") +BKP=$BKPTIMESTAMP"_backup" + + +# generate rolling folder +{% for target in backup_targets %} +{% for host in backup_hosts %} +{% if 'domain' in host %} +mkdir -p {{ target }}/{{ host.name + '_' + host.domain }}/$BKP +{% elif 'ip' in host %} +mkdir -p {{ target }}/{{ host.name + '_' + host.ip }}/$BKP +{% endif %} +{% endfor %} +{% endfor %} + + +# pull backups +{% for target in backup_targets %} +{% for host in backup_hosts %} +{% if 'domain' in host %} +rsync --rsh='ssh -p{{ ssh_port }} -i ~{{ backup_owner }}/.ssh/id_rsa' -chav --stats --ignore-missing-args {{ backup_owner }}@{{ host.domain }}:{{ backup_folder }}/{{ target }}.tgz {{ target }}/{{ host.name + '_' + host.domain }}/$BKP +{% elif 'ip' in host %} +rsync --rsh='ssh -p{{ ssh_port }} -i ~{{ backup_owner }}/.ssh/id_rsa' -chav --stats --ignore-missing-args {{ backup_owner }}@{{ host.ip }}:{{ backup_folder }}/{{ target }}.tgz {{ target }}/{{ host.name + '_' + host.ip }}/$BKP +{% endif %} +{% endfor %} +{% endfor %} + + +# link to latest +{% for target in backup_targets %} +{% for host in backup_hosts %} +{% if 'domain' in host %} +ln -sf {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.domain }}/$BKP {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.domain }}/LATEST +{% elif 'ip' in host %} +ln -sf {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.ip }}/$BKP {{ backup_folder }}/{{ target }}/{{ host.name + '_' + host.ip }}/LATEST +{% endif %} +{% endfor %} +{% endfor %} + + +# keep only last {{ backup_keep_days * ( backup_times_hour | length ) }} +{% for target in backup_targets %} +{% for host in backup_hosts %} +{% if 'domain' in host %} +DELETE_LIST=$(ls -1dtr {{ target }}/{{ host.name + '_' + host.domain }}/* | head -n -{{ backup_keep_days * ( backup_times_hour | length ) }}) +{% elif 'ip' in host %} +DELETE_LIST=$(ls -1dtr {{ target }}/{{ host.name + '_' + host.ip }}/* | head -n -{{ backup_keep_days * ( backup_times_hour | length ) }}) +{% endif %} +if (( $( echo $DELETE_LIST | wc -w ) > 0 )); then + rm -rf $DELETE_LIST +fi +{% endfor %} +{% endfor %} + + +rm {{ backup_lock }} diff --git a/roles/backup/templates/backup/backup_on_client.sh.j2 b/roles/backup/templates/backup/backup_on_client.sh.j2 new file mode 100755 index 0000000..ba38eed --- /dev/null +++ b/roles/backup/templates/backup/backup_on_client.sh.j2 @@ -0,0 +1,72 @@ +#!/bin/bash + +set -e -o pipefail + +if [ -f {{ backup_lock }} ]; then + echo "Backup lock! Cannot procceed as lock '{{ backup_lock }}' exist." + echo "Associated PID is $(cat {{ backup_lock }})" + exit 1 +fi +echo "$$" > {{ backup_lock }} + +cd {{ backup_folder }} + +# Load script envs +echo "Load script environment variables" +{% for scr_env in backup_client_script_envs %} +{% if 'path' in scr_env %} +export {{ scr_env.var }}={{ scr_env.path }}:${{ scr_env.var }} +{% else %} +export {{ scr_env.var }}={{ scr_env.value }} +{% endif %} +{% endfor %} + + +# Perform client pre backup scripts for +echo "Perform pre-backup scripts" +{% for cmd in backup_client_scripts %} +{{ cmd }}; +{% endfor %} + + +# Backup files and directories +echo "Copy files and directories" +while read DIRFILE; do + echo " - Copy $DIRFILE" + BACKUP_SUBDIR=$(dirname $DIRFILE) + mkdir -p "{{ backup_storage.file.path }}/$BACKUP_SUBDIR" + cp -r -u $DIRFILE "{{ backup_storage.file.path }}/$DIRFILE" +done < {{ backup_conf }}/filedir.list +rm -f file.tgz #remove old backup +tar czfv file.tgz file +rm -rf file/* + +# Backup databases +echo "Generate DB dumps" +while read DBS; do + DBTYPE=$(echo $DBS | cut -f 1 -d "{{ backup_conf_db_sep }}" ); + DBNAME=$(echo $DBS | cut -f 2 -d "{{ backup_conf_db_sep }}" ); + if [ $DBTYPE == "mariadb" ] || [ $DBTYPE == "mysql" ]; then + echo "- dump to {{ backup_storage.db.path }}/$DBTYPE/$DBNAME.sql" + mkdir -p {{ backup_storage.db.path }}/$DBTYPE; + mysqldump $DBNAME | gzip -c > {{ backup_storage.db.path }}/$DBTYPE/$DBNAME.sql.gz; + elif [ $DBTYPE == "pgsql" ]; then + echo "- dump to {{ backup_storage.db.path }}/$DBTYPE/$DBNAME.sql" + mkdir -p {{ backup_storage.db.path }}/$DBTYPE; + sudo -u postgres bash -c "pg_dump $DBNAME | gzip -c > /tmp/$DBNAME.sql.gz"; + mv /tmp/$DBNAME.sql.gz /backup/db/$DBTYPE/$DBNAME.sql.gz; + else + echo "- ERROR! DB type '$DBTYPE' for '$DBNAME' is unknown to backup." + fi +done < {{ backup_conf }}/db.list +rm -f db.tgz #remove old backup +tar czfv db.tgz db +rm -rf db/* + +#Mail +rm -f mail.tgz #remove old backup +tar czfv mail.tgz mail +rm -rf mail/* + +rm {{ backup_lock }} +chown {{ backup_owner }}:{{ backup_group }} {{ backup_folder }}/*.tgz \ No newline at end of file diff --git a/roles/backup/templates/backup/conf/db.list.j2 b/roles/backup/templates/backup/conf/db.list.j2 new file mode 100755 index 0000000..7b75127 --- /dev/null +++ b/roles/backup/templates/backup/conf/db.list.j2 @@ -0,0 +1,5 @@ +{% if 'db' in backup_targets %} +{% for db in backup_targets.db %} +{{ db.type }}{{ backup_conf_db_sep }}{{ db.name }} +{% endfor %} +{% endif %} \ No newline at end of file diff --git a/roles/backup/templates/backup/conf/filedir.list.j2 b/roles/backup/templates/backup/conf/filedir.list.j2 new file mode 100755 index 0000000..a045c6f --- /dev/null +++ b/roles/backup/templates/backup/conf/filedir.list.j2 @@ -0,0 +1,5 @@ +{% if 'file' in backup_targets %} +{% for filedir in backup_targets.file %} +{{ filedir }} +{% endfor %} +{% endif %} \ No newline at end of file diff --git a/roles/backup/templates/backup/conf/mail.list.j2 b/roles/backup/templates/backup/conf/mail.list.j2 new file mode 100755 index 0000000..7224c8d --- /dev/null +++ b/roles/backup/templates/backup/conf/mail.list.j2 @@ -0,0 +1,5 @@ +{% if 'mail' in backup_targets %} +{% for mail in backup_targets.mail %} +{{ mail }} +{% endfor %} +{% endif %} \ No newline at end of file diff --git a/roles/backup/vars/main.yml b/roles/backup/vars/main.yml new file mode 100755 index 0000000..27ac0f8 --- /dev/null +++ b/roles/backup/vars/main.yml @@ -0,0 +1,12 @@ +--- +backup_conf: "{{ backup_folder }}/conf" + +backup_conf_db_sep: ":" + +backup_storage: + mail: + path: "{{ backup_folder }}/mail" + db: + path: "{{ backup_folder }}/db" + file: + path: "{{ backup_folder }}/file" \ No newline at end of file diff --git a/roles/basis/LICENSE b/roles/basis/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/basis/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/basis/README.md b/roles/basis/README.md new file mode 100755 index 0000000..5b96b7b --- /dev/null +++ b/roles/basis/README.md @@ -0,0 +1,4 @@ +# cloud-basis + +Ansible role to setup basic cloud configuration for a cloud node. +E.g.: mount volumes, setup user, install basic software and so on. \ No newline at end of file diff --git a/roles/basis/defaults/main.yml b/roles/basis/defaults/main.yml new file mode 100755 index 0000000..8c0807f --- /dev/null +++ b/roles/basis/defaults/main.yml @@ -0,0 +1,78 @@ +--- +cloud_update: false +cloud_name: cloud +cloud_home: "/opt/{{ cloud_name }}" +cloud_type: "cloud" +cloud_env: production +cloud_env_path: "{{ cloud_home }}/{{ cloud_env }}" +cloud_host_group: server +cloud_control_version: 1.0.0 +cloud_control_name: cloud-control +cloud_git_branch_main: main +cloud_stage: prod + +cloud_tzdata: Europe/Berlin + +cloud_apps: /opt +cloud_storage: /srv +cloud_python_envs: "{{ cloud_apps }}/pyenv" + +shared_group: "{{ cloud_name }}" + +users: + - name: username + displayname: User Name + shell: /bin/bash + groups: + - sudo + - username + state: present + ssh_key: "ssh-rsa ABCDEF" + +default_groups: + - "ssh" + - "users" + - "cdrom" + - "{{ cloud_shared_group }}" + +ssh_port: 22 + +ssh_configs: + - Protocol 2 + - "Port {{ cloud_ssh_port }}" + - PermitRootLogin no + - PubkeyAuthentication yes + - PasswordAuthentication no + - PermitEmptyPasswords no + +fail2ban_bantime: 1h +fail2ban_maxretry: 3 +fail2ban_nginx_selfmade_filter: + - nginx-noscript + - nginx-nohome + - nginx-noproxy +fail2ban_nginx_default_filter: + - nginx-limit-req + - nginx-botsearch +fail2ban_activate_modules: + - sshd + - nginx + +basis_apps: + - passwd + - vim + - unzip + +#mount_points: +# - path: /some/path +# dev: /dev/sdb +# fstype: ext4 +# opts: noatime +# state: mounted +mount_points: [] + +swap_on: true +swap_file: /swapfile +#block size * block count = swap size (Bytes) +swap_block_size: 1024 +swap_block_count: 1048576 diff --git a/roles/basis/handlers/main.yml b/roles/basis/handlers/main.yml new file mode 100755 index 0000000..e7c289c --- /dev/null +++ b/roles/basis/handlers/main.yml @@ -0,0 +1,12 @@ +--- +- name: restart sshd service + service: + name: sshd + state: restarted + enabled: yes + +- name: restart fail2ban service + service: + name: fail2ban + state: restarted + enabled: yes diff --git a/roles/basis/meta/.galaxy_install_info b/roles/basis/meta/.galaxy_install_info new file mode 100755 index 0000000..323f84b --- /dev/null +++ b/roles/basis/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:09 ' +version: '' diff --git a/roles/basis/meta/main.yml b/roles/basis/meta/main.yml new file mode 100755 index 0000000..3c78dbc --- /dev/null +++ b/roles/basis/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: basis + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Basis role to setup a basic system with ansible; e.g. Users, Fail2Ban, SSHD + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - basis +dependencies: [] diff --git a/roles/basis/tasks/cloud_control.yml b/roles/basis/tasks/cloud_control.yml new file mode 100755 index 0000000..8bd2795 --- /dev/null +++ b/roles/basis/tasks/cloud_control.yml @@ -0,0 +1,8 @@ +--- +- name: setup Cloud Control script + template: + src: "templates{{ cloud_control_path }}/cloud-control.j2" + dest: "{{ cloud_control_path }}/{{ cloud_control_name }}" + owner: root + group: root + mode: 0754 diff --git a/roles/basis/tasks/fail2ban.yml b/roles/basis/tasks/fail2ban.yml new file mode 100755 index 0000000..21955ff --- /dev/null +++ b/roles/basis/tasks/fail2ban.yml @@ -0,0 +1,30 @@ +--- +- name: install fail2ban service + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: fail2ban + +- name: setup fail2ban config jail.local + template: + backup: yes + src: "templates{{ fail2ban_jail_conf }}.j2" + dest: "{{ fail2ban_jail_conf }}" + owner: root + group: root + mode: 0644 + notify: restart fail2ban service + +- name: Install fail2ban filter plugins nginx + template: + src: "templates{{ fail2ban_path }}/filter.d/{{ filter }}.local.j2" + dest: "{{ fail2ban_path }}/filter.d/{{ filter }}.local" + owner: root + group: root + mode: 0644 + loop: "{{ fail2ban_nginx_filter }}" + loop_control: + label: "{{ filter }}" + loop_var: filter + notify: restart fail2ban service diff --git a/roles/basis/tasks/main.yml b/roles/basis/tasks/main.yml new file mode 100755 index 0000000..07047bf --- /dev/null +++ b/roles/basis/tasks/main.yml @@ -0,0 +1,60 @@ +--- +- name: Limit access to cloude home only for root + file: + state: directory + path: "{{ cloud_home }}" + owner: root + group: root + mode: 0700 + recurse: yes + +- name: Install basic apps via apt + apt: + update_cache: yes + autoclean: yes + autoremove: yes + state: latest + install_recommends: yes + pkg: "{{ basis_apps }}" + +- name: set timezone + timezone: + name: "{{ cloud_tzdata }}" + +- name: Setup hahn-cloud users + import_tasks: users.yml + +- name: Setup and configure sshd service + import_tasks: sshd.yml + +- name: Setup and configure fail2ban service + import_tasks: fail2ban.yml + +- name: Setup and configure cloud control script + import_tasks: cloud_control.yml + +- name: Setup mount points + import_tasks: mount.yml + +- name: Setup swap + import_tasks: swap.yml + when: swap_on + +- name: Setup basic cloud folders for apps and storage + file: + state: directory + path: "{{ item }}" + mode: 0755 + owner: root + group: "{{ shared_group }}" + loop: + - "{{ cloud_apps }}" + - "{{ cloud_storage }}" + +- name: Setup shared python environment for all cloud users + file: + state: directory + path: "{{ cloud_python_envs }}" + mode: 0755 + owner: root + group: "{{ shared_group }}" diff --git a/roles/basis/tasks/mount.yml b/roles/basis/tasks/mount.yml new file mode 100755 index 0000000..dd28f6a --- /dev/null +++ b/roles/basis/tasks/mount.yml @@ -0,0 +1,20 @@ +- name: generate filesystem on mounts if not defined + filesystem: + fstype: "{{ mnt.fstype | default('ext4') }}" + dev: "{{ mnt.dev }}" + loop: "{{ mount_points }}" + loop_control: + loop_var: mnt + label: "{{ mnt.dev }}: {{ mnt.fstype | default('ext4') }}" + +- name: mount required points + mount: + path: "{{ mnt.path }}" + src: "{{ mnt.dev }}" + state: "{{ mnt.state | default('mounted') }}" + fstype: "{{ mnt.fstype | default('ext4') }}" + opts: "{{ mnt.opts | default('defaults') }}" + loop: "{{ mount_points }}" + loop_control: + loop_var: mnt + label: "{{ mnt.dev }}: {{ mnt.path }}" \ No newline at end of file diff --git a/roles/basis/tasks/sshd.yml b/roles/basis/tasks/sshd.yml new file mode 100755 index 0000000..027f716 --- /dev/null +++ b/roles/basis/tasks/sshd.yml @@ -0,0 +1,21 @@ +--- +- name: install OpenSSH server + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: openssh-server + +- name: setup sshd_config + lineinfile: + path: "{{ sshd_conf }}" + regexp: '^#?{{ configline.split(" ")[0] }}{% if configline.split(" ") | length > 1 %} {% endif %}' + line: "{{ configline }}" + owner: root + group: root + mode: 0644 + loop: "{{ ssh_configs }}" + loop_control: + loop_var: configline + label: "{{ configline }}" + notify: restart sshd service diff --git a/roles/basis/tasks/swap.yml b/roles/basis/tasks/swap.yml new file mode 100755 index 0000000..dd98ad0 --- /dev/null +++ b/roles/basis/tasks/swap.yml @@ -0,0 +1,35 @@ +--- +- name: generate file for swapping + command: + cmd: "dd if=/dev/zero of={{ swap_file }} bs={{ swap_block_size }} count={{ swap_block_count }}" + chdir: "/" + creates: "{{ swap_file }}" + register: swapfile_setup + +- name: prepare swap file + command: + cmd: "mkswap {{ swap_file }}" + chdir: "/" + when: swapfile_setup.changed + register: swapfile_creation + +- name: set swap permission + file: + path: "{{ swap_file }}" + mode: 0600 + +- name: activate swap + command: + cmd: "swapon {{ swap_file }}" + chdir: "/" + when: swapfile_creation.changed + +- name: mount swap on boot + mount: + path: none + src: "{{ swap_file }}" + fstype: swap + opts: sw + passno: 0 + dump: 0 + state: present diff --git a/roles/basis/tasks/users.yml b/roles/basis/tasks/users.yml new file mode 100755 index 0000000..85c776d --- /dev/null +++ b/roles/basis/tasks/users.yml @@ -0,0 +1,47 @@ +--- +- name: list active users + shell: cat /etc/passwd | grep -v "nologin" | cut -f 1 -d ":" + changed_when: false + register: active_users + +- name: Setup required groups of users + group: + name: "{{ group }}" + state: present + loop: "{{ ((users | json_query('[*].groups') | flatten) + default_groups) | unique }}" + loop_control: + loop_var: group + label: "{{ group }}" + +- name: Setup users + user: + name: "{{ user.name }}" + state: "{{ user.state }}" + comment: "{{ user.displayname }}" + group: "{{ user.name }}" + groups: "{{ default_groups | default([]) + user.groups }}" + append: yes + shell: "{{ user.shell }}" + loop: "{{ users }}" + loop_control: + loop_var: user + label: "{{ user.name }}" + register: new_user_setup + +- name: Setup User ssh_key + authorized_key: + user: "{{ user.name }}" + state: "{{ user.state | default('present') }}" + key: "{{ user.ssh_key }}" + loop: "{{ users | selectattr('ssh_key', 'defined') | list }}" + loop_control: + label: "{{ user.name }}" + loop_var: user + +- name: Activate password setup for newly created users + shell: " set -e pipefail; passwd -d {{ user }}; chage -d0 {{ user }}" + loop: "{{ new_user_setup | json_query('results[?changed==`true`].name') | difference(active_users.stdout_lines) }}" + loop_control: + label: "{{ user }}" + loop_var: user + when: new_user_setup.changed diff --git a/roles/basis/templates/etc/fail2ban/filter.d/nginx-nohome.local.j2 b/roles/basis/templates/etc/fail2ban/filter.d/nginx-nohome.local.j2 new file mode 100755 index 0000000..9a6994e --- /dev/null +++ b/roles/basis/templates/etc/fail2ban/filter.d/nginx-nohome.local.j2 @@ -0,0 +1,5 @@ +[Definition] + +failregex = ^ -.*GET .*/~.* + +ignoreregex = \ No newline at end of file diff --git a/roles/basis/templates/etc/fail2ban/filter.d/nginx-noproxy.local.j2 b/roles/basis/templates/etc/fail2ban/filter.d/nginx-noproxy.local.j2 new file mode 100755 index 0000000..e6742db --- /dev/null +++ b/roles/basis/templates/etc/fail2ban/filter.d/nginx-noproxy.local.j2 @@ -0,0 +1,5 @@ +[Definition] + +failregex = ^ -.*GET http.* + +ignoreregex = \ No newline at end of file diff --git a/roles/basis/templates/etc/fail2ban/filter.d/nginx-noscript.local.j2 b/roles/basis/templates/etc/fail2ban/filter.d/nginx-noscript.local.j2 new file mode 100755 index 0000000..900d07f --- /dev/null +++ b/roles/basis/templates/etc/fail2ban/filter.d/nginx-noscript.local.j2 @@ -0,0 +1,5 @@ +[Definition] + +failregex = ^ -.*GET.*(\.asp|\.exe|\.pl|\.cgi|\.scgi) + +ignoreregex = diff --git a/roles/basis/templates/etc/fail2ban/jail.local.j2 b/roles/basis/templates/etc/fail2ban/jail.local.j2 new file mode 100755 index 0000000..61f8e54 --- /dev/null +++ b/roles/basis/templates/etc/fail2ban/jail.local.j2 @@ -0,0 +1,34 @@ +[DEFAULT] +bantime = {{ fail2ban_bantime }} +findtime = {{ fail2ban_bantime }} +maxretry = {{ fail2ban_maxretry }} + +[sshd] +enabled = true +port = 22,{{ ssh_port }},ssh + +[nginx-http-auth] +enabled = {{ 'nginx' in fail2ban_activate_modules }} +port = http,https +filter = nginx-noscript +logpath = %(nginx_error_log)s +maxretry = 6 + +{% for filter in fail2ban_nginx_default_filter %} +[{{ filter }}] +enabled = {{ 'nginx' in fail2ban_activate_modules }} +port = http,https +filter = {{ filter }} +logpath = %(nginx_access_log)s +maxretry = 2 + +{% endfor %} +{% for filter in fail2ban_nginx_selfmade_filter %} +[{{ filter }}] +enabled = {{ 'nginx' in fail2ban_activate_modules }} +port = http,https +filter = {{ filter }} +logpath = %(nginx_access_log)s +maxretry = 2 + +{% endfor %} diff --git a/roles/basis/templates/etc/ssh/sshd_config.j2 b/roles/basis/templates/etc/ssh/sshd_config.j2 new file mode 100755 index 0000000..dd0a119 --- /dev/null +++ b/roles/basis/templates/etc/ssh/sshd_config.j2 @@ -0,0 +1,121 @@ +# $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $ + +# This is the sshd server system-wide configuration file. See +# sshd_config(5) for more information. + +# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin + +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options override the +# default value. +Protocol {{ ssh_protocol_version }} +Port {{ ssh_port }} +#AddressFamily any +#ListenAddress 0.0.0.0 +#ListenAddress :: + +#HostKey /etc/ssh/ssh_host_rsa_key +#HostKey /etc/ssh/ssh_host_ecdsa_key +#HostKey /etc/ssh/ssh_host_ed25519_key + +# Ciphers and keying +#RekeyLimit default none + +# Logging +#SyslogFacility AUTH +#LogLevel INFO + +# Authentication: + +#LoginGraceTime 10m +PermitRootLogin {{ ssh_login_root }} +#StrictModes yes +#MaxAuthTries 6 +#MaxSessions 10 + +PubkeyAuthentication {{ ssh_use_key }} + +# Expect .ssh/authorized_keys2 to be disregarded by default in future. +#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2 + +#AuthorizedPrincipalsFile none + +#AuthorizedKeysCommand none +#AuthorizedKeysCommandUser nobody + +# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts +#HostbasedAuthentication no +# Change to yes if you don't trust ~/.ssh/known_hosts for +# HostbasedAuthentication +#IgnoreUserKnownHosts no +# Don't read the user's ~/.rhosts and ~/.shosts files +#IgnoreRhosts yes + +# To disable tunneled clear text passwords, change to no here! +PasswordAuthentication {{ ssh_use_password }} +PermitEmptyPasswords no + +# Change to yes to enable challenge-response passwords (beware issues with +# some PAM modules and threads) +ChallengeResponseAuthentication no + +# Kerberos options +#KerberosAuthentication no +#KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes +#KerberosGetAFSToken no + +# GSSAPI options +#GSSAPIAuthentication no +#GSSAPICleanupCredentials yes +#GSSAPIStrictAcceptorCheck yes +#GSSAPIKeyExchange no + +# Set this to 'yes' to enable PAM authentication, account processing, +# and session processing. If this is enabled, PAM authentication will +# be allowed through the ChallengeResponseAuthentication and +# PasswordAuthentication. Depending on your PAM configuration, +# PAM authentication via ChallengeResponseAuthentication may bypass +# the setting of "PermitRootLogin without-password". +# If you just want the PAM account and session checks to run without +# PAM authentication, then enable this but set PasswordAuthentication +# and ChallengeResponseAuthentication to 'no'. +UsePAM yes + +#AllowAgentForwarding yes +#AllowTcpForwarding yes +#GatewayPorts no +X11Forwarding no +#X11DisplayOffset 10 +#X11UseLocalhost yes +#PermitTTY yes +PrintMotd no +#PrintLastLog yes +#TCPKeepAlive yes +#PermitUserEnvironment no +#Compression delayed +#ClientAliveInterval 0 +#ClientAliveCountMax 3 +#UseDNS no +#PidFile /var/run/sshd.pid +#MaxStartups 10:30:100 +#PermitTunnel no +#ChrootDirectory none +#VersionAddendum none + +# no default banner path +#Banner none + +# Allow client to pass locale environment variables +AcceptEnv LANG LC_* + +# override default of no subsystems +Subsystem sftp /usr/lib/openssh/sftp-server + +# Example of overriding settings on a per-user basis +#Match User anoncvs +# X11Forwarding no +# AllowTcpForwarding no +# PermitTTY no +# ForceCommand cvs server diff --git a/roles/basis/templates/usr/local/bin/cloud-control.j2 b/roles/basis/templates/usr/local/bin/cloud-control.j2 new file mode 100755 index 0000000..6179181 --- /dev/null +++ b/roles/basis/templates/usr/local/bin/cloud-control.j2 @@ -0,0 +1,155 @@ +#!/bin/bash +set -e + + +### VARIABLE ################################################################### +environment_folder={{ cloud_env_path }} +environment="{{ cloud_env }}" +host_type="{{ cloud_host_group }}" +script_name=$(basename $0) +cloud_name="{{ cloud_name }}" +cloud_type="{{ cloud_type }}" +version="{{ cloud_control_version }}" +branch_main="{{ cloud_git_branch_main }}" +################################################################################ + + + +### FUNCTION ################################################################### +to_working_directory() { + if [ ! -d $environment_folder/.git ] || [ ! -d $environment_folder ]; then + echo "Environment '$environment' in '$environment_folder' not available or folder not a git repository! Abort." + exit 1 + fi + cd $environment_folder/$environment +} + +help() { + echo "$script_name, version $version by L.Hahn" + echo "" + echo " $cloud_name script for cloud control" + echo " You can checkout environment (branches), rollout configurations," + echo " run ansible and restore entire configurations." + echo "" + echo "Usage: $script_name [command] [options]" + echo "" + echo "commands:" + echo " - help print this help" + echo " - maintenance setup local server into maintenance mode; no automatic ansible call" + echo " - environment" + echo " download checkout from remote repository" + echo " update load latest remote changes for current branch" + echo " reset stash changes and reset current branch from remote repository with latest changes" + echo " restore checkout latest $branch_main branch from remote repository" + echo " - update get latest roles according to environment requirements.yml" + echo " - play play current loaded ansible playbooks" + echo " - reset perform 1. environment restore, 2. update, 3. execute" + echo "" + echo "" + echo "example:" + echo "~# $script_name environment update" + echo " this will download changes from the currently active remote branch" +} + +environment() { + to_working_directory + current_branch=$(git branch | grep "^\*" | cut -d " " -f 2) + current_upstream=$(git rev-parse $current_branch@{upstream}) + + env_option=$1 + case $env_option in + "update") + echo "### Updating branch '$current_branch' in $environment_folder ###" + git pull + ;; + "reset") + echo "### Resetting branch '$current_branch' in $environment_folder ###" + git reset --hard $current_branch + git pull + ;; + "restore") + echo "### Restoring branch '$branch_main' in $environment_folder ###" + git reset --hard HEAD + git clean -f + git checkout $branch_main + git pull + ;; + "download") + if [ $# -lt 2 ]; then + echo "Missing branch name for environment downloading" + exit 1 + fi + echo "### Stashing branch '$current_branch' & downloading branch '$2' in $environment_folder ###" + git stash + git checkout -b $2 origin/$2 + git pull + ;; + *) + echo "Unknown environments option '$env_option', abort!" + exit 1 + ;; + esac +} + +maintenance() { + to_working_directory + echo "maint" +} + +update() { + to_working_directory + ansible-galaxy install -f -p roles/ -r requirements.yml +} + +play() { + to_working_directory + ansible-playbook $cloud_type"-"$host_type".yml" +} +################################################################################ + + +### MAIN ####################################################################### +if [ $# -eq 0 ]; then + help + exit 1 +fi +script_command=$1 + +case $script_command in + "help") + help + ;; + "maintenance") + maintenance + ;; + "environment") + if [ $# -lt 2 ]; then + echo "ERROR! environment command needs options! None provided." + echo "Call '~# $script_name help' for more information." + exit 1 + fi + environment $2 $3 + ;; + "update") + update + ;; + "play") + play + ;; + "reset") + echo "#=== restore environment ===#" + environment restore + echo "" + echo "#=== update roles ===#" + update + echo "" + echo "#=== play playbook ===#" + play + echo "" + ;; + *) + echo "Unknown command '$script_command', abort!" + echo "Call '~# $script_name help' for more information." + ;; +esac +################################################################################ \ No newline at end of file diff --git a/roles/basis/vars/main.yml b/roles/basis/vars/main.yml new file mode 100755 index 0000000..9598da3 --- /dev/null +++ b/roles/basis/vars/main.yml @@ -0,0 +1,7 @@ +--- +sshd_path: "/etc/ssh" +sshd_conf: "{{ sshd_path }}/sshd_config" +fail2ban_path: "/etc/fail2ban" +fail2ban_jail_conf: "{{ fail2ban_path }}/jail.local" + +cloud_control_path: "/usr/local/bin" diff --git a/roles/checkmk/LICENSE b/roles/checkmk/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/checkmk/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/checkmk/README.md b/roles/checkmk/README.md new file mode 100755 index 0000000..7c7d88f --- /dev/null +++ b/roles/checkmk/README.md @@ -0,0 +1,3 @@ +# cloud-checkmk + +Ansible role in order to setup a checkmk node for monitoring. \ No newline at end of file diff --git a/roles/checkmk/defaults/main.yml b/roles/checkmk/defaults/main.yml new file mode 100755 index 0000000..85f227c --- /dev/null +++ b/roles/checkmk/defaults/main.yml @@ -0,0 +1,28 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +domain_external: my-domain.tld + + +checkmk_edition: free +checkmk_version: 2.0.0p22 + +checkmk_server_arch: _0.bullseye_amd64.deb +checkmk_server_source: "https://download.checkmk.com/checkmk/{{ checkmk_version }}/check-mk-{{ checkmk_edition }}-{{ checkmk_version }}{{ checkmk_server_arch }}" +checkmk_server_sites: + - monitoring +checkmk_server_host: "{{ domain_external }}" +checkmk_server_scheme: https +# allow requests only from that ip (server), otherwise +# metrics are available from the entire world +checkmk_server_ip: 12.34.56.78 + +checkmk_agent_arch: "-1_all.deb" +checkmk_agent_site: "{{ checkmk_server_sites[0] }}" +checkmk_agent_source: "{{ checkmk_server_scheme }}://{{ checkmk_server_host }}/{{ checkmk_agent_site }}/check_mk/agents/check-mk-agent_{{ checkmk_version }}{{ checkmk_agent_arch }}" + +checkmk_admin_pass: VeryStrongAdminPass +checkmk_is_server: false \ No newline at end of file diff --git a/roles/checkmk/handlers/main.yml b/roles/checkmk/handlers/main.yml new file mode 100755 index 0000000..4ed2461 --- /dev/null +++ b/roles/checkmk/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: restart omd + systemd: + name: omd + state: restarted + +- name: restart xinetd + systemd: + name: xinetd + state: restarted \ No newline at end of file diff --git a/roles/checkmk/meta/.galaxy_install_info b/roles/checkmk/meta/.galaxy_install_info new file mode 100755 index 0000000..323f84b --- /dev/null +++ b/roles/checkmk/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:09 ' +version: '' diff --git a/roles/checkmk/meta/main.yml b/roles/checkmk/meta/main.yml new file mode 100755 index 0000000..df20d00 --- /dev/null +++ b/roles/checkmk/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: checkmk + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup CheckMK as server or agent. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - checkmk +dependencies: [] diff --git a/roles/checkmk/tasks/main.yml b/roles/checkmk/tasks/main.yml new file mode 100755 index 0000000..aa12cfa --- /dev/null +++ b/roles/checkmk/tasks/main.yml @@ -0,0 +1,29 @@ +--- +- name: install checkmk server + include_tasks: server.yml + when: checkmk_is_server | bool + +- name: install requirements for checkmk agents + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - xinetd + +- name: install agent from monitoring server + apt: + deb: "{{ checkmk_agent_source }}" + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + ignore_errors: yes + # if the monitoring server is not fast enough online... + +- name: restrict metrics access via xinetd only for montoring server + lineinfile: + path: "/etc/xinetd.d/check_mk" + regexp: '^\s*#?only_from\s*=' + line: "\tonly_from = {{ checkmk_server_ip }}" + ignore_errors: yes # if the monitoring server is not fast enough online... + notify: restart xinetd + diff --git a/roles/checkmk/tasks/server.yml b/roles/checkmk/tasks/server.yml new file mode 100755 index 0000000..a1a5741 --- /dev/null +++ b/roles/checkmk/tasks/server.yml @@ -0,0 +1,45 @@ +--- +- name: install requirements for checkmk + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - python3-passlib + + +- name: install checkmk from deb file + apt: + deb: "{{ checkmk_server_source }}" + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + +# apache2 is default installed via e.g. deb file +# in order to run e.g. nginx as webserver, we should disable it +- name: adjust apache2 systemd unit state + systemd: + name: apache2 + enabled: "{{ checkmk_system_apache2 | default('no') | bool }}" + daemon_reload: yes + +- name: generate checkmk sites + command: + cmd: "omd create {{ item }}" + creates: "/omd/sites/{{ item }}" + notify: restart omd + loop: "{{ checkmk_server_sites }}" + +- name: setup admin password for sites + htpasswd: + path: "/omd/sites/{{ cmk_site }}/etc/htpasswd" + name: "{{ checkmk_admin_user }}" + password: "{{ checkmk_admin_pass }}" + owner: "{{ cmk_site }}" + group: "{{ cmk_site }}" + mode: 0660 + become: yes + become_user: "{{ cmk_site }}" + loop: "{{ checkmk_server_sites }}" + loop_control: + loop_var: cmk_site + label: "site: {{ cmk_site }}" \ No newline at end of file diff --git a/roles/checkmk/vars/main.yml b/roles/checkmk/vars/main.yml new file mode 100755 index 0000000..3f4ca97 --- /dev/null +++ b/roles/checkmk/vars/main.yml @@ -0,0 +1,2 @@ +--- +checkmk_admin_user: cmkadmin diff --git a/roles/docker/LICENSE b/roles/docker/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/docker/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/docker/README.md b/roles/docker/README.md new file mode 100755 index 0000000..2b9a438 --- /dev/null +++ b/roles/docker/README.md @@ -0,0 +1,3 @@ +# cloud-docker + +Ansible role to setup basic docker and docker compose. \ No newline at end of file diff --git a/roles/docker/defaults/main.yml b/roles/docker/defaults/main.yml new file mode 100755 index 0000000..f050490 --- /dev/null +++ b/roles/docker/defaults/main.yml @@ -0,0 +1,7 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +docker_compose_version: 1.29.2 \ No newline at end of file diff --git a/roles/docker/handlers/main.yml b/roles/docker/handlers/main.yml new file mode 100755 index 0000000..ed97d53 --- /dev/null +++ b/roles/docker/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/docker/meta/.galaxy_install_info b/roles/docker/meta/.galaxy_install_info new file mode 100755 index 0000000..323f84b --- /dev/null +++ b/roles/docker/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:09 ' +version: '' diff --git a/roles/docker/meta/main.yml b/roles/docker/meta/main.yml new file mode 100755 index 0000000..382bdb1 --- /dev/null +++ b/roles/docker/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: docker + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup docker. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - docker +dependencies: [] diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml new file mode 100755 index 0000000..27eb8b7 --- /dev/null +++ b/roles/docker/tasks/main.yml @@ -0,0 +1,41 @@ +--- +# Basics +- name: install requirements for docker + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - apt-transport-https + - ca-certificates + - gnupg2 + - software-properties-common + - curl + +# Docker +- name: install docker repository key + apt_key: + url: https://download.docker.com/linux/debian/gpg + state: present + +- name: install docker repository + apt_repository: + repo: "deb [arch={{ ansible_kernel.split('-')[-1] }}] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable" + state: present + +- name: install docker (docker-ce + docker-ce-cli) + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - docker-ce + - docker-ce-cli + - python3-docker + +# Docker Compose +- name: download latest docker + get_url: + url: "https://github.com/docker/compose/releases/download/{{ docker_compose_version }}/docker-compose-linux-{{ ansible_architecture }}" + dest: /usr/local/bin/docker-compose + mode: 0755 \ No newline at end of file diff --git a/roles/docker/vars/main.yml b/roles/docker/vars/main.yml new file mode 100755 index 0000000..ed97d53 --- /dev/null +++ b/roles/docker/vars/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/easy-rsa/LICENSE b/roles/easy-rsa/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/easy-rsa/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/easy-rsa/README.md b/roles/easy-rsa/README.md new file mode 100755 index 0000000..21189ba --- /dev/null +++ b/roles/easy-rsa/README.md @@ -0,0 +1,3 @@ +# cloud-easyrsa + +Ansible role to provide easyrsa for VPN CA setup. Can be used standalone aswell. \ No newline at end of file diff --git a/roles/easy-rsa/defaults/main.yml b/roles/easy-rsa/defaults/main.yml new file mode 100755 index 0000000..3dfe409 --- /dev/null +++ b/roles/easy-rsa/defaults/main.yml @@ -0,0 +1,23 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +easy_rsa_ca: "{{ ansible_hostname }}" +easy_rsa_servers: + - name: some_server + state: present +easy_rsa_clients: + - name: username + state: present + - name: username.app + state: absent +easy_rsa_vars_conf: + country: "COUNTRY" + province: "PROVINCE" + city: "CITY" + company: "YOUR COMPANY" + mail: "admin@your-company.com" + + diff --git a/roles/easy-rsa/handlers/main.yml b/roles/easy-rsa/handlers/main.yml new file mode 100755 index 0000000..ed97d53 --- /dev/null +++ b/roles/easy-rsa/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/easy-rsa/meta/.galaxy_install_info b/roles/easy-rsa/meta/.galaxy_install_info new file mode 100755 index 0000000..d442d80 --- /dev/null +++ b/roles/easy-rsa/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:10 ' +version: '' diff --git a/roles/easy-rsa/meta/main.yml b/roles/easy-rsa/meta/main.yml new file mode 100755 index 0000000..d6e3044 --- /dev/null +++ b/roles/easy-rsa/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: easy-rsa + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup an easy-rsa for PKI, CA and certificates. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - easy-rsa +dependencies: [] \ No newline at end of file diff --git a/roles/easy-rsa/tasks/main.yml b/roles/easy-rsa/tasks/main.yml new file mode 100755 index 0000000..cbd92f2 --- /dev/null +++ b/roles/easy-rsa/tasks/main.yml @@ -0,0 +1,107 @@ +--- +- name: install openvpn, easy-rsa and recommendations + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - "easy-rsa" + +- name: Setup easy-rsa variables + template: + src: "templates{{ easy_rsa_home }}/vars.j2" + dest: "{{ easy_rsa_home }}/vars" + mode: 0644 + owner: root + group: root + +- name: initialise PKI + shell: + creates: "{{ easy_rsa_home }}/pki" + chdir: "{{ easy_rsa_home }}" + cmd: ./easyrsa init-pki + +- name: setup certificate authority + shell: + creates: "{{ easy_rsa_home }}/pki/ca.crt" + chdir: "{{ easy_rsa_home }}" + cmd: "echo '{{ easy_rsa_ca }}' | ./easyrsa build-ca nopass" + +- name: setup diffie-hellman key-pair for key exchange + shell: + creates: "{{ easy_rsa_home }}/pki/dh.pem" + chdir: "{{ easy_rsa_home }}" + cmd: ./easyrsa gen-dh + +- name: setup server certificates + shell: + cmd: | + echo '{{ srvr }}' | ./easyrsa gen-req {{ srvr }} nopass + echo 'yes' | ./easyrsa sign-req server {{ srvr }} + chdir: "{{ easy_rsa_home }}" + creates: "{{ easy_rsa_home }}/pki/issued/{{ srvr }}.crt" + loop: "{{ easy_rsa_servers_active }}" + loop_control: + loop_var: srvr + label: "{{ srvr }}" + +- name: setup client certificates + shell: + cmd: | + echo '{{ client }}' | ./easyrsa gen-req {{ client }} nopass + echo 'yes' | ./easyrsa sign-req client {{ client }} + chdir: "{{ easy_rsa_home }}" + creates: "{{ easy_rsa_home }}/pki/issued/{{ client }}.crt" + loop: "{{ easy_rsa_clients_active }}" + loop_control: + loop_var: client + label: "{{ client }}" + +- name: verify certificate integrety + command: "openssl verify -CAfile {{ easy_rsa_home }}/pki/ca.crt {{ easy_rsa_home }}/pki/issued/{{ cert }}.crt" + register: easy_rsa_cert_check + changed_when: ((easy_rsa_cert_check.stdout.split(' ') | length) > 1) and (easy_rsa_cert_check.stdout.split(' ')[1] != "OK") + loop: "{{ easy_rsa_entities_active }}" + loop_control: + loop_var: cert + label: "{{ cert }}" + +- name: find abstent easy-rsa certifcates + find: + paths: "{{ easy_rsa_home }}/pki/issued/" + pattern: "{{ cert }}.crt" + loop: "{{ easy_rsa_entities_passive }}" + loop_control: + loop_var: cert + label: "{{ cert }}" + register: easy_rsa_absent_certs + +- name: remove absent easy-rsa clients certs + file: + state: absent + path: "{{ client }}" + loop: "{{ easy_rsa_absent_certs.results | json_query('[*].files[*].path') | flatten }}" + loop_control: + loop_var: client + label: "{{ client | basename }}" + when: easy_rsa_absent_certs.results | length > 0 + +- name: find abstent easy-rsa keys + find: + paths: "{{ easy_rsa_home }}/pki/private/" + pattern: "{{ cert }}.key" + loop: "{{ easy_rsa_entities_passive }}" + loop_control: + loop_var: cert + label: "{{ cert }}" + register: easy_rsa_absent_keys + +- name: remove absent easy-rsa clients keys + file: + state: absent + path: "{{ client }}" + loop: "{{ easy_rsa_absent_keys.results | json_query('[*].files[*].path') | flatten }}" + loop_control: + loop_var: client + label: "{{ client | basename }}" + when: easy_rsa_absent_keys.results | length > 0 \ No newline at end of file diff --git a/roles/easy-rsa/templates/usr/share/easy-rsa/vars.j2 b/roles/easy-rsa/templates/usr/share/easy-rsa/vars.j2 new file mode 100755 index 0000000..80d3954 --- /dev/null +++ b/roles/easy-rsa/templates/usr/share/easy-rsa/vars.j2 @@ -0,0 +1,18 @@ +set_var EASYRSA "$PWD" +set_var EASYRSA_PKI "$EASYRSA/pki" +set_var EASYRSA_DN "cn_only" +set_var EASYRSA_REQ_COUNTRY "{{ easy_rsa_vars_conf.country }}" +set_var EASYRSA_REQ_PROVINCE "{{ easy_rsa_vars_conf.province }}" +set_var EASYRSA_REQ_CITY "{{ easy_rsa_vars_conf.city }}" +set_var EASYRSA_REQ_ORG "{{ easy_rsa_vars_conf.company }} CERTIFICATE AUTHORITY" +set_var EASYRSA_REQ_EMAIL "{{ easy_rsa_vars_conf.mail }}" +set_var EASYRSA_REQ_OU "{{ easy_rsa_vars_conf.company }} EASY CA" +set_var EASYRSA_KEY_SIZE 2048 +set_var EASYRSA_ALGO rsa +set_var EASYRSA_CA_EXPIRE 7500 +set_var EASYRSA_CERT_EXPIRE 365 +set_var EASYRSA_NS_SUPPORT "no" +set_var EASYRSA_NS_COMMENT "{{ easy_rsa_vars_conf.company }} CERTIFICATE AUTHORITY" +set_var EASYRSA_EXT_DIR "$EASYRSA/x509-types" +set_var EASYRSA_SSL_CONF "$EASYRSA/openssl-easyrsa.cnf" +set_var EASYRSA_DIGEST "sha256" \ No newline at end of file diff --git a/roles/easy-rsa/vars/main.yml b/roles/easy-rsa/vars/main.yml new file mode 100755 index 0000000..8fd7599 --- /dev/null +++ b/roles/easy-rsa/vars/main.yml @@ -0,0 +1,7 @@ +--- +easy_rsa_home: "/usr/share/easy-rsa" +easy_rsa_servers_active: "{{ easy_rsa_servers | json_query('[?state==`present`].name') }}" +easy_rsa_clients_active: "{{ easy_rsa_clients | json_query('[?state==`present`].name') }}" + +easy_rsa_entities_active: "{{ (easy_rsa_servers + easy_rsa_clients) | json_query('[?state==`present`].name') }}" +easy_rsa_entities_passive: "{{ (easy_rsa_servers + easy_rsa_clients) | json_query('[?state!=`present`].name') }}" diff --git a/roles/gitea/LICENSE b/roles/gitea/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/gitea/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/gitea/README.md b/roles/gitea/README.md new file mode 100755 index 0000000..a3b7cfa --- /dev/null +++ b/roles/gitea/README.md @@ -0,0 +1,3 @@ +# cloud-gitea + +Ansible role to setup gitea. \ No newline at end of file diff --git a/roles/gitea/defaults/main.yml b/roles/gitea/defaults/main.yml new file mode 100755 index 0000000..06af88c --- /dev/null +++ b/roles/gitea/defaults/main.yml @@ -0,0 +1,42 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +gitea_data_location: "{{ cloud_storage }}/gitea-data" + +gitea_db: + type: pgsql + name: gitea_db + user: gitea_dbu + pass: gitea_dbpw + port: 5432 + host: localhost + +gitea_version: 1.16.7 + +gitea_source: https://dl.gitea.io/gitea +gitea_arch: linux-amd64 + +gitea_domain: localhost +gitea_domain_protocol: https +gitea_port: 3000 +gitea_ssh_domain: localhost + +gitea_internal_token: INTERNAL_TOKEN +gitea_jwt_secret: JWT_SECRET +gitea_secret_key: SECRET_KEY + +gitea_mail: true +gitea_mail_type: smtp +gitea_mail_tls: true +gitea_mail_domain: mail.my-domain.tld +gitea_mail_port: 465 +gitea_mail_user: user@my-domain.tld +gitea_mail_from: "{{ gitea_mail_user }}" +gitea_mail_pass: ThisCouldBeYourAdd + +gitea_admin_user: gitea_adm +gitea_admin_pass: gitea_fancy_adm_password! +gitea_admin_mail: "{{ gitea_mail_user }}" diff --git a/roles/gitea/handlers/main.yml b/roles/gitea/handlers/main.yml new file mode 100755 index 0000000..186ea82 --- /dev/null +++ b/roles/gitea/handlers/main.yml @@ -0,0 +1,18 @@ +--- +- name: restart gitea + systemd: + name: gitea + state: restarted + +- name: generate admin user + command: + cmd: | + {{ gitea_link }}/gitea admin user create + --admin + --username {{ gitea_admin_user }} + --password '{{ gitea_admin_pass }}' + --email {{ gitea_admin_mail }} + --config {{ gitea_home }}/app.ini + --work-path {{ gitea_data_location }} + become: yes + become_user: "{{ gitea_usr }}" diff --git a/roles/gitea/meta/.galaxy_install_info b/roles/gitea/meta/.galaxy_install_info new file mode 100755 index 0000000..d442d80 --- /dev/null +++ b/roles/gitea/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:10 ' +version: '' diff --git a/roles/gitea/meta/main.yml b/roles/gitea/meta/main.yml new file mode 100755 index 0000000..4dc3f94 --- /dev/null +++ b/roles/gitea/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: gitea + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup gitea Git Server + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - gitea +dependencies: + - postgres diff --git a/roles/gitea/tasks/main.yml b/roles/gitea/tasks/main.yml new file mode 100755 index 0000000..d09fa18 --- /dev/null +++ b/roles/gitea/tasks/main.yml @@ -0,0 +1,90 @@ +--- +- name: install Gitea recommendations + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - "git" + +- name: setup gitea group + group: + name: "{{ gitea_usr }}" + state: "present" + +- name: setup gitea user + user: + name: "{{ gitea_usr }}" + home: "{{ gitea_home }}" + group: "{{ gitea_grp }}" + groups: + - "{{ gitea_grp }}" + comment: Virtual Gitea User + shell: /bin/bash + state: present + system: yes + +- name: setup gitea directories + file: + state: directory + path: "{{ item }}" + owner: "{{ gitea_usr }}" + group: "{{ gitea_grp }}" + mode: 0750 + loop: + - "{{ gitea_inst }}" + - "{{ gitea_home }}" + - "{{ gitea_data_location }}" + - "{{ gitea_data_location }}/custom" + - "{{ gitea_data_location }}/data" + - "{{ gitea_data_location }}/log" + +- name: setup gitea config directories + file: + state: directory + path: "{{ item }}" + owner: "root" + group: "{{ gitea_grp }}" + mode: 0770 + loop: + - "/etc/gitea" + +- name: setup gitea installation link + file: + state: link + src: "{{ gitea_inst }}" + dest: "{{ gitea_link }}" + owner: "{{ gitea_usr }}" + group: "{{ gitea_grp }}" + mode: 0750 + +- name: download gitea + get_url: + url: "{{ gitea_source }}/{{ gitea_version }}/gitea-{{ gitea_version }}-{{ gitea_arch }}" + dest: "{{ gitea_link }}/gitea" + owner: "{{ gitea_usr }}" + group: "{{ gitea_grp }}" + mode: 0775 + notify: generate admin user + +- name: setup gitea application ini + template: + src: opt/gitea/home/app.ini.j2 + dest: "{{ gitea_home }}/app.ini" + owner: root + group: "{{ gitea_grp }}" + mode: 0640 + notify: restart gitea + +- name: setup gitea systemd unit + template: + src: etc/systemd/system/gitea.service.j2 + dest: /etc/systemd/system/gitea.service + notify: restart gitea + +- name: enable gitea systemd unit + systemd: + name: gitea + enabled: yes + daemon_reload: yes + state: started diff --git a/roles/gitea/templates/etc/systemd/system/gitea.service.j2 b/roles/gitea/templates/etc/systemd/system/gitea.service.j2 new file mode 100755 index 0000000..c206e30 --- /dev/null +++ b/roles/gitea/templates/etc/systemd/system/gitea.service.j2 @@ -0,0 +1,27 @@ +[Unit] +Description=Gitea (Git with a cup of tea) +After=syslog.target +After=network.target +Wants=postgresql.service +After=postgresql.service +#Wants=memcached.service +#After=memcached.service +#Wants=redis.service +#After=redis.service + + +[Service] +#LimitMEMLOCK=infinity +#LimitNOFILE=65535 +RestartSec=2s +Type=simple +User={{ gitea_usr }} +Group={{ gitea_grp }} +WorkingDirectory={{ gitea_data_location }} +ExecStart={{ gitea_link }}/gitea web --config {{ gitea_home }}/app.ini +Restart=always +Environment=USER={{ gitea_usr }} HOME={{ gitea_home }} GITEA_WORK_DIR={{ gitea_data_location }} +#Environment=PATH=/path/to/git/bin:/bin:/sbin:/usr/bin:/usr/sbin + +[Install] +WantedBy=multi-user.target diff --git a/roles/gitea/templates/opt/gitea/home/app.ini.j2 b/roles/gitea/templates/opt/gitea/home/app.ini.j2 new file mode 100755 index 0000000..999428d --- /dev/null +++ b/roles/gitea/templates/opt/gitea/home/app.ini.j2 @@ -0,0 +1,80 @@ +APP_NAME = gitea@{{ gitea_ssh_domain }} +RUN_USER = {{ gitea_usr }} +RUN_MODE = {{ cloud_stage }} + +[database] +DB_TYPE = postgres +HOST = {{ gitea_db.host | default('localhost') }}:{{ gitea_db.port | default(5432) }} +NAME = {{ gitea_db.name }} +USER = {{ gitea_db.user }} +PASSWD = `{{ gitea_db.pass }}` +SCHEMA = +SSL_MODE = disable +CHARSET = utf8 +PATH = {{ gitea_data_location }}/data/gitea.db +LOG_SQL = false + +[repository] +ROOT = {{ gitea_data_location }}/data/gitea-repositories + +[server] +SSH_DOMAIN = {{ gitea_ssh_domain }} +DOMAIN = {{ gitea_domain }} +HTTP_PORT = {{ gitea_port | default(3000) }} +ROOT_URL = {{ gitea_domain_protocol }}://{{ gitea_ssh_domain }}/ +DISABLE_SSH = {{ gitea_ssh | default('false') }} +SSH_PORT = {{ ssh_port | default(22) }} +LFS_START_SERVER = {{ gitea_lfs | default('true') }} +LFS_CONTENT_PATH = {{ gitea_data_location }}/data/lfs +LFS_JWT_SECRET = `{{ gitea_jwt_secret }}` +OFFLINE_MODE = {{ gitea_is_offline | default('false') }} +LANDING_PAGE = {{ gitea_landing_page | default('explore') }} + +[mailer] +ENABLED = {{ gitea_mail }} +FROM = {{ gitea_mail_from }} +MAILER_TYPE = {{ gitea_mail_type }} +HOST = {{ gitea_mail_domain }}:{{ gitea_mail_port }} +IS_TLS_ENABLED = {% if gitea_mail_tls is defined and gitea_mail_tls%}true{% else %}false{% endif %} +USER = {{ gitea_mail_user }} +PASSWD = `{{ gitea_mail_pass }}` + +[service] +REGISTER_EMAIL_CONFIRM = true +ENABLE_NOTIFY_MAIL = true +DISABLE_REGISTRATION = true +ALLOW_ONLY_EXTERNAL_REGISTRATION = false +ENABLE_CAPTCHA = false +REQUIRE_SIGNIN_VIEW = false +DEFAULT_KEEP_EMAIL_PRIVATE = true +DEFAULT_ALLOW_CREATE_ORGANIZATION = true +DEFAULT_ENABLE_TIMETRACKING = true +NO_REPLY_ADDRESS = noreply.{{ gitea_ssh_domain }} + +[picture] +DISABLE_GRAVATAR = false +ENABLE_FEDERATED_AVATAR = true + +[openid] +ENABLE_OPENID_SIGNIN = false +ENABLE_OPENID_SIGNUP = false + +[oauth2_client] +ENABLE_AUTO_REGISTRATION = true +UPDATE_AVATAR = true +ACCOUNT_LINKING = auto + +[session] +PROVIDER = file + +[log] +MODE = file +LEVEL = info +ROOT_PATH = {{ gitea_data_location }}/log +ROUTER = file + +[security] +INSTALL_LOCK = true +INTERNAL_TOKEN = `{{ gitea_internal_token }}` +SECRET_KEY = `{{ gitea_secret_key }}` +PASSWORD_HASH_ALGO = pbkdf2 diff --git a/roles/gitea/vars/main.yml b/roles/gitea/vars/main.yml new file mode 100755 index 0000000..5e68b94 --- /dev/null +++ b/roles/gitea/vars/main.yml @@ -0,0 +1,7 @@ +--- +gitea_usr: gitea +gitea_grp: "{{ gitea_usr }}" + +gitea_link: "{{ cloud_apps }}/gitea/inst" +gitea_inst: "{{ cloud_apps }}/gitea/gitea-{{ gitea_version }}" +gitea_home: "{{ cloud_apps }}/gitea/home" diff --git a/roles/gocd-agent/LICENSE b/roles/gocd-agent/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/gocd-agent/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/gocd-agent/README.md b/roles/gocd-agent/README.md new file mode 100755 index 0000000..dcbd524 --- /dev/null +++ b/roles/gocd-agent/README.md @@ -0,0 +1,3 @@ +# cloud-gocd-agent + +Ansible role to provide an GoCD agent for an existing GoCD server; does not necessarly have to on the GoCD server node. \ No newline at end of file diff --git a/roles/gocd-agent/defaults/main.yml b/roles/gocd-agent/defaults/main.yml new file mode 100755 index 0000000..4a70b1c --- /dev/null +++ b/roles/gocd-agent/defaults/main.yml @@ -0,0 +1,23 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false +domain_external: "my-domain.tld" + +java_home: "{{ cloud_apps }}/java_jdk/java" + +www_group: www-data + + +gocd_version: 22.1.0 +gocd_build: 13913 + +gocd_source: "https://download.gocd.org/binaries/{{ gocd_version }}-{{ gocd_build }}/generic" +gocd_agent_file: "go-agent-{{ gocd_version }}-{{ gocd_build }}.zip" + +gocd_java_home: "{{ java_home }}" +gocd_agent_java_xmx: 1024m +gocd_agent_java_xms: 128m + +gocd_server: "https://gocd.{{ domain_external }}/go" \ No newline at end of file diff --git a/roles/gocd-agent/handlers/main.yml b/roles/gocd-agent/handlers/main.yml new file mode 100755 index 0000000..0e64e19 --- /dev/null +++ b/roles/gocd-agent/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart gocd-agent + systemd: + name: gocd-agent + state: restarted diff --git a/roles/gocd-agent/meta/.galaxy_install_info b/roles/gocd-agent/meta/.galaxy_install_info new file mode 100755 index 0000000..d442d80 --- /dev/null +++ b/roles/gocd-agent/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:10 ' +version: '' diff --git a/roles/gocd-agent/meta/main.yml b/roles/gocd-agent/meta/main.yml new file mode 100755 index 0000000..09e6c3e --- /dev/null +++ b/roles/gocd-agent/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: gocd-agent + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup GoCD agent for an GoCD CI/CD server + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - gocd-agent +dependencies: + - java diff --git a/roles/gocd-agent/tasks/main.yml b/roles/gocd-agent/tasks/main.yml new file mode 100755 index 0000000..228f777 --- /dev/null +++ b/roles/gocd-agent/tasks/main.yml @@ -0,0 +1,79 @@ +--- +- name: install GoCD recommendations + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - "unzip" + +- name: setup GoCD group + group: + name: "{{ gocd_usr }}" + state: "present" + +- name: setup Gocd user + user: + name: "{{ gocd_usr }}" + group: "{{ gocd_grp }}" + groups: + - "{{ gocd_grp }}" + comment: Virtual GocD User + shell: /sbin/nologin + state: present + system: yes + +- name: setup GoCD directories + file: + state: directory + path: "{{ item }}" + owner: "{{ gocd_usr }}" + group: "{{ gocd_grp }}" + mode: 0750 + loop: + - "{{ gocd_main }}" + +- name: download GoCD Agent and unarchive + unarchive: + src: "{{ gocd_source }}/{{ gocd_agent_file }}" + dest: "{{ gocd_main }}" + remote_src: yes + owner: "{{ gocd_usr }}" + group: "{{ gocd_grp }}" + mode: "o=" + creates: "{{ gocd_inst }}" + register: gocd_installation + +- name: configure GoCD wrapper properties + lineinfile: + path: "{{ gocd_inst }}/wrapper-config/wrapper-properties.conf" + regexp: "^(#\\s*|){{ item.key }}=" + line: "{{ item.key }}={{item.value}}" + loop: + - key: wrapper.java.command + value: "{{ gocd_java_home }}/bin/java" + - key: wrapper.app.parameter.101 + value: "{{ gocd_server }}" + - key: set.AGENT_STARTUP_ARGS + value: "-Xms{{ gocd_agent_java_xmx }} -Xmx{{ gocd_agent_java_xmx }}" + +- name: link installation dir + file: + state: link + src: "{{ gocd_inst }}" + dest: "{{ gocd_link }}" + owner: "{{ gocd_usr }}" + group: "{{ gocd_grp }}" + +- name: setup GoCD agent systemd unit + template: + src: etc/systemd/system/gocd-agent.service.j2 + dest: /etc/systemd/system/gocd-agent.service + notify: restart gocd-agent + +- name: enable GoCD agent systemd unit + systemd: + name: gocd-agent + enabled: yes + daemon_reload: yes + state: started diff --git a/roles/gocd-agent/templates/etc/systemd/system/gocd-agent.service.j2 b/roles/gocd-agent/templates/etc/systemd/system/gocd-agent.service.j2 new file mode 100755 index 0000000..019ccfa --- /dev/null +++ b/roles/gocd-agent/templates/etc/systemd/system/gocd-agent.service.j2 @@ -0,0 +1,15 @@ +[Unit] +Description=go-agent +After=syslog.target + +[Service] +Type=forking +ExecStart={{ gocd_link }}/bin/go-agent start sysd +ExecStop={{ gocd_link }}/bin/go-agent stop sysd +User={{ gocd_usr }} +Group={{ gocd_grp }} +KillMode=control-group +Environment=SYSTEMD_KILLMODE_WARNING=true + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/gocd-agent/vars/main.yml b/roles/gocd-agent/vars/main.yml new file mode 100755 index 0000000..897b7bb --- /dev/null +++ b/roles/gocd-agent/vars/main.yml @@ -0,0 +1,7 @@ +--- +gocd_main: "{{ cloud_apps }}/gocd-agent" +gocd_inst: "{{ gocd_main }}/go-agent-{{ gocd_version }}" +gocd_link: "{{ gocd_main }}/inst" + +gocd_usr: gocd +gocd_grp: "{{ gocd_usr }}" diff --git a/roles/gocd/LICENSE b/roles/gocd/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/gocd/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/gocd/README.md b/roles/gocd/README.md new file mode 100755 index 0000000..5f9e5b6 --- /dev/null +++ b/roles/gocd/README.md @@ -0,0 +1,3 @@ +# cloud-gocd + +Ansible role to provide GoCD as an alternative to Jenkins for CI/CD. \ No newline at end of file diff --git a/roles/gocd/defaults/main.yml b/roles/gocd/defaults/main.yml new file mode 100755 index 0000000..165be45 --- /dev/null +++ b/roles/gocd/defaults/main.yml @@ -0,0 +1,24 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +java_home: "{{ cloud_apps }}/java_jdk/java" + +www_group: www-data + + +gocd_version: 22.1.0 +gocd_build: 13913 + +gocd_source: "https://download.gocd.org/binaries/{{ gocd_version }}-{{ gocd_build }}/generic" +gocd_file: "go-server-{{ gocd_version }}-{{ gocd_build }}.zip" + +gocd_artifact_location: "{{ cloud_storage }}/gocd/gocd-artifacts" + +gocd_java_home: "{{ java_home }}" +gocd_java_xmx: 4G + +gocd_admin_user: gocd_admin +gocd_admin_pass: VerySecurePasswrd! diff --git a/roles/gocd/handlers/main.yml b/roles/gocd/handlers/main.yml new file mode 100755 index 0000000..cb5467e --- /dev/null +++ b/roles/gocd/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart gocd + systemd: + name: gocd + state: restarted diff --git a/roles/gocd/meta/.galaxy_install_info b/roles/gocd/meta/.galaxy_install_info new file mode 100755 index 0000000..d442d80 --- /dev/null +++ b/roles/gocd/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:10 ' +version: '' diff --git a/roles/gocd/meta/main.yml b/roles/gocd/meta/main.yml new file mode 100755 index 0000000..f8e5fa5 --- /dev/null +++ b/roles/gocd/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: gocd + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup GoCD for CI/CD + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - gocd +dependencies: + - java diff --git a/roles/gocd/tasks/main.yml b/roles/gocd/tasks/main.yml new file mode 100755 index 0000000..fbe8b10 --- /dev/null +++ b/roles/gocd/tasks/main.yml @@ -0,0 +1,104 @@ +--- +- name: install GoCD recommendations + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - "unzip" + - "apache2-utils" + - "python3-passlib" + +- name: setup GoCD group + group: + name: "{{ gocd_usr }}" + state: "present" + +- name: setup Gocd user + user: + name: "{{ gocd_usr }}" + group: "{{ gocd_grp }}" + groups: + - "{{ gocd_grp }}" + comment: Virtual GocD User + shell: /sbin/nologin + state: present + system: yes + +- name: setup GoCD directories + file: + state: directory + path: "{{ item }}" + owner: "{{ gocd_usr }}" + group: "{{ gocd_grp }}" + mode: 0750 + loop: + - "{{ gocd_main }}" + - "{{ gocd_data }}" + - "{{ gocd_home }}" + +- name: setup GoCD admin + htpasswd: + path: "{{ gocd_home }}/passwd.properties" + name: "{{ gocd_admin_user }}" + password: "{{ gocd_admin_pass }}" + owner: root + group: "{{ gocd_grp }}" + mode: 0640 + +- name: download GoCD and unarchive + unarchive: + src: "{{ gocd_source }}/{{ gocd_file }}" + dest: "{{ gocd_main }}" + remote_src: yes + owner: "{{ gocd_usr }}" + group: "{{ gocd_grp }}" + mode: "o=" + creates: "{{ gocd_inst }}" + register: gocd_installation + +- name: configure GoCD wrapper properties + lineinfile: + path: "{{ gocd_inst }}/wrapper-config/wrapper-properties.conf" + regexp: "^(#\\s*|){{ item.key }}=" + line: "{{ item.key }}={{item.value}}" + loop: + - key: wrapper.java.command + value: "{{ gocd_java_home }}/bin/java" + - key: wrapper.java.additional.105 + value: "-Xmx{{ gocd_java_xmx }}" + +- name: link installation dir + file: + state: link + src: "{{ gocd_inst }}" + dest: "{{ gocd_link }}" + owner: "{{ gocd_usr }}" + group: "{{ gocd_grp }}" + +- name: setup GoCD systemd unit + template: + src: etc/systemd/system/gocd.service.j2 + dest: /etc/systemd/system/gocd.service + notify: restart gocd + +- name: enable gocd systemd unit + systemd: + name: gocd + enabled: yes + daemon_reload: yes + state: started + +- name: Wait for GoCD to be started in order to configure artifact store + wait_for: + port: 8153 + delay: 10 + when: gocd_installation.changed + +- name: configure GoCD config after config setup by server + lineinfile: + path: "{{ gocd_inst }}/config/cruise-config.xml" + regexp: '^(\s*)(.*)$' + line: '\1{{ gocd_artifact_location }}' + backrefs: yes + notify: restart gocd diff --git a/roles/gocd/templates/etc/systemd/system/gocd.service.j2 b/roles/gocd/templates/etc/systemd/system/gocd.service.j2 new file mode 100755 index 0000000..13c00f6 --- /dev/null +++ b/roles/gocd/templates/etc/systemd/system/gocd.service.j2 @@ -0,0 +1,15 @@ +[Unit] +Description=go-server +After=syslog.target + +[Service] +Type=forking +ExecStart={{ gocd_link }}/bin/go-server start sysd +ExecStop={{ gocd_link }}/bin/go-server stop sysd +User={{ gocd_usr }} +Group={{ gocd_grp }} +KillMode=control-group +Environment=SYSTEMD_KILLMODE_WARNING=true + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/gocd/vars/main.yml b/roles/gocd/vars/main.yml new file mode 100755 index 0000000..4d56c3e --- /dev/null +++ b/roles/gocd/vars/main.yml @@ -0,0 +1,9 @@ +--- +gocd_main: "{{ cloud_apps }}/gocd" +gocd_inst: "{{ gocd_main }}/go-server-{{ gocd_version }}" +gocd_link: "{{ gocd_main }}/inst" +gocd_home: "{{ gocd_main }}/home" +gocd_data: "{{ gocd_artifact_location }}" + +gocd_usr: gocd +gocd_grp: "{{ gocd_usr }}" diff --git a/roles/java/LICENSE b/roles/java/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/java/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/java/README.md b/roles/java/README.md new file mode 100755 index 0000000..92297e5 --- /dev/null +++ b/roles/java/README.md @@ -0,0 +1,3 @@ +# cloud-java + +Ansible role to install Adoptium Temurin JDK needed for several tools. \ No newline at end of file diff --git a/roles/java/defaults/main.yml b/roles/java/defaults/main.yml new file mode 100755 index 0000000..7b3e1c8 --- /dev/null +++ b/roles/java/defaults/main.yml @@ -0,0 +1,20 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + + +java_home: "{{ java_homepath }}/java" + +java_version: + - stream: 17 + file: 17.0.2_8 + download: jdk-17.0.2+8 + - stream: 11 + file: 11.0.14.1_1 + download: jdk-11.0.14.1+1 + link: true + - stream: 8 + file: 8u322b06 + download: jdk8u322-b06 diff --git a/roles/java/handlers/main.yml b/roles/java/handlers/main.yml new file mode 100755 index 0000000..cd21505 --- /dev/null +++ b/roles/java/handlers/main.yml @@ -0,0 +1,2 @@ +--- + diff --git a/roles/java/meta/.galaxy_install_info b/roles/java/meta/.galaxy_install_info new file mode 100755 index 0000000..d442d80 --- /dev/null +++ b/roles/java/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:10 ' +version: '' diff --git a/roles/java/meta/main.yml b/roles/java/meta/main.yml new file mode 100755 index 0000000..31d665c --- /dev/null +++ b/roles/java/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: java + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup Java Adoptium / Eclipse Temurin + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - java +dependencies: [] diff --git a/roles/java/tasks/main.yml b/roles/java/tasks/main.yml new file mode 100755 index 0000000..0132be0 --- /dev/null +++ b/roles/java/tasks/main.yml @@ -0,0 +1,50 @@ +--- +- name: install requirements for docker + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - apt-transport-https + - ca-certificates + - gnupg2 + - software-properties-common + - curl + +- name: install java Adoptium Temurin repository key + apt_key: + url: https://packages.adoptium.net/artifactory/api/gpg/key/public + state: present + +- name: install Adoptium Temurin repository + apt_repository: + repo: "deb https://packages.adoptium.net/artifactory/deb {{ ansible_distribution_release }} main" + state: present + +- name: setup java directory + file: + state: directory + path: "{{ java_homepath }}" + mode: 0755 + +- name: download Adoptium Eclipse Temurin JDK + unarchive: + src: "https://github.com/adoptium/temurin{{ java.stream }}-binaries/releases/download/{{ java.download }}/OpenJDK{{ java.stream }}U-jdk_x64_linux_hotspot_{{ java.file }}.tar.gz" + dest: "{{ java_homepath }}" + creates: "{{ java_homepath }}/{{ java.download }}" + remote_src: true + mode: 0755 + loop: "{{ java_version }}" + loop_control: + loop_var: java + label: "{{ java.download }}" + +- name: setup generic java link + file: + state: link + src: "{{ java_homepath }}/{{ java.download }}" + dest: "{{ java_home }}" + loop: "{{ java_version | json_query('[?link==`true`]') }}" + loop_control: + loop_var: java + label: "{{ java.download }}" diff --git a/roles/java/vars/main.yml b/roles/java/vars/main.yml new file mode 100755 index 0000000..6298843 --- /dev/null +++ b/roles/java/vars/main.yml @@ -0,0 +1,2 @@ +--- +java_homepath: "{{ cloud_apps }}/java_jdk" diff --git a/roles/jenkins/LICENSE b/roles/jenkins/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/jenkins/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/jenkins/README.md b/roles/jenkins/README.md new file mode 100755 index 0000000..17bb1d2 --- /dev/null +++ b/roles/jenkins/README.md @@ -0,0 +1,3 @@ +# cloud-jenkins + +Ansible role to setup Jenkins on a node. \ No newline at end of file diff --git a/roles/jenkins/defaults/main.yml b/roles/jenkins/defaults/main.yml new file mode 100755 index 0000000..8aba216 --- /dev/null +++ b/roles/jenkins/defaults/main.yml @@ -0,0 +1,25 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + + +java_home: "{{ cloud_apps }}/java_jdk/java" + +www_group: www-data + + +jenkins_version: 2.332.3 + +jenkins_source: https://get.jenkins.io +jenkins_download: "war{% if jenkins_stream_stable | default('true') | bool %}-stable{% endif %}/{{ jenkins_version }}" +jenkins_file: "jenkins.war" + +jenkins_port: 8080 + +jenkins_log: false + +jenkins_data_location: "{{ cloud_storage }}/jenkins-data" + +jenkins_java_home: "{{ java_home }}" diff --git a/roles/jenkins/handlers/main.yml b/roles/jenkins/handlers/main.yml new file mode 100755 index 0000000..dc654e5 --- /dev/null +++ b/roles/jenkins/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart jenkins + systemd: + name: jenkins + state: restarted diff --git a/roles/jenkins/meta/.galaxy_install_info b/roles/jenkins/meta/.galaxy_install_info new file mode 100755 index 0000000..292e63c --- /dev/null +++ b/roles/jenkins/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:11 ' +version: '' diff --git a/roles/jenkins/meta/main.yml b/roles/jenkins/meta/main.yml new file mode 100755 index 0000000..1943f4e --- /dev/null +++ b/roles/jenkins/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: jenkins + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup Jenkins + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - jenkins +dependencies: + - java diff --git a/roles/jenkins/tasks/main.yml b/roles/jenkins/tasks/main.yml new file mode 100755 index 0000000..8054dc9 --- /dev/null +++ b/roles/jenkins/tasks/main.yml @@ -0,0 +1,72 @@ +--- +- name: setup jenkins group + group: + name: "{{ jenkins_usr }}" + state: "present" + +- name: setup jenkins user + user: + name: "{{ jenkins_usr }}" + group: "{{ jenkins_grp }}" + groups: + - "{{ jenkins_grp }}" + comment: Virtual Jenkins User + shell: /bin/bash + state: present + system: yes + +- name: setup jenkins webroot + file: + state: directory + path: "{{ jenkins_web }}" + owner: "{{ www_group }}" + group: "{{ jenkins_grp }}" + mode: 0770 + +- name: setup jenkins directories + file: + state: directory + path: "{{ item }}" + owner: "{{ jenkins_usr }}" + group: "{{ jenkins_grp }}" + mode: 0750 + loop: + - "{{ jenkins_inst }}" + - "{{ jenkins_home }}" + +- name: download jenkins war + get_url: + url: "{{ jenkins_source }}/{{ jenkins_download }}/{{ jenkins_file }}" + dest: "{{ jenkins_inst }}/{{ jenkins_file }}" + owner: "{{ jenkins_grp }}" + group: "{{ jenkins_usr }}" + +- name: link installation dir + file: + state: link + src: "{{ jenkins_inst }}" + dest: "{{ jenkins_link }}" + owner: "{{ jenkins_usr }}" + group: "{{ jenkins_grp }}" + +- name: template jenkins conf + template: + src: opt/jenkins/inst/jenkins.conf.j2 + dest: "{{ jenkins_inst }}/jenkins.conf" + owner: "{{ jenkins_usr }}" + group: "{{ jenkins_grp }}" + mode: 0750 + notify: restart jenkins + +- name: setup jenkins systemd unit + template: + src: etc/systemd/system/jenkins.service.j2 + dest: /etc/systemd/system/jenkins.service + notify: restart jenkins + +- name: enable jenkins systemd unit + systemd: + name: jenkins + enabled: yes + daemon_reload: yes + state: started diff --git a/roles/jenkins/templates/etc/systemd/system/jenkins.service.j2 b/roles/jenkins/templates/etc/systemd/system/jenkins.service.j2 new file mode 100755 index 0000000..48fb456 --- /dev/null +++ b/roles/jenkins/templates/etc/systemd/system/jenkins.service.j2 @@ -0,0 +1,16 @@ +[Unit] +Description=Jenkins +After=network.target + +[Service] +Type=simple +User={{ jenkins_usr }} +Group={{ jenkins_grp }} +EnvironmentFile={{ jenkins_link }}/jenkins.conf +ExecStart={{ jenkins_java_home }}/bin/java ${JAVA_ARGS} -jar ${JENKINS_WAR} ${JENKINS_ARGS} +WorkingDirectory={{ jenkins_home }} +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/jenkins/templates/opt/jenkins/inst/jenkins.conf.j2 b/roles/jenkins/templates/opt/jenkins/inst/jenkins.conf.j2 new file mode 100755 index 0000000..e0bcf5d --- /dev/null +++ b/roles/jenkins/templates/opt/jenkins/inst/jenkins.conf.j2 @@ -0,0 +1,74 @@ +# defaults for Jenkins automation server + +# arguments to pass to java + +# Allow graphs etc. to work even when an X server is present +JAVA_HOME={{ jenkins_java_home }} +JAVA_ARGS="-Djava.awt.headless=true -Xmx512m" + +# make jenkins listen on IPv4 address +#JAVA_ARGS="-Djava.net.preferIPv4Stack=true" + +PIDFILE=/var/run/jenkins.pid + +# user and group to be invoked as (default to jenkins) +JENKINS_USER={{ jenkins_usr }} +JENKINS_GROUP={{ jenkins_grp }} + +# location of the jenkins war file +JENKINS_WAR={{ jenkins_link }}/{{ jenkins_file }} + +# jenkins home location +JENKINS_HOME={{ jenkins_home }} + +# set this to false if you don't want Jenkins to run by itself +# in this set up, you are expected to provide a servlet container +# to host jenkins. +RUN_STANDALONE=true + +# log location. this may be a syslog facility.priority +JENKINS_LOG=/var/log/jenkins.log +#JENKINS_LOG=daemon.info + +# Whether to enable web access logging or not. +# Set to "yes" to enable logging to /var/log/$NAME/access_log +JENKINS_ENABLE_ACCESS_LOG="{% if jenkins_log | default('false') %}no{% else %}yes{% endif %}" + +# OS LIMITS SETUP +# comment this out to observe /etc/security/limits.conf +# this is on by default because http://github.com/jenkinsci/jenkins/commit/2fb288474e980d0e7ff9c4a3b768874835a3e92e +# reported that Ubuntu's PAM configuration doesn't include pam_limits.so, and as a result the # of file +# descriptors are forced to 1024 regardless of /etc/security/limits.conf +MAXOPENFILES=8192 + +# set the umask to control permission bits of files that Jenkins creates. +# 027 makes files read-only for group and inaccessible for others, which some security sensitive users +# might consider benefitial, especially if Jenkins runs in a box that's used for multiple purposes. +# Beware that 027 permission would interfere with sudo scripts that run on the master (JENKINS-25065.) +# +# Note also that the particularly sensitive part of $JENKINS_HOME (such as credentials) are always +# written without 'others' access. So the umask values only affect job configuration, build records, +# that sort of things. +# +# If commented out, the value from the OS is inherited, which is normally 022 (as of Ubuntu 12.04, +# by default umask comes from pam_umask(8) and /etc/login.defs + +# UMASK=027 + + +HTTP_PORT={{ jenkins_port }} + +# servlet context, important if you want to use apache proxying +PREFIX=/ + +# arguments to pass to jenkins. +# --javahome=$JAVA_HOME +# --httpListenAddress=$HTTP_HOST (default 0.0.0.0) +# --httpPort=$HTTP_PORT (default 8080; disable with -1) +# --httpsPort=$HTTP_PORT +# --argumentsRealm.passwd.$ADMIN_USER=[password] +# --argumentsRealm.roles.$ADMIN_USER=admin +# --webroot=~/.jenkins/war +# --prefix=$PREFIX + +JENKINS_ARGS="--webroot={{ jenkins_web }} --httpPort={{ jenkins_port }}" diff --git a/roles/jenkins/vars/main.yml b/roles/jenkins/vars/main.yml new file mode 100755 index 0000000..a2ae0f0 --- /dev/null +++ b/roles/jenkins/vars/main.yml @@ -0,0 +1,8 @@ +--- +jenkins_inst: "{{ cloud_apps }}/jenkins/jenkins_{{ jenkins_version }}" +jenkins_link: "{{ cloud_apps }}/jenkins/inst" +jenkins_home: "{{ jenkins_data_location }}" +jenkins_web: "{{ cloud_apps }}/jenkins/web" + +jenkins_usr: jenkins +jenkins_grp: "{{ jenkins_usr }}" diff --git a/roles/mailcow/LICENSE b/roles/mailcow/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/mailcow/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/mailcow/README.md b/roles/mailcow/README.md new file mode 100755 index 0000000..a4abc17 --- /dev/null +++ b/roles/mailcow/README.md @@ -0,0 +1,3 @@ +# cloud-mailcow + +Ansible role to setup mailcow on a node. \ No newline at end of file diff --git a/roles/mailcow/defaults/main.yml b/roles/mailcow/defaults/main.yml new file mode 100755 index 0000000..df726a9 --- /dev/null +++ b/roles/mailcow/defaults/main.yml @@ -0,0 +1,50 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false +domain_external: "my-domain.tld" + + +mailcow_instance: "{{ domain_external.split('.')[:-1] | join('') | regex_replace('[^a-zA-Z0-9]+','') }}" + +mailcow_db_name: mailcow +mailcow_db_root_pass: rootpass +mailcow_db_user: mailcow +mailcow_db_pass: userpass + +mailcow_cert_subjects: + CN: "mail.{{ domain_external }}" + C: "DE" + L: "Willich" + O: "mailcow" + OU: "mailcow" + ST: "NRW" +mailcow_http: 80 +mailcow_https: 443 + +mailcow_docker_ipv4_cidr: 172.22.1 +mailcow_docker_ipv6_cidr: fd4d:6169:6c63:6f77::/64 + +mailcow_dovecot_target: 127.0.0.1:19991 +mailcow_sql_target: 127.0.0.1:13306 +mailcow_solr_target: 127.0.0.1:18983 +mailcow_redis_target: 127.0.0.1:7654 +mailcow_sieve_port: 4190 + +mailcow_other_configs: [] + +mailcow_timezone: Europe/Berlin + +mailcow_gc_time: 7200 +mailcow_solr_heap_mb: 1024 +mailcow_redis_log_count: 9999 +mailcow_sogo_timeout_m: 480 + +mailcow_use_letsencrypt: true +mailcow_use_clamav: true +mailcow_use_sogo: true +mailcow_use_solr: true +mailcow_ip_check: true +mailcow_http_check: true +mailcow_no_http: true \ No newline at end of file diff --git a/roles/mailcow/handlers/main.yml b/roles/mailcow/handlers/main.yml new file mode 100755 index 0000000..0a00d55 --- /dev/null +++ b/roles/mailcow/handlers/main.yml @@ -0,0 +1,9 @@ +--- +- name: restart mailcow docker containers + command: + cmd: "{{ item }}" + chdir: "{{ mailcow_folder }}/" + loop: + - docker-compose down + - docker-compose up -d + when: not mailcow_start.changed \ No newline at end of file diff --git a/roles/mailcow/meta/.galaxy_install_info b/roles/mailcow/meta/.galaxy_install_info new file mode 100755 index 0000000..292e63c --- /dev/null +++ b/roles/mailcow/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:11 ' +version: '' diff --git a/roles/mailcow/meta/main.yml b/roles/mailcow/meta/main.yml new file mode 100755 index 0000000..dbe7fa7 --- /dev/null +++ b/roles/mailcow/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: mailcow + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup mailcow based on docker. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - mailcow +dependencies: + - docker diff --git a/roles/mailcow/tasks/main.yml b/roles/mailcow/tasks/main.yml new file mode 100755 index 0000000..bf0a9fc --- /dev/null +++ b/roles/mailcow/tasks/main.yml @@ -0,0 +1,112 @@ +--- +# Basic +- name: install requirements for mailcow + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - python3-openssl + +# Mailcow Setup +- name: clone mailcow repository + git: + repo: https://github.com/mailcow/mailcow-dockerized + dest: "{{ mailcow_folder }}" + update: no + +# Mailcow Config +- name: setup mailcow config + template: + src: opt/mailcow/mailcow.conf.j2 + dest: "{{ mailcow_folder }}/mailcow.conf" + mode: 0600 + notify: restart mailcow docker containers + +- name: setup env link to mailcow conf + file: + src: "{{ mailcow_folder }}/mailcow.conf" + dest: "{{ mailcow_folder }}/.env" + state: link + +- name: setup ssl folder + file: + path: "{{ mailcow_ssl_path }}" + state: directory + +- name: generate dh params + openssl_dhparam: + path: "{{ mailcow_ssl_path }}/dhparams.pem" + size: 2048 + notify: restart mailcow docker containers + when: not (mailcow_use_letsencrypt | default('false')) + +- name: use predefined dh params + copy: + remote_src: yes + src: "{{ mailcow_folder }}/data/assets/ssl-example/dhparams.pem" + dest: "{{ mailcow_ssl_path }}/dhparams.pem" + notify: restart mailcow docker containers + when: (mailcow_use_letsencrypt | default('false')) + +- name: generate ssl private key + openssl_privatekey: + path: "{{ mailcow_ssl_path }}/key.pem" + size: 4096 + register: key_generation + notify: restart mailcow docker containers + +- name: generate ssl cert request + openssl_csr: + path: "{{ mailcow_ssl_path }}/csr.pem" + privatekey_path: "{{ mailcow_ssl_path }}/key.pem" + common_name: "{{ mailcow_cert_subjects.CN }}" + country_name: "{{ mailcow_cert_subjects.C }}" + locality_name: "{{ mailcow_cert_subjects.L }}" + organization_name: "{{ mailcow_cert_subjects.O }}" + organizational_unit_name: "{{ mailcow_cert_subjects.OU }}" + state_or_province_name: "{{ mailcow_cert_subjects.ST }}" + when: key_generation.changed + register: cert_request + +- name: generate selfsigned certificate + openssl_certificate: + provider: selfsigned + path: "{{ mailcow_ssl_path }}/cert.pem" + privatekey_path: "{{ mailcow_ssl_path }}/key.pem" + csr_path: "{{ mailcow_ssl_path }}/csr.pem" + when: cert_request.changed + notify: restart mailcow docker containers + +- name: set http redirect to https + template: + src: opt/mailcow/data/conf/nginx/redirect.conf.j2 + dest: "{{ mailcow_folder }}/data/conf/nginx/redirect.conf" + notify: restart mailcow docker containers + when: mailcow_no_http + +# Mailcow build +- name: check mailcow image availability + command: docker image list + changed_when: false + register: docker_image_list + +- name: pull images for mailcow containers + command: + cmd: docker-compose pull + chdir: "{{ mailcow_folder }}/" + when: "'mailcow' not in docker_image_list.stdout" + +- name: find running mailcow containers + command: + cmd: "{% raw %}docker ps --format '{{.Image}}'{% endraw %}" + chdir: "{{ mailcow_folder }}/" + changed_when: false + register: docker_running_list + +- name: start mailcow containers if not running + command: + cmd: docker-compose up -d + chdir: "{{ mailcow_folder }}/" + when: "'mailcow' not in docker_running_list.stdout" + register: mailcow_start \ No newline at end of file diff --git a/roles/mailcow/templates/opt/mailcow/data/conf/nginx/redirect.conf.j2 b/roles/mailcow/templates/opt/mailcow/data/conf/nginx/redirect.conf.j2 new file mode 100755 index 0000000..c725864 --- /dev/null +++ b/roles/mailcow/templates/opt/mailcow/data/conf/nginx/redirect.conf.j2 @@ -0,0 +1,14 @@ +server { + root /web; + listen 80 default_server; + listen [::]:80 default_server; + include /etc/nginx/conf.d/server_name.active; + if ( $request_uri ~* "%0A|%0D" ) { return 403; } + location ^~ /.well-known/acme-challenge/ { + allow all; + default_type "text/plain"; + } + location / { + return 301 https://$host$uri$is_args$args; + } +} \ No newline at end of file diff --git a/roles/mailcow/templates/opt/mailcow/mailcow.conf.j2 b/roles/mailcow/templates/opt/mailcow/mailcow.conf.j2 new file mode 100755 index 0000000..a97a96d --- /dev/null +++ b/roles/mailcow/templates/opt/mailcow/mailcow.conf.j2 @@ -0,0 +1,236 @@ +# ------------------------------ +# mailcow web ui configuration +# ------------------------------ +# example.org is _not_ a valid hostname, use a fqdn here. +# Default admin user is "admin" +# Default password is "moohoo" + +MAILCOW_HOSTNAME=mail.{{ domain_external }} + +# Password hash algorithm +# Only certain password hash algorithm are supported. For a fully list of supported schemes, +# see https://mailcow.github.io/mailcow-dockerized-docs/model-passwd/ +MAILCOW_PASS_SCHEME=BLF-CRYPT + +# ------------------------------ +# SQL database configuration +# ------------------------------ + +DBNAME={{ mailcow_db_name }} +DBUSER={{ mailcow_db_user }} + +# Please use long, random alphanumeric strings (A-Za-z0-9) + +DBPASS={{ mailcow_db_pass }} +DBROOT={{ mailcow_db_root_pass }} + +# ------------------------------ +# HTTP/S Bindings +# ------------------------------ + +# You should use HTTPS, but in case of SSL offloaded reverse proxies: +# Might be important: This will also change the binding within the container. +# If you use a proxy within Docker, point it to the ports you set below. +# Do _not_ use IP:PORT in HTTP(S)_BIND or HTTP(S)_PORT +# IMPORTANT: Do not use port 8081, 9081 or 65510! +# Example: HTTP_BIND=1.2.3.4 +# For IPv4 and IPv6 leave it empty: HTTP_BIND= & HTTPS_PORT= +# For IPv6 see https://mailcow.github.io/mailcow-dockerized-docs/firststeps-ip_bindings/ + +HTTP_PORT={{ mailcow_http }} +HTTP_BIND= + +HTTPS_PORT={{ mailcow_https }} +HTTPS_BIND= + +# ------------------------------ +# Other bindings +# ------------------------------ +# You should leave that alone +# Format: 11.22.33.44:25 or 12.34.56.78:465 etc. + +SMTP_PORT=25 +SMTPS_PORT=465 +SUBMISSION_PORT=587 +IMAP_PORT=143 +IMAPS_PORT=993 +POP_PORT=110 +POPS_PORT=995 +SIEVE_PORT={{ mailcow_sieve_port }} +DOVEADM_PORT={{ mailcow_dovecot_target }} +SQL_PORT={{ mailcow_sql_target }} +SOLR_PORT={{ mailcow_solr_target }} +REDIS_PORT={{ mailcow_redis_target }} + +# Your timezone +# See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for a list of timezones +# Use the row named 'TZ database name' + pay attention for 'Notes' row + +TZ={{ mailcow_timezone }} + +# Fixed project name +# Please use lowercase letters only + +COMPOSE_PROJECT_NAME={{ mailcow_instance }} + +# Set this to "allow" to enable the anyone pseudo user. Disabled by default. +# When enabled, ACL can be created, that apply to "All authenticated users" +# This should probably only be activated on mail hosts, that are used exclusivly by one organisation. +# Otherwise a user might share data with too many other users. +ACL_ANYONE=disallow + +# Garbage collector cleanup +# Deleted domains and mailboxes are moved to /var/vmail/_garbage/timestamp_sanitizedstring +# How long should objects remain in the garbage until they are being deleted? (value in minutes) +# Check interval is hourly + +MAILDIR_GC_TIME={{ mailcow_gc_time }} + +# Additional SAN for the certificate +# +# You can use wildcard records to create specific names for every domain you add to mailcow. +# Example: Add domains "example.com" and "example.net" to mailcow, change ADDITIONAL_SAN to a value like: +#ADDITIONAL_SAN=imap.*,smtp.* +# This will expand the certificate to "imap.example.com", "smtp.example.com", "imap.example.net", "imap.example.net" +# plus every domain you add in the future. +# +# You can also just add static names... +#ADDITIONAL_SAN=srv1.example.net +# ...or combine wildcard and static names: +#ADDITIONAL_SAN=imap.*,srv1.example.com +# + +ADDITIONAL_SAN= + +# Additional server names for mailcow UI +# +# Specify alternative addresses for the mailcow UI to respond to +# This is useful when you set mail.* as ADDITIONAL_SAN and want to make sure mail.maildomain.com will always point to the mailcow UI. +# If the server name does not match a known site, Nginx decides by best-guess and may redirect users to the wrong web root. +# You can understand this as server_name directive in Nginx. +# Comma separated list without spaces! Example: ADDITIONAL_SERVER_NAMES=a.b.c,d.e.f + +ADDITIONAL_SERVER_NAMES= + +# Skip running ACME (acme-mailcow, Let's Encrypt certs) - y/n + +SKIP_LETS_ENCRYPT={{ 'n' if (mailcow_use_letsencrypt | default('true') | bool) else 'y' }} + +# Create seperate certificates for all domains - y/n +# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames +# see https://wiki.dovecot.org/SSL/SNIClientSupport +ENABLE_SSL_SNI=n + +# Skip IPv4 check in ACME container - y/n + +SKIP_IP_CHECK={{ 'n' if (mailcow_ip_check | default('true') | bool) else 'y' }} + +# Skip HTTP verification in ACME container - y/n + +SKIP_HTTP_VERIFICATION={{ 'n' if (mailcow_http_check | default('true') | bool) else 'y' }} + +# Skip ClamAV (clamd-mailcow) anti-virus (Rspamd will auto-detect a missing ClamAV container) - y/n + +SKIP_CLAMD={{ 'n' if (mailcow_use_clamav | default('true') | bool) else 'y' }} + +# Skip SOGo: Will disable SOGo integration and therefore webmail, DAV protocols and ActiveSync support (experimental, unsupported, not fully implemented) - y/n + +SKIP_SOGO={{ 'n' if (mailcow_use_sogo | default('true') | bool) else 'y' }} + +# Skip Solr on low-memory systems or if you do not want to store a readable index of your mails in solr-vol-1. + +SKIP_SOLR={{ 'n' if (mailcow_use_solr | default('true') | bool) else 'y' }} + +# Solr heap size in MB, there is no recommendation, please see Solr docs. +# Solr is a prone to run OOM and should be monitored. Unmonitored Solr setups are not recommended. + +SOLR_HEAP={{ mailcow_solr_heap_mb | default(1024) }} + +# Allow admins to log into SOGo as email user (without any password) + +ALLOW_ADMIN_EMAIL_LOGIN=n + +# Enable watchdog (watchdog-mailcow) to restart unhealthy containers + +USE_WATCHDOG=y + +# Send watchdog notifications by mail (sent from watchdog@MAILCOW_HOSTNAME) +# CAUTION: +# 1. You should use external recipients +# 2. Mails are sent unsigned (no DKIM) +# 3. If you use DMARC, create a separate DMARC policy ("v=DMARC1; p=none;" in _dmarc.MAILCOW_HOSTNAME) +# Multiple rcpts allowed, NO quotation marks, NO spaces + +#WATCHDOG_NOTIFY_EMAIL=a@example.com,b@example.com,c@example.com +#WATCHDOG_NOTIFY_EMAIL= + +# Notify about banned IP (includes whois lookup) +WATCHDOG_NOTIFY_BAN=n + +# Subject for watchdog mails. Defaults to "Watchdog ALERT" followed by the error message. +#WATCHDOG_SUBJECT= + +# Checks if mailcow is an open relay. Requires a SAL. More checks will follow. +# https://www.servercow.de/mailcow?lang=en +# https://www.servercow.de/mailcow?lang=de +# No data is collected. Opt-in and anonymous. +# Will only work with unmodified mailcow setups. +WATCHDOG_EXTERNAL_CHECKS=n + +# Max log lines per service to keep in Redis logs + +LOG_LINES={{ mailcow_redis_log_count }} + +# Internal IPv4 /24 subnet, format n.n.n (expands to n.n.n.0/24) +# Use private IPv4 addresses only, see https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses + +IPV4_NETWORK={{ mailcow_docker_ipv4_cidr }} + +# Internal IPv6 subnet in fc00::/7 +# Use private IPv6 addresses only, see https://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses + +IPV6_NETWORK={{ mailcow_docker_ipv6_cidr }} + +# Use this IPv4 for outgoing connections (SNAT) + +#SNAT_TO_SOURCE= + +# Use this IPv6 for outgoing connections (SNAT) + +#SNAT6_TO_SOURCE= + +# Create or override an API key for the web UI +# You _must_ define API_ALLOW_FROM, which is a comma separated list of IPs +# An API key defined as API_KEY has read-write access +# An API key defined as API_KEY_READ_ONLY has read-only access +# Allowed chars for API_KEY and API_KEY_READ_ONLY: a-z, A-Z, 0-9, - +# You can define API_KEY and/or API_KEY_READ_ONLY + +#API_KEY= +#API_KEY_READ_ONLY= +#API_ALLOW_FROM=172.22.1.1,127.0.0.1 + +# mail_home is ~/Maildir +MAILDIR_SUB=Maildir + +# SOGo session timeout in minutes +SOGO_EXPIRE_SESSION={{ mailcow_sogo_timeout_m }} + +# DOVECOT_MASTER_USER and DOVECOT_MASTER_PASS must both be provided. No special chars. +# Empty by default to auto-generate master user and password on start. +# User expands to DOVECOT_MASTER_USER@mailcow.local +# LEAVE EMPTY IF UNSURE +DOVECOT_MASTER_USER= +# LEAVE EMPTY IF UNSURE +DOVECOT_MASTER_PASS= + +# Let's Encrypt registration contact information +# Optional: Leave empty for none +# This value is only used on first order! +# Setting it at a later point will require the following steps: +# https://mailcow.github.io/mailcow-dockerized-docs/debug-reset-tls/ +ACME_CONTACT= + +{% for config_line in mailcow_other_configs %} +{{ config_line }} +{% endfor %} \ No newline at end of file diff --git a/roles/mailcow/vars/main.yml b/roles/mailcow/vars/main.yml new file mode 100755 index 0000000..6c81d25 --- /dev/null +++ b/roles/mailcow/vars/main.yml @@ -0,0 +1,3 @@ +--- +mailcow_folder: "{{ cloud_apps }}/mailcow" +mailcow_ssl_path: "{{ mailcow_folder }}/data/assets/ssl" \ No newline at end of file diff --git a/roles/mariadb/LICENSE b/roles/mariadb/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/mariadb/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/mariadb/README.md b/roles/mariadb/README.md new file mode 100755 index 0000000..7ab925d --- /dev/null +++ b/roles/mariadb/README.md @@ -0,0 +1,3 @@ +# cloud-mariadb + +Ansible role to provide MariaDB for tools that rely on it. \ No newline at end of file diff --git a/roles/mariadb/defaults/main.yml b/roles/mariadb/defaults/main.yml new file mode 100755 index 0000000..9f07781 --- /dev/null +++ b/roles/mariadb/defaults/main.yml @@ -0,0 +1,20 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +mariadb_version: 10.7.1 + +#mariadb root pass; defaults to empty +mariadb_root_pass: + +db_configs: + - type: mariadb + name: db_name + user: db_user + pass: db_user_password + priv: ALL + +mariadb_remote_root_login: false +mariadb_local_sock: "/var/run/mysqld/mysqld.sock" \ No newline at end of file diff --git a/roles/mariadb/handlers/main.yml b/roles/mariadb/handlers/main.yml new file mode 100755 index 0000000..cd21505 --- /dev/null +++ b/roles/mariadb/handlers/main.yml @@ -0,0 +1,2 @@ +--- + diff --git a/roles/mariadb/meta/.galaxy_install_info b/roles/mariadb/meta/.galaxy_install_info new file mode 100755 index 0000000..292e63c --- /dev/null +++ b/roles/mariadb/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:11 ' +version: '' diff --git a/roles/mariadb/meta/main.yml b/roles/mariadb/meta/main.yml new file mode 100755 index 0000000..4de0f30 --- /dev/null +++ b/roles/mariadb/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: mariadb + namespace: hahn-cloud + author: Lars Hahn + company: Data Learning + license: MIT + description: Role to setup mariadb with ansible. + min_ansible_version: 2.8 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - mariadb +dependencies: [] diff --git a/roles/mariadb/tasks/main.yml b/roles/mariadb/tasks/main.yml new file mode 100755 index 0000000..ea14bfa --- /dev/null +++ b/roles/mariadb/tasks/main.yml @@ -0,0 +1,81 @@ +--- +- name: install requirements for mariadb + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - apt-transport-https + - software-properties-common + - gpg + - gpg-agent + +- name: install mariadb repository key + apt_key: + url: https://mariadb.org/mariadb_release_signing_key.asc + state: present + +- name: install mariadb repository + apt_repository: + repo: "deb [arch={{ ansible_kernel.split('-')[-1] }}] https://archive.mariadb.org/mariadb-{{ mariadb_version }}/repo/debian/ {{ ansible_distribution_release }} main" + state: present + +- name: install mariadb + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - mariadb-server + - python3-pymysql + register: mdb_install + +- name: setup mariadb service + service: + name: mariadb + enabled: true + state: started + +# This should only run when initially installed +- name: initial setup mariadb root user + mysql_user: + check_implicit_admin: yes + name: "{{ mariadb_root_user }}" + password: "{{ mariadb_root_pass }}" + priv: '*.*:ALL,GRANT' + login_unix_socket: "{{ mariadb_local_sock }}" + when: mdb_install.changed + +- name: setup sql secrets file for root + template: + mode: 0600 + src: root/.my.cnf.j2 + dest: /root/.my.cnf + +- name: setup initial cleanup script + template: + mode: 0600 + src: root/secure_install.sql.j2 + dest: /root/secure_install.sql + +- name: run initial cleanup + shell: | + mysql + --defaults-extra-file /root/mdb_local.cnf + --no-auto-rehash + < /root/secure_install.sql + when: mdb_install.changed + +- name: remove all anonymous user accounts + mysql_user: + name: "" + host_all: yes + state: absent + login_unix_socket: "{{ mariadb_local_sock }}" + +- name: Setup databases based on mariadb conf + include_tasks: setup-db.yml + loop: "{{ db_configs | json_query('[?type==`mariadb`]') }}" + loop_control: + loop_var: db + label: "{% if 'dbname' in db %}{{ db.dbname }}{% elif 'dbuser' in db %}{{ db.dbuser }}{% else %}::pass_redacted::{% endif %}" \ No newline at end of file diff --git a/roles/mariadb/tasks/setup-db.yml b/roles/mariadb/tasks/setup-db.yml new file mode 100755 index 0000000..b21037e --- /dev/null +++ b/roles/mariadb/tasks/setup-db.yml @@ -0,0 +1,35 @@ +--- +- name: set DB related config options + set_fact: + local_db_name: "{{ db.name if 'name' in db else 'noentry' }}" + local_db_user: "{{ db.user if 'user' in db else 'noentry' }}" + local_db_pass: "{{ db.pass if 'pass' in db else 'noentry' }}" + local_db_user_prives: "{{ db.priv if 'priv' in db else 'ALL' }}" + +- name: "create {{ local_db_user }} user" + mysql_user: + name: "{{ local_db_user }}" + password: "{{ local_db_pass }}" + state: present + login_unix_socket: "{{ mariadb_local_sock }}" + when: + - local_db_user != 'noentry' + - local_db_pass != 'noentry' + +- name: "setup {{ local_db_name }} database" + mysql_db: + name: "{{ local_db_name }}" + state: present + login_unix_socket: "{{ mariadb_local_sock }}" + when: + - local_db_name != 'noentry' + +- name: "Grant privs '{{ local_db_user_prives }}' for user '{{ local_db_user }}' to database '{{ local_db_name }}'" + mysql_user: + append_privs: yes + name: "{{ local_db_user }}" + priv: "{{local_db_name}}.*:{{ local_db_user_prives }}" + login_unix_socket: "{{ mariadb_local_sock }}" + when: + - local_db_user != 'noentry' + - local_db_name != 'noentry' diff --git a/roles/mariadb/templates/root/.my.cnf.j2 b/roles/mariadb/templates/root/.my.cnf.j2 new file mode 100755 index 0000000..d7ce915 --- /dev/null +++ b/roles/mariadb/templates/root/.my.cnf.j2 @@ -0,0 +1,3 @@ +[client] +user={{ mariadb_root_user }} +password={{ mariadb_root_pass }} diff --git a/roles/mariadb/templates/root/secure_install.sql.j2 b/roles/mariadb/templates/root/secure_install.sql.j2 new file mode 100755 index 0000000..6ba1ade --- /dev/null +++ b/roles/mariadb/templates/root/secure_install.sql.j2 @@ -0,0 +1,4 @@ +DELETE FROM mysql.user WHERE User='{{ mariadb_root_user }}' AND Host NOT IN ('localhost', '127.0.0.1', '::1'); +DROP DATABASE IF EXISTS test; +DELETE FROM mysql.db WHERE Db='test' OR Db='test_%' +FLUSH PRIVILEGES; \ No newline at end of file diff --git a/roles/mariadb/vars/main.yml b/roles/mariadb/vars/main.yml new file mode 100755 index 0000000..7e12e18 --- /dev/null +++ b/roles/mariadb/vars/main.yml @@ -0,0 +1,2 @@ +--- +mariadb_root_user: "root" \ No newline at end of file diff --git a/roles/nextcloud/LICENSE b/roles/nextcloud/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/nextcloud/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/nextcloud/README.md b/roles/nextcloud/README.md new file mode 100755 index 0000000..4de85a6 --- /dev/null +++ b/roles/nextcloud/README.md @@ -0,0 +1,3 @@ +# cloud-nextcloud + +Ansible role to provide nextcloud \ No newline at end of file diff --git a/roles/nextcloud/defaults/main.yml b/roles/nextcloud/defaults/main.yml new file mode 100755 index 0000000..e78f8af --- /dev/null +++ b/roles/nextcloud/defaults/main.yml @@ -0,0 +1,59 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +nextcloud_version: 23.0.1 + +www_group: www-data + +ncloud_db: + type: pgsql + name: ncloud_db + user: ncloud_dbu + pass: ncloud_dbpw + +ncloud_admin_user: "nc-admin" +ncloud_admin_pass: "nc-password" +ncloud_data_location: "{{ cloud_storage }}/nextcloud" +ncloud_npush_port: 7867 + +ncloud_domain: + - my_domain.tld +ncloud_config: + - key: default_phone_region + value: "'DE'" + + +redis_port: 6379 +redis_remote_url: "" + + +ncloud_coturn_pass: SomeRandomString +ncloud_coturn_port: 5349 +ncloud_coturn_parallel_connection: 0 #0 is unlimited +ncloud_coturn_bandwitdh: 0 #0 B/s is unlimited +ncloud_coturn_session_lifetime: 600 + +ncloud_coturn_cert: "/etc/letsencrypt/live/{{ ncloud_domain[0] }}/fullchain.pem" +ncloud_coturn_pkey: "/etc/letsencrypt/live/{{ ncloud_domain[0] }}/privkey.pem" +ncloud_coturn_dhparam: "/etc/letsencrypt/ssl-dhparams.pem" + +coturn_configs: + - "tls-listening-port={{ ncloud_coturn_port }}" + - fingerprint + - use-auth-secret + - "static-auth-secret={{ ncloud_coturn_pass }}" + - "realm={{ ncloud_domain[0] }}" + - "total-quota={{ ncloud_coturn_parallel_connection }}" + - "bps-capacity={{ ncloud_coturn_bandwitdh }}" + - "stale-nonce={{ ncloud_coturn_session_lifetime }}" + - cipher-list=\“ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384\″ + - no-multicast-peers + - no-tlsv1 + - no-tlsv1_1 + - no-stdout-log + - "cert={{ ncloud_coturn_cert }}" + - "pkey={{ ncloud_coturn_pkey }}" + - "dh-file={{ ncloud_coturn_dhparam }}" \ No newline at end of file diff --git a/roles/nextcloud/handlers/main.yml b/roles/nextcloud/handlers/main.yml new file mode 100755 index 0000000..a52cb0a --- /dev/null +++ b/roles/nextcloud/handlers/main.yml @@ -0,0 +1,23 @@ +--- +- name: restart redis + systemd: + name: redis + state: restarted + +- name: setup notify_push + command: + chdir: "{{ ncloud_dir }}" + cmd: "php occ notify_push:setup https://{{ ncloud_domain[0] }}/push" + become: yes + become_user: "{{ www_group }}" + +- name: restart notify_push + systemd: + name: notify_push + state: restarted + daemon_reload: yes + +- name: restart coturn + systemd: + name: coturn + state: restarted \ No newline at end of file diff --git a/roles/nextcloud/meta/.galaxy_install_info b/roles/nextcloud/meta/.galaxy_install_info new file mode 100755 index 0000000..292e63c --- /dev/null +++ b/roles/nextcloud/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:11 ' +version: '' diff --git a/roles/nextcloud/meta/main.yml b/roles/nextcloud/meta/main.yml new file mode 100755 index 0000000..9231502 --- /dev/null +++ b/roles/nextcloud/meta/main.yml @@ -0,0 +1,19 @@ +--- +galaxy_info: + role_name: nextcloud + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup nextcloud; shoudld be used in combination with an DB-, HTTP-Server and letsencrypt. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - nextcloud +dependencies: + - postgres + - php + - nginx diff --git a/roles/nextcloud/tasks/coturn.yml b/roles/nextcloud/tasks/coturn.yml new file mode 100755 index 0000000..eb4d732 --- /dev/null +++ b/roles/nextcloud/tasks/coturn.yml @@ -0,0 +1,32 @@ +--- +- name: install coturn server + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - "coturn" + +- name: enable coturn server + lineinfile: + path: "/etc/default/coturn" + regexp: '^#?TURNSERVER_ENABLED=' + line: "TURNSERVER_ENABLED=1" + notify: restart coturn + +- name: enable coturn server + lineinfile: + path: "/etc/turnserver.conf" + regexp: '^#?{{ configline.split("=")[0] }}{% if configline.split("=") | length > 1 %}={% endif %}' + line: "{{ configline }}" + loop: "{{ coturn_configs }}" + loop_control: + loop_var: configline + label: "{{ configline }}" + notify: restart coturn + +- name: set coturn capabilities for port setting + capabilities: + path: /usr/bin/turnserver + capability: cap_net_bind_service=+ep + state: present diff --git a/roles/nextcloud/tasks/file_hpb.yml b/roles/nextcloud/tasks/file_hpb.yml new file mode 100755 index 0000000..e4db521 --- /dev/null +++ b/roles/nextcloud/tasks/file_hpb.yml @@ -0,0 +1,44 @@ +--- +- name: install notify_push + command: + chdir: "{{ ncloud_dir }}" + creates: "{{ ncloud_dir }}/apps/notify_push" + cmd: php occ app:install notify_push + become: yes + become_user: "{{ www_group }}" + notify: setup notify_push + register: notify_push + +- name: enable notify_push + command: + chdir: "{{ ncloud_dir }}" + creates: "{{ ncloud_dir }}/apps/notify_push" + cmd: php occ app:enable notify_push + become: yes + become_user: "{{ www_group }}" + when: notify_push.changed + +- name: configure trusted proxies + blockinfile: + marker: "// {mark} ANSIBLE MANAGED BLOCK" + path: "{{ ncloud_dir }}/config/config.php" + block: | + 'trusted_proxies' => + array ( + {% for proxy in ncloud_notify_trusted_proxies %} + {{ loop.index - 1}} => '{{ proxy }}', + {% endfor %} + ), + insertbefore: "\\);" + +- name: setup notify_push systemd unit + template: + src: etc/systemd/system/notify_push.service.j2 + dest: /etc/systemd/system/notify_push.service + +- name: enable notify_push systemd unit + systemd: + name: notify_push + enabled: yes + daemon_reload: yes + state: started \ No newline at end of file diff --git a/roles/nextcloud/tasks/main.yml b/roles/nextcloud/tasks/main.yml new file mode 100755 index 0000000..fd43cd9 --- /dev/null +++ b/roles/nextcloud/tasks/main.yml @@ -0,0 +1,46 @@ +--- +- name: setup nextcloud group + group: + name: "{{ ncgrp }}" + state: "present" + +- name: setup nextcloud user + user: + name: "{{ ncusr }}" + group: "{{ ncgrp }}" + groups: + - "{{ ncgrp }}" + comment: Virtual Nextcloud User + shell: /sbin/nologin + state: present + +- name: setup nextcloud storage folder + file: + path: "{{ ncloud_data_location }}" + state: directory + owner: "{{ www_group }}" + group: "{{ ncgrp }}" + mode: "o=" + + +- name: install nextcloud recommendations + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - "libxml2" + - "openssl" + - "ffmpeg" + - "libreoffice" + - "redis-server" + - "unzip" + +- name: install basic nextcloud + include_tasks: nextcloud.yml + +- name: install notify_push (file high-performance-backend) + include_tasks: file_hpb.yml + +- name: install coturn server + include_tasks: coturn.yml diff --git a/roles/nextcloud/tasks/nextcloud.yml b/roles/nextcloud/tasks/nextcloud.yml new file mode 100755 index 0000000..1479fc0 --- /dev/null +++ b/roles/nextcloud/tasks/nextcloud.yml @@ -0,0 +1,111 @@ +--- +- name: download nextcloud and unarchive + unarchive: + src: "https://download.nextcloud.com/server/releases/nextcloud-{{ nextcloud_version }}.zip" + dest: "{{ cloud_apps }}" + remote_src: yes + owner: "{{ www_group }}" + group: "{{ ncgrp }}" + mode: "o=" + creates: "{{ ncloud_dir }}" + +- name: install nextcloud + command: + chdir: "{{ ncloud_dir }}" + creates: "{{ ncloud_data_location }}/index.html" + cmd: > + php occ maintenance:install + --database '{{ ncloud_db.type }}' + --database-name '{{ ncloud_db.name }}' + --database-user '{{ ncloud_db.user }}' + --database-pass '{{ ncloud_db.pass }}' + --admin-user '{{ ncloud_admin_user }}' + --admin-pass '{{ ncloud_admin_pass }}' + --data-dir '{{ ncloud_data_location }}' + become: yes + become_user: "{{ www_group }}" + register: ncloud_installation + +- name: set nextcloud folder permission + file: + path: "{{ ncloud_data_location }}" + owner: "{{ www_group }}" + group: "{{ ncusr }}" + mode: "o=" + recurse: yes + +- name: configure php cron jobs + cron: + name: nextcloud-php + minute: "*/5" + job: "php -f {{ ncloud_dir }}/cron.php" + user: "{{ www_group }}" + +- name: configure local memory caching APCu + lineinfile: + path: "{{ ncloud_dir }}/config/config.php" + regexp: "\\s*'memcache.local' => " + line: " 'memcache.local' => '\\OC\\Memcache\\APCu'," + insertbefore: "\\);" + when: ncloud_installation.changed + +- name: configure redis conig + lineinfile: + path: "/etc/redis/redis.conf" + regexp: "(# |){{ item.key }} " + line: "{{ item.key }} {{ item.value }}" + loop: + - key: unixsocket + value: "{{ redis_socket }}" + - key: unixsocketperm + value: 775 + - key: bind + value: 127.0.0.1 + - key: daemonize + value: !unsafe yes + - key: stop-writes-on-bgsave-error + value: !unsafe no + - key: rdbcompression + value: !unsafe yes + - key: maxmemory + value: 50M + - key: maxmemory-policy + value: allkeys-lru + when: redis_remote_url | length == 0 + notify: restart redis + +- name: configure distributed memory caching APCu + lineinfile: + path: "{{ ncloud_dir }}/config/config.php" + regexp: "\\s*'{{ item.key }}' => " + line: " '{{ item.key }}' => {{ item.value }}," + insertbefore: "\\);" + loop: + - key: memcache.locking + value: "'\\OC\\Memcache\\Redis'" +# - key: memcache.distributed +# value: "'\\OC\\Memcache\\Redis'" +# - key: redis +# value: "['host' => '{% if redis_remote_url | length > 0 %}{{ redis_remote_url }}{% else %}{{ redis_socket }}{% endif %}','port' => {% if redis_remote_url | length > 0 %}{{ redis_port }}{% else %}0{% endif %}]" + loop_control: + label: "{{ item.key }}" + when: ncloud_installation.changed + +- name: configure other settings + lineinfile: + path: "{{ ncloud_dir }}/config/config.php" + regexp: "\\s*'{{ item.key }}' => " + line: " '{{ item.key }}' => {{ item.value }}," + insertbefore: "\\);" + loop: "{{ ncloud_config }}" + loop_control: + label: "{{ item.key }}" + when: ncloud_installation.changed + + +- name: configure nextcloud domain + replace: + path: "{{ ncloud_dir }}/config/config.php" + regexp: "^\\s*'trusted_domains'\\s*=>\\s*array\\s*\\((.*\\n)+\\s*\\),$" + replace: " 'trusted_domains' => \\n array ({% for domain in ncloud_domain %}\\n {{ loop.index - 1}} => '{{ domain }}',{% endfor %}\\n )," + when: ncloud_installation.changed \ No newline at end of file diff --git a/roles/nextcloud/templates/etc/systemd/system/notify_push.service.j2 b/roles/nextcloud/templates/etc/systemd/system/notify_push.service.j2 new file mode 100755 index 0000000..732c138 --- /dev/null +++ b/roles/nextcloud/templates/etc/systemd/system/notify_push.service.j2 @@ -0,0 +1,12 @@ +[Unit] +Description=Push daemon for Nextcloud clients +Documentation=https://github.com/nextcloud/notify_push + +[Service] +Environment=PORT={{ ncloud_npush_port }} +Environment=NEXTCLOUD_URL=https://{{ ncloud_domain[0] }} +ExecStart={{ ncloud_dir }}/apps/notify_push/bin/x86_64/notify_push {{ ncloud_dir }}/config/config.php +User={{ www_group }} + +[Install] +WantedBy = multi-user.target diff --git a/roles/nextcloud/vars/main.yml b/roles/nextcloud/vars/main.yml new file mode 100755 index 0000000..87b003d --- /dev/null +++ b/roles/nextcloud/vars/main.yml @@ -0,0 +1,13 @@ +--- +ncusr: "ncloud" +ncgrp: "{{ ncusr }}" + +ncloud_dir: "{{ cloud_apps }}/nextcloud" + +redis_socket: "/var/run/redis/redis-server.sock" + +ncloud_notify_trusted_proxies: + - "127.0.0.1" + - "::1" + - "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}" + - "{{ hostvars[inventory_hostname]['ansible_default_ipv6']['address'] }}" \ No newline at end of file diff --git a/roles/nexus/LICENSE b/roles/nexus/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/nexus/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/nexus/README.md b/roles/nexus/README.md new file mode 100755 index 0000000..860136e --- /dev/null +++ b/roles/nexus/README.md @@ -0,0 +1,3 @@ +# cloud-nexus + +Ansible role to provide Sonatype Nexus as artifact storage. \ No newline at end of file diff --git a/roles/nexus/defaults/main.yml b/roles/nexus/defaults/main.yml new file mode 100755 index 0000000..bfe034a --- /dev/null +++ b/roles/nexus/defaults/main.yml @@ -0,0 +1,16 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +java_home: "{{ cloud_apps }}/java-jdk/java" + +nexus_source: https://download.sonatype.com/nexus/3 +nexus_version: 3.37.3-02 + +nexus_java_home: "{{ java_home }}" + +nexus_data_location: "{{ cloud_storage }}/sonatype-nexus-data" +nexus_http_port: 8081 +nexus_http_host: 0.0.0.0 diff --git a/roles/nexus/handlers/main.yml b/roles/nexus/handlers/main.yml new file mode 100755 index 0000000..d83807b --- /dev/null +++ b/roles/nexus/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: restart nexus + systemd: + name: nexus + state: restarted + diff --git a/roles/nexus/meta/.galaxy_install_info b/roles/nexus/meta/.galaxy_install_info new file mode 100755 index 0000000..26b6545 --- /dev/null +++ b/roles/nexus/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:12 ' +version: '' diff --git a/roles/nexus/meta/main.yml b/roles/nexus/meta/main.yml new file mode 100755 index 0000000..fcc562a --- /dev/null +++ b/roles/nexus/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: nexus + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup Sonatype Nexus Repository 3.X + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - nexus +dependencies: + - java diff --git a/roles/nexus/tasks/main.yml b/roles/nexus/tasks/main.yml new file mode 100755 index 0000000..c607479 --- /dev/null +++ b/roles/nexus/tasks/main.yml @@ -0,0 +1,99 @@ +--- +- name: setup nexus group + group: + name: "{{ nexus_usr }}" + state: "present" + +- name: setup nexus user + user: + name: "{{ nexus_usr }}" + group: "{{ nexus_grp }}" + groups: + - "{{ nexus_grp }}" + comment: Virtual Sonytype Nesus User + shell: /bin/bash + state: present + system: yes + +- name: setup nexus directories + file: + state: directory + path: "{{ item }}" + owner: "{{ nexus_usr }}" + group: "{{ nexus_grp }}" + mode: 0750 + loop: + - "{{ nexus_home }}" + - "{{ nesux_dir }}" + +- name: download nexus + unarchive: + src: "{{ nexus_source }}/nexus-{{ nexus_version }}-unix.tar.gz" + dest: "{{ nesux_dir }}" + remote_src: yes + owner: "{{ nexus_usr }}" + group: "{{ nexus_grp }}" + mode: 0750 + creates: "{{ nexus_inst }}" + register: nexus_install + +- block: + - name: move nexus home appropriately + copy: + src: "{{ nesux_dir }}/sonatype-work/nexus3" + dest: "{{ nexus_home }}" + owner: "{{ nexus_usr }}" + group: "{{ nexus_grp }}" + mode: 0750 + - name: remove old home directory + file: + path: "{{ nesux_dir }}/sonatype-work" + state: absent + when: nexus_install.changed + +- name: link installed nexus homedir + file: + state: link + src: "{{ nexus_inst }}" + dest: "{{ nexus_link }}" + owner: "{{ nexus_usr }}" + group: "{{ nexus_grp }}" + +- name: setup nexus config directory + file: + state: directory + path: "{{ nexus_home }}/nexus3/etc" + owner: "{{ nexus_usr }}" + group: "{{ nexus_grp }}" + mode: 0750 + +- name: setup nexus vmoptions + template: + src: opt/sonatype-nexus/nexus/bin/nexus.vmoptions.j2 + dest: "{{nexus_inst }}/bin/nexus.vmoptions" + owner: "{{ nexus_usr }}" + group: "{{ nexus_grp }}" + mode: 0750 + notify: restart nexus + +- name: setup nexus properties + template: + src: opt/sonatype-nexus-data/nexus-config/nexus.properties.j2 + dest: "{{ nexus_home }}/nexus3/etc/nexus.properties" + owner: "{{ nexus_usr }}" + group: "{{ nexus_grp }}" + mode: 0750 + notify: restart nexus + +- name: setup nexus systemd unit + template: + src: etc/systemd/system/nexus.service.j2 + dest: /etc/systemd/system/nexus.service + notify: restart nexus + +- name: enable nexus systemd unit + systemd: + name: nexus + enabled: yes + daemon_reload: yes + state: started diff --git a/roles/nexus/templates/etc/systemd/system/nexus.service.j2 b/roles/nexus/templates/etc/systemd/system/nexus.service.j2 new file mode 100755 index 0000000..7ea689e --- /dev/null +++ b/roles/nexus/templates/etc/systemd/system/nexus.service.j2 @@ -0,0 +1,16 @@ +[Unit] +Description=nexus service +After=network.target + +[Service] +Type=forking +LimitNOFILE=65536 +Environment="JAVA_HOME={{ nexus_java_home }}" +ExecStart={{ nexus_link }}/bin/nexus start +ExecStop={{ nexus_link }}/bin/nexus stop +User={{ nexus_usr }} +Restart=on-abort +TimeoutSec=600 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/nexus/templates/opt/sonatype-nexus-data/nexus-config/nexus.properties.j2 b/roles/nexus/templates/opt/sonatype-nexus-data/nexus-config/nexus.properties.j2 new file mode 100755 index 0000000..6c998ce --- /dev/null +++ b/roles/nexus/templates/opt/sonatype-nexus-data/nexus-config/nexus.properties.j2 @@ -0,0 +1,12 @@ +# Jetty section +application-port={{ nexus_http_port }} +application-host={{ nexus_http_host }} +# nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-http.xml,${jetty.etc}/jetty-requestlog.xml +nexus-context-path=/ + +# Nexus section +# nexus-edition=nexus-pro-edition +# nexus-features=\ +# nexus-pro-feature + +# nexus.hazelcast.discovery.isEnabled=true diff --git a/roles/nexus/templates/opt/sonatype-nexus/nexus/bin/nexus.vmoptions.j2 b/roles/nexus/templates/opt/sonatype-nexus/nexus/bin/nexus.vmoptions.j2 new file mode 100755 index 0000000..8b704ad --- /dev/null +++ b/roles/nexus/templates/opt/sonatype-nexus/nexus/bin/nexus.vmoptions.j2 @@ -0,0 +1,40 @@ +-Xms{{ nexus_javaheap_min | default('2703m') }} +-Xmx{{ nexus_javaheap_max | default('2703m') }} +-XX:MaxDirectMemorySize={{ nexus_javaheap_directmax | default('2703m') }} +-XX:+UnlockDiagnosticVMOptions +-XX:+LogVMOutput +-XX:LogFile={{ nexus_home }}/nexus3/log/jvm.log +-XX:-OmitStackTraceInFastThrow +-Djava.net.preferIPv4Stack=true +-Dkaraf.home=. +-Dkaraf.base=. +-Dkaraf.etc=etc/karaf +-Djava.util.logging.config.file=etc/karaf/java.util.logging.properties +-Dkaraf.data={{ nexus_home }}/nexus3 +-Dkaraf.log={{ nexus_home }}/nexus3/log +-Djava.io.tmpdir={{ nexus_home }}/nexus3/tmp +-Dkaraf.startLocalConsole=false +-Djdk.tls.ephemeralDHKeySize=2048 +# +# additional vmoptions needed for Java9+ +# +# --add-reads=java.xml=java.logging +# --add-exports=java.base/org.apache.karaf.specs.locator=java.xml,ALL-UNNAMED +# --patch-module java.base=${KARAF_HOME}/lib/endorsed/org.apache.karaf.specs.locator-4.3.2.jar +# --patch-module java.xml=${KARAF_HOME}/lib/endorsed/org.apache.karaf.specs.java.xml-4.3.2.jar +# --add-opens java.base/java.security=ALL-UNNAMED +# --add-opens java.base/java.net=ALL-UNNAMED +# --add-opens java.base/java.lang=ALL-UNNAMED +# --add-opens java.base/java.util=ALL-UNNAMED +# --add-opens java.naming/javax.naming.spi=ALL-UNNAMED +# --add-opens java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED +# --add-exports=java.base/sun.net.www.protocol.http=ALL-UNNAMED +# --add-exports=java.base/sun.net.www.protocol.https=ALL-UNNAMED +# --add-exports=java.base/sun.net.www.protocol.jar=ALL-UNNAMED +# --add-exports=jdk.xml.dom/org.w3c.dom.html=ALL-UNNAMED +# --add-exports=jdk.naming.rmi/com.sun.jndi.url.rmi=ALL-UNNAMED +# --add-exports java.security.sasl/com.sun.security.sasl=ALL-UNNAMED +# +# comment out this vmoption when using Java9+ +# +-Djava.endorsed.dirs=lib/endorsed \ No newline at end of file diff --git a/roles/nexus/vars/main.yml b/roles/nexus/vars/main.yml new file mode 100755 index 0000000..3c42d8e --- /dev/null +++ b/roles/nexus/vars/main.yml @@ -0,0 +1,8 @@ +--- +nexus_usr: sonanexus +nexus_grp: "{{ nexus_usr }}" + +nesux_dir: "{{ cloud_apps }}/sonatype-nexus" +nexus_inst: "{{ nesux_dir }}/nexus-{{ nexus_version }}" +nexus_home: "{{ nexus_data_location }}" +nexus_link: "{{ nesux_dir }}/nexus" \ No newline at end of file diff --git a/roles/nginx/LICENSE b/roles/nginx/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/nginx/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/nginx/README.md b/roles/nginx/README.md new file mode 100755 index 0000000..f04f6eb --- /dev/null +++ b/roles/nginx/README.md @@ -0,0 +1,3 @@ +# cloud-nginx + +Ansible role to provide nginx. \ No newline at end of file diff --git a/roles/nginx/defaults/main.yml b/roles/nginx/defaults/main.yml new file mode 100755 index 0000000..5936b46 --- /dev/null +++ b/roles/nginx/defaults/main.yml @@ -0,0 +1,45 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +www_root: /var/www +www_group: www-data + +web_sites: [] + # - domain: my-domain.tld + # filetag: file.my-domain.tld + # state: present + # stream: false + # owner: user + # port: 80 + # port_option: "" + # root: "{{ www_root }}/my-domain.tld" + # root_setup: True + # index: + # - index.html + # - index.htm + # - index.php + # locations: + # - location: "/" + # options: "try_files $uri $uri/ =404;" + # options: + # access_log: "/var/log/nginx/my-domain-access.log" + # option_key: "option_value" + # add_header: + # - Referrer-Policy \"no-referrer\" always + # - someother header + # pre_options: "" + # post_options: "" + +nginx_conf_http: [] +nginx_conf: [] + +nginx_worker_count: 768 +nginx_pid: /run/nginx.pid +nginx_log_path: /var/log/nginx +nginx_gzip: true + +letsencrypt_mail_address: admin@my-domain.tld + diff --git a/roles/nginx/handlers/main.yml b/roles/nginx/handlers/main.yml new file mode 100755 index 0000000..5cbe74d --- /dev/null +++ b/roles/nginx/handlers/main.yml @@ -0,0 +1,11 @@ +--- +- name: reload nginx + systemd: + name: nginx + state: reloaded + +- name: restart nginx + systemd: + name: nginx + state: restarted + daemon_reload: yes diff --git a/roles/nginx/meta/.galaxy_install_info b/roles/nginx/meta/.galaxy_install_info new file mode 100755 index 0000000..26b6545 --- /dev/null +++ b/roles/nginx/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:12 ' +version: '' diff --git a/roles/nginx/meta/main.yml b/roles/nginx/meta/main.yml new file mode 100755 index 0000000..8e7d423 --- /dev/null +++ b/roles/nginx/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: nginx + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup an nginx server serving for Mail, WordPress, Redmine, and other services. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - nginx +dependencies: [] diff --git a/roles/nginx/tasks/letsencrypt.yml b/roles/nginx/tasks/letsencrypt.yml new file mode 100755 index 0000000..cd61137 --- /dev/null +++ b/roles/nginx/tasks/letsencrypt.yml @@ -0,0 +1,42 @@ +--- +- name: install python3 certbot and recommendations + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - python3-certbot + - python3-certbot-nginx + register: certbot_nginx_installation + +- name: create letsencrypt account + command: + cmd: | + certbot register + -m {{ letsencrypt_mail_address }} + --agree-tos + --noninteractive + --redirect + when: certbot_nginx_installation.changed + +- name: create let's-encrypt certificates for web web_sites + command: + cmd: | + certbot + --nginx + -d {% if cert_domain is not string and cert_domain is iterable %}{{ cert_domain | join(',') }}{% else %}{{ cert_domain }}{% endif %} + -m {{ letsencrypt_mail_address }} + --agree-tos + --noninteractive + --redirect + creates: "/etc/letsencrypt/live/{% if cert_domain is not string and cert_domain is iterable %}{{ cert_domain[0] }}{% else %}{{ cert_domain }}{% endif %}" + loop: "{{ web_sites | json_query('[?letsencrypt==`true`&&state==`present`].domain') }}" + loop_control: + loop_var: cert_domain + label: "{% if cert_domain is not string and cert_domain is iterable %}{{ cert_domain | join(',') }}{% else %}{{ cert_domain }}{% endif %}" + +- name: Setup cronjob for auto renewal + cron: + name: letsencrypt + special_time: daily + job: /usr/bin/certbot -q renew diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml new file mode 100755 index 0000000..0e4bf77 --- /dev/null +++ b/roles/nginx/tasks/main.yml @@ -0,0 +1,141 @@ +--- +- name: install nginx and recommendations + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - "nginx-full" + +- name: setup nginx configuration file + template: + src: etc/nginx/nginx.conf.j2 + dest: "{{ nginx_root_dir }}/nginx.conf" + +- name: setup nginx non-default folders + file: + state: directory + path: "{{ nginx_root_dir }}/{{ item }}" + loop: + - streams-available + - streams-enabled + +- name: setup document roots folder for nginx + file: + state: directory + path: "{{ website.root | default(www_root + '/' + website.domain) }}" + owner: "{{ website.owner | default(www_group) }}" + group: "{{ www_group }}" + when: website.root_setup | default(false) | bool + loop: "{{ web_sites }}" + loop_control: + loop_var: website + label: "{{ website.domain }}" + register: setup_doc_roots + +- name: setup default index.html if not exists + template: + src: index.html.j2 + dest: "{{ website.root | default(www_root + '/' + website.domain) }}/index.html" + force: no + owner: "{{ website.owner | default(www_group) }}" + group: "{{ www_group }}" + loop: "{{ setup_doc_roots.results | json_query('[?changed==`true`].website') }}" + loop_control: + loop_var: website + label: "{{ website.domain }}" + +- name: setup sites-available virtual host config + template: + src: etc/nginx/sites-available/website.j2 + dest: "{{ nginx_root_dir }}/sites-available/{{ website.filetag | default(website.domain) }}" + force: "{% if website.letsencrypt is defined and website.letsencrypt %}no{% else %}yes{% endif %}" + loop: "{{ web_sites | json_query('[?state==`present`&&(!stream||stream==`false`)]') }}" + loop_control: + loop_var: website + label: "{{ website.filetag | default(website.domain) }}" + +- name: setup streams-available virtual host config + template: + src: etc/nginx/streams-available/stream.j2 + dest: "{{ nginx_root_dir }}/streams-available/{{ stream.filetag | default(stream.domain) }}" + force: "{% if stream.letsencrypt is defined and stream.letsencrypt %}no{% else %}yes{% endif %}" + loop: "{{ web_sites | json_query('[?state==`present`&&stream==`true`]') }}" + loop_control: + loop_var: stream + label: "{{ stream.filetag | default(stream.domain) }}" + +- name: configure *-enabled file site links for virtual hosts + file: + state: "link" + src: "{{ nginx_root_dir }}/sites-available/{{ item.filetag | default(item.domain) }}" + dest: "{{ nginx_root_dir }}/sites-enabled/{{ item.filetag | default(item.domain) }}" + loop: "{{ web_sites | json_query('[?state==`present`&&(!stream||stream==`false`)]') }}" + loop_control: + label: "{{ item.filetag | default(item.domain) }}" + notify: reload nginx + +- name: configure *-enabled file stream links for virtual hosts + file: + state: "link" + src: "{{ nginx_root_dir }}/streams-available/{{ stream.filetag | default(stream.domain) }}" + dest: "{{ nginx_root_dir }}/streams-enabled/{{ stream.filetag | default(stream.domain) }}" + loop: "{{ web_sites | json_query('[?state==`present`&&stream==`true`]') }}" + loop_control: + loop_var: stream + label: "{{ stream.filetag | default(stream.domain) }}" + notify: reload nginx + +- name: remove links for inactive virtual host sites + file: + state: "absent" + path: "{{ nginx_root_dir }}/sites-enabled/{{ website.filetag | default(website.domain) }}" + loop: "{{ web_sites | json_query('[?state==`absent`&&(!stream||stream==`false`)]') }}" + loop_control: + loop_var: website + label: "{{ website.filetag | default(website.domain) }}" + notify: reload nginx + +- name: remove links for inactive virtual host streams + file: + state: "absent" + path: "{{ nginx_root_dir }}/streams-enabled/{{ stream.filetag | default(stream.domain) }}" + loop: "{{ web_sites | json_query('[?state==`absent`&&stream==`true`]') }}" + loop_control: + loop_var: stream + label: "{{ stream.filetag | default(stream.domain) }}" + notify: reload nginx + +- name: run letsencrypt certificate generation + import_tasks: letsencrypt.yml + +- name: setup sites-available virtual ssl host config + template: + src: etc/nginx/sites-available/website.j2 + dest: "{{ nginx_root_dir }}/sites-available/{{ website.filetag | default(website.domain) }}" + loop: "{{ web_sites | json_query('[?letsencrypt==`true`&&state==`present`&&(!stream||stream==`false`)]') }}" + loop_control: + loop_var: website + label: "{{ website.filetag | default(website.domain) }}" + vars: + use_ssl: true + notify: reload nginx + +- name: setup streams-available virtual ssl host config + template: + src: etc/nginx/streams-available/stream.j2 + dest: "{{ nginx_root_dir }}/streams-available/{{ stream.filetag | default(stream.domain) }}" + loop: "{{ web_sites | json_query('[?letsencrypt==`true`&&state==`present`&&stream==`true`]') }}" + loop_control: + loop_var: stream + label: "{{ stream.filetag | default(stream.domain) }}" + vars: + use_ssl: true + notify: reload nginx + +- name: enable nginx systemd unit + systemd: + name: nginx + enabled: yes + daemon_reload: yes + state: started diff --git a/roles/nginx/templates/etc/nginx/nginx.conf.j2 b/roles/nginx/templates/etc/nginx/nginx.conf.j2 new file mode 100755 index 0000000..bb3254d --- /dev/null +++ b/roles/nginx/templates/etc/nginx/nginx.conf.j2 @@ -0,0 +1,96 @@ +user {{ www_group }}; +worker_processes auto; +pid {{ nginx_pid }}; +include /etc/nginx/modules-enabled/*.conf; + +events { + worker_connections {{ nginx_worker_count }}; + # multi_accept on; +} + +http { + + ## + # Basic Settings + ## + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + # server_tokens off; + + # server_names_hash_bucket_size 64; + # server_name_in_redirect off; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ## + # SSL Settings + ## + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE + ssl_prefer_server_ciphers on; + + ## + # Logging Settings + ## + + access_log {{ nginx_log_path }}/access.log; + error_log {{ nginx_log_path }}/error.log; + + ## + # Gzip Settings + ## + + gzip {{ 'on' if nginx_gzip else 'off' }}; + + # gzip_vary on; + # gzip_proxied any; + # gzip_comp_level 6; + # gzip_buffers 16 8k; + # gzip_http_version 1.1; + # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + ## + # Virtual Host Configs + ## + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; + + ### Ansible included configs ### +{% for http_conf in nginx_conf_http %} + {{ http_conf | indent(width=4, indentfirst=False) }} +{% endfor %} +} +#mail { +# # See sample authentication script at: +# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript +# +# # auth_http localhost/auth.php; +# # pop3_capabilities "TOP" "USER"; +# # imap_capabilities "IMAP4rev1" "UIDPLUS"; +# +# server { +# listen localhost:110; +# protocol pop3; +# proxy on; +# } +# +# server { +# listen localhost:143; +# protocol imap; +# proxy on; +# } +#} + +stream { + include /etc/nginx/streams-enabled/*; +} + +{% for conf in nginx_conf %} +{{ conf }} +{% endfor %} \ No newline at end of file diff --git a/roles/nginx/templates/etc/nginx/sites-available/website.j2 b/roles/nginx/templates/etc/nginx/sites-available/website.j2 new file mode 100755 index 0000000..8e92acb --- /dev/null +++ b/roles/nginx/templates/etc/nginx/sites-available/website.j2 @@ -0,0 +1,74 @@ +{% if website.pre_options is defined %} +{{ website.pre_options }} +{% endif %} +server { +{% if website.domain is iterable and (website.domain is not mapping and website.domain is not string) %} + server_name {{ website.domain | join(' ') }}; + +{% else %} + server_name {{ website.domain }}; +{% endif %} +{% if website.root is not defined %} + root {{ www_root }}/{{ website.domain }}/{{ website.subroot | default('') }}; +{% elif website.root != 'noroot' %} + root {{ website.root }}; +{% endif %} +{% if website.index is not defined %} + index index.html index.php index.htm; +{% elif website.index != 'noindex' %} + index {{ website.index | join(' ') }}; +{% endif %} + +{% if website.options is defined %} +{% for key in website.options.keys() %} + {{ key }} {{ website.options[key] }}; +{% endfor %} +{% endif %} + +{% if website.add_header is defined %} +{% for header in website.add_header %} + add_header {{ header }}; +{% endfor %} +{% endif %} + +{% for www_location in website.locations | default(default_locations) %} + location {{ www_location.location | default('/') }} { + {{ www_location.options | default('try_files $uri $uri/ =404;') | indent(width=16, first=False) }} + } +{% endfor %} + + listen {% if use_ssl is defined and use_ssl %}443 ssl{% else %}{{ website.port | default(80) }}{% endif %}{% if website.port_option is defined and website.port_option != '' %}{{ website.port_option }}{% endif %}; + listen [::]:{% if use_ssl is defined and use_ssl %}443 ssl{% else %}{{ website.port | default(80) }}{% endif %}{% if website.port_option is defined and website.port_option != '' %}{{ website.port_option }}{% endif %}; +{% if use_ssl is defined and use_ssl %} + ssl_certificate /etc/letsencrypt/live/{% if website.domain is not string and website.domain is iterable %}{{ website.domain[0] }}{% else %}{{ website.domain }}{% endif %}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{% if website.domain is not string and website.domain is iterable %}{{ website.domain[0] }}{% else %}{{ website.domain }}{% endif %}/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; +{% endif %} +} +{% if use_ssl is defined and use_ssl %} +server { +{% if website.domain is iterable and website.domain is not string %} +{% for domain in website.domain %} + if ($host = {{ domain }}) { + return 301 https://$host$request_uri; + } +{% endfor %} +{% else %} + if ($host = {{ website.domain }}) { + return 301 https://$host$request_uri; + } +{% endif %} + listen {{ website.port | default(80) }}; + listen [::]:{{ website.port | default(80) }}; +{% if website.domain is iterable and (website.domain is not mapping and website.domain is not string) %} + server_name {{ website.domain | join(' ') }}; +{% else %} + server_name {{ website.domain }}; +{% endif %} + return 404; +} +{% endif %} +{% if website.post_options is defined %} +{{ website.post_options }} +{% endif %} diff --git a/roles/nginx/templates/etc/nginx/streams-available/stream.j2 b/roles/nginx/templates/etc/nginx/streams-available/stream.j2 new file mode 100755 index 0000000..22b1e43 --- /dev/null +++ b/roles/nginx/templates/etc/nginx/streams-available/stream.j2 @@ -0,0 +1,19 @@ +{% if stream.pre_options is defined %} +{{ stream.pre_options }} +{% endif %} +server { + listen {{ stream.port | default(8080) }} {% if use_ssl is defined and use_ssl %}ssl{% endif %}{% if stream.port_option is defined and stream.port_option != '' %}{{ stream.port_option }}{% endif %}; + listen [::]:{{ stream.port | default(8080) }} {% if use_ssl is defined and use_ssl %}ssl{% endif %}{% if stream.port_option is defined and stream.port_option != '' %}{{ stream.port_option }}{% endif %}; + +{% if stream.options is defined %} +{% for key in stream.options.keys() %} + {{ key }} {{ stream.options[key] }}; +{% endfor %} +{% endif %} + +{% if use_ssl is defined and use_ssl %} + ssl_certificate /etc/letsencrypt/live/{% if stream.domain is not string and stream.domain is iterable %}{{ stream.domain[0] }}{% else %}{{ stream.domain }}{% endif %}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{% if stream.domain is not string and stream.domain is iterable %}{{ stream.domain[0] }}{% else %}{{ stream.domain }}{% endif %}/privkey.pem; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; +{% endif %} +} \ No newline at end of file diff --git a/roles/nginx/templates/index.html.j2 b/roles/nginx/templates/index.html.j2 new file mode 100755 index 0000000..8b1529e --- /dev/null +++ b/roles/nginx/templates/index.html.j2 @@ -0,0 +1,10 @@ + + + {{ website.domain }} + + +

Hello World!

+

This is the initial template for {{ website.domain }}, setup was successfull!

+

Farewell, my old friend.

+ + \ No newline at end of file diff --git a/roles/nginx/vars/main.yml b/roles/nginx/vars/main.yml new file mode 100755 index 0000000..ecb0726 --- /dev/null +++ b/roles/nginx/vars/main.yml @@ -0,0 +1,7 @@ +--- +nginx_root_dir: /etc/nginx +default_docroot: /var/www + +default_locations: + - location: "/" + options: "try_files $uri $uri/ =404;" \ No newline at end of file diff --git a/roles/openproject/LICENSE b/roles/openproject/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/openproject/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/openproject/README.md b/roles/openproject/README.md new file mode 100755 index 0000000..4b3f012 --- /dev/null +++ b/roles/openproject/README.md @@ -0,0 +1,3 @@ +# cloud-openproject + +Ansible role to provide openproject as project management software. \ No newline at end of file diff --git a/roles/openproject/defaults/main.yml b/roles/openproject/defaults/main.yml new file mode 100755 index 0000000..5151e7b --- /dev/null +++ b/roles/openproject/defaults/main.yml @@ -0,0 +1,25 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false +domain_external: my-domain.tld + +openproject_major: 12 + +openproject_db: + type: pgsql + name: openproject_db + user: openproject_dbu + pass: openproject_dbpw + port: 5432 + host: localhost + +openproject_mail_smtp_host: "mail.{{ domain_external }}" +openproject_mail_smtp_port: 25 +openproject_mail_smtp_user: "openproject@{{ domain_external }}" +openproject_mail_smtp_pass: ThisCouldBeYourAd! +openproject_mail_smtp_domain: "{{ openproject_mail_smtp_host }}" +openproject_mail_admin: "admin@{{ domain_external }}" + +openproject_domain: "localhost" diff --git a/roles/openproject/handlers/main.yml b/roles/openproject/handlers/main.yml new file mode 100755 index 0000000..69ebf12 --- /dev/null +++ b/roles/openproject/handlers/main.yml @@ -0,0 +1,3 @@ +--- +- name: install openproject + command: openproject configure diff --git a/roles/openproject/meta/.galaxy_install_info b/roles/openproject/meta/.galaxy_install_info new file mode 100755 index 0000000..26b6545 --- /dev/null +++ b/roles/openproject/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:12 ' +version: '' diff --git a/roles/openproject/meta/main.yml b/roles/openproject/meta/main.yml new file mode 100755 index 0000000..3836124 --- /dev/null +++ b/roles/openproject/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: openproject + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup OpenProject. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - openproject +dependencies: + - postgres diff --git a/roles/openproject/tasks/main.yml b/roles/openproject/tasks/main.yml new file mode 100755 index 0000000..27aa546 --- /dev/null +++ b/roles/openproject/tasks/main.yml @@ -0,0 +1,45 @@ +--- +- name: install requirements for openproject + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - apt-transport-https + - ca-certificates + - gnupg2 + - software-properties-common + - curl + +- name: install openproject repository key + apt_key: + url: https://dl.packager.io/srv/opf/openproject/key + state: present + + +- name: install openproject repository + get_url: + url: https://dl.packager.io/srv/opf/openproject/stable/{{ openproject_major }}/installer/{{ ansible_distribution | lower }}/{{ ansible_distribution_major_version }}.repo + dest: /etc/apt/sources.list.d/openproject.list + owner: root + group: root + mode: 644 + force: yes + +- name: install openproject + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: openproject + register: openproject_install + +- name: template installation file + template: + src: etc/openproject/installer.dat.j2 + dest: /etc/openproject/installer.dat + owner: openproject + group: openproject + mode: 0640 + notify: install openproject + when: openproject_install.changed \ No newline at end of file diff --git a/roles/openproject/templates/etc/openproject/installer.dat.j2 b/roles/openproject/templates/etc/openproject/installer.dat.j2 new file mode 100755 index 0000000..0a283e2 --- /dev/null +++ b/roles/openproject/templates/etc/openproject/installer.dat.j2 @@ -0,0 +1,22 @@ +openproject/edition default + +postgres/autoinstall reuse +postgres/db_host {{ openproject_db.host | default('localhost') }} +postgres/db_port {{ openproject_db.port | default('5432') }} +postgres/db_username {{ openproject_db.user }} +postgres/db_password {{ openproject_db.pass }} +postgres/db_name {{ openproject_db.name }} +server/autoinstall skip +smtp/autoinstall smtp +smtp/authentication login + +smtp/host {{ openproject_mail_smtp_host }} +smtp/port {{ openproject_mail_smtp_port }} +smtp/username {{ openproject_mail_smtp_user }} +smtp/password {{ openproject_mail_smtp_pass }} +smtp/domain {{ openproject_mail_smtp_domain }} +smtp/admin_email {{ openproject_mail_admin }} +memcached/autoinstall install + +server/hostname {{ openproject_domain }} +server/ssl no diff --git a/roles/openproject/vars/main.yml b/roles/openproject/vars/main.yml new file mode 100755 index 0000000..cd21505 --- /dev/null +++ b/roles/openproject/vars/main.yml @@ -0,0 +1,2 @@ +--- + diff --git a/roles/php/LICENSE b/roles/php/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/php/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/php/README.md b/roles/php/README.md new file mode 100755 index 0000000..1d26b74 --- /dev/null +++ b/roles/php/README.md @@ -0,0 +1,3 @@ +# cloud-php + +Ansible role to provide php, e.g. used for nginx or wordpress. \ No newline at end of file diff --git a/roles/php/defaults/main.yml b/roles/php/defaults/main.yml new file mode 100755 index 0000000..912887c --- /dev/null +++ b/roles/php/defaults/main.yml @@ -0,0 +1,46 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + + +php_version: 8.0 + +php_modules: [] + +php_memory_limit: 128M +php_upload_max_filesize: 2M +php_post_max_size: 8M + +php_opcache_interned_strings_buffer: 8 +php_opcache_save_comments: 1 +php_opcache_revalidate_freq: 2 +php_opcache_validate_timestamps: 1 + +# see: https://spot13.com/pmcalculator/ +php_pm_max_children: 102 +php_pm_start_servers: 25 +php_pm_min_spare_servers: 25 +php_pm_max_spare_servers: 76 + + +php_fpm_config: [] +# - key: upload_max_filesize +# value: 2M + +php_fpm_poolconf: [] +# - key: pm.max_children +# value: 102 + +php_fpm_poolconf_env: + - key: HOSTNAME + value: $HOSTNAME + - key: PATH + value: /usr/local/bin:/usr/bin:/bin + - key: TMP + value: /tmp + - key: TMPDIR + value: /tmp + - key: TEMP + value: /tmp \ No newline at end of file diff --git a/roles/php/handlers/main.yml b/roles/php/handlers/main.yml new file mode 100755 index 0000000..d491444 --- /dev/null +++ b/roles/php/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart php-fpm + service: + name: "php{{ php_version }}-fpm" + state: restarted \ No newline at end of file diff --git a/roles/php/meta/.galaxy_install_info b/roles/php/meta/.galaxy_install_info new file mode 100755 index 0000000..26b6545 --- /dev/null +++ b/roles/php/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:12 ' +version: '' diff --git a/roles/php/meta/main.yml b/roles/php/meta/main.yml new file mode 100755 index 0000000..f6b376f --- /dev/null +++ b/roles/php/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + role_name: php + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup PHP; should be used in combination with an DB-, HTTP-Server. + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - php +dependencies: [] diff --git a/roles/php/tasks/main.yml b/roles/php/tasks/main.yml new file mode 100755 index 0000000..24b429b --- /dev/null +++ b/roles/php/tasks/main.yml @@ -0,0 +1,66 @@ +--- +- name: install requirements for php repository + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - apt-transport-https + - ca-certificates + - gnupg2 + - lsb-release + - libmagickcore-6.q16-6-extra # SVG for imagick php module + +- name: install PHP sury repository key + apt_key: + url: https://packages.sury.org/php/apt.gpg + state: present + +- name: install PHP sury repository + apt_repository: + repo: "deb https://packages.sury.org/php/ {{ ansible_distribution_release }} main" + state: present + +- name: install PHP and recommendations + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: "{{ php_modules + php_modules_base }}" + +- name: configure PHP fpm + lineinfile: + path: "{{ php_path }}/fpm/php.ini" + regexp: '^(;|){{ item.key }} = ' + line: "{{ item.key }} = {{ item.value }}" + loop: "{{ php_fpm_config + php_fpm_config_default }}" + loop_control: + label: "{{ item.key }}" + notify: restart php-fpm + +- name: configure PHP fpm pool conf + lineinfile: + path: "{{ php_path }}/fpm/pool.d/www.conf" + regexp: '^(;|){{ item.key }} = ' + line: "{{ item.key }} = {{ item.value }}" + loop: "{{ php_fpm_poolconf + php_fpm_poolconf_default }}" + loop_control: + label: "{{ item.key }}" + notify: restart php-fpm + +- name: configure PHP fpm env pool conf + lineinfile: + path: "{{ php_path }}/fpm/pool.d/www.conf" + regexp: '^(;|)env\[{{ item.key }}\] = ' + line: "env[{{ item.key }}] = {{ item.value }}" + loop: "{{ php_fpm_poolconf_env }}" + loop_control: + label: "{{ item.key }}" + notify: restart php-fpm + +- name: configure PHP apcu availability + lineinfile: + path: "{{ php_path }}/mods-available/apcu.ini" + regexp: '^(;\s*|)apc.enable_cli=' + line: "apc.enable_cli=1" + notify: restart php-fpm \ No newline at end of file diff --git a/roles/php/vars/main.yml b/roles/php/vars/main.yml new file mode 100755 index 0000000..56a3ce6 --- /dev/null +++ b/roles/php/vars/main.yml @@ -0,0 +1,53 @@ +--- +#a rather good selection for most apps +php_modules_base: + - "php{{ php_version }}-apcu" + - "php{{ php_version }}-bz2" + - "php{{ php_version }}-ctype" + - "php{{ php_version }}-curl" + - "php{{ php_version }}-dom" + - "php{{ php_version }}-fpm" + - "php{{ php_version }}-fileinfo" + - "php{{ php_version }}-gd" + - "php{{ php_version }}-gmp" + - "php{{ php_version }}-imagick" + - "php{{ php_version }}-intl" + - "php{{ php_version }}-mbstring" + - "php{{ php_version }}-memcached" + - "php{{ php_version }}-mysql" + - "php{{ php_version }}-pdo-pgsql" + - "php{{ php_version }}-posix" + - "php{{ php_version }}-redis" + - "php{{ php_version }}-simplexml" + - "php{{ php_version }}-xmlreader" + - "php{{ php_version }}-xmlwriter" + - "php{{ php_version }}-zip" + + +php_path: "/etc/php/{{ php_version }}" + +php_fpm_config_default: + - key: memory_limit + value: "{{ php_memory_limit }}" + - key: upload_max_filesize + value: "{{ php_upload_max_filesize }}" + - key: post_max_size + value: "{{ php_post_max_size }}" + - key: opcache.interned_strings_buffer + value: "{{ php_opcache_interned_strings_buffer }}" + - key: opcache.save_comments + value: "{{ php_opcache_save_comments }}" + - key: opcache.revalidate_freq + value: "{{ php_opcache_revalidate_freq }}" + - key: opcache.validate_timestamps + value: "{{ php_opcache_validate_timestamps }}" + +php_fpm_poolconf_default: + - key: pm.max_children + value: "{{ php_pm_max_children }}" + - key: pm.start_servers + value: "{{ php_pm_start_servers }}" + - key: pm.min_spare_servers + value: "{{ php_pm_min_spare_servers }}" + - key: pm.max_spare_servers + value: "{{ php_pm_max_spare_servers }}" \ No newline at end of file diff --git a/roles/postgres/LICENSE b/roles/postgres/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/postgres/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/postgres/README.md b/roles/postgres/README.md new file mode 100755 index 0000000..d9db307 --- /dev/null +++ b/roles/postgres/README.md @@ -0,0 +1,3 @@ +# cloud-postgresql + +Ansible role to provide PostgreSQL for tools where needed. \ No newline at end of file diff --git a/roles/postgres/defaults/main.yml b/roles/postgres/defaults/main.yml new file mode 100755 index 0000000..ad0ee10 --- /dev/null +++ b/roles/postgres/defaults/main.yml @@ -0,0 +1,15 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false +psql_user: postgres + +db_configs: + - type: pgsql + name: db_name + user: db_user + pass: db_user_password + priv: all + +postgresql_version: postgresql-13 \ No newline at end of file diff --git a/roles/postgres/handlers/main.yml b/roles/postgres/handlers/main.yml new file mode 100755 index 0000000..ed97d53 --- /dev/null +++ b/roles/postgres/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/postgres/meta/.galaxy_install_info b/roles/postgres/meta/.galaxy_install_info new file mode 100755 index 0000000..6ef7f73 --- /dev/null +++ b/roles/postgres/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:13 ' +version: '' diff --git a/roles/postgres/meta/main.yml b/roles/postgres/meta/main.yml new file mode 100755 index 0000000..89ec236 --- /dev/null +++ b/roles/postgres/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: postgresql + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup PostgreSQL, that can be used in multiple apps like Modoboa, Redmine or WordPress. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - postgresql +dependencies: [] diff --git a/roles/postgres/tasks/main.yml b/roles/postgres/tasks/main.yml new file mode 100755 index 0000000..4b6546a --- /dev/null +++ b/roles/postgres/tasks/main.yml @@ -0,0 +1,41 @@ +--- +- name: install requirements for postgresql + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - apt-transport-https + - ca-certificates + - gnupg2 + - software-properties-common + - curl + +- name: setup PostgreSQL repository + copy: + content: "deb http://apt.postgresql.org/pub/repos/apt {{ ansible_lsb.codename }}-pgdg main" + dest: "/etc/apt/sources.list.d/pgdg.list" + +- name: setup PostgreSQL repository signing key + apt_key: + url: https://www.postgresql.org/media/keys/ACCC4CF8.asc + state: present + +- name: install PostgreSQL packages + apt: + name: "{{ pkg }}" + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + update_cache: yes + loop: + - "{{ postgresql_version }}" + loop_control: + loop_var: pkg + label: "{{ pkg }}" + +- name: Setup databases based on PostgreSQL conf + include_tasks: setup_db.yml + loop: "{{ db_configs | json_query('[?type==`pgsql`]') }}" + loop_control: + loop_var: db + label: "{% if 'dbname' in db %}{{ db.dbname }}{% elif 'dbuser' in db %}{{ db.dbuser }}{% else %}::pass_redacted::{% endif %}" \ No newline at end of file diff --git a/roles/postgres/tasks/setup_db.yml b/roles/postgres/tasks/setup_db.yml new file mode 100755 index 0000000..05c5616 --- /dev/null +++ b/roles/postgres/tasks/setup_db.yml @@ -0,0 +1,41 @@ +--- +- name: set DB related config options + set_fact: + local_db_name: "{{ db.name if 'name' in db else 'noentry' }}" + local_db_user: "{{ db.user if 'user' in db else 'noentry'}}" + local_db_pass: "{{ db.pass if 'pass' in db else 'noentry'}}" + local_db_user_privs: "{{ db.priv if 'priv' in db else 'all'}}" + +- name: "create {{ local_db_user }} user" + postgresql_user: + state: present + name: "{{ local_db_user }}" + password: "{{ local_db_pass }}" + become: yes + become_user: "{{ psql_user }}" + when: + - local_db_user != 'noentry' + - local_db_pass != 'noentry' + +- name: "setup {{ local_db_name }} database" + postgresql_db: + state: present + name: "{{ local_db_name }}" + owner: "{{ local_db_user }}" + become: yes + become_user: "{{ psql_user }}" + when: + - local_db_name != 'noentry' + +- name: "Grant {{ local_db_user }} user access to {{ local_db_name }} database" + postgresql_privs: + type: database + database: "{{ local_db_name }}" + roles: "{{ local_db_user }}" + grant_option: yes + privs: "{{ local_db_user_privs }}" + become: yes + become_user: "{{ psql_user }}" + when: + - local_db_name != 'noentry' + - local_db_user != 'noentry' diff --git a/roles/postgres/vars/main.yml b/roles/postgres/vars/main.yml new file mode 100755 index 0000000..ed97d53 --- /dev/null +++ b/roles/postgres/vars/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/signaling/LICENSE b/roles/signaling/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/signaling/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/signaling/README.md b/roles/signaling/README.md new file mode 100755 index 0000000..f1918d6 --- /dev/null +++ b/roles/signaling/README.md @@ -0,0 +1,3 @@ +# cloud-nextcloud-signaling + +Ansible role to provide an high-performance signaling server for nextcloud. \ No newline at end of file diff --git a/roles/signaling/defaults/main.yml b/roles/signaling/defaults/main.yml new file mode 100755 index 0000000..4297a18 --- /dev/null +++ b/roles/signaling/defaults/main.yml @@ -0,0 +1,30 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +ncloud_coturn_pass: SomeRandomString +ncloud_coturn_port: 5349 +ncloud_domain: + - my_domain.tld + +signaling_janus_deb_target: "http://ftp.de.debian.org/debian/pool/main/j/janus/janus_1.0.0-4~bpo11+1_amd64.deb" + +signaling_janus_api_key: MyVeryCoolApiKeyInBase64 +signaling_hash_key: VeryCoolHashKeyForSignaling +signaling_block_key: VeryCoolBlockKeyForSignaling +signaling_ncloud_secret_key: VerySecretKeyForNextCloud + +signaling_listen_host: 127.0.0.1 +signaling_listen_port: 8080 +signaling_backend_name: cloudbackend + +signaling_janus_host: "{{ signaling_listen_host }}" +signaling_janus_port: 8188 + +signaling_janus_configs: + - 'stun_server = "{{ ncloud_domain[0] }}"' + - 'stun_port = {{ ncloud_coturn_port }}' + - 'full_trickle = true' + - 'turn_rest_api_key = "{{ signaling_janus_api_key }}"' \ No newline at end of file diff --git a/roles/signaling/handlers/main.yml b/roles/signaling/handlers/main.yml new file mode 100755 index 0000000..9d8d9ad --- /dev/null +++ b/roles/signaling/handlers/main.yml @@ -0,0 +1,11 @@ +--- +- name: restart janus + systemd: + name: janus + state: restarted + +- name: restart signaling + systemd: + name: signaling + state: restarted + daemon_reload: yes diff --git a/roles/signaling/meta/.galaxy_install_info b/roles/signaling/meta/.galaxy_install_info new file mode 100755 index 0000000..6ef7f73 --- /dev/null +++ b/roles/signaling/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:13 ' +version: '' diff --git a/roles/signaling/meta/main.yml b/roles/signaling/meta/main.yml new file mode 100755 index 0000000..ab8ffbb --- /dev/null +++ b/roles/signaling/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: signaling + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup a Signaling Server for NextCloud + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - signaling +dependencies: + - docker diff --git a/roles/signaling/tasks/janus.yml b/roles/signaling/tasks/janus.yml new file mode 100755 index 0000000..f432f43 --- /dev/null +++ b/roles/signaling/tasks/janus.yml @@ -0,0 +1,31 @@ +--- +- name: install janus from deb file + apt: + deb: "{{ signaling_janus_deb_target }}" + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + +- name: configure janus server + lineinfile: + path: "/etc/janus/janus.jcfg" + regexp: '^\s*#?{{ configline.split("=")[0] }}{% if configline.split("=") | length > 1 %}={% endif %}' + line: " {{ configline }}" + loop: "{{ signaling_janus_configs }}" + loop_control: + loop_var: configline + label: '{{ configline.split("=")[0] }}' + notify: restart janus + +- name: configure janus network + lineinfile: + path: "/etc/janus/janus.transport.http.jcfg" + regexp: '^\s*#?interface =' + line: ' interface = "lo"' + notify: restart janus + +- name: configure janus network + lineinfile: + path: "/etc/janus/janus.transport.websockets.jcfg" + regexp: '^\s*#?ws_interface =' + line: ' ws_interface = "lo"' + notify: restart janus diff --git a/roles/signaling/tasks/main.yml b/roles/signaling/tasks/main.yml new file mode 100755 index 0000000..9af5812 --- /dev/null +++ b/roles/signaling/tasks/main.yml @@ -0,0 +1,63 @@ +--- +- name: setup signaling group + group: + name: "{{ sggrp }}" + state: "present" + +- name: setup signaling user + user: + name: "{{ sgusr }}" + group: "{{ sggrp }}" + groups: + - "{{ sggrp }}" + comment: Virtual Signaling User + shell: /sbin/nologin + state: present + +- name: install requirements for signaling + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - make + - golang-go + - libsrtp2-1 + +- name: clone signaling server + git: + repo: https://github.com/strukturag/nextcloud-spreed-signaling.git + dest: "{{ cloud_apps }}/signaling-server" + notify: restart signaling + register: clone_signaling + +- name: build signaling server + make: + chdir: "{{ cloud_apps }}/signaling-server" + target: build + when: clone_signaling.changed + +- name: configure signaling server + template: + src: "app/signaling-server/server.conf.j2" + dest: "{{ cloud_apps }}/signaling-server/server.conf" + owner: "{{ sgusr }}" + group: "{{ sggrp }}" + mode: "o=" + notify: restart signaling + +- include_tasks: janus.yml + +- include_tasks: nats.yml + +- name: setup signaling systemd service unit + template: + src: "etc/systemd/system/signaling.service.j2" + dest: "/etc/systemd/system/signaling.service" + +- name: enable signaling systemd unit + systemd: + name: signaling + enabled: yes + daemon_reload: yes + state: started \ No newline at end of file diff --git a/roles/signaling/tasks/nats.yml b/roles/signaling/tasks/nats.yml new file mode 100755 index 0000000..f7983dd --- /dev/null +++ b/roles/signaling/tasks/nats.yml @@ -0,0 +1,14 @@ +--- +- name: pull nats docker image + docker_image: + name: "nats:latest" + source: pull + +- name: run docker container + docker_container: + name: NATSSERVER + image: "nats:latest" + state: started + ports: + - 4222:4222 + restart_policy: always diff --git a/roles/signaling/templates/app/signaling-server/server.conf.j2 b/roles/signaling/templates/app/signaling-server/server.conf.j2 new file mode 100755 index 0000000..112f715 --- /dev/null +++ b/roles/signaling/templates/app/signaling-server/server.conf.j2 @@ -0,0 +1,227 @@ +[http] +# IP and port to listen on for HTTP requests. +# Comment line to disable the listener. +listen = {{ signaling_listen_host }}:{{ signaling_listen_port }} + +# HTTP socket read timeout in seconds. +#readtimeout = 15 + +# HTTP socket write timeout in seconds. +#writetimeout = 15 + +#[https] +# IP and port to listen on for HTTPS requests. +# Comment line to disable the listener. +#listen = 127.0.0.1:8443 + +# HTTPS socket read timeout in seconds. +#readtimeout = 15 + +# HTTPS socket write timeout in seconds. +#writetimeout = 15 + +# Certificate / private key to use for the HTTPS server. +#certificate = /etc/nginx/ssl/server.crt +#key = /etc/nginx/ssl/server.key + +[app] +# Set to "true" to install pprof debug handlers. +# See "https://golang.org/pkg/net/http/pprof/" for further information. +debug = false + +# Set to "true" to allow subscribing any streams. This is insecure and should +# only be enabled for testing. By default only streams of users in the same +# room and call can be subscribed. +#allowsubscribeany = false + +[sessions] +# Secret value used to generate checksums of sessions. This should be a random +# string of 32 or 64 bytes. +hashkey = {{ signaling_hash_key }} + +# Optional key for encrypting data in the sessions. Must be either 16, 24 or +# 32 bytes. +# If no key is specified, data will not be encrypted (not recommended). +blockkey = {{ signaling_block_key }} + +[clients] +# Shared secret for connections from internal clients. This must be the same +# value as configured in the respective internal services. +internalsecret = the-shared-secret-for-internal-clients + +[backend] +# Comma-separated list of backend ids from which clients are allowed to connect +# from. Each backend will have isolated rooms, i.e. clients connecting to room +# "abc12345" on backend 1 will be in a different room than clients connected to +# a room with the same name on backend 2. Also sessions connected from different +# backends will not be able to communicate with each other. +backends = {{ signaling_backend_name }} + +# Allow any hostname as backend endpoint. This is extremely insecure and should +# only be used while running the benchmark client against the server. +allowall = false + +# Common shared secret for requests from and to the backend servers if +# "allowall" is enabled. This must be the same value as configured in the +# Nextcloud admin ui. +#secret = the-shared-secret + +# Timeout in seconds for requests to the backend. +timeout = 10 + +# Maximum number of concurrent backend connections per host. +connectionsperhost = 8 + +# If set to "true", certificate validation of backend endpoints will be skipped. +# This should only be enabled during development, e.g. to work with self-signed +# certificates. +#skipverify = false + +# Backend configurations as defined in the "[backend]" section above. The +# section names must match the ids used in "backends" above. +[{{ signaling_backend_name }}] +# URL of the Nextcloud instance +url = https://{{ ncloud_domain[0] }} +# Shared secret for requests from and to the backend servers. This must be the +# same value as configured in the Nextcloud admin ui. +secret = {{ signaling_ncloud_secret_key }} + +# Limit the number of sessions that are allowed to connect to this backend. +# Omit or set to 0 to not limit the number of sessions. +#sessionlimit = 10 + +# The maximum bitrate per publishing stream (in bits per second). +# Defaults to the maximum bitrate configured for the proxy / MCU. +#maxstreambitrate = 1048576 + +# The maximum bitrate per screensharing stream (in bits per second). +# Defaults to the maximum bitrate configured for the proxy / MCU. +#maxscreenbitrate = 2097152 + +#[another-backend] +# URL of the Nextcloud instance +#url = https://cloud.otherdomain.invalid + +# Shared secret for requests from and to the backend servers. This must be the +# same value as configured in the Nextcloud admin ui. +#secret = the-shared-secret + +[nats] +# Url of NATS backend to use. This can also be a list of URLs to connect to +# multiple backends. For local development, this can be set to ":loopback:" +# to process NATS messages internally instead of sending them through an +# external NATS backend. +#url = nats://localhost:4222 + +[mcu] +# The type of the MCU to use. Currently only "janus" and "proxy" are supported. +# Leave empty to disable MCU functionality. +type = janus +# For type "janus": the URL to the websocket endpoint of the MCU server. +# For type "proxy": a space-separated list of proxy URLs to connect to. +url = ws://{{ signaling_janus_host }}:{{ signaling_janus_port }} + +# The maximum bitrate per publishing stream (in bits per second). +# Defaults to 1 mbit/sec. +# For type "proxy": will be capped to the maximum bitrate configured at the +# proxy server that is used. +#maxstreambitrate = 1048576 + +# The maximum bitrate per screensharing stream (in bits per second). +# Default is 2 mbit/sec. +# For type "proxy": will be capped to the maximum bitrate configured at the +# proxy server that is used. +#maxscreenbitrate = 2097152 + +# For type "proxy": timeout in seconds for requests to the proxy server. +#proxytimeout = 2 + +# For type "proxy": type of URL configuration for proxy servers. +# Defaults to "static". +# +# Possible values: +# - static: A space-separated list of proxy URLs is given in the "url" option. +# - etcd: Proxy URLs are retrieved from an etcd cluster (see below). +#urltype = static + +# If set to "true", certificate validation of proxy servers will be skipped. +# This should only be enabled during development, e.g. to work with self-signed +# certificates. +#skipverify = false + +# For type "proxy": the id of the token to use when connecting to proxy servers. +#token_id = server1 + +# For type "proxy": the private key for the configured token id to use when +# connecting to proxy servers. +#token_key = privkey.pem + +# For url type "etcd": Comma-separated list of static etcd endpoints to +# connect to. +#endpoints = 127.0.0.1:2379,127.0.0.1:22379,127.0.0.1:32379 + +# For url type "etcd": Options to perform endpoint discovery through DNS SRV. +# Only used if no endpoints are configured manually. +#discoverysrv = example.com +#discoveryservice = foo + +# For url type "etcd": Path to private key, client certificate and CA +# certificate if TLS authentication should be used. +#clientkey = /path/to/etcd-client.key +#clientcert = /path/to/etcd-client.crt +#cacert = /path/to/etcd-ca.crt + +# For url type "etcd": Key prefix of MCU proxy entries. All keys below will be +# watched and assumed to contain a JSON document. The entry "address" from this +# document will be used as proxy URL, other contents in the document will be +# ignored. +# +# Example: +# "/signaling/proxy/server/one" -> {"address": "https://proxy1.domain.invalid"} +# "/signaling/proxy/server/two" -> {"address": "https://proxy2.domain.invalid"} +#keyprefix = /signaling/proxy/server + +[turn] +# API key that the MCU will need to send when requesting TURN credentials. +apikey = {{ signaling_janus_api_key }} + +# The shared secret to use for generating TURN credentials. This must be the +# same as on the TURN server. +secret = {{ ncloud_coturn_pass }} + +# A comma-separated list of TURN servers to use. Leave empty to disable the +# TURN REST API. +servers = turn:{{ ncloud_domain[0] }}:{{ ncloud_coturn_port }}?transport=udp,turn:{{ ncloud_domain[0] }}:{{ ncloud_coturn_port }}?transport=tcp + +[geoip] +# License key to use when downloading the MaxMind GeoIP database. You can +# register an account at "https://www.maxmind.com/en/geolite2/signup" for +# free. See "https://dev.maxmind.com/geoip/geoip2/geolite2/" for further +# information. +# Leave empty to disable GeoIP lookups. +#license = + +# Optional URL to download a MaxMind GeoIP database from. Will be generated if +# "license" is provided above. Can be a "file://" url if a local file should +# be used. Please note that the database must provide a country field when +# looking up IP addresses. +#url = + +[geoip-overrides] +# Optional overrides for GeoIP lookups. The key is an IP address / range, the +# value the associated country code. +#127.0.0.1 = DE +#192.168.0.0/24 = DE + +[continent-overrides] +# Optional overrides for continent mappings. The key is a continent code, the +# value a comma-separated list of continent codes to map the continent to. +# Use European servers for clients in Africa. +#AF = EU +# Use servers in North Africa for clients in South America. +#SA = NA + +[stats] +# Comma-separated list of IP addresses that are allowed to access the stats +# endpoint. Leave empty (or commented) to only allow access from "127.0.0.1". +#allowed_ips = \ No newline at end of file diff --git a/roles/signaling/templates/etc/systemd/system/signaling.service.j2 b/roles/signaling/templates/etc/systemd/system/signaling.service.j2 new file mode 100755 index 0000000..a23cb71 --- /dev/null +++ b/roles/signaling/templates/etc/systemd/system/signaling.service.j2 @@ -0,0 +1,11 @@ +[Unit] +Description=Nextcloud Talk signaling server + +[Service] +ExecStart={{ cloud_apps }}/signaling-server/bin/signaling --config {{ cloud_apps }}/signaling-server/server.conf +User={{ sgusr }} +Group={{ sggrp }} +Restart=on-failure + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/signaling/vars/main.yml b/roles/signaling/vars/main.yml new file mode 100755 index 0000000..65ecb08 --- /dev/null +++ b/roles/signaling/vars/main.yml @@ -0,0 +1,3 @@ +--- +sgusr: signaling +sggrp: "{{ sgusr }}" diff --git a/roles/vault/LICENSE b/roles/vault/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/vault/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/vault/README.md b/roles/vault/README.md new file mode 100755 index 0000000..08caaf2 --- /dev/null +++ b/roles/vault/README.md @@ -0,0 +1,3 @@ +# cloud-vault + +Ansible role in order to setup Hashicorp Vault. \ No newline at end of file diff --git a/roles/vault/defaults/main.yml b/roles/vault/defaults/main.yml new file mode 100755 index 0000000..e904740 --- /dev/null +++ b/roles/vault/defaults/main.yml @@ -0,0 +1,50 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +vault_source: https://releases.hashicorp.com/vault +vault_version: 1.9.3 +vault_arch: linux_amd64 + +vault_pid: "{{ cloud_apps }}/vault/vault.pid" + +vault_ui: "true" +vault_no_mlock: "false" + +vault_port: 8200 +vault_listener: 127.0.0.1 +vault_api_addr: "http://{{ vault_listener }}:{{ vault_port }}" +vault_cluster_addr: "" +vault_cluster_name: "vault" + + +vault_storage: + storage: "storage" + type: "file" + conf: | + path = "{{ vault_home }}/data" + +vault_listener_tcp: + address: "{{ vault_listener }}:{{ vault_port }}" + tls_disable: "true" + proxy_protocol_behavior: "{{ vault_proxy_behaviour }}" + + +vault_config_options: + max_lease_ttl: "10h" + default_lease_ttl: "10h" + +vault_config_blocks: + - conf: telemetry + type: + options: | + prometheus_retention_time = "30s" + disable_hostname = true + + +# required only, when vault_proxy_behaviour is e.g. set to allow_authorized +# see https://www.vaultproject.io/docs/configuration/listener/tcp#proxy_protocol_authorized_addrs +vault_proxy_authorised_addr: [] +vault_proxy_behaviour: use_always \ No newline at end of file diff --git a/roles/vault/handlers/main.yml b/roles/vault/handlers/main.yml new file mode 100755 index 0000000..9b97a9b --- /dev/null +++ b/roles/vault/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart vault + systemd: + name: vault + state: restarted diff --git a/roles/vault/meta/.galaxy_install_info b/roles/vault/meta/.galaxy_install_info new file mode 100755 index 0000000..6ef7f73 --- /dev/null +++ b/roles/vault/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:13 ' +version: '' diff --git a/roles/vault/meta/main.yml b/roles/vault/meta/main.yml new file mode 100755 index 0000000..5d15a14 --- /dev/null +++ b/roles/vault/meta/main.yml @@ -0,0 +1,16 @@ +--- +galaxy_info: + role_name: vault + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup vault from HashiCorp + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 11 + galaxy_tags: + - vault +dependencies: [] diff --git a/roles/vault/tasks/main.yml b/roles/vault/tasks/main.yml new file mode 100755 index 0000000..f7d7dfc --- /dev/null +++ b/roles/vault/tasks/main.yml @@ -0,0 +1,67 @@ +--- +- name: setup vault group + group: + name: "{{ vault_usr }}" + state: "present" + +- name: setup vault user + user: + name: "{{ vault_usr }}" + group: "{{ vault_grp }}" + groups: + - "{{ vault_grp }}" + comment: Virtual Vault User + shell: /sbin/nologin + state: present + +- name: setup vault directories + file: + state: directory + path: "{{ item }}" + owner: "{{ vault_usr }}" + group: "{{ vault_grp }}" + mode: 0750 + loop: + - "{{ vault_inst }}" + - "{{ vault_home }}" + - "{{ vault_log }}" + +- name: setup vault installation link + file: + state: link + src: "{{ vault_inst }}" + dest: "{{ vault_link }}" + owner: "{{ vault_usr }}" + group: "{{ vault_grp }}" + mode: 0750 + +- name: download vault and unarchive + unarchive: + src: "{{ vault_source }}/{{ vault_version }}/vault_{{ vault_version }}_{{ vault_arch }}.zip" + dest: "{{ vault_link }}" + remote_src: yes + owner: "{{ vault_usr }}" + group: "{{ vault_grp }}" + mode: "o-rwx" + creates: "{{ vault_link }}/vault" + +- name: setup vault config + template: + src: opt/vault/vault.conf.hcl.j2 + dest: "{{ vault_home }}/vault.conf.hcl" + owner: "{{ vault_usr }}" + group: "{{ vault_grp }}" + notify: restart vault + +- name: setup vault systemd unit + template: + src: etc/systemd/system/vault.service.j2 + dest: /etc/systemd/system/vault.service + notify: restart vault + +- name: enable vault systemd unit + systemd: + name: vault + enabled: yes + daemon_reload: yes + state: started diff --git a/roles/vault/templates/etc/systemd/system/vault.service.j2 b/roles/vault/templates/etc/systemd/system/vault.service.j2 new file mode 100755 index 0000000..113d9be --- /dev/null +++ b/roles/vault/templates/etc/systemd/system/vault.service.j2 @@ -0,0 +1,36 @@ +[Unit] +Description=Vault agent +Requires=network-online.target +After=network-online.target + +[Service] +User={{ vault_usr }} +Group={{ vault_grp }} +ProtectSystem=full +ProtectHome=read-only +PrivateTmp=yes +PrivateDevices=yes +SecureBits=keep-caps +AmbientCapabilities=CAP_IPC_LOCK +Capabilities=CAP_IPC_LOCK+ep +CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK +NoNewPrivileges=yes +ExecStart={{ vault_link }}/vault server -config {{ vault_home }}/vault.conf.hcl +ExecReload=/bin/kill --signal HUP $MAINPID +ExecStop={{ vault_link }}/vault operator step-down +KillMode=process +KillSignal=SIGINT +LimitMEMLOCK=infinity +LimitNOFILE=65536 +PIDFile={{ vault_pid }} +Restart=on-failure +RestartSec=5 +StartLimitInterval=20 +StartLimitBurst=5 +TimeoutStartSec=30 +StandardOutput=append:{{ vault_log }}/vault.log +StandardError=append:{{ vault_log }}/vault.err + + +[Install] +WantedBy=multi-user.target diff --git a/roles/vault/templates/opt/vault/vault.conf.hcl.j2 b/roles/vault/templates/opt/vault/vault.conf.hcl.j2 new file mode 100755 index 0000000..1b5dc9b --- /dev/null +++ b/roles/vault/templates/opt/vault/vault.conf.hcl.j2 @@ -0,0 +1,36 @@ +disable_mlock = {{ vault_no_mlock }} +ui = {{ vault_ui }} + + +api_addr = "{{ vault_api_addr }}" +{% if vault_cluster_addr != '' %} +cluster_addr = "{{ vault_cluster_addr }}" +cluster_name = "{{ vault_cluster_name }}" +disable_clustering = false +{% else %} +disable_clustering = true +{% endif %} + +pid_file = "{{ vault_pid }}" + +{{vault_storage.storage }} "{{ vault_storage.type }}" { + {{ vault_storage.conf }} +} + +listener "tcp" { +{% for key, value in vault_listener_tcp.items() %} + {{ key }} = "{{ value }}" +{% endfor %} +} + +{% for key, value in vault_config_options.items() %} +{{ key }} = "{{ value }}" +{% endfor %} + +{% for block in vault_config_blocks %} +{{ block.conf }}{% if block.type is defined and block.type is not none and block.type != '' %} "{{ block.type }}"{% endif %} { +{% filter indent(width=2) %} + {{ block.options }} +{% endfilter %} +} +{% endfor %} diff --git a/roles/vault/vars/main.yml b/roles/vault/vars/main.yml new file mode 100755 index 0000000..98c55e5 --- /dev/null +++ b/roles/vault/vars/main.yml @@ -0,0 +1,8 @@ +--- +vault_usr: vault +vault_grp: "{{ vault_usr }}" + +vault_link: "{{ cloud_apps }}/vault/inst" +vault_inst: "{{ cloud_apps }}/vault/vault_{{ vault_version }}" +vault_home: "{{ cloud_apps }}/vault/home" +vault_log: "{{ cloud_apps }}/vault/log" diff --git a/roles/vpn/LICENSE b/roles/vpn/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/vpn/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/vpn/README.md b/roles/vpn/README.md new file mode 100755 index 0000000..66c9474 --- /dev/null +++ b/roles/vpn/README.md @@ -0,0 +1,3 @@ +# cloud-vpn + +Ansible role to provide an OpenVPN server; relies on easyrsa for certificates. \ No newline at end of file diff --git a/roles/vpn/defaults/main.yml b/roles/vpn/defaults/main.yml new file mode 100755 index 0000000..e15ccae --- /dev/null +++ b/roles/vpn/defaults/main.yml @@ -0,0 +1,29 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false + +sysctl_configs: + - net.ipv4.ip_forward=1 + +vpn_host: "vpn.{{ domain_external | default('my-domain.tld') }}" +vpn_protocol: "udp" +vpn_server: "cloud-openvpn" +vpn_cidr: 10.10.10.0 +vpn_mask: 255.255.255.0 +vpn_port: 1194 +vpn_log: /var/log/openvpn/openvpn.log +vpn_dns: + - 208.67.222.222 + - 208.67.220.220 + +vpn_clients: + - name: username + state: present + +easy_rsa_home: "/usr/share/easy-rsa" + +easy_rsa_clients: + - name: easyrsa_username + state: present diff --git a/roles/vpn/handlers/main.yml b/roles/vpn/handlers/main.yml new file mode 100755 index 0000000..f377e6f --- /dev/null +++ b/roles/vpn/handlers/main.yml @@ -0,0 +1,9 @@ +--- +- name: restart sysctl + command: sysctl -p + +- name: restart openvpn + systemd: + name: openvpn@server + state: restarted + enabled: yes diff --git a/roles/vpn/meta/.galaxy_install_info b/roles/vpn/meta/.galaxy_install_info new file mode 100755 index 0000000..6ef7f73 --- /dev/null +++ b/roles/vpn/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:13 ' +version: '' diff --git a/roles/vpn/meta/main.yml b/roles/vpn/meta/main.yml new file mode 100755 index 0000000..9455611 --- /dev/null +++ b/roles/vpn/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: vpn + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup an OpenVPN server, to make e.g. personal networks available from remote. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - vpn +dependencies: + - easy-rsa diff --git a/roles/vpn/tasks/clients.yml b/roles/vpn/tasks/clients.yml new file mode 100755 index 0000000..a2ff28c --- /dev/null +++ b/roles/vpn/tasks/clients.yml @@ -0,0 +1,57 @@ +--- +- name: install client ovpn configs + template: + src: etc/openvpn/client/client.ovpn.j2 + dest: "{{ vpn_home }}/client/{{ client }}.ovpn" + mode: 0600 + owner: root + group: vpn + loop: "{{ vpn_clients_active }}" + loop_control: + loop_var: client + label: "{{ client }}" + +- name: find abstent clients ovpn config + find: + paths: "{{ vpn_home }}/client/" + pattern: "{{ client }}.*" + loop: "{{ vpn_clients_passive }}" + loop_control: + loop_var: client + label: "{{ client }}" + register: absent_clients + +- name: remove absent clients ovpn config + file: + state: absent + path: "{{ client }}" + loop: "{{ absent_clients.results | json_query('[*].files[*].path') | flatten }}" + loop_control: + loop_var: client + label: "{{ client | basename }}" + when: absent_clients.results | length > 0 + +- name: setup OpenVPN config folder for each vpn client + file: + state: directory + path: "/home/{{ user }}/.openvpn" + mode: 0700 + owner: "{{ user }}" + group: "{{ user }}" + loop: "{{ vpn_clients_active | map('regex_replace','\\.[^\\.]+$','') | list | unique }}" + loop_control: + loop_var: user + label: "{{ user }}" + +- name: rollout .ovpn single-file config for active clients + copy: + src: "{{ vpn_home }}/client/{{ client }}.ovpn" + dest: "/home/{{ client.split('.')[0] }}/.openvpn/" + mode: 0400 + owner: "{{ client.split('.')[0] }}" + group: "{{ client.split('.')[0] }}" + loop: "{{ vpn_clients_active }}" + loop_control: + loop_var: client + label: "{{ client }}" + \ No newline at end of file diff --git a/roles/vpn/tasks/main.yml b/roles/vpn/tasks/main.yml new file mode 100755 index 0000000..6a787b7 --- /dev/null +++ b/roles/vpn/tasks/main.yml @@ -0,0 +1,32 @@ +--- +- name: install openvpn and recommendations + apt: + update_cache: yes + state: "{% if cloud_update | bool %}latest{% else %}present{% endif %}" + install_recommends: yes + pkg: + - "openvpn" + +- name: setup sysctl config for openvpn + lineinfile: + path: "{{ sysctl_conf }}" + regexp: '^#?{{ configline.split("=")[0] }}=' + line: "{{ configline }}" + backrefs: yes + owner: root + group: root + mode: 0644 + loop: "{{ sysctl_configs }}" + loop_control: + loop_var: configline + label: "{{ configline }}" + notify: restart sysctl + +# run in a block only when for every vpn_client user there is a certificate +# otherwise ignore ... +# It is better to have a matching config (dependency currently only to vpn) +# then to catch spectacular cases. +- block: + - include_tasks: server.yml + - include_tasks: clients.yml + when: vpn_clients | difference(easy_rsa_clients) | length == 0 diff --git a/roles/vpn/tasks/server.yml b/roles/vpn/tasks/server.yml new file mode 100755 index 0000000..11ccb62 --- /dev/null +++ b/roles/vpn/tasks/server.yml @@ -0,0 +1,78 @@ +--- +- name: find installed openvpn clients + find: + paths: "{{ vpn_home }}/client/" + patterns: "*.crt" + register: easyrsa_key_file + +#- name: Setup default OpenVPN configuration +# shell: +# cmd: "gunzip -c {{ vpn_doc_examples }}/server.conf.gz > {{ vpn_home }}/server.conf" +# creates: "{{ vpn_home }}/server.conf" + +- name: Setup default OpenVPN configuration + copy: + src: "{{ vpn_doc_examples }}/server.conf" + dest: "{{ vpn_home }}/server.conf" + owner: root + group: root + force: no + +- name: find server TLS-Auth key + find: + paths: "{{ vpn_home }}/server/" + patterns: "ta.key" + register: tlsauth_key_files + +- name: generate TLS-Auth key + command: "openvpn --genkey --secret {{ vpn_tlsauth_key_file }}" + when: tlsauth_key_files.matched == 0 + +- name: install easy-rsa CA and server certs + copy: + src: "{{ easy_rsa_home }}/pki/{{ item }}" + dest: "{{ vpn_home }}/server/" + mode: 0600 + owner: root + group: root + loop: + - "ca.crt" + - "dh.pem" + - "private/{{ vpn_server }}.key" + - "issued/{{ vpn_server }}.crt" + +- name: setup OpenVPN configuration + lineinfile: + path: "{{ vpn_home }}/server.conf" + regexp: '^;?{{ configline.split(" ")[0] }}{% if configline.split(" ") | length > 1 %} {% endif %}' + line: "{{ configline }}" + owner: root + group: root + mode: 0644 + loop: "{{ vpn_server_conf }}" + loop_control: + loop_var: configline + label: "{{ configline }}" + notify: restart openvpn + +- name: off-setup OpenVPN configuration + lineinfile: + path: "{{ vpn_home }}/server.conf" + regexp: '^{{ configline.split(" ")[0] }}{% if configline.split(" ") | length > 1 %} {% endif %}' + line: ";{{ configline }}" + backrefs: yes + owner: root + group: root + mode: 0644 + loop: "{{ vpn_server_conf_off }}" + loop_control: + loop_var: configline + label: ";{{ configline }}" + notify: restart openvpn + +- name: enable openvpn@server systemd unit + systemd: + name: openvpn@server + enabled: yes + daemon_reload: yes + state: started \ No newline at end of file diff --git a/roles/vpn/templates/etc/openvpn/client/client.ovpn.j2 b/roles/vpn/templates/etc/openvpn/client/client.ovpn.j2 new file mode 100755 index 0000000..100a7ac --- /dev/null +++ b/roles/vpn/templates/etc/openvpn/client/client.ovpn.j2 @@ -0,0 +1,29 @@ +client +dev tun +proto {{ vpn_protocol }} +remote {{ vpn_host }} {{ vpn_port }} +remote-cert-tls server +key-direction 1 +cipher AES-256-CBC +auth SHA512 +auth-nocache +tls-version-min 1.2 +tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256 +comp-lzo +nobind +persist-key +persist-tun +mute-replay-warnings +verb 3 + +{{ lookup("file", easy_rsa_home + "/pki/ca.crt") }} + + +{{ lookup("file", easy_rsa_home + "/pki/issued/" + client + ".crt") }} + + +{{ lookup("file", easy_rsa_home + "/pki/private/" + client + ".key") }} + + +{{ lookup("file", vpn_tlsauth_key_file) }} + diff --git a/roles/vpn/vars/main.yml b/roles/vpn/vars/main.yml new file mode 100755 index 0000000..8d090b0 --- /dev/null +++ b/roles/vpn/vars/main.yml @@ -0,0 +1,43 @@ +--- +sysctl_path: "/etc/" +sysctl_conf: "{{ sysctl_path }}/sysctl.conf" + +vpn_doc_examples: /usr/share/doc/openvpn/examples/sample-config-files/ + +vpn_home: "/etc/openvpn" + +vpn_clients_active: "{{ vpn_clients | json_query('[?state==`present`].name') }}" +vpn_clients_passive: "{{ vpn_clients | json_query('[?state!=`present`].name') }}" + +vpn_tlsauth_key: ta.key +vpn_tlsauth_key_file: "{{ vpn_home }}/server/{{ vpn_tlsauth_key }}" + +vpn_server_conf: + - "port {{ vpn_port }}" + - "proto {{ vpn_protocol }}" + - dev tun + - "ca {{ vpn_home }}/server/ca.crt" + - "cert {{ vpn_home }}/server/{{ vpn_server }}.crt" + - "key {{ vpn_home }}/server/{{ vpn_server }}.key" + - "dh {{ vpn_home }}/server/dh.pem" + - topology subnet + - "server {{ vpn_cidr }} {{ vpn_mask }}" + - cipher AES-256-CBC + - "tls-auth {{ vpn_home }}/server/ta.key 0" + - tls-version-min 1.2 + - tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256 + - auth SHA512 + - auth-nocache + - keepalive 20 60 + - persist-key + - persist-tun + - client-to-client + - comp-lzo + - user nobody + - group nogroup + - "log-append {{ vpn_log }}" + - verb 3 + +vpn_server_conf_off: + - explicit-exit-notify 1 + diff --git a/roles/wordpress/LICENSE b/roles/wordpress/LICENSE new file mode 100755 index 0000000..2071b23 --- /dev/null +++ b/roles/wordpress/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/roles/wordpress/README.md b/roles/wordpress/README.md new file mode 100755 index 0000000..7ebbd16 --- /dev/null +++ b/roles/wordpress/README.md @@ -0,0 +1,3 @@ +# cloud-wordpress + +Ansible role to provide a clean wordpress instance or multiple instances. \ No newline at end of file diff --git a/roles/wordpress/defaults/main.yml b/roles/wordpress/defaults/main.yml new file mode 100755 index 0000000..1207803 --- /dev/null +++ b/roles/wordpress/defaults/main.yml @@ -0,0 +1,24 @@ +--- +cloud_apps: /opt +cloud_storage: /opt/storage +cloud_stage: prod +cloud_update: false +download_folder: "{{ cloud_apps }}/download-tmp" +shared_group: "cloud" + + +www_root: /var/www +www_group: www-data + +web_sites: + - domain: my-domain.tld + state: present + owner: user + root: "{{ www_root }}/my-domain.tld" + version: latest + worpress: true + db: + type: mariadb + name: db_name + user: db_user + pass: db_pass diff --git a/roles/wordpress/handlers/main.yml b/roles/wordpress/handlers/main.yml new file mode 100755 index 0000000..ed97d53 --- /dev/null +++ b/roles/wordpress/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/wordpress/meta/.galaxy_install_info b/roles/wordpress/meta/.galaxy_install_info new file mode 100755 index 0000000..6ef7f73 --- /dev/null +++ b/roles/wordpress/meta/.galaxy_install_info @@ -0,0 +1,2 @@ +install_date: 'So 29 Mai 2022 13:55:13 ' +version: '' diff --git a/roles/wordpress/meta/main.yml b/roles/wordpress/meta/main.yml new file mode 100755 index 0000000..8f7a4cb --- /dev/null +++ b/roles/wordpress/meta/main.yml @@ -0,0 +1,19 @@ +--- +galaxy_info: + role_name: wordpress + namespace: hahn-cloud + author: Lars Hahn + company: OpenDevChain + license: MIT + description: Role to setup wordpress; shoudld be used in combination with an DB-, HTTP-Server and letsencrypt. + min_ansible_version: 2.7 + platforms: + - name: Debian + versions: + - 10 + galaxy_tags: + - wordpress +dependencies: + - mariadb + - php + - nginx \ No newline at end of file diff --git a/roles/wordpress/tasks/main.yml b/roles/wordpress/tasks/main.yml new file mode 100755 index 0000000..788f85a --- /dev/null +++ b/roles/wordpress/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- name: setup download folder + file: + state: directory + path: "{{ download_folder }}" + +- name: setup wordpress folder + file: + state: directory + path: "{{ cloud_apps }}/wordpress" + mode: 0770 + owner: "{{ www_group }}" + group: "{{ shared_group }}" + +- name: download latest wordpress version + get_url: + url: https://wordpress.org/latest.tar.gz + dest: "{{ wordpress_latest_target }}" + +- name: setup WordPress websites + include_tasks: setup-wp.yml + loop: "{{ web_sites | json_query('[?wordpress==`true`&&state==`present`]') }}" + loop_control: + loop_var: wp + label: "{{ wp.domain }}" diff --git a/roles/wordpress/tasks/setup-wp.yml b/roles/wordpress/tasks/setup-wp.yml new file mode 100755 index 0000000..5dd3cd8 --- /dev/null +++ b/roles/wordpress/tasks/setup-wp.yml @@ -0,0 +1,70 @@ +--- +- name: set WP related config options + set_fact: + local_wp_root: "{{ wp.root | default(www_root + '/' + wp.domain) }}" + local_wp_owner: "{{ wp.owner | default('root') }}" + local_wp_version: "{{ wp.version | default('latest') }}" + +- name: "extract wordpress version {{ local_wp_version }} " + unarchive: + src: "{{ 'https://wordpress.org/wordpress-' + local_wp_version + '.tar.gz' if local_wp_version != 'latest' else wordpress_latest_target }}" + dest: "/tmp" + +- name: "extract wordpress archive to {{ local_wp_root }}" + copy: + remote_src: yes + src: "/tmp/wordpress/" + dest: "{{ local_wp_root }}" + force: no + mode: 770 + group: "{{ local_wp_owner }}" + owner: "{{ www_group }}" + + +- name: "adjust permissions for {{ local_wp_owner }}" + file: + path: "{{ local_wp_root }}" + group: "{{ local_wp_owner }}" + owner: "{{ www_group }}" + state: directory + recurse: yes + + +- name: generate example config + copy: + remote_src: yes + src: "{{ local_wp_root }}/wp-config-sample.php" + dest: "{{ local_wp_root }}/wp-config.php" + force: no + group: "{{ local_wp_owner }}" + owner: "{{ www_group }}" + +- name: adjust WP-config + lineinfile: + path: "{{ local_wp_root }}/wp-config.php" + regexp: "^define\\( '{{ item.conf }}'" + line: "define( '{{ item.conf }}', '{{ item.data }}' );" + loop: + - conf: "DB_NAME" + data: "{{ wp.db.name }}" + - conf: "DB_USER" + data: "{{ wp.db.user }}" + - conf: "DB_PASSWORD" + data: "{{ wp.db.pass }}" + loop_control: + label: "{{ item.conf }}" + +- name: adjust permissions in WP root for directories + changed_when: false + command: 'find {{ local_wp_root }} -type d -exec chmod 770 {} \;' + +- name: adjust permissions in WP root for files + changed_when: false + command: 'find {{ local_wp_root }} -type f -exec chmod 660 {} \;' + +- name: link wordpress folder to non-root user + file: + src: "{{ local_wp_root }}" + dest: "/home/{{ local_wp_owner }}/{{ wp.domain }}" + state: link + when: local_wp_owner != 'root' diff --git a/roles/wordpress/vars/main.yml b/roles/wordpress/vars/main.yml new file mode 100755 index 0000000..815724d --- /dev/null +++ b/roles/wordpress/vars/main.yml @@ -0,0 +1,2 @@ +--- +wordpress_latest_target: "{{ download_folder }}/wordpress-latest.tar.gz" \ No newline at end of file