diff --git a/SeaChest_PowerControl_x86_64-alpine-linux-musl_static b/SeaChest_PowerControl_x86_64-alpine-linux-musl_static new file mode 100755 index 0000000..732c45a Binary files /dev/null and b/SeaChest_PowerControl_x86_64-alpine-linux-musl_static differ diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..72e7995 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,4 @@ +[defaults] +inventory = inventory +remote_user = ansible +# Will use yubikey for ssh authentication \ No newline at end of file diff --git a/decrypt-vault.sh b/decrypt-vault.sh new file mode 100755 index 0000000..ac37c9a --- /dev/null +++ b/decrypt-vault.sh @@ -0,0 +1,7 @@ +#!/bin/bash +./helpers/get-passwords.sh + +ansible-vault decrypt vars/bootstrap.yaml --vault-password-file ./.passwords/vault +ansible-vault decrypt vars/vault.yaml --vault-password-file ./.passwords/vault + +./helpers/delete-passwords.sh diff --git a/encrypt-vault.sh b/encrypt-vault.sh new file mode 100755 index 0000000..4ba41d4 --- /dev/null +++ b/encrypt-vault.sh @@ -0,0 +1,7 @@ +#!/bin/bash +./helpers/get-passwords.sh + +ansible-vault encrypt vars/bootstrap.yaml --vault-password-file ./.passwords/vault +ansible-vault encrypt vars/vault.yaml --vault-password-file ./.passwords/vault + +./helpers/delete-passwords.sh diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg new file mode 100755 index 0000000..e7a69ee --- /dev/null +++ b/git-hooks/commit-msg @@ -0,0 +1,16 @@ +#!/bin/bash +# TODO: Clean this up so it iterates over the vars files +if grep -qE "vault_decrypted: true" "./vars/bigboi.yaml";then + echo "The vault isn't encrypted, run './encrypt-vault.sh' before committing." + exit 1 +fi + +if grep -qE "vault_decrypted: true" "./vars/bootstrap.yaml";then + echo "The vault isn't encrypted, run './encrypt-vault.sh' before committing." + exit 1 +fi + +if grep -qE "vault_decrypted: true" "./vars/smolboi.yaml";then + echo "The vault isn't encrypted, run './encrypt-vault.sh' before committing." + exit 1 +fi \ No newline at end of file diff --git a/helpers/delete-passwords.sh b/helpers/delete-passwords.sh new file mode 100755 index 0000000..4fc51c5 --- /dev/null +++ b/helpers/delete-passwords.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +if [[ -d "./.passwords" ]] +then + rm -rf "./.passwords" + echo "Deleted passwords" +else + echo "There were no passwords to delete" +fi \ No newline at end of file diff --git a/helpers/generate-password.sh b/helpers/generate-password.sh new file mode 100755 index 0000000..da87635 --- /dev/null +++ b/helpers/generate-password.sh @@ -0,0 +1,2 @@ +#!/bin/sh +mkpasswd --method=sha-512 \ No newline at end of file diff --git a/helpers/get-passwords.sh b/helpers/get-passwords.sh new file mode 100755 index 0000000..1224ee5 --- /dev/null +++ b/helpers/get-passwords.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if ! [[ -d "./.passwords" ]] +then + mkdir "./.passwords" + echo "Created passwords directory" +fi + +pass wholteza/encryption-keys/ansible/git.nmlk.se/vault > ./.passwords/vault +echo "Fetched vault password" +pass wholteza/network/linode/linone/ansible > ./.passwords/become +echo "Fetched ansible user password" +pass wholteza/network/linode/linone/wholteza > ./.passwords/bootstrap_become +echo "Fetched bootstrap user password" \ No newline at end of file diff --git a/install-requirements.sh b/install-requirements.sh new file mode 100755 index 0000000..a24b592 --- /dev/null +++ b/install-requirements.sh @@ -0,0 +1,7 @@ +#!/bin/sh +sudo apt update \ + && sudo apt install ansible \ + && ansible-galaxy collection install community.general \ + && ansible-galaxy collection install ansible.posix + +cp ./git-hooks/commit-msg ./.git/hooks/commit-msg \ No newline at end of file diff --git a/inventory b/inventory new file mode 100644 index 0000000..e2a5c95 --- /dev/null +++ b/inventory @@ -0,0 +1,2 @@ +[prod] +git.nmlk.se diff --git a/main.yaml b/main.yaml new file mode 100644 index 0000000..3be866c --- /dev/null +++ b/main.yaml @@ -0,0 +1,30 @@ +--- +- hosts: all + become: true + remote_user: wholteza + roles: + - role: shared/bootstrap + vars_files: + - vars/bootstrap.yaml +# - hosts: production_main +# become: true +# roles: +# # - role: shared/update +# - role: bigboi/users +# # - role: bigboi/network +# # - role: shared/avahi +# # - role: bigboi/nginx +# # - role: shared/ssmtp +# - role: bigboi/zfs +# # - role: shared/zfs-zed +# # - role: bigboi/samba +# # - role: bigboi/sanoid +# # - role: shared/powertop +# # - role: bigboi/apcupsd +# # - role: bigboi/libvirt +# # - role: bigboi/kvm-homeassistant +# - role: bigboi/docker +# # - role: artis3n.tailscale +# # - role: shared/various-cli-tools +# vars_files: +# - vars/vault.yaml diff --git a/readme.md b/readme.md index f395431..d080a60 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,27 @@ -# Readme +## Installing and running + +Prior to following the steps below you need to set up the ssh agent to work with the gpg key from your yubikey and also the `pass` password store. + +1. Install the required packages with `install-requirements.sh` +2. Run the playbook `run-playbook.sh` + - The script automatically fetches the required passwords from the password store + +The playbook requires the user `ansible` to be present on the target systems with the yubikey ssh key as authorized key and member of the sudoers group. + +The required password to be set on the ansible user can be found in `pass wholteza/network/lilleback/ansible/ansible`. + +While running fetched passwords will be placed in the `.temp` directory. Any script that creates that directory must delete it afterwards. + +## Making changes to the vault + +The vault encryption is managed by `ansible-vault` + passwords from `pass`. + +Use `./decrypt-vault.sh` and `./encrypt-vault.sh` to make the file into clear text and the other way around. + +There is a pre-commit git hook that will prevent you from committing if the file is clear text. + +Be careful because even if you have a non-encrypted version of the vault staged and the unstaged file is encrypted you will be able to commit atm. + +## Generating passwords for the vault file + +1. Use `generate-password.sh` to generate a hash of your password. diff --git a/roles/bootstrap/files/ansible.sudoers b/roles/bootstrap/files/ansible.sudoers new file mode 100644 index 0000000..e8c5b14 --- /dev/null +++ b/roles/bootstrap/files/ansible.sudoers @@ -0,0 +1 @@ +ansible ALL=(ALL) NOPASSWD:ALL diff --git a/roles/bootstrap/tasks/main.yaml b/roles/bootstrap/tasks/main.yaml new file mode 100644 index 0000000..a8cbdb4 --- /dev/null +++ b/roles/bootstrap/tasks/main.yaml @@ -0,0 +1,22 @@ +--- +- name: Create ansible user + user: + name: "{{ bootstrap.user.name }}" + password: "{{ bootstrap.user.password }}" + shell: /bin/bash + groups: sudo + create_home: yes + state: present + +- name: Add public ssh key + authorized_key: + user: "{{ bootstrap.user.name }}" + key: "{{ bootstrap.user.public_ssh_key }}" + +- name: Enable ansible user to run sudo without password + copy: + src: ansible.sudoers + dest: /etc/sudoers.d/ansible + owner: root + group: root + mode: 0440 \ No newline at end of file diff --git a/roles/docker/tasks/main.yaml b/roles/docker/tasks/main.yaml new file mode 100644 index 0000000..b7a06b3 --- /dev/null +++ b/roles/docker/tasks/main.yaml @@ -0,0 +1,120 @@ +# Frigate directories +- name: Ensure frigate root dir + file: + path: "{{docker.frigate.root_volume}}" + state: directory + owner: docker + group: root + +- name: Ensure frigate config dir + file: + path: "{{docker.frigate.config_volume}}" + state: directory + owner: docker + group: root + +- name: Ensure frigate media dir + file: + path: "{{docker.frigate.media_volume}}" + state: directory + owner: docker + group: root + +- name: Ensure frigate config file + template: + src: frigate-config.yml + dest: "{{docker.frigate.config_volume}}/config.yml" + owner: docker + group: root + register: frigate_config_file + +# Grafana directory +- name: Ensure grafana config dir + file: + path: "{{docker.grafana.data_volume}}" + state: directory + owner: docker + group: root + +# InfluxDB directory +- name: Ensure influxdb config dir + file: + path: "{{docker.influxdb.data_volume}}" + state: directory + owner: docker + group: root + +- name: Ensure photoprism config dir + file: + path: "{{docker.photoprism.data_volume}}" + state: directory + owner: photoprism + group: root + +- name: Ensure mariadb config dir + file: + path: "{{docker.mariadb.data_volume}}" + state: directory + owner: photoprism + group: root + +# Docker service setup +- name: Install requirements + apt: + name: + - docker.io + - docker-compose + - python3-pip + state: present + +- name: install requirements + ansible.builtin.pip: + name: docker + +- name: Enable docker + systemd: + name: docker + enabled: yes + +- name: Ensure docker project dir + file: + path: "{{docker.project_path}}" + state: directory + owner: docker + group: docker + +- name: Ensure docker-compose file + template: + src: docker-compose.yml + dest: "{{docker.project_path}}" + owner: ansible + group: ansible + register: docker_compose_file + +- name: Tear down existing project + community.docker.docker_compose: + project_src: "{{ docker.project_path }}" + state: absent + remove_orphans: yes + when: docker_compose_file.changed + +- name: Set up project + register: docker_project + community.docker.docker_compose: + project_src: "{{docker.project_path}}" + state: present + remove_orphans: yes + when: docker_compose_file.changed + +- name: debug + ansible.builtin.debug: + var: docker_project + when: docker_compose_file.changed + +# Restart frigate if needed +- name: Restart frigate + when: frigate_config_file.changed + community.docker.docker_container: + name: frigate + restart: true + state: started diff --git a/roles/docker/templates/docker-compose.yml b/roles/docker/templates/docker-compose.yml new file mode 100644 index 0000000..73c311f --- /dev/null +++ b/roles/docker/templates/docker-compose.yml @@ -0,0 +1,44 @@ +# ufw allow 443 +# uff allow 222 + +version: "3" + +services: + server: + image: gitea/gitea:latest + container_name: gitea + environment: + - USER_UID=1001 + restart: always + volumes: + - ./gitea:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "222:22" + labels: + - "traefik.enable=true" + - "traefik.http.routers.gitea.rule=Host(`git.nmlk.se`)" + - "traefik.http.routers.gitea.tls.certresolver=myresolver" + - "traefik.http.routers.gitea.tls=true" + - "traefik.http.services.gitea.loadbalancer.server.port=3000" + + proxy: + image: traefik:v2.8 + command: + - "--providers.docker=true" + - "--entrypoints.web.address=:80" + - "--entrypoints.websecure.address=:443" + - "--entrypoints.web.http.redirections.entrypoint.to=websecure" + - "--entrypoints.web.http.redirections.entrypoint.scheme=https" + - "--certificatesresolvers.myresolver.acme.tlschallenge=true" + # Uncomment this line to test: https://doc.traefik.io/traefik/user-guides/docker-compose/acme-tls/ + # - "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" + - "--certificatesresolvers.myresolver.acme.email=zackarias@montell.se" + - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" + ports: + - "80:80" + - "443:443" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + - "/services/docker/traefik:/letsencrypt" diff --git a/roles/users/tasks/main.yaml b/roles/users/tasks/main.yaml new file mode 100644 index 0000000..1a68275 --- /dev/null +++ b/roles/users/tasks/main.yaml @@ -0,0 +1,29 @@ +- name: Add groups + group: + name: "{{ item.name }}" + gid: "{{ item.gid }}" + state: present + loop: "{{ nas_groups|flatten(levels=1) }}" + loop_control: + label: "{{ item.name }}" + +- name: Add users + user: + name: "{{ item.name }}" + password: "{{ item.password|default('!') }}" + state: present + shell: "{{ item.shell|default('/bin/noshell') }}" + uid: "{{ item.uid }}" + groups: "{{ item.groups|default('') }}" + create_home: "{{ item.create_home|default('no') }}" + loop: "{{ nas_users|community.general.lists_mergeby(nas_service_users, 'name')|community.general.lists_mergeby(local_service_users, 'name') }}" + loop_control: + label: "{{ item.name }}" + +- name: Add public ssh keys + authorized_key: + user: "{{ item.name }}" + key: "{{ item.public_ssh_key }}" + loop: "{{ nas_users|community.general.lists_mergeby(nas_service_users, 'name')|flatten(levels=1)|rejectattr('public_ssh_key', 'undefined') }}" + loop_control: + label: "{{ item.name }}" diff --git a/run-playbook.sh b/run-playbook.sh new file mode 100755 index 0000000..9b8b9fc --- /dev/null +++ b/run-playbook.sh @@ -0,0 +1,18 @@ +#!/bin/sh +if ! grep -qE "vault_decrypted: true" "./vars/bootstrap.yaml"; +then + echo "Decrypt vaults using './decrypt-vault.sh' first!" + exit 1 +fi + +if ! grep -qE "vault_decrypted: true" "./vars/bigboi.yaml"; +then + echo "Decrypt vaults using './decrypt-vault.sh' first!" + exit 1 +fi + +./helpers/get-passwords.sh + +ansible-playbook main.yaml --become-method sudo --extra-vars "ansible_become_pass=$(cat .passwords/bootstrap_become)" + +./helpers/delete-passwords.sh \ No newline at end of file diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..5222265 --- /dev/null +++ b/todo.md @@ -0,0 +1,4 @@ +- smb passwords are not updating + +- improve structure for users + - concat lists? diff --git a/vars/bootstrap.yaml b/vars/bootstrap.yaml new file mode 100644 index 0000000..ecbe497 --- /dev/null +++ b/vars/bootstrap.yaml @@ -0,0 +1,37 @@ +$ANSIBLE_VAULT;1.1;AES256 +39383364616339613837626566326631616532613266333035366531613464623935643136373464 +3038653762666136383332326663336135626561663934620a306366616362363363333863663465 +31323065306464636237396236373939396162653066303562666536306239616461303261353538 +3236333837323962320a633935653965363336333930643763633838346437653665633965373964 +32643366363236353837396436646433383137633535303934323033386139343931613663383732 +62376133626339393864663162343461376433313136373831353335316636663733363139616566 +61333264376432343961626262313839323366396165353865653634306366373433623861643265 +35373437666231356633663461643664353666643264626637633431363035666232616539343639 +34333166643037303132613663313332393762336538646339643765633965333231383435653165 +39356136383532656534663532623864636566383865613930316361643130643734303537346564 +64633530353965636231376234646363623137623464383737333231353133313161393737366663 +36653464333662353733653266376231313961306636643835363935633366326461633239383264 +31316631356534386335613032653536363964333134333535343864323262646439313966643263 +37353064656230623833613936326664323738646165396432656532316530343230643239636565 +35346261303439623634393961333563343663383030336461613261326135653763656339636538 +39353631316533386136646237363435363630646263386430363135336362333531323931383965 +61653132353432633531323436353836306630623765376437333537323937663366623466643933 +33343537383938616433393562646134316638366435653063653831636166643832643363356530 +61343335636630363862633765383565643830343462313230636364326439626562303832633432 +35663530633662303032653730313761613634386265316564366633626330623635626436366636 +38396134386463626365623433623561383662623839356334663934393164303833393230323363 +64393135616535336164626339643861623738613338616530363466373664363237653737333233 +33373034346161333036393137323933353636383031646231623039346637653263366338376164 +66326433373238646535643535633939666631396466663737633931643531333634316466613033 +63613037346139663061356230393131343466623762313131326635353366313166623165353636 +32656233316666323533393765336561336138303163396631623264303237313761653663323030 +66653232626664616564363264396434306130363139613937383630346566666232306266653863 +37383030636330313239613237336264396134626436626464326236386536333361313865363165 +65376334353462616261326563656166623365363066333636616535653361323965633661333238 +30366463386133303661656335393333343734633731306164643031343734353634333938643766 +61366361616639616161383534373632323630336639353535383533376366373934323031623565 +31316236646434623235656266643566363336346639613466386338613561396262383630383563 +31346461363861316234316330633939623162613730343638376538633061616632623638353263 +32646665663462323565623537323161663532333230373837356235303963613234636562323263 +37336437333033356239373136386335666234393563616537386634336366383261346362303665 +66636165316231633762 diff --git a/vars/vault.yaml b/vars/vault.yaml new file mode 100644 index 0000000..50fd3f1 --- /dev/null +++ b/vars/vault.yaml @@ -0,0 +1,313 @@ +$ANSIBLE_VAULT;1.1;AES256 +35663239383464326563333164306264656632363761653263353635643630373034663239623961 +6539346430323066366363666630633236643536333464370a633337373339306437666331633365 +30376261636139393333626565656638336161636263303130306230643036333362313535613164 +6365643464633139350a383131326266346564346235653166343334626533316232646331333738 +36646330616661323436616538336463376561633963376430356434373763616539373234363839 +63306665336132366338346264356565626462313633663733653830303738393061323364613636 +38353435643164346235353931353338303235386533666264633237393433626332326532653761 +30313462343131373237383064396131613764313831363236653737303833626138376161363330 +37646433366266653436386630363366373135353232303538633038626461643864656133313735 +62303266666566343533623434656663346337613261336231306631396637636662333132363864 +39303065626633353865353830663430393361343736316534306633316636316365666436386562 +34616265306663316438333634376534373137373965366439313864656135303132636134623437 +37336231323036376466613634613565376533636563646537353038633663363065323839383338 +37343533353461343761616636623061363762666338393762343130336539333037356664333533 +38353634346430323339616466326638323633626461306266386631353533626261666534633839 +36356537326130646535313439313137633430333931646561383730633536636434303761393962 +39326432336235613561306135303762646466393561663963613162656538616539613134366236 +61306432373434663033383438343864343535623865383539343361373533336561316334643166 +32343065626433363962383038346239656237653063393935343932373666666561666335326239 +37366631346534323463346664346332383732666331643435656537366564313963306331636232 +39393032313834346539663866386161663762623632376632313132383135353538383533393839 +37366532393237393132326666333062623631663664393534636634323431636633376638616533 +39363435306637653862663366336233623637356166373133366138663961396263646331346539 +32373866346139643136373030353535646132633335313432616338306637666630383938653137 +36366439343735373864356434316464616465343933636135613562386234336164313663356462 +38313832303865316262643761333165346333303564373939326135303636616165653035643164 +66663039636366643661303863303437653638393539616130383437656438373066333831643032 +38373935353163336630643964613862656438323131643764623739303331613138373631386537 +32663363313361363163323033653039303630323931383563333862643662303434656263633339 +64383838326135666266663236653161633863326265306566616231333463343034343463303434 +34373833333436663932653438303937373738323363333534373063313939643534363265383936 +66613561313763633637333335393234393262353766636362356538653430326232383734616132 +64366365393161313766643065643763313732313865653436613731623364626261646232383065 +35366364313838363638346365333465333339643234323434383562396630663436343965363135 +63353664323534303263316433653563323864396366666236313138613537316534393261626430 +35316538303938326634636561396465306566346238663266626163663962383064376263356239 +64323361306238613830313039646539666465646563613537323038326264353263363464336264 +35376536333933353434343866663331333665346162343137346235373465363066306164383135 +66613464376136326137353037386136313030646138376338633232636165656132386263333430 +61626461306363633563383965363438666231303734303432646133393163306339336364626230 +36326439366433356636323539336262366266316364373039303963626433653239616163623933 +34383136666264626638353337316264363537336161623838303834343333623832613630653937 +63613539333334383637326237373965363535666362393336363937663265643537343865363936 +65383433323964363461336465653165666430396538363637306266663537346236663362393638 +61346564646334326464373936613637663930366663663433393264653631373139336435303063 +36393464323566343335303464663535336234386634623766366163663036353639666332396331 +62613736383738376666343263313039383564386238353761366330613965363365366563363065 +30643964633664383465396561323932663966646139303533636138336264336334343663613233 +63373765313263623239636131343030346438306533376562663365313532616266636637393634 +63653464326138333862383864353838613836623862316437303262316338616335626566616266 +34373962393634653665336130346438373265613662323138306265623666643762326333353936 +64393931353835363137393063343461656239653265376338356430333731376339303935343963 +34653830336336666337616462333336313132646339643164373565303539623033643935313038 +33383134616531393561643838333731393764363165333733626166383635366636306462616566 +62306164343333353462633534396362363236633231613339666335323137363338656462363338 +65646334366636353235656334326239373464333961333239373561303866323761636632393330 +35343463623533393261393931313932396664326164633036323264643365306330373263383835 +64323864663939323537376462393534643362306532653330353835613664396161306163376132 +64303732613136643939383165356532613832663066363839623739386161643435336335336238 +36373934333265313933376661623766386561316331333733363637343336363337363261383130 +34646266643161626137306464323462303766343962316563656338613538623131613361643930 +33326166666562353836353537663434323761343266313636366436656661636633346636663934 +34376266336533346231643831346161643630376165333736343430663738633965343234303131 +34326237333636633134366463343537363632346561383137643437376332616462376265306462 +32636637356665376139663337323234633563373437383063316334303336646134363766613363 +30333939306663646532393938373932646239316662626461313561353065656437643463666662 +61386435623235376332613039396164323366343664313132356137613662663163656630396536 +64323136666563363339306536653438363230376564366436663161376230313336373764613461 +31363861326631393661343031386532316139366161333437363665383434306665306333666661 +34346331613465663330393535366639316433323634363531346633643737636235356361616466 +35386236313433623366656237343464623836613131646432643330623366353666336534363935 +34336661336237323531363136636135306664323566363034633363393665666430326465346462 +33336661363831333837623539643135333632313038626533356333336566343162386237383737 +65356239343138343131316132653137623934373165396366616161373364396636346364636664 +64656338313831336361316539616530376438323430306433616532366165363832346665333136 +36376566646533346261653631353039383135653631626135323736643966323538623366666266 +65393632383235646261386439313763646466313364623839646631663137323230666135393938 +31623464646234346563366537376430303534386461656366303538623436376132363464313930 +39636261643939353431616635323764306537653539363939636130633538373739613234636331 +30633266346537323136613163363933313930633661373164393035613335613230353762643034 +35323631303337643365313635636230643137363031383061363931653131663930363137363435 +63366530383363623839663134366365323235313836356461353832356562383437353063306463 +35393539623263623531393133353366623332353365376365336431383339316461336265653839 +38666631643133646431383738626337386163643732653933323433396138623539623564373931 +32383637323833353631363730376564306137373031323038656534396339323030353061383234 +62336434616239333464643763643432653434353061336534656234313339656331333563616338 +36393033636363366434303561663632663961343861663431363033316663663835626539613538 +63383932323235346562636164373862666165633932396536313836626464616232376139353865 +62656231353231636462396237376630396138376432336265613362363332663336633165363963 +32383336383066323032366236356536366466666166326165316263303933393634336534333163 +38636366633135383930336363323161396539383933623331393235383538393931656666333838 +39393931373935323436393035333934316635653365343731336637356666326334663461376532 +65633033363835346230333436316664623366643836643730636237383864646131326265303638 +31396636366238356433633665366534616433303633353135663436363365313161353537393562 +35346563383436323661306338636231383937666135346362356239323835303130353037386230 +63373336636164383465363633343962376435633739313233363132313531366535643963636636 +32653938633866303430323030653832646463373261363262646462343131616338646632646334 +35653330623564633235656262383861386332346265393436363262363235656661316431373239 +61623533346561613435346433373862353035646361646630333537396561623062613739343139 +63643364666665393034316630316138336139386666313530373363623535643738643961396138 +31346362666361376633316537306332323833373263656237653464626439333834353739396432 +63613637383965383133336261626239626633316561346234643935353431353566326262363139 +65393731613638663837346661386665316637626561653866353832383964656634636561333434 +38313237326635656664343062633736383262626433613136336566313164613633363962303066 +63316165613830626139633538346130643462383737373565383435653439313032356430643038 +30393638653434623566303932303038613033643965373737383765343362333835396661393162 +33623835316537353331666139636165343561326238616635376438333739663235653339646639 +34306635353836663939613564303739626138343131333536363537613637353063386535396234 +61626333303665366463326363666439343865633164343630626233393065623162313963653238 +64363336393630366463666337346131303234633835303831323562653039626437363830386363 +30366136383064343237343862636235323837356536366664343166366366666663613339373065 +30653765356537346666383738316231316238613732653163633761633264376336613339613964 +62623638306436333266383937373939633762313533353366333461633763376566376166623531 +33303937346566363563366561363363346631353262663236303364306630343536633034343066 +65316662326130303336303131393966663962633632633162656364373637386333333530623631 +65396565613562616132626238316666616262343562313033623537666163363862316663313337 +32356438386633636437306530613934353539313137333238626132316439333736636437633461 +62666431326565636234343263363635393937343436653630653561343635323934383437666636 +65333830643031636564376661373538353138396336613134333332303530386466653538306466 +62383934613131313565646434366338653062633839396664376664623662366432633763333831 +30643135393434313561356565656133336638386532353332656133663137643161663534353661 +35663964303535326633346564663464616338373337363632316264333665613962393065646138 +31316535613539306233343533643130656266623161313436666566386166303635643037653536 +33336161393064613733633262623932303230646137313464366433346238303533353665393665 +32623731613632386265313831656638376565653236363231393462366162663933386332333861 +65633933363838393730623165666164633762663537663238643530646337383731383663333864 +38373939373562313330373632636238346532616635323362626431356137636661343536623532 +32666335666434373462356366636163633431363530333233653430333864613266393964373463 +30633537616262666365623137313164346231353438346566636238656332313331663433343839 +36663138353964346662343030396630633839333965363836636462353361623635663136313731 +62323564383933653966363233643566333132333037373363306239663361333336323366343738 +63626137626233303732336165326231313064323733393635633133663237303534316136313630 +36333530303764393035643163396630636561663330636662313639396531386635343666383138 +33323431646339303964313538633035313536373931383564316261653136333861393935376432 +36356139326233393536306261653031353665336535653766326364343230616531643038626539 +62303033326639393530313066333063303639653630656538616234626465366564323731626462 +31376533643637656363313537373664333864386164663464613631376661643630663938383637 +63656537636437346161636165386166626633646166346166623039303737666663633037376135 +30633838643765613831336132623461653031613937353765343431623134353938313538303739 +31363833653432333237633539666333313264353239363566346335663634333031323761613861 +35383863663566356133366234643763646161623761646364626337363634323762633264323638 +33383837363131323230643664353730633634376530393064366264613332313837666165363862 +37316430343063623032306432363638626562333331366131306236366337343734326436383864 +35643739396533363161343764643631376563333931656335363063653833336563306438353432 +34353633623764306565646531363031616233343565323063653133333135393332373262366431 +39356261626463326436333066666433646461353333353763373664393462653737666135336334 +30646338336136626438376564316530383031323236376364303734383738373431323035653061 +31663938393566633261356266623762633066363563356135333161383965396263633262353234 +39646334343238313635653765623234623436366565653961326163383539356134356430666237 +31336466303332613634393939613633356534656166323533303839356438366662623361633536 +34333931613762366239626538386130393435336365343233323066363637646238643438623261 +30656637333237313166333666666566303362653566303462396533316165633964346665623137 +66383365653365386563393835383962656430356232616666323166386638333236333362643866 +32333566633761663463306535333361666632613735656139616537663563613733373336343164 +35363738633535656535323136656637333330636337333962303765353666333961396434393138 +37363434643766323639396631613935616236346339383530343737666463383664373563353935 +39373165386162343634356630663835666261306637663234623930613532643439393531366336 +36373361393037613533386430623438346439393664336464363564646166313533353762346532 +33356533656364336438646363613737643834613137343666343465376537366337613839373031 +36633836643734386361663861303266353334393035636331616236373532393862633936373633 +37383634633861336264353430613931326337363665626364643839373263373633396239633961 +63666566386338613436656437353666666165373832326563396630613235656236626165663664 +32333464636461613238373564313137613265656634646139663730646135313866356565333561 +31303338393536653164623261313466386331613231643461626530323065346434353964303966 +32613137643939333435373938353537343061353639663838343931343530353035336366626166 +66626635336231373136366162386563656431323661393737653339663066393132313231323133 +63643837363731656138376136303134623335613830303138366538316232383565386434613335 +33666633303135313238663237643234373434313132333665346663643862383436303063626133 +66653132363938316337653735636138366163613266633663396666633463636531376330373433 +30393134363039313435646162616432643165626135343939373936626230653134393063613939 +37336639323133306463363035383865373630653833383265613164303262643737313731353235 +30303135343938326432663030613265366136396461646465323330373537613466316439303531 +30653562376230346563356336376135323538336564396563313966656639356331386263626432 +64616639643436646434633532363239666136666333643535303263323737663332303931366239 +37323338303361666664306362376165656537653034313436386563353531326534643630656131 +33306364343962383334636664316335633937383766653265373334613530653935363431333362 +30383332336339306433373266366137353066666533636261393835386230316535343437306231 +36653162643638653138633363363561623330356139613666343564313563623336643937346466 +64643963313831346465313138373462353331636665666332316163613337663435616665396330 +36663539633763323835343862626361363030653566373334656362613636383234623235303538 +37666163343466306365633863313332303661373234653734616439306537663563636239613437 +36316339633034663066633538373666353834663730656632626466656639396331333864386666 +30306334323139363866616432386366316361383935393863656138393731383163373863376630 +37396335303930623136343463383634366636333138363562623435363639373262373735393035 +64313831303737386566653362313639623261653034303734326433386639633363396163323033 +36653765396236393238313964316530363539383431346261643234386531383132626565666233 +63663436613239646135353331333338373236383262653932666135323963663039656535313761 +31363538323338366465633233333833323438636538356132373133383031373734633039643838 +30383035623631313935626563336265626134313937636434386461653930623831343332393465 +37616338336461313839663737393637353663343034306366303535313538633036663665396366 +30353237316331623930316632376534313236323633613565373934366362393337666431353434 +37323032366433316137653761313338383962396564646132643735633334643034323837623130 +36666262323135313430303434373830666464363734343931343338313066303162366439353833 +33363935353637376666336562656365623466613838643632623434316238363435323535646132 +38333363383835373761633265373465376131383265313733626662353833613662653535396432 +65376535633530316464643938306436653935613264656131646233653133323035346630623663 +38343162323164306535376330636538663839313937326638646231336231393035376165376163 +62373030636136636532626334353335653266643763623234353061306437623931343464306164 +34333835363962633964623363366466636635363664613238333662393536353561663937653065 +37663062316461643161653464346230656262333035383665346438386464306462306135366230 +39383437396262393930353737633962343564313338353034303362366637313065646162393161 +39333461616339663334356130666630653663616563663835376330633531346430616135666139 +39306336373366643035643163363130613538393634326330373134666563613933656666326463 +63663236383464636439343832373839323966343766323639393038306435333738633639363164 +65356663353139366263333661666438323165336162336439306263386435356161306162363136 +62366638306331373634316636386239393164623662623633613366656463363533663830313936 +35333233376664353365373235326265666639633366653037373135613136633065303465663037 +34333439346436306365643630343338303533363036633361383036313362646332346530663538 +36343736323163363261366331653862333034623663303261303064396134333738356562643161 +62306431393039363263323932663437313864623266373163386337303438616461393536303561 +34393466666364366434313430396636353061336263356134363763336133316330303664353765 +66663964363539366232643636383438396662396461336165616237666565386462376365373865 +35356262343034363439653564633762303965623230386239363538636134386661333732656636 +35653937353235333434313131323931376630626132373561636236613936613539626263316339 +66303433626133326262633632663765323137623135326362323531653839653835393034656662 +31653564303830623433346533323937373863613234383138653339303036633039386237393663 +35373638656439356164333031613166336435323039633164613937366239393736343039323964 +31356330666165653164346263303437663738363766653334393332396163643434316635616165 +35666632346538396435376530653930613838646435353539313931636431353830613138663433 +35323630666266626432633335356531326331626136613534373366643762343831376461303633 +38633663666134613062353962666630646239653135313835393238623961616232313136306162 +33366462613538343465363532636337626536653062393338323736383130323462623933633166 +34373963366635613131613161343432376439336163386530393932623537383734633132303132 +33386639346631666332616465396438356339316165633866633266376335633433643337653364 +63326232653239353063383266303733393038613063666265353235376531616231613366633132 +38363639353739383737363864613732613166316137363666643266643239613831323432633332 +31626166643739333261343066656634663531316639353338376230643037343837653434363939 +66386163306339376630633266633830613639626334363362346630613139653165373037313361 +35356463616132623036636639643536623637303333653131396432626237333335366336636663 +35323262616339313533613730303766393866613536336530383763643662663635336266613664 +37643462363431333236326233323534383735393630343364333462613764336532643336366232 +34333161393765383530363437363862663330316139316432393334353764616362336430613538 +39656263633932393932643031343866623439646235313634626339636437366663663632313232 +63363363306236396336373134363836346265396261333466343636343062373932303937336231 +65326166636531383837306339383466313763386636663432653833323333646133616132643532 +62633936346262646430333861616636613032666236636164626531363232353863396231656437 +37383638336536363363336432363335383465353136313237623837613163633535383137663038 +31663334616238333762393463373662303637353435376139366130303230613261313364313139 +38333361653238353365343937323263663234663633663466346530353562303362356236333137 +36323231333938623034623936373764363333613830656264386536666465386533336265393365 +61366232396531353361343131396166353633653564373164663636363861373630346530376335 +62653439663037613866383565626536656661316134383535323638663862363733386438313362 +63396433333461373636306137313736356338653261653866366530633561636338643865333763 +30653736653138333064653737346464633038303439653734353562373137653939336462633039 +30306365643664343230653136336666373631343234343238363362343438653466643163396163 +38316338623365366232316165313030323931373734663064306636356333633333623561646338 +39333238373766363963663238326564343461363731386630303332643131326465613466323637 +31636530383966633136343832323966623338323263323566303934653831613030356337313230 +39353434636136653133653165663438363638396133666332303631663166373864386565623061 +63363264623137623365303266653166313234663461633263316139633430623736636363343136 +63653937366363383065363763353663643162333766356465386137373161333765663566656332 +61653433346335356463663661366636393663646164616435396235336631613266313131643064 +37626663656564666264303262303139626136333834666465656265356366666330346364363963 +31643863353464636339626535393961346432663539316139336130663066343239643136323163 +64313139373130353165306230316138656462366231393663333834643239306434653061343339 +64396234343037303462303637323364393738613063303332386333383036353434313462383037 +63386564373366366135316264663934636264613137343331666135363561376636343562373233 +31656165393937383662663733333936366534633261663862333233343563393631653462376166 +61343434613561643333626635636562313662373563346665303635623936383732636433303863 +31373136343830633938386466306231353563303262303865363732623334663466393331653432 +33373932616362633838326562303163613839633833343765633363666138303538313564626562 +38343366396264633335326364316261356337336463643239323939363561303565653536373665 +39643264643663343866376538343362353165656233383031313834656234303863323833313831 +65323431643537336363633065613730616134343365353762306465366436333632653139626265 +31646565356562626538366433323537666438373465623337303733653466623736373730613838 +34356634393862613564636239613666313364333631303738313566643465653236343434613862 +37613762306639623531363963303734393764396634376335346534376265383435393862363862 +35643761303964373538613762366330303166656361633536636637376131333839303861363131 +63633562303637306264613166653962393564356264623038323865616535623762663233376531 +32303131336365373139333461663638643932643030616439646635346265646335323339313837 +62646139326336316239333436336437323764356562616461616662333033653033383764363262 +64643966316335393536343733363736323666333838626537396337306363633336383335353531 +62653466613762666662616434613432333237303332313035323737333430663034636563373233 +32636261343634623337373436663432323035356162316365653166623839316235656537626162 +65353937393634303036656339313739386564393435346264613564653063376334313962646334 +66663531303339663930643034613266393932653230306463316530656238626335373562353933 +32356437353961646638663666323363393334623731633861373339366131383563663531663033 +36653263656232343839633466303133353862306262376436666339363636383663333638663137 +65333936336635396435343361616162356232363131663065633863623262336164386435353331 +39363965343831336136383562643565633630666266626337396439303264343133613664366339 +66306363646538373839646539326336313536336165666130653434323033663632663562333130 +34313833393431616462393864363366376535303835306234333762333366383035326438343734 +64396563636361643833343766626162313562303235636235386337636630393335383334636261 +30343364626432343137613064373032303933363234623037386564653331343734356562643939 +62336230333538323933356235616234623931393035633865396161303536396235376664626665 +32396230323738316136386565346466326139646534336436303635323330616132643634323366 +32656435313565316636333666636465383037656334303037313433376462323439323734313437 +66636130313339363265613362643039323736313531373030316532353466396566646334303836 +64646466336530636538333939356136303939656564643063336465336230386633626430376131 +32333735323235306131346339343533383834343137646266386139373234633035303236333030 +31303861316266613437393362366535323930383463396538353539313265323364613665383333 +37663133303465356532633139626665343234366238313632653136333634346136316132613234 +38316337656661396465646532666135373664303532613134646239313333333932346339656137 +61356231383835353930316334353962323435616663326339636263316436643439303930373335 +62343038623031643033376162386164313939386638316639666363393730353863626631363833 +63383831313430353430646137306330386465346339613238616632663338313938343338623863 +33623636626530646132363563363839626563666330636132613063613261396136326539353835 +30363564336462393832666135623239616139386635613363346463393437326564303563386131 +38643936383339393634663062343661326562613332336362616565313962353231623664323463 +38316666316639373262343137653437663433656362663434306633396661373631313962306135 +30363136373039313833333733343932353462316632393364623665363834636137316464623538 +30313830626538633932313264373065383339303638333739353066316363663436303736653135 +30633966633463356161336536656136663234663563303138343633303766653562336261313462 +32316566343833383632383763306538646231656564656437333437333738396630376562656264 +37333135336630376239323837653635653361656537313565353938653734643834316565656436 +62333636323532643334636631363630323933613636326135366636323365646362313061373261 +38396262646561633066373939653239323466633430633731383762643163376531656236656363 +66386532653831633732316630343236396235303038646432393964363763313036383463353633 +33613065373461633631313437313237623332356639323235336164393864313062663363303035 +63336362313233313639303462616234333031653261646461623034343136333538326139643461 +61663266363631653366653935643431303566666133643632333231623564643636636334343366 +32376435616663393963