feat: use hexo
This commit is contained in:
600
source/_posts/2021/arch-linux-setup-guide.md
Normal file
600
source/_posts/2021/arch-linux-setup-guide.md
Normal file
@@ -0,0 +1,600 @@
|
||||
---
|
||||
title: Arch Linux Setup Guide
|
||||
date: 2021-02-12
|
||||
---
|
||||
|
||||
This note includes all commands I typed when I setup Arch Linux on my new baremetal server.
|
||||
|
||||
# Why I choose Arch Linux
|
||||
|
||||
- Simple as it should be
|
||||
- Outstanding community efforts to maintaining package registry
|
||||
- Well organized wiki resources
|
||||
|
||||
# Useful links
|
||||
|
||||
- [General recommendations](https://wiki.archlinux.org/index.php/General_recommendations#Users_and_groups)
|
||||
- [System maintenance](https://wiki.archlinux.org/index.php/System_maintenance)
|
||||
- [Improving performance](https://wiki.archlinux.org/index.php/Improving_performance#Know_your_system)
|
||||
- [Benchmarking - ArchWiki](https://wiki.archlinux.org/index.php/Benchmarking)
|
||||
|
||||
# Provisioning
|
||||
|
||||
## wipe whole disk
|
||||
|
||||
```bash
|
||||
wipefs -a /dev/sda
|
||||
```
|
||||
|
||||
## create parition
|
||||
|
||||
```bash
|
||||
parted
|
||||
|
||||
select /dev/sda
|
||||
mktable gpt
|
||||
mkpart EFI fat32 0 512MB # EFI
|
||||
mkpart Arch ext4 512MB 100% # Arch
|
||||
set 1 esp on # flag part1 as ESP
|
||||
quit
|
||||
```
|
||||
|
||||
## install file-system
|
||||
|
||||
```bash
|
||||
mkfs.vfat -F 32 /dev/sda1 # EFI
|
||||
mkfs.ext4 /dev/sda2 # Arch
|
||||
```
|
||||
|
||||
## mount disk
|
||||
|
||||
```bash
|
||||
mkdir -p /mnt/boot
|
||||
mount /dev/sda2 /mnt
|
||||
mount /dev/sda1 /mnt/boot
|
||||
```
|
||||
|
||||
## install base & linux kernel
|
||||
|
||||
```bash
|
||||
reflector -f 10 --latest 30 --protocol https --sort rate --save /etc/pacman.d/mirrorlist # optimize mirror list
|
||||
|
||||
pacstrap /mnt base linux linux-firmware vim man-db man-pages git informant
|
||||
# base-devel need to be included as well?
|
||||
genfstab -U /mnt >> /mnt/etc/fstab
|
||||
arch-chroot /mnt
|
||||
```
|
||||
|
||||
```bash
|
||||
pacman -Syu # upgrade
|
||||
pacman -Qe # list explicitly installed pkgs
|
||||
pacman -Rs # remove pkg and its deps
|
||||
pacman -Qtd # list orphans
|
||||
```
|
||||
|
||||
## bootloader
|
||||
|
||||
```bash
|
||||
pacman -S \
|
||||
grub \
|
||||
efibootmgr \
|
||||
amd-ucode # AMD microcode
|
||||
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB
|
||||
grub-mkconfig -o /boot/grub/grub.cfg
|
||||
```
|
||||
|
||||
## ntp
|
||||
|
||||
```bash
|
||||
sed -i -e 's/#NTP=/NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org/' -e 's/#Fall/Fall/' /etc/systemd/timesyncd.conf
|
||||
systemctl enable --now systemd-timesyncd
|
||||
```
|
||||
|
||||
## locale
|
||||
|
||||
```bash
|
||||
ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
|
||||
hwclock --systohc
|
||||
vim /etc/locale.gen & locale-gen
|
||||
echo "LANG=en_US.UTF-8" > /etc/locale.conf
|
||||
```
|
||||
|
||||
## network
|
||||
|
||||
```bash
|
||||
hostnamectl set-hostname polka
|
||||
hostnamectl set-chassis server
|
||||
|
||||
vim /etc/hosts
|
||||
# 127.0.0.1 localhost
|
||||
# ::1 localhost
|
||||
# 127.0.0.1 polka
|
||||
```
|
||||
|
||||
See https://systemd.network/systemd.network.html.
|
||||
|
||||
```ini
|
||||
# /etc/systemd/network/wired.network
|
||||
[Match]
|
||||
Name=enp5s0
|
||||
|
||||
[Network]
|
||||
#DHCP=yes
|
||||
Address=10.0.1.2/24
|
||||
Gateway=10.0.1.1
|
||||
DNS=10.0.1.100 # self-hosted DNS resolver
|
||||
DNS=1.1.1.1 # Cloudflare for the fallback DNS server
|
||||
MACVLAN=dns-shim # to handle local dns lookup to 10.0.1.100 which is managed by Docker macvlan driver
|
||||
```
|
||||
|
||||
```ini
|
||||
# /etc/systemd/network/dns-shim.netdev
|
||||
# to handle local dns lookup to 10.0.1.100
|
||||
[NetDev]
|
||||
Name=dns-shim
|
||||
Kind=macvlan
|
||||
|
||||
[MACVLAN]
|
||||
Mode=bridge
|
||||
```
|
||||
|
||||
```ini
|
||||
# /etc/systemd/network/dns-shim.network
|
||||
# to handle local dns lookup to 10.0.1.100
|
||||
[Match]
|
||||
Name=dns-shim
|
||||
|
||||
[Network]
|
||||
IPForward=yes
|
||||
|
||||
[Address]
|
||||
Address=10.0.1.103/32
|
||||
Scope=link
|
||||
|
||||
[Route]
|
||||
Destination=10.0.1.100/30
|
||||
```
|
||||
|
||||
`ip` equivalent to the above settings:
|
||||
|
||||
```bash
|
||||
ip link add dns-shim link enp5s0 type macvlan mode bridge # add macvlan shim
|
||||
ip a add 10.0.1.103/32 dev dns-shim # assign host ip to shim defined in docker-compose.yml
|
||||
ip link set dns-shim up # enable interface
|
||||
ip route add 10.0.1.100/30 dev dns-shim # route macvlan subnet to shim interface
|
||||
```
|
||||
|
||||
```bash
|
||||
systemctl enable --now systemd-networkd
|
||||
networkctl status
|
||||
|
||||
# for self-hosted dns resolver
|
||||
sed -r -i -e 's/#?DNSStubListener=yes/DNSStubListener=no/g' -e 's/#DNS=/DNS=10.0.1.100/g' /etc/systemd/resolved.conf
|
||||
ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
|
||||
|
||||
systemctl enable --now systemd-resolved
|
||||
resolvectl status
|
||||
resolvectl query ddg.gg
|
||||
drill @10.0.1.100 ddg.gg
|
||||
|
||||
# FIXME
|
||||
pacman -S wpa_supplicant
|
||||
vim /etc/wpa_supplicant/wpa_supplicant.conf
|
||||
# ctrl_interface=/run/wpa_supplicant
|
||||
# update_config=1
|
||||
wpa_supplicant -B -i wlp8s0 -c /etc/wpa_supplicant/wpa_supplicant.conf
|
||||
wpa_cli # default control socket -> /var/run/wpa_supplicant
|
||||
modinfo iwlwifi
|
||||
```
|
||||
|
||||
If `networkctl` keep showing `enp5s0` as `degraded`, then run `ip addr add 10.0.1.2/24 dev enp5s0 ` to manually assign static IP address for the workaround.
|
||||
|
||||
## firewall
|
||||
|
||||
```bash
|
||||
pacman -S firewalld
|
||||
# TODO
|
||||
```
|
||||
|
||||
See also [Introduction to Netfilter – To Linux and beyond !](https://home.regit.org/netfilter-en/netfilter/)
|
||||
|
||||
## shell
|
||||
|
||||
```bash
|
||||
pacman -S zsh
|
||||
chsh -s /bin/zsh
|
||||
```
|
||||
|
||||
## user
|
||||
|
||||
```bash
|
||||
passwd # change root passwd
|
||||
|
||||
useradd -m -s /bin/zsh uetchy # add local user
|
||||
passwd uetchy # change local user password
|
||||
|
||||
userdbctl # verify users
|
||||
|
||||
pacman -S sudo
|
||||
echo "%sudo ALL=(ALL) NOPASSWD:/usr/bin/pacman" > /etc/sudoers.d/pacman # allow pacman without password
|
||||
usermod -aG sudo uetchy # add local user to sudo group
|
||||
```
|
||||
|
||||
## ssh
|
||||
|
||||
```bash
|
||||
pacman -S openssh
|
||||
vim /etc/ssh/sshd_config
|
||||
systemctl enable --now sshd
|
||||
```
|
||||
|
||||
on the host machine:
|
||||
|
||||
```bash
|
||||
ssh-copy-id uetchy@10.0.1.2
|
||||
```
|
||||
|
||||
## AUR
|
||||
|
||||
```bash
|
||||
git clone https://aur.archlinux.org/yay.git
|
||||
cd yay
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
## finalize
|
||||
|
||||
```bash
|
||||
exit # leave chroot
|
||||
umount -R /mnt
|
||||
reboot
|
||||
```
|
||||
|
||||
# Additional setup
|
||||
|
||||
## gpgpu
|
||||
|
||||
```bash
|
||||
pacman -S nvidia
|
||||
cat /var/lib/modprobe.d/nvidia.conf # ensure having 'blacklist nouveau'
|
||||
|
||||
yay -S cuda-10.2 cudnn7-cuda10.2 # match the version number
|
||||
|
||||
nvidia-smi # test runtime
|
||||
```
|
||||
|
||||
## docker
|
||||
|
||||
```bash
|
||||
pacman -S docker docker-compose
|
||||
yay -S nvidia-container-runtime-bin
|
||||
vim /etc/docker/daemon.json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"log-driver": "journald",
|
||||
"log-opts": {
|
||||
"tag": "{{.ImageName}}/{{.Name}}/{{.ID}}"
|
||||
},
|
||||
"exec-opts": ["native.cgroupdriver=systemd"], // for kubernetes
|
||||
"runtimes": {
|
||||
// for docker-compose
|
||||
"nvidia": {
|
||||
"path": "/usr/bin/nvidia-container-runtime",
|
||||
"runtimeArgs": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
systemctl enable --now docker
|
||||
|
||||
groupadd docker
|
||||
usermod -aG docker user
|
||||
|
||||
docker run --rm -it --gpus all nvidia/cuda:10.2-cudnn7-runtime
|
||||
```
|
||||
|
||||
## telegraf
|
||||
|
||||
```bash
|
||||
yay -S telegraf
|
||||
vim /etc/telegraf/telegraf.conf
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/sudoers.d/telegraf
|
||||
Cmnd_Alias FAIL2BAN = /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status *
|
||||
telegraf ALL=(root) NOEXEC: NOPASSWD: FAIL2BAN
|
||||
Defaults!FAIL2BAN !logfile, !syslog, !pam_session
|
||||
```
|
||||
|
||||
## fail2ban
|
||||
|
||||
```
|
||||
pacman -S fail2ban
|
||||
systemctl enable --now fail2ban
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/fail2ban/jail.local
|
||||
[DEFAULT]
|
||||
bantime = 60m
|
||||
ignoreip = 127.0.0.1/8 10.0.1.0/24
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = 22,10122
|
||||
|
||||
[mailu]
|
||||
enabled = true
|
||||
backend = systemd
|
||||
journalmatch = CONTAINER_NAME=mailu_front_1
|
||||
port = smtp,submission
|
||||
chain = DOCKER-USER
|
||||
filter = mailu
|
||||
findtime = 600
|
||||
maxretry = 1
|
||||
bantime = 1d
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/fail2ban/filter.d/mailu.conf
|
||||
[INCLUDES]
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
failregex = ^%(__prefix_line)s\d+\/\d+\/\d+ \d+:\d+:\d+ \[info\] \d+#\d+: \*\d+ client login failed: "Authentication credentials invalid" while in http auth state, client: <HOST>, server: \S+, login: "<F-USER>\S+</F-USER>"$
|
||||
ignoreregex =
|
||||
```
|
||||
|
||||
```
|
||||
fail2ban-client reload
|
||||
fail2ban-client status mailu
|
||||
```
|
||||
|
||||
## sendmail
|
||||
|
||||
```bash
|
||||
yay -S sendmail
|
||||
```
|
||||
|
||||
## cfddns
|
||||
|
||||
Dynamic DNS for Cloudflare.
|
||||
|
||||
```
|
||||
yay -S cfddns
|
||||
```
|
||||
|
||||
```yml
|
||||
# File: /etc/cfddns/cfddns.yml
|
||||
token: <token>
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/cfddns/domains
|
||||
uechi.io
|
||||
datastore.uechi.io
|
||||
```
|
||||
|
||||
```
|
||||
systemctl enable --now cfddns
|
||||
```
|
||||
|
||||
## smart
|
||||
|
||||
```bash
|
||||
pacman -S smartmontools
|
||||
systemctl enable --now smartd
|
||||
```
|
||||
|
||||
## backup
|
||||
|
||||
```ini
|
||||
# File: /etc/backups/borg.service
|
||||
[Unit]
|
||||
Description=Borg Daily Backup Service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Nice=19
|
||||
IOSchedulingClass=2
|
||||
IOSchedulingPriority=7
|
||||
ExecStart=/etc/backups/run.sh
|
||||
```
|
||||
|
||||
```ini
|
||||
# File: /etc/backups/borg.timer
|
||||
[Unit]
|
||||
Description=Borg Daily Backup Timer
|
||||
|
||||
[Timer]
|
||||
WakeSystem=false
|
||||
OnCalendar=*-*-* 03:00
|
||||
RandomizedDelaySec=10min
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
```bash
|
||||
# File: /etc/backups/run.sh
|
||||
sleep 5
|
||||
|
||||
#
|
||||
# Script configuration
|
||||
#
|
||||
export BORG_PASSPHRASE="<PASSPHRASE>"
|
||||
MOUNTPOINT=/mnt/backup
|
||||
TARGET=$MOUNTPOINT/borg
|
||||
|
||||
# Archive name schema
|
||||
DATE=$(date --iso-8601)
|
||||
|
||||
# Options for borg create
|
||||
BORG_OPTS="--stats --compression lz4 --checkpoint-interval 86400"
|
||||
|
||||
# No one can answer if Borg asks these questions, it is better to just fail quickly
|
||||
# instead of hanging.
|
||||
export BORG_RELOCATED_REPO_ACCESS_IS_OK=no
|
||||
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no
|
||||
|
||||
# Log Borg version
|
||||
borg --version
|
||||
|
||||
echo "Starting backup for $DATE"
|
||||
|
||||
echo "# system"
|
||||
borg create $BORG_OPTS \
|
||||
--exclude /root/.cache \
|
||||
--exclude /var/cache \
|
||||
--exclude /var/lib/docker/devicemapper \
|
||||
--exclude /home \
|
||||
--one-file-system \
|
||||
$TARGET::'{hostname}-system-{now}' \
|
||||
/ /boot
|
||||
|
||||
echo "# home"
|
||||
borg create $BORG_OPTS \
|
||||
--exclude 'sh:/home/*/.cache' \
|
||||
--exclude 'sh:/home/*/.cargo' \
|
||||
$TARGET::'{hostname}-home-{now}' \
|
||||
/home/
|
||||
|
||||
echo "# data"
|
||||
borg create $BORG_OPTS \
|
||||
$TARGET::'{hostname}-data-{now}' \
|
||||
/mnt/data
|
||||
|
||||
echo "Start pruning"
|
||||
BORG_PRUNE_OPTS="--list --stats --keep-daily 7 --keep-weekly 4 --keep-monthly 3"
|
||||
borg prune $BORG_PRUNE_OPTS --prefix '{hostname}-home-' $TARGET
|
||||
borg prune $BORG_PRUNE_OPTS --prefix '{hostname}-system-' $TARGET
|
||||
borg prune $BORG_PRUNE_OPTS --prefix '{hostname}-data-' $TARGET
|
||||
|
||||
echo "Completed backup for $DATE"
|
||||
|
||||
# Just to be completely paranoid
|
||||
sync
|
||||
```
|
||||
|
||||
```bash
|
||||
ln -sf /etc/backups/borg.* /etc/systemd/system/
|
||||
systemctl enable --now borg
|
||||
```
|
||||
|
||||
## kubernetes
|
||||
|
||||
```bash
|
||||
pacman -S kubeadm kubelet kubectl
|
||||
systemctl enable --now kubelet
|
||||
kubeadm init --pod-network-cidr='10.244.0.0/16'
|
||||
cp /etc/kubernetes/admin.conf ~/.kube/config
|
||||
|
||||
kubectl taint nodes --all node-role.kubernetes.io/master- # to allow allocating pods to the master node
|
||||
|
||||
# setup flannel network manager
|
||||
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
|
||||
|
||||
# setup nginx ingress controller
|
||||
# TODO
|
||||
|
||||
kubectl cluster-info
|
||||
kubectl get nodes
|
||||
kubectl get pods -A
|
||||
kubectl get cm -n kube-system kubeadm-config -o yaml
|
||||
```
|
||||
|
||||
[Kubernetes - ArchWiki](https://wiki.archlinux.org/index.php/Kubernetes)
|
||||
|
||||
[Kubernetes Ingress Controller with NGINX Reverse Proxy and Wildcard SSL from Let's Encrypt - Shogan.tech](https://www.shogan.co.uk/kubernetes/kubernetes-ingress-controller-with-nginx-reverse-proxy-and-wildcard-ssl-from-lets-encrypt/)
|
||||
|
||||
## certs
|
||||
|
||||
```bash
|
||||
pacman -S certbot certbot-dns-cloudflare
|
||||
echo "dns_cloudflare_api_token = <token>" > ~/.secrets/certbot/cloudflare.ini
|
||||
chmod 600 ~/.secrets/certbot/cloudflare.ini
|
||||
certbot certonly \
|
||||
--email y@uechi.io \
|
||||
--agree-tos \
|
||||
--dns-cloudflare \
|
||||
--dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
|
||||
-d "*.uechi.io"
|
||||
openssl x509 -in /etc/letsencrypt/live/uechi.io/fullchain.pem -text
|
||||
certbot certificates
|
||||
|
||||
cat <<EOD > /etc/systemd/system/certbot.service
|
||||
[Unit]
|
||||
Description=Let's Encrypt renewal
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/certbot renew --quiet --agree-tos --deploy-hook "docker exec nginx-proxy-le /app/signal_le_service"
|
||||
EOD
|
||||
|
||||
cat <<EOD > /etc/systemd/system/certbot.timer
|
||||
[Unit]
|
||||
Description=Twice daily renewal of Let's Encrypt's certificates
|
||||
|
||||
[Timer]
|
||||
OnCalendar=0/12:00:00
|
||||
RandomizedDelaySec=1h
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOD
|
||||
```
|
||||
|
||||
- [Certbot - ArchWiki](https://wiki.archlinux.org/index.php/Certbot)
|
||||
- [Welcome to certbot-dns-cloudflare’s documentation! — certbot-dns-cloudflare 0 documentation](https://certbot-dns-cloudflare.readthedocs.io/en/stable/)
|
||||
- [docker-letsencrypt-nginx-proxy-companion/Standalone-certificates.md at master · nginx-proxy/docker-letsencrypt-nginx-proxy-companion](https://github.com/nginx-proxy/docker-letsencrypt-nginx-proxy-companion/blob/master/docs/Standalone-certificates.md)
|
||||
|
||||
## audio
|
||||
|
||||
```bash
|
||||
pacman -S alsa-utils # maybe requires reboot
|
||||
arecord -L # list devices
|
||||
|
||||
cat <<EOD > /etc/asound.conf
|
||||
pcm.m96k {
|
||||
type hw
|
||||
card M96k
|
||||
rate 44100
|
||||
format S32_LE
|
||||
}
|
||||
|
||||
pcm.!default {
|
||||
type plug
|
||||
slave.pcm "m96k"
|
||||
}
|
||||
EOD
|
||||
|
||||
arecord -vv /dev/null # test mic
|
||||
```
|
||||
|
||||
```
|
||||
alsamixer # gui mixer
|
||||
```
|
||||
|
||||
- [SoundcardTesting - AlsaProject](https://www.alsa-project.org/main/index.php/SoundcardTesting)
|
||||
- [Advanced Linux Sound Architecture/Troubleshooting - ArchWiki](https://wiki.archlinux.org/index.php/Advanced_Linux_Sound_Architecture/Troubleshooting#Microphone)
|
||||
- [ALSA project - the C library reference: PCM (digital audio) plugins](https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html)
|
||||
|
||||
# Maintenance
|
||||
|
||||
```bash
|
||||
systemctl --failed
|
||||
free -h
|
||||
htop
|
||||
lsblk -f
|
||||
nvidia-smi
|
||||
iotop
|
||||
sensors
|
||||
journalctl -p err
|
||||
networkctl status
|
||||
```
|
18
source/_posts/2021/braille.md
Normal file
18
source/_posts/2021/braille.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: 点字の表現力
|
||||
date: 2021-02-13
|
||||
---
|
||||
|
||||
「n 種類の文字を表現できる点字を作らないといけなくなったらどうしよう」
|
||||
|
||||
例としてドット が 3 つの点字を用意した。
|
||||
|
||||
- 組み合わせ数は 3P3 + 3P2 + 3P1
|
||||
- $f(N,K) = \sum_{i=K → 0} N P K$
|
||||
- 2 の n 乗っぽい
|
||||
- べき集合の濃度?
|
||||
- 2 進数のブール配列と考えるとわかりやすくなった
|
||||
- ということは X 個の表現をするために必要なブール配列の長さは $\lceil\log_2 (X)\rceil$
|
||||
- 例えばアルファベットなら 6 ドットの点字で表現できる
|
||||
- だから英語の点字は 6 つの点
|
||||
- 点字を 18 ドットに拡張すれば Unicode 13.0 の文字すべてを表現できる
|
102
source/_posts/2021/oauth-jwt-rfcs.md
Normal file
102
source/_posts/2021/oauth-jwt-rfcs.md
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: OAuth 2.0 と JWT 関連 RFC
|
||||
date: 2021-02-11
|
||||
---
|
||||
|
||||
個人的な調査のために OAuth 2.0 と JWT 関連 RFC を発行日順に並べています。
|
||||
|
||||
## [RFC6749](https://tools.ietf.org/html/rfc6749) — The OAuth 2.0 Authorization Framework
|
||||
|
||||
2012 年 10 月
|
||||
|
||||
OAuth 1.0a に代わる新たな認証基盤 OAuth 2.0 のコアを規定しており、特筆すべき点がいくつかある。
|
||||
|
||||
- `access_token` の内容は規定されておらず、ベンダーに委ねられている
|
||||
- JWS でもなんでもいい
|
||||
- リソースサーバーに `access_token` を渡す方法は規定されていない(同月発行の RFC6750 で規定された)
|
||||
|
||||
### Authorization Grant
|
||||
|
||||
トークンエンドポイントで`access_token`を発行してもらう際に使用できる Grant (許可証)は、提案中の拡張仕様を含めて 5 つある。
|
||||
|
||||
1. Authorization Code Grant: [RFC6749 – Section 1.3.1](https://tools.ietf.org/html/rfc6749#section-1.3.1)
|
||||
1. `grant_type=authorization_code`
|
||||
2. Authorization Code Grant with PKCE
|
||||
2. Implicit Flow: [RFC6749 – Section 1.3.2](https://tools.ietf.org/html/rfc6749#section-1.3.2)
|
||||
1. もともと CORS (Cross Origin Resource Sharing) が登場する以前の SPA で、POST リクエストを回避しつつ Access Token を得る"妥協案"として策定された
|
||||
2. CSRF 耐性が無い ([RFC6819 - Section 4.4.2.5](https://tools.ietf.org/html/rfc6819#section-4.4.2.5))ため、使うべきではない
|
||||
3. Resource Owner Password Credentials Grant: [RFC6749 – Section 1.3.3](https://tools.ietf.org/html/rfc6749#section-1.3.3)
|
||||
1. 直接パスワードで認証する形式
|
||||
4. Client Credentials Grant: [RFC6749 – Section 1.3.4](https://tools.ietf.org/html/rfc6749#section-1.3.4)
|
||||
1. クライアントシークレットでトークンを取得する形式。
|
||||
5. Device Grant: [RFC Draft — OAuth 2.0 Device Authorization Grant](https://tools.ietf.org/html/draft-ietf-oauth-device-flow-15)
|
||||
1. 入力機器が無い場合もある組み込みデバイス向けの認証フロー
|
||||
|
||||
## [RFC6750](https://tools.ietf.org/html/rfc6750) — The OAuth 2.0 Authorization Framework: Bearer Token Usage
|
||||
|
||||
2012 年 10 月
|
||||
|
||||
OAuth 2.0 において、`access_token`をリソースサーバーに渡す手法を規定する。OAuth 2.0 JWT Bearer Token Flow**ではない**。
|
||||
|
||||
手法として 3 つが挙げられている。
|
||||
|
||||
1. Bearer Token (**SHOULD**)
|
||||
2. Form Encoded Parameters (SHOULD NOT)
|
||||
3. URI Query Parameters (SHOULD NOT)
|
||||
|
||||
## [OICD](https://openid.net/specs/openid-connect-core-1_0.html) — OpenID Connect Core 1.0
|
||||
|
||||
2014 年 11 月
|
||||
|
||||
OAuth 2.0 の上にいくつか仕様を足したサブセット。
|
||||
|
||||
## [RFC7515](https://tools.ietf.org/html/rfc7515) — JSON Web Signature (JWS)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JSON ベースの署名プロトコル。
|
||||
|
||||
## [RFC7516](https://tools.ietf.org/html/rfc7516) — JSON Web Encryption (JWE)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JSON ベースの暗号化プロトコル。
|
||||
|
||||
## [RFC7517](https://tools.ietf.org/html/rfc7517) — JSON Web Key (JWK)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JWT の署名チェックに用いる公開鍵を配信するためのプロトコル。
|
||||
|
||||
## [RFC7518](https://tools.ietf.org/html/rfc7518) — JSON Web Algorithms (JWA)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JWS、JWE、JWK で利用されるアルゴリズム (alg)やその他プロパティを規定する。
|
||||
|
||||
## [RFC7519](https://tools.ietf.org/html/rfc7519) — JSON Web Token (JWT)
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
JWT は JSON を利用して Assertion を生成するための仕様。
|
||||
|
||||
## [RFC7521](https://tools.ietf.org/html/rfc7521) — Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants
|
||||
|
||||
2015 年 5 月
|
||||
|
||||
任意の Assertion を OAuth 2.0 Client Authentication の Client Credentials として使ったり、あるいは Authorization Grant として Access Token と交換するための仕様。
|
||||
|
||||
トークンエンドポイントに強化されたクライアント認証を付与する。続く RFC で、それぞれ SAML と JWT を使用したパターンを規定している。
|
||||
|
||||
**OAuth 2.0 JWT Bearer Token Flow**とも呼ばれている。
|
||||
|
||||
- [RFC7522](https://tools.ietf.org/html/rfc7522) — Security Assertion Markup Language (**SAML**) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants (2015 年 5 月)
|
||||
- [RFC7523](https://tools.ietf.org/html/rfc7523) — JSON Web Token (**JWT**) Profile for OAuth 2.0 Client Authentication and Authorization Grants (2015 年 5 月)
|
||||
|
||||
2015 年 5 月 https://tools.ietf.org/html/rfc7523
|
||||
|
||||
## [RFC Draft](https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-02) — JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens
|
||||
|
||||
2019 年 7 月
|
||||
|
||||
リソースサーバーに渡す Access Token に JWT を使用することを定めている。
|
148
source/_posts/2021/split-bill.md
Normal file
148
source/_posts/2021/split-bill.md
Normal file
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: 最小送金回数で精算する割り勘アルゴリズム
|
||||
date: 2021-02-14
|
||||
---
|
||||
|
||||
大人数でキャンプを楽しんだあとに待っているのは耐え難き送金処理です。
|
||||
次回から楽をするために、送金回数を最小化する制約で精算表を作る方法を考えてみます。
|
||||
|
||||
# tl;dr
|
||||
|
||||
アイディアは「最も支払わなかった人が最も支払った人に払えるだけ払う ⇢ 債権を再計算して繰り返す」です。
|
||||
|
||||
1. 全員の出費を算出(払い過ぎは正、払わなさすぎは負の数)
|
||||
2. 降順でソート(出費過多が先頭)
|
||||
3. リストの最後(最大債務者, 出費=L)がリストの最初(最大債権者, F)に $\min(F, |L|)$ を支払ってバランスを再計算
|
||||
4. 全員のバランスが 0 になるまで 2-3 を繰り返す
|
||||
|
||||
# 実験
|
||||
|
||||
実際にコードを書いて本当に望んでいる結果が得られるのかを検証します。
|
||||
|
||||
```5js
|
||||
const history = [
|
||||
{
|
||||
amount: 121,
|
||||
payer: "A",
|
||||
involves: ["A", "B", "C"],
|
||||
},
|
||||
{
|
||||
amount: 98,
|
||||
payer: "B",
|
||||
involves: ["A", "B", "C"],
|
||||
},
|
||||
{
|
||||
amount: 10,
|
||||
payer: "C",
|
||||
involves: ["A", "B", "C"],
|
||||
},
|
||||
{
|
||||
amount: 10,
|
||||
payer: "C",
|
||||
involves: ["A", "B"],
|
||||
},
|
||||
{
|
||||
amount: 50,
|
||||
payer: "C",
|
||||
involves: ["A"], // meaning C lent A 50
|
||||
},
|
||||
];
|
||||
|
||||
// calculate balance sheet
|
||||
const init = { balance: 0, consumption: 0 };
|
||||
Map.prototype.fetch = function (id) {
|
||||
return (
|
||||
this.get(id) || this.set(id, Object.assign({ name: id }, init)).get(id)
|
||||
);
|
||||
};
|
||||
|
||||
const data = new Map();
|
||||
|
||||
for (const { payer, amount, involves } of history) {
|
||||
const record = data.fetch(payer);
|
||||
record.balance += amount;
|
||||
const dept = Math.ceil(amount / involves.length);
|
||||
// actual payer should not owe extra dept coming from rounded up numbers
|
||||
const payerDept = amount - dept * (involves.length - 1);
|
||||
for (const deptor of involves.map((i) => data.fetch(i))) {
|
||||
const cost = Math.round(amount / involves.length);
|
||||
deptor.balance -= cost;
|
||||
deptor.consumption += cost;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
|
||||
// calculate transaction table
|
||||
const transaction = [];
|
||||
let paidTooMuch, paidLess;
|
||||
while (true) {
|
||||
for (const [_, tbl] of data) {
|
||||
if (tbl.balance >= (paidTooMuch?.balance || 0)) {
|
||||
paidTooMuch = tbl;
|
||||
}
|
||||
if (tbl.balance <= (paidLess?.balance || 0)) {
|
||||
paidLess = tbl;
|
||||
}
|
||||
}
|
||||
|
||||
if (paidLess.balance == 0 || paidTooMuch.balance == 0) break;
|
||||
|
||||
const amount = Math.min(paidTooMuch.balance, Math.abs(paidLess.balance));
|
||||
|
||||
transaction.push({
|
||||
sender: paidLess.name,
|
||||
receiver: paidTooMuch.name,
|
||||
amount,
|
||||
});
|
||||
|
||||
paidTooMuch.balance -= amount;
|
||||
paidLess.balance += amount;
|
||||
}
|
||||
|
||||
console.log("Settled");
|
||||
|
||||
console.log("\n# Transaction table");
|
||||
for (const ev of transaction) {
|
||||
console.log(`${ev.sender} owes ${ev.receiver} ¥${ev.amount}`);
|
||||
}
|
||||
|
||||
console.log("\n# History");
|
||||
for (const { payer, amount, involves } of history) {
|
||||
if (involves.length === 1) {
|
||||
console.log(`${payer} lent ¥${amount} to ${involves[0]}`);
|
||||
} else {
|
||||
console.log(`${payer} paid ¥${amount} for ${involves.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\n# Expenses");
|
||||
for (const [_, { name, consumption }] of data) {
|
||||
console.log(`${name} virtually paid ¥${consumption} in total`);
|
||||
}
|
||||
```
|
||||
|
||||
`history`に支払い履歴を書き込んでから実行すると、「送金表」「履歴」「実質支払総額」が得られます。
|
||||
|
||||
```md
|
||||
# Transaction table
|
||||
|
||||
A owes B ¥10
|
||||
C owes B ¥6
|
||||
|
||||
# History
|
||||
|
||||
A paid ¥121 for A, B, C
|
||||
B paid ¥98 for A, B, C
|
||||
C paid ¥10 for A, B, C
|
||||
C paid ¥10 for A, B
|
||||
C lent ¥50 to A
|
||||
|
||||
# Expenses
|
||||
|
||||
A virtually paid ¥131 in total
|
||||
B virtually paid ¥81 in total
|
||||
C virtually paid ¥76 in total
|
||||
```
|
||||
|
||||
プログラムに落とし込めたら、あとはアプリを作るなりスプレッドシートのマクロにするなり自由です。面倒なことは全部コンピューターにやらせよう!
|
Reference in New Issue
Block a user