快速创建TLS证书并部署到Docker服务

将下方代码保存为certbot.sh,修改头部变量后,上传到装有openssl组件的linux服务器上运行即可。

#!/bin/sh
#

export PASSWORD="密码"

export COUNTRY="CN"
export STATE="省"
export CITY="市"
export ORGANIZATION="公司名称"
export ORGANIZATIONAL_UNIT="Dev"
export COMMON_NAME="域名"
export EMAIL="电子邮件地址"

export HOST_NAME="COMMON_NAME"
export IP=`pingHOST_NAME -c 1 | sed '1{s/[^(]*(//;s/).*//;q}'`

export DIR="cert-HOST_NAME"

# Workspace

[ -dDIR ] || mkdir -p DIR

echo "PASSWORD:PASSWORD" > DIR/!nfo.txt
echo "HOST_NAME:HOST_NAME" >> DIR/!nfo.txt
echo "HOST_IP:IP" >> DIR/!nfo.txt

# Generate CA

openssl genrsa -aes256 -passout "pass:PASSWORD" -out "DIR/ca-key.pem" 4096

openssl req -new -x509 -days 3650 -key "DIR/ca-key.pem" -sha256 -out "DIR/ca.pem" -passin "pass:PASSWORD" -subj "/C=COUNTRY/ST=STATE/L=CITY/O=ORGANIZATION/OU=ORGANIZATIONAL_UNIT/CN=COMMON_NAME/emailAddress=EMAIL"

# Generate Server Certs

openssl genrsa -out "DIR/server-key.pem" 4096

openssl req -subj "/CN=HOST_NAME" -sha256 -new -key "DIR/server-key.pem" -out DIR/server.csr

echo "subjectAltName = DNS:HOST_NAME,IP:IP,IP:127.0.0.1">DIR/server.cnf
echo "extendedKeyUsage = serverAuth" >> DIR/server.cnf

openssl x509 -req -days 3650 -sha256 -inDIR/server.csr -passin "pass:PASSWORD" -CA "DIR/ca.pem" -CAkey "DIR/ca-key.pem" -CAcreateserial -out "DIR/server-cert.pem" -extfile DIR/server.cnf

# Generate Client Certs

openssl genrsa -out "DIR/client-key.pem" 4096

openssl req -subj '/CN=client' -new -key "DIR/client-key.pem" -outDIR/client.csr

echo "extendedKeyUsage = clientAuth" > DIR/client.cnf

openssl x509 -req -days 3650 -sha256 -inDIR/client.csr -passin "pass:PASSWORD" -CA "DIR/ca.pem" -CAkey "DIR/ca-key.pem" -CAcreateserial -out "DIR/client-cert.pem" -extfile DIR/client.cnf

# Modify Certs Permission

chmod 0400DIR/*-key.pem
chmod 0444 DIR/ca.pemDIR/*-cert.pem

# Remove Temporary Files

rm -f DIR/*.csrDIR/*.cnf

如果需要部署到Docker服务,请继续

# Install To Docker Daemon

mkdir -p /etc/docker/certs.d
cp DIR/ca.pem /etc/docker/certs.d/
cpDIR/server-*.pem /etc/docker/certs.d/

cat <<EOF >/etc/docker/daemon.json
{
    "tlsverify": true,
    "tlscacert": "/etc/docker/certs.d/ca.pem",
    "tlscert": "/etc/docker/certs.d/server-cert.pem",
    "tlskey": "/etc/docker/certs.d/server-key.pem",
    "hosts": ["tcp://0.0.0.0:2376", "unix:///var/run/docker.sock"]
}
EOF

# Modify Systemd Service

if [ -f /lib/systemd/system/docker.service ]; then
    sed -i 's#\["tcp:#\["fd://", "tcp:#' /etc/docker/daemon.json
    sed -i 's# -H fd://##' /lib/systemd/system/docker.service
    systemctl daemon-reload && systemctl restart docker
fi

特别注意

在systemd系统上-H已设置,因此无法使用hosts键来添加侦听地址,请参阅 https://docs.docker.com/install/linux/linux-postinstall/#configuring-remote-access-with-systemd-unit-file

PHP版百度云加速API/SDK封装

百度云加速API参考文档 https://su.baidu.com/help/index.html#/7_kaifazhinan/2_APIcankao-NEW/2_wangzhanjieru/2.1.1_tianjiayuming.md

<?php

/**
 * Author: anrip <https://www.arnip.com>
 * Update: 2021-04-13
 */

class Yunjiasu
{
    private su;

    privatename = '';
    private zone = [];

    public function __construct(domain, access_key,secret_key)
    {
        this->su = new YunjiasuCore(access_key, secret_key);this->zone = this->su->zones(['name' =>domain]);
        this->name =domain;
    }

    public function __call(name,arguments)
    {
        array_unshift(arguments,this->zone['id']);
        return call_user_func_array(array(this->su,name), arguments);
    }

    public function dns_records(data = [])
    {
        list =this->su->dns_records(this->zone['id']);
        if (empty(list) || empty(data)) {
            returnlist;
        }
        return array_filter(
            list,
            function (item) use (data) {
                isset(data['name']) && data['name'] .= '.' .this->name;
                return data === array_intersect_assoc(item, data);
            }
        );
    }

    public function dns_records_delete(data)
    {
        return array_map(
            function (rs) {
                returnthis->su->dns_records_delete(this->zone['id'],rs['id']);
            },
            this->dns_records(data)
        );
    }
}

class YunjiasuCore
{
    private api_base = 'https://api.su.baidu.com/';

    privateaccess_key = '';
    private secret_key = '';

    public function __construct(access_key, secret_key)
    {this->access_key = access_key;this->secret_key = secret_key;
    }

    ////////////////////////////////////////////////////////////////

    public function zones(data)
    {
        path = 'zones';
        returnthis->api_call('GET', path,data);
    }

    ////////////////////////////////////////////////////////////////

    public function dns_records(zone_id)
    {path = 'zones/' . zone_id . '/dns_records';
        returnthis->api_call('GET', path);
    }

    public function dns_records_insert(zone_id, data)
    {path = 'zones/' . zone_id . '/dns_records';
        returnthis->api_call('POST', path,data);
    }

    public function dns_records_update(zone_id,data)
    {
        path = 'zones/' .zone_id . '/dns_records';
        return this->api_call('PATCH',path, data);
    }

    public function dns_records_delete(zone_id, id)
    {path = 'zones/' . zone_id . '/dns_records/' .id;
        return this->api_call('DELETE',path);
    }

    ////////////////////////////////////////////////////////////////

    public function purge_cache(zone_id,data)
    {
        path = 'zones/' .zone_id . '/purge_cache';
        return this->api_call('DELETE',path, data);
    }

    ////////////////////////////////////////////////////////////////

    private function api_call(method, path,data = NULL)
    {
        path = 'v31/yjs/' .path;

        print_r("\n> " . method .\'/' .path);

        url =this->api_base . path;header = this->api_header(path, data);result = this->http_repuest(method, url,header, data);

        if (!empty(result['errors'])) {
            error = json_encode(result['errors']);
            throw new Exception(error);
        }

        if (!empty(result['result'])) {
            return result['result'];
        }

        if (!empty(result['success'])) {
            return ['success' => true];
        }

        return result;
    }

    private function api_header(path, data = NULL)
    {params = [
            'X-Auth-Access-Key' => this->access_key,
            'X-Auth-Nonce' => uniqid(),
            'X-Auth-Path-Info' =>path,
            'X-Auth-Signature-Method' => 'HMAC-SHA1',
            'X-Auth-Timestamp' => time(),
        ];

        if (is_array(data)) {params = array_merge(params,data);
        }

        ksort(params);header = signls = [];

        foreach (params as k =>v) {
            if (is_bool(v)) {v = v ? 'true' : 'false';
            }
            if (is_array(v)) {
                v = str_replace('","', '", "', json_encode(v, JSON_UNESCAPED_SLASHES));
            }
            if (strpos(k, 'X-Auth') === 0) {header[] = k . ':' .v;
            }
            if (v !== '') {signls[] = k . '=' .v;
            }
        }

        header[] = 'X-Auth-Sign:' . base64_encode(
            hash_hmac('sha1', implode('&',signls), this->secret_key, true)
        );

        returnheader;
    }

    ////////////////////////////////////////////////////////////////

    private function http_repuest(method,url, header = NULL,body = NULL)
    {
        ch = curl_init();

        if (method == 'GET' && body) {url .= '?' . http_build_query(body);body = NULL;
        }

        curl_setopt(ch, CURLOPT_URL,url);
        curl_setopt(ch, CURLOPT_CUSTOMREQUEST,method);
        curl_setopt(ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt(ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt(ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt(ch, CURLOPT_CONNECTTIMEOUT, 30);

        if (header) {
            curl_setopt(ch, CURLOPT_HTTPHEADER, header);
        }

        if (body) {
            if (is_array(body)) {body = json_encode(body);
            }
            curl_setopt(ch, CURLOPT_POSTFIELDS, body);
        }result = curl_exec(ch);errno = curl_errno(ch);error = curl_error(ch);

        curl_close(ch);

        if (errno) {
            return ['error' =>errno, 'message' => error];
        }

        return json_decode(result, true);
    }
}

Linux纯Shell实现DNSPod动态域名

开发背景:

公司有台嵌入式拨号上网设备,内置busybox和完整wget命令(支持https协议),但没有curl、python、ruby、php等扩展工具可用。网上现有基于DNSPod实现的Linux脚本无法在该设备上运行,于是写了个Shell版动态域名客户端(ddnspod),现在发布出来希望能给需要的朋友带来一些方便。

项目源码:

https://github.com/anrip/dnspod-shell

DnsPod官方文档

https://support.dnspod.cn/Support/api

功能介绍:

  • 优雅的函数封装,灵活的更新策略
  • 基于DNSPod最新用户API实现动态域名客户端
  • 支持基于系统计划任务实现更新,防止脚本意外终止
  • 可运行于多数类unix系统中,包括部分嵌入式小型系统
  • 依赖sed/wget/nslookup命令,wget也可使用curl替代

常见问题:

record line invailid 将脚本保存为utf-8格式即可
Record id invalid 检查账号权限、是否冲突等