add printer in linux

linux 添加打印机

记一次arch 上添加打印机的过程, 公司有一个Ricoh c5503 的打印机,ip 192.168.4.99
通过nmap这个地址可以发现开了不少端口

1
2
3
4
5
6
7
8
9
10
11
PORT     STATE SERVICE
21/tcp open ftp
23/tcp open telnet
80/tcp open http
139/tcp open netbios-ssn
514/tcp open shell
515/tcp open printer
631/tcp open ipp
7443/tcp open oracleas-https
8080/tcp open http-proxy
9100/tcp open jetdirect

启动avahi-daemon 服务

这个服务提供mDNS功能, 可以发现局域网内的主机提供的服务,例如打印服务,ssh。 各个主机通过224.0.0.251 地址多播自己提供的服务
主机也可以配置自己想广告的服务,这是用户可选的。 具体看wiki

通过执行一下命令可以看到打印服务

1
avahi-browse --all --ignore-local --resolve --terminate

找到有用的信息如下:

1
2
3
4
5
6
+    br0 IPv4 RICOH MP C4503 [002673734B1D]                 Web Site             local
= br0 IPv4 RICOH MP C4503 [002673734B1D] Web Site local
hostname = [RNP002673734B1D.local]
address = [192.168.4.99]
port = [80]
txt = ["path=/"]

安装cupsd

启动cups-browsed.service服务后, 可以通过网页也配置打印机, 包括分享自己的打印机和添加别人的打印机。
我这里需要添加打印机服务

  1. 浏览器输入http://localhost:631/ 可以打开网页
  2. administrator -> add printer 输入root和密码
  3. 有以下选项
    1
    2
    3
    4
    5
    6
    7
    8
    Backend Error Handler
    Internet Printing Protocol (ipp)
    LPD/LPR Host or Printer
    Internet Printing Protocol (ipps)
    AppSocket/HP JetDirect
    Internet Printing Protocol (http)
    Internet Printing Protocol (https)
    Windows Printer via SAMBA
  4. 我这里选择的AppSocket/HP JetDirect, 因为nmap显示是支持jetdirect的。
  5. 输入地址socket://192.168.4.99:9100
  6. 输入自定义的打印机名称和路径/

至此,浏览器打印页面就可以看到添加的打印机了

更新

开始时我走的ipp协议,打印机地址为’ipp://192.168.4.99:631/ipp’, 后来连不上打印机

通过查端口发现有维护人员修改了打印机端口信息, 631和80端口都被封了,可能是出于安全考虑

1
2
3
4
5
6
7
8
9
10
11
12
PORT      STATE    SERVICE
21/tcp open ftp
23/tcp open telnet
80/tcp filtered http
139/tcp open netbios-ssn
514/tcp open shell
515/tcp open printer
631/tcp filtered ipp
7443/tcp open oracleas-https
8080/tcp open http-proxy
9100/tcp open jetdirect
65000/tcp open unknown

631没有开放,参考windows的同事配置,发现他们用的9100端口(9100是走socket协议),所以需要重新添加打印机
新的网络地址即为’socket://127.0.0.1:9100’

oauth2 认证

oauth2 认证说明

oauth2 是依赖第三方的认证方式, 例如玩游戏的时候弹出QQ登录,微信登录。
这种游戏运营商并不需要用户注册,而是直接从QQ或者微信那里获取用户的OPENID, 然后游戏游戏运营商存储并通过OPENID来识别用户

而且, 有资质的游戏运营商还可以通过玩家的openid来获取用户的信息, 例如用户的手机号,网名,年龄等信息。
有资质这个是有QQ和微信来决定的,游戏运营商需要先去腾讯那里注册认证。腾讯愿意给游戏运营商分享多少信息是腾讯说了算。
所以有用户的数据在手, 腾讯稳稳当国内老大

以QQ登录为例,当玩家登录游戏时,游戏运营商先让用户访问QQ的auth2服务器,并带上游戏运营商的id。待QQ认证后会回调运营商注册的回调接口
一般为oauth/callback,并且带上用户的openid。 这样游戏运营商就知道是谁登录了。

如果游戏运营商需要更多用户资料时,例如注册, 运营商可以通过QQ的查询接口,密钥以及用户的openid 去查询。 这样就拉取到了用户的信息。
然后如果资料不全, 再让玩家补充,例如输入身份证号。这应该国家不准腾讯向别人分享的,必须要用户自己输入。。

使用github的oauth认证

  1. 先去github -> developer 创建oauth应用, 输入自己的回调地址。 当用户被github认证后,会调用这个地址

  2. 在服务端配置,利用一个开源 oauth2_proxy工具, 项目地址:

1
https://github.com/oauth2-proxy/oauth2-proxy
  1. 配置nginx

配置`oauth2_proxy

之前遇到问题,一直授权不成功, 原因是cookie_secure 配成了true, 即使是https下。原因未知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
auth_logging = true
auth_logging_format = "{{.Client}} - {{.Username}} [{{.Timestamp}}] [{{.Status}}] {{.Message}}"
## pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream
pass_basic_auth = true
# pass_user_headers = true
## pass the request Host Header to upstream
## when disabled the upstream Host is used as the Host Header
pass_host_header = true
## Email Domains to allow authentication for (this authorizes any email on this domain)
## for more granular authorization use `authenticated_emails_file`
## To authorize any email addresses use "*"
# email_domains = [
# "yourcompany.com"
# ]
email_domains="*"
## The OAuth Client ID, Secret
provider="github"
client_id = "cef54714c84e3b0c2248"
client_secret = "a96d3d94771273b5295202d03c0c2d3ca7f625dc"
## Pass OAuth Access token to upstream via "X-Forwarded-Access-Token"
pass_access_token = false
## Authenticated Email Addresses File (one email per line)
# authenticated_emails_file = ""
## Htpasswd File (optional)
## Additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption
## enabling exposes a username/login signin form
# htpasswd_file = ""
## Templates
## optional directory with custom sign_in.html and error.html
# custom_templates_dir = ""
## skip SSL checking for HTTPS requests
# ssl_insecure_skip_verify = false
## Cookie Settings
## Name - the cookie name
## Secret - the seed string for secure cookies; should be 16, 24, or 32 bytes
## for use with an AES cipher when cookie_refresh or pass_access_token
## is set
## Domain - (optional) cookie domain to force cookies to (ie: .yourcompany.com)
## Expire - (duration) expire timeframe for cookie
## Refresh - (duration) refresh the cookie when duration has elapsed after cookie was initially set.
## Should be less than cookie_expire; set to 0 to disable.
## On refresh, OAuth token is re-validated.
## (ie: 1h means tokens are refreshed on request 1hr+ after it was set)
## Secure - secure cookies are only sent by the browser of a HTTPS connection (recommended)
## HttpOnly - httponly cookies are not readable by javascript (recommended)
# cookie_name = "_oauth2_proxy"
cookie_secret = "beautyfly"
cookie_domains = "beautyflying.cn"
cookie_expire = "168h"
# cookie_refresh = ""
cookie_secure = false
# cookie_httponly = true

可以将oauth2 配置成服务

1
2
3
4
5
6
7
8
[Unit]
Description = OAuth2 proxy for www blog

[Service]
Type=simple
ExecStart=/usr/bin/oauth2_proxy -config /etc/oauth2-proxy.cfg
[Install]
WantedBy=multi-user.target

nginx 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $request_uri;
# or, if you are handling multiple domains:
# proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
}

location = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}

location / {

auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;

# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;

# if you enabled --pass-access-token, this will pass the token to the backend
auth_request_set $token $upstream_http_x_auth_request_access_token;
proxy_set_header X-Access-Token $token;

# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;

# When using the --set-authorization-header flag, some provider's cookies can exceed the 4kb
# limit and so the OAuth2 Proxy splits these into multiple parts.
# Nginx normally only copies the first `Set-Cookie` header from the auth_request to the response,
# so if your cookies are larger than 4kb, you will need to extract additional cookies manually.
auth_request_set $auth_cookie_name_upstream_1 $upstream_cookie_auth_cookie_name_1;

# Extract the Cookie attributes from the first Set-Cookie header and append them
# to the second part ($upstream_cookie_* variables only contain the raw cookie content)
if ($auth_cookie ~* "(; .*)") {
set $auth_cookie_name_0 $auth_cookie;
set $auth_cookie_name_1 "auth_cookie_name_1=$auth_cookie_name_upstream_1$1";
}

# Send both Set-Cookie headers now if there was a second part
if ($auth_cookie_name_upstream_1) {
add_header Set-Cookie $auth_cookie_name_0;
add_header Set-Cookie $auth_cookie_name_1;
}

root /usr/share/nginx/html/blog;
index index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

使用baidu 的oauth 认证

常见缺陷

主要收集一些常见缺陷和加固方法

javascript

  1. xss漏洞:
    在输入框注入代码如 <script> alert(1)</script>
    解决办法是校验输入

正则匹配

使用正则来校验输入合法性

软件加固里面经常使用的办法就是校验用户输入,例如要求名词需要是大小写或_开头, 支持包含大小写 ,中划线 -,下划线_以及点.

那么相应的正则表到式为^[a-zA-Z_][-a-zA-Z1-9_.]*$,校验方法为

1
2
echo $1 | grep -q '^[a-zA-Z_][-a-zA-Z1-9_.]*$'
test $? -eq 0 && echo "yes" || echo "no"

用python实现

1
2
3
4
5
6
7

Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> print(re.match('^[a-zA-Z_][a-zA-Z0-9\-_.]*$', '_abcdedf.', flags=0))
<re.Match object; span=(0, 9), match='_abcdedf.'>
>>> print(re.match('^[a-zA-Z_][a-zA-Z0-9\-_.]*$', '._abcdedf.', flags=0))
None

gitcommand

记录Git的重要操作

Git登录信息

在临时环境下拉代码, 如果重新生成密钥再添加到托管平台比较麻烦,要么拷贝自己的私钥过来,更加不安全。
这时候临时用密码登录最适合了, 但是每个pull fetch操作都要登录,这很烦躁,所以下面有2个方法解决

设置密码暂存

指类似sudo命令一样, 校验成功之后一段时间,不需要输入密码

1
git config --global   credential.helper 'cache --timeout 900'

我们都知道Git 的配置分3种级别 local system global, 这里推荐global。
因为只设置local,在有submodule的情况下,同样会提示输入密码。

设置密码永存

不推荐

1
git config --global   credential.helper store

cpp

C++ 小技巧

使用lambda 定义一个可变的const

因为const 不能改变的,只有在程序运行的时候初始化。 而通过lamda,可以通过获取命令行输入来改变值

1
2
3
4
5
const int x = []() -> int {
int t;
std::cin >> t;
return t;
}();

gdb调试容器内的进程

gdb

先来个gdb手册链接
这里记录一下自己常用的技巧

恢复debug符号

通常上线的程序要strip符号表来减少内存占用和硬盘占用,会执行以下命令

1
2
3
4
5
## 1. 只保留debug表的内容(可选),为以后调试用,生产时不用
objcopy --only-keep-debug foo foo.dbg

## 2. 将foo的debug表删除,不要任何调试信息,用于生产使用
objcopy --strip-debug foo

如果生产出问题了,就需要恢复debug信息,add-gnu-debuglink参数可以是foo.dbg, 也可以就是strip之前的foo文件
所以第一步是可选的

1
2
## 3. 恢复debug信息
objcopy --add-gnu-debuglink=foo.dbg foo

但其实第三步也是可选的,只要把foo.dbg放到foo相同位置时,gdb就可以读取到debug信息

使用.gdbinit

.gdbinit 文件是类似.bash_profile的文件,一般在home目录或者项目根目录, gdb 自动读取
也可以手动读取其他位置的, 在gdb执行source /somedir/.gdbinit

通常可以在这个文件里定义一些设置例如set pagination off 关闭输出分页, 就不用每次交互确定
还可以定义命令 如pvector可以打印vector内的所有成员, 非常比较方便

gdb 调试容器内的进程

因为容器内运行的程序都是strip了debug符号的,所以之前调试都是先将debug文件复制到容器对应位置,然后将源码也映射到容器内,而且还要安装gdb和重新开启priveledge模式, 非常繁琐。
所以尝试如下操作:

  1. 能不能单独一个容器安装gdb和符号和代码文件, 跨容器去调试另一个容器的进程呢?
  2. 能不能在宿主机上直接调试容器进程呢?

经过一番搜索,没有第一种方案的实践办法,但第二种方案亲测可行!

步骤

  1. 先启动容器,一般业务主进程的pid为1
  2. 在容器外面执行ps aux | grep xxx,因为在很多情况是多个容器跑同一个程序,所以ps aux 通过参数名来确定进程的pid, 当然最好的方式是通过进程namespace了
  3. 使用sudo gdb -p pid, 提示缺少Missing separate debuginfo for target
  4. 将debug符号文件复制到容器对应容器的目录
  5. 再次attach,提示缺少源文件/builds/project/src/aaa.cpp,这是cmake编译时的目录,需要替换
  6. 在gdb里执行命令 set substitute-path /builds/ /home/my-home/ # from -> to
  7. 然后使用l可以查看对应的源代码了

总结

可以直接在宿主机上运行gdb来调试容器进程,并替换代码路径为宿主机的目录。

ps: gdb 启动时默认会读取~/.gdbinit 文件, 但是gdb会提示, 这是由于调试docker需要sudo权限,而我的gdbinit在非root目录

1
2
3
4
5
6
7
warning: File "/home/h/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".                                                        
To enable execution of this file add
add-auto-load-safe-path /home/h/.gdbinit
line to your configuration file "/root/.gdbinit".
To completely disable this security protection add
set auto-load safe-path /
line to your configuration file "/root/.gdbinit".

也就是需要手动执行set auto-load safe-path / 或者将 add-auto-load-safe-path /home/h/.gdbinit写入/root/.gdbinit