docker入门

docker linux的安装

# 下载
sudo wget -qO- https://get.docker.com/ | sh # 下载docker安装shell脚本

# 添加用户到docker用户组
sudo usermod -aG docker xbf # 用户xbf添加到docker用户组

# 安装后启动服务
service docker start

# 使用阿里云docker加速器
https://cr.console.aliyun.com/#/accelerator

docker命令

docker build # 建立一个新的image
docker pull nginx # 下载一个iamge
docker run -p 8080:80 -d nginx # 运行容器
docker ps # 查看运行的docker程容器
docker stop 容器ID # 停止运行
docker commit -m '备注' # 提交新的images
docker rmi image的ID # 删除image

编写简单文件自创docker镜像

# 新增文件夹下Dockerfile文件
FROM alpine:latest
MAINTAINER zhorz
CMD echo "Hello Docker"

# 在新建的文件夹下运行命令: docker build -t hello_docker . 

# dockerfile语法
FROM base image
RUN 执行命令
ADD 添加文件
COPY 拷贝文件
CMD 执行命令
EXPOSE 暴露端口
WORKDIR 指定路径
MAINTAINER 维护者
ENV 设定环境变量
ENTRYPOINT 容器入
USER 指定用户
VOLUME mount point

重新学习 20200628

更换 unbuntu 源 https://developer.aliyun.com/mirror/ubuntu?spm=a2c6h.13651102.0.0.11871b11drdcbT

docker 下载 和安装
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

验证
apt list --installed | grep docker
sudo docker run hello-world

更换 docker 源 https://www.cnblogs.com/reasonzzy/p/11127359.html (最好用阿里云的,飞快)

重启使配置生效
sudo systemctl daemon-reload
sudo systemctl restart docker

windows 使用 docker : https://nickjanetakis.com/blog/setting-up-docker-for-windows-and-wsl-to-work-flawlessly

通过Dockerfile 安装

FROM php:7.4.6-fpm-buster
# 设置阿里云源
RUN echo 'deb http://mirrors.aliyun.com/debian/ buster main non-free contrib\n\
deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib\n\
deb http://mirrors.aliyun.com/debian-security buster/updates main\n\
deb-src http://mirrors.aliyun.com/debian-security buster/updates main\n\
deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib\n\
deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib\n\
deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib\n\
deb-src http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib' > /etc/apt/sources.list
RUN apt-get update
# gd库
RUN apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd
# pdo
RUN apt-get update && docker-php-ext-install pdo_mysql
# yaf
RUN pecl install yaf && docker-php-ext-enable yaf
# redis 
RUN pecl install redis && docker-php-ext-enable redis
# apcu 
RUN pecl install apcu && docker-php-ext-enable apcu
# pcntl
RUN docker-php-ext-install pcntl
# 生产环境 dev 要改成  product
RUN echo '[yaf]\n\
yaf.environ="dev"\n\
yaf.use_namespace=On\n\
yaf.use_spl_autoload=1' >> /usr/local/etc/php/conf.d/docker-php-ext-yaf.ini

docker build -t sm:php74_v2 .

交互式进入容器
sudo docker run -it -v /home/vagrant:/home/vagrant sm:php74_v2 /bin/bash

启动php-fpm

docker run --name php74 -d -p 9000:9000 -v /home/vagrant:/home/vagrant sm:php74_v2 /usr/local/sbin/php-fpm

修改 nginx 端口指向

function php74(){
tty=
tty -s && tty=--tty
sudo docker run
$tty
--interactive
--rm
--volume $PWD:/home/vagrant
--workdir /home/vagrant
sm:php74_v2 php "$@"
}

更改全局composer源 composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

php composer.phar -vvv --ignore-platform-reqs install

问题 php:7.4.6-fpm-buster 镜像缺少 pcntl、git

  1. Dockerfile 中添加 docker-php-ext-install pcntl
  2. 从源码编译

sudo docker history php:7.4.6-fpm-buster --no-trunc --format "{{.CreatedBy}}"
得知源码地址 https://www.php.net/distributions/php-7.4.6.tar.gz

cd /path/to/php-source-code/ext/pcntl
phpize
./configure --enable-pcntl --with-php-config=/path/to/php-config
make && make install
php.ini 中 引入 extension=pcntl.so

es6中的promise相关

// 例子1
  const testPromise = new Promise(function (resolve, reject) {
    let a = 5
    if (a === 5) {
      resolve('success')
    } else {
      reject('catch error2')
    }
  })
  // resolve 结果没法处理,一直抛出去
  testPromise.then(null, (res) => {
    console.log(res, 1)
  }).then(null, res => {
    console.log(res, 2)
  }).then(res => {
    console.log(res, 3)
  })

// 例子2
const testPromise = new Promise(function (resolve, reject) {
    let a = 5
    if (a === 5) {
      resolve('success')
    } else {
      reject('catch error2')
    }
  })
  testPromise.then(res => { console.log('res1', res); return 123}, err => { console.log('err1', err)})
    .then(res => { console.log('res2', res)}, err => { console.log('err2', err); return Promise.reject(456);})
    .then(res => { console.log('res3', res)}, err => { console.log('err3', err)})
    .then(res => { console.log('res4', res)}, err => { console.log('err4', err)})

87BCF1C372C9D7FF8013EB6EC00D8517.png

AF1C4ADC74288ED1B08554A85194FF08.png

react学习笔记

阮一峰入门教程

jsx 基本语法规则 遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析

// 组件的使用
class HelloMessage extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            liked: false
        }
    }
    static propTypes = {
        title: PropTypes.string.isRequired, // 验证属性
    }
    static defaultProps = {
        name: 'zhorz' // 设置默认属性 
    }
    toggle() {
        this.setState({liked: this.state.liked}) // 重新渲染组件
    }
    render() {
        return <h1>hello {this.props.name} <button onClick={this.toggle.bind(this)}>change</button></h1> 
        // 只能包含一个顶层标签,属性通过 this.props 对象获取
        // 通过 this.props.children (可以用React.Children代替) 可以获取组件子节点,vue中使用插槽 solt 实现
    }
}

// 注意属性名 class -> className,for -> htmlFor
ReactDOM.render(<HelloMessage name="zhorz"/>, document.getElementById('example'))


// react 获取 dom 的几种方式

class HelloMessage extends React.Component {
    handleClick() {
        this.refs.my.focus()
    }
    render() {
        return (
            <div>
                <input type="text" ref="my" />
                <input type="button" value="Focus the text input" onClick={this.handleClick.bind(this)} />
            </div>
        );
    }
}

class HelloMessage extends React.Component {
    constructor(props) {
        super(props)
        this.my = React.createRef()
    }
    handleClick() {
        this.my.current.focus()
    }
    render() {
        return (
            <div>
                <input type="text" ref={this.my} />
                <input type="button" value="Focus the text input" onClick={this.handleClick.bind(this)} />
            </div>
        );
    }
}

class HelloMessage extends React.Component {
    handleClick() {
        this.my.focus()
    }
    handleClick2 = () => {
        this.my.focus() // 使用箭头 函数,这种方式就不用 bind 了,this 指向定义时所在的对象
    }
    render() {
        return (
            <div>
                <input type="text" ref={(el) => { this.my = el}} />
                <input type="button" value="Focus the text input" onClick={this.handleClick2} />
            </div>
        );
    }
}

ReactDOM.render(
    <HelloMessage name="zhorz" />,
    document.getElementById('example')
);

style={{opacity: this.state.opacity}}
// 因为 React 组件样式是一个对象,所以第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象

极客时间demo

create-react-app 模块按需加载

create-react-app 引入组件

php+redis实现令牌桶限流

<?php
class RateLimit
{
    private $minNum = 60; //单个用户每分访问数
    private $dayNum = 10000; //单个用户每天总的访问量

    public function minLimit($uid)
    {
        $minNumKey = $uid . '_minNum';
        $dayNumKey = $uid . '_dayNum';
        $resMin    = $this->getRedis($minNumKey, $this->minNum, 60);
        $resDay    = $this->getRedis($minNumKey, $this->minNum, 86400);
        if (!$resMin['status'] || !$resDay['status']) {
            exit($resMin['msg'] . $resDay['msg']);
        }
    }

    public function getRedis($key, $initNum, $expire)
    {
        $nowtime  = time();
        $result   = ['status' => true, 'msg' => ''];
        $redisObj = $this->di->get('redis');
        $redis->watch($key); // redis watch 处理并发
        $limitVal = $redis->get($key);
        if ($limitVal) {
            $limitVal = json_decode($limitVal, true);
            $newNum   = min($initNum, ($limitVal['num'] - 1) + (($initNum / $expire) * ($nowtime - $limitVal['time'])));
            if ($newNum > 0) {
                $redisVal = json_encode(['num' => $newNum, 'time' => time()]);
            } else {
                return ['status' => false, 'msg' => '当前时刻令牌消耗完!'];
            }
        } else {
            $redisVal = json_encode(['num' => $initNum, 'time' => time()]);
        }
        $redis->multi();
        $redis->set($key, $redisVal);
        $rob_result = $redis->exec();
        if (!$rob_result) {
            $result = ['status' => false, 'msg' => '访问频次过多!'];
        }
        return $result;
    }
}

记一次Linux木马清除过程

记一次Linux木马清除过程

原文地址

xyl870612 FreeBuf 1周前
前段时间公司发生了一起服务器入侵事件,在此分享给大家也顺便理顺下linux入侵应急响应思路。

一、事件描述
某天监控同事反馈有台机器cpu飙高到2000%,可能机器已经被黑。于是登录上去查看,果然有个进程名为"HT8sUy71"的进程在作祟,这一看名字就不大可能是正常进程。

二、处理过程
2.1 查杀病毒进程
cpu占用率如此之高,基本可判断为挖矿程序无疑了,使用在线威胁情报系统对进程进行检测,不出所料,得出的标签是coinminer。这种挖矿进程一般都有自动重启机制,可能是某个进程的子进程,也可能在cron定时任务里出现。

首先,检查cron定时任务,发现/var/spool/cron/crontabs/root和/etc/cron.d/tomcat文件修改时间有变化,但是内容却无改动。

然后,使用ps -ef进行查找主进程,并无收获。同时发现此挖矿进程正在和“172.105.114.84”这个ip的8443端口进行通信,可能是黑客的一台远控服务器。

之后,lsof查看此进程在操作哪些文件,也无实际收获。

推测一定是有某个主进程在工作,so,继续通过ps和netstat查找监听所监听端口的异常。果不其然,看到异常进程,正在批量爆破,看来是被当做肉鸡了。同时使用lsof查看此进程文件,发现在调用/root/.ddg/4003.db文件,貌似是个加密的社工库。

判断是此程序即是主程序,kill掉挖矿进程后,使用strace -T -tt -e strace=full进行跟踪,发现主进程先对挖矿进程增加可执行权限,之后拉起进程。

那么现在kill掉主进程吧,发生个小插曲,发现主进程pid一直在变化,好吧,直接killall osryfa3。

至此,算是暂时消停一下,简单总结一下此病毒特征,其一方面释放挖矿病毒进行挖矿,同时又对公网ssh服务进行爆破以扩大感染面。在这里同时给了我一个提醒,或许这台服务器就是通过ssh爆破被入侵的。

2.2 查找入侵痕迹
一般情况下,入侵可能有以下几种方式:

a.各种弱口令爆破

b.系统漏洞的利用

c.应用漏洞的利用

上面说了,可能是通过ssh爆破被入侵的,我们先来验证一下。

查看登录日志,发现异常(下图是已经过滤掉正常登录的日志),基本可断定是通过ssh爆破入侵了,也可以断定ssh的访问控制已经失效。

cat /var/log/secure* | grep Accepted

结合以上ip查看爆破日志,确认以上ip的用户不是同一个人,是刚好有这么多ip同时爆破了root账户,而且最短的爆破时间只花了1分钟。想来这个root账户必然是个弱密码了。继续查找Failed日志的最早时间如下:

cat /var/log/secure* | grep Failed

基本可断定在这之前有人动过访问控制策略了,询问运维人员无果。

stats查看iptables、/etc/hosts.allow和/etc/hosts.deny文件状态,结合.bash_history命令,一切真相大白,一言难尽,你们自己体会。

2.3 病毒文件的清理
首先,使用stat查看上面两个进程的修改时间,大约在7月8号早上6点多,而ssh系统最早爆破时间是7月6号16:41。

然后,结合这两个时间来查找敏感目录中被修改过的系统文件,进行检查修复和清除。

find /etc/ /usr/bin/ /usr/sbin/ /bin/ /usr/local/bin/ /var/spool/cron/ -type f -mtime -3 | xargs ls -l

最后 ,使用chkrootkit、clamav、rkhunter一通查杀,当然,还是重装系统最保险。

三、总结
首先啰嗦一下,关于linux主机,高危端口真得万万不能全网开放。看了日志后,发现黑客真是时时刻刻在爆破啊。

关于linux入侵的排查思路,总结如下:

1.查看异常进程活动-查找是否有异常进程和端口占用
1.1查找占用cpu最多的进程,相关命令:运行top命令后,键入大写字母P按cpu排序;

1.2查找占用内存最多的进程,相关命令:运行top命令后,键入大写字母M

ps aux | sort -k4nr
1.3查找进程文件:

ls -la /proc/$pid/exe
1.4跟踪异常进程运行情况:

strace -tt -T -e trace=all -p $pid
1.5查看进程打开的文件

lsof -p $pid
1.6 查看进程端口情况

netstat -anltp | grep $pid
2.查看账号安全
2.1查看是否有存在新增异常账号:

a.查找特权用户

awk -F ":" '$3==0{print $1}' /etc/passwd
b.查找可以远程登录的账号信息

awk '/$1|$6/{print $1}' /etc/shadow
c.查找sudo权限账户

cat /etc/sudoers | grep -v "^#|^$" | grep "ALL=(ALL)"
2.2 查看是否有账号异常登录情况:

a.查看当前登录用户和其行为

w
b.查看所有用户最后一次登录的时间

lastlog
c.查看所有用户的登录注销信息及系统的启动、重启及关机事件

last
d.查看登录成功的日期、用户名及ip

grep "Accepted " /var/log/secure* | awk '{print $1,$2,$3,$9,$11}'
e.查看试图爆破主机的ip
grep refused /var/log/secure* | awk {'print $9'} | sort | uniq -c |sort -nr | moregrep "Failed password" /var/log/secure* | grep -E -o "(([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3}))" | uniq -c
f.查看有哪些ip在爆破主机的root账号

grep "Failed password for root" /var/log/secure | awk '{print $11}' | sort
g.查看爆破用户名字典

grep "Failed password" /var/log/secure | awk {'print $9'} | sort | uniq -c | sort -nr
3.查找异常文件
3.1 查找cron文件中是否存在恶意脚本

/var/spool/cron//etc/crontab /etc/cron.d/ /etc/cron.daily/* /etc/cron.hourly/* /etc/cron.monthly/* /etc/cron.weekly/ /etc/anacrontab /var/spool/anacron/* 3.2 查看最近一段时间内被修改的系统文件
find /etc/ /usr/bin/ /usr/sbin/ /bin/ /usr/local/bin/ -type f -mtime -T | xargs ls -la
3.3 按时间排序,确认最近是否有命令被替换,可以结合rpm -Va命令

ls -alt /usr/bin /usr/sbin /bin /usr/local/binrpm -Va>rpm.log
3.4 确认是否有异常开机启动项

cat /etc/rc.localchkconfig --list
4.借助工具查杀病毒和rootkit
4.1 查杀rootkit

chkrootkit (下载地址-http://www.chkrootkit.org)rkhunter (下载地址-http://rkhunter.sourceforge.net)
4.2 查杀病毒

clamav(下载地址-http://www.clamav.net/download.html)
4.3 查杀webshell

cloudwalker(下载地址-http://github.com/chaitin/cloudwalker)
工具用法不再赘述,大家自行查阅。

*本文原创作者:xyl870612,本文属于FreeBuf原创奖励计划,未经许可禁止转载