Create TOC

2020년 11월 29일

KDE/일출, 일몰에 따라 자동으로 테마 변경하기

KDE에는 일출, 일몰에 따라 자동으로 테마를 변경하는 설정이 없어서 따로 스크립트를 만들어서 변경한다.

일몰, 일출 시간을 얻기 위해서 python3-astral 패키지를 설치한다.

$ sudo apt install python3-astral

아래 python 스크립트를 ~/.local/bin/lookandfeel_change.py 으로 저장한다.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:set expandtab fenc=utf-8 ff=unix:

import os
import logging
import datetime
import subprocess
import argparse
from astral import Astral  # type:ignore


logging.basicConfig(format='[%(asctime)s]{%(name)s}(%(levelname)s) %(message)s', level=logging.INFO)
logger = logging.getLogger(os.path.basename(__file__))
KST = datetime.timezone(datetime.timedelta(hours=9))

DARK_THEME = 'org.kde.breezedark.desktop'
LIGHT_THEME = 'org.kde.breeze.desktop'


def kreadconfig(group: str, key: str) -> str:
    p = subprocess.run(['/usr/bin/kreadconfig5', '--group', group, '--key', key],
                       capture_output=True)
    return p.stdout.decode('utf-8').strip()


def kwriteconfig(group: str, key: str, value: str):
    p = subprocess.run(['/usr/bin/kwriteconfig5', '--group', group, '--key', key, value],
                       capture_output=True)
    logger.debug(p.stdout.decode('utf-8').strip())
    logger.debug(p.stderr.decode('utf-8').strip())


def apply_theme(theme: str):
    p = subprocess.run(['/usr/bin/lookandfeeltool', '-a', theme], capture_output=True)
    logger.debug(p.stdout.decode('utf-8').strip())
    logger.debug(p.stderr.decode('utf-8').strip())
    kwriteconfig('KDE', 'LookAndFeelPackage', theme)


def main():
    ast = Astral()
    city = ast['Seoul']
    now = datetime.datetime.now(tz=KST)
    today = city.sun(date=now, local=True)
    logger.debug(f'{today["sunrise"]} <= {now} < {today["sunset"]}')

    new_theme = DARK_THEME
    if (today['sunrise'] <= now < today['sunset']):
        new_theme = LIGHT_THEME

    cur_theme = kreadconfig('KDE', 'LookAndFeelPackage')

    if cur_theme != new_theme:
        apply_theme(new_theme)
        logger.debug(f'apply {new_theme}')
    else:
        logger.debug(f'already applied {cur_theme}')


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='일출/일몰시간을 기준으로 Plasma 테마를 변경')

    parser.add_argument('--debug', '-d', action='store_true')

    options = parser.parse_args()

    if options.debug:
        logger.setLevel(logging.DEBUG)

    main()

저장 후 실행 속성을 준다.

$ chmod +x ~/.local/bin/lookandfeel_change.py

이대로 cron에 등록하면 제대로 동작하지 않는다. cron으로 실행하는 경우 login 했을 때와 환경 변수 구성이 다르기 때문이다. 아래 script를 ~/.local/bin/lookandfeel_change.sh으로 저장한다.

#!/bin/bash

export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/계정id/bus
export DESKTOP_SESSION=plasma
export DISPLAY=:0
export PATH=/home/계정명/.local/bin:/usr/local/bin/:/usr/local/bin:/usr/bin:/bin:/usr/games
export SHELL=/bin/bash
export XDG_CURRENT_DESKTOP=KDE
export XDG_RUNTIME_DIR=/run/user/계정id
export XDG_DATA_DIRS=/var/lib/flatpak/exports/share:/usr/local/share:/usr/share

/home/계정명/.local/bin/lookandfeel_change.py

계정id는 id 명령으로 확인할 수 있다. Debian 계열인 경우 첫번째로 추가한 사용자 id는 보통 1000 이다.

$ id

저장 후 실행 속성을 준다.

$ chmod +x ~/.local/bin/lookandfeel_change.sh

이제 10분마다 실행할 수 있게 crontab에 등록한다.

*/10 * * * * /home/계정명/.local/bin/lookandfeel_change.sh

Debian/anbox

Debian Sid에 anbox를 설치하고 playstore 설치하는 방법을 기술한다.

anbox설치

$ sudo apt install anbox

android 이미지 설치

anbox 패키지에는 android 이미지를 가지고 있지 않다. 아래와 같이 파일을 받아서 /var/lib/anbox에 넣어줘야 한다.

$ wget https://build.anbox.io/android-images/2018/07/19/android_amd64.img
$ sudo mv android_amd64.img /var/lib/anbox/android.img

PlayStore 설치

install-playstore.sh스크립트를 받아서 설치한다.

$ sudo apt install lzip squashfs-tools
$ wget https://raw.githubusercontent.com/geeks-r-us/anbox-playstore-installer/master/install-playstore.sh
$ sudo mkdir /etc/systemd/system/anbox-container-manager.service.d/
$ sudo bash install-playstore.sh
$ sudo apt purge lzip squashfs-tools

설치가 끝나면 아래와 같이 실행하고

$ anbox launch --package=org.anbox.appmgr --component=org.anbox.appmgr.AppViewActivity

Settings를 실행하고 Apps > Google Play Services > Permissions 에서 모든 권한을 주면 된다.

실행 예

설치해보면 실행 안되는 앱이 꽤 많다.

ESCline

네이버 카페

2020년 11월 18일

Debian/rclone + autofs

rclone mount을 autofs에 적용하는 방법을 기술한다.

rclone 으로 myremote remote 설정을 추가했다고 가정한다.

설치

$ sudo apt install autofs fuse3

rclonefs

rclone mount helper 스크립트는 한글 locale 에서 제대로 동작하지 않는다. 아래와 같이 수정해서 /usr/local/bin/rclonefs로 저장한다.

#!/bin/bash
remote=$1
mountpoint=$2
shift 2

# Process -o parameters
while getopts :o: opts; do
	case $opts in
		o)
			params=${OPTARG//,/ }
			for param in $params; do
				if [ "$param" == "rw"   ]; then continue; fi
				if [ "$param" == "ro"   ]; then continue; fi
				if [ "$param" == "dev"  ]; then continue; fi
				if [ "$param" == "suid" ]; then continue; fi
				if [ "$param" == "exec" ]; then continue; fi
				if [ "$param" == "auto" ]; then continue; fi
				if [ "$param" == "nodev" ]; then continue; fi
				if [ "$param" == "nosuid" ]; then continue; fi
				if [ "$param" == "noexec" ]; then continue; fi
				if [ "$param" == "noauto" ]; then continue; fi
				if [[ $param == x-systemd.* ]]; then continue; fi
				trans="$trans --$param"
			done
			;;
		\?)
			echo "Invalid option: -$OPTARG"
			;;
	esac
done

# exec rclone
trans="$trans $remote $mountpoint"
# NOTE: do not try "mount --daemon" here, it does not play well with systemd automount, use '&'!
# NOTE: mount is suid and ignores pre-set PATHs -> specify explicitely
PATH=$PATH rclone mount $trans /dev/null 2>/dev/null &

# WARNING: this will loop forever if remote is actually empty!
until [ "`LANG=C \ls -l $mountpoint`" != 'total 0' ]; do
	sleep 1
done

저장 후 실행 권한을 준다.

$ sudo chmod +x /usr/local/bin/rclonefs

autofs 설정

cloud.autofs

아래 내용으로 /etc/auto.master.d/cloud.autofs파일을 만든다.

/cloud	/etc/auto.master.d/auto.rclone

auto.rclone

아래 내용을 참고로 /etc/auto.master.d/auto.rclone 파일을 만든다.

myremotedrive	-fstype=fuse.rclonefs,config=/home/$USER/.config/rclone/rclone.conf,allow-other,uid=$UID	:myremote:

설정 적용

autofs를 다시 시작한다.

$ sudo systemctl restart autofs.service

테스트

아래 명령으로 myremote 내용이 잘보이는지 확인해본다.

$ ls /cloud/myremotedrive

Debian/ZSwap 설정

initramfs 설정

z3fold를 추가한다.

$ sudo -s
# echo z3fold >> /etc/initramfs-tools/modules
# update-initramfs -u

grub 설정

/etc/default/grub 파일을 편집한다.

$ sudo vi /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULTzswap.enabled=1zswap.zpool=z3fold를 추가한다.

GRUB_CMDLINE_LINUX_DEFAULT="quiet zswap.enabled=1 zswap.zpool=z3fold"

grub 설정을 반영한다.

$ sudo update-grub

2020년 11월 8일

Debian/VirtualBox의 VRDE 설정

VNC 비밀 번호 설정

Debian에서 VirtualBox 패키지만 설치한 경우 원격디스플레이를 VNC를 사용할 수 있다. 단, 이때 VNC 비밀번호를 설정해줘야 한다.

$ VBoxManage modifyvm "가상머신 이름" --vrdeproperty VNCPassword=비밀번호

가상머신 이름은 아래와 같이 찾을 수 있다.

$ VBoxManage list vms

RDP 사용

우선 virtualbox-ext-pack을 설치한다.

$ sudo apt install virtualbox-ext-pack

VNC 대신 RDP를 사용하고 싶은 가상머신에 대해서 아래와 같이 설정한다.

$ VBoxManage modifyvm "가상머신 이름" --vrdeextpack "Oracle VM VirtualBox Extension Pack"

기본값으로 되돌리고 싶으면 아래와 같이 입력한다

$ VBoxManage modifyvm "가상머신 이름" --vrdeextpack default

2020년 9월 11일

python개발을 위한 code-server 설정

목차

    2020년 8월 29일

    Raspberry Pi4/몇 가지 팁

    wifi/bluetooth 끄기

    /boot/config.txtdtoverlay=disable-wifi, dtoverlay=disable-bt를 추가한다.

    [all]
    dtoverlay=disable-wifi
    dtoverlay=disable-bt

    IPv6 끄기

    sysctl 설정을 추가한다.

    $ sudo vi /etc/sysctl.d/local.conf
    
    # Disable IPv6
    net.ipv6.conf.all.disable_ipv6 = 1

    pigpiod 오류

    IPv6를 끄면 pigpiod.service가 동작하지 않는다.V73에서는 수정되었다고 한다. /lib/systemd/system/pigpiod.service에서 pigpiod 에 -n 127.0.0.1 옵션을 추가해준다.

    [Service]
    ExecStart=/usr/bin/pigpiod -l -n 127.0.0.1

    dhcp 서비스가 오류나면서 ip를 잃어버릴 때

    docker를 사용하는 경우 아래와 같이 docker에서 사용하는 interface를 제외시킨다.

    $ sudo vi /etc/dhcpcd.conf
    
    denyinterfaces veth*

    오류 발생시 고정 ip를 사용하도록 설정을 추가한다.

    $ sudo vi /etc/dhcpcd.conf
    
    
    # It is possible to fall back to a static IP if DHCP fails:
    # define static profile
    profile static_eth0
    static ip_address=192.168.0.x/24
    static routers=192.168.0.100
    static domain_name_servers=1.1.1.1
    
    # fallback to static profile on eth0
    interface eth0
    fallback static_eth0

    2020년 7월 30일

    Oracle Cloud VM 설정

    VM instance 만들기

    오라클 클라우드에서 평생 무료로 VPS 사용하기를 따라서 만들면 된다.

    도메인 설정

    컴퓨트 - 인스턴스에서 만든 인스턴스를 선택하고 공용 IP 주소를 확인해서 도메인 설정에 이용한다.

    쉘 변경

    $ sudo apt install zsh
    $ sudo chsh -s /bin/zsh ubuntu
    

    docker 환경 구성

    $ sudo apt install docker-compose
    $ sudo usermod -aG docker $USER
    

    Let's Enctrypt 인증서 발급

    방화벽 설정

    컴퓨트 - 인스턴스에서 만든 인스턴스를 선택하고 기본 VNIC - 서브넷 - 보안 목록에서 사용 중인 보안 목록을 선택한 후, 수신 규칙 추가를 통해서 80 포트에 대한 수신 규칙을 추가한다.

    인증서 발급

    $ docker run -it --rm --name certbot -v '/etc/letsencrypt:/etc/letsencrypt' -p 80:80 certbot/certbot certonly --agree-tos --no-eff-email --standalone --preferred-challenges http -d 도메인
    

    인증서 갱신

    매번 컨테이너를 생성/삭제하기 귀찮아서 docker-compose를 사용했다.

    아래와 같이 docker-compose.yml파일을 만든다.

    version: "3"
    
    services:
        certbot:
            container_name: certbot
            image: certbot/certbot
            ports:
                - "80:80/tcp"
            entrypoint: certbot renew
            volumes:
                - '/etc/letsencrypt:/etc/letsencrypt'

    아래 명령으로 컨테이너를 실행한다.

    $ docker-compose up
    

    문제가 없다면 crontab에 등록해서 적당히 실행시켜 둔다

    0 0 */2 * * docker-compose -f /home/ubuntu/docker/certbot/docker-compose.yml up

    2020년 7월 20일

    Docker/Caddy 를 이용한 reverse proxy 설정

    Caddy를 이용한 reverse proxy 설정을 기록한다. 인증서는 따로 /etc/letsencrypt에 갱신하기 때문에 그 인증서를 그대로 사용하게 설정했다.

    bitwarden

    https://mydomain.net:1234 -> bitwarden

    docker-compose.yml

    version: "3"
    
    services:
        bitwarden:
            container_name: bitwarden
            image: bitwardenrs/server:raspberry
            environment:
                - WEBSOCKET_ENABLED=true
                - ./bitwarden_data:/data
            restart: unless-stopped
        caddy-bitwarden:
            container_name: caddy-bitwarden
            image: caddy
            ports:
                - '1234:1234/tcp'
            environment:
                - ACME_AGREE=false
                - DOMAIN=mydomain.net
                - CADDY_PORT=1234
                - REDIRECT_URL=bitwarden:80
            volumes:
                - ./Caddyfile:/etc/caddy/Caddyfile:ro
                - /etc/letsencrypt:/etc/letsencrypt:ro
            restart: unless-stopped

    Caddyfile

    {$DOMAIN}:{$CADDY_PORT} {
    	tls /etc/letsencrypt/live/{$DOMAIN}/fullchain.pem /etc/letsencrypt/live/{$DOMAIN}/privkey.pem
    
    	header / {
    		Strict-Transport-Security "max-age=15768000;"
    		X-XSS-Protection "1; mode=block"
    		X-Frame-Options "DENY"
    		X-Robots-Tag "none"
    	}
    
    	# The negotiation endpoint is also proxied to Rocket
    	reverse_proxy /notifications/hub/negotiate bitwarden:80
    
    	# Notifications redirected to the websockets server
    	reverse_proxy /notifications/hub bitwarden:3012
    
    	# Proxy the Root directory to Rocket
    	reverse_proxy /* {$REDIRECT_URL}
    }

    nextcloud

    https://mydomain.net:1234 -> php-fpm(nextcloud)

    docker-compose.yml

    version: '3.1'
    
    services:
        nextcloud:
            image: nextcloud:fpm-alpine
            container_name: nextcloud
            environment:
                - TZ=Asia/Seoul
                - PUID=1001
                - PGID=1001
                - MYSQL_DATABASE=db이름
                - MYSQL_USER=mysql사용자이름
                - MYSQL_PASSWORD=mysql비밀번호
                - MYSQL_HOST=mysql서버주소
                - NEXTCLOUD_TRUSTED_DOMAINS=mydomain.net:1234
                - NEXTCLOUD_ADMIN_USER=nextcloud관리자이름
                - NEXTCLOUD_ADMIN_PASSWORD=nextcloud관리자비밀번호
            volumes:
                - ./nextcloud_html:/var/www/html
            restart: unless-stopped
        caddy-nextcloud:
            container_name: caddy-nextcloud
            image: caddy
            ports:
                - '1234:1234/tcp'
            environment:
                - ACME_AGREE=false
                - DOMAIN=mydomain.net
                - CADDY_PORT=1234
            volumes:
                - ./Caddyfile:/etc/caddy/Caddyfile:ro
                - ./nextcloud_html:/var/www/html:ro
                - /etc/letsencrypt:/etc/letsencrypt:ro
            restart: unless-stopped

    Caddyfile

    {$DOMAIN}:{$CADDY_PORT} {
    	tls /etc/letsencrypt/live/{$DOMAIN}/fullchain.pem /etc/letsencrypt/live/{$DOMAIN}/privkey.pem
    
    	header / {
    		Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;"
    		Referrer-Policy "no-referrer"
    		X-Content-Type-Options "nosniff"
    		X-Download-Options "noopen"
    		X-Frame-Options "SAMEORIGIN"
    		X-Permitted-Cross-Domain-Policies "none"
    		X-Robots-Tag "none"
    		X-XSS-Protection "1; mode=block"
    	}
    
    	root * /var/www/html
    	file_server
    
    	php_fastcgi nextcloud:9000
    
    	header /core/fonts {
    		Cache-Control "max-age=604800"
    	}
    
    	@phpFiles {
    		path_regexp phpfile ^/(remote|public|cron|core/ajax/update|status|ocs/v1|ocs/v2)\.php
    	}
    	rewrite @phpFiles {http.regexp.phpfile.0}
    
    	# Service discovery via well-known
    	redir /.well-known/carddav /remote.php/carddav 301
    	redir /.well-known/caldav /remote.php/caldav 301
    
    	@forbidden {
    		path	/.htaccess
    		path	/data/*
    		path	/config/*
    		path	/db_structure
    		path	/.xml
    		path	/README
    		path	/3rdparty/*
    		path	/lib/*
    		path	/templates/*
    		path	/occ
    		path	/console.php
    	}
    
    	respond @forbidden 404
    }

    path 제거

    {$DOMAIN}:{$CADDY_PORT}/foo/ -> 172.16.10.5:449/

    Caddyfile

    {$DOMAIN}:{$CADDY_PORT} {
    	tls /etc/letsencrypt/live/{$DOMAIN}/fullchain.pem /etc/letsencrypt/live/{$DOMAIN}/privkey.pem
    
    	header / {
    		# Enable HTTP Strict Transport Security (HSTS)
    		Strict-Transport-Security "max-age=15768000;"
    		X-Frame-Options "DENY"
    		X-Robots-Tag "none"
    	}
    
    	route /foo/* {
    		uri strip_prefix /foo
    		reverse_proxy 172.16.10.5:449
    	}
    }

    path 변경

    {$DOMAIN}:{$CADDY_PORT}/foo/ -> 172.16.10.5:449/bar/

    Caddyfile

    {$DOMAIN}:{$CADDY_PORT} {
    	tls /etc/letsencrypt/live/{$DOMAIN}/fullchain.pem /etc/letsencrypt/live/{$DOMAIN}/privkey.pem
    
    	header / {
    		# Enable HTTP Strict Transport Security (HSTS)
    		Strict-Transport-Security "max-age=15768000;"
    		X-Frame-Options "DENY"
    		X-Robots-Tag "none"
    	}
    
    	route /foo/* {
    		uri replace /foo/ /bar/
    		reverse_proxy 172.16.10.5:449
    	}
    }

    Raspbian/Watchdog 설정

    Raspberry pi에는 hardware watchdog timer가 있다. 이를 활용하면 문제가 발생했을 때 자동으로 재부팅시킬 수 있다.

    watchdog timer가 있는지 확인안다.

    $ dmesg | grep -i watchdog
    [    0.399099] bcm2835-wdt bcm2835-wdt: Broadcom BCM2835 watchdog timer
    

    /etc/systemd/system.conf 파일에서 RuntimeWatchdogSec 값과 ShutdownWatchdogSec 값을 바꾼다.

    RuntimeWatchdogSec=15
    RebootWatchdogSec=5min
    

    재부팅 후 watchdog 설정이 적용된 것을 확인한다.

    
    $ dmesg | grep -i watchdog
    [    0.399099] bcm2835-wdt bcm2835-wdt: Broadcom BCM2835 watchdog timer
    [    1.364292] systemd[1]: Hardware watchdog 'Broadcom BCM2835 Watchdog timer', version 0
    [    1.364350] systemd[1]: Set hardware watchdog to 15s.
    

    2020년 3월 4일

    Andriod/자동 프록시 설정시 Goolge Play Store나 YouTube가 동작하지 않는 문제 수정

    Andriod 기기에서 자동 프록시 (pac)를 설정하면 Google Play Store나 YouTube가 동작하지 않는다. 이때는 설정 - 앱 및 알림 - 배터리 최적화 (Optimize battery) 대상에서 ProxyHandler를 빼면 된다(최적화 하지 않음).

    2020년 1월 3일

    Raspbian/docker + GreenTunnel

    raspbian에서 GreenTunnel을 docker로 올리는 방법을 기술한다.

    docker image 만들기

    GreenTunnel 소스를 받는다

    $ git clone https://github.com/SadeghHayeri/GreenTunnel GreenTunnel
    

    GreenTunnel/Dockerfile 파일에서 FROM 부분을 아래와 같이 수정한다.

    FROM node:lts-buster-slim

    이미지를 빌드한다.

    $ cd GreenTunnel
    $ sudo docker build --tag green-tunnel ./
    

    이미지가 생성되었는지 확인한다.

    $ sudo docker images
    REPOSITORY                    TAG                 IMAGE ID            CREATED             SIZE
    green-tunnel                  latest              e0f67216c1d5        19 hours ago        158MB
    node                          lts-buster-slim     b490682c0125        5 days ago          134MB
    

    docker-compose.yml

    version: "3"
    
    services:
        green-tunnel:
            container_name: green-tunnel
            image: green-tunnel
            ports:
                - "8000:8000/tcp"
            restart: unless-stopped

    docker-compose 명령으로 container를 만든다.

    $ sudo docker-compose up -d
    

    테스트

    GreenTunnel을 실행시킨 raspberry pi의 ip와 port 8000 으로Firefox 등의 웹 브라우저의 proxy 설정에서 https proxy를 설정한다.

    GreenTunnel 로그는 아래 명령으로 확인할 수 있다.

    $ sudo docker logs -f green-tunnel
    

    proxy.pac

    이제 원하는 사이트에 접속할 때만 GreenTunnel 을 사용하도록 자동 프록시 설정 파일(proxy.pac)을 만든다.

    예제는 https://abc*.net, https://def.net, 접속에서만 GreenTunnel을 사용하도록 설정했다.

    function FindProxyForURL(url, host) {
        url = url.toLowerCase();
        host = host.toLowerCase();
    
        isHttp=(url.substring(0, 5) == "http:");
    
        if (isHttp) {
            return "DIRECT";
        }
    
        if (0
            || isPlainHostName(host)
            || shExpMatch(host, "*.local")
            || isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0")
            || isInNet(dnsResolve(host), "172.16.0.0",  "255.240.0.0")
            || isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0")
            || isInNet(dnsResolve(host), "127.0.0.0", "255.255.255.0")
            ) {
            return "DIRECT";
        }
    
        if (0
            || shExpMatch(url, "https://abc*.net/*")
            || shExpMatch(url, "https://def.net/*")
            ) {
            return "PROXY GreenTunnel주소:8000";
        }
        return "DIRECT";
    }

    proxy.pac을 http 로 접속 가능한 위치에 올리고 원하는 브라우저나 시스템 설정에서 http://어딘가/proxy.pac 형식으로 자동 프록시를 등록하면 된다.