From: nicoo Date: Sat, 21 Apr 2018 19:52:50 +0000 (+0200) Subject: Merge PR#9: Ansible syntax cleanup and Documentation X-Git-Url: https://git.realraum.at/?a=commitdiff_plain;h=2f94f67a10b42f4165dda07b9158a790fc8d8674;hp=54e06334c0a27e69b924ccfebbdbb55dd1075f2f;p=noc.git Merge PR#9: Ansible syntax cleanup and Documentation --- diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..60e9148 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,4 @@ +## this makes the git diff/merge helper for ansible-vault files work +## see: https://github.com/building5/ansible-vault-tools +[defaults] +vault_password_file = ansible/gpg/get-vault-pass.sh diff --git a/ansible/.gitattributes b/ansible/.gitattributes new file mode 100644 index 0000000..e5ac042 --- /dev/null +++ b/ansible/.gitattributes @@ -0,0 +1,2 @@ +vault.yml diff=ansible-vault merge=ansible-vault +*.vault.yml diff=ansible-vault merge=ansible-vault diff --git a/ansible/README.md b/ansible/README.md new file mode 100644 index 0000000..a020642 --- /dev/null +++ b/ansible/README.md @@ -0,0 +1,91 @@ +Overview +======== + +This should provide a short overview on how to use ansible + +### basic ansible playbook call + +``` +# -C ... check +# -D ... show diff of changes +ansible-playbook foo.yml -D -C +``` + +### basic ansible call +``` +# -m ... load module shell +# -a ... arguments to module call +ansible vex -m shell -a 'uname -a' +ansible servers -m apt -a 'name=foo state=present' +ansible desktops -m file -a 'name=/make/sure/this/file/is/gone state=absent' +``` + +### check if all server are reachable +``` +ansible servers -m ping +``` + +### deploy playbook +``` +ansible-playbook foo.yml +``` + +### deploy a single role to a single host +``` +./apply-role.sh wuerfel base +``` + +### deploy a single role to a group of hosts with check-mode to see what would be done +``` +./apply-role.sh servers base -C -D +``` + + +Local ssh config +---------------- + +By default hosts in the inventory use the FQDNs as the name so most +hosts should be reachable without any special configuration. +In addition r3 NOC uses the `localconfig` playbook/role to generate a +ssh config snippet to add nicer/shorter aliases for the hosts and also +to automatically add jump hosts and some other special settings. + +The way this works is that config snippets are generated inside +`~/.ssh/config.d/` and (optionally) then compiled to a single file +`~/.ssh/config`. If you want to use it as well you should move your +current ssh config file to `~/.ssh/config.d/` and run the playbook +`localconfig.yml`. +In order to make the generated config snippet work for different +people the role sources the file `~/.ssh/r3_localconfig.yml`. +All variables inside that file will take precedence of files from +`host_vars`, `group_vars`, gathered facts, etc. + + +Secrets +------- + +See [README_vault.md](/ansible/README_vault.md) on how to create vaults. + +In general vaults should live in `host_vars//vault.yml` or +`group_vars//vault.yml`. The variables defined inside the +vaults should be prefixed with `vault_` and be referenced by other +variables and not used directly in plays and roles. For example if you +want to set a secret variable `root_pasword` for host `foo` there should +be two files: + * `host_vars/foo/main.yml`: + ``` + root_password: "{{ vault_root_password }}" + ``` + * `host_vars/foo/vault.yml`: + ``` + vault_root_password: "this-is-very-secret" + ``` + +Of course the latter file needs to be created using `ansible-vault`. + +If you want to store secrets that by default shouldn't be automatically +exposed to hosts and groups as variables please put the vault files into +`secrets` directory and name them .vault.yml. + +r3 NOC uses [ansible-vault-tools](https://github.com/building5/ansible-vault-tools) +to manage/diff/merge changes in vaults. diff --git a/ansible/README_vault.md b/ansible/README_vault.md index 311cc2a..c930a1d 100644 --- a/ansible/README_vault.md +++ b/ansible/README_vault.md @@ -1,9 +1,10 @@ Secrets and Vaults ================== -All secrets are stored inside encrypted ansible vault files which live -inside the secrets directory. Access to the vault files is controlled via -GPG keys. Anybody who uses this ansible repository needs to have a GPG key. +All secrets are stored inside encrypted ansible vault files which live in +`host_vars`, `group_vars` or inside the `secrets` directory. +Access to the vault files is controlled via GPG keys. Anybody who uses this +ansible repository needs to have a GPG key. Creating a GPG key @@ -16,11 +17,11 @@ You can use the following command to generate a new GPG key: - select "RSA and RSA" as kind (should be option: 1) - set keysize to: 4096 - set key expiration to: 2y - - set Real name and eMail adress + - set Real name and eMail address - set a passphrase for the key (please use a strong passphrase!!!) ``` -This command prints the fingerprint and other inforamtion about the newly +This command prints the fingerprint and other information about the newly generated key. In the line starting with pub you can find the key ID. This ID can be used to uniquely identify your key. Here is a sample output: @@ -55,7 +56,7 @@ following command: ``` This will add the new key to the keyring stored inside the repository and -reencrypt the secret to unlock the vault for all keys inside the keyring. +re-encrypt the secret to unlock the vault for all keys inside the keyring. @@ -70,7 +71,7 @@ following command: ``` This will remove the key from the keyring stored inside the repository and -reencrypt the secret to unlock the vault for all remaining keys inside the +re-encrypt the secret to unlock the vault for all remaining keys inside the keyring. You can find out the key ID using the command: @@ -98,20 +99,20 @@ Working with Vault files * create new vault: ``` -# ansible-vault create secrets/foo.vault.yml + # ansible-vault create host_vars/foo/vault.yml ``` This will open up an editor which allows you to add variables. Once you store and close the file the content is automatically encrypted. * edit a vault file: ``` -# ansible-vault edit secrets/foo.vault.yml + # ansible-vault edit group_vars/foo/vault.yml ``` This will open up an editor which allows you to add/remove/change variables. Once you store and close the file the content is automatically encrypted. * show the contents of a vault file: ``` -# ansible-vault view secrets/foo.vault.yml + # ansible-vault view secrets/foo.vault.yml ``` - This will automatially decrypt the file and print it's contents. + This will automatically decrypt the file and print it's contents. diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 9621dfc..f8bb548 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -1,5 +1,5 @@ [defaults] -inventory = ./hosts +inventory = ./hosts.ini log_path = ./log nocows=1 vault_password_file = ./gpg/get-vault-pass.sh diff --git a/ansible/group_vars/all b/ansible/group_vars/all deleted file mode 100644 index 3037b7e..0000000 --- a/ansible/group_vars/all +++ /dev/null @@ -1,27 +0,0 @@ -user_groups: - noc: - - equinox - - gebi - - nicoo - - bernhard - -users: - equinox: - email: equinox@realraum.at - gpg: 0xD74907C9E64E6CED8FE3 - - gebi: - email: michael@mgeb.org - gpg: 0x6E302CF4D98B9702 - - nicoo: - email: nicolas@braud-santoni.eu - gpg: 0x3F41B0739AAD91B7CDC0 - - bernhard: - email: xro@realraum.at - gpg: 0xE3468B9CE81EB4F91486 - -noc_groups: - - adm - - sudo diff --git a/ansible/group_vars/all/main.yml b/ansible/group_vars/all/main.yml new file mode 100644 index 0000000..90463fc --- /dev/null +++ b/ansible/group_vars/all/main.yml @@ -0,0 +1,28 @@ +--- +user_groups: + noc: + - equinox + - gebi + - nicoo + - bernhard + +users: + equinox: + email: equinox@realraum.at + gpg: 0xD74907C9E64E6CED8FE3 + + gebi: + email: michael@mgeb.org + gpg: 0x6E302CF4D98B9702 + + nicoo: + email: nicolas@braud-santoni.eu + gpg: 0x3F41B0739AAD91B7CDC0 + + bernhard: + email: xro@realraum.at + gpg: 0xE3468B9CE81EB4F91486 + +noc_groups: + - adm + - sudo diff --git a/ansible/host_vars/athsdisc b/ansible/host_vars/athsdisc deleted file mode 100644 index 1540b11..0000000 --- a/ansible/host_vars/athsdisc +++ /dev/null @@ -1,5 +0,0 @@ -sshd_allowusers_host: - - luto - - robelix - - tomk32 - diff --git a/ansible/host_vars/athsdisc/main.yml b/ansible/host_vars/athsdisc/main.yml new file mode 100644 index 0000000..201d96a --- /dev/null +++ b/ansible/host_vars/athsdisc/main.yml @@ -0,0 +1,5 @@ +--- +sshd_allowusers_host: + - luto + - robelix + - tomk32 diff --git a/ansible/host_vars/ctf b/ansible/host_vars/ctf deleted file mode 100644 index 15b2514..0000000 --- a/ansible/host_vars/ctf +++ /dev/null @@ -1,3 +0,0 @@ -sshd_allowusers_host: - - f0rki - - verr diff --git a/ansible/host_vars/ctf/main.yml b/ansible/host_vars/ctf/main.yml new file mode 100644 index 0000000..5191ee0 --- /dev/null +++ b/ansible/host_vars/ctf/main.yml @@ -0,0 +1,4 @@ +--- +sshd_allowusers_host: + - f0rki + - verr diff --git a/ansible/host_vars/gnocci1/main.yml b/ansible/host_vars/gnocci1/main.yml new file mode 100644 index 0000000..954c9c7 --- /dev/null +++ b/ansible/host_vars/gnocci1/main.yml @@ -0,0 +1,2 @@ +--- +root_password: "{{ vault_root_password }}" diff --git a/ansible/host_vars/gnocci1/vault.yml b/ansible/host_vars/gnocci1/vault.yml new file mode 100644 index 0000000..df5d4ac --- /dev/null +++ b/ansible/host_vars/gnocci1/vault.yml @@ -0,0 +1,8 @@ +$ANSIBLE_VAULT;1.1;AES256 +66323066353065353661346261313235333834343034313532343739343531373035366364303138 +6433663331336264613830643035363962346131353830640a376336363433653437306236656230 +39313361376130316464333566383533396663393863646333393536613230333233333335323938 +3662646635383161360a333661663063343862373638373933383362383164623039383763613036 +61346661346261306465393039343732343635326364306363653666343130383836343539336439 +34306462316666623665323239613561663730353933633663636631323063383164643937366334 +393864666635663237346434613264303532 diff --git a/ansible/host_vars/gnocci2/main.yml b/ansible/host_vars/gnocci2/main.yml new file mode 100644 index 0000000..954c9c7 --- /dev/null +++ b/ansible/host_vars/gnocci2/main.yml @@ -0,0 +1,2 @@ +--- +root_password: "{{ vault_root_password }}" diff --git a/ansible/host_vars/gnocci2/vault.yml b/ansible/host_vars/gnocci2/vault.yml new file mode 100644 index 0000000..ab3ecca --- /dev/null +++ b/ansible/host_vars/gnocci2/vault.yml @@ -0,0 +1,8 @@ +$ANSIBLE_VAULT;1.1;AES256 +31366163653363386462333866383263366435353838623965653035623138356339633866623932 +3538626561373636313833333434393434616366303633370a346364356161616662666164323063 +30333934663463383034623730366365386536373465383362353132386434396461353039363863 +3861333238386263620a613539393937383264346566613330666165623363313838326638623563 +64643233613539356337613435376130633466313261616235326430326161663263343363343361 +36373736303233333831316266633365306435646634643166663038326364323839386430373438 +373966366161613436646365346339316365 diff --git a/ansible/host_vars/metrics b/ansible/host_vars/metrics deleted file mode 100644 index 2b1841f..0000000 --- a/ansible/host_vars/metrics +++ /dev/null @@ -1 +0,0 @@ -localconfig_ssh_config_user: root diff --git a/ansible/host_vars/metrics/main.yml b/ansible/host_vars/metrics/main.yml new file mode 100644 index 0000000..31b6b26 --- /dev/null +++ b/ansible/host_vars/metrics/main.yml @@ -0,0 +1,2 @@ +--- +localconfig_ssh_config_user: root diff --git a/ansible/host_vars/vex b/ansible/host_vars/vex deleted file mode 100644 index 3b04341..0000000 --- a/ansible/host_vars/vex +++ /dev/null @@ -1,5 +0,0 @@ -sshd_allowusers_host: - - git - - www - - www-data - - acme diff --git a/ansible/host_vars/vex/main.yml b/ansible/host_vars/vex/main.yml new file mode 100644 index 0000000..d75df90 --- /dev/null +++ b/ansible/host_vars/vex/main.yml @@ -0,0 +1,6 @@ +--- +sshd_allowusers_host: + - git + - www + - www-data + - acme diff --git a/ansible/host_vars/wuerfel b/ansible/host_vars/wuerfel deleted file mode 100644 index c7f5f5c..0000000 --- a/ansible/host_vars/wuerfel +++ /dev/null @@ -1 +0,0 @@ -sshd_allowgroup: ssh diff --git a/ansible/host_vars/wuerfel/main.yml b/ansible/host_vars/wuerfel/main.yml new file mode 100644 index 0000000..c3d5323 --- /dev/null +++ b/ansible/host_vars/wuerfel/main.yml @@ -0,0 +1,2 @@ +--- +sshd_allowgroup: ssh diff --git a/ansible/hosts b/ansible/hosts deleted file mode 100644 index b618973..0000000 --- a/ansible/hosts +++ /dev/null @@ -1,41 +0,0 @@ -[all:vars] -host_domain=realraum.at -ansible_host={{ inventory_hostname }}.{{ host_domain }} -ansible_user=root - -[baremetalservers] -alfred.mgmt - -[kvmhosts] -alfred.mgmt - -[virtualservers] -athsdisc -calendar.mgmt -ctf -entrance -galley.mgmt -hacksch.mgmt -## TODO: remove the variable once https://github.com/ansible/ansible/issues/39119 is fixed -metrics.mgmt localconfig_ssh_config_user=root -r3home.mgmt -tickets.mgmt -vex - -[servers:children] -baremetalservers -virtualservers - - -[desktops] -wuerfel - - -#[alix] -#gw -#torwaechter.mgmt - -#[apu] -#gnocchi1 -#gnocchi2 - diff --git a/ansible/hosts.ini b/ansible/hosts.ini new file mode 100644 index 0000000..b618973 --- /dev/null +++ b/ansible/hosts.ini @@ -0,0 +1,41 @@ +[all:vars] +host_domain=realraum.at +ansible_host={{ inventory_hostname }}.{{ host_domain }} +ansible_user=root + +[baremetalservers] +alfred.mgmt + +[kvmhosts] +alfred.mgmt + +[virtualservers] +athsdisc +calendar.mgmt +ctf +entrance +galley.mgmt +hacksch.mgmt +## TODO: remove the variable once https://github.com/ansible/ansible/issues/39119 is fixed +metrics.mgmt localconfig_ssh_config_user=root +r3home.mgmt +tickets.mgmt +vex + +[servers:children] +baremetalservers +virtualservers + + +[desktops] +wuerfel + + +#[alix] +#gw +#torwaechter.mgmt + +#[apu] +#gnocchi1 +#gnocchi2 + diff --git a/ansible/roles/base/handlers/main.yaml b/ansible/roles/base/handlers/main.yaml deleted file mode 100644 index 9b95e27..0000000 --- a/ansible/roles/base/handlers/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -- name: restart ssh - service: name=ssh state=restarted diff --git a/ansible/roles/base/handlers/main.yml b/ansible/roles/base/handlers/main.yml new file mode 100644 index 0000000..822887e --- /dev/null +++ b/ansible/roles/base/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart ssh + service: + name: ssh + state: restarted diff --git a/ansible/roles/base/tasks/main.yaml b/ansible/roles/base/tasks/main.yaml deleted file mode 100644 index f4cbe30..0000000 --- a/ansible/roles/base/tasks/main.yaml +++ /dev/null @@ -1,110 +0,0 @@ ---- -- set_fact: - sshd_allowusers: >- - {{ [ 'root' ] | union(user_groups.noc) - | union(sshd_allowusers_group | default([])) - | union(sshd_allowusers_host | default([])) }} - -- name: only allow pubkey auth for root - lineinfile: - dest: /etc/ssh/sshd_config - regexp: "^PermitRootLogin" - line: "PermitRootLogin without-password" - notify: restart ssh - -- name: limit allowed users (1/2) - lineinfile: - dest: /etc/ssh/sshd_config - regexp: "^#?AllowUsers" - line: "AllowUsers {{ ' '.join(sshd_allowusers) }}" - when: sshd_allowgroup is not defined - notify: restart ssh - -- block: - - name: "limit allowed users (2/2): Make sure AllowUsers is not in sshd_config" - lineinfile: - dest: /etc/ssh/sshd_config - regexp: "^AllowUsers" - state: absent - notify: restart ssh - - - name: "limit allowed users (2/2): Set AllowGroups in sshd_config" - lineinfile: - dest: /etc/ssh/sshd_config - regexp: "^#?AllowGroups" - line: AllowGroups {{ sshd_allowgroup }} - notify: restart ssh - - - name: "limit allowed users (2/2): Add allowed users to ssh group" - user: - name: "{{ item }}" - groups: "{{ sshd_allowgroup }}" - append: True - with_items: "{{ sshd_allowusers }}" - - when: sshd_allowgroup is defined - -- name: Set authorized keys for root user - authorized_key: - user: root - key: "{{ lookup('pipe','cat ssh/noc/*.pub') }}" - exclusive: yes - -- name: disable apt suggests and recommends - copy: src=02no-recommends dest=/etc/apt/apt.conf.d/ mode=0644 - -- name: install basic packages - apt: name={{ item }} state=present - with_items: - - less - - psmisc - - sudo - - htop - - dstat - - mtr-tiny - - tcpdump - - debian-goodies - - lsof - - haveged - - net-tools - - ntp - - screen - - aptitude - - unp - - ca-certificates - - file - - zsh - - python-apt - -- name: make sure grml-(etc|scripts)-core is not installed - apt: name={{ item }} state=absent purge=yes - with_items: - - grml-etc-core - - grml-scripts-core - -- block: - - name: install systemd specific packages - apt: name={{ item }} state=present - with_items: - - dbus - - libpam-systemd - - - name: set systemd-related environment variables - copy: src=xdg_runtime_dir.sh dest=/etc/profile.d/xdg_runtime_dir.sh mode=0644 - - when: ansible_service_mgr == "systemd" - -- name: install zshrc - copy: src={{ item.src }} dest={{ item.dest }} mode=0644 - with_items: - - { "src": "zprofile", "dest": "/etc/zsh/zprofile" } - - { "src": "zshrc", "dest": "/etc/zsh/zshrc" } - - { "src": "zshrc.skel", "dest": "/etc/skel/.zshrc" } - -- name: set root default shell to zsh - user: name=root shell=/bin/zsh - -- name: set default shell for adduser - lineinfile: dest=/etc/adduser.conf regexp={{ item.regexp }} line={{ item.line }} - with_items: - - { regexp: "^DSHELL", line: "DSHELL=/bin/zsh" } diff --git a/ansible/roles/base/tasks/main.yml b/ansible/roles/base/tasks/main.yml new file mode 100644 index 0000000..f209fe4 --- /dev/null +++ b/ansible/roles/base/tasks/main.yml @@ -0,0 +1,132 @@ +--- +- set_fact: + sshd_allowusers: >- + {{ [ 'root' ] | union(user_groups.noc) + | union(sshd_allowusers_group | default([])) + | union(sshd_allowusers_host | default([])) }} + +- name: only allow pubkey auth for root + lineinfile: + dest: /etc/ssh/sshd_config + regexp: "^PermitRootLogin" + line: "PermitRootLogin without-password" + notify: restart ssh + +- name: limit allowed users (1/2) + when: sshd_allowgroup is not defined + lineinfile: + dest: /etc/ssh/sshd_config + regexp: "^#?AllowUsers" + line: "AllowUsers {{ ' '.join(sshd_allowusers) }}" + notify: restart ssh + +- block: + - name: "limit allowed users (2/2): Make sure AllowUsers is not in sshd_config" + lineinfile: + dest: /etc/ssh/sshd_config + regexp: "^AllowUsers" + state: absent + notify: restart ssh + + - name: "limit allowed users (2/2): Set AllowGroups in sshd_config" + lineinfile: + dest: /etc/ssh/sshd_config + regexp: "^#?AllowGroups" + line: AllowGroups {{ sshd_allowgroup }} + notify: restart ssh + + - name: "limit allowed users (2/2): Add allowed users to ssh group" + user: + name: "{{ item }}" + groups: "{{ sshd_allowgroup }}" + append: True + with_items: "{{ sshd_allowusers }}" + + when: sshd_allowgroup is defined + +- name: Set authorized keys for root user + authorized_key: + user: root + key: "{{ lookup('pipe','cat ssh/noc/*.pub') }}" + exclusive: yes + +- name: disable apt suggests and recommends + copy: + src: 02no-recommends + dest: /etc/apt/apt.conf.d/ + mode: 0644 + +- name: install basic packages + apt: + name: + - less + - psmisc + - sudo + - htop + - dstat + - mtr-tiny + - tcpdump + - debian-goodies + - lsof + - haveged + - net-tools + - ntp + - screen + - aptitude + - unp + - ca-certificates + - file + - zsh + - python-apt + state: present + +- name: make sure grml-(etc|scripts)-core is not installed + apt: + name: + - grml-etc-core + - grml-scripts-core + state: absent + purge: yes + +- block: + - name: install systemd specific packages + apt: + name: + - dbus + - libpam-systemd + state: present + + - name: set systemd-related environment variables + copy: + src: xdg_runtime_dir.sh + dest: /etc/profile.d/xdg_runtime_dir.sh + mode: 0644 + + when: ansible_service_mgr == "systemd" + +- name: install zshrc + with_items: + - src: "zprofile" + dest: "/etc/zsh/zprofile" + - src: "zshrc" + dest: "/etc/zsh/zshrc" + - src: "zshrc.skel" + dest: "/etc/skel/.zshrc" + copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: 0644 + +- name: set root default shell to zsh + user: + name: root + shell: /bin/zsh + +- name: set default shell for adduser + with_items: + - regexp: "^DSHELL" + line: "DSHELL=/bin/zsh" + lineinfile: + dest: /etc/adduser.conf + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" diff --git a/ansible/roles/localconfig/tasks/main.yml b/ansible/roles/localconfig/tasks/main.yml index 79f9f57..6abee94 100644 --- a/ansible/roles/localconfig/tasks/main.yml +++ b/ansible/roles/localconfig/tasks/main.yml @@ -11,11 +11,11 @@ state: directory - name: install generated ssh config snippets + with_fileglob: + - "../templates/ssh/*.conf.j2" template: src: "ssh/{{ item | basename }}" dest: "~/.ssh/config.d/{{ item | basename | regex_replace('^(.*)\\.j2$', '\\1') }}" - with_fileglob: - - "../templates/ssh/*.conf.j2" - name: install static ssh config snippets copy: diff --git a/ansible/secrets/gnocchi1.yaml b/ansible/secrets/gnocchi1.yaml deleted file mode 100644 index ff2ba13..0000000 --- a/ansible/secrets/gnocchi1.yaml +++ /dev/null @@ -1,8 +0,0 @@ -$ANSIBLE_VAULT;1.1;AES256 -63633133636538336262323866386232386466376135633663353433316330303961643764613237 -3938333265333865396431386266326434323164333465390a363262326439646133653261646665 -37643938346564656131353235343136313162333933633537386532393639613233383435373735 -6564363831336563330a353964616431313530356434353761316335326331613364633363663465 -31653938363765643264623463636461623832376263326539353138383937656562636632393337 -38653030393762323934356335393536336262363333633032303938633730623463353130333163 -313434356336366164633761323263313866 diff --git a/ansible/secrets/gnocchi2.yaml b/ansible/secrets/gnocchi2.yaml deleted file mode 100644 index 7022536..0000000 --- a/ansible/secrets/gnocchi2.yaml +++ /dev/null @@ -1,8 +0,0 @@ -$ANSIBLE_VAULT;1.1;AES256 -39663934616437353434303961306630306237653763383830346462306164616366393437386163 -3462386237313763376236356331363936306564663965640a633762313637303663373865653032 -36633536633232623236386164616234383936306265383363633531373038623035636230623531 -6363376361373934640a626231613462373139623137313134663331336165613164346135306535 -30343063666162303430356466343338643165383839626537323666613463653463333464393234 -38373434306565623565326463386531623838616566633330626334363639393131383832356439 -613836626638366337646637363339363334