Remove role gitea

This commit is contained in:
flyingscorpio@clevo 2023-01-19 10:22:30 +01:00
parent f784701b74
commit c39c717d52
20 changed files with 0 additions and 718 deletions

View file

@ -1,5 +0,0 @@
# Fail2ban gitea filter
[Definition]
failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from <HOST>
ignoreregex =

View file

@ -1 +0,0 @@
0 5 * * * git USER=git /usr/local/bin/gitea_backup.sh backup && USER=git /usr/local/bin/gitea_backup.sh prune 2

View file

@ -1,29 +0,0 @@
---
- name: Receive gitea pgp key
ansible.builtin.command: gpg --keyserver hkps://keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
register: result
changed_when: '"not changed" not in result.stderr'
- name: Download gitea asc file
ansible.builtin.get_url:
url: "https://dl.gitea.io/gitea/{{ gitea_binary.json.latest.version }}/gitea-{{ gitea_binary.json.latest.version }}-linux-{{ gitea_binary_arch }}.asc"
dest: "/tmp/gitea-{{ gitea_binary.json.latest.version }}.asc"
mode: 0644
- name: Verify gitea binary with gpg
ansible.builtin.command: "gpg --verify /tmp/gitea-{{ gitea_binary.json.latest.version }}.asc {{ gitea_run_dir }}/gitea-{{ gitea_binary.json.latest.version }}"
register: result
failed_when: '"Good signature from" not in result.stderr'
- name: Start gitea service
become: true
ansible.builtin.systemd:
name: gitea
state: started
enabled: true
- name: Restart gitea service
become: true
ansible.builtin.systemd:
name: gitea
state: restarted

View file

@ -1,6 +0,0 @@
---
dependencies:
- role: setup_fail2ban
- role: setup_apache2
- role: setup_mariadb
- role: setup_certbot

View file

@ -1,25 +0,0 @@
---
- name: Enable proxy modules
become: true
community.general.apache2_module:
name: "{{ item }}"
state: present
with_items:
- proxy
- proxy_http
notify: Reload apache2 service
- name: Copy vHost conf
become: true
ansible.builtin.template:
src: apache2/git.tunuifranken.info.conf.j2
dest: /etc/apache2/sites-available/git.tunuifranken.info.conf
mode: 0644
notify: Reload apache2 service
- name: Activate vHost
become: true
ansible.builtin.command: a2ensite git.tunuifranken.info.conf
register: result
changed_when: "'already enabled' not in result.stdout"
notify: Reload apache2 service

View file

@ -1,25 +0,0 @@
---
- name: Copy gitea_backup.sh script
become: true
ansible.builtin.template:
src: gitea_backup.sh.j2
dest: /usr/local/bin/gitea_backup.sh
owner: git
group: git
mode: 0775
- name: Create gitea-dumps directory
become: true
ansible.builtin.file:
path: "{{ gitea_run_dir }}/gitea-dumps"
state: directory
owner: git
group: git
mode: 0755
- name: Setup gitea-backup crontab
become: true
ansible.builtin.copy:
src: gitea-backup.cron
dest: /etc/cron.d/gitea-backup
mode: 0644

View file

@ -1,48 +0,0 @@
---
- name: Find latest gitea version
ansible.builtin.uri:
url: https://dl.gitea.io/gitea/version.json
register: gitea_binary
- name: Find if latest gitea version is installed
become: true
ansible.builtin.stat:
path: "{{ gitea_run_dir }}/gitea-{{ gitea_binary.json.latest.version }}"
register: latest_gitea_binary
- name: Set gitea binary architecture to amd64
ansible.builtin.set_fact:
gitea_binary_arch: amd64
when: ansible_facts['architecture'] == 'x86_64'
- name: Set gitea binary architecture to arm-6
ansible.builtin.set_fact:
gitea_binary_arch: arm-6
when: ansible_facts['architecture'] != 'x86_64'
- name: Get latest gitea binary
become: true
ansible.builtin.get_url:
url: "https://dl.gitea.io/gitea/{{ gitea_binary.json.latest.version }}/gitea-{{ gitea_binary.json.latest.version }}-linux-{{ gitea_binary_arch }}"
dest: "{{ gitea_run_dir }}/gitea-{{ gitea_binary.json.latest.version }}"
owner: git
group: git
mode: 0664
when: not latest_gitea_binary.stat.exists
notify:
- Receive gitea pgp key
- Download gitea asc file
- Verify gitea binary with gpg
- name: Verify downloaded binary
ansible.builtin.meta: flush_handlers
- name: Copy gitea binary to global location
become: true
ansible.builtin.copy:
src: "{{ gitea_run_dir }}/gitea-{{ gitea_binary.json.latest.version }}"
dest: /usr/local/bin/gitea
remote_src: true
owner: root
group: root
mode: 0755

View file

@ -1,20 +0,0 @@
---
- name: Copy fail2ban filter
become: true
ansible.builtin.copy:
src: fail2ban/gitea-filter.conf
dest: /etc/fail2ban/filter.d/gitea.conf
owner: root
group: root
mode: 0644
notify: Restart fail2ban service
- name: Copy fail2ban jail
become: true
ansible.builtin.template:
src: fail2ban/gitea-jail.conf.j2
dest: /etc/fail2ban/jail.d/gitea.conf
owner: root
group: root
mode: 0644
notify: Restart fail2ban service

View file

@ -1,104 +0,0 @@
---
- name: Include vault variables
ansible.builtin.include_vars: vault.yml
- name: Include apache2 tasks
ansible.builtin.include_tasks: apache2.yml
- name: Include mariadb tasks
ansible.builtin.include_tasks: mariadb.yml
- name: Include unix tasks
ansible.builtin.include_tasks: unix.yml
- name: Include binary tasks
ansible.builtin.include_tasks: binary.yml
- name: Include backup tasks
ansible.builtin.include_tasks: backup.yml
- name: Copy /etc/systemd/system/gitea.service
become: true
ansible.builtin.template:
src: gitea.service.j2
dest: /etc/systemd/system/gitea.service
owner: root
group: root
mode: 0644
notify:
- Reload systemd daemon
- Start gitea service
- name: Copy config file
become: true
ansible.builtin.template:
src: app.ini.j2
dest: "{{ gitea_conf_dir }}/app.ini"
owner: git
group: git
mode: 0640
notify:
- Restart gitea service
- name: Make sure systemd daemon is reloaded
ansible.builtin.meta: flush_handlers
- name: Make sure gitea is running
become: true
ansible.builtin.systemd:
name: gitea
state: started
enabled: true
# fail2ban tasks need the gitea log file, which should be created when gitea runs
- name: Include fail2ban tasks
ansible.builtin.include_tasks: fail2ban.yml
- name: Include repos tasks
ansible.builtin.include_tasks: repos.yml
- name: Setup logrotate for gitea logs
become: true
ansible.builtin.template:
src: gitea.logrotate.j2
dest: /etc/logrotate.d/gitea
owner: root
group: root
mode: 0644
- name: Generate SSH keys for git
become: true
become_user: git
community.crypto.openssh_keypair:
path: ~/.ssh/id_rsa
type: rsa
comment: "git@{{ ansible_fqdn }}"
register: ssh_key
- name: Get previously added SSH keys
ansible.builtin.uri:
url: https://git.tunuifranken.info/api/v1/user/keys
method: GET
user: "{{ gitea_user }}"
password: "{{ gitea_pass }}"
force_basic_auth: true
register: present_ssh_keys
- name: List SSH fingerprints
ansible.builtin.set_fact:
present_ssh_fingerprints: "{{ present_ssh_keys.json | map(attribute='fingerprint') }}"
- name: Add SSH key using Gitea's API
ansible.builtin.uri:
url: https://git.tunuifranken.info/api/v1/user/keys
method: POST
user: "{{ gitea_user }}"
password: "{{ gitea_pass }}"
force_basic_auth: true
status_code: 201
body_format: json
body:
key: "{{ ssh_key.public_key | trim }}"
read_only: false
title: "{{ ssh_key.comment | trim }}"
when: ssh_key.fingerprint not in present_ssh_fingerprints

View file

@ -1,29 +0,0 @@
---
- name: Include vault variables
ansible.builtin.include_vars: vault.yml
- name: Create gitea database
become: true
community.mysql.mysql_db:
name: "{{ db_name }}"
state: present
encoding: utf8mb4
collation: utf8mb4_unicode_ci
login_unix_socket: /var/run/mysqld/mysqld.sock
- name: Set gitea database user and privileges
become: true
community.mysql.mysql_user:
name: "{{ db_user }}"
password: "{{ db_pass }}"
priv: "{{ db_name }}.*:ALL"
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
- name: Check gitea database connection
community.mysql.mysql_info:
login_user: "{{ db_user }}"
login_db: "{{ db_name }}"
login_host: localhost
login_password: "{{ db_pass }}"
filter: version

View file

@ -1,34 +0,0 @@
---
- name: Ask to push latest gitea_dump zipfile
ansible.builtin.pause:
prompt: "Local path to gitea dump, so we can push it [leave empty to not push]"
echo: true
register: user_gitea_dump_path
- name: Push latest gitea dump zipfile
become: true
ansible.builtin.copy:
src: "{{ user_gitea_dump_path.user_input }}"
dest: "{{ gitea_run_dir }}/gitea-dumps/{{ user_gitea_dump_path.user_input | basename }}"
owner: git
group: git
mode: 0640
when: user_gitea_dump_path.user_input != ''
- name: Find all gitea dumps on the server
become: true
ansible.builtin.find:
paths: "{{ gitea_run_dir }}/gitea-dumps/"
register: all_gitea_dumps
- name: Find latest gitea dump on the server
ansible.builtin.set_fact:
latest_gitea_dump: "{{ all_gitea_dumps.files | sort(attribute='mtime') | last }}"
- name: Deploy repos from latest gitea dump
become: true
become_user: git
ansible.builtin.command:
cmd: "/usr/local/bin/gitea_backup.sh restore {{ latest_gitea_dump.path }}"
# when this dir exists, the command won't run, so we don't overwrite existing repos
creates: "{{ gitea_run_dir }}/gitea-repositories"

View file

@ -1,55 +0,0 @@
---
- name: Install needed packages
become: true
ansible.builtin.apt:
name:
- git
- unzip
- gpg # to verify binary
- acl # for become_user: git
state: present
- name: Create git group
become: true
ansible.builtin.group:
name: git
system: true
- name: Create git user
become: true
ansible.builtin.user:
name: git
group: git
append: true
groups:
- sudo
- mail
create_home: false
home: "{{ gitea_run_dir }}"
shell: /bin/bash
system: true
- name: Create needed directories
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: git
group: git
mode: 0750
with_items:
- "{{ gitea_conf_dir }}"
- "{{ gitea_run_dir }}"
- "{{ gitea_custom_dir }}"
- "{{ gitea_data_dir }}"
- "{{ gitea_log_dir }}"
- name: Set sudoer permissions to git user
become: true
ansible.builtin.copy:
content: 'git ALL=(root) NOPASSWD:/usr/bin/systemctl'
dest: /etc/sudoers.d/git
owner: root
group: root
mode: 0440
validate: /usr/sbin/visudo -csf %s

View file

@ -1,28 +0,0 @@
<VirtualHost *:80>
ServerName git.tunuifranken.info
ServerAdmin {{ server_admin }}
DocumentRoot /var/www/empty
RewriteEngine on
RewriteCond %{SERVER_NAME} =git.tunuifranken.info
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
ErrorLog ${APACHE_LOG_DIR}/git.tunuifranken.info.error.log
CustomLog ${APACHE_LOG_DIR}/git.tunuifranken.info.access.log combined
</VirtualHost>
<VirtualHost *:443>
ServerName git.tunuifranken.info
ServerAdmin {{ server_admin }}
ProxyPreserveHost On
ProxyRequests off
AllowEncodedSlashes NoDecode
ProxyPass / http://localhost:3000/ nocanon
ProxyPassReverse / http://localhost:3000/
ErrorLog ${APACHE_LOG_DIR}/git.tunuifranken.info.error.log
CustomLog ${APACHE_LOG_DIR}/git.tunuifranken.info.access.log combined
#SSLCertificateFile /etc/letsencrypt/live/git.tunuifranken.info/fullchain.pem
#SSLCertificateKeyFile /etc/letsencrypt/live/git.tunuifranken.info/privkey.pem
</VirtualHost>

View file

@ -1,83 +0,0 @@
APP_NAME = {{ ansible_facts['env']['USER'] }}'s git
#APP_NAME = Gitea: Git with a cup of tea
RUN_USER = git
RUN_MODE = prod
[oauth2]
JWT_SECRET = {{ gitea_jtw_secret }}
[security]
INTERNAL_TOKEN = {{ gitea_internal_token }}
INSTALL_LOCK = true
SECRET_KEY = {{ gitea_secret_key }}
[database]
DB_TYPE = mysql
HOST = 127.0.0.1:3306
NAME = {{ db_name }}
USER = {{ db_user }}
PASSWD = {{ db_pass }}
SSL_MODE = disable
CHARSET = utf8mb4
[repository]
ROOT = {{ gitea_run_dir }}/gitea-repositories
DISABLE_HTTP_GIT = false
DEFAULT_BRANCH = main
DEFAULT_PUSH_CREATE_PRIVATE = true
ENABLE_PUSH_CREATE_USER = true
ENABLE_PUSH_CREATE_ORG = true
[server]
PROTOCOL = http
DOMAIN = {{ gitea_domain }}
HTTP_PORT = 3000
ROOT_URL = https://{{ gitea_domain }}
SSH_DOMAIN = {{ gitea_ssh_host }}
DISABLE_SSH = false
SSH_PORT = {{ gitea_ssh_port }}
LFS_START_SERVER = false
OFFLINE_MODE = false
LANDING_PAGE = explore
[mailer]
ENABLED = true
SMTP_ADDR = {{ mail_addr }}
SMTP_PORT = {{ mail_port }}
FROM = {{ gitea_mail_user }}@{{ mail_domain }}
USER = git
PASSWD = {{ gitea_mail_pass }}
[service]
REGISTER_EMAIL_CONFIRM = false
REGISTER_MANUAL_CONFIRM = true
ENABLE_NOTIFY_MAIL = true
DISABLE_REGISTRATION = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = false
REQUIRE_SIGNIN_VIEW = false
DEFAULT_KEEP_EMAIL_PRIVATE = true
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS = noreply.localhost
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = true
[openid]
ENABLE_OPENID_SIGNIN = true
ENABLE_OPENID_SIGNUP = true
[session]
PROVIDER = file
[log]
MODE = file
LEVEL = info
ROOT_PATH = {{ gitea_log_dir }}
[other]
SHOW_FOOTER_BRANDING = false
SHOW_FOOTER_VERSION = false
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false

View file

@ -1,8 +0,0 @@
[gitea]
enabled = true
filter = gitea
logpath = {{ gitea_log_dir }}/gitea.log
maxretry = 10
findtime = 3600
bantime = 900
action = iptables-allports

View file

@ -1,13 +0,0 @@
{{ gitea_log_dir }}/*.log
{
rotate 7
daily
missingok
notifempty
delaycompress
compress
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}

View file

@ -1,23 +0,0 @@
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
Requires=mariadb.service
[Service]
# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
#LimitMEMLOCK=infinity
#LimitNOFILE=65535
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory={{ gitea_run_dir }}
ExecStart=/usr/local/bin/gitea web --config {{ gitea_conf_dir }}/app.ini
Restart=always
Environment=USER=git HOME={{ gitea_run_dir }} GITEA_WORK_DIR={{ gitea_run_dir }}
[Install]
WantedBy=multi-user.target

View file

@ -1,136 +0,0 @@
#!/bin/bash
# Use gitea dump to make a backup of Gitea
# https://docs.gitea.io/en-us/backup-and-restore/
set -eu
set -o pipefail
IFS="$(printf '\n\t')"
PROGRAM="${0##*/}"
INSTALL_DIR=/usr/local/bin
TMP_DIR={{ gitea_run_dir }}/tmp
WORK_DIR={{ gitea_run_dir }}
DATA_DIR={{ gitea_data_dir }}
LOG_DIR={{ gitea_log_dir }}
REPO_DIR={{ gitea_run_dir }}/gitea-repositories/
CONFIG_FILE={{ gitea_conf_dir }}/app.ini
DUMP_DIR={{ gitea_run_dir }}/gitea-dumps
MYSQL_USER={{ db_user }}
MYSQL_DB={{ db_name }}
MYSQL_PW={{ db_pass }}
SCRIPT_LOGFILE={{ gitea_log_dir }}/gitea_backup.log
{% raw %}
if [ "$USER" != git ]; then
echo "You must run this as user 'git'" && exit 1
fi
print_help() {
cat <<-_EOF
$PROGRAM
Usage:
$PROGRAM {backup|restore <gitea_dump_file>|prune <int>}
Commands:
backup Create a backup. Will save to $DUMP_DIR.
restore Restore a previously created backup, given on the command line:
<gitea_dump_file>, absolute path or relative to $PWD.
prune Remove previously dumped backups and keep <int> backups.
Options:
-h Print this help message and quit.
_EOF
}
do_backup() {
echo "Backing up Gitea..."
stop_service
output_dump_file="$DUMP_DIR/gitea-dump-$(date +'%Y%m%d').zip"
cd "$INSTALL_DIR" || exit 1
./gitea dump -V -f "$output_dump_file" -c "$CONFIG_FILE" -t "$TMP_DIR" --skip-attachment-data --skip-lfs-data -w "$WORK_DIR"
chmod 640 "$output_dump_file" # Make the file readable by group for scp to different host by different user
restart_service
echo "Done."
}
do_restore() {
echo "Restoring Gitea backup..."
zip_file="$1"
zip_dir="${zip_file%.*}" # Remove file extension
chmod 600 "$zip_file" # Backup has changed permissions, restore them here
echo "Unzipping $zip_file..." && unzip "$zip_file" -d "$zip_dir" && echo " OK."
cd "$zip_dir" || exit 1
echo -n "Restoring $CONFIG_FILE..." && mv -f app.ini "$CONFIG_FILE" && echo " OK."
echo -n "Restoring $DATA_DIR..." && rsync -avz --delete data/ "$DATA_DIR" && rm -rf data && echo " OK."
echo -n "Restoring $LOG_DIR..." && rsync -avz log/ "$LOG_DIR" && rm -rf log && echo " OK."
echo -n "Restoring $REPO_DIR..." && mkdir -p "$REPO_DIR" && rsync -avz --delete repos/ "$REPO_DIR" && rm -rf repos && echo " OK."
echo -n "Changing ownership..." && chown -R git:git "$CONFIG_FILE" "$WORK_DIR" && echo " OK."
echo -n "Restoring MySQL database..." && mysql --default-character-set=utf8mb4 -u"$MYSQL_USER" -p"$MYSQL_PW" "$MYSQL_DB" < gitea-db.sql && rm gitea-db.sql && echo " OK."
rmdir "$zip_dir"
restart_service
regenerate_hooks
echo "Done."
}
do_prune() {
echo "Pruning Gitea backups and keeping $1 of them..."
dumps=("$DUMP_DIR"/gitea-dump-*.zip)
nb_of_dumps="${#dumps[@]}"
nb_to_keep="$1"
nb_to_prune=$((nb_of_dumps - nb_to_keep))
if [ "$nb_to_prune" -gt "$nb_of_dumps" ]; then
echo "There are only $nb_of_dumps backups, quitting." && exit 1
fi
if [ "$nb_to_prune" -le 0 ]; then
echo "Nothing to do" && exit 1
else
for ((i=0; i < nb_to_prune; i++)); do
echo -n "Removing ${dumps[$i]}..." && rm "${dumps[$i]}" && echo " OK."
done
fi
}
regenerate_hooks() {
echo "Regenerating hooks..."
cd "$INSTALL_DIR" || exit 1
./gitea admin regenerate hooks --config "$CONFIG_FILE" && echo "Done."
}
stop_service() {
echo -n "Stopping service..." && sudo systemctl stop gitea.service && echo " OK."
}
restart_service() {
echo -n "Restarting service..." && sudo systemctl restart gitea.service && echo " OK."
}
[ "$#" = 0 ] || [ "$1" = '-h' ] && print_help && exit 1
if [ "$#" = 2 ]; then
if [ "$1" = restore ]; then
echo "STARTING restore" >> "$SCRIPT_LOGFILE"
do_restore "$2" >> "$SCRIPT_LOGFILE" 2>&1
echo "DONE restore" >> "$SCRIPT_LOGFILE"
elif [ "$1" = prune ]; then
echo "STARTING prune" >> "$SCRIPT_LOGFILE"
do_prune "$2" >> "$SCRIPT_LOGFILE" 2>&1
echo "DONE prune" >> "$SCRIPT_LOGFILE"
else
print_help && exit 1
fi
elif [ "$#" = 1 ]; then
if [ "$1" != backup ]; then
print_help && exit 1
fi
echo "STARTING backup" >> "$SCRIPT_LOGFILE"
do_backup >> "$SCRIPT_LOGFILE" 2>&1
echo "DONE backup" >> "$SCRIPT_LOGFILE"
else
print_help && exit 1
fi
exit 0
{% endraw %}

View file

@ -1,20 +0,0 @@
---
server_admin: tfranken@protonmail.com
gitea_domain: git.tunuifranken.info
gitea_jtw_secret: "{{ vault_gitea_jtw_secret }}"
gitea_internal_token: "{{ vault_gitea_internal_token }}"
gitea_secret_key: "{{ vault_gitea_secret_key }}"
gitea_ssh_host: tunuifranken.info
gitea_ssh_port: 22
gitea_mail_user: git
gitea_mail_pass: "{{ vault_gitea_mail_pass }}"
gitea_user: "{{ vault_gitea_user }}"
gitea_pass: "{{ vault_gitea_pass }}"
db_name: giteadb
db_user: gitea
db_pass: "{{ vault_db_pass }}"
gitea_conf_dir: /etc/gitea
gitea_run_dir: /var/lib/gitea
gitea_custom_dir: "{{ gitea_run_dir }}/custom"
gitea_data_dir: "{{ gitea_run_dir }}/data"
gitea_log_dir: /var/log/gitea

View file

@ -1,26 +0,0 @@
$ANSIBLE_VAULT;1.1;AES256
33383163303062386335316434353633663432356239323763653633313834663063653561323935
6531633231363837633435383237313434633039386565350a333734356235633534393262353537
61616232346134643838316335333161383364663632626366633537653734613966306161373865
6364376561633263610a313266346134623565633236613564346461366562363765336266666237
34323734356533356635643738353565393832366339666232343936353631326632656266363539
62613865346637656235613432356166383930353263396434303335303136353133373666303431
63626364343233353437363864363132636434323639363232323935383536383666653530666537
63613231653932366438393632393262373066643961623237313639353934333232313437343461
38323938386465363033366234326138383639323136333735616466393962646434646438333939
66316136326131313636386239346333313933316533396161346236363963353964396131316337
62656232373636336236393432386538336233303565383966323433353237363662636135336563
36306635386639373036303734653339313734616461643838613766613437376335366263623561
63353366633262306262356438623965393464646332653333646462656562386564353532346638
39343235356366376461313063323036306639623463353764366331353963326166353235353937
33613231316365376565623431386166613031366635363663346363383462373166333431303764
30303864323264613133386365306366643634626562633266656535616565313830376338363237
64623236666137313365663038393263373061306261303066636230336530346239383066343438
33636163383866353634393333386463323265393932373834383132306464383538663836303665
31373262353733613732616432303935303738363563343033656465376465663630353235303839
61616132643633363466383632643261633732343461313136373966323735386331626431396364
39653938313366646366363233393938346435626632383363366632633931303866303562643931
32363534646537613663343266356365656238396533363331623639356437366462353931353130
32366232353365666366646635663638663935363331303230373036336435313832323864366331
64623637306161316637356562336333366635653965386166323065646463623564616234353438
61366635613066373132653566633832346565613736383831306236643661313365