Linux 使用 ssh-keygen 生成 RSA 密钥对

什么是 SSH 和 RSA密钥

RSA 是一种公钥加密算法,在 1977 年由麻省理工学院的 Ron Rivest, Adi Shamir, Leonard Adleman 三人一起提出,因此该算法命名以三人姓氏首字母组合而成。

SSH 是 Secure Shell 缩写,是建立在应用层和传输层基础上的安全协议,为计算机上运行的 Shell 提供安全的传输和使用环境。

传统的 rsh, FTP, POP 和 Telnet 网络协议因为传输时采用明文,很容易受到中间人方式攻击。为了防止远程传输信息出现泄露,SSH 协议支持对传输的数据进行加密,因此它还能防止 DNS 和 IP 欺骗。另外采取 SSH 协议传输的数据可以进行压缩,所以可以加快数据传输速度。最初 SSH 协议由芬兰的 Tatu Ylönen 在 1995 年设计开发,目前属于 SSH Communications Security 拥有,由于版权原因,1999 年 10 月开源软件 OpenSSH 被开发出来,它已成为事实上的 SSH 协议标准实现(SSH Communications Security 提供的 SSH 软件使用不同于 OpenSSH 的私钥格式),也是目前 Linux 标准配置。

工具

OpenSSH 提供了以下几个工具:

  1. ssh:实现 SSH 协议,用以建立安全连接,它替代了较早的 rlogin 和 Telnet。
  2. scp, sftp:利用 SSH 协议远程传输文件,它替代了较早的 rcp。
  3. sshd:SSH 服务器守护进程,运行在服务器端。
  4. ssh-keygen:用以生成 RSA 或 DSA 密钥对。
  5. ssh-agent, ssh-add:管理密钥的工具。
  6. ssh-keyscan:扫描网络中的主机,记录找到的公钥。

方案

交互界面生成命令

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/xavier/.ssh/id_rsa): id_rsa
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
The key fingerprint is:
ce:89:59:3d:a1:3a:99:b3:01:46:78:0f:d1:cc:d4:fa xavier@Qbee-X
The key's randomart image is:
+--[ RSA 2048]----+
|    .=..         |
|   . .+ .        |
|  . +  .  .      |
|   o o.  o .     |
|    o ..S o      |
|   . . XE. .     |
|      X +        |
|       =         |
|      .          |
+-----------------+

ssh-keygen 默认使用 RSA 算法,长度为 2048 位,生成一个私钥文件 id_rsa 和一个公钥文件 id_rsa.pub,两个文件默认保存在用户的 ~/.ssh 目录下。你可以在命令行交互过程指定密钥文件路径,也可以设置密钥口令,如果设置了密钥口令,在使用密钥进行登录时,需要输入口令。

快速生成命令

ssh-keygen -t rsa -P '' -f '/root/.ssh/id_rsa'

执行后将会生成 id_rsa 的密钥和 id_rsa.pub 的公钥。

拷贝公钥到远程机器

ssh-copy-id -i kelu@kelu.org.pub app@localhost

以密钥验证scp免密

 scp -i /home/app/.ssh/id_rsa app@localhost:/home/app/test.sh /tmp

参考资料


shell 脚本中切换用户并执行命令

背景

目前使用的是超级用户root,然而我需要使用脚本批量运行一些命令行,然而由于用户权限的原因无法直接使用。

对于常规的系统,我通常使用 sudo -u 的方式进行切换,例如备份 postgresql 数据库时:

sudo -u postgres pg_dump -F c -Z 9 -d abc > abc.dump

如果是没有 sudo 的系统,应当如何解决呢?

方案

还是以上文备份 postgresql 数据库为例,

如果只需要执行一行命令,使用:

su - postgres -c "pg_dump -F c -Z 9 -d abc > abc.dump"

如果执行多行命令,如下:

su - postgres <<EOF
/bin/bash -c "psql -c \"CREATE USER abc WITH PASSWORD 'abc';\""
/bin/bash -c "psql -c \"CREATE DATABASE abc OWNER abc;\""
/bin/bash -c "psql -c \"GRANT ALL PRIVILEGES ON DATABASE abc to abc;\""
pg_dump -F c -Z 9 -d abc > /var/lib/postgresql/dump/abc.dump
EOF

参考资料


Docker Alpine 镜像设置东八区

背景

最近做了一个alpine镜像,需要打印日志,发现打印出来的日志时间不对,需要对它进行时区设置。

镜像基于postgres:9.4-alpine

什么是 alpine

Alpine Linux Docker 镜像基于 Alpine Linux操作系统,后者是一个面向安全的轻型Linux发行版。不同于通常Linux发行版,Alpine Linux采用了musl libc和busybox以减小系统的体积和运行时资源消耗。在保持瘦身的同时,Alpine Linux还提供了自己的包管理工具apk,可以在其网站上查询,或者直接通过apk命令查询和安装。

Alpine Linux使用了musl,可能和其他Linux发行版使用的glibc实现会有所不同。在容器化中最可能遇到的是DNS问题,即musl实现的DNS服务不会使用resolv.conf文件中的search和domain两个配置,这对于一些通过DNS来进行服务发现的框架可能会遇到问题。

Alpine Linux Docker 镜像主要特点是容量非常小,只有5M,且拥有非常友好的包管理器。

方案

针对我的场景,为 alpine 设置TZ环境变量即可:

FROM postgres:9.4-alpine

ENV TZ=Asia/Shanghai

而相对于原生的alpine系统,可能需要更多的一些工作,例如安装tzdata,具体做法如下:

RUN apk update && apk add ca-certificates && update-ca-certificates && apk add --update tzdata
ENV TZ=Asia/Shanghai
RUN rm -rf /var/cache/apk/*

参考资料


为网站启用https,使用容器化的 Let's Encrypt

背景

去年有记录过一篇 Let’s Encrypt ,不过是基于源码的。最近在为服务器做迁移,将运维组件也容器化。这篇文章记录我使用 容器化 Let’s Encrypt 的过程。更详细的操作步骤可以参考我以前的文章——《Let’s Encrypt》

什么是 Let’s Encrypt

Let’s Encrypt是国外一个公共的免费SSL项目,由 Linux 基金会托管,它的来头不小,由Mozilla、思科、Akamai、IdenTrust和EFF等组织发起,目的就是向网站自动签发和管理免费证书,以便加速互联网由HTTP过渡到HTTPS,目前Facebook等大公司开始加入赞助行列。

Let’s Encrypt已经得了 IdenTrust 的交叉签名,这意味着其证书现在已经可以被Mozilla、Google、Microsoft和Apple等主流的浏览器所信任,你只需要在Web 服务器证书链中配置交叉签名,浏览器客户端会自动处理好其它的一切,Let’s Encrypt安装简单,未来大规模采用可能性非常大。

方案

Let’s Encrypt 分为 StandanloneWebroot 两种认证方式,主要区别在于:

  1. Standalone 的认证方式需要暂时占用服务器的 80 或者 443 端口,来进行获取和更新证书的操作。这意味着网站必须下线才行。
  2. Webroot 认证方式需要在域名配置文件里 server.conf (监听 80 端口那部分)中,添加一个通配规则,使得 certbot 可以生成一个特定的验证文件,同时让之后 Let’s Encrypt 的验证服务器发起的 http-01 请求可以验证到对应文件。

就目前来说我属于比较懒的一类人,目前暂时忍受住了临时下线的问题(网站下线10s左右),standalone较为简单,遂使用了 standalone 的方式。

例如首次认证test.kelu.org这个域名:

docker run -it --rm --name certbot \
  -p 80:80 \
  -v "$(pwd)/data:/etc/letsencrypt" \
  -v "$(pwd)/datalib:/var/lib/letsencrypt" \
  certbot/certbot certonly \
  --standalone \
  --email xxx@xxx.org --agree-tos \
  -d test.kelu.org

便在当前目录下的data文件夹里生成好了证书。

证书有三个月的有效期,此时运行renew命令即可续约:

docker run -it --rm --name certbot \
  -p 80:80 \
  -v "$(pwd)/data:/etc/letsencrypt" \
  -v "$(pwd)/datalib:/var/lib/letsencrypt" \
  certbot/certbot renew

nginx相关配置

因为一切都是容器化,nginx的配置也固化在文件中,下面是我的docker-compose.yml配置参考:

  nginx:
    image: openresty/openresty:alpine
    restart: always
    volumes:
      - ./:/var/www/html:rw
      - ./docker/letsencrypt/data:/etc/letsencrypt:rw
      - ./docker/openresty/conf.d:/etc/nginx/conf.d:rw
      - ./docker/openresty/conf/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf:rw
      - ./docker/log:/log:rw
    links:
      - "php"
    ports:
      - "80:80"
      - "443:443"

nginx 的配置如下:

server {
    listen       443;
    server_name  test.kelu.org;

    access_log /log/test.access.log;
    error_log  /log/test.error.log;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/test.kelu.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/test.kelu.org/privkey.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL;
    ssl_prefer_server_ciphers on;

    root         /var/www/html/;

    error_page 404 500 502 503 504 /index.html;
}

改进

本文是一个简单初级的生成证书的方式,可快速部署。

目前有几个待改进的点:

  1. 生成证书需要占用80、443端口,意味着当前业务必须停止。
  2. 证书 renew 也需要停止当前业务。
  3. 目前有以dns认证的方式获取证书,更加方便。

参考资料


Docker on Windows Operation not permitted

先前分享了一个容器化的开发环境——kelvinblood/docker-lnmp,在 mac 下运行无碍,可是跑到Windows下运行的时候,sock 的文件映射就出了问题:

2018/09/17 02:39:54 [crit] 6#6: *2 connect() to unix:/sock/www.sock failed (2: No such file or directory) while connecting to upstream, client: 10.0.75.1, server: , request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/sock/www.sock:", host: "10.0.75.2:8000"

让我疑惑的是,我还有很多的映射,都是ok的,比如log,怎么单单就这个 sock 出了问题?

谷歌了一番,发现 mongo 项目里也有很多人吐槽这个情况,看来并不是我一个人的问题。众多issue里发现了这个靠谱的中文答案:win10下部署报错:Operation not permitted, terminating #7

windows下只能使用volume,不能直接bind磁盘。

创建volume
docker volume create mongodata
docker volume create redisdata
docker volume create logsdata

将docker-compose.yml修改如下
version: "3.3"
...
    image: redis:4.0.6
    command: redis-server --appendonly yes
    volumes:
      - type: volume
        source: redisdata
        target: /data
...

# 一定要声明volumes
volumes:
  redisdata:
    external:
      name: redisdata
      
      
删除production.js中的db配置
运行docker-compose up -d

确实如此。我的修改在这个提交里可以参考:

[bugfix] use sock must use bind type for windows mount volume in dock…