QEMU虚拟化安装MacOS系统

需要的文件:

  1. 已转换过格式的 BaseSystem.img
  2. ESP.qcow2
  3. qemu-system-x86_64 >= 4.2.0
  4. UEFI 固件文件 OVMF_CODE.fd 和 OVMF_VARS-1024×768.fd

创建系统硬盘:

qemu-img create -f qcow2 system.qcow2 128G

创建虚拟机安装 MacOS 系统

qemu-system-x86_64 \
-enable-kvm \
-m 4G \
-machine q35,accel=kvm \
-smp 2 \
-cpu Penryn,vendor=GenuineIntel,kvm=on,+sse3,+sse4.2,+aes,+xsave,+avx,+xsaveopt,+xsavec,+xgetbv1,+avx2,+bmi2,+smep,+bmi1,+fma,+movbe,+invtsc \
-device isa-applesmc,osk="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc" \
-smbios type=2 \
-drive if=pflash,format=raw,readonly,file=OVMF_CODE.fd \
-drive if=pflash,format=raw,file=OVMF_VARS-1024x768.fd \
-vga virtio \
-usb -device usb-kbd -device usb-tablet \
-netdev user,id=net0 \
-device e1000-82545em,netdev=net0,id=net0,mac=52:54:00:0e:0d:25 \
-device ich9-ahci,id=sata \
-drive id=ESP,if=none,format=qcow2,file=ESP.qcow2 \
-device ide-hd,bus=sata.2,drive=ESP \
-drive id=InstallMedia,format=raw,if=none,file=BaseSystem.img \
-device ide-hd,bus=sata.3,drive=InstallMedia \
-drive id=SystemDisk,if=none,file=system.qcow2 \
-device ide-hd,bus=sata.4,drive=SystemDisk \
-vnc 0.0.0.0:0,to=99,id=default

qemu命令转换为 xml 文件

virsh domxml-from-native qemu-argv macos.args

设置静态IPV6地址

现在联通的家用宽带网络基本上都已经支持 IPV6 了,在光猫下的所有设备都可以获取一个 IPV6 的地址,如果想设置每台设备都有自定义和静态的IPV6地址,需要满足以下条件:

  1. 设备连接光猫或者路由器,可以自动获取到一个IPV6地址
  2. 在光猫或者路由器设置页面可以查询到 IPV6 地址前缀

一般在光猫的设置页面可以查询到地址前缀(联通光猫后台,状态,网络侧信息,IPv6 WAN连接信息, 获取前缀 ),比如

# IPV6 Prefix
e145:910a:2222:5400::/60

然后在路由器的设置页面可以获取到 WAN 口的 IPV6 地址和前缀,比如:

# WAN IPV6
e145:910a:2222:5400:8475:1111:3900:2020/64

# IPV6 Prefix
e145:910a:2222:5401::/64

路由器 WAN 口的 IPV6 地址来自于光猫的 DHCPV6 自动分配

我们的网络设备连接到路由器的 LAN 口上,可以获取到路由器的 DHCPV6 分配的 IPV6 地址,比如:

e145:910a:2222:5401:2583:8567:3467:3467/64

设备分配到的 IPV6 地址的前四位是 IPV6 前缀地址,后面四位是根据设备的 MAC 信息生成(或者随机生成)的主机地址,所以我们可以修改后面四位地址来实现静态 IPV6 地址。

CentOS 7 修改方法:

nmcli con mod ens3 ipv6.addresses "e145:910a:2222:5401:0000:0000:0000:0001/128" gw6 "e145:910a:2222:5401::"
nmcli con mod ens3 ipv6.method manual

e145:910a:2222:5401:0000:0000:0000:0001/128
也可以简写为
e145:910a:2222:5401::1/128

gw6 后面跟的是 IPV6 网关,需要填写的是路由器获取到的 IPV6 前缀

然后重启系统,查看新的 IP 地址

reboot

ip a

文章出处:蘑菇博客 www.mogublog.net 转载需联系作者授权

KVM虚拟化安装OpenWRT

OpenWRT 有 X86_64 的版本,可以安装在 KVM 虚拟化平台上

OpenWRT 系统下载地址:
https://downloads.openwrt.org/releases/19.07.0/targets/x86/64/openwrt-19.07.0-x86-64-combined-ext4.img.gz

解压后导入到 KVM

gunzip openwrt-19.07.0-x86-64-combined-ext4.img.gz

virt-install --name=openwrt --vcpus=1 --ram=512 --os-type=linux --disk path=/kvm/disk/openwrt.img,bus=ide --autostart --network bridge=br0,model=e1000 --network bridge=br0,model=e1000 --import --noautoconsole --graphics vnc,listen=0.0.0.0,password=000000

安装完成后用 VNC 登录设置 root 用户密码和查看 IP 地址,如果是桥接网络,可以设置 lan 口自动获取IP

# /etc/config/network

config interface 'lan'
    option ifname 'eth0'
    option proto 'dhcp'

然后重启系统

生成TOTP临时密码

生成二次验证的临时密码,兼容谷歌验证器

function base32Decode($in)
{
    $l = strlen($in);
    $n = $bs = 0;
    for ($i = 0; $i < $l; $i++) {
        $n <<= 5;
        $n += stripos('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', $in[$i]);
        $bs = ($bs + 5) % 8;
        $out .= $bs < 5 ? chr(($n & (255 << $bs)) >> $bs) : null;
    }
    return $out;
}

function getOTP($secret)
{
    $seed = base32Decode($secret);
    $time = str_pad(pack('N', intval(0 + time() / 30)), 8, "\x00", STR_PAD_LEFT);
    $hash = hash_hmac('sha1', $time, $seed, false);
    $otp = (hexdec(substr($hash, hexdec($hash[39]) * 2, 8)) & 0x7fffffff) % pow(10, 6);
    return sprintf("%'06u", $otp);
}

echo getOTP('secret_key');

列出腾讯云存储的文件

使用腾讯云 COS API 查看存储里的所有文件(最多1000个)

define('SecretId', 'SecretId');
define('SecretKey', 'SecretKey');

function listBucketFiles($region, $bucket)
{

    $host = $bucket . '.cos.' . $region . '.myqcloud.com';
    $path = '/';
    $url = 'https://' . $host . $path;
    $header = array(
        'Authorization: ' . requestSign($host, 'GET', $path),
        'Date: ' . gmdate('D, d M Y H:i:s T'),
    );

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    $response = curl_exec($curl);
    curl_close($curl);

    $files = [];
    $list = simplexml_load_string($response);
    foreach ($list->Contents as $v) {
        $files[] = (String) $v->Key;
    }

    return $files;
}

function requestSign($host, $method, $path)
{
    $signTime = (string) (time() - 60) . ';' . (string) (time() + 1200);
    $httpString = sprintf("%s\n%s\n\nhost=%s\n", strtolower($method), $path, $host);
    $stringToSign = sprintf("sha1\n%s\n%s\n", $signTime, sha1($httpString));
    $signKey = hash_hmac('sha1', $signTime, SecretKey);
    $signature = hash_hmac('sha1', $stringToSign, $signKey);
    return sprintf('q-sign-algorithm=sha1&q-ak=%s&q-sign-time=%s&q-key-time=%s&q-header-list=host&q-url-param-list=&q-signature=%s', SecretId, $signTime, $signTime, $signature);
}

创建私有CA证书签署自定义域名证书

一 创建 CA 证书

#!/bin/bash

mkdir CA
cd CA
openssl genrsa -out ca.key 4096
// openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=CN/ST=Beijing/L=Beijing/O=mogu/OU=CA/CN=mogu/emailAddress=admin@mogu.com"
echo "1" > serial

# Useage: sh createCA.sh

二 签名域名

#!/bin/bash

mkdir ${1}
cd ${1}
echo "[ req ]
default_bits       = 4096
distinguished_name = req_distinguished_name
req_extensions     = req_ext

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = Beijing
localityName                = Locality Name (eg, city)
localityName_default        = Beijing
organizationName            = Organization Name (eg, company)
organizationName_default    = www
organizationalUnitName            = Organizational Unit Name (eg, section)
organizationalUnitName_default    = IT
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = ${1}

[ req_ext ]
subjectAltName = @alt_names

[alt_names]
DNS.1   = ${1}
" > san.conf

openssl genrsa -out ${1}.key 4096

openssl req -new -key ${1}.key -out ${1}.csr -config san.conf -sha256

# serial 唯一
serial=$(cat ../CA/serial)
openssl x509 -req -days 3650 -in ${1}.csr -CA ../CA/ca.crt -CAkey ../CA/ca.key -set_serial $((serial+1)) -out ${1}.crt -extfile san.conf -extensions req_ext
echo $((serial+1)) > ../CA/serial

# Useage: sh signDoamin.sh localhost

使用阿里云API管理域名解析


define('AccessKeyId', 'AccessKeyId');
define('AccessKeySecret', 'AccessKeySecret');

date_default_timezone_set('UTC');

/**
 *  获取请求签名
 */
function getSign($add)
{
    $nonce = time() . rand(11111, 99999);
    $time = date('Y-m-d') . 'T' . date('H:i:s') . 'Z';
    $data = array(
        'AccessKeyId' => AccessKeyId,
        'Format' => 'json',
        'SignatureMethod' => 'HMAC-SHA1',
        'SignatureNonce' => $nonce,
        'SignatureVersion' => '1.0',
        'Timestamp' => $time,
        'Version' => '2015-01-09',
    );
    $data = array_merge($data, $add);
    ksort($data);
    $format = http_build_query($data);
    $signUrl = 'GET&%2F&' . urlencode($format);
    $sign = urlencode(base64_encode(hash_hmac('sha1', $signUrl, AccessKeySecret . '&', true)));
    return array('url' => $format, 'sign' => $sign);
}

/**
 *  获取域名列表
 */
function getDomains()
{
    $sign = getSign(['Action' => 'DescribeDomains']);
    $url = 'https://alidns.aliyuncs.com/?' . $sign['url'] . '&Signature=' . $sign['sign'];
    $data = json_decode(httpRequest($url));
    if (isset($data->Domains)) {
        return $data;
    } else {
        return false;
    }
}

/**
 *  获取域名解析记录
 */
function getDomainRecords($domain)
{
    $sign = getSign(['Action' => 'DescribeDomainRecords', 'DomainName' => $domain]);
    $url = 'https://alidns.aliyuncs.com/?' . $sign['url'] . '&Signature=' . $sign['sign'];
    $data = json_decode(httpRequest($url));
    if (isset($data->DomainRecords)) {
        return $data;
    } else {
        return false;
    }
}

/**
 *  获取子域名解析记录
 */
function getSubDomainRecords($domain)
{
    $sign = getSign(['Action' => 'DescribeSubDomainRecords', 'SubDomain' => $domain]);
    $url = 'https://alidns.aliyuncs.com/?' . $sign['url'] . '&Signature=' . $sign['sign'];
    $dns = json_decode(httpRequest($url));
    if (isset($dns->DomainRecords)) {
        return $dns;
    } else {
        return false;
    }
}

/**
 *  更新域名解析
 */
function updateSubDomainRecords($id, $name, $ip)
{
    $data = array(
        'Action' => 'UpdateDomainRecord',
        'RecordId' => $id,
        'RR' => $name,
        'Type' => 'A',
        'Value' => $ip,
    );
    $sign = getSign($data);
    $url = 'https://alidns.aliyuncs.com/?' . $sign['url'] . '&Signature=' . $sign['sign'];
    return httpRequest($url);
}

/**
 *  发起 HTTP 请求
 */
function httpRequest($url)
{
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    $data = curl_exec($curl);
    $error = curl_error($curl);
    curl_close($curl);
    if ($error) {
        return $error;
    } else {
        return $data;
    }
}

使用Google API获取谷歌相册的内容

首先要申请一个谷歌 Oauth2 应用的 ID 和密钥,然后用浏览器访问下面的地址获取授权码 code

https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=http://localhost&response_type=code&client_id={ClientID}&scope=https://www.googleapis.com/auth/photoslibrary+https://www.googleapis.com/auth/drive&access_type=offline

获取到 code 以后,请求谷歌服务器换取 refresh_token

define('Client_ID', 'client_id');
define('Client_Secrec', 'client_secrec');

function getRefreshToken($code) {
    $params = ['grant_type' => 'authorization_code', 'code' => $code, 'client_id' => Client_ID, 'client_secret' => Client_Secrec, 'redirect_uri' => 'http://localhost'];
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'https://www.googleapis.com/oauth2/v4/token');
    curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));
    $data = curl_exec($curl);
    curl_close($curl);
    return $data;
}

用 refresh_token 获取 access_token

function getAccessToken($refresh_token) {
    $params = ['grant_type' => 'refresh_token', 'client_id' => 'client_id', 'client_secret' => 'client_secre', 'refresh_token' => $refresh_token];
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'https://www.googleapis.com/oauth2/v4/token');
    curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));
    $data = curl_exec($curl);
    curl_close($curl);
    return $data;
}

获取Google Oauth2 JWT签名

首先获取服务账号密钥文件 google-gce.json

use Google\Auth\OAuth2;

require 'vendor/autoload.php';

function getGoogleJWT($scopes, $jsonKey) {
    $jsonKey = json_decode(file_get_contents($jsonKey), true);
    $config = [
        'audience' => 'https://oauth2.googleapis.com/token',
        'issuer' => $jsonKey['client_email'],
        'scope' => $scopes,
        'signingAlgorithm' => 'RS256',
        'signingKey' => $jsonKey['private_key'],
        'sub' => NULL,
        'tokenCredentialUri' => 'https://oauth2.googleapis.com/token'
    ];
    $auth = new OAuth2($config);
    return $auth->toJWT();
}

$scopes = ['https://www.googleapis.com/auth/drive.readonly'];

echo getGoogleJWT($scopes, 'google-gce.json');

然后用 JWT 签名就可以获取 Access Token 密钥,有效期为一小时

function getAccessToken($jwt) {
    $params = ['grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt];
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'https://oauth2.googleapis.com/token');
    curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));
    $data = curl_exec($curl);
    curl_close($curl);
    return $data;
}

$jwt = 'JWT';

echo getAccessToken($jwt);