Setting up VPC endpoint for Amazon S3

By default, accessing S3 resources from any instance or Kubernetes pod within a VPC involves outbound traffic via NAT or IGW. Not only this is less efficient, it also incurs a service fee due to the traffic. The cost can be significant if traffic is huge.


To keep the traffic within VPC, an S3 accesspoint for the specific S3 resources and a VPC endpoint can be created by following the general instruction.

After that, double check the policies at two places.

1. Policy for IAM

Go to AWS ConsoleIAM User. It is supposed to be a service account that has following policy attached

    "Version": "2012-10-17",
    "Statement": [
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
            "Resource": "*"
As a security best practice, DO NOT grant Allow to all actions, i.e. "Action": ["*"].
2. Policy for the endpoint

Go to AWS ConsoleVPC Endpoints the endpointPolicy

    "Version": "2012-10-17",
    "Id": "Policy1637977229005",
    "Statement": [
            "Sid": "Stmt1637977226759",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "*"
This endpoint policy is less constrained than the policy for user, because want the policy for user to be more specific.

After all, run traceroute from any of the instances within the VPC to verify.

  • Following output shows it is successful
$ traceroute
traceroute to (, 30 hops max, 60 byte packets
 1  * * *
 2  * * *
 3  * * *
28  * * *
29  * * *
30  * * *
  • Following output shows it is still going thru NAT/IGW
$ traceroute
traceroute to (, 30 hops max, 60 byte packets
 1 (  42.290 ms (  3.103 ms (  36.088 ms
 2 (  1.149 ms (  1.132 ms (  4.671 ms
 3 (  3.400 ms (  8.787 ms (  4.695 ms
 4 (  3.511 ms (  17.491 ms (  18.193 ms
 5 (  15.377 ms (  154.101 ms (  19.897 ms
 6 (  10.907 ms (  9.064 ms (  11.826 ms
 7 (  3.090 ms (  2.821 ms (  2.648 ms
 8 (  0.518 ms  0.569 ms  0.578 ms

Follow the troubleshooting steps if anything does not work.


Now we have both S3 access point and the VPC endpoint for S3 setup.

To access the S3 resource within VPC, instead of using s3://<bucket name> directly, use

s3://arn:aws:s3:ap-southeast-1:<account number>:accesspoint/<bucket name>

Some 3rd party library built for AWS S3 might not recognize the S3 access point URL and throw exceptions like

Caused by: java.lang.NullPointerException: null uri host.
        at java.util.Objects.requireNonNull(
        at org.apache.hadoop.fs.s3native.S3xLoginHelper.buildFSURI(
        at org.apache.hadoop.fs.s3a.S3AFileSystem.setUri(
        at org.apache.hadoop.fs.s3a.S3AFileSystem.initialize(
        at org.apache.flink.fs.s3.common.AbstractS3FileSystemFactory.create(
        ... 24 more

In that case, S3 access point alias could be used. It can be found under S3 BucketAccess Points. It looks something like

s3://<bucket name>-1ks47nsk5hyxi845kebsen1nyf11caps1b-s3alias

Clean unused firewall rules using PowerShell

In Windows, firewall rules can be easily cumulated over time. Every time a new application is launched, the user will be prompted to create a new firewall rule for the app. However, when applications get uninstalled, most of them would not remove the firewall rule it has created.

Once there are more than just a few unused firewall rules, the system becomes less secure due to the exposed protocols and ports. A mocked application at the exact same file path can easily take advantage of existing firewall rules created and make the system vulnerable.

Unused firewall rules can be cleaned up with just 3 lines of PowerShell commands, under elevated command prompt:

$unusedFilters = Get-NetFirewallApplicationFilter | Where-Object {$_.Program -notin ("Any", "System")} | Where-Object {-not (Test-Path ([System.Environment]::ExpandEnvironmentVariables($_.Program)))}

$unusedRules = Get-NetFirewallRule | Where-Object {$_.Name -in $unusedFilters.InstanceId}

$unusedRules | Remove-NetFirewallRule -Verbose
PSPrompt customized using oh-my-posh

Running docker on Nexus 5

My Raspberry Pi 3 died. It was used for running Plex server. It was running inside docker before the Pi died. I was looking for replacement. I looked into my basement and I found two Google_Nexus_5_(lg-hammerhead) phones. Then the story began…


  • you are fairly familiar with how flashing works, e.g. fastboot stuff.
  • you’ve already running postmarketos on your phone
  • you know what docker is
  • you know that you are probably gonna remove all the fancy UIs from your phone, and switch to postmarketos-ui-fbkeyboard or some console only mode
  • you’d better have a LAN connection on your phone rather than wifi


I can only prove this is working on my phones for now.

1. Edit kernel config

NOTE: before you make any change to kernel, always backup ~/.local/var/pmbootstrap/cache_git/pmaports/main/linux-postmarketos-qcom-msm8974/config-postmarketos-qcom-msm8974.armv7


pmbootstrap kconfig edit postmarketos-qcom-msm8974

where qcom-msm8974 is for my Nexus 5. You need to figure out what’s for your phone.

You have 2 options to learn what to set when configuring the kernel:

You can see the changes I made to my kernel config at

BTW – I’ve got a Ugreen USB 2.0 network adapter hooked, so I took the chance and selected

Device Drivers => Network device support => <*> USB Network Adapters => <*> ASIX AX88xxx Based USB 2.0 Ethernet Adapters 

2. Build the kernel

pmbootstrap build linux-postmarketos-qcom-msm8974 --force

I had to add --force otherwise it won’t build.

If everything is successful, you should find something like below


3. Install the kernel

Get the linux-postmarketos-qcom-msm8974-5.9.0_rc4-r0.apk copied onto your phone and do

sudo apk add -u linux-postmarketos-qcom-msm8974-5.9.0_rc4-r0.apk

Just in case, I also copied /boot/boot.img-postmarketos-qcom-msm8974 to local as boot.img, boot to bootloader and did

fastboot flash boot boot.img

Reboot the phone and run again to see if anything missing. My experience is you don’t need everything enabled.

4. Install docker

Boot to your phone, assuming you have a pretty good internet connection, then do

sudo apk add docker

Not only this installs docker, but also an important service containerd.

5. Get docker daemon running


sudo service docker start

We are not there yet. This first-time boot is for creating files, directories, group it needs. The docker command won’t work because of couple things that have to happen at boot time below.

DO NOT make docker to start on boot or you will get “can’t load program: function not implemented: unknown.” error.

BTW – If you hate doing sudo docker... everytime, you can optionally add yourself to docker group by

sudo vi /etc/group

Now reboot so at least containerd service is effective, in terms of automatically mounting cgroup2 to /sys/fs/cgroup.

sudo reboot

After reboot, do

sudo service docker start

You should see something like

Sudo service docker start.png

which you won’t see if you tried to start docker service before the reboot.

6. Verify things are good

Docker info.png
health check
Docker run hello world.png
Docker run fedora.png

What’s next?

It’d be fun to have kubernetes running on this little device. Not sure if I should go with k3s or microk8s.


最近 Windows 10 的锁屏壁纸都很赞

顺便分享一下 wsl 下如何找到这些图片的命令:

$ for asset in `file -i $(wslpath $(cmd.exe /c "<nul set /p=%UserProfile%" 2>/dev/null))/AppData/Local/Packages/Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy/LocalState/Assets/* | grep png | awk -F':' '{print $1}'`; do echo cp $asset /tmp$asset.png; done

输出是一堆 cp 命令,需要执行的话把 echo 去掉就行了。

cp /mnt/c/Users/xiaoh/AppData/Local/Packages/Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy/LocalState/Assets/04f9bd3de104a427ca557d53f05747c382a62bc3efb2422a0ba9bbe8aa21e757 /tmp/mnt/c/Users/xiaoh/AppData/Local/Packages/Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy/LocalState/Assets/04f9bd3de104a427ca557d53f05747c382a62bc3efb2422a0ba9bbe8aa21e757.png
cp /mnt/c/Users/xiaoh/AppData/Local/Packages/Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy/LocalState/Assets/1181ec5a0c631705dcaded34f58a171b842ed6783274b4bc7122e66eaa498a67 /tmp/mnt/c/Users/xiaoh/AppData/Local/Packages/Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy/LocalState/Assets/1181ec5a0c631705dcaded34f58a171b842ed6783274b4bc7122e66eaa498a67.png
cp /mnt/c/Users/xiaoh/AppData/Local/Packages/Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy/LocalState/Assets/ffadc5cae56e18df849d64d219b374745eb3f046a01a7ff3316c0ce4eb5d3c64 /tmp/mnt/c/Users/xiaoh/AppData/Local/Packages/Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy/LocalState/Assets/ffadc5cae56e18df849d64d219b374745eb3f046a01a7ff3316c0ce4eb5d3c64.png


Relying on complex tools to manage and build your system is going to hurt the end users. […] “If you try to hide the complexity of the system, you’ll end up with a more complex system”. Layers of abstraction that serve to hide internals are never a good thing. Instead, the internals should be designed in a way such that they NEED no hiding.

依赖复杂的工具来管理整个系统的结果是给最终用户带来恶化的体验。[…] “如果你尝试隐藏系统的复杂性,于是就得到一个更加复杂的系统。” 为了隐藏内部机制而设置抽象层并非良好的架构方法,而应该把内部机制设计得精良而无需隐藏。

— Aaron Griffin

在 k8s 中开一个 shell

经常需要一个远程临时的 shell 做些什么事,比如实验一下某个需要运行一晚上的脚本,用完环境就扔了。在 AWS 或者 Azure 上开一个新的 VM 总是感觉不够快捷,也不想专门浪费一个 VM 的资源为这个目的留着。

还好我有个 Kubernetes 下创建 pod 的权限,这下就方便多了。

首先创建一个 shell.yaml 来描述一个可以运行 bash shell 的 pod(容器镜像用的是官方的 bash 镜像):

apiVersion: v1
kind: Pod
  name: shell
    purpose: shell
  - name: shell
    image: bash
    command: ["tail"]
    args: ["-f", "/dev/null"]
  restartPolicy: OnFailure

然后执行下面的命令来创建这个 pod:

$ kubectl apply -f shell.yaml
pod/shell created

成功之后,就可以通过下面的命令获得一个远程的 shell 环境了:

$ kubectl exec -it shell -- bash --login

这个新创建的环境是基于 alpine linux 的,自然什么工具都没有自带。需要什么工具,只要用 apk add 命令安装就好了。我因为工作需要一般都会安装下面这些工具:

apk add git
apk add nodejs-current
apk add npm
apk add python3

如果想从或者向这个 pod/shell 复制文件的话,用下面的命令就好了:

kubectl cp ... ...


人们总是说迈出第一步是最难的。其实不对——最难的是第二步。往往接触一个新事物的时候,人的好奇和兴奋大于紧张和恐惧。各种名人传记里说第一步难的,都是得瑟,真的。而当第二个机遇摆在面前的时候,却早已习惯了第一步驻足在的地方。习惯了那里的人和物,习惯了各种形色,再也迈不出那双肥腿。《Inception》 里说,当在一个梦里久了,你会忘记当年梦想,然后“困”在那个层次,很难再走出来,直到死亡。现实也是如此。一个人从学校里出来的时候,两手空空。只有怀揣一把子冲劲,从一个结束走向另外一个开始。如果没有想过计划下一个结束和开始,那么一定有一天会突然惊醒,然后“掐指一算,都已是风烛残年1”。

今天看新闻,说 Google 换 CEO 了。Eric 在给这个公司贡献了十年之后,回到了二把手。人们早已习惯了 Google 各种创意的鬼点子,结果前进变成了定格,匀速发展成为了静止。当习惯成为了主导,丢失的就是方向。那么一切改变都是必要的,无论好坏。





Life is Like a Boat


Nobody knows who I really am
I never felt this empty before
And if I ever need someone to come along,
Who’s gonna comfort me, and keep me strong?

We are all rowing the boat of fate
The waves keep on coming and we can’t escape
But if we ever get lost on our way
The waves would guide you through another day

Far away, I’m breathing, as if I were transparent
It would seem I was in the dark, but I was only blindfolded

I give a prayer as I wait for the new day
Shining vividly up to the edge of that sea

Nobody knows who I really am
Maybe they just don’t give a damn
But if I ever need someone to come along
I know you would follow me, and keep me strong

People’s hearts change and sneak away from them
The moon in its new cycle leads the boats again

And every time I see your face
The ocean heaves up to my heart
You make me wanna strain at the oars, and soon
I can see the shore

Oh, I can see the shore
When will I see the shore?

I want you to know who I really am
I never thought I’d feel this way towards you
And if you ever need someone to come along,
I will follow you, and keep you strong

And still the journey continues on quiet days as well
The moon in its new cycle shines on the boats again

I give a prayer as I wait for the new day
Shining vividly up to the edge of that sea

And every time I see your face
The ocean heaves up to my heart
You make me wanna strain at the oars, and soon
I can see the shore

We are rowing the boat of fate, but the waves keep attacking us
But isn’t that still a wonderful journey? Aren’t any of them a wonderful journey?

This is it…

24 号因为身体微恙,没有参加当天的全球 Thrill The World 的活动。排练了一个月,很可惜。所以今天把全部的致敬都放在了这部纪录片上。本来是 28 号首映,但是惊喜发现今晚九点就有第一场了。于是安静地呆在办公室里加班,准备到时间准时出发去影院。

多谢小 A 提前帮忙买了电影票,以防人多爆场没有位置。后来才发现这个担心是多余的。虽然观众人数跟其它好莱坞大片不能比,但是还是能看到一些粉丝。电影还没有开始,就四处发 MJ 的 Sticker 贴在身上。影院也贴心地制作了钥匙链当作首映的礼物。

本来一开始那些粉丝看上去还要准备映后唱歌跳舞一番以表达敬意,但是近两个小时的放映之后,看到的只有沉默。对于我这个 15 年的老麦粉来说,绝大部分排练都似曾相识。感觉看到的不是新鲜的抢夺眼球的舞台特效,反而有一种感觉,就是他曾经回来过。

一个人活了 50 岁,却一丝不苟地从事了一个职业 45 年。片中有一个演唱会合作的歌手评价说,没有任何一个瑕疵可以逃过 MJ 的眼睛和耳朵。任何一错误都要更正从新来过。一切都是为了 Fans。熟悉的舞蹈和旋律,让人感觉时间从来都没有前进过。然后突然地,他就走了。喜欢麦当娜在之前MTV ‘09 的颁奖开幕上说的:Michael Jackson 前无古人后无来者,他当之无愧的 King of Pop。