无垠之码

深度剖析代码之道


docker命令小技巧

1.runlike工具


工作中总是难免遇到没有使用compose管理的容器服务,复杂的容器服务导出命令参数可能需要使用inspect子命令,分析json文件。runlike工具简化相关过程,直接使用runlike+容器名可以导出容器构建命令

pip install runlike
runlike posteio
docker run --name=posteio --hostname=mail.diyao.me --volume /data --env=TZ=Asia/Shanghai --env=DISABLE_CLAMAV=TRUE --network=host --restart=always --runtime=runc --detach=true analogic/poste.io

2.下一代构建引擎buildkit


buildkit是docker新一代镜像构建引擎,用来替代旧版docker build所使用的legacy builder,作为构建引擎的后端其前端使用docker的buildx插件进行调用与配置,实现现代化、多平台、安全可复现的容器镜像构建流程,同时buildx也支持分布式构建

sudo apt-get install docker-buildx-plugin

多平台方面,buildx支持构建以下类型镜像:

linux/arm64
linux/amd64
linux/amd64/v2
linux/riscv64
linux/ppc64le
linux/s390x
linux/386
linux/mips64le
linux/mips64
linux/arm/v7
linux/arm/v6

简单案例

使用buildx构建docker镜像,构建工作都在moby/buildkit镜像实例中进行,在容器中实际执行构建任务,包括拉取基础镜像、执行指令(如 RUN、COPY)、构建缓存、输出镜像等

# 创建buildx构建实例
# buildx-create命令的bootstrap参数,立即运行buildx实例,否则在构建时惰性运行实例
# buildx-create命令的driver参数,可以生产不同引擎的buildx实例
# 在后续的构建流程中,使用test实例

docker buildx create --name test --bootstrap   
docker buildx use test                         
docker buildx build --sbom=true --provenance=true --output=type=local,dest=build --platform=linux/amd64  -f Dockerfile .

# 多节点分布式构建
docker buildx create --name test --node test0 --driver docker-container ssh://root@192.168.5.46
docker buildx create --name test --node test1 --append --driver docker-container ssh://root@192.168.5.47

buildx支持多种构建引擎,下表列出引擎的相应使用场景

Read more...

开源威胁情报系统

1.opencti


OpenCTI is an open source platform allowing organizations to manage their cyber threat intelligence knowledge and observables. It has been created in order to structure, store, organize and visualize technical and non-technical information about cyber threats.

OpenCTI(Open Cyber Threat Intelligence Platform)是一个开源的网络威胁情报平台,旨在帮助组织集中化地收集、存储、组织、共享和可视化网络威胁情报数据。它基于图数据库模型,支持STIX 2(结构化威胁信息表达格式),并与多个情报源无缝集成。

1.1安装

opencti的安装方式分为手动安装和docker安装,其安装方法在官方网站已介绍十分全面,本文只记录相关踩坑部分。

mkdir -p /opt/opencti
cd /opt/opencti && git clone https://github.com/OpenCTI-Platform/docker.git platform
cd /opt/opencti && git clone https://github.com/OpenCTI-Platform/connectors.git connectors

注意事项:

  1. 使用docker安装需下载相关镜像,配置科学上网连接docker仓库
  2. 使用docker安装方式需要将rabbimtmq的端口映射至宿主机,同时需要在connecters中增加extra_hosts参数,rabbitmq:host-gateway
  3. 安装uuid,针对每一个connector使用uuid生成唯一的id

1.2概念

  1. Observable(可观察对象): 网络或系统中可以直接观测到的技术性数据元素

    例子:

    Read more...

加密shell脚本

neurobin-shc作为一款通用的SHELL脚本编译器,将换用户输入的SHELL脚本转换为C语言代码,编译链接形成一份剥离符号的可执行程序。shc并不是类似与cc的编译器工具,它只是简单的编码和加密SHELL脚本,生成C语言代码,同时增加比如脚本过期检测,等其他特性。其使用系统的编译器和相关工具生成剥离符号的可执行程序,程序与脚本执行效果相同。

需要注意的是其编译的二进制文件取决于SHELL解释器,shc产生的并非完全独立与系统的二进制程序,其处理后的可执行程序只能在编译主机相同的环境中运行

0.使用


shc支持很多选项,开启特定功能,比如用户可以使用-e指定脚本的有效执行时间,-m定制过期后的技术支持联系信息,-D选择在执行时显示调试信息,-v展示编译过程的参数信息,-S将root执行的脚本设置为setuid,-U使得程序防止strace,ptrace追踪,-r允许生成的二进制文件,能够在相同版本的操作系统运行

下面假设用户需要加密的脚本文件test.sh,使用shc -rf test.sh加密脚本,默认生成可执行文件test.sh.x和C语言文件test.sh.x.c

cat test.sh
#!/bin/bash
echo "hello,world

test.sh  test.sh.x  test.sh.x.c
test.sh.x: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=044499892c9c33d7c91f45feb1413e41dd1e2a27, for GNU/Linux 3.2.0, stripped

test.sh.x.c文件内容如下所示

#if 0
        shc Version 4.0.3, Generic Shell Script Compiler
        GNU GPL Version 3 Md Jahidul Hamid <jahidulhamid@yahoo.com>

        shc -rf test.sh 
#endif

static  char data [] = 
#define      tst1_z     22
#define      tst1       ((&data[1]))
        "\030\115\076\307\352\251\122\334\257\345\123\205\106\331\310\126"
        "\306\365\222\136\257\375\074\131\151\322\311\234"
#define      tst2_z     19
#define      tst2       ((&data[29]))
        "\373\001\053\034\243\243\327\252\343\047\144\311\002\062\041\060"
        "\005\045\255\050\026\056"
#define      shll_z     10
#define      shll       ((&data[50]))
        "\026\324\303\131\264\245\233\155\211\041\365"
#define      chk2_z     19
#define      chk2       ((&data[63]))
        "\343\134\027\267\010\164\350\146\006\173\011\157\012\014\065\350"
        "\070\374\246\344\244\152"
#define      lsto_z     1
#define      lsto       ((&data[83]))
        "\241"
#define      chk1_z     22
#define      chk1       ((&data[84]))
        "\263\160\137\140\034\005\137\221\361\364\205\373\062\214\141\366"
        "\004\177\354\243\302\204\026"
#define      date_z     1
#define      date       ((&data[107]))
        "\212"
#define      msg2_z     19
#define      msg2       ((&data[111]))
        "\124\074\340\040\252\304\020\035\177\177\014\140\114\351\344\073"
        "\344\317\367\045\356\177"
#define      rlax_z     1
#define      rlax       ((&data[130]))
        "\255"
#define      opts_z     1
#define      opts       ((&data[131]))
        "\234"
#define      pswd_z     256
#define      pswd       ((&data[152]))
        "\357\272\107\322\026\261\100\334\314\320\337\023\346\020\111\025"
        "\233\351\027\357\262\242\314\056\113\200\220\263\311\116\231\164"
        "\001\342\261\062\254\213\257\162\322\374\160\203\243\005\063\236"
        "\235\220\174\120\063\111\176\177\311\017\062\223\135\314\010\137"
        "\256\271\221\132\105\101\315\030\076\076\233\341\103\317\177\341"
        "\137\374\061\223\105\257\023\017\276\106\243\034\022\253\173\301"
        "\145\015\034\252\117\352\302\215\050\136\157\153\055\356\114\215"
        "\353\175\040\061\055\064\100\354\172\344\010\215\217\204\116\364"
        "\222\153\237\341\125\141\157\175\277\336\351\355\315\066\172\270"
        "\264\233\351\341\317\052\316\112\016\327\327\236\133\046\223\355"
        "\221\062\317\347\224\076\144\124\034\116\101\352\204\273\242\071"
        "\126\214\032\046\267\351\160\306\300\110\144\034\157\367\011\001"
        "\052\330\350\276\027\115\023\063\233\124\036\040\020\300\131\147"
        "\115\164\215\004\135\376\312\036\107\057\072\266\047\103\267\121"
        "\034\240\020\064\355\044\150\210\170\206\250\210\106\001\357\224"
        "\165\175\230\323\174\143\361\303\222\053\172\272\157\062\013\214"
        "\322\034\300\277\100\050\110\271\256\361\102\365\363\062\211\151"
        "\257\042\074\054\046\370\232"
#define      text_z     34
#define      text       ((&data[418]))
        "\252\356\135\373\375\114\265\121\361\372\222\156\226\243\055\231"
        "\035\244\362\272\374\214\031\277\114\012\305\343\323\073\203\042"
        "\047\167\354\114\031\202\220\354\201\105\037\314"
#define      msg1_z     65
#define      msg1       ((&data[461]))
        "\303\057\210\326\026\230\210\113\110\226\277\361\215\017\204\015"
        "\040\353\156\317\067\270\136\021\057\307\176\277\035\021\052\261"
        "\320\264\037\020\256\021\054\242\230\360\304\367\224\310\357\365"
        "\010\152\041\053\367\163\225\221\162\211\202\264\264\313\370\056"
        "\022\052\234\065\246\133\025\037\054\063\011\104\043\057\074\276"
        "\000\022"
#define      inlo_z     3
#define      inlo       ((&data[537]))
        "\002\114\021"
#define      xecc_z     15
#define      xecc       ((&data[542]))
        "\304\135\155\330\141\311\253\016\333\076\154\063\324\263\057\111"
        "\254\103\344\051"/* End of data[] */;
#define      hide_z     4096
#define SETUID 0        /* Define as 1 to call setuid(0) at start of script */
#define DEBUGEXEC       0       /* Define as 1 to debug execvp calls */
#define TRACEABLE       1       /* Define as 1 to enable ptrace the executable */
#define HARDENING       0       /* Define as 1 to disable ptrace/dump the executable */
#define BUSYBOXON       0       /* Define as 1 to enable work with busybox */
#define MMAP2           0       /* Define as 1 to use syscall mmap2 */

#if HARDENING
static const char * shc_x[] = {
"/*",
" * Copyright 2019 - Intika <intika@librefox.org>",
" * Replace ******** with secret read from fd 21",
" * Also change arguments location of sub commands (sh script commands)",
" * gcc -Wall -fpic -shared -o shc_secret.so shc_secret.c -ldl",
" */",
"",
"#define _GNU_SOURCE /* needed to get RTLD_NEXT defined in dlfcn.h */",
"#define PLACEHOLDER \"********\"",
"#include <dlfcn.h>",
"#include <stdlib.h>",
"#include <string.h>",
"#include <unistd.h>",
"#include <stdio.h>",
"#include <signal.h>",
"",
"static char secret[128000]; //max size",
"typedef int (*pfi)(int, char **, char **);",
"static pfi real_main;",
"",
"// copy argv to new location",
"char **copyargs(int argc, char** argv){",
"    char **newargv = malloc((argc+1)*sizeof(*argv));",
"    char *from,*to;",
"    int i,len;",
"",
"    for(i = 0; i<argc; i++){",
"        from = argv[i];",
"        len = strlen(from)+1;",
"        to = malloc(len);",
"        memcpy(to,from,len);",
"        // zap old argv space",
"        memset(from,'\\0',len);",
"        newargv[i] = to;",
"        argv[i] = 0;",
"    }",
"    newargv[argc] = 0;",
"    return newargv;",
"}",
"",
"static int mymain(int argc, char** argv, char** env) {",
"    //fprintf(stderr,        if (["localhost", "127.0.0.1"].indexOf(window.location.hostname) != -1) {
            document.getElementById('disqus_thread').innerHTML = 'Disqus comments not available by default when the website is previewed locally.';
            return;
        } \"Inject main argc = %d\\n\", argc);",
"    return real_main(argc, copyargs(argc,argv), env);",
"}",
"",
"int __libc_start_main(int (*main) (int, char**, char**),",
"                      int argc,",
"                      char **argv,",
"                      void (*init) (void),",
"                      void (*fini)(void),",
"                      void (*rtld_fini)(void),",
"                      void (*stack_end)){",
"    static int (*real___libc_start_main)() = NULL;",
"    int n;",
"",
"    if (!real___libc_start_main) {",
"        real___libc_start_main = dlsym(RTLD_NEXT, \"__libc_start_main\");",
"        if (!real___libc_start_main) abort();",
"    }",
"",
"    n = read(21, secret, sizeof(secret));",
"    if (n > 0) {",
"      int i;",
"",
"    if (secret[n - 1] == '\\n') secret[--n] = '\\0';",
"    for (i = 1; i < argc; i++)",
"        if (strcmp(argv[i], PLACEHOLDER) == 0)",
"          argv[i] = secret;",
"    }",
"",
"    real_main = main;",
"",
"    return real___libc_start_main(mymain, argc, argv, init, fini, rtld_fini, stack_end);",
"}",
"",
0};
#endif /* HARDENING */

/* rtc.c */

#include <sys/stat.h>
#include <sys/types.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

/* 'Alleged RC4' */

static unsigned char stte[256], indx, jndx, kndx;

/*
 * Reset arc4 stte. 
 */
void stte_0(void)
{
        indx = jndx = kndx = 0;
        do {
                stte[indx] = indx;
        } while (++indx);
}

/*
 * Set key. Can be used more than once. 
 */
void key(void * str, int len)
{
        unsigned char tmp, * ptr = (unsigned char *)str;
        while (len > 0) {
                do {
                        tmp = stte[indx];
                        kndx += tmp;
                        kndx += ptr[(int)indx % len];
                        stte[indx] = stte[kndx];
                        stte[kndx] = tmp;
                } while (++indx);
                ptr += 256;
                len -= 256;
        }
}

/*
 * Crypt data. 
 */
void arc4(void * str, int len)
{
        unsigned char tmp, * ptr = (unsigned char *)str;
        while (len > 0) {
                indx++;
                tmp = stte[indx];
                jndx += tmp;
                stte[indx] = stte[jndx];
                stte[jndx] = tmp;
                tmp += stte[indx];
                *ptr ^= stte[tmp];
                ptr++;
                len--;
        }
}

/* End of ARC4 */

#if HARDENING

#include <sys/ptrace.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/prctl.h>
#define PR_SET_PTRACER 0x59616d61

/* Seccomp Sandboxing Init */
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/socket.h>

#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/audit.h>

#define ArchField offsetof(struct seccomp_data, arch)

#define Allow(syscall) \
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_##syscall, 0, 1), \
    BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)

struct sock_filter filter[] = {
    /* validate arch */
    BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ArchField),
    BPF_JUMP( BPF_JMP+BPF_JEQ+BPF_K, AUDIT_ARCH_X86_64, 1, 0),
    BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),

    /* load syscall */
    BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),

    /* list of allowed syscalls */
    Allow(exit_group),  /* exits a process */
    Allow(brk),         /* for malloc(), inside libc */
#if MMAP2
    Allow(mmap2),       /* also for malloc() */
#else
    Allow(mmap),        /* also for malloc() */
#endif
    Allow(munmap),      /* for free(), inside libc */

    /* and if we don't match above, die */
    BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
};
struct sock_fprog filterprog = {
    .len = sizeof(filter)/sizeof(filter[0]),
    .filter = filter
};

/* Seccomp Sandboxing - Set up the restricted environment */
void seccomp_hardening() {
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
        perror("Could not start seccomp:");
        exit(1);
    }
    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &filterprog) == -1) {
        perror("Could not start seccomp:");
        exit(1);
    }
} 
/* End Seccomp Sandboxing Init */

void shc_x_file() {
    FILE *fp;
    int line = 0;

    if ((fp = fopen("/tmp/shc_x.c", "w")) == NULL ) {exit(1); exit(1);}
    for (line = 0; shc_x[line]; line++) fprintf(fp, "%s\n", shc_x[line]);
    fflush(fp);fclose(fp);
}

int make() {
        char * cc, * cflags, * ldflags;
        char cmd[4096];

        cc = getenv("CC");
        if (!cc) cc = "cc";

        sprintf(cmd, "%s %s -o %s %s", cc, "-Wall -fpic -shared", "/tmp/shc_x.so", "/tmp/shc_x.c -ldl");
        if (system(cmd)) {remove("/tmp/shc_x.c"); return -1;}
        remove("/tmp/shc_x.c"); return 0;
}

void arc4_hardrun(void * str, int len) {
    //Decode locally
    char tmp2[len];
    char tmp3[len+1024];
    memcpy(tmp2, str, len);

    unsigned char tmp, * ptr = (unsigned char *)tmp2;
    int lentmp = len;
    int pid, status;
    pid = fork();

    shc_x_file();
    if (make()) {exit(1);}

    setenv("LD_PRELOAD","/tmp/shc_x.so",1);

    if(pid==0) {

        //Start tracing to protect from dump & trace
        if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
            kill(getpid(), SIGKILL);
            _exit(1);
        }

        //Decode Bash
        while (len > 0) {
            indx++;
            tmp = stte[indx];
            jndx += tmp;
            stte[indx] = stte[jndx];
            stte[jndx] = tmp;
            tmp += stte[indx];
            *ptr ^= stte[tmp];
            ptr++;
            len--;
        }

        //Do the magic
        sprintf(tmp3, "%s %s", "'********' 21<<<", tmp2);

        //Exec bash script //fork execl with 'sh -c'
        system(tmp2);

        //Empty script variable
        memcpy(tmp2, str, lentmp);

        //Clean temp
        remove("/tmp/shc_x.so");

        //Sinal to detach ptrace
        ptrace(PTRACE_DETACH, 0, 0, 0);
        exit(0);
    }
    else {wait(&status);}

    /* Seccomp Sandboxing - Start */
    seccomp_hardening();

    exit(0);
}
#endif /* HARDENING */

/*
 * Key with file invariants. 
 */
int key_with_file(char * file)
{
        struct stat statf[1];
        struct stat control[1];

        if (stat(file, statf) < 0)
                return -1;

        /* Turn on stable fields */
        memset(control, 0, sizeof(control));
        control->st_ino = statf->st_ino;
        control->st_dev = statf->st_dev;
        control->st_rdev = statf->st_rdev;
        control->st_uid = statf->st_uid;
        control->st_gid = statf->st_gid;
        control->st_size = statf->st_size;
        control->st_mtime = statf->st_mtime;
        control->st_ctime = statf->st_ctime;
        key(control, sizeof(control));
        return 0;
}

#if DEBUGEXEC
void debugexec(char * sh11, int argc, char ** argv)
{
        int i;
        fprintf(stderr, "shll=%s\n", sh11 ? sh11 : "<null>");
        fprintf(stderr, "argc=%d\n", argc);
        if (!argv) {
                fprintf(stderr, "argv=<null>\n");
        } else { 
                for (i = 0; i <= argc ; i++)
                        fprintf(stderr, "argv[%d]=%.60s\n", i, argv[i] ? argv[i] : "<null>");
        }
}
#endif /* DEBUGEXEC */

void rmarg(char ** argv, char * arg)
{
        for (; argv && *argv && *argv != arg; argv++);
        for (; argv && *argv; argv++)
                *argv = argv[1];
}

void chkenv_end(void);

int chkenv(int argc)
{
        char buff[512];
        unsigned long mask, m;
        int l, a, c;
        char * string;
        extern char ** environ;

        mask = (unsigned long)getpid();
        stte_0();
         key(&chkenv, (void*)&chkenv_end - (void*)&chkenv);
         key(&data, sizeof(data));
         key(&mask, sizeof(mask));
        arc4(&mask, sizeof(mask));
        sprintf(buff, "x%lx", mask);
        string = getenv(buff);
#if DEBUGEXEC
        fprintf(stderr, "getenv(%s)=%s\n", buff, string ? string : "<null>");
#endif
        l = strlen(buff);
        if (!string) {
                /* 1st */
                sprintf(&buff[l], "=%lu %d", mask, argc);
                putenv(strdup(buff));
                return 0;
        }
        c = sscanf(string, "%lu %d%c", &m, &a, buff);
        if (c == 2 && m == mask) {
                /* 3rd */
                rmarg(environ, &string[-l - 1]);
                return 1 + (argc - a);
        }
        return -1;
}

void chkenv_end(void){}

#if HARDENING

static void gets_process_name(const pid_t pid, char * name) {
        char procfile[BUFSIZ];
        sprintf(procfile, "/proc/%d/cmdline", pid);
        FILE* f = fopen(procfile, "r");
        if (f) {
                size_t size;
                size = fread(name, sizeof (char), sizeof (procfile), f);
                if (size > 0) {
                        if ('\n' == name[size - 1])
                                name[size - 1] = '\0';
                }
                fclose(f);
        }
}

void hardening() {
    prctl(PR_SET_DUMPABLE, 0);
    prctl(PR_SET_PTRACER, -1);

    int pid = getppid();
    char name[256] = {0};
    gets_process_name(pid, name);

    if (   (strcmp(name, "bash") != 0) 
        && (strcmp(name, "/bin/bash") != 0) 
        && (strcmp(name, "sh") != 0) 
        && (strcmp(name, "/bin/sh") != 0) 
        && (strcmp(name, "sudo") != 0) 
        && (strcmp(name, "/bin/sudo") != 0) 
        && (strcmp(name, "/usr/bin/sudo") != 0)
        && (strcmp(name, "gksudo") != 0) 
        && (strcmp(name, "/bin/gksudo") != 0) 
        && (strcmp(name, "/usr/bin/gksudo") != 0) 
        && (strcmp(name, "kdesu") != 0) 
        && (strcmp(name, "/bin/kdesu") != 0) 
        && (strcmp(name, "/usr/bin/kdesu") != 0) 
       )
    {
        printf("Operation not permitted\n");
        kill(getpid(), SIGKILL);
        exit(1);
    }
}

#endif /* HARDENING */

#if !TRACEABLE

#define _LINUX_SOURCE_COMPAT
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

#if !defined(PT_ATTACHEXC) /* New replacement for PT_ATTACH */
   #if !defined(PTRACE_ATTACH) && defined(PT_ATTACH)
       #define PT_ATTACHEXC     PT_ATTACH
   #elif defined(PTRACE_ATTACH)
       #define PT_ATTACHEXC PTRACE_ATTACH
   #endif
#endif

void untraceable(char * argv0)
{
        char proc[80];
        int pid, mine;

        switch(pid = fork()) {
        case  0:
                pid = getppid();
                /* For problematic SunOS ptrace */
#if defined(__FreeBSD__)
                sprintf(proc, "/proc/%d/mem", (int)pid);
#else
                sprintf(proc, "/proc/%d/as",  (int)pid);
#endif
                close(0);
                mine = !open(proc, O_RDWR|O_EXCL);
                if (!mine && errno != EBUSY)
                        mine = !ptrace(PT_ATTACHEXC, pid, 0, 0);
                if (mine) {
                        kill(pid, SIGCONT);
                } else {
                        perror(argv0);
                        kill(pid, SIGKILL);
                }
                _exit(mine);
        case -1:
                break;
        default:
                if (pid == waitpid(pid, 0, 0))
                        return;
        }
        perror(argv0);
        _exit(1);
}
#endif /* !TRACEABLE */

char * xsh(int argc, char ** argv)
{
        char * scrpt;
        int ret, i, j;
        char ** varg;
        char * me = argv[0];
        if (me == NULL) { me = getenv("_"); }
        if (me == 0) { fprintf(stderr, "E: neither argv[0] nor $_ works."); exit(1); }

        ret = chkenv(argc);
        stte_0();
         key(pswd, pswd_z);
        arc4(msg1, msg1_z);
        arc4(date, date_z);
        if (date[0] && (atoll(date)<time(NULL)))
                return msg1;
        arc4(shll, shll_z);
        arc4(inlo, inlo_z);
        arc4(xecc, xecc_z);
        arc4(lsto, lsto_z);
        arc4(tst1, tst1_z);
         key(tst1, tst1_z);
        arc4(chk1, chk1_z);
        if ((chk1_z != tst1_z) || memcmp(tst1, chk1, tst1_z))
                return tst1;
        arc4(msg2, msg2_z);
        if (ret < 0)
                return msg2;
        varg = (char **)calloc(argc + 10, sizeof(char *));
        if (!varg)
                return 0;
        if (ret) {
                arc4(rlax, rlax_z);
                if (!rlax[0] && key_with_file(shll))
                        return shll;
                arc4(opts, opts_z);
#if HARDENING
            arc4_hardrun(text, text_z);
            exit(0);
       /* Seccomp Sandboxing - Start */
       seccomp_hardening();
#endif
                arc4(text, text_z);
                arc4(tst2, tst2_z);
                 key(tst2, tst2_z);
                arc4(chk2, chk2_z);
                if ((chk2_z != tst2_z) || memcmp(tst2, chk2, tst2_z))
                        return tst2;
                /* Prepend hide_z spaces to script text to hide it. */
                scrpt = malloc(hide_z + text_z);
                if (!scrpt)
                        return 0;
                memset(scrpt, (int) ' ', hide_z);
                memcpy(&scrpt[hide_z], text, text_z);
        } else {                        /* Reexecute */
                if (*xecc) {
                        scrpt = malloc(512);
                        if (!scrpt)
                                return 0;
                        sprintf(scrpt, xecc, me);
                } else {
                        scrpt = me;
                }
        }
        j = 0;
#if BUSYBOXON
        varg[j++] = "busybox";
        varg[j++] = "sh";
#else
        varg[j++] = argv[0];            /* My own name at execution */
#endif
        if (ret && *opts)
                varg[j++] = opts;       /* Options on 1st line of code */
        if (*inlo)
                varg[j++] = inlo;       /* Option introducing inline code */
        varg[j++] = scrpt;              /* The script itself */
        if (*lsto)
                varg[j++] = lsto;       /* Option meaning last option */
        i = (ret > 1) ? ret : 0;        /* Args numbering correction */
        while (i < argc)
                varg[j++] = argv[i++];  /* Main run-time arguments */
        varg[j] = 0;                    /* NULL terminated array */
#if DEBUGEXEC
        debugexec(shll, j, varg);
#endif
        execvp(shll, varg);
        return shll;
}

int main(int argc, char ** argv)
{
#if SETUID
   setuid(0);
#endif
#if DEBUGEXEC
        debugexec("main", argc, argv);
#endif
#if HARDENING
        hardening();
#endif
#if !TRACEABLE
        untraceable(argv[0]);
#endif
        argv[1] = xsh(argc, argv);
        fprintf(stderr, "%s%s%s: %s\n", argv[0],
                errno ? ": " : "",
                errno ? strerror(errno) : "",
                argv[1] ? argv[1] : "<null>"
        );
        return 1;
}

1.原理


分析test.sh.x.c源码,SETUID|DEBUGEXEC|TRACEABLE|HARDENING|BUSYBOXON|MMAP2宏控制相关的安全功能

Read more...

免费https证书的申请

提到免费https证书,就不得不提ACME协议。ACME(Automatic Certificate Management Environment)是一种用于自动管理HTTPS证书的开放协议,其追求自动化HTTPS证书的申请、颁发、续期和撤销,无需人工干预,通过API进行证书的创建、验证和更新,使用HTTP-01、DNS-01、TLS-ALPN-01验证域名所有权,自动化管理证书的生命周期,避免证书过期。

需要注意的是,通过ACME协议申请的证书有效期统一为90天,这种短期有效的设计更符合现代安全最佳实践。值得一提的是,随着技术发展,部分新型HTTP服务器(如Caddy)已内置ACME协议支持,开发者无需借助额外工具即可实现HTTPS证书的自动化管理,这大大降低了HTTPS的部署门槛。

HTTP-01:

这是当今最常见的验证方式。Let’s Encrypt向ACME客户端提供一个令牌,然后用户需要替换Web服务器http://<你的域名>/.well-known/acme-challenge/TOKEN(用提供的令牌替换TOKEN)路径上放置的指定文件。该文件包含令牌以及帐户密钥的指纹。一旦ACME客户端告诉Let’s Encrypt文件已准备就绪,Let’s Encrypt会尝试获取它(可能从多个地点进行多次尝试)。该方式不支持颁发通配符证书。

DNS-01:

此验证方式要求用户在该域名下的TXT记录中放置特定值来证明控制域名的DNS系统。该配置比HTTP-01略困难,但可以在某些HTTP-01不可用的情况下工作,同时允许您颁发通配符证书。Let’s Encrypt为用户的ACME客户端提供令牌后,用户需要将令牌记录放在 _acme-challenge.<YOUR_DOMAIN>下,然后Let’s Encrypt将向DNS系统查询该记录。

TLS-ALPN-01:

此验证方式客户端(Caddy)向ACME服务器(如 Let’s Encrypt)发起证书申请,服务器指定TLS-ALPN-01挑战类型,并提供一个挑战值(token),客户端需要响应这个挑战。客户端在443端口启动一个临时的TLS服务器,并使用ALPN扩展指定acme-tls/1协议。客户端使用token和授权信息生成一个特殊的自签名证书,作为TLS服务器的证书。ACME服务器尝试通过TLS-ALPN-01连接目标域的443端口,并检查客户端提供的自签名证书。(Caddy默认通过TLS-ALPN-01验证进行自动证书管理)

1.certbot


Let’s Encrypt是一个受信任的证书颁发机构CA,它由非营利组织Internet Security Research Group (ISRG)运营,旨在推动HTTPS的普及,通过自动化流程为网站提供免费的数字证书(DV 类型),其根证书(ISRG Root X1)已被所有主流操作系统、浏览器和设备的信任库预置,这里本文介绍的certbot是Let’s Encrypt官方推荐的证书申请工具。

certbot功能十分强大,本人但受限于部署条件,同时域名托管于cloudflare等因素,故使用certbot-dns-cloudflare插件实现自动证书申请与续期。

sudo apt-get install certbot python3-certbot-dns-cloudflare python-certbot-dns-cloudflare-doc
certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini -d test.diyao.me

certbot-dns-cloudflare插件的用法,可参见python-certbot-dns-cloudflare-doc文档

最后需要将certbot加入crontab中完成证书的自动续期

0 0 1 * *  /usr/bin/certbot renew --quiet

2.acme.sh


acme.sh是使用纯SHELL脚本编写的acme协议的客户端协议实现,无任何库或软件依赖。支持椭圆曲线加密的ECDSA类型证书,支持SAN(Subject Alternative Name)和通配符证书(一张SAN证书或通配符证书可以保护多域名)。

curl https://get.acme.sh | sh
acme.sh --register-account -m 530384671@qq.com

本文使用的DNS-01验证,针对市面大多数的域名托管商acme.sh都支持,比如aliyun,华为云等。当然acme.sh亦支持使用HTTP-01验证方式

acme.sh --issue --dns dns_cf -d test.diyao.me
acme.sh --issue --standalone -d test.diyao.me

通过–server参数,可以选择不同的免费CA证书颁发机构。目前,acme.sh支持以下CA:Let’s Encrypt、Buypass、ZeroSSL、SSL.com和Google。相较于certbot,acme.sh自动安装crontab完成证书的续期。

Read more...

现代编译构建工具-Ninja语言

在传统的C/C++项目中,通常采用make系统,使用Makefile约束进行整个项目的编译构建。Makefile指定的编译依赖规则会使编译流程简单,但是make的依赖大而且复杂,在大型项目编译时,使用的模块越来越多,Makefile组织的项目编译时间越来越长,这个对于编译效率来说是一个极大的浪费。在执行增量或无操作时Make相较于Ninja构建时很慢,特别诸如Google Chrome这种将40,000个输入文件编译为单个可执行文件的工程,这可能会大大降低开发人员在大型项目上的工作速度。

根据Chromium的实际测试:在超过30,000个源文件的情况下,Ninja也能够在1秒钟内开始进行真正的构建。与之相比,通过资深工程师进行编写的Makefiles文件也需要10-20秒才能开始构建。

Ninja是由Google员工Evan Martin开发的小型构建系统。Ninja注重速度,Ninja被设计为使其输入文件由更高级别的构建系统生成,并且其被设计为尽可能快地运行构建。同时与Make相比,Ninja缺少诸如字符串操作比较、隐式规则、函数和遍历搜索等的功能,因为Ninja生成文件不需要手工编写。相反,应使用"生成器"生成Ninja生成文件。CMake,Meson,Gyp(Google早期用来维护chromium项目的构建系统,GN则是用来替代GYP的工具)和Gn是流行的构建管理软件工具,这些更高层次的工具,支持生成Ninja文件构建工程项目。

Ninja特有功能:

  1. Ninja特别支持在构建时发现额外的依赖项,从而可以轻松地为C/C++代码获取正确的头文件
  2. 更改编译标志将导致输出重新生成,因为输出隐式依赖于用于生成它们的命令行(等等,啥意思?)
  3. 规则可以提供正在运行的命令的简短描述,因此您可以在构建时打印例如CC foo.o而不是长命令行
  4. 构建工作始终并行运行。默认情况下,根据系统具有的CPU核数决定并行执行的线程数
  5. 命令输出始终是缓冲的。这意味着并行运行的命令不会交错其输出,当命令失败时可以将其失败输出打印在产生故障的完整命令行旁边

疑问: 特有功能1中,gcc -M 输出用于make系统的规则,该规则描述了源文件的依赖关系,makefile也可以轻松地为C/C++代码获取正确的头文件(见文章Gcc部分参数说明)

接下来将通过具体示例,探索GN、Meson和CMake这些现代构建工具如何生成Ninja文件,并分析它们在实际项目中的应用优势。

0.GN


GN是专为生成Ninja文件而设计的,运行速度极快,尤其适合大型项目,已在Chromium项目验证。其语法简单直接,避免了复杂的逻辑处理,专注于配置构建规则并且内置对依赖项的高效处理和验证机制,减少了构建错误,能够很好地应对超大规模代码库。但是其由Google内部主导开发,社区生态和扩展能力并没有Cmake和Meson好,并且缺少某些高级特性,如内置的包管理和复杂的自定义逻辑支持。

gn把.gn文件转换成.ninja文件,然后Ninja根据.ninja文件将源码生成目标程序

GN工具安装

apt-get install -y ninja-build
git clone https://gn.googlesource.com/gn
cd gn && python3 build/gen.py --allow-warning
out/gn_unittests

GN构建样例

gn-sample.tar.gz是关于gn构建的入门样例

tree -a
.
├── build
│   ├── BUILDCONFIG.gn
│   └── config
│       └── toolchains
│           └── BUILD.gn
├── BUILD.gn
├── .gn
└── src
    ├── BUILD.gn
    ├── main.c
    ├── tools.c
    └── tools.h
  1. .gn文件: gn命令执行时最先加载的配置文件,.gn置于根目录,buildconfig参数指定构建配置文件位置. 例子中指定的是//build/BUILDCONFIG.gn
  2. build/BUILDCONFIG.gn: 针对项目的一些设置包括不同操作系统的设置,默认编译工具的设置,例子中通过set_default_toolchain,指定//build/config/toolchains:gcc
  3. build/config/toolchains/BUILD.gn: 这个是针对工具链的一些设置
  4. BUILD.gn: 一般进行配置的文件,主要是配置需要编译的文件,库,最终的可执行文件,本例中依赖子目录src中的a.out目标
buildconfig = "//build/BUILDCONFIG.gn"
group("default") {
  deps = [ "//src:a.out" ]
}
set_default_toolchain("//build/config/toolchains:gcc")
import("//build/config/component.gni")  // 允许使用component模板
declare_args() {
  is_debug = false
  is_component_build = false   // 配置[静态编译,动态编译];如果为true的话,就全部是动态库,否则就是静态库
  is_mac = false
}
executable("a.out") {
  sources = [
    "main.c",
  ]
  include_dirs = [
    ".",
  ]
  deps = [
    ":tools",
  ]
  libs = [ "json-c" ]
  if (is_debug) {
    cflags = [ "-g", "-O0" ]
  }

shared_library("tools") {
  sources = [
    "tools.c",
  ]
  if (is_debug) {
    cflags = [ "-g", "-O0" ]
  }

component("test") {
  sources = [ "tools.c" ]
  if (is_debug) {
    cflags = [ "-g", "-O0" ]
  }
}
toolchain("gcc") {
  tool("cc") {
    depfile = "{{output}}.d"
    command = "gcc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -{{output}}"
    depsformat = "gcc"
    description = "CC {{output}}"
    outputs =
        [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
  }
  tool("cxx") {
    depfile = "{{output}}.d"
    command = "g++ -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -{{output}}"
    depsformat = "gcc"
    description = "CXX {{output}}"
    outputs =
        [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
  }
  tool("alink") {
    command = "ar rcs {{output}} {{inputs}}"
    description = "AR {{target_output_name}}{{output_extension}}"
    outputs =
        [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
    default_output_extension = ".a"
    output_prefix = "lib"
  }
  tool("solink") {
    soname = "{{target_output_name}}{{output_extension}}"
    sofile = "{{output_dir}}/$soname"
    rspfile = soname + ".rsp"
    if (is_mac) {
      os_specific_option = "-install_name @executable_path/$sofile"
      rspfile_content = "{{inputs}} {{solibs}} {{libs}}"
    } else {
      os_specific_option = "-Wl,-soname=$soname"
      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
    }
    command = "g++ -shared {{ldflags}} -o $sofile $os_specific_option @$rspfile"
    description = "SOLINK $soname"
    default_output_extension = ".so"
    default_output_dir = "{{root_out_dir}}"
    outputs = [ sofile ]
    link_output = sofile
    depend_output = sofile
    output_prefix = "lib"
  }
  tool("link") {
    outfile = "{{target_output_name}}{{output_extension}}"
    rspfile = "$outfile.rsp"
    if (is_mac) {
      command = "g++ {{ldflags}} -o $outfile @$rspfile {{solibs}} {{libs}}"
    } else {
      command = "g++ {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
    }
    description = "LINK $outfile"
    default_output_dir = "{{root_out_dir}}"
    rspfile_content = "{{inputs}}"
    outputs = [ outfile ]
  }
  tool("stamp") {
    command = "touch {{output}}"
    description = "STAMP {{output}}"
  }
  tool("copy") {
    command = "cp -af {{source}} {{output}}"
    description = "COPY {{source}} {{output}}"
  }
}
template("component") {
  if (is_component_build) {
    _component_mode = "shared_library
    if (!defined(invoker.output_name)) {
      _output_name = get_label_info(":$target_name", "label_no_toolchain")
      _output_name = string_replace(_output_name, "$target_name:$target_name", target_name)
      _output_name = string_replace(_output_name, "//", "")
      _output_name = string_replace(_output_name, "/", "_")
      _output_name = string_replace(_output_name, ":", "_")
    }
  } else if (defined(invoker.static_component_type)) {
    assert(invoker.static_component_type == "static_library" || invoker.static_component_type == "source_set")
    _component_mode = invoker.static_component_type
  } else if (!defined(invoker.sources) || invoker.sources == []) {
    _component_mode = "source_set"
  } else {
    _component_mode = "static_library"
  }
  target(_component_mode, target_name) {
    if (defined(_output_name)) {
      output_name = _output_name
    }
    if (is_component_build) {
      output_extension = "so"
    }
    forward_variables_from(invoker, ["testonly", "visibility",])
    forward_variables_from(invoker, "*", ["testonly", "visibility",])
  }
}

GN相关概念

1.GN命令

Read more...

cicd-持续集成与持续交付

在快节奏的数字化时代,软件交付的速度和质量直接决定了企业的竞争力。传统的开发模式中,手动构建、测试和部署不仅效率低下,还容易引入人为错误,导致交付延迟或线上故障。持续集成与持续交付/部署的出现,彻底改变了这一局面。其通过自动化构建、测试和部署流程,让开发团队能够快速、安全、可靠地交付软件。无论是小型创业公司还是大型企业,采用CI/CD都能显著提升开发效率,减少人为失误,并加快产品迭代速度

0.CI/CD概况


CI/CD是敏捷开发和精益理念在DevOps中的实践,是一种以持续集成/交付/部署为手段,实现高效、安全、快速的软件交付方式

  • CI-Continuous Integration:持续集成是在源代码变更后自动检测、拉取、构建和在大多数情况下进行单元测试的过程
  • CD-Continuous Delivery和Continuous Deployment:持续交付和持续部署,持续交付,完成ci中构建及单元测试和集成测试的自动化流程后,可自动将已验证的代码发布到服务器,持续部署,意味着所有的变更都会被自动部署到生产环境中,当然出于业务考虑,可以选择不部署

补充概念:

敏捷开发是一种强调快速响应变化与持续交付价值的软件开发方法论,强调人际互动、快速和频繁的交付、增量价值交付、分散价值、客户协作反馈循环和持续改进

敏捷开发方法框架是指一组具体的流程、规则、角色和工具集合,用来指导团队按照敏捷原则进行软件开发和项目管理。它是敏捷开发理念的具体落地形式,其中包括Scrum,Kanban,Lean,LeSS,SAFe,Jira原生支持Scrum和Kanban,也可通过插件支持其他的敏捷开发框架

  1. Scrum是一种敏捷开发方法框架,专门用来管理复杂项目中的软件开发过程。它提供了一套明确的角色、事件和工件(工作产出),帮助团队在短周期内不断迭代、持续交付可用的软件
  2. Kanban是一种可视化、渐进式的工作管理方法,起源于丰田生产系统(TPS),后来被引入软件开发及知识工作领域,成为敏捷开发的重要实践之一。它强调持续改进(Kaizen)、限制在制品(WIP)和优化工作流程,帮助团队提高效率并减少浪费

image

工具选择

要具体落实CI/CD开发模式,通常需要在代码托管、持续集成、持续部署、构建发布、环境管理等各个环节,分别选择合适的工具组合进行实施。

  1. 代码托管

    代码托管工具五花八门,比如GitHub、GitLab、Gitea、Bitbucket、SVN等,并且其中很多现代代码托管平台如GitLab、GitHub不仅仅是代码仓库,还内置了CI/CD功能,提供一体化的DevOps支持

  2. 持续集成

    • Jenkins image
      • 优点:
        1. web界面管理,社区强大
        2. 插件丰富,文档丰富
        3. 历史久远(Hudson Java编写2005发布->Sun收购),应用广泛
        4. 分布式任务,功能强大
      • 缺点
        1. 插件及自生安装复杂
        2. 启动慢,运行慢,资源消耗高
        3. 插件编写语言只支持Groovy和Java
        4. 学习成本高,专人维护
    • Github Action
      • 优点:
        1. 轻量级,启动快,资源占用少,运行快
        2. 云原生支持docker,新兴CI/CD工具(Github 2019)
        3. 编写yaml文件,Pipeline语法比Jenkins-File语法简单
        4. 本机直接测试Pipeline是否正确
        5. 插件编写简单
      • 缺点:
        1. 插件有限, 需要公网环境
        2. 大型项目复杂的工作流程, 不如Jenkins
    • Drone image
      • 优点:
        1. 云原生设计,轻量易部署,适合K8s和GitOps场景
        2. Gitea/GitLab等集成良好
        3. YAML编排简单直观,插件机制灵活
        4. 支持Secrets管理、触发器等CI常见功能
      • 缺点:
        1. 文档较简略,相比Jenkins/GitLab CI
        2. UI功能简单,权限管理能力较弱
    • Travis-ci
    • GitLab
    • Gitea
  3. 持续部署

    持续部署工作通常利用持续集成平台的相关插件完成,插件帮助自动化地将代码部署到不同环境

    • drone的插件市场https://plugins.drone.io/,列出其支持的部署插件,及其详细用法
    • jenkins内置的插件管理界面,可以安装最新的持续部署相关插件
    • github市场的插件更为丰富https://github.com/marketplace

1.应用案例


Gitea案例

Gitea工作流

Gitea Actions的配置文件叫做workflow文件,存放在代码仓库的.gitea/workflows目录。workflow文件采用YAML格式,文件名可以任意取,但是后缀名统一为.yml。一个库可以有多个workflow文件。Gitea只要发现.gitea/workflows目录里面有.yml文件,就会自动运行该文件。Gitea Actions的语法与GitHub Actions以及Drone Pipeline高度兼容,因此可以直接参考GitHub的工作流规范来编写Gitea的工作流。

Read more...

般若波罗蜜多心经

译文:

观自在菩萨,行深般若波罗蜜多时,照见五蕴皆空,度一切苦厄。舍利子,色不异空,空不异色;色即是空,空即是色。受、想、行、识,亦复如是。舍利子,是诸法空相,不生不灭,不垢不净,不增不减,是故空中无色,无受、想、行、识;无眼、耳、鼻、舌、身、意;无色、声、香、味、触、法;无眼界,乃至无意识界;无无明,亦无无明尽;乃至无老死,亦无老死尽。无苦、集、灭、道,无智亦无得。以无所得故,菩提萨埵,依般若波罗蜜多故,心无罣碍。无罣碍故,无有恐怖,远离颠倒梦想,究竟涅槃。三世诸佛,依般若波罗蜜多故,得阿耨多罗三藐三菩提。故知般若波罗蜜多,是大神咒,是大明咒,是无上咒,是无等等咒,能除一切苦,真实不虚。故说般若波罗蜜多咒,即说咒曰:“揭谛、揭谛,波罗揭谛,波罗僧揭谛,菩提萨婆诃。”

梵语:

आर्यावलोकितेश्वरो बोधिसत्त्वो गंभीरायां प्रज्ञापारमितायां चर्यां चरमाणो व्यवलोकयति स्म । पंचस्कन्धाः । तांश्च स्वभावशून्यान्पश्यति स्म । इह शारिपुत्र रूपं शून्यता शून्यतैव रूपं रूपान्न पृथक्शून्यता शून्यताया न पृथग्रूपं यद्रूपं सा शून्यता या शून्यता तद्रूपं । एवमेव वेदनासंज्ञासंस्कारविज्ञानानि । इह शारिपुत्र सर्वधर्माः शून्यतालक्षणा अनुत्पन्ना अनिरुद्धा अमला न विमला नोना न परिपूर्णाः । तस्माच्छारिपुत्र शून्यतायां न रूपं न वेदना न संज्ञा न संस्कारा न विज्ञानानि । न चक्षुःश्रोत्रघ्राणजिह्वाकायमनांसी । न रूपशब्दगंधरसस्प्रष्टव्यधर्माः । न चक्षुर्धातुर्यावन्न मनोविज्ञानधातुः । न विद्या नाविद्या न विद्याक्षयो नाविद्याक्षयो यावन्न जरामरणं न जरामरणक्षयो न दुःखसमुदयनिरोधमार्गा न ज्ञानं न प्राप्तिः ॥ तस्मादप्राप्तित्वाद्बोधिसत्त्वाणां प्रज्ञापारमितामाश्रित्य विहरत्यचित्तावरणः । चित्तावरणनास्तित्वादत्रस्तो विपर्यासातिक्रान्तो निष्ठनिर्वाणः ।। त्र्यध्वव्यवस्थिताः सर्वबुद्धाः प्रज्ञापारमितामाश्रित्यानुत्तरां सम्यक्सम्बोधिमभिसंबुद्धाः ।। तस्माज्ज्ञातव्यं प्रज्ञापारमिता महामन्त्रो महाविद्यामन्त्रो ऽनुत्तरमन्त्रो ऽसमसममन्त्रः सर्वदुःखप्रशमनः । सत्यममिथ्यत्वात् । प्रज्ञपारमितायामुक्तो मन्त्रः । तद्यथा गते गते पारगते पारसंगते बोधि स्वाहा ।। इति प्रज्ञापारमिताहृदयं समाप्तम्

Read more...

terraform-从入门到放弃

注: 本文只简单的介绍Terraform的安装以及基本使用,文章基本可看作对Terraform官方文档的翻译以及部分自我理解。

Terraform旨在任何云以及数据中心上可以自动化的配置和管理资源,Terraform将云API或数据中心接口编码为声明性配置文件,实现基础设施即代码。(Infrastructure as Code,简称IaC,是一种管理和提供计算基础设施的方式,利用代码来自动化基础设施的部署和管理)

网站中描述Terraform的几点优势:

  • 使用HCL(HashiCorp Configuration Language)在Terraform文件中将基础设施编写为代码,以便从任何基础设施提供商处调配资源。
  • 构建基础设施自动化工作流程,以跨IT运营和开发团队编写、协作、重用和配置基础设施作为代码。
  • 通过基于角色的访问控制、策略实施和审计,为安全性、合规性和成本管理建立护栏,实现标准化。
  • 使用自助服务和基础设施作为代码将工作流自动化扩展到组织中的所有团队,并与VCS、ITSM(IT服务管理,如ServiceNow、Jira)和CI/CD集成。

使用场景:

  • IaC,使用Terraform可以将多云管理编码为声明书代码,Terraform提供的插件生态使得可以使用统一、易于学习的配置语言来定义资源,代码被编码、共享、版本化,并且通过一致的工作流在所有环境中执行。
  • 多云部署,Terraform采用单一的自动化工作流来管理多个基础设施和SaaS提供商,并处理跨云依赖关系,简化多云基础设施的生命周期管理和编排,适用于任何规模的环境。
  • Terraform支持K8S,可以通过单一工作流程配置Kubernetes集群、周边服务和应用程序资源。
  • 采用Consul-Terraform-Sync的网络基础设施自动化(NIA)可确保网络和安全基础设施安全地适应变化。Consul-Terraform-Sync根据Consul观察到的变化并通过Terraform进行管理,自动执行各种网络任务和工作流。这些任务可以由不同的事件触发,例如服务实例的扩展、服务地址或端口号的更改、服务标签、元数据或健康状况的更新等。这可以缩短交付时间并大大降低配置错误的可能性。
  • HCP Terraform提供灵活的远程运行环境,可以轻松集成到现有的版本控制系统、CI/CD管道和IT服务管理界面中。这种与现有工作流的深度集成,减少了工具更改的需求,简化了启动和运行的过程,同时确保了平台团队和开发人员的工作一致性。
  • HCP Terraform可以帮助您对团队可部署的基础设施配置实施策略。基于工单的审查流程通常会成为瓶颈,导致开发速度变慢。相反,您可以使用HashiCorp Sentinel(一种策略即代码框架),在Terraform进行基础设施变更之前,自动实施合规性和治理策略。
  • HashiCorp Vault将每个动态密钥与租约(lease)关联,并在租约到期时自动销毁这些凭据。Vault支持与多种系统集成的动态密钥,并且可以通过插件轻松扩展功能。
  • HCP Packer是用于云镜像及云镜像版本跟踪管理,并通过API提供这些信息。通过将常用的基础镜像可以被标准化、安全化,并通过自动化进行更新。

0.Terraform安装


这里只粘贴ubuntu系统的安装方法,Terraform也支持windows,mac,freebsd甚至solaris系统的安装,其他的方法参见文献1。

wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

1.入门样例


把大象放进冰箱一般情况只需要三个步骤,同样Terraform工作流核心包括三个阶段:

  1. 写下你的梦想。使用HCL语言编写声明式配置文件定义资源,资源可跨越多个云平台或数据中心。
  2. 规划。创建一个执行计划,描述根据现有基础设施和定义的配置来创建、更新或销毁的基础设施。
  3. 让梦想成真。Terraform按照预订的顺序执行相关操作,帮您圆梦。

image

啊,上帝赐给我一个像朱颖一样漂亮的女朋友

什么?定义她?哦,她姓朱,瓜子脸…哦,对了她坐我斜对面…

HCL可以被视为一种领域特定语言(DSL)。它被设计用于描述基础设施资源的配置和管理,特别是与HashiCorp的工具(如Terraform)一起使用时。

resource "aws_vpc" "main" {
  cidr_block = var.base_cidr_block
}

<BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>" {
  # Block body
  <IDENTIFIER> = <EXPRESSION> # Argument
}

示例代码展示了Terraform语言的基本语法结构。块是用于包含其他内容的配置容器,通常表示某种对象的配置,例如资源。每个块都有一个块类型,可以包含零个或多个标签,并具有一个主体,主体中可以定义多个参数以及嵌套块。Terraform的大部分功能通过配置文件中的顶级块进行声明和控制。 IDENTIFIER表示参数名,它们出现在块内。EXPRESSION表示一个值,可以是字面值,也可以通过引用和组合其他值来得到。

Read more...

科学上网-去你妈的gfw

0.代理方案


代理是一种网络技术,允许用户或应用通过中间服务器(代理服务器)与目标服务器进行通信,从而实现数据传输的控制、过滤、加速等功能。普通的HTTP/HTTPS代理、SOCKS代理、VPN等传统代理通常不专门设计来对抗防火墙和审查系统,其功能主要是聚焦于流量转发、访问控制、加密通信等。专用于GFW的代理在设计时就考虑到深度包检查、流量分析和阻断,其通过强加密、流量伪装与混淆等方式对抗网络封锁,一般都会采用客户端服务器模式

1.NavieProxy

NaïveProxy是一个基于Chromium网络栈(net 模块)开发的轻量级代理工具,其核心设计目标是模拟普通HTTPS流量,以规避深度包检测和流量审查。其客户端部分使用github-action构建项目,服务端使用Caddy充当代理服务器。

[Browser → Naïve client] ⟶ Censor ⟶ [Frontend → Naïve server] ⟶ Internet

代理过程中客户端浏览器的HTTPS请求被Naïve客户端接收(其用一个伪造的证书(如自动申请的免费证书)来和客户端完成TLS握手,拿到明文数据),随后客户端把明文包装成自己的HTTPS请求,再通过另一个 TLS连接发往后端真实代理服务器,代理服务器收到再解密、还原原始请求,再访问真实目标网站,数据的返程封装与请求过程一致。

安装

二进制安装,https://github.com/klzgrad/forwardproxy/releases中选择对应平台的二进制进程直接绿色安装

配置

服务器配置:
{
    order forward_proxy before file_server
}
:443, hostdare.diyao.me {
    tls hostdare@diyao.me
    forward_proxy {
        basic_auth diyao cauc.peter@gmail.com
        hide_ip
        hide_via
        probe_resistance
    }
    file_server {
        root /var/www/html
    }
}

客户端配置:
{
    "listen": "socks://127.0.0.1:1080",
    "proxy": "https://diyao:cauc.peter@gmail.com@hostdare.diyao.me"
}

2.Tuic

Tuic(全称为TUIC - Tailscale UDP In Congestion control)是一个现代、高性能、低延迟的UDP传输协议,专为代理和绕过网络限制场景而设计,尤其适合替代传统的TCP/TLS + HTTP/HTTPS代理技术。它本质上是一个基于QUIC协议的应用层代理协议。QUIC内建的多路复用,加密(TLS 1.3),快速握手,自带拥塞控制和重传机制,使得Tuic天生就具备低延迟、无HOL(头阻塞)和NAT穿透能力强等优势

Read more...
Previous Page 3 of 5 Next Page