Wednesday, 31 December 2025

AWS ECR image scan reports vulnerabilities

Question:When the image was scanned one month ago, there is no vulnerability. The same image is scanned today, and reports vulnerabilities. Why?

Answser: AWS has updated its' vulnerability definitions.

Question:Sometimes, after image is rebuilt using the same docker file, these vulnerabilities are gone. Why?

Answer: When the image is rebult, it will grab the latest libraries and these vulnerabilities may have been fixed in these new libraies. In the case, use this flag.

 --no-cache

Here is an example. Scan reports vulnerabilities related to curl

Dockerfile

FROM alpine:3.19
RUN apk update && apk add curl && apk upgrade

To build

 docker image build --no-cache  -t leo_test:latest .

When do apk update && apk add, it will get the latest libraries. At that time, alpine may have fix the curl issue. You may check and find that curl version has been changed.

curl --version

Tuesday, 30 December 2025

X-Frame-Options header for iframe

<iframe
        src="https://google.com"
        width="600"
        height="300"
        title="Demo Iframe"
        frameborder="0"
> </iframe>

Google.com will not be displayed in iframe. It is said that Google is refused to be connected. This is because that Google send out X-Frame-Options header

There are two values for this header

X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN

To set in apache httpd.conf

Header always append X-Frame-Options SAMEORIGIN
Header always append X-Frame-Options DENY

For the first option, will display content of the same domain. For the second option, will not display content from any domain

Thursday, 4 December 2025

ECS service and target group

  • associate ECS service to load balancer
  • define target group for load balancer
  • running tasks of this ECS service become targets of this target group
#cloudformation template snippet
Service:
    Type: "AWS::ECS::Service"
    Properties:
      Cluster: !FindInMap [BuildEnvironment, !Ref 'EnvironmentType', "cluster"]
      DesiredCount: !FindInMap [BuildEnvironment, !Ref 'EnvironmentType', "desire"]
      LoadBalancers:
        - ContainerName: !Sub "${EnvironmentType}-${APP}"
          ContainerPort: 80
          TargetGroupArn: my_target_group_arn

Monday, 1 December 2025

Encrypt and decrypt password using PHP sodium

Encrypt password

<?php
/**
 * encrypt.php
 */
$masterKey = "canyouguess";
$passwordToEncrypt = "top-secret";

//ecncypt password
$code = encrypt($masterKey, $passwordToEncrypt);
echo "code: ", $code, "\n";

/**
 * @param $masterKey
 * @param $password
 * @return string
 * @throws SodiumException
 */
function encrypt($masterKey, $password)
{
    // Derive a subkey of the correct length for secretbox
    $key = sodium_crypto_generichash($masterKey, '', SODIUM_CRYPTO_SECRETBOX_KEYBYTES
    );

    // Generate a random nonce
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

    // Encrypt
    $ciphertext = sodium_crypto_secretbox($password, $nonce, $key);

    // Prepend nonce for later decryption, and base64-encode the lot
    return base64_encode($nonce . $ciphertext);
}

In the directory of encrypt.php, run:

docker run -it --rm -v "$PWD":/usr/src -w /usr/src php:8.2-cli php encrypt.php

Output:

code: qKUilnBOnSPIOmKnwIoJAdOaC+9lhud/0eiTekLPokmuImFiRpuHvKG6AoUs7eT7fBY=

Decrypt code to get password

If do not have master key, there is no way to decrypt the code

<?php
/**
 * decrypt.php
 */

$masterKey = "canyouguess";
$code = "qKUilnBOnSPIOmKnwIoJAdOaC+9lhud/0eiTekLPokmuImFiRpuHvKG6AoUs7eT7fBY=";

//decrypt password using master key
$password = decrypt($masterKey, $code);
echo "password: ", $password, "\n";

/**
 * @param $masterKey
 * @param $code
 * @return string
 * @throws SodiumException
 */
function decrypt($masterKey, $code)
{
    $raw = base64_decode($code, true);
    if ($raw === false) {
        throw new RuntimeException('Invalid base64');
    }

    // same derived subkey
    $key = sodium_crypto_generichash($masterKey, '', SODIUM_CRYPTO_SECRETBOX_KEYBYTES
    );

    $nonceLen = SODIUM_CRYPTO_SECRETBOX_NONCEBYTES;
    if (strlen($raw) < $nonceLen) {
        throw new RuntimeException('Ciphertext too short');
    }

    // extract nonce and ciphertext
    $nonce = mb_substr($raw, 0, $nonceLen, '8bit');
    $ciphertext = mb_substr($raw, $nonceLen, null, '8bit');

    // decrypt & verify
    $plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
    if ($plaintext === false) {
        throw new RuntimeException('Decryption failed or message forged');
    }

    return $plaintext;
}

In the same directory of decrypt.php, run:

docker run -it --rm -v "$PWD":/usr/src -w /usr/src php:8.2-cli php decrypt.php

Output:

password: top-secret

Monday, 17 November 2025

Import and export in AWS CloudFormation

 
- ContainerName: !Sub "${AWS::StackName}"
  ContainerPort: 80
  TargetGroupArn: { "Fn::ImportValue" : { "Fn::Join" : [ "-", [ { "Ref" : "EnvironmentType" }, "leo-elb-elb-target-group"]] }}

If EnvironmentType is sandbox, we are importing sandbox-leo-elb-elb-target-group as TargetGroupArn


# part of codes from stack sandbox-leo-elb
# export a resource called leo-elb-elb-target-group
# click Outputs tab in AWS console for this stack. Also see Export Name: leo-elb-elb-target-group
Outputs:
  ELBTargetGroupHTTP:
    Description: The target group
    Value: !Ref ELBTargetGroup
    Export:
      Name: !Sub "${AWS::StackName}-elb-target-group"

Wednesday, 12 November 2025

Git cherry pick

how to cherry pick codes from development to staging

  1. need to find commit hash using git log
  2. git checkout release/1-staging
  3. git checkout -b feature/SMP-7777
  4. git cherry-pick hash
  5. git push --set-upstream origin feature/SMP-7777
  6. create code review

If pick mutiple commits, start cherry pick from old one.

//example
git checkout master

git checkout -b feature/my-4247-hot-fix

git cherry-pick cd5861fb4c098855fe99b1eb74790874c6be4570 22dfcc0dd6064029da786637e3ceb3b5b5d00719

git push -u origin feature/my-4247-hot-fix

Monday, 18 August 2025

If deploy process stuck because of AWS stack update

If AWS stack update is stuck, and deploy process will not be completed.
  • cancel Jenkin release process
  • go to AWS cloudformation. go to Stack Action. click Cancel update stack from drop down.
  • wait for update_rollback_complete state

Friday, 8 August 2025

AWS Lambda

To access AWS Cloudwatch Logs for Lambda function

  1. Click Monitor tab
  2. Click view CloudWatch Logs link

Use environment variable in node.js

//here SHARED_MS_BUCKET is env variable name
Bucket: process.env.SHARED_MS_BUCKET

Sunday, 11 May 2025

Edit host file for Window 11 WSL (Windows Subsystem for Linux)

You need to edit the following file using notepad and run as admin

 C:\Windows\System32\drivers\etc\hosts

If you edit subsystem linux's /etc/hosts, it will not affect browser such as Google Chrome

Access two github accounts in one linux machine

Assume you already can access your person github repositories using public key id_rsa.pub

  1. Create another ssh private / public key pair
    # file name: ~/.ssh/id_rsa2
     ssh-keygen
  2. upload public key to your company's github
  3. create a ssh config at .ssh/config
    Host github.com
      HostName github.com
      User git
      IdentityFile ~/.ssh/id_rsa
      AddKeysToAgent yes
      ServerAliveInterval 60
      ServerAliveCountMax 30
    
    # Work GitHub account
    Host github-work
      HostName github.com
      User git
      IdentityFile ~/.ssh/id_rsa2
      AddKeysToAgent yes
      ServerAliveInterval 60
      ServerAliveCountMax 30
  4. clone work repository
    git clone git@github-work:mycompany/jackt.git
  5. clone your person repository as normal because it will use the default ssh config

Tuesday, 15 April 2025

Access Mysql Database hosted on AWS EC2

To access the database, need to set up two things.

Step 1: add rule to the security group attached to the instance

  1. Edit inbound rule of the security group
  2. add a rule. Type: MYSQL/Aurora Soure: custom such as 137.229.127.0/24

Step 2: update privilidge of Mysql database

  1. log into the database as root or admin
  2. #works for mysql 5.7.8 and up. To change a user's ip
      RENAME USER 'teau'@'51.27.292.63' TO 'teau'@'125.256.111.%';
      # for new user, need to grant permission
  3. FLUSH PRIVILEGES;

Trouble shooting

If do not do the step one, most likely will see a connection timeout

If do not do the second step, will see something like

[MySQL][ODBC 8.0(w) Driver]Host '*.*.***.**' is not allowed to connect to this MySQL server
Unable to connect to the MySQL server "***.***.**.**". Check that the server is running and that you have
access privileges to the requested database.

Some useful Mysql commands

select version();

select user, host from mysql.user;

SHOW GRANTS FOR 'leo'@'52.30.111.16';

Tuesday, 25 March 2025

Schedule post to an end point using Amazon EventBridge Rules

  1. set up a SNS topic
  2. Create a subscription for this SNS topic. Choose protocol https and enter end point such as
    https://paysomething.sandbox.mysite.io/v1/tasks/process
  3. Go to Amazon EventBridge to create a rule
  4. Make the above SNS topic as target of the rule, and type is Schedule

Friday, 14 March 2025

AWS WAF

Bock ips to elb

    • Click IP set tab
    • Click Create IP set button
    • Given name, description and IP addresses
    • Click Create IP set button to save the IP set
    • Click Web ACLs tab
    • Click Create Web ACL button
    • Follow steps to create web ACL. Add the above ip set rule and action is block
    • For Default web ACL action for requests that don't match any rules, choose allow
    • Click Web ACLs tab agin. The newly created ACL should show up in the list
    • Click that ACL
    • Click Associate AWS resources tab
    • Click Add AWS resource button
    • Select the ebl and assoicate it to this Web ACL
  1. Test it using your own ip
  2. Go back to that Web ACL home page to check traffic log

Tuesday, 4 March 2025

Add Custom Http Header

Using curl


curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'cookiename: blue',
    'X-Apple-Store-Front: 143444,12'
]);

Google Chrome

Use ModHeader extension

Postman

Click Headers tab to add custom header

Thursday, 30 January 2025

PHP Propel

fake option for migration

docker-compose run --rm -w /var/src/Propel api ./../vendor/bin/propel migrate --fake

it doesn't actually run the migrations, but it marks them as having been completed. It will add versions in propel_migration table.

Friday, 24 January 2025

Public key and private key

Mainly there are two use cases.

  1. encrypte message using public key. Then decrypt message using private key
  2. digitally sign message using private key. Then verify signature using public key

Here is example for use case 2. Use SSH public key and private key to do authentication for github after you upload your public key onto github server

  1. You initiate an SSH connection to the server
  2. The server chooses a random challenge (a nonce or session identifier) and sends it to you
  3. Your SSH client uses your private key to compute a digital signature over that challenge
  4. You send the signature back to the server
  5. The server uses your stored public key to verify the signature
  6. If verification succeeds, only someone with the private key could have produced it
  7. You’re granted access; if it fails, you’re rejected

Another example for use case 2 is bitcoin transaction

  1. Ower uses private key to sign the transaction
  2. The signature is verfied by others using public key (the btc address comes from public key)

AWS policy to restrict ips to AWS Gateway API

Here is a sample policy. Only ips in the list will allow to call that API

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "223.333.923.28/32",
                        "162.222.229.139/32"
                    ]
                }
            },
            "Action": [
                "execute-api:Invoke"
            ],
            "Resource": [
                "arn:aws:execute-api:us-west-1:112334444444:1wertttt/*/POST/v1/bill",
                "arn:aws:execute-api:us-west-1:112334444444:1wertttt/*/GET/vi/bill/*"
            ],
            "Effect": "Allow"
        }
    ]
}

Monday, 13 January 2025

docker container run breakdown

Here is a command to run php 8 in a host machine which only has php 7 installed

docker run --rm --volume `pwd`:/var/src/ -w /var/src php:8.0.26-cli-alpine3.16  php ./bin/swagger_gen.php ./rest/swagger/v1/

Breakdown

  • --rm Automatically remove the container and its associated anonymous volumes when it exits
  • --volume `pwd`:/var/src/ Bind mount a volume (docker run --volume host-path:container-path
  • pwd present working directory of your host machine
  • -w Working directory inside the container

Saturday, 11 January 2025

Indirect modification of overloaded property has no effect

<?php
error_reporting(E_ALL);

class PropertyTest
{
    /**  Location for overloaded data.  */
    private $data = array();


    public function __set($name, $value)
    {
        $this->data[$name] = $value;
    }

    public function __get($name)
    {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        } else {
            throw new Exception($name . " not found");
        }
    }
}

$obj = new PropertyTest();
$obj->goods = ["a", "b"];

//the line below will throw notice
//Indirect modification of overloaded property PropertyTest::$goods has no effect
$obj->goods[] = "c";

//still got ["a", "b"]
var_dump($obj->goods);

To fix the error, replace the last two lines of codes with the below.

$obj->goods= array_merge($obj->goods, ['c']);

//got ["a", "b", "c"]
var_dump($obj->goods);

Here is another example

$obj = new PropertyTest();
$obj->test=["a"=>2];
var_dump($obj->test);
//will throw the notice and will not change value
$obj->test['a'] =3;
//the result is the same as what we get from the last dump
var_dump($obj->test);

To make it work as expected

$obj = new PropertyTest();
$obj->test=["a"=>2];
var_dump($obj->test);

$ans = $obj->test;
$ans['a'] = 3;
$obj->test = $ans;
var_dump($obj->test)