From 96ca1528c25ee57c94ea2defb5d905eda15b0931 Mon Sep 17 00:00:00 2001 From: chatton Date: Sun, 8 Oct 2023 20:51:43 +0100 Subject: [PATCH] wip --- host_vars/qnap.yml | 19 +- roles/portainer_authelia/defaults/main.yml | 11 + roles/portainer_authelia/handlers/main.yml | 3 + roles/portainer_authelia/tasks/main.yml | 45 +++ .../templates/configuration.j2 | 92 ++++++ .../templates/users_database.j2 | 8 + .../defaults/main.yml | 1 + .../handlers/main.yml | 3 + .../tasks/main.yml | 35 +++ .../templates/authelia-authrequest.j2 | 25 ++ .../templates/authelia-location.j2 | 36 +++ .../templates/proxy.j2 | 35 +++ vault_vars/qnap-vault.yml | 261 +++++++++--------- 13 files changed, 442 insertions(+), 132 deletions(-) create mode 100644 roles/portainer_authelia/defaults/main.yml create mode 100644 roles/portainer_authelia/handlers/main.yml create mode 100644 roles/portainer_authelia/tasks/main.yml create mode 100644 roles/portainer_authelia/templates/configuration.j2 create mode 100644 roles/portainer_authelia/templates/users_database.j2 create mode 100644 roles/portainer_nginx_proxy_manager/handlers/main.yml create mode 100644 roles/portainer_nginx_proxy_manager/templates/authelia-authrequest.j2 create mode 100644 roles/portainer_nginx_proxy_manager/templates/authelia-location.j2 create mode 100644 roles/portainer_nginx_proxy_manager/templates/proxy.j2 diff --git a/host_vars/qnap.yml b/host_vars/qnap.yml index 66c3c57..533687b 100644 --- a/host_vars/qnap.yml +++ b/host_vars/qnap.yml @@ -57,14 +57,15 @@ pihole_volumes: qnap_dns: {} portainer_roles: - - role: portainer_navidrome - - role: portainer_plex - - role: portainer_dashy - - role: portainer_diun - - role: portainer_dashdot - - role: portainer_glances - - role: portainer_arr - - role: portainer_pihole + - role: portainer_authelia +# - role: portainer_navidrome +# - role: portainer_plex +# - role: portainer_dashy +# - role: portainer_diun +# - role: portainer_dashdot +# - role: portainer_glances +# - role: portainer_arr +# - role: portainer_pihole - role: portainer_nginx_proxy_manager - - role: portainer_minio +# - role: portainer_minio # - role: portainer_wireguard_easy diff --git a/roles/portainer_authelia/defaults/main.yml b/roles/portainer_authelia/defaults/main.yml new file mode 100644 index 0000000..35d6c87 --- /dev/null +++ b/roles/portainer_authelia/defaults/main.yml @@ -0,0 +1,11 @@ +--- +authelia_state: present +authelia_image: authelia/authelia +authelia_tag: 4.37 +authelia_app_url: https://auth.cianhatton.ie +authelia_expose_port: 9091 +authelia_container_name: authelia +authelia_portainer_stack_name: authelia +authelia_config_dir: /etc/config/authelia +authelia_config_file: configuration.yml +authelia_users_database_file: users_database.yml diff --git a/roles/portainer_authelia/handlers/main.yml b/roles/portainer_authelia/handlers/main.yml new file mode 100644 index 0000000..b001bea --- /dev/null +++ b/roles/portainer_authelia/handlers/main.yml @@ -0,0 +1,3 @@ +--- +- name: restart-authelia + ansible.builtin.command: docker restart authelia diff --git a/roles/portainer_authelia/tasks/main.yml b/roles/portainer_authelia/tasks/main.yml new file mode 100644 index 0000000..cc61b27 --- /dev/null +++ b/roles/portainer_authelia/tasks/main.yml @@ -0,0 +1,45 @@ +--- +- name: Create config directory. + ansible.builtin.file: + path: '{{ authelia_config_dir }}' + state: directory + mode: '0755' + +- name: Template config file. + ansible.builtin.template: + src: "configuration.j2" + dest: '{{ authelia_config_dir }}/{{ authelia_config_file }}' + owner: root + group: root + mode: 0440 + notify: restart-authelia + +- name: Template users database file. + ansible.builtin.template: + src: "users_database.j2" + dest: '{{ authelia_config_dir }}/{{ authelia_users_database_file }}' + owner: root + group: root + mode: 0440 + notify: restart-authelia + +- name: "Authelia | Update Portainer." + chatton.portainer.portainer_stack: + username: '{{ portainer_user }}' + password: '{{ portainer.password }}' + base_url: '{{ portainer_base_url }}' + stack_name: '{{ authelia_portainer_stack_name }}' + endpoint_id: '{{ portainer_endpoint }}' + state: "{{ authelia_state }}" + definition: + version: '3.3' + services: + authelia: + image: "{{ authelia_image }}:{{ authelia_tag }}" + container_name: authelia + volumes: + - "{{ authelia_config_dir }}:/config" + ports: + - "{{ authelia_expose_port }}:9091" + environment: + - TZ=Europe/Dublin diff --git a/roles/portainer_authelia/templates/configuration.j2 b/roles/portainer_authelia/templates/configuration.j2 new file mode 100644 index 0000000..60e9cb4 --- /dev/null +++ b/roles/portainer_authelia/templates/configuration.j2 @@ -0,0 +1,92 @@ +# yamllint disable rule:comments-indentation +--- +############################################################################### +# Authelia Configuration # +############################################################################### + +theme: dark +jwt_secret: "{{ authelia_jwt_secret }}" + +default_redirection_url: https://google.com/ #where to redirect for a non-existent URL + +server: + host: 0.0.0.0 + port: "{{ authelia_expose_port }}" + path: "" + read_buffer_size: 4096 + write_buffer_size: 4096 + enable_pprof: false + enable_expvars: false + disable_healthcheck: false + tls: + key: "" + certificate: "" + +log: + level: debug + +totp: + issuer: cianhatton.ie + period: 30 + skew: 1 + +authentication_backend: + disable_reset_password: false + refresh_interval: 5m + file: + path: /config/users_database.yml #this is where your authorized users are stored + password: + algorithm: argon2id + iterations: 1 + key_length: 32 + salt_length: 16 + memory: 1024 + parallelism: 8 + +access_control: + default_policy: deny + rules: + ## bypass rule + - domain: + - "auth.cianhatton.ie" + policy: bypass + - domain: "git.cianhatton.ie" + policy: one_factor + #add or remove additional subdomains as necessary. currenlty only supports ONE top-level domain + #any time you add a new subdomain, you will need to restart the Authelia container to recognize the new settings/rules + +session: + name: authelia_session + secret: unsecure_session_secret #any text or number you want to add here to create jwt Token + expiration: 3600 # 1 hour + inactivity: 300 # 5 minutes + domain: cianhatton.ie # Should match whatever your root protected domain is + +regulation: + max_retries: 3 + find_time: 10m + ban_time: 12h + +storage: + local: + path: /config/db.sqlite3 #this is your databse. You could use a mysql database if you wanted, but we're going to use this one. + encryption_key: "{{ authelia_ecryption_key }}" + +notifier: + disable_startup_check: true #true/false + smtp: + username: "{{ authelia_email }}" + password: "{{ authelia_email_password}}" + host: smtp.gmail.com + port: 587 + sender: "{{ authelia_email }}" + identifier: localhost + subject: "[Authelia] {title}" #email subject + startup_check_address: "{{ authelia_email }}" + disable_require_tls: false + disable_html_emails: false + tls: + skip_verify: false + minimum_version: TLS1.2 + + diff --git a/roles/portainer_authelia/templates/users_database.j2 b/roles/portainer_authelia/templates/users_database.j2 new file mode 100644 index 0000000..70164bc --- /dev/null +++ b/roles/portainer_authelia/templates/users_database.j2 @@ -0,0 +1,8 @@ +users: + chatton: + displayname: "chatton" #whatever you want the display name to be + password: "{{ chatton_user_password_hash }}" #generated at https:/cargon2.online/ + email: "{{ authelia_email }}" + groups: #enter the groups you want the user to be part of belowc + - admins + - dev diff --git a/roles/portainer_nginx_proxy_manager/defaults/main.yml b/roles/portainer_nginx_proxy_manager/defaults/main.yml index 7dccb22..654caba 100644 --- a/roles/portainer_nginx_proxy_manager/defaults/main.yml +++ b/roles/portainer_nginx_proxy_manager/defaults/main.yml @@ -9,3 +9,4 @@ nginx_proxy_manager_container_name: nginx-proxy-manager nginx_proxy_manager_portainer_stack_name: nginx-proxy-manager nginx_proxy_manager_puid: 1000 nginx_proxy_manager_pgid: 1000 +nginx_proxy_manager_snippets_dir: /etc/nginx-proxy-manager/snippets diff --git a/roles/portainer_nginx_proxy_manager/handlers/main.yml b/roles/portainer_nginx_proxy_manager/handlers/main.yml new file mode 100644 index 0000000..ee2d595 --- /dev/null +++ b/roles/portainer_nginx_proxy_manager/handlers/main.yml @@ -0,0 +1,3 @@ +--- +- name: restart-nginx-proxy-manager + ansible.builtin.command: docker restart nginx-proxy-manager diff --git a/roles/portainer_nginx_proxy_manager/tasks/main.yml b/roles/portainer_nginx_proxy_manager/tasks/main.yml index 9dc70c9..5b5f9b7 100644 --- a/roles/portainer_nginx_proxy_manager/tasks/main.yml +++ b/roles/portainer_nginx_proxy_manager/tasks/main.yml @@ -1,4 +1,38 @@ --- +- name: Create snippets directory. + ansible.builtin.file: + path: '{{ nginx_proxy_manager_snippets_dir }}' + state: directory + mode: '0755' + notify: restart-nginx-proxy-manager + +- name: Create authrequest files. + notify: restart-nginx-proxy-manager + ansible.builtin.template: + src: "authelia-authrequest.j2" + dest: '{{ nginx_proxy_manager_snippets_dir }}/authelia-authrequest.conf' + owner: root + group: root + mode: 0440 + +- name: Create authelia-location.conf. + ansible.builtin.template: + src: "authelia-location.j2" + dest: '{{ nginx_proxy_manager_snippets_dir }}/authelia-location.conf' + owner: root + group: root + mode: 0440 + notify: restart-nginx-proxy-manager + +- name: Create proxy.conf. + ansible.builtin.template: + src: "proxy.j2" + dest: '{{ nginx_proxy_manager_snippets_dir }}/proxy.conf' + owner: root + group: root + mode: 0440 + notify: restart-nginx-proxy-manager + - name: "Nginx proxy manager | Restore any missing volumes from S3" ansible.builtin.include_role: name: chatton.docker_backup.docker_s3_volume_restore @@ -28,5 +62,6 @@ - "{{ nginx_proxy_manager_https_expose_port }}:4443" volumes: - "data:/config:rw" + - "{{ nginx_proxy_manager_snippets_dir }}:/snippets:ro" volumes: data: diff --git a/roles/portainer_nginx_proxy_manager/templates/authelia-authrequest.j2 b/roles/portainer_nginx_proxy_manager/templates/authelia-authrequest.j2 new file mode 100644 index 0000000..8c80ac6 --- /dev/null +++ b/roles/portainer_nginx_proxy_manager/templates/authelia-authrequest.j2 @@ -0,0 +1,25 @@ +## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource. +auth_request /authelia; + +## Set the $target_url variable based on the original request. + +## Comment this line if you're using nginx without the http_set_misc module. +set_escape_uri $target_url $scheme://$http_host$request_uri; + +## Uncomment this line if you're using NGINX without the http_set_misc module. +# set $target_url $scheme://$http_host$request_uri; + +## Save the upstream response headers from Authelia to variables. +auth_request_set $user $upstream_http_remote_user; +auth_request_set $groups $upstream_http_remote_groups; +auth_request_set $name $upstream_http_remote_name; +auth_request_set $email $upstream_http_remote_email; + +## Inject the response headers from the variables into the request made to the backend. +proxy_set_header Remote-User $user; +proxy_set_header Remote-Groups $groups; +proxy_set_header Remote-Name $name; +proxy_set_header Remote-Email $email; + +## If the subreqest returns 200 pass to the backend, if the subrequest returns 401 redirect to the portal. +error_page 401 =302 https://auth.cianhatton.ie/?rd=$target_url; diff --git a/roles/portainer_nginx_proxy_manager/templates/authelia-location.j2 b/roles/portainer_nginx_proxy_manager/templates/authelia-location.j2 new file mode 100644 index 0000000..67f23cf --- /dev/null +++ b/roles/portainer_nginx_proxy_manager/templates/authelia-location.j2 @@ -0,0 +1,36 @@ +set $upstream_authelia http://192.168.178.42:9091/api/verify; + +## Virtual endpoint created by nginx to forward auth requests. +location /authelia { + ## Essential Proxy Configuration + internal; + proxy_pass $upstream_authelia; + + ## Headers + ## The headers starting with X-* are required. + proxy_set_header X-Original-URL $scheme://$http_host$request_uri; + proxy_set_header X-Original-Method $request_method; + proxy_set_header X-Forwarded-Method $request_method; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Uri $request_uri; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Content-Length ""; + proxy_set_header Connection ""; + + ## Basic Proxy Configuration + proxy_pass_request_body off; + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead + proxy_redirect http:// $scheme://; + proxy_http_version 1.1; + proxy_cache_bypass $cookie_session; + proxy_no_cache $cookie_session; + proxy_buffers 4 32k; + client_body_buffer_size 128k; + + ## Advanced Proxy Configuration + send_timeout 5m; + proxy_read_timeout 240; + proxy_send_timeout 240; + proxy_connect_timeout 240; +} diff --git a/roles/portainer_nginx_proxy_manager/templates/proxy.j2 b/roles/portainer_nginx_proxy_manager/templates/proxy.j2 new file mode 100644 index 0000000..4098bb2 --- /dev/null +++ b/roles/portainer_nginx_proxy_manager/templates/proxy.j2 @@ -0,0 +1,35 @@ +## Headers +proxy_set_header Host $host; +proxy_set_header X-Original-URL $scheme://$http_host$request_uri; +proxy_set_header X-Forwarded-Proto $scheme; +proxy_set_header X-Forwarded-Host $http_host; +proxy_set_header X-Forwarded-Uri $request_uri; +proxy_set_header X-Forwarded-Ssl on; +proxy_set_header X-Forwarded-For $remote_addr; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header Connection ""; + +## Basic Proxy Configuration +client_body_buffer_size 128k; +proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; ## Timeout if the real server is dead. +proxy_redirect http:// $scheme://; +proxy_http_version 1.1; +proxy_cache_bypass $cookie_session; +proxy_no_cache $cookie_session; +proxy_buffers 64 256k; + +## Trusted Proxies Configuration +## Please read the following documentation before configuring this: +## https://www.authelia.com/integration/proxies/nginx/#trusted-proxies +# set_real_ip_from 10.0.0.0/8; +# set_real_ip_from 172.16.0.0/12; +# set_real_ip_from 192.168.0.0/16; +# set_real_ip_from fc00::/7; +real_ip_header X-Forwarded-For; +real_ip_recursive on; + +## Advanced Proxy Configuration +send_timeout 5m; +proxy_read_timeout 360; +proxy_send_timeout 360; +proxy_connect_timeout 360; diff --git a/vault_vars/qnap-vault.yml b/vault_vars/qnap-vault.yml index 39390ae..8eb0449 100644 --- a/vault_vars/qnap-vault.yml +++ b/vault_vars/qnap-vault.yml @@ -1,124 +1,139 @@ $ANSIBLE_VAULT;1.1;AES256 -66623036653934333335303734656332653762386566386238646561336564656166383237643433 -6264343132613863616434616334626465643665613637390a646633316439393538356133666462 -64646135653462363430343662653630643063366563623531383039616362303263323563313266 -3762393734666139630a646631613830353837373261336361373036396632386365653465373538 -39396638623261623266336464363833326561303765613738393933393231326363623631356134 -32346231313761303336306232653536353435363964373938653966383335363764613438663566 -63313439353564623539323139353262383339353239356364396339643864353532366237353039 -34343236313666636364613261353461336137373535616361353731656465343930396538336336 -63303166343165346636656439353832386230643965613561383462616564306437623162646164 -65343039613531343666623736323136633038363339313838643830666462333666333561396364 -37356663306661363335343436313535376436323164343764393930623932393734613835336363 -64343332626434643666376162333065333638613464346361326230643035376137313635303265 -64366666616263333530653064393066386336623866336332353830633334643432653336303239 -65623964623165373431303266636233343635653632666239653135616330313930356430386334 -36373732333431333565336462386637633339613939343535646639376533303834353038623063 -33623566333234343539346563623737623164386334623035333730613438343235326139323035 -62323937373435313263623037356534663938303830303232353737333166323466353565333930 -34623565386235343535303635336265343639353238383132356666326238366466316361386165 -61366664333431393038626239316333343435373434333761376231343361666135386235393730 -61626130363232383337313066373533333532663866326632653462343135323034303432663563 -34303465353662346130336336383335656361323734393835666631306432373566346537333037 -33313032626466316235323336333138633831663662663163326134393432346665613233346138 -66626631373539323932353861316563306161326138343962313536616361376361306132353265 -31363530633737613239643131376235623336316263336466383333313330363330333562623231 -61666364653239373136313138633231323964663461376133353162623937646237633836373831 -62366530663935303835373966356637623666613636623639303732643364643531323065316632 -65303738353532643431376639656538323431626139633835646638643064613932366639333937 -31653835366634643330353239353336363634663734623437343062653133386531626237643331 -39643630336561306265663735353765313536353565373631333064343239613538613665323866 -65613234363464313735616362653833613838333865666436626634663838323264613936336631 -37313964333937666161633461313264373862643136343764633130656532363433353434396138 -35653539343533343139313435626364656235333536396531663239383233303130323863393366 -39383764623565373166623865653866326636663338353865336531623534323632326564353432 -66376263323264343562623964626166316363383730333436303332663464666366633661663738 -32666231646335656331613937343166373134326636636262653732363231636531343334613165 -61363737313436306463613735636165633834643864383562326162663531613234316335623036 -33343461653063363235333933623730336539653836393933306638623461313237643433616137 -31306465626132303430616530366561666138366663373466383033306230316365306666643363 -65303663666136363662303363303339383239383738646331333130383938353230383839613234 -30383939643735303734643931633338343832343030316466323035313130363765633931663238 -63323734636630306165343534356235306431373938653035306532623061396566316133306135 -36613332343334323232333336353536366265306435616163356238353661343966663331616661 -61373431313831313133323538663333326162346532393939373765303764643638356335373537 -36366566386162663938363361616462393665386364666166666230313837613139633339343965 -38366636313166636437646166383566666431326561363833303434383534353162626166303437 -30623232333637613538613163633463633935623438323133383932356439326437346334643235 -31366134663734373031383832393430636466343132366265363966623138396138326365393331 -31326536633861373431616536366432656631613561353062323635613965393135393734353130 -33653430623234653266303538666564306335643464623530333937376262383638323935663034 -61343764366238376562386232326131316337353330303266383563373562363863666562323061 -63633161653833663136636630653139306233336431363238636166656133633366376231303330 -39386133633862336237653335626534643339303230366437303530316335313034656263343562 -62616661376266623162613132343239373966336135626363366439323434346332303531333431 -33333066616333373762663062363135663435353535393733313966616234383836626339633662 -31656564316336616232623832376266643565346338343535383430333964323561383238633035 -36366266656465303034626161373133346135646537643862386333363064626137653639306434 -36663932636432313139353730643138623534656635633262343963626164373664656266393830 -33306161333331393132353361323230636531646663323037643139393932393262323931643033 -34373164333232656664636462666262643362353632343632653237363030333461633165646336 -34653835326665666532316430313161343662653666333265353433633330393666396130306630 -39313263363066653732376462623638303235343230646662636331613333373433356331623032 -31336263663338613737343838656239353738363734633537613132343135336161366462313761 -38643463343830616534333233393031653437366466313333626365373861666138306263396561 -37353461323037313238646365656633313032653732366333616366616337643035303866313763 -32663263376136336339646364393038653838396262643864313266373366363662316335383130 -62666332323466343030366239363333363837376664643634353466376361633433343836633235 -31646561333534383139626233343266376132393566343439356336306362343736386431656163 -64653835353632363138633862353637383638373363643366643931316361343839646565383261 -36366531356361326338613335376465316533663161313065643063656133336461613063653134 -39366433333333366331306566623934303139326136616563393234353732376334613932343734 -65653863353438323337313235313361643437326336373038343835356236663462383831376435 -38373338666235313461623435336165373234336661366562663634666130313765623563623830 -30376434323131383137396234356261303332356335656165343562633536666365326439313266 -35306331646530353135633963633638306266393332373766323636373766653234306331316538 -30366636333036313630396631663966396664316362336366393734653431613565313337613162 -64363863343230373939356131396630313163303064333335636434363832373839323662336632 -62326464343366653231363532336533653533386565373937373038393966643437656137616464 -32636531623161356631323237663061336461313433333162616161633665626236396332643566 -39303332393139636465343266346630343363356336613163363334666666346361326666393465 -39353965666436623434633337303563363764626534353238633339313431366438373762336633 -38376162356363643336333638663036373833613063336162333738303663656338373465393638 -61626665303232653263643838373934346637313135363930633734643939356239653966633361 -63363135306631616631316563633635323737663435303162383936643433636635626665646464 -66613435663931336466393762366166366136343564666633346562313663653137393932346130 -66393338346665343264303933376536366131333937613533346331336430376636653934373133 -30633130656236376537323437373239346135643565306237356632316338363065326535303132 -62336535343337356561316131653635363766653566653461636335633066326164623136323465 -33393930303536376161633266616363376364366536303466666133346264316464326235386132 -32353138653633613462623563396230633233653031633736363032393163333039363037643062 -63366666326563613838313139643336313931633766346266653662663638346132636631353339 -66333666396461306630353831303238376136386666366335306565326430393237376536663761 -33363233373762663530356666643935626464376132373430363538623835333534636163616536 -63336234316430323735636232326434636131346338336239653636343339616164323739616663 -61663034633430306564356230386632333233353932323638633463383139306538623933316437 -61623739333264653232346237366138366630396134613030303539363364643464333834616330 -31386664316364636536333535343061363830396363383565333532343731333062373535383539 -36333330303465303063313737343933333363643264633836316439643362343536373739616235 -30653765326135303638303432356437376339393136656666666261333734623033646331396532 -34313662323662626165623733656561366265353334316366343733616633613363636539386238 -33323736363262376434623239646138336163373463656335643035636662646434363532613337 -62636236616265326664353463343163623132333536643238616235366330333938363330393035 -37343234623566376637363131653762663835343934623366353130393164393930323539386663 -66306165616563373637613534613637613063323837636534386331313038333738326463373864 -35666235643837373139343330646466313762323236383039363939663735323532376234333832 -37663632316636613631376238633565383534643937393939633634613538383134343564356234 -64616635373663386462333162323532353134336164333835336366376636356631303238333864 -31613839663436386163623534623663666466613031656338363734656636393430373837613764 -32663432666363383730316630363532366534646637323432393130656661356130613637313930 -39346335623364653534626134643435303665353633636138623331303739643461636362386432 -65356365666461653733653738663736616535303639343733303566343639643862663135663632 -38653434383862636637336238323339323739313134313061363938633862343433356538333838 -32376536363461616337366630643634666261396266366136336634333238646333623730653661 -62653165303832353431623131323430376538373136346164383332316330653335653930373564 -64393730383030356237313039343736323063303638313938383263623233373564636131383263 -35653634303038643739326637373666623433626338383661333036636566393932356166376339 -37643034323066343839623966623963623661663965383665636264346139643439613838613939 -64653465336334616339663535636265633939323435336663346138336234323838663562336634 -39366532633365386233633434316133383132326331303163396336353732376535356464343465 -62633932636537326437353431386236336239333036353139396232356233653533626533333137 -37633434316234303863636233356363653938353330666361653035366632633862306335633135 -61316563366335316538616262363838356436303264633366376531653938656163376536383265 -64343231616564316263333736653231636530346363613337613135653238393563356234626230 -613464386338646461623231356535396134 +32386533353933646332373535633237376435316365623339636534636137626464663564363336 +3331656466633733633965343365366134656365353332330a363838653035663365316435373733 +36613032613066626535656566306331373430616530373331363563326232323238613464643137 +3037646435336135640a366430323838613937306630353335653939336465323535306131646162 +64646361346633656438623235366361383935363935333333306238333565663634623935306638 +62353736373032326536383232323839346338623435626538343135383166653965613637306161 +65336361393665636562306330663137333230636163653630623730383437343836623330336463 +65646530306139383237363839333235303737666536356462653139653033343563356330643337 +64333165386334653964313531343362333038663137386137303332336537356264616631623230 +66663630373934323531316633636536306236623931666139313261356264353634383266363466 +36313565313864623063613039353833346261393230613438343432616362643339636239333132 +33636333376534353238626337663138333731303264313837663834666465666131383634613334 +33333265646566306361626435623166303166323439653035333634333864653862626362613131 +65303838363861653235333964306337376162633063643363366335646134346235623337333863 +63626636636235303964316436646237386331383963656333613533373963626632363736343131 +33326164343035386561623863363036633761656131306361326137636338373365373832326663 +36353365353366363061613231613861343566663437363163393331633230396531363932616236 +32613366366562346466393430613839663765346135666162326562313636303666656434326535 +64396463613630383564343034393365363663346663663961636634353661326637313938373065 +30623832353634626632346661633132346361396162343233623735386235323730313834303862 +33633539636537623335653333633834313639613163663839306564326332373963323639353162 +63336466346233613738633635323565653961356237306664666236623938323661343663353439 +62663238626534623330383137346436653935373937376165353032616533353930333565663063 +35376137666162383265393466386234323330376633313833326533393032626230316464383230 +38356133383437646430633930373962316334323166326534383732393961343564376136343039 +63326630356634393636636336393632386462356230336532613736663432316239313434353035 +32623238323834303638333835613664646436393638363865653037336439323861393766383061 +31386563333666386435636237656465303163313065363862343735623835666631626533383665 +39376334353063363332396166663061646436656262663936396136646333316563616264306461 +35616430336233356563333366653834323335626461633161383038616239353262613331363666 +65303364636537333833376534633164363232326332306461653864326335663961626166613666 +66373663656637663630613533646162666233646133643535366461316137353963313563623039 +65656464313261333633373665643838613965353564376163636365666430663837613830336362 +31396562383237376139613432336539323865346537646333303231353639666166663962346161 +35663638616330656232626437386437323237333664353861366266613961373433646263383966 +65333035386462623135326630346362323065656461306266363633396437313939653933356662 +33343864356465323931313932376538303234353361613330373664383436393130613565323138 +64653438333966643738613530663330346634313338306630663263306261383333626232343133 +34346433663836663765616339393761656361663264366464313263326666313131333535306638 +31643433616437343663663034383837643036653363306564353237646431306632353138633938 +35663239383433353433386436306362633866653065646630336439663539326434663435326663 +33376534303333636666366564393931303032613835643734393663653931643433346138653534 +39333833313634323532343463366237663965383338646236326131646563323663323238333465 +31643764306135363833393563333266313331613961303665633463326331333537656533383461 +35373063346137626266636632373538333334663363383938653166393538353934343633396637 +30323831653332323036613663616164666463323362323432303136313664343937643539313935 +31366538613830376639393031633333326433656464343763616335376339646637373939326230 +39323565366636356562363739366430323437373263343735346162633666333239643535356562 +34383630366135303534336366316531373565666264663765363733663531316530656637323337 +63366333306133313137363564666134646230623432373162393462653238333262326133633435 +35616434376431393261343035333637616662633366646439656237343234333266336265396165 +64386561353235343563613939653437333565646231663933343366623939313761326236336430 +65316435323561343165653531353161343138373737666566636336323963613766346363663536 +38313361336662626435333738306265333830396633663435346261376431346239373732346137 +38616162616438376432326130613663666365633365306265393064643466633035363136626436 +31666464643734356537373762333665336164393730376266353235343562343766643339656461 +35353730356632373738333933643466616234303963666365383936626138313733663766663464 +63306564663336366132623264393064303366666266306162656335336561313362383261313266 +65366564353761353531363933626330363737663735373163393962336632343833396333633364 +62646336616130643634366431356231656233333734666463623266623137366561343231343761 +64373136656563623839333730393633363233366337383866376430323339386562663030656533 +38663263653638336531356562643433313333353363653833373161373566376235356635333531 +62373963356663336163363033373337613365643937313839376435643531656637343337353635 +38393733653437623164666366656565353765623539303530623735636662343033373163626135 +31666361363330643262363130623038393861633736353966623631323234316335643536363831 +65653632343736306637326533613931626166363039363638643237646433303236363564616637 +37633162386265656261356466663639343662303232653435323565333431316234663765393437 +38333339386636633466363561343639343762623166313939626139363034373239626539353266 +36383862386432623738633335313231343839333337373365616166333833626639613639323233 +35343837643662313532656435343532373037316434636636383332626461663664376362316466 +35626633663866643566663130336239653737663961303436633661346263313062306331663634 +33373766656130383936386631653932373736643665356238326337326563666133613435323839 +38633632396133363536393730636561663633656365383835383639316636646335336265323432 +66363934393166363566643764663634316166373065646563363636383633663364376663653431 +64363433666631346236383431343063393630663933666364326465626532633932613061323438 +36353865346363316331636639333538653438333265666565383737323734373933393164626432 +34323963633263386233373861343130396233323062306266313238623731313730386530663739 +35343937346563356630353163313232323461363463646635393133646563323932653938636361 +30323632366561333038666233373839626233373533353835333366323134356436306164386138 +38663664333561323934393666396538633532613331303863323738646330323936303462663862 +32383531613539326532383331313963613463613230396334623765333334643937306166643634 +34336533373061396566366465346238613862313465316332393762663038666533643032356463 +62393235353635613935376233323432333262373234346263343863346561636135353130383137 +34333830393562636332316634353931653662363731393062333330393234636333333462333637 +35383661316234363038363135316639363830303234376438313438666533653736333962393461 +36346435636437383262313462616638626430383532346266386461663130393636633139333737 +62613963383334643264313034373534383433363533633139663863303361343964633562363931 +35306337316139626433346234303534303139383233373039343866653933666336323335366636 +39333338396139376539343136376334313961356265643637303864386661356435323138313266 +37323439636333653437363437313830343761313136356432663362396131626466346362366635 +33383630613333663137666231623062323162633861656261396132653064626536616561393531 +63306638376565623561376537386435653461663335326634643062666339333263356562316232 +30656430313130366631323766376166636564633539656539356666303531343630653633316135 +65376534613832316532366462316365366135623563656261353464653764616139316233666133 +36356337663331633464376532646664303663633466303135663961366332373338353437383461 +34623163666530386334623433383137373235356534306662396630616339326534656334373966 +63373665663435343531356264646464316133366333343630346165303939396232333834323639 +37383333333635353033613838383637346439313966393166356235636134306231356665333238 +61386130663564333731303466376564613535643862326336643865396438306332383537386461 +36653661623163396465303061663437623663373964636238306439333837623634306231656431 +30646230366238626339313165346533666264393437366665303564316135616234626663616632 +65656538623531396333346238663162363663346561663461666366666636333935653536653033 +64613330613639366534653334323631663436656333663764376661363137326231343030666438 +61663237316437396535353766623666333833343037373262386232376239383861303765623034 +65376661366364373036653837336135653530626663393264303966613263353634336166393164 +64356631633961383636343361393339353165343161386635316534333335303938326430383133 +37326562336566616362363166613361643436323838316135636564363632323362323662376630 +31356538343739643437333332306536646531396433313439343865303235313465623036303064 +62636335616335653231373831616164333335373437663936373935396537333836626139333463 +32613337393832653336346136663261656338393230313839303335363639323834643034666337 +62306330396430383130323063636535373533343438346535373463663265393034356664316235 +32393533346436663439616639396439613931386437663166373065356339326331663532633733 +39646639363466316462653061353938306538366636393164383935653864633637623734323061 +35636130326430633663333262383737646164663639336566336439343534313864336133633232 +61323165663935343866303961363231383337623037623638643265326631363937653638616136 +39353234626539623537383163363734383061323031313365383961623837393762393038386436 +34643737333830313539363531653232396562616466643838653438623330333964653464316433 +37346562363632353031653838303063653562623835313534306438313932386464323132323063 +39303339303965303434383063633765343636366438343931663735396366366131393162373661 +62646132663532386430363435393761366231363064363831633734656661353233633532363837 +62383539323035396338376162343437363836623563643730313063643939393864316135396237 +65393334656436303635306532623963303462616539343737663635343834633032643863303937 +64646464643737363937623834663164656635366334333438393465313337313365393430393235 +39333263396663366230346132353661643935653963613364643763313265653566303637393033 +30343131613231633331356666346630303965626430306562393439396135666138303638353634 +38306334376232323336393438623734376434333032393366613836313539623566346361303139 +32303037646362303461393561623132353234303535343566666639386361396332393566643535 +39353338646331343934313933653037356238643863356336346263353037326639373837363666 +34656131653863653831656630636330363462636432346631303433396461333366656434626263 +32333664356634656261306337376633653939316330333365626238333562363463316332363565 +31363632356665323166643033363163343461326339613738623033363866306532316436353737 +31323235303735333065383535316138616433633036333233323566613762343866353935636538 +33303866663135613265383337616239386234323965386637616237396436636464643663643063 +33626166373239313837323230383664663538396634626532333930663338316462636432666430 +65363137383232653834666539366362366461323738386136633861313266643735366361373832 +61316334356631363264653164353065663163656463323839613330393832383065343566366436 +64353366323963643733393036633831313331313963353263313930613563643831313161646534 +3065376662316537363766306135646663363333316232663836